imago/format/
builder.rs

1//! Builder for defining open options for images.
2
3use super::drivers::FormatDriverInstance;
4use super::gate::ImplicitOpenGate;
5use super::wrapped::WrappedFormat;
6use super::{Format, PreallocateMode};
7use crate::misc_helpers::ResultErrorContext;
8use crate::qcow2::Qcow2OpenBuilder;
9use crate::raw::RawOpenBuilder;
10use crate::vmdk::VmdkOpenBuilder;
11use crate::{Storage, StorageOpenOptions};
12use std::io;
13use std::path::{Path, PathBuf};
14
15/// Prepares opening an image.
16///
17/// There are common options for all kinds of formats, which are accessible through this trait’s
18/// methods, but there are also specialized options that depend on the format itself.  Each
19/// implementation will also provide such specialized methods, but opening an image should
20/// generally not require invoking those methods (i.e. sane defaults should apply).
21///
22/// See [`Qcow2OpenBuilder`] for an example implementation.
23pub trait FormatDriverBuilder<S: Storage + 'static>: Sized {
24    /// The format object that this builder will create.
25    type Format: FormatDriverInstance<Storage = S>;
26
27    /// Which format this is.
28    const FORMAT: Format;
29
30    /// Prepare opening the given image.
31    fn new(image: S) -> Self;
32
33    /// Prepare opening an image under the given path.
34    fn new_path<P: AsRef<Path>>(path: P) -> Self;
35
36    /// Whether the image should be writable or not.
37    fn write(self, writable: bool) -> Self;
38
39    /// Set base storage options for opened storage objects.
40    ///
41    /// When opening files (e.g. a backing file, or the path given to
42    /// [`FormatDriverBuilder::new_path()`]), use these options as the basis for opening their
43    /// respective storage objects.
44    ///
45    /// Any filename in `options` is ignored, as is writability. Both are overridden case by case
46    /// as needed.
47    fn storage_open_options(self, options: StorageOpenOptions) -> Self;
48
49    /// Open the image.
50    ///
51    /// Opens the image according to the options specified in `self`.  If files are to be opened
52    /// implicitly (e.g. backing files), the corresponding functions in `gate` will be invoked to
53    /// do so, which can decide, based on the options, to do so, or not, or modify the options
54    /// before opening the respective image/file.
55    ///
56    /// To prevent any implicitly referenced objects from being opened, use
57    /// [`DenyImplicitOpenGate`](crate::DenyImplicitOpenGate), to allow all implicitly referenced
58    /// objects to be opened as referenced, use
59    /// [`PermissiveImplicitOpenGate`](crate::PermissiveImplicitOpenGate) (but note the cautionary
60    /// note there).
61    ///
62    /// For example:
63    /// ```no_run
64    /// # let _ = async {
65    /// use imago::file::File;
66    /// use imago::qcow2::Qcow2;
67    /// use imago::{DenyImplicitOpenGate, FormatDriverBuilder};
68    ///
69    /// // Note we only override the backing file, not a potential external data file.  If the
70    /// // image has one, qcow2 would still attempt to open it, but `DenyImplicitOpenGate` would
71    /// // prevent that.
72    /// let image = Qcow2::<File>::builder_path("/path/to/image.qcow2")
73    ///     .backing(None)
74    ///     .open(DenyImplicitOpenGate::default())
75    ///     .await?;
76    /// # Ok::<(), std::io::Error>(())
77    /// # };
78    /// ```
79    #[allow(async_fn_in_trait)] // No need for Send
80    async fn open<G: ImplicitOpenGate<S>>(self, gate: G) -> io::Result<Self::Format>;
81
82    /// Synchronous wrapper around [`FormatDriverBuilder::open()`].
83    ///
84    /// This creates an async runtime, so the [`ImplicitOpenGate`] implementation is still supposed
85    /// to be async.
86    #[cfg(feature = "sync-wrappers")]
87    fn open_sync<G: ImplicitOpenGate<S>>(self, gate: G) -> io::Result<Self::Format> {
88        tokio::runtime::Builder::new_current_thread()
89            .build()?
90            .block_on(self.open(gate))
91    }
92
93    /// If possible, get the image’s path.
94    fn get_image_path(&self) -> Option<PathBuf>;
95
96    /// Return the set writable state.
97    fn get_writable(&self) -> bool;
98
99    /// Return the set storage options (if any).
100    fn get_storage_open_options(&self) -> Option<&StorageOpenOptions>;
101}
102
103/// Prepares creating (formatting) an image.
104///
105/// There are common options for all kinds of formats, which are accessible through this trait’s
106/// methods, but there are also specialized options that depend on the format itself.  Each
107/// implementation will provide such specialized methods.
108///
109/// See [`Qcow2CreateBuilder`](crate::qcow2::Qcow2CreateBuilder) for an example implementation.
110pub trait FormatCreateBuilder<S: Storage + 'static>: Sized {
111    /// Which format this is.
112    const FORMAT: Format;
113
114    /// Open builder type for this format.
115    type DriverBuilder: FormatDriverBuilder<S>;
116
117    /// Prepare formatting the given image file.
118    fn new(image: S) -> Self;
119
120    /// Set the virtual disk size.
121    fn size(self, size: u64) -> Self;
122
123    /// Set the desired preallocation mode.
124    fn preallocate(self, prealloc_mode: PreallocateMode) -> Self;
125
126    /// Format the image file.
127    ///
128    /// Formats the underlying image file according to the options specified in `self`.
129    ///
130    /// This will delete any currently present data in the image!
131    #[allow(async_fn_in_trait)] // No need for Send
132    async fn create(self) -> io::Result<()>;
133
134    /// Format the image file and open it.
135    ///
136    /// Same as [`FormatCreateBuilder::create()`], but also opens the image file.
137    ///
138    /// Note that the image file will always be opened as writable, regardless of whether this was
139    /// set in `open_builder` or not.  This is because formatting requires the image to be
140    /// writable.
141    #[allow(async_fn_in_trait)] // No need for Send
142    async fn create_open<G: ImplicitOpenGate<S>, F: FnOnce(S) -> io::Result<Self::DriverBuilder>>(
143        self,
144        open_gate: G,
145        open_builder_fn: F,
146    ) -> io::Result<<Self::DriverBuilder as FormatDriverBuilder<S>>::Format>;
147
148    /// Get the set virtual disk size.
149    fn get_size(&self) -> u64;
150
151    /// Get the preallocation mode.
152    fn get_preallocate(&self) -> PreallocateMode;
153}
154
155/// Image open builder with the most basic options.
156pub struct FormatDriverBuilderBase<S: Storage> {
157    /// Metadata (image) file
158    image: StorageOrPath<S>,
159
160    /// Whether the image is writable or not
161    writable: bool,
162
163    /// Options to be used for implicitly opened storage
164    storage_opts: Option<StorageOpenOptions>,
165}
166
167/// Image creation builder with the most basic options.
168pub struct FormatCreateBuilderBase<S: Storage> {
169    /// Metadata (image) file
170    image: S,
171
172    /// Virtual disk size
173    size: u64,
174
175    /// Preallocation mode
176    prealloc_mode: PreallocateMode,
177}
178
179impl<S: Storage + 'static> FormatDriverBuilderBase<S> {
180    /// Create a new instance of this type.
181    fn do_new(image: StorageOrPath<S>) -> Self {
182        FormatDriverBuilderBase {
183            image,
184            writable: false,
185            storage_opts: None,
186        }
187    }
188
189    /// Helper for [`FormatDriverBuilder::new()`].
190    pub fn new(image: S) -> Self {
191        Self::do_new(StorageOrPath::Storage(image))
192    }
193
194    /// Helper for [`FormatDriverBuilder::new_path()`].
195    pub fn new_path<P: AsRef<Path>>(path: P) -> Self {
196        Self::do_new(StorageOrPath::Path(path.as_ref().to_path_buf()))
197    }
198
199    /// Helper for [`FormatDriverBuilder::write()`].
200    pub fn set_write(&mut self, writable: bool) {
201        self.writable = writable;
202    }
203
204    /// Helper for [`FormatDriverBuilder::storage_open_options()`].
205    pub fn set_storage_open_options(&mut self, options: StorageOpenOptions) {
206        self.storage_opts = Some(options);
207    }
208
209    /// If possible, get the image’s path.
210    pub fn get_image_path(&self) -> Option<PathBuf> {
211        match &self.image {
212            StorageOrPath::Storage(s) => s.get_filename(),
213            StorageOrPath::Path(p) => Some(p.clone()),
214        }
215    }
216
217    /// Return the set writable state.
218    pub fn get_writable(&self) -> bool {
219        self.writable
220    }
221
222    /// Return the set storage options (if any).
223    pub fn get_storage_opts(&self) -> Option<&StorageOpenOptions> {
224        self.storage_opts.as_ref()
225    }
226
227    /// Create storage options.
228    ///
229    /// If any were set, return those, overriding their writable state based on the set writable
230    /// state ([`FormatDriverBuilderBase::set_write()`]).  Otherwise, create an empty set (again
231    /// with the writable state set as appropriate).
232    pub fn make_storage_opts(&self) -> StorageOpenOptions {
233        self.storage_opts
234            .as_ref()
235            .cloned()
236            .unwrap_or(StorageOpenOptions::new())
237            .write(self.writable)
238    }
239
240    /// Open the image’s storage object.
241    pub async fn open_image<G: ImplicitOpenGate<S>>(self, gate: &mut G) -> io::Result<S> {
242        let opts = self.make_storage_opts();
243        self.image.open_storage(opts, gate).await
244    }
245}
246
247impl<S: Storage> FormatCreateBuilderBase<S> {
248    /// Helper for [`FormatCreateBuilder::new()`].
249    pub fn new(image: S) -> Self {
250        FormatCreateBuilderBase {
251            image,
252            size: 0,
253            prealloc_mode: PreallocateMode::None,
254        }
255    }
256
257    /// Helper for [`FormatCreateBuilder::size()`].
258    pub fn set_size(&mut self, size: u64) {
259        self.size = size;
260    }
261
262    /// Helper for [`FormatCreateBuilder::preallocate()`].
263    pub fn set_preallocate(&mut self, prealloc_mode: PreallocateMode) {
264        self.prealloc_mode = prealloc_mode;
265    }
266
267    /// Get the set virtual disk size.
268    pub fn get_size(&self) -> u64 {
269        self.size
270    }
271
272    /// Get the preallocation mode.
273    pub fn get_preallocate(&self) -> PreallocateMode {
274        self.prealloc_mode
275    }
276
277    /// Get the image file to be formatted.
278    pub fn get_image(self) -> S {
279        self.image
280    }
281
282    /// Get the image file to be formatted, by reference.
283    pub fn get_image_ref(&self) -> &S {
284        &self.image
285    }
286}
287
288/// Alternatively a storage object or a path to it.
289///
290/// Only for internal use.  Externally, two separate functions should be provided.
291pub(crate) enum StorageOrPath<S: Storage> {
292    /// Storage object
293    Storage(S),
294
295    /// Path
296    Path(PathBuf),
297}
298
299impl<S: Storage + 'static> StorageOrPath<S> {
300    /// Open the storage object.
301    pub async fn open_storage<G: ImplicitOpenGate<S>>(
302        self,
303        opts: StorageOpenOptions,
304        gate: &mut G,
305    ) -> io::Result<S> {
306        match self {
307            StorageOrPath::Storage(s) => Ok(s),
308            StorageOrPath::Path(p) => gate
309                .open_storage(opts.filename(&p))
310                .await
311                .err_context(|| p.to_string_lossy()),
312        }
313    }
314}
315
316/// Alternatively an image or parameters for a builder for it.
317///
318/// Only for internal use.  Externally, two separate functions should be provided.
319pub(crate) enum FormatOrBuilder<S: Storage + 'static, F: WrappedFormat<S>> {
320    /// Image object
321    Format(F),
322
323    /// Qcow2 image builder
324    Qcow2Builder(Box<Qcow2OpenBuilder<S>>),
325
326    /// Raw image builder
327    RawBuilder(Box<RawOpenBuilder<S>>),
328
329    /// Vmdk image builder
330    VmdkBuilder(Box<VmdkOpenBuilder<S>>),
331}
332
333impl<S: Storage + 'static, F: WrappedFormat<S> + 'static> FormatOrBuilder<S, F> {
334    /// Create a new builder variant.
335    ///
336    /// Create a builder variant for the given format, opening the given image.
337    pub fn new_builder<P: AsRef<Path>>(format: Format, path: P) -> Self {
338        match format {
339            Format::Qcow2 => Self::Qcow2Builder(Box::new(Qcow2OpenBuilder::new_path(path))),
340            Format::Raw => Self::RawBuilder(Box::new(RawOpenBuilder::new_path(path))),
341            Format::Vmdk => Self::VmdkBuilder(Box::new(VmdkOpenBuilder::new_path(path))),
342        }
343    }
344
345    /// Open the image.
346    pub async fn open_format<G: ImplicitOpenGate<S>>(
347        self,
348        opts: StorageOpenOptions,
349        gate: &mut G,
350    ) -> io::Result<F> {
351        let f = match self {
352            FormatOrBuilder::Format(f) => return Ok(f),
353            FormatOrBuilder::Qcow2Builder(b) => {
354                let b = b.storage_open_options(opts);
355                gate.open_format(b).await?
356            }
357            FormatOrBuilder::RawBuilder(b) => {
358                let b = b.storage_open_options(opts);
359                gate.open_format(b).await?
360            }
361            FormatOrBuilder::VmdkBuilder(b) => {
362                let b = b.storage_open_options(opts);
363                gate.open_format(b).await?
364            }
365        };
366        Ok(F::wrap(f))
367    }
368}