
Ferramentas de reparo BTRFS personalizadas para corrupção severa da árvore de extent onde btrfs check --repair falha (segfault, loop ou deadlock)
Ferramentas personalizadas escritas durante a recuperação de um pool BTRFS de 12 TB com vários dispositivos e grave corrupção da árvore de extent (extent tree) que os comandos nativos (btrfs check --repair, --init-extent-tree, etc.) não conseguiram reparar.
Veja INCIDENT-ANALYSIS.md para um estudo de caso estruturado da recuperação, uma classificação de causa raiz e um conjunto de propostas construtivas para melhorias no btrfs-progs upstream que teriam evitado a necessidade da maioria dessas ferramentas.
Use estas ferramentas APENAS se btrfs check --repair falhar com segfault, entrar em um loop infinito ou deixar o sistema de arquivos em pior estado do que antes.
Casos documentados onde elas ajudam:
btrfs check --repair dá segfault em [3/8] checking extents (Issue #525)btrfs check --init-extent-tree trava (deadlock)btrfs check --repair entra em um loop infinito repetindo os mesmos reparosrescue=all,ro, falha ao montar RWEstas ferramentas NÃO são para corrupção leve. Para danos normais, tente btrfs check --repair primeiro.
--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)As ferramentas utilizam a API interna do btrfs-progs e devem ser compiladas dentro da árvore de fontes do btrfs-progs:
# 1. Clone o btrfs-progs
git clone --depth 1 --branch v6.19.1 https://github.com/kdave/btrfs-progs.git
cd btrfs-progs
# 2. Aplique o patch EEXIST (necessário para injeção em lote de backrefs)
patch -p1 < caminho/para/btrfs_fixes/patches/alloc_reserved_tree_block_eexist.patch
# 3. Configure e compile o btrfs-progs base
./autogen.sh
./configure
make -j$(nproc)
# 4. Copie os arquivos .c deste repositório para o diretório btrfs-progs
cp caminho/para/btrfs_fixes/programs/*.c .
# 5. Para cada programa, adicione ao Makefile:
echo '
PROGNAME: PROGNAME.o $(objects) $(libs_shared)
@echo " [LD] $@"
$(Q)$(CC) -o $@ PROGNAME.o $(objects) $(libs_shared) $(LDFLAGS) $(LIBS)
' >> Makefile
# 6. Compile
make PROGNAME
Ordem de execução recomendada:
scan_and_fix_all_backrefs.c (mais importante)A ferramenta mais importante. Percorre recursivamente cada árvore no sistema de arquivos (ROOT, CHUNK, EXTENT, FS, DEV, CSUM, UUID, FREE_SPACE) e detecta blocos de metadados que estão sem um METADATA_ITEM backref na árvore de extent. Injeta todos os backrefs ausentes em uma única transação para evitar o problema de "árvore raiz se move entre commits".
Uso:
sudo ./scan_and_fix_all_backrefs /dev/sdX # apenas varredura
sudo ./scan_and_fix_all_backrefs /dev/sdX --write # varredura + injeção
fix_owner_refs.cCorrige o owner no TREE_BLOCK_REF inline quando ele não corresponde ao btrfs_header_owner() real do bloco. Inconsistências ocorrem quando blocos são reatribuídos entre árvores durante reparos com falha.
sudo ./fix_owner_refs /dev/sdX # varredura
sudo ./fix_owner_refs /dev/sdX --write # correção
fix_bad_levels.cCorrige entradas METADATA_ITEM e EXTENT_ITEM com nível incorreto. Níveis corrompidos (ex.: 50, 55, 237) são lixo deixado por btrfs check --repair ao entrar em loop. Verificado contra o btrfs_header_level() real do bloco.
sudo ./fix_bad_levels /dev/sdX # varredura
sudo ./fix_bad_levels /dev/sdX --write # correção
fix_duplicate_extents.cExclui METADATA_ITEMs duplicados (mesmo bytenr, níveis diferentes na chave). Mantém aquele cujo nível corresponde a btrfs_header_level e exclui o outro.
sudo ./fix_duplicate_extents /dev/sdX # varredura
sudo ./fix_duplicate_extents /dev/sdX --write # excluir duplicatas
remove_stale_ptrs.cVarre cada nó de nível 1 da FS_TREE. Detecta ponteiros filhos obsoletos usando três verificações: incompatibilidade de owner, incompatibilidade de first_key ou uma first_key cujo tipo é inválido para a FS_TREE (ex.: BLOCK_GROUP_ITEM). Remove-os com btrfs_del_ptr.
sudo ./remove_stale_ptrs /dev/sdX # varredura
sudo ./remove_stale_ptrs /dev/sdX --write # remoção
fix_uuid_tree.c / fix_csum_tree.cCria uma folha vazia para a árvore UUID / árvore CSUM, respectivamente. Útil quando o ROOT_ITEM aponta para um bloco que foi reatribuído a outra árvore. O kernel regenera a árvore UUID automaticamente na montagem RW. Com uma árvore CSUM vazia, arquivos sinalizados como NODATASUM não falham na verificação.
sudo ./fix_uuid_tree /dev/sdX
sudo ./fix_csum_tree /dev/sdX
set_nodatasum.cDefine o sinalizador BTRFS_INODE_NODATASUM em inodes de arquivos regulares. Use isto se a árvore csum estiver vazia, mas os arquivos ainda tiverem checksums esperados, o que causa erros de leitura. Com NODATASUM, o kernel ignora as consultas de csum.
sudo ./set_nodatasum /dev/sdX # varredura
sudo ./set_nodatasum /dev/sdX --write # aplicar
fix_fstree_node.cVersão com uma lista codificada de blocos obsoletos. Prefira remove_stale_ptrs, que os detecta automaticamente. Use apenas se precisar de controle manual sobre quais blocos específicos remover.
add_backrefs.cVersão inicial com uma lista codificada de backrefs ausentes. Prefira scan_and_fix_all_backrefs, que os detecta automaticamente.
Quando as ferramentas básicas acima foram insuficientes (pool com 200K+ erros espalhados por várias árvores), estas ferramentas adicionais foram construídas:
scan_fstree_extents.c + scan_extent_tree.cScanners da Passada 1 e Passada 2 que percorrem a FS_TREE e a árvore de extent, respectivamente, produzindo arquivos TSV com cada mapeamento ref/extent. Usado para criar a entrada para rebuild_extent_tree_apply quando a árvore de extent precisa ser reconstruída do zero.
rebuild_extent_tree_apply.c (escritor pesado)O principal escritor da Fase 3. Pega uma lista previamente dobrada de refs (do diff de scan_fstree_extents + scan_extent_tree) e injeta 3M+ EXTENT_DATA_REFs na árvore de extent em blocos de 5000 por transação. Reduz a cada 50K itens para evitar paradas de re-shingle do DM-SMR. Verificado como bem-sucedido com 3.248.617 inserções em ~34 min em 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.cCorretor cirúrgico de campo único para BLOCK_GROUP_ITEM.used quando o escritor da Fase 3 deixa um bg específico com overshoot devido a itens file_extent sobrepostos pré-existentes. Usa o setter direto btrfs_set_block_group_used para evitar a contabilidade space_info de btrfs_update_block_group (que NÃO queremos aqui). Pré-valida flags & BTRFS_BLOCK_GROUP_DATA.
sudo ./patch_block_group_used /dev/sdX1 <bg_bytenr> <bg_length> <new_used> --write