#!/bin/sh # This script expects a source dataset and a target dataset as arguments. It # will then copy each snapshot of the source dataset into the target as a # subfolder with the name of the dataset. This is being archived by cloning the # source-dataset first into a sub-dataset of the source dataset named after the # snapshot. From there rsync is used to copy files into the target location. # After that finishes a snapshot will be created in the target with the original # snapshot-name and a suffix, which is the last part of the source dataset. # # Known Bugs: # # * This script will immidiatly stop when any command returns an error and it # will not clean up automatically the clones it created. But these clones are # not promoted by the script and can be deleted without altering the source # dataset and in order to re-execute the script after fixing the reason for the # error. # # * There is currently no sanity checking. Please do not copy files from a # dataset, which is a child of the source dataset. # # Example: # # ./zfs-squash-datasets.sh zpool/software zpool/projects # # would create a folder /zpool/projects/software within the dataset # zpool/projects set -o errexit getMountPoint() { zfs get -H -o value mountpoint "$1" ||\ (echo "mountpoint for dataset '$1' not found" ; exit 1) } listAllSnapshots() { zfs list -H -o name -tsnap -r "$1" ||\ (echo "Could not find snapshots under $1" ; exit 2) } DATASET=$1 DATASET_NAME=`basename $DATASET` DATASET_TARGET=$2 TARGET_PATH=`basename $DATASET_TARGET` MP_TARGET=`getMountPoint $2` test -d "$MP_TARGET" || (echo "failed to find mountpoint for $DATASET_TARGET: Its not $MP_TARGET" ; exit 3) for i in `listAllSnapshots $DATASET`; do SNAPSHOT=`echo "$i" | cut -d@ -f2` CLONETMP="$DATASET/$SNAPSHOT" zfs clone "$i" "$CLONETMP" ||\ (echo "Failed to create clone" ; exit 4) MP_CLONE=`getMountPoint $CLONETMP` test -d "$MP_CLONE" ||\ (echo "$CLONETMP: clones mount point not found" ; exit 5) # copy all files to the target location (set -x ; rsync -ahox --info=progress2 --delete "$MP_CLONE/" "$MP_TARGET/$TARGET_PATH" ||\ (echo "rsync exit status was $?" ; exit 6) ) (set -x ; zfs snap "$DATASET_TARGET@$SNAPSHOT-$DATASET_NAME" ||\ (echo "failed to create a snapshot $DATASET_TARGET: $SNAPSHOT-$DATASET_NAME" ; exit 7) ) # move clone out of the source dataset, so that we can resume # zfs promote "$CLONETMP" ||\ # (echo "Failed to promote clone '$CLONETMP'" ; exit 8) zfs destroy "$CLONETMP" ||\ (echo "Failed to destroy clone '$CLONETMP'" ; exit 8) # for better readability add two blank lines... echo echo done