Files
dsnap-sync/bin/snap-sync
James Barnett 78bc6ff8b9 If backupdir key is missing, use root dir to mounted disk
The user can hit enter when creating a new directory (no directory is
created). In doing so "backupdir" is set to nothing. Snapper does not
keep that key. Upon a search of the latest incremental backup we didn't
check for backupdir, we just had taken the first key (which ended up
being the UUID ). We now check for the backupdir key, and if it is
missing that means use the root directory on the mounted disk.

Fixes #13.
2016-11-11 15:05:12 -06:00

204 lines
6.6 KiB
Bash
Executable File

#!/bin/bash
# James W. Barnett
# Takes snapshots of each snapper configuration. It then sends the snapshot to
# a location on an external drive. After the initial transfer, it does
# incremental snapshots on later calls. It's important not to delete the
# snapshot created on your system since that will be used to determine the
# difference for the next incremental snapshot.
version="0.2"
set -e
while [[ $# -gt 0 ]]; do
key="$1"
case $key in
-d|--description)
description="$2"
shift 2
;;
-c|--config)
selected_configs="$2"
shift 2
;;
-u|--UUID)
uuid_cmdline="$2"
shift 2
;;
-n|--noconfirm)
noconfirm="yes"
shift
;;
-h|--help)
printf "snap-sync $version\n\n"
printf "Usage: snap-sync [options]\n\n"
printf "Options:\n"
printf " -d, --description <desc> Change the snapper description. Default: \"latest incremental backup\"\n"
printf " -c, --config <config> Specify the snapper configuration to use. Otherwise will perform for each snapper\n"
printf " configuration. Can list multiple configurations within quotes, space-separated\n"
printf " (e.g. -c \"root home\").\n"
printf " -n, --noconfirm Do not ask for confirmation for each configuration. Will still prompt for backup\n"
printf " directory name on first backup\n"
printf " -u, --UUID <UUID> Specify the UUID of the mounted BTRFS subvolume to back up to. Otherwise will prompt.\n"
printf " If multiple mount points are found with the same UUID, will prompt user.\n"
exit 1
;;
*)
printf "Error: Unknown option: $key\n"
printf "Run 'snap-sync -h' for valid options.\n"
exit 1
;;
esac
done
description=${description:-"latest incremental backup"}
uuid_cmdline=${uuid_cmdline:-"none"}
noconfirm=${noconfirm:-"no"}
if [[ $EUID -ne 0 ]]; then
printf "Script must be run as root.\n"
exit
fi
# It's important not to change this userdata in the snapshots, since that's how
# we find the previous one.
TARGETS="$(findmnt -n -v -t btrfs -o TARGET --list)"
UUIDS="$(findmnt -n -v -t btrfs -o UUID --list)"
declare -a TARGETS_ARRAY
declare -a UUIDS_ARRAY
i=0
disk=-1
disk_count=0
for x in $UUIDS; do
UUIDS_ARRAY[$i]=$x
if [[ "$x" == "$uuid_cmdline" ]]; then
disk=$i
disk_count=$(($disk_count+1))
fi
i=$((i+1))
done
i=0
for x in $TARGETS; do
TARGETS_ARRAY[$i]=$x
i=$((i+1))
done
if [[ "$disk_count" > 1 ]]; then
printf "Multiple mount points were found with UUID $uuid_cmdline.\n"
disk="-1"
fi
if [[ "$disk" == -1 ]]; then
if [[ "$disk_count" == 0 && "$uuid_cmdline" != "none" ]]; then
printf "A device with UUID $uuid_cmdline was not found to be mounted, or it is not a BTRFS device.\n"
fi
printf "Select a mounted BTRFS device to backup to.\n"
while [[ $disk -lt 0 || $disk -gt $i ]]; do
for x in "${!TARGETS_ARRAY[@]}"; do
printf "%4s) %s (%s)\n" "$((x+1))" "${UUIDS_ARRAY[$x]}" "${TARGETS_ARRAY[$x]}"
done
printf "%4s) Exit\n" "0"
read -r -p "Enter a number: " disk
done
if [[ $disk == 0 ]]; then
exit 0
fi
disk=$(($disk-1))
fi
selected_uuid="${UUIDS_ARRAY[$((disk))]}"
selected_mnt="${TARGETS_ARRAY[$((disk))]}"
printf "\nYou selected the disk with UUID %s.\n" "$selected_uuid"
printf "The disk is mounted at %s.\n" "$selected_mnt"
if [[ -f /etc/conf.d/snapper ]]; then
source /etc/conf.d/snapper
else
printf "ERROR: /etc/conf.d/snapper does not exist!\n"
exit 1
fi
selected_configs=${selected_configs:-$SNAPPER_CONFIGS}
for x in $selected_configs; do
printf "\n"
if [[ -f "/etc/snapper/configs/$x" ]]; then
source /etc/snapper/configs/$x
else
printf "Error: Selected snapper configuration $x does not exist.\n"
exit 1
fi
old_number=$(snapper -c "$x" list -t single | awk '/'"$selected_uuid"'/ {print $1}')
old_snapshot=$SUBVOLUME/.snapshots/$old_number/snapshot
if [[ -z "$old_number" ]]; then
printf "No backups have been performed for '%s' on this disk.\n" "$x"
read -r -p "Enter name of directory to store backups, relative to $selected_mnt (to be created if not existing): " mybackupdir
printf "This will be the initial backup for snapper configuration '%s' to this disk. This could take awhile.\n" "$x"
BACKUPDIR="$selected_mnt/$mybackupdir"
mkdir -p "$BACKUPDIR"
else
mybackupdir=$(snapper -c root list -t single | awk -F"|" '/'"$selected_uuid"'/ {print $5}' | awk -F "," '/backupdir/ {print $1}' | awk -F"=" '{print $2}')
BACKUPDIR="$selected_mnt/$mybackupdir"
if [[ ! -d $BACKUPDIR ]]; then
printf "ERROR: %s is not a directory on %s.\n" "$BACKUPDIR" "$selected_uuid"
exit 1
fi
fi
new_number=$(snapper -c "$x" create --print-number)
new_snapshot=$SUBVOLUME/.snapshots/$new_number/snapshot
new_info=$SUBVOLUME/.snapshots/$new_number/info.xml
sync
backup_location=$BACKUPDIR/$x/$new_number/
printf "Will backup %s to %s\n" "$new_snapshot" "$backup_location/snapshot"
if [[ $noconfirm == "yes" ]]; then
cont_backup="yes"
else
read -r -p "Continue with backup [Y/n]? " cont_backup
fi
if [[ "$cont_backup" != [Yy]"es" && "$cont_backup" != [Yy] && -n "$cont_backup" ]]; then
printf "Aborting backup for this configuration.\n"
snapper -c $x delete $new_number
continue
fi
mkdir -p "$backup_location"
if [[ -z "$old_number" ]]; then
btrfs send "$new_snapshot" | btrfs receive "$backup_location" &>/dev/null
else
# Sends the difference between the new snapshot and old snapshot to the
# backup location. Using the -c flag instead of -p tells it that there
# is an identical subvolume to the old snapshot at the receiving
# location where it can get its data. This helps speed up the transfer.
btrfs send "$new_snapshot" -c "$old_snapshot" | btrfs receive "$backup_location" &>/dev/null
snapper -c "$x" delete "$old_number"
fi
cp "$new_info" "$backup_location"
userdata="backupdir=$mybackupdir, uuid=$selected_uuid"
# Tag new snapshot as the latest
snapper -v -c "$x" modify -d "$description" -u "$userdata" "$new_number"
done
printf "\nDone!\n"