-#!/bin/bash -e
+#!/bin/bash
#
# btrfs-mksnapshot -- Make snapshots of btrfs filesystems
-# Copyright (c)2013 Alexander Barton (alex@barton.de)
+# 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
# (at your option) any later version.
#
-NAME=`basename "$0"`
+NAME=$(basename "$0")
-function Usage()
+Usage()
{
echo "Usage: $NAME [-1] [<path>]"; echo
echo " -1 Create only one shapshot per day."
while [ $# -ge 1 ]; do
case "$1" in
- [^\-]*)
+ [^-]*)
break
;;
-1)
[ -z "$DRY_RUN" ] || echo "VOLUME_PATH=$VOLUME_PATH"
# Detect btrfs filesystem
-FS_NAME=`grep " btrfs " /etc/fstab \
- | grep "^LABEL=" \
- | grep " $VOLUME_PATH " \
- | cut -d'"' -f2 \
- | cut -d'=' -f2- \
- | cut -d' ' -f1`
+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
[ -z "$DRY_RUN" ] || echo "FS_NAME=$FS_NAME"
# Detect btrfs subvolume name
-VOLUME_NAME=`grep " btrfs " /etc/fstab \
- | grep "subvol=" \
- | grep " $VOLUME_PATH " \
- | sed -r 's|.*subvol=([[:alnum:]]*).*|\1|'`
+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)
-[ -z "$DRY_RUN" ] || echo "VOLUME_NAME=$VOLUME_NAME"
+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"
echo "$NAME: Directory \"$FS_MOUNT_PATH\" does not exist!"
exit 1
fi
-grep " btrfs " /proc/mounts | grep " $FS_MOUNT_PATH " >/dev/null 2>&1
-if [ $? -ne 0 ]; then
+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"
-[ -z "$DRY_RUN" ] || exit 0
-
# Generate snapshot name
i=1
-date=`date +%Y%m%d`
+date=$(date +%Y%m%d)
while true; do
id="$date-$i"
- [ -e "$FS_MOUNT_PATH/$VOLUME_NAME-$id" ] || break
+ [ -e "$FS_MOUNT_PATH/$BASE_VOLUME_NAME@$id" ] || break
if [ -n "$ONLY_ONE_PER_DAY" ]; then
- echo "Daily snapshot of \"$VOLUME_PATH\" (\"$VOLUME_NAME\") in btrfs \"$FS_NAME\" already exists."
+ echo "Snapshot \"$BASE_VOLUME_NAME@$id\" of \"$VOLUME_PATH\" (\"$VOLUME_NAME\") in btrfs \"$FS_NAME\" already exists."
exit 0
fi
- i=`expr $i + 1`
+ 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"
-btrfs subvolume snapshot "$VOLUME_NAME" "$VOLUME_NAME-$id"
-find . -maxdepth 1 -name "$VOLUME_NAME"'*' -exec ls -1d {} \; | column
+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