use fs_ext4::bgd;
use fs_ext4::block_io::{BlockDevice, FileDevice};
use fs_ext4::error::Result;
use fs_ext4::fs::Filesystem;
use fs_ext4::inode::Inode;
use fs_ext4::path;
use std::path::Path;
use std::sync::Arc;
const TEST_IMAGE: &str = "test-disks/ext4-largedir.img";
fn open_or_skip() -> Option<(Arc<dyn BlockDevice>, Filesystem)> {
if !Path::new(TEST_IMAGE).exists() {
eprintln!("skip: {TEST_IMAGE} not built; run build-ext4-feature-images.sh largedir");
return None;
}
let dev = Arc::new(FileDevice::open(TEST_IMAGE).expect("open largedir image"));
let dev_dyn: Arc<dyn BlockDevice> = dev.clone();
let fs = Filesystem::mount(dev_dyn.clone()).expect("mount");
Some((dev_dyn, fs))
}
fn inode_reader(fs: &Filesystem) -> impl FnMut(u32) -> Result<Inode> + '_ {
move |ino: u32| -> Result<Inode> {
let (block, offset) = bgd::locate_inode(&fs.sb, &fs.groups, ino)?;
let block_data = fs.read_block(block)?;
let inode_size = fs.sb.inode_size as usize;
let off = offset as usize;
Inode::parse(&block_data[off..off + inode_size])
}
}
fn resolve(dev: &dyn BlockDevice, fs: &Filesystem, p: &str) -> u32 {
let mut reader = inode_reader(fs);
path::lookup(dev, &fs.sb, &mut reader, p).unwrap_or_else(|e| panic!("resolve {p}: {e}"))
}
#[test]
fn largedir_mount_succeeds_and_sees_control_file() {
let Some((dev, fs)) = open_or_skip() else {
return;
};
let ino = resolve(dev.as_ref(), &fs, "/small.txt");
assert!(ino >= 2);
}
#[test]
fn htree_resolves_boundary_entries() {
let Some((dev, fs)) = open_or_skip() else {
return;
};
for name in [
"file_00001.txt",
"file_00002.txt",
"file_35000.txt",
"file_69999.txt",
"file_70000.txt",
] {
let p = format!("/huge/{name}");
let ino = resolve(dev.as_ref(), &fs, &p);
assert!(ino >= 2, "resolve {p} -> {ino}");
}
}
#[test]
fn htree_random_sample_all_resolve() {
let Some((dev, fs)) = open_or_skip() else {
return;
};
for i in (1..=70_000u32).step_by(517) {
let p = format!("/huge/file_{i:05}.txt");
let ino = resolve(dev.as_ref(), &fs, &p);
assert!(ino >= 2, "resolve {p} -> {ino}");
}
}
#[test]
fn missing_entry_returns_notfound() {
let Some((dev, fs)) = open_or_skip() else {
return;
};
let mut reader = inode_reader(&fs);
let err = path::lookup(dev.as_ref(), &fs.sb, &mut reader, "/huge/file_99999.txt").unwrap_err();
use fs_ext4::error::Error;
assert!(matches!(err, Error::NotFound));
}