Skip to main content

vmgs/
vmgs_impl.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4use 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/// Operation types for provisioning telemetry.
45#[derive(Debug)]
46enum LogOpType {
47    VmgsProvision,
48}
49
50/// Info about a specific VMGS file.
51#[derive(Debug)]
52#[cfg_attr(feature = "mesh", derive(mesh_protobuf::Protobuf))]
53pub struct VmgsFileInfo {
54    /// Number of bytes allocated in the file.
55    pub allocated_bytes: u64,
56    /// Number of valid bytes in the file.
57    pub valid_bytes: u64,
58    /// Whether this file is encrypted.
59    pub encrypted: bool,
60}
61
62/// GSP types that can be used to encrypt a VMGS file.
63#[derive(Debug, Clone, Copy)]
64pub enum GspType {
65    /// No GSP
66    None,
67    /// GSP by ID
68    GspById,
69    /// GSP key
70    GspKey,
71}
72
73// Aggregates fully validated data from the FILE_TABLE and EXTENDED_FILE_TABLE
74// control blocks.
75#[derive(Clone, PartialEq, Eq, Debug)]
76#[cfg_attr(feature = "inspect", derive(Inspect))]
77struct ResolvedFileControlBlock {
78    // FILE_TABLE data
79    // ---------------
80    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    // EXTENDED_FILE_TABLE data
89    // ---------------
90    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            // sanity check: encrypted data should never be all zeros. if we
224            // find that it is all-zeroes, then that's indicative of some kind
225            // of logic error / data corruption
226            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        // Always allocate at least one block, to allow for zero sized data buffers
313        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/// Implementation of the VMGS file format, backed by a generic [`Disk`]
352/// device.
353#[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, retained for save-restore backwards compatibility
380    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    // explicit inspect implementation, since we want to massage the data's
398    // presentation a bit
399    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    /// Attempt to open the VMGS file, optionally formatting if it is
431    /// empty or corrupted.
432    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                    // The data store format is invalid or not supported.
451                    Error::InvalidFormat(_) => VmgsLogEvent::InvalidFormat,
452                    // The data store is corrupted.
453                    Error::CorruptFormat(_) => VmgsLogEvent::CorruptFormat,
454                    // All other errors
455                    _ => VmgsLogEvent::InitFailed,
456                };
457
458                logger.log_event_fatal(event_log_id).await;
459                Err(err)
460            }
461        }
462    }
463
464    /// Open the VMGS file.
465    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    /// Format and open a new VMGS file.
472    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    /// Format and open a new VMGS file.
480    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    /// Format and open a new VMGS file.
496    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                // set the reprovisioned marker to prevent the vmgs from
518                // repeatedly being reset
519                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        // clear the reprovisioned marker after successfully opening the vmgs
536        // without being requested to reprovision.
537        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    /// Formats the backing store with initial metadata, and sets active header.
606    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        // zero out the active header, the other one will be populated below
617        vmgs.write_header_internal(&VmgsHeader::new_zeroed(), vmgs.state.active_header_index)
618            .await?;
619
620        // write a blank, unencrypted file table for consistency with old impls
621        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        // write a blank file table
632        vmgs.write_files_internal(files, None).await?;
633
634        // write the active header
635        let (new_header, index) = vmgs.state.make_header();
636        vmgs.write_header_internal(&new_header, index).await?;
637
638        // Flush the device to persist changes
639        vmgs.storage.flush().await.map_err(Error::FlushDisk)?;
640
641        Ok(vmgs)
642    }
643
644    /// Get allocated and valid bytes from File Control Block for file_id.
645    ///
646    /// When reading data from a file, the buffer must be at least `valid_bytes` long.
647    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    /// Returns whether a file id is allocated
657    pub fn check_file_allocated(&self, file_id: FileId) -> bool {
658        self.state.fcbs.contains_key(&file_id)
659    }
660
661    /// Get info about all the files currently in the file table
662    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    /// Writes `buf` to a file_id, optionally encrypting or overwriting
674    /// encrypted data with plaintext. Updates file tables as appropriate.
675    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        // Update the header
730        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    /// Write a set of files and any necessary file tables
744    async fn write_files_internal<'a>(
745        &mut self,
746        // using a BTreeMap here so that the allocations are predictable
747        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        // ensure the necessary file tables are in the allocation list
753        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        // allocate space for the files
765        let mut files = state.allocate_space(files, self.storage.block_capacity())?;
766
767        // encrypt anything that needs to be encrypted except the extended file
768        // table, which hasn't been generated yet.
769        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        // generate and encrypt the extended file table
779        if let Some(res) = files.get_mut(&FileId::EXTENDED_FILE_TABLE) {
780            if state.encrypted_and_unlocked() {
781                // clear encryption key so we don't try to decrypt with the old key
782                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            // add the blank table if specified, even if not encrypted
787            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        // generate the file table now that all of the nonces and auth tags
796        // are in the temporary fcbs
797        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        // write the files
805        for (_, res) in files.iter() {
806            self.write_file_internal(&res.fcb, res.data.get()).await?;
807        }
808
809        Ok(())
810    }
811
812    /// Writes `buf` to the block offset specified in the file control block.
813    /// Encrypts the data and returns the auth tag if applicable.
814    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    /// write the new header to disk.
835    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    /// Reads the specified `file_id`, decrypting its contents.
852    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    /// Reads the specified `file_id`, but does not decrypt the contents.
857    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    /// User-facing file read
862    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    /// read a file_id, decrypting if requested and possible
889    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        // read the file
903        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        // decrypt if necessary
922        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    /// Writes `buf` to a file_id without encrypting it.
945    ///
946    /// If the file is already encrypted, this will return a failure. Use
947    /// [`Self::write_file_allow_overwrite_encrypted`] if you want to allow
948    /// this.
949    ///
950    /// To write encrypted data, use `write_file_encrypted` instead.
951    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    /// Writes `buf` to a file_id without encrypting it, allowing overwrites of
956    /// an already-encrypted file.
957    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    /// Encrypts `buf` and writes the encrypted payload to a file_id if the VMGS file has encryption configured.
966    /// If the VMGS doesn't have encryption configured, will do a plaintext write instead.
967    #[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    /// Move a file to a new file_id
973    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        // move the fcb to a different file id
993        let fcb = temp_state
994            .fcbs
995            .remove(&src)
996            .ok_or(Error::FileInfoNotAllocated(src))?;
997        temp_state.fcbs.insert(dst, fcb);
998
999        // write the new file table(s)
1000        self.write_files_internal(BTreeMap::new(), Some(&mut temp_state))
1001            .await?;
1002
1003        // Update the header
1004        self.write_header_and_apply(temp_state).await?;
1005
1006        Ok(())
1007    }
1008
1009    /// Delete a file
1010    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        // delete the fcb
1018        temp_state
1019            .fcbs
1020            .remove(&file_id)
1021            .ok_or(Error::FileInfoNotAllocated(file_id))?;
1022
1023        // write the new file table(s)
1024        self.write_files_internal(BTreeMap::new(), Some(&mut temp_state))
1025            .await?;
1026
1027        // Update the header
1028        self.write_header_and_apply(temp_state).await?;
1029
1030        Ok(())
1031    }
1032
1033    /// Decrypts the extended file table by the encryption_key and
1034    /// updates the related metadata in memory.
1035    #[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        // Iterate through two metadata keys and get the index of the valid key which can be successfully
1050        // decrypted by the encryption_key, as well as set the decrypted key as the VMGS's metadata key
1051        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                // older implementations didn't write unencrypted attributes
1080                // so configure them here so that the table is decrypted below
1081                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        // Read and decrypt the extended file table
1103        let extended_file_table_buffer = self
1104            .read_file_internal(FileId::EXTENDED_FILE_TABLE, true, Some(&temp_state))
1105            .await?;
1106
1107        // Update the cached extended file table
1108        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    /// Associates a new root key with the data store and removes the old
1124    /// encryption key, if it exists. If two keys already exist, the
1125    /// inactive key is removed first.
1126    #[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    /// Associates a new root key with the data store.
1162    #[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        // zero out the keys to ensure we get a new nonce
1202        temp_state.encrypted_metadata_keys[new_key_index] = VmgsEncryptionKey::new_zeroed();
1203
1204        // Allocate and write the new file tables
1205        if self.state.datastore_key_count == 0 {
1206            self.write_files_internal(BTreeMap::new(), Some(&mut temp_state))
1207                .await?;
1208        } else {
1209            // the extended file table should already exist, but we still need
1210            // to re-encrypt the metadata key.
1211            temp_state.encrypt_metadata_key()?;
1212        }
1213
1214        // Update the header on the storage device
1215        self.write_header_and_apply(temp_state).await?;
1216
1217        Ok(())
1218    }
1219
1220    /// Disassociates the root key at the specified index from the data store.
1221    #[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        // Remove the corresponding datastore_key
1238        temp_state.datastore_keys[key_index].fill(0);
1239
1240        // Remove the corresponding metadata_key
1241        temp_state.encrypted_metadata_keys[key_index] = VmgsEncryptionKey::new_zeroed();
1242
1243        // Update cached metadata
1244        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    /// Gets the encryption algorithm of the VMGS
1265    pub fn get_encryption_algorithm(&self) -> EncryptionAlgorithm {
1266        self.state.encryption_algorithm
1267    }
1268
1269    /// Whether the VMGS file is encrypted
1270    pub fn encrypted(&self) -> bool {
1271        self.state.encrypted()
1272    }
1273
1274    /// Whether the VMGS file was provisioned during the most recent boot
1275    pub fn was_provisioned_this_boot(&self) -> bool {
1276        self.state.provisioning_reason.is_some()
1277    }
1278
1279    /// Why this VMGS file was provisioned
1280    pub fn provisioning_reason(&self) -> Option<VmgsProvisioningReason> {
1281        self.state.provisioning_reason
1282    }
1283
1284    /// Write a provisioning marker to this VMGS file
1285    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    /// Get temporary Vmgs state
1307    fn temp_state(&self) -> VmgsState {
1308        self.state.clone()
1309    }
1310
1311    /// Apply the temporary Vmgs state
1312    fn apply(&mut self, temp_state: VmgsState) {
1313        self.state = temp_state;
1314    }
1315
1316    /// Apply the temporary Vmgs state
1317    async fn write_header_and_apply(&mut self, mut temp_state: VmgsState) -> Result<(), Error> {
1318        // Data must be hardened on persistent storage before the header is updated.
1319        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    /// Initializes a new VMGS header populated using the temporary state,
1377    /// which is updated to point to the new header.
1378    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    /// Whether the VMGS file is encrypted
1403    fn encrypted(&self) -> bool {
1404        self.encryption_algorithm != EncryptionAlgorithm::NONE
1405    }
1406
1407    /// Whether the VMGS file is unlocked
1408    fn unlocked(&self) -> bool {
1409        self.active_datastore_key_index.is_some()
1410    }
1411
1412    /// Whether the VMGS file is encrypted and unlocked
1413    fn encrypted_and_unlocked(&self) -> bool {
1414        self.encrypted() && self.unlocked()
1415    }
1416
1417    /// Update the metadata key
1418    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    /// Copies current file metadata to a file table structure.
1455    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    /// Copies current file metadata to an extended file table structure.
1464    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    /// maps out the used/unused space in the file and finds the smallest
1473    /// unused space to allocate new data.
1474    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        // populate the allocation list with any existing files
1480        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        // allocate space for the new files
1490        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/// Additional test-only functions for use in other crates that reveal
1500/// implmentation details of the vmgs datastore encryption keys.
1501#[cfg(feature = "test_helpers")]
1502mod test_helpers {
1503    use super::*;
1504
1505    impl Vmgs {
1506        /// Get the active datastore key index
1507        pub fn test_get_active_datastore_key_index(&self) -> Option<usize> {
1508            self.state.active_datastore_key_index
1509        }
1510
1511        /// Associates a new root key with the data store.
1512        #[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
1524/// Attempt to read both headers and separately return any validation errors
1525pub 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    // first_two_blocks will contain enough bytes to read the first two headers
1539    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; // TODO: zerocopy: use-rest-of-range (https://github.com/microsoft/openvmm/issues/759)
1547    let header_2 =
1548        VmgsHeader::read_from_prefix(&first_two_blocks[storage.aligned_header_size() as usize..])
1549            .unwrap()
1550            .0; // TODO: zerocopy: from-prefix (read_from_prefix): use-rest-of-range (https://github.com/microsoft/openvmm/issues/759)
1551    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
1576/// Determines which header to use given the results of checking the
1577/// validity of each of the headers.
1578pub 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 both headers are valid, find the header with the larger sequence number.
1585            // The header with the most recent sequence number is considered
1586            // the current copy. To handle integer overflow, a header with sequence number 0
1587            // is considered the current copy if and only if the other header contains 0xFFFFFFFF.
1588            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
1613/// Validate the contents of header match VMGS file type.
1614pub 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
1654/// Initializes cached file metadata from the specified header. (File control blocks)
1655fn 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        // Check if the file is allocated.
1666        if file_entry.allocation_size == 0 {
1667            continue;
1668        };
1669
1670        // Validate the file offset.
1671        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        // The file must entirely fit in the available space.
1679        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        // Validate the valid data size.
1687        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        // Initialize the file control block for this file ID
1695        fcbs.insert(file_id, fcb);
1696    }
1697
1698    Ok(fcbs)
1699}
1700
1701/// Convert block count to byte count.
1702fn 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
1710/// Generates a nonce for the encryption. First 4 bytes are a random seed, and last 8 bytes are zero's.
1711fn generate_nonce() -> VmgsNonce {
1712    let mut nonce = VmgsNonce::new_zeroed();
1713    // Generate a 4-byte random seed for nonce
1714    getrandom::fill(&mut nonce[..vmgs_format::VMGS_NONCE_RANDOM_SEED_SIZE]).expect("rng failure");
1715    nonce
1716}
1717
1718/// Increment Nonce by one.
1719fn increment_nonce(nonce: &mut VmgsNonce) -> Result<(), Error> {
1720    // Update the random seed of nonce
1721    getrandom::fill(&mut nonce[..vmgs_format::VMGS_NONCE_RANDOM_SEED_SIZE]).expect("rng failure");
1722
1723    // Increment the counter of nonce by 1.
1724    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
1735/// Checks whether an encryption key is all zero's.
1736fn is_empty_key(encryption_key: &[u8]) -> bool {
1737    encryption_key.iter().all(|&x| x == 0)
1738}
1739
1740/// Encrypts MetadataKey. Returns encrypted_metadata_key.
1741#[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/// Decrypts metadata_key. Returns decrypted_metadata_key.
1767#[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
1796/// Computes the cr32 checksum for a given byte stream.
1797fn 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
1808/// maps out the used/unused space in the file and finds the smallest
1809/// unused space to allocate new data.
1810fn allocate_helper(
1811    allocation_list: &mut Vec<AllocationBlock>,
1812    block_count: u32,
1813    block_capacity: u32,
1814) -> Result<u32, Error> {
1815    // sort by block offset
1816    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    // find smallest set of blocks that will fit the data we're allocating
1824    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        /// Construct a [`Vmgs`] instance, re-using existing saved-state from an
1927        /// earlier instance.
1928        ///
1929        /// # Safety
1930        ///
1931        /// `open_from_saved` does NOT perform ANY validation on the provided
1932        /// `state`, and will blindly assume that it matches the underlying
1933        /// `storage` instance!
1934        ///
1935        /// Callers MUST ensure that the provided `state` matches the provided
1936        /// `storage`, and that no external entities have modified `storage` between
1937        /// the call to `save` and `open_from_saved`.
1938        ///
1939        /// Failing to do so may result in data corruption/loss, read/write
1940        /// failures, encryption errors, etc... (though, notably: it will _not_
1941        /// result in any memory-unsafety, hence why the function isn't marked
1942        /// `unsafe`).
1943        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        /// Save the in-memory Vmgs file metadata.
2026        ///
2027        /// This saved state can be used alongside `open_from_saved` to obtain a
2028        /// new `Vmgs` instance _without_ needing to invoke any IOs on the
2029        /// underlying storage.
2030        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        // write
2164        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        // read
2171        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        // write
2184        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        // read
2192        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        // write
2199        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        // read
2207        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        // write
2214        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        // read
2222        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        // write
2235        let buf = b"hello world";
2236        vmgs.write_file(FileId::TPM_NVRAM, buf).await.unwrap();
2237
2238        // read
2239        let read_buf = vmgs.read_file(FileId::TPM_NVRAM).await.unwrap();
2240        assert_eq!(buf, &*read_buf);
2241
2242        // move
2243        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        // delete
2251        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        // Create VMGS file and write to different FileId's
2262        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        // Re-open VMGS file and read from the same FileId's
2289        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        // read
2385        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    // general functions
2394    #[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 VmgsFileEntry correctly converted to FileControlBlock
2478        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        // write
2505        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        // write
2535        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        // read
2546        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        // Read the file after re-opening the vmgs file
2554        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        // Unlock datastore
2564        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        // Add root key.
2583        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        // Write a file to the store.
2589        vmgs.write_file_encrypted(FileId::BIOS_NVRAM, &buf)
2590            .await
2591            .unwrap();
2592
2593        // Encrypt and overwrite the original file.
2594        vmgs.write_file_encrypted(FileId::BIOS_NVRAM, &buf_1)
2595            .await
2596            .unwrap();
2597
2598        // Verify new file contents
2599        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        // Add datastore key.
2613        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        // Write a file to the store.
2619        vmgs.write_file_encrypted(FileId::BIOS_NVRAM, &buf)
2620            .await
2621            .unwrap();
2622
2623        // Read the file, without closing the datastore
2624        let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2625        assert_eq!(buf, read_buf);
2626
2627        drop(vmgs);
2628
2629        // Read the file, after closing and reopening the data store.
2630        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        // Unlock the store.
2636
2637        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        // Change to a new datastore key.
2644        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        // Initialize version 3 data store
2663        let disk = new_test_file();
2664        let mut vmgs = Vmgs::format_new(disk.clone(), None).await.unwrap();
2665
2666        // Add datastore key.
2667        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        // Write a file to the store.
2673        vmgs.write_file_encrypted(FileId::BIOS_NVRAM, &buf)
2674            .await
2675            .unwrap();
2676
2677        // Read the file, after closing and reopening the data store.
2678        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        // Add new datastore key.
2688        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        // Read the file by using two different datastore keys, after closing and reopening the data store.
2694        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        // Remove the newly added datastore key and add it again.
2708        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        // Remove the old datastore key
2715        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        // Try to remove the old datastore key again
2720        let result = vmgs.remove_encryption_key(0).await;
2721        assert!(matches!(result, Err(Error::InvalidArgument(_))));
2722
2723        // Remove the new datastore key and try to read file content, which should be in encrypted state
2724        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        // Call write_file_encrypted on an unencrypted VMGS and check that plaintext was written
2733
2734        // Initialize version 3 data store
2735        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        // call write file encrypted
2740        vmgs.write_file_encrypted(FileId::BIOS_NVRAM, buf)
2741            .await
2742            .unwrap();
2743
2744        // Read
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        // ensure that when we re-create the VMGS object, we can still read the
2750        // FileId as plaintext
2751        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        // write
2773        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        // Manipulate the nonce and expect the read to fail.
2784        fcb.nonce[0] ^= 1;
2785
2786        // read and expect to fail
2787        let result = vmgs.read_file(FileId::BIOS_NVRAM).await;
2788        assert!(result.is_err());
2789
2790        // verify that the string is logged
2791        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        // Add datastore key.
2805        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        // Write a file to the store.
2812        vmgs.write_file_encrypted(FileId::BIOS_NVRAM, &buf)
2813            .await
2814            .unwrap();
2815
2816        // Read the file, without closing the datastore
2817        let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2818        assert_eq!(buf, read_buf);
2819
2820        // Close and reopen the store
2821        drop(vmgs);
2822        let mut vmgs = Vmgs::open(disk.clone(), None).await.unwrap();
2823
2824        // Unlock the store.
2825        vmgs.unlock_with_encryption_key(&encryption_key)
2826            .await
2827            .unwrap();
2828        assert_eq!(vmgs.state.active_datastore_key_index, Some(0));
2829
2830        // Read the file again
2831        let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2832        assert_eq!(buf, read_buf);
2833
2834        // Change to a new datastore key.
2835        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        // Read the file again
2843        let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2844        assert_eq!(buf, read_buf);
2845
2846        // Close and reopen the store
2847        drop(vmgs);
2848        let mut vmgs = Vmgs::open(disk, None).await.unwrap();
2849
2850        // Unlock the store.
2851        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        // Read the file again
2857        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        // Add datastore key.
2871        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        // Write a file to the store.
2878        vmgs.write_file_encrypted(FileId::BIOS_NVRAM, &buf)
2879            .await
2880            .unwrap();
2881
2882        // Read the file, without closing the datastore
2883        let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2884        assert_eq!(buf, read_buf);
2885
2886        // Add a new datastore key, but don't remove the old one.
2887        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        // Close and reopen the store
2895        drop(vmgs);
2896        let mut vmgs = Vmgs::open(disk.clone(), None).await.unwrap();
2897
2898        // Unlock the store.
2899        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        // Add yet another new datastore key. This should remove both previous keys
2905        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        // Close and reopen the store
2913        drop(vmgs);
2914        let mut vmgs = Vmgs::open(disk, None).await.unwrap();
2915
2916        // Unlock the store.
2917        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        // Read the file again
2923        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        // this test assumes the block capacity is 1024
2932        assert_eq!(block_capacity, 1024);
2933
2934        let mut allocation_list = Vec::new();
2935
2936        // add some "files"
2937        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        // remove the first one and make sure subsequent "files" are placed there
2951        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        // Make sure we error correctly when dealing with large files
2967        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}