Skip to main content

imago/qcow2/
metadata.rs

1//! Functionality for working with qcow2 metadata.
2
3use super::allocation::{ClustersToFreeEntry, PendingClusters};
4use super::compressed::CompressedClusterReadGuard;
5use super::types::*;
6use crate::io_buffers::IoBuffer;
7use crate::macros::numerical_enum;
8use crate::macros::on_disk_struct::{on_disk_struct, OnDiskStruct};
9use crate::misc_helpers::{invalid_data, DropGuard};
10use crate::sync_primitives::{Mutex, MutexGuard, OwnedRwLock, OwnedRwLockWriteGuard};
11use crate::{Storage, StorageExt};
12use maybe_async::maybe_async;
13use std::collections::HashMap;
14use std::mem::size_of;
15use std::num::{NonZero, TryFromIntError};
16use std::sync::atomic::{AtomicBool, AtomicU16, AtomicU32, AtomicU64, AtomicU8, Ordering};
17use std::sync::Arc;
18use std::{cmp, io};
19use tracing::error;
20
21/// Qcow header magic ("QFI\xfb").
22pub(super) const MAGIC: u32 = 0x51_46_49_fb;
23
24/// Maximum file length.
25const MAX_FILE_LENGTH: u64 = 0x0100_0000_0000_0000u64;
26
27/// Maximum permissible host offset.
28pub(super) const MAX_OFFSET: HostOffset = HostOffset(MAX_FILE_LENGTH - 512);
29
30/// Minimum cluster size.
31///
32/// Defined by the specification.
33pub(super) const MIN_CLUSTER_SIZE: usize = 512;
34
35/// Maximum cluster size.
36///
37/// This is QEMU’s limit, so we can apply it, too.
38pub(super) const MAX_CLUSTER_SIZE: usize = 2 * 1024 * 1024;
39
40/// Minimum number of bits per refcount entry.
41pub(super) const MIN_REFCOUNT_WIDTH: usize = 1;
42
43/// Maximum number of bits per refcount entry.
44pub(super) const MAX_REFCOUNT_WIDTH: usize = 64;
45
46on_disk_struct! {
47/// Qcow2 v2 header.
48struct V2Header/BE, no_gaps {
49    /// Qcow magic string ("QFI\xfb").
50    magic: u32[0],
51
52    /// Version number (valid values are 2 and 3).
53    version: u32[4],
54
55    /// Offset into the image file at which the backing file name is stored (NB: The string is not
56    /// null terminated).  0 if the image doesn’t have a backing file.
57    ///
58    /// Note: backing files are incompatible with raw external data files (auto-clear feature bit
59    /// 1).
60    backing_file_offset: u64[8],
61
62    /// Length of the backing file name in bytes.  Must not be longer than 1023 bytes.  Undefined
63    /// if the image doesn’t have a backing file.
64    backing_file_size: u32[16],
65
66    /// Number of bits that are used for addressing an offset within a cluster (`1 << cluster_bits`
67    /// is the cluster size).  Must not be less than 9 (i.e. 512 byte clusters).
68    ///
69    /// Note: qemu as of today has an implementation limit of 2 MB as the maximum cluster size and
70    /// won’t be able to open images with larger cluster sizes.
71    ///
72    /// Note: if the image has Extended L2 Entries then `cluster_bits` must be at least 14 (i.e.
73    /// 16384 byte clusters).
74    cluster_bits: u32[20],
75
76    /// Virtual disk size in bytes.
77    ///
78    /// Note: qemu has an implementation limit of 32 MB as the maximum L1 table size.  With a 2 MB
79    /// cluster size, it is unable to populate a virtual cluster beyond 2 EB (61 bits); with a 512
80    /// byte cluster size, it is unable to populate a virtual size larger than 128 GB (37 bits).
81    /// Meanwhile, L1/L2 table layouts limit an image to no more than 64 PB (56 bits) of populated
82    /// clusters, and an image may hit other limits first (such as a file system’s maximum size).
83    size: AtomicU64[24],
84
85    /// Encryption method:
86    ///
87    /// 0. no encryption
88    /// 1. AES encryption
89    /// 2. LUKS encryption
90    crypt_method: u32[32],
91
92    /// Number of entries in the active L1 table.
93    l1_size: AtomicU32[36],
94
95    /// Offset into the image file at which the active L1 table starts.  Must be aligned to a
96    /// cluster boundary.
97    l1_table_offset: AtomicU64[40],
98
99    /// Offset into the image file at which the refcount table starts.  Must be aligned to a
100    /// cluster boundary.
101    refcount_table_offset: AtomicU64[48],
102
103    /// Number of clusters that the refcount table occupies.
104    refcount_table_clusters: AtomicU32[56],
105
106    /// Number of snapshots contained in the image.
107    nb_snapshots: u32[60],
108
109    /// Offset into the image file at which the snapshot table starts.  Must be aligned to a
110    /// cluster boundary.
111    snapshots_offset: u64[64],
112}
113}
114
115on_disk_struct! {
116/// Qcow2 v3 header.
117struct V3HeaderBase/BE, no_gaps {
118    /// Bitmask of incompatible features.  An implementation must fail to open an image if an
119    /// unknown bit is set.
120    ///
121    /// 0. Dirty bit.  If this bit is set then refcounts may be inconsistent, make sure to scan
122    ///    L1/L2 tables to repair refcounts before accessing the image.
123    /// 1. Corrupt bit.  If this bit is set then any data structure may be corrupt and the image
124    ///    must not be written to (unless for regaining consistency).
125    /// 2. External data file bit.  If this bit is set, an external data file is used.  Guest
126    ///    clusters are then stored in the external data file.  For such images, clusters in the
127    ///    external data file are not refcounted.  The offset field in the Standard Cluster
128    ///    Descriptor must match the guest offset and neither compressed clusters nor internal
129    ///    snapshots are supported.  An External Data File Name header extension may be present if
130    ///    this bit is set.
131    /// 3. Compression type bit.  If this bit is set, a non-default compression is used for
132    ///    compressed clusters.  The compression_type field must be present and not zero.
133    /// 4. Extended L2 Entries.  If this bit is set then L2 table entries use an extended format
134    ///    that allows subcluster-based allocation.  See the Extended L2 Entries section for more
135    ///    details.
136    ///
137    /// Bits 5-63 are reserved (set to 0).
138    incompatible_features: u64[0],
139
140    /// Bitmask of compatible features.  An implementation can safely ignore any unknown bits that
141    /// are set.
142    ///
143    /// 0. Lazy refcounts bit.  If this bit is set then lazy refcount updates can be used.  This
144    ///    means marking the image file dirty and postponing refcount metadata updates.
145    ///
146    /// Bits 1-63 are reserved (set to 0).
147    compatible_features: u64[8],
148
149    /// Bitmask of auto-clear features.  An implementation may only write to an image with unknown
150    /// auto-clear features if it clears the respective bits from this field first.
151    ///
152    /// 0. Bitmaps extension bit.  This bit indicates consistency for the bitmaps extension data.
153    ///    It is an error if this bit is set without the bitmaps extension present.  If the bitmaps
154    ///    extension is present but this bit is unset, the bitmaps extension data must be
155    ///    considered inconsistent.
156    /// 1. Raw external data bit.  If this bit is set, the external data file can be read as a
157    ///    consistent standalone raw image without looking at the qcow2 metadata.  Setting this bit
158    ///    has a performance impact for some operations on the image (e.g. writing zeros requires
159    ///    writing to the data file instead of only setting the zero flag in the L2 table entry)
160    ///    and conflicts with backing files.  This bit may only be set if the External Data File
161    ///    bit (incompatible feature bit 1) is also set.
162    ///
163    /// Bits 2-63 are reserved (set to 0).
164    autoclear_features: u64[16],
165
166    /// Describes the width of a reference count block entry (width in bits: `refcount_bits = 1 <<
167    /// refcount_order`).  For version 2 images, the order is always assumed to be 4 (i.e.
168    /// `refcount_bits = 16`).  This value may not exceed 6 (i.e. `refcount_bits = 64`).
169    refcount_order: u32[24],
170
171    /// Length of the header structure in bytes.  For version 2 images, the length is always
172    /// assumed to be 72 bytes.  For version 3 it’s at least 104 bytes and must be a multiple of 8.
173    header_length: u32[28],
174}
175}
176
177impl Default for V3HeaderBase {
178    fn default() -> Self {
179        V3HeaderBase {
180            incompatible_features: 0,
181            compatible_features: 0,
182            autoclear_features: 0,
183            refcount_order: 4,
184            header_length: (V2Header::ON_DISK_SIZE + V3HeaderBase::ON_DISK_SIZE) as u32,
185        }
186    }
187}
188
189numerical_enum! {
190    /// Incompatible feature bits.
191    pub(super) enum IncompatibleFeatures as u64 {
192        Dirty = 1 << 0,
193        Corrupt = 1 << 1,
194        ExternalDataFile = 1 << 2,
195        CompressionType = 1 << 3,
196        ExtendedL2Entries = 1 << 4,
197    }
198}
199
200impl From<IncompatibleFeatures> for (FeatureType, u8) {
201    /// Get this feature’s feature name table key.
202    fn from(feat: IncompatibleFeatures) -> (FeatureType, u8) {
203        assert!((feat as u64).is_power_of_two());
204        (
205            FeatureType::Incompatible,
206            (feat as u64).trailing_zeros() as u8,
207        )
208    }
209}
210
211numerical_enum! {
212    /// Compatible feature bits.
213    pub(super) enum CompatibleFeatures as u64 {
214        LazyRefcounts = 1 << 0,
215    }
216}
217
218impl From<CompatibleFeatures> for (FeatureType, u8) {
219    /// Get this feature’s feature name table key.
220    fn from(feat: CompatibleFeatures) -> (FeatureType, u8) {
221        assert!((feat as u64).is_power_of_two());
222        (
223            FeatureType::Compatible,
224            (feat as u64).trailing_zeros() as u8,
225        )
226    }
227}
228
229numerical_enum! {
230    /// Autoclear feature bits.
231    pub(super) enum AutoclearFeatures as u64 {
232        Bitmaps = 1 << 0,
233        RawExternalData = 1 << 1,
234    }
235}
236
237impl From<AutoclearFeatures> for (FeatureType, u8) {
238    /// Get this feature’s feature name table key.
239    fn from(feat: AutoclearFeatures) -> (FeatureType, u8) {
240        assert!((feat as u64).is_power_of_two());
241        (FeatureType::Autoclear, (feat as u64).trailing_zeros() as u8)
242    }
243}
244
245numerical_enum! {
246    /// Extension type IDs.
247    pub(super) enum HeaderExtensionType as u32 {
248        /// End of extension list.
249        End = 0,
250
251        /// Backing file format string.
252        BackingFileFormat = 0xe2792aca,
253
254        /// Map of feature bits to human-readable names.
255        FeatureNameTable = 0x6803f857,
256
257        /// External data file filename string.
258        ExternalDataFileName = 0x44415441,
259    }
260}
261
262on_disk_struct! {
263/// Header for a header extension.
264#[derive(Default)]
265struct HeaderExtensionHeader/BE, no_gaps {
266    /// Type code of the header extension.
267    extension_type: u32[0],
268
269    /// Data length.
270    length: u32[4],
271}
272}
273
274numerical_enum! {
275    /// Feature type ID for the feature name table.
276    #[derive(Hash)]
277    pub(super) enum FeatureType as u8 {
278        Incompatible = 0,
279        Compatible = 1,
280        Autoclear = 2,
281    }
282}
283
284/// Header extensions (high-level representation).
285#[derive(Debug, Clone, Eq, PartialEq)]
286pub(super) enum HeaderExtension {
287    /// Backing file format string.
288    BackingFileFormat(String),
289
290    /// Map of feature bits to human-readable names.
291    FeatureNameTable(HashMap<(FeatureType, u8), String>),
292
293    /// External data file filename string.
294    ExternalDataFileName(String),
295
296    /// Unknown extension.
297    Unknown {
298        /// Type.
299        extension_type: u32,
300        /// Data (as read).
301        data: Vec<u8>,
302    },
303}
304
305/// Integrated header representation.
306pub(super) struct Header {
307    /// v2 part of the header.
308    v2: V2Header,
309
310    /// Base v3 part of the header.
311    v3: V3HeaderBase,
312
313    /// Unrecognized header fields.
314    unknown_header_fields: Vec<u8>,
315
316    /// Backing filename string.
317    backing_filename: Option<String>,
318
319    /// Extensions.
320    extensions: Vec<HeaderExtension>,
321
322    /// Whether an external data file is required.
323    external_data_file: bool,
324}
325
326#[maybe_async]
327impl Header {
328    /// Load the qcow2 header from disk.
329    ///
330    /// If `writable` is false, do not perform any modifications (e.g. clearing auto-clear bits).
331    pub async fn load<S: Storage>(image: &S, writable: bool) -> io::Result<Self> {
332        // TODO: More sanity checks.
333        let mut header_buf = vec![0u8; V2Header::ON_DISK_SIZE];
334        image.read(header_buf.as_mut_slice(), 0).await?;
335
336        let header: V2Header = decode_binary(&header_buf)?;
337        if header.magic != MAGIC {
338            return Err(invalid_data("Not a qcow2 file"));
339        }
340
341        let v3header_base = if header.version == 2 {
342            V3HeaderBase::default()
343        } else if header.version == 3 {
344            let mut header_buf = vec![0u8; V3HeaderBase::ON_DISK_SIZE];
345            image
346                .read(header_buf.as_mut_slice(), V2Header::ON_DISK_SIZE as u64)
347                .await?;
348            decode_binary(&header_buf)?
349        } else {
350            return Err(invalid_data(format!(
351                "qcow2 v{} is not supported",
352                header.version
353            )));
354        };
355
356        let cluster_size = 1usize.checked_shl(header.cluster_bits).ok_or_else(|| {
357            invalid_data(format!("Invalid cluster size: 2^{}", header.cluster_bits))
358        })?;
359        if !(MIN_CLUSTER_SIZE..=MAX_CLUSTER_SIZE).contains(&cluster_size) {
360            return Err(invalid_data(format!(
361                "Invalid cluster size: {cluster_size}; must be between {MIN_CLUSTER_SIZE} and {MAX_CLUSTER_SIZE}",
362            )));
363        }
364
365        let min_header_size = V2Header::ON_DISK_SIZE + V3HeaderBase::ON_DISK_SIZE;
366        if (v3header_base.header_length as usize) < min_header_size {
367            return Err(invalid_data(format!(
368                "qcow2 header too short: {} < {min_header_size}",
369                v3header_base.header_length,
370            )));
371        } else if (v3header_base.header_length as usize) > cluster_size {
372            return Err(invalid_data(format!(
373                "qcow2 header too big: {} > {cluster_size}",
374                v3header_base.header_length,
375            )));
376        }
377
378        let unknown_header_fields = if header.version == 2 {
379            Vec::new()
380        } else {
381            let mut unknown_header_fields =
382                vec![0u8; v3header_base.header_length as usize - min_header_size];
383            image
384                .read(&mut unknown_header_fields, min_header_size as u64)
385                .await?;
386            unknown_header_fields
387        };
388
389        let l1_offset = HostOffset(header.l1_table_offset.load(Ordering::Relaxed));
390        l1_offset
391            .checked_cluster(header.cluster_bits)
392            .ok_or_else(|| invalid_data(format!("Unaligned L1 table: {l1_offset}")))?;
393
394        let rt_offset = HostOffset(header.refcount_table_offset.load(Ordering::Relaxed));
395        rt_offset
396            .checked_cluster(header.cluster_bits)
397            .ok_or_else(|| invalid_data(format!("Unaligned refcount table: {rt_offset}")))?;
398
399        let rc_width = 1usize
400            .checked_shl(v3header_base.refcount_order)
401            .ok_or_else(|| {
402                invalid_data(format!(
403                    "Invalid refcount width: 2^{}",
404                    v3header_base.refcount_order
405                ))
406            })?;
407        if !(MIN_REFCOUNT_WIDTH..=MAX_REFCOUNT_WIDTH).contains(&rc_width) {
408            return Err(invalid_data(format!(
409                "Invalid refcount width: {rc_width}; must be between {MIN_REFCOUNT_WIDTH} and {MAX_REFCOUNT_WIDTH}",
410            )));
411        }
412
413        let backing_filename = if header.backing_file_offset != 0 {
414            let (offset, length) = (header.backing_file_offset, header.backing_file_size);
415            if length > 1023 {
416                return Err(invalid_data(format!(
417                    "Backing file name is too long ({length}, must not exceed 1023)"
418                )));
419            }
420
421            let end = offset.checked_add(length as u64).ok_or(invalid_data(
422                "Backing file name offset is invalid (too high)",
423            ))?;
424            if end >= cluster_size as u64 {
425                return Err(invalid_data(
426                    "Backing file name offset is invalid (beyond first cluster)",
427                ));
428            }
429
430            let mut backing_buf = vec![0; length as usize];
431            image.read(&mut backing_buf, offset).await?;
432
433            Some(
434                String::from_utf8(backing_buf)
435                    .map_err(|err| invalid_data(format!("Backing file name is invalid: {err}")))?,
436            )
437        } else {
438            None
439        };
440
441        let extensions = if header.version == 2 {
442            Vec::new()
443        } else {
444            let mut ext_offset: u64 = v3header_base.header_length as u64;
445            let mut extensions = Vec::<HeaderExtension>::new();
446            loop {
447                if ext_offset + HeaderExtensionHeader::ON_DISK_SIZE as u64 > cluster_size as u64 {
448                    return Err(invalid_data("Header extensions exceed the first cluster"));
449                }
450
451                let mut ext_hdr_buf = vec![0; HeaderExtensionHeader::ON_DISK_SIZE];
452                image.read(&mut ext_hdr_buf, ext_offset).await?;
453
454                ext_offset += HeaderExtensionHeader::ON_DISK_SIZE as u64;
455
456                let ext_hdr: HeaderExtensionHeader = decode_binary(&ext_hdr_buf)?;
457                let ext_end = ext_offset
458                    .checked_add(ext_hdr.length as u64)
459                    .ok_or_else(|| invalid_data("Header size overflow"))?;
460                if ext_end > cluster_size as u64 {
461                    return Err(invalid_data("Header extensions exceed the first cluster"));
462                }
463
464                let mut ext_data = vec![0; ext_hdr.length as usize];
465                image.read(&mut ext_data, ext_offset).await?;
466
467                ext_offset += (ext_hdr.length as u64).next_multiple_of(8);
468
469                let Some(extension) =
470                    HeaderExtension::deserialize(ext_hdr.extension_type, ext_data)?
471                else {
472                    break;
473                };
474
475                extensions.push(extension);
476            }
477            extensions
478        };
479
480        // Check for header extension conflicts
481        let backing_fmt = extensions
482            .iter()
483            .find(|ext| matches!(ext, HeaderExtension::BackingFileFormat(_)));
484        if let Some(backing_fmt) = backing_fmt {
485            let conflicting = extensions.iter().find(|ext| {
486                matches!(ext, HeaderExtension::BackingFileFormat(_)) && ext != &backing_fmt
487            });
488            if let Some(conflicting) = conflicting {
489                return Err(io::Error::other(format!(
490                    "Found conflicting backing file formats: {backing_fmt:?} != {conflicting:?}",
491                )));
492            }
493        }
494        let ext_data_file = extensions
495            .iter()
496            .find(|ext| matches!(ext, HeaderExtension::ExternalDataFileName(_)));
497        if let Some(ext_data_file) = ext_data_file {
498            let conflicting = extensions.iter().find(|ext| {
499                matches!(ext, HeaderExtension::ExternalDataFileName(_)) && ext != &ext_data_file
500            });
501            if let Some(conflicting) = conflicting {
502                return Err(io::Error::other(format!(
503                    "Found conflicting external data file names: {ext_data_file:?} != {conflicting:?}",
504                )));
505            }
506        }
507
508        let mut incompatible_features = v3header_base.incompatible_features;
509        let autoclear_features = v3header_base.autoclear_features;
510
511        let external_data_file =
512            incompatible_features & IncompatibleFeatures::ExternalDataFile as u64 != 0;
513        incompatible_features &= !(IncompatibleFeatures::ExternalDataFile as u64);
514
515        let mut header = Header {
516            v2: header,
517            v3: v3header_base,
518            unknown_header_fields,
519            backing_filename,
520            extensions,
521            external_data_file,
522        };
523
524        // No need to clear autoclear features for read-only images
525        if autoclear_features != 0 && writable {
526            header.v3.autoclear_features = 0;
527            header.write(image).await?;
528        }
529
530        if incompatible_features != 0 {
531            let feats = (0..64)
532                .filter(|bit| header.v3.incompatible_features & (1u64 << bit) != 0)
533                .map(|bit| {
534                    if let Some(name) = header.feature_name(FeatureType::Incompatible, bit) {
535                        format!("{bit} ({name})")
536                    } else {
537                        format!("{bit}")
538                    }
539                })
540                .collect::<Vec<String>>();
541
542            return Err(invalid_data(format!(
543                "Unrecognized incompatible feature(s) {}",
544                feats.join(", ")
545            )));
546        }
547
548        Ok(header)
549    }
550
551    /// Write the qcow2 header to disk.
552    pub async fn write<S: Storage>(&mut self, image: &S) -> io::Result<()> {
553        let header_len = if self.v2.version > 2 {
554            let len =
555                self.v2.on_disk_size() + self.v3.on_disk_size() + self.unknown_header_fields.len();
556            let len = len.next_multiple_of(8);
557            self.v3.header_length = len as u32;
558            len
559        } else {
560            V2Header::ON_DISK_SIZE
561        };
562
563        // If the header gets too long, try to remove the feature name table to make it small
564        // enough
565        let mut header_exts;
566        let mut backing_file_ofs;
567        loop {
568            header_exts = self.serialize_extensions()?;
569
570            backing_file_ofs = header_len
571                .checked_add(header_exts.len())
572                .ok_or_else(|| invalid_data("Header size overflow"))?;
573            let backing_file_len = self
574                .backing_filename
575                .as_ref()
576                .map(|n| n.len()) // length in bytes
577                .unwrap_or(0);
578            let header_end = backing_file_ofs
579                .checked_add(backing_file_len)
580                .ok_or_else(|| invalid_data("Header size overflow"))?;
581
582            if header_end <= self.cluster_size() {
583                break;
584            }
585
586            if !self
587                .extensions
588                .iter()
589                .any(|e| e.extension_type() == HeaderExtensionType::FeatureNameTable as u32)
590            {
591                return Err(io::Error::other(format!(
592                    "Header would be too long ({header_end} > {})",
593                    self.cluster_size()
594                )));
595            }
596            self.extensions
597                .retain(|e| e.extension_type() != HeaderExtensionType::FeatureNameTable as u32);
598        }
599
600        if let Some(backing) = self.backing_filename.as_ref() {
601            self.v2.backing_file_offset = backing_file_ofs as u64;
602            self.v2.backing_file_size = backing.len() as u32; // length in bytes
603        } else {
604            self.v2.backing_file_offset = 0;
605            self.v2.backing_file_size = 0;
606        };
607
608        let mut full_buf = encode_binary(&self.v2)?;
609        if self.v2.version > 2 {
610            full_buf.append(&mut encode_binary(&self.v3)?);
611            full_buf.extend_from_slice(&self.unknown_header_fields);
612            full_buf.resize(full_buf.len().next_multiple_of(8), 0);
613        }
614
615        full_buf.append(&mut header_exts);
616
617        if let Some(backing) = self.backing_filename.as_ref() {
618            full_buf.extend_from_slice(backing.as_bytes());
619        }
620
621        if full_buf.len() > self.cluster_size() {
622            return Err(io::Error::other(format!(
623                "Header is too big to write ({}, larger than a cluster ({}))",
624                full_buf.len(),
625                self.cluster_size(),
626            )));
627        }
628
629        image.write(&full_buf, 0).await
630    }
631
632    /// Create a header for a new image.
633    pub fn new(
634        cluster_bits: u32,
635        refcount_order: u32,
636        backing_filename: Option<String>,
637        backing_format: Option<String>,
638        external_data_file: Option<String>,
639    ) -> Self {
640        assert!((MIN_CLUSTER_SIZE..=MAX_CLUSTER_SIZE)
641            .contains(&1usize.checked_shl(cluster_bits).unwrap()));
642        assert!((MIN_REFCOUNT_WIDTH..=MAX_REFCOUNT_WIDTH)
643            .contains(&1usize.checked_shl(refcount_order).unwrap()));
644
645        let has_external_data_file = external_data_file.is_some();
646        let incompatible_features = if has_external_data_file {
647            IncompatibleFeatures::ExternalDataFile as u64
648        } else {
649            0
650        };
651
652        let mut extensions = vec![HeaderExtension::feature_name_table()];
653        if let Some(backing_format) = backing_format {
654            extensions.push(HeaderExtension::BackingFileFormat(backing_format));
655        }
656        if let Some(external_data_file) = external_data_file {
657            extensions.push(HeaderExtension::ExternalDataFileName(external_data_file));
658        }
659
660        Header {
661            v2: V2Header {
662                magic: MAGIC,
663                version: 3,
664                backing_file_offset: 0, // will be set by `Self::write()`
665                backing_file_size: 0,   // will be set by `Self::write()`
666                cluster_bits,
667                size: 0.into(),
668                crypt_method: 0,
669                l1_size: 0.into(),
670                l1_table_offset: 0.into(),
671                refcount_table_offset: 0.into(),
672                refcount_table_clusters: 0.into(),
673                nb_snapshots: 0,
674                snapshots_offset: 0,
675            },
676            v3: V3HeaderBase {
677                incompatible_features,
678                compatible_features: 0,
679                autoclear_features: 0,
680                refcount_order,
681                header_length: 0, // will be set by `Self::write()`
682            },
683            unknown_header_fields: Vec::new(),
684            backing_filename,
685            extensions,
686            external_data_file: has_external_data_file,
687        }
688    }
689
690    /// Update from a newly loaded header.
691    ///
692    /// Checks whether fields we consider immutable have remained the same, and updates mutable
693    /// fields.
694    pub fn update(&self, new_header: &Header) -> io::Result<()> {
695        /// Verify that the given field matches in `self` and `new_header`.
696        macro_rules! check_field {
697            ($($field:ident).*) => {
698                (self.$($field).* == new_header.$($field).*).then_some(()).ok_or_else(|| {
699                    io::Error::other(format!(
700                        "Incompatible header modification on {}: {} != {}",
701                        stringify!($($field).*),
702                        self.$($field).*,
703                        new_header.$($field).*
704                    ))
705                })
706            };
707        }
708
709        check_field!(v2.magic)?;
710        check_field!(v2.version)?;
711        check_field!(v2.backing_file_offset)?; // TODO: Should be mutable
712        check_field!(v2.backing_file_size)?; // TODO: Should be mutable
713        check_field!(v2.cluster_bits)?;
714        // Size is mutable
715        // L1 position is mutable
716        // Reftable position is mutable
717        check_field!(v2.crypt_method)?;
718        check_field!(v2.nb_snapshots)?; // TODO: Should be mutable
719        check_field!(v2.snapshots_offset)?; // TODO: Should be mutable
720        check_field!(v3.incompatible_features)?; // TODO: Should be mutable
721        check_field!(v3.compatible_features)?; // TODO: Should be mutable
722        check_field!(v3.autoclear_features)?; // TODO: Should be mutable
723        check_field!(v3.refcount_order)?;
724        // header length is OK to ignore (as long as it’s valid)
725
726        // TODO: Should be mutable
727        (self.unknown_header_fields == new_header.unknown_header_fields)
728            .then_some(())
729            .ok_or_else(|| io::Error::other("Unknown header fields modified"))?;
730        // TODO: Should be mutable
731        (self.backing_filename == new_header.backing_filename)
732            .then_some(())
733            .ok_or_else(|| io::Error::other("Backing filename modified"))?;
734        // TODO: Should be mutable
735        (self.extensions == new_header.extensions)
736            .then_some(())
737            .ok_or_else(|| io::Error::other("Header extensions modified"))?;
738
739        check_field!(external_data_file)?;
740
741        self.v2.size.store(
742            new_header.v2.size.load(Ordering::Relaxed),
743            Ordering::Relaxed,
744        );
745
746        self.v2.l1_table_offset.store(
747            new_header.v2.l1_table_offset.load(Ordering::Relaxed),
748            Ordering::Relaxed,
749        );
750        self.v2.l1_size.store(
751            new_header.v2.l1_size.load(Ordering::Relaxed),
752            Ordering::Relaxed,
753        );
754        self.v2.refcount_table_offset.store(
755            new_header.v2.refcount_table_offset.load(Ordering::Relaxed),
756            Ordering::Relaxed,
757        );
758        self.v2.refcount_table_clusters.store(
759            new_header
760                .v2
761                .refcount_table_clusters
762                .load(Ordering::Relaxed),
763            Ordering::Relaxed,
764        );
765
766        Ok(())
767    }
768
769    /// Guest disk size.
770    pub fn size(&self) -> u64 {
771        self.v2.size.load(Ordering::Relaxed)
772    }
773
774    /// Require a minimum qcow2 version.
775    ///
776    /// Return an error if the version requirement is not met.
777    pub fn require_version(&self, minimum: u32) -> io::Result<()> {
778        let version = self.v2.version;
779        if version >= minimum {
780            Ok(())
781        } else {
782            Err(io::Error::new(
783                io::ErrorKind::Unsupported,
784                format!("qcow2 version {minimum} required, image has version {version}"),
785            ))
786        }
787    }
788
789    /// Set the guest disk size.
790    pub fn set_size(&self, new_size: u64) {
791        self.v2.size.store(new_size, Ordering::Relaxed)
792    }
793
794    /// log2 of the cluster size.
795    pub fn cluster_bits(&self) -> u32 {
796        self.v2.cluster_bits
797    }
798
799    /// Cluster size in bytes.
800    pub fn cluster_size(&self) -> usize {
801        1 << self.cluster_bits()
802    }
803
804    /// Number of entries per L2 table.
805    pub fn l2_entries(&self) -> usize {
806        // 3 == log2(size_of::<u64>())
807        1 << (self.cluster_bits() - 3)
808    }
809
810    /// log2 of the number of entries per refcount block.
811    pub fn rb_bits(&self) -> u32 {
812        // log2(cluster_size / (refcount_bits / 8 bits per byte))
813        // = log2(cluster_size * 8 / refcount_bits)
814        // = log2(cluster_size) + log2(8) - log2(refcount_bits)
815        self.cluster_bits() + 3 - self.refcount_order()
816    }
817
818    /// Number of entries per refcount block.
819    pub fn rb_entries(&self) -> usize {
820        1 << self.rb_bits()
821    }
822
823    /// log2 of the refcount bits.
824    pub fn refcount_order(&self) -> u32 {
825        self.v3.refcount_order
826    }
827
828    /// Offset of the L1 table.
829    pub fn l1_table_offset(&self) -> HostOffset {
830        HostOffset(self.v2.l1_table_offset.load(Ordering::Relaxed))
831    }
832
833    /// Number of entries in the L1 table.
834    pub fn l1_table_entries(&self) -> usize {
835        self.v2.l1_size.load(Ordering::Relaxed) as usize
836    }
837
838    /// Enter a new L1 table in the image header.
839    ///
840    /// If the L1 table’s clusters have been newly allocated, `new_allocation` must be their
841    /// pending clusters guard, which is disarmed once the table is entered.
842    pub fn set_l1_table(
843        &self,
844        l1_table: &L1Table,
845        new_allocation: Option<PendingClusters>,
846    ) -> io::Result<()> {
847        if let Some(new_allocation) = new_allocation.as_ref() {
848            debug_assert_eq!(Some(new_allocation.cluster()), l1_table.get_cluster());
849        }
850
851        let offset = l1_table.get_offset().ok_or_else(|| {
852            io::Error::new(
853                io::ErrorKind::InvalidInput,
854                "New L1 table has no assigned offset",
855            )
856        })?;
857
858        let entries = l1_table.entries();
859        let entries = entries
860            .try_into()
861            .map_err(|err| invalid_data(format!("Too many L1 entries ({entries}): {err}")))?;
862
863        self.v2.l1_table_offset.store(offset.0, Ordering::Relaxed);
864        self.v2.l1_size.store(entries, Ordering::Relaxed);
865        if let Some(new_allocation) = new_allocation {
866            new_allocation.make_persistent();
867        }
868
869        Ok(())
870    }
871
872    /// Offset of the refcount table.
873    pub fn reftable_offset(&self) -> HostOffset {
874        HostOffset(self.v2.refcount_table_offset.load(Ordering::Relaxed))
875    }
876
877    /// Number of clusters occupied by the refcount table.
878    pub fn reftable_clusters(&self) -> ClusterCount {
879        ClusterCount(self.v2.refcount_table_clusters.load(Ordering::Relaxed) as u64)
880    }
881
882    /// Number of entries in the refcount table.
883    pub fn reftable_entries(&self) -> usize {
884        // 3 == log2(size_of::<u64>())
885        (self.reftable_clusters().byte_size(self.cluster_bits()) >> 3) as usize
886    }
887
888    /// Enter a new refcount table in the image header.
889    pub fn set_reftable(&self, reftable: &RefTable) -> io::Result<()> {
890        let offset = reftable.get_offset().ok_or_else(|| {
891            io::Error::new(
892                io::ErrorKind::InvalidInput,
893                "New refcount table has no assigned offset",
894            )
895        })?;
896
897        let clusters = reftable.cluster_count();
898        let clusters = clusters.0.try_into().map_err(|err| {
899            invalid_data(format!("Too many reftable clusters ({clusters}): {err}"))
900        })?;
901
902        self.v2
903            .refcount_table_clusters
904            .store(clusters, Ordering::Relaxed);
905
906        self.v2
907            .refcount_table_offset
908            .store(offset.0, Ordering::Relaxed);
909
910        Ok(())
911    }
912
913    /// Backing filename from the image header (if any).
914    pub fn backing_filename(&self) -> Option<&String> {
915        self.backing_filename.as_ref()
916    }
917
918    /// Backing format string from the image header (if any).
919    pub fn backing_format(&self) -> Option<&String> {
920        self.extensions.iter().find_map(|e| match e {
921            HeaderExtension::BackingFileFormat(fmt) => Some(fmt),
922            _ => None,
923        })
924    }
925
926    /// Whether this image requires an external data file.
927    pub fn external_data_file(&self) -> bool {
928        self.external_data_file
929    }
930
931    /// External data file filename from the image header (if any).
932    pub fn external_data_filename(&self) -> Option<&String> {
933        self.extensions.iter().find_map(|e| match e {
934            HeaderExtension::ExternalDataFileName(filename) => Some(filename),
935            _ => None,
936        })
937    }
938
939    /// Translate a feature bit to a human-readable name.
940    ///
941    /// Uses the feature name table from the image header, if present.
942    pub fn feature_name(&self, feat_type: FeatureType, bit: u32) -> Option<&String> {
943        for e in &self.extensions {
944            if let HeaderExtension::FeatureNameTable(names) = e {
945                if let Some(name) = names.get(&(feat_type, bit as u8)) {
946                    return Some(name);
947                }
948            }
949        }
950
951        None
952    }
953
954    /// Serialize all header extensions.
955    fn serialize_extensions(&self) -> io::Result<Vec<u8>> {
956        let mut result = Vec::new();
957        for e in &self.extensions {
958            let mut data = e.serialize_data()?;
959            let ext_hdr = HeaderExtensionHeader {
960                extension_type: e.extension_type(),
961                length: data.len().try_into().map_err(|err| {
962                    invalid_data(format!("Header extension too long ({}): {err}", data.len()))
963                })?,
964            };
965            result.append(&mut encode_binary(&ext_hdr)?);
966            result.append(&mut data);
967            result.resize(result.len().next_multiple_of(8), 0);
968        }
969
970        let end_ext = HeaderExtensionHeader {
971            extension_type: HeaderExtensionType::End as u32,
972            length: 0,
973        };
974        result.append(&mut encode_binary(&end_ext)?);
975        result.resize(result.len().next_multiple_of(8), 0);
976
977        Ok(result)
978    }
979
980    /// Helper for functions that just need to change little bits in the v2 header part.
981    async fn write_v2_header<S: Storage>(&self, image: &S) -> io::Result<()> {
982        let v2_header = encode_binary(&self.v2)?;
983        image.write(&v2_header, 0).await
984    }
985
986    /// Write the refcount table pointer (offset and size) to disk.
987    pub async fn write_reftable_pointer<S: Storage>(&self, image: &S) -> io::Result<()> {
988        // TODO: Just write the reftable offset and size
989        self.write_v2_header(image).await
990    }
991
992    /// Write the L1 table pointer (offset and size) to disk.
993    pub async fn write_l1_table_pointer<S: Storage>(&self, image: &S) -> io::Result<()> {
994        // TODO: Just write the L1 table offset and size
995        self.write_v2_header(image).await
996    }
997
998    /// Write the guest disk size to disk.
999    pub async fn write_size<S: Storage>(&self, image: &S) -> io::Result<()> {
1000        // TODO: Just write the size
1001        self.write_v2_header(image).await
1002    }
1003}
1004
1005impl HeaderExtension {
1006    /// Parse an extension from its type and data.  Unrecognized types are stored as `Unknown`
1007    /// extensions, encountering the end of extensions returns `Ok(None)`.
1008    fn deserialize(ext_type: u32, data: Vec<u8>) -> io::Result<Option<Self>> {
1009        let ext = if let Ok(ext_type) = HeaderExtensionType::try_from(ext_type) {
1010            match ext_type {
1011                HeaderExtensionType::End => return Ok(None),
1012                HeaderExtensionType::BackingFileFormat => {
1013                    let fmt = String::from_utf8(data).map_err(|err| {
1014                        invalid_data(format!("Invalid backing file format: {err}"))
1015                    })?;
1016                    HeaderExtension::BackingFileFormat(fmt)
1017                }
1018                HeaderExtensionType::FeatureNameTable => {
1019                    let mut feats = HashMap::new();
1020                    for feat in data.chunks(48) {
1021                        let feat_type: FeatureType = match feat[0].try_into() {
1022                            Ok(ft) => ft,
1023                            Err(_) => continue, // skip unrecognized entries
1024                        };
1025                        // Cannot use CStr to parse this, as it may not be NUL-terminated.
1026                        // Use this to remove everything from the first NUL byte.
1027                        let feat_name_bytes = feat[2..].split(|c| *c == 0).next().unwrap();
1028                        // Then just use it as a UTF-8 string.
1029                        let feat_name = String::from_utf8_lossy(feat_name_bytes);
1030                        feats.insert((feat_type, feat[1]), feat_name.to_string());
1031                    }
1032                    HeaderExtension::FeatureNameTable(feats)
1033                }
1034                HeaderExtensionType::ExternalDataFileName => {
1035                    let filename = String::from_utf8(data).map_err(|err| {
1036                        invalid_data(format!("Invalid external data file name: {err}"))
1037                    })?;
1038                    HeaderExtension::ExternalDataFileName(filename)
1039                }
1040            }
1041        } else {
1042            HeaderExtension::Unknown {
1043                extension_type: ext_type,
1044                data,
1045            }
1046        };
1047
1048        Ok(Some(ext))
1049    }
1050
1051    /// Return the extension type ID.
1052    fn extension_type(&self) -> u32 {
1053        match self {
1054            HeaderExtension::BackingFileFormat(_) => HeaderExtensionType::BackingFileFormat as u32,
1055            HeaderExtension::FeatureNameTable(_) => HeaderExtensionType::FeatureNameTable as u32,
1056            HeaderExtension::ExternalDataFileName(_) => {
1057                HeaderExtensionType::ExternalDataFileName as u32
1058            }
1059            HeaderExtension::Unknown {
1060                extension_type,
1061                data: _,
1062            } => *extension_type,
1063        }
1064    }
1065
1066    /// Serialize this extension’s data (exclusing its header).
1067    fn serialize_data(&self) -> io::Result<Vec<u8>> {
1068        match self {
1069            HeaderExtension::BackingFileFormat(fmt) => Ok(fmt.as_bytes().into()),
1070            HeaderExtension::FeatureNameTable(map) => {
1071                let mut result = Vec::new();
1072                for (bit, name) in map {
1073                    result.push(bit.0 as u8);
1074                    result.push(bit.1);
1075
1076                    let mut padded_name = vec![0; 46];
1077                    let name_bytes = name.as_bytes();
1078                    // Might truncate in the middle of a multibyte character, but getting that
1079                    // right is complicated and probably not worth it
1080                    let truncated_len = cmp::min(name_bytes.len(), 46);
1081                    padded_name[..truncated_len].copy_from_slice(&name_bytes[..truncated_len]);
1082                    result.extend_from_slice(&padded_name);
1083                }
1084                Ok(result)
1085            }
1086            HeaderExtension::ExternalDataFileName(filename) => Ok(filename.as_bytes().into()),
1087            HeaderExtension::Unknown {
1088                extension_type: _,
1089                data,
1090            } => Ok(data.clone()),
1091        }
1092    }
1093
1094    /// Creates a [`Self::FeatureNameTable`].
1095    fn feature_name_table() -> Self {
1096        use AutoclearFeatures as A;
1097        use CompatibleFeatures as C;
1098        use IncompatibleFeatures as I;
1099
1100        let mut map = HashMap::new();
1101
1102        map.insert(I::Dirty.into(), "dirty".into());
1103        map.insert(I::Corrupt.into(), "corrupt".into());
1104        map.insert(I::ExternalDataFile.into(), "external data file".into());
1105        map.insert(
1106            I::CompressionType.into(),
1107            "extended compression type".into(),
1108        );
1109        map.insert(I::ExtendedL2Entries.into(), "extended L2 entries".into());
1110
1111        map.insert(C::LazyRefcounts.into(), "lazy refcounts".into());
1112
1113        map.insert(A::Bitmaps.into(), "persistent dirty bitmaps".into());
1114        map.insert(A::RawExternalData.into(), "raw external data file".into());
1115
1116        HeaderExtension::FeatureNameTable(map)
1117    }
1118}
1119
1120/// L1 table entry.
1121///
1122/// - Bit 0 - 8: Reserved (set to 0)
1123/// - Bit 9 – 55: Bits 9-55 of the offset into the image file at which the L2 table starts.  Must
1124///   be aligned to a cluster boundary.  If the offset is 0, the L2 table and all clusters
1125///   described by this L2 table are unallocated.
1126/// - Bit 56 - 62: Reserved (set to 0)
1127/// - Bit 63: 0 for an L2 table that is unused or requires COW, 1 if its refcount is exactly one.
1128///   This information is only accurate in the active L1 table.
1129#[derive(Copy, Clone, Default, Debug)]
1130pub(super) struct L1Entry(u64);
1131
1132impl L1Entry {
1133    /// Offset of the L2 table, if any.
1134    pub fn l2_offset(&self) -> Option<HostOffset> {
1135        let ofs = self.0 & 0x00ff_ffff_ffff_fe00u64;
1136        if ofs == 0 {
1137            None
1138        } else {
1139            Some(HostOffset(ofs))
1140        }
1141    }
1142
1143    /// Whether the L2 table’s cluster is “copied”.
1144    ///
1145    /// `true` means is refcount is one, `false` means modifying it will require COW.
1146    pub fn is_copied(&self) -> bool {
1147        self.0 & (1u64 << 63) != 0
1148    }
1149
1150    /// Return all reserved bits.
1151    pub fn reserved_bits(&self) -> u64 {
1152        self.0 & 0x7f00_0000_0000_01feu64
1153    }
1154}
1155
1156impl TableEntry for L1Entry {
1157    fn try_from_plain(value: u64, header: &Header) -> io::Result<Self> {
1158        let entry = L1Entry(value);
1159
1160        if entry.reserved_bits() != 0 {
1161            return Err(invalid_data(format!(
1162                "Invalid L1 entry 0x{value:x}, reserved bits set (0x{:x})",
1163                entry.reserved_bits(),
1164            )));
1165        }
1166
1167        if let Some(l2_ofs) = entry.l2_offset() {
1168            if l2_ofs.in_cluster_offset(header.cluster_bits()) != 0 {
1169                return Err(invalid_data(format!(
1170                    "Invalid L1 entry 0x{value:x}, offset ({l2_ofs}) is not aligned to cluster size (0x{:x})",
1171                    header.cluster_size(),
1172                )));
1173            }
1174        }
1175
1176        Ok(entry)
1177    }
1178
1179    fn to_plain(&self) -> u64 {
1180        self.0
1181    }
1182}
1183
1184/// L1 table.
1185#[derive(Debug)]
1186pub(super) struct L1Table {
1187    /// First cluster in the image file.
1188    cluster: Option<HostCluster>,
1189
1190    /// Table data.
1191    data: Box<[L1Entry]>,
1192
1193    /// log2 of the cluster size.
1194    cluster_bits: u32,
1195
1196    /// Whether this table has been modified since it was last written.
1197    modified: AtomicBool,
1198}
1199
1200impl L1Table {
1201    /// Create a clone that covers at least `at_least_index`.
1202    pub fn clone_and_grow(&self, at_least_index: usize, header: &Header) -> io::Result<Self> {
1203        let new_entry_count = cmp::max(at_least_index + 1, self.data.len());
1204        let new_entry_count =
1205            new_entry_count.next_multiple_of(header.cluster_size() / size_of::<L1Entry>());
1206
1207        if new_entry_count > <Self as Table>::MAX_ENTRIES {
1208            return Err(io::Error::other(
1209                "Cannot grow the image to this size; L1 table would become too big",
1210            ));
1211        }
1212
1213        let mut new_data = vec![L1Entry::default(); new_entry_count];
1214        new_data[..self.data.len()].copy_from_slice(&self.data);
1215
1216        Ok(Self {
1217            cluster: None,
1218            data: new_data.into_boxed_slice(),
1219            cluster_bits: header.cluster_bits(),
1220            modified: true.into(),
1221        })
1222    }
1223
1224    /// Check whether `index` is in bounds.
1225    pub fn in_bounds(&self, index: usize) -> bool {
1226        index < self.data.len()
1227    }
1228
1229    /// Enter the given L2 table into this L1 table.
1230    ///
1231    /// If the L2 table’s cluster has been newly allocated, `new_allocation` must be its pending
1232    /// clusters guard, which is disarmed once the table is entered.
1233    pub fn enter_l2_table(
1234        &mut self,
1235        index: usize,
1236        l2: &L2Table,
1237        new_allocation: Option<PendingClusters>,
1238    ) -> io::Result<()> {
1239        if let Some(new_allocation) = new_allocation.as_ref() {
1240            debug_assert_eq!(Some(new_allocation.cluster()), l2.get_cluster());
1241        }
1242
1243        let l2_offset = l2.get_offset().ok_or_else(|| {
1244            io::Error::new(
1245                io::ErrorKind::InvalidInput,
1246                "L2 table has no assigned offset",
1247            )
1248        })?;
1249
1250        let l1entry = L1Entry((1 << 63) | l2_offset.0);
1251        debug_assert!(l1entry.reserved_bits() == 0);
1252        self.data[index] = l1entry;
1253        self.modified.store(true, Ordering::Relaxed);
1254        if let Some(new_allocation) = new_allocation {
1255            new_allocation.make_persistent();
1256        }
1257
1258        Ok(())
1259    }
1260}
1261
1262impl Table for L1Table {
1263    type InternalEntry = L1Entry;
1264    type Entry = L1Entry;
1265    const NAME: &'static str = "L1 table";
1266
1267    /// Maximum number of L1 table entries.
1268    ///
1269    /// Limit taken from QEMU; if QEMU rejects this, we can, too.
1270    const MAX_ENTRIES: usize = 4 * 1024 * 1024;
1271
1272    fn from_data(data: Box<[L1Entry]>, header: &Header) -> Self {
1273        Self {
1274            cluster: None,
1275            data,
1276            cluster_bits: header.cluster_bits(),
1277            modified: true.into(),
1278        }
1279    }
1280
1281    fn entries(&self) -> usize {
1282        self.data.len()
1283    }
1284
1285    fn get_ref(&self, index: usize) -> Option<&L1Entry> {
1286        self.data.get(index)
1287    }
1288
1289    fn get(&self, index: usize) -> L1Entry {
1290        self.data.get(index).copied().unwrap_or(L1Entry(0))
1291    }
1292
1293    fn get_cluster(&self) -> Option<HostCluster> {
1294        self.cluster
1295    }
1296
1297    fn get_offset(&self) -> Option<HostOffset> {
1298        self.cluster.map(|index| index.offset(self.cluster_bits))
1299    }
1300
1301    fn set_cluster(&mut self, cluster: HostCluster) {
1302        self.cluster = Some(cluster);
1303        self.modified.store(true, Ordering::Relaxed);
1304    }
1305
1306    fn unset_cluster(&mut self) {
1307        self.cluster = None;
1308    }
1309
1310    fn is_modified(&self) -> bool {
1311        self.modified.load(Ordering::Relaxed)
1312    }
1313
1314    fn clear_modified(&self) {
1315        self.modified.store(false, Ordering::Relaxed);
1316    }
1317
1318    fn set_modified(&self) {
1319        self.modified.store(true, Ordering::Relaxed);
1320    }
1321
1322    fn cluster_bits(&self) -> u32 {
1323        self.cluster_bits
1324    }
1325}
1326
1327/// L2 table entry.
1328///
1329/// - Bit 0 - 61: Cluster descriptor
1330/// - Bit 62: 0 for standard clusters, 1 for compressed clusters
1331/// - Bit 63: 0 for clusters that are unused, compressed or require COW.  1 for standard clusters
1332///   whose refcount is exactly one.  This information is only accurate in L2 tables that are
1333///   reachable from the active L1 table.  With external data files, all guest clusters have an
1334///   implicit refcount of 1 (because of the fixed host = guest mapping for guest cluster offsets),
1335///   so this bit should be 1 for all allocated clusters.
1336///
1337/// Standard Cluster Descriptor:
1338/// - Bit 0: If set to 1, the cluster reads as all zeros. The host cluster offset can be used to
1339///   describe a preallocation, but it won’t be used for reading data from this cluster, nor is
1340///   data read from the backing file if the cluster is unallocated.  With version 2 or with
1341///   extended L2 entries (see the next section), this is always 0.
1342/// - Bit 1 – 8: Reserved (set to 0)
1343/// - Bit 9 – 55: Bits 9-55 of host cluster offset. Must be aligned to a cluster boundary. If the
1344///   offset is 0 and bit 63 is clear, the cluster is unallocated. The offset may only be 0 with
1345///   bit 63 set (indicating a host cluster offset of 0) when an external data file is used.
1346/// - Bit 56 - 61: Reserved (set to 0)
1347///
1348/// Compressed Cluster Descriptor (`x = 62 - (cluster_bits - 8)`):
1349/// - Bit 0 - x-1: Host cluster offset.  This is usually _not_ aligned to a cluster or sector
1350///   boundary!  If cluster_bits is small enough that this field includes bits beyond 55, those
1351///   upper bits must be set to 0.
1352/// - Bit x - 61: Number of additional 512-byte sectors used for the compressed data, beyond the
1353///   sector containing the offset in the previous field. Some of these sectors may reside in the
1354///   next contiguous host cluster.  Note that the compressed data does not necessarily occupy all
1355///   of the bytes in the final sector; rather, decompression stops when it has produced a cluster
1356///   of data.  Another compressed cluster may map to the tail of the final sector used by this
1357///   compressed cluster.
1358#[derive(Copy, Clone, Default, Debug)]
1359pub(super) struct L2Entry(u64);
1360
1361/// Internal actual type of L2 entries.
1362///
1363/// Using atomic allows flushing L2 tables from the cache while they are write-locked.
1364#[derive(Default, Debug)]
1365pub(super) struct AtomicL2Entry(AtomicU64);
1366
1367/// High-level representation of an L2 entry.
1368#[derive(Debug, Clone)]
1369pub(super) enum L2Mapping {
1370    /// Data is in the data file.
1371    DataFile {
1372        /// Cluster in the data file.
1373        host_cluster: HostCluster,
1374
1375        /// Whether the cluster has a refcount of exactly 1.
1376        copied: bool,
1377    },
1378
1379    /// Data is in the backing file.
1380    Backing {
1381        /// Guest cluster index.
1382        backing_offset: u64,
1383    },
1384
1385    /// Data is zero.
1386    Zero {
1387        /// Preallocated cluster in the data file, if any.
1388        host_cluster: Option<HostCluster>,
1389
1390        /// Whether the preallocated cluster has a refcount of exactly 1.
1391        copied: bool,
1392    },
1393
1394    /// Data is compressed.
1395    Compressed {
1396        /// Offset in the data file.
1397        host_offset: HostOffset,
1398
1399        /// Upper limit on the number of bytes that comprise the compressed data.
1400        length: NonZero<u64>,
1401    },
1402}
1403
1404impl L2Entry {
1405    /// Offset of the data cluster, if any.
1406    ///
1407    /// Assumes the L2 entry references a data cluster, not a compressed cluster.
1408    ///
1409    /// `external_data_file` must be true when using an external data file; in this case, offset 0
1410    /// is a valid offset, and can only be distinguished from “unallocated” by whether the COPIED
1411    /// flag is set or not (which it always is when using an external data file).
1412    pub fn cluster_offset(&self, external_data_file: bool) -> Option<HostOffset> {
1413        let ofs = self.0 & 0x00ff_ffff_ffff_fe00u64;
1414        if ofs != 0 || (external_data_file && self.is_copied()) {
1415            Some(HostOffset(ofs))
1416        } else {
1417            None
1418        }
1419    }
1420
1421    /// Whether the cluster is compressed.
1422    pub fn is_compressed(&self) -> bool {
1423        self.0 & (1u64 << 62) != 0
1424    }
1425
1426    /// Whether the cluster is “copied”.
1427    ///
1428    /// `true` means is refcount is one, `false` means modifying it will require COW.
1429    pub fn is_copied(&self) -> bool {
1430        self.0 & (1u64 << 63) != 0
1431    }
1432
1433    /// Clear “copied” flag.
1434    #[must_use]
1435    pub fn without_copied(self) -> Self {
1436        L2Entry(self.0 & !(1u64 << 63))
1437    }
1438
1439    /// Whether the cluster is a zero cluster.
1440    ///
1441    /// Assumes the L2 entry references a data cluster, not a compressed cluster.
1442    pub fn is_zero(&self) -> bool {
1443        self.0 & (1u64 << 0) != 0
1444    }
1445
1446    /// Return all reserved bits.
1447    pub fn reserved_bits(&self) -> u64 {
1448        if self.is_compressed() {
1449            self.0 & 0x8000_0000_0000_0000u64
1450        } else {
1451            self.0 & 0x3f00_0000_0000_01feu64
1452        }
1453    }
1454
1455    /// Return the full compressed cluster descriptor.
1456    pub fn compressed_descriptor(&self) -> u64 {
1457        self.0 & 0x3fff_ffff_ffff_ffffu64
1458    }
1459
1460    /// If this entry is compressed, return the start host offset and upper limit on the compressed
1461    /// number of bytes.
1462    pub fn compressed_range(&self, cluster_bits: u32) -> Option<(HostOffset, NonZero<u64>)> {
1463        if self.is_compressed() {
1464            let desc = self.compressed_descriptor();
1465            let compressed_offset_bits = 62 - (cluster_bits - 8);
1466            let offset = desc & ((1 << compressed_offset_bits) - 1) & 0x00ff_ffff_ffff_ffffu64;
1467            let sectors: u64 = desc >> compressed_offset_bits;
1468            // The first sector is not considered in `sectors`, so we add it and subtract the
1469            // number of bytes there that do not belong to this compressed cluster
1470            // Unwrap is safe: ∀y: 0 <= y & 511 <= 511, so ∀x >= 0: (x + 1) * 512 - (y & 511) >= 1
1471            let length = NonZero::new((sectors + 1) * 512 - (offset & 511)).unwrap();
1472
1473            Some((HostOffset(offset), length))
1474        } else {
1475            None
1476        }
1477    }
1478
1479    /// If this entry is allocated, return the first host cluster and the number of clusters it
1480    /// references.
1481    ///
1482    /// `external_data_file` must be true when using an external data file.
1483    fn allocation(
1484        &self,
1485        cluster_bits: u32,
1486        external_data_file: bool,
1487    ) -> Option<(HostCluster, ClusterCount)> {
1488        if let Some((offset, length)) = self.compressed_range(cluster_bits) {
1489            // Compressed clusters can cross host cluster boundaries, and thus occupy two clusters
1490            let first_cluster = offset.cluster(cluster_bits);
1491            let cluster_count = ClusterCount::from_byte_size(
1492                offset + length.get() - first_cluster.offset(cluster_bits),
1493                cluster_bits,
1494            );
1495            Some((first_cluster, cluster_count))
1496        } else {
1497            self.cluster_offset(external_data_file)
1498                .map(|ofs| (ofs.cluster(cluster_bits), ClusterCount(1)))
1499        }
1500    }
1501
1502    /// Return the high-level `L2Mapping` representation.
1503    ///
1504    /// `guest_cluster` is the guest cluster being accessed, `cluster_bits` is log2 of the cluster
1505    /// size.  `external_data_file` must be true when using an external data file.
1506    fn into_mapping(
1507        self,
1508        guest_cluster: GuestCluster,
1509        cluster_bits: u32,
1510        external_data_file: bool,
1511    ) -> io::Result<L2Mapping> {
1512        let mapping = if let Some((offset, length)) = self.compressed_range(cluster_bits) {
1513            L2Mapping::Compressed {
1514                host_offset: offset,
1515                length,
1516            }
1517        } else if self.is_zero() {
1518            let host_cluster = self
1519                .cluster_offset(external_data_file)
1520                .map(|ofs| {
1521                    ofs.checked_cluster(cluster_bits).ok_or_else(|| {
1522                        let offset = guest_cluster.offset(cluster_bits);
1523                        io::Error::other(format!(
1524                            "Unaligned pre-allocated zero cluster at {offset}; L2 entry: {self:?}"
1525                        ))
1526                    })
1527                })
1528                .transpose()?;
1529
1530            L2Mapping::Zero {
1531                host_cluster,
1532                copied: host_cluster.is_some() && self.is_copied(),
1533            }
1534        } else if let Some(host_offset) = self.cluster_offset(external_data_file) {
1535            let host_cluster = host_offset.checked_cluster(cluster_bits).ok_or_else(|| {
1536                let offset = guest_cluster.offset(cluster_bits);
1537                io::Error::other(format!(
1538                    "Unaligned data cluster at {offset}; L2 entry: {self:?}"
1539                ))
1540            })?;
1541
1542            L2Mapping::DataFile {
1543                host_cluster,
1544                copied: self.is_copied(),
1545            }
1546        } else {
1547            L2Mapping::Backing {
1548                backing_offset: guest_cluster.offset(cluster_bits).0,
1549            }
1550        };
1551
1552        Ok(mapping)
1553    }
1554
1555    /// Create an L2 entry from its high-level `L2Mapping` representation.
1556    fn from_mapping(value: L2Mapping, cluster_bits: u32) -> Self {
1557        let num_val: u64 = match value {
1558            L2Mapping::DataFile {
1559                host_cluster,
1560                copied,
1561            } => {
1562                debug_assert!(host_cluster.offset(cluster_bits) <= MAX_OFFSET);
1563                if copied {
1564                    (1 << 63) | host_cluster.offset(cluster_bits).0
1565                } else {
1566                    host_cluster.offset(cluster_bits).0
1567                }
1568            }
1569
1570            L2Mapping::Backing { backing_offset: _ } => 0,
1571
1572            L2Mapping::Zero {
1573                host_cluster,
1574                copied,
1575            } => {
1576                let host_offset = host_cluster.map(|hc| hc.offset(cluster_bits));
1577                debug_assert!(host_offset.unwrap_or(HostOffset(0)) <= MAX_OFFSET);
1578                if copied {
1579                    (1 << 63) | host_offset.unwrap().0 | 0x1
1580                } else {
1581                    host_offset.unwrap_or(HostOffset(0)).0 | 0x1
1582                }
1583            }
1584
1585            L2Mapping::Compressed {
1586                host_offset,
1587                length,
1588            } => {
1589                let compressed_offset_bits = 62 - (cluster_bits - 8);
1590                let length = length.get();
1591
1592                assert!(length < 1 << cluster_bits);
1593                assert!(host_offset.0 < 1 << compressed_offset_bits);
1594
1595                // The first sector is not considered, so we subtract the number of bytes in it
1596                // that belong to this compressed cluster from `length`:
1597                // ceil((length - (512 - (host_offset & 511))) / 512)
1598                // = (length + 511 - 512 + (host_offset & 511)) / 512
1599                let sectors = (length - 1 + (host_offset.0 & 511)) / 512;
1600
1601                (1 << 62) | (sectors << compressed_offset_bits) | host_offset.0
1602            }
1603        };
1604
1605        let entry = L2Entry(num_val);
1606        debug_assert!(entry.reserved_bits() == 0);
1607        entry
1608    }
1609}
1610
1611impl AtomicL2Entry {
1612    /// Get the contained value.
1613    fn get(&self) -> L2Entry {
1614        L2Entry(self.0.load(Ordering::Relaxed))
1615    }
1616
1617    /// Exchange the contained value.
1618    ///
1619    /// # Safety
1620    /// Caller must ensure that:
1621    /// (1) No reader sees invalid intermediate states.
1622    /// (2) Updates are done atomically (do not depend on prior state of the L2 table), or there is
1623    ///     only one writer at a time.
1624    unsafe fn swap(&self, l2e: L2Entry) -> L2Entry {
1625        L2Entry(self.0.swap(l2e.0, Ordering::Relaxed))
1626    }
1627}
1628
1629impl TableEntry for AtomicL2Entry {
1630    fn try_from_plain(value: u64, header: &Header) -> io::Result<Self> {
1631        let entry = L2Entry(value);
1632
1633        if entry.reserved_bits() != 0 {
1634            return Err(invalid_data(format!(
1635                "Invalid L2 entry 0x{value:x}, reserved bits set (0x{:x})",
1636                entry.reserved_bits(),
1637            )));
1638        }
1639
1640        if let Some(offset) = entry.cluster_offset(header.external_data_file()) {
1641            if !entry.is_compressed() && offset.in_cluster_offset(header.cluster_bits()) != 0 {
1642                return Err(invalid_data(format!(
1643                    "Invalid L2 entry 0x{value:x}, offset ({offset}) is not aligned to cluster size (0x{:x})",
1644                    header.cluster_size(),
1645                )));
1646            }
1647        }
1648
1649        Ok(AtomicL2Entry(AtomicU64::new(entry.0)))
1650    }
1651
1652    fn to_plain(&self) -> u64 {
1653        self.get().0
1654    }
1655}
1656
1657impl L2Mapping {
1658    /// Check whether two mappings are consecutive.
1659    ///
1660    /// Given the `preceding` mapping, check whether `self` is consecutive to it, i.e. is the same
1661    /// kind of mapping, and the offsets are consecutive.
1662    pub fn is_consecutive(&self, preceding: &L2Mapping, cluster_bits: u32) -> bool {
1663        match preceding {
1664            L2Mapping::DataFile {
1665                host_cluster: prior_cluster,
1666                copied,
1667            } => {
1668                if let L2Mapping::DataFile {
1669                    host_cluster: next_cluster,
1670                    copied: next_copied,
1671                } = self
1672                {
1673                    *next_cluster == *prior_cluster + ClusterCount(1) && *next_copied == *copied
1674                } else {
1675                    false
1676                }
1677            }
1678
1679            L2Mapping::Backing {
1680                backing_offset: prior_backing_offset,
1681            } => {
1682                let Some(expected_next) = prior_backing_offset.checked_add(1 << cluster_bits)
1683                else {
1684                    return false;
1685                };
1686
1687                if let L2Mapping::Backing {
1688                    backing_offset: next_offset,
1689                } = self
1690                {
1691                    *next_offset == expected_next
1692                } else {
1693                    false
1694                }
1695            }
1696
1697            L2Mapping::Zero {
1698                host_cluster: _,
1699                copied: _,
1700            } => {
1701                // Cluster and copied do not matter; every read is continuous regardless (always
1702                // zero), and every write is, too (always allocate)
1703                matches!(
1704                    self,
1705                    L2Mapping::Zero {
1706                        host_cluster: _,
1707                        copied: _,
1708                    }
1709                )
1710            }
1711
1712            L2Mapping::Compressed {
1713                host_offset: _,
1714                length: _,
1715            } => {
1716                // Not really true, but in practice it is.  Reads need to go through a special
1717                // function anyway, and every write will need COW anyway.
1718                matches!(
1719                    self,
1720                    L2Mapping::Compressed {
1721                        host_offset: _,
1722                        length: _,
1723                    }
1724                )
1725            }
1726        }
1727    }
1728}
1729
1730/// L2 table.
1731#[derive(Debug)]
1732pub(super) struct L2Table {
1733    /// Cluster of the L2 table.
1734    cluster: Option<HostCluster>,
1735
1736    /// Table data.
1737    data: Box<[AtomicL2Entry]>,
1738
1739    /// log2 of the cluster size.
1740    cluster_bits: u32,
1741
1742    /// Whether this image uses an external data file.
1743    external_data_file: bool,
1744
1745    /// Whether this table has been modified since it was last written.
1746    modified: AtomicBool,
1747
1748    /// Lock for creating `L2TableWriteGuard` (write) and `CompressedClusterReadGuard` (read).
1749    ///
1750    /// This is an owning (`Arc`-backed) lock, i.e. any access guard will have a strong reference
1751    /// to it and can thus be held independently of lifetime concerns, e.g. across a data write
1752    /// while a prepared mapping is not yet committed (see `Qcow2::prepare_data_mapping()`).
1753    ///
1754    /// Consequently, it may be held outside of qcow2 code paths, e.g. while a data write is in
1755    /// flight (between preparing and committing a data mapping for it).  Thus, writing this table
1756    /// to disk must work without the lock because otherwise flushing the table would deadlock
1757    /// against such writes.
1758    writer_lock: OwnedRwLock<()>,
1759
1760    /// Serializes writing this table back to disk (see [`L2Table::write_if_modified()`]).
1761    writeback_lock: Mutex<()>,
1762}
1763
1764/// Write guard for an L2 table.
1765///
1766/// Owns a reference to the table, so holding it also keeps the table pinned in the L2 cache.
1767#[derive(Debug)]
1768pub(super) struct L2TableWriteGuard {
1769    /// Referenced L2 table.
1770    table: Arc<L2Table>,
1771
1772    /// Held write guard on that L2 table.
1773    lock: OwnedRwLockWriteGuard<()>,
1774}
1775
1776#[maybe_async]
1777impl L2Table {
1778    /// Create a new zeroed L2 table.
1779    pub fn new_cleared(header: &Header) -> Self {
1780        let mut data = Vec::with_capacity(header.l2_entries());
1781        data.resize_with(header.l2_entries(), Default::default);
1782
1783        L2Table {
1784            cluster: None,
1785            data: data.into_boxed_slice(),
1786            cluster_bits: header.cluster_bits(),
1787            external_data_file: header.external_data_file(),
1788            modified: true.into(),
1789            writer_lock: Default::default(),
1790            writeback_lock: Default::default(),
1791        }
1792    }
1793
1794    /// Look up a cluster mapping.
1795    pub fn get_mapping(&self, lookup_cluster: GuestCluster) -> io::Result<L2Mapping> {
1796        self.get(lookup_cluster.l2_index(self.cluster_bits))
1797            .into_mapping(lookup_cluster, self.cluster_bits, self.external_data_file)
1798    }
1799
1800    /// Allow modifying this L2 table.
1801    ///
1802    /// Note that readers are allowed to exist while modifications are happening, unless they hold
1803    /// a [`CompressedClusterReadGuard`].
1804    ///
1805    /// The returned guard keeps the table pinned in the L2 cache.
1806    pub async fn lock_write(self: &Arc<Self>) -> L2TableWriteGuard {
1807        L2TableWriteGuard {
1808            table: Arc::clone(self),
1809            lock: self.writer_lock.write().await,
1810        }
1811    }
1812
1813    /// Lock the given compressed cluster for reading.
1814    ///
1815    /// Prevent modifications of this L2 table while the returned guard is held.
1816    ///
1817    /// Return `None` if `cluster` is not compressed (checked under the guard).
1818    pub async fn lock_compressed_read(
1819        &self,
1820        cluster: GuestCluster,
1821    ) -> io::Result<Option<CompressedClusterReadGuard<'_>>> {
1822        let lock = self.writer_lock.read().await;
1823        CompressedClusterReadGuard::new(self, cluster, lock.into())
1824    }
1825
1826    /// Write this table to disk if it has been modified since it was last written.
1827    ///
1828    /// This is the entry point for writing back a table that is part of the L2 cache; it
1829    /// serializes such write-backs against each other via `writeback_lock`.  Writing the table
1830    /// clears the modified flag before copying the entries, so without that lock, two concurrent
1831    /// write-backs could race such that the one with the older copy writes last, leaving that
1832    /// older copy on disk while the table no longer appears modified.  Holding the lock across the
1833    /// modified check means that a write-back which has to wait only writes again if something
1834    /// changed after the preceding write-back’s copy.
1835    ///
1836    /// `cluster` is the cluster this table is expected to belong to; an `assert!()` verifies that
1837    /// it really is the table’s cluster.
1838    pub async fn write_if_modified<S: Storage>(
1839        &self,
1840        image: &S,
1841        cluster: HostCluster,
1842    ) -> io::Result<()> {
1843        let _lock = self.writeback_lock.lock().await;
1844
1845        if !self.is_modified() {
1846            return Ok(());
1847        }
1848
1849        assert!(self.get_cluster().unwrap() == cluster);
1850        self.write(image).await
1851    }
1852}
1853
1854impl L2TableWriteGuard {
1855    /// Look up a cluster mapping.
1856    pub fn get_mapping(&self, lookup_cluster: GuestCluster) -> io::Result<L2Mapping> {
1857        self.table.get_mapping(lookup_cluster)
1858    }
1859
1860    /// Get a read guard for the given compressed cluster.
1861    ///
1862    /// The returned guard borrows this write guard, so the table cannot be modified while it is
1863    /// held.
1864    ///
1865    /// Return `None` if `cluster` is not compressed (checked under the guard).
1866    pub fn compressed_read_guard(
1867        &self,
1868        cluster: GuestCluster,
1869    ) -> io::Result<Option<CompressedClusterReadGuard<'_>>> {
1870        CompressedClusterReadGuard::new(&self.table, cluster, (&self.lock).into())
1871    }
1872
1873    /// Enter the given raw data cluster mapping into the L2 table.
1874    ///
1875    /// If the previous entry pointed to an allocated cluster, return the old allocation so its
1876    /// refcount can be decreased.
1877    ///
1878    /// If the allocation is reused, `None` is returned, so this function only returns `Some(_)` if
1879    /// some cluster is indeed leaked.
1880    ///
1881    /// If `host_cluster` has been newly allocated, `new_allocation` must be its pending clusters
1882    /// guard, which is disarmed once the mapping is in place.
1883    #[must_use = "Leaked allocation must be freed"]
1884    pub fn map_cluster(
1885        &mut self,
1886        index: usize,
1887        host_cluster: HostCluster,
1888        new_allocation: Option<PendingClusters>,
1889    ) -> Option<ClustersToFreeEntry> {
1890        if let Some(new_allocation) = new_allocation.as_ref() {
1891            debug_assert_eq!(new_allocation.cluster(), host_cluster);
1892        }
1893
1894        let new = L2Entry::from_mapping(
1895            L2Mapping::DataFile {
1896                host_cluster,
1897                copied: true,
1898            },
1899            self.table.cluster_bits,
1900        );
1901        // Safe: We set a full valid mapping, and there is only one writer (thanks to
1902        // `L2TableWriteGuard`).
1903        let l2e = unsafe { self.table.data[index].swap(new) };
1904        self.table.modified.store(true, Ordering::Relaxed);
1905        if let Some(new_allocation) = new_allocation {
1906            new_allocation.make_persistent();
1907        }
1908
1909        let allocation = l2e.allocation(self.table.cluster_bits, self.table.external_data_file);
1910        if let Some((a_cluster, a_count)) = allocation {
1911            if a_cluster == host_cluster && a_count == ClusterCount(1) {
1912                None
1913            } else {
1914                Some(ClustersToFreeEntry::data(a_cluster, a_count))
1915            }
1916        } else {
1917            None
1918        }
1919    }
1920
1921    /// Make the given index a zero mapping.
1922    ///
1923    /// If `keep_allocation` is true, keep the zero cluster pre-allocated if there is a
1924    /// pre-existing single-cluster allocation (i.e. data cluster or pre-allocated zero cluster).
1925    /// Otherwise, the existing mapping is discarded.
1926    ///
1927    /// If a previous mapping is discarded, return the old allocation so its refcount can be
1928    /// decreased.
1929    #[must_use = "Leaked allocation must be freed"]
1930    pub fn zero_cluster(
1931        &mut self,
1932        index: usize,
1933        keep_allocation: bool,
1934    ) -> io::Result<Option<ClustersToFreeEntry>> {
1935        let cluster_copied = if keep_allocation {
1936            match self.table.data[index].get().into_mapping(
1937                GuestCluster(0), // only used for backing, which we ignore
1938                self.table.cluster_bits,
1939                self.table.external_data_file,
1940            )? {
1941                L2Mapping::DataFile {
1942                    host_cluster,
1943                    copied,
1944                } => Some((host_cluster, copied)),
1945                L2Mapping::Backing { backing_offset: _ } => None,
1946                L2Mapping::Zero {
1947                    host_cluster: Some(host_cluster),
1948                    copied,
1949                } => Some((host_cluster, copied)),
1950                L2Mapping::Zero {
1951                    host_cluster: None,
1952                    copied: _,
1953                } => None,
1954                L2Mapping::Compressed {
1955                    host_offset: _,
1956                    length: _,
1957                } => None,
1958            }
1959        } else {
1960            None
1961        };
1962
1963        let retained = cluster_copied.is_some();
1964        let new = if let Some((cluster, copied)) = cluster_copied {
1965            L2Mapping::Zero {
1966                host_cluster: Some(cluster),
1967                copied,
1968            }
1969        } else {
1970            L2Mapping::Zero {
1971                host_cluster: None,
1972                copied: false,
1973            }
1974        };
1975        let new = L2Entry::from_mapping(new, self.table.cluster_bits);
1976
1977        // Safe: We set a full valid mapping, and there is only one writer (thanks to
1978        // `L2TableWriteGuard`).
1979        let old = unsafe { self.table.data[index].swap(new) };
1980        self.table.modified.store(true, Ordering::Relaxed);
1981
1982        let leaked = if !retained {
1983            old.allocation(self.table.cluster_bits, self.table.external_data_file)
1984                .map(|(first_cluster, count)| ClustersToFreeEntry::data(first_cluster, count))
1985        } else {
1986            None
1987        };
1988        Ok(leaked)
1989    }
1990
1991    /// Remove the given mapping, leaving it empty.
1992    ///
1993    /// If a previous mapping is discarded, return the old allocation so its refcount can be
1994    /// decreased.
1995    #[must_use = "Leaked allocation must be freed"]
1996    pub fn discard_cluster(&mut self, index: usize) -> Option<ClustersToFreeEntry> {
1997        let new = L2Entry(0);
1998
1999        // Safe: We set a full valid mapping, and there is only one writer (thanks to
2000        // `L2TableWriteGuard`).
2001        let old = unsafe { self.table.data[index].swap(new) };
2002        self.table.modified.store(true, Ordering::Relaxed);
2003
2004        old.allocation(self.table.cluster_bits, self.table.external_data_file)
2005            .map(|(first_cluster, count)| ClustersToFreeEntry::data(first_cluster, count))
2006    }
2007}
2008
2009impl Table for L2Table {
2010    type InternalEntry = AtomicL2Entry;
2011    type Entry = L2Entry;
2012    const NAME: &'static str = "L2 table";
2013    const MAX_ENTRIES: usize = MAX_CLUSTER_SIZE / 8;
2014
2015    fn from_data(data: Box<[AtomicL2Entry]>, header: &Header) -> Self {
2016        assert!(data.len() == header.l2_entries());
2017
2018        Self {
2019            cluster: None,
2020            data,
2021            cluster_bits: header.cluster_bits(),
2022            external_data_file: header.external_data_file(),
2023            modified: true.into(),
2024            writer_lock: Default::default(),
2025            writeback_lock: Default::default(),
2026        }
2027    }
2028
2029    fn entries(&self) -> usize {
2030        self.data.len()
2031    }
2032
2033    fn get_ref(&self, index: usize) -> Option<&AtomicL2Entry> {
2034        self.data.get(index)
2035    }
2036
2037    fn get(&self, index: usize) -> L2Entry {
2038        self.data
2039            .get(index)
2040            .map(|l2e| l2e.get())
2041            .unwrap_or(L2Entry(0))
2042    }
2043
2044    fn get_cluster(&self) -> Option<HostCluster> {
2045        self.cluster
2046    }
2047
2048    fn get_offset(&self) -> Option<HostOffset> {
2049        self.cluster.map(|index| index.offset(self.cluster_bits))
2050    }
2051
2052    fn set_cluster(&mut self, cluster: HostCluster) {
2053        self.cluster = Some(cluster);
2054        self.modified.store(true, Ordering::Relaxed);
2055    }
2056
2057    fn unset_cluster(&mut self) {
2058        self.cluster = None;
2059    }
2060
2061    fn is_modified(&self) -> bool {
2062        self.modified.load(Ordering::Relaxed)
2063    }
2064
2065    fn clear_modified(&self) {
2066        self.modified.store(false, Ordering::Relaxed);
2067    }
2068
2069    fn set_modified(&self) {
2070        self.modified.store(true, Ordering::Relaxed);
2071    }
2072
2073    fn cluster_bits(&self) -> u32 {
2074        self.cluster_bits
2075    }
2076}
2077
2078impl Clone for L2Table {
2079    fn clone(&self) -> Self {
2080        let mut data = Vec::with_capacity(self.data.len());
2081        for entry in &self.data {
2082            // None of these can be `copied`
2083            let entry = entry.get().without_copied();
2084            data.push(AtomicL2Entry(AtomicU64::new(entry.0)));
2085        }
2086
2087        let modified = AtomicBool::new(self.is_modified());
2088
2089        L2Table {
2090            cluster: None,
2091            data: data.into_boxed_slice(),
2092            cluster_bits: self.cluster_bits,
2093            external_data_file: self.external_data_file,
2094            modified,
2095            writer_lock: Default::default(),
2096            writeback_lock: Default::default(),
2097        }
2098    }
2099}
2100
2101impl Drop for L2Table {
2102    fn drop(&mut self) {
2103        if self.is_modified() {
2104            error!("L2 table dropped while modified; was the image closed before being flushed?");
2105        }
2106    }
2107}
2108
2109/// Refcount table entry.
2110#[derive(Copy, Clone, Default, Debug)]
2111pub(super) struct RefTableEntry(u64);
2112
2113impl RefTableEntry {
2114    /// Offset of the referenced refblock, if any.
2115    pub fn refblock_offset(&self) -> Option<HostOffset> {
2116        let ofs = self.0 & 0xffff_ffff_ffff_fe00u64;
2117        if ofs == 0 {
2118            None
2119        } else {
2120            Some(HostOffset(ofs))
2121        }
2122    }
2123
2124    /// Return all reserved bits.
2125    pub fn reserved_bits(&self) -> u64 {
2126        self.0 & 0x0000_0000_0000_01ffu64
2127    }
2128}
2129
2130impl TableEntry for RefTableEntry {
2131    fn try_from_plain(value: u64, header: &Header) -> io::Result<Self> {
2132        let entry = RefTableEntry(value);
2133
2134        if entry.reserved_bits() != 0 {
2135            return Err(invalid_data(format!(
2136                "Invalid reftable entry 0x{value:x}, reserved bits set (0x{:x})",
2137                entry.reserved_bits(),
2138            )));
2139        }
2140
2141        if let Some(rb_ofs) = entry.refblock_offset() {
2142            if rb_ofs.in_cluster_offset(header.cluster_bits()) != 0 {
2143                return Err(invalid_data(
2144                    format!(
2145                        "Invalid reftable entry 0x{value:x}, offset ({rb_ofs}) is not aligned to cluster size (0x{:x})",
2146                        header.cluster_size(),
2147                    ),
2148                ));
2149            }
2150        }
2151
2152        Ok(entry)
2153    }
2154
2155    fn to_plain(&self) -> u64 {
2156        self.0
2157    }
2158}
2159
2160/// Refcount table.
2161#[derive(Debug)]
2162pub(super) struct RefTable {
2163    /// First cluster in the image file.
2164    cluster: Option<HostCluster>,
2165
2166    /// Table data.
2167    data: Box<[RefTableEntry]>,
2168
2169    /// log2 of the cluster size.
2170    cluster_bits: u32,
2171
2172    /// Whether this table has been modified since it was last written.
2173    modified: AtomicBool,
2174}
2175
2176impl RefTable {
2177    /// Create a clone that covers at least `at_least_index`.
2178    ///
2179    /// Also ensure that beyond `at_least_index`, there are enough entries to self-describe the new
2180    /// refcount table (so that it can actually be allocated).
2181    pub fn clone_and_grow(&self, header: &Header, at_least_index: usize) -> io::Result<Self> {
2182        let cluster_size = header.cluster_size();
2183        let rb_entries = header.rb_entries();
2184
2185        // There surely is an optimal O(1) solution, but probably would look less clear, and this
2186        // is not a hot path.
2187        let mut extra_rbs = 1;
2188        let new_entry_count = loop {
2189            let entry_count = cmp::max(at_least_index + 1 + extra_rbs, self.data.len());
2190            let entry_count = entry_count.next_multiple_of(cluster_size / size_of::<u64>());
2191            let size = entry_count * size_of::<u64>();
2192            // Full number of clusters needed to both the new reftable *and* the `extra_rbs`
2193            let refcount_clusters = size / cluster_size + extra_rbs;
2194            let rbs_needed = refcount_clusters.div_ceil(rb_entries);
2195            if extra_rbs == rbs_needed {
2196                break entry_count;
2197            }
2198            extra_rbs = rbs_needed;
2199        };
2200
2201        if new_entry_count > <Self as Table>::MAX_ENTRIES {
2202            return Err(io::Error::other(
2203                "Cannot grow the image to this size; refcount table would become too big",
2204            ));
2205        }
2206
2207        let mut new_data = vec![RefTableEntry::default(); new_entry_count];
2208        new_data[..self.data.len()].copy_from_slice(&self.data);
2209
2210        Ok(Self {
2211            cluster: None,
2212            data: new_data.into_boxed_slice(),
2213            cluster_bits: header.cluster_bits(),
2214            modified: true.into(),
2215        })
2216    }
2217
2218    /// Check whether `index` is in bounds.
2219    pub fn in_bounds(&self, index: usize) -> bool {
2220        index < self.data.len()
2221    }
2222
2223    /// Enter the given refcount block into this refcount table.
2224    ///
2225    /// If the refblock’s cluster has been newly allocated, `new_allocation` must be its pending
2226    /// clusters guard, which is disarmed once the refblock is entered.
2227    pub fn enter_refblock(
2228        &mut self,
2229        index: usize,
2230        rb: &RefBlock,
2231        new_allocation: Option<PendingClusters>,
2232    ) -> io::Result<()> {
2233        if let Some(new_allocation) = new_allocation.as_ref() {
2234            debug_assert_eq!(Some(new_allocation.cluster()), rb.get_cluster());
2235        }
2236
2237        let rb_offset = rb.get_offset().ok_or_else(|| {
2238            io::Error::new(
2239                io::ErrorKind::InvalidInput,
2240                "Refcount block as no assigned offset",
2241            )
2242        })?;
2243
2244        let rt_entry = RefTableEntry(rb_offset.0);
2245        debug_assert!(rt_entry.reserved_bits() == 0);
2246        self.data[index] = rt_entry;
2247        self.modified.store(true, Ordering::Relaxed);
2248        if let Some(new_allocation) = new_allocation {
2249            new_allocation.make_persistent();
2250        }
2251
2252        Ok(())
2253    }
2254}
2255
2256impl Table for RefTable {
2257    type InternalEntry = RefTableEntry;
2258    type Entry = RefTableEntry;
2259    const NAME: &'static str = "Refcount table";
2260
2261    /// Maximum number of refcount table entries.
2262    ///
2263    /// Not in QEMU, but makes sense to limit to the same as the L1 table.  Note that refcount
2264    /// blocks usually cover more clusters than an L2 table, so this generally allows larger image
2265    /// files than would be necessary for the maximum guest disk size determined by the maximum
2266    /// number of L1 entries.
2267    const MAX_ENTRIES: usize = <L1Table as Table>::MAX_ENTRIES;
2268
2269    fn from_data(data: Box<[RefTableEntry]>, header: &Header) -> Self {
2270        Self {
2271            cluster: None,
2272            data,
2273            cluster_bits: header.cluster_bits(),
2274            modified: true.into(),
2275        }
2276    }
2277
2278    fn entries(&self) -> usize {
2279        self.data.len()
2280    }
2281
2282    fn get_ref(&self, index: usize) -> Option<&RefTableEntry> {
2283        self.data.get(index)
2284    }
2285
2286    fn get(&self, index: usize) -> RefTableEntry {
2287        self.data.get(index).copied().unwrap_or(RefTableEntry(0))
2288    }
2289
2290    fn get_cluster(&self) -> Option<HostCluster> {
2291        self.cluster
2292    }
2293
2294    fn get_offset(&self) -> Option<HostOffset> {
2295        self.cluster.map(|index| index.offset(self.cluster_bits))
2296    }
2297
2298    fn set_cluster(&mut self, cluster: HostCluster) {
2299        self.cluster = Some(cluster);
2300        self.modified.store(true, Ordering::Relaxed);
2301    }
2302
2303    fn unset_cluster(&mut self) {
2304        self.cluster = None;
2305    }
2306
2307    fn is_modified(&self) -> bool {
2308        self.modified.load(Ordering::Relaxed)
2309    }
2310
2311    fn clear_modified(&self) {
2312        self.modified.store(false, Ordering::Relaxed);
2313    }
2314
2315    fn set_modified(&self) {
2316        self.modified.store(true, Ordering::Relaxed);
2317    }
2318
2319    fn cluster_bits(&self) -> u32 {
2320        self.cluster_bits
2321    }
2322}
2323
2324/// Refcount block.
2325pub(super) struct RefBlock {
2326    /// Cluster in the image file.
2327    cluster: Option<HostCluster>,
2328
2329    /// Raw table data (big endian).
2330    raw_data: IoBuffer,
2331
2332    /// log2 of the refcount bits.
2333    refcount_order: u32,
2334
2335    /// log2 of the cluster size.
2336    cluster_bits: u32,
2337
2338    /// Whether this block has been modified since it was last written.
2339    modified: AtomicBool,
2340
2341    /// Lock for creating `RefBlockWriteGuard`.
2342    writer_lock: Mutex<()>,
2343
2344    /// Serializes writing this block back to disk (see [`RefBlock::write_if_modified()`]).
2345    writeback_lock: Mutex<()>,
2346}
2347
2348/// Write guard for a refblock.
2349pub(super) struct RefBlockWriteGuard<'a> {
2350    /// Referenced refblock.
2351    rb: &'a RefBlock,
2352
2353    /// Held guard mutex on that refblock.
2354    _lock: MutexGuard<'a, ()>,
2355}
2356
2357#[maybe_async]
2358impl RefBlock {
2359    /// Create a new zeroed refcount block.
2360    pub fn new_cleared<S: Storage>(for_image: &S, header: &Header) -> io::Result<Self> {
2361        let mut raw_data = IoBuffer::new(header.cluster_size(), for_image.mem_align())?;
2362        raw_data.as_mut().into_slice().fill(0);
2363
2364        Ok(RefBlock {
2365            cluster: None,
2366            raw_data,
2367            refcount_order: header.refcount_order(),
2368            cluster_bits: header.cluster_bits(),
2369            modified: true.into(),
2370            writer_lock: Default::default(),
2371            writeback_lock: Default::default(),
2372        })
2373    }
2374
2375    /// Load a refcount block from disk.
2376    pub async fn load<S: Storage>(
2377        image: &S,
2378        header: &Header,
2379        cluster: HostCluster,
2380    ) -> io::Result<Self> {
2381        let cluster_bits = header.cluster_bits();
2382        let cluster_size = 1 << cluster_bits;
2383        let refcount_order = header.refcount_order();
2384        let offset = cluster.offset(cluster_bits);
2385
2386        check_table(
2387            "Refcount block",
2388            offset.0,
2389            cluster_size,
2390            1,
2391            MAX_CLUSTER_SIZE,
2392            cluster_size,
2393        )?;
2394
2395        let mut raw_data =
2396            IoBuffer::new(cluster_size, cmp::max(image.mem_align(), size_of::<u64>()))?;
2397        image.read(&mut raw_data, offset.0).await?;
2398
2399        Ok(RefBlock {
2400            cluster: Some(cluster),
2401            raw_data,
2402            refcount_order,
2403            cluster_bits,
2404            modified: false.into(),
2405            writer_lock: Default::default(),
2406            writeback_lock: Default::default(),
2407        })
2408    }
2409
2410    /// Copy data from `self.raw_data` using the `get` function.
2411    ///
2412    /// `mem_align` is the minimum memory alignment for the returned buffer.
2413    fn copy_raw_data<T: Copy + Sized, F: Fn(*const T) -> T>(
2414        &self,
2415        mem_align: usize,
2416        get: F,
2417    ) -> io::Result<IoBuffer> {
2418        // Check that `T` is the right type
2419        assert!(cmp::max((1 << self.refcount_order) / 8, 1) == size_of::<T>());
2420
2421        let byte_size = 1 << self.cluster_bits;
2422        let mut buffer = IoBuffer::new(byte_size, cmp::max(mem_align, size_of::<T>()))?;
2423
2424        // Safe because this is the right access type
2425        let raw_in = unsafe { self.raw_data.as_ref().into_typed_slice::<T>() };
2426        // Safe because we have just allocated this, and it fits the alignment
2427        let raw_out = unsafe { buffer.as_mut().into_typed_slice::<T>() };
2428
2429        for (i, value) in raw_in.iter().enumerate() {
2430            raw_out[i] = get(value as *const T);
2431        }
2432
2433        Ok(buffer)
2434    }
2435
2436    /// Write a refcount block to disk.
2437    pub async fn write<S: Storage>(&self, image: &S) -> io::Result<()> {
2438        let offset = self
2439            .get_offset()
2440            .ok_or_else(|| io::Error::other("Cannot write qcow2 refcount block, no offset set"))?;
2441
2442        self.clear_modified();
2443        // Re-set the modified flag if we don’t get to execute the write
2444        let mut modified_guard = DropGuard::new(|| self.set_modified());
2445
2446        let buffer = match self.refcount_order {
2447            // refcount_bits == 1, 2, 4, 8
2448            0..=3 => self.copy_raw_data::<u8, _>(image.mem_align(), |ptr| {
2449                // Safe because we only read
2450                unsafe { AtomicU8::from_ptr(ptr as *mut u8) }.load(Ordering::Relaxed)
2451            })?,
2452
2453            // refcount_bits == 16
2454            4 => self.copy_raw_data::<u16, _>(image.mem_align(), |ptr| {
2455                // Safe because we only read
2456                unsafe { AtomicU16::from_ptr(ptr as *mut u16) }.load(Ordering::Relaxed)
2457            })?,
2458
2459            // refcount_bits == 32
2460            5 => self.copy_raw_data::<u32, _>(image.mem_align(), |ptr| {
2461                // Safe because we only read
2462                unsafe { AtomicU32::from_ptr(ptr as *mut u32) }.load(Ordering::Relaxed)
2463            })?,
2464
2465            // refcount_bits == 64
2466            6 => self.copy_raw_data::<u64, _>(image.mem_align(), |ptr| {
2467                // Safe because we only read
2468                unsafe { AtomicU64::from_ptr(ptr as *mut u64) }.load(Ordering::Relaxed)
2469            })?,
2470
2471            _ => unreachable!(),
2472        };
2473
2474        image.write(&buffer, offset.0).await?;
2475        // Write executed, disarm guard, flag can remaing cleared
2476        modified_guard.disarm();
2477
2478        Ok(())
2479    }
2480
2481    /// Get the block’s cluster in the image file.
2482    pub fn get_cluster(&self) -> Option<HostCluster> {
2483        self.cluster
2484    }
2485
2486    /// Get the block’s offset in the image file.
2487    pub fn get_offset(&self) -> Option<HostOffset> {
2488        self.cluster.map(|index| index.offset(self.cluster_bits))
2489    }
2490
2491    /// Change the block’s cluster in the image file (for writing).
2492    pub fn set_cluster(&mut self, cluster: HostCluster) {
2493        self.cluster = Some(cluster);
2494        self.set_modified();
2495    }
2496
2497    /// Calculate sub-byte refcount access parameters.
2498    ///
2499    /// For a given refcount index, return its:
2500    /// - byte index,
2501    /// - access mask,
2502    /// - in-byte shift.
2503    fn sub_byte_refcount_access(&self, index: usize) -> (usize, u8, usize) {
2504        let order = self.refcount_order;
2505        debug_assert!(order < 3);
2506
2507        // Note that `order` is in bits, i.e. `1 << order` is the number of bits.  `index` is in
2508        // units of refcounts, so `index << order` is the bit index, and `index << (order - 3)` is
2509        // then the byte index, which is equal to `index >> (3 - order)`.
2510        let byte_index = index >> (3 - order);
2511        // `1 << order` is the bits per refcount (bprc), so `(1 << bprc) - 1` is the mask for one
2512        // refcount (its maximum value).
2513        let mask = (1 << (1 << order)) - 1;
2514        // `index` is in units of refcounts, so `index << order` is the bit index.  `% 8`, we get
2515        // the base index inside of a byte.
2516        let shift = (index << order) % 8;
2517
2518        (byte_index, mask, shift)
2519    }
2520
2521    /// Get the given cluster’s refcount.
2522    pub fn get(&self, index: usize) -> u64 {
2523        match self.refcount_order {
2524            // refcount_bits == 1, 2, 4
2525            0..=2 => {
2526                let (index, mask, shift) = self.sub_byte_refcount_access(index);
2527                let raw_data_slice = unsafe { self.raw_data.as_ref().into_typed_slice::<u8>() };
2528                let atomic =
2529                    unsafe { AtomicU8::from_ptr(&raw_data_slice[index] as *const u8 as *mut u8) };
2530                ((atomic.load(Ordering::Relaxed) >> shift) & mask) as u64
2531            }
2532
2533            // refcount_bits == 8
2534            3 => {
2535                let raw_data_slice = unsafe { self.raw_data.as_ref().into_typed_slice::<u8>() };
2536                let atomic =
2537                    unsafe { AtomicU8::from_ptr(&raw_data_slice[index] as *const u8 as *mut u8) };
2538                atomic.load(Ordering::Relaxed) as u64
2539            }
2540
2541            // refcount_bits == 16
2542            4 => {
2543                let raw_data_slice = unsafe { self.raw_data.as_ref().into_typed_slice::<u16>() };
2544                let atomic = unsafe {
2545                    AtomicU16::from_ptr(&raw_data_slice[index] as *const u16 as *mut u16)
2546                };
2547                u16::from_be(atomic.load(Ordering::Relaxed)) as u64
2548            }
2549
2550            // refcount_bits == 32
2551            5 => {
2552                let raw_data_slice = unsafe { self.raw_data.as_ref().into_typed_slice::<u32>() };
2553                let atomic = unsafe {
2554                    AtomicU32::from_ptr(&raw_data_slice[index] as *const u32 as *mut u32)
2555                };
2556                u32::from_be(atomic.load(Ordering::Relaxed)) as u64
2557            }
2558
2559            // refcount_bits == 64
2560            6 => {
2561                let raw_data_slice = unsafe { self.raw_data.as_ref().into_typed_slice::<u64>() };
2562                let atomic = unsafe {
2563                    AtomicU64::from_ptr(&raw_data_slice[index] as *const u64 as *mut u64)
2564                };
2565                u64::from_be(atomic.load(Ordering::Relaxed))
2566            }
2567
2568            _ => unreachable!(),
2569        }
2570    }
2571
2572    /// Allow modifying this refcount block.
2573    ///
2574    /// Note that readers are allowed to exist while modifications are happening.
2575    pub async fn lock_write(&self) -> RefBlockWriteGuard<'_> {
2576        RefBlockWriteGuard {
2577            rb: self,
2578            _lock: self.writer_lock.lock().await,
2579        }
2580    }
2581
2582    /// Write this block to disk if it has been modified since it was last written.
2583    ///
2584    /// This is the entry point for writing back a block that is part of the refblock cache; it
2585    /// serializes such write-backs against each other via `writeback_lock`.  Writing the block
2586    /// clears the modified flag before copying the data, so without that lock, two concurrent
2587    /// write-backs could race such that the one with the older copy writes last, leaving that
2588    /// older copy on disk while the block no longer appears modified.  Holding the lock across the
2589    /// modified check means that a write-back which has to wait only writes again if something
2590    /// changed after the preceding write-back’s copy.
2591    ///
2592    /// `cluster` is the cluster this block is expected to belong to; an `assert!()` verifies that
2593    /// it really is the block’s cluster.
2594    pub async fn write_if_modified<S: Storage>(
2595        &self,
2596        image: &S,
2597        cluster: HostCluster,
2598    ) -> io::Result<()> {
2599        let _lock = self.writeback_lock.lock().await;
2600
2601        if !self.is_modified() {
2602            return Ok(());
2603        }
2604
2605        assert!(self.get_cluster().unwrap() == cluster);
2606        self.write(image).await
2607    }
2608
2609    /// Check whether this block has been modified since it was last written.
2610    pub fn is_modified(&self) -> bool {
2611        self.modified.load(Ordering::Relaxed)
2612    }
2613
2614    /// Clear the modified flag.
2615    pub fn clear_modified(&self) {
2616        self.modified.store(false, Ordering::Relaxed);
2617    }
2618
2619    /// Set the modified flag.
2620    pub fn set_modified(&self) {
2621        self.modified.store(true, Ordering::Relaxed);
2622    }
2623
2624    /// Check whether the given cluster’s refcount is 0.
2625    pub fn is_zero(&self, index: usize) -> bool {
2626        self.get(index) == 0
2627    }
2628}
2629
2630impl RefBlockWriteGuard<'_> {
2631    /// # Safety
2632    /// Caller must ensure there are no concurrent writers.
2633    unsafe fn fetch_update_bitset(
2634        bitset: &AtomicU8,
2635        change: i64,
2636        base_mask: u8,
2637        shift: usize,
2638    ) -> io::Result<u64> {
2639        let mask = base_mask << shift;
2640
2641        // load + store is OK without concurrent writers
2642        let full = bitset.load(Ordering::Relaxed);
2643        let old = (full & mask) >> shift;
2644        let new = if change > 0 {
2645            let change = change.try_into().map_err(|_| {
2646                io::Error::new(
2647                    io::ErrorKind::InvalidInput,
2648                    format!("Requested refcount change of {change} is too big for the image’s refcount width"),
2649                )
2650            })?;
2651            old.checked_add(change)
2652        } else {
2653            let change = (-change).try_into().map_err(|_| {
2654                io::Error::new(
2655                    io::ErrorKind::InvalidInput,
2656                    format!("Requested refcount change of {change} is too big for the image’s refcount width"),
2657                )
2658            })?;
2659            old.checked_sub(change)
2660        };
2661        let new = new.ok_or_else(|| {
2662            invalid_data(format!(
2663                "Changing refcount from {old} by {change} would overflow"
2664            ))
2665        })?;
2666        if new > base_mask {
2667            return Err(invalid_data(format!(
2668                "Changing refcount from {old} to {new} (by {change}) would overflow"
2669            )));
2670        }
2671
2672        let full = (full & !mask) | (new << shift);
2673        bitset.store(full, Ordering::Relaxed);
2674        Ok(old as u64)
2675    }
2676
2677    /// # Safety
2678    /// Caller must ensure there are no concurrent writers.
2679    unsafe fn fetch_update_full<
2680        T,
2681        L: FnOnce(&T) -> u64,
2682        S: FnOnce(&T, u64) -> Result<(), TryFromIntError>,
2683    >(
2684        atomic: &T,
2685        change: i64,
2686        load: L,
2687        store: S,
2688    ) -> io::Result<u64> {
2689        // load + store is OK without concurrent writers
2690        let old = load(atomic);
2691
2692        let new = if change > 0 {
2693            old.checked_add(change as u64)
2694        } else {
2695            old.checked_sub(-change as u64)
2696        };
2697        let new = new.ok_or_else(|| {
2698            invalid_data(format!(
2699                "Changing refcount from {old} by {change} would overflow"
2700            ))
2701        })?;
2702
2703        store(atomic, new).map_err(|_| {
2704            invalid_data(format!(
2705                "Changing refcount from {old} to {new} (by {change}) would overflow"
2706            ))
2707        })?;
2708
2709        Ok(old)
2710    }
2711
2712    /// Modify the given cluster’s refcount.
2713    fn modify(&mut self, index: usize, change: i64) -> io::Result<u64> {
2714        let result = match self.rb.refcount_order {
2715            // refcount_bits == 1, 2, 4
2716            0..=2 => {
2717                let (index, mask, shift) = self.rb.sub_byte_refcount_access(index);
2718                let raw_data_slice = unsafe { self.rb.raw_data.as_ref().into_typed_slice::<u8>() };
2719                let atomic =
2720                    unsafe { AtomicU8::from_ptr(&raw_data_slice[index] as *const u8 as *mut u8) };
2721                // Safe: `RefBlockWriteGuard` ensures there are no concurrent writers.
2722                unsafe { Self::fetch_update_bitset(atomic, change, mask, shift) }
2723            }
2724
2725            // refcount_bits == 8
2726            3 => {
2727                let raw_data_slice = unsafe { self.rb.raw_data.as_ref().into_typed_slice::<u8>() };
2728                let atomic =
2729                    unsafe { AtomicU8::from_ptr(&raw_data_slice[index] as *const u8 as *mut u8) };
2730                // Safe: `RefBlockWriteGuard` ensures there are no concurrent writers.
2731                unsafe {
2732                    Self::fetch_update_full(
2733                        atomic,
2734                        change,
2735                        |a| a.load(Ordering::Relaxed) as u64,
2736                        |a, v| {
2737                            a.store(v.try_into()?, Ordering::Relaxed);
2738                            Ok(())
2739                        },
2740                    )
2741                }
2742            }
2743
2744            // refcount_bits == 16
2745            4 => {
2746                let raw_data_slice = unsafe { self.rb.raw_data.as_ref().into_typed_slice::<u16>() };
2747                let atomic = unsafe {
2748                    AtomicU16::from_ptr(&raw_data_slice[index] as *const u16 as *mut u16)
2749                };
2750                unsafe {
2751                    Self::fetch_update_full(
2752                        atomic,
2753                        change,
2754                        |a| u16::from_be(a.load(Ordering::Relaxed)) as u64,
2755                        |a, v| {
2756                            a.store(u16::try_from(v)?.to_be(), Ordering::Relaxed);
2757                            Ok(())
2758                        },
2759                    )
2760                }
2761            }
2762
2763            // refcount_bits == 32
2764            5 => {
2765                let raw_data_slice = unsafe { self.rb.raw_data.as_ref().into_typed_slice::<u32>() };
2766                let atomic = unsafe {
2767                    AtomicU32::from_ptr(&raw_data_slice[index] as *const u32 as *mut u32)
2768                };
2769                unsafe {
2770                    Self::fetch_update_full(
2771                        atomic,
2772                        change,
2773                        |a| u32::from_be(a.load(Ordering::Relaxed)) as u64,
2774                        |a, v| {
2775                            a.store(u32::try_from(v)?.to_be(), Ordering::Relaxed);
2776                            Ok(())
2777                        },
2778                    )
2779                }
2780            }
2781
2782            // refcount_bits == 64
2783            6 => {
2784                let raw_data_slice = unsafe { self.rb.raw_data.as_ref().into_typed_slice::<u64>() };
2785                let atomic = unsafe {
2786                    AtomicU64::from_ptr(&raw_data_slice[index] as *const u64 as *mut u64)
2787                };
2788                unsafe {
2789                    Self::fetch_update_full(
2790                        atomic,
2791                        change,
2792                        |a| u64::from_be(a.load(Ordering::Relaxed)),
2793                        |a, v| {
2794                            a.store(v.to_be(), Ordering::Relaxed);
2795                            Ok(())
2796                        },
2797                    )
2798                }
2799            }
2800
2801            _ => unreachable!(),
2802        };
2803
2804        let result = result?;
2805        self.rb.modified.store(true, Ordering::Relaxed);
2806        Ok(result)
2807    }
2808
2809    /// Increment the given cluster’s refcount.
2810    ///
2811    /// Returns the old value.
2812    pub fn increment(&mut self, index: usize) -> io::Result<u64> {
2813        self.modify(index, 1)
2814    }
2815
2816    /// Decrement the given cluster’s refcount.
2817    ///
2818    /// Returns the old value.
2819    pub fn decrement(&mut self, index: usize) -> io::Result<u64> {
2820        self.modify(index, -1)
2821    }
2822
2823    /// Check whether the given cluster’s refcount is 0.
2824    pub fn is_zero(&self, index: usize) -> bool {
2825        self.rb.is_zero(index)
2826    }
2827}
2828
2829impl Drop for RefBlock {
2830    fn drop(&mut self) {
2831        if self.is_modified() {
2832            error!(
2833                "Refcount block dropped while modified; was the image closed before being flushed?"
2834            );
2835        }
2836    }
2837}
2838
2839/// Generic trait for qcow2 table entries (L1, L2, refcount table).
2840pub trait TableEntry
2841where
2842    Self: Sized,
2843{
2844    /// Load the given raw value, checking it for validity.
2845    fn try_from_plain(value: u64, header: &Header) -> io::Result<Self>;
2846
2847    /// Return the contained raw value.
2848    fn to_plain(&self) -> u64;
2849}
2850
2851/// Generic trait for qcow2 metadata tables (L1, L2, refcount table).
2852#[maybe_async(AFIT)]
2853pub trait Table: Sized {
2854    /// Internal type for each table entry.
2855    type InternalEntry: TableEntry;
2856    /// Externally visible type for each table entry.
2857    type Entry: Copy;
2858    /// User-readable struct name.
2859    const NAME: &'static str;
2860    /// Maximum allowable number of entries.
2861    const MAX_ENTRIES: usize;
2862
2863    /// Create a new table with the given contents
2864    fn from_data(data: Box<[Self::InternalEntry]>, header: &Header) -> Self;
2865
2866    /// Number of entries.
2867    fn entries(&self) -> usize;
2868    /// Get the given entry (as reference).
2869    fn get_ref(&self, index: usize) -> Option<&Self::InternalEntry>;
2870    /// Get the given entry (copied).
2871    fn get(&self, index: usize) -> Self::Entry;
2872    /// Get this table’s (first) cluster in the image file.
2873    fn get_cluster(&self) -> Option<HostCluster>;
2874    /// Get this table’s offset in the image file.
2875    fn get_offset(&self) -> Option<HostOffset>;
2876    /// Set this table’s (first) cluster in the image file (for writing).
2877    fn set_cluster(&mut self, cluster: HostCluster);
2878    /// Remove the table’s association with any cluster in the image file.
2879    fn unset_cluster(&mut self);
2880
2881    /// Return log2 of the cluster size.
2882    ///
2883    /// All tables store this anyway.
2884    fn cluster_bits(&self) -> u32;
2885
2886    /// Check whether this table has been modified since it was last written.
2887    fn is_modified(&self) -> bool;
2888    /// Clear the modified flag.
2889    fn clear_modified(&self);
2890    /// Set the modified flag.
2891    fn set_modified(&self);
2892
2893    /// Table size in bytes.
2894    fn byte_size(&self) -> usize {
2895        self.entries() * size_of::<u64>()
2896    }
2897
2898    /// Number of clusters used by this table.
2899    fn cluster_count(&self) -> ClusterCount {
2900        ClusterCount::from_byte_size(self.byte_size() as u64, self.cluster_bits())
2901    }
2902
2903    /// Load a table from the image file.
2904    async fn load<S: Storage>(
2905        image: &S,
2906        header: &Header,
2907        cluster: HostCluster,
2908        entries: usize,
2909    ) -> io::Result<Self> {
2910        let offset = cluster.offset(header.cluster_bits());
2911
2912        check_table(
2913            Self::NAME,
2914            offset.0,
2915            entries,
2916            size_of::<u64>(),
2917            Self::MAX_ENTRIES,
2918            header.cluster_size(),
2919        )?;
2920
2921        let byte_size = entries * size_of::<u64>();
2922        let mut buffer = IoBuffer::new(byte_size, cmp::max(image.mem_align(), size_of::<u64>()))?;
2923
2924        image.read(&mut buffer, offset.0).await?;
2925
2926        // Safe because `u64` is a plain type, and the alignment fits
2927        let raw_table = unsafe { buffer.as_ref().into_typed_slice::<u64>() };
2928
2929        let mut table = Vec::<Self::InternalEntry>::with_capacity(entries);
2930        for be_value in raw_table {
2931            table.push(Self::InternalEntry::try_from_plain(
2932                u64::from_be(*be_value),
2933                header,
2934            )?)
2935        }
2936
2937        let mut table = Self::from_data(table.into_boxed_slice(), header);
2938        table.set_cluster(cluster);
2939        table.clear_modified();
2940        Ok(table)
2941    }
2942
2943    /// Write a table to the image file.
2944    ///
2945    /// Callers must ensure the table is copied, i.e. its refcount is 1.
2946    async fn write<S: Storage>(&self, image: &S) -> io::Result<()> {
2947        let offset = self
2948            .get_offset()
2949            .ok_or_else(|| io::Error::other("Cannot write qcow2 metadata table, no offset set"))?;
2950
2951        check_table(
2952            Self::NAME,
2953            offset.0,
2954            self.entries(),
2955            size_of::<u64>(),
2956            Self::MAX_ENTRIES,
2957            1 << self.cluster_bits(),
2958        )?;
2959
2960        let byte_size = self.byte_size();
2961        let mut buffer = IoBuffer::new(byte_size, cmp::max(image.mem_align(), size_of::<u64>()))?;
2962
2963        self.clear_modified();
2964        // Re-set the modified flag if we don’t get to execute the write
2965        let mut modified_guard = DropGuard::new(|| self.set_modified());
2966
2967        // Safe because we have just allocated this, and it fits the alignment
2968        let raw_table = unsafe { buffer.as_mut().into_typed_slice::<u64>() };
2969        for (i, be_value) in raw_table.iter_mut().enumerate() {
2970            // 0 always works, that’s by design.
2971            *be_value = self.get_ref(i).map(|e| e.to_plain()).unwrap_or(0).to_be();
2972        }
2973
2974        image.write(&buffer, offset.0).await?;
2975        // Write executed, disarm guard, flag can remaing cleared
2976        modified_guard.disarm();
2977
2978        Ok(())
2979    }
2980
2981    /// Write at least the given single (modified) entry to the image file.
2982    ///
2983    /// Potentially writes more of the table, if alignment requirements ask for that.
2984    async fn write_entry<S: Storage>(&self, image: &S, index: usize) -> io::Result<()> {
2985        // If the storage alignment does not exceed the cluster size, we can prevent a generic
2986        // storage-layer RMW by making an aligned write of the fully aligned portion of the table
2987        // that includes the requested index.
2988        // Otherwise, it would be unsafe because the tail of the table may not be a full alignment
2989        // block (so we cannot access it this way), and it would also be rather useless because we
2990        // do not even know if the table’s first cluster is aligned anyway. So fall back to
2991        // storage-layer RMW then.
2992        let alignment = image
2993            .mem_align()
2994            .max(image.req_align())
2995            .max(size_of::<u64>());
2996        let cluster_size = 1 << self.cluster_bits();
2997        let alignment = if alignment <= cluster_size {
2998            alignment
2999        } else {
3000            size_of::<u64>()
3001        };
3002        let entries_to_write = alignment / size_of::<u64>();
3003
3004        let offset = self
3005            .get_offset()
3006            .ok_or_else(|| io::Error::other("Cannot write qcow2 metadata table, no offset set"))?;
3007
3008        check_table(
3009            Self::NAME,
3010            offset.0,
3011            self.entries(),
3012            size_of::<u64>(),
3013            Self::MAX_ENTRIES,
3014            cluster_size,
3015        )?;
3016
3017        let mut buffer = IoBuffer::new(alignment, cmp::max(image.mem_align(), size_of::<u64>()))?;
3018
3019        // Safe because we have just allocated this, and it fits the alignment
3020        let raw_entries = unsafe { buffer.as_mut().into_typed_slice::<u64>() };
3021        let first_index = (index / entries_to_write) * entries_to_write;
3022        #[allow(clippy::needless_range_loop)]
3023        for i in 0..entries_to_write {
3024            // 0 always works, that’s by design.
3025            raw_entries[i] = self
3026                .get_ref(first_index + i)
3027                .map(|e| e.to_plain())
3028                .unwrap_or(0)
3029                .to_be();
3030        }
3031
3032        image
3033            .write(&buffer, offset.0 + (first_index * size_of::<u64>()) as u64)
3034            .await
3035    }
3036}
3037
3038/// Check whether the given table offset/size is valid.
3039///
3040/// Also works for refcount blocks (with cheating, because their entry size can be less than a
3041/// byte), which is why it is outside of [`Table`].
3042fn check_table(
3043    name: &str,
3044    offset: u64,
3045    entries: usize,
3046    entry_size: usize,
3047    max_entries: usize,
3048    cluster_size: usize,
3049) -> io::Result<()> {
3050    if entries > max_entries {
3051        return Err(invalid_data(format!(
3052            "{name} too big: {entries} > {max_entries}",
3053        )));
3054    }
3055
3056    if !offset.is_multiple_of(cluster_size as u64) {
3057        return Err(invalid_data(format!("{name}: Unaligned offset: {offset}")));
3058    }
3059
3060    let byte_size = entries
3061        .checked_mul(entry_size)
3062        .ok_or_else(|| invalid_data(format!("{name} size overflow: {entries} * {entry_size}")))?;
3063    let end_offset = offset
3064        .checked_add(byte_size as u64)
3065        .ok_or_else(|| invalid_data(format!("{name} offset overflow: {offset} + {byte_size}")))?;
3066    if end_offset > MAX_FILE_LENGTH {
3067        return Err(invalid_data(format!(
3068            "{name}: Invalid end offset: {end_offset} > {MAX_FILE_LENGTH}"
3069        )));
3070    }
3071
3072    Ok(())
3073}
3074
3075/// Return a byte buffer for `val`.
3076fn encode_binary<T: OnDiskStruct>(val: &T) -> io::Result<Vec<u8>> {
3077    let mut vec = vec![0; T::ON_DISK_SIZE];
3078    val.store_to(&mut vec)?;
3079    Ok(vec)
3080}
3081
3082/// Decode `T` from the given byte buffer.
3083fn decode_binary<T: OnDiskStruct>(slice: &[u8]) -> io::Result<T> {
3084    T::load_from(slice)
3085}
3086
3087#[cfg(test)]
3088mod tests {
3089    use super::*;
3090    use crate::test::mem_storage::{MemStorage, MemStorageState};
3091    use parking_lot::Mutex;
3092    use std::sync::Arc;
3093
3094    /// Regression test: If the metadata file has alignment requirements larger than the cluster
3095    /// size, then `write_entry()` must not try to up-align the request to that, because having the
3096    /// request span multiple clusters is a very bad idea then, as this might overwrite clusters
3097    /// adjacent to the table.
3098    /// This test is such a case:
3099    /// - Alignment is 4096
3100    /// - Cluster size is 512
3101    /// - The L1 table spans 5 clusters (from cluster 8 == 4096B onwards), that is, 2560B
3102    ///
3103    /// The old `write_entry()` tried to be clever and reduce the alignment it used such that it
3104    /// would not exceed the table length, i.e. 2048B in this case, but that is (a) very unhelpful
3105    /// because 2048 is just less than 4096, so does not help at all, and (b) will still be wrong
3106    /// for the unaligned tail of the L1 table.
3107    ///
3108    /// So this test verifies that writing into that tail (cluster 4 of the L1 table) will not
3109    /// overwrite anything past the L1 table.
3110    #[maybe_async::test(feature = "sync", async(feature = "async", tokio::test))]
3111    async fn l1_write_entry_beyond_table_end() {
3112        let uninitialized: u8 = 0xa5;
3113
3114        let header = Header::new(9, 4, None, None, None);
3115        let state = Arc::new(MemStorageState {
3116            data: Mutex::new(vec![uninitialized; 16384]),
3117            ..Default::default()
3118        });
3119        let mem = MemStorage::new(Arc::clone(&state), true).with_align(4096);
3120
3121        let l1_cluster_start = 8;
3122        let l1_offset = l1_cluster_start * 512;
3123        let l1_entries = 320;
3124        let l1_byte_size = l1_entries * size_of::<u64>();
3125
3126        // Index 300 is at offset 2400, i.e. well into cluster 4 (which begins at index 256)
3127        let test_index = 300;
3128        let test_entry = (1 << 63) | 0x10000;
3129
3130        let mut data = vec![L1Entry::default(); l1_entries];
3131        data[test_index] = L1Entry::try_from_plain(test_entry, &header).unwrap();
3132        let mut l1 = L1Table::from_data(data.into_boxed_slice(), &header);
3133        l1.set_cluster(HostCluster(l1_cluster_start as u64));
3134
3135        l1.write_entry(&mem, test_index).await.unwrap();
3136
3137        // Verify the entry was written
3138        let data = state.data.lock();
3139        let offset = l1_offset + test_index * size_of::<u64>();
3140        let entry = u64::from_be_bytes(
3141            data[offset..(offset + size_of::<u64>())]
3142                .try_into()
3143                .unwrap(),
3144        );
3145        assert_eq!(entry, test_entry);
3146
3147        // Verify nothing past the L1 table was clobbered
3148        let offset_past = l1_offset + l1_byte_size;
3149        let next_alignment_boundary = offset_past.next_multiple_of(4096);
3150        let clobbered = data[offset_past..next_alignment_boundary]
3151            .iter()
3152            .filter(|b| **b != uninitialized)
3153            .count();
3154        assert_eq!(clobbered, 0, "bytes after the L1 table were overwritten");
3155    }
3156}