1use crate::error::Error;
5use crate::logger::VmgsLogEvent;
6use crate::logger::VmgsLogger;
7use crate::storage::VmgsStorage;
8use cvm_tracing::CVM_ALLOWED;
9use disk_backend::Disk;
10#[cfg(feature = "inspect")]
11use inspect::Inspect;
12#[cfg(feature = "inspect")]
13use inspect_counters::Counter;
14use std::collections::BTreeMap;
15use std::collections::HashMap;
16use std::num::NonZeroU32;
17use std::sync::Arc;
18use vmgs_format::EncryptionAlgorithm;
19use vmgs_format::FileAttribute;
20use vmgs_format::FileId;
21use vmgs_format::VMGS_BYTES_PER_BLOCK;
22use vmgs_format::VMGS_ENCRYPTION_KEY_SIZE;
23use vmgs_format::VMGS_FILE_TABLE_BLOCK_SIZE;
24use vmgs_format::VMGS_MIN_FILE_BLOCK_OFFSET;
25use vmgs_format::VMGS_NONCE_SIZE;
26use vmgs_format::VMGS_SIGNATURE;
27use vmgs_format::VMGS_VERSION_3_0;
28use vmgs_format::VmgsAuthTag;
29use vmgs_format::VmgsDatastoreKey;
30use vmgs_format::VmgsEncryptionKey;
31use vmgs_format::VmgsExtendedFileEntry;
32use vmgs_format::VmgsExtendedFileTable;
33use vmgs_format::VmgsFileEntry;
34use vmgs_format::VmgsFileTable;
35use vmgs_format::VmgsHeader;
36use vmgs_format::VmgsMarkers;
37use vmgs_format::VmgsNonce;
38use vmgs_format::VmgsProvisioningMarker;
39use vmgs_format::VmgsProvisioningReason;
40use zerocopy::FromBytes;
41use zerocopy::FromZeros;
42use zerocopy::IntoBytes;
43
44#[derive(Debug)]
46enum LogOpType {
47 VmgsProvision,
48}
49
50#[derive(Debug)]
52#[cfg_attr(feature = "mesh", derive(mesh_protobuf::Protobuf))]
53pub struct VmgsFileInfo {
54 pub allocated_bytes: u64,
56 pub valid_bytes: u64,
58 pub encrypted: bool,
60}
61
62#[derive(Debug, Clone, Copy)]
64pub enum GspType {
65 None,
67 GspById,
69 GspKey,
71}
72
73#[derive(Clone, PartialEq, Eq, Debug)]
76#[cfg_attr(feature = "inspect", derive(Inspect))]
77struct ResolvedFileControlBlock {
78 block_offset: u32,
81 #[cfg_attr(feature = "inspect", inspect(with = "|x| x.get()"))]
82 allocated_blocks: NonZeroU32,
83 valid_bytes: u64,
84
85 nonce: VmgsNonce,
86 authentication_tag: VmgsAuthTag,
87
88 attributes: FileAttribute,
91 encryption_key: VmgsDatastoreKey,
92}
93
94impl ResolvedFileControlBlock {
95 fn new(block_offset: u32, block_count: u32, valid_bytes: usize, encrypt: bool) -> Self {
96 let (attributes, encryption_key, nonce) = if encrypt {
97 (
98 FileAttribute::new()
99 .with_encrypted(true)
100 .with_authenticated(true),
101 {
102 let mut encryption_key = VmgsDatastoreKey::new_zeroed();
103 getrandom::fill(&mut encryption_key).expect("rng failure");
104 encryption_key
105 },
106 generate_nonce(),
107 )
108 } else {
109 (
110 FileAttribute::new(),
111 VmgsDatastoreKey::new_zeroed(),
112 VmgsNonce::new_zeroed(),
113 )
114 };
115
116 ResolvedFileControlBlock {
117 block_offset,
118 allocated_blocks: NonZeroU32::new(block_count).unwrap(),
119 valid_bytes: valid_bytes as u64,
120
121 nonce,
122 authentication_tag: VmgsAuthTag::new_zeroed(),
123
124 attributes,
125 encryption_key,
126 }
127 }
128
129 fn file_info(&self) -> VmgsFileInfo {
130 VmgsFileInfo {
131 allocated_bytes: block_count_to_byte_count(self.allocated_blocks.get()),
132 valid_bytes: self.valid_bytes,
133 encrypted: self.encrypted(),
134 }
135 }
136
137 fn encrypted(&self) -> bool {
138 self.attributes.encrypted() || self.attributes.authenticated()
139 }
140
141 fn fill_file_entry(&self, version: u32, file_entry: &mut VmgsFileEntry) {
142 file_entry.offset = self.block_offset;
143 file_entry.allocation_size = self.allocated_blocks.get();
144 file_entry.valid_data_size = self.valid_bytes;
145
146 if version >= VMGS_VERSION_3_0 {
147 file_entry.nonce.copy_from_slice(&self.nonce);
148 file_entry
149 .authentication_tag
150 .copy_from_slice(&self.authentication_tag);
151 file_entry.attributes = self.attributes;
152 }
153 }
154
155 fn fill_extended_file_entry(&self, extended_file_entry: &mut VmgsExtendedFileEntry) {
156 extended_file_entry.attributes = self.attributes;
157 extended_file_entry
158 .encryption_key
159 .copy_from_slice(&self.encryption_key);
160 }
161
162 fn from_file_entry(version: u32, file_entry: &VmgsFileEntry) -> Self {
163 let (nonce, authentication_tag, attributes) = if version >= VMGS_VERSION_3_0 {
164 (
165 file_entry.nonce,
166 file_entry.authentication_tag,
167 file_entry.attributes,
168 )
169 } else {
170 Default::default()
171 };
172
173 ResolvedFileControlBlock {
174 block_offset: file_entry.offset,
175 allocated_blocks: NonZeroU32::new(file_entry.allocation_size).unwrap(),
176 valid_bytes: file_entry.valid_data_size,
177
178 nonce,
179 authentication_tag,
180
181 attributes,
182 encryption_key: VmgsDatastoreKey::new_zeroed(),
183 }
184 }
185
186 #[cfg_attr(not(feature = "encryption"), expect(dead_code))]
187 fn update_extended_data(&mut self, extended_file_entry: &VmgsExtendedFileEntry) {
188 self.attributes = extended_file_entry.attributes;
189 self.encryption_key = extended_file_entry.encryption_key;
190 }
191
192 #[cfg_attr(not(feature = "encryption"), expect(unused_variables))]
193 fn encrypt(&mut self, data: &[u8]) -> Result<Vec<u8>, Error> {
194 #[cfg(not(feature = "encryption"))]
195 unreachable!("Encryption requires the encryption feature");
196 #[cfg(feature = "encryption")]
197 {
198 let encrypted = crate::encrypt::vmgs_encrypt(
199 &self.encryption_key,
200 &self.nonce,
201 data,
202 &mut self.authentication_tag,
203 )?;
204
205 if encrypted.len() as u64 != self.valid_bytes {
206 return Err(Error::UnexpectedLength(
207 "encrypted data",
208 self.valid_bytes as usize,
209 encrypted.len(),
210 ));
211 }
212
213 Ok(encrypted)
214 }
215 }
216
217 #[cfg_attr(not(feature = "encryption"), expect(unused_variables))]
218 fn decrypt(&self, data: &[u8]) -> Result<Vec<u8>, Error> {
219 #[cfg(not(feature = "encryption"))]
220 unreachable!("Encryption requires the encryption feature");
221 #[cfg(feature = "encryption")]
222 {
223 if data.iter().all(|x| *x == 0) {
227 return Err(Error::InvalidFormat("encrypted data is all-zeros".into()));
228 }
229
230 let decrypted = crate::encrypt::vmgs_decrypt(
231 &self.encryption_key,
232 &self.nonce,
233 data,
234 &self.authentication_tag,
235 )?;
236
237 if decrypted.len() as u64 != self.valid_bytes {
238 return Err(Error::UnexpectedLength(
239 "decrypted data",
240 self.valid_bytes as usize,
241 decrypted.len(),
242 ));
243 }
244
245 Ok(decrypted)
246 }
247 }
248
249 fn clear_encryption(&mut self) {
250 self.nonce.zero();
251 self.authentication_tag.zero();
252 self.encryption_key.zero();
253 }
254}
255
256enum RefOrOwned<'a> {
257 Ref(&'a [u8]),
258 Owned(Vec<u8>),
259}
260
261impl<'a> RefOrOwned<'a> {
262 fn placeholder<T>() -> Self {
263 RefOrOwned::Owned(vec![0; size_of::<T>()])
264 }
265
266 fn len(&self) -> usize {
267 match self {
268 RefOrOwned::Ref(x) => x.len(),
269 RefOrOwned::Owned(x) => x.len(),
270 }
271 }
272
273 fn copy_from_slice(&mut self, src: &[u8]) {
274 match self {
275 RefOrOwned::Ref(_) => panic!("cannot modify ref"),
276 RefOrOwned::Owned(x) => x.copy_from_slice(src),
277 }
278 }
279
280 fn get(&self) -> &[u8] {
281 match self {
282 RefOrOwned::Ref(x) => x,
283 RefOrOwned::Owned(x) => x,
284 }
285 }
286
287 fn replace(&mut self, new_value: Self) {
288 assert_eq!(self.len(), new_value.len());
289 *self = new_value;
290 }
291}
292
293struct AllocRequest<'a> {
294 data: RefOrOwned<'a>,
295 encrypt: bool,
296}
297
298impl<'a> AllocRequest<'a> {
299 fn new(data: RefOrOwned<'a>, encrypt: bool) -> Self {
300 Self { data, encrypt }
301 }
302
303 fn allocate(
304 self,
305 allocation_list: &mut Vec<AllocationBlock>,
306 block_capacity: u32,
307 ) -> Result<AllocResult<'a>, Error> {
308 let valid_bytes = self.data.len();
309
310 let mut block_count = (round_up_count(valid_bytes, VMGS_BYTES_PER_BLOCK)
311 / VMGS_BYTES_PER_BLOCK as u64) as u32;
312 if block_count == 0 {
314 block_count = 1;
315 }
316 if block_count as u64 > vmgs_format::VMGS_MAX_FILE_SIZE_BLOCKS {
317 return Err(Error::WriteFileBlocks);
318 }
319
320 let block_offset = allocate_helper(allocation_list, block_count, block_capacity)?;
321
322 let fcb =
323 ResolvedFileControlBlock::new(block_offset, block_count, valid_bytes, self.encrypt);
324
325 Ok(AllocResult {
326 fcb,
327 data: self.data,
328 })
329 }
330}
331
332struct AllocResult<'a> {
333 fcb: ResolvedFileControlBlock,
334 data: RefOrOwned<'a>,
335}
336
337impl<'a> AllocResult<'a> {
338 fn encrypt(&mut self) -> Result<(), Error> {
339 self.data
340 .replace(RefOrOwned::Owned(self.fcb.encrypt(self.data.get())?));
341 Ok(())
342 }
343
344 fn encrypt_from(&mut self, data: &[u8]) -> Result<(), Error> {
345 self.data
346 .replace(RefOrOwned::Owned(self.fcb.encrypt(data)?));
347 Ok(())
348 }
349}
350
351#[cfg_attr(feature = "inspect", derive(Inspect))]
354pub struct Vmgs {
355 storage: VmgsStorage,
356
357 #[cfg(feature = "inspect")]
358 stats: vmgs_inspect::VmgsStats,
359
360 state: VmgsState,
361
362 #[cfg_attr(feature = "inspect", inspect(skip))]
363 logger: Option<Arc<dyn VmgsLogger>>,
364}
365
366#[cfg_attr(feature = "inspect", derive(Inspect))]
367#[derive(Clone)]
368struct VmgsState {
369 active_header_index: usize,
370 active_header_sequence_number: u32,
371 version: u32,
372 #[cfg_attr(feature = "inspect", inspect(with = "vmgs_inspect::fcbs"))]
373 fcbs: HashMap<FileId, ResolvedFileControlBlock>,
374 encryption_algorithm: EncryptionAlgorithm,
375 datastore_key_count: u8,
376 active_datastore_key_index: Option<usize>,
377 #[cfg_attr(feature = "inspect", inspect(iter_by_index))]
378 datastore_keys: [VmgsDatastoreKey; 2],
379 unused_metadata_key: VmgsDatastoreKey,
381 #[cfg_attr(feature = "inspect", inspect(iter_by_index))]
382 encrypted_metadata_keys: [VmgsEncryptionKey; 2],
383 reprovisioned: bool,
384 provisioning_reason: Option<VmgsProvisioningReason>,
385}
386
387#[cfg(feature = "inspect")]
388mod vmgs_inspect {
389 use super::*;
390
391 #[derive(Default)]
392 pub struct IoStat {
393 pub attempt: Counter,
394 pub resolved: Counter,
395 }
396
397 impl Inspect for IoStat {
400 fn inspect(&self, req: inspect::Request<'_>) {
401 let mut resp = req.respond();
402 resp.counter("ok", self.resolved.get())
403 .counter("err", self.attempt.get() - self.resolved.get());
404 }
405 }
406
407 #[derive(Inspect, Default)]
408 pub struct VmgsStats {
409 #[inspect(with = "stat_map")]
410 pub read: HashMap<FileId, IoStat>,
411 #[inspect(with = "stat_map")]
412 pub write: HashMap<FileId, IoStat>,
413 }
414
415 pub(super) fn fcbs(fcbs: &HashMap<FileId, ResolvedFileControlBlock>) -> impl Inspect + '_ {
416 inspect::adhoc(|req| {
417 let mut res = req.respond();
418 for (id, fcb) in fcbs.iter() {
419 res.field(&format!("{}-{:?}", id.0, id), fcb);
420 }
421 })
422 }
423
424 pub fn stat_map(map: &HashMap<FileId, IoStat>) -> impl Inspect + '_ {
425 inspect::iter_by_key(map).map_key(|x| format!("{:?}", x))
426 }
427}
428
429impl Vmgs {
430 pub async fn try_open(
433 disk: Disk,
434 logger: Option<Arc<dyn VmgsLogger>>,
435 format_on_empty: bool,
436 format_on_failure: bool,
437 ) -> Result<Self, Error> {
438 match Self::open(disk.clone(), logger.clone()).await {
439 Ok(vmgs) => Ok(vmgs),
440 Err(Error::EmptyFile) if format_on_empty => {
441 tracing::info!(CVM_ALLOWED, "empty vmgs file, formatting");
442 Self::format_new_with_reason(disk, VmgsProvisioningReason::Empty, logger).await
443 }
444 Err(err) if format_on_failure => {
445 tracing::warn!(CVM_ALLOWED, ?err, "vmgs initialization error, reformatting");
446 Self::format_new_with_reason(disk, VmgsProvisioningReason::Failure, logger).await
447 }
448 Err(err) => {
449 let event_log_id = match err {
450 Error::InvalidFormat(_) => VmgsLogEvent::InvalidFormat,
452 Error::CorruptFormat(_) => VmgsLogEvent::CorruptFormat,
454 _ => VmgsLogEvent::InitFailed,
456 };
457
458 logger.log_event_fatal(event_log_id).await;
459 Err(err)
460 }
461 }
462 }
463
464 pub async fn open(disk: Disk, logger: Option<Arc<dyn VmgsLogger>>) -> Result<Self, Error> {
466 tracing::debug!(CVM_ALLOWED, "opening VMGS datastore");
467 let storage = VmgsStorage::new_validated(disk).map_err(Error::Initialization)?;
468 Self::open_inner(storage, logger).await
469 }
470
471 pub async fn format_new(
473 disk: Disk,
474 logger: Option<Arc<dyn VmgsLogger>>,
475 ) -> Result<Self, Error> {
476 Self::format_new_with_reason(disk, VmgsProvisioningReason::Request, logger).await
477 }
478
479 pub async fn format_new_with_reason(
481 disk: Disk,
482 reason: VmgsProvisioningReason,
483 logger: Option<Arc<dyn VmgsLogger>>,
484 ) -> Result<Self, Error> {
485 tracing::info!(
486 CVM_ALLOWED,
487 op_type = ?LogOpType::VmgsProvision,
488 ?reason,
489 "formatting and initializing VMGS datastore"
490 );
491 let storage = VmgsStorage::new_validated(disk).map_err(Error::Initialization)?;
492 Self::format_new_inner(storage, VMGS_VERSION_3_0, reason, logger).await
493 }
494
495 pub async fn request_format(
497 disk: Disk,
498 logger: Option<Arc<dyn VmgsLogger>>,
499 ) -> Result<Self, Error> {
500 let mut storage = VmgsStorage::new_validated(disk).map_err(Error::Initialization)?;
501
502 match Self::open_header(&mut storage).await {
503 Ok((active_header, active_header_index)) if active_header.markers.reprovisioned() => {
504 tracing::info!(CVM_ALLOWED, "reprovisioned marker found, skipping format");
505 Self::finish_open(storage, active_header, active_header_index, logger).await
506 }
507 _ => {
508 tracing::info!(CVM_ALLOWED, "formatting vmgs file on request");
509 let mut vmgs = Vmgs::format_new_inner(
510 storage,
511 VMGS_VERSION_3_0,
512 VmgsProvisioningReason::Request,
513 logger,
514 )
515 .await?;
516
517 vmgs.set_reprovisioned(true).await?;
520
521 Ok(vmgs)
522 }
523 }
524 }
525
526 async fn open_inner(
527 mut storage: VmgsStorage,
528 logger: Option<Arc<dyn VmgsLogger>>,
529 ) -> Result<Self, Error> {
530 let (active_header, active_header_index) = Self::open_header(&mut storage).await?;
531
532 let mut vmgs =
533 Self::finish_open(storage, active_header, active_header_index, logger).await?;
534
535 vmgs.set_reprovisioned(false).await?;
538
539 Ok(vmgs)
540 }
541
542 async fn open_header(storage: &mut VmgsStorage) -> Result<(VmgsHeader, usize), Error> {
543 let (header_1, header_2) = read_headers_inner(storage).await.map_err(|(e, _)| e)?;
544
545 let active_header_index =
546 get_active_header(validate_header(&header_1), validate_header(&header_2))?;
547
548 let active_header = if active_header_index == 0 {
549 header_1
550 } else {
551 header_2
552 };
553
554 Ok((active_header, active_header_index))
555 }
556
557 async fn finish_open(
558 storage: VmgsStorage,
559 active_header: VmgsHeader,
560 active_header_index: usize,
561 logger: Option<Arc<dyn VmgsLogger>>,
562 ) -> Result<Vmgs, Error> {
563 let mut vmgs = Self {
564 storage,
565
566 state: VmgsState::from_header(active_header, active_header_index),
567
568 #[cfg(feature = "inspect")]
569 stats: Default::default(),
570
571 logger,
572 };
573
574 let file_table_buffer = vmgs
575 .read_file_internal(FileId::FILE_TABLE, false, None)
576 .await?;
577 vmgs.state.fcbs = initialize_file_metadata(
578 VmgsFileTable::ref_from_bytes(&file_table_buffer)
579 .map_err(|_| Error::InvalidFormat("incorrect file table size".into()))?,
580 vmgs.state.version,
581 vmgs.storage.block_capacity(),
582 )?;
583
584 Ok(vmgs)
585 }
586
587 fn new(
588 storage: VmgsStorage,
589 version: u32,
590 reason: VmgsProvisioningReason,
591 logger: Option<Arc<dyn VmgsLogger>>,
592 ) -> Vmgs {
593 Self {
594 storage,
595
596 state: VmgsState::new(version, Some(reason)),
597
598 #[cfg(feature = "inspect")]
599 stats: Default::default(),
600
601 logger,
602 }
603 }
604
605 async fn format_new_inner(
607 storage: VmgsStorage,
608 version: u32,
609 reason: VmgsProvisioningReason,
610 logger: Option<Arc<dyn VmgsLogger>>,
611 ) -> Result<Vmgs, Error> {
612 tracing::info!(CVM_ALLOWED, "Formatting new VMGS file.");
613
614 let mut vmgs = Self::new(storage, version, reason, logger);
615
616 vmgs.write_header_internal(&VmgsHeader::new_zeroed(), vmgs.state.active_header_index)
618 .await?;
619
620 let files = if version >= VMGS_VERSION_3_0 {
622 [(
623 FileId::EXTENDED_FILE_TABLE,
624 AllocRequest::new(RefOrOwned::placeholder::<VmgsExtendedFileTable>(), false),
625 )]
626 .into()
627 } else {
628 BTreeMap::new()
629 };
630
631 vmgs.write_files_internal(files, None).await?;
633
634 let (new_header, index) = vmgs.state.make_header();
636 vmgs.write_header_internal(&new_header, index).await?;
637
638 vmgs.storage.flush().await.map_err(Error::FlushDisk)?;
640
641 Ok(vmgs)
642 }
643
644 pub fn get_file_info(&self, file_id: FileId) -> Result<VmgsFileInfo, Error> {
648 Ok(self
649 .state
650 .fcbs
651 .get(&file_id)
652 .ok_or(Error::FileInfoNotAllocated(file_id))?
653 .file_info())
654 }
655
656 pub fn check_file_allocated(&self, file_id: FileId) -> bool {
658 self.state.fcbs.contains_key(&file_id)
659 }
660
661 pub fn dump_file_table(&self) -> Vec<(FileId, VmgsFileInfo)> {
663 let mut file_table = self
664 .state
665 .fcbs
666 .iter()
667 .map(|(file_id, fcb)| (*file_id, fcb.file_info()))
668 .collect::<Vec<_>>();
669 file_table.sort_by_key(|(file_id, _)| *file_id);
670 file_table
671 }
672
673 async fn write_file_inner(
676 &mut self,
677 file_id: FileId,
678 buf: &[u8],
679 encrypt: bool,
680 overwrite_encrypted: bool,
681 ) -> Result<(), Error> {
682 #[cfg(feature = "inspect")]
683 self.stats
684 .write
685 .entry(file_id)
686 .or_default()
687 .attempt
688 .increment();
689
690 if matches!(file_id, FileId::FILE_TABLE | FileId::EXTENDED_FILE_TABLE) {
691 return Err(Error::FileId);
692 }
693 if buf.len() > vmgs_format::VMGS_MAX_FILE_SIZE_BYTES as usize {
694 return Err(Error::WriteFileLength);
695 }
696
697 let mut temp_state = self.temp_state();
698
699 if encrypt && !temp_state.encrypted_and_unlocked() {
700 tracing::trace!(
701 CVM_ALLOWED,
702 "VMGS file not encrypted and unlocked, performing plaintext write"
703 );
704 }
705
706 let encrypt = encrypt && temp_state.encrypted_and_unlocked();
707 let existing_encrypted = temp_state
708 .fcbs
709 .get(&file_id)
710 .is_some_and(|fcb| fcb.encrypted());
711
712 if !encrypt && existing_encrypted {
713 if overwrite_encrypted {
714 tracing::warn!(
715 CVM_ALLOWED,
716 "overwriting encrypted file with plaintext data!"
717 )
718 } else {
719 return Err(Error::OverwriteEncrypted);
720 }
721 }
722
723 self.write_files_internal(
724 [(file_id, AllocRequest::new(RefOrOwned::Ref(buf), encrypt))].into(),
725 Some(&mut temp_state),
726 )
727 .await?;
728
729 self.write_header_and_apply(temp_state).await?;
731
732 #[cfg(feature = "inspect")]
733 self.stats
734 .write
735 .entry(file_id)
736 .or_default()
737 .resolved
738 .increment();
739
740 Ok(())
741 }
742
743 async fn write_files_internal<'a>(
745 &mut self,
746 mut files: BTreeMap<FileId, AllocRequest<'a>>,
748 temp_state: Option<&mut VmgsState>,
749 ) -> Result<(), Error> {
750 let state = temp_state.unwrap_or(&mut self.state);
751
752 files.insert(
754 FileId::FILE_TABLE,
755 AllocRequest::new(RefOrOwned::placeholder::<VmgsFileTable>(), false),
756 );
757 if state.encrypted_and_unlocked() {
758 files.insert(
759 FileId::EXTENDED_FILE_TABLE,
760 AllocRequest::new(RefOrOwned::placeholder::<VmgsExtendedFileTable>(), true),
761 );
762 }
763
764 let mut files = state.allocate_space(files, self.storage.block_capacity())?;
766
767 for (file_id, res) in files.iter_mut() {
770 if *file_id != FileId::EXTENDED_FILE_TABLE {
771 if res.fcb.encrypted() {
772 res.encrypt()?;
773 }
774 state.fcbs.insert(*file_id, res.fcb.clone());
775 }
776 }
777
778 if let Some(res) = files.get_mut(&FileId::EXTENDED_FILE_TABLE) {
780 if state.encrypted_and_unlocked() {
781 res.fcb.clear_encryption();
783 let new_extended_file_table = state.make_extended_file_table()?;
784 res.encrypt_from(new_extended_file_table.as_bytes())?;
785 }
786 state
788 .fcbs
789 .insert(FileId::EXTENDED_FILE_TABLE, res.fcb.clone());
790 if state.encrypted_and_unlocked() {
791 state.encrypt_metadata_key()?;
792 }
793 }
794
795 let new_file_table = state.make_file_table()?;
798 files
799 .get_mut(&FileId::FILE_TABLE)
800 .unwrap()
801 .data
802 .copy_from_slice(new_file_table.as_bytes());
803
804 for (_, res) in files.iter() {
806 self.write_file_internal(&res.fcb, res.data.get()).await?;
807 }
808
809 Ok(())
810 }
811
812 async fn write_file_internal(
815 &mut self,
816 fcb: &ResolvedFileControlBlock,
817 buf: &[u8],
818 ) -> Result<(), Error> {
819 if let Err(e) = self
820 .storage
821 .write_block(block_count_to_byte_count(fcb.block_offset), buf)
822 .await
823 {
824 self.logger
825 .log_event_fatal(VmgsLogEvent::AccessFailed)
826 .await;
827
828 return Err(Error::WriteDisk(e));
829 }
830
831 Ok(())
832 }
833
834 async fn write_header_internal(
836 &mut self,
837 header: &VmgsHeader,
838 index: usize,
839 ) -> Result<(), Error> {
840 assert!(index < 2);
841 self.storage
842 .write_block(
843 index as u64 * self.storage.aligned_header_size(),
844 header.as_bytes(),
845 )
846 .await
847 .map_err(Error::WriteDisk)?;
848 Ok(())
849 }
850
851 pub async fn read_file(&mut self, file_id: FileId) -> Result<Vec<u8>, Error> {
853 self.read_file_inner(file_id, true).await
854 }
855
856 pub async fn read_file_raw(&mut self, file_id: FileId) -> Result<Vec<u8>, Error> {
858 self.read_file_inner(file_id, false).await
859 }
860
861 async fn read_file_inner(&mut self, file_id: FileId, decrypt: bool) -> Result<Vec<u8>, Error> {
863 #[cfg(feature = "inspect")]
864 self.stats
865 .read
866 .entry(file_id)
867 .or_default()
868 .attempt
869 .increment();
870
871 if matches!(file_id, FileId::FILE_TABLE | FileId::EXTENDED_FILE_TABLE) {
872 return Err(Error::FileId);
873 }
874
875 let buf = self.read_file_internal(file_id, decrypt, None).await?;
876
877 #[cfg(feature = "inspect")]
878 self.stats
879 .read
880 .entry(file_id)
881 .or_default()
882 .resolved
883 .increment();
884
885 Ok(buf)
886 }
887
888 async fn read_file_internal(
890 &mut self,
891 file_id: FileId,
892 decrypt: bool,
893 temp_state: Option<&VmgsState>,
894 ) -> Result<Vec<u8>, Error> {
895 let state = temp_state.unwrap_or(&self.state);
896
897 let fcb = state
898 .fcbs
899 .get(&file_id)
900 .ok_or(Error::FileInfoNotAllocated(file_id))?;
901
902 let buf = {
904 let mut buf = vec![0; fcb.valid_bytes as usize];
905
906 if let Err(e) = self
907 .storage
908 .read_block(block_count_to_byte_count(fcb.block_offset), &mut buf)
909 .await
910 {
911 self.logger
912 .log_event_fatal(VmgsLogEvent::AccessFailed)
913 .await;
914
915 return Err(Error::ReadDisk(e));
916 }
917
918 buf
919 };
920
921 if decrypt
923 && state.version >= VMGS_VERSION_3_0
924 && state.encrypted_and_unlocked()
925 && fcb.encrypted()
926 {
927 match fcb.decrypt(&buf) {
928 Err(e) => {
929 self.logger
930 .log_event_fatal(VmgsLogEvent::AccessFailed)
931 .await;
932
933 Err(e)
934 }
935 Ok(b) => Ok(b),
936 }
937 } else if fcb.encrypted() && decrypt {
938 Err(Error::NeedsUnlock)
939 } else {
940 Ok(buf)
941 }
942 }
943
944 pub async fn write_file(&mut self, file_id: FileId, buf: &[u8]) -> Result<(), Error> {
952 self.write_file_inner(file_id, buf, false, false).await
953 }
954
955 pub async fn write_file_allow_overwrite_encrypted(
958 &mut self,
959 file_id: FileId,
960 buf: &[u8],
961 ) -> Result<(), Error> {
962 self.write_file_inner(file_id, buf, false, true).await
963 }
964
965 #[cfg(feature = "encryption")]
968 pub async fn write_file_encrypted(&mut self, file_id: FileId, buf: &[u8]) -> Result<(), Error> {
969 self.write_file_inner(file_id, buf, true, true).await
970 }
971
972 pub async fn move_file(
974 &mut self,
975 src: FileId,
976 dst: FileId,
977 allow_overwrite: bool,
978 ) -> Result<(), Error> {
979 if [src, dst]
980 .iter()
981 .any(|id| matches!(*id, FileId::FILE_TABLE | FileId::EXTENDED_FILE_TABLE))
982 {
983 return Err(Error::FileId);
984 }
985
986 if !allow_overwrite && self.state.fcbs.contains_key(&dst) {
987 return Err(Error::OverwriteMove);
988 }
989
990 let mut temp_state = self.temp_state();
991
992 let fcb = temp_state
994 .fcbs
995 .remove(&src)
996 .ok_or(Error::FileInfoNotAllocated(src))?;
997 temp_state.fcbs.insert(dst, fcb);
998
999 self.write_files_internal(BTreeMap::new(), Some(&mut temp_state))
1001 .await?;
1002
1003 self.write_header_and_apply(temp_state).await?;
1005
1006 Ok(())
1007 }
1008
1009 pub async fn delete_file(&mut self, file_id: FileId) -> Result<(), Error> {
1011 if matches!(file_id, FileId::FILE_TABLE | FileId::EXTENDED_FILE_TABLE) {
1012 return Err(Error::FileId);
1013 }
1014
1015 let mut temp_state = self.temp_state();
1016
1017 temp_state
1019 .fcbs
1020 .remove(&file_id)
1021 .ok_or(Error::FileInfoNotAllocated(file_id))?;
1022
1023 self.write_files_internal(BTreeMap::new(), Some(&mut temp_state))
1025 .await?;
1026
1027 self.write_header_and_apply(temp_state).await?;
1029
1030 Ok(())
1031 }
1032
1033 #[cfg(feature = "encryption")]
1036 pub async fn unlock_with_encryption_key(
1037 &mut self,
1038 encryption_key: &[u8; VMGS_ENCRYPTION_KEY_SIZE],
1039 ) -> Result<(), Error> {
1040 if self.state.version < VMGS_VERSION_3_0 {
1041 return Err(Error::EncryptionNotSupported);
1042 }
1043 if !self.encrypted() {
1044 return Err(Error::NotEncrypted);
1045 }
1046
1047 let mut temp_state = self.temp_state();
1048
1049 let mut valid_index_and_key = None;
1052 let mut errs = [None, None];
1053
1054 for (i, key) in temp_state.encrypted_metadata_keys.iter().enumerate() {
1055 let result = decrypt_metadata_key(
1056 encryption_key,
1057 &key.nonce,
1058 &key.encryption_key,
1059 &key.authentication_tag,
1060 );
1061
1062 match result {
1063 Ok(metadata_key) => {
1064 valid_index_and_key = Some((i, metadata_key));
1065 break;
1066 }
1067 Err(err) => {
1068 errs[i] = Some(err);
1069 }
1070 }
1071 }
1072
1073 match valid_index_and_key {
1074 Some((i, metadata_key)) => {
1075 let fcb = temp_state
1076 .fcbs
1077 .get_mut(&FileId::EXTENDED_FILE_TABLE)
1078 .ok_or(Error::FileInfoNotAllocated(FileId::EXTENDED_FILE_TABLE))?;
1079 fcb.attributes.set_encrypted(true);
1082 fcb.attributes.set_authenticated(true);
1083 fcb.encryption_key.copy_from_slice(&metadata_key);
1084 temp_state.datastore_keys[i].copy_from_slice(encryption_key);
1085 temp_state.active_datastore_key_index = Some(i);
1086 }
1087 None => {
1088 tracing::error!(
1089 CVM_ALLOWED,
1090 error = &errs[0].take().unwrap() as &dyn std::error::Error,
1091 "first index failed to decrypt",
1092 );
1093 tracing::error!(
1094 CVM_ALLOWED,
1095 error = &errs[1].take().unwrap() as &dyn std::error::Error,
1096 "second index failed to decrypt",
1097 );
1098 return Err(Error::DecryptMetadataKey);
1099 }
1100 }
1101
1102 let extended_file_table_buffer = self
1104 .read_file_internal(FileId::EXTENDED_FILE_TABLE, true, Some(&temp_state))
1105 .await?;
1106
1107 let extended_file_table =
1109 VmgsExtendedFileTable::ref_from_bytes(extended_file_table_buffer.as_bytes())
1110 .map_err(|_| Error::InvalidFormat("incorrect extended file table size".into()))?;
1111
1112 for (file_id, fcb) in temp_state.fcbs.iter_mut() {
1113 if *file_id != FileId::EXTENDED_FILE_TABLE {
1114 fcb.update_extended_data(&extended_file_table.entries[*file_id]);
1115 }
1116 }
1117
1118 self.apply(temp_state);
1119
1120 Ok(())
1121 }
1122
1123 #[cfg(feature = "encryption")]
1127 pub async fn update_encryption_key(
1128 &mut self,
1129 encryption_key: &[u8],
1130 encryption_algorithm: EncryptionAlgorithm,
1131 ) -> Result<(), Error> {
1132 let old_index = self.state.active_datastore_key_index;
1133
1134 match self
1135 .add_new_encryption_key(encryption_key, encryption_algorithm)
1136 .await
1137 {
1138 Ok(_) => {}
1139 Err(Error::DatastoreKeysFull) => {
1140 if let Some(old_index) = old_index {
1141 let inactive_index = if old_index == 0 { 1 } else { 0 };
1142 tracing::warn!(CVM_ALLOWED, inactive_index, "removing inactive key");
1143 self.remove_encryption_key(inactive_index).await?;
1144 tracing::trace!(CVM_ALLOWED, "attempting to add the key again");
1145 self.add_new_encryption_key(encryption_key, encryption_algorithm)
1146 .await?;
1147 } else {
1148 return Err(Error::NoActiveDatastoreKey);
1149 }
1150 }
1151 Err(e) => return Err(e),
1152 };
1153
1154 if let Some(old_index) = old_index {
1155 self.remove_encryption_key(old_index).await?;
1156 }
1157
1158 Ok(())
1159 }
1160
1161 #[cfg(feature = "encryption")]
1163 async fn add_new_encryption_key(
1164 &mut self,
1165 encryption_key: &[u8],
1166 encryption_algorithm: EncryptionAlgorithm,
1167 ) -> Result<(), Error> {
1168 if self.state.version < VMGS_VERSION_3_0 {
1169 return Err(Error::EncryptionNotSupported);
1170 }
1171 if self.encrypted() && !self.state.unlocked() {
1172 return Err(Error::NeedsUnlock);
1173 }
1174 if self.state.datastore_key_count == self.state.datastore_keys.len() as u8 {
1175 return Err(Error::DatastoreKeysFull);
1176 }
1177 if is_empty_key(encryption_key) {
1178 return Err(Error::InvalidArgument("empty encryption key"));
1179 }
1180 if encryption_algorithm == EncryptionAlgorithm::NONE {
1181 return Err(Error::InvalidArgument(
1182 "encryption algorithm cannot be none",
1183 ));
1184 }
1185 if self.encrypted() && encryption_algorithm != self.state.encryption_algorithm {
1186 return Err(Error::InvalidArgument(
1187 "Encryption algorithm provided to add_new_encryption_key does not match VMGS's encryption algorithm.",
1188 ));
1189 }
1190
1191 let new_key_index = self
1192 .state
1193 .active_datastore_key_index
1194 .map_or(0, |i| if i == 0 { 1 } else { 0 });
1195
1196 let mut temp_state = self.temp_state();
1197 temp_state.encryption_algorithm = encryption_algorithm;
1198 temp_state.datastore_keys[new_key_index].copy_from_slice(encryption_key);
1199 temp_state.active_datastore_key_index = Some(new_key_index);
1200 temp_state.datastore_key_count += 1;
1201 temp_state.encrypted_metadata_keys[new_key_index] = VmgsEncryptionKey::new_zeroed();
1203
1204 if self.state.datastore_key_count == 0 {
1206 self.write_files_internal(BTreeMap::new(), Some(&mut temp_state))
1207 .await?;
1208 } else {
1209 temp_state.encrypt_metadata_key()?;
1212 }
1213
1214 self.write_header_and_apply(temp_state).await?;
1216
1217 Ok(())
1218 }
1219
1220 #[cfg(feature = "encryption")]
1222 async fn remove_encryption_key(&mut self, key_index: usize) -> Result<(), Error> {
1223 if self.state.version < VMGS_VERSION_3_0 {
1224 return Err(Error::EncryptionNotSupported);
1225 }
1226 if self.encrypted() && !self.state.unlocked() {
1227 return Err(Error::NeedsUnlock);
1228 }
1229 if self.state.datastore_key_count != self.state.datastore_keys.len() as u8
1230 && self.state.active_datastore_key_index != Some(key_index)
1231 {
1232 return Err(Error::InvalidArgument("key index"));
1233 }
1234
1235 let mut temp_state = self.temp_state();
1236
1237 temp_state.datastore_keys[key_index].fill(0);
1239
1240 temp_state.encrypted_metadata_keys[key_index] = VmgsEncryptionKey::new_zeroed();
1242
1243 if temp_state.datastore_key_count == 1 {
1245 temp_state.encryption_algorithm = EncryptionAlgorithm::NONE;
1246 temp_state.datastore_key_count = 0;
1247 temp_state.active_datastore_key_index = None;
1248 } else {
1249 temp_state.datastore_key_count = 1;
1250
1251 let new_active_datastore_key_index = if key_index == 0 { 1 } else { 0 };
1252 if is_empty_key(&temp_state.datastore_keys[new_active_datastore_key_index]) {
1253 temp_state.active_datastore_key_index = None;
1254 } else {
1255 temp_state.active_datastore_key_index = Some(new_active_datastore_key_index);
1256 }
1257 }
1258
1259 self.write_header_and_apply(temp_state).await?;
1260
1261 Ok(())
1262 }
1263
1264 pub fn get_encryption_algorithm(&self) -> EncryptionAlgorithm {
1266 self.state.encryption_algorithm
1267 }
1268
1269 pub fn encrypted(&self) -> bool {
1271 self.state.encrypted()
1272 }
1273
1274 pub fn was_provisioned_this_boot(&self) -> bool {
1276 self.state.provisioning_reason.is_some()
1277 }
1278
1279 pub fn provisioning_reason(&self) -> Option<VmgsProvisioningReason> {
1281 self.state.provisioning_reason
1282 }
1283
1284 pub async fn write_provisioning_marker(
1286 &mut self,
1287 marker: &VmgsProvisioningMarker,
1288 ) -> Result<(), Error> {
1289 self.write_file(
1290 FileId::PROVISIONING_MARKER,
1291 serde_json::to_string(marker)?.as_bytes(),
1292 )
1293 .await
1294 }
1295
1296 async fn set_reprovisioned(&mut self, value: bool) -> Result<(), Error> {
1297 if self.state.reprovisioned != value {
1298 tracing::info!(reprovisioned = value, "update vmgs marker");
1299 let mut temp_state = self.temp_state();
1300 temp_state.reprovisioned = value;
1301 self.write_header_and_apply(temp_state).await?;
1302 }
1303 Ok(())
1304 }
1305
1306 fn temp_state(&self) -> VmgsState {
1308 self.state.clone()
1309 }
1310
1311 fn apply(&mut self, temp_state: VmgsState) {
1313 self.state = temp_state;
1314 }
1315
1316 async fn write_header_and_apply(&mut self, mut temp_state: VmgsState) -> Result<(), Error> {
1318 self.storage.flush().await.map_err(Error::FlushDisk)?;
1320
1321 let (new_header, index) = temp_state.make_header();
1322 self.write_header_internal(&new_header, index).await?;
1323 self.apply(temp_state);
1324 Ok(())
1325 }
1326}
1327
1328impl VmgsState {
1329 fn new(version: u32, provisioning_reason: Option<VmgsProvisioningReason>) -> Self {
1330 Self {
1331 active_header_index: 1,
1332 active_header_sequence_number: 0,
1333 version,
1334 fcbs: HashMap::new(),
1335 encryption_algorithm: EncryptionAlgorithm::NONE,
1336 datastore_key_count: 0,
1337 active_datastore_key_index: None,
1338 datastore_keys: [VmgsDatastoreKey::new_zeroed(); 2],
1339 unused_metadata_key: VmgsDatastoreKey::new_zeroed(),
1340 encrypted_metadata_keys: std::array::from_fn(|_| VmgsEncryptionKey::new_zeroed()),
1341 reprovisioned: false,
1342 provisioning_reason,
1343 }
1344 }
1345
1346 fn from_header(header: VmgsHeader, header_index: usize) -> Self {
1347 let mut state = Self::new(header.version, None);
1348
1349 state.active_header_index = header_index;
1350 state.active_header_sequence_number = header.sequence;
1351
1352 if header.version >= VMGS_VERSION_3_0 {
1353 state.encryption_algorithm = header.encryption_algorithm;
1354 state.encrypted_metadata_keys = header.metadata_keys;
1355 for key in &state.encrypted_metadata_keys {
1356 if !is_empty_key(&key.encryption_key) {
1357 state.datastore_key_count += 1;
1358 }
1359 }
1360 state.reprovisioned = header.markers.reprovisioned();
1361 }
1362
1363 state.fcbs.insert(
1364 FileId::FILE_TABLE,
1365 ResolvedFileControlBlock::new(
1366 header.file_table_offset,
1367 header.file_table_size,
1368 size_of::<VmgsFileTable>(),
1369 false,
1370 ),
1371 );
1372
1373 state
1374 }
1375
1376 fn make_header(&mut self) -> (VmgsHeader, usize) {
1379 let file_table_fcb = self.fcbs.get(&FileId::FILE_TABLE).unwrap();
1380 let mut header = VmgsHeader {
1381 signature: VMGS_SIGNATURE,
1382 version: self.version,
1383 header_size: size_of::<VmgsHeader>() as u32,
1384 file_table_offset: file_table_fcb.block_offset,
1385 file_table_size: file_table_fcb.allocated_blocks.get(),
1386 encryption_algorithm: self.encryption_algorithm,
1387 markers: VmgsMarkers::new().with_reprovisioned(self.reprovisioned),
1388 ..VmgsHeader::new_zeroed()
1389 };
1390 header.metadata_keys = self.encrypted_metadata_keys.clone();
1391
1392 self.active_header_sequence_number = self.active_header_sequence_number.wrapping_add(1);
1393 self.active_header_index = if self.active_header_index == 0 { 1 } else { 0 };
1394
1395 header.sequence = self.active_header_sequence_number;
1396 header.checksum = 0;
1397 header.checksum = compute_crc32(header.as_bytes());
1398
1399 (header, self.active_header_index)
1400 }
1401
1402 fn encrypted(&self) -> bool {
1404 self.encryption_algorithm != EncryptionAlgorithm::NONE
1405 }
1406
1407 fn unlocked(&self) -> bool {
1409 self.active_datastore_key_index.is_some()
1410 }
1411
1412 fn encrypted_and_unlocked(&self) -> bool {
1414 self.encrypted() && self.unlocked()
1415 }
1416
1417 fn encrypt_metadata_key(&mut self) -> Result<(), Error> {
1419 let current_index = self.active_datastore_key_index.ok_or(Error::NeedsUnlock)?;
1420 let metadata_key = &self
1421 .fcbs
1422 .get(&FileId::EXTENDED_FILE_TABLE)
1423 .ok_or(Error::FileInfoNotAllocated(FileId::EXTENDED_FILE_TABLE))?
1424 .encryption_key;
1425
1426 self.unused_metadata_key.copy_from_slice(metadata_key);
1427
1428 if is_empty_key(&self.encrypted_metadata_keys[current_index].nonce) {
1429 self.encrypted_metadata_keys[current_index]
1430 .nonce
1431 .copy_from_slice(&generate_nonce());
1432 } else {
1433 increment_nonce(&mut self.encrypted_metadata_keys[current_index].nonce)?;
1434 }
1435
1436 let mut metadata_key_auth_tag = VmgsAuthTag::new_zeroed();
1437 let encrypted_metadata_key = encrypt_metadata_key(
1438 &self.datastore_keys[current_index],
1439 &self.encrypted_metadata_keys[current_index].nonce,
1440 metadata_key,
1441 &mut metadata_key_auth_tag,
1442 )?;
1443
1444 self.encrypted_metadata_keys[current_index]
1445 .authentication_tag
1446 .copy_from_slice(&metadata_key_auth_tag);
1447 self.encrypted_metadata_keys[current_index]
1448 .encryption_key
1449 .copy_from_slice(&encrypted_metadata_key);
1450
1451 Ok(())
1452 }
1453
1454 fn make_file_table(&self) -> Result<VmgsFileTable, Error> {
1456 let mut new_file_table = VmgsFileTable::new_zeroed();
1457 for (file_id, fcb) in self.fcbs.iter() {
1458 fcb.fill_file_entry(self.version, &mut new_file_table.entries[*file_id]);
1459 }
1460 Ok(new_file_table)
1461 }
1462
1463 fn make_extended_file_table(&self) -> Result<VmgsExtendedFileTable, Error> {
1465 let mut new_extended_file_table = VmgsExtendedFileTable::new_zeroed();
1466 for (file_id, fcb) in self.fcbs.iter() {
1467 fcb.fill_extended_file_entry(&mut new_extended_file_table.entries[*file_id]);
1468 }
1469 Ok(new_extended_file_table)
1470 }
1471
1472 fn allocate_space<'a>(
1475 &self,
1476 files_to_allocate: BTreeMap<FileId, AllocRequest<'a>>,
1477 block_capacity: u32,
1478 ) -> Result<BTreeMap<FileId, AllocResult<'a>>, Error> {
1479 let mut allocation_list = self
1481 .fcbs
1482 .values()
1483 .map(|fcb| AllocationBlock {
1484 block_offset: fcb.block_offset,
1485 allocated_blocks: fcb.allocated_blocks.get(),
1486 })
1487 .collect();
1488
1489 files_to_allocate
1491 .into_iter()
1492 .map(|(file_id, req)| {
1493 Ok((file_id, req.allocate(&mut allocation_list, block_capacity)?))
1494 })
1495 .collect()
1496 }
1497}
1498
1499#[cfg(feature = "test_helpers")]
1502mod test_helpers {
1503 use super::*;
1504
1505 impl Vmgs {
1506 pub fn test_get_active_datastore_key_index(&self) -> Option<usize> {
1508 self.state.active_datastore_key_index
1509 }
1510
1511 #[cfg(feature = "encryption")]
1513 pub async fn test_add_new_encryption_key(
1514 &mut self,
1515 encryption_key: &[u8],
1516 encryption_algorithm: EncryptionAlgorithm,
1517 ) -> Result<(), Error> {
1518 self.add_new_encryption_key(encryption_key, encryption_algorithm)
1519 .await
1520 }
1521 }
1522}
1523
1524pub async fn read_headers(
1526 disk: Disk,
1527) -> Result<(VmgsHeader, VmgsHeader), (Error, Option<(VmgsHeader, VmgsHeader)>)> {
1528 let mut storage = VmgsStorage::new(disk);
1529 match (storage.validate(), read_headers_inner(&mut storage).await) {
1530 (Ok(_), res) => res,
1531 (Err(e), res) => Err((Error::Initialization(e), res.ok())),
1532 }
1533}
1534
1535async fn read_headers_inner(
1536 storage: &mut VmgsStorage,
1537) -> Result<(VmgsHeader, VmgsHeader), (Error, Option<(VmgsHeader, VmgsHeader)>)> {
1538 let mut first_two_blocks = [0; (VMGS_BYTES_PER_BLOCK * 2) as usize];
1540
1541 storage
1542 .read_block(0, &mut first_two_blocks)
1543 .await
1544 .map_err(|e| (Error::ReadDisk(e), None))?;
1545
1546 let header_1 = VmgsHeader::read_from_prefix(&first_two_blocks).unwrap().0; let header_2 =
1548 VmgsHeader::read_from_prefix(&first_two_blocks[storage.aligned_header_size() as usize..])
1549 .unwrap()
1550 .0; let headers = (header_1, header_2);
1552
1553 if vmgs_is_v1(&first_two_blocks) {
1554 Err((Error::V1Format, Some(headers)))
1555 } else if vmgs_headers_empty(&headers.0, &headers.1) {
1556 Err((Error::EmptyFile, Some(headers)))
1557 } else {
1558 Ok(headers)
1559 }
1560}
1561
1562fn vmgs_is_v1(first_two_blocks: &[u8; 2 * VMGS_BYTES_PER_BLOCK as usize]) -> bool {
1563 const EFI_SIGNATURE: &[u8] = b"EFI PART";
1564 const EFI_SIGNATURE_OFFSET: usize = 512;
1565
1566 EFI_SIGNATURE
1567 == &first_two_blocks[EFI_SIGNATURE_OFFSET..EFI_SIGNATURE_OFFSET + EFI_SIGNATURE.len()]
1568}
1569
1570fn vmgs_headers_empty(header_1: &VmgsHeader, header_2: &VmgsHeader) -> bool {
1571 let empty_header = VmgsHeader::new_zeroed();
1572
1573 header_1.as_bytes() == empty_header.as_bytes() && header_2.as_bytes() == empty_header.as_bytes()
1574}
1575
1576pub fn get_active_header(
1579 header_1: Result<&VmgsHeader, Error>,
1580 header_2: Result<&VmgsHeader, Error>,
1581) -> Result<usize, Error> {
1582 let active_header_index =
1583 if let (Ok(header_1), Ok(header_2)) = (header_1.as_deref(), header_2.as_deref()) {
1584 if header_1.sequence == header_2.sequence.wrapping_add(1) {
1589 0
1590 } else if header_2.sequence == header_1.sequence.wrapping_add(1) {
1591 1
1592 } else {
1593 return Err(Error::CorruptFormat(format!(
1594 "Invalid header sequence numbers. Header 1: {}, Header 2: {}",
1595 header_1.sequence, header_2.sequence
1596 )));
1597 }
1598 } else if header_1.is_ok() {
1599 0
1600 } else if header_2.is_ok() {
1601 1
1602 } else {
1603 return Err(Error::InvalidFormat(format!(
1604 "No valid header: Header 1: {} Header 2: {}",
1605 header_1.err().unwrap(),
1606 header_2.err().unwrap()
1607 )));
1608 };
1609
1610 Ok(active_header_index)
1611}
1612
1613pub fn validate_header(header: &VmgsHeader) -> Result<&VmgsHeader, Error> {
1615 if header.signature != VMGS_SIGNATURE {
1616 return Err(Error::InvalidFormat(String::from(
1617 "Invalid header signature",
1618 )));
1619 }
1620 if header.version != VMGS_VERSION_3_0 {
1621 return Err(Error::InvalidFormat(String::from("Invalid header version")));
1622 }
1623 if header.header_size != size_of::<VmgsHeader>() as u32 {
1624 return Err(Error::InvalidFormat(String::from("Invalid header size")));
1625 }
1626 if header.file_table_offset < VMGS_MIN_FILE_BLOCK_OFFSET {
1627 return Err(Error::InvalidFormat(String::from(
1628 "Invalid file table offset",
1629 )));
1630 }
1631 if header.file_table_size != VMGS_FILE_TABLE_BLOCK_SIZE {
1632 return Err(Error::InvalidFormat(String::from(
1633 "Invalid file table size",
1634 )));
1635 }
1636 if header.encryption_algorithm > EncryptionAlgorithm::AES_GCM {
1637 return Err(Error::InvalidFormat(String::from(
1638 "Invalid encryption algorithm",
1639 )));
1640 }
1641
1642 let stored_checksum = header.checksum;
1643 let mut zero_checksum_header = header.clone();
1644 zero_checksum_header.checksum = 0;
1645 let computed_checksum = compute_crc32(zero_checksum_header.as_bytes());
1646 if stored_checksum != computed_checksum {
1647 return Err(Error::CorruptFormat(String::from(
1648 "Invalid header checksum",
1649 )));
1650 }
1651 Ok(header)
1652}
1653
1654fn initialize_file_metadata(
1656 file_table: &VmgsFileTable,
1657 version: u32,
1658 block_capacity: u32,
1659) -> Result<HashMap<FileId, ResolvedFileControlBlock>, Error> {
1660 let mut fcbs = HashMap::new();
1661
1662 for (file_id, file_entry) in file_table.entries.iter().enumerate() {
1663 let file_id = FileId(file_id as u32);
1664
1665 if file_entry.allocation_size == 0 {
1667 continue;
1668 };
1669
1670 if file_entry.offset < VMGS_MIN_FILE_BLOCK_OFFSET || file_entry.offset >= block_capacity {
1672 return Err(Error::CorruptFormat(format!(
1673 "Invalid file offset {} for file_id {:?} \n{:?}",
1674 file_entry.offset, file_id, file_entry
1675 )));
1676 }
1677
1678 let file_allocation_end_block = file_entry.offset + file_entry.allocation_size;
1680 if file_allocation_end_block > block_capacity {
1681 return Err(Error::CorruptFormat(String::from(
1682 "Invalid file allocation end block",
1683 )));
1684 }
1685
1686 let file_allocation_size_bytes = block_count_to_byte_count(file_entry.allocation_size);
1688 if file_entry.valid_data_size > file_allocation_size_bytes {
1689 return Err(Error::CorruptFormat(String::from("Invalid data size")));
1690 }
1691
1692 let fcb = ResolvedFileControlBlock::from_file_entry(version, file_entry);
1693
1694 fcbs.insert(file_id, fcb);
1696 }
1697
1698 Ok(fcbs)
1699}
1700
1701fn block_count_to_byte_count(block_count: u32) -> u64 {
1703 block_count as u64 * VMGS_BYTES_PER_BLOCK as u64
1704}
1705
1706fn round_up_count(count: usize, pow2: u32) -> u64 {
1707 (count as u64 + pow2 as u64 - 1) & !(pow2 as u64 - 1)
1708}
1709
1710fn generate_nonce() -> VmgsNonce {
1712 let mut nonce = VmgsNonce::new_zeroed();
1713 getrandom::fill(&mut nonce[..vmgs_format::VMGS_NONCE_RANDOM_SEED_SIZE]).expect("rng failure");
1715 nonce
1716}
1717
1718fn increment_nonce(nonce: &mut VmgsNonce) -> Result<(), Error> {
1720 getrandom::fill(&mut nonce[..vmgs_format::VMGS_NONCE_RANDOM_SEED_SIZE]).expect("rng failure");
1722
1723 for i in &mut nonce[vmgs_format::VMGS_NONCE_RANDOM_SEED_SIZE..] {
1725 *i = i.wrapping_add(1);
1726
1727 if *i != 0 {
1728 break;
1729 }
1730 }
1731
1732 Ok(())
1733}
1734
1735fn is_empty_key(encryption_key: &[u8]) -> bool {
1737 encryption_key.iter().all(|&x| x == 0)
1738}
1739
1740#[cfg_attr(not(feature = "encryption"), expect(unused_variables))]
1742fn encrypt_metadata_key(
1743 encryption_key: &[u8; VMGS_ENCRYPTION_KEY_SIZE],
1744 nonce: &[u8; VMGS_NONCE_SIZE],
1745 metadata_key: &[u8],
1746 authentication_tag: &mut [u8],
1747) -> Result<Vec<u8>, Error> {
1748 #[cfg(not(feature = "encryption"))]
1749 unreachable!("Encryption requires the encryption feature");
1750 #[cfg(feature = "encryption")]
1751 {
1752 let encrypted_metadata_key =
1753 crate::encrypt::vmgs_encrypt(encryption_key, nonce, metadata_key, authentication_tag)?;
1754
1755 if encrypted_metadata_key.len() != metadata_key.len() {
1756 return Err(Error::UnexpectedLength(
1757 "encrypted metadata key",
1758 encrypted_metadata_key.len(),
1759 metadata_key.len(),
1760 ));
1761 }
1762 Ok(encrypted_metadata_key)
1763 }
1764}
1765
1766#[cfg_attr(
1768 not(feature = "encryption"),
1769 expect(unused_variables),
1770 expect(dead_code)
1771)]
1772fn decrypt_metadata_key(
1773 datastore_key: &[u8; VMGS_ENCRYPTION_KEY_SIZE],
1774 nonce: &[u8; VMGS_NONCE_SIZE],
1775 metadata_key: &[u8],
1776 authentication_tag: &[u8],
1777) -> Result<Vec<u8>, Error> {
1778 #[cfg(not(feature = "encryption"))]
1779 unreachable!("Encryption requires the encryption feature");
1780 #[cfg(feature = "encryption")]
1781 {
1782 let decrypted_metadata_key =
1783 crate::encrypt::vmgs_decrypt(datastore_key, nonce, metadata_key, authentication_tag)?;
1784 if decrypted_metadata_key.len() != metadata_key.len() {
1785 return Err(Error::UnexpectedLength(
1786 "decrypted metadata key",
1787 metadata_key.len(),
1788 decrypted_metadata_key.len(),
1789 ));
1790 }
1791
1792 Ok(decrypted_metadata_key)
1793 }
1794}
1795
1796fn compute_crc32(buf: &[u8]) -> u32 {
1798 let mut hasher = crc32fast::Hasher::new();
1799 hasher.update(buf);
1800 hasher.finalize()
1801}
1802
1803struct AllocationBlock {
1804 block_offset: u32,
1805 allocated_blocks: u32,
1806}
1807
1808fn allocate_helper(
1811 allocation_list: &mut Vec<AllocationBlock>,
1812 block_count: u32,
1813 block_capacity: u32,
1814) -> Result<u32, Error> {
1815 allocation_list.sort_by_key(|a| a.block_offset);
1817
1818 let mut best_offset = 0;
1819 let mut best_free_count = 0;
1820 let mut last_allocation_end_offset = VMGS_MIN_FILE_BLOCK_OFFSET;
1821 let mut found = false;
1822
1823 for fcb in allocation_list.iter() {
1825 if fcb.block_offset < last_allocation_end_offset {
1826 return Err(Error::AllocateOffset);
1827 }
1828 let free_count = fcb.block_offset - last_allocation_end_offset;
1829 if free_count >= block_count && (best_free_count == 0 || free_count < best_free_count) {
1830 best_free_count = free_count;
1831 best_offset = last_allocation_end_offset;
1832 found = true;
1833 }
1834 last_allocation_end_offset = fcb.block_offset + fcb.allocated_blocks;
1835 }
1836 if last_allocation_end_offset < block_capacity {
1837 let free_count = block_capacity - last_allocation_end_offset;
1838 if free_count >= block_count && (best_free_count == 0 || free_count < best_free_count) {
1839 best_offset = last_allocation_end_offset;
1840 found = true;
1841 }
1842 }
1843 if !found {
1844 return Err(Error::InsufficientResources);
1845 }
1846
1847 allocation_list.push(AllocationBlock {
1848 block_offset: best_offset,
1849 allocated_blocks: block_count,
1850 });
1851 Ok(best_offset)
1852}
1853
1854#[cfg(feature = "save_restore")]
1855#[expect(missing_docs)]
1856pub mod save_restore {
1857 use super::*;
1858
1859 pub mod state {
1860 use mesh_protobuf::Protobuf;
1861 use std::num::NonZeroU32;
1862
1863 pub type SavedVmgsNonce = [u8; 12];
1864 pub type SavedVmgsAuthTag = [u8; 16];
1865 pub type SavedVmgsDatastoreKey = [u8; 32];
1866
1867 #[derive(Protobuf)]
1868 #[mesh(package = "vmgs")]
1869 pub struct SavedResolvedFileControlBlock {
1870 #[mesh(1)]
1871 pub block_offset: u32,
1872 #[mesh(2)]
1873 pub allocated_blocks: NonZeroU32,
1874 #[mesh(3)]
1875 pub valid_bytes: u64,
1876 #[mesh(4)]
1877 pub nonce: SavedVmgsNonce,
1878 #[mesh(5)]
1879 pub authentication_tag: SavedVmgsAuthTag,
1880 #[mesh(6)]
1881 pub attributes: u32,
1882 #[mesh(7)]
1883 pub encryption_key: SavedVmgsDatastoreKey,
1884 }
1885
1886 #[derive(Protobuf)]
1887 #[mesh(package = "vmgs")]
1888 pub struct SavedVmgsEncryptionKey {
1889 #[mesh(1)]
1890 pub nonce: SavedVmgsNonce,
1891 #[mesh(2)]
1892 pub authentication_tag: SavedVmgsAuthTag,
1893 #[mesh(3)]
1894 pub encryption_key: SavedVmgsDatastoreKey,
1895 }
1896
1897 #[derive(Protobuf)]
1898 #[mesh(package = "vmgs")]
1899 pub struct SavedVmgsState {
1900 #[mesh(1)]
1901 pub active_header_index: usize,
1902 #[mesh(2)]
1903 pub active_header_sequence_number: u32,
1904 #[mesh(3)]
1905 pub version: u32,
1906 #[mesh(4)]
1907 pub fcbs: Vec<(u32, SavedResolvedFileControlBlock)>,
1908 #[mesh(5)]
1909 pub encryption_algorithm: u16,
1910 #[mesh(6)]
1911 pub datastore_key_count: u8,
1912 #[mesh(7)]
1913 pub active_datastore_key_index: Option<usize>,
1914 #[mesh(8)]
1915 pub datastore_keys: [SavedVmgsDatastoreKey; 2],
1916 #[mesh(9)]
1917 pub metadata_key: SavedVmgsDatastoreKey,
1918 #[mesh(10)]
1919 pub encrypted_metadata_keys: [SavedVmgsEncryptionKey; 2],
1920 #[mesh(11)]
1921 pub reprovisioned: bool,
1922 }
1923 }
1924
1925 impl Vmgs {
1926 pub fn open_from_saved(
1944 disk: Disk,
1945 state: state::SavedVmgsState,
1946 logger: Option<Arc<dyn VmgsLogger>>,
1947 ) -> Self {
1948 let state::SavedVmgsState {
1949 active_header_index,
1950 active_header_sequence_number,
1951 version,
1952 fcbs,
1953 encryption_algorithm,
1954 datastore_key_count,
1955 active_datastore_key_index,
1956 datastore_keys,
1957 metadata_key,
1958 encrypted_metadata_keys,
1959 reprovisioned,
1960 } = state;
1961
1962 Self {
1963 storage: VmgsStorage::new(disk),
1964 #[cfg(feature = "inspect")]
1965 stats: Default::default(),
1966
1967 state: VmgsState {
1968 active_header_index,
1969 active_header_sequence_number,
1970 version,
1971 fcbs: fcbs
1972 .into_iter()
1973 .map(|(file_id, fcb)| {
1974 let state::SavedResolvedFileControlBlock {
1975 block_offset,
1976 allocated_blocks,
1977 valid_bytes,
1978 nonce,
1979 authentication_tag,
1980 attributes,
1981 encryption_key,
1982 } = fcb;
1983
1984 (
1985 FileId(file_id),
1986 ResolvedFileControlBlock {
1987 block_offset,
1988 allocated_blocks,
1989 valid_bytes,
1990 nonce,
1991 authentication_tag,
1992 attributes: FileAttribute::from(attributes),
1993 encryption_key,
1994 },
1995 )
1996 })
1997 .collect(),
1998 encryption_algorithm: EncryptionAlgorithm(encryption_algorithm),
1999 datastore_key_count,
2000 active_datastore_key_index,
2001 datastore_keys,
2002 unused_metadata_key: metadata_key,
2003 encrypted_metadata_keys: encrypted_metadata_keys.map(|k| {
2004 let state::SavedVmgsEncryptionKey {
2005 nonce,
2006 authentication_tag,
2007 encryption_key,
2008 } = k;
2009
2010 VmgsEncryptionKey {
2011 nonce,
2012 reserved: 0,
2013 authentication_tag,
2014 encryption_key,
2015 }
2016 }),
2017 reprovisioned,
2018 provisioning_reason: None,
2019 },
2020
2021 logger,
2022 }
2023 }
2024
2025 pub fn save(&self) -> state::SavedVmgsState {
2031 let Self {
2032 storage: _,
2033
2034 #[cfg(feature = "inspect")]
2035 stats: _,
2036
2037 state:
2038 VmgsState {
2039 active_header_index,
2040 active_header_sequence_number,
2041 version,
2042 fcbs,
2043 encryption_algorithm,
2044 datastore_key_count,
2045 active_datastore_key_index,
2046 datastore_keys,
2047 unused_metadata_key: metadata_key,
2048 encrypted_metadata_keys,
2049 reprovisioned,
2050 provisioning_reason: _,
2051 },
2052
2053 logger: _,
2054 } = self;
2055
2056 state::SavedVmgsState {
2057 active_header_index: *active_header_index,
2058 active_header_sequence_number: *active_header_sequence_number,
2059 version: *version,
2060 fcbs: fcbs
2061 .iter()
2062 .map(|(file_id, fcb)| {
2063 let ResolvedFileControlBlock {
2064 block_offset,
2065 allocated_blocks,
2066 valid_bytes,
2067 nonce,
2068 authentication_tag,
2069 attributes,
2070 encryption_key,
2071 } = fcb;
2072
2073 (
2074 file_id.0,
2075 state::SavedResolvedFileControlBlock {
2076 block_offset: *block_offset,
2077 allocated_blocks: *allocated_blocks,
2078 valid_bytes: *valid_bytes,
2079 nonce: *nonce,
2080 authentication_tag: *authentication_tag,
2081 attributes: (*attributes).into(),
2082 encryption_key: *encryption_key,
2083 },
2084 )
2085 })
2086 .collect(),
2087 encryption_algorithm: encryption_algorithm.0,
2088 datastore_key_count: *datastore_key_count,
2089 active_datastore_key_index: *active_datastore_key_index,
2090 datastore_keys: *datastore_keys,
2091 metadata_key: *metadata_key,
2092 encrypted_metadata_keys: std::array::from_fn(|i| {
2093 let VmgsEncryptionKey {
2094 nonce,
2095 reserved: _,
2096 authentication_tag,
2097 encryption_key,
2098 } = encrypted_metadata_keys[i];
2099
2100 state::SavedVmgsEncryptionKey {
2101 nonce,
2102 authentication_tag,
2103 encryption_key,
2104 }
2105 }),
2106 reprovisioned: *reprovisioned,
2107 }
2108 }
2109 }
2110}
2111
2112#[cfg(test)]
2113mod tests {
2114 use super::*;
2115 use pal_async::async_test;
2116 use parking_lot::Mutex;
2117 use std::sync::Arc;
2118 #[cfg(feature = "encryption")]
2119 use vmgs_format::VMGS_ENCRYPTION_KEY_SIZE;
2120 use vmgs_format::VmgsProvisioner;
2121
2122 const ONE_MEGA_BYTE: u64 = 1024 * 1024;
2123
2124 struct TestVmgsLogger {
2125 data: Arc<Mutex<String>>,
2126 }
2127
2128 #[async_trait::async_trait]
2129 impl VmgsLogger for TestVmgsLogger {
2130 async fn log_event_fatal(&self, _event: VmgsLogEvent) {
2131 let mut data = self.data.lock();
2132 *data = "test logger".to_string();
2133 }
2134 }
2135
2136 fn new_test_file() -> Disk {
2137 disklayer_ram::ram_disk(4 * ONE_MEGA_BYTE, false).unwrap()
2138 }
2139
2140 #[async_test]
2141 async fn empty_vmgs() {
2142 let disk = new_test_file();
2143
2144 let result = Vmgs::open(disk, None).await;
2145 assert!(matches!(result, Err(Error::EmptyFile)));
2146 }
2147
2148 #[async_test]
2149 async fn format_empty_vmgs() {
2150 let disk = new_test_file();
2151 let result = Vmgs::format_new(disk, None).await;
2152 assert!(result.is_ok());
2153 }
2154
2155 #[async_test]
2156 async fn basic_read_write() {
2157 let disk = new_test_file();
2158 let mut vmgs = Vmgs::format_new(disk, None).await.unwrap();
2159 assert_eq!(vmgs.state.active_header_index, 0);
2160 assert_eq!(vmgs.state.active_header_sequence_number, 1);
2161 assert_eq!(vmgs.state.version, VMGS_VERSION_3_0);
2162
2163 let buf = b"hello world";
2165 vmgs.write_file(FileId::BIOS_NVRAM, buf).await.unwrap();
2166
2167 assert_eq!(vmgs.state.active_header_index, 1);
2168 assert_eq!(vmgs.state.active_header_sequence_number, 2);
2169
2170 let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2172
2173 assert_eq!(buf, &*read_buf);
2174 assert_eq!(vmgs.state.active_header_index, 1);
2175 assert_eq!(vmgs.state.active_header_sequence_number, 2);
2176 }
2177
2178 #[async_test]
2179 async fn basic_read_write_large() {
2180 let disk = new_test_file();
2181 let mut vmgs = Vmgs::format_new(disk, None).await.unwrap();
2182
2183 let buf: Vec<u8> = (0..).map(|x| x as u8).take(1024 * 4 + 1).collect();
2185
2186 vmgs.write_file(FileId::BIOS_NVRAM, &buf).await.unwrap();
2187
2188 assert_eq!(vmgs.state.active_header_index, 1);
2189 assert_eq!(vmgs.state.active_header_sequence_number, 2);
2190
2191 let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2193
2194 assert_eq!(buf, read_buf);
2195 assert_eq!(vmgs.state.active_header_index, 1);
2196 assert_eq!(vmgs.state.active_header_sequence_number, 2);
2197
2198 let buf: Vec<u8> = (0..).map(|x| x as u8).take(1024 * 4 * 4 + 1).collect();
2200
2201 vmgs.write_file(FileId::TPM_PPI, &buf).await.unwrap();
2202
2203 assert_eq!(vmgs.state.active_header_index, 0);
2204 assert_eq!(vmgs.state.active_header_sequence_number, 3);
2205
2206 let read_buf = vmgs.read_file(FileId::TPM_PPI).await.unwrap();
2208
2209 assert_eq!(buf, read_buf);
2210 assert_eq!(vmgs.state.active_header_index, 0);
2211 assert_eq!(vmgs.state.active_header_sequence_number, 3);
2212
2213 let buf: Vec<u8> = (0..).map(|x| x as u8).take(1024 * 4 * 4 * 4 + 1).collect();
2215
2216 vmgs.write_file(FileId::GUEST_FIRMWARE, &buf).await.unwrap();
2217
2218 assert_eq!(vmgs.state.active_header_index, 1);
2219 assert_eq!(vmgs.state.active_header_sequence_number, 4);
2220
2221 let read_buf = vmgs.read_file(FileId::GUEST_FIRMWARE).await.unwrap();
2223
2224 assert_eq!(buf, read_buf);
2225 assert_eq!(vmgs.state.active_header_index, 1);
2226 assert_eq!(vmgs.state.active_header_sequence_number, 4);
2227 }
2228
2229 #[async_test]
2230 async fn move_delete() {
2231 let disk = new_test_file();
2232 let mut vmgs = Vmgs::format_new(disk, None).await.unwrap();
2233
2234 let buf = b"hello world";
2236 vmgs.write_file(FileId::TPM_NVRAM, buf).await.unwrap();
2237
2238 let read_buf = vmgs.read_file(FileId::TPM_NVRAM).await.unwrap();
2240 assert_eq!(buf, &*read_buf);
2241
2242 vmgs.move_file(FileId::TPM_NVRAM, FileId::ATTEST, false)
2244 .await
2245 .unwrap();
2246 vmgs.read_file(FileId::TPM_NVRAM).await.unwrap_err();
2247 let read_buf = vmgs.read_file(FileId::ATTEST).await.unwrap();
2248 assert_eq!(buf, &*read_buf);
2249
2250 vmgs.delete_file(FileId::ATTEST).await.unwrap();
2252 vmgs.read_file(FileId::ATTEST).await.unwrap_err();
2253 }
2254
2255 #[async_test]
2256 async fn open_existing_file() {
2257 let buf_1 = b"hello world";
2258 let buf_2 = b"short sentence";
2259 let buf_3 = b"funny joke";
2260
2261 let disk = new_test_file();
2263 let mut vmgs = Vmgs::format_new(disk.clone(), None).await.unwrap();
2264
2265 vmgs.write_file(FileId::BIOS_NVRAM, buf_1).await.unwrap();
2266
2267 assert_eq!(vmgs.state.active_header_index, 1);
2268 assert_eq!(vmgs.state.active_header_sequence_number, 2);
2269 assert_eq!(vmgs.state.fcbs[&FileId(0)].block_offset, 4);
2270 assert_eq!(vmgs.state.fcbs[&FileId(1)].block_offset, 5);
2271
2272 vmgs.write_file(FileId::TPM_PPI, buf_2).await.unwrap();
2273
2274 assert_eq!(vmgs.state.active_header_index, 0);
2275 assert_eq!(vmgs.state.active_header_sequence_number, 3);
2276 assert_eq!(vmgs.state.fcbs[&FileId(0)].block_offset, 2);
2277 assert_eq!(vmgs.state.fcbs[&FileId(1)].block_offset, 5);
2278 assert_eq!(vmgs.state.fcbs[&FileId(2)].block_offset, 6);
2279
2280 vmgs.write_file(FileId::BIOS_NVRAM, buf_3).await.unwrap();
2281
2282 assert_eq!(vmgs.state.active_header_index, 1);
2283 assert_eq!(vmgs.state.active_header_sequence_number, 4);
2284 assert_eq!(vmgs.state.fcbs[&FileId(0)].block_offset, 4);
2285 assert_eq!(vmgs.state.fcbs[&FileId(1)].block_offset, 7);
2286 assert_eq!(vmgs.state.fcbs[&FileId(2)].block_offset, 6);
2287
2288 drop(vmgs);
2290
2291 let mut vmgs = Vmgs::open(disk, None).await.unwrap();
2292
2293 assert_eq!(vmgs.state.fcbs[&FileId(0)].block_offset, 4);
2294 assert_eq!(vmgs.state.fcbs[&FileId(1)].block_offset, 7);
2295 assert_eq!(vmgs.state.fcbs[&FileId(2)].block_offset, 6);
2296 let read_buf_1 = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2297
2298 assert_eq!(buf_3, &*read_buf_1);
2299 assert_eq!(vmgs.state.active_header_index, 1);
2300 assert_eq!(vmgs.state.active_header_sequence_number, 4);
2301
2302 let read_buf_2 = vmgs.read_file(FileId::TPM_PPI).await.unwrap();
2303
2304 assert_eq!(buf_2, &*read_buf_2);
2305 assert_eq!(vmgs.state.fcbs[&FileId(0)].block_offset, 4);
2306 assert_eq!(vmgs.state.fcbs[&FileId(1)].block_offset, 7);
2307 assert_eq!(vmgs.state.fcbs[&FileId(2)].block_offset, 6);
2308 }
2309
2310 #[async_test]
2311 async fn multiple_read_write() {
2312 let disk = new_test_file();
2313 let mut vmgs = Vmgs::format_new(disk, None).await.unwrap();
2314
2315 let buf_1 = b"Data data data";
2316 let buf_2 = b"password";
2317 let buf_3 = b"other data data";
2318
2319 vmgs.write_file(FileId::BIOS_NVRAM, buf_1).await.unwrap();
2320 let read_buf_1 = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2321 assert_eq!(buf_1, &*read_buf_1);
2322 assert_eq!(vmgs.state.active_header_index, 1);
2323 assert_eq!(vmgs.state.active_header_sequence_number, 2);
2324
2325 vmgs.write_file(FileId::TPM_PPI, buf_2).await.unwrap();
2326 let info = vmgs.get_file_info(FileId::TPM_PPI).unwrap();
2327 assert_eq!(info.valid_bytes as usize, buf_2.len());
2328 let read_buf_2 = vmgs.read_file(FileId::TPM_PPI).await.unwrap();
2329 assert_eq!(buf_2, &*read_buf_2);
2330 assert_eq!(vmgs.state.active_header_index, 0);
2331 assert_eq!(vmgs.state.active_header_sequence_number, 3);
2332
2333 vmgs.write_file(FileId::BIOS_NVRAM, buf_3).await.unwrap();
2334 let read_buf_3 = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2335 assert_eq!(buf_3, &*read_buf_3);
2336 assert_eq!(vmgs.state.active_header_index, 1);
2337 assert_eq!(vmgs.state.active_header_sequence_number, 4);
2338
2339 vmgs.write_file(FileId::BIOS_NVRAM, buf_1).await.unwrap();
2340 let read_buf_1 = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2341 assert_eq!(buf_1, &*read_buf_1);
2342 assert_eq!(vmgs.state.active_header_index, 0);
2343 assert_eq!(vmgs.state.active_header_sequence_number, 5);
2344
2345 vmgs.write_file(FileId::TPM_PPI, buf_2).await.unwrap();
2346 let read_buf_2 = vmgs.read_file(FileId::TPM_PPI).await.unwrap();
2347 assert_eq!(buf_2, &*read_buf_2);
2348 assert_eq!(vmgs.state.active_header_index, 1);
2349 assert_eq!(vmgs.state.active_header_sequence_number, 6);
2350
2351 vmgs.write_file(FileId::BIOS_NVRAM, buf_3).await.unwrap();
2352 let read_buf_3 = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2353 assert_eq!(buf_3, &*read_buf_3);
2354 assert_eq!(vmgs.state.active_header_index, 0);
2355 assert_eq!(vmgs.state.active_header_sequence_number, 7);
2356 }
2357
2358 #[async_test]
2359 async fn test_insufficient_resources() {
2360 let disk = new_test_file();
2361 let mut vmgs = Vmgs::format_new(disk, None).await.unwrap();
2362
2363 let buf: Vec<u8> = vec![1; ONE_MEGA_BYTE as usize * 5];
2364 let result = vmgs.write_file(FileId::BIOS_NVRAM, &buf).await;
2365 assert!(result.is_err());
2366 if let Err(e) = result {
2367 match e {
2368 Error::InsufficientResources => (),
2369 _ => panic!("Wrong error returned"),
2370 }
2371 } else {
2372 panic!("Should have returned Insufficient resources error");
2373 }
2374 }
2375
2376 #[async_test]
2377 async fn test_empty_write() {
2378 let disk = new_test_file();
2379 let mut vmgs = Vmgs::format_new(disk, None).await.unwrap();
2380
2381 let buf: Vec<u8> = Vec::new();
2382 vmgs.write_file(FileId::BIOS_NVRAM, &buf).await.unwrap();
2383
2384 let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2386
2387 assert_eq!(buf, read_buf);
2388 assert_eq!(read_buf.len(), 0);
2389 assert_eq!(vmgs.state.active_header_index, 1);
2390 assert_eq!(vmgs.state.active_header_sequence_number, 2);
2391 }
2392
2393 #[test]
2395 fn test_block_count_to_byte_count() {
2396 let block_count = 10;
2397 let byte_count = block_count_to_byte_count(block_count);
2398 assert!(byte_count == block_count as u64 * VMGS_BYTES_PER_BLOCK as u64);
2399 }
2400
2401 #[test]
2402 fn test_validate_header() {
2403 let mut header = VmgsHeader::new_zeroed();
2404 header.signature = VMGS_SIGNATURE;
2405 header.version = VMGS_VERSION_3_0;
2406 header.header_size = size_of::<VmgsHeader>() as u32;
2407 header.file_table_offset = VMGS_MIN_FILE_BLOCK_OFFSET;
2408 header.file_table_size = VMGS_FILE_TABLE_BLOCK_SIZE;
2409 header.checksum = compute_crc32(header.as_bytes());
2410
2411 let result = validate_header(&header);
2412 assert!(result.is_ok());
2413
2414 let mut header_signature = header.clone();
2415 header_signature.signature = 0;
2416 header_signature.checksum = 0;
2417 header_signature.checksum = compute_crc32(header_signature.as_bytes());
2418 let result = validate_header(&header_signature);
2419 match result {
2420 Err(Error::InvalidFormat(err)) => assert_eq!(err, "Invalid header signature"),
2421 _ => panic!(),
2422 };
2423
2424 let mut header_version = header.clone();
2425 header_version.version = 0;
2426 header_version.checksum = 0;
2427 header_version.checksum = compute_crc32(header_version.as_bytes());
2428 match validate_header(&header_version) {
2429 Err(Error::InvalidFormat(err)) => assert_eq!(err, "Invalid header version"),
2430 _ => panic!(),
2431 };
2432
2433 let mut header_header_size = header.clone();
2434 header_header_size.header_size = 0;
2435 header_header_size.checksum = 0;
2436 header_header_size.checksum = compute_crc32(header_header_size.as_bytes());
2437 match validate_header(&header_header_size) {
2438 Err(Error::InvalidFormat(err)) => assert_eq!(err, "Invalid header size"),
2439 _ => panic!(),
2440 };
2441
2442 let mut header_ft_offset = header.clone();
2443 header_ft_offset.file_table_offset = 0;
2444 header_ft_offset.checksum = 0;
2445 header_ft_offset.checksum = compute_crc32(header_ft_offset.as_bytes());
2446 match validate_header(&header_ft_offset) {
2447 Err(Error::InvalidFormat(err)) => assert_eq!(err, "Invalid file table offset"),
2448 _ => panic!(),
2449 };
2450
2451 let mut header_ft_size = header.clone();
2452 header_ft_size.file_table_size = 0;
2453 header_ft_size.checksum = 0;
2454 header_ft_size.checksum = compute_crc32(header_ft_size.as_bytes());
2455 match validate_header(&header_ft_size) {
2456 Err(Error::InvalidFormat(err)) => assert_eq!(err, "Invalid file table size"),
2457 _ => panic!(),
2458 };
2459 }
2460
2461 #[test]
2462 fn test_initialize_file_metadata() {
2463 let mut file_table = VmgsFileTable::new_zeroed();
2464
2465 file_table.entries[0].offset = 6;
2466 file_table.entries[0].allocation_size = 1;
2467 file_table.entries[1].offset = 2;
2468 file_table.entries[1].allocation_size = 1;
2469 file_table.entries[2].offset = 4;
2470 file_table.entries[2].allocation_size = 5;
2471 file_table.entries[3].offset = 3;
2472 file_table.entries[3].allocation_size = 3;
2473
2474 let block_capacity = 1000;
2475
2476 let fcbs = initialize_file_metadata(&file_table, VMGS_VERSION_3_0, block_capacity).unwrap();
2477 assert!(fcbs[&FileId(0)].block_offset == 6);
2479 assert!(fcbs[&FileId(0)].allocated_blocks.get() == 1);
2480 assert!(fcbs[&FileId(1)].block_offset == 2);
2481 assert!(fcbs[&FileId(1)].allocated_blocks.get() == 1);
2482 assert!(fcbs[&FileId(2)].block_offset == 4);
2483 assert!(fcbs[&FileId(2)].allocated_blocks.get() == 5);
2484 assert!(fcbs[&FileId(3)].block_offset == 3);
2485 assert!(fcbs[&FileId(3)].allocated_blocks.get() == 3);
2486 }
2487
2488 #[test]
2489 fn test_round_up_count() {
2490 assert!(round_up_count(0, 4096) == 0);
2491 assert!(round_up_count(1, 4096) == 4096);
2492 assert!(round_up_count(4095, 4096) == 4096);
2493 assert!(round_up_count(4096, 4096) == 4096);
2494 assert!(round_up_count(4097, 4096) == 8192);
2495 }
2496
2497 #[async_test]
2498 async fn test_header_sequence_overflow() {
2499 let disk = new_test_file();
2500 let mut vmgs = Vmgs::format_new(disk, None).await.unwrap();
2501
2502 vmgs.state.active_header_sequence_number = u32::MAX;
2503
2504 let buf = b"hello world";
2506 vmgs.write_file(FileId::BIOS_NVRAM, buf).await.unwrap();
2507
2508 assert_eq!(vmgs.state.active_header_index, 1);
2509 assert_eq!(vmgs.state.active_header_sequence_number, 0);
2510
2511 vmgs.state.active_header_index = 0;
2512 vmgs.state.active_header_sequence_number = u32::MAX;
2513
2514 let mut temp_state = vmgs.temp_state();
2515
2516 let (new_header, index) = temp_state.make_header();
2517 vmgs.write_header_internal(&new_header, index)
2518 .await
2519 .unwrap();
2520 vmgs.apply(temp_state);
2521
2522 assert_eq!(vmgs.state.active_header_index, 1);
2523 assert_eq!(vmgs.state.active_header_sequence_number, 0);
2524 assert_eq!(new_header.sequence, 0);
2525 }
2526
2527 #[cfg(feature = "encryption")]
2528 #[async_test]
2529 async fn write_file_v3() {
2530 let disk = new_test_file();
2531 let mut vmgs = Vmgs::format_new(disk.clone(), None).await.unwrap();
2532 let encryption_key = [12; VMGS_ENCRYPTION_KEY_SIZE];
2533
2534 let buf = b"hello world";
2536 let buf_1 = b"hello universe";
2537 vmgs.write_file(FileId::BIOS_NVRAM, buf).await.unwrap();
2538 vmgs.update_encryption_key(&encryption_key, EncryptionAlgorithm::AES_GCM)
2539 .await
2540 .unwrap();
2541 vmgs.write_file_encrypted(FileId::TPM_PPI, buf_1)
2542 .await
2543 .unwrap();
2544
2545 let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2547 assert_eq!(buf, &*read_buf);
2548 let info = vmgs.get_file_info(FileId::TPM_PPI).unwrap();
2549 assert_eq!(info.valid_bytes as usize, buf_1.len());
2550 let read_buf = vmgs.read_file(FileId::TPM_PPI).await.unwrap();
2551 assert_eq!(buf_1, &*read_buf);
2552
2553 drop(vmgs);
2555 let mut vmgs = Vmgs::open(disk, None).await.unwrap();
2556 let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2557 assert_eq!(buf, read_buf.as_bytes());
2558 let info = vmgs.get_file_info(FileId::TPM_PPI).unwrap();
2559 assert_eq!(info.valid_bytes as usize, buf_1.len());
2560 let read_buf = vmgs.read_file_raw(FileId::TPM_PPI).await.unwrap();
2561 assert_ne!(buf_1, read_buf.as_bytes());
2562
2563 vmgs.unlock_with_encryption_key(&encryption_key)
2565 .await
2566 .unwrap();
2567 let info = vmgs.get_file_info(FileId::TPM_PPI).unwrap();
2568 assert_eq!(info.valid_bytes as usize, buf_1.len());
2569 let read_buf = vmgs.read_file(FileId::TPM_PPI).await.unwrap();
2570 assert_eq!(buf_1, &*read_buf);
2571 }
2572
2573 #[cfg(feature = "encryption")]
2574 #[async_test]
2575 async fn overwrite_file_v3() {
2576 let disk = new_test_file();
2577 let mut vmgs = Vmgs::format_new(disk, None).await.unwrap();
2578 let encryption_key = [1; VMGS_ENCRYPTION_KEY_SIZE];
2579 let buf = vec![1; 8 * 1024];
2580 let buf_1 = vec![2; 8 * 1024];
2581
2582 vmgs.add_new_encryption_key(&encryption_key, EncryptionAlgorithm::AES_GCM)
2584 .await
2585 .unwrap();
2586 assert_eq!(vmgs.state.active_datastore_key_index, Some(0));
2587
2588 vmgs.write_file_encrypted(FileId::BIOS_NVRAM, &buf)
2590 .await
2591 .unwrap();
2592
2593 vmgs.write_file_encrypted(FileId::BIOS_NVRAM, &buf_1)
2595 .await
2596 .unwrap();
2597
2598 let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2600 assert_eq!(buf_1, read_buf);
2601 }
2602
2603 #[cfg(feature = "encryption")]
2604 #[async_test]
2605 async fn file_encryption() {
2606 let buf: Vec<u8> = (0..255).collect();
2607 let encryption_key = [1; VMGS_ENCRYPTION_KEY_SIZE];
2608
2609 let disk = new_test_file();
2610 let mut vmgs = Vmgs::format_new(disk.clone(), None).await.unwrap();
2611
2612 vmgs.add_new_encryption_key(&encryption_key, EncryptionAlgorithm::AES_GCM)
2614 .await
2615 .unwrap();
2616 assert_eq!(vmgs.state.active_datastore_key_index, Some(0));
2617
2618 vmgs.write_file_encrypted(FileId::BIOS_NVRAM, &buf)
2620 .await
2621 .unwrap();
2622
2623 let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2625 assert_eq!(buf, read_buf);
2626
2627 drop(vmgs);
2628
2629 let mut vmgs = Vmgs::open(disk, None).await.unwrap();
2631
2632 let info = vmgs.get_file_info(FileId::BIOS_NVRAM).unwrap();
2633 assert_eq!(info.valid_bytes as usize, buf.len());
2634
2635 vmgs.unlock_with_encryption_key(&encryption_key)
2638 .await
2639 .unwrap();
2640
2641 assert_eq!(vmgs.state.active_datastore_key_index, Some(0));
2642
2643 let new_encryption_key = [2; VMGS_ENCRYPTION_KEY_SIZE];
2645 vmgs.add_new_encryption_key(&new_encryption_key, EncryptionAlgorithm::AES_GCM)
2646 .await
2647 .unwrap();
2648 assert_eq!(vmgs.state.active_datastore_key_index, Some(1));
2649 vmgs.remove_encryption_key(0).await.unwrap();
2650
2651 let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2652 assert_eq!(buf, read_buf);
2653 }
2654
2655 #[cfg(feature = "encryption")]
2656 #[async_test]
2657 async fn add_new_encryption_key() {
2658 let buf: Vec<u8> = (0..255).collect();
2659 let encryption_key = [1; VMGS_ENCRYPTION_KEY_SIZE];
2660 let new_encryption_key = [5; VMGS_ENCRYPTION_KEY_SIZE];
2661
2662 let disk = new_test_file();
2664 let mut vmgs = Vmgs::format_new(disk.clone(), None).await.unwrap();
2665
2666 vmgs.add_new_encryption_key(&encryption_key, EncryptionAlgorithm::AES_GCM)
2668 .await
2669 .unwrap();
2670 assert_eq!(vmgs.state.active_datastore_key_index, Some(0));
2671
2672 vmgs.write_file_encrypted(FileId::BIOS_NVRAM, &buf)
2674 .await
2675 .unwrap();
2676
2677 drop(vmgs);
2679 let mut vmgs = Vmgs::open(disk.clone(), None).await.unwrap();
2680 vmgs.unlock_with_encryption_key(&encryption_key)
2681 .await
2682 .unwrap();
2683 assert_eq!(vmgs.state.active_datastore_key_index, Some(0));
2684 let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2685 assert_eq!(read_buf, buf);
2686
2687 vmgs.add_new_encryption_key(&new_encryption_key, EncryptionAlgorithm::AES_GCM)
2689 .await
2690 .unwrap();
2691 assert_eq!(vmgs.state.active_datastore_key_index, Some(1));
2692
2693 drop(vmgs);
2695 let mut vmgs = Vmgs::open(disk, None).await.unwrap();
2696 vmgs.unlock_with_encryption_key(&encryption_key)
2697 .await
2698 .unwrap();
2699 assert_eq!(vmgs.state.active_datastore_key_index, Some(0));
2700 vmgs.unlock_with_encryption_key(&new_encryption_key)
2701 .await
2702 .unwrap();
2703 assert_eq!(vmgs.state.active_datastore_key_index, Some(1));
2704 let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2705 assert_eq!(read_buf, buf);
2706
2707 vmgs.remove_encryption_key(1).await.unwrap();
2709 vmgs.add_new_encryption_key(&new_encryption_key, EncryptionAlgorithm::AES_GCM)
2710 .await
2711 .unwrap();
2712 assert_eq!(vmgs.state.active_datastore_key_index, Some(1));
2713
2714 vmgs.remove_encryption_key(0).await.unwrap();
2716 let result = vmgs.unlock_with_encryption_key(&encryption_key).await;
2717 assert!(matches!(result, Err(Error::DecryptMetadataKey)));
2718
2719 let result = vmgs.remove_encryption_key(0).await;
2721 assert!(matches!(result, Err(Error::InvalidArgument(_))));
2722
2723 vmgs.remove_encryption_key(1).await.unwrap();
2725 let read_buf = vmgs.read_file_raw(FileId::BIOS_NVRAM).await;
2726 assert_ne!(read_buf.unwrap(), buf);
2727 }
2728
2729 #[cfg(feature = "encryption")]
2730 #[async_test]
2731 async fn test_write_file_encrypted() {
2732 let disk = new_test_file();
2736 let mut vmgs = Vmgs::format_new(disk.clone(), None).await.unwrap();
2737 let buf = b"This is plaintext";
2738
2739 vmgs.write_file_encrypted(FileId::BIOS_NVRAM, buf)
2741 .await
2742 .unwrap();
2743
2744 let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2746 assert_eq!(vmgs.state.encryption_algorithm, EncryptionAlgorithm::NONE);
2747 assert_eq!(buf, &*read_buf);
2748
2749 drop(vmgs);
2752 let mut vmgs = Vmgs::open(disk, None).await.unwrap();
2753
2754 let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2755 assert_eq!(vmgs.state.encryption_algorithm, EncryptionAlgorithm::NONE);
2756 assert_eq!(buf, &*read_buf);
2757 }
2758
2759 #[cfg(feature = "encryption")]
2760 #[async_test]
2761 async fn test_logger() {
2762 let disk = new_test_file();
2763 let data = Arc::new(Mutex::new(String::new()));
2764 let mut vmgs = Vmgs::format_new(
2765 disk.clone(),
2766 Some(Arc::new(TestVmgsLogger { data: data.clone() })),
2767 )
2768 .await
2769 .unwrap();
2770 let encryption_key = [12; VMGS_ENCRYPTION_KEY_SIZE];
2771
2772 let buf = b"hello world";
2774 vmgs.update_encryption_key(&encryption_key, EncryptionAlgorithm::AES_GCM)
2775 .await
2776 .unwrap();
2777 vmgs.write_file_encrypted(FileId::BIOS_NVRAM, buf)
2778 .await
2779 .unwrap();
2780
2781 let fcb = vmgs.state.fcbs.get_mut(&FileId::BIOS_NVRAM).unwrap();
2782
2783 fcb.nonce[0] ^= 1;
2785
2786 let result = vmgs.read_file(FileId::BIOS_NVRAM).await;
2788 assert!(result.is_err());
2789
2790 let result = data.lock();
2792 assert_eq!(*result, "test logger");
2793 }
2794
2795 #[cfg(feature = "encryption")]
2796 #[async_test]
2797 async fn update_key() {
2798 let buf: Vec<u8> = (0..255).collect();
2799 let encryption_key = [1; VMGS_ENCRYPTION_KEY_SIZE];
2800
2801 let disk = new_test_file();
2802 let mut vmgs = Vmgs::format_new(disk.clone(), None).await.unwrap();
2803
2804 vmgs.update_encryption_key(&encryption_key, EncryptionAlgorithm::AES_GCM)
2806 .await
2807 .unwrap();
2808 assert_eq!(vmgs.state.active_datastore_key_index, Some(0));
2809 assert_eq!(vmgs.state.datastore_key_count, 1);
2810
2811 vmgs.write_file_encrypted(FileId::BIOS_NVRAM, &buf)
2813 .await
2814 .unwrap();
2815
2816 let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2818 assert_eq!(buf, read_buf);
2819
2820 drop(vmgs);
2822 let mut vmgs = Vmgs::open(disk.clone(), None).await.unwrap();
2823
2824 vmgs.unlock_with_encryption_key(&encryption_key)
2826 .await
2827 .unwrap();
2828 assert_eq!(vmgs.state.active_datastore_key_index, Some(0));
2829
2830 let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2832 assert_eq!(buf, read_buf);
2833
2834 let new_encryption_key = [2; VMGS_ENCRYPTION_KEY_SIZE];
2836 vmgs.update_encryption_key(&new_encryption_key, EncryptionAlgorithm::AES_GCM)
2837 .await
2838 .unwrap();
2839 assert_eq!(vmgs.state.active_datastore_key_index, Some(1));
2840 assert_eq!(vmgs.state.datastore_key_count, 1);
2841
2842 let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2844 assert_eq!(buf, read_buf);
2845
2846 drop(vmgs);
2848 let mut vmgs = Vmgs::open(disk, None).await.unwrap();
2849
2850 vmgs.unlock_with_encryption_key(&new_encryption_key)
2852 .await
2853 .unwrap();
2854 assert_eq!(vmgs.state.active_datastore_key_index, Some(1));
2855
2856 let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2858 assert_eq!(buf, read_buf);
2859 }
2860
2861 #[cfg(feature = "encryption")]
2862 #[async_test]
2863 async fn update_key_no_space() {
2864 let buf: Vec<u8> = (0..255).collect();
2865 let encryption_key = [1; VMGS_ENCRYPTION_KEY_SIZE];
2866
2867 let disk = new_test_file();
2868 let mut vmgs = Vmgs::format_new(disk.clone(), None).await.unwrap();
2869
2870 vmgs.update_encryption_key(&encryption_key, EncryptionAlgorithm::AES_GCM)
2872 .await
2873 .unwrap();
2874 assert_eq!(vmgs.state.active_datastore_key_index, Some(0));
2875 assert_eq!(vmgs.state.datastore_key_count, 1);
2876
2877 vmgs.write_file_encrypted(FileId::BIOS_NVRAM, &buf)
2879 .await
2880 .unwrap();
2881
2882 let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2884 assert_eq!(buf, read_buf);
2885
2886 let new_encryption_key = [2; VMGS_ENCRYPTION_KEY_SIZE];
2888 vmgs.add_new_encryption_key(&new_encryption_key, EncryptionAlgorithm::AES_GCM)
2889 .await
2890 .unwrap();
2891 assert_eq!(vmgs.state.active_datastore_key_index, Some(1));
2892 assert_eq!(vmgs.state.datastore_key_count, 2);
2893
2894 drop(vmgs);
2896 let mut vmgs = Vmgs::open(disk.clone(), None).await.unwrap();
2897
2898 vmgs.unlock_with_encryption_key(&new_encryption_key)
2900 .await
2901 .unwrap();
2902 assert_eq!(vmgs.state.active_datastore_key_index, Some(1));
2903
2904 let another_encryption_key = [2; VMGS_ENCRYPTION_KEY_SIZE];
2906 vmgs.update_encryption_key(&another_encryption_key, EncryptionAlgorithm::AES_GCM)
2907 .await
2908 .unwrap();
2909 assert_eq!(vmgs.state.active_datastore_key_index, Some(0));
2910 assert_eq!(vmgs.state.datastore_key_count, 1);
2911
2912 drop(vmgs);
2914 let mut vmgs = Vmgs::open(disk, None).await.unwrap();
2915
2916 vmgs.unlock_with_encryption_key(&another_encryption_key)
2918 .await
2919 .unwrap();
2920 assert_eq!(vmgs.state.active_datastore_key_index, Some(0));
2921
2922 let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2924 assert_eq!(buf, read_buf);
2925 }
2926
2927 #[test]
2928 fn test_allocate_helper() {
2929 let block_capacity =
2930 (vmgs_format::VMGS_DEFAULT_CAPACITY / (VMGS_BYTES_PER_BLOCK as u64)) as u32;
2931 assert_eq!(block_capacity, 1024);
2933
2934 let mut allocation_list = Vec::new();
2935
2936 assert_eq!(
2938 allocate_helper(&mut allocation_list, 3, block_capacity).unwrap(),
2939 2
2940 );
2941 assert_eq!(
2942 allocate_helper(&mut allocation_list, 95, block_capacity).unwrap(),
2943 5
2944 );
2945 assert_eq!(
2946 allocate_helper(&mut allocation_list, 2, block_capacity).unwrap(),
2947 100
2948 );
2949
2950 allocation_list.remove(0);
2952
2953 assert_eq!(
2954 allocate_helper(&mut allocation_list, 1, block_capacity).unwrap(),
2955 2
2956 );
2957 assert_eq!(
2958 allocate_helper(&mut allocation_list, 3, block_capacity).unwrap(),
2959 102
2960 );
2961 assert_eq!(
2962 allocate_helper(&mut allocation_list, 2, block_capacity).unwrap(),
2963 3
2964 );
2965
2966 let mut allocation_list = Vec::new();
2968
2969 allocate_helper(&mut allocation_list, 1025, block_capacity).unwrap_err();
2970 assert_eq!(
2971 allocate_helper(&mut allocation_list, 511, block_capacity).unwrap(),
2972 2
2973 );
2974 assert_eq!(
2975 allocate_helper(&mut allocation_list, 511, block_capacity).unwrap(),
2976 513
2977 );
2978 allocate_helper(&mut allocation_list, 1, block_capacity).unwrap_err();
2979 }
2980
2981 #[async_test]
2982 async fn test_provisioning_marker() {
2983 const EXPECTED_MARKER: &str = r#"{"provisioner":"openhcl","reason":"empty","tpm_version":"1.38","tpm_nvram_size":32768,"akcert_size":4096,"akcert_attrs":"0x42060004","provisioner_version":"unit test"}"#;
2984
2985 let disk = new_test_file();
2986 let data = Arc::new(Mutex::new(String::new()));
2987 let mut vmgs = Vmgs::format_new_with_reason(
2988 disk.clone(),
2989 VmgsProvisioningReason::Empty,
2990 Some(Arc::new(TestVmgsLogger { data: data.clone() })),
2991 )
2992 .await
2993 .unwrap();
2994
2995 let marker = VmgsProvisioningMarker {
2996 provisioner: VmgsProvisioner::OpenHcl,
2997 reason: vmgs.provisioning_reason().unwrap(),
2998 tpm_version: "1.38".to_string(),
2999 tpm_nvram_size: 32768,
3000 akcert_size: 4096,
3001 akcert_attrs: "0x42060004".to_string(),
3002 provisioner_version: "unit test".to_string(),
3003 };
3004
3005 vmgs.write_provisioning_marker(&marker).await.unwrap();
3006
3007 let read_buf = vmgs.read_file(FileId::PROVISIONING_MARKER).await.unwrap();
3008 assert_eq!(EXPECTED_MARKER.as_bytes(), read_buf);
3009 }
3010}