前回からの続き。
とりあえず、結論から言うと、データの救出は可能でした。 画像ファイルもほとんどすべて戻ってきました。 よかった!
てなわけで、一応最初からまとめた状態で今回のトラブルをまとめてみましょう。 FreeBSD におけるディスククラッシュからの救出のサンプルにはなるでしょう。 また、fsck する部分以外は他の OS でのクラッシュ時も応用がききそうです。 Windows のクラッシュしたディスクも、FreeBSD マシンに接続して、 この方法である程度復旧できる可能性もあるんじゃないかな?
1 年と少し後になりましたが、 このノウハウの応用で BIOS からさえ認識されなくなったディスクから、 Windows XP の NTFS パーティションを救出しました。 詳しくはこちらのページをご覧下さい (2004/03/14 追記)
さらにこの辺によると、最近は dd(1) の ibs と obs を使い分けると速くなる話や、 -current の /usr/src/tools/recoverdisk や ports に dd_rescue (ports/sysutils/dd_rescue) などがあるようで、いろいろ便利になっているようです。 (2005/04/13 追記)
FreeBSD を利用していたメインマシンの ThinkPad X21 で、 突然 Wnn7 で漢字変換ができなくなる (おそらく辞書が読めなくなった)。 そんなこともつゆ知らずに再起動してみると、起動時に異音がして、 しばらくためらった挙げ句に起動してくる状況 (そもそも再起動したことが間違いだが、 ここまでひどいことになるとはそのときは思わなかった)。
ディスクが極めて危険な状態にあることは明らか、ということで、 とりあえず重要なメール、ドキュメントから ssh+tar でバックアップを開始。 途中まで取ったところで案の定システムクラッシュ。 もう一度起動するのは fsck が動いてなお傷口を広げそうなため、 再起動を断念。
結局完全にバックアップが取れたのは、以下のものだけ。 これだけ完全に取れただけでもラッキーだった。
この他にも、3 週間前に胸騒ぎがして、 取ったバックアップがあった。 これはメールすべて、ドキュメントファイルすべて、ソースファイルすべて、 画像ファイルすべてを取ったつもりであったが、 ミスで画像ファイルは昨年 4 月 29 日までのものしか取れていなかった (教訓: バックアップは取れたかどうかを確認しなければならない)。
このため、ここ 1 年半分のこの日記の元となった、 オリジナルサイズの画像ファイルが、その当時での推定で 10,000 枚弱、失われた (データ復活後に正確な数を計算したところ、画像ファイル約 8,600 枚、 動画約 400 本が失われた計算になる)。 ただし今年 10 月中旬以降と、8 月のデジカメ画像の一部は、 たまたま消していなかったデジカメのコンパクトフラッシュと、 旅先の ADSL 回線から取った某所のバックアップからリストア可能な状態だった。
新しい 60GB の 2.5 インチディスクを購入し、FreeBSD 4.7-RELEASE をインストール。 一応の環境設定を行う。
この作業と並行して、 別のマシンにクラッシュしたディスクをつなぎ、 リードオンリーモードでの強制マウントを試みるものの、 何と次のように表示され失敗!
# mount -f -o ro /dev/ad2s1e /mnt
ad2s1e: hard error reading fsbn 3145823 of 16-31 (ad2s1 bn 3145823; cn 195 tn 208 sn 44) trying PIO mode
ad2s1e: hard error reading fsbn 3145823 of 16-31 (ad2s1 bn 3145823; cn 195 tn 208 sn 44) status=59 error=40
mount: /dev/ads1e: Input/output error
絶望! とりあえず即座に救う手だてはない。 しかし、冷静に考えてほとんどのデータは残っているはずなので、 このディスクを捨てるにはまだ早い。 あきらめずに次の作業に移る。 明日からこのマシンが使えないと仕事に支障を来たすので、 とりあえず取れているバックアップから使えるレベルに持っていくことを考えるべき。 Web に現状報告するとともに、助けを乞う (^^;。
この判断は結果的に、いろいろな意味で正しかったと言える (ここでは、いい意味での教訓: (1)データが存在する限り、簡単にあきらめてはいけない、(2)完璧でなくても、必要な時間までに最良のものを用意できれば良い、そして一番重要なことは、(3)本当に困った時に他人に聞くことは恥ではない)。
とりあえずこの日は、環境構築と、 現存する限りのバックアップからのリストアに全力をつくす。
なんと、~/.ssh/ が失われたために、 ssh でログインできなくなってしまった重要なホストが多数存在することが判明 (教訓: 重要なデータと思ってバックアップを取っても、必ず見逃しはある。フルバックアップを取るべし)。
~/.ssh/ は、ゴミになりかけていたディスク換装前のディスクから発掘され、 なんとか各方面へのログインが再び可能に。
ありがたいことに、アドバイスのメールが! dd(1) の conv=sync,noerror オプションを使うと、 読み込みエラー発生時に、bs 単位で 0 でパディングし、 読み込みを継続してくれるとのこと!
参考: FreeBSD 4.7-RELEASE の man dd より
sync Pad every input block to the input buffer size. Spaces are used for pad bytes if a block oriented conversion value is specified, otherwise NUL bytes are used. noerror Do not stop processing on an input error. When an input error occurs, a diagnostic message followed by the current input and output block counts will be writ- ten to the standard error output in the same format as the standard completion message. If the sync conver- sion is also specified, any missing input data will be replaced with NUL bytes (or with spaces if a block ori- ented conversion value was specified) and processed as a normal input buffer. If the sync conversion is not specified, the input block is omitted from the output. On input files which are not tapes or pipes, the file offset will be positioned past the block in which the error occurred using lseek(2).
すげえ! こりゃ最高! 早速やってみよう。 count つけてね、とメールには書いてあったので (確かに止まらなくなる危険性がありそうなので、 count をつけろというのはもっともだ)、 念のために bs=512 と明示的にデフォルト値を指定して、 起動時の dmesg メッセージ、
ad2: 38154MB[77520/16/63] at ata1-master UDMA66
から、シリンダ×ヘッド×セクタの計算で、 77520×16×63 = 78140160 を算出し、 count=78140160 オプションをつけてみることにした (78140160×512/1024/1024 = 38154MB で、計算は合っている)。
# dd if=/dev/ad2 of=broken.img bs=512 count=78140160 conv=sync,noerror
ad2: hard error reading fsbn 360559 (ad2 bn 360559; cn 22 tn 113 sn 10) trying PIO mode
ad2: hard error reading fsbn 360559 (ad2 bn 360559; cn 22 tn 113 sn 10) status=59 error=40
ad2: hard error reading fsbn 360559 (ad2 bn 360559; cn 22 tn 113 sn 10) status=59 error=40
ad2: hard error reading fsbn 360560 (ad2 bn 360560; cn 22 tn 113 sn 11) status=59 error=40
...(続く)...
この時のエラーログ完全版をここに示す。 このログからわかることは、読み取れないセクタの数が現在 685 個あること。 そしてそれらが、いくつかの LBA 上の集団として偏在しているということだ (セクタ番号 360559〜360574、360719〜365388、366995〜366996、 371023〜371030、721007〜721038、721071〜721086、814639〜814648、 3145807〜3145810、3148591〜3148618、50251748、56492143〜56492158、56492991〜56493006、 56493055〜56493070、56493119〜56493134、56497501〜56499374、56501117〜56501118、 56502205〜56502206、56502541〜56502542、56541739〜56541742、 56541787〜56541790、56544735〜56544740、56762479〜56762494、 56848719〜56848798、57122927〜57122942、57147791〜57147822、 57148751〜57148758、57149119〜57149132、57199855〜57199934、 57200453〜57200454、57393263〜57393278、57422639〜57422686、 57423173〜57423182、57843823〜57843838、57869135〜57869246、 57870821〜57870830)。
後半になるほど偏在性が顕著である。 ほとんどのエラーがセクタ番号 56000000〜58000000 以降 (約26GB〜27GB地点) に集中しており、 特に 57869135〜57869246 セクタでは何と 112 セクタ連続で死んでいる。 ゾーニングの影響もあるのだろう。
一方 22 日に mount に挑戦したときにエラーが出たセクタ 3145823 は、 ほぼ 1.5GB 地点に位置しており、 ルート 1GB、スワップ 512MB の後に作られたパーティション ad?s1e のスーパーブロックやルートディレクトリが位置する可能性のある場所である。 2 パーティション構成で、ほとんどのファイルがこの ad?s1e にあったため、 ここのルートディレクトリ近辺が修復不能なほど死んでいたら本当に絶望だ。
作業用に 120GB のハードディスクを購入。 vn(4) を使って、吸い出したイメージのマウントを試みる。 このディスクイメージは、サイズ 40GB のファイルだが、 必ずファイルをコピーしてから作業を行う (作業用ディスクとして、 40GB の 2 倍以上のディスクを購入したのはそのため)。
で、やってみたもののずいぶんうまくない。 なぜか、mount には問題ない vn0s1a ですら、fsck(8) できない。 vnconfig(8) にはきちんと -s labels をつけているのだが。
# vnconfig -s labels -c vn0 /ext/dd/broken.img # mount -f -o ro /dev/rvn0s1a /mnt # df Filesystem 1K-blocks Used Avail Capacity Mounted on /dev/ad0s1a 128990 49594 69078 42% / /dev/ad0s1f 257998 6634 230726 3% /tmp /dev/ad0s1g 58230740 43091008 10481274 80% /usr /dev/ad0s1e 257998 13084 224276 6% /var procfs 4 4 0 100% /proc /dev/ad1s1e 118738616 78159366 31080162 72% /ext /dev/rvn0s1a 1016303 599891 335108 64% /mnt # umount /mnt # mount -f -o ro /dev/rvn0s1e /mnt mount: /dev/rvn0s1e on /mnt: incorrect super block # fsck -n /dev/rvn0s1a ** /dev/rvn0s1a (NO WRITE) BAD SUPER BLOCK: VALUES IN SUPER BLOCK DISAGREE WITH THOSE IN FIRST ALTERNATE /dev/rvn0s1a: INCOMPLETE LABEL: type 4.2BSD fsize 0, frag 0, cpg 0, size 2097152
参考: FreeBSD 4.7-RELEASE の man vnconfig より
-r flag Reset flag. The list of allowed flags and their meanings are: labels use disk/slice labels. ...(略)... -s flag Set flag. The list of allowed flags and their meanings are the same as for the -r option.
とりあえず、vn(4) に関する追求は後でやることにして、 せっかく 120GB のディスクがあるのだから、 ここにイメージを dd(1) してみよう。
さて、ここで謎を一つ。 壊れたディスクの dd(1) を取ることでハードエラーは悪化するのか?
ということでヒトバシラー精神に則り、 もう一度全体の dd(1) イメージをとって、エラーが増えるかどうかを実験してみた。 結論から言うと、エラーセクタ数は 685 で変化なし。 一度壊れてしまえば、 全体を dd で舐めたところでエラーセクタ数が増えない場合もあるということだ。
てなわけで、イメージを実際のディスクに貼ってみることに。 40GB ディスクのイメージを 120GB に貼って大丈夫かというと…、 fdisk パーティションテーブルさえしっかり 40GB のまま貼ってしまえば大丈夫なのである。 BIOS やほとんどの OS は見事にだまされてくれる。 まあ、少なくとも古い Windows の fdisk.exe とかは、 偽のジオメトリの書かれたディスクにいつまでもだまされてくれて、 120GB のディスクが 40GB としてしか認識がされないという風に困ってしまうのだが、 その場合は
# dd if=/dev/zero bs=65536 count=1 of=/dev/ad2
のようにして、 fdisk パーティションテーブルをすっ飛ばしてしまえばいいのだ (乱暴だな)。dd(1)、最強! (ちなみに、本当に修復不能なほどディスクの内容が吹っ飛ぶので、 何をやっているかわからない人は絶対にまねしちゃダメ!)
ということで、とりあえずやってみよう。
# dd if=broken.img of=/dev/rad2
これだと、adc1 に 5000 回/秒もの割り込みが入ってしまい、 遅くて話にならない (2.5MB/s 程度)。 この調子だと 4, 5 時間くらい貼り終わるのにかかりそう。 そういえば、noerror,sync で dd した場合は、 エラーをゼロパディングする単位が bs だったので、 標準の bs=512 でやる価値があったものの、 今回はこんなのんびりやることもないので、 bs=65536 にしてみる。
# dd if=broken.img of=/dev/rad2 bs=65536
すると 18MB/s 程度に性能が向上し、 約 35 分で 40GB の書き込みが終了した。
ここまでが前回で書いた分。 これからはそれに引き続きやったことの記録だ。 さらに続くぞ。
さて、正常に書き込めているかどうか disklabel(8) で確認してみよう。
# disklabel ad2s1
# /dev/ad2s1c:
type: ESDI
disk: ad0s1
label:
flags:
bytes/sector: 512
sectors/track: 63
tracks/cylinder: 255
sectors/cylinder: 16065
cylinders: 4863
sectors/unit: 78140097
rpm: 3600
interleave: 1
trackskew: 0
cylinderskew: 0
headswitch: 0 # milliseconds
track-to-track seek: 0 # milliseconds
drivedata: 0
8 partitions:
# size offset fstype [fsize bsize bps/cpg]
a: 2097152 0 4.2BSD 0 0 0 # (Cyl. 0 - 130*)
b: 1048576 2097152 swap # (Cyl. 130*- 195*)
c: 78140097 0 unused 0 0 # (Cyl. 0 - 4863*)
e: 74994369 3145728 4.2BSD 0 0 0 # (Cyl. 195*- 4863*)
グレイト! いいぞ! じゃ、リードオンリーモード (-n) で fsck(8) してみようじゃないか。
# fsck -n /dev/rad2s1a ** /dev/rad2s1a (NO WRITE) BAD SUPER BLOCK: VALUES IN SUPER BLOCK DISAGREE WITH THOSE IN FIRST ALTERNATE /dev/rad2s1a: INCOMPLETE LABEL: type 4.2BSD fsize 0, frag 0, cpg 0, size 2097152 # fsck -n /dev/rad2s1e ** /dev/rad2s1e (NO WRITE) BAD SUPER BLOCK: MAGIC NUMBER WRONG /dev/rad2s1e: INCOMPLETE LABEL: type 4.2BSD fsize 0, frag 0, cpg 0, size 74994369
あべし! スーパーブロックが壊れているとな。 こういう場合はどうするんだったじゃろ、 と、google してみると、fsck の -b オプションを使うと良いらしい。 ふむ man fsck すると…なるほど。
-b Use the block specified immediately after the flag as the super block for the filesystem. Block 32 is usually an alternate super block.
あれだな、インストール時などに newfs(8) するとき、 「(for fsck -b)」とかいって現れるあの数字群。 あの数字のどれを突っ込んでもいいように見えるが、 そんなものいくらなんでもメモしてないよ。 とりあえず「通常 32 番セクタは代替スーパーブロックである」と読めるので、 32 でやってみようじゃないか。
# fsck -b 32 -n /dev/rad2s1e Alternate super block location: 32 ** /dev/rad2s1e (NO WRITE) ** Last Mounted on ** Phase 1 - Check Blocks and Sizes ** Phase 2 - Check Pathnames ^C#
オーケー、大丈夫そうなので ^C で止めた。 今度は対話モードでやってみよう。
# fsck -b 32 /dev/rad2s1e Alternate super block location: 32 ** /dev/rad2s1e ** Last Mounted on ** Phase 1 - Check Blocks and Sizes ** Phase 2 - Check Pathnames UNALLOCATED I=6482189 OWNER=root MODE=0 SIZE=0 MTIME=Jan 1 09:00 1970 NAME=/home/hosokawa/Mail/HPCMIPS/4101 REMOVE? [yn] y UNALLOCATED I=6482193 OWNER=root MODE=0 SIZE=0 MTIME=Jan 1 09:00 1970 NAME=/home/hosokawa/Mail/ITC/37174 REMOVE? [yn] y ...(延々続く)... DIRECTORY /home/hosokawa/Mail/ITC: CONTAINS EMPTY BLOCKS ADJUST LENGTH? [yn] y YOU MUST RERUN FSCK AFTERWARDS UNALLOCATED I=6482465 OWNER=root MODE=0 SIZE=0 MTIME=Jan 1 09:00 1970 NAME=?/hosokawa/.wl-thread-entity REMOVE? [yn] y UNALLOCATED I=6482466 OWNER=root MODE=0 SIZE=0 MTIME=Jan 1 09:00 1970 NAME=?/hosokawa/.wl-thread-entity-list REMOVE? [yn] y ...(さらに延々続く)... REMOVE? [yn] ^C#
うーん、「YOU MUST RERUN FSCK AFTERWARDS」なんておそろしげなメッセージが出ているね。 あまりに REMOVE ばっかり多いので、fsck を -y しちゃえ。えいっ。
# fsck -y -b 32 /dev/rad2s1e
Alternate super block location: 32
** /dev/rad2s1e
** Last Mounted on
** Phase 1 - Check Blocks and Sizes
PARTIALLY TRUNCATED INODE I=6446663
SALVAGE? yes
INCORRECT BLOCK COUNT I=6446663 (720 should be 672)
CORRECT? yes
PARTIALLY TRUNCATED INODE I=6470823
SALVAGE? yes
INCORRECT BLOCK COUNT I=6470823 (560 should be 528)
CORRECT? yes
** Phase 2 - Check Pathnames
** Phase 3 - Check Connectivity
** Phase 4 - Check Reference Counts
UNREF FILE I=6446652 OWNER=hosokawa MODE=100644
SIZE=136425 MTIME=Oct 2 11:33 2002
RECONNECT? yes
UNREF FILE I=6446653 OWNER=hosokawa MODE=100644
SIZE=133629 MTIME=Oct 2 11:33 2002
RECONNECT? yes
...(延々続く)...
** Phase 5 - Check Cyl groups
FREE BLK COUNT(S) WRONG IN SUPERBLK
SALVAGE? yes
SUMMARY INFORMATION BAD
SALVAGE? yes
BLK(S) MISSING IN BIT MAPS
SALVAGE? yes
CG 592: BAD MAGIC NUMBER
CG 595: BAD MAGIC NUMBER
CG 599: BAD MAGIC NUMBER
CG 602: BAD MAGIC NUMBER
CG 607: BAD MAGIC NUMBER
926413 files, 30296425 used, 6047857 free (223337 frags, 728065 blocks, 0.6% fra
gmentation)
UPDATE STANDARD SUPERBLOCK? yes
***** FILE SYSTEM MARKED CLEAN *****
***** FILE SYSTEM WAS MODIFIED *****
よし! やったぞ。「UPDATE STANDARD SUPERBLOCK?」なんてやってくれたようだし、 これでやっと ad2s1e を mount できるかな?
# mount -o ro /dev/ad2s1e /mnt # df Filesystem 1K-blocks Used Avail Capacity Mounted on /dev/ad0s1a 128990 50046 68626 42% / /dev/ad0s1f 257998 6634 230726 3% /tmp /dev/ad0s1g 58230740 43091056 10481226 80% /usr /dev/ad0s1e 257998 13014 224346 5% /var procfs 4 4 0 100% /proc /dev/ad2s1e 36344282 30296425 3140315 91% /mnt # cd /mnt # ls X11R6 games libdata obj src bin home libexec ports src.cvs compat include local sbin tmp doc lib lost+found share vmware
キターッ! ついにマウントできました。 残骸置き場の /mnt/lost+found には、ファイル 4103 個が入っていましたが、 うち自動的に再生成可能な (いわばキャッシュ) ~/.elmo/ の下にあったと思われるファイルが半分以上、 これも再生成可能な .xvpics/ のアイコンが数十個、 すでにバックアップのあったメールが数百個……などと、 実質的な被害はかなり小さく抑えられました。
ということで、最大の教訓: そこにデータがある限り、簡単にあきらめてはいけない。 うーん、重要だ。