From 0cd64b92465be64e1fb55acf407830a97b2d4af5 Mon Sep 17 00:00:00 2001 From: P. J. McDermott Date: Tue, 22 May 2018 18:02:14 -0400 Subject: bin/bakdb: New script --- (limited to 'bin/bakdb') diff --git a/bin/bakdb b/bin/bakdb new file mode 100755 index 0000000..7ab7c8c --- /dev/null +++ b/bin/bakdb @@ -0,0 +1,113 @@ +#!/bin/sh + +set -eu + +prev_hash= +prev_bk_file= + +# Back up a database and output a SHA-256 hash of its SQL dump. +backup() +{ + local db_file="${1}" + local bk_file="${2}" + shift 2 + + sqlite3 "${db_file}" ".backup ${bk_file}~" + sqlite3 "${bk_file}~" '.dump' | sha256sum | cut -d ' ' -f 1 + return 0 +} + +# Read previous backup's hash and file name. +# Returns 0 if the new backup's hash is equal to the previous backup's hash, or +# 1 otherwise. +compare_hashes() +{ + local db_file="${1}" + local bk_hash="${2}" + shift 2 + + { read -r prev_hash prev_bk_file 0<"${db_file}.bakhash"; } 2>/dev/null \ + || : + + case "${prev_hash}" in "${bk_hash}") return 0;; esac + return 1 +} + +# Make a symbolic link with the new backup file name to the previous backup +# file. +# No path resolution is performed, so either absolute paths should be used or +# all backup files should be in the same directory. +# The linking of the final backup file and the unlinking of the temporary file +# are not atomic, but as long as they're done in this order, the only possible +# consequence is that the linking is done but an extraneous temporary file +# remains. +link_to_prev() +{ + local bk_file="${1}" + shift 1 + + ln -s "${prev_bk_file}.xz" "${bk_file}.xz" + rm "${bk_file}~" + + return 0 +} + +# Compress and save a backup file. +save_backup() +{ + local db_file="${1}" + shift 1 + + xz "${bk_file}~" + mv "${bk_file}~.xz" "${bk_file}.xz" + + return 0 +} + +# Save a backup file's name and hash for future comparison and linking. +save_hash() +{ + local db_file="${1}" + local bk_file="${2}" + local bk_hash="${3}" + shift 3 + + printf '%s %s\n' "${bk_hash}" "${bk_file}" 1>"${db_file}.bakhash~" + mv "${db_file}.bakhash~" "${db_file}.bakhash" + + return 0 +} + +usage() +{ + printf 'Usage: %s \n' "${0}" + + return 0 +} + +main() +{ + local db_file= + local bk_file= + local bk_hash= + + if [ ${#} -ne 2 ]; then + usage 1>&2 + return 1 + fi + db_file="${1}" + bk_file="${2}" + shift 2 + + bk_hash="$(backup "${db_file}" "${bk_file}")" + if compare_hashes "${db_file}" "${bk_hash}"; then + link_to_prev "${bk_file}" + else + save_backup "${bk_file}" + save_hash "${db_file}" "${bk_file}" "${bk_hash}" + fi + + return 0 +} + +main "${@}" -- cgit v0.9.1