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.
This commit is contained in:
Jarno Rankinen 2021-01-05 00:39:13 +02:00
parent d003820acb
commit a13cba3790
1 changed files with 104 additions and 51 deletions

155
snapsh
View File

@ -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