From f4c6bee7ca4ff9931ad8e028f400b9b3884dacec Mon Sep 17 00:00:00 2001 From: root <root@pontos02.fi.muni.cz> Date: Fri, 17 Apr 2020 10:17:15 +0200 Subject: [PATCH] backup: Per-source backup --- backup-tar/backup.sh | 99 ++++++++++++++++++++++++++------------------ 1 file changed, 59 insertions(+), 40 deletions(-) diff --git a/backup-tar/backup.sh b/backup-tar/backup.sh index f1dcb72..d7715db 100755 --- a/backup-tar/backup.sh +++ b/backup-tar/backup.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -set -e +set -ex SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" @@ -13,58 +13,77 @@ fail() { exit 1 } -TYPE=$1 -[[ "$TYPE" ]] || { - if ! ls *.snar >&/dev/null; then - TYPE=full +TARGET=backup-$HOSTNAME@antea +SOURCES_FILE=$SCRIPT_DIR/sources_$HOSTNAME +. $SOURCES_FILE +[[ $SOURCES ]] || fail "SOURCES expected to be set in $SOURCES_FILE" +TIMESTAMP=$(date +"%Y-%m-%d") + +mkdir lock || fail "ERROR: backup already in progress (lock dir exists)" +trap "rmdir lock" EXIT + +get_snars() { find $1/ -type f -name '*.snar' -printf '%f\n' | sort; } +get_type() { + mkdir -p "$1" + + if ! ls $1/*.snar >&/dev/null; then + echo full else - LAST_FULL=$(date --date=$(ls *.snar | sort | grep full | tail -n1 | sed 's/[.].*$//') +%s) - LAST_BIG=$(date --date=$(ls *.snar | sort | grep '\(full\|weekly\)' | tail -n1 | sed 's/[.].*$//') +%s) + LAST_FULL=$(date --date=$(get_snars $1 | grep full | tail -n1 | sed 's/[.].*$//') +%s) + LAST_BIG=$(date --date=$(get_snars $1 | grep '\(full\|weekly\)' | tail -n1 | sed 's/[.].*$//') +%s) NOW=$(date +%s) # do a full backup every 8 weeks if [[ $(($NOW - $LAST_FULL)) -ge $((3600 * 24 * 7 * 8)) ]]; then - TYPE=full + echo full elif [[ $(($NOW - $LAST_BIG)) -ge $((3600 * 24 * 7)) ]]; then - TYPE=weekly + echo weekly else - TYPE=daily + echo daily fi fi - echo "Performing $TYPE backup (autodetected)" >&2 } -[[ "$TYPE" = "full" ]] || [[ "$TYPE" = "weekly" ]] || [[ "$TYPE" = "daily" ]] || \ - fail "usage: $0 [full|weekly|daily]" -TARGET=backup-$HOSTNAME@antea -LOCAL_HOME_ID=$(stat -c %d /home) -SOURCES_FILE=$SCRIPT_DIR/sources_$HOSTNAME -. $SOURCES_FILE -[[ $SOURCES ]] || fail "SOURCES expected to be set in $SOURCES_FILE" -TIMESTAMP=$(date +"%Y-%m-%d") +for SRC in $SOURCES; do + SRC_ID=$(echo $SRC | sed 's|^/||') + mkdir -p $SRC_ID -BACKUP_FILE=${TIMESTAMP}.${TYPE}.tar.xz -INCFILE_TGT=stamps/${TIMESTAMP}.${TYPE}.snar.xz -INCFILE=${TIMESTAMP}.${TYPE}.snar -INCFILE_TMP=${INCFILE}.tmp + TYPE=$(get_type $SRC_ID) + [[ "$TYPE" = "full" ]] || [[ "$TYPE" = "weekly" ]] || [[ "$TYPE" = "daily" ]] || \ + fail "invalid backup type for $SRC: $TYPE" -INC_FILTER="." -if [[ "$TYPE" = "weekly" ]]; then - INC_FILTER='\(full\|weekly\)' -fi + BACKUP_FILE=${SRC_ID}/${TIMESTAMP}.${TYPE}.tar.xz + INCFILE_TGT=stamps/${SRC_ID}/${TIMESTAMP}.${TYPE}.snar.xz + INCFILE=${SRC_ID}/${TIMESTAMP}.${TYPE}.snar + INCFILE_TMP=${INCFILE}.tmp -mkdir lock || fail "ERROR: backup already in progress (lock dir exists)" -trap "rmdir lock" EXIT + [[ -f $INCFILE_TMP ]] && fail "ERROR: backup already in progress (incremental backup file exists)" -[[ -f $INCFILE_TMP ]] && fail "ERROR: backup already in progress (incremental backup file exists)" -if [[ "$TYPE" != "full" ]]; then - LAST=$(ls *.snar | sort | grep $INC_FILTER | tail -n1) - if [[ -f "$LAST" ]]; then - cp --reflink=auto $LAST $INCFILE_TMP + INC_FILTER="." + if [[ "$TYPE" = "weekly" ]]; then + INC_FILTER='\(full\|weekly\)' fi -fi -ssh $TARGET "mkdir -p stamps" -tar --create --xattrs --acls --listed-incremental=$INCFILE_TMP $SOURCES | \ - xz -T$COMPRESS_THRS -3 | ssh $TARGET "cat > $BACKUP_FILE" -xz -T$COMPRESS_THRS --keep --stdout $INCFILE_TMP | ssh $TARGET "cat > $INCFILE_TGT" -mv $INCFILE_TMP $INCFILE + if [[ "$TYPE" != "full" ]]; then + LAST=${SRC_ID}/$(get_snars $SRC_ID | grep $INC_FILTER | tail -n1) + if [[ -f "$LAST" ]]; then + cp --reflink=auto $LAST $INCFILE_TMP + fi + fi + + ssh $TARGET "mkdir -p stamps/${SRC_ID} ${SRC_ID}" + echo "Backup $SRC ($TYPE)…" + tar --create --xattrs --acls --listed-incremental=$INCFILE_TMP $SRC | \ + xz -T$COMPRESS_THRS -3 | ssh $TARGET "umask 0027; cat > $BACKUP_FILE" + xz -T$COMPRESS_THRS --keep --stdout $INCFILE_TMP | ssh $TARGET "umask 0027; cat > $INCFILE_TGT" + if echo $SRC | grep -q '^/home'; then + USR=$(echo $SRC | sed 's|^/home/\([^/]*\).*$|\1|') + ssh $TARGET "setfacl -m 'u:$USR:r' $BACKUP_FILE" + DIR=$(dirname $BACKUP_FILE) + while true; do + ssh $TARGET "setfacl -m 'u:$USR:rx' $DIR" + [[ "$DIR" = "." ]] && break + DIR=$(dirname $DIR) + done + fi + mv $INCFILE_TMP $INCFILE +done -- GitLab