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;
8#[cfg(with_encryption)]
9use anyhow::Context;
10#[cfg(with_encryption)]
11use anyhow::anyhow;
12use cvm_tracing::CVM_ALLOWED;
13use disk_backend::Disk;
14#[cfg(feature = "inspect")]
15use inspect::Inspect;
16#[cfg(feature = "inspect")]
17use inspect_counters::Counter;
18use std::collections::HashMap;
19use std::num::NonZeroU32;
20use std::sync::Arc;
21use vmgs_format::EncryptionAlgorithm;
22use vmgs_format::FileAttribute;
23use vmgs_format::FileId;
24use vmgs_format::VMGS_BYTES_PER_BLOCK;
25use vmgs_format::VMGS_EXTENDED_FILE_TABLE_BLOCK_SIZE;
26use vmgs_format::VMGS_FILE_TABLE_BLOCK_SIZE;
27use vmgs_format::VMGS_MIN_FILE_BLOCK_OFFSET;
28use vmgs_format::VMGS_SIGNATURE;
29use vmgs_format::VMGS_VERSION_3_0;
30use vmgs_format::VmgsAuthTag;
31use vmgs_format::VmgsDatastoreKey;
32use vmgs_format::VmgsEncryptionKey;
33use vmgs_format::VmgsExtendedFileTable;
34use vmgs_format::VmgsFileTable;
35use vmgs_format::VmgsHeader;
36use vmgs_format::VmgsMarkers;
37use vmgs_format::VmgsNonce;
38use zerocopy::FromBytes;
39use zerocopy::FromZeros;
40use zerocopy::IntoBytes;
41
42/// Info about a specific VMGS file.
43#[derive(Debug)]
44pub struct VmgsFileInfo {
45    /// Number of bytes allocated in the file.
46    pub allocated_bytes: u64,
47    /// Number of valid bytes in the file.
48    pub valid_bytes: u64,
49    /// Whether this file is encrypted.
50    pub encrypted: bool,
51}
52
53// Aggregates fully validated data from the FILE_TABLE and EXTENDED_FILE_TABLE
54// control blocks.
55#[derive(Clone, Copy, PartialEq, Eq, Debug)]
56#[cfg_attr(feature = "inspect", derive(Inspect))]
57struct ResolvedFileControlBlock {
58    // FILE_TABLE data
59    // ---------------
60    block_offset: u32,
61    #[cfg_attr(feature = "inspect", inspect(with = "|x| x.get()"))]
62    allocated_blocks: NonZeroU32,
63    valid_bytes: u64,
64
65    nonce: VmgsNonce,
66    authentication_tag: VmgsAuthTag,
67
68    // EXTENDED_FILE_TABLE data
69    // ---------------
70    attributes: FileAttribute,
71    encryption_key: VmgsDatastoreKey,
72}
73
74/// Implementation of the VMGS file format, backed by a generic [`Disk`]
75/// device.
76#[cfg_attr(feature = "inspect", derive(Inspect))]
77pub struct Vmgs {
78    storage: VmgsStorage,
79
80    #[cfg(feature = "inspect")]
81    stats: vmgs_inspect::VmgsStats,
82
83    active_header_index: usize,
84    active_header_sequence_number: u32,
85    version: u32,
86    #[cfg_attr(feature = "inspect", inspect(with = "vmgs_inspect::fcbs"))]
87    fcbs: HashMap<FileId, ResolvedFileControlBlock>,
88    encryption_algorithm: EncryptionAlgorithm,
89    #[allow(dead_code)]
90    datastore_key_count: u8,
91    active_datastore_key_index: Option<usize>,
92    #[cfg_attr(feature = "inspect", inspect(iter_by_index))]
93    datastore_keys: [VmgsDatastoreKey; 2],
94    metadata_key: VmgsDatastoreKey,
95    #[cfg_attr(feature = "inspect", inspect(iter_by_index))]
96    encrypted_metadata_keys: [VmgsEncryptionKey; 2],
97    reprovisioned: bool,
98
99    #[cfg_attr(feature = "inspect", inspect(skip))]
100    logger: Option<Arc<dyn VmgsLogger>>,
101}
102
103#[cfg(feature = "inspect")]
104mod vmgs_inspect {
105    use super::*;
106
107    #[derive(Default)]
108    pub struct IoStat {
109        pub attempt: Counter,
110        pub resolved: Counter,
111    }
112
113    // explicit inspect implementation, since we want to massage the data's
114    // presentation a bit
115    impl Inspect for IoStat {
116        fn inspect(&self, req: inspect::Request<'_>) {
117            let mut resp = req.respond();
118            resp.counter("ok", self.resolved.get())
119                .counter("err", self.attempt.get() - self.resolved.get());
120        }
121    }
122
123    #[derive(Inspect, Default)]
124    pub struct VmgsStats {
125        #[inspect(with = "stat_map")]
126        pub read: HashMap<FileId, IoStat>,
127        #[inspect(with = "stat_map")]
128        pub write: HashMap<FileId, IoStat>,
129    }
130
131    pub(super) fn fcbs(fcbs: &HashMap<FileId, ResolvedFileControlBlock>) -> impl Inspect + '_ {
132        inspect::adhoc(|req| {
133            let mut res = req.respond();
134            for (id, fcb) in fcbs.iter() {
135                res.field(&format!("{}-{:?}", id.0, id), fcb);
136            }
137        })
138    }
139
140    pub fn stat_map(map: &HashMap<FileId, IoStat>) -> impl Inspect + '_ {
141        inspect::iter_by_key(map).map_key(|x| format!("{:?}", x))
142    }
143}
144
145impl Vmgs {
146    /// Attempt to open the VMGS file, optionally formatting if it is
147    /// empty or corrupted.
148    pub async fn try_open(
149        disk: Disk,
150        logger: Option<Arc<dyn VmgsLogger>>,
151        format_on_empty: bool,
152        format_on_failure: bool,
153    ) -> Result<Self, Error> {
154        match Self::open(disk.clone(), logger.clone()).await {
155            Ok(vmgs) => Ok(vmgs),
156            Err(Error::EmptyFile) if format_on_empty => {
157                tracing::info!(CVM_ALLOWED, "empty vmgs file, formatting");
158                Self::format_new(disk, logger).await
159            }
160            Err(err) if format_on_failure => {
161                tracing::warn!(CVM_ALLOWED, ?err, "vmgs initialization error, reformatting");
162                Self::format_new(disk, logger).await
163            }
164            Err(err) => {
165                let event_log_id = match err {
166                    // The data store format is invalid or not supported.
167                    Error::InvalidFormat(_) => VmgsLogEvent::InvalidFormat,
168                    // The data store is corrupted.
169                    Error::CorruptFormat(_) => VmgsLogEvent::CorruptFormat,
170                    // All other errors
171                    _ => VmgsLogEvent::InitFailed,
172                };
173
174                logger.log_event_fatal(event_log_id).await;
175                Err(err)
176            }
177        }
178    }
179
180    /// Open the VMGS file.
181    pub async fn open(disk: Disk, logger: Option<Arc<dyn VmgsLogger>>) -> Result<Self, Error> {
182        tracing::debug!(CVM_ALLOWED, "opening VMGS datastore");
183        let storage = VmgsStorage::new_validated(disk).map_err(Error::Initialization)?;
184        Self::open_inner(storage, logger).await
185    }
186
187    /// Format and open a new VMGS file.
188    pub async fn format_new(
189        disk: Disk,
190        logger: Option<Arc<dyn VmgsLogger>>,
191    ) -> Result<Self, Error> {
192        tracing::debug!(CVM_ALLOWED, "formatting and initializing VMGS datastore");
193        let storage = VmgsStorage::new_validated(disk).map_err(Error::Initialization)?;
194        Self::format_new_inner(storage, logger).await
195    }
196
197    /// Format and open a new VMGS file.
198    pub async fn request_format(
199        disk: Disk,
200        logger: Option<Arc<dyn VmgsLogger>>,
201    ) -> Result<Self, Error> {
202        let mut storage = VmgsStorage::new_validated(disk).map_err(Error::Initialization)?;
203
204        match Self::open_header(&mut storage).await {
205            Ok((active_header, active_header_index)) if active_header.markers.reprovisioned() => {
206                tracing::info!(CVM_ALLOWED, "reprovisioned marker found, skipping format");
207                Self::finish_open(storage, active_header, active_header_index, logger).await
208            }
209            _ => {
210                tracing::info!(CVM_ALLOWED, "formatting vmgs file on request");
211                let mut vmgs = Vmgs::format_new_inner(storage, logger).await?;
212
213                // set the reprovisioned marker to prevent the vmgs from
214                // repeatedly being reset
215                vmgs.set_reprovisioned(true).await?;
216
217                Ok(vmgs)
218            }
219        }
220    }
221
222    async fn format_new_inner(
223        mut storage: VmgsStorage,
224        logger: Option<Arc<dyn VmgsLogger>>,
225    ) -> Result<Self, Error> {
226        let active_header = Self::format(&mut storage, VMGS_VERSION_3_0).await?;
227        Self::finish_open(storage, active_header, 0, logger).await
228    }
229
230    async fn open_inner(
231        mut storage: VmgsStorage,
232        logger: Option<Arc<dyn VmgsLogger>>,
233    ) -> Result<Self, Error> {
234        let (active_header, active_header_index) = Self::open_header(&mut storage).await?;
235
236        let mut vmgs =
237            Self::finish_open(storage, active_header, active_header_index, logger).await?;
238
239        // clear the reprovisioned marker after successfully opening the vmgs
240        // without being requested to reprovision.
241        vmgs.set_reprovisioned(false).await?;
242
243        Ok(vmgs)
244    }
245
246    async fn open_header(storage: &mut VmgsStorage) -> Result<(VmgsHeader, usize), Error> {
247        let (header_1, header_2) = read_headers_inner(storage).await?;
248
249        let empty_header = VmgsHeader::new_zeroed();
250
251        if header_1.as_bytes() == empty_header.as_bytes()
252            && header_2.as_bytes() == empty_header.as_bytes()
253        {
254            return Err(Error::EmptyFile);
255        }
256
257        let active_header_index =
258            get_active_header(validate_header(&header_1), validate_header(&header_2))?;
259
260        let active_header = if active_header_index == 0 {
261            header_1
262        } else {
263            header_2
264        };
265
266        Ok((active_header, active_header_index))
267    }
268
269    async fn finish_open(
270        mut storage: VmgsStorage,
271        active_header: VmgsHeader,
272        active_header_index: usize,
273        logger: Option<Arc<dyn VmgsLogger>>,
274    ) -> Result<Vmgs, Error> {
275        let version = active_header.version;
276        let (encryption_algorithm, encrypted_metadata_keys, datastore_key_count, reprovisioned) =
277            if version >= VMGS_VERSION_3_0 {
278                let encryption_algorithm =
279                    if active_header.encryption_algorithm == EncryptionAlgorithm::AES_GCM {
280                        EncryptionAlgorithm::AES_GCM
281                    } else {
282                        EncryptionAlgorithm::NONE
283                    };
284                let encrypted_metadata_keys = active_header.metadata_keys;
285
286                let is_key_zero_empty = is_empty_key(&encrypted_metadata_keys[0].encryption_key);
287                let is_key_one_empty = is_empty_key(&encrypted_metadata_keys[1].encryption_key);
288                let datastore_key_count = {
289                    if is_key_zero_empty && is_key_one_empty {
290                        0
291                    } else if !is_key_zero_empty && !is_key_one_empty {
292                        encrypted_metadata_keys.len() as u8
293                    } else {
294                        1
295                    }
296                };
297                (
298                    encryption_algorithm,
299                    active_header.metadata_keys,
300                    datastore_key_count,
301                    active_header.markers.reprovisioned(),
302                )
303            } else {
304                (
305                    EncryptionAlgorithm::NONE,
306                    [VmgsEncryptionKey::new_zeroed(); 2],
307                    0,
308                    false,
309                )
310            };
311
312        // Read the file table and initialize the internal file metadata.
313        let file_table_size_bytes = block_count_to_byte_count(active_header.file_table_size);
314        let file_table_offset_bytes = block_count_to_byte_count(active_header.file_table_offset);
315
316        let mut file_table_buffer = vec![0; file_table_size_bytes as usize];
317
318        if let Err(e) = storage
319            .read_block(file_table_offset_bytes, file_table_buffer.as_mut_slice())
320            .await
321        {
322            return Err(Error::CorruptFormat(format!(
323                "Error reading file table: {:?}",
324                e
325            )));
326        }
327
328        let file_table = VmgsFileTable::ref_from_prefix(&file_table_buffer)
329            .unwrap()
330            .0; // TODO: zerocopy: ref-from-prefix: use-rest-of-range (https://github.com/microsoft/openvmm/issues/759)
331
332        let file_control_blocks =
333            initialize_file_metadata(file_table, version, storage.block_capacity())?;
334
335        Ok(Self {
336            storage,
337
338            active_header_index,
339            active_header_sequence_number: active_header.sequence,
340            version,
341            fcbs: file_control_blocks,
342            encryption_algorithm,
343            datastore_key_count,
344            active_datastore_key_index: None,
345            datastore_keys: [VmgsDatastoreKey::new_zeroed(); 2],
346            metadata_key: VmgsDatastoreKey::new_zeroed(),
347            encrypted_metadata_keys,
348            reprovisioned,
349
350            #[cfg(feature = "inspect")]
351            stats: Default::default(),
352
353            logger,
354        })
355    }
356
357    /// Formats the backing store with initial metadata, and sets active header.
358    async fn format(storage: &mut VmgsStorage, version: u32) -> Result<VmgsHeader, Error> {
359        tracing::info!(CVM_ALLOWED, "Formatting new VMGS file.");
360        let aligned_header_size = round_up_count(size_of::<VmgsHeader>(), storage.sector_size());
361
362        // The second header is initialized as invalid (all zeros).
363        let mut header = VmgsHeader::new_zeroed();
364
365        storage
366            .write_block(aligned_header_size, header.as_bytes())
367            .await
368            .map_err(Error::WriteDisk)?;
369
370        // Write an empty file table at min offset. All entries are zeroed except
371        // for the first one, which is the file table itself
372        let mut file_table = VmgsFileTable::new_zeroed();
373        file_table.entries[FileId::FILE_TABLE].offset = VMGS_MIN_FILE_BLOCK_OFFSET;
374        file_table.entries[FileId::FILE_TABLE].allocation_size = VMGS_FILE_TABLE_BLOCK_SIZE;
375        file_table.entries[FileId::FILE_TABLE].valid_data_size =
376            block_count_to_byte_count(VMGS_FILE_TABLE_BLOCK_SIZE);
377        if version >= VMGS_VERSION_3_0 {
378            file_table.entries[FileId::EXTENDED_FILE_TABLE].offset =
379                VMGS_MIN_FILE_BLOCK_OFFSET + VMGS_FILE_TABLE_BLOCK_SIZE;
380            file_table.entries[FileId::EXTENDED_FILE_TABLE].allocation_size =
381                VMGS_EXTENDED_FILE_TABLE_BLOCK_SIZE;
382            file_table.entries[FileId::EXTENDED_FILE_TABLE].valid_data_size =
383                block_count_to_byte_count(VMGS_EXTENDED_FILE_TABLE_BLOCK_SIZE);
384        }
385
386        storage
387            .write_block(
388                block_count_to_byte_count(VMGS_MIN_FILE_BLOCK_OFFSET),
389                file_table.as_bytes(),
390            )
391            .await
392            .map_err(Error::WriteDisk)?;
393
394        initialize_file_metadata(&file_table, VMGS_VERSION_3_0, storage.block_capacity())?;
395
396        // Write an empty extended file table if the datastore supports V3.
397        if version >= VMGS_VERSION_3_0 {
398            let extended_file_table = VmgsExtendedFileTable::new_zeroed();
399            storage
400                .write_block(
401                    block_count_to_byte_count(
402                        VMGS_MIN_FILE_BLOCK_OFFSET + VMGS_FILE_TABLE_BLOCK_SIZE,
403                    ),
404                    extended_file_table.as_bytes(),
405                )
406                .await
407                .map_err(Error::WriteDisk)?;
408        }
409
410        // Write the first header as the valid header
411        header.signature = VMGS_SIGNATURE;
412        header.version = VMGS_VERSION_3_0;
413        header.sequence = 1;
414        header.header_size = size_of::<VmgsHeader>() as u32;
415        header.file_table_offset = VMGS_MIN_FILE_BLOCK_OFFSET;
416        header.file_table_size = VMGS_FILE_TABLE_BLOCK_SIZE;
417        header.checksum = compute_crc32(header.as_bytes());
418        header.encryption_algorithm = EncryptionAlgorithm::NONE;
419
420        storage
421            .write_block(0, header.as_bytes())
422            .await
423            .map_err(Error::WriteDisk)?;
424
425        // Flush the device to persist changes
426        storage.flush().await.map_err(Error::FlushDisk)?;
427
428        Ok(header)
429    }
430
431    /// Get allocated and valid bytes from File Control Block for file_id.
432    ///
433    /// When reading data from a file, the buffer must be at least `valid_bytes` long.
434    pub fn get_file_info(&self, file_id: FileId) -> Result<VmgsFileInfo, Error> {
435        let fcb = self.fcbs.get(&file_id).ok_or(Error::FileInfoNotAllocated)?;
436
437        Ok(VmgsFileInfo {
438            allocated_bytes: block_count_to_byte_count(fcb.allocated_blocks.get()),
439            valid_bytes: fcb.valid_bytes,
440            encrypted: fcb.attributes.encrypted() || fcb.attributes.authenticated(),
441        })
442    }
443
444    /// maps out the used/unused space in the file and finds the smallest unused space to allocate new data.
445    /// Appends the newly allocated FileControlBlock to the end of temp_fcbs.
446    ///
447    /// # Arguments
448    ///
449    /// * 'block_count' - allocated_blocks to allocate for new FCB
450    fn allocate_space(
451        &mut self,
452        block_count: u32,
453        temp_fcbs: &mut Vec<ResolvedFileControlBlock>,
454        valid_bytes: u64,
455    ) -> Result<(), Error> {
456        struct AllocationBlock {
457            block_offset: u32,
458            allocated_blocks: u32,
459        }
460
461        if block_count == 0 {
462            return Err(Error::AllocateZero);
463        }
464        // map out file offets/sizes to see what space is unused
465        let mut allocation_list = Vec::new();
466        for (_, fcb) in self.fcbs.iter() {
467            allocation_list.push(AllocationBlock {
468                block_offset: fcb.block_offset,
469                allocated_blocks: fcb.allocated_blocks.get(),
470            });
471        }
472
473        for temp_fcb in temp_fcbs.iter() {
474            allocation_list.push(AllocationBlock {
475                block_offset: temp_fcb.block_offset,
476                allocated_blocks: temp_fcb.allocated_blocks.get(),
477            });
478        }
479        // TODO: this will get removed when allocation_list is re-written
480        // sort by block offset
481        allocation_list.sort_by_key(|a| a.block_offset);
482
483        let mut best_offset = 0;
484        let mut best_free_count = 0;
485        let mut last_allocation_end_offset = VMGS_MIN_FILE_BLOCK_OFFSET;
486        let mut found = false;
487
488        // find smallest set of blocks that will fit the data we're allocating
489        for fcb in allocation_list.iter() {
490            if fcb.block_offset < last_allocation_end_offset {
491                return Err(Error::AllocateOffset);
492            }
493            let free_count = fcb.block_offset - last_allocation_end_offset;
494            if free_count >= block_count && (best_free_count == 0 || free_count < best_free_count) {
495                best_free_count = free_count;
496                best_offset = last_allocation_end_offset;
497                found = true;
498            }
499            last_allocation_end_offset = fcb.block_offset + fcb.allocated_blocks;
500        }
501        if last_allocation_end_offset < self.storage.block_capacity() {
502            let free_count = self.storage.block_capacity() - last_allocation_end_offset;
503            if free_count >= block_count && (best_free_count == 0 || free_count < best_free_count) {
504                best_offset = last_allocation_end_offset;
505                found = true;
506            }
507        }
508        if !found {
509            return Err(Error::InsufficientResources);
510        }
511        let new_fcb = ResolvedFileControlBlock {
512            block_offset: best_offset,
513            allocated_blocks: NonZeroU32::new(block_count).unwrap(),
514            valid_bytes,
515            attributes: FileAttribute::new(),
516            nonce: VmgsNonce::new_zeroed(),
517            authentication_tag: VmgsAuthTag::new_zeroed(),
518            encryption_key: VmgsDatastoreKey::new_zeroed(),
519        };
520        temp_fcbs.push(new_fcb);
521
522        Ok(())
523    }
524
525    /// Writes `buf` to file, then updates file table to point to updated data.
526    async fn write_file_internal(
527        &mut self,
528        file_id: FileId,
529        buf: &[u8],
530        file_table_fcb: &mut ResolvedFileControlBlock,
531        data_fcb: &mut ResolvedFileControlBlock,
532        should_encrypt: bool,
533        should_write_file_table: bool,
534    ) -> Result<(), Error> {
535        let data_nonce_auth_tag = if should_encrypt {
536            let data_encryption_key = {
537                let mut encryption_key = VmgsDatastoreKey::new_zeroed();
538                getrandom::fill(&mut encryption_key).expect("rng failure");
539                encryption_key
540            };
541            let data_nonce = generate_nonce();
542            let mut data_auth_tag = VmgsAuthTag::new_zeroed();
543
544            if let Err(e) = self
545                .write_encrypted_data(
546                    data_fcb.block_offset,
547                    &data_encryption_key,
548                    &data_nonce,
549                    buf,
550                    &mut data_auth_tag,
551                )
552                .await
553            {
554                self.logger
555                    .log_event_fatal(VmgsLogEvent::AccessFailed)
556                    .await;
557
558                return Err(e);
559            }
560
561            // Update the data file control block.
562            data_fcb.nonce.copy_from_slice(&data_nonce);
563            data_fcb
564                .encryption_key
565                .copy_from_slice(&data_encryption_key);
566            data_fcb.authentication_tag.copy_from_slice(&data_auth_tag);
567            Some((data_nonce, data_auth_tag))
568        } else {
569            // Write the file contents to the newly allocated space
570            if let Err(e) = self
571                .storage
572                .write_block(block_count_to_byte_count(data_fcb.block_offset), buf)
573                .await
574            {
575                self.logger
576                    .log_event_fatal(VmgsLogEvent::AccessFailed)
577                    .await;
578
579                return Err(Error::WriteDisk(e));
580            }
581            None
582        };
583
584        // Initialize the new file table with current metadata for all files.
585        let mut new_file_table = VmgsFileTable::new_zeroed();
586        for (file_id, fcb) in self.fcbs.iter() {
587            let new_file_entry = &mut new_file_table.entries[*file_id];
588
589            new_file_entry.offset = fcb.block_offset;
590            new_file_entry.allocation_size = fcb.allocated_blocks.get();
591            new_file_entry.valid_data_size = fcb.valid_bytes;
592
593            if self.version >= VMGS_VERSION_3_0 {
594                new_file_entry.nonce.copy_from_slice(&fcb.nonce);
595                new_file_entry
596                    .authentication_tag
597                    .copy_from_slice(&fcb.authentication_tag);
598            }
599        }
600
601        // Fill in the metadata for the file being written.
602        let file_entry = &mut new_file_table.entries[file_id];
603        *file_entry = vmgs_format::VmgsFileEntry {
604            offset: data_fcb.block_offset,
605            allocation_size: data_fcb.allocated_blocks.get(),
606            valid_data_size: buf.len() as u64,
607            ..vmgs_format::VmgsFileEntry::new_zeroed()
608        };
609
610        if let Some((data_nonce, data_auth_tag)) = data_nonce_auth_tag {
611            // Fill in the V3 fields of the file table entry.
612            file_entry.nonce.copy_from_slice(&data_nonce);
613            file_entry
614                .authentication_tag
615                .copy_from_slice(&data_auth_tag);
616        }
617
618        // Fill in the metadata for the new file table itself (file ID 0)
619        let file_table_entry = &mut new_file_table.entries[FileId::FILE_TABLE];
620        *file_table_entry = vmgs_format::VmgsFileEntry {
621            offset: file_table_fcb.block_offset,
622            allocation_size: file_table_fcb.allocated_blocks.get(),
623            valid_data_size: file_table_fcb.valid_bytes,
624            ..vmgs_format::VmgsFileEntry::new_zeroed()
625        };
626
627        if should_write_file_table {
628            // Write out the new file table.
629            if let Err(e) = self
630                .storage
631                .write_block(
632                    block_count_to_byte_count(file_table_fcb.block_offset),
633                    new_file_table.as_bytes(),
634                )
635                .await
636            {
637                self.logger
638                    .log_event_fatal(VmgsLogEvent::AccessFailed)
639                    .await;
640
641                return Err(Error::WriteDisk(e));
642            }
643        }
644
645        // Update the in-memory file control blocks. Updating file_control_block last ensures
646        // operation to be atomic, in case the program crashes on the write above or any intermediate operation
647
648        self.fcbs.insert(FileId::FILE_TABLE, *file_table_fcb);
649        self.fcbs.insert(file_id, *data_fcb);
650
651        Ok(())
652    }
653
654    /// Copies current file metadata to an extended file table structure.
655    fn fill_extended_file_table(
656        &mut self,
657        new_extended_file_table: &mut VmgsExtendedFileTable,
658    ) -> Result<(), Error> {
659        *new_extended_file_table = VmgsExtendedFileTable::new_zeroed();
660        for (file_id, fcb) in self.fcbs.iter_mut() {
661            let extended_file_entry = &mut new_extended_file_table.entries[*file_id];
662            extended_file_entry.attributes = fcb.attributes;
663            extended_file_entry
664                .encryption_key
665                .copy_from_slice(&fcb.encryption_key);
666        }
667
668        Ok(())
669    }
670
671    /// Update and write new header to storage device.
672    async fn update_header(&mut self, new_header: &mut VmgsHeader) -> Result<(), Error> {
673        // Wrapping prevents integer overflow checks
674        new_header.sequence = self.active_header_sequence_number.wrapping_add(1);
675        new_header.checksum = 0;
676        new_header.checksum = compute_crc32(new_header.as_bytes());
677
678        let new_header_index = if self.active_header_index == 0 { 1 } else { 0 };
679
680        self.storage
681            .write_block(
682                new_header_index as u64 * self.storage.aligned_header_size(),
683                new_header.as_bytes(),
684            )
685            .await
686            .map_err(Error::WriteDisk)?;
687        self.set_active_header(new_header_index, new_header.sequence);
688        Ok(())
689    }
690
691    /// Sets the active header index and sequence number.
692    fn set_active_header(
693        &mut self,
694        active_header_index: usize,
695        active_header_sequence_number: u32,
696    ) {
697        assert!(active_header_index < 2);
698        self.active_header_index = active_header_index;
699        self.active_header_sequence_number = active_header_sequence_number;
700    }
701
702    /// Reads the specified `file_id`, decrypting its contents.
703    pub async fn read_file(&mut self, file_id: FileId) -> Result<Vec<u8>, Error> {
704        self.read_file_inner(file_id, true).await
705    }
706
707    /// Reads the specified `file_id`, but does not decrypt the contents.
708    pub async fn read_file_raw(&mut self, file_id: FileId) -> Result<Vec<u8>, Error> {
709        self.read_file_inner(file_id, false).await
710    }
711
712    async fn read_file_inner(&mut self, file_id: FileId, decrypt: bool) -> Result<Vec<u8>, Error> {
713        #[cfg(feature = "inspect")]
714        self.stats
715            .read
716            .entry(file_id)
717            .or_default()
718            .attempt
719            .increment();
720
721        let file_info = self.get_file_info(file_id)?;
722        if file_id == FileId::FILE_TABLE {
723            return Err(Error::FileId);
724        }
725
726        let fcb = self.fcbs[&file_id];
727
728        let mut buf = vec![0; file_info.valid_bytes as usize];
729
730        let file_is_encrypted = fcb.attributes.encrypted() || fcb.attributes.authenticated();
731
732        if decrypt
733            && self.version >= VMGS_VERSION_3_0
734            && self.encryption_algorithm != EncryptionAlgorithm::NONE
735            && file_is_encrypted
736            && self.active_datastore_key_index.is_some()
737        {
738            if let Err(e) = self
739                .read_decrypted_data(
740                    fcb.block_offset,
741                    &fcb.encryption_key,
742                    &fcb.nonce,
743                    &fcb.authentication_tag,
744                    &mut buf,
745                )
746                .await
747            {
748                self.logger
749                    .log_event_fatal(VmgsLogEvent::AccessFailed)
750                    .await;
751
752                return Err(e);
753            }
754        } else if file_is_encrypted && decrypt {
755            return Err(Error::ReadEncrypted);
756        } else {
757            let byte_offset = block_count_to_byte_count(fcb.block_offset);
758            if let Err(e) = self.storage.read_block(byte_offset, &mut buf).await {
759                self.logger
760                    .log_event_fatal(VmgsLogEvent::AccessFailed)
761                    .await;
762
763                return Err(Error::ReadDisk(e));
764            }
765        }
766
767        #[cfg(feature = "inspect")]
768        self.stats
769            .read
770            .entry(file_id)
771            .or_default()
772            .resolved
773            .increment();
774
775        Ok(buf)
776    }
777
778    /// Writes `buf` to a file_id without encrypting it.
779    ///
780    /// If the file is already encrypted, this will return a failure. Use
781    /// [`Self::write_file_allow_overwrite_encrypted`] if you want to allow
782    /// this.
783    ///
784    /// To write encrypted data, use `write_file_encrypted` instead.
785    pub async fn write_file(&mut self, file_id: FileId, buf: &[u8]) -> Result<(), Error> {
786        self.write_file_inner(file_id, buf, false).await
787    }
788
789    /// Writes `buf` to a file_id without encrypting it, allowing overrites of
790    /// an already-encrypted file.
791    pub async fn write_file_allow_overwrite_encrypted(
792        &mut self,
793        file_id: FileId,
794        buf: &[u8],
795    ) -> Result<(), Error> {
796        self.write_file_inner(file_id, buf, true).await
797    }
798
799    async fn write_file_inner(
800        &mut self,
801        file_id: FileId,
802        buf: &[u8],
803        overwrite_encrypted: bool,
804    ) -> Result<(), Error> {
805        #[cfg(feature = "inspect")]
806        self.stats
807            .write
808            .entry(file_id)
809            .or_default()
810            .attempt
811            .increment();
812
813        if file_id == FileId::FILE_TABLE {
814            return Err(Error::FileId);
815        }
816        if buf.len() > vmgs_format::VMGS_MAX_FILE_SIZE_BYTES as usize {
817            return Err(Error::WriteFileLength);
818        }
819        let mut blocks_to_allocate =
820            (round_up_count(buf.len(), VMGS_BYTES_PER_BLOCK) / VMGS_BYTES_PER_BLOCK as u64) as u32;
821        // Always allocate at least one block, to allow for zero sized data buffers
822        if blocks_to_allocate == 0 {
823            blocks_to_allocate = 1;
824        }
825        if blocks_to_allocate as u64 > vmgs_format::VMGS_MAX_FILE_SIZE_BLOCKS {
826            return Err(Error::WriteFileBlocks);
827        }
828        if self
829            .fcbs
830            .get(&file_id)
831            .map(|fcb| fcb.attributes.encrypted())
832            .unwrap_or(false)
833        {
834            if overwrite_encrypted {
835                tracing::warn!(
836                    CVM_ALLOWED,
837                    "overwriting encrypted file with plaintext data!"
838                )
839            } else {
840                return Err(Error::OverwriteEncrypted);
841            }
842        }
843
844        // Allocate space for the new file contents and the new file table.
845        // On success, the contents of the temporary FCBs are copied to the existing FCBs.
846        let mut temp_fcbs: Vec<ResolvedFileControlBlock> = Vec::new();
847        // file_table_fcb
848        self.allocate_space(
849            VMGS_FILE_TABLE_BLOCK_SIZE,
850            &mut temp_fcbs,
851            block_count_to_byte_count(VMGS_FILE_TABLE_BLOCK_SIZE),
852        )?;
853        // data_fcb
854        self.allocate_space(blocks_to_allocate, &mut temp_fcbs, buf.len() as u64)?;
855
856        // extended_file_table_fcb is Some() if we should write to extended file table.
857        let extended_file_table_fcb = if self.encryption_algorithm == EncryptionAlgorithm::NONE
858            || self
859                .fcbs
860                .get(&file_id)
861                .map(|f| f.attributes == FileAttribute::new())
862                .unwrap_or(true)
863        {
864            None
865        } else {
866            self.allocate_space(
867                VMGS_EXTENDED_FILE_TABLE_BLOCK_SIZE,
868                &mut temp_fcbs,
869                block_count_to_byte_count(VMGS_EXTENDED_FILE_TABLE_BLOCK_SIZE),
870            )?;
871            temp_fcbs.last_mut().unwrap().attributes = FileAttribute::new()
872                .with_encrypted(true)
873                .with_authenticated(true);
874
875            Some(temp_fcbs.pop().unwrap())
876        };
877
878        // the C++ code originally implemented gsl::finally to deallocate these from the allocation list
879        // on exception. However, currently with this being a single threaded Rust crate, that shouldn't be
880        // needed. When switching to multithreading, that may change.
881        let mut data_fcb = temp_fcbs.pop().unwrap();
882        let mut file_table_fcb = temp_fcbs.pop().unwrap();
883
884        data_fcb.attributes = FileAttribute::new();
885
886        // Write the file contents to the newly allocated space.
887        self.write_file_internal(
888            file_id,
889            buf,
890            &mut file_table_fcb,
891            &mut data_fcb,
892            false,
893            // Write the file table to the storage since there is no need to manipulate the extended file table.
894            extended_file_table_fcb.is_none(),
895        )
896        .await?;
897
898        if let Some(mut extended_table_fcb) = extended_file_table_fcb {
899            // Initialize the new extended file table with current metadata for all files.
900            let mut new_extended_file_table = VmgsExtendedFileTable::new_zeroed();
901            self.fill_extended_file_table(&mut new_extended_file_table)?;
902
903            // Fill in the metadata for the new extended table.
904            let extended_file_entry = &mut new_extended_file_table.entries[file_id];
905            extended_file_entry.attributes = data_fcb.attributes;
906            extended_file_entry
907                .encryption_key
908                .copy_from_slice(&data_fcb.encryption_key);
909
910            // Write the extended file table to the newly allocated space.
911            self.write_file_internal(
912                FileId::EXTENDED_FILE_TABLE,
913                new_extended_file_table.as_bytes(),
914                &mut file_table_fcb,
915                &mut extended_table_fcb,
916                true,
917                true,
918            )
919            .await?;
920        }
921
922        // Data must be hardened on persistent storage before the header is updated.
923        self.storage.flush().await.map_err(Error::FlushDisk)?;
924
925        // Prepare a new header.
926        let mut new_header = self.prepare_new_header(&file_table_fcb);
927
928        if self.encryption_algorithm != EncryptionAlgorithm::NONE {
929            if let Some(extended_table_fcb) = extended_file_table_fcb {
930                let mut metadata_key_auth_tag = VmgsAuthTag::new_zeroed();
931                self.metadata_key
932                    .copy_from_slice(&extended_table_fcb.encryption_key);
933
934                let current_index = self.active_datastore_key_index.unwrap();
935
936                increment_nonce(&mut self.encrypted_metadata_keys[current_index].nonce)?;
937
938                let encrypted_metadata_key = encrypt_metadata_key(
939                    &self.datastore_keys[current_index],
940                    &self.encrypted_metadata_keys[current_index].nonce,
941                    &self.metadata_key,
942                    &mut metadata_key_auth_tag,
943                )?;
944
945                self.encrypted_metadata_keys[current_index]
946                    .authentication_tag
947                    .copy_from_slice(&metadata_key_auth_tag);
948                self.encrypted_metadata_keys[current_index]
949                    .encryption_key
950                    .copy_from_slice(&encrypted_metadata_key);
951            }
952
953            new_header.encryption_algorithm = self.encryption_algorithm;
954            new_header
955                .metadata_keys
956                .copy_from_slice(&self.encrypted_metadata_keys);
957        }
958
959        self.update_header(&mut new_header).await?;
960
961        #[cfg(feature = "inspect")]
962        self.stats
963            .write
964            .entry(file_id)
965            .or_default()
966            .resolved
967            .increment();
968
969        Ok(())
970    }
971
972    /// Encrypts `buf` and writes the encrypted payload to a file_id if the VMGS file has encryption configured.
973    /// If the VMGS doesn't have encryption configured, will do a plaintext write instead.
974    #[cfg(with_encryption)]
975    pub async fn write_file_encrypted(&mut self, file_id: FileId, buf: &[u8]) -> Result<(), Error> {
976        if file_id == FileId::FILE_TABLE {
977            return Err(Error::FileId);
978        }
979        if buf.len() > vmgs_format::VMGS_MAX_FILE_SIZE_BYTES as usize {
980            return Err(Error::WriteFileLength);
981        }
982        let mut blocks_to_allocate =
983            (round_up_count(buf.len(), VMGS_BYTES_PER_BLOCK) / VMGS_BYTES_PER_BLOCK as u64) as u32;
984        // Always allocate at least one block, to allow for zero sized data buffers
985        if blocks_to_allocate == 0 {
986            blocks_to_allocate = 1;
987        }
988        if blocks_to_allocate as u64 > vmgs_format::VMGS_MAX_FILE_SIZE_BLOCKS {
989            return Err(Error::WriteFileBlocks);
990        }
991        if self.encryption_algorithm == EncryptionAlgorithm::NONE {
992            tracing::trace!(
993                CVM_ALLOWED,
994                "VMGS file not encrypted, performing plaintext write"
995            );
996            return self.write_file(file_id, buf).await;
997        }
998
999        // Allocate space for the new file contents and the new file table.
1000        // On success, the contents of the temporary FCBs are copied to the existing FCBs.
1001        let mut temp_fcbs: Vec<ResolvedFileControlBlock> = Vec::new();
1002        // file_table_fcb
1003        self.allocate_space(
1004            VMGS_FILE_TABLE_BLOCK_SIZE,
1005            &mut temp_fcbs,
1006            block_count_to_byte_count(VMGS_FILE_TABLE_BLOCK_SIZE),
1007        )?;
1008        // data_fcb
1009        self.allocate_space(blocks_to_allocate, &mut temp_fcbs, buf.len() as u64)?;
1010
1011        let mut extended_file_table_fcb = {
1012            self.allocate_space(
1013                VMGS_EXTENDED_FILE_TABLE_BLOCK_SIZE,
1014                &mut temp_fcbs,
1015                block_count_to_byte_count(VMGS_EXTENDED_FILE_TABLE_BLOCK_SIZE),
1016            )?;
1017            temp_fcbs.last_mut().unwrap().attributes = FileAttribute::new()
1018                .with_encrypted(true)
1019                .with_authenticated(true);
1020            temp_fcbs.pop().unwrap()
1021        };
1022
1023        // the C++ code originally implemented gsl::finally to deallocate these from the allocation list
1024        // on exception. However, currently with this being a single threaded Rust crate, that shouldn't be
1025        // needed. When switching to multithreading, that may change.
1026        let mut data_fcb = temp_fcbs.pop().unwrap();
1027        let mut file_table_fcb = temp_fcbs.pop().unwrap();
1028
1029        data_fcb.attributes = FileAttribute::new()
1030            .with_encrypted(true)
1031            .with_authenticated(true);
1032
1033        // Write the file contents to the newly allocated space.
1034        self.write_file_internal(
1035            file_id,
1036            buf,
1037            &mut file_table_fcb,
1038            &mut data_fcb,
1039            true,
1040            false,
1041        )
1042        .await?;
1043
1044        // Initialize the new extended file table with current metadata for all files.
1045        let mut new_extended_file_table = VmgsExtendedFileTable::new_zeroed();
1046        self.fill_extended_file_table(&mut new_extended_file_table)?;
1047
1048        // Fill in the metadata for the new extended table.
1049        let extended_file_entry = &mut new_extended_file_table.entries[file_id];
1050        extended_file_entry.attributes = data_fcb.attributes;
1051        extended_file_entry
1052            .encryption_key
1053            .copy_from_slice(&data_fcb.encryption_key);
1054
1055        // Write the extended file table to the newly allocated space.
1056        self.write_file_internal(
1057            FileId::EXTENDED_FILE_TABLE,
1058            new_extended_file_table.as_bytes(),
1059            &mut file_table_fcb,
1060            &mut extended_file_table_fcb,
1061            true,
1062            true,
1063        )
1064        .await?;
1065
1066        // Data must be hardened on persistent storage before the header is updated.
1067        self.storage.flush().await.map_err(Error::FlushDisk)?;
1068
1069        // Prepare a new header.
1070        let mut new_header = self.prepare_new_header(&file_table_fcb);
1071
1072        if self.encryption_algorithm != EncryptionAlgorithm::NONE {
1073            let mut metadata_key_auth_tag = VmgsAuthTag::new_zeroed();
1074            self.metadata_key
1075                .copy_from_slice(&extended_file_table_fcb.encryption_key);
1076
1077            let active_key = self.active_datastore_key_index.unwrap();
1078            increment_nonce(&mut self.encrypted_metadata_keys[active_key].nonce)?;
1079
1080            let encrypted_metadata_key = encrypt_metadata_key(
1081                &self.datastore_keys[active_key],
1082                &self.encrypted_metadata_keys[active_key].nonce,
1083                &self.metadata_key,
1084                &mut metadata_key_auth_tag,
1085            )?;
1086
1087            self.encrypted_metadata_keys[active_key]
1088                .authentication_tag
1089                .copy_from_slice(&metadata_key_auth_tag);
1090            self.encrypted_metadata_keys[active_key]
1091                .encryption_key
1092                .copy_from_slice(&encrypted_metadata_key);
1093
1094            new_header.encryption_algorithm = self.encryption_algorithm;
1095            new_header
1096                .metadata_keys
1097                .copy_from_slice(&self.encrypted_metadata_keys);
1098        }
1099
1100        self.update_header(&mut new_header).await
1101    }
1102
1103    /// Decrypts the extended file table by the encryption_key and
1104    /// updates the related metadata in memory.
1105    #[cfg(with_encryption)]
1106    pub async fn unlock_with_encryption_key(&mut self, encryption_key: &[u8]) -> Result<(), Error> {
1107        if self.version < VMGS_VERSION_3_0 {
1108            return Err(Error::Other(anyhow!(
1109                "unlock_with_encryption_key() not supported with VMGS version"
1110            )));
1111        }
1112        if self.encryption_algorithm == EncryptionAlgorithm::NONE {
1113            return Err(Error::Other(anyhow!(
1114                "unlock_with_encryption_key() not supported with None EncryptionAlgorithm"
1115            )));
1116        }
1117
1118        // Iterate through two metadata keys and get the index of the valid key which can be successfully
1119        // decrypted by the encryption_key, as well as set the decrypted key as the VMGS's metadata key
1120        let mut valid_index = None;
1121        let mut errs = [None, None];
1122
1123        for (i, key) in self.encrypted_metadata_keys.iter().enumerate() {
1124            let result = decrypt_metadata_key(
1125                encryption_key,
1126                &key.nonce,
1127                &key.encryption_key,
1128                &key.authentication_tag,
1129            );
1130
1131            match result {
1132                Ok(metadata_key) => {
1133                    self.metadata_key.copy_from_slice(&metadata_key);
1134                    valid_index = Some(i);
1135                    break;
1136                }
1137                Err(err) => {
1138                    errs[i] = Some(err);
1139                }
1140            }
1141        }
1142
1143        let valid_index = match valid_index {
1144            Some(idx) => idx,
1145            None => {
1146                tracing::error!(
1147                    CVM_ALLOWED,
1148                    error = &errs[0].take().unwrap() as &dyn std::error::Error,
1149                    "first index failed to decrypt",
1150                );
1151                tracing::error!(
1152                    CVM_ALLOWED,
1153                    error = &errs[1].take().unwrap() as &dyn std::error::Error,
1154                    "second index failed to decrypt",
1155                );
1156                return Err(Error::Other(anyhow::anyhow!(
1157                    "failed to use the root key provided to decrypt VMGS metadata key"
1158                )));
1159            }
1160        };
1161        let extended_file_header = self.fcbs[&FileId::EXTENDED_FILE_TABLE];
1162        let extended_file_table_size_bytes =
1163            block_count_to_byte_count(extended_file_header.allocated_blocks.get());
1164        let mut extended_file_table_buffer = vec![0; extended_file_table_size_bytes as usize];
1165        let self_metadata_key = self.metadata_key;
1166
1167        // Read and decrypt the extended file table
1168        self.read_decrypted_data(
1169            extended_file_header.block_offset,
1170            &self_metadata_key,
1171            &extended_file_header.nonce,
1172            &extended_file_header.authentication_tag,
1173            &mut extended_file_table_buffer,
1174        )
1175        .await
1176        .context("failed to decrypt extended file table")?;
1177
1178        // Update the cached extended file table
1179        let extended_file_table =
1180            VmgsExtendedFileTable::read_from_prefix(extended_file_table_buffer.as_bytes())
1181                .map_err(|_| anyhow!("Invalid decrypted extended file table"))? // TODO: zerocopy: use result (https://github.com/microsoft/openvmm/issues/759)
1182                .0;
1183        for (file_id, fcb) in self.fcbs.iter_mut() {
1184            fcb.attributes = extended_file_table.entries[*file_id].attributes;
1185            fcb.encryption_key = extended_file_table.entries[*file_id].encryption_key;
1186        }
1187
1188        self.datastore_keys[valid_index].copy_from_slice(encryption_key);
1189        self.active_datastore_key_index = Some(valid_index);
1190
1191        Ok(())
1192    }
1193
1194    /// Encrypts the plaintext data and writes the encrypted data to the storage.
1195    #[cfg_attr(not(with_encryption), expect(unused_variables))]
1196    async fn write_encrypted_data(
1197        &mut self,
1198        block_offset: u32,
1199        encryption_key: &[u8],
1200        nonce: &[u8],
1201        plaintext_data: &[u8],
1202        authentication_tag: &mut [u8],
1203    ) -> Result<(), Error> {
1204        #[cfg(not(with_encryption))]
1205        unreachable!("Encryption requires the encryption feature");
1206        #[cfg(with_encryption)]
1207        {
1208            let encrypted_text = crate::encrypt::vmgs_encrypt(
1209                encryption_key,
1210                nonce,
1211                plaintext_data,
1212                authentication_tag,
1213            )?;
1214
1215            // Write the encrypted file contents to the newly allocated space.
1216            self.storage
1217                .write_block(block_count_to_byte_count(block_offset), &encrypted_text)
1218                .await
1219                .map_err(Error::WriteDisk)?;
1220
1221            Ok(())
1222        }
1223    }
1224
1225    /// Decrypts the encrypted data and reads it to the buffer.
1226    #[cfg_attr(not(with_encryption), expect(unused_variables))]
1227    async fn read_decrypted_data(
1228        &mut self,
1229        block_offset: u32,
1230        decryption_key: &[u8],
1231        nonce: &[u8],
1232        authentication_tag: &[u8],
1233        plaintext_data: &mut [u8],
1234    ) -> Result<(), Error> {
1235        #[cfg(not(with_encryption))]
1236        unreachable!("Encryption requires the encryption feature");
1237        #[cfg(with_encryption)]
1238        {
1239            // Read and decrypt the encrypted file contents.
1240            let mut buf = vec![0; plaintext_data.len()];
1241
1242            self.storage
1243                .read_block(block_count_to_byte_count(block_offset), &mut buf)
1244                .await
1245                .map_err(Error::ReadDisk)?;
1246
1247            // sanity check: encrypted data should never be all zeros. if we
1248            // find that it is all-zeroes, then that's indicative of some kind
1249            // of logic error / data corruption
1250            if buf.iter().all(|x| *x == 0) {
1251                return Err(Error::InvalidFormat("encrypted data is all-zeros".into()));
1252            }
1253
1254            let decrypted_text =
1255                crate::encrypt::vmgs_decrypt(decryption_key, nonce, &buf, authentication_tag)?;
1256            if decrypted_text.len() != plaintext_data.len() {
1257                return Err(Error::Other(anyhow!(
1258                    "Decrypt error, slice sizes should match."
1259                )));
1260            }
1261            plaintext_data.copy_from_slice(&decrypted_text);
1262
1263            Ok(())
1264        }
1265    }
1266
1267    /// Associates a new root key with the data store and removes the old
1268    /// encryption key, if it exists. If two keys already exist, the
1269    /// inactive key is removed first.
1270    #[cfg(with_encryption)]
1271    pub async fn update_encryption_key(
1272        &mut self,
1273        encryption_key: &[u8],
1274        encryption_algorithm: EncryptionAlgorithm,
1275    ) -> Result<(), Error> {
1276        let old_index = self.active_datastore_key_index;
1277
1278        match self
1279            .add_new_encryption_key(encryption_key, encryption_algorithm)
1280            .await
1281        {
1282            Ok(_) => {}
1283            Err(Error::DatastoreKeysFull) => {
1284                if let Some(old_index) = old_index {
1285                    let inactive_index = if old_index == 0 { 1 } else { 0 };
1286                    tracing::warn!(CVM_ALLOWED, inactive_index, "removing inactive key");
1287                    self.remove_encryption_key(inactive_index).await?;
1288                    tracing::trace!(CVM_ALLOWED, "attempting to add the key again");
1289                    self.add_new_encryption_key(encryption_key, encryption_algorithm)
1290                        .await?;
1291                } else {
1292                    return Err(Error::NoActiveDatastoreKey);
1293                }
1294            }
1295            Err(e) => return Err(e),
1296        };
1297
1298        if let Some(old_index) = old_index {
1299            self.remove_encryption_key(old_index).await?;
1300        }
1301
1302        Ok(())
1303    }
1304
1305    /// Associates a new root key with the data store.
1306    #[cfg(with_encryption)]
1307    async fn add_new_encryption_key(
1308        &mut self,
1309        encryption_key: &[u8],
1310        encryption_algorithm: EncryptionAlgorithm,
1311    ) -> Result<(), Error> {
1312        if self.version < VMGS_VERSION_3_0 {
1313            return Err(Error::Other(anyhow!(
1314                "add_new_encryption_key() not supported with VMGS version"
1315            )));
1316        }
1317        if self.encryption_algorithm != EncryptionAlgorithm::NONE
1318            && self.active_datastore_key_index.is_none()
1319        {
1320            return Err(Error::Other(anyhow!(
1321                "add_new_encryption_key() invalid datastore key index"
1322            )));
1323        }
1324        if self.datastore_key_count == self.datastore_keys.len() as u8 {
1325            return Err(Error::DatastoreKeysFull);
1326        }
1327        if is_empty_key(encryption_key) {
1328            return Err(Error::Other(anyhow!("Trying to add empty encryption key")));
1329        }
1330        if encryption_algorithm == EncryptionAlgorithm::NONE {
1331            return Err(Error::Other(anyhow!(
1332                "Encryption not supported for VMGS file"
1333            )));
1334        }
1335        if self.encryption_algorithm != EncryptionAlgorithm::NONE
1336            && encryption_algorithm != self.encryption_algorithm
1337        {
1338            return Err(Error::Other(anyhow!(
1339                "Encryption algorithm provided to add_new_encryption_key does not match VMGS's encryption algorithm."
1340            )));
1341        }
1342
1343        let mut new_key_index = 0;
1344        let mut new_metadata_key = self.metadata_key;
1345        if self.datastore_key_count == 0 {
1346            // Allocate space for the new file table and the new extended file table.
1347            // Two temporary FCBs will be added to the allocation list, and will be unlinked
1348            // from the allocation list no matter whether the function succeeds or fails.
1349            // On success, the contents of the temporary FCBs are copied to the existing FCBs.
1350            let mut temp_fcbs: Vec<ResolvedFileControlBlock> = Vec::new();
1351            self.allocate_space(
1352                VMGS_FILE_TABLE_BLOCK_SIZE,
1353                &mut temp_fcbs,
1354                block_count_to_byte_count(VMGS_FILE_TABLE_BLOCK_SIZE),
1355            )?;
1356
1357            self.allocate_space(
1358                VMGS_EXTENDED_FILE_TABLE_BLOCK_SIZE,
1359                &mut temp_fcbs,
1360                block_count_to_byte_count(VMGS_EXTENDED_FILE_TABLE_BLOCK_SIZE),
1361            )?;
1362
1363            let mut extended_file_table_fcb = temp_fcbs.pop().unwrap();
1364            let mut file_table_fcb = temp_fcbs.pop().unwrap();
1365
1366            extended_file_table_fcb.attributes = FileAttribute::new()
1367                .with_encrypted(true)
1368                .with_authenticated(true);
1369
1370            // Initialize a new extended file table.
1371            let new_extended_file_table = VmgsExtendedFileTable::new_zeroed();
1372            // Write the extended file table to the newly allocated space
1373            self.write_file_internal(
1374                FileId::EXTENDED_FILE_TABLE,
1375                new_extended_file_table.as_bytes(),
1376                &mut file_table_fcb,
1377                &mut extended_file_table_fcb,
1378                true,
1379                true,
1380            )
1381            .await?;
1382
1383            new_metadata_key
1384                .copy_from_slice(&self.fcbs[&FileId::EXTENDED_FILE_TABLE].encryption_key);
1385        } else if self.active_datastore_key_index == Some(0) {
1386            new_key_index = 1;
1387        }
1388
1389        // Prepare a new header.
1390        let mut new_header = self.prepare_new_header(&self.fcbs[&FileId::FILE_TABLE]);
1391        new_header.encryption_algorithm = EncryptionAlgorithm::AES_GCM;
1392
1393        // Use the new datastore key to encrypt the metadata key.
1394        let metadata_key_nonce = generate_nonce();
1395        let mut metadata_key_auth_tag = VmgsAuthTag::new_zeroed();
1396        let encrypted_metadata_key = encrypt_metadata_key(
1397            encryption_key,
1398            &metadata_key_nonce,
1399            &new_metadata_key,
1400            &mut metadata_key_auth_tag,
1401        )?;
1402
1403        self.encrypted_metadata_keys[new_key_index]
1404            .nonce
1405            .copy_from_slice(&metadata_key_nonce);
1406        self.encrypted_metadata_keys[new_key_index]
1407            .authentication_tag
1408            .copy_from_slice(&metadata_key_auth_tag);
1409        self.encrypted_metadata_keys[new_key_index]
1410            .encryption_key
1411            .copy_from_slice(&encrypted_metadata_key);
1412
1413        new_header
1414            .metadata_keys
1415            .copy_from_slice(&self.encrypted_metadata_keys);
1416
1417        // Update the header on the storage device
1418        self.update_header(&mut new_header).await?;
1419
1420        // Update the cached DataStore key.
1421        self.datastore_keys[new_key_index].copy_from_slice(encryption_key);
1422        self.metadata_key.copy_from_slice(&new_metadata_key);
1423        self.datastore_key_count += 1;
1424        self.encryption_algorithm = encryption_algorithm;
1425        self.active_datastore_key_index = Some(new_key_index);
1426
1427        Ok(())
1428    }
1429
1430    /// Disassociates the root key at the specified index from the data store.
1431    #[cfg(with_encryption)]
1432    async fn remove_encryption_key(&mut self, key_index: usize) -> Result<(), Error> {
1433        if self.version < VMGS_VERSION_3_0 {
1434            return Err(Error::Other(anyhow!(
1435                "remove_encryption_key() not supported with VMGS version."
1436            )));
1437        }
1438        if self.encryption_algorithm != EncryptionAlgorithm::NONE
1439            && self.active_datastore_key_index.is_none()
1440        {
1441            return Err(Error::Other(anyhow!(
1442                "remove_encryption_key() invalid datastore key index or encryption algorithm."
1443            )));
1444        }
1445        if self.datastore_key_count != self.datastore_keys.len() as u8
1446            && self.active_datastore_key_index != Some(key_index)
1447        {
1448            return Err(Error::Other(anyhow!(
1449                "remove_encryption_key() invalid key_index"
1450            )));
1451        }
1452
1453        // Remove the corresponding datastore_key
1454        self.datastore_keys[key_index].fill(0);
1455
1456        // Remove the corresponding metadata_key
1457        self.encrypted_metadata_keys[key_index] = VmgsEncryptionKey::new_zeroed();
1458
1459        // Prepare a new header
1460        let mut new_header = self.prepare_new_header(&self.fcbs[&FileId::FILE_TABLE]);
1461        new_header
1462            .metadata_keys
1463            .copy_from_slice(&self.encrypted_metadata_keys);
1464
1465        // Set the encryption algorithm to none, when there is only one valid metadata key before removal
1466        if self.datastore_key_count == 1 {
1467            new_header.encryption_algorithm = EncryptionAlgorithm::NONE;
1468        } else {
1469            new_header.encryption_algorithm = self.encryption_algorithm;
1470        }
1471
1472        // Update the header on the storage device
1473        self.update_header(&mut new_header).await?;
1474
1475        // Update cached metadata
1476        if self.datastore_key_count == 1 {
1477            self.encryption_algorithm = EncryptionAlgorithm::NONE;
1478            self.datastore_key_count = 0;
1479            self.active_datastore_key_index = None;
1480        } else {
1481            self.datastore_key_count = 1;
1482
1483            let new_active_datastore_key_index = if key_index == 0 { 1 } else { 0 };
1484            if is_empty_key(&self.datastore_keys[new_active_datastore_key_index]) {
1485                self.active_datastore_key_index = None;
1486            } else {
1487                self.active_datastore_key_index = Some(new_active_datastore_key_index);
1488            }
1489        }
1490
1491        Ok(())
1492    }
1493
1494    /// Gets the encryption algorithm of the VMGS
1495    pub fn get_encryption_algorithm(&self) -> EncryptionAlgorithm {
1496        self.encryption_algorithm
1497    }
1498
1499    /// Whether the VMGS file is encrypted
1500    pub fn is_encrypted(&self) -> bool {
1501        self.encryption_algorithm != EncryptionAlgorithm::NONE
1502    }
1503
1504    fn prepare_new_header(&self, file_table_fcb: &ResolvedFileControlBlock) -> VmgsHeader {
1505        VmgsHeader {
1506            signature: VMGS_SIGNATURE,
1507            version: self.version,
1508            header_size: size_of::<VmgsHeader>() as u32,
1509            file_table_offset: file_table_fcb.block_offset,
1510            file_table_size: file_table_fcb.allocated_blocks.get(),
1511            markers: VmgsMarkers::new().with_reprovisioned(self.reprovisioned),
1512            ..VmgsHeader::new_zeroed()
1513        }
1514    }
1515
1516    async fn set_reprovisioned(&mut self, value: bool) -> Result<(), Error> {
1517        if self.reprovisioned != value {
1518            tracing::info!(reprovisioned = value, "update vmgs marker");
1519            self.reprovisioned = value;
1520            let mut new_header = self.prepare_new_header(&self.fcbs[&FileId::FILE_TABLE]);
1521            self.update_header(&mut new_header).await?;
1522        }
1523        Ok(())
1524    }
1525}
1526
1527/// Additional test-only functions for use in other crates that reveal
1528/// implmentation details of the vmgs datastore encryption keys.
1529#[cfg(feature = "test_helpers")]
1530mod test_helpers {
1531    use super::*;
1532
1533    impl Vmgs {
1534        /// Get the active datastore key index
1535        pub fn test_get_active_datastore_key_index(&self) -> Option<usize> {
1536            self.active_datastore_key_index
1537        }
1538
1539        /// Associates a new root key with the data store.
1540        #[cfg(with_encryption)]
1541        pub async fn test_add_new_encryption_key(
1542            &mut self,
1543            encryption_key: &[u8],
1544            encryption_algorithm: EncryptionAlgorithm,
1545        ) -> Result<(), Error> {
1546            self.add_new_encryption_key(encryption_key, encryption_algorithm)
1547                .await
1548        }
1549    }
1550}
1551
1552/// Read both headers. For compatibility with the V1 format, the headers are
1553/// at logical sectors 0 and 1
1554pub async fn read_headers(disk: Disk) -> Result<(VmgsHeader, VmgsHeader), Error> {
1555    read_headers_inner(&mut VmgsStorage::new(disk)).await
1556}
1557
1558async fn read_headers_inner(storage: &mut VmgsStorage) -> Result<(VmgsHeader, VmgsHeader), Error> {
1559    // Read both headers, and determine the active one. For compatibility with
1560    // the V1 format, the headers are at logical sectors 0 and 1
1561    let mut first_two_blocks = [0; (VMGS_BYTES_PER_BLOCK * 2) as usize];
1562    storage
1563        .read_block(0, &mut first_two_blocks)
1564        .await
1565        .map_err(Error::ReadDisk)?;
1566
1567    // first_two_blocks will contain enough bytes to read the first two headers
1568    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)
1569    let header_2 =
1570        VmgsHeader::read_from_prefix(&first_two_blocks[storage.aligned_header_size() as usize..])
1571            .unwrap()
1572            .0; // TODO: zerocopy: from-prefix (read_from_prefix): use-rest-of-range (https://github.com/microsoft/openvmm/issues/759)
1573    Ok((header_1, header_2))
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
1637    let stored_checksum = header.checksum;
1638    let mut zero_checksum_header = *header;
1639    zero_checksum_header.checksum = 0;
1640    let computed_checksum = compute_crc32(zero_checksum_header.as_bytes());
1641    if stored_checksum != computed_checksum {
1642        return Err(Error::CorruptFormat(String::from(
1643            "Invalid header checksum",
1644        )));
1645    }
1646    Ok(header)
1647}
1648
1649/// Initializes cached file metadata from the specified header. (File control blocks)
1650fn initialize_file_metadata(
1651    file_table: &VmgsFileTable,
1652    version: u32,
1653    block_capacity: u32,
1654) -> Result<HashMap<FileId, ResolvedFileControlBlock>, Error> {
1655    let file_entries = file_table.entries;
1656    let mut file_control_blocks = HashMap::new();
1657
1658    for (file_id, file_entry) in file_entries.iter().enumerate() {
1659        let file_id = FileId(file_id as u32);
1660
1661        // Check if the file is allocated.
1662        let Some(allocated_blocks) = NonZeroU32::new(file_entry.allocation_size) else {
1663            continue;
1664        };
1665
1666        // Validate the file offset.
1667        if file_entry.offset < VMGS_MIN_FILE_BLOCK_OFFSET || file_entry.offset >= block_capacity {
1668            return Err(Error::CorruptFormat(format!(
1669                "Invalid file offset {} for file_id {:?} \n{:?}",
1670                file_entry.offset, file_id, file_entry
1671            )));
1672        }
1673
1674        // The file must entirely fit in the available space.
1675        let file_allocation_end_block = file_entry.offset + file_entry.allocation_size;
1676        if file_allocation_end_block > block_capacity {
1677            return Err(Error::CorruptFormat(String::from(
1678                "Invalid file allocation end block",
1679            )));
1680        }
1681
1682        // Validate the valid data size.
1683        let file_allocation_size_bytes = block_count_to_byte_count(file_entry.allocation_size);
1684        if file_entry.valid_data_size > file_allocation_size_bytes {
1685            return Err(Error::CorruptFormat(String::from("Invalid data size")));
1686        }
1687
1688        // Initialize the file control block for this file ID
1689        file_control_blocks.insert(file_id, {
1690            let (nonce, authentication_tag) = if version >= VMGS_VERSION_3_0 {
1691                (file_entry.nonce, file_entry.authentication_tag)
1692            } else {
1693                Default::default()
1694            };
1695
1696            ResolvedFileControlBlock {
1697                block_offset: file_entry.offset,
1698                allocated_blocks,
1699                valid_bytes: file_entry.valid_data_size,
1700
1701                nonce,
1702                authentication_tag,
1703
1704                attributes: FileAttribute::new(),
1705                encryption_key: VmgsDatastoreKey::new_zeroed(),
1706            }
1707        });
1708    }
1709
1710    Ok(file_control_blocks)
1711}
1712
1713/// Convert block count to byte count.
1714fn block_count_to_byte_count(block_count: u32) -> u64 {
1715    block_count as u64 * VMGS_BYTES_PER_BLOCK as u64
1716}
1717
1718fn round_up_count(count: usize, pow2: u32) -> u64 {
1719    (count as u64 + pow2 as u64 - 1) & !(pow2 as u64 - 1)
1720}
1721
1722/// Generates a nonce for the encryption. First 4 bytes are a random seed, and last 8 bytes are zero's.
1723fn generate_nonce() -> VmgsNonce {
1724    let mut nonce = VmgsNonce::new_zeroed();
1725    // Generate a 4-byte random seed for nonce
1726    getrandom::fill(&mut nonce[..4]).expect("rng failure");
1727    nonce
1728}
1729
1730/// Increment Nonce by one.
1731fn increment_nonce(nonce: &mut VmgsNonce) -> Result<(), Error> {
1732    // Update the random seed of nonce
1733    getrandom::fill(&mut nonce[..vmgs_format::VMGS_NONCE_RANDOM_SEED_SIZE]).expect("rng failure");
1734
1735    // Increment the counter of nonce by 1.
1736    for i in &mut nonce[vmgs_format::VMGS_NONCE_RANDOM_SEED_SIZE..] {
1737        *i = i.wrapping_add(1);
1738
1739        if *i != 0 {
1740            break;
1741        }
1742    }
1743
1744    Ok(())
1745}
1746
1747/// Checks whether an encryption key is all zero's.
1748fn is_empty_key(encryption_key: &[u8]) -> bool {
1749    encryption_key.iter().all(|&x| x == 0)
1750}
1751
1752/// Encrypts MetadataKey. Returns encrypted_metadata_key.
1753#[cfg_attr(not(with_encryption), expect(unused_variables))]
1754fn encrypt_metadata_key(
1755    encryption_key: &[u8],
1756    nonce: &[u8],
1757    metadata_key: &[u8],
1758    authentication_tag: &mut [u8],
1759) -> Result<Vec<u8>, Error> {
1760    #[cfg(not(with_encryption))]
1761    unreachable!("Encryption requires the encryption feature");
1762    #[cfg(with_encryption)]
1763    {
1764        let encrypted_metadata_key =
1765            crate::encrypt::vmgs_encrypt(encryption_key, nonce, metadata_key, authentication_tag)?;
1766
1767        if encrypted_metadata_key.len() != metadata_key.len() {
1768            return Err(Error::Other(anyhow!(format!(
1769                "encrypted metadata key length ({}) doesn't match metadata key length ({})",
1770                encrypted_metadata_key.len(),
1771                metadata_key.len()
1772            ))));
1773        }
1774        Ok(encrypted_metadata_key)
1775    }
1776}
1777
1778/// Decrypts metadata_key. Returns decrypted_metadata_key.
1779#[cfg_attr(not(with_encryption), expect(unused_variables), expect(dead_code))]
1780fn decrypt_metadata_key(
1781    datastore_key: &[u8],
1782    nonce: &[u8],
1783    metadata_key: &[u8],
1784    authentication_tag: &[u8],
1785) -> Result<Vec<u8>, Error> {
1786    #[cfg(not(with_encryption))]
1787    unreachable!("Encryption requires the encryption feature");
1788    #[cfg(with_encryption)]
1789    {
1790        let decrypted_metadata_key =
1791            crate::encrypt::vmgs_decrypt(datastore_key, nonce, metadata_key, authentication_tag)?;
1792        if decrypted_metadata_key.len() != metadata_key.len() {
1793            return Err(Error::Other(anyhow!(format!(
1794                "decrypted metadata key length ({}) doesn't match metadata key length ({})",
1795                decrypted_metadata_key.len(),
1796                metadata_key.len()
1797            ))));
1798        }
1799
1800        Ok(decrypted_metadata_key)
1801    }
1802}
1803
1804/// Computes the cr32 checksum for a given byte stream.
1805fn compute_crc32(buf: &[u8]) -> u32 {
1806    let mut hasher = crc32fast::Hasher::new();
1807    hasher.update(buf);
1808    hasher.finalize()
1809}
1810
1811#[cfg(feature = "save_restore")]
1812#[expect(missing_docs)]
1813pub mod save_restore {
1814    use super::*;
1815
1816    pub mod state {
1817        use mesh_protobuf::Protobuf;
1818        use std::num::NonZeroU32;
1819
1820        pub type SavedVmgsNonce = [u8; 12];
1821        pub type SavedVmgsAuthTag = [u8; 16];
1822        pub type SavedVmgsDatastoreKey = [u8; 32];
1823
1824        #[derive(Protobuf)]
1825        #[mesh(package = "vmgs")]
1826        pub struct SavedResolvedFileControlBlock {
1827            #[mesh(1)]
1828            pub block_offset: u32,
1829            #[mesh(2)]
1830            pub allocated_blocks: NonZeroU32,
1831            #[mesh(3)]
1832            pub valid_bytes: u64,
1833            #[mesh(4)]
1834            pub nonce: SavedVmgsNonce,
1835            #[mesh(5)]
1836            pub authentication_tag: SavedVmgsAuthTag,
1837            #[mesh(6)]
1838            pub attributes: u32,
1839            #[mesh(7)]
1840            pub encryption_key: SavedVmgsDatastoreKey,
1841        }
1842
1843        #[derive(Protobuf)]
1844        #[mesh(package = "vmgs")]
1845        pub struct SavedVmgsEncryptionKey {
1846            #[mesh(1)]
1847            pub nonce: SavedVmgsNonce,
1848            #[mesh(2)]
1849            pub authentication_tag: SavedVmgsAuthTag,
1850            #[mesh(3)]
1851            pub encryption_key: SavedVmgsDatastoreKey,
1852        }
1853
1854        #[derive(Protobuf)]
1855        #[mesh(package = "vmgs")]
1856        pub struct SavedVmgsState {
1857            #[mesh(1)]
1858            pub active_header_index: usize,
1859            #[mesh(2)]
1860            pub active_header_sequence_number: u32,
1861            #[mesh(3)]
1862            pub version: u32,
1863            #[mesh(4)]
1864            pub fcbs: Vec<(u32, SavedResolvedFileControlBlock)>,
1865            #[mesh(5)]
1866            pub encryption_algorithm: u16,
1867            #[mesh(6)]
1868            pub datastore_key_count: u8,
1869            #[mesh(7)]
1870            pub active_datastore_key_index: Option<usize>,
1871            #[mesh(8)]
1872            pub datastore_keys: [SavedVmgsDatastoreKey; 2],
1873            #[mesh(9)]
1874            pub metadata_key: SavedVmgsDatastoreKey,
1875            #[mesh(10)]
1876            pub encrypted_metadata_keys: [SavedVmgsEncryptionKey; 2],
1877            #[mesh(11)]
1878            pub reprovisioned: bool,
1879        }
1880    }
1881
1882    impl Vmgs {
1883        /// Construct a [`Vmgs`] instance, re-using existing saved-state from an
1884        /// earlier instance.
1885        ///
1886        /// # Safety
1887        ///
1888        /// `open_from_saved` does NOT perform ANY validation on the provided
1889        /// `state`, and will blindly assume that it matches the underlying
1890        /// `storage` instance!
1891        ///
1892        /// Callers MUST ensure that the provided `state` matches the provided
1893        /// `storage`, and that no external entities have modified `storage` between
1894        /// the call to `save` and `open_from_saved`.
1895        ///
1896        /// Failing to do so may result in data corruption/loss, read/write
1897        /// failures, encryption errors, etc... (though, notably: it will _not_
1898        /// result in any memory-unsafety, hence why the function isn't marked
1899        /// `unsafe`).
1900        pub fn open_from_saved(
1901            disk: Disk,
1902            state: state::SavedVmgsState,
1903            logger: Option<Arc<dyn VmgsLogger>>,
1904        ) -> Self {
1905            let state::SavedVmgsState {
1906                active_header_index,
1907                active_header_sequence_number,
1908                version,
1909                fcbs,
1910                encryption_algorithm,
1911                datastore_key_count,
1912                active_datastore_key_index,
1913                datastore_keys,
1914                metadata_key,
1915                encrypted_metadata_keys,
1916                reprovisioned,
1917            } = state;
1918
1919            Self {
1920                storage: VmgsStorage::new(disk),
1921                #[cfg(feature = "inspect")]
1922                stats: Default::default(),
1923
1924                active_header_index,
1925                active_header_sequence_number,
1926                version,
1927                fcbs: fcbs
1928                    .into_iter()
1929                    .map(|(file_id, fcb)| {
1930                        let state::SavedResolvedFileControlBlock {
1931                            block_offset,
1932                            allocated_blocks,
1933                            valid_bytes,
1934                            nonce,
1935                            authentication_tag,
1936                            attributes,
1937                            encryption_key,
1938                        } = fcb;
1939
1940                        (
1941                            FileId(file_id),
1942                            ResolvedFileControlBlock {
1943                                block_offset,
1944                                allocated_blocks,
1945                                valid_bytes,
1946                                nonce,
1947                                authentication_tag,
1948                                attributes: FileAttribute::from(attributes),
1949                                encryption_key,
1950                            },
1951                        )
1952                    })
1953                    .collect(),
1954                encryption_algorithm: EncryptionAlgorithm(encryption_algorithm),
1955                datastore_key_count,
1956                active_datastore_key_index,
1957                datastore_keys,
1958                metadata_key,
1959                encrypted_metadata_keys: encrypted_metadata_keys.map(|k| {
1960                    let state::SavedVmgsEncryptionKey {
1961                        nonce,
1962                        authentication_tag,
1963                        encryption_key,
1964                    } = k;
1965
1966                    VmgsEncryptionKey {
1967                        nonce,
1968                        reserved: 0,
1969                        authentication_tag,
1970                        encryption_key,
1971                    }
1972                }),
1973                reprovisioned,
1974                logger,
1975            }
1976        }
1977
1978        /// Save the in-memory Vmgs file metadata.
1979        ///
1980        /// This saved state can be used alongside `open_from_saved` to obtain a
1981        /// new `Vmgs` instance _without_ needing to invoke any IOs on the
1982        /// underlying storage.
1983        pub fn save(&self) -> state::SavedVmgsState {
1984            let Self {
1985                storage: _,
1986
1987                #[cfg(feature = "inspect")]
1988                    stats: _,
1989
1990                active_header_index,
1991                active_header_sequence_number,
1992                version,
1993                fcbs,
1994                encryption_algorithm,
1995                datastore_key_count,
1996                active_datastore_key_index,
1997                datastore_keys,
1998                metadata_key,
1999                encrypted_metadata_keys,
2000                logger: _,
2001                reprovisioned,
2002            } = self;
2003
2004            state::SavedVmgsState {
2005                active_header_index: *active_header_index,
2006                active_header_sequence_number: *active_header_sequence_number,
2007                version: *version,
2008                fcbs: fcbs
2009                    .iter()
2010                    .map(|(file_id, fcb)| {
2011                        let ResolvedFileControlBlock {
2012                            block_offset,
2013                            allocated_blocks,
2014                            valid_bytes,
2015                            nonce,
2016                            authentication_tag,
2017                            attributes,
2018                            encryption_key,
2019                        } = fcb;
2020
2021                        (
2022                            file_id.0,
2023                            state::SavedResolvedFileControlBlock {
2024                                block_offset: *block_offset,
2025                                allocated_blocks: *allocated_blocks,
2026                                valid_bytes: *valid_bytes,
2027                                nonce: *nonce,
2028                                authentication_tag: *authentication_tag,
2029                                attributes: (*attributes).into(),
2030                                encryption_key: *encryption_key,
2031                            },
2032                        )
2033                    })
2034                    .collect(),
2035                encryption_algorithm: encryption_algorithm.0,
2036                datastore_key_count: *datastore_key_count,
2037                active_datastore_key_index: *active_datastore_key_index,
2038                datastore_keys: *datastore_keys,
2039                metadata_key: *metadata_key,
2040                encrypted_metadata_keys: encrypted_metadata_keys.map(|k| {
2041                    let VmgsEncryptionKey {
2042                        nonce,
2043                        reserved: _,
2044                        authentication_tag,
2045                        encryption_key,
2046                    } = k;
2047
2048                    state::SavedVmgsEncryptionKey {
2049                        nonce,
2050                        authentication_tag,
2051                        encryption_key,
2052                    }
2053                }),
2054                reprovisioned: *reprovisioned,
2055            }
2056        }
2057    }
2058}
2059
2060#[cfg(test)]
2061mod tests {
2062    use super::*;
2063    use pal_async::async_test;
2064    #[cfg(with_encryption)]
2065    use parking_lot::Mutex;
2066    #[cfg(with_encryption)]
2067    use std::sync::Arc;
2068    #[cfg(with_encryption)]
2069    use vmgs_format::VMGS_ENCRYPTION_KEY_SIZE;
2070
2071    const ONE_MEGA_BYTE: u64 = 1024 * 1024;
2072
2073    #[cfg(with_encryption)]
2074    struct TestVmgsLogger {
2075        data: Arc<Mutex<String>>,
2076    }
2077
2078    #[cfg(with_encryption)]
2079    #[async_trait::async_trait]
2080    impl VmgsLogger for TestVmgsLogger {
2081        async fn log_event_fatal(&self, _event: VmgsLogEvent) {
2082            let mut data = self.data.lock();
2083            *data = "test logger".to_string();
2084        }
2085    }
2086
2087    fn new_test_file() -> Disk {
2088        disklayer_ram::ram_disk(4 * ONE_MEGA_BYTE, false).unwrap()
2089    }
2090
2091    #[async_test]
2092    async fn empty_vmgs() {
2093        let disk = new_test_file();
2094
2095        let result = Vmgs::open(disk, None).await;
2096        assert!(matches!(result, Err(Error::EmptyFile)));
2097    }
2098
2099    #[async_test]
2100    async fn format_empty_vmgs() {
2101        let disk = new_test_file();
2102        let result = Vmgs::format_new(disk, None).await;
2103        assert!(result.is_ok());
2104    }
2105
2106    #[async_test]
2107    async fn basic_read_write() {
2108        let disk = new_test_file();
2109        let mut vmgs = Vmgs::format_new(disk, None).await.unwrap();
2110        assert_eq!(vmgs.active_header_index, 0);
2111        assert_eq!(vmgs.active_header_sequence_number, 1);
2112        assert_eq!(vmgs.version, VMGS_VERSION_3_0);
2113
2114        // write
2115        let buf = b"hello world";
2116        vmgs.write_file(FileId::BIOS_NVRAM, buf).await.unwrap();
2117
2118        assert_eq!(vmgs.active_header_index, 1);
2119        assert_eq!(vmgs.active_header_sequence_number, 2);
2120
2121        // read
2122        let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2123
2124        assert_eq!(buf, &*read_buf);
2125        assert_eq!(vmgs.active_header_index, 1);
2126        assert_eq!(vmgs.active_header_sequence_number, 2);
2127    }
2128
2129    #[async_test]
2130    async fn basic_read_write_large() {
2131        let disk = new_test_file();
2132        let mut vmgs = Vmgs::format_new(disk, None).await.unwrap();
2133
2134        // write
2135        let buf: Vec<u8> = (0..).map(|x| x as u8).take(1024 * 4 + 1).collect();
2136
2137        vmgs.write_file(FileId::BIOS_NVRAM, &buf).await.unwrap();
2138
2139        assert_eq!(vmgs.active_header_index, 1);
2140        assert_eq!(vmgs.active_header_sequence_number, 2);
2141
2142        // read
2143        let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2144
2145        assert_eq!(buf, read_buf);
2146        assert_eq!(vmgs.active_header_index, 1);
2147        assert_eq!(vmgs.active_header_sequence_number, 2);
2148
2149        // write
2150        let buf: Vec<u8> = (0..).map(|x| x as u8).take(1024 * 4 * 4 + 1).collect();
2151
2152        vmgs.write_file(FileId::TPM_PPI, &buf).await.unwrap();
2153
2154        assert_eq!(vmgs.active_header_index, 0);
2155        assert_eq!(vmgs.active_header_sequence_number, 3);
2156
2157        // read
2158        let read_buf = vmgs.read_file(FileId::TPM_PPI).await.unwrap();
2159
2160        assert_eq!(buf, read_buf);
2161        assert_eq!(vmgs.active_header_index, 0);
2162        assert_eq!(vmgs.active_header_sequence_number, 3);
2163
2164        // write
2165        let buf: Vec<u8> = (0..).map(|x| x as u8).take(1024 * 4 * 4 * 4 + 1).collect();
2166
2167        vmgs.write_file(FileId::GUEST_FIRMWARE, &buf).await.unwrap();
2168
2169        assert_eq!(vmgs.active_header_index, 1);
2170        assert_eq!(vmgs.active_header_sequence_number, 4);
2171
2172        // read
2173        let read_buf = vmgs.read_file(FileId::GUEST_FIRMWARE).await.unwrap();
2174
2175        assert_eq!(buf, read_buf);
2176        assert_eq!(vmgs.active_header_index, 1);
2177        assert_eq!(vmgs.active_header_sequence_number, 4);
2178    }
2179
2180    #[async_test]
2181    async fn open_existing_file() {
2182        let buf_1 = b"hello world";
2183        let buf_2 = b"short sentence";
2184        let buf_3 = b"funny joke";
2185
2186        // Create VMGS file and write to different FileId's
2187        let disk = new_test_file();
2188        let mut vmgs = Vmgs::format_new(disk.clone(), None).await.unwrap();
2189
2190        vmgs.write_file(FileId::BIOS_NVRAM, buf_1).await.unwrap();
2191
2192        assert_eq!(vmgs.active_header_index, 1);
2193        assert_eq!(vmgs.active_header_sequence_number, 2);
2194        assert_eq!(vmgs.fcbs[&FileId(0)].block_offset, 4);
2195        assert_eq!(vmgs.fcbs[&FileId(1)].block_offset, 5);
2196
2197        vmgs.write_file(FileId::TPM_PPI, buf_2).await.unwrap();
2198
2199        assert_eq!(vmgs.active_header_index, 0);
2200        assert_eq!(vmgs.active_header_sequence_number, 3);
2201        assert_eq!(vmgs.fcbs[&FileId(0)].block_offset, 2);
2202        assert_eq!(vmgs.fcbs[&FileId(1)].block_offset, 5);
2203        assert_eq!(vmgs.fcbs[&FileId(2)].block_offset, 6);
2204
2205        vmgs.write_file(FileId::BIOS_NVRAM, buf_3).await.unwrap();
2206
2207        assert_eq!(vmgs.active_header_index, 1);
2208        assert_eq!(vmgs.active_header_sequence_number, 4);
2209        assert_eq!(vmgs.fcbs[&FileId(0)].block_offset, 4);
2210        assert_eq!(vmgs.fcbs[&FileId(1)].block_offset, 7);
2211        assert_eq!(vmgs.fcbs[&FileId(2)].block_offset, 6);
2212
2213        // Re-open VMGS file and read from the same FileId's
2214        drop(vmgs);
2215
2216        let mut vmgs = Vmgs::open(disk, None).await.unwrap();
2217
2218        assert_eq!(vmgs.fcbs[&FileId(0)].block_offset, 4);
2219        assert_eq!(vmgs.fcbs[&FileId(1)].block_offset, 7);
2220        assert_eq!(vmgs.fcbs[&FileId(2)].block_offset, 6);
2221        let read_buf_1 = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2222
2223        assert_eq!(buf_3, &*read_buf_1);
2224        assert_eq!(vmgs.active_header_index, 1);
2225        assert_eq!(vmgs.active_header_sequence_number, 4);
2226
2227        let read_buf_2 = vmgs.read_file(FileId::TPM_PPI).await.unwrap();
2228
2229        assert_eq!(buf_2, &*read_buf_2);
2230        assert_eq!(vmgs.fcbs[&FileId(0)].block_offset, 4);
2231        assert_eq!(vmgs.fcbs[&FileId(1)].block_offset, 7);
2232        assert_eq!(vmgs.fcbs[&FileId(2)].block_offset, 6);
2233    }
2234
2235    #[async_test]
2236    async fn multiple_read_write() {
2237        let disk = new_test_file();
2238        let mut vmgs = Vmgs::format_new(disk, None).await.unwrap();
2239
2240        let buf_1 = b"Data data data";
2241        let buf_2 = b"password";
2242        let buf_3 = b"other data data";
2243
2244        vmgs.write_file(FileId::BIOS_NVRAM, buf_1).await.unwrap();
2245        let read_buf_1 = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2246        assert_eq!(buf_1, &*read_buf_1);
2247        assert_eq!(vmgs.active_header_index, 1);
2248        assert_eq!(vmgs.active_header_sequence_number, 2);
2249
2250        vmgs.write_file(FileId::TPM_PPI, buf_2).await.unwrap();
2251        let info = vmgs.get_file_info(FileId::TPM_PPI).unwrap();
2252        assert_eq!(info.valid_bytes as usize, buf_2.len());
2253        let read_buf_2 = vmgs.read_file(FileId::TPM_PPI).await.unwrap();
2254        assert_eq!(buf_2, &*read_buf_2);
2255        assert_eq!(vmgs.active_header_index, 0);
2256        assert_eq!(vmgs.active_header_sequence_number, 3);
2257
2258        vmgs.write_file(FileId::BIOS_NVRAM, buf_3).await.unwrap();
2259        let read_buf_3 = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2260        assert_eq!(buf_3, &*read_buf_3);
2261        assert_eq!(vmgs.active_header_index, 1);
2262        assert_eq!(vmgs.active_header_sequence_number, 4);
2263
2264        vmgs.write_file(FileId::BIOS_NVRAM, buf_1).await.unwrap();
2265        let read_buf_1 = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2266        assert_eq!(buf_1, &*read_buf_1);
2267        assert_eq!(vmgs.active_header_index, 0);
2268        assert_eq!(vmgs.active_header_sequence_number, 5);
2269
2270        vmgs.write_file(FileId::TPM_PPI, buf_2).await.unwrap();
2271        let read_buf_2 = vmgs.read_file(FileId::TPM_PPI).await.unwrap();
2272        assert_eq!(buf_2, &*read_buf_2);
2273        assert_eq!(vmgs.active_header_index, 1);
2274        assert_eq!(vmgs.active_header_sequence_number, 6);
2275
2276        vmgs.write_file(FileId::BIOS_NVRAM, buf_3).await.unwrap();
2277        let read_buf_3 = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2278        assert_eq!(buf_3, &*read_buf_3);
2279        assert_eq!(vmgs.active_header_index, 0);
2280        assert_eq!(vmgs.active_header_sequence_number, 7);
2281    }
2282
2283    #[async_test]
2284    async fn test_insufficient_resources() {
2285        let disk = new_test_file();
2286        let mut vmgs = Vmgs::format_new(disk, None).await.unwrap();
2287
2288        let buf: Vec<u8> = vec![1; ONE_MEGA_BYTE as usize * 5];
2289        let result = vmgs.write_file(FileId::BIOS_NVRAM, &buf).await;
2290        assert!(result.is_err());
2291        if let Err(e) = result {
2292            match e {
2293                Error::InsufficientResources => (),
2294                _ => panic!("Wrong error returned"),
2295            }
2296        } else {
2297            panic!("Should have returned Insufficient resources error");
2298        }
2299    }
2300
2301    #[async_test]
2302    async fn test_empty_write() {
2303        let disk = new_test_file();
2304        let mut vmgs = Vmgs::format_new(disk, None).await.unwrap();
2305
2306        let buf: Vec<u8> = Vec::new();
2307        vmgs.write_file(FileId::BIOS_NVRAM, &buf).await.unwrap();
2308
2309        // read
2310        let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2311
2312        assert_eq!(buf, read_buf);
2313        assert_eq!(read_buf.len(), 0);
2314        assert_eq!(vmgs.active_header_index, 1);
2315        assert_eq!(vmgs.active_header_sequence_number, 2);
2316    }
2317
2318    // general functions
2319    #[test]
2320    fn test_block_count_to_byte_count() {
2321        let block_count = 10;
2322        let byte_count = block_count_to_byte_count(block_count);
2323        assert!(byte_count == block_count as u64 * VMGS_BYTES_PER_BLOCK as u64);
2324    }
2325
2326    #[test]
2327    fn test_validate_header() {
2328        let mut header = VmgsHeader::new_zeroed();
2329        header.signature = VMGS_SIGNATURE;
2330        header.version = VMGS_VERSION_3_0;
2331        header.header_size = size_of::<VmgsHeader>() as u32;
2332        header.file_table_offset = VMGS_MIN_FILE_BLOCK_OFFSET;
2333        header.file_table_size = VMGS_FILE_TABLE_BLOCK_SIZE;
2334        header.checksum = compute_crc32(header.as_bytes());
2335
2336        let result = validate_header(&header);
2337        assert!(result.is_ok());
2338
2339        let mut header_signature = header;
2340        header_signature.signature = 0;
2341        header_signature.checksum = 0;
2342        header_signature.checksum = compute_crc32(header_signature.as_bytes());
2343        let result = validate_header(&header_signature);
2344        match result {
2345            Err(Error::InvalidFormat(err)) => assert_eq!(err, "Invalid header signature"),
2346            _ => panic!(),
2347        };
2348
2349        let mut header_version = header;
2350        header_version.version = 0;
2351        header_version.checksum = 0;
2352        header_version.checksum = compute_crc32(header_version.as_bytes());
2353        match validate_header(&header_version) {
2354            Err(Error::InvalidFormat(err)) => assert_eq!(err, "Invalid header version"),
2355            _ => panic!(),
2356        };
2357
2358        let mut header_header_size = header;
2359        header_header_size.header_size = 0;
2360        header_header_size.checksum = 0;
2361        header_header_size.checksum = compute_crc32(header_header_size.as_bytes());
2362        match validate_header(&header_header_size) {
2363            Err(Error::InvalidFormat(err)) => assert_eq!(err, "Invalid header size"),
2364            _ => panic!(),
2365        };
2366
2367        let mut header_ft_offset = header;
2368        header_ft_offset.file_table_offset = 0;
2369        header_ft_offset.checksum = 0;
2370        header_ft_offset.checksum = compute_crc32(header_ft_offset.as_bytes());
2371        match validate_header(&header_ft_offset) {
2372            Err(Error::InvalidFormat(err)) => assert_eq!(err, "Invalid file table offset"),
2373            _ => panic!(),
2374        };
2375
2376        let mut header_ft_size = header;
2377        header_ft_size.file_table_size = 0;
2378        header_ft_size.checksum = 0;
2379        header_ft_size.checksum = compute_crc32(header_ft_size.as_bytes());
2380        match validate_header(&header_ft_size) {
2381            Err(Error::InvalidFormat(err)) => assert_eq!(err, "Invalid file table size"),
2382            _ => panic!(),
2383        };
2384    }
2385
2386    #[test]
2387    fn test_initialize_file_metadata() {
2388        let mut file_table = VmgsFileTable::new_zeroed();
2389
2390        file_table.entries[0].offset = 6;
2391        file_table.entries[0].allocation_size = 1;
2392        file_table.entries[1].offset = 2;
2393        file_table.entries[1].allocation_size = 1;
2394        file_table.entries[2].offset = 4;
2395        file_table.entries[2].allocation_size = 5;
2396        file_table.entries[3].offset = 3;
2397        file_table.entries[3].allocation_size = 3;
2398
2399        let block_capacity = 1000;
2400
2401        let fcbs = initialize_file_metadata(&file_table, VMGS_VERSION_3_0, block_capacity).unwrap();
2402        // assert VmgsFileEntry correctly converted to FileControlBlock
2403        assert!(fcbs[&FileId(0)].block_offset == 6);
2404        assert!(fcbs[&FileId(0)].allocated_blocks.get() == 1);
2405        assert!(fcbs[&FileId(1)].block_offset == 2);
2406        assert!(fcbs[&FileId(1)].allocated_blocks.get() == 1);
2407        assert!(fcbs[&FileId(2)].block_offset == 4);
2408        assert!(fcbs[&FileId(2)].allocated_blocks.get() == 5);
2409        assert!(fcbs[&FileId(3)].block_offset == 3);
2410        assert!(fcbs[&FileId(3)].allocated_blocks.get() == 3);
2411    }
2412
2413    #[test]
2414    fn test_round_up_count() {
2415        assert!(round_up_count(0, 4096) == 0);
2416        assert!(round_up_count(1, 4096) == 4096);
2417        assert!(round_up_count(4095, 4096) == 4096);
2418        assert!(round_up_count(4096, 4096) == 4096);
2419        assert!(round_up_count(4097, 4096) == 8192);
2420    }
2421
2422    #[async_test]
2423    async fn test_header_sequence_overflow() {
2424        let disk = new_test_file();
2425        let mut vmgs = Vmgs::format_new(disk, None).await.unwrap();
2426
2427        vmgs.active_header_sequence_number = u32::MAX;
2428
2429        // write
2430        let buf = b"hello world";
2431        vmgs.write_file(FileId::BIOS_NVRAM, buf).await.unwrap();
2432
2433        assert_eq!(vmgs.active_header_index, 1);
2434        assert_eq!(vmgs.active_header_sequence_number, 0);
2435
2436        vmgs.set_active_header(0, u32::MAX);
2437
2438        let mut new_header = VmgsHeader::new_zeroed();
2439        vmgs.update_header(&mut new_header).await.unwrap();
2440
2441        assert_eq!(vmgs.active_header_index, 1);
2442        assert_eq!(vmgs.active_header_sequence_number, 0);
2443        assert_eq!(new_header.sequence, 0);
2444    }
2445
2446    #[cfg(with_encryption)]
2447    #[async_test]
2448    async fn write_file_v3() {
2449        let disk = new_test_file();
2450        let mut vmgs = Vmgs::format_new(disk.clone(), None).await.unwrap();
2451        let encryption_key = [12; VMGS_ENCRYPTION_KEY_SIZE];
2452
2453        // write
2454        let buf = b"hello world";
2455        let buf_1 = b"hello universe";
2456        vmgs.write_file(FileId::BIOS_NVRAM, buf).await.unwrap();
2457        vmgs.update_encryption_key(&encryption_key, EncryptionAlgorithm::AES_GCM)
2458            .await
2459            .unwrap();
2460        vmgs.write_file_encrypted(FileId::TPM_PPI, buf_1)
2461            .await
2462            .unwrap();
2463
2464        // read
2465        let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2466        assert_eq!(buf, &*read_buf);
2467        let info = vmgs.get_file_info(FileId::TPM_PPI).unwrap();
2468        assert_eq!(info.valid_bytes as usize, buf_1.len());
2469        let read_buf = vmgs.read_file(FileId::TPM_PPI).await.unwrap();
2470        assert_eq!(buf_1, &*read_buf);
2471
2472        // Read the file after re-opening the vmgs file
2473        drop(vmgs);
2474        let mut vmgs = Vmgs::open(disk, None).await.unwrap();
2475        let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2476        assert_eq!(buf, read_buf.as_bytes());
2477        let info = vmgs.get_file_info(FileId::TPM_PPI).unwrap();
2478        assert_eq!(info.valid_bytes as usize, buf_1.len());
2479        let read_buf = vmgs.read_file(FileId::TPM_PPI).await.unwrap();
2480        assert_ne!(buf_1, read_buf.as_bytes());
2481
2482        // Unlock datastore
2483        vmgs.unlock_with_encryption_key(&encryption_key)
2484            .await
2485            .unwrap();
2486        let info = vmgs.get_file_info(FileId::TPM_PPI).unwrap();
2487        assert_eq!(info.valid_bytes as usize, buf_1.len());
2488        let read_buf = vmgs.read_file(FileId::TPM_PPI).await.unwrap();
2489        assert_eq!(buf_1, &*read_buf);
2490    }
2491
2492    #[cfg(with_encryption)]
2493    #[async_test]
2494    async fn overwrite_file_v3() {
2495        let disk = new_test_file();
2496        let mut vmgs = Vmgs::format_new(disk, None).await.unwrap();
2497        let encryption_key = [1; VMGS_ENCRYPTION_KEY_SIZE];
2498        let buf = vec![1; 8 * 1024];
2499        let buf_1 = vec![2; 8 * 1024];
2500
2501        // Add root key.
2502        vmgs.add_new_encryption_key(&encryption_key, EncryptionAlgorithm::AES_GCM)
2503            .await
2504            .unwrap();
2505        assert_eq!(vmgs.active_datastore_key_index, Some(0));
2506
2507        // Write a file to the store.
2508        vmgs.write_file_encrypted(FileId::BIOS_NVRAM, &buf)
2509            .await
2510            .unwrap();
2511
2512        // Encrypt and overwrite the original file.
2513        vmgs.write_file_encrypted(FileId::BIOS_NVRAM, &buf_1)
2514            .await
2515            .unwrap();
2516
2517        // Verify new file contents
2518        let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2519        assert_eq!(buf_1, read_buf);
2520    }
2521
2522    #[cfg(with_encryption)]
2523    #[async_test]
2524    async fn file_encryption() {
2525        let buf: Vec<u8> = (0..255).collect();
2526        let encryption_key = [1; VMGS_ENCRYPTION_KEY_SIZE];
2527
2528        let disk = new_test_file();
2529        let mut vmgs = Vmgs::format_new(disk.clone(), None).await.unwrap();
2530
2531        // Add datastore key.
2532        vmgs.add_new_encryption_key(&encryption_key, EncryptionAlgorithm::AES_GCM)
2533            .await
2534            .unwrap();
2535        assert_eq!(vmgs.active_datastore_key_index, Some(0));
2536
2537        // Write a file to the store.
2538        vmgs.write_file_encrypted(FileId::BIOS_NVRAM, &buf)
2539            .await
2540            .unwrap();
2541
2542        // Read the file, without closing the datastore
2543        let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2544        assert_eq!(buf, read_buf);
2545
2546        drop(vmgs);
2547
2548        // Read the file, after closing and reopening the data store.
2549        let mut vmgs = Vmgs::open(disk, None).await.unwrap();
2550
2551        let info = vmgs.get_file_info(FileId::BIOS_NVRAM).unwrap();
2552        assert_eq!(info.valid_bytes as usize, buf.len());
2553
2554        // Unlock the store.
2555
2556        vmgs.unlock_with_encryption_key(&encryption_key)
2557            .await
2558            .unwrap();
2559
2560        assert_eq!(vmgs.active_datastore_key_index, Some(0));
2561
2562        // Change to a new datastore key.
2563        let new_encryption_key = [2; VMGS_ENCRYPTION_KEY_SIZE];
2564        vmgs.add_new_encryption_key(&new_encryption_key, EncryptionAlgorithm::AES_GCM)
2565            .await
2566            .unwrap();
2567        assert_eq!(vmgs.active_datastore_key_index, Some(1));
2568        vmgs.remove_encryption_key(0).await.unwrap();
2569
2570        let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2571        assert_eq!(buf, read_buf);
2572    }
2573
2574    #[cfg(with_encryption)]
2575    #[async_test]
2576    async fn add_new_encryption_key() {
2577        let buf: Vec<u8> = (0..255).collect();
2578        let encryption_key = [1; VMGS_ENCRYPTION_KEY_SIZE];
2579        let new_encryption_key = [5; VMGS_ENCRYPTION_KEY_SIZE];
2580
2581        // Initialize version 3 data store
2582        let disk = new_test_file();
2583        let mut vmgs = Vmgs::format_new(disk.clone(), None).await.unwrap();
2584
2585        // Add datastore key.
2586        vmgs.add_new_encryption_key(&encryption_key, EncryptionAlgorithm::AES_GCM)
2587            .await
2588            .unwrap();
2589        assert_eq!(vmgs.active_datastore_key_index, Some(0));
2590
2591        // Write a file to the store.
2592        vmgs.write_file_encrypted(FileId::BIOS_NVRAM, &buf)
2593            .await
2594            .unwrap();
2595
2596        // Read the file, after closing and reopening the data store.
2597        drop(vmgs);
2598        let mut vmgs = Vmgs::open(disk.clone(), None).await.unwrap();
2599        vmgs.unlock_with_encryption_key(&encryption_key)
2600            .await
2601            .unwrap();
2602        assert_eq!(vmgs.active_datastore_key_index, Some(0));
2603        let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2604        assert_eq!(read_buf, buf);
2605
2606        // Add new datastore key.
2607        vmgs.add_new_encryption_key(&new_encryption_key, EncryptionAlgorithm::AES_GCM)
2608            .await
2609            .unwrap();
2610        assert_eq!(vmgs.active_datastore_key_index, Some(1));
2611
2612        // Read the file by using two different datastore keys, after closing and reopening the data store.
2613        drop(vmgs);
2614        let mut vmgs = Vmgs::open(disk, None).await.unwrap();
2615        vmgs.unlock_with_encryption_key(&encryption_key)
2616            .await
2617            .unwrap();
2618        assert_eq!(vmgs.active_datastore_key_index, Some(0));
2619        vmgs.unlock_with_encryption_key(&new_encryption_key)
2620            .await
2621            .unwrap();
2622        assert_eq!(vmgs.active_datastore_key_index, Some(1));
2623        let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2624        assert_eq!(read_buf, buf);
2625
2626        // Remove the newly added datastore key and add it again.
2627        vmgs.remove_encryption_key(1).await.unwrap();
2628        vmgs.add_new_encryption_key(&new_encryption_key, EncryptionAlgorithm::AES_GCM)
2629            .await
2630            .unwrap();
2631        assert_eq!(vmgs.active_datastore_key_index, Some(1));
2632
2633        // Remove the old datastore key
2634        vmgs.remove_encryption_key(0).await.unwrap();
2635        let result = vmgs.unlock_with_encryption_key(&encryption_key).await;
2636        assert!(matches!(result, Err(Error::Other(_))));
2637
2638        // Try to remove the old datastore key again
2639        let result = vmgs.remove_encryption_key(0).await;
2640        assert!(matches!(result, Err(Error::Other(_))));
2641
2642        // Remove the new datastore key and try to read file content, which should be in encrypted state
2643        vmgs.remove_encryption_key(1).await.unwrap();
2644        let read_buf = vmgs.read_file_raw(FileId::BIOS_NVRAM).await;
2645        assert_ne!(read_buf.unwrap(), buf);
2646    }
2647
2648    #[cfg(with_encryption)]
2649    #[async_test]
2650    async fn test_write_file_encrypted() {
2651        // Call write_file_encrypted on an unencrypted VMGS and check that plaintext was written
2652
2653        // Initialize version 3 data store
2654        let disk = new_test_file();
2655        let mut vmgs = Vmgs::format_new(disk.clone(), None).await.unwrap();
2656        let buf = b"This is plaintext";
2657
2658        // call write file encrypted
2659        vmgs.write_file_encrypted(FileId::BIOS_NVRAM, buf)
2660            .await
2661            .unwrap();
2662
2663        // Read
2664        let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2665        assert_eq!(vmgs.encryption_algorithm, EncryptionAlgorithm::NONE);
2666        assert_eq!(buf, &*read_buf);
2667
2668        // ensure that when we re-create the VMGS object, we can still read the
2669        // FileId as plaintext
2670        drop(vmgs);
2671        let mut vmgs = Vmgs::open(disk, None).await.unwrap();
2672
2673        let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2674        assert_eq!(vmgs.encryption_algorithm, EncryptionAlgorithm::NONE);
2675        assert_eq!(buf, &*read_buf);
2676    }
2677
2678    #[cfg(with_encryption)]
2679    #[async_test]
2680    async fn test_logger() {
2681        let disk = new_test_file();
2682        let data = Arc::new(Mutex::new(String::new()));
2683        let mut vmgs = Vmgs::format_new(
2684            disk.clone(),
2685            Some(Arc::new(TestVmgsLogger { data: data.clone() })),
2686        )
2687        .await
2688        .unwrap();
2689        let encryption_key = [12; VMGS_ENCRYPTION_KEY_SIZE];
2690
2691        // write
2692        let buf = b"hello world";
2693        vmgs.update_encryption_key(&encryption_key, EncryptionAlgorithm::AES_GCM)
2694            .await
2695            .unwrap();
2696        vmgs.write_file_encrypted(FileId::BIOS_NVRAM, buf)
2697            .await
2698            .unwrap();
2699
2700        let fcb = vmgs.fcbs.get_mut(&FileId::BIOS_NVRAM).unwrap();
2701
2702        // Manipulate the nonce and expect the read to fail.
2703        fcb.nonce[0] ^= 1;
2704
2705        // read and expect to fail
2706        let result = vmgs.read_file(FileId::BIOS_NVRAM).await;
2707        assert!(result.is_err());
2708
2709        // verify that the string is logged
2710        let result = data.lock();
2711        assert_eq!(*result, "test logger");
2712    }
2713
2714    #[cfg(with_encryption)]
2715    #[async_test]
2716    async fn update_key() {
2717        let buf: Vec<u8> = (0..255).collect();
2718        let encryption_key = [1; VMGS_ENCRYPTION_KEY_SIZE];
2719
2720        let disk = new_test_file();
2721        let mut vmgs = Vmgs::format_new(disk.clone(), None).await.unwrap();
2722
2723        // Add datastore key.
2724        vmgs.update_encryption_key(&encryption_key, EncryptionAlgorithm::AES_GCM)
2725            .await
2726            .unwrap();
2727        assert_eq!(vmgs.active_datastore_key_index, Some(0));
2728        assert_eq!(vmgs.datastore_key_count, 1);
2729
2730        // Write a file to the store.
2731        vmgs.write_file_encrypted(FileId::BIOS_NVRAM, &buf)
2732            .await
2733            .unwrap();
2734
2735        // Read the file, without closing the datastore
2736        let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2737        assert_eq!(buf, read_buf);
2738
2739        // Close and reopen the store
2740        drop(vmgs);
2741        let mut vmgs = Vmgs::open(disk.clone(), None).await.unwrap();
2742
2743        // Unlock the store.
2744        vmgs.unlock_with_encryption_key(&encryption_key)
2745            .await
2746            .unwrap();
2747        assert_eq!(vmgs.active_datastore_key_index, Some(0));
2748
2749        // Read the file again
2750        let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2751        assert_eq!(buf, read_buf);
2752
2753        // Change to a new datastore key.
2754        let new_encryption_key = [2; VMGS_ENCRYPTION_KEY_SIZE];
2755        vmgs.update_encryption_key(&new_encryption_key, EncryptionAlgorithm::AES_GCM)
2756            .await
2757            .unwrap();
2758        assert_eq!(vmgs.active_datastore_key_index, Some(1));
2759        assert_eq!(vmgs.datastore_key_count, 1);
2760
2761        // Read the file again
2762        let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2763        assert_eq!(buf, read_buf);
2764
2765        // Close and reopen the store
2766        drop(vmgs);
2767        let mut vmgs = Vmgs::open(disk, None).await.unwrap();
2768
2769        // Unlock the store.
2770        vmgs.unlock_with_encryption_key(&new_encryption_key)
2771            .await
2772            .unwrap();
2773        assert_eq!(vmgs.active_datastore_key_index, Some(1));
2774
2775        // Read the file again
2776        let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2777        assert_eq!(buf, read_buf);
2778    }
2779
2780    #[cfg(with_encryption)]
2781    #[async_test]
2782    async fn update_key_no_space() {
2783        let buf: Vec<u8> = (0..255).collect();
2784        let encryption_key = [1; VMGS_ENCRYPTION_KEY_SIZE];
2785
2786        let disk = new_test_file();
2787        let mut vmgs = Vmgs::format_new(disk.clone(), None).await.unwrap();
2788
2789        // Add datastore key.
2790        vmgs.update_encryption_key(&encryption_key, EncryptionAlgorithm::AES_GCM)
2791            .await
2792            .unwrap();
2793        assert_eq!(vmgs.active_datastore_key_index, Some(0));
2794        assert_eq!(vmgs.datastore_key_count, 1);
2795
2796        // Write a file to the store.
2797        vmgs.write_file_encrypted(FileId::BIOS_NVRAM, &buf)
2798            .await
2799            .unwrap();
2800
2801        // Read the file, without closing the datastore
2802        let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2803        assert_eq!(buf, read_buf);
2804
2805        // Add a new datastore key, but don't remove the old one.
2806        let new_encryption_key = [2; VMGS_ENCRYPTION_KEY_SIZE];
2807        vmgs.add_new_encryption_key(&new_encryption_key, EncryptionAlgorithm::AES_GCM)
2808            .await
2809            .unwrap();
2810        assert_eq!(vmgs.active_datastore_key_index, Some(1));
2811        assert_eq!(vmgs.datastore_key_count, 2);
2812
2813        // Close and reopen the store
2814        drop(vmgs);
2815        let mut vmgs = Vmgs::open(disk.clone(), None).await.unwrap();
2816
2817        // Unlock the store.
2818        vmgs.unlock_with_encryption_key(&new_encryption_key)
2819            .await
2820            .unwrap();
2821        assert_eq!(vmgs.active_datastore_key_index, Some(1));
2822
2823        // Add yet another new datastore key. This should remove both previous keys
2824        let another_encryption_key = [2; VMGS_ENCRYPTION_KEY_SIZE];
2825        vmgs.update_encryption_key(&another_encryption_key, EncryptionAlgorithm::AES_GCM)
2826            .await
2827            .unwrap();
2828        assert_eq!(vmgs.active_datastore_key_index, Some(0));
2829        assert_eq!(vmgs.datastore_key_count, 1);
2830
2831        // Close and reopen the store
2832        drop(vmgs);
2833        let mut vmgs = Vmgs::open(disk, None).await.unwrap();
2834
2835        // Unlock the store.
2836        vmgs.unlock_with_encryption_key(&another_encryption_key)
2837            .await
2838            .unwrap();
2839        assert_eq!(vmgs.active_datastore_key_index, Some(0));
2840
2841        // Read the file again
2842        let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2843        assert_eq!(buf, read_buf);
2844    }
2845}