timaging - libdevuansdk - common library for devuan's simple distro kits
HTML git clone https://git.parazyd.org/libdevuansdk
DIR Log
DIR Files
DIR Refs
DIR Submodules
DIR README
DIR LICENSE
---
timaging (8971B)
---
1 #!/usr/bin/env zsh
2 # shellcheck shell=bash
3 # Copyright (c) 2016-2021 Ivan J. <parazyd@dyne.org>
4 # This file is part of libdevuansdk
5 #
6 # This source code is free software: you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation, either version 3 of the License, or
9 # (at your option) any later version.
10 #
11 # This software is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
17 # along with this source code. If not, see <http://www.gnu.org/licenses/>.
18
19 vars+=(bootpart rootpart loopdevice)
20
21 strapdir_to_image()
22 {
23 fn strapdir_to_image
24 req=(workdir strapdir)
25 ckreq || return 1
26
27 notice "Copying strapdir to image ..."
28
29 if [[ ! -d "$workdir/mnt" ]]; then
30 die "$workdir/mnt doesn't exist. Did you run image_mount?"
31 zerr; return 1
32 fi
33
34 pushd "$strapdir"
35 sudo find . \
36 -not -path "./dev/*" \
37 -a -not -path "./proc/*" \
38 -a -not -path "./sys/*" \
39 | sudo cpio --quiet -adm -p "$workdir/mnt" || { zerr; return 1; }
40 popd
41 }
42
43 image_prepare_raw()
44 {
45 fn image_prepare_raw
46 req=(workdir size image_name)
47 ckreq || return 1
48
49 notice "Creating raw image of $size MB"
50 touch "$workdir/${image_name}.img"
51 chattr -f +C "$workdir/${image_name}.img"
52 dd if=/dev/zero of="$workdir/${image_name}.img" bs=1M count="$size" || { zerr; return 1; }
53 }
54
55 image_prepare_qcow2()
56 {
57 fn image_prepare_qcow2
58 req=(workdir size image_name)
59 ckreq || return 1
60
61 notice "Creating qcow2 image of $size MB"
62 touch "$workdir/${image_name}.qcow2"
63 chattr -f +C "$workdir/${image_name}.qcow2"
64 qemu-img create -f qcow2 "${workdir}/${image_name}.qcow2" "${size}M" || { zerr; return 1; }
65 }
66
67 image_format_partitions()
68 {
69 fn image_format_partitions
70 req=(bootfs bootpart rootpart)
71 ckreq || return 1
72
73 notice "Formatting image partitions"
74
75 case "$bootfs" in
76 none)
77 act "Skipping boot partition"
78 ;;
79 vfat|fat|dos)
80 act "Formatting boot as VFAT"
81 sudo mkfs.vfat ${=bootopts} "${bootpart}" >>/dev/null || { zerr; return 1; }
82 ;;
83 ext?)
84 act "Formatting boot as $bootfs"
85 sudo mkfs.${bootfs} ${=bootopts} "${bootpart}" || { zerr; return 1; }
86 ;;
87 btrfs)
88 act "Formatting boot as btrfs"
89 sudo mkfs.btrfs ${=bootopts} "${bootpart}" || { zerr; return 1; }
90 ;;
91 f2fs)
92 act "Formatting boot as f2fs"
93 sudo mkfs.f2fs ${=bootopts} "${bootpart}" || { zerr; return 1; }
94 ;;
95 "")
96 die "No bootfs filesystem set!"
97 zerr; return 1
98 ;;
99 *)
100 die "Unimplemented filesystem: $bootfs"
101 die "Please report it for inclusion."
102 zerr; return 1
103 ;;
104 esac
105
106 case "$rootfs" in
107 none)
108 act "Skipping root partition"
109 ;;
110 vfat|fat|dos)
111 act "Formatting root as VFAT"
112 sudo mkfs.vfat ${=rootopts} "${rootpart}" >>/dev/null || { zerr; return 1; }
113 ;;
114 ext?)
115 act "Formatting root as $rootfs"
116 sudo mkfs.${rootfs} ${=rootopts} "${rootpart}" || { zerr; return 1; }
117 ;;
118 btrfs)
119 act "Formatting root as btrfs"
120 sudo mkfs.btrfs ${=rootopts} "${rootpart}" || { zerr; return 1; }
121 ;;
122 f2fs)
123 act "Formatting root as f2fs"
124 sudo mkfs.f2fs ${=rootopts} "${rootpart}" || { zerr; return 1; }
125 ;;
126 "")
127 die "No rootfs filesystem set!"
128 zerr; return 1
129 ;;
130 *)
131 die "Unimplemented filesystem: $rootfs"
132 die "Please report it for inclusion."
133 zerr; return 1
134 ;;
135 esac
136 }
137
138 image_connect_raw()
139 {
140 fn image_connect_raw
141
142 notice "Connecting raw image to loop device"
143
144 loopdevice="$(findloopdev)"
145 if [[ -z "$loopdevice" ]]; then
146 die "Didn't find a free loop device"
147 zerr; return 1
148 fi
149
150 bootpart="${loopdevice}p1"
151 rootpart="${loopdevice}p2"
152 }
153
154 image_connect_qcow2()
155 {
156 fn image_connect_qcow2
157 req=(workdir image_name)
158 ckreq || return 1
159
160 notice "Connecting qcow2 image to nbd device"
161
162 sudo modprobe nbd max_part=8 || { zerr; return 1; }
163 loopdevice="$(findnbddev)"
164 if [[ -z "$loopdevice" ]]; then
165 die "Didn't find a free nbd device"
166 zerr; return 1
167 fi
168
169 sudo qemu-nbd --connect="${loopdevice}" "$workdir/${image_name}.qcow2" || { zerr; return 1; }
170 }
171
172 image_partition_dos()
173 {
174 fn image_partition_dos
175 req=(loopdevice dos_boot dos_root)
176 ckreq || return 1
177
178 notice "Partitioning dos image"
179
180 sudo parted "$loopdevice" --script -- mklabel msdos || { zerr; return 1; }
181 sudo parted "$loopdevice" --script -- mkpart primary "$dos_boot" || { zerr; return 1; }
182 sudo parted "$loopdevice" --script -- mkpart primary "$dos_root" || { zerr; return 1; }
183 if [[ -n "$bootable_part" ]]; then
184 sudo parted "$loopdevice" --script -- set "$bootable_part" boot on
185 fi
186
187 sudo partprobe "$loopdevice" || { zerr; return 1; }
188 }
189
190 image_partition_gpt()
191 {
192 fn image_partition_gpt
193 req=(loopdevice bootpart rootpart gpt_boot gpt_root)
194 ckreq || return 1
195
196 notice "Partitioning gpt image"
197
198 sudo parted "$loopdevice" --script -- mklabel gpt || { zerr; return 1; }
199 sudo cgpt create -z "$loopdevice" || { zerr; return 1; }
200 sudo cgpt create "$loopdevice" || { zerr; return 1; }
201
202 sudo cgpt add -i 1 -t kernel -b ${gpt_boot[1]} -s ${gpt_boot[2]} \
203 -l kernel -S 1 -T 5 -P 10 "$loopdevice" || { zerr; return 1; }
204
205 sudo cgpt add -i 2 -t data -b ${gpt_root[1]} -s \
206 $(expr $(sudo cgpt show "$loopdevice" \
207 | awk '/Sec GPT table/ {print $1}') - ${gpt_root[1]}) \
208 -l Root "$loopdevice" || { zerr; return 1; }
209
210 sudo partprobe "$loopdevice" || { zerr; return 1; }
211 }
212
213 image_mount()
214 {
215 fn image_mount
216 req=(workdir bootpart rootpart bootfs)
217 ckreq || return 1
218
219 notice "Mounting image to $workdir/mnt"
220
221 mkdir -p "$workdir/mnt"
222 sudo mount "$rootpart" "$workdir/mnt" || { zerr; return 1; }
223 act "Mounted root partition"
224
225 if [[ "$bootfs" = none ]]; then
226 return
227 fi
228
229 sudo mkdir -p "$workdir/mnt/boot"
230 sudo mount "$bootpart" "$workdir/mnt/boot" || { zerr; return 1; }
231 act "Mounted boot partition"
232 }
233
234 image_umount()
235 {
236 fn image_umount
237 req=(workdir loopdevice)
238 ckreq || return 1
239
240 notice "Umounting image from $workdir/mnt"
241
242 sudo umount -R "$workdir/mnt" || { zerr; return 1; }
243 act "Umounted"
244
245 act "Flushing bytes and buffers"
246 sudo blockdev --flushbufs "$loopdevice" || { zerr; return 1; }
247 sudo python -c 'import os; os.fsync(open("'$loopdevice'", "r+b"))' || { zerr; return 1; }
248 }
249
250 image_disconnect_raw()
251 {
252 fn image_disconnect_raw
253 req=(loopdevice bootfs rootfs bootpart rootpart)
254 ckreq || return 1
255
256 notice "Disconnecting image from $loopdevice"
257
258 act "Rechecking filesystems"
259 case "$bootfs" in
260 ext?)
261 sudo e2fsck -fy "$bootpart"
262 sudo resize2fs "$bootpart"
263 ;;
264 esac
265
266 case "$rootfs" in
267 ext?)
268 sudo e2fsck -fy "$rootpart"
269 sudo resize2fs "$rootpart"
270 ;;
271 esac
272
273 act "Disconnecting"
274 sudo partx -dv "$loopdevice" >>/dev/null || {
275 die "partx failed to remove $loopdevice"
276 zerr; return 1
277 }
278
279 sudo losetup -d "$loopdevice" || {
280 die "losetup failed to remove $loopdevice"
281 zerr; return 1
282 }
283 }
284
285 image_disconnect_qcow2()
286 {
287 fn image_disconnect_qcow2
288 req=(loopdevice bootfs rootfs bootpart rootpart)
289 ckreq || return 1
290
291 notice "Disconnecting image from $loopdevice"
292
293 act "Rechecking filesystems"
294 case "$bootfs" in
295 ext?)
296 sudo e2fsck -fy "$bootpart"
297 sudo resize2fs "$bootpart"
298 ;;
299 esac
300
301 case "$rootfs" in
302 ext?)
303 sudo e2fsck -fy "$rootpart"
304 sudo resize2fs "$rootpart"
305 ;;
306 esac
307
308 act "Disconnecting"
309
310 sudo qemu-nbd --disconnect "$loopdevice" || { zerr; return 1; }
311 }
312
313 image_raw_to_qcow2()
314 {
315 fn image_raw_to_qcow2
316 req=(image_name workdir)
317 ckreq || return 1
318
319 notice "Converting raw image to qcow2"
320 pushd "$workdir" || { zerr; return 1; }
321 touch "${image_name}.qcow2"
322 chattr -f +C "${image_name}.qcow2"
323 qemu-img convert -f raw -O qcow2 "${image_name}.img" "${image_name}.qcow2" || { zerr; return 1; }
324 popd
325 }
326
327 image_raw_to_vdi()
328 {
329 fn image_raw_to_vdi
330 req=(image_name workdir)
331 ckreq || return 1
332
333 notice "Converting raw image to vdi"
334 pushd "$workdir" || { zerr; return 1; }
335 touch "${image_name}.vdi"
336 chattr -f +C "${image_name}.vdi"
337 qemu-img convert -f raw -O vdi "${image_name}.img" "${image_name}.vdi" || { zerr; return 1; }
338 #VBoxManage modifyhd "${image_name}.vdi" --type immutable --compact || { zerr; return 1; }
339 popd
340 }
341
342 image_pack_dist()
343 {
344 fn image_pack_dist
345 req=(R image_name workdir)
346 ckreq || return 1
347
348 notice "Packing up built images"
349
350 local _xzcomp=""
351 local _rsuffix="img"
352
353 if [[ -n "$COMPRESS_IMAGE" ]]; then
354 if command -v pixz >/dev/null; then
355 _xzcomp="$(command -v pixz)"
356 else
357 _xzcomp="$(command -v xz)"
358 fi
359 _rsuffix="img.xz"
360 fi
361
362 pushd "$workdir" || { zerr; return 1; }
363
364 if [[ -n "$COMPRESS_IMAGE" ]]; then
365 act "Compressing images with $_xzcomp"
366 silly
367 $_xzcomp "${image_name}.img" || { zerr; return 1; }
368 # TODO: cpio image?
369 fi
370
371 act "Calculating sha256 checksums"
372 silly
373 sha256sum "${image_name}.${_rsuffix}" > "${image_name}.${_rsuffix}.sha256"
374 # TODO: cpio image?
375 mkdir -p "$R/dist"
376 mv -v "${image_name}".* "$R/dist" || { zerr; return 1; }
377
378 notice "Done! Thanks for being patient!"
379
380 popd
381 }