PSARC/2008/483 ZFS clone -o
authorahrens
Fri, 01 Aug 2008 16:32:18 -0700
changeset 7265 cc18862247da
parent 7264 89c9135b80e3
child 7266 bbfd6395568b
PSARC/2008/483 ZFS clone -o PSARC/2008/484 ZFS snapshot properties 6613766 create-time properties for clones 6701797 want user properties on snapshots
usr/src/cmd/zdb/zdb.c
usr/src/cmd/zfs/zfs_main.c
usr/src/cmd/zoneadm/zfs.c
usr/src/cmd/zpool/zpool_main.c
usr/src/grub/grub-0.95/stage2/fsys_zfs.c
usr/src/grub/grub-0.95/stage2/zfs-include/zfs.h
usr/src/lib/libzfs/common/libzfs.h
usr/src/lib/libzfs/common/libzfs_dataset.c
usr/src/lib/libzfs/common/libzfs_sendrecv.c
usr/src/uts/common/fs/zfs/arc.c
usr/src/uts/common/fs/zfs/dsl_dataset.c
usr/src/uts/common/fs/zfs/dsl_deleg.c
usr/src/uts/common/fs/zfs/dsl_prop.c
usr/src/uts/common/fs/zfs/sys/dsl_dataset.h
usr/src/uts/common/fs/zfs/sys/dsl_prop.h
usr/src/uts/common/fs/zfs/zfs_ioctl.c
usr/src/uts/common/fs/zfs/zfs_vfsops.c
usr/src/uts/common/fs/zfs/zvol.c
usr/src/uts/common/sys/fs/zfs.h
--- a/usr/src/cmd/zdb/zdb.c	Fri Aug 01 16:15:10 2008 -0700
+++ b/usr/src/cmd/zdb/zdb.c	Fri Aug 01 16:32:18 2008 -0700
@@ -802,6 +802,8 @@
 	    (u_longlong_t)ds->ds_flags);
 	(void) printf("\t\tnext_clones_obj = %llu\n",
 	    (u_longlong_t)ds->ds_next_clones_obj);
+	(void) printf("\t\tprops_obj = %llu\n",
+	    (u_longlong_t)ds->ds_props_obj);
 	(void) printf("\t\tbp = %s\n", blkbuf);
 }
 
--- a/usr/src/cmd/zfs/zfs_main.c	Fri Aug 01 16:15:10 2008 -0700
+++ b/usr/src/cmd/zfs/zfs_main.c	Fri Aug 01 16:32:18 2008 -0700
@@ -175,8 +175,8 @@
 {
 	switch (idx) {
 	case HELP_CLONE:
-		return (gettext("\tclone [-p] <snapshot> "
-		    "<filesystem|volume>\n"));
+		return (gettext("\tclone [-p] [-o property=value] ... "
+		    "<snapshot> <filesystem|volume>\n"));
 	case HELP_CREATE:
 		return (gettext("\tcreate [-p] [-o property=value] ... "
 		    "<filesystem>\n"
@@ -192,7 +192,7 @@
 		    "[filesystem|volume|snapshot] ...\n"));
 	case HELP_INHERIT:
 		return (gettext("\tinherit [-r] <property> "
-		    "<filesystem|volume> ...\n"));
+		    "<filesystem|volume|snapshot> ...\n"));
 	case HELP_UPGRADE:
 		return (gettext("\tupgrade [-v]\n"
 		    "\tupgrade [-r] [-V version] <-a | filesystem ...>\n"));
@@ -221,11 +221,11 @@
 		return (gettext("\tsend [-R] [-[iI] snapshot] <snapshot>\n"));
 	case HELP_SET:
 		return (gettext("\tset <property=value> "
-		    "<filesystem|volume> ...\n"));
+		    "<filesystem|volume|snapshot> ...\n"));
 	case HELP_SHARE:
 		return (gettext("\tshare <-a | filesystem>\n"));
 	case HELP_SNAPSHOT:
-		return (gettext("\tsnapshot [-r] "
+		return (gettext("\tsnapshot [-r] [-o property=value] ... "
 		    "<filesystem@snapname|volume@snapname>\n"));
 	case HELP_UNMOUNT:
 		return (gettext("\tunmount [-f] "
@@ -397,8 +397,35 @@
 	exit(requested ? 0 : 2);
 }
 
+static int
+parseprop(nvlist_t *props)
+{
+	char *propname = optarg;
+	char *propval, *strval;
+
+	if ((propval = strchr(propname, '=')) == NULL) {
+		(void) fprintf(stderr, gettext("missing "
+		    "'=' for -o option\n"));
+		return (-1);
+	}
+	*propval = '\0';
+	propval++;
+	if (nvlist_lookup_string(props, propname, &strval) == 0) {
+		(void) fprintf(stderr, gettext("property '%s' "
+		    "specified multiple times\n"), propname);
+		return (-1);
+	}
+	if (nvlist_add_string(props, propname, propval) != 0) {
+		(void) fprintf(stderr, gettext("internal "
+		    "error: out of memory\n"));
+		return (-1);
+	}
+	return (0);
+
+}
+
 /*
- * zfs clone [-p] <snap> <fs | vol>
+ * zfs clone [-p] [-o prop=value] ... <snap> <fs | vol>
  *
  * Given an existing dataset, create a writable copy whose initial contents
  * are the same as the source.  The newly created dataset maintains a
@@ -410,21 +437,32 @@
 static int
 zfs_do_clone(int argc, char **argv)
 {
-	zfs_handle_t *zhp;
+	zfs_handle_t *zhp = NULL;
 	boolean_t parents = B_FALSE;
+	nvlist_t *props;
 	int ret;
 	int c;
 
+	if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0) {
+		(void) fprintf(stderr, gettext("internal error: "
+		    "out of memory\n"));
+		return (1);
+	}
+
 	/* check options */
-	while ((c = getopt(argc, argv, "p")) != -1) {
+	while ((c = getopt(argc, argv, "o:p")) != -1) {
 		switch (c) {
+		case 'o':
+			if (parseprop(props))
+				return (1);
+			break;
 		case 'p':
 			parents = B_TRUE;
 			break;
 		case '?':
 			(void) fprintf(stderr, gettext("invalid option '%c'\n"),
 			    optopt);
-			usage(B_FALSE);
+			goto usage;
 		}
 	}
 
@@ -435,16 +473,16 @@
 	if (argc < 1) {
 		(void) fprintf(stderr, gettext("missing source dataset "
 		    "argument\n"));
-		usage(B_FALSE);
+		goto usage;
 	}
 	if (argc < 2) {
 		(void) fprintf(stderr, gettext("missing target dataset "
 		    "argument\n"));
-		usage(B_FALSE);
+		goto usage;
 	}
 	if (argc > 2) {
 		(void) fprintf(stderr, gettext("too many arguments\n"));
-		usage(B_FALSE);
+		goto usage;
 	}
 
 	/* open the source dataset */
@@ -466,7 +504,7 @@
 	}
 
 	/* pass to libzfs */
-	ret = zfs_clone(zhp, argv[1], NULL);
+	ret = zfs_clone(zhp, argv[1], props);
 
 	/* create the mountpoint if necessary */
 	if (ret == 0) {
@@ -481,8 +519,16 @@
 	}
 
 	zfs_close(zhp);
-
-	return (ret == 0 ? 0 : 1);
+	nvlist_free(props);
+
+	return (!!ret);
+
+usage:
+	if (zhp)
+		zfs_close(zhp);
+	nvlist_free(props);
+	usage(B_FALSE);
+	return (-1);
 }
 
 /*
@@ -511,11 +557,8 @@
 	boolean_t bflag = B_FALSE;
 	boolean_t parents = B_FALSE;
 	int ret = 1;
-	nvlist_t *props = NULL;
+	nvlist_t *props;
 	uint64_t intval;
-	char *propname;
-	char *propval = NULL;
-	char *strval;
 	int canmount;
 
 	if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0) {
@@ -566,25 +609,8 @@
 			}
 			break;
 		case 'o':
-			propname = optarg;
-			if ((propval = strchr(propname, '=')) == NULL) {
-				(void) fprintf(stderr, gettext("missing "
-				    "'=' for -o option\n"));
+			if (parseprop(props))
 				goto error;
-			}
-			*propval = '\0';
-			propval++;
-			if (nvlist_lookup_string(props, propname,
-			    &strval) == 0) {
-				(void) fprintf(stderr, gettext("property '%s' "
-				    "specified multiple times\n"), propname);
-				goto error;
-			}
-			if (nvlist_add_string(props, propname, propval) != 0) {
-				(void) fprintf(stderr, gettext("internal "
-				    "error: out of memory\n"));
-				goto error;
-			}
 			break;
 		case 's':
 			noreserve = B_TRUE;
@@ -626,6 +652,7 @@
 		uint64_t spa_version;
 		char *p;
 		zfs_prop_t resv_prop;
+		char *strval;
 
 		if (p = strchr(argv[0], '/'))
 			*p = '\0';
@@ -1256,13 +1283,29 @@
  */
 
 static int
-inherit_callback(zfs_handle_t *zhp, void *data)
+inherit_recurse_cb(zfs_handle_t *zhp, void *data)
 {
 	char *propname = data;
 	int ret;
-
-	ret = zfs_prop_inherit(zhp, propname);
-	return (ret != 0);
+	zfs_prop_t prop = zfs_name_to_prop(propname);
+
+	/*
+	 * If we're doing it recursively, then ignore properties that
+	 * are not valid for this type of dataset.
+	 */
+	if (prop != ZPROP_INVAL &&
+	    !zfs_prop_valid_for_type(prop, zfs_get_type(zhp)))
+		return (0);
+
+	return (zfs_prop_inherit(zhp, propname) != 0);
+}
+
+static int
+inherit_cb(zfs_handle_t *zhp, void *data)
+{
+	char *propname = data;
+
+	return (zfs_prop_inherit(zhp, propname) != 0);
 }
 
 static int
@@ -1329,9 +1372,13 @@
 		usage(B_FALSE);
 	}
 
-	ret = zfs_for_each(argc, argv, recurse,
-	    ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME, NULL, NULL,
-	    inherit_callback, propname, B_FALSE);
+	if (recurse) {
+		ret = zfs_for_each(argc, argv, recurse, ZFS_TYPE_DATASET,
+		    NULL, NULL, inherit_recurse_cb, propname, B_FALSE);
+	} else {
+		ret = zfs_for_each(argc, argv, recurse, ZFS_TYPE_DATASET,
+		    NULL, NULL, inherit_cb, propname, B_FALSE);
+	}
 
 	return (ret);
 }
