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}