XFS误删文件恢复实战:从inode原理到xfs_undelete工具使用
发布时间:2026/10/9 4:21:04 锦皓数字建站

简介面向 Linux 运维与系统开发人员的一份技术文献聚焦 XFS 文件系统中的文件误删除场景讲解目录项、索引节点与数据块的删除机制并给出数据保护、只读挂载、分区备份及 xfs_undelete 工具恢复的完整思路。内容源自 2021.02《网络安全和信息化》故障诊断与处理栏目对需要处理生产环境误删事故的工程师有直接参考价值。资源为单个 PDF 文档压缩包仅 1 个文件大小 951KB便于下载后直接阅读或归档。除 xfs_undelete 的使用外文中还涉及 Tcl 8.6 与 Tcllib 的环境准备、常用 mount/dd 命令等关键操作适合作为 Linux 文件系统维护与数据恢复主题的参考文献。已有 2874 人学习下载说明该主题受到较多运维人员关注是一份可快速查阅的实战型资料。1. 误删文件别急着写盘XFS 恢复的时间窗口比你想象的长Linux XFS 文件系统误删除文件恢复核心结论先放在前面在 XFS 上执行 rm 删除文件操作系统并不会立刻擦掉磁盘上的索引节点和数据块内容只是把目录项里指向该 inode 的入口摘掉了。这意味着被删文件的数据在磁盘上大概率还是完整的真正决定你能不能找回的是删除之后你做了什么。如果你继续以读写方式挂载分区跑数据库、写日志、下载东西那些刚释放的数据块随时可能被重新分配出去一旦被覆盖神仙工具也救不回来。这篇文章面向两类人一类是不小心在 shell 里 rm 删了重要文件的运维或开发另一类是想搞清楚 XFS 恢复工具链怎么搭、依赖怎么装、坑在哪的从业者。下面按我实际恢复操作的顺序来讲原理、保护、工具、踩坑以及最后一招兜底技巧。2. XFS 删除原理与恢复可行性目录项、inode 与数据块的关系2.1 文件在 XFS 上是怎么组织的要把恢复这件事讲透得先从文件的底层结构说起。Linux 文件系统里的一个文件一般由三部分组成目录项dentry、索引节点inode和数据块block。目录项负责记录文件的管理与组织信息包含文件名、文件类型、权限这类元数据以及最重要的东西——该文件对应的 inode 编号。inode 里存的是文件属性包括大小、时间戳、属主属组还有指向数据块的指针列表。数据块才是真正存文件内容的地方一般每个块是 4KB 或更大取决于 mkfs.xfs 创建文件系统时指定的块大小。当你在 shell 里执行 rm 删除一个文件时操作系统做的事情非常克制它只删除了目录项中该文件名所对应的那条索引记录把 inode 编号从目录里摘掉然后把这个 inode 标记为已释放。至于 inode 本身的内容比如权限、时间戳、块指针以及数据块里存的那些字节统统原封不动地留在磁盘上。这就是数据恢复的理论基础——删除操作其实是一次断链而不是擦除。XFS 作为企业级文件系统在这方面和 ext4 的逻辑是相似的区别在于它的元数据管理更复杂数据块分配策略也更激进这对恢复操作提出了更高要求。这里要特别提醒一个容易误解的点很多人以为进了回收站才算安全在图形桌面里删文件确实会移到 Trash 目录但 shell 里用 rm 删除是直接越过回收站的。所以先搞清楚自己的文件是哪种方式删掉的如果是 rm 删的下面这套流程才是你的后悔药。2.2 为什么立刻停止写入是恢复的第一原则了解了删除原理就该明白恢复的关键约束了数据块不被覆盖恢复就有戏被覆盖了就是神仙难救。XFS 的块分配器在文件删除后会把释放的块空间重新纳入分配池一旦有新的写入请求这些块可能被分配给新文件。对于繁忙的服务器系统每秒可能都在产生日志、临时文件、数据库 WAL 写入被删除文件的数据块在几分钟甚至几秒内就可能被重新分配掉。所以误删之后的时间线应该是这样的发现误删的第一秒立即停止对所在分区的所有写操作。如果被删文件在根分区最狠的保护措施是直接关机需要启动时用单用户模式把根分区以只读方式挂载。被删文件在其他分区那就把该分区立即以只读方式重新挂载或者直接卸载。对于没条件关机的服务器至少要做到断网或者停掉业务进程中断用户对分区的写操作。我见过不少人误删后第一反应是去网上搜索工具等工具下好了、依赖装完了数据块也已经被覆盖了大半这时候跑恢复工具只能扫出一堆残缺文件。有一个操作顺序值得强调先保护再备份备份完再做恢复尝试。保护的目的是让当前磁盘状态冻结在删除后的那一刻备份的目的是给后续的恢复工具实验留一个副本。直接在原始分区上反复跑多个恢复工具本身就是一种写操作可能会造成二次破坏。把分区镜像成一个文件在这个镜像文件上做各种恢复实验是最稳妥的路径。2.3 XFS 与 ext4 恢复工具的选型差异很多从 ext4 转过来的运维第一反应是去用 extundelete 或者 debugfs跑到 XFS 分区上一看工具直接报不支持。这是文件系统元数据结构的差异决定的ext4 的恢复工具没办法理解 XFS 的 B 树结构和日志机制。XFS 上的恢复工具选择本来就少开源的主要就是 xfs_undelete 和 PhotoRec 两条路线。xfs_undelete 是 Tcl 脚本写的思路是通过解析 XFS 的 inode 节点找出那些已释放但未被覆盖的 inode然后按删除时间命名恢复文件。PhotoRec 是通用文件恢复工具不看文件系统元数据直接扫描数据块的特征头根据文件签名识别文件类型。这两者的恢复逻辑完全不同xfs_undelete 恢复出来的文件保留相对完整的元信息PhotoRec 靠内容识别文件类型但完全不关心原始文件名和目录结构。实际操作中两者并不冲突先跑一个再用另一个补漏能提高找回率。3. 数据保护与备份误删后的第一步操作3.1 只读挂载的正确姿势与 busy 处理发现文件被误删后第一件事就是让分区进入只读状态。具体操作以 CentOS 7.7、/home 在 /dev/sda2 分区上为例。用 mount 命令重挂载为只读mount -o remount,r /dev/sda2执行完用 mount 命令确认输出里有没有 rw 字样确保真的切到了只读。这里有个细节有些发行版上 remount 必须带完整的挂载参数否则会报错或静默失败我一般习惯写成mount -o remount,r /dev/sda2 /home把挂载点也带上兼容性更好。如果分区正在被进程占用remount 会提示 target is busy。你当然可以一个个去排查是哪个进程占用的但更常见的是直接用 fuser 强制清理fuser -kmiv /dev/sda2这个命令会向所有正在使用该分区的进程发送 SIGKILL 信号i 参数表示交互模式v 显示详细信息。在生产环境上执行前一定确认这个分区上没有跑关键业务否则进程会被直接杀掉。我在一次实际操作中就是没注意这个分区上挂着 Java 应用fuser 一条命令把 JVM 干掉了恢复完文件还得去拉起服务。更稳妥的做法是先用fuser -v /dev/sda2看有哪些进程占用确认可以清理后再加 -k 参数。3.2 dd 备份与空间核查分区进入只读状态后下一步是备份整个分区。这个步骤不能省因为后续的恢复工具都可能在分区上产生读写操作尤其是 xfs_undelete 这类工具在扫描恢复过程中可能会尝试修改文件系统的某些状态。先备份一份完整镜像实验都在镜像上做原始分区始终保持只读。dd if/dev/sda2 of/root/sda2.img bs4M statusprogress备份前先检查目标位置的空间是否足够df -h /root ls -lh /dev/sda2如果 /root 空间小于分区总容量dd 写到一半就会报错 No space left on device不仅镜像不完整前面的努力也白费。更合理的目标位置是另一块独立磁盘或网络存储避免把备份写进同一个分区——这在极端情况下会覆盖被删除文件的数据块。bs4M 是块大小参数比默认的 512 字节大得多dd 的读写次数更少整体速度快不少。statusprogress 是 GNU coreutils 8.24 才有的参数老版本 CentOS 上用不了改成不带这个参数它也会显示统计信息只是看不到实时进度。备份完建议用file /root/sda2.img验证镜像文件头是否正常再用losetup --read-only -f /root/sda2.img挂载成只读 loop 设备来测试确认镜像可读后再对镜像做后续恢复操作。这一步在后续工具失败时很有用因为你可以随时回到这个干净镜像重新来。3.3 镜像挂载与恢复后的状态还原备份完成后恢复工具可以直接操作原始块设备也可以操作镜像文件。我的习惯是先对镜像做一次恢复尝试确认工具能正常扫描出文件再决定要不要直接在原始分区上操作。xfs_undelete 支持从块设备或 XFS 镜像恢复文件PhotoRec 也支持扫描 dd 出来的镜像文件。把实验对象从物理分区换成镜像文件好处是就算工具运行过程中发生异常也不会对原分区造成任何影响。恢复工作全部结束后重新挂载回读写模式mount -o remount,rw /dev/sda2 /home这里要注意恢复出来的文件通常不能直接回到原来的位置需要先确认恢复文件完整无误再手动移动或替换。恢复工具不会帮你重建目录结构文件都是平铺在一个恢复目录里所以还原阶段往往是手工活一个个检查、改名、归档。4. xfs_undelete依赖安装与文件恢复实战4.1 前置依赖检查Tcl 版本是第一道坎xfs_undelete 是 Tcl 脚本工具从 GitHub 上下载解压后不需要编译直接运行。但它对运行环境有硬性要求Tcl 8.6 及以上版本、Tcllib 库、GNU coreutils系统自带。CentOS 7.7 系统自带的 Tcl 是 8.5恰好不满足要求。先检查本机情况rpm -qa tcl tclsh % puts $tcl_version 8.5 % exit第一条命令看 Tcl 是否安装没有输出就是没装。tclsh 命令存在说明 Tcl 核心可用进入交互模式后 puts 输出版本号。8.5 这个版本就是 xfs_undelete 跑不起来的典型原因。需要注意rpm -qa tcl只能检查 RPM 包安装情况如果你是用源码编译装的 Tcl这个命令可能查不到直接执行 tclsh 看版本就行。4.2 编译 Tcl 8.6 与软链接处理Tcl 8.6 的源码可以从 SourceForge 或官方仓库下载这里以 tcl8.6.10 为例。很多人到这一步就开始纠结编译参数其实 Tcl 的编译很干净默认参数就行wget https://downloads.sourceforge.net/project/tcl/Tcl/8.6.10/tcl8.6.10-src.tar.gz tar -zxvf tcl8.6.10-src.tar.gz cd tcl8.6.10/unix ./configure --prefix/usr/local make -j$(nproc) make install默认参数编译安装后Tcl 的可执行文件会装在 /usr/local/bin/tclsh8.6而系统原有的 tclsh 命令还指向旧版本。这里有个关键操作处理好 tclsh 的软链接否则 xfs_undelete 运行时调用的还是系统自带的 8.5 版本。做法是先把原 tclsh 改名备份再创建软链接mv /usr/bin/tclsh /usr/bin/tclsh8.5 ln -s /usr/local/bin/tclsh8.6 /usr/bin/tclsh tclsh % puts $tcl_version 8.6做软链接时要确认 /usr/local/bin/tclsh8.6 这个路径真实存在不同版本号编译出来的可执行文件名后缀可能不一样用 ls 确认一下再 ln避免链接到不存在的文件。改完之后再用 puts 验证版本号确保 xfs_undelete 运行时拿到的是 8.6。4.3 Tcllib 安装与 TCLLIBPATH 导出Tcllib 是 Tcl 的标准库集合xfs_undelete 依赖其中的 cmdline 模块来解析命令行参数。没有 Tcllib 或没有配置库搜索路径运行时会直接报错 cant find cmdline package这是最典型的排错现场。Tcllib 的安装同样是默认参数wget https://sourceforge.net/projects/tcllib/files/tcllib/1.20/tcllib-1.20.tar.gz tar -zxvf tcllib-1.20.tar.gz cd tcllib-1.20 ./configure --prefix/usr/local make make install安装完成后需要让 Tcl 解释器能找到库文件的搜索路径。安装后的库文件一般在 /usr/local/lib/tcllib1.20 目录下通过环境变量 TCLLIBPATH 告诉 Tcl 去哪里找export TCLLIBPATH/usr/local/lib/tcllib1.20 echo export TCLLIBPATH/usr/local/lib/tcllib1.20 ~/.bashrc source ~/.bashrc这里有两个容易踩的细节。第一export 只对当前 shell 会话生效重启终端后就会丢失所以必须写入 ~/.bashrc我原文里写的是 ~/.bash但实际 CentOS 上更标准的是 ~/.bashrc两个文件都会被登录 shell 加载写成 .bashrc 更稳妥。第二TCLLIBPATH 的路径要和你实际安装的 tcllib 版本号一致我用的 1.20你装的是 1.21 就写 tcllib1.21路径对不上库还是找不到。依赖全部就绪后可以用一个简单命令验证环境是否完整tclsh package require cmdline; puts cmdline OK能输出 cmdline OK说明 Tcl 版本和库路径都没问题可以跑恢复工具了。4.4 运行 xfs_undelete 恢复文件依赖确认无误后就可以开始恢复操作了。xfs_undelete 支持从块设备或 XFS 镜像恢复文件这里以直接从 /dev/sda2 块设备恢复为例./xfs_undelete /dev/sda2运行后工具会开始扫描分区上的 inode 节点找出已释放但数据块未被覆盖的文件。在恢复过程中终端会持续显示已恢复文件的名称这些文件默认保存在当前目录下的 xfs_undeleted 目录中。恢复的文件以删除时间命名比如 2020-07-10-12-50_27124.txt 这个文件名就是按删除时间加 inode 号生成的。可以根据删除时间初步判断目标文件再查看内容确认。xfs_undelete 还支持几种常用的恢复方式通过 --help 可以看到完整参数列表。在实际使用中比较有用的是按文件类型过滤、按时间范围过滤以及指定起始 inode 号扫描。对于文件类型明确的场景比如确定误删的是一批 .pdf 文件用类型过滤能大幅缩短扫描时间。需要注意的是这个工具恢复不出原始文件名和目录结构只能还原文件内容。如果项目文件成百上千且目录层级复杂恢复后整理命名会是个大工作量。工具运行时会自动尝试将分区以只读方式挂载如果分区本来就是只读的那更好。恢复结束后重新以读写方式挂载分区正常使用。这里补充一点如果刚才已经做了 dd 镜像推荐优先对镜像文件跑恢复./xfs_undelete /root/sda2.img镜像文件的路径随意工具根据文件内容判断格式不需要额外指定参数。这样做的最大好处是不碰原始分区就算 xfs_undelete 运行中意外中断或行为异常原分区的数据状态完全不受影响。5. 避坑与排查误删恢复中最常见的五个翻车点5.1 现象恢复出来的文件全是损坏的打不开也没法用原因几乎可以肯定是误删后分区没有及时进入只读状态数据块被后续写入覆盖了一部分。覆盖一块文件就坏一块覆盖多块就是个空壳文件。恢复工具扫描到的可能只是残留的块指针读出来的数据是新的内容拼接旧的内容文件校验直接失败。解决方法是评估覆盖的范围有多大。如果只是零星覆盖可以尝试另一个工具从内容特征角度扫描Parted 这类按文件头和内容特征扫描的恢复工具有时候能捞出残留的完整文件。如果覆盖严重那就只能在备份中找到最近版本的副本了。这也验证了恢复领域的一句实话工具只能救回没被覆盖的数据数据保护永远排在工具前面。5.2 现象xfs_undelete 运行时报 cant find cmdline package这个报错的排查路径非常明确。cmdline 是 Tcllib 提供的模块报这个错说明 Tcllib 没有安装或者装了但没有把库文件路径告诉 Tcl 解释器。先检查 Tcllib 是否真的装上了ls /usr/local/lib 下有没有 tcllib 开头的目录。有的话检查 TCLLIBPATH 环境变量是否包含这个路径echo $TCLLIBPATH 看一眼。没有的话回到 export 和写入 ~/.bashrc 那一步。还有一个容易忽略的情况Tcl 8.6 编译安装时如果指定了不同的 --prefixtclsh 的路径和 TCLLIBPATH 的路径都要跟着变。解决后重新执行 package require cmdline 验证确认模块能被找到再跑 xfs_undelete。5.3 现象mount -o remount,r /dev/sda2 提示 target is busy分区被进程占用是最常见的原因。解决分两步先执行 fuser -v /dev/sda2 查看哪些进程在使用该分区确认为非关键进程后执行 fuser -kmiv /dev/sda2 强制终止最后重新 remount。这里要强调一个生产环境的操作纪律fuser -k 会无条件杀进程执行前把当前占用进程列表截图保存方便事后排查哪些服务需要重启。还有另一种情况是当前目录就在该分区内shell 自身占用导致 remount 失败先 cd 到其他目录再执行。经常有人忽略这一点在 /home 里执行mount -o remount,r /dev/sda2 /home结果 shell 的工作目录卡在分区内导致提示 busy切目录之后就正常了。5.4 现象恢复出来的文件名全是时间戳找不到目标文件这是 xfs_undelete 的工作方式决定的它恢复的文件以删除时间命名而不是原始文件名。所以面对大量恢复文件时一眼看过去全是日期时间加 inode 号完全不知道对应哪个文件。解决思路是先根据你的删除时间缩小范围xfs_undelete 恢复的文件命名就是精确到秒的删除时间回忆一下大概几点几分误删的直接按时间找。如果同一时间段删了大量文件就按扩展名区分txt 的、jpg 的、conf 的分类排查。最有效的还是文件内容定位用 file 命令看文件类型用 grep 搜索已知内容片段。比如你知道被删的脚本里有一个特定的函数名grep -r 函数名 恢复目录就能快速锁定目标。这个阶段的经验是恢复出的文件数量越多越要有一个系统化的排查方式否则几百个文件逐个打开看不现实。5.5 现象dd 备份过程中目标磁盘空间写满备份中断dd 备份前没有核实空间是常见失误。恢复操作的时间窗口本来就紧浪费在备份失败上极其尴尬。解决是一套组合动作先 df -h 确认目标挂载点的可用空间再 du -sh /dev/sda2 或直接看分区大小两者对比确认目标空间大于分区空间才动手。更稳妥的是把镜像写到独立磁盘或远程存储。dd 中断后已生成的镜像文件不完整file 命令检查会发现文件类型异常。这时可以选择换一个大空间目标重新备份或者干脆跳过镜像直接在原始分区上小心操作。当然如果前面已经做了保护分区保持只读状态直接跑 xfs_undelete 风险也可控。6. PhotoRec 与已打开文件的恢复两条兜底路径6.1 PhotoRec 的向导式操作与文件签名扫描xfs_undelete 解决不了所有问题特别是文件系统的 inode 信息被破坏时这时候 PhotoRec 是很好的替补。PhotoRec 的优势在于它不依赖文件系统的元数据直接扫描磁盘数据块的内容特征通过文件签名识别文件类型。它支持 XFS也支持其他几乎所有文件系统。从 cgsecurity.org 下载后不需要编译直接跑 photorec_static 二进制文件即可chmod x photorec_static ./photorec_static /root/sda2.img运行后是向导式文本菜单关键选项是选择分区类型时选 Other然后选择要扫描的文件类型。默认扫描所有类型如果只想找回图片或文档可以进入 File Opt 菜单按类型勾选。扫描完成后恢复的文件保存在 photorec 所在目录下的 recup_dir.1、recup_dir.2 等文件夹里每批最多 500 个文件。终端最后一行会显示恢复的文件数量和存储位置。PhotoRec 恢复的文件数量往往比 xfs_undelete 多原因是它按文件签名扫描能找回一些 inode 已经损坏的文件碎片但恢复出的文件是乱序编号没有原始文件名和目录结构整理成本也更高。6.2 通过 /proc/pid/fd 恢复已被删除的打开文件还有一种特殊情况我在实际工作中处理过多次文件被别人删了但某个进程还开着这个文件。Linux 内核在 /proc 目录下为每个进程创建一个以 pid 命名的子目录其中的 fd 子目录记录了该进程打开的所有文件描述符。文件被删除后只要进程没关闭 fd文件内容就还在内核缓存里完整可读。恢复方法非常简单先找到进程号和 fd 号然后复制出来。ls -l /proc/17114/fd/3 cp /proc/17114/fd/3 /home/slb/readme.txt cat /home/slb/readme.txtls 的输出里会看到 3 - /home/slb/readme.txt (deleted) 这样的标记括号里的 deleted 是关键信号。17114 是打开该文件的进程 pid比如 more 命令的进程号3 是文件描述符编号后面的 r 表示以只读方式打开。复制时要确认目标路径有空间cp 完成后立刻 cat 验证内容完整性。这个方法在生产环境特别实用比如运维误删了 log 文件但服务还在写或者误删了配置文件但应用进程还在运行都能用这个方式捞回来。前提是进程不能重启重启后 fd 就关闭了缓存没了这条路就断了。所以发现误删后第一反应不要是重启服务先查 lsof 看看有没有进程占着这个文件。6.3 无法启动系统的场景与收尾习惯如果系统已经无法启动还有一种恢复路径用 KNOPPIX 这类 Live CD 引导系统在分区没有损坏的情况下直接挂载分区把文件复制到外部存储。如果分区存在错误XFS 上可以先卸载分区再执行 xfs_repair 修复修复后再尝试挂载读取。还有一种情况是长期未使用的机器读取分区文件时出现 I/O 错误可以尝试dd if/dev/sda2 of/dev/sda2强制重读扇区触发磁头重新定位。这个操作不会写入新数据只是让硬盘自检一遍遇到坏道会触发重新映射没有额外风险。数据恢复这件事做多了会形成一套自己的流程惯性。我现在每次执行 rm 批量删除之前都会强制走一遍三问这些文件有没有在别的地方备份过删掉之后有没有进程还在占用如果误删了能不能承受后果从那以后我的生产服务器上所有 rm 命令都会先写成 mv 到 /tmp 目录观察 24 小时确认没问题再清理。重要目录的视频监控项目也在跑定时 xfsdump 备份xfs 文件系统用系统自带的 xfsdump 备份、xfsrestore 恢复这套组合拳比任何事后恢复都靠谱。希望这篇实操笔记帮到你误删之后按顺序操作找回数据的概率并不低。本文还有配套的精品资源点击获取
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。