@@ -2189,7 +2236,7 @@
 }
 
 /*
- * zfs snapshot [-r] <fs@snap>
+ * zfs snapshot [-r] [-o prop=value] ... <fs@snap>
  *
  * Creates a snapshot with the given name.  While functionally equivalent to
  * 'zfs create', it is a separate command to differentiate intent.
@@ -2200,17 +2247,28 @@
 	boolean_t recursive = B_FALSE;
 	int ret;
 	char c;
+	nvlist_t *props;
+
+	if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0) {
+		(void) fprintf(stderr, gettext("internal error: "
+		    "out of memory\n"));
+		return (1);
+	}
 
 	/* check options */
-	while ((c = getopt(argc, argv, ":r")) != -1) {
+	while ((c = getopt(argc, argv, "ro:")) != -1) {
 		switch (c) {
+		case 'o':
+			if (parseprop(props))
+				return (1);
+			break;
 		case 'r':
 			recursive = B_TRUE;
 			break;
 		case '?':
 			(void) fprintf(stderr, gettext("invalid option '%c'\n"),
 			    optopt);
-			usage(B_FALSE);
+			goto usage;
 		}
 	}
 
@@ -2220,17 +2278,23 @@
 	/* check number of arguments */
 	if (argc < 1) {
 		(void) fprintf(stderr, gettext("missing snapshot argument\n"));
-		usage(B_FALSE);
+		goto usage;
 	}
 	if (argc > 1) {
 		(void) fprintf(stderr, gettext("too many arguments\n"));
-		usage(B_FALSE);
+		goto usage;
 	}
 
-	ret = zfs_snapshot(g_zfs, argv[0], recursive);
+	ret = zfs_snapshot(g_zfs, argv[0], recursive, props);
+	nvlist_free(props);
 	if (ret && recursive)
 		(void) fprintf(stderr, gettext("no snapshots were created\n"));
 	return (ret != 0);
+
+usage:
+	nvlist_free(props);
+	usage(B_FALSE);
+	return (-1);
 }
 
 /*
--- a/usr/src/cmd/zoneadm/zfs.c	Fri Aug 01 16:15:10 2008 -0700
+++ b/usr/src/cmd/zoneadm/zfs.c	Fri Aug 01 16:32:18 2008 -0700
@@ -304,7 +304,7 @@
 
 	if (pre_snapshot(presnapbuf) != Z_OK)
 		return (Z_ERR);
-	res = zfs_snapshot(g_zfs, snapshot_name, B_FALSE);
+	res = zfs_snapshot(g_zfs, snapshot_name, B_FALSE, NULL);
 	if (post_snapshot(postsnapbuf) != Z_OK)
 		return (Z_ERR);
 
--- a/usr/src/cmd/zpool/zpool_main.c	Fri Aug 01 16:15:10 2008 -0700
+++ b/usr/src/cmd/zpool/zpool_main.c	Fri Aug 01 16:32:18 2008 -0700
@@ -3462,6 +3462,7 @@
 		    "properties\n"));
 		(void) printf(gettext(" 10  Cache devices\n"));
 		(void) printf(gettext(" 11  Improved scrub performance\n"));
+		(void) printf(gettext(" 12  Snapshot properties\n"));
 		(void) printf(gettext("For more information on a particular "
 		    "version, including supported releases, see:\n\n"));
 		(void) printf("http://www.opensolaris.org/os/community/zfs/"
--- a/usr/src/grub/grub-0.95/stage2/fsys_zfs.c	Fri Aug 01 16:15:10 2008 -0700
+++ b/usr/src/grub/grub-0.95/stage2/fsys_zfs.c	Fri Aug 01 16:32:18 2008 -0700
@@ -269,8 +269,7 @@
 		return (-1);
 
 	if (uber->ub_magic == UBERBLOCK_MAGIC &&
-	    uber->ub_version >= SPA_VERSION_1 &&
-	    uber->ub_version <= SPA_VERSION)
+	    uber->ub_version > 0 && uber->ub_version <= SPA_VERSION)
 		return (0);
 
 	return (-1);
--- a/usr/src/grub/grub-0.95/stage2/zfs-include/zfs.h	Fri Aug 01 16:15:10 2008 -0700
+++ b/usr/src/grub/grub-0.95/stage2/zfs-include/zfs.h	Fri Aug 01 16:32:18 2008 -0700
@@ -29,18 +29,7 @@
 /*
  * On-disk version number.
  */
