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