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}