pub trait FormatDriverInstance:
Any
+ Debug
+ Display
+ Send
+ Sync {
type Storage: Storage;
Show 18 methods
// Required methods
fn format(&self) -> Format;
unsafe fn probe<'life0, 'async_trait>(
storage: &'life0 Self::Storage,
) -> Pin<Box<dyn Future<Output = Result<bool>> + 'async_trait>>
where Self: Sized + 'async_trait,
'life0: 'async_trait;
fn size(&self) -> u64;
fn collect_storage_dependencies(&self) -> Vec<&Self::Storage>;
fn writable(&self) -> bool;
fn get_mapping<'a, 'async_trait>(
&'a self,
offset: u64,
max_length: u64,
) -> Pin<Box<dyn Future<Output = Result<(ShallowMapping<'a, Self::Storage>, u64)>> + 'async_trait>>
where Self: 'async_trait,
'a: 'async_trait;
fn ensure_data_mapping<'a, 'async_trait>(
&'a self,
offset: u64,
length: u64,
overwrite: bool,
) -> Pin<Box<dyn Future<Output = Result<(&'a Self::Storage, u64, u64)>> + 'async_trait>>
where Self: 'async_trait,
'a: 'async_trait;
fn flush<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = Result<()>> + 'async_trait>>
where Self: 'async_trait,
'life0: 'async_trait;
fn sync<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = Result<()>> + 'async_trait>>
where Self: 'async_trait,
'life0: 'async_trait;
unsafe fn invalidate_cache<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = Result<()>> + 'async_trait>>
where Self: 'async_trait,
'life0: 'async_trait;
fn resize_grow<'life0, 'async_trait>(
&'life0 self,
new_size: u64,
prealloc_mode: PreallocateMode,
) -> Pin<Box<dyn Future<Output = Result<()>> + 'async_trait>>
where Self: 'async_trait,
'life0: 'async_trait;
fn resize_shrink<'life0, 'async_trait>(
&'life0 mut self,
new_size: u64,
) -> Pin<Box<dyn Future<Output = Result<()>> + 'async_trait>>
where Self: 'async_trait,
'life0: 'async_trait;
// Provided methods
fn zero_granularity(&self) -> Option<u64> { ... }
fn ensure_zero_mapping<'life0, 'async_trait>(
&'life0 self,
_offset: u64,
_length: u64,
) -> Pin<Box<dyn Future<Output = Result<(u64, u64)>> + 'async_trait>>
where Self: 'async_trait,
'life0: 'async_trait { ... }
fn discard_to_zero<'life0, 'async_trait>(
&'life0 mut self,
_offset: u64,
_length: u64,
) -> Pin<Box<dyn Future<Output = Result<(u64, u64)>> + 'async_trait>>
where Self: 'async_trait,
'life0: 'async_trait { ... }
fn discard_to_any<'life0, 'async_trait>(
&'life0 mut self,
_offset: u64,
_length: u64,
) -> Pin<Box<dyn Future<Output = Result<(u64, u64)>> + 'async_trait>>
where Self: 'async_trait,
'life0: 'async_trait { ... }
fn discard_to_backing<'life0, 'async_trait>(
&'life0 mut self,
_offset: u64,
_length: u64,
) -> Pin<Box<dyn Future<Output = Result<(u64, u64)>> + 'async_trait>>
where Self: 'async_trait,
'life0: 'async_trait { ... }
fn readv_special<'life0, 'life1, 'async_trait>(
&'life0 self,
_bufv: IoVectorMut<'life1>,
_offset: u64,
) -> Pin<Box<dyn Future<Output = Result<()>> + 'async_trait>>
where Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait { ... }
}
Expand description
Implementation of a disk image format.
Required Associated Types§
Required Methods§
Sourceunsafe fn probe<'life0, 'async_trait>(
storage: &'life0 Self::Storage,
) -> Pin<Box<dyn Future<Output = Result<bool>> + 'async_trait>>where
Self: Sized + 'async_trait,
'life0: 'async_trait,
unsafe fn probe<'life0, 'async_trait>(
storage: &'life0 Self::Storage,
) -> Pin<Box<dyn Future<Output = Result<bool>> + 'async_trait>>where
Self: Sized + 'async_trait,
'life0: 'async_trait,
Check whether storage
has this format.
This is only a rough test and does not guarantee that opening storage
under this format
will succeed. Generally, it will only check the magic bytes (if available). For formats
that do not have distinct features (like raw), this will always return true
.
§Safety
Probing is inherently dangerous: Image formats like qcow2 allow referencing external files;
if you use imago to give untrusted parties (like VM guests) access to VM disk image files,
this will give those parties access to data in those files. Opening images from untrusted
sources can therefore be quite dangerous. Gating
(ImplicitOpenGate
) can help mitigate this.
If you do not know an image’s format, that is a sign it does not come from a trusted source, and so opening it in a non-raw format may be quite dangerous.
Perhaps most important to note is that giving an untrusted party (like a VM guest) access to a raw image file allows that party to modify the whole file. It may write image headers into this image file, causing a subsequent probe operation to recognize it as a non-raw image, referencing arbitrary files on the host filesystem!
When using imago to give an untrusted third party access to VM disk images, the guidelines for probing are thus:
- Do not probe. If at all possible, obtain an image’s format from a trusted side channel.
- If there is no other way, probe each given image only once, before that untrusted third party (like a VM guest) had write access to it; remember the probed format, and open the image exclusively as that format.
When working with even potentially untrusted images, you should always use an
ImplicitOpenGate
to prevent access to files you do not
wish to access.
Sourcefn collect_storage_dependencies(&self) -> Vec<&Self::Storage>
fn collect_storage_dependencies(&self) -> Vec<&Self::Storage>
Recursively collect all storage objects associated with this image.
“Recursive” means to recurse to other images like e.g. a backing file.
Sourcefn writable(&self) -> bool
fn writable(&self) -> bool
Return whether this image may be modified.
This state must not change via interior mutability, i.e. as long as this FDI is wrapped in
a FormatAccess
, its writability must remain constant.
Sourcefn get_mapping<'a, 'async_trait>(
&'a self,
offset: u64,
max_length: u64,
) -> Pin<Box<dyn Future<Output = Result<(ShallowMapping<'a, Self::Storage>, u64)>> + 'async_trait>>where
Self: 'async_trait,
'a: 'async_trait,
fn get_mapping<'a, 'async_trait>(
&'a self,
offset: u64,
max_length: u64,
) -> Pin<Box<dyn Future<Output = Result<(ShallowMapping<'a, Self::Storage>, u64)>> + 'async_trait>>where
Self: 'async_trait,
'a: 'async_trait,
Return the mapping at offset
.
Find what offset
is mapped to, return that mapping information, and the length of that
continuous mapping (from offset
).
To determine that continuous mapping length, drivers should not perform additional I/O
beyond what is necessary to get mapping information for offset
itself.
max_length
is a hint how long of a range is required at all, but the returned length may
exceed that value if that simplifies the implementation.
The returned length must only be 0 if ShallowMapping::Eof
is returned.
Sourcefn ensure_data_mapping<'a, 'async_trait>(
&'a self,
offset: u64,
length: u64,
overwrite: bool,
) -> Pin<Box<dyn Future<Output = Result<(&'a Self::Storage, u64, u64)>> + 'async_trait>>where
Self: 'async_trait,
'a: 'async_trait,
fn ensure_data_mapping<'a, 'async_trait>(
&'a self,
offset: u64,
length: u64,
overwrite: bool,
) -> Pin<Box<dyn Future<Output = Result<(&'a Self::Storage, u64, u64)>> + 'async_trait>>where
Self: 'async_trait,
'a: 'async_trait,
Ensure that offset
is directly mapped to some storage object, up to a length of length
.
Return the storage object, the corresponding offset there, and the continuous length that
the driver was able to map (less than or equal to length
).
If the returned length is less than length
, drivers can expect subsequent calls to
allocate the rest of the original range. Therefore, if a driver knows in advance that it
is impossible to fully map the given range (e.g. because it lies partially or fully beyond
the end of the disk), it should return an error immediately.
If overwrite
is true, the contents in the range are supposed to be overwritten and may be
discarded. Otherwise, they must be kept.
Should not break existing data mappings, i.e. not discard or repurpose existing data mappings. Making them unused, but retaining them as allocated so they can safely be written to (albeit with no effect) is OK; discarding them so that they may be reused for other mappings is not.
Sourcefn flush<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = Result<()>> + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn flush<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = Result<()>> + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
Flush internal buffers.
Does not need to ensure those buffers are synced to disk (hardware), and does not need to drop them, i.e. they may still be used on later accesses.
Sourcefn sync<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = Result<()>> + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn sync<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = Result<()>> + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
Sync data already written to the storage hardware.
Does not need to ensure internal buffers are written, i.e. should generally just be passed
through to Storage::sync()
for all underlying storage objects.
Sourceunsafe fn invalidate_cache<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = Result<()>> + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
unsafe fn invalidate_cache<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = Result<()>> + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
Drop internal buffers.
Drop all internal buffers, but do not flush them! All internal data must then be reloaded from disk.
§Safety
Not flushing internal buffers may cause image corruption. The caller must ensure the on-disk state is consistent.
Sourcefn resize_grow<'life0, 'async_trait>(
&'life0 self,
new_size: u64,
prealloc_mode: PreallocateMode,
) -> Pin<Box<dyn Future<Output = Result<()>> + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn resize_grow<'life0, 'async_trait>(
&'life0 self,
new_size: u64,
prealloc_mode: PreallocateMode,
) -> Pin<Box<dyn Future<Output = Result<()>> + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
Resize to the given size, which must be greater than the current size.
Set the disk size to new_size
, preallocating the new space according to prealloc_mode
.
Depending on the image format, it is possible some preallocation modes are not supported,
in which case an std::io::ErrorKind::Unsupported
is returned.
If the current size is already new_size
or greater, do nothing.
Sourcefn resize_shrink<'life0, 'async_trait>(
&'life0 mut self,
new_size: u64,
) -> Pin<Box<dyn Future<Output = Result<()>> + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn resize_shrink<'life0, 'async_trait>(
&'life0 mut self,
new_size: u64,
) -> Pin<Box<dyn Future<Output = Result<()>> + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
Truncate to the given size, which must be smaller than the current size.
Set the disk size to new_size
, discarding the data after new_size
.
May break existing data mappings thanks to the mutable self
reference.
If the current size is already new_size
or smaller, do nothing.
Provided Methods§
Sourcefn zero_granularity(&self) -> Option<u64>
fn zero_granularity(&self) -> Option<u64>
Granularity on which blocks can be marked as zero.
This is the granularity for FormatDriverInstance::ensure_zero_mapping()
.
Return None
if zero blocks are not supported.
Sourcefn ensure_zero_mapping<'life0, 'async_trait>(
&'life0 self,
_offset: u64,
_length: u64,
) -> Pin<Box<dyn Future<Output = Result<(u64, u64)>> + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn ensure_zero_mapping<'life0, 'async_trait>(
&'life0 self,
_offset: u64,
_length: u64,
) -> Pin<Box<dyn Future<Output = Result<(u64, u64)>> + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
Ensure that the given range is efficiently mapped as zeroes.
Must not write any data. Return the range (offset and length) that could actually be
zeroed, which must be a subset of the range given by offset
and length
. The returned
offset must be as close to offset
as possible, i.e. no zero mapping is possible between
offset
and the returned offset (e.g. because of format-inherent granularity).
The returned length may be zero in case zeroing would theoretically be possible, but not for this range at this granularity.
Should not break existing data mappings, i.e. not discard or repurpose existing data mappings. Making them unused, but retaining them as allocated so they can safely be written to (albeit with no effect) is OK; discarding them so that they may be reused for other mappings is not.
Sourcefn discard_to_zero<'life0, 'async_trait>(
&'life0 mut self,
_offset: u64,
_length: u64,
) -> Pin<Box<dyn Future<Output = Result<(u64, u64)>> + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn discard_to_zero<'life0, 'async_trait>(
&'life0 mut self,
_offset: u64,
_length: u64,
) -> Pin<Box<dyn Future<Output = Result<(u64, u64)>> + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
Discard the given range, ensure it is read back as zeroes.
Effectively the same as FormatDriverInstance::ensure_zero_mapping()
, but may break
existing data mappings thanks to the mutable self
reference, which ensures that old data
mappings returned by FormatDriverInstance::get_mapping()
cannot be held onto.
Sourcefn discard_to_any<'life0, 'async_trait>(
&'life0 mut self,
_offset: u64,
_length: u64,
) -> Pin<Box<dyn Future<Output = Result<(u64, u64)>> + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn discard_to_any<'life0, 'async_trait>(
&'life0 mut self,
_offset: u64,
_length: u64,
) -> Pin<Box<dyn Future<Output = Result<(u64, u64)>> + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
Discard the given range.
Effectively the same as FormatDriverInstance::discard_to_zero()
, but the discarded area
may read as any data. Backing file data should not reappear, however.
Sourcefn discard_to_backing<'life0, 'async_trait>(
&'life0 mut self,
_offset: u64,
_length: u64,
) -> Pin<Box<dyn Future<Output = Result<(u64, u64)>> + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn discard_to_backing<'life0, 'async_trait>(
&'life0 mut self,
_offset: u64,
_length: u64,
) -> Pin<Box<dyn Future<Output = Result<(u64, u64)>> + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
Discard the given range, such that the backing image becomes visible.
Deallocate the range such that in deallocated blocks, the backing image’s data (if one
exists) will show, i.e. FormatDriverInstance::get_mapping()
should return an indirect
mapping. When there is no backing image, those blocks should appear as zero.
Return the range (offset and length) that could actually be discarded, which must be a
subset of offset
and length
, and the returned offset must be as close to offset
as
possible (like for FormatDriverInstance::discard_to_backing()
.
May break existing data mappings thanks to the mutable self
reference.
Sourcefn readv_special<'life0, 'life1, 'async_trait>(
&'life0 self,
_bufv: IoVectorMut<'life1>,
_offset: u64,
) -> Pin<Box<dyn Future<Output = Result<()>> + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
fn readv_special<'life0, 'life1, 'async_trait>(
&'life0 self,
_bufv: IoVectorMut<'life1>,
_offset: u64,
) -> Pin<Box<dyn Future<Output = Result<()>> + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
Read data from a ShallowMapping::Special
area.