Files
Lucas RayanandClaude Sonnet 5 6328220c60
CI / Format and clippy (push) Successful in 1m57s
CI / Dependency audit (push) Successful in 13s
CI / Check desktop app (Linux) (push) Successful in 3m54s
CI / Tests (Linux) (push) Failing after 7m25s
Release / Create release (push) Successful in 6s
Release / CLI (linux-aarch64) (push) Successful in 1m48s
Release / CLI (linux-x86_64) (push) Successful in 2m39s
Release / Desktop app (linux-aarch64) (push) Successful in 10m9s
Release / Desktop app (linux-x86_64) (push) Successful in 12m20s
Release / Desktop app (windows) (push) Successful in 7m32s
Release / Build latest.json (push) Successful in 5s
chore(i18n): finish translating code comments and scripts to English
Completes the English-only standardization for code, comments, scripts
and CI templates (UI/CLI-facing strings stay bilingual pt-BR/en,
untouched). Covers the remaining crates/, apps/gui/ui/ and
apps/gui/src-tauri/ comments, .gitea/ workflow and template comments,
.githooks/ hooks, and the scripts/ source comments for the files
already renamed in a prior commit (check.sh, generate-logo.py,
install-hooks.sh).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VAyr8d7HuAdUGB6aA5xmDD
2026-10-30 20:43:02 -03:00

86 lines
3.1 KiB
Rust

//! exFAT: injects a deleted directory entry set (`0x05`/`0x40`/`0x41`) into a `mkfs.exfat` image.
mod common;
use common::*;
use guara_core::*;
use std::process::Command;
#[test]
fn exfat_apagado_e_recuperado_com_nome() {
let dir = tempfile::tempdir().unwrap();
let img = dir.path().join("e.img");
let ok = Command::new("truncate")
.args(["-s", "32M"])
.arg(&img)
.status()
.map(|s| s.success())
.unwrap_or(false)
&& Command::new("mkfs.exfat")
.arg(&img)
.output()
.map(|o| o.status.success())
.unwrap_or(false);
if !ok {
eprintln!("SKIP: mkfs.exfat ausente");
return;
}
let mut d = std::fs::read(&img).unwrap();
let ss = 1usize << d[108];
let cs = ss << d[109];
let heap = u32::from_le_bytes(d[88..92].try_into().unwrap()) as usize * ss;
let root = u32::from_le_bytes(d[96..100].try_into().unwrap()) as usize;
let root_off = heap + (root - 2) * cs;
// Find the first unused 32-byte slot in the root directory cluster.
let mut slot = root_off;
while d[slot] != 0 {
slot += 32;
}
let content: Vec<u8> = (0..70_000u32)
.map(|i| (i.wrapping_mul(2654435761) >> 13) as u8)
.collect();
let first_cluster = 300u32;
let data_off = heap + (first_cluster as usize - 2) * cs;
d[data_off..data_off + content.len()].copy_from_slice(&content);
let name = "relatório final.txt";
let units: Vec<u16> = name.encode_utf16().collect();
// `0x05` = deleted file entry (`0x85` with in-use bit cleared), followed by `0x40` stream and `0x41` name entries.
let n_name = units.len().div_ceil(15);
d[slot] = 0x05;
d[slot + 1] = (1 + n_name) as u8;
d[slot + 12..slot + 14].copy_from_slice(&0x6000u16.to_le_bytes()); // timestamp
d[slot + 14..slot + 16].copy_from_slice(&((44u16 << 9) | (6 << 5) | 15).to_le_bytes()); // 2024-06-15
let st = slot + 32;
d[st] = 0x40;
d[st + 1] = 0x03; // `NoFatChain` bit set (contiguous allocation)
d[st + 3] = units.len() as u8;
d[st + 20..st + 24].copy_from_slice(&first_cluster.to_le_bytes());
d[st + 24..st + 32].copy_from_slice(&(content.len() as u64).to_le_bytes());
d[st + 8..st + 16].copy_from_slice(&(content.len() as u64).to_le_bytes());
for k in 0..n_name {
let e = slot + 64 + k * 32;
d[e] = 0x41;
for (j, u) in units.iter().skip(k * 15).take(15).enumerate() {
d[e + 2 + j * 2..e + 4 + j * 2].copy_from_slice(&u.to_le_bytes());
}
}
std::fs::write(&img, &d).unwrap();
let res = scan_path(
&img,
&ScanOptions {
mode: ScanMode::Quick,
..Default::default()
},
);
assert!(res.warnings.is_empty(), "{:?}", res.warnings);
assert_eq!(res.found.len(), 1);
let f = &res.found[0];
assert_eq!(f.name, "relatório final.txt");
assert_eq!(f.origin, Origin::Exfat);
assert_eq!(f.size, content.len() as u64);
assert!(f.modified.is_some());
let out = tempfile::tempdir().unwrap();
recover_all(&img, &res.found, out.path());
assert!(hashes_in(out.path()).contains_key(&sha256(&content)));
}