Files
felhom.eu/documentation/audits/debian13-restic-2026-10-16/tools/locks.sh
T

37 lines
2.4 KiB
Bash

#!/usr/bin/env bash
# R-1105: lock behaviour, 0.14.0 vs 0.18.1. A lock left by a KILLED process on ANOTHER hostname (the C2 shape:
# the recreated container has a new hostname, so restic cannot prove the lock dead). Scratch repos only.
set -u
S=<scratch>
OUT=${1:?out}; W=$S/lockwork; rm -rf $W; mkdir -p $W $OUT
export RESTIC_PASSWORD_FILE=$W/pw RESTIC_CACHE_DIR=$W/cache TZ=UTC
head -c 32 /dev/urandom | od -An -tx1 | tr -d ' \n' > $RESTIC_PASSWORD_FILE
mkdir -p $W/src; echo a > $W/src/a.txt
for V in 0.14.0 0.18.1; do
R=$S/bin/restic-$V; REPO=$W/repo-$V; O=$OUT/$V; mkdir -p $O
$R -r $REPO init >/dev/null 2>&1; $R -r $REPO backup --tag t $W/src >/dev/null 2>&1; $R -r $REPO backup --tag t $W/src >/dev/null 2>&1
for KIND in shared exclusive; do
if [ $KIND = shared ]; then
# a backup reading stdin from a pipe that never ends holds a NON-exclusive lock
( sleep 600 | unshare --uts --map-root-user sh -c "hostname other-box; exec $R -r $REPO backup --stdin --stdin-filename x" ) >/dev/null 2>&1 &
else
# an exclusive holder: prune, killed the moment its lock file appears (the C2 crash shape)
( unshare --uts --map-root-user sh -c "hostname other-box; exec $R -r $REPO prune" ) >/dev/null 2>&1 &
fi
for i in $(seq 1 400); do [ -n "$(ls $REPO/locks 2>/dev/null)" ] && break; sleep 0.005; done
pkill -9 -f "restic-$V -r $REPO" ; pkill -f 'sleep 600'; sleep 0.5
echo "locks after kill: $(ls $REPO/locks | wc -l)" > $O/$KIND.txt
if [ "$(ls $REPO/locks | wc -l)" = 0 ]; then echo "NO LOCK LEFT — holder finished first" >> $O/$KIND.txt; continue; fi
{ echo "--- backup"; $R -r $REPO backup --tag t $W/src 2>&1; echo "rc=$?"
echo "--- forget --prune"; $R -r $REPO forget --keep-last 1 --prune 2>&1; echo "rc=$?"
echo "--- restore --no-lock"; $R -r $REPO restore latest --target $W/r1-$V-$KIND --no-lock 2>&1 | tail -2; echo "rc=${PIPESTATUS[0]}"
echo "--- unlock (stale only)"; $R -r $REPO unlock 2>&1; echo "rc=$?"; echo "locks: $(ls $REPO/locks | wc -l)"
echo "--- unlock --remove-all"; $R -r $REPO unlock --remove-all 2>&1; echo "rc=$?"; echo "locks: $(ls $REPO/locks | wc -l)"
echo "--- forget --prune after"; $R -r $REPO forget --keep-last 1 --prune 2>&1 | tail -1; echo "rc=${PIPESTATUS[0]}"
} >> $O/$KIND.txt
$R -r $REPO backup --tag t $W/src >/dev/null 2>&1
done
done
sed -i "s#$W#<W>#g" $OUT/*/*.txt
echo done