magisk-zapret2/tests/shell/transactional-start.sh
loop-uh eb55359f27 Remove dead legacy migration, track journals, and WAL reader
The legacy direct-rule migration ran unconditionally on every start and
stop to prove and retire firewall rules written by pre-chain module
generations. Reboot is the migration barrier: iptables state does not
survive it and updated module code never runs before it, so the entire
transactional prover (~665 lines) is replaced by deletion-on-sight of
its marker. The track-journal grammar (~324 lines) and the
firewall-teardown WAL reader validated formats with no remaining
producer; obsolete artifacts are now retired wholesale under the
lifecycle lock by retire_obsolete_state_artifacts. Also drops the dead
owner-generation reload cluster and the write-only nfqws2.cmdline
mirror.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-25 02:16:40 +03:00

162 lines
6.4 KiB
Shell

#!/bin/sh
set -eu
ROOT=$(CDPATH= cd -- "$(dirname "$0")/../.." && pwd)
TMP=${Z2_TEST_TMP:?}
CASE="$TMP/transactional-start"
MOD="$CASE/module"
SCRIPTS="$MOD/zapret2/scripts"
STATE="$CASE/state"
LOG="$CASE/calls"
fail() { echo "FAIL: transactional-start: $*" >&2; exit 1; }
mkdir -p "$SCRIPTS" "$STATE"
chmod 0700 "$STATE"
cp "$ROOT/zapret2/scripts/common.sh" "$SCRIPTS/common.sh"
cp "$ROOT/zapret2/scripts/command-builder.sh" "$SCRIPTS/command-builder.sh"
sed '$d' "$ROOT/zapret2/scripts/zapret-start.sh" > "$SCRIPTS/zapret-start-defs.sh"
cat > "$SCRIPTS/scenario.sh" <<'EOF'
#!/bin/sh
set -eu
. "$(dirname "$0")/zapret-start-defs.sh"
LOG=${Z2_START_TEST_LOG:?}
log_error() { printf 'log:%s\n' "$1" >> "$LOG"; }
release_lifecycle_lock() { echo release >> "$LOG"; return 0; }
write_ok_status() { echo status-ok >> "$LOG"; return 0; }
set_owner_phase() { return 0; }
restore_status_facts() { return 0; }
snapshot_owned_state() {
SNAP_PID=""; SNAP_PID_START=""; SNAP_GENERATION=""; SNAP_PID_VERIFIED=0
SNAP_IPV4=0; SNAP_IPV6=0; SNAP_RULES=0; SNAP_CHAINS=0; SNAP_ANCHORS=0
}
write_iptables_status() { echo status-error >> "$LOG"; return 0; }
case "$1" in
pre)
CONTROLLED_TEARDOWN_STARTED=0
rollback_start() { echo unexpected-rollback >> "$LOG"; return 0; }
fail_start preflight-failure
;;
post|signal)
CONTROLLED_TEARDOWN_STARTED=1; FIREWALL_MUTATED=1
rollback_start() { echo rollback-new >> "$LOG"; FIREWALL_MUTATED=0; return 0; }
if [ "$1" = signal ]; then handle_signal TERM; else fail_start post-teardown-failure; fi
;;
mismatch)
ensure_state_dir() { return 0; }
acquire_lifecycle_lock() { return 0; }
audit_recovery_artifacts() { return 0; }
uninstall_tombstone_allows_start() { return 0; }
read_install_generation_meta() { echo metadata-rejected >> "$LOG"; return 1; }
read_verified_pidfile() {
OWNER_STATE_PHASE=active
OWNER_STATE_SCHEMA_VERSION="$OWNER_STATE_VERSION"
return 0
}
write_runtime_owner_marker() { echo MUTATION-owner-marker >> "$LOG"; return 0; }
prepare_lifecycle_log() { echo MUTATION-log >> "$LOG"; return 0; }
main
;;
fast-match|fast-mismatch)
REPLACE=1
FAST_REPLACE_BASELINE=1
FAST_REPLACE_FIREWALL_FINGERPRINT=matching-firewall
FAST_REPLACE_IPV6_ACTIVE=1
FAST_REPLACE_IPV4_CONNBYTES=1
FAST_REPLACE_IPV6_CONNBYTES=1
z2_fw_tool_available() { [ "$1" = ip6tables ]; }
z2_fw_restore_available() { [ "$1" = ip6tables ]; }
prepare_new_firewall_identity() {
FIREWALL_TAG=stable0001
ZAPRET2_OUT=ZAPRET2_OUT
ZAPRET2_IN=ZAPRET2_IN
PENDING_OWNER_GENERATION=request-generation
return 0
}
prepare_owner_generation_spec() {
OWNER_WRITE_IPV4_RULES=4
OWNER_WRITE_IPV6_RULES=4
if [ "$1" = 1 ] && [ "$2" = 1 ] && [ "$PORTS_TCP" = 80,443 ]; then
OWNER_WRITE_FIREWALL_FINGERPRINT=matching-firewall
else
OWNER_WRITE_FIREWALL_FINGERPRINT=different-firewall
fi
return 0
}
PORTS_TCP=80,443
if [ "$1" = fast-mismatch ]; then PORTS_TCP=443; fi
if prepare_fast_replace_candidate; then
[ "$1" = fast-match ] || exit 3
[ "$FAST_REPLACE_READY" = 1 ] &&
[ "$IPV4_RULES:$IPV6_RULES:$IPV6_ACTIVE" = 4:4:1 ]
else
[ "$1" = fast-mismatch ] && [ "$FAST_REPLACE_READY" = 0 ]
fi
;;
published-fast)
STARTED_PID=4321
STARTED_PID_START=98765
PENDING_OWNER_GENERATION=request-generation
PUBLISHED_PID="$STARTED_PID"
PUBLISHED_START="$STARTED_PID_START"
PUBLISHED_GENERATION="$PENDING_OWNER_GENERATION"
PUBLISHED_FIREWALL_FINGERPRINT=matching-firewall
PUBLISHED_IPV4_RULES=4
PUBLISHED_IPV6_RULES=4
PUBLISHED_INSTALL_GENERATION=install-generation
PUBLISHED_INSTALL_ARCHIVE_SHA256=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
FAST_REPLACE_FIREWALL_FINGERPRINT=matching-firewall
IPV4_RULES=4
IPV6_RULES=4
IPV6_ACTIVE=1
INSTALL_META_GENERATION=install-generation
INSTALL_META_ARCHIVE_SHA256=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
read_verified_pidfile() { exit 91; }
read_install_generation_meta() { exit 92; }
fast_replace_health_ok
[ "$HEALTH_PID:$HEALTH_PID_START:$HEALTH_GENERATION" = \
"$STARTED_PID:$STARTED_PID_START:$PENDING_OWNER_GENERATION" ]
;;
*) exit 2 ;;
esac
EOF
chmod 0755 "$SCRIPTS/scenario.sh"
run_failure() {
scenario="$1"
: > "$LOG"
set +e
Z2_START_TEST_LOG="$LOG" STATE_DIR="$STATE" sh "$SCRIPTS/scenario.sh" "$scenario" > "$CASE/$scenario.out" 2>&1
rc=$?
set -e
[ "$rc" = 1 ] || fail "$scenario did not fail transactionally"
}
printf 'generation=prior-generation\n' > "$CASE/prior-owner"
prior_hash=$(sha256sum "$CASE/prior-owner" | awk '{print $1}')
run_failure pre
[ "$prior_hash" = "$(sha256sum "$CASE/prior-owner" | awk '{print $1}')" ] || fail "pre-teardown failure changed prior generation"
! grep -q '^unexpected-' "$LOG" || fail "pre-teardown failure entered rollback/restore"
run_failure post
grep -Fxq rollback-new "$LOG" || fail "post-teardown failure did not roll back new state"
! grep -Fq exact-prior-restored "$LOG" ||
fail "post-teardown failure restored an obsolete generation"
run_failure mismatch
grep -Fxq metadata-rejected "$LOG" || fail "install generation mismatch was not checked"
! grep -q '^MUTATION-' "$LOG" || fail "install generation mismatch mutated lifecycle state"
run_failure signal
grep -Fxq rollback-new "$LOG" || fail "signal did not roll back new state"
! grep -Fq exact-prior-restored "$LOG" || fail "signal restored an obsolete generation"
Z2_START_TEST_LOG="$LOG" STATE_DIR="$STATE" sh "$SCRIPTS/scenario.sh" fast-match ||
fail "matching authenticated firewall topology did not select daemon-only replacement"
Z2_START_TEST_LOG="$LOG" STATE_DIR="$STATE" sh "$SCRIPTS/scenario.sh" fast-mismatch ||
fail "changed firewall topology did not fall back to the full transaction"
Z2_START_TEST_LOG="$LOG" STATE_DIR="$STATE" sh "$SCRIPTS/scenario.sh" published-fast ||
fail "published owner receipt was re-read instead of consumed directly"
echo "Transactional start shell tests passed"