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}