blob: 8f9ba600de86aae5235733b1890ceeccf9325bbe [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 {
blobfs_ramdisk::BlobfsRamdisk,
fuchsia_async as fasync,
fuchsia_pkg_testing::{Package, PackageBuilder, SystemImageBuilder, VerificationError},
matches::assert_matches,
pkgfs_ramdisk::PkgfsRamdisk,
std::collections::HashSet,
std::fmt::Debug,
std::future::Future,
std::io::{self, Read, Write},
};
fn ls_simple(d: openat::DirIter) -> Result<Vec<String>, io::Error> {
Ok(d.map(|i| i.map(|entry| entry.file_name().to_string_lossy().into()))
.collect::<Result<Vec<_>, _>>()?)
}
trait AsRootDir {
fn as_root_dir(&self) -> openat::Dir;
}
impl AsRootDir for PkgfsRamdisk {
fn as_root_dir(&self) -> openat::Dir {
self.root_dir().expect("getting pkgfs root dir")
}
}
fn ls(root: &dyn AsRootDir, path: impl openat::AsPath) -> Result<Vec<String>, io::Error> {
let d = root.as_root_dir();
ls_simple(d.list_dir(path)?)
}
/// Allows us to call sort inline
fn sorted<T: Ord>(mut vec: Vec<T>) -> Vec<T> {
vec.sort();
vec
}
fn verify_contents<'a>(
pkg: &'a Package,
dir: fidl_fuchsia_io::DirectoryProxy,
) -> impl Future<Output = Result<(), VerificationError>> + 'a {
async move { pkg.verify_contents(&dir).await }
}
fn subdir_proxy(d: &openat::Dir, path: &str) -> fidl_fuchsia_io::DirectoryProxy {
let handle = fdio::transfer_fd(
d.open_file(path).unwrap_or_else(|e| panic!("opening {}: {:?}", path, e)),
)
.unwrap();
fidl_fuchsia_io::DirectoryProxy::new(
fuchsia_async::Channel::from_channel(handle.into()).unwrap(),
)
}
fn copy_file_with_len(
d: &openat::Dir,
path: &std::path::Path,
source: &mut std::fs::File,
) -> Result<(), anyhow::Error> {
use std::convert::TryInto;
let mut bytes = vec![];
source.read_to_end(&mut bytes)?;
let mut file = d.write_file(path, 0777)?;
file.set_len(bytes.len().try_into().unwrap())?;
file.write_all(&bytes)?;
Ok(())
}
fn install(pkgfs: &PkgfsRamdisk, pkg: &Package) {
let d = pkgfs.root_dir().expect("getting pkgfs root dir");
copy_file_with_len(
&d,
&std::path::Path::new(&format!("install/pkg/{}", pkg.meta_far_merkle_root())),
&mut pkg.meta_far().unwrap(),
)
.unwrap();
let mut needs: HashSet<String> =
ls_simple(d.list_dir(format!("needs/packages/{}", pkg.meta_far_merkle_root())).unwrap())
.unwrap()
.into_iter()
.collect();
for mut content in pkg.content_blob_files() {
let merkle = content.merkle.to_string();
if needs.contains(&merkle) {
copy_file_with_len(
&d,
&std::path::Path::new("install/blob").join(&merkle),
&mut content.file,
)
.unwrap_or_else(|e| panic!("error writing {}: {:?}", merkle, e));
needs.remove(&merkle);
}
}
// Quick sanity check that we actually installed the package.
let mut file_contents = String::new();
d.open_file(format!("versions/{}/meta", pkg.meta_far_merkle_root()))
.unwrap()
.read_to_string(&mut file_contents)
.unwrap();
assert_eq!(file_contents, format!("{}", pkg.meta_far_merkle_root()))
}
/// Helper function implementing the logic for the asser_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)
}};
}
async fn example_package() -> Package {
PackageBuilder::new("example")
.add_resource_at("a/b", "Hello world!\n".as_bytes())
.build()
.await
.expect("build package")
}
#[fasync::run_singlethreaded(test)]
async fn test_pkgfs_short_write() {
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(),
&"b5690901cd8664a742eb0a7d2a068eb0d4ff49c10a615cfa4c0044dd2eaccd93"
.parse::<fuchsia_merkle::Hash>()
.unwrap()
);
let mut meta_far = pkg.meta_far().expect("meta.far");
{
let mut to_write = d
.new_file(
"install/pkg/b5690901cd8664a742eb0a7d2a068eb0d4ff49c10a615cfa4c0044dd2eaccd93",
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/b5690901cd8664a742eb0a7d2a068eb0d4ff49c10a615cfa4c0044dd2eaccd93"
)
.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/b5690901cd8664a742eb0a7d2a068eb0d4ff49c10a615cfa4c0044dd2eaccd93"
)
.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/b5690901cd8664a742eb0a7d2a068eb0d4ff49c10a615cfa4c0044dd2eaccd93",
),
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/b5690901cd8664a742eb0a7d2a068eb0d4ff49c10a615cfa4c0044dd2eaccd93/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"),
[
"b5690901cd8664a742eb0a7d2a068eb0d4ff49c10a615cfa4c0044dd2eaccd93",
"e5892a9b652ede2e19460a9103fd9cb3417f782a8d29f6c93ec0c31170a94af3"
],
);
drop(d);
pkgfs.stop().await.expect("stopping pkgfs");
}
#[fasync::run_singlethreaded(test)]
async fn test_pkgfs_restart_install() {
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(),
&"b5690901cd8664a742eb0a7d2a068eb0d4ff49c10a615cfa4c0044dd2eaccd93"
.parse::<fuchsia_merkle::Hash>()
.unwrap()
);
// Start package install
// first, some checks to see if it's already installed
assert_error_kind!(
d.metadata("versions/b5690901cd8664a742eb0a7d2a068eb0d4ff49c10a615cfa4c0044dd2eaccd93")
.map(|m| m.is_dir()),
io::ErrorKind::NotFound,
);
assert_error_kind!(
d.metadata(
"needs/packages/b5690901cd8664a742eb0a7d2a068eb0d4ff49c10a615cfa4c0044dd2eaccd93"
)
.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/b5690901cd8664a742eb0a7d2a068eb0d4ff49c10a615cfa4c0044dd2eaccd93",
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/b5690901cd8664a742eb0a7d2a068eb0d4ff49c10a615cfa4c0044dd2eaccd93"
)
.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/b5690901cd8664a742eb0a7d2a068eb0d4ff49c10a615cfa4c0044dd2eaccd93"
)
.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/b5690901cd8664a742eb0a7d2a068eb0d4ff49c10a615cfa4c0044dd2eaccd93")
.map(|m| m.is_dir()),
io::ErrorKind::NotFound
);
assert_error_kind!(
d.metadata(
"needs/packages/b5690901cd8664a742eb0a7d2a068eb0d4ff49c10a615cfa4c0044dd2eaccd93"
)
.map(|m| m.is_dir()),
io::ErrorKind::NotFound
);
// Retry installing meta.far (fails with EEXIST)
assert_error_kind!(
d.new_file(
"install/pkg/b5690901cd8664a742eb0a7d2a068eb0d4ff49c10a615cfa4c0044dd2eaccd93",
0600,
)
.map(|_| ()),
io::ErrorKind::AlreadyExists
);
assert_error_kind!(
d.metadata("versions/b5690901cd8664a742eb0a7d2a068eb0d4ff49c10a615cfa4c0044dd2eaccd93")
.map(|m| m.is_dir()),
io::ErrorKind::NotFound
);
// Check needs again.
assert_eq!(
d.metadata(
"needs/packages/b5690901cd8664a742eb0a7d2a068eb0d4ff49c10a615cfa4c0044dd2eaccd93"
)
.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/b5690901cd8664a742eb0a7d2a068eb0d4ff49c10a615cfa4c0044dd2eaccd93"
)
.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/b5690901cd8664a742eb0a7d2a068eb0d4ff49c10a615cfa4c0044dd2eaccd93"
),
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/b5690901cd8664a742eb0a7d2a068eb0d4ff49c10a615cfa4c0044dd2eaccd93/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"),
[
"b5690901cd8664a742eb0a7d2a068eb0d4ff49c10a615cfa4c0044dd2eaccd93",
"e5892a9b652ede2e19460a9103fd9cb3417f782a8d29f6c93ec0c31170a94af3"
],
);
drop(d);
pkgfs.stop().await.expect("stopping pkgfs");
}
#[fasync::run_singlethreaded(test)]
async fn test_pkgfs_restart_install_already_done() {
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(),
&"b5690901cd8664a742eb0a7d2a068eb0d4ff49c10a615cfa4c0044dd2eaccd93"
.parse::<fuchsia_merkle::Hash>()
.unwrap()
);
// Start package install
// first, some checks to see if it's already installed
assert_error_kind!(
d.metadata("versions/b5690901cd8664a742eb0a7d2a068eb0d4ff49c10a615cfa4c0044dd2eaccd93")
.map(|m| m.is_dir()),
io::ErrorKind::NotFound
);
assert_error_kind!(
d.metadata(
"needs/packages/b5690901cd8664a742eb0a7d2a068eb0d4ff49c10a615cfa4c0044dd2eaccd93"
)
.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/b5690901cd8664a742eb0a7d2a068eb0d4ff49c10a615cfa4c0044dd2eaccd93",
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/b5690901cd8664a742eb0a7d2a068eb0d4ff49c10a615cfa4c0044dd2eaccd93"
)
.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/b5690901cd8664a742eb0a7d2a068eb0d4ff49c10a615cfa4c0044dd2eaccd93"
)
.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/b5690901cd8664a742eb0a7d2a068eb0d4ff49c10a615cfa4c0044dd2eaccd93")
.map(|m| m.is_dir()),
io::ErrorKind::NotFound
);
assert_error_kind!(
d.metadata(
"needs/packages/b5690901cd8664a742eb0a7d2a068eb0d4ff49c10a615cfa4c0044dd2eaccd93"
)
.map(|m| m.is_dir()),
io::ErrorKind::NotFound
);
// Retry installing meta.far (fails with Invalid argument)
d.new_file(
"install/pkg/b5690901cd8664a742eb0a7d2a068eb0d4ff49c10a615cfa4c0044dd2eaccd93",
0600,
)
.expect_err("already exists");
// Recheck versions
assert_eq!(
d.metadata("versions/b5690901cd8664a742eb0a7d2a068eb0d4ff49c10a615cfa4c0044dd2eaccd93")
.map(|m| m.is_dir())
.map_err(|e| format!("{:?}", e)),
Ok(true)
);
// Check needs again.
assert_error_kind!(
d.metadata(
"needs/packages/b5690901cd8664a742eb0a7d2a068eb0d4ff49c10a615cfa4c0044dd2eaccd93"
)
.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/b5690901cd8664a742eb0a7d2a068eb0d4ff49c10a615cfa4c0044dd2eaccd93/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"),
[
"b5690901cd8664a742eb0a7d2a068eb0d4ff49c10a615cfa4c0044dd2eaccd93",
"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(),
&"b5690901cd8664a742eb0a7d2a068eb0d4ff49c10a615cfa4c0044dd2eaccd93"
.parse::<fuchsia_merkle::Hash>()
.unwrap()
);
// Start package install
// first, some checks to see if it's already installed
assert_error_kind!(
d.metadata("versions/b5690901cd8664a742eb0a7d2a068eb0d4ff49c10a615cfa4c0044dd2eaccd93")
.map(|m| m.is_dir()),
io::ErrorKind::NotFound
);
assert_error_kind!(
d.metadata(
"needs/packages/b5690901cd8664a742eb0a7d2a068eb0d4ff49c10a615cfa4c0044dd2eaccd93"
)
.map(|m| m.is_dir()),
io::ErrorKind::NotFound
);
// Create (but don't write) the meta.far
d.new_file(
"install/pkg/b5690901cd8664a742eb0a7d2a068eb0d4ff49c10a615cfa4c0044dd2eaccd93",
0600,
)
.expect("create install file");
assert_error_kind!(
d.metadata("versions/b5690901cd8664a742eb0a7d2a068eb0d4ff49c10a615cfa4c0044dd2eaccd93")
.map(|m| m.is_dir()),
io::ErrorKind::NotFound
);
assert_eq!(
ls_simple(d.list_dir("needs/packages").expect("list dir")).expect("list dir contents"),
["b5690901cd8664a742eb0a7d2a068eb0d4ff49c10a615cfa4c0044dd2eaccd93"]
);
assert_eq!(
ls_simple(
d.list_dir(
"needs/packages/b5690901cd8664a742eb0a7d2a068eb0d4ff49c10a615cfa4c0044dd2eaccd93"
)
.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::start_with_blobfs(
blobfs,
Some(&system_image_package.meta_far_merkle_root().to_string()),
)
.expect("starting pkgfs");
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 = "22e41860aa333dec2aea3899aa764a53a6ea7c179e6c47bf3a8163d89024343e";
let pkgfs =
PkgfsRamdisk::start_with_blobfs(blobfs, Some(system_image_merkle)).expect("starting pkgfs");
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_error_kind!(
ls_simple(d.list_dir("system").unwrap()),
io::ErrorKind::Other, // Not supported
);
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(&[&pkg]).build().await;
let blobfs = BlobfsRamdisk::start().unwrap();
system_image_package.write_to_blobfs_dir(&blobfs.root_dir().unwrap());
let pkgfs = PkgfsRamdisk::start_with_blobfs(
blobfs,
Some(&system_image_package.meta_far_merkle_root().to_string()),
)
.expect("starting pkgfs");
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(&[&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::start_with_blobfs(
blobfs,
Some(&system_image_package.meta_far_merkle_root().to_string()),
)
.expect("starting pkgfs");
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_install_update() {
// GC doesn't work without a working system image
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::start_with_blobfs(
blobfs,
Some(&system_image_package.meta_far_merkle_root().to_string()),
)
.expect("starting pkgfs");
let d = pkgfs.root_dir().expect("getting pkgfs root dir");
let pkg = example_package().await;
install(&pkgfs, &pkg);
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");
let pkg2 = PackageBuilder::new("example")
.add_resource_at("a/b", "Hello world 2!\n".as_bytes())
.build()
.await
.expect("build package");
install(&pkgfs, &pkg2);
assert_eq!(sorted(ls(&pkgfs, "packages").unwrap()), ["example", "system_image"]);
assert_eq!(ls_simple(d.list_dir("packages/example").unwrap()).unwrap(), ["0"]);
verify_contents(&pkg2, subdir_proxy(&d, "packages/example/0"))
.await
.expect("pkg2 replaced pkg");
assert_eq!(
sorted(ls(&pkgfs, "versions").unwrap()),
sorted(vec![
pkg2.meta_far_merkle_root().to_string(),
system_image_package.meta_far_merkle_root().to_string()
])
);
// old version is no longer accesible.
assert_error_kind!(
d.metadata(&format!("versions/{}", pkg.meta_far_merkle_root())).map(|m| m.is_dir()),
io::ErrorKind::NotFound
);
{
let blobfs_dir = pkgfs.blobfs().root_dir().unwrap();
// Old blobs still in blobfs.
let expected_blobs = sorted(
pkg.list_blobs()
.unwrap()
.into_iter()
.chain(pkg2.list_blobs().unwrap())
.chain(system_image_package.list_blobs().unwrap())
.map(|m| m.to_string())
.collect(),
);
assert_eq!(sorted(ls_simple(blobfs_dir.list_dir(".").unwrap()).unwrap()), expected_blobs);
// Trigger GC
d.remove_dir("ctl/garbage").unwrap();
// pkg blobs are in blobfs no longer
let expected_blobs = sorted(
pkg2.list_blobs()
.unwrap()
.into_iter()
.chain(system_image_package.list_blobs().unwrap())
.map(|m| m.to_string())
.collect(),
);
let got_blobs = sorted(ls_simple(blobfs_dir.list_dir(".").unwrap()).unwrap());
assert_eq!(got_blobs, expected_blobs);
}
drop(d);
pkgfs.stop().await.expect("stopping pkgfs");
}
#[fasync::run_singlethreaded(test)]
async fn test_pkgfs_restart_deactivates_ephemeral_packages() {
let pkgfs = PkgfsRamdisk::start().expect("starting pkgfs");
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]).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::start_with_blobfs(
blobfs,
Some(&system_image_package.meta_far_merkle_root().to_string()),
)
.expect("starting pkgfs");
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::start_with_blobfs(
blobfs,
Some(&system_image_package.meta_far_merkle_root().to_string()),
)
.expect("starting pkgfs");
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::start_with_blobfs(
blobfs,
Some(&system_image_package.meta_far_merkle_root().to_string()),
)
.expect("starting pkgfs");
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_shadowed_cache_package() {
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());
pkg.write_to_blobfs_dir(&blobfs.root_dir().unwrap());
let pkgfs = PkgfsRamdisk::start_with_blobfs(
blobfs,
Some(&system_image_package.meta_far_merkle_root().to_string()),
)
.expect("starting pkgfs");
let d = pkgfs.root_dir().expect("getting pkgfs root dir");
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");
let pkg2 = PackageBuilder::new("example")
.add_resource_at("a/b", "Hello world 2!\n".as_bytes())
.build()
.await
.expect("build package");
install(&pkgfs, &pkg2);
assert_eq!(sorted(ls(&pkgfs, "packages").unwrap()), ["example", "system_image"]);
assert_eq!(ls_simple(d.list_dir("packages/example").unwrap()).unwrap(), ["0"]);
verify_contents(&pkg2, subdir_proxy(&d, "packages/example/0"))
.await
.expect("pkg2 replaced pkg");
assert_eq!(
sorted(ls(&pkgfs, "versions").unwrap()),
sorted(vec![
pkg2.meta_far_merkle_root().to_string(),
system_image_package.meta_far_merkle_root().to_string()
])
);
// cached version is no longer accesible.
assert_error_kind!(
d.metadata(&format!("versions/{}", pkg.meta_far_merkle_root())).map(|m| m.is_dir()),
io::ErrorKind::NotFound
);
{
let blobfs_dir = pkgfs.blobfs().root_dir().unwrap();
// Old blobs still in blobfs.
let expected_blobs = sorted(
pkg.list_blobs()
.unwrap()
.into_iter()
.chain(pkg2.list_blobs().unwrap())
.chain(system_image_package.list_blobs().unwrap())
.map(|m| m.to_string())
.collect(),
);
assert_eq!(sorted(ls_simple(blobfs_dir.list_dir(".").unwrap()).unwrap()), expected_blobs);
// Trigger GC
d.remove_dir("ctl/garbage").unwrap();
// cached pkg blobs are in blobfs no longer
let expected_blobs = sorted(
pkg2.list_blobs()
.unwrap()
.into_iter()
.chain(system_image_package.list_blobs().unwrap())
.map(|m| m.to_string())
.collect(),
);
let got_blobs = sorted(ls_simple(blobfs_dir.list_dir(".").unwrap()).unwrap());
assert_eq!(got_blobs, expected_blobs);
}
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]).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::start_with_blobfs(
blobfs,
Some(&system_image_package.meta_far_merkle_root().to_string()),
)
.expect("starting pkgfs");
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::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 =
"b5690901cd8664a742eb0a7d2a068eb0d4ff49c10a615cfa4c0044dd2eaccd93";
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/b5690901cd8664a742eb0a7d2a068eb0d4ff49c10a615cfa4c0044dd2eaccd93",
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/b5690901cd8664a742eb0a7d2a068eb0d4ff49c10a615cfa4c0044dd2eaccd93"
)
.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/b5690901cd8664a742eb0a7d2a068eb0d4ff49c10a615cfa4c0044dd2eaccd93"
)
.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/b5690901cd8664a742eb0a7d2a068eb0d4ff49c10a615cfa4c0044dd2eaccd93/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 = PackageBuilder::new("system_image")
.add_resource_at(
"data/static_packages",
format!("example/0={}", pkg.meta_far_merkle_root()).as_bytes(),
)
.build()
.await
.unwrap();
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::start_with_blobfs(
blobfs,
Some(system_image_package.meta_far_merkle_root().to_string()),
)
.expect("starting pkgfs");
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");
}