Skip to content
KitploitKITPLOIT
StrumentiBlog
Invia
StrumentiBlog
Invia

Strumenti di Hacking, PenTest e Cybersecurity per il tuo Arsenale di Sicurezza!

Kitploit è una directory di strumenti di hacking, cybersecurity e pentesting. Scopri gli ultimi aggiornamenti dei progetti per trovare vulnerabilità, analizzare sistemi, automatizzare i test e rafforzare la tua sicurezza.

··Feed·Contatto·Privacy·© 2026 Kitploit

Directory degli strumenti

Categorie

Vedi tutte le categorie
Loading categories
btrfs_fixes — Strumenti di riparazione BTRFS personalizzati per grave corruzione dell'albero delle estensioni dove btrfs check --repair fallisce (segfault, loop o deadlock) | Kitploit
Strumenti/GitHubGitHub/msedek/btrfs_fixes
Analisi delle VulnerabilitàInformatica ForenseRecupero DatiPaper e RicercaApprendimento e Formazione
GitHubmsedek/btrfs_fixes

btrfs_fixes

Strumenti di riparazione BTRFS personalizzati per grave corruzione dell'albero delle estensioni dove btrfs check --repair fallisce (segfault, loop o deadlock)

Vedi Repository
94 mesi faNon ancora revisionato

Più Popolari

Vedi tutti →

Scopri gli strumenti più utilizzati dalla nostra community.

Esplora tutti gli strumenti

Sfoglia la nostra collezione di strumenti

Vedi tutti gli strumenti →
Condividi

btrfs_fixes: Strumenti Personalizzati per la Riparazione di BTRFS

Strumenti personalizzati scritti durante il recupero di un pool BTRFS multi-dispositivo da 12TB con grave corruzione dell'albero degli extent che i comandi nativi (btrfs check --repair, --init-extent-tree, ecc.) non sono riusciti a riparare.

Vedi INCIDENT-ANALYSIS.md per un caso di studio strutturato del recupero, una classificazione delle cause principali e una serie di proposte costruttive per miglioramenti upstream di btrfs-progs che avrebbero impedito la necessità della maggior parte di questi strumenti.

Quando usare questi strumenti

Usa questi strumenti SOLO se btrfs check --repair va in segfault, entra in un loop infinito, o lascia il filesystem in condizioni peggiori di prima.

