diff options
author | Dave Reisner <d@falconindy.com> | 2010-01-07 23:02:24 -0500 |
---|---|---|
committer | Dave Reisner <d@falconindy.com> | 2010-01-07 23:02:24 -0500 |
commit | 68b1571b8c0deedf6366c24fabb69b4ed3b8348f (patch) | |
tree | 3603cec684ea0d263021fc5219025fa4335d008b | |
download | squashfu-68b1571b8c0deedf6366c24fabb69b4ed3b8348f.tar.gz |
Initial commit: extremely alpha
-rw-r--r-- | DOCUMENTATION | 41 | ||||
-rw-r--r-- | INSTALL | 10 | ||||
-rw-r--r-- | etc/squashfu | 62 | ||||
-rwxr-xr-x | squashfu | 141 |
4 files changed, 254 insertions, 0 deletions
diff --git a/DOCUMENTATION b/DOCUMENTATION new file mode 100644 index 0000000..af175b7 --- /dev/null +++ b/DOCUMENTATION @@ -0,0 +1,41 @@ +SquashFu - an alternative backup solution + Inspired by http://forums.gentoo.org/viewtopic-p-4732709.html + +Requirements: aufs, aufs2-util, squashfs-tools, rsync + +Goal: To create a backup solution which provides incremental backups and compression, + and which also provides an easy way to roll back. + +Design: + A directory structure is created as follows: + backup_root/ + |- seed.sfs + |- ro/ + |- rw/ + |- bins/ + |-1/ <-- Monday incremental + |-2/ <-- Tuesday incremental + |-3/ <-- Wednesday incremental + |-4/ <-- Thursday incremental + |-5/ <-- Friday incremental + |-6/ <-- Saturday incremental + |-7/ <-- Sunday incremental + + seed.sfs is created from an initial backup and compressed using SquashFS. It + is then mounted, using a loopback device on ro/. Using aufs2, a union mount is + formed by ro/ and each of the numbered bins, each corresponding to a day. + + At the time of the backup, the day is determined in order to prepare the union. If + today is Thursday, `date +%u` returns 4. In order for rsync to properly create our + incremental, we need to compare current data with the seed plus incrementals leading + up to Thursday, so we create our Aufs mount with the branches bins/4, bins/3, bins/2, + bins/1, and ro/ (in that order). When rsync now writes to the resulting union, aufs + happily receives the data into the first available (writable) branch of the union, + which is bins/4. The next day, the union mount is reformed adding the next bin... + + If and when you want to roll back, the process is simple. Mount the seed plus + the necessary bins to increment the seed up to the day of interest. + + + + @@ -0,0 +1,10 @@ +WARNING: EXTREMELY ALPHA. MAY SET YOUR CAT ON FIRE. + +If you don't care about your cat... +-Put etc/squashfu in /etc/squashfu +-Make a backup directory somewhere where you want your files +-Read over /etc/squashfu and set it the way you want +-Make a cron job to run this bad boy at the time you want +-Hang onto ya nuts. + +Some day this will be a real program. diff --git a/etc/squashfu b/etc/squashfu new file mode 100644 index 0000000..54c5706 --- /dev/null +++ b/etc/squashfu @@ -0,0 +1,62 @@ +# Config file for Super Deluxe SquashFu Backup Express + +# The base directory where backups will go. +# This is the only directory that absolutely needs to be +# created by the user. All others will be created as needed +# by the actual backup (and inside this root). +BKUP_ROOT="/mnt/Gluttony/squashup" + +# Filename for the seed generated. You shouldn't need +# to change this +SEED="${BKUP_ROOT}/$HOSTNAME-seed.sfs" + +# This is an optional offset for determining what bin is used +# for a nightly backup. If you run your backups in the early +# morning for the previous day's work, you'll want to set this +# to -1. Values other than 0 or -1 will cause bad things to +# happen. +# TODO: This can also be overridden with the --modifier option +# at runtime +MODIFIER=0 + +# These are the options that are passed directly to rsync. +# The -u flag is a necessity, or else incrementals will +# not be created properly. See 'man rsync' for more info. +RSYNC_OPTS=("-Rua" "--delete" "--stats") + +# This option determines whether the previous week's seed +# and bins are kept when creating a fresh seed for the next +# week. If this value is anything besides 1, the seed and bins +# will be discarded. If you have room for a second seed, +# keep this enabled until you are sure everything is working +# correctly. +KEEP_LAST_WEEK=1 + +# Set this to the day of the week you want a resquash to occur. +# Days are numbered according to the output of 'date +%u' -- 1 +# through 7, with 1 being Monday, 7 being Sunday. If this number +# is out of bounds or this day is missed, you MUST create the +# new squash manually using --resquash. This is again compared +# to the output of 'date +%u' but does NOT take into account +# the MODIFIER above. +RESQUASH_DAY=7 + + +# The following defines what will and won't be backed up. All lines +# MUST be commented, followed by a + for include, or a - for exclude. +# All other lines will be ignored (or things will break). + +#+/boot +#+/etc +#+/home +#+/root +#+/usr/share +#+/var/lib/pacman + +#-/boot/lost+found +#-/home/*/.cache +#-/home/*/.thumbnails +#-/home/*/.lyrics +#-/home/*/.gnome2 +#-/home/*/dev/git/*/.git +#-/home/lost+found diff --git a/squashfu b/squashfu new file mode 100755 index 0000000..7b42d46 --- /dev/null +++ b/squashfu @@ -0,0 +1,141 @@ +#!/bin/bash + +# Declare base options, overwrite with user specs as necessary +# MAKE SURE TO CHANGE THIS BEFORE PUSHING PUBLIC +CONFIG=/home/haruko/dev/git/squashfu/etc/squashfu +source $CONFIG +if [[ $? -gt 0 ]]; then + echo "FATAL: Error in config file. Please check your syntax" + exit 1 +fi + +# Informational output w/ happy colors +debug () { + echo -e '\033[1;33m??\033[1;m ' $* +} + +die () { + echo -e '\033[1;31m!!\033[1;m ' $* + exit 1 +} +mount_aufs_by_num () { + # Check for the union already being mounted + grep "${BKUP_ROOT}/rw" /proc/mounts >/dev/null && umount "$BKUP_ROOT/rw" + + # build branch string + branches="br=" + for i in `seq $1 -1 1`; do + branches="${branches}${BKUP_ROOT}/bins/${i}:" + done + branches="${branches}${BKUP_ROOT}/ro" + + # build and execute mount command + mount -t aufs none $BKUP_ROOT/rw -o udba=reval,$branches +} + +create_new_seed () { + # For our very first seed, we're writing directly to disk, so + # Delete the data after the squashed seed has been created + [[ "$1" == "--initial" ]] && run_rsync + + # Create a new squashfs based on the contents of the union + mksquashfs "${BKUP_ROOT}/rw" "$SEED" -b 65536 + + # Delete the rsync source since its now squashed + [[ "$1" == "--initial" ]] && rm -rf "${BKUP_ROOT}/rw/*" +} + +move_old_tree () { + storage="${BKUP_ROOT}/bkup-$(date +%Y-%m-%d)" + mkdir "$storage" + cd "$BKUP_ROOT" && mv {$SEED,bins/} "$storage" +} + +mount_seed () { + debug "Mounting seed" + # Mount the squashed seed, failing if we can't + mount -o loop,ro "${SEED}" "${BKUP_ROOT}/ro" || { + echo FATAL: Error mounting $SEED; + exit 1; + } +} + +mount_aufs_by_day() { + # convert DoW to a number + mount_aufs_by_num `date --date=$1 +%u` +} + +run_rsync() { + # Gather includes and excludes from config file + # No error checking here -- user better not have + # effed up the config + INCLUDES=($(grep ^#+ $CONFIG | cut -d+ -f2-)) + EXCLUDES=($(grep ^#- $CONFIG | cut -d- -f2-)) + + # rsync source to $BKUP_ROOT/rw + echo Rsync executing with: + echo Options: ${RSYNC_OPTS[@]} + echo Includes: ${INCLUDES[@]} + echo Excludes: ${EXCLUDES[@]} + rsync ${RSYNC_OPTS[@]} ${INCLUDES[@]} ${EXCLUDES[@]} ${BKUP_ROOT}/rw || return 1 +} + +# Unmount union and squash +unmount_all () { + #Union must be unmounted first, or bad things happen + umount "${BKUP_ROOT}/rw" + umount "$SEED" +} + +# Sanity checks +# - Are we root? +[[ $UID -eq 0 ]] || { echo "Must be root!"; exit 1; } + +# - is our BKUP_ROOT valid? (FAIL) +[[ -w "${BKUP_ROOT}" ]] || + { echo "FATAL: Backup root '$BKUP_ROOT' is not a valid location!"; + echo "Please check the setting in /etc/squashfu"; exit 1; } + +# Blindly unmount all just in case +unmount_all + +# - do we have a proper (expected) directory structure in place? +# Use cd to BKUP_ROOT to avoid issues with brace expansion in a quoted path +cd "$BKUP_ROOT" && mkdir -p {rw,ro,bins/{1,2,3,4,5,6,7}} + +# Prep work +# - does seed exist? (if not, our backup is creating the seed) +[[ -f "$SEED" ]] || { + echo "No seed found -- creating a new one..."; + create_new_seed "--initial"; +} +# mount seed if it exists and is not already mounted +grep "${BKUP_ROOT}/ro" /proc/mounts >/dev/null || mount_seed + +# Prepare union mount with proper bins +mount_aufs_by_num $(( $(date +%u) + $MODIFIER )) + +# Ready for backup! +run_rsync + +# 5) Cleanup +# - Is this resquash day? If so, we need a new squash +# - If new squash creation fails, we're in trouble. (by default, keep previous week) +[[ $(date +%u) -eq $RESQUASH_DAY ]] && { + create_new_seed + # Set aside last week's tree if user opted to, else delete it all + if [[ $KEEP_LAST_WEEK -eq 1 ]]; then + move_old_tree + else + find "${BKUP_ROOT}/bins/" -type f -delete + rm $SEED + fi +} + +unmount_all + +# 6) Optional behavior +# --seed-initial Create new seed +# --rollback $1 $2 Rollback to the day specified by $1, mounting at $2 +# --resquash Create a new seed +# |