1use crate::errors::FxfsError;
9use crate::object_handle::ReadObjectHandle;
10use anyhow::{Context as _, Error, bail};
11use async_trait::async_trait;
12use std::ops::Range;
13use storage_device::buffer::MutableBufferRef;
14use storage_device::buffer_allocator::BufferFuture;
15use storage_device::{Device, ReadOptions};
16
17pub struct ReadOnlyDevice<H: ReadObjectHandle> {
19 handle: H,
20}
21
22impl<H: ReadObjectHandle> ReadOnlyDevice<H> {
23 pub fn new(handle: H) -> Result<Self, Error> {
24 let device = Self { handle };
25 if device.block_size() == 0 {
27 bail!("Expected non-zero block size.");
28 }
29 Ok(device)
30 }
31}
32
33#[async_trait]
34impl<H: ReadObjectHandle> Device for ReadOnlyDevice<H> {
35 fn allocate_buffer(&self, size: usize) -> BufferFuture<'_> {
36 self.handle.allocate_buffer(size)
37 }
38
39 fn block_size(&self) -> u32 {
40 self.handle.block_size().get() as u32
41 }
42
43 fn block_count(&self) -> u64 {
44 self.handle.get_size() / self.handle.block_size()
45 }
46
47 async fn read_with_opts(
48 &self,
49 offset: u64,
50 buffer: MutableBufferRef<'_>,
51 _read_opts: ReadOptions,
52 ) -> Result<(), Error> {
53 let len = buffer.len();
54 let amount = self.handle.read(offset, buffer).await?;
55 if amount != len {
56 return Err(FxfsError::OutOfRange).context("short read from underlying object");
57 }
58 Ok(())
59 }
60
61 async fn close(&self) -> Result<(), Error> {
62 Ok(())
63 }
64
65 fn is_read_only(&self) -> bool {
66 true
67 }
68
69 fn supports_trim(&self) -> bool {
70 false
71 }
72
73 async fn write_with_opts(
74 &self,
75 _offset: u64,
76 _buffer: storage_device::buffer::BufferRef<'_>,
77 _write_opts: storage_device::WriteOptions,
78 ) -> Result<(), Error> {
79 unreachable!()
80 }
81
82 async fn flush(&self) -> Result<(), Error> {
83 unreachable!()
84 }
85
86 async fn trim(&self, _range: Range<u64>) -> Result<(), Error> {
87 unreachable!()
88 }
89}
90
91#[cfg(test)]
92mod tests {
93 use super::*;
94 use crate::filesystem::FxFilesystem;
95 use crate::object_handle::ObjectHandle as _;
96 use crate::object_store::transaction::{LockKey, lock_keys};
97 use crate::object_store::volume::root_volume;
98 use crate::object_store::{DataObjectHandle, Directory, NewChildStoreOptions, ObjectStore};
99 use std::sync::Arc;
100 use storage_device::DeviceHolder;
101 use storage_device::fake_device::FakeDevice;
102
103 async fn create_test_file(
106 fs: &Arc<FxFilesystem>,
107 num_blocks: usize,
108 ) -> DataObjectHandle<ObjectStore> {
109 let root_vol = root_volume(fs.clone()).await.unwrap();
110 let store = root_vol.new_volume("test", NewChildStoreOptions::default()).await.unwrap();
111 let test_vol_root =
112 Directory::open(&store, store.root_directory_object_id()).await.unwrap();
113
114 let mut transaction = fs
115 .root_store()
116 .new_transaction(
117 lock_keys![LockKey::object(
118 store.store_object_id(),
119 store.root_directory_object_id()
120 )],
121 Default::default(),
122 )
123 .await
124 .unwrap();
125
126 let object = test_vol_root.create_child_file(&mut transaction, "test_file").await.unwrap();
127 transaction.commit().await.unwrap();
128
129 {
130 let mut transaction = object.new_transaction().await.unwrap();
131 let block_size = object.block_size().get() as usize;
132 let mut buffer = object.allocate_buffer(block_size * num_blocks).await;
133 for i in 0..num_blocks {
134 let buff_range = (i * block_size)..((i + 1) * block_size);
135 buffer.subslice_mut(buff_range).fill((i % 0xFF) as u8);
136 }
137 object.txn_write(&mut transaction, 0, buffer.as_ref()).await.unwrap();
138
139 transaction.commit().await.unwrap();
140 }
141
142 object
143 }
144
145 #[fuchsia::test]
146 async fn test_read_only_virtual_device() {
147 const BLOCK_SIZE: usize = 4096;
148 const TEST_FILE_BLOCK_COUNT: usize = 64;
149 let device = DeviceHolder::new(FakeDevice::new(512, BLOCK_SIZE as u32));
150 let fs = FxFilesystem::new_empty(device).await.unwrap();
151 let handle = create_test_file(&fs, TEST_FILE_BLOCK_COUNT).await;
152 let handle_as_device = ReadOnlyDevice::new(handle).unwrap();
153 assert_eq!(handle_as_device.block_size(), BLOCK_SIZE as u32);
154 assert_eq!(handle_as_device.block_count(), TEST_FILE_BLOCK_COUNT as u64);
155
156 let mut buffer = handle_as_device.allocate_buffer(BLOCK_SIZE * TEST_FILE_BLOCK_COUNT).await;
158 handle_as_device.read(0, buffer.as_mut()).await.unwrap();
159 let data = buffer.to_vec();
160 for i in 0..TEST_FILE_BLOCK_COUNT {
161 let buff_range = (i * BLOCK_SIZE)..((i + 1) * BLOCK_SIZE);
162 assert_eq!(data[buff_range], [(i % 0xFF) as u8; BLOCK_SIZE]);
163 }
164
165 let mut buffer = handle_as_device.allocate_buffer(BLOCK_SIZE).await;
167 handle_as_device.read((BLOCK_SIZE * 4) as u64, buffer.as_mut()).await.unwrap();
168 assert_eq!(buffer.to_vec(), [4u8; BLOCK_SIZE]);
169 }
170}