#!/usr/bin/env bash set -euo pipefail # Creates btrfs subvolume snapshots of /, /home, /opt into /.snapshots/ # Names: root-YYYY-MM-DD, home-YYYY-MM-DD, opt-YYYY-MM-DD # If a snapshot with the same name exists, it is rotated to -old; existing -old is deleted first. SNAPDIR="/.snapshots" DATE="$(date +%F)" declare -A SRC=( [root]="/" [home]="/home" [opt]="/opt" ) need_root() { if [[ "${EUID:-$(id -u)}" -ne 0 ]]; then echo "ERROR: must run as root" >&2 exit 1 fi } ensure_snapdir() { if [[ ! -d "$SNAPDIR" ]]; then mkdir -p "$SNAPDIR" fi } is_btrfs_subvol() { local p="$1" btrfs subvolume show "$p" >/dev/null 2>&1 } rotate_if_exists() { local base="$1" local cur="${SNAPDIR}/${base}-${DATE}" local old="${cur}-old" if is_btrfs_subvol "$cur"; then if is_btrfs_subvol "$old"; then btrfs subvolume delete "$old" elif [[ -e "$old" ]]; then # Path exists but is not a btrfs subvolume (unexpected) — remove it rm -rf --one-file-system "$old" fi # Rename current snapshot to -old mv -T "$cur" "$old" fi } create_snapshot() { local base="$1" local src="${SRC[$base]}" local dst="${SNAPDIR}/${base}-${DATE}" # Ensure destination doesn't exist (could happen if mv failed or something created it) if is_btrfs_subvol "$dst"; then echo "ERROR: destination subvolume already exists: $dst" >&2 exit 1 elif [[ -e "$dst" ]]; then echo "ERROR: destination path exists and is not a subvolume: $dst" >&2 exit 1 fi btrfs subvolume snapshot "$src" "$dst" } main() { need_root ensure_snapdir # Rotate existing snapshots (if any) for base in root home opt; do rotate_if_exists "$base" done # Create new snapshots for base in root home opt; do create_snapshot "$base" done } main "$@"