Skip to content

Instantly share code, notes, and snippets.

@bbrala
Created January 27, 2017 12:18
Show Gist options
  • Save bbrala/1866ad499a08aecaf1be67b1c3150574 to your computer and use it in GitHub Desktop.
Save bbrala/1866ad499a08aecaf1be67b1c3150574 to your computer and use it in GitHub Desktop.
rsyc backup using hardlinks
#!/bin/sh
#
# Based on http://www.noah.org/engineering/src/shell/rsync_backup
# Retention count 21 for making 3 backups each day for a week.
#
# This maintains a rotating backup. This will normalize permissions on
# all files and directories on backups. It has happened that someone removed
# owner write permissions on some files, thus breaking the backup process. This
# prevents that from happening. All this permission changing it tedious, but it
# eliminates any doubts. I could have done this with "chmod -R +X", but I
# wanted to explicitly set the permission bits.
#
# Pass two arguments: rsync_backup SOURCE_PATH BACKUP_PATH
#
# $Id: rsync_backup 222 2016-01-21 22:05:30Z noah $
usage() {
echo "usage: rsync_backup [-v] [-n] SOURCE_PATH BACKUP_PATH"
echo " SOURCE_PATH and BACKUP_PATH may be ssh-style remote paths; although,"
echo " BACKUP_PATH is usually a local directory where you want the"
echo " backup set stored."
echo " -v : set verbose mode"
echo " -n : normalize directory and file permissions to 755 for dirs and 644 for files."
}
# How many folders to rotate. 21 for 3 backups a day
RETENTION_COUNT=21
VERBOSE=0
NORMALIZE_PERMS=0
while getopts ":vnh" options; do
echo $options
case $options in
v ) VERBOSE=1;;
n ) NORMALIZE_PERMS=1;;
h ) usage
exit 1;;
\? ) usage
exit 1;;
* ) usage
exit 1;;
esac
done
shift $(($OPTIND - 1))
SOURCE_PATH=$1
BACKUP_PATH=$2
if [ -z $SOURCE_PATH ] ; then
echo "Missing argument. Give source path and backup path."
usage
exit 1
fi
if [ -z $BACKUP_PATH ] ; then
echo "Missing argument. Give source path and backup path."
usage
exit 1
fi
SOURCE_BASE=`basename $SOURCE_PATH`
PERMS_DIR=755
PERMS_FILE=644
if [ $VERBOSE ]; then
RSYNC_OPTS="-a --delete -v"
date
else
RSYNC_OPTS="-a --delete -q"
fi
if [ ! -d $BACKUP_PATH ] ; then
mkdir $BACKUP_PATH
fi
COUNTER=$RETENTION_COUNT
while [ $COUNTER -ge 0 ]
do
if [ ! -d $BACKUP_PATH/$SOURCE_BASE.$COUNTER ] ; then
mkdir $BACKUP_PATH/$SOURCE_BASE.$COUNTER
fi
COUNTER=$(( $COUNTER - 1 ))
done
# TODO All these find operations to clean up permissions is going to add a lot
# of overhead as the backup set gets bigger. At 100 GB it's not a big deal. The
# correct thing would be to have an exception based system where I correct
# permissions when/if they cause a problem.
# Rotate backups.
if [ $NORMALIZE_PERMS -eq 1 ]; then
if [ $VERBOSE ]; then
echo "Normalizing file permissions."
fi
find $BACKUP_PATH/$SOURCE_BASE.21 -type d -exec chmod $PERMS_DIR {} \;
find $BACKUP_PATH/$SOURCE_BASE.21 -type f -exec chmod $PERMS_FILE {} \;
fi
rm -rf $BACKUP_PATH/$SOURCE_BASE.$RETENTION_COUNT
COUNTER=$(( $RETENTION_COUNT - 1 ))
while [ $COUNTER -gt 1 ]
do
PREVIOUS=$(( $COUNTER - 1 ))
mv $BACKUP_PATH/$SOURCE_BASE.$PREVIOUS $BACKUP_PATH/$SOURCE_BASE.$COUNTER
COUNTER=$(( $COUNTER - 1 ))
done
cp -al $BACKUP_PATH/$SOURCE_BASE.0 $BACKUP_PATH/$SOURCE_BASE.1
# Backup.
if [ $NORMALIZE_PERMS -eq 1]; then
if [ $VERBOSE -eq 1 ]; then
echo "Normalizing file permissions."
fi
find $BACKUP_PATH/$SOURCE_BASE.0 -type d -exec chmod $PERMS_DIR {} \;
find $BACKUP_PATH/$SOURCE_BASE.0 -type f -exec chmod $PERMS_FILE {} \;
fi
rsync $RSYNC_OPTS $SOURCE_PATH/. $BACKUP_PATH/$SOURCE_BASE.0/.
RSYNC_EXIT_STATUS=$?
if [ $NORMALIZE_PERMS -eq 1 ]; then
if [ $VERBOSE -eq 1 ]; then
echo "Normalizing file permissions."
fi
find $BACKUP_PATH/$SOURCE_BASE.0 -type d -exec chmod $PERMS_DIR {} \;
find $BACKUP_PATH/$SOURCE_BASE.0 -type f -exec chmod $PERMS_FILE {} \;
fi
# Ignore error code 24, "rsync warning: some files vanished before they could be transferred".
if [ $RSYNC_EXIT_STATUS -eq 24 ] ; then
RSYNC_EXIT_STATUS=0
fi
# Create a timestamp file to show when backup process completed successfully.
if [ $RSYNC_EXIT_STATUS -eq 0 ] ; then
rm -f $BACKUP_PATH/$SOURCE_BASE.0/BACKUP_ERROR
date > $BACKUP_PATH/$SOURCE_BASE.0/BACKUP_TIMESTAMP
else # Create a timestamp if there was an error.
rm -f $BACKUP_PATH/$SOURCE_BASE.0/BACKUP_TIMESTAMP
echo "rsync failed" > $BACKUP_PATH/$SOURCE_BASE.0/BACKUP_ERROR
date >> $BACKUP_PATH/$SOURCE_BASE.0/BACKUP_ERROR
echo $RSYNC_EXIT_STATUS >> $BACKUP_PATH/$SOURCE_BASE.0/BACKUP_ERROR
fi
if [ $VERBOSE -eq 1 ]; then
date
fi
exit $RSYNC_EXIT_STATUS
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment