You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
159 lines
4.8 KiB
159 lines
4.8 KiB
#!/bin/bash -e |
|
|
|
# defaults |
|
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} |
|
|
|
# overwrite defaults in configs |
|
if test -e /etc/btrfs-snapshots.conf; then |
|
. /etc/btrfs-snapshots.conf |
|
fi |
|
if test -e ~/.btrfs-snapshots; then |
|
. ~/.btrfs-snapshots |
|
fi |
|
|
|
# evaluate commandline |
|
vols=() |
|
del=${DEL} |
|
dryrun=0 |
|
fast=0 |
|
periodity=${0%/*} |
|
periodity=${periodity##*/} |
|
if [[ $periodity =~ ^cron\... ]]; then |
|
periodity=${periodity#cron} |
|
else |
|
periodity= |
|
fi |
|
|
|
case "$periodity" in |
|
(.hourly) del=${HOURLY_DEL}; fast=1;; |
|
(.daily) del=${DAILY_DEL};; |
|
(.weekly) del=${WEEKLY_DEL}; fast=1;; |
|
(.monthly) del=${MONTHLY_DEL}; fast=1;; |
|
esac |
|
while test $# -gt 0; do |
|
case "$1" in |
|
(-h|--help) cat <<EOF |
|
$0 [OPTIONS] |
|
|
|
OPTIONS |
|
|
|
-h, --help show this help |
|
-n, --dry-run execute dry run, do not backup, just show commands |
|
-f, --fast fast run, do not optimize btrfs (otherwise done daily) |
|
-p, --path <path> add a path that contains a btrfs (sub-) volume |
|
(defaults to: ${BTRFS_VOLUMES}) |
|
-m, --mnt <path> temporary mount point (default: ${TMP_MNT}) |
|
|
|
not yet implemented: |
|
|
|
-d, --del <number> delete oldes snapshots beginning at <numbers>-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;; |
|
(-f|--fast) fast=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" |
|
if test $fast -eq 0; then |
|
echo " " btrfs filesystem defragment ${fs} |
|
echo " " btrfs balance start ${fs} |
|
fi |
|
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" |
|
if test $fast -eq 0; then |
|
echo "Defragment subvolume: '${fs}'" |
|
sudo btrfs filesystem defragment ${fs} |
|
echo "Rebalance subvolume: '${fs}'" |
|
sudo btrfs balance start ${fs} |
|
fi |
|
else |
|
echo "ERROR: no device found for $fs" 1>&2 |
|
exit 2 |
|
fi |
|
done
|
|
|