$ cat important_file > /dev/null &
[1] 9711
$ rm important_file
$ killall -STOP cat
[1]+ Stopped cat important_file > /tmp/p
$ ls -l /proc/`pidof cat`/fd/
total 0
lrwx------ 1 vi vi 64 May 13 20:32 0 -> /dev/pts/29
l-wx------ 1 vi vi 64 May 13 20:32 1 -> /tmp/p
lrwx------ 1 vi vi 64 May 13 20:32 2 -> /dev/pts/29
lr-x------ 1 vi vi 64 May 13 20:32 3 -> /home/vi/important_file (deleted)
如何恢复important_file
?
我尝试过类似
injcode -m dup2 -ofd=3 -ofilename=/tmp/recovered_file -oflags=O_CREAT $PID_OF_CAT
但它什么也不做。
答案1
如果 /home 是 NFS,则 /home/vi 中将有一个 .nfsNNNNNNNNNN 文件,您可以访问/复制该文件。如果 home 是本地文件系统,则应该能够通过 /proc/PID/fd/3 链接执行相同的操作:
cp /proc/PID/fd/3 /tmp/recovered_file
如果你想真正取消删除该文件,这里有一个博客文章就此主题而言。
答案2
... 比在给定时间进行复制(并且仅收集该文件内容在该时间的快照)更好的方法是将该tail -f
文件“”复制到一个新文件中:
tail -c +0 -f /proc/PIDofProgram>/fd/# > /new/path/to/file
(感谢 tail 的细心程序员,它甚至可以处理二进制输出。)
在运行时, 本身tail -f
会保持文件打开,从而安全地防止原始程序结束时文件被从磁盘清除。因此,不要tail -f
在原始程序结束后立即停止 —— 首先检查 tail'ed/new/path/to/file
是否是你想要的。如果不是(或者由于其他原因不令人满意),你可以再次复制原始文件,但这次后对它的所有写入均已由“程序”完成,并且来自仍在运行的tail -f
/proc/PIDoftail/fd/ 目录。
答案3
使用 lsof 查找 inode 编号,然后使用 debugfs 重新创建指向它的硬链接。例如:
# lsof -p 12345 | grep /var/log/messages
syslogd 12345 root 3w REG 8,3 3000 987654 /var/log/messages (deleted)
# mount | grep var
/dev/sda2 on /var type ext3 (rw)
# debugfs -w /dev/sda2
debugfs: cd log
debugfs: ln <987654> tmp
debugfs: mi tmp
Mode [0100600]
User ID [0]
Group ID [0]
Size [3181271]
Creation time [1375916400]
Modification time [1375916322]
Access time [1375939901]
Deletion time [9601027] 0
Link count [0] 1
Block count [6232]
File flags [0x0]
...snip...
debugfs: q
# mv /var/log/tmp /var/log/messages
# ls -al /var/log/messages
-rw------- 0 root root 3301 Aug 8 10:10 /var/log/messages
在您抱怨之前,我伪造了上述记录,因为我现在手头没有已删除的文件;-)
我使用mi
将删除时间和链接计数重置为合理值(分别为 0 和 1),但它无法正常工作 - 您可以看到链接计数保持在零ls
。我认为内核可能正在缓存 inode 数据。为了安全起见,您应该在使用 debugfs 后尽早进行 fsck。
根据我的经验,您应该使用临时文件名创建链接,然后重命名为正确的名称。直接将其链接到原始文件名似乎会导致目录损坏。YMMV!
答案4
如果您的文件仍在被写入(因此您不能使用cp
),而您没有使用ext
(因此您不能使用debugfs
),那么这里有一种不可知的方法来实现它。
但是,一旦您使用ps aux | grep
或找到该过程,并且您已经通过仔细检查了已删除的文件名,lsof -p $pid -a -d $fd
您可以使用以下方法生成一个新过程,其唯一的工作是恢复它:
(tail -f -n +1 --pid $pid /proc/$pid/fd/$fd) >$file </dev/null 2>&1 &
奇怪的包装只是为了防范HUP
,所以请随意不使用它或将其换成nohup