Экстенты, файлы, суперблоки. Как работают файловые системы ext3 и ext4 и как в них восстанавливать данные
Life-Hack [Жизнь-Взлом]/ХакингКаждый, кто работал в Linux, хотя бы однажды удалял ценный файл, а то и весь корневой каталог целиком! rm -rf живее всех живых, резервной копии нет (а должна бы быть!), времени на поиски и выбор утилит для восстановления — тоже. Как же быть?
INFO
В 2006 году вышла в свет книга Криса Касперски «Восстановление данных», которая быстро стала бестселлером. Сейчас эта книга готовится к переизданию. Мы публикуем отрывок из этой книги, посвященный восстановлению данных в файловых системах ext.
Ошибочное удаление файлов в *NIX — это достаточно распространенное явление, наверное, даже более частое, чем в мире Microsoft. Под Windows большинство файловых операций выполняется вручную с помощью проводника или других интерактивных средств типа FAR или Total Commander. Интерактивные среды есть и в Linux (KDE, GNOME, XFCE…), но немалая часть фанатов Linux — поклонники командной строки. Командная же строка — это регулярные выражения и скрипты, то есть автоматизированные средства управления — мощные, удобные и, при неправильном использовании, разрушительные. Малейшая небрежность — и можешь навсегда попрощаться со своими файлами!
Перефразируя Булгакова, можно сказать: мало того что файл смертен, так он еще и внезапно смертен! Беда никогда не предупреждает о своем приходе, и администратору приходится быть постоянно начеку. Несколько секунд назад все было хорошо: цвела весна, винчестер оживленно стрекотал всеми своими головками, администратор отхлебывал кофе из черной кружки с надписью root, как вдруг сотни гигабайт ценнейших данных внезапно разлетелись на мелкие осколки. Все силы брошены на разгребание завалов и спасение всех, кого еще можно спасти.
Доступность исходных текстов драйвера файловой системы значительно упрощает исследование ее внутренней структуры, которая, кстати говоря, очень проста. Поэтому восстановление данных на разделах ext2/3/4 — задача тривиальная.
ЗНАКОМЬТЕСЬ! СЕМЕЙСТВО РАСШИРЕННЫХ ФАЙЛОВЫХ СИСТЕМ
Изначально Linux был чем‑то вроде вольного пересказа ОС Minix, разработка велась под ней же, и работали первые версии Linux на файловой системе Minix. Называлась та незамысловато — MINIX file system — и, в свою очередь, была вдохновлена файловой системой UNIX — UFS. Но, поскольку сама Minix разрабатывалась скорее в учебных целях, ее файловая система не обладала широкими возможностями. Например, размер раздела не мог превышать 64 Мбайт, а максимальная длина имени файла — 14 или 30 символов в зависимости от версии. Чтобы преодолеть такие ограничения, начали разрабатывать собственную ФС для Linux.
Немного об истоках
Новая файловая система расширяла возможности MINIXfs, за что, видимо, и получила название extended filesystem. Первая реализация расширенной файловой системы, ext fs, увидела свет в 1992 году в ядре Linux версии 0.96c. Теперь приверженцы Linux были ограничены двумя гигабайтами для раздела, а файлы могли иметь имя длиной до 255 символов. Тем не менее эта ФС была все еще сравнительно проста, поэтому дальнейшее ее развитие не заставило себя долго ждать. Примерно в это же время, кстати, в Linux появился такой уровень абстракции, как виртуальная файловая система (VFS), облегчающий добавление поддержки новых ФС в ядро.
С появлением через пару лет ext2 максимальные размеры файла и файловой системы возросли до 16 Гбайт и 2 Тбайт соответственно (при размере блока 1 Кбайт). Часть блоков (обычно 5%) теперь резервировалась под рут, не позволяя обычным пользователям заполнить весь раздел без остатка. Тогда эта ФС стала практически стандартом де‑факто на линуксах, а ее реализации, говорят, были и под NT.
Поколение ext3
Третья расширенная файловая система (Third extended file system, ext3) появилась почти двадцать лет назад в одной из версий Linux 2.4.14. Она во многом напоминает свою предшественницу, ext2, но отличается поддержкой журналирования (в терминологии NTFS — транзакций). В отличие от ext2fs, она намного бережнее относится к массиву каталогов, хотя, как мы увидим чуть далее, нам это не сильно поможет.
В начале диска расположен загрузочный сектор, который на незагрузочных разделах может быть пустым. За ним по смещению 1024 байта от начала первого сектора лежит суперблок (superblock
), содержащий ключевую информацию о структуре файловой системы. В FAT и NTFS эта информация хранится непосредственно в загрузочном секторе. В первую очередь нас будет интересовать 32-разрядное поле s_log_block_size
, расположенное по смещению 18h
байт от начала суперблока. Здесь хранится размер одного блока (block
) или, в терминологии MS-DOS/Windows, кластера, выраженный в виде указателя позиции, на которую нужно сдвинуть число 200h
. В естественных единицах это будет звучать так: block_size = 200h << s_log_block_size
(байт). То есть если s_log_block_size
равен нулю, размер одного блока составляет 400h
байт, или два стандартных сектора. Структура дискового тома, отформатированного под ext3fs, показана в листинге ниже. Подобную информацию можно увидеть в выводе команды fsstat
.
Смещение Размер Описание
-------- ------ ------------------------
0 1 boot record ; Загрузочный сектор
-- block group 0 -- ; Группа блоков 0
(1024 bytes) 1 superblock ; Суперблок
2 1 group descriptors ; Дескриптор группы
3 1 block bitmap ; Карта свободных блоков
4 1 inode bitmap ; Карта свободных inode
5 214 inode table ; Массив inode (сведения о файлах)
219 7974 data blocks ; Блоки данных (файлы, каталоги)
-- block group 1 -- ; Группа блоков 1
819 1 superblock backup ; Копия суперблока
819 1 group descriptors backup ; Копия дескриптора группы
819 1 block bitmap ; Карта свободных блоков
819 1 inode bitmap ; Карта свободных inode
819 214 inode table ; Массив inode (сведения о файлах)
840 7974 data blocks ; Блоки данных (файлы, каталоги)
-- block group 2 -- ; Группа блоков 2
16385 1 block bitmap ; Карта свободных блоков
16386 1 inode bitmap ; Карта свободных inode
16387 214 inode table ; Массив inode (сведения о файлах)
16601 3879 data blocks ; Блоки данных (файлы, каталоги)
Вслед за суперблоком идут дескрипторы групп (group descriptors
) и карты свободного пространства, они же битовые карты (block bitmap
/inode bitmap
), которые не имеют большого практического значения для наших целей. Что же касается таблицы инод, расположенной за ними, то она заслуживает более подробного рассмотрения. В ext3fs (как и многих других файловых системах из мира UNIX) так называемый индексный дескриптор (inode, он же инода) играет ту же самую роль, что и файловая запись в NTFS. Здесь сосредоточена вся информация о файле, кроме его имени: тип файла (обычный файл, каталог, символьная ссылка и так далее), его логический и физический размер, схема размещения на диске, время создания, модификации, последнего доступа или удаления, права доступа, а также ссылки на файл. Формат представления иноды в ext3 выглядит так:
Смещение Размер Описание
-------- ------ -------------
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 ; Значение, зависящее от ОС
40 12x4 i_block ; Ссылки на первые 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 ; ACL файла
108 4 i_dir_acl ; ACL директории
112 4 i_faddr ; Положение последнего фрагмента
116 12 i_osd2 ; Структура, зависящая от ОС
Первые 12 блоков, занимаемых файлом, называются непосредственными блоками (для наглядности они отделены переносами). Они хранятся в массиве DIRECT BLOCKS
непосредственно в теле иноды. Каждый элемент массива представляет собой 32-битный номер блока. При среднем значении BLOCK_SIZE
, равном 4 Кбайт, непосредственные блоки могут адресовать до 4 × 12 == 48
Кбайт данных. Если файл превышает этот размер, создаются один или несколько блоков косвенной адресации (INDIRECT BLOCK
).
Первый блок косвенной адресации (1x INDIRECT BLOCK
, или просто INDIRECT BLOCK
) хранит ссылки на другие непосредственные блоки. Адрес этого блока хранится в поле i_indirect_block
в inode. Как легко вычислить, он адресует порядка 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 состоит в том, что, поскольку все ссылки хранятся в неупакованном виде, для каждого блока файла можно быстро найти соответствующий ему косвенный блок, даже если inode полностью разрушен!
Имя файла, как уже сказано, в inode не хранится. Ищи его внутри каталогов, представляющих собой массив записей, формат которого выглядит так:
Смещение Размер Описание
-------- ------ ----------
0 4 inode ; Ссылка на inode
4 2 rec_len ; Длина данной записи
6 1 name_len ; Длина имени файла
7 1 file_type ; Тип файла
8 ... name ; Имя файла
При удалении файла операционная система находит соответствующую запись в каталоге, обнуляет поле inode
и увеличивает размер предшествующей записи (поле rec_len
) на величину удаляемой записи. Таким образом, предшествующая запись «поглощает» удаленную. Хотя имя файла в течение некоторого времени остается нетронутым, ссылка на соответствующий ему индексный дескриптор (inode
) оказывается уничтоженной. Это создает проблему, так как теперь придется разбираться, какому файлу принадлежит обнаруженное имя.
Отдельно стоит поговорить о журнале файловой системы. Он гарантирует целостность файловой системы в случае непредвиденных сбоев. При этом важно понимать, что целостность файловой системы совсем не означает сохранность файлов!
Тем не менее наличие журнала играет важную роль при восстановлении данных в ext3/4, поскольку информация в нем зачастую помогает восстанавливать взаимосвязи элементов каталогов, инод и содержимого файлов (если она, конечно, еще не была затерта новыми событиями файловой системы). Вот одна из причин, почему важно как можно скорее отмонтировать раздел со случайно удаленными файлами!
Журнал файловой системы ext3 (да и ext4) может работать в трех режимах, и от выбора будет зависеть как надежность, так и производительность:
- обратной записи (writeback) — в журнал вносится только общая информация об операциях (метаданные), причем асинхронно по отношению к изменению в самих данных;
- упорядочивания (ordered) — в журнал также вносятся только метаданные, но перед записью изменений в файле на диск;
- полного журналирования (journal) — в журнал записываются и метаданные, и изменения в самом файле. Этот вариант, соответственно, самый «прожорливый», но только он может обеспечить целостность данных. Два предыдущих лишь ускоряют выявление ошибок ФС при проверке утилитой
fsck
и позволяют восстановить целостность файловой системы, но не содержимого хранящихся файлов.
В самом индексном дескрипторе при удалении файла тоже происходят большие изменения. Количество ссылок (i_links_count
) обнуляется и обновляется поле, хранящее время последнего удаления (i_dtime
). Все блоки, принадлежащие файлу, в карте свободного пространства (block bitmap
) помечаются как неиспользуемые, после чего данный inode
также освобождается (обновляется inode bitmap
).
Что принесла нам ext4?
Потолок разделов ext3 составляет 16 Тбайт. И если обычным пользователям это ограничение до лампочки, то в корпоративных сегментах оно приносит неудобства. К тому же использование битовых карт на больших разделах (начиная примерно с 1 Тбайт) заметно нагружает ЦП. В результате примерно в ядре версии 2.6.19 появилась ext4fs. Она не так совместима с ext3, как та совместима с ext2, хотя ext3-раздел можно примонтировать как ext4 и наоборот (но тогда теряется смысл нововведений в последней ФС). Более того, разработчики предусмотрели возможность перехода с ext3 на ext4 без переформатирования раздела.
Одно из главных улучшений четвертой расширенной файловой системы — использование дерева экстентов вместо карты свободных и занятых блоков, благодаря чему она намного эффективнее управляется с большими файлами. Это улучшает масштабируемость на разделы больших объемов. В то же время механизм экстентов позволяет уменьшать фрагментацию файлов, копируя фрагментированные части в непрерывные экстенты.
Также в ext4 появился механизм контрольных сумм журнала, гарантирующий, что в файловую систему будут внесены корректные изменения. Но, как и предшественница, ext4 может работать вообще без журнала, что немного улучшает ее производительность, но неизбежно ухудшает надежность файловой системы. Тем не менее данный режим можно назвать более предпочтительным для устройств на основе флеш‑памяти, поскольку очень частые изменения файла журнала расходуют ресурс ячеек памяти.
В общем, казалось бы, с такими новшествами наши данные должны зажить новой жизнью, не зная горестей! Но они по‑прежнему могут пропасть по многочисленным причинам. Что же на это скажет нам ext4?
Структура блоков ext4 не сильно отличается от имеющейся в ext3. Немного бо́льшие изменения ожидали структуру индексных дескрипторов. Во‑первых, она приобрела новые поля, стала вдвое объемнее и занимает минимум 256 байт. Это позволяет ей хранить, например, метки времени с наносекундной точностью (раньше была точность до секунды), счетчик изменений файла (он дает клиенту NFS возможность понять, изменился ли файл на стороне сервера), а также версию inode и ее контрольную сумму.
Сами же номера инод стали 48-битными, благодаря чему поддерживаются разделы до 1 Эбайт при блоке в 4 Кбайт. Подробно ознакомиться со структурой inode в ext4 можно в соответствующей документации. А мы заметим, что вместо карты блоков в иноде ext4 по смещению 0x28
расположилось дерево экстентов i_block[EXT4_N_BLOCKS=15]
. Экстенты определяют непрерывный участок в несколько расположенных друг за другом блоков. Один экстент может адресовать до 128 Мбайт при блоке в 4 Кбайт. Всего в одном индексном дескрипторе может храниться четыре экстента, а если их не хватает, то используется дерево экстентов, напоминающее схему косвенной адресации блоков в ext3.
Получить содержимое служебных данных файловой системы в удобочитаемом виде ты можешь с помощью команды fsstat
:
FILE SYSTEM INFORMATION
--------------------------------------------
File System Type: Ext4
< . . . >
Source OS: Linux
Dynamic Structure
Compat Features: Journal, Ext Attributes, Resize Inode, Dir Index
InCompat Features: Filetype, Needs Recovery, Extents, Flexible Block Groups,
Read Only Compat Features: Sparse Super, Large File, Huge File, Extra Inode Size
Journal ID: 00
Journal Inode: 8
METADATA INFORMATION
--------------------------------------------
Inode Range: 1 - 1572865
Root Directory: 2
Free Inodes: 1313879
Inode Size: 256
Orphan Inodes: 1456398, 1456397, 1456396, 1456395, 1456394,
CONTENT INFORMATION
--------------------------------------------
Block Groups Per Flex Group: 16
Block Range: 0 - 6291199
Block Size: 4096
Free Blocks: 4295049
< . . . >
Удаление файла в ext4 происходит в целом так же, как и в ext3: в записи каталога обнуляется поле inode и предшествующая запись поглощает удаляемую. В журнал при этом вносятся соответствующие записи. Также, благодаря использованию экстентов, удаление больших файлов в ext4 быстрее, чем в ext3.
В ПОИСКАХ УТРАЧЕННЫХ ДАННЫХ
В первую очередь обязательно размонтируй дисковый раздел или, на худой конец, перемонтируй его в режим «только чтение». Лечение активных разделов зачастую лишь увеличивает масштабы разрушений. Если восстанавливаемые файлы находятся на основном системном разделе, у нас два пути — загрузиться с Live CD или подключить восстанавливаемый жесткий диск на Linux-машину вторым.
Чтобы случайно что‑нибудь не испортить, никогда не редактируй диск напрямую. Работай с его копией! Копию можно создать командой cp /dev/sdb1 my_dump
, где sdb1
— имя устройства, а my_dump
— имя файла‑дампа. Файл‑дамп можно поместить на любом свободном разделе или скопировать на другую машину по сети. Все дисковые утилиты не заметят подвоха и будут работать с ним как с настоящим разделом. При необходимости его даже можно смонтировать на файловую систему: mount my_dump mount_point --o loop
, чтобы убедиться, что восстановление прошло успешно. Команда cp my_dump /dev/sdb1
копирует восстановленный файл‑дамп обратно в раздел, хотя делать это совсем необязательно. Проще (и безопаснее) копировать только восстанавливаемые файлы.
Особенности восстановления файлов в ext3
В ext3fs полное восстановление файлов невозможно, даже если эти файлы были только что удалены. В этом отношении данная файловая система проигрывает как FAT, так и NTFS. Как минимум — теряется имя файла. Точнее говоря, как минимум теряется связь имен файлов с их содержимым. При удалении небольшого количества хорошо известных файлов эта проблема остается решаемой. Однако ситуация серьезно осложняется, если ты удалил несколько служебных подкаталогов, в которых никогда раньше не заглядывали.
Достаточно часто индексные дескрипторы назначаются в том же порядке, в котором создаются записи в таблице каталогов. Благодаря наличию расширений имен файлов (.c, .gz, .mpg и так далее) количество возможных комбинаций соответствий обычно оказывается сравнительно небольшим. Тем не менее восстановить удаленный корневой каталог в автоматическом режиме никому не удастся (а вот NTFS с этим справляется без труда).
В целом стратегия восстановления выглядит приблизительно так: сканируем таблицу индексных дескрипторов (inode table
) и отбираем все записи, чье поле i_links_count
равно нулю. Сортируем их по дате удаления, чтобы файлы, удаленные последними, оказались в верхних позициях списка. Как вариант, если ты помнишь примерное время удаления файла, можно просто наложить фильтр.
Если соответствующие индексные дескрипторы еще не затерты вновь создаваемыми файлами, извлекаем список прямых/косвенных блоков и записываем их в дамп, корректируя его размер с учетом «логического» размера файла, за который отвечает поле i_size
.
А что с ext4?
По сути, процесс восстановления файлов (вернее, проблемы, препятствующие этому) тот же, что и в ext3. Однако лет этак десять назад появилась утилита с замечательным названием ext4magic (которая, впрочем, умеет работать и с разделами ext3). Она уже несколько лет не обновлялась, но и сами структуры ФС тоже по большей части сформировались давно.
Эта программа в отдельных случаях способна спасти не только файлы, но и их названия вместе со всеми атрибутами! Важную роль при восстановлении играет состояние журнала, которое и позволяет реконструировать взаимосвязь файлов с описывающей их служебной информацией — элементами каталогов и индексными дескрипторами. Естественно, чем меньше прошло времени и сделано изменений в файловой системе с момента удаления файлов, тем больше вероятность их успешно восстановить. После случайного удаления файла следует как можно быстрее отмонтировать ФС и по возможности создать образ восстанавливаемого раздела и работать уже с ним. Что ж, в этом деле без магии (и бубна) никак!
В примере ниже ты можешь видеть, как с ее помощью удается восстановить пару свежеудаленных файлов. Первой командой мы получаем информацию о состоянии корневого каталога файловой системы (-f
принимает директорию относительно корня раздела, а не корневого каталога твоей Linux-системы). В списке файлов номера инод удаленных файлов взяты в угловые скобки, а их размер отображается как равный нулю.
Следующая команда возвращает из небытия файлы, которые утилита сочла удаленными за последние 24 часа. По умолчанию они сохраняются в папку RECOVERDIR
в рабочем каталоге.
WARNING
Не забывай, что восстанавливать файлы на сам восстанавливаемый раздел — очень плохая затея!
Более того, сейчас, когда Linux далеко не только «конструктор для гиков», ext4magic уже не единственная тулза, умеющая восстанавливать файлы на ext-разделах. Вообще говоря, таким утилитам можно посвятить отдельную статью, но на всякий случай твои драгоценные данные могут спасти: DMDE, PhotoRec (от создателей утилиты testdisk
), The Sleuth Kit, foremost.
Также существует утилита R-Studio, поддерживающая и восстановление с разделов ext2/3/4. Есть версия и под Linux, а еще — бесплатный продукт под названием R-linux, работающий исключительно с семейством расширенных файловых систем. Вообще говоря, часть из перечисленных программ восстанавливает файлы по их сигнатурам из сырых образов разделов. Они, считай, не зависят от используемой ФС, но в то же время не помогут восстановить имена файлов и структуру каталогов и малополезны при фрагментации файлов.
ОН ВЫЖИВЕТ?
Файлам — жить! Даже если им случилось попасть под горячую руку не на разделе NTFS. И чем меньше времени прошло с момента неудачного удаления, тем больше шансов на успешное восстановление. Особенно если вовремя размонтировать разделы, внимательно проверять вводимые команды и вовремя делать резервные копии. Ты ведь сделал бэкап?