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