1use super::types::*;
4use crate::io_buffers::IoBuffer;
5use crate::macros::numerical_enum;
6use crate::macros::on_disk_struct::{on_disk_struct, OnDiskStruct};
7use crate::misc_helpers::invalid_data;
8use crate::sync_primitives::{Mutex, MutexGuard};
9use crate::{Storage, StorageExt};
10use maybe_async::maybe_async;
11use std::collections::HashMap;
12use std::mem::size_of;
13use std::num::TryFromIntError;
14use std::sync::atomic::{AtomicBool, AtomicU16, AtomicU32, AtomicU64, AtomicU8, Ordering};
15use std::{cmp, io};
16use tracing::error;
17
18pub(super) const MAGIC: u32 = 0x51_46_49_fb;
20
21const MAX_FILE_LENGTH: u64 = 0x0100_0000_0000_0000u64;
23
24pub(super) const MAX_OFFSET: HostOffset = HostOffset(MAX_FILE_LENGTH - 512);
26
27pub(super) const MIN_CLUSTER_SIZE: usize = 512;
31
32pub(super) const MAX_CLUSTER_SIZE: usize = 2 * 1024 * 1024;
36
37pub(super) const MIN_REFCOUNT_WIDTH: usize = 1;
39
40pub(super) const MAX_REFCOUNT_WIDTH: usize = 64;
42
43on_disk_struct! {
44struct V2Header/BE, no_gaps {
46 magic: u32[0],
48
49 version: u32[4],
51
52 backing_file_offset: u64[8],
58
59 backing_file_size: u32[16],
62
63 cluster_bits: u32[20],
72
73 size: AtomicU64[24],
81
82 crypt_method: u32[32],
88
89 l1_size: AtomicU32[36],
91
92 l1_table_offset: AtomicU64[40],
95
96 refcount_table_offset: AtomicU64[48],
99
100 refcount_table_clusters: AtomicU32[56],
102
103 nb_snapshots: u32[60],
105
106 snapshots_offset: u64[64],
109}
110}
111
112on_disk_struct! {
113struct V3HeaderBase/BE, no_gaps {
115 incompatible_features: u64[0],
136
137 compatible_features: u64[8],
145
146 autoclear_features: u64[16],
162
163 refcount_order: u32[24],
167
168 header_length: u32[28],
171}
172}
173
174impl Default for V3HeaderBase {
175 fn default() -> Self {
176 V3HeaderBase {
177 incompatible_features: 0,
178 compatible_features: 0,
179 autoclear_features: 0,
180 refcount_order: 4,
181 header_length: (V2Header::ON_DISK_SIZE + V3HeaderBase::ON_DISK_SIZE) as u32,
182 }
183 }
184}
185
186numerical_enum! {
187 pub(super) enum IncompatibleFeatures as u64 {
189 Dirty = 1 << 0,
190 Corrupt = 1 << 1,
191 ExternalDataFile = 1 << 2,
192 CompressionType = 1 << 3,
193 ExtendedL2Entries = 1 << 4,
194 }
195}
196
197impl From<IncompatibleFeatures> for (FeatureType, u8) {
198 fn from(feat: IncompatibleFeatures) -> (FeatureType, u8) {
200 assert!((feat as u64).is_power_of_two());
201 (
202 FeatureType::Incompatible,
203 (feat as u64).trailing_zeros() as u8,
204 )
205 }
206}
207
208numerical_enum! {
209 pub(super) enum CompatibleFeatures as u64 {
211 LazyRefcounts = 1 << 0,
212 }
213}
214
215impl From<CompatibleFeatures> for (FeatureType, u8) {
216 fn from(feat: CompatibleFeatures) -> (FeatureType, u8) {
218 assert!((feat as u64).is_power_of_two());
219 (
220 FeatureType::Compatible,
221 (feat as u64).trailing_zeros() as u8,
222 )
223 }
224}
225
226numerical_enum! {
227 pub(super) enum AutoclearFeatures as u64 {
229 Bitmaps = 1 << 0,
230 RawExternalData = 1 << 1,
231 }
232}
233
234impl From<AutoclearFeatures> for (FeatureType, u8) {
235 fn from(feat: AutoclearFeatures) -> (FeatureType, u8) {
237 assert!((feat as u64).is_power_of_two());
238 (FeatureType::Autoclear, (feat as u64).trailing_zeros() as u8)
239 }
240}
241
242numerical_enum! {
243 pub(super) enum HeaderExtensionType as u32 {
245 End = 0,
247
248 BackingFileFormat = 0xe2792aca,
250
251 FeatureNameTable = 0x6803f857,
253
254 ExternalDataFileName = 0x44415441,
256 }
257}
258
259on_disk_struct! {
260#[derive(Default)]
262struct HeaderExtensionHeader/BE, no_gaps {
263 extension_type: u32[0],
265
266 length: u32[4],
268}
269}
270
271numerical_enum! {
272 #[derive(Hash)]
274 pub(super) enum FeatureType as u8 {
275 Incompatible = 0,
276 Compatible = 1,
277 Autoclear = 2,
278 }
279}
280
281#[derive(Debug, Clone, Eq, PartialEq)]
283pub(super) enum HeaderExtension {
284 BackingFileFormat(String),
286
287 FeatureNameTable(HashMap<(FeatureType, u8), String>),
289
290 ExternalDataFileName(String),
292
293 Unknown {
295 extension_type: u32,
297 data: Vec<u8>,
299 },
300}
301
302pub(super) struct Header {
304 v2: V2Header,
306
307 v3: V3HeaderBase,
309
310 unknown_header_fields: Vec<u8>,
312
313 backing_filename: Option<String>,
315
316 extensions: Vec<HeaderExtension>,
318
319 external_data_file: bool,
321}
322
323#[maybe_async]
324impl Header {
325 pub async fn load<S: Storage>(image: &S, writable: bool) -> io::Result<Self> {
329 let mut header_buf = vec![0u8; V2Header::ON_DISK_SIZE];
331 image.read(header_buf.as_mut_slice(), 0).await?;
332
333 let header: V2Header = decode_binary(&header_buf)?;
334 if header.magic != MAGIC {
335 return Err(invalid_data("Not a qcow2 file"));
336 }
337
338 let v3header_base = if header.version == 2 {
339 V3HeaderBase::default()
340 } else if header.version == 3 {
341 let mut header_buf = vec![0u8; V3HeaderBase::ON_DISK_SIZE];
342 image
343 .read(header_buf.as_mut_slice(), V2Header::ON_DISK_SIZE as u64)
344 .await?;
345 decode_binary(&header_buf)?
346 } else {
347 return Err(invalid_data(format!(
348 "qcow2 v{} is not supported",
349 header.version
350 )));
351 };
352
353 let cluster_size = 1usize.checked_shl(header.cluster_bits).ok_or_else(|| {
354 invalid_data(format!("Invalid cluster size: 2^{}", header.cluster_bits))
355 })?;
356 if !(MIN_CLUSTER_SIZE..=MAX_CLUSTER_SIZE).contains(&cluster_size) {
357 return Err(invalid_data(format!(
358 "Invalid cluster size: {cluster_size}; must be between {MIN_CLUSTER_SIZE} and {MAX_CLUSTER_SIZE}",
359 )));
360 }
361
362 let min_header_size = V2Header::ON_DISK_SIZE + V3HeaderBase::ON_DISK_SIZE;
363 if (v3header_base.header_length as usize) < min_header_size {
364 return Err(invalid_data(format!(
365 "qcow2 header too short: {} < {min_header_size}",
366 v3header_base.header_length,
367 )));
368 } else if (v3header_base.header_length as usize) > cluster_size {
369 return Err(invalid_data(format!(
370 "qcow2 header too big: {} > {cluster_size}",
371 v3header_base.header_length,
372 )));
373 }
374
375 let unknown_header_fields = if header.version == 2 {
376 Vec::new()
377 } else {
378 let mut unknown_header_fields =
379 vec![0u8; v3header_base.header_length as usize - min_header_size];
380 image
381 .read(&mut unknown_header_fields, min_header_size as u64)
382 .await?;
383 unknown_header_fields
384 };
385
386 let l1_offset = HostOffset(header.l1_table_offset.load(Ordering::Relaxed));
387 l1_offset
388 .checked_cluster(header.cluster_bits)
389 .ok_or_else(|| invalid_data(format!("Unaligned L1 table: {l1_offset}")))?;
390
391 let rt_offset = HostOffset(header.refcount_table_offset.load(Ordering::Relaxed));
392 rt_offset
393 .checked_cluster(header.cluster_bits)
394 .ok_or_else(|| invalid_data(format!("Unaligned refcount table: {rt_offset}")))?;
395
396 let rc_width = 1usize
397 .checked_shl(v3header_base.refcount_order)
398 .ok_or_else(|| {
399 invalid_data(format!(
400 "Invalid refcount width: 2^{}",
401 v3header_base.refcount_order
402 ))
403 })?;
404 if !(MIN_REFCOUNT_WIDTH..=MAX_REFCOUNT_WIDTH).contains(&rc_width) {
405 return Err(invalid_data(format!(
406 "Invalid refcount width: {rc_width}; must be between {MIN_REFCOUNT_WIDTH} and {MAX_REFCOUNT_WIDTH}",
407 )));
408 }
409
410 let backing_filename = if header.backing_file_offset != 0 {
411 let (offset, length) = (header.backing_file_offset, header.backing_file_size);
412 if length > 1023 {
413 return Err(invalid_data(format!(
414 "Backing file name is too long ({length}, must not exceed 1023)"
415 )));
416 }
417
418 let end = offset.checked_add(length as u64).ok_or(invalid_data(
419 "Backing file name offset is invalid (too high)",
420 ))?;
421 if end >= cluster_size as u64 {
422 return Err(invalid_data(
423 "Backing file name offset is invalid (beyond first cluster)",
424 ));
425 }
426
427 let mut backing_buf = vec![0; length as usize];
428 image.read(&mut backing_buf, offset).await?;
429
430 Some(
431 String::from_utf8(backing_buf)
432 .map_err(|err| invalid_data(format!("Backing file name is invalid: {err}")))?,
433 )
434 } else {
435 None
436 };
437
438 let extensions = if header.version == 2 {
439 Vec::new()
440 } else {
441 let mut ext_offset: u64 = v3header_base.header_length as u64;
442 let mut extensions = Vec::<HeaderExtension>::new();
443 loop {
444 if ext_offset + HeaderExtensionHeader::ON_DISK_SIZE as u64 > cluster_size as u64 {
445 return Err(invalid_data("Header extensions exceed the first cluster"));
446 }
447
448 let mut ext_hdr_buf = vec![0; HeaderExtensionHeader::ON_DISK_SIZE];
449 image.read(&mut ext_hdr_buf, ext_offset).await?;
450
451 ext_offset += HeaderExtensionHeader::ON_DISK_SIZE as u64;
452
453 let ext_hdr: HeaderExtensionHeader = decode_binary(&ext_hdr_buf)?;
454 let ext_end = ext_offset
455 .checked_add(ext_hdr.length as u64)
456 .ok_or_else(|| invalid_data("Header size overflow"))?;
457 if ext_end > cluster_size as u64 {
458 return Err(invalid_data("Header extensions exceed the first cluster"));
459 }
460
461 let mut ext_data = vec![0; ext_hdr.length as usize];
462 image.read(&mut ext_data, ext_offset).await?;
463
464 ext_offset += (ext_hdr.length as u64).next_multiple_of(8);
465
466 let Some(extension) =
467 HeaderExtension::deserialize(ext_hdr.extension_type, ext_data)?
468 else {
469 break;
470 };
471
472 extensions.push(extension);
473 }
474 extensions
475 };
476
477 let backing_fmt = extensions
479 .iter()
480 .find(|ext| matches!(ext, HeaderExtension::BackingFileFormat(_)));
481 if let Some(backing_fmt) = backing_fmt {
482 let conflicting = extensions.iter().find(|ext| {
483 matches!(ext, HeaderExtension::BackingFileFormat(_)) && ext != &backing_fmt
484 });
485 if let Some(conflicting) = conflicting {
486 return Err(io::Error::other(format!(
487 "Found conflicting backing file formats: {backing_fmt:?} != {conflicting:?}",
488 )));
489 }
490 }
491 let ext_data_file = extensions
492 .iter()
493 .find(|ext| matches!(ext, HeaderExtension::ExternalDataFileName(_)));
494 if let Some(ext_data_file) = ext_data_file {
495 let conflicting = extensions.iter().find(|ext| {
496 matches!(ext, HeaderExtension::ExternalDataFileName(_)) && ext != &ext_data_file
497 });
498 if let Some(conflicting) = conflicting {
499 return Err(io::Error::other(format!(
500 "Found conflicting external data file names: {ext_data_file:?} != {conflicting:?}",
501 )));
502 }
503 }
504
505 let mut incompatible_features = v3header_base.incompatible_features;
506 let autoclear_features = v3header_base.autoclear_features;
507
508 let external_data_file =
509 incompatible_features & IncompatibleFeatures::ExternalDataFile as u64 != 0;
510 incompatible_features &= !(IncompatibleFeatures::ExternalDataFile as u64);
511
512 let mut header = Header {
513 v2: header,
514 v3: v3header_base,
515 unknown_header_fields,
516 backing_filename,
517 extensions,
518 external_data_file,
519 };
520
521 if autoclear_features != 0 && writable {
523 header.v3.autoclear_features = 0;
524 header.write(image).await?;
525 }
526
527 if incompatible_features != 0 {
528 let feats = (0..64)
529 .filter(|bit| header.v3.incompatible_features & (1u64 << bit) != 0)
530 .map(|bit| {
531 if let Some(name) = header.feature_name(FeatureType::Incompatible, bit) {
532 format!("{bit} ({name})")
533 } else {
534 format!("{bit}")
535 }
536 })
537 .collect::<Vec<String>>();
538
539 return Err(invalid_data(format!(
540 "Unrecognized incompatible feature(s) {}",
541 feats.join(", ")
542 )));
543 }
544
545 Ok(header)
546 }
547
548 pub async fn write<S: Storage>(&mut self, image: &S) -> io::Result<()> {
550 let header_len = if self.v2.version > 2 {
551 let len =
552 self.v2.on_disk_size() + self.v3.on_disk_size() + self.unknown_header_fields.len();
553 let len = len.next_multiple_of(8);
554 self.v3.header_length = len as u32;
555 len
556 } else {
557 V2Header::ON_DISK_SIZE
558 };
559
560 let mut header_exts;
563 let mut backing_file_ofs;
564 loop {
565 header_exts = self.serialize_extensions()?;
566
567 backing_file_ofs = header_len
568 .checked_add(header_exts.len())
569 .ok_or_else(|| invalid_data("Header size overflow"))?;
570 let backing_file_len = self
571 .backing_filename
572 .as_ref()
573 .map(|n| n.len()) .unwrap_or(0);
575 let header_end = backing_file_ofs
576 .checked_add(backing_file_len)
577 .ok_or_else(|| invalid_data("Header size overflow"))?;
578
579 if header_end <= self.cluster_size() {
580 break;
581 }
582
583 if !self
584 .extensions
585 .iter()
586 .any(|e| e.extension_type() == HeaderExtensionType::FeatureNameTable as u32)
587 {
588 return Err(io::Error::other(format!(
589 "Header would be too long ({header_end} > {})",
590 self.cluster_size()
591 )));
592 }
593 self.extensions
594 .retain(|e| e.extension_type() != HeaderExtensionType::FeatureNameTable as u32);
595 }
596
597 if let Some(backing) = self.backing_filename.as_ref() {
598 self.v2.backing_file_offset = backing_file_ofs as u64;
599 self.v2.backing_file_size = backing.len() as u32; } else {
601 self.v2.backing_file_offset = 0;
602 self.v2.backing_file_size = 0;
603 };
604
605 let mut full_buf = encode_binary(&self.v2)?;
606 if self.v2.version > 2 {
607 full_buf.append(&mut encode_binary(&self.v3)?);
608 full_buf.extend_from_slice(&self.unknown_header_fields);
609 full_buf.resize(full_buf.len().next_multiple_of(8), 0);
610 }
611
612 full_buf.append(&mut header_exts);
613
614 if let Some(backing) = self.backing_filename.as_ref() {
615 full_buf.extend_from_slice(backing.as_bytes());
616 }
617
618 if full_buf.len() > self.cluster_size() {
619 return Err(io::Error::other(format!(
620 "Header is too big to write ({}, larger than a cluster ({}))",
621 full_buf.len(),
622 self.cluster_size(),
623 )));
624 }
625
626 image.write(&full_buf, 0).await
627 }
628
629 pub fn new(
631 cluster_bits: u32,
632 refcount_order: u32,
633 backing_filename: Option<String>,
634 backing_format: Option<String>,
635 external_data_file: Option<String>,
636 ) -> Self {
637 assert!((MIN_CLUSTER_SIZE..=MAX_CLUSTER_SIZE)
638 .contains(&1usize.checked_shl(cluster_bits).unwrap()));
639 assert!((MIN_REFCOUNT_WIDTH..=MAX_REFCOUNT_WIDTH)
640 .contains(&1usize.checked_shl(refcount_order).unwrap()));
641
642 let has_external_data_file = external_data_file.is_some();
643 let incompatible_features = if has_external_data_file {
644 IncompatibleFeatures::ExternalDataFile as u64
645 } else {
646 0
647 };
648
649 let mut extensions = vec![HeaderExtension::feature_name_table()];
650 if let Some(backing_format) = backing_format {
651 extensions.push(HeaderExtension::BackingFileFormat(backing_format));
652 }
653 if let Some(external_data_file) = external_data_file {
654 extensions.push(HeaderExtension::ExternalDataFileName(external_data_file));
655 }
656
657 Header {
658 v2: V2Header {
659 magic: MAGIC,
660 version: 3,
661 backing_file_offset: 0, backing_file_size: 0, cluster_bits,
664 size: 0.into(),
665 crypt_method: 0,
666 l1_size: 0.into(),
667 l1_table_offset: 0.into(),
668 refcount_table_offset: 0.into(),
669 refcount_table_clusters: 0.into(),
670 nb_snapshots: 0,
671 snapshots_offset: 0,
672 },
673 v3: V3HeaderBase {
674 incompatible_features,
675 compatible_features: 0,
676 autoclear_features: 0,
677 refcount_order,
678 header_length: 0, },
680 unknown_header_fields: Vec::new(),
681 backing_filename,
682 extensions,
683 external_data_file: has_external_data_file,
684 }
685 }
686
687 pub fn update(&self, new_header: &Header) -> io::Result<()> {
692 macro_rules! check_field {
694 ($($field:ident).*) => {
695 (self.$($field).* == new_header.$($field).*).then_some(()).ok_or_else(|| {
696 io::Error::other(format!(
697 "Incompatible header modification on {}: {} != {}",
698 stringify!($($field).*),
699 self.$($field).*,
700 new_header.$($field).*
701 ))
702 })
703 };
704 }
705
706 check_field!(v2.magic)?;
707 check_field!(v2.version)?;
708 check_field!(v2.backing_file_offset)?; check_field!(v2.backing_file_size)?; check_field!(v2.cluster_bits)?;
711 check_field!(v2.crypt_method)?;
715 check_field!(v2.nb_snapshots)?; check_field!(v2.snapshots_offset)?; check_field!(v3.incompatible_features)?; check_field!(v3.compatible_features)?; check_field!(v3.autoclear_features)?; check_field!(v3.refcount_order)?;
721 (self.unknown_header_fields == new_header.unknown_header_fields)
725 .then_some(())
726 .ok_or_else(|| io::Error::other("Unknown header fields modified"))?;
727 (self.backing_filename == new_header.backing_filename)
729 .then_some(())
730 .ok_or_else(|| io::Error::other("Backing filename modified"))?;
731 (self.extensions == new_header.extensions)
733 .then_some(())
734 .ok_or_else(|| io::Error::other("Header extensions modified"))?;
735
736 check_field!(external_data_file)?;
737
738 self.v2.size.store(
739 new_header.v2.size.load(Ordering::Relaxed),
740 Ordering::Relaxed,
741 );
742
743 self.v2.l1_table_offset.store(
744 new_header.v2.l1_table_offset.load(Ordering::Relaxed),
745 Ordering::Relaxed,
746 );
747 self.v2.l1_size.store(
748 new_header.v2.l1_size.load(Ordering::Relaxed),
749 Ordering::Relaxed,
750 );
751 self.v2.refcount_table_offset.store(
752 new_header.v2.refcount_table_offset.load(Ordering::Relaxed),
753 Ordering::Relaxed,
754 );
755 self.v2.refcount_table_clusters.store(
756 new_header
757 .v2
758 .refcount_table_clusters
759 .load(Ordering::Relaxed),
760 Ordering::Relaxed,
761 );
762
763 Ok(())
764 }
765
766 pub fn size(&self) -> u64 {
768 self.v2.size.load(Ordering::Relaxed)
769 }
770
771 pub fn require_version(&self, minimum: u32) -> io::Result<()> {
775 let version = self.v2.version;
776 if version >= minimum {
777 Ok(())
778 } else {
779 Err(io::Error::new(
780 io::ErrorKind::Unsupported,
781 format!("qcow2 version {minimum} required, image has version {version}"),
782 ))
783 }
784 }
785
786 pub fn set_size(&self, new_size: u64) {
788 self.v2.size.store(new_size, Ordering::Relaxed)
789 }
790
791 pub fn cluster_bits(&self) -> u32 {
793 self.v2.cluster_bits
794 }
795
796 pub fn cluster_size(&self) -> usize {
798 1 << self.cluster_bits()
799 }
800
801 pub fn l2_entries(&self) -> usize {
803 1 << (self.cluster_bits() - 3)
805 }
806
807 pub fn rb_bits(&self) -> u32 {
809 self.cluster_bits() + 3 - self.refcount_order()
813 }
814
815 pub fn rb_entries(&self) -> usize {
817 1 << self.rb_bits()
818 }
819
820 pub fn refcount_order(&self) -> u32 {
822 self.v3.refcount_order
823 }
824
825 pub fn l1_table_offset(&self) -> HostOffset {
827 HostOffset(self.v2.l1_table_offset.load(Ordering::Relaxed))
828 }
829
830 pub fn l1_table_entries(&self) -> usize {
832 self.v2.l1_size.load(Ordering::Relaxed) as usize
833 }
834
835 pub fn set_l1_table(&self, l1_table: &L1Table) -> io::Result<()> {
837 let offset = l1_table.get_offset().ok_or_else(|| {
838 io::Error::new(
839 io::ErrorKind::InvalidInput,
840 "New L1 table has no assigned offset",
841 )
842 })?;
843
844 let entries = l1_table.entries();
845 let entries = entries
846 .try_into()
847 .map_err(|err| invalid_data(format!("Too many L1 entries ({entries}): {err}")))?;
848
849 self.v2.l1_table_offset.store(offset.0, Ordering::Relaxed);
850
851 self.v2.l1_size.store(entries, Ordering::Relaxed);
852
853 Ok(())
854 }
855
856 pub fn reftable_offset(&self) -> HostOffset {
858 HostOffset(self.v2.refcount_table_offset.load(Ordering::Relaxed))
859 }
860
861 pub fn reftable_clusters(&self) -> ClusterCount {
863 ClusterCount(self.v2.refcount_table_clusters.load(Ordering::Relaxed) as u64)
864 }
865
866 pub fn reftable_entries(&self) -> usize {
868 (self.reftable_clusters().byte_size(self.cluster_bits()) >> 3) as usize
870 }
871
872 pub fn set_reftable(&self, reftable: &RefTable) -> io::Result<()> {
874 let offset = reftable.get_offset().ok_or_else(|| {
875 io::Error::new(
876 io::ErrorKind::InvalidInput,
877 "New refcount table has no assigned offset",
878 )
879 })?;
880
881 let clusters = reftable.cluster_count();
882 let clusters = clusters.0.try_into().map_err(|err| {
883 invalid_data(format!("Too many reftable clusters ({clusters}): {err}"))
884 })?;
885
886 self.v2
887 .refcount_table_clusters
888 .store(clusters, Ordering::Relaxed);
889
890 self.v2
891 .refcount_table_offset
892 .store(offset.0, Ordering::Relaxed);
893
894 Ok(())
895 }
896
897 pub fn backing_filename(&self) -> Option<&String> {
899 self.backing_filename.as_ref()
900 }
901
902 pub fn backing_format(&self) -> Option<&String> {
904 self.extensions.iter().find_map(|e| match e {
905 HeaderExtension::BackingFileFormat(fmt) => Some(fmt),
906 _ => None,
907 })
908 }
909
910 pub fn external_data_file(&self) -> bool {
912 self.external_data_file
913 }
914
915 pub fn external_data_filename(&self) -> Option<&String> {
917 self.extensions.iter().find_map(|e| match e {
918 HeaderExtension::ExternalDataFileName(filename) => Some(filename),
919 _ => None,
920 })
921 }
922
923 pub fn feature_name(&self, feat_type: FeatureType, bit: u32) -> Option<&String> {
927 for e in &self.extensions {
928 if let HeaderExtension::FeatureNameTable(names) = e {
929 if let Some(name) = names.get(&(feat_type, bit as u8)) {
930 return Some(name);
931 }
932 }
933 }
934
935 None
936 }
937
938 fn serialize_extensions(&self) -> io::Result<Vec<u8>> {
940 let mut result = Vec::new();
941 for e in &self.extensions {
942 let mut data = e.serialize_data()?;
943 let ext_hdr = HeaderExtensionHeader {
944 extension_type: e.extension_type(),
945 length: data.len().try_into().map_err(|err| {
946 invalid_data(format!("Header extension too long ({}): {err}", data.len()))
947 })?,
948 };
949 result.append(&mut encode_binary(&ext_hdr)?);
950 result.append(&mut data);
951 result.resize(result.len().next_multiple_of(8), 0);
952 }
953
954 let end_ext = HeaderExtensionHeader {
955 extension_type: HeaderExtensionType::End as u32,
956 length: 0,
957 };
958 result.append(&mut encode_binary(&end_ext)?);
959 result.resize(result.len().next_multiple_of(8), 0);
960
961 Ok(result)
962 }
963
964 async fn write_v2_header<S: Storage>(&self, image: &S) -> io::Result<()> {
966 let v2_header = encode_binary(&self.v2)?;
967 image.write(&v2_header, 0).await
968 }
969
970 pub async fn write_reftable_pointer<S: Storage>(&self, image: &S) -> io::Result<()> {
972 self.write_v2_header(image).await
974 }
975
976 pub async fn write_l1_table_pointer<S: Storage>(&self, image: &S) -> io::Result<()> {
978 self.write_v2_header(image).await
980 }
981
982 pub async fn write_size<S: Storage>(&self, image: &S) -> io::Result<()> {
984 self.write_v2_header(image).await
986 }
987}
988
989impl HeaderExtension {
990 fn deserialize(ext_type: u32, data: Vec<u8>) -> io::Result<Option<Self>> {
993 let ext = if let Ok(ext_type) = HeaderExtensionType::try_from(ext_type) {
994 match ext_type {
995 HeaderExtensionType::End => return Ok(None),
996 HeaderExtensionType::BackingFileFormat => {
997 let fmt = String::from_utf8(data).map_err(|err| {
998 invalid_data(format!("Invalid backing file format: {err}"))
999 })?;
1000 HeaderExtension::BackingFileFormat(fmt)
1001 }
1002 HeaderExtensionType::FeatureNameTable => {
1003 let mut feats = HashMap::new();
1004 for feat in data.chunks(48) {
1005 let feat_type: FeatureType = match feat[0].try_into() {
1006 Ok(ft) => ft,
1007 Err(_) => continue, };
1009 let feat_name_bytes = feat[2..].split(|c| *c == 0).next().unwrap();
1012 let feat_name = String::from_utf8_lossy(feat_name_bytes);
1014 feats.insert((feat_type, feat[1]), feat_name.to_string());
1015 }
1016 HeaderExtension::FeatureNameTable(feats)
1017 }
1018 HeaderExtensionType::ExternalDataFileName => {
1019 let filename = String::from_utf8(data).map_err(|err| {
1020 invalid_data(format!("Invalid external data file name: {err}"))
1021 })?;
1022 HeaderExtension::ExternalDataFileName(filename)
1023 }
1024 }
1025 } else {
1026 HeaderExtension::Unknown {
1027 extension_type: ext_type,
1028 data,
1029 }
1030 };
1031
1032 Ok(Some(ext))
1033 }
1034
1035 fn extension_type(&self) -> u32 {
1037 match self {
1038 HeaderExtension::BackingFileFormat(_) => HeaderExtensionType::BackingFileFormat as u32,
1039 HeaderExtension::FeatureNameTable(_) => HeaderExtensionType::FeatureNameTable as u32,
1040 HeaderExtension::ExternalDataFileName(_) => {
1041 HeaderExtensionType::ExternalDataFileName as u32
1042 }
1043 HeaderExtension::Unknown {
1044 extension_type,
1045 data: _,
1046 } => *extension_type,
1047 }
1048 }
1049
1050 fn serialize_data(&self) -> io::Result<Vec<u8>> {
1052 match self {
1053 HeaderExtension::BackingFileFormat(fmt) => Ok(fmt.as_bytes().into()),
1054 HeaderExtension::FeatureNameTable(map) => {
1055 let mut result = Vec::new();
1056 for (bit, name) in map {
1057 result.push(bit.0 as u8);
1058 result.push(bit.1);
1059
1060 let mut padded_name = vec![0; 46];
1061 let name_bytes = name.as_bytes();
1062 let truncated_len = cmp::min(name_bytes.len(), 46);
1065 padded_name[..truncated_len].copy_from_slice(&name_bytes[..truncated_len]);
1066 result.extend_from_slice(&padded_name);
1067 }
1068 Ok(result)
1069 }
1070 HeaderExtension::ExternalDataFileName(filename) => Ok(filename.as_bytes().into()),
1071 HeaderExtension::Unknown {
1072 extension_type: _,
1073 data,
1074 } => Ok(data.clone()),
1075 }
1076 }
1077
1078 fn feature_name_table() -> Self {
1080 use AutoclearFeatures as A;
1081 use CompatibleFeatures as C;
1082 use IncompatibleFeatures as I;
1083
1084 let mut map = HashMap::new();
1085
1086 map.insert(I::Dirty.into(), "dirty".into());
1087 map.insert(I::Corrupt.into(), "corrupt".into());
1088 map.insert(I::ExternalDataFile.into(), "external data file".into());
1089 map.insert(
1090 I::CompressionType.into(),
1091 "extended compression type".into(),
1092 );
1093 map.insert(I::ExtendedL2Entries.into(), "extended L2 entries".into());
1094
1095 map.insert(C::LazyRefcounts.into(), "lazy refcounts".into());
1096
1097 map.insert(A::Bitmaps.into(), "persistent dirty bitmaps".into());
1098 map.insert(A::RawExternalData.into(), "raw external data file".into());
1099
1100 HeaderExtension::FeatureNameTable(map)
1101 }
1102}
1103
1104#[derive(Copy, Clone, Default, Debug)]
1114pub(super) struct L1Entry(u64);
1115
1116impl L1Entry {
1117 pub fn l2_offset(&self) -> Option<HostOffset> {
1119 let ofs = self.0 & 0x00ff_ffff_ffff_fe00u64;
1120 if ofs == 0 {
1121 None
1122 } else {
1123 Some(HostOffset(ofs))
1124 }
1125 }
1126
1127 pub fn is_copied(&self) -> bool {
1131 self.0 & (1u64 << 63) != 0
1132 }
1133
1134 pub fn reserved_bits(&self) -> u64 {
1136 self.0 & 0x7f00_0000_0000_01feu64
1137 }
1138}
1139
1140impl TableEntry for L1Entry {
1141 fn try_from_plain(value: u64, header: &Header) -> io::Result<Self> {
1142 let entry = L1Entry(value);
1143
1144 if entry.reserved_bits() != 0 {
1145 return Err(invalid_data(format!(
1146 "Invalid L1 entry 0x{value:x}, reserved bits set (0x{:x})",
1147 entry.reserved_bits(),
1148 )));
1149 }
1150
1151 if let Some(l2_ofs) = entry.l2_offset() {
1152 if l2_ofs.in_cluster_offset(header.cluster_bits()) != 0 {
1153 return Err(invalid_data(format!(
1154 "Invalid L1 entry 0x{value:x}, offset ({l2_ofs}) is not aligned to cluster size (0x{:x})",
1155 header.cluster_size(),
1156 )));
1157 }
1158 }
1159
1160 Ok(entry)
1161 }
1162
1163 fn to_plain(&self) -> u64 {
1164 self.0
1165 }
1166}
1167
1168#[derive(Debug)]
1170pub(super) struct L1Table {
1171 cluster: Option<HostCluster>,
1173
1174 data: Box<[L1Entry]>,
1176
1177 cluster_bits: u32,
1179
1180 modified: AtomicBool,
1182}
1183
1184impl L1Table {
1185 pub fn clone_and_grow(&self, at_least_index: usize, header: &Header) -> io::Result<Self> {
1187 let new_entry_count = cmp::max(at_least_index + 1, self.data.len());
1188 let new_entry_count =
1189 new_entry_count.next_multiple_of(header.cluster_size() / size_of::<L1Entry>());
1190
1191 if new_entry_count > <Self as Table>::MAX_ENTRIES {
1192 return Err(io::Error::other(
1193 "Cannot grow the image to this size; L1 table would become too big",
1194 ));
1195 }
1196
1197 let mut new_data = vec![L1Entry::default(); new_entry_count];
1198 new_data[..self.data.len()].copy_from_slice(&self.data);
1199
1200 Ok(Self {
1201 cluster: None,
1202 data: new_data.into_boxed_slice(),
1203 cluster_bits: header.cluster_bits(),
1204 modified: true.into(),
1205 })
1206 }
1207
1208 pub fn in_bounds(&self, index: usize) -> bool {
1210 index < self.data.len()
1211 }
1212
1213 pub fn enter_l2_table(&mut self, index: usize, l2: &L2Table) -> io::Result<()> {
1215 let l2_offset = l2.get_offset().ok_or_else(|| {
1216 io::Error::new(
1217 io::ErrorKind::InvalidInput,
1218 "L2 table has no assigned offset",
1219 )
1220 })?;
1221
1222 let l1entry = L1Entry((1 << 63) | l2_offset.0);
1223 debug_assert!(l1entry.reserved_bits() == 0);
1224 self.data[index] = l1entry;
1225 self.modified.store(true, Ordering::Relaxed);
1226
1227 Ok(())
1228 }
1229}
1230
1231impl Table for L1Table {
1232 type InternalEntry = L1Entry;
1233 type Entry = L1Entry;
1234 const NAME: &'static str = "L1 table";
1235
1236 const MAX_ENTRIES: usize = 4 * 1024 * 1024;
1240
1241 fn from_data(data: Box<[L1Entry]>, header: &Header) -> Self {
1242 Self {
1243 cluster: None,
1244 data,
1245 cluster_bits: header.cluster_bits(),
1246 modified: true.into(),
1247 }
1248 }
1249
1250 fn entries(&self) -> usize {
1251 self.data.len()
1252 }
1253
1254 fn get_ref(&self, index: usize) -> Option<&L1Entry> {
1255 self.data.get(index)
1256 }
1257
1258 fn get(&self, index: usize) -> L1Entry {
1259 self.data.get(index).copied().unwrap_or(L1Entry(0))
1260 }
1261
1262 fn get_cluster(&self) -> Option<HostCluster> {
1263 self.cluster
1264 }
1265
1266 fn get_offset(&self) -> Option<HostOffset> {
1267 self.cluster.map(|index| index.offset(self.cluster_bits))
1268 }
1269
1270 fn set_cluster(&mut self, cluster: HostCluster) {
1271 self.cluster = Some(cluster);
1272 self.modified.store(true, Ordering::Relaxed);
1273 }
1274
1275 fn unset_cluster(&mut self) {
1276 self.cluster = None;
1277 }
1278
1279 fn is_modified(&self) -> bool {
1280 self.modified.load(Ordering::Relaxed)
1281 }
1282
1283 fn clear_modified(&self) {
1284 self.modified.store(false, Ordering::Relaxed);
1285 }
1286
1287 fn set_modified(&self) {
1288 self.modified.store(true, Ordering::Relaxed);
1289 }
1290
1291 fn cluster_bits(&self) -> u32 {
1292 self.cluster_bits
1293 }
1294}
1295
1296#[derive(Copy, Clone, Default, Debug)]
1328pub(super) struct L2Entry(u64);
1329
1330#[derive(Default, Debug)]
1334pub(super) struct AtomicL2Entry(AtomicU64);
1335
1336#[derive(Debug, Clone)]
1338pub(super) enum L2Mapping {
1339 DataFile {
1341 host_cluster: HostCluster,
1343
1344 copied: bool,
1346 },
1347
1348 Backing {
1350 backing_offset: u64,
1352 },
1353
1354 Zero {
1356 host_cluster: Option<HostCluster>,
1358
1359 copied: bool,
1361 },
1362
1363 Compressed {
1365 host_offset: HostOffset,
1367
1368 length: u64,
1370 },
1371}
1372
1373impl L2Entry {
1374 pub fn cluster_offset(&self, external_data_file: bool) -> Option<HostOffset> {
1382 let ofs = self.0 & 0x00ff_ffff_ffff_fe00u64;
1383 if ofs != 0 || (external_data_file && self.is_copied()) {
1384 Some(HostOffset(ofs))
1385 } else {
1386 None
1387 }
1388 }
1389
1390 pub fn is_compressed(&self) -> bool {
1392 self.0 & (1u64 << 62) != 0
1393 }
1394
1395 pub fn is_copied(&self) -> bool {
1399 self.0 & (1u64 << 63) != 0
1400 }
1401
1402 #[must_use]
1404 pub fn without_copied(self) -> Self {
1405 L2Entry(self.0 & !(1u64 << 63))
1406 }
1407
1408 pub fn is_zero(&self) -> bool {
1412 self.0 & (1u64 << 0) != 0
1413 }
1414
1415 pub fn reserved_bits(&self) -> u64 {
1417 if self.is_compressed() {
1418 self.0 & 0x8000_0000_0000_0000u64
1419 } else {
1420 self.0 & 0x3f00_0000_0000_01feu64
1421 }
1422 }
1423
1424 pub fn compressed_descriptor(&self) -> u64 {
1426 self.0 & 0x3fff_ffff_ffff_ffffu64
1427 }
1428
1429 pub fn compressed_range(&self, cluster_bits: u32) -> Option<(HostOffset, u64)> {
1432 if self.is_compressed() {
1433 let desc = self.compressed_descriptor();
1434 let compressed_offset_bits = 62 - (cluster_bits - 8);
1435 let offset = desc & ((1 << compressed_offset_bits) - 1) & 0x00ff_ffff_ffff_ffffu64;
1436 let sectors = desc >> compressed_offset_bits;
1437 let length = (sectors + 1) * 512 - (offset & 511);
1440
1441 Some((HostOffset(offset), length))
1442 } else {
1443 None
1444 }
1445 }
1446
1447 fn allocation(
1452 &self,
1453 cluster_bits: u32,
1454 external_data_file: bool,
1455 ) -> Option<(HostCluster, ClusterCount)> {
1456 if let Some((offset, length)) = self.compressed_range(cluster_bits) {
1457 let first_cluster = offset.cluster(cluster_bits);
1459 let cluster_count = ClusterCount::from_byte_size(
1460 offset + length - first_cluster.offset(cluster_bits),
1461 cluster_bits,
1462 );
1463 Some((first_cluster, cluster_count))
1464 } else {
1465 self.cluster_offset(external_data_file)
1466 .map(|ofs| (ofs.cluster(cluster_bits), ClusterCount(1)))
1467 }
1468 }
1469
1470 fn into_mapping(
1475 self,
1476 guest_cluster: GuestCluster,
1477 cluster_bits: u32,
1478 external_data_file: bool,
1479 ) -> io::Result<L2Mapping> {
1480 let mapping = if let Some((offset, length)) = self.compressed_range(cluster_bits) {
1481 L2Mapping::Compressed {
1482 host_offset: offset,
1483 length,
1484 }
1485 } else if self.is_zero() {
1486 let host_cluster = self
1487 .cluster_offset(external_data_file)
1488 .map(|ofs| {
1489 ofs.checked_cluster(cluster_bits).ok_or_else(|| {
1490 let offset = guest_cluster.offset(cluster_bits);
1491 io::Error::other(format!(
1492 "Unaligned pre-allocated zero cluster at {offset}; L2 entry: {self:?}"
1493 ))
1494 })
1495 })
1496 .transpose()?;
1497
1498 L2Mapping::Zero {
1499 host_cluster,
1500 copied: host_cluster.is_some() && self.is_copied(),
1501 }
1502 } else if let Some(host_offset) = self.cluster_offset(external_data_file) {
1503 let host_cluster = host_offset.checked_cluster(cluster_bits).ok_or_else(|| {
1504 let offset = guest_cluster.offset(cluster_bits);
1505 io::Error::other(format!(
1506 "Unaligned data cluster at {offset}; L2 entry: {self:?}"
1507 ))
1508 })?;
1509
1510 L2Mapping::DataFile {
1511 host_cluster,
1512 copied: self.is_copied(),
1513 }
1514 } else {
1515 L2Mapping::Backing {
1516 backing_offset: guest_cluster.offset(cluster_bits).0,
1517 }
1518 };
1519
1520 Ok(mapping)
1521 }
1522
1523 fn from_mapping(value: L2Mapping, cluster_bits: u32) -> Self {
1525 let num_val: u64 = match value {
1526 L2Mapping::DataFile {
1527 host_cluster,
1528 copied,
1529 } => {
1530 debug_assert!(host_cluster.offset(cluster_bits) <= MAX_OFFSET);
1531 if copied {
1532 (1 << 63) | host_cluster.offset(cluster_bits).0
1533 } else {
1534 host_cluster.offset(cluster_bits).0
1535 }
1536 }
1537
1538 L2Mapping::Backing { backing_offset: _ } => 0,
1539
1540 L2Mapping::Zero {
1541 host_cluster,
1542 copied,
1543 } => {
1544 let host_offset = host_cluster.map(|hc| hc.offset(cluster_bits));
1545 debug_assert!(host_offset.unwrap_or(HostOffset(0)) <= MAX_OFFSET);
1546 if copied {
1547 (1 << 63) | host_offset.unwrap().0 | 0x1
1548 } else {
1549 host_offset.unwrap_or(HostOffset(0)).0 | 0x1
1550 }
1551 }
1552
1553 L2Mapping::Compressed {
1554 host_offset,
1555 length,
1556 } => {
1557 let compressed_offset_bits = 62 - (cluster_bits - 8);
1558 assert!(length < 1 << cluster_bits);
1559 assert!(host_offset.0 < 1 << compressed_offset_bits);
1560
1561 let sectors = (length - 1 + (host_offset.0 & 511)) / 512;
1566
1567 (1 << 62) | (sectors << compressed_offset_bits) | host_offset.0
1568 }
1569 };
1570
1571 let entry = L2Entry(num_val);
1572 debug_assert!(entry.reserved_bits() == 0);
1573 entry
1574 }
1575}
1576
1577impl AtomicL2Entry {
1578 fn get(&self) -> L2Entry {
1580 L2Entry(self.0.load(Ordering::Relaxed))
1581 }
1582
1583 unsafe fn swap(&self, l2e: L2Entry) -> L2Entry {
1591 L2Entry(self.0.swap(l2e.0, Ordering::Relaxed))
1592 }
1593}
1594
1595impl TableEntry for AtomicL2Entry {
1596 fn try_from_plain(value: u64, header: &Header) -> io::Result<Self> {
1597 let entry = L2Entry(value);
1598
1599 if entry.reserved_bits() != 0 {
1600 return Err(invalid_data(format!(
1601 "Invalid L2 entry 0x{value:x}, reserved bits set (0x{:x})",
1602 entry.reserved_bits(),
1603 )));
1604 }
1605
1606 if let Some(offset) = entry.cluster_offset(header.external_data_file()) {
1607 if !entry.is_compressed() && offset.in_cluster_offset(header.cluster_bits()) != 0 {
1608 return Err(invalid_data(format!(
1609 "Invalid L2 entry 0x{value:x}, offset ({offset}) is not aligned to cluster size (0x{:x})",
1610 header.cluster_size(),
1611 )));
1612 }
1613 }
1614
1615 Ok(AtomicL2Entry(AtomicU64::new(entry.0)))
1616 }
1617
1618 fn to_plain(&self) -> u64 {
1619 self.get().0
1620 }
1621}
1622
1623impl L2Mapping {
1624 pub fn is_consecutive(&self, preceding: &L2Mapping, cluster_bits: u32) -> bool {
1629 match preceding {
1630 L2Mapping::DataFile {
1631 host_cluster: prior_cluster,
1632 copied,
1633 } => {
1634 if let L2Mapping::DataFile {
1635 host_cluster: next_cluster,
1636 copied: next_copied,
1637 } = self
1638 {
1639 *next_cluster == *prior_cluster + ClusterCount(1) && *next_copied == *copied
1640 } else {
1641 false
1642 }
1643 }
1644
1645 L2Mapping::Backing {
1646 backing_offset: prior_backing_offset,
1647 } => {
1648 let Some(expected_next) = prior_backing_offset.checked_add(1 << cluster_bits)
1649 else {
1650 return false;
1651 };
1652
1653 if let L2Mapping::Backing {
1654 backing_offset: next_offset,
1655 } = self
1656 {
1657 *next_offset == expected_next
1658 } else {
1659 false
1660 }
1661 }
1662
1663 L2Mapping::Zero {
1664 host_cluster: _,
1665 copied: _,
1666 } => {
1667 matches!(
1670 self,
1671 L2Mapping::Zero {
1672 host_cluster: _,
1673 copied: _,
1674 }
1675 )
1676 }
1677
1678 L2Mapping::Compressed {
1679 host_offset: _,
1680 length: _,
1681 } => {
1682 matches!(
1685 self,
1686 L2Mapping::Compressed {
1687 host_offset: _,
1688 length: _,
1689 }
1690 )
1691 }
1692 }
1693 }
1694}
1695
1696#[derive(Debug)]
1698pub(super) struct L2Table {
1699 cluster: Option<HostCluster>,
1701
1702 data: Box<[AtomicL2Entry]>,
1704
1705 cluster_bits: u32,
1707
1708 external_data_file: bool,
1710
1711 modified: AtomicBool,
1713
1714 writer_lock: Mutex<()>,
1716}
1717
1718#[derive(Debug)]
1720pub(super) struct L2TableWriteGuard<'a> {
1721 table: &'a L2Table,
1723
1724 _lock: MutexGuard<'a, ()>,
1726}
1727
1728#[maybe_async]
1729impl L2Table {
1730 pub fn new_cleared(header: &Header) -> Self {
1732 let mut data = Vec::with_capacity(header.l2_entries());
1733 data.resize_with(header.l2_entries(), Default::default);
1734
1735 L2Table {
1736 cluster: None,
1737 data: data.into_boxed_slice(),
1738 cluster_bits: header.cluster_bits(),
1739 external_data_file: header.external_data_file(),
1740 modified: true.into(),
1741 writer_lock: Default::default(),
1742 }
1743 }
1744
1745 pub fn get_mapping(&self, lookup_cluster: GuestCluster) -> io::Result<L2Mapping> {
1747 self.get(lookup_cluster.l2_index(self.cluster_bits))
1748 .into_mapping(lookup_cluster, self.cluster_bits, self.external_data_file)
1749 }
1750
1751 pub async fn lock_write(&self) -> L2TableWriteGuard<'_> {
1755 L2TableWriteGuard {
1756 table: self,
1757 _lock: self.writer_lock.lock().await,
1758 }
1759 }
1760}
1761
1762impl L2TableWriteGuard<'_> {
1763 pub fn get_mapping(&self, lookup_cluster: GuestCluster) -> io::Result<L2Mapping> {
1765 self.table.get_mapping(lookup_cluster)
1766 }
1767
1768 #[must_use = "Leaked allocation must be freed"]
1777 pub fn map_cluster(
1778 &mut self,
1779 index: usize,
1780 host_cluster: HostCluster,
1781 ) -> Option<(HostCluster, ClusterCount)> {
1782 let new = L2Entry::from_mapping(
1783 L2Mapping::DataFile {
1784 host_cluster,
1785 copied: true,
1786 },
1787 self.table.cluster_bits,
1788 );
1789 let l2e = unsafe { self.table.data[index].swap(new) };
1792 self.table.modified.store(true, Ordering::Relaxed);
1793
1794 let allocation = l2e.allocation(self.table.cluster_bits, self.table.external_data_file);
1795 if let Some((a_cluster, a_count)) = allocation {
1796 if a_cluster == host_cluster && a_count == ClusterCount(1) {
1797 None
1798 } else {
1799 allocation
1800 }
1801 } else {
1802 None
1803 }
1804 }
1805
1806 #[must_use = "Leaked allocation must be freed"]
1816 pub fn zero_cluster(
1817 &mut self,
1818 index: usize,
1819 keep_allocation: bool,
1820 ) -> io::Result<Option<(HostCluster, ClusterCount)>> {
1821 let cluster_copied = if keep_allocation {
1822 match self.table.data[index].get().into_mapping(
1823 GuestCluster(0), self.table.cluster_bits,
1825 self.table.external_data_file,
1826 )? {
1827 L2Mapping::DataFile {
1828 host_cluster,
1829 copied,
1830 } => Some((host_cluster, copied)),
1831 L2Mapping::Backing { backing_offset: _ } => None,
1832 L2Mapping::Zero {
1833 host_cluster: Some(host_cluster),
1834 copied,
1835 } => Some((host_cluster, copied)),
1836 L2Mapping::Zero {
1837 host_cluster: None,
1838 copied: _,
1839 } => None,
1840 L2Mapping::Compressed {
1841 host_offset: _,
1842 length: _,
1843 } => None,
1844 }
1845 } else {
1846 None
1847 };
1848
1849 let retained = cluster_copied.is_some();
1850 let new = if let Some((cluster, copied)) = cluster_copied {
1851 L2Mapping::Zero {
1852 host_cluster: Some(cluster),
1853 copied,
1854 }
1855 } else {
1856 L2Mapping::Zero {
1857 host_cluster: None,
1858 copied: false,
1859 }
1860 };
1861 let new = L2Entry::from_mapping(new, self.table.cluster_bits);
1862
1863 let old = unsafe { self.table.data[index].swap(new) };
1866 self.table.modified.store(true, Ordering::Relaxed);
1867
1868 let leaked = if !retained {
1869 old.allocation(self.table.cluster_bits, self.table.external_data_file)
1870 } else {
1871 None
1872 };
1873 Ok(leaked)
1874 }
1875
1876 #[must_use = "Leaked allocation must be freed"]
1882 pub fn discard_cluster(&mut self, index: usize) -> Option<(HostCluster, ClusterCount)> {
1883 let new = L2Entry(0);
1884
1885 let old = unsafe { self.table.data[index].swap(new) };
1888 self.table.modified.store(true, Ordering::Relaxed);
1889
1890 old.allocation(self.table.cluster_bits, self.table.external_data_file)
1891 }
1892}
1893
1894impl Table for L2Table {
1895 type InternalEntry = AtomicL2Entry;
1896 type Entry = L2Entry;
1897 const NAME: &'static str = "L2 table";
1898 const MAX_ENTRIES: usize = MAX_CLUSTER_SIZE / 8;
1899
1900 fn from_data(data: Box<[AtomicL2Entry]>, header: &Header) -> Self {
1901 assert!(data.len() == header.l2_entries());
1902
1903 Self {
1904 cluster: None,
1905 data,
1906 cluster_bits: header.cluster_bits(),
1907 external_data_file: header.external_data_file(),
1908 modified: true.into(),
1909 writer_lock: Default::default(),
1910 }
1911 }
1912
1913 fn entries(&self) -> usize {
1914 self.data.len()
1915 }
1916
1917 fn get_ref(&self, index: usize) -> Option<&AtomicL2Entry> {
1918 self.data.get(index)
1919 }
1920
1921 fn get(&self, index: usize) -> L2Entry {
1922 self.data
1923 .get(index)
1924 .map(|l2e| l2e.get())
1925 .unwrap_or(L2Entry(0))
1926 }
1927
1928 fn get_cluster(&self) -> Option<HostCluster> {
1929 self.cluster
1930 }
1931
1932 fn get_offset(&self) -> Option<HostOffset> {
1933 self.cluster.map(|index| index.offset(self.cluster_bits))
1934 }
1935
1936 fn set_cluster(&mut self, cluster: HostCluster) {
1937 self.cluster = Some(cluster);
1938 self.modified.store(true, Ordering::Relaxed);
1939 }
1940
1941 fn unset_cluster(&mut self) {
1942 self.cluster = None;
1943 }
1944
1945 fn is_modified(&self) -> bool {
1946 self.modified.load(Ordering::Relaxed)
1947 }
1948
1949 fn clear_modified(&self) {
1950 self.modified.store(false, Ordering::Relaxed);
1951 }
1952
1953 fn set_modified(&self) {
1954 self.modified.store(true, Ordering::Relaxed);
1955 }
1956
1957 fn cluster_bits(&self) -> u32 {
1958 self.cluster_bits
1959 }
1960}
1961
1962impl Clone for L2Table {
1963 fn clone(&self) -> Self {
1964 let mut data = Vec::with_capacity(self.data.len());
1965 for entry in &self.data {
1966 let entry = entry.get().without_copied();
1968 data.push(AtomicL2Entry(AtomicU64::new(entry.0)));
1969 }
1970
1971 let modified = AtomicBool::new(self.is_modified());
1972
1973 L2Table {
1974 cluster: None,
1975 data: data.into_boxed_slice(),
1976 cluster_bits: self.cluster_bits,
1977 external_data_file: self.external_data_file,
1978 modified,
1979 writer_lock: Default::default(),
1980 }
1981 }
1982}
1983
1984impl Drop for L2Table {
1985 fn drop(&mut self) {
1986 if self.is_modified() {
1987 error!("L2 table dropped while modified; was the image closed before being flushed?");
1988 }
1989 }
1990}
1991
1992#[derive(Copy, Clone, Default, Debug)]
1994pub(super) struct RefTableEntry(u64);
1995
1996impl RefTableEntry {
1997 pub fn refblock_offset(&self) -> Option<HostOffset> {
1999 let ofs = self.0 & 0xffff_ffff_ffff_fe00u64;
2000 if ofs == 0 {
2001 None
2002 } else {
2003 Some(HostOffset(ofs))
2004 }
2005 }
2006
2007 pub fn reserved_bits(&self) -> u64 {
2009 self.0 & 0x0000_0000_0000_01ffu64
2010 }
2011}
2012
2013impl TableEntry for RefTableEntry {
2014 fn try_from_plain(value: u64, header: &Header) -> io::Result<Self> {
2015 let entry = RefTableEntry(value);
2016
2017 if entry.reserved_bits() != 0 {
2018 return Err(invalid_data(format!(
2019 "Invalid reftable entry 0x{value:x}, reserved bits set (0x{:x})",
2020 entry.reserved_bits(),
2021 )));
2022 }
2023
2024 if let Some(rb_ofs) = entry.refblock_offset() {
2025 if rb_ofs.in_cluster_offset(header.cluster_bits()) != 0 {
2026 return Err(invalid_data(
2027 format!(
2028 "Invalid reftable entry 0x{value:x}, offset ({rb_ofs}) is not aligned to cluster size (0x{:x})",
2029 header.cluster_size(),
2030 ),
2031 ));
2032 }
2033 }
2034
2035 Ok(entry)
2036 }
2037
2038 fn to_plain(&self) -> u64 {
2039 self.0
2040 }
2041}
2042
2043#[derive(Debug)]
2045pub(super) struct RefTable {
2046 cluster: Option<HostCluster>,
2048
2049 data: Box<[RefTableEntry]>,
2051
2052 cluster_bits: u32,
2054
2055 modified: AtomicBool,
2057}
2058
2059impl RefTable {
2060 pub fn clone_and_grow(&self, header: &Header, at_least_index: usize) -> io::Result<Self> {
2065 let cluster_size = header.cluster_size();
2066 let rb_entries = header.rb_entries();
2067
2068 let mut extra_rbs = 1;
2071 let new_entry_count = loop {
2072 let entry_count = cmp::max(at_least_index + 1 + extra_rbs, self.data.len());
2073 let entry_count = entry_count.next_multiple_of(cluster_size / size_of::<u64>());
2074 let size = entry_count * size_of::<u64>();
2075 let refcount_clusters = size / cluster_size + extra_rbs;
2077 let rbs_needed = refcount_clusters.div_ceil(rb_entries);
2078 if extra_rbs == rbs_needed {
2079 break entry_count;
2080 }
2081 extra_rbs = rbs_needed;
2082 };
2083
2084 if new_entry_count > <Self as Table>::MAX_ENTRIES {
2085 return Err(io::Error::other(
2086 "Cannot grow the image to this size; refcount table would become too big",
2087 ));
2088 }
2089
2090 let mut new_data = vec![RefTableEntry::default(); new_entry_count];
2091 new_data[..self.data.len()].copy_from_slice(&self.data);
2092
2093 Ok(Self {
2094 cluster: None,
2095 data: new_data.into_boxed_slice(),
2096 cluster_bits: header.cluster_bits(),
2097 modified: true.into(),
2098 })
2099 }
2100
2101 pub fn in_bounds(&self, index: usize) -> bool {
2103 index < self.data.len()
2104 }
2105
2106 pub fn enter_refblock(&mut self, index: usize, rb: &RefBlock) -> io::Result<()> {
2108 let rb_offset = rb.get_offset().ok_or_else(|| {
2109 io::Error::new(
2110 io::ErrorKind::InvalidInput,
2111 "Refcount block as no assigned offset",
2112 )
2113 })?;
2114
2115 let rt_entry = RefTableEntry(rb_offset.0);
2116 debug_assert!(rt_entry.reserved_bits() == 0);
2117 self.data[index] = rt_entry;
2118 self.modified.store(true, Ordering::Relaxed);
2119
2120 Ok(())
2121 }
2122}
2123
2124impl Table for RefTable {
2125 type InternalEntry = RefTableEntry;
2126 type Entry = RefTableEntry;
2127 const NAME: &'static str = "Refcount table";
2128
2129 const MAX_ENTRIES: usize = <L1Table as Table>::MAX_ENTRIES;
2136
2137 fn from_data(data: Box<[RefTableEntry]>, header: &Header) -> Self {
2138 Self {
2139 cluster: None,
2140 data,
2141 cluster_bits: header.cluster_bits(),
2142 modified: true.into(),
2143 }
2144 }
2145
2146 fn entries(&self) -> usize {
2147 self.data.len()
2148 }
2149
2150 fn get_ref(&self, index: usize) -> Option<&RefTableEntry> {
2151 self.data.get(index)
2152 }
2153
2154 fn get(&self, index: usize) -> RefTableEntry {
2155 self.data.get(index).copied().unwrap_or(RefTableEntry(0))
2156 }
2157
2158 fn get_cluster(&self) -> Option<HostCluster> {
2159 self.cluster
2160 }
2161
2162 fn get_offset(&self) -> Option<HostOffset> {
2163 self.cluster.map(|index| index.offset(self.cluster_bits))
2164 }
2165
2166 fn set_cluster(&mut self, cluster: HostCluster) {
2167 self.cluster = Some(cluster);
2168 self.modified.store(true, Ordering::Relaxed);
2169 }
2170
2171 fn unset_cluster(&mut self) {
2172 self.cluster = None;
2173 }
2174
2175 fn is_modified(&self) -> bool {
2176 self.modified.load(Ordering::Relaxed)
2177 }
2178
2179 fn clear_modified(&self) {
2180 self.modified.store(false, Ordering::Relaxed);
2181 }
2182
2183 fn set_modified(&self) {
2184 self.modified.store(true, Ordering::Relaxed);
2185 }
2186
2187 fn cluster_bits(&self) -> u32 {
2188 self.cluster_bits
2189 }
2190}
2191
2192pub(super) struct RefBlock {
2194 cluster: Option<HostCluster>,
2196
2197 raw_data: IoBuffer,
2199
2200 refcount_order: u32,
2202
2203 cluster_bits: u32,
2205
2206 modified: AtomicBool,
2208
2209 writer_lock: Mutex<()>,
2211}
2212
2213pub(super) struct RefBlockWriteGuard<'a> {
2215 rb: &'a RefBlock,
2217
2218 _lock: MutexGuard<'a, ()>,
2220}
2221
2222#[maybe_async]
2223impl RefBlock {
2224 pub fn new_cleared<S: Storage>(for_image: &S, header: &Header) -> io::Result<Self> {
2226 let mut raw_data = IoBuffer::new(header.cluster_size(), for_image.mem_align())?;
2227 raw_data.as_mut().into_slice().fill(0);
2228
2229 Ok(RefBlock {
2230 cluster: None,
2231 raw_data,
2232 refcount_order: header.refcount_order(),
2233 cluster_bits: header.cluster_bits(),
2234 modified: true.into(),
2235 writer_lock: Default::default(),
2236 })
2237 }
2238
2239 pub async fn load<S: Storage>(
2241 image: &S,
2242 header: &Header,
2243 cluster: HostCluster,
2244 ) -> io::Result<Self> {
2245 let cluster_bits = header.cluster_bits();
2246 let cluster_size = 1 << cluster_bits;
2247 let refcount_order = header.refcount_order();
2248 let offset = cluster.offset(cluster_bits);
2249
2250 check_table(
2251 "Refcount block",
2252 offset.0,
2253 cluster_size,
2254 1,
2255 MAX_CLUSTER_SIZE,
2256 cluster_size,
2257 )?;
2258
2259 let mut raw_data =
2260 IoBuffer::new(cluster_size, cmp::max(image.mem_align(), size_of::<u64>()))?;
2261 image.read(&mut raw_data, offset.0).await?;
2262
2263 Ok(RefBlock {
2264 cluster: Some(cluster),
2265 raw_data,
2266 refcount_order,
2267 cluster_bits,
2268 modified: false.into(),
2269 writer_lock: Default::default(),
2270 })
2271 }
2272
2273 fn copy_raw_data<T: Copy + Sized, F: Fn(*const T) -> T>(
2277 &self,
2278 mem_align: usize,
2279 get: F,
2280 ) -> io::Result<IoBuffer> {
2281 assert!(cmp::max((1 << self.refcount_order) / 8, 1) == size_of::<T>());
2283
2284 let byte_size = 1 << self.cluster_bits;
2285 let mut buffer = IoBuffer::new(byte_size, cmp::max(mem_align, size_of::<T>()))?;
2286
2287 let raw_in = unsafe { self.raw_data.as_ref().into_typed_slice::<T>() };
2289 let raw_out = unsafe { buffer.as_mut().into_typed_slice::<T>() };
2291
2292 for (i, value) in raw_in.iter().enumerate() {
2293 raw_out[i] = get(value as *const T);
2294 }
2295
2296 Ok(buffer)
2297 }
2298
2299 pub async fn write<S: Storage>(&self, image: &S) -> io::Result<()> {
2301 let offset = self
2302 .get_offset()
2303 .ok_or_else(|| io::Error::other("Cannot write qcow2 refcount block, no offset set"))?;
2304
2305 self.clear_modified();
2306
2307 let buffer = match self.refcount_order {
2308 0..=3 => self.copy_raw_data::<u8, _>(image.mem_align(), |ptr| {
2310 unsafe { AtomicU8::from_ptr(ptr as *mut u8) }.load(Ordering::Relaxed)
2312 })?,
2313
2314 4 => self.copy_raw_data::<u16, _>(image.mem_align(), |ptr| {
2316 unsafe { AtomicU16::from_ptr(ptr as *mut u16) }.load(Ordering::Relaxed)
2318 })?,
2319
2320 5 => self.copy_raw_data::<u32, _>(image.mem_align(), |ptr| {
2322 unsafe { AtomicU32::from_ptr(ptr as *mut u32) }.load(Ordering::Relaxed)
2324 })?,
2325
2326 6 => self.copy_raw_data::<u64, _>(image.mem_align(), |ptr| {
2328 unsafe { AtomicU64::from_ptr(ptr as *mut u64) }.load(Ordering::Relaxed)
2330 })?,
2331
2332 _ => unreachable!(),
2333 };
2334
2335 if let Err(err) = image.write(&buffer, offset.0).await {
2336 self.set_modified();
2337 return Err(err);
2338 }
2339
2340 Ok(())
2341 }
2342
2343 pub fn get_cluster(&self) -> Option<HostCluster> {
2345 self.cluster
2346 }
2347
2348 pub fn get_offset(&self) -> Option<HostOffset> {
2350 self.cluster.map(|index| index.offset(self.cluster_bits))
2351 }
2352
2353 pub fn set_cluster(&mut self, cluster: HostCluster) {
2355 self.cluster = Some(cluster);
2356 self.set_modified();
2357 }
2358
2359 fn sub_byte_refcount_access(&self, index: usize) -> (usize, u8, usize) {
2366 let order = self.refcount_order;
2367 debug_assert!(order < 3);
2368
2369 let byte_index = index >> (3 - order);
2373 let mask = (1 << (1 << order)) - 1;
2376 let shift = (index << order) % 8;
2379
2380 (byte_index, mask, shift)
2381 }
2382
2383 pub fn get(&self, index: usize) -> u64 {
2385 match self.refcount_order {
2386 0..=2 => {
2388 let (index, mask, shift) = self.sub_byte_refcount_access(index);
2389 let raw_data_slice = unsafe { self.raw_data.as_ref().into_typed_slice::<u8>() };
2390 let atomic =
2391 unsafe { AtomicU8::from_ptr(&raw_data_slice[index] as *const u8 as *mut u8) };
2392 ((atomic.load(Ordering::Relaxed) >> shift) & mask) as u64
2393 }
2394
2395 3 => {
2397 let raw_data_slice = unsafe { self.raw_data.as_ref().into_typed_slice::<u8>() };
2398 let atomic =
2399 unsafe { AtomicU8::from_ptr(&raw_data_slice[index] as *const u8 as *mut u8) };
2400 atomic.load(Ordering::Relaxed) as u64
2401 }
2402
2403 4 => {
2405 let raw_data_slice = unsafe { self.raw_data.as_ref().into_typed_slice::<u16>() };
2406 let atomic = unsafe {
2407 AtomicU16::from_ptr(&raw_data_slice[index] as *const u16 as *mut u16)
2408 };
2409 u16::from_be(atomic.load(Ordering::Relaxed)) as u64
2410 }
2411
2412 5 => {
2414 let raw_data_slice = unsafe { self.raw_data.as_ref().into_typed_slice::<u32>() };
2415 let atomic = unsafe {
2416 AtomicU32::from_ptr(&raw_data_slice[index] as *const u32 as *mut u32)
2417 };
2418 u32::from_be(atomic.load(Ordering::Relaxed)) as u64
2419 }
2420
2421 6 => {
2423 let raw_data_slice = unsafe { self.raw_data.as_ref().into_typed_slice::<u64>() };
2424 let atomic = unsafe {
2425 AtomicU64::from_ptr(&raw_data_slice[index] as *const u64 as *mut u64)
2426 };
2427 u64::from_be(atomic.load(Ordering::Relaxed))
2428 }
2429
2430 _ => unreachable!(),
2431 }
2432 }
2433
2434 pub async fn lock_write(&self) -> RefBlockWriteGuard<'_> {
2438 RefBlockWriteGuard {
2439 rb: self,
2440 _lock: self.writer_lock.lock().await,
2441 }
2442 }
2443
2444 pub fn is_modified(&self) -> bool {
2446 self.modified.load(Ordering::Relaxed)
2447 }
2448
2449 pub fn clear_modified(&self) {
2451 self.modified.store(false, Ordering::Relaxed);
2452 }
2453
2454 pub fn set_modified(&self) {
2456 self.modified.store(true, Ordering::Relaxed);
2457 }
2458
2459 pub fn is_zero(&self, index: usize) -> bool {
2461 self.get(index) == 0
2462 }
2463}
2464
2465impl RefBlockWriteGuard<'_> {
2466 unsafe fn fetch_update_bitset(
2469 bitset: &AtomicU8,
2470 change: i64,
2471 base_mask: u8,
2472 shift: usize,
2473 ) -> io::Result<u64> {
2474 let mask = base_mask << shift;
2475
2476 let full = bitset.load(Ordering::Relaxed);
2478 let old = (full & mask) >> shift;
2479 let new = if change > 0 {
2480 let change = change.try_into().map_err(|_| {
2481 io::Error::new(
2482 io::ErrorKind::InvalidInput,
2483 format!("Requested refcount change of {change} is too big for the image’s refcount width"),
2484 )
2485 })?;
2486 old.checked_add(change)
2487 } else {
2488 let change = (-change).try_into().map_err(|_| {
2489 io::Error::new(
2490 io::ErrorKind::InvalidInput,
2491 format!("Requested refcount change of {change} is too big for the image’s refcount width"),
2492 )
2493 })?;
2494 old.checked_sub(change)
2495 };
2496 let new = new.ok_or_else(|| {
2497 invalid_data(format!(
2498 "Changing refcount from {old} by {change} would overflow"
2499 ))
2500 })?;
2501 if new > base_mask {
2502 return Err(invalid_data(format!(
2503 "Changing refcount from {old} to {new} (by {change}) would overflow"
2504 )));
2505 }
2506
2507 let full = (full & !mask) | (new << shift);
2508 bitset.store(full, Ordering::Relaxed);
2509 Ok(old as u64)
2510 }
2511
2512 unsafe fn fetch_update_full<
2515 T,
2516 L: FnOnce(&T) -> u64,
2517 S: FnOnce(&T, u64) -> Result<(), TryFromIntError>,
2518 >(
2519 atomic: &T,
2520 change: i64,
2521 load: L,
2522 store: S,
2523 ) -> io::Result<u64> {
2524 let old = load(atomic);
2526
2527 let new = if change > 0 {
2528 old.checked_add(change as u64)
2529 } else {
2530 old.checked_sub(-change as u64)
2531 };
2532 let new = new.ok_or_else(|| {
2533 invalid_data(format!(
2534 "Changing refcount from {old} by {change} would overflow"
2535 ))
2536 })?;
2537
2538 store(atomic, new).map_err(|_| {
2539 invalid_data(format!(
2540 "Changing refcount from {old} to {new} (by {change}) would overflow"
2541 ))
2542 })?;
2543
2544 Ok(old)
2545 }
2546
2547 fn modify(&mut self, index: usize, change: i64) -> io::Result<u64> {
2549 let result = match self.rb.refcount_order {
2550 0..=2 => {
2552 let (index, mask, shift) = self.rb.sub_byte_refcount_access(index);
2553 let raw_data_slice = unsafe { self.rb.raw_data.as_ref().into_typed_slice::<u8>() };
2554 let atomic =
2555 unsafe { AtomicU8::from_ptr(&raw_data_slice[index] as *const u8 as *mut u8) };
2556 unsafe { Self::fetch_update_bitset(atomic, change, mask, shift) }
2558 }
2559
2560 3 => {
2562 let raw_data_slice = unsafe { self.rb.raw_data.as_ref().into_typed_slice::<u8>() };
2563 let atomic =
2564 unsafe { AtomicU8::from_ptr(&raw_data_slice[index] as *const u8 as *mut u8) };
2565 unsafe {
2567 Self::fetch_update_full(
2568 atomic,
2569 change,
2570 |a| a.load(Ordering::Relaxed) as u64,
2571 |a, v| {
2572 a.store(v.try_into()?, Ordering::Relaxed);
2573 Ok(())
2574 },
2575 )
2576 }
2577 }
2578
2579 4 => {
2581 let raw_data_slice = unsafe { self.rb.raw_data.as_ref().into_typed_slice::<u16>() };
2582 let atomic = unsafe {
2583 AtomicU16::from_ptr(&raw_data_slice[index] as *const u16 as *mut u16)
2584 };
2585 unsafe {
2586 Self::fetch_update_full(
2587 atomic,
2588 change,
2589 |a| u16::from_be(a.load(Ordering::Relaxed)) as u64,
2590 |a, v| {
2591 a.store(u16::try_from(v)?.to_be(), Ordering::Relaxed);
2592 Ok(())
2593 },
2594 )
2595 }
2596 }
2597
2598 5 => {
2600 let raw_data_slice = unsafe { self.rb.raw_data.as_ref().into_typed_slice::<u32>() };
2601 let atomic = unsafe {
2602 AtomicU32::from_ptr(&raw_data_slice[index] as *const u32 as *mut u32)
2603 };
2604 unsafe {
2605 Self::fetch_update_full(
2606 atomic,
2607 change,
2608 |a| u32::from_be(a.load(Ordering::Relaxed)) as u64,
2609 |a, v| {
2610 a.store(u32::try_from(v)?.to_be(), Ordering::Relaxed);
2611 Ok(())
2612 },
2613 )
2614 }
2615 }
2616
2617 6 => {
2619 let raw_data_slice = unsafe { self.rb.raw_data.as_ref().into_typed_slice::<u64>() };
2620 let atomic = unsafe {
2621 AtomicU64::from_ptr(&raw_data_slice[index] as *const u64 as *mut u64)
2622 };
2623 unsafe {
2624 Self::fetch_update_full(
2625 atomic,
2626 change,
2627 |a| u64::from_be(a.load(Ordering::Relaxed)),
2628 |a, v| {
2629 a.store(v.to_be(), Ordering::Relaxed);
2630 Ok(())
2631 },
2632 )
2633 }
2634 }
2635
2636 _ => unreachable!(),
2637 };
2638
2639 let result = result?;
2640 self.rb.modified.store(true, Ordering::Relaxed);
2641 Ok(result)
2642 }
2643
2644 pub fn increment(&mut self, index: usize) -> io::Result<u64> {
2648 self.modify(index, 1)
2649 }
2650
2651 pub fn decrement(&mut self, index: usize) -> io::Result<u64> {
2655 self.modify(index, -1)
2656 }
2657
2658 pub fn is_zero(&self, index: usize) -> bool {
2660 self.rb.is_zero(index)
2661 }
2662}
2663
2664impl Drop for RefBlock {
2665 fn drop(&mut self) {
2666 if self.is_modified() {
2667 error!(
2668 "Refcount block dropped while modified; was the image closed before being flushed?"
2669 );
2670 }
2671 }
2672}
2673
2674pub trait TableEntry
2676where
2677 Self: Sized,
2678{
2679 fn try_from_plain(value: u64, header: &Header) -> io::Result<Self>;
2681
2682 fn to_plain(&self) -> u64;
2684}
2685
2686#[maybe_async(AFIT)]
2688pub trait Table: Sized {
2689 type InternalEntry: TableEntry;
2691 type Entry: Copy;
2693 const NAME: &'static str;
2695 const MAX_ENTRIES: usize;
2697
2698 fn from_data(data: Box<[Self::InternalEntry]>, header: &Header) -> Self;
2700
2701 fn entries(&self) -> usize;
2703 fn get_ref(&self, index: usize) -> Option<&Self::InternalEntry>;
2705 fn get(&self, index: usize) -> Self::Entry;
2707 fn get_cluster(&self) -> Option<HostCluster>;
2709 fn get_offset(&self) -> Option<HostOffset>;
2711 fn set_cluster(&mut self, cluster: HostCluster);
2713 fn unset_cluster(&mut self);
2715
2716 fn cluster_bits(&self) -> u32;
2720
2721 fn is_modified(&self) -> bool;
2723 fn clear_modified(&self);
2725 fn set_modified(&self);
2727
2728 fn byte_size(&self) -> usize {
2730 self.entries() * size_of::<u64>()
2731 }
2732
2733 fn cluster_count(&self) -> ClusterCount {
2735 ClusterCount::from_byte_size(self.byte_size() as u64, self.cluster_bits())
2736 }
2737
2738 async fn load<S: Storage>(
2740 image: &S,
2741 header: &Header,
2742 cluster: HostCluster,
2743 entries: usize,
2744 ) -> io::Result<Self> {
2745 let offset = cluster.offset(header.cluster_bits());
2746
2747 check_table(
2748 Self::NAME,
2749 offset.0,
2750 entries,
2751 size_of::<u64>(),
2752 Self::MAX_ENTRIES,
2753 header.cluster_size(),
2754 )?;
2755
2756 let byte_size = entries * size_of::<u64>();
2757 let mut buffer = IoBuffer::new(byte_size, cmp::max(image.mem_align(), size_of::<u64>()))?;
2758
2759 image.read(&mut buffer, offset.0).await?;
2760
2761 let raw_table = unsafe { buffer.as_ref().into_typed_slice::<u64>() };
2763
2764 let mut table = Vec::<Self::InternalEntry>::with_capacity(entries);
2765 for be_value in raw_table {
2766 table.push(Self::InternalEntry::try_from_plain(
2767 u64::from_be(*be_value),
2768 header,
2769 )?)
2770 }
2771
2772 let mut table = Self::from_data(table.into_boxed_slice(), header);
2773 table.set_cluster(cluster);
2774 table.clear_modified();
2775 Ok(table)
2776 }
2777
2778 async fn write<S: Storage>(&self, image: &S) -> io::Result<()> {
2782 let offset = self
2783 .get_offset()
2784 .ok_or_else(|| io::Error::other("Cannot write qcow2 metadata table, no offset set"))?;
2785
2786 check_table(
2787 Self::NAME,
2788 offset.0,
2789 self.entries(),
2790 size_of::<u64>(),
2791 Self::MAX_ENTRIES,
2792 1 << self.cluster_bits(),
2793 )?;
2794
2795 let byte_size = self.byte_size();
2796 let mut buffer = IoBuffer::new(byte_size, cmp::max(image.mem_align(), size_of::<u64>()))?;
2797
2798 self.clear_modified();
2799
2800 let raw_table = unsafe { buffer.as_mut().into_typed_slice::<u64>() };
2802 for (i, be_value) in raw_table.iter_mut().enumerate() {
2803 *be_value = self.get_ref(i).map(|e| e.to_plain()).unwrap_or(0).to_be();
2805 }
2806
2807 if let Err(err) = image.write(&buffer, offset.0).await {
2808 self.set_modified();
2809 return Err(err);
2810 }
2811
2812 Ok(())
2813 }
2814
2815 async fn write_entry<S: Storage>(&self, image: &S, index: usize) -> io::Result<()> {
2819 let byte_size = self.byte_size();
2823 let power_of_two_up_to_byte_size = ((byte_size / 2) + 1).next_power_of_two();
2824 let alignment = cmp::min(
2825 power_of_two_up_to_byte_size,
2826 cmp::max(
2827 cmp::max(image.mem_align(), image.req_align()),
2828 size_of::<u64>(),
2829 ),
2830 );
2831 let alignment_in_entries = alignment / size_of::<u64>();
2832
2833 let offset = self
2834 .get_offset()
2835 .ok_or_else(|| io::Error::other("Cannot write qcow2 metadata table, no offset set"))?;
2836
2837 check_table(
2838 Self::NAME,
2839 offset.0,
2840 self.entries(),
2841 size_of::<u64>(),
2842 Self::MAX_ENTRIES,
2843 1 << self.cluster_bits(),
2844 )?;
2845
2846 let mut buffer = IoBuffer::new(alignment, cmp::max(image.mem_align(), size_of::<u64>()))?;
2847
2848 let raw_entries = unsafe { buffer.as_mut().into_typed_slice::<u64>() };
2850 let first_index = (index / alignment_in_entries) * alignment_in_entries;
2851 #[allow(clippy::needless_range_loop)]
2852 for i in 0..alignment_in_entries {
2853 raw_entries[i] = self
2855 .get_ref(first_index + i)
2856 .map(|e| e.to_plain())
2857 .unwrap_or(0)
2858 .to_be();
2859 }
2860
2861 image
2862 .write(&buffer, offset.0 + (first_index * size_of::<u64>()) as u64)
2863 .await
2864 }
2865}
2866
2867fn check_table(
2872 name: &str,
2873 offset: u64,
2874 entries: usize,
2875 entry_size: usize,
2876 max_entries: usize,
2877 cluster_size: usize,
2878) -> io::Result<()> {
2879 if entries > max_entries {
2880 return Err(invalid_data(format!(
2881 "{name} too big: {entries} > {max_entries}",
2882 )));
2883 }
2884
2885 if !offset.is_multiple_of(cluster_size as u64) {
2886 return Err(invalid_data(format!("{name}: Unaligned offset: {offset}")));
2887 }
2888
2889 let byte_size = entries
2890 .checked_mul(entry_size)
2891 .ok_or_else(|| invalid_data(format!("{name} size overflow: {entries} * {entry_size}")))?;
2892 let end_offset = offset
2893 .checked_add(byte_size as u64)
2894 .ok_or_else(|| invalid_data(format!("{name} offset overflow: {offset} + {byte_size}")))?;
2895 if end_offset > MAX_FILE_LENGTH {
2896 return Err(invalid_data(format!(
2897 "{name}: Invalid end offset: {end_offset} > {MAX_FILE_LENGTH}"
2898 )));
2899 }
2900
2901 Ok(())
2902}
2903
2904fn encode_binary<T: OnDiskStruct>(val: &T) -> io::Result<Vec<u8>> {
2906 let mut vec = vec![0; T::ON_DISK_SIZE];
2907 val.store_to(&mut vec)?;
2908 Ok(vec)
2909}
2910
2911fn decode_binary<T: OnDiskStruct>(slice: &[u8]) -> io::Result<T> {
2913 T::load_from(slice)
2914}