#!/usr/bin/env bash # Copyright (C) 2023-2024 Umorpha Systems # SPDX-License-Identifier: AGPL-3.0-or-later # Dependencies: # - /usr/bin/env (coreutils, busybox) # - cat (coreutils, busybox) # - dd (coreutils, busybox--only needed fo typ=uboot-sunxi) # - install (coreutils, busybox) # - mkdir (coreutils, busybox) # - mktemp (coreutils, busybox) # - rm (coreutils, busybox) # - rmdir (coreutils, busybox) # - losetup (optional) (util-linux; the busybox one doesn't work) # - umount (util-linux, busybox) # - blkid (util-linux) # - mount (util-linux) # - bash (bash) # - sgdisk (gptfdisk--only needed fo typ=uboot-sunxi) # - systemd-repart (systemd) # - lvcreate (lvm2) # - lvresize (lvm2) # - pvcreate (lvm2) # - pvs (lvm2) # - vgchange (lvm2) # - vgcreate (lvm2) # - vgremove (lvm2) # - vgs (lvm2) # - pv (pv) # - mkfs.ext4 (e2fsprogs) # - mkfs.vfat (dosfstools) # - arch-chroot (arch-install-scripts) # - umorpha-mount # # Dependencies of arch-chroot (as of v28): # - cat (coreutils, busybox) # - install (coreutils, busybox) # - ln (coreutils, busybox) # - touch (coreutils, busybox) # - chroot (coreutils; the busybox one doesn't work) # - readlink (coreutils; the busybox one doesn't work) # - mountpoint (util-linux, busybox) # - umount (util-linux, busybox) # - unshare (util-linux, busybox) # - mount (util-linux) # - grep (grep, busybox) # - bash (bash) # # Dependencies of umorpha-mount: # - sh (bash, busybox) # - getopt (util-linux, busybox) # - mkdir (coreutils, busybox) # - mount (util-linux) # # Of those, these are the ones that aren't already in an initcpio: # - arch-chroot (arch-install-scripts) # - bash (bash) # - chroot (coreutils) # - readlink (coreutils) # - pv (pv) # - systemd-repart (systemd) # - mkfs.ext4 (e2fsprogs) # - mkfs.vfat (dosfstools) # - lvcreate (lvm2) # - lvresize (lvm2) # - pvcreate (lvm2) # - pvs (lvm2) # - vgchange (lvm2) # - vgcreate (lvm2) # - vgremove (lvm2) # - vgs (lvm2) set -euE -o pipefail v() { printf >&2 ' $ %s\n' "${*@Q}" "$@" } in_array() { local needle haystack straw needle=$1 haystack=("${@:2}") for straw in "${haystack[@]}"; do if [[ "$needle" == "$straw" ]]; then return 0 fi done return 1 } main() { local typ device rootfs slot typ=$1 rootfs=$2 device=$3 slot='a' echo ":: Installing '${rootfs}' to '${device}' for '${typ}'" if [[ "$rootfs" != *rootfs* ]]; then echo >&2 "!! Filename '${rootfs}' does not look like a rootfs filename" return 2 fi case "$typ" in uefi|bios|uboot-sunxi) :;; *) echo >&2 "!! Install type '${typ}' is not valid" return 2 ;; esac ######################################################################## cleanup=() _do_cleanup() { if [[ $? == 0 ]]; then echo ":: Cleaning up..." else echo >&2 "!! ERROR! Cleaning up..." fi for (( i=${#cleanup[@]}-1; i >= 0; i-- )); do eval "${cleanup[$i]}" done } trap '_do_cleanup' EXIT local tmpdir tmpdir="$(mktemp -dt "${0##*/}.XXXXXXXXXX")" cleanup+="rmdir -- ${tmpdir@Q}" ######################################################################## local is_loop=false if [[ -f "$device" ]]; then echo " file '${device}' is not a block device, setting up a loopback device..." device=$(v losetup --find --show --partscan -- "$device") echo " ... '${device}'" cleanup+=("v losetup --detach ${device@Q}") is_loop=true fi local devicep="$device" if [[ "$devicep" == *[0-9] ]]; then devicep+='p' fi ######################################################################## echo ":: Setting up GPT" n=0 cat >"$tmpdir/00-esp.conf" <<-EOF [Partition] # EFI System Partition, specified by UEFI Type=esp Format=vfat Label=Umorpha EFI System Partition SizeMinBytes=512M SizeMaxBytes=512M EOF n_esp=$((++n)) sector_size='' case "$typ" in bios) cat >"$tmpdir/05-bios.conf" <<-EOF [Partition] # GRUB BIOS Boot partition, specified by GRUB Type=21686148-6449-6E6F-744E-656564454649 Label=Umorpha BIOS Boot Partition SizeMinBytes=1M SizeMaxBytes=1M EOF n_bios=$((++n)) ;; uboot-sunxi) sector_size=512 # Commented out because `BegOffset=` and `EndOffset=` # aren't real systemd-repart options. # #cat >"$tmpdir/05-uboot.conf" <<-EOF # [Partition] # # U-Boot Environment partition, specified by U-Boot # Type=3DE21764-95BD-54BD-A5C3-4ABE786F38A8 # Label=Umorpha U-Boot Partition # BegOffset=8192b # EndOffset=1M #EOF #n_uboot=$((++n)) ;; esac cat >"$tmpdir/10-lvm.conf" <<-EOF [Partition] # LVM2 partition, specified by ??? # https://en.wikipedia.org/w/index.php?title=User_talk:Claunia&diff=prev&oldid=1182832243 Type=E6D6D379-F507-44C2-A23C-238F2A3DF928 Label=Umorpha LVM Partition EOF n_lvm=$((++n)) v systemd-repart \ --empty=allow --dry-run=no \ --no-pager \ --definitions="$tmpdir" \ ${sector_size:+--sector-size="$sector_size"} \ "$device" rm -- "$tmpdir"/*.conf if [[ $typ == uboot-sunxi ]]; then # Shrink the number of entries in the partition table # to make room for U-Boot; the location that sunxi # chips need us to put U-Boot at is by default # occupied by the partition table. # # def+min table location is LBA=2, def+min entry size # is 128b, def number of entries is 128, which means # that it ranges from 1KiB to 17KiB. # # U-Boot needs to go at 8KiB, meaning that if we want # leave the partition table at the default location it # can be a maximum of 7KiB. # # 7KiB / (128b/entry) = 56entries v sgdisk --resize-table=56 "$device" # TODO: I'd like to set up a partition for U-Boot, to # make it clear that it's there (instead of putting it # in unpartitioned space). But the default # FirstUsableLBA is set to 1MiB, and so sgdisk will # silently bump the partition's start LBA to 1MiB. # And I don't see a way to have sgdisk change the # FirstUsableLBA. # #v sgdisk \ # --new=3:$((8*1024/512)):$((1024*1024/512)) \ # --typecode=3DE21764-95BD-54BD-A5C3-4ABE786F38A8 \ # --change-name=3:'Umorpha U-Boot Partition' \ # --sort # "$device" fi ######################################################################## echo ":: Setting up LVM" if ! in_array "${devicep}${n_lvm}" $(pvs --noheadings --options=pv_name); then v pvcreate "${devicep}${n_lvm}" fi if ! in_array vg_umorpha $(vgs --noheadings --options=vg_name); then v vgcreate vg_umorpha "${devicep}${n_lvm}" fi if $is_loop; then cleanup+=("v vgchange --activate=n vg_umorpha") fi # Use short `-c` instead of `--format` so this works with busybox. size="$(stat -c '%s' -- "$rootfs")" if [[ -e /dev/mapper/vg_umorpha-lv_root_$slot ]]; then # Ugg, lvresize exits with non-zero for no-ops, which we can't # even really do a pre-flight check for since it might round a # size up. v lvresize --size="${size}b" "/dev/mapper/vg_umorpha-lv_root_$slot" || true else v lvcreate --zero=y --wipesignatures=y --yes --name="lv_root_$slot" \ --size="${size}b" vg_umorpha fi v pv -- "$rootfs" >"/dev/mapper/vg_umorpha-lv_root_$slot" if ! [[ -e /dev/mapper/vg_umorpha-lv_root_overlay ]]; then v lvcreate --zero=y --wipesignatures=y --yes \ --name=lv_root_overlay --size=1G vg_umorpha fi if [[ -z "$(blkid --output=export /dev/mapper/vg_umorpha-lv_root_overlay | sed -n s/^TYPE=//p)" ]]; then v mkfs.ext4 /dev/mapper/vg_umorpha-lv_root_overlay fi ######################################################################## echo ":: Configuring boot" local selector_boot selector_root selector_overlay selector_boot="$( blkid --output=export "${devicep}${n_esp}" | grep ^UUID=)" selector_root="$( blkid --output=export "/dev/mapper/vg_umorpha-lv_root_$slot" | grep ^UUID=)" selector_overlay="$(blkid --output=export /dev/mapper/vg_umorpha-lv_root_overlay | grep ^UUID=)" v declare -p selector_boot selector_root selector_overlay v umorpha-mount \ --root="/dev/mapper/vg_umorpha-lv_root_$slot:auto:ro" \ --overlay='/dev/mapper/vg_umorpha-lv_root_overlay:auto:rw' \ --boot="${devicep}${n_esp}:auto:rw" \ --basedir="$tmpdir" cleanup+=("rmdir -- ${tmpdir@Q}/root") cleanup+=("v umount ${tmpdir@Q}/root") cleanup+=("rmdir -- ${tmpdir@Q}/overlay") cleanup+=("v umount ${tmpdir@Q}/overlay") cleanup+=("rmdir -- ${tmpdir@Q}/mnt") cleanup+=("v umount ${tmpdir@Q}/mnt") cleanup+=("v umount ${tmpdir@Q}/mnt/boot") case "$typ" in uefi|uboot-sunxi) if [[ $typ == uboot-sunxi ]]; then v dd if="$(echo "$tmpdir/mnt"/usr/lib/u-boot/*/u-boot-sunxi-with-spl.bin)" of="$device" conv=notrunc bs=512 seek=16 fi mkdir -p -- "$tmpdir/mnt/etc/kernel" echo "root=$selector_root overlay=$selector_overlay boot=$selector_boot rw" >"$tmpdir/mnt/etc/kernel/cmdline" mkdir -p -- "$tmpdir/mnt/boot/EFI/Linux" bootloader_src=$(echo "$tmpdir"/mnt/usr/lib/systemd/boot/efi/systemd-boot*.efi) bootloader_dst=${bootloader_src##*-} bootloader_dst="$tmpdir/mnt/boot/EFI/BOOT/${bootloader_dst^^}" v install -D "$bootloader_src" "$bootloader_dst" ;; bios) v arch-chroot -- "$tmpdir/mnt" grub-install --target=i386-pc "$device" install -Dm600 /dev/stdin "$tmpdir/mnt/boot/grub/grub.cfg" <<-EOF insmod part_gpt insmod part_msdos if [ -s \$prefix/grubenv ]; then load_env fi if [ -s \$prefix/grub_a.cfg ]; then configfile \$prefix/grub_a.cfg fi if [ -s \$prefix/grub_b.cfg ]; then configfile \$prefix/grub_b.cfg fi EOF { for krnl in "$tmpdir/mnt"/usr/lib/modules/*/vmlinuz; do pkgbase=$(cat -- ${krnl%vmlinuz}pkgbase) cat <<-EOF menuentry 'Parabola GNU/Linux-libre, slot ${slot}, ${pkgbase} kernel' set search --no-floppy --fs-uuid --set=root ${selector_boot#UUID=} echo 'Loading ${pkgbase} kernel...' linux /vmlinuz-${pkgbase} root=$selector_root overlay=$selector_overlay boot=$selector_boot rw echo 'Loading initial ramdisk...' initrd /initramfs-${pkgbase}.img echo 'Booting...' boot EOF done } | install -Dm600 /dev/stdin "$tmpdir/mnt/boot/grub/grub_${slot}.cfg" ;; esac if ! v arch-chroot -- "$tmpdir/mnt" sh -c "printf '%s\n' usr/lib/modules/*/vmlinuz | /usr/share/libalpm/scripts/mkinitcpio install"; then v arch-chroot -- "$tmpdir/mnt" sh -c "MKINITCPIO_PROCESS_PRESET=linux-libre-fallback bash -x /bin/mkinitcpio -k /usr/lib/modules/6.7.4-gnu-1/vmlinuz -U /boot/EFI/Linux/parabola-linux-libre-fallback.efi -S autodetect --microcode '/boot/*-ucode.img'" fi v arch-chroot -- "$tmpdir/mnt" ldconfig v arch-chroot -- "$tmpdir/mnt" systemd-machine-id-setup v arch-chroot -- "$tmpdir/mnt" update-ca-trust } main "$@"