HDDが壊れかけたときのデータ救出法です。
■経緯
・録画サーバの一次保存HDD(Seageteの1TB玉)にあるデレマスを見てたら突然readが不安定になった。dmesgとかSMARTとか見たらread errorが多発してた。
・老害なのでddで吸い出したら494GBで引っかかる。「conv=sync,noerror」を付けても1時間で3MBしか進まないという素敵な状況。
・イマドキの時代、read error出たらサクッと諦めて次のセクタに進む吸い出しソフトないの?
■そんなわけで
老害だから知らなかった「ddrescue」なるソフト。だって死にかけのHDDを吸い出すのって多分十数年ぶりだぜ。
世の中には「GNU ddrescue」と「dd_rescue」の2種類があるそうですが、ここで扱うのは「GNU ddrescue」です。
■つかいかた
普通にconfigure→make→make installで入れる。
その後はこちらのサイト参照。
「んじゃなんでお前ブログ書いてるんだよ」と仰るのはごもっともなんですが。
この貴重な資料、Geocitiesに置いて放置されてるんですよね…Geocitiesの将来を考えると「書いておいた方がいい」と思った次第。
■GNU ddrescueのここがスゴイ
・read errorが発生したらガン無視スキップしてくれて(吐き出し先はゼロパディングらしい)、不良セクタの位置がロギングされる
・一旦全部書き出した後、ログに残った不良セクタだけを決め打ちでリトライできる
・もはや人類には不可能な「どのファイルが不良セクタの被害を受けたか」を調べられる
どれをとっても非常にありがたい機能です。
■実作業
以下の条件で行いました。
・CentOS6
・壊れかけのHDD: /dev/sdb (sdb1がext4)
(NTFSな人は吸い出した後で各自工夫してください)
・直接別のHDDにdd的に吐けるけど、今回はファイルサーバにファイル(/mnt/fs/back.img)として吐き出す
・ddrescueのログは/mnt/fs/ddrescue.logに吐き出す
・吐きだしたイメージ(/mnt/fs/back.img)は、うまくいったら/mnt/imageにmountする
基本書式は
ddrescue [オプション] [読込元] [書き出し先] [ログ出力先]
です。書き出し先はファイルでも直デバイスでもOK。
直デバイスに書き出す人はHDDの指定を間違えないよう気をつけてください。
まず初回はread errorを無視するモード(-n)で読みます。-vは進捗詳細表示。
パーティションはmountさせてません。
------
# ddrescue -n -v /dev/sdb /mnt/fs/back.img /mnt/fs/ddrescue.log
------
進捗がこんな感じで表示されて、終わったらこんな感じ。
1TBで1.65日かかった。エラーは意外と少ない。100MB切ってる。
------
rescued: 1000 GB, errsize: 92286 kB, current rate: 0 B/s
ipos: 999370 MB, errors: 819, average rate: 7011 kB/s
opos: 999370 MB, run time: 1.65 d, successful read: 2 s ago
Finished
------
次にエラー箇所だけ読みます。
-dはダイレクトアクセス、-fはoverwrite、-r3は「3回リトライ」で増やしたい人は増やしてください(でも増やしすぎると死にかけのHDDに負荷がかかる)
------
# ddrescue -d -f -r3 -v /dev/sdb /mnt/fs/back.img /mnt/fs/ddrescue.log
------
結果です。
errorsの数が変わってないので、ダメなモノはダメだった模様。
------
Current status
rescued: 1000 GB, errsize: 92286 kB, current rate: 0 B/s
ipos: 999370 MB, errors: 819, average rate: 0 B/s
opos: 999370 MB, run time: 1.93 m, successful read: 1.93 m ago
Finished
------
では保存したファイル(/mnt/fs/back.img)を/mnt/imageにmountしてみましょう。
------
# mount /mnt/fs/back.img /mnt/image
mount: you must specify the filesystem type
------
loopbackじゃないとダメな模様。あれ?
------
# mount -o loop /mnt/fs/back.img /mnt/image
mount: you must specify the filesystem type
------
先頭セクタの位置がなんかズレてるようです。
fdiskないしpartedで該当パーティションの開始位置(セクタ)を確認します。
------
・fdiskの場合
# fdisk -lu /mnt/fs/back.img
(中略)
Device Boot Start End Blocks Id System
/mnt/fs/back.img 1 63 1953520064 976760001 83 Linux
Partition 1 has different physical/logical endings:
phys=(1023, 254, 63) logical=(121600, 254, 63)
・partedの場合
# parted /mnt/fs/back.img
(parted) unit s
(parted) p
モデル: (file)
ディスク /mnt/fs/back.img
セクタサイズ (論理/物理): 512B/512B
パーティションテーブル: gpt
番号 開始 終了 サイズ ファイルシステム 名前 フラグ
1 63s 953520064s 1953520001s ext4 zfs
------
というわけでmountするのにセクタの先頭位置のオフセットが必要です。
オフセット量は「Startの値 * 512」(この場合は63 * 512 = 32256)です。
------
# mount -o loop,offset=32256 /mnt/fs/back.img /mnt/image/
------
mountできたら/mnt/image内のファイルを新しいHDDにコピーしちゃってください。
これでデータ救出は完了です。
コピーが終わったら、このイメージを使って「どのファイルに不良セクタが当たってたか」を調べます。
やってることは以下の感じです。
・不良セクタとしてロギングされた位置に、任意の文字列を書き込む
・findとgrepを使って、ひたすらファイル内を文字列検索する
力業だなあ…
というわけでデータを書き換えるんですが、元に戻せます(後述)
要はgrepなので置き換え文字列は「元データとかぶらない文字列」にする必要がありますが、参考にしたサイトは文字列を「DEADBEEF」にしてました。
俗に言うHexspeak(16進数で単語を作る)ってやつです。
んじゃうちは「FACEFEED」にします(AlphaのWindows触ってみたかったなあ)
この文字列は一旦ファイル(/mnt/fs/tmpfile)に入れます。
あとイメージをmountしたままなので、一旦umountします。
------
printf "FACEFEED" > /mnt/fs/tmpfile
umount /mnt/image
------
不良セクタを文字列(FACEFEED)で置き換えます。
------
ddrescue --fill=- /mnt/fs/tmpfile /mnt/image/back.img /mnt/image/rescue.log
------
6秒であっという間に終わります。100MBないし。
------
filled size: 92286 kB, filled areas: 819, current rate: 5050 kB/s
remain size: 0 B, remain areas: 0, average rate: 15357 kB/s
current pos: 999370 MB, run time: 6 s
Finished
------
終わったらtmpfileを待避させ(させる理由は分からんが必要なくね?)、もう一回イメージをmountして、ひたすらgrepします。teeで出力をファイルとコンソールの両方に向けるとよいでしょう。
------
# mv /mnt/fs/tmpfile /var/tmp
# mount -o loop,offset=32256 /mnt/fs/back.img /mnt/image/
# find /mnt/image -type f -exec grep "FACEFEED" '{}' ';' | tee /mnt/fs/find_broken_file.log
------
出力はこんな感じです。この子たちが被害を受けました。
------
バイナリー・ファイル/mnt/image/3542-21-20150308-1700-22.m2tは一致しました
バイナリー・ファイル/mnt/image/3551-21-20150305-0229-25.m2tは一致しました
バイナリー・ファイル/mnt/image/3615-10-20150315-0030-161.m2tは一致しました
バイナリー・ファイル/mnt/image/3597-7-20150219-0000-211.m2tは一致しました
バイナリー・ファイル/mnt/image/3546-21-20150313-0120-21.m2tは一致しました
バイナリー・ファイル/mnt/image/3596-9-20150315-0030-211.m2tは一致しました
バイナリー・ファイル/mnt/image/3633-7-20150305-0230-141.m2tは一致しました
バイナリー・ファイル/mnt/image/3596-10-20150315-0030-16.m2tは一致しました
バイナリー・ファイル/mnt/image/3518-22-20150315-0800-24.m2tは一致しました
バイナリー・ファイル/mnt/image/3616-23-20150320-2300-211.m2tは一致しました
バイナリー・ファイル/mnt/image/3631-6-20150221-0120-21.m2tは一致しました
バイナリー・ファイル/mnt/image/3548-25-20150321-0157-22.m2tは一致しました
------
書き換えた箇所を元に戻したい人は、umountして以下をどうぞ(ゼロパディングしてるだけなんですが)
------
umount /mnt/image
ddrescue --fill=- /dev/zero /mnt/image/back.img /mnt/image/rescue.log
------
ファイルはfoltia anime lockerで録画したTSなんで、何のアニメかはうまく探してあげてください。
でも850GBもあるアニメを全部見てエラーチェックやるよりよっぽどマシでございます。
被害はたいして興味が無かったアニメばかりでよかったです。