最近这台 ImmortalWrt x86 路由器出现了明显的空间问题,根分区 / 已经被打满,nlbwmon 开始报 No space left on deviceuhttpd 也出现了 Bus error。一开始看起来像是“硬盘太小”,但实际检查后发现并不是磁盘容量不够,而是系统根分区本身只分到了很小的一块空间。

这篇文章记录一下我是怎么把这台 ImmortalWrt 24.10.5 的根分区从 300M 扩到 5G 的,以及过程中踩到的坑。

问题现象

先看根分区状态:

1
df -h

当时的输出核心信息是:

1
2
Filesystem                Size      Used Available Use% Mounted on
/dev/root 290.4M 283.6M 860.0K 100% /

看起来像是系统盘很小,但实际磁盘并不小。继续查看磁盘分区:

1
2
fdisk -l /dev/sda
lsblk -o NAME,SIZE,FSTYPE,TYPE,MOUNTPOINTS

实际结果类似这样:

1
2
3
4
5
Disk /dev/sda: 238.47 GiB

Device Start End Sectors Size Type
/dev/sda1 512 66047 65536 32M Linux filesystem
/dev/sda2 66048 680447 614400 300M Linux filesystem

也就是说:

  • 整块硬盘有 238G
  • /boot32M
  • 根分区 /dev/sda2 只有 300M
  • 后面还有大量未分配空间

根因到这里就很明确了:不是硬盘小,而是系统安装时根分区就只做了 300M

扩容前一定先备份

这种操作涉及分区表和文件系统,第一步一定不是改盘,而是备份。

我先在路由器上导出了配置和关键信息,再拉回本地机器:

1
2
3
4
5
6
sysupgrade -b /tmp/sysupgrade-backup.tar.gz
fdisk -l /dev/sda > /tmp/fdisk.txt
block info > /tmp/block-info.txt
mount > /tmp/mount.txt
uci export > /tmp/uci-export.txt
cd /etc && tar -czf /tmp/etc-config.tar.gz config

然后把这些文件保存到本机工作目录里,避免路由器后续操作失败时没有回滚依据。

先扩分区,再扩文件系统

扩容不能上来就 resize2fs,因为那时候分区本身还是 300M。正确顺序是:

  1. 先把 /dev/sda2 分区扩到目标大小
  2. 再把 ext4 文件系统扩到新分区上限

这台机器的 /dev/sda2 是磁盘最后一个有效分区,后面全是空闲空间,所以非常适合直接扩展。

先用 parted 调整分区:

1
2
3
parted -s /dev/sda unit MiB print free
parted -s /dev/sda unit MiB resizepart 2 5152
parted -s /dev/sda unit MiB print free

这里 5152MiB 的意思是把第 2 个分区的结束位置扩到大约 5GiB

调整后的结果类似这样:

1
2
3
Number  Start    End        Size
1 0.25MiB 32.2MiB 32.0MiB
2 32.3MiB 5152MiB 5120MiB

之后重启一次,让内核重新读取新的分区表。

第一个坑:在线 ext4 扩容失败

分区扩好后,先尝试常规做法:

1
resize2fs /dev/sda2

理论上很多 ext4 场景支持在线扩容,但这台 ImmortalWrt x86 并没有顺利完成,报错大意是:

1
2
3
resize2fs: Invalid argument
reserve_backup_gdb
ext4_resize_fs: error (-22)

dmesg 里也能看到类似信息,说明不是命令写错,而是这套 ext4 元数据本身不适合直接在线扩到目标大小。

当时文件系统虽然有轻微变化,但并没有真正扩到 5G,依然停留在很小的空间上。

第二个坑:系统里缺少 tune2fs

继续排查后发现,真正要解决这个 ext4 问题,需要先处理 resize_inode 相关元数据,而这个步骤要依赖 tune2fs

但这台机器虽然装了 e2fsprogs,却没有 tune2fs 命令:

1
tune2fs: not found

所以后面先安装:

1
2
opkg update
opkg install tune2fs

这个坑比较关键。如果没有 tune2fs,后续“看起来像在修 ext4”的操作,其实只是做了不完整的一半。

