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 is_read_only(&self) -> bool {
166        self.read_only
167    }
168
169    fn supports_trim(&self) -> bool {
170        self.remote.block_flags().contains(BlockDeviceFlag::TRIM_SUPPORT)
171    }
172
173    async fn connect_mapper(
174        &self,
175        server_end: fidl::endpoints::ServerEnd<fidl_fuchsia_storage_block::MapperMarker>,
176    ) -> Result<(), Status> {
177        self.remote.connect_mapper(server_end).await
178    }
179}
180
181impl<T> Drop for BlockDevice<T> {
182    fn drop(&mut self) {
183        // We can't detach the VmoId because we're not async here, but we are tearing down the
184        // connection to the block device so we don't really need to.
185        let _ = self.vmoid.take().into_id();
186    }
187}
188
189#[cfg(test)]
190mod tests {
191    use crate::Device;
192    use crate::block_device::BlockDevice;
193    use fake_block_client::FakeBlockClient;
194    use zx::Status;
195
196    #[fuchsia::test]
197    async fn test_lifecycle() {
198        let device =
199            BlockDevice::new(FakeBlockClient::new(1024, 1024), false).await.expect("new failed");
200
201        {
202            let _buf = device.allocate_buffer(8192).await;
203        }
204
205        device.close().await.expect("Close failed");
206    }
207
208    #[fuchsia::test]
209    async fn test_read_write_buffer() {
210        let device =
211            BlockDevice::new(FakeBlockClient::new(1024, 1024), false).await.expect("new failed");
212
213        {
214            let mut buf1 = device.allocate_buffer(8192).await;
215            let mut buf2 = device.allocate_buffer(1024).await;
216            buf1.fill(0xaa);
217            buf2.fill(0xbb);
218            device.write(65536, buf1.as_ref()).await.expect("Write failed");
219            device.write(65536 + 8192, buf2.as_ref()).await.expect("Write failed");
220        }
221        {
222            let mut buf = device.allocate_buffer(8192 + 1024).await;
223            device.read(65536, buf.as_mut()).await.expect("Read failed");
224            let mut data = vec![0u8; 8192 + 1024];
225            buf.copy_to_slice(&mut data);
226            assert_eq!(data[..8192], vec![0xaa as u8; 8192]);
227            assert_eq!(data[8192..], vec![0xbb as u8; 1024]);
228        }
229
230        device.close().await.expect("Close failed");
231    }
232
233    #[fuchsia::test]
234    async fn test_read_only() {
235        let device =
236            BlockDevice::new(FakeBlockClient::new(1024, 1024), true).await.expect("new failed");
237        let mut buf1 = device.allocate_buffer(8192).await;
238        buf1.fill(0xaa);
239        let err = device.write(65536, buf1.as_ref()).await.expect_err("Write succeeded");
240        assert_eq!(err.root_cause().downcast_ref::<Status>().unwrap(), &Status::ACCESS_DENIED);
241    }
242
243    #[fuchsia::test]
244    async fn test_unaligned_access() {
245        let device =
246            BlockDevice::new(FakeBlockClient::new(1024, 1024), false).await.expect("new failed");
247        let mut buf1 = device.allocate_buffer(device.block_size() as usize * 2).await;
248        buf1.fill(0xaa);
249
250        // Write checks
251        {
252            let err = device.write(1, buf1.as_ref()).await.expect_err("Write succeeded");
253            assert_eq!(err.root_cause().downcast_ref::<Status>().unwrap(), &Status::INVALID_ARGS);
254        }
255        {
256            let err = device
257                .write(0, buf1.subslice(1..(device.block_size() as usize + 1)))
258                .await
259                .expect_err("Write succeeded");
260            assert_eq!(err.root_cause().downcast_ref::<Status>().unwrap(), &Status::INVALID_ARGS);
261        }
262        {
263            let err = device
264                .write(0, buf1.subslice(1..device.block_size() as usize))
265                .await
266                .expect_err("Write succeeded");
267            assert_eq!(err.root_cause().downcast_ref::<Status>().unwrap(), &Status::INVALID_ARGS);
268        }
269        {
270            let err = device
271                .write(0, buf1.subslice(0..(device.block_size() as usize + 1)))
272                .await
273                .expect_err("Write succeeded");
274            assert_eq!(err.root_cause().downcast_ref::<Status>().unwrap(), &Status::INVALID_ARGS);
275        }
276
277        // Read checks
278        {
279            let err = device.read(1, buf1.as_mut()).await.expect_err("Read succeeded");
280            assert_eq!(err.root_cause().downcast_ref::<Status>().unwrap(), &Status::INVALID_ARGS);
281        }
282        {
283            let err = device
284                .read(0, buf1.subslice_mut(1..(device.block_size() as usize + 1)))
285                .await
286                .expect_err("Read succeeded");
287            assert_eq!(err.root_cause().downcast_ref::<Status>().unwrap(), &Status::INVALID_ARGS);
288        }
289        {
290            let err = device
291                .read(0, buf1.subslice_mut(1..device.block_size() as usize))
292                .await
293                .expect_err("Read succeeded");
294            assert_eq!(err.root_cause().downcast_ref::<Status>().unwrap(), &Status::INVALID_ARGS);
295        }
296        {
297            let err = device
298                .read(0, buf1.subslice_mut(0..(device.block_size() as usize + 1)))
299                .await
300                .expect_err("Read succeeded");
301            assert_eq!(err.root_cause().downcast_ref::<Status>().unwrap(), &Status::INVALID_ARGS);
302        }
303
304        // Trim
305        {
306            let err = device.trim(1..device.block_size() as u64).await.expect_err("Read succeeded");
307            assert_eq!(err.root_cause().downcast_ref::<Status>().unwrap(), &Status::INVALID_ARGS);
308        }
309        {
310            let err =
311                device.trim(1..(device.block_size() as u64 + 1)).await.expect_err("Read succeeded");
312            assert_eq!(err.root_cause().downcast_ref::<Status>().unwrap(), &Status::INVALID_ARGS);
313        }
314        {
315            let err =
316                device.trim(0..(device.block_size() as u64 + 1)).await.expect_err("Read succeeded");
317            assert_eq!(err.root_cause().downcast_ref::<Status>().unwrap(), &Status::INVALID_ARGS);
318        }
319    }
320
321    #[fuchsia::test]
322    async fn test_foreign_buffer_read_write() {
323        let device1 =
324            BlockDevice::new(FakeBlockClient::new(1024, 1024), false).await.expect("new failed");
325        let device2 =
326            BlockDevice::new(FakeBlockClient::new(1024, 1024), false).await.expect("new failed");
327
328        // Write data using a buffer allocated from device2 to device1 (foreign write)
329        let mut foreign_buf = device2.allocate_buffer(8192).await;
330        foreign_buf.fill(0xaa);
331        device1.write(0, foreign_buf.as_ref()).await.expect("Foreign write failed");
332
333        // Read data back using a buffer allocated from device2 from device1 (foreign read)
334        let mut foreign_read_buf = device2.allocate_buffer(8192).await;
335        device1.read(0, foreign_read_buf.as_mut()).await.expect("Foreign read failed");
336
337        let mut data = vec![0u8; 8192];
338        foreign_read_buf.copy_to_slice(&mut data);
339        assert_eq!(data, vec![0xaa; 8192]);
340    }
341}