## Linux Ext2fs Undeletion mini-HOWTO

Aaron
Crane

    aaronc@pobox.com

JF Project
日本語訳

    JF@linux.or.jp

v1.3, 2 February 1999

次のような状況を想像してほしい。ここ 3 日間、飲まず食わすでシャワーも浴びずに 作業を続け、ようやくその抑えきれないハッキングの衝動が実を結んだ。ついに、 プログラムが完成したのだ。世界的な賞賛と名声をもたらすだろうほどの。 あとは tar でかためて、Metalab にあげるだけだ。おっと、Emacs の バックアップファイルの削除を忘れていた。ここであなたは、`rm * ~` とコマンドを打つ。そして、嗚呼、コマンドに余計な スペースを入れてしまったことに気付くのである。世紀の逸品 を削除してしまった！ しかし、万策尽きたわけではない。この文書は、Ext2 ファイルシステム上で削除してしまったファイルを復旧する方法について解説して いる。おそらく、最後にあなたはそのプログラムをリリースできるはず....

## はじめに

この mini-Howto は、ext2 ファイルシステム上で削除してしまったファイルを 復旧する方法について、そのヒントを提供しようとするものです。 また、そもそもファイルを誤って削除しないようにするための予防策についても、 若干説明しています。

もちろん、たった今 `rm` コマンドで 「ちょっとした事故」を起こしてしまった人のために、この文書は役に立つでしょう。 とはいえ、いずれにしても多くの読者に本文書を読んでもらいたいとも願っています。 いまはまだわからないでしょうが、 ある日この情報があなたの窮地を救うかもしれないのですから。

この文書は、読者が UNIX ファイルシステム全般についてある程度知っている ことを前提にしていますが、普段 Linux 使っている人ならたいてい意味が分かる ようにもしたつもりです。もし読者が全くの初心者なら、Linux 上でのファイルの 復旧には、さしあたりかなりの技術的知識と忍耐とが要求されているということを 述べておきます。(訳注： The Linux Kernel に比較的詳しい解説があります。)

ext2 ファイルシステム上での削除ファイルの復旧には、少なくともそのファイルが 保存されていた生(raw)デバイスに対する読み出しアクセスが不可欠です。一般に このことは root での作業を意味しますが、(Debian GNU/Linux などの) ディストリビューションのなかには、`disk` グループに所属しているメンバーなら、 そうしたデバイスにアクセスできるようになっているものもあります。 `e2fsprogs` パッケージにある `debugfs` も必要になりますが、 このユーティリティはお使いのディストリビューションに含まれていると思います。

著者がこの文書を書いたのは、`rm -r` というなんとも 愚かで破壊的なコマンドを root で使ってしまったという個人的な経験に 由来しています。97 個の JPEG ファイルを削除してしまったのです。これらは 是非とも必要で、かつ他から再び取得するのはほとんど 不可能な代物でした。しかし、( の章にある) tips を使ってかなり粘った結果、91 枚を無傷で復旧できました。残りの画像に ついても、 5 枚まではなんとか取り戻すことができました (各々がどういう画像か 分かる程度の復旧でした)。最後の一枚だけは表示不能でしたが、これについても 情報欠落を 1024 バイト以下程度にはできたと思っています (しかし不運にも欠落した情報はファイルの先頭部分だったのです。 JFIF ファイル形式については何も知らなかった当時としては、 出来るだけのことはやったのですが)。

削除ファイルの復旧率として、どの程度の値を期待できるかについては のちほど議論するつもりです。

### 更新歴

この文書の公式リリースとそのリリース日は、以下のとおりです。

- v1.0, 1997年1月18日

- v1.1, 1997年7月23日 ( 参照)

- v1.2, 1997年8月4日 ( 参照)

- v1.3, 1999年2月2日 ( 参照)

#### バージョン 1.1 での変更点

バージョン 1.1 での変更点は、なによりも、ファイル復旧の例題にあった 著者の思い違いを訂正したことです。誤りを指摘してくれたすべての方に 感謝します。プログラムの相互作用について書く際には、もっと慎重に なろうと思います。

二つ目は、UNIX ファイルシステムのレイアウトの説明を書き直したことです。 これによって、おそらくより分かり易くなったと思います。もともと自分でも満足 してはいなかったのと、複数の方からのコメントでも不明瞭との指摘をいただいて いたからです。

三つ目は、文書の中程に uuencode した `fsgrab` の gzip tarball があったのを削除したことです。このプログラムは、現在 著者の ウェブサイト や Metalab (およびそのミラーサイト)から入手できます。

四つ目は、この文書が Linux Documentation Project の SGML Tools に合わせて SGML 化されたことです。このマークアップ言語は、スクリーン表示や印刷の 便宜に応じて、(HTML や LaTeX などの)他の複数のマークアップ言語へも 簡単に変換できます。この利点のひとつは、印刷時の美しい版組が より簡単に実現できるということです。またウェブ上で閲覧する場合には クロスリファレンスやハイパーリンクが使えます。

#### バージョン 1.2 での変更点

この改訂は、おもに項目の追加のためです。主として、読者からの提案による 変更であり、そのひとつは特に重要なものです。

第一の変更箇所は、Egil Kvalegerg <egil@kvalengerg.no> からの提案 です。彼は、`debugfs` の `dump` コマンド を指摘してくれました。Egil、どうもありがとう。

第二の変更箇所は、重要なファイルを削除してしまわないために `chattr` コマンドを使うということを指摘した点です。 Herman Suijs <H.P.M.Suijs@kub.nl> からの指摘です。

文書の abstract が改訂されました。いくつかの組織とソフトウェアに関する URL も追加しました。(typo の訂正を含む)各種マイナーな変更もありました。

#### バージョン 1.3 での変更点