实际可行的做法:两阶段扩容

因为这是路由器,不能长时间手工守在单用户模式里,所以我最后采用的是两阶段做法:

第一阶段:修 ext4 元数据并执行文件系统检查

第一阶段脚本做这些事情:

1
2
3
4
mount -o remount,ro /
tune2fs -O ^resize_inode /dev/sda2
e2fsck -fy /dev/sda2
reboot -f

这里的重点是:

  • 把根文件系统重新挂成只读
  • tune2fs 去掉 resize_inode
  • e2fsck 修复 ext4 元数据
  • 然后强制重启

实际日志里可以看到 e2fsck 确实修复了相关问题,例如:

1
2
3
4
Filesystem does not have resize_inode enabled, but s_reserved_gdt_blocks is 23; should be zero.  Fix? yes
Resize_inode not enabled, but the resize inode is non-zero. Clear? yes
rootfs: ***** FILE SYSTEM WAS MODIFIED *****
rootfs: ***** REBOOT SYSTEM *****

第二阶段:开机后自动执行 resize2fs

第一阶段完成并重启后,再由开机脚本执行:

1
resize2fs /dev/sda2

这一次就成功了,输出类似:

1
The filesystem on /dev/sda2 is now 1310656 (4k) blocks long.

之后再看磁盘空间:

1
df -h /

结果变成:

1
2
Filesystem                Size      Used Available Use% Mounted on
/dev/root 4.9G 283.7M 4.6G 6% /

扩容到这里就真正完成了。

最终验证

扩容完成后,我额外做了这些验证:

检查根分区大小

1
2
fdisk -l /dev/sda
df -h /

确认:

  • /dev/sda2 分区已经是 5G
  • / 文件系统已经增长到约 4.9G

检查网络和服务

1
2
3
4
5
/etc/init.d/dropbear status
/etc/init.d/uhttpd status
/etc/init.d/network status
/etc/init.d/firewall status
ping -c 2 223.5.5.5

结果都正常,说明这次扩容没有把 SSH、网络和核心服务带坏。

扩容后的一个附带问题

因为过程中做过强制重启,/boot 的 FAT 分区被标记成了未正常卸载。只读检查结果是:

1
fsck.fat -n -v /dev/sda1

核心信息为:

1
2
Dirty bit is set. Fs was not properly unmounted and some data may be corrupt.
Leaving filesystem unchanged.

这说明:

  • FAT 文件系统结构本身没有发现明显损坏
  • 只是脏标记被置位
  • 如果要真正清掉这个标记,需要在 /dev/sda1 未挂载时再跑一次修复模式的 fsck.fat

这次扩容的关键经验

这次最重要的经验其实就三条:

1. 先确认是不是“分区太小”,不要只看 df -h

df -h 只能告诉你文件系统满了,不能告诉你底层磁盘布局到底是什么样。像这次问题,真正的根因是 238G 硬盘只给了根分区 300M

2. OpenWrt/ImmortalWrt x86 的 ext4 扩容,不一定能直接在线成功

如果看到 reserve_backup_gdberror (-22) 这类信息,不要在同一个思路上反复硬试。先去看 ext4 元数据问题,而不是只会重复执行 resize2fs

3. 缺工具时先补齐环境

如果系统里没有 tune2fs,那就先装,不要幻想能靠“差不多的命令”糊过去。很多时候不是方案错了,而是执行条件根本没满足。

总结

这次扩容的本质不是“把一个目录清一清”,而是:

  • 识别出根分区安装得过小
  • 先扩 GPT 分区
  • 再修 ext4 元数据
  • 最后完成文件系统扩容

最终结果是:

  • 根分区从 300M 提升到 5G
  • 实际根文件系统可用空间提升到 4.6G
  • 系统、SSH、网络和核心服务都正常恢复

如果你也在 ImmortalWrt/OpenWrt x86 上遇到 / 爆满,但实际硬盘很大的情况,优先检查分区布局。很多时候问题不在磁盘本身,而在安装镜像时只给了一个很小的根分区。