-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
OpenZFS 4986 - receiving replication stream fails if any snapshot exc…
…eeds refquota Authored by: Dan McDonald <[email protected]> Reviewed by: John Kennedy <[email protected]> Reviewed by: Matthew Ahrens <[email protected]> Approved by: Gordon Ross <[email protected]> Ported-by: Brian Behlendorf <[email protected]> OpenZFS-issue: https://www.illumos.org/issues/4986 OpenZFS-commit: openzfs/openzfs@5878fad
- Loading branch information
1 parent
f8866f8
commit 671c935
Showing
2 changed files
with
121 additions
and
19 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -26,6 +26,7 @@ | |
* Copyright (c) 2012 Pawel Jakub Dawidek <[email protected]>. | ||
* All rights reserved | ||
* Copyright (c) 2013 Steven Hartland. All rights reserved. | ||
* Copyright 2015, OmniTI Computer Consulting, Inc. All rights reserved. | ||
*/ | ||
|
||
#include <assert.h> | ||
|
@@ -66,7 +67,7 @@ extern void zfs_setprop_error(libzfs_handle_t *, zfs_prop_t, int, char *); | |
|
||
static int zfs_receive_impl(libzfs_handle_t *, const char *, const char *, | ||
recvflags_t *, int, const char *, nvlist_t *, avl_tree_t *, char **, int, | ||
uint64_t *); | ||
uint64_t *, const char *); | ||
static int guid_to_name(libzfs_handle_t *, const char *, | ||
uint64_t, boolean_t, char *); | ||
|
||
|
@@ -2621,6 +2622,7 @@ zfs_receive_package(libzfs_handle_t *hdl, int fd, const char *destname, | |
nvlist_t *stream_nv = NULL; | ||
avl_tree_t *stream_avl = NULL; | ||
char *fromsnap = NULL; | ||
char *sendsnap = NULL; | ||
char *cp; | ||
char tofs[ZFS_MAXNAMELEN]; | ||
char sendfs[ZFS_MAXNAMELEN]; | ||
|
@@ -2769,8 +2771,16 @@ zfs_receive_package(libzfs_handle_t *hdl, int fd, const char *destname, | |
*/ | ||
(void) strlcpy(sendfs, drr->drr_u.drr_begin.drr_toname, | ||
ZFS_MAXNAMELEN); | ||
if ((cp = strchr(sendfs, '@')) != NULL) | ||
if ((cp = strchr(sendfs, '@')) != NULL) { | ||
*cp = '\0'; | ||
/* | ||
* Find the "sendsnap", the final snapshot in a replication | ||
* stream. zfs_receive_one() handles certain errors | ||
* differently, depending on if the contained stream is the | ||
* last one or not. | ||
*/ | ||
sendsnap = (cp + 1); | ||
} | ||
|
||
/* Finally, receive each contained stream */ | ||
do { | ||
|
@@ -2783,7 +2793,7 @@ zfs_receive_package(libzfs_handle_t *hdl, int fd, const char *destname, | |
*/ | ||
error = zfs_receive_impl(hdl, destname, NULL, flags, fd, | ||
sendfs, stream_nv, stream_avl, top_zfs, cleanup_fd, | ||
action_handlep); | ||
action_handlep, sendsnap); | ||
if (error == ENODATA) { | ||
error = 0; | ||
break; | ||
|
@@ -2948,7 +2958,7 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap, | |
const char *originsnap, recvflags_t *flags, dmu_replay_record_t *drr, | ||
dmu_replay_record_t *drr_noswap, const char *sendfs, nvlist_t *stream_nv, | ||
avl_tree_t *stream_avl, char **top_zfs, int cleanup_fd, | ||
uint64_t *action_handlep) | ||
uint64_t *action_handlep, const char *finalsnap) | ||
{ | ||
zfs_cmd_t zc = {"\0"}; | ||
time_t begin_time; | ||
|
@@ -2965,6 +2975,7 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap, | |
nvlist_t *snapprops_nvlist = NULL; | ||
zprop_errflags_t prop_errflags; | ||
boolean_t recursive; | ||
char *snapname = NULL; | ||
|
||
begin_time = time(NULL); | ||
|
||
|
@@ -2975,7 +2986,6 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap, | |
ENOENT); | ||
|
||
if (stream_avl != NULL) { | ||
char *snapname; | ||
nvlist_t *fs = fsavl_find(stream_avl, drrb->drr_toguid, | ||
&snapname); | ||
nvlist_t *props; | ||
|
@@ -3316,7 +3326,21 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap, | |
ZPROP_N_MORE_ERRORS) == 0) { | ||
trunc_prop_errs(intval); | ||
break; | ||
} else { | ||
} else if (snapname == NULL || finalsnap == NULL || | ||
strcmp(finalsnap, snapname) == 0 || | ||
strcmp(nvpair_name(prop_err), | ||
zfs_prop_to_name(ZFS_PROP_REFQUOTA)) != 0) { | ||
/* | ||
* Skip the special case of, for example, | ||
* "refquota", errors on intermediate | ||
* snapshots leading up to a final one. | ||
* That's why we have all of the checks above. | ||
* | ||
* See zfs_ioctl.c's extract_delay_props() for | ||
* a list of props which can fail on | ||
* intermediate snapshots, but shouldn't | ||
* affect the overall receive. | ||
*/ | ||
(void) snprintf(tbuf, sizeof (tbuf), | ||
dgettext(TEXT_DOMAIN, | ||
"cannot receive %s property on %s"), | ||
|
@@ -3501,7 +3525,7 @@ static int | |
zfs_receive_impl(libzfs_handle_t *hdl, const char *tosnap, | ||
const char *originsnap, recvflags_t *flags, int infd, const char *sendfs, | ||
nvlist_t *stream_nv, avl_tree_t *stream_avl, char **top_zfs, int cleanup_fd, | ||
uint64_t *action_handlep) | ||
uint64_t *action_handlep, const char *finalsnap) | ||
{ | ||
int err; | ||
dmu_replay_record_t drr, drr_noswap; | ||
|
@@ -3597,10 +3621,11 @@ zfs_receive_impl(libzfs_handle_t *hdl, const char *tosnap, | |
if ((cp = strchr(nonpackage_sendfs, '@')) != NULL) | ||
*cp = '\0'; | ||
sendfs = nonpackage_sendfs; | ||
VERIFY(finalsnap == NULL); | ||
} | ||
return (zfs_receive_one(hdl, infd, tosnap, originsnap, flags, | ||
&drr, &drr_noswap, sendfs, stream_nv, stream_avl, top_zfs, | ||
cleanup_fd, action_handlep)); | ||
cleanup_fd, action_handlep, finalsnap)); | ||
} else { | ||
assert(DMU_GET_STREAM_HDRTYPE(drrb->drr_versioninfo) == | ||
DMU_COMPOUNDSTREAM); | ||
|
@@ -3678,7 +3703,7 @@ zfs_receive(libzfs_handle_t *hdl, const char *tosnap, nvlist_t *props, | |
VERIFY(cleanup_fd >= 0); | ||
|
||
err = zfs_receive_impl(hdl, tosnap, originsnap, flags, infd, NULL, NULL, | ||
stream_avl, &top_zfs, cleanup_fd, &action_handle); | ||
stream_avl, &top_zfs, cleanup_fd, &action_handle, NULL); | ||
|
||
VERIFY(0 == close(cleanup_fd)); | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -22,6 +22,7 @@ | |
/* | ||
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. | ||
* Portions Copyright 2011 Martin Matuska | ||
* Copyright 2015, OmniTI Computer Consulting, Inc. All rights reserved. | ||
* Portions Copyright 2012 Pawel Jakub Dawidek <[email protected]> | ||
* Copyright (c) 2012, Joyent, Inc. All rights reserved. | ||
* Copyright 2015 Nexenta Systems, Inc. All rights reserved. | ||
|
@@ -3990,6 +3991,56 @@ props_reduce(nvlist_t *props, nvlist_t *origprops) | |
} | ||
} | ||
|
||
/* | ||
* Extract properties that cannot be set PRIOR to the receipt of a dataset. | ||
* For example, refquota cannot be set until after the receipt of a dataset, | ||
* because in replication streams, an older/earlier snapshot may exceed the | ||
* refquota. We want to receive the older/earlier snapshot, but setting | ||
* refquota pre-receipt will set the dsl's ACTUAL quota, which will prevent | ||
* the older/earlier snapshot from being received (with EDQUOT). | ||
* | ||
* The ZFS test "zfs_receive_011_pos" demonstrates such a scenario. | ||
* | ||
* libzfs will need to be judicious handling errors encountered by props | ||
* extracted by this function. | ||
*/ | ||
static nvlist_t * | ||
extract_delay_props(nvlist_t *props) | ||
{ | ||
nvlist_t *delayprops; | ||
nvpair_t *nvp, *tmp; | ||
static const zfs_prop_t delayable[] = { ZFS_PROP_REFQUOTA, 0 }; | ||
int i; | ||
|
||
VERIFY(nvlist_alloc(&delayprops, NV_UNIQUE_NAME, KM_SLEEP) == 0); | ||
|
||
for (nvp = nvlist_next_nvpair(props, NULL); nvp != NULL; | ||
nvp = nvlist_next_nvpair(props, nvp)) { | ||
/* | ||
* strcmp() is safe because zfs_prop_to_name() always returns | ||
* a bounded string. | ||
*/ | ||
for (i = 0; delayable[i] != 0; i++) { | ||
if (strcmp(zfs_prop_to_name(delayable[i]), | ||
nvpair_name(nvp)) == 0) { | ||
break; | ||
} | ||
} | ||
if (delayable[i] != 0) { | ||
tmp = nvlist_prev_nvpair(props, nvp); | ||
VERIFY(nvlist_add_nvpair(delayprops, nvp) == 0); | ||
VERIFY(nvlist_remove_nvpair(props, nvp) == 0); | ||
nvp = tmp; | ||
} | ||
} | ||
|
||
if (nvlist_empty(delayprops)) { | ||
nvlist_free(delayprops); | ||
delayprops = NULL; | ||
} | ||
return (delayprops); | ||
} | ||
|
||
#ifdef DEBUG | ||
static boolean_t zfs_ioc_recv_inject_err; | ||
#endif | ||
|
@@ -4026,6 +4077,7 @@ zfs_ioc_recv(zfs_cmd_t *zc) | |
offset_t off; | ||
nvlist_t *props = NULL; /* sent properties */ | ||
nvlist_t *origprops = NULL; /* existing properties */ | ||
nvlist_t *delayprops = NULL; /* sent properties applied post-receive */ | ||
char *origin = NULL; | ||
char *tosnap; | ||
char tofs[ZFS_MAXNAMELEN]; | ||
|
@@ -4106,21 +4158,12 @@ zfs_ioc_recv(zfs_cmd_t *zc) | |
props_error = dsl_prop_set_hasrecvd(tofs); | ||
|
||
if (props_error == 0) { | ||
delayprops = extract_delay_props(props); | ||
(void) zfs_set_prop_nvlist(tofs, ZPROP_SRC_RECEIVED, | ||
props, errors); | ||
} | ||
} | ||
|
||
if (zc->zc_nvlist_dst_size != 0 && | ||
(nvlist_smush(errors, zc->zc_nvlist_dst_size) != 0 || | ||
put_nvlist(zc, errors) != 0)) { | ||
/* | ||
* Caller made zc->zc_nvlist_dst less than the minimum expected | ||
* size or supplied an invalid address. | ||
*/ | ||
props_error = SET_ERROR(EINVAL); | ||
} | ||
|
||
off = fp->f_offset; | ||
error = dmu_recv_stream(&drc, fp->f_vnode, &off, zc->zc_cleanup_fd, | ||
&zc->zc_action_handle); | ||
|
@@ -4145,6 +4188,40 @@ zfs_ioc_recv(zfs_cmd_t *zc) | |
} else { | ||
error = dmu_recv_end(&drc, NULL); | ||
} | ||
|
||
/* Set delayed properties now, after we're done receiving. */ | ||
if (delayprops != NULL && error == 0) { | ||
(void) zfs_set_prop_nvlist(tofs, ZPROP_SRC_RECEIVED, | ||
delayprops, errors); | ||
} | ||
} | ||
|
||
if (delayprops != NULL) { | ||
/* | ||
* Merge delayed props back in with initial props, in case | ||
* we're DEBUG and zfs_ioc_recv_inject_err is set (which means | ||
* we have to make sure clear_received_props() includes | ||
* the delayed properties). | ||
* | ||
* Since zfs_ioc_recv_inject_err is only in DEBUG kernels, | ||
* using ASSERT() will be just like a VERIFY. | ||
*/ | ||
ASSERT(nvlist_merge(props, delayprops, 0) == 0); | ||
nvlist_free(delayprops); | ||
} | ||
|
||
/* | ||
* Now that all props, initial and delayed, are set, report the prop | ||
* errors to the caller. | ||
*/ | ||
if (zc->zc_nvlist_dst_size != 0 && | ||
(nvlist_smush(errors, zc->zc_nvlist_dst_size) != 0 || | ||
put_nvlist(zc, errors) != 0)) { | ||
/* | ||
* Caller made zc->zc_nvlist_dst less than the minimum expected | ||
* size or supplied an invalid address. | ||
*/ | ||
props_error = SET_ERROR(EINVAL); | ||
} | ||
|
||
zc->zc_cookie = off - fp->f_offset; | ||
|