Skip to main content

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