#!/bin/bash # Does the installer mention a spare drive — and only a genuinely spare one? # # scripts/dev/lp-installer-disks-test # # initPickRoots can only offer MOUNTED filesystems, so on a box whose second # drive is new (unmounted, often unformatted) it returned in silence and the # prompt never appeared at all. That taught exactly the users who have a spare # disk that the feature does not exist. # # The notice that fills that gap has to be careful in two specific ways, and # both are cases below: # # 1. "Has no mount point" is not "is free". The disk holding the root # filesystem has no mount point of its own — its partition does — so the # obvious check offers the user the disk they booted from. # 2. lsblk's raw output renders an empty mount point as a run of spaces, which # `read` collapses, shifting every later column left. An LVM member then # parses as a mount point and gets offered as free space. Hence -P. # # Runs the REAL _initMentionUnmounted extracted from init.sh against a stubbed # lsblk, so editing the heuristic is what makes this fail. REPO="$(cd "$(dirname "$0")/../.." && pwd)" fail=0 chk(){ if [[ "$2" == "$3" ]]; then echo " ok $1"; else echo " FAIL $1: got '$2' want '$3'"; fail=1; fi; } FN=$(awk '/^_initMentionUnmounted\(\)/,/^\}/' "$REPO/init.sh") [[ -n "$FN" ]] || { echo " FAIL could not extract _initMentionUnmounted from init.sh"; exit 1; } # Fixtures are written as lsblk -P rows. The stub HONOURS the output flag it is # called with: asked for -r it re-renders them raw, collapsing the empty fields # the way lsblk really does. Without that the stub answers -P to everything and # the test cannot tell the two parsers apart — which is the bug being guarded # against, so a stub that ignores the flag guards nothing. STUB_SRC=$(mktemp) { echo 'lsblk() {' echo ' case " $* " in' echo ' *" -P"*) cat "$ROWS_FILE"; return 0 ;;' echo ' esac' echo ' sed -E '"'"'s/[A-Z]+="([^"]*)"/\1/g; s/ +/ /g; s/^ +| +$//g'"'"' "$ROWS_FILE"' echo '}' } > "$STUB_SRC" trap 'rm -f "$STUB_SRC"' EXIT runrows() { local tmp rf out tmp=$(mktemp); rf=$(mktemp) cat > "$rf" { echo 'isNotice(){ echo "NOTICE: $*"; }' cat "$STUB_SRC" echo "$FN" echo '_initMentionUnmounted' } > "$tmp" out=$(ROWS_FILE="$rf" bash "$tmp" 2>/dev/null) rm -f "$tmp" "$rf" printf '%s\n' "$out" } ROOT_ROWS='NAME="nvme0n1" TYPE="disk" SIZE="931.5G" MOUNTPOINT="" FSTYPE="" PKNAME="" NAME="nvme0n1p1" TYPE="part" SIZE="1G" MOUNTPOINT="/boot/efi" FSTYPE="vfat" PKNAME="nvme0n1" NAME="nvme0n1p2" TYPE="part" SIZE="930G" MOUNTPOINT="" FSTYPE="crypto_LUKS" PKNAME="nvme0n1" NAME="dm-0" TYPE="crypt" SIZE="930G" MOUNTPOINT="/" FSTYPE="ext4" PKNAME="nvme0n1p2"' echo "--- a normal single-disk box (root on LUKS over nvme0n1p2) ---" out=$(runrows <<< "$ROOT_ROWS") chk "offers nothing" "$(grep -c NOTICE <<< "$out")" "0" echo "--- a spare unformatted disk ---" out=$(runrows <<< "$ROOT_ROWS NAME=\"sdb\" TYPE=\"disk\" SIZE=\"3.6T\" MOUNTPOINT=\"\" FSTYPE=\"\" PKNAME=\"\"") chk "mentions it" "$(grep -c NOTICE <<< "$out")" "1" chk "names the device" "$(grep -c '/dev/sdb' <<< "$out")" "1" chk "says it is blank" "$(grep -c 'no filesystem yet' <<< "$out")" "1" echo "--- a spare formatted partition ---" out=$(runrows <<< "$ROOT_ROWS NAME=\"sdb\" TYPE=\"disk\" SIZE=\"3.6T\" MOUNTPOINT=\"\" FSTYPE=\"\" PKNAME=\"\" NAME=\"sdb1\" TYPE=\"part\" SIZE=\"3.6T\" MOUNTPOINT=\"\" FSTYPE=\"ext4\" PKNAME=\"sdb\"") chk "names the partition" "$(grep -c '/dev/sdb1' <<< "$out")" "1" chk "not the whole disk as well" "$(grep -cE '/dev/sdb ' <<< "$out")" "0" echo "--- an LVM member is not free space (the column-shift case) ---" out=$(runrows <<< "$ROOT_ROWS NAME=\"sdb\" TYPE=\"disk\" SIZE=\"2T\" MOUNTPOINT=\"\" FSTYPE=\"\" PKNAME=\"\" NAME=\"sdb1\" TYPE=\"part\" SIZE=\"2T\" MOUNTPOINT=\"\" FSTYPE=\"LVM2_member\" PKNAME=\"sdb\"") chk "offers nothing" "$(grep -c NOTICE <<< "$out")" "0" echo "--- a mounted spare is already usable, so the picker handles it ---" out=$(runrows <<< "$ROOT_ROWS NAME=\"sdb\" TYPE=\"disk\" SIZE=\"2T\" MOUNTPOINT=\"\" FSTYPE=\"\" PKNAME=\"\" NAME=\"sdb1\" TYPE=\"part\" SIZE=\"2T\" MOUNTPOINT=\"/mnt/data\" FSTYPE=\"ext4\" PKNAME=\"sdb\"") chk "offers nothing" "$(grep -c NOTICE <<< "$out")" "0" echo "--- a partition whose CHILD is mounted (nothing on the part itself) ---" # sdb1 has no mount point and no member fstype of its own; only dm-1 below it is # mounted. Without walking the parent chain, sdb1 looks like free space. out=$(runrows <<< "$ROOT_ROWS NAME=\"sdb\" TYPE=\"disk\" SIZE=\"2T\" MOUNTPOINT=\"\" FSTYPE=\"\" PKNAME=\"\" NAME=\"sdb1\" TYPE=\"part\" SIZE=\"2T\" MOUNTPOINT=\"\" FSTYPE=\"\" PKNAME=\"sdb\" NAME=\"dm-1\" TYPE=\"crypt\" SIZE=\"2T\" MOUNTPOINT=\"/data\" FSTYPE=\"ext4\" PKNAME=\"sdb1\"") chk "offers nothing" "$(grep -c NOTICE <<< "$out")" "0" echo "--- small stubs are not app-data drives ---" out=$(runrows <<< "$ROOT_ROWS NAME=\"sdb\" TYPE=\"disk\" SIZE=\"512M\" MOUNTPOINT=\"\" FSTYPE=\"\" PKNAME=\"\"") chk "offers nothing" "$(grep -c NOTICE <<< "$out")" "0" echo "" if (( fail )); then echo "FAILED"; exit 1; fi echo "All installer disk-notice checks passed."