#!/usr/bin/env bash
# backup/stages/snapshot-btrfs.sh
# Take read-only btrfs snapshots of the subvolumes in SUBVOLS[], and
# prune to KEEP newest per subvolume.
set -euo pipefail
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=/dev/null
. "${HERE}/../../scripts/lib/log.sh"
SNAP_ROOT="${SNAP_ROOT:-/mnt/tank/.snapshots}"
KEEP="${KEEP:-30}"
TS="$(date +%Y%m%dT%H%M%S)"
SUBVOLS=(
/mnt/tank/srv
/mnt/tank/photos
/mnt/tank/docs
/mnt/tank/vaultwarden
)
have_btrfs() {
command -v btrfs >/dev/null 2>&1
}
snapshot_one() {
local src="$1"
local name; name=$(basename "${src}")
local dest="${SNAP_ROOT}/${name}/${TS}"
install -d "${SNAP_ROOT}/${name}"
if btrfs subvolume snapshot -r "${src}" "${dest}" >/dev/null; then
log_info "snap ${name} -> ${dest}"
else
log_err "snap failed for ${name}"
return 1
fi
}
prune_one() {
local subvol_dir="$1"
local count; count=$(find "${subvol_dir}" -maxdepth 1 -mindepth 1 -type d | wc -l)
if (( count <= KEEP )); then
return 0
fi
find "${subvol_dir}" -maxdepth 1 -mindepth 1 -type d \
| sort \
| head -n $((count - KEEP)) \
| while read -r old; do
log_info "prune ${old}"
btrfs subvolume delete "${old}" >/dev/null || log_err "prune failed ${old}"
done
}
main() {
if ! have_btrfs; then
log_err "btrfs-progs not installed"
exit 2
fi
install -d "${SNAP_ROOT}"
for sv in "${SUBVOLS[@]}"; do
snapshot_one "${sv}"
prune_one "${SNAP_ROOT}/$(basename "${sv}")"
done
}
main "$@"