четверг, 5 марта 2009 г.

Восстановление файла с поврежденного ext3 раздела

Восстановление файла с поврежденного ext3 раздела

Представьте себе ситуацию: файловая система повреждена, раздел не монтируется, вместо корневого каталога нули.

# mount /dev/md1 /mnt
mount: wrong fs type, bad option, bad superblock on /dev/md1,
missing codepage or other error
In some cases useful info is found in syslog - try
dmesg | tail or so

Хорошо, что есть бэкап. Вы смотрите как дела обстоят с бэкапом, и тут вдруг обнаруживаете, что в бэкапе нет одного очень нужного файла. И если можно где-то найти этот файл - то только в недрах погибшего раздела. Оказавшись в подобной ситуации я сразу пожалел, о том что, ранее не интересовался внутренним устройством ext3fs. Что я собственно знаю о ней? Да похоже ничего, иноды там какие-то…


Но делать что-то надо. Детально разбираться времени уже нет, так что погуглив на тему ext3fs, выясняю следующее:

Основные составляющие ext3fs:

  • каталоги
  • указатели inode
  • блоки данных

И есть еще суперблок, в котором хранится важнейшая информация о ФС, такая как, размер блока, количество инодов, и многое другое.

Самое важное, что хранит в себе inode:

  • размер файла
  • id владельца
  • id группы
  • даты модификации/доступа
  • список блоков данных, где собственно и хранится файл

Жаль имени файла нет, было бы удобнее, но почему нет - понятно, имен-то (hardlink-ов) может быть много. А хранятся имена, естественно, в каталогах. Каталог по сути тот же inode, только другого типа, в нем есть та же самая информация о владельце и датах, а главное, в блоках данных хранится список имен файлов и каталогов с номерами соотвествующих inode.

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

Это несколько упрощенный взгляд на ext3fs, но сейчас не до деталей и подробностей, файл нужно доставать.

Главный инструмент который нам поможет - debugfs.

debugfs -c /dev/md1
debugfs 1.39 (29-May-2006)
/dev/md1: catastrophic mode - not reading inode or group bitmaps

Посмотрим что там в суперблоке:

debugfs: stats
Filesystem volume name:
Last mounted on:
Filesystem UUID: 24c5e529-02c4-4775-bb9f-e0c1f1b4a676
Filesystem magic number: 0xEF53
Filesystem revision #: 1 (dynamic)
Filesystem features: resize_inode dir_index filetype sparse_super large_file
Default mount options: user_xattr acl
Filesystem state: not clean with errors
Errors behavior: Continue
Filesystem OS type: Linux
Inode count: 121831424
Block count: 121806816
Reserved block count: 6090340
Free blocks: 117940889
Free inodes: 121831413
First block: 0
Block size: 4096

В данном случае, размер блока 4096, кол-во инодов - 121831424

Теперь вполне можно было бы проитерировать все иноды, найти все каталоги, и в каком-то из них и будет ссылка на искомый файл.

Создадим файл stat.cmd с содержимым вида:

stat <1>
stat <2>
...
stat <кол-во inode>

Выполним:

# debugfs -f stat.cmd -c /dev/md1 | grep 'Type: dir'

Получим список каталогов:

...
Inode: 32957 Type: directory Mode: 0755 Flags: 0x0 Generation: 1325685135
Inode: 32987 Type: directory Mode: 0755 Flags: 0x0 Generation: 1325685176
Inode: 33025 Type: directory Mode: 0755 Flags: 0x0 Generation: 1325685227
...

Теперь можем посмотреть содержимое каталога:

# debugfs -f stat.cmd -c /dev/md1
debugfs: ls <32957>
32957 (12) . 2590762 (44) .. 32956 (24) china-util.elc
32958 (20) chinese.elc 32959 (24) cyril-util.elc
...

Видим список файлов и что очень важно, видим inode родительского каталога - 2590762, т.е. можем просмотреть его содержимое:

