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