Skip to content

Instantly share code, notes, and snippets.

@frizz925
Created July 7, 2023 12:56
Show Gist options
  • Save frizz925/d3d9a97ddd1e334d5144feb56ac862a4 to your computer and use it in GitHub Desktop.
Save frizz925/d3d9a97ddd1e334d5144feb56ac862a4 to your computer and use it in GitHub Desktop.
Bash script to manage Btrfs snapshots
#!/bin/bash
set -eo pipefail
SCRIPT_PATH=$0
MOUNT_DEV=/dev/mapper/fedora-root
MOUNT_PATH=/mnt
SNAPSHOTS_SUBVOL=.snapshots
SNAPSHOTS_PATH="$MOUNT_PATH/$SNAPSHOTS_SUBVOL"
SOURCE_SUBVOLS=(base usr_local)
cleanup() {
mountpoint -q $MOUNT_PATH && umount $MOUNT_PATH
}
trap cleanup EXIT
mount_fs() {
if ! mountpoint -q $MOUNT_PATH; then
[ ! -d $MOUNT_PATH ] && mkdir -p $MOUNT_PATH
mount $MOUNT_DEV $MOUNT_PATH
fi
}
ACTION=$1
if [ $# -lt 1 ]; then
echo "Usage: $0 <create|list|restore|delete|replace>" >&2
exit 1
fi
SNAPSHOT_NAME=$2
if [ -z "$SNAPSHOT_NAME" ]; then
SNAPSHOT_NAME=latest
fi
SNAPSHOT_PATH=$SNAPSHOTS_PATH/$SNAPSHOT_NAME
create_snapshot() {
if [ -d $SNAPSHOT_PATH ]; then
if [ "$SNAPSHOT_NAME" != "latest" ]; then
echo "Snapshot exists: $SNAPSHOT_NAME" >&2
exit 1
fi
if [ ! -s $SNAPSHOT_PATH/timestamp ]; then
echo "Latest snapshot missing the timestamp file" >&2
exit 1
fi
SNAPSHOT_TS=$(cat $SNAPSHOT_PATH/timestamp)
rm $SNAPSHOT_PATH/timestamp
mv $SNAPSHOT_PATH $SNAPSHOTS_PATH/$SNAPSHOT_TS
fi
for SUBVOL in ${SOURCE_SUBVOLS[@]}; do
[ ! -d $SNAPSHOT_PATH ] && mkdir -p $SNAPSHOT_PATH
btrfs subvol snap -r $MOUNT_PATH/$SUBVOL $SNAPSHOT_PATH/$SUBVOL
done
if [ "$SNAPSHOT_NAME" = "latest" ]; then
date +%FT%T > $SNAPSHOT_PATH/timestamp
fi
}
ensure_snapshot() {
if [ ! -d $SNAPSHOT_PATH ]; then
echo "Snapshot doesn't exist: $SNAPSHOT_NAME" >&2
exit 1
fi
}
delete_snapshot() {
ensure_snapshot
for SUBVOL in ${SOURCE_SUBVOLS[@]}; do
[ -d $SNAPSHOT_PATH/$SUBVOL ] && btrfs subvol delete $SNAPSHOT_PATH/$SUBVOL
done
[ -f $SNAPSHOT_PATH/timestamp ] && rm $SNAPSHOT_PATH/timestamp
rmdir $SNAPSHOT_PATH
}
restore_snapshot() {
ensure_snapshot
for SUBVOL in ${SOURCE_SUBVOLS[@]}; do
rsync -avruP --delete $SNAPSHOT_PATH/$SUBVOL $MOUNT_PATH/$SUBVOL
done
}
replace_snapshot() {
[ -d $SNAPSHOT_PATH ] && delete_snapshot
create_snapshot
}
mount_fs
case "$ACTION" in
create)
create_snapshot
;;
restore)
restore_snapshot
;;
delete)
delete_snapshot
;;
replace)
replace_snapshot
;;
list)
ls -1 $SNAPSHOTS_PATH
;;
esac
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment