fix(apt): snapshot recursive roots separately

This commit is contained in:
2026-09-16 10:01:41 +02:00
parent 457db3934c
commit 9b4af01de2

View File

@@ -2,13 +2,13 @@
set -o pipefail set -o pipefail
set -xv
declare -r config_file='/etc/apt-zfs-snapshot.conf' declare -r config_file='/etc/apt-zfs-snapshot.conf'
declare -r zfs_property='space.quico:auto-snapshot' declare -r zfs_property='space.quico:auto-snapshot'
declare -r zfs_property_value='true' declare -r zfs_property_value='true'
declare -r zfs_max_name_length=255 declare -r zfs_max_name_length=255
set -xv
if [[ -r "${config_file}" ]]; then if [[ -r "${config_file}" ]]; then
# shellcheck disable=SC1090 # shellcheck disable=SC1090
source "${config_file}" source "${config_file}"
@@ -34,7 +34,7 @@ downgrade_suffix="${downgrade_suffix:-down}"
mixed_suffix="${mixed_suffix:-mixd}" mixed_suffix="${mixed_suffix:-mixd}"
package_separator="${package_separator:-:}" package_separator="${package_separator:-:}"
declare -a packages operations declare -a packages operations successful_datasets
declare existing_snapshots declare existing_snapshots
print_msg() { print_msg() {
@@ -78,7 +78,7 @@ parse_apt_input() {
continue continue
fi fi
read -r package old_version _old_arch direction new_version _new_arch _multiarch action <<<"${line}" read -r package old_version _old_arch _old_multiarch direction new_version _new_arch _new_multiarch action <<<"${line}"
[[ -n "${package}" ]] || continue [[ -n "${package}" ]] || continue
if [[ "${action}" == '**CONFIGURE**' ]]; then if [[ "${action}" == '**CONFIGURE**' ]]; then
continue continue
@@ -95,18 +95,31 @@ parse_apt_input() {
} }
get_datasets() { get_datasets() {
local dataset value local dataset value candidate parent
local -a candidates
local mounted_datasets
if [[ -n "${snapshot_roots}" ]]; then if [[ -n "${snapshot_roots}" ]]; then
read -r -a snappable_datasets <<<"${snapshot_roots}" read -r -a candidates <<<"${snapshot_roots}"
snappable_datasets=("${candidates[@]}")
return 0 return 0
fi fi
mounted_datasets="$(findmnt -rn -t zfs -o SOURCE)"
while IFS=$'\t' read -r dataset value; do while IFS=$'\t' read -r dataset value; do
[[ "${value}" == "${zfs_property_value}" && "${dataset}" != *@* ]] || continue [[ "${value}" == "${zfs_property_value}" && "${dataset}" != *@* ]] || continue
if [[ "${snap_only_local_datasets}" != 'true' ]] || findmnt -rn -t zfs -o SOURCE | grep -Fxq -- "${dataset}"; then if [[ "${snap_only_local_datasets}" != 'true' ]] || grep -Fxq -- "${dataset}" <<<"${mounted_datasets}"; then
snappable_datasets+=("${dataset}") candidates+=("${dataset}")
fi fi
done < <(zfs get -H -o name,value "${zfs_property}") done < <(zfs get -H -t filesystem,volume -o name,value "${zfs_property}")
# An inherited property marks every descendant. Keep only the highest
# selected dataset because the snapshot operation is recursive.
for dataset in "${candidates[@]}"; do
for parent in "${candidates[@]}"; do
[[ "${dataset}" == "${parent}"/* ]] && continue 2
done
snappable_datasets+=("${dataset}")
done
} }
operation_suffix() { operation_suffix() {
@@ -181,7 +194,7 @@ prune_snapshots() {
[[ "${dry_run}" == 'true' ]] && return 0 [[ "${dry_run}" == 'true' ]] && return 0
limit="${snapshots_trivial_keep}" limit="${snapshots_trivial_keep}"
[[ "${severity}" == "${important_suffix}" ]] && limit="${snapshots_important_keep}" [[ "${severity}" == "${important_suffix}" ]] && limit="${snapshots_important_keep}"
for dataset in "${snappable_datasets[@]}"; do for dataset in "${successful_datasets[@]}"; do
# Query only the root dataset. Destroying its recursive snapshot also # Query only the root dataset. Destroying its recursive snapshot also
# removes the matching descendant snapshots as one snapshot tree. # removes the matching descendant snapshots as one snapshot tree.
mapfile -t snapshots < <(zfs list -H -t snapshot -o name -s creation "${dataset}" | grep -F "${dataset}@${snapshot_prefix}${field_separator}" | grep -F "${field_separator}sev:${severity}${field_separator}") mapfile -t snapshots < <(zfs list -H -t snapshot -o name -s creation "${dataset}" | grep -F "${dataset}@${snapshot_prefix}${field_separator}" | grep -F "${field_separator}sev:${severity}${field_separator}")
@@ -220,10 +233,21 @@ main() {
printf ' %s\n' "${names[@]}" >&2 printf ' %s\n' "${names[@]}" >&2
return 0 return 0
fi fi
zfs snapshot -r "${names[@]}" || { print_msg WARN 'ZFS snapshot failed'; return 0; } successful_datasets=()
printf '[INFO] Created ZFS snapshots:\n' >&2 local name dataset snapshot_failed='false'
printf ' %s\n' "${names[@]}" >&2 for name in "${names[@]}"; do
prune_snapshots dataset="${name%%@*}"
if zfs snapshot -r "${name}"; then
successful_datasets+=("${dataset}")
printf '[INFO] Created ZFS snapshots for %s:\n' "${dataset}" >&2
printf ' %s\n' "${name}" >&2
else
snapshot_failed='true'
print_msg WARN "ZFS snapshot failed for ${dataset}"
fi
done
[[ "${snapshot_failed}" == 'false' ]] || print_msg WARN 'Snapshot set is incomplete'
((${#successful_datasets[@]} > 0)) && prune_snapshots
} }
main main