vmgs/
vmgs_impl.rs

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