Switch compsvc to use authfs_service

With authfs_service, we no longer need compsvc_worker.rs and authfs.rs
for the task setup. Now, for each request, compsvc can just request
FDs from authfs_service then pass to the task.

Also, fixed the integer type of remote FD to match ParcelFileDescriptor.

Bug: 194717985
Test: atest ComposHostTestCases
Change-Id: I8c0be106243778ac20e7cd96a778db4e34aef051
diff --git a/compos/src/compsvc.rs b/compos/src/compsvc.rs
index ae242de..14b520e 100644
--- a/compos/src/compsvc.rs
+++ b/compos/src/compsvc.rs
@@ -15,34 +15,35 @@
  */
 
 //! compsvc is a service to run computational tasks in a PVM upon request. It is able to set up
-//! file descriptors backed by fd_server and pass the file descriptors to the actual tasks for
-//! read/write. The service also attempts to sandbox the execution so that one task cannot leak or
-//! impact future tasks.
-//!
-//! The current architecture / process hierarchy looks like:
-//! - compsvc (handle requests)
-//!   - compsvc_worker (for environment setup)
-//!     - authfs (fd translation)
-//!     - actual task
+//! file descriptors backed by authfs (via authfs_service) and pass the file descriptors to the
+//! actual tasks.
 
 use anyhow::Result;
 use log::error;
 use minijail::{self, Minijail};
-use std::path::PathBuf;
+use std::ffi::CString;
+use std::os::unix::io::AsRawFd;
+use std::path::{Path, PathBuf};
 
 use crate::signer::Signer;
+use authfs_aidl_interface::aidl::com::android::virt::fs::{
+    AuthFsConfig::AuthFsConfig, IAuthFs::IAuthFs, IAuthFsService::IAuthFsService,
+    InputFdAnnotation::InputFdAnnotation, OutputFdAnnotation::OutputFdAnnotation,
+};
+use authfs_aidl_interface::binder::ParcelFileDescriptor;
 use compos_aidl_interface::aidl::com::android::compos::ICompService::{
     BnCompService, ICompService,
 };
 use compos_aidl_interface::aidl::com::android::compos::Metadata::Metadata;
 use compos_aidl_interface::binder::{
-    BinderFeatures, Interface, Result as BinderResult, Status, StatusCode, Strong,
+    BinderFeatures, ExceptionCode, Interface, Result as BinderResult, Status, StatusCode, Strong,
 };
 
-const WORKER_BIN: &str = "/apex/com.android.compos/bin/compsvc_worker";
+const AUTHFS_SERVICE_NAME: &str = "authfs_service";
 
-// TODO: Replace with a valid directory setup in the VM.
-const AUTHFS_MOUNTPOINT: &str = "/data/local/tmp";
+/// The number that represents the file descriptor number expecting by the task. The number may be
+/// meaningless in the current process.
+pub type PseudoRawFd = i32;
 
 /// Constructs a binder object that implements ICompService. task_bin is the path to the binary that will
 /// be run when execute() is called. If debuggable is true then stdout/stderr from the binary will be
@@ -52,83 +53,128 @@
     debuggable: bool,
     signer: Option<Box<dyn Signer>>,
 ) -> Strong<dyn ICompService> {
-    let service = CompService {
-        worker_bin: PathBuf::from(WORKER_BIN.to_owned()),
-        task_bin,
-        debuggable,
-        signer,
-    };
+    let service = CompService { task_bin: PathBuf::from(task_bin), debuggable, signer };
     BnCompService::new_binder(service, BinderFeatures::default())
 }
 
 struct CompService {
-    task_bin: String,
-    worker_bin: PathBuf,
+    task_bin: PathBuf,
     debuggable: bool,
     #[allow(dead_code)] // TODO: Make use of this
     signer: Option<Box<dyn Signer>>,
 }
 
