testsAndMisc/linux_configuration/boot_recovery/lib/discovery.sh
Krzysztof kuhy Rudnicki 0fb3e3eee2 Cap extensionless scripts, and split the one that was hiding
check_file_length.sh required a dot in the filename, so every
extensionless executable was silently uncapped. is_source_file() now
reads the first two bytes and accepts a shebang, which catches real
scripts while still skipping data files like i3/config.

That immediately surfaced boot_recovery/boot-repair at 756 lines --
three times the largest capped shell file in the repo, uncapped purely
because its name has no dot in it. Split along its own section markers
into lib/{discovery,repair_fs,repair_kernel,modes}.sh, leaving the entry
script at 218.

The report block stays inline on purpose. It is top-level code with exit
calls rather than function definitions, and sourcing it changed control
flow: 30 of 42 tests failed. That is recorded in the lib headers so the
next reader does not retry it.

No vmbox pass for this one, deliberately. boot-repair is UEFI/ESP tooling
and the guest is BIOS/GRUB, so a sandbox green would be actively
misleading -- the vmbox skill says so itself. The evidence is the fixture
suite, which drives the real script against synthetic root trees: 42
passed before, 42 passed after.

verify_shell_split.sh reports 31 of 32 functions byte-identical. The one
difference is clean_shadow_files, where quoting $ESP_MOUNTPOINT fixed a
genuine SC2086 that only became visible once the function left the
monolith -- shellcheck could no longer see the readonly declaration and
stopped assuming the expansion was safe.

lib/tests/run_all.sh delegates to that same suite rather than standing up
a second, weaker harness: these libraries need the entry script's strict
mode and globals, so testing them in isolation would test a fiction.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PJrqKERAo5QUeEHWS5Asgp
2026-08-23 19:13:40 +02:00

154 lines
4.6 KiB
Bash
Executable File

#!/usr/bin/env bash
# Path, kernel and ESP discovery, plus mount state.
#
# Sourced by boot-repair, not executed. Split out of it verbatim to bring the
# entry script under the 250-line cap; it inherits the caller's strict mode,
# its $ROOT/$MODE globals and the output helpers defined there.
#
# shellcheck shell=bash
# shellcheck source=../boot-repair
# ----------------------------------------------------------------------------
# ----------------------------------------------------------------------------
# Path helpers — everything is expressed relative to \$ROOT so the logic can be
# exercised against a fixture tree without touching the real machine.
# ----------------------------------------------------------------------------
# Join $ROOT with an absolute path, avoiding a doubled leading slash.
rpath() {
local p="$1"
if [[ $ROOT == "/" ]]; then
printf '%s' "$p"
else
printf '%s%s' "${ROOT%/}" "$p"
fi
}
# True when operating on the live system (so mount/modprobe are meaningful).
is_live() { [[ $ROOT == "/" ]]; }
# ----------------------------------------------------------------------------
# Kernel discovery
# ----------------------------------------------------------------------------
# Read the version banner out of a kernel image. Works on a bare file with no
# tooling beyond grep, which matters in a degraded emergency shell.
# Usage: kernel_image_version <path> -> prints e.g. 7.1.5-arch1-2, or nothing
kernel_image_version() {
local img="$1"
[[ -r $img ]] || return 1
grep -aoE '[0-9]+\.[0-9]+\.[0-9]+-arch[0-9]+-[0-9]+' "$img" 2>/dev/null | head -1
}
# A module tree is "complete" only if it has both the kernel image and actual
# modules. The failure mode leaves behind a directory holding just the
# modules.* metadata files, which must not be mistaken for a usable kernel.
kernel_tree_complete() {
local dir="$1"
[[ -f $dir/vmlinuz ]] || return 1
[[ -d $dir/kernel ]] || return 1
return 0
}
# Newest complete module tree under $ROOT/usr/lib/modules, by version sort.
newest_complete_kernel() {
local moddir kver best=""
moddir="$(rpath /usr/lib/modules)"
[[ -d $moddir ]] || return 1
while IFS= read -r kver; do
[[ -n $kver ]] || continue
kernel_tree_complete "$moddir/$kver" || continue
best="$kver"
done < <(find "$moddir" -mindepth 1 -maxdepth 1 -type d -printf '%f\n' 2>/dev/null | sort -V)
[[ -n $best ]] || return 1
printf '%s' "$best"
}
# ----------------------------------------------------------------------------
# ESP discovery
# ----------------------------------------------------------------------------
# The fstab spec for /boot (e.g. "UUID=8A14-F8A7"), if present.
fstab_boot_spec() {
local fstab spec mnt
fstab="$(rpath /etc/fstab)"
[[ -r $fstab ]] || return 1
while read -r spec mnt _; do
[[ $spec == \#* ]] && continue
[[ -z ${spec:-} ]] && continue
if [[ $mnt == "$ESP_MOUNTPOINT" ]]; then
printf '%s' "$spec"
return 0
fi
done <"$fstab"
return 1
}
# The mount options field for /boot in fstab.
fstab_boot_options() {
local fstab spec mnt opts
fstab="$(rpath /etc/fstab)"
[[ -r $fstab ]] || return 1
while read -r spec mnt _ opts _; do
[[ $spec == \#* ]] && continue
[[ -z ${spec:-} ]] && continue
if [[ $mnt == "$ESP_MOUNTPOINT" ]]; then
printf '%s' "$opts"
return 0
fi
done <"$fstab"
return 1
}
# Resolve the fstab spec to a block device. Prefers /dev/disk/by-uuid so it
# works without blkid, which may be unavailable in a minimal shell.
resolve_esp_device() {
local spec uuid dev
if [[ -n $ESP_DEV_OVERRIDE ]]; then
printf '%s' "$ESP_DEV_OVERRIDE"
return 0
fi
spec="$(fstab_boot_spec)" || return 1
case "$spec" in
UUID=*)
uuid="${spec#UUID=}"
if [[ -e /dev/disk/by-uuid/$uuid ]]; then
dev="$(readlink -f "/dev/disk/by-uuid/$uuid")"
printf '%s' "$dev"
return 0
fi
# Fall back to blkid when the by-uuid symlink is absent.
if command -v blkid >/dev/null 2>&1; then
dev="$(blkid -U "$uuid" 2>/dev/null)" && [[ -n $dev ]] && {
printf '%s' "$dev"
return 0
}
fi
return 1
;;
/dev/*)
printf '%s' "$spec"
return 0
;;
*)
return 1
;;
esac
}
# ----------------------------------------------------------------------------
# Mount state
# ----------------------------------------------------------------------------
boot_is_mounted() {
is_live || return 1
findmnt -n "$ESP_MOUNTPOINT" >/dev/null 2>&1
}
# True when the system booted via UEFI, i.e. an ESP can exist at all. The
# kernel only creates /sys/firmware/efi when it was loaded by EFI firmware.
#
# Expressed via rpath so a fixture tree can declare its own firmware type;
# on the live machine this resolves to the real /sys path and is exact.
system_is_uefi() { [[ -d "$(rpath /sys/firmware/efi)" ]]; }