lde. Oh, and I've changed `Sunsite' to `Metalab' throughout. ]]> 最初のリリースから 17 カ月経ちましたが、新しい追加事項はほとんどありません。 今回は、小さな間違いをいくつか修正しただけです(typo やリンク切れ等、-- 特に、Open Group へのリンクがなかったことの修正など)。そして、カーネル バージョンや `lde` のバージョンなどあまりにも古くなった 記述を一部更新しました。それと、"Sunsite" を "Metalab" に変更しました。

このリリースは、バージョン 2.0 になる前の最後のリリースだと 考えています。2.0 は、完全な HOWTO 文書になるはずです。メジャーバージョン 番号の繰り上げにふさわしい充分な解説を加えられるよう、目下作業中です。

### この文書の正式な公開場所

この文書の最新の公開バージョンは、必ず次の場所(およびそのミラーサイト) に掲載する予定です。

Linux Documentation Project

また、最新版は、 著者のウェブサイトにも各種フォーマットで掲載されています。

- SGML ソース 。これは、著者が SGML Tools パッケージを使って書いたこの文書の ソースファイルです。

- HTML。 これは、SGML ソースから自動的に生成される HTML バージョンです。

- Plain text 。これも、SGML ソースから自動的に生成されるテキストバージョンです。

## ファイルを誤って削除しないためには

ファイルの復旧という点に関して、Linux は MS-DOS とは勝手が違うということを まず肝に銘じてください。MS-DOS (およびその悪魔の子である Windows 95) の場合、一般にファイルの復旧はかなり簡単です。復旧処理を自動で行う ユーティリティが、オペレーティングシステム (まあ、著者はこの用語を 広義の意味で使うわけですが) に付属しているからです。しかし、Linux の場合、 そうはいきません。

従って、ルール No.1 (そう言いたければ「聖なる誓い」) は

### 鉄則

バックアップを取るべし

stick to it. Myself, I use a spare hard disk on a second machine, and periodically mirror my home directory onto it over the ethernet. For more information on planning a backup schedule, read Frisch (1995) (see section ). ]]> すなわち、何であれ、バックアップを取るようにすること。 すべてのデータが対象です。著者同様、 読者のコンピュータにはメールその他の交信記録やプログラム、各種文書など、 長年に渡る情報が大量に蓄積されているはずです。もしも突発的なディスク 障害が起こったり、あるいは(縁起でもないですが)悪意を持ったクラッカーに ディスクの内容を消去されてしまったりした場合、日常生活がどれほど混乱するか を考えてみてください。決してあり得ないことではありません。現に、著者は まさにそうした状況に陥った多くの人たちとメールのやり取りをしてきました。 賢明な Linux ユーザは、今すぐ適当なバックアップデバイスを買ってきて、 適切なバックアップスケジュールを立てた上で、それを厳守するようおすすめ します。著者自身は、サブマシン上の余ったディスクを使って、イーサネット 経由でホームディレクトリを定期的にミラーするようにしています。バックアップ スケジュールの立て方に関する詳細は、Frish (1995) をご覧下さい ( 参照)。

では、バックアップをしていない場合は、どうすべきでしょうか？ (あるいはバックアップがあったとしても。 重要なデータについては、幾重もの安全対策をとることは悪い考えではありません)

重要なファイルのパーミッションは 440 (あるいはそれより厳しい値) にしましょう。 書き込みをできなくすれば、 `rm` コマンドで削除する前に、削除をするか否かの確認メッセージが 表示されるようになります。(ただ、著者の場合、それでも `rm -r` コマンドでディレクトリ内を再帰的に削除する際には、 最初の確認表示の段階でプログラムを停止させて、`rm -rf` コマンドを打ってしまったりします。)

特別なファイルに関しては、隠しディレクトリにハードリンクを作成するのも よい方法です。これは、あるシステム管理者から聞いた話ですが、彼は `/etc/passwd` ファイルを何度か誤って削除したことが あったそうです (これはシステムにほぼ破壊的なダメージを与えます)。 それに対する対処方法のひとつは、以下のようなものだったそうです (これは root で実行します)。

      # mkdir /.backup
      # ln /etc/passwd /.backup

こうしておくと、ファイルの内容を完全に消去するのがかなり面倒になります。 もし次のようなコマンドを実行したとしても、

      # rm /etc/passwd

その際には、

      # ln /.backup/passwd /etc

とすれば、ファイルを復旧できます。もちろん、ファイルを上書きしてしまうと この方法は役に立たないので、いずれにしてもバックアップは必要です。

ext2 ファイルシステム上では、ext2 の属性を利用してファイルやディレクトリを 保護することができます。この属性の操作には `chattr` コマンドを使います。属性にはまず、「追加のみ可能(append-only)」属性という のがあります。この属性が付いたファイルは、内容の追加は可能ですが、削除 が出来なくなり、既存の内容を上書きすることもできなくなります。ディレクトリ にこの属性が付いている場合は、そのディレクトリ内のファイルやサブディレクトリ は、通常通り変更は可能ですが、ファイルの削除が出来なくなります。 「追加のみ可能」属性は、次のように設定します。

      $ chattr +a ファイル名

また、「変更不可(immutable)」属性というのもあり、これは root 権限でのみ 設定や解除ができるようになっています。この属性が付いたファイルや ディレクトリは、変更、削除、名前の変更、(ハード)リンクの作成が不可能に なります。設定は、次のようにします。

      # chattr +i ファイル名

ext2fs には、さらに「削除不可(undeletable)」という属性もあります (`chattr` に `+u` を付けて設定します)。意味するところ は、この属性を付けたファイルが削除された場合、実際に再利用されるかどうかに 関わらず、一旦安全な場所に移動され、一定期間経過後に削除されるというもの です。ただ残念なことに、この機能はメインストリームのカーネルにはまだ 実装されていません。過去に実装されかけたことはあったのですが、(著者の知る 限り) 現在のカーネルではまだ利用可能とはなっていません。

`rm` は `rm` `-i` へのエイリアスとするか、同機能の関数に置き換えるべきだ、 と主張する人もいます (`rm` `-i` は、ファイル 削除の際に必ず確認を求めます)。実際に Red Hat は、root を含む全ユーザに 対してこの機能をデフォルトで設定しています。しかし、著者の個人的な意見では、 ソフトウェアは命令通り忠実に動くべきだと思っているので、こうした見解には 賛同できません。これには問題もあります。遅かれ早かれ、ユーザはシングル・ ユーザモードで起動したり、別のシェルを使ったり、異なるマシンを利用したり するはずですが、そうした場合にはこの `rm` 機能は 無効になります。削除確認の表示が出ることに慣れきっていると、自分が 置かれた状況を考えずに、一度に多数のファイルを削除指定してしまわない とも限りません。同様に、`rm` コマンドを各種 スクリプトやプログラムで置き換えるのも、著者の考えでは、危険なこと だと思います。

もう少しましな方法としては、`rm` とは別のコマンド名で、 「ゴミ箱」形式の削除ができるようなパッケージを使ってみることです。 詳しくは、Peek, et al (1993) ( 参照) を 見てください。ただ、これにも問題はあります。 このパッケージの作者は、削除についてユーザが無頓着になるよう奨励している ように思われるからです。Unix システム上では、むしろ、自己責任に基づく 慎重な運用というアプローチが基本になるはずです。

## どの程度の復旧率が見込めるのか？

場合によります。Linux のような高機能、マルチユーザ、 マルチタスクのオペレーティングシステムでファイルを復旧する場合の問題点の ひとつは、誰がいつディスクに書き込みをするか分からないということです。 ファイルを削除するよう命令されると、オペレーティングシステムは そのファイルが占めていたディスクブロックを、新規ファイルに割り当てる ための空間として認識します。(これは、Unix 系オペレーティングシステム に見られる一般原則の典型的な例です。つまり、カーネルやその関連ツールは、 ユーザが仕組みを理解した上で作業していることを前提にしているということです。) 一般に、マシンを酷使しているほど、ファイルの復旧は難しくなります。

また、ディスクのフラグメンテーションもファイルの復旧率に影響します。 削除ファイルがフラグメンテーションの激しいパーティションに あった場合には、そのファイル全体を復旧できる確率は低下します。

もし読者のマシンが (著者のマシンのように) 事実上シングルユーザのワーク ステーションであり、ファイルを削除してしまった瞬間にディスクを酷使するような 処理が行われていなかったとするなら、冒頭で紹介した著者の場合と同等の 復旧率を達成できると思われます。著者はファイルの 94 % 近くを無傷で 復旧しました (しかもこれらはバイナリです)。おそらく 80 % 以上復旧できれば、 かなり満足できるはずだと思います。

## では、どうやって復旧するのか？

復旧作業の主要な部分は、raw パーティションデバイスからデータを見つけだして、 +それをオペレーティングシステムが認識できるようにすることです。 +これには基本的に二種類の方法があります。ひとつは、 現在のファイルシステム上にある削除されたファイルの i ノードをいじって 'deleted' フラグを取り除き、データが魔法のようにもとの場所に 戻ってくるように祈る、というものです。もうひとつの、より安全ではありますが 時間のかかる方法は、パーティション内にあるデータを探し出して、 別のファイルシステム上の新規ファイルにそのデータを書き出すというものです。

ただし、データ復旧に取りかかる前に、やっておくべき作業が何段階かあります。 詳細は、, および をご覧ください。 実際にファイルを復旧する方法は、 , , , を参照してください。

## ファイルシステムのマウント解除

どちらの方法を使うにせよ、まず最初に削除したファイルが入っているファイル システムのマウントを解除する必要があります。ここで慌てると、ファイルシステム をめちゃめちゃにしてしまう可能性があるので注意してください。 この作業は、ファイルを削除してしまったことに気が付いた時点で 出来る限りすばやく 行わなければなりません。マウント解除が早ければ 早いほど、データが上書きされる可能性も少なくなります。

最も簡単な方法は、次のようなものです。ここでは、削除したファイルが `/usr` にあるものと仮定しています。

      # umount /usr

しかし、`/usr` 以下に置かれているものが使えなくなる のは困るかもしれません。その場合、read-only で再マウントします。

      # mount -o ro,remount /usr

削除したファイルがルートパーティション上にある場合は、`-n` オプションを付けて、`/etc/mtab` ファイルへの書き込みを 避ける必要があります。

      # mount -n -o ro,remount /

どの方法でマウント解除するかに関わりなく、他のプロセスが当該のファイルシステム を使っている可能性もあります (その場合、マウント解除は失敗し、 "Resource busy" のエラーが表示されます)。こういう時のために、特定のファイル やマウントポイントを使っているプロセスに対してシグナルを送るプログラムが 存在します。`fuser` です。これを `/usr` パーティションに対して使ってみましょう。

      # fuser -v -m /usr

上記コマンドによって、対象となるプロセスが列記されます。重要なプロセスが 存在しないことが確認できたら、次のコマンドを実行します。

      # fuser -k -v -m /usr

こうすると個々のプロセスに `SIGKILL` シグナルを送ります (これはプロセスを確実に殺すシグナルです)。あるいは、次のように しても結構です。

      # fuser -k -TERM -v -m /usr

これは個々のプロセスに `SIGTERM` シグナルを送ります (これはプロセスを正常に終了させるシグナルです)。

## i-node を直接書き換える場合の準備

著者としては、この方法はおすすめしません。これはファイルシステムの低レベル 層をいじるわけですが、そういうやり方が賢明だとは思えないからです。また この方法では、確実に復旧できるのは個々のファイルの最初の 12 ブロックだけ なのです。したがって、大きなファイルを復旧する際は、通常、 最終的に他の方法を使わざるを得ません。(とはいえ、詳しい情報は、 をご覧ください。)

どうしてもこの方法を使わないと気が済まない場合は、 raw パーティションのデータを他のパーティション上にディスクイメージとして コピーし、そのイメージを loopback を使ってマウントすることをおすすめ します。

      # cp /dev/hda5 /root/working
      # mount -t ext2 -o loop /root/working /mnt

(注意してほしいのは、`mount` コマンドのバージョンが 古いと、上記のコマンドで問題が生じるかもしれないということです。 `mount` が上手く動かない場合、最新バージョンか、少なくとも version 2.7 以上に更新することを強くおすすめします。古いバージョンには、 致命的なセキュリティ上のバグがあるからです。)

このように loopback を使っておくと、 そのファイルシステムを完全に破壊してしまった場合でも、 raw パーティションを再度コピーしてやり直すだけで済みます。

## 別の場所にデータを書き出す場合の準備

/usr, and a `/home`. With all these to choose from, you should have no problem: just create a new directory on one of these. ]]> 別の場所にデータを書き出す方法を取る場合は、まずレスキューパーティションが あるかどうかを確認する必要があります。つまり、復旧したいファイルのコピー を書き出す場所を確保するわけです。おそらく、読者のシステムには複数の パーティションがあり、ルートや `/usr`、`/home` は別々のパーティションに分かれていると思います。 このうちのひとつを使っても、全く問題ないはずです。どれかひとつの パーティション上に、新規のディレクトリを作成しましょう。

ルートパーティションしかなくて、すべてをそこに保存している場合は、 若干面倒です。それでも MS-DOS や Windows のパーティションが使えたり、 もしくはカーネルモジュールとして ramdisk ドライバを使えるようには なっているのではないでしょうか？ ramdisk を使うには (これは、1.3.48 以降のカーネルであることが前提です) 次のようにします。

      # dd if=/dev/zero of=/dev/ram0 bs=1k count=2048
      # mke2fs -v -m 0 /dev/ram0 2048
      # mount -t ext2 /dev/ram0 /mnt

これによって 2 MB の ramdisk 領域が作成され、`/mnt` 上にマウントされます。

少々注意: 読者が `kerneld` (もしくは、 2.2.x や 2.1.x 以降のカーネルを使っているならその後継である `kmod` ) を使ってカーネルモジュールをロードしている場合、ramdisk 領域 から不揮発性ストレージへのコピーが終了するまで ramdisk のマウントを解除 してはいけません。ramdisk 領域のマウントを解除すると、`kerneld` はモジュールを (一定時間経過後に) アンロードできるものと思って しまい、さらに実際にアンロードされてしまうと、ramdisk のメモリ領域は カーネルの他の部分に再利用されてしまって、苦労してデータの復旧に費やした 時間が全く無駄になってしまうからです。

Zip や Jaz, LS-120 といったリムーバブル装置を持っているなら、レスキュー パーティションの置き場所として是非利用してください。持っていない場合は、 フロッピーでいきましょう。

他に必要になるのは、パーティションデバイス上から必要なデータを読み出せる プログラムです。いざとなれば `dd` でも出来るのですが、でも例えば、パーティションの 600 MB 目から 800MB 目までの 領域を読み出そうとするような場合、dd は最初の 600 MB の領域を無視できず、 どうしても頭から読み込もうとしてしまうのです。高速なディスクを使っている 場合でも、これにはかなりの時間が掛かります。この振る舞いをなんとかしようと して、著者は、パーティションの途中にある領域を適切に読み出しできる プログラムを作成しました。`fsgrab` という名前の プログラムです。このプログラムのソースパッケージは、著者のウェブサイト や Metalab (とそのミラーサイト) にあります。読者がこの方法を使ってくれる ものと仮定して、ここから先は、読者が `fsgrab` を 持っているものとして説明します。

ただ、復旧しようとしているファイルの大きさがどれも 12 ディスクブロックを 超えない場合 (1 ディスクブロックは通常 1 Kb です)、`fsgrab` は不要です。

`fsgrab` が必要だがダウンロードとビルドがイヤな場合は、 `fsgrab` のコマンドラインを `dd` のコマンドラインに置き換えて考えてください。例えば、`fsgrab` のコマンドラインが以下のような場合、

      fsgrab -c count -s skip device

これに相当する `dd` のコマンドラインは (おそらく処理時間が 余分に掛かりますが) 次にようになります。

      dd bs=1k if=device count=count skip=skip

ここで一応警告しておきます。`fsgrab` プログラムは、 著者のもとでは完璧に機能しましたが、読者のもとでどのような振る舞いを しようとも著者は一切責任を負えません。実際これは、きちっと設計された プログラムではなく、なんとか処理をこなすだけものにすぎません。免責 に関する詳細は、プログラム付属の `COPYRIGHT` ファイルに記載された "No Warranty" の章をご覧ください (the GNU General Public License です)。

## 削除した i-node を 見つけだす

次に踏むべき手順は、ファイルシステムを走査して、どの i-node が最近解放されたのかを調べることです。この作業を実行するには `debugfs` を使います。`debugfs` にファイルシステムが 保存されているデバイスのデバイスファイル名を付けて起動してください。

      # debugfs /dev/hda5

i-node を直接変更したい場合は、`-w` オプションを付けて ファイルシステムへの書き込みができるようにします。

      # debugfs -w /dev/hda5

削除した i-node を探すための `debugfs` のコマンドは `lsdel` です。プロンプトから次のように打ち込んで ください。

      debugfs:  lsdel

ディスク装置へのアクセス音がしばらく続いた後で、長い i-node のリストがパイプ 処理され、(`$PAGER` で設定した) ページャに表示されます。この リストを別の場所に保存したい場合、`less` を使っている なら、`-o` オプションの後に出力ファイル名を指定します。 `less` 以外を使っているなら、出力を別の 場所に送る必要があります。次のようにしてください。

      debugfs:  quit
      # echo lsdel | debugfs /dev/hda5 > lsdel.out

ここで、削除日時、ファイルサイズ、ファイルタイプ、パーミッションの数値、 ファイル所有者などの情報だけに基づいて、復旧したいファイルがどれなのかを 見つけださなければなりません。運良く、五分ほど前に削除した大きなファイル 等であれば、比較的簡単に見つけだすことができるでしょう。もしそうでなければ、 リストをしらみつぶしにあたっていきましょう。

可能なら、i-node のリストをプリントアウトすることをおすすめします。 調べるのがずっと楽になります。

## i-node の詳細を調べる

`debugfs` には `stat` コマンドがあり、 これを使うと i-node の詳細を表示することができます。 復旧リストにあるそれぞれの i-node に対して `stat` コマンドを実行してください。例えば、i-node 番号 148003 を調べようとする 場合は、次のようにします。

      debugfs:  stat <148003>
      Inode: 148003   Type: regular    Mode:  0644   Flags: 0x0   Version: 1
      User:   503   Group:   100   Size: 6065
      File ACL: 0    Directory ACL: 0
      Links: 0   Blockcount: 12
      Fragment:  Address: 0    Number: 0    Size: 0
      ctime: 0x31a9a574 -- Mon May 27 13:52:04 1996
      atime: 0x31a21dd1 -- Tue May 21 20:47:29 1996
      mtime: 0x313bf4d7 -- Tue Mar  5 08:01:27 1996
      dtime: 0x31a9a574 -- Mon May 27 13:52:04 1996
      BLOCKS:
      594810 594811 594814 594815 594816 594817
      TOTAL: 6

復旧したいファイルがたくさんあるなら、上記の手続きを自動化したいと お思いでしょう。`lsdel` で作成した復旧リストが `lsdel.out` ファイルに入っているとするなら、まず次のようにします。

      # cut -c1-6 lsdel.out | grep "[0-9]" | tr -d " " > inodes

これによって作成された `inodes` ファイルには、 復旧すべき i-node の i-node 番号だけが一行にひとつずつ記述されています。 この情報をファイルに保存したのは、その方が後々便利だからです。そして 次に、以下のようにします。

      # sed 's/^.*$/stat <\0>/' inodes | debugfs /dev/hda5 > stats

以上で `stats` ファイルに、`stat` コマンドの出力結果が全て記録されます。

## データブロックの復旧

ここでの手順は、復旧しようとするファイルの大きさが 12 ディスクブロック以下 であるか否かによって、非常に簡単にもなり、逆に面倒にもなり得ます。

### 小さなファイル

ファイルのサイズが 12 ブロック以下なら、ファイルのデータを保持している ブロックナンバーのすべてが、検出した i-node の中に書き込まれています。 その i-node に対して `stat` を実行すると、 ブロックナンバーを直接読み出すことができます。 さらに `debugfs` には、 この処理を自動化するコマンドも組み込まれています。 前章で取り上げた例をここでも使って説明します。

      debugfs:  stat <148003>
      Inode: 148003   Type: regular    Mode:  0644   Flags: 0x0   Version: 1
      User:   503   Group:   100   Size: 6065
      File ACL: 0    Directory ACL: 0
      Links: 0   Blockcount: 12
      Fragment:  Address: 0    Number: 0    Size: 0
      ctime: 0x31a9a574 -- Mon May 27 13:52:04 1996
      atime: 0x31a21dd1 -- Tue May 21 20:47:29 1996
      mtime: 0x313bf4d7 -- Tue Mar  5 08:01:27 1996
      dtime: 0x31a9a574 -- Mon May 27 13:52:04 1996
      BLOCKS:
      594810 594811 594814 594815 594816 594817
      TOTAL: 6

上記ファイルは 6 つのブロックから成り立っています。12 ブロックという 限度以下なので、`debugfs` コマンドを使ってこの ファイルを別の場所、ここでは `/mnt/recovered.000` に書き出すことができます。

      debugfs:  dump <148003> /mnt/recovered.000

もちろん `fsgrab` コマンドでも同様の処理が可能です。 `fsgrab` の場合は、次のようにします。

      # fsgrab -c 2 -s 594810 /dev/hda5 > /mnt/recovered.000
      # fsgrab -c 4 -s 594814 /dev/hda5 >> /mnt/recovered.000

`debugfs` でも `fsgrab` でも、 `/mnt/recovered.000` の末尾に不要な情報が付加されて しまいますが、全く問題はありません。この余分な情報を取り除くには、 i-node の `Size` フィールドにある値を取り出し、 この値を `dd` コマンドの `bs` オプションに指定するのが最も簡単でしょう。

      # dd count=1 if=/mnt/recovered.000 of=/mnt/resized.000 bs=6065

もちろん、ファイルを構成していたディスクブロックのいくつかは、既に上書き されてしまっているかもしれません。その場合は運が悪かったとしか言えません。 そのブロックを復旧することは不可能です。(パーティションをすばやく umount できたか否かが勝負の分かれ目です。)

### 大きなファイル

ファイルのデータが 12 ブロック以上ある場合は、ちょっと面倒です。さしあたり、 先ず UNIX のファイルシステムがどういう構造になっているのかを若干理解しておく 必要があります。ファイルのデータは、「ブロック」と呼ばれる単位に分割されて ディスク上に保存されています。このブロックには、頭から順に番号が付けられて います。また、ファイルは「i-node」という情報も持ち、i-node にはファイル 所有者やパーミッション、ファイルタイプ等の情報が記載されています。ブロック と同じく i-node にも順に番号が振られていますが、 この二つのシーケンスは全く別のものです。 ディレクトリエントリは、ファイル名と i-node 番号とからなります。

しかしこれだけでは、カーネルはディレクトリエントリに対応するデータを 見つけることが出来ません。それゆえ i-node には、次のように、ファイル データのブロックの位置に関する情報も記載されるようになっています。

- 12 データブロック分のブロック番号は、i-node に直接記載されています。 これらのディスクブロックは、直接ブロック(direct block) と呼ばれることもあります。

- i-node には、間接ブロック(indirect block) のブロック 番号がひとつ記載されます。この間接ブロックには、直接ブロックを超過した 256 ブロック分のブロック番号が記載されています。

- i-node には、二重間接ブロック(doubly indirect block) のブロック番号がひとつ記載されます。二重間接ブロックには、上記の間接ブロック を超過した 256 個分の間接ブロックが記載されています。

- i-node には、三重間接ブロック(triply indirect block) のブロック番号がひとつ記載されます。三重間接ブロックには、上記の二重間接 ブロックを超過した 256 個分の二重間接ブロックのブロック番号が記載されています。

以上をもう一度読んでください。確かにややこしいですが、非常に重要です。

現在、バージョン 2.0.36 以降のすべてのカーネルでは、ファイルを削除する際に 間接ブロック (および二重間接ブロック以上) を すべてゼロしてしまうようになっています。それゆえ、12 ブロック以上の ファイルの場合、ファイル内容の復旧どころか、必要なブロック番号を見つけ出せる 保証すらありません。

いまのところ著者がたどり着いた唯一の方法は、そのファイルのデータがフラグメント 化せずに連続して並んでいると仮定して、作業を行ってみるということです。 フラグメント化してしまっている場合は非常にやっかいです。フラグメント化 していないと仮定するなら、データブロックの並び方には (ファイルの データブロック数に対応して) 次のようなパターンが考えられます。

0 から 12 ブロック

  上述したように、ブロック番号は、i-node に記載されています。

13 から 268 ブロック

  直接ブロックのあとに間接ブロックがひとつあり、その後に 256 個の データブロックが並びます。

269 から 65804 ブロック

  上記同様、12 個の直接ブロック、ひとつの (不要な) 間接ブロック、そして 256 個 のブロックが並びます。その後に、ひとつの (不要な) 二重間接ブロックがあり、 さらにひとつの (不要な) 間接ブロックと 256 個のデータブロックという組み合わせ が 256 組続きます。

65805 ブロック以上

  冒頭の 65804 ブロックまでの並びは上記と同様です。その後に、ひとつの(不要な) 三重間接ブロックがあり、さらに 256 組の「二重間接シーケンス」が 続きます。二重間接シーケンスは、(不要な) 二重間接ブロックを先頭とし、その後に ひとつの (不要な) 間接ブロックと 256 個のデータブロックという組み合わせが 256 組続く並びによって構成されます。

もちろん、ここで仮定したデータブロックのブロック番号が正しいとしても、 その中身であるデータそのものが書き換えられていないという保証はありません。 さらに、ファイルが大きくなればなるほど、そのファイルが(特殊な環境を覗いて) なんらかのフラグメンテーションを起こさずに、ファイルシステムに書き込まれて いる可能性は小さくなります。

注意してほしいのは、この文書ではこれまでブロックサイズが標準的な 1024 バイトで あると仮定した上で説明しているということです。ブロックサイズをそれ以上に 設定している場合、上述の数字は変化します。特に、個々のブロック番号は 4 バイト 長なので、"ブロックサイズ/4" は、個々の間接ブロックにあるブロック番号の数と 一致します。それゆえ、上記の説明で 256 という数字が現れたときは、 必ず "ブロックサイズ/4" に置き換えてください。「必要なブロックの数」の境界も 変わることになるはずです。

それでは、大きなファイルの復旧例を見てみましょう。

      debugfs:  stat <1387>
      Inode: 148004   Type: regular    Mode:  0644   Flags: 0x0   Version: 1
      User:   503   Group:   100   Size: 1851347
      File ACL: 0    Directory ACL: 0
      Links: 0   Blockcount: 3616
      Fragment:  Address: 0    Number: 0    Size: 0
      ctime: 0x31a9a574 -- Mon May 27 13:52:04 1996
      atime: 0x31a21dd1 -- Tue May 21 20:47:29 1996
      mtime: 0x313bf4d7 -- Tue Mar  5 08:01:27 1996
      dtime: 0x31a9a574 -- Mon May 27 13:52:04 1996
      BLOCKS:
      8314 8315 8316 8317 8318 8319 8320 8321 8322 8323 8324 8325 8326 8583
      TOTAL: 14

上記ファイルがフラグメント化していない可能性は、そこそこあるように思われます。 i-node にリストアップされている冒頭の 12 ブロック (これらはすべてデータブロックです) は確かに連続しています。 まずこれらのブロックを復旧することから始めましょう。

      # fsgrab -c 12 -s 8314 /dev/hda5 > /mnt/recovered.001

ここで、i-node にリストアップされている次のブロック、すなわち 8326 は 間接ブロックなので無視できます。次にこの間接ブロックに続いて 256 個の データブロックが連続していると信じましょう (8327 番から 8582 番までです)。

      # fsgrab -c 256 -s 8327 /dev/hda5 >> /mnt/recovered.001

i-node に表記された最後のブロックは 8583 です。ここでもまた、ファイルの データブロックが連続しているものとみなします。上記で書き出した 最後のデータブロックは、8327 + 225 = 8582 番でした。次の 8583 ブロックは 二重間接ブロックなので無視できます。そしてこのブロックの後に、 間接ブロック (これは無視) に続く 256 のデータブロックが 256 組続くわけです。 これらをすばやく計算すると、次のコマンドを打つべきだということになります。 二重間接ブロック 8583 と (おそらく) そのすぐ後にある間接ブロック 8584 はスキップして、 データブロックである 8585 から始めている点に注意してください。

      # fsgrab -c 256 -s 8585 /dev/hda5 >> /mnt/recovered.001
      # fsgrab -c 256 -s 8842 /dev/hda5 >> /mnt/recovered.001
      # fsgrab -c 256 -s 9099 /dev/hda5 >> /mnt/recovered.001
      # fsgrab -c 256 -s 9356 /dev/hda5 >> /mnt/recovered.001
      # fsgrab -c 256 -s 9613 /dev/hda5 >> /mnt/recovered.001
      # fsgrab -c 256 -s 9870 /dev/hda5 >> /mnt/recovered.001

上記をまとめると、これまでに 12 + (7 * 256) 個のブロック、すなわち 1804 個の ブロックを書き出したことになります。i-node を `stat` コマンドで調べた結果は、'`blockcount`' で 3616 個と 表示されていました。残念ながら、この表記でのブロック長は (UNIX での伝統を 引き継いで) 512 バイトで計算されているので、実際は 1024 バイトで計算すると 3616/2 = 1808 ブロックということになります。つまり、あと 4 ブロック足りない だけだということです。最後に書き出されたブロックは 10125 番であったので、 再度これまでと同様に、間接ブロック(10126 番)をスキップして、それら最後の 4 ブロックを書き出します。

      # fsgrab -c 4 -s 10127 /dev/hda5 >> /mnt/recovered.001

以上で、運が良ければ、ファイル全体を復旧することに成功したことになります。

## i-node を直接いじる

この方法は、表面的には、先ほどよりもずっと簡単です。しかし以前も触れた通り、 12 ブロック以上の大きなファイルに対しては役に立ちません。

復旧したいファイルそれぞれに対して、usage count を 1 に設定し、deletion time を 0 に設定します。これには `debugfs` の `mi` (modify i-node) コマンドを使います。例として、これまでと同じく、 i-node 148003 を使って、i-node の変更処理を説明します。

      debugfs:  mi <148003>
                                Mode    [0100644]
                             User ID    [503]
                            Group ID    [100]
                                Size    [6065]
                       Creation time    [833201524]
                   Modification time    [832708049]
                         Access time    [826012887]
                       Deletion time    [833201524] 0
                          Link count    [0] 1
                         Block count    [12]
                          File flags    [0x0]
                           Reserved1    [0]
                            File acl    [0]
                       Directory acl    [0]
                    Fragment address    [0]
                     Fragment number    [0]
                       Fragment size    [0]
                     Direct Block #0    [594810]
                     Direct Block #1    [594811]
                     Direct Block #2    [594814]
                     Direct Block #3    [594815]
                     Direct Block #4    [594816]
                     Direct Block #5    [594817]
                     Direct Block #6    [0]
                     Direct Block #7    [0]
                     Direct Block #8    [0]
                     Direct Block #9    [0]
                    Direct Block #10    [0]
                    Direct Block #11    [0]
                      Indirect Block    [0]
               Double Indirect Block    [0]
               Triple Indirect Block    [0]

上記で、著者は deletion time に 0 を、link count に 1 を設定し、それ以外 のフィールドではそのままリターンキーを押しました。復旧したいファイル がたくさんある場合、この方法は確かにちょっと面倒ではありますが、 まあなんとかなると思います。 こうした方法を野暮だと思うのなら、かわいい 「ゴミ箱」アイコン付きのグラフィカルな "オペレーティングシステム" の方を とっくに使っているはずでしょうから。

ところで、`mi` コマンドの出力には、Creation time という フィールドがあります。これはウソです! (少なくとも誤解の素です。) 実際には、UNIX ファイルシステムではファイルの 作成時を知ることはできません。構造体 `stat` のメンバ である `st&lowbar;ctime` は、i-node の更新時間を示す ものです。つまり i-node 内の何らかの情報が最後に変更された時刻を示している わけです。と、まあ、この話はここまで。

著者が上記の例で使った `debugfs` よりも新しいバージョン を使う場合は、例題で示したフィールドのいくつかが省略されるかもしれない ので注意してください(特に、`Reserved1` と fragment フィールド (のいくつか) は省略されるようです)。

i-node の変更が済んだら、`debugfs` を終了して、以下のように してください。

      # e2fsck -f /dev/hda5

これには次のような意味があります。すなわち、削除されていたおのおのファイル は既に文字通り復旧しているのですが、まだディレクトリエントリには表示されない 状態です。`e2fsck` プログラムはこの状態を検出して、 そのファイルシステムの `/lost+found` ディレクトリ内に 個々のファイルのディレクトリエントリを追加するようになっています。 (したがってパーティションが通常 `/usr` にマウント されるのだとすると、ファイルシステムが次にマウントされた際に、復旧ファイルは `/usr/lost+found` に現れるわけです。) ここまで来れば、 あとはそのファイルの中身からファイル名を割り出して、そのファイルを ファイルシステムツリーの適切な場所に戻すだけです。

`e2fsck` を実行した際には、各種情報が出力されると同時に、 どのダメージを修復すべきかの質問が表示されます。"summary information" もしくは変更した i-node に関する事柄が表示された場合は、すべて yes で 答えてください。それ以外の質問については読者の判断にお任せしますが、 通常はすべての質問に yes で答えておくほうが無難です。`e2fsck` コマンドが終了したら、ファイルシステムを再マウントできます。

実は、`e2fsck` を使ってファイルを `/lost+found` に出現させる以外の方法もあります。`debugfs` を使えば、そのノードに対するリンクをファイルシステム内に 作成できるのです。i-node を変更した後で `debugfs` 作成することが可能です。i-node を変更した後で、`debugfs` の `link` コマンドを使います。

      debugfs:  link <148003> foo.txt

これによって、`debugfs` コマンドが正しいと判断した ディレクトリ内に `foo.txt` という名前のファイルが 作成されます。この `foo.txt` というファイルが、読者の 求めていたファイルとなるはずです。ただこの場合でも、`e2fsck` を使って、summary 情報や block count 等を修復する必要があります。

## 将来的にはもっと簡単になるでしょうか？

なります。事実、既にそうなってきていると著者は考えています。ただ、先ほど 説明したように、現在の安定版カーネル (2.0.x シリーズ) では、i-node の 間接ブロック番号はファイルの削除時にゼロに戻されてしまいます。しかしこれは 2.1.x シリーズの開発版カーネルと 2.2.x の安定版カーネルには当てはまりません。 今これを書いている日時が 1999年2月2日なのですが、数日前にカーネル 2.2.1 が リリースされたところです。1, 2 カ月すれば、Linux ベンダーは 2.2.x カーネルを 使ったディストリビューションを作りはじめるでしょう。

安定版カーネルで間接ブロックに伴う困難が克服されれば、著者が手動で i-node をいじることに反対していた論拠の多くも消失します。同時に、`debugfs` 付属の `dump` コマンドを大きなファイルに対して 使用したり、もっと簡単な他の復旧ツールを使うといったことが可能になる でしょう。

## 一連のプロセスを自動化するツールはないですか？

あることはあります。ただ残念ながらそれらには、手動で i-node をいじる場合と 同じ問題があります。つまり、間接ブロックのデータを復旧できなないのです。 しかし前述したように、これは遠からず問題にならなくなるかもしれないので、 ここでそうしたプログラムをざっと眺めてみる価値はあります。

著者は、`e2recover` というツールを作成しました。これは Perl で書かれた、実質的には `fsgrab` のラッパーです。 間接ブロックのブロック番号がゼロにされてしまった場合でもファイルを取り扱える よう工夫をこらしてあり、フラグメンテーションさえなければ、非常によい 結果をもたらすと思います。また復旧したファイルのパーミッション (および可能 ならファイル所有者についても) を正しく設定し、復旧されたファイルが 元のファイルサイズと一致しているか否かも確認します。

`e2recover` は、来るべきこの文書の改訂版のために 作成されました。つまり今のところ、`e2recover` 用の詳しい解説は、この文書の次回の改訂の際に記載するめどしか立っていない ということです。ただ、それはそれとして、現状でも `e2recover` は役に立つはずです。ダウンロードする場合は 著者のウェブサイト をお使いください。近いうちに Metalab からもダウンロードできるようになる と思います。

Scott D. Heavner は、`lde`、すなわち the Linux Disk Editor の作者です。これは、バイナリディスクエディタであると同時に、 `debugfs` の代替ツールとして ext2 と minix ファイル システム、および xia ファイルシステムを扱うことができます (ただし 2.1.x や 2.2.x カーネルでは xia のサポートは打ち切られています)。 そして、ファイルの一連のブロックを辿ったり、ディスク情報を検索したり して、ファイルの復旧作業を手助けする機能も付いています。また、ファイル システムの基本的な概念を述べた良質の文書と、復旧の際のツールの使い方を 述べた分かり易い文書とが添付されています。`lde` の バージョン 2.4 は、 Metalab やそのミラーサイト、および 作成者のウェブサイト から入手できます。

mc . This is a full-screen file management tool, based AFAIK on a certain MS-DOS program commonly known as `NC'. `mc` supports the mouse on the Linux console and in an xterm, and provides virtual file systems which allow tricks like `cd`-ing to a tarfile. Among its virtual file systems is one for ext2 undeletion. It all sounds very handy, although I must admit I don't use the program myself -- I prefer good old-fashioned shell commands. ]]> もうひとつの方法として、GNU Midnight Commander, `mc` が あります。これはフルスクリーンのファイル管理ツールであり、著者の知る限り、 'NC' と呼ばれていた MS-DOS プログラムがベースになっています。`mc` は、Linux コンソールや xterm 上でのマウス操作をサポートし、 さらに仮想ファイルシステムを提供することで、tar ファイル内にディレクトリ を移動するといったトリックも可能になっています。その仮想ファイルシステム の機能のひとつとして、ext2 でのファイルの復旧があります。使いやすさは 抜群です。ただ、実際のところ、著者自身は使ってはいません。古き良きのシェル コマンド方式のほうが好きだからです。

