diff --git a/roles/debian_server/files/btrfs-backup b/roles/debian_server/files/btrfs-backup index cb65985e819cd6fbc0884e7b2739ff9f71396edb..ba57c4d81e985255d2c7125e8e026f3745b153e6 100755 --- a/roles/debian_server/files/btrfs-backup +++ b/roles/debian_server/files/btrfs-backup @@ -1,45 +1,64 @@ #!/bin/sh set -eu -list_subvolumes() { - btrfs subvolume list --sort=path "$1" | awk '{print $9}' | uniq +error() { + echo "$@" >&2 + exit 1 } -cleanup() { - for volume in $(list_subvolumes "$source/.snapshot") - do - if [ -d "$source/.snapshot/$volume" ] - then - btrfs subvolume delete "$source/.snapshot/$volume" - fi - done - btrfs subvolume delete "$source/.snapshot" - sync --file-system "$source" +usage() { + error "Usage: $0 <start|status|cleanup> <source> <destination>" } -if [ "$#" -ne 2 ] -then - echo "Usage: $0 source destination" >&2 - exit 1 -fi - -source="$1" -destination="$2" +start() { + [ "$#" -eq 2 ] || usage + source="$1" + destination="$2" + [ -d "$source" ] || error 'Source is not a btrfs volume.' + [ "$(df --output=fstype "$source" | tail +1)" = 'btrfs' ] || \ + error 'Source is not a btrfs volume.' + [ -b "$destination" ] || error 'Destination is not a block device.' + echo "This will delete all data on $destination !" >&2 + echo 'You have 5 seconds to abort with ctrl+c.' >&2 + sleep 6 + btrfs device add -f "$destination" "$source" + btrfs balance start -f --bg -dconvert=dup -mconvert=dup -sconvert=dup "$source" + echo 'Backup has started.' + echo 'You can check if it has finished by running:' + echo "$0 status $source" +} -# Before the first snapshot is made. -subvolumes="$(list_subvolumes "$source")" -trap 'cleanup' INT QUIT EXIT TERM +status() { + # Ignore the destination, if passed. + [ "$#" -eq 2 ] || [ "$#" -eq 1 ] || usage + source="$1" + [ -d "$source" ] || error 'Source is not a btrfs volume.' + [ "$(df --output=fstype "$source" | tail +1)" = 'btrfs' ] || \ + error 'Source is not a btrfs volume.' + if btrfs balance status "$source" | grep -q '^No balance found' + then + echo 'Backup has finished.' + else + echo 'Backup is still running.' + exit 2 + fi +} -# It would be better to take all snapshots atomically, but that's not possible. -btrfs subvolume snapshot "$source" "$source/.snapshot" -for volume in $subvolumes -do - rm --dir "$source/.snapshot/$volume" - btrfs subvolume snapshot "$source/$volume" "$source/.snapshot/$volume" -done -sync --file-system "$source" +cleanup() { + # Ignore the destination, if passed. + [ "$#" -eq 2 ] || [ "$#" -eq 1 ] || usage + source="$1" + [ -d "$source" ] || error 'Source is not a btrfs volume.' + [ "$(df --output=fstype "$source" | tail +1)" = 'btrfs' ] || \ + error 'Source is not a btrfs volume.' + btrfs device remove "$destination" "$source" + btrfs balance start --force -dconvert=single -mconvert=single -sconvert=single"$source" +} -rsync --archive \ - --delete \ - "$source/.snapshot/" \ - "$destination" +[ "$#" -gt 1 ] || usage +case "$1" in + start) shift; start "$@";; + status) shift; status "$@";; + cleanup) shift; cleanup "$@";; + *) usage;; +esac