#!/bin/bash # Snapshot the root dataset before a pacman transaction, then prune to the most # recent $KEEP pre-pacman snapshots. Run from the zfs-snapshot.hook pacman hook # (PreTransaction). Sanoid doesn't manage these (they aren't autosnap_ names), # so retention is enforced here at creation time. # # Defaults match the live zroot layout; the ZFS_PRE_* env vars override them so # the pruning logic is unit-testable against a fake zfs on PATH. POOL="${ZFS_PRE_POOL:-zroot}" DATASET="${ZFS_PRE_DATASET:-$POOL/ROOT/default}" LOCKFILE="${ZFS_PRE_LOCKFILE:-/tmp/.zfs-pre-snapshot.lock}" MIN_INTERVAL="${ZFS_PRE_MIN_INTERVAL:-60}" KEEP="${ZFS_PRE_KEEP:-10}" # pre-pacman snapshots to retain (recent-transaction rollback) # Skip if a snapshot was created within the last $MIN_INTERVAL seconds. A single # pacman invocation can fire several transactions; this stops a burst of them # from each cutting a near-identical snapshot. if [ -f "$LOCKFILE" ]; then last=$(stat -c %Y "$LOCKFILE" 2>/dev/null || echo 0) now=$(date +%s) if (( now - last < MIN_INTERVAL )); then exit 0 fi fi TIMESTAMP=$(date +%Y-%m-%d_%H-%M-%S) SNAPSHOT_NAME="pre-pacman_$TIMESTAMP" if zfs snapshot "$DATASET@$SNAPSHOT_NAME"; then echo "Created snapshot: $DATASET@$SNAPSHOT_NAME" touch "$LOCKFILE" # Keep only the most recent $KEEP pre-pacman snapshots; destroy older ones. zfs list -H -o name -t snapshot -s creation "$DATASET" 2>/dev/null \ | grep '@pre-pacman_' \ | head -n -"$KEEP" \ | while read -r old; do zfs destroy "$old" && echo "Pruned old snapshot: $old" done else echo "Warning: Failed to create snapshot" >&2 fi