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::{
12    storage, DenyImplicitOpenGate, ShallowMapping, Storage, StorageExt, StorageOpenOptions,
13};
14use async_trait::async_trait;
15use std::fmt::{self, Display, Formatter};
16use std::io;
17use std::path::{Path, PathBuf};
18use std::sync::atomic::{AtomicU64, Ordering};
19
20/// Wraps a storage object without any translation.
21#[derive(Debug)]
22pub struct Raw<S: Storage + 'static> {
23    /// Wrapped storage object.
24    inner: S,
25
26    /// Whether this image may be modified.
27    writable: bool,
28
29    /// Disk size, which is the file size when this object was created.
30    size: AtomicU64,
31}
32
33impl<S: Storage + 'static> Raw<S> {
34    /// Create a new [`FormatDriverBuilder`] instance for the given image.
35    pub fn builder(image: S) -> RawOpenBuilder<S> {
36        RawOpenBuilder::new(image)
37    }
38
39    /// Create a new [`FormatDriverBuilder`] instance for an image under the given path.
40    pub fn builder_path<P: AsRef<Path>>(image_path: P) -> RawOpenBuilder<S> {
41        RawOpenBuilder::new_path(image_path)
42    }
43
44    /// Create a new [`FormatCreateBuilder`] instance for the given file.
45    pub fn create_builder(image: S) -> RawCreateBuilder<S> {
46        RawCreateBuilder::new(image)
47    }
48
49    /// Wrap `inner`, allowing it to be used as a disk image in raw format.
50    pub async fn open_image(inner: S, writable: bool) -> io::Result<Self> {
51        let size = inner.size()?;
52        Ok(Raw {
53            inner,
54            writable,
55            size: size.into(),
56        })
57    }
58
59    /// Open the given path as a storage object, and wrap it in `Raw`.
60    pub async fn open_path<P: AsRef<Path>>(path: P, writable: bool) -> io::Result<Self> {
61        let storage_opts = StorageOpenOptions::new().write(writable).filename(path);
62        let inner = S::open(storage_opts).await?;
63        Self::open_image(inner, writable).await
64    }
65
66    /// Wrap `inner`, allowing it to be used as a disk image in raw format.
67    #[cfg(feature = "sync-wrappers")]
68    pub fn open_image_sync(inner: S, writable: bool) -> io::Result<Self> {
69        let size = inner.size()?;
70        Ok(Raw {
71            inner,
72            writable,
73            size: size.into(),
74        })
75    }
76
77    #[cfg(feature = "sync-wrappers")]
78    /// Synchronous wrapper around [`Raw::open_path()`].
79    pub fn open_path_sync<P: AsRef<Path>>(path: P, writable: bool) -> io::Result<Self> {
80        tokio::runtime::Builder::new_current_thread()
81            .build()?
82            .block_on(Self::open_path(path, writable))
83    }
84}
85
86#[async_trait(?Send)]
87impl<S: Storage + 'static> FormatDriverInstance for Raw<S> {
88    type Storage = S;
89
90    fn format(&self) -> Format {
91        Format::Raw
92    }
93
94    async unsafe fn probe(_storage: &S) -> io::Result<bool>
95    where
96        Self: Sized,
97    {
98        Ok(true)
99    }
100
101    fn size(&self) -> u64 {
102        self.size.load(Ordering::Relaxed)
103    }
104
105    fn zero_granularity(&self) -> Option<u64> {
106        None
107    }
108
109    fn collect_storage_dependencies(&self) -> Vec<&S> {
110        vec![&self.inner]
111    }
112
113    fn writable(&self) -> bool {
114        self.writable
115    }
116
117    async fn get_mapping<'a>(
118        &'a self,
119        offset: u64,
120        max_length: u64,
121    ) -> io::Result<(ShallowMapping<'a, S>, u64)> {
122        let remaining = match self.size().checked_sub(offset) {
123            None | Some(0) => return Ok((ShallowMapping::Eof {}, 0)),
124            Some(remaining) => remaining,
125        };
126
127        Ok((
128            ShallowMapping::Raw {
129                storage: &self.inner,
130                offset,
131                writable: true,
132            },
133            std::cmp::min(max_length, remaining),
134        ))
135    }
136
137    async fn ensure_data_mapping<'a>(
138        &'a self,
139        offset: u64,
140        length: u64,
141        _overwrite: bool,
142    ) -> io::Result<(&'a S, u64, u64)> {
143        let Some(remaining) = self.size().checked_sub(offset) else {
144            return Err(io::Error::other("Cannot allocate past the end of file"));
145        };
146        if length > remaining {
147            return Err(io::Error::other("Cannot allocate past the end of file"));
148        }
149
150        Ok((&self.inner, offset, length))
151    }
152
153    async fn ensure_zero_mapping(&self, offset: u64, length: u64) -> io::Result<(u64, u64)> {
154        let zero_align = self.inner.zero_align();
155        assert!(zero_align.is_power_of_two());
156
157        let zero_align_mask = zero_align as u64 - 1;
158
159        let aligned_end = (offset + length) & !zero_align_mask;
160        let aligned_offset = (offset + zero_align_mask) & !zero_align_mask;
161        let aligned_length = aligned_end.saturating_sub(aligned_offset);
162        if aligned_length == 0 {
163            return Ok((aligned_offset, 0));
164        }
165
166        // FIXME: Introduce request flags, and request no fallback
167        self.inner
168            .write_zeroes(aligned_offset, aligned_length)
169            .await?;
170        Ok((aligned_offset, aligned_length))
171    }
172
173    async unsafe fn discard_to_zero_unsafe(
174        &self,
175        offset: u64,
176        length: u64,
177    ) -> io::Result<(u64, u64)> {
178        self.ensure_zero_mapping(offset, length).await
179    }
180
181    async unsafe fn discard_to_any_unsafe(
182        &self,
183        offset: u64,
184        length: u64,
185    ) -> io::Result<(u64, u64)> {
186        let discard_align = self.inner.discard_align();
187        assert!(discard_align.is_power_of_two());
188
189        let discard_align_mask = discard_align as u64 - 1;
190
191        let aligned_end = (offset + length) & !discard_align_mask;
192        let aligned_offset = (offset + discard_align_mask) & !discard_align_mask;
193        let aligned_length = aligned_end.saturating_sub(aligned_offset);
194        if aligned_length == 0 {
195            return Ok((aligned_offset, 0));
196        }
197
198        self.inner.discard(aligned_offset, aligned_length).await?;
199        Ok((aligned_offset, aligned_length))
200    }
201
202    async unsafe fn discard_to_backing_unsafe(
203        &self,
204        offset: u64,
205        length: u64,
206    ) -> io::Result<(u64, u64)> {
207        unsafe { self.discard_to_zero_unsafe(offset, length).await }
208    }
209
210    async fn flush(&self) -> io::Result<()> {
211        // No internal buffers to flush
212        self.inner.flush().await
213    }
214
215    async fn sync(&self) -> io::Result<()> {
216        self.inner.sync().await
217    }
218
219    async unsafe fn invalidate_cache(&self) -> io::Result<()> {
220        // No internal buffers to drop
221        // Safe: Caller says we should do this
222        unsafe { self.inner.invalidate_cache() }.await
223    }
224
225    async fn resize_grow(
226        &self,
227        new_size: u64,
228        format_prealloc_mode: PreallocateMode,
229    ) -> io::Result<()> {
230        if self
231            .size
232            .fetch_update(Ordering::Relaxed, Ordering::Relaxed, |old| {
233                (new_size > old).then_some(new_size)
234            })
235            .is_err()
236        {
237            return Ok(()); // only grow, else do nothing
238        }
239
240        let storage_prealloc_mode = match format_prealloc_mode {
241            PreallocateMode::None => storage::PreallocateMode::None,
242            PreallocateMode::Zero | PreallocateMode::FormatAllocate => {
243                storage::PreallocateMode::Zero
244            }
245            PreallocateMode::FullAllocate => storage::PreallocateMode::Allocate,
246            PreallocateMode::WriteData => storage::PreallocateMode::WriteData,
247        };
248        self.inner.resize(new_size, storage_prealloc_mode).await
249    }
250
251    async fn resize_shrink(&mut self, new_size: u64) -> io::Result<()> {
252        if self
253            .size
254            .fetch_update(Ordering::Relaxed, Ordering::Relaxed, |old| {
255                (new_size < old).then_some(new_size)
256            })
257            .is_err()
258        {
259            return Ok(()); // only shrink, else do nothing
260        }
261
262        self.inner
263            .resize(new_size, storage::PreallocateMode::None)
264            .await
265    }
266}
267
268impl<S: Storage + 'static> Display for Raw<S> {
269    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
270        write!(f, "raw[{}]", self.inner)
271    }
272}
273
274/// Options builder for opening a raw image.
275pub struct RawOpenBuilder<S: Storage + 'static>(FormatDriverBuilderBase<S>);
276
277impl<S: Storage + 'static> FormatDriverBuilder<S> for RawOpenBuilder<S> {
278    type Format = Raw<S>;
279    const FORMAT: Format = Format::Raw;
280
281    fn new(image: S) -> Self {
282        RawOpenBuilder(FormatDriverBuilderBase::new(image))
283    }
284
285    fn new_path<P: AsRef<Path>>(path: P) -> Self {
286        RawOpenBuilder(FormatDriverBuilderBase::new_path(path))
287    }
288
289    fn write(mut self, writable: bool) -> Self {
290        self.0.set_write(writable);
291        self
292    }
293
294    fn storage_open_options(mut self, options: StorageOpenOptions) -> Self {
295        self.0.set_storage_open_options(options);
296        self
297    }
298
299    async fn open<G: ImplicitOpenGate<S>>(self, mut gate: G) -> io::Result<Self::Format> {
300        let writable = self.0.get_writable();
301        let file = self.0.open_image(&mut gate).await?;
302        Raw::open_image(file, writable).await
303    }
304
305    fn get_image_path(&self) -> Option<PathBuf> {
306        self.0.get_image_path()
307    }
308
309    fn get_writable(&self) -> bool {
310        self.0.get_writable()
311    }
312
313    fn get_storage_open_options(&self) -> Option<&StorageOpenOptions> {
314        self.0.get_storage_opts()
315    }
316}
317
318/// Creation builder for a new raw image.
319pub struct RawCreateBuilder<S: Storage + 'static>(FormatCreateBuilderBase<S>);
320
321impl<S: Storage + 'static> FormatCreateBuilder<S> for RawCreateBuilder<S> {
322    const FORMAT: Format = Format::Raw;
323    type DriverBuilder = RawOpenBuilder<S>;
324
325    fn new(image: S) -> Self {
326        RawCreateBuilder(FormatCreateBuilderBase::new(image))
327    }
328
329    fn size(mut self, size: u64) -> Self {
330        self.0.set_size(size);
331        self
332    }
333
334    fn preallocate(mut self, prealloc_mode: PreallocateMode) -> Self {
335        self.0.set_preallocate(prealloc_mode);
336        self
337    }
338
339    fn get_size(&self) -> u64 {
340        self.0.get_size()
341    }
342
343    fn get_preallocate(&self) -> PreallocateMode {
344        self.0.get_preallocate()
345    }
346
347    async fn create(self) -> io::Result<()> {
348        self.create_open(DenyImplicitOpenGate::default(), |image| {
349            Ok(Raw::builder(image))
350        })
351        .await?;
352        Ok(())
353    }
354
355    async fn create_open<
356        G: ImplicitOpenGate<S>,
357        F: FnOnce(S) -> io::Result<Self::DriverBuilder>,
358    >(
359        self,
360        open_gate: G,
361        open_builder_fn: F,
362    ) -> io::Result<Raw<S>> {
363        let size = self.0.get_size();
364        let prealloc = self.0.get_preallocate();
365        let image = self.0.get_image();
366
367        // Clear of data (and allow for full preallocation, if requested)
368        if image.size()? > 0 {
369            image.resize(size, storage::PreallocateMode::None).await?;
370        }
371
372        let img = open_builder_fn(image)?.write(true).open(open_gate).await?;
373        if size > 0 {
374            img.resize_grow(size, prealloc).await?;
375        }
376
377        Ok(img)
378    }
379}