]> git.ipfire.org Git - thirdparty/snapper.git/commitdiff
Add srt, the Snapshot (home dir) Restore Tool script and its README
authorDan MacDonald <allcoms@gmail.com>
Sat, 21 Mar 2026 18:33:09 +0000 (18:33 +0000)
committerDan MacDonald <allcoms@gmail.com>
Sat, 21 Mar 2026 18:33:09 +0000 (18:33 +0000)
scripts/srt-README.md [new file with mode: 0644]
scripts/srt.sh [new file with mode: 0644]

diff --git a/scripts/srt-README.md b/scripts/srt-README.md
new file mode 100644 (file)
index 0000000..e406873
--- /dev/null
@@ -0,0 +1,50 @@
+# SRT
+
+SRT is the [Snapper](http://snapper.io/) Snapshot Restoration Tool.
+
+## WARNING
+
+Running SRT can be dangerous! srt provides a text interface to easily choose a snapper home directory snapshot to revert to.
+
+**All files created since the date of the chosen snapshot will be deleted!**
+
+Use the Dry Run mode to preview what files would be deleted if you restore to the chosen snapshots state. You should always use Dry Run mode first. I cannot be held resposible for any loss of data or damage caused by running this program. You run SRT entirely at your own risk.
+
+Quit any browsers and stop any other running programs before running this script locally.
+
+## Why does SRT exist?
+
+I was a happy ZFS user for several years before I tried BTRFS and one of my favourite ZFS features is filesystem snapshots. ZFS has snapshots of datasets whereas BTRFS has snapshots of subvolumes but there are differences in how subvols and datasets are handled and how snapper reverts snapshots compared to ZFS.
+
+When I started using BTRFS I wanted to be able to revert snapper snapshots, at least of user home directories, in a very similar way to how the `zfs rollback` command works but unfortunately the `snapper rollback` command does not work in a similar manner to zfs rollback. snapper also has an `undochange` subcommand which is closer to what I wanted but I think its confusing for users because it undoes changes between two snapshots instead of reverting to one. In many cases this would require using rsync to work out what would happen wheh using `snapper undochange`.
+
+I was unable to find a program to use alongside snapper that made it easy to restore users home directories to a previous snapshot state. I don't want a new snapshot or subvol to be created when I do a rollback which is why I wrote srt, to effectively rollback without having to deal with handling more subvols or trying to work out what result undochange might give me.
+
+SRT functions a bit like `zfs rollback`, reverting all files in the current users home directory to how they were as per a pre-defined snapper timeline created snapshot but it doesn't destroy the intervening snapshots so you could potentially revert to a newer snapshot after reverting to an older one, if available. A new snapshot or subvol is not created when you rollback with SRT.
+
+## Required Snapper configuration
+
+You do not need to have root permissions to run SRT. It requires that your user has read access to their own `~/.snapshots` directory.
+
+This tool cannot work without the following snapper configuration which needs to be applied for every user who wants to run srt.
+
+This script presumes your users snapper config name matches the Linux user name of the user who wants to run srt.
+
+```
+chown root:USERS_GID /home/USER/.snapshots/
+chmod g+rx /home/USER/.snapshots/
+```
+
+You would need to replace USERS_GID with your Linux users GID and USER the srt users Linux user name. You can find your users GID by running the `id` command.
+
+Your user also needs permission to run the snapper tool to list the snapshots date and time:
+
+```
+snapper -c USER set-config ALLOW_USERS=USER
+```
+
+Here you replace USER with your user name to enable your user to be able to query snapper for snapshot info.
+
+## Using srt
+
+With a suitable snapper timeline config in place and the above snapper configuration per Linux user, you can copy `srt.sh` into your $PATH and make it executable so that users can just run `srt` to pick a snapshot to restore by following the on screen instructions and using the cursor keys and ENTER to select options.
diff --git a/scripts/srt.sh b/scripts/srt.sh
new file mode 100644 (file)
index 0000000..1817014
--- /dev/null
@@ -0,0 +1,108 @@
+#!/bin/bash
+
+# SRT - (Snapper) Snapshot Restore Tool v1.0
+# by Dan MacDonald
+
+# SRT makes it easy to restore user home directories from BTRFS snapshots created by Snapper using rsync and a simple dialog TUI interface.
+# SRT does not require root permission to run but it does require rsync, dialog and read access to your users ~/.snapshots dir.
+
+# Close your web browser and any open documents before running this, if you are running it locally.
+
+# Configuration
+CONFIG_NAME=$(whoami)
+SNAPSHOT_DIR="$HOME/.snapshots"
+
+# Dependency Checks
+for cmd in dialog rsync snapper; do
+    if ! command -v "$cmd" &> /dev/null; then
+        echo "Error: Missing $cmd"
+        exit 1
+    fi
+done
+
+# Loading message
+# We use --infobox because it doesn't wait for user input
+dialog --title "Snapper Restore Tool" --infobox "\nProcessing snapshot data for user '$CONFIG_NAME'...\nPlease wait." 7 60
+
+# Build the snapshots menu
+MENU_OPTIONS=()
+while read -r line; do
+    ID=$(echo "$line" | awk -F'|' '{print $1}' | xargs)
+    DATE=$(echo "$line" | awk -F'|' '{print $4}' | xargs)
+    DESC=$(echo "$line" | awk -F'|' '{print $8}' | xargs)
+
+    if [[ "$ID" =~ ^[0-9]+$ ]] && [ "$ID" -ne 0 ]; then
+        MENU_OPTIONS+=("$ID" "[$DATE] $DESC")
+    fi
+done < <(snapper -c "$CONFIG_NAME" list --disable-used-space 2>/dev/null | grep -v "current" | grep "|")
+
+if [ ${#MENU_OPTIONS[@]} -eq 0 ]; then
+    dialog --title "Error" --msgbox "No snapshots found for config: $CONFIG_NAME" 10 60
+    exit 1
+fi
+
+# Reverse list so newest is at the top
+REVERSED_OPTIONS=()
+for (( i=${#MENU_OPTIONS[@]}-2; i>=0; i-=2 )); do
+    REVERSED_OPTIONS+=("${MENU_OPTIONS[i]}" "${MENU_OPTIONS[i+1]}")
+done
+
+# Snapshot selection
+SNAP_NUM=$(dialog --title "Snapper Restore Tool: $CONFIG_NAME" \
+    --cancel-label "Exit" \
+    --menu "Select a snapshot to restore:" 20 90 12 \
+    "${REVERSED_OPTIONS[@]}" \
+    3>&1 1>&2 2>&3)
+
+[ $? -ne 0 ] && clear && exit
+
+# Action choice
+ACTION=$(dialog --title "Snapshot #$SNAP_NUM options" \
+    --cancel-label "Back" \
+    --menu "Select an action:" 12 60 3 \
+    "1" "DRY RUN (Preview changes and Exit)" \
+    "2" "RESTORE (Destructive Revert)" \
+    3>&1 1>&2 2>&3)
+
+[ $? -ne 0 ] && clear && exit
+
+TARGET_PATH="$SNAPSHOT_DIR/$SNAP_NUM/snapshot"
+
+# --- ACTION 1: DRY RUN ---
+if [ "$ACTION" == "1" ]; then
+    clear
+    echo "--- DRY RUN PREVIEW: Snapshot #$SNAP_NUM ---"
+    echo "Snapshot Source: $TARGET_PATH"
+    echo "Target Directory: $HOME"
+    echo "------------------------------------------------------------"
+
+    # Run rsync dry run
+    rsync -aAXvi --dry-run --delete --exclude='.snapshots' "$TARGET_PATH/" "$HOME/"
+
+    echo "------------------------------------------------------------"
+    echo "DRY RUN COMPLETE. No files were changed."
+    echo "Script exiting."
+    exit 0
+fi
+
+# --- ACTION 2: RESTORE ---
+if [ "$ACTION" == "2" ]; then
+    dialog --title "!!! FINAL WARNING !!!" --colors \
+        --yesno "User: \Zb$CONFIG_NAME\Zn\nSnapshot: \Zb#$SNAP_NUM\Zn\n\nEverything in your home directory (except the ~/.snapshots directory) will be \ZbDELETED\Zn.\n\nContinue?" 15 65
+
+    if [ $? -eq 0 ]; then
+        clear
+        echo "EXECUTING RESTORE: Snapshot #$SNAP_NUM"
+        echo "----------------------------------------"
+
+        rsync -aAXvh --delete --exclude='.snapshots' --info=progress2 "$TARGET_PATH/" "$HOME/"
+
+        if [ ${PIPESTATUS[0]} -eq 0 ]; then
+            dialog --title "Success" --msgbox "Home directory successfully rolled back to snapshot #$SNAP_NUM." 7 60
+        else
+            echo -e "\nError: Rsync failed. Check for open files."
+        fi
+    fi
+fi
+
+clear