#!/bin/bash
#
# This file is sourced by sdm for the burn and shrink commands
# It is not callable by anything other than sdm
# because it needs access to a bazillion variables defined
# in sdm, so can only run in the context of sdm. 
#
function write_burnmsg() {
    #
    # $1 has the message to write
    # It's written to the terminal and added to burnmsg
    #
    local msg="$1"
    if [[ "$SDMPT" != "" ]] && [[ $fpartcopy -eq 0 ]]
    then
	if ismounted $SDMPT && [[ -f $SDMPT/etc/sdm/history ]]
	then
	    logtoboth "$msg"
	else
	    burnmsg+=("$(thisdate) $msg")
	    echo "$msg"
	fi
    else
	burnmsg+=("$(thisdate) $msg")
	echo "$msg"
    fi
    return 0
}    

function ddctrlc() {
    printf "\n%% Caught CTRL/C during burn. Cleaning up...\n"
    if [ "$ddpid" != "" ]
    then
	kill -HUP $ddpid >/dev/null 2>&1
	wait $ddpid >/dev/null 2>&1
	exit 1
    fi
}

function setdiskid() {
    local dev="$1" diskid="$2"
    sfdisk --disk-id $dev 0x${diskid}
    if [ $? -ne 0 ]    # Assume it's an older version of sfdisk without --disk-id if it fails
    then
	write_burnmsg "% sfdisk failed; Trying fdisk to set Disk ID"
        fdisk $dev >/dev/null <<EOF
x
i
0x$diskid
r
w
EOF
        errifrc $? "? fdisk error"
	write_burnmsg "> Disk ID '$diskid' set successfully with fdisk"
    else
	write_burnmsg "> Disk ID '$diskid' set successfully with sfdisk"
    fi
}

function writediskids() {
    local gptuuid1 gptuuid2

    if [ -f $SDMPT/boot/firmware/cmdline.txt ]
    then
	if [ $fgpt -eq 0 ]
	then
	    write_burnmsg "> Set MBR rootfs partition ID '$newdiskid' in cmdline.txt (was $olddiskid)"
	    sed -i "s/${olddiskid}/${newdiskid}/" $SDMPT/boot/firmware/cmdline.txt
	else
	    gptuuid1=$(blkid $(getspname $burndev 1) | sed -n 's|^.*PARTUUID="\(\S\+\)".*|\1|p')
	    gptuuid2=$(blkid $(getspname $burndev 2) | sed -n 's|^.*PARTUUID="\(\S\+\)".*|\1|p')
	    if [ "$cvtrootfs" == "lvm" ]
	    then
		write_burnmsg "> Set GPT rootfs '$mapper' in cmdline.txt"
		sed -i "s|root=PARTUUID=${olddiskid}-02|root=$mapper|" $SDMPT/boot/firmware/cmdline.txt
		gptpart2=$mapper
	    else
		write_burnmsg "> Set GPT rootfs partition GUID '$gptuuid2' in cmdline.txt"
		sed -i "s/${olddiskid}-02/$gptuuid2/" $SDMPT/boot/firmware/cmdline.txt
		gptpart2="PARTUUID=$gptuuid2"
	    fi
	fi
	# init_resize disappeared in 2022-09-06 build, not in systems with /boot/firmware
    elif [ -f $SDMPT/boot/cmdline.txt ]
    then
	write_burnmsg "> Set new Disk ID '$newdiskid' in /etc/fstab and /boot/cmdline.txt"
	sed -i "s/${olddiskid}/${newdiskid}/" $SDMPT/boot/cmdline.txt
	# Quirks not there in bullseye or bookworm, but keep for posterity
	#sed -i 's| sdhci\.debug_quirks2=4||' $SDMPT/boot/cmdline.txt
	# init_resize disappeared in 2022-09-06 build, but keep this here for older IMGs
	sed -i 's| init=/usr/lib/raspi-config/init_resize\.sh||' $SDMPT/boot/cmdline.txt
    fi
    if [ $fgpt -eq 0 ]
    then
	write_burnmsg "> Set MBR partition Disk IDs in fstab"
	sed -i "s/${olddiskid}/${newdiskid}/g" $SDMPT/etc/fstab
    else
	write_burnmsg "> Set GPT partition GUIDs in fstab"
	sed -i "s/${olddiskid}-01/$gptuuid1/" $SDMPT/etc/fstab
	sed -i "s|PARTUUID=${olddiskid}-02|$gptpart2|" $SDMPT/etc/fstab
    fi
}

