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:
1 parent
e5ae3ea570
commit
4d93d9d512
5 files changed
+80
-11
No files matched your search
@@ -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<_>>()
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user