blob: 00c8d04c9678f86bfd7e0ead9bf39f4547e89409 [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/versions filesystem.
use {
crate::package,
fidl_fuchsia_io::{DirectoryMarker, DirectoryProxy, DirectoryRequestStream},
fuchsia_hash::Hash,
fuchsia_zircon::Status,
};
/// An open handle to /pkgfs/versions
#[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/versions",
fidl_fuchsia_io::OPEN_RIGHT_READABLE,
)?;
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,
"versions",
fidl_fuchsia_io::OPEN_RIGHT_READABLE,
)?,
})
}
/// 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)
}
/// Open the package given by `meta_far_merkle`. Verifies the OnOpen event before returning.
pub async fn open_package(
&self,
meta_far_merkle: &Hash,
) -> Result<package::Directory, package::OpenError> {
// TODO(fxbug.dev/37858) allow opening as executable too
let flags = fidl_fuchsia_io::OPEN_RIGHT_READABLE;
let dir =
io_util::directory::open_directory(&self.proxy, &meta_far_merkle.to_string(), flags)
.await
.map_err(|e| match e {
io_util::node::OpenError::OpenError(Status::NOT_FOUND) => {
package::OpenError::NotFound
}
other => package::OpenError::Io(other),
})?;
Ok(package::Directory::new(dir))
}
}
#[cfg(test)]
mod tests {
use {
super::*, crate::install::BlobKind, fuchsia_pkg_testing::PackageBuilder,
matches::assert_matches, pkgfs_ramdisk::PkgfsRamdisk,
};
#[fuchsia_async::run_singlethreaded(test)]
async fn open_non_existant_package_fails() {
let pkgfs = PkgfsRamdisk::start().unwrap();
let root = pkgfs.root_dir_proxy().unwrap();
let client = Client::open_from_pkgfs_root(&root).unwrap();
let merkle = fuchsia_merkle::MerkleTree::from_reader(std::io::empty()).unwrap().root();
assert_matches!(client.open_package(&merkle).await, Err(package::OpenError::NotFound));
pkgfs.stop().await.unwrap();
}
#[fuchsia_async::run_singlethreaded(test)]
async fn open_package_single_blob() {
let pkgfs = PkgfsRamdisk::start().unwrap();
let root = pkgfs.root_dir_proxy().unwrap();
let install = crate::install::Client::open_from_pkgfs_root(&root).unwrap();
let client = Client::open_from_pkgfs_root(&root).unwrap();
let pkg = PackageBuilder::new("uniblob").build().await.unwrap();
let pkg_merkle = pkg.meta_far_merkle_root().to_owned();
install.write_meta_far(&pkg).await;
assert_matches!(client.open_package(&pkg_merkle).await, Ok(_));
assert_matches!(
pkg.verify_contents(&client.open_package(&pkg_merkle).await.unwrap().into_proxy())
.await,
Ok(())
);
pkgfs.stop().await.unwrap();
}
#[fuchsia_async::run_singlethreaded(test)]
async fn open_package_multiple_blobs() {
let pkgfs = PkgfsRamdisk::start().unwrap();
let root = pkgfs.root_dir_proxy().unwrap();
let install = crate::install::Client::open_from_pkgfs_root(&root).unwrap();
let client = Client::open_from_pkgfs_root(&root).unwrap();
let pkg = PackageBuilder::new("multiblob")
.add_resource_at("data/first", "contents of first blob".as_bytes())
.add_resource_at("data/second", "contents of second blob".as_bytes())
.build()
.await
.unwrap();
let pkg_contents = pkg.meta_contents().unwrap().contents().to_owned();
let pkg_merkle = pkg.meta_far_merkle_root().to_owned();
install.write_meta_far(&pkg).await;
// Package is not complete yet, so opening fails.
assert_matches!(client.open_package(&pkg_merkle).await, Err(package::OpenError::NotFound));
install
.write_blob(
pkg_contents["data/first"],
BlobKind::Data,
"contents of first blob".as_bytes(),
)
.await;
install
.write_blob(
pkg_contents["data/second"],
BlobKind::Data,
"contents of second blob".as_bytes(),
)
.await;
assert_matches!(client.open_package(&pkg_merkle).await, Ok(_));
assert_matches!(
pkg.verify_contents(&client.open_package(&pkg_merkle).await.unwrap().into_proxy())
.await,
Ok(())
);
pkgfs.stop().await.unwrap();
}
}