1use 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
16pub 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 pub async fn new(remote: T, read_only: bool) -> Result<Self, Error> {
29 let buffer_source = BufferSource::new(TRANSFER_VMO_SIZE);
30 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 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 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 {
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 {
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 {
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}