HDDが壊れそうなときにdd使うのは古い。というわけでGNU ddrescue

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」です。

https://www.gnu.org/software/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でなければ、ヘタすると全部のアニメを端から端まで見てデータチェックをするハメになってたわけで。なかなか優秀なツールです。

 

タイトルとURLをコピーしました