Skip to main content

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}