#!/usr/bin/env bash # Copyright (C) 2023 Umorpha Systems # SPDX-License-Identifier: AGPL-3.0-or-later # Dependencies: # - /usr/bin/env (coreutils, busybox) # - cat (coreutils, busybox) # - 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) # - systemd-repart (systemd) # - lvcreate (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) # - 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) :;; *) 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 v vgremove --yes vg_umorpha || true ######################################################################## echo ":: Setting up GPT" n=0 cat >"$tmpdir/00-esp.conf" <<-EOF [Partition] Type=esp Format=vfat Label=Umorpha EFI System Partition SizeMinBytes=512M SizeMaxBytes=512M EOF n_esp=$((++n)) if [[ $typ == bios ]]; then cat >"$tmpdir/05-bios.conf" <<-EOF [Partition] Type=21686148-6449-6E6F-744E-656564454649 Label=Umorpha BIOS Boot Partition SizeMinBytes=1M SizeMaxBytes=1M EOF n_bios=$((++n)) fi cat >"$tmpdir/10-lvm.conf" <<-EOF [Partition] # 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" \ "$device" rm -- "$tmpdir"/*.conf ######################################################################## 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+=("v umount ${tmpdir@Q}/mnt/boot") cleanup+=("v umount ${tmpdir@Q}/mnt") cleanup+=("rmdir -- ${tmpdir@Q}/mnt") cleanup+=("v umount ${tmpdir@Q}/overlay") cleanup+=("rmdir -- ${tmpdir@Q}/overlay") cleanup+=("v umount ${tmpdir@Q}/root") cleanup+=("rmdir -- ${tmpdir@Q}/root") case "$typ" in uefi) 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" v install -D "$tmpdir/mnt/usr/lib/systemd/boot/efi/systemd-bootx64.efi" "$tmpdir/mnt/boot/EFI/BOOT/BOOTX64.EFI" ;; 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 v arch-chroot -- "$tmpdir/mnt" sh -c "printf '%s\n' usr/lib/modules/*/vmlinuz | /usr/share/libalpm/scripts/mkinitcpio install" v arch-chroot -- "$tmpdir/mnt" ldconfig v arch-chroot -- "$tmpdir/mnt" systemd-machine-id-setup v arch-chroot -- "$tmpdir/mnt" update-ca-trust } main "$@"