use anyhow::{Context, Error};
use fidl::endpoints::ClientEnd;
use fidl_fuchsia_io as fio;
use std::sync::Arc;
use zx::AsHandleRef as _;
pub trait Directory {
fn open(&self, path: &str, flags: fio::Flags, server_end: zx::Channel) -> Result<(), Error>;
}
impl Directory for fio::DirectoryProxy {
fn open(&self, path: &str, flags: fio::Flags, server_end: zx::Channel) -> Result<(), Error> {
let () = self.open3(path, flags, &fio::Options::default(), server_end.into())?;
Ok(())
}
}
impl Directory for fio::DirectorySynchronousProxy {
fn open(&self, path: &str, flags: fio::Flags, server_end: zx::Channel) -> Result<(), Error> {
let () = self.open3(path, flags, &fio::Options::default(), server_end.into())?;
Ok(())
}
}
impl Directory for ClientEnd<fio::DirectoryMarker> {
fn open(&self, path: &str, flags: fio::Flags, server_end: zx::Channel) -> Result<(), Error> {
let raw_handle = self.channel().as_handle_ref().raw_handle();
unsafe {
let borrowed: zx::Channel = zx::Handle::from_raw(raw_handle).into();
let proxy = fio::DirectorySynchronousProxy::new(borrowed);
proxy.open3(path, flags, &fio::Options::default(), server_end.into())?;
std::mem::forget(proxy.into_channel());
}
Ok(())
}
}
pub trait AsRefDirectory {
fn as_ref_directory(&self) -> &dyn Directory;
}
impl AsRefDirectory for fio::DirectoryProxy {
fn as_ref_directory(&self) -> &dyn Directory {
self
}
}
impl AsRefDirectory for fio::DirectorySynchronousProxy {
fn as_ref_directory(&self) -> &dyn Directory {
self
}
}
impl AsRefDirectory for ClientEnd<fio::DirectoryMarker> {
fn as_ref_directory(&self) -> &dyn Directory {
self
}
}
impl<T: Directory> AsRefDirectory for Box<T> {
fn as_ref_directory(&self) -> &dyn Directory {
&**self
}
}
impl<T: Directory> AsRefDirectory for Arc<T> {
fn as_ref_directory(&self) -> &dyn Directory {
&**self
}
}
impl<T: Directory> AsRefDirectory for &T {
fn as_ref_directory(&self) -> &dyn Directory {
*self
}
}
pub fn open_directory_async(
parent: &impl AsRefDirectory,
path: &str,
rights: fio::Rights,
) -> Result<fio::DirectoryProxy, Error> {
let (dir, server_end) = fidl::endpoints::create_proxy::<fio::DirectoryMarker>()
.context("creating directory proxy")?;
let flags = fio::Flags::PROTOCOL_DIRECTORY | fio::Flags::from_bits_truncate(rights.bits());
let () = parent
.as_ref_directory()
.open(path, flags, server_end.into_channel())
.context("opening directory without describe")?;
Ok(dir)
}
pub fn open_file_async(
parent: &impl AsRefDirectory,
path: &str,
rights: fio::Rights,
) -> Result<fio::FileProxy, Error> {
let (file, server_end) =
fidl::endpoints::create_proxy::<fio::FileMarker>().context("creating file proxy")?;
let flags = fio::Flags::PROTOCOL_FILE | fio::Flags::from_bits_truncate(rights.bits());
let () = parent
.as_ref_directory()
.open(path, flags, server_end.into_channel())
.context("opening file without describe")?;
Ok(file)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::client::test_util::run_directory_server;
use assert_matches::assert_matches;
use fuchsia_async as fasync;
use vfs::directory::immutable::simple;
use vfs::file::vmo::read_only;
use vfs::pseudo_directory;
#[fasync::run_singlethreaded(test)]
async fn open_directory_async_real() {
let dir = pseudo_directory! {
"dir" => simple(),
};
let dir = run_directory_server(dir);
let dir = open_directory_async(&dir, "dir", fio::Rights::empty()).unwrap();
fuchsia_fs::directory::close(dir).await.unwrap();
}
#[fasync::run_singlethreaded(test)]
async fn open_directory_async_fake() {
let dir = pseudo_directory! {
"dir" => simple(),
};
let dir = run_directory_server(dir);
let dir = open_directory_async(&dir, "fake", fio::Rights::empty()).unwrap();
assert_matches!(fuchsia_fs::directory::close(dir).await, Err(_));
}
#[fasync::run_singlethreaded(test)]
async fn open_file_async_real() {
let dir = pseudo_directory! {
"file" => read_only("read_only"),
};
let dir = run_directory_server(dir);
let file = open_file_async(&dir, "file", fio::Rights::READ_BYTES).unwrap();
fuchsia_fs::file::close(file).await.unwrap();
}
#[fasync::run_singlethreaded(test)]
async fn open_file_async_fake() {
let dir = pseudo_directory! {
"file" => read_only("read_only"),
};
let dir = run_directory_server(dir);
let fake = open_file_async(&dir, "fake", fio::Rights::READ_BYTES).unwrap();
assert_matches!(fuchsia_fs::file::close(fake).await, Err(_));
}
}