1use {
6 crate::{
7 Device,
8 buffer::{BufferFuture, BufferRef, MutableBufferRef},
9 buffer_allocator::{BufferAllocator, BufferSource},
10 },
11 anyhow::{Error, ensure},
12 async_trait::async_trait,
13 block_protocol::{ReadOptions, WriteOptions},
14 std::{ops::Range, os::unix::fs::FileExt},
16};
17
18pub struct FileBackedDevice {
22 allocator: BufferAllocator,
23 file: std::fs::File,
24 block_count: u64,
25 block_size: u32,
26}
27
28const TRANSFER_HEAP_SIZE: usize = 32 * 1024 * 1024;
29
30impl FileBackedDevice {
31 pub fn new(file: std::fs::File, block_size: u32) -> Self {
34 let size = file.metadata().unwrap().len();
35 assert!(block_size > 0 && size > 0);
36 Self::new_with_block_count(file, block_size, size / block_size as u64)
37 }
38
39 pub fn new_with_block_count(file: std::fs::File, block_size: u32, block_count: u64) -> Self {
44 let allocator =
48 BufferAllocator::new(block_size as usize, BufferSource::new(TRANSFER_HEAP_SIZE));
49 Self { allocator, file, block_count, block_size }
50 }
51}
52
53#[async_trait]
54impl Device for FileBackedDevice {
55 fn allocate_buffer(&self, size: usize) -> BufferFuture<'_> {
56 self.allocator.allocate_buffer(size)
57 }
58
59 fn block_size(&self) -> u32 {
60 self.block_size
61 }
62
63 fn block_count(&self) -> u64 {
64 self.block_count
65 }
66
67 async fn read_with_opts(
68 &self,
69 offset: u64,
70 mut buffer: MutableBufferRef<'_>,
71 _read_opts: ReadOptions,
72 ) -> Result<(), Error> {
73 assert_eq!(offset % self.block_size() as u64, 0);
74 assert_eq!(buffer.range().start % self.block_size() as usize, 0);
75 assert_eq!(buffer.len() % self.block_size() as usize, 0);
76 ensure!(offset + buffer.len() as u64 <= self.size(), "Reading past end of file");
77 if let Some(slice) = buffer.try_as_mut_slice() {
79 self.file.read_exact_at(slice, offset)?;
80 } else {
81 let mut data = vec![0u8; buffer.len()];
82 self.file.read_exact_at(&mut data, offset)?;
83 buffer.copy_from_slice(&data);
84 }
85 Ok(())
86 }
87
88 async fn write_with_opts(
89 &self,
90 offset: u64,
91 buffer: BufferRef<'_>,
92 _write_opts: WriteOptions,
93 ) -> Result<(), Error> {
94 assert_eq!(offset % self.block_size() as u64, 0);
95 assert_eq!(buffer.range().start % self.block_size() as usize, 0);
96 assert_eq!(buffer.len() % self.block_size() as usize, 0);
97 ensure!(offset + buffer.len() as u64 <= self.size(), "Writing past end of file");
98 if let Some(slice) = buffer.try_as_slice() {
100 self.file.write_all_at(slice, offset)?;
101 } else {
102 let data = buffer.to_vec();
103 self.file.write_all_at(&data, offset)?;
104 }
105 Ok(())
106 }
107
108 async fn trim(&self, range: Range<u64>) -> Result<(), Error> {
109 assert_eq!(range.start % self.block_size() as u64, 0);
110 assert_eq!(range.end % self.block_size() as u64, 0);
111 const BUF: [u8; 8192] = [0xab; 8192];
117 let mut offset = range.start;
118 while offset < range.end {
119 let len = std::cmp::min(BUF.len(), range.end as usize - offset as usize);
120 self.file.write_at(&BUF[..len], offset)?;
121 offset += len as u64;
122 }
123 Ok(())
124 }
125
126 async fn close(&self) -> Result<(), Error> {
127 self.file.sync_all()?;
129 Ok(())
130 }
131
132 async fn flush(&self) -> Result<(), Error> {
133 self.file.sync_data().map_err(Into::into)
134 }
135
136 fn barrier(&self) {}
137
138 fn is_read_only(&self) -> bool {
139 false
140 }
141
142 fn supports_trim(&self) -> bool {
143 true
146 }
147}
148
149#[cfg(test)]
150mod tests {
151 use crate::Device;
152 use crate::file_backed_device::FileBackedDevice;
153 use std::fs::{File, OpenOptions};
154 use std::path::PathBuf;
155
156 fn create_file() -> (PathBuf, File) {
157 let mut temp_path = std::env::temp_dir();
158 temp_path.push(format!("file_{:x}", rand::random::<u64>()));
159 let (pathbuf, file) = (
160 temp_path.clone(),
161 OpenOptions::new()
162 .read(true)
163 .write(true)
164 .create_new(true)
165 .open(temp_path.as_path())
166 .unwrap_or_else(|e| panic!("create {:?} failed: {:?}", temp_path.as_path(), e)),
167 );
168 file.set_len(1024 * 1024).expect("Failed to truncate file");
169 (pathbuf, file)
170 }
171
172 #[fuchsia::test]
173 async fn test_lifecycle() {
174 let (_path, file) = create_file();
175 let device = FileBackedDevice::new(file, 512);
176
177 {
178 let _buf = device.allocate_buffer(8192).await;
179 }
180
181 device.close().await.expect("Close failed");
182 }
183
184 #[fuchsia::test]
185 async fn test_read_write() {
186 let (_path, file) = create_file();
187 let device = FileBackedDevice::new(file, 512);
188
189 {
190 let mut buf1 = device.allocate_buffer(8192).await;
191 let mut buf2 = device.allocate_buffer(8192).await;
192 buf1.fill(0xaa);
193 buf2.fill(0xbb);
194 device.write(65536, buf1.as_ref()).await.expect("Write failed");
195 device.write(65536 + 8192, buf2.as_ref()).await.expect("Write failed");
196 }
197 {
198 let mut buf = device.allocate_buffer(16384).await;
199 device.read(65536, buf.as_mut()).await.expect("Read failed");
200 let mut data = vec![0u8; 16384];
201 buf.copy_to_slice(&mut data);
202 assert_eq!(data[..8192], vec![0xaa as u8; 8192]);
203 assert_eq!(data[8192..], vec![0xbb as u8; 8192]);
204 }
205
206 device.close().await.expect("Close failed");
207 }
208
209 #[fuchsia::test]
210 async fn test_read_write_past_end_of_file_fails() {
211 let (_path, file) = create_file();
212 let device = FileBackedDevice::new(file, 512);
213
214 {
215 let mut buf = device.allocate_buffer(8192).await;
216 let offset = (device.size() as usize - buf.len() + device.block_size() as usize) as u64;
217 buf.fill(0xaa);
218 device.write(offset, buf.as_ref()).await.expect_err("Write should have failed");
219 device.read(offset, buf.as_mut()).await.expect_err("Read should have failed");
220 }
221
222 device.close().await.expect("Close failed");
223 }
224
225 #[fuchsia::test]
226 async fn test_writes_persist() {
227 let (path, file) = create_file();
228 let device = FileBackedDevice::new(file, 512);
229
230 {
231 let mut buf1 = device.allocate_buffer(8192).await;
232 let mut buf2 = device.allocate_buffer(8192).await;
233 buf1.fill(0xaa);
234 buf2.fill(0xbb);
235 device.write(65536, buf1.as_ref()).await.expect("Write failed");
236 device.write(65536 + 8192, buf2.as_ref()).await.expect("Write failed");
237 }
238 device.close().await.expect("Close failed");
239
240 let file = File::open(path.as_path()).expect("Open failed");
241 let device = FileBackedDevice::new(file, 512);
242
243 {
244 let mut buf = device.allocate_buffer(16384).await;
245 device.read(65536, buf.as_mut()).await.expect("Read failed");
246 let mut data = vec![0u8; 16384];
247 buf.copy_to_slice(&mut data);
248 assert_eq!(data[..8192], vec![0xaa as u8; 8192]);
249 assert_eq!(data[8192..], vec![0xbb as u8; 8192]);
250 }
251 device.close().await.expect("Close failed");
252 }
253}