-impl CompService {
-    fn run_worker_in_jail_and_wait(&self, args: &[String]) -> Result<(), minijail::Error> {
-        let mut jail = Minijail::new()?;
-
-        // TODO(b/185175567): New user and uid namespace when supported. Run as nobody.
-        // New mount namespace to isolate the FUSE mount.
-        jail.namespace_vfs();
-
-        let inheritable_fds = if self.debuggable {
-            vec![1, 2] // inherit/redirect stdout/stderr for debugging
-        } else {
-            vec![]
-        };
-        let _pid = jail.run(&self.worker_bin, &inheritable_fds, args)?;
-        jail.wait()
-    }
-
-    fn build_worker_args(&self, args: &[String], metadata: &Metadata) -> Vec<String> {
-        let mut worker_args = vec![
-            WORKER_BIN.to_string(),
-            "--authfs-root".to_string(),
-            AUTHFS_MOUNTPOINT.to_string(),
-        ];
-        for annotation in &metadata.input_fd_annotations {
-            worker_args.push("--in-fd".to_string());
-            worker_args.push(format!("{}:{}", annotation.fd, annotation.file_size));
-        }
-        for annotation in &metadata.output_fd_annotations {
-            worker_args.push("--out-fd".to_string());
-            worker_args.push(annotation.fd.to_string());
-        }
-        if self.debuggable {
-            worker_args.push("--debug".to_string());
-        }
-        worker_args.push("--".to_string());
-
-        // Do not accept arbitrary code execution. We want to execute some specific task of this
-        // service. Use the associated executable.
-        worker_args.push(self.task_bin.clone());
-        worker_args.extend_from_slice(&args[1..]);
-        worker_args
-    }
-}
-
 impl Interface for CompService {}
 
 impl ICompService for CompService {
     fn execute(&self, args: &[String], metadata: &Metadata) -> BinderResult<i8> {
-        let worker_args = self.build_worker_args(args, metadata);
+        // Mount authfs (via authfs_service).
+        let authfs_config = build_authfs_config(metadata);
+        let authfs = get_authfs_service()?.mount(&authfs_config)?;
 
-        match self.run_worker_in_jail_and_wait(&worker_args) {
+        // The task expects to receive FD numbers that match its flags (e.g. --zip-fd=42) prepared
+        // on the host side. Since the local FD opened from authfs (e.g. /authfs/42) may not match
+        // the task's expectation, prepare a FD mapping and let minijail prepare the correct FD
+        // setup.
+        let fd_mapping =
+            open_authfs_files_for_fd_mapping(&authfs, &authfs_config).map_err(|e| {
+                new_binder_exception(
+                    ExceptionCode::SERVICE_SPECIFIC,
+                    format!("Failed to create FDs on authfs: {:?}", e),
+                )
+            })?;
+
+        let jail =
+            spawn_jailed_task(&self.task_bin, args, fd_mapping, self.debuggable).map_err(|e| {
+                new_binder_exception(
+                    ExceptionCode::SERVICE_SPECIFIC,
+                    format!("Failed to spawn the task: {:?}", e),
+                )
+            })?;
+        let jail_result = jail.wait();
+
+        // Be explicit about the lifetime, which should last at least until the task is finished.
+        drop(authfs);
+
+        match jail_result {
             Ok(_) => Ok(0), // TODO(b/161471326): Sign the output on succeed.
             Err(minijail::Error::ReturnCode(exit_code)) => {
                 error!("Task failed with exit code {}", exit_code);
                 Err(Status::from(StatusCode::FAILED_TRANSACTION))
             }
             Err(e) => {
-                error!("Unexpected error: {}", e);
+                error!("Unexpected minijail error: {}", e);
                 Err(Status::from(StatusCode::UNKNOWN_ERROR))
             }
         }
     }
 }
+
+fn get_authfs_service() -> BinderResult<Strong<dyn IAuthFsService>> {
+    Ok(authfs_aidl_interface::binder::get_interface(AUTHFS_SERVICE_NAME)?)
+}
+
+fn build_authfs_config(metadata: &Metadata) -> AuthFsConfig {
+    AuthFsConfig {
+        port: 3264, // TODO: support dynamic port
+        inputFdAnnotations: metadata
+            .input_fd_annotations
+            .iter()
+            .map(|x| InputFdAnnotation { fd: x.fd, fileSize: x.file_size })
+            .collect(),
+        outputFdAnnotations: metadata
+            .output_fd_annotations
+            .iter()
+            .map(|x| OutputFdAnnotation { fd: x.fd })
+            .collect(),
+    }
+}
+
+fn open_authfs_files_for_fd_mapping(
+    authfs: &Strong<dyn IAuthFs>,
+    config: &AuthFsConfig,
+) -> Result<Vec<(ParcelFileDescriptor, PseudoRawFd)>> {
+    let mut fd_mapping = Vec::new();
+
+    let results: Result<Vec<_>> = config
+        .inputFdAnnotations
+        .iter()
+        .map(|annotation| Ok((authfs.openFile(annotation.fd, false)?, annotation.fd)))
+        .collect();
+    fd_mapping.append(&mut results?);
+
+    let results: Result<Vec<_>> = config
+        .outputFdAnnotations
+        .iter()
+        .map(|annotation| Ok((authfs.openFile(annotation.fd, true)?, annotation.fd)))
+        .collect();
+    fd_mapping.append(&mut results?);
+
+    Ok(fd_mapping)
+}
+
+fn spawn_jailed_task(
+    executable: &Path,
+    args: &[String],
+    fd_mapping: Vec<(ParcelFileDescriptor, PseudoRawFd)>,
+    debuggable: bool,
+) -> Result<Minijail> {
+    // TODO(b/185175567): Run in a more restricted sandbox.
+    let jail = Minijail::new()?;
+
+    let mut preserve_fds = if debuggable {
+        // Inherit/redirect stdout/stderr for debugging, assuming no conflict
+        vec![(1, 1), (2, 2)]
+    } else {
+        vec![]
+    };
+
+    preserve_fds.extend(fd_mapping.iter().map(|(f, id)| (f.as_raw_fd(), *id)));
+
+    let _pid = jail.run_remap(executable, preserve_fds.as_slice(), args)?;
+    Ok(jail)
+}
+
+fn new_binder_exception<T: AsRef<str>>(exception: ExceptionCode, message: T) -> Status {
+    Status::new_exception(exception, CString::new(message.as_ref()).as_deref().ok())
+}