function copybypart() {
    #
    # Copy the partitions one at a time and change rootfs format
    #
    local srcdev="$1" dstdev="$2" dsttype="${3:-dos}" fstype="${4:-ext4}"
    local fatnum fatstart fatend fatsize
    local extnum extstart extend extsize
    local misc p1 p2 rootfs

    p1=$(getpartname $dstdev 1)
    p2=$(getpartname $dstdev 2)
    while read line
    do
	if [[ "$line" =~ "msdos" ]] || [[ "$line" =~ "fat32" ]]
	then
            IFS=":" read fatnum fatstart fatend fatsize misc <<< $line
	elif [[ "$line" =~ "ext4" ]]
	then
            IFS=":" read extnum extstart extend extsize misc <<< $line
	fi
    done < <(parted --machine --script $srcdev unit S print)

    if [[ "$fatnum" == "" ]] || [[ "$extnum" == "" ]]
    then
	errexit "? Expected partitions not found on $srcdev"
    fi
    #
    # Delete old partitions on destination and set disk label type
    # If lvm on the disk then delete it
    #
    write_burnmsg "* Remove partitions on $dstdev"
    [ "$(lvmfind $dstdev pv)" != "" ] && lvmdelete $dstdev
    # Just in case
    wipefs --all ${dstdev}$p1 >/dev/null 2>&1
    wipefs --all ${dstdev}$p2 >/dev/null 2>&1
    sgdisk --zap-all $dstdev >/dev/null 2>&1
    partprobe #>/dev/null 2>&1
    if [ "$dsttype" == "dos" ]
    then
	write_burnmsg "* Create MBR partitions on $dstdev"
	echo "label:$dsttype" | sfdisk --force $dstdev >/dev/null 2>&1
	errifrc $? "? Error creating label on $dstdev"
	parted -s $dstdev mkpart primary fat32 ${fatstart} ${fatend}
	errifrc $? "? Error creating boot partition on $dstdev"
	parted -s $dstdev mkpart primary ext4 ${extstart} ${extend}
	errifrc $? "? Error creating root partition on $dstdev"
	write_burnmsg "> Set new disk ID '$newdiskid' on '$burndev'"
	setdiskid $dstdev $newdiskid
    else
	write_burnmsg "> Create GPT partitions on $dstdev"
	parted -s $dstdev mklabel gpt
	errifrc $? "? Error creating label on $dstdev"
	sgdisk --new 1:$fatstart:$fatend $dstdev >/dev/null
	errifrc $? "? Error creating boot partition on $dstdev"
	sgdisk --typecode 1:0700 $dstdev >/dev/null
	errifrc $? "? Error modifying partition 1 type on $dstdev"
	[ "$fstype" == "zfs" ] && tc="bf00" || tc="8300"
	sgdisk --new 2:$extstart:$extend $dstdev --typecode 2:$tc >/dev/null
	errifrc $? "? Error creating root partition on $dstdev"
    fi
    partprobe

    #declare -x SDMPT=$(makemtpt)
    #declare -x SDMPX=$(makemtptX)
    write_burnmsg "> Create bootfs file system on $dstdev"
    mkfs.vfat -F 32 ${dstdev}$p1 #>/dev/null 2>&1
    errifrc $? "? Error creating bootfs file system on $dstdev"
    rootdev=${dstdev}$p2
    rootfs=$rootdev
    case "$fstype" in
	ext4)
	    write_burnmsg "> Create 'ext4' rootfs file system on $rootdev"
	    mkfs.ext4 -F ${dstdev}$p2 >/dev/null 2>&1
	    #dbgshell "Point 1"
	    errifrc $? "? Error creating ext4 rootfs file system on $dstdev"
	    ;;
	btrfs)
	    write_burnmsg "> Create 'btrfs' rootfs file system on $rootdev"
	    mkfs.btrfs -f ${dstdev}$p2 >/dev/null
	    errifrc $? "? Error creating btrfs rootfs file system on $dstdev"
	    ;;
	lvm)
	    rootfs="/dev/$vgname/rootfs"
	    write_burnmsg "> Create 'lvm' rootfs file system on $rootdev"
	    parted -s $dstdev set 1 boot on
	    errifrc $? "? Error setting partition 1 'boot on' on $rootdev"
	    parted -s $dstdev set 2 lvm on
	    errifrc $? "? Error setting partition 2 'lvm on' on $rootdev"
	    lvremove $rootfs --yes >/dev/null 2>&1
	    vgremove /dev/$vgname --yes >/dev/null 2>&1
	    pvremove $rootdev --yes >/dev/null 2>&1
	    partprobe
	    write_burnmsg "> Create LVM physical volume: pvcreate $rootdev"
	    pvcreate $rootdev --force --force --yes
	    errifrc $? "? Error initializing partition 2 lvm on $dstdev"
	    write_burnmsg "> Create LVM volume group: vgcreate $vgname $rootdev"
	    vgcreate $vgname $rootdev --force --force --yes
	    errifrc $? "? Error creating volume group '$vgname' on $dstdev"
	    write_burnmsg "> Create LVM logical volume: lvcreate --name rootfs $vgname"
	    lvcreate --name rootfs -l 100%FREE $vgname --yes
	    errifrc $? "? Error creating logical volume $(basename $rootfs) in volume group $(hostname) on $dstdev"
	    sgdisk --mbrtogpt --typecode 1:0700 $dstdev >/dev/null
	    errifrc $? "? Error modifying partition 1 type on $dstdev"
	    partprobe
	    write_burnmsg "> Create 'ext4' rootfs file system on $rootfs"
	    mkfs.ext4 -F $rootfs >/dev/null 2>&1
	    errifrc $? "? Error creating ext4 rootfs file system on $rootfs"
	    ;;
	zfs)
	    write_burnmsg "> Create 'zfs' rootfs file system on $rootdev"

	    zfsoptimized=0
	    rpool=rpool
	    p2name="${dstdev##/dev/}${p2}"
	    # *** NOTE *** -f
	    zpool create -f -o ashift=12 -o autotrim=on -O acltype=posixacl -O canmount=off -O compression=lz4 -O dnodesize=auto -O normalization=formD -O relatime=on -O xattr=sa  $rpool $p2name
	    errifrc $? "? Error creating zfs pool"
	    zfs create -o canmount=off -o mountpoint=none $rpool/ROOT
	    zfs create -o canmount=noauto -o mountpoint=$SDMPT $rpool/ROOT/debian
	    zfs mount $rpool/ROOT/debian
	    if [ $zfsoptimized -eq 1 ]
	    then
		zfs create $rpool/home
		zfs create -o mountpoint=/root $rpool/home/root
		chmod 700 $SDMPT/root
		zfs create -o canmount=off -o mountpoint=/var $rpool/var
		zfs create -o canmount=off  $rpool/var/lib
		zfs create $rpool/var/log
		zfs create $rpool/var/spool
		zfs create -o com.sun:auto-snapshot=false $rpool/var/cache
		zfs create -o com.sun:auto-snapshot=false $rpool/var/lib/nfs
		zfs create -o com.sun:auto-snapshot=false $rpool/var/tmp
		chmod 1777 $SDMPT/var/tmp
		zfs create $rpool/srv
		zfs create -o canmount=off $rpool/usr
		zfs create $rpool/usr/local
		zfs create $rpool/var/lib/AccountsService
		zfs create $rpool/var/lib/NetworkManager
		zfs create -o com.sun:auto-snapshot=false $rpool/var/lib/docker
		zfs create -o com.sun:auto-snapshot=false $rpool/tmp
		chmod 1777 $SDMPT/tmp
		mkdir -m 755 $SDMPT/run
		mkdir -m 1777 $SDMPT/run/lock
		zfs create $rpool/var/mail
		zfs create $rpool/var/lib/apt
		zfs create $rpool/var/lib/dpkg
		zfs create $rpool/boot
		mkdir -p -m 755 $SDMPT/boot/firmware
	    fi
	    zfs unmount $rpool/ROOT/debian
	    ;;
	*)
	    errexit "? Unrecognized fstype '$fstype'"
	    ;;
    esac

    domount $srcdev IMG $SDMPX

    [ -d $SDMPX/boot/firmware ] && msdir="$SDMPX/boot/firmware" || msdir="$SDMPX/boot"
    [ -d $SDMPX/boot/firmware ] && mddir="$SDMPT/boot/firmware" || mddir="$SDMPT/boot"
    # Create the bootfs so domount works
    if [ "$fstype" != "zfs" ]
    then
	mount $rootfs $SDMPT
	mkdir -p $mddir
	umount $SDMPT
    else
	zfs mount rpool/ROOT/debian
	mkdir -p $mddir
	zfs unmount rpool/ROOT/debian
    fi

    domount $dstdev "Device" $SDMPT $fstype
    write_burnmsg "> Copy bootfs to $dstdev"
    rsync -aH $msdir/ $mddir
    errifrc $? "? Error copying bootfs partition to $dstdev"

    write_burnmsg "> Copy rootfs to $dstdev"
    rsync -aH $SDMPX/ $SDMPT
    errifrc $? "? Error copying rootfs partition to $dstdev"
    sync ; sync
    write_burnmsg "> rootfs copy complete"
}

