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