Восстановление структуры каталогов файловой системы Ext2fs
Очень важно, чтобы вы сразу отключили необходимый раздел командой umount, не производя с ним больше никаких операций. Если вы после удаления необходимых файлов запишите на этот раздел что-нибудь, то ваши шансы на успешное восстановление файлов резко уменьшаются.
Также вам понадобится достаточно новая версия ядра, потому что ядра 2.0.x и ниже очищают блоки косвенной адресации, что не позволит вам восстановить файлы длиной больше 12 блоков.
Я опишу лишь один метод восстановления, и не уделю большого внимания ошибкам, которые могут возникнуть в процессе восстановления. Если вы решите, что какой-то из нижеописанных шагов привел к ошибке, то я не рекомендую вам продолжать следовать этим советам.
Приготовления
Отключите раздел, на котором находились удаленные файлы. Назовем этот раздел /dev/hdx1:
# umount /dev/hdx1
Узнайте размер /dev/hdx1 в блоках командой:
# fdisk -l /dev/hdx
Теперь для дальнейшей работы вам понадобится дополнительный раздел того же размера, что и /dev/hdx1. Предположим, что у вас есть пустой жесткий диск /dev/hdy:
# fdisk /dev/hdy
Создайте на нем раздел того же размера, что и /dev/hdx1. Здесь и ниже размер - это размер раздела /dev/hdx1 в блоках (каждый блок - это 1024 байта), который вы узнали ранее.
Я использую fdisk версии 2.10f. Если вы используете другую версию fdisk, то работа с ней может различаться.
fdisk: n <- Создать новый раздел.
fdisk: p <- Основной раздел.
fdisk: <- Просто нажмите Enter, чтобы использовать предложенный начальный цилиндр.
fdisk: +размерK <- создайте раздел, имеющий тот же размер, что и /dev/hdx1.
fdisk: w <- Запишите таблицу разделов на диск и выйдите из программы.
Теперь скопируем содержимое исходного раздела на новый диск:
# dd if=/dev/hdx1 of=/dev/hdy1 bs=1k
Это может занять дительное время, в зависимости от размера раздела. Вы можете ускорить процесс, увеличив размер блока bs, но вам придется сделать его таким, чтобы раздел состоял из целого количества этих блоков.
С этого момента мы будем работать только с этой копией исходного раздела, чтобы мы всегда могли откатиться назад, если что-то пойдет не так.
Находим номера inode удаленных каталогов
Мы попытаемся выяснить номера inode удаленных каталогов:
# debugfs /dev/hdy1
Перейдите к тому месту, где были удаленные каталоги. Внутри debugfs вы можете обычным образом использовать команды ls и cd:
debugfs: ls -l
Эа команда выдаст на экран примерно следующее:
179289 20600 0 0 0 17-Feb-100 18:26 file-1
918209 40700 500 500 4096 16-Jan-100 15:18 file-2
160321 41777 0 0 4096 3-Jun-100 06:13 file-3
177275 60660 0 6 0 5-May-98 22:32 file-4
229380 100600 500 500 89891 19-Dec-99 15:40 file-5
213379 120777 0 0 17 16-Jan-100 14:24 file-6
Описание полей:
Номер inode.
Первые две (или одна) цифры означают вид inode:
2 = Символьное устройство
4 = Каталог
6 = Блочное устройство
10 = Обычный файл
12 = Символьная ссылка
Следующие четыре цифры - это обычные права доступа к файлу в формате Unix.
Владелец файла (в числовой форме).
Группа файла (в числовой форме).
Размер в байтах.
Дата (Вы наверно уже заметили здесь "Проблему-2000" в действии =)).
Время.
Имя файла.
Теперь выгрузим родительский каталог на диск. Здесь и ниже inode - это соответствующий номер inode (не забудьте символы '<' и '>').
debugfs: dump <inode> debugfs-dump
Выйдите из debugfs:
debugfs: quit
Анализируем содержимое каталога
Просмотрите выгруженное содержимое каталога в читаемом формате:
# xxd debugfs-dump | less
Каждая запись состоит из пяти полей. Байты первых двух полей представлены в обратном порядке. Это значит, что первый байт - самый младший.
Описание полей:
4 байта - номер inode.
2 байта - длина этой записи.
1 байт - длина имени файла (1-255).
1 байт - тип файла (0-7).
0 = Неизвестный
1 = Обычный файл
2 = Каталог
3 = Символьное устройство
4 = Блочное устройство
5 = Поток FIFO
6 = Поток SOCK
7 = Символьная ссылка
Имя файла (1-255 символов).
Если запись удаляется из каталога, то размер предыдущей записи увеличивается на размер удаляемой записи (предыдущая запись как бы "съедает" следующую).
Если файл переименовывается в более короткое имя, то уменьшается значение третьего поля.
Первая запись, которую вы увидите - это сам каталог, представляемый одной точкой.
Предположим, что у нас есть следующая запись в каталоге:
c1 02 0e 00 40 00 05 01 'u' 't' 'i' 'l' 's'
В ней номер inode будет "e02c1" (в шестнадцатиричной форме) или 918209 (в десятичной). Следующая запись находится через 64 байте (шестнадцатиричное 40). Мы также видим, что имя файла состоит из 5 байт ("utils") и что тип файла (01) соответствует обычному файлу.
Теперь пересчитаем номера inode подкаталогов в десятичную форму.
Если вы не любите производить такие операции вручную, то я для вас написал небольшую программу на C. Программа берет содержимое каталога (созданное debugfs, как описано ранее в разделе Разд. Находим номера inode удаленных каталогов). На стандартном выводе вы получаете список имен файлов и номеров inode.
Перед запуском этой программы вам надо загрузить записанное содержимое каталога в двоичный редактор и изменить поле "длина записи каталога" в записи, предшествующей восстанавливаемой. Это просто: если мы обозначим длину предшествующей записи как x, а длину записи, которую вы хотите восстановить как y, то вам надо заменить поле, содержащее x на x-y.
Программа называется e2dirana (ext2fs directory analyse), и ее можно найти по адресу http://www.matematik.su.se/~tomase/ext2fs-undeletion/
Находим удаленные inode
Получаем список всех удаленных inode.
# echo lsdel | debugfs /dev/hdy1 > lsdel.out
Одна проблема состоит в том, что debugfs не выдаст номера inode файлов, у которых была нулевая длина (у вас могли быть такие файлы, например, в каталоге /etc). Я опишу решение этой проблемы в разделах Разд. Пересчет и Разд. Последние коррективы.
Загрузите "lsdel.out" в текстовый редактор. Список inode должен быть отсортирован по времени удаления. Попробуйте точно вспомнить время, когда вы дали команду rm -rf. Скорее всего, это была последняя команда, и удаленные inode будут находиться в конце списка. Удалите все не интересующие вас строки. Запишите этот файл как "lsdel.out-selected".
Теперь мы удалим из этого файла все, кроме номеров inode:
# cut -b 1-8 lsdel.out-selected | tr -d " " > inodes
Для полной уверенности проверьте, что удаленные каталоги, номера которых мы нашли ранее, находятся в этом списке.
# grep ^inode$ inodes
, где inode - это соответствующий номер inode.
Активизируем inode
Теперь пришло время изменить некоторые флаги удаленных inode.
Скопируйте следующие 6 строк в файл, назвав его "make-debugfs-input".
#!/bin/sh
awk '{ print "mi <" $1 ">\n"\
"\n\n\n\n\n\n\n"\
"0\n"\
"1\n"\
"\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n" }'
Этим мы имитируем ручное исправление inode. Мы устанавливаем время удаления в 0 и количество ссылок в 1.
Я использую debugfs версии 1.18, и, если у вас другая версия, то вам, возможно, придется изменить количество "нажатий" клавиши Enter в вышеприведенном скрипте.
Теперь изменяем inode:
# ./make-debugfs-input < inodes | debugfs -w /dev/hdy1 | tail -c 40
Если все пройдет хорошо, то последнее сообщение должно быть таким: "Triple Indirect Block [0] debugfs:".
Добавляем записи в каталоги
Запустите debugfs в режиме чтения-записи.
# debugfs -w /dev/hdy1
Теперь вым надо добавить удаленные каталоги в каталог, где они ранее находились:
debugfs: link <inode> directoryname
Здесь inode - это номер inode, а directoryname - это номер каталога.
После того, как вы добавите ссылки, вы заметите, что удаленные каталоги появились в текущем каталоге. Вы можете теперь просмотреть его содержимое (при помощи debugfs).
Правда, размер каждого каталога равен 0, и это надо исправить, иначе они будут выглядеть пустыми в команде ls.
Выйдите из debugfs:
debugfs: quit
Пересчет
Теперь наступило время вызвать e2fsck для пересчета размеров и контрольных сумм.
Я использую e2fsck версии 1.18. Если у вас другая версия, то, возможно, ее параметры или сама работа с программой могли измениться.
Если вы точно знаете, что у вас НЕ было файлов с нулевой длиной, то вы можете сделать следующее: (см. ниже); и пропустить все остальное (Вы, конечно, можете не использовать параметр y, но вам придется вручную отвечать на все вопросы - это может занять длительное время.).
# e2fsck -f -y /dev/hdy1 > e2fsck.out 2>&1
Если же вы хотите восстановить файлы с нулевой длиной, то вам надо ответить n на все вопросы об удалении записей и y на все остальные.
Скопируйте следующие 7 строк в файл "e2fsck-wrapper".
#!/usr/bin/expect -f
set timeout -1
spawn /sbin/e2fsck -f $argv
expect {
"Clear<y>? " { send "n" ; exp_continue }
"<y>? " { send "y" ; exp_continue }
}
Запустите скрипт.
# ./e2fsck-wrapper /dev/hdy1 > e2fsck.out 2>&1
Просмотрите файл "e2fsck.out", чтобы узнать, что сообщил e2fsck о вашем разделе.
Если каталог /lost+found не пуст
Некоторые из ваших каталогов или файлов могут не появиться в обычных местах. Вместо этого они могут появиться в каталоге /lost+found под именами, состоящими из их номеров inode и старых имен.
В этом случае указатель в элементе ".." каталога скорее всего изменился, и указывает на один из последних файлов каталога (я не знаю, почему это происходит - возможно, это ошибка в драйвере файловой системы).
Изучите 3-ю фазу файла "e2fsck.out" (в ней проверяется связность каталогов). Там вы увидите названия каталогов, которые были затронуты e2fsck. Запишите их на диск (как было описано в главе Разд. Находим номера inode удаленных каталогов).
Запустите e2dirana как с флагом p, так и без него (так вы измените указатель на ".."). Здесь и ниже dump - это записанное на диск содержимое каталога.
# ext2fs-directory-analyse dump > dump1
# ext2fs-directory-analyse -p dump > dump2
Сравните результат работы программ
# diff dump1 dump2
Если эти файлы не равны, значит в этом каталоге есть пропавшие файлы. Переместите данные файлы из каталога /lost+found в правильное место. Здесь dest - это симвользная ссылка на каталог-приемник. Поместите результат работы этого мини-скрипта в файл, и запустите его, если там все правильно.
# diff dump1 dump2 |\
tail -n $[`diff dump1 dump2 | wc -l`-1] | cut -b 3- |\
sed -e 's/^\([^ ]*\) \(.*\)$/mv lost+found\/#\1 dest\/"\2"/' |\
sed -e 's/!/"\\\!"/g'
Повторяйте эти действия до тех пор, пока каталог /lost+found не будет пуст.