1use crate::error::{Error, Result};
12use std::fs::{File, OpenOptions};
13use std::io::{Read, Seek, SeekFrom, Write};
14use std::path::Path;
15use std::sync::Mutex;
16
17pub trait BlockDevice: Send + Sync {
19 fn read_at(&self, offset: u64, buf: &mut [u8]) -> Result<()>;
21
22 fn size_bytes(&self) -> u64;
24
25 fn write_at(&self, _offset: u64, _buf: &[u8]) -> Result<()> {
28 Err(Error::Corrupt(
29 "block device is read-only (no write_at impl)",
30 ))
31 }
32
33 fn flush(&self) -> Result<()> {
36 Ok(())
37 }
38
39 fn is_writable(&self) -> bool {
42 false
43 }
44
45 fn populate_cache(&self, _block: u64, _bytes: Vec<u8>) {}
58
59 fn unpin_all(&self) {}
64
65 fn invalidate_cache(&self) -> Result<()> {
69 Ok(())
70 }
71}
72
73pub struct FileDevice {
75 file: Mutex<File>,
76 size: u64,
77 writable: bool,
78}
79
80impl FileDevice {
81 pub fn open(path: &str) -> Result<Self> {
83 Self::open_path(Path::new(path))
84 }
85
86 pub fn open_path(path: &Path) -> Result<Self> {
90 let file = File::open(path)?;
91 let size = file.metadata()?.len();
92 Ok(Self {
93 file: Mutex::new(file),
94 size,
95 writable: false,
96 })
97 }
98
99 pub fn open_rw(path: &str) -> Result<Self> {
103 Self::open_path_rw(Path::new(path))
104 }
105
106 pub fn open_path_rw(path: &Path) -> Result<Self> {
109 let file = OpenOptions::new().read(true).write(true).open(path)?;
110 let size = file.metadata()?.len();
111 Ok(Self {
112 file: Mutex::new(file),
113 size,
114 writable: true,
115 })
116 }
117
118 pub fn open_best_effort(path: &str) -> Result<Self> {
122 match Self::open_rw(path) {
123 Ok(d) => Ok(d),
124 Err(_) => Self::open(path),
125 }
126 }
127}
128
129impl BlockDevice for FileDevice {
130 fn read_at(&self, offset: u64, buf: &mut [u8]) -> Result<()> {
131 let mut f = self.file.lock().unwrap();
132 f.seek(SeekFrom::Start(offset))?;
133 f.read_exact(buf)?;
134 Ok(())
135 }
136
137 fn size_bytes(&self) -> u64 {
138 self.size
139 }
140
141 fn write_at(&self, offset: u64, buf: &[u8]) -> Result<()> {
142 if !self.writable {
143 return Err(Error::Corrupt("FileDevice opened read-only"));
144 }
145 let mut f = self.file.lock().unwrap();
146 f.seek(SeekFrom::Start(offset))?;
147 f.write_all(buf)?;
148 Ok(())
149 }
150
151 fn flush(&self) -> Result<()> {
152 if !self.writable {
153 return Ok(());
154 }
155 let mut f = self.file.lock().unwrap();
156 f.flush()?;
157 f.sync_data()?;
158 Ok(())
159 }
160
161 fn is_writable(&self) -> bool {
162 self.writable
163 }
164}
165
166pub type ReadCb = Box<dyn Fn(u64, &mut [u8]) -> std::io::Result<()> + Send + Sync>;
168pub type WriteCb = Box<dyn Fn(u64, &[u8]) -> std::io::Result<()> + Send + Sync>;
170pub type FlushCb = Box<dyn Fn() -> std::io::Result<()> + Send + Sync>;
172
173pub struct CallbackDevice {
177 pub size: u64,
178 pub read: ReadCb,
179 pub write: Option<WriteCb>,
180 pub flush: Option<FlushCb>,
181}
182
183impl BlockDevice for CallbackDevice {
184 fn read_at(&self, offset: u64, buf: &mut [u8]) -> Result<()> {
185 (self.read)(offset, buf)?;
186 Ok(())
187 }
188
189 fn size_bytes(&self) -> u64 {
190 self.size
191 }
192
193 fn write_at(&self, offset: u64, buf: &[u8]) -> Result<()> {
194 match &self.write {
195 Some(f) => {
196 f(offset, buf)?;
197 Ok(())
198 }
199 None => Err(Error::Corrupt("CallbackDevice has no write callback")),
200 }
201 }
202
203 fn flush(&self) -> Result<()> {
204 match &self.flush {
205 Some(f) => {
206 f()?;
207 Ok(())
208 }
209 None => Ok(()),
210 }
211 }
212
213 fn is_writable(&self) -> bool {
214 self.write.is_some()
215 }
216}
217
218pub struct AlignedDevice<D: BlockDevice> {
239 inner: D,
240 sector: u64,
241 write_lock: Mutex<()>,
242}
243
244impl<D: BlockDevice> AlignedDevice<D> {
245 pub fn new(inner: D, sector: u32) -> Self {
248 assert!(
249 sector.is_power_of_two(),
250 "sector size {sector} is not a power of two"
251 );
252 AlignedDevice {
253 inner,
254 sector: sector as u64,
255 write_lock: Mutex::new(()),
256 }
257 }
258
259 fn is_aligned(&self, offset: u64, len: usize) -> bool {
260 let mask = self.sector - 1;
261 offset & mask == 0 && (len as u64) & mask == 0
262 }
263
264 fn span(&self, offset: u64, len: usize) -> Result<(u64, u64)> {
266 let mask = self.sector - 1;
267 let end = offset
268 .checked_add(len as u64)
269 .and_then(|e| e.checked_add(mask))
270 .ok_or(Error::Corrupt("device request overflows u64"))?
271 & !mask;
272 if end > self.inner.size_bytes() {
273 return Err(Error::Corrupt("device request past the end of the device"));
274 }
275 Ok((offset & !mask, end))
276 }
277}
278
279impl<D: BlockDevice> BlockDevice for AlignedDevice<D> {
280 fn read_at(&self, offset: u64, buf: &mut [u8]) -> Result<()> {
281 if self.is_aligned(offset, buf.len()) {
282 return self.inner.read_at(offset, buf);
283 }
284 let (start, end) = self.span(offset, buf.len())?;
285 let mut bounce = vec![0u8; (end - start) as usize];
286 self.inner.read_at(start, &mut bounce)?;
287 let head = (offset - start) as usize;
288 buf.copy_from_slice(&bounce[head..head + buf.len()]);
289 Ok(())
290 }
291
292 fn size_bytes(&self) -> u64 {
293 self.inner.size_bytes()
294 }
295
296 fn write_at(&self, offset: u64, buf: &[u8]) -> Result<()> {
297 let _guard = self.write_lock.lock().unwrap_or_else(|p| p.into_inner());
298 if self.is_aligned(offset, buf.len()) {
299 return self.inner.write_at(offset, buf);
300 }
301 let (start, end) = self.span(offset, buf.len())?;
302 let sector = self.sector as usize;
303 let mut bounce = vec![0u8; (end - start) as usize];
304 let head = (offset - start) as usize;
305 let tail = head + buf.len();
306 if head != 0 {
309 self.inner.read_at(start, &mut bounce[..sector])?;
310 }
311 let last = bounce.len() - sector;
312 if !tail.is_multiple_of(sector) && (last != 0 || head == 0) {
313 self.inner
314 .read_at(start + last as u64, &mut bounce[last..])?;
315 }
316 bounce[head..tail].copy_from_slice(buf);
317 self.inner.write_at(start, &bounce)
318 }
319
320 fn flush(&self) -> Result<()> {
321 self.inner.flush()
322 }
323
324 fn is_writable(&self) -> bool {
325 self.inner.is_writable()
326 }
327
328 fn populate_cache(&self, block: u64, bytes: Vec<u8>) {
329 self.inner.populate_cache(block, bytes)
330 }
331
332 fn unpin_all(&self) {
333 self.inner.unpin_all()
334 }
335
336 fn invalidate_cache(&self) -> Result<()> {
337 self.inner.invalidate_cache()
338 }
339}
340
341#[cfg(test)]
346mod aligned_device_tests {
347 use super::*;
348
349 const SECTOR: u64 = 512;
350
351 struct Strict(Mutex<Vec<u8>>);
353
354 impl Strict {
355 fn check(offset: u64, len: usize) -> Result<()> {
356 if !offset.is_multiple_of(SECTOR) || !(len as u64).is_multiple_of(SECTOR) {
357 return Err(Error::Corrupt("unaligned request"));
358 }
359 Ok(())
360 }
361 }
362
363 impl BlockDevice for Strict {
364 fn read_at(&self, offset: u64, buf: &mut [u8]) -> Result<()> {
365 Self::check(offset, buf.len())?;
366 let b = self.0.lock().unwrap();
367 buf.copy_from_slice(&b[offset as usize..offset as usize + buf.len()]);
368 Ok(())
369 }
370 fn size_bytes(&self) -> u64 {
371 self.0.lock().unwrap().len() as u64
372 }
373 fn write_at(&self, offset: u64, buf: &[u8]) -> Result<()> {
374 Self::check(offset, buf.len())?;
375 let mut b = self.0.lock().unwrap();
376 b[offset as usize..offset as usize + buf.len()].copy_from_slice(buf);
377 Ok(())
378 }
379 fn is_writable(&self) -> bool {
380 true
381 }
382 }
383
384 fn pattern(len: usize) -> Vec<u8> {
385 (0..len).map(|i| (i * 7 + 3) as u8).collect()
386 }
387
388 #[test]
392 fn unaligned_reads_and_writes_match_a_byte_granular_model() {
393 let size = 4 * SECTOR as usize;
394 let cases: &[(u64, usize)] = &[
395 (0, 1),
396 (1, 10),
397 (500, 12),
398 (511, 1),
399 (510, 4),
400 (0, 512),
401 (512, 1024),
402 (100, 1500),
403 (1, 2046),
404 (1024, 1),
405 (2047, 1),
406 (0, 2048),
407 (3, 0),
408 ];
409 for &(off, len) in cases {
410 let base = pattern(size);
411 let dev = AlignedDevice::new(Strict(Mutex::new(base.clone())), SECTOR as u32);
412 let data: Vec<u8> = (0..len).map(|i| !(i as u8)).collect();
413 dev.write_at(off, &data)
414 .unwrap_or_else(|e| panic!("write {len}@{off}: {e:?}"));
415 let mut model = base;
416 model[off as usize..off as usize + len].copy_from_slice(&data);
417 assert!(
418 *dev.inner.0.lock().unwrap() == model,
419 "write {len}@{off} disturbed bytes outside it"
420 );
421 let mut back = vec![0u8; len];
422 dev.read_at(off, &mut back)
423 .unwrap_or_else(|e| panic!("read {len}@{off}: {e:?}"));
424 assert_eq!(back, data, "read {len}@{off}");
425 }
426 }
427
428 #[test]
429 fn a_request_past_the_end_is_refused_not_wrapped() {
430 let dev = AlignedDevice::new(Strict(Mutex::new(vec![0; 1024])), SECTOR as u32);
431 let mut buf = [0u8; 4];
432 assert!(dev.read_at(1022, &mut buf).is_err());
433 assert!(dev.write_at(1022, &buf).is_err());
434 assert!(dev.read_at(u64::MAX - 1, &mut buf).is_err());
435 }
436}