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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
|
#+TITLE: ZFS pre-pacman snapshot installer step (durable retention)
#+DATE: 2026-06-29
#+SOURCE: handoff from the home project, 2026-06-29
* Problem
A pacman =PreTransaction= hook snapshots =zroot/ROOT/default@pre-pacman_<ts>=
before every transaction, but nothing prunes them. Sanoid doesn't manage them
(they aren't =autosnap_= names), so they accumulated to 53 on velox between
April and the 2026-06-29 health check. Unbounded, they fill the pool over time.
* What's actually on velox vs. archsetup
The live =/usr/local/bin/zfs-pre-snapshot= is *not* authored by archsetup —
=git grep= for its content (=MIN_INTERVAL=, the pre-pacman =LOCKFILE= logic)
finds nothing tracked. The =PreTransaction= hooks in the archsetup monolith
(~lines 910, 1907, 1942) are the live-update guard, a different hook. The
script appears hand-placed on velox.
The 2026-01-17 security doc line "ZFS pre-pacman snapshots (already in
install-archzfs)" is therefore *out of date* — archsetup does not install this.
Incorporating the fix is a NET-NEW installer step, not a patch to an existing
one. Correct that stale doc line as part of the work.
velox was patched live (pruned to 10, script replaced with the self-pruning
version below); live backup at =/usr/local/bin/zfs-pre-snapshot.bak-2026-06-29=.
* Proposed installer step
In the archzfs / ZFS-on-root install path, gated to ZFS-root installs (velox is
the only ZFS daily driver; ratio is btrfs), install:
1. =/etc/pacman.d/hooks/05-zfs-snapshot.hook= — the =PreTransaction= hook that
runs the script. *Not included in the handoff* — source it from velox
(=/etc/pacman.d/hooks/05-zfs-snapshot.hook=) or write it.
2. =/usr/local/bin/zfs-pre-snapshot= — the =KEEP=10= self-pruning version
below.
Tests live in archsetup, so this wants an archsetup session and a ZFS-root VM
test (=make test FS_PROFILE=zfs=), not a cross-project edit from home.
* The script (KEEP=10 self-pruning version)
#+begin_src bash
#!/bin/bash
POOL="zroot"
DATASET="$POOL/ROOT/default"
LOCKFILE="/tmp/.zfs-pre-snapshot.lock"
MIN_INTERVAL=60
KEEP=10 # how many pre-pacman snapshots to retain (rollback safety for recent transactions)
# Skip if a snapshot was created within the last 60 seconds
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"
# Retention: keep only the most recent $KEEP pre-pacman snapshots, destroy older ones.
# Sanoid does not manage these (they aren't autosnap_), so prune them here at creation time.
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
#+end_src
* Implementation (2026-06-30)
- Hook sourced from velox (=/etc/pacman.d/hooks/05-zfs-snapshot.hook=) and embedded
as a heredoc in =configure_pre_pacman_snapshots()=.
- The installer removes the legacy unprefixed hook filenames during migration;
leaving them would run duplicate hooks, including a too-late snapshot.
- Insertion point: a new =configure_pre_pacman_snapshots()= gated on
=is_zfs_root=, called from =boot_ux= (the last step) so the hook doesn't fire
during the install's own package operations — the first pre-pacman snapshot is
the fresh system. The script ships as =scripts/zfs-pre-snapshot= (the
=zfs-replicate= pattern), made =ZFS_PRE_*=-env-overridable for testability.
- Tests: =tests/zfs-pre-snapshot/= unit-tests the pruning logic against a fake
=zfs= (creates, prunes oldest-past-KEEP, ignores non-=pre-pacman_= snapshots,
honors the lockfile, warns on snapshot failure); =test_boot.py= asserts the
hook + script land on a ZFS install; the orchestrator test pins the new
=boot_ux= substep.
* Note on the "stale security doc"
The 2026-01-17 line "ZFS pre-pacman snapshots (already in install-archzfs)" is
*not* stale: that file is an archive generated by install-archzfs (see its
header and footer), and the claim is accurate for install-archzfs. The real gap
was that archsetup took sanoid from install-archzfs but never ported the
pre-pacman hook. This change ports it. The archive is left untouched.
* Remaining
- ZFS-root VM verification (=make test FS_PROFILE=zfs=) before the task closes.
|