-#define	SPA_VERSION_1			1ULL
-#define	SPA_VERSION_2			2ULL
-#define	SPA_VERSION_3			3ULL
-#define	SPA_VERSION_4			4ULL
-#define	SPA_VERSION_5			5ULL
-#define	SPA_VERSION_6			6ULL
-#define	SPA_VERSION_7			7ULL
-#define	SPA_VERSION_8			8ULL
-#define	SPA_VERSION_9			9ULL
-#define	SPA_VERSION_10			10ULL
-#define	SPA_VERSION_11			11ULL
-#define	SPA_VERSION			SPA_VERSION_11
+#define	SPA_VERSION			12ULL
 
 /*
  * The following are configuration names used in the nvlist describing a pool's
--- a/usr/src/lib/libzfs/common/libzfs.h	Fri Aug 01 16:15:10 2008 -0700
+++ b/usr/src/lib/libzfs/common/libzfs.h	Fri Aug 01 16:32:18 2008 -0700
@@ -431,7 +431,7 @@
 extern int zfs_destroy(zfs_handle_t *);
 extern int zfs_destroy_snaps(zfs_handle_t *, char *);
 extern int zfs_clone(zfs_handle_t *, const char *, nvlist_t *);
-extern int zfs_snapshot(libzfs_handle_t *, const char *, boolean_t);
+extern int zfs_snapshot(libzfs_handle_t *, const char *, boolean_t, nvlist_t *);
 extern int zfs_rollback(zfs_handle_t *, zfs_handle_t *, boolean_t);
 extern int zfs_rename(zfs_handle_t *, const char *, boolean_t);
 extern int zfs_send(zfs_handle_t *, const char *, const char *,
--- a/usr/src/lib/libzfs/common/libzfs_dataset.c	Fri Aug 01 16:15:10 2008 -0700
+++ b/usr/src/lib/libzfs/common/libzfs_dataset.c	Fri Aug 01 16:32:18 2008 -0700
@@ -578,13 +578,6 @@
 	int chosen_normal = -1;
 	int chosen_utf = -1;
 
-	if (type == ZFS_TYPE_SNAPSHOT) {
-		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
-		    "snapshot properties cannot be modified"));
-		(void) zfs_error(hdl, EZFS_PROPTYPE, errbuf);
-		return (NULL);
-	}
-
 	if (nvlist_alloc(&ret, NV_UNIQUE_NAME, 0) != 0) {
 		(void) no_memory(hdl);
 		return (NULL);
@@ -632,6 +625,13 @@
 			continue;
 		}
 
+		if (type == ZFS_TYPE_SNAPSHOT) {
+			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+			    "this property can not be modified for snapshots"));
+			(void) zfs_error(hdl, EZFS_PROPTYPE, errbuf);
+			goto error;
+		}
+
 		if (!zfs_prop_valid_for_type(prop, type)) {
 			zfs_error_aux(hdl,
 			    dgettext(TEXT_DOMAIN, "'%s' does not "
@@ -3450,10 +3450,11 @@
  * Takes a snapshot of the given dataset.
  */
 int
-zfs_snapshot(libzfs_handle_t *hdl, const char *path, boolean_t recursive)
+zfs_snapshot(libzfs_handle_t *hdl, const char *path, boolean_t recursive,
+    nvlist_t *props)
 {
 	const char *delim;
-	char *parent;
+	char parent[ZFS_MAXNAMELEN];
 	zfs_handle_t *zhp;
 	zfs_cmd_t zc = { 0 };
 	int ret;
@@ -3466,16 +3467,27 @@
 	if (!zfs_validate_name(hdl, path, ZFS_TYPE_SNAPSHOT, B_TRUE))
 		return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
 
+	if (props) {
+		if ((props = zfs_valid_proplist(hdl, ZFS_TYPE_SNAPSHOT,
+		    props, B_FALSE, NULL, errbuf)) == NULL)
+			return (-1);
+
+		if (zcmd_write_src_nvlist(hdl, &zc, props) != 0) {
+			nvlist_free(props);
+			return (-1);
+		}
+
+		nvlist_free(props);
+	}
+
 	/* make sure the parent exists and is of the appropriate type */
 	delim = strchr(path, '@');
-	if ((parent = zfs_alloc(hdl, delim - path + 1)) == NULL)
-		return (-1);
 	(void) strncpy(parent, path, delim - path);
 	parent[delim - path] = '\0';
 
 	if ((zhp = zfs_open(hdl, parent, ZFS_TYPE_FILESYSTEM |
 	    ZFS_TYPE_VOLUME)) == NULL) {
-		free(parent);
+		zcmd_free_nvlists(&zc);
 		return (-1);
 	}
 
@@ -3488,6 +3500,8 @@
 	zc.zc_cookie = recursive;
 	ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_SNAPSHOT, &zc);
 
+	zcmd_free_nvlists(&zc);
+
 	/*
 	 * if it was recursive, the one that actually failed will be in
 	 * zc.zc_name.
@@ -3510,7 +3524,6 @@
 			    dgettext(TEXT_DOMAIN,
 			    "Volume successfully snapshotted, but device links "
 			    "were not created"));
-			free(parent);
 			zfs_close(zhp);
 			return (-1);
 		}
@@ -3519,7 +3532,6 @@
 	if (ret != 0)
 		(void) zfs_standard_error(hdl, errno, errbuf);
 
-	free(parent);
 	zfs_close(zhp);
 
 	return (ret);
--- a/usr/src/lib/libzfs/common/libzfs_sendrecv.c	Fri Aug 01 16:15:10 2008 -0700
+++ b/usr/src/lib/libzfs/common/libzfs_sendrecv.c	Fri Aug 01 16:32:18 2008 -0700
@@ -166,6 +166,7 @@
 	uint64_t parent_fromsnap_guid;
 	nvlist_t *parent_snaps;
 	nvlist_t *fss;
+	nvlist_t *snapprops;
 	const char *fromsnap;
 	const char *tosnap;
 
@@ -182,6 +183,7 @@
 	 *
 	 *	 "props" -> { name -> value (only if set here) }
 	 *	 "snaps" -> { name (lastname) -> number (guid) }
+	 *	 "snapprops" -> { name (lastname) -> { name -> value } }
 	 *
 	 *	 "origin" -> number (guid) (if clone)
 	 *	 "sent" -> boolean (not on-disk)
@@ -192,12 +194,15 @@
 	 */
 } send_data_t;
 
+static void send_iterate_prop(zfs_handle_t *zhp, nvlist_t *nv);
+
 static int
 send_iterate_snap(zfs_handle_t *zhp, void *arg)
 {
 	send_data_t *sd = arg;
 	uint64_t guid = zhp->zfs_dmustats.dds_guid;
 	char *snapname;
+	nvlist_t *nv;
 
 	snapname = strrchr(zhp->zfs_name, '@')+1;
 
@@ -212,6 +217,11 @@
 		sd->parent_fromsnap_guid = guid;
 	}
 
+	VERIFY(0 == nvlist_alloc(&nv, NV_UNIQUE_NAME, 0));
+	send_iterate_prop(zhp, nv);
+	VERIFY(0 == nvlist_add_nvlist(sd->snapprops, snapname, nv));
+	nvlist_free(nv);
+
 	zfs_close(zhp);
 	return (0);
 }
@@ -235,6 +245,8 @@
 			uint64_t value;
 			verify(nvlist_lookup_uint64(propnv,
 			    ZPROP_VALUE, &value) == 0);
