Восстановление информации своими руками

         

Атрибут полного имени $FILE_NAME


Атрибут полного имени файла хранит имя файла в соответствующем пространстве имен. Таким атрибутов у файла можно быть и несколько (например, win32-имя и MS-DOS имя). Здесь же хранятся и жесткие ссылки (hard link), если они есть.

Структура атрибута полного имени приведена ниже:



смещение

размер

описание

~ ~

стандартный атрибутный заголовок (standard attribute header)

00h

8

ссылка (file reference) на материнский каталог

08h

8

C – время создания (creation) файла

10h

8

A – время последнего изменения (altered) файла

18h

8

M – время последнего изменения файловой записи (MFT changed)

20h

8

R – время последнего чтения (read) файла

28h

8

выделенный размер (allocated size) файла

30h

8

реальный размер (real size) файла

38h

4

флаг (см. таблицу 9)

3Ch

4

используется HPFS

40h

1

длина имени в символах – L

41h

1

пространство имен файла (filename namespace)

42h

2L

имя файла в формате UNICODE без завершающего нуля



Атрибут списка атрибутов $ATTRIBUTE_LIST


Атрибут списка атрибутов (прямо каламбур) используется в тех случаях когда все атрибуты файла не умещаются в базовой файловой записи и файловая система вынуждена располагать их в расширенных. Индексы расширенных файловых записей содержатся в атрибуте списка атрибутов, помещаемом в базовую файловую запись.

При каких обстоятельствах атрибуты не умещаются в одной файловой записи? Это может произойти когда: а)файл содержит много альтернативных имен или жестких ссылок; б) файл очень-очень сильно фрагментирован; в) файл содержит очень сложный дескриптор безопасности; г) файл имеет очень много потоков данных (т. е. атрибутов типа $DATA).

Структура атрибута списка атрибутов приведена ниже:

смещение

размер

описание

~ ~

стандартный атрибутный заголовок (standard attribute header)

00h

4

тип (type) атрибута (см. таблицу 8)

04h

2

длина записи (record length)

06h

1

длина имени (name length), или ноль, если нет. условно – N

07h

1

смещение имени (offset to name), или ноль если нет

08h

8

начальный виртуальный кластер (starting VCN)

10h

8

ссылка на базовую/расширенную файловую запись

18h

2

идентификатор атрибута (attribute ID)

1Ah

2N

if N > 0, то имя в формате UNICODE



Атрибут стандартной информации $STANDARD_INFORMATION


Атрибут стандартной информации описывает время создания/изменения/последнего доступа к файлу и права доступа, а так же некоторую другую вспомогательную информацию (например, квоты):

смещение

размер

ОС

описание

~ ~

любая

стандартный атрибутный заголовок (standard attribute header)

00h

8

любая

C время создания (creation) файла

08h

8

любая

A время изменения (altered) файла

10h

8

любая

M время изменения файловой записи (MFT changed)

18h

8

любая

R время последнего чтения (read) файла

20h

4

любая

права доступа MS-DOS (MS-DOS file permissions)

значение

описание

0001h

только на чтение (read-only)

0002h

скрытый (hidden)

0004h

системный (system)

0020h

архивный (archive)

0040h

устройство (device)

0080h

обычный (normal)

0100h

временный (temporary)

0200h

разряженный (sparse) файл

0400h

reparse point

0800h

сжатый (compressed)

1000h

оффлайноый (offline)

2000h

не идексируемый (not content indexed)

4000h

зашифрованный (encrypted)

24h

4

любая

старшее двойное слово номера версии (maximum number of versions)

28h

4

любая

младшее двойное слово номера версии (version number)

2Ch

4

любая

идентификатор класса (class ID)

30h

4

2K

идентификатор владельца (owner ID)

34h

4

2K

идентификатор безопасности (security ID)

38h

8

2K

количество квотируемых байт (quota charged)

40h

8

2K

номер последней последовательности обновления (update sequence number USN)



Атрибуты (attribute)


Структурно всякий атрибут стоит из атрибутного заголовка (attribute header) и тела атрибута (attribute body). Заголовок атрибута всегда хранится в файловой записи, расположенной внутри MFT (см. "файловые записи"). Тела резидентных атрибутов хранятся там же. Нерезидентные атрибуты хранят свое тело вне MFT, в одном или нескольких кластерах, перечисленных в заголовке данного атрибута в специальном списке (см. "списки отрезков"). Если 8-разрдное поле, расположенное по смещению 08h байт от начала атрибутного заголовка, равно нулю– атрибут считается резидентным, а если единице – то нет. Любые другие значения недопустимы.

Первые четыре байта атрибутного заголовка определяют его тип. Тип атрибута в свою очередь определяет формат представления тела атрибута. В частности, тело атрибута данных (тип: 80h – $DATA) представляет собой "сырую" последовательность байт. Тело атрибута стандартной информации (тип: 10h – $STANDARD_INFORMATION) описывает время его создания, права доступа и т. д. Подробнее см. "типы атрибутов".

Следующие четыре байта заголовка содержат длину атрибута, выражаемую в байтах. Длина нерезидентного атрибута равна сумме длин его тела и заголовка, а длина резидентного атрибута равна длине его заголовка. Короче говоря, если к смещению атрибута добавить его длину, мы получим указатель на следующий атрибут (или маркер конца, если текущий атрибут – последний в цепочке).

Длина тела резидентных атрибутов, выраженная в байтах, храниться в 32-разрядном поле, расположенном по смещению 10h байт от начала атрибутного заголовка. 16-разрядное поле, следующее за его концом, хранит смещение резидентного тела, отсчитываемое от начала атрибутного заголовка. С нерезидентными атрибутами в этом плане все намного сложнее и для хранения длины их тела используется множество полей. Реальный размер тела атрибута

(real size of attribute), выраженный в байтах, хранится в 64-разрядном (!) поле, находящемся по смещению 30h байт от начала атрибутного заголовка. Следующее за ним 64-разрядное поле хранит инициализированный размер потока (initialized data size of the stream), выраженный в байтах и, судя по всему, всегда равный реальному размеру тела атрибута. 64-разрядное поле, расположенное по смещению 28h байт от начала атрибутного заголовка, хранит выделенный размер (allocated size of attribute), выраженный в байтах и равный реальному размеру тела атрибута округленному до размера кластера (в большую сторону).


Два 64- разрядных поля, расположенные по смещению 10h и 18h байт от начала атрибутного заголовка задают первый (starting VCN) и последний (last VCN) номер виртуального кластера, принадлежащего телу нерезидентного атрибута. Виртуальные кластеры представляют собой логические номера кластеров, не зависящие от своего физического расположения на диске. В подавляющем большинстве случав, номер первого кластера тела нерезидентного атрибута равен нулю, а последний – количеству кластеров занятых телом атрибута, уменьшенном на единицу. 16-разрядное поле, расположенное по смещению 20h от начала атрибутного заголовка, содержит указатель на массив Data Runs, расположенный внутри этого заголовка и описывающий логический порядок размещения нерезидентного тела атрибута на диске (подробнее см. "списки отрезков").

Каждый атрибут имеет свой собственный идентификатор (attribute ID), уникальный для данной файловой записи и хранящийся в 16-разрядном поле, расположенном по смещению 0Eh от начала атрибутного заголовка.

Если атрибут имеет имя (attribute Name), то 16-разрядное поле, расположенное по смещению 0Ah байт от атрибутного заголовка, содержит указатель на него. Для безымянных атрибутов оно равно нулю (а большинство атрибутов безымянны!). Имя атрибута хранится в атрибутном заголовке в формате UNICODE, а его длина определяется 8-разрядным полем, расположенным по смещению 09h байт от начала атрибутного заголовка.

Если тело атрибута сжато, зашифровано или разряжено, 16-разряное поле флагов, расположенное по смещению 0Ch байт от начала атрибутного заголовка не равно нулю.

Остальные поля не играют сколь ни будь существенной роли и потому здесь не рассматриваются.

смещение

размер

значение

описание

00h

4

тип (type) атрибута (например,. 0x10, 0x60, 0xB0)

04h

4

длина атрибута, включая этот заголовок

08h

1

00h

нерезидентный флаг (non-resident flag)

09h

1

N

длина имени атрибута (ноль если атрибут безымянный)

0Ah

2

18h

смещение имени (ноль если атрибут безымянный)

0Ch

2

00h

флаги

значение

описание

0001h

сжатый атрибут (compressed)

4000h

зашифрованный атрибут (encrypted)

8000h

разряженный атрибут (sparse)

0Eh

2

идентификатор атрибута (attribute ID)

10h

4

L

длина тела атрибута, без заголовка

14h

2

2N+18h

смещение тела атрибута

16h

1

индексный флаг

17h

1

00h

для выравнивания

18h

2N

UNICODE

имя атрибута (если есть)

2N+18h

L

тело атрибута


Автоматическое восстановление файла


Утилиты, восстанавливающие удаленные файлы, не входит в комплект штатной поставки WindowsNT и их приходится приобретать отдельно (а ведь в MS-DOS такая утилита была!). Опасаясь угробить файловую систему окончательно, большинство из них избегает прямой записи на диск – вместо этого вам предлагается считать удаленный файл и переписать его в другое место (но только на сам восстанавливаемый раздел). Не слишком-то удачное решение! А если на остальных дисках свободного места нет или восстанавливаемый диск имеет всего лишь один логический раздел? Предположим, вам необходимо восстановить базу данных в несколько гигабайт. Можно, конечно, подключить второй винчестер, скопировать ее туда, а затем обратно, но сколько же это займет времени, не говоря уже о том, что сервер лучше не выключать, а горячую замену поддерживают далеко не все жесткие диски! Другой недостаток подобных утилит – слишком медленная работа. Вместо того, чтобы найти один-единственный файл, имя которого нам известно, они проводят полномасштабные маневры, сканируя весь раздел целиком. При работе с большими дисками на это уходит от одного до нескольких часов впустую потраченного времени.

С другой стороны, утилиты, вносящие изменения непосредственно в саму NTFS, рискуют серьезно повредить дисковый том, после чего ему не помогут даже профессионалы. Настоящие хакеры [администраторы] не доверят никакому коду, кроме своего собственного, особенно, если исходные тексты недоступны, а документация туманна и двусмысленна. Различные версии NTFS отличаются друг от друга. Последние радикальные изменения произошли в Windows XP (NTFS версии 3.1) – массив последовательности обновления (Update Sequence Number-n-Array) переместился на шесть байтов вперед, а его место было отдано под выравнивание и поле номера текущей файловой записи (Number of this MFT Record). Восстанавливающая утилита должна не только поддерживать вашу версию файловой системы, но и безошибочно отличать ее ото всех остальных (при обновлении Windows 2000 до Windows XP обновления файловой системы не происходит вплоть до переформатирования диска). Попробуй, потом объясни начальству "это не я, это она все испортила!".

Наконец, в момент удаления файла утилит для его восстановления может просто не оказаться под рукой (что поделаешь – закон подлости!) и тогда приходится рассчитывать только на свои силы.



Автоматическое восстановление отформатированного диска


Форматирование не уничтожает файловые записи пользовательских файлов и они могут быть полностью восстановлены. Спасением данных занимается множество утилит (R-Studio, EasyRecovery, GetDataBack и т.д.), однако, прямых наследников unformat'а среди них как-то не наблюдается. Unformat восстанавливал весь том целиком, а эти всего лишь "вытягивают" отдельные уцелевшие файлы/каталоги, переписывая их на новый носитель. Да где же нам его взять?! Запись на оптические накопители отпадает сразу — во-первых, для сохранения 80 — 120 Гбайтного жесткого диска в лучшем случае потребуется грузовик (на чем еще вы перевезете такое количество болванок?), во-вторых, непосредственная запись CD-R/RW не всегда возможна, ведь при крахе системы восстанавливающие утилиты приходится загружать с CD-ROM, а в большинстве компьютеров установлен только один оптический привод, и, в-третьих, ни одна известная мне утилита не позволяет "разрезать" большие файлы на несколько маленьких. Можно, конечно, перегнать данные по локальной сети (а она есть?) или установить дополнительный винчестер (корпус компьютера не опечатан, имеется свободные каналы контроллера и лишняя наличность в кармане). Но не слишком ли это хлопотно? Тем не менее, для "вытягивая" пары сотен особо ценных файлов такая методика вполне подходит.

Продемонстрируем технику автоматического восстановления данных на примере утилиты R-Studio от компании R-TT Inc (www.r-tt.com). Это довольно мощный и в тоже время простой в управлении инструмент, на который можно положиться. После запуска утилиты мы сразу попадаем в окно "Drive View", где перечислены все физические устройства и логические разделы. Находим среди них "свой" и, нажав правую клавишу мыши, говорим "Scan".

Нас запрашивают: начальный сектор для сканирования (start), по умолчанию равный 0 — оставляем его без изменений. Размер сканируемой области (size), по умолчанию развертывается на весь раздел. Это гарантирует, что сканер обнаружит все уцелевшие файловые записи, хотя сам поиск займет значительное время. Можно ли ускорить этот процесс? Давайте возьмем ручку и подсчитаем. Допустим, восстанавливаемый раздел содержит сто тысяч файлов. Типичный размер файловой записи составляет 1 Кбайт. При условии, что $MFT не фрагментирован, достаточно просканировать всего ~100 Мбайт от начала раздела. Если эта величина не превышает 10% полной емкости тома (размер пространства зарезервированного под MFT) и диск никогда не заполнялся более чем на 90%, то, скорее всего, все так и есть. В противном случае, $MFT наверняка фрагментирован и живописно разбросан по всему диску. Впрочем, в случае ошибки мы ничем не рискуем. Вводим N Кбайт, где N – предполагаемое кол-во файлов (каталог также считается файлом) и выполняем сканирование — если один или несколько файлов останутся необнаруженными, возвращаемся к настройкам по умолчанию и повторяем процедуру сканирования вновь (если количество имеющихся файлов заранее неизвестно, вводим 10% от емкости тома). В поле File System выбираем файловую систему NTFS, сбрасывая галочки напротив двух остальных (FAT и Ext2FS), т. к. они нам ни к чему, и нажимам "Scan".



Автоматизированные доктора


Более убогой утилиты, чем ChkDsk– стандартный дисковый "доктор", входящий в штатный комплект поставки Windows, – по-видимому не придумать даже сценаристам из Голливуда. Система диагностики ошибок упрощена до минимума – доктор лишь информирует о факте их наличия, но отказывается говорить, что именно по его мнению повреждено и что он собирается лечить, поэтому последствия такого "врачевания" могут носить фатальный характер.

Известно много случаев, когда ChkDsk залечивал до смерти полностью исправные разделы. С другой стороны, успешно проведенных операций восстановления на его счету намного больше. Обычно он используется неквалифицированными пользователями (и администраторами) для периодической проверки разделов и исправления мелких искажений файловой системы.



Что происходит при форматировании


Форматирование диска— это сложная многостадийная операция, намного более сложная и намного более многостадийная, чем это может показаться на первый взгляд. Кто писал свой собственный форматер дискет (а в конце восьмидесятых—начале девяностых его писали практически все) — тот поймет. Свои исследования мы начнем с изучения NTFS-тома, отформатированного под NTFS (техника восстановления NTFS-томов, отформатированных под FAT16/32 дана в одноименной врезке).

При выполнении команды "format X: /U /FS:NTFS" в файловой системе диска X: происходят следующие изменения (форматирование диска GUI-утилитой, вызываемой из контекстного меню "проводника" осуществляется по аналогичной схеме):

q       формируется boot-сектор в формате NTFS;

q       генерируется новый серийный номер диска и записывается в boot-сектор по смещению 48h байт от его начала;

q       рассчитывается новая контрольная сумма boot-сектора и записывается по смещению 50h от его начала (подробнее см. первую статью этого цикла: "загрузочный сектор — базовые концепции");

q       создается новый $MFT-файл, содержащий сведения обо всех файлах на диске, и как правило, размещаемый поверх старого $MFT-файла; исключения здесь крайне редки — разве что прежний $MFT был заблаговременно перемещен дефрагментатором, или при форматировании назначен новый размер кластера. Во всех остальных случаях первые ~24 файловых записи (FILE Record) мрут безвозвратно. В них находится — непосредственно сам $MFT, $MFTMirr, корневой каталог, /$LogFile – файл транзакций, /$BITMAP – карта свободного пространства, /$Secure – дескрипторы безопасности и другие служебные файлы;

