首页 > Sysadmin > 正文

Linux 恢复被删文件:先停写入再挑工具

huyuanwen 2026-10-26 Sysadmin

删除只是去掉名字并释放数据块,块要等被新写入覆盖才真正消失;能不能救,看名字还在不在、块有没有被复用。

先判断还有没有救

stat -c 'blocks=%b size=%s' big.log
truncate -s 0 big.log
stat -c 'blocks=%b size=%s' big.log

“清空文件”和”删除”是两回事:truncate 把 size 置零、数据块被释放后,文件本身读不到任何内容。实测(WSL Ubuntu 24.04.4)一个 15 字节文件的 stat 块数从 8 变成 0,cat 输出为空——内容只能靠扫空闲块找回。

元信息层面,CGSecurity 的说法是:文件被删后文件名、时间、大小、第一个数据块的位置都会丢(ext3/ext4 里目录项中的名字往往还在,但数据块位置被抹掉),数据只在被新数据覆盖之前还在 [2]。所以分两条路:靠名字用文件系统工具,靠内容用签名 carving。Ubuntu noble 的 testdisk 包描述就是 “Partition scanner and disk recovery tool, and PhotoRec file recovery tool”,实测 dpkg -L testdisk 列出 /usr/bin/photorec,包内确实带 photorec [3]。

进程还开着这个文件时:走 /proc/PID/fd

unlink(2) 的措辞是:删除的只是名字,如果还有进程打开这个文件,文件会一直存在到最后一个引用它的文件描述符关闭 [4];proc(5) 说明 /proc/PID/fd 下每个条目是指向实际文件的符号链接 [5]。这就是删除后还能立刻取回内容的窗口。

ls -l /proc/PID/fd/0
readlink /proc/PID/fd/0
cat /proc/PID/fd/0 > /other-disk/recovered.txt

实测:文件被 unlink 后 readlink /proc/PID/fd/0 显示 ... (deleted),cat 和 cp 都能拿到原内容;进程一关,同一入口立刻消失,这条路就没了。权限上,读这些符号链接受 ptrace 读取检查约束,非特权进程可能读不到别人的 fd [5]。

立刻停止写入,恢复结果写到别的设备

sudo umount /data
dd if=/dev/sda2 of=/mnt/other-disk/sda2.img bs=4M
photorec /log /d /mnt/other-disk/recoup /cmd /mnt/other-disk/sda2.img fileopt,everything,enable,freespace,search

CGSecurity 手册对 /d 的原话是输出目录 “should be on a device different from the one you are recovering from” [1]——在被删数据所在分区上继续写,等于用恢复出来的文件去覆盖还没扫到的数据。

在正在使用的分区上直接 mount -o remount,ro 经常失败:实测在 WSL 的 ext4 根分区执行时报 mount point is busy,只能先卸载或从别的介质启动。顺带一个值得注意的点:该根分区的挂载参数里带 discard,ext4(5) 手册说 discard 控制块释放时是否向设备发 discard/TRIM [8],SSD 上被 TRIM 过的块不能指望还能扫到(这条是文档推论,未实测)。

PhotoRec:按签名 carving,名字基本保不住

CGSecurity 手册给出非交互写法 photorec /log /d DIR /cmd DEVICE 命令列表,命令按顺序逗号分隔 [1]。

实测(PhotoRec 7.1,8 MB 原始镜像,把一张真实 PNG 写进空闲区再扫 freespace):恢复出 1 个文件,名字是 f0004096.png,原始文件名完全没保住;指定的输出目录被追加成 recoup.1,手册说 PhotoRec 会给目录加数字后缀并从 .1 开始 [1];同目录生成 report.xml 记录恢复清单。另外两个实测细节:结构不完整的文件会被拒绝(日志 1 invalid files found and rejected);把签名前 8 字节覆盖后再扫同一个镜像,结果 0 个文件。

extundelete:靠 journal,名字可能保住,但不承诺

extundelete /mnt/other-disk/sda2.img --restore-all

extundelete 只处理 ext3/ext4,用分区 journal 里存的信息尝试恢复,手册原话是 “There is no guarantee that any particular file will be able to be undeleted”;--restore-all 尽量按删除前的名字还原,还原不了的写成 file.NNNN [6]。它也接受 dd 做的分区镜像,所以先做镜像再跑更稳。实测(0.2.4-3build2):对不是 ext 的文件系统跑,直接报 Bad magic number in super-block。

debugfs 的 list_deleted_inodes(lsdel)只能列出已释放的 inode,手册明确说这条对 ext2 的误删有用,对 ext3/ext4 没用,因为 inode 释放后数据块位置已不可用 [7](这条只核对了文档,尚未实测)。

回滚、排错速查与局限

| 现象 | 原因 | 处理 | |—|—|—| | 恢复出来全是 f0001234.png 这类名字 | carving 只认签名 | 走名字路线:extundelete / TestDisk | | 恢复结果为空 | 块已被新写入覆盖或被 TRIM | 扫镜像,不扫正在写的原设备 | | 输出目录出现 .1、.2 | 同名目录已存在,PhotoRec 自动加后缀 | 每次换新输出目录 | | remount ro 报 busy | 文件系统正被使用 | 先 umount,或从别的介质启动 | | 文件只恢复出一半 | 碎片化或结构校验失败 | 按 report.xml 核对,重扫 |

回滚依据:恢复前用 dd 做的分区镜像就是回滚点,重跑只扫镜像、不碰原设备;被恢复的文件写到别的设备,原分区保持只读。

四个常见问题:清空(truncate)过的文件还能救吗——块没被复用的话靠签名 carving 可能捞回,文件本身已读不到内容;rm 之后越早停写入越容易恢复,因为块还没被覆盖;SSD 上被 TRIM 的块不能指望;恢复工具不能在正在写数据的分区上跑。

状态:草稿待审。实测环境 WSL2 Ubuntu 24.04.4(kernel 6.6.114.1-microsoft-standard-WSL2)、PhotoRec/TestDisk 7.1、extundelete 0.2.4、e2fsprogs 1.47.0,日期 2026-10-26。已实测:/proc/PID/fd 取回被删文件、truncate 后块归零、PhotoRec 脚本化 carving(含输出目录 .1、无效文件被拒、覆盖签名后扫不到)、extundelete 对非 ext 文件系统报错、busy 时无法 ro 重挂载、testdisk 包内含 photorec。仅文档核查尚未执行:ext3/ext4 按名恢复、TRIM 后不可恢复、debugfs lsdel、真机重启与实盘 SSD。


参考资料

文中主要命令已在 WSL Ubuntu(24.04)实测,个别条目(未装工具/安装类命令)仅核对官方文档,未实际执行。