
Пользовательские инструменты восстановления BTRFS для серьезного повреждения дерева extent, когда btrfs check --repair завершается неудачей (ошибка сегментации, зацикливание или взаимная блокировка)
Пользовательские инструменты, написанные в процессе восстановления 12-ТБ пула BTRFS на нескольких устройствах с серьёзным повреждением дерева экстентов, которое не смогли исправить стандартные команды (btrfs check --repair, --init-extent-tree и т.д.).
См. INCIDENT-ANALYSIS.md для структурированного описания случая восстановления, классификации первопричин и набора конструктивных предложений по улучшению btrfs-progs, которые позволили бы избежать необходимости в большинстве этих инструментов.
Используйте эти инструменты ТОЛЬКО если btrfs check --repair вызывает segfault, входит в бесконечный цикл или оставляет файловую систему в худшем состоянии, чем до него.
Документированные случаи, в которых они помогают:
btrfs check --repair вызывает segfault на [3/8] checking extents (Issue #525)btrfs check --init-extent-tree вызывает взаимоблокировкуbtrfs check --repair входит в бесконечный цикл, повторяя одни и те же исправленияrescue=all,ro, не удаётся смонтировать RWЭти инструменты НЕ для лёгких повреждений. При обычных повреждениях сначала попробуйте 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 включается явно)Инструменты используют внутренний API btrfs-progs и должны собираться внутри дерева исходных кодов 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
Рекомендуемый порядок выполнения:
scan_and_fix_all_backrefs.c (самый важный)Самый важный инструмент. Рекурсивно обходит каждое дерево в файловой системе (ROOT, CHUNK, EXTENT, FS, DEV, CSUM, UUID, FREE_SPACE) и обнаруживает блоки метаданных, у которых отсутствует обратная ссылка METADATA_ITEM в дереве экстентов. Вставляет все недостающие обратные ссылки в одну транзакцию, чтобы избежать проблемы «перемещения корневого дерева между фиксациями».
Использование:
sudo ./scan_and_fix_all_backrefs /dev/sdX # только сканирование
sudo ./scan_and_fix_all_backrefs /dev/sdX --write # сканирование + вставка
fix_owner_refs.cИсправляет owner в inline TREE_BLOCK_REF, когда он не соответствует фактическому btrfs_header_owner() блока. Несоответствия возникают при перераспределении блоков между деревьями во время неудачных попыток восстановления.
sudo ./fix_owner_refs /dev/sdX # сканирование
sudo ./fix_owner_refs /dev/sdX --write # исправление
fix_bad_levels.cИсправляет записи METADATA_ITEM и EXTENT_ITEM с неверным уровнем. Повреждённые уровни (например, 50, 55, 237) — это мусор, оставшийся после того, как btrfs check --repair вошёл в цикл. Проверяется по реальному btrfs_header_level() блока.
sudo ./fix_bad_levels /dev/sdX # сканирование
sudo ./fix_bad_levels /dev/sdX --write # исправление
fix_duplicate_extents.cУдаляет дубликаты METADATA_ITEM (одинаковый bytenr, разные уровни в ключе). Сохраняет тот, уровень которого совпадает с btrfs_header_level, и удаляет другой.
sudo ./fix_duplicate_extents /dev/sdX # сканирование
sudo ./fix_duplicate_extents /dev/sdX --write # удаление дубликатов
remove_stale_ptrs.cСканирует каждый узел уровня‑1 FS_TREE. Обнаруживает устаревшие дочерние указатели с помощью трёх проверок: несоответствие owner, несоответствие first_key или first_key с типом, недопустимым для FS_TREE (например, BLOCK_GROUP_ITEM). Удаляет их с помощью btrfs_del_ptr.
sudo ./remove_stale_ptrs /dev/sdX # сканирование
sudo ./remove_stale_ptrs /dev/sdX --write # удаление
fix_uuid_tree.c / fix_csum_tree.cСоздаёт пустой лист для дерева UUID / дерева CSUM соответственно. Полезно, когда ROOT_ITEM указывает на блок, который был переназначен другому дереву. Ядро автоматически пересоздаёт дерево UUID при монтировании RW. При пустом дереве CSUM файлы с флагом NODATASUM не дают сбоя проверки.
sudo ./fix_uuid_tree /dev/sdX
sudo ./fix_csum_tree /dev/sdX
set_nodatasum.cУстанавливает флаг BTRFS_INODE_NODATASUM на инодах обычных файлов. Используйте это, если дерево контрольных сумм пусто, но файлы всё ещё имеют ожидаемые контрольные суммы, что вызывает ошибки чтения. С NODATASUM ядро пропускает поиск контрольных сумм.
sudo ./set_nodatasum /dev/sdX # сканирование
sudo ./set_nodatasum /dev/sdX --write # применение
fix_fstree_node.cВерсия с жёстко заданным списком устаревших блоков. Предпочитайте remove_stale_ptrs, который обнаруживает их автоматически. Используйте это только если вам нужен ручной контроль над тем, какие конкретные блоки удалять.
add_backrefs.cНачальная версия с жёстко заданным списком недостающих обратных ссылок. Предпочитайте scan_and_fix_all_backrefs, который обнаруживает их автоматически.
Когда базовых инструментов, описанных выше, было недостаточно (пул с 200 000+ ошибок, распределённых по нескольким деревьям), были созданы следующие дополнительные инструменты:
scan_fstree_extents.c + scan_extent_tree.cСканеры проходов 1 и 2, которые обходят FS_TREE и дерево экстентов соответственно, создавая TSV-файлы со всеми сопоставлениями ссылок/экстентов. Используются для построения входных данных для rebuild_extent_tree_apply, когда дерево экстентов нужно перестроить с нуля.
rebuild_extent_tree_apply.c (тяжёлый записывающий инструмент)Основной записывающий инструмент фазы 3. Принимает предварительно свёрнутый список ссылок (из разницы scan_fstree_extents + scan_extent_tree) и вставляет 3M+ EXTENT_DATA_REF в дерево экстентов порциями по 5000 за транзакцию. Ограничивает каждые 50 тыс. элементов, чтобы избежать зависаний при решингел DM-SMR. Проверено успешно: 3 248 617 вставок примерно за 34 минуты на 3× 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