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    pub(super) async fn preallocate_zero(&self, mut offset: u64, length: u64) -> io::Result<()> {
11        let max_offset = offset.checked_add(length).ok_or_else(|| {
12            io::Error::new(io::ErrorKind::InvalidInput, "Preallocate range overflow")
13        })?;
14
15        while offset < max_offset {
16            let (zofs, zlen) = self
17                .ensure_zero_mapping(offset, max_offset - offset)
18                .await?;
19            if zofs > offset {
20                self.preallocate_write_data(offset, zofs - offset).await?;
21            }
22            offset = zofs + zlen;
23            if zlen == 0 && offset < max_offset {
24                self.preallocate_write_data(offset, max_offset - offset)
25                    .await?;
26                break;
27            }
28        }
29
30        Ok(())
31    }
32
33    /// Preallocate the given range as data clusters.
34    ///
35    /// Does not write data beyond trying to ensure `storage_prealloc_mode` for the underlying
36    /// clusters.
37    pub(super) async fn preallocate(
38        &self,
39        mut offset: u64,
40        length: u64,
41        storage_prealloc_mode: storage::PreallocateMode,
42    ) -> io::Result<()> {
43        let max_offset = offset.checked_add(length).ok_or_else(|| {
44            io::Error::new(io::ErrorKind::InvalidInput, "Preallocate range overflow")
45        })?;
46
47        if let Some(data_file) = self.storage.as_ref() {
48            data_file.resize(max_offset, storage_prealloc_mode).await?;
49        }
50
51        while offset < max_offset {
52            let (file, fofs, flen) = self
53                .ensure_data_mapping(offset, max_offset - offset, true)
54                .await?;
55            // TODO: This is terrible, `do_ensure_data_mapping()` should get a parameter for this
56            let file_end_ofs = fofs + flen;
57            if let Ok(file_size) = file.size() {
58                if file_size < file_end_ofs {
59                    file.resize(file_end_ofs, storage_prealloc_mode).await?;
60                }
61            }
62            offset += flen;
63        }
64
65        Ok(())
66    }
67
68    /// Write zeroes to the given range.
69    pub(super) async fn preallocate_write_data(
70        &self,
71        mut offset: u64,
72        length: u64,
73    ) -> io::Result<()> {
74        let max_offset = offset.checked_add(length).ok_or_else(|| {
75            io::Error::new(io::ErrorKind::InvalidInput, "Preallocate range overflow")
76        })?;
77
78        while offset < max_offset {
79            let (file, fofs, flen) = self
80                .ensure_data_mapping(offset, max_offset - offset, true)
81                .await?;
82            write_full_zeroes(file, fofs, flen).await?;
83            offset += flen;
84        }
85
86        Ok(())
87    }
88}