imaging (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 }