1use fidl_fuchsia_fxfs::{BlobCreatorMarker, BlobReaderMarker};
6use fshost_assembly_config;
7use fuchsia_component_test::{Capability, ChildOptions, ChildRef, RealmBuilder, Ref, Route};
8use futures::future::FutureExt as _;
9use std::collections::HashMap;
10
11use {
12 fidl_fuchsia_fshost as ffshost, fidl_fuchsia_fxfs as ffxfs, fidl_fuchsia_io as fio,
13 fidl_fuchsia_logger as flogger, fidl_fuchsia_process as fprocess,
14 fidl_fuchsia_storage_partitions as fpartitions, fidl_fuchsia_update_verify as ffuv,
15};
16
17pub trait IntoValueSpec {
18 fn into_value_spec(self) -> cm_rust::ConfigValueSpec;
19}
20
21impl IntoValueSpec for bool {
22 fn into_value_spec(self) -> cm_rust::ConfigValueSpec {
23 cm_rust::ConfigValueSpec {
24 value: cm_rust::ConfigValue::Single(cm_rust::ConfigSingleValue::Bool(self)),
25 }
26 }
27}
28
29impl IntoValueSpec for u64 {
30 fn into_value_spec(self) -> cm_rust::ConfigValueSpec {
31 cm_rust::ConfigValueSpec {
32 value: cm_rust::ConfigValue::Single(cm_rust::ConfigSingleValue::Uint64(self)),
33 }
34 }
35}
36
37impl IntoValueSpec for String {
38 fn into_value_spec(self) -> cm_rust::ConfigValueSpec {
39 cm_rust::ConfigValueSpec {
40 value: cm_rust::ConfigValue::Single(cm_rust::ConfigSingleValue::String(self)),
41 }
42 }
43}
44
45impl<'a> IntoValueSpec for &'a str {
46 fn into_value_spec(self) -> cm_rust::ConfigValueSpec {
47 self.to_string().into_value_spec()
48 }
49}
50
51#[derive(Debug, Clone)]
59pub struct FshostBuilder {
60 component_name: &'static str,
61 config_values: HashMap<&'static str, cm_rust::ConfigValueSpec>,
62 create_starnix_volume_crypt: bool,
63 block_device_config_json: String,
64 crypt_policy: crypt_policy::Policy,
65}
66
67impl FshostBuilder {
68 pub fn new(component_name: &'static str) -> FshostBuilder {
69 FshostBuilder {
70 component_name,
71 config_values: HashMap::new(),
72 create_starnix_volume_crypt: false,
73 block_device_config_json: String::from("[]"),
74 crypt_policy: crypt_policy::Policy::Null,
75 }
76 }
77
78 pub fn create_starnix_volume_crypt(&mut self) -> &mut Self {
79 self.create_starnix_volume_crypt = true;
80 self
81 }
82
83 pub fn set_config_value(&mut self, key: &'static str, value: impl IntoValueSpec) -> &mut Self {
84 assert!(
85 self.config_values.insert(key, value.into_value_spec()).is_none(),
86 "Attempted to insert duplicate config value '{}'!",
87 key
88 );
89 self
90 }
91
92 pub fn set_device_config(
93 &mut self,
94 config: Vec<fshost_assembly_config::BlockDeviceConfig>,
95 ) -> &mut Self {
96 self.block_device_config_json = serde_json::to_string(&config).unwrap();
97 self
98 }
99
100 pub fn set_crypt_policy(&mut self, policy: crypt_policy::Policy) -> &mut Self {
101 self.crypt_policy = policy;
102 self
103 }
104
105 pub async fn build(mut self, realm_builder: &RealmBuilder) -> ChildRef {
106 let fshost_url = format!("#meta/{}.cm", self.component_name);
107 log::info!(fshost_url:%; "building test fshost instance");
108 let fshost = realm_builder
109 .add_child("test-fshost", fshost_url, ChildOptions::new().eager())
110 .await
111 .unwrap();
112
113 let bootfs = vfs::pseudo_directory! {
114 "boot" => vfs::pseudo_directory! {
115 "config" => vfs::pseudo_directory! {
116 "fshost" => vfs::file::read_only(&self.block_device_config_json),
117 "zxcrypt" => vfs::file::read_only(&format!("{}", self.crypt_policy)),
118 },
119 },
120 };
121 let bootfs = realm_builder
122 .add_local_child(
123 "bootfs",
124 move |handles| {
125 let bootfs = bootfs.clone();
126 async move {
127 let scope = vfs::ExecutionScope::new();
128 vfs::directory::serve_on(
129 bootfs,
130 fio::PERM_READABLE,
131 scope.clone(),
132 handles.outgoing_dir,
133 );
134 scope.wait().await;
135 Ok(())
136 }
137 .boxed()
138 },
139 ChildOptions::new(),
140 )
141 .await
142 .unwrap();
143 realm_builder
144 .add_route(
145 Route::new()
146 .capability(Capability::directory("boot").rights(fio::R_STAR_DIR).path("/boot"))
147 .from(&bootfs)
148 .to(&fshost),
149 )
150 .await
151 .unwrap();
152
153 let mut map = HashMap::from([
155 ("no_zxcrypt", "fuchsia.fshost.NoZxcrypt"),
156 ("ramdisk_image", "fuchsia.fshost.RamdiskImage"),
157 ("gpt_all", "fuchsia.fshost.GptAll"),
158 ("check_filesystems", "fuchsia.fshost.CheckFilesystems"),
159 ("blobfs_max_bytes", "fuchsia.fshost.BlobfsMaxBytes"),
160 ("data_max_bytes", "fuchsia.fshost.DataMaxBytes"),
161 ("format_data_on_corruption", "fuchsia.fshost.FormatDataOnCorruption"),
162 ("data_filesystem_format", "fuchsia.fshost.DataFilesystemFormat"),
163 ("blobfs", "fuchsia.fshost.Blobfs"),
164 ("bootpart", "fuchsia.fshost.BootPart"),
165 ("factory", "fuchsia.fshost.Factory"),
166 ("fvm", "fuchsia.fshost.Fvm"),
167 ("gpt", "fuchsia.fshost.Gpt"),
168 ("mbr", "fuchsia.fshost.Mbr"),
169 ("merge_super_and_userdata", "fuchsia.fshost.MergeSuperAndUserdata"),
170 ("data", "fuchsia.fshost.Data"),
171 ("use_disk_migration", "fuchsia.fshost.UseDiskMigration"),
172 ("disable_block_watcher", "fuchsia.fshost.DisableBlockWatcher"),
173 ("fvm_slice_size", "fuchsia.fshost.FvmSliceSize"),
174 ("blobfs_initial_inodes", "fuchsia.fshost.BlobfsInitialInodes"),
175 (
176 "blobfs_use_deprecated_padded_format",
177 "fuchsia.fshost.BlobfsUseDeprecatedPaddedFormat",
178 ),
179 ("fxfs_blob", "fuchsia.fshost.FxfsBlob"),
180 ("fxfs_crypt_url", "fuchsia.fshost.FxfsCryptUrl"),
181 ("storage_host", "fuchsia.fshost.StorageHost"),
182 ("disable_automount", "fuchsia.fshost.DisableAutomount"),
183 ("starnix_volume_name", "fuchsia.fshost.StarnixVolumeName"),
184 ("inline_crypto", "fuchsia.fshost.InlineCrypto"),
185 ("blobfs_write_compression_algorithm", "fuchsia.blobfs.WriteCompressionAlgorithm"),
186 ("blobfs_cache_eviction_policy", "fuchsia.blobfs.CacheEvictionPolicy"),
187 ("provision_fxfs", "fuchsia.fshost.ProvisionFxfs"),
188 ("watch_deprecated_v1_drivers", "fuchsia.fshost.WatchDeprecatedV1Drivers"),
189 ]);
190
191 if self.create_starnix_volume_crypt {
192 let user_fxfs_crypt = realm_builder
193 .add_child("user_fxfs_crypt", "#meta/fxfs-crypt.cm", ChildOptions::new().eager())
194 .await
195 .unwrap();
196 realm_builder
197 .add_route(
198 Route::new()
199 .capability(Capability::protocol::<ffxfs::CryptMarker>())
200 .capability(Capability::protocol::<ffxfs::CryptManagementMarker>())
201 .from(&user_fxfs_crypt)
202 .to(Ref::parent()),
203 )
204 .await
205 .unwrap();
206 }
207
208 self.config_values.insert("fxfs_crypt_url", "#meta/fxfs-crypt.cm".into_value_spec());
210 for (key, value) in self.config_values {
211 let cap_name = map[key];
212 realm_builder
213 .add_capability(cm_rust::CapabilityDecl::Config(cm_rust::ConfigurationDecl {
214 name: cap_name.parse().unwrap(),
215 value: value.value,
216 }))
217 .await
218 .unwrap();
219 realm_builder
220 .add_route(
221 Route::new()
222 .capability(Capability::configuration(cap_name))
223 .from(Ref::self_())
224 .to(&fshost),
225 )
226 .await
227 .unwrap();
228 map.remove(key);
229 }
230
231 let fshost_config_url = format!("#meta/{}_config.cm", self.component_name);
233 let fshost_config = realm_builder
234 .add_child("test-fshost-config", fshost_config_url, ChildOptions::new().eager())
235 .await
236 .unwrap();
237 for (_, value) in map.iter() {
238 realm_builder
239 .add_route(
240 Route::new()
241 .capability(Capability::configuration(*value))
242 .from(&fshost_config)
243 .to(&fshost),
244 )
245 .await
246 .unwrap();
247 }
248
249 realm_builder
250 .add_route(
251 Route::new()
252 .capability(Capability::protocol::<ffshost::AdminMarker>())
253 .capability(Capability::protocol::<ffshost::RecoveryMarker>())
254 .capability(Capability::protocol::<ffuv::ComponentOtaHealthCheckMarker>())
255 .capability(Capability::protocol::<ffshost::StarnixVolumeProviderMarker>())
256 .capability(Capability::protocol::<fpartitions::PartitionsManagerMarker>())
257 .capability(Capability::protocol::<BlobCreatorMarker>())
258 .capability(Capability::protocol::<BlobReaderMarker>())
259 .capability(Capability::directory("blob").rights(fio::RW_STAR_DIR))
260 .capability(
261 Capability::directory("blob-exec")
262 .rights(fio::RW_STAR_DIR | fio::Operations::EXECUTE),
263 )
264 .capability(Capability::directory("block").rights(fio::R_STAR_DIR))
265 .capability(Capability::directory("debug_block").rights(fio::R_STAR_DIR))
266 .capability(Capability::directory("data").rights(fio::RW_STAR_DIR))
267 .capability(Capability::directory("tmp").rights(fio::RW_STAR_DIR))
268 .capability(Capability::directory("volumes").rights(fio::RW_STAR_DIR))
269 .capability(Capability::service::<fpartitions::PartitionServiceMarker>())
270 .from(&fshost)
271 .to(Ref::parent()),
272 )
273 .await
274 .unwrap();
275
276 realm_builder
277 .add_route(
278 Route::new()
279 .capability(Capability::protocol::<flogger::LogSinkMarker>())
280 .capability(Capability::protocol::<fprocess::LauncherMarker>())
281 .from(Ref::parent())
282 .to(&fshost),
283 )
284 .await
285 .unwrap();
286
287 realm_builder
288 .add_route(
289 Route::new()
290 .capability(
291 Capability::protocol_by_name("fuchsia.scheduler.RoleManager").optional(),
292 )
293 .capability(
294 Capability::protocol_by_name("fuchsia.tracing.provider.Registry")
295 .optional(),
296 )
297 .capability(
298 Capability::protocol_by_name("fuchsia.kernel.VmexResource").optional(),
299 )
300 .capability(
301 Capability::protocol_by_name("fuchsia.memorypressure.Provider").optional(),
302 )
303 .from(Ref::void())
304 .to(&fshost),
305 )
306 .await
307 .unwrap();
308
309 fshost
310 }
311}