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+ WORK_IMG=" ${BASENAME% .img} -btrfs.img"
40+
41+ # 使用当前目录下的 tmp 文件夹
42+ TEMP_BASE=" ./tmp"
43+ TEMP_MNT=" ${TEMP_BASE} /btrfs_mnt_$$ "
44+ TEMP_DATA=" ${TEMP_BASE} /btrfs_data_$$ "
45+ LOOP_DEV=" "
46+
47+ # 定义退出清理函数
48+ cleanup () {
49+ mountpoint -q " $TEMP_MNT " && umount " $TEMP_MNT "
50+ [ -n " $LOOP_DEV " ] && losetup -d " $LOOP_DEV "
51+ # 仅删除本次运行生成的临时子目录,保留 ./tmp 父目录
52+ rm -rf " $TEMP_MNT " " $TEMP_DATA "
53+ }
54+ trap cleanup EXIT
55+
56+ # --- 核心流程 ---
57+
58+ echo " 1. 解压镜像..."
59+ pigz -d -c " $SOURCE_FILE " > " $WORK_IMG "
60+
61+ echo " 2. 挂载镜像..."
62+ LOOP_DEV=$( losetup -fP --show " $WORK_IMG " )
63+ ROOT_PART=" ${LOOP_DEV} p2" # 默认 OpenWrt Rootfs 为 p2
64+
65+ if [ ! -b " $ROOT_PART " ]; then
66+ echo " Error: 未找到分区 $ROOT_PART "
67+ exit 1
68+ fi
69+
70+ OLD_UUID=$( blkid -s UUID -o value " $ROOT_PART " )
71+ echo " 原 UUID 为: $OLD_UUID "
72+ OLD_PARTUUID=$( blkid -s PARTUUID -o value " $ROOT_PART " )
73+ echo " 原 Partition UUID : $OLD_PARTUUID "
74+
75+ # 创建临时目录
76+ mkdir -p " $TEMP_MNT " " $TEMP_DATA "
77+
78+ echo " 3. 提取原系统数据..."
79+ mount " $ROOT_PART " " $TEMP_MNT "
80+ rsync -aAX --exclude " lost+found" " $TEMP_MNT /" " $TEMP_DATA /"
81+ umount " $TEMP_MNT "
82+
83+ echo " 4. 格式化为 Btrfs..."
84+ mkfs.btrfs -f -L rootfs -m single -U " $OLD_UUID " " $ROOT_PART "
85+
86+ echo " 5. 还原数据..."
87+ mount -t btrfs -o compress=zstd " $ROOT_PART " " $TEMP_MNT "
88+ btrfs property set " $TEMP_MNT " compression zstd
89+ rsync -aAX " $TEMP_DATA /" " $TEMP_MNT /"
90+
91+ echo " 6. 更新系统配置..."
92+ # 验证 Filesystem UUID (mkfs -U 参数的效果)
93+ NEW_UUID=$( blkid -s UUID -o value " $ROOT_PART " )
94+ echo " 验证 UUID 完整性..."
95+ if [ " $NEW_UUID " != " $OLD_UUID " ]; then
96+ echo " Error: Filesystem UUID 未能保留!"
97+ echo " 期望: $OLD_UUID "
98+ echo " 实际: $NEW_UUID "
99+ exit 1
100+ fi
101+ echo " [OK] Filesystem UUID 保持一致: $NEW_UUID "
102+ # 验证 PARTUUID (mkfs 通常不会改变这个)
103+ NEW_PARTUUID=$( blkid -s PARTUUID -o value " $ROOT_PART " )
104+ if [ " $NEW_PARTUUID " != " $OLD_PARTUUID " ]; then
105+ echo " Error: PARTUUID 发生了改变!"
106+ echo " 原: $OLD_PARTUUID "
107+ echo " 新: $NEW_PARTUUID "
108+ exit 1
109+ fi
110+ echo " [OK] PARTUUID 保持一致: $NEW_PARTUUID "
111+
112+ # 修改 fstab
113+ mkdir -p " $TEMP_MNT /etc/config"
114+ cat >> " $TEMP_MNT /etc/config/fstab" << EOF
115+
116+ config mount
117+ option target '/'
118+ option uuid '$NEW_UUID '
119+ option enabled '1'
120+ option fstype 'btrfs'
121+ option options 'rw,noatime,compress=zstd,space_cache=v2'
122+ EOF
123+
124+ # 删除所有挂载点为 / 的行,如果有
125+ sed -i ' \#\s/\s#d' " $TEMP_MNT /etc/fstab" 2> /dev/null || true
126+ echo " PARTUUID=$NEW_PARTUUID / btrfs rw,noatime,compress=zstd,space_cache=v2 0 0" >> " $TEMP_MNT /etc/fstab"
127+
128+ # --- 收尾 ---
129+ umount " $TEMP_MNT "
130+ losetup -d " $LOOP_DEV "
131+ LOOP_DEV=" "
132+
133+ if [ " $GITHUB_ACTIONS " != " true" ]; then
134+ echo " 7. 压缩输出文件..."
135+ pigz -9 " $WORK_IMG "
136+ rm -f " $WORK_IMG "
137+ fi
138+
139+ echo " === 转换成功 ==="
0 commit comments