1+ #! /bin/bash
2+
3+ # 检查参数数量
4+ if [ " $# " -ne 2 ]; then
5+ echo " Usage: $0 <source.raw.xz> <target.qcow2>"
6+ exit 1
7+ fi
8+
9+ SOURCE=$1
10+ TARGET=$2
11+ RAW_FILE=" temp.raw"
12+ QCOW2_FILE=" temp.qcow2"
13+ MOUNT_DIR=" mnt"
14+ SOURCE_MOUNT_DIR=" mnt_source"
15+ NBD_DEVICE=" /dev/nbd0"
16+ LOOP_DEVICE=" "
17+ PARTITION_LOOP_DEVICE=" "
18+
19+ # 检查是否以 root 用户运行
20+ if [ " $( id -u) " -ne 0 ]; then
21+ echo " Error: This script must be run as root."
22+ exit 1
23+ fi
24+
25+ # 定义依赖包列表
26+ DEPENDENCIES=(" qemu-utils" " e2fsprogs" " mount" " util-linux" " xz-utils" " rsync" )
27+
28+ # 安装所有依赖项
29+ echo " Installing required dependencies..."
30+ apt-get update -qq
31+ apt-get install -y " ${DEPENDENCIES[@]} "
32+ if [ $? -ne 0 ]; then
33+ echo " Failed to install required dependencies. Exiting..."
34+ exit 1
35+ fi
36+
37+ # 检查 source 文件是否存在
38+ if [ ! -f " $SOURCE " ]; then
39+ echo " Error: Source file $SOURCE does not exist."
40+ exit 1
41+ fi
42+
43+ # 创建临时目录
44+ mkdir -p " $MOUNT_DIR "
45+ mkdir -p " $SOURCE_MOUNT_DIR "
46+
47+ # Step 1: 解压 .raw.xz 文件为 .raw
48+ echo " Decompressing .raw.xz to .raw..."
49+ xz -d -k " $SOURCE " -c > " $RAW_FILE "
50+ if [ $? -ne 0 ]; then
51+ echo " Failed to decompress .raw.xz file."
52+ rm -rf " $MOUNT_DIR " " $SOURCE_MOUNT_DIR "
53+ exit 1
54+ fi
55+
56+ # Step 2: 使用 losetup 挂载 .raw 文件为块设备
57+ echo " Attaching .raw file to loop device..."
58+ LOOP_DEVICE=$( losetup -f)
59+ losetup " $LOOP_DEVICE " " $RAW_FILE "
60+ if [ $? -ne 0 ]; then
61+ echo " Failed to attach .raw file to loop device."
62+ rm -f " $RAW_FILE "
63+ rm -rf " $MOUNT_DIR " " $SOURCE_MOUNT_DIR "
64+ exit 1
65+ fi
66+
67+ # Step 3: 获取第三个分区的起始扇区和大小
68+ echo " Reading partition table..."
69+ PART_INFO=$( fdisk -l " $LOOP_DEVICE " | grep " ^${LOOP_DEVICE} p" )
70+ if [ $? -ne 0 ]; then
71+ echo " Failed to read partition table."
72+ losetup -d " $LOOP_DEVICE "
73+ rm -f " $RAW_FILE "
74+ rm -rf " $MOUNT_DIR " " $SOURCE_MOUNT_DIR "
75+ exit 1
76+ fi
77+
78+ # 提取第三个分区的信息
79+ PART_START=$( echo " $PART_INFO " | awk ' NR==3 {print $2}' )
80+ PART_SIZE=$( echo " $PART_INFO " | awk ' NR==3 {print $4}' )
81+ if [ -z " $PART_START " ] || [ -z " $PART_SIZE " ]; then
82+ echo " Failed to extract partition information for the third partition."
83+ losetup -d " $LOOP_DEVICE "
84+ rm -f " $RAW_FILE "
85+ rm -rf " $MOUNT_DIR " " $SOURCE_MOUNT_DIR "
86+ exit 1
87+ fi
88+
89+ # Step 4: 使用 losetup 挂载第三个分区
90+ echo " Attaching third partition to loop device..."
91+ PARTITION_LOOP_DEVICE=$( losetup -f)
92+ losetup -o $(( PART_START * 512 )) --sizelimit $(( PART_SIZE * 512 )) " $PARTITION_LOOP_DEVICE " " $LOOP_DEVICE "
93+ if [ $? -ne 0 ]; then
94+ echo " Failed to attach third partition to loop device."
95+ losetup -d " $LOOP_DEVICE "
96+ rm -f " $RAW_FILE "
97+ rm -rf " $MOUNT_DIR " " $SOURCE_MOUNT_DIR "
98+ exit 1
99+ fi
100+
101+ # Step 5: 创建一个 120G 的 qcow2 文件
102+ echo " Creating a 120G qcow2 file..."
103+ qemu-img create -f qcow2 " $QCOW2_FILE " 120G
104+ if [ $? -ne 0 ]; then
105+ echo " Failed to create qcow2 file."
106+ losetup -d " $LOOP_DEVICE "
107+ losetup -d " $PARTITION_LOOP_DEVICE "
108+ rm -f " $RAW_FILE "
109+ rm -rf " $MOUNT_DIR " " $SOURCE_MOUNT_DIR "
110+ exit 1
111+ fi
112+
113+ # Step 6: 使用 nbd 挂载 qcow2 文件
114+ echo " Attaching qcow2 file to NBD device..."
115+ modprobe nbd max_part=8
116+ qemu-nbd -c " $NBD_DEVICE " " $QCOW2_FILE "
117+ if [ $? -ne 0 ]; then
118+ echo " Failed to attach qcow2 file to NBD device."
119+ losetup -d " $LOOP_DEVICE "
120+ losetup -d " $PARTITION_LOOP_DEVICE "
121+ rm -f " $RAW_FILE " " $QCOW2_FILE "
122+ rm -rf " $MOUNT_DIR " " $SOURCE_MOUNT_DIR "
123+ exit 1
124+ fi
125+
126+ # 等待设备就绪
127+ sleep 5
128+
129+ # Step 7: 格式化 nbd 分区为 ext4
130+ echo " Formatting NBD partition with ext4..."
131+ mkfs.ext4 " $NBD_DEVICE "
132+ if [ $? -ne 0 ]; then
133+ echo " Failed to format NBD partition."
134+ qemu-nbd -d " $NBD_DEVICE "
135+ losetup -d " $LOOP_DEVICE "
136+ losetup -d " $PARTITION_LOOP_DEVICE "
137+ rm -f " $RAW_FILE " " $QCOW2_FILE "
138+ rm -rf " $MOUNT_DIR " " $SOURCE_MOUNT_DIR "
139+ exit 1
140+ fi
141+
142+ # Step 8: 挂载分区
143+ echo " Mounting NBD partition..."
144+ mount " $NBD_DEVICE " " $MOUNT_DIR "
145+ if [ $? -ne 0 ]; then
146+ echo " Failed to mount NBD partition."
147+ qemu-nbd -d " $NBD_DEVICE "
148+ losetup -d " $LOOP_DEVICE "
149+ losetup -d " $PARTITION_LOOP_DEVICE "
150+ rm -f " $RAW_FILE " " $QCOW2_FILE "
151+ rm -rf " $MOUNT_DIR " " $SOURCE_MOUNT_DIR "
152+ exit 1
153+ fi
154+
155+ # Step 9: 挂载第三个分区
156+ echo " Mounting third partition..."
157+ mount " $PARTITION_LOOP_DEVICE " " $SOURCE_MOUNT_DIR "
158+ if [ $? -ne 0 ]; then
159+ echo " Failed to mount third partition."
160+ umount " $MOUNT_DIR "
161+ qemu-nbd -d " $NBD_DEVICE "
162+ losetup -d " $LOOP_DEVICE "
163+ losetup -d " $PARTITION_LOOP_DEVICE "
164+ rm -f " $RAW_FILE " " $QCOW2_FILE "
165+ rm -rf " $MOUNT_DIR " " $SOURCE_MOUNT_DIR "
166+ exit 1
167+ fi
168+
169+ # Step 10: 将第三个分区的内容复制到目标分区
170+ echo " Copying data from third partition to mounted directory..."
171+ rsync -a --info=progress2 " $SOURCE_MOUNT_DIR /" " $MOUNT_DIR /"
172+ if [ $? -ne 0 ]; then
173+ echo " Failed to copy data from third partition."
174+ umount " $SOURCE_MOUNT_DIR "
175+ umount " $MOUNT_DIR "
176+ qemu-nbd -d " $NBD_DEVICE "
177+ losetup -d " $LOOP_DEVICE "
178+ losetup -d " $PARTITION_LOOP_DEVICE "
179+ rm -f " $RAW_FILE " " $QCOW2_FILE "
180+ rm -rf " $MOUNT_DIR " " $SOURCE_MOUNT_DIR "
181+ exit 1
182+ fi
183+
184+ umount " $SOURCE_MOUNT_DIR "
185+
186+ # Step 11: 创建 /sbin/init 到 /init 的相对路径软链接
187+ echo " Creating relative symbolic link sbin/init -> init..."
188+ cd " $MOUNT_DIR " || { echo " Failed to change directory to $MOUNT_DIR " ; exit 1; }
189+ ln -srf " sbin/init" " init"
190+ if [ $? -ne 0 ]; then
191+ echo " Failed to create relative symbolic link sbin/init -> init."
192+ cd -
193+ umount " $MOUNT_DIR "
194+ qemu-nbd -d " $NBD_DEVICE "
195+ losetup -d " $LOOP_DEVICE "
196+ losetop -d " $PARTITION_LOOP_DEVICE "
197+ rm -f " $RAW_FILE " " $QCOW2_FILE "
198+ rm -rf " $MOUNT_DIR " " $SOURCE_MOUNT_DIR "
199+ exit 1
200+ fi
201+ cd -
202+
203+ # Step 12: 禁用 kernel 和 kernel-core 的更新
204+ echo " Disabling kernel updates in DNF configuration..."
205+ DNF_CONF=" $MOUNT_DIR /etc/dnf/dnf.conf"
206+ if [ -f " $DNF_CONF " ]; then
207+ # 添加 exclude 规则
208+ echo " exclude=kernel* kernel-core*" >> " $DNF_CONF "
209+ if [ $? -ne 0 ]; then
210+ echo " Failed to update DNF configuration."
211+ umount " $MOUNT_DIR "
212+ qemu-nbd -d " $NBD_DEVICE "
213+ losetup -d " $LOOP_DEVICE "
214+ losetup -d " $PARTITION_LOOP_DEVICE "
215+ rm -f " $RAW_FILE " " $QCOW2_FILE "
216+ rm -rf " $MOUNT_DIR " " $SOURCE_MOUNT_DIR "
217+ exit 1
218+ fi
219+ else
220+ echo " Warning: DNF configuration file not found at $DNF_CONF . Skipping kernel update disable."
221+ fi
222+
223+ # Step 13: 卸载分区并断开 nbd 和 loop 连接
224+ echo " Unmounting and detaching devices..."
225+ umount " $MOUNT_DIR "
226+ qemu-nbd -d " $NBD_DEVICE "
227+ losetup -d " $LOOP_DEVICE "
228+ losetup -d " $PARTITION_LOOP_DEVICE "
229+ if [ $? -ne 0 ]; then
230+ echo " Failed to detach devices."
231+ exit 1
232+ fi
233+
234+ # Step 14: 使用 qemu-img convert 压缩 qcow2 文件为目标文件
235+ echo " Compressing qcow2 file using qemu-img convert..."
236+ qemu-img convert -O qcow2 -c " $QCOW2_FILE " " $TARGET "
237+ if [ $? -ne 0 ]; then
238+ echo " Failed to compress qcow2 file."
239+ exit 1
240+ fi
241+
242+ # 清理临时文件
243+ echo " Cleaning up temporary files..."
244+ rm -rf " $RAW_FILE " " $QCOW2_FILE " " $MOUNT_DIR " " $SOURCE_MOUNT_DIR "
245+
246+ echo " Conversion completed successfully. Target file: $TARGET "
0 commit comments