Fix zipfuse race condition
We mount the APK by running zipfuse (asynchronously), and then try to
run the payload from within it. There is a race condition - if the
mount hasn't happened, the payload binary won't appear.
This showed up when I switched to config-less VMs (so we no longer
read the JSON configuration between the two operations) - but only on
cuttlefish, and only in non-debug mode.
Fix it by using a property to signal when the mount has completed.
Bug: 243513572
Test: atest MicrodroidTests (locally & via acloud)
Change-Id: Ib777e7f28afafebd128f8e0c149d485ab9351273
diff --git a/zipfuse/Android.bp b/zipfuse/Android.bp
index 3aba94a..1bdc5fe 100644
--- a/zipfuse/Android.bp
+++ b/zipfuse/Android.bp
@@ -13,9 +13,10 @@
"libclap",
"libfuse_rust",
"liblibc",
- "libzip",
- "libscopeguard",
"liblog_rust",
+ "librustutils",
+ "libscopeguard",
+ "libzip",
],
// libfuse_rust, etc don't support 32-bit targets
multilib: {
diff --git a/zipfuse/src/main.rs b/zipfuse/src/main.rs
index 8400a72..9411759 100644
--- a/zipfuse/src/main.rs
+++ b/zipfuse/src/main.rs
@@ -20,10 +20,11 @@
mod inode;
-use anyhow::Result;
+use anyhow::{Context as AnyhowContext, Result};
use clap::{App, Arg};
use fuse::filesystem::*;
use fuse::mount::*;
+use rustutils::system_properties;
use std::collections::HashMap;
use std::convert::TryFrom;
use std::ffi::{CStr, CString};
@@ -52,6 +53,12 @@
.takes_value(false)
.help("Disallow the execution of binary files"),
)
+ .arg(
+ Arg::with_name("readyprop")
+ .short('p')
+ .takes_value(true)
+ .help("Specify a property to be set when mount is ready"),
+ )
.arg(Arg::with_name("ZIPFILE").required(true))
.arg(Arg::with_name("MOUNTPOINT").required(true))
.get_matches();
@@ -60,7 +67,8 @@
let mount_point = matches.value_of("MOUNTPOINT").unwrap().as_ref();
let options = matches.value_of("options");
let noexec = matches.is_present("noexec");
- run_fuse(zip_file, mount_point, options, noexec)?;
+ let ready_prop = matches.value_of("readyprop");
+ run_fuse(zip_file, mount_point, options, noexec, ready_prop)?;
Ok(())
}
@@ -70,6 +78,7 @@
mount_point: &Path,
extra_options: Option<&str>,
noexec: bool,
+ ready_prop: Option<&str>,
) -> Result<()> {
const MAX_READ: u32 = 1 << 20; // TODO(jiyong): tune this
const MAX_WRITE: u32 = 1 << 13; // This is a read-only filesystem
@@ -94,6 +103,11 @@
}
fuse::mount(mount_point, "zipfuse", mount_flags, &mount_options)?;
+
+ if let Some(property_name) = ready_prop {
+ system_properties::write(property_name, "1").context("Failed to set readyprop")?;
+ }
+
let mut config = fuse::FuseConfig::new();
config.dev_fuse(dev_fuse).max_write(MAX_WRITE).max_read(MAX_READ);
Ok(config.enter_message_loop(ZipFuse::new(zip_file)?)?)