--- old/usr/src/lib/libzfs/common/libzfs.h	Wed Dec  2 13:25:00 2015
+++ new/usr/src/lib/libzfs/common/libzfs.h	Wed Dec  2 13:25:00 2015
@@ -659,6 +659,9 @@
 
 	/* do not mount file systems as they are extracted (private) */
 	boolean_t nomount;
+
+	/* Internal-only:  zfs_receive_one() is a recursive receive. */
+	boolean_t recursive;
 } recvflags_t;
 
 extern int zfs_receive(libzfs_handle_t *, const char *, nvlist_t *,
--- old/usr/src/lib/libzfs/common/libzfs_sendrecv.c	Wed Dec  2 13:25:01 2015
+++ new/usr/src/lib/libzfs/common/libzfs_sendrecv.c	Wed Dec  2 13:25:01 2015
@@ -24,6 +24,7 @@
  * Copyright (c) 2011, 2015 by Delphix. All rights reserved.
  * Copyright (c) 2012, Joyent, Inc. All rights reserved.
  * Copyright (c) 2013 Steven Hartland. All rights reserved.
+ * Copyright 2015, OmniTI Computer Consulting, Inc. All rights reserved.
  */
 
 #include <assert.h>
@@ -58,7 +59,7 @@
 
 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 *);
 
@@ -2561,6 +2562,7 @@
 	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];
@@ -2709,8 +2711,15 @@
 	 */
 	(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, so zfs_receive_one() can set filesystem properties
+		 * ONLY when receiving that final snapshot.
+		 */
+		sendsnap = (cp + 1);
+	}
 
 	/* Finally, receive each contained stream */
 	do {
@@ -2723,7 +2732,7 @@
 		 */
 		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;
@@ -2887,9 +2896,9 @@
 static int
 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,
+    dmu_replay_record_t *drr_noswap, const char *sendfs,
     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;
@@ -2905,7 +2914,6 @@
 	prop_changelist_t *clp = NULL;
 	nvlist_t *snapprops_nvlist = NULL;
 	zprop_errflags_t prop_errflags;
-	boolean_t recursive;
 
 	begin_time = time(NULL);
 
@@ -2912,9 +2920,6 @@
 	(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
 	    "cannot receive"));
 
-	recursive = (nvlist_lookup_boolean(stream_nv, "not_recursive") ==
-	    ENOENT);
-
 	if (stream_avl != NULL) {
 		char *snapname;
 		nvlist_t *fs = fsavl_find(stream_avl, drrb->drr_toguid,
@@ -2921,11 +2926,20 @@
 		    &snapname);
 		nvlist_t *props;
 		int ret;
+		boolean_t is_finalsnap;
 
+		VERIFY(fs != NULL);
 		(void) nvlist_lookup_uint64(fs, "parentfromsnap",
 		    &parent_snapguid);
-		err = nvlist_lookup_nvlist(fs, "props", &props);
-		if (err)
+		/*
+		 * Can safely use strcmp because at least "snapname" has been
+		 * verified.
+		 */
+		is_finalsnap = (strcmp(snapname, finalsnap) == 0);
+
+		if (is_finalsnap)
+			err = nvlist_lookup_nvlist(fs, "props", &props);
+		if (!is_finalsnap || err)
 			VERIFY(0 == nvlist_alloc(&props, NV_UNIQUE_NAME, 0));
 
 		if (flags->canmountoff) {
@@ -2933,7 +2947,7 @@
 			    zfs_prop_to_name(ZFS_PROP_CANMOUNT), 0));
 		}
 		ret = zcmd_write_src_nvlist(hdl, &zc, props);
-		if (err)
+		if (!is_finalsnap || err)
 			nvlist_free(props);
 
 		if (0 == nvlist_lookup_nvlist(fs, "snapprops", &props)) {
@@ -3004,7 +3018,7 @@
 		chopprefix = drrb->drr_toname + strlen(sendfs);
 	} else {
 		/* A snapshot was specified as an exact path (no -d or -e). */
-		if (recursive) {
+		if (flags->recursive) {
 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 			    "cannot specify snapshot name for multi-snapshot "
 			    "stream"));
@@ -3448,7 +3462,7 @@
 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;
@@ -3544,10 +3558,17 @@
 			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));
+		flags->recursive =
+		    (nvlist_lookup_boolean(stream_nv, "not_recursive") ==
+		    ENOENT);
+		err = zfs_receive_one(hdl, infd, tosnap, originsnap, flags,
+		    &drr, &drr_noswap, sendfs, stream_avl, top_zfs,
+		    cleanup_fd, action_handlep, finalsnap);
+		/* Clear out internal-only flags. */
+		flags->recursive = B_FALSE;
+		return (err);
 	} else {
 		assert(DMU_GET_STREAM_HDRTYPE(drrb->drr_versioninfo) ==
 		    DMU_COMPOUNDSTREAM);
@@ -3582,7 +3603,7 @@
 	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));
 
