Call odrefresh in VM from composd
composd needs to prepare the directory, run a fd_server, etc. then
request to run odrefresh in the VM.
`FdServerConfig` and `FdServer` are introduced to make starting a
fd_server from composd easier.
Also, add a testing command in composd_cmd.
Bug: 205750213
Test: atest ComposHostTestCases
Test: With some local hacks in ART, with SELinux disabled in the VM,
odrefresh completed with exit code 80 and output files that
look normal (at least sizes are).
Change-Id: I52c9d1ad369eea6d423831adb42087a3bcf30d66
diff --git a/compos/composd/src/service.rs b/compos/composd/src/service.rs
index d9963d1..3738e18 100644
--- a/compos/composd/src/service.rs
+++ b/compos/composd/src/service.rs
@@ -18,7 +18,9 @@
//! desired.
use crate::compilation_task::CompilationTask;
+use crate::fd_server_helper::FdServerConfig;
use crate::instance_manager::InstanceManager;
+use crate::instance_starter::CompOsInstance;
use crate::util::to_binder_result;
use android_system_composd::aidl::android::system::composd::{
ICompilationTask::{BnCompilationTask, ICompilationTask},
@@ -29,7 +31,12 @@
self, BinderFeatures, ExceptionCode, Interface, Status, Strong, ThreadState,
};
use anyhow::{Context, Result};
-use rustutils::users::{AID_ROOT, AID_SYSTEM};
+use compos_common::COMPOS_DATA_ROOT;
+use rustutils::{system_properties, users::AID_ROOT, users::AID_SYSTEM};
+use std::fs::{create_dir, File, OpenOptions};
+use std::os::unix::fs::OpenOptionsExt;
+use std::os::unix::io::AsRawFd;
+use std::path::{Path, PathBuf};
use std::sync::Arc;
pub struct IsolatedCompilationService {
@@ -50,13 +57,14 @@
&self,
callback: &Strong<dyn ICompilationTaskCallback>,
) -> binder::Result<Strong<dyn ICompilationTask>> {
- let calling_uid = ThreadState::get_calling_uid();
- // This should only be called by system server, or root while testing
- if calling_uid != AID_SYSTEM && calling_uid != AID_ROOT {
- return Err(Status::new_exception(ExceptionCode::SECURITY, None));
- }
+ check_test_permissions()?;
to_binder_result(self.do_start_test_compile(callback))
}
+
+ fn startTestOdrefresh(&self) -> binder::Result<i8> {
+ check_test_permissions()?;
+ to_binder_result(self.do_odrefresh_for_test())
+ }
}
impl IsolatedCompilationService {
@@ -70,4 +78,62 @@
Ok(BnCompilationTask::new_binder(task, BinderFeatures::default()))
}
+
+ fn do_odrefresh_for_test(&self) -> Result<i8> {
+ let mut staging_dir_path = PathBuf::from(COMPOS_DATA_ROOT);
+ staging_dir_path.push("test-artifacts");
+ to_binder_result(create_dir(&staging_dir_path))?;
+
+ let compos = self
+ .instance_manager
+ .start_test_instance()
+ .context("Starting CompOS for odrefresh test")?;
+ self.do_odrefresh(compos, &staging_dir_path)
+ }
+
+ fn do_odrefresh(&self, compos: Arc<CompOsInstance>, staging_dir_path: &Path) -> Result<i8> {
+ let output_dir = open_dir_path(staging_dir_path)?;
+ let system_dir = open_dir_path(Path::new("/system"))?;
+
+ // Spawn a fd_server to serve the FDs.
+ let fd_server_config = FdServerConfig {
+ ro_dir_fds: vec![system_dir.as_raw_fd()],
+ rw_dir_fds: vec![output_dir.as_raw_fd()],
+ ..Default::default()
+ };
+ let fd_server_raii = fd_server_config.into_fd_server()?;
+
+ let zygote_arch = system_properties::read("ro.zygote")?;
+ let result = compos.get_service().odrefresh(
+ system_dir.as_raw_fd(),
+ output_dir.as_raw_fd(),
+ &zygote_arch,
+ );
+ drop(fd_server_raii);
+ Ok(result?.exitCode)
+ }
+}
+
+fn check_test_permissions() -> binder::Result<()> {
+ let calling_uid = ThreadState::get_calling_uid();
+ // This should only be called by system server, or root while testing
+ if calling_uid != AID_SYSTEM && calling_uid != AID_ROOT {
+ Err(Status::new_exception(ExceptionCode::SECURITY, None))
+ } else {
+ Ok(())
+ }
+}
+
+/// Returns an owned FD of the directory path. It currently returns a `File` as a FD owner, but
+/// it's better to use `std::os::unix::io::OwnedFd` once/if it becomes standard.
+fn open_dir_path(path: &Path) -> Result<File> {
+ OpenOptions::new()
+ .custom_flags(libc::O_PATH | libc::O_DIRECTORY)
+ // The custom flags above is not taken into consideration by the unix implementation of
+ // OpenOptions for flag validation. So even though the man page of open(2) says that
+ // most flags include access mode are ignored, we still need to set a "valid" mode to
+ // make the library happy. The value does not appear to matter elsewhere in the library.
+ .read(true)
+ .open(path)
+ .with_context(|| format!("Failed to open {} directory as path fd", path.display()))
}