Personal archive
Как я починил NTFS, который CHKDSK не мог исправить: Event ID 55, MFT 0x100000000001a и $Reparse

Всем привет. На днях я столкнулся с довольно редкой и крайне неприятной проблемой NTFS. Один из моих жестких дисков начал постоянно определяться Windows как поврежденный: раздел получал флаг dirty, при загрузке запускалась проверка диска, а в системном журнале появлялась ошибка NTFS Event ID 55.
Самое интересное заключалось в том, что штатные средства Windows никакой реальной ошибки не находили. chkdsk раз за разом сообщал, что файловая система исправна, но после проверки проблема возвращалась.
Я довольно долго искал решение в интернете и нашел несколько людей с практически идентичной ошибкой, в том числе с тем же номером MFT-записи 0x100000000001a. Однако рабочего способа исправления именно этой ситуации мне найти не удалось.
В итоге пришлось разбираться с внутренней структурой NTFS, делать копию метаданных раздела, изучать $Extend\$Reparse и даже написать несколько небольших утилит для точечного удаления поврежденных служебных записей. В моем случае это действительно помогло: после ремонта Windows очистила dirty-флаг, repair state изменился с 0x11 на 0x00, ошибки Event ID 55 прекратились, а последующие перезагрузки больше не запускали проверку диска.
Возможно, эта заметка сэкономит кому-нибудь несколько дней поисков.

