storage_device/
file_backed_device.rs1use {
6 crate::{
7 buffer::{BufferFuture, BufferRef, MutableBufferRef},
8 buffer_allocator::{BufferAllocator, BufferSource},
9 Device,
10 },
11 anyhow::{ensure, Error},
12 async_trait::async_trait,
13 block_protocol::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(&self, offset: u64, mut buffer: MutableBufferRef<'_>) -> Result<(), Error> {
68 assert_eq!(offset % self.block_size() as u64, 0);
69 assert_eq!(buffer.range().start % self.block_size() as usize, 0);
70 assert_eq!(buffer.len() % self.block_size() as usize, 0);
71 ensure!(offset + buffer.len() as u64 <= self.size(), "Reading past end of file");
72 self.file.read_exact_at(buffer.as_mut_slice(), offset)?;
74 Ok(())
75 }
76
77 async fn write_with_opts(
78 &self,
79 offset: u64,
80 buffer: BufferRef<'_>,
81 _opts: WriteOptions,
82 ) -> Result<(), Error> {
83 assert_eq!(offset % self.block_size() as u64, 0);
84 assert_eq!(buffer.range().start % self.block_size() as usize, 0);
85 assert_eq!(buffer.len() % self.block_size() as usize, 0);
86 ensure!(offset + buffer.len() as u64 <= self.size(), "Writing past end of file");
87 self.file.write_all_at(buffer.as_slice(), offset)?;
89 Ok(())
90 }
91
92 async fn trim(&self, range: Range<u64>) -> Result<(), Error> {
93 assert_eq!(range.start % self.block_size() as u64, 0);
94 assert_eq!(range.end % self.block_size() as u64, 0);
95 const BUF: [u8; 8192] = [0xab; 8192];
101 let mut offset = range.start;
102 while offset < range.end {
103 let len = std::cmp::min(BUF.len(), range.end as usize - offset as usize);
104 self.file.write_at(&BUF[..len], offset)?;
105 offset += len as u64;
106 }
107 Ok(())
108 }
109
110 async fn close(&self) -> Result<(), Error> {
111 self.file.sync_all()?;
113 Ok(())
114 }
115
116 async fn flush(&self) -> Result<(), Error> {
117 self.file.sync_data().map_err(Into::into)
118 }
119
120 fn barrier(&self) {}
121
122 fn is_read_only(&self) -> bool {
123 false
124 }
125
126 fn supports_trim(&self) -> bool {
127 true
130 }
131}
132
133#[cfg(test)]
134mod tests {
135 use crate::file_backed_device::FileBackedDevice;
136 use crate::Device;
137 use std::fs::{File, OpenOptions};
138 use std::path::PathBuf;
139
140 fn create_file() -> (PathBuf, File) {
141 let mut temp_path = std::env::temp_dir();
142 temp_path.push(format!("file_{:x}", rand::random::<u64>()));
143 let (pathbuf, file) = (
144 temp_path.clone(),
145 OpenOptions::new()
146 .read(true)
147 .write(true)
148 .create_new(true)
149 .open(temp_path.as_path())
150 .unwrap_or_else(|e| panic!("create {:?} failed: {:?}", temp_path.as_path(), e)),
151 );
152 file.set_len(1024 * 1024).expect("Failed to truncate file");
153 (pathbuf, file)
154 }
155
156 #[fuchsia::test]
157 async fn test_lifecycle() {
158 let (_path, file) = create_file();
159 let device = FileBackedDevice::new(file, 512);
160
161 {
162 let _buf = device.allocate_buffer(8192).await;
163 }
164
165 device.close().await.expect("Close failed");
166 }
167
168 #[fuchsia::test]
169 async fn test_read_write() {
170 let (_path, file) = create_file();
171 let device = FileBackedDevice::new(file, 512);
172
173 {
174 let mut buf1 = device.allocate_buffer(8192).await;
175 let mut buf2 = device.allocate_buffer(8192).await;
176 buf1.as_mut_slice().fill(0xaa as u8);
177 buf2.as_mut_slice().fill(0xbb as u8);
178 device.write(65536, buf1.as_ref()).await.expect("Write failed");
179 device.write(65536 + 8192, buf2.as_ref()).await.expect("Write failed");
180 }
181 {
182 let mut buf = device.allocate_buffer(16384).await;
183 device.read(65536, buf.as_mut()).await.expect("Read failed");
184 assert_eq!(buf.as_slice()[..8192], vec![0xaa as u8; 8192]);
185 assert_eq!(buf.as_slice()[8192..], vec![0xbb as u8; 8192]);
186 }
187
188 device.close().await.expect("Close failed");
189 }
190
191 #[fuchsia::test]
192 async fn test_read_write_past_end_of_file_fails() {
193 let (_path, file) = create_file();
194 let device = FileBackedDevice::new(file, 512);
195
196 {
197 let mut buf = device.allocate_buffer(8192).await;
198 let offset = (device.size() as usize - buf.len() + device.block_size() as usize) as u64;
199 buf.as_mut_slice().fill(0xaa as u8);
200 device.write(offset, buf.as_ref()).await.expect_err("Write should have failed");
201 device.read(offset, buf.as_mut()).await.expect_err("Read should have failed");
202 }
203
204 device.close().await.expect("Close failed");
205 }
206
207 #[fuchsia::test]
208 async fn test_writes_persist() {
209 let (path, file) = create_file();
210 let device = FileBackedDevice::new(file, 512);
211
212 {
213 let mut buf1 = device.allocate_buffer(8192).await;
214 let mut buf2 = device.allocate_buffer(8192).await;
215 buf1.as_mut_slice().fill(0xaa as u8);
216 buf2.as_mut_slice().fill(0xbb as u8);
217 device.write(65536, buf1.as_ref()).await.expect("Write failed");
218 device.write(65536 + 8192, buf2.as_ref()).await.expect("Write failed");
219 }
220 device.close().await.expect("Close failed");
221
222 let file = File::open(path.as_path()).expect("Open failed");
223 let device = FileBackedDevice::new(file, 512);
224
225 {
226 let mut buf = device.allocate_buffer(16384).await;
227 device.read(65536, buf.as_mut()).await.expect("Read failed");
228 assert_eq!(buf.as_slice()[..8192], vec![0xaa as u8; 8192]);
229 assert_eq!(buf.as_slice()[8192..], vec![0xbb as u8; 8192]);
230 }
231 device.close().await.expect("Close failed");
232 }
233}