-`-with-ext2undel` option; you'll also need the development libraries and include files that come with the `e2fsprogs` package. The version provided in Debian GNU/Linux is built in this way; the same may apply to packages for other Linux distributions. Once the program is built, you can tell it to `cd undel:/dev/hda5`, and get a `directory listing' of deleted files. Like many current undeletion tools, it handles zeroed indirect blocks poorly -- it typically just recovers the first 12k of long files. ]]> 復旧機能を使うには、プログラムを `--with-ext2undel` オプションを付けて configure を実行する必要があります。また `e2fsprogs` に付属する開発用ライブラリとインクルードファイルも 必要になります。Debian GNU/Linux に同梱されている `mc` は、予めこのオプション付きで ビルドされています。他のディストリビューションでもおそらく同じだと 思われます。プログラムのビルドが済んだら、`mc` に `cd undel:/dev/hda5` と命令して、削除ファイルの一覧を 見ることができます。今日の多くの復旧ツール同様、`mc` もゼロ埋めされた間接ブロックを上手く扱えません。一般に、大きなファイル の冒頭 12k だけしか復旧できないこともあります。

最新バージョンは、 the Midnight Commander の ftp サイトからダウンロードできます。

## あとがき

書くための時間と、それに値するトピックがある限り、この文書を定期的に更新 していきたいと思っています。つまり、著者は読者からのコメントを切望して います。文章がもっと分かり易くならないか、より簡単に解決する方法はないか、 自動処理をする新しいツールが出ていないか等々、この文書や `fsgrab` 、`e2recover` ツールに関する話題があれば、 是非 <aaronc@pobox.com> までメールをお送りください。

