1+ #! /bin/bash
2+ set -e
3+
4+ # --- 权限与依赖检查 ---
5+ if [ " $( id -u) " -ne 0 ]; then
6+ echo " Error: 请使用 sudo (root权限) 运行"
7+ exit 1
8+ fi
9+
10+ # GitHub Actions 环境自动安装 (使用 apt-fast)
11+ if [ " $GITHUB_ACTIONS " = " true" ]; then
12+ echo " 正在安装依赖..."
13+ sudo apt-fast install -y -qq btrfs-progs rsync pigz
14+ fi
15+
16+ # 检查必要工具
17+ for cmd in mkfs.btrfs rsync losetup blkid pigz; do
18+ if ! command -v $cmd & > /dev/null; then
19+ echo " Error: 缺少工具 $cmd "
20+ exit 1
21+ fi
22+ done
23+
24+ # --- 输入校验 ---
25+ SOURCE_FILE=" $1 "
26+ if [[ -z " $SOURCE_FILE " || " $SOURCE_FILE " != * .gz ]]; then
27+ echo " 用法: $0 <image.img.gz>"
28+ echo " Error: 仅支持 .gz 格式的压缩镜像"
29+ exit 1
30+ fi
31+
32+ if [ ! -f " $SOURCE_FILE " ]; then
33+ echo " Error: 文件不存在 $SOURCE_FILE "
34+ exit 1
35+ fi
36+
37+ # --- 变量定义 ---
38+ BASENAME=$( basename " $SOURCE_FILE " .gz)
39+
40+ # --- 替换文件名 ext4 为 btrfs ---
41+ if [[ " $BASENAME " == * " ext4" * ]]; then
42+ # 如果文件名包含 ext4,则进行替换
43+ WORK_IMG=" ${BASENAME// ext4/ btrfs} "
44+ else
45+ # 如果原文件名不含 ext4,则在 .img 前面加上 -btrfs 后缀
46+ WORK_IMG=" ${BASENAME% .img} -btrfs.img"
47+ fi
48+
49+ # 使用当前目录下的 tmp 文件夹
50+ TEMP_BASE=" ./tmp"
51+ TEMP_MNT=" ${TEMP_BASE} /btrfs_mnt_$$ "
52+ TEMP_DATA=" ${TEMP_BASE} /btrfs_data_$$ "
53+ LOOP_DEV=" "
54+
55+ # 定义退出清理函数
56+ cleanup () {
57+ mountpoint -q " $TEMP_MNT " && umount " $TEMP_MNT "
58+ [ -n " $LOOP_DEV " ] && losetup -d " $LOOP_DEV "
59+ # 仅删除本次运行生成的临时子目录,保留 ./tmp 父目录
60+ rm -rf " $TEMP_MNT " " $TEMP_DATA "
61+ }
62+ trap cleanup EXIT
63+
64+ # --- 核心流程 ---
65+
66+ echo " 1. 解压镜像到: $WORK_IMG ..."
67+ pigz -d -c " $SOURCE_FILE " > " $WORK_IMG "
68+
69+ echo " 2. 挂载镜像..."
70+ LOOP_DEV=$( losetup -fP --show " $WORK_IMG " )
71+ ROOT_PART=" ${LOOP_DEV} p2" # 默认 OpenWrt Rootfs 为 p2
72+
73+ if [ ! -b " $ROOT_PART " ]; then
74+ echo " Error: 未找到分区 $ROOT_PART "
75+ exit 1
76+ fi
77+
78+ OLD_UUID=$( blkid -s UUID -o value " $ROOT_PART " )
79+ echo " 原 UUID 为: $OLD_UUID "
80+ OLD_PARTUUID=$( blkid -s PARTUUID -o value " $ROOT_PART " )
81+ echo " 原 Partition UUID : $OLD_PARTUUID "
82+
83+ # 创建临时目录
84+ mkdir -p " $TEMP_MNT " " $TEMP_DATA "
85+
86+ echo " 3. 提取原系统数据..."
87+ mount " $ROOT_PART " " $TEMP_MNT "
88+ rsync -aAX --exclude " lost+found" " $TEMP_MNT /" " $TEMP_DATA /"
89+ umount " $TEMP_MNT "
90+
91+ echo " 4. 格式化为 Btrfs..."
92+ mkfs.btrfs -f -L rootfs -m single -U " $OLD_UUID " " $ROOT_PART "
93+
94+ echo " 5. 还原数据..."
95+ mount -t btrfs -o compress=zstd " $ROOT_PART " " $TEMP_MNT "
96+ btrfs property set " $TEMP_MNT " compression zstd
97+ rsync -aAX " $TEMP_DATA /" " $TEMP_MNT /"
98+
99+ echo " 6. 更新系统配置..."
100+ # 验证 Filesystem UUID (mkfs -U 参数的效果)
101+ NEW_UUID=$( blkid -s UUID -o value " $ROOT_PART " )
102+ echo " 验证 UUID 完整性..."
103+ if [ " $NEW_UUID " != " $OLD_UUID " ]; then
104+ echo " Error: Filesystem UUID 未能保留!"
105+ echo " 期望: $OLD_UUID "
106+ echo " 实际: $NEW_UUID "
107+ exit 1
108+ fi
109+ echo " [OK] Filesystem UUID 保持一致: $NEW_UUID "
110+ # 验证 PARTUUID (mkfs 通常不会改变这个)
111+ NEW_PARTUUID=$( blkid -s PARTUUID -o value " $ROOT_PART " )
112+ if [ " $NEW_PARTUUID " != " $OLD_PARTUUID " ]; then
113+ echo " Error: PARTUUID 发生了改变!"
114+ echo " 原: $OLD_PARTUUID "
115+ echo " 新: $NEW_PARTUUID "
116+ exit 1
117+ fi
118+ echo " [OK] PARTUUID 保持一致: $NEW_PARTUUID "
119+
120+ # 修改 fstab
121+ mkdir -p " $TEMP_MNT /etc/config"
122+ cat >> " $TEMP_MNT /etc/config/fstab" << EOF
123+
124+ config mount
125+ option target '/'
126+ option uuid '$NEW_UUID '
127+ option enabled '1'
128+ option fstype 'btrfs'
129+ option options 'rw,noatime,compress=zstd,space_cache=v2'
130+ EOF
131+
132+ # 删除所有挂载点为 / 的行,如果有
133+ sed -i ' \#\s/\s#d' " $TEMP_MNT /etc/fstab" 2> /dev/null || true
134+ echo " PARTUUID=$NEW_PARTUUID / btrfs rw,noatime,compress=zstd,space_cache=v2 0 0" >> " $TEMP_MNT /etc/fstab"
135+
136+ # --- 收尾 ---
137+ umount " $TEMP_MNT "
138+ losetup -d " $LOOP_DEV "
139+ LOOP_DEV=" "
140+
141+ if [ " $GITHUB_ACTIONS " != " true" ]; then
142+ echo " 7. 压缩输出文件..."
143+ pigz -9 " $WORK_IMG "
144+ # 这里的 WORK_IMG 已经是替换过名字的文件,pigz 会生成 WORK_IMG.gz
145+ rm -f " $WORK_IMG "
146+ fi
147+
148+ echo " === 转换成功 ==="
0 commit comments