use anyhow::Error;
use fdio::Namespace;
use fidl_fuchsia_io as fio;
use std::collections::{hash_map::Entry, HashMap};
use std::sync::Arc;
use vfs::directory::{
self, entry::DirectoryEntry, helper::DirectlyMutable, mutable::simple as pfs,
};
use vfs::execution_scope::ExecutionScope;
type Directory = Arc<pfs::Simple>;
pub struct NamespaceBinder {
scope: ExecutionScope,
dirs: HashMap<String, Directory>,
}
impl NamespaceBinder {
pub fn new(scope: ExecutionScope) -> NamespaceBinder {
NamespaceBinder { scope, dirs: HashMap::new() }
}
pub fn bind_at_path(
&mut self,
path: &str,
entry: Arc<dyn DirectoryEntry>,
) -> Result<(), Error> {
let ns = Namespace::installed()?;
let (dir_path, entry_name) =
path.rsplit_once('/').ok_or_else(|| format_err!("path must be absolute"))?;
let dir = match self.dirs.entry(dir_path.to_string()) {
Entry::Occupied(map_entry) => map_entry.into_mut(),
Entry::Vacant(map_entry) => {
let dir = pfs::simple();
let (client, server) = fidl::endpoints::create_endpoints();
directory::entry_container::Directory::open(
dir.clone(),
self.scope.clone(),
fio::OpenFlags::RIGHT_READABLE
| fio::OpenFlags::RIGHT_WRITABLE
| fio::OpenFlags::DIRECTORY,
vfs::path::Path::dot(),
fidl::endpoints::ServerEnd::new(server.into_channel()),
);
ns.bind(dir_path, client)?;
map_entry.insert(dir)
}
};
dir.add_entry(entry_name, entry)?;
Ok(())
}
}
impl Drop for NamespaceBinder {
fn drop(&mut self) {
let ns = Namespace::installed().unwrap();
for path in self.dirs.keys() {
ns.unbind(path).unwrap();
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use fidl::endpoints::ClientEnd;
use fidl_test_placeholders as echo;
use fuchsia_zircon as zx;
use futures::TryStreamExt;
#[fuchsia_async::run_singlethreaded(test)]
async fn test_bind_at_path() -> Result<(), Error> {
const NAMESPACE_PATH: &str = "/some/path/in/the/namespace";
let scope = ExecutionScope::new();
let mut ns = NamespaceBinder::new(scope);
let echo_entry =
vfs::service::host(move |mut stream: echo::EchoRequestStream| async move {
while let Ok(Some(request)) = stream.try_next().await {
let echo::EchoRequest::EchoString { value, responder } = request;
responder.send(Some(&value.unwrap())).expect("responder failed");
}
});
ns.bind_at_path(NAMESPACE_PATH, echo_entry)?;
let (client, server) = zx::Channel::create();
fdio::service_connect(NAMESPACE_PATH, server)?;
let proxy = ClientEnd::<echo::EchoMarker>::new(client).into_proxy()?;
let res = proxy.echo_string(Some("hello world")).await?;
assert_eq!(res, Some("hello world".to_string()));
Ok(())
}
}