Skip to main content

imago/qcow2/
preallocation.rs

1//! Implementation for preallocation.
2//!
3//! Preallocation is used for new images or when growing images.
4
5use super::*;
6use crate::storage::ext::write_full_zeroes;
7
8#[maybe_async]
9impl<S: Storage + 'static, F: WrappedFormat<S> + 'static> Qcow2<S, F> {
10    /// Make the given range zero.
11    ///
12    /// Bypasses disk bound checking, i.e. can and will write beyond the image end.
13    pub(super) async fn preallocate_zero(&self, mut offset: u64, length: u64) -> io::Result<()> {
14        let max_offset = offset.checked_add(length).ok_or_else(|| {
15            io::Error::new(io::ErrorKind::InvalidInput, "Preallocate range overflow")
16        })?;
17
18        // It does not matter what happens after the virtual disk end, so we may align up to the
19        // next full cluster (this prevents needless COW at the image end)
20        let max_offset = max_offset.next_multiple_of(self.header.cluster_size() as u64);
21
22        while offset < max_offset {
23            let (zofs, zlen) = self
24                .ensure_fixed_mapping(
25                    GuestOffset(offset),
26                    max_offset - offset,
27                    FixedMapping::ZeroRetainAllocation,
28                )
29                .await?;
30            let zofs = zofs.0;
31            if zofs > offset {
32                self.preallocate(offset, zofs - offset, storage::PreallocateMode::Zero)
33                    .await?;
34            }
35            offset = zofs + zlen;
36            if zlen == 0 && offset < max_offset {
37                self.preallocate(offset, max_offset - offset, storage::PreallocateMode::Zero)
38                    .await?;
39                break;
40            }
41        }
42
43        Ok(())
44    }
45
46    /// Preallocate the given range as data clusters.
47    ///
48    /// Does not write data beyond trying to ensure `storage_prealloc_mode` for the underlying
49    /// clusters.
50    ///
51    /// Bypasses disk bound checking, i.e. can and will write beyond the image end.
52    pub(super) async fn preallocate(
53        &self,
54        mut offset: u64,
55        length: u64,
56        storage_prealloc_mode: storage::PreallocateMode,
57    ) -> io::Result<()> {
58        let max_offset = offset.checked_add(length).ok_or_else(|| {
59            io::Error::new(io::ErrorKind::InvalidInput, "Preallocate range overflow")
60        })?;
61
62        // External data file: Resize with preallocation to exact size
63        if let Some(data_file) = self.storage.as_ref() {
64            data_file.resize(max_offset, storage_prealloc_mode).await?;
65        }
66
67        while offset < max_offset {
68            let (file, fofs, flen) = self
69                .do_ensure_data_mapping(GuestOffset(offset), max_offset - offset, true, true)
70                .await?;
71
72            // Data in metadata file: Allocate data areas as we go
73            if self.storage.is_none() {
74                match storage_prealloc_mode {
75                    storage::PreallocateMode::None => (), // handled below
76                    storage::PreallocateMode::Zero => {
77                        file.write_zeroes(fofs, flen).await?;
78                    }
79                    storage::PreallocateMode::Allocate => {
80                        file.write_allocated_zeroes(fofs, flen).await?;
81                    }
82                    storage::PreallocateMode::WriteData => {
83                        write_full_zeroes(file, fofs, flen).await?;
84                    }
85                }
86            }
87
88            offset += flen;
89        }
90
91        Ok(())
92    }
93}