Skip to main content

storage_device/
block_device.rs

1// Copyright 2021 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::Device;
6use crate::buffer::{BufferFuture, BufferRef, MutableBufferRef};
7use crate::buffer_allocator::{BufferAllocator, BufferSource};
8use anyhow::{Error, bail, ensure};
9use async_trait::async_trait;
10use block_client::{
11    BlockClient, BlockDeviceFlag, BufferSlice, MutableBufferSlice, ReadOptions, VmoId, WriteOptions,
12};
13use std::ops::Range;
14use zx::Status;
15
16/// BlockDevice is an implementation of Device backed by a real block device behind a FIFO.
17pub struct BlockDevice<T> {
18    allocator: BufferAllocator,
19    remote: T,
20    read_only: bool,
21    vmoid: VmoId,
22}
23
24const TRANSFER_VMO_SIZE: usize = 128 * 1024 * 1024;
25
26impl<T: BlockClient> BlockDevice<T> {
27    /// Creates a new BlockDevice over `remote`.
28    pub async fn new(remote: T, read_only: bool) -> Result<Self, Error> {
29        let buffer_source = BufferSource::new(TRANSFER_VMO_SIZE);
30        // SAFETY: We only attach this VMO once here, and we ensure no references are held
31        // during I/O via the BufferAllocator and pointer-based BufferRef types.
32        let vmoid = unsafe { remote.attach_vmo(buffer_source.vmo()) }.await?;
33        let allocator = BufferAllocator::new(remote.block_size() as usize, buffer_source);
34        Ok(Self { allocator, remote, read_only, vmoid })
35    }
36}
37
38#[async_trait]
39impl<T: BlockClient> Device for BlockDevice<T> {
40    fn allocate_buffer(&self, size: usize) -> BufferFuture<'_> {
41        self.allocator.allocate_buffer(size)
42    }
43
44    fn clean_transfer_buffer(&self) {
45        self.allocator.clean_transfer_buffer();
46    }
47
48    fn block_size(&self) -> u32 {
49        self.remote.block_size()
50    }
51
52    fn block_count(&self) -> u64 {
53        self.remote.block_count()
54    }
55
56    async fn read_with_opts(
57        &self,
58        offset: u64,
59        buffer: MutableBufferRef<'_>,
60        read_opts: ReadOptions,
61    ) -> Result<(), Error> {
62        if buffer.len() == 0 {
63            return Ok(());
64        }
65        ensure!(self.vmoid.is_valid(), Status::INVALID_ARGS);
66        ensure!(offset % (self.block_size() as u64) == 0, Status::INVALID_ARGS);
67        ensure!(buffer.range().start % (self.block_size() as usize) == 0, Status::INVALID_ARGS);
68        ensure!(buffer.range().end % (self.block_size() as usize) == 0, Status::INVALID_ARGS);
69        Ok(self
70            .remote
71            .read_at_with_opts(
72                MutableBufferSlice::new_with_vmo_id(
73                    &self.vmoid,
74                    buffer.range().start as u64,
75                    buffer.len() as u64,
76                ),
77                offset,
78                read_opts,
79            )
80            .await?)
81    }
82
83    async fn write_with_opts(
84        &self,
85        offset: u64,
86        buffer: BufferRef<'_>,
87        opts: WriteOptions,
88    ) -> Result<(), Error> {
89        if self.read_only {
90            bail!(Status::ACCESS_DENIED);
91        }
92        if buffer.len() == 0 {
93            return Ok(());
94        }
95        ensure!(self.vmoid.is_valid(), "Device is closed");
96        ensure!(offset % (self.block_size() as u64) == 0, Status::INVALID_ARGS);
97        ensure!(buffer.range().start % (self.block_size() as usize) == 0, Status::INVALID_ARGS);
98        ensure!(buffer.range().end % (self.block_size() as usize) == 0, Status::INVALID_ARGS);
99        Ok(self
100            .remote
101            .write_at_with_opts(
102                BufferSlice::new_with_vmo_id(
103                    &self.vmoid,
104                    buffer.range().start as u64,
105                    buffer.len() as u64,
106                ),
107                offset,
108                opts,
109            )
110            .await?)
111    }
112
113    async fn trim(&self, range: Range<u64>) -> Result<(), Error> {
114        if self.read_only {
115            bail!(Status::ACCESS_DENIED);
116        }
117        ensure!(range.start % (self.block_size() as u64) == 0, Status::INVALID_ARGS);
118        ensure!(range.end % (self.block_size() as u64) == 0, Status::INVALID_ARGS);
119        Ok(self.remote.trim(range).await?)
120    }
121
122    async fn close(&self) -> Result<(), Error> {
123        // We can leak the VMO id because we are closing the device.
124        let _ = self.vmoid.take().into_id();
125        Ok(self.remote.close().await?)
126    }
127
128    async fn flush(&self) -> Result<(), Error> {
129        Ok(self.remote.flush().await?)
130    }
131
132    fn barrier(&self) {
133        self.remote.barrier()
134    }
135
136    fn is_read_only(&self) -> bool {
137        self.read_only
138    }
139
140    fn supports_trim(&self) -> bool {
141        self.remote.block_flags().contains(BlockDeviceFlag::TRIM_SUPPORT)
142    }
143}
144
145impl<T> Drop for BlockDevice<T> {
146    fn drop(&mut self) {
147        // We can't detach the VmoId because we're not async here, but we are tearing down the
148        // connection to the block device so we don't really need to.
149        let _ = self.vmoid.take().into_id();
150    }
151}
152
153#[cfg(test)]
154mod tests {
155    use crate::Device;
156    use crate::block_device::BlockDevice;
157    use fake_block_client::FakeBlockClient;
158    use zx::Status;
159
160    #[fuchsia::test]
161    async fn test_lifecycle() {
162        let device =
163            BlockDevice::new(FakeBlockClient::new(1024, 1024), false).await.expect("new failed");
164
165        {
166            let _buf = device.allocate_buffer(8192).await;
167        }
168
169        device.close().await.expect("Close failed");
170    }
171
172    #[fuchsia::test]
173    async fn test_read_write_buffer() {
174        let device =
175            BlockDevice::new(FakeBlockClient::new(1024, 1024), false).await.expect("new failed");
176
177        {
178            let mut buf1 = device.allocate_buffer(8192).await;
179            let mut buf2 = device.allocate_buffer(1024).await;
180            buf1.as_mut_slice().fill(0xaa as u8);
181            buf2.as_mut_slice().fill(0xbb as u8);
182            device.write(65536, buf1.as_ref()).await.expect("Write failed");
183            device.write(65536 + 8192, buf2.as_ref()).await.expect("Write failed");
184        }
185        {
186            let mut buf = device.allocate_buffer(8192 + 1024).await;
187            device.read(65536, buf.as_mut()).await.expect("Read failed");
188            assert_eq!(buf.as_slice()[..8192], vec![0xaa as u8; 8192]);
189            assert_eq!(buf.as_slice()[8192..], vec![0xbb as u8; 1024]);
190        }
191
192        device.close().await.expect("Close failed");
193    }
194
195    #[fuchsia::test]
196    async fn test_read_only() {
197        let device =
198            BlockDevice::new(FakeBlockClient::new(1024, 1024), true).await.expect("new failed");
199        let mut buf1 = device.allocate_buffer(8192).await;
200        buf1.as_mut_slice().fill(0xaa as u8);
201        let err = device.write(65536, buf1.as_ref()).await.expect_err("Write succeeded");
202        assert_eq!(err.root_cause().downcast_ref::<Status>().unwrap(), &Status::ACCESS_DENIED);
203    }
204
205    #[fuchsia::test]
206    async fn test_unaligned_access() {
207        let device =
208            BlockDevice::new(FakeBlockClient::new(1024, 1024), false).await.expect("new failed");
209        let mut buf1 = device.allocate_buffer(device.block_size() as usize * 2).await;
210        buf1.as_mut_slice().fill(0xaa as u8);
211
212        // Write checks
213        {
214            let err = device.write(1, buf1.as_ref()).await.expect_err("Write succeeded");
215            assert_eq!(err.root_cause().downcast_ref::<Status>().unwrap(), &Status::INVALID_ARGS);
216        }
217        {
218            let err = device
219                .write(0, buf1.subslice(1..(device.block_size() as usize + 1)))
220                .await
221                .expect_err("Write succeeded");
222            assert_eq!(err.root_cause().downcast_ref::<Status>().unwrap(), &Status::INVALID_ARGS);
223        }
224        {
225            let err = device
226                .write(0, buf1.subslice(1..device.block_size() as usize))
227                .await
228                .expect_err("Write succeeded");
229            assert_eq!(err.root_cause().downcast_ref::<Status>().unwrap(), &Status::INVALID_ARGS);
230        }
231        {
232            let err = device
233                .write(0, buf1.subslice(0..(device.block_size() as usize + 1)))
234                .await
235                .expect_err("Write succeeded");
236            assert_eq!(err.root_cause().downcast_ref::<Status>().unwrap(), &Status::INVALID_ARGS);
237        }
238
239        // Read checks
240        {
241            let err = device.read(1, buf1.as_mut()).await.expect_err("Read succeeded");
242            assert_eq!(err.root_cause().downcast_ref::<Status>().unwrap(), &Status::INVALID_ARGS);
243        }
244        {
245            let err = device
246                .read(0, buf1.subslice_mut(1..(device.block_size() as usize + 1)))
247                .await
248                .expect_err("Read succeeded");
249            assert_eq!(err.root_cause().downcast_ref::<Status>().unwrap(), &Status::INVALID_ARGS);
250        }
251        {
252            let err = device
253                .read(0, buf1.subslice_mut(1..device.block_size() as usize))
254                .await
255                .expect_err("Read succeeded");
256            assert_eq!(err.root_cause().downcast_ref::<Status>().unwrap(), &Status::INVALID_ARGS);
257        }
258        {
259            let err = device
260                .read(0, buf1.subslice_mut(0..(device.block_size() as usize + 1)))
261                .await
262                .expect_err("Read succeeded");
263            assert_eq!(err.root_cause().downcast_ref::<Status>().unwrap(), &Status::INVALID_ARGS);
264        }
265
266        // Trim
267        {
268            let err = device.trim(1..device.block_size() as u64).await.expect_err("Read succeeded");
269            assert_eq!(err.root_cause().downcast_ref::<Status>().unwrap(), &Status::INVALID_ARGS);
270        }
271        {
272            let err =
273                device.trim(1..(device.block_size() as u64 + 1)).await.expect_err("Read succeeded");
274            assert_eq!(err.root_cause().downcast_ref::<Status>().unwrap(), &Status::INVALID_ARGS);
275        }
276        {
277            let err =
278                device.trim(0..(device.block_size() as u64 + 1)).await.expect_err("Read succeeded");
279            assert_eq!(err.root_cause().downcast_ref::<Status>().unwrap(), &Status::INVALID_ARGS);
280        }
281    }
282}