削除したファイルをlsofで復元する

たとえば、あなたが午後いっぱいを費やして製作したオーディオ・ファイルの再生を楽しみながら、「やあ、こいつはすごい音だ! こっちに移動しておくとしよう」と思ったとする。そのとき、潜在意識が違和感を唱えるのだ。「ええと、mvだっけ、rmじゃないのか?」…やっちまった。そのショックはよくわかる。誰にでもありうる失敗だ。だが、失われたファイルを取り戻す簡単な方法はある。しかも、それはどの標準Linuxシステムでも使える方法だから、覚えておいて絶対に損はない。

簡単に説明すると、Linuxファイルシステム上にあるように見えるファイルは、実際はinodeへのリンクに過ぎない。inodeには、ファイルのあらゆるプロパティ(アクセス権や所有権など)のほか、ファイルの中味が実際に存在するディスク上のデータブロックのアドレスも記録される。rmコマンドでファイルを削除すると、ファイルのinodeを指すリンクは削除されるが、inodeそのものは削除されない。削除した時点で、他のプロセス(オーディオ・プレーヤーなど)でファイルがまだ開かれている場合もある。このようなプロセスがすべて終了し、すべてのリンクが削除されるまで、inodeとそれに関連付けられたデータブロックが書き込みの対象となることはない。

このように実際のファイルが削除されるまでタイムラグがあることは、すばやい復元の成功にとって重要なポイントだ。ファイルを開いているプロセスがあれば、データはまだ存在する。たとえディレクトリリストからファイルが消えてしまっていても。

ここで、Linuxプロセス擬似ファイルシステム、つまり/procディレクトリの出番となる。システムに存在するプロセスは、自分と同じ名前のディレクトリをこの/procディレクトリ下に持つ。そのディレクトリにはさまざまなものが入っているが、その1つであるfd("file descriptor")サブディレクトリにはプロセスで開かれているすべてのファイルへのリンクが含まれる。ファイルをファイルシステムから削除した後も、データのコピーは以下の場所にある。

/proc/process id/fd/file descriptor

どこに行けばよいかは、ファイルを開いているプロセスのIDとファイル記述子を取得するとわかる。これらの情報は、lsofで取得が可能だ。lsofという名は"list open files"(開いているファイルをリストする)に由来する(実際にはそれ以上の機能があり、便利なツールなのでほとんどのシステムに標準でインストールされる。インストールされていない場合は、最新のバージョンを作者から直接入手できる)。

lsofから情報を取得すれば、データを/procからコピーできる。これで終わりだ。

以上の操作は実習してみるのが一番わかりやすいだろう。まず、削除してから復元するサンプルのテキスト・ファイルを作成する。

$ man lsof | col -b > myfile

次に、今作成したファイルの内容を表示してみよう。

$ less myfile

lsofの長大な manページが、 lessによってプレーンテキスト形式で出力される。

次にCtrl-Zを押して、lessの出力を中断する。シェルプロンプトで以下のコマンドを実行して、ファイルが存在することを確認する。

$ ls -l myfile
-rw-r--r--  1 jimbo jimbo 114383 Oct 31 16:14 myfile
$ stat myfile
  File: `myfile'
  Size: 114383          Blocks: 232        IO Block: 4096   regular file
Device: 341h/833d       Inode: 1276722     Links: 1
Access: (0644/-rw-r--r--)  Uid: ( 1010/    jimbo)   Gid: ( 1010/    jimbo)
Access: 2006-10-31 16:15:08.423715488 -0400
Modify: 2006-10-31 16:14:52.684417746 -0400
Change: 2006-10-31 16:14:52.684417746 -0400

よし、確かに存在する。さて、次に大失敗をやらかすことにしよう。

$ rm myfile
$ ls -l myfile
ls: myfile: No such file or directory
$ stat myfile
stat: cannot stat `myfile': No such file or directory
$

ファイルは消えた。

この時点では、ファイルを使用しているプロセスは残しておく必要がある。そうしないと、ファイルが本当に削除され、トラブルが深刻になる。このデモで使っているバックグラウンドのlessプロセスは、キルするかシェルを終了しない限りどこにも行かないが、ビデオやサウンドのファイルを再生している場合は、ファイルを削除したことに気が付いた時点ですぐにアプリケーションの再生を一時停止するか、他の手段でプロセスをフリーズする必要がある。そうすれば、ファイルの再生がやがて終了し、プロセスが終了することを避けられる。

さて、ファイルの復元に取りかかろう。最初に、lsofの出力を見てみる。

$ lsof | grep myfile
less      4158    jimbo    4r      REG       3,65   114383   1276722 /home/jimbo/myfile (deleted)

1列目はプロセスに関連付けられたコマンドの名前、2列目はプロセスID、4列目の数字はファイル記述子である("4r"の"r"は"regular file"、つまり通常のファイルを意味する)。この出力からわかるのは、プロセス4158がまだファイルを開いていること、ファイル記述子が4であることだ。あとは、この情報を/procからコピーする方法さえわかればよい。

ファイルを復元するのだから、cp-aフラグを使えばいいと思うかもしれない。だが、実際にはそれをしないことが重要なのである。もしこのコマンドを実行すると、ファイル内のリテラル・データをコピーするのではなく、元のディレクトリにリストされていた、今は壊れてしまったファイルへのシンボリック・リンクをコピーすることになる。

$ ls -l /proc/4158/fd/4
lr-x------  1 jimbo jimbo 64 Oct 31 16:18 /proc/4158/fd/4 -> /home/jimbo/myfile (deleted)
$ cp -a /proc/4158/fd/4 myfile.wrong
$ ls -l myfile.wrong
lrwxr-xr-x  1 jimbo jimbo 24 Oct 31 16:22 myfile.wrong -> /home/jimbo/myfile (deleted)
$ file myfile.wrong
myfile.wrong: broken symbolic link to `/home/jimbo/myfile (deleted)'
$ file /proc/4158/fd/4
/proc/4158/fd/4: broken symbolic link to `/home/jimbo/myfile (deleted)'

そこで、代わりに単純な古きcpでコピーする。

$ cp /proc/4158/fd/4 myfile.saved

最後に、目的が達成されたことを確認する。

$ ls -l myfile.saved
-rw-r--r--  1 jimbo jimbo 114383 Oct 31 16:25 myfile.saved
$ man lsof | col -b > myfile.new
$ cmp myfile.saved myfile.new

cmpで不一致は報告されない。復元はまごうことなき本物である。

余談だが、失われたファイルの復元以外にも、 lsofにはたくさんの便利な機能がある。

NewsForge.com 原文