summaryrefslogtreecommitdiff
path: root/tarship.sh
diff options
context:
space:
mode:
authorYour Name <you@example.com>2026-02-20 23:54:19 +0800
committerYour Name <you@example.com>2026-02-20 23:54:19 +0800
commit54578ee884795384e052d33364699c97091bd1cb (patch)
treed9ec606b543bf11d18a893223f5fb45fc910f268 /tarship.sh
parent53ff71f41a2722db4cf7e384a3ae36102f40ef59 (diff)
Diffstat (limited to 'tarship.sh')
-rwxr-xr-xtarship.sh9
1 files changed, 6 insertions, 3 deletions
diff --git a/tarship.sh b/tarship.sh
index cf03a1b..51d593e 100755
--- a/tarship.sh
+++ b/tarship.sh
@@ -8,14 +8,16 @@ backup)
8 SRC="${2:?$USAGE}" DEST="${3:?$USAGE}" NAME="$(basename "$SRC").tar.gz" 8 SRC="${2:?$USAGE}" DEST="${3:?$USAGE}" NAME="$(basename "$SRC").tar.gz"
9 [ ! -d "$SRC" ] && echo "Error: not a directory: $SRC" && exit 1 9 [ ! -d "$SRC" ] && echo "Error: not a directory: $SRC" && exit 1
10 du -sh "$SRC"; SIZE=$(du -sb "$SRC" | awk '{print $1}'); LHASH=$(mktemp); trap "rm -f $LHASH" EXIT 10 du -sh "$SRC"; SIZE=$(du -sb "$SRC" | awk '{print $1}'); LHASH=$(mktemp); trap "rm -f $LHASH" EXIT
11 # remote: tar+pigz stream over ssh, tee hash both sides 11 # tar+pigz stream over ssh, hash both sides in one connection, abort on mismatch
12 # ssh-agent bash -c 'ssh-add ~/keyfile && tar cf - /backup_dir | pv | pigz -1 | ssh root@host "tee /dest/backup_dir.tar.gz | sha256sum"'
12 if [[ "$DEST" == *:* ]]; then 13 if [[ "$DEST" == *:* ]]; then
13 H="${DEST%%:*}" D="${DEST#*:}"; echo "→ ${H}:${D}/${NAME}" 14 H="${DEST%%:*}" D="${DEST#*:}"; echo "→ ${H}:${D}/${NAME}"
14 RHASH=$(tar cf - "$SRC" | pv -s "$SIZE" | pigz -1 | tee >(sha256sum | awk '{print $1}' > "$LHASH") | ssh "$H" "h=\$(tee ${D}/${NAME} | sha256sum | awk '{print \$1}'); echo \$h > ${D}/${NAME}.sha256; echo \$h") 15 RHASH=$(tar cf - "$SRC" | pv -s "$SIZE" | pigz -1 | tee >(sha256sum | awk '{print $1}' > "$LHASH") | ssh "$H" "h=\$(tee ${D}/${NAME} | sha256sum | awk '{print \$1}'); echo \$h > ${D}/${NAME}.sha256; echo \$h")
15 L=$(cat "$LHASH"); echo "Local: $L"; echo "Remote: $RHASH" 16 L=$(cat "$LHASH"); echo "Local: $L"; echo "Remote: $RHASH"
16 [ "$L" = "$RHASH" ] && echo "Checksum OK" || { echo "CHECKSUM MISMATCH!"; exit 1; } 17 [ "$L" = "$RHASH" ] && echo "Checksum OK" || { echo "CHECKSUM MISMATCH!"; exit 1; }
17 echo "Verify: ssh $H \"sha256sum ${D}/${NAME}\"" 18 echo "Verify: ssh $H \"sha256sum ${D}/${NAME}\""
18 # local: tar+pigz to file, write .sha256 sidecar 19 # tar+pigz to local file, write .sha256 sidecar next to it
20 # tar cf - /backup_dir | pv | pigz -1 > /tmp/backup_dir.tar.gz && sha256sum /tmp/backup_dir.tar.gz
19 else 21 else
20 echo "→ $DEST/$NAME" 22 echo "→ $DEST/$NAME"
21 tar cf - "$SRC" | pv -s "$SIZE" | pigz -1 > "$DEST/$NAME" 23 tar cf - "$SRC" | pv -s "$SIZE" | pigz -1 > "$DEST/$NAME"
@@ -27,7 +29,8 @@ restore)
27 FILE="${2:?$USAGE}" 29 FILE="${2:?$USAGE}"
28 [[ "$FILE" == *.tar.gz ]] && [ -f "$FILE" ] || { echo "Error: not a .tar.gz file: $FILE"; exit 1; } 30 [[ "$FILE" == *.tar.gz ]] && [ -f "$FILE" ] || { echo "Error: not a .tar.gz file: $FILE"; exit 1; }
29 echo "$FILE ($(numfmt --to=iec $(stat -c%s "$FILE"))) → $(pwd)" 31 echo "$FILE ($(numfmt --to=iec $(stat -c%s "$FILE"))) → $(pwd)"
30 # verify checksum before extracting 32 # verify .sha256 sidecar matches archive, then decompress+extract to current dir
33 # sha256sum -c backup_dir.tar.gz.sha256 && pv backup_dir.tar.gz | pigz -d | tar xf -
31 if [ -f "$FILE.sha256" ]; then 34 if [ -f "$FILE.sha256" ]; then
32 echo "Verifying..."; EXP=$(cat "$FILE.sha256"); ACT=$(sha256sum "$FILE" | awk '{print $1}') 35 echo "Verifying..."; EXP=$(cat "$FILE.sha256"); ACT=$(sha256sum "$FILE" | awk '{print $1}')
33 [ "$EXP" = "$ACT" ] && echo "Checksum OK" || { echo "CHECKSUM MISMATCH!"; exit 1; } 36 [ "$EXP" = "$ACT" ] && echo "Checksum OK" || { echo "CHECKSUM MISMATCH!"; exit 1; }