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: BlockDevice + ?Sized> BlockDevice for Box<T> {
83 fn write_at(&self, offset: u64, buf: &[u8]) -> Result<()> {
84 (**self).write_at(offset, buf)
85 }
86 fn flush(&self) -> Result<()> {
87 (**self).flush()
88 }
89 fn is_writable(&self) -> bool {
90 (**self).is_writable()
91 }
92}