mirror of
https://git.proxmox.com/git/mirror_zfs.git
synced 2024-11-17 10:01:01 +03:00
OpenZFS 4986 - receiving replication stream fails if any snapshot exceeds refquota
Authored by: Dan McDonald <danmcd@omniti.com> Reviewed by: John Kennedy <john.kennedy@delphix.com> Reviewed by: Matthew Ahrens <mahrens@delphix.com> Approved by: Gordon Ross <gordon.ross@nexenta.com> Ported-by: Brian Behlendorf <behlendorf1@llnl.gov> OpenZFS-issue: https://www.illumos.org/issues/4986 OpenZFS-commit: https://github.com/openzfs/openzfs/commit/5878fad
This commit is contained in:
parent
f8866f8ae3
commit
671c93546c
@ -26,6 +26,7 @@
|
|||||||
* Copyright (c) 2012 Pawel Jakub Dawidek <pawel@dawidek.net>.
|
* Copyright (c) 2012 Pawel Jakub Dawidek <pawel@dawidek.net>.
|
||||||
* All rights reserved
|
* All rights reserved
|
||||||
* Copyright (c) 2013 Steven Hartland. All rights reserved.
|
* Copyright (c) 2013 Steven Hartland. All rights reserved.
|
||||||
|
* Copyright 2015, OmniTI Computer Consulting, Inc. All rights reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <assert.h>
|
#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 *,
|
static int zfs_receive_impl(libzfs_handle_t *, const char *, const char *,
|
||||||
recvflags_t *, int, const char *, nvlist_t *, avl_tree_t *, char **, int,
|
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 *,
|
static int guid_to_name(libzfs_handle_t *, const char *,
|
||||||
uint64_t, boolean_t, 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;
|
nvlist_t *stream_nv = NULL;
|
||||||
avl_tree_t *stream_avl = NULL;
|
avl_tree_t *stream_avl = NULL;
|
||||||
char *fromsnap = NULL;
|
char *fromsnap = NULL;
|
||||||
|
char *sendsnap = NULL;
|
||||||
char *cp;
|
char *cp;
|
||||||
char tofs[ZFS_MAXNAMELEN];
|
char tofs[ZFS_MAXNAMELEN];
|
||||||
char sendfs[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,
|
(void) strlcpy(sendfs, drr->drr_u.drr_begin.drr_toname,
|
||||||
ZFS_MAXNAMELEN);
|
ZFS_MAXNAMELEN);
|
||||||
if ((cp = strchr(sendfs, '@')) != NULL)
|
if ((cp = strchr(sendfs, '@')) != NULL) {
|
||||||
*cp = '\0';
|
*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 */
|
/* Finally, receive each contained stream */
|
||||||
do {
|
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,
|
error = zfs_receive_impl(hdl, destname, NULL, flags, fd,
|
||||||
sendfs, stream_nv, stream_avl, top_zfs, cleanup_fd,
|
sendfs, stream_nv, stream_avl, top_zfs, cleanup_fd,
|
||||||
action_handlep);
|
action_handlep, sendsnap);
|
||||||
if (error == ENODATA) {
|
if (error == ENODATA) {
|
||||||
error = 0;
|
error = 0;
|
||||||
break;
|
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,
|
const char *originsnap, recvflags_t *flags, dmu_replay_record_t *drr,
|
||||||
dmu_replay_record_t *drr_noswap, const char *sendfs, nvlist_t *stream_nv,
|
dmu_replay_record_t *drr_noswap, const char *sendfs, nvlist_t *stream_nv,
|
||||||
avl_tree_t *stream_avl, char **top_zfs, int cleanup_fd,
|
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"};
|
zfs_cmd_t zc = {"\0"};
|
||||||
time_t begin_time;
|
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;
|
nvlist_t *snapprops_nvlist = NULL;
|
||||||
zprop_errflags_t prop_errflags;
|
zprop_errflags_t prop_errflags;
|
||||||
boolean_t recursive;
|
boolean_t recursive;
|
||||||
|
char *snapname = NULL;
|
||||||
|
|
||||||
begin_time = time(NULL);
|
begin_time = time(NULL);
|
||||||
|
|
||||||
@ -2975,7 +2986,6 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
|
|||||||
ENOENT);
|
ENOENT);
|
||||||
|
|
||||||
if (stream_avl != NULL) {
|
if (stream_avl != NULL) {
|
||||||
char *snapname;
|
|
||||||
nvlist_t *fs = fsavl_find(stream_avl, drrb->drr_toguid,
|
nvlist_t *fs = fsavl_find(stream_avl, drrb->drr_toguid,
|
||||||
&snapname);
|
&snapname);
|
||||||
nvlist_t *props;
|
nvlist_t *props;
|
||||||
@ -3316,7 +3326,21 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
|
|||||||
ZPROP_N_MORE_ERRORS) == 0) {
|
ZPROP_N_MORE_ERRORS) == 0) {
|
||||||
trunc_prop_errs(intval);
|
trunc_prop_errs(intval);
|
||||||
break;
|
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),
|
(void) snprintf(tbuf, sizeof (tbuf),
|
||||||
dgettext(TEXT_DOMAIN,
|
dgettext(TEXT_DOMAIN,
|
||||||
"cannot receive %s property on %s"),
|
"cannot receive %s property on %s"),
|
||||||
@ -3501,7 +3525,7 @@ static int
|
|||||||
zfs_receive_impl(libzfs_handle_t *hdl, const char *tosnap,
|
zfs_receive_impl(libzfs_handle_t *hdl, const char *tosnap,
|
||||||
const char *originsnap, recvflags_t *flags, int infd, const char *sendfs,
|
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,
|
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;
|
int err;
|
||||||
dmu_replay_record_t drr, drr_noswap;
|
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)
|
if ((cp = strchr(nonpackage_sendfs, '@')) != NULL)
|
||||||
*cp = '\0';
|
*cp = '\0';
|
||||||
sendfs = nonpackage_sendfs;
|
sendfs = nonpackage_sendfs;
|
||||||
|
VERIFY(finalsnap == NULL);
|
||||||
}
|
}
|
||||||
return (zfs_receive_one(hdl, infd, tosnap, originsnap, flags,
|
return (zfs_receive_one(hdl, infd, tosnap, originsnap, flags,
|
||||||
&drr, &drr_noswap, sendfs, stream_nv, stream_avl, top_zfs,
|
&drr, &drr_noswap, sendfs, stream_nv, stream_avl, top_zfs,
|
||||||
cleanup_fd, action_handlep));
|
cleanup_fd, action_handlep, finalsnap));
|
||||||
} else {
|
} else {
|
||||||
assert(DMU_GET_STREAM_HDRTYPE(drrb->drr_versioninfo) ==
|
assert(DMU_GET_STREAM_HDRTYPE(drrb->drr_versioninfo) ==
|
||||||
DMU_COMPOUNDSTREAM);
|
DMU_COMPOUNDSTREAM);
|
||||||
@ -3678,7 +3703,7 @@ zfs_receive(libzfs_handle_t *hdl, const char *tosnap, nvlist_t *props,
|
|||||||
VERIFY(cleanup_fd >= 0);
|
VERIFY(cleanup_fd >= 0);
|
||||||
|
|
||||||
err = zfs_receive_impl(hdl, tosnap, originsnap, flags, infd, NULL, NULL,
|
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));
|
VERIFY(0 == close(cleanup_fd));
|
||||||
|
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
|
||||||
* Portions Copyright 2011 Martin Matuska
|
* Portions Copyright 2011 Martin Matuska
|
||||||
|
* Copyright 2015, OmniTI Computer Consulting, Inc. All rights reserved.
|
||||||
* Portions Copyright 2012 Pawel Jakub Dawidek <pawel@dawidek.net>
|
* Portions Copyright 2012 Pawel Jakub Dawidek <pawel@dawidek.net>
|
||||||
* Copyright (c) 2012, Joyent, Inc. All rights reserved.
|
* Copyright (c) 2012, Joyent, Inc. All rights reserved.
|
||||||
* Copyright 2015 Nexenta Systems, Inc. All rights reserved.
|
* Copyright 2015 Nexenta Systems, Inc. All rights reserved.
|
||||||
@ -3990,6 +3991,56 @@ next:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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
|
#ifdef DEBUG
|
||||||
static boolean_t zfs_ioc_recv_inject_err;
|
static boolean_t zfs_ioc_recv_inject_err;
|
||||||
#endif
|
#endif
|
||||||
@ -4026,6 +4077,7 @@ zfs_ioc_recv(zfs_cmd_t *zc)
|
|||||||
offset_t off;
|
offset_t off;
|
||||||
nvlist_t *props = NULL; /* sent properties */
|
nvlist_t *props = NULL; /* sent properties */
|
||||||
nvlist_t *origprops = NULL; /* existing properties */
|
nvlist_t *origprops = NULL; /* existing properties */
|
||||||
|
nvlist_t *delayprops = NULL; /* sent properties applied post-receive */
|
||||||
char *origin = NULL;
|
char *origin = NULL;
|
||||||
char *tosnap;
|
char *tosnap;
|
||||||
char tofs[ZFS_MAXNAMELEN];
|
char tofs[ZFS_MAXNAMELEN];
|
||||||
@ -4106,21 +4158,12 @@ zfs_ioc_recv(zfs_cmd_t *zc)
|
|||||||
props_error = dsl_prop_set_hasrecvd(tofs);
|
props_error = dsl_prop_set_hasrecvd(tofs);
|
||||||
|
|
||||||
if (props_error == 0) {
|
if (props_error == 0) {
|
||||||
|
delayprops = extract_delay_props(props);
|
||||||
(void) zfs_set_prop_nvlist(tofs, ZPROP_SRC_RECEIVED,
|
(void) zfs_set_prop_nvlist(tofs, ZPROP_SRC_RECEIVED,
|
||||||
props, errors);
|
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;
|
off = fp->f_offset;
|
||||||
error = dmu_recv_stream(&drc, fp->f_vnode, &off, zc->zc_cleanup_fd,
|
error = dmu_recv_stream(&drc, fp->f_vnode, &off, zc->zc_cleanup_fd,
|
||||||
&zc->zc_action_handle);
|
&zc->zc_action_handle);
|
||||||
@ -4145,6 +4188,40 @@ zfs_ioc_recv(zfs_cmd_t *zc)
|
|||||||
} else {
|
} else {
|
||||||
error = dmu_recv_end(&drc, NULL);
|
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;
|
zc->zc_cookie = off - fp->f_offset;
|
||||||
|
Loading…
Reference in New Issue
Block a user