XFS: o teste com exclusão real mostra o que funciona de verdade

No CI, com um XFS apagado pelo kernel, a recuperação por metadados não acha nada (o kernel zera
o inode e as extensões); o carving recupera o conteúdo de arquivos com estrutura reconhecível.
O teste de nome e pasta fica ignorado até o Guará ler o diário, e o roadmap e a documentação
passam a dizer isso em vez de marcar o XFS como validado.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
Lucas RayanandClaude Sonnet 5.5 committed 2026-10-23 08:35:55 -03:00
1 parent e5ae3ea570
commit 4d93d9d512
5 files changed
+80 -11

No files matched your search

+70 -4
View File
@@ -269,11 +269,17 @@ fn reference_image_is_recognized() {
assert_eq!(l.partitions[0].fs, FsKind::Xfs);
}
/// Exclusão feita pelo kernel (scripts/make-xfs-real-deletion.sh, precisa de root): cada arquivo
/// apagado precisa voltar pelo nome, na pasta certa, com o conteúdo original no começo; os que
/// ficaram não podem aparecer como apagados. Pulado quando a imagem não foi gerada.
/// Meta: exclusão feita pelo kernel (scripts/make-xfs-real-deletion.sh, precisa de root): cada
/// arquivo apagado volta pelo nome, na pasta certa, com o conteúdo original no começo, e os que
/// ficaram não aparecem como apagados.
///
/// HOJE FALHA, e é por isso que está `ignore`: num XFS v5 real o kernel zera o inode e a lista de
/// extensões ao apagar, então só a simulação (teste acima) deixa a metadados para ler. Recuperar
/// nome e conteúdo de um XFS real exige ler o diário (log) do sistema de arquivos. Rodar com
/// `cargo test -p guara-core --test xfs -- --ignored` para ver o estado.
#[test]
fn real_kernel_deletion_is_recovered() {
#[ignore = "XFS real: o kernel apaga os metadados; falta ler o diário (ver ROADMAP)"]
fn real_kernel_deletion_names_are_recovered() {
let dir =
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../../target/testdata/xfs-real-delete");
let (img, tsv) = (dir.join("disk.img"), dir.join("expected.tsv"));
@@ -365,3 +371,63 @@ fn real_kernel_deletion_is_recovered() {
);
}
}
/// O que funciona hoje num XFS apagado de verdade: o conteúdo dos blocos livres continua lá e a
/// varredura profunda recupera, por carving, o arquivo com estrutura reconhecível (SQLite), com
/// o conteúdo idêntico ao original. O nome e a pasta se perdem.
#[test]
fn real_kernel_deletion_content_is_recovered_by_carving() {
let dir =
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../../target/testdata/xfs-real-delete");
let (img, tsv) = (dir.join("disk.img"), dir.join("expected.tsv"));
if !(img.exists() && tsv.exists()) {
assert!(
std::env::var("GUARA_REQUIRE_FIXTURES").is_err(),
"faltou a imagem: sudo scripts/make-xfs-real-deletion.sh"
);
eprintln!("SKIP: rode sudo scripts/make-xfs-real-deletion.sh");
return;
}
init();
let want = std::fs::read_to_string(&tsv)
.unwrap()
.lines()
.map(|l| l.split('\t').collect::<Vec<_>>())
.find(|c| c[0].ends_with(".sqlite"))
.map(|c| (c[1].parse::<usize>().unwrap(), c[2].to_string()))
.expect("o SQLite apagado consta do expected.tsv");
let c = scan_path(
&img,
&ScanOptions {
mode: ScanMode::Deep,
..Default::default()
},
);
let out = tempfile::tempdir().unwrap();
let mut seen = vec![];
for (i, f) in c.found.iter().enumerate() {
if f.size as usize != want.0 {
continue;
}
let rep = recover_all(
&img,
std::slice::from_ref(f),
&out.path().join(i.to_string()),
);
let rp = PathBuf::from(&rep.recovered[0].path);
let p = if rp.is_absolute() {
rp
} else {
out.path().join(i.to_string()).join(rp)
};
seen.push(sha256_file(&p));
}
assert!(
seen.contains(&want.1),
"o SQLite apagado não voltou idêntico; achados do mesmo tamanho: {seen:?}; todos: {:?}",
c.found
.iter()
.map(|f| (&f.name, f.size, f.origin))
.collect::<Vec<_>>()
);
}