imago/format/drivers.rs
1//! Internal image format driver interface.
2//!
3//! Provides the internal interface for image format drivers to provide their services, on which
4//! the publically visible interface [`FormatAccess`] is built.
5
6use super::{Format, PreallocateMode};
7use crate::io_buffers::IoVectorMut;
8use crate::{FormatAccess, Storage};
9use maybe_async::maybe_async;
10use std::any::Any;
11use std::fmt::{self, Debug, Display};
12use std::{cmp, io};
13
14/// Implementation of a disk image format.
15#[maybe_async(?Send)]
16#[allow(clippy::double_must_use)] // strange interaction between `maybe_async(?Send)` and `async`
17pub trait FormatDriverInstance: Any + Debug + Display + Send + Sync {
18 /// Type of storage used.
19 type Storage: Storage;
20
21 /// Return which format this is.
22 fn format(&self) -> Format;
23
24 /// Check whether `storage` has this format.
25 ///
26 /// This is only a rough test and does not guarantee that opening `storage` under this format
27 /// will succeed. Generally, it will only check the magic bytes (if available). For formats
28 /// that do not have distinct features (like raw), this will always return `true`.
29 ///
30 /// # Safety
31 /// Probing is inherently dangerous: Image formats like qcow2 allow referencing external files;
32 /// if you use imago to give untrusted parties (like VM guests) access to VM disk image files,
33 /// this will give those parties access to data in those files. Opening images from untrusted
34 /// sources can therefore be quite dangerous. Gating
35 /// ([`ImplicitOpenGate`](super::gate::ImplicitOpenGate)) can help mitigate this.
36 ///
37 /// If you do not know an image’s format, that is a sign it does not come from a trusted
38 /// source, and so opening it in a non-raw format may be quite dangerous.
39 ///
40 /// Perhaps most important to note is that giving an untrusted party (like a VM guest) access
41 /// to a raw image file allows that party to modify the whole file. It may write image headers
42 /// into this image file, causing a subsequent probe operation to recognize it as a non-raw
43 /// image, referencing arbitrary files on the host filesystem!
44 ///
45 /// When using imago to give an untrusted third party access to VM disk images, the guidelines
46 /// for probing are thus:
47 /// - Do not probe. If at all possible, obtain an image’s format from a trusted side channel.
48 /// - If there is no other way, probe each given image only once, before that untrusted third
49 /// party (like a VM guest) had write access to it; remember the probed format, and open the
50 /// image exclusively as that format.
51 ///
52 /// When working with even potentially untrusted images, you should always use an
53 /// [`ImplicitOpenGate`](super::gate::ImplicitOpenGate) to prevent access to files you do not
54 /// wish to access.
55 async unsafe fn probe(storage: &Self::Storage) -> io::Result<bool>
56 where
57 Self: Sized;
58
59 /// Size of the disk represented by this image.
60 fn size(&self) -> u64;
61
62 /// Granularity on which blocks can be marked as zero.
63 ///
64 /// This is the granularity for [`FormatDriverInstance::ensure_zero_mapping()`].
65 ///
66 /// Return `None` if zero blocks are not supported.
67 fn zero_granularity(&self) -> Option<u64> {
68 None
69 }
70
71 /// Recursively collect all storage objects associated with this image.
72 ///
73 /// “Recursive” means to recurse to other images like e.g. a backing file.
74 fn collect_storage_dependencies(&self) -> Vec<&Self::Storage>;
75
76 /// Return whether this image may be modified.
77 ///
78 /// This state must not change via interior mutability, i.e. as long as this FDI is wrapped in
79 /// a `FormatAccess`, its writability must remain constant.
80 fn writable(&self) -> bool;
81
82 /// Return the mapping at `offset`.
83 ///
84 /// Find what `offset` is mapped to, return that mapping information, and the length of that
85 /// continuous mapping (from `offset`).
86 ///
87 /// To determine that continuous mapping length, drivers should not perform additional I/O
88 /// beyond what is necessary to get mapping information for `offset` itself.
89 ///
90 /// `max_length` is a hint how long of a range is required at all, but the returned length may
91 /// exceed that value if that simplifies the implementation.
92 ///
93 /// The returned length must only be 0 if `ShallowMapping::Eof` is returned.
94 #[allow(clippy::needless_lifetimes)] // Elidable in sync, but async needs a named lifetime for the boxed future bound
95 async fn get_mapping<'a>(
96 &'a self,
97 offset: u64,
98 max_length: u64,
99 ) -> io::Result<(ShallowMapping<'a, Self::Storage>, u64)>;
100
101 /// Ensure that `offset` is directly mapped to some storage object, up to a length of `length`.
102 ///
103 /// Return the storage object, the corresponding offset there, and the continuous length that
104 /// the driver was able to map (less than or equal to `length`).
105 ///
106 /// If the returned length is less than `length`, drivers can expect subsequent calls to
107 /// allocate the rest of the original range. Therefore, if a driver knows in advance that it
108 /// is impossible to fully map the given range (e.g. because it lies partially or fully beyond
109 /// the end of the disk), it should return an error immediately.
110 ///
111 /// If `overwrite` is true, the contents in the range are supposed to be overwritten and may be
112 /// discarded. Otherwise, they must be kept.
113 ///
114 /// Should not break existing data mappings, i.e. not discard or repurpose existing data
115 /// mappings. Making them unused, but retaining them as allocated so they can safely be
116 /// written to (albeit with no effect) is OK; discarding them so that they may be reused for
117 /// other mappings is not.
118 ///
119 /// NOTE: Will be deprecated in imago 0.3 in favor of
120 /// [`FormatDriverInstance::prepare_data_mapping()`] and
121 /// [`FormatDriverInstance::commit_data_mapping()`].
122 #[allow(clippy::needless_lifetimes)] // Elidable in sync, but async needs a named lifetime for the boxed future bound
123 async fn ensure_data_mapping<'a>(
124 &'a self,
125 offset: u64,
126 length: u64,
127 overwrite: bool,
128 ) -> io::Result<(&'a Self::Storage, u64, u64)> {
129 let mapping = self.prepare_data_mapping(offset, length, overwrite).await?;
130 let storage = mapping.storage();
131 let host_offset = mapping.offset();
132 let length = mapping.length();
133
134 self.commit_data_mapping(mapping).await?;
135
136 Ok((storage, host_offset, length))
137 }
138
139 /// Prepare a raw data mapping at `offset`.
140 ///
141 /// Ensure that `offset` is directly mapped to some storage object, up to a length of `length`.
142 ///
143 /// This mapping needs not be immediately visible (unless it exists already); the returned
144 /// [`PreparedDataMapping`] will tell the user where to write the data for the range, and once
145 /// that data has been written, [`FormatDriverInstance::commit_data_mapping()`] is called to
146 /// finalize it and make the mapping actually visible.
147 ///
148 /// When the returned object is dropped without having been committed, the range must keep its
149 /// previous mapping, and whatever was allocated for the prepared mapping must be released.
150 ///
151 /// Drivers may delay any other I/O on the image until the returned object is committed or
152 /// dropped.
153 ///
154 /// If `overwrite` is true, the contents in the range are supposed to be overwritten and may be
155 /// discarded. Otherwise, they should be kept.
156 ///
157 /// The default implementation is based on [`FormatDriverInstance::ensure_data_mapping()`],
158 /// i.e. makes the mapping visible immediately and leaves nothing to commit.
159 ///
160 /// NOTE: Starting from the imago 0.3 release, this function will no longer have a default
161 /// implementation. Then, *this* function will need to be implemented, *not*
162 /// [`FormatDriverInstance::ensure_data_mapping()`].
163 #[allow(clippy::needless_lifetimes)] // Elidable in sync, but async needs a named lifetime for the boxed future bound
164 async fn prepare_data_mapping<'a>(
165 &'a self,
166 offset: u64,
167 length: u64,
168 overwrite: bool,
169 ) -> io::Result<PreparedDataMapping<'a, Self::Storage>> {
170 let (storage, offset, length) = self.ensure_data_mapping(offset, length, overwrite).await?;
171 Ok(PreparedDataMapping::existing(storage, offset, length))
172 }
173
174 /// Make a mapping prepared by [`FormatDriverInstance::prepare_data_mapping()`] visible.
175 ///
176 /// Must only be called once the data has been written to the prepared location. `mapping`
177 /// must have been returned by `prepare_data_mapping()` on this same object.
178 ///
179 /// The default implementation matches the default implementation of
180 /// [`FormatDriverInstance::prepare_data_mapping()`], which leaves nothing to commit.
181 ///
182 /// NOTE: Starting from the imago 0.3 release, this function will no longer have a default
183 /// implementation, and will have to be implemented together with
184 /// [`FormatDriverInstance::prepare_data_mapping()`].
185 async fn commit_data_mapping(
186 &self,
187 mapping: PreparedDataMapping<'_, Self::Storage>,
188 ) -> io::Result<()> {
189 if mapping.into_state().is_some() {
190 // The default implementation of `prepare_data_mapping()` left nothing to commit, so if
191 // there is something now, it came from a different implementation.
192 return Err(io::Error::new(
193 io::ErrorKind::InvalidInput,
194 "Prepared data mapping does not belong to this image",
195 ));
196 }
197 Ok(())
198 }
199
200 /// Ensure that the given range is efficiently mapped as zeroes.
201 ///
202 /// Must not write any data. Return the range (offset and length) that could actually be
203 /// zeroed, which must be a subset of the range given by `offset` and `length`. The returned
204 /// offset must be as close to `offset` as possible, i.e. no zero mapping is possible between
205 /// `offset` and the returned offset (e.g. because of format-inherent granularity).
206 ///
207 /// The returned length may be zero in case zeroing would theoretically be possible, but not
208 /// for this range at this granularity.
209 ///
210 /// Should not break existing data mappings, i.e. not discard or repurpose existing data
211 /// mappings. Making them unused, but retaining them as allocated so they can safely be
212 /// written to (albeit with no effect) is OK; discarding them so that they may be reused for
213 /// other mappings is not.
214 async fn ensure_zero_mapping(&self, _offset: u64, _length: u64) -> io::Result<(u64, u64)> {
215 Err(io::ErrorKind::Unsupported.into())
216 }
217
218 /// Discard the given range, ensure it is read back as zeroes.
219 ///
220 /// Effectively the same as [`FormatDriverInstance::ensure_zero_mapping()`], but may break
221 /// existing data mappings thanks to the mutable `self` reference, which ensures that old data
222 /// mappings returned by [`FormatDriverInstance::get_mapping()`] cannot be held onto.
223 async fn discard_to_zero(&mut self, offset: u64, length: u64) -> io::Result<(u64, u64)> {
224 // Safe: `&mut self` guarantees nobody has concurrent data mappings
225 unsafe { self.discard_to_zero_unsafe(offset, length).await }
226 }
227
228 /// Discard the given range, ensure it is read back as zeroes.
229 ///
230 /// Unsafe variant of [FormatDriverInstance::discard_to_zero()], only requiring an immutable
231 /// &self
232 ///
233 /// # Safety
234 /// This function is marked as unsafe because:
235 /// - It may invalidate all existing data mappings.
236 ///
237 /// The caller must ensure that no other references to this driver instance exist and that
238 /// the caller must ensure that all previously looked up mappings are no longer assumed to
239 /// be valid after this operation.
240 ///
241 /// Because mappings contain references to the block driver instance, one way to do so is
242 /// to have a mutable reference to the block driver instance, which will automatically
243 /// ensure there are no other references (and thus no mappings). In that case, you can use
244 /// the safe variant [`Self::discard_to_zero()`].
245 async unsafe fn discard_to_zero_unsafe(
246 &self,
247 _offset: u64,
248 _length: u64,
249 ) -> io::Result<(u64, u64)> {
250 Err(io::ErrorKind::Unsupported.into())
251 }
252
253 /// Discard the given range.
254 ///
255 /// Effectively the same as [`FormatDriverInstance::discard_to_zero()`], but the discarded area
256 /// may read as any data. Backing file data should not reappear, however.
257 async fn discard_to_any(&mut self, offset: u64, length: u64) -> io::Result<(u64, u64)> {
258 // Safe: `&mut self` guarantees nobody has concurrent data mappings
259 unsafe { self.discard_to_any_unsafe(offset, length).await }
260 }
261
262 /// Discard the given range.
263 ///
264 /// Unsafe variant of [FormatDriverInstance::discard_to_any()], only requiring an immutable
265 /// &self
266 ///
267 /// # Safety
268 /// This function is marked as unsafe because:
269 /// - It may invalidate all existing data mappings.
270 ///
271 /// The caller must ensure that no other references to this driver instance exist and that
272 /// the caller must ensure that all previously looked up mappings are no longer assumed to
273 /// be valid after this operation.
274 ///
275 /// Because mappings contain references to the block driver instance, one way to do so is
276 /// to have a mutable reference to the block driver instance, which will automatically
277 /// ensure there are no other references (and thus no mappings). In that case, you can use
278 /// the safe variant [`Self::discard_to_any()`].
279 async unsafe fn discard_to_any_unsafe(
280 &self,
281 _offset: u64,
282 _length: u64,
283 ) -> io::Result<(u64, u64)> {
284 Err(io::ErrorKind::Unsupported.into())
285 }
286
287 /// Discard the given range, such that the backing image becomes visible.
288 ///
289 /// Deallocate the range such that in deallocated blocks, the backing image’s data (if one
290 /// exists) will show, i.e. [`FormatDriverInstance::get_mapping()`] should return an indirect
291 /// mapping. When there is no backing image, those blocks should appear as zero.
292 ///
293 /// Return the range (offset and length) that could actually be discarded, which must be a
294 /// subset of `offset` and `length`, and the returned offset must be as close to `offset` as
295 /// possible (like for [`FormatDriverInstance::discard_to_backing()`].
296 ///
297 /// May break existing data mappings thanks to the mutable `self` reference.
298 async fn discard_to_backing(&mut self, offset: u64, length: u64) -> io::Result<(u64, u64)> {
299 // Safe: `&mut self` guarantees nobody has concurrent data mappings
300 unsafe { self.discard_to_backing_unsafe(offset, length).await }
301 }
302
303 /// Discard the given range, such that the backing image becomes visible.
304 ///
305 /// Unsafe variant of [FormatDriverInstance::discard_to_backing()], only requiring an immutable
306 /// &self
307 ///
308 /// # Safety
309 /// This function is marked as unsafe because:
310 /// - It may invalidate all existing data mappings.
311 ///
312 /// The caller must ensure that no other references to this driver instance exist and that
313 /// the caller must ensure that all previously looked up mappings are no longer assumed to
314 /// be valid after this operation.
315 ///
316 /// Because mappings contain references to the block driver instance, one way to do so is
317 /// to have a mutable reference to the block driver instance, which will automatically
318 /// ensure there are no other references (and thus no mappings). In that case, you can use
319 /// the safe variant [`Self::discard_to_backing()`].
320 async unsafe fn discard_to_backing_unsafe(
321 &self,
322 _offset: u64,
323 _length: u64,
324 ) -> io::Result<(u64, u64)> {
325 Err(io::ErrorKind::Unsupported.into())
326 }
327
328 /// Read data from a `ShallowMapping::Special` area.
329 async fn readv_special(&self, _bufv: IoVectorMut<'_>, _offset: u64) -> io::Result<()> {
330 Err(io::ErrorKind::Unsupported.into())
331 }
332
333 /// Read data from a `ShallowMapping::Special` area, returning the unhandled tail.
334 ///
335 /// Like [`Self::readv_special()`], but if the area turns out not to be special (anymore) from
336 /// some point on (e.g. because a concurrent write has changed its mapping), stop there and
337 /// return that unhandled tail of `bufv` and its offset. That tail has not been written to, so
338 /// the caller can read it according to its current (non-special) mapping.
339 ///
340 /// Return `None` if the whole area has been read.
341 ///
342 /// The default implementation calls [`Self::readv_special()`], so never returns a tail.
343 ///
344 /// NOTE: Starting from the imago 0.3 release, this function will no longer have a default
345 /// implementation building on [`Self::readv_special()`], and instead will return an error.
346 /// Then, *this* function will need to be implemented, *not* [`Self::readv_special()`].
347 async fn try_readv_special<'a>(
348 &self,
349 bufv: IoVectorMut<'a>,
350 offset: u64,
351 ) -> io::Result<Option<(IoVectorMut<'a>, u64)>> {
352 self.readv_special(bufv, offset).await?;
353 Ok(None)
354 }
355
356 /// Flush internal buffers.
357 ///
358 /// Does not need to ensure those buffers are synced to disk (hardware), and does not need to
359 /// drop them, i.e. they may still be used on later accesses.
360 async fn flush(&self) -> io::Result<()>;
361
362 /// Sync data already written to the storage hardware.
363 ///
364 /// Does not need to ensure internal buffers are written, i.e. should generally just be passed
365 /// through to `Storage::sync()` for all underlying storage objects.
366 async fn sync(&self) -> io::Result<()>;
367
368 /// Drop internal buffers.
369 ///
370 /// Drop all internal buffers, but do not flush them! All internal data must then be reloaded
371 /// from disk.
372 ///
373 /// # Safety
374 /// Not flushing internal buffers may cause image corruption. The caller must ensure the
375 /// on-disk state is consistent.
376 async unsafe fn invalidate_cache(&self) -> io::Result<()>;
377
378 /// Resize to the given size, which must be greater than the current size.
379 ///
380 /// Set the disk size to `new_size`, preallocating the new space according to `prealloc_mode`.
381 /// Depending on the image format, it is possible some preallocation modes are not supported,
382 /// in which case an [`std::io::ErrorKind::Unsupported`] is returned.
383 ///
384 /// If the current size is already `new_size` or greater, do nothing.
385 async fn resize_grow(&self, new_size: u64, prealloc_mode: PreallocateMode) -> io::Result<()>;
386
387 /// Truncate to the given size, which must be smaller than the current size.
388 ///
389 /// Set the disk size to `new_size`, discarding the data after `new_size`.
390 ///
391 /// May break existing data mappings thanks to the mutable `self` reference.
392 ///
393 /// If the current size is already `new_size` or smaller, do nothing.
394 async fn resize_shrink(&mut self, new_size: u64) -> io::Result<()>;
395}
396
397/// Data mapping that has been prepared, but not committed yet.
398///
399/// Returned by [`FormatAccess::prepare_data_mapping()`]: Says where to write the data for the
400/// range that is to be mapped. Once that data has been written, pass this object to
401/// [`FormatAccess::commit_data_mapping()`] to make the mapping visible.
402///
403/// Dropping this object without committing it discards any prepared mapping change.
404///
405/// Until this object is committed or dropped, any other I/O on the image may wait for it.
406#[must_use = "prepared mappings must be committed to complete the write operation"]
407pub struct PreparedDataMapping<'a, S: Storage + 'static> {
408 /// Storage object to write the data to.
409 storage: &'a S,
410
411 /// Offset in `storage` to write the data to.
412 offset: u64,
413
414 /// Length of the mapping.
415 length: u64,
416
417 /// Driver-specific state required to commit this mapping; `None` if nothing needs to be
418 /// committed.
419 state: Option<Box<dyn Any>>,
420}
421
422impl<'a, S: Storage + 'static> PreparedDataMapping<'a, S> {
423 /// Create a prepared mapping that does not require anything to be committed.
424 ///
425 /// For use by format drivers, e.g. when the mapping exists already.
426 pub fn existing(storage: &'a S, offset: u64, length: u64) -> Self {
427 PreparedDataMapping {
428 storage,
429 offset,
430 length,
431 state: None,
432 }
433 }
434
435 /// Create a prepared mapping that must be committed, using the driver-specific `state`.
436 ///
437 /// For use by format drivers. Dropping `state` must discard the prepared mapping.
438 pub fn to_commit(storage: &'a S, offset: u64, length: u64, state: Box<dyn Any>) -> Self {
439 PreparedDataMapping {
440 storage,
441 offset,
442 length,
443 state: Some(state),
444 }
445 }
446
447 /// Storage object to write the data to.
448 pub fn storage(&self) -> &'a S {
449 self.storage
450 }
451
452 /// Offset in [`Self::storage()`] to write the data to.
453 pub fn offset(&self) -> u64 {
454 self.offset
455 }
456
457 /// Length of the mapping.
458 pub fn length(&self) -> u64 {
459 self.length
460 }
461
462 /// Whether holding this object can delay other I/O on the image.
463 ///
464 /// `true` means the range only becomes visible once it is committed, and other I/O on the
465 /// image may be blocked in order not to interfere with that.
466 ///
467 /// `false` means holding it will never delay other I/O. In this case, the mapping is visible
468 /// already: the range keeps reading as it does now, and committing it will most likely do
469 /// nothing ([`FormatAccess::commit_data_mapping()`] must still be called by users, though).
470 pub fn is_nonblocking(&self) -> bool {
471 self.state.is_none()
472 }
473
474 /// Take the driver-specific state, if any.
475 ///
476 /// For use by format drivers.
477 pub fn into_state(self) -> Option<Box<dyn Any>> {
478 self.state
479 }
480
481 /// Limit the length of the mapping to `max_length`.
482 pub(crate) fn limit_length(&mut self, max_length: u64) {
483 self.length = cmp::min(self.length, max_length);
484 }
485}
486
487impl<S: Storage + 'static> Debug for PreparedDataMapping<'_, S> {
488 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
489 f.debug_struct("PreparedDataMapping")
490 .field("storage", &self.storage)
491 .field("offset", &self.offset)
492 .field("length", &self.length)
493 .field("has_state", &self.state.is_some())
494 .finish()
495 }
496}
497
498/// Non-recursive mapping information.
499///
500/// Mapping information as returned by [`FormatDriverInstance::get_mapping()`], only looking at
501/// that format layer’s information.
502#[derive(Debug)]
503#[non_exhaustive]
504pub enum ShallowMapping<'a, S: Storage + 'static> {
505 /// Raw data.
506 #[non_exhaustive]
507 Raw {
508 /// Storage object where this data is stored.
509 storage: &'a S,
510
511 /// Offset in `storage` where this data is stored.
512 offset: u64,
513
514 /// Whether this mapping may be written to.
515 ///
516 /// If `true`, you can directly write to `offset` on `storage` to change the disk image’s
517 /// data accordingly.
518 ///
519 /// If `false`, the disk image format does not allow writing to `offset` on `storage`; a
520 /// new mapping must be allocated first.
521 writable: bool,
522 },
523
524 /// Data lives in a different disk image (e.g. a backing file).
525 #[non_exhaustive]
526 Indirect {
527 /// Format instance where this data can be obtained.
528 layer: &'a FormatAccess<S>,
529
530 /// Offset in `layer` where this data can be obtained.
531 offset: u64,
532
533 /// Whether this mapping may be written to.
534 ///
535 /// If `true`, you can directly write to `offset` on `layer` to change the disk image’s
536 /// data accordingly.
537 ///
538 /// If `false`, the disk image format does not allow writing to `offset` on `layer`; a new
539 /// mapping must be allocated first.
540 writable: bool,
541 },
542
543 /// Range is to be read as zeroes.
544 #[non_exhaustive]
545 Zero {
546 /// Whether these zeroes are explicit on this layer.
547 ///
548 /// Differential image formats (like qcow2) track information about the status for all
549 /// blocks in the image (called clusters in case of qcow2). Perhaps most importantly, they
550 /// track whether a block is allocated or not:
551 /// - Allocated blocks have their data in the image.
552 /// - Unallocated blocks do not have their data in this image, but have to be read from a
553 /// backing image (which results in [`ShallowMapping::Indirect`] mappings).
554 ///
555 /// Thus, such images represent the difference from their backing image (hence
556 /// “differential”).
557 ///
558 /// Without a backing image, this feature can be used for sparse allocation: Unallocated
559 /// blocks are simply interpreted to be zero. These ranges will be noted as
560 /// [`ShallowMapping::Zero`] with `explicit` set to false.
561 ///
562 /// Formats like qcow2 can track more information beyond just the allocation status,
563 /// though, for example, whether a block should read as zero. Such blocks similarly do not
564 /// need to have their data stored in the image file, but are still not treated as
565 /// unallocated, so will never be read from a backing image, regardless of whether one
566 /// exists or not.
567 ///
568 /// These ranges are noted as [`ShallowMapping::Zero`] with `explicit` set to true.
569 explicit: bool,
570 },
571
572 /// End of file reached.
573 #[non_exhaustive]
574 Eof {},
575
576 /// Data is encoded in some manner, e.g. compressed or encrypted.
577 ///
578 /// Such data cannot be accessed directly, but must be interpreted by the image format driver.
579 #[non_exhaustive]
580 Special {
581 /// Original (“guest”) offset to pass to `FormatDriverInstance::readv_special()`.
582 offset: u64,
583 },
584}