Skip to main content

fs_core/
slice.rs

1//! Slice adapters — view a byte sub-range of any `BlockRead` as its own
2//! device. Useful any time you want to feed a fragment of a larger
3//! device to a consumer that expects a whole block source — partition
4//! probes, image-file extents, mmap-style views, fuzzer harnesses.
5//!
6//! Two variants:
7//!
8//! - [`SliceReader`] borrows the parent, lifetime-tied. Cheaper when the
9//!   parent outlives the slice and you can express that statically.
10//! - [`OwnedSlice`] holds an `Arc` to the parent. Use when the parent's
11//!   lifetime can't be expressed in a borrow (FFI handles, slice handed
12//!   across thread boundaries, etc.).
13//!
14//! Both treat the slice as **read-only** — reads inside the range are
15//! forwarded to the parent, reads outside return [`Error::ShortRead`].
16//! The default `Err(ReadOnly)` write path from [`BlockDevice`] applies.
17
18use crate::block::{BlockDevice, BlockRead};
19use crate::error::{Error, Result};
20use std::sync::Arc;
21
22/// Borrowed slice of a parent `BlockRead`.
23///
24/// `read_at(0, …)` reads `start` of the parent. Reads past `length`
25/// return [`Error::ShortRead`].
26pub struct SliceReader<'a> {
27    parent: &'a (dyn BlockRead + 'a),
28    start: u64,
29    length: u64,
30}
31
32impl<'a> SliceReader<'a> {
33    pub fn new(parent: &'a (dyn BlockRead + 'a), start: u64, length: u64) -> Self {
34        Self {
35            parent,
36            start,
37            length,
38        }
39    }
40
41    /// Byte offset of this slice on the parent device.
42    pub fn start(&self) -> u64 {
43        self.start
44    }
45
46    /// Length of this slice in bytes (== `size_bytes()`).
47    pub fn length(&self) -> u64 {
48        self.length
49    }
50}
51
52impl<'a> BlockRead for SliceReader<'a> {
53    fn read_at(&self, offset: u64, buf: &mut [u8]) -> Result<()> {
54        let want = buf.len() as u64;
55        if offset
56            .checked_add(want)
57            .map(|e| e > self.length)
58            .unwrap_or(true)
59        {
60            return Err(Error::ShortRead {
61                offset,
62                want: buf.len(),
63                got: 0,
64            });
65        }
66        self.parent.read_at(self.start + offset, buf)
67    }
68
69    fn size_bytes(&self) -> u64 {
70        self.length
71    }
72}
73
74/// Slices are read-only by default — even where the parent is writable,
75/// slicing is almost always paired with a read-only inspection or
76/// dispatch workflow.
77impl<'a> BlockDevice for SliceReader<'a> {}
78
79/// Owned slice over an `Arc<dyn BlockRead>`. Use when the parent's
80/// lifetime can't be expressed in a borrow — e.g. when the slice is
81/// handed across an FFI boundary or stored in a long-lived struct.
82pub struct OwnedSlice {
83    parent: Arc<dyn BlockRead>,
84    start: u64,
85    length: u64,
86}
87
88impl OwnedSlice {
89    pub fn new(parent: Arc<dyn BlockRead>, start: u64, length: u64) -> Self {
90        Self {
91            parent,
92            start,
93            length,
94        }
95    }
96
97    pub fn start(&self) -> u64 {
98        self.start
99    }
100
101    pub fn length(&self) -> u64 {
102        self.length
103    }
104}
105
106impl BlockRead for OwnedSlice {
107    fn read_at(&self, offset: u64, buf: &mut [u8]) -> Result<()> {
108        let want = buf.len() as u64;
109        if offset
110            .checked_add(want)
111            .map(|e| e > self.length)
112            .unwrap_or(true)
113        {
114            return Err(Error::ShortRead {
115                offset,
116                want: buf.len(),
117                got: 0,
118            });
119        }
120        self.parent.read_at(self.start + offset, buf)
121    }
122
123    fn size_bytes(&self) -> u64 {
124        self.length
125    }
126}
127
128/// Same rationale as `SliceReader`: read-only by default.
129impl BlockDevice for OwnedSlice {}
130
131/// Owned, read-WRITE slice over an `Arc<dyn BlockDevice>`. Use when the
132/// parent is writable and the slice should propagate writes (e.g. an
133/// individual partition handed to a filesystem driver). Reads + writes
134/// outside `[0, length)` return [`Error::ShortRead`] / [`Error::OutOfBounds`].
135pub struct OwnedRwSlice {
136    parent: Arc<dyn BlockDevice>,
137    start: u64,
138    length: u64,
139}
140
141impl OwnedRwSlice {
142    pub fn new(parent: Arc<dyn BlockDevice>, start: u64, length: u64) -> Self {
143        Self {
144            parent,
145            start,
146            length,
147        }
148    }
149
150    pub fn start(&self) -> u64 {
151        self.start
152    }
153
154    pub fn length(&self) -> u64 {
155        self.length
156    }
157}
158
159impl BlockRead for OwnedRwSlice {
160    fn read_at(&self, offset: u64, buf: &mut [u8]) -> Result<()> {
161        let want = buf.len() as u64;
162        if offset
163            .checked_add(want)
164            .map(|e| e > self.length)
165            .unwrap_or(true)
166        {
167            return Err(Error::ShortRead {
168                offset,
169                want: buf.len(),
170                got: 0,
171            });
172        }
173        self.parent.read_at(self.start + offset, buf)
174    }
175
176    fn size_bytes(&self) -> u64 {
177        self.length
178    }
179}
180
181impl BlockDevice for OwnedRwSlice {
182    fn write_at(&self, offset: u64, buf: &[u8]) -> Result<()> {
183        let want = buf.len() as u64;
184        if offset
185            .checked_add(want)
186            .map(|e| e > self.length)
187            .unwrap_or(true)
188        {
189            return Err(Error::OutOfBounds {
190                offset,
191                len: want,
192                size: self.length,
193            });
194        }
195        if !self.parent.is_writable() {
196            return Err(Error::ReadOnly);
197        }
198        self.parent.write_at(self.start + offset, buf)
199    }
200
201    fn flush(&self) -> Result<()> {
202        self.parent.flush()
203    }
204
205    fn is_writable(&self) -> bool {
206        self.parent.is_writable()
207    }
208}
209
210#[cfg(test)]
211mod tests {
212    use super::*;
213    use std::sync::Mutex;
214
215    struct Bytes(Mutex<Vec<u8>>);
216    impl BlockRead for Bytes {
217        fn read_at(&self, offset: u64, buf: &mut [u8]) -> Result<()> {
218            let b = self.0.lock().unwrap();
219            let start = offset as usize;
220            let end = start + buf.len();
221            if end > b.len() {
222                return Err(Error::ShortRead {
223                    offset,
224                    want: buf.len(),
225                    got: b.len().saturating_sub(start),
226                });
227            }
228            buf.copy_from_slice(&b[start..end]);
229            Ok(())
230        }
231        fn size_bytes(&self) -> u64 {
232            self.0.lock().unwrap().len() as u64
233        }
234    }
235
236    #[test]
237    fn slice_reader_rebases_offsets() {
238        let mut v = vec![0u8; 4096];
239        v[2000..2004].copy_from_slice(&[0xAB, 0xCD, 0xEF, 0x01]);
240        let dev = Bytes(Mutex::new(v));
241
242        let slice = SliceReader::new(&dev, 2000, 4);
243        assert_eq!(slice.size_bytes(), 4);
244        assert_eq!(slice.start(), 2000);
245        assert_eq!(slice.length(), 4);
246
247        let mut buf = [0u8; 4];
248        slice.read_at(0, &mut buf).unwrap();
249        assert_eq!(buf, [0xAB, 0xCD, 0xEF, 0x01]);
250    }
251
252    #[test]
253    fn slice_reader_rejects_out_of_bounds() {
254        let dev = Bytes(Mutex::new(vec![0u8; 4096]));
255        let slice = SliceReader::new(&dev, 0, 16);
256        let mut buf = [0u8; 8];
257        match slice.read_at(12, &mut buf) {
258            Err(Error::ShortRead { .. }) => {}
259            other => panic!("expected ShortRead, got {other:?}"),
260        }
261    }
262
263    #[test]
264    fn owned_slice_works_through_arc() {
265        let mut v = vec![0u8; 4096];
266        v[100..104].copy_from_slice(&[0x11, 0x22, 0x33, 0x44]);
267        let dev: Arc<dyn BlockRead> = Arc::new(Bytes(Mutex::new(v)));
268
269        let slice = OwnedSlice::new(dev, 100, 4);
270        assert_eq!(slice.size_bytes(), 4);
271        let mut buf = [0u8; 4];
272        slice.read_at(0, &mut buf).unwrap();
273        assert_eq!(buf, [0x11, 0x22, 0x33, 0x44]);
274    }
275
276    #[test]
277    fn slices_reject_writes_via_blockdevice_default() {
278        let dev = Bytes(Mutex::new(vec![0u8; 16]));
279        let slice = SliceReader::new(&dev, 0, 8);
280        let err = BlockDevice::write_at(&slice, 0, &[1u8; 4]).unwrap_err();
281        assert!(matches!(err, Error::ReadOnly));
282    }
283
284    #[test]
285    fn owned_slice_accessors_report_geometry() {
286        let dev: Arc<dyn BlockRead> = Arc::new(Bytes(Mutex::new(vec![0u8; 4096])));
287        let slice = OwnedSlice::new(dev, 512, 256);
288        assert_eq!(slice.start(), 512);
289        assert_eq!(slice.length(), 256);
290        assert_eq!(slice.size_bytes(), 256);
291    }
292
293    /// Writable in-memory device for exercising `OwnedRwSlice`.
294    struct RwBytes(Mutex<Vec<u8>>);
295    impl BlockRead for RwBytes {
296        fn read_at(&self, offset: u64, buf: &mut [u8]) -> Result<()> {
297            let b = self.0.lock().unwrap();
298            let start = offset as usize;
299            let end = start + buf.len();
300            if end > b.len() {
301                return Err(Error::ShortRead {
302                    offset,
303                    want: buf.len(),
304                    got: b.len().saturating_sub(start),
305                });
306            }
307            buf.copy_from_slice(&b[start..end]);
308            Ok(())
309        }
310        fn size_bytes(&self) -> u64 {
311            self.0.lock().unwrap().len() as u64
312        }
313    }
314    impl BlockDevice for RwBytes {
315        fn write_at(&self, offset: u64, buf: &[u8]) -> Result<()> {
316            let mut b = self.0.lock().unwrap();
317            let s = offset as usize;
318            b[s..s + buf.len()].copy_from_slice(buf);
319            Ok(())
320        }
321        fn is_writable(&self) -> bool {
322            true
323        }
324    }
325
326    #[test]
327    fn owned_rw_slice_accessors_report_geometry() {
328        let dev: Arc<dyn BlockDevice> = Arc::new(RwBytes(Mutex::new(vec![0u8; 64])));
329        let slice = OwnedRwSlice::new(dev, 16, 32);
330        assert_eq!(slice.start(), 16);
331        assert_eq!(slice.length(), 32);
332        assert_eq!(slice.size_bytes(), 32);
333        assert!(slice.is_writable());
334    }
335
336    #[test]
337    fn owned_rw_slice_rebases_reads_and_writes() {
338        let dev: Arc<dyn BlockDevice> = Arc::new(RwBytes(Mutex::new(vec![0u8; 64])));
339        let slice = OwnedRwSlice::new(dev.clone(), 16, 32);
340
341        // Write through the slice lands at parent offset 16.
342        slice.write_at(0, &[0xDE, 0xAD, 0xBE, 0xEF]).unwrap();
343        let mut buf = [0u8; 4];
344        slice.read_at(0, &mut buf).unwrap();
345        assert_eq!(buf, [0xDE, 0xAD, 0xBE, 0xEF]);
346
347        // Confirm rebasing against the parent directly.
348        let mut pbuf = [0u8; 4];
349        dev.read_at(16, &mut pbuf).unwrap();
350        assert_eq!(pbuf, [0xDE, 0xAD, 0xBE, 0xEF]);
351    }
352
353    #[test]
354    fn owned_rw_slice_rejects_out_of_bounds_write() {
355        let dev: Arc<dyn BlockDevice> = Arc::new(RwBytes(Mutex::new(vec![0u8; 64])));
356        let slice = OwnedRwSlice::new(dev, 0, 8);
357        match slice.write_at(6, &[0u8; 4]) {
358            Err(Error::OutOfBounds { .. }) => {}
359            other => panic!("expected OutOfBounds, got {other:?}"),
360        }
361    }
362
363    #[test]
364    fn owned_rw_slice_flush_delegates_to_parent() {
365        let dev: Arc<dyn BlockDevice> = Arc::new(RwBytes(Mutex::new(vec![0u8; 8])));
366        let slice = OwnedRwSlice::new(dev, 0, 8);
367        // Default `flush` on RwBytes is a no-op success; the slice forwards it.
368        slice.flush().unwrap();
369    }
370}