Núcleo em Rust somente leitura: NTFS, FAT, exFAT, partições MBR/GPT e perdidas, carving por estrutura, fusão sem duplicatas, confiança por arquivo, imagem forense, pausar/retomar. CLI `guara`, UI web/Tauri, CI e releases por tag. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
89 lines
2.5 KiB
Rust
89 lines
2.5 KiB
Rust
//! Recuperação de ponta a ponta sobre imagens reais (FAT32, NTFS, ext4).
|
|
|
|
mod common;
|
|
use common::*;
|
|
use guara_core::*;
|
|
|
|
fn check(fs: &str, only_carvable: bool) {
|
|
let Some(dir) = fixtures() else { return };
|
|
let img = dir.join(format!("{fs}.img"));
|
|
let before = sha256_file(&img);
|
|
let res = scan_path(&img, &ScanOptions::default());
|
|
let out = tempfile::tempdir().unwrap();
|
|
let rep = recover_all(&img, &res.found, out.path());
|
|
assert!(rep.failed.is_empty(), "falhas: {:?}", rep.failed);
|
|
let got = hashes_in(out.path());
|
|
for (hash, path) in expected(&dir, fs) {
|
|
let carvable = [".jpg", ".png", ".pdf", ".docx", ".db"]
|
|
.iter()
|
|
.any(|e| path.ends_with(e));
|
|
if only_carvable && !carvable {
|
|
continue;
|
|
}
|
|
assert!(
|
|
got.contains_key(&hash),
|
|
"[{fs}] não recuperado bit a bit: {path}"
|
|
);
|
|
}
|
|
// Os dois métodos juntos não podem listar o mesmo conteúdo duas vezes.
|
|
let mut seen = std::collections::HashSet::new();
|
|
for f in &res.found {
|
|
if let Some(o) = f.first_offset() {
|
|
assert!(seen.insert(o), "[{fs}] achado duplicado no offset {o}");
|
|
}
|
|
}
|
|
assert_eq!(before, sha256_file(&img), "a origem foi modificada!");
|
|
}
|
|
|
|
#[test]
|
|
fn fat32_recupera_apagados_com_nome_e_pasta() {
|
|
check("fat32", false);
|
|
let Some(dir) = fixtures() else { return };
|
|
let res = scan_path(&dir.join("fat32.img"), &ScanOptions::default());
|
|
assert!(res
|
|
.found
|
|
.iter()
|
|
.any(|f| f.dir.as_deref() == Some("Documentos/Trabalho") && f.name == "relatorio.pdf"));
|
|
assert!(
|
|
res.found.iter().all(|f| f.name != "mantida.jpg"),
|
|
"arquivo existente não deve ser listado"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn ntfs_recupera_apagados() {
|
|
check("ntfs", false);
|
|
}
|
|
|
|
#[test]
|
|
fn ext4_recupera_por_assinatura() {
|
|
check("ext4", true);
|
|
}
|
|
|
|
#[test]
|
|
fn varredura_rapida_nao_faz_carving() {
|
|
let Some(dir) = fixtures() else { return };
|
|
let res = scan_path(
|
|
&dir.join("ext4.img"),
|
|
&ScanOptions {
|
|
mode: ScanMode::Quick,
|
|
..Default::default()
|
|
},
|
|
);
|
|
assert!(res.found.is_empty());
|
|
let res = scan_path(
|
|
&dir.join("ntfs.img"),
|
|
&ScanOptions {
|
|
mode: ScanMode::Quick,
|
|
..Default::default()
|
|
},
|
|
);
|
|
assert_eq!(
|
|
res.found
|
|
.iter()
|
|
.filter(|f| f.origin == Origin::Ntfs)
|
|
.count(),
|
|
7
|
|
);
|
|
}
|