libdevuansdk

common library for devuan's simple distro kits
git clone https://git.parazyd.org/libdevuansdk
Log | Files | Refs | Submodules | README | LICENSE

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 }