function sdm_burndevfile() {

    # Stash burn messages in an array until log on SD Card is mounted and available 
    declare -x SDMNSPAWN="Burn0"
    declare -a burnmsg
    fpartcopy=0
    write_burnmsg "* Start Burn"
    burnstart="$(getcdate)"
    newdiskid="$(printf  "%08x" "$(((RANDOM*RANDOM*RANDOM*RANDOM+RANDOM)&0xFFFFFFFF))")"
    if [ $burn -eq 1 ]
    then
	#
	# Burning to a device
	#
	[ ! -b $burndev ] && errexit "? '$burndev' is not a block device"
	[ "$burndev" == "" ] && errexit "? No storage device specified"
	perr=0
	for p in 1 2 3 4 5 6 7 8
	do
	    pdev="$(getpartname $burndev $p)"
	    ismounted ${burndev}${pdev} && echo "? Partition ${burndev}${pdev} is mounted" && perr=1
	done
	[ $perr -eq 1 ] && errexit "? Use 'sudo umount' to unmount each mounted partition, then redo the burn command"
	notmounted $burndev || errexit "? Device '$burndev' is mounted"   # one last check
	! sfdisk -l $burndev >/dev/null 2>&1 && errexit "? Cannot access '$burndev'; is there a disk in the drive?"
	bsize=$(stat --printf %s $dimg)
	write_burnmsg "> Burn '$dimg' $(getgbstr $bsize) to '$burndev'..."
	olddiskid="$((blkid -o value $dimg) | (read id ; echo $id))"
	if [ "$cvtrootfs" == "" ]
	then
	    write_burnmsg "> Burn command line: $cmdline"
	    ddcmd="dd if=$dimg of=$burndev status=progress $ddsw"
	    write_burnmsg "> dd command: $ddcmd"
	    # Burn the IMG to the device but zero first and last blocks first
	    zero1stlast $burndev
	    #echo "$ddcmd"
	    trap "ddctrlc" SIGINT
	    $ddcmd &
	    ddpid=$!
	    wait $ddpid >/dev/null 2>&1
	    [ $? -ne 0 ] && errexit "? Exiting due to dd error"
	    trap SIGINT
	    write_burnmsg "> dd Copy completed"
	    sync ; sleep 1 ; sync
	    if [ $fgpt -eq 0 ]
	    then
		write_burnmsg "> Set new disk ID '$newdiskid' on '$burndev'"
		setdiskid $burndev $newdiskid
	    else
		write_burnmsg "> Convert disk '$burndev' to GPT"
		sgdisk --mbrtogpt --randomize-guids $burndev >/dev/null 2>&1
		errifrc $? "? Error converting MBR to GPT"
		write_burnmsg "> Set boot partition boot code 0c on '$burndev'"
		gdisk $burndev <<EOF &>/dev/null
r
h
1
n
0c
n
n
w
y
EOF
		errifrc $? "? Error Setting MBR hex code"
	    fi
	else
	    [ $fgpt -eq 0 ] && pt=dos || pt=gpt
	    declare -x SDMPT=$(makemtpt)
	    declare -x SDMPX=$(makemtptX)
	    [ "$hname" == "" ] && vgname="vg1" || vgname=$hname
	    mapper="/dev/mapper/$vgname-rootfs"
	    fpartcopy=1
	    copybypart $dimg $burndev $pt $cvtrootfs
	    fpartcopy=0
	    docleanup
	    sync ; sleep 1 ; sync
	fi
	partprobe
	sync ; sleep 1 ; sync
	declare -x SDMPT=$(makemtpt)
	domount "$burndev" "Device" $SDMPT $cvtrootfs
    else
	#
	# Burning to a file
	#
	[ "$burnfilefile" == "" ] && errexit "? No Output IMG file specified"
	[ -f $burnfilefile ] && errexit "? Output IMG file '$burnfilefile' exists"
	bsize=$(stat --printf %s $dimg)
	write_burnmsg "* Burn '$dimg' $(getgbstr $bsize) to IMG '$burnfilefile'..."
	burnmsg+=("$(thisdate) > Burn IMG command line: $cmdline")
	# cp will fail if not on a COW (btrfs) file system
	cp --reflink=always $dimg $burnfilefile 2>/dev/null
	if [ $? -eq 0 ]
	then
	    burnmsg+=("$(thisdate) > Burn IMG using 'cp' on copy-on-write file system")
	else
	    ddcmd="dd if=$dimg of=$burnfilefile status=progress $ddsw"
	    write_burnmsg "> dd command: $ddcmd"
	    # Burn the IMG to the file
	    #echo "$ddcmd"
	    trap "ddctrlc" SIGINT
	    $ddcmd &
	    ddpid=$!
	    wait $ddpid >/dev/null 2>&1
	    [ $? -ne 0 ] && errexit "? Exiting due to dd error"
	    trap SIGINT
	    write_burnmsg "> Image copy completed"
	fi
	olddiskid="$((blkid -o value $burnfilefile) | (read id ; echo $id))"
	loopdev=$(losetup --show -P -f $burnfilefile)
	sync ; sleep 1 ; sync
	write_burnmsg "> Set new disk ID '$newdiskid' on '$burnfilefile'"
	setdiskid $loopdev $newdiskid
	losetup -d $loopdev
	sync ; sleep 1 ; sync
	declare -x SDMPT=$(makemtpt)
	domount "$burnfilefile" "IMG" $SDMPT $cvtrootfs
	expandroot=1           # Force Expand Root into the newly written IMG so it will happen when burned to something with sdm
    fi
    #
    # Write out the accumulated messages into mounted image if image has been customized
    #
    if [ -d $SDMPT/etc/sdm ]
    then
	#write_burnmsg "> Flush accumulated burn log messages"
	for (( i=0 ; i < ${#burnmsg[@]} ; i++ ))
	do
	    echo "${burnmsg[$i]}" >> $SDMPT/etc/sdm/history
	done
	#
	# Burn messages are written, all further messages written with logtoboth, which will write them to the terminal
	# and to $SDMPT/etc/sdm/history in the mounted image or image file
	#
	xsettings="autologin b0script b1script bootscripts burnplugins domain expandroot exports fchroot hname fredact fnoexpandroot myuser noreboot"
	xsettings="$xsettings nowaittimesync plugindebug plugins reboot rebootwait regensshkeys"
	# Save settings made with the --burn command
	# Define variable b<varname> for each <varname>
	for e in $xsettings
	do
	    eval b$e=\${!e}
	done
	source $SDMPT/etc/sdm/sdm-readparams                   # Read settings from the SD card
	# Update settings with settings from cmd line as appropriate
	[ "$bb0script" != "$b0script" ] && b0script=$bb0script
	[ "$bb1script" != "$b1script" ] && b1script=$bb1script
	[ "$bdomain" != "" ] && domain=$bdomain
	[ $breboot -eq 1 ] && reboot=1
	[ $bnoreboot -eq 1 ] && reboot=0 && noreboot=1
	[ $bbootscripts -eq 1 ] && bootscripts="$bbootscripts"
	[ $brebootwait -ne $drebootwait ] && rebootwait=$brebootwait
	[ "$bhname" != "" ] && hname="$bhname"
	plugins="$bplugins"
	[ $bautologin -eq 1 ] && autologin=1
	[ "$bburnplugins" != "" ] && burnplugins="$bburnplugins"
	expandroot=$((bexpandroot|expandroot))
	fchroot=$((bfchroot|fchroot))
	fnoexpandroot=$((bfnoexpandroot|fnoexpandroot))
	regensshkeys=$((bregensshkeys|regensshkeys))
	nowaittimesync=$((bnowaittimesync|nowaittimesync))
	plugindebug=$((bplugindebug|plugindebug))
	[ "$hname" == "" ] && write_burnmsg "% hostname not specified with --host; will not be written"
	# --bupdate must specified on the burn command line
	initvirt logtoboth  # Must be done after fchroot is set
	[ "$bupdate" != "" ] && checkupdsdm update "$bupdate" || checkupdsdm check "$bupdate"
	if ispluginselected parted "$burnplugins"
	then
	    write_burnmsg "> Force --no-expand-root for parted burn-plugin"
	    fnoexpandroot=1
	fi
	if [ $fnoexpandroot -eq 1 ]
	then
	    write_burnmsg "> Force --regen-ssh-host-keys for --no-expand-root"
	    regensshkeys=1
	elif [[ $expandroot -eq 1 ]] && [[ "$burndev" != "" ]]
	then
	    docleanup keep
	    expandpartition $burndev 0 logtoboth $cvtrootfs
	fi
	writediskids
	if [ $((fnoexpandroot|expandroot)) -eq 1 -a $regensshkeys -eq 1 ]
	then
	    write_burnmsg "> Disable unneeded RasPiOS firstboot service and /etc/init.d/resize2fs_once"
	    if [ -f $SDMPT/boot/firmware/cmdline.txt ]
	    then
		sed -i 's| init=/usr/lib/raspberrypi-sys-mods/firstboot||' $SDMPT/boot/firmware/cmdline.txt
	    elif [ -f $SDMPT/boot/cmdline.txt ]
	    then
		sed -i 's| init=/usr/lib/raspberrypi-sys-mods/firstboot||' $SDMPT/boot/cmdline.txt
	    fi
	    [ -f $SDMPT/etc/init.d/resize2fs_once ] && mv $SDMPT/etc/init.d/resize2fs_once $SDMPT/etc/init.d/.sdm.resize2fs_once
	else
	    write_burnmsg "> RasPiOS firstboot service not disabled; at least one of --expand-root and --regen-ssh-host-keys not set"
	fi
	case "$cvtrootfs" in
	    btrfs)
		write_burnmsg "> Set file system type 'btrfs' in cmdline.txt and /etc/fstab"
		sed -i 's/ext4/btrfs/' $SDMPT/etc/fstab
		sed -i 's/ext4/btrfs/' $SDMPT/boot/firmware/cmdline.txt
		;;
	    zfs)
		write_burnmsg "> Configure zfs file system in cmdline.txt"
		sed -i 's%root=.*rootwait%root=ZFS=rpool/ROOT/debian rootwait%' $SDMPT/boot/firmware/cmdline.txt
		;;
	esac

	if [ $regensshkeys -eq 1 ]
	then
	    write_burnmsg "> Disable regenerate_ssh_host_keys service; sdm FirstBoot will run it instead"
	    sdm_runspawncmd Burn1 run-command-quietly "systemctl disable regenerate_ssh_host_keys"
	    touch $SDMPT/etc/ssh/sshd_not_to_be_run
	fi
	#
	# Set hostname into the image
	#
	[ "$hname" != "" ] && updatehostname $hname

	#if [[ "$loadllocal" =~ "|wifi|" ]]
	#then
	    #[ "$wificountry" == "" ] && write_burnmsg "% No --wifi-country specified with --loadlocal wifi; Using 'US' for a short time" && wificountry="US"
	#fi
	if [ "$bkeymap" != "" ]
	then
	    write_burnmsg "> Load Keymap '$keymap' to set during FirstBoot"
	    keymap=$bkeymap
	fi
	if [ "$b1script" != "" ]
	then
	    if [ -f $b1script ]
	    then
		if [ ! -f $SDMPT/etc/sdm/assets/$(basename $b1script) ]
		then
		    write_burnmsg "> Copy '$b1script' to /etc/sdm/assets"
		    cp -a $b1script $SDMPT/etc/sdm/assets
		    setfileownmode $SDMPT/etc/sdm/assets/$(basename $b1script) 755
		fi
	    else
		if [ ! -f $SDMPT/etc/sdm/assets/$(basename $b1script) ]
		then
		   write_burnmsg "? --b1script '$b1script' not found"
		   b1script=""
		fi
	    fi
	fi
	hostname="$hname"         # So it gets written to updated params on SD card
	[ $bootscripts -eq 1 ] && state="enabled" || state="disabled"
	write_burnmsg "> First System Boot Custom Boot Scripts $state"
	[ $rebootwait -ne $drebootwait ] && wmsg=" with a $rebootwait second wait" || wmsg=""
	[ $reboot -eq 1 ] && state="enabled${wmsg}" || state="disabled"
	write_burnmsg "> First System Boot automatic restart $state"
	writeconfig               # Write updated params to the SD card so they are available in the following and at boot
	if [ "$b0script" != "" ]
	then
	    write_burnmsg "> Execute function 'do_b0script' in --b0script '$b0script'"
	    source $b0script
	    ftype=$(type -t do_b0script)
	    [ "$ftype" == "function" ] && do_b0script || write_burnmsg "% Function 'do_b0script' not found in '$b0script'; Skipping..."
	fi
	if [ "$plugins" != "" ]
	then
	    plugin_logorder "$plugins"
	    runplugins_exit "$plugins" 0
	fi

	[ -d $SDMPT/etc/lightdm ] && xldm=1 || xldm=0

	if [ "$b1script" != "" ]
	then
	    write_burnmsg "> Run --b1script '$b1script'"
	    sdm_runspawncmd Burn1 b1script "$b1script"
	fi

	if [ "$plugins" != "" ]
	then
	    sdm_runspawncmd Burn1 run-plugin-list 1 "$plugins" || exit
	    sdm_runspawncmd Burn1 run-plugin-list post-install "$plugins" || exit
	    # Set lightdm burn enable delay if it was installed as part of burning
	    [ -d $SDMPT/etc/lightdm -a $xldm -eq 0 ] && sdm_runspawncmd Burn1 burn-enable-lightdm-delay
	    source $SDMPT/etc/sdm/sdm-readparams       #Reread in case it got updated running plugins (e.g. myuser from user plugin)
	    plugins="$bplugins"
	fi

	resetpluginlist
	#
	# Redact passwords if requested
	#
	if [[ $bfredact -eq 1 ]]
	then
	    write_burnmsg "> Redact passwords from /etc/sdm/cparams and /etc/sdm/history"
	    runoneplugin user redact "redact"
	fi

	declare -x SDMNSPAWN="Burn0"
    else
	echo $"% IMG '$dimg' is not sdm-enhanced
  Logs and configuration updates will not be written"
	[ "$b0script" != "" ] && echo "  and b0script '$b0script' will not be processed"
	[ "$b1script" != "" ] && echo "  and b1script '$b1script' will not be processed"
	[ "$plugins" != "" ] && echo "   and plugins '$plugins' will not be processed"
	[ "$hname" != "" ] && updatehostname $hname
	if [[ $expandroot -eq 1 ]] && [[ "$burndev" != "" ]]
	then
	    docleanup keep
	    expandpartition $burndev 0 echo $cvtrootfs
	fi
	writediskids
	if [ "$cvtrootfs" != "" ]
	then
	    errexit "? Sorry for the late notice but --convert-root requires an sdm-enhanced IMG"
	fi
    fi
    #
    # Install required packages for btrfs and lvm and do update-initramfs if needed
    #
    case "$cvtrootfs" in
	btrfs)
	    if [ ! -x $SDMPT/bin/btrfs ]
	    then
		write_burnmsg "> Install btrfs-progs onto burned disk"
		sdm_runspawncmd Burn1 run-command "apt install --yes --no-install-recommends btrfs-progs"
	    else
		write_burnmsg "> Enable btrfs in initramfs"
		sdm_runspawncmd Burn1 run-command "update-initramfs -u"
	    fi
	    ;;
	lvm)
	    if [ ! -x $SDMPT/sbin/lvcreate ]
	    then
		write_burnmsg "> Install lvm2 onto burned disk"
		sdm_runspawncmd Burn1 run-command "apt install --yes  --no-install-recommends lvm2"
	    else
		write_burnmsg "> Enable lvm in initramfs"
		sdm_runspawncmd Burn1 run-command "update-initramfs -u"
	    fi
	    ;;
	zfs)
	    if [ ! -x $SDMPT/bin/zfs ]
	    then
		write_burnmsg "> Install zfs onto burned disk"
		sdm_runspawncmd Burn1 run-command "debconf-set-selections <<< 'zfs-dkms zfs-dkms/note-incompatible-licenses boolean false'"
		#sdm_runspawncmd Burn1 run-command "apt install --yes  --no-install-recommends zfsutils-linux zfs-dkms zfs-zed zfs-initramfs"
	    else
		write_burnmsg "> Enable zfs in initramfs"
		sdm_runspawncmd Burn1 run-command "update-initramfs -u"
	    fi
    esac
    write_burnmsg "* Burn Completed"
    logtoboth "> Burn elapsed time: $(datediff "$burnstart" "$(getcdate)")"
    printnotes
    #
    # Call burn-plugins
    #
    docleanup

    # Redefine logtoboth so it just does an "echo" as the log is inaccessible from now on
    function logtoboth() {
	echo "$1"
    }

    if [ "$burnplugins" != "" ]
    then
	logtoboth "* Run Post-Burn plugins"
	[ "$burndev" != "" ] && imgtype="Device"
	[ "$burnfilefile" != "" ] && imgtype="IMG"
	runplugins_exit "$burnplugins" burn-complete "burndev=$burndev|burnfilefile=$burnfilefile|imgtype=$imgtype"
    fi
}
#
# Print partitions in IMG 
#
sdm_ppart() {
    echo "* Human-readable partition list"
    parted -s $dimg unit s print
    echo ""
    echo "* Machine-readable partition list"
    parted -ms $dimg unit s print
    #echo "0: filespec:bytesS:file:512:512:msdos::;"
    #echo "1: partnum:startS:endS:sizeS:fstype:::;"
    #echo "2 partnum:startS:endS:sizeS:fstype:::;"
}
#
# Shrink IMG file
#
sdm_shrink() {

    [ $dimgdev -eq 1 ] && errexit "? --shrink only operates on IMG files"
    loopdev=""
    fstype=""
    fsize=""
    bsize=$(stat --printf %s $dimg)
    rc=0
    echo "* Start shrink on IMG '$dimg'"
    while read line
    do
	if [[ "$line" =~ "msdos" ]]
	then
	    fsize=$(IFS=":" read fs bytes file n1 n2 fs <<< $line ; echo $bytes)
	    fsize=${fsize%B}
	elif [[ "$line" =~ "ext4" ]]
	then
	    IFS=":;" read partnum partstart partend partsize fstype etc etc2 etc3 <<< $line
	    partstart=${partstart%B}
	    partend=${partend%B}
	    partsize=${partsize%B}
	fi
    done < <(parted -ms $dimg unit B print)
    #
    #   https://alioth-lists.debian.net/pipermail/parted-devel/2006-December/000573.html
    # $l1: BYT;  ** error if not BYT 
    # $l2: filespec:bytesB:file:512:512:msdos::;
    # $l3: partnum:startB:endB:sizeB:fstype:::;
    # $l4: partnum:startB:endB:sizeB:fstype:::;
    #
    [ "$fstype" != "ext4" -o "$fsize" == "" ] && errexit "? IMG '$dimg' does not appear to be a RasPiOS IMG with two partitions"
    if [ $fsize -ne $bsize ]
    then
	errexit "? Discrepancy between stat and parted on file size; parted size: $fsize  stat size: $bsize"
    fi
    # Get partition type information for the 2nd partition (ext4)
    # ** Is this needed? it will fail miserably later which might be OK
#    pline=$(parted -s $dimg unit B print | grep ext4)     # Slightly different than parted done above (no -m)
#    if [[ "$pline" =~ "ext4" ]]
#    then
#	IFS=" " read p1 p2 p3 p4 parttype p6 <<< $pline
#	[ "$parttype" != "primary" ] && errexit "Partition type '$parttype' not supported"
#    else
#	errexit "? No ext4 partition found"
#    fi

    # Create loop device to the partition
    # Debug loggers and echos are handy for debugging kernel log messages
    [ $plugindebug -eq 1 ] && logger "Create loop device"
    loopdev=$(losetup -f --show -o "$partstart" "$dimg")
    # bx is a dummy due to leading ",". Other 2 can be in either order
    IFS="," read bx b1 b2 <<< $(tune2fs -l $loopdev | while read aline ;\
				do \
				    # If one we want, strip the text and all spaces, then echo it for consumption outside this subshell
				    [[ "$aline" =~ "Block count" ]] && (bc=${aline##Block count: } ; bc=${bc// } ; echo -n ",bc${bc}");\
					[[ "$aline" =~ "Block size" ]] && (bs=${aline##Block size: } ; bs=${bs// } ; echo -n ",bs${bs}");\
					done) ; rc=${PIPESTATUS[0]}
    [ $rc -ne 0 ] && losetup -d $loopdev 2>/dev/null && errexit "? tune2fs failed with status $rc"
    # Set bc=block count, bs=blocksize. Handle both orderings: <nn>bc,<nn>bs or <nn>bs,<nn>bc
    [ "${b1#bc}" != "$b1" ] && bc=${b1#bc}
    [ "${b2#bs}" != "$b2" ] && bs=${b2#bs}
    [ "${b1#bs}" != "$b1" ] && bc=${b1#bs}  #In case in other order
    [ "${b2#bc}" != "$b2" ] && bs=${b2#bc}  #...
    [ "$bc" == "" -o "$bs" == "" ] && losetup -d $loopdev 2>/dev/null && errexit "? tune2fs failed to read the file system"

    [ $plugindebug -eq 1 ] && logger "Check the file system"
    echo "> Check the file system"
    e2fsck -pf $loopdev
    rc=$?
    [ $rc -ge 4 ] && losetup -d $loopdev 2>/dev/null && errexit "? e2fsck reports that file system is corrupt $rc"

    [ $plugindebug -eq 1 ] && logger "Get new partition size"
    oresize=$(resize2fs -P $loopdev)
    rc=$?
    [ $rc -ne 0 ] && losetup -d $loopdev 2>/dev/null && errifrc $? "? resize2fs problem"
    # Strip everything before ": " leaving only the new partition size, then up it a bit
    newsize=${oresize##*: }
    newsize=$((newsize+(imgext/(bs/512))))

    if [ $plugindebug -eq 1 ]
    then
	echo "> Shrink data"
	echo "  Partition Start: $partstart"
	echo "  Partition End:   $partend"
	echo "  Partition Size:  $partsize"
	echo "  oresize: ${oresize##*: }"
	echo "  New size: $newsize"
	echo "  bc: $bc"
	echo "  bs: $bs"
	echo "  imgext: $imgext"
    fi

    [ $bc -le $newsize ] && { losetup -d $loopdev 2>/dev/null  ; echo "% Image cannot be shrunk any further" ; return 0 ; }

    # Shrink the file system
    [ $plugindebug -eq 1 ] && logger "Shrink the file system"
    partnewsize=$((newsize*bs))
    bcb=$((bc*bs))
    echo "> Shrink the root file system from $bc $(getgbstr $bcb) to $newsize ${bs}-byte blocks $(getgbstr $partnewsize)"
    resize2fs -M -p $loopdev $newsize
    rc=$?
    losetup -d $loopdev 2>/dev/null
    [ $rc -ne 0 ] && errifrc $rc "? resize2fs shrink exited with status '$rc'"
    rzp=0
    if [ $rzp -eq 0 ]
    then
	# Shrink the partition in the file
	partnewend=$((partstart+partnewsize-1))
	printf "%% Ignore Warning about shrinking a partition may cause data loss\n\n"
	printf 'Yes\n' | parted -a none $dimg unit B resizepart 2 $partnewend ---pretend-input-tty 2>&1
	errifrc $? "? parted resize partition failed"
    else
	# Shrink the partition by removing and recreating it
	# Not used, but keep for a while
	[ $plugindebug -eq 1 ] && logger "Remove old partition"
	echo "> Remove old partition"
	parted -s -a minimal $dimg rm $partnum
	errifrc $? "? parted rm partition failed with status"
	[ $plugindebug -eq 1 ] && logger "Make new partition"
	echo "> Make new partition"
	partnewend=$((partstart+partnewsize-1))
	parttype="primary"
	parted -s $dimg unit B mkpart $parttype $partstart $partnewend
	errifrc $? "? parted mkpart failed with status"
    fi
    #
    # Shrink the IMG
    #
    [ $plugindebug -eq 1 ] && logger "Shrink the IMG"
    newfilesize=$((partnewsize+partstart))
    echo "> Shrink the image by truncating to $newfilesize $(getgbstr $newfilesize)"
    truncate -s ${newfilesize%B} $dimg
    errifrc $? "? Truncate failed with status "
    echo "* Shrink complete"
    asize=$(stat --printf %s $dimg)
    echo "  IMG '$dimg' was $(getgbstr $bsize) now $(getgbstr $asize)"
    return 0
}
