use fs_ext4::block_io::FileDevice;
use fs_ext4::fs::Filesystem;
use std::sync::Arc;
fn e2fsck_clean(image: &str, what: &str) {
fs_ext4_test_support::assert_e2fsck_clean(image, what);
}
fn indexed_root(tag: &str) -> (String, String) {
let root = fs_ext4_test_support::temp_path!("fs_ext4_233_{}_{}", tag, std::process::id());
std::fs::create_dir_all(&root).unwrap();
for i in 1..=512 {
std::fs::write(format!("{root}/file_{i}.txt"), format!("f{i:04}\n")).unwrap();
}
let image = format!("{root}.img");
std::fs::File::create(&image)
.and_then(|f| f.set_len(16 * 1024 * 1024))
.unwrap();
let mkfs = fs_ext4_test_support::oracle("mkfs.ext4")
.args([
"-q",
"-F",
"-b",
"4096",
"-O",
"dir_index,metadata_csum",
"-d",
&root,
])
.arg(&image)
.output();
assert!(
mkfs.status.success(),
"{}",
String::from_utf8_lossy(&mkfs.stderr)
);
fs_ext4_test_support::oracle("e2fsck")
.args(["-fyD", &image])
.judged()
.repaired("e2fsck -fyD indexes the root");
(root, image)
}
#[test]
fn a_root_whose_dx_tail_looks_like_a_dirent_tail_takes_creates_and_unlinks() {
let (root, image) = indexed_root("tail");
{
let fs = Filesystem::mount(Arc::new(FileDevice::open_rw(&image).unwrap())).unwrap();
let (dir, _) = fs.read_inode_verified(2).unwrap();
assert!(
dir.flags & fs_ext4::inode::InodeFlags::INDEX.bits() != 0,
"e2fsck -D did not index the root"
);
let phys = fs.map_inode_logical(&dir, 0).unwrap().unwrap();
let mut block = fs.read_block(phys).unwrap();
let limit = u16::from_le_bytes([block[32], block[33]]) as usize;
let tail = 32 + limit * 8;
assert_eq!(
tail,
block.len() - 8,
"the dx_tail is not at the block's end"
);
block[tail..tail + 4].copy_from_slice(&[0x0c, 0x00, 0x00, 0xde]);
fs.csum.patch_dx_tail(2, dir.generation, &mut block, 32);
assert!(
fs_ext4::dir::has_csum_tail(&block),
"the root does not end in a dirent-tail shape, so this tests nothing"
);
fs.dev
.write_at(phys * u64::from(fs.sb.block_size()), &block)
.unwrap();
}
e2fsck_clean(&image, "as built");
let fs = Filesystem::mount(Arc::new(FileDevice::open_rw(&image).unwrap())).unwrap();
fs.apply_create("/new.txt", 0o644).expect("create");
fs.apply_pwrite("/new.txt", 0, b"hello").expect("write");
fs.apply_unlink("/new.txt").expect("unlink the new file");
fs.apply_unlink("/file_7.txt")
.expect("unlink a file the index already held");
drop(fs);
e2fsck_clean(&image, "after create and unlinks");
let _ = std::fs::remove_dir_all(&root);
let _ = std::fs::remove_file(&image);
}
#[test]
fn a_node_shaped_leaf_does_not_stop_a_create() {
let (root, image) = indexed_root("leaf");
{
let fs = Filesystem::mount(Arc::new(FileDevice::open_rw(&image).unwrap())).unwrap();
let (dir, _) = fs.read_inode_verified(2).unwrap();
let last = dir.size / u64::from(fs.sb.block_size()) - 1;
let phys = fs.map_inode_logical(&dir, last).unwrap().unwrap();
let mut block = vec![0u8; fs.sb.block_size() as usize];
let len = block.len() as u16;
block[4..6].copy_from_slice(&len.to_le_bytes());
fs.dev
.write_at(phys * u64::from(fs.sb.block_size()), &block)
.unwrap();
}
let fs = Filesystem::mount(Arc::new(FileDevice::open_rw(&image).unwrap())).unwrap();
fs.apply_create("/after_the_empty_leaf.txt", 0o644)
.expect("create past a node-shaped leaf");
drop(fs);
let _ = std::fs::remove_dir_all(&root);
let _ = std::fs::remove_file(&image);
}