+			if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT)
+				continue;
 		} else {
 			char *source;
 			if (nvlist_lookup_string(propnv,
@@ -292,9 +304,12 @@
 	/* iterate over snaps, and set sd->parent_fromsnap_guid */
 	sd->parent_fromsnap_guid = 0;
 	VERIFY(0 == nvlist_alloc(&sd->parent_snaps, NV_UNIQUE_NAME, 0));
+	VERIFY(0 == nvlist_alloc(&sd->snapprops, NV_UNIQUE_NAME, 0));
 	(void) zfs_iter_snapshots(zhp, send_iterate_snap, sd);
 	VERIFY(0 == nvlist_add_nvlist(nvfs, "snaps", sd->parent_snaps));
+	VERIFY(0 == nvlist_add_nvlist(nvfs, "snapprops", sd->snapprops));
 	nvlist_free(sd->parent_snaps);
+	nvlist_free(sd->snapprops);
 
 	/* add this fs to nvlist */
 	(void) snprintf(guidstring, sizeof (guidstring),
@@ -1180,7 +1195,7 @@
 		    snapelem; snapelem = nextsnapelem) {
 			uint64_t thisguid;
 			char *stream_snapname;
-			nvlist_t *found;
+			nvlist_t *found, *props;
 
 			nextsnapelem = nvlist_next_nvpair(snaps, snapelem);
 
@@ -1209,6 +1224,22 @@
 
 			stream_nvfs = found;
 
+			if (0 == nvlist_lookup_nvlist(stream_nvfs, "snapprops",
+			    &props) && 0 == nvlist_lookup_nvlist(props,
+			    stream_snapname, &props)) {
+				zfs_cmd_t zc = { 0 };
+
+				zc.zc_cookie = B_TRUE; /* clear current props */
+				snprintf(zc.zc_name, sizeof (zc.zc_name),
+				    "%s@%s", fsname, nvpair_name(snapelem));
+				if (zcmd_write_src_nvlist(hdl, &zc,
+				    props) == 0) {
+					(void) zfs_ioctl(hdl,
+					    ZFS_IOC_SET_PROP, &zc);
+					zcmd_free_nvlists(&zc);
+				}
+			}
+
 			/* check for different snapname */
 			if (strcmp(nvpair_name(snapelem),
 			    stream_snapname) != 0) {
@@ -1530,6 +1561,7 @@
 	boolean_t stream_wantsnewfs;
 	uint64_t parent_snapguid = 0;
 	prop_changelist_t *clp = NULL;
+	nvlist_t *snapprops_nvlist = NULL;
 
 	begin_time = time(NULL);
 
@@ -1537,7 +1569,9 @@
 	    "cannot receive"));
 
 	if (stream_avl != NULL) {
-		nvlist_t *fs = fsavl_find(stream_avl, drrb->drr_toguid, NULL);
+		char *snapname;
+		nvlist_t *fs = fsavl_find(stream_avl, drrb->drr_toguid,
+		    &snapname);
 		nvlist_t *props;
 		int ret;
 
@@ -1555,6 +1589,11 @@
 		if (err)
 			nvlist_free(props);
 
+		if (0 == nvlist_lookup_nvlist(fs, "snapprops", &props)) {
+			VERIFY(0 == nvlist_lookup_nvlist(props,
+			    snapname, &snapprops_nvlist));
+		}
+
 		if (ret != 0)
 			return (-1);
 	}
@@ -1787,6 +1826,18 @@
 
 	err = ioctl_err = zfs_ioctl(hdl, ZFS_IOC_RECV, &zc);
 	ioctl_errno = errno;
+	zcmd_free_nvlists(&zc);
+
+	if (err == 0 && snapprops_nvlist) {
+		zfs_cmd_t zc2 = { 0 };
+
+		strcpy(zc2.zc_name, zc.zc_value);
+		if (zcmd_write_src_nvlist(hdl, &zc2, snapprops_nvlist) == 0) {
+			(void) zfs_ioctl(hdl, ZFS_IOC_SET_PROP, &zc2);
+			zcmd_free_nvlists(&zc2);
+		}
+	}
+
 	if (err && (ioctl_errno == ENOENT || ioctl_errno == ENODEV)) {
 		/*
 		 * It may be that this snapshot already exists,
@@ -1822,7 +1873,6 @@
 		*cp = '@';
 	}
 
-	zcmd_free_nvlists(&zc);
 
 	if (ioctl_err != 0) {
 		switch (ioctl_errno) {
--- a/usr/src/uts/common/fs/zfs/arc.c	Fri Aug 01 16:15:10 2008 -0700
+++ b/usr/src/uts/common/fs/zfs/arc.c	Fri Aug 01 16:32:18 2008 -0700
@@ -2417,6 +2417,7 @@
     uint32_t *arc_flags, const zbookmark_t *zb)
 {
 	int err;
+	arc_buf_hdr_t *hdr = pbuf->b_hdr;
 
 	ASSERT(!refcount_is_zero(&pbuf->b_hdr->b_refcnt));
 	ASSERT3U((char *)bp - (char *)pbuf->b_data, <, pbuf->b_hdr->b_size);
@@ -2425,6 +2426,7 @@
 	err = arc_read_nolock(pio, spa, bp, done, private, priority,
 	    zio_flags, arc_flags, zb);
 
+	ASSERT3P(hdr, ==, pbuf->b_hdr);
 	rw_exit(&pbuf->b_hdr->b_datalock);
 	return (err);
 }
--- a/usr/src/uts/common/fs/zfs/dsl_dataset.c	Fri Aug 01 16:15:10 2008 -0700
+++ b/usr/src/uts/common/fs/zfs/dsl_dataset.c	Fri Aug 01 16:32:18 2008 -0700
@@ -397,11 +397,11 @@
 			if (need_lock)
 				rw_enter(&dp->dp_config_rwlock, RW_READER);
 
-			err = dsl_prop_get_ds_locked(ds->ds_dir,
+			err = dsl_prop_get_ds(ds,
 			    "refreservation", sizeof (uint64_t), 1,
 			    &ds->ds_reserved, NULL);
 			if (err == 0) {
-				err = dsl_prop_get_ds_locked(ds->ds_dir,
+				err = dsl_prop_get_ds(ds,
 				    "refquota", sizeof (uint64_t), 1,
 				    &ds->ds_quota, NULL);
 			}
@@ -1674,6 +1674,10 @@
 		VERIFY(0 == dmu_object_free(mos,
 		    ds->ds_phys->ds_next_clones_obj, tx));
 	}
+	if (ds->ds_phys->ds_props_obj != 0) {
+		VERIFY(0 == zap_destroy(mos,
+		    ds->ds_phys->ds_props_obj, tx));
+	}
 	dsl_dir_close(ds->ds_dir, ds);
 	ds->ds_dir = NULL;
 	dsl_dataset_drain_refs(ds, tag);
--- a/usr/src/uts/common/fs/zfs/dsl_deleg.c	Fri Aug 01 16:15:10 2008 -0700
+++ b/usr/src/uts/common/fs/zfs/dsl_deleg.c	Fri Aug 01 16:32:18 2008 -0700
@@ -19,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -533,42 +533,33 @@
  * Check if user has requested permission.
  */
 int
-dsl_deleg_access(const char *ddname, const char *perm, cred_t *cr)
+dsl_deleg_access(const char *dsname, const char *perm, cred_t *cr)
 {
-	dsl_dir_t *dd, *startdd;
+	dsl_dataset_t *ds;
+	dsl_dir_t *dd;
 	dsl_pool_t *dp;
 	void *cookie;
 	int	error;
 	char	checkflag = ZFS_DELEG_LOCAL;
-	const char *tail;
 	objset_t *mos;
 	avl_tree_t permsets;
 	perm_set_t *setnode;
 
-	/*
-	 * Use tail so that zfs_ioctl() code doesn't have
-	 * to always to to figure out parent name in order
-	 * to do access check.  for example renaming a snapshot
-	 */
-	error = dsl_dir_open(ddname, FTAG, &startdd, &tail);
+	error = dsl_dataset_hold(dsname, FTAG, &ds);
 	if (error)
 		return (error);
 
-	if (tail && tail[0] != '@') {
-		dsl_dir_close(startdd, FTAG);
-		return (ENOENT);
-	}
-	dp = startdd->dd_pool;
+	dp = ds->ds_dir->dd_pool;
 	mos = dp->dp_meta_objset;
 
 	if (dsl_delegation_on(mos) == B_FALSE) {
-		dsl_dir_close(startdd, FTAG);
+		dsl_dataset_rele(ds, FTAG);
 		return (ECANCELED);
 	}
 
 	if (spa_version(dmu_objset_spa(dp->dp_meta_objset)) <
 	    SPA_VERSION_DELEGATED_PERMS) {
-		dsl_dir_close(startdd, FTAG);
+		dsl_dataset_rele(ds, FTAG);
 		return (EPERM);
 	}
 
@@ -576,7 +567,7 @@
 	    offsetof(perm_set_t, p_node));
 
 	rw_enter(&dp->dp_config_rwlock, RW_READER);
-	for (dd = startdd; dd != NULL; dd = dd->dd_parent,
+	for (dd = ds->ds_dir; dd != NULL; dd = dd->dd_parent,
 	    checkflag = ZFS_DELEG_DESCENDENT) {
 		uint64_t zapobj;
 		boolean_t expanded;
@@ -588,7 +579,7 @@
 		if (!INGLOBALZONE(curproc)) {
 			uint64_t zoned;
 
-			if (dsl_prop_get_ds_locked(dd,
+			if (dsl_prop_get_dd(dd,
 			    zfs_prop_to_name(ZFS_PROP_ZONED),
 			    8, 1, &zoned, NULL) != 0)
 				break;
@@ -637,7 +628,7 @@
 	error = EPERM;
 success:
 	rw_exit(&dp->dp_config_rwlock);
-	dsl_dir_close(startdd, FTAG);
+	dsl_dataset_rele(ds, FTAG);
 
 	cookie = NULL;
 	while ((setnode = avl_destroy_nodes(&permsets, &cookie)) != NULL)
--- a/usr/src/uts/common/fs/zfs/dsl_prop.c	Fri Aug 01 16:15:10 2008 -0700
+++ b/usr/src/uts/common/fs/zfs/dsl_prop.c	Fri Aug 01 16:32:18 2008 -0700
@@ -68,13 +68,16 @@
 	return (0);
 }
 
-static int
-dsl_prop_get_impl(dsl_dir_t *dd, const char *propname,
+int
+dsl_prop_get_dd(dsl_dir_t *dd, const char *propname,
     int intsz, int numint, void *buf, char *setpoint)
 {
 	int err = ENOENT;
+	objset_t *mos = dd->dd_pool->dp_meta_objset;
 	zfs_prop_t prop;
 
+	ASSERT(RW_LOCK_HELD(&dd->dd_pool->dp_config_rwlock));
+
 	if (setpoint)
 		setpoint[0] = '\0';
 
@@ -85,7 +88,6 @@
 	 * ouside this loop.
 	 */
 	for (; dd != NULL; dd = dd->dd_parent) {
-		objset_t *mos = dd->dd_pool->dp_meta_objset;
 		ASSERT(RW_LOCK_HELD(&dd->dd_pool->dp_config_rwlock));
 		err = zap_lookup(mos, dd->dd_phys->dd_props_zapobj,
 		    propname, intsz, numint, buf);
@@ -107,6 +109,26 @@
 	return (err);
 }
 
+int
+dsl_prop_get_ds(dsl_dataset_t *ds, const char *propname,
+    int intsz, int numint, void *buf, char *setpoint)
+{
+	ASSERT(RW_LOCK_HELD(&ds->ds_dir->dd_pool->dp_config_rwlock));
+
+	if (ds->ds_phys->ds_props_obj) {
+		int err = zap_lookup(ds->ds_dir->dd_pool->dp_meta_objset,
+		    ds->ds_phys->ds_props_obj, propname, intsz, numint, buf);
+		if (err != ENOENT) {
+			if (setpoint)
+				dsl_dataset_name(ds, setpoint);
+			return (err);
+		}
+	}
+
+	return (dsl_prop_get_dd(ds->ds_dir, propname,
+	    intsz, numint, buf, setpoint));
+}
+
 /*
  * Register interest in the named property.  We'll call the callback
  * once to notify it of the current property value, and again each time
@@ -119,19 +141,20 @@
     dsl_prop_changed_cb_t *callback, void *cbarg)
 {
 	dsl_dir_t *dd = ds->ds_dir;
+	dsl_pool_t *dp = dd->dd_pool;
 	uint64_t value;
 	dsl_prop_cb_record_t *cbr;
 	int err;
 	int need_rwlock;
 
-	need_rwlock = !RW_WRITE_HELD(&dd->dd_pool->dp_config_rwlock);
+	need_rwlock = !RW_WRITE_HELD(&dp->dp_config_rwlock);
 	if (need_rwlock)
-		rw_enter(&dd->dd_pool->dp_config_rwlock, RW_READER);
+		rw_enter(&dp->dp_config_rwlock, RW_READER);
 
-	err = dsl_prop_get_impl(dd, propname, 8, 1, &value, NULL);
+	err = dsl_prop_get_ds(ds, propname, 8, 1, &value, NULL);
 	if (err != 0) {
 		if (need_rwlock)
-			rw_exit(&dd->dd_pool->dp_config_rwlock);
+			rw_exit(&dp->dp_config_rwlock);
 		return (err);
 	}
 
@@ -147,56 +170,30 @@
 
 	cbr->cbr_func(cbr->cbr_arg, value);
 
-	VERIFY(0 == dsl_dir_open_obj(dd->dd_pool, dd->dd_object,
+	VERIFY(0 == dsl_dir_open_obj(dp, dd->dd_object,
 	    NULL, cbr, &dd));
 	if (need_rwlock)
-		rw_exit(&dd->dd_pool->dp_config_rwlock);
-	/* Leave dataset open until this callback is unregistered */
+		rw_exit(&dp->dp_config_rwlock);
+	/* Leave dir open until this callback is unregistered */
 	return (0);
 }
 
 int
-dsl_prop_get_dd(dsl_dir_t *dd, const char *propname,
-    int intsz, int numints, void *buf, char *setpoint)
-{
-	int err;
-
-	rw_enter(&dd->dd_pool->dp_config_rwlock, RW_READER);
-	err = dsl_prop_get_impl(dd, propname, intsz, numints, buf, setpoint);
-	rw_exit(&dd->dd_pool->dp_config_rwlock);
-
-	return (err);
-}
-
-/*
- * Get property when config lock is already held.
- */
-int dsl_prop_get_ds_locked(dsl_dir_t *dd, const char *propname,
+dsl_prop_get(const char *dsname, const char *propname,
     int intsz, int numints, void *buf, char *setpoint)
 {
-	ASSERT(RW_LOCK_HELD(&dd->dd_pool->dp_config_rwlock));
-	return (dsl_prop_get_impl(dd, propname, intsz, numints, buf, setpoint));
-}
-
-int
-dsl_prop_get(const char *ddname, const char *propname,
-    int intsz, int numints, void *buf, char *setpoint)
-{
-	dsl_dir_t *dd;
-	const char *tail;
+	dsl_dataset_t *ds;
 	int err;
 
-	err = dsl_dir_open(ddname, FTAG, &dd, &tail);
+	err = dsl_dataset_hold(dsname, FTAG, &ds);
 	if (err)
 		return (err);
-	if (tail && tail[0] != '@') {
-		dsl_dir_close(dd, FTAG);
-		return (ENOENT);
-	}
 
-	err = dsl_prop_get_dd(dd, propname, intsz, numints, buf, setpoint);
+	rw_enter(&ds->ds_dir->dd_pool->dp_config_rwlock, RW_READER);
+	err = dsl_prop_get_ds(ds, propname, intsz, numints, buf, setpoint);
+	rw_exit(&ds->ds_dir->dd_pool->dp_config_rwlock);
 
-	dsl_dir_close(dd, FTAG);
+	dsl_dataset_rele(ds, FTAG);
 	return (err);
 }
 
@@ -282,6 +279,7 @@
 	zap_cursor_t zc;
 	zap_attribute_t *za;
 	int err;
+	uint64_t dummyval;
 
 	ASSERT(RW_WRITE_HELD(&dp->dp_config_rwlock));
 	err = dsl_dir_open_obj(dp, ddobj, NULL, FTAG, &dd);
@@ -294,7 +292,7 @@
 		 * being inherited here or below; stop the recursion.
 		 */
 		err = zap_lookup(mos, dd->dd_phys->dd_props_zapobj, propname,
-		    8, 1, &value);
+		    8, 1, &dummyval);
 		if (err == 0) {
 			dsl_dir_close(dd, FTAG);
 			return;
@@ -303,11 +301,22 @@
 	}
 
 	mutex_enter(&dd->dd_lock);
-	for (cbr = list_head(&dd->dd_prop_cbs);
-	    cbr; cbr = list_next(&dd->dd_prop_cbs, cbr)) {
-		if (strcmp(cbr->cbr_propname, propname) == 0) {
-			cbr->cbr_func(cbr->cbr_arg, value);
-		}
+	for (cbr = list_head(&dd->dd_prop_cbs); cbr;
+	    cbr = list_next(&dd->dd_prop_cbs, cbr)) {
+		uint64_t propobj = cbr->cbr_ds->ds_phys->ds_props_obj;
+
+		if (strcmp(cbr->cbr_propname, propname) != 0)
+			continue;
+
+		/*
+		 * If the property is set on this ds, then it is not
+		 * inherited here; don't call the callback.
+		 */
+		if (propobj && 0 == zap_lookup(mos, propobj, propname,
+		    8, 1, &dummyval))
+			continue;
+
+		cbr->cbr_func(cbr->cbr_arg, value);
 	}
 	mutex_exit(&dd->dd_lock);
 
@@ -335,22 +344,35 @@
 static void
 dsl_prop_set_sync(void *arg1, void *arg2, cred_t *cr, dmu_tx_t *tx)
 {
-	dsl_dir_t *dd = arg1;
+	dsl_dataset_t *ds = arg1;
 	struct prop_set_arg *psa = arg2;
-	objset_t *mos = dd->dd_pool->dp_meta_objset;
-	uint64_t zapobj = dd->dd_phys->dd_props_zapobj;
-	uint64_t intval;
+	objset_t *mos = ds->ds_dir->dd_pool->dp_meta_objset;
+	uint64_t zapobj, intval;
 	int isint;
 	char valbuf[32];
 	char *valstr;
 
 	isint = (dodefault(psa->name, 8, 1, &intval) == 0);
 
+	if (dsl_dataset_is_snapshot(ds)) {
+		ASSERT(spa_version(ds->ds_dir->dd_pool->dp_spa) >=
+		    SPA_VERSION_SNAP_PROPS);
+		if (ds->ds_phys->ds_props_obj == 0) {
+			dmu_buf_will_dirty(ds->ds_dbuf, tx);
+			ds->ds_phys->ds_props_obj =
+			    zap_create(mos,
+			    DMU_OT_DSL_PROPS, DMU_OT_NONE, 0, tx);
+		}
+		zapobj = ds->ds_phys->ds_props_obj;
+	} else {
+		zapobj = ds->ds_dir->dd_phys->dd_props_zapobj;
+	}
+
 	if (psa->numints == 0) {
 		int err = zap_remove(mos, zapobj, psa->name, tx);
 		ASSERT(err == 0 || err == ENOENT);
 		if (isint) {
-			VERIFY(0 == dsl_prop_get_impl(dd->dd_parent,
+			VERIFY(0 == dsl_prop_get_ds(ds,
 			    psa->name, 8, 1, &intval, NULL));
 		}
 	} else {
@@ -361,8 +383,25 @@
 	}
 
 	if (isint) {
-		dsl_prop_changed_notify(dd->dd_pool,
-		    dd->dd_object, psa->name, intval, TRUE);
+		if (dsl_dataset_is_snapshot(ds)) {
+			dsl_prop_cb_record_t *cbr;
+			/*
+			 * It's a snapshot; nothing can inherit this
+			 * property, so just look for callbacks on this
+			 * ds here.
+			 */
+			mutex_enter(&ds->ds_dir->dd_lock);
+			for (cbr = list_head(&ds->ds_dir->dd_prop_cbs); cbr;
+			    cbr = list_next(&ds->ds_dir->dd_prop_cbs, cbr)) {
+				if (cbr->cbr_ds == ds &&
+				    strcmp(cbr->cbr_propname, psa->name) == 0)
+					cbr->cbr_func(cbr->cbr_arg, intval);
+			}
+			mutex_exit(&ds->ds_dir->dd_lock);
+		} else {
+			dsl_prop_changed_notify(ds->ds_dir->dd_pool,
+			    ds->ds_dir->dd_object, psa->name, intval, TRUE);
+		}
 	}
 	if (isint) {
 		(void) snprintf(valbuf, sizeof (valbuf),
@@ -372,9 +411,8 @@
 		valstr = (char *)psa->buf;
 	}
 	spa_history_internal_log((psa->numints == 0) ? LOG_DS_INHERIT :
-	    LOG_DS_PROPSET, dd->dd_pool->dp_spa, tx, cr,
-	    "%s=%s dataset = %llu", psa->name, valstr,
-	    dd->dd_phys->dd_head_dataset_obj);
+	    LOG_DS_PROPSET, ds->ds_dir->dd_pool->dp_spa, tx, cr,
+	    "%s=%s dataset = %llu", psa->name, valstr, ds->ds_object);
 }
 
 void
@@ -396,27 +434,13 @@
 }
 
 int
-dsl_prop_set_dd(dsl_dir_t *dd, const char *propname,
+dsl_prop_set(const char *dsname, const char *propname,
     int intsz, int numints, const void *buf)
 {
+	dsl_dataset_t *ds;
+	int err;
 	struct prop_set_arg psa;
 
-	psa.name = propname;
-	psa.intsz = intsz;
-	psa.numints = numints;
-	psa.buf = buf;
-
-	return (dsl_sync_task_do(dd->dd_pool,
-	    NULL, dsl_prop_set_sync, dd, &psa, 2));
-}
-
-int
-dsl_prop_set(const char *ddname, const char *propname,
-    int intsz, int numints, const void *buf)
-{
-	dsl_dir_t *dd;
-	int err;
-
 	/*
 	 * We must do these checks before we get to the syncfunc, since
 	 * it can't fail.
@@ -426,11 +450,24 @@
 	if (intsz * numints >= ZAP_MAXVALUELEN)
 		return (E2BIG);
 
-	err = dsl_dir_open(ddname, FTAG, &dd, NULL);
+	err = dsl_dataset_hold(dsname, FTAG, &ds);
 	if (err)
 		return (err);
-	err = dsl_prop_set_dd(dd, propname, intsz, numints, buf);
-	dsl_dir_close(dd, FTAG);
+
+	if (dsl_dataset_is_snapshot(ds) &&
+	    spa_version(ds->ds_dir->dd_pool->dp_spa) < SPA_VERSION_SNAP_PROPS) {
+		dsl_dataset_rele(ds, FTAG);
+		return (ENOTSUP);
+	}
+
+	psa.name = propname;
+	psa.intsz = intsz;
+	psa.numints = numints;
+	psa.buf = buf;
+	err = dsl_sync_task_do(ds->ds_dir->dd_pool,
+	    NULL, dsl_prop_set_sync, ds, &psa, 2);
+
+	dsl_dataset_rele(ds, FTAG);
 	return (err);
 }
 
@@ -442,43 +479,51 @@
 {
 	dsl_dataset_t *ds = os->os->os_dsl_dataset;
 	dsl_dir_t *dd = ds->ds_dir;
-	boolean_t snapshot;
+	boolean_t snapshot = dsl_dataset_is_snapshot(ds);
 	int err = 0;
-	dsl_pool_t *dp;
-	objset_t *mos;
-
-	snapshot = dsl_dataset_is_snapshot(ds);
+	dsl_pool_t *dp = dd->dd_pool;
+	objset_t *mos = dp->dp_meta_objset;
+	uint64_t propobj = ds->ds_phys->ds_props_obj;
 
 	VERIFY(nvlist_alloc(nvp, NV_UNIQUE_NAME, KM_SLEEP) == 0);
 
-	dp = dd->dd_pool;
-	mos = dp->dp_meta_objset;
+	if (local && snapshot && !propobj)
+		return (0);
 
 	rw_enter(&dp->dp_config_rwlock, RW_READER);
-	for (; dd != NULL; dd = dd->dd_parent) {
+	while (dd != NULL) {
 		char setpoint[MAXNAMELEN];
 		zap_cursor_t zc;
 		zap_attribute_t za;
+		dsl_dir_t *dd_next;
 
-		dsl_dir_name(dd, setpoint);
+		if (propobj) {
+			dsl_dataset_name(ds, setpoint);
+			dd_next = dd;
+		} else {
+			dsl_dir_name(dd, setpoint);
+			propobj = dd->dd_phys->dd_props_zapobj;
+			dd_next = dd->dd_parent;
+		}
 
-		for (zap_cursor_init(&zc, mos, dd->dd_phys->dd_props_zapobj);
+		for (zap_cursor_init(&zc, mos, propobj);
 		    (err = zap_cursor_retrieve(&zc, &za)) == 0;
 		    zap_cursor_advance(&zc)) {
 			nvlist_t *propval;
-			zfs_prop_t prop;
-			/*
-			 * Skip non-inheritable properties.
-			 */
-			if ((prop = zfs_name_to_prop(za.za_name)) !=
-			    ZPROP_INVAL && !zfs_prop_inheritable(prop) &&
-			    dd != ds->ds_dir)
+			zfs_prop_t prop = zfs_name_to_prop(za.za_name);
+
+			/* Skip non-inheritable properties. */
+			if (prop != ZPROP_INVAL &&
+			    !zfs_prop_inheritable(prop) &&
+			    (dd != ds->ds_dir || (snapshot && dd != dd_next)))
 				continue;
 
-			if (snapshot &&
+			/* Skip properties not valid for this type. */
+			if (snapshot && prop != ZPROP_INVAL &&
 			    !zfs_prop_valid_for_type(prop, ZFS_TYPE_SNAPSHOT))
 				continue;
 
+			/* Skip properties already defined */
 			if (nvlist_lookup_nvlist(*nvp, za.za_name,
 			    &propval) == 0)
 				continue;
@@ -491,10 +536,8 @@
 				 */
 				char *tmp = kmem_alloc(za.za_num_integers,
 				    KM_SLEEP);
-				err = zap_lookup(mos,
-				    dd->dd_phys->dd_props_zapobj,
-				    za.za_name, 1, za.za_num_integers,
-				    tmp);
+				err = zap_lookup(mos, propobj,
+				    za.za_name, 1, za.za_num_integers, tmp);
 				if (err != 0) {
 					kmem_free(tmp, za.za_num_integers);
 					break;
@@ -528,6 +571,8 @@
 		 */
 		if (local)
 			break;
+		dd = dd_next;
+		propobj = 0;
 	}
 	rw_exit(&dp->dp_config_rwlock);
 
--- a/usr/src/uts/common/fs/zfs/sys/dsl_dataset.h	Fri Aug 01 16:15:10 2008 -0700
+++ b/usr/src/uts/common/fs/zfs/sys/dsl_dataset.h	Fri Aug 01 16:32:18 2008 -0700
@@ -93,8 +93,9 @@
 	uint64_t ds_guid;
 	uint64_t ds_flags;		/* DS_FLAG_* */
 	blkptr_t ds_bp;
-	uint64_t ds_next_clones_obj;	/* DMU_OT_DSL_NEXT_CLONES */
-	uint64_t ds_pad[7]; /* pad out to 320 bytes for good measure */
+	uint64_t ds_next_clones_obj;	/* DMU_OT_DSL_CLONES */
+	uint64_t ds_props_obj;		/* DMU_OT_DSL_PROPS for snaps */
+	uint64_t ds_pad[6]; /* pad out to 320 bytes for good measure */
 } dsl_dataset_phys_t;
 
 typedef struct dsl_dataset {
--- a/usr/src/uts/common/fs/zfs/sys/dsl_prop.h	Fri Aug 01 16:15:10 2008 -0700
+++ b/usr/src/uts/common/fs/zfs/sys/dsl_prop.h	Fri Aug 01 16:32:18 2008 -0700
@@ -37,6 +37,7 @@
 #endif
 
 struct dsl_dataset;
+struct dsl_dir;
 
 /* The callback func may not call into the DMU or DSL! */
 typedef void (dsl_prop_changed_cb_t)(void *arg, uint64_t newval);
@@ -55,20 +56,18 @@
     dsl_prop_changed_cb_t *callback, void *cbarg);
 int dsl_prop_numcb(struct dsl_dataset *ds);
 
-int dsl_prop_get_dd(dsl_dir_t *dd, const char *propname,
-    int intsz, int numints, void *buf, char *setpoint);
 int dsl_prop_get(const char *ddname, const char *propname,
     int intsz, int numints, void *buf, char *setpoint);
 int dsl_prop_get_integer(const char *ddname, const char *propname,
     uint64_t *valuep, char *setpoint);
 int dsl_prop_get_all(objset_t *os, nvlist_t **nvp, boolean_t local);
-int dsl_prop_get_ds_locked(dsl_dir_t *dd, const char *propname,
+int dsl_prop_get_ds(struct dsl_dataset *ds, const char *propname,
+    int intsz, int numints, void *buf, char *setpoint);
+int dsl_prop_get_dd(struct dsl_dir *dd, const char *propname,
     int intsz, int numints, void *buf, char *setpoint);
 
 int dsl_prop_set(const char *ddname, const char *propname,
     int intsz, int numints, const void *buf);
-int dsl_prop_set_dd(dsl_dir_t *dd, const char *propname,
-    int intsz, int numints, const void *buf);
 void dsl_prop_set_uint64_sync(dsl_dir_t *dd, const char *name, uint64_t val,
     cred_t *cr, dmu_tx_t *tx);
 
--- a/usr/src/uts/common/fs/zfs/zfs_ioctl.c	Fri Aug 01 16:15:10 2008 -0700
+++ b/usr/src/uts/common/fs/zfs/zfs_ioctl.c	Fri Aug 01 16:32:18 2008 -0700
@@ -92,6 +92,7 @@
 	boolean_t		zvec_his_log;
 } zfs_ioc_vec_t;
 
+static void clear_props(char *dataset, nvlist_t *props);
 static int zfs_fill_zplprops_root(uint64_t, nvlist_t *, nvlist_t *,
     boolean_t *);
 int zfs_set_prop_nvlist(const char *, nvlist_t *);
@@ -1465,8 +1466,6 @@
 				return (ENOTSUP);
 			break;
 		}
-		if ((error = zfs_secpolicy_setprop(name, prop, CRED())) != 0)
-			return (error);
 	}
 
 	elem = NULL;
@@ -1579,6 +1578,7 @@
  * zc_name		name of filesystem
  * zc_value		name of property to inherit
  * zc_nvlist_src{_size}	nvlist of properties to apply
+ * zc_cookie		clear existing local props?
  *
  * outputs:		none
  */
@@ -1592,6 +1592,21 @@
 	    &nvl)) != 0)
 		return (error);
 
+	if (zc->zc_cookie) {
+		nvlist_t *origprops;
+		objset_t *os;
+
+		if (dmu_objset_open(zc->zc_name, DMU_OST_ANY,
+		    DS_MODE_USER | DS_MODE_READONLY, &os) == 0) {
+			if (dsl_prop_get_all(os, &origprops, TRUE) == 0) {
+				clear_props(zc->zc_name, origprops);
+				nvlist_free(origprops);
+			}
+			dmu_objset_close(os);
+		}
+
+	}
+
 	error = zfs_set_prop_nvlist(zc->zc_name, nvl);
 
 	nvlist_free(nvl);
@@ -2129,6 +2144,27 @@
 	return (error);
 }
 
+struct snap_prop_arg {
+	nvlist_t *nvprops;
+	const char *snapname;
+};
+
+static int
+set_snap_props(char *name, void *arg)
+{
+	struct snap_prop_arg *snpa = arg;
+	int len = strlen(name) + strlen(snpa->snapname) + 2;
+	char *buf = kmem_alloc(len, KM_SLEEP);
+	int err;
+
+	(void) snprintf(buf, len, "%s@%s", name, snpa->snapname);
+	err = zfs_set_prop_nvlist(buf, snpa->nvprops);
+	if (err)
+		(void) dmu_objset_destroy(buf);
+	kmem_free(buf, len);
+	return (err);
+}
+
 /*
  * inputs:
  * zc_name	name of filesystem
@@ -2140,10 +2176,40 @@
 static int
 zfs_ioc_snapshot(zfs_cmd_t *zc)
 {
+	nvlist_t *nvprops = NULL;
+	int error;
+	boolean_t recursive = zc->zc_cookie;
+
 	if (snapshot_namecheck(zc->zc_value, NULL, NULL) != 0)
 		return (EINVAL);
-	return (dmu_objset_snapshot(zc->zc_name,
-	    zc->zc_value, zc->zc_cookie));
+
+	if (zc->zc_nvlist_src != NULL &&
+	    (error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
+	    &nvprops)) != 0)
+		return (error);
+
+	error = dmu_objset_snapshot(zc->zc_name, zc->zc_value, recursive);
+
+	/*
+	 * It would be nice to do this atomically.
+	 */
+	if (error == 0) {
+		struct snap_prop_arg snpa;
+		snpa.nvprops = nvprops;
+		snpa.snapname = zc->zc_value;
+		if (recursive) {
+			error = dmu_objset_find(zc->zc_name,
+			    set_snap_props, &snpa, DS_FIND_CHILDREN);
+			if (error) {
+				(void) dmu_snapshots_destroy(zc->zc_name,
+				    zc->zc_value);
+			}
+		} else {
+			error = set_snap_props(zc->zc_name, &snpa);
+		}
+	}
+	nvlist_free(nvprops);
+	return (error);
 }
 
 int
--- a/usr/src/uts/common/fs/zfs/zfs_vfsops.c	Fri Aug 01 16:15:10 2008 -0700
+++ b/usr/src/uts/common/fs/zfs/zfs_vfsops.c	Fri Aug 01 16:32:18 2008 -0700
@@ -477,8 +477,9 @@
 
 		dmu_objset_name(os, osname);
 		if (error = dsl_prop_get_integer(osname, "nbmand", &nbmand,
-		    NULL))
-		return (error);
+		    NULL)) {
+			return (error);
+		}
 	}
 
 	/*
--- a/usr/src/uts/common/fs/zfs/zvol.c	Fri Aug 01 16:15:10 2008 -0700
+++ b/usr/src/uts/common/fs/zfs/zvol.c	Fri Aug 01 16:32:18 2008 -0700
@@ -843,7 +843,11 @@
 		error = dmu_free_long_range(zv->zv_objset,
 		    ZVOL_OBJ, volsize, DMU_OBJECT_END);
 
-	if (error == 0) {
+	/*
+	 * If we are using a faked-up state (zv_minor == 0) then don't
+	 * try to update the in-core zvol state.
+	 */
+	if (error == 0 && zv->zv_minor) {
 		zv->zv_volsize = volsize;
 		zvol_size_changed(zv, maj);
 	}
@@ -857,25 +861,31 @@
 	int error;
 	dmu_object_info_t doi;
 	uint64_t old_volsize = 0ULL;
+	zvol_state_t state = { 0 };
 
 	mutex_enter(&zvol_state_lock);
 
 	if ((zv = zvol_minor_lookup(name)) == NULL) {
-		mutex_exit(&zvol_state_lock);
-		return (ENXIO);
+		/*
+		 * If we are doing a "zfs clone -o volsize=", then the
+		 * minor node won't exist yet.
+		 */
+		error = dmu_objset_open(name, DMU_OST_ZVOL, DS_MODE_OWNER,
+		    &state.zv_objset);
+		if (error != 0)
+			goto out;
+		zv = &state;
 	}
 	old_volsize = zv->zv_volsize;
 
 	if ((error = dmu_object_info(zv->zv_objset, ZVOL_OBJ, &doi)) != 0 ||
 	    (error = zvol_check_volsize(volsize,
-	    doi.doi_data_block_size)) != 0) {
-		mutex_exit(&zvol_state_lock);
-		return (error);
-	}
+	    doi.doi_data_block_size)) != 0)
+		goto out;
 
 	if (zv->zv_flags & ZVOL_RDONLY || (zv->zv_mode & DS_MODE_READONLY)) {
-		mutex_exit(&zvol_state_lock);
-		return (EROFS);
+		error = EROFS;
+		goto out;
 	}
 
 	error = zvol_update_volsize(zv, maj, volsize);
