DATASET_ROOT=$1 DATASET_TARGET=$2 MOUNTPOINT_TARGET=$(zfs get -H mountpoint -o value $DATASET_TARGET) if ! test -d "$MOUNTPOINT_TARGET"; then echo "Please run this program with a source and a target dataset name as arguments" echo "e.g. $0 zpool/dataset1 zpool/dataset2" exit fi # make sure, that ctrl-c also works in the inner loops trap exit INT # recursively contains all snapshot names (the part after the @) from root on, sorted and only once SNAPSHOTS=$(zfs list -H -r $DATASET_ROOT -oname -tsnap | cut -d@ -f2 | sort | uniq) DATASETS=$(zfs list -H -r $DATASET_ROOT -oname -t filesystem) echo "We are going to flatten the hierachy of this dataset:" printf " %s\n" $DATASETS echo "into the dataset with the name:" echo " $DATASET_TARGET" echo "mounted under:" echo " $MOUNTPOINT_TARGET" echo "" echo "You have 10 seconds to abort this with CTRL-C" sleep 10 for SNAPSHOT in $SNAPSHOTS; do echo "" echo "" echo "===========================" echo "Next snapshot is: $SNAPSHOT" echo "===========================" for DATASET in $DATASETS; do MOUNTPOINT=$(zfs get -H mountpoint -o value $DATASET) if [[ $MOUNTPOINT == "legacy" ]]; then MOUNTPOINT=$(findmnt -n -o target $DATASET) if test -d $MOUNTPOINT; then >&2 echo "Dataset has a legacy mountpoint, but was not found in the fstab: $DATASET" continue fi fi if [[ $MOUNTPOINT == "-" ]]; then >&2 echo "Dataset does not have a mount point or is a vdev: $DATASET" continue fi SNAPDIR=$(realpath $MOUNTPOINT/.zfs/snapshot/$SNAPSHOT) if test -d $SNAPDIR; then TARGET_DIR="$(realpath $MOUNTPOINT_TARGET)$(realpath $MOUNTPOINT)" mkdir -p "$TARGET_DIR" echo rsync --info=stats3 -a "$SNAPDIR/" "$TARGET_DIR/" if ! rsync --info=stats3 -a "$SNAPDIR/" "$TARGET_DIR/" ; then >&2 echo "rsync could not entirely copy from $SNAPDIR to $DATASET_TARGET" fi fi done NEW_SNAPSHOT="$DATASET_TARGET@$SNAPSHOT" echo "Files copied, creating snapshot: $NEW_SNAPSHOT" zfs snap $NEW_SNAPSHOT done