#!/bin/sh
# DF-0001 — fix-validation: same choreography as run.sh but WITHOUT the
# "INVARIANTS KASSERT strings must be present" precondition (since the
# fix *removes* the KASSERTs and therefore the panic strings from the
# kernel binary). On the patched kernel we expect:
#   * ftruncate(2) returns ESTALE (the GETATTR error), NOT panic
#   * guest stays up after the trigger
#   * no panic signature in dfbsd-qemu/boot.log
# Exits 0 if no panic was observed and the guest is still up.
set -eu
cd "$(dirname "$0")"
HERE="$(pwd)"
ROOT="$(cd "$HERE/../../.." && pwd)"
VM="$ROOT/dfbsd-qemu/vm.sh"
CFG="$ROOT/dfbsd-qemu/config"

echo "==> [1/5] ensure vfs.quota_enabled=1 (loader tunable, reboot if needed)"
$VM run_root 'grep -q quota_enabled /boot/loader.conf || echo "vfs.quota_enabled=\"1\"" >> /boot/loader.conf; sync'
if [ "$($VM run_root 'sysctl -n vfs.quota_enabled')" != "1" ]; then
    echo "    quota not yet enabled -> non-reverting reboot"
    ( $VM down >/dev/null 2>&1 ) || { P=$(cat "$ROOT/dfbsd-qemu/vm.pid"); kill -9 "$P" 2>/dev/null; }
    sleep 3
    $VM up 90
fi
$VM run_root 'test "$(sysctl -n vfs.quota_enabled)" = "1" || { echo "quota not enabled"; exit 4; }'

echo "==> [2/5] build trigger + stand up loopback NFS server + soft mount"
$VM run_user 'mkdir -p poc/DF-0001'
scp -F "$CFG" -q "$HERE/estale_trig.c" dfbsd-maxx:poc/DF-0001/
$VM run_user 'cd poc/DF-0001 && cc -O0 -g -o estale_trig estale_trig.c'
$VM run_root '
    mkdir -p /export; chmod 777 /export
    printf "/export -maproot=root -network 127.0.0.0 -mask 255.0.0.0\n" > /etc/exports
    pkill -x nfsd 2>/dev/null; pkill -x mountd 2>/dev/null; pkill -x rpcbind 2>/dev/null
    sleep 1
    rpcbind 2>/dev/null; sleep 1
    mountd 2>/dev/null; sleep 1
    nfsd -t -u -n 4 2>/dev/null; sleep 2
    umount -f /mnt 2>/dev/null; mkdir -p /mnt
    mount_nfs -U -s -x 1 -t 1 \
        -o acregmin=0,acregmax=0,acdirmin=0,acdirmax=0 127.0.0.1:/export /mnt
    dd if=/dev/zero of=/export/estale_target bs=4096 count=1 2>/dev/null
    chown maxx:maxx /export/estale_target; chmod 644 /export/estale_target
    ls -l /mnt/estale_target
'

echo "==> [3/5] launch estale_trig as maxx (opens fd, holds fixed filehandle)"
$VM run_user 'cd poc/DF-0001 && nohup ./estale_trig /mnt/estale_target > /tmp/df0001.out 2>&1 & echo "pid $!"'
sleep 3
$VM run_user 'cat /tmp/df0001.out'

echo "==> [4/5] invalidate the fd filehandle SERVER-SIDE (delete + recreate -> stale FH)"
$VM run_root 'rm -f /export/estale_target; sync; touch /export/estale_target; chown maxx:maxx /export/estale_target; chmod 644 /export/estale_target; echo handle_invalidated'

echo "==> [5/5] wait + observe (FIXED = no panic, guest up, ftruncate returns error)"
# give the trigger time to wake and call ftruncate, then check guest liveness
sleep 20
st=$($VM status 2>/dev/null || echo down)
echo "    guest status after trigger: $st"

echo "--- maxx trigger stdout/stderr ---"
$VM run_user 'cat /tmp/df0001.out 2>/dev/null || echo "(no output file)"' || true

echo "--- check boot.log for panic ---"
if grep -qE 'panic: kern_(f)?truncate' "$ROOT/dfbsd-qemu/boot.log"; then
    echo "!!! PANIC detected in boot.log — fix FAILED"
    awk '/panic: kern_(f)?truncate/{p=1} p{print} /^db> /{p=0; exit}' "$ROOT/dfbsd-qemu/boot.log"
    exit 2
else
    echo "no panic: kern_(f)?truncate in boot.log — fix HELD"
fi

if [ "$st" = "up" ]; then
    echo "==> RESULT: FIXED (guest up, no panic)"
    exit 0
else
    echo "==> RESULT: guest down (might be a different bug; inspect boot.log)"
    exit 3
fi
