svn commit: r297103 - in stable/10: cddl/contrib/opensolaris/lib/libzfs/common sys/cddl/contrib/opensolaris/uts/common/fs/zfs
Alexander Motin
mav at FreeBSD.org
Mon Mar 21 00:03:57 UTC 2016
Author: mav
Date: Mon Mar 21 00:03:55 2016
New Revision: 297103
URL: https://svnweb.freebsd.org/changeset/base/297103
Log:
MFC r294817: MFV r294816:
4986 receiving replication stream fails if any snapshot exceeds refquota
Reviewed by: John Kennedy <john.kennedy at delphix.com>
Reviewed by: Matthew Ahrens <mahrens at delphix.com>
Approved by: Gordon Ross <gordon.ross at nexenta.com>
Author: Dan McDonald <danmcd at omniti.com>
illumos/illumos-gate at 5878fad70d76d8711f6608c1f80b0447601261c6
Modified:
stable/10/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_sendrecv.c
stable/10/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c
Directory Properties:
stable/10/ (props changed)
Modified: stable/10/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_sendrecv.c
==============================================================================
--- stable/10/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_sendrecv.c Mon Mar 21 00:01:59 2016 (r297102)
+++ stable/10/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_sendrecv.c Mon Mar 21 00:03:55 2016 (r297103)
@@ -26,6 +26,7 @@
* Copyright (c) 2012 Pawel Jakub Dawidek <pawel at dawidek.net>.
* All rights reserved.
* Copyright (c) 2013 Steven Hartland. All rights reserved.
+ * Copyright 2015, OmniTI Computer Consulting, Inc. All rights reserved.
*/
#include <assert.h>
@@ -67,7 +68,7 @@ extern void zfs_setprop_error(libzfs_han
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 *);
@@ -2602,6 +2603,7 @@ zfs_receive_package(libzfs_handle_t *hdl
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];
@@ -2750,8 +2752,16 @@ zfs_receive_package(libzfs_handle_t *hdl
*/
(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 {
@@ -2764,7 +2774,7 @@ zfs_receive_package(libzfs_handle_t *hdl
*/
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;
@@ -2930,7 +2940,7 @@ zfs_receive_one(libzfs_handle_t *hdl, in
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;
@@ -2947,6 +2957,7 @@ zfs_receive_one(libzfs_handle_t *hdl, in
nvlist_t *snapprops_nvlist = NULL;
zprop_errflags_t prop_errflags;
boolean_t recursive;
+ char *snapname = NULL;
begin_time = time(NULL);
@@ -2957,7 +2968,6 @@ zfs_receive_one(libzfs_handle_t *hdl, in
ENOENT);
if (stream_avl != NULL) {
- char *snapname;
nvlist_t *fs = fsavl_find(stream_avl, drrb->drr_toguid,
&snapname);
nvlist_t *props;
@@ -3313,7 +3323,21 @@ zfs_receive_one(libzfs_handle_t *hdl, in
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"),
@@ -3498,7 +3522,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;
@@ -3594,10 +3618,11 @@ zfs_receive_impl(libzfs_handle_t *hdl, c
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);
@@ -3632,7 +3657,7 @@ zfs_receive(libzfs_handle_t *hdl, const
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));
Modified: stable/10/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c
==============================================================================
--- stable/10/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c Mon Mar 21 00:01:59 2016 (r297102)
+++ stable/10/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c Mon Mar 21 00:03:55 2016 (r297103)
@@ -25,6 +25,7 @@
* All rights reserved.
* Copyright 2013 Martin Matuska <mm at FreeBSD.org>. All rights reserved.
* Copyright 2014 Xin Li <delphij at FreeBSD.org>. All rights reserved.
+ * Copyright 2015, OmniTI Computer Consulting, Inc. All rights reserved.
* Copyright 2015 Nexenta Systems, Inc. All rights reserved.
* Copyright (c) 2014, Joyent, Inc. All rights reserved.
* Copyright (c) 2011, 2015 by Delphix. All rights reserved.
@@ -4176,6 +4177,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
static boolean_t zfs_ioc_recv_inject_err;
#endif
@@ -4212,6 +4263,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];
@@ -4297,21 +4349,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, &off, zc->zc_cleanup_fd,
&zc->zc_action_handle);
@@ -4336,6 +4379,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;
More information about the svn-src-stable-10
mailing list