适用场景:将 OpenWrt / ImmortalWrt 固件写入 eMMC / SD 卡 / SSD 后,固件只占用了很小一部分空间,剩余大量空间未被使用。本教程将介绍如何不重新刷机,在系统运行中将 overlay 分区扩容至磁盘剩余空间。
- 步骤 1:安装所需软件包
- 步骤 2:查看当前磁盘分区情况
- 步骤 3:使用 parted 扩容物理分区
- 步骤 4:验证分区已扩容
- 步骤 5:确认文件系统仍为旧大小
- 步骤 6:查看当前 loop 设备挂载状态
- 步骤 7:创建新的 loop 设备
- 步骤 8:确认新 loop 设备创建成功
- 步骤 9:挂载并卸载新 loop 设备(触发日志回放)
- 步骤 10:确认文件系统类型
- 步骤 11:扩容文件系统
- 步骤 12:(EFI 引导用户必看)修复 GRUB 引导 UUID
- 步骤 13:重启并验证最终结果
- 附录 A:OpenWrt overlay 原理简介
- 附录 B:一键脚本(熟练用户复制即用)
- 附录 C:流程总览图
目的:安装本教程所需的全部命令行工具。
注意:OpenWrt 从 25.x 版本开始,包管理器已从
opkg切换为apk。请根据你的系统版本选择对应的命令。
# 刷新软件包索引
apk update
# 安装所需工具
apk add lsblk fdisk resize2fs losetup blkid f2fs-tools parted# 刷新软件包索引
opkg update
# 安装所需工具
opkg install lsblk fdisk resize2fs losetup blkid f2fs-tools parted各软件包用途说明:
| 软件包 | 提供的命令 | 用途 |
|---|---|---|
lsblk |
lsblk |
列出块设备信息(查看文件系统类型) |
fdisk |
fdisk |
查看磁盘分区表(本教程仅用于查看,不用于分区操作) |
parted |
parted |
GNU 分区工具,用于调整分区大小(比 fdisk 更简单安全) |
resize2fs |
resize2fs |
扩容 ext2/3/4 文件系统 |
losetup |
losetup |
管理 loop 回环设备 |
blkid |
blkid |
查看块设备的 UUID、文件系统类型等属性 |
f2fs-tools |
resize.f2fs |
扩容 f2fs 文件系统 |
可选:如果你使用的是 ImmortalWrt 等第三方固件,可以尝试安装
diskman(LuCI 图形化磁盘管理插件),但 OpenWrt 官方源不提供此包。
示例安装输出:apk(点击展开)
root@OpenWrt:~# apk add lsblk fdisk resize2fs losetup blkid f2fs-tools parted
( 1/12) Installing blkid (2.41.3-r1)
Executing blkid-2.41.3-r1.post-install
( 2/12) Installing f2fsck (1.16.0-r4)
Executing f2fsck-1.16.0-r4.post-install
( 3/12) Installing f2fs-tools (1.16.0-r4)
Executing f2fs-tools-1.16.0-r4.post-install
( 4/12) Installing libfdisk1 (2.41.3-r1)
( 5/12) Installing terminfo (6.4-r3)
( 6/12) Installing libncurses6 (6.4-r3)
( 7/12) Installing fdisk (2.41.3-r1)
( 8/12) Installing losetup (2.41.3-r1)
( 9/12) Installing libmount1 (2.41.3-r1)
(10/12) Installing lsblk (2.41.3-r1)
(11/12) Installing resize2fs (1.47.3-r1)
(12/12) Installing tree (2.2.1-r2)
OK: 18.5 MiB in 152 packages
示例安装输出:opkg(点击展开)
root@ImmortalWrt:~# opkg install lsblk fdisk resize2fs losetup blkid f2fs-tools parted
Installing lsblk (2.39-2) to root...
Installing libmount1 (2.39-2) to root...
Package fdisk (2.39-2) installed in root is up to date.
Installing resize2fs (1.47.0-2) to root...
Installing losetup (2.39-2) to root...
Installing blkid (2.39-2) to root...
Installing f2fs-tools (1.16.0-1) to root...
Installing f2fsck (1.16.0-1) to root...
Configuring f2fsck.
Configuring f2fs-tools.
Configuring resize2fs.
Configuring libmount1.
Configuring lsblk.
Configuring losetup.
Configuring blkid.
目的:了解磁盘设备名称、分区布局、各分区的大小,确认我们要扩容的是哪个分区。
fdisk -l你需要从输出中找到以下关键信息:
- 磁盘设备名(如
/dev/sda、/dev/mmcblk0) - 磁盘总容量
- 需要扩容的分区(通常是最后一个 Linux filesystem 分区,即 overlay 所在的分区)
- 该分区的起始扇区(Start)——这个数字后续可能需要用到
示例输出:x86_64 SSD 设备(点击展开)
Disk /dev/loop0: 291.06 MiB, 305201152 bytes, 596096 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
GPT PMBR size mismatch (680479 != 123091919) will be corrected by write.
Disk /dev/sda: 58.69 GiB, 63023063040 bytes, 123091920 sectors
Disk model: SanDisk SSD i110
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: gpt
Disk identifier: 20C60F33-AFC6-96E2-AE0B-E001AE609500
Device Start End Sectors Size Type
/dev/sda1 512 66047 65536 32M Linux filesystem
/dev/sda2 66048 680447 614400 300M Linux filesystem
/dev/sda128 34 511 478 239K BIOS boot
Partition table entries are not in disk order.
示例输出:ARM eMMC 设备(点击展开)
Disk /dev/mmcblk0: 29.12 GiB, 31268536320 bytes, 61071360 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x5452574f
Device Boot Start End Sectors Size Id Type
/dev/mmcblk0p1 * 65536 98303 32768 16M 83 Linux
/dev/mmcblk0p2 131072 344063 212992 104M 83 Linux
以 x86_64 SSD 为例的解读:
| 项目 | 值 | 说明 |
|---|---|---|
| 磁盘设备 | /dev/sda |
SSD 存储设备 |
| 磁盘总容量 | 58.69 GiB | 物理磁盘总大小 |
分区1 (/dev/sda1) |
32M | 内核/引导分区(kernel) |
分区2 (/dev/sda2) |
300M | 根文件系统分区(包含 overlay),这就是要扩容的分区 |
分区128 (/dev/sda128) |
239K | BIOS boot 分区(GPT 引导用) |
| 未使用空间 | 约 58G | 我们要把这些空间分给分区2 |
目的:将分区2(overlay 所在分区)扩展到磁盘末尾,使其占用所有剩余空间。
为什么用 parted 而不是 fdisk? fdisk 需要"删除分区 → 重建分区"的交互操作,步骤繁多、容易出错(起始扇区必须对齐、签名不能删除等)。
parted支持直接resizepart原地扩容,不需要删除分区,更安全更简单。
这是最简单直观的方式,一步一步操作,新手推荐使用。
第一步:打开 parted 并指定磁盘设备
parted /dev/mmcblk0将
/dev/mmcblk0替换为你在步骤 2 中看到的磁盘设备名(如/dev/sda)。进入 parted 交互界面后,你会看到:
GNU Parted 3.6 Using /dev/mmcblk0 Welcome to GNU Parted! Type 'help' to view a list of commands. (parted)
第二步:查看当前分区表
(parted) print
输出示例:
Model: MMC Y2P032 (sd/mmc) Disk /dev/mmcblk0: 31.3GB Sector size (logical/physical): 512B/512B Partition Table: msdos Disk Flags: Number Start End Size Type File system Flags 1 33.6MB 50.3MB 16.8MB primary ext2 boot 2 67.1MB 176MB 109MB primary确认:分区 2 就是我们要扩容的分区(109MB → 要扩容到磁盘末尾 31.3GB)。
第三步:执行扩容
(parted) resizepart 2 100%
resizepart— 调整分区大小的命令2— 要调整的分区编号(就是print输出中Number列的值)100%— 扩展到磁盘末尾,占满所有剩余空间
提示:如果你不想占满全部空间,可以指定具体大小,如
5GiB、10GiB等。
第四步:退出 parted
(parted) quit
退出时可能提示
Information: You may need to update /etc/fstab.,这是正常现象,无需处理。
完整交互过程一览:
root@OpenWrt:~# parted /dev/mmcblk0
GNU Parted 3.6
Using /dev/mmcblk0
Welcome to GNU Parted! Type 'help' to view a list of commands.
(parted) print
Model: MMC Y2P032 (sd/mmc)
Disk /dev/mmcblk0: 31.3GB
Sector size (logical/physical): 512B/512B
Partition Table: msdos
Disk Flags:
Number Start End Size Type File system Flags
1 33.6MB 50.3MB 16.8MB primary ext2 boot
2 67.1MB 176MB 109MB primary
(parted) resizepart 2 100%
(parted) quit
Information: You may need to update /etc/fstab.
就这么简单!3 条命令(print → resizepart → quit)就完成了分区扩容。
如果你想用一条命令直接完成扩容(无需手动输入设备名和分区号),可以使用以下命令。它会自动检测根分区所在的磁盘和分区号:
parted -f -s \
/dev/$(basename $(dirname $(readlink -f /sys/dev/block/$(awk '$9=="/dev/root"{print $3}' /proc/self/mountinfo)))) \
resizepart \
$(cat /sys/dev/block/$(awk '$9=="/dev/root"{print $3}' /proc/self/mountinfo)/partition) \
100%参数说明:
| 参数 | 含义 |
|---|---|
-f |
强制模式,跳过所有确认提示 |
-s |
脚本模式,非交互式运行 |
resizepart |
子命令:调整现有分区的大小 |
100% |
将该分区扩展到磁盘末尾 |
这条命令的自动定位原理(点击展开)
看起来很复杂,实际上就是自动完成了"找磁盘设备名"和"找分区号"两件事:
1. 自动获取根分区的设备号
awk '$9=="/dev/root"{print $3}' /proc/self/mountinfo
# 示例输出: 179:2(即 mmcblk0p2)或 8:2(即 sda2)- 从
/proc/self/mountinfo(内核提供的挂载信息文件)中查找/dev/root挂载点 $3是该设备的major:minor设备号
2. 通过设备号推导出磁盘名
readlink -f /sys/dev/block/179:2
# 输出: /sys/devices/platform/.../block/mmcblk0/mmcblk0p2
dirname ... # → .../block/mmcblk0
basename ... # → mmcblk0readlink -f解析出 sysfs 中该块设备的完整路径dirname取上一层目录(即父磁盘),basename取最后一段 → 得到磁盘名
3. 获取分区号
cat /sys/dev/block/179:2/partition
# 输出: 2所以这条命令最终等价于:parted -f -s /dev/mmcblk0 resizepart 2 100%
目的:确认物理分区已经成功扩大。
fdisk -l示例输出(点击展开)
Disk /dev/sda: 58.69 GiB, 63023063040 bytes, 123091920 sectors
Disk model: SanDisk SSD i110
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: gpt
Disk identifier: 20C60F33-AFC6-96E2-AE0B-E001AE609500
Device Start End Sectors Size Type
/dev/sda1 512 66047 65536 32M Linux filesystem
/dev/sda2 66048 10551295 10485248 5G Linux filesystem
/dev/sda128 34 511 478 239K BIOS boot
Partition table entries are not in disk order.
检查要点:确认分区2的 Size 已经变大(例如从 300M → 5G 或更大)。
⚠️ 注意:此时仅仅是物理分区表被修改了,文件系统本身还不知道空间变大了。接下来我们需要扩容文件系统。
目的:验证文件系统确实还没有感知到新空间,需要后续操作。
df -Thdf— 显示文件系统磁盘使用情况-T— 显示文件系统类型-h— 以人类可读的格式显示大小
示例输出(点击展开)
Filesystem Type Size Used Available Use% Mounted on
/dev/root squashfs 9.0M 9.0M 0 100% /rom
tmpfs tmpfs 906.0M 1.1M 904.9M 0% /tmp
/dev/loop0 f2fs 289.1M 63.1M 226.0M 22% /overlay
overlayfs:/overlay overlay 289.1M 63.1M 226.0M 22% /
/dev/sda1 vfat 31.9M 7.9M 24.1M 25% /boot
tmpfs tmpfs 512.0K 0 512.0K 0% /dev
关键发现:/overlay 对应的 /dev/loop0 仍然只有 289.1M(原始大小),并没有变成我们刚扩容的大小。
原因:OpenWrt 使用 loop 设备(/dev/loop0)挂载 overlay 分区。这个 loop 设备在系统启动时创建,它仍然指向旧的空间范围。我们需要创建一个新的 loop 设备,让它覆盖扩容后的完整空间。
目的:了解当前 loop 设备的配置参数——特别是 OFFSET 偏移量和后端设备,后面创建新 loop 设备时必须保持一致。
losetup示例输出(点击展开)
NAME SIZELIMIT OFFSET AUTOCLEAR RO BACK-FILE DIO LOG-SEC
/dev/loop0 0 9371648 1 0 /sda2 0 512
各字段说明:
| 字段 | 值(示例) | 含义 |
|---|---|---|
NAME |
/dev/loop0 |
loop 设备名称 |
SIZELIMIT |
0 |
大小限制(0 = 不限制,使用后端设备全部空间) |
OFFSET |
9371648 |
偏移量(字节),overlay 文件系统在分区内的起始位置 |
AUTOCLEAR |
1 |
自动清除标志(1 = 系统自动创建的设备) |
RO |
0 |
是否只读(0 = 可读写) |
BACK-FILE |
/sda2 |
后端文件/设备(overlay 实际存储的物理分区) |
📌 重要:记住
OFFSET值(此例为9371648),创建新 loop 设备时必须使用相同的偏移量!因为分区前半部分是只读的 squashfs 系统镜像,偏移量之后才是可写的 overlay 文件系统。
目的:用相同的偏移量,基于扩容后的分区设备路径 /dev/sda2,创建一个新的 loop 设备。这个新 loop 设备能看到扩大后的全部空间。
losetup -f -o 9371648 /dev/sda2参数说明:
| 参数 | 含义 |
|---|---|
losetup |
loop 设备管理命令 |
-f |
自动选择下一个空闲的 loop 设备编号(如 /dev/loop1) |
-o 9371648 |
设置偏移量,必须与步骤 6 中看到的 OFFSET 值一致 |
/dev/sda2 |
后端物理分区设备(注意:这里用的是 /dev/sda2 而不是 /sda2) |
/sda2vs/dev/sda2的区别:
/sda2(无/dev前缀)是 overlay 挂载在根目录/下的路径/dev/sda2是实际的块设备路径- 创建新 loop 设备时必须使用
/dev/sda2(块设备路径)
如果你不想手动查看和输入偏移量,可以用以下命令自动获取:
losetup -f \
-o $(losetup -l -n -O OFFSET $(df /overlay | awk 'NR==2{print $1}')) \
/dev$(losetup -l -n -O BACK-FILE $(df /overlay | awk 'NR==2{print $1}'))拆解说明:
| 嵌套命令 | 作用 | 示例输出 |
|---|---|---|
df /overlay | awk 'NR==2{print $1}' |
获取 overlay 当前使用的 loop 设备名 | /dev/loop0 |
losetup -l -n -O OFFSET /dev/loop0 |
查询该 loop 设备的偏移量 | 9371648 |
losetup -l -n -O BACK-FILE /dev/loop0 |
查询该 loop 设备的后端文件路径 | /sda2 |
/dev + /sda2 |
拼接出完整的块设备路径 | /dev/sda2 |
目的:查看 loop 设备列表,确认新设备已创建,并找出它的名称。
losetup示例输出(点击展开)
NAME SIZELIMIT OFFSET AUTOCLEAR RO BACK-FILE DIO LOG-SEC
/dev/loop1 0 9371648 0 0 /dev/sda2 0 512
/dev/loop0 0 9371648 1 0 /sda2 0 512
对比两个 loop 设备:
| 项目 | /dev/loop0(原有) |
/dev/loop1(新建) |
|---|---|---|
| OFFSET | 9371648 | 9371648(一致 ✓) |
| AUTOCLEAR | 1(系统自动创建) | 0(我们手动创建) |
| BACK-FILE | /sda2(挂载路径) |
/dev/sda2(块设备路径) |
通过
AUTOCLEAR标志可以区分:0 = 我们手动创建的,1 = 系统自动创建的。
也可以用以下命令快速筛选出新建的 loop 设备名:
losetup -l -n -O NAME,AUTOCLEAR | awk '$2=="0"{print $1}'- 示例输出:
/dev/loop1 - 📌 记住这个设备名,后续步骤都要用到
目的:将新 loop 设备临时挂载一次再卸载,触发文件系统的日志回放(log replay),确保文件系统处于干净状态。
mount /dev/loop1 /mnt挂载后可以顺便验证数据是否完好:
ls /mntupper work
upper目录存放的是 overlay 可写层的文件(你安装的软件、修改的配置等),work是 overlay 内部工作目录。
确认数据无误后,卸载:
umount /dev/loop1为什么这一步是必要的?
- f2fs 和 ext4 都使用**日志机制(journal/log)**来保证数据一致性
- 如果文件系统在上次使用后没有被干净地卸载(unclean shutdown),日志中会有未回放的记录
resize.f2fs和resize2fs都要求文件系统处于**干净状态(clean state)**才能执行扩容- 挂载动作会自动触发日志回放,使文件系统进入干净状态
如果跳过这一步,f2fs 扩容时会报错:
[f2fs_do_mount:3651] Mount unclean image to replay log first
目的:确定 overlay 使用的是 f2fs 还是 ext4,因为两者的扩容命令不同。
lsblk -f示例输出(点击展开)
NAME FSTYPE FSVER LABEL UUID FSAVAIL FSUSE% MOUNTPOINTS
loop0 f2fs rootfs_data 1858fcd6-7767-11ef-979c-5fec82ce50f5 225.9M 22% /overlay
loop1 f2fs rootfs_data 1858fcd6-7767-11ef-979c-5fec82ce50f5
sda
├─sda1 vfat kernel 1234-ABCD 24.1M 25% /boot
├─sda2 squashfs 0 100% /rom
└─sda128
关键看 loop0 或 loop1 那一行的 FSTYPE 列:
- 如果是
f2fs→ 执行步骤 11 的 方案 A - 如果是
ext2/ext3/ext4→ 执行步骤 11 的 方案 B
根据上一步确认的文件系统类型,选择对应的扩容命令。
resize.f2fs /dev/loop1| 参数 | 含义 |
|---|---|
resize.f2fs |
f2fs 文件系统大小调整工具(由 f2fs-tools 包提供) |
/dev/loop1 |
我们在步骤 7 创建的新 loop 设备 |
resize.f2fs会自动检测 loop 设备指向的分区大小,将 f2fs 文件系统扩展到该设备的全部可用空间。
示例输出(点击展开)
Info: Force to resize
Info: MKFS version
"Linux version 5.15.162 ..."
Info: FSCK version
from "Linux version 5.15.162 ..."
to "Linux version 5.15.162 ..."
Info: superblock features = 0 :
Info: superblock encrypt level = 0, salt = 00000000000000000000000000000000
Info: Segments per section = 1
Info: Sections per zone = 1
Info: total FS sectors = 596096 (291 MB)
Info: CKPT version = 43a353a3
...
[rebuild_checkpoint: 603] Info: Done to rebuild checkpoint blocks
[update_superblock: 765] Info: Done to update superblock
Done: 0.799958 secs
resize2fs -f /dev/loop1| 参数 | 含义 |
|---|---|
resize2fs |
ext 系列文件系统调整大小工具(由 e2fsprogs / resize2fs 包提供) |
-f |
强制执行(Force),跳过一些安全检查 |
/dev/loop1 |
我们在步骤 7 创建的新 loop 设备 |
示例输出(点击展开)
resize2fs 1.46.5 (30-Dec-2021)
Filesystem at /dev/loop1 is mounted on /overlay; on-line resizing required
old_desc_blocks = 1, new_desc_blocks = 24
The filesystem on /dev/loop1 is now 52428539 (4k) blocks long.
仅适用于 EFI 固件(文件名包含
-efi的固件)。非 EFI 用户可以跳过此步。
如何判断是否是 EFI 固件?
# 带 -efi 的固件(需要此步骤):
immortalwrt-23.05.3-x86-64-generic-squashfs-combined-efi.img
immortalwrt-23.05.3-x86-64-generic-ext4-combined-efi.img
# 不带 -efi 的固件(跳过此步骤):
immortalwrt-23.05.3-x86-64-generic-squashfs-combined.img
immortalwrt-23.05.3-x86-64-generic-ext4-combined.img
目的:分区被删除重建后,PARTUUID 可能会发生变化。如果 GRUB 引导配置中的 UUID 与实际分区 UUID 不匹配,重启后将无法启动系统。
blkid示例输出(点击展开)
/dev/loop1: LABEL="rootfs_data" UUID="1858fcd6-7767-11ef-979c-5fec82ce50f5" BLOCK_SIZE="4096" TYPE="f2fs"
/dev/loop0: LABEL="rootfs_data" UUID="1858fcd6-7767-11ef-979c-5fec82ce50f5" BLOCK_SIZE="4096" TYPE="f2fs"
/dev/sda2: BLOCK_SIZE="262144" TYPE="squashfs" PARTUUID="cfd7abf3-cf18-4a4a-95e4-46f2013c080f"
/dev/sda128: PARTUUID="20c60f33-afc6-96e2-ae0b-e001ae609580"
/dev/sda1: SEC_TYPE="msdos" LABEL_FATBOOT="kernel" LABEL="kernel" UUID="1234-ABCD" BLOCK_SIZE="512" TYPE="vfat" PARTUUID="20c60f33-afc6-96e2-ae0b-e001ae609501"
📌 找到 /dev/sda2 那一行的 PARTUUID,本例中为:
PARTUUID="cfd7abf3-cf18-4a4a-95e4-46f2013c080f"
cat /boot/grub/grub.cfgvi /boot/grub/grub.cfg将文件中所有的 PARTUUID=xxx 替换为步骤 12.1 中获取到的 /dev/sda2 的 PARTUUID 值。
修改后的完整配置参考:
serial --unit=0 --speed=115200 --word=8 --parity=no --stop=1 --rtscts=off
terminal_input console serial; terminal_output console serial
set default="0"
set timeout="3"
search -l kernel -s root
menuentry "ImmortalWrt" {
linux /boot/vmlinuz root=PARTUUID=cfd7abf3-cf18-4a4a-95e4-46f2013c080f rootwait console=tty0 console=ttyS0,115200n8 noinitrd
}
menuentry "ImmortalWrt (failsafe)" {
linux /boot/vmlinuz failsafe=true root=PARTUUID=cfd7abf3-cf18-4a4a-95e4-46f2013c080f rootwait console=tty0 console=ttyS0,115200n8 noinitrd
}
⚠️ 注意:文件中有两处PARTUUID=,分别在ImmortalWrt和ImmortalWrt (failsafe)菜单项中,两处都要改。
reboot重启完成后,登录系统验证:
df -Th示例输出(点击展开)
Filesystem Type Size Used Available Use% Mounted on
/dev/root squashfs 9.0M 9.0M 0 100% /rom
tmpfs tmpfs 906.0M 104.0K 905.9M 0% /tmp
/dev/loop0 f2fs 5.0G 162.8M 4.8G 3% /overlay
overlayfs:/overlay overlay 5.0G 162.8M 4.8G 3% /
tmpfs tmpfs 512.0K 0 512.0K 0% /dev
/dev/sda1 vfat 31.9M 7.9M 24.1M 25% /mnt/sda1
🎉 大功告成! /overlay 已经从原来的 289.1M 扩容到了 5.0G,可用空间 4.8G!
理解 OpenWrt 的存储架构有助于明白为什么扩容需要这么多步骤:
┌──────────────────────────────────────────────────┐
│ / (根目录) │
│ 类型: overlayfs │
│ = 只读层(lower) + 可写层(upper) 的合并视图 │
├──────────────────────────────────────────────────┤
│ │
│ 只读层 (lower) 可写层 (upper) │
│ /rom /overlay │
│ /dev/root /dev/loop0 │
│ squashfs f2fs 或 ext4 │
│ 存放原始固件系统文件 存放用户安装的软件、 │
│ 修改的配置文件等 │
│ │
├──────────────────────────────────────────────────┤
│ │
│ 物理分区 /dev/sda2 │
│ ┌─────────────────┬────────────────────────┐ │
│ │ squashfs 镜像 │ overlay 文件系统 │ │
│ │ (只读系统) │ (可写层,loop 设备挂载) │ │
│ │ │ │ │
│ │← OFFSET 偏移量 →│ │ │
│ └─────────────────┴────────────────────────┘ │
│ │
└──────────────────────────────────────────────────┘
关键点:
- squashfs + overlayfs 架构是 OpenWrt 能够"恢复出厂设置"的原理——只需清空 overlay 可写层,就能恢复到只读层的原始状态
- OFFSET 偏移量:分区的前半部分是 squashfs 只读镜像,偏移量之后才是 overlay 文件系统。所以创建 loop 设备时必须指定正确的偏移量
- loop 设备:Linux 内核的 loop 设备机制允许将文件或设备的一部分(通过偏移量)当作独立的块设备来使用
以下脚本将步骤 3 ~ 步骤 11 合并为一条命令链,使用
&&连接确保每一步成功后才执行下一步。
⚠️ EFI 用户注意:一键脚本不包含步骤 12(修复 GRUB UUID),请在执行脚本前或重启前手动完成。
parted -f -s \
/dev/$(basename $(dirname $(readlink -f /sys/dev/block/$(awk '$9=="/dev/root"{print $3}' /proc/self/mountinfo)))) \
resizepart \
$(cat /sys/dev/block/$(awk '$9=="/dev/root"{print $3}' /proc/self/mountinfo)/partition) \
100% && \
NEW_LOOP=$(losetup --show -f \
-o $(losetup -l -n -O OFFSET $(df /overlay | awk 'NR==2{print $1}')) \
/dev$(losetup -l -n -O BACK-FILE $(df /overlay | awk 'NR==2{print $1}'))) && \
mount $NEW_LOOP /mnt && \
umount $NEW_LOOP && \
resize.f2fs $NEW_LOOP && \
rebootparted -f -s \
/dev/$(basename $(dirname $(readlink -f /sys/dev/block/$(awk '$9=="/dev/root"{print $3}' /proc/self/mountinfo)))) \
resizepart \
$(cat /sys/dev/block/$(awk '$9=="/dev/root"{print $3}' /proc/self/mountinfo)/partition) \
100% && \
NEW_LOOP=$(losetup --show -f \
-o $(losetup -l -n -O OFFSET $(df /overlay | awk 'NR==2{print $1}')) \
/dev$(losetup -l -n -O BACK-FILE $(df /overlay | awk 'NR==2{print $1}'))) && \
mount $NEW_LOOP /mnt && \
umount $NEW_LOOP && \
resize2fs -f $NEW_LOOP && \
reboot ┌────────────────────────────────────────────────────────────────┐
│ 步骤 1 opkg install ... 安装所需工具 │
│ ↓ │
│ 步骤 2 fdisk -l 查看磁盘分区布局 │
│ ↓ │
│ 步骤 3 parted resizepart 100% 扩大物理分区到磁盘末尾 │
│ ↓ │
│ 步骤 4 fdisk -l 验证分区已扩大 │
│ ↓ │
│ 步骤 5 df -Th 确认文件系统仍为旧大小 │
│ ↓ │
│ 步骤 6 losetup 查看当前 loop 设备状态 │
│ ↓ │
│ 步骤 7 losetup -f -o ... 创建新的 loop 设备 │
│ ↓ │
│ 步骤 8 losetup 确认新 loop 设备创建成功 │
│ ↓ │
│ 步骤 9 mount + umount 触发日志回放,确保文件系统干净 │
│ ↓ │
│ 步骤 10 lsblk -f 判断文件系统类型 │
│ ↓ │
│ 步骤 11 resize.f2fs / resize2fs 扩容文件系统 │
│ ↓ │
│ 步骤 12 blkid + vi grub.cfg (EFI) 修复引导 UUID │
│ ↓ │
│ 步骤 13 reboot + df -Th 重启生效 & 验证结果 │
└────────────────────────────────────────────────────────────────┘