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