Skip to content

Instantly share code, notes, and snippets.

@elundmark
Created October 27, 2013 14:46
Show Gist options
  • Save elundmark/7183083 to your computer and use it in GitHub Desktop.
Save elundmark/7183083 to your computer and use it in GitHub Desktop.
#!/bin/sh
#
# This maintains a one week 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 2008-02-21 22:05:30Z noah $
usage() {
echo "usage: rsync_backup [-v] [-n] SOURCE_PATH BACKUP_PATH [[DIR_LIST_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."
}
VERBOSE=0
NORMALIZE_PERMS=0
while getopts ":vnh" options; do
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 [ $3 ] && [ -f $3 ] ; then
DIR_LIST=$3
elif [ ! $3 ] || [ ! -f $3 ] ; then
echo "$3 is not a file"
exit 1
fi
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 -eq 1 ]; then
if [ $DIR_LIST ] && [ -f $DIR_LIST ] ; then
# having files-from needs -r set
echo "Using --files-from $DIR_LIST with $SOURCE_BASE as the top path"
RSYNC_OPTS="-a --recursive --delete -v --files-from=$DIR_LIST"
else
# -a includes -r
RSYNC_OPTS="-a --delete -v"
fi
date
else
if [ $DIR_LIST ] && [ -f $DIR_LIST ] ; then
# having files-from needs -r set
RSYNC_OPTS="-a --recursive --delete -q --files-from=$DIR_LIST"
else
# -a includes -r
RSYNC_OPTS="-a --delete -q"
fi
fi
# Create the rotation directories if they don't exist.
if [ ! -d $BACKUP_PATH ] ; then
mkdir $BACKUP_PATH
fi
if [ ! -d $BACKUP_PATH/$SOURCE_BASE.0 ] ; then
mkdir $BACKUP_PATH/$SOURCE_BASE.0
fi
if [ ! -d $BACKUP_PATH/$SOURCE_BASE.1 ] ; then
mkdir $BACKUP_PATH/$SOURCE_BASE.1
fi
if [ ! -d $BACKUP_PATH/$SOURCE_BASE.2 ] ; then
mkdir $BACKUP_PATH/$SOURCE_BASE.2
fi
if [ ! -d $BACKUP_PATH/$SOURCE_BASE.3 ] ; then
mkdir $BACKUP_PATH/$SOURCE_BASE.3
fi
if [ ! -d $BACKUP_PATH/$SOURCE_BASE.4 ] ; then
mkdir $BACKUP_PATH/$SOURCE_BASE.4
fi
if [ ! -d $BACKUP_PATH/$SOURCE_BASE.5 ] ; then
mkdir $BACKUP_PATH/$SOURCE_BASE.5
fi
if [ ! -d $BACKUP_PATH/$SOURCE_BASE.6 ] ; then
mkdir $BACKUP_PATH/$SOURCE_BASE.6
fi
if [ ! -d $BACKUP_PATH/$SOURCE_BASE.7 ] ; then
mkdir $BACKUP_PATH/$SOURCE_BASE.7
fi
if [ ! -d $BACKUP_PATH/$SOURCE_BASE.8 ] ; then
mkdir $BACKUP_PATH/$SOURCE_BASE.8
fi
if [ ! -d $BACKUP_PATH/$SOURCE_BASE.9 ] ; then
mkdir $BACKUP_PATH/$SOURCE_BASE.9
fi
# 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 -eq 1 ]; then
echo "Normalizing file permissions."
fi
find $BACKUP_PATH/$SOURCE_BASE.9 -type d -exec chmod $PERMS_DIR {} \;
find $BACKUP_PATH/$SOURCE_BASE.9 -type f -exec chmod $PERMS_FILE {} \;
fi
rm -rf $BACKUP_PATH/$SOURCE_BASE.9
mv $BACKUP_PATH/$SOURCE_BASE.8 $BACKUP_PATH/$SOURCE_BASE.9
mv $BACKUP_PATH/$SOURCE_BASE.7 $BACKUP_PATH/$SOURCE_BASE.8
mv $BACKUP_PATH/$SOURCE_BASE.6 $BACKUP_PATH/$SOURCE_BASE.7
mv $BACKUP_PATH/$SOURCE_BASE.5 $BACKUP_PATH/$SOURCE_BASE.6
mv $BACKUP_PATH/$SOURCE_BASE.4 $BACKUP_PATH/$SOURCE_BASE.5
mv $BACKUP_PATH/$SOURCE_BASE.3 $BACKUP_PATH/$SOURCE_BASE.4
mv $BACKUP_PATH/$SOURCE_BASE.2 $BACKUP_PATH/$SOURCE_BASE.3
mv $BACKUP_PATH/$SOURCE_BASE.1 $BACKUP_PATH/$SOURCE_BASE.2
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 = 24 ] ; then
RSYNC_EXIT_STATUS=0
fi
# Create a timestamp file to show when backup process completed successfully.
if [ $RSYNC_EXIT_STATUS = 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