--- old/usr/src/pkg/manifests/system-test-zfstest.mf	Wed Dec  2 13:25:01 2015
+++ new/usr/src/pkg/manifests/system-test-zfstest.mf	Wed Dec  2 13:25:01 2015
@@ -665,6 +665,9 @@
 file \
     path=opt/zfs-tests/tests/functional/cli_root/zfs_receive/zfs_receive_010_pos \
     mode=0555
+file \
+    path=opt/zfs-tests/tests/functional/cli_root/zfs_receive/zfs_receive_011_pos \
+    mode=0555
 file path=opt/zfs-tests/tests/functional/cli_root/zfs_rename/cleanup mode=0555
 file path=opt/zfs-tests/tests/functional/cli_root/zfs_rename/setup mode=0555
 file path=opt/zfs-tests/tests/functional/cli_root/zfs_rename/zfs_rename.cfg \
--- old/usr/src/test/zfs-tests/runfiles/delphix.run	Wed Dec  2 13:25:01 2015
+++ new/usr/src/test/zfs-tests/runfiles/delphix.run	Wed Dec  2 13:25:01 2015
@@ -140,7 +140,7 @@
 tests = ['zfs_receive_001_pos', 'zfs_receive_002_pos', 'zfs_receive_003_pos',
     'zfs_receive_005_neg', 'zfs_receive_006_pos',
     'zfs_receive_007_neg', 'zfs_receive_008_pos', 'zfs_receive_009_neg',
-    'zfs_receive_010_pos']
+    'zfs_receive_010_pos', 'zfs_receive_011_pos']
 
 [/opt/zfs-tests/tests/functional/cli_root/zfs_rename]
 tests = ['zfs_rename_001_pos', 'zfs_rename_002_pos', 'zfs_rename_003_pos',
--- old/usr/src/test/zfs-tests/runfiles/omnios.run	Wed Dec  2 13:25:01 2015
+++ new/usr/src/test/zfs-tests/runfiles/omnios.run	Wed Dec  2 13:25:01 2015
@@ -139,7 +139,8 @@
 [/opt/zfs-tests/tests/functional/cli_root/zfs_receive]
 tests = ['zfs_receive_001_pos', 'zfs_receive_002_pos', 'zfs_receive_003_pos',
     'zfs_receive_005_neg', 'zfs_receive_006_pos',
-    'zfs_receive_007_neg', 'zfs_receive_008_pos', 'zfs_receive_009_neg']
+    'zfs_receive_007_neg', 'zfs_receive_008_pos', 'zfs_receive_009_neg',
+    'zfs_receive_010_pos', 'zfs_receive_011_pos']
 
 [/opt/zfs-tests/tests/functional/cli_root/zfs_rename]
 tests = ['zfs_rename_001_pos', 'zfs_rename_002_pos', 'zfs_rename_003_pos',
--- old/usr/src/test/zfs-tests/runfiles/openindiana.run	Wed Dec  2 13:25:01 2015
+++ new/usr/src/test/zfs-tests/runfiles/openindiana.run	Wed Dec  2 13:25:01 2015
@@ -139,7 +139,8 @@
 [/opt/zfs-tests/tests/functional/cli_root/zfs_receive]
 tests = ['zfs_receive_001_pos', 'zfs_receive_002_pos', 'zfs_receive_003_pos',
     'zfs_receive_005_neg', 'zfs_receive_006_pos',
-    'zfs_receive_007_neg', 'zfs_receive_008_pos', 'zfs_receive_009_neg']
+    'zfs_receive_007_neg', 'zfs_receive_008_pos', 'zfs_receive_009_neg',
+    'zfs_receive_010_pos', 'zfs_receive_011_pos']
 
 [/opt/zfs-tests/tests/functional/cli_root/zfs_rename]
 tests = ['zfs_rename_001_pos', 'zfs_rename_002_pos', 'zfs_rename_003_pos',
--- old/usr/src/test/zfs-tests/tests/functional/cli_root/zfs_receive/Makefile	Wed Dec  2 13:25:01 2015
+++ new/usr/src/test/zfs-tests/tests/functional/cli_root/zfs_receive/Makefile	Wed Dec  2 13:25:01 2015
@@ -29,7 +29,8 @@
 	zfs_receive_007_neg \
 	zfs_receive_008_pos \
 	zfs_receive_009_neg \
-	zfs_receive_010_pos
+	zfs_receive_010_pos \
+	zfs_receive_011_pos
 
 CMDS = $(PROGS:%=$(TESTDIR)/%)
 $(CMDS) := FILEMODE = 0555
--- /dev/null	Wed Dec  2 13:25:01 2015
+++ new/usr/src/test/zfs-tests/tests/functional/cli_root/zfs_receive/zfs_receive_011_pos.ksh	Wed Dec  2 13:25:01 2015
@@ -0,0 +1,84 @@
+#!/bin/ksh -p
+#
+# CDDL HEADER START
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source.  A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2015, OmniTI Computer Consulting, Inc. All rights reserved.
+#
+
+. $STF_SUITE/include/libtest.shlib
+
+#
+# DESCRIPTION:
+#	refquota should be sent-and-received, but it should not interfere with
+#	the receipt of intermediate snapshots that may have preceded the
+#	final snapshot, where the refquota should hold.
+#
+# STRATEGY:
+#	1. Create a filesystem.
+#	2. Create two equal-sized large files.
+#	3. Snapshot the filesystem.
+#	4. Remove one of the two large files.
+#	5. Create a refquota larger than one file, but smaller than both.
+#	6. Snapshot the filesystem again.
+#	7. Send a replication stream of the second snapshot to a new filesystem.
+#
+#
+
+verify_runnable "both"
+
+typeset streamfile=/var/tmp/streamfile.$$
+
+function cleanup
+{
+	log_must $RM $streamfile
+	log_must $ZFS destroy -rf $TESTPOOL/$TESTFS1
+	log_must $ZFS destroy -rf $TESTPOOL/$TESTFS2
+}
+
+log_assert "refquota is properly sent-and-received, regardless of any " \
+	"intermediate snapshots sent by a replication stream."
+log_onexit cleanup
+
+orig=$TESTPOOL/$TESTFS1
+dest=$TESTPOOL/$TESTFS2
+#	1. Create a filesystem.
+log_must $ZFS create $orig
+origdir=$(get_prop mountpoint $orig)
+
+#	2. Create two equal-sized large files.
+log_must $MKFILE 5M $origdir/file1
+log_must $MKFILE 5M $origdir/file2
+log_must $SYNC
+
+#	3. Snapshot the filesystem.
+log_must $ZFS snapshot $orig@1
+
+#	4. Remove one of the two large files.
+log_must $RM $origdir/file2
+log_must $SYNC
+
+#	5. Create a refquota larger than one file, but smaller than both.
+log_must $ZFS set refquota=8M $orig
+
+#	6. Snapshot the filesystem again.
+log_must $ZFS snapshot $orig@2
+
+#	7. Send a replication stream of the second snapshot to a new filesystem.
+log_must eval "$ZFS send -R $orig@2 > $streamfile"
+log_must eval "$ZFS recv $dest < $streamfile"
+
+log_pass "refquota is properly sent-and-received, regardless of any " \
+	"intermediate snapshots sent by a replication stream."
--- old/usr/src/uts/common/fs/zfs/zfs_ioctl.c	Wed Dec  2 13:25:02 2015
+++ new/usr/src/uts/common/fs/zfs/zfs_ioctl.c	Wed Dec  2 13:25:02 2015
@@ -4090,6 +4090,55 @@
 	}
 }
 
