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」です。
GNU ddrescueの使い方
普通にconfigure→make→make installで入れる。
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
(zfsフラグが立っているのは、ZFSで使ったHDDをそのまま流用したからです…)
というわけでmountするのにセクタの先頭位置のオフセットが必要です。
オフセット量は「Startの値 * 512」(この場合は63 * 512 = 32256)です。
# mount -o loop,offset=32256 /mnt/fs/back.img /mnt/image/
mountできたら/mnt/image内のファイルを新しいHDDにコピーしちゃってください。
これでデータ救出は完了です。
コピーが終わったら、このイメージを使って「どのファイルに不良セクタが当たってたか」を調べます。
やってることは以下な感じです。
- 不良セクタとしてロギングされた位置に、任意の文字列を書き込む
- findとgrepを使って、ひたすらファイル内を文字列検索する
力業だなあw
というわけでデータを書き換えるんですが、元に戻せます(後述)
要はgrepなので置き換える文字列は「元データとかぶらない文字列」にする必要があります。
今回は「HOGEPIYO」にしてみます。
この文字列は一旦ファイル(/mnt/fs/tmpfile)に入れます。
# echo -n "HOGEPIYO" > /mnt/fs/tmpfile
あとイメージをmountしたままなので、一旦umountします。
# umount /mnt/image
不良セクタを文字列(HOGEPIYO)で置き換えます。
# 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なんで、何のアニメかはうまく探してあげてください。
しかしGNU ddrescueでなければ、ヘタすると全部のアニメを端から端まで見てデータチェックをするハメになってたわけで。なかなか優秀なツールです。