use fs_ext4::block_io::FileDevice;
use fs_ext4::{Filesystem, InodeRef};
use std::sync::Arc;
const ROOT: u32 = 2;
fn mkfs(tag: &str, features: &str) -> String {
let path =
fs_ext4_test_support::temp_path!("fs_ext4_inode_api_{tag}_{}.img", std::process::id());
std::fs::File::create(&path)
.and_then(|f| f.set_len(64 * 1024 * 1024))
.unwrap();
let out = fs_ext4_test_support::oracle("mkfs.ext4")
.args(["-q", "-F", "-b", "1024", "-O", features])
.arg(&path)
.output();
assert!(
out.status.success(),
"{}",
String::from_utf8_lossy(&out.stderr)
);
path
}
fn write_through_inodes(fs: &Filesystem) {
let a = fs.apply_mkdir_at(ROOT, b"a", 0o755).unwrap();
let b = fs.apply_mkdir_at(ROOT, b"b", 0o755).unwrap();
let sub = fs.apply_mkdir_at(a, b"sub", 0o700).unwrap();
let f = fs.apply_create_at(a, b"f", 0o644).unwrap();
let f = InodeRef::new(f, fs.stat_ino(f).unwrap().generation);
fs.apply_pwrite_ino(f, 0, &vec![0xA5; 70_000]).unwrap();
fs.apply_pwrite_ino(f, 200_000, b"sparse tail").unwrap();
fs.apply_link_at(f, b, b"g").unwrap();
fs.apply_symlink_at(a, b"fast", b"f").unwrap();
fs.apply_symlink_at(a, b"slow", "t".repeat(300).as_bytes())
.unwrap();
fs.apply_mknod_at(sub, b"fifo", 0o010644, 0, 0).unwrap();
fs.apply_create_at(sub, b"caf\xe9", 0o600).unwrap();
fs.apply_rename_at(a, b"sub", b, b"sub", false).unwrap();
fs.apply_rename_at(ROOT, b"a", ROOT, b"c", false).unwrap();
let big = fs.apply_mkdir_at(ROOT, b"big", 0o755).unwrap();
for i in 0..300 {
let name = format!("entry-with-a-reasonably-long-name-{i:04}");
fs.apply_create_at(big, name.as_bytes(), 0o644).unwrap();
}
for i in (0..300).step_by(2) {
let name = format!("entry-with-a-reasonably-long-name-{i:04}");
fs.apply_unlink_at(big, name.as_bytes()).unwrap();
}
let victim = fs.apply_create_at(b, b"victim", 0o644).unwrap();
fs.apply_pwrite_ino(victim, 0, &[1u8; 5000]).unwrap();
fs.apply_rename_at(
big,
b"entry-with-a-reasonably-long-name-0001",
b,
b"victim",
true,
)
.unwrap();
fs.apply_chmod_ino(f, 0o600).unwrap();
fs.apply_chown_ino(f, 1000, 1000).unwrap();
fs.apply_utimens_ino(f, 1_600_000_000, 1, 1_600_000_001, 2)
.unwrap();
fs.apply_truncate_ino(f, 1000).unwrap();
fs.apply_truncate_ino(f, 50_000).unwrap();
let c = fs.lookup_at(ROOT, b"c").unwrap();
fs.apply_unlink_at(c, b"f").unwrap();
assert_eq!(fs.stat_ino(f).unwrap().links_count, 1);
let sub = fs.lookup_at(b, b"sub").unwrap();
fs.apply_unlink_at(sub, b"fifo").unwrap();
fs.apply_unlink_at(sub, b"caf\xe9").unwrap();
fs.apply_rmdir_at(b, b"sub").unwrap();
}
#[test]
fn a_volume_written_through_inodes_is_clean() {
for (tag, features) in [("csum", "metadata_csum"), ("nocsum", "^metadata_csum")] {
let path = mkfs(tag, features);
{
let fs = Filesystem::mount(Arc::new(FileDevice::open_rw(&path).unwrap())).unwrap();
write_through_inodes(&fs);
}
fs_ext4_test_support::assert_e2fsck_clean(&path, &format!("inode API, {tag}"));
std::fs::remove_file(&path).ok();
}
}