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    async fn read_with_opts_internal(
38        &self,
39        offset: u64,
40        buffer: MutableBufferRef<'_>,
41        read_opts: ReadOptions,
42    ) -> Result<(), Error> {
43        if buffer.len() == 0 {
44            return Ok(());
45        }
46        ensure!(self.vmoid.is_valid(), Status::INVALID_ARGS);
47        ensure!(offset % (self.block_size() as u64) == 0, Status::INVALID_ARGS);
48        ensure!(buffer.range().start % (self.block_size() as usize) == 0, Status::INVALID_ARGS);
49        ensure!(buffer.range().end % (self.block_size() as usize) == 0, Status::INVALID_ARGS);
50        Ok(self
51            .remote
52            .read_at_with_opts(
53                MutableBufferSlice::new_with_vmo_id(
54                    &self.vmoid,
55                    buffer.range().start as u64,
56                    buffer.len() as u64,
57                ),
58                offset,
59                read_opts,
60            )
61            .await?)
62    }
63
64    async fn write_with_opts_internal(
65        &self,
66        offset: u64,
67        buffer: BufferRef<'_>,
68        opts: WriteOptions,
69    ) -> Result<(), Error> {
70        if self.read_only {
71            bail!(Status::ACCESS_DENIED);
72        }
73        if buffer.len() == 0 {
74            return Ok(());
75        }
76        ensure!(self.vmoid.is_valid(), "Device is closed");
77        ensure!(offset % (self.block_size() as u64) == 0, Status::INVALID_ARGS);
78        ensure!(buffer.range().start % (self.block_size() as usize) == 0, Status::INVALID_ARGS);
79        ensure!(buffer.range().end % (self.block_size() as usize) == 0, Status::INVALID_ARGS);
80        Ok(self
81            .remote
82            .write_at_with_opts(
83                BufferSlice::new_with_vmo_id(
84                    &self.vmoid,
85                    buffer.range().start as u64,
86                    buffer.len() as u64,
87                ),
88                offset,
89                opts,
90            )
91            .await?)
92    }
93}
94
95#[async_trait]
96impl<T: BlockClient> Device for BlockDevice<T> {
97    fn allocate_buffer(&self, size: usize) -> BufferFuture<'_> {
98        self.allocator.allocate_buffer(size)
99    }
100
101    fn clean_transfer_buffer(&self) {
102        self.allocator.clean_transfer_buffer();
103    }
104
105    fn block_size(&self) -> u32 {
106        self.remote.block_size()
107    }
108
109    fn block_count(&self) -> u64 {
110        self.remote.block_count()
111    }
112
113    async fn read_with_opts(
114        &self,
115        offset: u64,
116        mut buffer: MutableBufferRef<'_>,
117        read_opts: ReadOptions,
118    ) -> Result<(), Error> {
119        if buffer.allocator_id() != self.allocator.identifier() {
120            // Foreign buffer (from another allocator). Force copy!
121            let mut temp_buf = self.allocator.allocate_buffer(buffer.len()).await;
122            self.read_with_opts_internal(offset, temp_buf.as_mut(), read_opts).await?;
123            buffer.as_mut_ptr_slice().copy_from_ptr_slice(temp_buf.as_ptr_slice());
124            Ok(())
125        } else {
126            self.read_with_opts_internal(offset, buffer, read_opts).await
127        }
128    }
129
130    async fn write_with_opts(
131        &self,
132        offset: u64,
133        buffer: BufferRef<'_>,
134        opts: WriteOptions,
135    ) -> Result<(), Error> {
136        if buffer.allocator_id() != self.allocator.identifier() {
137            // Foreign buffer (from another allocator). Force copy!
138            let mut temp_buf = self.allocator.allocate_buffer(buffer.len()).await;
139            temp_buf.as_mut().as_mut_ptr_slice().copy_from_ptr_slice(buffer.as_ptr_slice());
140            self.write_with_opts_internal(offset, temp_buf.as_ref(), opts).await
141        } else {
142            self.write_with_opts_internal(offset, buffer, opts).await
143        }
144    }
145
146    async fn trim(&self, range: Range<u64>) -> Result<(), Error> {
147        if self.read_only {
148            bail!(Status::ACCESS_DENIED);
149        }
150        ensure!(range.start % (self.block_size() as u64) == 0, Status::INVALID_ARGS);
151        ensure!(range.end % (self.block_size() as u64) == 0, Status::INVALID_ARGS);
152        Ok(self.remote.trim(range).await?)
153    }
154
155    async fn close(&self) -> Result<(), Error> {
156        // We can leak the VMO id because we are closing the device.
157        let _ = self.vmoid.take().into_id();
158        Ok(self.remote.close().await?)
159    }
160
161    async fn flush(&self) -> Result<(), Error> {
162        Ok(self.remote.flush().await?)
163    }
164
165    fn barrier(&self) {
166        self.remote.barrier()
167    }
168
169    fn is_read_only(&self) -> bool {
170        self.read_only
171    }
172
173    fn supports_trim(&self) -> bool {
174        self.remote.block_flags().contains(BlockDeviceFlag::TRIM_SUPPORT)
175    }
176}
177
178impl<T> Drop for BlockDevice<T> {
179    fn drop(&mut self) {
180        // We can't detach the VmoId because we're not async here, but we are tearing down the
181        // connection to the block device so we don't really need to.
182        let _ = self.vmoid.take().into_id();
183    }
184}
185
186#[cfg(test)]
187mod tests {
188    use crate::Device;
189    use crate::block_device::BlockDevice;
190    use fake_block_client::FakeBlockClient;
191    use zx::Status;
192
193    #[fuchsia::test]
194    async fn test_lifecycle() {
195        let device =
196            BlockDevice::new(FakeBlockClient::new(1024, 1024), false).await.expect("new failed");
197
198        {
199            let _buf = device.allocate_buffer(8192).await;
200        }
201
202        device.close().await.expect("Close failed");
203    }
204
205    #[fuchsia::test]
206    async fn test_read_write_buffer() {
207        let device =
208            BlockDevice::new(FakeBlockClient::new(1024, 1024), false).await.expect("new failed");
209
210        {
211            let mut buf1 = device.allocate_buffer(8192).await;
212            let mut buf2 = device.allocate_buffer(1024).await;
213            buf1.fill(0xaa);
214            buf2.fill(0xbb);
215            device.write(65536, buf1.as_ref()).await.expect("Write failed");
216            device.write(65536 + 8192, buf2.as_ref()).await.expect("Write failed");
217        }
218        {
219            let mut buf = device.allocate_buffer(8192 + 1024).await;
220            device.read(65536, buf.as_mut()).await.expect("Read failed");
221            let mut data = vec![0u8; 8192 + 1024];
222            buf.copy_to_slice(&mut data);
223            assert_eq!(data[..8192], vec![0xaa as u8; 8192]);
224            assert_eq!(data[8192..], vec![0xbb as u8; 1024]);
225        }
226
227        device.close().await.expect("Close failed");
228    }
229
230    #[fuchsia::test]
231    async fn test_read_only() {
232        let device =
233            BlockDevice::new(FakeBlockClient::new(1024, 1024), true).await.expect("new failed");
234        let mut buf1 = device.allocate_buffer(8192).await;
235        buf1.fill(0xaa);
236        let err = device.write(65536, buf1.as_ref()).await.expect_err("Write succeeded");
237        assert_eq!(err.root_cause().downcast_ref::<Status>().unwrap(), &Status::ACCESS_DENIED);
238    }
239
240    #[fuchsia::test]
241    async fn test_unaligned_access() {
242        let device =
243            BlockDevice::new(FakeBlockClient::new(1024, 1024), false).await.expect("new failed");
244        let mut buf1 = device.allocate_buffer(device.block_size() as usize * 2).await;
245        buf1.fill(0xaa);
246
247        // Write checks
248        {
249            let err = device.write(1, buf1.as_ref()).await.expect_err("Write succeeded");
250            assert_eq!(err.root_cause().downcast_ref::<Status>().unwrap(), &Status::INVALID_ARGS);
251        }
252        {
253            let err = device
254                .write(0, buf1.subslice(1..(device.block_size() as usize + 1)))
255                .await
256                .expect_err("Write succeeded");
257            assert_eq!(err.root_cause().downcast_ref::<Status>().unwrap(), &Status::INVALID_ARGS);
258        }
259        {
260            let err = device
261                .write(0, buf1.subslice(1..device.block_size() as usize))
262                .await
263                .expect_err("Write succeeded");
264            assert_eq!(err.root_cause().downcast_ref::<Status>().unwrap(), &Status::INVALID_ARGS);
265        }
266        {
267            let err = device
268                .write(0, buf1.subslice(0..(device.block_size() as usize + 1)))
269                .await
270                .expect_err("Write succeeded");
271            assert_eq!(err.root_cause().downcast_ref::<Status>().unwrap(), &Status::INVALID_ARGS);
272        }
273
274        // Read checks
275        {
276            let err = device.read(1, buf1.as_mut()).await.expect_err("Read succeeded");
277            assert_eq!(err.root_cause().downcast_ref::<Status>().unwrap(), &Status::INVALID_ARGS);
278        }
279        {
280            let err = device
281                .read(0, buf1.subslice_mut(1..(device.block_size() as usize + 1)))
282                .await
283                .expect_err("Read succeeded");
284            assert_eq!(err.root_cause().downcast_ref::<Status>().unwrap(), &Status::INVALID_ARGS);
285        }
286        {
287            let err = device
288                .read(0, buf1.subslice_mut(1..device.block_size() as usize))
289                .await
290                .expect_err("Read succeeded");
291            assert_eq!(err.root_cause().downcast_ref::<Status>().unwrap(), &Status::INVALID_ARGS);
292        }
293        {
294            let err = device
295                .read(0, buf1.subslice_mut(0..(device.block_size() as usize + 1)))
296                .await
297                .expect_err("Read succeeded");
298            assert_eq!(err.root_cause().downcast_ref::<Status>().unwrap(), &Status::INVALID_ARGS);
299        }
300
301        // Trim
302        {
303            let err = device.trim(1..device.block_size() as u64).await.expect_err("Read succeeded");
304            assert_eq!(err.root_cause().downcast_ref::<Status>().unwrap(), &Status::INVALID_ARGS);
305        }
306        {
307            let err =
308                device.trim(1..(device.block_size() as u64 + 1)).await.expect_err("Read succeeded");
309            assert_eq!(err.root_cause().downcast_ref::<Status>().unwrap(), &Status::INVALID_ARGS);
310        }
311        {
312            let err =
313                device.trim(0..(device.block_size() as u64 + 1)).await.expect_err("Read succeeded");
314            assert_eq!(err.root_cause().downcast_ref::<Status>().unwrap(), &Status::INVALID_ARGS);
315        }
316    }
317
318    #[fuchsia::test]
319    async fn test_foreign_buffer_read_write() {
320        let device1 =
321            BlockDevice::new(FakeBlockClient::new(1024, 1024), false).await.expect("new failed");
322        let device2 =
323            BlockDevice::new(FakeBlockClient::new(1024, 1024), false).await.expect("new failed");
324
325        // Write data using a buffer allocated from device2 to device1 (foreign write)
326        let mut foreign_buf = device2.allocate_buffer(8192).await;
327        foreign_buf.fill(0xaa);
328        device1.write(0, foreign_buf.as_ref()).await.expect("Foreign write failed");
329
330        // Read data back using a buffer allocated from device2 from device1 (foreign read)
331        let mut foreign_read_buf = device2.allocate_buffer(8192).await;
332        device1.read(0, foreign_read_buf.as_mut()).await.expect("Foreign read failed");
333
334        let mut data = vec![0u8; 8192];
335        foreign_read_buf.copy_to_slice(&mut data);
336        assert_eq!(data, vec![0xaa; 8192]);
337    }
338}