Skip to content
KitploitKITPLOIT
ToolsExploitsBlog
Log in
Submit
ToolsExploitsBlog
Submit

Hacking, PenTest, and Cybersecurity Tools for Your Security Arsenal!

Kitploit is a directory of hacking, cybersecurity, and pentesting tools. Discover the latest project updates to find vulnerabilities, analyze systems, automate testing, and strengthen your security.

FeedsContactPrivacy© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
btrfs_fixes — Custom BTRFS repair tools for severe extent tree corruption where btrfs check --repair fails (segfault, loop, or deadlock) | Kitploit
Tools/GitHubGitHub/msedek/btrfs_fixes
Vulnerability AnalysisForensicsData RecoveryPapers & ResearchLearning & Education
GitHubmsedek/btrfs_fixes

btrfs_fixes

Custom BTRFS repair tools for severe extent tree corruption where btrfs check --repair fails (segfault, loop, or deadlock)

View Repository
9226 months agoNot yet reviewed

Most Popular

View all →

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools →
Share

btrfs_fixes: Custom BTRFS Repair Tools

Custom tools written during recovery of a 12TB multi-device BTRFS pool with severe extent tree corruption that the native commands (btrfs check --repair, --init-extent-tree, etc.) could not repair.

See INCIDENT-ANALYSIS.md for a structured case study of the recovery, a root cause classification, and a set of constructive proposals for upstream btrfs-progs improvements that would have prevented the need for most of these tools.

When to use these tools

Use these tools ONLY if btrfs check --repair segfaults, enters an infinite loop, or leaves the filesystem in worse shape than before.

Documented cases where they help:

  • btrfs check --repair segfaults at [3/8] checking extents (Issue #525)
  • btrfs check --init-extent-tree deadlocks
  • btrfs check --repair enters an infinite loop repeating the same repairs
  • Extent tree with thousands of METADATA_ITEMs carrying wrong owner/level/generation
  • FS_TREE with stale child pointers that reference blocks reused by other trees
  • Pool only mounts with rescue=all,ro, fails to mount RW

These tools are NOT for light corruption. For normal damage, try btrfs check --repair first.

Warnings

  • BACK UP metadata BEFORE running any tool with --write:
    for DEV in sda1 sdb1 sdc1; do
      sudo dd if=/dev/$DEV of=sb_${DEV}.bin bs=4096 count=1 skip=16
    done
    
  • These tools make irreversible changes to the filesystem
  • All default to scan-only mode (--write is opt-in)
  • The filesystem must be unmounted when running these tools
  • Requires btrfs-progs v6.19.1 or similar, with the EEXIST patch applied

Build

The tools use the internal btrfs-progs API and must be built inside the btrfs-progs source tree:

# 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

Tools

Recommended execution order:

1. scan_and_fix_all_backrefs.c (most important)

The most important tool. Recursively walks every tree in the filesystem (ROOT, CHUNK, EXTENT, FS, DEV, CSUM, UUID, FREE_SPACE) and detects metadata blocks that are missing a METADATA_ITEM backref in the extent tree. Injects all missing backrefs in a single transaction to avoid the "root tree moves between commits" problem.

Usage:

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

Fixes the owner in the inline TREE_BLOCK_REF when it doesn't match the block's actual btrfs_header_owner(). Mismatches occur when blocks are reassigned between trees during failed repairs.

sudo ./fix_owner_refs /dev/sdX          # scan
sudo ./fix_owner_refs /dev/sdX --write  # fix

3. fix_bad_levels.c

Fixes METADATA_ITEM and EXTENT_ITEM entries with an incorrect level. Corrupt levels (e.g. 50, 55, 237) are garbage left behind by btrfs check --repair entering a loop. Verified against the block's real btrfs_header_level().

sudo ./fix_bad_levels /dev/sdX          # scan
sudo ./fix_bad_levels /dev/sdX --write  # fix

4. fix_duplicate_extents.c

Deletes duplicate METADATA_ITEMs (same bytenr, different levels in the key). Keeps the one whose level matches btrfs_header_level and deletes the other.

sudo ./fix_duplicate_extents /dev/sdX          # scan
sudo ./fix_duplicate_extents /dev/sdX --write  # delete duplicates

5. remove_stale_ptrs.c

Scans every level-1 node of the FS_TREE. Detects stale child pointers using three checks: owner mismatch, first_key mismatch, or a first key whose type is invalid for the FS_TREE (e.g. BLOCK_GROUP_ITEM). Removes them with btrfs_del_ptr.

sudo ./remove_stale_ptrs /dev/sdX          # scan
sudo ./remove_stale_ptrs /dev/sdX --write  # remove

6. fix_uuid_tree.c / fix_csum_tree.c

Creates an empty leaf for the UUID tree / CSUM tree respectively. Useful when the ROOT_ITEM points to a block that was reassigned to another tree. The kernel regenerates the UUID tree automatically on RW mount. With an empty CSUM tree, files flagged NODATASUM do not fail verification.

sudo ./fix_uuid_tree /dev/sdX
sudo ./fix_csum_tree /dev/sdX

7. set_nodatasum.c

Sets the BTRFS_INODE_NODATASUM flag on regular file inodes. Use this if the csum tree is empty but files still have expected checksums, which causes read errors. With NODATASUM, the kernel skips csum lookups.

sudo ./set_nodatasum /dev/sdX          # scan
sudo ./set_nodatasum /dev/sdX --write  # apply

8. fix_fstree_node.c

Version with a hardcoded list of stale blocks. Prefer remove_stale_ptrs, which detects them automatically. Only use this if you need manual control over which specific blocks to remove.

9. add_backrefs.c

Initial version with a hardcoded list of missing backrefs. Prefer scan_and_fix_all_backrefs, which detects them automatically.

Session 2 toolset (2026-04-04/05): Extended recovery for massive corruption

When the baseline tools above were insufficient (pool with 200K+ errors spread across multiple trees), these additional tools were built:

scan_fstree_extents.c + scan_extent_tree.c

Pass 1 and Pass 2 scanners that walk the FS_TREE and extent tree respectively, producing TSV files with every ref/extent mapping. Used to build input for rebuild_extent_tree_apply when the extent tree needs to be rebuilt from scratch.

rebuild_extent_tree_apply.c (heavy writer)

The main Phase 3 writer. Takes a pre-folded list of refs (from scan_fstree_extents

  • scan_extent_tree diff) and injects 3M+ EXTENT_DATA_REFs into the extent tree in chunks of 5000 per transaction. Throttles every 50K items to avoid DM-SMR re-shingle stalls. Verified successful at 3,248,617 inserts in ~34 min on 3× WD40EFAX SMR disks.
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

Surgical single-field patcher for BLOCK_GROUP_ITEM.used when Fase 3 writer leaves a specific bg with overshoot due to pre-existing overlapping file_extent_items. Uses btrfs_set_block_group_used direct setter to avoid btrfs_update_block_group space_info accounting (which we DON'T want here). Pre-validates flags & BTRFS_BLOCK_GROUP_DATA.

sudo ./patch_block_group_used /dev/sdX1 <bg_bytenr> <bg_length> <new_used> --write

remove_extent_items_by_key.c

Deletes a hardcoded list of (bytenr, num_bytes, expected_inode) EXTENT_ITEMs from the extent tree. Used to clean up overlapping stale extents in a single leaf that prevent RO mount. Per-item sanity checks before delete (7 invariants including inode allowlist). Runs with rebuilding_extent_tree=1 + reinit_extent_tree=true to skip space accounting (caller patches used manually first via patch_block_group_used).

Download Tool