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