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