Skip to content

Instantly share code, notes, and snippets.

@sarva
Created May 13, 2011 09:37
Show Gist options
  • Save sarva/970260 to your computer and use it in GitHub Desktop.
Save sarva/970260 to your computer and use it in GitHub Desktop.
Incremental BTRFS backups
#!/bin/bash
#
# Sync data using rsync to a backup BTRFS fs subvolume and create
# incremental revolving snapshots
#
# Setting the -c option will setup crontab to continue running the
# backup at the set granularity (default daily)
#
# Install:
#
# cd /usr/local/bin
# wget https://gist.github.com/raw/970260/btrfs-backup.sh
# chmod 755 btrfs-backup.sh
#
# Show usage:
#
# btrfs-backup.sh -h
#
usage {
cat << EOF
usage: $0 [options] /path/to/backup /path/to/btrfs/volume subvol-name
Run / setup incremental backups to a BTRFS volume
OPTIONS:
-h Show this message
-g Granularity for increments. OPTIONS: hour, day, week, month DEFAULT: day. Use with -c will setup an appropriate crontab
-i Number of increments to save. DEFAULT: 7. -g hour -i 24 would save 24 hourly snapshots before overwriting old snapshots
-c Setup crontab job with current settings
-l Log file to append backup/snapshot log to.
-s Sleep mode. Unmount and put the BTRFS device to sleep after backup. Requires the volume to be setup in /etc/fstab
-v Verbose rsync
EOF
}
DX=day
INC=7
CRON=false
SUBVOL=
LOG=
SLEEP=
VERBOSE=">/dev/null"
while getopts “hcvsg:i:l:” OPTION
do
case $OPTION in
h)
usage
exit 1
;;
c)
CRON=true
;;
v)
VERBOSE=""
;;
s)
SLEEP=true
;;
g)
DX=$OPTARG
;;
i)
INC=$OPTARG
;;
l)
LOG=$OPTARG
;;
?)
usage
exit
;;
esac
done
if [ -z "$1" ] || [ -z "$2" ] || [ -z "$3" ]; then
usage
exit 1
fi
function snapshot {
# delete old snapshot
if [ -d $1/$2-$3 ]
then
btrfsctl -D $2-$3 $1 $VERBOSE
fi
btrfsctl -s $1/$2-$3 $1/$2 $VERBOSE
if [ ! -z "$LOG" ]; then
echo "snapshot $2-$3: `date`" >> $LOG
fi
}
# find the appropriate absolute timestamp depending on granularity. day=daystamp, hour=hourstamp etc
# timestamp forever increase with time so are useful for ensuring increments can loop over each other
case $DX in
hour)
dx=60*60
;;
day)
dx=60*60*24
;;
week)
dx=60*60*24*7
;;
?)
echo "Invalid granularity, only hour, day, week are supported"
exit 1
;;
esac
# If sleep is specified, try and mount the volume first.
# Otherwise it should already be mounted
$SLEEP &&
#setup sleep
(mount $2 &&
DEV=`mount | grep "$2 type btrfs" | awk '{print $1}'`) ||
# without sleep
(test -d $2/$3 &&
mount | grep "$2 type btrfs" >/dev/null)
if [ $? -ne 1 ]; then
$SLEEP &&
echo "Unable to mount BTRFS volume or it does not appear to be BTRFS. Make sure it is setup in /etc/fstab" ||
echo "$2 does not appear to be a mounted BTRFS volume. It must be the parent BTRFS volume and not a subvolume. Use -s to set subvolumes"
exit 1
fi
if [ ! -d $1 ] && [ ! -f $1 ]; then
echo "$1 does not exist"
fi
# rsync into BTRFS volume
rsync -va --progress --numeric-ids --delete-before --ignore-errors --partial --inplace --sparse $1/ $2/${3/%\//} $VERBOSE
# the granularity-adjusted timestamp value
((ts=`date +%s`/$dx))
# snapshot dir
# TODO provide option to save snapshots into a different directory
((num=$ts%$INC))
snapshot $2 $3 $num
# unmount and put to sleep
$SLEEP &&
umount $2 $&&
hdparm -Y $DEV $VERBOSE
# setup a crontab entry for this backup job
if $CRON; then
[ $DX == "hour" ] && HOUR="*" || HOUR="0"
[ $DX == "week" ] && WEEK="0" || WEEK="*"
CMD="$0 -g $DX -i $INC `[ ! -z "$LOG ] && echo "-l $LOG"` `$SLEEP && echo "-s"` $1 $2 $3"
echo "`crontab -l`"$'\n'"0 $HOUR * * $WEEK $CMD" | crontab
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment