Don't start authfs until fd_server is ready

We pass a pipe fd to fd_server which it closes when it is ready to
accept connections. pvm_exec waits for this before triggering
compilation, which will cause authfs in the VM to connect back to
fd_server.

Also improved a bunch of error handling/error logging code, since I
managed to hit a lot of failure cases.

Fix: 201764092
Bug: 186126194
Test: atest ComposTestCase
Change-Id: I99ea5ba4fb390d47485aef3cbaaccedd046ae9fe
diff --git a/authfs/fd_server/src/main.rs b/authfs/fd_server/src/main.rs
index 12f013c..32ffc0a 100644
--- a/authfs/fd_server/src/main.rs
+++ b/authfs/fd_server/src/main.rs
@@ -32,6 +32,7 @@
 use std::convert::TryInto;
 use std::fs::File;
 use std::io;
+use std::os::raw;
 use std::os::unix::fs::FileExt;
 use std::os::unix::io::{AsRawFd, FromRawFd};
 
@@ -292,7 +293,12 @@
     Ok((fd, FdConfig::ReadWrite(file)))
 }
 
-fn parse_args() -> Result<BTreeMap<i32, FdConfig>> {
+struct Args {
+    fd_pool: BTreeMap<i32, FdConfig>,
+    ready_fd: Option<File>,
+}
+
+fn parse_args() -> Result<Args> {
     #[rustfmt::skip]
     let matches = clap::App::new("fd_server")
         .arg(clap::Arg::with_name("ro-fds")
@@ -303,6 +309,9 @@
              .long("rw-fds")
              .multiple(true)
              .number_of_values(1))
+        .arg(clap::Arg::with_name("ready-fd")
+            .long("ready-fd")
+            .takes_value(true))
         .get_matches();
 
     let mut fd_pool = BTreeMap::new();
@@ -318,8 +327,13 @@
             fd_pool.insert(fd, config);
         }
     }
-
-    Ok(fd_pool)
+    let ready_fd = if let Some(arg) = matches.value_of("ready-fd") {
+        let fd = arg.parse::<i32>()?;
+        Some(fd_to_file(fd)?)
+    } else {
+        None
+    };
+    Ok(Args { fd_pool, ready_fd })
 }
 
 fn main() -> Result<()> {
@@ -327,16 +341,22 @@
         android_logger::Config::default().with_tag("fd_server").with_min_level(log::Level::Debug),
     );
 
-    let fd_pool = parse_args()?;
+    let args = parse_args()?;
 
-    let mut service = FdService::new_binder(fd_pool).as_binder();
+    let mut service = FdService::new_binder(args.fd_pool).as_binder();
+    let mut ready_notifier = ReadyNotifier(args.ready_fd);
+
     debug!("fd_server is starting as a rpc service.");
     // SAFETY: Service ownership is transferring to the server and won't be valid afterward.
     // Plus the binder objects are threadsafe.
+    // RunRpcServerCallback does not retain a reference to ready_callback, and only ever
+    // calls it with the param we provide during the lifetime of ready_notifier.
     let retval = unsafe {
-        binder_rpc_unstable_bindgen::RunRpcServer(
+        binder_rpc_unstable_bindgen::RunRpcServerCallback(
             service.as_native_mut() as *mut binder_rpc_unstable_bindgen::AIBinder,
             RPC_SERVICE_PORT,
+            Some(ReadyNotifier::ready_callback),
+            ready_notifier.as_void_ptr(),
         )
     };
     if retval {
@@ -346,3 +366,25 @@
         bail!("Premature termination of RPC server");
     }
 }
+
+struct ReadyNotifier(Option<File>);
+
+impl ReadyNotifier {
+    fn notify(&mut self) {
+        debug!("fd_server is ready");
+        // Close the ready-fd if we were given one to signal our readiness.
+        drop(self.0.take());
+    }
+
+    fn as_void_ptr(&mut self) -> *mut raw::c_void {
+        self as *mut _ as *mut raw::c_void
+    }
+
+    unsafe extern "C" fn ready_callback(param: *mut raw::c_void) {
+        // SAFETY: This is only ever called by RunRpcServerCallback, within the lifetime of the
+        // ReadyNotifier, with param taking the value returned by as_void_ptr (so a properly aligned
+        // non-null pointer to an initialized instance).
+        let ready_notifier = param as *mut Self;
+        ready_notifier.as_mut().unwrap().notify()
+    }
+}