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