С чего всё началось
Проблемный диск — обычный HDD Western Digital Blue на 1 ТБ, NTFS-раздел которого используется одновременно из Windows 11 и Ubuntu.
В Windows это диск E:, в Linux — /dev/sdb2.
В какой-то момент после аварийного отключения питания Ubuntu отказалась нормально монтировать NTFS-раздел. Я точно не могу утверждать, что именно отключение электричества стало причиной повреждения, но незадолго до этого компьютер действительно потерял питание во время работы с одним из программных проектов — возможно, в процессе сборки, компиляции или другой активной файловой операции.
Позже это обстоятельство станет особенно интересным, потому что поврежденной оказалась структура NTFS, связанная с reparse points — junction и symbolic links, которые активно используются, например, инструментами Node.js и различными package manager.
Однако установить первоначальную причину повреждения однозначно мне не удалось, поэтому дальше речь будет именно о диагностике и ремонте, а не о поиске виноватого :).
Как проблема выглядела в Windows
Первое, что бросалось в глаза, — Windows постоянно считала раздел грязным. Соответственно каждый раз при загрузке системы мне предлагалось проверить диск. Проверка завершалась успешно и вроде бы в следующий раз ничего не должно было мешать загрузить без проверки, но как бы не так! А Убунту вообще перестал монтировать диск, потому что у него пометка грязного стояла. Windows же штатными методами, как оказалось, решить проблему была не в состоянии. Делал я разные чеки итд, ничего не помогало.
Команда:
fsutil dirty query E:
показывала, что том имеет установленный dirty bit.
Еще интереснее выглядело состояние штатного механизма NTFS self-healing:
fsutil repair state E:
Windows отвечала:
0x11 — Требуется полная проверка диска
При этом полноценная проверка диска ситуацию не исправляла.
В системном журнале постоянно появлялся NTFS Event ID 55 примерно такого содержания:
Обнаружено повреждение в структуре файловой системы на томе E:.
Основная таблица файлов (MFT) содержит поврежденную запись файла.
Номер ссылки файла: 0x100000000001a.
Имя файла: "<не удалось определить имя файла>".
Иногда рядом появлялся Event ID 98 с требованием перевести том в автономный режим и выполнить полную проверку.
Именно 0x100000000001a впоследствии оказался главным ключом ко всей истории.
Самое странное: CHKDSK говорил, что всё нормально
Я перепробовал практически все очевидные штатные варианты: chkdsk E: /f /x, загрузочный autochk, проверку из WinRE, Repair-Volume -OfflineScanAndFix, Repair-Volume -Scan, /spotfix, /scan /forceofflinefix, ручной запуск fsutil repair initiate для найденной file reference. Проверялись также служебные очереди $Corrupt и $Verify.
Результат получался парадоксальным.
CHKDSK писал:
Windows has scanned the file system and found no problems.
No further action is required.
и:
0 KB in bad sectors.
Но Windows после этого продолжала считать том поврежденным.
То есть получался замкнутый круг: система говорит «нужно выполнить полную проверку», полная проверка говорит «ошибок нет», а затем всё начинается сначала.
Именно на этом этапе я начал искать подобные случаи в интернете. Как оказалось, ошибка не уникальна. Мне удалось найти сообщения других пользователей с тем же номером file reference 0x100000000001a и поврежденным индексом :$R:$INDEX_ALLOCATION.
Причём такие сообщения встречаются как минимум с 2019–2020 годов, а в 2026 году появился ещё один практически идентичный случай: NTFS после монтирования снова становился dirty, Event ID 55 указывал на ту же запись, а chkdsk /f /r не находил никаких проблем.
Но готового способа починить именно эту структуру $Reparse я так и не нашёл. Поэтому дальше пришлось разбираться самостоятельно.
Может быть, просто умирает HDD?
Это было одной из первых версий.
Однако SMART выглядел вполне нормально:
SMART overall-health: PASSED
Raw_Read_Error_Rate: 0
Reallocated_Sector_Ct: 0
Current_Pending_Sector: 0
Offline_Uncorrectable: 0
Power_On_Hours: 11542
Был накоплен UDMA_CRC_Error_Count = 363, но это скорее исторический показатель проблем передачи данных — например кабеля или SATA-порта. Он сам по себе не объяснял текущую логическую ошибку NTFS.
Ни pending sectors, ни reallocations, ни uncorrectable sectors не было.
Сам CHKDSK также показывал:
0 KB in bad sectors.
Поэтому версия с физически погибшим сектором постепенно отошла на второй план.
Расшифровываем загадочный 0x100000000001a
В NTFS file reference содержит номер записи MFT и sequence number.
Младшая часть:
0x1a
в десятичном виде — это:
26
То есть Windows снова и снова жаловалась на MFT record 26.
Дальше я перешел в Ubuntu и воспользовался утилитами проекта ntfs-3g.
ntfsinfo -f -v -i 26 /dev/sdb2
Команда дала очень интересный результат.
MFT record 26 оказался системным файлом:
$Extend\$Reparse
И вот тут расследование наконец сдвинулось с места.
Что такое $Reparse
NTFS хранит информацию о reparse points в специальном системном индексе $Extend\$Reparse.
К reparse points относятся, в частности, junctions, symbolic links, mount points и некоторые другие специальные объекты Windows.
Иными словами, это глобальный индекс файловой системы, в котором NTFS учитывает reparse points тома.
Именно его Windows считала поврежденным.
Что оказалось внутри MFT 26
У $Reparse присутствовал $ATTRIBUTE_LIST, и именно здесь обнаружилась очень подозрительная структура.
Для $INDEX_ALLOCATION:$R одновременно существовали две записи:
$INDEX_ALLOCATION:$R
VCN 0 -> MFT 574762, instance 0
$INDEX_ALLOCATION:$R
VCN 0 -> MFT 26, instance 3
Первая была реальной allocation extent:
MFT record: 574762
Lowest VCN: 0
Highest VCN: 0
Data size: 4096
Allocated size: 4096
Initialized size: 4096
Ее runlist содержал один кластер:
VCN 0 -> LCN 0xb587b72, length 1
Но одновременно в самом MFT 26 находился второй extent того же $INDEX_ALLOCATION:$R:
Lowest VCN: 0
Highest VCN: -1
Data size: 0
Allocated size: 0
Initialized size: 0
Получалось, что два extent одного и того же атрибута начинались с VCN 0.
С bitmap ситуация была похожей.
$BITMAP:$R -> MFT 574762, instance 1, size 8
$BITMAP:$R -> MFT 26, instance 4, size 32
То есть $Reparse содержал явно аномальное сочетание дублирующих служебных атрибутов.
Сравнение со здоровыми NTFS
Чтобы не заниматься ремонтом на основании одного лишь ощущения «выглядит странно», я сравнил MFT 26 с другими исправными NTFS-разделами.
На другом диске с пустым $Reparse структура была предельно простой:
$STANDARD_INFORMATION
$FILE_NAME
$INDEX_ROOT:$R
Никаких:
$ATTRIBUTE_LIST
$INDEX_ALLOCATION
$BITMAP
там не было.
На системном NTFS-разделе Windows $Reparse был, наоборот, очень большим и содержал тысячи записей. Но и там структура была логичной:
один $INDEX_ROOT
один $INDEX_ALLOCATION
один $BITMAP
без конфликтующих дублей.
Это было уже довольно сильным подтверждением, что найденная структура на проблемном HDD действительно неконсистентна.
Еще одно важное доказательство
На диске оставалось несколько вполне рабочих junction.
Windows могла их прочитать:
fsutil reparsepoint query ...
Но попытка удалить один reparse point:
fsutil reparsepoint delete ...
заканчивалась:
Error 1392
Файл или папка повреждены. Чтение невозможно.
И в тот же момент в журнале снова появлялся Event ID 55 с:
0x100000000001a
Это было очень показательно.
Сами junction читались нормально. Ошибка возникала именно тогда, когда Windows пыталась изменить глобальный $Reparse index.
Прежде чем что-либо чинить — резервная копия
На этом этапе экспериментировать непосредственно на HDD было бы плохой идеей.
Сначала я скопировал важные файлы на отдельный внешний диск и проверил содержимое копии.
После этого с помощью ntfsclone был создан metadata-only образ NTFS:
sudo ntfsclone --metadata --ignore-fs-check --force \
--output /home/alexeys2/hdd-ntfs-metadata.img \
/dev/sdb2
Затем была создана отдельная рабочая копия образа.
Очень важный момент: metadata-only образ
ntfsclone— это не полноценный backup пользовательских файлов. Его нельзя просто восстановить целиком на исходный диск и ожидать, что все данные останутся на месте.
Он использовался исключительно как лаборатория для экспериментов с метаданными.
Лаборатория на копии
Для работы я собрал свежую версию: ntfs-3g 2026.7.7 прямо из исходников.
После удаления всех реальных reparse points в рабочем образе сам $INDEX_ROOT:$R корректно превратился в маленький пустой индекс:
Data size: 48
Index header flags: 0x00
Index entries total: 1
Одна запись — это служебная END entry.
Но поврежденные хвосты никуда не исчезли.
$ATTRIBUTE_LIST
$INDEX_ALLOCATION
$INDEX_ALLOCATION
$BITMAP
$BITMAP
Именно их штатный CHKDSK почему-то не убирал.
Первый этап ремонта
У ntfs-3g есть утилита ntfstruncate.
После нескольких экспериментов на рабочей копии была выполнена команда:
ntfstruncate -f -vv IMAGE 26 0xa0 '$R' 0
После этого произошла интересная вещь.
Пустой $INDEX_ALLOCATION:$R из базовой MFT 26 исчез, а внешний allocation в MFT 574762 был уменьшен до нуля:
Highest VCN: -1
Data size: 0
Allocated size: 0
Initialized size: 0
Его runlist стал пустым.
Дополнительно я напрямую проверил системный $Bitmap NTFS и убедился, что бывший allocation cluster действительно был освобожден, а не просто потерял ссылку.
Это был первый успешный шаг.
Почему дальше пришлось писать собственные утилиты
На этом этапе пользоваться высокоуровневым ntfs_attr_remove() вслепую было опасно.
Изначально существовали несколько атрибутов с одинаковыми type/name, поэтому библиотека не давала возможности надежно указать: «удали именно вот эту attribute record».
Я полез в исходники ntfs-3g 2026.7.7.
Там нашлась функция:
ntfs_attr_record_rm(ntfs_attr_search_ctx *ctx)
которая удаляет конкретную найденную запись атрибута.
Причем она умеет удалять соответствующую запись из $ATTRIBUTE_LIST, освобождать extent MFT, если он после этого пуст, и убирать сам $ATTRIBUTE_LIST, когда он больше не нужен.
Поэтому вместо одной универсальной «ремонтной программы» были написаны три очень узкие утилиты.
Каждая из них перед записью проверяла множество условий и отказывалась что-либо делать, если структура отличалась от ожидаемой хотя бы в одном существенном месте.
Первая удаляла оставшийся пустой внешний $INDEX_ALLOCATION:$R.
Вторая — конкретный внешний $BITMAP:$R из MFT 574762.
Третья — последний базовый $BITMAP:$R из MFT 26.
Что получилось на тестовом образе
После трех операций $Reparse выглядел так:
$STANDARD_INFORMATION
$FILE_NAME
$INDEX_ROOT:$R
И больше ничего.
Параметры root:
Data size: 48
Index header flags: 0x00
Index entries total: 1
Это полностью совпало со структурой $Reparse на здоровом контрольном NTFS-разделе.
После этого:
ntfsfix -n IMAGE
успешно проверил MFT, MFTMirr и альтернативный boot sector.
Только после этого я решился повторить процедуру на настоящем HDD.
Ремонт реального раздела
Сначала живой /dev/sdb2 был еще раз проверен и размонтирован.
Оставшиеся пять junction были временно удалены через ntfs-3g. Это были ссылки внутри node_modules; их target-каталоги при этом не удалялись.
После этого $INDEX_ROOT стал пустым small index:
Index header flags: 0x00
Index entries total: 1
Далее в точности повторилась уже проверенная на metadata image последовательность:
1. ntfstruncate для $INDEX_ALLOCATION:$R
2. удаление оставшегося empty allocation
3. удаление external bitmap и освобождение MFT extent
4. удаление последнего base bitmap
Важно: числа вроде
MFT 574762,LCN 0xb587b72и instance0 / 1 / 3 / 4относятся только к моему тому. На другом NTFS эти значения почти наверняка будут другими, поэтому просто копировать их из этой статьи нельзя.
Что осталось после ремонта
Финальный ntfsinfo на настоящем HDD показал:
Dumping attribute $STANDARD_INFORMATION
Data size: 72
Dumping attribute $FILE_NAME
Data size: 82
Dumping attribute $INDEX_ROOT
Data size: 48
Index header flags: 0x00
Index entries total: 1
Больше не существовало:
$ATTRIBUTE_LIST
$INDEX_ALLOCATION
$BITMAP
То есть структура стала такой же, как у исправного пустого $Reparse.
Проверка:
ntfsfix -n /dev/sdb2
завершилась успешно:
Mounting volume... OK
Processing of $MFT and $MFTMirr completed successfully.
Checking the alternate boot sector... OK
NTFS volume version is 3.1.
NTFS partition /dev/sdb2 was processed successfully.
Момент истины — возвращаемся в Windows
После ремонта я загрузился обратно в Windows. Windows при загрузке как обычно предложил мне выполнить автоматическую проверку диска. Я ее не отменял. Ну это как бы логично, потому что мы не снимали в Убунту искуственно этот флаг dirty поэтому я хотел, чтобы чекдиск штатно сделал все как полагается. Провел проверку и убрал dirty сам.
После окончания проверки первой командой было:
fsutil dirty query E:
И впервые за всё время я увидел:
Том - E: не является "грязным"
Дальше:
fsutil repair state E:
ответил:
Имя тома: E: (\Device\HarddiskVolume8)
Состояние повреждения: 0x00 — Очистить
До ремонта здесь постоянно было:
0x11 — Требуется полная проверка диска
Проверка свежих событий NTFS Event ID 55 после загрузки ничего не нашла.
После этого я сделал еще одну контрольную перезагрузку.
На этот раз Windows загрузилась без проверки диска.
Проблема была устранена.
Что, скорее всего, произошло
У меня есть одна правдоподобная версия.
Незадолго до появления проблемы компьютер действительно аварийно выключался из-за отключения электричества во время какой-то активной работы с программным проектом — возможно, сборки или компиляции.
На разделе при этом существовало много junction и symbolic links внутри node_modules.
Возможно, питание пропало как раз во время изменения $Extend\$Reparse, и NTFS остался в промежуточном состоянии: сам индекс продолжал работать, но $ATTRIBUTE_LIST, $INDEX_ALLOCATION и $BITMAP больше не соответствовали нормальной структуре.
Но подчеркну еще раз: это гипотеза, а не установленная причина.
Более того, часть проектов, junction которых находились в $Reparse, вообще никогда не запускалась мной из Ubuntu. Поэтому обвинять Linux или ntfs-3g оснований нет.
Точно так же нет доказательств, что проблема была вызвана Codex, Node.js или каким-то конкретным package manager.
Почему CHKDSK этого не исправил
Это, пожалуй, самый интересный вопрос всей истории.
Windows прекрасно понимала, что с файловой системой что-то не так:
dirty = true
repair state = 0x11
Event ID 55
Более того, Event ID 55 практически пальцем показывал на MFT 26 и :$R:$INDEX_ALLOCATION.
Но CHKDSK снова и снова сообщал:
Windows has scanned the file system and found no problems.
Возможно, это очень редкий edge case: содержимое самого reparse index уже было логически допустимым, тогда как его служебная структура extent/attribute-list оставалась неконсистентной.
Получался своеобразный тупик: обычная проверка не считала состояние достаточно поврежденным, чтобы перестроить $Reparse, но NTFS-драйвер Windows при работе с индексом продолжал обнаруживать нарушение.
Можно ли использовать эту инструкцию на другом диске?
Не как набор команд для слепого копирования.
Если у вас просто dirty NTFS — не надо лезть в MFT 26.
Сначала должны совпасть характерные симптомы:
Event ID 55
file reference 0x100000000001a
:$R:$INDEX_ALLOCATION
dirty возвращается после CHKDSK
repair state остается 0x11
После этого нужно исследовать собственный $Extend\$Reparse и убедиться, что его структура действительно похожа на описанную здесь.
Обязательно сначала сделать резервную копию важных данных.
И любые ручные изменения MFT сначала воспроизводить на metadata clone.
В этой истории мы несколько часов экспериментировали исключительно с копией файловой системы и только после получения структуры, идентичной здоровому контрольному NTFS, повторили операции на настоящем диске.
Итог
Эта история началась как обычное «Windows опять хочет проверить диск», а закончилась разбором внутренних структур NTFS и чтением исходников ntfs-3g.
Самое удивительное — штатные средства Windows практически всё время знали, где находится повреждение, но исправить его не смогли.
В моем случае решением стало точечное приведение $Extend\$Reparse к нормальной структуре.
Результат после ремонта:
dirty: false
repair state: 0x00
Event ID 55: отсутствует
autochk: больше не запускается
Если вы попали на эту статью по запросу:
0x100000000001a
:$R:$INDEX_ALLOCATION
NTFS Event ID 55
fsutil repair state 0x11
то, скорее всего, я прекрасно понимаю, сколько времени вы уже потратили на CHKDSK.
Надеюсь, этот разбор поможет хотя бы понять, куда именно смотреть.


