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