Skip to main content

imago/
raw.rs

1//! Access generic files as images.
2//!
3//! Allows accessing generic storage objects (`Storage`) as images (i.e. `FormatAccess`).
4
5use crate::format::builder::{
6    FormatCreateBuilder, FormatCreateBuilderBase, FormatDriverBuilder, FormatDriverBuilderBase,
7};
8use crate::format::drivers::FormatDriverInstance;
9use crate::format::gate::ImplicitOpenGate;
10use crate::format::{Format, PreallocateMode};
11use crate::sync_primitives::{Mutex, MutexGuard};
12use crate::{
13    storage, DenyImplicitOpenGate, ShallowMapping, Storage, StorageExt, StorageOpenOptions,
14};
15use maybe_async::maybe_async;
16use std::fmt::{self, Display, Formatter};
17use std::io;
18use std::path::{Path, PathBuf};
19use std::sync::atomic::{AtomicU64, Ordering};
20
21/// Wraps a storage object without any translation.
22#[derive(Debug)]
23pub struct Raw<S: Storage + 'static> {
24    /// Wrapped storage object.
25    inner: S,
26
27    /// Whether this image may be modified.
28    writable: bool,
29
30    /// Disk size, which is the file size when this object was created.
31    ///
32    /// Copy of the value in `resize_lock`, for lock-free reading.  Must only be changed via
33    /// [`Raw::set_size()`].
34    size: AtomicU64,
35
36    /// Disk size, for resize operations.
37    ///
38    /// Every resize path (growing and shrinking) must hold this lock throughout, and change the
39    /// size only via [`Raw::set_size()`].  This serializes resizes, so a resize to a smaller size
40    /// cannot overtake one to a larger size and shrink the storage below the image size.
41    resize_lock: Mutex<u64>,
42}
43
44#[maybe_async]
45impl<S: Storage + 'static> Raw<S> {
46    /// Create a new [`FormatDriverBuilder`] instance for the given image.
47    pub fn builder(image: S) -> RawOpenBuilder<S> {
48        RawOpenBuilder::new(image)
49    }
50
51    /// Create a new [`FormatDriverBuilder`] instance for an image under the given path.
52    pub fn builder_path<P: AsRef<Path>>(image_path: P) -> RawOpenBuilder<S> {
53        RawOpenBuilder::new_path(image_path)
54    }
55
56    /// Create a new [`FormatCreateBuilder`] instance for the given file.
57    pub fn create_builder(image: S) -> RawCreateBuilder<S> {
58        RawCreateBuilder::new(image)
59    }
60
61    /// Wrap `inner`, allowing it to be used as a disk image in raw format.
62    pub async fn open_image(inner: S, writable: bool) -> io::Result<Self> {
63        let size = inner.size()?;
64        Ok(Raw {
65            inner,
66            writable,
67            size: size.into(),
68            resize_lock: Mutex::new(size),
69        })
70    }
71
72    /// Open the given path as a storage object, and wrap it in `Raw`.
73    pub async fn open_path<P: AsRef<Path>>(path: P, writable: bool) -> io::Result<Self> {
74        let storage_opts = StorageOpenOptions::new().write(writable).filename(path);
75        let inner = S::open(storage_opts).await?;
76        Self::open_image(inner, writable).await
77    }
78
79    /// Wrap `inner`, allowing it to be used as a disk image in raw format.
80    #[cfg(feature = "sync-wrappers")]
81    pub fn open_image_sync(inner: S, writable: bool) -> io::Result<Self> {
82        let size = inner.size()?;
83        Ok(Raw {
84            inner,
85            writable,
86            size: size.into(),
87            resize_lock: Mutex::new(size),
88        })
89    }
90
91    #[cfg(feature = "sync-wrappers")]
92    /// Synchronous wrapper around [`Raw::open_path()`].
93    pub fn open_path_sync<P: AsRef<Path>>(path: P, writable: bool) -> io::Result<Self> {
94        tokio::runtime::Builder::new_current_thread()
95            .build()?
96            .block_on(Self::open_path(path, writable))
97    }
98
99    /// Set the disk size.
100    ///
101    /// Requires the guard of `resize_lock`, which must be held throughout the whole resize
102    /// operation.
103    fn set_size(&self, size_guard: &mut MutexGuard<'_, u64>, new_size: u64) {
104        **size_guard = new_size;
105        self.size.store(new_size, Ordering::Relaxed);
106    }
107}
108
109#[maybe_async(?Send)]
110impl<S: Storage + 'static> FormatDriverInstance for Raw<S> {
111    type Storage = S;
112
113    fn format(&self) -> Format {
114        Format::Raw
115    }
116
117    async unsafe fn probe(_storage: &S) -> io::Result<bool>
118    where
119        Self: Sized,
120    {
121        Ok(true)
122    }
123
124    fn size(&self) -> u64 {
125        self.size.load(Ordering::Relaxed)
126    }
127
128    fn zero_granularity(&self) -> Option<u64> {
129        None
130    }
131
132    fn collect_storage_dependencies(&self) -> Vec<&S> {
133        vec![&self.inner]
134    }
135
136    fn writable(&self) -> bool {
137        self.writable
138    }
139
140    #[allow(clippy::needless_lifetimes)] // Elidable in sync, but async needs a named lifetime for the boxed future bound
141    async fn get_mapping<'a>(
142        &'a self,
143        offset: u64,
144        max_length: u64,
145    ) -> io::Result<(ShallowMapping<'a, S>, u64)> {
146        let remaining = match self.size().checked_sub(offset) {
147            None | Some(0) => return Ok((ShallowMapping::Eof {}, 0)),
148            Some(remaining) => remaining,
149        };
150
151        Ok((
152            ShallowMapping::Raw {
153                storage: &self.inner,
154                offset,
155                writable: true,
156            },
157            std::cmp::min(max_length, remaining),
158        ))
159    }
160
161    #[allow(clippy::needless_lifetimes)] // Elidable in sync, but async needs a named lifetime for the boxed future bound
162    async fn ensure_data_mapping<'a>(
163        &'a self,
164        offset: u64,
165        length: u64,
166        _overwrite: bool,
167    ) -> io::Result<(&'a S, u64, u64)> {
168        let Some(remaining) = self.size().checked_sub(offset) else {
169            return Err(io::Error::other("Cannot allocate past the end of file"));
170        };
171        if length > remaining {
172            return Err(io::Error::other("Cannot allocate past the end of file"));
173        }
174
175        Ok((&self.inner, offset, length))
176    }
177
178    async fn ensure_zero_mapping(&self, offset: u64, length: u64) -> io::Result<(u64, u64)> {
179        let zero_align = self.inner.zero_align();
180        assert!(zero_align.is_power_of_two());
181
182        let zero_align_mask = zero_align as u64 - 1;
183
184        let aligned_end = (offset + length) & !zero_align_mask;
185        let aligned_offset = (offset + zero_align_mask) & !zero_align_mask;
186        let aligned_length = aligned_end.saturating_sub(aligned_offset);
187        if aligned_length == 0 {
188            return Ok((aligned_offset, 0));
189        }
190
191        // FIXME: Introduce request flags, and request no fallback
192        self.inner
193            .write_zeroes(aligned_offset, aligned_length)
194            .await?;
195        Ok((aligned_offset, aligned_length))
196    }
197
198    async unsafe fn discard_to_zero_unsafe(
199        &self,
200        offset: u64,
201        length: u64,
202    ) -> io::Result<(u64, u64)> {
203        self.ensure_zero_mapping(offset, length).await
204    }
205
206    async unsafe fn discard_to_any_unsafe(
207        &self,
208        offset: u64,
209        length: u64,
210    ) -> io::Result<(u64, u64)> {
211        let discard_align = self.inner.discard_align();
212        assert!(discard_align.is_power_of_two());
213
214        let discard_align_mask = discard_align as u64 - 1;
215
216        let aligned_end = (offset + length) & !discard_align_mask;
217        let aligned_offset = (offset + discard_align_mask) & !discard_align_mask;
218        let aligned_length = aligned_end.saturating_sub(aligned_offset);
219        if aligned_length == 0 {
220            return Ok((aligned_offset, 0));
221        }
222
223        self.inner.discard(aligned_offset, aligned_length).await?;
224        Ok((aligned_offset, aligned_length))
225    }
226
227    async unsafe fn discard_to_backing_unsafe(
228        &self,
229        offset: u64,
230        length: u64,
231    ) -> io::Result<(u64, u64)> {
232        unsafe { self.discard_to_zero_unsafe(offset, length).await }
233    }
234
235    async fn flush(&self) -> io::Result<()> {
236        // No internal buffers to flush
237        self.inner.flush().await
238    }
239
240    async fn sync(&self) -> io::Result<()> {
241        self.inner.sync().await
242    }
243
244    async unsafe fn invalidate_cache(&self) -> io::Result<()> {
245        // No internal buffers to drop
246        // Safe: Caller says we should do this
247        unsafe { self.inner.invalidate_cache() }.await
248    }
249
250    async fn resize_grow(
251        &self,
252        new_size: u64,
253        format_prealloc_mode: PreallocateMode,
254    ) -> io::Result<()> {
255        let mut size_guard = self.resize_lock.lock().await;
256        if new_size <= *size_guard {
257            return Ok(()); // only grow, else do nothing
258        }
259
260        let storage_prealloc_mode = match format_prealloc_mode {
261            PreallocateMode::None => storage::PreallocateMode::None,
262            PreallocateMode::Zero | PreallocateMode::FormatAllocate => {
263                storage::PreallocateMode::Zero
264            }
265            PreallocateMode::FullAllocate => storage::PreallocateMode::Allocate,
266            PreallocateMode::WriteData => storage::PreallocateMode::WriteData,
267        };
268
269        // Grow the storage first, so the image never extends beyond it
270        self.inner.resize(new_size, storage_prealloc_mode).await?;
271
272        self.set_size(&mut size_guard, new_size);
273        Ok(())
274    }
275
276    async fn resize_shrink(&mut self, new_size: u64) -> io::Result<()> {
277        // Not strictly necessary with `&mut self`, but ensures all resize paths look the same
278        let mut size_guard = self.resize_lock.lock().await;
279        let old_size = *size_guard;
280        if new_size >= old_size {
281            return Ok(()); // only shrink, else do nothing
282        }
283
284        // Shrink the image first, so it never extends beyond the storage
285        self.set_size(&mut size_guard, new_size);
286
287        if let Err(err) = self
288            .inner
289            .resize(new_size, storage::PreallocateMode::None)
290            .await
291        {
292            self.set_size(&mut size_guard, old_size);
293            return Err(err);
294        }
295
296        Ok(())
297    }
298}
299
300impl<S: Storage + 'static> Display for Raw<S> {
301    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
302        write!(f, "raw[{}]", self.inner)
303    }
304}
305
306/// Options builder for opening a raw image.
307pub struct RawOpenBuilder<S: Storage + 'static>(FormatDriverBuilderBase<S>);
308
309#[maybe_async(AFIT)]
310impl<S: Storage + 'static> FormatDriverBuilder<S> for RawOpenBuilder<S> {
311    type Format = Raw<S>;
312    const FORMAT: Format = Format::Raw;
313
314    fn new(image: S) -> Self {
315        RawOpenBuilder(FormatDriverBuilderBase::new(image))
316    }
317
318    fn new_path<P: AsRef<Path>>(path: P) -> Self {
319        RawOpenBuilder(FormatDriverBuilderBase::new_path(path))
320    }
321
322    fn write(mut self, writable: bool) -> Self {
323        self.0.set_write(writable);
324        self
325    }
326
327    fn storage_open_options(mut self, options: StorageOpenOptions) -> Self {
328        self.0.set_storage_open_options(options);
329        self
330    }
331
332    async fn open<G: ImplicitOpenGate<S>>(self, mut gate: G) -> io::Result<Self::Format> {
333        let writable = self.0.get_writable();
334        let file = self.0.open_image(&mut gate).await?;
335        Raw::open_image(file, writable).await
336    }
337
338    fn get_image_path(&self) -> Option<PathBuf> {
339        self.0.get_image_path()
340    }
341
342    fn get_writable(&self) -> bool {
343        self.0.get_writable()
344    }
345
346    fn get_storage_open_options(&self) -> Option<&StorageOpenOptions> {
347        self.0.get_storage_opts()
348    }
349}
350
351/// Creation builder for a new raw image.
352pub struct RawCreateBuilder<S: Storage + 'static>(FormatCreateBuilderBase<S>);
353
354#[maybe_async(AFIT)]
355impl<S: Storage + 'static> FormatCreateBuilder<S> for RawCreateBuilder<S> {
356    const FORMAT: Format = Format::Raw;
357    type DriverBuilder = RawOpenBuilder<S>;
358
359    fn new(image: S) -> Self {
360        RawCreateBuilder(FormatCreateBuilderBase::new(image))
361    }
362
363    fn size(mut self, size: u64) -> Self {
364        self.0.set_size(size);
365        self
366    }
367
368    fn preallocate(mut self, prealloc_mode: PreallocateMode) -> Self {
369        self.0.set_preallocate(prealloc_mode);
370        self
371    }
372
373    fn get_size(&self) -> u64 {
374        self.0.get_size()
375    }
376
377    fn get_preallocate(&self) -> PreallocateMode {
378        self.0.get_preallocate()
379    }
380
381    async fn create(self) -> io::Result<()> {
382        self.create_open(DenyImplicitOpenGate::default(), |image| {
383            Ok(Raw::builder(image))
384        })
385        .await?;
386        Ok(())
387    }
388
389    async fn create_open<
390        G: ImplicitOpenGate<S>,
391        F: FnOnce(S) -> io::Result<Self::DriverBuilder>,
392    >(
393        self,
394        open_gate: G,
395        open_builder_fn: F,
396    ) -> io::Result<Raw<S>> {
397        let size = self.0.get_size();
398        let prealloc = self.0.get_preallocate();
399        let image = self.0.get_image();
400
401        // Clear of data (and allow for full preallocation, if requested)
402        if image.size()? > 0 {
403            image.resize(size, storage::PreallocateMode::None).await?;
404        }
405
406        let img = open_builder_fn(image)?.write(true).open(open_gate).await?;
407        if size > 0 {
408            img.resize_grow(size, prealloc).await?;
409        }
410
411        Ok(img)
412    }
413}
414
415#[cfg(test)]
416mod tests {
417    use super::*;
418    use crate::io_buffers::{IoVector, IoVectorMut};
419    use crate::null::Null;
420    use crate::storage::drivers::CommonStorageHelper;
421    use crate::FormatAccess;
422    use std::sync::atomic::AtomicBool;
423
424    /// Null storage whose resizes can be made to fail, or be held back.
425    #[derive(Debug, Default)]
426    struct ResizeTestStorage {
427        /// Storage size.
428        size: AtomicU64,
429
430        /// Whether `resize()` should fail.
431        fail_resize: AtomicBool,
432
433        /// Hold back resizing to this size until `release_resize` is notified (0: none).
434        #[cfg(feature = "async")]
435        hold_resize_to: AtomicU64,
436
437        /// Releases a held back resize.
438        #[cfg(feature = "async")]
439        release_resize: tokio::sync::Notify,
440
441        /// Storage helper.
442        common_storage_helper: CommonStorageHelper,
443    }
444
445    impl ResizeTestStorage {
446        fn new(size: u64) -> Self {
447            ResizeTestStorage {
448                size: size.into(),
449                ..Default::default()
450            }
451        }
452    }
453
454    impl Display for ResizeTestStorage {
455        fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
456            write!(f, "resize-test:[{}B]", self.size.load(Ordering::Relaxed))
457        }
458    }
459
460    #[maybe_async(AFIT)]
461    impl Storage for ResizeTestStorage {
462        fn size(&self) -> io::Result<u64> {
463            Ok(self.size.load(Ordering::Relaxed))
464        }
465
466        async unsafe fn pure_readv(
467            &self,
468            mut bufv: IoVectorMut<'_>,
469            _offset: u64,
470        ) -> io::Result<()> {
471            bufv.fill(0);
472            Ok(())
473        }
474
475        async unsafe fn pure_writev(&self, _bufv: IoVector<'_>, _offset: u64) -> io::Result<()> {
476            Ok(())
477        }
478
479        async fn flush(&self) -> io::Result<()> {
480            Ok(())
481        }
482
483        async fn sync(&self) -> io::Result<()> {
484            Ok(())
485        }
486
487        async unsafe fn invalidate_cache(&self) -> io::Result<()> {
488            Ok(())
489        }
490
491        fn get_storage_helper(&self) -> &CommonStorageHelper {
492            &self.common_storage_helper
493        }
494
495        async fn resize(
496            &self,
497            new_size: u64,
498            _prealloc_mode: storage::PreallocateMode,
499        ) -> io::Result<()> {
500            #[cfg(feature = "async")]
501            if self.hold_resize_to.load(Ordering::Relaxed) == new_size {
502                self.release_resize.notified().await;
503            }
504
505            if self.fail_resize.load(Ordering::Relaxed) {
506                return Err(io::Error::other("Resize failed"));
507            }
508
509            self.size.store(new_size, Ordering::Relaxed);
510            Ok(())
511        }
512    }
513
514    /// Return the image’s storage.
515    fn storage(image: &FormatAccess<ResizeTestStorage>) -> &ResizeTestStorage {
516        image.collect_storage_dependencies()[0]
517    }
518
519    /// A failed resize must leave the image size unchanged.
520    #[maybe_async::test(feature = "sync", async(feature = "async", tokio::test))]
521    async fn test_failed_resize() {
522        let raw = Raw::open_image(ResizeTestStorage::new(1024), true)
523            .await
524            .unwrap();
525        let mut image = FormatAccess::new(raw);
526
527        storage(&image).fail_resize.store(true, Ordering::Relaxed);
528
529        image
530            .resize_grow(2048, PreallocateMode::None)
531            .await
532            .unwrap_err();
533        assert_eq!(image.size(), 1024);
534        assert_eq!(storage(&image).size().unwrap(), 1024);
535
536        image.resize_shrink(512).await.unwrap_err();
537        assert_eq!(image.size(), 1024);
538        assert_eq!(storage(&image).size().unwrap(), 1024);
539
540        // Once resizing works again, sizes must follow
541        storage(&image).fail_resize.store(false, Ordering::Relaxed);
542
543        image
544            .resize_grow(2048, PreallocateMode::None)
545            .await
546            .unwrap();
547        assert_eq!(image.size(), 2048);
548        assert_eq!(storage(&image).size().unwrap(), 2048);
549
550        image.resize_shrink(512).await.unwrap();
551        assert_eq!(image.size(), 512);
552        assert_eq!(storage(&image).size().unwrap(), 512);
553    }
554
555    /// A grow to a smaller size must not overtake a concurrent grow to a larger size, which would
556    /// shrink the storage below the image size.
557    #[cfg(feature = "async")]
558    #[tokio::test]
559    async fn test_concurrent_grow() {
560        let raw = Raw::open_image(ResizeTestStorage::new(1024), true)
561            .await
562            .unwrap();
563        let image = FormatAccess::new(raw);
564
565        // Hold back the smaller grow, which starts first, so the larger one could complete first
566        storage(&image)
567            .hold_resize_to
568            .store(2048, Ordering::Relaxed);
569
570        // `join3()` polls in order: The smaller grow first, then the larger grow, then the release
571        let (small, large, ()) = futures::future::join3(
572            image.resize_grow(2048, PreallocateMode::None),
573            image.resize_grow(4096, PreallocateMode::None),
574            async { storage(&image).release_resize.notify_one() },
575        )
576        .await;
577        small.unwrap();
578        large.unwrap();
579
580        assert_eq!(image.size(), 4096);
581        assert_eq!(storage(&image).size().unwrap(), 4096);
582    }
583
584    /// Requests reaching beyond the disk size must fail without touching (and thus growing) the
585    /// underlying storage.
586    #[maybe_async::test(feature = "sync", async(feature = "async", tokio::test))]
587    async fn test_access_beyond_disk_size() {
588        let size = 1024;
589        let raw = Raw::open_image(Null::new(size), true).await.unwrap();
590        let mut image = FormatAccess::new(raw);
591
592        let check = |image: &FormatAccess<Null>, result: io::Result<()>, req: &str| {
593            let err = result.expect_err(req);
594            assert_eq!(err.kind(), io::ErrorKind::UnexpectedEof, "{req}: {err}");
595            let storage_size = image.collect_storage_dependencies()[0].size().unwrap();
596            assert_eq!(storage_size, size, "{req}");
597        };
598
599        // Entirely beyond the end, and crossing the end
600        for offset in [size, size / 2] {
601            let result = image.write(&[0u8; 4096][..], offset).await;
602            check(&image, result, &format!("write() at {offset}"));
603
604            let result = image.write_zeroes(offset, 4096).await;
605            check(&image, result, &format!("write_zeroes() at {offset}"));
606
607            let result = image.discard_to_zero(offset, 4096).await;
608            check(&image, result, &format!("discard_to_zero() at {offset}"));
609
610            let result = image.discard_to_any(offset, 4096).await;
611            check(&image, result, &format!("discard_to_any() at {offset}"));
612
613            let result = image.discard_to_backing(offset, 4096).await;
614            check(&image, result, &format!("discard_to_backing() at {offset}"));
615
616            let result = image
617                .ensure_data_mapping(offset, 4096, true)
618                .await
619                .map(|_| ());
620            check(
621                &image,
622                result,
623                &format!("ensure_data_mapping() at {offset}"),
624            );
625        }
626
627        // Empty requests at the end are fine
628        image.write(&[0u8; 0][..], size).await.unwrap();
629        image.write_zeroes(size, 0).await.unwrap();
630        image.discard_to_zero(size, 0).await.unwrap();
631        image.discard_to_any(size, 0).await.unwrap();
632        image.discard_to_backing(size, 0).await.unwrap();
633
634        // Empty data mappings, at the end and anywhere else
635        for offset in [size, 0] {
636            let (_, _, length) = image.ensure_data_mapping(offset, 0, true).await.unwrap();
637            assert_eq!(length, 0, "ensure_data_mapping() at {offset}");
638        }
639
640        assert_eq!(image.size(), size);
641    }
642}