imago/qcow2/
builder.rs

1//! Builders for opening and creating qcow2 images.
2
3use super::*;
4use crate::format::builder::{
5    FormatCreateBuilderBase, FormatDriverBuilderBase, FormatOrBuilder, StorageOrPath,
6};
7use crate::DenyImplicitOpenGate;
8use std::marker::PhantomData;
9use std::path::PathBuf;
10
11/// Options builder for opening a qcow2 image.
12///
13/// Allows setting various options one by one to open a qcow2 image.
14pub struct Qcow2OpenBuilder<S: Storage + 'static, F: WrappedFormat<S> + 'static = FormatAccess<S>> {
15    /// Basic options.
16    base: FormatDriverBuilderBase<S>,
17
18    /// Backing image
19    ///
20    /// `None` to open the image as specified by the image header, `Some(None)` to not open any
21    /// backing image, and `Some(Some(_))` to use that backing image.
22    backing: Option<Option<FormatOrBuilder<S, F>>>,
23
24    /// External data file
25    ///
26    /// `None` to open the file as specified by the image header, `Some(None)` to not open any data
27    /// file, and `Some(Some(_))` to use that data file.
28    data_file: Option<Option<StorageOrPath<S>>>,
29}
30
31/// Options builder for creating (formatting) a qcow2 image.
32///
33/// Allows setting various options for a new qcow2 image.
34pub struct Qcow2CreateBuilder<S: Storage + 'static, F: WrappedFormat<S> + 'static = FormatAccess<S>>
35{
36    /// Basic options.
37    base: FormatCreateBuilderBase<S>,
38
39    /// Backing image filename and format
40    backing: Option<(String, String)>,
41
42    /// External data file name and the file itself
43    data_file: Option<(String, S)>,
44
45    /// Cluster size
46    cluster_size: usize,
47
48    /// Refcount bit width
49    refcount_width: usize,
50
51    /// Needed for the correct `create_open()` return type
52    _wrapped_format: PhantomData<F>,
53}
54
55impl<S: Storage + 'static, F: WrappedFormat<S> + 'static> Qcow2OpenBuilder<S, F> {
56    /// Create a new instance.
57    fn with_base(base: FormatDriverBuilderBase<S>) -> Self {
58        Qcow2OpenBuilder {
59            base,
60            backing: None,
61            data_file: None,
62        }
63    }
64
65    /// Set a backing image.
66    ///
67    /// This overrides the implicit backing image given in the image header.  Passing `None` means
68    /// not to use any backing image (regardless of whether the image header defines a backing
69    /// image).
70    pub fn backing(mut self, backing: Option<F>) -> Self {
71        self.backing = Some(backing.map(FormatOrBuilder::Format));
72        self
73    }
74
75    /// Declare a backing image by path.
76    ///
77    /// Let imago open the given path as an image with the given format.
78    ///
79    /// Use with caution, as the given image will be opened with default options.
80    /// [`Qcow2OpenBuilder::backing()`] is preferable, as it allows you control over how the
81    /// backing image is opened.
82    pub fn backing_path<P: AsRef<Path>>(mut self, backing: P, format: Format) -> Self {
83        self.backing = Some(Some(FormatOrBuilder::new_builder(format, backing)));
84        self
85    }
86
87    /// Set an external data file.
88    ///
89    /// This overrides the implicit external data file given in the image header.  Passing `None`
90    /// means not to use any external data file (regardless of whether the image header defines an
91    /// external data file, and regardless of whether the image header says the image has an
92    /// external data file).
93    ///
94    /// Similarly, passing a data file will then always use that data file, regardless of whether
95    /// the image header says the image has an external data file.
96    ///
97    /// Note that it is wrong to set a data file for an image that does not have one, and it is
98    /// wrong to enforce not using a data file for an image that has one.  There is no way to know
99    /// whether the image needs an external data file until it is opened.
100    ///
101    /// If you want to open a specific data file if and only if the image needs it, call
102    /// `Qcow2OpenBuilder::data_file(None)` to prevent any data file from being automatically
103    /// opened; open the image, then check [`Qcow2::requires_external_data_file()`], and, if true,
104    /// invoke [`Qcow2::set_data_file()`].
105    pub fn data_file(mut self, data_file: Option<S>) -> Self {
106        self.data_file = Some(data_file.map(StorageOrPath::Storage));
107        self
108    }
109}
110
111impl<S: Storage + 'static, F: WrappedFormat<S> + 'static> FormatDriverBuilder<S>
112    for Qcow2OpenBuilder<S, F>
113{
114    type Format = Qcow2<S, F>;
115    const FORMAT: Format = Format::Qcow2;
116
117    fn new(image: S) -> Self {
118        Self::with_base(FormatDriverBuilderBase::new(image))
119    }
120
121    fn new_path<P: AsRef<Path>>(path: P) -> Self {
122        Self::with_base(FormatDriverBuilderBase::new_path(path))
123    }
124
125    fn write(mut self, write: bool) -> Self {
126        self.base.set_write(write);
127        self
128    }
129
130    fn storage_open_options(mut self, options: StorageOpenOptions) -> Self {
131        self.base.set_storage_open_options(options);
132        self
133    }
134
135    async fn open<G: ImplicitOpenGate<S>>(self, mut gate: G) -> io::Result<Self::Format> {
136        let writable = self.base.get_writable();
137        let storage_opts = self.base.make_storage_opts();
138        let metadata = self.base.open_image(&mut gate).await?;
139
140        let mut qcow2 = Qcow2::<S, F>::do_open(metadata, writable, storage_opts.clone()).await?;
141
142        if let Some(backing) = self.backing {
143            let backing = match backing {
144                None => None,
145                Some(backing) => Some(
146                    backing
147                        .open_format(storage_opts.clone().write(false), &mut gate)
148                        .await
149                        .err_context(|| "Backing file")?,
150                ),
151            };
152            qcow2.set_backing(backing);
153        }
154
155        if let Some(data_file) = self.data_file {
156            let data_file = match data_file {
157                None => None,
158                Some(data_file) => Some(
159                    data_file
160                        .open_storage(storage_opts, &mut gate)
161                        .await
162                        .err_context(|| "External data file")?,
163                ),
164            };
165            qcow2.set_data_file(data_file);
166        }
167
168        qcow2.open_implicit_dependencies_gated(gate).await?;
169
170        Ok(qcow2)
171    }
172
173    fn get_image_path(&self) -> Option<PathBuf> {
174        self.base.get_image_path()
175    }
176
177    fn get_writable(&self) -> bool {
178        self.base.get_writable()
179    }
180
181    fn get_storage_open_options(&self) -> Option<&StorageOpenOptions> {
182        self.base.get_storage_opts()
183    }
184}
185
186impl<S: Storage + 'static, F: WrappedFormat<S> + 'static> Qcow2CreateBuilder<S, F> {
187    /// Set a backing image.
188    ///
189    /// Set the path to the backing image to be written into the image header; this path will be
190    /// interpreted relative to the qcow2 image file.
191    ///
192    /// The backing format should be one of “qcow2” or “raw”.
193    ///
194    /// Neither of filename or format are checked for validity.
195    pub fn backing(mut self, backing_filename: String, backing_format: String) -> Self {
196        self.backing = Some((backing_filename, backing_format));
197        self
198    }
199
200    /// Set an external data file.
201    ///
202    /// Set the path for an external data file.  This path will be interpreted relative to the
203    /// qcow2 image file.  This path is not checked for whether it matches `file` or even points to
204    /// anything at all.
205    ///
206    /// `file` is the data file itself; it is necessary to pass this storage object into the
207    /// builder for preallocation purposes.
208    pub fn data_file(mut self, filename: String, file: S) -> Self {
209        self.data_file = Some((filename, file));
210        self
211    }
212
213    /// Set the cluster size (in bytes).
214    ///
215    /// A cluster is the unit of allocation for qcow2 images.  Smaller clusters can lead to better
216    /// COW performance, but worse performance for fully allocated images, and have increased
217    /// metadata size overhead.
218    ///
219    /// Must be a power of two between 512 and 2 MiB (inclusive).
220    ///
221    /// The default is 64 KiB.
222    pub fn cluster_size(mut self, size: usize) -> Self {
223        self.cluster_size = size;
224        self
225    }
226
227    /// Set the refcount width in bits.
228    ///
229    /// Reference counting is used to determine empty areas in the image file, though this only
230    /// needs refcounts of 0 and 1, i.e. a reference bit width of 1.
231    ///
232    /// Larger refcount bit widths are only needed when using internal snapshots, in which case
233    /// multiple snapshots can share clusters.
234    ///
235    /// Must be a power of two between 1 and 64 (inclusive).
236    ///
237    /// The default is 16 bits.
238    pub fn refcount_width(mut self, bits: usize) -> Self {
239        self.refcount_width = bits;
240        self
241    }
242}
243
244impl<S: Storage + 'static, F: WrappedFormat<S> + 'static> FormatCreateBuilder<S>
245    for Qcow2CreateBuilder<S, F>
246{
247    const FORMAT: Format = Format::Qcow2;
248    type DriverBuilder = Qcow2OpenBuilder<S, F>;
249
250    fn new(image: S) -> Self {
251        Qcow2CreateBuilder {
252            base: FormatCreateBuilderBase::new(image),
253            backing: None,
254            data_file: None,
255            cluster_size: 65536,
256            refcount_width: 16,
257            _wrapped_format: PhantomData,
258        }
259    }
260
261    fn size(mut self, size: u64) -> Self {
262        self.base.set_size(size);
263        self
264    }
265
266    fn preallocate(mut self, prealloc_mode: PreallocateMode) -> Self {
267        self.base.set_preallocate(prealloc_mode);
268        self
269    }
270
271    fn get_size(&self) -> u64 {
272        self.base.get_size()
273    }
274
275    fn get_preallocate(&self) -> PreallocateMode {
276        self.base.get_preallocate()
277    }
278
279    async fn create(self) -> io::Result<()> {
280        self.create_open(DenyImplicitOpenGate::default(), |image| {
281            // data file will be set by `create_open()`
282            Ok(Qcow2::<S, F>::builder(image).backing(None).write(true))
283        })
284        .await?
285        .flush()
286        .await?;
287
288        Ok(())
289    }
290
291    async fn create_open<
292        G: ImplicitOpenGate<S>,
293        OBF: FnOnce(S) -> io::Result<Qcow2OpenBuilder<S, F>>,
294    >(
295        self,
296        open_gate: G,
297        open_builder_fn: OBF,
298    ) -> io::Result<Qcow2<S, F>> {
299        let size = self.base.get_size();
300        let prealloc = self.base.get_preallocate();
301        let image = self.base.get_image();
302
303        let cluster_size = self.cluster_size;
304        if !cluster_size.is_power_of_two() {
305            return Err(io::Error::new(
306                io::ErrorKind::InvalidInput,
307                format!("Cluster size {cluster_size} is not a power of two"),
308            ));
309        }
310
311        let cs_range = MIN_CLUSTER_SIZE..=MAX_CLUSTER_SIZE;
312        if !cs_range.contains(&cluster_size) {
313            return Err(io::Error::new(
314                io::ErrorKind::InvalidInput,
315                format!("Cluster size {cluster_size} not in {cs_range:?}"),
316            ));
317        }
318
319        let cluster_bits = cluster_size.trailing_zeros();
320        assert!(1 << cluster_bits == cluster_size);
321
322        let refcount_width = self.refcount_width;
323        if !refcount_width.is_power_of_two() {
324            return Err(io::Error::new(
325                io::ErrorKind::InvalidInput,
326                format!("Refcount width {refcount_width} is not a power of two"),
327            ));
328        }
329
330        let rw_range = MIN_REFCOUNT_WIDTH..=MAX_REFCOUNT_WIDTH;
331        if !rw_range.contains(&refcount_width) {
332            return Err(io::Error::new(
333                io::ErrorKind::InvalidInput,
334                format!("Refcount width {refcount_width} not in {rw_range:?}"),
335            ));
336        }
337
338        let refcount_order = refcount_width.trailing_zeros();
339        assert!(1 << refcount_order == refcount_width);
340
341        // Clear of data
342        if image.size()? > 0 {
343            image.resize(size, storage::PreallocateMode::None).await?;
344        }
345
346        // Allocate just header and a minimal refcount structure.  The image will have a length of
347        // 0 at first, so doesn’t need an L1 table.
348        // To give the image the correct size, we just open and resize it.
349        //
350        // Cluster use:
351        // 0. Header
352        // 1. Refcount table
353        // 2. Refcount block
354        //
355        // Technically, we could also just write the header without refcount info, but the dirty
356        // bit set.  Too cheeky for my taste, though.
357
358        let (backing_fname, backing_format) = match self.backing {
359            Some((fname, fmt)) => (Some(fname), Some(fmt)),
360            None => (None, None),
361        };
362
363        let (data_file_name, data_file) = match self.data_file {
364            Some((fname, file)) => (Some(fname), Some(file)),
365            None => (None, None),
366        };
367
368        let mut header = Header::new(
369            cluster_bits,
370            refcount_order,
371            backing_fname,
372            backing_format,
373            data_file_name,
374        );
375
376        let mut rb = RefBlock::new_cleared(&image, &header)?;
377        rb.set_cluster(HostCluster(2));
378        {
379            let mut rb_locked = rb.lock_write().await;
380            rb_locked.increment(0)?; // header
381            rb_locked.increment(1)?; // reftable
382            rb_locked.increment(2)?; // refblock
383        }
384        rb.write(&image).await?;
385
386        let mut rt = RefTable::from_data(Box::new([]), &header).clone_and_grow(&header, 0)?;
387        rt.set_cluster(HostCluster(1));
388        rt.enter_refblock(0, &rb)?;
389        rt.write(&image).await?;
390
391        header.set_reftable(&rt)?;
392        header.write(&image).await?;
393
394        let img = open_builder_fn(image)?
395            .write(true)
396            .data_file(data_file)
397            .open(open_gate)
398            .await?;
399        if size > 0 {
400            img.resize_grow(size, prealloc).await?;
401        }
402
403        Ok(img)
404    }
405}