use std::io::{Read, Seek, SeekFrom};
use std::sync::{Mutex, PoisonError};
use forensic_vfs::{ImageSource, VfsError, VfsResult};
use crate::Aff4Reader;
pub struct Aff4Source {
inner: Mutex<Aff4Reader>,
len: u64,
}
impl Aff4Source {
pub fn new(reader: Aff4Reader) -> Self {
let len = reader.virtual_disk_size();
Self {
inner: Mutex::new(reader),
len,
}
}
}
impl ImageSource for Aff4Source {
fn len(&self) -> u64 {
self.len
}
fn read_at(&self, offset: u64, buf: &mut [u8]) -> VfsResult<usize> {
let io_err = |op: &'static str| move |source: std::io::Error| VfsError::Io { op, source };
let avail = self.len.saturating_sub(offset);
if avail == 0 {
return Ok(0);
}
let want = (buf.len() as u64).min(avail) as usize;
let mut guard = self.inner.lock().unwrap_or_else(PoisonError::into_inner);
guard
.seek(SeekFrom::Start(offset))
.map_err(io_err("aff4::seek"))?;
let mut total = 0;
while total < want {
let Some(slot) = buf.get_mut(total..want) else {
break; };
match guard.read(slot).map_err(io_err("aff4::read"))? {
0 => break,
n => total += n,
}
}
Ok(total)
}
}
#[cfg(test)]
#[allow(clippy::unwrap_used, clippy::expect_used)]
mod tests {
use std::io::Cursor;
use std::sync::Arc;
use forensic_vfs::ImageSource;
use super::Aff4Source;
use crate::Aff4Reader;
static BASE_LINEAR: &[u8] = include_bytes!("../tests/data/Base-Linear.aff4");
const VIRTUAL_DISK_SIZE: u64 = 268_435_456;
#[test]
fn aff4_reader_is_an_image_source() {
let reader = Aff4Reader::open_reader(Box::new(Cursor::new(BASE_LINEAR)))
.expect("open Base-Linear.aff4");
assert_eq!(reader.virtual_disk_size(), VIRTUAL_DISK_SIZE);
let src: Arc<dyn ImageSource> = Arc::new(Aff4Source::new(reader));
assert_eq!(src.len(), VIRTUAL_DISK_SIZE);
assert!(!src.is_empty());
let mut sector = vec![0u8; 512];
assert_eq!(src.read_at(0, &mut sector).expect("read_at 0"), 512);
assert_eq!((sector[510], sector[511]), (0x55, 0xAA));
assert_ne!(
sector.as_slice(),
[0u8; 512].as_slice(),
"sector 0 is a real MBR, not a sparse zero region"
);
let mut eof = [0u8; 16];
assert_eq!(
src.read_at(VIRTUAL_DISK_SIZE, &mut eof).expect("eof read"),
0
);
let mut over = [0u8; 64];
assert_eq!(
src.read_at(VIRTUAL_DISK_SIZE - 8, &mut over)
.expect("straddle-EOF read"),
8
);
assert_eq!(
src.read_at(VIRTUAL_DISK_SIZE + 4096, &mut over)
.expect("past-eof read"),
0
);
}
}