2013年3月24日日曜日

壊れたTime Machineボリュームからファイルを復旧させようとした思い出

私のようにHFS Plusのファイルを復旧したい人がいるかもしれません。まったく不完全ながら復旧プログラムを作る方法の参考になるかもしれないので書いておきます。

2013-03-27 追記
 こんな記事を見つけました。ハードディスクの中身を誤って消した場合のファイル復旧方法
 これを知っていれば、こんな苦労はすることはありませんでした。

2013-04-23 
 期待して見てがっかりする人がいるかもしれませんので、
 タイトルを「復旧させるヒント」→「復旧させようとした思い出」に変更しました。

はじめに


もうかなり記憶が薄れかけている2010年頃の話です。自宅のMacをTime Machineでバックアップをとっていたのですが、Time Machineボリュームのマウントに失敗するなどのエラーが頻発するようになりました。そのままかなりの間ほっておいたのですが、急にMacのシステムが壊れてしまい再インストールを行いました。ここでTime Machineからデータを復旧しようとしたのですが、Time Machineのファイルシステムも壊れているようで全くマウントできませんでした。

ということで、失意の中もうデータはなかったものとして諦めようと思いました。しかし、今まで撮った写真データなどはもう帰って来ません。私は重い腰を上げHFS Plusの仕様を探し、こわれたTime Machineボリュームから復旧させるツールの作成にとりかかりました。

まずは、Pythonで作り始めました。Pythonならばインタープリターで試しながら、復旧ルーチンを組むことができるので、プログラムが早くできます。

ファイル・フォルダカタログを探す


まず、HFS Plusの構造体をすべてPythonで実装するという無駄なことを考えていたのですが、 Pythonで実行しながら作っていたため、途中でファイルとフォルダのエントリ(カタログという)の前に特定のバイト列が付いていることに気が付きました。この特定のバイト列を探せば簡単に復旧できると思いました。

  File ......... 00, 02, 00, 0E
  Folder ... 00, 01, 00, 0C

まあ、これでは関係ないデータもファイルとして復旧されてしまう可能性はありますが、できるだけ多くのデータを復旧させるという目的は叶えられるはずです。仕様上最後のバイトが違うことはありうるので注意が必要ですが、私のTime Machineのボリューム上では必要なファイルはそうなっていました。

うろ覚えですが、カタログ情報は特殊な一つのファイルに収められています。正しく処理するのであればカタログファイルを取得し、順次処理する必要があるのですが、中身がB-treeでおさめられています。よくわからないのでやめました。まあファイルシステムが壊れているのでこれでよかったのかもしれません。

エクステントの処理


カタログの情報が取得できた所で、その情報からファイルを復旧させなければなりません。カタログ情報からファイルのデータのある位置とサイズが確認できるのでそれを復旧させればいいだけなのですが、色々な理由でデータはディスク上の複数の位置に分割されている場合があります。ちなみに分割されたデータひとつひとつのことをエクステントと言います。なので、複数のエクステントに分けられている場合、1つに結合しなければなりません。

しかし、私のTime Machineボリューム上では分割されているデータが無いようだったので、最初のエクステントのみ復旧とさせました。これは溢れない限りデータを追加追加で使用するTime Machineボリュームだったからうまくいったのかもしれません。エクステントの結合するだけならば難しくないのですが、エクステントがたくさんあるとエクステントオーバーフローファイルというものに収められるらしいです。これがどこにあるのかよくわからないので考えないことにしました。

それとディスク上の位置やサイズはブロック単位で管理されているので、ブロックサイズを取得しなければなりません。これはボリュームヘッダに書かれていますので、これを読み込みます。

また、大きなサイズのデータには興味がなかったので、100MB以上のデータは処理を省きました。
HFS Plusにはデータフォークとリソースフォークがありますが、リソースフォークは無視しました。

復旧するファイルがどこのフォルダにあったか


あとはどこのフォルダにデータがあったかですが、これはファイルのカタログに親フォルダ番号があるのでそのフォルダということはわかります。フォルダ番号は対象のフォルダカタログに書いてありますので、ファイルを復旧させる前にすべてのフォルダのカタログ情報をPythonの配列にすべて放り込みました。そのため、メモリをとても使うようになりました。


動かしてみる


運が良かったとはいえ、2日で復旧プログラムを作ることができました。そして、プログラムを動かしました。しかし、数日動かし続けても全く終わらずやっとカタログ情報の読み込みが完了しただけでした。またファイル名のところで文字コードがおかしいといくつもエラーが出ていました。とりあえず私は実行を止めました。

C言語で一部再実装


とりあえず私はカタログ情報の位置取得をC言語で組み直しました。カタログ情報の位置取得だけならば、上述のように特定のバイト列だけを探せばいいので簡単です。カタログからファイルに出力させ、そこからPythonのデータ復旧プログラムに渡すという手段をとることにしました。

またカタログ情報をファイルに出力させたところ、UTF-16のファイル名にゴミが入るため、UTF-16の文字のペアが途中から崩れ、ファイルの後半が読み込めなくなるということが起こりました。理由は、英語ファイル名しか考慮していないコードが悪いのだということは気づきましたが、'\0'の文字を全て消すプログラムを作ってUTF-8で読み込ませたところ、うまく行ったので、あまり深く考えずこれで良しとしました。今考えればよくうまく行ったものです。


カタログ情報作成をC言語に直したので、カタログについては数時間で完了しました。そしてPythonの復旧プログラムを動かしました。これはやはり1、2日かかりましたが、必要なファイルをすべて復旧することができました。ファイル名エラーについては、やはり起こるのですが、必要なファイルは復旧できたのでエラーを起こしたファイルについては無視することにしました。

しかし、本当に恐ろしかったのはここからでした。復旧したところ重複したファイルや要らないファイルが山ほど出来ました。私はPythonプログラムに入力する前のカタログ情報ファイルからいらないファイルをすべて手作業で削る作業を行いました。これに一ヶ月程度かけ、そしてPythonプログラムを動かし必要なファイルのみを復旧させることができました。

最後に


もうあれから随分たちますが、あれからというもの私はバックアップを2つ以上とることにしています。私のようにHFS Plusのファイルを復旧したい人のヒントぐらいにはなるかもしれません。ひどいコードで後ろ指をさされるかもしれませんが、直す気力もないのでプログラムを不完全なまま以下に公開します。変な英語で説明が書かれている件については深く考えないで下さい。

buggy_hfsp_recovery_test



0 件のコメント:

コメントを投稿