Skip to content

Instantly share code, notes, and snippets.

@Zayrick
Created February 27, 2026 11:41
Show Gist options
  • Select an option

  • Save Zayrick/7a0ad84d2e4e20d9e1ad40af59753a06 to your computer and use it in GitHub Desktop.

Select an option

Save Zayrick/7a0ad84d2e4e20d9e1ad40af59753a06 to your computer and use it in GitHub Desktop.

OpenWrt / ImmortalWrt 在线扩容 overlay 分区教程

适用场景:将 OpenWrt / ImmortalWrt 固件写入 eMMC / SD 卡 / SSD 后,固件只占用了很小一部分空间,剩余大量空间未被使用。本教程将介绍如何不重新刷机,在系统运行中将 overlay 分区扩容至磁盘剩余空间。


目录


步骤 1:安装所需软件包

目的:安装本教程所需的全部命令行工具。

注意:OpenWrt 从 25.x 版本开始,包管理器已从 opkg 切换为 apk。请根据你的系统版本选择对应的命令。

OpenWrt 25.x 及以上(使用 apk)

# 刷新软件包索引
apk update

# 安装所需工具
apk add lsblk fdisk resize2fs losetup blkid f2fs-tools parted

OpenWrt 23.x 及以下 / ImmortalWrt(使用 opkg)

# 刷新软件包索引
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.

步骤 2:查看当前磁盘分区情况

目的:了解磁盘设备名称、分区布局、各分区的大小,确认我们要扩容的是哪个分区

fdisk -l

