Skip to main content

imago/qcow2/
metadata.rs

1//! Functionality for working with qcow2 metadata.
2
3use 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
18/// Qcow header magic ("QFI\xfb").
19pub(super) const MAGIC: u32 = 0x51_46_49_fb;
20
21/// Maximum file length.
22const MAX_FILE_LENGTH: u64 = 0x0100_0000_0000_0000u64;
23
24/// Maximum permissible host offset.
25pub(super) const MAX_OFFSET: HostOffset = HostOffset(MAX_FILE_LENGTH - 512);
26
27/// Minimum cluster size.
28///
29/// Defined by the specification.
30pub(super) const MIN_CLUSTER_SIZE: usize = 512;
31
32/// Maximum cluster size.
33///
34/// This is QEMU’s limit, so we can apply it, too.
35pub(super) const MAX_CLUSTER_SIZE: usize = 2 * 1024 * 1024;
36
37/// Minimum number of bits per refcount entry.
38pub(super) const MIN_REFCOUNT_WIDTH: usize = 1;
39
40/// Maximum number of bits per refcount entry.
41pub(super) const MAX_REFCOUNT_WIDTH: usize = 64;
42
43on_disk_struct! {
44/// Qcow2 v2 header.
45struct V2Header/BE, no_gaps {
46    /// Qcow magic string ("QFI\xfb").
47    magic: u32[0],
48
49    /// Version number (valid values are 2 and 3).
50    version: u32[4],
51
52    /// Offset into the image file at which the backing file name is stored (NB: The string is not
53    /// null terminated).  0 if the image doesn’t have a backing file.
54    ///
55    /// Note: backing files are incompatible with raw external data files (auto-clear feature bit
56    /// 1).
57    backing_file_offset: u64[8],
58
59    /// Length of the backing file name in bytes.  Must not be longer than 1023 bytes.  Undefined
60    /// if the image doesn’t have a backing file.
61    backing_file_size: u32[16],
62
63    /// Number of bits that are used for addressing an offset within a cluster (`1 << cluster_bits`
64    /// is the cluster size).  Must not be less than 9 (i.e. 512 byte clusters).
65    ///
66    /// Note: qemu as of today has an implementation limit of 2 MB as the maximum cluster size and
67    /// won’t be able to open images with larger cluster sizes.
68    ///
69    /// Note: if the image has Extended L2 Entries then `cluster_bits` must be at least 14 (i.e.
70    /// 16384 byte clusters).
71    cluster_bits: u32[20],
72
73    /// Virtual disk size in bytes.
74    ///
75    /// Note: qemu has an implementation limit of 32 MB as the maximum L1 table size.  With a 2 MB
76    /// cluster size, it is unable to populate a virtual cluster beyond 2 EB (61 bits); with a 512
77    /// byte cluster size, it is unable to populate a virtual size larger than 128 GB (37 bits).
78    /// Meanwhile, L1/L2 table layouts limit an image to no more than 64 PB (56 bits) of populated
79    /// clusters, and an image may hit other limits first (such as a file system’s maximum size).
80    size: AtomicU64[24],
81
82    /// Encryption method:
83    ///
84    /// 0. no encryption
85    /// 1. AES encryption
86    /// 2. LUKS encryption
87    crypt_method: u32[32],
88
89    /// Number of entries in the active L1 table.
90    l1_size: AtomicU32[36],
91
92    /// Offset into the image file at which the active L1 table starts.  Must be aligned to a
93    /// cluster boundary.
94    l1_table_offset: AtomicU64[40],
95
96    /// Offset into the image file at which the refcount table starts.  Must be aligned to a
97    /// cluster boundary.
98    refcount_table_offset: AtomicU64[48],
99
100    /// Number of clusters that the refcount table occupies.
101    refcount_table_clusters: AtomicU32[56],
102
103    /// Number of snapshots contained in the image.
104    nb_snapshots: u32[60],
105
106    /// Offset into the image file at which the snapshot table starts.  Must be aligned to a
107    /// cluster boundary.
108    snapshots_offset: u64[64],
109}
110}
111
112on_disk_struct! {
113/// Qcow2 v3 header.
114struct V3HeaderBase/BE, no_gaps {
115    /// Bitmask of incompatible features.  An implementation must fail to open an image if an
116    /// unknown bit is set.
117    ///
118    /// 0. Dirty bit.  If this bit is set then refcounts may be inconsistent, make sure to scan
119    ///    L1/L2 tables to repair refcounts before accessing the image.
120    /// 1. Corrupt bit.  If this bit is set then any data structure may be corrupt and the image
121    ///    must not be written to (unless for regaining consistency).
122    /// 2. External data file bit.  If this bit is set, an external data file is used.  Guest
123    ///    clusters are then stored in the external data file.  For such images, clusters in the
124    ///    external data file are not refcounted.  The offset field in the Standard Cluster
125    ///    Descriptor must match the guest offset and neither compressed clusters nor internal
126    ///    snapshots are supported.  An External Data File Name header extension may be present if
127    ///    this bit is set.
128    /// 3. Compression type bit.  If this bit is set, a non-default compression is used for
129    ///    compressed clusters.  The compression_type field must be present and not zero.
130    /// 4. Extended L2 Entries.  If this bit is set then L2 table entries use an extended format
131    ///    that allows subcluster-based allocation.  See the Extended L2 Entries section for more
132    ///    details.
133    ///
134    /// Bits 5-63 are reserved (set to 0).
135    incompatible_features: u64[0],
136
137    /// Bitmask of compatible features.  An implementation can safely ignore any unknown bits that
138    /// are set.
139    ///
140    /// 0. Lazy refcounts bit.  If this bit is set then lazy refcount updates can be used.  This
141    ///    means marking the image file dirty and postponing refcount metadata updates.
142    ///
143    /// Bits 1-63 are reserved (set to 0).
144    compatible_features: u64[8],
145
146    /// Bitmask of auto-clear features.  An implementation may only write to an image with unknown
147    /// auto-clear features if it clears the respective bits from this field first.
148    ///
149    /// 0. Bitmaps extension bit.  This bit indicates consistency for the bitmaps extension data.
150    ///    It is an error if this bit is set without the bitmaps extension present.  If the bitmaps
151    ///    extension is present but this bit is unset, the bitmaps extension data must be
152    ///    considered inconsistent.
153    /// 1. Raw external data bit.  If this bit is set, the external data file can be read as a
154    ///    consistent standalone raw image without looking at the qcow2 metadata.  Setting this bit
155    ///    has a performance impact for some operations on the image (e.g. writing zeros requires
156    ///    writing to the data file instead of only setting the zero flag in the L2 table entry)
157    ///    and conflicts with backing files.  This bit may only be set if the External Data File
158    ///    bit (incompatible feature bit 1) is also set.
159    ///
160    /// Bits 2-63 are reserved (set to 0).
161    autoclear_features: u64[16],
162
163    /// Describes the width of a reference count block entry (width in bits: `refcount_bits = 1 <<
164    /// refcount_order`).  For version 2 images, the order is always assumed to be 4 (i.e.
165    /// `refcount_bits = 16`).  This value may not exceed 6 (i.e. `refcount_bits = 64`).
166    refcount_order: u32[24],
167
168    /// Length of the header structure in bytes.  For version 2 images, the length is always
169    /// assumed to be 72 bytes.  For version 3 it’s at least 104 bytes and must be a multiple of 8.
170    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    /// Incompatible feature bits.
188    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    /// Get this feature’s feature name table key.
199    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    /// Compatible feature bits.
210    pub(super) enum CompatibleFeatures as u64 {
211        LazyRefcounts = 1 << 0,
212    }
213}
214
215impl From<CompatibleFeatures> for (FeatureType, u8) {
216    /// Get this feature’s feature name table key.
217    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    /// Autoclear feature bits.
228    pub(super) enum AutoclearFeatures as u64 {
229        Bitmaps = 1 << 0,
230        RawExternalData = 1 << 1,
231    }
232}
233
234impl From<AutoclearFeatures> for (FeatureType, u8) {
235    /// Get this feature’s feature name table key.
236    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    /// Extension type IDs.
244    pub(super) enum HeaderExtensionType as u32 {
245        /// End of extension list.
246        End = 0,
247
248        /// Backing file format string.
249        BackingFileFormat = 0xe2792aca,
250
251        /// Map of feature bits to human-readable names.
252        FeatureNameTable = 0x6803f857,
253
254        /// External data file filename string.
255        ExternalDataFileName = 0x44415441,
256    }
257}
258
259on_disk_struct! {
260/// Header for a header extension.
261#[derive(Default)]
262struct HeaderExtensionHeader/BE, no_gaps {
263    /// Type code of the header extension.
264    extension_type: u32[0],
265
266    /// Data length.
267    length: u32[4],
268}
269}
270
271numerical_enum! {
272    /// Feature type ID for the feature name table.
273    #[derive(Hash)]
274    pub(super) enum FeatureType as u8 {
275        Incompatible = 0,
276        Compatible = 1,
277        Autoclear = 2,
278    }
279}
280
281/// Header extensions (high-level representation).
282#[derive(Debug, Clone, Eq, PartialEq)]
283pub(super) enum HeaderExtension {
284    /// Backing file format string.
285    BackingFileFormat(String),
286
287    /// Map of feature bits to human-readable names.
288    FeatureNameTable(HashMap<(FeatureType, u8), String>),
289
290    /// External data file filename string.
291    ExternalDataFileName(String),
292
293    /// Unknown extension.
294    Unknown {
295        /// Type.
296        extension_type: u32,
297        /// Data (as read).
298        data: Vec<u8>,
299    },
300}
301
302/// Integrated header representation.
303pub(super) struct Header {
304    /// v2 part of the header.
305    v2: V2Header,
306
307    /// Base v3 part of the header.
308    v3: V3HeaderBase,
309
310    /// Unrecognized header fields.
311    unknown_header_fields: Vec<u8>,
312
313    /// Backing filename string.
314    backing_filename: Option<String>,
315
316    /// Extensions.
317    extensions: Vec<HeaderExtension>,
318
319    /// Whether an external data file is required.
320    external_data_file: bool,
321}
322
323#[maybe_async]
324impl Header {
325    /// Load the qcow2 header from disk.
326    ///
327    /// If `writable` is false, do not perform any modifications (e.g. clearing auto-clear bits).
328    pub async fn load<S: Storage>(image: &S, writable: bool) -> io::Result<Self> {
329        // TODO: More sanity checks.
330        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        // Check for header extension conflicts
478        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        // No need to clear autoclear features for read-only images
522        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    /// Write the qcow2 header to disk.
549    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        // If the header gets too long, try to remove the feature name table to make it small
561        // enough
562        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()) // length in bytes
574                .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; // length in bytes
600        } 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    /// Create a header for a new image.
630    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, // will be set by `Self::write()`
662                backing_file_size: 0,   // will be set by `Self::write()`
663                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, // will be set by `Self::write()`
679            },
680            unknown_header_fields: Vec::new(),
681            backing_filename,
682            extensions,
683            external_data_file: has_external_data_file,
684        }
685    }
686
687    /// Update from a newly loaded header.
688    ///
689    /// Checks whether fields we consider immutable have remained the same, and updates mutable
690    /// fields.
691    pub fn update(&self, new_header: &Header) -> io::Result<()> {
692        /// Verify that the given field matches in `self` and `new_header`.
693        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)?; // TODO: Should be mutable
709        check_field!(v2.backing_file_size)?; // TODO: Should be mutable
710        check_field!(v2.cluster_bits)?;
711        // Size is mutable
712        // L1 position is mutable
713        // Reftable position is mutable
714        check_field!(v2.crypt_method)?;
715        check_field!(v2.nb_snapshots)?; // TODO: Should be mutable
716        check_field!(v2.snapshots_offset)?; // TODO: Should be mutable
717        check_field!(v3.incompatible_features)?; // TODO: Should be mutable
718        check_field!(v3.compatible_features)?; // TODO: Should be mutable
719        check_field!(v3.autoclear_features)?; // TODO: Should be mutable
720        check_field!(v3.refcount_order)?;
721        // header length is OK to ignore (as long as it’s valid)
722
723        // TODO: Should be mutable
724        (self.unknown_header_fields == new_header.unknown_header_fields)
725            .then_some(())
726            .ok_or_else(|| io::Error::other("Unknown header fields modified"))?;
727        // TODO: Should be mutable
728        (self.backing_filename == new_header.backing_filename)
729            .then_some(())
730            .ok_or_else(|| io::Error::other("Backing filename modified"))?;
731        // TODO: Should be mutable
732        (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    /// Guest disk size.
767    pub fn size(&self) -> u64 {
768        self.v2.size.load(Ordering::Relaxed)
769    }
770
771    /// Require a minimum qcow2 version.
772    ///
773    /// Return an error if the version requirement is not met.
774    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    /// Set the guest disk size.
787    pub fn set_size(&self, new_size: u64) {
788        self.v2.size.store(new_size, Ordering::Relaxed)
789    }
790
791    /// log2 of the cluster size.
792    pub fn cluster_bits(&self) -> u32 {
793        self.v2.cluster_bits
794    }
795
796    /// Cluster size in bytes.
797    pub fn cluster_size(&self) -> usize {
798        1 << self.cluster_bits()
799    }
800
801    /// Number of entries per L2 table.
802    pub fn l2_entries(&self) -> usize {
803        // 3 == log2(size_of::<u64>())
804        1 << (self.cluster_bits() - 3)
805    }
806
807    /// log2 of the number of entries per refcount block.
808    pub fn rb_bits(&self) -> u32 {
809        // log2(cluster_size / (refcount_bits / 8 bits per byte))
810        // = log2(cluster_size * 8 / refcount_bits)
811        // = log2(cluster_size) + log2(8) - log2(refcount_bits)
812        self.cluster_bits() + 3 - self.refcount_order()
813    }
814
815    /// Number of entries per refcount block.
816    pub fn rb_entries(&self) -> usize {
817        1 << self.rb_bits()
818    }
819
820    /// log2 of the refcount bits.
821    pub fn refcount_order(&self) -> u32 {
822        self.v3.refcount_order
823    }
824
825    /// Offset of the L1 table.
826    pub fn l1_table_offset(&self) -> HostOffset {
827        HostOffset(self.v2.l1_table_offset.load(Ordering::Relaxed))
828    }
829
830    /// Number of entries in the L1 table.
831    pub fn l1_table_entries(&self) -> usize {
832        self.v2.l1_size.load(Ordering::Relaxed) as usize
833    }
834
835    /// Enter a new L1 table in the image header.
836    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    /// Offset of the refcount table.
857    pub fn reftable_offset(&self) -> HostOffset {
858        HostOffset(self.v2.refcount_table_offset.load(Ordering::Relaxed))
859    }
860
861    /// Number of clusters occupied by the refcount table.
862    pub fn reftable_clusters(&self) -> ClusterCount {
863        ClusterCount(self.v2.refcount_table_clusters.load(Ordering::Relaxed) as u64)
864    }
865
866    /// Number of entries in the refcount table.
867    pub fn reftable_entries(&self) -> usize {
868        // 3 == log2(size_of::<u64>())
869        (self.reftable_clusters().byte_size(self.cluster_bits()) >> 3) as usize
870    }
871
872    /// Enter a new refcount table in the image header.
873    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    /// Backing filename from the image header (if any).
898    pub fn backing_filename(&self) -> Option<&String> {
899        self.backing_filename.as_ref()
900    }
901
902    /// Backing format string from the image header (if any).
903    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    /// Whether this image requires an external data file.
911    pub fn external_data_file(&self) -> bool {
912        self.external_data_file
913    }
914
915    /// External data file filename from the image header (if any).
916    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    /// Translate a feature bit to a human-readable name.
924    ///
925    /// Uses the feature name table from the image header, if present.
926    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    /// Serialize all header extensions.
939    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    /// Helper for functions that just need to change little bits in the v2 header part.
965    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    /// Write the refcount table pointer (offset and size) to disk.
971    pub async fn write_reftable_pointer<S: Storage>(&self, image: &S) -> io::Result<()> {
972        // TODO: Just write the reftable offset and size
973        self.write_v2_header(image).await
974    }
975
976    /// Write the L1 table pointer (offset and size) to disk.
977    pub async fn write_l1_table_pointer<S: Storage>(&self, image: &S) -> io::Result<()> {
978        // TODO: Just write the L1 table offset and size
979        self.write_v2_header(image).await
980    }
981
982    /// Write the guest disk size to disk.
983    pub async fn write_size<S: Storage>(&self, image: &S) -> io::Result<()> {
984        // TODO: Just write the size
985        self.write_v2_header(image).await
986    }
987}
988
989impl HeaderExtension {
990    /// Parse an extension from its type and data.  Unrecognized types are stored as `Unknown`
991    /// extensions, encountering the end of extensions returns `Ok(None)`.
992    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, // skip unrecognized entries
1008                        };
1009                        // Cannot use CStr to parse this, as it may not be NUL-terminated.
1010                        // Use this to remove everything from the first NUL byte.
1011                        let feat_name_bytes = feat[2..].split(|c| *c == 0).next().unwrap();
1012                        // Then just use it as a UTF-8 string.
1013                        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    /// Return the extension type ID.
1036    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    /// Serialize this extension’s data (exclusing its header).
1051    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                    // Might truncate in the middle of a multibyte character, but getting that
1063                    // right is complicated and probably not worth it
1064                    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    /// Creates a [`Self::FeatureNameTable`].
1079    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/// L1 table entry.
1105///
1106/// - Bit 0 - 8: Reserved (set to 0)
1107/// - Bit 9 – 55: Bits 9-55 of the offset into the image file at which the L2 table starts.  Must
1108///   be aligned to a cluster boundary.  If the offset is 0, the L2 table and all clusters
1109///   described by this L2 table are unallocated.
1110/// - Bit 56 - 62: Reserved (set to 0)
1111/// - Bit 63: 0 for an L2 table that is unused or requires COW, 1 if its refcount is exactly one.
1112///   This information is only accurate in the active L1 table.
1113#[derive(Copy, Clone, Default, Debug)]
1114pub(super) struct L1Entry(u64);
1115
1116impl L1Entry {
1117    /// Offset of the L2 table, if any.
1118    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    /// Whether the L2 table’s cluster is “copied”.
1128    ///
1129    /// `true` means is refcount is one, `false` means modifying it will require COW.
1130    pub fn is_copied(&self) -> bool {
1131        self.0 & (1u64 << 63) != 0
1132    }
1133
1134    /// Return all reserved bits.
1135    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/// L1 table.
1169#[derive(Debug)]
1170pub(super) struct L1Table {
1171    /// First cluster in the image file.
1172    cluster: Option<HostCluster>,
1173
1174    /// Table data.
1175    data: Box<[L1Entry]>,
1176
1177    /// log2 of the cluster size.
1178    cluster_bits: u32,
1179
1180    /// Whether this table has been modified since it was last written.
1181    modified: AtomicBool,
1182}
1183
1184impl L1Table {
1185    /// Create a clone that covers at least `at_least_index`.
1186    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    /// Check whether `index` is in bounds.
1209    pub fn in_bounds(&self, index: usize) -> bool {
1210        index < self.data.len()
1211    }
1212
1213    /// Enter the given L2 table into this L1 table.
1214    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    /// Maximum number of L1 table entries.
1237    ///
1238    /// Limit taken from QEMU; if QEMU rejects this, we can, too.
1239    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/// L2 table entry.
1297///
1298/// - Bit 0 - 61: Cluster descriptor
1299/// - Bit 62: 0 for standard clusters, 1 for compressed clusters
1300/// - Bit 63: 0 for clusters that are unused, compressed or require COW.  1 for standard clusters
1301///   whose refcount is exactly one.  This information is only accurate in L2 tables that are
1302///   reachable from the active L1 table.  With external data files, all guest clusters have an
1303///   implicit refcount of 1 (because of the fixed host = guest mapping for guest cluster offsets),
1304///   so this bit should be 1 for all allocated clusters.
1305///
1306/// Standard Cluster Descriptor:
1307/// - Bit 0: If set to 1, the cluster reads as all zeros. The host cluster offset can be used to
1308///   describe a preallocation, but it won’t be used for reading data from this cluster, nor is
1309///   data read from the backing file if the cluster is unallocated.  With version 2 or with
1310///   extended L2 entries (see the next section), this is always 0.
1311/// - Bit 1 – 8: Reserved (set to 0)
1312/// - Bit 9 – 55: Bits 9-55 of host cluster offset. Must be aligned to a cluster boundary. If the
1313///   offset is 0 and bit 63 is clear, the cluster is unallocated. The offset may only be 0 with
1314///   bit 63 set (indicating a host cluster offset of 0) when an external data file is used.
1315/// - Bit 56 - 61: Reserved (set to 0)
1316///
1317/// Compressed Cluster Descriptor (`x = 62 - (cluster_bits - 8)`):
1318/// - Bit 0 - x-1: Host cluster offset.  This is usually _not_ aligned to a cluster or sector
1319///   boundary!  If cluster_bits is small enough that this field includes bits beyond 55, those
1320///   upper bits must be set to 0.
1321/// - Bit x - 61: Number of additional 512-byte sectors used for the compressed data, beyond the
1322///   sector containing the offset in the previous field. Some of these sectors may reside in the
1323///   next contiguous host cluster.  Note that the compressed data does not necessarily occupy all
1324///   of the bytes in the final sector; rather, decompression stops when it has produced a cluster
1325///   of data.  Another compressed cluster may map to the tail of the final sector used by this
1326///   compressed cluster.
1327#[derive(Copy, Clone, Default, Debug)]
1328pub(super) struct L2Entry(u64);
1329
1330/// Internal actual type of L2 entries.
1331///
1332/// Using atomic allows flushing L2 tables from the cache while they are write-locked.
1333#[derive(Default, Debug)]
1334pub(super) struct AtomicL2Entry(AtomicU64);
1335
1336/// High-level representation of an L2 entry.
1337#[derive(Debug, Clone)]
1338pub(super) enum L2Mapping {
1339    /// Data is in the data file.
1340    DataFile {
1341        /// Cluster in the data file.
1342        host_cluster: HostCluster,
1343
1344        /// Whether the cluster has a refcount of exactly 1.
1345        copied: bool,
1346    },
1347
1348    /// Data is in the backing file.
1349    Backing {
1350        /// Guest cluster index.
1351        backing_offset: u64,
1352    },
1353
1354    /// Data is zero.
1355    Zero {
1356        /// Preallocated cluster in the data file, if any.
1357        host_cluster: Option<HostCluster>,
1358
1359        /// Whether the preallocated cluster has a refcount of exactly 1.
1360        copied: bool,
1361    },
1362
1363    /// Data is compressed.
1364    Compressed {
1365        /// Offset in the data file.
1366        host_offset: HostOffset,
1367
1368        /// Upper limit on the number of bytes that comprise the compressed data.
1369        length: u64,
1370    },
1371}
1372
1373impl L2Entry {
1374    /// Offset of the data cluster, if any.
1375    ///
1376    /// Assumes the L2 entry references a data cluster, not a compressed cluster.
1377    ///
1378    /// `external_data_file` must be true when using an external data file; in this case, offset 0
1379    /// is a valid offset, and can only be distinguished from “unallocated” by whether the COPIED
1380    /// flag is set or not (which it always is when using an external data file).
1381    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    /// Whether the cluster is compressed.
1391    pub fn is_compressed(&self) -> bool {
1392        self.0 & (1u64 << 62) != 0
1393    }
1394
1395    /// Whether the cluster is “copied”.
1396    ///
1397    /// `true` means is refcount is one, `false` means modifying it will require COW.
1398    pub fn is_copied(&self) -> bool {
1399        self.0 & (1u64 << 63) != 0
1400    }
1401
1402    /// Clear “copied” flag.
1403    #[must_use]
1404    pub fn without_copied(self) -> Self {
1405        L2Entry(self.0 & !(1u64 << 63))
1406    }
1407
1408    /// Whether the cluster is a zero cluster.
1409    ///
1410    /// Assumes the L2 entry references a data cluster, not a compressed cluster.
1411    pub fn is_zero(&self) -> bool {
1412        self.0 & (1u64 << 0) != 0
1413    }
1414
1415    /// Return all reserved bits.
1416    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    /// Return the full compressed cluster descriptor.
1425    pub fn compressed_descriptor(&self) -> u64 {
1426        self.0 & 0x3fff_ffff_ffff_ffffu64
1427    }
1428
1429    /// If this entry is compressed, return the start host offset and upper limit on the compressed
1430    /// number of bytes.
1431    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            // The first sector is not considered in `sectors`, so we add it and subtract the
1438            // number of bytes there that do not belong to this compressed cluster
1439            let length = (sectors + 1) * 512 - (offset & 511);
1440
1441            Some((HostOffset(offset), length))
1442        } else {
1443            None
1444        }
1445    }
1446
1447    /// If this entry is allocated, return the first host cluster and the number of clusters it
1448    /// references.
1449    ///
1450    /// `external_data_file` must be true when using an external data file.
1451    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            // Compressed clusters can cross host cluster boundaries, and thus occupy two clusters
1458            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    /// Return the high-level `L2Mapping` representation.
1471    ///
1472    /// `guest_cluster` is the guest cluster being accessed, `cluster_bits` is log2 of the cluster
1473    /// size.  `external_data_file` must be true when using an external data file.
1474    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    /// Create an L2 entry from its high-level `L2Mapping` representation.
1524    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                // The first sector is not considered, so we subtract the number of bytes in it
1562                // that belong to this compressed cluster from `length`:
1563                // ceil((length - (512 - (host_offset & 511))) / 512)
1564                // = (length + 511 - 512 + (host_offset & 511)) / 512
1565                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    /// Get the contained value.
1579    fn get(&self) -> L2Entry {
1580        L2Entry(self.0.load(Ordering::Relaxed))
1581    }
1582
1583    /// Exchange the contained value.
1584    ///
1585    /// # Safety
1586    /// Caller must ensure that:
1587    /// (1) No reader sees invalid intermediate states.
1588    /// (2) Updates are done atomically (do not depend on prior state of the L2 table), or there is
1589    ///     only one writer at a time.
1590    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    /// Check whether two mappings are consecutive.
1625    ///
1626    /// Given the `preceding` mapping, check whether `self` is consecutive to it, i.e. is the same
1627    /// kind of mapping, and the offsets are consecutive.
1628    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                // Cluster and copied do not matter; every read is continuous regardless (always
1668                // zero), and every write is, too (always allocate)
1669                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                // Not really true, but in practice it is.  Reads need to go through a special
1683                // function anyway, and every write will need COW anyway.
1684                matches!(
1685                    self,
1686                    L2Mapping::Compressed {
1687                        host_offset: _,
1688                        length: _,
1689                    }
1690                )
1691            }
1692        }
1693    }
1694}
1695
1696/// L2 table.
1697#[derive(Debug)]
1698pub(super) struct L2Table {
1699    /// Cluster of the L2 table.
1700    cluster: Option<HostCluster>,
1701
1702    /// Table data.
1703    data: Box<[AtomicL2Entry]>,
1704
1705    /// log2 of the cluster size.
1706    cluster_bits: u32,
1707
1708    /// Whether this image uses an external data file.
1709    external_data_file: bool,
1710
1711    /// Whether this table has been modified since it was last written.
1712    modified: AtomicBool,
1713
1714    /// Lock for creating `L2TableWriteGuard`.
1715    writer_lock: Mutex<()>,
1716}
1717
1718/// Write guard for an L2 table.
1719#[derive(Debug)]
1720pub(super) struct L2TableWriteGuard<'a> {
1721    /// Referenced L2 table.
1722    table: &'a L2Table,
1723
1724    /// Held guard mutex on that L2 table.
1725    _lock: MutexGuard<'a, ()>,
1726}
1727
1728#[maybe_async]
1729impl L2Table {
1730    /// Create a new zeroed L2 table.
1731    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    /// Look up a cluster mapping.
1746    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    /// Allow modifying this L2 table.
1752    ///
1753    /// Note that readers are allowed to exist while modifications are happening.
1754    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    /// Look up a cluster mapping.
1764    pub fn get_mapping(&self, lookup_cluster: GuestCluster) -> io::Result<L2Mapping> {
1765        self.table.get_mapping(lookup_cluster)
1766    }
1767
1768    /// Enter the given raw data cluster mapping into the L2 table.
1769    ///
1770    /// If the previous entry pointed to an allocated cluster, return the old allocation so its
1771    /// refcount can be decreased (offset of the first cluster and number of clusters -- compressed
1772    /// clusters can span across host cluster boundaries).
1773    ///
1774    /// If the allocation is reused, `None` is returned, so this function only returns `Some(_)` if
1775    /// some cluster is indeed leaked.
1776    #[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        // Safe: We set a full valid mapping, and there is only one writer (thanks to
1790        // `L2TableWriteGuard`).
1791        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    /// Make the given index a zero mapping.
1807    ///
1808    /// If `keep_allocation` is true, keep the zero cluster pre-allocated if there is a
1809    /// pre-existing single-cluster allocation (i.e. data cluster or pre-allocated zero cluster).
1810    /// Otherwise, the existing mapping is discarded.
1811    ///
1812    /// If a previous mapping is discarded, return the old allocation so its refcount can be
1813    /// decreased (offset of the first cluster and number of clusters -- compressed clusters can
1814    /// span across host cluster boundaries).
1815    #[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), // only used for backing, which we ignore
1824                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        // Safe: We set a full valid mapping, and there is only one writer (thanks to
1864        // `L2TableWriteGuard`).
1865        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    /// Remove the given mapping, leaving it empty.
1877    ///
1878    /// If a previous mapping is discarded, return the old allocation so its refcount can be
1879    /// decreased (offset of the first cluster and number of clusters -- compressed clusters can
1880    /// span across host cluster boundaries).
1881    #[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        // Safe: We set a full valid mapping, and there is only one writer (thanks to
1886        // `L2TableWriteGuard`).
1887        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            // None of these can be `copied`
1967            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/// Refcount table entry.
1993#[derive(Copy, Clone, Default, Debug)]
1994pub(super) struct RefTableEntry(u64);
1995
1996impl RefTableEntry {
1997    /// Offset of the referenced refblock, if any.
1998    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    /// Return all reserved bits.
2008    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/// Refcount table.
2044#[derive(Debug)]
2045pub(super) struct RefTable {
2046    /// First cluster in the image file.
2047    cluster: Option<HostCluster>,
2048
2049    /// Table data.
2050    data: Box<[RefTableEntry]>,
2051
2052    /// log2 of the cluster size.
2053    cluster_bits: u32,
2054
2055    /// Whether this table has been modified since it was last written.
2056    modified: AtomicBool,
2057}
2058
2059impl RefTable {
2060    /// Create a clone that covers at least `at_least_index`.
2061    ///
2062    /// Also ensure that beyond `at_least_index`, there are enough entries to self-describe the new
2063    /// refcount table (so that it can actually be allocated).
2064    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        // There surely is an optimal O(1) solution, but probably would look less clear, and this
2069        // is not a hot path.
2070        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            // Full number of clusters needed to both the new reftable *and* the `extra_rbs`
2076            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    /// Check whether `index` is in bounds.
2102    pub fn in_bounds(&self, index: usize) -> bool {
2103        index < self.data.len()
2104    }
2105
2106    /// Enter the given refcount block into this refcount table.
2107    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    /// Maximum number of refcount table entries.
2130    ///
2131    /// Not in QEMU, but makes sense to limit to the same as the L1 table.  Note that refcount
2132    /// blocks usually cover more clusters than an L2 table, so this generally allows larger image
2133    /// files than would be necessary for the maximum guest disk size determined by the maximum
2134    /// number of L1 entries.
2135    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
2192/// Refcount block.
2193pub(super) struct RefBlock {
2194    /// Cluster in the image file.
2195    cluster: Option<HostCluster>,
2196
2197    /// Raw table data (big endian).
2198    raw_data: IoBuffer,
2199
2200    /// log2 of the refcount bits.
2201    refcount_order: u32,
2202
2203    /// log2 of the cluster size.
2204    cluster_bits: u32,
2205
2206    /// Whether this block has been modified since it was last written.
2207    modified: AtomicBool,
2208
2209    /// Lock for creating `RefBlockWriteGuard`.
2210    writer_lock: Mutex<()>,
2211}
2212
2213/// Write guard for a refblock.
2214pub(super) struct RefBlockWriteGuard<'a> {
2215    /// Referenced refblock.
2216    rb: &'a RefBlock,
2217
2218    /// Held guard mutex on that refblock.
2219    _lock: MutexGuard<'a, ()>,
2220}
2221
2222#[maybe_async]
2223impl RefBlock {
2224    /// Create a new zeroed refcount block.
2225    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    /// Load a refcount block from disk.
2240    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    /// Copy data from `self.raw_data` using the `get` function.
2274    ///
2275    /// `mem_align` is the minimum memory alignment for the returned buffer.
2276    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        // Check that `T` is the right type
2282        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        // Safe because this is the right access type
2288        let raw_in = unsafe { self.raw_data.as_ref().into_typed_slice::<T>() };
2289        // Safe because we have just allocated this, and it fits the alignment
2290        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    /// Write a refcount block to disk.
2300    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            // refcount_bits == 1, 2, 4, 8
2309            0..=3 => self.copy_raw_data::<u8, _>(image.mem_align(), |ptr| {
2310                // Safe because we only read
2311                unsafe { AtomicU8::from_ptr(ptr as *mut u8) }.load(Ordering::Relaxed)
2312            })?,
2313
2314            // refcount_bits == 16
2315            4 => self.copy_raw_data::<u16, _>(image.mem_align(), |ptr| {
2316                // Safe because we only read
2317                unsafe { AtomicU16::from_ptr(ptr as *mut u16) }.load(Ordering::Relaxed)
2318            })?,
2319
2320            // refcount_bits == 32
2321            5 => self.copy_raw_data::<u32, _>(image.mem_align(), |ptr| {
2322                // Safe because we only read
2323                unsafe { AtomicU32::from_ptr(ptr as *mut u32) }.load(Ordering::Relaxed)
2324            })?,
2325
2326            // refcount_bits == 64
2327            6 => self.copy_raw_data::<u64, _>(image.mem_align(), |ptr| {
2328                // Safe because we only read
2329                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    /// Get the block’s cluster in the image file.
2344    pub fn get_cluster(&self) -> Option<HostCluster> {
2345        self.cluster
2346    }
2347
2348    /// Get the block’s offset in the image file.
2349    pub fn get_offset(&self) -> Option<HostOffset> {
2350        self.cluster.map(|index| index.offset(self.cluster_bits))
2351    }
2352
2353    /// Change the block’s cluster in the image file (for writing).
2354    pub fn set_cluster(&mut self, cluster: HostCluster) {
2355        self.cluster = Some(cluster);
2356        self.set_modified();
2357    }
2358
2359    /// Calculate sub-byte refcount access parameters.
2360    ///
2361    /// For a given refcount index, return its:
2362    /// - byte index,
2363    /// - access mask,
2364    /// - in-byte shift.
2365    fn sub_byte_refcount_access(&self, index: usize) -> (usize, u8, usize) {
2366        let order = self.refcount_order;
2367        debug_assert!(order < 3);
2368
2369        // Note that `order` is in bits, i.e. `1 << order` is the number of bits.  `index` is in
2370        // units of refcounts, so `index << order` is the bit index, and `index << (order - 3)` is
2371        // then the byte index, which is equal to `index >> (3 - order)`.
2372        let byte_index = index >> (3 - order);
2373        // `1 << order` is the bits per refcount (bprc), so `(1 << bprc) - 1` is the mask for one
2374        // refcount (its maximum value).
2375        let mask = (1 << (1 << order)) - 1;
2376        // `index` is in units of refcounts, so `index << order` is the bit index.  `% 8`, we get
2377        // the base index inside of a byte.
2378        let shift = (index << order) % 8;
2379
2380        (byte_index, mask, shift)
2381    }
2382
2383    /// Get the given cluster’s refcount.
2384    pub fn get(&self, index: usize) -> u64 {
2385        match self.refcount_order {
2386            // refcount_bits == 1, 2, 4
2387            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            // refcount_bits == 8
2396            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            // refcount_bits == 16
2404            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            // refcount_bits == 32
2413            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            // refcount_bits == 64
2422            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    /// Allow modifying this refcount block.
2435    ///
2436    /// Note that readers are allowed to exist while modifications are happening.
2437    pub async fn lock_write(&self) -> RefBlockWriteGuard<'_> {
2438        RefBlockWriteGuard {
2439            rb: self,
2440            _lock: self.writer_lock.lock().await,
2441        }
2442    }
2443
2444    /// Check whether this block has been modified since it was last written.
2445    pub fn is_modified(&self) -> bool {
2446        self.modified.load(Ordering::Relaxed)
2447    }
2448
2449    /// Clear the modified flag.
2450    pub fn clear_modified(&self) {
2451        self.modified.store(false, Ordering::Relaxed);
2452    }
2453
2454    /// Set the modified flag.
2455    pub fn set_modified(&self) {
2456        self.modified.store(true, Ordering::Relaxed);
2457    }
2458
2459    /// Check whether the given cluster’s refcount is 0.
2460    pub fn is_zero(&self, index: usize) -> bool {
2461        self.get(index) == 0
2462    }
2463}
2464
2465impl RefBlockWriteGuard<'_> {
2466    /// # Safety
2467    /// Caller must ensure there are no concurrent writers.
2468    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        // load + store is OK without concurrent writers
2477        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    /// # Safety
2513    /// Caller must ensure there are no concurrent writers.
2514    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        // load + store is OK without concurrent writers
2525        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    /// Modify the given cluster’s refcount.
2548    fn modify(&mut self, index: usize, change: i64) -> io::Result<u64> {
2549        let result = match self.rb.refcount_order {
2550            // refcount_bits == 1, 2, 4
2551            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                // Safe: `RefBlockWriteGuard` ensures there are no concurrent writers.
2557                unsafe { Self::fetch_update_bitset(atomic, change, mask, shift) }
2558            }
2559
2560            // refcount_bits == 8
2561            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                // Safe: `RefBlockWriteGuard` ensures there are no concurrent writers.
2566                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            // refcount_bits == 16
2580            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            // refcount_bits == 32
2599            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            // refcount_bits == 64
2618            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    /// Increment the given cluster’s refcount.
2645    ///
2646    /// Returns the old value.
2647    pub fn increment(&mut self, index: usize) -> io::Result<u64> {
2648        self.modify(index, 1)
2649    }
2650
2651    /// Decrement the given cluster’s refcount.
2652    ///
2653    /// Returns the old value.
2654    pub fn decrement(&mut self, index: usize) -> io::Result<u64> {
2655        self.modify(index, -1)
2656    }
2657
2658    /// Check whether the given cluster’s refcount is 0.
2659    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
2674/// Generic trait for qcow2 table entries (L1, L2, refcount table).
2675pub trait TableEntry
2676where
2677    Self: Sized,
2678{
2679    /// Load the given raw value, checking it for validity.
2680    fn try_from_plain(value: u64, header: &Header) -> io::Result<Self>;
2681
2682    /// Return the contained raw value.
2683    fn to_plain(&self) -> u64;
2684}
2685
2686/// Generic trait for qcow2 metadata tables (L1, L2, refcount table).
2687#[maybe_async(AFIT)]
2688pub trait Table: Sized {
2689    /// Internal type for each table entry.
2690    type InternalEntry: TableEntry;
2691    /// Externally visible type for each table entry.
2692    type Entry: Copy;
2693    /// User-readable struct name.
2694    const NAME: &'static str;
2695    /// Maximum allowable number of entries.
2696    const MAX_ENTRIES: usize;
2697
2698    /// Create a new table with the given contents
2699    fn from_data(data: Box<[Self::InternalEntry]>, header: &Header) -> Self;
2700
2701    /// Number of entries.
2702    fn entries(&self) -> usize;
2703    /// Get the given entry (as reference).
2704    fn get_ref(&self, index: usize) -> Option<&Self::InternalEntry>;
2705    /// Get the given entry (copied).
2706    fn get(&self, index: usize) -> Self::Entry;
2707    /// Get this table’s (first) cluster in the image file.
2708    fn get_cluster(&self) -> Option<HostCluster>;
2709    /// Get this table’s offset in the image file.
2710    fn get_offset(&self) -> Option<HostOffset>;
2711    /// Set this table’s (first) cluster in the image file (for writing).
2712    fn set_cluster(&mut self, cluster: HostCluster);
2713    /// Remove the table’s association with any cluster in the image file.
2714    fn unset_cluster(&mut self);
2715
2716    /// Return log2 of the cluster size.
2717    ///
2718    /// All tables store this anyway.
2719    fn cluster_bits(&self) -> u32;
2720
2721    /// Check whether this table has been modified since it was last written.
2722    fn is_modified(&self) -> bool;
2723    /// Clear the modified flag.
2724    fn clear_modified(&self);
2725    /// Set the modified flag.
2726    fn set_modified(&self);
2727
2728    /// Table size in bytes.
2729    fn byte_size(&self) -> usize {
2730        self.entries() * size_of::<u64>()
2731    }
2732
2733    /// Number of clusters used by this table.
2734    fn cluster_count(&self) -> ClusterCount {
2735        ClusterCount::from_byte_size(self.byte_size() as u64, self.cluster_bits())
2736    }
2737
2738    /// Load a table from the image file.
2739    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        // Safe because `u64` is a plain type, and the alignment fits
2762        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    /// Write a table to the image file.
2779    ///
2780    /// Callers must ensure the table is copied, i.e. its refcount is 1.
2781    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        // Safe because we have just allocated this, and it fits the alignment
2801        let raw_table = unsafe { buffer.as_mut().into_typed_slice::<u64>() };
2802        for (i, be_value) in raw_table.iter_mut().enumerate() {
2803            // 0 always works, that’s by design.
2804            *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    /// Write at least the given single (modified) entry to the image file.
2816    ///
2817    /// Potentially writes more of the table, if alignment requirements ask for that.
2818    async fn write_entry<S: Storage>(&self, image: &S, index: usize) -> io::Result<()> {
2819        // This alignment calculation code implicitly assumes that the cluster size is aligned to
2820        // the storage’s request/memory alignment, but that is often fair.  If that is not the
2821        // case, there is not much we can do anyway.
2822        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        // Safe because we have just allocated this, and it fits the alignment
2849        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            // 0 always works, that’s by design.
2854            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
2867/// Check whether the given table offset/size is valid.
2868///
2869/// Also works for refcount blocks (with cheating, because their entry size can be less than a
2870/// byte), which is why it is outside of [`Table`].
2871fn 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
2904/// Return a byte buffer for `val`.
2905fn 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
2911/// Decode `T` from the given byte buffer.
2912fn decode_binary<T: OnDiskStruct>(slice: &[u8]) -> io::Result<T> {
2913    T::load_from(slice)
2914}