Skip to content
KitploitKITPLOIT
OutilsBlog
Soumettre
OutilsBlog
Soumettre

Outils de Hacking, PenTest et Cybersécurité pour votre Arsenal de Sécurité !

Kitploit est un répertoire d'outils de hacking, de cybersécurité et de pentesting. Découvrez les dernières mises à jour des projets pour trouver des vulnérabilités, analyser des systèmes, automatiser les tests et renforcer votre sécurité.

··Flux·Contact·Confidentialité·© 2026 Kitploit

Répertoire d'outils

Catégories

Voir toutes les catégories
Loading categories
btrfs_fixes — Outils de réparation BTRFS personnalisés pour corruption grave de l'arbre d'extents lorsque btrfs check --repair échoue (segfault, boucle ou blocage) | Kitploit
Outils/GitHubGitHub/msedek/btrfs_fixes
Analyse des VulnérabilitésAnalyse ForensiqueRécupération de DonnéesArticles et RechercheApprentissage et Éducation
GitHubmsedek/btrfs_fixes

btrfs_fixes

Outils de réparation BTRFS personnalisés pour corruption grave de l'arbre d'extents lorsque btrfs check --repair échoue (segfault, boucle ou blocage)

Voir le dépôt
9il y a 4 moisPas encore vérifié

Populaires

Voir tout →

Découvrez les outils les plus utilisés par notre communauté.

Explorer tous les outils

Parcourez notre collection d'outils

Voir tous les outils →
Partager

btrfs_fixes: Outils de réparation BTRFS personnalisés

Outils personnalisés écrits lors de la récupération d'un pool BTRFS multi-périphériques de 12 To avec une corruption sévère de l'arbre des extents que les commandes natives (btrfs check --repair, --init-extent-tree, etc.) n'ont pas pu réparer.

Voir INCIDENT-ANALYSIS.md pour une étude de cas structurée de la récupération, une classification des causes profondes et un ensemble de propositions constructives pour des améliorations en amont de btrfs-progs qui auraient évité le besoin de la plupart de ces outils.

Quand utiliser ces outils

Utilisez ces outils UNIQUEMENT si btrfs check --repair fait un segfault, entre dans une boucle infinie, ou laisse le système de fichiers dans un état pire qu'avant.

