blob: b19af568843ff9074e60ef2c5d30e91dfeee7fe9 [file] [log] [blame]
// Copyright 2020 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 an open package directory.
use {
fidl::endpoints::{ClientEnd, Proxy, ServerEnd},
fidl_fuchsia_io::{DirectoryMarker, DirectoryProxy, FileProxy},
fuchsia_hash::Hash,
fuchsia_pkg::MetaContents,
thiserror::Error,
};
/// An error encountered while opening a package
#[derive(Debug, Error)]
#[allow(missing_docs)]
pub enum OpenError {
#[error("the package does not exist")]
NotFound,
#[error("while opening the package: {0}")]
Io(io_util::node::OpenError),
}
/// An open package directory
#[derive(Debug)]
pub struct Directory {
proxy: DirectoryProxy,
}
impl Directory {
pub(crate) fn new(proxy: DirectoryProxy) -> Self {
Self { proxy }
}
/// Returns the current component's package directory.
pub fn open_from_namespace() -> Result<Self, io_util::node::OpenError> {
let dir =
io_util::directory::open_in_namespace("/pkg", fidl_fuchsia_io::OPEN_RIGHT_READABLE)?;
Ok(Self::new(dir))
}
/// Cleanly close the package directory, consuming self.
pub async fn close(self) -> Result<(), io_util::node::CloseError> {
io_util::directory::close(self.proxy).await
}
/// Ask pkgfs to also serve this package directory on the given directory request.
pub fn reopen(
&self,
dir_request: ServerEnd<DirectoryMarker>,
) -> Result<(), io_util::node::CloneError> {
io_util::directory::clone_onto_no_describe(&self.proxy, None, dir_request)
}
/// Reads the merkle root of the package.
pub async fn merkle_root(&self) -> Result<Hash, anyhow::Error> {
let merkle = self.read_file_to_string("meta").await?;
Ok(merkle.parse()?)
}
/// Open the file in the package given by `path` with the given access `rights`.
pub async fn open_file(
&self,
path: &str,
rights: OpenRights,
) -> Result<FileProxy, io_util::node::OpenError> {
io_util::directory::open_file(&self.proxy, path, rights.to_flags()).await
}
/// Unwraps the inner DirectoryProxy, consuming self.
pub fn into_proxy(self) -> DirectoryProxy {
self.proxy
}
/// Unwraps the inner fidl ClientEnd, consuming self.
pub fn into_client_end(self) -> ClientEnd<DirectoryMarker> {
self.proxy
.into_channel()
.expect("no other users of the wrapped channel")
.into_zx_channel()
.into()
}
/// Returns the list of blobs needed by this package, does not include meta.far blob itself.
pub async fn blobs(&self) -> Result<impl Iterator<Item = Hash>, anyhow::Error> {
let meta_contents = self.read_file_to_string("meta/contents").await?;
let meta_contents = MetaContents::deserialize(meta_contents.as_bytes())?;
Ok(meta_contents.into_contents().into_iter().map(|(_, hash)| hash))
}
async fn read_file_to_string(&self, path: &str) -> Result<String, anyhow::Error> {
let f = self.open_file(path, OpenRights::Read).await?;
Ok(io_util::file::read_to_string(&f).await?)
}
}
/// Possible open rights when opening a file within a package.
#[derive(Debug, Clone, PartialEq, Eq)]
#[allow(missing_docs)]
pub enum OpenRights {
Read,
ReadExecute,
}
impl OpenRights {
fn to_flags(&self) -> u32 {
match self {
OpenRights::Read => fidl_fuchsia_io::OPEN_RIGHT_READABLE,
OpenRights::ReadExecute => {
fidl_fuchsia_io::OPEN_RIGHT_READABLE | fidl_fuchsia_io::OPEN_RIGHT_EXECUTABLE
}
}
}
}
#[cfg(test)]
mod tests {
use {super::*, matches::assert_matches};
#[fuchsia_async::run_singlethreaded(test)]
async fn identity() {
let pkg = Directory::open_from_namespace().unwrap();
let (proxy, server_end) = fidl::endpoints::create_proxy().unwrap();
assert_matches!(pkg.reopen(server_end), Ok(()));
assert_matches!(Directory::new(proxy).close().await, Ok(()));
pkg.into_client_end();
}
#[fuchsia_async::run_singlethreaded(test)]
async fn merkle_root() {
let pkg = Directory::open_from_namespace().unwrap();
let merkle: Hash = std::fs::read_to_string("/pkg/meta").unwrap().parse().unwrap();
assert_eq!(pkg.merkle_root().await.unwrap(), merkle);
}
}