
Strumenti di riparazione BTRFS personalizzati per grave corruzione dell'albero delle estensioni dove btrfs check --repair fallisce (segfault, loop o deadlock)
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.
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 bloccabtrfs check --repair entra in un loop infinito ripetendo le stesse riparazionirescue=all,ro, non riesce a montare RWQuesti strumenti NON sono per corruzione leggera. Per danni normali, prova prima btrfs check --repair.
--write:
for DEV in sda1 sdb1 sdc1; do
sudo dd if=/dev/$DEV of=sb_${DEV}.bin bs=4096 count=1 skip=16
done
--write è opt-in)Gli strumenti utilizzano l'API interna di btrfs-progs e devono essere compilati all'interno dell'albero sorgente di btrfs-progs:
# 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
Ordine di esecuzione consigliato:
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:
sudo ./scan_and_fix_all_backrefs /dev/sdX # scan only
sudo ./scan_and_fix_all_backrefs /dev/sdX --write # scan + inject
fix_owner_refs.cCorregge 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.
sudo ./fix_owner_refs /dev/sdX # scan
sudo ./fix_owner_refs /dev/sdX --write # fix
fix_bad_levels.cCorregge 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.
sudo ./fix_bad_levels /dev/sdX # scan
sudo ./fix_bad_levels /dev/sdX --write # fix
fix_duplicate_extents.cElimina METADATA_ITEM duplicati (stesso bytenr, livelli diversi nella chiave). Conserva quello il cui livello corrisponde a btrfs_header_level ed elimina l'altro.
sudo ./fix_duplicate_extents /dev/sdX # scan
sudo ./fix_duplicate_extents /dev/sdX --write # delete duplicates
remove_stale_ptrs.cScansiona 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.
sudo ./remove_stale_ptrs /dev/sdX # scan
sudo ./remove_stale_ptrs /dev/sdX --write # remove
fix_uuid_tree.c / fix_csum_tree.cCrea 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.
sudo ./fix_uuid_tree /dev/sdX
sudo ./fix_csum_tree /dev/sdX
set_nodatasum.cImposta 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.
sudo ./set_nodatasum /dev/sdX # scan
sudo ./set_nodatasum /dev/sdX --write # apply
fix_fstree_node.cVersione 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.
add_backrefs.cVersione iniziale con una lista hardcoded di backref mancanti. Preferisci scan_and_fix_all_backrefs, che li rileva automaticamente.
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.cScanner 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.
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.cCorrettore 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.
sudo ./patch_block_group_used /dev/sdX1 <bg_bytenr> <bg_length> <new_used> --write