Cas documentés où ils aident :

  • btrfs check --repair fait un segfault à [3/8] checking extents (Issue #525)
  • btrfs check --init-extent-tree se bloque (deadlock)
  • btrfs check --repair entre dans une boucle infinie en répétant les mêmes réparations
  • Arbre des extents avec des milliers de METADATA_ITEM portant un owner/level/generation erroné
  • FS_TREE avec des pointeurs enfants obsolètes qui référencent des blocs réutilisés par d'autres arbres
  • Pool ne monte qu'avec rescue=all,ro, échoue à monter en RW

Ces outils ne sont PAS pour des corruptions légères. Pour des dégâts normaux, essayez d'abord btrfs check --repair.

Avertissements

  • Sauvegardez les métadonnées AVANT d'exécuter un outil avec --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
    
  • Ces outils effectuent des modifications irréversibles sur le système de fichiers
  • Tous sont par défaut en mode scan uniquement (--write est optionnel)
  • Le système de fichiers doit être démonté lors de l'exécution de ces outils
  • Nécessite btrfs-progs v6.19.1 ou similaire, avec le patch EEXIST appliqué

Construction

Les outils utilisent l'API interne de btrfs-progs et doivent être compilés à l'intérieur de l'arborescence source de 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

Outils

Ordre d'exécution recommandé :

1. scan_and_fix_all_backrefs.c (le plus important)

L'outil le plus important. Parcourt récursivement chaque arbre du système de fichiers (ROOT, CHUNK, EXTENT, FS, DEV, CSUM, UUID, FREE_SPACE) et détecte les blocs de métadonnées qui n'ont pas de METADATA_ITEM de backref dans l'arbre des extents. Injecte tous les backrefs manquants en une seule transaction pour éviter le problème de "déplacement de l'arbre racine entre les commits".

Utilisation :

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

Corrige le owner dans le TREE_BLOCK_REF en ligne lorsqu'il ne correspond pas au vrai btrfs_header_owner() du bloc. Les discordances surviennent lorsque des blocs sont réassignés entre arbres lors de réparations échouées.

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

3. fix_bad_levels.c

Corrige les entrées METADATA_ITEM et EXTENT_ITEM avec un niveau incorrect. Les niveaux corrompus (par exemple 50, 55, 237) sont des déchets laissés par btrfs check --repair entrant dans une boucle. Vérifié par rapport au vrai btrfs_header_level() du bloc.

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

4. fix_duplicate_extents.c

Supprime les METADATA_ITEM en double (même bytenr, niveaux différents dans la clé). Garde celui dont le niveau correspond à btrfs_header_level et supprime l'autre.

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

5. remove_stale_ptrs.c

Scanne chaque nœud de niveau 1 du FS_TREE. Détecte les pointeurs enfants obsolètes à l'aide de trois vérifications : discordance d'owner, discordance de first_key, ou une first_key dont le type est invalide pour le FS_TREE (par exemple BLOCK_GROUP_ITEM). Les supprime avec 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

Crée une feuille vide pour l'arbre UUID / l'arbre CSUM respectivement. Utile lorsque le ROOT_ITEM pointe vers un bloc qui a été réassigné à un autre arbre. Le noyau régénère automatiquement l'arbre UUID lors du montage RW. Avec un arbre CSUM vide, les fichiers marqués NODATASUM n'échouent pas à la vérification.

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

7. set_nodatasum.c

Définit le flag BTRFS_INODE_NODATASUM sur les inodes de fichiers réguliers. Utilisez ceci si l'arbre csum est vide mais que les fichiers ont toujours des sommes de contrôle attendues, ce qui cause des erreurs de lecture. Avec NODATASUM, le noyau ignore les recherches csum.

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

8. fix_fstree_node.c

Version avec une liste codée en dur de blocs obsolètes. Préférez remove_stale_ptrs, qui les détecte automatiquement. Utilisez-le uniquement si vous avez besoin d'un contrôle manuel sur les blocs spécifiques à supprimer.

9. add_backrefs.c

Version initiale avec une liste codée en dur de backrefs manquants. Préférez scan_and_fix_all_backrefs, qui les détecte automatiquement.

Ensemble d'outils de la session 2 (2026-04-04/05) : Récupération étendue pour corruption massive

Lorsque les outils de base ci-dessus étaient insuffisants (pool avec plus de 200 000 erreurs réparties sur plusieurs arbres), ces outils supplémentaires ont été construits :

scan_fstree_extents.c + scan_extent_tree.c

Scanners de passe 1 et passe 2 qui parcourent respectivement le FS_TREE et l'arbre des extents, produisant des fichiers TSV avec chaque mapping ref/extent. Utilisé pour construire l'entrée de rebuild_extent_tree_apply lorsque l'arbre des extents doit être reconstruit à partir de zéro.

rebuild_extent_tree_apply.c (écrivain lourd)

Le principal écrivain de la phase 3. Prend une liste pré-pliée de refs (à partir du diff de scan_fstree_extents + scan_extent_tree) et injecte plus de 3 millions d'EXTENT_DATA_REF dans l'arbre des extents par paquets de 5000 par transaction. Limite tous les 50 000 éléments pour éviter les blocages de ré-aimantation DM-SMR. Vérifié avec succès à 3 248 617 insertions en ~34 min sur 3× disques SMR WD40EFAX.

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

Correcteur chirurgical à un seul champ pour BLOCK_GROUP_ITEM.used lorsque l'écrivain de la phase 3 laisse un groupe de blocs spécifique avec un dépassement dû à des file_extent_items préexistants qui se chevauchent. Utilise le setter direct btrfs_set_block_group_used pour éviter la comptabilité space_info de btrfs_update_block_group (que nous ne voulons PAS ici). Pré-valide 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

Supprime une liste codée en dur d'EXTENT_ITEM (bytenr, num_bytes, expected_inode) de l'arbre des extents. Utilisé pour nettoyer des extents obsolètes qui se chevauchent dans une seule feuille, empêchant le montage RO. Vérifications de cohérence par élément avant suppression (7 invariants, y compris une liste blanche d'inodes). S'exécute avec rebuilding_extent_tree=1 + reinit_extent_tree=true pour ignorer la comptabilité space_info (l'appelant corrige d'abord used manuellement via patch_block_group_used).

clean_orphan_dir_entries.c

Nettoie les entrées DIR_ITEM + DIR_INDEX orphelines du FS_TREE. Par paquets de 100 entrées par transaction. Met à jour i_size de l'INODE_ITEM parent (décrémenter de namelen × 2 : bogue critique corrigé : la v1 décrémentait de namelen seulement, laissant les répertoires dans un état invalide). Liste d'exclusion codée en dur pour les noms de répertoires de premier niveau critiques (par exemple pelis, series, music, backups, homestorage). NE JAMAIS décrémenter i_size par namelen brut : BTRFS stocke une comptabilité de namelen × 2.

clean_orphan_inode_refs.c

Parcourt le FS_TREE pour les éléments INODE_REF dont key.offset (inode parent) figure dans une liste d'orphelins parents. Ignore INODE_EXTREF pour éviter les faux positifs (le key.offset de EXTREF est un hachage, pas un ID parent). Par paquets de 32 par transaction.

fix_dir_inode_counts.c

Recalcule i_size = sum(name_len × 2) et nlink = 1 pour les inodes de répertoire dont les comptes ont été corrompus par des bogues précédents de nettoyage des orphelins. CRITIQUE pour la sécurité : si un répertoire a nlink = 2, un simple rm -rf sur son chemin supprimera silencieusement des milliers de sous-répertoires (bombe rmdir). Parcourt les entrées DIR_INDEX, recoupe avec DIR_ITEM pour la détection de collisions de hachage (0 collisions vérifiées empiriquement).

remove_orphan_inode_subtrees.c

Supprime les sous-arbres d'inodes orphelins (familles DIR + REG autonomes) du FS_TREE. Pour chaque cible : parcourt et supprime EXTENT_DATA, INODE_REF, INODE_EXTREF, XATTR, et enfin INODE_ITEM. Transaction par famille DIR (atomique par sous-arbre), paquets de 50 pour les REG autonomes. Liste d'exclusion paranoïaque codée en dur.

⚠️ AVERTISSEMENT DE SÉCURITÉ MAJEUR : voir "Critère de sous-ensemble infaillible" ci-dessous.

remove_stale_ptrs_v2.c

Version améliorée de remove_stale_ptrs : détecte les feuilles vides avec parent expected_key (v1 ignorait ce cas), scan récursif à 2 niveaux (racine→niveau1 + niveau1→feuilles), tampon dynamique (pas de limite 512), tolère les échecs de read_tree_block.

insert_one_extent_poc.c

Preuve de concept pour l'insertion d'un seul extent avec validation. Utilisé pour valider le chemin API avant d'exécuter rebuild_extent_tree_apply.

Critère de sous-ensemble infaillible (CRITIQUE)

Lors de la session du 2026-04-05, remove_orphan_inode_subtrees a planté deux fois sur la même assertion BUG_ON pour deux raisons différentes :

Vecteur de crash 1 : btrfs_cow_block(leaf) direct sur une feuille MIXED (gen 3601, contient à la fois des inodes orphelins et vivants) → update_ref_for_cow parcourt les enfants → __btrfs_mod_ref(inc=1) sur des enfants frères obsolètes → btrfs_free_extent(phantom) retourne -ENOENT → BUG_ON → SIGABRT.

Vecteur de crash 2 (découvert plus tard, évité par filtrage) : btrfs_del_items après purge draine une feuille en dessous de LEAF_DATA_SIZE/4 = 4096 octets → invoque push_leaf_left(sibling) ou push_leaf_right(sibling) → si le frère a gen ≤ last_snapshot = 3701, btrfs_block_can_be_shared retourne 1 → update_ref_for_cow entre dans le chemin refs > 1 → btrfs_inc_ref(cow_sibling, 0) → __btrfs_mod_ref(cow, level=0, inc=1) → itère tous les EXTENT_DATA du frère obsolète → btrfs_inc_extent_ref(phantom_bytenr) → BUG_ON(err) dans extent-tree.c:1302 → SIGABRT.

Les flags fs_info->rebuilding_extent_tree = 1 et trans->reinit_extent_tree = true ne sauvent PAS le chemin INC : ils exemptent seulement BTRFS_DROP_DELAYED_REF (vérifié dans extent-tree.c:3885). BTRFS_ADD_DELAYED_REF (de btrfs_inc_ref) est fatal.

Critère infaillible pour tout inode cible qui sera supprimé :

  1. La feuille hébergeant l'INODE_ITEM de l'inode a gen > 3701 (post-crash)
  2. Le parent niveau 1 de la feuille a gen > 3701
  3. Estimation des octets used après purge > 4096 (pas de déclencheur de rééquilibrage)
  4. TOUS les frères immédiats dans le nœud parent ont gen > 3701 (même si la condition 3 échoue, le rééquilibrage vers des frères post-crash est sûr)
  5. Toutes les cibles de backref (EXTENT_DATA disk_bytenr) résolvent dans l'arbre des extents actuel (pas de -ENOENT lors de la recherche de backref)

Violer l'une des conditions 3+4 déclenche le vecteur de crash 2. La condition 5 est disculpée par reinit_extent_tree pour DROP mais PAS pour INC (c'est ce qu'invoque push_leaf_left).

Modèle de validation empirique

Pour tout ensemble d'inodes orphelins candidats, parcourez le vidage du FS_TREE et classez chaque feuille cible selon les 5 conditions infaillibles. Exemple de modèle (anonymisé) :

Les feuilles où ≥90% des éléments sont orphelins sont la zone de danger : elles draineront en dessous du seuil de rééquilibrage (LEAF_DATA_SIZE/4 = 4096 octets) avec certitude, forçant push_leaf_left/right. Si un frère immédiat dans le nœud parent a gen ≤ last_snapshot, le push déclenche CoW sur ce frère, qui entre dans le chemin btrfs_block_can_be_shared → refs > 1 → btrfs_inc_ref → __btrfs_mod_ref(inc=1) et plante avec BUG_ON(err) dans btrfs_inc_extent_ref.

Atténuation : exclure les inodes incriminés du fichier d'entrée. L'outil traite tout ce qui passe la validation pré-vol ; les feuilles avec des cibles mixtes sûres/non sûres peuvent être partiellement traitées en ne listant que le sous-ensemble sûr. La sémantique de transaction par famille signifie que chaque famille sûre est validée atomiquement même si d'autres familles sont exclues.

Résultat empirique d'une session : en partant de N orphelins candidats, après application des 5 conditions, le sous-ensemble sûr final était d'environ 14% de l'entrée, mais ce sous-ensemble a été validé sans un seul BUG_ON, avec une différence de 0 octet sur un sha256 de référence des fichiers vivants capturé avant écriture.

Le patch : EEXIST dans alloc_reserved_tree_block

patches/alloc_reserved_tree_block_eexist.patch modifie btrfs-progs pour que lorsque alloc_reserved_tree_block trouve que le METADATA_ITEM existe déjà, il retourne 0 au lieu de propager EEXIST. Ceci est nécessaire pour que l'injection par lots de backrefs fonctionne : lors de l'injection de nombreux backrefs, le système de refs différées tente également de créer des METADATA_ITEM pour les blocs nouvellement alloués via COW et entre en collision avec ceux que nous avons déjà insérés.

Flux de travail complet pour une récupération sévère

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

Limitations connues

  1. Chaque réparation peut créer de nouveaux problèmes via COW : lorsqu'un outil modifie l'arbre des extents, btrfs applique COW sur les nœuds affectés. Les nouveaux nœuds copient les pointeurs des anciens, ce qui peut propager des pointeurs obsolètes. Plusieurs passages peuvent être nécessaires.

  2. Les discordances de backref d'extent de données ne sont pas corrigées : ces outils ne touchent qu'aux backrefs de métadonnées. Les comptes de ref incorrects sur les extents de données (fréquents après des exécutions échouées de btrfs check --repair) ne sont pas nettoyés.

  3. Les inodes orphelins ne sont pas nettoyés : les entrées de répertoires orphelins dans le FS_TREE (références à des inodes qui n'existent plus) ne sont pas supprimées.

  4. Ne remplace pas btrfs check --repair : ces outils ciblent des scénarios spécifiques. Pour des dégâts légers ou modérés, btrfs check --repair est préférable.

Leçons apprises

  1. NE JAMAIS couper l'alimentation en force d'un système de fichiers BTRFS multi-périphériques : la corruption combinée de l'arbre d'espace libre et de l'arbre des extents est extrêmement difficile à réparer.

  2. NE JAMAIS exécuter btrfs check --repair plusieurs fois de suite si la première exécution n'a pas tout résolu : cela peut entrer dans une boucle infinie et aggraver considérablement le système de fichiers.

  3. Toujours sauvegarder les superblocs avant chaque opération d'écriture.

  4. trans->reinit_extent_tree = true est la clé pour ignorer les échecs DROP dans les refs différées pour les blocs sans backref.

  5. fs_info->rebuilding_extent_tree = 1 désactive les vérifications d'espace pendant les réparations.

  6. Un grand commit avec de nombreuses insertions est meilleur que plusieurs petits commits, car les commits intermédiaires déplacent l'arbre racine.

  7. Les backup_slots dans le SB ne sont PAS des sauvegardes historiques : ce sont une fenêtre glissante des 4 commits les plus récents seulement. Une boucle btrfs check --repair de plus de 46 000 commits fera tourner chaque emplacement ~11 000 fois en quelques minutes, anéantissant tout état pré-crash récupérable depuis le noyau. Pour une rétention réelle, vous avez besoin de flux btrfs subvolume snapshot ou btrfs send explicites vers un autre périphérique.

Avis de non-responsabilité

Ces outils ont été écrits pour un cas de récupération spécifique où les outils natifs échouaient. Ils ne sont pas testés pour des cas d'utilisation généraux. Utilisez-les uniquement si vous comprenez le code et acceptez le risque de perte de données.

Toujours copier vos données avant de tenter toute réparation si possible.

Licence

GPL-2.0 (compatible avec btrfs-progs, dont ces outils utilisent l'API interne).

Télécharger l’outil
FeuilleGenÉléments orphelins / totalUtilisé après purge (est.)Rééquilibrage ?Frères immédiatsVerdict
$LEAF_Apost-crashsurtout orphelin, purge lourdeen dessous du seuilOUItous post-crash✓ sûr
$LEAF_Bpost-crashsurtout vivant, purge légèreau-dessus du seuilNONparent propre✓ sûr
$LEAF_Cpost-crashpresque 100% orphelinbien en dessous de 4096OUI forcépré-crash obsolète❌ CRASH
  • reinit_extent_tree est ASYMÉTRIQUE : n'exempte que BTRFS_DROP_DELAYED_REF, PAS BTRFS_ADD_DELAYED_REF. Tout chemin de code qui appelle btrfs_inc_ref sur une feuille obsolète (y compris push_leaf_left/right lors du rééquilibrage) plantera toujours via btrfs_inc_extent_ref → BUG_ON(err).

  • Le critère de sécurité pour traiter les inodes dans un FS_TREE endommagé doit inclure les frères, pas seulement la feuille cible elle-même. Voir la section "Critère de sous-ensemble infaillible".

  • Le sha256 de référence des fichiers VIVANTS est la seule preuve empirique des invariants. Capturez-le avant toute opération d'écriture, différenciez après. Toute différence = retour arrière.

  • i_size d'un répertoire est stocké comme sum(name_len × 2), PAS sum(name_len). Tout outil de nettoyage d'orphelins qui décrémente i_size lors de la suppression d'une entrée doit décrémenter de namelen × 2. Se tromper là-dessus laisse les répertoires dans un état invalide qui peut se manifester plus tard comme nlink = 2, ce qui déclenche une bombe rmdir si le pool est monté RW (un simple rm -rf sur un parent peut supprimer silencieusement des milliers de sous-répertoires).

  • Les agents de révision experts avec des preuves empiriques sont essentiels. La session du 2026-04-05 a utilisé deux réviseurs Opus parallèles (internes btrfs + opérations) qui ont analysé le plan proposé par rapport à la sortie de dump-tree. Ils ont détecté un vecteur de crash déterministe (push_leaf_left → frère obsolète) qui aurait répété les échecs précédents. Une révision textuelle du plan sans analyse empirique de dump-tree aurait manqué cela.