1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
|
#!/usr/bin/env bash
#
# ZFS backup script.
#
# Creates temporary ZFS snapshots, and 'zfs sends' them (or differences
# between them and previous snapshots) down fifo nodes in
# /etc/burp/fifos.
#
# On success, the ZFS script renames the temporary snapshot
# to a more permanent name so that the snapshots can be tracked consistently.
#
# To use, put something like this in your burp.conf.
# You can add multiple pools.
#
# backup_script=/etc/burp/zfs_script
# backup_script_arg=7
# backup_script_arg=rpool/export/home@burp
# backup_script_arg=rpool/ROOT/s10x_u9wos_14a/var@burp
# backup_script_post_run_on_fail=1
#
# A snapshot numbered between 1 to $MAX_SNAPS is created on a backup. The
# number is incremented for each backup. Number 1 is always a full ZFS snapshot.
# The rest are incrementals based on the previous number. After $MAX_SNAPS,
# the sequence starts from 1 again.
#
# You will want to configure the burp server to keep enough backups so that
# a ZFS 'full' snapshot is always available. For example, if you set $MAX_SNAPS
# to 7, you will want to keep at least 14 backups on the server to guarantee
# that you can always restore at least 7 backups into the past.
#
# To do a restore, you will need to do this (for example), starting from number
# 1:
# mkfifo /etc/burp/fifos/rpool/export/home@burp<number>
# cat /etc/burp/fifos/rpool/export/home@burp<number> | zfs recv <zfs recv args>
#
# Then repeat, each time incrementing the number until you reach the backup
# that you want to end up on.
if [ $# -lt 7 ] ; then
echo "$0: not enough arguments" 1>&2
exit 1
fi
MODE="$1" ; shift
FAILED="$1" ; shift; shift; shift; shift
MAX_SNAPS="$1" ; shift
if [ "$MODE" = "pre" ] ; then
declare -a arr=($@)
# Set up the fifo directory
PIPE_DIR=/etc/burp/fifos
rm -rf "$PIPE_DIR" || exit 1
mkdir -p "$PIPE_DIR" || exit 1
# Create all the snapshots that we will need.
a=0
while [ $a -lt $# ] ; do
SNAPSHOT="${arr[$a]}"
SNAPSHOT_TMP="$SNAPSHOT"tmp
# Find out how many snapshots already exist.
inc=1
while [ "$inc" -le $MAX_SNAPS ] ; do
/usr/sbin/zfs list "$SNAPSHOT$inc" >/dev/null 2>&1 || break
inc=$((inc+1))
done
if [ "$inc" -gt $MAX_SNAPS ] ; then
# Had enough snapshots now. Start from 1 again.
inc=1
fi
# Destroy all the unneeded snapshots
i=$inc
while [ "$i" -le $MAX_SNAPS ] ; do
/usr/sbin/zfs destroy "$SNAPSHOT$i" >/dev/null 2>&1
/usr/sbin/zfs destroy "$SNAPSHOT_TMP$i" >/dev/null 2>&1
i=$((i+1))
done
# Create the new snapshot.
/usr/sbin/zfs snapshot -r "$SNAPSHOT_TMP$inc" || exit 1
# Create a pipe.
ZFS_PIPE=$PIPE_DIR/$SNAPSHOT$inc
mkdir -p "${ZFS_PIPE%/*}"
mkfifo "$ZFS_PIPE" || exit 1
chmod 777 "$ZFS_PIPE" || exit 1
a=$((a+1))
done
# Need to exec here, otherwise burp will wait forever for this script to
# return.
exec > /dev/null 2>&1
# Now, go through the list and do zfs sends for all the new snapshots.
a=0
while [ $a -lt $# ] ; do
SNAPSHOT="${arr[$a]}"
SNAPSHOT_TMP="${arr[$a]}tmp"
# Figure out the most recent snapshot again.
inc=1
while [ "$inc" -le $MAX_SNAPS ] ; do
/usr/sbin/zfs list "$SNAPSHOT$inc" >/dev/null 2>&1 || break
inc=$((inc+1))
done
ZFS_PIPE=$PIPE_DIR/$SNAPSHOT$inc
if [ "$inc" = 1 ] ; then
# First in the chain. Just do the full zfs send.
/usr/sbin/zfs send "$SNAPSHOT_TMP$inc" > "$ZFS_PIPE" &
else
# Not the first in the chain. Do an incremental zfs send.
/usr/sbin/zfs send -i "$SNAPSHOT$((inc-1))" "$SNAPSHOT_TMP$inc" > "$ZFS_PIPE" &
fi
a=$((a+1))
done
exit 0
elif [ "$MODE" = "post" ] ; then
while [ "$#" -gt 0 ] ; do
SNAPSHOT=$1 ; shift
SNAPSHOT_TMP="$SNAPSHOT"tmp
i=1
if [ "$FAILED" = "1" ] ; then
while [ "$i" -le $MAX_SNAPS ] ; do
/usr/sbin/zfs destroy "$SNAPSHOT_TMP$i" >/dev/null 2>&1
i=$((i+1))
done
else
while [ "$i" -le $MAX_SNAPS ] ; do
/usr/sbin/zfs list "$SNAPSHOT_TMP$i" >/dev/null 2>&1 && \
/usr/sbin/zfs rename "$SNAPSHOT_TMP$i" "$SNAPSHOT$i"
i=$((i+1))
done
fi
done
exit 0
else
echo "$0: unknown mode: $MODE" 1>&2
exit 1
fi
|