The OpenNET Project / Index page

[ новости /+++ | форум | wiki | теги | ]

Восстановление ext2/ext3 раздела при помощи debugfs.ext2 (ext3 ext2 fs linux debug)


<< Предыдущая ИНДЕКС Исправить src / Печать Следующая >>
Ключевые слова: ext3, ext2, fs, linux, debug,  (найти похожие документы)
From: darkk <http://darkk.livejournal.com>; Date: Mon, 26 Nov 2007 18:21:07 +0000 (UTC) Subject: Восстановление ext2/ext3 раздела при помощи debugfs.ext2 Оригинал: http://darkk.livejournal.com/28545.html Дано: битый раздел, на котором лежит N месяцев работы. Бэкапов нет. e2fsck теряет волю, ситуацию усугубляет растущее кол-во badblock-ов на винте. livecd storage # e2fsck -n /dev/hdb1 e2fsck 1.38 (30-Jun-2005) Superblock has an invalid ext3 journal (inode 8). Clear? no e2fsck: Illegal inode number while checking ext3 journal for /dev/hdb1 При попытке сказать yes - e2fsck падал по подобию assert(), когда искал root, к орневая директория была куском нулей livecd storage # debugfs -c /dev/hdb1 debugfs 1.38 (30-Jun-2005) /dev/hdb1: catastrophic mode - not reading inode or group bitmaps debugfs: show_super_stats -h ... Inode count: 7340032 Block count: 14659304 ... Узнаем свободные inode и получаем листинг условно-живых директорий livecd storage # for ((i=2; $i <= 7340032; i++)); do echo testi '<'$i'>'; done > testi.in livecd storage # debugfs -f testi.in /dev/hdb1 > testi.out livecd storage # grep 'Inode [0-9]* is marked in use' testi.out | grep -E -o '[ 0-9]+' > used.inodes livecd storage # cat used.inodes | sed 's,.*,ls <&>,' > ls.in livecd storage # debugfs -f ls.in /dev/hdb1 > ls.out livecd storage # bzip2 -c ls.out > ls.out.bz2 Крайне полезно этот ls.out залить в sql-базу (например так), чтобы шустро собирать по нему статистику... У меня размер основной таблицы entries (inode, name, curdir, size) индексированной базы (схема прилагается) получился под 100 мегабайт с 60гигового раздела. Затем можно пополнить базу следующими (НЕ абсолютно верными) наблюдениями: INSERT INTO directories SELECT inode, curdir FROM entries GROUP BY inode HAVING COUNT(*) > 1; INSERT INTO inodes SELECT inode, COUNT(*) FROM entries GROUP BY inode; INSERT INTO broken_dirs SELECT entries.inode FROM entries LEFT JOIN entries AS dirs ON (dirs.inode = entries.inode AND dirs.name = '.') WHERE entries.name = '..' AND dirs.name IS NULL GROUP BY entries.inode; INSERT INTO hardlinks SELECT inodes.inode FROM inodes LEFT JOIN entries ON (inodes.inode = entries.inode AND (entries.name IN ('.', '..'))) WHERE nlink > 1 AND entries.name IS NULL; Еще кстати будет перелить раздел на новый винт, который не сыпется с околозвуковой скоростью, при том в двух экземплярах, один для экспериментов, второй на всякий случай Теперь ищем детей / (inode-2) и возвращаем их на место mysql> select * from entries where inode = 2; +-------+---------+------+------+ | inode | curdir | name | size | # комментарий, изходя из содержания +-------+---------+------+------+ | 2 | 2392065 | .. | 12 | /var | 2 | 6275073 | .. | 12 | /home ... +-------+---------+------+------+ 17 rows in set (0.00 sec) mysql> select * from entries where curdir = 2392065; +---------+---------+---------+------+ | inode | curdir | name | size | +---------+---------+---------+------+ | 2392065 | 2392065 | . | 12 | | 2 | 2392065 | .. | 12 | | 2392066 | 2392065 | lock | 12 | | 2392067 | 2392065 | run | 12 | | 2392068 | 2392065 | backups | 16 | | 2392069 | 2392065 | cache | 16 | ... # да, это явно /var +---------+---------+---------+------+ 15 rows in set (0.03 sec) debugfs: clri <2> debugfs: set_inode_field <2> mode 040755 debugfs: set_inode_field <2> links_count 17 debugfs: find_free_block 1 Free blocks found: 33869 debugfs: set_inode_field <2> bmap[0] 33869 debugfs: set_inode_field <2> blocks 8 debugfs: mkdir <2> debugfs: ls <2> 12 (12) . 2 (12) .. 12 (4072) <2> debugfs: unlink <2>/<2> debugfs: unlink <2>/. debugfs: unlink <2>/.. debugfs: ls 0 (4096) . debugfs: ln <2> <2>/. debugfs: ln <2> <2>/.. debugfs: ls 2 (12) . 2 (4084) .. debugfs: ln <2392065> <2>/var skipped debugfs: ln <6275073> <2>/home debugfs: ^D livecd ~ # e2fsck /dev/hda1 А потом устраиваем большое переименование /lost+found livecd ~ # ls /mnt/slave/lost+found | { echo "lock tables lostnfound write;" ; sed 's,#,,;s,.*,insert into lostnfound values(&);,'; } | mysql ext2backup livecd ~ # { echo "select inode, curdir, name from lostnfound inner join entries using (inode) where name != '.' and name != '..';" | mysql ext2backup | while read inode curdir name; do base="/mnt/slave/smth-found"; echo "mkdir -p \"$base/#$curdir\"; mv \"/mnt/slave/lost+found/#${inode}\" \"$base/#$curdir/$name\""; done; } | sed 1d > renamer.sh livecd ~ # { echo "select lostnfound.inode as lostinode, if(parent.name <=> null, concat('#', entries.inode), parent.name) from lostnfound inner join entries on (entries.name = '..' and curdir = lostnfound.inode) left join entries as parent on (parent.name != '.' and parent.name != '..' and parent.inode = entries.inode);" | mysql ext2backup | while read inode curdir; do base="/mnt/slave/smth-found"; echo "mkdir -p \"$base/$curdir\"; mv \"/mnt/slave/lost+found/#${inode}\" \"$base/$curdir/#$inode\""; done; } | sed 1d > renamer.sh После чего почти все в почти читаемом виде с ФС почти восстановлено (в lost+found у меня осталось после данной махинации процентов 15% от первоначального объема).
debugfs-ls-scan.pl #!/usr/bin/perl use DBI; use strict; my $db = DBI->connect("dbi:mysql:database=ext2backup", "root", "") or die $DBI::errstr; my $inserter = $db->prepare( "INSERT INTO `entries`(`inode`,`curdir`,`name`, `size`) VALUES (?,?,?,?)"); my $counter = 0; my $curdir = undef; while (<>) { my @chunks = split / /; foreach my $chunk (@chunks) { $chunk =~ s/ +$//; if ($chunk =~ /^ ?([0-9]+) \(([0-9]+)\) (.*)$/) { my ($inode, $size, $name) = ($1, $2, $3); if ($name eq ".") { $curdir = $inode; } # print "($inode, $size, $name) at $curdir\n"; $inserter->execute($inode, $curdir, $name, $size) or die $DBI::errstr; } else { die "Invalid chunk: $chunk" } } $counter++; if (($counter % 100) == 0) { print "$counter lines...\n"; } } # vim:set tabstop=4 softtabstop=4 shiftwidth=4: # vim:set foldmethod=marker foldlevel=32 foldmarker={,}:
debugfs-mysql-schema.sql -- MySQL dump 10.10 -- -- Host: localhost Database: ext2backup -- ------------------------------------------------------ -- Server version 5.0.22-Debian_0ubuntu6.06.2-log -- -- Table structure for table `broken_dirs` -- DROP TABLE IF EXISTS `broken_dirs`; CREATE TABLE `broken_dirs` ( `inode` int(11) NOT NULL, PRIMARY KEY (`inode`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1; -- -- Table structure for table `entries` -- DROP TABLE IF EXISTS `entries`; CREATE TABLE `entries` ( `inode` int(11) NOT NULL, `curdir` int(11) NOT NULL, `name` varchar(255) character set utf8 collate utf8_unicode_ci NOT NULL, `size` int(11) NOT NULL, KEY `inode` (`inode`), KEY `curdir` (`curdir`), KEY `name` (`name`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1; -- -- Table structure for table `hardlinks` -- DROP TABLE IF EXISTS `hardlinks`; CREATE TABLE `hardlinks` ( `inode` int(11) NOT NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1; -- -- Table structure for table `inodes` -- DROP TABLE IF EXISTS `inodes`; CREATE TABLE `inodes` ( `inode` int(11) NOT NULL, `nlink` int(11) NOT NULL, PRIMARY KEY (`inode`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1; -- -- Table structure for table `lostnfound` -- DROP TABLE IF EXISTS `lostnfound`; CREATE TABLE `lostnfound` ( `inode` int(11) default NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1;

<< Предыдущая ИНДЕКС Исправить src / Печать Следующая >>

 Добавить комментарий
Имя:
E-Mail:
Заголовок:
Текст:




Партнёры:
PostgresPro
Inferno Solutions
Hosting by Hoster.ru
Хостинг:

Закладки на сайте
Проследить за страницей
Created 1996-2024 by Maxim Chirkov
Добавить, Поддержать, Вебмастеру