disk/qcow/
mod.rs

1// Copyright 2018 The ChromiumOS Authors
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5mod qcow_raw_file;
6mod refcount;
7mod vec_cache;
8
9use std::cmp::max;
10use std::cmp::min;
11use std::fs::File;
12use std::io;
13use std::io::Read;
14use std::io::Seek;
15use std::io::SeekFrom;
16use std::io::Write;
17use std::mem::size_of;
18use std::path::PathBuf;
19use std::str;
20
21use base::error;
22use base::AsRawDescriptor;
23use base::AsRawDescriptors;
24use base::FileAllocate;
25use base::FileReadWriteAtVolatile;
26use base::FileSetLen;
27use base::FileSync;
28use base::PunchHole;
29use base::RawDescriptor;
30use base::VolatileMemory;
31use base::VolatileSlice;
32use base::WriteZeroesAt;
33use cros_async::Executor;
34use libc::EINVAL;
35use libc::ENOSPC;
36use libc::ENOTSUP;
37use remain::sorted;
38use sync::Mutex;
39use thiserror::Error;
40
41use crate::asynchronous::DiskFlush;
42use crate::qcow::qcow_raw_file::QcowRawFile;
43use crate::qcow::refcount::RefCount;
44use crate::qcow::vec_cache::CacheMap;
45use crate::qcow::vec_cache::Cacheable;
46use crate::qcow::vec_cache::VecCache;
47use crate::AsyncDisk;
48use crate::AsyncDiskFileWrapper;
49use crate::DiskFile;
50use crate::DiskFileParams;
51use crate::DiskGetLen;
52use crate::ToAsyncDisk;
53
54#[sorted]
55#[derive(Error, Debug)]
56pub enum Error {
57    #[error("backing file path escapes the first cluster")]
58    BackingFileEscapesFirstCluster,
59    #[error("backing file io error: {0}")]
60    BackingFileIo(io::Error),
61    #[error("backing file open error: {0}")]
62    BackingFileOpen(Box<crate::Error>),
63    #[error("backing file name is too long: {0} bytes over")]
64    BackingFileTooLong(usize),
65    #[error("compressed blocks not supported")]
66    CompressedBlocksNotSupported,
67    #[error("failed to evict cache: {0}")]
68    EvictingCache(io::Error),
69    #[error("file larger than max of {MAX_QCOW_FILE_SIZE}: {0}")]
70    FileTooBig(u64),
71    #[error("failed to get file size: {0}")]
72    GettingFileSize(io::Error),
73    #[error("failed to get refcount: {0}")]
74    GettingRefcount(refcount::Error),
75    #[error("failed to parse filename: {0}")]
76    InvalidBackingFileName(str::Utf8Error),
77    #[error("invalid cluster index")]
78    InvalidClusterIndex,
79    #[error("invalid cluster size")]
80    InvalidClusterSize,
81    #[error("failed to parse header extension")]
82    InvalidHeaderExtension,
83    #[error("invalid index")]
84    InvalidIndex,
85    #[error("invalid L1 table offset")]
86    InvalidL1TableOffset,
87    #[error("invalid L1 table size {0}")]
88    InvalidL1TableSize(u32),
89    #[error("invalid magic")]
90    InvalidMagic,
91    #[error("invalid offset")]
92    InvalidOffset(u64),
93    #[error("invalid refcount table offset")]
94    InvalidRefcountTableOffset,
95    #[error("invalid refcount table size: {0}")]
96    InvalidRefcountTableSize(u64),
97    #[error("backing file format extension is missing")]
98    MissingBackingFileFormat,
99    #[error("no free clusters")]
100    NoFreeClusters,
101    #[error("no refcount clusters")]
102    NoRefcountClusters,
103    #[error("not enough space for refcounts")]
104    NotEnoughSpaceForRefcounts,
105    #[error("failed to open file: {0}")]
106    OpeningFile(io::Error),
107    #[error("failed to open file: {0}")]
108    ReadingHeader(io::Error),
109    #[error("failed to read pointers: {0}")]
110    ReadingPointers(io::Error),
111    #[error("failed to read ref count block: {0}")]
112    ReadingRefCountBlock(refcount::Error),
113    #[error("failed to read ref counts: {0}")]
114    ReadingRefCounts(io::Error),
115    #[error("failed to rebuild ref counts: {0}")]
116    RebuildingRefCounts(io::Error),
117    #[error("refcount table offset past file end")]
118    RefcountTableOffEnd,
119    #[error("too many clusters specified for refcount table")]
120    RefcountTableTooLarge,
121    #[error("failed to seek file: {0}")]
122    SeekingFile(io::Error),
123    #[error("failed to set refcount refcount: {0}")]
124    SettingRefcountRefcount(io::Error),
125    #[error("size too small for number of clusters")]
126    SizeTooSmallForNumberOfClusters,
127    #[error("snapshots not supported")]
128    SnapshotsNotSupported,
129    #[error("l1 entry table too large: {0}")]
130    TooManyL1Entries(u64),
131    #[error("ref count table too large: {0}")]
132    TooManyRefcounts(u64),
133    #[error("unsupported backing file format: {0}")]
134    UnsupportedBackingFileFormat(String),
135    #[error("unsupported incompatible features: {0:x}")]
136    UnsupportedIncompatibleFeatures(u64),
137    #[error("unsupported refcount order")]
138    UnsupportedRefcountOrder,
139    #[error("unsupported version: {0}")]
140    UnsupportedVersion(u32),
141    #[error("failed to write header: {0}")]
142    WritingHeader(io::Error),
143}
144
145pub type Result<T> = std::result::Result<T, Error>;
146
147// Maximum data size supported.
148const MAX_QCOW_FILE_SIZE: u64 = 0x01 << 44; // 16 TB.
149
150// QCOW magic constant that starts the header.
151pub const QCOW_MAGIC: u32 = 0x5146_49fb;
152// Default to a cluster size of 2^DEFAULT_CLUSTER_BITS
153const DEFAULT_CLUSTER_BITS: u32 = 16;
154// Limit clusters to reasonable sizes. Choose the same limits as qemu. Making the clusters smaller
155// increases the amount of overhead for book keeping.
156const MIN_CLUSTER_BITS: u32 = 9;
157const MAX_CLUSTER_BITS: u32 = 21;
158// The L1 and RefCount table are kept in RAM, only handle files that require less than 35M entries.
159// This easily covers 1 TB files. When support for bigger files is needed the assumptions made to
160// keep these tables in RAM needs to be thrown out.
161const MAX_RAM_POINTER_TABLE_SIZE: u64 = 35_000_000;
162// Only support 2 byte refcounts, 2^refcount_order bits.
163const DEFAULT_REFCOUNT_ORDER: u32 = 4;
164
165const V3_BARE_HEADER_SIZE: u32 = 104;
166
167// bits 0-8 and 56-63 are reserved.
168const L1_TABLE_OFFSET_MASK: u64 = 0x00ff_ffff_ffff_fe00;
169const L2_TABLE_OFFSET_MASK: u64 = 0x00ff_ffff_ffff_fe00;
170// Flags
171const ZERO_CLUSTER_FLAG: u64 = 1;
172const COMPRESSED_FLAG: u64 = 1 << 62;
173const CLUSTER_USED_FLAG: u64 = 1 << 63;
174const COMPATIBLE_FEATURES_LAZY_REFCOUNTS: u64 = 1 << 0;
175
176// The format supports a "header extension area".
177const QCOW2_EXT_MAGIC_BACKING_FORMAT: u32 = 0xe279_2aca;
178
179// Defined by the specification
180const MAX_BACKING_FILE_SIZE: u32 = 1023;
181
182/// Contains the information from the header of a qcow file.
183#[derive(Clone, Debug)]
184pub struct QcowHeader {
185    pub magic: u32,
186    pub version: u32,
187
188    pub backing_file_offset: u64,
189    pub backing_file_size: u32,
190
191    pub cluster_bits: u32,
192    pub size: u64,
193    pub crypt_method: u32,
194
195    pub l1_size: u32,
196    pub l1_table_offset: u64,
197
198    pub refcount_table_offset: u64,
199    pub refcount_table_clusters: u32,
200
201    pub nb_snapshots: u32,
202    pub snapshots_offset: u64,
203
204    // v3 entries
205    pub incompatible_features: u64,
206    pub compatible_features: u64,
207    pub autoclear_features: u64,
208    pub refcount_order: u32,
209    pub header_size: u32,
210
211    // Post-header entries
212    pub backing_file_path: Option<String>,
213    pub backing_file_format: Option<crate::ImageType>,
214}
215
216// Reads the next u16 from the file.
217fn read_u16_from_file(mut f: &File) -> Result<u16> {
218    let mut value = [0u8; 2];
219    (&mut f)
220        .read_exact(&mut value)
221        .map_err(Error::ReadingHeader)?;
222    Ok(u16::from_be_bytes(value))
223}
224
225// Reads the next u32 from the file.
226fn read_u32_from_file(mut f: &File) -> Result<u32> {
227    let mut value = [0u8; 4];
228    (&mut f)
229        .read_exact(&mut value)
230        .map_err(Error::ReadingHeader)?;
231    Ok(u32::from_be_bytes(value))
232}
233
234// Reads the next u64 from the file.
235fn read_u64_from_file(mut f: &File) -> Result<u64> {
236    let mut value = [0u8; 8];
237    (&mut f)
238        .read_exact(&mut value)
239        .map_err(Error::ReadingHeader)?;
240    Ok(u64::from_be_bytes(value))
241}
242
243fn image_type_to_format_str(image_type: crate::ImageType) -> Result<&'static str> {
244    match image_type {
245        crate::ImageType::Raw => Ok("raw"),
246        crate::ImageType::Qcow2 => Ok("qcow2"),
247        crate::ImageType::CompositeDisk => Ok("composite"),
248        other => Err(Error::UnsupportedBackingFileFormat(format!("{other:?}"))),
249    }
250}
251
252fn format_str_to_image_type(s: &str) -> Result<crate::ImageType> {
253    match s {
254        "raw" => Ok(crate::ImageType::Raw),
255        "qcow2" => Ok(crate::ImageType::Qcow2),
256        "composite" => Ok(crate::ImageType::CompositeDisk),
257        other => Err(Error::UnsupportedBackingFileFormat(other.to_string())),
258    }
259}
260
261impl QcowHeader {
262    /// Creates a QcowHeader from a reference to a file.
263    pub fn new(f: &mut File) -> Result<QcowHeader> {
264        f.seek(SeekFrom::Start(0)).map_err(Error::ReadingHeader)?;
265
266        let magic = read_u32_from_file(f)?;
267        if magic != QCOW_MAGIC {
268            return Err(Error::InvalidMagic);
269        }
270
271        let mut header = QcowHeader {
272            magic,
273            version: read_u32_from_file(f)?,
274            backing_file_offset: read_u64_from_file(f)?,
275            backing_file_size: read_u32_from_file(f)?,
276            cluster_bits: read_u32_from_file(f)?,
277            size: read_u64_from_file(f)?,
278            crypt_method: read_u32_from_file(f)?,
279            l1_size: read_u32_from_file(f)?,
280            l1_table_offset: read_u64_from_file(f)?,
281            refcount_table_offset: read_u64_from_file(f)?,
282            refcount_table_clusters: read_u32_from_file(f)?,
283            nb_snapshots: read_u32_from_file(f)?,
284            snapshots_offset: read_u64_from_file(f)?,
285            incompatible_features: read_u64_from_file(f)?,
286            compatible_features: read_u64_from_file(f)?,
287            autoclear_features: read_u64_from_file(f)?,
288            refcount_order: read_u32_from_file(f)?,
289            header_size: read_u32_from_file(f)?,
290            backing_file_path: None,
291            backing_file_format: None,
292        };
293
294        if !(MIN_CLUSTER_BITS..=MAX_CLUSTER_BITS).contains(&header.cluster_bits) {
295            return Err(Error::InvalidClusterSize);
296        }
297
298        let cluster_size = 1u64 << header.cluster_bits;
299
300        if header.backing_file_offset != 0 {
301            let backing_file_end = header
302                .backing_file_offset
303                .checked_add(header.backing_file_size as u64)
304                .ok_or(Error::BackingFileEscapesFirstCluster)?;
305            if backing_file_end > cluster_size {
306                return Err(Error::BackingFileEscapesFirstCluster);
307            }
308        }
309
310        let (backing_file_format, backing_file_path) = Self::parse_backing_info(
311            f,
312            header.header_size,
313            cluster_size,
314            header.backing_file_offset,
315            header.backing_file_size,
316        )?;
317        header.backing_file_format = backing_file_format;
318        header.backing_file_path = backing_file_path;
319        Ok(header)
320    }
321
322    fn parse_backing_info(
323        f: &mut File,
324        header_size: u32,
325        cluster_size: u64,
326        backing_file_offset: u64,
327        backing_file_size: u32,
328    ) -> Result<(Option<crate::ImageType>, Option<String>)> {
329        let mut backing_file_format = None;
330        let mut ext_offset = (header_size as u64 + 7) & !7;
331
332        // Avoid reading past the first cluster or past backing_file_offset if it is set.
333        let limit = if backing_file_offset != 0 {
334            backing_file_offset
335        } else {
336            cluster_size
337        };
338
339        loop {
340            if ext_offset >= limit {
341                break;
342            }
343
344            let next_offset = ext_offset
345                .checked_add(8)
346                .ok_or(Error::InvalidHeaderExtension)?;
347            if next_offset > limit {
348                return Err(Error::InvalidHeaderExtension);
349            }
350
351            f.seek(SeekFrom::Start(ext_offset))
352                .map_err(Error::ReadingHeader)?;
353            let ext_type = read_u32_from_file(f)?;
354            let ext_len = read_u32_from_file(f)?;
355            if ext_type == 0 {
356                break;
357            }
358
359            let ext_end = next_offset
360                .checked_add(ext_len as u64)
361                .ok_or(Error::InvalidHeaderExtension)?;
362            if ext_end > limit {
363                return Err(Error::InvalidHeaderExtension);
364            }
365
366            if ext_type == QCOW2_EXT_MAGIC_BACKING_FORMAT {
367                let mut backing_format_bytes = vec![0u8; ext_len as usize];
368                f.read_exact(&mut backing_format_bytes)
369                    .map_err(Error::ReadingHeader)?;
370                let format_str = String::from_utf8(backing_format_bytes)
371                    .map_err(|_| Error::InvalidHeaderExtension)?;
372                let format = format_str_to_image_type(&format_str)?;
373                backing_file_format = Some(format);
374            }
375
376            let padded_len = (ext_len as u64 + 7) & !7;
377            let increment = padded_len
378                .checked_add(8)
379                .ok_or(Error::InvalidHeaderExtension)?;
380            ext_offset = ext_offset
381                .checked_add(increment)
382                .ok_or(Error::InvalidHeaderExtension)?;
383        }
384
385        if backing_file_size > MAX_BACKING_FILE_SIZE {
386            return Err(Error::BackingFileTooLong(backing_file_size as usize));
387        }
388        let backing_file_path = if backing_file_offset != 0 {
389            f.seek(SeekFrom::Start(backing_file_offset))
390                .map_err(Error::ReadingHeader)?;
391            let mut backing_file_name_bytes = vec![0u8; backing_file_size as usize];
392            f.read_exact(&mut backing_file_name_bytes)
393                .map_err(Error::ReadingHeader)?;
394            Some(
395                String::from_utf8(backing_file_name_bytes)
396                    .map_err(|err| Error::InvalidBackingFileName(err.utf8_error()))?,
397            )
398        } else {
399            None
400        };
401
402        Ok((backing_file_format, backing_file_path))
403    }
404
405    pub fn create_for_size_and_path(
406        size: u64,
407        backing: Option<(&str, crate::ImageType)>,
408    ) -> Result<QcowHeader> {
409        let cluster_bits: u32 = DEFAULT_CLUSTER_BITS;
410        let cluster_size: u32 = 0x01 << cluster_bits;
411        let mut ext_size = 8; // For end marker
412        let (backing_file, backing_file_format) = match backing {
413            Some((path, format)) => {
414                let format_str = image_type_to_format_str(format)?;
415                let format_len = format_str.len() as u32;
416                ext_size += 8 + ((format_len + 7) & !7);
417                (Some(path), Some(format))
418            }
419            None => (None, None),
420        };
421        let max_length: usize = (cluster_size - V3_BARE_HEADER_SIZE - ext_size) as usize;
422        if let Some(path) = backing_file {
423            if path.len() > max_length {
424                return Err(Error::BackingFileTooLong(path.len() - max_length));
425            }
426        }
427        if size > MAX_QCOW_FILE_SIZE {
428            return Err(Error::FileTooBig(size));
429        }
430        // L2 blocks are always one cluster long. They contain cluster_size/sizeof(u64) addresses.
431        let l2_size: u32 = cluster_size / size_of::<u64>() as u32;
432        let num_clusters: u32 = size.div_ceil(u64::from(cluster_size)) as u32;
433        let num_l2_clusters: u32 = num_clusters.div_ceil(l2_size);
434        let l1_clusters: u32 = num_l2_clusters.div_ceil(cluster_size);
435        let header_clusters = (size_of::<QcowHeader>() as u32).div_ceil(cluster_size);
436        Ok(QcowHeader {
437            magic: QCOW_MAGIC,
438            version: 3,
439            backing_file_offset: if backing_file.is_none() {
440                0
441            } else {
442                V3_BARE_HEADER_SIZE as u64 + ext_size as u64
443            },
444            backing_file_size: backing_file.map_or(0, |x| x.len()) as u32,
445            cluster_bits: DEFAULT_CLUSTER_BITS,
446            size,
447            crypt_method: 0,
448            l1_size: num_l2_clusters,
449            l1_table_offset: u64::from(cluster_size),
450            // The refcount table is after l1 + header.
451            refcount_table_offset: u64::from(cluster_size * (l1_clusters + 1)),
452            refcount_table_clusters: {
453                // Pre-allocate enough clusters for the entire refcount table as it must be
454                // continuous in the file. Allocate enough space to refcount all clusters, including
455                // the refcount clusters.
456                let max_refcount_clusters = max_refcount_clusters(
457                    DEFAULT_REFCOUNT_ORDER,
458                    cluster_size,
459                    num_clusters + l1_clusters + num_l2_clusters + header_clusters,
460                ) as u32;
461                // The refcount table needs to store the offset of each refcount cluster.
462                (max_refcount_clusters * size_of::<u64>() as u32).div_ceil(cluster_size)
463            },
464            nb_snapshots: 0,
465            snapshots_offset: 0,
466            incompatible_features: 0,
467            compatible_features: 0,
468            autoclear_features: 0,
469            refcount_order: DEFAULT_REFCOUNT_ORDER,
470            header_size: V3_BARE_HEADER_SIZE,
471            backing_file_path: backing_file.map(String::from),
472            backing_file_format,
473        })
474    }
475
476    /// Write the header to `file`.
477    pub fn write_to<F: Write + Seek>(&self, file: &mut F) -> Result<()> {
478        // Writes the next u32 to the file.
479        fn write_u32_to_file<F: Write>(f: &mut F, value: u32) -> Result<()> {
480            f.write_all(&value.to_be_bytes())
481                .map_err(Error::WritingHeader)
482        }
483
484        // Writes the next u64 to the file.
485        fn write_u64_to_file<F: Write>(f: &mut F, value: u64) -> Result<()> {
486            f.write_all(&value.to_be_bytes())
487                .map_err(Error::WritingHeader)
488        }
489
490        write_u32_to_file(file, self.magic)?;
491        write_u32_to_file(file, self.version)?;
492        write_u64_to_file(file, self.backing_file_offset)?;
493        write_u32_to_file(file, self.backing_file_size)?;
494        write_u32_to_file(file, self.cluster_bits)?;
495        write_u64_to_file(file, self.size)?;
496        write_u32_to_file(file, self.crypt_method)?;
497        write_u32_to_file(file, self.l1_size)?;
498        write_u64_to_file(file, self.l1_table_offset)?;
499        write_u64_to_file(file, self.refcount_table_offset)?;
500        write_u32_to_file(file, self.refcount_table_clusters)?;
501        write_u32_to_file(file, self.nb_snapshots)?;
502        write_u64_to_file(file, self.snapshots_offset)?;
503        write_u64_to_file(file, self.incompatible_features)?;
504        write_u64_to_file(file, self.compatible_features)?;
505        write_u64_to_file(file, self.autoclear_features)?;
506        write_u32_to_file(file, self.refcount_order)?;
507        write_u32_to_file(file, self.header_size)?;
508        if let Some(format) = self.backing_file_format {
509            let format_str = image_type_to_format_str(format)?;
510            write_u32_to_file(file, QCOW2_EXT_MAGIC_BACKING_FORMAT)?;
511            write_u32_to_file(file, format_str.len() as u32)?;
512            file.write_all(format_str.as_bytes())
513                .map_err(Error::WritingHeader)?;
514            // Pad to 8-byte boundary
515            let padding = ((format_str.len() + 7) & !7) - format_str.len();
516            if padding > 0 {
517                const ZEROES: [u8; 8] = [0u8; 8];
518                file.write_all(&ZEROES[..padding])
519                    .map_err(Error::WritingHeader)?;
520            }
521        }
522        write_u32_to_file(file, 0)?; // header extension type: end of header extension area
523        write_u32_to_file(file, 0)?; // length of header extension data: 0
524        if let Some(backing_file_path) = self.backing_file_path.as_ref() {
525            write!(file, "{backing_file_path}").map_err(Error::WritingHeader)?;
526        }
527
528        // Set the file length by seeking and writing a zero to the last byte. This avoids needing
529        // a `File` instead of anything that implements seek as the `file` argument.
530        // Zeros out the l1 and refcount table clusters.
531        let cluster_size = 0x01u64 << self.cluster_bits;
532        let refcount_blocks_size = u64::from(self.refcount_table_clusters) * cluster_size;
533        file.seek(SeekFrom::Start(
534            self.refcount_table_offset + refcount_blocks_size - 2,
535        ))
536        .map_err(Error::WritingHeader)?;
537        file.write(&[0u8]).map_err(Error::WritingHeader)?;
538
539        Ok(())
540    }
541}
542
543fn max_refcount_clusters(refcount_order: u32, cluster_size: u32, num_clusters: u32) -> u64 {
544    // Use u64 as the product of the u32 inputs can overflow.
545    let refcount_bytes = (0x01 << refcount_order as u64) / 8;
546    let for_data = (u64::from(num_clusters) * refcount_bytes).div_ceil(u64::from(cluster_size));
547    let for_refcounts = (for_data * refcount_bytes).div_ceil(u64::from(cluster_size));
548    for_data + for_refcounts
549}
550
551/// Represents a qcow2 file. This is a sparse file format maintained by the qemu project.
552/// Full documentation of the format can be found in the qemu repository.
553///
554/// # Example
555///
556/// ```
557/// # use std::path::PathBuf;
558/// # use base::FileReadWriteAtVolatile;
559/// # use disk::QcowFile;
560/// # use disk::DiskFileParams;
561/// # use base::VolatileSlice;
562/// # fn test(file: std::fs::File, path: PathBuf) -> std::io::Result<()> {
563///     let mut q = QcowFile::from(file, DiskFileParams {
564///         path,
565///         ..Default::default()
566///     }).expect("Can't open qcow file");
567///     let mut buf = [0u8; 12];
568///     let mut vslice = VolatileSlice::new(&mut buf);
569///     q.read_at_volatile(vslice, 10)?;
570/// #   Ok(())
571/// # }
572/// ```
573#[derive(Debug)]
574pub struct QcowFile {
575    inner: Mutex<QcowFileInner>,
576    // Copy of `inner.header.size` outside the mutex.
577    virtual_size: u64,
578}
579
580#[derive(Debug)]
581struct QcowFileInner {
582    raw_file: QcowRawFile,
583    header: QcowHeader,
584    l1_table: VecCache<u64>,
585    l2_entries: u64,
586    l2_cache: CacheMap<VecCache<L2CacheEntry>>,
587    refcounts: RefCount,
588    current_offset: u64,
589    unref_clusters: Vec<u64>, // List of freshly unreferenced clusters.
590    // List of unreferenced clusters available to be used. unref clusters become available once the
591    // removal of references to them have been synced to disk.
592    avail_clusters: Vec<u64>,
593    backing_file: Option<Box<dyn DiskFile>>,
594}
595
596// An entry in an L2 table, storing the host cluster offset and zero-cluster flag.
597#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
598struct L2CacheEntry(u64);
599
600impl L2CacheEntry {
601    const UNALLOCATED: Self = Self(0);
602    const ZERO: Self = Self(ZERO_CLUSTER_FLAG);
603
604    fn new_data(cluster_addr: u64) -> Self {
605        debug_assert_eq!(cluster_addr & !L2_TABLE_OFFSET_MASK, 0);
606        Self(cluster_addr)
607    }
608
609    fn from_disk(raw: u64) -> std::io::Result<Self> {
610        if raw & COMPRESSED_FLAG != 0 {
611            return Err(std::io::Error::from_raw_os_error(ENOTSUP));
612        }
613        Ok(Self(raw & (L2_TABLE_OFFSET_MASK | ZERO_CLUSTER_FLAG)))
614    }
615
616    fn to_disk(self) -> u64 {
617        if self.cluster_addr().is_some() {
618            self.0 | CLUSTER_USED_FLAG
619        } else {
620            self.0
621        }
622    }
623
624    fn cluster_addr(self) -> Option<u64> {
625        let addr = self.0 & L2_TABLE_OFFSET_MASK;
626        (addr != 0).then_some(addr)
627    }
628
629    fn is_zero(self) -> bool {
630        self.0 & ZERO_CLUSTER_FLAG != 0
631    }
632}
633
634// Where the data for a guest address is read from.
635enum ClusterLocation {
636    // At this offset in the qcow file.
637    Data(u64),
638    // Not allocated: read from the backing file, or as zeroes if there is none.
639    Unallocated,
640    // A zero cluster, which reads as zeroes even if there is a backing file.
641    Zero,
642}
643
644impl DiskFile for QcowFile {}
645
646impl DiskFlush for QcowFile {
647    fn flush(&self) -> io::Result<()> {
648        // Using fsync is overkill here, but, the code for flushing state to file tangled up with
649        // the fsync, so it is best we can do for now.
650        self.fsync()
651    }
652}
653
654impl QcowFile {
655    /// Creates a QcowFile from `file`. File must be a valid qcow2 image.
656    pub fn from(mut file: File, params: DiskFileParams) -> Result<QcowFile> {
657        let header = QcowHeader::new(&mut file)?;
658
659        // Only v3 files are supported.
660        if header.version != 3 {
661            return Err(Error::UnsupportedVersion(header.version));
662        }
663
664        if header.incompatible_features != 0 {
665            return Err(Error::UnsupportedIncompatibleFeatures(
666                header.incompatible_features,
667            ));
668        }
669
670        if header.nb_snapshots != 0 {
671            return Err(Error::SnapshotsNotSupported);
672        }
673
674        // Make sure that the L1 table fits in RAM.
675        if u64::from(header.l1_size) > MAX_RAM_POINTER_TABLE_SIZE {
676            return Err(Error::InvalidL1TableSize(header.l1_size));
677        }
678
679        let cluster_bits: u32 = header.cluster_bits;
680        if !(MIN_CLUSTER_BITS..=MAX_CLUSTER_BITS).contains(&cluster_bits) {
681            return Err(Error::InvalidClusterSize);
682        }
683        let cluster_size = 0x01u64 << cluster_bits;
684
685        // Limit the total size of the disk.
686        if header.size > MAX_QCOW_FILE_SIZE {
687            return Err(Error::FileTooBig(header.size));
688        }
689
690        let backing_file = if let Some(backing_file_path) = header.backing_file_path.as_ref() {
691            let backing_params = DiskFileParams {
692                path: PathBuf::from(backing_file_path),
693                // The backing file is only read from.
694                is_read_only: true,
695                // TODO: Should pass `params.is_overlapped` through here. Needs testing.
696                // is_overlapped: false,
697                is_direct: params.is_direct,
698                lock: params.lock,
699                depth: params.depth + 1,
700                ..Default::default()
701            };
702            let backing_image_type = match header.backing_file_format {
703                Some(format) => format,
704                None => return Err(Error::MissingBackingFileFormat),
705            };
706            let backing_disk_file = crate::open_disk_file_as(backing_params, backing_image_type)
707                .map_err(|e| Error::BackingFileOpen(Box::new(e)))?;
708            Some(backing_disk_file)
709        } else {
710            None
711        };
712
713        // Only support two byte refcounts.
714        let refcount_bits: u64 = 0x01u64
715            .checked_shl(header.refcount_order)
716            .ok_or(Error::UnsupportedRefcountOrder)?;
717        if refcount_bits != 16 {
718            return Err(Error::UnsupportedRefcountOrder);
719        }
720        let refcount_bytes = refcount_bits.div_ceil(8);
721
722        // Need at least one refcount cluster
723        if header.refcount_table_clusters == 0 {
724            return Err(Error::NoRefcountClusters);
725        }
726        offset_is_cluster_boundary(header.l1_table_offset, header.cluster_bits)?;
727        offset_is_cluster_boundary(header.snapshots_offset, header.cluster_bits)?;
728        // refcount table must be a cluster boundary, and within the file's virtual or actual size.
729        offset_is_cluster_boundary(header.refcount_table_offset, header.cluster_bits)?;
730        let file_size = file.metadata().map_err(Error::GettingFileSize)?.len();
731        if header.refcount_table_offset > max(file_size, header.size) {
732            return Err(Error::RefcountTableOffEnd);
733        }
734
735        // The first cluster should always have a non-zero refcount, so if it is 0,
736        // this is an old file with broken refcounts, which requires a rebuild.
737        let mut refcount_rebuild_required = true;
738        file.seek(SeekFrom::Start(header.refcount_table_offset))
739            .map_err(Error::SeekingFile)?;
740        let first_refblock_addr = read_u64_from_file(&file)?;
741        if first_refblock_addr != 0 {
742            file.seek(SeekFrom::Start(first_refblock_addr))
743                .map_err(Error::SeekingFile)?;
744            let first_cluster_refcount = read_u16_from_file(&file)?;
745            if first_cluster_refcount != 0 {
746                refcount_rebuild_required = false;
747            }
748        }
749
750        if (header.compatible_features & COMPATIBLE_FEATURES_LAZY_REFCOUNTS) != 0 {
751            refcount_rebuild_required = true;
752        }
753
754        let mut raw_file =
755            QcowRawFile::from(file, cluster_size).ok_or(Error::InvalidClusterSize)?;
756        if refcount_rebuild_required {
757            QcowFileInner::rebuild_refcounts(&mut raw_file, header.clone())?;
758        }
759
760        let l2_size = cluster_size / size_of::<u64>() as u64;
761        let num_clusters = header.size.div_ceil(cluster_size);
762        let num_l2_clusters = num_clusters.div_ceil(l2_size);
763        let l1_clusters = num_l2_clusters.div_ceil(cluster_size);
764        let header_clusters = (size_of::<QcowHeader>() as u64).div_ceil(cluster_size);
765        if num_l2_clusters > MAX_RAM_POINTER_TABLE_SIZE {
766            return Err(Error::TooManyL1Entries(num_l2_clusters));
767        }
768        let l1_table = VecCache::from_vec(
769            raw_file
770                .read_pointer_table(
771                    header.l1_table_offset,
772                    num_l2_clusters,
773                    Some(L1_TABLE_OFFSET_MASK),
774                )
775                .map_err(Error::ReadingHeader)?,
776        );
777
778        let num_clusters = header.size.div_ceil(cluster_size);
779        let refcount_clusters = max_refcount_clusters(
780            header.refcount_order,
781            cluster_size as u32,
782            (num_clusters + l1_clusters + num_l2_clusters + header_clusters) as u32,
783        );
784        // Check that the given header doesn't have a suspiciously sized refcount table.
785        if u64::from(header.refcount_table_clusters) > 2 * refcount_clusters {
786            return Err(Error::RefcountTableTooLarge);
787        }
788        if l1_clusters + refcount_clusters > MAX_RAM_POINTER_TABLE_SIZE {
789            return Err(Error::TooManyRefcounts(refcount_clusters));
790        }
791        let refcount_block_entries = cluster_size / refcount_bytes;
792        let refcounts = RefCount::new(
793            &mut raw_file,
794            header.refcount_table_offset,
795            refcount_clusters,
796            refcount_block_entries,
797            cluster_size,
798        )
799        .map_err(Error::ReadingRefCounts)?;
800
801        let l2_entries = cluster_size / size_of::<u64>() as u64;
802
803        let mut inner = QcowFileInner {
804            raw_file,
805            header,
806            l1_table,
807            l2_entries,
808            l2_cache: CacheMap::new(100),
809            refcounts,
810            current_offset: 0,
811            unref_clusters: Vec::new(),
812            avail_clusters: Vec::new(),
813            backing_file,
814        };
815
816        // Check that the L1 and refcount tables fit in a 64bit address space.
817        inner
818            .header
819            .l1_table_offset
820            .checked_add(inner.l1_address_offset(inner.virtual_size()))
821            .ok_or(Error::InvalidL1TableOffset)?;
822        inner
823            .header
824            .refcount_table_offset
825            .checked_add(u64::from(inner.header.refcount_table_clusters) * cluster_size)
826            .ok_or(Error::InvalidRefcountTableOffset)?;
827
828        inner.find_avail_clusters()?;
829
830        let virtual_size = inner.virtual_size();
831        Ok(QcowFile {
832            inner: Mutex::new(inner),
833            virtual_size,
834        })
835    }
836
837    /// Creates a new QcowFile at the given path.
838    pub fn new(file: File, params: DiskFileParams, virtual_size: u64) -> Result<QcowFile> {
839        let header = QcowHeader::create_for_size_and_path(virtual_size, None)?;
840        QcowFile::new_from_header(file, params, header)
841    }
842
843    /// Creates a new QcowFile at the given path.
844    pub fn new_from_backing(
845        file: File,
846        params: DiskFileParams,
847        backing_file_name: &str,
848    ) -> Result<QcowFile> {
849        // Open the backing file as a `DiskFile` to determine its size (which may not match the
850        // filesystem size).
851        let (size, backing_format) = {
852            let backing_params = DiskFileParams {
853                path: PathBuf::from(backing_file_name),
854                // The backing file is only read from.
855                is_read_only: true,
856                // TODO: Should pass `params.is_overlapped` through here. Needs testing.
857                // is_overlapped: false,
858                is_direct: params.is_direct,
859                lock: params.lock,
860                depth: params.depth + 1,
861                ..Default::default()
862            };
863            let backing_file = crate::sys::open_raw_disk_image(&backing_params)
864                .map_err(|e| Error::BackingFileOpen(Box::new(e)))?;
865            let backing_image_type = crate::detect_image_type(&backing_file, false)
866                .map_err(|e| Error::BackingFileOpen(Box::new(e)))?;
867            let backing_disk_file =
868                crate::disk_file_from_file(backing_file, backing_params, backing_image_type)
869                    .map_err(|e| Error::BackingFileOpen(Box::new(e)))?;
870            (
871                backing_disk_file.get_len().map_err(Error::BackingFileIo)?,
872                backing_image_type,
873            )
874        };
875        let header =
876            QcowHeader::create_for_size_and_path(size, Some((backing_file_name, backing_format)))?;
877        QcowFile::new_from_header(file, params, header)
878    }
879
880    fn new_from_header(
881        mut file: File,
882        params: DiskFileParams,
883        header: QcowHeader,
884    ) -> Result<QcowFile> {
885        file.seek(SeekFrom::Start(0)).map_err(Error::SeekingFile)?;
886        header.write_to(&mut file)?;
887
888        let mut qcow = Self::from(file, params)?;
889        let inner = qcow.inner.get_mut();
890
891        // Set the refcount for each refcount table cluster.
892        let cluster_size = 0x01u64 << inner.header.cluster_bits;
893        let refcount_table_base = inner.header.refcount_table_offset;
894        let end_cluster_addr =
895            refcount_table_base + u64::from(inner.header.refcount_table_clusters) * cluster_size;
896
897        let mut cluster_addr = 0;
898        while cluster_addr < end_cluster_addr {
899            let mut unref_clusters = inner
900                .set_cluster_refcount(cluster_addr, 1)
901                .map_err(Error::SettingRefcountRefcount)?;
902            inner.unref_clusters.append(&mut unref_clusters);
903            cluster_addr += cluster_size;
904        }
905
906        Ok(qcow)
907    }
908
909    pub fn set_backing_file(&mut self, backing: Option<Box<dyn DiskFile>>) {
910        self.inner.get_mut().backing_file = backing;
911    }
912}
913
914impl QcowFileInner {
915    /// Returns the first cluster in the file with a 0 refcount. Used for testing.
916    #[cfg(test)]
917    fn first_zero_refcount(&mut self) -> Result<Option<u64>> {
918        let file_size = self
919            .raw_file
920            .file_mut()
921            .metadata()
922            .map_err(Error::GettingFileSize)?
923            .len();
924        let cluster_size = 0x01u64 << self.header.cluster_bits;
925
926        let mut cluster_addr = 0;
927        while cluster_addr < file_size {
928            let cluster_refcount = self
929                .refcounts
930                .get_cluster_refcount(&mut self.raw_file, cluster_addr)
931                .map_err(Error::GettingRefcount)?;
932            if cluster_refcount == 0 {
933                return Ok(Some(cluster_addr));
934            }
935            cluster_addr += cluster_size;
936        }
937        Ok(None)
938    }
939
940    fn find_avail_clusters(&mut self) -> Result<()> {
941        let cluster_size = self.raw_file.cluster_size();
942
943        let file_size = self
944            .raw_file
945            .file_mut()
946            .metadata()
947            .map_err(Error::GettingFileSize)?
948            .len();
949
950        for i in (0..file_size).step_by(cluster_size as usize) {
951            let refcount = self
952                .refcounts
953                .get_cluster_refcount(&mut self.raw_file, i)
954                .map_err(Error::GettingRefcount)?;
955            if refcount == 0 {
956                self.avail_clusters.push(i);
957            }
958        }
959
960        Ok(())
961    }
962
963    /// Rebuild the reference count tables.
964    fn rebuild_refcounts(raw_file: &mut QcowRawFile, header: QcowHeader) -> Result<()> {
965        fn add_ref(refcounts: &mut [u16], cluster_size: u64, cluster_address: u64) -> Result<()> {
966            let idx = (cluster_address / cluster_size) as usize;
967            if idx >= refcounts.len() {
968                return Err(Error::InvalidClusterIndex);
969            }
970            refcounts[idx] += 1;
971            Ok(())
972        }
973
974        // Add a reference to the first cluster (header plus extensions).
975        fn set_header_refcount(refcounts: &mut [u16], cluster_size: u64) -> Result<()> {
976            add_ref(refcounts, cluster_size, 0)
977        }
978
979        // Add references to the L1 table clusters.
980        fn set_l1_refcounts(
981            refcounts: &mut [u16],
982            header: QcowHeader,
983            cluster_size: u64,
984        ) -> Result<()> {
985            let l1_clusters = u64::from(header.l1_size).div_ceil(cluster_size);
986            let l1_table_offset = header.l1_table_offset;
987            for i in 0..l1_clusters {
988                add_ref(refcounts, cluster_size, l1_table_offset + i * cluster_size)?;
989            }
990            Ok(())
991        }
992
993        // Traverse the L1 and L2 tables to find all reachable data clusters.
994        fn set_data_refcounts(
995            refcounts: &mut [u16],
996            header: QcowHeader,
997            cluster_size: u64,
998            raw_file: &mut QcowRawFile,
999        ) -> Result<()> {
1000            let l1_table = raw_file
1001                .read_pointer_table(
1002                    header.l1_table_offset,
1003                    header.l1_size as u64,
1004                    Some(L1_TABLE_OFFSET_MASK),
1005                )
1006                .map_err(Error::ReadingPointers)?;
1007            for l1_index in 0..header.l1_size as usize {
1008                let l2_addr_disk = *l1_table.get(l1_index).ok_or(Error::InvalidIndex)?;
1009                if l2_addr_disk != 0 {
1010                    // Add a reference to the L2 table cluster itself.
1011                    add_ref(refcounts, cluster_size, l2_addr_disk)?;
1012
1013                    // Read the L2 table and find all referenced data clusters.
1014                    let l2_table = raw_file
1015                        .read_pointer_table(
1016                            l2_addr_disk,
1017                            cluster_size / size_of::<u64>() as u64,
1018                            Some(L2_TABLE_OFFSET_MASK),
1019                        )
1020                        .map_err(Error::ReadingPointers)?;
1021                    for data_cluster_addr in l2_table {
1022                        if data_cluster_addr != 0 {
1023                            add_ref(refcounts, cluster_size, data_cluster_addr)?;
1024                        }
1025                    }
1026                }
1027            }
1028
1029            Ok(())
1030        }
1031
1032        // Add references to the top-level refcount table clusters.
1033        fn set_refcount_table_refcounts(
1034            refcounts: &mut [u16],
1035            header: QcowHeader,
1036            cluster_size: u64,
1037        ) -> Result<()> {
1038            let refcount_table_offset = header.refcount_table_offset;
1039            for i in 0..header.refcount_table_clusters as u64 {
1040                add_ref(
1041                    refcounts,
1042                    cluster_size,
1043                    refcount_table_offset + i * cluster_size,
1044                )?;
1045            }
1046            Ok(())
1047        }
1048
1049        // Allocate clusters for refblocks.
1050        // This needs to be done last so that we have the correct refcounts for all other
1051        // clusters.
1052        fn alloc_refblocks(
1053            refcounts: &mut [u16],
1054            cluster_size: u64,
1055            refblock_clusters: u64,
1056            pointers_per_cluster: u64,
1057        ) -> Result<Vec<u64>> {
1058            let refcount_table_entries = refblock_clusters.div_ceil(pointers_per_cluster);
1059            let mut ref_table = vec![0; refcount_table_entries as usize];
1060            let mut first_free_cluster: u64 = 0;
1061            for refblock_addr in &mut ref_table {
1062                loop {
1063                    if first_free_cluster >= refcounts.len() as u64 {
1064                        return Err(Error::NotEnoughSpaceForRefcounts);
1065                    }
1066                    if refcounts[first_free_cluster as usize] == 0 {
1067                        break;
1068                    }
1069                    first_free_cluster += 1;
1070                }
1071
1072                *refblock_addr = first_free_cluster * cluster_size;
1073                add_ref(refcounts, cluster_size, *refblock_addr)?;
1074
1075                first_free_cluster += 1;
1076            }
1077
1078            Ok(ref_table)
1079        }
1080
1081        // Write the updated reference count blocks and reftable.
1082        fn write_refblocks(
1083            refcounts: &[u16],
1084            mut header: QcowHeader,
1085            ref_table: &[u64],
1086            raw_file: &mut QcowRawFile,
1087            refcount_block_entries: u64,
1088        ) -> Result<()> {
1089            // Rewrite the header with lazy refcounts enabled while we are rebuilding the tables.
1090            header.compatible_features |= COMPATIBLE_FEATURES_LAZY_REFCOUNTS;
1091            raw_file
1092                .file_mut()
1093                .seek(SeekFrom::Start(0))
1094                .map_err(Error::SeekingFile)?;
1095            header.write_to(raw_file.file_mut())?;
1096
1097            for (i, refblock_addr) in ref_table.iter().enumerate() {
1098                // Write a block of refcounts to the location indicated by refblock_addr.
1099                let refblock_start = i * (refcount_block_entries as usize);
1100                let refblock_end = min(
1101                    refcounts.len(),
1102                    refblock_start + refcount_block_entries as usize,
1103                );
1104                let refblock = &refcounts[refblock_start..refblock_end];
1105                raw_file
1106                    .write_refcount_block(*refblock_addr, refblock)
1107                    .map_err(Error::WritingHeader)?;
1108
1109                // If this is the last (partial) cluster, pad it out to a full refblock cluster.
1110                if refblock.len() < refcount_block_entries as usize {
1111                    let refblock_padding =
1112                        vec![0u16; refcount_block_entries as usize - refblock.len()];
1113                    raw_file
1114                        .write_refcount_block(
1115                            *refblock_addr + refblock.len() as u64 * 2,
1116                            &refblock_padding,
1117                        )
1118                        .map_err(Error::WritingHeader)?;
1119                }
1120            }
1121
1122            // Rewrite the top-level refcount table.
1123            raw_file
1124                .write_pointer_table(header.refcount_table_offset, ref_table, 0)
1125                .map_err(Error::WritingHeader)?;
1126
1127            // Rewrite the header again, now with lazy refcounts disabled.
1128            header.compatible_features &= !COMPATIBLE_FEATURES_LAZY_REFCOUNTS;
1129            raw_file
1130                .file_mut()
1131                .seek(SeekFrom::Start(0))
1132                .map_err(Error::SeekingFile)?;
1133            header.write_to(raw_file.file_mut())?;
1134
1135            Ok(())
1136        }
1137
1138        let cluster_size = raw_file.cluster_size();
1139
1140        let file_size = raw_file
1141            .file_mut()
1142            .metadata()
1143            .map_err(Error::GettingFileSize)?
1144            .len();
1145
1146        let refcount_bits = 1u64 << header.refcount_order;
1147        let refcount_bytes = refcount_bits.div_ceil(8);
1148        let refcount_block_entries = cluster_size / refcount_bytes;
1149        let pointers_per_cluster = cluster_size / size_of::<u64>() as u64;
1150        let data_clusters = header.size.div_ceil(cluster_size);
1151        let l2_clusters = data_clusters.div_ceil(pointers_per_cluster);
1152        let l1_clusters = l2_clusters.div_ceil(cluster_size);
1153        let header_clusters = (size_of::<QcowHeader>() as u64).div_ceil(cluster_size);
1154        let max_clusters = data_clusters + l2_clusters + l1_clusters + header_clusters;
1155        let mut max_valid_cluster_index = max_clusters;
1156        let refblock_clusters = max_valid_cluster_index.div_ceil(refcount_block_entries);
1157        let reftable_clusters = refblock_clusters.div_ceil(pointers_per_cluster);
1158        // Account for refblocks and the ref table size needed to address them.
1159        let refblocks_for_refs =
1160            (refblock_clusters + reftable_clusters).div_ceil(refcount_block_entries);
1161        let reftable_clusters_for_refs = refblocks_for_refs.div_ceil(refcount_block_entries);
1162        max_valid_cluster_index += refblock_clusters + reftable_clusters;
1163        max_valid_cluster_index += refblocks_for_refs + reftable_clusters_for_refs;
1164
1165        if max_valid_cluster_index > MAX_RAM_POINTER_TABLE_SIZE {
1166            return Err(Error::InvalidRefcountTableSize(max_valid_cluster_index));
1167        }
1168
1169        let max_valid_cluster_offset = max_valid_cluster_index * cluster_size;
1170        if max_valid_cluster_offset < file_size - cluster_size {
1171            return Err(Error::InvalidRefcountTableSize(max_valid_cluster_offset));
1172        }
1173
1174        let mut refcounts = vec![0; max_valid_cluster_index as usize];
1175
1176        // Find all references clusters and rebuild refcounts.
1177        set_header_refcount(&mut refcounts, cluster_size)?;
1178        set_l1_refcounts(&mut refcounts, header.clone(), cluster_size)?;
1179        set_data_refcounts(&mut refcounts, header.clone(), cluster_size, raw_file)?;
1180        set_refcount_table_refcounts(&mut refcounts, header.clone(), cluster_size)?;
1181
1182        // Allocate clusters to store the new reference count blocks.
1183        let ref_table = alloc_refblocks(
1184            &mut refcounts,
1185            cluster_size,
1186            refblock_clusters,
1187            pointers_per_cluster,
1188        )?;
1189
1190        // Write updated reference counts and point the reftable at them.
1191        write_refblocks(
1192            &refcounts,
1193            header,
1194            &ref_table,
1195            raw_file,
1196            refcount_block_entries,
1197        )
1198    }
1199
1200    // Limits the range so that it doesn't exceed the virtual size of the file.
1201    fn limit_range_file(&self, address: u64, count: usize) -> usize {
1202        if address.checked_add(count as u64).is_none() || address > self.virtual_size() {
1203            return 0;
1204        }
1205        min(count as u64, self.virtual_size() - address) as usize
1206    }
1207
1208    // Limits the range so that it doesn't overflow the end of a cluster.
1209    fn limit_range_cluster(&self, address: u64, count: usize) -> usize {
1210        let offset: u64 = self.raw_file.cluster_offset(address);
1211        let limit = self.raw_file.cluster_size() - offset;
1212        min(count as u64, limit) as usize
1213    }
1214
1215    // Gets the maximum virtual size of this image.
1216    fn virtual_size(&self) -> u64 {
1217        self.header.size
1218    }
1219
1220    // Gets the offset of `address` in the L1 table.
1221    fn l1_address_offset(&self, address: u64) -> u64 {
1222        let l1_index = self.l1_table_index(address);
1223        l1_index * size_of::<u64>() as u64
1224    }
1225
1226    // Gets the offset of `address` in the L1 table.
1227    fn l1_table_index(&self, address: u64) -> u64 {
1228        (address / self.raw_file.cluster_size()) / self.l2_entries
1229    }
1230
1231    // Gets the offset of `address` in the L2 table.
1232    fn l2_table_index(&self, address: u64) -> u64 {
1233        (address / self.raw_file.cluster_size()) % self.l2_entries
1234    }
1235
1236    // Gets the location of the given guest address. If L1, L2, or data clusters have yet to be
1237    // allocated, return `Unallocated`.
1238    fn file_offset_read(&mut self, address: u64) -> std::io::Result<ClusterLocation> {
1239        if address >= self.virtual_size() {
1240            return Err(std::io::Error::from_raw_os_error(EINVAL));
1241        }
1242
1243        let l1_index = self.l1_table_index(address) as usize;
1244        let l2_addr_disk = *self
1245            .l1_table
1246            .get(l1_index)
1247            .ok_or_else(|| std::io::Error::from_raw_os_error(EINVAL))?;
1248
1249        if l2_addr_disk == 0 {
1250            return Ok(ClusterLocation::Unallocated);
1251        }
1252
1253        let l2_index = self.l2_table_index(address) as usize;
1254
1255        if !self.l2_cache.contains_key(&l1_index) {
1256            // Not in the cache.
1257            let table =
1258                VecCache::from_vec(Self::read_l2_cluster(&mut self.raw_file, l2_addr_disk)?);
1259
1260            let l1_table = &self.l1_table;
1261            let raw_file = &mut self.raw_file;
1262            self.l2_cache.insert(l1_index, table, |index, evicted| {
1263                Self::write_l2_cluster(raw_file, l1_table[index], evicted.get_values())
1264            })?;
1265        };
1266
1267        let entry = self.l2_cache.get(&l1_index).unwrap()[l2_index];
1268        if entry.is_zero() {
1269            return Ok(ClusterLocation::Zero);
1270        }
1271        match entry.cluster_addr() {
1272            Some(cluster_addr) => Ok(ClusterLocation::Data(
1273                cluster_addr + self.raw_file.cluster_offset(address),
1274            )),
1275            None => Ok(ClusterLocation::Unallocated),
1276        }
1277    }
1278
1279    // Gets the offset of the given guest address in the host file. If L1, L2, or data clusters need
1280    // to be allocated, they will be.
1281    fn file_offset_write(&mut self, address: u64) -> std::io::Result<u64> {
1282        if address >= self.virtual_size() {
1283            return Err(std::io::Error::from_raw_os_error(EINVAL));
1284        }
1285
1286        let l1_index = self.l1_table_index(address) as usize;
1287        let l2_addr_disk = *self
1288            .l1_table
1289            .get(l1_index)
1290            .ok_or_else(|| std::io::Error::from_raw_os_error(EINVAL))?;
1291        let l2_index = self.l2_table_index(address) as usize;
1292
1293        let mut set_refcounts = Vec::new();
1294
1295        if !self.l2_cache.contains_key(&l1_index) {
1296            // Not in the cache.
1297            let l2_table = if l2_addr_disk == 0 {
1298                // Allocate a new cluster to store the L2 table and update the L1 table to point
1299                // to the new table.
1300                let new_addr: u64 = self.get_new_cluster(None)?;
1301                // The cluster refcount starts at one meaning it is used but doesn't need COW.
1302                set_refcounts.push((new_addr, 1));
1303                self.l1_table[l1_index] = new_addr;
1304                VecCache::new(self.l2_entries as usize)
1305            } else {
1306                VecCache::from_vec(Self::read_l2_cluster(&mut self.raw_file, l2_addr_disk)?)
1307            };
1308            let l1_table = &self.l1_table;
1309            let raw_file = &mut self.raw_file;
1310            self.l2_cache.insert(l1_index, l2_table, |index, evicted| {
1311                Self::write_l2_cluster(raw_file, l1_table[index], evicted.get_values())
1312            })?;
1313        }
1314
1315        let mut entry = self.l2_cache.get(&l1_index).unwrap()[l2_index];
1316        if entry.is_zero() && entry.cluster_addr().is_some() {
1317            // A preallocated zero cluster may still contain stale non-zero data on disk and has
1318            // the zero flag set in the L2 entry. Release it first so a fresh zeroed cluster is
1319            // allocated with a normal data L2 entry.
1320            self.zero_cluster(address)?;
1321            entry = self.l2_cache.get(&l1_index).unwrap()[l2_index];
1322        }
1323        let cluster_addr = match entry.cluster_addr() {
1324            None => {
1325                let initial_data = if entry.is_zero() {
1326                    // Zero clusters don't read from the backing file.
1327                    None
1328                } else if let Some(backing) = self.backing_file.as_mut() {
1329                    let cluster_size = self.raw_file.cluster_size();
1330                    let cluster_begin = address - (address % cluster_size);
1331                    let mut cluster_data = vec![0u8; cluster_size as usize];
1332                    let volatile_slice = VolatileSlice::new(&mut cluster_data);
1333                    backing.read_exact_at_volatile(volatile_slice, cluster_begin)?;
1334                    Some(cluster_data)
1335                } else {
1336                    None
1337                };
1338                // Need to allocate a data cluster
1339                let cluster_addr = self.append_data_cluster(initial_data)?;
1340                self.update_cluster_addr(l1_index, l2_index, cluster_addr, &mut set_refcounts)?;
1341                cluster_addr
1342            }
1343            Some(a) => a,
1344        };
1345
1346        for (addr, count) in set_refcounts {
1347            let mut newly_unref = self.set_cluster_refcount(addr, count)?;
1348            self.unref_clusters.append(&mut newly_unref);
1349        }
1350
1351        Ok(cluster_addr + self.raw_file.cluster_offset(address))
1352    }
1353
1354    // Updates the l1 and l2 tables to point to the new `cluster_addr`.
1355    fn update_cluster_addr(
1356        &mut self,
1357        l1_index: usize,
1358        l2_index: usize,
1359        cluster_addr: u64,
1360        set_refcounts: &mut Vec<(u64, u16)>,
1361    ) -> io::Result<()> {
1362        if !self.l2_cache.get(&l1_index).unwrap().dirty() {
1363            // Free the previously used cluster if one exists. Modified tables are always
1364            // witten to new clusters so the L1 table can be committed to disk after they
1365            // are and L1 never points at an invalid table.
1366            // The index must be valid from when it was insterted.
1367            let addr = self.l1_table[l1_index];
1368            if addr != 0 {
1369                self.unref_clusters.push(addr);
1370                set_refcounts.push((addr, 0));
1371            }
1372
1373            // Allocate a new cluster to store the L2 table and update the L1 table to point
1374            // to the new table. The cluster will be written when the cache is flushed, no
1375            // need to copy the data now.
1376            let new_addr: u64 = self.get_new_cluster(None)?;
1377            // The cluster refcount starts at one indicating it is used but doesn't need
1378            // COW.
1379            set_refcounts.push((new_addr, 1));
1380            self.l1_table[l1_index] = new_addr;
1381        }
1382        // 'unwrap' is OK because it was just added.
1383        self.l2_cache.get_mut(&l1_index).unwrap()[l2_index] = L2CacheEntry::new_data(cluster_addr);
1384        Ok(())
1385    }
1386
1387    // Allocate a new cluster and return its offset within the raw file.
1388    fn get_new_cluster(&mut self, initial_data: Option<Vec<u8>>) -> std::io::Result<u64> {
1389        // First use a pre allocated cluster if one is available.
1390        if let Some(free_cluster) = self.avail_clusters.pop() {
1391            if let Some(initial_data) = initial_data {
1392                self.raw_file.write_cluster(free_cluster, initial_data)?;
1393            } else {
1394                self.raw_file.zero_cluster(free_cluster)?;
1395            }
1396            return Ok(free_cluster);
1397        }
1398
1399        let max_valid_cluster_offset = self.refcounts.max_valid_cluster_offset();
1400        if let Some(new_cluster) = self.raw_file.add_cluster_end(max_valid_cluster_offset)? {
1401            if let Some(initial_data) = initial_data {
1402                self.raw_file.write_cluster(new_cluster, initial_data)?;
1403            }
1404            Ok(new_cluster)
1405        } else {
1406            error!("No free clusters in get_new_cluster()");
1407            Err(std::io::Error::from_raw_os_error(ENOSPC))
1408        }
1409    }
1410
1411    // Allocate and initialize a new data cluster. Returns the offset of the
1412    // cluster in to the file on success.
1413    fn append_data_cluster(&mut self, initial_data: Option<Vec<u8>>) -> std::io::Result<u64> {
1414        let new_addr: u64 = self.get_new_cluster(initial_data)?;
1415        // The cluster refcount starts at one indicating it is used but doesn't need COW.
1416        let mut newly_unref = self.set_cluster_refcount(new_addr, 1)?;
1417        self.unref_clusters.append(&mut newly_unref);
1418        Ok(new_addr)
1419    }
1420
1421    // Zero the cluster starting at `address`, deallocating its storage if any was allocated.
1422    // Any future reads of this cluster will return all zeroes.
1423    fn zero_cluster(&mut self, address: u64) -> std::io::Result<()> {
1424        if address >= self.virtual_size() {
1425            return Err(std::io::Error::from_raw_os_error(EINVAL));
1426        }
1427
1428        // Unallocated clusters read from the backing file, so leave a zero cluster if there is one.
1429        let new_entry = if self.backing_file.is_some() {
1430            L2CacheEntry::ZERO
1431        } else {
1432            L2CacheEntry::UNALLOCATED
1433        };
1434
1435        let l1_index = self.l1_table_index(address) as usize;
1436        let l2_addr_disk = *self
1437            .l1_table
1438            .get(l1_index)
1439            .ok_or_else(|| std::io::Error::from_raw_os_error(EINVAL))?;
1440        let l2_index = self.l2_table_index(address) as usize;
1441
1442        if l2_addr_disk == 0 && new_entry == L2CacheEntry::UNALLOCATED {
1443            // The whole L2 table for this address is not allocated yet,
1444            // so the cluster must also be unallocated.
1445            return Ok(());
1446        }
1447
1448        if !self.l2_cache.contains_key(&l1_index) {
1449            // Not in the cache.
1450            let table = if l2_addr_disk == 0 {
1451                // Allocate an L2 table to hold the zero cluster.
1452                let new_addr = self.get_new_cluster(None)?;
1453                let mut newly_unref = self.set_cluster_refcount(new_addr, 1)?;
1454                self.unref_clusters.append(&mut newly_unref);
1455                self.l1_table[l1_index] = new_addr;
1456                VecCache::new(self.l2_entries as usize)
1457            } else {
1458                VecCache::from_vec(Self::read_l2_cluster(&mut self.raw_file, l2_addr_disk)?)
1459            };
1460            let l1_table = &self.l1_table;
1461            let raw_file = &mut self.raw_file;
1462            self.l2_cache.insert(l1_index, table, |index, evicted| {
1463                Self::write_l2_cluster(raw_file, l1_table[index], evicted.get_values())
1464            })?;
1465        }
1466
1467        let entry = self.l2_cache.get(&l1_index).unwrap()[l2_index];
1468        if entry == new_entry {
1469            // This cluster is already zeroed; nothing to do.
1470            return Ok(());
1471        }
1472
1473        // Rewrite the L2 entry to remove the cluster mapping.
1474        // unwrap is safe as we just checked/inserted this entry.
1475        self.l2_cache.get_mut(&l1_index).unwrap()[l2_index] = new_entry;
1476
1477        let Some(cluster_addr) = entry.cluster_addr() else {
1478            // No storage was allocated for this cluster.
1479            return Ok(());
1480        };
1481
1482        // Decrement the refcount.
1483        let refcount = self
1484            .refcounts
1485            .get_cluster_refcount(&mut self.raw_file, cluster_addr)
1486            .map_err(|_| std::io::Error::from_raw_os_error(EINVAL))?;
1487        if refcount == 0 {
1488            return Err(std::io::Error::from_raw_os_error(EINVAL));
1489        }
1490
1491        let new_refcount = refcount - 1;
1492        let mut newly_unref = self.set_cluster_refcount(cluster_addr, new_refcount)?;
1493        self.unref_clusters.append(&mut newly_unref);
1494
1495        if new_refcount == 0 {
1496            let cluster_size = self.raw_file.cluster_size();
1497            // This cluster is no longer in use; deallocate the storage.
1498            // The underlying FS may not support FALLOC_FL_PUNCH_HOLE,
1499            // so don't treat an error as fatal.  Future reads will return zeros anyways.
1500            let _ = self.raw_file.file().punch_hole(cluster_addr, cluster_size);
1501            self.unref_clusters.push(cluster_addr);
1502        }
1503        Ok(())
1504    }
1505
1506    // Fill a range of `length` bytes starting at `address` with zeroes.
1507    // Any future reads of this range will return all zeroes.
1508    // This will deallocate cluster storage when possible.
1509    fn zero_bytes(&mut self, address: u64, length: usize) -> std::io::Result<()> {
1510        let write_count: usize = self.limit_range_file(address, length);
1511
1512        let mut nwritten: usize = 0;
1513        while nwritten < write_count {
1514            let curr_addr = address + nwritten as u64;
1515            let count = self.limit_range_cluster(curr_addr, write_count - nwritten);
1516
1517            if count == self.raw_file.cluster_size() as usize {
1518                // Full cluster - zero the cluster and deallocate any storage.
1519                self.zero_cluster(curr_addr)?;
1520            } else {
1521                // Partial cluster - zero out the relevant bytes.
1522                let offset = match self.file_offset_read(curr_addr)? {
1523                    ClusterLocation::Data(offset) => Some(offset),
1524                    // There is a backing file, so we need to allocate a cluster in order to
1525                    // zero out the hole-punched bytes such that the backing file contents do not
1526                    // show through.
1527                    ClusterLocation::Unallocated if self.backing_file.is_some() => {
1528                        Some(self.file_offset_write(curr_addr)?)
1529                    }
1530                    // Any space in unallocated or zero clusters can be left alone, since they
1531                    // already read back as zeroes.
1532                    ClusterLocation::Unallocated | ClusterLocation::Zero => None,
1533                };
1534                if let Some(offset) = offset {
1535                    // Partial cluster - zero it out.
1536                    self.raw_file.file().write_zeroes_all_at(offset, count)?;
1537                }
1538            }
1539
1540            nwritten += count;
1541        }
1542        Ok(())
1543    }
1544
1545    // Reads an L2 cluster from the disk, returning an error if the file can't be read or if any
1546    // cluster is compressed.
1547    fn read_l2_cluster(
1548        raw_file: &mut QcowRawFile,
1549        cluster_addr: u64,
1550    ) -> std::io::Result<Vec<L2CacheEntry>> {
1551        raw_file
1552            .read_pointer_cluster(cluster_addr, None)?
1553            .into_iter()
1554            .map(L2CacheEntry::from_disk)
1555            .collect()
1556    }
1557
1558    // Writes an L2 cluster to the disk.
1559    fn write_l2_cluster(
1560        raw_file: &mut QcowRawFile,
1561        cluster_addr: u64,
1562        table: &[L2CacheEntry],
1563    ) -> std::io::Result<()> {
1564        let file_values: Vec<u64> = table.iter().map(|entry| entry.to_disk()).collect();
1565        raw_file.write_pointer_table(cluster_addr, &file_values, 0)
1566    }
1567
1568    // Set the refcount for a cluster with the given address.
1569    // Returns a list of any refblocks that can be reused, this happens when a refblock is moved,
1570    // the old location can be reused.
1571    fn set_cluster_refcount(&mut self, address: u64, refcount: u16) -> std::io::Result<Vec<u64>> {
1572        let mut added_clusters = Vec::new();
1573        let mut unref_clusters = Vec::new();
1574        let mut refcount_set = false;
1575        let mut new_cluster = None;
1576
1577        while !refcount_set {
1578            match self.refcounts.set_cluster_refcount(
1579                &mut self.raw_file,
1580                address,
1581                refcount,
1582                new_cluster.take(),
1583            ) {
1584                Ok(None) => {
1585                    refcount_set = true;
1586                }
1587                Ok(Some(freed_cluster)) => {
1588                    unref_clusters.push(freed_cluster);
1589                    refcount_set = true;
1590                }
1591                Err(refcount::Error::EvictingRefCounts(e)) => {
1592                    return Err(e);
1593                }
1594                Err(refcount::Error::InvalidIndex) => {
1595                    return Err(std::io::Error::from_raw_os_error(EINVAL));
1596                }
1597                Err(refcount::Error::NeedCluster(addr)) => {
1598                    // Read the address and call set_cluster_refcount again.
1599                    new_cluster = Some((
1600                        addr,
1601                        VecCache::from_vec(self.raw_file.read_refcount_block(addr)?),
1602                    ));
1603                }
1604                Err(refcount::Error::NeedNewCluster) => {
1605                    // Allocate the cluster and call set_cluster_refcount again.
1606                    let addr = self.get_new_cluster(None)?;
1607                    added_clusters.push(addr);
1608                    new_cluster = Some((
1609                        addr,
1610                        VecCache::new(self.refcounts.refcounts_per_block() as usize),
1611                    ));
1612                }
1613                Err(refcount::Error::ReadingRefCounts(e)) => {
1614                    return Err(e);
1615                }
1616            }
1617        }
1618
1619        for addr in added_clusters {
1620            self.set_cluster_refcount(addr, 1)?;
1621        }
1622        Ok(unref_clusters)
1623    }
1624
1625    fn sync_caches(&mut self) -> std::io::Result<()> {
1626        // Write out all dirty L2 tables.
1627        for (l1_index, l2_table) in self.l2_cache.iter_mut().filter(|(_k, v)| v.dirty()) {
1628            // The index must be valid from when we insterted it.
1629            let addr = self.l1_table[*l1_index];
1630            if addr != 0 {
1631                Self::write_l2_cluster(&mut self.raw_file, addr, l2_table.get_values())?;
1632            } else {
1633                return Err(std::io::Error::from_raw_os_error(EINVAL));
1634            }
1635            l2_table.mark_clean();
1636        }
1637        // Write the modified refcount blocks.
1638        self.refcounts.flush_blocks(&mut self.raw_file)?;
1639        // Make sure metadata(file len) and all data clusters are written.
1640        self.raw_file.file_mut().sync_data()?;
1641
1642        // Push L1 table and refcount table last as all the clusters they point to are now
1643        // guaranteed to be valid.
1644        let mut sync_required = false;
1645        if self.l1_table.dirty() {
1646            self.raw_file.write_pointer_table(
1647                self.header.l1_table_offset,
1648                self.l1_table.get_values(),
1649                0,
1650            )?;
1651            self.l1_table.mark_clean();
1652            sync_required = true;
1653        }
1654        sync_required |= self.refcounts.flush_table(&mut self.raw_file)?;
1655        if sync_required {
1656            self.raw_file.file_mut().sync_data()?;
1657        }
1658        Ok(())
1659    }
1660
1661    // Reads `count` bytes starting at `address`, calling `cb` repeatedly with the data source,
1662    // number of bytes read so far, offset to read from, and number of bytes to read from the file
1663    // in that invocation. If None is given to `cb` in place of the backing file, the `cb` should
1664    // infer zeros would have been read.
1665    fn read_cb<F>(&mut self, address: u64, count: usize, mut cb: F) -> std::io::Result<usize>
1666    where
1667        F: FnMut(Option<&mut dyn DiskFile>, usize, u64, usize) -> std::io::Result<()>,
1668    {
1669        let read_count: usize = self.limit_range_file(address, count);
1670
1671        let mut nread: usize = 0;
1672        while nread < read_count {
1673            let curr_addr = address + nread as u64;
1674            let location = self.file_offset_read(curr_addr)?;
1675            let count = self.limit_range_cluster(curr_addr, read_count - nread);
1676
1677            match (location, self.backing_file.as_mut()) {
1678                (ClusterLocation::Data(offset), _) => {
1679                    cb(Some(self.raw_file.file_mut()), nread, offset, count)?;
1680                }
1681                (ClusterLocation::Unallocated, Some(backing)) => {
1682                    cb(Some(backing.as_mut()), nread, curr_addr, count)?;
1683                }
1684                _ => cb(None, nread, 0, count)?,
1685            }
1686
1687            nread += count;
1688        }
1689        Ok(read_count)
1690    }
1691
1692    // Writes `count` bytes starting at `address`, calling `cb` repeatedly with the backing file,
1693    // number of bytes written so far, raw file offset, and number of bytes to write to the file in
1694    // that invocation.
1695    fn write_cb<F>(&mut self, address: u64, count: usize, mut cb: F) -> std::io::Result<usize>
1696    where
1697        F: FnMut(&mut File, usize, u64, usize) -> std::io::Result<()>,
1698    {
1699        let write_count: usize = self.limit_range_file(address, count);
1700
1701        let mut nwritten: usize = 0;
1702        while nwritten < write_count {
1703            let curr_addr = address + nwritten as u64;
1704            let offset = self.file_offset_write(curr_addr)?;
1705            let count = self.limit_range_cluster(curr_addr, write_count - nwritten);
1706
1707            cb(self.raw_file.file_mut(), nwritten, offset, count)?;
1708
1709            nwritten += count;
1710        }
1711        Ok(write_count)
1712    }
1713}
1714
1715impl Drop for QcowFile {
1716    fn drop(&mut self) {
1717        let _ = self.inner.get_mut().sync_caches();
1718    }
1719}
1720
1721impl AsRawDescriptors for QcowFile {
1722    fn as_raw_descriptors(&self) -> Vec<RawDescriptor> {
1723        // Taking a lock here feels wrong, but this method is generally only used during
1724        // sandboxing, so it should be OK.
1725        let inner = self.inner.lock();
1726        let mut descriptors = vec![inner.raw_file.file().as_raw_descriptor()];
1727        if let Some(backing) = &inner.backing_file {
1728            descriptors.append(&mut backing.as_raw_descriptors());
1729        }
1730        descriptors
1731    }
1732}
1733
1734impl Read for QcowFile {
1735    fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
1736        let inner = self.inner.get_mut();
1737        let len = buf.len();
1738        let slice = VolatileSlice::new(buf);
1739        let read_count = inner.read_cb(
1740            inner.current_offset,
1741            len,
1742            |file, already_read, offset, count| {
1743                let sub_slice = slice.get_slice(already_read, count).unwrap();
1744                match file {
1745                    Some(f) => f.read_exact_at_volatile(sub_slice, offset),
1746                    None => {
1747                        sub_slice.write_bytes(0);
1748                        Ok(())
1749                    }
1750                }
1751            },
1752        )?;
1753        inner.current_offset += read_count as u64;
1754        Ok(read_count)
1755    }
1756}
1757
1758impl Seek for QcowFile {
1759    fn seek(&mut self, pos: SeekFrom) -> std::io::Result<u64> {
1760        let inner = self.inner.get_mut();
1761        let new_offset: Option<u64> = match pos {
1762            SeekFrom::Start(off) => Some(off),
1763            SeekFrom::End(off) => {
1764                if off < 0 {
1765                    0i64.checked_sub(off)
1766                        .and_then(|increment| inner.virtual_size().checked_sub(increment as u64))
1767                } else {
1768                    inner.virtual_size().checked_add(off as u64)
1769                }
1770            }
1771            SeekFrom::Current(off) => {
1772                if off < 0 {
1773                    0i64.checked_sub(off)
1774                        .and_then(|increment| inner.current_offset.checked_sub(increment as u64))
1775                } else {
1776                    inner.current_offset.checked_add(off as u64)
1777                }
1778            }
1779        };
1780
1781        if let Some(o) = new_offset {
1782            if o <= inner.virtual_size() {
1783                inner.current_offset = o;
1784                return Ok(o);
1785            }
1786        }
1787        Err(std::io::Error::from_raw_os_error(EINVAL))
1788    }
1789}
1790
1791impl Write for QcowFile {
1792    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
1793        let inner = self.inner.get_mut();
1794        let write_count = inner.write_cb(
1795            inner.current_offset,
1796            buf.len(),
1797            |file, offset, raw_offset, count| {
1798                file.seek(SeekFrom::Start(raw_offset))?;
1799                file.write_all(&buf[offset..(offset + count)])
1800            },
1801        )?;
1802        inner.current_offset += write_count as u64;
1803        Ok(write_count)
1804    }
1805
1806    fn flush(&mut self) -> std::io::Result<()> {
1807        self.fsync()
1808    }
1809}
1810
1811impl FileReadWriteAtVolatile for QcowFile {
1812    fn read_at_volatile(&self, slice: VolatileSlice, offset: u64) -> io::Result<usize> {
1813        let mut inner = self.inner.lock();
1814        inner.read_cb(offset, slice.size(), |file, read, offset, count| {
1815            let sub_slice = slice.get_slice(read, count).unwrap();
1816            match file {
1817                Some(f) => f.read_exact_at_volatile(sub_slice, offset),
1818                None => {
1819                    sub_slice.write_bytes(0);
1820                    Ok(())
1821                }
1822            }
1823        })
1824    }
1825
1826    fn write_at_volatile(&self, slice: VolatileSlice, offset: u64) -> io::Result<usize> {
1827        let mut inner = self.inner.lock();
1828        inner.write_cb(offset, slice.size(), |file, offset, raw_offset, count| {
1829            let sub_slice = slice.get_slice(offset, count).unwrap();
1830            file.write_all_at_volatile(sub_slice, raw_offset)
1831        })
1832    }
1833}
1834
1835impl FileSync for QcowFile {
1836    fn fsync(&self) -> std::io::Result<()> {
1837        let mut inner = self.inner.lock();
1838        inner.sync_caches()?;
1839        let unref_clusters = std::mem::take(&mut inner.unref_clusters);
1840        inner.avail_clusters.extend(unref_clusters);
1841        Ok(())
1842    }
1843
1844    fn fdatasync(&self) -> io::Result<()> {
1845        // QcowFile does not implement fdatasync. Just fall back to fsync.
1846        self.fsync()
1847    }
1848}
1849
1850impl FileSetLen for QcowFile {
1851    fn set_len(&self, _len: u64) -> std::io::Result<()> {
1852        Err(std::io::Error::other(
1853            "set_len() not supported for QcowFile",
1854        ))
1855    }
1856}
1857
1858impl DiskGetLen for QcowFile {
1859    fn get_len(&self) -> io::Result<u64> {
1860        Ok(self.virtual_size)
1861    }
1862}
1863
1864impl FileAllocate for QcowFile {
1865    fn allocate(&self, offset: u64, len: u64) -> io::Result<()> {
1866        let mut inner = self.inner.lock();
1867        // Call write_cb with a do-nothing callback, which will have the effect
1868        // of allocating all clusters in the specified range.
1869        inner.write_cb(
1870            offset,
1871            len as usize,
1872            |_file, _offset, _raw_offset, _count| Ok(()),
1873        )?;
1874        Ok(())
1875    }
1876}
1877
1878impl PunchHole for QcowFile {
1879    fn punch_hole(&self, offset: u64, length: u64) -> std::io::Result<()> {
1880        let mut inner = self.inner.lock();
1881        let mut remaining = length;
1882        let mut offset = offset;
1883        while remaining > 0 {
1884            let chunk_length = min(remaining, usize::MAX as u64) as usize;
1885            inner.zero_bytes(offset, chunk_length)?;
1886            remaining -= chunk_length as u64;
1887            offset += chunk_length as u64;
1888        }
1889        Ok(())
1890    }
1891}
1892
1893impl WriteZeroesAt for QcowFile {
1894    fn write_zeroes_at(&self, offset: u64, length: usize) -> io::Result<usize> {
1895        self.punch_hole(offset, length as u64)?;
1896        Ok(length)
1897    }
1898}
1899
1900impl ToAsyncDisk for QcowFile {
1901    fn to_async_disk(self: Box<Self>, ex: &Executor) -> crate::Result<Box<dyn AsyncDisk>> {
1902        Ok(Box::new(AsyncDiskFileWrapper::new(*self, ex)))
1903    }
1904}
1905
1906// Returns an Error if the given offset doesn't align to a cluster boundary.
1907fn offset_is_cluster_boundary(offset: u64, cluster_bits: u32) -> Result<()> {
1908    if offset & ((0x01 << cluster_bits) - 1) != 0 {
1909        return Err(Error::InvalidOffset(offset));
1910    }
1911    Ok(())
1912}
1913
1914#[cfg(test)]
1915mod tests {
1916    use std::fs::OpenOptions;
1917    use std::io::Read;
1918    use std::io::Seek;
1919    use std::io::SeekFrom;
1920    use std::io::Write;
1921
1922    use tempfile::tempfile;
1923    use tempfile::TempDir;
1924
1925    use super::*;
1926
1927    fn valid_header() -> Vec<u8> {
1928        vec![
1929            0x51u8, 0x46, 0x49, 0xfb, // magic
1930            0x00, 0x00, 0x00, 0x03, // version
1931            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // backing file offset
1932            0x00, 0x00, 0x00, 0x00, // backing file size
1933            0x00, 0x00, 0x00, 0x10, // cluster_bits
1934            0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, // size
1935            0x00, 0x00, 0x00, 0x00, // crypt method
1936            0x00, 0x00, 0x01, 0x00, // L1 size
1937            0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, // L1 table offset
1938            0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, // refcount table offset
1939            0x00, 0x00, 0x00, 0x03, // refcount table clusters
1940            0x00, 0x00, 0x00, 0x00, // nb snapshots
1941            0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, // snapshots offset
1942            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // incompatible_features
1943            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // compatible_features
1944            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // autoclear_features
1945            0x00, 0x00, 0x00, 0x04, // refcount_order
1946            0x00, 0x00, 0x00, 0x68, // header_length
1947        ]
1948    }
1949
1950    // Test case found by clusterfuzz to allocate excessive memory.
1951    fn test_huge_header() -> Vec<u8> {
1952        vec![
1953            0x51, 0x46, 0x49, 0xfb, // magic
1954            0x00, 0x00, 0x00, 0x03, // version
1955            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // backing file offset
1956            0x00, 0x00, 0x00, 0x00, // backing file size
1957            0x00, 0x00, 0x00, 0x09, // cluster_bits
1958            0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, // size
1959            0x00, 0x00, 0x00, 0x00, // crypt method
1960            0x00, 0x00, 0x01, 0x00, // L1 size
1961            0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, // L1 table offset
1962            0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, // refcount table offset
1963            0x00, 0x00, 0x00, 0x03, // refcount table clusters
1964            0x00, 0x00, 0x00, 0x00, // nb snapshots
1965            0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, // snapshots offset
1966            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // incompatible_features
1967            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // compatible_features
1968            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // autoclear_features
1969            0x00, 0x00, 0x00, 0x04, // refcount_order
1970            0x00, 0x00, 0x00, 0x68, // header_length
1971        ]
1972    }
1973
1974    fn test_params() -> DiskFileParams {
1975        DiskFileParams {
1976            path: PathBuf::from("/foo"),
1977            ..Default::default()
1978        }
1979    }
1980
1981    fn basic_file(header: &[u8]) -> File {
1982        let mut disk_file = tempfile().expect("failed to create temp file");
1983        disk_file.write_all(header).unwrap();
1984        disk_file.set_len(0x8000_0000).unwrap();
1985        disk_file.seek(SeekFrom::Start(0)).unwrap();
1986        disk_file
1987    }
1988
1989    fn with_basic_file<F>(header: &[u8], mut testfn: F)
1990    where
1991        F: FnMut(File),
1992    {
1993        testfn(basic_file(header)); // File closed when the function exits.
1994    }
1995
1996    fn with_default_file<F>(file_size: u64, mut testfn: F)
1997    where
1998        F: FnMut(QcowFile),
1999    {
2000        let file = tempfile().expect("failed to create temp file");
2001        let qcow_file = QcowFile::new(file, test_params(), file_size).unwrap();
2002
2003        testfn(qcow_file); // File closed when the function exits.
2004    }
2005
2006    // Test helper function to convert a normal slice to a VolatileSlice and write it.
2007    fn write_all_at(qcow: &mut QcowFile, data: &[u8], offset: u64) -> std::io::Result<()> {
2008        let mut mem = data.to_owned();
2009        let vslice = VolatileSlice::new(&mut mem);
2010        qcow.write_all_at_volatile(vslice, offset)
2011    }
2012
2013    // Test helper function to read to a VolatileSlice and copy it to a normal slice.
2014    fn read_exact_at(qcow: &mut QcowFile, data: &mut [u8], offset: u64) -> std::io::Result<()> {
2015        let mut mem = data.to_owned();
2016        let vslice = VolatileSlice::new(&mut mem);
2017        qcow.read_exact_at_volatile(vslice, offset)?;
2018        vslice.copy_to(data);
2019        Ok(())
2020    }
2021
2022    #[test]
2023    fn default_header() {
2024        let header = QcowHeader::create_for_size_and_path(0x10_0000, None);
2025        let mut disk_file = tempfile().expect("failed to create temp file");
2026        header
2027            .expect("Failed to create header.")
2028            .write_to(&mut disk_file)
2029            .expect("Failed to write header to shm.");
2030        disk_file.seek(SeekFrom::Start(0)).unwrap();
2031        QcowFile::from(disk_file, test_params())
2032            .expect("Failed to create Qcow from default Header");
2033    }
2034
2035    #[test]
2036    fn header_read() {
2037        with_basic_file(&valid_header(), |mut disk_file: File| {
2038            QcowHeader::new(&mut disk_file).expect("Failed to create Header.");
2039        });
2040    }
2041
2042    #[test]
2043    fn invalid_magic() {
2044        let invalid_header = vec![0x51u8, 0x46, 0x4a, 0xfb];
2045        with_basic_file(&invalid_header, |mut disk_file: File| {
2046            QcowHeader::new(&mut disk_file).expect_err("Invalid header worked.");
2047        });
2048    }
2049
2050    #[test]
2051    fn invalid_refcount_order() {
2052        let mut header = valid_header();
2053        header[99] = 2;
2054        with_basic_file(&header, |disk_file: File| {
2055            QcowFile::from(disk_file, test_params()).expect_err("Invalid refcount order worked.");
2056        });
2057    }
2058
2059    #[test]
2060    fn invalid_incompatible_features() {
2061        let mut header = valid_header();
2062        header[79] = 1;
2063        with_basic_file(&header, |disk_file: File| {
2064            assert!(matches!(
2065                QcowFile::from(disk_file, test_params()),
2066                Err(Error::UnsupportedIncompatibleFeatures(1))
2067            ));
2068        });
2069    }
2070
2071    #[test]
2072    fn invalid_nb_snapshots() {
2073        let mut header = valid_header();
2074        header[63] = 1;
2075        with_basic_file(&header, |disk_file: File| {
2076            assert!(matches!(
2077                QcowFile::from(disk_file, test_params()),
2078                Err(Error::SnapshotsNotSupported)
2079            ));
2080        });
2081    }
2082
2083    #[test]
2084    fn invalid_cluster_bits() {
2085        let mut header = valid_header();
2086        header[23] = 3;
2087        with_basic_file(&header, |disk_file: File| {
2088            QcowFile::from(disk_file, test_params()).expect_err("Failed to create file.");
2089        });
2090    }
2091
2092    #[test]
2093    fn test_header_huge_file() {
2094        let header = test_huge_header();
2095        with_basic_file(&header, |disk_file: File| {
2096            QcowFile::from(disk_file, test_params()).expect_err("Failed to create file.");
2097        });
2098    }
2099
2100    #[test]
2101    fn test_header_excessive_file_size_rejected() {
2102        let mut header = valid_header();
2103        header[24..32].copy_from_slice(&[0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1e]);
2104        with_basic_file(&header, |disk_file: File| {
2105            QcowFile::from(disk_file, test_params()).expect_err("Failed to create file.");
2106        });
2107    }
2108
2109    #[test]
2110    fn test_huge_l1_table() {
2111        let mut header = valid_header();
2112        header[36] = 0x12;
2113        with_basic_file(&header, |disk_file: File| {
2114            QcowFile::from(disk_file, test_params()).expect_err("Failed to create file.");
2115        });
2116    }
2117
2118    #[test]
2119    fn test_header_1_tb_file_min_cluster() {
2120        let mut header = test_huge_header();
2121        header[24] = 0;
2122        header[26] = 1;
2123        header[31] = 0;
2124        // 1 TB with the min cluster size makes the arrays too big, it should fail.
2125        with_basic_file(&header, |disk_file: File| {
2126            QcowFile::from(disk_file, test_params()).expect_err("Failed to create file.");
2127        });
2128    }
2129
2130    #[cfg_attr(windows, ignore = "TODO(b/257958782): Enable large test on windows")]
2131    #[test]
2132    fn test_header_1_tb_file() {
2133        let mut header = test_huge_header();
2134        // reset to 1 TB size.
2135        header[24] = 0;
2136        header[26] = 1;
2137        header[31] = 0;
2138        // set cluster_bits
2139        header[23] = 16;
2140        with_basic_file(&header, |disk_file: File| {
2141            let mut qcow =
2142                QcowFile::from(disk_file, test_params()).expect("Failed to create file.");
2143            let value = 0x0000_0040_3f00_ffffu64;
2144            write_all_at(&mut qcow, &value.to_le_bytes(), 0x100_0000_0000 - 8)
2145                .expect("failed to write data");
2146        });
2147    }
2148
2149    #[test]
2150    fn test_header_huge_num_refcounts() {
2151        let mut header = valid_header();
2152        header[56..60].copy_from_slice(&[0x02, 0x00, 0xe8, 0xff]);
2153        with_basic_file(&header, |disk_file: File| {
2154            QcowFile::from(disk_file, test_params())
2155                .expect_err("Created disk with excessive refcount clusters");
2156        });
2157    }
2158
2159    #[test]
2160    fn test_header_huge_refcount_offset() {
2161        let mut header = valid_header();
2162        header[48..56].copy_from_slice(&[0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x02, 0x00]);
2163        with_basic_file(&header, |disk_file: File| {
2164            QcowFile::from(disk_file, test_params())
2165                .expect_err("Created disk with excessive refcount offset");
2166        });
2167    }
2168
2169    #[cfg_attr(windows, ignore = "TODO(b/257958782): Enable large test on windows")]
2170    #[test]
2171    fn write_read_start() {
2172        with_basic_file(&valid_header(), |disk_file: File| {
2173            let mut q = QcowFile::from(disk_file, test_params()).unwrap();
2174            write_all_at(&mut q, b"test first bytes", 0).expect("Failed to write test string.");
2175            let mut buf = [0u8; 4];
2176            read_exact_at(&mut q, &mut buf, 0).expect("Failed to read.");
2177            assert_eq!(&buf, b"test");
2178        });
2179    }
2180
2181    #[cfg_attr(windows, ignore = "TODO(b/257958782): Enable large test on windows")]
2182    #[test]
2183    fn write_read_start_backing() {
2184        let disk_file = basic_file(&valid_header());
2185        let mut backing = QcowFile::from(disk_file, test_params()).unwrap();
2186        write_all_at(&mut backing, b"test first bytes", 0).expect("Failed to write test string.");
2187        let mut buf = [0u8; 4];
2188        let wrapping_disk_file = basic_file(&valid_header());
2189        let mut wrapping = QcowFile::from(wrapping_disk_file, test_params()).unwrap();
2190        wrapping.set_backing_file(Some(Box::new(backing)));
2191        read_exact_at(&mut wrapping, &mut buf, 0).expect("Failed to read.");
2192        assert_eq!(&buf, b"test");
2193    }
2194
2195    #[cfg_attr(windows, ignore = "TODO(b/257958782): Enable large test on windows")]
2196    #[test]
2197    fn write_read_start_backing_overlap() {
2198        let disk_file = basic_file(&valid_header());
2199        let mut backing = QcowFile::from(disk_file, test_params()).unwrap();
2200        write_all_at(&mut backing, b"test first bytes", 0).expect("Failed to write test string.");
2201        let wrapping_disk_file = basic_file(&valid_header());
2202        let mut wrapping = QcowFile::from(wrapping_disk_file, test_params()).unwrap();
2203        wrapping.set_backing_file(Some(Box::new(backing)));
2204        write_all_at(&mut wrapping, b"TEST", 0).expect("Failed to write second test string.");
2205        let mut buf = [0u8; 10];
2206        read_exact_at(&mut wrapping, &mut buf, 0).expect("Failed to read.");
2207        assert_eq!(&buf, b"TEST first");
2208    }
2209
2210    #[cfg_attr(windows, ignore = "TODO(b/257958782): Enable large test on windows")]
2211    #[test]
2212    fn offset_write_read() {
2213        with_basic_file(&valid_header(), |disk_file: File| {
2214            let mut q = QcowFile::from(disk_file, test_params()).unwrap();
2215            let b = [0x55u8; 0x1000];
2216            write_all_at(&mut q, &b, 0xfff2000).expect("Failed to write test string.");
2217            let mut buf = [0u8; 4];
2218            read_exact_at(&mut q, &mut buf, 0xfff2000).expect("Failed to read.");
2219            assert_eq!(buf[0], 0x55);
2220        });
2221    }
2222
2223    #[cfg_attr(windows, ignore = "TODO(b/257958782): Enable large test on windows")]
2224    #[test]
2225    fn write_zeroes_read() {
2226        with_basic_file(&valid_header(), |disk_file: File| {
2227            let mut q = QcowFile::from(disk_file, test_params()).unwrap();
2228            // Write some test data.
2229            let b = [0x55u8; 0x1000];
2230            write_all_at(&mut q, &b, 0xfff2000).expect("Failed to write test string.");
2231            // Overwrite the test data with zeroes.
2232            q.write_zeroes_all_at(0xfff2000, 0x200)
2233                .expect("Failed to write zeroes.");
2234            // Verify that the correct part of the data was zeroed out.
2235            let mut buf = [0u8; 0x1000];
2236            read_exact_at(&mut q, &mut buf, 0xfff2000).expect("Failed to read.");
2237            assert_eq!(buf[0], 0);
2238            assert_eq!(buf[0x1FF], 0);
2239            assert_eq!(buf[0x200], 0x55);
2240            assert_eq!(buf[0xFFF], 0x55);
2241        });
2242    }
2243
2244    #[cfg_attr(windows, ignore = "TODO(b/257958782): Enable large test on windows")]
2245    #[test]
2246    fn write_zeroes_full_cluster() {
2247        // Choose a size that is larger than a cluster.
2248        // valid_header uses cluster_bits = 12, which corresponds to a cluster size of 4096.
2249        const CHUNK_SIZE: usize = 4096 * 2 + 512;
2250        with_basic_file(&valid_header(), |disk_file: File| {
2251            let mut q = QcowFile::from(disk_file, test_params()).unwrap();
2252            // Write some test data.
2253            let b = [0x55u8; CHUNK_SIZE];
2254            write_all_at(&mut q, &b, 0).expect("Failed to write test string.");
2255            // Overwrite the full cluster with zeroes.
2256            q.write_zeroes_all_at(0, CHUNK_SIZE)
2257                .expect("Failed to write zeroes.");
2258            // Verify that the data was zeroed out.
2259            let mut buf = [0u8; CHUNK_SIZE];
2260            read_exact_at(&mut q, &mut buf, 0).expect("Failed to read.");
2261            assert_eq!(buf[0], 0);
2262            assert_eq!(buf[CHUNK_SIZE - 1], 0);
2263        });
2264    }
2265
2266    #[cfg_attr(windows, ignore = "TODO(b/257958782): Enable large test on windows")]
2267    #[test]
2268    fn write_zeroes_backing() {
2269        let disk_file = basic_file(&valid_header());
2270        let mut backing = QcowFile::from(disk_file, test_params()).unwrap();
2271        // Write some test data.
2272        let b = [0x55u8; 0x1000];
2273        write_all_at(&mut backing, &b, 0xfff2000).expect("Failed to write test string.");
2274        let wrapping_disk_file = basic_file(&valid_header());
2275        let mut wrapping = QcowFile::from(wrapping_disk_file, test_params()).unwrap();
2276        wrapping.set_backing_file(Some(Box::new(backing)));
2277        // Overwrite the test data with zeroes.
2278        // This should allocate new clusters in the wrapping file so that they can be zeroed.
2279        wrapping
2280            .write_zeroes_all_at(0xfff2000, 0x200)
2281            .expect("Failed to write zeroes.");
2282        // Verify that the correct part of the data was zeroed out.
2283        let mut buf = [0u8; 0x1000];
2284        read_exact_at(&mut wrapping, &mut buf, 0xfff2000).expect("Failed to read.");
2285        assert_eq!(buf[0], 0);
2286        assert_eq!(buf[0x1FF], 0);
2287        assert_eq!(buf[0x200], 0x55);
2288        assert_eq!(buf[0xFFF], 0x55);
2289    }
2290
2291    #[cfg_attr(windows, ignore = "TODO(b/257958782): Enable large test on windows")]
2292    #[test]
2293    fn write_zeroes_backing_full_cluster() {
2294        const CLUSTER_SIZE: usize = 0x10000;
2295        let mut backing = tempfile().unwrap();
2296        backing.write_all(&[0x55; 2 * CLUSTER_SIZE]).unwrap();
2297        let disk_file = basic_file(&valid_header());
2298        let mut q = QcowFile::from(disk_file.try_clone().unwrap(), test_params()).unwrap();
2299        q.set_backing_file(Some(Box::new(backing.try_clone().unwrap())));
2300
2301        // Zeroing a full cluster should write a zero cluster instead of allocating storage.
2302        q.write_zeroes_all_at(0, CLUSTER_SIZE).unwrap();
2303        drop(q);
2304        let mut q = QcowFile::from(disk_file, test_params()).unwrap();
2305        let inner = q.inner.get_mut();
2306        let l2_addr = inner.l1_table[0];
2307        let l2_table = inner.raw_file.read_pointer_cluster(l2_addr, None).unwrap();
2308        assert_eq!(l2_table[0], ZERO_CLUSTER_FLAG);
2309
2310        q.set_backing_file(Some(Box::new(backing)));
2311        let mut buf = vec![0u8; 2 * CLUSTER_SIZE];
2312        read_exact_at(&mut q, &mut buf, 0).unwrap();
2313        assert!(buf[..CLUSTER_SIZE].iter().all(|&b| b == 0));
2314        assert!(buf[CLUSTER_SIZE..].iter().all(|&b| b == 0x55));
2315
2316        // Writing to a zero cluster should not copy data from the backing file.
2317        write_all_at(&mut q, &[0xaa], 0).unwrap();
2318        read_exact_at(&mut q, &mut buf, 0).unwrap();
2319        assert_eq!(buf[0], 0xaa);
2320        assert!(buf[1..CLUSTER_SIZE].iter().all(|&b| b == 0));
2321
2322        // Zeroing the cluster again should free its storage.
2323        let inner = q.inner.get_mut();
2324        let ClusterLocation::Data(data_addr) = inner.file_offset_read(0).unwrap() else {
2325            panic!("cluster should be allocated");
2326        };
2327        q.write_zeroes_all_at(0, CLUSTER_SIZE).unwrap();
2328        read_exact_at(&mut q, &mut buf, 0).unwrap();
2329        assert!(buf[..CLUSTER_SIZE].iter().all(|&b| b == 0));
2330        let inner = q.inner.get_mut();
2331        let refcount = inner
2332            .refcounts
2333            .get_cluster_refcount(&mut inner.raw_file, data_addr)
2334            .unwrap();
2335        assert_eq!(refcount, 0);
2336    }
2337
2338    #[cfg_attr(windows, ignore = "TODO(b/257958782): Enable large test on windows")]
2339    #[test]
2340    fn write_preallocated_zero_cluster() {
2341        const CLUSTER_SIZE: usize = 0x10000;
2342        with_basic_file(&valid_header(), |disk_file: File| {
2343            let mut q = QcowFile::from(disk_file, test_params()).unwrap();
2344            write_all_at(&mut q, &[0x55; CLUSTER_SIZE], 0).unwrap();
2345
2346            // Mark the cluster as a preallocated zero cluster while its on-disk bytes are still
2347            // non-zero.
2348            let inner = q.inner.get_mut();
2349            let ClusterLocation::Data(preallocated) = inner.file_offset_read(0).unwrap() else {
2350                panic!("cluster should be allocated");
2351            };
2352            inner.l2_cache.get_mut(&0).unwrap()[0] = L2CacheEntry(preallocated | ZERO_CLUSTER_FLAG);
2353
2354            // A partial write to the preallocated zero cluster must clear the zero flag and must
2355            // not expose the stale 0x55 bytes from the rest of the cluster.
2356            write_all_at(&mut q, &[0xaa], 0).unwrap();
2357            let mut buf = vec![0u8; CLUSTER_SIZE];
2358            read_exact_at(&mut q, &mut buf, 0).unwrap();
2359            assert_eq!(buf[0], 0xaa);
2360            assert!(buf[1..].iter().all(|&b| b == 0));
2361            let inner = q.inner.get_mut();
2362            let refcount = inner
2363                .refcounts
2364                .get_cluster_refcount(&mut inner.raw_file, preallocated)
2365                .unwrap();
2366            assert_eq!(refcount, 0);
2367        });
2368    }
2369    #[test]
2370    fn test_header() {
2371        with_basic_file(&valid_header(), |disk_file: File| {
2372            let mut q = QcowFile::from(disk_file, test_params()).unwrap();
2373            assert_eq!(q.inner.get_mut().virtual_size(), 0x20_0000_0000);
2374        });
2375    }
2376
2377    #[test]
2378    fn read_small_buffer() {
2379        with_basic_file(&valid_header(), |disk_file: File| {
2380            let mut q = QcowFile::from(disk_file, test_params()).unwrap();
2381            let mut b = [5u8; 16];
2382            read_exact_at(&mut q, &mut b, 1000).expect("Failed to read.");
2383            assert_eq!(0, b[0]);
2384            assert_eq!(0, b[15]);
2385        });
2386    }
2387
2388    #[cfg_attr(windows, ignore = "TODO(b/257958782): Enable large test on windows")]
2389    #[test]
2390    fn replay_ext4() {
2391        with_basic_file(&valid_header(), |disk_file: File| {
2392            let mut q = QcowFile::from(disk_file, test_params()).unwrap();
2393            const BUF_SIZE: usize = 0x1000;
2394            let mut b = [0u8; BUF_SIZE];
2395
2396            struct Transfer {
2397                pub write: bool,
2398                pub addr: u64,
2399            }
2400
2401            // Write transactions from mkfs.ext4.
2402            let xfers: Vec<Transfer> = vec![
2403                Transfer {
2404                    write: false,
2405                    addr: 0xfff0000,
2406                },
2407                Transfer {
2408                    write: false,
2409                    addr: 0xfffe000,
2410                },
2411                Transfer {
2412                    write: false,
2413                    addr: 0x0,
2414                },
2415                Transfer {
2416                    write: false,
2417                    addr: 0x1000,
2418                },
2419                Transfer {
2420                    write: false,
2421                    addr: 0xffff000,
2422                },
2423                Transfer {
2424                    write: false,
2425                    addr: 0xffdf000,
2426                },
2427                Transfer {
2428                    write: false,
2429                    addr: 0xfff8000,
2430                },
2431                Transfer {
2432                    write: false,
2433                    addr: 0xffe0000,
2434                },
2435                Transfer {
2436                    write: false,
2437                    addr: 0xffce000,
2438                },
2439                Transfer {
2440                    write: false,
2441                    addr: 0xffb6000,
2442                },
2443                Transfer {
2444                    write: false,
2445                    addr: 0xffab000,
2446                },
2447                Transfer {
2448                    write: false,
2449                    addr: 0xffa4000,
2450                },
2451                Transfer {
2452                    write: false,
2453                    addr: 0xff8e000,
2454                },
2455                Transfer {
2456                    write: false,
2457                    addr: 0xff86000,
2458                },
2459                Transfer {
2460                    write: false,
2461                    addr: 0xff84000,
2462                },
2463                Transfer {
2464                    write: false,
2465                    addr: 0xff89000,
2466                },
2467                Transfer {
2468                    write: false,
2469                    addr: 0xfe7e000,
2470                },
2471                Transfer {
2472                    write: false,
2473                    addr: 0x100000,
2474                },
2475                Transfer {
2476                    write: false,
2477                    addr: 0x3000,
2478                },
2479                Transfer {
2480                    write: false,
2481                    addr: 0x7000,
2482                },
2483                Transfer {
2484                    write: false,
2485                    addr: 0xf000,
2486                },
2487                Transfer {
2488                    write: false,
2489                    addr: 0x2000,
2490                },
2491                Transfer {
2492                    write: false,
2493                    addr: 0x4000,
2494                },
2495                Transfer {
2496                    write: false,
2497                    addr: 0x5000,
2498                },
2499                Transfer {
2500                    write: false,
2501                    addr: 0x6000,
2502                },
2503                Transfer {
2504                    write: false,
2505                    addr: 0x8000,
2506                },
2507                Transfer {
2508                    write: false,
2509                    addr: 0x9000,
2510                },
2511                Transfer {
2512                    write: false,
2513                    addr: 0xa000,
2514                },
2515                Transfer {
2516                    write: false,
2517                    addr: 0xb000,
2518                },
2519                Transfer {
2520                    write: false,
2521                    addr: 0xc000,
2522                },
2523                Transfer {
2524                    write: false,
2525                    addr: 0xd000,
2526                },
2527                Transfer {
2528                    write: false,
2529                    addr: 0xe000,
2530                },
2531                Transfer {
2532                    write: false,
2533                    addr: 0x10000,
2534                },
2535                Transfer {
2536                    write: false,
2537                    addr: 0x11000,
2538                },
2539                Transfer {
2540                    write: false,
2541                    addr: 0x12000,
2542                },
2543                Transfer {
2544                    write: false,
2545                    addr: 0x13000,
2546                },
2547                Transfer {
2548                    write: false,
2549                    addr: 0x14000,
2550                },
2551                Transfer {
2552                    write: false,
2553                    addr: 0x15000,
2554                },
2555                Transfer {
2556                    write: false,
2557                    addr: 0x16000,
2558                },
2559                Transfer {
2560                    write: false,
2561                    addr: 0x17000,
2562                },
2563                Transfer {
2564                    write: false,
2565                    addr: 0x18000,
2566                },
2567                Transfer {
2568                    write: false,
2569                    addr: 0x19000,
2570                },
2571                Transfer {
2572                    write: false,
2573                    addr: 0x1a000,
2574                },
2575                Transfer {
2576                    write: false,
2577                    addr: 0x1b000,
2578                },
2579                Transfer {
2580                    write: false,
2581                    addr: 0x1c000,
2582                },
2583                Transfer {
2584                    write: false,
2585                    addr: 0x1d000,
2586                },
2587                Transfer {
2588                    write: false,
2589                    addr: 0x1e000,
2590                },
2591                Transfer {
2592                    write: false,
2593                    addr: 0x1f000,
2594                },
2595                Transfer {
2596                    write: false,
2597                    addr: 0x21000,
2598                },
2599                Transfer {
2600                    write: false,
2601                    addr: 0x22000,
2602                },
2603                Transfer {
2604                    write: false,
2605                    addr: 0x24000,
2606                },
2607                Transfer {
2608                    write: false,
2609                    addr: 0x40000,
2610                },
2611                Transfer {
2612                    write: false,
2613                    addr: 0x0,
2614                },
2615                Transfer {
2616                    write: false,
2617                    addr: 0x3000,
2618                },
2619                Transfer {
2620                    write: false,
2621                    addr: 0x7000,
2622                },
2623                Transfer {
2624                    write: false,
2625                    addr: 0x0,
2626                },
2627                Transfer {
2628                    write: false,
2629                    addr: 0x1000,
2630                },
2631                Transfer {
2632                    write: false,
2633                    addr: 0x2000,
2634                },
2635                Transfer {
2636                    write: false,
2637                    addr: 0x3000,
2638                },
2639                Transfer {
2640                    write: false,
2641                    addr: 0x0,
2642                },
2643                Transfer {
2644                    write: false,
2645                    addr: 0x449000,
2646                },
2647                Transfer {
2648                    write: false,
2649                    addr: 0x48000,
2650                },
2651                Transfer {
2652                    write: false,
2653                    addr: 0x48000,
2654                },
2655                Transfer {
2656                    write: false,
2657                    addr: 0x448000,
2658                },
2659                Transfer {
2660                    write: false,
2661                    addr: 0x44a000,
2662                },
2663                Transfer {
2664                    write: false,
2665                    addr: 0x48000,
2666                },
2667                Transfer {
2668                    write: false,
2669                    addr: 0x48000,
2670                },
2671                Transfer {
2672                    write: true,
2673                    addr: 0x0,
2674                },
2675                Transfer {
2676                    write: true,
2677                    addr: 0x448000,
2678                },
2679                Transfer {
2680                    write: true,
2681                    addr: 0x449000,
2682                },
2683                Transfer {
2684                    write: true,
2685                    addr: 0x44a000,
2686                },
2687                Transfer {
2688                    write: true,
2689                    addr: 0xfff0000,
2690                },
2691                Transfer {
2692                    write: true,
2693                    addr: 0xfff1000,
2694                },
2695                Transfer {
2696                    write: true,
2697                    addr: 0xfff2000,
2698                },
2699                Transfer {
2700                    write: true,
2701                    addr: 0xfff3000,
2702                },
2703                Transfer {
2704                    write: true,
2705                    addr: 0xfff4000,
2706                },
2707                Transfer {
2708                    write: true,
2709                    addr: 0xfff5000,
2710                },
2711                Transfer {
2712                    write: true,
2713                    addr: 0xfff6000,
2714                },
2715                Transfer {
2716                    write: true,
2717                    addr: 0xfff7000,
2718                },
2719                Transfer {
2720                    write: true,
2721                    addr: 0xfff8000,
2722                },
2723                Transfer {
2724                    write: true,
2725                    addr: 0xfff9000,
2726                },
2727                Transfer {
2728                    write: true,
2729                    addr: 0xfffa000,
2730                },
2731                Transfer {
2732                    write: true,
2733                    addr: 0xfffb000,
2734                },
2735                Transfer {
2736                    write: true,
2737                    addr: 0xfffc000,
2738                },
2739                Transfer {
2740                    write: true,
2741                    addr: 0xfffd000,
2742                },
2743                Transfer {
2744                    write: true,
2745                    addr: 0xfffe000,
2746                },
2747                Transfer {
2748                    write: true,
2749                    addr: 0xffff000,
2750                },
2751            ];
2752
2753            for xfer in &xfers {
2754                if xfer.write {
2755                    write_all_at(&mut q, &b, xfer.addr).expect("Failed to write.");
2756                } else {
2757                    read_exact_at(&mut q, &mut b, xfer.addr).expect("Failed to read.");
2758                }
2759            }
2760        });
2761    }
2762
2763    #[cfg_attr(windows, ignore = "TODO(b/257958782): Enable large test on windows")]
2764    #[test]
2765    fn combo_write_read() {
2766        with_default_file(1024 * 1024 * 1024 * 256, |mut qcow_file| {
2767            const NUM_BLOCKS: usize = 55;
2768            const BLOCK_SIZE: usize = 0x1_0000;
2769            const OFFSET: u64 = 0x1_0000_0020;
2770            let data = [0x55u8; BLOCK_SIZE];
2771            let mut readback = [0u8; BLOCK_SIZE];
2772            for i in 0..NUM_BLOCKS {
2773                let seek_offset = OFFSET + (i as u64) * (BLOCK_SIZE as u64);
2774                write_all_at(&mut qcow_file, &data, seek_offset)
2775                    .expect("Failed to write test data.");
2776                // Read back the data to check it was written correctly.
2777                read_exact_at(&mut qcow_file, &mut readback, seek_offset).expect("Failed to read.");
2778                for (orig, read) in data.iter().zip(readback.iter()) {
2779                    assert_eq!(orig, read);
2780                }
2781            }
2782            // Check that address 0 is still zeros.
2783            read_exact_at(&mut qcow_file, &mut readback, 0).expect("Failed to read.");
2784            for read in readback.iter() {
2785                assert_eq!(*read, 0);
2786            }
2787            // Check the data again after the writes have happened.
2788            for i in 0..NUM_BLOCKS {
2789                let seek_offset = OFFSET + (i as u64) * (BLOCK_SIZE as u64);
2790                read_exact_at(&mut qcow_file, &mut readback, seek_offset).expect("Failed to read.");
2791                for (orig, read) in data.iter().zip(readback.iter()) {
2792                    assert_eq!(orig, read);
2793                }
2794            }
2795
2796            assert_eq!(
2797                qcow_file.inner.get_mut().first_zero_refcount().unwrap(),
2798                None
2799            );
2800        });
2801    }
2802
2803    #[test]
2804    fn rebuild_refcounts() {
2805        with_basic_file(&valid_header(), |mut disk_file: File| {
2806            let header = QcowHeader::new(&mut disk_file).expect("Failed to create Header.");
2807            let cluster_size = 65536;
2808            let mut raw_file =
2809                QcowRawFile::from(disk_file, cluster_size).expect("Failed to create QcowRawFile.");
2810            QcowFileInner::rebuild_refcounts(&mut raw_file, header)
2811                .expect("Failed to rebuild recounts.");
2812        });
2813    }
2814
2815    #[cfg_attr(windows, ignore = "TODO(b/257958782): Enable large test on windows")]
2816    #[test]
2817    fn nested_qcow() {
2818        let tmp_dir = TempDir::new().unwrap();
2819
2820        // A file `backing` is backing a qcow file `qcow.l1`, which in turn is backing another
2821        // qcow file.
2822        let backing_file_path = tmp_dir.path().join("backing");
2823        let _backing_file = OpenOptions::new()
2824            .read(true)
2825            .write(true)
2826            .create_new(true)
2827            .open(&backing_file_path)
2828            .unwrap();
2829
2830        let level1_qcow_file_path = tmp_dir.path().join("qcow.l1");
2831        let level1_qcow_file = OpenOptions::new()
2832            .read(true)
2833            .write(true)
2834            .create_new(true)
2835            .open(&level1_qcow_file_path)
2836            .unwrap();
2837        let _level1_qcow_file = QcowFile::new_from_backing(
2838            level1_qcow_file,
2839            test_params(),
2840            backing_file_path.to_str().unwrap(),
2841        )
2842        .unwrap();
2843
2844        let level2_qcow_file = tempfile().unwrap();
2845        let _level2_qcow_file = QcowFile::new_from_backing(
2846            level2_qcow_file,
2847            test_params(),
2848            level1_qcow_file_path.to_str().unwrap(),
2849        )
2850        .expect("failed to create level2 qcow file");
2851    }
2852
2853    #[test]
2854    fn io_seek() {
2855        with_default_file(1024 * 1024 * 10, |mut qcow_file| {
2856            // Cursor should start at 0.
2857            assert_eq!(qcow_file.stream_position().unwrap(), 0);
2858
2859            // Seek 1 MB from start.
2860            assert_eq!(
2861                qcow_file.seek(SeekFrom::Start(1024 * 1024)).unwrap(),
2862                1024 * 1024
2863            );
2864
2865            // Rewind 1 MB + 1 byte (past beginning) - seeking to a negative offset is an error and
2866            // should not move the cursor.
2867            qcow_file
2868                .seek(SeekFrom::Current(-(1024 * 1024 + 1)))
2869                .expect_err("negative offset seek should fail");
2870            assert_eq!(qcow_file.stream_position().unwrap(), 1024 * 1024);
2871
2872            // Seek to last byte.
2873            assert_eq!(
2874                qcow_file.seek(SeekFrom::End(-1)).unwrap(),
2875                1024 * 1024 * 10 - 1
2876            );
2877
2878            // Seek to EOF.
2879            assert_eq!(qcow_file.seek(SeekFrom::End(0)).unwrap(), 1024 * 1024 * 10);
2880
2881            // Seek past EOF is not allowed.
2882            qcow_file
2883                .seek(SeekFrom::End(1))
2884                .expect_err("seek past EOF should fail");
2885        });
2886    }
2887
2888    #[test]
2889    fn io_write_read() {
2890        with_default_file(1024 * 1024 * 10, |mut qcow_file| {
2891            const BLOCK_SIZE: usize = 0x1_0000;
2892            let data_55 = [0x55u8; BLOCK_SIZE];
2893            let data_aa = [0xaau8; BLOCK_SIZE];
2894            let mut readback = [0u8; BLOCK_SIZE];
2895
2896            qcow_file.write_all(&data_55).unwrap();
2897            assert_eq!(qcow_file.stream_position().unwrap(), BLOCK_SIZE as u64);
2898
2899            qcow_file.write_all(&data_aa).unwrap();
2900            assert_eq!(qcow_file.stream_position().unwrap(), BLOCK_SIZE as u64 * 2);
2901
2902            // Read BLOCK_SIZE of just 0xaa.
2903            assert_eq!(
2904                qcow_file
2905                    .seek(SeekFrom::Current(-(BLOCK_SIZE as i64)))
2906                    .unwrap(),
2907                BLOCK_SIZE as u64
2908            );
2909            qcow_file.read_exact(&mut readback).unwrap();
2910            assert_eq!(qcow_file.stream_position().unwrap(), BLOCK_SIZE as u64 * 2);
2911            for (orig, read) in data_aa.iter().zip(readback.iter()) {
2912                assert_eq!(orig, read);
2913            }
2914
2915            // Read BLOCK_SIZE of just 0x55.
2916            qcow_file.rewind().unwrap();
2917            qcow_file.read_exact(&mut readback).unwrap();
2918            for (orig, read) in data_55.iter().zip(readback.iter()) {
2919                assert_eq!(orig, read);
2920            }
2921
2922            // Read BLOCK_SIZE crossing between the block of 0x55 and 0xaa.
2923            qcow_file
2924                .seek(SeekFrom::Start(BLOCK_SIZE as u64 / 2))
2925                .unwrap();
2926            qcow_file.read_exact(&mut readback).unwrap();
2927            for (orig, read) in data_55[BLOCK_SIZE / 2..]
2928                .iter()
2929                .chain(data_aa[..BLOCK_SIZE / 2].iter())
2930                .zip(readback.iter())
2931            {
2932                assert_eq!(orig, read);
2933            }
2934        });
2935    }
2936
2937    #[test]
2938    fn header_with_backing_format() {
2939        let header = QcowHeader::create_for_size_and_path(
2940            0x10_0000,
2941            Some(("/my/path/to/a/file", crate::ImageType::Raw)),
2942        )
2943        .expect("Failed to create header.");
2944        let mut disk_file = tempfile().expect("failed to create temp file");
2945        header
2946            .write_to(&mut disk_file)
2947            .expect("Failed to write header.");
2948        disk_file.seek(SeekFrom::Start(0)).unwrap();
2949        let read_header = QcowHeader::new(&mut disk_file).expect("Failed to create header.");
2950        assert_eq!(
2951            header.backing_file_path,
2952            Some(String::from("/my/path/to/a/file"))
2953        );
2954        assert_eq!(read_header.backing_file_path, header.backing_file_path);
2955        assert_eq!(read_header.backing_file_format, Some(crate::ImageType::Raw));
2956    }
2957
2958    #[test]
2959    fn test_backing_format_extension_magic() {
2960        const EXPECTED_MAGIC: [u8; 4] = [0xe2, 0x79, 0x2a, 0xca];
2961
2962        let header = QcowHeader::create_for_size_and_path(
2963            0x10_0000,
2964            Some(("/my/path/to/a/file", crate::ImageType::Raw)),
2965        )
2966        .expect("Failed to create header.");
2967        let mut disk_file = tempfile().expect("failed to create temp file");
2968        header
2969            .write_to(&mut disk_file)
2970            .expect("Failed to write header.");
2971
2972        // Verify serialized header extension magic bytes on disk at offset V3_BARE_HEADER_SIZE
2973        // (104) equal the literal big-endian bytes [0xe2, 0x79, 0x2a, 0xca].
2974        let mut magic_bytes = [0u8; 4];
2975        disk_file
2976            .seek(SeekFrom::Start(V3_BARE_HEADER_SIZE as u64))
2977            .unwrap();
2978        disk_file.read_exact(&mut magic_bytes).unwrap();
2979        assert_eq!(magic_bytes, EXPECTED_MAGIC);
2980
2981        // Verify that QcowHeader::new parses a header containing those literal bytes.
2982        disk_file.seek(SeekFrom::Start(0)).unwrap();
2983        let read_header = QcowHeader::new(&mut disk_file).expect("Failed to parse header.");
2984        assert_eq!(read_header.backing_file_format, Some(crate::ImageType::Raw));
2985
2986        // Overwrite the magic bytes with an unrecognized extension type to ensure
2987        // QcowHeader::new does not recognize unknown extensions as backing format.
2988        disk_file
2989            .seek(SeekFrom::Start(V3_BARE_HEADER_SIZE as u64))
2990            .unwrap();
2991        disk_file.write_all(&[0xe2, 0x79, 0x26, 0x8f]).unwrap();
2992        disk_file.seek(SeekFrom::Start(0)).unwrap();
2993        let header_with_unknown_ext =
2994            QcowHeader::new(&mut disk_file).expect("Failed to parse header.");
2995        assert_eq!(header_with_unknown_ext.backing_file_format, None);
2996
2997        // Rewrite literal specification bytes and verify it parses successfully.
2998        disk_file
2999            .seek(SeekFrom::Start(V3_BARE_HEADER_SIZE as u64))
3000            .unwrap();
3001        disk_file.write_all(&EXPECTED_MAGIC).unwrap();
3002        disk_file.seek(SeekFrom::Start(0)).unwrap();
3003        let read_header = QcowHeader::new(&mut disk_file).expect("Failed to parse header.");
3004        assert_eq!(read_header.backing_file_format, Some(crate::ImageType::Raw));
3005    }
3006
3007    #[test]
3008    fn test_backing_format_respected() {
3009        let temp_dir = TempDir::new().expect("failed to create temp dir");
3010        let backing_path = temp_dir.path().join("backing.raw");
3011        let parent_path = temp_dir.path().join("parent.qcow2");
3012
3013        // Create backing file with fake Qcow2 magic
3014        let mut backing_file = File::create(&backing_path).unwrap();
3015        backing_file.write_all(&QCOW_MAGIC.to_be_bytes()).unwrap();
3016        backing_file.set_len(1024 * 1024).unwrap();
3017        drop(backing_file);
3018
3019        // Create parent Qcow2 header specifying backing file and backing format = RAW
3020        let header_raw = QcowHeader::create_for_size_and_path(
3021            1024 * 1024 * 10,
3022            Some((backing_path.to_str().unwrap(), crate::ImageType::Raw)),
3023        )
3024        .expect("Failed to create header.");
3025
3026        let mut parent_file = File::create(&parent_path).unwrap();
3027        header_raw.write_to(&mut parent_file).unwrap();
3028        drop(parent_file);
3029
3030        // Open parent. It should succeed because backing is opened as raw.
3031        let parent_file = OpenOptions::new()
3032            .read(true)
3033            .write(true)
3034            .open(&parent_path)
3035            .unwrap();
3036        let mut params = test_params();
3037        params.path = parent_path.clone();
3038        let qcow = QcowFile::from(parent_file, params);
3039        assert!(
3040            qcow.is_ok(),
3041            "Failed to open with explicit raw backing: {:?}",
3042            qcow.err()
3043        );
3044
3045        // Create parent Qcow2 header WITHOUT backing format extension
3046        let mut header_auto = QcowHeader::create_for_size_and_path(
3047            1024 * 1024 * 10,
3048            Some((backing_path.to_str().unwrap(), crate::ImageType::Raw)),
3049        )
3050        .expect("Failed to create header.");
3051        header_auto.backing_file_format = None;
3052        header_auto.backing_file_offset = V3_BARE_HEADER_SIZE as u64 + 8;
3053
3054        let mut parent_file_auto = File::create(&parent_path).unwrap();
3055        header_auto.write_to(&mut parent_file_auto).unwrap();
3056        drop(parent_file_auto);
3057
3058        // Open parent. It should fail because backing format extension is missing.
3059        let parent_file_auto = OpenOptions::new()
3060            .read(true)
3061            .write(true)
3062            .open(&parent_path)
3063            .unwrap();
3064        let mut params = test_params();
3065        params.path = parent_path.clone();
3066        let qcow_auto = QcowFile::from(parent_file_auto, params);
3067        assert!(
3068            matches!(
3069                qcow_auto.as_ref().err(),
3070                Some(&Error::MissingBackingFileFormat)
3071            ),
3072            "Expected Error::MissingBackingFileFormat, got: {qcow_auto:?}"
3073        );
3074    }
3075
3076    #[test]
3077    fn test_unsupported_backing_format() {
3078        // 1. Creation time rejection of AndroidSparse
3079        let header_err = QcowHeader::create_for_size_and_path(
3080            1024 * 1024 * 10,
3081            Some(("backing.img", crate::ImageType::AndroidSparse)),
3082        );
3083        assert!(
3084            matches!(
3085                header_err.as_ref().err(),
3086                Some(&Error::UnsupportedBackingFileFormat(_))
3087            ),
3088            "Expected Error::UnsupportedBackingFileFormat, got: {header_err:?}"
3089        );
3090
3091        // 2. Open time rejection of unknown format string
3092        let header = QcowHeader::create_for_size_and_path(
3093            1024 * 1024 * 10,
3094            Some(("backing.raw", crate::ImageType::Raw)),
3095        )
3096        .unwrap();
3097
3098        let mut file = tempfile().unwrap();
3099        header.write_to(&mut file).unwrap();
3100
3101        // Overwrite the format extension with "invalid" (len 7, fits in same 8-byte pad block)
3102        file.seek(SeekFrom::Start(108)).unwrap();
3103        file.write_all(&7u32.to_be_bytes()).unwrap(); // New length: 7
3104        file.write_all(b"invalid\0").unwrap(); // New format string + 1 byte padding to 8 bytes
3105
3106        // Try to read it back. It should fail during header parsing.
3107        file.seek(SeekFrom::Start(0)).unwrap();
3108        let read_result = QcowHeader::new(&mut file);
3109        match &read_result {
3110            Err(Error::UnsupportedBackingFileFormat(fmt)) => {
3111                assert_eq!(fmt, "invalid");
3112            }
3113            other => {
3114                panic!("Expected Error::UnsupportedBackingFileFormat(\"invalid\"), got: {other:?}")
3115            }
3116        }
3117    }
3118
3119    #[test]
3120    fn test_create_excessive_size_rejected() {
3121        let header = QcowHeader::create_for_size_and_path(MAX_QCOW_FILE_SIZE + 1, None);
3122        assert!(matches!(header.err(), Some(Error::FileTooBig(_))));
3123    }
3124
3125    #[cfg(feature = "composite-disk")]
3126    #[test]
3127    fn test_composite_backing() {
3128        use protobuf::Message;
3129        use protos::cdisk_spec;
3130
3131        use crate::composite::CDISK_MAGIC;
3132
3133        let temp_dir = TempDir::new().expect("failed to create temp dir");
3134        let component_path = temp_dir.path().join("component.raw");
3135        let spec_path = temp_dir.path().join("backing.cdisk");
3136        let parent_path = temp_dir.path().join("parent.qcow2");
3137
3138        // Create component file (raw)
3139        let component_file = File::create(&component_path).unwrap();
3140        component_file.set_len(1024 * 1024).unwrap(); // 1MB
3141        drop(component_file);
3142
3143        // Create composite spec
3144        let mut proto = cdisk_spec::CompositeDisk::new();
3145        proto.version = 2;
3146        proto.length = 1024 * 1024;
3147        let mut component = cdisk_spec::ComponentDisk::new();
3148        // Use relative path for component
3149        component.file_path = String::from("component.raw");
3150        component.offset = 0;
3151        component.file_offset = 0;
3152        component.read_write_capability = cdisk_spec::ReadWriteCapability::READ_WRITE.into();
3153        proto.component_disks.push(component);
3154
3155        let mut spec_file = File::create(&spec_path).unwrap();
3156        spec_file.write_all(CDISK_MAGIC.as_bytes()).unwrap();
3157        proto.write_to_writer(&mut spec_file).unwrap();
3158        drop(spec_file);
3159
3160        // Create parent Qcow2 header specifying composite backing file
3161        let header = QcowHeader::create_for_size_and_path(
3162            1024 * 1024 * 10,
3163            Some((spec_path.to_str().unwrap(), crate::ImageType::CompositeDisk)),
3164        )
3165        .expect("Failed to create header.");
3166
3167        let mut parent_file = File::create(&parent_path).unwrap();
3168        header.write_to(&mut parent_file).unwrap();
3169        drop(parent_file);
3170
3171        // Open parent. It should succeed and open backing as composite.
3172        let parent_file = OpenOptions::new()
3173            .read(true)
3174            .write(true)
3175            .open(&parent_path)
3176            .unwrap();
3177        let mut params = test_params();
3178        params.path = parent_path.clone();
3179        let qcow = QcowFile::from(parent_file, params);
3180        assert!(
3181            qcow.is_ok(),
3182            "Failed to open with composite backing: {:?}",
3183            qcow.err()
3184        );
3185    }
3186
3187    #[test]
3188    fn test_backing_file_escapes_first_cluster() {
3189        // Create a header with backing file
3190        let mut header = QcowHeader::create_for_size_and_path(
3191            1024 * 1024 * 10,
3192            Some(("backing.raw", crate::ImageType::Raw)),
3193        )
3194        .unwrap();
3195
3196        // Default cluster size is 65536.
3197        // Manually set backing_file_offset to be outside the first cluster.
3198        header.backing_file_offset = 70000;
3199
3200        let mut file = tempfile().unwrap();
3201        header.write_to(&mut file).unwrap();
3202
3203        // Try to read it back. It should fail because backing_file_offset escapes first cluster.
3204        let read_result = QcowHeader::new(&mut file);
3205        match &read_result {
3206            Err(Error::BackingFileEscapesFirstCluster) => {}
3207            other => panic!("Expected Error::BackingFileEscapesFirstCluster, got: {other:?}"),
3208        }
3209    }
3210}