Fix intra-pool resumable 'zfs send -t <token>'

Because resuming from a token requires "guid" -> "snapshot" mapping
we have to walk the whole dataset hierarchy to find the right snapshot
to send; when both source and destination exists, for an incremental
resumable stream, libzfs gets confused and picks up the wrong snapshot
to send from: this results in attempting to send

   "destination@snap1 -> source@snap2"

instead of

   "source@snap1 -> source@snap2"

which fails with a "Invalid cross-device link" error (EXDEV).

Fix this by adjusting the logic behind dataset traversal in
zfs_iter_children() to pick the right snapshot to send from.

Additionally update dry-run 'zfs send -t' to print its output to
stderr: this is consistent with other dry-run commands.

Reviewed-by: George Melikov <mail@gmelikov.ru>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: loli10K <ezomori.nozomu@gmail.com>
Closes #6618
Closes #6619
Closes #6623
This commit is contained in:
LOLi 2017-10-11 00:22:05 +02:00 committed by Tony Hutter
parent 91b2f6ab1c
commit 926c6ec453
9 changed files with 18 additions and 17 deletions

View File

@ -428,10 +428,10 @@ zfs_iter_children(zfs_handle_t *zhp, zfs_iter_f func, void *data)
{ {
int ret; int ret;
if ((ret = zfs_iter_filesystems(zhp, func, data)) != 0) if ((ret = zfs_iter_snapshots(zhp, B_FALSE, func, data)) != 0)
return (ret); return (ret);
return (zfs_iter_snapshots(zhp, B_FALSE, func, data)); return (zfs_iter_filesystems(zhp, func, data));
} }

View File

@ -1609,6 +1609,7 @@ zfs_send_resume(libzfs_handle_t *hdl, sendflags_t *flags, int outfd,
int error = 0; int error = 0;
char name[ZFS_MAX_DATASET_NAME_LEN]; char name[ZFS_MAX_DATASET_NAME_LEN];
enum lzc_send_flags lzc_flags = 0; enum lzc_send_flags lzc_flags = 0;
FILE *fout = (flags->verbose && flags->dryrun) ? stdout : stderr;
(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
"cannot resume send")); "cannot resume send"));
@ -1623,9 +1624,9 @@ zfs_send_resume(libzfs_handle_t *hdl, sendflags_t *flags, int outfd,
return (zfs_error(hdl, EZFS_FAULT, errbuf)); return (zfs_error(hdl, EZFS_FAULT, errbuf));
} }
if (flags->verbose) { if (flags->verbose) {
(void) fprintf(stderr, dgettext(TEXT_DOMAIN, (void) fprintf(fout, dgettext(TEXT_DOMAIN,
"resume token contents:\n")); "resume token contents:\n"));
nvlist_print(stderr, resume_nvl); nvlist_print(fout, resume_nvl);
} }
if (nvlist_lookup_string(resume_nvl, "toname", &toname) != 0 || if (nvlist_lookup_string(resume_nvl, "toname", &toname) != 0 ||
@ -1682,7 +1683,7 @@ zfs_send_resume(libzfs_handle_t *hdl, sendflags_t *flags, int outfd,
lzc_flags, &size); lzc_flags, &size);
if (error == 0) if (error == 0)
size = MAX(0, (int64_t)(size - bytes)); size = MAX(0, (int64_t)(size - bytes));
send_print_verbose(stderr, zhp->zfs_name, fromname, send_print_verbose(fout, zhp->zfs_name, fromname,
size, flags->parsable); size, flags->parsable);
} }

View File

