From a13cba3790e42d1d30a09ec614f8d0e5e188e66a Mon Sep 17 00:00:00 2001 From: Jarno Rankinen Date: Tue, 5 Jan 2021 00:39:13 +0200 Subject: [PATCH] Implemented automatic toplevel mounting snapsh now checks if the toplevel subvolume is mounted at the path defined in TOPLEVEL, and if not, does it automatically. --- snapsh | 155 ++++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 104 insertions(+), 51 deletions(-) diff --git a/snapsh b/snapsh index 2986a69..091136f 100755 --- a/snapsh +++ b/snapsh @@ -42,16 +42,61 @@ 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 + -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 --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" + -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" +} + +mount_check() { + + # Check that the toplevel subvolume (id=5) is mounted, and if not, + # mount it to path defined in $TOPLEVEL + + mount | grep subvolid=5 | grep "${TOPLEVEL}" > /dev/null + if [[ "$?" -ne 0 ]]; then + # Get the UUID of the current btrfs volume + MOUNT_UUID=$(btrfs filesystem show | grep uuid | cut -d ':' -f 3) + # Create the mountpoint for the toplevel if needed + [[ -d ${TOPLEVEL} ]] || mkdir -p ${TOPLEVEL} + # Mount the toplevel + mount -U ${MOUNT_UUID} -o subvolid=5 ${TOPLEVEL} + fi + + if [[ -n ${SNAPSHOT} ]]; then + # If taking a snapshot, + # check that the subvolume storing snapshots exists and if not, ask + # to create it. + ${BTRFS_EXECUTABLE} subvolume show ${SNAPSHOTS_LOCATION} > /dev/null + if [[ "$?" -ne 0 ]]; then + printf "Subvolume ${SNAPSHOTS_LOCATION} does not exist. Create it now?\n" + read -n 1 -p "y/n: " + + if [[ "${REPLY}" == "y" ]]; then + # Create subvolume defined with SNAPSHOTS_LOCATION + ${BTRFS_EXECUTABLE} subvolume create ${SNAPSHOTS_LOCATION} + unset ${REPLY} + else + exit 1 + fi + fi + else + # Otherwise, just check that the subvolume in SNAPSHOTS_LOCATION + # exists, and notify if not. + ${BTRFS_EXECUTABLE} subvolume show ${SNAPSHOTS_LOCATION} > /dev/null + if [[ "$?" -ne 0 ]]; then + printf "Subvolume ${SNAPSHOTS_LOCATION} does not exist.\n" + exit 1 + fi + fi + } @@ -60,49 +105,42 @@ snapshot() { EXIT_CODE=0 root_check #Check that script is run with root privileges. + mount_check #Check that the toplevel subvolume is mounted - # 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 - # Create subvolume defined with SNAPSHOTS_LOCATION - ${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 from the source - 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" + # 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 from the source + 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} + + let EXIT_CODE=${EXIT_CODE}+$? + + # 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" + + exit ${EXIT_CODE} } @@ -110,6 +148,8 @@ snapshot() { list() { root_check + mount_check + shopt -s nullglob NUM=1 SNAPSHOTS=(${SNAPSHOTS_LOCATION}/*/) @@ -117,14 +157,18 @@ list() { printf "No snapshots found in ${SNAPSHOTS_LOCATION}.\n" exit 0 fi - printf "%6s %s %s %26s %s %s %6s %s %s\n" "Number" "|" "Time:" "|" "Source" "|" "Type" "|" "Description" + printf "%6s %s %s %26s %10s %s %6s %s %s\n" "Number" "|" "Time:" "|" \ + "Source" "|" "Type" "|" "Description" for snapshot in ${SNAPSHOTS[@]}; do if [[ -z "${SET_TYPE}" ]]; then . ${snapshot}/.snapsh - printf "%8s %32s %8s %8s %s\n" "${NUM} |" "${DATE} |" "${SOURCE_SUBVOLUME} |" "${TYPE} |" "${DESCRIPTION}" + printf "%8s %32s %12s %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}" + [[ "${SET_TYPE}" == "${TYPE}" ]] && printf "%8s %32s %12s %8s %s\n" \ + "${NUM} |" "${DATE} |" "${SOURCE_SUBVOLUME} |" "${TYPE} |" \ + "${DESCRIPTION}" fi let NUM=NUM+1 @@ -136,6 +180,7 @@ list() { remove() { root_check + mount_check # List snapshots in to array SNAPSHOTS SNAPSHOTS=(${SNAPSHOTS_LOCATION}/*/) @@ -170,6 +215,8 @@ remove() { rollback() { root_check # Check root privileges + mount_check # Check that the toplevel subvolume is mounted + SNAPSHOTS=(${SNAPSHOTS_LOCATION}/*/) # List snapshots to array # Check that NUBER to roll back to is a valid snapshot @@ -302,7 +349,7 @@ fi # Options parsing: -OPTIONS=$(getopt -a -n snapsh -o hs:d:lr:t: --long help,snapshot:,description:,list,remove:,rollback:,type:,post-rollback -- "$@") +OPTIONS=$(getopt -a -n snapsh -o hs:d:lr:t: --long help,snapshot:,description:,list,remove:,rollback:,type:,post-rollback,mount -- "$@") # Invalid options (getopt returns nonzero) if [[ "$?" -ne 0 ]]; then @@ -373,6 +420,12 @@ while true; do shift 2 ;; + --mount) + mount_check + shift + break + ;; + --) shift break