## Credit と参考文献

> Isaac Newton

> もし私が他人よりも遠くを見ているとするなら、それは、私が巨人たちの 肩の上に立っているからだ。

この mini-HOWTO は、Robin Glover <swrglovr@met.rdg.ac.uk< が comp.os.linux.misc に投稿した記事を元にしています。彼のアイデアをもとに この mini-HOWTO を作ることを許可してくれた Robin に感謝します。

またここで、本書について著者にメールを送ってくれたすべての人にも、 もう一度感謝します。励ましのコメントをいただいて、報われる 思いです。

以下に、参考文献をいくつか紹介します。

- Frisch, Aeleen (1995), Essential System Administration, second edition, O'Reilly and Associates, Inc., ISBN: 1-56592-127-5. ]]> `Frisch`, Aelenn (1995), Essential System Administration, second edition, O'Reilly and Associates, Inc.. ISBN: 1-56592-127-5

- `Garfinkel`, Simson, Daniel `Weise` and Steven `Strassmann` (1994), The Unix-Haters Handbook, IDG Books, ISBN: 1-56884-203-1.

  この文書の大半は、「自分たち」のオペレーティングシステムが Unix よりも ずっと優れている考える人たちによる、子供じみたたわごとにすぎず、残りの大半も、 GNU のような出来のいいユーザ空間を持っている場合には全く当てはまらないこと ばかりです。とはいえ、くだらない記述の中にも、光るものがないわけでは ありません。例えば、Unix 上でのファイルの削除がどれほど安易に行われ得るか についての議論は、読むに値します。

