#!/bin/bash -e if test -e /etc/btrfs-snapshot.conf; then . /etc/btrfs-snapshot.conf fi if test -e ~/.btrfs-snapshot; then . ~/.btrfs-snapshot fi HOURLY_DEL=25 DAILY_DEL=8 WEEKLY_DEL=5 MONTHLY_DEL= BTRFS_VOLUMES=${BTRFS_VOLUMES:-$(awk '!/^#/ && $3=="btrfs" {print $2}' /etc/fstab | tr '\n' ' ' | sed 's, $,,')} TMP_MNT=${TMP_MNT:-/var/tmp/btrfs-backup} vols=() del=${DEL} dryrun=0 periodity=${0%/*} periodity=${periodity##*/} if [[ $periodity =~ ^cron\... ]]; then periodity=${periodity#cron} else periodity= fi case "$periodity" in (.hourly) del=${HOURLY_DEL};; (.daily) del=${DAILY_DEL};; (.weekly) del=${WEEKLY_DEL};; (.monthly) del=${MONTHLY_DEL};; esac while test $# -gt 0; do case "$1" in (-h|--help) cat < add a path that contains a btrfs (sub-) volume (defaults to: ${BTRFS_VOLUMES}) -m, --mnt temporary mount point (default: ${TMP_MNT}) not yet implemented: -d, --del delete oldes snapshots beginning at -th ENVIRONMENT BTRFS_VOLUMES specify all volume pathes (instead of -p) TMP_MNT temporary mount point (instead of -m) DESCRIPTION Creates a snapshot for all btrfs volumes specified. Snapshot is named from the subvol name or if there is no subvol, from the path by appending -snapshot-YYYY-MM-DD-HH-mm. To create regular snapshots on a daily base, just run: sudo cp btrfs-snapshots.sh /etc/cron.daily/btrfs-snapshots If btrfs-snapshots is run from a cron.daily, cron.hourly, cron. monthly or cron.weekly directory, the periodity is automatically appended to the snapshot name, and the expiry is set meaningfull: - hourly → keep only 24 hours, --del 25 - daily → keep only 7 days, --del 8 - weekly → keep only 4 weeks, --del 5 - monthly → old snapshots are not deleted EOF exit;; (-n|--dry-run) dryrun=1;; (-p|--path) shift; vols+=( "$1" );; (-m|--mnt) shift; TMP_MNT="$1";; (-d|--del) shift; del="$1";; (*) echo "ERROR: unknown option $1, try $0 --help" 1>&2 exit 1;; esac if test $# -lt 1; then echo "ERROR: missing argument, try $0 --help" 1>&2 exit 1 fi shift done if test -n "${vols[*]}"; then BTRFS_VOLUMES="${vols[*]}" fi test -d "${TMP_MNT}" || mkdir -p "${TMP_MNT}" for fs in ${BTRFS_VOLUMES}; do device=$(mount | awk '$3=="'$fs'" && $5=="btrfs" {print $1}') subvol=$(mount | awk '$3=="'$fs'" && $5=="btrfs" {split(substr($6, 2, length($6)-2), res, ","); for (r in res) {if (res[r]~/^subvol=/) {sub(/^subvol=/,"" , res[r]); print res[r]}}}') if test -n "$subvol"; then if test "$subvol" = "/@"; then name=${subvol} else name=${subvol}- fi else if test "$fs" = "/"; then name="/" else name=${fs#/} name=${name%/} name=/${name//\//-}- fi fi date="-$(date +%Y-%m-%d-%H-%M)" target="${name}snapshot${periodity}" if test -n "$device"; then if mount | grep -q " on $TMP_MNT type "; then sudo umount "$TMP_MNT" fi sudo mount "$device" "$TMP_MNT" if test $dryrun -eq 1; then echo -e "→ \e[1mbackup ${subvol:+subvol $subvol of }$fs on $device\e[0m" echo " " mount "$device" "$TMP_MNT" echo " " btrfs subvolume snapshot "${TMP_MNT}${subvol}" "${TMP_MNT}${target}${date}" if test -n "$del"; then for f in $(ls -d1 "${TMP_MNT}${target}"-* | sort -r | tail -n +"$del"); do echo " " btrfs subvolume delete "$f" done fi echo " " umount "$TMP_MNT" else sudo btrfs subvolume snapshot "${TMP_MNT}${subvol}" "${TMP_MNT}${target}${date}" if test -n "$del"; then for f in $(ls -d1 "${TMP_MNT}${target}"-* | sort -r | tail -n +"$del"); do sudo btrfs subvolume delete "$f" done fi fi sudo umount "$TMP_MNT" else echo "ERROR: no device found for $fs" 1>&2 exit 2 fi done