@ -513,11 +513,13 @@ function test_fs_setup
{ {
typeset sendfs=$1 typeset sendfs=$1
typeset recvfs=$2 typeset recvfs=$2
typeset streamfs=$3
typeset sendpool=${sendfs%%/*} typeset sendpool=${sendfs%%/*}
typeset recvpool=${recvfs%%/*} typeset recvpool=${recvfs%%/*}
datasetexists $sendfs && log_must zfs destroy -r $sendpool datasetexists $sendfs && log_must zfs destroy -r $sendpool
datasetexists $recvfs && log_must zfs destroy -r $recvpool datasetexists $recvfs && log_must zfs destroy -r $recvpool
datasetexists $streamfs && log_must zfs destroy -r $streamfs
if $(datasetexists $sendfs || zfs create -o compress=lz4 $sendfs); then if $(datasetexists $sendfs || zfs create -o compress=lz4 $sendfs); then
mk_files 1000 256 0 $sendfs & mk_files 1000 256 0 $sendfs &
@ -546,10 +548,7 @@ function test_fs_setup
">/$sendpool/incremental.zsend" ">/$sendpool/incremental.zsend"
fi fi
if datasetexists $streamfs; then log_must zfs create -o compress=lz4 $streamfs
log_must zfs destroy -r $streamfs
fi
log_must zfs create -o compress=lz4 $sendpool/stream
} }
# #
@ -663,9 +662,10 @@ function resume_cleanup
{ {
typeset sendfs=$1 typeset sendfs=$1
typeset streamfs=$2 typeset streamfs=$2
typeset sendpool=${sendfs%%/*}
datasetexists $sendfs && log_must zfs destroy -r $sendfs datasetexists $sendfs && log_must zfs destroy -r $sendfs
datasetexists $streamfs && log_must zfs destroy -r $streamfs datasetexists $streamfs && log_must zfs destroy -r $streamfs
cleanup_pool $POOL2 cleanup_pool $POOL2
rm -f /$POOL/initial.zsend /$POOL/incremental.zsend rm -f /$sendpool/initial.zsend /$sendpool/incremental.zsend
} }

View File

@ -48,8 +48,8 @@ sendfs=$POOL/sendfs
recvfs=$POOL3/recvfs recvfs=$POOL3/recvfs
streamfs=$POOL2/stream streamfs=$POOL2/stream
for sendfs in $POOL2/sendfs $POOL2; do for sendfs in $POOL2/sendfs $POOL3; do
test_fs_setup $sendfs $recvfs test_fs_setup $sendfs $recvfs $streamfs
resume_test "zfs send -v $sendfs@a" $streamfs $recvfs resume_test "zfs send -v $sendfs@a" $streamfs $recvfs
resume_test "zfs send -v -i @a $sendfs@b" $streamfs $recvfs resume_test "zfs send -v -i @a $sendfs@b" $streamfs $recvfs
file_check $sendfs $recvfs file_check $sendfs $recvfs

View File

@ -42,7 +42,7 @@ streamfs=$POOL/stream
log_onexit resume_cleanup $sendfs $streamfs log_onexit resume_cleanup $sendfs $streamfs
test_fs_setup $sendfs $recvfs test_fs_setup $sendfs $recvfs $streamfs
resume_test "zfs send -D -v $sendfs@a" $streamfs $recvfs resume_test "zfs send -D -v $sendfs@a" $streamfs $recvfs
file_check $sendfs $recvfs file_check $sendfs $recvfs

View File

@ -44,7 +44,7 @@ streamfs=$POOL/stream
log_onexit resume_cleanup $sendfs $streamfs log_onexit resume_cleanup $sendfs $streamfs
test_fs_setup $sendfs $recvfs test_fs_setup $sendfs $recvfs $streamfs
resume_test "zfs send -v -e $sendfs@a" $streamfs $recvfs resume_test "zfs send -v -e $sendfs@a" $streamfs $recvfs
resume_test "zfs send -v -e -i @a $sendfs@b" $streamfs $recvfs resume_test "zfs send -v -e -i @a $sendfs@b" $streamfs $recvfs
file_check $sendfs $recvfs file_check $sendfs $recvfs

View File

@ -52,7 +52,7 @@ streamfs=$POOL/stream
log_onexit resume_cleanup $sendfs $streamfs log_onexit resume_cleanup $sendfs $streamfs
test_fs_setup $sendfs $recvfs test_fs_setup $sendfs $recvfs $streamfs
log_must zfs bookmark $sendfs@a $sendfs#bm_a log_must zfs bookmark $sendfs@a $sendfs#bm_a
log_must zfs destroy $sendfs@a log_must zfs destroy $sendfs@a
log_must zfs receive -v $recvfs </$POOL/initial.zsend log_must zfs receive -v $recvfs </$POOL/initial.zsend

View File

@ -49,7 +49,7 @@ streamfs=$POOL/stream
log_onexit resume_cleanup $sendfs $streamfs log_onexit resume_cleanup $sendfs $streamfs
test_fs_setup $sendfs $recvfs test_fs_setup $sendfs $recvfs $streamfs
log_must zfs unmount $sendfs log_must zfs unmount $sendfs
resume_test "zfs send $sendfs" $streamfs $recvfs resume_test "zfs send $sendfs" $streamfs $recvfs
file_check $sendfs $recvfs file_check $sendfs $recvfs

View File

@ -41,7 +41,7 @@ streamfs=$POOL/stream
log_assert "Verify compressed send streams can be resumed if interrupted" log_assert "Verify compressed send streams can be resumed if interrupted"
log_onexit resume_cleanup $sendfs $streamfs log_onexit resume_cleanup $sendfs $streamfs
test_fs_setup $sendfs $recvfs test_fs_setup $sendfs $recvfs $streamfs
resume_test "zfs send -c -v $sendfs@a" $streamfs $recvfs resume_test "zfs send -c -v $sendfs@a" $streamfs $recvfs
resume_test "zfs send -c -v -i @a $sendfs@b" $streamfs $recvfs resume_test "zfs send -c -v -i @a $sendfs@b" $streamfs $recvfs
file_check $sendfs $recvfs file_check $sendfs $recvfs