Formatação (cargo fmt) e ajustes do clippy para o CI da 1.0.0
Release / Cria a release (push) Canceled after 0s
Release / CLI (linux-aarch64) (push) Canceled after 0s
Release / CLI (linux-x86_64) (push) Canceled after 0s
Release / CLI (macos-aarch64) (push) Canceled after 0s
Release / CLI (macos-x86_64) (push) Canceled after 0s
Release / CLI (windows-x86_64) (push) Canceled after 0s
Release / App desktop (linux-x86_64) (push) Canceled after 0s
Release / App desktop (macos-aarch64) (push) Canceled after 0s
Release / App desktop (macos-x86_64) (push) Canceled after 0s
Release / App desktop (windows-x86_64) (push) Canceled after 0s
Release / Publica a release (push) Canceled after 0s
Release / Cria a release (push) Canceled after 0s
Release / CLI (linux-aarch64) (push) Canceled after 0s
Release / CLI (linux-x86_64) (push) Canceled after 0s
Release / CLI (macos-aarch64) (push) Canceled after 0s
Release / CLI (macos-x86_64) (push) Canceled after 0s
Release / CLI (windows-x86_64) (push) Canceled after 0s
Release / App desktop (linux-x86_64) (push) Canceled after 0s
Release / App desktop (macos-aarch64) (push) Canceled after 0s
Release / App desktop (macos-x86_64) (push) Canceled after 0s
Release / App desktop (windows-x86_64) (push) Canceled after 0s
Release / Publica a release (push) Canceled after 0s
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
74218e7beb
commit
beb83fef92
49 files changed
+3806
-854
No files matched your search
+102
-16
@@ -8,7 +8,11 @@ use std::path::PathBuf;
|
||||
use std::process::Command;
|
||||
|
||||
fn have(t: &str) -> bool {
|
||||
Command::new("sh").args(["-c", &format!("command -v {t}")]).output().map(|o| o.status.success()).unwrap_or(false)
|
||||
Command::new("sh")
|
||||
.args(["-c", &format!("command -v {t}")])
|
||||
.output()
|
||||
.map(|o| o.status.success())
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
fn data(seed: u64, n: usize) -> Vec<u8> {
|
||||
@@ -36,7 +40,11 @@ fn be64(b: &[u8], o: usize) -> u64 {
|
||||
fn unpack(r: &[u8]) -> (u64, u64, u64) {
|
||||
let hi = be64(r, 0);
|
||||
let lo = be64(r, 8);
|
||||
((hi >> 9) & ((1 << 54) - 1), ((hi & 0x1FF) << 43) | (lo >> 21), lo & 0x1F_FFFF)
|
||||
(
|
||||
(hi >> 9) & ((1 << 54) - 1),
|
||||
((hi & 0x1FF) << 43) | (lo >> 21),
|
||||
lo & 0x1F_FFFF,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -55,7 +63,11 @@ fn deleted_file_is_recovered_from_free_inode_and_stale_directory_entry() {
|
||||
std::fs::write(src.join(format!("a_longa_{i}.txt")), format!("arquivo {i}")).unwrap();
|
||||
}
|
||||
let mut proto = String::from("/dev/null\n0 0\nd--755 0 0\ndocs d--755 0 0\n");
|
||||
let mut names: Vec<_> = std::fs::read_dir(&src).unwrap().flatten().map(|e| e.file_name().to_string_lossy().to_string()).collect();
|
||||
let mut names: Vec<_> = std::fs::read_dir(&src)
|
||||
.unwrap()
|
||||
.flatten()
|
||||
.map(|e| e.file_name().to_string_lossy().to_string())
|
||||
.collect();
|
||||
names.sort();
|
||||
for n in &names {
|
||||
proto.push_str(&format!(" {n} ---644 0 0 {}\n", src.join(n).display()));
|
||||
@@ -64,8 +76,16 @@ fn deleted_file_is_recovered_from_free_inode_and_stale_directory_entry() {
|
||||
let pf = dir.path().join("proto");
|
||||
std::fs::write(&pf, proto).unwrap();
|
||||
let img = dir.path().join("x.img");
|
||||
std::fs::File::create(&img).unwrap().set_len(320 << 20).unwrap();
|
||||
let o = Command::new("mkfs.xfs").args(["-q", "-f", "-p"]).arg(&pf).arg(&img).output().unwrap();
|
||||
std::fs::File::create(&img)
|
||||
.unwrap()
|
||||
.set_len(320 << 20)
|
||||
.unwrap();
|
||||
let o = Command::new("mkfs.xfs")
|
||||
.args(["-q", "-f", "-p"])
|
||||
.arg(&pf)
|
||||
.arg(&img)
|
||||
.output()
|
||||
.unwrap();
|
||||
assert!(o.status.success(), "{}", String::from_utf8_lossy(&o.stderr));
|
||||
|
||||
let db = |cmds: &[&str]| -> String {
|
||||
@@ -78,11 +98,39 @@ fn deleted_file_is_recovered_from_free_inode_and_stale_directory_entry() {
|
||||
String::from_utf8_lossy(&c.output().unwrap().stdout).to_string()
|
||||
};
|
||||
let nc = db(&["blockget -n", "ncheck"]);
|
||||
let ino: u64 = nc.lines().find(|l| l.ends_with("docs/relatorio.dat")).unwrap().split_whitespace().next().unwrap().parse().unwrap();
|
||||
let ino: u64 = nc
|
||||
.lines()
|
||||
.find(|l| l.ends_with("docs/relatorio.dat"))
|
||||
.unwrap()
|
||||
.split_whitespace()
|
||||
.next()
|
||||
.unwrap()
|
||||
.parse()
|
||||
.unwrap();
|
||||
let conv = db(&[&format!("convert ino {ino} byte")]);
|
||||
let ino_off = usize::from_str_radix(conv.split("0x").nth(1).unwrap().split_whitespace().next().unwrap().trim_end_matches(')'), 16).unwrap();
|
||||
let ino_off = usize::from_str_radix(
|
||||
conv.split("0x")
|
||||
.nth(1)
|
||||
.unwrap()
|
||||
.split_whitespace()
|
||||
.next()
|
||||
.unwrap()
|
||||
.trim_end_matches(')'),
|
||||
16,
|
||||
)
|
||||
.unwrap();
|
||||
let sbv = db(&["sb 0", "p agblocks agblklog inopblog"]);
|
||||
let num = |k: &str| -> u64 { sbv.lines().find(|l| l.starts_with(k)).unwrap().split('=').nth(1).unwrap().trim().parse().unwrap() };
|
||||
let num = |k: &str| -> u64 {
|
||||
sbv.lines()
|
||||
.find(|l| l.starts_with(k))
|
||||
.unwrap()
|
||||
.split('=')
|
||||
.nth(1)
|
||||
.unwrap()
|
||||
.trim()
|
||||
.parse()
|
||||
.unwrap()
|
||||
};
|
||||
let (agblocks, agblklog, inopblog) = (num("agblocks"), num("agblklog"), num("inopblog"));
|
||||
|
||||
let mut b = std::fs::read(&img).unwrap();
|
||||
@@ -90,8 +138,10 @@ fn deleted_file_is_recovered_from_free_inode_and_stale_directory_entry() {
|
||||
// extents do arquivo (no inode, formato 3)
|
||||
assert_eq!(b[ino_off + 5], 2);
|
||||
let nex = be32(&b[ino_off..], 76) as usize;
|
||||
let exts: Vec<_> = (0..nex).map(|i| unpack(&b[ino_off + 176 + i * 16..])).collect();
|
||||
assert_eq!(exts.iter().map(|e| e.2).sum::<u64>() * 4096 >= 150_000, true);
|
||||
let exts: Vec<_> = (0..nex)
|
||||
.map(|i| unpack(&b[ino_off + 176 + i * 16..]))
|
||||
.collect();
|
||||
assert!(exts.iter().map(|e| e.2).sum::<u64>() * 4096 >= 150_000);
|
||||
// 1) libera o inode como o kernel: modo 0, links 0, tamanho 0, 0 extents
|
||||
b[ino_off + 2..ino_off + 4].fill(0);
|
||||
b[ino_off + 16..ino_off + 20].fill(0);
|
||||
@@ -125,7 +175,14 @@ fn deleted_file_is_recovered_from_free_inode_and_stale_directory_entry() {
|
||||
assert_eq!(blevel, 1);
|
||||
let bb = ag_off + broot * bs;
|
||||
let nrec = be16(&b[bb..], 6) as usize;
|
||||
let mut recs: Vec<(u32, u32)> = (0..nrec).map(|i| (be32(&b[bb + 56 + i * 8..], 0), be32(&b[bb + 56 + i * 8..], 4))).collect();
|
||||
let mut recs: Vec<(u32, u32)> = (0..nrec)
|
||||
.map(|i| {
|
||||
(
|
||||
be32(&b[bb + 56 + i * 8..], 0),
|
||||
be32(&b[bb + 56 + i * 8..], 4),
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
for e in &exts {
|
||||
recs.push(((e.1 & ((1 << agblklog) - 1)) as u32, e.2 as u32));
|
||||
}
|
||||
@@ -144,18 +201,38 @@ fn deleted_file_is_recovered_from_free_inode_and_stale_directory_entry() {
|
||||
b[bb + 6..bb + 8].copy_from_slice(&(merged.len() as u16).to_be_bytes());
|
||||
// 4) entrada de diretório liberada: perde os 4 primeiros bytes do inode (viram marca + tamanho)
|
||||
let name = b"relatorio.dat";
|
||||
let pos = (0..b.len() - 20).find(|&p| &b[p + 9..p + 9 + name.len()] == name && b[p + 8] as usize == name.len() && be64(&b[p..], 0) == ino).expect("entrada do diretório");
|
||||
let pos = (0..b.len() - 20)
|
||||
.find(|&p| {
|
||||
&b[p + 9..p + 9 + name.len()] == name
|
||||
&& b[p + 8] as usize == name.len()
|
||||
&& be64(&b[p..], 0) == ino
|
||||
})
|
||||
.expect("entrada do diretório");
|
||||
let size = (8 + 1 + name.len() + 1 + 2usize).div_ceil(8) * 8;
|
||||
b[pos..pos + 2].copy_from_slice(&[0xFF, 0xFF]);
|
||||
b[pos + 2..pos + 4].copy_from_slice(&(size as u16).to_be_bytes());
|
||||
std::fs::write(&img, &b).unwrap();
|
||||
|
||||
let c = scan_path(&img, &ScanOptions { mode: ScanMode::Deep, ..Default::default() });
|
||||
let c = scan_path(
|
||||
&img,
|
||||
&ScanOptions {
|
||||
mode: ScanMode::Deep,
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
let f = c
|
||||
.found
|
||||
.iter()
|
||||
.find(|f| f.origin == Origin::Xfs)
|
||||
.unwrap_or_else(|| panic!("nada do XFS: {:?}", c.found.iter().map(|f| (&f.name, f.origin)).collect::<Vec<_>>()));
|
||||
.unwrap_or_else(|| {
|
||||
panic!(
|
||||
"nada do XFS: {:?}",
|
||||
c.found
|
||||
.iter()
|
||||
.map(|f| (&f.name, f.origin))
|
||||
.collect::<Vec<_>>()
|
||||
)
|
||||
});
|
||||
assert_eq!(f.name, "relatorio.dat");
|
||||
assert_eq!(f.dir.as_deref(), Some("/docs"));
|
||||
assert!(f.confidence >= Confidence::Medium, "{:?}", f.note);
|
||||
@@ -168,7 +245,10 @@ fn deleted_file_is_recovered_from_free_inode_and_stale_directory_entry() {
|
||||
assert!(got.starts_with(&doc), "conteúdo inicial difere");
|
||||
assert_eq!(got.len() % 4096, 0);
|
||||
// Só um arquivo apagado.
|
||||
assert_eq!(c.found.iter().filter(|f| f.origin == Origin::Xfs).count(), 1);
|
||||
assert_eq!(
|
||||
c.found.iter().filter(|f| f.origin == Origin::Xfs).count(),
|
||||
1
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -178,7 +258,13 @@ fn reference_image_is_recognized() {
|
||||
return;
|
||||
}
|
||||
init();
|
||||
let c = scan_path(&p, &ScanOptions { mode: ScanMode::Quick, ..Default::default() });
|
||||
let c = scan_path(
|
||||
&p,
|
||||
&ScanOptions {
|
||||
mode: ScanMode::Quick,
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
let l = c.layout.unwrap();
|
||||
assert_eq!(l.partitions[0].fs, FsKind::Xfs);
|
||||
}
|
||||
Reference in new issue
Block a user