blob: 81deb0fe0b86429c72d4a359bb7a16ff5cd0051e [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.
#![cfg(test)]
use {
crate::*,
assert_matches::assert_matches,
blobfs_ramdisk::BlobfsRamdisk,
fidl_fuchsia_io as fio, fuchsia_async as fasync,
fuchsia_merkle::Hash,
fuchsia_pkg::{OpenRights, PackageDirectory},
fuchsia_pkg_testing::{Package, PackageBuilder, SystemImageBuilder, VerificationError},
fuchsia_zircon::Status,
pkgfs_ramdisk::PkgfsRamdisk,
std::{
fmt::Debug,
fs,
io::{self, Read, Write},
},
};
/// Helper function implementing the logic for the assert_error_kind! macro
///
/// Returns Ok(io:Error) if the kind matches, otherwise returns Err(String) with the panic message.
fn assert_error_kind_helper<T: Debug>(
result: Result<T, io::Error>,
result_expr: &'static str,
expected: io::ErrorKind,
) -> Result<io::Error, String> {
match result {
Ok(val) => Err(format!(
r"assertion failed: `{}.is_err()`
result: `Ok({:?})`,
expected: `Err({{ kind: {:?}, .. }})`",
result_expr, val, expected
)),
Err(err) if err.kind() == expected => Ok(err),
Err(err) => Err(format!(
r"assertion failed: `{expr}.unwrap_err().kind() == {expected:?}`
err.kind(): `{kind:?}`,
expected: `{expected:?}`
full err: `{full:?}`",
expr = result_expr,
expected = expected,
kind = err.kind(),
full = err,
)),
}
}
macro_rules! assert_error_kind {
($result:expr, $expected:expr) => {{
assert_error_kind_helper($result, stringify!($result), $expected)
.unwrap_or_else(|err_string| panic!("{}", err_string))
}};
($result:expr, $expected:expr,) => {{
assert_error_kind!($result, $expected)
}};
}
#[fasync::run_singlethreaded(test)]
async fn test_pkgfs_short_write() {
let pkgfs = PkgfsRamdisk::builder()
.enforce_packages_non_static_allowlist(false) // turn off allowlist enforcement
.start()
.unwrap();
let blobfs_root_dir = pkgfs.blobfs().root_dir().unwrap();
let d = pkgfs.root_dir().expect("getting pkgfs root dir");
let pkg = example_package().await;
assert_eq!(
pkg.meta_far_merkle_root(),
&"dac936c184d9258e892591d6f060f4cbecbe6789b7d3bb993b1d1b26d32c344b"
.parse::<fuchsia_merkle::Hash>()
.unwrap()
);
let mut meta_far = pkg.meta_far().expect("meta.far");
{
let mut to_write = d
.new_file(
"install/pkg/dac936c184d9258e892591d6f060f4cbecbe6789b7d3bb993b1d1b26d32c344b",
0600,
)
.expect("create install file");
to_write.set_len(meta_far.metadata().unwrap().len()).expect("truncate meta.far");
io::copy(&mut meta_far, &mut to_write).expect("write meta.far");
}
assert_eq!(
ls_simple(
d.list_dir(
"needs/packages/dac936c184d9258e892591d6f060f4cbecbe6789b7d3bb993b1d1b26d32c344b"
)
.expect("list dir")
)
.expect("list dir contents"),
["e5892a9b652ede2e19460a9103fd9cb3417f782a8d29f6c93ec0c31170a94af3"]
);
// Short blob write
{
let mut blob_install = d
.new_file(
"install/blob/e5892a9b652ede2e19460a9103fd9cb3417f782a8d29f6c93ec0c31170a94af3",
0600,
)
.expect("create blob install file");
let blob_contents = b"Hello world!\n";
blob_install.set_len(blob_contents.len() as u64).expect("truncate blob");
blob_install.write_all(b"Hello world!").expect("write blob");
}
// Blob still needed
assert_eq!(
ls_simple(
d.list_dir(
"needs/packages/dac936c184d9258e892591d6f060f4cbecbe6789b7d3bb993b1d1b26d32c344b"
)
.expect("list dir")
)
.expect("list dir contents"),
["e5892a9b652ede2e19460a9103fd9cb3417f782a8d29f6c93ec0c31170a94af3"]
);
// Full blob write
{
let mut blob_install = d
.new_file(
"install/blob/e5892a9b652ede2e19460a9103fd9cb3417f782a8d29f6c93ec0c31170a94af3",
0600,
)
.expect("create blob install file");
let blob_contents = b"Hello world!\n";
blob_install.set_len(blob_contents.len() as u64).expect("truncate blob");
blob_install.write_all(blob_contents).expect("write blob");
}
// Blob needs no more packages
assert_error_kind!(
d.list_dir(
"needs/packages/dac936c184d9258e892591d6f060f4cbecbe6789b7d3bb993b1d1b26d32c344b",
),
io::ErrorKind::NotFound,
);
verify_contents(&pkg, subdir_proxy(&d, "packages/example/0"))
.await
.expect("valid example package");
let mut file_contents = String::new();
d.open_file("versions/dac936c184d9258e892591d6f060f4cbecbe6789b7d3bb993b1d1b26d32c344b/a/b")
.expect("read package file")
.read_to_string(&mut file_contents)
.expect("read package file");
assert_eq!(&file_contents, "Hello world!\n");
assert_eq!(
ls_simple(blobfs_root_dir.list_dir(".").expect("list dir")).expect("list dir contents"),
[
"dac936c184d9258e892591d6f060f4cbecbe6789b7d3bb993b1d1b26d32c344b",
"e5892a9b652ede2e19460a9103fd9cb3417f782a8d29f6c93ec0c31170a94af3"
],
);
drop(d);
pkgfs.stop().await.expect("stopping pkgfs");
}
#[fasync::run_singlethreaded(test)]
async fn test_pkgfs_restart_install() {
let pkgfs = PkgfsRamdisk::builder()
.enforce_packages_non_static_allowlist(false) // turn off allowlist enforcement
.start()
.unwrap();
let blobfs_root_dir = pkgfs.blobfs().root_dir().unwrap();
let d = pkgfs.root_dir().expect("getting pkgfs root dir");
let pkg = example_package().await;
assert_eq!(
pkg.meta_far_merkle_root(),
&"dac936c184d9258e892591d6f060f4cbecbe6789b7d3bb993b1d1b26d32c344b"
.parse::<fuchsia_merkle::Hash>()
.unwrap()
);
// Start package install
// first, some checks to see if it's already installed
assert_error_kind!(
d.metadata("versions/dac936c184d9258e892591d6f060f4cbecbe6789b7d3bb993b1d1b26d32c344b")
.map(|m| m.is_dir()),
io::ErrorKind::NotFound,
);
assert_error_kind!(
d.metadata(
"needs/packages/dac936c184d9258e892591d6f060f4cbecbe6789b7d3bb993b1d1b26d32c344b"
)
.map(|m| m.is_dir()),
io::ErrorKind::NotFound,
);
// Install the meta.far
{
let mut meta_far = pkg.meta_far().expect("meta.far");
let mut to_write = d
.new_file(
"install/pkg/dac936c184d9258e892591d6f060f4cbecbe6789b7d3bb993b1d1b26d32c344b",
0600,
)
.expect("create install file");
to_write.set_len(meta_far.metadata().unwrap().len()).expect("truncate meta.far");
io::copy(&mut meta_far, &mut to_write).expect("write meta.far");
}
assert_eq!(
ls_simple(
d.list_dir(
"needs/packages/dac936c184d9258e892591d6f060f4cbecbe6789b7d3bb993b1d1b26d32c344b"
)
.expect("list dir")
)
.expect("list dir contents"),
["e5892a9b652ede2e19460a9103fd9cb3417f782a8d29f6c93ec0c31170a94af3"]
);
// Short blob write
{
let mut blob_install = d
.new_file(
"install/blob/e5892a9b652ede2e19460a9103fd9cb3417f782a8d29f6c93ec0c31170a94af3",
0600,
)
.expect("create blob install file");
let blob_contents = b"Hello world!\n";
blob_install.set_len(blob_contents.len() as u64).expect("truncate blob");
blob_install.write_all(b"Hello world!").expect("write blob");
}
// Blob still needed
assert_eq!(
ls_simple(
d.list_dir(
"needs/packages/dac936c184d9258e892591d6f060f4cbecbe6789b7d3bb993b1d1b26d32c344b"
)
.expect("list dir")
)
.expect("list dir contents"),
["e5892a9b652ede2e19460a9103fd9cb3417f782a8d29f6c93ec0c31170a94af3"]
);
// Restart pkgfs (without dynamic index)
drop(d);
let pkgfs = pkgfs.restart().expect("restarting pkgfs");
let d = pkgfs.root_dir().expect("getting pkgfs root dir");
// Restart package install
assert_error_kind!(
d.metadata("versions/dac936c184d9258e892591d6f060f4cbecbe6789b7d3bb993b1d1b26d32c344b")
.map(|m| m.is_dir()),
io::ErrorKind::NotFound
);
assert_error_kind!(
d.metadata(
"needs/packages/dac936c184d9258e892591d6f060f4cbecbe6789b7d3bb993b1d1b26d32c344b"
)
.map(|m| m.is_dir()),
io::ErrorKind::NotFound
);
// Retry installing meta.far (fails with AlreadyExists)
assert_error_kind!(
d.new_file(
"install/pkg/dac936c184d9258e892591d6f060f4cbecbe6789b7d3bb993b1d1b26d32c344b",
0600,
)
.map(|_| ()),
io::ErrorKind::AlreadyExists
);
assert_error_kind!(
d.metadata("versions/dac936c184d9258e892591d6f060f4cbecbe6789b7d3bb993b1d1b26d32c344b")
.map(|m| m.is_dir()),
io::ErrorKind::NotFound
);
// Check needs again.
assert_eq!(
d.metadata(
"needs/packages/dac936c184d9258e892591d6f060f4cbecbe6789b7d3bb993b1d1b26d32c344b"
)
.map(|m| m.is_dir())
.map_err(|e| format!("{:?}", e)),
Ok(true)
);
// Needs exists, so we don't need to worry about meta.far write having failed.
assert_eq!(
ls_simple(
d.list_dir(
"needs/packages/dac936c184d9258e892591d6f060f4cbecbe6789b7d3bb993b1d1b26d32c344b"
)
.expect("list dir")
)
.expect("list dir contents"),
["e5892a9b652ede2e19460a9103fd9cb3417f782a8d29f6c93ec0c31170a94af3"]
);
// Full blob write
{
let mut blob_install = d
.new_file(
"install/blob/e5892a9b652ede2e19460a9103fd9cb3417f782a8d29f6c93ec0c31170a94af3",
0600,
)
.expect("create blob install file");
let blob_contents = b"Hello world!\n";
blob_install.set_len(blob_contents.len() as u64).expect("truncate blob");
blob_install.write_all(blob_contents).expect("write blob");
}
// Blob Needs no more packages
assert_error_kind!(
d.list_dir(
"needs/packages/dac936c184d9258e892591d6f060f4cbecbe6789b7d3bb993b1d1b26d32c344b"
),
io::ErrorKind::NotFound
);
verify_contents(&pkg, subdir_proxy(&d, "packages/example/0"))
.await
.expect("valid example package");
let mut file_contents = String::new();
d.open_file("versions/dac936c184d9258e892591d6f060f4cbecbe6789b7d3bb993b1d1b26d32c344b/a/b")
.expect("read versions file")
.read_to_string(&mut file_contents)
.expect("read versions file");
assert_eq!(&file_contents, "Hello world!\n");
assert_eq!(
ls_simple(blobfs_root_dir.list_dir(".").expect("list dir")).expect("list dir contents"),
[
"dac936c184d9258e892591d6f060f4cbecbe6789b7d3bb993b1d1b26d32c344b",
"e5892a9b652ede2e19460a9103fd9cb3417f782a8d29f6c93ec0c31170a94af3"
],
);
drop(d);
pkgfs.stop().await.expect("stopping pkgfs");
}
#[fasync::run_singlethreaded(test)]
async fn test_pkgfs_restart_install_already_done() {
let pkgfs = PkgfsRamdisk::builder()
.enforce_packages_non_static_allowlist(false) // turn off allowlist enforcement
.start()
.unwrap();
let blobfs_root_dir = pkgfs.blobfs().root_dir().unwrap();
let d = pkgfs.root_dir().expect("getting pkgfs root dir");
let pkg = example_package().await;
assert_eq!(
pkg.meta_far_merkle_root(),
&"dac936c184d9258e892591d6f060f4cbecbe6789b7d3bb993b1d1b26d32c344b"
.parse::<fuchsia_merkle::Hash>()
.unwrap()
);
// Start package install
// first, some checks to see if it's already installed
assert_error_kind!(
d.metadata("versions/dac936c184d9258e892591d6f060f4cbecbe6789b7d3bb993b1d1b26d32c344b")
.map(|m| m.is_dir()),
io::ErrorKind::NotFound
);
assert_error_kind!(
d.metadata(
"needs/packages/dac936c184d9258e892591d6f060f4cbecbe6789b7d3bb993b1d1b26d32c344b"
)
.map(|m| m.is_dir()),
io::ErrorKind::NotFound
);
// Install the meta.far
{
let mut meta_far = pkg.meta_far().expect("meta.far");
let mut to_write = d
.new_file(
"install/pkg/dac936c184d9258e892591d6f060f4cbecbe6789b7d3bb993b1d1b26d32c344b",
0600,
)
.expect("create install file");
to_write.set_len(meta_far.metadata().unwrap().len()).expect("truncate meta.far");
io::copy(&mut meta_far, &mut to_write).expect("write meta.far");
}
assert_eq!(
ls_simple(
d.list_dir(
"needs/packages/dac936c184d9258e892591d6f060f4cbecbe6789b7d3bb993b1d1b26d32c344b"
)
.expect("list dir")
)
.expect("list dir contents"),
["e5892a9b652ede2e19460a9103fd9cb3417f782a8d29f6c93ec0c31170a94af3"]
);
// blob write direct to blobfs
{
let mut blob_install = blobfs_root_dir
.new_file("e5892a9b652ede2e19460a9103fd9cb3417f782a8d29f6c93ec0c31170a94af3", 0600)
.expect("create blob install file");
let blob_contents = b"Hello world!\n";
blob_install.set_len(blob_contents.len() as u64).expect("truncate blob");
blob_install.write_all(blob_contents).expect("write blob");
}
// Blob still needed
assert_eq!(
ls_simple(
d.list_dir(
"needs/packages/dac936c184d9258e892591d6f060f4cbecbe6789b7d3bb993b1d1b26d32c344b"
)
.expect("list dir")
)
.expect("list dir contents"),
["e5892a9b652ede2e19460a9103fd9cb3417f782a8d29f6c93ec0c31170a94af3"]
);
// Restart pkgfs (without dynamic index)
drop(d);
let pkgfs = pkgfs.restart().expect("restarting pkgfs");
let d = pkgfs.root_dir().expect("getting pkgfs root dir");
// Restart package install
assert_error_kind!(
d.metadata("versions/dac936c184d9258e892591d6f060f4cbecbe6789b7d3bb993b1d1b26d32c344b")
.map(|m| m.is_dir()),
io::ErrorKind::NotFound
);
assert_error_kind!(
d.metadata(
"needs/packages/dac936c184d9258e892591d6f060f4cbecbe6789b7d3bb993b1d1b26d32c344b"
)
.map(|m| m.is_dir()),
io::ErrorKind::NotFound
);
// Retry installing meta.far (fails with Invalid argument)
d.new_file(
"install/pkg/dac936c184d9258e892591d6f060f4cbecbe6789b7d3bb993b1d1b26d32c344b",
0600,
)
.expect_err("already exists");
// Recheck versions
assert_eq!(
d.metadata("versions/dac936c184d9258e892591d6f060f4cbecbe6789b7d3bb993b1d1b26d32c344b")
.map(|m| m.is_dir())
.map_err(|e| format!("{:?}", e)),
Ok(true)
);
// Check needs again.
assert_error_kind!(
d.metadata(
"needs/packages/dac936c184d9258e892591d6f060f4cbecbe6789b7d3bb993b1d1b26d32c344b"
)
.map(|m| m.is_dir()),
io::ErrorKind::NotFound
);
verify_contents(&pkg, subdir_proxy(&d, "packages/example/0"))
.await
.expect("valid example package");
let mut file_contents = String::new();
d.open_file("versions/dac936c184d9258e892591d6f060f4cbecbe6789b7d3bb993b1d1b26d32c344b/a/b")
.expect("read package file")
.read_to_string(&mut file_contents)
.expect("read package file");
assert_eq!(&file_contents, "Hello world!\n");
assert_eq!(
ls_simple(blobfs_root_dir.list_dir(".").expect("list dir")).expect("list dir contents"),
[
"dac936c184d9258e892591d6f060f4cbecbe6789b7d3bb993b1d1b26d32c344b",
"e5892a9b652ede2e19460a9103fd9cb3417f782a8d29f6c93ec0c31170a94af3"
],
);
drop(d);
pkgfs.stop().await.expect("stopping pkgfs");
}
#[fasync::run_singlethreaded(test)]
async fn test_pkgfs_restart_install_failed_meta_far() {
let pkgfs = PkgfsRamdisk::start().expect("starting pkgfs");
let blobfs_root_dir = pkgfs.blobfs().root_dir().unwrap();
let d = pkgfs.root_dir().expect("getting pkgfs root dir");
let pkg = example_package().await;
assert_eq!(
pkg.meta_far_merkle_root(),
&"dac936c184d9258e892591d6f060f4cbecbe6789b7d3bb993b1d1b26d32c344b"
.parse::<fuchsia_merkle::Hash>()
.unwrap()
);
// Start package install
// first, some checks to see if it's already installed
assert_error_kind!(
d.metadata("versions/dac936c184d9258e892591d6f060f4cbecbe6789b7d3bb993b1d1b26d32c344b")
.map(|m| m.is_dir()),
io::ErrorKind::NotFound
);
assert_error_kind!(
d.metadata(
"needs/packages/dac936c184d9258e892591d6f060f4cbecbe6789b7d3bb993b1d1b26d32c344b"
)
.map(|m| m.is_dir()),
io::ErrorKind::NotFound
);
// Create (but don't write) the meta.far
d.new_file(
"install/pkg/dac936c184d9258e892591d6f060f4cbecbe6789b7d3bb993b1d1b26d32c344b",
0600,
)
.expect("create install file");
assert_error_kind!(
d.metadata("versions/dac936c184d9258e892591d6f060f4cbecbe6789b7d3bb993b1d1b26d32c344b")
.map(|m| m.is_dir()),
io::ErrorKind::NotFound
);
assert_eq!(
ls_simple(d.list_dir("needs/packages").expect("list dir")).expect("list dir contents"),
["dac936c184d9258e892591d6f060f4cbecbe6789b7d3bb993b1d1b26d32c344b"]
);
assert_eq!(
ls_simple(
d.list_dir(
"needs/packages/dac936c184d9258e892591d6f060f4cbecbe6789b7d3bb993b1d1b26d32c344b"
)
.expect("list dir")
)
.expect("list dir contents"),
Vec::<&str>::new()
);
assert_eq!(
ls_simple(blobfs_root_dir.list_dir(".").expect("list dir")).expect("list dir contents"),
Vec::<&str>::new()
);
drop(d);
pkgfs.stop().await.expect("stopping pkgfs");
}
#[fasync::run_singlethreaded(test)]
async fn test_pkgfs_with_empty_static_index() {
let system_image_package = SystemImageBuilder::new().build().await;
let blobfs = BlobfsRamdisk::start().unwrap();
system_image_package.write_to_blobfs_dir(&blobfs.root_dir().unwrap());
let pkgfs = PkgfsRamdisk::builder()
.blobfs(blobfs)
.system_image_merkle(system_image_package.meta_far_merkle_root())
.start()
.unwrap();
let d = pkgfs.root_dir().expect("getting pkgfs root dir");
verify_contents(&system_image_package, subdir_proxy(&d, "packages/system_image/0"))
.await
.expect("valid /packages/system_image/0");
verify_contents(&system_image_package, subdir_proxy(&d, "system"))
.await
.expect("valid /system");
verify_contents(
&system_image_package,
subdir_proxy(&d, &format!("versions/{}", system_image_package.meta_far_merkle_root())),
)
.await
.expect("system_image in /versions");
drop(d);
pkgfs.stop().await.expect("stopping pkgfs");
}
#[fasync::run_singlethreaded(test)]
async fn test_pkgfs_with_system_image_meta_far_missing() {
let blobfs = BlobfsRamdisk::start().unwrap();
// Arbitrarily pick a system_image merkle (that isn't present)
let system_image_merkle: Hash =
"22e41860aa333dec2aea3899aa764a53a6ea7c179e6c47bf3a8163d89024343e".parse().unwrap();
let pkgfs = PkgfsRamdisk::builder()
.blobfs(blobfs)
.system_image_merkle(&system_image_merkle)
.start()
.unwrap();
let d = pkgfs.root_dir().expect("getting pkgfs root dir");
assert_error_kind!(d.open_file("packages/system_image/0/meta"), io::ErrorKind::NotFound);
assert_eq!(
ls_simple(d.list_dir("system").unwrap()).unwrap_err().raw_os_error(),
Some(libc::EOPNOTSUPP),
);
drop(d);
pkgfs.stop().await.expect("stopping pkgfs");
}
#[fasync::run_singlethreaded(test)]
async fn test_pkgfs_with_system_image_base_package_missing() {
let pkg = example_package().await;
let system_image_package = SystemImageBuilder::new().static_packages(&[&pkg]).build().await;
let blobfs = BlobfsRamdisk::start().unwrap();
system_image_package.write_to_blobfs_dir(&blobfs.root_dir().unwrap());
let pkgfs = PkgfsRamdisk::builder()
.blobfs(blobfs)
.system_image_merkle(system_image_package.meta_far_merkle_root())
.start()
.unwrap();
let d = pkgfs.root_dir().expect("getting pkgfs root dir");
verify_contents(&system_image_package, subdir_proxy(&d, "system"))
.await
.expect("valid system_image");
assert_eq!(sorted(ls(&pkgfs, "packages").unwrap()), ["example", "system_image"]);
assert_error_kind!(d.list_dir("packages/example/0"), io::ErrorKind::NotFound);
drop(d);
pkgfs.stop().await.expect("stopping pkgfs");
}
#[fasync::run_singlethreaded(test)]
async fn test_pkgfs_with_system_image_base_package_missing_content_blob() {
let pkg = example_package().await;
let system_image_package = SystemImageBuilder::new().static_packages(&[&pkg]).build().await;
let blobfs = BlobfsRamdisk::start().unwrap();
system_image_package.write_to_blobfs_dir(&blobfs.root_dir().unwrap());
blobfs.add_blob_from(&pkg.meta_far_merkle_root(), pkg.meta_far().unwrap()).unwrap();
let pkgfs = PkgfsRamdisk::builder()
.blobfs(blobfs)
.system_image_merkle(system_image_package.meta_far_merkle_root())
.start()
.unwrap();
let d = pkgfs.root_dir().expect("getting pkgfs root dir");
verify_contents(&system_image_package, subdir_proxy(&d, "system"))
.await
.expect("valid system_image");
assert_eq!(sorted(ls(&pkgfs, "packages").unwrap()), ["example", "system_image"]);
let mut file_contents = String::new();
d.open_file("packages/example/0/meta")
.expect("example should be present")
.read_to_string(&mut file_contents)
.expect("example meta should be readable");
assert_eq!(file_contents.parse(), Ok(*pkg.meta_far_merkle_root()));
// Can even list the package
let contents = ls_simple(d.list_dir("packages/example/0/a").unwrap()).unwrap();
assert_eq!(contents, &["b"]);
// Can't read the file
assert_matches!(
verify_contents(&pkg, subdir_proxy(&d, "packages/example/0")).await,
Err(VerificationError::MissingFile { path }) if path == "a/b"
);
drop(d);
pkgfs.stop().await.expect("stopping pkgfs");
}
#[fasync::run_singlethreaded(test)]
async fn test_pkgfs_packages_doesnt_show_base_package_updates() {
let original_base_pkg = PackageBuilder::new("example")
.add_resource_at("a/b", "Hello world!\n".as_bytes())
.build()
.await
.expect("build package");
let system_image_package =
SystemImageBuilder::new().static_packages(&[&original_base_pkg]).build().await;
let blobfs = BlobfsRamdisk::start().unwrap();
system_image_package.write_to_blobfs_dir(&blobfs.root_dir().unwrap());
original_base_pkg.write_to_blobfs_dir(&blobfs.root_dir().unwrap());
let pkgfs = PkgfsRamdisk::builder()
.blobfs(blobfs)
.system_image_merkle(system_image_package.meta_far_merkle_root())
.start()
.unwrap();
let d = pkgfs.root_dir().expect("getting pkgfs root dir");
// When we start, pkgfs/packages contains the original base pkg
verify_contents(&original_base_pkg, subdir_proxy(&d, "packages/example/0"))
.await
.expect("valid system_image");
let expected_versions = vec![
original_base_pkg.meta_far_merkle_root().to_string(),
system_image_package.meta_far_merkle_root().to_string(),
];
assert_eq!(sorted(ls(&pkgfs, "versions").unwrap()), sorted(expected_versions));
// Update the base package
let updated_base_pkg = PackageBuilder::new("example")
.add_resource_at("a/b", "UPDATED Hello world!\n".as_bytes())
.build()
.await
.expect("build package");
install(&pkgfs, &updated_base_pkg);
// pkgfs/packages SHOULD NOT see the update, whereas pkgfs/versions SHOULD
verify_contents(&original_base_pkg, subdir_proxy(&d, "packages/example/0"))
.await
.expect("valid system_image");
let expected_versions: Vec<String> =
vec![original_base_pkg, updated_base_pkg, system_image_package]
.into_iter()
.map(|pkg| pkg.meta_far_merkle_root().to_string())
.collect();
assert_eq!(sorted(ls(&pkgfs, "versions").unwrap()), sorted(expected_versions));
// Drop the directory before we shutdown the server that's serving it.
// In practice, this probably doesn't make a difference.
drop(d);
pkgfs.stop().await.expect("stopping pkgfs");
}
#[fasync::run_singlethreaded(test)]
async fn test_pkgfs_restart_deactivates_ephemeral_packages() {
let pkgfs = PkgfsRamdisk::builder()
.enforce_packages_non_static_allowlist(false) // turn off allowlist enforcement
.start()
.unwrap();
let d = pkgfs.root_dir().expect("getting pkgfs root dir");
let pkg = example_package().await;
install(&pkgfs, &pkg);
assert_eq!(ls(&pkgfs, "packages").unwrap(), ["example"]);
assert_eq!(ls_simple(d.list_dir("packages/example").unwrap()).unwrap(), ["0"]);
verify_contents(&pkg, subdir_proxy(&d, "packages/example/0"))
.await
.expect("valid example package");
drop(d);
let pkgfs = pkgfs.restart().unwrap();
let d = pkgfs.root_dir().expect("getting pkgfs root dir");
// package is no longer accesible.
assert_eq!(ls(&pkgfs, "packages").unwrap(), Vec::<&str>::new());
assert_eq!(ls(&pkgfs, "versions").unwrap(), Vec::<&str>::new());
assert_error_kind!(
d.metadata("packages/example/0").map(|m| m.is_dir()),
io::ErrorKind::NotFound
);
assert_error_kind!(
d.metadata(&format!("versions/{}", pkg.meta_far_merkle_root())).map(|m| m.is_dir()),
io::ErrorKind::NotFound
);
drop(d);
pkgfs.stop().await.expect("stopping pkgfs");
}
#[fasync::run_singlethreaded(test)]
async fn test_pkgfs_with_cache_index() {
let pkg = example_package().await;
let system_image_package = SystemImageBuilder::new()
.cache_packages(&[&pkg])
.pkgfs_non_static_packages_allowlist(&["example"])
.build()
.await;
let blobfs = BlobfsRamdisk::start().unwrap();
system_image_package.write_to_blobfs_dir(&blobfs.root_dir().unwrap());
pkg.write_to_blobfs_dir(&blobfs.root_dir().unwrap());
let pkgfs = PkgfsRamdisk::builder()
.blobfs(blobfs)
.system_image_merkle(system_image_package.meta_far_merkle_root())
.start()
.unwrap();
let d = pkgfs.root_dir().expect("getting pkgfs root dir");
verify_contents(&system_image_package, subdir_proxy(&d, "system"))
.await
.expect("valid system_image");
assert_eq!(sorted(ls(&pkgfs, "packages").unwrap()), ["example", "system_image"]);
verify_contents(&pkg, subdir_proxy(&d, "packages/example/0"))
.await
.expect("valid example package");
drop(d);
pkgfs.stop().await.expect("stopping pkgfs");
}
#[fasync::run_singlethreaded(test)]
async fn test_pkgfs_with_cache_index_missing_cache_meta_far() {
let pkg = example_package().await;
let system_image_package = SystemImageBuilder::new().cache_packages(&[&pkg]).build().await;
let blobfs = BlobfsRamdisk::start().unwrap();
system_image_package.write_to_blobfs_dir(&blobfs.root_dir().unwrap());
let pkgfs = PkgfsRamdisk::builder()
.blobfs(blobfs)
.system_image_merkle(system_image_package.meta_far_merkle_root())
.start()
.unwrap();
let d = pkgfs.root_dir().expect("getting pkgfs root dir");
verify_contents(&system_image_package, subdir_proxy(&d, "system"))
.await
.expect("valid system_image");
assert_eq!(ls(&pkgfs, "packages").unwrap(), ["system_image"]);
assert_error_kind!(d.open_file("packages/example/0/meta"), io::ErrorKind::NotFound);
assert_error_kind!(
d.open_file(format!("versions/{}/meta", pkg.meta_far_merkle_root())),
io::ErrorKind::NotFound
);
drop(d);
pkgfs.stop().await.expect("stopping pkgfs");
}
#[fasync::run_singlethreaded(test)]
async fn test_pkgfs_with_cache_index_missing_cache_content_blob() {
let pkg = example_package().await;
let system_image_package = SystemImageBuilder::new().cache_packages(&[&pkg]).build().await;
let blobfs = BlobfsRamdisk::start().unwrap();
system_image_package.write_to_blobfs_dir(&blobfs.root_dir().unwrap());
blobfs.add_blob_from(&pkg.meta_far_merkle_root(), pkg.meta_far().unwrap()).unwrap();
let pkgfs = PkgfsRamdisk::builder()
.blobfs(blobfs)
.system_image_merkle(system_image_package.meta_far_merkle_root())
.start()
.unwrap();
let d = pkgfs.root_dir().expect("getting pkgfs root dir");
verify_contents(&system_image_package, subdir_proxy(&d, "system"))
.await
.expect("valid system_image");
assert_eq!(ls(&pkgfs, "packages").unwrap(), ["system_image"]);
assert_error_kind!(d.open_file("packages/example/0/meta"), io::ErrorKind::NotFound);
assert_error_kind!(
d.open_file(format!("versions/{}/meta", pkg.meta_far_merkle_root())),
io::ErrorKind::NotFound,
);
assert_eq!(ls_simple(d.list_dir("needs/packages").unwrap()).unwrap(), Vec::<&str>::new());
drop(d);
pkgfs.stop().await.expect("stopping pkgfs");
}
#[fasync::run_singlethreaded(test)]
async fn test_pkgfs_restart_reveals_shadowed_cache_package() {
let pkg = example_package().await;
let system_image_package = SystemImageBuilder::new()
.cache_packages(&[&pkg])
.pkgfs_non_static_packages_allowlist(&["example"])
.build()
.await;
let blobfs = BlobfsRamdisk::start().unwrap();
system_image_package.write_to_blobfs_dir(&blobfs.root_dir().unwrap());
pkg.write_to_blobfs_dir(&blobfs.root_dir().unwrap());
let pkgfs = PkgfsRamdisk::builder()
.blobfs(blobfs)
.system_image_merkle(system_image_package.meta_far_merkle_root())
.start()
.unwrap();
let d = pkgfs.root_dir().expect("getting pkgfs root dir");
let pkg2 = PackageBuilder::new("example")
.add_resource_at("a/b", "Hello world 2!\n".as_bytes())
.build()
.await
.expect("build package");
install(&pkgfs, &pkg2);
verify_contents(&pkg2, subdir_proxy(&d, "packages/example/0"))
.await
.expect("pkg2 replaced pkg");
// cache version is inaccessible
assert_error_kind!(
d.metadata(&format!("versions/{}", pkg.meta_far_merkle_root())).map(|m| m.is_dir()),
io::ErrorKind::NotFound
);
drop(d);
let pkgfs = pkgfs.restart().unwrap();
let d = pkgfs.root_dir().expect("getting pkgfs root dir");
// cache version is accessible again.
verify_contents(&pkg, subdir_proxy(&d, "packages/example/0")).await.unwrap();
verify_contents(&pkg, subdir_proxy(&d, &format!("versions/{}", pkg.meta_far_merkle_root())))
.await
.unwrap();
// updated version is gone
assert_error_kind!(
d.metadata(&format!("versions/{}", pkg2.meta_far_merkle_root())).map(|m| m.is_dir()),
io::ErrorKind::NotFound
);
drop(d);
pkgfs.stop().await.expect("stopping pkgfs");
}
#[fasync::run_singlethreaded(test)]
async fn test_pkgfs() {
let pkgfs = PkgfsRamdisk::builder()
.enforce_packages_non_static_allowlist(false) // turn off allowlist enforcement
.start()
.unwrap();
let blobfs_root_dir = pkgfs.blobfs().root_dir().unwrap();
let d = pkgfs.root_dir().unwrap();
let pkg = PackageBuilder::new("example")
.add_resource_at("a/b", "Hello world!\n".as_bytes())
.build()
.await
.expect("build package");
// merkle root of pkg's meta.far.
const META_FAR_MERKLE_ROOT: &'static str =
"dac936c184d9258e892591d6f060f4cbecbe6789b7d3bb993b1d1b26d32c344b";
assert_eq!(
pkg.meta_far_merkle_root(),
&META_FAR_MERKLE_ROOT.parse::<fuchsia_merkle::Hash>().unwrap()
);
// merkle root of "Hello world!\n", the single blob in pkg.
const CONTENT_BLOB_MERKLE_ROOT: &'static str =
"e5892a9b652ede2e19460a9103fd9cb3417f782a8d29f6c93ec0c31170a94af3";
assert_eq!(
pkg.meta_contents().unwrap().contents()["a/b"],
CONTENT_BLOB_MERKLE_ROOT.parse::<fuchsia_merkle::Hash>().unwrap()
);
let mut meta_far = pkg.meta_far().expect("meta.far");
{
let mut to_write = d
.new_file(
"install/pkg/dac936c184d9258e892591d6f060f4cbecbe6789b7d3bb993b1d1b26d32c344b",
0600,
)
.expect("create install file");
to_write.set_len(meta_far.metadata().unwrap().len()).expect("set_len meta.far");
std::io::copy(&mut meta_far, &mut to_write).expect("write meta.far");
}
assert_eq!(
ls_simple(
d.list_dir(
"needs/packages/dac936c184d9258e892591d6f060f4cbecbe6789b7d3bb993b1d1b26d32c344b"
)
.expect("list dir")
)
.expect("list dir contents"),
[CONTENT_BLOB_MERKLE_ROOT]
);
// Full blob write
{
let mut blob_install = d
.new_file(
"install/blob/e5892a9b652ede2e19460a9103fd9cb3417f782a8d29f6c93ec0c31170a94af3",
0600,
)
.expect("create blob install file");
let blob_contents = b"Hello world!\n";
blob_install.set_len(blob_contents.len() as u64).expect("truncate blob");
blob_install.write_all(blob_contents).expect("write blob");
}
// Blob Needs no more packages
assert_eq!(
d.list_dir(
"needs/packages/dac936c184d9258e892591d6f060f4cbecbe6789b7d3bb993b1d1b26d32c344b"
)
.expect_err("check empty needs dir")
.kind(),
std::io::ErrorKind::NotFound
);
let mut file_contents = String::new();
d.open_file("packages/example/0/a/b")
.expect("read package file")
.read_to_string(&mut file_contents)
.expect("read package file");
assert_eq!(&file_contents, "Hello world!\n");
let mut file_contents = String::new();
d.open_file("versions/dac936c184d9258e892591d6f060f4cbecbe6789b7d3bb993b1d1b26d32c344b/a/b")
.expect("read package file")
.read_to_string(&mut file_contents)
.expect("read package file");
assert_eq!(&file_contents, "Hello world!\n");
assert_eq!(
ls_simple(blobfs_root_dir.list_dir(".").expect("list dir")).expect("list dir contents"),
[META_FAR_MERKLE_ROOT, CONTENT_BLOB_MERKLE_ROOT,],
);
drop(d);
pkgfs.stop().await.unwrap();
}
#[fasync::run_singlethreaded(test)]
async fn test_pkgfs_with_system_image() {
let pkg = PackageBuilder::new("example")
.add_resource_at("a/b", "Hello world!\n".as_bytes())
.build()
.await
.expect("build package");
let system_image_package = SystemImageBuilder::new().static_packages(&[&pkg]).build().await;
let blobfs = BlobfsRamdisk::start().unwrap();
system_image_package.write_to_blobfs_dir(&blobfs.root_dir().unwrap());
pkg.write_to_blobfs_dir(&blobfs.root_dir().unwrap());
let pkgfs = PkgfsRamdisk::builder()
.blobfs(blobfs)
.system_image_merkle(system_image_package.meta_far_merkle_root())
.start()
.unwrap();
let d = pkgfs.root_dir().expect("getting pkgfs root dir");
let mut file_contents = String::new();
d.open_file("packages/example/0/a/b")
.expect("read package file1")
.read_to_string(&mut file_contents)
.expect("read package file2");
assert_eq!(&file_contents, "Hello world!\n");
let mut file_contents = String::new();
d.open_file(format!("versions/{}/a/b", pkg.meta_far_merkle_root()))
.expect("read package file3")
.read_to_string(&mut file_contents)
.expect("read package file4");
assert_eq!(&file_contents, "Hello world!\n");
drop(d);
pkgfs.stop().await.expect("shutting down pkgfs");
}
#[fasync::run_singlethreaded(test)]
async fn test_pkgfs_packages_dynamic_packages_allowlist_succeeds() {
let pkg = example_package().await;
let system_image_package = SystemImageBuilder::new()
.cache_packages(&[&pkg])
.pkgfs_non_static_packages_allowlist(&["example"])
.build()
.await;
let blobfs = BlobfsRamdisk::start().unwrap();
system_image_package.write_to_blobfs_dir(&blobfs.root_dir().unwrap());
pkg.write_to_blobfs_dir(&blobfs.root_dir().unwrap());
let pkgfs = PkgfsRamdisk::builder()
.blobfs(blobfs)
.system_image_merkle(system_image_package.meta_far_merkle_root())
.start()
.unwrap();
let d = pkgfs.root_dir().expect("getting pkgfs root dir");
// We've put the 'example' package on the allowlist, so we should be
// able to read files from it through pkgfs/packages
let mut file_contents = String::new();
d.open_file("packages/example/0/a/b")
.expect("read package file1")
.read_to_string(&mut file_contents)
.expect("read package file2");
assert_eq!(&file_contents, "Hello world!\n");
// We should be able to see our package in the /packages directory
assert_eq!(
sorted(ls_simple(d.list_dir("packages").expect("list dir")).expect("list dir contents")),
["example", "system_image"]
);
drop(d);
pkgfs.stop().await.expect("shutting down pkgfs");
}
#[fasync::run_singlethreaded(test)]
async fn test_pkgfs_packages_dynamic_packages_allowlist_fails() {
let pkg = example_package().await;
let system_image_package = SystemImageBuilder::new()
.cache_packages(&[&pkg])
.pkgfs_non_static_packages_allowlist(&["not_example"])
.build()
.await;
let blobfs = BlobfsRamdisk::start().unwrap();
system_image_package.write_to_blobfs_dir(&blobfs.root_dir().unwrap());
pkg.write_to_blobfs_dir(&blobfs.root_dir().unwrap());
let pkgfs = PkgfsRamdisk::builder()
.blobfs(blobfs)
.system_image_merkle(system_image_package.meta_far_merkle_root())
.start()
.unwrap();
let d = pkgfs.root_dir().expect("getting pkgfs root dir");
// Test the 'Open' path - we shouldn't be able to open a file in our
// package through /packages.
// The 'example' package wasn't in the allowlist but is on the disk,
// so we expect this to fail with a permission error, which is bubbled up as Other.
assert_eq!(
d.open_file("packages/example/0/a/b")
.expect_err("shouldn't be able to open file in non-allowlisted package")
.raw_os_error(),
Some(libc::EBADF),
);
// The not_on_disk package wasn't in the allowlist but _isn't_ on the disk,
// so we expect this to fail with a NotFound.
assert_eq!(
d.open_file("packages/not_on_disk/0/a/b")
.expect_err("shouldn't be able to open file in package not on disk")
.kind(),
std::io::ErrorKind::NotFound
);
// Test the 'Read' path for /packages - listing
// We should *not* be able to see our package in the /packages directory
assert_eq!(
// This doesn't have to be sorted, but leaving it as sorted so anyone who modifies this doesn't trip over the possible bug.
sorted(ls_simple(d.list_dir("packages").expect("list dir")).expect("list dir contents")),
["system_image"] // We should not see the 'example' package, since it's a dynamic package that's not on the allowlist.
);
drop(d);
pkgfs.stop().await.expect("shutting down pkgfs");
}
// Test the enforcement flag of `pkgfs`. If set to false, it should return dynamic packages
// from /pkgfs/packages even if they aren't on the allowlist.
#[fasync::run_singlethreaded(test)]
async fn test_pkgfs_packages_dynamic_packages_allowlist_enforcement_flag() {
let pkg = example_package().await;
let system_image_package = SystemImageBuilder::new()
.cache_packages(&[&pkg])
.pkgfs_non_static_packages_allowlist(&["not_example"])
.build()
.await;
let blobfs = BlobfsRamdisk::start().unwrap();
system_image_package.write_to_blobfs_dir(&blobfs.root_dir().unwrap());
pkg.write_to_blobfs_dir(&blobfs.root_dir().unwrap());
let pkgfs = PkgfsRamdisk::builder()
.blobfs(blobfs)
.system_image_merkle(system_image_package.meta_far_merkle_root())
.enforce_packages_non_static_allowlist(false) // turn off allowlist enforcement
.start()
.unwrap();
let d = pkgfs.root_dir().expect("getting pkgfs root dir");
let mut file_contents = String::new();
d.open_file("packages/example/0/a/b")
.expect("read package file1")
.read_to_string(&mut file_contents)
.expect("read package file2");
assert_eq!(&file_contents, "Hello world!\n");
// We should be able to see our package in the /packages directory
assert_eq!(
sorted(ls_simple(d.list_dir("packages").expect("list dir")).expect("list dir contents")),
["example", "system_image"]
);
drop(d);
pkgfs.stop().await.expect("shutting down pkgfs");
}
async fn executable_package() -> Package {
PackageBuilder::new("execute")
.add_resource_at("meta/bin1", "please don't put binaries in meta fars".as_bytes())
.add_resource_at("subdir/bin2", "or random subdirectories".as_bytes())
.add_resource_at("bin/bin3", "/bin is good".as_bytes())
.add_resource_at("lib/bin4", "/lib too".as_bytes())
.build()
.await
.unwrap()
}
#[derive(Debug, Clone, Copy)]
enum Executability {
AllowExecute,
BlockExecute,
}
async fn verify_executable_package_rights_on_pkg_dir(
pkgdir: PackageDirectory,
executability: Executability,
) {
// All resources should be openable without execute rights.
assert_matches!(pkgdir.open_file("meta/bin1", OpenRights::Read).await, Ok(_));
assert_matches!(pkgdir.open_file("subdir/bin2", OpenRights::Read).await, Ok(_));
assert_matches!(pkgdir.open_file("bin/bin3", OpenRights::Read).await, Ok(_));
assert_matches!(pkgdir.open_file("lib/bin4", OpenRights::Read).await, Ok(_));
// Metadata is never allowed to be executable.
assert_matches!(
pkgdir.open_file("meta/bin1", OpenRights::ReadExecute).await,
Err(pkgfs::OpenError::OpenError(_))
);
match executability {
Executability::AllowExecute => {
assert_matches!(pkgdir.open_file("subdir/bin2", OpenRights::ReadExecute).await, Ok(_));
assert_matches!(pkgdir.open_file("bin/bin3", OpenRights::ReadExecute).await, Ok(_));
assert_matches!(pkgdir.open_file("lib/bin4", OpenRights::ReadExecute).await, Ok(_));
}
Executability::BlockExecute => {
// See //src/lib/thinfs/zircon/rpc/rpc.go's errorToZx for why fs.ErrPermission maps
// to Status::BAD_HANDLE instead of Status::ACCESS_DENIED.
assert_matches!(
pkgdir.open_file("subdir/bin2", OpenRights::ReadExecute).await,
Err(pkgfs::OpenError::OpenError(Status::BAD_HANDLE))
);
assert_matches!(
pkgdir.open_file("bin/bin3", OpenRights::ReadExecute).await,
Err(pkgfs::OpenError::OpenError(Status::BAD_HANDLE))
);
assert_matches!(
pkgdir.open_file("lib/bin4", OpenRights::ReadExecute).await,
Err(pkgfs::OpenError::OpenError(Status::BAD_HANDLE))
);
}
}
}
async fn verify_executable_package_rights(
pkgfs: &PkgfsRamdisk,
pkg: &Package,
executability: Executability,
) {
let root_dir_proxy = pkgfs.root_dir_proxy().unwrap();
let pkg_merkle = pkg.meta_far_merkle_root();
// Peform the same checks against the package opened through /pkgfs/packages and
// /pkgfs/versions.
let pkgfs_packages = pkgfs::packages::Client::open_from_pkgfs_root(&root_dir_proxy).unwrap();
let pkgdir = pkgfs_packages.open_package("execute", None).await.unwrap();
verify_executable_package_rights_on_pkg_dir(pkgdir, executability).await;
let pkgfs_versions = pkgfs::versions::Client::open_from_pkgfs_root(&root_dir_proxy).unwrap();
let pkgdir = pkgfs_versions.open_package(pkg_merkle).await.unwrap();
verify_executable_package_rights_on_pkg_dir(pkgdir, executability).await;
}
#[fasync::run_singlethreaded(test)]
async fn executability_enforcement_allows_base_package() {
let pkg = executable_package().await;
let system_image_package = SystemImageBuilder::new()
.static_packages(&[&pkg])
.pkgfs_non_static_packages_allowlist(&["execute"])
.build()
.await;
let blobfs = BlobfsRamdisk::start().unwrap();
system_image_package.write_to_blobfs_dir(&blobfs.root_dir().unwrap());
pkg.write_to_blobfs_dir(&blobfs.root_dir().unwrap());
let pkgfs = PkgfsRamdisk::builder()
.blobfs(blobfs)
.system_image_merkle(system_image_package.meta_far_merkle_root())
.enforce_non_base_executability_restrictions(true)
.start()
.unwrap();
verify_executable_package_rights(&pkgfs, &pkg, Executability::AllowExecute).await;
pkgfs.stop().await.expect("shutting down pkgfs");
}
#[fasync::run_singlethreaded(test)]
async fn executability_enforcement_blocks_cache_package() {
let pkg = executable_package().await;
let system_image_package = SystemImageBuilder::new().cache_packages(&[&pkg]).build().await;
let blobfs = BlobfsRamdisk::start().unwrap();
system_image_package.write_to_blobfs_dir(&blobfs.root_dir().unwrap());
pkg.write_to_blobfs_dir(&blobfs.root_dir().unwrap());
let pkgfs = PkgfsRamdisk::builder()
.blobfs(blobfs)
.system_image_merkle(system_image_package.meta_far_merkle_root())
.enforce_non_base_executability_restrictions(true)
.enforce_packages_non_static_allowlist(false)
.start()
.unwrap();
verify_executable_package_rights(&pkgfs, &pkg, Executability::BlockExecute).await;
pkgfs.stop().await.expect("shutting down pkgfs");
}
#[fasync::run_singlethreaded(test)]
async fn executability_enforcement_defaults_to_on() {
let pkg = executable_package().await;
let system_image_package = SystemImageBuilder::new().cache_packages(&[&pkg]).build().await;
let blobfs = BlobfsRamdisk::start().unwrap();
system_image_package.write_to_blobfs_dir(&blobfs.root_dir().unwrap());
pkg.write_to_blobfs_dir(&blobfs.root_dir().unwrap());
let pkgfs = PkgfsRamdisk::builder()
.blobfs(blobfs)
.system_image_merkle(system_image_package.meta_far_merkle_root())
.enforce_packages_non_static_allowlist(false)
.start()
.unwrap();
verify_executable_package_rights(&pkgfs, &pkg, Executability::BlockExecute).await;
pkgfs.stop().await.expect("shutting down pkgfs");
}
#[fasync::run_singlethreaded(test)]
async fn executability_enforcement_allows_allowlisted_package() {
let pkg = executable_package().await;
let system_image_package = SystemImageBuilder::new()
.cache_packages(&[&pkg])
.pkgfs_non_static_packages_allowlist(&["execute"])
.build()
.await;
let blobfs = BlobfsRamdisk::start().unwrap();
system_image_package.write_to_blobfs_dir(&blobfs.root_dir().unwrap());
pkg.write_to_blobfs_dir(&blobfs.root_dir().unwrap());
let pkgfs = PkgfsRamdisk::builder()
.blobfs(blobfs)
.system_image_merkle(system_image_package.meta_far_merkle_root())
.enforce_non_base_executability_restrictions(true)
.start()
.unwrap();
verify_executable_package_rights(&pkgfs, &pkg, Executability::AllowExecute).await;
pkgfs.stop().await.expect("shutting down pkgfs");
}
#[fasync::run_singlethreaded(test)]
async fn executability_enforcement_blocks_unknown_package() {
let pkg = executable_package().await;
let system_image_package = SystemImageBuilder::new().build().await;
let blobfs = BlobfsRamdisk::start().unwrap();
system_image_package.write_to_blobfs_dir(&blobfs.root_dir().unwrap());
let pkgfs = PkgfsRamdisk::builder()
.blobfs(blobfs)
.system_image_merkle(system_image_package.meta_far_merkle_root())
.enforce_non_base_executability_restrictions(true)
.enforce_packages_non_static_allowlist(false)
.start()
.unwrap();
install(&pkgfs, &pkg);
verify_executable_package_rights(&pkgfs, &pkg, Executability::BlockExecute).await;
pkgfs.stop().await.expect("shutting down pkgfs");
}
#[fasync::run_singlethreaded(test)]
async fn executability_enforcement_allows_unknown_package_if_dynamically_disabled() {
let pkg = executable_package().await;
let system_image_package = SystemImageBuilder::new().build().await;
let blobfs = BlobfsRamdisk::start().unwrap();
system_image_package.write_to_blobfs_dir(&blobfs.root_dir().unwrap());
let pkgfs = PkgfsRamdisk::builder()
.blobfs(blobfs)
.system_image_merkle(system_image_package.meta_far_merkle_root())
.enforce_non_base_executability_restrictions(false)
.enforce_packages_non_static_allowlist(false)
.start()
.unwrap();
install(&pkgfs, &pkg);
verify_executable_package_rights(&pkgfs, &pkg, Executability::AllowExecute).await;
pkgfs.stop().await.expect("shutting down pkgfs");
}
#[fasync::run_singlethreaded(test)]
async fn executability_enforcement_blocks_update_to_base_package() {
let pkg1 = PackageBuilder::new("execute")
.add_resource_at("bin/app", "run me".as_bytes())
.build()
.await
.unwrap();
let pkg2 = executable_package().await;
let system_image_package = SystemImageBuilder::new().static_packages(&[&pkg1]).build().await;
let blobfs = BlobfsRamdisk::start().unwrap();
system_image_package.write_to_blobfs_dir(&blobfs.root_dir().unwrap());
pkg1.write_to_blobfs_dir(&blobfs.root_dir().unwrap());
let pkgfs = PkgfsRamdisk::builder()
.blobfs(blobfs)
.system_image_merkle(system_image_package.meta_far_merkle_root())
.enforce_non_base_executability_restrictions(true)
.start()
.unwrap();
let pkgfs_versions =
pkgfs::versions::Client::open_from_pkgfs_root(&pkgfs.root_dir_proxy().unwrap()).unwrap();
// Install a different package with the same name so the static index is updated.
install(&pkgfs, &pkg2);
// The base package is executable.
let pkgdir1 = pkgfs_versions.open_package(pkg1.meta_far_merkle_root()).await.unwrap();
assert_matches!(pkgdir1.open_file("bin/app", OpenRights::ReadExecute).await, Ok(_));
// But the update to it isn't.
let pkgdir2 = pkgfs_versions.open_package(pkg2.meta_far_merkle_root()).await.unwrap();
verify_executable_package_rights_on_pkg_dir(pkgdir2, Executability::BlockExecute).await;
pkgfs.stop().await.expect("shutting down pkgfs");
}
#[fasync::run_singlethreaded(test)]
async fn open_executable_allowed_when_statically_disabled() {
let pkg = executable_package().await;
let system_image_package = SystemImageBuilder::new()
.cache_packages(&[&pkg])
.pkgfs_disable_executability_restrictions()
.build()
.await;
let blobfs = BlobfsRamdisk::start().unwrap();
system_image_package.write_to_blobfs_dir(&blobfs.root_dir().unwrap());
pkg.write_to_blobfs_dir(&blobfs.root_dir().unwrap());
let pkgfs = PkgfsRamdisk::builder()
.blobfs(blobfs)
.system_image_merkle(system_image_package.meta_far_merkle_root())
.enforce_non_base_executability_restrictions(true)
.enforce_packages_non_static_allowlist(false)
.start()
.unwrap();
verify_executable_package_rights(&pkgfs, &pkg, Executability::AllowExecute).await;
let pkgfs = pkgfs
.into_builder()
.unwrap()
.enforce_non_base_executability_restrictions(false)
.enforce_packages_non_static_allowlist(false)
.start()
.unwrap();
verify_executable_package_rights(&pkgfs, &pkg, Executability::AllowExecute).await;
pkgfs.stop().await.expect("shutting down pkgfs");
}
// A previous version of pkgfs/thinfs crashed on a call to get_flags on the /pkg directory, since get_flags
// was an unimplemented transitional method.
// TODO(fxbug.dev/55663): remove this test when unimplemented transitional methods do not crash the server.
#[fuchsia_async::run_singlethreaded(test)]
async fn test_pkgfs_get_flags() {
// Try get_flags on our own package directory.
// thinfs returns an error for this call, which closes the channel.
let this_pkg_dir =
io_util::open_directory_in_namespace("/pkg", io_util::OpenFlags::RIGHT_READABLE)
.expect("opening /pkg");
let (status, flags) = this_pkg_dir.get_flags().await.expect("getting directory flags");
assert_eq!(status, Status::NOT_SUPPORTED.into_raw());
assert_eq!(flags, fio::OpenFlags::empty());
// Try get_flags on a file within our package directory.
// thinfs maps GetFlags to GetFlags, so this should not close the channel.
let meta_far_file_proxy =
io_util::open_file_in_namespace("/pkg/meta", io_util::OpenFlags::RIGHT_READABLE)
.expect("opening /pkg/meta as file");
let (zx_result, flags) = meta_far_file_proxy.get_flags().await.expect("getting file flags");
assert_eq!(zx_result, Status::OK.into_raw());
assert_eq!(flags, io_util::OpenFlags::RIGHT_READABLE);
// We should still be able to read our own package directory and read our own merkle root,
// which means pkgfs hasn't crashed.
assert!(fs::read_to_string("/pkg/meta").expect("read to string").len() > 0);
}
// A previous version of pkgfs/thinfs crashed on a call to set_flags, since set_flags
// was an unimplemented transitional method.
// TODO(fxbug.dev/55663): remove this test when unimplemented transitional methods do not crash the server.
#[fuchsia_async::run_singlethreaded(test)]
async fn test_pkgfs_set_flags() {
// Try set_flags on our own package directory.
// thinfs returns an error for this call, which closes the channel.
let this_pkg_dir =
io_util::open_directory_in_namespace("/pkg", io_util::OpenFlags::RIGHT_READABLE)
.expect("opening /pkg");
let status =
this_pkg_dir.set_flags(fio::OpenFlags::APPEND).await.expect("setting directory flags");
assert_eq!(status, Status::NOT_SUPPORTED.into_raw());
// Try set_flags on a file within our package directory.
// thinfs returns an error for this call, which closes the channel
let meta_far_file_proxy =
io_util::open_file_in_namespace("/pkg/meta", io_util::OpenFlags::RIGHT_READABLE)
.expect("opening /pkg/meta as file");
let status =
meta_far_file_proxy.set_flags(fio::OpenFlags::APPEND).await.expect("setting file flags");
assert_eq!(status, Status::NOT_SUPPORTED.into_raw());
// We should still be able to read our own package directory and read our own merkle root,
// which means pkgfs hasn't crashed.
assert!(fs::read_to_string("/pkg/meta").expect("read to string").len() > 0);
}
// Test that pkgfs correctly handles interleaved opens and closes on the same file in the meta
// virtual directory
#[fuchsia_async::run_singlethreaded(test)]
async fn test_multiple_opens_on_meta_file() {
let package = PackageBuilder::new("example")
.add_resource_at("meta/a", "Hello world!\n".as_bytes())
.add_resource_at("meta/b", "These are some bytes\n".as_bytes())
.add_resource_at("some/path", "An actual file\n".as_bytes())
.build()
.await
.expect("build package");
let system_image_package = SystemImageBuilder::new().static_packages(&[&package]).build().await;
let blobfs = BlobfsRamdisk::start().unwrap();
system_image_package.write_to_blobfs_dir(&blobfs.root_dir().unwrap());
package.write_to_blobfs_dir(&blobfs.root_dir().unwrap());
let pkgfs = PkgfsRamdisk::builder()
.blobfs(blobfs)
.system_image_merkle(system_image_package.meta_far_merkle_root())
.start()
.unwrap();
let d = pkgfs.root_dir_proxy().expect("getting pkgfs root dir");
let meta_directory = io_util::directory::open_directory(
&d,
&format!("versions/{}/meta/", package.meta_far_merkle_root()),
io_util::OpenFlags::RIGHT_READABLE,
)
.await
.expect("open meta dir");
// We should be able to open a file twice, close one version, then read from the second.
let file_a =
io_util::directory::open_file(&meta_directory, "a", io_util::OpenFlags::RIGHT_READABLE)
.await
.unwrap();
let file_a_2 =
io_util::directory::open_file(&meta_directory, "a", io_util::OpenFlags::RIGHT_READABLE)
.await
.unwrap();
file_a.close().await.unwrap().map_err(Status::from_raw).unwrap();
let vmo = file_a_2
.get_backing_memory(fio::VmoFlags::READ | fio::VmoFlags::PRIVATE_CLONE)
.await
.unwrap()
.map_err(Status::from_raw)
.unwrap();
let size = vmo.get_content_size().unwrap();
assert_ne!(size, 0);
file_a_2.close().await.unwrap().map_err(Status::from_raw).unwrap();
pkgfs.stop().await.expect("shutting down pkgfs");
}
// Test that pkgfs correctly handles closing a parent directory when a child file is open
#[fuchsia_async::run_singlethreaded(test)]
async fn test_opening_file_within_directory_and_closing_directory() {
let package = PackageBuilder::new("example")
.add_resource_at("meta/subdir/a", "Hello world!\n".as_bytes())
.add_resource_at("meta/subdir/b", "These are some bytes\n".as_bytes())
.build()
.await
.expect("build package");
let system_image_package = SystemImageBuilder::new().static_packages(&[&package]).build().await;
let blobfs = BlobfsRamdisk::start().unwrap();
system_image_package.write_to_blobfs_dir(&blobfs.root_dir().unwrap());
package.write_to_blobfs_dir(&blobfs.root_dir().unwrap());
let pkgfs = PkgfsRamdisk::builder()
.blobfs(blobfs)
.system_image_merkle(system_image_package.meta_far_merkle_root())
.start()
.unwrap();
let d = pkgfs.root_dir_proxy().expect("getting pkgfs root dir");
let meta_directory = io_util::directory::open_directory(
&d,
&format!("versions/{}/meta/", package.meta_far_merkle_root()),
io_util::OpenFlags::RIGHT_READABLE,
)
.await
.expect("open meta dir");
// We should be able to open subdir in a meta directory, open a file within it,
// close the directory, and still read from the file.
let subdir = io_util::directory::open_directory(
&meta_directory,
"subdir",
io_util::OpenFlags::RIGHT_READABLE,
)
.await
.unwrap();
let file_a = io_util::directory::open_file(&subdir, "a", io_util::OpenFlags::RIGHT_READABLE)
.await
.unwrap();
subdir.close().await.unwrap().map_err(Status::from_raw).unwrap();
let a_contents = io_util::file::read_to_string(&file_a).await.unwrap();
assert_eq!(a_contents, "Hello world!\n");
file_a.close().await.unwrap().map_err(Status::from_raw).unwrap();
pkgfs.stop().await.expect("shutting down pkgfs");
}
#[fuchsia_async::run_singlethreaded(test)]
async fn test_walking_the_pkg_dir() {
let paths = [
"meta/a",
"meta/b",
"meta/c/d/e/f/g",
"meta/c/d/e/f/h",
"meta/c/i",
"meta/c/j",
"a",
"b/d/e/f/g",
"b/d/e/f/h",
"c/i",
"c/j",
];
let mut package = PackageBuilder::new("example");
for path in &paths {
package = package.add_resource_at(path, path.as_bytes());
}
let package = package.build().await.expect("build package");
let system_image_package = SystemImageBuilder::new().static_packages(&[&package]).build().await;
let blobfs = BlobfsRamdisk::start().unwrap();
system_image_package.write_to_blobfs_dir(&blobfs.root_dir().unwrap());
package.write_to_blobfs_dir(&blobfs.root_dir().unwrap());
let pkgfs = PkgfsRamdisk::builder()
.blobfs(blobfs)
.system_image_merkle(system_image_package.meta_far_merkle_root())
.start()
.unwrap();
let pkgfs_dir = pkgfs.root_dir_proxy().expect("getting pkgfs root dir");
// Make sure we can access the files directly through the root.
{
let pkg_directory = io_util::directory::open_directory(
&pkgfs_dir,
&format!("versions/{}", package.meta_far_merkle_root()),
io_util::OpenFlags::RIGHT_READABLE,
)
.await
.expect("open meta dir");
for path in &paths {
let file = io_util::directory::open_file(
&pkg_directory,
path,
io_util::OpenFlags::RIGHT_READABLE,
)
.await
.unwrap();
let body = io_util::file::read_to_string(&file).await.unwrap();
assert_eq!(path, &body);
}
}
// Next, walk through the directories to get to the files.
for path in &paths {
let mut d = io_util::directory::open_directory(
&pkgfs_dir,
&format!("versions/{}", package.meta_far_merkle_root()),
io_util::OpenFlags::RIGHT_READABLE,
)
.await
.expect("open meta dir");
let mut entries = path.split('/');
let mut entry = entries.next().unwrap();
while let Some(child) = entries.next() {
d = io_util::directory::open_directory(&d, entry, io_util::OpenFlags::RIGHT_READABLE)
.await
.unwrap();
entry = child;
}
let file = io_util::directory::open_file(&d, entry, io_util::OpenFlags::RIGHT_READABLE)
.await
.unwrap();
let body = io_util::file::read_to_string(&file).await.unwrap();
assert_eq!(path, &body);
}
pkgfs.stop().await.expect("shutting down pkgfs");
}
#[fuchsia_async::run_singlethreaded(test)]
async fn test_interacting_with_broken_pkg_dir_does_not_break_pkgfs() {
let expected_contents = "Hello world!\n";
let package_to_corrupt = PackageBuilder::new("example0")
.add_resource_at("meta/a", expected_contents.as_bytes())
.build()
.await
.expect("build package");
let package_still_good = PackageBuilder::new("example1")
.add_resource_at("meta/b", expected_contents.as_bytes())
.build()
.await
.expect("build package");
let system_image_package = SystemImageBuilder::new()
.static_packages(&[&package_to_corrupt, &package_still_good])
.build()
.await;
let blobfs = BlobfsRamdisk::start().unwrap();
let blobfs_proxy = blobfs.root_dir_proxy().unwrap();
system_image_package.write_to_blobfs_dir(&blobfs.root_dir().unwrap());
package_to_corrupt.write_to_blobfs_dir(&blobfs.root_dir().unwrap());
package_still_good.write_to_blobfs_dir(&blobfs.root_dir().unwrap());
let pkgfs = PkgfsRamdisk::builder()
.blobfs(blobfs)
.system_image_merkle(system_image_package.meta_far_merkle_root())
.start()
.unwrap();
let pkgfs_dir = pkgfs.root_dir_proxy().expect("getting pkgfs root dir");
// Open a pkg dir
let pkg_dir = io_util::directory::open_directory(
&pkgfs_dir,
&format!("versions/{}", package_to_corrupt.meta_far_merkle_root()),
io_util::OpenFlags::RIGHT_READABLE,
)
.await
.expect("open meta dir");
// Delete the meta.far blob of the open pkg dir
blobfs_proxy
.unlink(&package_to_corrupt.meta_far_merkle_root().to_string(), fio::UnlinkOptions::EMPTY)
.await
.expect("unlink fidl")
.expect("unlink status");
// Opening the meta/ directory should fail
assert_matches!(
io_util::directory::open_directory(
&pkg_dir,
"meta",
io_util::OpenFlags::RIGHT_READABLE
).await,
Err(io_util::node::OpenError::OpenError(s)) if s == Status::NOT_FOUND
);
// Opening a meta file should fail
assert_matches!(
io_util::directory::open_file(
&pkg_dir,
"meta/a",
io_util::OpenFlags::RIGHT_READABLE
).await,
Err(io_util::node::OpenError::OpenError(s)) if s == Status::NOT_FOUND
);
// Interacting with other pkg dirs should continue to work
let file = io_util::directory::open_file(
&pkgfs_dir,
&format!("versions/{}/meta/b", package_still_good.meta_far_merkle_root()),
io_util::OpenFlags::RIGHT_READABLE,
)
.await
.unwrap();
let actual_contents = io_util::file::read_to_string(&file).await.unwrap();
assert_eq!(actual_contents, expected_contents);
pkgfs.stop().await.expect("shutting down pkgfs");
}