削除したファイルを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
にはたくさんの便利な機能がある。