#!/bin/bash # Supply pkgbase and vmlinuz metadata missing from Arch Linux ARM kernels. set -uo pipefail readonly SELF=linux-aarch64-pkgbase-shim readonly ROOT=${OMARCHY_KERNEL_SHIM_ROOT:-} readonly IMAGE=$ROOT/boot/Image readonly LIMINE_INSTALL_SCRIPT=$ROOT/usr/share/libalpm/scripts/limine-mkinitcpio-install declare -A DIRS=() modules_in_transaction=0 self_in_transaction=0 rebuild=0 failed=0 owner_of() { pacman -Qqo "${1#"$ROOT"}" 2>/dev/null } # NeedsTargets requires consuming every line from stdin. while IFS= read -r line; do case $line in usr/lib/modules/*) v=${line#usr/lib/modules/} v=${v%%/*} [[ -n $v ]] && DIRS["$ROOT/usr/lib/modules/$v"]=1 modules_in_transaction=1 ;; "$SELF") self_in_transaction=1 ;; esac done # Cover kernels already present when the shim itself is installed. if ((self_in_transaction)); then for d in "$ROOT"/usr/lib/modules/*/; do [[ -d $d ]] && DIRS["${d%/}"]=1 done fi for dir in "${!DIRS[@]}"; do [[ -d $dir ]] || continue # Not a packaged kernel (a leftover or DKMS-only directory). owners=$(owner_of "$dir") || continue owner=$(owner_of "$IMAGE") || continue # The kernel and its headers can both own the directory. Use the image's # single owner, provided it is also one of the directory's owners. [[ -n $owner && $owner != *$'\n'* ]] || continue if ! printf '%s\n' "$owners" | grep -Fx -- "$owner" >/dev/null; then echo "$SELF: $owner owns $IMAGE but not $dir, leaving it alone" >&2 continue fi # Preserve package-owned files; refresh metadata previously written by the shim. owner_of "$dir/pkgbase" >/dev/null && continue owner_of "$dir/vmlinuz" >/dev/null && continue if [[ -e $dir/pkgbase && $(<"$dir/pkgbase") != "$owner" ]]; then echo "$SELF: $dir/pkgbase does not name $owner, leaving $dir alone" >&2 continue fi if [[ -f $dir/pkgbase ]] && cmp -s "$IMAGE" "$dir/vmlinuz"; then continue fi image_tmp=$(mktemp "$dir/.vmlinuz.XXXXXX") || { failed=1; continue; } if ! cp --preserve=mode "$IMAGE" "$image_tmp" || ! mv -f "$image_tmp" "$dir/vmlinuz"; then echo "$SELF: failed to copy $IMAGE to $dir/vmlinuz" >&2 rm -f "$image_tmp" failed=1 continue fi if ! printf '%s\n' "$owner" >"$dir/pkgbase"; then failed=1 continue fi echo "$SELF: wrote pkgbase ($owner) and vmlinuz in $dir" # Rebuild when the transaction will not trigger mkinitcpio itself. if ! ((modules_in_transaction)) || ! pacman -Qlq "$owner" 2>/dev/null | grep '^/usr/lib/initcpio/' >/dev/null; then rebuild=1 fi done # Remove directories containing only stale shim metadata. for dir in "$ROOT"/usr/lib/modules/*/; do dir=${dir%/} [[ -f $dir/pkgbase && -f $dir/vmlinuz ]] || continue owner_of "$dir" >/dev/null && continue owner_of "$dir/pkgbase" >/dev/null && continue owner_of "$dir/vmlinuz" >/dev/null && continue [[ -z $(find "$dir" -mindepth 1 ! -name pkgbase ! -name vmlinuz -print -quit) ]] || continue rm -f "$dir/pkgbase" "$dir/vmlinuz" && rmdir "$dir" && echo "$SELF: removed leftover $dir" done # Respect the installer's deferred boot-image build. deferred_hook=$ROOT/etc/pacman.d/hooks/90-mkinitcpio-install.hook if ((rebuild)) && [[ -L $deferred_hook && $(readlink "$deferred_hook") == /dev/null ]]; then echo "$SELF: $deferred_hook is masked, leaving the rebuild to limine-update" rebuild=0 fi if ((rebuild)); then if [[ -x $LIMINE_INSTALL_SCRIPT ]]; then echo rebuild | "$LIMINE_INSTALL_SCRIPT" || failed=1 else echo "$SELF: $LIMINE_INSTALL_SCRIPT not found; run mkinitcpio for the new kernel yourself" >&2 fi fi exit "$failed"