Skip to main content

fs_core/
block.rs

1//! Block-device traits.
2//!
3//! Two layers because not every consumer needs writes:
4//!
5//! - [`BlockRead`] is the minimum: positioned reads + size. Disk-image
6//!   readers (qcow2 reading) and probes (partition table walk) only need
7//!   this much.
8//! - [`BlockDevice`] extends `BlockRead` with optional `write_at` / `flush`
9//!   / `is_writable`. Read-only devices that opt into the larger trait
10//!   inherit the default `Err(ReadOnly)` write path automatically.
11//!
12//! Both traits are `Send + Sync` so callers can hold them behind `Arc<dyn _>`
13//! across thread boundaries.
14
15use crate::error::{Error, Result};
16
17/// Read-only random-access block device.
18pub trait BlockRead: Send + Sync {
19    /// Read exactly `buf.len()` bytes starting at `offset` (bytes from the
20    /// start of the device).
21    fn read_at(&self, offset: u64, buf: &mut [u8]) -> Result<()>;
22
23    /// Total device size in bytes. Used for bounds checks.
24    fn size_bytes(&self) -> u64;
25}
26
27/// Read-write random-access block device. Implementors that genuinely
28/// support writes override `write_at` / `flush` / `is_writable`. The
29/// defaults model a strict read-only device.
30pub trait BlockDevice: BlockRead {
31    /// Write exactly `buf.len()` bytes at `offset`. Default: returns
32    /// [`Error::ReadOnly`].
33    fn write_at(&self, _offset: u64, _buf: &[u8]) -> Result<()> {
34        Err(Error::ReadOnly)
35    }
36
37    /// Flush pending writes to stable storage. No-op by default.
38    fn flush(&self) -> Result<()> {
39        Ok(())
40    }
41
42    /// Whether `write_at` is likely to succeed. Mount paths use this to
43    /// decide whether to attempt journal replay or stay strict-read-only.
44    fn is_writable(&self) -> bool {
45        false
46    }
47}
48
49// Forwarding impls so `Arc<T>` and `Box<T>` work transparently as
50// `&dyn BlockRead` / `&dyn BlockDevice`.
51
52impl<T: BlockRead + ?Sized> BlockRead for std::sync::Arc<T> {
53    fn read_at(&self, offset: u64, buf: &mut [u8]) -> Result<()> {
54        (**self).read_at(offset, buf)
55    }
56    fn size_bytes(&self) -> u64 {
57        (**self).size_bytes()
58    }
59}
60
61impl<T: BlockDevice + ?Sized> BlockDevice for std::sync::Arc<T> {
62    fn write_at(&self, offset: u64, buf: &[u8]) -> Result<()> {
63        (**self).write_at(offset, buf)
64    }
65    fn flush(&self) -> Result<()> {
66        (**self).flush()
67    }
68    fn is_writable(&self) -> bool {
69        (**self).is_writable()
70    }
71}
72
73impl<T: BlockRead + ?Sized> BlockRead for Box<T> {
74    fn read_at(&self, offset: u64, buf: &mut [u8]) -> Result<()> {
75        (**self).read_at(offset, buf)
76    }
77    fn size_bytes(&self) -> u64 {
78        (**self).size_bytes()
79    }
80}
81
82impl<T: BlockRead + ?Sized> BlockRead for &T {
83    fn read_at(&self, offset: u64, buf: &mut [u8]) -> Result<()> {
84        (**self).read_at(offset, buf)
85    }
86    fn size_bytes(&self) -> u64 {
87        (**self).size_bytes()
88    }
89}
90
91impl<T: BlockDevice + ?Sized> BlockDevice for Box<T> {
92    fn write_at(&self, offset: u64, buf: &[u8]) -> Result<()> {
93        (**self).write_at(offset, buf)
94    }
95    fn flush(&self) -> Result<()> {
96        (**self).flush()
97    }
98    fn is_writable(&self) -> bool {
99        (**self).is_writable()
100    }
101}