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