Skip to content

Instantly share code, notes, and snippets.

@InternetUnexplorer
Last active January 18, 2021 01:50
Show Gist options
  • Save InternetUnexplorer/9213c0327a664e042d57efb80a1c8cb7 to your computer and use it in GitHub Desktop.
Save InternetUnexplorer/9213c0327a664e042d57efb80a1c8cb7 to your computer and use it in GitHub Desktop.
My current backup "solution". Both source and destination filesystems are btrfs. This will be replaced eventually; you should use btrfs-send or similar instead.
#!/usr/bin/env bash
################################
BLOCK_DEVICE=/dev/disk/by-label/Backup
DM_NAME=cryptbackup
MOUNT_POINT=/mnt/backup
################################
color() { echo -n "$(tput bold)$(tput setaf ${1})${2}$(tput sgr 0)" ; }
abort() { echo "$(color 1 'ERROR:') $(color 7 "${@}")"; exit 1 ; }
warn() { echo "$(color 3 'WARN:') $(color 7 "${@}")" ; }
info() { echo "$(color 4 'INFO:') $(color 7 "${@}")" ; }
step() { echo "$(color 4 '::') $(color 7 "${@}")" ; }
################################
set -euo pipefail
DM_PATH="/dev/mapper/${DM_NAME}"
SRC="/"
PARENT="${MOUNT_POINT}/$(hostname)"
DEST="${PARENT}/latest"
SNAPSHOT="${PARENT}/$(date --iso-8601)"
# Make sure we have root privileges
[ "$EUID" -eq 0 ] || exec sudo bash "$0" "$@"
# Make sure the backup block device exists before mounting
[[ -e "${BLOCK_DEVICE}" ]] \
|| abort "'${BLOCK_DEVICE}' is not a valid block device."
# Open block device if not already opened
if [[ ! -e "${DM_PATH}" ]]; then
step "Opening block device '${BLOCK_DEVICE}'..."
cryptsetup open -- "${BLOCK_DEVICE}" "${DM_NAME}" \
|| abort "Unable to open device."
fi
# Mount mapped device if not already mapped
if ! mountpoint -- "${MOUNT_POINT}" > /dev/null; then
step "Mounting '${DM_PATH}' at '${MOUNT_POINT}'..."
mount -o compress=zstd:2 -- "${DM_PATH}" "${MOUNT_POINT}" \
|| abort "Unable to mount device."
fi
# Make sure ${BACKUP_ROOT}/.backup exists and is a file
[[ -f "${MOUNT_POINT}/.backup" ]] \
|| abort "'${MOUNT_POINT}' missing '.backup' file."
# Check whether a backup directory exists for this host
[[ -d "${PARENT}" ]] || info "Directory '${PARENT}' will be created."
# Check whether a subvolume exists for this host
[[ -d "${DEST}" ]] || info "Subvolume '${DEST}' will be created."
# Check whether a snapshot with the same date exists for this host
[[ ! -d "${SNAPSHOT}" ]] \
|| warn "Existing snapshot '${SNAPSHOT}' will be overwritten."
# Print source, destination, and snapshot paths
echo "$(color 8 '------------------------------------------------')"
echo " $(color 4 "${SRC}") $(color 7 '->') $(color 5 "${DEST}")"
echo " $(color 0 "${SRC}") $(color 0 ' ') $(color 2 "${SNAPSHOT}")"
echo "$(color 8 '------------------------------------------------')"
# Ask for confirmation
read -p "$(color 7 'Proceed? (yes/no):') "
[[ $REPLY =~ ^(y|yes)$ ]] || exit 1
# Create parent directory if not already present
if [[ ! -d "${PARENT}" ]]; then
step "Creating directory '${PARENT}'..."
mkdir -- "${PARENT}" || abort "Unable to create directory."
fi
# Create destination subvolume if not already present
if [[ ! -d "${DEST}" ]]; then
step "Creating subvolume '${DEST}'..."
btrfs subvolume create -- "${DEST}" || abort "Unable to create subvolume."
fi
# Synchronize files (index is built first)
step "Synchronizing files..."
rsync --archive \
--acls \
--xattrs \
--sparse \
--relative \
--numeric-ids \
--delete \
--delete-excluded \
--include='/home/***' \
--exclude='*' \
--info=progress2 \
-- "${SRC}" "${DEST}"
( [ $? -eq 0 ] || [ $? -eq 24 ] ) || abort "rsync exited with errors."
# Remove existing snapshot with the same date if present
if [[ -d "${SNAPSHOT}" ]]; then
step "Removing existing snapshot '${SNAPSHOT}'..."
btrfs subvolume delete -- "${SNAPSHOT}" \
|| abort "Unable to delete snapshot."
fi
# Create new snapshot
step "Creating snapshot '${SNAPSHOT}'..."
btrfs subvolume snapshot -r -- "${DEST}" "${SNAPSHOT}" \
|| abort "Unable to create snapshot."
# Unmount device
step "Unmounting '${MOUNT_POINT}'..."
umount -- "${MOUNT_POINT}" || abort "Unable to unmount device."
# Close device
step "Closing '${DM_PATH}'..."
cryptsetup close -- "${DM_NAME}" || abort "Unable to close device."
step "Backup complete."
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment