#!/usr/bin/env bash #!/bin/bash # Check for required argument if [ $# -ne 1 ]; then echo "Usage: $(basename "$0") SNAPPER_CONFIG" echo "Example: $(basename "$0") root" exit 1 fi # Configuration SNAPPER_CONFIG="$1" # Get the actual snapshot location from snapper config SOURCE_PATH=$(sudo snapper -c "$SNAPPER_CONFIG" get-config | grep '^SUBVOLUME' | cut -d'=' -f2 | tr -d '"'| awk {'print $3'}) echo "SOURCE_PATH: $SOURCE_PATH" if [ -z "$SOURCE_PATH" ]; then echo "ERROR: Could not determine snapshot path for config '$SNAPPER_CONFIG'" exit 1 fi # Convert subvolume path to snapshot path SNAPSHOT_PATH="$SOURCE_PATH/.snapshots" if [ ! -d "$SNAPSHOT_PATH" ]; then echo "ERROR: Snapshot directory '$SNAPSHOT_PATH' does not exist" exit 1 fi # Get latest snapshot number from snapper LATEST_SNAPSHOT=$(sudo snapper -c "$SNAPPER_CONFIG" list | tail -n 1 | awk '{print $1}') REMOTE_HOST="root@synology" BASE_DEST_PATH="/volume1/BTRFS_Receives/`hostname`/${SNAPPER_CONFIG}" DEST_PATH="/volume1/BTRFS_Receives/`hostname`/${SNAPPER_CONFIG}/${LATEST_SNAPSHOT}" STATE_FILE="/var/lib/snapper-backup-${SNAPPER_CONFIG}.state" LOG_FILE="/var/log/snapper-backup-${SNAPPER_CONFIG}.log" KEEP_SNAPSHOTS=5 # Get latest successful transfer number LAST_TRANSFERRED=$(cat "$STATE_FILE" 2>/dev/null || echo "") # Ensure we exit on any error set -e # Verify snapper config exists if ! sudo snapper -c "$SNAPPER_CONFIG" list &>/dev/null; then echo "ERROR: Snapper config '$SNAPPER_CONFIG' does not exist" exit 1 fi # Logging function log() { echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" | sudo tee -a "$LOG_FILE" } # Function to verify snapshot exists verify_snapshot() { local snapshot_num="$1" if [ ! -d "$SNAPSHOT_PATH/$snapshot_num/snapshot" ]; then log "ERROR: Snapshot $snapshot_num does not exist" return 1 fi return 0 } # Function to verify remote connectivity verify_remote() { if ! ssh -q "$REMOTE_HOST" "exit"; then log "ERROR: Cannot connect to remote host" exit 1 fi } # Function to get remote snapshots get_remote_snapshots() { ssh "$REMOTE_HOST" "find '$BASE_DEST_PATH' -maxdepth 1 -type d -name 'snapshot*' | sort -n" } # Function to cleanup old remote snapshots cleanup_remote_snapshots() { local snapshots=($(get_remote_snapshots)) local count=${#snapshots[@]} if [ $count -gt $KEEP_SNAPSHOTS ]; then local to_delete=$((count - KEEP_SNAPSHOTS)) log "Cleaning up $to_delete old snapshots on remote system" for ((i=0; i<$to_delete; i++)); do local snapshot="${snapshots[$i]}" log "Deleting remote snapshot: $snapshot" ssh "$REMOTE_HOST" "btrfs subvolume delete '$snapshot'" || \ log "WARNING: Failed to delete snapshot $snapshot" done fi } # Start backup process log "Starting backup for snapper config: $SNAPPER_CONFIG" log "Using snapshot path: $SNAPSHOT_PATH" # Verify remote connectivity first verify_remote # Verify snapshots exist verify_snapshot "$LATEST_SNAPSHOT" || exit 1 if [ -n "$LAST_TRANSFERRED" ]; then verify_snapshot "$LAST_TRANSFERRED" || exit 1 fi # Exit if no new snapshots to transfer if [ "$LAST_TRANSFERRED" = "$LATEST_SNAPSHOT" ]; then log "No new snapshots to transfer" exit 0 fi # Create destination directory if it doesn't exist ssh "$REMOTE_HOST" "mkdir -p '$DEST_PATH'" # Perform the transfer if [ -z "$LAST_TRANSFERRED" ]; then # First time backup - full send log "Performing full send of snapshot $LATEST_SNAPSHOT" sudo btrfs send "$SNAPSHOT_PATH/$LATEST_SNAPSHOT/snapshot" | \ pv --bytes | \ ssh "$REMOTE_HOST" "btrfs receive '$DEST_PATH'" && { sudo echo "$LATEST_SNAPSHOT" | sudo tee "$STATE_FILE" log "Full send completed successfully" } || { log "ERROR: Full send failed" exit 1 } else # Incremental send log "Performing incremental send from $LAST_TRANSFERRED to $LATEST_SNAPSHOT" sudo btrfs send -p "$SNAPSHOT_PATH/$LAST_TRANSFERRED/snapshot" \ "$SNAPSHOT_PATH/$LATEST_SNAPSHOT/snapshot" | \ pv --bytes | \ ssh "$REMOTE_HOST" "btrfs receive '$DEST_PATH'" && { sudo echo "$LATEST_SNAPSHOT" | sudo tee "$STATE_FILE" log "Incremental send completed successfully" } || { log "ERROR: Incremental send failed" exit 1 } fi # Cleanup old snapshots if transfer was successful cleanup_remote_snapshots # Verify remote snapshots log "Current remote snapshots:" get_remote_snapshots | sudo tee -a "$LOG_FILE" # Final verification if ! ssh "$REMOTE_HOST" "btrfs subvolume show '$DEST_PATH/snapshot'" &>/dev/null; then log "WARNING: Final verification failed" exit 1 fi log "Backup completed successfully"