#!/bin/sh
# DF-2550 fix-verification driver.  Run on the PATCHED (single-fix) kernel.
# Same NFS repro as run.sh, but the expected outcome is the NEGATION:
#   - on baseline (#0): guest PANICS ("locking against myself") after the
#     failing VOP_GETATTR leaks the vnode lock.
#   - on patched  (#1): NO panic; the fchown loop keeps returning the
#     VOP_GETATTR error (e.g. EINTR/EIO) cleanly for many iterations,
#     because vput(vp) now releases the lock on the error path. Guest stays up.
set -u
DIR=$(cd "$(dirname "$0")" && pwd)
ROOT=$(cd "$DIR/../../.." && pwd)
SSH="ssh -F $ROOT/dfbsd-qemu/config -o BatchMode=yes -o ConnectTimeout=8"
VM="$ROOT/dfbsd-qemu/vm.sh"
MNT=/mnt/nfs; EXP=/export
LOG="$DIR/fix_run.log"

echo "[fix] patched kernel:" | tee "$LOG"
$VM run_root 'sysctl -n kern.version | head -1' 2>&1 | tee -a "$LOG"

echo "[fix] NFS server up" | tee -a "$LOG"
$VM run_root 'umount -f '"$MNT"' 2>/dev/null
pkill -9 nfsd 2>/dev/null; pkill -9 mountd 2>/dev/null; pkill -9 rpcbind 2>/dev/null; sleep 1
mkdir -p '"$EXP"' '"$MNT"'; echo "nfs repro" > '"$EXP"'/f; chown 1001 '"$EXP"'/f; chmod 644 '"$EXP"'/f
echo "'"$EXP"' -alldirs -maproot=0 127.0.0.1" > /etc/exports
/usr/sbin/rpcbind; sleep 1; /sbin/mountd; sleep 1; /sbin/nfsd -t -u -n 4; sleep 2
mount_nfs -3 -U -s -t 1 -x 1 -o acregmin=0,acregmax=0 127.0.0.1:'"$EXP"' '"$MNT"' && echo MOUNT_OK
ls -l '"$MNT"'/f' 2>&1 | grep -E "MOUNT_OK|maxx" | tee -a "$LOG"

rm -f "$DIR/trigger_fix.out"
( $SSH dfbsd-maxx /bin/sh <<EOF
cd poc/DF-2550 && exec ./trigger $MNT/f
EOF
) > "$DIR/trigger_fix.out" 2>&1 &
TPID=$!
sleep 4
echo "[fix] steady state:" | tee -a "$LOG"; tail -2 "$DIR/trigger_fix.out" | tee -a "$LOG"

echo "[fix] kill NFS server" | tee -a "$LOG"
$VM run_root 'pkill -9 nfsd; sleep 1; pgrep nfsd || echo nfsd_dead' 2>&1 | grep -E "dead" | tee -a "$LOG"

echo "[fix] wait 90s; on patched kernel the guest must STAY UP" | tee -a "$LOG"
i=0
while [ $i -lt 45 ]; do
    if ! $VM run_root 'true' >/dev/null 2>&1; then
        echo "[fix] !!! guest went DOWN at +$((i*2))s (PANIC = fix FAILED)" | tee -a "$LOG"; break
    fi
    sleep 2; i=$((i+1))
done

echo "===== trigger_fix.out (tail) =====" | tee -a "$LOG"
tail -10 "$DIR/trigger_fix.out" | tee -a "$LOG"
echo "===== guest status (expect up) =====" | tee -a "$LOG"
$VM status 2>&1 | tee -a "$LOG"
echo "===== serial panic check (expect none) =====" | tee -a "$LOG"
$VM log 2000 2>/dev/null | grep -iE "panic:|locking against myself" | tail -5 | tee -a "$LOG"

if $VM run_root 'true' >/dev/null 2>&1; then
    echo "[fix] RESULT: guest still UP after NFS failure -> NO PANIC -> FIX VALIDATED" | tee -a "$LOG"
    exit 0
else
    echo "[fix] RESULT: guest DOWN -> fix did NOT prevent the panic" | tee -a "$LOG"
    exit 1
fi
