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