- `Glover`, Robin (31 Jan 1996), HOW-TO : undelete linux files (ext2fs/debugfs), comp.os.linux.misc Usenet posting.

- `Peek`, Jerry, Tim `O'Reilly`, Mike `Loukides` et al (1993), UNIX Power Tools , O'Reilly and Associates, Inc./Random House, Inc., ISBN: 0-679-79073-X. Second edition, 1998.

## 法的な事柄

商標はすべて、それぞれの所有者がその権利を保持しています。特に、

- MS-DOS と Windows は、Microsoft の商標です。

- UNIX は、the Open Group の商標です。

- Linux は、USA その他の国における Linus Torvalds の商標です。

この文書は、Copyright (c) 1997, 1999 Aaron Crane <aaronc@pobox.com> が著作権を保持しています。この文書の頒布については、この著作権表示自体を 含めた全文全体として、自由に頒布することを許可します。しかし、文書の改変は、 著者もしくは Linux Documentation Project の HOWTO コーディネータの許諾が ない限り行わないでください。批評や引用目的で、この文書の僅かな部分を逐語 的に複製することは認められています。その場合、文書の複製に際し、引用元を適切に 表示するならば、この著作権表示を記載する必要はありません。

この文書をコンピュータ用の媒体もしくは印刷媒体で販売しようとする場合、 著者自身か Linux HOWTO コーディネータにその計画を事前に知らせてくれるよう 希望します。ただ、これはあくまで希望です。

現在の Linux HOWTO コーディネータは、Tim Bynum < linux-howto@metalab.unc.edu> です。

## 日本語訳について

誤字・脱字・誤訳等は <JF@linux.or.jp> までお知らせください。

- 翻訳：
- 吉川雅英 <kikkawa@m.u-tokyo.ac.jp> (1997/10/16)
- 校正：
- 中野武雄
- 菊谷誠
- 更新：
- 千旦裕司 <ysenda@pop01.odn.ne.jp> (2001/09/11)
- 校正：
- 中野武雄 <nakano@apm.seikei.ac.jp>