记一次Btrfs文件系统只读事故

xeonds

2026-06-16 11:28:10

问题症状

系统运行中,Btrfs 文件系统突然变为只读,所有写入操作失败。查看 dmesg 发现以下关键错误:

BTRFS error: metadata reservation failed for delayed dir item deletion, error: -28
BTRFS: Transaction aborted (error -28)
BTRFS error: level verify failed on logical 1079556358144 mirror 1 wanted 1 found 2

随后文件系统自我保护,强制挂载为只读。

空间使用情况

Overall:
    Device size:        953.87GiB
    Device allocated:   663.06GiB
    Device unallocated: 290.81GiB
    Used:               649.06GiB

Data,single:    Size:641.00GiB, Used:632.19GiB (98.63%)
Metadata,DUP:   Size:11.00GiB, Used:8.43GiB  (76.65%)
System,DUP:     Size:32.00MiB, Used:112.00KiB(0.34%)

虽然整体空间未满(未分配 290GiB),但因元数据操作碎片化导致预留失败,触发事务中止,进而引发元数据树校验错误。

排查过程

  1. umount 失败 —— 大量 Docker 进程占用,设备 busy,无法直接卸载。
  2. 尝试 btrfs rescue zero-log 失败 —— 即使卸载后,内核仍持有设备引用,无法以独占方式打开。
  3. 尝试释放内核引用 —— 通过 rmmod btrfs、sysfs delete 等方式均失败(内核 Lockdown 或其他安全策略不适用)。
  4. 决定重启 —— 最终选择修改 /etc/fstab 注释掉对应挂载条目后重启系统。

最终解决方案

步骤一:注释 fstab 避免启动时自动挂载

sudo nano /etc/fstab
# 将对应 UUID 或设备行前加 # 注释

步骤二:重启系统

sudo reboot

步骤三:只读挂载并备份数据(可选)

sudo mount -o ro /dev/nvme0n1p1 /mnt
sudo rsync -av /mnt/重要目录 /备份路径

步骤四:清除损坏的事务日志

sudo umount /mnt
sudo btrfs rescue zero-log /dev/nvme0n1p1

步骤五:重新挂载为读写

sudo mount -o rw /dev/nvme0n1p1 /mnt

步骤六:验证文件系统健康

sudo btrfs scrub start -B /mnt
sudo btrfs balance start -dusage=5 -musage=5 /mnt

原因分析

内核在元数据碎片化严重时,对 ENOSPC(Error -28)的处理存在缺陷,导致事务中止后无法正确回滚,引发元数据树层级校验失败(level verify failed)。Btrfs 检测到元数据不一致后强制只读保护数据。

预防措施

  1. 定期 balance,避免元数据严重碎片化:btrfs balance start -dusage=10 -musage=10 /挂载点
  2. 保持内核更新,尽早使用修复了相关 bug 的长期支持版本(建议 5.15+ 或 6.1+)。
  3. 监控空间使用,避免数据块组长期处于高位使用率。

遇到类似问题的读者请优先备份数据,再尝试修复。