#!/bin/bash # # btrfs-mksnapshot -- Make snapshots of btrfs filesystems # Copyright (c)2013-2020 Alexander Barton (alex@barton.de) # # 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 2 of the License, or # (at your option) any later version. # NAME=$(basename "$0") Usage() { echo "Usage: $NAME [-1] []"; echo echo " -1 Create only one shapshot per day." echo " -n Dry run, perform a trial run with no changes made." echo " Volume root path (default: \"/\")." echo; exit 1 } ONLY_ONE_PER_DAY= DRY_RUN= while [ $# -ge 1 ]; do case "$1" in [^-]*) break ;; -1) ONLY_ONE_PER_DAY=1 ;; -n) DRY_RUN=1 ;; *) Usage esac shift done [ $# -gt 1 ] && Usage [ -z "$DRY_RUN" ] || echo "ONLY_ONE_PER_DAY=$ONLY_ONE_PER_DAY" # The subvolume to snapshot, "/" by default [ $# -ge 1 ] && VOLUME_PATH="$1" || VOLUME_PATH="/" [ -z "$DRY_RUN" ] || echo "VOLUME_PATH=$VOLUME_PATH" # Detect btrfs filesystem FS_NAME=$(btrfs filesystem show -m "$VOLUME_PATH" 2>/dev/null | head -1 | cut -d"'" -f2) if [ -z "$FS_NAME" ]; then echo "$NAME: Failed to detect btrfs filesystem label for \"$VOLUME_PATH\"!" exit 1 fi [ -z "$DRY_RUN" ] || echo "FS_NAME=$FS_NAME" # Detect btrfs subvolume name VOLUME_NAME=$(findmnt -nf -o source "$VOLUME_PATH" | grep -F '[' | sed -r 's|^.*\[/(.*)\]|\1|') if [ -z "$VOLUME_NAME" ] && [ "$VOLUME_PATH" = "/" ]; then VOLUME_NAME=$(btrfs subvolume get-default / | cut -d' ' -f9) fi if [ -z "$VOLUME_NAME" ]; then echo "$NAME: Failed to detect btrfs subvolume name for \"$VOLUME_PATH\"!" exit 1 fi BASE_VOLUME_NAME=$(echo "$VOLUME_NAME" | cut -d'@' -f1) if [ -n "$DRY_RUN" ]; then echo "VOLUME_NAME=$VOLUME_NAME" echo "BASE_VOLUME_NAME=$BASE_VOLUME_NAME" fi # Detect mount point of the whole btrfs filesystem FS_MOUNT_PATH="/media/$FS_NAME" if [ ! -d "$FS_MOUNT_PATH" ]; then echo "$NAME: Directory \"$FS_MOUNT_PATH\" does not exist!" exit 1 fi if ! grep -q " $FS_MOUNT_PATH btrfs " /proc/mounts; then echo "$NAME: btrfs \"$FS_NAME\" seems not to be mounted on \"$FS_MOUNT_PATH\"!" exit 1 fi [ -z "$DRY_RUN" ] || echo "FS_MOUNT_PATH=$FS_MOUNT_PATH" # Generate snapshot name i=1 date=$(date +%Y%m%d) while true; do id="$date-$i" [ -e "$FS_MOUNT_PATH/$BASE_VOLUME_NAME@$id" ] || break if [ -n "$ONLY_ONE_PER_DAY" ]; then echo "Snapshot \"$BASE_VOLUME_NAME@$id\" of \"$VOLUME_PATH\" (\"$VOLUME_NAME\") in btrfs \"$FS_NAME\" already exists." exit 0 fi i=$((i + 1)) done NEW_VOLUME_NAME="$BASE_VOLUME_NAME@$id" [ -z "$DRY_RUN" ] || echo "NEW_VOLUME_NAME=$NEW_VOLUME_NAME" [ -z "$DRY_RUN" ] || exit 0 echo "Creating snapshot of \"$VOLUME_PATH\" (\"$VOLUME_NAME\") in btrfs \"$FS_NAME\" ..." set -x cd "$FS_MOUNT_PATH" || exit 1 btrfs subvolume snapshot -r "$VOLUME_NAME" "$NEW_VOLUME_NAME" find . -maxdepth 1 \( -name "$BASE_VOLUME_NAME" -o \ -name "$BASE_VOLUME_NAME"@'*' \) -exec ls -1d {} \; | sort | column