diff --git a/syncoid b/syncoid index b57aa43..f5715a2 100755 --- a/syncoid +++ b/syncoid @@ -837,15 +837,16 @@ sub syncdataset { } if (defined $args{'no-sync-snap'}) { if (defined $args{'create-bookmark'}) { - my $ret = createbookmark($sourcehost, $sourcefs, $newsyncsnap, $newsyncsnap); + my %existingbookmarks = getbookmarks($sourcehost,$sourcefs,$sourceisroot); + my $guid = $snaps{'source'}{$newsyncsnap}{'guid'}; + my $ret = createbookmark($sourcehost, $sourcefs, $newsyncsnap, $newsyncsnap, $guid, %existingbookmarks); $ret == 0 or do { # fallback: assume naming conflict and try again with guid based suffix - my $guid = $snaps{'source'}{$newsyncsnap}{'guid'}; - $guid = substr($guid, 0, 6); + my $suffix = substr($guid, 0, 6); - writelog('INFO', "bookmark creation failed, retrying with guid based suffix ($guid)..."); + writelog('INFO', "bookmark creation failed, retrying with guid based suffix ($suffix)..."); - my $ret = createbookmark($sourcehost, $sourcefs, $newsyncsnap, "$newsyncsnap$guid"); + my $ret = createbookmark($sourcehost, $sourcefs, $newsyncsnap, "$newsyncsnap$suffix", $guid, %existingbookmarks); $ret == 0 or do { if ($exitcode < 2) { $exitcode = 2; } return 0; @@ -1066,7 +1067,12 @@ sub syncbookmark { } # end syncbookmark sub createbookmark { - my ($sourcehost, $sourcefs, $snapname, $bookmark) = @_; + my ($sourcehost, $sourcefs, $snapname, $bookmark, $guid, %existingbookmarks) = @_; + + if (defined $existingbookmarks{$guid} && $existingbookmarks{$guid}{'name'} eq $bookmark) { + writelog('INFO', "bookmark already exists, skipping creation"); + return 0; + } my $sourcefsescaped = escapeshellparam($sourcefs); my $bookmarkescaped = escapeshellparam($bookmark); diff --git a/tests/syncoid/12_bookmark_check_guid_on_existing/run.sh b/tests/syncoid/12_bookmark_check_guid_on_existing/run.sh new file mode 100755 index 0000000..f25d72e --- /dev/null +++ b/tests/syncoid/12_bookmark_check_guid_on_existing/run.sh @@ -0,0 +1,93 @@ +#!/bin/bash + +# test if guid of existing bookmark matches new guid + +set -x +set -e + +. ../../common/lib.sh + +POOL_IMAGE="/tmp/syncoid-test-12.zpool" +MOUNT_TARGET="/tmp/syncoid-test-12.mount" +POOL_SIZE="1000M" +POOL_NAME="syncoid-test-12" + +truncate -s "${POOL_SIZE}" "${POOL_IMAGE}" + +zpool create -m "${MOUNT_TARGET}" -f "${POOL_NAME}" "${POOL_IMAGE}" + +function cleanUp { + zpool export "${POOL_NAME}" +} + +function getGuid { + zfs get -H guid "$1" | awk '{print $3}' +} + +# export pool in any case +trap cleanUp EXIT + +zfs create "${POOL_NAME}/a" +zfs snapshot "${POOL_NAME}/a@s0" + +# This fully replicates a to b +../../../syncoid --debug --no-sync-snap --no-rollback --create-bookmark "${POOL_NAME}"/a "${POOL_NAME}"/b + +# This fully replicates a to c +../../../syncoid --debug --no-sync-snap --no-rollback --create-bookmark "${POOL_NAME}"/a "${POOL_NAME}"/c + +bookmark_guid=$(getGuid "${POOL_NAME}/a#s0") +snap_a_guid=$(getGuid "${POOL_NAME}/a@s0") +snap_b_guid=$(getGuid "${POOL_NAME}/b@s0") +snap_c_guid=$(getGuid "${POOL_NAME}/c@s0") + +# Bookmark guid should equal guid of all snapshots +if [ "${bookmark_guid}" != "${snap_a_guid}" ] || \ + [ "${bookmark_guid}" != "${snap_b_guid}" ] || \ + [ "${bookmark_guid}" != "${snap_c_guid}" ]; then + exit 1 +fi + +bookmark_suffix="${bookmark_guid:0:6}" +fallback_bookmark="${POOL_NAME}/a#s0${bookmark_suffix}" + +# Fallback bookmark should not exist +if zfs get guid "${fallback_bookmark}"; then + exit 1 +fi + +zfs snapshot "${POOL_NAME}/a@s1" + +# Create bookmark so syncoid is forced to create fallback bookmark +zfs bookmark "${POOL_NAME}/a@s0" "${POOL_NAME}/a#s1" + +# This incrementally replicates from a@s0 to a@s1 and should create a +# bookmark with fallback suffix +../../../syncoid --debug --no-sync-snap --no-rollback --create-bookmark "${POOL_NAME}"/a "${POOL_NAME}"/b + +snap_guid=$(getGuid "${POOL_NAME}/a@s1") +bookmark_suffix="${snap_guid:0:6}" +fallback_bookmark="${POOL_NAME}/a#s1${bookmark_suffix}" + +# Fallback bookmark guid should equal guid of snapshot +if [ "$(getGuid "${fallback_bookmark}")" != "${snap_guid}" ]; then + exit 1 +fi + +zfs snapshot "${POOL_NAME}/a@s2" + +snap_guid=$(getGuid "${POOL_NAME}/a@s2") +bookmark_suffix="${snap_guid:0:6}" +fallback_bookmark="${POOL_NAME}/a#s2${bookmark_suffix}" + +# Create bookmark and fallback bookmark so syncoid should fail +zfs bookmark "${POOL_NAME}/a@s0" "${POOL_NAME}/a#s2" +zfs bookmark "${POOL_NAME}/a@s0" "${fallback_bookmark}" + +# This incrementally replicates from a@s1 to a@s2 and should fail to create a +# bookmark with fallback suffix +if ../../../syncoid --debug --no-sync-snap --no-rollback --create-bookmark "${POOL_NAME}"/a "${POOL_NAME}"/b; then + exit 1 +fi + +exit 0