#!/bin/bash set -e -o pipefail mkdir -p /backups chmod 750 /backups chown root:sudo /backups mkdir -p /snapshots chmod 700 /snapshots chown root:root /snapshots name="" clone_sources=() while (( $# > 0 )); do case "$1" in -n) name="$2" shift ;; -c) clone_sources+=(-c "$2") shift ;; *) cat <&2 usage: sudo $0 [-n ] [-c ] creates a encrypted backup of the btrfs subvolume mounted at '/' and uses all the '-c' options as clone source snapshots to reduce the size of the final file. see 'man btrfs-send' for details of how '-c' works. EOF exit 1 ;; esac shift done if [[ -n "$name" ]]; then name="-$name" fi name="snap-$(date --iso-8601=date -u)$name" echo "final base name: '$name'" if [[ "$1" =~ ^'-' ]]; then cat <<'EOF' >&2 usage: sudo ./backup.sh [] creates a encrypted backup of the btrfs subvolume mounted at `/` EOF exit 1 fi echo "creating btrfs snapshot with name: '$name'" zst_name="$name.btrfs.zst" out_name="/backups/$zst_name.gpg" prompt=0 if [[ -d /snapshots/"$name" ]]; then prompt=1 echo "snapshot already exists: /snapshots/$name" >&2 fi if [[ -f "$out_name" ]]; then prompt=1 echo "output file already exists: $out_name" >&2 fi mapfile -t existing_backups < <(find /backups/ -maxdepth 1 -name 'snap-*.btrfs.zst.gpg' -print) for existing_backup in "${existing_backups[@]}"; do if [[ "$existing_backup" != "$out_name" ]]; then echo "other backup files exist, you should copy them to a remote system and then delete them." >&2 echo "If you don't delete them, they will be included in this backup, using waay more space than necessary." >&2 printf "e.g., run: sudo rm %q\n" "$existing_backup" >&2 exit 1 fi done if ((prompt)); then if read -r -p "snapshot and/or output file already exists, do you want to overwrite them? [y/n]: " -t 300 REPLY && [[ "$REPLY" =~ ^[yY]$ ]]; then btrfs subvolume delete /snapshots/"$name" rm -f "$out_name" else echo "exiting" >&2 exit 1 fi fi btrfs subvolume snapshot -r / /snapshots/"$name" echo "encrypting and writing to: $out_name" exec 3<&0 btrfs send "${clone_sources[@]}" /snapshots/"$name" | zstd --fast -T0 | gpg --symmetric --compress-algo none --set-filename "$zst_name" -o- /dev/fd/4 4<&0- <&3 > "$out_name" echo "written to: $out_name"