クラッシュしたディスクから可能な限りデータを救出したいと思って作ったプログラムです。 同様なツールとして、ddrescureなど存在しましたが、私の用途には合いませんでした。 なんぜIOエラーの後、ディスクが見えなくなってしまうので、その後何度リトライしても無駄なのです。 しかも、Linuxを再起動しないと復帰しない・・・
何かしら手段があるのかもしれませんが、私の調べた限り有効な手段がみつかりませんでした。 仕方がないので、C言語で単純なread/writeプログラムを作成して、IOエラーが発生したら終了するというのを作りました。 IOエラーで終了するたびにLinuxを再起動すること数百回・・・どのセクタまでコピーしたのか、エラーしたセクタ番号は・・ 手書きメモには限界を感じたので、コピーしたセクタ番号とエラーしたセクタ番号を管理する機能を追加しました。 なんだかんだで、この時点でC++になっていました。
これで、実行したら前回エラーした箇所からコピー開始をしてくれるようになったので、Linux機をもう1台用意して sshで実行と再起動をループするようにしました。
延々と再起動を繰り返して一昼夜、どこまで進んだのか判りません、いつ終わるのか検討も付きません。 だいたいbadセクタが1個だけポツンと存在する訳でなく、その一帯が全部badなのですから、コピー開始して最初のセクタでIOエラーが発生して再起動・・・ 標準出力を目視するまもなくシャットダウン・・・ 訳がわからないので、進捗計算を追加して、状況をscpで保存するようにしました。
さて、状況がわかってくると、ほとんど進んでいないことが判りました。 考えてみれば、1セクタごとにIOエラーで再起動しているのですから当たり前です。 IOエラーしたら、1Mバイトほどスキップして再開するようにしました。あまり状況は改善しませんでした。 1Gほどスキップするようにしたら、やっとそれなりに進むようになりました。
この時点で、進捗管理がややこしくなりました。終わったセクタとエラーしたセクタとスキップしたセクタ・・・ 進捗管理の処理を最初から書き直しました。自分は何をやっているのだろうと想いましたが。
一晩経って、ひと通りのコピーが終わりましたが、スキップした1GBの領域が数十箇所も存在します。 さすがにこれは気持ち悪いので、この領域を細分してコピーする処理を追加しました。 これにより、数メガ〜数十メガの領域が数百カ所のこりました。いずれも先頭セクタは確実にIOエラー、それ以降は不明という状態です。
ここに来るまでにデバッグを繰り返した中で、同じセクタでもエラーになる場合と、ならない場合があることに気づきました。 よくよく調べてみると、そのセクタがread bufferの途中に含まれるとエラーになり、bufferの最後なら正常に読み込めるみたいです。 何度もテストを繰り返した結果、hddの転送が複数セクタまとめて行われる事を思い出しました。 hdparmコマンドの-mオプションのやつです。 これは当然、1に設定するべきだろうと試しましたが、現在のSATAディスクでは設定する方法が見つかりませんでした。何か手段がありそうなのですが・・・ 仕方がないので、残った数Mの領域は、最後のセクタからコピーする処理を追加しました。
また延々と再起動を繰り返し、途中でバグを発見して修正しつつ、無限に続くかと思うような再起動。ディスクよりマシンが壊れるんじゃないかと。 そして残ったのが、512バイトから数十キロバイトの未処理領域が数千箇所。
この段階で発見したのが、セクタ番号から該当するファイル名を割り出せるという事実。 早速、残されたログと進捗管理のファイルから、エラーと未処理のセクタ番号をリストアップ。 セクタ番号からファイルパスを調べるシェルスクリプトを作成して結果がでるまで、さらに一昼夜 その結果をみて溜息をつきました。 badセクタのほとんどがfirefoxのキャッシュファイルだったのです。こんなもん意地になって復旧せんでええやろ・・・ もっとこの事実にはやく気づいていれば、とっくに終わっていたのに。
とは言うものの、システムのファイルにも多数のbadセクタが存在していました。 が、ファイルパスからyumコマンドでパッケージ名を割り出して、削除して最インストールすれば大丈夫なはず。 唯一、仮想マシンのqcow2に1セクタだけbadセクタが含まれていましたが、テスト環境としての仮想マシンなので初期化しても大丈夫 それ以外の重要なフィアル達は大丈夫と確信が持てました。
そして、コピー済のイメージをループバックマウントしてchrootして壊れたファイルを削除し、パッケージを再インストールし、 新しいhddに書き込んで無事に復旧することができました。
このような状況で、行き当たりばったりに作成したプログラムですが、また使う機会があるかもしれないと思い、githubにバックアップしておきます。
処理内容は、C言語のhello worldに続いて紹介されるような fread/fwriteのプログラムです。 これに若干のioctlと上記の経緯の処理をスパゲッティした内容になります。 もし、あなたが何か血迷ってこれを使おうと思っているならば、ソースを読んで全て自己責任で使って下さい。