#!/usr/bin/env bash ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## btrfs-snapshots.sh ## ## ## ## Take read-only snapshots of BTRFS ## ## subvolumes ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## Information ## (don't touch unless you are me) ## NAME="btrfs-snapshots.sh" VERSION="0.2.0" AUTHOR="david@socialnerds.org" LICENSE="MIT" DESCRIPTION="Take read-only snapshots of BTRFS subvolumes." WEBSITE="https://git.socialnerds.org/david/scripts" CHANGELOG=("[2023-10-26][v0.2.0] Complete rewrite" "[2021-01-01][v0.1.0] Initial version") ## ## Configuration ## EXECUTABLE="$(basename $0)" LIBRARIES="lib.sh" DEPENDENCIES="basename dirname readlink ln mkdir rm ls date btrfs" REQUIRE_ROOT=1 SUBVOLUMES="/" DESTINATION_ROOT=/srv/snapshots PREFIX="snapshot-" TIMESTAMP=$(date +%Y%m%d%H%M) #add %S if you want sub-minute snapshots NAME="$PREFIX$TIMESTAMP" # How many snapshots should be kept? MAX_SNAPSHOTS=32 ## ## Functions ## # Print help information function print_help() { printf "%s\n\n%s\n%b\n\n%s\n %-15s %s\n %-15s %s\n %-15s %s\n %-15s %s\n" \ "$DESCRIPTION" "Usage:" "$LIB_BOLD$EXECUTABLE $LIB_CLEAR" "Options:" \ "-h, --help" "Print help screen and exit" \ "-i, --info" "Print script information and exit" \ "-v, --verbose" "More verbose output" \ "-q, --quiet" "No output except errors (overrides -v)" } ## ## Preflight ## # Load BASH libraries SCRIPT_PATH=$(readlink -f "$0") SCRIPT_DIR=$(dirname "$SCRIPT_PATH") for LIBRARY in $LIBRARIES; do if [[ -r "$SCRIPT_DIR/$LIBRARY" ]]; then #echo "Loading library file [$SCRIPT_DIR/$LIBRARY]" source "$SCRIPT_DIR/$LIBRARY" else echo "Error: Cannot load library file [$SCRIPT_DIR/$LIBRARY]" exit 1 fi done # Check for root privileges if ! lib_amiroot && [[ $REQUIRE_ROOT -eq 1 ]]; then lib_print "!You need to have root privileges" exit 1 fi # Check for dependencies MISSING_COMMANDS=$(lib_missing_commands $DEPENDENCIES) if [[ -n "$MISSING_COMMANDS" ]]; then lib_print "!One or more commands missing [$MISSING_COMMANDS]" lib_print "Try installing them with your package manager" exit 1 fi ## ## Liftoff ## exit 0 # take the snapshots for SUBVOLUME in $SUBVOLUMES; do if [ -z ${SUBVOLUME#/} ]; then SNAPSHOT_PATH="$DESTINATION_ROOT" else PATH="$DESTINATION_ROOT/${SUBVOLUME#/}" fi if [ ! -d $SNAPSHOT_PATH ]; then echo "warning: snapshot path ($SNAPSHOT_PATH) does not yet exist. creating." mkdir -p $SNAPSHOT_PATH fi if [ -d $SNAPSHOT_PATH/$NAME ]; then echo "warning: snapshots with same name already exists. skipping." else #TODO: handle failed snapshots and failed link creations btrfs -q subvolume snapshot -r $SUBVOLUME $SNAPSHOT_PATH/$NAME if [ -h $SNAPSHOT_PATH/latest ]; then rm $SNAPSHOT_PATH/latest fi ln -sf $SNAPSHOT_PATH/$NAME $SNAPSHOT_PATH/latest fi done # Delete old snapshots SNAPSHOTS=$(ls -r $SNAPSHOT_PATH | grep $PREFIX) i=0 for SNAPSHOT in $SNAPSHOTS; do if [ $i -ge $MAX_SNAPSHOTS ]; then #TODO: handle failed snapshot deletions btrfs -q subvolume delete $SNAPSHOT_PATH/$SNAPSHOT fi i=$((i+1)) done ## ## Here be dragons ## exit 0