#![allow(clippy::unwrap_used, clippy::expect_used)]
use forensic_mount::{open_image, FsFileType};
#[test]
fn open_image_on_non_image_errors() {
let dir = std::env::temp_dir().join(format!("4n6mount_e2e_{}", std::process::id()));
std::fs::create_dir_all(&dir).unwrap();
let path = dir.join("not-an-image.bin");
std::fs::write(&path, vec![0x5Au8; 64 * 1024]).unwrap();
let res = open_image(&path);
assert!(
res.is_err(),
"open_image must fail loud on a non-image file, got Ok"
);
std::fs::remove_dir_all(&dir).ok();
}
#[test]
fn open_image_on_missing_path_errors() {
let path = std::env::temp_dir().join("4n6mount_e2e_definitely_absent_path.img");
assert!(open_image(&path).is_err(), "missing path must Err");
}
const RECOGNIZABLE_ROOT: &[&[u8]] = &[
b"$MFT",
b"$LogFile",
b"$Extend",
b"$Boot",
b"$Bitmap",
b"Windows",
b"Users",
b"EFI",
];
const MAX_NODES: usize = 5_000;
const MAX_DEPTH: u32 = 6;
const MAX_READ_SIZE: u64 = 16 * 1024 * 1024;
#[test]
fn e2e_real_image_reads_through_vfs() {
let Some(img) = std::env::var_os("FN_E2E_IMAGE") else {
eprintln!("SKIP e2e_real_image_reads_through_vfs: set FN_E2E_IMAGE=<path/to/image.E01>");
return;
};
let img = std::path::PathBuf::from(img);
if !img.is_file() {
eprintln!(
"SKIP e2e_real_image_reads_through_vfs: {} is not a file",
img.display()
);
return;
}
let mut fs = open_image(&img).expect("open_image must mount a real disk image");
let root = fs.root_ino();
let root_entries = fs.read_dir(root).expect("read_dir(root) must succeed");
assert!(!root_entries.is_empty(), "root listing must be non-empty");
let names: Vec<String> = root_entries
.iter()
.map(forensic_mount::FsDirEntry::name_str)
.collect();
eprintln!("e2e: {} root entries: {:?}", root_entries.len(), names);
assert!(
root_entries
.iter()
.any(|e| RECOGNIZABLE_ROOT.contains(&e.name.as_slice())),
"root must contain a recognizable filesystem name (one of {:?}); saw {:?}",
RECOGNIZABLE_ROOT
.iter()
.map(|n| String::from_utf8_lossy(n).to_string())
.collect::<Vec<_>>(),
names,
);
let mut queue: std::collections::VecDeque<(u64, String, u32)> =
std::collections::VecDeque::new();
let mut visited: std::collections::HashSet<u64> = std::collections::HashSet::new();
queue.push_back((root, String::new(), 0));
visited.insert(root);
let mut nodes = 0usize;
while let Some((ino, path, depth)) = queue.pop_front() {
nodes += 1;
if nodes > MAX_NODES {
eprintln!("e2e: node cap {MAX_NODES} reached before finding a readable file");
break;
}
let Ok(entries) = fs.read_dir(ino) else {
continue;
};
for e in entries {
if e.name == b"." || e.name == b".." {
continue;
}
let child_path = format!("{path}/{}", e.name_str());
match e.file_type {
FsFileType::Directory => {
if depth < MAX_DEPTH && visited.insert(e.inode) {
queue.push_back((e.inode, child_path, depth + 1));
}
}
FsFileType::RegularFile => {
let Ok(meta) = fs.metadata(e.inode) else {
continue;
};
if meta.size == 0 || meta.size > MAX_READ_SIZE {
continue;
}
let bytes = fs
.read_file(e.inode)
.expect("read_file on a real regular file must succeed");
assert!(
!bytes.is_empty(),
"read_file returned empty for {child_path:?} (metadata size {})",
meta.size
);
assert_eq!(
bytes.len() as u64,
meta.size,
"read_file byte count for {child_path:?} must equal metadata size",
);
eprintln!(
"e2e: VERIFIED regular file {child_path:?} — read {} bytes == metadata size \
(first bytes: {:02x?}) through the vfs-0.5 filesystem reader",
bytes.len(),
&bytes[..bytes.len().min(16)],
);
return;
}
_ => {}
}
}
}
panic!(
"no non-empty regular file (0 < size <= {MAX_READ_SIZE}) found within {nodes} nodes / \
depth {MAX_DEPTH} — the vfs-0.5 reader surfaced no readable file content"
);
}