1use blob_writer::BlobWriter;
6use block_client::{BlockClient as _, RemoteBlockClient};
7use delivery_blob::{CompressionMode, Type1Blob};
8use fake_block_server::FakeServer;
9use fidl::endpoints::Proxy as _;
10use fidl_fuchsia_fs_startup::{CreateOptions, MountOptions};
11use fidl_fuchsia_fxfs::{BlobCreatorProxy, CryptManagementMarker, CryptMarker, KeyPurpose};
12use fs_management::filesystem::{
13 BlockConnector, DirBasedBlockConnector, Filesystem, ServingMultiVolumeFilesystem,
14};
15use fs_management::format::constants::{F2FS_MAGIC, FXFS_MAGIC, MINFS_MAGIC};
16use fs_management::{Fvm, Fxfs, BLOBFS_TYPE_GUID, DATA_TYPE_GUID, FVM_TYPE_GUID};
17use fuchsia_component::client::connect_to_protocol_at_dir_svc;
18use fuchsia_component_test::{Capability, ChildOptions, RealmBuilder, RealmInstance, Ref, Route};
19use fuchsia_hash::Hash;
20use gpt_component::gpt::GptManager;
21use key_bag::Aes256Key;
22use std::ops::Deref;
23use std::sync::Arc;
24use storage_isolated_driver_manager::fvm::format_for_fvm;
25use uuid::Uuid;
26use zerocopy::{Immutable, IntoBytes};
27use zx::{self as zx, HandleBased};
28use {fidl_fuchsia_io as fio, fidl_fuchsia_logger as flogger, fuchsia_async as fasync};
29
30pub const TEST_DISK_BLOCK_SIZE: u32 = 512;
31pub const FVM_SLICE_SIZE: u64 = 32 * 1024;
32
33pub const DEFAULT_F2FS_MIN_BYTES: u64 = 50 * 1024 * 1024;
43pub const DEFAULT_DATA_VOLUME_SIZE: u64 = DEFAULT_F2FS_MIN_BYTES + 1024 * 1024;
44pub const DEFAULT_REMAINING_VOLUME_SIZE: u64 = 4 * 1024 * 1024;
45pub const DEFAULT_DISK_SIZE: u64 = DEFAULT_DATA_VOLUME_SIZE + DEFAULT_REMAINING_VOLUME_SIZE;
48
49const KEY_BAG_CONTENTS: &'static str = "\
54{
55 \"version\":1,
56 \"keys\": {
57 \"0\":{
58 \"Aes128GcmSivWrapped\": [
59 \"7a7c6a718cfde7078f6edec5\",
60 \"7cc31b765c74db3191e269d2666267022639e758fe3370e8f36c166d888586454fd4de8aeb47aadd81c531b0a0a66f27\"
61 ]
62 },
63 \"1\":{
64 \"Aes128GcmSivWrapped\": [
65 \"b7d7f459cbee4cc536cc4324\",
66 \"9f6a5d894f526b61c5c091e5e02a7ff94d18e6ad36a0aa439c86081b726eca79e6b60bd86ee5d86a20b3df98f5265a99\"
67 ]
68 }
69 }
70}";
71
72pub const BLOB_CONTENTS: [u8; 1000] = [1; 1000];
73
74const DATA_KEY: Aes256Key = Aes256Key::create([
75 0xcf, 0x9e, 0x45, 0x2a, 0x22, 0xa5, 0x70, 0x31, 0x33, 0x3b, 0x4d, 0x6b, 0x6f, 0x78, 0x58, 0x29,
76 0x04, 0x79, 0xc7, 0xd6, 0xa9, 0x4b, 0xce, 0x82, 0x04, 0x56, 0x5e, 0x82, 0xfc, 0xe7, 0x37, 0xa8,
77]);
78
79const METADATA_KEY: Aes256Key = Aes256Key::create([
80 0x0f, 0x4d, 0xca, 0x6b, 0x35, 0x0e, 0x85, 0x6a, 0xb3, 0x8c, 0xdd, 0xe9, 0xda, 0x0e, 0xc8, 0x22,
81 0x8e, 0xea, 0xd8, 0x05, 0xc4, 0xc9, 0x0b, 0xa8, 0xd8, 0x85, 0x87, 0x50, 0x75, 0x40, 0x1c, 0x4c,
82]);
83
84const FVM_PART_INSTANCE_GUID: [u8; 16] = [3u8; 16];
85const DEFAULT_TEST_TYPE_GUID: [u8; 16] = [
86 0x66, 0x73, 0x68, 0x6F, 0x73, 0x74, 0x20, 0x69, 0x6E, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x73,
87];
88
89async fn create_hermetic_crypt_service(
90 data_key: Aes256Key,
91 metadata_key: Aes256Key,
92) -> RealmInstance {
93 let builder = RealmBuilder::new().await.unwrap();
94 let url = "#meta/fxfs-crypt.cm";
95 let crypt = builder.add_child("fxfs-crypt", url, ChildOptions::new().eager()).await.unwrap();
96 builder
97 .add_route(
98 Route::new()
99 .capability(Capability::protocol::<CryptMarker>())
100 .capability(Capability::protocol::<CryptManagementMarker>())
101 .from(&crypt)
102 .to(Ref::parent()),
103 )
104 .await
105 .unwrap();
106 builder
107 .add_route(
108 Route::new()
109 .capability(Capability::protocol::<flogger::LogSinkMarker>())
110 .from(Ref::parent())
111 .to(&crypt),
112 )
113 .await
114 .unwrap();
115 let realm = builder.build().await.expect("realm build failed");
116 let crypt_management =
117 realm.root.connect_to_protocol_at_exposed_dir::<CryptManagementMarker>().unwrap();
118 let wrapping_key_id_0 = [0; 16];
119 let mut wrapping_key_id_1 = [0; 16];
120 wrapping_key_id_1[0] = 1;
121 crypt_management
122 .add_wrapping_key(&wrapping_key_id_0, data_key.deref())
123 .await
124 .unwrap()
125 .expect("add_wrapping_key failed");
126 crypt_management
127 .add_wrapping_key(&wrapping_key_id_1, metadata_key.deref())
128 .await
129 .unwrap()
130 .expect("add_wrapping_key failed");
131 crypt_management
132 .set_active_key(KeyPurpose::Data, &wrapping_key_id_0)
133 .await
134 .unwrap()
135 .expect("set_active_key failed");
136 crypt_management
137 .set_active_key(KeyPurpose::Metadata, &wrapping_key_id_1)
138 .await
139 .unwrap()
140 .expect("set_active_key failed");
141 realm
142}
143
144pub async fn write_test_blob(blob_creator: BlobCreatorProxy, data: &[u8]) -> Hash {
146 let hash = fuchsia_merkle::from_slice(data).root();
147 let compressed_data = Type1Blob::generate(&data, CompressionMode::Always);
148
149 let blob_writer_client_end = blob_creator
150 .create(&hash.into(), false)
151 .await
152 .expect("transport error on create")
153 .expect("failed to create blob");
154
155 let writer = blob_writer_client_end.into_proxy();
156 let mut blob_writer = BlobWriter::create(writer, compressed_data.len() as u64)
157 .await
158 .expect("failed to create BlobWriter");
159 blob_writer.write(&compressed_data).await.unwrap();
160 hash
161}
162
163pub enum Disk {
164 Prebuilt(zx::Vmo, Option<[u8; 16]>),
165 Builder(DiskBuilder),
166}
167
168impl Disk {
169 pub async fn into_vmo_and_type_guid(self) -> (zx::Vmo, Option<[u8; 16]>) {
170 match self {
171 Disk::Prebuilt(vmo, guid) => (vmo, guid),
172 Disk::Builder(builder) => builder.build().await,
173 }
174 }
175
176 pub fn builder(&mut self) -> &mut DiskBuilder {
177 match self {
178 Disk::Prebuilt(..) => panic!("attempted to get builder for prebuilt disk"),
179 Disk::Builder(builder) => builder,
180 }
181 }
182}
183
184#[derive(Debug, Default)]
185pub struct DataSpec {
186 pub format: Option<&'static str>,
187 pub zxcrypt: bool,
188}
189
190#[derive(Debug)]
191pub struct VolumesSpec {
192 pub fxfs_blob: bool,
193 pub create_data_partition: bool,
194}
195
196enum FxfsType {
197 Fxfs(Box<dyn BlockConnector>),
198 FxBlob(ServingMultiVolumeFilesystem, RealmInstance),
199}
200
201#[derive(Debug)]
202pub struct DiskBuilder {
203 size: u64,
204 uninitialized: bool,
206 blob_hash: Option<Hash>,
207 data_volume_size: u64,
208 data_spec: DataSpec,
209 volumes_spec: VolumesSpec,
210 corrupt_data: bool,
212 gpt: bool,
213 extra_volumes: Vec<&'static str>,
214 format_volume_manager: bool,
216 legacy_data_label: bool,
217 fs_switch: Option<String>,
219 type_guid: Option<[u8; 16]>,
221}
222
223impl DiskBuilder {
224 pub fn uninitialized() -> DiskBuilder {
225 Self { uninitialized: true, type_guid: None, ..Self::new() }
226 }
227
228 pub fn new() -> DiskBuilder {
229 DiskBuilder {
230 size: DEFAULT_DISK_SIZE,
231 uninitialized: false,
232 blob_hash: None,
233 data_volume_size: DEFAULT_DATA_VOLUME_SIZE,
234 data_spec: DataSpec { format: None, zxcrypt: false },
235 volumes_spec: VolumesSpec { fxfs_blob: false, create_data_partition: true },
236 corrupt_data: false,
237 gpt: false,
238 extra_volumes: Vec::new(),
239 format_volume_manager: true,
240 legacy_data_label: false,
241 fs_switch: None,
242 type_guid: Some(DEFAULT_TEST_TYPE_GUID),
243 }
244 }
245
246 pub fn set_uninitialized(&mut self) -> &mut Self {
247 self.uninitialized = true;
248 self
249 }
250
251 pub fn size(&mut self, size: u64) -> &mut Self {
252 self.size = size;
253 self
254 }
255
256 pub fn data_volume_size(&mut self, data_volume_size: u64) -> &mut Self {
257 assert_eq!(
258 data_volume_size % FVM_SLICE_SIZE,
259 0,
260 "data_volume_size {} needs to be a multiple of fvm slice size {}",
261 data_volume_size,
262 FVM_SLICE_SIZE
263 );
264 self.data_volume_size = data_volume_size;
265 self.size = self.size.max(self.data_volume_size + DEFAULT_REMAINING_VOLUME_SIZE);
269 self
270 }
271
272 pub fn format_volumes(&mut self, volumes_spec: VolumesSpec) -> &mut Self {
273 self.volumes_spec = volumes_spec;
274 self
275 }
276
277 pub fn format_data(&mut self, data_spec: DataSpec) -> &mut Self {
278 log::info!(data_spec:?; "formatting data volume");
279 if !self.volumes_spec.fxfs_blob {
280 assert!(self.format_volume_manager);
281 } else {
282 if let Some(format) = data_spec.format {
283 assert_eq!(format, "fxfs");
284 }
285 }
286 self.data_spec = data_spec;
287 self
288 }
289
290 pub fn set_fs_switch(&mut self, content: &str) -> &mut Self {
291 self.fs_switch = Some(content.to_string());
292 self
293 }
294
295 pub fn corrupt_data(&mut self) -> &mut Self {
296 self.corrupt_data = true;
297 self
298 }
299
300 pub fn with_gpt(&mut self) -> &mut Self {
301 self.gpt = true;
302 self.type_guid = None;
305 self
306 }
307
308 pub fn with_extra_volume(&mut self, volume_name: &'static str) -> &mut Self {
309 self.extra_volumes.push(volume_name);
310 self
311 }
312
313 pub fn with_unformatted_volume_manager(&mut self) -> &mut Self {
314 assert!(self.data_spec.format.is_none());
315 self.format_volume_manager = false;
316 self
317 }
318
319 pub fn with_legacy_data_label(&mut self) -> &mut Self {
320 self.legacy_data_label = true;
321 self
322 }
323
324 pub async fn build(mut self) -> (zx::Vmo, Option<[u8; 16]>) {
325 log::info!("building disk: {:?}", self);
326 if self.data_spec.format == Some("f2fs") {
327 assert!(self.data_volume_size >= DEFAULT_F2FS_MIN_BYTES);
328 }
329
330 let vmo = zx::Vmo::create(self.size).unwrap();
331
332 if self.uninitialized {
333 return (vmo, self.type_guid);
334 }
335
336 let server = Arc::new(FakeServer::from_vmo(
337 TEST_DISK_BLOCK_SIZE,
338 vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap(),
339 ));
340
341 if self.gpt {
342 let client = Arc::new(RemoteBlockClient::new(server.block_proxy()).await.unwrap());
344 let _ = gpt::Gpt::format(
345 client,
346 vec![gpt::PartitionInfo {
347 label: "fvm".to_string(),
348 type_guid: gpt::Guid::from_bytes(FVM_TYPE_GUID),
349 instance_guid: gpt::Guid::from_bytes(FVM_PART_INSTANCE_GUID),
350 start_block: 64,
351 num_blocks: self.size / TEST_DISK_BLOCK_SIZE as u64 - 128,
352 flags: 0,
353 }],
354 )
355 .await
356 .expect("gpt format failed");
357 }
358
359 if !self.format_volume_manager {
360 return (vmo, self.type_guid);
361 }
362
363 let mut gpt = None;
364 let connector: Box<dyn BlockConnector> = if self.gpt {
365 let partitions_dir = vfs::directory::immutable::simple();
367 let manager =
368 GptManager::new(server.block_proxy(), partitions_dir.clone()).await.unwrap();
369 let dir =
370 vfs::directory::serve(partitions_dir, fio::PERM_READABLE | fio::PERM_WRITABLE);
371 gpt = Some(manager);
372 Box::new(DirBasedBlockConnector::new(dir, String::from("part-000/volume")))
373 } else {
374 Box::new(move |server_end| Ok(server.connect(server_end)))
376 };
377
378 if self.volumes_spec.fxfs_blob {
379 self.build_fxfs_as_volume_manager(connector).await;
380 } else {
381 self.build_fvm_as_volume_manager(connector).await;
382 }
383 if let Some(gpt) = gpt {
384 gpt.shutdown().await;
385 }
386 (vmo, self.type_guid)
387 }
388
389 async fn build_fxfs_as_volume_manager(&mut self, connector: Box<dyn BlockConnector>) {
390 let crypt_realm = create_hermetic_crypt_service(DATA_KEY, METADATA_KEY).await;
391 let mut fxfs = Filesystem::from_boxed_config(connector, Box::new(Fxfs::default()));
392 fxfs.format().await.expect("format failed");
394 let mut fs = fxfs.serve_multi_volume().await.expect("serve_multi_volume failed");
395 let blob_volume = fs
396 .create_volume(
397 "blob",
398 CreateOptions::default(),
399 MountOptions { as_blob: Some(true), ..MountOptions::default() },
400 )
401 .await
402 .expect("failed to create blob volume");
403 let blob_creator = connect_to_protocol_at_dir_svc::<fidl_fuchsia_fxfs::BlobCreatorMarker>(
404 blob_volume.exposed_dir(),
405 )
406 .expect("failed to connect to the Blob service");
407 self.blob_hash = Some(write_test_blob(blob_creator, &BLOB_CONTENTS).await);
408
409 for volume in &self.extra_volumes {
410 fs.create_volume(volume, CreateOptions::default(), MountOptions::default())
411 .await
412 .expect("failed to make extra fxfs volume");
413 }
414
415 if self.data_spec.format.is_some() {
416 self.init_data_fxfs(FxfsType::FxBlob(fs, crypt_realm)).await;
417 } else {
418 fs.shutdown().await.expect("shutdown failed");
419 }
420 }
421
422 async fn build_fvm_as_volume_manager(&mut self, connector: Box<dyn BlockConnector>) {
423 let block_device = connector.connect_block().unwrap().into_proxy();
424 fasync::unblock(move || format_for_fvm(&block_device, FVM_SLICE_SIZE as usize))
425 .await
426 .unwrap();
427 let mut fvm_fs = Filesystem::from_boxed_config(connector, Box::new(Fvm::dynamic_child()));
428 let mut fvm = fvm_fs.serve_multi_volume().await.unwrap();
429
430 {
431 let blob_volume = fvm
432 .create_volume(
433 "blobfs",
434 CreateOptions {
435 type_guid: Some(BLOBFS_TYPE_GUID),
436 guid: Some(Uuid::new_v4().into_bytes()),
437 ..Default::default()
438 },
439 MountOptions {
440 uri: Some(String::from("#meta/blobfs.cm")),
441 ..Default::default()
442 },
443 )
444 .await
445 .expect("failed to make fvm blobfs volume");
446 let blob_creator =
447 connect_to_protocol_at_dir_svc::<fidl_fuchsia_fxfs::BlobCreatorMarker>(
448 blob_volume.exposed_dir(),
449 )
450 .expect("failed to connect to the Blob service");
451 self.blob_hash = Some(write_test_blob(blob_creator, &BLOB_CONTENTS).await);
452 }
453 fvm.shutdown_volume("blobfs").await.unwrap();
454
455 if self.volumes_spec.create_data_partition {
456 let data_label = if self.legacy_data_label { "minfs" } else { "data" };
457
458 let _crypt_service;
459 let crypt = if self.data_spec.format != Some("fxfs") && self.data_spec.zxcrypt {
460 let (crypt, stream) = fidl::endpoints::create_request_stream();
461 _crypt_service = fasync::Task::spawn(zxcrypt_crypt::run_crypt_service(
462 crypt_policy::Policy::Null,
463 stream,
464 ));
465 Some(crypt)
466 } else {
467 None
468 };
469 let uri = match (&self.data_spec.format, self.corrupt_data) {
470 (None, _) => None,
471 (_, true) => None,
472 (Some("fxfs"), false) => None,
473 (Some("minfs"), false) => Some(String::from("#meta/minfs.cm")),
474 (Some("f2fs"), false) => Some(String::from("#meta/f2fs.cm")),
475 (Some(format), _) => panic!("unsupported data volume format '{}'", format),
476 };
477
478 let data_volume = fvm
479 .create_volume(
480 data_label,
481 CreateOptions {
482 initial_size: Some(self.data_volume_size),
483 type_guid: Some(DATA_TYPE_GUID),
484 guid: Some(Uuid::new_v4().into_bytes()),
485 ..Default::default()
486 },
487 MountOptions { crypt, uri, ..Default::default() },
488 )
489 .await
490 .unwrap();
491
492 if self.corrupt_data {
493 let volume_proxy = connect_to_protocol_at_dir_svc::<
494 fidl_fuchsia_hardware_block_volume::VolumeMarker,
495 >(data_volume.exposed_dir())
496 .unwrap();
497 match self.data_spec.format {
498 Some("fxfs") => self.write_magic(volume_proxy, FXFS_MAGIC, 0).await,
499 Some("minfs") => self.write_magic(volume_proxy, MINFS_MAGIC, 0).await,
500 Some("f2fs") => self.write_magic(volume_proxy, F2FS_MAGIC, 1024).await,
501 _ => (),
502 }
503 } else if self.data_spec.format == Some("fxfs") {
504 let dir = fuchsia_fs::directory::clone(data_volume.exposed_dir()).unwrap();
505 self.init_data_fxfs(FxfsType::Fxfs(Box::new(DirBasedBlockConnector::new(
506 dir,
507 String::from("svc/fuchsia.hardware.block.volume.Volume"),
508 ))))
509 .await
510 } else if self.data_spec.format.is_some() {
511 self.write_test_data(data_volume.root()).await;
512 fvm.shutdown_volume(data_label).await.unwrap();
513 }
514 }
515
516 for volume in &self.extra_volumes {
517 fvm.create_volume(
518 volume,
519 CreateOptions {
520 type_guid: Some(DATA_TYPE_GUID),
521 guid: Some(Uuid::new_v4().into_bytes()),
522 ..Default::default()
523 },
524 MountOptions::default(),
525 )
526 .await
527 .expect("failed to make extra fvm volume");
528 }
529
530 fvm.shutdown().await.expect("fvm shutdown failed");
531 }
532
533 async fn init_data_fxfs(&self, fxfs: FxfsType) {
534 let mut fxblob = false;
535 let (mut fs, crypt_realm) = match fxfs {
536 FxfsType::Fxfs(connector) => {
537 let crypt_realm = create_hermetic_crypt_service(DATA_KEY, METADATA_KEY).await;
538 let mut fxfs =
539 Filesystem::from_boxed_config(connector, Box::new(Fxfs::dynamic_child()));
540 fxfs.format().await.expect("format failed");
541 (fxfs.serve_multi_volume().await.expect("serve_multi_volume failed"), crypt_realm)
542 }
543 FxfsType::FxBlob(fs, crypt_realm) => {
544 fxblob = true;
545 (fs, crypt_realm)
546 }
547 };
548
549 let vol = {
550 let vol = fs
551 .create_volume("unencrypted", CreateOptions::default(), MountOptions::default())
552 .await
553 .expect("create_volume failed");
554 let keys_dir = fuchsia_fs::directory::create_directory(
555 vol.root(),
556 "keys",
557 fio::PERM_READABLE | fio::PERM_WRITABLE,
558 )
559 .await
560 .unwrap();
561 let keys_file = fuchsia_fs::directory::open_file(
562 &keys_dir,
563 "fxfs-data",
564 fio::Flags::FLAG_MAYBE_CREATE
565 | fio::Flags::PROTOCOL_FILE
566 | fio::PERM_READABLE
567 | fio::PERM_WRITABLE,
568 )
569 .await
570 .unwrap();
571 let mut key_bag = KEY_BAG_CONTENTS.as_bytes();
572 if self.corrupt_data && fxblob {
573 key_bag = &BLOB_CONTENTS;
574 }
575 fuchsia_fs::file::write(&keys_file, key_bag).await.unwrap();
576 fuchsia_fs::file::close(keys_file).await.unwrap();
577 fuchsia_fs::directory::close(keys_dir).await.unwrap();
578
579 let crypt_service = Some(
580 crypt_realm
581 .root
582 .connect_to_protocol_at_exposed_dir::<CryptMarker>()
583 .expect("Unable to connect to Crypt service")
584 .into_channel()
585 .unwrap()
586 .into_zx_channel()
587 .into(),
588 );
589 fs.create_volume(
590 "data",
591 CreateOptions::default(),
592 MountOptions { crypt: crypt_service, ..MountOptions::default() },
593 )
594 .await
595 .expect("create_volume failed")
596 };
597 self.write_test_data(&vol.root()).await;
598 fs.shutdown().await.expect("shutdown failed");
599 }
600
601 async fn write_test_data(&self, root: &fio::DirectoryProxy) {
609 fuchsia_fs::directory::open_file(
610 root,
611 ".testdata",
612 fio::Flags::FLAG_MAYBE_CREATE | fio::PERM_READABLE,
613 )
614 .await
615 .unwrap();
616
617 let ssh_dir = fuchsia_fs::directory::create_directory(
618 root,
619 "ssh",
620 fio::PERM_READABLE | fio::PERM_WRITABLE,
621 )
622 .await
623 .unwrap();
624 let authorized_keys = fuchsia_fs::directory::open_file(
625 &ssh_dir,
626 "authorized_keys",
627 fio::Flags::FLAG_MAYBE_CREATE | fio::PERM_READABLE | fio::PERM_WRITABLE,
628 )
629 .await
630 .unwrap();
631 fuchsia_fs::file::write(&authorized_keys, "public key!").await.unwrap();
632 fuchsia_fs::directory::create_directory(&ssh_dir, "config", fio::PERM_READABLE)
633 .await
634 .unwrap();
635
636 fuchsia_fs::directory::create_directory(&root, "problems", fio::PERM_READABLE)
637 .await
638 .unwrap();
639
640 if let Some(content) = &self.fs_switch {
641 let fs_switch = fuchsia_fs::directory::open_file(
642 &root,
643 "fs_switch",
644 fio::Flags::FLAG_MAYBE_CREATE | fio::PERM_READABLE | fio::PERM_WRITABLE,
645 )
646 .await
647 .unwrap();
648 fuchsia_fs::file::write(&fs_switch, content).await.unwrap();
649 }
650 }
651
652 async fn write_magic<const N: usize>(
653 &self,
654 volume_proxy: fidl_fuchsia_hardware_block_volume::VolumeProxy,
655 value: [u8; N],
656 offset: u64,
657 ) {
658 let client = block_client::RemoteBlockClient::new(volume_proxy)
659 .await
660 .expect("Failed to create client");
661 let block_size = client.block_size() as usize;
662 assert!(value.len() <= block_size);
663 let mut data = vec![0xffu8; block_size];
664 data[..value.len()].copy_from_slice(&value);
665 let buffer = block_client::BufferSlice::Memory(&data[..]);
666 client.write_at(buffer, offset).await.expect("write failed");
667 }
668
669 pub(crate) async fn build_as_zbi_ramdisk(self) -> zx::Vmo {
672 const ZBI_TYPE_STORAGE_RAMDISK: u32 = 0x4b534452;
675 const ZBI_FLAGS_VERSION: u32 = 0x00010000;
676 const ZBI_ITEM_MAGIC: u32 = 0xb5781729;
677 const ZBI_FLAGS_STORAGE_COMPRESSED: u32 = 0x00000001;
678
679 #[repr(C)]
680 #[derive(IntoBytes, Immutable)]
681 struct ZbiHeader {
682 type_: u32,
683 length: u32,
684 extra: u32,
685 flags: u32,
686 _reserved0: u32,
687 _reserved1: u32,
688 magic: u32,
689 _crc32: u32,
690 }
691
692 let (ramdisk_vmo, _) = self.build().await;
693 let extra = ramdisk_vmo.get_size().unwrap() as u32;
694 let mut decompressed_buf = vec![0u8; extra as usize];
695 ramdisk_vmo.read(&mut decompressed_buf, 0).unwrap();
696 let compressed_buf = zstd::encode_all(decompressed_buf.as_slice(), 0).unwrap();
697 let length = compressed_buf.len() as u32;
698
699 let header = ZbiHeader {
700 type_: ZBI_TYPE_STORAGE_RAMDISK,
701 length,
702 extra,
703 flags: ZBI_FLAGS_VERSION | ZBI_FLAGS_STORAGE_COMPRESSED,
704 _reserved0: 0,
705 _reserved1: 0,
706 magic: ZBI_ITEM_MAGIC,
707 _crc32: 0,
708 };
709
710 let header_size = std::mem::size_of::<ZbiHeader>() as u64;
711 let zbi_vmo = zx::Vmo::create(header_size + length as u64).unwrap();
712 zbi_vmo.write(header.as_bytes(), 0).unwrap();
713 zbi_vmo.write(&compressed_buf, header_size).unwrap();
714
715 zbi_vmo
716 }
717}