diff --git a/scripts/backup-zfs-dataset.sh b/scripts/backup-zfs-dataset.sh index 806d918..db9fbd3 100755 --- a/scripts/backup-zfs-dataset.sh +++ b/scripts/backup-zfs-dataset.sh @@ -1,15 +1,17 @@ #!/bin/bash -# Create archived backups of zfs datasets, with support for incremental backups from automated snapshots. -# Usage: backup-zfs-dataset [OPTIONS] +# Create archived backups of zfs datasets +# Usage: backup-zfs-dataset [OPTIONS] [ ] usage() { >&2 printf "Usage: %s [OPTIONS] [ ]\n" "$0" >&2 printf "Options:\n" - >&2 printf "\t-c --compression-level \t Specify compression level (integer)\n" >&2 printf "\t-s --dataset \t Specify dataset name\n" >&2 printf "\t-d --destination \t Specify destination\n" + >&2 printf "\t-b --base \t Create a new base\n" + >&2 printf "\t-i --incremental \t (Default) Create a new incremental backup\n" + >&2 printf "\t-c --compression-level \t Specify compression level (integer)\n" >&2 printf "\t-m --max-size \t Specify maximum size of archive parts\n" - >&2 printf "\t-f --force \t Force overwriting existing backups if the backup already exists\n" + >&2 printf "\t-t --tag \t Provide a name to tag the archive\n" exit "${1:-1}" } @@ -17,14 +19,6 @@ usage() { while [[ $# -gt 0 ]]; do case "${1}" in - -c | --compression_level) - if ! [[ "${2}" =~ [[:digit:]] ]]; then - >&2 printf "Error: Invalid compression level: '%s'\n" "${2}" - usage - fi - compression_level="${2}" - shift 2 - ;; -s | --dataset) if ! [ -n "${2}" ]; then >&2 printf "Error: Invalid dataset: '%s'\n" "${2}" @@ -41,6 +35,28 @@ while [[ $# -gt 0 ]]; do destination="${2}" shift 2 ;; + -b | --base) + if [ "${create_base:=1}" -neq 1 ]; then + >&2 printf "Error: Cannot create base backup when specifying differently.\n" + usage + fi + shift 1 + ;; + -i | --incremental) + if [ "${create_base:=0}" -neq 0 ]; then + >&2 printf "Error: Cannot create incremental backup when specifying differently.\n" + usage + fi + shift 1 + ;; + -c | --compression_level) + if ! [[ "${2}" =~ [[:digit:]] ]]; then + >&2 printf "Error: Invalid compression level: '%s'\n" "${2}" + usage + fi + compression_level="${2}" + shift 2 + ;; -m | --max-size) if ! [[ "${2}" =~ [[:digit:]](K|M|G) ]]; then >&2 printf "Error: Invalid maximum size: '%s'\n" "${2}" @@ -49,10 +65,10 @@ while [[ $# -gt 0 ]]; do max_size="${2}" shift 2 ;; - -f | --force) - force_create_manual_backup=1 - shift 1 - ;; + -t | --tag) + tag="${2}-" + shift 2 + ;; *) >&2 printf "Error: Invalid option: '%s'\n" "${1}" usage @@ -77,6 +93,23 @@ fi compression_level="${compression_level:=1}" max_size="${max_size:=2G}" +# Check if you need to make a new base backup +if [ "${create_base:=0}" -eq 1 ]; then + snapshot_name="manual-$( date +%Y-%m-%d_%H-%M )" + output_filename="${destination}/${tag:=}${snapshot_name}.gz" + # Create ZFS snapshot + printf "Creating snapshot\n" + sudo zfs snapshot "${dataset}@${snapshot_name}" + # Compress it + printf "Backing up now\n" + sudo zfs send --verbose "${dataset}@${snapshot_name}" \ + | gzip "-${compression_level}" --verbose --rsyncable \ + | split - --verbose -b "${max_size}" "${output_filename}.part." + printf "Written base backup to: '%s'.\n" "${output_filename}" + printf "Done!\n" + exit 0 +fi + # Working snapshots # Find snapshots @@ -101,31 +134,9 @@ set -e # Backups -# Base backup. -output_filename="${destination}/${latest_manual}.gz" -existing_backup="$( find "${destination}" -type f -name "${latest_manual}.gz.part.[a-z][a-z]" -print -quit )" -if [ -z ${existing_backup} ]; then - printf "Info: If you've manually created a new snapshot, you might want to remove the old backups.\n" - printf "Latest manual snapshot was not yet backed up, backing up now.\n" - sudo zfs send --verbose "${dataset}@${latest_manual}" \ - | gzip "-${compression_level}" --verbose --rsyncable \ - | split - --verbose -b "${max_size}" "${output_filename}.part." - printf "Written manual backup to: %s\n" "${output_filename}" -elif [ "${force_create_manual_backup}" ]; then - printf "Removing previous backup files.\n" - find "${destination}" -type f -name "${latest_manual}.gz.part.[a-z][a-z]" -print -delete - printf "Backing up manual snapshot.\n" - sudo zfs send --verbose "${dataset}@${latest_manual}" \ - | gzip "-${compression_level}" --verbose --rsyncable \ - | split - --verbose -b "${max_size}" "${output_filename}.part." - printf "Written manual backup to: %s\n" "${output_filename}" -else - printf "Found existing backup of manual snapshot: %s\n" "${existing_backup}" -fi - # Incremental incremental backup. printf "Creating incremental backup between %s and %s\n" "${latest_manual}" "${latest_auto}" -output_filename="${destination}/${latest_manual}-${latest_auto}.gz" +output_filename="${destination}/${tag}${latest_manual}-${latest_auto}.gz" sudo zfs send --verbose -i "@${latest_manual}" "${dataset}@${latest_auto}" \ | gzip "-${compression_level}" --verbose \ | split - --verbose -b "${max_size}" "${output_filename}.part."