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() 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 fn barrier(&self) {
91 unreachable!()
92 }
93}
94
95#[cfg(test)]
96mod tests {
97 use super::*;
98 use crate::filesystem::FxFilesystem;
99 use crate::object_handle::ObjectHandle as _;
100 use crate::object_store::transaction::{LockKey, lock_keys};
101 use crate::object_store::volume::root_volume;
102 use crate::object_store::{DataObjectHandle, Directory, NewChildStoreOptions, ObjectStore};
103 use std::sync::Arc;
104 use storage_device::DeviceHolder;
105 use storage_device::fake_device::FakeDevice;
106
107 async fn create_test_file(
110 fs: &Arc<FxFilesystem>,
111 num_blocks: usize,
112 ) -> DataObjectHandle<ObjectStore> {
113 let root_vol = root_volume(fs.clone()).await.unwrap();
114 let store = root_vol.new_volume("test", NewChildStoreOptions::default()).await.unwrap();
115 let test_vol_root =
116 Directory::open(&store, store.root_directory_object_id()).await.unwrap();
117
118 let mut transaction = fs
119 .clone()
120 .new_transaction(
121 lock_keys![LockKey::object(
122 store.store_object_id(),
123 store.root_directory_object_id()
124 )],
125 Default::default(),
126 )
127 .await
128 .unwrap();
129
130 let object = test_vol_root.create_child_file(&mut transaction, "test_file").await.unwrap();
131 transaction.commit().await.unwrap();
132
133 {
134 let mut transaction = object.new_transaction().await.unwrap();
135 let block_size = object.block_size() as usize;
136 let mut buffer = object.allocate_buffer(block_size * num_blocks).await;
137 for i in 0..num_blocks {
138 let buff_range = (i * block_size)..((i + 1) * block_size);
139 buffer.as_mut_slice()[buff_range].fill((i % 0xFF) as u8);
140 }
141 object.txn_write(&mut transaction, 0, buffer.as_ref()).await.unwrap();
142
143 transaction.commit().await.unwrap();
144 }
145
146 object
147 }
148
149 #[fuchsia::test]
150 async fn test_read_only_virtual_device() {
151 const BLOCK_SIZE: usize = 4096;
152 const TEST_FILE_BLOCK_COUNT: usize = 64;
153 let device = DeviceHolder::new(FakeDevice::new(512, BLOCK_SIZE as u32));
154 let fs = FxFilesystem::new_empty(device).await.unwrap();
155 let handle = create_test_file(&fs, TEST_FILE_BLOCK_COUNT).await;
156 let handle_as_device = ReadOnlyDevice::new(handle).unwrap();
157 assert_eq!(handle_as_device.block_size(), BLOCK_SIZE as u32);
158 assert_eq!(handle_as_device.block_count(), TEST_FILE_BLOCK_COUNT as u64);
159
160 let mut buffer = handle_as_device.allocate_buffer(BLOCK_SIZE * TEST_FILE_BLOCK_COUNT).await;
162 handle_as_device.read(0, buffer.as_mut()).await.unwrap();
163 for i in 0..TEST_FILE_BLOCK_COUNT {
164 let buff_range = (i * BLOCK_SIZE)..((i + 1) * BLOCK_SIZE);
165 assert_eq!(buffer.as_slice()[buff_range], [(i % 0xFF) as u8; BLOCK_SIZE]);
166 }
167
168 let mut buffer = handle_as_device.allocate_buffer(BLOCK_SIZE).await;
170 handle_as_device.read((BLOCK_SIZE * 4) as u64, buffer.as_mut()).await.unwrap();
171 assert_eq!(buffer.as_slice(), [4u8; BLOCK_SIZE]);
172 }
173}