
Herramientas personalizadas de reparación BTRFS para corrupción grave del árbol de extent donde btrfs check --repair falla (segfault, bucle o bloqueo)
Herramientas escritas durante la recuperación de un pool BTRFS multidivisa de 12 TB con
corrupción grave del árbol de extent que los comandos nativos (btrfs check --repair,
--init-extent-tree, etc.) no pudieron reparar.
Consulte INCIDENT-ANALYSIS.md para un estudio de caso
estructurado de la recuperación, una clasificación de causa raíz y un conjunto de
propuestas constructivas para mejoras en btrfs-progs que habrían evitado
la necesidad de la mayoría de estas herramientas.
Use estas herramientas SOLO si btrfs check --repair se cuelga, entra en un bucle
infinito o deja el sistema de archivos en peor estado que antes.
Casos documentados donde ayudan:
btrfs check --repair se cuelga en [3/8] checking extents (Issue #525)btrfs check --init-extent-tree se bloqueabtrfs check --repair entra en un bucle infinito repitiendo las mismas reparacionesrescue=all,ro, falla al montar RWEstas herramientas NO son para corrupción leve. Para daños normales, pruebe
primero 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 es optativo)Las herramientas usan la API interna de btrfs-progs y deben compilarse dentro del árbol de fuentes de btrfs-progs:
# 1. Clonar btrfs-progs
git clone --depth 1 --branch v6.19.1 https://github.com/kdave/btrfs-progs.git
cd btrfs-progs
# 2. Aplicar el parche EEXIST (necesario para la inyección por lotes de backrefs)
patch -p1 < ruta/a/btrfs_fixes/patches/alloc_reserved_tree_block_eexist.patch
# 3. Configurar y compilar btrfs-progs base
./autogen.sh
./configure
make -j$(nproc)
# 4. Copiar los archivos .c de este repositorio al directorio de btrfs-progs
cp ruta/a/btrfs_fixes/programs/*.c .
# 5. Para cada programa, agregar al Makefile:
echo '
PROGNAME: PROGNAME.o $(objects) $(libs_shared)
@echo " [LD] $@"
$(Q)$(CC) -o $@ PROGNAME.o $(objects) $(libs_shared) $(LDFLAGS) $(LIBS)
' >> Makefile
# 6. Compilar
make PROGNAME
Orden de ejecución recomendado:
scan_and_fix_all_backrefs.c (la más importante)La herramienta más importante. Recorre recursivamente cada árbol en el sistema de archivos (ROOT, CHUNK, EXTENT, FS, DEV, CSUM, UUID, FREE_SPACE) y detecta bloques de metadatos que carecen de un METADATA_ITEM backref en el árbol de extent. Inyecta todos los backrefs faltantes en una sola transacción para evitar el problema de "el árbol raíz se mueve entre commits".
Uso:
sudo ./scan_and_fix_all_backrefs /dev/sdX # solo escaneo
sudo ./scan_and_fix_all_backrefs /dev/sdX --write # escaneo + inyección
fix_owner_refs.cCorrige el owner en el TREE_BLOCK_REF inline cuando no coincide con el
btrfs_header_owner() real del bloque. Las discrepancias ocurren cuando los bloques
se reasignan entre árboles durante reparaciones fallidas.
sudo ./fix_owner_refs /dev/sdX # escaneo
sudo ./fix_owner_refs /dev/sdX --write # corregir
fix_bad_levels.cCorrige entradas METADATA_ITEM y EXTENT_ITEM con un nivel incorrecto. Niveles
corruptos (p. ej. 50, 55, 237) son basura dejada por btrfs check --repair
al entrar en un bucle. Se verifica contra el btrfs_header_level() real del bloque.
sudo ./fix_bad_levels /dev/sdX # escaneo
sudo ./fix_bad_levels /dev/sdX --write # corregir
fix_duplicate_extents.cElimina METADATA_ITEM duplicados (mismo bytenr, diferentes niveles en la clave).
Conserva aquel cuyo nivel coincide con btrfs_header_level y elimina el otro.
sudo ./fix_duplicate_extents /dev/sdX # escaneo
sudo ./fix_duplicate_extents /dev/sdX --write # eliminar duplicados
remove_stale_ptrs.cEscanea cada nodo de nivel 1 del FS_TREE. Detecta punteros hijo obsoletos usando
tres comprobaciones: discrepancia de owner, discrepancia de first_key, o un first_key cuyo tipo
no es válido para el FS_TREE (p. ej. BLOCK_GROUP_ITEM). Los elimina con
btrfs_del_ptr.
sudo ./remove_stale_ptrs /dev/sdX # escaneo
sudo ./remove_stale_ptrs /dev/sdX --write # eliminar
fix_uuid_tree.c / fix_csum_tree.cCrea una hoja vacía para el árbol UUID / árbol CSUM respectivamente. Útil cuando el ROOT_ITEM apunta a un bloque que fue reasignado a otro árbol. El kernel regenera el árbol UUID automáticamente al montar RW. Con un árbol CSUM vacío, los archivos marcados como NODATASUM no fallan la verificación.
sudo ./fix_uuid_tree /dev/sdX
sudo ./fix_csum_tree /dev/sdX
set_nodatasum.cEstablece la marca BTRFS_INODE_NODATASUM en inodos de archivo regulares. Use esto si
el árbol de csum está vacío pero los archivos aún tienen checksums esperados, lo que causa
errores de lectura. Con NODATASUM, el kernel omite las búsquedas de csum.
sudo ./set_nodatasum /dev/sdX # escaneo
sudo ./set_nodatasum /dev/sdX --write # aplicar
fix_fstree_node.cVersión con una lista codificada de bloques obsoletos. Prefiera remove_stale_ptrs,
que los detecta automáticamente. Use esto solo si necesita control manual
sobre qué bloques específicos eliminar.
add_backrefs.cVersión inicial con una lista codificada de backrefs faltantes. Prefiera
scan_and_fix_all_backrefs, que los detecta automáticamente.
Cuando las herramientas base anteriores fueron insuficientes (pool con 200K+ errores distribuidos en múltiples árboles), se construyeron estas herramientas adicionales:
scan_fstree_extents.c + scan_extent_tree.cEscáneres de Pasada 1 y Pasada 2 que recorren el FS_TREE y el árbol de extent respectivamente,
produciendo archivos TSV con cada mapeo ref/extent. Se usan para construir entrada para
rebuild_extent_tree_apply cuando el árbol de extent necesita ser reconstruido desde cero.
rebuild_extent_tree_apply.c (escritor pesado)El escritor principal de la Fase 3. Toma una lista preprocesada de refs (del diff de scan_fstree_extents
scan_extent_tree) e inyecta 3M+ EXTENT_DATA_REF en el árbol de extent
en fragmentos de 5000 por transacción. Se regula cada 50K elementos para evitar
bloqueos por re-shingle en discos DM-SMR. Se verificó exitoso con 3,248,617 inserciones en ~34 min en
3 discos SMR WD40EFAX.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.cParche quirúrgico de un solo campo para BLOCK_GROUP_ITEM.used cuando el escritor de Fase 3
deja un bg específico con exceso debido a file_extent_items superpuestos preexistentes.
Utiliza el setter directo btrfs_set_block_group_used para evitar la contabilidad btrfs_update_block_group
space_info (que NO queremos aquí). Prevalida flags & BTRFS_BLOCK_GROUP_DATA.
sudo ./patch_block_group_used /dev/sdX1 <bg_bytenr> <bg_length> <new_used> --write