| // 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_merkle::Hash, |
| fuchsia_pkg_testing::{Package, PackageBuilder, SystemImageBuilder, VerificationError}, |
| fuchsia_zircon::Status, |
| matches::assert_matches, |
| pkgfs::package::OpenRights, |
| pkgfs_ramdisk::PkgfsRamdisk, |
| std::{ |
| collections::HashSet, |
| fmt::Debug, |
| fs, |
| future::Future, |
| io::{self, Read, Write}, |
| }, |
| }; |
| |
| mod get_buffer; |
| |
| 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::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(), |
| &"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::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(), |
| &"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::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(), |
| &"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::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_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().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_install_update() { |
| // GC doesn't work without a working system image |
| let system_image_package = |
| SystemImageBuilder::new().pkgfs_non_static_packages_allowlist(&["example"]).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"); |
| |
| 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::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_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"); |
| |
| 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]) |
| .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 = |
| "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 = 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") |
| .kind(), |
| std::io::ErrorKind::Other |
| ); |
| |
| // 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: pkgfs::package::Directory, |
| 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 //garnet/go/src/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 node_get_flags on the /pkg directory, since node_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_node_get_flags() { |
| // Try node_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::OPEN_RIGHT_READABLE) |
| .expect("opening /pkg"); |
| let (status, flags) = this_pkg_dir.node_get_flags().await.expect("getting directory flags"); |
| assert_eq!(status, Status::NOT_SUPPORTED.into_raw()); |
| assert_eq!(flags, 0); |
| |
| // Try node_get_flags on a file within our package directory. |
| // thinfs maps NodeGetFlags to GetFlags, so this should not close the channel. |
| let meta_far_file_proxy = |
| io_util::open_file_in_namespace("/pkg/meta", io_util::OPEN_RIGHT_READABLE) |
| .expect("opening /pkg/meta as file"); |
| let (zx_result, flags) = |
| meta_far_file_proxy.node_get_flags().await.expect("getting file flags"); |
| assert_eq!(zx_result, Status::OK.into_raw()); |
| assert_eq!(flags, io_util::OPEN_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 node_set_flags, since node_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_node_set_flags() { |
| // Try node_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::OPEN_RIGHT_READABLE) |
| .expect("opening /pkg"); |
| let status = this_pkg_dir |
| .node_set_flags(fidl_fuchsia_io::OPEN_FLAG_APPEND) |
| .await |
| .expect("setting directory flags"); |
| assert_eq!(status, Status::NOT_SUPPORTED.into_raw()); |
| |
| // Try node_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::OPEN_RIGHT_READABLE) |
| .expect("opening /pkg/meta as file"); |
| let status = meta_far_file_proxy |
| .node_set_flags(fidl_fuchsia_io::OPEN_FLAG_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::OPEN_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::OPEN_RIGHT_READABLE) |
| .await |
| .unwrap(); |
| let file_a_2 = |
| io_util::directory::open_file(&meta_directory, "a", io_util::OPEN_RIGHT_READABLE) |
| .await |
| .unwrap(); |
| |
| file_a.close().await.unwrap(); |
| let (status, buffer) = file_a_2 |
| .get_buffer(fidl_fuchsia_io::VMO_FLAG_READ | fidl_fuchsia_io::VMO_FLAG_PRIVATE) |
| .await |
| .unwrap(); |
| Status::ok(status).unwrap(); |
| let buffer = buffer.unwrap(); |
| assert_ne!(buffer.size, 0); |
| file_a_2.close().await.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::OPEN_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::OPEN_RIGHT_READABLE) |
| .await |
| .unwrap(); |
| let file_a = |
| io_util::directory::open_file(&subdir, "a", io_util::OPEN_RIGHT_READABLE).await.unwrap(); |
| subdir.close().await.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(); |
| |
| 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::OPEN_RIGHT_READABLE, |
| ) |
| .await |
| .expect("open meta dir"); |
| |
| for path in &paths { |
| let file = io_util::directory::open_file(&pkg_directory, path, io_util::OPEN_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::OPEN_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::OPEN_RIGHT_READABLE, |
| ).await.unwrap(); |
| |
| entry = child; |
| } |
| |
| let file = io_util::directory::open_file(&d, entry, io_util::OPEN_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::OPEN_RIGHT_READABLE, |
| ) |
| .await |
| .expect("open meta dir"); |
| |
| // Delete the meta.far blob of the open pkg dir |
| Status::ok( |
| blobfs_proxy |
| .unlink(&package_to_corrupt.meta_far_merkle_root().to_string()) |
| .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::OPEN_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::OPEN_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::OPEN_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"); |
| } |