debugfs: ls <2590762>
2590762 (12) . 2590642 (4084) .. 2590760 (20) abbrev.elc
2590765 (20) align.elc 2590771 (20) autoarg.elc
...

Т.о. фактически мы можем перемещатся по структуре каталогов, лишь бы ветвь директорий в которой лежит искомый файл не была повреждена. Это уже дает неплохие шансы найти файл.

Но к сожалению, не все так просто когда структура каталогов повреждена, да и посмотрите на кол-во inode в данном случае, поиск всех директорий для 500Gb диска времени займет слишком много.

Другой путь - найти блок данных принадлежащий искомому файлу, с помощью debugfs узнать номер inode, и если все сложилось удачно, вытащить содержимое файла.

Например, нам удалось выяснить что блок 94844 принадлежит искомому файлу, спросим у debugfs номер inode c помошью команды icheck:

debugfs: icheck 94844
Block Inode number
94844 69267

Подумав некоторое время debugfs сказал что inode - 69267. Запросим информацию об этом inode:

debugfs: stat <69267>
Inode: 69267 Type: regular Mode: 0444 Flags: 0x0 Generation: 2933723684
User: 0 Group: 0 Size: 15214
File ACL: 0 Directory ACL: 0
Links: 1 Blockcount: 32
Fragment: Address: 0 Number: 0 Size: 0
ctime: 0x47f3754b -- Wed Apr 2 16:00:11 2008
atime: 0x48002a85 -- Sat Apr 12 07:20:37 2008
mtime: 0x47f3754b -- Wed Apr 2 16:00:11 2008
BLOCKS:
(0-3):94844-94847
TOTAL: 4

Скорее всего по владельцу, группе и размеру уже станет ясно, искомый файл это или нет. Теперь попросим debugfs вытащить содержимое файла, с помощю команды dump:

debugfs: dump <69267> /tmp/69267.file

Если все сложилось удачно, в /tmp/69267.file лежит то, что мы искали.

Остается открытым вопрос, как найти блок данных, принадлежащий файлу. Естественно, придется искать по какой-то подстроке, которую как мы предполагаем, содержит файл.

Первое что приходит в голову - воспользоватся hexdump и grep, например вот так:

cat /dev/sda1 | hexdump -C | grep Linux
...
001fc830 64 69 74 0a 23 20 4c 69 6e 75 78 20 6b 65 72 6e |dit.# Linux kern|
00205470 20 49 53 44 4e 34 4c 69 6e 75 78 0a 23 0a 43 4f | ISDN4Linux.#.CO|
002092d0 63 65 64 20 4c 69 6e 75 78 20 53 6f 75 6e 64 20 |ced Linux Sound |
...

Если нам повезет и искомая подстрока будет внтури 16-байтного блока, то вполне может быть и найдем. Но скорость поиска очень низкая, годится только для совсем небольших разделов. В моем же случае для поиска пришлось набросать небольшую програмку, которую я обозвал bfind.

Работает она примерно вот так:

cat /dev/sda1 | ./bfind Linux
...
5214 +300 20 2e 63 6f 6d 70 2e 73 6f 66 74 2e 6c 69 6e 75 | .comp.soft.linu|
5214 +376 6d 70 2e 73 6f 66 74 2e 6c 69 6e 75 78 2e 6e 69 |mp.soft.linux.ni|
5214 +423 70 2e 73 6f 66 74 2e 6c 69 6e 75 78 2e 6e 69 78 |p.soft.linux.nix|
...

И главное, работает быстро. Первый столбец - это и есть номер 4k-блока данных.
Если понадобится уточнить содержимое найденных блоков, можно воспользоваться утилитой fsgrab.

fsgrab -b 4096 -c 4 -s 105633828 -f /dev/md1 > /tmp/105633828.block

В данном случае 4 блока по 4096 байта, начиная с 105633828-го будут сохранены в указанный файл.

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

Комментариев нет: