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
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
86 lines
3.1 KiB
Rust
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)));
|
|
}
|