Merge "authfs: refine/rename trait ReadOnlyDataByChunk"
diff --git a/authfs/src/fusefs.rs b/authfs/src/fusefs.rs
index 4728418..d97291c 100644
--- a/authfs/src/fusefs.rs
+++ b/authfs/src/fusefs.rs
@@ -44,12 +44,27 @@
pub type Inode = u64;
type Handle = u64;
+/// `FileConfig` defines the file type supported by AuthFS.
pub enum FileConfig {
- LocalVerifiedReadonlyFile(VerifiedFileReader<LocalFileReader, LocalFileReader>, u64),
- LocalUnverifiedReadonlyFile(LocalFileReader, u64),
- RemoteVerifiedReadonlyFile(VerifiedFileReader<RemoteFileReader, RemoteMerkleTreeReader>, u64),
- RemoteUnverifiedReadonlyFile(RemoteFileReader, u64),
- RemoteVerifiedNewFile(VerifiedFileEditor<RemoteFileEditor>),
+ /// A file type that is verified against fs-verity signature (thus read-only). The file is
+ /// backed by a local file. Debug only.
+ LocalVerifiedReadonlyFile {
+ reader: VerifiedFileReader<LocalFileReader, LocalFileReader>,
+ file_size: u64,
+ },
+ /// A file type that is a read-only passthrough from a local file. Debug only.
+ LocalUnverifiedReadonlyFile { reader: LocalFileReader, file_size: u64 },
+ /// A file type that is verified against fs-verity signature (thus read-only). The file is
+ /// served from a remote server.
+ RemoteVerifiedReadonlyFile {
+ reader: VerifiedFileReader<RemoteFileReader, RemoteMerkleTreeReader>,
+ file_size: u64,
+ },
+ /// A file type that is a read-only passthrough from a file on a remote serrver.
+ RemoteUnverifiedReadonlyFile { reader: RemoteFileReader, file_size: u64 },
+ /// A file type that is initially empty, and the content is stored on a remote server. File
+ /// integrity is guaranteed with private Merkle tree.
+ RemoteVerifiedNewFile { editor: VerifiedFileEditor<RemoteFileEditor> },
}
struct AuthFs {
@@ -191,14 +206,14 @@
// be static.
let inode = num.parse::<Inode>().map_err(|_| io::Error::from_raw_os_error(libc::ENOENT))?;
let st = match self.get_file_config(&inode)? {
- FileConfig::LocalVerifiedReadonlyFile(_, file_size)
- | FileConfig::LocalUnverifiedReadonlyFile(_, file_size)
- | FileConfig::RemoteUnverifiedReadonlyFile(_, file_size)
- | FileConfig::RemoteVerifiedReadonlyFile(_, file_size) => {
+ FileConfig::LocalVerifiedReadonlyFile { file_size, .. }
+ | FileConfig::LocalUnverifiedReadonlyFile { file_size, .. }
+ | FileConfig::RemoteUnverifiedReadonlyFile { file_size, .. }
+ | FileConfig::RemoteVerifiedReadonlyFile { file_size, .. } => {
create_stat(inode, *file_size, FileMode::ReadOnly)?
}
- FileConfig::RemoteVerifiedNewFile(file) => {
- create_stat(inode, file.size(), FileMode::ReadWrite)?
+ FileConfig::RemoteVerifiedNewFile { editor } => {
+ create_stat(inode, editor.size(), FileMode::ReadWrite)?
}
};
Ok(Entry {
@@ -218,14 +233,14 @@
) -> io::Result<(libc::stat64, Duration)> {
Ok((
match self.get_file_config(&inode)? {
- FileConfig::LocalVerifiedReadonlyFile(_, file_size)
- | FileConfig::LocalUnverifiedReadonlyFile(_, file_size)
- | FileConfig::RemoteUnverifiedReadonlyFile(_, file_size)
- | FileConfig::RemoteVerifiedReadonlyFile(_, file_size) => {
+ FileConfig::LocalVerifiedReadonlyFile { file_size, .. }
+ | FileConfig::LocalUnverifiedReadonlyFile { file_size, .. }
+ | FileConfig::RemoteUnverifiedReadonlyFile { file_size, .. }
+ | FileConfig::RemoteVerifiedReadonlyFile { file_size, .. } => {
create_stat(inode, *file_size, FileMode::ReadOnly)?
}
- FileConfig::RemoteVerifiedNewFile(file) => {
- create_stat(inode, file.size(), FileMode::ReadWrite)?
+ FileConfig::RemoteVerifiedNewFile { editor } => {
+ create_stat(inode, editor.size(), FileMode::ReadWrite)?
}
},
DEFAULT_METADATA_TIMEOUT,
@@ -241,23 +256,23 @@
// Since file handle is not really used in later operations (which use Inode directly),
// return None as the handle.
match self.get_file_config(&inode)? {
- FileConfig::LocalVerifiedReadonlyFile(_, _)
- | FileConfig::RemoteVerifiedReadonlyFile(_, _) => {
+ FileConfig::LocalVerifiedReadonlyFile { .. }
+ | FileConfig::RemoteVerifiedReadonlyFile { .. } => {
check_access_mode(flags, libc::O_RDONLY)?;
// Once verified, and only if verified, the file content can be cached. This is not
// really needed for a local file, but is the behavior of RemoteVerifiedReadonlyFile
// later.
Ok((None, fuse::sys::OpenOptions::KEEP_CACHE))
}
- FileConfig::LocalUnverifiedReadonlyFile(_, _)
- | FileConfig::RemoteUnverifiedReadonlyFile(_, _) => {
+ FileConfig::LocalUnverifiedReadonlyFile { .. }
+ | FileConfig::RemoteUnverifiedReadonlyFile { .. } => {
check_access_mode(flags, libc::O_RDONLY)?;
// Do not cache the content. This type of file is supposed to be verified using
// dm-verity. The filesystem mount over dm-verity already is already cached, so use
// direct I/O here to avoid double cache.
Ok((None, fuse::sys::OpenOptions::DIRECT_IO))
}
- FileConfig::RemoteVerifiedNewFile(_) => {
+ FileConfig::RemoteVerifiedNewFile { .. } => {
// No need to check access modes since all the modes are allowed to the
// read-writable file.
Ok((None, fuse::sys::OpenOptions::KEEP_CACHE))
@@ -277,22 +292,22 @@
_flags: u32,
) -> io::Result<usize> {
match self.get_file_config(&inode)? {
- FileConfig::LocalVerifiedReadonlyFile(file, file_size) => {
- read_chunks(w, file, *file_size, offset, size)
+ FileConfig::LocalVerifiedReadonlyFile { reader, file_size } => {
+ read_chunks(w, reader, *file_size, offset, size)
}
- FileConfig::LocalUnverifiedReadonlyFile(file, file_size) => {
- read_chunks(w, file, *file_size, offset, size)
+ FileConfig::LocalUnverifiedReadonlyFile { reader, file_size } => {
+ read_chunks(w, reader, *file_size, offset, size)
}
- FileConfig::RemoteVerifiedReadonlyFile(file, file_size) => {
- read_chunks(w, file, *file_size, offset, size)
+ FileConfig::RemoteVerifiedReadonlyFile { reader, file_size } => {
+ read_chunks(w, reader, *file_size, offset, size)
}
- FileConfig::RemoteUnverifiedReadonlyFile(file, file_size) => {
- read_chunks(w, file, *file_size, offset, size)
+ FileConfig::RemoteUnverifiedReadonlyFile { reader, file_size } => {
+ read_chunks(w, reader, *file_size, offset, size)
}
- FileConfig::RemoteVerifiedNewFile(file) => {
+ FileConfig::RemoteVerifiedNewFile { editor } => {
// Note that with FsOptions::WRITEBACK_CACHE, it's possible for the kernel to
// request a read even if the file is open with O_WRONLY.
- read_chunks(w, file, file.size(), offset, size)
+ read_chunks(w, editor, editor.size(), offset, size)
}
}
}
@@ -310,10 +325,10 @@
_flags: u32,
) -> io::Result<usize> {
match self.get_file_config(&inode)? {
- FileConfig::RemoteVerifiedNewFile(file) => {
+ FileConfig::RemoteVerifiedNewFile { editor } => {
let mut buf = vec![0; size as usize];
r.read_exact(&mut buf)?;
- file.write_at(&buf, offset)
+ editor.write_at(&buf, offset)
}
_ => Err(io::Error::from_raw_os_error(libc::EBADF)),
}
diff --git a/authfs/src/main.rs b/authfs/src/main.rs
index a4b0d40..0db73e9 100644
--- a/authfs/src/main.rs
+++ b/authfs/src/main.rs
@@ -207,8 +207,8 @@
let service = Arc::new(Mutex::new(service));
let authenticator = FakeAuthenticator::always_succeed();
- Ok(FileConfig::RemoteVerifiedReadonlyFile(
- VerifiedFileReader::new(
+ Ok(FileConfig::RemoteVerifiedReadonlyFile {
+ reader: VerifiedFileReader::new(
&authenticator,
RemoteFileReader::new(Arc::clone(&service), remote_id),
file_size,
@@ -216,13 +216,12 @@
RemoteMerkleTreeReader::new(Arc::clone(&service), remote_id),
)?,
file_size,
- ))
+ })
}
fn new_config_remote_unverified_file(remote_id: i32, file_size: u64) -> Result<FileConfig> {
- let file_reader =
- RemoteFileReader::new(Arc::new(Mutex::new(file::get_local_binder())), remote_id);
- Ok(FileConfig::RemoteUnverifiedReadonlyFile(file_reader, file_size))
+ let reader = RemoteFileReader::new(Arc::new(Mutex::new(file::get_local_binder())), remote_id);
+ Ok(FileConfig::RemoteUnverifiedReadonlyFile { reader, file_size })
}
fn new_config_local_ro_file(
@@ -237,21 +236,21 @@
let authenticator = FakeAuthenticator::always_succeed();
let mut sig = Vec::new();
let _ = File::open(signature)?.read_to_end(&mut sig)?;
- let file_reader =
+ let reader =
VerifiedFileReader::new(&authenticator, file_reader, file_size, sig, merkle_tree_reader)?;
- Ok(FileConfig::LocalVerifiedReadonlyFile(file_reader, file_size))
+ Ok(FileConfig::LocalVerifiedReadonlyFile { reader, file_size })
}
fn new_config_local_ro_file_unverified(file_path: &PathBuf) -> Result<FileConfig> {
- let file_reader = LocalFileReader::new(File::open(file_path)?)?;
- let file_size = file_reader.len();
- Ok(FileConfig::LocalUnverifiedReadonlyFile(file_reader, file_size))
+ let reader = LocalFileReader::new(File::open(file_path)?)?;
+ let file_size = reader.len();
+ Ok(FileConfig::LocalUnverifiedReadonlyFile { reader, file_size })
}
fn new_config_remote_new_verified_file(remote_id: i32) -> Result<FileConfig> {
let remote_file =
RemoteFileEditor::new(Arc::new(Mutex::new(file::get_local_binder())), remote_id);
- Ok(FileConfig::RemoteVerifiedNewFile(VerifiedFileEditor::new(remote_file)))
+ Ok(FileConfig::RemoteVerifiedNewFile { editor: VerifiedFileEditor::new(remote_file) })
}
fn prepare_file_pool(args: &Args) -> Result<BTreeMap<Inode, FileConfig>> {
diff --git a/authfs/tests/Android.bp b/authfs/tests/Android.bp
index 56e54f2..bacb890 100644
--- a/authfs/tests/Android.bp
+++ b/authfs/tests/Android.bp
@@ -1,3 +1,7 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
java_test_host {
name: "AuthFsHostTest",
srcs: ["java/**/*.java"],
diff --git a/microdroid/Android.bp b/microdroid/Android.bp
index e818420..f853b75 100644
--- a/microdroid/Android.bp
+++ b/microdroid/Android.bp
@@ -59,7 +59,6 @@
// These files are temporary and only for test.
// TODO(b/178993690): migrate cil files to Soong
- "microdroid_plat_sepolicy.cil",
"microdroid_plat_mapping_file",
"microdroid_plat_sepolicy_and_mapping.sha256",
] + microdroid_shell_and_utilities,
@@ -67,6 +66,7 @@
common: {
deps: [
"com.android.runtime",
+ "plat_sepolicy.cil",
"plat_file_contexts",
"plat_hwservice_contexts",
"plat_property_contexts",
@@ -247,7 +247,7 @@
genrule {
name: "microdroid_plat_sepolicy_and_mapping.sha256_gen",
srcs: [
- ":microdroid_plat_sepolicy.cil",
+ ":plat_sepolicy.cil",
":microdroid_plat_mapping_file",
],
out: ["plat_sepolicy_and_mapping.sha256"],
@@ -276,7 +276,7 @@
name: "microdroid_precompiled_sepolicy_gen",
tools: ["secilc"],
srcs: [
- ":microdroid_plat_sepolicy.cil",
+ ":plat_sepolicy.cil",
":microdroid_plat_mapping_file",
":microdroid_plat_pub_versioned.cil",
":microdroid_vendor_sepolicy.cil",
diff --git a/virtmanager/Android.bp b/virtmanager/Android.bp
index 9fc4f42..1b2aec1 100644
--- a/virtmanager/Android.bp
+++ b/virtmanager/Android.bp
@@ -10,6 +10,7 @@
rustlibs: [
"android.system.virtmanager-rust",
"libandroid_logger",
+ "libbinder_rs", // TODO(dbrazdil): remove once b/182890877 is fixed
"liblog_rust",
"libserde_json",
"libserde",
diff --git a/virtmanager/aidl/android/system/virtmanager/IVirtManager.aidl b/virtmanager/aidl/android/system/virtmanager/IVirtManager.aidl
index 79010da..ab03c18 100644
--- a/virtmanager/aidl/android/system/virtmanager/IVirtManager.aidl
+++ b/virtmanager/aidl/android/system/virtmanager/IVirtManager.aidl
@@ -30,4 +30,17 @@
* and as such is only permitted from the shell user.
*/
VirtualMachineDebugInfo[] debugListVms();
+
+ /**
+ * Hold a strong reference to a VM in Virt Manager. This method is only intended for debug
+ * purposes, and as such is only permitted from the shell user.
+ */
+ void debugHoldVmRef(IVirtualMachine vm);
+
+ /**
+ * Drop reference to a VM that is being held by Virt Manager. Returns the reference if VM was
+ * found and null otherwise. This method is only intended for debug purposes, and as such is
+ * only permitted from the shell user.
+ */
+ @nullable IVirtualMachine debugDropVmRef(int cid);
}
diff --git a/virtmanager/src/aidl.rs b/virtmanager/src/aidl.rs
index 8105051..8c963d2 100644
--- a/virtmanager/src/aidl.rs
+++ b/virtmanager/src/aidl.rs
@@ -17,6 +17,7 @@
use crate::config::VmConfig;
use crate::crosvm::VmInstance;
use crate::{Cid, FIRST_GUEST_CID};
+use ::binder::FromIBinder; // TODO(dbrazdil): remove once b/182890877 is fixed
use android_system_virtmanager::aidl::android::system::virtmanager::IVirtManager::IVirtManager;
use android_system_virtmanager::aidl::android::system::virtmanager::IVirtualMachine::{
BnVirtualMachine, IVirtualMachine,
@@ -82,6 +83,33 @@
.collect();
Ok(cids)
}
+
+ /// Hold a strong reference to a VM in Virt Manager. This method is only intended for debug
+ /// purposes, and as such is only permitted from the shell user.
+ fn debugHoldVmRef(&self, vmref: &dyn IVirtualMachine) -> binder::Result<()> {
+ if !debug_access_allowed() {
+ return Err(StatusCode::PERMISSION_DENIED.into());
+ }
+
+ // Workaround for b/182890877.
+ let vm: Strong<dyn IVirtualMachine> = FromIBinder::try_from(vmref.as_binder()).unwrap();
+
+ let state = &mut *self.state.lock().unwrap();
+ state.debug_hold_vm(vm);
+ Ok(())
+ }
+
+ /// Drop reference to a VM that is being held by Virt Manager. Returns the reference if VM was
+ /// found and None otherwise. This method is only intended for debug purposes, and as such is
+ /// only permitted from the shell user.
+ fn debugDropVmRef(&self, cid: i32) -> binder::Result<Option<Strong<dyn IVirtualMachine>>> {
+ if !debug_access_allowed() {
+ return Err(StatusCode::PERMISSION_DENIED.into());
+ }
+
+ let state = &mut *self.state.lock().unwrap();
+ Ok(state.debug_drop_vm(cid))
+ }
}
/// Check whether the caller of the current Binder method is allowed to call debug methods.
@@ -123,6 +151,10 @@
/// Binder client are dropped the weak reference here will become invalid, and will be removed
/// from the list opportunistically the next time `add_vm` is called.
vms: Vec<Weak<VmInstance>>,
+
+ /// Vector of strong VM references held on behalf of users that cannot hold them themselves.
+ /// This is only used for debugging purposes.
+ debug_held_vms: Vec<Strong<dyn IVirtualMachine>>,
}
impl State {
@@ -140,11 +172,22 @@
// Actually add the new VM.
self.vms.push(vm);
}
+
+ /// Store a strong VM reference.
+ fn debug_hold_vm(&mut self, vm: Strong<dyn IVirtualMachine>) {
+ self.debug_held_vms.push(vm);
+ }
+
+ /// Retrieve and remove a strong VM reference.
+ fn debug_drop_vm(&mut self, cid: i32) -> Option<Strong<dyn IVirtualMachine>> {
+ let pos = self.debug_held_vms.iter().position(|vm| vm.getCid() == Ok(cid))?;
+ Some(self.debug_held_vms.swap_remove(pos))
+ }
}
impl Default for State {
fn default() -> Self {
- State { next_cid: FIRST_GUEST_CID, vms: vec![] }
+ State { next_cid: FIRST_GUEST_CID, vms: vec![], debug_held_vms: vec![] }
}
}
diff --git a/vm/Android.bp b/vm/Android.bp
index 248af4d..4bb9727 100644
--- a/vm/Android.bp
+++ b/vm/Android.bp
@@ -10,7 +10,6 @@
rustlibs: [
"android.system.virtmanager-rust",
"libanyhow",
- "libbinder_rs",
"libenv_logger",
"liblibc",
"liblog_rust",
diff --git a/vm/src/main.rs b/vm/src/main.rs
index 34031f7..8c2a084 100644
--- a/vm/src/main.rs
+++ b/vm/src/main.rs
@@ -18,12 +18,9 @@
use android_system_virtmanager::aidl::android::system::virtmanager::IVirtManager::IVirtManager;
use android_system_virtmanager::binder::{
- get_interface, ParcelFileDescriptor, ProcessState, Strong,
+ get_interface, DeathRecipient, IBinder, ParcelFileDescriptor, ProcessState, Strong,
};
use anyhow::{Context, Error};
-// TODO: Import these via android_system_virtmanager::binder once https://r.android.com/1619403 is
-// submitted.
-use binder::{DeathRecipient, IBinder};
use std::fs::File;
use std::io;
use std::os::unix::io::{AsRawFd, FromRawFd};
@@ -42,6 +39,15 @@
/// Path to VM config JSON
#[structopt(parse(from_os_str))]
config: PathBuf,
+
+ /// Detach VM from the terminal and run in the background
+ #[structopt(short, long)]
+ daemonize: bool,
+ },
+ /// Stop a virtual machine running in the background
+ Stop {
+ /// CID of the virtual machine
+ cid: u32,
},
/// List running virtual machines
List,
@@ -58,24 +64,44 @@
.context("Failed to find Virt Manager service")?;
match opt {
- Opt::Run { config } => command_run(virt_manager, &config),
+ Opt::Run { config, daemonize } => command_run(virt_manager, &config, daemonize),
+ Opt::Stop { cid } => command_stop(virt_manager, cid),
Opt::List => command_list(virt_manager),
}
}
/// Run a VM from the given configuration file.
-fn command_run(virt_manager: Strong<dyn IVirtManager>, config_path: &PathBuf) -> Result<(), Error> {
+fn command_run(
+ virt_manager: Strong<dyn IVirtManager>,
+ config_path: &PathBuf,
+ daemonize: bool,
+) -> Result<(), Error> {
let config_filename = config_path.to_str().context("Failed to parse VM config path")?;
let stdout_file = ParcelFileDescriptor::new(duplicate_stdout()?);
- let vm =
- virt_manager.startVm(config_filename, Some(&stdout_file)).context("Failed to start VM")?;
+ let stdout = if daemonize { None } else { Some(&stdout_file) };
+ let vm = virt_manager.startVm(config_filename, stdout).context("Failed to start VM")?;
+
let cid = vm.getCid().context("Failed to get CID")?;
println!("Started VM from {} with CID {}.", config_filename, cid);
- // Wait until the VM dies. If we just returned immediately then the IVirtualMachine Binder
- // object would be dropped and the VM would be killed.
- wait_for_death(&mut vm.as_binder())?;
- println!("VM died");
+ if daemonize {
+ // Pass the VM reference back to Virt Manager and have it hold it in the background.
+ virt_manager.debugHoldVmRef(&*vm).context("Failed to pass VM to Virt Manager")
+ } else {
+ // Wait until the VM dies. If we just returned immediately then the IVirtualMachine Binder
+ // object would be dropped and the VM would be killed.
+ wait_for_death(&mut vm.as_binder())?;
+ println!("VM died");
+ Ok(())
+ }
+}
+
+/// Retrieve reference to a previously daemonized VM and stop it.
+fn command_stop(virt_manager: Strong<dyn IVirtManager>, cid: u32) -> Result<(), Error> {
+ virt_manager
+ .debugDropVmRef(cid as i32)
+ .context("Failed to get VM from Virt Manager")?
+ .context("CID does not correspond to a running background VM")?;
Ok(())
}