imago/format/access.rs
1//! Actual public image access functionality.
2//!
3//! Provides access to different image formats via `FormatAccess` objects.
4
5use super::drivers::{FormatDriverInstance, ShallowMapping};
6use super::PreallocateMode;
7use crate::io_buffers::{IoVector, IoVectorMut};
8use crate::storage::ext::write_full_zeroes;
9use crate::vector_select::FutureVector;
10use crate::{Storage, StorageExt};
11use std::fmt::{self, Display, Formatter};
12use std::{cmp, io, ptr};
13
14/// Provides access to a disk image.
15#[derive(Debug)]
16pub struct FormatAccess<S: Storage + 'static> {
17 /// Image format driver.
18 inner: Box<dyn FormatDriverInstance<Storage = S>>,
19
20 /// Whether this image may be modified.
21 writable: bool,
22
23 /// How many asynchronous requests to perform per read request in parallel.
24 read_parallelization: usize,
25
26 /// How many asynchronous requests to perform per write request in parallel.
27 write_parallelization: usize,
28}
29
30/// Fully recursive mapping information.
31///
32/// Mapping information that resolves down to the storage object layer (except for special data).
33#[derive(Debug)]
34#[non_exhaustive]
35pub enum Mapping<'a, S: Storage + 'static> {
36 /// Raw data.
37 #[non_exhaustive]
38 Raw {
39 /// Storage object where this data is stored.
40 storage: &'a S,
41
42 /// Offset in `storage` where this data is stored.
43 offset: u64,
44
45 /// Whether this mapping may be written to.
46 ///
47 /// If `true`, you can directly write to `offset` on `storage` to change the disk image’s
48 /// data accordingly.
49 ///
50 /// If `false`, the disk image format does not allow writing to `offset` on `storage`; a
51 /// new mapping must be allocated first.
52 writable: bool,
53 },
54
55 /// Range is to be read as zeroes.
56 #[non_exhaustive]
57 Zero {
58 /// Whether these zeroes are explicit on this image (the top layer).
59 ///
60 /// Differential image formats (like qcow2) track information about the status for all
61 /// blocks in the image (called clusters in case of qcow2). Perhaps most importantly, they
62 /// track whether a block is allocated or not:
63 /// - Allocated blocks have their data in the image.
64 /// - Unallocated blocks do not have their data in this image, but have to be read from a
65 /// backing image (which results in [`ShallowMapping::Indirect`] mappings).
66 ///
67 /// Thus, such images represent the difference from their backing image (hence
68 /// “differential”).
69 ///
70 /// Without a backing image, this feature can be used for sparse allocation: Unallocated
71 /// blocks are simply interpreted to be zero. These ranges will be noted as
72 /// [`Mapping::Zero`] with `explicit` set to false.
73 ///
74 /// Formats like qcow2 can track more information beyond just the allocation status,
75 /// though, for example, whether a block should read as zero. Such blocks similarly do not
76 /// need to have their data stored in the image file, but are still not treated as
77 /// unallocated, so will never be read from a backing image, regardless of whether one
78 /// exists or not.
79 ///
80 /// These ranges are noted as [`Mapping::Zero`] with `explicit` set to true.
81 explicit: bool,
82 },
83
84 /// End of file reached.
85 ///
86 /// The accompanying length is always 0.
87 #[non_exhaustive]
88 Eof {},
89
90 /// Data is encoded in some manner, e.g. compressed or encrypted.
91 ///
92 /// Such data cannot be accessed directly, but must be interpreted by the image format driver.
93 #[non_exhaustive]
94 Special {
95 /// Format layer where this special data was encountered.
96 layer: &'a FormatAccess<S>,
97
98 /// Original (“guest”) offset on `layer` to pass to `readv_special()`.
99 offset: u64,
100 },
101}
102
103/// Resolved read plan for a disk image range.
104///
105/// A read plan describes how a requested image range maps to storage ranges, zero-filled ranges,
106/// end-of-file ranges, or special format-driver ranges. It does not issue I/O and does not
107/// allocate or modify image metadata.
108#[derive(Debug)]
109pub struct FormatReadPlan<'a, S: Storage + 'static> {
110 /// Requested read length in bytes.
111 len: u64,
112
113 /// Ordered steps covering the requested range.
114 steps: Vec<FormatReadPlanStep<'a, S>>,
115}
116
117/// One step in a [`FormatReadPlan`].
118#[derive(Debug)]
119#[non_exhaustive]
120pub enum FormatReadPlanStep<'a, S: Storage + 'static> {
121 /// Range that can be read directly from a storage object.
122 #[non_exhaustive]
123 Raw {
124 /// Storage object where this data is stored.
125 storage: &'a S,
126
127 /// Offset in `storage` where this data is stored.
128 offset: u64,
129
130 /// Offset in the requested image where this step starts.
131 image_offset: u64,
132
133 /// Length of this step in bytes.
134 len: u64,
135
136 /// Whether this raw mapping may be written to directly.
137 writable: bool,
138 },
139
140 /// Range that should be returned as zeroes.
141 #[non_exhaustive]
142 Zero {
143 /// Offset in the requested image where this step starts.
144 image_offset: u64,
145
146 /// Length of this step in bytes.
147 len: u64,
148
149 /// Whether these zeroes are explicit on the top format layer.
150 explicit: bool,
151 },
152
153 /// Range beyond the image's top-layer end of file.
154 ///
155 /// Normal reads fill this range with zeroes.
156 #[non_exhaustive]
157 Eof {
158 /// Offset in the requested image where this step starts.
159 image_offset: u64,
160
161 /// Length of this step in bytes.
162 len: u64,
163 },
164
165 /// Range that must be interpreted by the image format driver.
166 #[non_exhaustive]
167 Special {
168 /// Format layer where this special data was encountered.
169 layer: &'a FormatAccess<S>,
170
171 /// Original offset on `layer` to pass to `readv_special()`.
172 offset: u64,
173
174 /// Offset in the requested image where this step starts.
175 image_offset: u64,
176
177 /// Length of this step in bytes.
178 len: u64,
179 },
180}
181
182impl<'a, S: Storage + 'static> FormatReadPlan<'a, S> {
183 /// Create an empty read plan for a request of `len` bytes.
184 fn new(len: u64) -> Self {
185 FormatReadPlan {
186 len,
187 steps: Vec::new(),
188 }
189 }
190
191 /// Add a step to the read plan.
192 fn push(&mut self, step: FormatReadPlanStep<'a, S>) {
193 self.steps.push(step);
194 }
195
196 /// Return the requested read length in bytes.
197 pub fn len(&self) -> u64 {
198 self.len
199 }
200
201 /// Return whether this plan covers an empty request.
202 pub fn is_empty(&self) -> bool {
203 self.len == 0
204 }
205
206 /// Return the ordered steps covering the requested range.
207 pub fn steps(&self) -> &[FormatReadPlanStep<'a, S>] {
208 &self.steps
209 }
210}
211
212// When adding new public methods, don’t forget to add them to sync_wrappers, too.
213impl<S: Storage + 'static> FormatAccess<S> {
214 /// Wrap a format driver instance in `FormatAccess`.
215 ///
216 /// `FormatAccess` provides I/O access to disk images, based on the functionality offered by
217 /// the individual format drivers via `FormatDriverInstance`.
218 pub fn new<D: FormatDriverInstance<Storage = S> + 'static>(inner: D) -> Self {
219 let writable = inner.writable();
220 FormatAccess {
221 inner: Box::new(inner),
222 read_parallelization: 1,
223 write_parallelization: 1,
224 writable,
225 }
226 }
227
228 /// Return the contained format driver instance.
229 pub fn inner(&self) -> &dyn FormatDriverInstance<Storage = S> {
230 self.inner.as_ref()
231 }
232
233 /// Return the contained format driver instance.
234 pub fn inner_mut(&mut self) -> &mut dyn FormatDriverInstance<Storage = S> {
235 self.inner.as_mut()
236 }
237
238 /// Return the disk size in bytes.
239 pub fn size(&self) -> u64 {
240 self.inner.size()
241 }
242
243 /// Set the number of simultaneous async requests per read.
244 ///
245 /// When issuing read requests, issue this many async requests in parallel (still in a single
246 /// thread). The default count is `1`, i.e. no parallel requests.
247 pub fn set_async_read_parallelization(&mut self, count: usize) {
248 self.read_parallelization = count;
249 }
250
251 /// Set the number of simultaneous async requests per write.
252 ///
253 /// When issuing write requests, issue this many async requests in parallel (still in a single
254 /// thread). The default count is `1`, i.e. no parallel requests.
255 pub fn set_async_write_parallelization(&mut self, count: usize) {
256 self.write_parallelization = count;
257 }
258
259 /// Return all storage dependencies of this image.
260 ///
261 /// Includes recursive dependencies, i.e. those from other image dependencies like backing
262 /// images.
263 pub(crate) fn collect_storage_dependencies(&self) -> Vec<&S> {
264 self.inner.collect_storage_dependencies()
265 }
266
267 /// Minimal I/O alignment, for both length and offset.
268 ///
269 /// All requests to this image should be aligned to this value, both in length and offset.
270 ///
271 /// Requests that do not match this alignment will be realigned internally, which requires
272 /// creating bounce buffers and read-modify-write cycles for write requests, which is costly,
273 /// so should be avoided.
274 pub fn req_align(&self) -> usize {
275 self.inner
276 .collect_storage_dependencies()
277 .into_iter()
278 .fold(1, |max, s| cmp::max(max, s.req_align()))
279 }
280
281 /// Minimal memory buffer alignment, for both address and length.
282 ///
283 /// All buffers used in requests to this image should be aligned to this value, both their
284 /// address and length.
285 ///
286 /// Request buffers that do not match this alignment will be realigned internally, which
287 /// requires creating bounce buffers, which is costly, so should be avoided.
288 pub fn mem_align(&self) -> usize {
289 self.inner
290 .collect_storage_dependencies()
291 .into_iter()
292 .fold(1, |max, s| cmp::max(max, s.mem_align()))
293 }
294
295 /// Read the data from the given mapping.
296 async fn read_chunk(
297 &self,
298 mut bufv: IoVectorMut<'_>,
299 mapping: Mapping<'_, S>,
300 ) -> io::Result<()> {
301 match mapping {
302 Mapping::Raw {
303 storage,
304 offset,
305 writable: _,
306 } => storage.readv(bufv, offset).await,
307
308 Mapping::Zero { explicit: _ } | Mapping::Eof {} => {
309 bufv.fill(0);
310 Ok(())
311 }
312
313 // FIXME: TOCTTOU problem. Not sure how to fully fix it, if possible at all.
314 // (Concurrent writes can change the mapping, but the driver will have to reload the
315 // mapping because it cannot pass it in `NonRecursiveMapping::Special`. It may then
316 // find that this is no longer a “special” range. Even passing the low-level mapping
317 // information in `Mapping::Special` wouldn’t fully fix it, though: If concurrent
318 // writes change the low-level cluster type, and the driver then tries to e.g.
319 // decompress the data that was there, that may well fail.)
320 Mapping::Special { layer, offset } => layer.inner.readv_special(bufv, offset).await,
321 }
322 }
323
324 /// Return the shallow mapping at `offset`.
325 ///
326 /// Find what `offset` is mapped to, which may be another format layer, return that
327 /// information, and the length of the continuous mapping (from `offset`).
328 ///
329 /// Use [`FormatAccess::get_mapping()`] to recursively fully resolve references to other format
330 /// layers.
331 pub async fn get_shallow_mapping(
332 &self,
333 offset: u64,
334 max_length: u64,
335 ) -> io::Result<(ShallowMapping<'_, S>, u64)> {
336 self.inner
337 .get_mapping(offset, max_length)
338 .await
339 .map(|(m, l)| (m, cmp::min(l, max_length)))
340 }
341
342 /// Return the recursively resolved mapping at `offset`.
343 ///
344 /// Find what `offset` is mapped to, return that mapping information, and the length of that
345 /// continuous mapping (from `offset`).
346 ///
347 /// All data references to other format layers are automatically resolved (recursively), so
348 /// that the result are more “trivial” mappings (unless prevented by special mappings like
349 /// compressed clusters).
350 pub async fn get_mapping(
351 &self,
352 mut offset: u64,
353 mut max_length: u64,
354 ) -> io::Result<(Mapping<'_, S>, u64)> {
355 let mut format_layer = self;
356 let mut writable_gate = true;
357
358 loop {
359 let (mapping, length) = format_layer.get_shallow_mapping(offset, max_length).await?;
360
361 match mapping {
362 ShallowMapping::Raw {
363 storage,
364 offset,
365 writable,
366 } => {
367 return Ok((
368 Mapping::Raw {
369 storage,
370 offset,
371 writable: writable && writable_gate,
372 },
373 length,
374 ))
375 }
376
377 ShallowMapping::Indirect {
378 layer: recurse_layer,
379 offset: recurse_offset,
380 writable: recurse_writable,
381 } => {
382 format_layer = recurse_layer;
383 offset = recurse_offset;
384 writable_gate = recurse_writable;
385 max_length = length;
386 }
387
388 ShallowMapping::Zero { explicit } => {
389 // If this is not the top layer, always clear `explicit`
390 return if explicit && ptr::eq(format_layer, self) {
391 Ok((Mapping::Zero { explicit: true }, length))
392 } else {
393 Ok((Mapping::Zero { explicit: false }, length))
394 };
395 }
396
397 ShallowMapping::Eof {} => {
398 // Return EOF only on top layer, zero otherwise
399 return if ptr::eq(format_layer, self) {
400 Ok((Mapping::Eof {}, 0))
401 } else {
402 Ok((Mapping::Zero { explicit: false }, max_length))
403 };
404 }
405
406 ShallowMapping::Special { offset } => {
407 return Ok((
408 Mapping::Special {
409 layer: format_layer,
410 offset,
411 },
412 length,
413 ));
414 }
415 }
416 }
417 }
418
419 /// Plan a read without issuing storage I/O.
420 ///
421 /// The returned plan covers `length` bytes starting at `offset`, matching the behavior of
422 /// [`FormatAccess::readv()`]: raw ranges point at the underlying storage object, zero ranges
423 /// should be filled with zeroes, EOF ranges should also be filled with zeroes, and special
424 /// ranges must be read through the format driver.
425 pub async fn plan_read(
426 &self,
427 mut offset: u64,
428 mut length: u64,
429 ) -> io::Result<FormatReadPlan<'_, S>> {
430 offset.checked_add(length).ok_or_else(|| {
431 io::Error::new(io::ErrorKind::InvalidInput, "Read plan range overflow")
432 })?;
433
434 let requested_length = length;
435 let mut plan = FormatReadPlan::new(requested_length);
436
437 while length > 0 {
438 let (mapping, mapped_length) = self.get_mapping(offset, length).await?;
439 if mapped_length == 0 {
440 assert!(mapping.is_eof());
441 plan.push(FormatReadPlanStep::Eof {
442 image_offset: offset,
443 len: length,
444 });
445 break;
446 }
447
448 let step_length = cmp::min(mapped_length, length);
449 match mapping {
450 Mapping::Raw {
451 storage,
452 offset: storage_offset,
453 writable,
454 } => plan.push(FormatReadPlanStep::Raw {
455 storage,
456 offset: storage_offset,
457 image_offset: offset,
458 len: step_length,
459 writable,
460 }),
461
462 Mapping::Zero { explicit } => plan.push(FormatReadPlanStep::Zero {
463 image_offset: offset,
464 len: step_length,
465 explicit,
466 }),
467
468 Mapping::Eof {} => plan.push(FormatReadPlanStep::Eof {
469 image_offset: offset,
470 len: step_length,
471 }),
472
473 Mapping::Special {
474 layer,
475 offset: special_offset,
476 } => plan.push(FormatReadPlanStep::Special {
477 layer,
478 offset: special_offset,
479 image_offset: offset,
480 len: step_length,
481 }),
482 }
483
484 offset += step_length;
485 length -= step_length;
486 }
487
488 Ok(plan)
489 }
490
491 /// Create a raw data mapping at `offset`.
492 ///
493 /// Ensure that `offset` is directly mapped to some storage object, up to a length of `length`.
494 /// Return the storage object, the corresponding offset there, and the continuous length that
495 /// we were able to map (less than or equal to `length`).
496 ///
497 /// If `overwrite` is true, the contents in the range are supposed to be overwritten and may be
498 /// discarded. Otherwise, they are kept.
499 pub async fn ensure_data_mapping(
500 &self,
501 offset: u64,
502 length: u64,
503 overwrite: bool,
504 ) -> io::Result<(&S, u64, u64)> {
505 let (storage, mapped_offset, mapped_length) = self
506 .inner
507 .ensure_data_mapping(offset, length, overwrite)
508 .await?;
509 let mapped_length = cmp::min(length, mapped_length);
510 assert!(mapped_length > 0);
511 Ok((storage, mapped_offset, mapped_length))
512 }
513
514 /// Read data at `offset` into `bufv`.
515 ///
516 /// Reads until `bufv` is filled completely, i.e. will not do short reads. When reaching the
517 /// end of file, the rest of `bufv` is filled with 0.
518 pub async fn readv(&self, mut bufv: IoVectorMut<'_>, mut offset: u64) -> io::Result<()> {
519 let mut workers = (self.read_parallelization > 1).then(FutureVector::new);
520
521 while !bufv.is_empty() {
522 let (mapping, chunk_length) = self.get_mapping(offset, bufv.len()).await?;
523 if chunk_length == 0 {
524 assert!(mapping.is_eof());
525 bufv.fill(0);
526 break;
527 }
528
529 if let Some(workers) = workers.as_mut() {
530 while workers.len() >= self.read_parallelization {
531 workers.select().await?;
532 }
533 }
534
535 let (chunk, remainder) = bufv.split_at(chunk_length);
536 bufv = remainder;
537 offset += chunk_length;
538
539 if let Some(workers) = workers.as_mut() {
540 workers.push(Box::pin(self.read_chunk(chunk, mapping)));
541 } else {
542 self.read_chunk(chunk, mapping).await?;
543 }
544 }
545
546 if let Some(mut workers) = workers {
547 workers.discarding_join().await?;
548 }
549
550 Ok(())
551 }
552
553 /// Read data at `offset` into `buf`.
554 ///
555 /// Reads until `buf` is filled completely, i.e. will not do short reads. When reaching the
556 /// end of file, the rest of `buf` is filled with 0.
557 pub async fn read(&self, buf: impl Into<IoVectorMut<'_>>, offset: u64) -> io::Result<()> {
558 self.readv(buf.into(), offset).await
559 }
560
561 /// Write data from `bufv` to `offset`.
562 ///
563 /// Writes all data from `bufv` (or returns an error), i.e. will not do short writes. Reaching
564 /// the end of file before the end of the buffer results in an error.
565 pub async fn writev(&self, mut bufv: IoVector<'_>, mut offset: u64) -> io::Result<()> {
566 if !self.writable {
567 return Err(io::Error::other("Image is read-only"));
568 }
569
570 // Limit to disk size
571 let disk_size = self.inner.size();
572 if offset >= disk_size {
573 return Ok(());
574 }
575 if bufv.len() > disk_size - offset {
576 bufv = bufv.split_at(disk_size - offset).0;
577 }
578
579 let mut workers = (self.write_parallelization > 1).then(FutureVector::new);
580
581 while !bufv.is_empty() {
582 let (storage, st_offset, st_length) =
583 self.ensure_data_mapping(offset, bufv.len(), true).await?;
584
585 if let Some(workers) = workers.as_mut() {
586 while workers.len() >= self.write_parallelization {
587 workers.select().await?;
588 }
589 }
590
591 let (chunk, remainder) = bufv.split_at(st_length);
592 bufv = remainder;
593 offset += st_length;
594
595 if let Some(workers) = workers.as_mut() {
596 workers.push(Box::pin(storage.writev(chunk, st_offset)));
597 } else {
598 storage.writev(chunk, st_offset).await?;
599 }
600 }
601
602 if let Some(mut workers) = workers {
603 workers.discarding_join().await?;
604 }
605
606 Ok(())
607 }
608
609 /// Write data from `buf` to `offset`.
610 ///
611 /// Writes all data from `bufv` (or returns an error), i.e. will not do short writes. Reaching
612 /// the end of file before the end of the buffer results in an error.
613 pub async fn write(&self, buf: impl Into<IoVector<'_>>, offset: u64) -> io::Result<()> {
614 self.writev(buf.into(), offset).await
615 }
616
617 /// Check whether the given range is zero.
618 ///
619 /// Checks for zero mappings, not zero data (although this might be changed in the future).
620 ///
621 /// Errors are treated as non-zero areas.
622 async fn is_range_zero(&self, mut offset: u64, mut length: u64) -> bool {
623 while length > 0 {
624 match self.get_mapping(offset, length).await {
625 Ok((Mapping::Zero { explicit: _ }, mlen)) => {
626 offset += mlen;
627 length -= mlen;
628 }
629 _ => return false,
630 };
631 }
632
633 true
634 }
635
636 /// Ensure the given range reads as zeroes, without write-zeroes support.
637 ///
638 /// Does not require support for efficient zeroing, instead writing zeroes when the range is
639 /// not zero yet. If `allocate` is true, areas that are not currently allocated will be
640 /// allocated to write zeroes there; if it is false, unallocated areas that currently read as
641 /// zero are left alone.
642 ///
643 /// However, can still use efficient zero support if present.
644 ///
645 /// The main use case is to handle unaligned zero requests. Quite inefficient for large areas.
646 async fn soft_ensure_zero(&self, mut offset: u64, mut length: u64) -> io::Result<()> {
647 // “Fast” path: Try to efficiently zero as much as possible
648 if let Some(gran) = self.inner.zero_granularity() {
649 let end = offset.checked_add(length).ok_or_else(|| {
650 io::Error::new(
651 io::ErrorKind::InvalidInput,
652 format!("Write-zero wrap-around: {offset} + {length}"),
653 )
654 })?;
655 let mut aligned_start = offset - offset % gran;
656 // Could be handled, but don’t bother
657 let mut aligned_end = end.checked_next_multiple_of(gran).ok_or_else(|| {
658 io::Error::new(
659 io::ErrorKind::InvalidInput,
660 "Write-zero wrap-around at cluster granularity",
661 )
662 })?;
663
664 aligned_end = cmp::min(aligned_end, self.size());
665
666 // Whether the whole area could be efficiently zeroed
667 let mut fully_zeroed = true;
668
669 if offset > aligned_start
670 && !self
671 .is_range_zero(aligned_start, offset - aligned_start)
672 .await
673 {
674 // Non-zero head, we cannot zero that cluster. Still try to zero as much as
675 // possible.
676 fully_zeroed = false;
677 aligned_start += gran;
678 }
679 if end < aligned_end && !self.is_range_zero(end, aligned_end - end).await {
680 // Non-zero tail, we cannot zero that cluster. Still try to zero as much as
681 // possible.
682 fully_zeroed = false;
683 aligned_end -= gran;
684 }
685
686 while aligned_start < aligned_end {
687 let res = self
688 .inner
689 .ensure_zero_mapping(aligned_start, aligned_end - aligned_start)
690 .await;
691 if let Ok((zofs, zlen)) = res {
692 if zofs != aligned_start || zlen == 0 {
693 // Produced a gap, so will need to fall back, but still try to zero as
694 // much as possible
695 fully_zeroed = false;
696 if zlen == 0 {
697 // Cannot go on
698 break;
699 }
700 }
701 aligned_start = zofs + zlen;
702 } else {
703 // Ignore errors, just fall back
704 fully_zeroed = false;
705 break;
706 }
707 }
708
709 if fully_zeroed {
710 // Everything zeroed, no need to check
711 return Ok(());
712 }
713 }
714
715 // Slow path: Everything that is not zero in this layer is allocated as data and zeroes are
716 // written. The more we zeroed in the fast path, the quicker this will be.
717 while length > 0 {
718 let (mapping, mlen) = self.inner.get_mapping(offset, length).await?;
719 let mlen = cmp::min(mlen, length);
720
721 let mapping = match mapping {
722 ShallowMapping::Raw {
723 storage,
724 offset,
725 writable,
726 } => writable.then_some((storage, offset)),
727 // For already zero clusters, we don’t need to do anything
728 ShallowMapping::Zero { explicit: true } => {
729 // Nothing to be done
730 offset += mlen;
731 length -= mlen;
732 continue;
733 }
734 // For unallocated clusters, we should establish zero data
735 ShallowMapping::Zero { explicit: false }
736 | ShallowMapping::Indirect {
737 layer: _,
738 offset: _,
739 writable: _,
740 } => None,
741 ShallowMapping::Eof {} => {
742 return Err(io::ErrorKind::UnexpectedEof.into());
743 }
744 ShallowMapping::Special { offset: _ } => None,
745 };
746
747 let (file, mofs, mlen) = if let Some((file, mofs)) = mapping {
748 (file, mofs, mlen)
749 } else {
750 self.ensure_data_mapping(offset, mlen, true).await?
751 };
752
753 write_full_zeroes(file, mofs, mlen).await?;
754 offset += mlen;
755 length -= mlen;
756 }
757
758 Ok(())
759 }
760
761 /// Ensure the given range reads as zeroes.
762 ///
763 /// May use efficient zeroing for a subset of the given range, if supported by the format.
764 /// Will not discard anything, which keeps existing data mappings usable, albeit writing to
765 /// mappings that are now zeroed may have no effect.
766 ///
767 /// Check if [`FormatAccess::discard_to_zero()`] better suits your needs: It may work better on
768 /// a wider range of formats (`write_zeroes()` requires support for preallocated zero clusters,
769 /// which qcow2 does have, but other formats may not), and can actually free up space.
770 /// However, because it can break existing data mappings, it requires a mutable `self`
771 /// reference.
772 pub async fn write_zeroes(&self, mut offset: u64, length: u64) -> io::Result<()> {
773 let max_offset = offset.checked_add(length).ok_or_else(|| {
774 io::Error::new(io::ErrorKind::InvalidInput, "Write-zeroes range overflow")
775 })?;
776
777 while offset < max_offset {
778 let (zofs, zlen) = self
779 .inner
780 .ensure_zero_mapping(offset, max_offset - offset)
781 .await?;
782 if zlen == 0 {
783 break;
784 }
785 // Fill up head, i.e. the range [offset, zofs)
786 self.soft_ensure_zero(offset, zofs - offset).await?;
787 offset = zofs + zlen;
788 }
789
790 // Fill up tail, i.e. the remaining range [offset, max_offset)
791 self.soft_ensure_zero(offset, max_offset - offset).await?;
792 Ok(())
793 }
794
795 /// Discard the given range, ensure it is read back as zeroes.
796 ///
797 /// Effectively the same as [`FormatAccess::write_zeroes()`], but discard as much of the
798 /// existing allocation as possible. This breaks existing data mappings, so needs a mutable
799 /// reference to `self`, which ensures that existing data references (which have the lifetime
800 /// of an immutable `self` reference) cannot be kept.
801 ///
802 /// Areas that cannot be discarded (because of format-inherent alignment restrictions) are
803 /// still overwritten with zeroes, unless discarding is not supported altogether.
804 pub async fn discard_to_zero(&mut self, offset: u64, length: u64) -> io::Result<()> {
805 // Safe: `&mut self` guarantees nobody has concurrent data mappings
806 unsafe { self.discard_to_zero_unsafe(offset, length).await }
807 }
808
809 /// Discard the given range, ensure it is read back as zeroes.
810 ///
811 /// Unsafe variant of [`FormatAccess::discard_to_zero()`], only requiring an immutable `&self`.
812 ///
813 /// # Safety
814 ///
815 /// This function may invalidate existing data mappings. The caller must ensure to invalidate
816 /// all concurrently existing data mappings they have. Note that this includes concurrent
817 /// accesses through this type ([`FormatAccess`]), which may hold these mappings internally
818 /// while they run.
819 ///
820 /// One way to ensure safety is to have a mutable reference to `self`, which allows using the
821 /// safe variant [`FormatAccess::discard_to_zero()`].
822 pub async unsafe fn discard_to_zero_unsafe(
823 &self,
824 mut offset: u64,
825 length: u64,
826 ) -> io::Result<()> {
827 let max_offset = offset.checked_add(length).ok_or_else(|| {
828 io::Error::new(
829 io::ErrorKind::InvalidInput,
830 "Discard-to-zero range overflow",
831 )
832 })?;
833
834 while offset < max_offset {
835 // Safe: Caller guarantees this is safe
836 let (zofs, zlen) = unsafe {
837 self.inner
838 .discard_to_zero_unsafe(offset, max_offset - offset)
839 .await?
840 };
841 if zlen == 0 {
842 break;
843 }
844 // Fill up head, i.e. the range [offset, zofs)
845 self.soft_ensure_zero(offset, zofs - offset).await?;
846 offset = zofs + zlen;
847 }
848
849 // Fill up tail, i.e. the remaining range [offset, max_offset)
850 self.soft_ensure_zero(offset, max_offset - offset).await?;
851 Ok(())
852 }
853
854 /// Discard the given range, not guaranteeing specific data on read-back.
855 ///
856 /// Discard as much of the given range as possible, and keep the rest as-is. Does not
857 /// guarantee any specific data on read-back, in contrast to
858 /// [`FormatAccess::discard_to_zero()`].
859 ///
860 /// Discarding being unsupported by this format is still returned as an error
861 /// ([`std::io::ErrorKind::Unsupported`])
862 pub async fn discard_to_any(&mut self, offset: u64, length: u64) -> io::Result<()> {
863 unsafe { self.discard_to_any_unsafe(offset, length).await }
864 }
865
866 /// Discard the given range, not guaranteeing specific data on read-back.
867 ///
868 /// Unsafe variant of [`FormatAccess::discard_to_any()`], only requiring an immutable `&self`.
869 ///
870 /// # Safety
871 ///
872 /// This function may invalidate existing data mappings. The caller must ensure to invalidate
873 /// all concurrently existing data mappings they have. Note that this includes concurrent
874 /// accesses through this type ([`FormatAccess`]), which may hold these mappings internally
875 /// while they run.
876 ///
877 /// One way to ensure safety is to have a mutable reference to `self`, which allows using the
878 /// safe variant [`FormatAccess::discard_to_any()`].
879 pub async unsafe fn discard_to_any_unsafe(
880 &self,
881 mut offset: u64,
882 length: u64,
883 ) -> io::Result<()> {
884 let max_offset = offset.checked_add(length).ok_or_else(|| {
885 io::Error::new(io::ErrorKind::InvalidInput, "Discard-to-any range overflow")
886 })?;
887
888 while offset < max_offset {
889 // Safe: Caller guarantees this is safe
890 let (dofs, dlen) = unsafe {
891 self.inner
892 .discard_to_any_unsafe(offset, max_offset - offset)
893 .await?
894 };
895 if dlen == 0 {
896 break;
897 }
898 offset = dofs + dlen;
899 }
900
901 Ok(())
902 }
903
904 /// Discard the given range, such that the backing image becomes visible.
905 ///
906 /// Discard as much of the given range as possible so that a backing image’s data becomes
907 /// visible, and keep the rest as-is. This breaks existing data mappings, so needs a mutable
908 /// reference to `self`, which ensures that existing data references (which have the lifetime
909 /// of an immutable `self` reference) cannot be kept.
910 pub async fn discard_to_backing(&mut self, offset: u64, length: u64) -> io::Result<()> {
911 // Safe: `&mut self` guarantees nobody has concurrent data mappings
912 unsafe { self.discard_to_backing_unsafe(offset, length).await }
913 }
914
915 /// Discard the given range, such that the backing image becomes visible.
916 ///
917 /// Unsafe variant of [`FormatAccess::discard_to_backing()`], only requiring an immutable
918 /// `&self`.
919 ///
920 /// # Safety
921 ///
922 /// This function may invalidate existing data mappings. The caller must ensure to invalidate
923 /// all concurrently existing data mappings they have. Note that this includes concurrent
924 /// accesses through this type ([`FormatAccess`]), which may hold these mappings internally
925 /// while they run.
926 ///
927 /// One way to ensure safety is to have a mutable reference to `self`, which allows using the
928 /// safe variant [`FormatAccess::discard_to_backing()`].
929 pub async unsafe fn discard_to_backing_unsafe(
930 &self,
931 mut offset: u64,
932 length: u64,
933 ) -> io::Result<()> {
934 let max_offset = offset.checked_add(length).ok_or_else(|| {
935 io::Error::new(
936 io::ErrorKind::InvalidInput,
937 "Discard-to-backing range overflow",
938 )
939 })?;
940
941 while offset < max_offset {
942 // Safe: Caller guarantees this is safe
943 let (dofs, dlen) = unsafe {
944 self.inner
945 .discard_to_backing_unsafe(offset, max_offset - offset)
946 .await?
947 };
948 if dlen == 0 {
949 break;
950 }
951 offset = dofs + dlen;
952 }
953
954 Ok(())
955 }
956
957 /// Flush internal buffers. Always call this before drop!
958 ///
959 /// Does not necessarily sync those buffers to disk. When using `flush()`, consider whether
960 /// you want to call `sync()` afterwards.
961 ///
962 /// Because of the current lack of stable `async_drop`, you must manually call this before
963 /// dropping a `FormatAccess` instance! (Not necessarily for read-only images, though.)
964 ///
965 /// Note that this will not drop the buffers, so they may still be used to serve later
966 /// accesses. Use [`FormatAccess::invalidate_cache()`] to drop all buffers.
967 pub async fn flush(&self) -> io::Result<()> {
968 self.inner.flush().await
969 }
970
971 /// Sync data already written to the storage hardware.
972 ///
973 /// This does not necessarily include flushing internal buffers, i.e. `flush`. When using
974 /// `sync()`, consider whether you want to call `flush()` before it.
975 pub async fn sync(&self) -> io::Result<()> {
976 self.inner.sync().await
977 }
978
979 /// Drop internal buffers.
980 ///
981 /// This drops all internal buffers, but does not flush them! All cached data is reloaded from
982 /// disk on subsequent accesses.
983 ///
984 /// # Safety
985 /// Not flushing internal buffers may cause image corruption. You must ensure the on-disk
986 /// state is consistent.
987 pub async unsafe fn invalidate_cache(&self) -> io::Result<()> {
988 // Safety ensured by caller
989 unsafe { self.inner.invalidate_cache() }.await
990 }
991
992 /// Resize to the given size.
993 ///
994 /// Set the disk size to `new_size`. If `new_size` is smaller than the current size, ignore
995 /// both preallocation modes and discard the data after `new_size`.
996 ///
997 /// If `new_size` is larger than the current size, `prealloc_mode` determines whether and how
998 /// the new range should be allocated; depending on the image format, is possible some
999 /// preallocation modes are not supported, in which case an [`std::io::ErrorKind::Unsupported`]
1000 /// is returned.
1001 ///
1002 /// This may break existing data mappings, so needs a mutable reference to `self`, which
1003 /// ensures that existing data references (which have the lifetime of an immutable `self`
1004 /// reference) cannot be kept.
1005 ///
1006 /// See also [`FormatAccess::resize_grow()`] and [`FormatAccess::resize_shrink()`], whose more
1007 /// specialized interface may be useful when you know whether you want to grow or shrink the
1008 /// image.
1009 pub async fn resize(
1010 &mut self,
1011 new_size: u64,
1012 prealloc_mode: PreallocateMode,
1013 ) -> io::Result<()> {
1014 match new_size.cmp(&self.size()) {
1015 std::cmp::Ordering::Less => self.resize_shrink(new_size).await,
1016 std::cmp::Ordering::Equal => Ok(()),
1017 std::cmp::Ordering::Greater => self.resize_grow(new_size, prealloc_mode).await,
1018 }
1019 }
1020
1021 /// Resize to the given size, which must be greater than the current size.
1022 ///
1023 /// Set the disk size to `new_size`, preallocating the new space according to `prealloc_mode`.
1024 /// Depending on the image format, it is possible some preallocation modes are not supported,
1025 /// in which case an [`std::io::ErrorKind::Unsupported`] is returned.
1026 ///
1027 /// If the current size is already `new_size` or greater, do nothing.
1028 pub async fn resize_grow(
1029 &self,
1030 new_size: u64,
1031 prealloc_mode: PreallocateMode,
1032 ) -> io::Result<()> {
1033 self.inner.resize_grow(new_size, prealloc_mode).await
1034 }
1035
1036 /// Truncate to the given size, which must be smaller than the current size.
1037 ///
1038 /// Set the disk size to `new_size`, discarding the data after `new_size`.
1039 ///
1040 /// May break existing data mappings thanks to the mutable `self` reference.
1041 ///
1042 /// If the current size is already `new_size` or smaller, do nothing.
1043 pub async fn resize_shrink(&mut self, new_size: u64) -> io::Result<()> {
1044 self.inner.resize_shrink(new_size).await
1045 }
1046}
1047
1048impl<S: Storage> Mapping<'_, S> {
1049 /// Return `true` if and only if this mapping signifies the end of file.
1050 pub fn is_eof(&self) -> bool {
1051 matches!(self, Mapping::Eof {})
1052 }
1053}
1054
1055impl<S: Storage> Display for FormatAccess<S> {
1056 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1057 self.inner.fmt(f)
1058 }
1059}
1060
1061impl<S: Storage> Display for Mapping<'_, S> {
1062 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1063 match self {
1064 Mapping::Raw {
1065 storage,
1066 offset,
1067 writable,
1068 } => {
1069 let writable = if *writable { "rw" } else { "ro" };
1070 write!(f, "{storage}:0x{offset:x}/{writable}")
1071 }
1072
1073 Mapping::Zero { explicit } => {
1074 let explicit = if *explicit { "explicit" } else { "unallocated" };
1075 write!(f, "<zero:{explicit}>")
1076 }
1077
1078 Mapping::Eof {} => write!(f, "<eof>"),
1079
1080 Mapping::Special { layer, offset } => {
1081 write!(f, "<special:{layer}:0x{offset:x}>")
1082 }
1083 }
1084 }
1085}
1086
1087#[cfg(test)]
1088mod tests {
1089 use super::{FormatAccess, FormatReadPlanStep};
1090 use crate::null::Null;
1091 use crate::raw::Raw;
1092 use std::io;
1093
1094 #[test]
1095 fn raw_read_plan_exposes_storage_extent() -> io::Result<()> {
1096 let runtime = tokio::runtime::Builder::new_current_thread().build()?;
1097
1098 runtime.block_on(async {
1099 let raw = Raw::open_image(Null::new(4096), true).await?;
1100 let image = FormatAccess::new(raw);
1101 let plan = image.plan_read(512, 1024).await?;
1102
1103 assert_eq!(plan.len(), 1024);
1104 assert!(!plan.is_empty());
1105 assert_eq!(plan.steps().len(), 1);
1106
1107 match &plan.steps()[0] {
1108 FormatReadPlanStep::Raw {
1109 offset,
1110 image_offset,
1111 len,
1112 ..
1113 } => {
1114 assert_eq!(*offset, 512);
1115 assert_eq!(*image_offset, 512);
1116 assert_eq!(*len, 1024);
1117 }
1118 step => panic!("expected raw step, got {step:?}"),
1119 }
1120
1121 Ok(())
1122 })
1123 }
1124
1125 #[test]
1126 fn raw_read_plan_marks_eof_tail() -> io::Result<()> {
1127 let runtime = tokio::runtime::Builder::new_current_thread().build()?;
1128
1129 runtime.block_on(async {
1130 let raw = Raw::open_image(Null::new(4096), true).await?;
1131 let image = FormatAccess::new(raw);
1132 let plan = image.plan_read(3072, 2048).await?;
1133
1134 assert_eq!(plan.len(), 2048);
1135 assert_eq!(plan.steps().len(), 2);
1136
1137 match &plan.steps()[0] {
1138 FormatReadPlanStep::Raw {
1139 offset,
1140 image_offset,
1141 len,
1142 ..
1143 } => {
1144 assert_eq!(*offset, 3072);
1145 assert_eq!(*image_offset, 3072);
1146 assert_eq!(*len, 1024);
1147 }
1148 step => panic!("expected raw step, got {step:?}"),
1149 }
1150
1151 match &plan.steps()[1] {
1152 FormatReadPlanStep::Eof { image_offset, len } => {
1153 assert_eq!(*image_offset, 4096);
1154 assert_eq!(*len, 1024);
1155 }
1156 step => panic!("expected eof step, got {step:?}"),
1157 }
1158
1159 Ok(())
1160 })
1161 }
1162}
1163
1164/*
1165#[cfg(feature = "async-drop")]
1166impl<S: Storage> std::future::AsyncDrop for FormatAccess<S> {
1167 type Dropper<'a> = std::pin::Pin<Box<dyn std::future::Future<Output = ()> + 'a>> where S: 'a;
1168
1169 fn async_drop(self: std::pin::Pin<&mut Self>) -> Self::Dropper<'_> {
1170 Box::pin(async move {
1171 if let Err(err) = self.flush().await {
1172 let inner = &self.inner;
1173 tracing::error!("Failed to flush {inner}: {err}");
1174 }
1175 })
1176 }
1177}
1178*/