#!/usr/bin/env bash set -eu evidence=${1:?evidence directory is required} root=/work/erofs-freebsd-pre dut=$root/repo-pre-15 base=/work/build/vm-freebsd-build.qcow2.bp askpass=/work/build/.repo22-ssh-askpass module=${B21_MODULE:-$root/planning/pre15/evidence/20260816T-B21-abi15-build/B21-erofs-zstdio0.ko} source_tree=${B21_FREEBSD_SRC:-/work/dev-freebsd-releng} owned_pid= port= loaded=0 mounted=0 md_units= test ! -e "$evidence" mkdir -p "$evidence" : >"$evidence/cleanup.log" ssh_args=(env DISPLAY=:0 SSH_ASKPASS="$askpass" SSH_ASKPASS_REQUIRE=force ssh -n -o BatchMode=no -o PubkeyAuthentication=no -o PreferredAuthentications=keyboard-interactive,password -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o LogLevel=ERROR -o ConnectTimeout=5) scp_args=(env DISPLAY=:0 SSH_ASKPASS="$askpass" SSH_ASKPASS_REQUIRE=force scp -O -q -o BatchMode=no -o PubkeyAuthentication=no -o PreferredAuthentications=keyboard-interactive,password -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o LogLevel=ERROR -o ConnectTimeout=5) guest() { timeout -k 5 60 "${ssh_args[@]}" -p "$port" root@127.0.0.1 "$@" } capture() { local name=$1 shift set +e guest "$@" >"$evidence/$name.stdout" 2>"$evidence/$name.stderr" local rc=$? set -e printf '%s\n' "$rc" >"$evidence/$name.rc" } scp_to() { local source_path=$1 local destination_path=$2 timeout -k 5 60 "${scp_args[@]}" -P "$port" "$source_path" \ "root@127.0.0.1:$destination_path" } cleanup() { set +e local cleanup_rc=0 if test "$mounted" = 1; then guest umount /mnt/pre15-b21 >>"$evidence/cleanup.log" 2>&1 || cleanup_rc=1; fi for md in $md_units; do guest mdconfig -d -u "$md" >>"$evidence/cleanup.log" 2>&1 || cleanup_rc=1; done if test "$loaded" = 1; then guest kldunload /root/B21-erofs-zstdio0.ko >>"$evidence/cleanup.log" 2>&1 || cleanup_rc=1; fi if test -n "$owned_pid" && kill -0 "$owned_pid" 2>/dev/null; then test "$owned_pid" != 26318 || exit 99 kill "$owned_pid" 2>/dev/null wait "$owned_pid" 2>/dev/null fi test ! -e "$evidence/owned-overlay.qcow2" || unlink "$evidence/owned-overlay.qcow2" python3 - "$port" <<'PY' import socket import sys if not sys.argv[1]: raise SystemExit(0) with socket.socket() as sock: sock.settimeout(0.2) raise SystemExit(0 if sock.connect_ex(("127.0.0.1", int(sys.argv[1]))) else 1) PY test $? = 0 || cleanup_rc=1 stat -c 'path=%n size=%s inode=%i mode=%f mtime=%Y ctime=%Z' "$base" \ >"$evidence/base-metadata-after.txt" cmp -s "$evidence/base-metadata-before.txt" "$evidence/base-metadata-after.txt" || cleanup_rc=1 if test "$cleanup_rc" = 0 && test ! -e "$evidence/owned-overlay.qcow2"; then printf '%s\n' PASS >"$evidence/cleanup.status" else printf '%s\n' FAIL >"$evidence/cleanup.status" fi } trap cleanup EXIT HUP INT TERM test -x "$askpass" test -f "$module" test -f "$base" stat -c 'path=%n size=%s inode=%i mode=%f mtime=%Y ctime=%Z' "$base" \ >"$evidence/base-metadata-before.txt" git -C "$dut" rev-parse HEAD >"$evidence/dut-head.txt" { git -C "$source_tree" log -1 --format='%H %cs %s' sed -n '76,77p' "$source_tree/sys/sys/param.h" sed -n '53,54p' "$source_tree/sys/conf/newvers.sh" } >"$evidence/source-identity.txt" printf '%s\n' 'SSH options precede root@127.0.0.1 and the remote command' \ >"$evidence/transport-contract.txt" python3 -B "$dut/tests/pre15/fixtures/B21-super-generate.py" \ --spec "$dut/tests/pre15/fixtures/B21-super-spec.json" \ --output "$evidence/first" --work "$evidence/first-work" \ >"$evidence/generate-first.stdout" 2>"$evidence/generate-first.stderr" python3 -B "$dut/tests/pre15/fixtures/B21-super-generate.py" \ --spec "$dut/tests/pre15/fixtures/B21-super-spec.json" \ --output "$evidence/second" --work "$evidence/second-work" \ >"$evidence/generate-second.stdout" 2>"$evidence/generate-second.stderr" cmp "$evidence/first/fixture-index.json" "$evidence/second/fixture-index.json" python3 -B "$dut/tests/pre15/fixtures/B21-super-oracle.py" \ --fixtures "$evidence/first" --report "$evidence/oracle.json" \ >"$evidence/oracle.stdout" 2>"$evidence/oracle.stderr" python3 - "$evidence/first/fixture-index.json" "$evidence/oracle.json" \ "$evidence/matrix.tsv" <<'PY' import json import sys from pathlib import Path index = json.loads(Path(sys.argv[1]).read_text()) oracle = json.loads(Path(sys.argv[2]).read_text()) assert index["case_count"] == 13 and oracle["passed_count"] == 13 rows = ["id\texpected_errno\treject"] for fixture, result in zip(index["cases"], oracle["results"], strict=True): rows.append( f"{fixture['id']}\t{result['actual_errno']}\t" f"{fixture['expected_reject']}" ) Path(sys.argv[3]).write_text("\n".join(rows) + "\n") PY tar -C "$evidence/first" -czf "$evidence/fixtures.tar.gz" . qemu-img create -f qcow2 -F qcow2 -b "$base" \ "$evidence/owned-overlay.qcow2" >"$evidence/qemu-img.stdout" \ 2>"$evidence/qemu-img.stderr" port=$(python3 - <<'PY' import socket with socket.socket() as sock: sock.bind(("127.0.0.1", 0)) port = sock.getsockname()[1] if port == 9222: raise SystemExit("protected port selected") print(port) PY ) printf '%s\n' "$port" >"$evidence/owned-port.txt" qemu-system-x86_64 -accel tcg,thread=multi -cpu qemu64 -m 6144 -smp 4 \ -drive "file=$evidence/owned-overlay.qcow2,if=virtio,format=qcow2" \ -netdev "user,id=net0,hostfwd=tcp:127.0.0.1:$port-:22" \ -device virtio-net-pci,netdev=net0 -display none \ -serial "file:$evidence/serial.log" -monitor none \ >"$evidence/qemu.stdout" 2>"$evidence/qemu.stderr" & owned_pid=$! printf '%s\n' "$owned_pid" >"$evidence/owned-pid.txt" test "$owned_pid" != 26318 ready=0 for attempt in $(seq 1 180); do if ! kill -0 "$owned_pid" 2>/dev/null; then exit 21; fi set +e guest true >"$evidence/readiness.stdout" 2>"$evidence/readiness.stderr" readiness_rc=$? set -e printf '%s\n' "$readiness_rc" >"$evidence/readiness.rc" if test "$readiness_rc" = 0; then ready=1; break; fi sleep 1 done printf 'attempts=%s ready=%s\n' "$attempt" "$ready" >"$evidence/readiness.txt" test "$ready" = 1 capture guest-uname uname -a capture guest-freebsd-version freebsd-version -kru capture guest-osreldate sysctl -n kern.osreldate capture guest-kldstat-before kldstat capture guest-kldstat-module-before kldstat -q -m erofs capture guest-dmesg-before dmesg test "$(cat "$evidence/guest-kldstat-module-before.rc")" = 1 scp_to "$module" /root/B21-erofs-zstdio0.ko \ >"$evidence/upload-module.stdout" 2>"$evidence/upload-module.stderr" scp_to "$dut/tests/pre15/fixtures/B21-qemu-probe.c" /root/B21-qemu-probe.c \ >"$evidence/upload-probe.stdout" 2>"$evidence/upload-probe.stderr" scp_to "$evidence/fixtures.tar.gz" /root/B21-fixtures.tar.gz \ >"$evidence/upload-fixtures.stdout" 2>"$evidence/upload-fixtures.stderr" capture guest-module-file file /root/B21-erofs-zstdio0.ko capture guest-module-sha256 sha256 -q /root/B21-erofs-zstdio0.ko capture guest-prepare sh -c \ 'rm -rf /root/B21-fixtures /mnt/pre15-b21; mkdir /root/B21-fixtures /mnt/pre15-b21; tar -xzf /root/B21-fixtures.tar.gz -C /root/B21-fixtures; cc -O2 -Wall -Wextra -Werror -std=c17 -o /root/B21-qemu-probe /root/B21-qemu-probe.c' test "$(cat "$evidence/guest-prepare.rc")" = 0 capture B21-kldload kldload /root/B21-erofs-zstdio0.ko test "$(cat "$evidence/B21-kldload.rc")" = 0 loaded=1 capture guest-kldstat-after-load kldstat capture guest-kldstat-module-after-load kldstat -q -m erofs capture guest-dmesg-after-load dmesg test "$(cat "$evidence/guest-kldstat-module-after-load.rc")" = 0 printf 'id\texpected_errno\tactual_errno\treject\n' \ >"$evidence/qemu-matrix.tsv" while IFS="$(printf '\t')" read -r id expected_errno reject; do test "$id" != id || continue set +e md=$(guest mdconfig -a -t vnode -f "/root/B21-fixtures/$id.erofs" \ 2>"$evidence/$id-md.stderr") md_rc=$? set -e printf '%s\n' "$md_rc" >"$evidence/$id-md.rc" test "$md_rc" = 0 md_units="$md_units $md" set +e probe_result=$(guest /root/B21-qemu-probe "/dev/$md" /mnt/pre15-b21 \ 2>"$evidence/$id-probe.stderr") probe_rc=$? set -e printf '%s\n' "$probe_rc" >"$evidence/$id-probe.rc" printf '%s\n' "$probe_result" >"$evidence/$id-probe.stdout" actual_errno=$(printf '%s\n' "$probe_result" | \ sed -n 's/.*errno=\([0-9][0-9]*\).*/\1/p') test -n "$actual_errno" printf '%s\t%s\t%s\t%s\n' "$id" "$expected_errno" "$actual_errno" \ "$reject" >>"$evidence/qemu-matrix.tsv" test "$actual_errno" = "$expected_errno" if test "$actual_errno" = 0; then mounted=1 guest find /mnt/pre15-b21 -mindepth 1 -maxdepth 1 -print \ >"$evidence/$id-readdir.txt" guest umount /mnt/pre15-b21 mounted=0 fi guest mdconfig -d -u "$md" md_units=$(printf '%s\n' "$md_units" | sed "s/ $md//") done <"$evidence/matrix.tsv" capture guest-dmesg-after dmesg capture guest-kldstat-after kldstat capture guest-kldstat-module-after kldstat -q -m erofs diff -u "$evidence/guest-dmesg-before.stdout" \ "$evidence/guest-dmesg-after.stdout" >"$evidence/dmesg.diff" || true if grep -Eqi 'panic:|lock order reversal|witness.*warning|use-after-free' \ "$evidence/dmesg.diff"; then exit 10 fi guest kldunload /root/B21-erofs-zstdio0.ko >"$evidence/guest-kldunload.stdout" \ 2>"$evidence/guest-kldunload.stderr" loaded=0 capture guest-kldstat-module-clean kldstat -q -m erofs capture guest-mount-clean mount -p capture guest-md-clean mdconfig -l test "$(cat "$evidence/guest-kldstat-module-clean.rc")" = 1 test ! -s "$evidence/guest-md-clean.stdout" test -z "$(awk '$3 == "erofs" { print }' "$evidence/guest-mount-clean.stdout")" printf '%s\n' reached >"$evidence/target.marker" printf '%s\n' 'B21_PASS TC172-super-trust 13/13' >"$evidence/RESULT.txt"