blob: bc25606986b5a2f18c2aa26752fd5a6b158a4e4b [file] [log] [blame]
// Copyright 2021 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.
use {
crate::{
additional_boot_args::{AdditionalBootConfigCollection, AdditionalBootConfigContents},
static_pkgs::collection::{StaticPkgsCollection, StaticPkgsError},
},
anyhow::{Context, Result},
fuchsia_archive::Utf8Reader as FarReader,
fuchsia_hash::Hash,
fuchsia_url::{PackageName, PackageVariant},
maplit::hashset,
scrutiny::model::{collector::DataCollector, model::DataModel},
scrutiny_utils::{
artifact::{ArtifactReader, FileArtifactReader},
key_value::parse_key_value,
package::{
extract_system_image_hash_string, verify_package_merkle, PackageError,
PackageIndexContents,
},
url::from_package_name_variant_path,
},
std::{
collections::{HashMap, HashSet},
path::{Path, PathBuf},
str::{from_utf8, FromStr},
sync::Arc,
},
};
static META_FAR_CONTENTS_LISTING_PATH: &str = "meta/contents";
static STATIC_PKGS_LISTING_PATH: &str = "data/static_packages";
#[derive(Debug)]
struct StaticPkgsData {
deps: HashSet<PathBuf>,
static_pkgs: PackageIndexContents,
}
#[derive(Debug)]
struct ErrorWithDeps {
pub deps: HashSet<PathBuf>,
pub error: StaticPkgsError,
}
fn collect_static_pkgs(
additional_boot_args: AdditionalBootConfigContents,
mut artifact_reader: Box<dyn ArtifactReader>,
) -> Result<StaticPkgsData, ErrorWithDeps> {
// Get system image path from ["bin/pkgsvr", <system-image-hash>] cmd.
let system_image_merkle_string = extract_system_image_hash_string(&additional_boot_args)
.map_err(|err| ErrorWithDeps { deps: artifact_reader.get_deps(), error: err.into() })?;
// Read system image package and verify its merkle.
verify_package_merkle(&system_image_merkle_string, &mut artifact_reader).map_err(|err| {
let error = match err {
PackageError::MalformedPackageHash { actual_hash } => {
StaticPkgsError::MalformedSystemImageHash { actual_hash }
}
PackageError::FailedToOpenPackage { package_path, io_error } => {
StaticPkgsError::FailedToOpenSystemImage {
system_image_path: package_path,
io_error,
}
}
PackageError::FailedToReadPackage { package_path, io_error } => {
StaticPkgsError::FailedToReadSystemImage {
system_image_path: package_path,
io_error,
}
}
PackageError::FailedToVerifyPackage { expected_merkle_root, computed_merkle_root } => {
StaticPkgsError::FailedToVerifySystemImage {
expected_merkle_root,
computed_merkle_root,
}
}
};
ErrorWithDeps { deps: artifact_reader.get_deps(), error }
})?;
// Parse system image.
let system_image_path = Path::new(&system_image_merkle_string);
let system_image_pkg =
artifact_reader.open(&Path::new(system_image_path)).map_err(|err| ErrorWithDeps {
deps: artifact_reader.get_deps(),
error: StaticPkgsError::FailedToReadSystemImage {
system_image_path: system_image_path.to_path_buf(),
io_error: err.to_string(),
},
})?;
let mut system_image_far = FarReader::new(system_image_pkg).map_err(|err| ErrorWithDeps {
deps: artifact_reader.get_deps(),
error: StaticPkgsError::FailedToParseSystemImage {
system_image_path: system_image_path.to_path_buf(),
parse_error: err.to_string(),
},
})?;
// Extract "data/static_packages" hash from "meta/contents" file.
let system_image_data_contents = parse_key_value(
from_utf8(&system_image_far.read_file(META_FAR_CONTENTS_LISTING_PATH).map_err(|err| {
ErrorWithDeps {
deps: artifact_reader.get_deps(),
error: StaticPkgsError::FailedToReadSystemImageMetaFile {
system_image_path: system_image_path.to_path_buf(),
file_name: META_FAR_CONTENTS_LISTING_PATH.to_string(),
far_error: err.to_string(),
},
}
})?)
.map_err(|err| ErrorWithDeps {
deps: artifact_reader.get_deps(),
error: StaticPkgsError::FailedToDecodeSystemImageMetaFile {
system_image_path: system_image_path.to_path_buf(),
file_name: META_FAR_CONTENTS_LISTING_PATH.to_string(),
utf8_error: err.to_string(),
},
})?,
)
.map_err(|err| ErrorWithDeps {
deps: artifact_reader.get_deps(),
error: StaticPkgsError::FailedToParseSystemImageMetaFile {
system_image_path: system_image_path.to_path_buf(),
file_name: META_FAR_CONTENTS_LISTING_PATH.to_string(),
parse_error: err.to_string(),
},
})?;
let static_pkgs_merkle_string =
system_image_data_contents.get(STATIC_PKGS_LISTING_PATH).ok_or_else(|| ErrorWithDeps {
deps: artifact_reader.get_deps(),
error: StaticPkgsError::MissingStaticPkgsEntry {
system_image_path: system_image_path.to_path_buf(),
file_name: STATIC_PKGS_LISTING_PATH.to_string(),
},
})?;
// Verify static pkgs merkle.
verify_package_merkle(static_pkgs_merkle_string, &mut artifact_reader).map_err(|err| {
let error = match err {
PackageError::MalformedPackageHash { actual_hash } => {
StaticPkgsError::MalformedStaticPkgsHash { actual_hash }
}
PackageError::FailedToOpenPackage { package_path, io_error } => {
StaticPkgsError::FailedToReadStaticPkgs { static_pkgs_path: package_path, io_error }
}
PackageError::FailedToReadPackage { package_path, io_error } => {
StaticPkgsError::FailedToReadStaticPkgs { static_pkgs_path: package_path, io_error }
}
PackageError::FailedToVerifyPackage { expected_merkle_root, computed_merkle_root } => {
StaticPkgsError::FailedToVerifyStaticPkgs {
expected_merkle_root,
computed_merkle_root,
}
}
};
ErrorWithDeps { deps: artifact_reader.get_deps(), error }
})?;
let static_pkgs_path = Path::new(static_pkgs_merkle_string);
// Read static packages index and parse it.
let mut static_pkgs = artifact_reader.open(static_pkgs_path).map_err(|err| ErrorWithDeps {
deps: artifact_reader.get_deps(),
error: StaticPkgsError::FailedToReadStaticPkgs {
static_pkgs_path: static_pkgs_path.to_path_buf(),
io_error: err.to_string(),
},
})?;
let mut static_pkgs_contents = String::new();
static_pkgs.read_to_string(&mut static_pkgs_contents).map_err(|err| ErrorWithDeps {
deps: artifact_reader.get_deps(),
error: StaticPkgsError::FailedToParseStaticPkgs {
static_pkgs_path: static_pkgs_path.to_path_buf(),
parse_error: err.to_string(),
},
})?;
let static_pkgs = parse_key_value(&static_pkgs_contents).map_err(|err| ErrorWithDeps {
deps: artifact_reader.get_deps(),
error: StaticPkgsError::FailedToParseStaticPkgs {
static_pkgs_path: static_pkgs_path.to_path_buf(),
parse_error: err.to_string(),
},
})?;
let static_pkgs = static_pkgs
.into_iter()
.map(|(name_and_variant, merkle)| {
let url = from_package_name_variant_path(name_and_variant)?;
let merkle = Hash::from_str(&merkle)?;
Ok(((url.name().clone(), url.variant().map(|v| v.clone())), merkle))
})
// Handle errors via collect
// Iter<Result<_, __>> into Result<Vec<_>, __>.
.collect::<Result<Vec<((PackageName, Option<PackageVariant>), Hash)>>>()
.map_err(|err| ErrorWithDeps {
deps: artifact_reader.get_deps(),
error: StaticPkgsError::FailedToParseStaticPkgs {
static_pkgs_path: static_pkgs_path.to_path_buf(),
parse_error: format!(
"Failed to parse static packages name/variant=merkle: {:?}",
err
),
},
})?
// Collect Vec<(_, __)> into HashMap<_, __>.
.into_iter()
.collect::<HashMap<(PackageName, Option<PackageVariant>), Hash>>();
Ok(StaticPkgsData { deps: artifact_reader.get_deps(), static_pkgs })
}
#[derive(Default)]
pub struct StaticPkgsCollector;
impl DataCollector for StaticPkgsCollector {
fn collect(&self, model: Arc<DataModel>) -> Result<()> {
let model_config = model.config();
let additional_boot_args_data: Result<Arc<AdditionalBootConfigCollection>> = model.get();
if let Err(err) = additional_boot_args_data {
model
.set(StaticPkgsCollection {
static_pkgs: None,
deps: hashset! {},
errors: vec![StaticPkgsError::FailedToReadAdditionalBootConfigData {
model_error: err.to_string(),
}],
})
.context("Static packages collector failed to store errors in model")?;
return Ok(());
}
let additional_boot_args_data = additional_boot_args_data.unwrap();
let mut deps = additional_boot_args_data.deps.clone();
let mut errors: Vec<StaticPkgsError> = Vec::new();
if additional_boot_args_data.additional_boot_args.is_none() {
errors.push(StaticPkgsError::MissingAdditionalBootConfigData);
}
if additional_boot_args_data.errors.len() > 0 {
errors.push(StaticPkgsError::AdditionalBootConfigDataContainsErrors {
additional_boot_args_data_errors: additional_boot_args_data.errors.clone(),
});
}
if errors.len() > 0 {
model
.set(StaticPkgsCollection { static_pkgs: None, deps, errors })
.context("Static packages collector failed to store errors in model")?;
return Ok(());
}
let additional_boot_args = additional_boot_args_data.additional_boot_args.clone().unwrap();
let artifact_reader =
FileArtifactReader::new(&PathBuf::new(), &model_config.blobs_directory());
let data: StaticPkgsCollection =
match collect_static_pkgs(additional_boot_args, Box::new(artifact_reader)) {
Ok(static_pkgs_data) => {
deps.extend(static_pkgs_data.deps.into_iter());
StaticPkgsCollection {
static_pkgs: Some(static_pkgs_data.static_pkgs),
deps,
errors: vec![],
}
}
Err(err) => StaticPkgsCollection {
static_pkgs: None,
deps: deps.union(&err.deps).map(PathBuf::clone).collect(),
errors: vec![err.error],
},
};
model.set(data).context("Static packages collector failed to store result in model")?;
Ok(())
}
}
#[cfg(test)]
mod tests {
use {
super::{
collect_static_pkgs, ErrorWithDeps, StaticPkgsCollector,
META_FAR_CONTENTS_LISTING_PATH, STATIC_PKGS_LISTING_PATH,
},
crate::{
additional_boot_args::{AdditionalBootConfigCollection, AdditionalBootConfigError},
static_pkgs::collection::{StaticPkgsCollection, StaticPkgsError},
},
anyhow::{anyhow, Context, Result},
fuchsia_archive::write as far_write,
fuchsia_merkle::{Hash, MerkleTree, HASH_SIZE},
fuchsia_url::{PackageName, PackageVariant},
maplit::{btreemap, hashmap, hashset},
scrutiny::model::collector::DataCollector,
scrutiny_testing::{artifact::MockArtifactReader, fake::fake_data_model},
scrutiny_utils::package::{PKGFS_BINARY_PATH, PKGFS_CMD_ADDITIONAL_BOOT_CONFIG_KEY},
std::{
collections::{BTreeMap, HashMap},
io::{BufWriter, Read, Write},
path::PathBuf,
str::FromStr,
sync::Arc,
},
};
fn create_system_image_far(static_pkgs_merkle: Option<Hash>) -> Vec<u8> {
let mut system_image_far = BufWriter::new(Vec::new());
let meta_contents = match static_pkgs_merkle {
Some(static_pkgs_merkle) => {
format!("{}={}\n", STATIC_PKGS_LISTING_PATH, static_pkgs_merkle)
}
None => "".to_string(),
};
let meta_contents_bytes = meta_contents.as_bytes();
let meta_contents_reader: Box<dyn Read> = Box::new(meta_contents_bytes);
let path_content_map: BTreeMap<&str, (u64, Box<dyn Read>)> = btreemap! {
META_FAR_CONTENTS_LISTING_PATH =>
(meta_contents_bytes.len() as u64, meta_contents_reader),
};
far_write(&mut system_image_far, path_content_map).unwrap();
system_image_far.into_inner().unwrap()
}
fn create_static_pkgs_listing(
mapping: HashMap<(PackageName, Option<PackageVariant>), Hash>,
) -> Vec<u8> {
let mut static_pkgs_listing = BufWriter::new(Vec::new());
// let iter: <HashMap<(PackageName, Option<PackageVariant>), Hash> as std::iter::IntoIterator>::IntoIter =
// mapping.into_iter();
for ((name, variant), merkle) in mapping {
match variant {
Some(variant) => {
write!(static_pkgs_listing, "{}/{}={}\n", name, variant, merkle).unwrap()
}
None => write!(static_pkgs_listing, "{}={}\n", name, merkle).unwrap(),
};
}
static_pkgs_listing.into_inner().unwrap()
}
#[fuchsia::test]
fn test_missing_all_data() -> Result<()> {
// Model contains no data (in particular, no additional boot config data).
let model = fake_data_model();
let collector = StaticPkgsCollector::default();
collector
.collect(model.clone())
.context("Failed to return cleanly when data missing from model")?;
let result: Arc<StaticPkgsCollection> =
model.get().context("Failed to get static pkgs data put to model")?;
assert!(result.static_pkgs.is_none());
assert_eq!(result.errors.len(), 1);
match &result.errors[0] {
StaticPkgsError::FailedToReadAdditionalBootConfigData { .. } => Ok(()),
err => Err(anyhow!("Unexpected error: {}", err.to_string())),
}
}
#[fuchsia::test]
fn test_missing_config_data() -> Result<()> {
let model = fake_data_model();
// Result from additional boot config contains no config.
let additional_boot_args_result = AdditionalBootConfigCollection {
deps: hashset! {},
additional_boot_args: None,
errors: vec![],
};
model
.clone()
.set(additional_boot_args_result)
.context("Failed to store additional boot config result")?;
let collector = StaticPkgsCollector::default();
collector
.collect(model.clone())
.context("Failed to return cleanly when data missing from model")?;
let result: Arc<StaticPkgsCollection> =
model.get().context("Failed to get static pkgs data put to model")?;
assert!(result.static_pkgs.is_none());
assert_eq!(result.errors.len(), 1);
match &result.errors[0] {
StaticPkgsError::MissingAdditionalBootConfigData => Ok(()),
err => Err(anyhow!("Unexpected error: {}", err.to_string())),
}
}
#[fuchsia::test]
fn test_err_from_additional_boot_args() -> Result<()> {
let model = fake_data_model();
// Result from additional boot config contains no config and an error.
let additional_boot_args_result = AdditionalBootConfigCollection {
deps: hashset! {},
additional_boot_args: None,
errors: vec![AdditionalBootConfigError::FailedToReadZbi {
update_package_path: "update.far".into(),
io_error: "Failed to read file at update.far".to_string(),
}],
};
model
.clone()
.set(additional_boot_args_result)
.context("Failed to store additional boot config result")?;
let collector = StaticPkgsCollector::default();
collector
.collect(model.clone())
.context("Failed to return cleanly when data missing from model")?;
let result: Arc<StaticPkgsCollection> =
model.get().context("Failed to get static pkgs data put to model")?;
assert!(result.static_pkgs.is_none());
assert_eq!(result.errors.len(), 2);
match (&result.errors[0], &result.errors[1]) {
(
StaticPkgsError::MissingAdditionalBootConfigData,
StaticPkgsError::AdditionalBootConfigDataContainsErrors { .. },
)
| (
StaticPkgsError::AdditionalBootConfigDataContainsErrors { .. },
StaticPkgsError::MissingAdditionalBootConfigData,
) => Ok(()),
(err1, err2) => {
Err(anyhow!("Unexpected errors: {} and {}", err1.to_string(), err2.to_string()))
}
}
}
#[fuchsia::test]
fn test_missing_pkgfs_cmd_entry() {
let mock_artifact_reader = MockArtifactReader::new();
let additional_boot_args = hashmap! {};
let result = collect_static_pkgs(additional_boot_args, Box::new(mock_artifact_reader));
match result {
Err(ErrorWithDeps { error: StaticPkgsError::MissingPkgfsCmdEntry { .. }, .. }) => {
return
}
_ => panic!("Unexpected result: {:?}", result),
};
}
#[fuchsia::test]
fn test_pkgfs_cmd_too_short() {
let mock_artifact_reader = MockArtifactReader::new();
let additional_boot_args = hashmap! {
PKGFS_CMD_ADDITIONAL_BOOT_CONFIG_KEY.to_string() => vec![PKGFS_BINARY_PATH.to_string()],
};
let result = collect_static_pkgs(additional_boot_args, Box::new(mock_artifact_reader));
match result {
Err(ErrorWithDeps { error: StaticPkgsError::UnexpectedPkgfsCmdLen { .. }, .. }) => {
return
}
_ => panic!("Unexpected result: {:?}", result),
};
}
#[fuchsia::test]
fn test_pkgfs_cmd_too_long() {
let mock_artifact_reader = MockArtifactReader::new();
let additional_boot_args = hashmap! {
PKGFS_CMD_ADDITIONAL_BOOT_CONFIG_KEY.to_string() => vec![
PKGFS_BINARY_PATH.to_string(),
"param1".to_string(),
"param2".to_string(),
],
};
let result = collect_static_pkgs(additional_boot_args, Box::new(mock_artifact_reader));
match result {
Err(ErrorWithDeps { error: StaticPkgsError::UnexpectedPkgfsCmdLen { .. }, .. }) => {
return
}
_ => panic!("Unexpected result: {:?}", result),
};
}
#[fuchsia::test]
fn test_bad_pkgfs_cmd() {
let mock_artifact_reader = MockArtifactReader::new();
let bad_cmd_name = "unexpected/pkgsvr/path";
let additional_boot_args = hashmap! {
PKGFS_CMD_ADDITIONAL_BOOT_CONFIG_KEY.to_string() => vec![
bad_cmd_name.to_string(),
Hash::from([0; HASH_SIZE]).to_string(),
],
};
let result = collect_static_pkgs(additional_boot_args, Box::new(mock_artifact_reader));
match result {
Err(ErrorWithDeps { error: StaticPkgsError::UnexpectedPkgfsCmd { .. }, .. }) => return,
_ => panic!("Unexpected result: {:?}", result),
};
}
#[fuchsia::test]
fn test_invalid_system_image_merkle() {
let mock_artifact_reader = MockArtifactReader::new();
let bad_merkle_root = "I am not a merkle root";
let additional_boot_args = hashmap! {
PKGFS_CMD_ADDITIONAL_BOOT_CONFIG_KEY.to_string() => vec![
PKGFS_BINARY_PATH.to_string(),
bad_merkle_root.to_string(),
],
};
let result = collect_static_pkgs(additional_boot_args, Box::new(mock_artifact_reader));
match result {
Err(ErrorWithDeps {
error: StaticPkgsError::MalformedSystemImageHash { .. }, ..
}) => return,
_ => panic!("Unexpected result: {:?}", result),
};
}
#[fuchsia::test]
fn test_missing_system_image() {
let mock_artifact_reader = MockArtifactReader::new();
let designated_system_image_hash = Hash::from([0; HASH_SIZE]);
let additional_boot_args = hashmap! {
PKGFS_CMD_ADDITIONAL_BOOT_CONFIG_KEY.to_string() => vec![
PKGFS_BINARY_PATH.to_string(),
designated_system_image_hash.to_string(),
],
};
let result = collect_static_pkgs(additional_boot_args, Box::new(mock_artifact_reader));
match result {
Err(ErrorWithDeps {
error: StaticPkgsError::FailedToReadSystemImage { .. }, ..
}) => return,
_ => panic!("Unexpected result: {:?}", result),
};
}
#[fuchsia::test]
fn test_incorrect_system_image_merkle() {
let mut mock_artifact_reader = MockArtifactReader::new();
// Code under test designates `designated_system_image_hash` as "where to find system image
// blob". This value is mapped to a valid system image file according to
// `mock_artifact_reader`, but is not the correct hash for the file.
let designated_system_image_hash = Hash::from([0; HASH_SIZE]);
let system_image_contents = create_system_image_far(None);
let system_image_hash =
MerkleTree::from_reader(system_image_contents.as_slice()).unwrap().root();
assert!(designated_system_image_hash != system_image_hash);
// Incorrectly map `designated_system_image_hash` to `system_image_contents` (that's not its
// content hash!).
mock_artifact_reader.append_artifact(
&PathBuf::from(designated_system_image_hash.to_string()),
system_image_contents,
);
let additional_boot_args = hashmap! {
PKGFS_CMD_ADDITIONAL_BOOT_CONFIG_KEY.to_string() => vec![
PKGFS_BINARY_PATH.to_string(),
designated_system_image_hash.to_string(),
],
};
let result = collect_static_pkgs(additional_boot_args, Box::new(mock_artifact_reader));
match result {
Err(ErrorWithDeps {
error: StaticPkgsError::FailedToVerifySystemImage { .. }, ..
}) => return,
_ => panic!("Unexpected result: {:?}", result),
};
}
#[fuchsia::test]
fn test_missing_static_pkgs() {
let mut mock_artifact_reader = MockArtifactReader::new();
// `None` below implies no static packages entry in system image package.
let system_image_contents = create_system_image_far(None);
let system_image_hash =
MerkleTree::from_reader(system_image_contents.as_slice()).unwrap().root();
mock_artifact_reader
.append_artifact(&PathBuf::from(system_image_hash.to_string()), system_image_contents);
let additional_boot_args = hashmap! {
PKGFS_CMD_ADDITIONAL_BOOT_CONFIG_KEY.to_string() => vec![
PKGFS_BINARY_PATH.to_string(),
system_image_hash.to_string(),
],
};
let result = collect_static_pkgs(additional_boot_args, Box::new(mock_artifact_reader));
match result {
Err(ErrorWithDeps {
error: StaticPkgsError::MissingStaticPkgsEntry { .. }, ..
}) => return,
_ => panic!("Unexpected result: {:?}", result),
};
}
#[fuchsia::test]
fn test_static_pkgs_not_found() {
let mut mock_artifact_reader = MockArtifactReader::new();
let static_pkgs = hashmap! {};
let static_pkgs_contents = create_static_pkgs_listing(static_pkgs.clone());
let static_pkgs_hash =
MerkleTree::from_reader(static_pkgs_contents.as_slice()).unwrap().root();
let system_image_contents = create_system_image_far(Some(static_pkgs_hash.clone()));
let system_image_hash =
MerkleTree::from_reader(system_image_contents.as_slice()).unwrap().root();
// Note: `mock_artifact_reader does not have static packages manifest added, so it will
// yield an error when code under test attempts to said manifest.
mock_artifact_reader
.append_artifact(&PathBuf::from(system_image_hash.to_string()), system_image_contents);
let result = collect_static_pkgs(
hashmap! {
PKGFS_CMD_ADDITIONAL_BOOT_CONFIG_KEY.to_string() => vec![
PKGFS_BINARY_PATH.to_string(),
system_image_hash.to_string(),
],
},
Box::new(mock_artifact_reader),
);
match result {
Err(ErrorWithDeps {
error: StaticPkgsError::FailedToReadStaticPkgs { .. }, ..
}) => return,
_ => panic!("Unexpected result: {:?}", result),
};
}
#[fuchsia::test]
fn test_incorrect_static_pkgs_merkle() {
let mut mock_artifact_reader = MockArtifactReader::new();
let static_pkgs = hashmap! {};
let static_pkgs_contents = create_static_pkgs_listing(static_pkgs.clone());
let static_pkgs_hash =
MerkleTree::from_reader(static_pkgs_contents.as_slice()).unwrap().root();
// System image designates `designated_static_pkgs_hash` as "where to find static pkgs
// listing". This value is mapped to the static pkgs file according to
// `mock_artifact_reader`, but is not the correct hash for the file.
let designated_static_pkgs_hash = Hash::from([0; HASH_SIZE]);
let system_image_contents =
create_system_image_far(Some(designated_static_pkgs_hash.clone()));
let system_image_hash =
MerkleTree::from_reader(system_image_contents.as_slice()).unwrap().root();
assert!(designated_static_pkgs_hash != static_pkgs_hash);
// Incorrectly map `designated_static_pkgs_hash` to `static_pkgs_contents` (that's not its
// content hash!).
mock_artifact_reader.append_artifact(
&PathBuf::from(designated_static_pkgs_hash.to_string()),
static_pkgs_contents,
);
mock_artifact_reader
.append_artifact(&PathBuf::from(system_image_hash.to_string()), system_image_contents);
let additional_boot_args = hashmap! {
PKGFS_CMD_ADDITIONAL_BOOT_CONFIG_KEY.to_string() => vec![
PKGFS_BINARY_PATH.to_string(),
system_image_hash.to_string(),
],
};
let result = collect_static_pkgs(additional_boot_args, Box::new(mock_artifact_reader));
match result {
Err(ErrorWithDeps {
error: StaticPkgsError::FailedToVerifyStaticPkgs { .. }, ..
}) => return,
_ => panic!("Unexpected result: {:?}", result),
};
}
#[fuchsia::test]
fn test_empty_static_pkgs() {
let mut mock_artifact_reader = MockArtifactReader::new();
let static_pkgs = hashmap! {};
let static_pkgs_contents = create_static_pkgs_listing(static_pkgs.clone());
let static_pkgs_hash =
MerkleTree::from_reader(static_pkgs_contents.as_slice()).unwrap().root();
let system_image_contents = create_system_image_far(Some(static_pkgs_hash.clone()));
let system_image_hash =
MerkleTree::from_reader(system_image_contents.as_slice()).unwrap().root();
mock_artifact_reader
.append_artifact(&PathBuf::from(static_pkgs_hash.to_string()), static_pkgs_contents);
mock_artifact_reader
.append_artifact(&PathBuf::from(system_image_hash.to_string()), system_image_contents);
let result = collect_static_pkgs(
hashmap! {
PKGFS_CMD_ADDITIONAL_BOOT_CONFIG_KEY.to_string() => vec![
PKGFS_BINARY_PATH.to_string(),
system_image_hash.to_string(),
],
},
Box::new(mock_artifact_reader),
)
.unwrap();
assert_eq!(result.static_pkgs, static_pkgs);
}
#[fuchsia::test]
fn test_some_static_pkgs() {
let mut mock_artifact_reader = MockArtifactReader::new();
let alpha_hash = Hash::from([0; HASH_SIZE]);
let beta_hash = Hash::from([1; HASH_SIZE]);
let static_pkgs = hashmap! {
(PackageName::from_str("alpha").unwrap(), Some(PackageVariant::zero())) => alpha_hash.clone(),
(PackageName::from_str("beta").unwrap(), Some(PackageVariant::zero())) => beta_hash.clone(),
};
let static_pkgs_contents = create_static_pkgs_listing(static_pkgs.clone());
let static_pkgs_hash =
MerkleTree::from_reader(static_pkgs_contents.as_slice()).unwrap().root();
let system_image_contents = create_system_image_far(Some(static_pkgs_hash.clone()));
let system_image_hash =
MerkleTree::from_reader(system_image_contents.as_slice()).unwrap().root();
mock_artifact_reader
.append_artifact(&PathBuf::from(static_pkgs_hash.to_string()), static_pkgs_contents);
mock_artifact_reader
.append_artifact(&PathBuf::from(system_image_hash.to_string()), system_image_contents);
let result = collect_static_pkgs(
hashmap! {
PKGFS_CMD_ADDITIONAL_BOOT_CONFIG_KEY.to_string() => vec![
PKGFS_BINARY_PATH.to_string(),
system_image_hash.to_string(),
],
},
Box::new(mock_artifact_reader),
)
.unwrap();
assert_eq!(result.static_pkgs, static_pkgs);
}
}