#!/bin/sh set -eu : "$PRE15_DUT" : "$PRE15_ROOT" : "$PRE15_CASE_TMP" : "$PRE15_RUN_DIR" : "$PRE15_LIB_DIR" . "$PRE15_LIB_DIR/runner.sh" test "$PRE15_MODE" = qemu || pre15_infra_blocked \ 'B19a focused case requires exact-ABI QEMU mode' fixture_dir=$PRE15_DUT/tests/pre15/fixtures generator=$fixture_dir/B19a-xattr-generate.py spec=$fixture_dir/B19a-xattr-spec.json probe=$fixture_dir/B19a-xattr-probe.c kld_builder=$fixture_dir/B28-build-kld.sh artifacts=$PRE15_RUN_DIR/artifacts first=$PRE15_CASE_TMP/fixtures-first second=$PRE15_CASE_TMP/fixtures-second module=$PRE15_CASE_TMP/B19a-erofs.ko for tool in cc cmp diff file mkfs.erofs nm python3 scp sha256sum tar timeout; do command -v "$tool" >/dev/null 2>&1 || \ pre15_infra_blocked "missing B19a QEMU tool: $tool" done for value in PRE15_QEMU_CONTROL_PATH PRE15_QEMU_SSH_KEY \ PRE15_QEMU_SSH_PORT PRE15_QEMU_SSH_USER; do test -n "$(eval "printf '%s' \"\$$value\"")" || \ pre15_runner_fail "$value is required" done for fixture in "$generator" "$spec" "$probe" "$kld_builder"; do pre15_record_fixture "b19a-$(basename "$fixture")" "$fixture" done for source in erofs_vnops.c internal.h xattr.c; do pre15_record_fixture "b19a-$source" "$PRE15_DUT/src/$source" done mkdir -p "$artifacts" if ! timeout -k 5 180 python3 -B "$generator" --spec "$spec" \ --output "$first" --work "$PRE15_CASE_TMP/generate-first" \ >"$artifacts/generate-first.json" 2>"$artifacts/generate-first.stderr"; then pre15_runner_fail 'B19a first fixture generation failed' fi if ! timeout -k 5 180 python3 -B "$generator" --spec "$spec" \ --output "$second" --work "$PRE15_CASE_TMP/generate-second" \ >"$artifacts/generate-second.json" 2>"$artifacts/generate-second.stderr"; then pre15_runner_fail 'B19a second fixture generation failed' fi if ! diff -qr "$first" "$second" >"$artifacts/fixture-repeat.diff"; then pre15_runner_fail 'B19a focused fixtures are not byte reproducible' fi pre15_record_fixture b19a-valid-image "$first/valid.erofs" tar -C "$first" -czf "$PRE15_CASE_TMP/B19a-fixtures.tar.gz" . if ! timeout -k 10 600 /bin/sh "$kld_builder" "$PRE15_DUT" \ "$PRE15_FREEBSD_SRC" "$module" "$PRE15_CASE_TMP/kld-work" 0 \ >"$artifacts/kld-build.stdout" 2>"$artifacts/kld-build.stderr"; then pre15_dut_fail 'B19a exact-ABI KLD build failed' fi pre15_record_module "$module" file "$module" >"$artifacts/kld-file.txt" sha256sum "$module" >"$artifacts/kld-sha256.txt" nm -u "$module" | LC_ALL=C sort >"$artifacts/kld-nm-u.txt" pre15_scp() { timeout -k 5 60 scp -O -q \ -o BatchMode=yes -o StrictHostKeyChecking=no \ -o UserKnownHostsFile=/dev/null -o ConnectTimeout=5 \ -o "ControlPath=$PRE15_QEMU_CONTROL_PATH" \ -i "$PRE15_QEMU_SSH_KEY" -P "$PRE15_QEMU_SSH_PORT" \ "$1" "$PRE15_QEMU_SSH_USER@127.0.0.1:$2" } if ! pre15_scp "$PRE15_CASE_TMP/B19a-fixtures.tar.gz" \ /root/B19a-fixtures.tar.gz || ! pre15_scp "$probe" /root/B19a-xattr-probe.c || ! pre15_scp "$module" /root/B19a-erofs.ko; then pre15_infra_blocked 'could not transfer B19a module, probe, or fixtures' fi pre15_guest_ssh_bounded \ 'rm -rf /root/pre15-b19a-fixtures && mkdir /root/pre15-b19a-fixtures && tar -xzf /root/B19a-fixtures.tar.gz -C /root/pre15-b19a-fixtures' || pre15_infra_blocked 'could not prepare B19a guest fixtures' pre15_guest_ssh_bounded cc -std=c11 -Wall -Wextra -Werror \ -o /root/B19a-xattr-probe /root/B19a-xattr-probe.c || pre15_infra_blocked 'could not compile the B19a guest probe' if pre15_guest_ssh_bounded kldstat -n B19a-erofs >/dev/null 2>&1; then pre15_infra_blocked 'guest already has an EROFS KLD loaded' fi pre15_guest_ssh_bounded dmesg >"$artifacts/dmesg-before-load.txt" if ! pre15_guest_ssh_bounded kldload /root/B19a-erofs.ko \ >"$artifacts/kldload.stdout" 2>"$artifacts/kldload.stderr"; then pre15_dut_fail 'B19a exact-ABI KLD load failed' fi pre15_own_guest_kld B19a-erofs 'B19a exact-source KLD' pre15_guest_ssh_bounded kldstat >"$artifacts/kldstat-after-load.txt" b19a_unown() { tmp=$PRE15_CASE_TMP/ownership.$$ awk -F ' ' -v kind="$1" -v value="$2" \ '!($1 == kind && $2 == value)' "$PRE15_OWNERSHIP_FILE" >"$tmp" mv "$tmp" "$PRE15_OWNERSHIP_FILE" } b19a_mount() { image=$1 mountpoint=$2 label=$3 md=$(pre15_guest_ssh_bounded mdconfig -a -t vnode \ -f "/root/pre15-b19a-fixtures/$image") || pre15_dut_fail "$label provider attach failed" case "$md" in md[0-9]*) ;; *) pre15_runner_fail "unexpected B19a md unit: $md" ;; esac pre15_own_guest_md "$md" "$label provider" pre15_guest_ssh_bounded mkdir -p "$mountpoint" if ! pre15_guest_ssh_bounded mount -t erofs -o ro "/dev/$md" \ "$mountpoint"; then pre15_dut_fail "$label mount failed" fi pre15_own_guest_mount "$mountpoint" "$label mount" printf '%s\n' "$md" } b19a_detach() { mountpoint=$1 md=$2 pre15_guest_ssh_bounded umount "$mountpoint" || pre15_dut_fail "B19a unmount failed: $mountpoint" b19a_unown guest-mount "$mountpoint" pre15_guest_ssh_bounded mdconfig -d -u "$md" || pre15_dut_fail "B19a md detach failed: $md" b19a_unown guest-md "$md" } valid_md=$(b19a_mount valid.erofs /mnt/pre15-b19a-valid 'B19a valid') valid=/mnt/pre15-b19a-valid/target.bin pre15_guest_ssh_bounded /root/B19a-xattr-probe valid "$valid" \ >"$artifacts/valid-first.txt" pre15_guest_ssh_bounded /root/B19a-xattr-probe concurrent "$valid" 16 10 \ >"$artifacts/valid-concurrent.txt" pre15_guest_ssh_bounded /root/B19a-xattr-probe valid "$valid" \ >"$artifacts/valid-repeat.txt" b19a_detach /mnt/pre15-b19a-valid "$valid_md" for image in corrupt-shared-count.erofs corrupt-shared-id.erofs corrupt-inline-name.erofs; do label=$(basename "$image" .erofs) mountpoint=/mnt/pre15-b19a-$label md=$(b19a_mount "$image" "$mountpoint" "B19a $label") if ! pre15_guest_ssh_bounded /root/B19a-xattr-probe corrupt \ "$mountpoint/target.bin" >"$artifacts/$label.txt" 2>&1; then pre15_dut_fail "$label did not return EINTEGRITY" fi pre15_guest_ssh_bounded umount "$mountpoint" || pre15_dut_fail "$label unmount failed" b19a_unown guest-mount "$mountpoint" pre15_guest_ssh_bounded mdconfig -d -u "$md" || pre15_dut_fail "$label md detach failed" b19a_unown guest-md "$md" done normal_md=$(b19a_mount valid.erofs /mnt/pre15-b19a-normal \ 'B19a normal-unmount') normal_pid=$(pre15_guest_ssh_bounded \ 'cd /mnt/pre15-b19a-normal && sleep 60 >/tmp/B19a-normal.sleep 2>&1 & echo $!') if pre15_guest_ssh_bounded umount /mnt/pre15-b19a-normal \ >"$artifacts/normal-unmount.stdout" 2>"$artifacts/normal-unmount.stderr"; then pre15_dut_fail 'normal unmount unexpectedly ignored a held vnode' fi pre15_guest_ssh_bounded kill "$normal_pid" || true pre15_guest_ssh_bounded umount /mnt/pre15-b19a-normal || pre15_dut_fail 'normal unmount failed after releasing held vnode' b19a_unown guest-mount /mnt/pre15-b19a-normal pre15_guest_ssh_bounded mdconfig -d -u "$normal_md" || pre15_dut_fail 'normal-unmount md detach failed' b19a_unown guest-md "$normal_md" forced_md=$(b19a_mount valid.erofs /mnt/pre15-b19a-forced \ 'B19a forced-unmount') forced_pid=$(pre15_guest_ssh_bounded \ 'cd /mnt/pre15-b19a-forced && sleep 60 >/tmp/B19a-forced.sleep 2>&1 & echo $!') pre15_guest_ssh_bounded umount -f /mnt/pre15-b19a-forced || pre15_dut_fail 'forced unmount failed' b19a_unown guest-mount /mnt/pre15-b19a-forced pre15_guest_ssh_bounded kill "$forced_pid" || true pre15_guest_ssh_bounded mdconfig -d -u "$forced_md" || pre15_dut_fail 'forced-unmount md detach failed' b19a_unown guest-md "$forced_md" pre15_guest_ssh_bounded dmesg >"$artifacts/dmesg-after.txt" diff -u "$artifacts/dmesg-before-load.txt" "$artifacts/dmesg-after.txt" \ >"$artifacts/dmesg.diff" || true sed -n '/^+++ /d; /^+/s/^+/ /p' "$artifacts/dmesg.diff" \ >"$artifacts/dmesg-added.txt" if grep -Eqi 'panic:|fatal trap|lock order reversal|witness.*warning|use-after-free|pager fault|undefined symbol|linker.*error' \ "$artifacts/dmesg-added.txt"; then pre15_dut_fail 'B19a QEMU produced a kernel diagnostic' fi pre15_guest_ssh_bounded kldunload B19a-erofs || pre15_dut_fail 'B19a KLD unload failed' b19a_unown guest-kld B19a-erofs if pre15_guest_ssh_bounded kldstat -n B19a-erofs >/dev/null 2>&1; then pre15_dut_fail 'B19a KLD remained loaded after cleanup' fi pre15_guest_ssh_bounded mount >"$artifacts/mount-after.txt" pre15_guest_ssh_bounded mdconfig -l >"$artifacts/md-after.txt" if grep -q 'pre15-b19a-' "$artifacts/mount-after.txt" || grep -q 'B19a' "$artifacts/md-after.txt"; then pre15_dut_fail 'B19a guest mount or md resource remained after cleanup' fi pre15_target_reached printf '%s\n' 'B19a exact-ABI xattr cache lifecycle PASS'