Casi documentati in cui aiutano:

  • btrfs check --repair va in segfault a [3/8] checking extents (Issue #525)
  • btrfs check --init-extent-tree si blocca
  • btrfs check --repair entra in un loop infinito ripetendo le stesse riparazioni
  • Albero degli extent con migliaia di METADATA_ITEM con owner/level/generation errati
  • FS_TREE con puntatori figli obsoleti che riferiscono blocchi riutilizzati da altri alberi
  • Il pool si monta solo con rescue=all,ro, non riesce a montare RW

Questi strumenti NON sono per corruzione leggera. Per danni normali, prova prima btrfs check --repair.

Avvertenze

  • EFFETTUA UN BACKUP dei metadati PRIMA di eseguire qualsiasi strumento con --write:
    root@kitploit:~
    for DEV in sda1 sdb1 sdc1; do
      sudo dd if=/dev/$DEV of=sb_${DEV}.bin bs=4096 count=1 skip=16
    done
    
  • Questi strumenti apportano modifiche irreversibili al filesystem
  • Tutti predefiniti in modalità solo scansione (--write è opt-in)
  • Il filesystem deve essere smontato quando si eseguono questi strumenti
  • Richiede btrfs-progs v6.19.1 o simile, con la patch EEXIST applicata

Compilazione

Gli strumenti utilizzano l'API interna di btrfs-progs e devono essere compilati all'interno dell'albero sorgente di btrfs-progs:

root@kitploit:~
# 1. Clone btrfs-progs
git clone --depth 1 --branch v6.19.1 https://github.com/kdave/btrfs-progs.git
cd btrfs-progs

# 2. Apply the EEXIST patch (required for batch backref injection)
patch -p1 < path/to/btrfs_fixes/patches/alloc_reserved_tree_block_eexist.patch

# 3. Configure and build base btrfs-progs
./autogen.sh
./configure
make -j$(nproc)

# 4. Copy the .c files from this repo into the btrfs-progs directory
cp path/to/btrfs_fixes/programs/*.c .

# 5. For each program, add to the Makefile:
echo '
PROGNAME: PROGNAME.o $(objects) $(libs_shared)
	@echo "  [LD]    $@"
	$(Q)$(CC) -o $@ PROGNAME.o $(objects) $(libs_shared) $(LDFLAGS) $(LIBS)
' >> Makefile

# 6. Build
make PROGNAME

Strumenti

Ordine di esecuzione consigliato:

1. scan_and_fix_all_backrefs.c (più importante)

Lo strumento più importante. Attraversa ricorsivamente ogni albero nel filesystem (ROOT, CHUNK, EXTENT, FS, DEV, CSUM, UUID, FREE_SPACE) e rileva i blocchi di metadati a cui manca un backref METADATA_ITEM nell'albero degli extent. Inietta tutti i backref mancanti in una singola transazione per evitare il problema dello "spostamento dell'albero radice tra i commit".

Utilizzo:

root@kitploit:~
sudo ./scan_and_fix_all_backrefs /dev/sdX          # scan only
sudo ./scan_and_fix_all_backrefs /dev/sdX --write  # scan + inject

2. fix_owner_refs.c

Corregge il campo owner nel TREE_BLOCK_REF inline quando non corrisponde all'effettivo btrfs_header_owner() del blocco. Le discrepanze si verificano quando i blocchi vengono riassegnati tra alberi durante riparazioni fallite.

root@kitploit:~
sudo ./fix_owner_refs /dev/sdX          # scan
sudo ./fix_owner_refs /dev/sdX --write  # fix

3. fix_bad_levels.c

Corregge le voci METADATA_ITEM e EXTENT_ITEM con un livello errato. Livelli corrotti (es. 50, 55, 237) sono spazzatura lasciata da btrfs check --repair in un loop. Verificato rispetto al reale btrfs_header_level() del blocco.

root@kitploit:~
sudo ./fix_bad_levels /dev/sdX          # scan
sudo ./fix_bad_levels /dev/sdX --write  # fix

4. fix_duplicate_extents.c

Elimina METADATA_ITEM duplicati (stesso bytenr, livelli diversi nella chiave). Conserva quello il cui livello corrisponde a btrfs_header_level ed elimina l'altro.

root@kitploit:~
sudo ./fix_duplicate_extents /dev/sdX          # scan
sudo ./fix_duplicate_extents /dev/sdX --write  # delete duplicates

5. remove_stale_ptrs.c

Scansiona ogni nodo di livello 1 del FS_TREE. Rileva puntatori figli obsoleti usando tre controlli: owner non corrispondente, first_key non corrispondente, o una first_key il cui tipo non è valido per FS_TREE (es. BLOCK_GROUP_ITEM). Li rimuove con btrfs_del_ptr.

root@kitploit:~
sudo ./remove_stale_ptrs /dev/sdX          # scan
sudo ./remove_stale_ptrs /dev/sdX --write  # remove

6. fix_uuid_tree.c / fix_csum_tree.c

Crea una foglia vuota rispettivamente per l'albero UUID / albero CSUM. Utile quando il ROOT_ITEM punta a un blocco riassegnato ad un altro albero. Il kernel rigenera automaticamente l'albero UUID al montaggio RW. Con un albero CSUM vuoto, i file contrassegnati NODATASUM non falliscono la verifica.

root@kitploit:~
sudo ./fix_uuid_tree /dev/sdX
sudo ./fix_csum_tree /dev/sdX

7. set_nodatasum.c

Imposta il flag BTRFS_INODE_NODATASUM sugli inode dei file regolari. Usalo se l'albero csum è vuoto ma i file hanno ancora checksum attesi, causando errori di lettura. Con NODATASUM, il kernel salta le ricerche di csum.

root@kitploit:~
sudo ./set_nodatasum /dev/sdX          # scan
sudo ./set_nodatasum /dev/sdX --write  # apply

8. fix_fstree_node.c

Versione con una lista hardcoded di blocchi obsoleti. Preferisci remove_stale_ptrs, che li rileva automaticamente. Usalo solo se hai bisogno di controllo manuale su quali blocchi specifici rimuovere.

9. add_backrefs.c

Versione iniziale con una lista hardcoded di backref mancanti. Preferisci scan_and_fix_all_backrefs, che li rileva automaticamente.

Set di strumenti Sessione 2 (2026-04-04/05): Recupero esteso per corruzione massiccia

Quando gli strumenti di base sopra erano insufficienti (pool con 200K+ errori distribuiti su più alberi), questi strumenti aggiuntivi sono stati costruiti:

scan_fstree_extents.c + scan_extent_tree.c

Scanner di Passo 1 e Passo 2 che attraversano rispettivamente FS_TREE e albero degli extent, producendo file TSV con ogni mapping ref/extent. Utilizzati per costruire l'input per rebuild_extent_tree_apply quando l'albero degli extent deve essere ricostruito da zero.

rebuild_extent_tree_apply.c (scrittore pesante)

Il principale scrittore della Fase 3. Prende una lista pre-ripiegata di ref (dal diff di scan_fstree_extents + scan_extent_tree) e inietta 3M+ EXTENT_DATA_REF nell'albero degli extent in blocchi di 5000 per transazione. Limita ogni 50K elementi per evitare stalli di ri-shingling DM-SMR. Verificato con successo a 3.248.617 inserimenti in ~34 min su 3 dischi WD40EFAX SMR.

root@kitploit:~
sudo ./rebuild_extent_tree_apply /dev/sdX1 refs_folded.txt to_insert.txt watermark.txt --dryrun
sudo ./rebuild_extent_tree_apply /dev/sdX1 refs_folded.txt to_insert.txt watermark.txt --write

patch_block_group_used.c

Correttore chirurgico a singolo campo per BLOCK_GROUP_ITEM.used quando lo scrittore della Fase 3 lascia un bg specifico con overshoot a causa di file_extent_items sovrapposti preesistenti. Utilizza btrfs_set_block_group_used setter diretto per evitare la contabilità space_info di btrfs_update_block_group (che NON vogliamo qui). Pre-valida flags & BTRFS_BLOCK_GROUP_DATA.

root@kitploit:~
sudo ./patch_block_group_used /dev/sdX1 <bg_bytenr> <bg_length> <new_used> --write

remove_extent_items_by_key.c

Elimina una lista hardcoded di EXTENT_ITEM (bytenr, num_bytes, expected_inode) dall'albero degli extent. Usato per pulire extent obsoleti sovrapposti in una singola foglia che impediscono il montaggio RO. Controlli di sanità per ogni elemento prima dell'eliminazione (7 invarianti inclusa whitelist inode). Esegue con rebuilding_extent_tree=1 + reinit_extent_tree=true per saltare la contabilità spazio (il chiamante corregge used manualmente prima tramite patch_block_group_used).

clean_orphan_dir_entries.c

Pulisce le voci DIR_ITEM + DIR_INDEX orfane da FS_TREE. Blocchi di 100 voci per transazione. Aggiorna i_size di INODE_ITEM padre (decremento di namelen × 2: bug critico corretto: v1 decrementava solo di namelen, lasciando le directory in stato non valido). Lista di esclusione hardcoded per nomi di directory di primo livello critici (es. pelis, series, music, backups, homestorage). MAI decrementare i_size per il solo namelen: BTRFS memorizza una contabilità di namelen × 2.

clean_orphan_inode_refs.c

Attraversa FS_TREE per elementi INODE_REF il cui key.offset (inode padre) è in una lista di padri orfani. Salta INODE_EXTREF per evitare falsi positivi (il key.offset di EXTREF è un hash, non un ID padre). Blocchi di 32 per transazione.

fix_dir_inode_counts.c

Ricalcola i_size = sum(name_len × 2) e nlink = 1 per inode DIR i cui conteggi sono stati corrotti da bug precedenti nella pulizia degli orfani. CRITICO per la sicurezza: se un qualsiasi DIR ha nlink = 2, un singolo rm -rf sul suo percorso eliminerà silenziosamente migliaia di sottodirectory (bomba rmdir). Attraversa le voci DIR_INDEX, incrocia i controlli con DIR_ITEM per il rilevamento di collisioni hash (0 collisioni verificate empiricamente).

remove_orphan_inode_subtrees.c

Rimuove i sottoalberi di inode orfani (famiglie DIR + REG standalone) da FS_TREE. Per ogni target: attraversa ed elimina EXTENT_DATA, INODE_REF, INODE_EXTREF, XATTR e infine INODE_ITEM. Transazione per famiglia DIR (atomica per sottoalbero), blocchi di 50 per REG standalone. Lista di esclusione paranoica hardcoded.

⚠️ AVVERTENZA IMPORTANTE per la sicurezza: vedi "Criterio del sottoinsieme a prova di proiettile" sotto.

remove_stale_ptrs_v2.c

Versione migliorata di remove_stale_ptrs: rileva foglie vuote con parent expected_key (v1 saltava questo caso), scansione ricorsiva a 2 livelli (radice→livello1 + livello1→foglie), buffer dinamico (nessun limite di 512), tollera fallimenti di read_tree_block.

insert_one_extent_poc.c

PoC per l'inserimento di un singolo extent con validazione. Usato per validare il percorso API prima di eseguire rebuild_extent_tree_apply.

Criterio del sottoinsieme a prova di proiettile (CRITICO)

Durante la sessione del 2026-04-05, remove_orphan_inode_subtrees è crashato due volte sulla stessa asserzione BUG_ON per due ragioni differenti:

Vettore di crash 1: btrfs_cow_block(leaf) diretto su una foglia MIXED (gen 3601, contiene sia inode orfani che vivi) → update_ref_for_cow attraversa i figli → __btrfs_mod_ref(inc=1) su figli fratelli obsoleti → btrfs_free_extent(phantom) restituisce -ENOENT → BUG_ON → SIGABRT.

Vettore di crash 2 (scoperto in seguito, evitato filtrando): btrfs_del_items dopo la pulizia riduce una foglia al di sotto di LEAF_DATA_SIZE/4 = 4096 byte → invoca push_leaf_left(sibling) o push_leaf_right(sibling) → se il fratello ha gen ≤ last_snapshot = 3701, btrfs_block_can_be_shared restituisce 1 → update_ref_for_cow entra nel percorso refs > 1 → btrfs_inc_ref(cow_sibling, 0) → __btrfs_mod_ref(cow, level=0, inc=1) → itera tutti gli EXTENT_DATA del fratello obsoleto → btrfs_inc_extent_ref(phantom_bytenr) → BUG_ON(err) in extent-tree.c:1302 → SIGABRT.

I flag fs_info->rebuilding_extent_tree = 1 e trans->reinit_extent_tree = true NON salvano il percorso INC: esentano solo BTRFS_DROP_DELAYED_REF (verificato in extent-tree.c:3885). BTRFS_ADD_DELAYED_REF (da btrfs_inc_ref) è fatale.

Criterio a prova di proiettile per qualsiasi inode target che verrà eliminato:

  1. La foglia che ospita l'INODE_ITEM dell'inode ha gen > 3701 (post-crash)
  2. Il padre di livello 1 della foglia ha gen > 3701
  3. I byte used stimati dopo la pulizia sono > 4096 (nessun trigger di ribilanciamento)
  4. TUTTI i fratelli immediati nel nodo padre hanno gen > 3701 (anche se la condizione 3 fallisce, il ribilanciamento verso fratelli post-crash è sicuro)
  5. Eventuali target di backref (EXTENT_DATA disk_bytenr) si risolvono nell'albero degli extent corrente (nessun -ENOENT nella ricerca di backref)

Violare una qualsiasi delle condizioni 3+4 attiva il vettore di crash 2. La condizione 5 è esonerata da reinit_extent_tree per DROP ma NON per INC (che è ciò che invoca push_leaf_left).

Schema di validazione empirica

Per qualsiasi insieme di inode orfani candidati, attraversa il dump di FS_TREE e classifica ogni foglia target secondo le 5 condizioni a prova di proiettile. Esempio di schema (anonimizzato):

Le foglie in cui ≥90% degli elementi sono orfani sono la zona pericolosa: scenderanno al di sotto della soglia di ribilanciamento (LEAF_DATA_SIZE/4 = 4096 byte) con certezza, forzando push_leaf_left/right. Se un qualsiasi fratello immediato nel nodo padre ha gen ≤ last_snapshot, il push attiva CoW su quel fratello, che entra nel percorso btrfs_block_can_be_shared → refs > 1 → btrfs_inc_ref → __btrfs_mod_ref(inc=1) e crasha con BUG_ON(err) in btrfs_inc_extent_ref.

Mitigazione: escludi gli inode problematici dal file di input. Lo strumento elabora tutto ciò che supera la validazione pre-volo; le foglie con target misti sicuri/non sicuri possono essere elaborate parzialmente elencando solo il sottoinsieme sicuro. La semantica di transazione per famiglia significa che ogni famiglia sicura viene impegnata atomicamente anche se altre famiglie sono escluse.

Risultato empirico da una sessione: partendo da N orfani candidati, dopo aver applicato tutte e 5 le condizioni il sottoinsieme sicuro finale era circa il 14% dell'input, ma quel sottoinsieme è stato impegnato senza un singolo BUG_ON, con un diff di 0 byte su uno sha256 di base dei file live catturati prima della scrittura.

La patch: EEXIST in alloc_reserved_tree_block

patches/alloc_reserved_tree_block_eexist.patch modifica btrfs-progs in modo che quando alloc_reserved_tree_block trova che METADATA_ITEM esiste già, restituisce 0 invece di propagare EEXIST. Ciò è necessario affinché l'iniezione batch di backref funzioni: quando si inietta molti backref, anche il sistema di delayed refs cerca di creare METADATA_ITEM per blocchi appena allocati tramite COW e collide con quelli che abbiamo già inserito.

Flusso di lavoro completo per recupero grave

root@kitploit:~
# 1. Backup
mkdir -p backup
for DEV in /dev/sdX1 /dev/sdY1; do
  sudo dd if=$DEV of=backup/$(basename $DEV).sb bs=4096 count=1 skip=16
done

# 2. Make sure the filesystem is unmounted
sudo umount /mnt/pool 2>/dev/null

# 3. Zero the log tree (if applicable)
sudo btrfs rescue zero-log /dev/sdX1

# 4. Scan + fix everything (in order)
sudo ./scan_and_fix_all_backrefs /dev/sdX1 --write
sudo ./fix_bad_levels /dev/sdX1 --write
sudo ./fix_owner_refs /dev/sdX1 --write
sudo ./fix_duplicate_extents /dev/sdX1 --write
sudo ./remove_stale_ptrs /dev/sdX1 --write

# 5. Re-scan to verify convergence
sudo ./scan_and_fix_all_backrefs /dev/sdX1
sudo ./remove_stale_ptrs /dev/sdX1

# 6. If the csum tree is broken:
sudo ./fix_csum_tree /dev/sdX1
sudo ./set_nodatasum /dev/sdX1 --write

# 7. Try mounting RW
sudo mount -o rw /dev/sdX1 /mnt/pool

# 8. If it mounts, verify with btrfs check readonly
sudo btrfs check --force /dev/sdX1

Limitazioni note

  1. Ogni riparazione può creare nuovi problemi tramite COW: quando uno strumento modifica l'albero degli extent, btrfs esegue COW sui nodi interessati. I nuovi nodi copiano i puntatori da quelli vecchi, il che può propagare puntatori obsoleti. Potrebbero essere necessari più passaggi.

  2. Le discrepanze dei riferimenti agli extent dati non vengono corrette: questi strumenti toccano solo i backref dei metadati. I conteggi di ref errati sugli extent dati (comuni dopo esecuzioni fallite di btrfs check --repair) non vengono puliti.

  3. Gli inode orfani non vengono puliti: le voci di directory orfane in FS_TREE (riferimenti a inode che non esistono più) non vengono rimosse.

  4. Non sostituisce btrfs check --repair: questi strumenti mirano a scenari specifici. Per danni leggeri o moderati, btrfs check --repair è migliore.

Lezioni apprese

  1. MAI effettuare un power-cycle improvviso su un filesystem BTRFS multi-dispositivo: la corruzione combinata dell'albero dello spazio libero e dell'albero degli extent è estremamente difficile da riparare.

  2. MAI eseguire btrfs check --repair più volte di seguito se la prima esecuzione non ha risolto tutto: può entrare in un loop infinito e peggiorare drasticamente il filesystem.

  3. Eseguire sempre il backup dei superblocchi prima di ogni operazione di scrittura.

  4. trans->reinit_extent_tree = true è fondamentale per ignorare i fallimenti DROP nei delayed refs per blocchi senza backref.

  5. fs_info->rebuilding_extent_tree = 1 disabilita i controlli di spazio durante le riparazioni.

  6. Un grande commit con molti inserimenti è meglio di tanti piccoli commit, perché i commit intermedi spostano l'albero radice.

  7. I backup_slots nel SB NON sono backup storici: sono una finestra scorrevole dei soli ultimi 4 commit. Un loop di btrfs check --repair di 46.000+ commit ruoterà ogni slot circa 11.000 volte in minuti, obliterando qualsiasi stato pre-crash recuperabile dal kernel. Per una conservazione reale sono necessari flussi espliciti di btrfs subvolume snapshot o btrfs send verso un altro dispositivo.

  8. : esenta solo , NON . Qualsiasi percorso di codice che chiami su una foglia obsoleta (inclusi durante il ribilanciamento) crasha ancora tramite → .

Disclaimer

Questi strumenti sono stati scritti per un caso di recupero specifico in cui gli strumenti nativi fallivano. Non sono testati per casi d'uso generali. Usali solo se comprendi il codice e accetti il rischio di perdita di dati.

Se possibile, copia sempre i tuoi dati prima di tentare qualsiasi riparazione.

Licenza

GPL-2.0 (compatibile con btrfs-progs, la cui API interna questi strumenti utilizzano).

Scarica lo strumento
FogliaGenElementi orfani / totaleUsato post-pulizia (stima)Ribilanciamento?Fratelli immediatiVerdetto
$LEAF_Apost-crashper lo più orfani, pulizia pesantesotto sogliaSÌtutti post-crash✓ sicuro
$LEAF_Bpost-crashper lo più vivi, pulizia leggerasopra sogliaNOgenitore pulito✓ sicuro
$LEAF_Cpost-crashquasi 100% orfanimolto sotto 4096SÌ forzatoobsoleto pre-crash❌ CRASH
reinit_extent_tree è ASIMMETRICO
BTRFS_DROP_DELAYED_REF
BTRFS_ADD_DELAYED_REF
btrfs_inc_ref
push_leaf_left/right
btrfs_inc_extent_ref
BUG_ON(err)
  • Il criterio di sicurezza per l'elaborazione degli inode in un FS_TREE danneggiato deve includere i fratelli, non solo la foglia target stessa. Vedi sezione "Criterio del sottoinsieme a prova di proiettile".

  • Lo sha256 di base dei file LIVE è l'unica prova empirica degli invarianti. Catturalo prima di qualsiasi operazione di scrittura, confrontalo dopo. Qualsiasi discrepanza = rollback.

  • i_size delle DIR è memorizzato come sum(name_len × 2), NON sum(name_len). Qualunque strumento di pulizia degli orfani che decrementi i_size alla rimozione di una voce deve decrementare di namelen × 2. Sbagliare questo lascia le DIR in uno stato non valido che può manifestarsi come nlink = 2 in seguito: ciò innesca una bomba rmdir se il pool è montato RW (un singolo rm -rf su un genitore può eliminare silenziosamente migliaia di sottodirectory).

  • Revisori esperti con prove empiriche sono fondamentali. La sessione del 2026-04-05 ha utilizzato due revisori Opus paralleli (interni btrfs + operazioni) che hanno analizzato il piano proposto rispetto all'output dump-tree. Hanno individuato un vettore di crash deterministico (push_leaf_left → fratello obsoleto) che avrebbe ripetuto i fallimenti precedenti. Una revisione testuale del piano senza analisi empirica del dump-tree lo avrebbe perso.