storage_device/
ranged_device.rs

1// Copyright 2025 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use crate::buffer::{BufferRef, MutableBufferRef};
6use crate::buffer_allocator::BufferFuture;
7use crate::{Device, ReadOptions, WriteOptions};
8use anyhow::{Error, anyhow, bail, ensure};
9use async_trait::async_trait;
10use std::ops::Range;
11use std::sync::Arc;
12
13/// Wrapper around a Device where we can only access a region within it.
14pub struct RangedDevice {
15    // The underlying device.
16    source: Arc<dyn Device>,
17    // Range (in bytes) of the accessible region in `source`.
18    range: Range<u64>,
19}
20
21impl RangedDevice {
22    pub fn new(source: Arc<dyn Device>, start_block: u64, num_blocks: u64) -> Result<Self, Error> {
23        ensure!(
24            start_block + num_blocks <= source.block_count(),
25            "failed to create RangedDevice (out of range)"
26        );
27        ensure!(num_blocks > 0, "failed to create RangedDevice (no size)");
28        let start = start_block
29            .checked_mul(source.block_size() as u64)
30            .ok_or_else(|| anyhow!("arithmetic overflow calculating ranged start"))?;
31        let end = (start_block + num_blocks)
32            .checked_mul(source.block_size() as u64)
33            .ok_or_else(|| anyhow!("arithmetic overflow calculating ranged end"))?;
34
35        Ok(Self { source: source.clone(), range: (start..end) })
36    }
37
38    fn num_blocks(&self) -> u64 {
39        (self.range.end - self.range.start) / self.block_size() as u64
40    }
41}
42
43#[async_trait]
44impl Device for RangedDevice {
45    fn allocate_buffer(&self, size: usize) -> BufferFuture<'_> {
46        self.source.allocate_buffer(size)
47    }
48
49    fn block_size(&self) -> u32 {
50        self.source.block_size()
51    }
52
53    fn block_count(&self) -> u64 {
54        self.num_blocks()
55    }
56
57    async fn read_with_opts(
58        &self,
59        offset: u64,
60        buffer: MutableBufferRef<'_>,
61        _read_opts: ReadOptions,
62    ) -> Result<(), Error> {
63        let adjusted_offset = self
64            .range
65            .start
66            .checked_add(offset)
67            .ok_or_else(|| anyhow!("arithmetic overflow calculating offset"))?;
68        ensure!(
69            adjusted_offset + buffer.len() as u64 <= self.range.end,
70            "reading past end of device"
71        );
72        self.source.read(adjusted_offset, buffer).await
73    }
74
75    async fn write_with_opts(
76        &self,
77        offset: u64,
78        buffer: BufferRef<'_>,
79        opts: WriteOptions,
80    ) -> Result<(), Error> {
81        let adjusted_offset = self
82            .range
83            .start
84            .checked_add(offset)
85            .ok_or_else(|| anyhow!("arithmetic overflow calculating offset"))?;
86        ensure!(
87            adjusted_offset + buffer.len() as u64 <= self.range.end,
88            "writing past end of device"
89        );
90        self.source.write_with_opts(adjusted_offset, buffer, opts).await
91    }
92
93    async fn trim(&self, _range: Range<u64>) -> Result<(), Error> {
94        bail!("RangedDevice does not support trim");
95    }
96
97    async fn close(&self) -> Result<(), Error> {
98        self.source.close().await
99    }
100
101    async fn flush(&self) -> Result<(), Error> {
102        self.source.flush().await
103    }
104
105    fn barrier(&self) {
106        self.source.barrier()
107    }
108
109    fn is_read_only(&self) -> bool {
110        self.source.is_read_only()
111    }
112
113    fn supports_trim(&self) -> bool {
114        false
115    }
116
117    fn reopen(&self, read_only: bool) {
118        self.source.reopen(read_only)
119    }
120}
121
122#[cfg(test)]
123mod tests {
124    use super::RangedDevice;
125    use crate::Device;
126    use crate::fake_device::FakeDevice;
127    use std::sync::Arc;
128
129    #[fuchsia::test]
130    async fn test_ranged_device_reads() {
131        const BLOCK_SIZE: usize = 512;
132        let device = Arc::new(FakeDevice::new(8, BLOCK_SIZE as u32));
133
134        let mut buffer = device.allocate_buffer(BLOCK_SIZE).await;
135        buffer.as_mut_slice().copy_from_slice(&[1; 512]);
136        device.write(BLOCK_SIZE as u64, buffer.as_ref()).await.expect("failed to write to device");
137
138        buffer.as_mut_slice().copy_from_slice(&[2; 512]);
139        device
140            .write(2 * BLOCK_SIZE as u64, buffer.as_ref())
141            .await
142            .expect("failed to write to device");
143
144        // Create a RangedDevice starting from block offset one, for three blocks.
145        let sub_device =
146            RangedDevice::new(device.clone(), 1, 3).expect("failed to create new RangedDevice");
147
148        // Test reading from RangedDevice
149        let mut ranged_device_buffer = sub_device.allocate_buffer(BLOCK_SIZE).await;
150        sub_device
151            .read(0, ranged_device_buffer.as_mut())
152            .await
153            .expect("failed to read from RangedDevice");
154        assert_eq!(ranged_device_buffer.as_slice(), [1; 512]);
155
156        sub_device
157            .read(BLOCK_SIZE as u64, ranged_device_buffer.as_mut())
158            .await
159            .expect("failed to read from RangedDevice");
160        assert_eq!(ranged_device_buffer.as_slice(), [2; 512]);
161
162        sub_device
163            .read(2 * BLOCK_SIZE as u64, ranged_device_buffer.as_mut())
164            .await
165            .expect("failed to read from RangedDevice");
166        assert_eq!(ranged_device_buffer.as_slice(), [0; 512]);
167
168        sub_device
169            .read(3 * BLOCK_SIZE as u64, ranged_device_buffer.as_mut())
170            .await
171            .expect_err("unexepectedly passed reading out of range of RangedDevice");
172    }
173
174    #[fuchsia::test]
175    async fn test_ranged_device_writes() {
176        const BLOCK_SIZE: usize = 512;
177        let device = Arc::new(FakeDevice::new(8, BLOCK_SIZE as u32));
178
179        // Create a RangedDevice starting from block offset one, for three blocks.
180        let block_offset = 1;
181        let sub_device = RangedDevice::new(device.clone(), block_offset, 3)
182            .expect("failed to create new RangedDevice");
183
184        let mut invalid_buffer = sub_device.allocate_buffer(4 * BLOCK_SIZE).await;
185        invalid_buffer.as_mut_slice().copy_from_slice(&[3; 2048]);
186        sub_device
187            .write(0, invalid_buffer.as_ref())
188            .await
189            .expect_err("unexpectedly passed writing a buffer that is too big");
190
191        let mut write_buffer = sub_device.allocate_buffer(BLOCK_SIZE).await;
192        write_buffer.as_mut_slice().copy_from_slice(&[3; 512]);
193        let write_block_offset = 2;
194        sub_device
195            .write(write_block_offset * BLOCK_SIZE as u64, write_buffer.as_ref())
196            .await
197            .expect("failed to write to RangedDevice");
198
199        // Verify write on underlying device.
200        let mut read_buffer = device.allocate_buffer(BLOCK_SIZE).await;
201        device
202            .read((block_offset + write_block_offset) * BLOCK_SIZE as u64, read_buffer.as_mut())
203            .await
204            .expect("failed to read from device");
205        assert_eq!(read_buffer.as_slice(), [3; 512]);
206    }
207}