fix(restore): robust empty-dir listing + injection-proof container swap

Finding 1: no-arg branch used 'ls ... | while' which, under set -euo
pipefail, exits 1 on an empty backups dir (glob matches nothing). Replace
with shopt nullglob array check that prints a clear message and exit 0.

Finding 2: pass $TS and $SNAP_NAME as positional args to the inner alpine
shell instead of interpolating them into the sh -c string, making the swap
injection-proof. Behavior unchanged: pre-restore copy -> clear WAL/SHM -> install.
This commit is contained in:
Jannis Braun
2026-06-20 02:38:07 +02:00
parent 70da1ede64
commit a4333c4c23
+12 -5
View File
@@ -16,8 +16,15 @@ fi
# No arg: list snapshots newest-first and exit.
if [[ $# -eq 0 ]]; then
shopt -s nullglob
snaps=("$BACKUP_DIR"/*.db)
shopt -u nullglob
if [[ ${#snaps[@]} -eq 0 ]]; then
echo "No snapshots found in $BACKUP_DIR."
exit 0
fi
echo "Available snapshots (newest first):"
ls -1t "$BACKUP_DIR"/*.db 2>/dev/null | while read -r f; do
for f in $(ls -1t "$BACKUP_DIR"/*.db); do
printf " %s (%s)\n" "$(basename "$f")" "$(du -h "$f" | cut -f1)"
done
echo ""
@@ -45,14 +52,14 @@ docker compose stop backspace
# (youruser is in the docker group on both boxes — no sudo prompt.)
TS="$(date -u +%Y%m%dT%H%M%S)"
echo "[2/3] Swapping DB inside a root container (pre-restore copy + WAL clear + install)..."
docker run --rm -v "$(pwd)/data:/data" alpine sh -c "
docker run --rm -v "$(pwd)/data:/data" alpine sh -c '
set -e
if [ -f /data/backspace.db ]; then
cp /data/backspace.db /data/backups/backspace-${TS}-pre-restore.db
cp /data/backspace.db "/data/backups/backspace-$1-pre-restore.db"
fi
rm -f /data/backspace.db-wal /data/backspace.db-shm
cp /data/backups/${SNAP_NAME} /data/backspace.db
"
cp "/data/backups/$2" /data/backspace.db
' sh "$TS" "$SNAP_NAME"
echo "[3/3] Starting backspace container..."
docker compose start backspace