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::buffer::{BufferFuture, BufferRef, MutableBufferRef};
6use crate::buffer_allocator::{BufferAllocator, BufferSource};
7use crate::Device;
8use anyhow::{bail, ensure, Error};
9use async_trait::async_trait;
10use block_client::{BlockClient, BlockFlags, BufferSlice, MutableBufferSlice, VmoId, WriteOptions};
11use std::ops::Range;
12use zx::Status;
13
14/// BlockDevice is an implementation of Device backed by a real block device behind a FIFO.
15pub struct BlockDevice {
16    allocator: BufferAllocator,
17    remote: Box<dyn BlockClient>,
18    read_only: bool,
19    vmoid: VmoId,
20}
21
22const TRANSFER_VMO_SIZE: usize = 128 * 1024 * 1024;
23
24impl BlockDevice {
25    /// Creates a new BlockDevice over |remote|.
26    pub async fn new(remote: Box<dyn BlockClient>, read_only: bool) -> Result<Self, Error> {
27        let buffer_source = BufferSource::new(TRANSFER_VMO_SIZE);
28        let vmoid = remote.attach_vmo(buffer_source.vmo()).await?;
29        let allocator = BufferAllocator::new(remote.block_size() as usize, buffer_source);
30        Ok(Self { allocator, remote, read_only, vmoid })
31    }
32}
33
34#[async_trait]
35impl Device for BlockDevice {
36    fn allocate_buffer(&self, size: usize) -> BufferFuture<'_> {
37        self.allocator.allocate_buffer(size)
38    }
39
40    fn block_size(&self) -> u32 {
41        self.remote.block_size()
42    }
43
44    fn block_count(&self) -> u64 {
45        self.remote.block_count()
46    }
47
48    async fn read(&self, offset: u64, buffer: MutableBufferRef<'_>) -> Result<(), Error> {
49        if buffer.len() == 0 {
50            return Ok(());
51        }
52        ensure!(self.vmoid.is_valid(), "Device is closed");
53        assert_eq!(offset % self.block_size() as u64, 0);
54        assert_eq!(buffer.range().start % self.block_size() as usize, 0);
55        assert_eq!((offset + buffer.len() as u64) % self.block_size() as u64, 0);
56        Ok(self
57            .remote
58            .read_at(
59                MutableBufferSlice::new_with_vmo_id(
60                    &self.vmoid,
61                    buffer.range().start as u64,
62                    buffer.len() as u64,
63                ),
64                offset,
65            )
66            .await?)
67    }
68
69    async fn write_with_opts(
70        &self,
71        offset: u64,
72        buffer: BufferRef<'_>,
73        opts: WriteOptions,
74    ) -> Result<(), Error> {
75        if self.read_only {
76            bail!(Status::ACCESS_DENIED);
77        }
78        if buffer.len() == 0 {
79            return Ok(());
80        }
81        ensure!(self.vmoid.is_valid(), "Device is closed");
82        assert_eq!(offset % self.block_size() as u64, 0);
83        assert_eq!(buffer.range().start % self.block_size() as usize, 0);
84        assert_eq!((offset + buffer.len() as u64) % self.block_size() as u64, 0);
85        Ok(self
86            .remote
87            .write_at_with_opts(
88                BufferSlice::new_with_vmo_id(
89                    &self.vmoid,
90                    buffer.range().start as u64,
91                    buffer.len() as u64,
92                ),
93                offset,
94                opts,
95            )
96            .await?)
97    }
98
99    async fn trim(&self, range: Range<u64>) -> Result<(), Error> {
100        if self.read_only {
101            bail!(Status::ACCESS_DENIED);
102        }
103        assert_eq!(range.start % self.block_size() as u64, 0);
104        assert_eq!(range.end % self.block_size() as u64, 0);
105        Ok(self.remote.trim(range).await?)
106    }
107
108    async fn close(&self) -> Result<(), Error> {
109        // We can leak the VMO id because we are closing the device.
110        let _ = self.vmoid.take().into_id();
111        Ok(self.remote.close().await?)
112    }
113
114    async fn flush(&self) -> Result<(), Error> {
115        Ok(self.remote.flush().await?)
116    }
117
118    fn is_read_only(&self) -> bool {
119        self.read_only
120    }
121
122    fn supports_trim(&self) -> bool {
123        self.remote.block_flags().contains(BlockFlags::TRIM_SUPPORT)
124    }
125}
126
127impl Drop for BlockDevice {
128    fn drop(&mut self) {
129        // We can't detach the VmoId because we're not async here, but we are tearing down the
130        // connection to the block device so we don't really need to.
131        let _ = self.vmoid.take().into_id();
132    }
133}
134
135#[cfg(test)]
136mod tests {
137    use crate::block_device::BlockDevice;
138    use crate::Device;
139    use fake_block_client::FakeBlockClient;
140    use zx::Status;
141
142    #[fuchsia::test]
143    async fn test_lifecycle() {
144        let device = BlockDevice::new(Box::new(FakeBlockClient::new(1024, 1024)), false)
145            .await
146            .expect("new failed");
147
148        {
149            let _buf = device.allocate_buffer(8192).await;
150        }
151
152        device.close().await.expect("Close failed");
153    }
154
155    #[fuchsia::test]
156    async fn test_read_write_buffer() {
157        let device = BlockDevice::new(Box::new(FakeBlockClient::new(1024, 1024)), false)
158            .await
159            .expect("new failed");
160
161        {
162            let mut buf1 = device.allocate_buffer(8192).await;
163            let mut buf2 = device.allocate_buffer(1024).await;
164            buf1.as_mut_slice().fill(0xaa as u8);
165            buf2.as_mut_slice().fill(0xbb as u8);
166            device.write(65536, buf1.as_ref()).await.expect("Write failed");
167            device.write(65536 + 8192, buf2.as_ref()).await.expect("Write failed");
168        }
169        {
170            let mut buf = device.allocate_buffer(8192 + 1024).await;
171            device.read(65536, buf.as_mut()).await.expect("Read failed");
172            assert_eq!(buf.as_slice()[..8192], vec![0xaa as u8; 8192]);
173            assert_eq!(buf.as_slice()[8192..], vec![0xbb as u8; 1024]);
174        }
175
176        device.close().await.expect("Close failed");
177    }
178
179    #[fuchsia::test]
180    async fn test_read_only() {
181        let device = BlockDevice::new(Box::new(FakeBlockClient::new(1024, 1024)), true)
182            .await
183            .expect("new failed");
184        let mut buf1 = device.allocate_buffer(8192).await;
185        buf1.as_mut_slice().fill(0xaa as u8);
186        let err = device.write(65536, buf1.as_ref()).await.expect_err("Write succeeded");
187        assert_eq!(err.root_cause().downcast_ref::<Status>().unwrap(), &Status::ACCESS_DENIED);
188    }
189}