@@ -893,6 +903,10 @@
 		}
 	}
 
+out:
+	if (state.zv_objset)
+		dmu_objset_close(state.zv_objset);
+
 	mutex_exit(&zvol_state_lock);
 
 	return (error);
--- a/usr/src/uts/common/sys/fs/zfs.h	Fri Aug 01 16:15:10 2008 -0700
+++ b/usr/src/uts/common/sys/fs/zfs.h	Fri Aug 01 16:32:18 2008 -0700
@@ -244,13 +244,14 @@
 #define	SPA_VERSION_9			9ULL
 #define	SPA_VERSION_10			10ULL
 #define	SPA_VERSION_11			11ULL
+#define	SPA_VERSION_12			12ULL
 /*
  * When bumping up SPA_VERSION, make sure GRUB ZFS understands the on-disk
  * format change. Go to usr/src/grub/grub-0.95/stage2/{zfs-include/, fsys_zfs*},
  * and do the appropriate changes.
  */
-#define	SPA_VERSION			SPA_VERSION_11
-#define	SPA_VERSION_STRING		"11"
+#define	SPA_VERSION			SPA_VERSION_12
+#define	SPA_VERSION_STRING		"12"
 
 /*
  * Symbolic names for the changes that caused a SPA_VERSION switch.
@@ -283,6 +284,7 @@
 #define	SPA_VERSION_NEXT_CLONES		SPA_VERSION_11
 #define	SPA_VERSION_ORIGIN		SPA_VERSION_11
 #define	SPA_VERSION_DSL_SCRUB		SPA_VERSION_11
+#define	SPA_VERSION_SNAP_PROPS		SPA_VERSION_12
 
 /*
  * ZPL version - rev'd whenever an incompatible on-disk format change