diff --git a/README.md b/README.md index 9e30707..35403ab 100644 --- a/README.md +++ b/README.md @@ -10,10 +10,11 @@ Btrfs snapshot managing bash script - Script needs the toplevel subvolume (id=5) mounted somewhere. Default location is `/root/btrfs-toplevel`, but you can mount it anywhere you like and define it with `TOPLEVEL` variable. (A separate config file will be implemented later). - Will create a subvolume named snapshots by default to the toplevel. This can also be changed with `SNAPSHOTS_LOCATION`. - Display usage instructions with `snapsh -h` or `snapsh --help` -- Taking snapshots requires root priviledges. Take a snapshot with `snapsh -s SUBVOLUME` or `snapsh --snapshot SUBVOLUME`, where `SUBVOLUME` is the name of the source subvolume. You can add a description for the snapshot with the `-d | --description` option (must be used before the `-s` option)

Example with Fedora default btrfs layout with `root` and `home` subvolumes:
`snapsh -d "This is a snapshot" -s root`
This will create a snapshot called `root_snapshot_YYYY.MM.DD-hh:mm:ss` to the `snapshots` subvolume (or the one you defined with `SNAPSHOTS_LOCATION`), with a description "This is a snapshot" +- Taking snapshots requires root priviledges. Take a snapshot with `snapsh -s SUBVOLUME` or `snapsh --snapshot SUBVOLUME`, where `SUBVOLUME` is the name of the source subvolume. You can add a description for the snapshot with the `-d | --description` option

