system_image/
system_image.rs1use crate::{
6 get_system_image_hash, CachePackages, CachePackagesInitError, StaticPackages,
7 StaticPackagesInitError,
8};
9use anyhow::Context as _;
10use fuchsia_hash::Hash;
11use package_directory::RootDir;
12use std::sync::Arc;
13
14static DISABLE_RESTRICTIONS_FILE_PATH: &str = "data/pkgfs_disable_executability_restrictions";
15
16#[derive(Debug, PartialEq, Eq, Clone, Copy)]
17pub enum ExecutabilityRestrictions {
18 Enforce,
19 DoNotEnforce,
20}
21
22pub struct SystemImage {
24 root_dir: Arc<RootDir<blobfs::Client>>,
25}
26
27impl SystemImage {
28 pub async fn new(
29 blobfs: blobfs::Client,
30 boot_args: &fidl_fuchsia_boot::ArgumentsProxy,
31 ) -> Result<Self, anyhow::Error> {
32 let hash = get_system_image_hash(boot_args).await.context("getting system_image hash")?;
33 let root_dir = RootDir::new(blobfs, hash)
34 .await
35 .with_context(|| format!("creating RootDir for system_image: {hash}"))?;
36 Ok(SystemImage { root_dir })
37 }
38
39 pub fn from_root_dir(root_dir: Arc<RootDir<blobfs::Client>>) -> Self {
41 Self { root_dir }
42 }
43
44 pub fn load_executability_restrictions(&self) -> ExecutabilityRestrictions {
45 match self.root_dir.has_file(DISABLE_RESTRICTIONS_FILE_PATH) {
46 true => ExecutabilityRestrictions::DoNotEnforce,
47 false => ExecutabilityRestrictions::Enforce,
48 }
49 }
50
51 pub fn hash(&self) -> &Hash {
53 self.root_dir.hash()
54 }
55
56 pub async fn cache_packages(&self) -> Result<CachePackages, CachePackagesInitError> {
58 self.root_dir
59 .read_file("data/cache_packages.json")
60 .await
61 .map_err(CachePackagesInitError::ReadCachePackagesJson)
62 .and_then(|content| CachePackages::from_json(content.as_slice()))
63 }
64
65 pub async fn static_packages(&self) -> Result<StaticPackages, StaticPackagesInitError> {
67 StaticPackages::deserialize(
68 self.root_dir
69 .read_file("data/static_packages")
70 .await
71 .map_err(StaticPackagesInitError::ReadStaticPackages)?
72 .as_slice(),
73 )
74 .map_err(StaticPackagesInitError::ProcessingStaticPackages)
75 }
76
77 pub fn into_root_dir(self) -> Arc<RootDir<blobfs::Client>> {
79 self.root_dir
80 }
81
82 pub fn package_path() -> fuchsia_pkg::PackagePath {
84 fuchsia_pkg::PackagePath::from_name_and_variant(
85 "system_image".parse().expect("valid package name"),
86 fuchsia_pkg::PackageVariant::zero(),
87 )
88 }
89}
90
91#[cfg(test)]
92mod tests {
93 use super::*;
94 use assert_matches::assert_matches;
95 use fuchsia_pkg_testing::SystemImageBuilder;
96
97 struct TestEnv {
98 _blobfs: blobfs_ramdisk::BlobfsRamdisk,
99 }
100
101 impl TestEnv {
102 async fn new(system_image: SystemImageBuilder) -> (Self, SystemImage) {
103 let blobfs = blobfs_ramdisk::BlobfsRamdisk::start().await.unwrap();
104 let system_image = system_image.build().await;
105 system_image.write_to_blobfs(&blobfs).await;
106 let root_dir = RootDir::new(blobfs.client(), *system_image.hash()).await.unwrap();
107 (Self { _blobfs: blobfs }, SystemImage { root_dir })
108 }
109 }
110
111 #[fuchsia_async::run_singlethreaded(test)]
112 async fn cache_packages_fails_without_config_files() {
113 let (_env, system_image) = TestEnv::new(SystemImageBuilder::new()).await;
114 assert_matches!(
115 system_image.cache_packages().await,
116 Err(CachePackagesInitError::ReadCachePackagesJson(
117 package_directory::ReadFileError::NoFileAtPath { .. }
118 ))
119 );
120 }
121
122 #[fuchsia_async::run_singlethreaded(test)]
123 async fn cache_packages_deserialize_valid_line_oriented() {
124 let (_env, system_image) = TestEnv::new(
125 SystemImageBuilder::new()
126 .cache_package("name/variant".parse().unwrap(), [0; 32].into()),
127 )
128 .await;
129
130 assert_eq!(
131 system_image.cache_packages().await.unwrap(),
132 CachePackages::from_entries(
133 vec!["fuchsia-pkg://fuchsia.com/name/variant?hash=0000000000000000000000000000000000000000000000000000000000000000"
134 .parse()
135 .unwrap()
136 ]
137 )
138 );
139 }
140
141 #[fuchsia_async::run_singlethreaded(test)]
142 async fn static_packages_deserialize_valid_line_oriented() {
143 let (_env, system_image) = TestEnv::new(
144 SystemImageBuilder::new()
145 .static_package("name/variant".parse().unwrap(), [0; 32].into()),
146 )
147 .await;
148
149 assert_eq!(
150 system_image.static_packages().await.unwrap(),
151 StaticPackages::from_entries(vec![("name/variant".parse().unwrap(), [0; 32].into())])
152 );
153 }
154}