blob: bcbf2cde803d1258944d66b71b39a10564c30eeb [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//! Typesafe wrappers around the /pkgfs/ctl filesystem.
use {
fidl_fuchsia_io::{DirectoryMarker, DirectoryProxy, DirectoryRequestStream},
fidl_fuchsia_io2::UnlinkOptions,
fuchsia_zircon::Status,
thiserror::Error,
};
/// An error encountered while garbage collecting blobs
#[derive(Debug, Error)]
#[allow(missing_docs)]
pub enum GcError {
#[error("while sending request: {}", _0)]
Fidl(fidl::Error),
#[error("unlink failed with status: {}", _0)]
UnlinkError(Status),
}
/// An error encountered while syncing
#[derive(Debug, Error)]
#[allow(missing_docs)]
pub enum SyncError {
#[error("while sending request: {}", _0)]
Fidl(fidl::Error),
#[error("sync failed with status: {}", _0)]
SyncError(Status),
}
/// An open handle to /pkgfs/ctl
#[derive(Debug, Clone)]
pub struct Client {
proxy: DirectoryProxy,
}
impl Client {
/// Returns an client connected to pkgfs from the current component's namespace
pub fn open_from_namespace() -> Result<Self, io_util::node::OpenError> {
let proxy = io_util::directory::open_in_namespace(
"/pkgfs/ctl",
fidl_fuchsia_io::OPEN_RIGHT_READABLE | fidl_fuchsia_io::OPEN_RIGHT_WRITABLE,
)?;
Ok(Client { proxy })
}
/// Returns an client connected to pkgfs from the given pkgfs root dir.
pub fn open_from_pkgfs_root(pkgfs: &DirectoryProxy) -> Result<Self, io_util::node::OpenError> {
Ok(Client {
proxy: io_util::directory::open_directory_no_describe(
pkgfs,
"ctl",
fidl_fuchsia_io::OPEN_RIGHT_READABLE | fidl_fuchsia_io::OPEN_RIGHT_WRITABLE,
)?,
})
}
/// Creates a new client backed by the returned request stream. This constructor should not be
/// used outside of tests.
///
/// # Panics
///
/// Panics on error
pub fn new_test() -> (Self, DirectoryRequestStream) {
let (proxy, stream) =
fidl::endpoints::create_proxy_and_stream::<DirectoryMarker>().unwrap();
(Self { proxy }, stream)
}
/// Performs a garbage collection
pub async fn gc(&self) -> Result<(), GcError> {
self.proxy
.unlink2("garbage", UnlinkOptions::EMPTY)
.await
.map_err(GcError::Fidl)?
.map_err(|s| GcError::UnlinkError(Status::from_raw(s)))
}
/// Performs a sync
pub async fn sync(&self) -> Result<(), SyncError> {
match self.proxy.sync().await.map(Status::from_raw) {
Ok(Status::OK) => Ok(()),
Ok(status) => Err(SyncError::SyncError(status)),
Err(err) => Err(SyncError::Fidl(err)),
}
}
}
#[cfg(test)]
mod tests {
use {
super::*,
fidl::endpoints::create_proxy_and_stream,
fidl_fuchsia_io::{DirectoryMarker, DirectoryRequest},
fuchsia_async as fasync,
futures_util::stream::TryStreamExt,
matches::assert_matches,
};
#[fasync::run_singlethreaded(test)]
async fn gc() {
let (proxy, mut stream) = create_proxy_and_stream::<DirectoryMarker>().unwrap();
fasync::Task::spawn(async move {
match stream.try_next().await.unwrap().unwrap() {
DirectoryRequest::Unlink2 { name, options: _, responder } => {
assert_eq!(name, "garbage");
responder.send(&mut Ok(())).unwrap();
}
other => panic!("unexpected request: {:?}", other),
}
})
.detach();
assert_matches!(Client { proxy }.gc().await, Ok(()));
}
#[fasync::run_singlethreaded(test)]
async fn sync() {
let (proxy, mut stream) = create_proxy_and_stream::<DirectoryMarker>().unwrap();
fasync::Task::spawn(async move {
match stream.try_next().await.unwrap().unwrap() {
DirectoryRequest::Sync { responder } => {
responder.send(Status::OK.into_raw()).unwrap();
}
other => panic!("unexpected request: {:?}", other),
}
})
.detach();
assert_matches!(Client { proxy }.sync().await, Ok(()));
}
}