q       инициируется $MFT:$DATA — назначается новая длина ($MFT:$30.AllocatedSize, $MFT:$30.RealSize, $MFT:$80.AllocatedSize, $MFT:$80.RealSize, $MFT:$80.CompressionSize, $MFT:$80.InitializedSize, $MFT:$80.LastVCN), дата/время создания/последней модификации ($MFT:$10.FileCreationTime, $MFT:$10.FileAlertedTime, $MFT:$10.FileReadTime, $MFT:$30.FileCreationTime, $MFT:$30.FileAlertedTime, $MFT:$30.MFTChangeTime, $MFT:$30.FileReadTime) и, самое главное, создается новый список отрезков (data-runs), необратимо затирающий старый, а это значит, что собирать фрагментированный $MFT нам придется по частям (###эк, как тебя разворотило — укорял Василий Иванович Анку, в которую угодил артиллеристский снаряд);


q       создается новый /$MFT:$BITMAP, отвечающий за занятость файловых записей в MFT — все старые записи помечаются как свободные, однако, их фактического удаления при этом не происходит (поле FileRecord.flags остается нетронутым), благодаря чему процедура восстановления заметно упрощается. Чаще всего $MFT:$BITMAP располагается на том же самом месте, что и старый (т. е. между boot-сектором и MFT), забивая прежнее содержимое нулями, однако, с помощью утилиты chkdsk его можно восстановить;

q       создается новый /$BITMAP-файл, отвечающий за распределение дискового пространства (свободные и занятые кластера), опять-таки затирающий старый, но восстанавливаемый chkdsk'ом;

q       создается новый файл журнала транзакций — /$LogFile, в структуру которого мы углубляться все равно не будем, хотя в NTFS LINUX Project она описана достаточно подробно, но восстанавливать транзакции — это уж слишком; ### но восстановление транзакций нам никто не оплатит, так что нехай они идут лесом;

q       в заголовок файловой записи $MFT заноситься новый LogFile Sequence Number или сокращенного LSN;

q       $MFT назначается новый номер последовательности обновления (Update Sequence Number);

q       создается новое зеркало $MFTMirr, необратимое затирающее старое (в текущих системах оно расположено посередине NTFS-раздела), в результате чего возникает резонный вопрос — на хрена нам зеркало, которого ничего не отображает ### какой прок от зеркала, которого ничего не отражает?;

q       создаются новые /$Volume, /$AttrDef и другие служебные файлы, играющие сугубо вспомогательную роль и легко восстанавливаемые chkdsk'ом (хотя и /$Volume и присутствует в зеркальной копии MFT, его ценность явно преувеличена);

q       осуществляется проверка целостности поверхности и все обнаруженные плохие кластеры заносятся в файл /$BadClus;



q       формируется новый корневой каталог;

q       если до форматирования тома на нем присутствовал /System Volume Information-файл, то он обновляется, в противном случае новый /System Volume Information создается только после перезагрузки;

На самом деле, процесс форматирования протекает намного сложнее, но не будем в него углубляться — мы же не форматер пишем! Интересующееся могут взять в руки IDA Pro и расковырять format.com самостоятельно. Подсказка — format.com содержит лишь высокоуровневую надстройку, опирающуюся на библиотеки ifsutil.dll, untfs.dll и… непосредственно сам драйвер файловой системы. Так что дизассемблировать придется много. Чтобы упросить себе работу, можно пронаблюдать за процессом форматирования с помощью шпионских средств — утилит Марка Руссиновича FileMon и DiskMon, бесплатные копии которых можно скачать с www.sysinternals.com. Так же не забывайте о точках останова на основные native-API функции такие как NtFsControlFile, NtDeviceIoControlFile и т. д. Да будет soft-ice вам в помощь!




Динамические диски


Динамические диски, впервые появившиеся в Windows2000 – это все тот же программный RAID, призванный преодолеть ограничения стандартных механизмов разбиения с учетом ошибок своего прямого предшественника программного RAID'а Windows NT, хранящего конфигурационную информацию в системном реестре, что во-первых, препятствовало его перемещению с машины на машину, а во-вторых делало очень уязвимым к порче реестра.



Дисковые доктора


В мире UNIX проверка целостности файловой системы обычно осуществляется программой fsck (аналог chkdsk под WindowsNT), представляющей собой консольную утилиту, практически лишенную пользовательского интерфейса и входящую в штатный комплект поставки любого дистрибьютива. Как и любое другое полностью автоматизированное средство она не только лечит, но, случается, что и калечит, так что пользоваться ей следует с большой осторожностью.



Если вдруг случился сбой и данные оказались утеряны…


Прежде всего– не паникуйте! Заниматься восстановлением можно только на трезвую голову. Непродуманные, лихорадочные действия только усугубляют ваше и без того незавидное положение!

Не используйте никаких автоматизированных "лечилок", если полностью в них не уверенны. Последствия такого лечения могут быть катастрофическими, а результаты "восстановления" – необратимыми. То же самое относится и к "специалистам", обитающим в фирмах непонятного происхождения и орудующих все теми же автоматизированными утилитами, которыми вы можете воспользоваться и без них. Некоторые пытаются создавать необходимый инструментарий самостоятельно. Чаще всего он оказывается неработоспособным еще с рождения, но зато какая гордость для фирмы! Какое впечатляющее средство демонстрации собственной крутизны! Поверьте, утилиты типа Easy Recovery и Get Data Back далеко не дураки писали (да еще и при участии непосредственных разработчиков оригинального драйвера NTFS, хорошо знающих все его тонкости и особенности поведения). Это лучшее из того, что есть на рынке и пока еще никому не удалось их превзойти! (разумеется, речь идет лишь об автоматизированном восстановлении).

Ничего не записывайте на восстанавливаемый диск и не позволяйте этого делать остальным приложениям! Если вы случайно удалили файл с системного диска, ни в коем случае не выходите из Windows "культурным" способом. Лучше нажмите RESET (при штатом завершении сеанса, система сохраняет на диске текущую конфигурацию, существенно увеличивая риск необратимого затирания удаленного файла).

Не пытайтесь "насиловать" сбойные сектора многократными чтениями – это лишь расширяет дефектную область на соседние сектора и здорово уродует магнитную головку, после чего здоровые сектора не смогут читаться тоже. Лучше выполните длинное (long) чтение с диска с отключенными контролирующими кодами, тогда контроллер возвратит все, что осталось от сектора (зачастую сбой затрагивает только несколько байт).


Если винчестер издает подозрительные звуки типа постукивания или скрежета, немедленно выключите питание компьютера (опять-таки, не позволяя системе ничего писать на диск ), поскольку в любой момент винчестер может доломаться окончательно и тогда ему уже никакой электронщик не поможет.

Восстанавливайте SCSI (и, в особенности, RAID!) диски только на "родном" контроллере (различные контроллеры используют различные схемы трансляции адресов). Если же контроллер сдох, то либо ремонтируйте его, либо ищите точно такой же. С IDE-дисками в этом плане намного проще, – их контроллеры более или менее стандартизованы, однако, с дисками большого объема (свыше 528 Мбайт) уже начинается неразбериха и путаница, ставящая их в зависимость от конкретной BIOS и выбранного режима работы (NORMAL, LBA или LARGE). Если восстанавливаемый диск работает под управлением нестандартных драйверов типа Rocket, OnDisk и т.д., они должны присутствовать и на загрузочной диске (загрузочном CD), с которого производится восстановление.

Наконец, если данные восстановить так и не удалось – не расстраивайтесь. Во всем в жизни надо видеть и хорошие стороны, даже когда ничего хорошего нет.


Файловая система NTFS извне и изнутри


крис касперски

"Покажи мне свои структуры данных– и я скажу кто ты!"

народная программистская мудрость

в этой статье ### главе мы рассмотрим основные структуры данных файловой системы NTFS, определяющие ее суть – главную файловую запись MFT, файловые записи FILE Record, последовательности обновления (update sequence или fix-ups), атрибуты (так же называемые потоками – streams) и списки отрезков (run-lists).

без этих знаний осмысленная работа с редактором диска и ручное/полуавтоматическое восстановление данных просто невозможны!



Файловые записи (FILE Record)


Благодаря наличию DiskExplorer'а от Runtime Software с файловыми записями практически никогда не приходится работать вручную, тем не менее знание их структуры нам не помешает.

Структурно файловая запись состоит из заголовка

(header) и одного или нескольких атрибутов

(attribute) произвольной длинны, завершаемых маркером конца (end marker) – четырехбайтовым шестнадцатеричным значением FFFFFFFFh (см. листинг 1). Несмотря на то, что количество и длина атрибутов меняется от одной файловой записи к другой, размер самой структуры FILE Record строго фиксирован и в больше случаев равен 1 Кбайту (это значение храниться в $boot-файле, не путать с boot-сектором!). Причем, первый байт файловой записи всегда совпадает с началом сектора.

Если реальная длина атрибутов меньше размеров файловой записи, ее хвост просто не используется. Если же атрибуты не вмещаются в отведенное им пространство, создается дополнительная файловая запись (extra FILE Record), ссылающаяся на свою предшественницу.

FILE Record

      Header            ; заголовок

      Attribute 1       ; атрибут 1

      Attribute 2       ; атрибут 2

      ...               ; …

      Attribute N       ; атрибут N

End Marker (FFFFFFFFh)  ; маркер конца



Главная файловая запись (master file table)


В процессе форматирования логического раздела, в его начале создается так называемая MTF-зона (MFT-zone), по умолчанию занимающая 12.5% от емкости тома (а вовсе не 12%, как утверждается во многих публикациях), хотя в зависимости от значения от параметра NtfsMftZoneReservation она может составлять 25%, 37% или 50%.

В этой области расположен $MFT-файл, изначально занимающий порядка 64секторов и растущий от начала MFT-зоны к ее концу по мере создания новых пользовательских файлов/подкаталогов. Таким образом, чем больше файлов содержаться на дисковом томе, тем больше размер MTF. Приблизительный размер MFT-файла можно оценить по следующей формуле: sizeof(FILE Record) N Files, где sizeof(FILE Record) обычно равен 1 Кбайт, а N Files – полное количество файлов/подканалов раздела, включая недавно удаленные.

Для предотвращения фрагментации $MFT-файла, MFT-зона удержится зарезервированной вплоть до полного исчерпания свободного пространства тома, затем незадействованный "хвост" MFT-зоны усекается в два раза, освобождая место для пользовательских файлов. Этот процесс может повторяться многократно, вплоть до полной отдачи всего зарезервированного пространства. Решение красивое, хотя и не новое. Многие их файловых систем восьмидесятых позволяли резервировать заданное дисковое пространство в хвосте активных файлов, тем самым сокращая их фрагментацию (причем, любых файлов, а не только служебных). В частности, такая способность была у DOS 3.0, разработанной для персональных компьютерах типа Агат. Может кто помнит такую машину?

Когда $MFT-файл достигает границ MFT-зоны, в ходе своего последующего роста он неизбежно фрагментируется, вызывая обвальное падение производительности файловой системы, причем подавляющее большинство дефрагментатов $MFT-файл не обрабатывают! А ведь API дефрагментации, встроенное в штатный NTFS-драйвер это в принципе позволяет! Подробности (вместе с самой утилитой дефрагментации) можно найти на сайте Марка Руссиновича. Но, как бы там ни было, заполнять дисковый том более чем на 88% его емкости категорически не рекомендуется!

При необходимости, $MFT-файл может быть перемещен в любую часть диска и тогда в начале тома его уже не окажется. Стартовый адрес $MFT-файла хранится в Boot-секторе по смещению 30h байт от его начала (см. "boot-сектор", описанный в предыдущей статье данного цикла) и в подавляющем большинстве случаев этот адрес ссылается на 4й кластер.



Hex-редакторы


UNIX– это вам не Windows! Без дисковых редакторов здесь, в принципе, можно и обойтись. Берем любой hex-редактор, открываем соответствующее дисковое устройство (например, /dev/sdb1) и редактируем его в свое удовольствие. Красота! Старожилы должно быть помнят, как во времена первой молодости MS-DOS, когда ни HIEW'а, ни QVIEW'a еще не существовало, правка исполняемых файлов на предмет "отлома" ненужного 7xh обычно осуществлялось DiskEdit'ом, т. е. дисковый редактор использовался как hex. Вот! А в UNIX, наоборот, hex-редакторы используются для редактирования диска.

Какой редактор выбрать? В общем-то это дела вкуса (причем, не только вашего, но еще и составителя дистрьбьютива). Одни предпочитают консольный hexedit, другие тяготеют к графическому khededit, а третьи выбирают BIEW (урезанная калька со всем известного HIEW'a).



Инструментарий


Программ, пригодных для восстановления данных, под Linux совсем немного, намного меньше, чем под WindowsNT, да и тем до совершенства как до Луны. Но ведь не разрабатывать же весь необходимый инструментарий самостоятельно?! Будем использовать то, что дают, утешая себя тем, что нашим предкам, работающим на динозаврах машинной эры, приходилось намного сложнее.



Инструменты


Даже если у вас золотые руки и светлая голова, при восстановлении данных ни за что не обойтись без инструментов. В идеале вы должны быть готовыми разработать все необходимое для работы самостоятельно. Восстановление данных– довольно кропотливая и рутинная работа и при реанимации 80 – 120 Гбайтного диска без автоматизации никуда. Недостаток всех известных дисковых докторов – отсутствие встроенного языка или хотя бы развитой системы макрокоманд. Естественно, прежде чем что-то автоматизировать необходимо разобраться в ситуации и выполнить эту работу может только человек. Компьютеру доверять ее ни в коем случае нельзя – для этого он недостаточно интеллектуален. Только человек может надежно отличать где лежат актуальные данные, а где мусор.

Однако, не стоит впадать и в другую крайность, в очередной раз изобретая велосипед. Среди представленных на рынке утилит, есть практически все необходимое. Естественно, большинство распространяются по коммерческой схеме и за них приходится платить. К сожалению, многие из дорогостоящих инструментов не оправдывают своих ожиданий и к выброшенным на ветер деньгам примешивается горечь утраты по безвозвратно потерянным данным. Автор протестировал большое количество разнообразных программных продуктов и ниже описывает наиболее предпочтительные из них, проверенные энтропией и временем.



Как мы будем действовать


После непреднамеренного удаления одного или нескольких файлов немедленно демонтируйте раздел и запустите дисковый редактор, работающий на секторов уровне. Например, можно воспользоваться BSD-портом уже известного нам редактора lde. К сожалению, на моем системе (4.5 BSD) он работает крайне нестабильно и не отображает основные структуры данных в удобочитаемом виде, хотя поддержка UFS в нем заявлена. При наличии достаточного количества свободного места можно скопировать раздел в файл и натравить на него любой hex-редактор (например, biew) или открыть непосредственно само устройство раздела (типа /dev/ad0s1a). А еще можно вставить в привод загрузочный CD-ROM с Windows PE и воспользоваться любым Windows-редактором от Microsoft Disk Probe до Runtime Disk Explorer'а. То же самое справедливо и для Norton Disk Editor'а, запущенного c дискеты из-под MS-DOS (правда ни диски большого объема, ни SCSI-устройства он не поддерживает). Еще можно запустить KNOPPIX или любой Live LINUX, ориентированный на восстановление (правда, в большинстве "реанимационных" дистрибутивов, и в частности, Frenzy 0.3, никакого дискового редактора вообще нет!)

В общем, как говорится, на вкус и цвет товарищей нет…



командный файл для ручного


Теперь можно смело пускать chkdsk. Если же он по-прежнему не работает, значит, либо поврежден загрузочный сектор (а методику его восстановления мы уже обсуждали), либо run-list файла $MFT:$DATA не совпадает с истинным началом MFT (найдите сектор с файловой записью $MFT внутри и исправьте run-list), либо размер кластера, прописанный в boot-секторе отличается от его фактического размера (о том как определять истинный размер кластера мы уже только что говорили).

Если же сбой был настолько серьезен, что вместе с $MFT пострадало и зеркало, задача сводится к восстановлению отформатированного диска. Кстати говоря, при тяжелых разрушениях файловой структуры, когда на диске образуется настоящий кавардак, восстановление тома полезно начинать с… его форматирования. Нет, это не первоапрельская шутка! Утилита format формирует заведомо исправные ключевые структуры, ну а подключение файловых записей — не проблема. Главное — сохраните run-list файла $MFT:$DATA, если, конечно, он еще уцелел. Все остальное — дело техники!



структура дискового тома, размеченного под ext2fs


Вслед за супер-блоком идут дескрипторы групп (group descriptors), и карты свободного пространства, в просторечии — битмапы (block bitmap/inode bitmap), которые нас мало интересуют, а вот indoe-таблицу, расположенную за ними, мы рассмотрим поподробнее. В ext2fs (как и многих других файловых системах из мира UNIX), inode играет ту же самую роль, что и FILE Record в NTFS. Здесь сосредоточена вся информация о файле: тип файла (обычный файл, директория, символьная ссылка и т. д.), логический и физический размер, схема размещения на диске, время создания, модификации, последнего доступа и удаления, правда доступа и количество ссылок на файл.

смещение   размер описание

------- ------- -----------

    0       2 i_mode       ; формат представления описание

    2       2 i_uid        ; uid пользователя

    4       4 i_size       ; размер файла в байтах

    8       4 i_atime             ; время последнего доступа к файлу

   12       4 i_ctime             ; время создания файла

   16       4 i_mtime             ; время модификации файла

   20       4 i_dtime             ; время удаления файла

   24       2 i_gid        ; gid группы

   26       2 i_links_count ; количество ссылок на файл (0 – файл удален)

   28       4 i_blocks            ; количество блоков, принадлежащих файлу

   32       4 i_flags             ; разные флаги

   36       4 i_osd1       ; OS dependant value

   40  12 x 4 i_block             ; 12 DIRECT BLOCKS (ссылки на первые 12 блоков файла)

   88       4 i_iblock            ; 1x INDIRECT BLOCK

   92       4 i_2iblock    ; 2x INDIRECT BLOCK

   96       4 i_3iblock    ; 3x INDIRECT BLOCK

  100       4 i_generation ; поколение файла (используется NFS)

  104       4 i_file_acl   ; внешние

атрибуты

  108       4 i_dir_acl    ; higer size

  112       4 i_faddr             ; положение последнего фрагмента

  116      12 i_osd2       ; OS dependant structure



структура файловой записи


Первые четыре байта заголовка оккупированы магической последовательностью "FILE", сигнализирующей о том, что мы имеем дело с файловой записью типа FILE Record. При восстановлении сильно фрагментированного $MFT файла это обстоятельство играет решающую роль, поскольку позволяет отличить сектора, принадлежащие MFT, от всех остальных секторов.

Следом за сигнатурой идет 16-разрядный указатель, содержащий смещение последовательности обновления (update sequence, см. "последовательности обновления"). Под "указателем" здесь и до конца раздела подразумевается смещение от начала сектора, отсчитываемое от нуля и выраженное в байтах. В NT и W2K это поле всегда равно 002Ah, поэтому для поиска файловых записей можно использовать сигнатуру "FILE*\x00", что уменьшает вероятность ложных срабатываний. Правда, в XP и более старших системах, последовательность обновления хранится по смещению 002Dh и поэтому сигнатура приобретает следующий вид "FILE-\x00".

Размер заголовка (кстати сказать, варьирующийся от одной операционной системы к другой) в явном виде нигде не хранится, вместо этого в заголовке присутствует указатель на первый атрибут, содержащий его смещение в байтах относительно начала FILE Record, расположенный по смещению 14h байт от начла сектора. Смещения последующих атрибутов (если они есть) определяются путем сложения размеров всех предыдущих атрибутов (размер каждого из атрибутов содержится в его заголовке) со смещением первого атрибута. За концом последнего атрибута находится маркер конца – FFFFFFFFh.

В добавок к этому длина файловой записи хранится в двух полях – 32-разрядное поле реального размера (real size), находящееся по смещению 18h байт от начала сектора, содержит совокупный размер заголовка, всех его атрибутов и маркера конца, округленный по 8 байтной границе. 32-разрядное поле выделенного размера (allocated size), находящееся по смещению 1Сh байт от начала сектора, содержит действительный размер файловой записи в байтах, округленный по размеру сектора. Документация Linux-NTFS Project (версия 0.4) утверждает, что allocated size должен быть кратен размеру кластера, однако, в действительности это не так. В частности, на моей машине, allocated size равен четвертинке кластера.


16- разрядное поле флагов, находящееся по смещению 16h байт от начала сектора, в подавляющем большинстве случаев принимает одно из трех следующих значений: 00h – данная файловая запись не используется или ассоциированный с ней файл/каталог удален, 01h – файловая запись используется и описывает файл, 02h – файловая запись используется и описывает каталог.

64-разрядное поле, находящееся по смещению 20h байт от начала сектора содержит индекс базовой файловой записи. Для первой файловой записи это поле всегда равно нулю, а для всех последующих, расширенных (extra) записей – индексу первой файловой записи. Расширенные файловые записи могут находится в любых частях MFT, не обязательно рядом с основной записью. А коль скоро так, необходим какой-то механизм, обеспечивающий быстрый поиск расширенных файловых записей, принадлежащих данному файлу (просматривать весь MFT целиком не предлагать). И этот механизм основан на ведении списков атрибутов $ATTRIBUTE_LIST. Список атрибутов представляет собой специальный атрибут, добавляемый к первой файловой записи и содержащий индексы расширенных записей. Формат списка атрибутов приведен в разделе "типы атрибутов".

Остальные поля заголовка файловой записи не столь важны и поэтому здесь не рассматриваются. При необходимости обращайтесь к документации "Linux-NTFS Project".

смещение

размер

ОС

описание

00h

4

любая

сигнатура (magic number) 'FILE'

04h

2

любая

смещение номера последовательности обновления (update sequence number)

06h

2

любая

размер в словах номера последовательности обновления и массива обновления (Update Sequence Number & Array), условно (S)

08h

8

любая

номер последовательности файла транзакций ($LogFile Sequence Number или сокращенно LSN)

10h

2

любая

номер последовательности (sequence number)

12h

2

любая

счетчик жестких ссылок (hard link)

14h

2

любая

смещение первого атрибута (attribute)

16h

2

любая

флаги (flags)

значение

описание

0x00

файловая запись не используется

0x01

файловая запись используется и описывает файл (file)

0x02

файловая запись используется и описывает каталог (directory)

0x04

только Бил Гейтс знает

0x08

только Бил Гейтс знает

18h

4

любая

реальный размер (real size) файловой записи

1Ch

4

любая

выделенный размер (allocated size) файловой записи

20h

8

любая

ссылка (file reference) на базовую файловую запись (base FILE record) или ноль, если данная файловая запись базовая

28h

2

любая

идентификатор следующего атрибута (next attribute ID)

2Ah

2

XP

для выравнивания

2Ch

4

XP

индекс данной файловой записи (number of this MFT record)

2

любая

номер последовательности обновления (update sequence number)

2S-2

любая

массив последовательности обновления (update sequence array)


оригинальная файловая запись до восстановления


Сигнатура "FILE" указывает на начало файловой записи. А раз так, по смещению 04h байт будет расположен 16-разрядный указатель на номер последовательности обновления. В данном случае он равен 002Ah. ОК! Переходим по смещению 002Ah и видим, что здесь лежит слово 0006h. Перемещаемся в конец сектора и сверяем его с последними двумя байтами. Как и предполагалось, они совпадают. Повторяем ту же самую операцию со следующим сектором. Собственно говоря, количество секторов может и не равняться двум. Чтобы не гадать на кофейной гуще, необходимо извлечь 16-разрядное значение, расположенное по смещению 06h от начала файловой записи (в данном случае оно равно 0003h) и вычесть из него единицу. Действительно, получается два (сектора).

Теперь нам необходимо найти массив последовательности обновления, хранящий оригинальное значение последнего слова каждого из секторов. Смещение массива обновления равно значению указателя на последовательность обновления увеличенной на два, т. е. в данном случае 002Ah + 02h == 002Ch. Извлекаем первое слово (в данном случае равное 00h 00h) и записываем его в конец первого сектора. Извлекаем следующей слово (47h 11h) и записываем его в конец второго сектора.

В результате чего, восстановленный сектор будет выглядеть так:

-->
начало первого сектора FILE Record

00000000:  46 49 4C 45-2A 00 03 00-7C 77 1A 04-02 00 00 00  FILE* ¦ |w>¦O  

00000010:  01 00 02 00-30 00 01 00-28 02 00 00-00 04 00 00  O O 0 O (O   ¦ 

00000020:  00 00 00 00-00 00 00 00-06 00 06 00-00 00 47 11          ¦ ¦   G<

000001F0:  00 00 00 00-00 00 00 00-00 00 00 00-00 00 00 00                ¦

<-- конец первого сектора FILE Record

000003F0:  07 CC E1 0D-00 09 00 00-FF FF FF FF-82 79 47 11  •Iad 0  yyyyВy¦

<-- конец второго сектора FILE Record



формат представления массива директорий


При удалении файла, операционная система находит соответствующую запись в директории. Обнуляет поле inode

и увеличивает размер предшествующей записи (поле ren_len) на величину удаляемой. То есть, предшествующая запись как бы "поглощает" удаленную. И хотя имя файла до поры до времени остается нетронутым, ссылка на соответствующую ему inod'у оказывается уничтоженной. Вот и попробуй разобраться какому файлу какое имя принадлежит!

В самой inod'e при удалении файла тоже происходят большие изменения. Количество ссылок (i_links_count) сбрасывается в нуль и обновляется поле последнего удаления (i_dtime). Все блоки, принадлежащие файлу, в карте свободного пространства (block bitmap) помечаются как неиспользуемые, после чего данная inod'а так же освобождается (inode bitmap).



формат супер-блока (второстепенные поля опущены)


За концом супеблока, на некотором отдалении от него, находится первая группа цилиндров. В начале каждой группы расположена служебная структура cg (далее по тексту — описатель группы цилиндров, термин мой — КК), содержащая магическую последовательность 55h 02h 09h по которую все уцелевшие группы можно найти даже при полностью испорченном супеблоке (штатным образом, стартовые адреса всех последующих групп вычисляются путем умножения номера группы на ее размер, содержащийся в поле fs_cgsize).

Другие важные параметры:

q       cg_cgx — порядковой номер группы, отсчитываемый от нуля;

q       cg_old_niblk — кол-во inode в данной группе;

q       cg_ndblk — кол-во блоков данных в данной группе;

q       csum — кол-во свободных inode и блоков данных в данной группе;

q       cg_iusedoff — смещение карты занятых inod'e, отсчитываемое от начала данной группы и измеряемое в байтах;

q       cg_freeoff — смещение карты свободного пространства (байты от начла группы);

Структура cg определена в файле /src/ufs/ffs/fs.h и выглядит следующим образом:

#define CG_MAGIC     0x090255

#define MAXFRAG      8

struct cg {

/* 0x00 */ int32_t  cg_firstfield;       /* historic cyl groups linked list */

/* 0x04 */ int32_t  cg_magic;                   /* magic number */

/* 0x08 */ int32_t  cg_old_time;         /* time last written */

/* 0x0С */ int32_t  cg_cgx;              /* we are the cgx'th cylinder group */

/* 0x10 */ int16_t  cg_old_ncyl;         /* number of cyl's this cg */

/* 0x12 */ int16_t  cg_old_niblk;        /* number of inode blocks this cg */

/* 0x14 */ int32_t  cg_ndblk;                   /* number of data blocks this cg */

/* 0x18 */ struct  csum cg_cs;           /* cylinder summary information */

/* 0x28 */ int32_t  cg_rotor;                   /* position of last used block */


/* 0x2T */ int32_tа cg_frotor;аааааааааа /* position of last used frag */

/* 0x30 */ int32_tа cg_irotor;аааааааааа /* position of last used inode */

/* 0x34 */ int32_tа cg_frsum[MAXFRAG];аа /* counts of available frags */

/* 0x54 */ int32_tа cg_old_btotoff;аааааааааааа /* (int32) block totals per cylinder */

/* 0x58 */ int32_tа cg_old_boff;аааааааа /* (u_int16) free block positions */

/* 0x5T */ int32_tа cg_iusedoff;аааааааа /* (u_int8) used inode map */

/* 0x60 */ int32_tа cg_freeoff;ааааааааа /* (u_int8) free block map */

/* 0x64 */ int32_tа cg_nextfreeoff;аааааааааааа /* (u_int8) next available space */

/* 0x68 */ int32_tа cg_clustersumoff;аааааааааа /* (u_int32) counts of avail clusters */

/* 0x6T */ int32_tа cg_clusteroff;аааааа /* (u_int8) free cluster map */

/* 0x70 */ int32_tа cg_nclusterblks;ааааааааааа /* number of clusters this cg */

/* 0x74 */ int32_tа cg_niblk;аааааааааааааааааа /* number of inode blocks this cg */

/* 0x78 */ int32_tа cg_initediblk;аааааа /* last initialized inode */

/* 0x7T */ int32_tа cg_sparecon32[3];аааааааааа /* reserved for future use */

/* 0x00 */ ufs_time_t cg_time;аааааааааа /* time last written */

/* 0x00 */ int64_tа cg_sparecon64[3];аааааааааа /* reserved for future use */

/* 0x00 */ u_int8_t cg_space[1];аааааааа /* space for cylinder group maps */

а/* actually longer */


интерфейс с IDE-диском в режиме CHS


Сервисные функции BIOS'а, напротив, адресуют диск слегка по своему:

регистр                 значение

AL                          кол-во секторов для обработки

CH                          номер цилиндра (биты 0-7)

CL                          номер цилиндра (биты 6-7), номер сектора (биты 0-5)

DH                          номер головки

DL                          привод на шине | 80h



формат представления inode


Первые 12 блоков, занимаемых файлов, хранятся непосредственно в самой inod'е в массиве DIRECT BLOCKS (непосредственные блоки, для наглядности выделены полужирным). Каждый элемент массива представляет собой 32-битный номер блока. При среднем значении BLOCK_SIZE в 4 Кбайта, DIRECT BLOCK'и могут адресовать до 4 12 == 48 Байт данных. Если файл превышает этот размер, создаются один или несколько блоков косвенной адресации (INDIRECT BLOCK). Первый блок косвенной адресации (1x INDIRECT BLOCK или просто INDIRECT BLOCK) хранит ссылки на другие непосредственные блоки и может. Адрес этого блока хранится в поле i_indirect_block в inod'e. Как легко посчитать, он адресует порядка BLOCK_SIZE/sizeof(DWORD) * BLOCK_SIZE = 4096/4 *4 Мбайт данных. Если этого вдруг окажется недостаточно, создается дважды косвенный блок (2x INDIRECT BLOCK или DOUBLE INDIRECT BLOCK), хранящий указатели на косвенные блоки, что позволяет адресовать (BLOCK_SIZE/sizeof(DWORD))**2* BLOCK_SIZE =4096/4 ** 4096 == 4 Гбайт данных. Если же этого все равно недостаточно, создается трижды косвенный блок (3x INDIRECT BLOCK или TRIPLE INDIRECT BLOCK), содержащий ссылки на дважды косвенные блоки (на данном рисунке трижды косвенный блок не показан).

Отметим, что по сравнению с NTFS такая схема хранения информации об размещении намного более просто устроена, но в месте с тем и прожорлива. Однако, одна обладает одним несомненным достоинством, которое рвет NTFS как Тузик грелку. Поскольку все ссылки хранятся в неупакованном виде, для каждого блока файла мы может быстро найти соответствующий ему косвенный блок, даже если inod'а полностью разрушена.



интерфейс с прерыванием INT13h BIOS


Таким образом, на адресацию цилиндров BIOS отводит всего 10 бит и потому максимальное количество цилиндров на диске ограничено всего 1024, что при четырех битной адресации головок, дает предельно достижимый объем диска в 512 * 210 * 26 * 24 == 536870912 байт или 512 Мб. Ха! Производители винчестеров перешагнули этот барьер уже много лет назад и с той поры, кстати говоря, очень многое изменилось. MS-DOS ушла небытие, а пришедшая ей на смену Windows работает с диском через собственный драйвер и ограничения BIOS ее никак не касаются. Ну почти не касаются… Ведь первичную загрузку операционной системы осуществляет никто иной как BIOS и если системные компоненты расположены в секторах, находящихся за пределами 1024 сектора, операционная система попросту не будет загружена! Причем, это относиться ко всем операционным системам, а не только к критикуемой Windows!

Для преодоления этого ограничения BIOS вводит дополнительный уровень трансляции (режим LARGE), увеличивая количество головок (благо, BIOS выделяет для их адресации аж 8 бит, против 4 бит, выделяемых контроллером диска). К следствие, предельно допустимый объем диска теперь составляет 512 210 26 * 28 == 8.589.934.592 байт или 8 Гбайт. Это в теории. На практике же большинство BIOS'ов содержали грубые ошибки и при работе с дисками свыше 2 Гб они либо банально зависали, либо теряли старшие разряды цилиндра, обращаясь к началу диска и необратимо гробя все служебные структуры. До сих пор многие вполне современные BIOS'ы не позволяют адресовать более 64 головок (виртуальных), что ограничивает предельно допустимый объем диска все тем же 2 Гбайтами. Поэтому, при переустановке Windows поверх старой версии на логический диск емкостью свыше 2 Гбайт, она может перестать загружаться. Все очень просто! Когда система ставится на только что отформатированный диск, она располагает все свои файлы в самом начале, но по мере же заполнения диска, область свободного пространства отодвигается все дальше к концу… Кстати говоря, отодвинуть файлы первичной загрузки может и дефрагментатор (или установка пакета обновления). Короче говоря, владельцем больших винчестеров настоятельно рекомендуется разбить свое хозяйство на несколько дисков, установив размер первого (загрузочного) раздела не более, чем в 8 Гбайт, а лучше даже в 2 Гбайта.

SCSI-устройства от рождения поддерживают прозрачный механизм логической адресации, или сокращенно LBA (Linear Block Address), последовательно нумерующий все сектора от 0 до последнего сектора диска. В IDE-накопителях LBA-адресация появилась только начиная с ATA-3, но быстро завоевала всеобщее признание. Разрядность адресации определяется устройством. В SCSI она от рождения 32-битная, а IDE-устройства вплоть до принятия спецификации ATA-6 были ограничены 28 битами, которые распределялись следующим образом:

порт                       значение

0172/01F2            кол-во секторов

0173/01F3            номер сектора (биты 0-7)

0174/01F4            номер сектора (биты 8-15)

0175/01F5            номер сектора (биты 16-24)

0176/01F6            номер сектора (биты 24-28), привод на шине (бит 4), режим CHS/LBA (бит 6)



структура описателя группы цилиндров


Между описателем группы цилиндров и группой inode расположена карта занятых inode и карта свободного дискового пространства, представляющие собой обыкновенные битовые поля, точно такие же как и в NTFS. При восстановлении удаленных файлов без этих карт никуда! Отделяя зерна от плевел, они существенно сужают круг поиска, что особенно хорошо заметно на дисках, заполненных более чем наполовину.

За картами следует массив inod'ов, смещение которого содержится в поле cg_iusedoff (адрес первой группы inode продублирован в суперблоке). По сути, в UFS структура inode ничем не отличается от ext2fs, только расположение полей другое. К тому же имеется только один блок косвенной адресации вместо трех, но это уже детали, в которые не будет углубляться (иначе или зависнем или завязнем), а лучше рассмотрим назначение фундаментальных полей, к числу которых принадлежат:

q       di_nlink — кол-во ссылок на файл (0 означает "удален");

q       di_size — размер файла в байтах;

q       di_atime/di_atimensec — время последнего доступа к файлу;

q       di_mtime/di_mtimensec — время последней модификации;

q       di_ctime/di_ctimensec – время последнего изменения inode;

q       di_db – адреса первых 12-блоков данных файла, отсчитываемые в фрагментах от начала группы цилиндров;

q       di_ib — адрес блоков косвенной адресации (фрагменты от начала группы);

Сама структура inode определена в файле /src/ufs/ufs/dinode.h и для UFS1 выглядит так:

struct dinode {

/* 0x00 */    u_int16_t     di_mode;      /*   0: IFMT, permissions; see below. */

/* 0x02 */    int16_t              di_nlink;     /*   2: File link count. */

/* 0x04 */    union {

              u_int16_t oldids[2];       /*   4: Ffs: old user and group ids. */


ааааааааааааа int32_tаааааа а inumber;аааааааааа /*аа 4: Lfs: inode number. */

аааааа } di_u;

/* 0x08 */ааа u_int64_tаааа di_size;ааааа /*аа 8: File byte count. */

/* 0x10 */ааа int32_tааааааааааааа di_atime;аааа /*а 16: Last access time. */

/* 0x14 */ааа int32_tааааааааааааа di_atimensec; /*а 20: Last access time. */

/* 0x18 */ааа int32_tааааааааааааа di_mtime;аааа /*а 24: Last modified time. */

/* 0x1C */ааа int32_tааааааааааааа di_mtimensec; /*а 28: Last modified time. */

/* 0x20 */ааа int32_tааааааааааааа di_ctime;аааа /*а 32: Last inode change time. */

/* 0x24 */ааа int32_tааааааааааааа di_ctimensec; /*а 36: Last inode change time. */

/* 0x28 */ааа ufs_daddr_tаа di_db[NDADDR];аааааа /*а 40: Direct disk blocks. */

/* 0x58 */ааа ufs_daddr_tаа di_ib[NIADDR];аааааа /*а 88: Indirect disk blocks. */

/* 0x64 */ааа u_int32_tаааа di_flags;аааа /* 100: Status flags (chflags). */

/* 0x68 */ааа int32_tааааааааааааа di_blocks;ааа /* 104: Blocks actually held. */

/* 0x6C */ааа int32_tааааааааааааа di_gen;ааааааааааааа /* 108: Generation number. */

/* 0x70 */ааа u_int32_tаааа di_uid;ааааааааааааа /* 112: File owner. */

/* 0x74 */ааа u_int32_tаааа di_gid;ааааааааааааа /* 116: File group. */

/* 0x78 */ааа int32_tааааааааааааа di_spare[2];а /* 120: Reserved; currently unused */

};


интерфейс с IDE-диском в режиме LBA


Как можно видеть, 28 битная адресация обеспечивает поддержку дисков с объемом вплоть до 128 Гбайт, однако, включение в BIOS поддержки LBA еще не отменяет 8 Гбайтного ограничения и номер последнего адресуемого цилиндра по прежнему остается равным 1024 со всеми вытекающими отсюда последствиями. SCSI-дискам с их подлинно 32 битной адресацией несколько проще и они поддерживают законные 2 Тбайта, а все потому что управляются своим собственным BIOS'ом, на которых не наложено никаких дурацких пережитков старины.

Утвержденная ATA-6 48 битная адресация расширяет предельно допустимые размеры IDE-дисков до астрономических величин (конкретно – до 131.072 Тбайт), по крайней мере в теории. На практике же, в Windows 2000 с пакетом обновления SP2 или более ранним, отсутствует поддержка 48 разрядной LBA и для работы с большими дисками необходимо обновить драйвер Atapi.sys и добавить к следующему ключу реестра HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\atapi\Parameters параметр EnableBigLba типа DWORD со значением 1. (за подробностями обращайтесь к Microsoft Knowledge Base: 260910).

Один физический диск может быть разбит на несколько логических, каждый из которых последовательно нумеруется от первого до последнего сектора либо "сквозной" адресацией, либо по CHS-схеме. В одних случаях Windows требует задания абсолютного номера сектора (который на самом деле никакой не абсолютный, а относительный, отсчитывающийся от стартового сектора раздела), в других – ожидает увидеть "святую троицу" (цилиндр, головку, сектор), опять-таки, отсчитывающихся от стартового сектора. Так, если раздел начинается с адреса 123/15/62, то первой его головкой все равно будет головка 0!

На уровне файловой системы операционная система адресует диск кластерами (cluster). Каждый кластер образован непрерывной последовательностью секторов, количество которых равно степени двойки (1, 2, 4, 8…). Размер кластера задается на этапе форматирования диска и в дальнейшем уже не меняется. Основное назначение кластеров – уменьшение фрагментации файлов и уменьшение разрядности служебных файловых структур. В частности, FAT16 нумеруют кластеры двойными словами и потому может адресовать не более 10000h*sizeof(cluster) дискового пространства. Легко видеть, что уже на 80 Гбайтовом диске размер кластера составляет 1 Мбайт и десяток файлов по одному байту каждый сожрут 10 Мбайт! Впечатляет, не правда ли? NTFS, оперирующая 64 битными величинами, не страдает подобными ограничениями и типичная величина кластера, выбираемая по умолчанию, составляет всего 4 сектора. В отличии от секторов, кластеры нумеруются начиная с нуля.



ручное декодирование файловой записи (разные атрибуты выделены разным цветом)


Первым делом необходимо восстановить оригинальное содержимое последовательности обновления. По смещению 04h от начала сектора лежит 16-разрядный указатель на нее, равный в данном случае 2Ah (значит, это NTFS 3.0 или младше). А что у нас лежит по смещению 2Ah? Ага, пара байт 03 00. Это – номер последовательности обновления. Сверяем его с содержимым двух последних байт этого и следующего секторов (смещения 1FEh и 3FEh соответственно). Они равны! Значит, данная файловая запись цела (по крайней мере внешне) и можно переходить к операции спасения. По смещению 2Ch расположен массив, содержащий оригинальные значения последовательности обновления. Количество элементов в нем равно содержимому 16-разрядного поля, расположенному по смещению 06h от начала сектора и уменьшенного на единицу (т. е. в данном случае 03h – 01h == 02h). Извлекаем два слова начиная со смещения 2Ch (в данном случае они равны 00 00 и 00 00) и записываем их в конец первого и последнего секторов.

Теперь нам необходимо выяснить – используется ли данная файловая запись или ассоциированный с ней файл/каталог был удален. 16-разрядное поле, расположенное по смещению 16h, содержит значение 01h. Следовательно, перед нами файл, а не каталог и этот файл еще не удален. Но является ли данная файловая запись базовой для данного файла или мы имеет дело с ее продолжением? 64-разрядное поле, расположенное по смещению 20h, равно нулю, следовательно, данная файловая запись – базовая.

ОК, переходим к исследованию атрибутов. 16-разрядное поле, находящееся по смещению 14h равно 30h, следовательно, заголовок первого атрибута начинается со смещения 30h от начала сектора.

Первое двойное слово атрибута равно 10h, значит, перед нами атрибут типа $STANDARD_INFORMATION. 32-разрядное поле длины атрибута, находящееся по смещению 04h и равное в данном случае 60h байт, позволяет нам вычислить смещение следующего атрибута в списке – 30h (смещение нашего атрибута) + 60h (его длина) == 90h (смещение следующего атрибута). Первое двойное слово следующего атрибута равно 30h, значит, это атрибут типа $NAME и следующее 32-разрядное поле хранит его длину, равную в данном случае 70h. Сложив длину атрибута с его смещением, мы получим смещение следующего атрибута – 90h + 70h == 100h. Первые двойное слово третьего атрибута равно 80h, следовательно это атрибут типа $DATA, хранящий основные данные файла. Складываем его смешение с длиной – 100h + 32h == 132h. Опс! Мы наткнулись на частокол FFFFFFh, сигнализирующий о том, что атрибут $DATA последний в списке.


Теперь, разбив файловую запись на атрибуты, как мясник рассекает телячью тушу (try the veil, как говаривал старина Шрек), не грех будет приступить к исследованию каждого из атрибутов в отдельности. Начнем с имени. 8-разрядное поле, находящееся по смещению 08h от начала атрибутного заголовка (и по смещению 98h от начала сектора), содержит флаг неризидентности, который в данном случае равен нулю (т. е. атрибут резидентный и его тело хранится непосредственно в самой файловой записи, что есть гуд). 16-разрядное поле, расположенное по смещению 0Ch от начала атрибутного заголовка (и по смещению 9Ch от начала сектора) равно нулю, следовательно тело атрибута не сжато и не зашифровано. Хорошо! Тогда подать это тело на стол! 32-разрядное поле, расположенное по смещению 10h от начала атрибутного заголовка и по смещению A0h от начала сектора, содержит длину атрибутного тела, равную в данном случае 54h байт, а 16-разрядное поле, расположенное по смещению 14h от начала атрибутного заголовка и по смещению A4h от начла сектора хранит смещение атрибутного тела, равное в данном случае 18h, следовательно, тело атрибута $NAME располагается по смещению A8h от начала сектора.

Формат атрибута типа $NAME описан в таблице XX. Первые восемь байт содержат ссылку на материнский каталога данного файла, равную в данном случае 11ADBh:01 (индекс – 11ADBh, номер последовательности – 01h). Следующие 32-байта содержат штаммы времени создания, изменения и времени последнего доступа к файлу. По смещению 28h от начала тела атрибута и D0h от начала сектора лежит 64-разрядное поле выделенного размера, а за ним – 64-разрядное поле реального размера. Оба равны нулю, что означает, что за размером файла следует обращаться к атрибутам типа $DATA.

Длина имени файла содержится в 8-разрядном поле, находящемся по смещению 40h байт от начала тела атрибута и по смещению E8h от начала сектора. В данном случае оно равно 09h. Само же имя начинается со смещения 42h от начала тела атрибута и со смещения EAh от начала сектора. И здесь находится Ilfak.dbx.



Переходим к атрибуту основных данных файла, пропустив атрибут стандартной информации, который не содержит решительного ничего интересного. 8-разрядный флаг неризидентности, расположенный по смещению 08h от начала атрибутного заголовка и по смещению 108h от начала сектора, равен 01h, следовательно атрибут неризидентный. 16-разрядный флаг, расположенный по смещению 0Ch от начала атрибутного заголовка и по смещению 10Сh от начала сектора, равен нулю, значит, атрибут не сжат и не зашифрован. 8-разрядное поле, расположенное по смещению 09h от начала атрибутного заголовка и по смещению 109h от начала сектора, равно нулю – атрибут безымянный. Реальная длина тела атрибута (в байтах) содержится в 64-разрядном поле, расположенном по смещению 30h от начала атрибутного заголовка и по смещению 130h от начла сектора. В данном случае она равна 4ED1F0h (5.165.552). Два 64-разрядных поля, расположенных по смещениям 10h/110h и 18h/118h байт от начала атрибутного заголовка/сектора соответственно, содержат начальный и конечный номер виртуального кластера неризидентного тела. В данном случае они равны: 0000h/4EDh.

Остается лишь декодировать список отрезков, адрес которого хранится в 16-разрядном поле, находящемся по смещению 20h от начала атрибутного заголовка и 120h от начала сектора. В данном случае оно равно 40h, что соответствует смещению от начала сектора в 140h. Сам же список отрезков выгляди так: 32 EE 04 D9 91 00 00. Ага! Два байта занимает поле длины (равное в данном случае 04EEh кластерам) и три – поле начального кластера (0091h). Завершающий ноль на конце говорит о том, что этот отрезок последний в списке отрезком.

Подытожим полученную информацию. Файл называется Ilfak.dbx, он начинается с кластера 0091h и продолжается вплоть до кластера 57Fh, при реальной длине файла в 5.165.552 байт. За сим все! Теперь уже ничего не стоит скопировать файл на резервный носитель (например, ZIP или стример).


С точки зрения UFS, директории


Имена файлов хранятся в директориях. В inod'ах их нет. С точки зрения UFS, директории являются обыкновенными файлами (ну, может, не совсем обыкновенными) и могут хранится в любом месте, принадлежащем группе цилиндров. Файловая система UFS поддерживает несколько типов хеширования директорий, однако на структуре хранения имен это никак не отражается. Имена хранятся в блоках, называемых DIRBLKSIZ в структурах типа direct, выровненных по 4'х байтовой границе.




структура direct, отвечающая за хранение имен файлов и директорий


На этом описание файловой системы UFS можно считать законченным. Для ручного восстановления данных приведенной информации вполне достаточно.



LiveCD


За последние несколько лет появилось множество дистрибьютивов Linux'а, загружающихся прямо с CD-ROM и не требующих установки на винчестер. Очень удобная штука для восстановления данных. Однако, далеко не все дистрибьютвы для этого пригодны (в частности, недавно анонсированный в "Системном Администраторе" SuSE не подходит точно), поэтому кратный обзор не помешает.

KNOPPIX 3.7 — самый лучший дистрибутив из всех. Основан на GNU/Debian. Занимает всего один диск, но содержит практически все: от дисковых утилит и компиляторов до офисных пакетов и мультимедийных приложений. Очень шустро работает, требует от 128 Мбайт оперативной памяти (если меньше — будет свопиться на диск). В 2004 году издательство O'Reilly выпустило шикарную книгу "KNOPPIX Hacks", содержащую главы, посвященные технике восстановления данных. Саму книжку можно найти в Осле, а KNOPPIX заказать в Интернет-магазине www.linuxcenter.ru.

Ferenzy 0.3 – дистрибьютив, основанный на Free BSD с небольшим количеством дисковых утилит, ориентированных на ext2fs, в то время как основной файловой системой самой BSD является USF/FFS и ext2fs она поддерживает постольку-поскольку. Тем не менее для восстановительных работ данный диск вполне пригоден, и всем поклонникам BSD его можно смело рекомендовать.

SuSE 9.2 – отвратительный дистрибутив. Занимает два диска (один с KDE, другой с GNOME). Требует не менее 256 Мбайт оперативной памяти (правда, KDE версия запускается и при 220 Мбайтах). Очень медленно работает и не содержит ничего кроме щепотки офисных программ.



Master boot record – базовые концепции


Первые жесткие диски были небольшого (даже по тем временам) размера и форматировались практически так же, как и дискеты, однако их объемы стремительно росли и MS-DOS уже не могла их целиком адресовать. Для преодоления этого ограничения был введен механизм разделов (partitions), разбивающий один физический диск на несколько логических, каждый из который имел свою собственную файловую систему и форматировался независимо от других. За счет чего это достигается?

В первом секторе физического диска (цилиндр0/головка 0/сектор 1) хранится специальная структура данных – maser boot record (главная загрузочная запись) или сокращенно MBR. Она состоит из двух основных частей – первичного загрузчика (master boot code) и таблицы разделов (partition table), описывающей схему разбиения и геометрию каждого из логических дисков. В конце сектора по смещению 1FE находится сигнатура 55h AAh, по которой BIOS определяет признак "загрузочности" сектора. Даже если вы не хотите дробить свой винчестер на части и форматируете его как один диск, присутствие maser boot record обязательно.



Master boot record – техника восстановления


Существуют множество утилит для автоматического восстановления master boot code и partition table (GetData Back, Easy Recovery, Active Data Recovery Software и т. д.). До некоторого времени они вполне успешно справлялись со своей задачей, восстанавливая даже полностью уничтоженные таблицы разделов, однако с появлением емких винтов, преодолевших барьер в 2 Гбайт с помощью всевозможных расширений, они стали часто путаться и потому доверять им нельзя. Если не хотите потерять свои данные – восстанавливайте MBR самостоятельно (тем более, что это достаточно простая операция, не требующая особой квалификации). Восстановление значительно упрощается, если в вашем распоряжении имеется копия таблицы разделов, снятая Sector Inspector'ом или подобными ей утилитами. Однако, чаще всего ее все-таки нет…

Если операционная система отказывается загружаться, а на экране появляется ругательство от BIOS типа "Disk Boot failure, Non-System disk or disk error... Press Enter to restart", это указывает на разрушение сигнатуры 55h AAh, обычно сопровождаемое смертью первичного загрузчика. Внимание! Очень важно отличать сообщение BIOS от сообщений первичного загрузчика и boot-сектора. Зайдите в BIOS Setup и отключите все загрузочные устройства, оставив активным только диск A: с вытащенной дискетой. А теперь перезагрузитесь и запомните какое сообщение появится на экране. Это и будет ругательством BIOS'а.

Восстановить сигнатуру 55h AAh можно в любом дисковом редакторе. Когда будете это делать, убедитесь, что в начале диска присутствуют осмысленный master boot code (если вы испытываете затруднение с дизассемблированием в уме, воспользуйтесь IDA PRO или HIEW'ом). Вы не умеете дизассемблировать? Тогда попробуйте оценить степень "нормальности" первичного загрузчика визуально (однако, для этого опять-таки требуется опыт работы с кодом). В начале более или менее стандартного загрузчика расположено приблизительно 100h байт машинного кода, в котором обнаруживаются последовательности: 00 7С, 1B 7C, BE 07, CD 13, CD 18, CD 10, 55 AA, а затем идут характерные текстовые сообщения: Invalid partition table, Error loading operating system, Missing operating system..... ну или подобные им. Если загрузчик поврежден, но сигнатура 55 AA цела, то попытка загрузки с такого диска оберется неизменным зависанием.


Восстановить "слетевший" или искореженный первичный загрузчик можно с помощью утилиты FDISK.EXE, запущенной с ключом /MBR, записывающий в главную загрузочную запись первого диска стандартный master boor code, или командой FIXMBR консоли аварийного восстановления в Windows 2000 (недокументированный ключ /CMBR, появившийся в MS-DOS 7.0, позволяет выбирать любой из подключенный дисков). Внимание! Если вы использовали нестандартный загрузчик (такой, например, как LILO), то после перезаписи MBR сможете загружаться только с основного раздела, а для запуска операционных систем из других разделов, вам придется переустановить свой мультизагрузочный менеджер (вообще-то, такой менеджер можно написать и самостоятельно, при наличии HIEW'а, а если лучше транслятора ассемблера – работа не займет и получаса).

Как уже говорилось, некоторые загрузчики изменяют схему трансляции адресов жесткого диска и со штатным загрузчиком такой диск будет полностью не работоспособен. Попробуйте переустановить загрузчик с дистрибьютивных дисков – быть может, это поможет. В противном случае, ничего не остается как писать свой собственный загрузчик, определять текущую геометрию диска и соответствующим образом транслировать секторные адреса. Это довольно сложная задача, требующая серьезной подготовки и здесь ее лучше не обсуждать.

Если загрузчик говорит "Invalid partition table", это еще не обозначает, что таблица разделов повреждена, просто ни один из основных разделов не назначен активным. Такое случается при использовании нестандартных загрузчиков, загружающих операционную систему из расширенного раздела. После выполнения команды FDISK /MBR или установке операционной системы, автоматически заменяющий первичный загрузчик своим собственным, он, не обнаружит в пределах досягаемости ни одного активного раздела, и естественно разразится многоэтажным ругательством. Такое поведение в частности характерно для Windows 98. Для решения проблемы либо восстановите прежний загрузчик, либо установите операционную систему на первичный раздел и, запустив FDISK, сделайте его активным.



Загрузитесь с системной дискеты (другого винчестера, CD-диска) и посмотрите – видны ли ваши логические диски или нет. Если да, то смело переходите к следующему пункту, в противном случае соберитесь с духом и приготовьтесь немного поработать руками и головой.

Восстановление основного раздела, созданного FDISK'ом или Disk Manager'ом в большинстве случаев осуществляется элементарно, а остальные, как правило, восстанавливать и не требуется, поскольку именно MBR гибнет чаще всего, а расширенные патриции, рассредоточенные по диску дохнут разве что при явном удалении разделов средствами FDISK/Disk Manager.

Адрес стартового сектора первого логического диска всегда равен 0/1/1 (Cylinder/Head/Sector), относительный (Relative) сектор – количеству головок жестка диска уменьшенных на единицу (сведения о геометрии диска можно почерпнуть из любого дискового редактора, в том числе и Sector Inspector'a). Конечный сектор определить несколько сложнее. Если загрузочный сектор цел (см. "загрузочный сектор – техника восстановления"), то узнать количество секторов в разделе патриции (total sectors) можно и из поля BootRecord.NumberSectors, увеличив его значение на единицу. Тогда конечный цилиндр будет равен LastCyl := TotalSectors/(Heads*SecPerTrack), где Heads – кол-во головок на физическом диске, а SecPerTrack – кол-во секторов на трек. Конечная головка равна LastHead := (Total Sector – (LastCyl*Heads SecPerTrack))/SecPerTrack, а конечный сектор равен LastSec :== (Total Sector – (LastCyl*Heads SecPerTrack)) % SecPerTrack. Пропишите полученные значения в MBR и посмотрите – не находится ли за вычисленным концом раздела следующий раздел? Это должна быть либо расширенная таблица разделов, либо boot-сектор. Если это так, создайте еще одну запись в partition table, заполнив его соответствующим образом.

А если boot-сектор отсутствует и не может быть восстановлен – реально ли восстановить таблицу разделов или нет? Да, можно. Необходимо лишь найти boot или partition следующих разделов, в чем вам поможет контекстный поиск. Ищите сектора, содержащие сигнатуру 55h AAh в конце. Отличить boot от partition очень просто (в boot секторе по смещению два байта от его начала расположен идентификатор производителя (NTFS, MSWIN4.1 и т.д.). Логично, что размер текущего раздела на один сектор меньше, а зная размер и геометрию диска можно рассчитать и конечный цилиндр/головку/сектор.



Только учтите, что Windows хранит копию boot сектора, которая в зависимости от версии может быть расположена либо в середине раздела, либо в его конце. Другие копии могут находится в архивных файлах и файле подкачке. Кстати говоря, посмотрите – не содержится ли среди них ничего удобоваримого –  Как отличить копию сектора от оригинала? Элементарно, Ватсон! Если это подлинник вслед за ним пойдут служебные структуры файловой системы (в частности, для NTFS таблица MFT, каждая запись которой начинается с легко узнаваемой строки FILE*). Собственно говоря, поскольку служебные структуры файловой системы обычно располагаются на более или менее предсказуемом смещении относительно начала раздела, то отталкиваясь от их "географического" расположения, мы может установить размеры каждого из логических дисков, даже если все-все-все boot/partition уничтожены.

Что произойдет, если границы разделов окажутся определенными неверно? Если мы переборщим, увеличив размер раздела сверх необходимо, все будет нормально работать, поскольку карта свободного пространства хранится в специальной структуре (у NTFS это файл $bitmap, а у FAT13/32 – непосредственно сама FAT-таблица) и "запредельные" сектора будут добавлены только после переформатирования раздела. Если все что нам нужно – это скопировать данные с восстанавливаемого диска на другой носитель, но возиться с подгонкой параметров partition table не нужно! Распахните ее на весь физический диск и дело с концом!

Естественно, такой способ восстановления подходит только для первого раздела диска, а для всех последующих нам потребуется определить стартовый сектор. Это определение должно быть очень точным, поскольку все структуры файловой системы адресуются от начала логического диска и ошибка в один-единственный сектор сделает весь этот тонкий механизм полностью неработоспособным. К счастью, некоторые из структур ссылаются сами на себя, давая нам ключ к разгадке. В частности, файлы $mft/$mftmiff содержат номер своего первого кластера. Стоит нам найти первую запись FILE*, как мы узнаем на каком именно секторе мы сейчас находится (конечно, при условии, что сумеем определить количество секторов на кластер, но это уже другая тема – см. загрузочный сектор – базовые концепции).


MS-DOS


Доступны все символы пространства имен win32 (без учета регистра), за исключением: '+' (знак плюс), ',' (знак запятая), '.' (знак точка), ';' (точна с запятой), '=' (знак равно). Имя файла не должно превышать восьми символов за которыми следует необязательное расширение с длиной от одного до трех символов.



На обломках империи


При удалении файла на UFS-разделе происходит следующее (события перечислены в порядке расположения соответствующих структур в разделе и могут не совпадать с порядком их возникновения):

q       в суперблоке обновляется поле fs_time (время последнего доступа к разделу);

q       в суперблоке обновляется структура fs_cstotal (кол-во свободных inod'ов и блоков данных в разделе);

q       в группе цилиндров обновляются карты занятых inod'ов и блоков данных — inod'е и все блоки данных удаляемого файла помечаются как освобожденные;

q       в indoe материнского каталога обновляются поля времени последнего доступа и модификации;

q       в indoe материнского каталога обновляется поле времени последнего изменения inode;

q       в inode удаляемого файла поля di_mode (IFMT, permissions), di_nlink (кол-во ссылок на файл) и di_size (размер файла) варварски обнуляются;

q       в inode удаляемого файла поля di_db (массив указателей на 12 первых блоков файла) и di_ib (указатель на блок косвенной адресации) безжалостно затираются нулями;

q       в inode удаляемого файла обновляются поля времени последней модификации и изменения inod'е, время последнего доступа при этом остается неизменным;

q       в inode удаляемого файла обновляется поле di_spare. В исходных текстах оно помечено как "Reserved; currently unused", но просмотр дампа показывает, что это не так. Судя по всему здесь хранится нечто вроде последовательности обновления (update sequence), используемой для контроля целостности indoe, однако, это только предположение;

q       в директории удаленного файла, размер предшествующей структуры direct увеличивается на d_reclen, в результате чего она как бы "поглощает" имя удаляемого файла, однако, его затирания не происходит, во всяком случае оно затирается не сразу, а только тогда, когда в этом возникнет реальная необходимость;



Назначение некоторых служебных файлов


NTFS содержит большое количество служебных файлов (метафайлов) строго определенного формата, важнейший из которых– $MFT – мы только что рассмотрели. Остальные метафайлы играют вспомогательную роль и для восстановления данных знать их структуру в общем-то и необязательно. Тем не менее если они окажутся искажены, штатный драйвер файловой системы не сможет работать с таким томом, поэтому иметь некоторые представления о назначении каждого из них все же необходимо.

У нас нет возможности рассказать о структуре всех метафайлов (да и незачем дублировать Linux-NTFS Project), поэтому эта информация здесь не приводится.

inode

имя файла

ОС

описание

0

$MFT

любая

главная файловая таблица (Master File Table, MFT)

1

$MFTMirr

любая

резервная копия первых четырех элементов 4 MFT

2

$LogFile

любая

журнал транзакций (transactional logging file)

3

$Volume

любая

серийный номер, время создания, dirty flag (флаг не сброшенного кэша) тома

4

$AttrDef

любая

определение атрибутов

5

. (точка)

любая

корневой каталог (root directory) тома

6

$Bitmap

любая

карта свободного/занятого пространства

7

$Boot

любая

загрузочная записи (boot record) тома

8

$BadClus

любая

список плохих кластеров (bad clusters) тома

9

$Quota

NT

информация о квотах (quota information)

9

$Secure

2K

использованные дескрипторы безопасности (security descriptors)

10

$UpCase

любая

таблица заглавных символов (uppercase characters ) для трансляции имен

11

$Extend

2K

каталоги: $ObjId, $Quota, $Reparse, $UsnJrnl

12-15

не используется

любая

помечены как использованные, но в действительности пустые

16-23

не используется

любая

помечены как неиспользуемые

любой

ObjId

2K

уникальные идентификаторы каждого файла

любой

$Quota

2K

информация о квотах (quota information)

любой

$Reparse

2K

информация типа reparse point

любой

$UsnJrnl

2K

журнал шифрованной файловой системы (journaling of encryption)

> 24

польз. файл

любая

обычные файлы

> 24

польз. каталог

любая

обычные каталоги

Таблица 11 назначение основных стандартных файлов



Немного истории


UFS ведет свою историю от S5 FS — самой первой файловой системы, написанной для UNIX в далеком 1974 году. S5 FS была крайне простой и неповоротливой (по некоторым данным 2%-5% от "сырой" производительности голого диска), но понятия суперблока (super-block), файловых записей (inodes) и блоков данных (blocks) в ней уже существовали.

В процессе работы над дистрибутивом 4.2 BSD, вышедшим в 1983 году, ординальная файловая система претерпела некоторые улучшения. Были добавлены длинные имена, символические ссылки и т. д. Так родилась UFS.

В 4.3 BSD, увидевшей свет уже в следующем году, улучшения носили намного более радикальный, если не сказать революционный, характер. Появились концепции фрагментов (fragments) и групп цилиндров (cylinder groups). Быстродействие файловой системны существенно возросло, что и определило ее название FFS – Fast File System (быстрая файловая система).

Все последующие версии линейки 4.x BSD прошли под знаменем FFS, но в 5.x BSD файловая система вновь изменилась. Для поддержки дисков большого объема ширину всех адресных полей пришлось удвоить: 32-битная нумерация фрагментов уступила место 64-битной. Были внесены и другие менее существенные усовершенствования.

Фактически мы имеем дело с тремя различными файловыми системами, не совместимыми друг с другом на уровне базовых структур данных, однако, некоторые источники склонны рассматривать FFS как надстройку над UFS. "UFS (and UFS2) define on-disk data layout. FFS sits on top of UFS (1 or 2) and provides directory structure information, and a variety of disk access optimizations" говорит "Little UFS2 FAQ" (UFS/UFS2 определяет раскладку данных на диске. FFS реализована поверх UFS 1 или 2 и отвечает за структуру директорий и некоторых дисковых оптимизаций). Действительно, если заглянуть в исходные тексты файловой системы, можно обнаружить два подкаталога — /ufs и /ffs. В /ffs находится определение суперблока (базовой структуры, отвечающей за раскладку данных), а в /ufs – определение inode и структуры директорий, что опровергает данный тезис, с точки зрения которого все должно быть с точностью до наоборот.

Чтобы не увязнуть в болоте терминологический тонкостей, под UFS мы будем понимать основную файловую систему 4.5 BSD, а под UFS2 – основную файловую систему 5.х BSD.



Обзор NTFS с высоты птичьего полета


Основным структурным элементом всякой файловой системы является том (volume), в случае с FAT совпадающий с разделом (partition), о котором мы говорили в прошлой статье (см. "Системный администратор" N10 за 2004 год) ### главе. NTFS поддерживает тома, состоящие из нескольких разделов. Подробнее схему отображения томов на разделы мы обсудим в следующей главе ### статье этого цикла, а пока же будем для простоты считать, что том представляет собой отформатированный раздел (т. е. раздел, содержащий служебные структуры файловой системы).



Отладчики файловой системы


Отладчиками файловой системы называют утилиты, дающие доступ к "святая святых" файловой системе и позволяющие манипулировать ключевыми структурами данных по своему усмотрению. Чем они отличаются от простых редакторов? Редактор работает на более низком уровне— уровне блоков или секторов. Он, в принципе, может представлять некоторые структуры в наглядном виде, однако, в их "физический" смысл никак не вникает.

Отладчик файловой системы работает через драйвер и потому испортить раздел с его помощью намного сложнее. Он реализует довольно высокоуровневые операции, такие как установка/снятие флага занятости блока, создание новой символьной ссылки и т. д. А вот "посекторного" hex-редактора отладчики файловой системы обычно не содержат, поэтому обе категории программ взаимно дополняют друг друга.

Большинство (если не все) дистрибутивов Linux'а включают в себя отладчик debugfs, поддерживающий ext2fs и отчасти ext3fs.



<<<<


По умолчанию Windows создает базовые диски (см. расшифровку терминов в таблице 5), но всякий базовый диск в любой момент времени может быть обновлен до динамического (это даже не потребует перезагрузки). Динамические диски не пользуются таблицей разделов, а потому и не имеют проблем, связанных с ограничением CHS-разрядности и позволяют создавать тома практически неограниченного размера. Однако, динамические диски, созданные путем обновления основных разделов, все-таки остаются в partition table, при этом их Boot ID меняется на 42h. Если эта информация окажется удалена, система откажется подключать такой динамический диск. Кстати говоря, Windows может быть установлена только на обновленный динамический диск, поскольку BIOS может загружать систему лишь с тех разделов которые перечислены в partition table, а динамические диски, созданные "на лету" в нее как раз и не попадают.

Схема разбиения динамических дисков содержаться в Базе Менеджера Логических Дисков – Logical Disk Manager Database или сокращенно LDM. Это протоколируемая (journalled) база данных, поддерживающая транзакции и устойчивая к сбоям. Если в процессе манипуляции с томами неожиданно исчезнет питание, при последующем включении компьютера будет выполнен откат в предыдущее состояние. При переносе винчестера, содержащего один или несколько динамических дисков, на другую систему, они автоматически распознаются и монтируются, как обыкновенные диски.

LMD-база хранится в последнем мегабайте жесткого диска, а для дисков, полученных путем обновления базового раздела до динамического – в последнем мегабайте этого самого раздела, а идентификатор системы Boot ID соответствующей записи в Partition Table принимает значение 42h. Так происходит потому, что при стандартном разбиении винчестера в его конце просто не остается свободно места и операционной системе приходится сохранять эту информацию непосредственно в самом обновляемом диске (естественно, для этого в нем должен быть свободен по меньшей мере 1 Мбайт).



Подготовка к восстановлению


Прежде, чем приступать к восстановлению, обязательно размонтируете дисковый раздел или на худой конец перемонтируете его в режим "только на чтение". Лечение активных разделов зачастую только увеличивает масштабы разрушения. Если восстанавливаемые файлы находятся на основном системном разделе у нас два пути– загрузится с LiveCD или подключить восстанавливаемый жесткий диск на Linux-машину вторым.

Чтобы чего-нибудь не испортить, никогда не редактируйте диск напрямую. Работайте с его копией! Копию можно создать командой cp /dev/sdb1 my_dump, где sdb1 – имя устройства, а my_dump — имя файла-дампа. Файл-дамп можно разместить на любом свободном разделе или перегнать на другую машину по сети. Все дисковые утилиты (lde, debugsf, fschk) не заметят подвоха и будут работать с ним как с "настоящим" разделом. При необходимости его даже можно смонтировать на файловую систему: mount my_dump mount_point –o loop, чтобы убедиться, что восстановление прошло успешно. Команда cp my_dump /dev/sdb1

копирует восстановленный файл-дамп обратно в раздел, хотя делать это совсем необязательно. Проще (и безопаснее) копировать только восстанавливаемые файлы.



POSIX


Допустимы все UNICODE-символы (с учетом регистра), за исключением символа нуля (NULL), обратного слеша ('/') и знака двоеточия (':'). Последнее, кстати говоря, не ограничение POSIX, а ограничение NTFS, использующей этот символ для доступа к именованным атрибутам. Максимально допустимая длина имени составляет 255 символов.



Последовательности обновления (update sequence)


Будучи очень важными компонентами файловой системы, $MFT, INDEX и $LogFile нуждаются в механизме контроля целостности своего содержимого. Традиционно для этого используется ECC/EDC-коды, однако, во времена проектирования NTFS процессоры были не настолько быстрыми как теперь и расчет корректирующих кодов занимал значительное время, существенно снижающее производительность файловой системы. Поэтому, от них пришлось отказаться. Вместо этого, разработчики NTFS применили так называемые последовательности обновления (update sequence), так же называемые fix-up'ами.

В конец каждого из секторов, слагающих файловую запись (INDEX Record, RCRD Record или RSTR Record) записывается специальный 16-байтовый номер последовательности обновления (update sequence number), дублируемый в заголовке файловой записи. При каждой операции чтения два последних байта сектора сверяется с соответствующим полем заголовка и, если NTFS-драйвер обнаруживает расхождение, данная файловая запись считается недействительной.

Основное назначение последовательностей обновления– защита от "обрыва записи". Если в процессе записи сектора на диск, исчезнет питающее напряжение, может случиться так, что половина файловой записи будет успешно записана, а половина – сохранит прежнее содержимое (файловая запись, как мы помним, обычно состоит из двух секторов). После восстановления питания, драйвер файловой системы не может уверенно сказать – была ли файловая запись записана целиком или нет. Вот тут-то последовательности обновления и выручают! При каждой перезаписи сектора update sequence увеличивается на единицу и потому если произошел обрыв записи, значение последовательности обновления, находящейся в заголовке файловой записи не будет совпадать с последовательностью обновления, расположенной в конце сектора.

Оригинальное содержимое, расположенное "под" последовательностью обновления, хранится в специальном массиве обновления (update sequence array), расположенном в заголовке файловой записи непосредственно за концом update sequence number. Для восстановления файловой записи в исходный вид, мы должны извлечь из заголовка указатель на update sequence number (он хранится по смещению 04h байт от начала заголовка), и сверить лежащее по этому адресу 16-байтное значение с последним словом каждого из секторов, слагающих файловую запись (INDEX Record, RCRD Record или RSTR Record). Если они не совпадут, значит соответствующая структура данных повреждена и использовать ее следует с очень большой осторожностью (а на первых порах лучше не использовать вообще).


По смещению 006h от начала сектора находится 16-разрядное поле, хранящее совокупный размер номера последовательности обновления вместе с массивном последовательности обновления (sizeof(update sequence number) + sizeif(update sequence array)), выраженный в словах (не в байтах!). Поскольку, размер номера последовательности обновления всегда равен одному слову, то размер массива последовательности обновления, выпаженный в байтах, равен: (update sequence number & update sequence array ? 1)*2. Соответственно, смещение массива оригинального содержимого равно: (offset to update sequence number) + 2. В NT и W2K update sequence number всегда располагается по смещению 2Ah от начала FILE Record Header/INDEX Header, а update sequence array – по смещению 2Сh. В XP же и более старших системах – по смещениям 2Dh и 2Fh соответственно.

Первое слово массива последовательности обновления соответствует последнему слову первого сектора FILE Record/INDEX. Второе – последнему слову второго сектора, и т. д. Для восстановления сектора в исходный вид, мы должны вернуть все элементы массива последовательности обновления на их законные места (естественно, модифицируется не сам сектор, а его копия в памяти).

Продемонстрируем это на следующем примере:

--> начало первого сектора FILE Record

00000000:  46 49 4C 45-2A 00 03 00-7C 77 1A 04-02 00 00 00  FILE* ¦ |w>¦O  

00000010:  01 00 02 00-30 00 01 00-28 02 00 00-00 04 00 00  O O 0 O (O   ¦ 

00000020:  00 00 00 00-00 00 00 00-06 00 06 00-00 00 47 11          ¦ ¦   G<



000001F0:  00 00 00 00-00 00 00 00-00 00 00 00-00 00 06 00                ¦

<-- конец первого сектора FILE Record



000003F0:  07 CC E1 0D-00 09 00 00-FF FF FF FF-82 79 06 00  •Iad 0  yyyyВy¦

<-- конец второго сектора FILE Record


Пространства имен (name spaces)


NTFS изначально проектировалась как системно-независимая файловая система, способная работать со множеством различных подсистем, как-то: win32, MS-DOS, POSIX и т.д. Поскольку, каждая из них налагает свои собственные ограничения на набор символов, допустимых для использования в имени файла, NTFS вынуждена поддерживать несколько независимых пространств имен (name space).



Путешествие по NTFS


Рассказ о NTFS был бы неполным без практической иллюстрации техники разбора файловой записи "руками". До сих пор мы витали в облаках теоретической абстракции. Пора спускаться на грешную землю.

Воспользовавшись любым дисковым редактором, например, Disk Probe, попробуем декодировать одну файловую запись вручную. Найдем сектор, содержащий сигнатуру "FILE" в его начале (не обязательно брать первый встретившийся сектор). Он может выглядеть, например, так:

        :  00 01 02 03 04 05 06 07 ¦ 08 09 0A 0B 0C 0D 0E 0F

00000000:  46 49 4C 45 2A 00 03 00 ¦ 60 79 1A 04 02 00 00 00   FILE* ¦ `y>¦O  

00000010:  01 00 01 00 30 00 01 00 ¦ 50 01 00 00 00 04 00 00   O O 0 O PO   ¦ 

00000020:  00 00 00 00 00 00 00 00 ¦ 04 00 03 00 00 00 00 00           ¦ ¦    

00000030:  10 00 00 00 60 00 00 00 ¦ 00 00 00 00 00 00 00 00   >   `

00000040:  48 00 00 00 18 00 00 00 ¦ B0 D5 C9 2F C6 0B C4 01   H   ^   --г/¦>-O

00000050:  E0 5A B3 7B A9 FA C3 01 ¦ 90 90 F1 2F C6 0B C4 01   рZ¦{й·+OРРё/¦>-O

00000060:  50 7F BC FE C8 0B C4 01 ¦ 20 00 00 00 00 00 00 00   P¦-¦L>-O

00000070:  00 00 00 00 00 00 00 00 ¦ 00 00 00 00 05 01 00 00               ¦O  

00000080:  00 00 00 00 00 00 00 00 ¦ 00 00 00 00 00 00 00 00

00000090:  30 00 00 00 70 00 00 00 ¦ 00 00 00 00 00 00 02 00   0   p         O 

000000A0:  54 00 00 00 18 00 01 00 ¦ DB 1A 01 00 00 00 01 00   T   ^ O ->O   O 

000000B0:  B0 D5 C9 2F C6 0B C4 01 ¦ B0 D5 C9 2F C6 0B C4 01   --г/¦>-O--г/¦>-O

000000C0:  B0 D5 C9 2F C6 0B C4 01 ¦ B0 D5 C9 2F C6 0B C4 01   --г/¦>-O--г/¦>-O

000000D0:  00 00 00 00 00 00 00 00 ¦ 00 00 00 00 00 00 00 00

000000E0:  20 00 00 00 00 00 00 00 ¦ 09 03 49 00 6C 00 66 00           0¦I l f 

000000F0:  61 00 6B 00 2E 00 64 00 ¦ 62 00 78 00 00 00 00 00   a k . d b x

00000100:  80 00 00 00 48 00 00 00 ¦ 01 00 00 00 00 00 03 00   А   H   O     ¦ 

00000110:  00 00 00 00 00 00 00 00 ¦ ED 04 00 00 00 00 00 00           э¦

00000120:  40 00 00 00 00 00 00 00 ¦ 00 E0 4E 00 00 00 00 00   @        рN

00000130:  F0 D1 4E 00 00 00 00 00 ¦ F0 D1 4E 00 00 00 00 00   ЁTN     ЁTN

00000140:  32 EE 04 D9 91 00 00 81 ¦ FF FF FF FF 82 79 47 11   2ю¦-С  Б    ВyG<

000001F0:  00 00 00 00 00 00 00 00 ¦ 00 00 00 00 00 00 03 00                 ¦

        :  00 01 02 03 04 05 06 07 ¦ 08 09 0A 0B 0C 0D 0F 0F



Разгребая кластерные обломки


Рассмотрим более сложный случай восстановления, когда файловая запись уже затерта. Если удаленный файл был резидентным (т.е. хранил свое тело в MFT), то восстанавливать уже нечего. Даже если на его месте создан нерезидентный файл (а файловая запись нерезидентного файла заканчивается там, где начинается резидентное тело), операционная система заботливо заполнит оставшийся "хвост" нулями и для восстановления оригинального содержимого придется прибегнуть к дорогостоящему оборудованию, читающему поверхность "блинов" жесткого диска на физическом уровне.

С нерезидентными файлами, хранящими свое тело вне MFT, ситуация обстоит не так плачевно, хотя проблем тоже хватает. Порядок размещения файла на диске хранится в run-list'e внутри файловой записи в MFT (теперь уже затертой) и потому, возможен лишь контекстный поиск по содержимому. Запускаем диск-редактор, вводим последовательность, заведомо содержащуюся в удаленном файле, но не встречающуюся во всех остальных и нажимаем "search". Для ускорения поиска можно искать только в свободном дисковом пространстве (за это отвечает файл /$BITMAP). Известные мне редакторы пренебрегают этой возможностью (а зря!), однако, утилиту "продвинутого" поиска несложно написать и самостоятельно.

Нефрагментированные файлы восстанавливаются элементарно. Просто выделяем группу секторов и записываем ее на диск (только ни в коем случае не на сам восстанавливаемый том!). Единственная проблема – как определить оригинальную длину? Некоторые типы файлов допускают присутствие "мусора" в своем хвосте (и тогда нам остается следовать правилу "лучше перебрать, чем недобрать"), а некоторые нет! Если конец не удается определить визуально (например, pdf-файлы завершаются сигнатурой "%%EOF"), проанализируйте заголовок файла – среди прочей полезной информации обычно там присутствует и его размер. Тут все зависит от структуры конкретного файла и универсальных рекомендаций дать невозможно.


Если файл фрагментирован – дело намного хуже. Ситуация практически безнадежна, поскольку, чтобы собрать разрозненные цепочки кластеров воедино, необходимо хорошо знать содержимое удаленного файла. В этом смысле, NTFS восстанавливается намного хуже, чем FAT. Последовательность фрагментов файла, хранящаяся в File Allocation Table в виде однонаправленного списка очень живуча. Если список не поврежден, достаточно лишь найти его первый элемент (а сделать это проще простого, поскольку он будет указывать на заголовок файла с вполне предсказуемым содержимым). Даже если список "разрубить" на несколько частей, они продолжать жить собственной жизнью и нам останется лишь подобрать комбинацию как их правильно склеить воедино. Список гибнет лишь при полном затирании FTA'а, что случается прямо скажем не часто. В NTFS же порядок фрагментов файла хранится в крохотных списках отрезков и их гибель – обычное дело, после чего мы остается один на один с миллионом беспорядочно разбросанных кластеров. С текстовыми файлами еще куда бы то ни шло, но что делать, если это электронная таблица, графическое изображение или архив какой? Без знания стратегии выделения дискового пространства здесь никуда. Порядок, в котором драйвер файловой системы находит подходящие свободные фрагменты, не определен и варьируется в зависимости от множества обстоятельств, однако, кое-какие закономерности в нем все же присутствуют.

Анализируя списки отрезков сильно фрагментированных дисков мне удалось установить следующее: сначала заполняются самые большие "дыры", двигаясь от конца MFT-зоны к концу диска. Затем, драйвер файловой системы возвращается назад и начинает заполнять дыры поменьше и так продолжается до тех пор, пока файл не оказывается на диске целиком. Последними заполняются дыры размером в один кластер. Просматривая карту диска, представленную файлом /$BITMAP, мы можем в точности восстановить порядок размещения фрагментов удаленного файла, наскоро собрав их воедино. Во всяком случае, теоретически. Практически же на этом пути нас ждут коварные препятствия. С момента создания восстанавливаемого файла карта свободного дискового пространства могла капитально преобразиться. Всякое удаление файлов высвобождает одну или несколько дыр, хаотично перемешивающихся с дырами восстанавливаемого файла, искажая картину. Как этому противостоять? Сканируем MFT в поисках записей, помеченных как удаленные, но еще не затертых. Декодируем run-list'ы и вычеркиваем соответствующие им фрагменты из списка кандидатов на восстановление. Это существенно сужает круг поиска, хотя количество комбинаций в которые можно собрать фрагментированный файл по-прежнему остается велико. Но это еще что…



Самое "интересное" начинается, когда на диск одновременно записываются несколько файлов (например, скачиваемых ReGet'ом из Интернета) или файл постепенно увеличивает свой размер (набираете дипломную работу в Word'е?), а в это время на диск записываются другие файлы. Когда к существующему файлу дописывается крошечная порция данных, файловая система находит наименьшую дыру, затем следующую наименьшую дыру и т. д., вплоть до тех пор пока маленькие дыры не исчерпаются и тогда наступает черед дыр побольше. Как следствие, файл выходит сильно фрагментированным – это раз. Файл заполняется не от больших дыр к меньшим, а наоборот (т. е. происходит инверсия стратегии размещения) – это два. Маленькие фрагменты одного файла перемешиваются с маленькими фрагментами других файлов – это три.

Хуже всего поддаются восстановлению документы, созданные в MS Office и вот почему: приложение создает большое количество резервных копий редактируемого файла, как в текущем каталоге, так и в каталоге %TEMP%. Вот и разберись какой фрагмент какому файлу принадлежит!

Проще всего восстанавливаться ZIP-архивы. Для этого вам даже не потребуется запускать дисковый редактор. Откройте временный файл на запись, сделайте seek на размер свободного дискового пространства, закройте файл. А теперь обработайте его утилитой pkzipfix.exe (или запустите стандартный pkzip.exe с ключом Fix). В "исправленном" файле волшебным образом появятся все уцелевшие ZIP-архивы! Внутренняя структура ZIP-архива такова, что pkzipfix легко распознает даже переупорядоченные блоки, поэтому высокая степень фрагментации ему не помеха.

Дефрагментация тоже происходит интересно. Стандартное API дефрагментации в силу малопонятных ограничений оперирует не единичными кластерами, а блоками! Минимальный размер блока составляет 16 кластеров, причем начало блока должно быть кратно 16 кластерам в файле! Как следствие – количество мелких дыр после дефрагментации только возрастает, а непрерывных областей свободного пространства практически совсем не остается. Кстати говоря, перемещать внутрь MFT-зоны тоже ничего нельзя. "На томе С: свободно 17%, но только 5% доступно для использования дефргаментатора диска. Для эффективной работы дефрагментатор требует по крайней мере 15% доступного свободного места" – знакомое сообщение, не правда ли? "Недоступное" для дефрагментатора место находится внутри MFT-зоны (как мы помним, при форматировании диска под $MFT-файл резервируется 10% от емкости тома, а затем по мере исчерпания дискового пространства, $MFT-файл усекается наполовину и освободившееся пространство заселяется пользовательскими файлами). Таким образом, для гарантированной работы дефргаментатора ему нужно 10% + 15% == 25% свободного дискового пространства. Не слишком ли высокая плата за дефргаментацию? Если же у вас свободно свыше 25%, настоятельно рекомендуется создать на диске временный файл и выполнить seek, чтобы заполнить все более или менее крупные дыры, не давая их изородовать дефрагментатору (естественно, после дефрагментации этот файл нужно удалить). Кстати говоря, на сжатые файлы ограничение в 16 кластеров не распространяется, поэтому мелкие файлы очень выгодно держать в сжатом состоянии – это существенно уменьшает фрагменацию тома. Почаще дефрагментируйте свой диск! Это не только увеличит быстродействие, но и упростит восстановление удаленных файлов с затертой FILE Record.


Редактор диска


Настоящие профессионалы восстанавливают разрушенные логические структуры непосредственно в дисковом редакторе, не доверяя никаким автоматизированным утилитам (кроме своих собственных), поскольку никогда не известно наперед, какой подлости от них следует ждать. Так будем поступать и мы, делая основной упор именно на ручном восстановлении. Коль скоро дисковый редактор станет нашим главным инструментом, это должен быть хороший и комфортный редактор, в противном случае, восстановление из увлекательной работы превратится в пытку.

Лучшим, и кстати говоря до сих пор никем не превзойденным, дисковым редактором, когда-либо созданным за всю историю существования IBM PC, был и остается знаменитый Norton DiskEditor

от компании Symantec. Удобная навигация по диску, просмотр большинства служебных структур в естественном виде, мощный контекстный поиск до предела упростили процедуру восстановления, взяв всю рутинную работу на себя. Старичок и поныне остается в строю. Естественно, под Windows NT он не запускается, однако, работает под MS-DOS и Windows 9x, наследуя все ограничения, накладываемые BIOS'ом на предельно допустимый объем диска в 8 Гбайт (правда, попытка восстановление диска из многозадачной среды, коей и является Winnows 9x, могут носить диаметрально противоположный характер, впрочем, на NTFS-разделы это условие не распространяется. Windows 9x не поддерживает NTFS и ничего не пишет на ее разделы). К сожалению, DiskEdit ничего не знает об NTFS и потому разбирать все структуры приходится вручную. Но еще пол-беды. DiskEdit'or не умеет работать с UNICODE, а это уже хуже. Поэтому, лучше выбрать другой редактор.


Чаще всего линуксоиды редактируют диски при помощи lde (расшифровывается как Linux Disk Editor). Это, конечно, не Norton Disk Editor, но и не Microsoft Disk Probe. Профессионально-ориентированный инструмент консольного типа с разумным набором функциональных возможностей. Понимает ext2fs, minix, xiafs и отчасти FAT (в перспективе обещана поддержка NTFS, которая на LINUX'e ### линухе никому не нужна, а вот отсутствие в этом списке UFS и FFS очень огорчает).

Поддерживает: отображение/редактирование содержимого в HEX-формате, просмотр супер-блока (super-block), файловых записей (inode) и директорий в удобочитаемом виде; контекстный поиск, поиск файловых записей, ссылающихся на данный блок (полезная штука, только к сожалению, реализованная кое-как и срабатывающая не всегда), режим восстановления с ручным редактированием списка прямых/косвенных блоков, сброс дампа на диск и некоторые другие второстепенные операции.

Может работать как в пакетном, так и в интерактивном режимах. В пакетном режиме все управление осуществляется посредством командной строки, что позволяет полностью или частично автоматизировать некоторые рутинные операции.

Распространяется в исходных текстах (http://lde.sourceforge.net/), денег не требует, работает практически под любой UNIX-совместимой операционной системой (включая Free BSD) и входит во все "правильные" дистрибьютивы (например, в KNOPPIX).



структура файловой системы s5/ext2fs (а) и ufs (b)


Адресация ведется либо по физическим смещениям, измеряемых в байтах и отсчитываемых от начала группы цилиндров (реже — UFS-раздела), либо в номерах фрагментов, отсчитываемых от тех же самых точек. Допустим, размер блока составляет 16 Кбайт, разбитых на 8 фрагментов. Тогда 69'й сектор будет иметь смещение 512 х 69 == 35328 байт или 1024 x (16/8)/512 x 69 = 276 фрагментов.

В начале раздела расположен загрузочный сектор, затем следует суперблок, за которым находится одна или несколько групп цилиндров. Для перестраховки, копия суперблока дублируется в каждой группе. Загрузочный сектор не дублируется, но по соображениям унификации и единообразия, под него просто выделяется место. Таким образом, относительная адресация блоков в каждой группе остается неизменной.



структура дискового тома под NTFS


$MFT-файл представляет собой массив записей типа FILE Record

(или в терминологии UNIX – inodes), каждая из которых описывает соответствующий ей файл или подкаталог (подробнее см. "файловые записи"). В подавляющем большинстве случаев один файл/подкаталог полностью описывается одной-единственной записью FILE Record, хотя теоретически, этих записей может потребоваться и несколько.

Для ссылки на одну файловую запись из другой используется ее порядковый номер (он же индекс) в $MFT файле, отсчитываемый от нуля. Файловая ссылка (file reference) состоит из двух частей (см. таблицу 2) – 48-битного индекса и 16-битного номера последовательности (sequence number).

При удалении файла/каталога соответствующая ему файловая последовательность помечается как неиспользуемая. При создании новых файлов, записи, помеченные как неиспользуемые, могут задействоваться вновь, при этом счетчик номера последовательности, хранящийся внутри файловой записи, увеличивается на единицу. Этот механизм позволяет отслеживать "мертвые" ссылки на уже удаленные файлы – очевидно, sequence number внутри file reference будет отличается от номера последовательности соответствующей файловой записи (этой проверкой занимается утилита chkdsk и автоматически, насколько мне известно, она не выполняется).

смещение

размер

описание

00h

6

индекс файловой записи (FILE record number), отсчитываемый от нуля

06h

2

номер последовательности (sequence number)



R-Studio осуществляет поиск уцелевших файловых записей


В процессе сканирования будут найдены все уцелевшие файлы (как удаленные, так и нет) и восстановлена структура директорий по корневой каталог включительно. Постойте! Как же так?! Ведь при форматировании он был уничтожен и сформирован заново! Хе-хе. Файловую систему NTFS можно уничтожить только динамитом. Как уже отмечалось, в отличии от FAT, в NTFS каталоги являются лишь вспомогательной структурой данных, проиндексированной для ускорения отображения их содержимого. Всякая файловая запись независимо от своего происхождения, содержит ссылку на материнский каталог, представляющую собой номер записи в MFT. А запись корневого каталога всегда располагается по одному и тому же месту!

Удаленные файловые записи могут ссылаться на уже уничтоженные каталоги. R-Studio складывает их в $$$FolderXXX, где XXX – порядковый номер директории. Иерархия подкаталогов в большинстве случаев успешно восстанавливается.

Просмотр виртуального дерева обнаруженных файлов осуществляется нажатием кнопки <F5> или через одноименный пункт контекстного меню. Выбрав файл (или даже целый каталог с кучей подкаталогов) жмем <F2> или залезаем в предварительный просмотр/редактирование (пункт edit/view контекстного меню). Это достаточно мощный инструмент, отображающий внутреннее содержимое восстанавливаемого файла со всеми его атрибутами, списками отрезков и т. д. в "очеловеченном" формате, хотя до NT Exploder'а ему ох как далеко. При желании можно восстановить все файлы за раз ("Recovery All"), или выбрать восстановление по маске (Mask).



феникс собственной персоной


The Sleuth Kit представляет собой бесплатно распространяемый комплект утилит для ручного восстановления файловой системы, который можно найти по адресу (http://www.sleuthkit.org/), там же (http://www.sleuthkit.org/sleuthkit/docs/ref_fs.html) лежит краткий how-to. Увы, чудес не бывает и вся методика восстановления сводится к сканированию свободного пространства на предмет поиска фрагментов с известным содержимым.

Foremost — еще одна бесплатная утилита для восстановления удаленных файлов, основанная формате их заголовков на особенностях структуры. Естественно, она работает только с теми файлами, чье строение ей известно. Тем не менее, по сравнению с ее предшественницами это большой шаг вперед! Кстати говоря, утилита взаимодействует с файловой системой не напрямую, а обрабатывает файлы полученные командой dd или набором Sleuth Kit, благодаря чему она "поддерживает" все файловые системы. Последняя версия лежит на сервере http://foremost.sourceforge.net/



ручное восстановление MFT. Подчеркнуты поля, подлежащие изменению


Коварный NT Explorer не позволяет редактировать поля в естественном режиме отображения, заставляя нас переключаться в HEX-mode и искать смещения всех значений самостоятельно. Найти заголовок атрибута $DATA очень просто — в его начале расположена последовательность 80 00 00 00 xx 00 00 00 01. В NTFS версии 3.0 она находится по смещению F8h от начала сектора. Поле Real Size во всех версиях NTFS располагается по смещению 30h относительно заголовка, а поля Allocated Size и Initialized Size соответственно по смещениям 28h/38h байт, причем значение Allocates Size должно быть кратно размеру кластера. Кстати, о кластерах. Убедитесь, что при переформатировании диска размер кластера не изменился, в противном случае у вас ничего не получится. Как восстановить исходный размер кластера? Да очень просто — набраться мужества и переформатировать восстанавливаемый диск с ключом /A:x, где x – размер кластера. А как его определить? Возьмем любой файл с известным содержимым и проанализируем его run-list. Пускаем контекстный поиск по всему диску, находим файл, запоминаем (записываем на бумажке) его стартовый сектор, после чего открываем закрепленный за ним FILE Record, декодируем run-list и вычисляем номер первого кластера. Делим номер сектора на номер кластера и получаем искомую величину.

Теперь необходимо сгенерировать новый run-list. В общем случае он будет выглядеть так: 13 XX XX XX YY 00, где XX XX XX – трехбайтовый размер $MFT в кластерах, а YY – стартовый кластер. Стартовый кластер обязательно должен указывать на первый кластер MFT, в противном случае chkdsk не сможет работать. Если новый run-list длиннее нынешнего (а именно так скорее всего и будет) необходимо скорректировать длину атрибутного заголовка (она расположена по смещению 04h от его начала). Проделав эту нехитрую операцию, запустим chkdsk с ключом /F и блаженно откинемся на спинку кресла, созерцая как возрождаются наши милые папки и файлы. Единственное, что не восстанавливается — так это дескрипторы безопасности: всем файлам/папкам назначаются права доступа по умолчанию. В остальном же, с отремонтированным диском вполне можно работать, не опасаясь, что он рухнет окончательно. Файлы, ссылающиеся на несуществующие каталоги складываются в папку Found.xxx. Это "долгожители" пережившие несколько циклов переформатирования, в буквальном смысле вытащенные с потустороннего света.


Сложнее восстановить том, чей MFT сильно фрагментирован. Прежний run-list при форматировании был уничтожен, зеркальная копия так же пострадала. Ничего другого не остается, как собирать все фрагменты руками. Звучит намного страшнее, чем выглядит. В отличии от всех остальных файлов диска, $MFT-файл имеет замечательную сигнатуру FILE, присутствующую в начале каждой файловой записи. Все, что нам нужно — последовательно сканируя раздел от первого кластера до последнего, выписать начало и конец каждого из фрагментов, принадлежащих MFT. Затем из этой цепочки необходимо исключить $MFTMirr. Его легко узнать — он расположен в середине раздела и содержит копии файловых записей $MFT, $MFTMirr, $LogFile и $Volume, причем $MFTMirr ссылается сам на себя. Допустим, наш список выглядит так: 08h – 333h, 669h – 966h, 1013 – 3210h. В грубом приближении ему будет соответствовать следующий run-list: 12 2B 03 08 22 23 03 69 96 22 FD 21 13 10 00. (Подробнее о кодировании/декодировании run-list'ов см. "списки отрезков" в прошлой статье этого цикла).

"В грубом" потому, что мы не знаем в какой последовательности располагались эти отрезки в файле (порядок расположения фрагментов на диске далеко не всегда совпадает с порядком отрезков в run-list'е). Что произойдет, если порядок сборки $MFT-файла окажется нарушен? А вот что — внутри MFT все файловые записи ссылаются друг на друга по своему порядковому номеру, представляющим индекс массива. Эти ссылки необходимы для восстановления структуры директорий, организации hard link'ов и еще кое-чего. Ссылки на материнский каталог дублируются в индексах и восстанавливаются элементарно. Hard link'и мрут безвозвратно (ну разве что попробовать пересобрать $MFT-файл в другом порядке), но они практически нигде и никем не используются, как говорится, было бы что терять. По-настоящему туго приходится сильно фрагментированным файлам, занимающим несколько файловых записей, раскиданных по разным $MFT-фрагментам. Здесь выручает только перестановка фрагментов. К счастью, количество комбинаций обычно бывает невелико и процедура восстановления занимает совсем немного времени. Хорошая новость – начиная с NTFS версии 3.1 (соответствующей Windows XP) в MFT номера файловых записей хранятся в явном виде (четырехбайтовое поле, расположенное по смещению 2Ch от начала FILE Record), что делает задачу восстановления тривиальной.


ChkDsk за работой


GetDataBack от создателя Disk Explorer'а. Полная автоматизация и никакой ручной работы. Сканирует MFT и выводит все файлы, которые только удалось найти (включая удаленные), рассовывая их по директориям (при условии, что соответствующие индексы не повреждены). Если споткнется о BAD-сектор – вылетит не прощаясь. Зато поддерживает удаленное восстановление, создание образов дисков, и мощную систему поиска по файлам (дата/размер), но почему-то нет поиска по содержимому, что не есть хорошо. Допустим, вы хотите восстановить файл со своей диссертацией ключевые слова которой вам известны, а вот в каких секторах они располагаются – не ведомо. Тоже самое относится и к поиску файла записной книжки с телефоном приятеля. Тем не менее, для большинства рядовых задач по восстановлению, возможностей GetDataBack'а хватает с лихвой. Демонстрационную версию программы под NTFS можно раздобыть по адресу (http://www.runtime.org/gdbnt.zip). Она все показывает, но восстанавливать ничего не дает. Однако, позволяет открывать файлы ассоциированным с ними приложениями. Важно отметить, GetDataBack не является доктором, таким как NDD или ChkDsk. Она не лечит разделы, а всего лишь позволяет скопировать из них уцелевшие файлы.



схематичное представление разбитого диска


При старте компьютера, BIOS выбирает загрузочный винчестер (обычно Primary Master, но порядок загрузки в большинстве BIOS'ов можно изменять, а самые продвинутые из них при удержании ESC по время прохождения post'а (процесса начального тестирования оборудования) даже выводят интерактивное меню), считывает первый сектор (цилиндр 0/головка 0/сектор 1) в память по адресу 0000h:7C00h, проверяет наличие сигнатуры 55h AAh в его конце и если такая сигнатура действительно обнаруживается передает управление на 0000h:7C000h. В противном случае анализируется следующее загрузочное устройство, а если таковое отсутствует – выдается ругательное сообщение.

Первичный загрузчик, получив управление, сканирует partition table (которая уже загружена в память!), находит активный раздел (Boot Indicator === 80h), извлекает номер стартового сектора раздела, так же называемого boot-сектором, загружает его в память по адресу 0000h:7C00h (предварительно переместив свое тело в другое место, чтобы избежать затирания), убеждается в наличие сигнатуры 55h AAh, передавая управление по 0000h:7C00h, в противном случае выдается ругательное сообщение и после нажатия на клавишу компьютер перезагружается. Некоторые загрузчики поддерживают несколько активных разделов, последовательно перебирая их один за другим, но это уже отсебятина разработчиков, выходящих за стандартные спецификации Microsoft, что, впрочем, никого не смущает.

Если первичный загрузчик поврежден, то BIOS не сможет запустить операционную систему с такого диска, однако, при подключении его "вторым" (или загрузке с дискеты) все логические диски будут доступны. Как минимум они должны быть "видны", т. е. команды C:, D:, . E: выполняются нормально, правда работоспособность команды dir уже не гарантируется. Во-первых, для этого файловая система соответствующего раздела должна быть известна загруженной операционной системе и не повреждена, а, во-вторых, должен быть цел boot-сектор (но об этом позже).

Partition Table, которую анализирует master boot code, а чуть позже – драйвер логических дисков операционной системы, состоит из четырех 10h записей, расположенных по смещению 1BEh, 1CEh, 1DEh, 1EEh байт от начала диска соответственно. Каждая из них описывает свой логический раздел, задавая его стартовый и конечный сектора, записанные в CHS-формате (да! даже если диск работает в LBA-режиме, патриции все равно адресуются через CHS!). Поле относительное смещение раздела, отсчитываемое от начала таблицы разделов, является вспомогательным и его избыточность очевидна. Тоже самое относится и полю с общим количеством секторов на диске – как будто это нельзя вычислить на основе стартового и конечного секторов! Одни операционные системы и загрузчики игнорируют вспомогательные поля, другие же их активно используют, поэтому они должны соответствовать действительности.


Поле идентификатора диска содержит уникальную 32-разрядную последовательность, помогающую операционной системе отличить один смонтированный диск от другого и автоматически копирующую в следующий ключ реестра: HKLM\SYSTEM\MountedDevices. На самом деле, Windows свободно обходится и без него, поэтому содержимого этого поля некритично.

Поле Boot ID содержит идентификатор файловой системы, установленной на разделе, который в случае NTFS равен 07h. За динамическими дисками, согласно фирменной спецификации, закреплен идентификатор 42h. На самом деле, это справедливо лишь для тех из них, что получены путем обновления (update) обычного раздела до динамического. Сведения об остальных динамических дисках в таблице разделов не хранятся, а содержатся в последнем мегабайте физического диска в LDM-базе, и для стандартных дисковых менеджеров они не видны. При установке операционной системы семейства Windows 9х или UNIX на винчестер, содержащий динамические диски, они могут быть необратимо утеряны, поскольку согласно таблице разделов занятое ими пространство отмечено как свободное. Тем не менее, загрузочный логический диск (независимо от того динамический он или нет) в обязательном порядке должен присутствовать в partition table, иначе BIOS не сможет его загрузить.

Четырех записей partition table, обеспечивающих всего четыре логических диска, явно не хватало, но расширять таблицу разделов было уже некуда – последняя запись упиралась в конец сектора, а использовать следующий сектор разработчиком не хотелось, поскольку его активно использовали многие вирусы и нестандартные драйвера, к тому же это все равно не решало проблемы, а лишь оттягивало конец. Тогда инженеры нашли другое решение, предложив концепцию расширенных разделов (Extended partition). Если boot ID некоторого раздела равен 05h или 0Fh, он трактуется как "виртуальный физический диск"[3], со своей собственной partition table, расположенной в его начале, на которую и указывает стартовый сектор расширенного раздела. Короче говоря, таблица разделов получается вложенной и уровень вложения ограничен разве что свободным местом жесткого диска и количеством стековой памяти загрузчика (при условии, что он использует рекурсивный алгоритм сканирования). Таблица разделов как бы размазывается вдоль винчестера. Большинство утилит резервирования сохраняют лишь первый сектор, чего явно недостаточно (впрочем, первый сектор гибнет намного чаще других, так что даже плохая политика резервирования лучше, чем совсем ничего).




дисковый редактор LDE (Linux Disk Editor)


Работа с lde на первых порах производит довольно странное впечатление: чувствуешь себя неандертальцем, пересевшим с IBM PC на УКНЦИ/ZX Spectrum и добывающим огонь трением. Впрочем, со временем это проходит (программист, как известно, существо неприхотливое и ко всему привыкающее). Как вариант, можно загрузится со "спасательной дискеты" и использовать знакомый Norton Disk Editor или Runtime NT Explorer, запущенной из-под Windows PE (версия Windows NT, запускающаяся с CD-ROM без предварительной установки на жесткий диск). С одной стороны это удобно (привычный интерфейс и все такое), с другой — ни Disk Editor, ни NT Explorer не поддерживают ext2fs/ext3fs, и все структуры данных приходится декодировать вручную.



обычный (слева) и разряженный (справа) тома


Большинство файловых систем трактуют том как совокупность файлов, свободного дискового пространства и служебных структур файловой системы, но в NTFS все

служебные структуры представлены файлами, которые (как и это и положено файлу) могут находится в любом

месте тома, при необходимости фрагментируя себя на несколько частей.

Самым главным служебным файлом является $MFT – Master File Table (Главная Файловая Таблица) – своеобразная база данных, хранящая информацию обо всех файлах тома – их именах, атрибутах, способе и порядке размещения на диске (каталог так же является файлом особого типа, со списком принадлежащих ему файлов и подкаталогов внутри). Важно подчеркнуть, что в MFT присутствуют все файлы, находящиеся во всех подкаталогах тома, поэтому для восстановления диска наличия $MTF-файла будет вполне достаточно.

Остальные служебные файлы (кстати говоря, называемые метафайлами

или метаданными – metafile/metadata соответственно и всегда предваряются знаком доллара '$') носят сугубо вспомогательный характер, интересный только самой файловой системе. К ним в первую очередь относится: $LogFile – файл транзакций, $Bitmap – карта свободного/занятого пространства, $BadClust – перечень плохих кластеров и т. д. Подробнее см. "назначение некоторых служебных файлов". Текущие версии Windows блокируют доступ к служебным файлам с прикладного уровня (даже с правами администратора!) и всякая попытка открытия/создания такого файла в корневом каталоге обречена на неуспех.

Классическое определение, данное в учебниках информатики, отождествляет файл с именованной записью на диске. Большинство файловых систем добавляет к этому понятие атрибута (attribute) – некоторой вспомогательной характеристики, описывающей время создания, права доступа и т. д. В NTFS имя файла, данные файла и его атрибуты полностью уравнены в правах. Можно сказать, что всякий NTFS-файл представляет собой совокупность атрибутов, каждый из которых хранится как отдельный поток (streams) байтов, поэтому во избежании путаницы, атрибуты, хранящие данные файла, часто называют потоками.


Каждый атрибут состоит из тела (body) и заголовка (header). Атрибуты делятся на резидентные (resident) и нерезидентные (non-resident). Резидентные атрибуты хранятся непосредственно в $MTF, что существенно уменьшает грануляцию дискового пространства и сокращает время доступа. Нерезидентные – хранят в $MTF лишь свой заголовок, описывающий порядок размещения атрибута на диске (см. "списки отрезков")

Назначение атрибута определяется его типом

(type) – четырехбайтовым шестнадцатеричным значением. При желании атрибуту можно дать еще и имя

(name), состоящее из символов, входящих в соответствующее пространство имен (см. "пространства имен"). Подавляющее большинство файлов имеет по меньшей мере три атрибута: стандартная информация о файле (время создания, модификации, последнего доступа, правда доступа и т. д.) хранится в атрибуте типа 10h, условно обозначаемым $STANDARD_INFORMATION. Ранние версии Windows NT позволяли обращаться к атрибутам по их условным обозначениям, однако, Windows 2000 и Windows XP лишены этой возможности. Полное имя файла (не путать с путем!) хранится в атрибуте типа 30h ($FILE_NAME). Если у файла есть одно или более альтернативных имен (например, MS-DOS имя), таких атрибутов может быть и несколько (см. "типы атрибутов"). Здесь же хранится ссылка (file reference) на материнский каталог, позволяющая разобраться к какому каталогу данный файл/подкаталог принадлежит. Данные файла по умолчанию хранятся в безымянном атрибуте типа 80h ($DATA), однако, при желании прикладные приложения могут создавать дополнительные потоки данных, отделяя имя атрибута от имени файла знаком двоеточия (например, "ECHO xxx > file:attr1; ECHO yyy > file:attr2; more < file:attr1; more  < file:attr2").

Изначально в NTFS была заложена способность индексации любых атрибутов, значительно сокращающая время поиска файла по заданному списку критериев (например, времени последнего доступа). Внутренне индексы хранятся в виде двоичных деревьев, поэтому среднее время выполнения запроса оценивается как O(lg n). Однако, в текущих NTFS-драйверах реализована индексация лишь по одному атрибуту – имени файла. Как уже говорилось выше, каталог представляет собой особенный файл – файл индексов (INDEX). В отличии от FAT, где файл каталога представляет единственный источник данных об организации файлов, в NTFS файл каталога используется лишь для ускорения доступа к содержимому директории и не является обязательным, поскольку ссылка на материнский каталог всякого файла в обязательном порядке присутствует в атрибуте его имени ($FILE_NAME).

Каждый атрибут может быть зашифрован, разряжен или сжат. Однако, техника работы с такими атрибутами далеко выходит за рамки первичного знакомства с организацией файловой системы и будет рассмотрена позднее. А пока же мы углубимся в изучение фундамента файловой системы – структуры $MFT.


последовательно расположенные группы цилиндров


В UFS cуперблок располагается по смещению 8192 байт от начала раздела, что соответствует 16-сектору. В UFS2 он "переехал" на 65536 байт (128 секторов) от начала, освобождая место для дисковой метки и первичного загрузчика операционной системы, а для действительно больших (в исходных текстах — piggy, т. е. "свинских") систем предусмотрена возможность перемещения суперблока по адресу 262144 байт (целых 512 секторов)!

Среди прочей информации суперблок содержит:

q       cblkno — смещение первой группы блока цилиндров, измеряемый в фрагментах, отсчитываемых от начала раздела;

q       fs_iblkno — смещение первой inode в первой группе цилиндров (фрагменты от начала раздела);

q       fs_dblkno — смещение первого блока данных в первой группе цилиндров (фрагменты от начала раздела);

q       fs_ncg — кол-во групп цилиндров (штуки);

q       fs_bsize – размер одного блока в байтах;

q       fs_fsize — размер одного фрагмента в байтах;

q       fs_frag — кол-во фрагментов в блоке;

q       fs_fpg – размер каждой группы цилиндров, выраженный в блоках (так же может быть найден через fs_cgsize);

Для перевода смещений, выраженных в фрагментах, в номера секторов, служит следующая формула: sec_n(fragment_offset) = fragment_offset*(fs_bsize/fs_frag/512) или ее более короткая разновидность: sec_n(fragment_offset) = fragment_offset*fs_fsize

/512;

Структура суперблока определена в файле /src/ufs/ffs/fs.h и в упрощенном виде выглядит так:

struct fs {

/* 0x00 */    int32_t       fs_firstfield;      /* historic file system linked list, */

/* 0x04 */    int32_t       fs_unused_1;        /*     used for incore super blocks */


/* 0x08 */ааа ufs_daddr_t fs_sblkno;аааа аааааа /* addr of super-block in filesys */

/* 0x0C */ааа ufs_daddr_t fs_cblkno;ааааааааааа /* offset of cyl-block in filesys */

/* 0x10 */ааа ufs_daddr_t fs_iblkno;ааааааааааа /* offset of inode-blocks in filesys */

/* 0x14 */ааа ufs_daddr_t fs_dblkno;аааа аааааа /* offset of first data after cg */

/* 0x18 */ааа int32_tаааааа fs_cgoffset;ааааааа /* cylinder group offset in cylinder */

/* 0x1C */ааа int32_tаааааа fs_cgmask;ааааааааа /* used to calc mod fs_ntrak */

/* 0x20 */ааа time_t аааааа аfs_time;ааааааааааа /* last time written */

/* 0x24 */ааа int32_tаааааа fs_size;ааааааааааа /* number of blocks in fs */

/* 0x28 */ааа int32_tаааааа fs_dsize;аааааааааа /* number of data blocks in fs */

/* 0x2C */ааа int32_tаааааа fs_ncg;аааааааааааа /* number of cylinder groups */

/* 0x30 */ааа int32_tаааааа fs_bsize;аааааааааа /* size of basic blocks in fs */

/* 0x34 */ааа int32_tаааааа fs_fsize;аааааааааа /* size of frag blocks in fs */

/* 0x38 */ааа int32_tаааааа fs_frag;ааааааааааа /* number of frags in a block in fs */

/* these are configuration parameters */

/* 0x3T */ааа int32_tаааааа fs_minfree;а аааааа /* minimum percentage of free blocks */

/* 0x40 */ааа int32_tаааааа fs_rotdelay; аааааа /* num of ms for optimal next block */

/* 0x44 */ааа int32_tаааааа fs_rps;аааааааааааа /* disk revolutions per second */

/* sizes determined by number of cylinder groups and their sizes */

/* 0x98 */ааа ufs_daddr_t fs_csaddr;ааааааааааа /* blk addr of cyl grp summary area */

/* 0x9C */ааа int32_tаааааа fs_cssize;ааааааааа /* size of cyl grp summary area */

/* 0xA0 */ааа int32_tаааааа fs_cgsize;ааааааааа /* cylinder group size */

/* these fields can be computed from the others */

/* 0xB4 */ааа int32_tаааааа fs_cpg;аааааааааааа /* cylinders per group */

/* 0xB8 */ааа int32_tаааааа fs_ipg;аааааааааааа /* inodes per group */

/* 0xBC */ааа int32_tаааааа fs_fpg;аааааааааааа /* blocks per group * fs_frag */

/* these fields are cleared at mount time */

/* 0xD0 */ааа int8_tаа fs_fmod;ааааааааа /* super block modified flag */

/* 0xD1 */ааа int8_tаа fs_clean;аааааааа /* file system is clean flag */

/* 0xD2 */ааа int8_t аааааа аfs_ronly;аааааааааа /* mounted read-only flag */

/* 0xD3 */ааа int8_tаа fs_flags;аааааааа /* see FS_ flags below */

/* 0xD4 */ааа u_char fs_fsmnt[MAXMNTLEN];ааааа /* name mounted on */

};


ручное восстановление файла с помощью Disk Probe


Исправляем 00h на 01h, записываем изменения и… Ничего не выходит?! А чего вы хотели! Ведь помимо этого необходимо еще: а) сообщить файлу /$MFT:$BITMAP, что данная MFT-запись вновь используется; б) отобрать у файла /$BITMAP номера кластеров, принадлежащие восстанавливаемому файлу; с) перестроить двоичное дерево индексов, хранящее содержимое каталога. Первые два пункта не проблема, но вот над последним придется попыхтеть. Или… просто запустить chkdsk с ключом /F. Он самостоятельно найдет "потерянный" файл и внесет все необходимые изменения в файловую систему. От нас потребуется только установить флаг по смещению 16h в единицу, а остальное – его забота. После этих нехитрых манипуляций файл оказывается в своем родном каталоге. Восстановленный!

C:\chkdsk D: /F

Тип файловой системы: NTFS.

Проверка файлов завершена.

Проверка индексов завершена.

Восстановление потерянных файлов.

Восстановление потерянного файла test.txt в файле каталога 5

Замена неправильного идентификатора безопасности для файла 29

Проверка дескрипторов безопасности завершена.

Исправление ошибок в атрибуте BITMAP основной таблицы файлов.

Windows сделала изменения в файловой системе.

   1068290 КБ всего на диске.

        20 КБ в 2 файлах.

         4 КБ в 9 индексах.

         0 КБ в поврежденных секторах.

      7894 КБ используется системой.

      7392 КБ занято под файл журнала.

   1060372 КБ свободно на диске.

Размер кластера:                   2048 байт.

Всего кластеров на диске:        534145.

    530186 кластеров на диске.



Disk Editor отображает корневую директорию


Microsoft DiskProbe, входящий в состав бесплатно распространяемого пакета Support Tools, это незатейливый и довольно неудобный в использовании дисковый редактор. Если все, что вам нужно – это подправить пару байт в нужных секторах, Disk Probe вполне подойдет, но для восстановления серьезных разрушений он непригоден. Тем менее, базовые функции редактирования им поддерживаются – чтение (запись) логических/физических секторов и групп, просмотр Partition Table, FAT16 и NTFS boot-секторов в естественном виде, поддержка UNICODE, глобальный поиск по фиксированному/произвольному смещению строки от начала сектора, запись/восстановление секторов в/из файла и т. д. Основная претензия – отсутствие горячих клавиш и невозможность перехода к следующему сектору по PagePown (для каждого сектора приходится лезть в меню, что ужасно напрягает).



схематичное изображение inode


В UFS2 формат inode был существенно изменен — появилось множество новых полей, удвоилась ширина адресных полей и т. д. Что это обозначает для нас в практическом плане? Смещения всех полей изменились, только и всего, а общий принцип работы с inod'ами остался прежним:

struct ufs2_dinode {

/* 0x00 */ u_int16_t di_mode;      /*   0: IFMT, permissions; see below. */

/* 0x02 */ int16_t   di_nlink;     /*   2: File link count. */

/* 0x04 */ u_int32_t di_uid;              /*   4: File owner. */

/* 0x08 */ u_int32_t di_gid;              /*   8: File group. */

/* 0x0C */ u_int32_t di_blksize;   /*  12: Inode blocksize. */

/* 0x10 */ u_int64_t di_size;      /*  16: File byte count. */

/* 0x18 */ u_int64_t di_blocks;    /*  24: Bytes actually held. */

/* 0x20 */ ufs_time_t      di_atime;     /*  32: Last access time. */

/* 0x28 */ ufs_time_t      di_mtime;     /*  40: Last modified time. */

/* 0x30 */ ufs_time_t      di_ctime;     /*  48: Last inode change time. */

/* 0x38 */ ufs_time_t      di_birthtime; /*  56: Inode creation time. */

/* 0x40 */ int32_t   di_mtimensec; /*  64: Last modified time. */

/* 0x44 */ int32_t   di_atimensec; /*  68: Last access time. */

/* 0x48 */ int32_t   di_ctimensec; /*  72: Last inode change time. */

/* 0x4C */ int32_t   di_birthnsec; /*  76: Inode creation time. */

/* 0x50 */ int32_t   di_gen;              /*  80: Generation number. */

/* 0x54 */ u_int32_t di_kernflags; /*  84: Kernel flags. */

/* 0x58 */ u_int32_t di_flags;     /*  88: Status flags (chflags). */

/* 0x5C */ int32_t   di_extsize;   /*  92: External attributes block. */

/* 0x60 */ ufs2_daddr_tdi_extb[NXADDR];/*  96: External attributes block. */

/* 0x70 */ ufs2_daddr_tdi_db[NDADDR];    /* 112: Direct disk blocks. */

/* 0xD0 */ ufs2_daddr_tdi_ib[NIADDR];    /* 208: Indirect disk blocks. */

/* 0xE8 */ int64_t   di_spare[3];  /* 232: Reserved; currently unused */

 };