Skip to content
Snippets Groups Projects
Commit fc06953e authored by nimrod's avatar nimrod
Browse files

Another take on btrfs pool backup.

The thought is this: convert the pool to a mirror, add the destination
to the pool, wait for everything to duplicate, remove the destination
and convert back to a single device pool. The upside is that all of the
subvolumes are copied without any special effort and that the state of
them is synced entirely. The downside is that it doesn't work (I think
it's the superblock), one of the devices ends up as broken. Further work
is needed.
parent c5fd0196
Branches btrfs-backup
No related tags found
No related merge requests found
Pipeline #1381 passed
#!/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
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment