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(
1084 &mut self,
1085 encryption_key: &[u8],
1086 ) -> Result<usize, Error> {
1087 if self.version < VMGS_VERSION_3_0 {
1088 return Err(Error::Other(anyhow!(
1089 "unlock_with_encryption_key() not supported with VMGS version"
1090 )));
1091 }
1092 if self.encryption_algorithm == EncryptionAlgorithm::NONE {
1093 return Err(Error::Other(anyhow!(
1094 "unlock_with_encryption_key() not supported with None EncryptionAlgorithm"
1095 )));
1096 }
1097
1098 let mut valid_index = None;
1101 let mut errs = [None, None];
1102
1103 for (i, key) in self.encrypted_metadata_keys.iter().enumerate() {
1104 let result = decrypt_metadata_key(
1105 encryption_key,
1106 &key.nonce,
1107 &key.encryption_key,
1108 &key.authentication_tag,
1109 );
1110
1111 match result {
1112 Ok(metadata_key) => {
1113 self.metadata_key.copy_from_slice(&metadata_key);
1114 valid_index = Some(i);
1115 break;
1116 }
1117 Err(err) => {
1118 errs[i] = Some(err);
1119 }
1120 }
1121 }
1122
1123 let valid_index = match valid_index {
1124 Some(idx) => idx,
1125 None => {
1126 tracing::error!(
1127 CVM_ALLOWED,
1128 error = &errs[0].take().unwrap() as &dyn std::error::Error,
1129 "first index failed to decrypt",
1130 );
1131 tracing::error!(
1132 CVM_ALLOWED,
1133 error = &errs[1].take().unwrap() as &dyn std::error::Error,
1134 "second index failed to decrypt",
1135 );
1136 return Err(Error::Other(anyhow::anyhow!(
1137 "failed to use the root key provided to decrypt VMGS metadata key"
1138 )));
1139 }
1140 };
1141 let extended_file_header = self.fcbs[&FileId::EXTENDED_FILE_TABLE];
1142 let extended_file_table_size_bytes =
1143 block_count_to_byte_count(extended_file_header.allocated_blocks.get());
1144 let mut extended_file_table_buffer = vec![0; extended_file_table_size_bytes as usize];
1145 let self_metadata_key = self.metadata_key;
1146
1147 self.read_decrypted_data(
1149 extended_file_header.block_offset,
1150 &self_metadata_key,
1151 &extended_file_header.nonce,
1152 &extended_file_header.authentication_tag,
1153 &mut extended_file_table_buffer,
1154 )
1155 .await
1156 .context("failed to decrypt extended file table")?;
1157
1158 let extended_file_table =
1160 VmgsExtendedFileTable::read_from_prefix(extended_file_table_buffer.as_bytes())
1161 .map_err(|_| anyhow!("Invalid decrypted extended file table"))? .0;
1163 for (file_id, fcb) in self.fcbs.iter_mut() {
1164 fcb.attributes = extended_file_table.entries[*file_id].attributes;
1165 fcb.encryption_key = extended_file_table.entries[*file_id].encryption_key;
1166 }
1167
1168 self.datastore_keys[valid_index].copy_from_slice(encryption_key);
1169 self.active_datastore_key_index = Some(valid_index);
1170
1171 Ok(valid_index)
1172 }
1173
1174 #[cfg_attr(not(with_encryption), expect(unused_variables))]
1176 async fn write_encrypted_data(
1177 &mut self,
1178 block_offset: u32,
1179 encryption_key: &[u8],
1180 nonce: &[u8],
1181 plaintext_data: &[u8],
1182 authentication_tag: &mut [u8],
1183 ) -> Result<(), Error> {
1184 #[cfg(not(with_encryption))]
1185 unreachable!("Encryption requires the encryption feature");
1186 #[cfg(with_encryption)]
1187 {
1188 let encrypted_text = crate::encrypt::vmgs_encrypt(
1189 encryption_key,
1190 nonce,
1191 plaintext_data,
1192 authentication_tag,
1193 )?;
1194
1195 self.storage
1197 .write_block(block_count_to_byte_count(block_offset), &encrypted_text)
1198 .await
1199 .map_err(Error::WriteDisk)?;
1200
1201 Ok(())
1202 }
1203 }
1204
1205 #[cfg_attr(not(with_encryption), expect(unused_variables))]
1207 async fn read_decrypted_data(
1208 &mut self,
1209 block_offset: u32,
1210 decryption_key: &[u8],
1211 nonce: &[u8],
1212 authentication_tag: &[u8],
1213 plaintext_data: &mut [u8],
1214 ) -> Result<(), Error> {
1215 #[cfg(not(with_encryption))]
1216 unreachable!("Encryption requires the encryption feature");
1217 #[cfg(with_encryption)]
1218 {
1219 let mut buf = vec![0; plaintext_data.len()];
1221
1222 self.storage
1223 .read_block(block_count_to_byte_count(block_offset), &mut buf)
1224 .await
1225 .map_err(Error::ReadDisk)?;
1226
1227 if buf.iter().all(|x| *x == 0) {
1231 return Err(Error::InvalidFormat("encrypted data is all-zeros".into()));
1232 }
1233
1234 let decrypted_text =
1235 crate::encrypt::vmgs_decrypt(decryption_key, nonce, &buf, authentication_tag)?;
1236 if decrypted_text.len() != plaintext_data.len() {
1237 return Err(Error::Other(anyhow!(
1238 "Decrypt error, slice sizes should match."
1239 )));
1240 }
1241 plaintext_data.copy_from_slice(&decrypted_text);
1242
1243 Ok(())
1244 }
1245 }
1246
1247 #[cfg(with_encryption)]
1249 pub async fn add_new_encryption_key(
1250 &mut self,
1251 encryption_key: &[u8],
1252 encryption_algorithm: EncryptionAlgorithm,
1253 ) -> Result<usize, Error> {
1254 if self.version < VMGS_VERSION_3_0 {
1255 return Err(Error::Other(anyhow!(
1256 "add_new_encryption_key() not supported with VMGS version"
1257 )));
1258 }
1259 if self.encryption_algorithm != EncryptionAlgorithm::NONE
1260 && self.active_datastore_key_index.is_none()
1261 {
1262 return Err(Error::Other(anyhow!(
1263 "add_new_encryption_key() invalid datastore key index"
1264 )));
1265 }
1266 if self.datastore_key_count == self.datastore_keys.len() as u8 {
1267 return Err(Error::Other(anyhow!(
1268 "add_new_encryption_key() no space to add new encryption key"
1269 )));
1270 }
1271 if is_empty_key(encryption_key) {
1272 return Err(Error::Other(anyhow!("Trying to add empty encryption key")));
1273 }
1274 if encryption_algorithm == EncryptionAlgorithm::NONE {
1275 return Err(Error::Other(anyhow!(
1276 "Encryption not supported for VMGS file"
1277 )));
1278 }
1279 if self.encryption_algorithm != EncryptionAlgorithm::NONE
1280 && encryption_algorithm != self.encryption_algorithm
1281 {
1282 return Err(Error::Other(anyhow!(
1283 "Encryption algorithm provided to add_new_encryption_key does not match VMGS's encryption algorithm."
1284 )));
1285 }
1286
1287 let mut new_key_index = 0;
1288 let mut new_metadata_key = self.metadata_key;
1289 if self.datastore_key_count == 0 {
1290 let mut temp_fcbs: Vec<ResolvedFileControlBlock> = Vec::new();
1295 self.allocate_space(
1296 VMGS_FILE_TABLE_BLOCK_SIZE,
1297 &mut temp_fcbs,
1298 block_count_to_byte_count(VMGS_FILE_TABLE_BLOCK_SIZE),
1299 )?;
1300
1301 self.allocate_space(
1302 VMGS_EXTENDED_FILE_TABLE_BLOCK_SIZE,
1303 &mut temp_fcbs,
1304 block_count_to_byte_count(VMGS_EXTENDED_FILE_TABLE_BLOCK_SIZE),
1305 )?;
1306
1307 let mut extended_file_table_fcb = temp_fcbs.pop().unwrap();
1308 let mut file_table_fcb = temp_fcbs.pop().unwrap();
1309
1310 extended_file_table_fcb.attributes = FileAttribute::new()
1311 .with_encrypted(true)
1312 .with_authenticated(true);
1313
1314 let new_extended_file_table = VmgsExtendedFileTable::new_zeroed();
1316 self.write_file_internal(
1318 FileId::EXTENDED_FILE_TABLE,
1319 new_extended_file_table.as_bytes(),
1320 &mut file_table_fcb,
1321 &mut extended_file_table_fcb,
1322 true,
1323 true,
1324 )
1325 .await?;
1326
1327 new_metadata_key
1328 .copy_from_slice(&self.fcbs[&FileId::EXTENDED_FILE_TABLE].encryption_key);
1329 } else if self.active_datastore_key_index == Some(0) {
1330 new_key_index = 1;
1331 }
1332
1333 let mut new_header = self.prepare_new_header(&self.fcbs[&FileId::FILE_TABLE]);
1335 new_header.encryption_algorithm = EncryptionAlgorithm::AES_GCM;
1336
1337 let metadata_key_nonce = generate_nonce();
1339 let mut metadata_key_auth_tag = VmgsAuthTag::new_zeroed();
1340 let encrypted_metadata_key = encrypt_metadata_key(
1341 encryption_key,
1342 &metadata_key_nonce,
1343 &new_metadata_key,
1344 &mut metadata_key_auth_tag,
1345 )?;
1346
1347 self.encrypted_metadata_keys[new_key_index]
1348 .nonce
1349 .copy_from_slice(&metadata_key_nonce);
1350 self.encrypted_metadata_keys[new_key_index]
1351 .authentication_tag
1352 .copy_from_slice(&metadata_key_auth_tag);
1353 self.encrypted_metadata_keys[new_key_index]
1354 .encryption_key
1355 .copy_from_slice(&encrypted_metadata_key);
1356
1357 new_header
1358 .metadata_keys
1359 .copy_from_slice(&self.encrypted_metadata_keys);
1360
1361 self.update_header(&mut new_header).await?;
1363
1364 self.datastore_keys[new_key_index].copy_from_slice(encryption_key);
1366 self.metadata_key.copy_from_slice(&new_metadata_key);
1367 self.datastore_key_count += 1;
1368 self.encryption_algorithm = encryption_algorithm;
1369 self.active_datastore_key_index = Some(new_key_index);
1370
1371 Ok(new_key_index)
1372 }
1373
1374 #[cfg(with_encryption)]
1376 pub async fn remove_encryption_key(&mut self, key_index: usize) -> Result<(), Error> {
1377 if self.version < VMGS_VERSION_3_0 {
1378 return Err(Error::Other(anyhow!(
1379 "remove_encryption_key() not supported with VMGS version."
1380 )));
1381 }
1382 if self.encryption_algorithm != EncryptionAlgorithm::NONE
1383 && self.active_datastore_key_index.is_none()
1384 {
1385 return Err(Error::Other(anyhow!(
1386 "remove_encryption_key() invalid datastore key index or encryption algorithm."
1387 )));
1388 }
1389 if self.datastore_key_count != self.datastore_keys.len() as u8
1390 && self.active_datastore_key_index != Some(key_index)
1391 {
1392 return Err(Error::Other(anyhow!(
1393 "remove_encryption_key() invalid key_index"
1394 )));
1395 }
1396
1397 self.datastore_keys[key_index].fill(0);
1399
1400 self.encrypted_metadata_keys[key_index] = VmgsEncryptionKey::new_zeroed();
1402
1403 let mut new_header = self.prepare_new_header(&self.fcbs[&FileId::FILE_TABLE]);
1405 new_header
1406 .metadata_keys
1407 .copy_from_slice(&self.encrypted_metadata_keys);
1408
1409 if self.datastore_key_count == 1 {
1411 new_header.encryption_algorithm = EncryptionAlgorithm::NONE;
1412 } else {
1413 new_header.encryption_algorithm = self.encryption_algorithm;
1414 }
1415
1416 self.update_header(&mut new_header).await?;
1418
1419 if self.datastore_key_count == 1 {
1421 self.encryption_algorithm = EncryptionAlgorithm::NONE;
1422 self.datastore_key_count = 0;
1423 self.active_datastore_key_index = None;
1424 } else {
1425 self.datastore_key_count = 1;
1426
1427 let new_active_datastore_key_index = if key_index == 0 { 1 } else { 0 };
1428 if is_empty_key(&self.datastore_keys[new_active_datastore_key_index]) {
1429 self.active_datastore_key_index = None;
1430 } else {
1431 self.active_datastore_key_index = Some(new_active_datastore_key_index);
1432 }
1433 }
1434
1435 Ok(())
1436 }
1437
1438 pub fn get_encryption_algorithm(&self) -> EncryptionAlgorithm {
1440 self.encryption_algorithm
1441 }
1442
1443 pub fn is_encrypted(&self) -> bool {
1445 self.encryption_algorithm != EncryptionAlgorithm::NONE
1446 }
1447
1448 pub fn get_active_datastore_key_index(&self) -> Option<usize> {
1450 self.active_datastore_key_index
1451 }
1452
1453 fn prepare_new_header(&self, file_table_fcb: &ResolvedFileControlBlock) -> VmgsHeader {
1454 VmgsHeader {
1455 signature: VMGS_SIGNATURE,
1456 version: self.version,
1457 header_size: size_of::<VmgsHeader>() as u32,
1458 file_table_offset: file_table_fcb.block_offset,
1459 file_table_size: file_table_fcb.allocated_blocks.get(),
1460 ..VmgsHeader::new_zeroed()
1461 }
1462 }
1463}
1464
1465pub async fn read_headers(disk: Disk) -> Result<(VmgsHeader, VmgsHeader), Error> {
1468 read_headers_inner(&mut VmgsStorage::new(disk)).await
1469}
1470
1471async fn read_headers_inner(storage: &mut VmgsStorage) -> Result<(VmgsHeader, VmgsHeader), Error> {
1472 let mut first_two_blocks = [0; (VMGS_BYTES_PER_BLOCK * 2) as usize];
1475 storage
1476 .read_block(0, &mut first_two_blocks)
1477 .await
1478 .map_err(Error::ReadDisk)?;
1479
1480 let header_1 = VmgsHeader::read_from_prefix(&first_two_blocks).unwrap().0; let header_2 =
1483 VmgsHeader::read_from_prefix(&first_two_blocks[storage.aligned_header_size() as usize..])
1484 .unwrap()
1485 .0; Ok((header_1, header_2))
1487}
1488
1489pub fn get_active_header(
1492 header_1: Result<&VmgsHeader, Error>,
1493 header_2: Result<&VmgsHeader, Error>,
1494) -> Result<usize, Error> {
1495 let active_header_index =
1496 if let (Ok(header_1), Ok(header_2)) = (header_1.as_deref(), header_2.as_deref()) {
1497 if header_1.sequence == header_2.sequence.wrapping_add(1) {
1502 0
1503 } else if header_2.sequence == header_1.sequence.wrapping_add(1) {
1504 1
1505 } else {
1506 return Err(Error::CorruptFormat(format!(
1507 "Invalid header sequence numbers. Header 1: {}, Header 2: {}",
1508 header_1.sequence, header_2.sequence
1509 )));
1510 }
1511 } else if header_1.is_ok() {
1512 0
1513 } else if header_2.is_ok() {
1514 1
1515 } else {
1516 return Err(Error::InvalidFormat(format!(
1517 "No valid header: Header 1: {} Header 2: {}",
1518 header_1.err().unwrap(),
1519 header_2.err().unwrap()
1520 )));
1521 };
1522
1523 Ok(active_header_index)
1524}
1525
1526pub fn validate_header(header: &VmgsHeader) -> Result<&VmgsHeader, Error> {
1528 if header.signature != VMGS_SIGNATURE {
1529 return Err(Error::InvalidFormat(String::from(
1530 "Invalid header signature",
1531 )));
1532 }
1533 if header.version != VMGS_VERSION_3_0 {
1534 return Err(Error::InvalidFormat(String::from("Invalid header version")));
1535 }
1536 if header.header_size != size_of::<VmgsHeader>() as u32 {
1537 return Err(Error::InvalidFormat(String::from("Invalid header size")));
1538 }
1539 if header.file_table_offset < VMGS_MIN_FILE_BLOCK_OFFSET {
1540 return Err(Error::InvalidFormat(String::from(
1541 "Invalid file table offset",
1542 )));
1543 }
1544 if header.file_table_size != VMGS_FILE_TABLE_BLOCK_SIZE {
1545 return Err(Error::InvalidFormat(String::from(
1546 "Invalid file table size",
1547 )));
1548 }
1549
1550 let stored_checksum = header.checksum;
1551 let mut zero_checksum_header = *header;
1552 zero_checksum_header.checksum = 0;
1553 let computed_checksum = compute_crc32(zero_checksum_header.as_bytes());
1554 if stored_checksum != computed_checksum {
1555 return Err(Error::CorruptFormat(String::from(
1556 "Invalid header checksum",
1557 )));
1558 }
1559 Ok(header)
1560}
1561
1562fn initialize_file_metadata(
1564 file_table: &VmgsFileTable,
1565 version: u32,
1566 block_capacity: u32,
1567) -> Result<HashMap<FileId, ResolvedFileControlBlock>, Error> {
1568 let file_entries = file_table.entries;
1569 let mut file_control_blocks = HashMap::new();
1570
1571 for (file_id, file_entry) in file_entries.iter().enumerate() {
1572 let file_id = FileId(file_id as u32);
1573
1574 let Some(allocated_blocks) = NonZeroU32::new(file_entry.allocation_size) else {
1576 continue;
1577 };
1578
1579 if file_entry.offset < VMGS_MIN_FILE_BLOCK_OFFSET || file_entry.offset >= block_capacity {
1581 return Err(Error::CorruptFormat(format!(
1582 "Invalid file offset {} for file_id {:?} \n{:?}",
1583 file_entry.offset, file_id, file_entry
1584 )));
1585 }
1586
1587 let file_allocation_end_block = file_entry.offset + file_entry.allocation_size;
1589 if file_allocation_end_block > block_capacity {
1590 return Err(Error::CorruptFormat(String::from(
1591 "Invalid file allocation end block",
1592 )));
1593 }
1594
1595 let file_allocation_size_bytes = block_count_to_byte_count(file_entry.allocation_size);
1597 if file_entry.valid_data_size > file_allocation_size_bytes {
1598 return Err(Error::CorruptFormat(String::from("Invalid data size")));
1599 }
1600
1601 file_control_blocks.insert(file_id, {
1603 let (nonce, authentication_tag) = if version >= VMGS_VERSION_3_0 {
1604 (file_entry.nonce, file_entry.authentication_tag)
1605 } else {
1606 Default::default()
1607 };
1608
1609 ResolvedFileControlBlock {
1610 block_offset: file_entry.offset,
1611 allocated_blocks,
1612 valid_bytes: file_entry.valid_data_size,
1613
1614 nonce,
1615 authentication_tag,
1616
1617 attributes: FileAttribute::new(),
1618 encryption_key: VmgsDatastoreKey::new_zeroed(),
1619 }
1620 });
1621 }
1622
1623 Ok(file_control_blocks)
1624}
1625
1626fn block_count_to_byte_count(block_count: u32) -> u64 {
1628 block_count as u64 * VMGS_BYTES_PER_BLOCK as u64
1629}
1630
1631fn round_up_count(count: usize, pow2: u32) -> u64 {
1632 (count as u64 + pow2 as u64 - 1) & !(pow2 as u64 - 1)
1633}
1634
1635fn generate_nonce() -> VmgsNonce {
1637 let mut nonce = VmgsNonce::new_zeroed();
1638 getrandom::fill(&mut nonce[..4]).expect("rng failure");
1640 nonce
1641}
1642
1643fn increment_nonce(nonce: &mut VmgsNonce) -> Result<(), Error> {
1645 getrandom::fill(&mut nonce[..vmgs_format::VMGS_NONCE_RANDOM_SEED_SIZE]).expect("rng failure");
1647
1648 for i in &mut nonce[vmgs_format::VMGS_NONCE_RANDOM_SEED_SIZE..] {
1650 *i = i.wrapping_add(1);
1651
1652 if *i != 0 {
1653 break;
1654 }
1655 }
1656
1657 Ok(())
1658}
1659
1660fn is_empty_key(encryption_key: &[u8]) -> bool {
1662 encryption_key.iter().all(|&x| x == 0)
1663}
1664
1665#[cfg_attr(not(with_encryption), expect(unused_variables))]
1667fn encrypt_metadata_key(
1668 encryption_key: &[u8],
1669 nonce: &[u8],
1670 metadata_key: &[u8],
1671 authentication_tag: &mut [u8],
1672) -> Result<Vec<u8>, Error> {
1673 #[cfg(not(with_encryption))]
1674 unreachable!("Encryption requires the encryption feature");
1675 #[cfg(with_encryption)]
1676 {
1677 let encrypted_metadata_key =
1678 crate::encrypt::vmgs_encrypt(encryption_key, nonce, metadata_key, authentication_tag)?;
1679
1680 if encrypted_metadata_key.len() != metadata_key.len() {
1681 return Err(Error::Other(anyhow!(format!(
1682 "encrypted metadata key length ({}) doesn't match metadata key length ({})",
1683 encrypted_metadata_key.len(),
1684 metadata_key.len()
1685 ))));
1686 }
1687 Ok(encrypted_metadata_key)
1688 }
1689}
1690
1691#[cfg_attr(not(with_encryption), expect(unused_variables), expect(dead_code))]
1693fn decrypt_metadata_key(
1694 datastore_key: &[u8],
1695 nonce: &[u8],
1696 metadata_key: &[u8],
1697 authentication_tag: &[u8],
1698) -> Result<Vec<u8>, Error> {
1699 #[cfg(not(with_encryption))]
1700 unreachable!("Encryption requires the encryption feature");
1701 #[cfg(with_encryption)]
1702 {
1703 let decrypted_metadata_key =
1704 crate::encrypt::vmgs_decrypt(datastore_key, nonce, metadata_key, authentication_tag)?;
1705 if decrypted_metadata_key.len() != metadata_key.len() {
1706 return Err(Error::Other(anyhow!(format!(
1707 "decrypted metadata key length ({}) doesn't match metadata key length ({})",
1708 decrypted_metadata_key.len(),
1709 metadata_key.len()
1710 ))));
1711 }
1712
1713 Ok(decrypted_metadata_key)
1714 }
1715}
1716
1717fn compute_crc32(buf: &[u8]) -> u32 {
1719 let mut hasher = crc32fast::Hasher::new();
1720 hasher.update(buf);
1721 hasher.finalize()
1722}
1723
1724#[cfg(feature = "save_restore")]
1725#[expect(missing_docs)]
1726pub mod save_restore {
1727 use super::*;
1728
1729 pub mod state {
1730 use mesh_protobuf::Protobuf;
1731 use std::num::NonZeroU32;
1732
1733 pub type SavedVmgsNonce = [u8; 12];
1734 pub type SavedVmgsAuthTag = [u8; 16];
1735 pub type SavedVmgsDatastoreKey = [u8; 32];
1736
1737 #[derive(Protobuf)]
1738 #[mesh(package = "vmgs")]
1739 pub struct SavedResolvedFileControlBlock {
1740 #[mesh(1)]
1741 pub block_offset: u32,
1742 #[mesh(2)]
1743 pub allocated_blocks: NonZeroU32,
1744 #[mesh(3)]
1745 pub valid_bytes: u64,
1746 #[mesh(4)]
1747 pub nonce: SavedVmgsNonce,
1748 #[mesh(5)]
1749 pub authentication_tag: SavedVmgsAuthTag,
1750 #[mesh(6)]
1751 pub attributes: u32,
1752 #[mesh(7)]
1753 pub encryption_key: SavedVmgsDatastoreKey,
1754 }
1755
1756 #[derive(Protobuf)]
1757 #[mesh(package = "vmgs")]
1758 pub struct SavedVmgsEncryptionKey {
1759 #[mesh(1)]
1760 pub nonce: SavedVmgsNonce,
1761 #[mesh(2)]
1762 pub authentication_tag: SavedVmgsAuthTag,
1763 #[mesh(3)]
1764 pub encryption_key: SavedVmgsDatastoreKey,
1765 }
1766
1767 #[derive(Protobuf)]
1768 #[mesh(package = "vmgs")]
1769 pub struct SavedVmgsState {
1770 #[mesh(1)]
1771 pub active_header_index: usize,
1772 #[mesh(2)]
1773 pub active_header_sequence_number: u32,
1774 #[mesh(3)]
1775 pub version: u32,
1776 #[mesh(4)]
1777 pub fcbs: Vec<(u32, SavedResolvedFileControlBlock)>,
1778 #[mesh(5)]
1779 pub encryption_algorithm: u16,
1780 #[mesh(6)]
1781 pub datastore_key_count: u8,
1782 #[mesh(7)]
1783 pub active_datastore_key_index: Option<usize>,
1784 #[mesh(8)]
1785 pub datastore_keys: [SavedVmgsDatastoreKey; 2],
1786 #[mesh(9)]
1787 pub metadata_key: SavedVmgsDatastoreKey,
1788 #[mesh(10)]
1789 pub encrypted_metadata_keys: [SavedVmgsEncryptionKey; 2],
1790 }
1791 }
1792
1793 impl Vmgs {
1794 pub fn open_from_saved(
1812 disk: Disk,
1813 state: state::SavedVmgsState,
1814 logger: Option<Arc<dyn VmgsLogger>>,
1815 ) -> Self {
1816 let state::SavedVmgsState {
1817 active_header_index,
1818 active_header_sequence_number,
1819 version,
1820 fcbs,
1821 encryption_algorithm,
1822 datastore_key_count,
1823 active_datastore_key_index,
1824 datastore_keys,
1825 metadata_key,
1826 encrypted_metadata_keys,
1827 } = state;
1828
1829 Self {
1830 storage: VmgsStorage::new(disk),
1831 #[cfg(feature = "inspect")]
1832 stats: Default::default(),
1833
1834 active_header_index,
1835 active_header_sequence_number,
1836 version,
1837 fcbs: fcbs
1838 .into_iter()
1839 .map(|(file_id, fcb)| {
1840 let state::SavedResolvedFileControlBlock {
1841 block_offset,
1842 allocated_blocks,
1843 valid_bytes,
1844 nonce,
1845 authentication_tag,
1846 attributes,
1847 encryption_key,
1848 } = fcb;
1849
1850 (
1851 FileId(file_id),
1852 ResolvedFileControlBlock {
1853 block_offset,
1854 allocated_blocks,
1855 valid_bytes,
1856 nonce,
1857 authentication_tag,
1858 attributes: FileAttribute::from(attributes),
1859 encryption_key,
1860 },
1861 )
1862 })
1863 .collect(),
1864 encryption_algorithm: EncryptionAlgorithm(encryption_algorithm),
1865 datastore_key_count,
1866 active_datastore_key_index,
1867 datastore_keys,
1868 metadata_key,
1869 encrypted_metadata_keys: encrypted_metadata_keys.map(|k| {
1870 let state::SavedVmgsEncryptionKey {
1871 nonce,
1872 authentication_tag,
1873 encryption_key,
1874 } = k;
1875
1876 VmgsEncryptionKey {
1877 nonce,
1878 reserved: 0,
1879 authentication_tag,
1880 encryption_key,
1881 }
1882 }),
1883 logger,
1884 }
1885 }
1886
1887 pub fn save(&self) -> state::SavedVmgsState {
1893 let Self {
1894 storage: _,
1895
1896 #[cfg(feature = "inspect")]
1897 stats: _,
1898
1899 active_header_index,
1900 active_header_sequence_number,
1901 version,
1902 fcbs,
1903 encryption_algorithm,
1904 datastore_key_count,
1905 active_datastore_key_index,
1906 datastore_keys,
1907 metadata_key,
1908 encrypted_metadata_keys,
1909 logger: _,
1910 } = self;
1911
1912 state::SavedVmgsState {
1913 active_header_index: *active_header_index,
1914 active_header_sequence_number: *active_header_sequence_number,
1915 version: *version,
1916 fcbs: fcbs
1917 .iter()
1918 .map(|(file_id, fcb)| {
1919 let ResolvedFileControlBlock {
1920 block_offset,
1921 allocated_blocks,
1922 valid_bytes,
1923 nonce,
1924 authentication_tag,
1925 attributes,
1926 encryption_key,
1927 } = fcb;
1928
1929 (
1930 file_id.0,
1931 state::SavedResolvedFileControlBlock {
1932 block_offset: *block_offset,
1933 allocated_blocks: *allocated_blocks,
1934 valid_bytes: *valid_bytes,
1935 nonce: *nonce,
1936 authentication_tag: *authentication_tag,
1937 attributes: (*attributes).into(),
1938 encryption_key: *encryption_key,
1939 },
1940 )
1941 })
1942 .collect(),
1943 encryption_algorithm: encryption_algorithm.0,
1944 datastore_key_count: *datastore_key_count,
1945 active_datastore_key_index: *active_datastore_key_index,
1946 datastore_keys: *datastore_keys,
1947 metadata_key: *metadata_key,
1948 encrypted_metadata_keys: encrypted_metadata_keys.map(|k| {
1949 let VmgsEncryptionKey {
1950 nonce,
1951 reserved: _,
1952 authentication_tag,
1953 encryption_key,
1954 } = k;
1955
1956 state::SavedVmgsEncryptionKey {
1957 nonce,
1958 authentication_tag,
1959 encryption_key,
1960 }
1961 }),
1962 }
1963 }
1964 }
1965}
1966
1967#[cfg(test)]
1968mod tests {
1969 use super::*;
1970 use pal_async::async_test;
1971 #[cfg(with_encryption)]
1972 use parking_lot::Mutex;
1973 #[cfg(with_encryption)]
1974 use std::sync::Arc;
1975 #[cfg(with_encryption)]
1976 use vmgs_format::VMGS_ENCRYPTION_KEY_SIZE;
1977
1978 const ONE_MEGA_BYTE: u64 = 1024 * 1024;
1979
1980 #[cfg(with_encryption)]
1981 struct TestVmgsLogger {
1982 data: Arc<Mutex<String>>,
1983 }
1984
1985 #[cfg(with_encryption)]
1986 #[async_trait::async_trait]
1987 impl VmgsLogger for TestVmgsLogger {
1988 async fn log_event_fatal(&self, _event: VmgsLogEvent) {
1989 let mut data = self.data.lock();
1990 *data = "test logger".to_string();
1991 }
1992 }
1993
1994 fn new_test_file() -> Disk {
1995 disklayer_ram::ram_disk(4 * ONE_MEGA_BYTE, false).unwrap()
1996 }
1997
1998 #[async_test]
1999 async fn empty_vmgs() {
2000 let disk = new_test_file();
2001
2002 let result = Vmgs::open(disk, None).await;
2003 assert!(matches!(result, Err(Error::EmptyFile)));
2004 }
2005
2006 #[async_test]
2007 async fn format_empty_vmgs() {
2008 let disk = new_test_file();
2009 let result = Vmgs::format_new(disk, None).await;
2010 assert!(result.is_ok());
2011 }
2012
2013 #[async_test]
2014 async fn basic_read_write() {
2015 let disk = new_test_file();
2016 let mut vmgs = Vmgs::format_new(disk, None).await.unwrap();
2017 assert_eq!(vmgs.active_header_index, 0);
2018 assert_eq!(vmgs.active_header_sequence_number, 1);
2019 assert_eq!(vmgs.version, VMGS_VERSION_3_0);
2020
2021 let buf = b"hello world";
2023 vmgs.write_file(FileId::BIOS_NVRAM, buf).await.unwrap();
2024
2025 assert_eq!(vmgs.active_header_index, 1);
2026 assert_eq!(vmgs.active_header_sequence_number, 2);
2027
2028 let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2030
2031 assert_eq!(buf, &*read_buf);
2032 assert_eq!(vmgs.active_header_index, 1);
2033 assert_eq!(vmgs.active_header_sequence_number, 2);
2034 }
2035
2036 #[async_test]
2037 async fn basic_read_write_large() {
2038 let disk = new_test_file();
2039 let mut vmgs = Vmgs::format_new(disk, None).await.unwrap();
2040
2041 let buf: Vec<u8> = (0..).map(|x| x as u8).take(1024 * 4 + 1).collect();
2043
2044 vmgs.write_file(FileId::BIOS_NVRAM, &buf).await.unwrap();
2045
2046 assert_eq!(vmgs.active_header_index, 1);
2047 assert_eq!(vmgs.active_header_sequence_number, 2);
2048
2049 let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2051
2052 assert_eq!(buf, read_buf);
2053 assert_eq!(vmgs.active_header_index, 1);
2054 assert_eq!(vmgs.active_header_sequence_number, 2);
2055
2056 let buf: Vec<u8> = (0..).map(|x| x as u8).take(1024 * 4 * 4 + 1).collect();
2058
2059 vmgs.write_file(FileId::TPM_PPI, &buf).await.unwrap();
2060
2061 assert_eq!(vmgs.active_header_index, 0);
2062 assert_eq!(vmgs.active_header_sequence_number, 3);
2063
2064 let read_buf = vmgs.read_file(FileId::TPM_PPI).await.unwrap();
2066
2067 assert_eq!(buf, read_buf);
2068 assert_eq!(vmgs.active_header_index, 0);
2069 assert_eq!(vmgs.active_header_sequence_number, 3);
2070
2071 let buf: Vec<u8> = (0..).map(|x| x as u8).take(1024 * 4 * 4 * 4 + 1).collect();
2073
2074 vmgs.write_file(FileId::GUEST_FIRMWARE, &buf).await.unwrap();
2075
2076 assert_eq!(vmgs.active_header_index, 1);
2077 assert_eq!(vmgs.active_header_sequence_number, 4);
2078
2079 let read_buf = vmgs.read_file(FileId::GUEST_FIRMWARE).await.unwrap();
2081
2082 assert_eq!(buf, read_buf);
2083 assert_eq!(vmgs.active_header_index, 1);
2084 assert_eq!(vmgs.active_header_sequence_number, 4);
2085 }
2086
2087 #[async_test]
2088 async fn open_existing_file() {
2089 let buf_1 = b"hello world";
2090 let buf_2 = b"short sentence";
2091 let buf_3 = b"funny joke";
2092
2093 let disk = new_test_file();
2095 let mut vmgs = Vmgs::format_new(disk.clone(), None).await.unwrap();
2096
2097 vmgs.write_file(FileId::BIOS_NVRAM, buf_1).await.unwrap();
2098
2099 assert_eq!(vmgs.active_header_index, 1);
2100 assert_eq!(vmgs.active_header_sequence_number, 2);
2101 assert_eq!(vmgs.fcbs[&FileId(0)].block_offset, 4);
2102 assert_eq!(vmgs.fcbs[&FileId(1)].block_offset, 5);
2103
2104 vmgs.write_file(FileId::TPM_PPI, buf_2).await.unwrap();
2105
2106 assert_eq!(vmgs.active_header_index, 0);
2107 assert_eq!(vmgs.active_header_sequence_number, 3);
2108 assert_eq!(vmgs.fcbs[&FileId(0)].block_offset, 2);
2109 assert_eq!(vmgs.fcbs[&FileId(1)].block_offset, 5);
2110 assert_eq!(vmgs.fcbs[&FileId(2)].block_offset, 6);
2111
2112 vmgs.write_file(FileId::BIOS_NVRAM, buf_3).await.unwrap();
2113
2114 assert_eq!(vmgs.active_header_index, 1);
2115 assert_eq!(vmgs.active_header_sequence_number, 4);
2116 assert_eq!(vmgs.fcbs[&FileId(0)].block_offset, 4);
2117 assert_eq!(vmgs.fcbs[&FileId(1)].block_offset, 7);
2118 assert_eq!(vmgs.fcbs[&FileId(2)].block_offset, 6);
2119
2120 drop(vmgs);
2122
2123 let mut vmgs = Vmgs::open(disk, None).await.unwrap();
2124
2125 assert_eq!(vmgs.fcbs[&FileId(0)].block_offset, 4);
2126 assert_eq!(vmgs.fcbs[&FileId(1)].block_offset, 7);
2127 assert_eq!(vmgs.fcbs[&FileId(2)].block_offset, 6);
2128 let read_buf_1 = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2129
2130 assert_eq!(buf_3, &*read_buf_1);
2131 assert_eq!(vmgs.active_header_index, 1);
2132 assert_eq!(vmgs.active_header_sequence_number, 4);
2133
2134 let read_buf_2 = vmgs.read_file(FileId::TPM_PPI).await.unwrap();
2135
2136 assert_eq!(buf_2, &*read_buf_2);
2137 assert_eq!(vmgs.fcbs[&FileId(0)].block_offset, 4);
2138 assert_eq!(vmgs.fcbs[&FileId(1)].block_offset, 7);
2139 assert_eq!(vmgs.fcbs[&FileId(2)].block_offset, 6);
2140 }
2141
2142 #[async_test]
2143 async fn multiple_read_write() {
2144 let disk = new_test_file();
2145 let mut vmgs = Vmgs::format_new(disk, None).await.unwrap();
2146
2147 let buf_1 = b"Data data data";
2148 let buf_2 = b"password";
2149 let buf_3 = b"other data data";
2150
2151 vmgs.write_file(FileId::BIOS_NVRAM, buf_1).await.unwrap();
2152 let read_buf_1 = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2153 assert_eq!(buf_1, &*read_buf_1);
2154 assert_eq!(vmgs.active_header_index, 1);
2155 assert_eq!(vmgs.active_header_sequence_number, 2);
2156
2157 vmgs.write_file(FileId::TPM_PPI, buf_2).await.unwrap();
2158 let info = vmgs.get_file_info(FileId::TPM_PPI).unwrap();
2159 assert_eq!(info.valid_bytes as usize, buf_2.len());
2160 let read_buf_2 = vmgs.read_file(FileId::TPM_PPI).await.unwrap();
2161 assert_eq!(buf_2, &*read_buf_2);
2162 assert_eq!(vmgs.active_header_index, 0);
2163 assert_eq!(vmgs.active_header_sequence_number, 3);
2164
2165 vmgs.write_file(FileId::BIOS_NVRAM, buf_3).await.unwrap();
2166 let read_buf_3 = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2167 assert_eq!(buf_3, &*read_buf_3);
2168 assert_eq!(vmgs.active_header_index, 1);
2169 assert_eq!(vmgs.active_header_sequence_number, 4);
2170
2171 vmgs.write_file(FileId::BIOS_NVRAM, buf_1).await.unwrap();
2172 let read_buf_1 = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2173 assert_eq!(buf_1, &*read_buf_1);
2174 assert_eq!(vmgs.active_header_index, 0);
2175 assert_eq!(vmgs.active_header_sequence_number, 5);
2176
2177 vmgs.write_file(FileId::TPM_PPI, buf_2).await.unwrap();
2178 let read_buf_2 = vmgs.read_file(FileId::TPM_PPI).await.unwrap();
2179 assert_eq!(buf_2, &*read_buf_2);
2180 assert_eq!(vmgs.active_header_index, 1);
2181 assert_eq!(vmgs.active_header_sequence_number, 6);
2182
2183 vmgs.write_file(FileId::BIOS_NVRAM, buf_3).await.unwrap();
2184 let read_buf_3 = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2185 assert_eq!(buf_3, &*read_buf_3);
2186 assert_eq!(vmgs.active_header_index, 0);
2187 assert_eq!(vmgs.active_header_sequence_number, 7);
2188 }
2189
2190 #[async_test]
2191 async fn test_insufficient_resources() {
2192 let disk = new_test_file();
2193 let mut vmgs = Vmgs::format_new(disk, None).await.unwrap();
2194
2195 let buf: Vec<u8> = vec![1; ONE_MEGA_BYTE as usize * 5];
2196 let result = vmgs.write_file(FileId::BIOS_NVRAM, &buf).await;
2197 assert!(result.is_err());
2198 if let Err(e) = result {
2199 match e {
2200 Error::InsufficientResources => (),
2201 _ => panic!("Wrong error returned"),
2202 }
2203 } else {
2204 panic!("Should have returned Insufficient resources error");
2205 }
2206 }
2207
2208 #[async_test]
2209 async fn test_empty_write() {
2210 let disk = new_test_file();
2211 let mut vmgs = Vmgs::format_new(disk, None).await.unwrap();
2212
2213 let buf: Vec<u8> = Vec::new();
2214 vmgs.write_file(FileId::BIOS_NVRAM, &buf).await.unwrap();
2215
2216 let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2218
2219 assert_eq!(buf, read_buf);
2220 assert_eq!(read_buf.len(), 0);
2221 assert_eq!(vmgs.active_header_index, 1);
2222 assert_eq!(vmgs.active_header_sequence_number, 2);
2223 }
2224
2225 #[test]
2227 fn test_block_count_to_byte_count() {
2228 let block_count = 10;
2229 let byte_count = block_count_to_byte_count(block_count);
2230 assert!(byte_count == block_count as u64 * VMGS_BYTES_PER_BLOCK as u64);
2231 }
2232
2233 #[test]
2234 fn test_validate_header() {
2235 let mut header = VmgsHeader::new_zeroed();
2236 header.signature = VMGS_SIGNATURE;
2237 header.version = VMGS_VERSION_3_0;
2238 header.header_size = size_of::<VmgsHeader>() as u32;
2239 header.file_table_offset = VMGS_MIN_FILE_BLOCK_OFFSET;
2240 header.file_table_size = VMGS_FILE_TABLE_BLOCK_SIZE;
2241 header.checksum = compute_crc32(header.as_bytes());
2242
2243 let result = validate_header(&header);
2244 assert!(result.is_ok());
2245
2246 let mut header_signature = header;
2247 header_signature.signature = 0;
2248 header_signature.checksum = 0;
2249 header_signature.checksum = compute_crc32(header_signature.as_bytes());
2250 let result = validate_header(&header_signature);
2251 match result {
2252 Err(Error::InvalidFormat(err)) => assert_eq!(err, "Invalid header signature"),
2253 _ => panic!(),
2254 };
2255
2256 let mut header_version = header;
2257 header_version.version = 0;
2258 header_version.checksum = 0;
2259 header_version.checksum = compute_crc32(header_version.as_bytes());
2260 match validate_header(&header_version) {
2261 Err(Error::InvalidFormat(err)) => assert_eq!(err, "Invalid header version"),
2262 _ => panic!(),
2263 };
2264
2265 let mut header_header_size = header;
2266 header_header_size.header_size = 0;
2267 header_header_size.checksum = 0;
2268 header_header_size.checksum = compute_crc32(header_header_size.as_bytes());
2269 match validate_header(&header_header_size) {
2270 Err(Error::InvalidFormat(err)) => assert_eq!(err, "Invalid header size"),
2271 _ => panic!(),
2272 };
2273
2274 let mut header_ft_offset = header;
2275 header_ft_offset.file_table_offset = 0;
2276 header_ft_offset.checksum = 0;
2277 header_ft_offset.checksum = compute_crc32(header_ft_offset.as_bytes());
2278 match validate_header(&header_ft_offset) {
2279 Err(Error::InvalidFormat(err)) => assert_eq!(err, "Invalid file table offset"),
2280 _ => panic!(),
2281 };
2282
2283 let mut header_ft_size = header;
2284 header_ft_size.file_table_size = 0;
2285 header_ft_size.checksum = 0;
2286 header_ft_size.checksum = compute_crc32(header_ft_size.as_bytes());
2287 match validate_header(&header_ft_size) {
2288 Err(Error::InvalidFormat(err)) => assert_eq!(err, "Invalid file table size"),
2289 _ => panic!(),
2290 };
2291 }
2292
2293 #[test]
2294 fn test_initialize_file_metadata() {
2295 let mut file_table = VmgsFileTable::new_zeroed();
2296
2297 file_table.entries[0].offset = 6;
2298 file_table.entries[0].allocation_size = 1;
2299 file_table.entries[1].offset = 2;
2300 file_table.entries[1].allocation_size = 1;
2301 file_table.entries[2].offset = 4;
2302 file_table.entries[2].allocation_size = 5;
2303 file_table.entries[3].offset = 3;
2304 file_table.entries[3].allocation_size = 3;
2305
2306 let block_capacity = 1000;
2307
2308 let fcbs = initialize_file_metadata(&file_table, VMGS_VERSION_3_0, block_capacity).unwrap();
2309 assert!(fcbs[&FileId(0)].block_offset == 6);
2311 assert!(fcbs[&FileId(0)].allocated_blocks.get() == 1);
2312 assert!(fcbs[&FileId(1)].block_offset == 2);
2313 assert!(fcbs[&FileId(1)].allocated_blocks.get() == 1);
2314 assert!(fcbs[&FileId(2)].block_offset == 4);
2315 assert!(fcbs[&FileId(2)].allocated_blocks.get() == 5);
2316 assert!(fcbs[&FileId(3)].block_offset == 3);
2317 assert!(fcbs[&FileId(3)].allocated_blocks.get() == 3);
2318 }
2319
2320 #[test]
2321 fn test_round_up_count() {
2322 assert!(round_up_count(0, 4096) == 0);
2323 assert!(round_up_count(1, 4096) == 4096);
2324 assert!(round_up_count(4095, 4096) == 4096);
2325 assert!(round_up_count(4096, 4096) == 4096);
2326 assert!(round_up_count(4097, 4096) == 8192);
2327 }
2328
2329 #[async_test]
2330 async fn test_header_sequence_overflow() {
2331 let disk = new_test_file();
2332 let mut vmgs = Vmgs::format_new(disk, None).await.unwrap();
2333
2334 vmgs.active_header_sequence_number = u32::MAX;
2335
2336 let buf = b"hello world";
2338 vmgs.write_file(FileId::BIOS_NVRAM, buf).await.unwrap();
2339
2340 assert_eq!(vmgs.active_header_index, 1);
2341 assert_eq!(vmgs.active_header_sequence_number, 0);
2342
2343 vmgs.set_active_header(0, u32::MAX);
2344
2345 let mut new_header = VmgsHeader::new_zeroed();
2346 vmgs.update_header(&mut new_header).await.unwrap();
2347
2348 assert_eq!(vmgs.active_header_index, 1);
2349 assert_eq!(vmgs.active_header_sequence_number, 0);
2350 assert_eq!(new_header.sequence, 0);
2351 }
2352
2353 #[cfg(with_encryption)]
2354 #[async_test]
2355 async fn write_file_v3() {
2356 let disk = new_test_file();
2357 let mut vmgs = Vmgs::format_new(disk.clone(), None).await.unwrap();
2358 let encryption_key = [12; VMGS_ENCRYPTION_KEY_SIZE];
2359
2360 let buf = b"hello world";
2362 let buf_1 = b"hello universe";
2363 vmgs.write_file(FileId::BIOS_NVRAM, buf).await.unwrap();
2364 vmgs.add_new_encryption_key(&encryption_key, EncryptionAlgorithm::AES_GCM)
2365 .await
2366 .unwrap();
2367 vmgs.write_file_encrypted(FileId::TPM_PPI, buf_1)
2368 .await
2369 .unwrap();
2370
2371 let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2373 assert_eq!(buf, &*read_buf);
2374 let info = vmgs.get_file_info(FileId::TPM_PPI).unwrap();
2375 assert_eq!(info.valid_bytes as usize, buf_1.len());
2376 let read_buf = vmgs.read_file(FileId::TPM_PPI).await.unwrap();
2377 assert_eq!(buf_1, &*read_buf);
2378
2379 drop(vmgs);
2381 let mut vmgs = Vmgs::open(disk, None).await.unwrap();
2382 let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2383 assert_eq!(buf, read_buf.as_bytes());
2384 let info = vmgs.get_file_info(FileId::TPM_PPI).unwrap();
2385 assert_eq!(info.valid_bytes as usize, buf_1.len());
2386 let read_buf = vmgs.read_file(FileId::TPM_PPI).await.unwrap();
2387 assert_ne!(buf_1, read_buf.as_bytes());
2388
2389 vmgs.unlock_with_encryption_key(&encryption_key)
2391 .await
2392 .unwrap();
2393 let info = vmgs.get_file_info(FileId::TPM_PPI).unwrap();
2394 assert_eq!(info.valid_bytes as usize, buf_1.len());
2395 let read_buf = vmgs.read_file(FileId::TPM_PPI).await.unwrap();
2396 assert_eq!(buf_1, &*read_buf);
2397 }
2398
2399 #[cfg(with_encryption)]
2400 #[async_test]
2401 async fn overwrite_file_v3() {
2402 let disk = new_test_file();
2403 let mut vmgs = Vmgs::format_new(disk, None).await.unwrap();
2404 let encryption_key = [1; VMGS_ENCRYPTION_KEY_SIZE];
2405 let buf = vec![1; 8 * 1024];
2406 let buf_1 = vec![2; 8 * 1024];
2407
2408 let key_index = vmgs
2410 .add_new_encryption_key(&encryption_key, EncryptionAlgorithm::AES_GCM)
2411 .await
2412 .unwrap();
2413 assert_eq!(key_index, 0);
2414
2415 vmgs.write_file_encrypted(FileId::BIOS_NVRAM, &buf)
2417 .await
2418 .unwrap();
2419
2420 vmgs.write_file_encrypted(FileId::BIOS_NVRAM, &buf_1)
2422 .await
2423 .unwrap();
2424
2425 let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2427 assert_eq!(buf_1, read_buf);
2428 }
2429
2430 #[cfg(with_encryption)]
2431 #[async_test]
2432 async fn file_encryption() {
2433 let buf: Vec<u8> = (0..255).collect();
2434 let encryption_key = [1; VMGS_ENCRYPTION_KEY_SIZE];
2435
2436 let disk = new_test_file();
2437 let mut vmgs = Vmgs::format_new(disk.clone(), None).await.unwrap();
2438
2439 let key_index = vmgs
2441 .add_new_encryption_key(&encryption_key, EncryptionAlgorithm::AES_GCM)
2442 .await
2443 .unwrap();
2444 assert_eq!(key_index, 0);
2445
2446 vmgs.write_file_encrypted(FileId::BIOS_NVRAM, &buf)
2448 .await
2449 .unwrap();
2450
2451 let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2453 assert_eq!(buf, read_buf);
2454
2455 drop(vmgs);
2456
2457 let mut vmgs = Vmgs::open(disk, None).await.unwrap();
2459
2460 let info = vmgs.get_file_info(FileId::BIOS_NVRAM).unwrap();
2461 assert_eq!(info.valid_bytes as usize, buf.len());
2462
2463 let key_index = vmgs
2466 .unlock_with_encryption_key(&encryption_key)
2467 .await
2468 .unwrap();
2469
2470 assert_eq!(key_index, 0);
2471
2472 let new_encryption_key = [2; VMGS_ENCRYPTION_KEY_SIZE];
2474 let key_index = vmgs
2475 .add_new_encryption_key(&new_encryption_key, EncryptionAlgorithm::AES_GCM)
2476 .await
2477 .unwrap();
2478 assert_eq!(key_index, 1);
2479 vmgs.remove_encryption_key(0).await.unwrap();
2480
2481 let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2482 assert_eq!(buf, read_buf);
2483 }
2484
2485 #[cfg(with_encryption)]
2486 #[async_test]
2487 async fn add_new_encryption_key() {
2488 let buf: Vec<u8> = (0..255).collect();
2489 let encryption_key = [1; VMGS_ENCRYPTION_KEY_SIZE];
2490 let new_encryption_key = [5; VMGS_ENCRYPTION_KEY_SIZE];
2491
2492 let disk = new_test_file();
2494 let mut vmgs = Vmgs::format_new(disk.clone(), None).await.unwrap();
2495
2496 let key_index = vmgs
2498 .add_new_encryption_key(&encryption_key, EncryptionAlgorithm::AES_GCM)
2499 .await
2500 .unwrap();
2501 assert_eq!(key_index, 0);
2502
2503 vmgs.write_file_encrypted(FileId::BIOS_NVRAM, &buf)
2505 .await
2506 .unwrap();
2507
2508 drop(vmgs);
2510 let mut vmgs = Vmgs::open(disk.clone(), None).await.unwrap();
2511 let key_index = vmgs
2512 .unlock_with_encryption_key(&encryption_key)
2513 .await
2514 .unwrap();
2515 assert_eq!(key_index, 0);
2516 let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2517 assert_eq!(read_buf, buf);
2518
2519 let key_index = vmgs
2521 .add_new_encryption_key(&new_encryption_key, EncryptionAlgorithm::AES_GCM)
2522 .await
2523 .unwrap();
2524 assert_eq!(key_index, 1);
2525
2526 drop(vmgs);
2528 let mut vmgs = Vmgs::open(disk, None).await.unwrap();
2529 let key_index = vmgs
2530 .unlock_with_encryption_key(&encryption_key)
2531 .await
2532 .unwrap();
2533 assert_eq!(key_index, 0);
2534 let key_index = vmgs
2535 .unlock_with_encryption_key(&new_encryption_key)
2536 .await
2537 .unwrap();
2538 assert_eq!(key_index, 1);
2539 let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2540 assert_eq!(read_buf, buf);
2541
2542 vmgs.remove_encryption_key(key_index).await.unwrap();
2544 let key_index = vmgs
2545 .add_new_encryption_key(&new_encryption_key, EncryptionAlgorithm::AES_GCM)
2546 .await
2547 .unwrap();
2548 assert_eq!(key_index, 1);
2549
2550 vmgs.remove_encryption_key(0).await.unwrap();
2552 let result = vmgs.unlock_with_encryption_key(&encryption_key).await;
2553 assert!(matches!(result, Err(Error::Other(_))));
2554
2555 let result = vmgs.remove_encryption_key(0).await;
2557 assert!(matches!(result, Err(Error::Other(_))));
2558
2559 vmgs.remove_encryption_key(1).await.unwrap();
2561 let read_buf = vmgs.read_file_raw(FileId::BIOS_NVRAM).await;
2562 assert_ne!(read_buf.unwrap(), buf);
2563 }
2564
2565 #[cfg(with_encryption)]
2566 #[async_test]
2567 async fn test_write_file_encrypted() {
2568 let disk = new_test_file();
2572 let mut vmgs = Vmgs::format_new(disk.clone(), None).await.unwrap();
2573 let buf = b"This is plaintext";
2574
2575 vmgs.write_file_encrypted(FileId::BIOS_NVRAM, buf)
2577 .await
2578 .unwrap();
2579
2580 let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2582 assert_eq!(vmgs.encryption_algorithm, EncryptionAlgorithm::NONE);
2583 assert_eq!(buf, &*read_buf);
2584
2585 drop(vmgs);
2588 let mut vmgs = Vmgs::open(disk, None).await.unwrap();
2589
2590 let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2591 assert_eq!(vmgs.encryption_algorithm, EncryptionAlgorithm::NONE);
2592 assert_eq!(buf, &*read_buf);
2593 }
2594
2595 #[cfg(with_encryption)]
2596 #[async_test]
2597 async fn test_logger() {
2598 let disk = new_test_file();
2599 let data = Arc::new(Mutex::new(String::new()));
2600 let mut vmgs = Vmgs::format_new(
2601 disk.clone(),
2602 Some(Arc::new(TestVmgsLogger { data: data.clone() })),
2603 )
2604 .await
2605 .unwrap();
2606 let encryption_key = [12; VMGS_ENCRYPTION_KEY_SIZE];
2607
2608 let buf = b"hello world";
2610 vmgs.add_new_encryption_key(&encryption_key, EncryptionAlgorithm::AES_GCM)
2611 .await
2612 .unwrap();
2613 vmgs.write_file_encrypted(FileId::BIOS_NVRAM, buf)
2614 .await
2615 .unwrap();
2616
2617 let fcb = vmgs.fcbs.get_mut(&FileId::BIOS_NVRAM).unwrap();
2618
2619 fcb.nonce[0] ^= 1;
2621
2622 let result = vmgs.read_file(FileId::BIOS_NVRAM).await;
2624 assert!(result.is_err());
2625
2626 let result = data.lock();
2628 assert_eq!(*result, "test logger");
2629 }
2630}