aboutsummaryrefslogtreecommitdiff
path: root/scripts/zfs-pre-snapshot
blob: ed914d0250869724398c6d6f57d2734ef07930ff (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#!/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