#!/bin/sh
# DF-4801 exact trigger (panic-free variant): revive waitlist orphaned.
#
# The revive state machine is driven one block per ioctl by userland.
# sd->waitlist is drained ONLY from revive_block()'s success path
# (vinumrevive.c:198-218).  Prime the sd into sd_reviving with ONE
# `setstate up` (runs exactly one clean revive_block with a 64k block,
# empty waitlist; cursor lands at sector 128 = byte 64k), then simply
# never drive it again: every write straddling the cursor is queued on
# the waitlist forever.  No set_sd_state transition away from
# sd_reviving (down/stale/detach/stop) drains it, so the writers hang
# uninterruptibly (biowait without PCATCH).
#
# Writers target ONLY the straddle window [64k,96k) so every write is
# XFR_REVIVECONFLICT -> waitlisted (never the REQUEST_DOWN /
# trailing-BAD-group launch path of the DF-4721 arm, so this trigger
# is deterministic and does not race the UAF).
#
# Expect (stock): writers freeze in state D immediately; kill -9
# ineffective; `vinum setstate -f down` (aborting the revive) leaves
# them hung.  Expect (fix): abort drains -> writers return (EIO).
date
echo "== setup =="
sh /tmp/df4800_setup.sh

echo "== prime sd into sd_reviving (one 64k block via ONE ioctl, then STOP driving it) =="
cc -O -o /tmp/prime /tmp/prime.c 2>/dev/null || echo "prime build failed"
/tmp/prime 1
vinum list | tr -d "\r" | grep -E "^S "

echo "== 4 writers hammering the straddle window [64k,96k) =="
for id in 1 2 3 4; do
  (
    i=0
    while [ ! -f /tmp/df4801.stop ]; do
      off=$(( 16 + id % 8 ))
      dd if=/dev/zero of=/dev/vinum/vol/vol bs=4k seek=$off count=1 conv=notrunc >/dev/null 2>&1
      i=$((i + 1))
      echo $i > /tmp/df4801.w.$id
    done
  ) &
  eval WPID$id=$!
done
sleep 3
echo "== writer progress (should be FROZEN: waitlisted, no drainer) =="
for id in 1 2 3 4; do echo "writer$id: $(cat /tmp/df4801.w.$id 2>/dev/null) writes"; done
sleep 3
echo "-- +3s --"
for id in 1 2 3 4; do echo "writer$id: $(cat /tmp/df4801.w.$id 2>/dev/null) writes"; done

echo "== ps: hung writers =="
ps -axww -o pid,stat,wchan,command | grep -E "[d]d if=/dev/zero of=/dev/vinum|seek=\$" | head -8

echo "== kill -9 the writer subshells AND dd children =="
for id in 1 2 3 4; do kill -9 $(eval echo \$WPID$id) 2>/dev/null; done
pkill -9 -f "dd if=/dev/zero of=/dev/vinum" 2>/dev/null
sleep 3
echo "-- after kill -9 --"
N=$(ps -axww -o pid,stat | awk '$2 ~ /^D/ {n++} END {print n+0}')
echo "D-state threads now: $N"
ps -axww -o pid,stat,wchan,command | grep "[d]d if=/dev/zero of=/dev/vinum" && echo ">>> UNKILLABLE HUNG WRITERS <<<" || echo "writers gone (no hang)"

echo "== abort the revive: setstate down -- does anything drain? =="
vinum setstate -f down vol.p1.s0 2>&1 | tr -d '\r' | head -3
sleep 3
echo "-- after revive abort --"
ps -axww -o pid,stat,wchan,command | grep "[d]d if=/dev/zero of=/dev/vinum" && echo ">>> STILL HUNG AFTER ABORT <<<" || echo "writers gone (drained: fix working)"
vinum list | tr -d "\r" | grep -E "^S "
touch /tmp/df4801.stop
dmesg | tail -3
date