你需要从输出中找到以下关键信息

  1. 磁盘设备名(如 /dev/sda/dev/mmcblk0
  2. 磁盘总容量
  3. 需要扩容的分区(通常是最后一个 Linux filesystem 分区,即 overlay 所在的分区)
  4. 该分区的起始扇区(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

步骤 3:使用 parted 扩容物理分区

目的:将分区2(overlay 所在分区)扩展到磁盘末尾,使其占用所有剩余空间。

为什么用 parted 而不是 fdisk? fdisk 需要"删除分区 → 重建分区"的交互操作,步骤繁多、容易出错(起始扇区必须对齐、签名不能删除等)。parted 支持直接 resizepart 原地扩容,不需要删除分区,更安全更简单。

3.1 推荐方式:交互式 parted(最直观)

这是最简单直观的方式,一步一步操作,新手推荐使用。

第一步:打开 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% — 扩展到磁盘末尾,占满所有剩余空间

提示:如果你不想占满全部空间,可以指定具体大小,如 5GiB10GiB 等。

第四步:退出 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 条命令(printresizepartquit)就完成了分区扩容。

3.2 进阶方式:非交互式一条命令搞定

如果你想用一条命令直接完成扩容(无需手动输入设备名和分区号),可以使用以下命令。它会自动检测根分区所在的磁盘和分区号

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 ...  # → mmcblk0
  • readlink -f 解析出 sysfs 中该块设备的完整路径
  • dirname 取上一层目录(即父磁盘),basename 取最后一段 → 得到磁盘名

3. 获取分区号

cat /sys/dev/block/179:2/partition
# 输出: 2

所以这条命令最终等价于:parted -f -s /dev/mmcblk0 resizepart 2 100%


步骤 4:验证分区已扩容

目的:确认物理分区已经成功扩大。

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 或更大)。

⚠️ 注意:此时仅仅是物理分区表被修改了,文件系统本身还不知道空间变大了。接下来我们需要扩容文件系统。


步骤 5:确认文件系统仍为旧大小

目的:验证文件系统确实还没有感知到新空间,需要后续操作。

df -Th
  • df — 显示文件系统磁盘使用情况
  • -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 设备,让它覆盖扩容后的完整空间。


步骤 6:查看当前 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 文件系统。


步骤 7:创建新的 loop 设备

目的:用相同的偏移量,基于扩容后的分区设备路径 /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

/sda2 vs /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

步骤 8:确认新 loop 设备创建成功

目的:查看 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
  • 📌 记住这个设备名,后续步骤都要用到

步骤 9:挂载并卸载新 loop 设备(触发日志回放)

目的:将新 loop 设备临时挂载一次再卸载,触发文件系统的日志回放(log replay),确保文件系统处于干净状态。

mount /dev/loop1 /mnt

挂载后可以顺便验证数据是否完好:

ls /mnt
upper  work

upper 目录存放的是 overlay 可写层的文件(你安装的软件、修改的配置等),work 是 overlay 内部工作目录。

确认数据无误后,卸载:

umount /dev/loop1

为什么这一步是必要的?

  • f2fs 和 ext4 都使用**日志机制(journal/log)**来保证数据一致性
  • 如果文件系统在上次使用后没有被干净地卸载(unclean shutdown),日志中会有未回放的记录
  • resize.f2fsresize2fs 都要求文件系统处于**干净状态(clean state)**才能执行扩容
  • 挂载动作会自动触发日志回放,使文件系统进入干净状态

如果跳过这一步,f2fs 扩容时会报错:

[f2fs_do_mount:3651] Mount unclean image to replay log first

步骤 10:确认文件系统类型

目的:确定 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

关键看 loop0loop1 那一行的 FSTYPE

  • 如果是 f2fs → 执行步骤 11 的 方案 A
  • 如果是 ext2 / ext3 / ext4 → 执行步骤 11 的 方案 B

步骤 11:扩容文件系统

根据上一步确认的文件系统类型,选择对应的扩容命令。

方案 A:f2fs 文件系统

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

方案 B:ext2/3/4 文件系统

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.

步骤 12:(EFI 引导用户必看)修复 GRUB 引导 UUID

仅适用于 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 不匹配,重启后将无法启动系统

12.1 查看当前分区的 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"

12.2 查看当前 GRUB 配置

cat /boot/grub/grub.cfg

12.3 修改 GRUB 配置

vi /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=,分别在 ImmortalWrtImmortalWrt (failsafe) 菜单项中,两处都要改


步骤 13:重启并验证最终结果

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!


附录 A:OpenWrt overlay 原理简介

理解 OpenWrt 的存储架构有助于明白为什么扩容需要这么多步骤:

┌──────────────────────────────────────────────────┐
│                  / (根目录)                        │
│            类型: overlayfs                         │
│     = 只读层(lower) + 可写层(upper) 的合并视图      │
├──────────────────────────────────────────────────┤
│                                                  │
│  只读层 (lower)          可写层 (upper)            │
│  /rom                    /overlay                │
│  /dev/root               /dev/loop0              │
│  squashfs                f2fs 或 ext4             │
│  存放原始固件系统文件      存放用户安装的软件、       │
│                          修改的配置文件等           │
│                                                  │
├──────────────────────────────────────────────────┤
│                                                  │
│             物理分区 /dev/sda2                     │
│  ┌─────────────────┬────────────────────────┐    │
│  │ squashfs 镜像    │ overlay 文件系统         │    │
│  │ (只读系统)       │ (可写层,loop 设备挂载)  │    │
│  │                 │                        │    │
│  │← OFFSET 偏移量 →│                        │    │
│  └─────────────────┴────────────────────────┘    │
│                                                  │
└──────────────────────────────────────────────────┘

关键点

  1. squashfs + overlayfs 架构是 OpenWrt 能够"恢复出厂设置"的原理——只需清空 overlay 可写层,就能恢复到只读层的原始状态
  2. OFFSET 偏移量:分区的前半部分是 squashfs 只读镜像,偏移量之后才是 overlay 文件系统。所以创建 loop 设备时必须指定正确的偏移量
  3. loop 设备:Linux 内核的 loop 设备机制允许将文件或设备的一部分(通过偏移量)当作独立的块设备来使用

附录 B:一键脚本(熟练用户复制即用)

以下脚本将步骤 3 ~ 步骤 11 合并为一条命令链,使用 && 连接确保每一步成功后才执行下一步。

⚠️ EFI 用户注意:一键脚本不包含步骤 12(修复 GRUB UUID),请在执行脚本前或重启前手动完成。

f2fs 一键扩容脚本

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 && \
reboot

ext2/3/4 一键扩容脚本

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 && \
resize2fs -f $NEW_LOOP && \
reboot

附录 C:流程总览图

 ┌────────────────────────────────────────────────────────────────┐
 │  步骤 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         重启生效 & 验证结果            │
 └────────────────────────────────────────────────────────────────┘
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment