1use crate::error::Error;
5use crate::logger::VmgsLogEvent;
6use crate::logger::VmgsLogger;
7use crate::storage::VmgsStorage;
8#[cfg(with_encryption)]
9use anyhow::Context;
10#[cfg(with_encryption)]
11use anyhow::anyhow;
12use cvm_tracing::CVM_ALLOWED;
13use disk_backend::Disk;
14#[cfg(feature = "inspect")]
15use inspect::Inspect;
16#[cfg(feature = "inspect")]
17use inspect_counters::Counter;
18use std::collections::HashMap;
19use std::num::NonZeroU32;
20use std::sync::Arc;
21use vmgs_format::EncryptionAlgorithm;
22use vmgs_format::FileAttribute;
23use vmgs_format::FileId;
24use vmgs_format::VMGS_BYTES_PER_BLOCK;
25use vmgs_format::VMGS_EXTENDED_FILE_TABLE_BLOCK_SIZE;
26use vmgs_format::VMGS_FILE_TABLE_BLOCK_SIZE;
27use vmgs_format::VMGS_MIN_FILE_BLOCK_OFFSET;
28use vmgs_format::VMGS_SIGNATURE;
29use vmgs_format::VMGS_VERSION_3_0;
30use vmgs_format::VmgsAuthTag;
31use vmgs_format::VmgsDatastoreKey;
32use vmgs_format::VmgsEncryptionKey;
33use vmgs_format::VmgsExtendedFileTable;
34use vmgs_format::VmgsFileTable;
35use vmgs_format::VmgsHeader;
36use vmgs_format::VmgsNonce;
37use zerocopy::FromBytes;
38use zerocopy::FromZeros;
39use zerocopy::IntoBytes;
40
41#[derive(Debug)]
43pub struct VmgsFileInfo {
44 pub allocated_bytes: u64,
46 pub valid_bytes: u64,
48 pub encrypted: bool,
50}
51
52#[derive(Clone, Copy, PartialEq, Eq, Debug)]
55#[cfg_attr(feature = "inspect", derive(Inspect))]
56struct ResolvedFileControlBlock {
57 block_offset: u32,
60 #[cfg_attr(feature = "inspect", inspect(with = "|x| x.get()"))]
61 allocated_blocks: NonZeroU32,
62 valid_bytes: u64,
63
64 nonce: VmgsNonce,
65 authentication_tag: VmgsAuthTag,
66
67 attributes: FileAttribute,
70 encryption_key: VmgsDatastoreKey,
71}
72
73#[cfg_attr(feature = "inspect", derive(Inspect))]
76pub struct Vmgs {
77 storage: VmgsStorage,
78
79 #[cfg(feature = "inspect")]
80 stats: vmgs_inspect::VmgsStats,
81
82 active_header_index: usize,
83 active_header_sequence_number: u32,
84 version: u32,
85 #[cfg_attr(feature = "inspect", inspect(with = "vmgs_inspect::fcbs"))]
86 fcbs: HashMap<FileId, ResolvedFileControlBlock>,
87 encryption_algorithm: EncryptionAlgorithm,
88 #[allow(dead_code)]
89 datastore_key_count: u8,
90 active_datastore_key_index: Option<usize>,
91 #[cfg_attr(feature = "inspect", inspect(iter_by_index))]
92 datastore_keys: [VmgsDatastoreKey; 2],
93 metadata_key: VmgsDatastoreKey,
94 #[cfg_attr(feature = "inspect", inspect(iter_by_index))]
95 encrypted_metadata_keys: [VmgsEncryptionKey; 2],
96
97 #[cfg_attr(feature = "inspect", inspect(skip))]
98 logger: Option<Arc<dyn VmgsLogger>>,
99}
100
101#[cfg(feature = "inspect")]
102mod vmgs_inspect {
103 use super::*;
104
105 #[derive(Default)]
106 pub struct IoStat {
107 pub attempt: Counter,
108 pub resolved: Counter,
109 }
110
111 impl Inspect for IoStat {
114 fn inspect(&self, req: inspect::Request<'_>) {
115 let mut resp = req.respond();
116 resp.counter("ok", self.resolved.get())
117 .counter("err", self.attempt.get() - self.resolved.get());
118 }
119 }
120
121 #[derive(Inspect, Default)]
122 pub struct VmgsStats {
123 #[inspect(with = "stat_map")]
124 pub read: HashMap<FileId, IoStat>,
125 #[inspect(with = "stat_map")]
126 pub write: HashMap<FileId, IoStat>,
127 }
128
129 pub(super) fn fcbs(fcbs: &HashMap<FileId, ResolvedFileControlBlock>) -> impl Inspect + '_ {
130 inspect::adhoc(|req| {
131 let mut res = req.respond();
132 for (id, fcb) in fcbs.iter() {
133 res.field(&format!("{}-{:?}", id.0, id), fcb);
134 }
135 })
136 }
137
138 pub fn stat_map(map: &HashMap<FileId, IoStat>) -> impl Inspect + '_ {
139 inspect::iter_by_key(map).map_key(|x| format!("{:?}", x))
140 }
141}
142
143impl Vmgs {
144 pub async fn try_open(
147 disk: Disk,
148 logger: Option<Arc<dyn VmgsLogger>>,
149 format_on_empty: bool,
150 format_on_failure: bool,
151 ) -> Result<Self, Error> {
152 match Vmgs::open(disk.clone(), logger.clone()).await {
153 Ok(vmgs) => Ok(vmgs),
154 Err(Error::EmptyFile) if format_on_empty => {
155 tracing::info!(CVM_ALLOWED, "empty vmgs file, formatting");
156 Vmgs::format_new(disk, logger).await
157 }
158 Err(err) if format_on_failure => {
159 tracing::warn!(CVM_ALLOWED, ?err, "vmgs initialization error, reformatting");
160 Vmgs::format_new(disk, logger).await
161 }
162 Err(err) => {
163 let event_log_id = match err {
164 Error::InvalidFormat(_) => VmgsLogEvent::InvalidFormat,
166 Error::CorruptFormat(_) => VmgsLogEvent::CorruptFormat,
168 _ => VmgsLogEvent::InitFailed,
170 };
171
172 logger.log_event_fatal(event_log_id).await;
173 Err(err)
174 }
175 }
176 }
177
178 pub async fn format_new(
180 disk: Disk,
181 logger: Option<Arc<dyn VmgsLogger>>,
182 ) -> Result<Self, Error> {
183 let mut storage = VmgsStorage::new(disk);
184 tracing::debug!(CVM_ALLOWED, "formatting and initializing VMGS datastore");
185 Vmgs::validate_file(&storage)?;
187
188 let active_header = Self::format(&mut storage, VMGS_VERSION_3_0).await?;
189
190 Self::finish_open(storage, active_header, 0, logger).await
191 }
192
193 pub async fn open(disk: Disk, logger: Option<Arc<dyn VmgsLogger>>) -> Result<Self, Error> {
195 tracing::debug!(CVM_ALLOWED, "opening VMGS datastore");
196 let mut storage = VmgsStorage::new(disk);
197 Vmgs::validate_file(&storage)?;
199
200 let (header_1, header_2) = read_headers_inner(&mut storage).await?;
201
202 let empty_header = VmgsHeader::new_zeroed();
203
204 if header_1.as_bytes() == empty_header.as_bytes()
205 && header_2.as_bytes() == empty_header.as_bytes()
206 {
207 return Err(Error::EmptyFile);
208 }
209
210 let active_header_index =
211 get_active_header(validate_header(&header_1), validate_header(&header_2))?;
212
213 let active_header = if active_header_index == 0 {
214 header_1
215 } else {
216 header_2
217 };
218
219 Self::finish_open(storage, active_header, active_header_index, logger).await
220 }
221
222 async fn finish_open(
223 mut storage: VmgsStorage,
224 active_header: VmgsHeader,
225 active_header_index: usize,
226 logger: Option<Arc<dyn VmgsLogger>>,
227 ) -> Result<Vmgs, Error> {
228 let version = active_header.version;
229 let (encryption_algorithm, encrypted_metadata_keys, datastore_key_count) =
230 if version >= VMGS_VERSION_3_0 {
231 let encryption_algorithm =
232 if active_header.encryption_algorithm == EncryptionAlgorithm::AES_GCM {
233 EncryptionAlgorithm::AES_GCM
234 } else {
235 EncryptionAlgorithm::NONE
236 };
237 let encrypted_metadata_keys = active_header.metadata_keys;
238
239 let is_key_zero_empty = is_empty_key(&encrypted_metadata_keys[0].encryption_key);
240 let is_key_one_empty = is_empty_key(&encrypted_metadata_keys[1].encryption_key);
241 let datastore_key_count = {
242 if is_key_zero_empty && is_key_one_empty {
243 0
244 } else if !is_key_zero_empty && !is_key_one_empty {
245 encrypted_metadata_keys.len() as u8
246 } else {
247 1
248 }
249 };
250 (
251 encryption_algorithm,
252 active_header.metadata_keys,
253 datastore_key_count,
254 )
255 } else {
256 (
257 EncryptionAlgorithm::NONE,
258 [VmgsEncryptionKey::new_zeroed(); 2],
259 0,
260 )
261 };
262
263 let file_table_size_bytes = block_count_to_byte_count(active_header.file_table_size);
265 let file_table_offset_bytes = block_count_to_byte_count(active_header.file_table_offset);
266
267 let mut file_table_buffer = vec![0; file_table_size_bytes as usize];
268
269 if let Err(e) = storage
270 .read_block(file_table_offset_bytes, file_table_buffer.as_mut_slice())
271 .await
272 {
273 return Err(Error::CorruptFormat(format!(
274 "Error reading file table: {:?}",
275 e
276 )));
277 }
278
279 let file_table = VmgsFileTable::ref_from_prefix(&file_table_buffer)
280 .unwrap()
281 .0; let file_control_blocks =
284 initialize_file_metadata(file_table, version, storage.block_capacity())?;
285
286 Ok(Self {
287 storage,
288
289 active_header_index,
290 active_header_sequence_number: active_header.sequence,
291 version,
292 fcbs: file_control_blocks,
293 encryption_algorithm,
294 datastore_key_count,
295 active_datastore_key_index: None,
296 datastore_keys: [VmgsDatastoreKey::new_zeroed(); 2],
297 metadata_key: VmgsDatastoreKey::new_zeroed(),
298 encrypted_metadata_keys,
299
300 #[cfg(feature = "inspect")]
301 stats: Default::default(),
302
303 logger,
304 })
305 }
306
307 async fn format(storage: &mut VmgsStorage, version: u32) -> Result<VmgsHeader, Error> {
309 tracing::info!(CVM_ALLOWED, "Formatting new VMGS file.");
310 let aligned_header_size = round_up_count(size_of::<VmgsHeader>(), storage.sector_size());
311
312 let mut header = VmgsHeader::new_zeroed();
314
315 storage
316 .write_block(aligned_header_size, header.as_bytes())
317 .await
318 .map_err(Error::WriteDisk)?;
319
320 let mut file_table = VmgsFileTable::new_zeroed();
323 file_table.entries[FileId::FILE_TABLE].offset = VMGS_MIN_FILE_BLOCK_OFFSET;
324 file_table.entries[FileId::FILE_TABLE].allocation_size = VMGS_FILE_TABLE_BLOCK_SIZE;
325 file_table.entries[FileId::FILE_TABLE].valid_data_size =
326 block_count_to_byte_count(VMGS_FILE_TABLE_BLOCK_SIZE);
327 if version >= VMGS_VERSION_3_0 {
328 file_table.entries[FileId::EXTENDED_FILE_TABLE].offset =
329 VMGS_MIN_FILE_BLOCK_OFFSET + VMGS_FILE_TABLE_BLOCK_SIZE;
330 file_table.entries[FileId::EXTENDED_FILE_TABLE].allocation_size =
331 VMGS_EXTENDED_FILE_TABLE_BLOCK_SIZE;
332 file_table.entries[FileId::EXTENDED_FILE_TABLE].valid_data_size =
333 block_count_to_byte_count(VMGS_EXTENDED_FILE_TABLE_BLOCK_SIZE);
334 }
335
336 storage
337 .write_block(
338 block_count_to_byte_count(VMGS_MIN_FILE_BLOCK_OFFSET),
339 file_table.as_bytes(),
340 )
341 .await
342 .map_err(Error::WriteDisk)?;
343
344 initialize_file_metadata(&file_table, VMGS_VERSION_3_0, storage.block_capacity())?;
345
346 if version >= VMGS_VERSION_3_0 {
348 let extended_file_table = VmgsExtendedFileTable::new_zeroed();
349 storage
350 .write_block(
351 block_count_to_byte_count(
352 VMGS_MIN_FILE_BLOCK_OFFSET + VMGS_FILE_TABLE_BLOCK_SIZE,
353 ),
354 extended_file_table.as_bytes(),
355 )
356 .await
357 .map_err(Error::WriteDisk)?;
358 }
359
360 header.signature = VMGS_SIGNATURE;
362 header.version = VMGS_VERSION_3_0;
363 header.sequence = 1;
364 header.header_size = size_of::<VmgsHeader>() as u32;
365 header.file_table_offset = VMGS_MIN_FILE_BLOCK_OFFSET;
366 header.file_table_size = VMGS_FILE_TABLE_BLOCK_SIZE;
367 header.checksum = compute_crc32(header.as_bytes());
368 header.encryption_algorithm = EncryptionAlgorithm::NONE;
369
370 storage
371 .write_block(0, header.as_bytes())
372 .await
373 .map_err(Error::WriteDisk)?;
374
375 storage.flush().await.map_err(Error::FlushDisk)?;
377
378 Ok(header)
379 }
380
381 fn validate_file(storage: &VmgsStorage) -> Result<(), Error> {
382 let sector_count = storage.sector_count();
383 let sector_size = storage.sector_size();
384
385 if sector_count == 0 || sector_count > u64::MAX / 4096 {
390 return Err(Error::Initialization(format!(
391 "Invalid sector count of {}",
392 sector_count,
393 )));
394 }
395
396 if sector_size != 512 && sector_size != 4096 {
399 return Err(Error::Initialization(format!(
400 "Invalid sector size {}",
401 sector_size
402 )));
403 }
404
405 Ok(())
406 }
407
408 pub fn get_file_info(&self, file_id: FileId) -> Result<VmgsFileInfo, Error> {
412 let fcb = self.fcbs.get(&file_id).ok_or(Error::FileInfoAllocated)?;
413
414 Ok(VmgsFileInfo {
415 allocated_bytes: block_count_to_byte_count(fcb.allocated_blocks.get()),
416 valid_bytes: fcb.valid_bytes,
417 encrypted: fcb.attributes.encrypted() || fcb.attributes.authenticated(),
418 })
419 }
420
421 fn allocate_space(
428 &mut self,
429 block_count: u32,
430 temp_fcbs: &mut Vec<ResolvedFileControlBlock>,
431 valid_bytes: u64,
432 ) -> Result<(), Error> {
433 struct AllocationBlock {
434 block_offset: u32,
435 allocated_blocks: u32,
436 }
437
438 if block_count == 0 {
439 return Err(Error::AllocateZero);
440 }
441 let mut allocation_list = Vec::new();
443 for (_, fcb) in self.fcbs.iter() {
444 allocation_list.push(AllocationBlock {
445 block_offset: fcb.block_offset,
446 allocated_blocks: fcb.allocated_blocks.get(),
447 });
448 }
449
450 for temp_fcb in temp_fcbs.iter() {
451 allocation_list.push(AllocationBlock {
452 block_offset: temp_fcb.block_offset,
453 allocated_blocks: temp_fcb.allocated_blocks.get(),
454 });
455 }
456 allocation_list.sort_by_key(|a| a.block_offset);
459
460 let mut best_offset = 0;
461 let mut best_free_count = 0;
462 let mut last_allocation_end_offset = VMGS_MIN_FILE_BLOCK_OFFSET;
463 let mut found = false;
464
465 for fcb in allocation_list.iter() {
467 if fcb.block_offset < last_allocation_end_offset {
468 return Err(Error::AllocateOffset);
469 }
470 let free_count = fcb.block_offset - last_allocation_end_offset;
471 if free_count >= block_count && (best_free_count == 0 || free_count < best_free_count) {
472 best_free_count = free_count;
473 best_offset = last_allocation_end_offset;
474 found = true;
475 }
476 last_allocation_end_offset = fcb.block_offset + fcb.allocated_blocks;
477 }
478 if last_allocation_end_offset < self.storage.block_capacity() {
479 let free_count = self.storage.block_capacity() - last_allocation_end_offset;
480 if free_count >= block_count && (best_free_count == 0 || free_count < best_free_count) {
481 best_offset = last_allocation_end_offset;
482 found = true;
483 }
484 }
485 if !found {
486 return Err(Error::InsufficientResources);
487 }
488 let new_fcb = ResolvedFileControlBlock {
489 block_offset: best_offset,
490 allocated_blocks: NonZeroU32::new(block_count).unwrap(),
491 valid_bytes,
492 attributes: FileAttribute::new(),
493 nonce: VmgsNonce::new_zeroed(),
494 authentication_tag: VmgsAuthTag::new_zeroed(),
495 encryption_key: VmgsDatastoreKey::new_zeroed(),
496 };
497 temp_fcbs.push(new_fcb);
498
499 Ok(())
500 }
501
502 async fn write_file_internal(
504 &mut self,
505 file_id: FileId,
506 buf: &[u8],
507 file_table_fcb: &mut ResolvedFileControlBlock,
508 data_fcb: &mut ResolvedFileControlBlock,
509 should_encrypt: bool,
510 should_write_file_table: bool,
511 ) -> Result<(), Error> {
512 let data_nonce_auth_tag = if should_encrypt {
513 let data_encryption_key = {
514 let mut encryption_key = VmgsDatastoreKey::new_zeroed();
515 getrandom::fill(&mut encryption_key).expect("rng failure");
516 encryption_key
517 };
518 let data_nonce = generate_nonce();
519 let mut data_auth_tag = VmgsAuthTag::new_zeroed();
520
521 if let Err(e) = self
522 .write_encrypted_data(
523 data_fcb.block_offset,
524 &data_encryption_key,
525 &data_nonce,
526 buf,
527 &mut data_auth_tag,
528 )
529 .await
530 {
531 self.logger
532 .log_event_fatal(VmgsLogEvent::AccessFailed)
533 .await;
534
535 return Err(e);
536 }
537
538 data_fcb.nonce.copy_from_slice(&data_nonce);
540 data_fcb
541 .encryption_key
542 .copy_from_slice(&data_encryption_key);
543 data_fcb.authentication_tag.copy_from_slice(&data_auth_tag);
544 Some((data_nonce, data_auth_tag))
545 } else {
546 if let Err(e) = self
548 .storage
549 .write_block(block_count_to_byte_count(data_fcb.block_offset), buf)
550 .await
551 {
552 self.logger
553 .log_event_fatal(VmgsLogEvent::AccessFailed)
554 .await;
555
556 return Err(Error::WriteDisk(e));
557 }
558 None
559 };
560
561 let mut new_file_table = VmgsFileTable::new_zeroed();
563 for (file_id, fcb) in self.fcbs.iter() {
564 let new_file_entry = &mut new_file_table.entries[*file_id];
565
566 new_file_entry.offset = fcb.block_offset;
567 new_file_entry.allocation_size = fcb.allocated_blocks.get();
568 new_file_entry.valid_data_size = fcb.valid_bytes;
569
570 if self.version >= VMGS_VERSION_3_0 {
571 new_file_entry.nonce.copy_from_slice(&fcb.nonce);
572 new_file_entry
573 .authentication_tag
574 .copy_from_slice(&fcb.authentication_tag);
575 }
576 }
577
578 let file_entry = &mut new_file_table.entries[file_id];
580 *file_entry = vmgs_format::VmgsFileEntry {
581 offset: data_fcb.block_offset,
582 allocation_size: data_fcb.allocated_blocks.get(),
583 valid_data_size: buf.len() as u64,
584 ..vmgs_format::VmgsFileEntry::new_zeroed()
585 };
586
587 if let Some((data_nonce, data_auth_tag)) = data_nonce_auth_tag {
588 file_entry.nonce.copy_from_slice(&data_nonce);
590 file_entry
591 .authentication_tag
592 .copy_from_slice(&data_auth_tag);
593 }
594
595 let file_table_entry = &mut new_file_table.entries[FileId::FILE_TABLE];
597 *file_table_entry = vmgs_format::VmgsFileEntry {
598 offset: file_table_fcb.block_offset,
599 allocation_size: file_table_fcb.allocated_blocks.get(),
600 valid_data_size: file_table_fcb.valid_bytes,
601 ..vmgs_format::VmgsFileEntry::new_zeroed()
602 };
603
604 if should_write_file_table {
605 if let Err(e) = self
607 .storage
608 .write_block(
609 block_count_to_byte_count(file_table_fcb.block_offset),
610 new_file_table.as_bytes(),
611 )
612 .await
613 {
614 self.logger
615 .log_event_fatal(VmgsLogEvent::AccessFailed)
616 .await;
617
618 return Err(Error::WriteDisk(e));
619 }
620 }
621
622 self.fcbs.insert(FileId::FILE_TABLE, *file_table_fcb);
626 self.fcbs.insert(file_id, *data_fcb);
627
628 Ok(())
629 }
630
631 fn fill_extended_file_table(
633 &mut self,
634 new_extended_file_table: &mut VmgsExtendedFileTable,
635 ) -> Result<(), Error> {
636 *new_extended_file_table = VmgsExtendedFileTable::new_zeroed();
637 for (file_id, fcb) in self.fcbs.iter_mut() {
638 let extended_file_entry = &mut new_extended_file_table.entries[*file_id];
639 extended_file_entry.attributes = fcb.attributes;
640 extended_file_entry
641 .encryption_key
642 .copy_from_slice(&fcb.encryption_key);
643 }
644
645 Ok(())
646 }
647
648 async fn update_header(&mut self, new_header: &mut VmgsHeader) -> Result<(), Error> {
650 new_header.sequence = self.active_header_sequence_number.wrapping_add(1);
652 new_header.checksum = 0;
653 new_header.checksum = compute_crc32(new_header.as_bytes());
654
655 let new_header_index = if self.active_header_index == 0 { 1 } else { 0 };
656
657 self.storage
658 .write_block(
659 new_header_index as u64 * self.storage.aligned_header_size(),
660 new_header.as_bytes(),
661 )
662 .await
663 .map_err(Error::WriteDisk)?;
664 self.set_active_header(new_header_index, new_header.sequence);
665 Ok(())
666 }
667
668 fn set_active_header(
670 &mut self,
671 active_header_index: usize,
672 active_header_sequence_number: u32,
673 ) {
674 assert!(active_header_index < 2);
675 self.active_header_index = active_header_index;
676 self.active_header_sequence_number = active_header_sequence_number;
677 }
678
679 pub async fn read_file(&mut self, file_id: FileId) -> Result<Vec<u8>, Error> {
681 self.read_file_inner(file_id, true).await
682 }
683
684 pub async fn read_file_raw(&mut self, file_id: FileId) -> Result<Vec<u8>, Error> {
686 self.read_file_inner(file_id, false).await
687 }
688
689 async fn read_file_inner(&mut self, file_id: FileId, decrypt: bool) -> Result<Vec<u8>, Error> {
690 #[cfg(feature = "inspect")]
691 self.stats
692 .read
693 .entry(file_id)
694 .or_default()
695 .attempt
696 .increment();
697
698 let file_info = self.get_file_info(file_id)?;
699 if file_id == FileId::FILE_TABLE {
700 return Err(Error::FileId);
701 }
702
703 let fcb = self.fcbs[&file_id];
704
705 let mut buf = vec![0; file_info.valid_bytes as usize];
706
707 let file_is_encrypted = fcb.attributes.encrypted() || fcb.attributes.authenticated();
708
709 if decrypt
710 && self.version >= VMGS_VERSION_3_0
711 && self.encryption_algorithm != EncryptionAlgorithm::NONE
712 && file_is_encrypted
713 && self.active_datastore_key_index.is_some()
714 {
715 if let Err(e) = self
716 .read_decrypted_data(
717 fcb.block_offset,
718 &fcb.encryption_key,
719 &fcb.nonce,
720 &fcb.authentication_tag,
721 &mut buf,
722 )
723 .await
724 {
725 self.logger
726 .log_event_fatal(VmgsLogEvent::AccessFailed)
727 .await;
728
729 return Err(e);
730 }
731 } else if file_is_encrypted && decrypt {
732 return Err(Error::ReadEncrypted);
733 } else {
734 let byte_offset = block_count_to_byte_count(fcb.block_offset);
735 if let Err(e) = self.storage.read_block(byte_offset, &mut buf).await {
736 self.logger
737 .log_event_fatal(VmgsLogEvent::AccessFailed)
738 .await;
739
740 return Err(Error::ReadDisk(e));
741 }
742 }
743
744 #[cfg(feature = "inspect")]
745 self.stats
746 .read
747 .entry(file_id)
748 .or_default()
749 .resolved
750 .increment();
751
752 Ok(buf)
753 }
754
755 pub async fn write_file(&mut self, file_id: FileId, buf: &[u8]) -> Result<(), Error> {
763 self.write_file_inner(file_id, buf, false).await
764 }
765
766 pub async fn write_file_allow_overwrite_encrypted(
769 &mut self,
770 file_id: FileId,
771 buf: &[u8],
772 ) -> Result<(), Error> {
773 self.write_file_inner(file_id, buf, true).await
774 }
775
776 async fn write_file_inner(
777 &mut self,
778 file_id: FileId,
779 buf: &[u8],
780 overwrite_encrypted: bool,
781 ) -> Result<(), Error> {
782 #[cfg(feature = "inspect")]
783 self.stats
784 .write
785 .entry(file_id)
786 .or_default()
787 .attempt
788 .increment();
789
790 if file_id == FileId::FILE_TABLE {
791 return Err(Error::FileId);
792 }
793 if buf.len() > vmgs_format::VMGS_MAX_FILE_SIZE_BYTES as usize {
794 return Err(Error::WriteFileLength);
795 }
796 let mut blocks_to_allocate =
797 (round_up_count(buf.len(), VMGS_BYTES_PER_BLOCK) / VMGS_BYTES_PER_BLOCK as u64) as u32;
798 if blocks_to_allocate == 0 {
800 blocks_to_allocate = 1;
801 }
802 if blocks_to_allocate as u64 > vmgs_format::VMGS_MAX_FILE_SIZE_BLOCKS {
803 return Err(Error::WriteFileBlocks);
804 }
805 if self
806 .fcbs
807 .get(&file_id)
808 .map(|fcb| fcb.attributes.encrypted())
809 .unwrap_or(false)
810 {
811 if overwrite_encrypted {
812 tracing::warn!(
813 CVM_ALLOWED,
814 "overwriting encrypted file with plaintext data!"
815 )
816 } else {
817 return Err(Error::OverwriteEncrypted);
818 }
819 }
820
821 let mut temp_fcbs: Vec<ResolvedFileControlBlock> = Vec::new();
824 self.allocate_space(
826 VMGS_FILE_TABLE_BLOCK_SIZE,
827 &mut temp_fcbs,
828 block_count_to_byte_count(VMGS_FILE_TABLE_BLOCK_SIZE),
829 )?;
830 self.allocate_space(blocks_to_allocate, &mut temp_fcbs, buf.len() as u64)?;
832
833 let extended_file_table_fcb = if self.encryption_algorithm == EncryptionAlgorithm::NONE
835 || self
836 .fcbs
837 .get(&file_id)
838 .map(|f| f.attributes == FileAttribute::new())
839 .unwrap_or(true)
840 {
841 None
842 } else {
843 self.allocate_space(
844 VMGS_EXTENDED_FILE_TABLE_BLOCK_SIZE,
845 &mut temp_fcbs,
846 block_count_to_byte_count(VMGS_EXTENDED_FILE_TABLE_BLOCK_SIZE),
847 )?;
848 temp_fcbs.last_mut().unwrap().attributes = FileAttribute::new()
849 .with_encrypted(true)
850 .with_authenticated(true);
851
852 Some(temp_fcbs.pop().unwrap())
853 };
854
855 let mut data_fcb = temp_fcbs.pop().unwrap();
859 let mut file_table_fcb = temp_fcbs.pop().unwrap();
860
861 data_fcb.attributes = FileAttribute::new();
862
863 self.write_file_internal(
865 file_id,
866 buf,
867 &mut file_table_fcb,
868 &mut data_fcb,
869 false,
870 extended_file_table_fcb.is_none(),
872 )
873 .await?;
874
875 if let Some(mut extended_table_fcb) = extended_file_table_fcb {
876 let mut new_extended_file_table = VmgsExtendedFileTable::new_zeroed();
878 self.fill_extended_file_table(&mut new_extended_file_table)?;
879
880 let extended_file_entry = &mut new_extended_file_table.entries[file_id];
882 extended_file_entry.attributes = data_fcb.attributes;
883 extended_file_entry
884 .encryption_key
885 .copy_from_slice(&data_fcb.encryption_key);
886
887 self.write_file_internal(
889 FileId::EXTENDED_FILE_TABLE,
890 new_extended_file_table.as_bytes(),
891 &mut file_table_fcb,
892 &mut extended_table_fcb,
893 true,
894 true,
895 )
896 .await?;
897 }
898
899 self.storage.flush().await.map_err(Error::FlushDisk)?;
901
902 let mut new_header = self.prepare_new_header(&file_table_fcb);
904
905 if self.encryption_algorithm != EncryptionAlgorithm::NONE {
906 if let Some(extended_table_fcb) = extended_file_table_fcb {
907 let mut metadata_key_auth_tag = VmgsAuthTag::new_zeroed();
908 self.metadata_key
909 .copy_from_slice(&extended_table_fcb.encryption_key);
910
911 let current_index = self.active_datastore_key_index.unwrap();
912
913 increment_nonce(&mut self.encrypted_metadata_keys[current_index].nonce)?;
914
915 let encrypted_metadata_key = encrypt_metadata_key(
916 &self.datastore_keys[current_index],
917 &self.encrypted_metadata_keys[current_index].nonce,
918 &self.metadata_key,
919 &mut metadata_key_auth_tag,
920 )?;
921
922 self.encrypted_metadata_keys[current_index]
923 .authentication_tag
924 .copy_from_slice(&metadata_key_auth_tag);
925 self.encrypted_metadata_keys[current_index]
926 .encryption_key
927 .copy_from_slice(&encrypted_metadata_key);
928 }
929
930 new_header.encryption_algorithm = self.encryption_algorithm;
931 new_header
932 .metadata_keys
933 .copy_from_slice(&self.encrypted_metadata_keys);
934 }
935
936 self.update_header(&mut new_header).await?;
937
938 #[cfg(feature = "inspect")]
939 self.stats
940 .write
941 .entry(file_id)
942 .or_default()
943 .resolved
944 .increment();
945
946 Ok(())
947 }
948
949 #[cfg(with_encryption)]
952 pub async fn write_file_encrypted(&mut self, file_id: FileId, buf: &[u8]) -> Result<(), Error> {
953 if file_id == FileId::FILE_TABLE {
954 return Err(Error::FileId);
955 }
956 if buf.len() > vmgs_format::VMGS_MAX_FILE_SIZE_BYTES as usize {
957 return Err(Error::WriteFileLength);
958 }
959 let mut blocks_to_allocate =
960 (round_up_count(buf.len(), VMGS_BYTES_PER_BLOCK) / VMGS_BYTES_PER_BLOCK as u64) as u32;
961 if blocks_to_allocate == 0 {
963 blocks_to_allocate = 1;
964 }
965 if blocks_to_allocate as u64 > vmgs_format::VMGS_MAX_FILE_SIZE_BLOCKS {
966 return Err(Error::WriteFileBlocks);
967 }
968 if self.encryption_algorithm == EncryptionAlgorithm::NONE {
969 tracing::trace!(
970 CVM_ALLOWED,
971 "VMGS file not encrypted, performing plaintext write"
972 );
973 return self.write_file(file_id, buf).await;
974 }
975
976 let mut temp_fcbs: Vec<ResolvedFileControlBlock> = Vec::new();
979 self.allocate_space(
981 VMGS_FILE_TABLE_BLOCK_SIZE,
982 &mut temp_fcbs,
983 block_count_to_byte_count(VMGS_FILE_TABLE_BLOCK_SIZE),
984 )?;
985 self.allocate_space(blocks_to_allocate, &mut temp_fcbs, buf.len() as u64)?;
987
988 let mut extended_file_table_fcb = {
989 self.allocate_space(
990 VMGS_EXTENDED_FILE_TABLE_BLOCK_SIZE,
991 &mut temp_fcbs,
992 block_count_to_byte_count(VMGS_EXTENDED_FILE_TABLE_BLOCK_SIZE),
993 )?;
994 temp_fcbs.last_mut().unwrap().attributes = FileAttribute::new()
995 .with_encrypted(true)
996 .with_authenticated(true);
997 temp_fcbs.pop().unwrap()
998 };
999
1000 let mut data_fcb = temp_fcbs.pop().unwrap();
1004 let mut file_table_fcb = temp_fcbs.pop().unwrap();
1005
1006 data_fcb.attributes = FileAttribute::new()
1007 .with_encrypted(true)
1008 .with_authenticated(true);
1009
1010 self.write_file_internal(
1012 file_id,
1013 buf,
1014 &mut file_table_fcb,
1015 &mut data_fcb,
1016 true,
1017 false,
1018 )
1019 .await?;
1020
1021 let mut new_extended_file_table = VmgsExtendedFileTable::new_zeroed();
1023 self.fill_extended_file_table(&mut new_extended_file_table)?;
1024
1025 let extended_file_entry = &mut new_extended_file_table.entries[file_id];
1027 extended_file_entry.attributes = data_fcb.attributes;
1028 extended_file_entry
1029 .encryption_key
1030 .copy_from_slice(&data_fcb.encryption_key);
1031
1032 self.write_file_internal(
1034 FileId::EXTENDED_FILE_TABLE,
1035 new_extended_file_table.as_bytes(),
1036 &mut file_table_fcb,
1037 &mut extended_file_table_fcb,
1038 true,
1039 true,
1040 )
1041 .await?;
1042
1043 self.storage.flush().await.map_err(Error::FlushDisk)?;
1045
1046 let mut new_header = self.prepare_new_header(&file_table_fcb);
1048
1049 if self.encryption_algorithm != EncryptionAlgorithm::NONE {
1050 let mut metadata_key_auth_tag = VmgsAuthTag::new_zeroed();
1051 self.metadata_key
1052 .copy_from_slice(&extended_file_table_fcb.encryption_key);
1053
1054 let active_key = self.active_datastore_key_index.unwrap();
1055 increment_nonce(&mut self.encrypted_metadata_keys[active_key].nonce)?;
1056
1057 let encrypted_metadata_key = encrypt_metadata_key(
1058 &self.datastore_keys[active_key],
1059 &self.encrypted_metadata_keys[active_key].nonce,
1060 &self.metadata_key,
1061 &mut metadata_key_auth_tag,
1062 )?;
1063
1064 self.encrypted_metadata_keys[active_key]
1065 .authentication_tag
1066 .copy_from_slice(&metadata_key_auth_tag);
1067 self.encrypted_metadata_keys[active_key]
1068 .encryption_key
1069 .copy_from_slice(&encrypted_metadata_key);
1070
1071 new_header.encryption_algorithm = self.encryption_algorithm;
1072 new_header
1073 .metadata_keys
1074 .copy_from_slice(&self.encrypted_metadata_keys);
1075 }
1076
1077 self.update_header(&mut new_header).await
1078 }
1079
1080 #[cfg(with_encryption)]
1083 pub async fn unlock_with_encryption_key(&mut self, encryption_key: &[u8]) -> Result<(), Error> {
1084 if self.version < VMGS_VERSION_3_0 {
1085 return Err(Error::Other(anyhow!(
1086 "unlock_with_encryption_key() not supported with VMGS version"
1087 )));
1088 }
1089 if self.encryption_algorithm == EncryptionAlgorithm::NONE {
1090 return Err(Error::Other(anyhow!(
1091 "unlock_with_encryption_key() not supported with None EncryptionAlgorithm"
1092 )));
1093 }
1094
1095 let mut valid_index = None;
1098 let mut errs = [None, None];
1099
1100 for (i, key) in self.encrypted_metadata_keys.iter().enumerate() {
1101 let result = decrypt_metadata_key(
1102 encryption_key,
1103 &key.nonce,
1104 &key.encryption_key,
1105 &key.authentication_tag,
1106 );
1107
1108 match result {
1109 Ok(metadata_key) => {
1110 self.metadata_key.copy_from_slice(&metadata_key);
1111 valid_index = Some(i);
1112 break;
1113 }
1114 Err(err) => {
1115 errs[i] = Some(err);
1116 }
1117 }
1118 }
1119
1120 let valid_index = match valid_index {
1121 Some(idx) => idx,
1122 None => {
1123 tracing::error!(
1124 CVM_ALLOWED,
1125 error = &errs[0].take().unwrap() as &dyn std::error::Error,
1126 "first index failed to decrypt",
1127 );
1128 tracing::error!(
1129 CVM_ALLOWED,
1130 error = &errs[1].take().unwrap() as &dyn std::error::Error,
1131 "second index failed to decrypt",
1132 );
1133 return Err(Error::Other(anyhow::anyhow!(
1134 "failed to use the root key provided to decrypt VMGS metadata key"
1135 )));
1136 }
1137 };
1138 let extended_file_header = self.fcbs[&FileId::EXTENDED_FILE_TABLE];
1139 let extended_file_table_size_bytes =
1140 block_count_to_byte_count(extended_file_header.allocated_blocks.get());
1141 let mut extended_file_table_buffer = vec![0; extended_file_table_size_bytes as usize];
1142 let self_metadata_key = self.metadata_key;
1143
1144 self.read_decrypted_data(
1146 extended_file_header.block_offset,
1147 &self_metadata_key,
1148 &extended_file_header.nonce,
1149 &extended_file_header.authentication_tag,
1150 &mut extended_file_table_buffer,
1151 )
1152 .await
1153 .context("failed to decrypt extended file table")?;
1154
1155 let extended_file_table =
1157 VmgsExtendedFileTable::read_from_prefix(extended_file_table_buffer.as_bytes())
1158 .map_err(|_| anyhow!("Invalid decrypted extended file table"))? .0;
1160 for (file_id, fcb) in self.fcbs.iter_mut() {
1161 fcb.attributes = extended_file_table.entries[*file_id].attributes;
1162 fcb.encryption_key = extended_file_table.entries[*file_id].encryption_key;
1163 }
1164
1165 self.datastore_keys[valid_index].copy_from_slice(encryption_key);
1166 self.active_datastore_key_index = Some(valid_index);
1167
1168 Ok(())
1169 }
1170
1171 #[cfg_attr(not(with_encryption), expect(unused_variables))]
1173 async fn write_encrypted_data(
1174 &mut self,
1175 block_offset: u32,
1176 encryption_key: &[u8],
1177 nonce: &[u8],
1178 plaintext_data: &[u8],
1179 authentication_tag: &mut [u8],
1180 ) -> Result<(), Error> {
1181 #[cfg(not(with_encryption))]
1182 unreachable!("Encryption requires the encryption feature");
1183 #[cfg(with_encryption)]
1184 {
1185 let encrypted_text = crate::encrypt::vmgs_encrypt(
1186 encryption_key,
1187 nonce,
1188 plaintext_data,
1189 authentication_tag,
1190 )?;
1191
1192 self.storage
1194 .write_block(block_count_to_byte_count(block_offset), &encrypted_text)
1195 .await
1196 .map_err(Error::WriteDisk)?;
1197
1198 Ok(())
1199 }
1200 }
1201
1202 #[cfg_attr(not(with_encryption), expect(unused_variables))]
1204 async fn read_decrypted_data(
1205 &mut self,
1206 block_offset: u32,
1207 decryption_key: &[u8],
1208 nonce: &[u8],
1209 authentication_tag: &[u8],
1210 plaintext_data: &mut [u8],
1211 ) -> Result<(), Error> {
1212 #[cfg(not(with_encryption))]
1213 unreachable!("Encryption requires the encryption feature");
1214 #[cfg(with_encryption)]
1215 {
1216 let mut buf = vec![0; plaintext_data.len()];
1218
1219 self.storage
1220 .read_block(block_count_to_byte_count(block_offset), &mut buf)
1221 .await
1222 .map_err(Error::ReadDisk)?;
1223
1224 if buf.iter().all(|x| *x == 0) {
1228 return Err(Error::InvalidFormat("encrypted data is all-zeros".into()));
1229 }
1230
1231 let decrypted_text =
1232 crate::encrypt::vmgs_decrypt(decryption_key, nonce, &buf, authentication_tag)?;
1233 if decrypted_text.len() != plaintext_data.len() {
1234 return Err(Error::Other(anyhow!(
1235 "Decrypt error, slice sizes should match."
1236 )));
1237 }
1238 plaintext_data.copy_from_slice(&decrypted_text);
1239
1240 Ok(())
1241 }
1242 }
1243
1244 #[cfg(with_encryption)]
1248 pub async fn update_encryption_key(
1249 &mut self,
1250 encryption_key: &[u8],
1251 encryption_algorithm: EncryptionAlgorithm,
1252 ) -> Result<(), Error> {
1253 let old_index = self.active_datastore_key_index;
1254
1255 match self
1256 .add_new_encryption_key(encryption_key, encryption_algorithm)
1257 .await
1258 {
1259 Ok(_) => {}
1260 Err(Error::DatastoreKeysFull) => {
1261 if let Some(old_index) = old_index {
1262 let inactive_index = if old_index == 0 { 1 } else { 0 };
1263 tracing::warn!(CVM_ALLOWED, inactive_index, "removing inactive key");
1264 self.remove_encryption_key(inactive_index).await?;
1265 tracing::trace!(CVM_ALLOWED, "attempting to add the key again");
1266 self.add_new_encryption_key(encryption_key, encryption_algorithm)
1267 .await?;
1268 } else {
1269 return Err(Error::NoActiveDatastoreKey);
1270 }
1271 }
1272 Err(e) => return Err(e),
1273 };
1274
1275 if let Some(old_index) = old_index {
1276 self.remove_encryption_key(old_index).await?;
1277 }
1278
1279 Ok(())
1280 }
1281
1282 #[cfg(with_encryption)]
1284 async fn add_new_encryption_key(
1285 &mut self,
1286 encryption_key: &[u8],
1287 encryption_algorithm: EncryptionAlgorithm,
1288 ) -> Result<(), Error> {
1289 if self.version < VMGS_VERSION_3_0 {
1290 return Err(Error::Other(anyhow!(
1291 "add_new_encryption_key() not supported with VMGS version"
1292 )));
1293 }
1294 if self.encryption_algorithm != EncryptionAlgorithm::NONE
1295 && self.active_datastore_key_index.is_none()
1296 {
1297 return Err(Error::Other(anyhow!(
1298 "add_new_encryption_key() invalid datastore key index"
1299 )));
1300 }
1301 if self.datastore_key_count == self.datastore_keys.len() as u8 {
1302 return Err(Error::DatastoreKeysFull);
1303 }
1304 if is_empty_key(encryption_key) {
1305 return Err(Error::Other(anyhow!("Trying to add empty encryption key")));
1306 }
1307 if encryption_algorithm == EncryptionAlgorithm::NONE {
1308 return Err(Error::Other(anyhow!(
1309 "Encryption not supported for VMGS file"
1310 )));
1311 }
1312 if self.encryption_algorithm != EncryptionAlgorithm::NONE
1313 && encryption_algorithm != self.encryption_algorithm
1314 {
1315 return Err(Error::Other(anyhow!(
1316 "Encryption algorithm provided to add_new_encryption_key does not match VMGS's encryption algorithm."
1317 )));
1318 }
1319
1320 let mut new_key_index = 0;
1321 let mut new_metadata_key = self.metadata_key;
1322 if self.datastore_key_count == 0 {
1323 let mut temp_fcbs: Vec<ResolvedFileControlBlock> = Vec::new();
1328 self.allocate_space(
1329 VMGS_FILE_TABLE_BLOCK_SIZE,
1330 &mut temp_fcbs,
1331 block_count_to_byte_count(VMGS_FILE_TABLE_BLOCK_SIZE),
1332 )?;
1333
1334 self.allocate_space(
1335 VMGS_EXTENDED_FILE_TABLE_BLOCK_SIZE,
1336 &mut temp_fcbs,
1337 block_count_to_byte_count(VMGS_EXTENDED_FILE_TABLE_BLOCK_SIZE),
1338 )?;
1339
1340 let mut extended_file_table_fcb = temp_fcbs.pop().unwrap();
1341 let mut file_table_fcb = temp_fcbs.pop().unwrap();
1342
1343 extended_file_table_fcb.attributes = FileAttribute::new()
1344 .with_encrypted(true)
1345 .with_authenticated(true);
1346
1347 let new_extended_file_table = VmgsExtendedFileTable::new_zeroed();
1349 self.write_file_internal(
1351 FileId::EXTENDED_FILE_TABLE,
1352 new_extended_file_table.as_bytes(),
1353 &mut file_table_fcb,
1354 &mut extended_file_table_fcb,
1355 true,
1356 true,
1357 )
1358 .await?;
1359
1360 new_metadata_key
1361 .copy_from_slice(&self.fcbs[&FileId::EXTENDED_FILE_TABLE].encryption_key);
1362 } else if self.active_datastore_key_index == Some(0) {
1363 new_key_index = 1;
1364 }
1365
1366 let mut new_header = self.prepare_new_header(&self.fcbs[&FileId::FILE_TABLE]);
1368 new_header.encryption_algorithm = EncryptionAlgorithm::AES_GCM;
1369
1370 let metadata_key_nonce = generate_nonce();
1372 let mut metadata_key_auth_tag = VmgsAuthTag::new_zeroed();
1373 let encrypted_metadata_key = encrypt_metadata_key(
1374 encryption_key,
1375 &metadata_key_nonce,
1376 &new_metadata_key,
1377 &mut metadata_key_auth_tag,
1378 )?;
1379
1380 self.encrypted_metadata_keys[new_key_index]
1381 .nonce
1382 .copy_from_slice(&metadata_key_nonce);
1383 self.encrypted_metadata_keys[new_key_index]
1384 .authentication_tag
1385 .copy_from_slice(&metadata_key_auth_tag);
1386 self.encrypted_metadata_keys[new_key_index]
1387 .encryption_key
1388 .copy_from_slice(&encrypted_metadata_key);
1389
1390 new_header
1391 .metadata_keys
1392 .copy_from_slice(&self.encrypted_metadata_keys);
1393
1394 self.update_header(&mut new_header).await?;
1396
1397 self.datastore_keys[new_key_index].copy_from_slice(encryption_key);
1399 self.metadata_key.copy_from_slice(&new_metadata_key);
1400 self.datastore_key_count += 1;
1401 self.encryption_algorithm = encryption_algorithm;
1402 self.active_datastore_key_index = Some(new_key_index);
1403
1404 Ok(())
1405 }
1406
1407 #[cfg(with_encryption)]
1409 async fn remove_encryption_key(&mut self, key_index: usize) -> Result<(), Error> {
1410 if self.version < VMGS_VERSION_3_0 {
1411 return Err(Error::Other(anyhow!(
1412 "remove_encryption_key() not supported with VMGS version."
1413 )));
1414 }
1415 if self.encryption_algorithm != EncryptionAlgorithm::NONE
1416 && self.active_datastore_key_index.is_none()
1417 {
1418 return Err(Error::Other(anyhow!(
1419 "remove_encryption_key() invalid datastore key index or encryption algorithm."
1420 )));
1421 }
1422 if self.datastore_key_count != self.datastore_keys.len() as u8
1423 && self.active_datastore_key_index != Some(key_index)
1424 {
1425 return Err(Error::Other(anyhow!(
1426 "remove_encryption_key() invalid key_index"
1427 )));
1428 }
1429
1430 self.datastore_keys[key_index].fill(0);
1432
1433 self.encrypted_metadata_keys[key_index] = VmgsEncryptionKey::new_zeroed();
1435
1436 let mut new_header = self.prepare_new_header(&self.fcbs[&FileId::FILE_TABLE]);
1438 new_header
1439 .metadata_keys
1440 .copy_from_slice(&self.encrypted_metadata_keys);
1441
1442 if self.datastore_key_count == 1 {
1444 new_header.encryption_algorithm = EncryptionAlgorithm::NONE;
1445 } else {
1446 new_header.encryption_algorithm = self.encryption_algorithm;
1447 }
1448
1449 self.update_header(&mut new_header).await?;
1451
1452 if self.datastore_key_count == 1 {
1454 self.encryption_algorithm = EncryptionAlgorithm::NONE;
1455 self.datastore_key_count = 0;
1456 self.active_datastore_key_index = None;
1457 } else {
1458 self.datastore_key_count = 1;
1459
1460 let new_active_datastore_key_index = if key_index == 0 { 1 } else { 0 };
1461 if is_empty_key(&self.datastore_keys[new_active_datastore_key_index]) {
1462 self.active_datastore_key_index = None;
1463 } else {
1464 self.active_datastore_key_index = Some(new_active_datastore_key_index);
1465 }
1466 }
1467
1468 Ok(())
1469 }
1470
1471 pub fn get_encryption_algorithm(&self) -> EncryptionAlgorithm {
1473 self.encryption_algorithm
1474 }
1475
1476 pub fn is_encrypted(&self) -> bool {
1478 self.encryption_algorithm != EncryptionAlgorithm::NONE
1479 }
1480
1481 fn prepare_new_header(&self, file_table_fcb: &ResolvedFileControlBlock) -> VmgsHeader {
1482 VmgsHeader {
1483 signature: VMGS_SIGNATURE,
1484 version: self.version,
1485 header_size: size_of::<VmgsHeader>() as u32,
1486 file_table_offset: file_table_fcb.block_offset,
1487 file_table_size: file_table_fcb.allocated_blocks.get(),
1488 ..VmgsHeader::new_zeroed()
1489 }
1490 }
1491}
1492
1493#[cfg(feature = "test_helpers")]
1496mod test_helpers {
1497 use super::*;
1498
1499 impl Vmgs {
1500 pub fn test_get_active_datastore_key_index(&self) -> Option<usize> {
1502 self.active_datastore_key_index
1503 }
1504
1505 #[cfg(with_encryption)]
1507 pub async fn test_add_new_encryption_key(
1508 &mut self,
1509 encryption_key: &[u8],
1510 encryption_algorithm: EncryptionAlgorithm,
1511 ) -> Result<(), Error> {
1512 self.add_new_encryption_key(encryption_key, encryption_algorithm)
1513 .await
1514 }
1515 }
1516}
1517
1518pub async fn read_headers(disk: Disk) -> Result<(VmgsHeader, VmgsHeader), Error> {
1521 read_headers_inner(&mut VmgsStorage::new(disk)).await
1522}
1523
1524async fn read_headers_inner(storage: &mut VmgsStorage) -> Result<(VmgsHeader, VmgsHeader), Error> {
1525 let mut first_two_blocks = [0; (VMGS_BYTES_PER_BLOCK * 2) as usize];
1528 storage
1529 .read_block(0, &mut first_two_blocks)
1530 .await
1531 .map_err(Error::ReadDisk)?;
1532
1533 let header_1 = VmgsHeader::read_from_prefix(&first_two_blocks).unwrap().0; let header_2 =
1536 VmgsHeader::read_from_prefix(&first_two_blocks[storage.aligned_header_size() as usize..])
1537 .unwrap()
1538 .0; Ok((header_1, header_2))
1540}
1541
1542pub fn get_active_header(
1545 header_1: Result<&VmgsHeader, Error>,
1546 header_2: Result<&VmgsHeader, Error>,
1547) -> Result<usize, Error> {
1548 let active_header_index =
1549 if let (Ok(header_1), Ok(header_2)) = (header_1.as_deref(), header_2.as_deref()) {
1550 if header_1.sequence == header_2.sequence.wrapping_add(1) {
1555 0
1556 } else if header_2.sequence == header_1.sequence.wrapping_add(1) {
1557 1
1558 } else {
1559 return Err(Error::CorruptFormat(format!(
1560 "Invalid header sequence numbers. Header 1: {}, Header 2: {}",
1561 header_1.sequence, header_2.sequence
1562 )));
1563 }
1564 } else if header_1.is_ok() {
1565 0
1566 } else if header_2.is_ok() {
1567 1
1568 } else {
1569 return Err(Error::InvalidFormat(format!(
1570 "No valid header: Header 1: {} Header 2: {}",
1571 header_1.err().unwrap(),
1572 header_2.err().unwrap()
1573 )));
1574 };
1575
1576 Ok(active_header_index)
1577}
1578
1579pub fn validate_header(header: &VmgsHeader) -> Result<&VmgsHeader, Error> {
1581 if header.signature != VMGS_SIGNATURE {
1582 return Err(Error::InvalidFormat(String::from(
1583 "Invalid header signature",
1584 )));
1585 }
1586 if header.version != VMGS_VERSION_3_0 {
1587 return Err(Error::InvalidFormat(String::from("Invalid header version")));
1588 }
1589 if header.header_size != size_of::<VmgsHeader>() as u32 {
1590 return Err(Error::InvalidFormat(String::from("Invalid header size")));
1591 }
1592 if header.file_table_offset < VMGS_MIN_FILE_BLOCK_OFFSET {
1593 return Err(Error::InvalidFormat(String::from(
1594 "Invalid file table offset",
1595 )));
1596 }
1597 if header.file_table_size != VMGS_FILE_TABLE_BLOCK_SIZE {
1598 return Err(Error::InvalidFormat(String::from(
1599 "Invalid file table size",
1600 )));
1601 }
1602
1603 let stored_checksum = header.checksum;
1604 let mut zero_checksum_header = *header;
1605 zero_checksum_header.checksum = 0;
1606 let computed_checksum = compute_crc32(zero_checksum_header.as_bytes());
1607 if stored_checksum != computed_checksum {
1608 return Err(Error::CorruptFormat(String::from(
1609 "Invalid header checksum",
1610 )));
1611 }
1612 Ok(header)
1613}
1614
1615fn initialize_file_metadata(
1617 file_table: &VmgsFileTable,
1618 version: u32,
1619 block_capacity: u32,
1620) -> Result<HashMap<FileId, ResolvedFileControlBlock>, Error> {
1621 let file_entries = file_table.entries;
1622 let mut file_control_blocks = HashMap::new();
1623
1624 for (file_id, file_entry) in file_entries.iter().enumerate() {
1625 let file_id = FileId(file_id as u32);
1626
1627 let Some(allocated_blocks) = NonZeroU32::new(file_entry.allocation_size) else {
1629 continue;
1630 };
1631
1632 if file_entry.offset < VMGS_MIN_FILE_BLOCK_OFFSET || file_entry.offset >= block_capacity {
1634 return Err(Error::CorruptFormat(format!(
1635 "Invalid file offset {} for file_id {:?} \n{:?}",
1636 file_entry.offset, file_id, file_entry
1637 )));
1638 }
1639
1640 let file_allocation_end_block = file_entry.offset + file_entry.allocation_size;
1642 if file_allocation_end_block > block_capacity {
1643 return Err(Error::CorruptFormat(String::from(
1644 "Invalid file allocation end block",
1645 )));
1646 }
1647
1648 let file_allocation_size_bytes = block_count_to_byte_count(file_entry.allocation_size);
1650 if file_entry.valid_data_size > file_allocation_size_bytes {
1651 return Err(Error::CorruptFormat(String::from("Invalid data size")));
1652 }
1653
1654 file_control_blocks.insert(file_id, {
1656 let (nonce, authentication_tag) = if version >= VMGS_VERSION_3_0 {
1657 (file_entry.nonce, file_entry.authentication_tag)
1658 } else {
1659 Default::default()
1660 };
1661
1662 ResolvedFileControlBlock {
1663 block_offset: file_entry.offset,
1664 allocated_blocks,
1665 valid_bytes: file_entry.valid_data_size,
1666
1667 nonce,
1668 authentication_tag,
1669
1670 attributes: FileAttribute::new(),
1671 encryption_key: VmgsDatastoreKey::new_zeroed(),
1672 }
1673 });
1674 }
1675
1676 Ok(file_control_blocks)
1677}
1678
1679fn block_count_to_byte_count(block_count: u32) -> u64 {
1681 block_count as u64 * VMGS_BYTES_PER_BLOCK as u64
1682}
1683
1684fn round_up_count(count: usize, pow2: u32) -> u64 {
1685 (count as u64 + pow2 as u64 - 1) & !(pow2 as u64 - 1)
1686}
1687
1688fn generate_nonce() -> VmgsNonce {
1690 let mut nonce = VmgsNonce::new_zeroed();
1691 getrandom::fill(&mut nonce[..4]).expect("rng failure");
1693 nonce
1694}
1695
1696fn increment_nonce(nonce: &mut VmgsNonce) -> Result<(), Error> {
1698 getrandom::fill(&mut nonce[..vmgs_format::VMGS_NONCE_RANDOM_SEED_SIZE]).expect("rng failure");
1700
1701 for i in &mut nonce[vmgs_format::VMGS_NONCE_RANDOM_SEED_SIZE..] {
1703 *i = i.wrapping_add(1);
1704
1705 if *i != 0 {
1706 break;
1707 }
1708 }
1709
1710 Ok(())
1711}
1712
1713fn is_empty_key(encryption_key: &[u8]) -> bool {
1715 encryption_key.iter().all(|&x| x == 0)
1716}
1717
1718#[cfg_attr(not(with_encryption), expect(unused_variables))]
1720fn encrypt_metadata_key(
1721 encryption_key: &[u8],
1722 nonce: &[u8],
1723 metadata_key: &[u8],
1724 authentication_tag: &mut [u8],
1725) -> Result<Vec<u8>, Error> {
1726 #[cfg(not(with_encryption))]
1727 unreachable!("Encryption requires the encryption feature");
1728 #[cfg(with_encryption)]
1729 {
1730 let encrypted_metadata_key =
1731 crate::encrypt::vmgs_encrypt(encryption_key, nonce, metadata_key, authentication_tag)?;
1732
1733 if encrypted_metadata_key.len() != metadata_key.len() {
1734 return Err(Error::Other(anyhow!(format!(
1735 "encrypted metadata key length ({}) doesn't match metadata key length ({})",
1736 encrypted_metadata_key.len(),
1737 metadata_key.len()
1738 ))));
1739 }
1740 Ok(encrypted_metadata_key)
1741 }
1742}
1743
1744#[cfg_attr(not(with_encryption), expect(unused_variables), expect(dead_code))]
1746fn decrypt_metadata_key(
1747 datastore_key: &[u8],
1748 nonce: &[u8],
1749 metadata_key: &[u8],
1750 authentication_tag: &[u8],
1751) -> Result<Vec<u8>, Error> {
1752 #[cfg(not(with_encryption))]
1753 unreachable!("Encryption requires the encryption feature");
1754 #[cfg(with_encryption)]
1755 {
1756 let decrypted_metadata_key =
1757 crate::encrypt::vmgs_decrypt(datastore_key, nonce, metadata_key, authentication_tag)?;
1758 if decrypted_metadata_key.len() != metadata_key.len() {
1759 return Err(Error::Other(anyhow!(format!(
1760 "decrypted metadata key length ({}) doesn't match metadata key length ({})",
1761 decrypted_metadata_key.len(),
1762 metadata_key.len()
1763 ))));
1764 }
1765
1766 Ok(decrypted_metadata_key)
1767 }
1768}
1769
1770fn compute_crc32(buf: &[u8]) -> u32 {
1772 let mut hasher = crc32fast::Hasher::new();
1773 hasher.update(buf);
1774 hasher.finalize()
1775}
1776
1777#[cfg(feature = "save_restore")]
1778#[expect(missing_docs)]
1779pub mod save_restore {
1780 use super::*;
1781
1782 pub mod state {
1783 use mesh_protobuf::Protobuf;
1784 use std::num::NonZeroU32;
1785
1786 pub type SavedVmgsNonce = [u8; 12];
1787 pub type SavedVmgsAuthTag = [u8; 16];
1788 pub type SavedVmgsDatastoreKey = [u8; 32];
1789
1790 #[derive(Protobuf)]
1791 #[mesh(package = "vmgs")]
1792 pub struct SavedResolvedFileControlBlock {
1793 #[mesh(1)]
1794 pub block_offset: u32,
1795 #[mesh(2)]
1796 pub allocated_blocks: NonZeroU32,
1797 #[mesh(3)]
1798 pub valid_bytes: u64,
1799 #[mesh(4)]
1800 pub nonce: SavedVmgsNonce,
1801 #[mesh(5)]
1802 pub authentication_tag: SavedVmgsAuthTag,
1803 #[mesh(6)]
1804 pub attributes: u32,
1805 #[mesh(7)]
1806 pub encryption_key: SavedVmgsDatastoreKey,
1807 }
1808
1809 #[derive(Protobuf)]
1810 #[mesh(package = "vmgs")]
1811 pub struct SavedVmgsEncryptionKey {
1812 #[mesh(1)]
1813 pub nonce: SavedVmgsNonce,
1814 #[mesh(2)]
1815 pub authentication_tag: SavedVmgsAuthTag,
1816 #[mesh(3)]
1817 pub encryption_key: SavedVmgsDatastoreKey,
1818 }
1819
1820 #[derive(Protobuf)]
1821 #[mesh(package = "vmgs")]
1822 pub struct SavedVmgsState {
1823 #[mesh(1)]
1824 pub active_header_index: usize,
1825 #[mesh(2)]
1826 pub active_header_sequence_number: u32,
1827 #[mesh(3)]
1828 pub version: u32,
1829 #[mesh(4)]
1830 pub fcbs: Vec<(u32, SavedResolvedFileControlBlock)>,
1831 #[mesh(5)]
1832 pub encryption_algorithm: u16,
1833 #[mesh(6)]
1834 pub datastore_key_count: u8,
1835 #[mesh(7)]
1836 pub active_datastore_key_index: Option<usize>,
1837 #[mesh(8)]
1838 pub datastore_keys: [SavedVmgsDatastoreKey; 2],
1839 #[mesh(9)]
1840 pub metadata_key: SavedVmgsDatastoreKey,
1841 #[mesh(10)]
1842 pub encrypted_metadata_keys: [SavedVmgsEncryptionKey; 2],
1843 }
1844 }
1845
1846 impl Vmgs {
1847 pub fn open_from_saved(
1865 disk: Disk,
1866 state: state::SavedVmgsState,
1867 logger: Option<Arc<dyn VmgsLogger>>,
1868 ) -> Self {
1869 let state::SavedVmgsState {
1870 active_header_index,
1871 active_header_sequence_number,
1872 version,
1873 fcbs,
1874 encryption_algorithm,
1875 datastore_key_count,
1876 active_datastore_key_index,
1877 datastore_keys,
1878 metadata_key,
1879 encrypted_metadata_keys,
1880 } = state;
1881
1882 Self {
1883 storage: VmgsStorage::new(disk),
1884 #[cfg(feature = "inspect")]
1885 stats: Default::default(),
1886
1887 active_header_index,
1888 active_header_sequence_number,
1889 version,
1890 fcbs: fcbs
1891 .into_iter()
1892 .map(|(file_id, fcb)| {
1893 let state::SavedResolvedFileControlBlock {
1894 block_offset,
1895 allocated_blocks,
1896 valid_bytes,
1897 nonce,
1898 authentication_tag,
1899 attributes,
1900 encryption_key,
1901 } = fcb;
1902
1903 (
1904 FileId(file_id),
1905 ResolvedFileControlBlock {
1906 block_offset,
1907 allocated_blocks,
1908 valid_bytes,
1909 nonce,
1910 authentication_tag,
1911 attributes: FileAttribute::from(attributes),
1912 encryption_key,
1913 },
1914 )
1915 })
1916 .collect(),
1917 encryption_algorithm: EncryptionAlgorithm(encryption_algorithm),
1918 datastore_key_count,
1919 active_datastore_key_index,
1920 datastore_keys,
1921 metadata_key,
1922 encrypted_metadata_keys: encrypted_metadata_keys.map(|k| {
1923 let state::SavedVmgsEncryptionKey {
1924 nonce,
1925 authentication_tag,
1926 encryption_key,
1927 } = k;
1928
1929 VmgsEncryptionKey {
1930 nonce,
1931 reserved: 0,
1932 authentication_tag,
1933 encryption_key,
1934 }
1935 }),
1936 logger,
1937 }
1938 }
1939
1940 pub fn save(&self) -> state::SavedVmgsState {
1946 let Self {
1947 storage: _,
1948
1949 #[cfg(feature = "inspect")]
1950 stats: _,
1951
1952 active_header_index,
1953 active_header_sequence_number,
1954 version,
1955 fcbs,
1956 encryption_algorithm,
1957 datastore_key_count,
1958 active_datastore_key_index,
1959 datastore_keys,
1960 metadata_key,
1961 encrypted_metadata_keys,
1962 logger: _,
1963 } = self;
1964
1965 state::SavedVmgsState {
1966 active_header_index: *active_header_index,
1967 active_header_sequence_number: *active_header_sequence_number,
1968 version: *version,
1969 fcbs: fcbs
1970 .iter()
1971 .map(|(file_id, fcb)| {
1972 let ResolvedFileControlBlock {
1973 block_offset,
1974 allocated_blocks,
1975 valid_bytes,
1976 nonce,
1977 authentication_tag,
1978 attributes,
1979 encryption_key,
1980 } = fcb;
1981
1982 (
1983 file_id.0,
1984 state::SavedResolvedFileControlBlock {
1985 block_offset: *block_offset,
1986 allocated_blocks: *allocated_blocks,
1987 valid_bytes: *valid_bytes,
1988 nonce: *nonce,
1989 authentication_tag: *authentication_tag,
1990 attributes: (*attributes).into(),
1991 encryption_key: *encryption_key,
1992 },
1993 )
1994 })
1995 .collect(),
1996 encryption_algorithm: encryption_algorithm.0,
1997 datastore_key_count: *datastore_key_count,
1998 active_datastore_key_index: *active_datastore_key_index,
1999 datastore_keys: *datastore_keys,
2000 metadata_key: *metadata_key,
2001 encrypted_metadata_keys: encrypted_metadata_keys.map(|k| {
2002 let VmgsEncryptionKey {
2003 nonce,
2004 reserved: _,
2005 authentication_tag,
2006 encryption_key,
2007 } = k;
2008
2009 state::SavedVmgsEncryptionKey {
2010 nonce,
2011 authentication_tag,
2012 encryption_key,
2013 }
2014 }),
2015 }
2016 }
2017 }
2018}
2019
2020#[cfg(test)]
2021mod tests {
2022 use super::*;
2023 use pal_async::async_test;
2024 #[cfg(with_encryption)]
2025 use parking_lot::Mutex;
2026 #[cfg(with_encryption)]
2027 use std::sync::Arc;
2028 #[cfg(with_encryption)]
2029 use vmgs_format::VMGS_ENCRYPTION_KEY_SIZE;
2030
2031 const ONE_MEGA_BYTE: u64 = 1024 * 1024;
2032
2033 #[cfg(with_encryption)]
2034 struct TestVmgsLogger {
2035 data: Arc<Mutex<String>>,
2036 }
2037
2038 #[cfg(with_encryption)]
2039 #[async_trait::async_trait]
2040 impl VmgsLogger for TestVmgsLogger {
2041 async fn log_event_fatal(&self, _event: VmgsLogEvent) {
2042 let mut data = self.data.lock();
2043 *data = "test logger".to_string();
2044 }
2045 }
2046
2047 fn new_test_file() -> Disk {
2048 disklayer_ram::ram_disk(4 * ONE_MEGA_BYTE, false).unwrap()
2049 }
2050
2051 #[async_test]
2052 async fn empty_vmgs() {
2053 let disk = new_test_file();
2054
2055 let result = Vmgs::open(disk, None).await;
2056 assert!(matches!(result, Err(Error::EmptyFile)));
2057 }
2058
2059 #[async_test]
2060 async fn format_empty_vmgs() {
2061 let disk = new_test_file();
2062 let result = Vmgs::format_new(disk, None).await;
2063 assert!(result.is_ok());
2064 }
2065
2066 #[async_test]
2067 async fn basic_read_write() {
2068 let disk = new_test_file();
2069 let mut vmgs = Vmgs::format_new(disk, None).await.unwrap();
2070 assert_eq!(vmgs.active_header_index, 0);
2071 assert_eq!(vmgs.active_header_sequence_number, 1);
2072 assert_eq!(vmgs.version, VMGS_VERSION_3_0);
2073
2074 let buf = b"hello world";
2076 vmgs.write_file(FileId::BIOS_NVRAM, buf).await.unwrap();
2077
2078 assert_eq!(vmgs.active_header_index, 1);
2079 assert_eq!(vmgs.active_header_sequence_number, 2);
2080
2081 let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2083
2084 assert_eq!(buf, &*read_buf);
2085 assert_eq!(vmgs.active_header_index, 1);
2086 assert_eq!(vmgs.active_header_sequence_number, 2);
2087 }
2088
2089 #[async_test]
2090 async fn basic_read_write_large() {
2091 let disk = new_test_file();
2092 let mut vmgs = Vmgs::format_new(disk, None).await.unwrap();
2093
2094 let buf: Vec<u8> = (0..).map(|x| x as u8).take(1024 * 4 + 1).collect();
2096
2097 vmgs.write_file(FileId::BIOS_NVRAM, &buf).await.unwrap();
2098
2099 assert_eq!(vmgs.active_header_index, 1);
2100 assert_eq!(vmgs.active_header_sequence_number, 2);
2101
2102 let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2104
2105 assert_eq!(buf, read_buf);
2106 assert_eq!(vmgs.active_header_index, 1);
2107 assert_eq!(vmgs.active_header_sequence_number, 2);
2108
2109 let buf: Vec<u8> = (0..).map(|x| x as u8).take(1024 * 4 * 4 + 1).collect();
2111
2112 vmgs.write_file(FileId::TPM_PPI, &buf).await.unwrap();
2113
2114 assert_eq!(vmgs.active_header_index, 0);
2115 assert_eq!(vmgs.active_header_sequence_number, 3);
2116
2117 let read_buf = vmgs.read_file(FileId::TPM_PPI).await.unwrap();
2119
2120 assert_eq!(buf, read_buf);
2121 assert_eq!(vmgs.active_header_index, 0);
2122 assert_eq!(vmgs.active_header_sequence_number, 3);
2123
2124 let buf: Vec<u8> = (0..).map(|x| x as u8).take(1024 * 4 * 4 * 4 + 1).collect();
2126
2127 vmgs.write_file(FileId::GUEST_FIRMWARE, &buf).await.unwrap();
2128
2129 assert_eq!(vmgs.active_header_index, 1);
2130 assert_eq!(vmgs.active_header_sequence_number, 4);
2131
2132 let read_buf = vmgs.read_file(FileId::GUEST_FIRMWARE).await.unwrap();
2134
2135 assert_eq!(buf, read_buf);
2136 assert_eq!(vmgs.active_header_index, 1);
2137 assert_eq!(vmgs.active_header_sequence_number, 4);
2138 }
2139
2140 #[async_test]
2141 async fn open_existing_file() {
2142 let buf_1 = b"hello world";
2143 let buf_2 = b"short sentence";
2144 let buf_3 = b"funny joke";
2145
2146 let disk = new_test_file();
2148 let mut vmgs = Vmgs::format_new(disk.clone(), None).await.unwrap();
2149
2150 vmgs.write_file(FileId::BIOS_NVRAM, buf_1).await.unwrap();
2151
2152 assert_eq!(vmgs.active_header_index, 1);
2153 assert_eq!(vmgs.active_header_sequence_number, 2);
2154 assert_eq!(vmgs.fcbs[&FileId(0)].block_offset, 4);
2155 assert_eq!(vmgs.fcbs[&FileId(1)].block_offset, 5);
2156
2157 vmgs.write_file(FileId::TPM_PPI, buf_2).await.unwrap();
2158
2159 assert_eq!(vmgs.active_header_index, 0);
2160 assert_eq!(vmgs.active_header_sequence_number, 3);
2161 assert_eq!(vmgs.fcbs[&FileId(0)].block_offset, 2);
2162 assert_eq!(vmgs.fcbs[&FileId(1)].block_offset, 5);
2163 assert_eq!(vmgs.fcbs[&FileId(2)].block_offset, 6);
2164
2165 vmgs.write_file(FileId::BIOS_NVRAM, buf_3).await.unwrap();
2166
2167 assert_eq!(vmgs.active_header_index, 1);
2168 assert_eq!(vmgs.active_header_sequence_number, 4);
2169 assert_eq!(vmgs.fcbs[&FileId(0)].block_offset, 4);
2170 assert_eq!(vmgs.fcbs[&FileId(1)].block_offset, 7);
2171 assert_eq!(vmgs.fcbs[&FileId(2)].block_offset, 6);
2172
2173 drop(vmgs);
2175
2176 let mut vmgs = Vmgs::open(disk, None).await.unwrap();
2177
2178 assert_eq!(vmgs.fcbs[&FileId(0)].block_offset, 4);
2179 assert_eq!(vmgs.fcbs[&FileId(1)].block_offset, 7);
2180 assert_eq!(vmgs.fcbs[&FileId(2)].block_offset, 6);
2181 let read_buf_1 = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2182
2183 assert_eq!(buf_3, &*read_buf_1);
2184 assert_eq!(vmgs.active_header_index, 1);
2185 assert_eq!(vmgs.active_header_sequence_number, 4);
2186
2187 let read_buf_2 = vmgs.read_file(FileId::TPM_PPI).await.unwrap();
2188
2189 assert_eq!(buf_2, &*read_buf_2);
2190 assert_eq!(vmgs.fcbs[&FileId(0)].block_offset, 4);
2191 assert_eq!(vmgs.fcbs[&FileId(1)].block_offset, 7);
2192 assert_eq!(vmgs.fcbs[&FileId(2)].block_offset, 6);
2193 }
2194
2195 #[async_test]
2196 async fn multiple_read_write() {
2197 let disk = new_test_file();
2198 let mut vmgs = Vmgs::format_new(disk, None).await.unwrap();
2199
2200 let buf_1 = b"Data data data";
2201 let buf_2 = b"password";
2202 let buf_3 = b"other data data";
2203
2204 vmgs.write_file(FileId::BIOS_NVRAM, buf_1).await.unwrap();
2205 let read_buf_1 = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2206 assert_eq!(buf_1, &*read_buf_1);
2207 assert_eq!(vmgs.active_header_index, 1);
2208 assert_eq!(vmgs.active_header_sequence_number, 2);
2209
2210 vmgs.write_file(FileId::TPM_PPI, buf_2).await.unwrap();
2211 let info = vmgs.get_file_info(FileId::TPM_PPI).unwrap();
2212 assert_eq!(info.valid_bytes as usize, buf_2.len());
2213 let read_buf_2 = vmgs.read_file(FileId::TPM_PPI).await.unwrap();
2214 assert_eq!(buf_2, &*read_buf_2);
2215 assert_eq!(vmgs.active_header_index, 0);
2216 assert_eq!(vmgs.active_header_sequence_number, 3);
2217
2218 vmgs.write_file(FileId::BIOS_NVRAM, buf_3).await.unwrap();
2219 let read_buf_3 = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2220 assert_eq!(buf_3, &*read_buf_3);
2221 assert_eq!(vmgs.active_header_index, 1);
2222 assert_eq!(vmgs.active_header_sequence_number, 4);
2223
2224 vmgs.write_file(FileId::BIOS_NVRAM, buf_1).await.unwrap();
2225 let read_buf_1 = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2226 assert_eq!(buf_1, &*read_buf_1);
2227 assert_eq!(vmgs.active_header_index, 0);
2228 assert_eq!(vmgs.active_header_sequence_number, 5);
2229
2230 vmgs.write_file(FileId::TPM_PPI, buf_2).await.unwrap();
2231 let read_buf_2 = vmgs.read_file(FileId::TPM_PPI).await.unwrap();
2232 assert_eq!(buf_2, &*read_buf_2);
2233 assert_eq!(vmgs.active_header_index, 1);
2234 assert_eq!(vmgs.active_header_sequence_number, 6);
2235
2236 vmgs.write_file(FileId::BIOS_NVRAM, buf_3).await.unwrap();
2237 let read_buf_3 = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2238 assert_eq!(buf_3, &*read_buf_3);
2239 assert_eq!(vmgs.active_header_index, 0);
2240 assert_eq!(vmgs.active_header_sequence_number, 7);
2241 }
2242
2243 #[async_test]
2244 async fn test_insufficient_resources() {
2245 let disk = new_test_file();
2246 let mut vmgs = Vmgs::format_new(disk, None).await.unwrap();
2247
2248 let buf: Vec<u8> = vec![1; ONE_MEGA_BYTE as usize * 5];
2249 let result = vmgs.write_file(FileId::BIOS_NVRAM, &buf).await;
2250 assert!(result.is_err());
2251 if let Err(e) = result {
2252 match e {
2253 Error::InsufficientResources => (),
2254 _ => panic!("Wrong error returned"),
2255 }
2256 } else {
2257 panic!("Should have returned Insufficient resources error");
2258 }
2259 }
2260
2261 #[async_test]
2262 async fn test_empty_write() {
2263 let disk = new_test_file();
2264 let mut vmgs = Vmgs::format_new(disk, None).await.unwrap();
2265
2266 let buf: Vec<u8> = Vec::new();
2267 vmgs.write_file(FileId::BIOS_NVRAM, &buf).await.unwrap();
2268
2269 let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2271
2272 assert_eq!(buf, read_buf);
2273 assert_eq!(read_buf.len(), 0);
2274 assert_eq!(vmgs.active_header_index, 1);
2275 assert_eq!(vmgs.active_header_sequence_number, 2);
2276 }
2277
2278 #[test]
2280 fn test_block_count_to_byte_count() {
2281 let block_count = 10;
2282 let byte_count = block_count_to_byte_count(block_count);
2283 assert!(byte_count == block_count as u64 * VMGS_BYTES_PER_BLOCK as u64);
2284 }
2285
2286 #[test]
2287 fn test_validate_header() {
2288 let mut header = VmgsHeader::new_zeroed();
2289 header.signature = VMGS_SIGNATURE;
2290 header.version = VMGS_VERSION_3_0;
2291 header.header_size = size_of::<VmgsHeader>() as u32;
2292 header.file_table_offset = VMGS_MIN_FILE_BLOCK_OFFSET;
2293 header.file_table_size = VMGS_FILE_TABLE_BLOCK_SIZE;
2294 header.checksum = compute_crc32(header.as_bytes());
2295
2296 let result = validate_header(&header);
2297 assert!(result.is_ok());
2298
2299 let mut header_signature = header;
2300 header_signature.signature = 0;
2301 header_signature.checksum = 0;
2302 header_signature.checksum = compute_crc32(header_signature.as_bytes());
2303 let result = validate_header(&header_signature);
2304 match result {
2305 Err(Error::InvalidFormat(err)) => assert_eq!(err, "Invalid header signature"),
2306 _ => panic!(),
2307 };
2308
2309 let mut header_version = header;
2310 header_version.version = 0;
2311 header_version.checksum = 0;
2312 header_version.checksum = compute_crc32(header_version.as_bytes());
2313 match validate_header(&header_version) {
2314 Err(Error::InvalidFormat(err)) => assert_eq!(err, "Invalid header version"),
2315 _ => panic!(),
2316 };
2317
2318 let mut header_header_size = header;
2319 header_header_size.header_size = 0;
2320 header_header_size.checksum = 0;
2321 header_header_size.checksum = compute_crc32(header_header_size.as_bytes());
2322 match validate_header(&header_header_size) {
2323 Err(Error::InvalidFormat(err)) => assert_eq!(err, "Invalid header size"),
2324 _ => panic!(),
2325 };
2326
2327 let mut header_ft_offset = header;
2328 header_ft_offset.file_table_offset = 0;
2329 header_ft_offset.checksum = 0;
2330 header_ft_offset.checksum = compute_crc32(header_ft_offset.as_bytes());
2331 match validate_header(&header_ft_offset) {
2332 Err(Error::InvalidFormat(err)) => assert_eq!(err, "Invalid file table offset"),
2333 _ => panic!(),
2334 };
2335
2336 let mut header_ft_size = header;
2337 header_ft_size.file_table_size = 0;
2338 header_ft_size.checksum = 0;
2339 header_ft_size.checksum = compute_crc32(header_ft_size.as_bytes());
2340 match validate_header(&header_ft_size) {
2341 Err(Error::InvalidFormat(err)) => assert_eq!(err, "Invalid file table size"),
2342 _ => panic!(),
2343 };
2344 }
2345
2346 #[test]
2347 fn test_initialize_file_metadata() {
2348 let mut file_table = VmgsFileTable::new_zeroed();
2349
2350 file_table.entries[0].offset = 6;
2351 file_table.entries[0].allocation_size = 1;
2352 file_table.entries[1].offset = 2;
2353 file_table.entries[1].allocation_size = 1;
2354 file_table.entries[2].offset = 4;
2355 file_table.entries[2].allocation_size = 5;
2356 file_table.entries[3].offset = 3;
2357 file_table.entries[3].allocation_size = 3;
2358
2359 let block_capacity = 1000;
2360
2361 let fcbs = initialize_file_metadata(&file_table, VMGS_VERSION_3_0, block_capacity).unwrap();
2362 assert!(fcbs[&FileId(0)].block_offset == 6);
2364 assert!(fcbs[&FileId(0)].allocated_blocks.get() == 1);
2365 assert!(fcbs[&FileId(1)].block_offset == 2);
2366 assert!(fcbs[&FileId(1)].allocated_blocks.get() == 1);
2367 assert!(fcbs[&FileId(2)].block_offset == 4);
2368 assert!(fcbs[&FileId(2)].allocated_blocks.get() == 5);
2369 assert!(fcbs[&FileId(3)].block_offset == 3);
2370 assert!(fcbs[&FileId(3)].allocated_blocks.get() == 3);
2371 }
2372
2373 #[test]
2374 fn test_round_up_count() {
2375 assert!(round_up_count(0, 4096) == 0);
2376 assert!(round_up_count(1, 4096) == 4096);
2377 assert!(round_up_count(4095, 4096) == 4096);
2378 assert!(round_up_count(4096, 4096) == 4096);
2379 assert!(round_up_count(4097, 4096) == 8192);
2380 }
2381
2382 #[async_test]
2383 async fn test_header_sequence_overflow() {
2384 let disk = new_test_file();
2385 let mut vmgs = Vmgs::format_new(disk, None).await.unwrap();
2386
2387 vmgs.active_header_sequence_number = u32::MAX;
2388
2389 let buf = b"hello world";
2391 vmgs.write_file(FileId::BIOS_NVRAM, buf).await.unwrap();
2392
2393 assert_eq!(vmgs.active_header_index, 1);
2394 assert_eq!(vmgs.active_header_sequence_number, 0);
2395
2396 vmgs.set_active_header(0, u32::MAX);
2397
2398 let mut new_header = VmgsHeader::new_zeroed();
2399 vmgs.update_header(&mut new_header).await.unwrap();
2400
2401 assert_eq!(vmgs.active_header_index, 1);
2402 assert_eq!(vmgs.active_header_sequence_number, 0);
2403 assert_eq!(new_header.sequence, 0);
2404 }
2405
2406 #[cfg(with_encryption)]
2407 #[async_test]
2408 async fn write_file_v3() {
2409 let disk = new_test_file();
2410 let mut vmgs = Vmgs::format_new(disk.clone(), None).await.unwrap();
2411 let encryption_key = [12; VMGS_ENCRYPTION_KEY_SIZE];
2412
2413 let buf = b"hello world";
2415 let buf_1 = b"hello universe";
2416 vmgs.write_file(FileId::BIOS_NVRAM, buf).await.unwrap();
2417 vmgs.update_encryption_key(&encryption_key, EncryptionAlgorithm::AES_GCM)
2418 .await
2419 .unwrap();
2420 vmgs.write_file_encrypted(FileId::TPM_PPI, buf_1)
2421 .await
2422 .unwrap();
2423
2424 let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2426 assert_eq!(buf, &*read_buf);
2427 let info = vmgs.get_file_info(FileId::TPM_PPI).unwrap();
2428 assert_eq!(info.valid_bytes as usize, buf_1.len());
2429 let read_buf = vmgs.read_file(FileId::TPM_PPI).await.unwrap();
2430 assert_eq!(buf_1, &*read_buf);
2431
2432 drop(vmgs);
2434 let mut vmgs = Vmgs::open(disk, None).await.unwrap();
2435 let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2436 assert_eq!(buf, read_buf.as_bytes());
2437 let info = vmgs.get_file_info(FileId::TPM_PPI).unwrap();
2438 assert_eq!(info.valid_bytes as usize, buf_1.len());
2439 let read_buf = vmgs.read_file(FileId::TPM_PPI).await.unwrap();
2440 assert_ne!(buf_1, read_buf.as_bytes());
2441
2442 vmgs.unlock_with_encryption_key(&encryption_key)
2444 .await
2445 .unwrap();
2446 let info = vmgs.get_file_info(FileId::TPM_PPI).unwrap();
2447 assert_eq!(info.valid_bytes as usize, buf_1.len());
2448 let read_buf = vmgs.read_file(FileId::TPM_PPI).await.unwrap();
2449 assert_eq!(buf_1, &*read_buf);
2450 }
2451
2452 #[cfg(with_encryption)]
2453 #[async_test]
2454 async fn overwrite_file_v3() {
2455 let disk = new_test_file();
2456 let mut vmgs = Vmgs::format_new(disk, None).await.unwrap();
2457 let encryption_key = [1; VMGS_ENCRYPTION_KEY_SIZE];
2458 let buf = vec![1; 8 * 1024];
2459 let buf_1 = vec![2; 8 * 1024];
2460
2461 vmgs.add_new_encryption_key(&encryption_key, EncryptionAlgorithm::AES_GCM)
2463 .await
2464 .unwrap();
2465 assert_eq!(vmgs.active_datastore_key_index, Some(0));
2466
2467 vmgs.write_file_encrypted(FileId::BIOS_NVRAM, &buf)
2469 .await
2470 .unwrap();
2471
2472 vmgs.write_file_encrypted(FileId::BIOS_NVRAM, &buf_1)
2474 .await
2475 .unwrap();
2476
2477 let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2479 assert_eq!(buf_1, read_buf);
2480 }
2481
2482 #[cfg(with_encryption)]
2483 #[async_test]
2484 async fn file_encryption() {
2485 let buf: Vec<u8> = (0..255).collect();
2486 let encryption_key = [1; VMGS_ENCRYPTION_KEY_SIZE];
2487
2488 let disk = new_test_file();
2489 let mut vmgs = Vmgs::format_new(disk.clone(), None).await.unwrap();
2490
2491 vmgs.add_new_encryption_key(&encryption_key, EncryptionAlgorithm::AES_GCM)
2493 .await
2494 .unwrap();
2495 assert_eq!(vmgs.active_datastore_key_index, Some(0));
2496
2497 vmgs.write_file_encrypted(FileId::BIOS_NVRAM, &buf)
2499 .await
2500 .unwrap();
2501
2502 let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2504 assert_eq!(buf, read_buf);
2505
2506 drop(vmgs);
2507
2508 let mut vmgs = Vmgs::open(disk, None).await.unwrap();
2510
2511 let info = vmgs.get_file_info(FileId::BIOS_NVRAM).unwrap();
2512 assert_eq!(info.valid_bytes as usize, buf.len());
2513
2514 vmgs.unlock_with_encryption_key(&encryption_key)
2517 .await
2518 .unwrap();
2519
2520 assert_eq!(vmgs.active_datastore_key_index, Some(0));
2521
2522 let new_encryption_key = [2; VMGS_ENCRYPTION_KEY_SIZE];
2524 vmgs.add_new_encryption_key(&new_encryption_key, EncryptionAlgorithm::AES_GCM)
2525 .await
2526 .unwrap();
2527 assert_eq!(vmgs.active_datastore_key_index, Some(1));
2528 vmgs.remove_encryption_key(0).await.unwrap();
2529
2530 let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2531 assert_eq!(buf, read_buf);
2532 }
2533
2534 #[cfg(with_encryption)]
2535 #[async_test]
2536 async fn add_new_encryption_key() {
2537 let buf: Vec<u8> = (0..255).collect();
2538 let encryption_key = [1; VMGS_ENCRYPTION_KEY_SIZE];
2539 let new_encryption_key = [5; VMGS_ENCRYPTION_KEY_SIZE];
2540
2541 let disk = new_test_file();
2543 let mut vmgs = Vmgs::format_new(disk.clone(), None).await.unwrap();
2544
2545 vmgs.add_new_encryption_key(&encryption_key, EncryptionAlgorithm::AES_GCM)
2547 .await
2548 .unwrap();
2549 assert_eq!(vmgs.active_datastore_key_index, Some(0));
2550
2551 vmgs.write_file_encrypted(FileId::BIOS_NVRAM, &buf)
2553 .await
2554 .unwrap();
2555
2556 drop(vmgs);
2558 let mut vmgs = Vmgs::open(disk.clone(), None).await.unwrap();
2559 vmgs.unlock_with_encryption_key(&encryption_key)
2560 .await
2561 .unwrap();
2562 assert_eq!(vmgs.active_datastore_key_index, Some(0));
2563 let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2564 assert_eq!(read_buf, buf);
2565
2566 vmgs.add_new_encryption_key(&new_encryption_key, EncryptionAlgorithm::AES_GCM)
2568 .await
2569 .unwrap();
2570 assert_eq!(vmgs.active_datastore_key_index, Some(1));
2571
2572 drop(vmgs);
2574 let mut vmgs = Vmgs::open(disk, None).await.unwrap();
2575 vmgs.unlock_with_encryption_key(&encryption_key)
2576 .await
2577 .unwrap();
2578 assert_eq!(vmgs.active_datastore_key_index, Some(0));
2579 vmgs.unlock_with_encryption_key(&new_encryption_key)
2580 .await
2581 .unwrap();
2582 assert_eq!(vmgs.active_datastore_key_index, Some(1));
2583 let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2584 assert_eq!(read_buf, buf);
2585
2586 vmgs.remove_encryption_key(1).await.unwrap();
2588 vmgs.add_new_encryption_key(&new_encryption_key, EncryptionAlgorithm::AES_GCM)
2589 .await
2590 .unwrap();
2591 assert_eq!(vmgs.active_datastore_key_index, Some(1));
2592
2593 vmgs.remove_encryption_key(0).await.unwrap();
2595 let result = vmgs.unlock_with_encryption_key(&encryption_key).await;
2596 assert!(matches!(result, Err(Error::Other(_))));
2597
2598 let result = vmgs.remove_encryption_key(0).await;
2600 assert!(matches!(result, Err(Error::Other(_))));
2601
2602 vmgs.remove_encryption_key(1).await.unwrap();
2604 let read_buf = vmgs.read_file_raw(FileId::BIOS_NVRAM).await;
2605 assert_ne!(read_buf.unwrap(), buf);
2606 }
2607
2608 #[cfg(with_encryption)]
2609 #[async_test]
2610 async fn test_write_file_encrypted() {
2611 let disk = new_test_file();
2615 let mut vmgs = Vmgs::format_new(disk.clone(), None).await.unwrap();
2616 let buf = b"This is plaintext";
2617
2618 vmgs.write_file_encrypted(FileId::BIOS_NVRAM, buf)
2620 .await
2621 .unwrap();
2622
2623 let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2625 assert_eq!(vmgs.encryption_algorithm, EncryptionAlgorithm::NONE);
2626 assert_eq!(buf, &*read_buf);
2627
2628 drop(vmgs);
2631 let mut vmgs = Vmgs::open(disk, None).await.unwrap();
2632
2633 let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2634 assert_eq!(vmgs.encryption_algorithm, EncryptionAlgorithm::NONE);
2635 assert_eq!(buf, &*read_buf);
2636 }
2637
2638 #[cfg(with_encryption)]
2639 #[async_test]
2640 async fn test_logger() {
2641 let disk = new_test_file();
2642 let data = Arc::new(Mutex::new(String::new()));
2643 let mut vmgs = Vmgs::format_new(
2644 disk.clone(),
2645 Some(Arc::new(TestVmgsLogger { data: data.clone() })),
2646 )
2647 .await
2648 .unwrap();
2649 let encryption_key = [12; VMGS_ENCRYPTION_KEY_SIZE];
2650
2651 let buf = b"hello world";
2653 vmgs.update_encryption_key(&encryption_key, EncryptionAlgorithm::AES_GCM)
2654 .await
2655 .unwrap();
2656 vmgs.write_file_encrypted(FileId::BIOS_NVRAM, buf)
2657 .await
2658 .unwrap();
2659
2660 let fcb = vmgs.fcbs.get_mut(&FileId::BIOS_NVRAM).unwrap();
2661
2662 fcb.nonce[0] ^= 1;
2664
2665 let result = vmgs.read_file(FileId::BIOS_NVRAM).await;
2667 assert!(result.is_err());
2668
2669 let result = data.lock();
2671 assert_eq!(*result, "test logger");
2672 }
2673
2674 #[cfg(with_encryption)]
2675 #[async_test]
2676 async fn update_key() {
2677 let buf: Vec<u8> = (0..255).collect();
2678 let encryption_key = [1; VMGS_ENCRYPTION_KEY_SIZE];
2679
2680 let disk = new_test_file();
2681 let mut vmgs = Vmgs::format_new(disk.clone(), None).await.unwrap();
2682
2683 vmgs.update_encryption_key(&encryption_key, EncryptionAlgorithm::AES_GCM)
2685 .await
2686 .unwrap();
2687 assert_eq!(vmgs.active_datastore_key_index, Some(0));
2688 assert_eq!(vmgs.datastore_key_count, 1);
2689
2690 vmgs.write_file_encrypted(FileId::BIOS_NVRAM, &buf)
2692 .await
2693 .unwrap();
2694
2695 let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2697 assert_eq!(buf, read_buf);
2698
2699 drop(vmgs);
2701 let mut vmgs = Vmgs::open(disk.clone(), None).await.unwrap();
2702
2703 vmgs.unlock_with_encryption_key(&encryption_key)
2705 .await
2706 .unwrap();
2707 assert_eq!(vmgs.active_datastore_key_index, Some(0));
2708
2709 let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2711 assert_eq!(buf, read_buf);
2712
2713 let new_encryption_key = [2; VMGS_ENCRYPTION_KEY_SIZE];
2715 vmgs.update_encryption_key(&new_encryption_key, EncryptionAlgorithm::AES_GCM)
2716 .await
2717 .unwrap();
2718 assert_eq!(vmgs.active_datastore_key_index, Some(1));
2719 assert_eq!(vmgs.datastore_key_count, 1);
2720
2721 let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2723 assert_eq!(buf, read_buf);
2724
2725 drop(vmgs);
2727 let mut vmgs = Vmgs::open(disk, None).await.unwrap();
2728
2729 vmgs.unlock_with_encryption_key(&new_encryption_key)
2731 .await
2732 .unwrap();
2733 assert_eq!(vmgs.active_datastore_key_index, Some(1));
2734
2735 let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2737 assert_eq!(buf, read_buf);
2738 }
2739
2740 #[cfg(with_encryption)]
2741 #[async_test]
2742 async fn update_key_no_space() {
2743 let buf: Vec<u8> = (0..255).collect();
2744 let encryption_key = [1; VMGS_ENCRYPTION_KEY_SIZE];
2745
2746 let disk = new_test_file();
2747 let mut vmgs = Vmgs::format_new(disk.clone(), None).await.unwrap();
2748
2749 vmgs.update_encryption_key(&encryption_key, EncryptionAlgorithm::AES_GCM)
2751 .await
2752 .unwrap();
2753 assert_eq!(vmgs.active_datastore_key_index, Some(0));
2754 assert_eq!(vmgs.datastore_key_count, 1);
2755
2756 vmgs.write_file_encrypted(FileId::BIOS_NVRAM, &buf)
2758 .await
2759 .unwrap();
2760
2761 let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2763 assert_eq!(buf, read_buf);
2764
2765 let new_encryption_key = [2; VMGS_ENCRYPTION_KEY_SIZE];
2767 vmgs.add_new_encryption_key(&new_encryption_key, EncryptionAlgorithm::AES_GCM)
2768 .await
2769 .unwrap();
2770 assert_eq!(vmgs.active_datastore_key_index, Some(1));
2771 assert_eq!(vmgs.datastore_key_count, 2);
2772
2773 drop(vmgs);
2775 let mut vmgs = Vmgs::open(disk.clone(), None).await.unwrap();
2776
2777 vmgs.unlock_with_encryption_key(&new_encryption_key)
2779 .await
2780 .unwrap();
2781 assert_eq!(vmgs.active_datastore_key_index, Some(1));
2782
2783 let another_encryption_key = [2; VMGS_ENCRYPTION_KEY_SIZE];
2785 vmgs.update_encryption_key(&another_encryption_key, EncryptionAlgorithm::AES_GCM)
2786 .await
2787 .unwrap();
2788 assert_eq!(vmgs.active_datastore_key_index, Some(0));
2789 assert_eq!(vmgs.datastore_key_count, 1);
2790
2791 drop(vmgs);
2793 let mut vmgs = Vmgs::open(disk, None).await.unwrap();
2794
2795 vmgs.unlock_with_encryption_key(&another_encryption_key)
2797 .await
2798 .unwrap();
2799 assert_eq!(vmgs.active_datastore_key_index, Some(0));
2800
2801 let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2803 assert_eq!(buf, read_buf);
2804 }
2805}