authfs: Support RPC binder
This change adds a new flag --rpc-binder to authfs, and --cid to
fd_server. The flag allows both to communicate through vsock. The
capability of local binder is kept for now (and still the default),
but can be removed later.
The change relies on the newly introduced libbinder_rpc_unstable.so
and the corresponding bindgen, in order to access the unstable API from
Rust.
Also, add authfs and libbinder_rpc_unstable to microdroid.
Bug: 190547489
Bug: 189947807
Test: [Android shell] sh -c 'exec 9<>/data/local/tmp/output \
/apex/com.android.virt/bin/fd_server --rw-fds 9 --rpc-binder'
[VM shell] /apex/com.android.virt/bin/authfs \
/data/local/tmp --cid 2 --remote-new-rw-file 9:9
[VM shell 2] ps -A > /data/local/tmp/9
[Android shell] cat /data/local/tmp/output # see correct data
Change-Id: I200f746aa4078508a0f0d2498a1525bb898a6e3b
diff --git a/authfs/Android.bp b/authfs/Android.bp
index 16d0b3a..174914f 100644
--- a/authfs/Android.bp
+++ b/authfs/Android.bp
@@ -14,6 +14,8 @@
"libandroid_logger",
"libanyhow",
"libauthfs_crypto_bindgen",
+ "libbinder_rpc_unstable_bindgen",
+ "libbinder_rs",
"libcfg_if",
"libfuse_rust",
"liblibc",
@@ -27,7 +29,10 @@
enabled: false,
},
},
- shared_libs: ["libcrypto"],
+ shared_libs: [
+ "libcrypto",
+ "libbinder_rpc_unstable",
+ ],
defaults: ["crosvm_defaults"],
}
diff --git a/authfs/fd_server/Android.bp b/authfs/fd_server/Android.bp
index 748a5b6..8ddbf69 100644
--- a/authfs/fd_server/Android.bp
+++ b/authfs/fd_server/Android.bp
@@ -9,11 +9,16 @@
"authfs_aidl_interface-rust",
"libandroid_logger",
"libanyhow",
+ "libbinder_rpc_unstable_bindgen",
+ "libbinder_rs",
"libclap",
"liblibc",
"liblog_rust",
"libnix",
],
prefer_rlib: true,
+ shared_libs: [
+ "libbinder_rpc_unstable",
+ ],
apex_available: ["com.android.virt"],
}
diff --git a/authfs/fd_server/src/main.rs b/authfs/fd_server/src/main.rs
index 204d1b1..5137a2e 100644
--- a/authfs/fd_server/src/main.rs
+++ b/authfs/fd_server/src/main.rs
@@ -37,6 +37,7 @@
use std::os::unix::io::{AsRawFd, FromRawFd};
use anyhow::{bail, Context, Result};
+use binder::unstable_api::AsNative;
use log::{debug, error};
use authfs_aidl_interface::aidl::com::android::virt::fs::IVirtFdService::{
@@ -48,6 +49,7 @@
};
const SERVICE_NAME: &str = "authfs_fd_server";
+const RPC_SERVICE_PORT: u32 = 3264; // TODO: support dynamic port for multiple fd_server instances
fn new_binder_exception<T: AsRef<str>>(exception: ExceptionCode, message: T) -> Status {
Status::new_exception(exception, CString::new(message.as_ref()).as_deref().ok())
@@ -275,7 +277,7 @@
Ok((fd, FdConfig::ReadWrite(file)))
}
-fn parse_args() -> Result<BTreeMap<i32, FdConfig>> {
+fn parse_args() -> Result<(bool, BTreeMap<i32, FdConfig>)> {
#[rustfmt::skip]
let matches = clap::App::new("fd_server")
.arg(clap::Arg::with_name("ro-fds")
@@ -286,6 +288,8 @@
.long("rw-fds")
.multiple(true)
.number_of_values(1))
+ .arg(clap::Arg::with_name("rpc-binder")
+ .long("rpc-binder"))
.get_matches();
let mut fd_pool = BTreeMap::new();
@@ -301,7 +305,9 @@
fd_pool.insert(fd, config);
}
}
- Ok(fd_pool)
+
+ let rpc_binder = matches.is_present("rpc-binder");
+ Ok((rpc_binder, fd_pool))
}
fn main() -> Result<()> {
@@ -309,14 +315,32 @@
android_logger::Config::default().with_tag("fd_server").with_min_level(log::Level::Debug),
);
- let fd_pool = parse_args()?;
+ let (rpc_binder, fd_pool) = parse_args()?;
- ProcessState::start_thread_pool();
-
- add_service(SERVICE_NAME, FdService::new_binder(fd_pool).as_binder())
- .with_context(|| format!("Failed to register service {}", SERVICE_NAME))?;
- debug!("fd_server is running.");
-
- ProcessState::join_thread_pool();
- bail!("Unexpected exit after join_thread_pool")
+ if rpc_binder {
+ let mut service = FdService::new_binder(fd_pool).as_binder();
+ 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.
+ let retval = unsafe {
+ binder_rpc_unstable_bindgen::RunRpcServer(
+ service.as_native_mut() as *mut binder_rpc_unstable_bindgen::AIBinder,
+ RPC_SERVICE_PORT,
+ )
+ };
+ if retval {
+ debug!("RPC server has shut down gracefully");
+ Ok(())
+ } else {
+ bail!("Premature termination of RPC server");
+ }
+ } else {
+ ProcessState::start_thread_pool();
+ let service = FdService::new_binder(fd_pool).as_binder();
+ add_service(SERVICE_NAME, service)
+ .with_context(|| format!("Failed to register service {}", SERVICE_NAME))?;
+ debug!("fd_server is running as a local service.");
+ ProcessState::join_thread_pool();
+ bail!("Unexpected exit after join_thread_pool")
+ }
}
diff --git a/authfs/src/file.rs b/authfs/src/file.rs
index 4b43786..033dbd6 100644
--- a/authfs/src/file.rs
+++ b/authfs/src/file.rs
@@ -4,21 +4,56 @@
pub use local_file::LocalFileReader;
pub use remote_file::{RemoteFileEditor, RemoteFileReader, RemoteMerkleTreeReader};
+use binder::unstable_api::{new_spibinder, AIBinder};
+use binder::FromIBinder;
use std::io;
use crate::common::CHUNK_SIZE;
-
-use authfs_aidl_interface::aidl::com::android::virt::fs::IVirtFdService;
+use authfs_aidl_interface::aidl::com::android::virt::fs::IVirtFdService::IVirtFdService;
use authfs_aidl_interface::binder::{get_interface, Strong};
-// TODO(victorhsieh): use remote binder.
-pub fn get_local_binder() -> Strong<dyn IVirtFdService::IVirtFdService> {
- let service_name = "authfs_fd_server";
- get_interface(&service_name).expect("Cannot reach authfs_fd_server binder service")
-}
+pub type VirtFdService = Strong<dyn IVirtFdService>;
pub type ChunkBuffer = [u8; CHUNK_SIZE as usize];
+pub const RPC_SERVICE_PORT: u32 = 3264;
+
+fn get_local_binder() -> io::Result<VirtFdService> {
+ let service_name = "authfs_fd_server";
+ get_interface(&service_name).map_err(|e| {
+ io::Error::new(
+ io::ErrorKind::AddrNotAvailable,
+ format!("Cannot reach authfs_fd_server binder service: {}", e),
+ )
+ })
+}
+
+fn get_rpc_binder(cid: u32) -> io::Result<VirtFdService> {
+ // SAFETY: AIBinder returned by RpcClient has correct reference count, and the ownership can be
+ // safely taken by new_spibinder.
+ let ibinder = unsafe {
+ new_spibinder(binder_rpc_unstable_bindgen::RpcClient(cid, RPC_SERVICE_PORT) as *mut AIBinder)
+ };
+ if let Some(ibinder) = ibinder {
+ Ok(IVirtFdService::try_from(ibinder).map_err(|e| {
+ io::Error::new(
+ io::ErrorKind::AddrNotAvailable,
+ format!("Cannot connect to RPC service: {}", e),
+ )
+ })?)
+ } else {
+ Err(io::Error::new(io::ErrorKind::InvalidInput, "Invalid raw AIBinder"))
+ }
+}
+
+pub fn get_binder_service(cid: Option<u32>) -> io::Result<VirtFdService> {
+ if let Some(cid) = cid {
+ get_rpc_binder(cid)
+ } else {
+ get_local_binder()
+ }
+}
+
/// A trait for reading data by chunks. Chunks can be read by specifying the chunk index. Only the
/// last chunk may have incomplete chunk size.
pub trait ReadByChunk {
diff --git a/authfs/src/file/remote_file.rs b/authfs/src/file/remote_file.rs
index bd99893..0b6c007 100644
--- a/authfs/src/file/remote_file.rs
+++ b/authfs/src/file/remote_file.rs
@@ -19,14 +19,9 @@
use std::io;
use std::sync::{Arc, Mutex};
-use super::{ChunkBuffer, RandomWrite, ReadByChunk};
+use super::{ChunkBuffer, RandomWrite, ReadByChunk, VirtFdService};
use crate::common::CHUNK_SIZE;
-use authfs_aidl_interface::aidl::com::android::virt::fs::IVirtFdService;
-use authfs_aidl_interface::binder::Strong;
-
-type VirtFdService = Strong<dyn IVirtFdService::IVirtFdService>;
-
fn remote_read_chunk(
service: &Arc<Mutex<VirtFdService>>,
remote_fd: i32,
diff --git a/authfs/src/main.rs b/authfs/src/main.rs
index b30195a..593fa74 100644
--- a/authfs/src/main.rs
+++ b/authfs/src/main.rs
@@ -53,6 +53,10 @@
#[structopt(parse(from_os_str))]
mount_point: PathBuf,
+ /// CID of the VM where the service runs.
+ #[structopt(long)]
+ cid: Option<u32>,
+
/// A read-only remote file with integrity check. Can be multiple.
///
/// For example, `--remote-verified-file 5:10:1234:/path/to/cert` tells the filesystem to
@@ -205,8 +209,11 @@
})
}
-fn new_config_remote_verified_file(remote_id: i32, file_size: u64) -> Result<FileConfig> {
- let service = file::get_local_binder();
+fn new_config_remote_verified_file(
+ service: file::VirtFdService,
+ remote_id: i32,
+ file_size: u64,
+) -> Result<FileConfig> {
let signature = service.readFsveritySignature(remote_id).context("Failed to read signature")?;
let service = Arc::new(Mutex::new(service));
@@ -223,8 +230,12 @@
})
}
-fn new_config_remote_unverified_file(remote_id: i32, file_size: u64) -> Result<FileConfig> {
- let reader = RemoteFileReader::new(Arc::new(Mutex::new(file::get_local_binder())), remote_id);
+fn new_config_remote_unverified_file(
+ service: file::VirtFdService,
+ remote_id: i32,
+ file_size: u64,
+) -> Result<FileConfig> {
+ let reader = RemoteFileReader::new(Arc::new(Mutex::new(service)), remote_id);
Ok(FileConfig::RemoteUnverifiedReadonlyFile { reader, file_size })
}
@@ -251,31 +262,38 @@
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);
+fn new_config_remote_new_verified_file(
+ service: file::VirtFdService,
+ remote_id: i32,
+) -> Result<FileConfig> {
+ let remote_file = RemoteFileEditor::new(Arc::new(Mutex::new(service)), remote_id);
Ok(FileConfig::RemoteVerifiedNewFile { editor: VerifiedFileEditor::new(remote_file) })
}
fn prepare_file_pool(args: &Args) -> Result<BTreeMap<Inode, FileConfig>> {
let mut file_pool = BTreeMap::new();
+ let service = file::get_binder_service(args.cid)?;
+
for config in &args.remote_ro_file {
file_pool.insert(
config.ino,
- new_config_remote_verified_file(config.remote_id, config.file_size)?,
+ new_config_remote_verified_file(service.clone(), config.remote_id, config.file_size)?,
);
}
for config in &args.remote_ro_file_unverified {
file_pool.insert(
config.ino,
- new_config_remote_unverified_file(config.remote_id, config.file_size)?,
+ new_config_remote_unverified_file(service.clone(), config.remote_id, config.file_size)?,
);
}
for config in &args.remote_new_rw_file {
- file_pool.insert(config.ino, new_config_remote_new_verified_file(config.remote_id)?);
+ file_pool.insert(
+ config.ino,
+ new_config_remote_new_verified_file(service.clone(), config.remote_id)?,
+ );
}
for config in &args.local_ro_file {
diff --git a/microdroid/Android.bp b/microdroid/Android.bp
index 4f0d17a..174916b 100644
--- a/microdroid/Android.bp
+++ b/microdroid/Android.bp
@@ -101,7 +101,11 @@
lib64: {
deps: [
"apkdmverity",
+ "authfs",
"zipfuse",
+
+ // TODO(b/184872979): Needed by authfs. Remove once the Rust API is created.
+ "libbinder_rpc_unstable",
],
},
},