+/*
+ * 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 a prior snapshot may exceed the refquota, and refquotas only apply
+ * to the current dataset.  The caller (libzfs) will manage these properties
+ * somewhat as well to make sure they only come down with the last dataset in
+ * a replication stream, but we still need to be safe about it here in
+ * kernel-land.
+ */
+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, ZFS_PROP_REFRESERVATION, 0 };
+	boolean_t dontbother = B_TRUE;
+	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;
+			dontbother = B_FALSE;  /* Actually, do bother! */
+		}
+	}
+
+	if (dontbother) {
+		nvlist_free(delayprops);
+		delayprops = NULL;
+	}
+	return (delayprops);
+}
+
 #ifdef	DEBUG
 static boolean_t zfs_ioc_recv_inject_err;
 #endif
@@ -4126,6 +4175,7 @@
 	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];
@@ -4206,21 +4256,12 @@
 		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);
@@ -4245,8 +4286,31 @@
 		} else {
 			error = dmu_recv_end(&drc, NULL);
 		}
+
+		/* Set delayed properties now, after we're done receiving. */
+		if (delayprops != NULL) {
+			(void) zfs_set_prop_nvlist(tofs, ZPROP_SRC_RECEIVED,
+			    delayprops, errors);
+		}
 	}
 
+	/* Merge delayprops back in with regular props, in case of errors. */
+	if (delayprops != NULL) {
+		VERIFY(nvlist_merge(props, delayprops, 0) == 0);
+		nvlist_free(delayprops);
+	}
+
+	/* Put the props error list into zc AFTER the delayprops. */
+	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;
 	if (VOP_SEEK(fp->f_vnode, fp->f_offset, &off, NULL) == 0)
 		fp->f_offset = off;
