'zfs receive' fails with "dataset is busy"

Receiving an incremental stream after an interrupted "zfs receive -s"
fails with the message "dataset is busy": this is because we still have
the hidden clone ../%recv from the resumable receive.

Improve the error message suggesting the existence of a partially
complete resumable stream from "zfs receive -s" which can be either
aborted ("zfs receive -A") or resumed ("zfs send -t").

Reviewed-by: Giuseppe Di Natale <dinatale2@llnl.gov>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: George Melikov <mail@gmelikov.ru>
Signed-off-by: loli10K <ezomori.nozomu@gmail.com>
Closes #7129 
Closes #7154
This commit is contained in:
LOLi 2018-02-12 21:28:59 +01:00 committed by Brian Behlendorf
parent a893627fac
commit c03f04708c
2 changed files with 18 additions and 2 deletions

View File

@ -6123,7 +6123,7 @@ share_mount_one(zfs_handle_t *zhp, int op, int flags, char *protocol,
(void) fprintf(stderr, gettext("cannot %s '%s': " (void) fprintf(stderr, gettext("cannot %s '%s': "
"Contains partially-completed state from " "Contains partially-completed state from "
"\"zfs receive -r\", which can be resumed with " "\"zfs receive -s\", which can be resumed with "
"\"zfs send -t\"\n"), "\"zfs send -t\"\n"),
cmdname, zfs_get_name(zhp)); cmdname, zfs_get_name(zhp));
return (1); return (1);

View File

@ -3605,6 +3605,7 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
zfs_type_t type; zfs_type_t type;
boolean_t toplevel = B_FALSE; boolean_t toplevel = B_FALSE;
boolean_t zoned = B_FALSE; boolean_t zoned = B_FALSE;
boolean_t hastoken = B_FALSE;
begin_time = time(NULL); begin_time = time(NULL);
bzero(origin, MAXNAMELEN); bzero(origin, MAXNAMELEN);
@ -3928,6 +3929,11 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
/* we want to know if we're zoned when validating -o|-x props */ /* we want to know if we're zoned when validating -o|-x props */
zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED); zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED);
/* may need this info later, get it now we have zhp around */
if (zfs_prop_get(zhp, ZFS_PROP_RECEIVE_RESUME_TOKEN, NULL, 0,
NULL, NULL, 0, B_TRUE) == 0)
hastoken = B_TRUE;
/* gather existing properties on destination */ /* gather existing properties on destination */
origprops = fnvlist_alloc(); origprops = fnvlist_alloc();
fnvlist_merge(origprops, zhp->zfs_props); fnvlist_merge(origprops, zhp->zfs_props);
@ -4186,9 +4192,19 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
break; break;
case EDQUOT: case EDQUOT:
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"destination %s space quota exceeded"), name); "destination %s space quota exceeded."), name);
(void) zfs_error(hdl, EZFS_NOSPC, errbuf); (void) zfs_error(hdl, EZFS_NOSPC, errbuf);
break; break;
case EBUSY:
if (hastoken) {
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"destination %s contains "
"partially-complete state from "
"\"zfs receive -s\"."), name);
(void) zfs_error(hdl, EZFS_BUSY, errbuf);
break;
}
/* fallthru */
default: default:
(void) zfs_standard_error(hdl, ioctl_errno, errbuf); (void) zfs_standard_error(hdl, ioctl_errno, errbuf);
} }