Skip to main content

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*/