|
| 1 | +#!/usr/bin/env bash |
| 2 | +# Offline-verify a freshly installed Margine target disk for the Flatpak |
| 3 | +# bake (forum 12247 install gate). Exposes the qcow2 via qemu-nbd (reusing |
| 4 | +# the hardened nbd dance from inject-gui-probe.sh), mounts the btrfs root |
| 5 | +# read-only, and asserts the baked /var/lib/flatpak that broke on the SER8 |
| 6 | +# is present, configured, populated, and correctly SELinux-labeled. |
| 7 | +# usage (root): verify-install-disk.sh <target.qcow2> |
| 8 | +# |
| 9 | +# NB: deliberately NOT `set -e` — a verify script must run ALL checks and |
| 10 | +# report, not die on the first non-zero. Critical setup steps check |
| 11 | +# explicitly; assertions accumulate into $fail. |
| 12 | +set -uo pipefail |
| 13 | +shopt -s nullglob |
| 14 | + |
| 15 | +QCOW="${1:?usage: verify-install-disk.sh <target.qcow2>}" |
| 16 | +NBD=/dev/nbd0 |
| 17 | +MNT=/mnt/verifyroot |
| 18 | +set -x # trace: pinpoint any setup failure in CI logs |
| 19 | + |
| 20 | +cleanup() { |
| 21 | + set +x |
| 22 | + for _ in 1 2 3; do mountpoint -q "$MNT" || break; umount -R "$MNT" 2>/dev/null && break; sleep 1; done |
| 23 | + sync |
| 24 | + qemu-nbd --disconnect "$NBD" >/dev/null 2>&1 || true |
| 25 | +} |
| 26 | +trap cleanup EXIT |
| 27 | + |
| 28 | +qemu-nbd --disconnect "$NBD" >/dev/null 2>&1 || true |
| 29 | +modprobe -r nbd 2>/dev/null || true |
| 30 | +modprobe nbd max_part=16 2>/dev/null || modprobe nbd 2>/dev/null || true |
| 31 | +[[ -e /dev/nbd0 ]] || { set +x; echo "::error::nbd module unavailable (/dev/nbd0 missing)"; exit 1; } |
| 32 | + |
| 33 | +if ! qemu-nbd --connect="$NBD" "$QCOW"; then |
| 34 | + set +x; echo "::error::qemu-nbd --connect failed for $QCOW"; exit 1 |
| 35 | +fi |
| 36 | +partprobe "$NBD" 2>/dev/null || partx -u "$NBD" 2>/dev/null || true |
| 37 | +parts=() |
| 38 | +for _ in $(seq 1 20); do |
| 39 | + parts=("$NBD"p*) |
| 40 | + if [[ ${#parts[@]} -gt 0 ]]; then break; fi |
| 41 | + sleep 1 |
| 42 | + partprobe "$NBD" 2>/dev/null || true |
| 43 | +done |
| 44 | + |
| 45 | +ROOT="" |
| 46 | +for p in "$NBD"p*; do |
| 47 | + if [[ "$(blkid -o value -s TYPE "$p" 2>/dev/null)" == "btrfs" ]]; then ROOT="$p"; break; fi |
| 48 | +done |
| 49 | +if [[ -z "$ROOT" ]]; then |
| 50 | + set +x; echo "::error::no btrfs root partition on $QCOW"; lsblk "$NBD" 2>/dev/null || true; blkid "$NBD"p* 2>/dev/null || true; exit 1 |
| 51 | +fi |
| 52 | +mkdir -p "$MNT" |
| 53 | +# Mount the btrfs TOP (subvolid 5) so EVERY subvol is traversable in one mount. |
| 54 | +# CRITICAL (review wzskaqvwo, confirmed on this bootc host): Margine's |
| 55 | +# margine.conf default_partitioning gives /var its OWN btrfs subvol, and |
| 56 | +# install-flatpaks.ks bakes into THAT dedicated /var (/mnt/sysimage/var/lib) — |
| 57 | +# NOT the per-deployment stateroot var (/ostree/deploy/$sr/var), which is just |
| 58 | +# an empty .ostree-selabeled stub. At the btrfs top the bake therefore lives at |
| 59 | +# $MNT/var/lib/flatpak. Reading the stateroot stub would FALSE-FAIL every check. |
| 60 | +mount -o ro,subvolid=5 "$ROOT" "$MNT" 2>/dev/null \ |
| 61 | + || mount -o ro "$ROOT" "$MNT" 2>/dev/null \ |
| 62 | + || { set +x; echo "::error::could not mount btrfs root $ROOT"; exit 1; } |
| 63 | + |
| 64 | +# VARLIB = the lib/ dir that actually contains the baked flatpak/ (the dedicated |
| 65 | +# /var subvol), located robustly across subvol layouts. |
| 66 | +VARLIB="" |
| 67 | +for cand in "$MNT/var/lib" "$MNT/root/var/lib"; do |
| 68 | + if [[ -d "$cand/flatpak" ]]; then VARLIB="$cand"; break; fi |
| 69 | +done |
| 70 | +if [[ -z "$VARLIB" ]]; then |
| 71 | + fp="$(find "$MNT" -maxdepth 6 -type d -path '*/var/lib/flatpak' 2>/dev/null | head -1)" |
| 72 | + [[ -n "$fp" ]] && VARLIB="$(dirname "$fp")" |
| 73 | +fi |
| 74 | +if [[ -z "$VARLIB" ]]; then |
| 75 | + # last resort: mount the dedicated var subvol explicitly |
| 76 | + umount -R "$MNT" 2>/dev/null |
| 77 | + if mount -o ro,subvol=var "$ROOT" "$MNT" 2>/dev/null && [[ -d "$MNT/lib/flatpak" ]]; then VARLIB="$MNT/lib"; fi |
| 78 | +fi |
| 79 | +set +x |
| 80 | +if [[ -z "$VARLIB" ]]; then |
| 81 | + echo "::error::no var/lib/flatpak on installed disk — bake missing or unexpected subvol layout" |
| 82 | + ls -laR "$MNT" 2>/dev/null | head -80 || true |
| 83 | + exit 1 |
| 84 | +fi |
| 85 | +echo "varlib=$VARLIB (dedicated /var btrfs subvol)" |
| 86 | +ls -la "$VARLIB/flatpak" 2>/dev/null | head -20 || true |
| 87 | + |
| 88 | +fail=0 |
| 89 | +ok() { printf ' OK %s\n' "$1"; } |
| 90 | +bad() { printf '::error::%s\n' "$1"; fail=1; } |
| 91 | + |
| 92 | +[[ -d "$VARLIB/flatpak/repo/refs/remotes/flathub" ]] \ |
| 93 | + && ok "flatpak repo has refs/remotes/flathub" \ |
| 94 | + || bad "flatpak repo MISSING refs/remotes/flathub (the exact SER8 break)" |
| 95 | + |
| 96 | +grep -q 'remote "flathub"' "$VARLIB/flatpak/repo/config" 2>/dev/null \ |
| 97 | + && ok "flathub present in repo/config" \ |
| 98 | + || bad "flathub not in flatpak/repo/config" |
| 99 | + |
| 100 | +N=$(find "$VARLIB/flatpak/app" -maxdepth 1 -mindepth 1 -type d 2>/dev/null | wc -l) |
| 101 | +echo " baked apps under flatpak/app: $N" |
| 102 | +[[ "$N" -ge 10 ]] && ok "app count $N >= 10" || bad "only $N baked apps (expected dozens)" |
| 103 | + |
| 104 | +ctx="$(getfattr -n security.selinux --only-values "$VARLIB/flatpak" 2>/dev/null | tr -d '\0' || true)" |
| 105 | +echo " /var/lib/flatpak SELinux context: ${ctx:-<unreadable>}" |
| 106 | +case "$ctx" in |
| 107 | + *var_lib_t*) ok "SELinux context is var_lib_t" ;; |
| 108 | + "") echo " ::warning::could not read security.selinux xattr (non-fatal)" ;; |
| 109 | + *) bad "SELinux context '$ctx' wrong — /var/lib/flatpak must be var_lib_t (var_t is NOT enough)" ;; |
| 110 | +esac |
| 111 | + |
| 112 | +echo |
| 113 | +if [[ "$fail" -eq 0 ]]; then echo "MARGINE-INSTALL-FLATPAK: PASS"; else echo "MARGINE-INSTALL-FLATPAK: FAIL"; fi |
| 114 | +exit "$fail" |
0 commit comments