chore(i18n): finish translating code comments and scripts to English
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
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
This commit is contained in:
1 parent
da212e6926
commit
6328220c60
129 files changed
+1961
-1834
No files matched your search
@@ -1,10 +1,10 @@
|
||||
//! ext2/3/4: arquivos apagados com nome, por três caminhos que se completam:
|
||||
//! ext2/3/4: recovers deleted files with names via three complementary techniques:
|
||||
//!
|
||||
//! 1. inodes com `dtime` cujo mapa de extents ainda tem sobras legíveis;
|
||||
//! 2. cópias antigas do inode guardadas no diário (jbd2), de antes da exclusão;
|
||||
//! 3. entradas de diretório "escondidas" na folga do `rec_len` (dão o nome).
|
||||
//! 1. deleted inodes (`dtime != 0`) whose extent tree or block pointers still have remnants;
|
||||
//! 2. pre-deletion inode copies preserved in the jbd2 journal;
|
||||
//! 3. unlinked directory entries hidden inside `rec_len` slack space (recovering filenames).
|
||||
//!
|
||||
//! Também devolve o mapa de blocos livres, para o carving varrer só o que está livre.
|
||||
//! Also returns the free-block map so file carving only scans unallocated blocks.
|
||||
|
||||
use super::{make_found, merge_extents, Ctx, NewFound, VolumeScan};
|
||||
use crate::signatures;
|
||||
@@ -103,7 +103,7 @@ struct Ext<'a> {
|
||||
journal_inum: u32,
|
||||
}
|
||||
|
||||
/// Sequência de (bloco lógico, bloco físico, quantidade, não-inicializado).
|
||||
/// Sequence of `(logical_block, physical_block, block_count, is_uninitialized)`.
|
||||
type ExtentList = Vec<(u64, u64, u64, bool)>;
|
||||
|
||||
impl<'a> Ext<'a> {
|
||||
@@ -201,7 +201,7 @@ impl<'a> Ext<'a> {
|
||||
Inode::parse(&self.inode_bytes(ino)?)
|
||||
}
|
||||
|
||||
/// Extents de uma árvore ext4 (raiz em `node`, 60 bytes ou um bloco).
|
||||
/// Collects extents from an ext4 extent tree (`node` is the 60-byte `i_block` root or a block).
|
||||
fn walk_extent_node(
|
||||
&self,
|
||||
node: &[u8],
|
||||
@@ -248,7 +248,7 @@ impl<'a> Ext<'a> {
|
||||
true
|
||||
}
|
||||
|
||||
/// Blocos de um inode ext2/3 (mapa de blocos clássico).
|
||||
/// Collects data blocks from an ext2/3 indirect block map.
|
||||
fn block_map(&self, blk: &[u8; 60], max_blocks: u64) -> ExtentList {
|
||||
let mut v: Vec<u64> = vec![];
|
||||
let ptr = |i: usize| le32(blk, i * 4) as u64;
|
||||
@@ -308,7 +308,7 @@ impl<'a> Ext<'a> {
|
||||
out
|
||||
}
|
||||
|
||||
/// Extents do arquivo (ordenados pelo bloco lógico), cortados em `size` bytes.
|
||||
/// Returns file byte extents (ordered by logical block) truncated to `size` bytes.
|
||||
fn file_extents(&self, ino: &Inode, size: u64) -> Vec<Extent> {
|
||||
let need = size.div_ceil(self.bs);
|
||||
let mut list: ExtentList = vec![];
|
||||
@@ -351,7 +351,7 @@ impl<'a> Ext<'a> {
|
||||
out
|
||||
}
|
||||
|
||||
/// Mapa de blocos livres (bitmap), em extents absolutos.
|
||||
/// Free-block map (from block bitmaps) as absolute byte extents.
|
||||
fn free_map(&self, ctx: &Ctx) -> (Vec<Extent>, Vec<bool>) {
|
||||
let mut free: Vec<Extent> = vec![];
|
||||
let mut allocated_all: Vec<bool> = vec![];
|
||||
@@ -418,7 +418,7 @@ struct DirEnt {
|
||||
live: bool,
|
||||
}
|
||||
|
||||
/// Lê as entradas de um bloco de diretório, inclusive as escondidas na folga.
|
||||
/// Parses directory entries in a directory block, including unlinked entries in `rec_len` slack.
|
||||
fn parse_dir_block(b: &[u8], max_ino: u64, out: &mut Vec<DirEnt>) {
|
||||
let mut off = 0usize;
|
||||
while off + 8 <= b.len() {
|
||||
@@ -437,11 +437,11 @@ fn parse_dir_block(b: &[u8], max_ino: u64, out: &mut Vec<DirEnt>) {
|
||||
live: true,
|
||||
});
|
||||
}
|
||||
// Folga: entradas apagadas ficam coladas depois do fim "real" desta.
|
||||
// Slack space: unlinked entries remain tucked right after the active entry's true length.
|
||||
let mut s = off + if ino != 0 || nlen > 0 { real } else { 8 };
|
||||
let end = off + rec;
|
||||
if ino == 0 {
|
||||
s = end; // entrada nula: sem folga confiável
|
||||
s = end; // zeroed entry: no reliable slack to scan
|
||||
}
|
||||
while s + 8 <= end {
|
||||
let dino = le32(b, s) as u64;
|
||||
@@ -472,9 +472,9 @@ fn parse_dir_block(b: &[u8], max_ino: u64, out: &mut Vec<DirEnt>) {
|
||||
}
|
||||
|
||||
struct Names {
|
||||
/// inode -> (pasta, nome) de entradas vivas.
|
||||
/// `inode -> (parent_dir, name)` from live directory entries.
|
||||
live: HashMap<u64, (String, String)>,
|
||||
/// inode -> (pasta, nome) de entradas apagadas (mais recente por varredura).
|
||||
/// `inode -> (parent_dir, name)` from unlinked slack directory entries.
|
||||
dead: HashMap<u64, (String, String)>,
|
||||
}
|
||||
|
||||
@@ -531,9 +531,9 @@ fn walk_dirs(ext: &Ext, ctx: &Ctx) -> Names {
|
||||
names
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------- diário
|
||||
// ---------------------------------------------------------------- journal
|
||||
|
||||
/// Cópias de inodes guardadas no diário: inode -> lista (ordem do diário) de registros brutos.
|
||||
/// Inode copies found in the jbd2 journal: `inode_num -> Vec<raw_inode_bytes>` in journal order.
|
||||
fn journal_inodes(ext: &Ext, ctx: &Ctx) -> HashMap<u64, Vec<Vec<u8>>> {
|
||||
let mut out: HashMap<u64, Vec<Vec<u8>>> = HashMap::new();
|
||||
if ext.journal_inum == 0 {
|
||||
@@ -543,7 +543,7 @@ fn journal_inodes(ext: &Ext, ctx: &Ctx) -> HashMap<u64, Vec<Vec<u8>>> {
|
||||
return out;
|
||||
};
|
||||
let jext = ext.file_extents(&jino, jino.size.min(1 << 30));
|
||||
// Blocos físicos do diário em ordem lógica.
|
||||
// Physical blocks of the journal in logical sequence.
|
||||
let mut jblocks: Vec<u64> = vec![];
|
||||
for e in &jext {
|
||||
if e.sparse {
|
||||
@@ -576,9 +576,9 @@ fn journal_inodes(ext: &Ext, ctx: &Ctx) -> HashMap<u64, Vec<Vec<u8>>> {
|
||||
8 + if is64 { 4 } else { 0 }
|
||||
};
|
||||
let have_csum = csum_v2 || csum_v3;
|
||||
// Tabela de inodes: faixa de blocos de cada grupo.
|
||||
// Inode table block ranges for each block group.
|
||||
let itable_blocks = (ext.ipg * ext.inode_size).div_ceil(ext.bs);
|
||||
let mut tables: Vec<(u64, u64, u64)> = vec![]; // (início, fim, primeiro inode)
|
||||
let mut tables: Vec<(u64, u64, u64)> = vec![]; // (start_block, end_block, first_inode_num)
|
||||
for (g, gr) in ext.groups.iter().enumerate() {
|
||||
tables.push((
|
||||
gr.inode_table,
|
||||
@@ -588,9 +588,9 @@ fn journal_inodes(ext: &Ext, ctx: &Ctx) -> HashMap<u64, Vec<Vec<u8>>> {
|
||||
}
|
||||
let n = jblocks.len();
|
||||
let first = (be32(&jsb, 20) as usize).clamp(1, n);
|
||||
let mut seqs: Vec<(u32, usize)> = vec![]; // (sequência, índice do descritor)
|
||||
let mut seqs: Vec<(u32, usize)> = vec![]; // (commit_sequence, descriptor_index)
|
||||
let mut idx = first;
|
||||
// Varredura física: qualquer bloco que seja descritor, em ordem do diário.
|
||||
// Scan journal blocks for descriptor blocks in sequence order.
|
||||
while idx < n {
|
||||
if idx % 4096 == 4095 && (ctx.cancelled)() {
|
||||
break;
|
||||
@@ -664,7 +664,7 @@ fn journal_inodes(ext: &Ext, ctx: &Ctx) -> HashMap<u64, Vec<Vec<u8>>> {
|
||||
out
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------- varredura
|
||||
// -------------------------------------------------------------- scan
|
||||
|
||||
pub fn scan(ctx: &mut Ctx, base: u64, part_len: u64) -> VolumeScan {
|
||||
let mut res = VolumeScan::default();
|
||||
@@ -680,7 +680,7 @@ pub fn scan(ctx: &mut Ctx, base: u64, part_len: u64) -> VolumeScan {
|
||||
let mut emitted: HashSet<u64> = HashSet::new();
|
||||
let mut seen_extents: HashSet<(u64, u64)> = HashSet::new();
|
||||
|
||||
// 1) Inodes apagados na tabela atual.
|
||||
// 1) Deleted inodes in the current inode table.
|
||||
let total = ext.groups.len() as u64;
|
||||
for (g, gr) in ext.groups.iter().enumerate() {
|
||||
if g % 16 == 15 && (ctx.cancelled)() {
|
||||
@@ -711,7 +711,7 @@ pub fn scan(ctx: &mut Ctx, base: u64, part_len: u64) -> VolumeScan {
|
||||
if !deleted || !ino.is_reg() {
|
||||
continue;
|
||||
}
|
||||
// Melhor fonte: a cópia do diário anterior à exclusão; senão o que sobrou no inode.
|
||||
// Best source: pre-deletion copy from the journal; fallback to remnants in the current inode.
|
||||
let mut cand: Option<(Inode, &str)> = None;
|
||||
if let Some(snaps) = journal.get(&ino_no) {
|
||||
for raw in snaps.iter().rev() {
|
||||
@@ -749,13 +749,13 @@ pub fn scan(ctx: &mut Ctx, base: u64, part_len: u64) -> VolumeScan {
|
||||
}
|
||||
}
|
||||
|
||||
// 2) Inodes que só existem no diário (liberados e já reaproveitados/zerados).
|
||||
// 2) Inodes only preserved in the journal (already zeroed in the main inode table).
|
||||
for (ino_no, snaps) in &journal {
|
||||
if emitted.contains(ino_no) || names.live.contains_key(ino_no) || *ino_no < 11 {
|
||||
continue;
|
||||
}
|
||||
let cur = ext.read_inode(*ino_no);
|
||||
// Se o inode atual está vivo e é outro arquivo, não mexe.
|
||||
// Skip if the inode is currently live in the filesystem.
|
||||
if cur
|
||||
.as_ref()
|
||||
.map(|c| c.dtime == 0 && c.links > 0 && c.mode != 0)
|
||||
@@ -805,7 +805,7 @@ fn emit_inode(
|
||||
if ino.flags & EXT4_EXTENTS_FL != 0 {
|
||||
let mut list: ExtentList = vec![];
|
||||
if !ext.walk_extent_node(&ino.block, 5, &mut list, true) {
|
||||
// Sobras de extent em inode truncado: cabeçalho zerado, registros ainda lá.
|
||||
// Extent remnants in a truncated inode: header count was zeroed, but extent entries remain.
|
||||
list = remnant_extents(ext, &ino.block);
|
||||
}
|
||||
if list.is_empty() {
|
||||
@@ -834,7 +834,7 @@ fn emit_inode(
|
||||
if !seen.insert((first.0, first.1)) {
|
||||
return false;
|
||||
}
|
||||
// Quantos blocos de dados continuam livres? Reaproveitados => baixa confiança.
|
||||
// Check how many data blocks are still unallocated; reallocated blocks lower confidence.
|
||||
let mut reused = 0u64;
|
||||
let mut total_blocks = 0u64;
|
||||
for e in extents.iter().filter(|e| !e.sparse) {
|
||||
@@ -859,7 +859,7 @@ fn emit_inode(
|
||||
));
|
||||
}
|
||||
if estimated {
|
||||
// Tamanho desconhecido: tenta achar o fim real pelo conteúdo.
|
||||
// Unknown exact size (zeroed on deletion): try to refine size via format structure.
|
||||
if let Some(h) = extents
|
||||
.iter()
|
||||
.find(|e| !e.sparse)
|
||||
@@ -932,7 +932,7 @@ fn layout_from_list(ext: &Ext, list: &ExtentList, size: u64) -> Vec<Extent> {
|
||||
out
|
||||
}
|
||||
|
||||
/// Lê registros de extent deixados em i_block depois que o cabeçalho foi zerado.
|
||||
/// Reads leftover extent records from `i_block` after `eh_entries` was zeroed on deletion.
|
||||
fn remnant_extents(ext: &Ext, block: &[u8; 60]) -> ExtentList {
|
||||
let mut out: ExtentList = vec![];
|
||||
if le16(block, 0) != EXT_MAGIC || le16(block, 6) != 0 {
|
||||
@@ -958,8 +958,8 @@ fn remnant_extents(ext: &Ext, block: &[u8; 60]) -> ExtentList {
|
||||
out
|
||||
}
|
||||
|
||||
/// Quão coerente é a estrutura do volume: fração das entradas de diretório cujo inode confere
|
||||
/// com o tipo declarado. Serve para escolher parâmetros de RAID sem metadados.
|
||||
/// Measures filesystem structural coherence: fraction of directory entries whose target inode
|
||||
/// matches the declared file type. Used to score candidate metadata-less RAID parameters.
|
||||
pub fn consistency_score(src: &dyn Source, base: u64) -> Option<f64> {
|
||||
let ext = Ext::open(src, base)?;
|
||||
let mut queue = vec![2u64];
|
||||
@@ -988,8 +988,8 @@ pub fn consistency_score(src: &dyn Source, base: u64) -> Option<f64> {
|
||||
o += ext.bs;
|
||||
}
|
||||
}
|
||||
// A entrada "." de um diretório aponta para ele mesmo: só confere se inode e bloco
|
||||
// vieram do lugar certo.
|
||||
// A directory's "." entry points to itself: only matches if both the inode table and
|
||||
// directory data block were mapped from the right RAID stripe.
|
||||
dots_total += 1;
|
||||
if ents.iter().any(|e| e.live && e.name == "." && e.ino == d) {
|
||||
dots_ok += 1;
|
||||
|
||||
Reference in new issue
Block a user