Example with Fedora default btrfs layout with `root` and `home` subvolumes:
`snapsh -s root -d "This is a snapshot" `
This will create a snapshot called `root_snapshot_YYYY.MM.DD-hh:mm:ss` to the `snapshots` subvolume (or the one you defined with `SNAPSHOTS_LOCATION`), with a description "This is a snapshot" - Snapshots can be listed with `snapsh -l` or `snapsh --list` - Delete snapshots with the `-r` or `--remove` option. List snapshots first with `snapsh -l`, then delete snapshot with e.g. `snapsh -r 2`, where 2 is the number of the deletable snapshot in the `-l` listing. The list numbers always start from 1 and increment from there, so always check the number before deletion. Batch deletion might be implemented later. - Rollback to a snapshot with `snapsh --rollback NUMBER`, where NUMBER is the number listed in `snapsh -l`. The subvolume will be detected from the snapshot. E.g. `snapsh --rollback 14`. Script will ask to reboot system once done. +- Snapshot type displayed in listing can be set with the `-t, --type` option. When used in conjunction with `-l | --list`, it will filter the results based on the type. E.g. `snapsh -l -t manual` will list all the snapshots with the type `manual`. Type can be one of `manual, auto, boot` or `backup`. Note that only `manual` and `backup` are implemented properly at this stage, but you can still use the other types as well. ### Planned features: - separate config file diff --git a/snapsh b/snapsh deleted file mode 100755 index c389931..0000000 --- a/snapsh +++ /dev/null @@ -1,306 +0,0 @@ -#!/bin/bash - -## snapsh -## Copyright (C) 2020 Jarno Rankinen -## -## This program is free software: you can redistribute it and/or modify -## it under the terms of the GNU General Public License as published by -## the Free Software Foundation, either version 3 of the License, or -## (at your option) any later version. -## -## This program is distributed in the hope that it will be useful, -## but WITHOUT ANY WARRANTY; without even the implied warranty of -## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -## GNU General Public License for more details. -## -## You should have received a copy of the GNU General Public License -## along with this program. If not, see . - -# Environment set up: - -TOPLEVEL="/root/btrfs-toplevel" -SNAPSHOTS_LOCATION="/root/btrfs-toplevel/snapshots" - -BTRFS_EXECUTABLE=$(which btrfs) -TIMESTAMP=$(date +%Y.%m.%d-%H:%M:%S) -SUBVOLUME="root" -DESCRIPTION="" - - - -help() { - printf "Usage:\n -snapsh [OPTIONS] - -Options: - -h, --help Display this help message - -s SUBVOL, --snapshot SUBVOL Take a snapshot of subvolume named SUBVOL. - -d STR, --description STR Add a description for the snapshot displayed in the - snapshots listing. - -t TYPE, --type TYPE Set the type of snapshot, where TYPE=manual|auto|boot|backup - Can be used with -l, --list to filter results - -l, --list List snapshots - -r NUMBER, --remove NUMBER Remove snapshot NUMBER. See snapshot numbers with - snapsh -l - --rollback NUMBER Roll back to snapshot NUMBER. See snapshot numbers - with snapsh -l. Target subvolume is detected from - snapshot automatically.\n" -} - - - -snapshot() { - - EXIT_CODE=0 - root_check - - # Check that the subvolume storing snapshots exists - if [[ ! -d ${SNAPSHOTS_LOCATION} ]]; then - printf "Subvolume ${SNAPSHOTS_LOCATION} does not exist. Create it now?\n" - read -n 1 -p "y/n: " - - if [[ "${REPLY}" == "y" ]]; then - ${BTRFS_EXECUTABLE} subvolume create ${SNAPSHOTS_LOCATION} - unset ${REPLY} - else - EXIT_CODE=1 - fi - else - # TYPE defaults to manual - if [[ -z "${SET_TYPE}" ]]; then - SET_TYPE="manual" - fi - - printf "Creating snapshot of subvolume ${SUBVOLUME} as ${SUBVOLUME}_snapshot_${TIMESTAMP}\n" - - # Create info file for listing snapshots - # Created first on the source subvolume, then deleted - printf "DATE=\"$(date)\" - SOURCE_SUBVOLUME=\"${SUBVOLUME}\" - DESCRIPTION=\"${DESCRIPTION}\" - TYPE=\"${SET_TYPE}\"\n" > ${TOPLEVEL}/${SUBVOLUME}/.snapsh - - # Create readonly subvolume - ${BTRFS_EXECUTABLE} subvolume snapshot -r ${TOPLEVEL}/${SUBVOLUME} ${SNAPSHOTS_LOCATION}/${SUBVOLUME}_snapshot_${TIMESTAMP} - - # Delete info file from source - rm -f ${TOPLEVEL}/${SUBVOLUME}/.snapsh - - printf "Snapshot created! - ${SUBVOLUME}_snapshot_${TIMESTAMP} - DATE=$(date) - SOURCE_SUBVOLUME=${SUBVOLUME} - DESCRIPTION=${DESCRIPTION} - TYPE=\"${SET_TYPE}\"\n" - - fi - - exit ${EXIT_CODE} -} - - - -list() { - root_check - NUM=1 - printf "%6s %s %s %26s %s %s %6s %s %s\n" "Number" "|" "Time:" "|" "Source" "|" "Type" "|" "Description" - for snapshot in ${SNAPSHOTS_LOCATION}/*/; do - if [[ -z "${SET_TYPE}" ]]; then - . ${snapshot}/.snapsh - printf "%8s %32s %8s %8s %s\n" "${NUM} |" "${DATE} |" "${SOURCE_SUBVOLUME} |" "${TYPE} |" "${DESCRIPTION}" - elif [[ -n "${SET_TYPE}" ]]; then - . ${snapshot}/.snapsh - [[ "${SET_TYPE}" == "${TYPE}" ]] && printf "%8s %32s %8s %8s %s\n" "${NUM} |" "${DATE} |" "${SOURCE_SUBVOLUME} |" "${TYPE} |" "${DESCRIPTION}" - fi - - let NUM=NUM+1 - done - exit 0 -} - - - -remove() { - root_check - SNAPSHOTS=(${SNAPSHOTS_LOCATION}/*/) - - if [[ "${REMOVE_TARGET}" -gt "${#SNAPSHOTS[@]}" ]]; then - printf "Snapshot number ${REMOVE_TARGET} does not exist.\n" - exit 1 - elif [[ "${REMOVE_TARGET}" -lt 1 ]]; then - printf "Number must be greater than 0.\n" - exit 1 - fi - - let INDEX=${REMOVE_TARGET}-1 - TARGET=${SNAPSHOTS[${INDEX}]} - . ${TARGET}/.snapsh - - printf "Delete snapshot ${REMOVE_TARGET}: ${DATE}, source subvolume ${SOURCE_SUBVOLUME}, ${TYPE}, ${DESCRIPTION} (y/n)? " - read -n 1 - if [[ "${REPLY}" == "y" ]]; then - printf "\n" - ${BTRFS_EXECUTABLE} property set ${TARGET} ro false - ${BTRFS_EXECUTABLE} subvolume delete ${TARGET} - exit 0 - else - printf "\nAborted by user.\n" - exit 1 - fi -} - - - -rollback() { - root_check - SNAPSHOTS=(${SNAPSHOTS_LOCATION}/*/) - - if [[ "${ROLLBACK_TARGET}" -gt "${#SNAPSHOTS[@]}" ]]; then - printf "Snapshot number ${ROLLBACK_TARGET} does not exist.\n" - exit 1 - elif [[ "${ROLLBACK_TARGET}" -lt 1 ]]; then - printf "Number must be greater than 0.\n" - exit 1 - fi - - let INDEX=${ROLLBACK_TARGET}-1 - TARGET=${SNAPSHOTS[${INDEX}]} - . ${TARGET}/.snapsh - - printf "You are about to roll back to snapshot ${ROLLBACK_TARGET}: ${DATE}, subvolume ${SOURCE_SUBVOLUME}, type ${TYPE}, ${DESCRIPTION}.\nAre you sure (yes/no)? " - read - - if [[ "${REPLY}" == "yes" ]]; then - unset ${REPLY} - printf "\nCreating a backup snapshot of ${SOURCE_SUBVOLUME}...\n" - # Create info file - printf "DATE=\"$(date)\" - SOURCE_SUBVOLUME=\"${SOURCE_SUBVOLUME}\" - DESCRIPTION=\"Rollback backup\" - TYPE=\"backup\"\n" > ${TOPLEVEL}/${SOURCE_SUBVOLUME}/.snapsh - - # Create backup snapshot - ${BTRFS_EXECUTABLE} subvolume snapshot -r ${TOPLEVEL}/${SUBVOLUME} ${SNAPSHOTS_LOCATION}/${SUBVOLUME}_snapshot_${TIMESTAMP} - rm -f ${TOPLEVEL}/${SOURCE_SUBVOLUME}/.snapsh - - # Rename current subvolume - printf "Renaming ${SOURCE_SUBVOLUME} to ${SOURCE_SUBVOLUME}.backup...\n" - mv ${TOPLEVEL}/${SOURCE_SUBVOLUME} ${TOPLEVEL}/${SOURCE_SUBVOLUME}.backup - - printf "Copying ${TARGET} to ${TOPLEVEL}/${SOURCE_SUBVOLUME}...\n" - ${BTRFS_EXECUTABLE} subvolume snapshot ${TARGET} ${TOPLEVEL}/${SOURCE_SUBVOLUME} - - printf "System needs to be restarted. Do you wish to do that now? (recommended!)? (y/n) " - read -n 1 - - if [[ "${REPLY}" == "y" ]]; then - systemctl reboot & exit 0 - else - printf "\nPlease restart system as soon as possible. Any changes to subvolume ${SOURCE_SUBVOLUME} will not persist after rebooting.\n" - exit 1 - fi - - else - printf "\nAborted by user\n" - exit 1 - fi - -} - - - -# Check for root permissions -root_check() { - if [[ "$UID" -ne 0 ]]; then - printf "This option needs root permission.\n" - exit 1 - fi -} - - - -# If no options are given, display help -if [[ "$#" -eq 0 ]]; then - help - exit 0 -fi - - -# Options parsing: -OPTIONS=$(getopt -a -n snapsh -o hs:d:lr:t: --long help,snapshot:,description:,list,remove:,rollback:,type: -- "$@") - -# Invalid options (getopt returns nonzero) -if [[ "$?" -ne 0 ]]; then - printf "Invalid options.\n" - help - exit 1 -fi - -eval set -- "$OPTIONS" -while true; do - case "$1" in - - -h | --help) - help - shift - exit 0 - ;; - - -d | --description) - DESCRIPTION="$2" - shift 2 - ;; - - -s | --snapshot) - SUBVOLUME="$2" - SNAPSHOT=true - shift 2 - ;; - - -l | --list) - LIST=true - shift - ;; - - -r | --remove) - REMOVE_TARGET="$2" - remove - shift 2 - ;; - - --rollback) - ROLLBACK_TARGET="$2" - rollback - shift 2 - ;; - - -t | --type) - case "$2" in - manual) - SET_TYPE="manual";; - auto) - SET_TYPE="auto";; - boot) - SET_TYPE="boot";; - backup) - SET_TYPE="backup";; - *) - printf "\nIncorrect type value.\n" - exit 1;; - esac - shift 2 - ;; - - --) - shift - break - ;; - - esac -done - -if [[ -n "${SNAPSHOT}" ]]; then - snapshot -elif [[ -n "${LIST}" ]]; then - list -fi \ No newline at end of file