PSARC 2006/486 ZFS canmount property
authoreschrock
Tue, 05 Sep 2006 11:37:36 -0700
changeset 2676 5cee47eddab6
parent 2675 54d99ec6d12d
child 2677 212d61b14a8b
PSARC 2006/486 ZFS canmount property PSARC 2006/497 ZFS create time properties PSARC 2006/502 ZFS get all datasets PSARC 2006/504 ZFS user properties 6269805 properties should be set via an nvlist. 6281585 user defined properties 6349494 'zfs list' output annoying for even moderately long dataset names 6366244 'canmount' option for container-like functionality 6367103 create-time properties 6416639 RFE: provide zfs get -a 6437808 ZFS module version should match on-disk version 6454551 'zfs create -b blocksize filesystem' should fail. 6457478 unrecognized character in error message with 'zpool create -R' command 6457865 missing device name in the error message of 'zpool clear' command 6458571 zfs_ioc_set_prop() doesn't validate input 6458614 zfs ACL #defines should use prefix 6458638 get_configs() accesses bogus memory 6458678 zvol functions should be moved out of zfs_ioctl.h 6458683 zfs_cmd_t could use more cleanup 6458691 common routines to manage zfs_cmd_t nvlists 6460398 zpool import cores on zfs_prop_get 6461029 zpool status -x noexisting-pool has incorrect error message. 6461223 index translations should live with property definitions 6461424 zpool_unmount_datasets() has some busted logic 6461427 zfs_realloc() would be useful 6461757 'zpool status' can report the wrong number of persistent errors 6461784 recursive zfs_snapshot() leaks memory
usr/src/cmd/truss/codes.c
usr/src/cmd/zfs/Makefile
usr/src/cmd/zfs/zfs_iter.c
usr/src/cmd/zfs/zfs_iter.h
usr/src/cmd/zfs/zfs_main.c
usr/src/cmd/zoneadm/Makefile
usr/src/cmd/zoneadm/zfs.c
usr/src/cmd/zoneadmd/vplat.c
usr/src/cmd/zpool/zpool_main.c
usr/src/common/zfs/zfs_prop.c
usr/src/common/zfs/zfs_prop.h
usr/src/lib/libzfs/common/libzfs.h
usr/src/lib/libzfs/common/libzfs_changelist.c
usr/src/lib/libzfs/common/libzfs_config.c
usr/src/lib/libzfs/common/libzfs_dataset.c
usr/src/lib/libzfs/common/libzfs_graph.c
usr/src/lib/libzfs/common/libzfs_impl.h
usr/src/lib/libzfs/common/libzfs_import.c
usr/src/lib/libzfs/common/libzfs_mount.c
usr/src/lib/libzfs/common/libzfs_pool.c
usr/src/lib/libzfs/common/libzfs_util.c
usr/src/lib/libzfs/common/mapfile-vers
usr/src/uts/common/fs/zfs/dmu.c
usr/src/uts/common/fs/zfs/dsl_prop.c
usr/src/uts/common/fs/zfs/sys/spa.h
usr/src/uts/common/fs/zfs/sys/zfs_acl.h
usr/src/uts/common/fs/zfs/sys/zfs_ioctl.h
usr/src/uts/common/fs/zfs/sys/zvol.h
usr/src/uts/common/fs/zfs/zfs_acl.c
usr/src/uts/common/fs/zfs/zfs_ctldir.c
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/truss/codes.c	Mon Sep 04 19:32:13 2006 -0700
+++ b/usr/src/cmd/truss/codes.c	Tue Sep 05 11:37:36 2006 -0700
@@ -901,14 +901,6 @@
 		"zfs_cmd_t" },
 	{ (uint_t)ZFS_IOC_SET_PROP,		"ZFS_IOC_SET_PROP",
 		"zfs_cmd_t" },
-	{ (uint_t)ZFS_IOC_SET_QUOTA,		"ZFS_IOC_SET_QUOTA",
-		"zfs_cmd_t" },
-	{ (uint_t)ZFS_IOC_SET_RESERVATION,	"ZFS_IOC_SET_RESERVATION",
-		"zfs_cmd_t" },
-	{ (uint_t)ZFS_IOC_SET_VOLSIZE,		"ZFS_IOC_SET_VOLSIZE",
-		"zfs_cmd_t" },
-	{ (uint_t)ZFS_IOC_SET_VOLBLOCKSIZE,	"ZFS_IOC_SET_VOLBLOCKSIZE",
-		"zfs_cmd_t" },
 	{ (uint_t)ZFS_IOC_CREATE_MINOR,		"ZFS_IOC_CREATE_MINOR",
 		"zfs_cmd_t" },
 	{ (uint_t)ZFS_IOC_REMOVE_MINOR,		"ZFS_IOC_REMOVE_MINOR",
--- a/usr/src/cmd/zfs/Makefile	Mon Sep 04 19:32:13 2006 -0700
+++ b/usr/src/cmd/zfs/Makefile	Tue Sep 05 11:37:36 2006 -0700
@@ -2,9 +2,8 @@
 # CDDL HEADER START
 #
 # The contents of this file are subject to the terms of the
-# Common Development and Distribution License, Version 1.0 only
-# (the "License").  You may not use this file except in compliance
-# with the License.
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
 #
 # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 # or http://www.opensolaris.org/os/licensing.
@@ -20,7 +19,7 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
 # ident	"%Z%%M%	%I%	%E% SMI"
@@ -39,7 +38,7 @@
 ROOTETCFSTYPE=  $(ROOTETC)/fs/$(FSTYPE)
 USRLIBFSTYPE=	$(ROOTLIB)/fs/$(FSTYPE)
 
-LDLIBS += -lzfs -luutil -lumem
+LDLIBS += -lzfs -luutil -lumem -lnvpair
 
 C99MODE=	-xc99=%all
 C99LMODE=	-Xc99=%all
--- a/usr/src/cmd/zfs/zfs_iter.c	Mon Sep 04 19:32:13 2006 -0700
+++ b/usr/src/cmd/zfs/zfs_iter.c	Tue Sep 05 11:37:36 2006 -0700
@@ -59,6 +59,7 @@
 	int		cb_recurse;
 	zfs_type_t	cb_types;
 	zfs_sort_column_t *cb_sortcol;
+	zfs_proplist_t	**cb_proplist;
 } callback_data_t;
 
 uu_avl_pool_t *avl_pool;
@@ -84,6 +85,11 @@
 		uu_avl_node_init(node, &node->zn_avlnode, avl_pool);
 		if (uu_avl_find(cb->cb_avl, node, cb->cb_sortcol,
 		    &idx) == NULL) {
+			if (cb->cb_proplist &&
+			    zfs_expand_proplist(zhp, cb->cb_proplist) != 0) {
+				free(node);
+				return (-1);
+			}
 			uu_avl_insert(cb->cb_avl, node, idx);
 			dontclose = 1;
 		} else {
@@ -105,17 +111,25 @@
 	return (0);
 }
 
-void
-zfs_add_sort_column(zfs_sort_column_t **sc, zfs_prop_t prop,
+int
+zfs_add_sort_column(zfs_sort_column_t **sc, const char *name,
     boolean_t reverse)
 {
 	zfs_sort_column_t *col;
+	zfs_prop_t prop;
+
+	if ((prop = zfs_name_to_prop(name)) == ZFS_PROP_INVAL &&
+	    !zfs_prop_user(name))
+		return (-1);
 
 	col = safe_malloc(sizeof (zfs_sort_column_t));
 
 	col->sc_prop = prop;
 	col->sc_reverse = reverse;
-	col->sc_next = NULL;
+	if (prop == ZFS_PROP_INVAL) {
+		col->sc_user_prop = safe_malloc(strlen(name) + 1);
+		(void) strcpy(col->sc_user_prop, name);
+	}
 
 	if (*sc == NULL) {
 		col->sc_last = col;
@@ -124,6 +138,8 @@
 		(*sc)->sc_last->sc_next = col;
 		(*sc)->sc_last = col;
 	}
+
+	return (0);
 }
 
 void
@@ -133,6 +149,7 @@
 
 	while (sc != NULL) {
 		col = sc->sc_next;
+		free(sc->sc_user_prop);
 		free(sc);
 		sc = col;
 	}
@@ -214,48 +231,72 @@
 	zfs_sort_column_t *psc;
 
 	for (psc = sc; psc != NULL; psc = psc->sc_next) {
-		char lstr[ZFS_MAXPROPLEN], rstr[ZFS_MAXPROPLEN];
+		char lbuf[ZFS_MAXPROPLEN], rbuf[ZFS_MAXPROPLEN];
+		char *lstr, *rstr;
 		uint64_t lnum, rnum;
-		int lvalid, rvalid;
+		boolean_t lvalid, rvalid;
 		int ret = 0;
 
-		if (zfs_prop_is_string(psc->sc_prop)) {
-			lvalid = zfs_prop_get(l, psc->sc_prop, lstr,
-			    sizeof (lstr), NULL, NULL, 0, B_TRUE);
-			rvalid = zfs_prop_get(r, psc->sc_prop, rstr,
-			    sizeof (rstr), NULL, NULL, 0, B_TRUE);
+		/*
+		 * We group the checks below the generic code.  If 'lstr' and
+		 * 'rstr' are non-NULL, then we do a string based comparison.
+		 * Otherwise, we compare 'lnum' and 'rnum'.
+		 */
+		lstr = rstr = NULL;
+		if (psc->sc_prop == ZFS_PROP_INVAL) {
+			nvlist_t *luser, *ruser;
+			nvlist_t *lval, *rval;
+
+			luser = zfs_get_user_props(l);
+			ruser = zfs_get_user_props(r);
 
-			if ((lvalid == -1) && (rvalid == -1))
-				continue;
-			if (lvalid == -1)
-				return (1);
-			else if (rvalid == -1)
-				return (-1);
+			lvalid = (nvlist_lookup_nvlist(luser,
+			    psc->sc_user_prop, &lval) == 0);
+			rvalid = (nvlist_lookup_nvlist(ruser,
+			    psc->sc_user_prop, &rval) == 0);
 
-			ret = strcmp(lstr, rstr);
+			if (lvalid)
+				verify(nvlist_lookup_string(lval,
+				    ZFS_PROP_VALUE, &lstr) == 0);
+			if (rvalid)
+				verify(nvlist_lookup_string(rval,
+				    ZFS_PROP_VALUE, &rstr) == 0);
+
+		} else if (zfs_prop_is_string(psc->sc_prop)) {
+			lvalid = (zfs_prop_get(l, psc->sc_prop, lbuf,
+			    sizeof (lbuf), NULL, NULL, 0, B_TRUE) == 0);
+			rvalid = (zfs_prop_get(r, psc->sc_prop, rbuf,
+			    sizeof (rbuf), NULL, NULL, 0, B_TRUE) == 0);
+
+			lstr = lbuf;
+			rstr = rbuf;
 		} else {
 			lvalid = zfs_prop_valid_for_type(psc->sc_prop,
 			    zfs_get_type(l));
 			rvalid = zfs_prop_valid_for_type(psc->sc_prop,
 			    zfs_get_type(r));
 
-			if (!lvalid && !rvalid)
-				continue;
-			else if (!lvalid)
-				return (1);
-			else if (!rvalid)
-				return (-1);
+			if (lvalid)
+				(void) zfs_prop_get_numeric(l, psc->sc_prop,
+				    &lnum, NULL, NULL, 0);
+			if (rvalid)
+				(void) zfs_prop_get_numeric(r, psc->sc_prop,
+				    &rnum, NULL, NULL, 0);
+		}
 
-			(void) zfs_prop_get_numeric(l, psc->sc_prop, &lnum,
-			    NULL, NULL, 0);
-			(void) zfs_prop_get_numeric(r, psc->sc_prop, &rnum,
-			    NULL, NULL, 0);
+		if (!lvalid && !rvalid)
+			continue;
+		else if (!lvalid)
+			return (1);
+		else if (!rvalid)
+			return (-1);
 
-			if (lnum < rnum)
-				ret = -1;
-			else if (lnum > rnum)
-				ret = 1;
-		}
+		if (lstr)
+			ret = strcmp(lstr, rstr);
+		if (lnum < rnum)
+			ret = -1;
+		else if (lnum > rnum)
+			ret = 1;
 
 		if (ret != 0) {
 			if (psc->sc_reverse == B_TRUE)
@@ -269,7 +310,8 @@
 
 int
 zfs_for_each(int argc, char **argv, boolean_t recurse, zfs_type_t types,
-    zfs_sort_column_t *sortcol, zfs_iter_f callback, void *data)
+    zfs_sort_column_t *sortcol, zfs_proplist_t **proplist, zfs_iter_f callback,
+    void *data)
 {
 	callback_data_t cb;
 	int ret = 0;
@@ -287,6 +329,7 @@
 
 	cb.cb_sortcol = sortcol;
 	cb.cb_recurse = recurse;
+	cb.cb_proplist = proplist;
 	cb.cb_types = types;
 	if ((cb.cb_avl = uu_avl_create(avl_pool, NULL, UU_DEFAULT)) == NULL) {
 		(void) fprintf(stderr,
--- a/usr/src/cmd/zfs/zfs_iter.h	Mon Sep 04 19:32:13 2006 -0700
+++ b/usr/src/cmd/zfs/zfs_iter.h	Tue Sep 05 11:37:36 2006 -0700
@@ -36,12 +36,13 @@
 	struct zfs_sort_column	*sc_next;
 	struct zfs_sort_column	*sc_last;
 	zfs_prop_t		sc_prop;
+	char			*sc_user_prop;
 	boolean_t		sc_reverse;
 } zfs_sort_column_t;
 
 int zfs_for_each(int, char **, boolean_t, zfs_type_t, zfs_sort_column_t *,
-    zfs_iter_f, void *);
-void zfs_add_sort_column(zfs_sort_column_t **, zfs_prop_t, boolean_t);
+    zfs_proplist_t **, zfs_iter_f, void *);
+int zfs_add_sort_column(zfs_sort_column_t **, const char *, boolean_t);
 void zfs_free_sort_columns(zfs_sort_column_t *);
 
 #ifdef	__cplusplus
--- a/usr/src/cmd/zfs/zfs_main.c	Mon Sep 04 19:32:13 2006 -0700
+++ b/usr/src/cmd/zfs/zfs_main.c	Tue Sep 05 11:37:36 2006 -0700
@@ -26,6 +26,7 @@
 #pragma ident	"%Z%%M%	%I%	%E% SMI"
 
 #include <assert.h>
+#include <ctype.h>
 #include <errno.h>
 #include <libgen.h>
 #include <libintl.h>
@@ -161,8 +162,10 @@
 	case HELP_CLONE:
 		return (gettext("\tclone <snapshot> <filesystem|volume>\n"));
 	case HELP_CREATE:
-		return (gettext("\tcreate <filesystem>\n"
-		    "\tcreate [-s] [-b blocksize] -V <size> <volume>\n"));
+		return (gettext("\tcreate [[-o property=value] ... ] "
+		    "<filesystem>\n"
+		    "\tcreate [-s] [-b blocksize] [[-o property=value] ...]\n"
+		    "\t    -V <size> <volume>\n"));
 	case HELP_DESTROY:
 		return (gettext("\tdestroy [-rRf] "
 		    "<filesystem|volume|snapshot>\n"));
@@ -170,7 +173,7 @@
 		return (gettext("\tget [-rHp] [-o field[,field]...] "
 		    "[-s source[,source]...]\n"
 		    "\t    <all | property[,property]...> "
-		    "<filesystem|volume|snapshot> ...\n"));
+		    "[filesystem|volume|snapshot] ...\n"));
 	case HELP_INHERIT:
 		return (gettext("\tinherit [-r] <property> "
 		    "<filesystem|volume> ...\n"));
@@ -302,6 +305,8 @@
 		}
 		(void) fprintf(fp, gettext("\nSizes are specified in bytes "
 		    "with standard units such as K, M, G, etc.\n"));
+		(void) fprintf(fp, gettext("\n\nUser-defined properties can "
+		    "be specified by using a name containing a colon (:).\n"));
 	} else {
 		/*
 		 * TRANSLATION NOTE:
@@ -312,6 +317,14 @@
 		    gettext("\nFor the property list, run: zfs set|get\n"));
 	}
 
+	/*
+	 * See comments at end of main().
+	 */
+	if (getenv("ZFS_ABORT") != NULL) {
+		(void) printf("dumping core by request\n");
+		abort();
+	}
+
 	exit(requested ? 0 : 2);
 }
 
@@ -357,7 +370,7 @@
 		return (1);
 
 	/* pass to libzfs */
-	ret = zfs_clone(zhp, argv[2]);
+	ret = zfs_clone(zhp, argv[2], NULL);
 
 	/* create the mountpoint if necessary */
 	if (ret == 0) {
@@ -375,8 +388,8 @@
 }
 
 /*
- * zfs create fs
- * zfs create [-s] [-b blocksize] -V vol size
+ * zfs create [-o prop=value] ... fs
+ * zfs create [-s] [-b blocksize] [-o prop=value] ... -V vol size
  *
  * Create a new dataset.  This command can be used to create filesystems
  * and volumes.  Snapshot creation is handled by 'zfs snapshot'.
@@ -390,22 +403,79 @@
 zfs_do_create(int argc, char **argv)
 {
 	zfs_type_t type = ZFS_TYPE_FILESYSTEM;
-	zfs_handle_t *zhp;
-	char *size = NULL;
-	char *blocksize = NULL;
+	zfs_handle_t *zhp = NULL;
+	uint64_t volsize;
 	int c;
 	boolean_t noreserve = B_FALSE;
-	int ret;
+	int ret = 1;
+	nvlist_t *props = NULL;
+	uint64_t intval;
+	char *propname;
+	char *propval, *strval;
+
+	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, ":V:b:s")) != -1) {
+	while ((c = getopt(argc, argv, ":V:b:so:")) != -1) {
 		switch (c) {
 		case 'V':
 			type = ZFS_TYPE_VOLUME;
-			size = optarg;
+			if (zfs_nicestrtonum(g_zfs, optarg, &intval) != 0) {
+				(void) fprintf(stderr, gettext("bad volume "
+				    "size '%s': %s\n"), optarg,
+				    libzfs_error_description(g_zfs));
+				goto error;
+			}
+
+			if (nvlist_add_uint64(props,
+			    zfs_prop_to_name(ZFS_PROP_VOLSIZE),
+			    intval) != 0) {
+				(void) fprintf(stderr, gettext("internal "
+				    "error: out of memory\n"));
+				goto error;
+			}
+			volsize = intval;
 			break;
 		case 'b':
-			blocksize = optarg;
+			if (zfs_nicestrtonum(g_zfs, optarg, &intval) != 0) {
+				(void) fprintf(stderr, gettext("bad volume "
+				    "block size '%s': %s\n"), optarg,
+				    libzfs_error_description(g_zfs));
+				goto error;
+			}
+
+			if (nvlist_add_uint64(props,
+			    zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE),
+			    intval) != 0) {
+				(void) fprintf(stderr, gettext("internal "
+				    "error: out of memory\n"));
+				goto error;
+			}
+			break;
+		case 'o':
+			propname = optarg;
+			if ((propval = strchr(propname, '=')) == NULL) {
+				(void) fprintf(stderr, gettext("missing "
+				    "'=' for -o option\n"));
+				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;
@@ -413,19 +483,19 @@
 		case ':':
 			(void) fprintf(stderr, gettext("missing size "
 			    "argument\n"));
-			usage(B_FALSE);
+			goto badusage;
 			break;
 		case '?':
 			(void) fprintf(stderr, gettext("invalid option '%c'\n"),
 			    optopt);
-			usage(B_FALSE);
+			goto badusage;
 		}
 	}
 
 	if (noreserve && type != ZFS_TYPE_VOLUME) {
 		(void) fprintf(stderr, gettext("'-s' can only be used when "
 		    "creating a volume\n"));
-		usage(B_FALSE);
+		goto badusage;
 	}
 
 	argc -= optind;
@@ -435,33 +505,32 @@
 	if (argc == 0) {
 		(void) fprintf(stderr, gettext("missing %s argument\n"),
 		    zfs_type_to_name(type));
-		usage(B_FALSE);
+		goto badusage;
 	}
 	if (argc > 1) {
 		(void) fprintf(stderr, gettext("too many arguments\n"));
-		usage(B_FALSE);
+		goto badusage;
+	}
+
+	if (type == ZFS_TYPE_VOLUME && !noreserve &&
+	    nvlist_lookup_string(props, zfs_prop_to_name(ZFS_PROP_RESERVATION),
+	    &strval) != 0) {
+		if (nvlist_add_uint64(props,
+		    zfs_prop_to_name(ZFS_PROP_RESERVATION),
+		    volsize) != 0) {
+			(void) fprintf(stderr, gettext("internal "
+			    "error: out of memory\n"));
+			nvlist_free(props);
+			return (1);
+		}
 	}
 
 	/* pass to libzfs */
-	if (zfs_create(g_zfs, argv[0], type, size, blocksize) != 0)
-		return (1);
+	if (zfs_create(g_zfs, argv[0], type, props) != 0)
+		goto error;
 
 	if ((zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_ANY)) == NULL)
-		return (1);
-
-	/*
-	 * Volume handling.  By default, we try to create a reservation of equal
-	 * size for the volume.  If we can't do this, then destroy the dataset
-	 * and report an error.
-	 */
-	if (type == ZFS_TYPE_VOLUME && !noreserve) {
-		if (zfs_prop_set(zhp, ZFS_PROP_RESERVATION, size) != 0) {
-			(void) fprintf(stderr, gettext("use '-s' to create a "
-			    "volume without a matching reservation\n"));
-			(void) zfs_destroy(zhp);
-			return (1);
-		}
-	}
+		goto error;
 
 	/*
 	 * Mount and/or share the new filesystem as appropriate.  We provide a
@@ -480,8 +549,15 @@
 		ret = 0;
 	}
 
-	zfs_close(zhp);
+error:
+	if (zhp)
+		zfs_close(zhp);
+	nvlist_free(props);
 	return (ret);
+badusage:
+	nvlist_free(props);
+	usage(B_FALSE);
+	return (2);
 }
 
 /*
@@ -765,11 +841,11 @@
 typedef struct get_cbdata {
 	int cb_sources;
 	int cb_columns[4];
-	int cb_nprop;
+	int cb_colwidths[5];
 	boolean_t cb_scripted;
 	boolean_t cb_literal;
-	boolean_t cb_isall;
-	zfs_prop_t cb_prop[ZFS_NPROP_ALL];
+	boolean_t cb_first;
+	zfs_proplist_t *cb_proplist;
 } get_cbdata_t;
 
 #define	GET_COL_NAME		1
@@ -778,15 +854,110 @@
 #define	GET_COL_SOURCE		4
 
 /*
+ * Print the column headers for 'zfs get'.
+ */
+static void
+print_get_headers(get_cbdata_t *cbp)
+{
+	zfs_proplist_t *pl = cbp->cb_proplist;
+	int i;
+	char *title;
+	size_t len;
+
+	cbp->cb_first = B_FALSE;
+	if (cbp->cb_scripted)
+		return;
+
+	/*
+	 * Start with the length of the column headers.
+	 */
+	cbp->cb_colwidths[GET_COL_NAME] = strlen(gettext("NAME"));
+	cbp->cb_colwidths[GET_COL_PROPERTY] = strlen(gettext("PROPERTY"));
+	cbp->cb_colwidths[GET_COL_VALUE] = strlen(gettext("VALUE"));
+	cbp->cb_colwidths[GET_COL_SOURCE] = strlen(gettext("SOURCE"));
+
+	/*
+	 * Go through and calculate the widths for each column.  For the
+	 * 'source' column, we kludge it up by taking the worst-case scenario of
+	 * inheriting from the longest name.  This is acceptable because in the
+	 * majority of cases 'SOURCE' is the last column displayed, and we don't
+	 * use the width anyway.  Note that the 'VALUE' column can be oversized,
+	 * if the name of the property is much longer the any values we find.
+	 */
+	for (pl = cbp->cb_proplist; pl != NULL; pl = pl->pl_next) {
+		/*
+		 * 'PROPERTY' column
+		 */
+		if (pl->pl_prop != ZFS_PROP_INVAL) {
+			len = strlen(zfs_prop_to_name(pl->pl_prop));
+			if (len > cbp->cb_colwidths[GET_COL_PROPERTY])
+				cbp->cb_colwidths[GET_COL_PROPERTY] = len;
+		} else {
+			len = strlen(pl->pl_user_prop);
+			if (len > cbp->cb_colwidths[GET_COL_PROPERTY])
+				cbp->cb_colwidths[GET_COL_PROPERTY] = len;
+		}
+
+		/*
+		 * 'VALUE' column
+		 */
+		if ((pl->pl_prop != ZFS_PROP_NAME || !pl->pl_all) &&
+		    pl->pl_width > cbp->cb_colwidths[GET_COL_VALUE])
+			cbp->cb_colwidths[GET_COL_VALUE] = pl->pl_width;
+
+		/*
+		 * 'NAME' and 'SOURCE' columns
+		 */
+		if (pl->pl_prop == ZFS_PROP_NAME &&
+		    pl->pl_width > cbp->cb_colwidths[GET_COL_NAME]) {
+			cbp->cb_colwidths[GET_COL_NAME] = pl->pl_width;
+			cbp->cb_colwidths[GET_COL_SOURCE] = pl->pl_width +
+			    strlen(gettext("inherited from"));
+		}
+	}
+
+	/*
+	 * Now go through and print the headers.
+	 */
+	for (i = 0; i < 4; i++) {
+		switch (cbp->cb_columns[i]) {
+		case GET_COL_NAME:
+			title = gettext("NAME");
+			break;
+		case GET_COL_PROPERTY:
+			title = gettext("PROPERTY");
+			break;
+		case GET_COL_VALUE:
+			title = gettext("VALUE");
+			break;
+		case GET_COL_SOURCE:
+			title = gettext("SOURCE");
+			break;
+		default:
+			title = NULL;
+		}
+
+		if (title != NULL) {
+			if (i == 3 || cbp->cb_columns[i + 1] == 0)
+				(void) printf("%s", title);
+			else
+				(void) printf("%-*s  ",
+				    cbp->cb_colwidths[cbp->cb_columns[i]],
+				    title);
+		}
+	}
+	(void) printf("\n");
+}
+
+/*
  * Display a single line of output, according to the settings in the callback
  * structure.
  */
 static void
-print_one_property(zfs_handle_t *zhp, get_cbdata_t *cbp, zfs_prop_t prop,
+print_one_property(zfs_handle_t *zhp, get_cbdata_t *cbp, const char *propname,
     const char *value, zfs_source_t sourcetype, const char *source)
 {
 	int i;
-	int width;
 	const char *str;
 	char buf[128];
 
@@ -796,25 +967,24 @@
 	if ((sourcetype & cbp->cb_sources) == 0)
 		return;
 
+	if (cbp->cb_first)
+		print_get_headers(cbp);
+
 	for (i = 0; i < 4; i++) {
 		switch (cbp->cb_columns[i]) {
 		case GET_COL_NAME:
-			width = 15;
 			str = zfs_get_name(zhp);
 			break;
 
 		case GET_COL_PROPERTY:
-			width = 13;
-			str = zfs_prop_to_name(prop);
+			str = propname;
 			break;
 
 		case GET_COL_VALUE:
-			width = 25;
 			str = value;
 			break;
 
 		case GET_COL_SOURCE:
-			width = 15;
 			switch (sourcetype) {
 			case ZFS_SRC_NONE:
 				str = "-";
@@ -849,7 +1019,9 @@
 		else if (cbp->cb_scripted)
 			(void) printf("%s\t", str);
 		else
-			(void) printf("%-*s  ", width, str);
+			(void) printf("%-*s  ",
+			    cbp->cb_colwidths[cbp->cb_columns[i]],
+			    str);
 
 	}
 
@@ -866,20 +1038,62 @@
 	zfs_source_t sourcetype;
 	char source[ZFS_MAXNAMELEN];
 	get_cbdata_t *cbp = data;
-	int i;
-
-	for (i = 0; i < cbp->cb_nprop; i++) {
-		if (zfs_prop_get(zhp, cbp->cb_prop[i], buf,
-		    sizeof (buf), &sourcetype, source, sizeof (source),
-		    cbp->cb_literal) != 0) {
-			if (cbp->cb_isall)
-				continue;
-			(void) strlcpy(buf, "-", sizeof (buf));
-			sourcetype = ZFS_SRC_NONE;
+	nvlist_t *userprop = zfs_get_user_props(zhp);
+	zfs_proplist_t *pl = cbp->cb_proplist;
+	nvlist_t *propval;
+	char *strval;
+	char *sourceval;
+
+	for (; pl != NULL; pl = pl->pl_next) {
+		/*
+		 * Skip the special fake placeholder.  This will also skip over
+		 * the name property when 'all' is specified.
+		 */
+		if (pl->pl_prop == ZFS_PROP_NAME &&
+		    pl == cbp->cb_proplist)
+			continue;
+
+		if (pl->pl_prop != ZFS_PROP_INVAL) {
+			if (zfs_prop_get(zhp, pl->pl_prop, buf,
+			    sizeof (buf), &sourcetype, source,
+			    sizeof (source),
+			    cbp->cb_literal) != 0) {
+				if (pl->pl_all)
+					continue;
+				sourcetype = ZFS_SRC_NONE;
+				(void) strlcpy(buf, "-", sizeof (buf));
+			}
+
+			print_one_property(zhp, cbp,
+			    zfs_prop_to_name(pl->pl_prop),
+			    buf, sourcetype, source);
+		} else {
+			if (nvlist_lookup_nvlist(userprop,
+			    pl->pl_user_prop, &propval) != 0) {
+				if (pl->pl_all)
+					continue;
+				sourcetype = ZFS_SRC_NONE;
+				strval = "-";
+			} else {
+				verify(nvlist_lookup_string(propval,
+				    ZFS_PROP_VALUE, &strval) == 0);
+				verify(nvlist_lookup_string(propval,
+				    ZFS_PROP_SOURCE, &sourceval) == 0);
+
+				if (strcmp(sourceval,
+				    zfs_get_name(zhp)) == 0) {
+					sourcetype = ZFS_SRC_LOCAL;
+				} else {
+					sourcetype = ZFS_SRC_INHERITED;
+					(void) strlcpy(source,
+					    sourceval, sizeof (source));
+				}
+			}
+
+			print_one_property(zhp, cbp,
+			    pl->pl_user_prop, strval, sourcetype,
+			    source);
 		}
-
-		print_one_property(zhp, cbp, cbp->cb_prop[i],
-		    buf, sourcetype, source);
 	}
 
 	return (0);
@@ -890,10 +1104,10 @@
 {
 	get_cbdata_t cb = { 0 };
 	boolean_t recurse = B_FALSE;
-	int c;
-	char *value, *fields, *badopt;
-	int i;
+	int i, c;
+	char *value, *fields;
 	int ret;
+	zfs_proplist_t fake_name = { 0 };
 
 	/*
 	 * Set up default columns and sources.
@@ -1014,62 +1228,39 @@
 
 	fields = argv[0];
 
-	/*
-	 * If the user specifies 'all', the behavior of 'zfs get' is slightly
-	 * different, because we don't show properties which don't apply to the
-	 * given dataset.
-	 */
-	if (strcmp(fields, "all") == 0)
-		cb.cb_isall = B_TRUE;
-
-	if ((ret = zfs_get_proplist(fields, cb.cb_prop, ZFS_NPROP_ALL,
-	    &cb.cb_nprop, &badopt)) != 0) {
-		if (ret == EINVAL)
-			(void) fprintf(stderr, gettext("invalid property "
-			    "'%s'\n"), badopt);
-		else
-			(void) fprintf(stderr, gettext("too many properties "
-			    "specified\n"));
+	if (zfs_get_proplist(g_zfs, fields, &cb.cb_proplist) != 0)
 		usage(B_FALSE);
-	}
 
 	argc--;
 	argv++;
 
-	/* check for at least one dataset name */
-	if (argc < 1) {
-		(void) fprintf(stderr, gettext("missing dataset argument\n"));
-		usage(B_FALSE);
-	}
-
 	/*
-	 * Print out any headers
+	 * As part of zfs_expand_proplist(), we keep track of the maximum column
+	 * width for each property.  For the 'NAME' (and 'SOURCE') columns, we
+	 * need to know the maximum name length.  However, the user likely did
+	 * not specify 'name' as one of the properties to fetch, so we need to
+	 * make sure we always include at least this property for
+	 * print_get_headers() to work properly.
 	 */
-	if (!cb.cb_scripted) {
-		int i;
-		for (i = 0; i < 4; i++) {
-			switch (cb.cb_columns[i]) {
-			case GET_COL_NAME:
-				(void) printf("%-15s  ", "NAME");
-				break;
-			case GET_COL_PROPERTY:
-				(void) printf("%-13s  ", "PROPERTY");
-				break;
-			case GET_COL_VALUE:
-				(void) printf("%-25s  ", "VALUE");
-				break;
-			case GET_COL_SOURCE:
-				(void) printf("%s", "SOURCE");
-				break;
-			}
-		}
-		(void) printf("\n");
+	if (cb.cb_proplist != NULL) {
+		fake_name.pl_prop = ZFS_PROP_NAME;
+		fake_name.pl_width = strlen(gettext("NAME"));
+		fake_name.pl_next = cb.cb_proplist;
+		cb.cb_proplist = &fake_name;
 	}
 
+	cb.cb_first = B_TRUE;
+
 	/* run for each object */
-	return (zfs_for_each(argc, argv, recurse, ZFS_TYPE_ANY, NULL,
-	    get_callback, &cb));
-
+	ret = zfs_for_each(argc, argv, recurse, ZFS_TYPE_ANY, NULL,
+	    &cb.cb_proplist, get_callback, &cb);
+
+	if (cb.cb_proplist == &fake_name)
+		zfs_free_proplist(fake_name.pl_next);
+	else
+		zfs_free_proplist(cb.cb_proplist);
+
+	return (ret);
 }
 
 /*
@@ -1086,9 +1277,7 @@
 static int
 inherit_callback(zfs_handle_t *zhp, void *data)
 {
-	zfs_prop_t prop = (zfs_prop_t)data;
-
-	return (zfs_prop_inherit(zhp, prop) != 0);
+	return (zfs_prop_inherit(zhp, data) != 0);
 }
 
 static int
@@ -1127,33 +1316,35 @@
 	}
 
 	propname = argv[0];
-
-	/*
-	 * Get and validate the property before iterating over the datasets.  We
-	 * do this now so as to avoid printing out an error message for each and
-	 * every dataset.
-	 */
-	if ((prop = zfs_name_to_prop(propname)) == ZFS_PROP_INVAL) {
-		(void) fprintf(stderr, gettext("invalid property '%s'\n"),
+	argc--;
+	argv++;
+
+	if ((prop = zfs_name_to_prop(propname)) != ZFS_PROP_INVAL) {
+		if (zfs_prop_readonly(prop)) {
+			(void) fprintf(stderr, gettext(
+			    "%s property is read-only\n"),
+			    propname);
+			return (1);
+		}
+		if (!zfs_prop_inheritable(prop)) {
+			(void) fprintf(stderr, gettext("'%s' property cannot "
+			    "be inherited\n"), propname);
+			if (prop == ZFS_PROP_QUOTA ||
+			    prop == ZFS_PROP_RESERVATION)
+				(void) fprintf(stderr, gettext("use 'zfs set "
+				    "%s=none' to clear\n"), propname);
+			return (1);
+		}
+	} else if (!zfs_prop_user(propname)) {
+		(void) fprintf(stderr, gettext(
+		    "invalid property '%s'\n"),
 		    propname);
 		usage(B_FALSE);
 	}
-	if (zfs_prop_readonly(prop)) {
-		(void) fprintf(stderr, gettext("%s property is read-only\n"),
-		    propname);
-		return (1);
-	}
-	if (!zfs_prop_inheritable(prop)) {
-		(void) fprintf(stderr, gettext("%s property cannot be "
-		    "inherited\n"), propname);
-		(void) fprintf(stderr, gettext("use 'zfs set %s=none' to "
-		    "clear\n"), propname);
-		return (1);
-	}
-
-	return (zfs_for_each(argc - 1, argv + 1, recurse,
-	    ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME, NULL,
-	    inherit_callback, (void *)prop));
+
+	return (zfs_for_each(argc, argv, recurse,
+	    ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME, NULL, NULL,
+	    inherit_callback, propname));
 }
 
 /*
@@ -1175,26 +1366,45 @@
 typedef struct list_cbdata {
 	boolean_t	cb_first;
 	boolean_t	cb_scripted;
-	zfs_prop_t	cb_fields[ZFS_NPROP_ALL];
-	int		cb_fieldcount;
+	zfs_proplist_t	*cb_proplist;
 } list_cbdata_t;
 
 /*
  * Given a list of columns to display, output appropriate headers for each one.
  */
 static void
-print_header(zfs_prop_t *fields, size_t count)
+print_header(zfs_proplist_t *pl)
 {
+	char headerbuf[ZFS_MAXPROPLEN];
+	const char *header;
 	int i;
-
-	for (i = 0; i < count; i++) {
-		if (i != 0)
+	boolean_t first = B_TRUE;
+	boolean_t right_justify;
+
+	for (; pl != NULL; pl = pl->pl_next) {
+		if (!first) {
 			(void) printf("  ");
-		if (i == count - 1)
-			(void) printf("%s", zfs_prop_column_name(fields[i]));
-		else	/* LINTED - format specifier */
-			(void) printf(zfs_prop_column_format(fields[i]),
-			    zfs_prop_column_name(fields[i]));
+		} else {
+			first = B_FALSE;
+		}
+
+		right_justify = B_FALSE;
+		if (pl->pl_prop != ZFS_PROP_INVAL) {
+			header = zfs_prop_column_name(pl->pl_prop);
+			right_justify = zfs_prop_align_right(pl->pl_prop);
+		} else {
+			for (i = 0; pl->pl_user_prop[i] != '\0'; i++)
+				headerbuf[i] = toupper(pl->pl_user_prop[i]);
+			headerbuf[i] = '\0';
+			header = headerbuf;
+		}
+
+		if (pl->pl_next == NULL && !right_justify)
+			(void) printf("%s", header);
+		else if (right_justify)
+			(void) printf("%*s", pl->pl_width, header);
+		else
+			(void) printf("%-*s", pl->pl_width, header);
 	}
 
 	(void) printf("\n");
@@ -1205,34 +1415,57 @@
  * to the described layout.
  */
 static void
-print_dataset(zfs_handle_t *zhp, zfs_prop_t *fields, size_t count, int scripted)
+print_dataset(zfs_handle_t *zhp, zfs_proplist_t *pl, int scripted)
 {
-	int i;
+	boolean_t first = B_TRUE;
 	char property[ZFS_MAXPROPLEN];
-
-	for (i = 0; i < count; i++) {
-		if (i != 0) {
+	nvlist_t *userprops = zfs_get_user_props(zhp);
+	nvlist_t *propval;
+	char *propstr;
+	boolean_t right_justify;
+	int width;
+
+	for (; pl != NULL; pl = pl->pl_next) {
+		if (!first) {
 			if (scripted)
 				(void) printf("\t");
 			else
 				(void) printf("  ");
+		} else {
+			first = B_FALSE;
 		}
 
-		if (zfs_prop_get(zhp, fields[i], property,
-		    sizeof (property), NULL, NULL, 0, B_FALSE) != 0)
-			(void) strlcpy(property, "-", sizeof (property));
+		right_justify = B_FALSE;
+		if (pl->pl_prop != ZFS_PROP_INVAL) {
+			if (zfs_prop_get(zhp, pl->pl_prop, property,
+			    sizeof (property), NULL, NULL, 0, B_FALSE) != 0)
+				propstr = "-";
+			else
+				propstr = property;
+
+			right_justify = zfs_prop_align_right(pl->pl_prop);
+		} else {
+			if (nvlist_lookup_nvlist(userprops,
+			    pl->pl_user_prop, &propval) != 0)
+				propstr = "-";
+			else
+				verify(nvlist_lookup_string(propval,
+				    ZFS_PROP_VALUE, &propstr) == 0);
+		}
+
+		width = pl->pl_width;
 
 		/*
 		 * If this is being called in scripted mode, or if this is the
 		 * last column and it is left-justified, don't include a width
 		 * format specifier.
 		 */
-		if (scripted || (i == count - 1 &&
-		    strchr(zfs_prop_column_format(fields[i]), '-') != NULL))
-			(void) printf("%s", property);
-		else	/* LINTED - format specifier */
-			(void) printf(zfs_prop_column_format(fields[i]),
-			    property);
+		if (scripted || (pl->pl_next == NULL && !right_justify))
+			(void) printf("%s", propstr);
+		else if (right_justify)
+			(void) printf("%*s", width, propstr);
+		else
+			(void) printf("%-*s", width, propstr);
 	}
 
 	(void) printf("\n");
@@ -1248,12 +1481,11 @@
 
 	if (cbp->cb_first) {
 		if (!cbp->cb_scripted)
-			print_header(cbp->cb_fields, cbp->cb_fieldcount);
+			print_header(cbp->cb_proplist);
 		cbp->cb_first = B_FALSE;
 	}
 
-	print_dataset(zhp, cbp->cb_fields, cbp->cb_fieldcount,
-	    cbp->cb_scripted);
+	print_dataset(zhp, cbp->cb_proplist, cbp->cb_scripted);
 
 	return (0);
 }
@@ -1273,14 +1505,10 @@
 	char *value;
 	int ret;
 	char *type_subopts[] = { "filesystem", "volume", "snapshot", NULL };
-	char *badopt;
-	int alloffset;
 	zfs_sort_column_t *sortcol = NULL;
 
 	/* check options */
 	while ((c = getopt(argc, argv, ":o:rt:Hs:S:")) != -1) {
-		zfs_prop_t	prop;
-
 		switch (c) {
 		case 'o':
 			fields = optarg;
@@ -1292,22 +1520,20 @@
 			scripted = B_TRUE;
 			break;
 		case 's':
-			if ((prop = zfs_name_to_prop(optarg)) ==
-			    ZFS_PROP_INVAL) {
+			if (zfs_add_sort_column(&sortcol, optarg,
+			    B_FALSE) != 0) {
 				(void) fprintf(stderr,
 				    gettext("invalid property '%s'\n"), optarg);
 				usage(B_FALSE);
 			}
-			zfs_add_sort_column(&sortcol, prop, B_FALSE);
 			break;
 		case 'S':
-			if ((prop = zfs_name_to_prop(optarg)) ==
-			    ZFS_PROP_INVAL) {
+			if (zfs_add_sort_column(&sortcol, optarg,
+			    B_TRUE) != 0) {
 				(void) fprintf(stderr,
 				    gettext("invalid property '%s'\n"), optarg);
 				usage(B_FALSE);
 			}
-			zfs_add_sort_column(&sortcol, prop, B_TRUE);
 			break;
 		case 't':
 			types = 0;
@@ -1354,31 +1580,16 @@
 	 * normally include the name of the dataset.  For 'zfs list', we always
 	 * want this property to be first.
 	 */
-	if (strcmp(fields, "all") == 0) {
-		cb.cb_fields[0] = ZFS_PROP_NAME;
-		alloffset = 1;
-	} else {
-		alloffset = 0;
-	}
-
-	if ((ret = zfs_get_proplist(fields, cb.cb_fields + alloffset,
-	    ZFS_NPROP_ALL - alloffset, &cb.cb_fieldcount, &badopt)) != 0) {
-		if (ret == EINVAL)
-			(void) fprintf(stderr, gettext("invalid property "
-			    "'%s'\n"), badopt);
-		else
-			(void) fprintf(stderr, gettext("too many properties "
-			    "specified\n"));
+	if (zfs_get_proplist(g_zfs, fields, &cb.cb_proplist) != 0)
 		usage(B_FALSE);
-	}
-
-	cb.cb_fieldcount += alloffset;
+
 	cb.cb_scripted = scripted;
 	cb.cb_first = B_TRUE;
 
-	ret = zfs_for_each(argc, argv, recurse, types, sortcol,
+	ret = zfs_for_each(argc, argv, recurse, types, sortcol, &cb.cb_proplist,
 	    list_callback, &cb);
 
+	zfs_free_proplist(cb.cb_proplist);
 	zfs_free_sort_columns(sortcol);
 
 	if (ret == 0 && cb.cb_first)
@@ -1652,7 +1863,6 @@
 typedef struct set_cbdata {
 	char		*cb_propname;
 	char		*cb_value;
-	zfs_prop_t	cb_prop;
 } set_cbdata_t;
 
 static int
@@ -1661,92 +1871,7 @@
 	set_cbdata_t *cbp = data;
 	int ret = 1;
 
-	/* don't allow setting of properties for snapshots */
-	if (zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT) {
-		(void) fprintf(stderr, gettext("cannot set %s property for "
-		    "'%s': snapshot properties cannot be modified\n"),
-		    cbp->cb_propname, zfs_get_name(zhp));
-		return (1);
-	}
-
-	/*
-	 * If we're changing the volsize, make sure the value is appropriate,
-	 * and set the reservation if this is a non-sparse volume.
-	 */
-	if (cbp->cb_prop == ZFS_PROP_VOLSIZE &&
-	    zfs_get_type(zhp) == ZFS_TYPE_VOLUME) {
-		uint64_t volsize = zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE);
-		uint64_t avail = zfs_prop_get_int(zhp, ZFS_PROP_AVAILABLE);
-		uint64_t reservation = zfs_prop_get_int(zhp,
-		    ZFS_PROP_RESERVATION);
-		uint64_t blocksize = zfs_prop_get_int(zhp,
-		    ZFS_PROP_VOLBLOCKSIZE);
-		uint64_t value;
-
-		verify(zfs_nicestrtonum(cbp->cb_value, &value) == 0);
-
-		if (value % blocksize != 0) {
-			char buf[64];
-
-			zfs_nicenum(blocksize, buf, sizeof (buf));
-			(void) fprintf(stderr, gettext("cannot set %s for "
-			    "'%s': must be a multiple of volume block size "
-			    "(%s)\n"), cbp->cb_propname, zfs_get_name(zhp),
-			    buf);
-			return (1);
-		}
-
-		if (value == 0) {
-			(void) fprintf(stderr, gettext("cannot set %s for "
-			    "'%s': cannot be zero\n"), cbp->cb_propname,
-			    zfs_get_name(zhp));
-			return (1);
-		}
-
-		if (volsize == reservation) {
-			if (value > volsize && (value - volsize) > avail) {
-				(void) fprintf(stderr, gettext("cannot set "
-				    "%s property for '%s': volume size exceeds "
-				    "amount of available space\n"),
-				    cbp->cb_propname, zfs_get_name(zhp));
-				return (1);
-			}
-
-			if (zfs_prop_set(zhp, ZFS_PROP_RESERVATION,
-			    cbp->cb_value) != 0) {
-				(void) fprintf(stderr, gettext("volsize and "
-				    "reservation must remain equal\n"));
-				return (1);
-			}
-		}
-	}
-
-	/*
-	 * Do not allow the reservation to be set above the volume size. We do
-	 * this here instead of inside libzfs because libzfs violates this rule
-	 * internally.
-	 */
-	if (cbp->cb_prop == ZFS_PROP_RESERVATION &&
-	    zfs_get_type(zhp) == ZFS_TYPE_VOLUME) {
-		uint64_t value;
-		uint64_t volsize;
-
-		volsize = zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE);
-		if (strcmp(cbp->cb_value, "none") == 0)
-			value = 0;
-		else
-			verify(zfs_nicestrtonum(cbp->cb_value, &value) == 0);
-
-		if (value > volsize) {
-			(void) fprintf(stderr, gettext("cannot set %s "
-			    "for '%s': size is greater than current "
-			    "volume size\n"), cbp->cb_propname,
-			    zfs_get_name(zhp));
-			return (-1);
-		}
-	}
-
-	if (zfs_prop_set(zhp, cbp->cb_prop, cbp->cb_value) != 0) {
+	if (zfs_prop_set(zhp, cbp->cb_propname, cbp->cb_value) != 0) {
 		switch (libzfs_errno(g_zfs)) {
 		case EZFS_MOUNTFAILED:
 			(void) fprintf(stderr, gettext("property may be set "
@@ -1803,30 +1928,9 @@
 		    gettext("missing property in property=value argument\n"));
 		usage(B_FALSE);
 	}
-	if (*cb.cb_value == '\0') {
-		(void) fprintf(stderr,
-		    gettext("missing value in property=value argument\n"));
-		usage(B_FALSE);
-	}
-
-	/* get the property type */
-	if ((cb.cb_prop = zfs_name_to_prop(cb.cb_propname)) ==
-	    ZFS_PROP_INVAL) {
-		(void) fprintf(stderr,
-		    gettext("invalid property '%s'\n"), cb.cb_propname);
-		usage(B_FALSE);
-	}
-
-	/*
-	 * Validate that the value is appropriate for this property.  We do this
-	 * once now so we don't generate multiple errors each time we try to
-	 * apply it to a dataset.
-	 */
-	if (zfs_prop_validate(g_zfs, cb.cb_prop, cb.cb_value, NULL) != 0)
-		return (1);
 
 	return (zfs_for_each(argc - 2, argv + 2, B_FALSE,
-	    ZFS_TYPE_ANY, NULL, set_callback, &cb));
+	    ZFS_TYPE_ANY, NULL, NULL, set_callback, &cb));
 }
 
 /*
@@ -2125,7 +2229,7 @@
 	share_mount_cbdata_t *cbp = data;
 	const char *cmdname = cbp->cb_type == OP_SHARE ? "share" : "mount";
 	struct mnttab mnt;
-	uint64_t zoned;
+	uint64_t zoned, canmount;
 
 	if (cbp->cb_options == NULL)
 		mnt.mnt_mntopts = "";
@@ -2165,6 +2269,7 @@
 	    sizeof (mountpoint), NULL, NULL, 0, B_FALSE) == 0);
 	verify(zfs_prop_get(zhp, ZFS_PROP_SHARENFS, shareopts,
 	    sizeof (shareopts), NULL, NULL, 0, B_FALSE) == 0);
+	canmount = zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT);
 
 	if (cbp->cb_type == OP_SHARE) {
 		if (strcmp(shareopts, "off") == 0) {
@@ -2205,6 +2310,15 @@
 		return (1);
 	}
 
+	if (!canmount) {
+		if (!cbp->cb_explicit)
+			return (0);
+
+		(void) fprintf(stderr, gettext("cannot %s '%s': 'canmount' "
+		    "property is set to 'off'\n"), cmdname, zfs_get_name(zhp));
+		return (1);
+	}
+
 	/*
 	 * At this point, we have verified that the mountpoint and/or shareopts
 	 * are appropriate for auto management.  Determine if the filesystem is
--- a/usr/src/cmd/zoneadm/Makefile	Mon Sep 04 19:32:13 2006 -0700
+++ b/usr/src/cmd/zoneadm/Makefile	Tue Sep 05 11:37:36 2006 -0700
@@ -43,7 +43,7 @@
 POFILE=zoneadm_all.po
 POFILES= $(OBJS:%.o=%.po)
 
-LDLIBS += -lzonecfg -lsocket -lgen -lpool -lbsm -lzfs -luuid
+LDLIBS += -lzonecfg -lsocket -lgen -lpool -lbsm -lzfs -luuid -lnvpair
 
 .KEEP_STATE:
 
--- a/usr/src/cmd/zoneadm/zfs.c	Mon Sep 04 19:32:13 2006 -0700
+++ b/usr/src/cmd/zoneadm/zfs.c	Tue Sep 05 11:37:36 2006 -0700
@@ -407,14 +407,27 @@
 	int		err;
 	zfs_handle_t	*zhp;
 	zfs_handle_t	*clone;
+	nvlist_t	*props = NULL;
 
 	if ((zhp = zfs_open(g_zfs, snapshot_name, ZFS_TYPE_SNAPSHOT)) == NULL)
 		return (Z_NO_ENTRY);
 
 	(void) printf(gettext("Cloning snapshot %s\n"), snapshot_name);
 
-	err = zfs_clone(zhp, zonepath);
+	if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0 ||
+	    nvlist_add_boolean_value(props,
+	    zfs_prop_to_name(ZFS_PROP_SHARENFS), B_FALSE) != 0) {
+		nvlist_free(props);
+		(void) fprintf(stderr, gettext("could not create ZFS clone "
+		    "%s: out of memory\n"), zonepath);
+		return (Z_ERR);
+	}
+
+	err = zfs_clone(zhp, zonepath, props);
 	zfs_close(zhp);
+
+	nvlist_free(props);
+
 	if (err != 0)
 		return (Z_ERR);
 
@@ -431,20 +444,11 @@
 		    "%s\n"), zfs_get_name(clone));
 		res = Z_ERR;
 
-	} else {
-		if (zfs_prop_set(clone, ZFS_PROP_SHARENFS, "off") != 0) {
-			/* we won't consider this a failure */
-			(void) fprintf(stderr, gettext("could not turn off the "
-			    "'sharenfs' property on ZFS clone %s\n"),
-			    zfs_get_name(clone));
-		}
-
-		if (clean_out_clone() != Z_OK) {
-			(void) fprintf(stderr, gettext("could not remove the "
-			    "software inventory from ZFS clone %s\n"),
-			    zfs_get_name(clone));
-			res = Z_ERR;
-		}
+	} else if (clean_out_clone() != Z_OK) {
+		(void) fprintf(stderr, gettext("could not remove the "
+		    "software inventory from ZFS clone %s\n"),
+		    zfs_get_name(clone));
+		res = Z_ERR;
 	}
 
 	zfs_close(clone);
@@ -700,25 +704,33 @@
 {
 	zfs_handle_t	*zhp;
 	char		zfs_name[MAXPATHLEN];
+	nvlist_t	*props = NULL;
 
 	if (path2name(zonepath, zfs_name, sizeof (zfs_name)) != Z_OK)
 		return;
 
-	if (zfs_create(g_zfs, zfs_name, ZFS_TYPE_FILESYSTEM, NULL, NULL) != 0 ||
+	if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0 ||
+	    nvlist_add_boolean_value(props, zfs_prop_to_name(ZFS_PROP_SHARENFS),
+	    B_FALSE) != 0) {
+		nvlist_free(props);
+		(void) fprintf(stderr, gettext("cannot create ZFS dataset %s: "
+		    "out of memory\n"), zfs_name);
+	}
+
+	if (zfs_create(g_zfs, zfs_name, ZFS_TYPE_FILESYSTEM, props) != 0 ||
 	    (zhp = zfs_open(g_zfs, zfs_name, ZFS_TYPE_ANY)) == NULL) {
 		(void) fprintf(stderr, gettext("cannot create ZFS dataset %s: "
 		    "%s\n"), zfs_name, libzfs_error_description(g_zfs));
+		nvlist_free(props);
 		return;
 	}
 
+	nvlist_free(props);
+
 	if (zfs_mount(zhp, NULL, 0) != 0) {
 		(void) fprintf(stderr, gettext("cannot mount ZFS dataset %s: "
 		    "%s\n"), zfs_name, libzfs_error_description(g_zfs));
 		(void) zfs_destroy(zhp);
-	} else if (zfs_prop_set(zhp, ZFS_PROP_SHARENFS, "off") != 0) {
-		(void) fprintf(stderr, gettext("file system %s successfully "
-		    "created,\nbut could not turn off the 'sharenfs' "
-		    "property\n"), zfs_name);
 	} else {
 		if (chmod(zonepath, S_IRWXU) != 0) {
 			(void) fprintf(stderr, gettext("file system %s "
@@ -849,7 +861,8 @@
 	if ((zhp = mount2zhandle(zonepath)) == NULL)
 		return (Z_ERR);
 
-	if (zfs_prop_set(zhp, ZFS_PROP_MOUNTPOINT, new_zonepath) == 0) {
+	if (zfs_prop_set(zhp, zfs_prop_to_name(ZFS_PROP_MOUNTPOINT),
+	    new_zonepath) == 0) {
 		/*
 		 * Clean up the old mount point.  We ignore any failure since
 		 * the zone is already successfully mounted on the new path.
--- a/usr/src/cmd/zoneadmd/vplat.c	Mon Sep 04 19:32:13 2006 -0700
+++ b/usr/src/cmd/zoneadmd/vplat.c	Tue Sep 05 11:37:36 2006 -0700
@@ -2605,7 +2605,8 @@
 		 * first because we'll get EPERM if it is already set.
 		 */
 		if (!zfs_prop_get_int(zhp, ZFS_PROP_ZONED) &&
-		    zfs_prop_set(zhp, ZFS_PROP_ZONED, "on") != 0) {
+		    zfs_prop_set(zhp, zfs_prop_to_name(ZFS_PROP_ZONED),
+		    "on") != 0) {
 			zerror(zlogp, B_FALSE, "cannot set 'zoned' "
 			    "property for ZFS dataset '%s'\n",
 			    dstab.zone_dataset_name);
--- a/usr/src/cmd/zpool/zpool_main.c	Mon Sep 04 19:32:13 2006 -0700
+++ b/usr/src/cmd/zpool/zpool_main.c	Tue Sep 05 11:37:36 2006 -0700
@@ -291,6 +291,14 @@
 		}
 	}
 
+	/*
+	 * See comments at end of main().
+	 */
+	if (getenv("ZFS_ABORT") != NULL) {
+		(void) printf("dumping core by request\n");
+		abort();
+	}
+
 	exit(requested ? 0 : 2);
 }
 
@@ -572,7 +580,7 @@
 
 	if (altroot != NULL && altroot[0] != '/') {
 		(void) fprintf(stderr, gettext("invalid alternate root '%s': "
-		    "must be an absolute path\n"));
+		    "must be an absolute path\n"), altroot);
 		nvlist_free(nvroot);
 		return (1);
 	}
@@ -651,7 +659,8 @@
 			if (pool != NULL) {
 				if (mountpoint != NULL)
 					verify(zfs_prop_set(pool,
-					    ZFS_PROP_MOUNTPOINT,
+					    zfs_prop_to_name(
+					    ZFS_PROP_MOUNTPOINT),
 					    mountpoint) == 0);
 				if (zfs_mount(pool, NULL, 0) == 0)
 					ret = zfs_share(pool);
@@ -2362,6 +2371,7 @@
 
 typedef struct status_cbdata {
 	int		cb_count;
+	boolean_t	cb_allpools;
 	boolean_t	cb_verbose;
 	boolean_t	cb_explain;
 	boolean_t	cb_first;
@@ -2685,8 +2695,15 @@
 	 * If we were given 'zpool status -x', only report those pools with
 	 * problems.
 	 */
-	if (reason == ZPOOL_STATUS_OK && cbp->cb_explain)
+	if (reason == ZPOOL_STATUS_OK && cbp->cb_explain) {
+		if (!cbp->cb_allpools) {
+			(void) printf(gettext("pool '%s' is healthy\n"),
+			    zpool_get_name(zhp));
+			if (cbp->cb_first)
+				cbp->cb_first = B_FALSE;
+		}
 		return (0);
+	}
 
 	if (cbp->cb_first)
 		cbp->cb_first = B_FALSE;
@@ -2852,8 +2869,8 @@
 				(void) printf(gettext("errors: No known data "
 				    "errors\n"));
 			else if (!cbp->cb_verbose)
-				(void) printf(gettext("errors: %d data errors, "
-				    "use '-v' for a list\n"), nerr);
+				(void) printf(gettext("errors: %llu data "
+				    "errors, use '-v' for a list\n"), nerr);
 			else
 				print_error_log(zhp);
 		}
@@ -2901,20 +2918,15 @@
 
 	cb.cb_first = B_TRUE;
 
+	if (argc == 0)
+		cb.cb_allpools = B_TRUE;
+
 	ret = for_each_pool(argc, argv, B_TRUE, status_callback, &cb);
 
 	if (argc == 0 && cb.cb_count == 0)
 		(void) printf(gettext("no pools available\n"));
-	else if (cb.cb_explain && cb.cb_first) {
-		if (argc == 0) {
-			(void) printf(gettext("all pools are healthy\n"));
-		} else {
-			int i;
-			for (i = 0; i < argc; i++)
-				(void) printf(gettext("pool '%s' is healthy\n"),
-				    argv[i]);
-		}
-	}
+	else if (cb.cb_explain && cb.cb_first && cb.cb_allpools)
+		(void) printf(gettext("all pools are healthy\n"));
 
 	return (ret);
 }
--- a/usr/src/common/zfs/zfs_prop.c	Mon Sep 04 19:32:13 2006 -0700
+++ b/usr/src/common/zfs/zfs_prop.c	Tue Sep 05 11:37:36 2006 -0700
@@ -77,86 +77,89 @@
 	int		pd_types;
 	const char	*pd_values;
 	const char	*pd_colname;
-	const char	*pd_colfmt;
+	boolean_t	pd_rightalign;
 } prop_desc_t;
 
 static prop_desc_t zfs_prop_table[ZFS_NPROP_ALL] = {
 	{ "type",	prop_type_string,	0,	NULL,	prop_readonly,
-	    ZFS_TYPE_ANY, "filesystem | volume | snapshot", "TYPE", "%10s" },
+	    ZFS_TYPE_ANY, "filesystem | volume | snapshot", "TYPE", B_TRUE },
 	{ "creation",	prop_type_number,	0,	NULL,	prop_readonly,
-	    ZFS_TYPE_ANY, "<date>", "CREATION", "%-20s" },
+	    ZFS_TYPE_ANY, "<date>", "CREATION", B_FALSE },
 	{ "used",	prop_type_number,	0,	NULL,	prop_readonly,
-	    ZFS_TYPE_ANY, "<size>",	"USED", "%5s" },
+	    ZFS_TYPE_ANY, "<size>",	"USED", B_TRUE },
 	{ "available",	prop_type_number,	0,	NULL,	prop_readonly,
-	    ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME, "<size>", "AVAIL", "%5s" },
+	    ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME, "<size>", "AVAIL", B_TRUE },
 	{ "referenced",	prop_type_number,	0,	NULL,	prop_readonly,
 	    ZFS_TYPE_ANY,
-	    "<size>", "REFER", "%5s" },
+	    "<size>", "REFER", B_TRUE },
 	{ "compressratio", prop_type_number,	0,	NULL,	prop_readonly,
-	    ZFS_TYPE_ANY, "<1.00x or higher if compressed>", "RATIO", "%5s" },
+	    ZFS_TYPE_ANY, "<1.00x or higher if compressed>", "RATIO", B_TRUE },
 	{ "mounted",	prop_type_boolean,	0,	NULL,	prop_readonly,
-	    ZFS_TYPE_FILESYSTEM, "yes | no | -", "MOUNTED", "%7s" },
+	    ZFS_TYPE_FILESYSTEM, "yes | no | -", "MOUNTED", B_TRUE },
 	{ "origin",	prop_type_string,	0,	NULL,	prop_readonly,
 	    ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME, "<snapshot>", "ORIGIN",
-	    "%-20s" },
+	    B_FALSE },
 	{ "quota",	prop_type_number,	0,	NULL,	prop_default,
-	    ZFS_TYPE_FILESYSTEM, "<size> | none", "QUOTA", "%5s" },
+	    ZFS_TYPE_FILESYSTEM, "<size> | none", "QUOTA", B_TRUE },
 	{ "reservation", prop_type_number,	0,	NULL,	prop_default,
 	    ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME,
-	    "<size> | none", "RESERV", "%6s" },
+	    "<size> | none", "RESERV", B_TRUE },
 	{ "volsize",	prop_type_number,	0,	NULL,	prop_default,
-	    ZFS_TYPE_VOLUME, "<size>", "VOLSIZE", "%7s" },
+	    ZFS_TYPE_VOLUME, "<size>", "VOLSIZE", B_TRUE },
 	{ "volblocksize", prop_type_number,	8192,	NULL,	prop_readonly,
-	    ZFS_TYPE_VOLUME, "512 to 128k, power of 2",	"VOLBLOCK", "%8s" },
+	    ZFS_TYPE_VOLUME, "512 to 128k, power of 2",	"VOLBLOCK", B_TRUE },
 	{ "recordsize",	prop_type_number,	SPA_MAXBLOCKSIZE,	NULL,
 	    prop_inherit,
 	    ZFS_TYPE_FILESYSTEM,
-	    "512 to 128k, power of 2", "RECSIZE", "%7s" },
+	    "512 to 128k, power of 2", "RECSIZE", B_TRUE },
 	{ "mountpoint",	prop_type_string,	0,	"/",	prop_inherit,
 	    ZFS_TYPE_FILESYSTEM,
-	    "<path> | legacy | none", "MOUNTPOINT", "%-20s" },
+	    "<path> | legacy | none", "MOUNTPOINT", B_FALSE },
 	{ "sharenfs",	prop_type_string,	0,	"off",	prop_inherit,
 	    ZFS_TYPE_FILESYSTEM,
-	    "on | off | share(1M) options", "SHARENFS", "%-15s" },
+	    "on | off | share(1M) options", "SHARENFS", B_FALSE },
 	{ "checksum",	prop_type_index,	ZIO_CHECKSUM_DEFAULT,	"on",
 	    prop_inherit,	ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME,
-	    "on | off | fletcher2 | fletcher4 | sha256", "CHECKSUM", "%10s" },
+	    "on | off | fletcher2 | fletcher4 | sha256", "CHECKSUM", B_TRUE },
 	{ "compression", prop_type_index,	ZIO_COMPRESS_DEFAULT,	"off",
 	    prop_inherit,	ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME,
-	    "on | off | lzjb", "COMPRESS", "%8s" },
+	    "on | off | lzjb", "COMPRESS", B_TRUE },
 	{ "atime",	prop_type_boolean,	1,	NULL,	prop_inherit,
 	    ZFS_TYPE_FILESYSTEM,
-	    "on | off", "ATIME", "%5s" },
+	    "on | off", "ATIME", B_TRUE },
 	{ "devices",	prop_type_boolean,	1,	NULL,	prop_inherit,
 	    ZFS_TYPE_FILESYSTEM | ZFS_TYPE_SNAPSHOT,
-	    "on | off", "DEVICES", "%7s" },
+	    "on | off", "DEVICES", B_TRUE },
 	{ "exec",	prop_type_boolean,	1,	NULL,	prop_inherit,
 	    ZFS_TYPE_FILESYSTEM | ZFS_TYPE_SNAPSHOT,
-	    "on | off", "EXEC", "%4s" },
+	    "on | off", "EXEC", B_TRUE },
 	{ "setuid",	prop_type_boolean,	1,	NULL,	prop_inherit,
 	    ZFS_TYPE_FILESYSTEM | ZFS_TYPE_SNAPSHOT, "on | off", "SETUID",
-	    "%6s" },
+	    B_TRUE },
 	{ "readonly",	prop_type_boolean,	0,	NULL,	prop_inherit,
 	    ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME,
-	    "on | off", "RDONLY", "%6s" },
+	    "on | off", "RDONLY", B_TRUE },
 	{ "zoned",	prop_type_boolean,	0,	NULL,	prop_inherit,
 	    ZFS_TYPE_FILESYSTEM,
-	    "on | off", "ZONED", "%5s" },
+	    "on | off", "ZONED", B_TRUE },
 	{ "snapdir",	prop_type_index,	ZFS_SNAPDIR_HIDDEN, "hidden",
 	    prop_inherit,
 	    ZFS_TYPE_FILESYSTEM,
-	    "hidden | visible", "SNAPDIR", "%7s" },
-	{ "aclmode", prop_type_index,	GROUPMASK, "groupmask", prop_inherit,
+	    "hidden | visible", "SNAPDIR", B_TRUE },
+	{ "aclmode", prop_type_index,	ZFS_ACL_GROUPMASK, "groupmask",
+	    prop_inherit, ZFS_TYPE_FILESYSTEM,
+	    "discard | groupmask | passthrough", "ACLMODE", B_TRUE },
+	{ "aclinherit", prop_type_index,	ZFS_ACL_SECURE,	"secure",
+	    prop_inherit, ZFS_TYPE_FILESYSTEM,
+	    "discard | noallow | secure | passthrough", "ACLINHERIT", B_TRUE },
+	{ "canmount",	prop_type_boolean,	1,	NULL,	prop_default,
 	    ZFS_TYPE_FILESYSTEM,
-	    "discard | groupmask | passthrough", "ACLMODE", "%11s" },
-	{ "aclinherit", prop_type_index,	SECURE,	"secure", prop_inherit,
-	    ZFS_TYPE_FILESYSTEM,
-	    "discard | noallow | secure | passthrough", "ACLINHERIT", "%11s" },
+	    "on | off", "CANMOUNT", B_TRUE },
 	{ "createtxg",	prop_type_number,	0,	NULL,	prop_readonly,
 	    ZFS_TYPE_ANY, NULL, NULL, NULL},
 	{ "name",	prop_type_string,	0,	NULL,	prop_readonly,
 	    ZFS_TYPE_ANY,
-	    NULL, "NAME", "%-20s" },
+	    NULL, "NAME", B_FALSE },
 };
 
 zfs_proptype_t
@@ -165,8 +168,8 @@
 	return (zfs_prop_table[prop].pd_proptype);
 }
 
-static int
-propname_match(const char *p, int prop, size_t len)
+static boolean_t
+propname_match(const char *p, zfs_prop_t prop, size_t len)
 {
 	const char *propname = zfs_prop_table[prop].pd_name;
 #ifndef _KERNEL
@@ -176,16 +179,16 @@
 
 #ifndef _KERNEL
 	if (colname == NULL)
-		return (0);
+		return (B_FALSE);
 #endif
 
 	if (len == strlen(propname) &&
 	    strncmp(p, propname, len) == 0)
-		return (1);
+		return (B_TRUE);
 
 #ifndef _KERNEL
 	if (len != strlen(colname))
-		return (0);
+		return (B_FALSE);
 
 	for (c = 0; c < len; c++)
 		if (p[c] != tolower(colname[c]))
@@ -193,7 +196,7 @@
 
 	return (colname[c] == '\0');
 #else
-	return (0);
+	return (B_FALSE);
 #endif
 }
 
@@ -215,6 +218,42 @@
 }
 
 /*
+ * For user property names, we allow all lowercase alphanumeric characters, plus
+ * a few useful punctuation characters.
+ */
+static int
+valid_char(char c)
+{
+	return ((c >= 'a' && c <= 'z') ||
+	    (c >= '0' && c <= '9') ||
+	    c == '-' || c == '_' || c == '.' || c == ':');
+}
+
+/*
+ * Returns true if this is a valid user-defined property (one with a ':').
+ */
+boolean_t
+zfs_prop_user(const char *name)
+{
+	int i;
+	char c;
+	boolean_t foundsep = B_FALSE;
+
+	for (i = 0; i < strlen(name); i++) {
+		c = name[i];
+		if (!valid_char(c))
+			return (B_FALSE);
+		if (c == ':')
+			foundsep = B_TRUE;
+	}
+
+	if (!foundsep)
+		return (B_FALSE);
+
+	return (B_TRUE);
+}
+
+/*
  * Return the default value for the given property.
  */
 const char *
@@ -238,7 +277,6 @@
 	return (zfs_prop_table[prop].pd_attr == prop_readonly);
 }
 
-#ifndef _KERNEL
 /*
  * Given a property ID, returns the corresponding name.
  */
@@ -257,6 +295,117 @@
 	return (zfs_prop_table[prop].pd_attr == prop_inherit);
 }
 
+#ifndef _KERNEL
+
+typedef struct zfs_index {
+	const char *name;
+	uint64_t index;
+} zfs_index_t;
+
+static zfs_index_t checksum_table[] = {
+	{ "on",		ZIO_CHECKSUM_ON },
+	{ "off",	ZIO_CHECKSUM_OFF },
+	{ "fletcher2",	ZIO_CHECKSUM_FLETCHER_2 },
+	{ "fletcher4",	ZIO_CHECKSUM_FLETCHER_4 },
+	{ "sha256",	ZIO_CHECKSUM_SHA256 },
+	{ NULL }
+};
+
+static zfs_index_t compress_table[] = {
+	{ "on",		ZIO_COMPRESS_ON },
+	{ "off",	ZIO_COMPRESS_OFF },
+	{ "lzjb",	ZIO_COMPRESS_LZJB },
+	{ NULL }
+};
+
+static zfs_index_t snapdir_table[] = {
+	{ "hidden",	ZFS_SNAPDIR_HIDDEN },
+	{ "visible",	ZFS_SNAPDIR_VISIBLE },
+	{ NULL }
+};
+
+static zfs_index_t acl_mode_table[] = {
+	{ "discard",	ZFS_ACL_DISCARD },
+	{ "groupmask",	ZFS_ACL_GROUPMASK },
+	{ "passthrough", ZFS_ACL_PASSTHROUGH },
+	{ NULL }
+};
+
+static zfs_index_t acl_inherit_table[] = {
+	{ "discard",	ZFS_ACL_DISCARD },
+	{ "noallow",	ZFS_ACL_NOALLOW },
+	{ "secure",	ZFS_ACL_SECURE },
+	{ "passthrough", ZFS_ACL_PASSTHROUGH },
+	{ NULL }
+};
+
+static zfs_index_t *
+zfs_prop_index_table(zfs_prop_t prop)
+{
+	switch (prop) {
+	case ZFS_PROP_CHECKSUM:
+		return (checksum_table);
+		break;
+	case ZFS_PROP_COMPRESSION:
+		return (compress_table);
+		break;
+	case ZFS_PROP_SNAPDIR:
+		return (snapdir_table);
+		break;
+	case ZFS_PROP_ACLMODE:
+		return (acl_mode_table);
+		break;
+	case ZFS_PROP_ACLINHERIT:
+		return (acl_inherit_table);
+		break;
+	default:
+		return (NULL);
+	}
+}
+
+
+/*
+ * Tables of index types, plus functions to convert between the user view
+ * (strings) and internal representation (uint64_t).
+ */
+int
+zfs_prop_string_to_index(zfs_prop_t prop, const char *string, uint64_t *index)
+{
+	zfs_index_t *table;
+	int i;
+
+	if ((table = zfs_prop_index_table(prop)) == NULL)
+		return (-1);
+
+	for (i = 0; table[i].name != NULL; i++) {
+		if (strcmp(string, table[i].name) == 0) {
+			*index = table[i].index;
+			return (0);
+		}
+	}
+
+	return (-1);
+}
+
+int
+zfs_prop_index_to_string(zfs_prop_t prop, uint64_t index, const char **string)
+{
+	zfs_index_t *table;
+	int i;
+
+	if ((table = zfs_prop_index_table(prop)) == NULL)
+		return (-1);
+
+	for (i = 0; table[i].name != NULL; i++) {
+		if (table[i].index == index) {
+			*string = table[i].name;
+			return (0);
+		}
+	}
+
+	return (-1);
+}
+
 /*
  * Returns TRUE if the property applies to the given dataset types.
  */
@@ -299,110 +448,75 @@
 }
 
 /*
- * Returns the column formatting for the given property.  Used only in
- * 'zfs list -o', but centralized here with the other property information.
+ * Returns whether the given property should be displayed right-justified for
+ * 'zfs list'.
  */
-const char *
-zfs_prop_column_format(zfs_prop_t prop)
+boolean_t
+zfs_prop_align_right(zfs_prop_t prop)
 {
-	return (zfs_prop_table[prop].pd_colfmt);
+	return (zfs_prop_table[prop].pd_rightalign);
 }
 
 /*
- * Given a comma-separated list of fields, fills in the specified array with a
- * list of properties.  The keyword "all" can be used to specify all properties.
- * The 'count' parameter returns the number of matching properties placed into
- * the list.  The 'props' parameter must point to an array of at least
- * ZFS_NPROP_ALL elements.
+ * Determines the minimum width for the column, and indicates whether it's fixed
+ * or not.  Only string columns are non-fixed.
  */
-int
-zfs_get_proplist(char *fields, zfs_prop_t *props, int max,
-    int *count, char **badopt)
+size_t
+zfs_prop_width(zfs_prop_t prop, boolean_t *fixed)
 {
+	prop_desc_t *pd = &zfs_prop_table[prop];
+	zfs_index_t *idx;
+	size_t ret;
 	int i;
-	size_t len;
-	char *s, *p;
 
-	*count = 0;
+	*fixed = B_TRUE;
 
 	/*
-	 * Check for the special value "all", which indicates all properties
-	 * should be displayed.
+	 * Start with the width of the column name.
 	 */
-	if (strcmp(fields, "all") == 0) {
-		if (max < ZFS_NPROP_VISIBLE)
-			return (EOVERFLOW);
-
-		for (i = 0; i < ZFS_NPROP_VISIBLE; i++)
-			props[i] = i;
-		*count = ZFS_NPROP_VISIBLE;
-		return (0);
-	}
+	ret = strlen(pd->pd_colname);
 
 	/*
-	 * It would be nice to use getsubopt() here, but the inclusion of column
-	 * aliases makes this more effort than it's worth.
+	 * For fixed-width values, make sure the width is large enough to hold
+	 * any possible value.
 	 */
-	s = fields;
-	while (*s != '\0') {
-		if ((p = strchr(s, ',')) == NULL) {
-			len = strlen(s);
-			p = s + len;
-		} else {
-			len = p - s;
-		}
-
+	switch (pd->pd_proptype) {
+	case prop_type_number:
 		/*
-		 * Check for empty options.
+		 * The maximum length of a human-readable number is 5 characters
+		 * ("20.4M", for example).
 		 */
-		if (len == 0) {
-			*badopt = "";
-			return (EINVAL);
-		}
-
+		if (ret < 5)
+			ret = 5;
 		/*
-		 * Check all regular property names.
+		 * 'creation' is handled specially because it's a number
+		 * internally, but displayed as a date string.
 		 */
-		for (i = 0; i < ZFS_NPROP_ALL; i++) {
-			if (propname_match(s, i, len))
-				break;
-		}
-
+		if (prop == ZFS_PROP_CREATION)
+			*fixed = B_FALSE;
+		break;
+	case prop_type_boolean:
 		/*
-		 * If no column is specified, then return failure, setting
-		 * 'badopt' to point to the invalid option.
+		 * The maximum length of a boolean value is 3 characters, for
+		 * "off".
 		 */
-		if (i == ZFS_NPROP_ALL) {
-			s[len] = '\0';
-			*badopt = s;
-			return (EINVAL);
+		if (ret < 3)
+			ret = 3;
+		break;
+	case prop_type_index:
+		idx = zfs_prop_index_table(prop);
+		for (i = 0; idx[i].name != NULL; i++) {
+			if (strlen(idx[i].name) > ret)
+				ret = strlen(idx[i].name);
 		}
+		break;
 
-		/*
-		 * If the user specified too many options (by using the same one
-		 * multiple times). return an error with 'badopt' set to NULL to
-		 * indicate this condition.
-		 */
-		if (*count == max)
-			return (EOVERFLOW);
-
-		props[*count] = i;
-		*count += 1;
-
-		s = p;
-		if (*s == ',')
-			s++;
+	case prop_type_string:
+		*fixed = B_FALSE;
+		break;
 	}
 
-	/*
-	 * If no fields were specified, return an error.
-	 */
-	if (*count == 0) {
-		*badopt = "";
-		return (EINVAL);
-	}
-
-	return (0);
+	return (ret);
 }
 
 #endif
--- a/usr/src/common/zfs/zfs_prop.h	Mon Sep 04 19:32:13 2006 -0700
+++ b/usr/src/common/zfs/zfs_prop.h	Tue Sep 05 11:37:36 2006 -0700
@@ -2,9 +2,8 @@
  * CDDL HEADER START
  *
  * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License").  You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
  *
  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  * or http://www.opensolaris.org/os/licensing.
@@ -20,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -48,6 +47,9 @@
 } zfs_proptype_t;
 
 zfs_proptype_t zfs_prop_get_type(zfs_prop_t);
+int zfs_prop_string_to_index(zfs_prop_t, const char *, uint64_t *);
+int zfs_prop_index_to_string(zfs_prop_t, uint64_t, const char **);
+size_t zfs_prop_width(zfs_prop_t, boolean_t *);
 
 #ifdef	__cplusplus
 }
--- a/usr/src/lib/libzfs/common/libzfs.h	Mon Sep 04 19:32:13 2006 -0700
+++ b/usr/src/lib/libzfs/common/libzfs.h	Tue Sep 05 11:37:36 2006 -0700
@@ -201,10 +201,6 @@
 extern int zpool_refresh_stats(zpool_handle_t *, boolean_t *);
 extern int zpool_get_errlog(zpool_handle_t *, nvlist_t ***, size_t *);
 
-#define	ZPOOL_ERR_DATASET	"dataset"
-#define	ZPOOL_ERR_OBJECT	"object"
-#define	ZPOOL_ERR_RANGE		"range"
-
 /*
  * Import and export functions
  */
@@ -246,25 +242,36 @@
  * Property management functions.  Some functions are shared with the kernel,
  * and are found in sys/fs/zfs.h.
  */
-const char *zfs_prop_to_name(zfs_prop_t);
-int zfs_prop_set(zfs_handle_t *, zfs_prop_t, const char *);
-int zfs_prop_get(zfs_handle_t *, zfs_prop_t, char *, size_t, zfs_source_t *,
-    char *, size_t, boolean_t);
-int zfs_prop_get_numeric(zfs_handle_t *, zfs_prop_t, uint64_t *, zfs_source_t *,
-    char *, size_t);
-uint64_t zfs_prop_get_int(zfs_handle_t *, zfs_prop_t);
-int zfs_prop_validate(libzfs_handle_t *, zfs_prop_t, const char *, uint64_t *);
-int zfs_prop_inheritable(zfs_prop_t);
-int zfs_prop_inherit(zfs_handle_t *, zfs_prop_t);
-const char *zfs_prop_values(zfs_prop_t);
-int zfs_prop_valid_for_type(zfs_prop_t, int);
-const char *zfs_prop_default_string(zfs_prop_t prop);
-uint64_t zfs_prop_default_numeric(zfs_prop_t);
-int zfs_prop_is_string(zfs_prop_t prop);
-const char *zfs_prop_column_name(zfs_prop_t);
-const char *zfs_prop_column_format(zfs_prop_t);
-int zfs_get_proplist(char *fields, zfs_prop_t *proplist, int max, int *count,
-    char **badopt);
+extern const char *zfs_prop_to_name(zfs_prop_t);
+extern int zfs_prop_set(zfs_handle_t *, const char *, const char *);
+extern int zfs_prop_get(zfs_handle_t *, zfs_prop_t, char *, size_t,
+    zfs_source_t *, char *, size_t, boolean_t);
+extern int zfs_prop_get_numeric(zfs_handle_t *, zfs_prop_t, uint64_t *,
+    zfs_source_t *, char *, size_t);
+extern uint64_t zfs_prop_get_int(zfs_handle_t *, zfs_prop_t);
+extern const char *zfs_prop_get_string(zfs_handle_t *, zfs_prop_t);
+extern int zfs_prop_inherit(zfs_handle_t *, const char *);
+extern const char *zfs_prop_values(zfs_prop_t);
+extern int zfs_prop_valid_for_type(zfs_prop_t, int);
+extern const char *zfs_prop_default_string(zfs_prop_t prop);
+extern uint64_t zfs_prop_default_numeric(zfs_prop_t);
+extern int zfs_prop_is_string(zfs_prop_t prop);
+extern const char *zfs_prop_column_name(zfs_prop_t);
+extern boolean_t zfs_prop_align_right(zfs_prop_t);
+
+typedef struct zfs_proplist {
+	zfs_prop_t	pl_prop;
+	char		*pl_user_prop;
+	struct zfs_proplist *pl_next;
+	boolean_t	pl_all;
+	size_t		pl_width;
+	boolean_t	pl_fixed;
+} zfs_proplist_t;
+
+extern int zfs_get_proplist(libzfs_handle_t *, char *, zfs_proplist_t **);
+extern void zfs_free_proplist(zfs_proplist_t *);
+extern int zfs_expand_proplist(zfs_handle_t *, zfs_proplist_t **);
+extern nvlist_t *zfs_get_user_props(zfs_handle_t *);
 
 #define	ZFS_MOUNTPOINT_NONE	"none"
 #define	ZFS_MOUNTPOINT_LEGACY	"legacy"
@@ -283,10 +290,10 @@
  * Functions to create and destroy datasets.
  */
 extern int zfs_create(libzfs_handle_t *, const char *, zfs_type_t,
-    const char *, const char *);
+    nvlist_t *);
 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 *);
+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_rollback(zfs_handle_t *, zfs_handle_t *, int);
 extern int zfs_rename(zfs_handle_t *, const char *);
@@ -331,7 +338,7 @@
  * Utility function to convert a number to a human-readable form.
  */
 extern void zfs_nicenum(uint64_t, char *, size_t);
-extern int zfs_nicestrtonum(const char *, uint64_t *);
+extern int zfs_nicestrtonum(libzfs_handle_t *, const char *, uint64_t *);
 
 /*
  * Pool destroy special.  Remove the device information without destroying
--- a/usr/src/lib/libzfs/common/libzfs_changelist.c	Mon Sep 04 19:32:13 2006 -0700
+++ b/usr/src/lib/libzfs/common/libzfs_changelist.c	Tue Sep 05 11:37:36 2006 -0700
@@ -107,7 +107,7 @@
 		 * If we have a volume and this was a rename, remove the
 		 * /dev/zvol links
 		 */
-		if (cn->cn_handle->zfs_volblocksize &&
+		if (ZFS_IS_VOLUME(cn->cn_handle) &&
 		    clp->cl_realprop == ZFS_PROP_NAME) {
 			if (zvol_remove_link(cn->cn_handle->zfs_hdl,
 			    cn->cn_handle->zfs_name) != 0)
@@ -166,7 +166,7 @@
 		 * If this is a volume and we're doing a rename, recreate the
 		 * /dev/zvol links.
 		 */
-		if (cn->cn_handle->zfs_volblocksize &&
+		if (ZFS_IS_VOLUME(cn->cn_handle) &&
 		    clp->cl_realprop == ZFS_PROP_NAME) {
 			if (zvol_create_link(cn->cn_handle->zfs_hdl,
 			    cn->cn_handle->zfs_name) != 0)
@@ -358,7 +358,7 @@
 	 * rename, then always add it to the changelist.
 	 */
 
-	if (!(zhp->zfs_volblocksize && clp->cl_realprop == ZFS_PROP_NAME) &&
+	if (!(ZFS_IS_VOLUME(zhp) && clp->cl_realprop == ZFS_PROP_NAME) &&
 	    zfs_prop_get(zhp, clp->cl_prop, property,
 	    sizeof (property), &sourcetype, where, sizeof (where),
 	    B_FALSE) != 0) {
@@ -508,6 +508,8 @@
 	} else if (prop == ZFS_PROP_ZONED) {
 		clp->cl_prop = ZFS_PROP_MOUNTPOINT;
 		clp->cl_allchildren = B_TRUE;
+	} else if (prop == ZFS_PROP_CANMOUNT) {
+		clp->cl_prop = ZFS_PROP_MOUNTPOINT;
 	} else {
 		clp->cl_prop = prop;
 	}
--- a/usr/src/lib/libzfs/common/libzfs_config.c	Mon Sep 04 19:32:13 2006 -0700
+++ b/usr/src/lib/libzfs/common/libzfs_config.c	Tue Sep 05 11:37:36 2006 -0700
@@ -129,19 +129,9 @@
 			return (no_memory(hdl));
 	}
 
-	/*
-	 * Issue the ZFS_IOC_POOL_CONFIGS ioctl.
-	 * This can fail for one of two reasons:
-	 *
-	 * 	EEXIST		The generation counts match, nothing to do.
-	 * 	ENOMEM		The zc_config_dst buffer isn't large enough to
-	 * 			hold the config; zc_config_dst_size will have
-	 *			been modified to tell us how much to allocate.
-	 */
-	zc.zc_config_dst_size = 1024;
-	if ((zc.zc_config_dst = (uint64_t)(uintptr_t)
-	    zfs_alloc(hdl, zc.zc_config_dst_size)) == NULL)
+	if (zcmd_alloc_dst_nvlist(hdl, &zc, 0) != 0)
 		return (-1);
+
 	for (;;) {
 		zc.zc_cookie = hdl->libzfs_ns_gen;
 		if (ioctl(hdl->libzfs_fd, ZFS_IOC_POOL_CONFIGS, &zc) != 0) {
@@ -150,18 +140,18 @@
 				/*
 				 * The namespace hasn't changed.
 				 */
-				free((void *)(uintptr_t)zc.zc_config_dst);
+				zcmd_free_nvlists(&zc);
 				return (0);
 
 			case ENOMEM:
-				free((void *)(uintptr_t)zc.zc_config_dst);
-				if ((zc.zc_config_dst = (uint64_t)(uintptr_t)
-				    zfs_alloc(hdl, zc.zc_config_dst_size))
-				    == NULL)
+				if (zcmd_expand_dst_nvlist(hdl, &zc) != 0) {
+					zcmd_free_nvlists(&zc);
 					return (-1);
+				}
 				break;
 
 			default:
+				zcmd_free_nvlists(&zc);
 				return (zfs_standard_error(hdl, errno,
 				    dgettext(TEXT_DOMAIN, "failed to read "
 				    "pool configuration")));
@@ -172,13 +162,12 @@
 		}
 	}
 
-	if (nvlist_unpack((void *)(uintptr_t)zc.zc_config_dst,
-	    zc.zc_config_dst_size, &config, 0) != 0) {
-		free((void *)(uintptr_t)zc.zc_config_dst);
-		return (no_memory(hdl));
+	if (zcmd_read_dst_nvlist(hdl, &zc, &config) != 0) {
+		zcmd_free_nvlists(&zc);
+		return (-1);
 	}
 
-	free((void *)(uintptr_t)zc.zc_config_dst);
+	zcmd_free_nvlists(&zc);
 
 	/*
 	 * Clear out any existing configuration information.
@@ -256,6 +245,7 @@
 	zfs_cmd_t zc = { 0 };
 	int error;
 	nvlist_t *config;
+	libzfs_handle_t *hdl = zhp->zpool_hdl;
 
 	*missing = B_FALSE;
 	(void) strcpy(zc.zc_name, zhp->zpool_name);
@@ -263,9 +253,7 @@
 	if (zhp->zpool_config_size == 0)
 		zhp->zpool_config_size = 1 << 16;
 
-	zc.zc_config_dst_size = zhp->zpool_config_size;
-	if ((zc.zc_config_dst = (uint64_t)(uintptr_t)
-	    zfs_alloc(zhp->zpool_hdl, zc.zc_config_dst_size)) == NULL)
+	if (zcmd_alloc_dst_nvlist(hdl, &zc, zhp->zpool_config_size) != 0)
 		return (-1);
 
 	for (;;) {
@@ -279,13 +267,12 @@
 		}
 
 		if (errno == ENOMEM) {
-			free((void *)(uintptr_t)zc.zc_config_dst);
-			if ((zc.zc_config_dst = (uint64_t)(uintptr_t)
-			    zfs_alloc(zhp->zpool_hdl,
-			    zc.zc_config_dst_size)) == NULL)
+			if (zcmd_expand_dst_nvlist(hdl, &zc) != 0) {
+				zcmd_free_nvlists(&zc);
 				return (-1);
+			}
 		} else {
-			free((void *)(uintptr_t)zc.zc_config_dst);
+			zcmd_free_nvlists(&zc);
 			if (errno == ENOENT || errno == EINVAL)
 				*missing = B_TRUE;
 			zhp->zpool_state = POOL_STATE_UNAVAIL;
@@ -293,14 +280,14 @@
 		}
 	}
 
-	if (nvlist_unpack((void *)(uintptr_t)zc.zc_config_dst,
-	    zc.zc_config_dst_size, &config, 0) != 0) {
-		free((void *)(uintptr_t)zc.zc_config_dst);
-		return (no_memory(zhp->zpool_hdl));
+	if (zcmd_read_dst_nvlist(hdl, &zc, &config) != 0) {
+		zcmd_free_nvlists(&zc);
+		return (-1);
 	}
 
-	zhp->zpool_config_size = zc.zc_config_dst_size;
-	free((void *)(uintptr_t)zc.zc_config_dst);
+	zcmd_free_nvlists(&zc);
+
+	zhp->zpool_config_size = zc.zc_nvlist_dst_size;
 
 	if (set_pool_health(config) != 0) {
 		nvlist_free(config);
--- a/usr/src/lib/libzfs/common/libzfs_dataset.c	Mon Sep 04 19:32:13 2006 -0700
+++ b/usr/src/lib/libzfs/common/libzfs_dataset.c	Tue Sep 05 11:37:36 2006 -0700
@@ -43,6 +43,7 @@
 
 #include <sys/spa.h>
 #include <sys/zio.h>
+#include <sys/zap.h>
 #include <libzfs.h>
 
 #include "zfs_namecheck.h"
@@ -186,27 +187,57 @@
 }
 
 /*
+ * This function takes the raw DSL properties, and filters out the user-defined
+ * properties into a separate nvlist.
+ */
+static int
+process_user_props(zfs_handle_t *zhp)
+{
+	libzfs_handle_t *hdl = zhp->zfs_hdl;
+	nvpair_t *elem;
+	nvlist_t *propval;
+
+	nvlist_free(zhp->zfs_user_props);
+
+	if (nvlist_alloc(&zhp->zfs_user_props, NV_UNIQUE_NAME, 0) != 0)
+		return (no_memory(hdl));
+
+	elem = NULL;
+	while ((elem = nvlist_next_nvpair(zhp->zfs_props, elem)) != NULL) {
+		if (!zfs_prop_user(nvpair_name(elem)))
+			continue;
+
+		verify(nvpair_value_nvlist(elem, &propval) == 0);
+		if (nvlist_add_nvlist(zhp->zfs_user_props,
+		    nvpair_name(elem), propval) != 0)
+			return (no_memory(hdl));
+	}
+
+	return (0);
+}
+
+/*
  * Utility function to gather stats (objset and zpl) for the given object.
  */
 static int
 get_stats(zfs_handle_t *zhp)
 {
 	zfs_cmd_t zc = { 0 };
+	libzfs_handle_t *hdl = zhp->zfs_hdl;
 
 	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
 
-	if ((zc.zc_config_src = (uint64_t)(uintptr_t)malloc(1024)) == NULL)
+	if (zcmd_alloc_dst_nvlist(hdl, &zc, 0) != 0)
 		return (-1);
-	zc.zc_config_src_size = 1024;
 
 	while (ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) != 0) {
 		if (errno == ENOMEM) {
-			free((void *)(uintptr_t)zc.zc_config_src);
-			if ((zc.zc_config_src = (uint64_t)(uintptr_t)
-			    malloc(zc.zc_config_src_size)) == NULL)
+			if (zcmd_expand_dst_nvlist(hdl, &zc) != 0) {
+				zcmd_free_nvlists(&zc);
 				return (-1);
+			}
 		} else {
-			free((void *)(uintptr_t)zc.zc_config_src);
+			zcmd_free_nvlists(&zc);
 			return (-1);
 		}
 	}
@@ -214,23 +245,24 @@
 	bcopy(&zc.zc_objset_stats, &zhp->zfs_dmustats,
 	    sizeof (zc.zc_objset_stats));
 
-	(void) strcpy(zhp->zfs_root, zc.zc_root);
+	(void) strlcpy(zhp->zfs_root, zc.zc_value, sizeof (zhp->zfs_root));
 
 	if (zhp->zfs_props) {
 		nvlist_free(zhp->zfs_props);
 		zhp->zfs_props = NULL;
 	}
 
-	if (nvlist_unpack((void *)(uintptr_t)zc.zc_config_src,
-	    zc.zc_config_src_size, &zhp->zfs_props, 0) != 0) {
-		free((void *)(uintptr_t)zc.zc_config_src);
+	if (zcmd_read_dst_nvlist(hdl, &zc, &zhp->zfs_props) != 0) {
+		zcmd_free_nvlists(&zc);
 		return (-1);
 	}
 
-	zhp->zfs_volsize = zc.zc_volsize;
-	zhp->zfs_volblocksize = zc.zc_volblocksize;
-
-	free((void *)(uintptr_t)zc.zc_config_src);
+	zcmd_free_nvlists(&zc);
+
+	zhp->zfs_volstats = zc.zc_vol_stats;
+
+	if (process_user_props(zhp) != 0)
+		return (-1);
 
 	return (0);
 }
@@ -373,64 +405,11 @@
 {
 	if (zhp->zfs_mntopts)
 		free(zhp->zfs_mntopts);
-	if (zhp->zfs_props)
-		nvlist_free(zhp->zfs_props);
+	nvlist_free(zhp->zfs_props);
+	nvlist_free(zhp->zfs_user_props);
 	free(zhp);
 }
 
-struct {
-	const char *name;
-	uint64_t value;
-} checksum_table[] = {
-	{ "on",		ZIO_CHECKSUM_ON },
-	{ "off",	ZIO_CHECKSUM_OFF },
-	{ "fletcher2",	ZIO_CHECKSUM_FLETCHER_2 },
-	{ "fletcher4",	ZIO_CHECKSUM_FLETCHER_4 },
-	{ "sha256",	ZIO_CHECKSUM_SHA256 },
-	{ NULL }
-};
-
-struct {
-	const char *name;
-	uint64_t value;
-} compress_table[] = {
-	{ "on",		ZIO_COMPRESS_ON },
-	{ "off",	ZIO_COMPRESS_OFF },
-	{ "lzjb",	ZIO_COMPRESS_LZJB },
-	{ NULL }
-};
-
-struct {
-	const char *name;
-	uint64_t value;
-} snapdir_table[] = {
-	{ "hidden",	ZFS_SNAPDIR_HIDDEN },
-	{ "visible",	ZFS_SNAPDIR_VISIBLE },
-	{ NULL }
-};
-
-struct {
-	const char *name;
-	uint64_t value;
-} acl_mode_table[] = {
-	{ "discard",	DISCARD },
-	{ "groupmask",	GROUPMASK },
-	{ "passthrough", PASSTHROUGH },
-	{ NULL }
-};
-
-struct {
-	const char *name;
-	uint64_t value;
-} acl_inherit_table[] = {
-	{ "discard",	DISCARD },
-	{ "noallow",	NOALLOW },
-	{ "secure",	SECURE },
-	{ "passthrough", PASSTHROUGH },
-	{ NULL }
-};
-
-
 /*
  * Given a numeric suffix, convert the value into a number of bits that the
  * resulting value must be shifted.
@@ -541,272 +520,496 @@
 }
 
 int
-zfs_nicestrtonum(const char *str, uint64_t *val)
+zfs_nicestrtonum(libzfs_handle_t *hdl, const char *str, uint64_t *val)
+{
+	return (nicestrtonum(hdl, str, val));
+}
+
+/*
+ * The prop_parse_*() functions are designed to allow flexibility in callers
+ * when setting properties.  At the DSL layer, all properties are either 64-bit
+ * numbers or strings.  We want the user to be able to ignore this fact and
+ * specify properties as native values (boolean, for example) or as strings (to
+ * simplify command line utilities).  This also handles converting index types
+ * (compression, checksum, etc) from strings to their on-disk index.
+ */
+
+static int
+prop_parse_boolean(libzfs_handle_t *hdl, nvpair_t *elem, uint64_t *val)
 {
-	return (nicestrtonum(NULL, str, val));
+	uint64_t ret;
+
+	switch (nvpair_type(elem)) {
+	case DATA_TYPE_STRING:
+		{
+			char *value;
+			VERIFY(nvpair_value_string(elem, &value) == 0);
+
+			if (strcmp(value, "on") == 0) {
+				ret = 1;
+			} else if (strcmp(value, "off") == 0) {
+				ret = 0;
+			} else {
+				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+				    "property '%s' must be 'on' or 'off'"),
+				    nvpair_name(elem));
+				return (-1);
+			}
+			break;
+		}
+
+	case DATA_TYPE_UINT64:
+		{
+			VERIFY(nvpair_value_uint64(elem, &ret) == 0);
+			if (ret > 1) {
+				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+				    "'%s' must be a boolean value"),
+				    nvpair_name(elem));
+				return (-1);
+			}
+			break;
+		}
+
+	case DATA_TYPE_BOOLEAN_VALUE:
+		{
+			boolean_t value;
+			VERIFY(nvpair_value_boolean_value(elem, &value) == 0);
+			ret = value;
+			break;
+		}
+
+	default:
+		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+		    "'%s' must be a boolean value"),
+		    nvpair_name(elem));
+		return (-1);
+	}
+
+	*val = ret;
+	return (0);
+}
+
+static int
+prop_parse_number(libzfs_handle_t *hdl, nvpair_t *elem, zfs_prop_t prop,
+    uint64_t *val)
+{
+	uint64_t ret;
+	boolean_t isnone = B_FALSE;
+
+	switch (nvpair_type(elem)) {
+	case DATA_TYPE_STRING:
+		{
+			char *value;
+			(void) nvpair_value_string(elem, &value);
+			if (strcmp(value, "none") == 0) {
+				isnone = B_TRUE;
+				ret = 0;
+			} else if (nicestrtonum(hdl, value, &ret) != 0) {
+				return (-1);
+			}
+			break;
+		}
+
+	case DATA_TYPE_UINT64:
+		(void) nvpair_value_uint64(elem, &ret);
+		break;
+
+	default:
+		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+		    "'%s' must be a number"),
+		    nvpair_name(elem));
+		return (-1);
+	}
+
+	/*
+	 * Quota special: force 'none' and don't allow 0.
+	 */
+	if (ret == 0 && !isnone && prop == ZFS_PROP_QUOTA) {
+		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+		    "use 'none' to disable quota"));
+		return (-1);
+	}
+
+	*val = ret;
+	return (0);
+}
+
+static int
+prop_parse_index(libzfs_handle_t *hdl, nvpair_t *elem, zfs_prop_t prop,
+    uint64_t *val)
+{
+	char *propname = nvpair_name(elem);
+	char *value;
+
+	if (nvpair_type(elem) != DATA_TYPE_STRING) {
+		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+		    "'%s' must be a string"), propname);
+		return (-1);
+	}
+
+	(void) nvpair_value_string(elem, &value);
+
+	if (zfs_prop_string_to_index(prop, value, val) != 0) {
+		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+		    "'%s' must be one of '%s'"), propname,
+		    zfs_prop_values(prop));
+		return (-1);
+	}
+
+	return (0);
 }
 
 /*
- * Given a property type and value, verify that the value is appropriate.  Used
- * by zfs_prop_set() and some libzfs consumers.
+ * Given an nvlist of properties to set, validates that they are correct, and
+ * parses any numeric properties (index, boolean, etc) if they are specified as
+ * strings.
  */
-int
-zfs_prop_validate(libzfs_handle_t *hdl, zfs_prop_t prop, const char *value,
-    uint64_t *intval)
+static nvlist_t *
+zfs_validate_properties(libzfs_handle_t *hdl, zfs_type_t type, nvlist_t *nvl,
+    uint64_t zoned, zfs_handle_t *zhp, const char *errbuf)
 {
-	const char *propname = zfs_prop_to_name(prop);
-	uint64_t number;
-	char errbuf[1024];
-	int i;
-
-	/*
-	 * Check to see if this a read-only property.
-	 */
-	if (zfs_prop_readonly(prop))
-		return (zfs_error(hdl, EZFS_PROPREADONLY,
-		    dgettext(TEXT_DOMAIN, "cannot set %s property"), propname));
-
-	(void) snprintf(errbuf, sizeof (errbuf),
-	    dgettext(TEXT_DOMAIN, "bad %s value '%s'"), propname, value);
-
-	/* See if the property value is too long */
-	if (strlen(value) >= ZFS_MAXPROPLEN) {
-		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "value is too long"));
-		return (zfs_error(hdl, EZFS_BADPROP, errbuf));
+	nvpair_t *elem;
+	const char *propname;
+	zfs_prop_t prop;
+	uint64_t intval;
+	char *strval;
+	nvlist_t *ret;
+
+	if (nvlist_alloc(&ret, NV_UNIQUE_NAME, 0) != 0) {
+		(void) no_memory(hdl);
+		return (NULL);
+	}
+
+	if (type == ZFS_TYPE_SNAPSHOT) {
+		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+		    "snaphot properties cannot be modified"));
+		(void) zfs_error(hdl, EZFS_PROPTYPE, errbuf);
+		goto error;
 	}
 
-	/* Perform basic checking based on property type */
-	switch (zfs_prop_get_type(prop)) {
-	case prop_type_boolean:
-		if (strcmp(value, "on") == 0) {
-			number = 1;
-		} else if (strcmp(value, "off") == 0) {
-			number = 0;
-		} else {
-			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
-			    "must be 'on' or 'off'"));
-			return (zfs_error(hdl, EZFS_BADPROP, errbuf));
+	elem = NULL;
+	while ((elem = nvlist_next_nvpair(nvl, elem)) != NULL) {
+		propname = nvpair_name(elem);
+
+		/*
+		 * Make sure this property is valid and applies to this type.
+		 */
+		if ((prop = zfs_name_to_prop(propname)) == ZFS_PROP_INVAL) {
+			if (!zfs_prop_user(propname)) {
+				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+				    "invalid property '%s'"),
+				    propname);
+				(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
+				goto error;
+			} else {
+				/*
+				 * If this is a user property, make sure it's a
+				 * string, and that it's less than
+				 * ZAP_MAXNAMELEN.
+				 */
+				if (nvpair_type(elem) != DATA_TYPE_STRING) {
+					zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+					    "'%s' must be a string"),
+					    propname);
+					(void) zfs_error(hdl, EZFS_BADPROP,
+					    errbuf);
+					goto error;
+				}
+
+				if (strlen(nvpair_name(elem)) >=
+				    ZAP_MAXNAMELEN) {
+					zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+					    "property name '%s' is too long"),
+					    propname);
+					(void) zfs_error(hdl, EZFS_BADPROP,
+					    errbuf);
+					goto error;
+				}
+			}
+
+			(void) nvpair_value_string(elem, &strval);
+			if (nvlist_add_string(ret, propname, strval) != 0) {
+				(void) no_memory(hdl);
+				goto error;
+			}
+			continue;
 		}
-		break;
-
-	case prop_type_number:
-		/* treat 'none' as 0 */
-		if (strcmp(value, "none") == 0) {
-			number = 0;
+
+		/*
+		 * Normalize the name, to get rid of shorthand abbrevations.
+		 */
+		propname = zfs_prop_to_name(prop);
+
+		if (!zfs_prop_valid_for_type(prop, type)) {
+			zfs_error_aux(hdl,
+			    dgettext(TEXT_DOMAIN, "'%s' does not "
+			    "apply to datasets of this type"), propname);
+			(void) zfs_error(hdl, EZFS_PROPTYPE, errbuf);
+			goto error;
+		}
+
+		if (zfs_prop_readonly(prop) &&
+		    (prop != ZFS_PROP_VOLBLOCKSIZE || zhp != NULL)) {
+			zfs_error_aux(hdl,
+			    dgettext(TEXT_DOMAIN, "'%s' is readonly"),
+			    propname);
+			(void) zfs_error(hdl, EZFS_PROPREADONLY, errbuf);
+			goto error;
+		}
+
+		/*
+		 * Convert any properties to the internal DSL value types.
+		 */
+		strval = NULL;
+		switch (zfs_prop_get_type(prop)) {
+		case prop_type_boolean:
+			if (prop_parse_boolean(hdl, elem, &intval) != 0) {
+				(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
+				goto error;
+			}
 			break;
-		}
-
-		if (nicestrtonum(hdl, value, &number) != 0)
-			return (zfs_error(hdl, EZFS_BADPROP, errbuf));
-
-		/* don't allow 0 for quota, use 'none' instead */
-		if (prop == ZFS_PROP_QUOTA && number == 0 &&
-		    strcmp(value, "none") != 0) {
-			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
-			    "use 'quota=none' to disable"));
-			return (zfs_error(hdl, EZFS_BADPROP, errbuf));
-		}
-
-		/* must be power of two within SPA_{MIN,MAX}BLOCKSIZE */
-		if (prop == ZFS_PROP_RECORDSIZE ||
-		    prop == ZFS_PROP_VOLBLOCKSIZE) {
-			if (number < SPA_MINBLOCKSIZE ||
-			    number > SPA_MAXBLOCKSIZE || !ISP2(number)) {
+
+		case prop_type_string:
+			if (nvpair_type(elem) != DATA_TYPE_STRING) {
 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
-				    "must be power of 2 from %u to %uk"),
-				    (uint_t)SPA_MINBLOCKSIZE,
-				    (uint_t)SPA_MAXBLOCKSIZE >> 10);
-				return (zfs_error(hdl, EZFS_BADPROP, errbuf));
+				    "'%s' must be a string"),
+				    propname);
+				(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
+				goto error;
 			}
-		}
-
-		break;
-
-	case prop_type_string:
-	case prop_type_index:
-		/*
-		 * The two writable string values, 'mountpoint' and
-		 * 'checksum' need special consideration.  The 'index' types are
-		 * specified as strings by the user, but passed to the kernel as
-		 * integers.
-		 */
-		switch (prop) {
-		case ZFS_PROP_MOUNTPOINT:
-			if (strcmp(value, ZFS_MOUNTPOINT_NONE) == 0 ||
-			    strcmp(value, ZFS_MOUNTPOINT_LEGACY) == 0)
-				break;
-
-			if (value[0] != '/') {
+			(void) nvpair_value_string(elem, &strval);
+			if (strlen(strval) >= ZFS_MAXPROPLEN) {
 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
-				    "must be an absolute path, 'none', or "
-				    "'legacy'"));
-				return (zfs_error(hdl, EZFS_BADPROP, errbuf));
+				    "'%s' is too long"), propname);
+				(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
+				goto error;
+			}
+			break;
+
+		case prop_type_number:
+			if (prop_parse_number(hdl, elem, prop, &intval) != 0) {
+				(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
+				goto error;
+			}
+			break;
+
+		case prop_type_index:
+			if (prop_parse_index(hdl, elem, prop, &intval) != 0) {
+				(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
+				goto error;
 			}
 			break;
 
-		case ZFS_PROP_CHECKSUM:
-			for (i = 0; checksum_table[i].name != NULL; i++) {
-				if (strcmp(value, checksum_table[i].name)
-				    == 0) {
-					number = checksum_table[i].value;
-					break;
-				}
+		default:
+			abort();
+		}
+
+		/*
+		 * Add the result to our return set of properties.
+		 */
+		if (strval) {
+			if (nvlist_add_string(ret, propname, strval) != 0) {
+				(void) no_memory(hdl);
+				goto error;
 			}
-
-			if (checksum_table[i].name == NULL) {
+		} else if (nvlist_add_uint64(ret, propname, intval) != 0) {
+			(void) no_memory(hdl);
+			goto error;
+		}
+
+		/*
+		 * Perform some additional checks for specific properties.
+		 */
+		switch (prop) {
+		case ZFS_PROP_RECORDSIZE:
+		case ZFS_PROP_VOLBLOCKSIZE:
+			/* must be power of two within SPA_{MIN,MAX}BLOCKSIZE */
+			if (intval < SPA_MINBLOCKSIZE ||
+			    intval > SPA_MAXBLOCKSIZE || !ISP2(intval)) {
 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
-				    "must be 'on', 'off', 'fletcher2', "
-				    "'fletcher4', or 'sha256'"));
-				return (zfs_error(hdl, EZFS_BADPROP, errbuf));
+				    "'%s' must be power of 2 from %u "
+				    "to %uk"), propname,
+				    (uint_t)SPA_MINBLOCKSIZE,
+				    (uint_t)SPA_MAXBLOCKSIZE >> 10);
+				(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
+				goto error;
 			}
 			break;
 
-		case ZFS_PROP_COMPRESSION:
-			for (i = 0; compress_table[i].name != NULL; i++) {
-				if (strcmp(value, compress_table[i].name)
-				    == 0) {
-					number = compress_table[i].value;
-					break;
-				}
-			}
-
-			if (compress_table[i].name == NULL) {
+		case ZFS_PROP_MOUNTPOINT:
+			if (strcmp(strval, ZFS_MOUNTPOINT_NONE) == 0 ||
+			    strcmp(strval, ZFS_MOUNTPOINT_LEGACY) == 0)
+				break;
+
+			if (strval[0] != '/') {
 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
-				    "must be 'on', 'off', or 'lzjb'"));
-				return (zfs_error(hdl, EZFS_BADPROP, errbuf));
-			}
-			break;
-
-		case ZFS_PROP_SNAPDIR:
-			for (i = 0; snapdir_table[i].name != NULL; i++) {
-				if (strcmp(value, snapdir_table[i].name) == 0) {
-					number = snapdir_table[i].value;
-					break;
-				}
-			}
-
-			if (snapdir_table[i].name == NULL) {
-				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
-				    "must be 'hidden' or 'visible'"));
-				return (zfs_error(hdl, EZFS_BADPROP, errbuf));
+				    "'%s' must be an absolute path, "
+				    "'none', or 'legacy'"), propname);
+				(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
+				goto error;
 			}
 			break;
-
-		case ZFS_PROP_ACLMODE:
-			for (i = 0; acl_mode_table[i].name != NULL; i++) {
-				if (strcmp(value, acl_mode_table[i].name)
-				    == 0) {
-					number = acl_mode_table[i].value;
-					break;
+		}
+
+		/*
+		 * For the mountpoint and sharenfs properties, check if it can
+		 * be set in a global/non-global zone based on the zoned
+		 * property value:
+		 *
+		 *		global zone	    non-global zone
+		 * -----------------------------------------------------
+		 * zoned=on	mountpoint (no)	    mountpoint (yes)
+		 *		sharenfs (no)	    sharenfs (no)
+		 *
+		 * zoned=off	mountpoint (yes)	N/A
+		 *		sharenfs (yes)
+		 */
+		if (prop == ZFS_PROP_MOUNTPOINT || prop == ZFS_PROP_SHARENFS) {
+			if (zoned) {
+				if (getzoneid() == GLOBAL_ZONEID) {
+					zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+					    "'%s' cannot be set on "
+					    "dataset in a non-global zone"),
+					    propname);
+					(void) zfs_error(hdl, EZFS_ZONED,
+					    errbuf);
+					goto error;
+				} else if (prop == ZFS_PROP_SHARENFS) {
+					zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+					    "'%s' cannot be set in "
+					    "a non-global zone"), propname);
+					(void) zfs_error(hdl, EZFS_ZONED,
+					    errbuf);
+					goto error;
+				}
+			} else if (getzoneid() != GLOBAL_ZONEID) {
+				/*
+				 * If zoned property is 'off', this must be in
+				 * a globle zone. If not, something is wrong.
+				 */
+				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+				    "'%s' cannot be set while dataset "
+				    "'zoned' property is set"), propname);
+				(void) zfs_error(hdl, EZFS_ZONED, errbuf);
+				goto error;
+			}
+		}
+
+		/*
+		 * For changes to existing volumes, we have some additional
+		 * checks to enforce.
+		 */
+		if (type == ZFS_TYPE_VOLUME && zhp != NULL) {
+			uint64_t volsize = zfs_prop_get_int(zhp,
+			    ZFS_PROP_VOLSIZE);
+			uint64_t blocksize = zfs_prop_get_int(zhp,
+			    ZFS_PROP_VOLBLOCKSIZE);
+			char buf[64];
+
+			switch (prop) {
+			case ZFS_PROP_RESERVATION:
+				if (intval > volsize) {
+					zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+					    "'%s' is greater than current "
+					    "volume size"), propname);
+					(void) zfs_error(hdl, EZFS_BADPROP,
+					    errbuf);
+					goto error;
+				}
+				break;
+
+			case ZFS_PROP_VOLSIZE:
+				if (intval % blocksize != 0) {
+					zfs_nicenum(blocksize, buf,
+					    sizeof (buf));
+					zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+					    "'%s' must be a multiple of "
+					    "volume block size (%s)"),
+					    propname, buf);
+					(void) zfs_error(hdl, EZFS_BADPROP,
+					    errbuf);
+					goto error;
+				}
+
+				if (intval == 0) {
+					zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+					    "'%s' cannot be zero"),
+					    propname);
+					(void) zfs_error(hdl, EZFS_BADPROP,
+					    errbuf);
+					goto error;
 				}
 			}
-
-			if (acl_mode_table[i].name == NULL) {
-				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
-				    "must be 'disacard', 'groupmask', or "
-				    "'passthrough'"));
-				return (zfs_error(hdl, EZFS_BADPROP, errbuf));
-			}
-			break;
-
-		case ZFS_PROP_ACLINHERIT:
-			for (i = 0; acl_inherit_table[i].name != NULL; i++) {
-				if (strcmp(value, acl_inherit_table[i].name)
-				    == 0) {
-					number = acl_inherit_table[i].value;
-					break;
-				}
-			}
-
-			if (acl_inherit_table[i].name == NULL) {
-				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
-				    "must be 'discard, 'noallow', 'secure', "
-				    "or 'passthrough'"));
-				return (zfs_error(hdl, EZFS_BADPROP, errbuf));
-			}
-			break;
-
-		case ZFS_PROP_SHARENFS:
-			/*
-			 * Nothing to do for 'sharenfs', this gets passed on to
-			 * share(1M) verbatim.
-			 */
-			break;
 		}
 	}
 
-	if (intval != NULL)
-		*intval = number;
-
-	return (0);
+	/*
+	 * If this is an existing volume, and someone is setting the volsize,
+	 * make sure that it matches the reservation, or add it if necessary.
+	 */
+	if (zhp != NULL && type == ZFS_TYPE_VOLUME &&
+	    nvlist_lookup_uint64(ret, zfs_prop_to_name(ZFS_PROP_VOLSIZE),
+	    &intval) == 0) {
+		uint64_t old_volsize = zfs_prop_get_int(zhp,
+		    ZFS_PROP_VOLSIZE);
+		uint64_t old_reservation = zfs_prop_get_int(zhp,
+		    ZFS_PROP_RESERVATION);
+		uint64_t new_reservation;
+
+		if (old_volsize == old_reservation &&
+		    nvlist_lookup_uint64(ret,
+		    zfs_prop_to_name(ZFS_PROP_RESERVATION),
+		    &new_reservation) != 0) {
+			if (nvlist_add_uint64(ret,
+			    zfs_prop_to_name(ZFS_PROP_RESERVATION),
+			    intval) != 0) {
+				(void) no_memory(hdl);
+				goto error;
+			}
+		}
+	}
+
+	return (ret);
+
+error:
+	nvlist_free(ret);
+	return (NULL);
 }
 
 /*
  * Given a property name and value, set the property for the given dataset.
  */
 int
-zfs_prop_set(zfs_handle_t *zhp, zfs_prop_t prop, const char *propval)
+zfs_prop_set(zfs_handle_t *zhp, const char *propname, const char *propval)
 {
-	const char *propname = zfs_prop_to_name(prop);
-	uint64_t number;
 	zfs_cmd_t zc = { 0 };
-	int ret;
-	prop_changelist_t *cl;
+	int ret = -1;
+	prop_changelist_t *cl = NULL;
 	char errbuf[1024];
 	libzfs_handle_t *hdl = zhp->zfs_hdl;
-
-	if (zfs_prop_validate(zhp->zfs_hdl, prop, propval, &number) != 0)
-		return (-1);
-
+	nvlist_t *nvl = NULL, *realprops;
+	zfs_prop_t prop;
 
 	(void) snprintf(errbuf, sizeof (errbuf),
-	    dgettext(TEXT_DOMAIN, "cannot set %s for '%s'"), propname,
+	    dgettext(TEXT_DOMAIN, "cannot set property for '%s'"),
 	    zhp->zfs_name);
 
-	/*
-	 * Check to see if the value applies to this type
-	 */
-	if (!zfs_prop_valid_for_type(prop, zhp->zfs_type))
-		return (zfs_error(hdl, EZFS_PROPTYPE, errbuf));
-
-	/*
-	 * For the mountpoint and sharenfs properties, check if it can be set
-	 * in a global/non-global zone based on the zoned property value:
-	 *
-	 *		global zone	    non-global zone
-	 * -----------------------------------------------------
-	 * zoned=on	mountpoint (no)	    mountpoint (yes)
-	 *		sharenfs (no)	    sharenfs (no)
-	 *
-	 * zoned=off	mountpoint (yes)	N/A
-	 *		sharenfs (yes)
-	 */
-	if (prop == ZFS_PROP_MOUNTPOINT || prop == ZFS_PROP_SHARENFS) {
-		if (zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) {
-			if (getzoneid() == GLOBAL_ZONEID) {
-				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
-				    "dataset is used in a non-global zone"));
-				return (zfs_error(hdl, EZFS_ZONED, errbuf));
-			} else if (prop == ZFS_PROP_SHARENFS) {
-				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
-				    "filesystems cannot be shared in a "
-				    "non-global zone"));
-				return (zfs_error(hdl, EZFS_ZONED, errbuf));
-			}
-		} else if (getzoneid() != GLOBAL_ZONEID) {
-			/*
-			 * If zoned property is 'off', this must be in
-			 * a globle zone. If not, something is wrong.
-			 */
-			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
-			    "dataset is used in a non-global zone, but "
-			    "'zoned' property is not set"));
-			return (zfs_error(hdl, EZFS_ZONED, errbuf));
-		}
+	if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0 ||
+	    nvlist_add_string(nvl, propname, propval) != 0) {
+		(void) no_memory(hdl);
+		goto error;
 	}
 
+	if ((realprops = zfs_validate_properties(hdl, zhp->zfs_type, nvl,
+	    zfs_prop_get_int(zhp, ZFS_PROP_ZONED), zhp, errbuf)) == NULL)
+		goto error;
+	nvlist_free(nvl);
+	nvl = realprops;
+
+	prop = zfs_name_to_prop(propname);
+
 	if ((cl = changelist_gather(zhp, prop, 0)) == NULL)
-		return (-1);
+		goto error;
 
 	if (prop == ZFS_PROP_MOUNTPOINT && changelist_haszonedchild(cl)) {
 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
@@ -824,48 +1027,10 @@
 	 */
 	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
 
-	switch (prop) {
-	case ZFS_PROP_QUOTA:
-		zc.zc_cookie = number;
-		ret = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_SET_QUOTA, &zc);
-		break;
-	case ZFS_PROP_RESERVATION:
-		zc.zc_cookie = number;
-		ret = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_SET_RESERVATION,
-		    &zc);
-		break;
-	case ZFS_PROP_MOUNTPOINT:
-	case ZFS_PROP_SHARENFS:
-		/*
-		 * These properties are passed down as real strings.
-		 */
-		(void) strlcpy(zc.zc_prop_name, propname,
-		    sizeof (zc.zc_prop_name));
-		(void) strlcpy(zc.zc_prop_value, propval,
-		    sizeof (zc.zc_prop_value));
-		zc.zc_intsz = 1;
-		zc.zc_numints = strlen(propval) + 1;
-		ret = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_SET_PROP, &zc);
-		break;
-	case ZFS_PROP_VOLSIZE:
-		zc.zc_volsize = number;
-		ret = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_SET_VOLSIZE, &zc);
-		break;
-	case ZFS_PROP_VOLBLOCKSIZE:
-		zc.zc_volblocksize = number;
-		ret = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_SET_VOLBLOCKSIZE,
-		    &zc);
-		break;
-	default:
-		(void) strlcpy(zc.zc_prop_name, propname,
-		    sizeof (zc.zc_prop_name));
-		/* LINTED - alignment */
-		*(uint64_t *)zc.zc_prop_value = number;
-		zc.zc_intsz = 8;
-		zc.zc_numints = 1;
-		ret = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_SET_PROP, &zc);
-		break;
-	}
+	if (zcmd_write_src_nvlist(hdl, &zc, nvl, NULL) != 0)
+		goto error;
+
+	ret = ioctl(hdl->libzfs_fd, ZFS_IOC_SET_PROP, &zc);
 
 	if (ret != 0) {
 		switch (errno) {
@@ -900,7 +1065,7 @@
 			if (prop == ZFS_PROP_VOLBLOCKSIZE)
 				(void) zfs_error(hdl, EZFS_VOLHASDATA, errbuf);
 			else
-				return (zfs_standard_error(hdl, EBUSY, errbuf));
+				(void) zfs_standard_error(hdl, EBUSY, errbuf);
 			break;
 
 		case EROFS:
@@ -926,14 +1091,15 @@
 		 * Refresh the statistics so the new property value
 		 * is reflected.
 		 */
-		if ((ret = changelist_postfix(cl)) != 0)
-			goto error;
-
-		(void) get_stats(zhp);
+		if ((ret = changelist_postfix(cl)) == 0)
+			(void) get_stats(zhp);
 	}
 
 error:
-	changelist_free(cl);
+	nvlist_free(nvl);
+	zcmd_free_nvlists(&zc);
+	if (cl)
+		changelist_free(cl);
 	return (ret);
 }
 
@@ -941,18 +1107,39 @@
  * Given a property, inherit the value from the parent dataset.
  */
 int
-zfs_prop_inherit(zfs_handle_t *zhp, zfs_prop_t prop)
+zfs_prop_inherit(zfs_handle_t *zhp, const char *propname)
 {
-	const char *propname = zfs_prop_to_name(prop);
 	zfs_cmd_t zc = { 0 };
 	int ret;
 	prop_changelist_t *cl;
 	libzfs_handle_t *hdl = zhp->zfs_hdl;
 	char errbuf[1024];
+	zfs_prop_t prop;
 
 	(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
 	    "cannot inherit %s for '%s'"), propname, zhp->zfs_name);
 
+	if ((prop = zfs_name_to_prop(propname)) == ZFS_PROP_INVAL) {
+		/*
+		 * For user properties, the amount of work we have to do is very
+		 * small, so just do it here.
+		 */
+		if (!zfs_prop_user(propname)) {
+			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+			    "invalid property"));
+			return (zfs_error(hdl, EZFS_BADPROP, errbuf));
+		}
+
+		(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
+		(void) strlcpy(zc.zc_value, propname, sizeof (zc.zc_value));
+
+		if (ioctl(zhp->zfs_hdl->libzfs_fd,
+		    ZFS_IOC_SET_PROP, &zc) != 0)
+			return (zfs_standard_error(hdl, errno, errbuf));
+
+		return (0);
+	}
+
 	/*
 	 * Verify that this property is inheritable.
 	 */
@@ -969,7 +1156,7 @@
 		return (zfs_error(hdl, EZFS_PROPTYPE, errbuf));
 
 	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
-	(void) strlcpy(zc.zc_prop_name, propname, sizeof (zc.zc_prop_name));
+	(void) strlcpy(zc.zc_value, propname, sizeof (zc.zc_value));
 
 	if (prop == ZFS_PROP_MOUNTPOINT && getzoneid() == GLOBAL_ZONEID &&
 	    zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) {
@@ -995,8 +1182,6 @@
 	if ((ret = changelist_prefix(cl)) != 0)
 		goto error;
 
-	zc.zc_numints = 0;
-
 	if ((ret = ioctl(zhp->zfs_hdl->libzfs_fd,
 	    ZFS_IOC_SET_PROP, &zc)) != 0) {
 		return (zfs_standard_error(hdl, errno, errbuf));
@@ -1011,7 +1196,6 @@
 		(void) get_stats(zhp);
 	}
 
-
 error:
 	changelist_free(cl);
 	return (ret);
@@ -1240,11 +1424,11 @@
 		break;
 
 	case ZFS_PROP_VOLSIZE:
-		*val = zhp->zfs_volsize;
+		*val = zhp->zfs_volstats.zv_volsize;
 		break;
 
 	case ZFS_PROP_VOLBLOCKSIZE:
-		*val = zhp->zfs_volblocksize;
+		*val = zhp->zfs_volstats.zv_volblocksize;
 		break;
 
 	case ZFS_PROP_USED:
@@ -1259,6 +1443,10 @@
 		*val = (zhp->zfs_mntopts != NULL);
 		break;
 
+	case ZFS_PROP_CANMOUNT:
+		*val = getprop_uint64(zhp, prop, source);
+		break;
+
 	default:
 		zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
 		    "cannot get non-numeric property"));
@@ -1308,8 +1496,8 @@
 	char *source = NULL;
 	uint64_t val;
 	char *str;
-	int i;
 	const char *root;
+	const char *strval;
 
 	/*
 	 * Check to see if this property applies to our object
@@ -1327,6 +1515,7 @@
 	case ZFS_PROP_ZONED:
 	case ZFS_PROP_DEVICES:
 	case ZFS_PROP_EXEC:
+	case ZFS_PROP_CANMOUNT:
 		/*
 		 * Basic boolean values are built on top of
 		 * get_numeric_property().
@@ -1357,53 +1546,13 @@
 		break;
 
 	case ZFS_PROP_COMPRESSION:
-		val = getprop_uint64(zhp, prop, &source);
-		for (i = 0; compress_table[i].name != NULL; i++) {
-			if (compress_table[i].value == val)
-				break;
-		}
-		assert(compress_table[i].name != NULL);
-		(void) strlcpy(propbuf, compress_table[i].name, proplen);
-		break;
-
 	case ZFS_PROP_CHECKSUM:
-		val = getprop_uint64(zhp, prop, &source);
-		for (i = 0; checksum_table[i].name != NULL; i++) {
-			if (checksum_table[i].value == val)
-				break;
-		}
-		assert(checksum_table[i].name != NULL);
-		(void) strlcpy(propbuf, checksum_table[i].name, proplen);
-		break;
-
 	case ZFS_PROP_SNAPDIR:
-		val = getprop_uint64(zhp, prop, &source);
-		for (i = 0; snapdir_table[i].name != NULL; i++) {
-			if (snapdir_table[i].value == val)
-				break;
-		}
-		assert(snapdir_table[i].name != NULL);
-		(void) strlcpy(propbuf, snapdir_table[i].name, proplen);
-		break;
-
 	case ZFS_PROP_ACLMODE:
-		val = getprop_uint64(zhp, prop, &source);
-		for (i = 0; acl_mode_table[i].name != NULL; i++) {
-			if (acl_mode_table[i].value == val)
-				break;
-		}
-		assert(acl_mode_table[i].name != NULL);
-		(void) strlcpy(propbuf, acl_mode_table[i].name, proplen);
-		break;
-
 	case ZFS_PROP_ACLINHERIT:
 		val = getprop_uint64(zhp, prop, &source);
-		for (i = 0; acl_inherit_table[i].name != NULL; i++) {
-			if (acl_inherit_table[i].value == val)
-				break;
-		}
-		assert(acl_inherit_table[i].name != NULL);
-		(void) strlcpy(propbuf, acl_inherit_table[i].name, proplen);
+		verify(zfs_prop_index_to_string(prop, val, &strval) == 0);
+		(void) strlcpy(propbuf, strval, proplen);
 		break;
 
 	case ZFS_PROP_CREATION:
@@ -1737,10 +1886,12 @@
 }
 
 /*
- * Checks to make sure that the given path has a parent, and that it exists.
+ * Checks to make sure that the given path has a parent, and that it exists.  We
+ * also fetch the 'zoned' property, which is used to validate property settings
+ * when creating new datasets.
  */
 static int
-check_parents(libzfs_handle_t *hdl, const char *path)
+check_parents(libzfs_handle_t *hdl, const char *path, uint64_t *zoned)
 {
 	zfs_cmd_t zc = { 0 };
 	char parent[ZFS_MAXNAMELEN];
@@ -1783,9 +1934,9 @@
 		}
 	}
 
+	*zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED);
 	/* we are in a non-global zone, but parent is in the global zone */
-	if (getzoneid() != GLOBAL_ZONEID &&
-	    !zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) {
+	if (getzoneid() != GLOBAL_ZONEID && !(*zoned)) {
 		(void) zfs_standard_error(hdl, EPERM, errbuf);
 		zfs_close(zhp);
 		return (-1);
@@ -1805,29 +1956,18 @@
 }
 
 /*
- * Create a new filesystem or volume.  'sizestr' and 'blocksizestr' are used
- * only for volumes, and indicate the size and blocksize of the volume.
+ * Create a new filesystem or volume.
  */
 int
 zfs_create(libzfs_handle_t *hdl, const char *path, zfs_type_t type,
-	const char *sizestr, const char *blocksizestr)
+    nvlist_t *props)
 {
 	zfs_cmd_t zc = { 0 };
 	int ret;
 	uint64_t size = 0;
 	uint64_t blocksize = zfs_prop_default_numeric(ZFS_PROP_VOLBLOCKSIZE);
 	char errbuf[1024];
-
-	/* convert sizestr into integer size */
-	if (sizestr != NULL && nicestrtonum(hdl, sizestr, &size) != 0)
-		return (zfs_error(hdl, EZFS_BADPROP, dgettext(TEXT_DOMAIN,
-		    "bad volume size '%s'"), sizestr));
-
-	/* convert blocksizestr into integer blocksize */
-	if (blocksizestr != NULL && nicestrtonum(hdl, blocksizestr,
-	    &blocksize) != 0)
-		return (zfs_error(hdl, EZFS_BADPROP, dgettext(TEXT_DOMAIN,
-		    "bad volume blocksize '%s'"), blocksizestr));
+	uint64_t zoned;
 
 	(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
 	    "cannot create '%s'"), path);
@@ -1837,7 +1977,7 @@
 		return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
 
 	/* validate parents exist */
-	if (check_parents(hdl, path) != 0)
+	if (check_parents(hdl, path, &zoned) != 0)
 		return (-1);
 
 	/*
@@ -1859,6 +1999,10 @@
 	else
 		zc.zc_objset_type = DMU_OST_ZFS;
 
+	if (props && (props = zfs_validate_properties(hdl, type, props, zoned,
+	    NULL, errbuf)) == 0)
+		return (-1);
+
 	if (type == ZFS_TYPE_VOLUME) {
 		/*
 		 * If we are creating a volume, the size and block size must
@@ -1867,43 +2011,57 @@
 		 * volsize must be a multiple of the block size, and cannot be
 		 * zero.
 		 */
-		if (size == 0) {
+		if (props == NULL || nvlist_lookup_uint64(props,
+		    zfs_prop_to_name(ZFS_PROP_VOLSIZE), &size) != 0) {
+			nvlist_free(props);
 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
-			    "cannot be zero"));
-			return (zfs_error(hdl, EZFS_BADPROP,
-			    dgettext(TEXT_DOMAIN, "bad volume size '%s'"),
-			    sizestr));
+			    "missing volume size"));
+			return (zfs_error(hdl, EZFS_BADPROP, errbuf));
 		}
 
-		if (blocksize < SPA_MINBLOCKSIZE ||
-		    blocksize > SPA_MAXBLOCKSIZE || !ISP2(blocksize)) {
+		if ((ret = nvlist_lookup_uint64(props,
+		    zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE),
+		    &blocksize)) != 0) {
+			if (ret == ENOENT) {
+				blocksize = zfs_prop_default_numeric(
+				    ZFS_PROP_VOLBLOCKSIZE);
+			} else {
+				nvlist_free(props);
+				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+				    "missing volume block size"));
+				return (zfs_error(hdl, EZFS_BADPROP, errbuf));
+			}
+		}
+
+		if (size == 0) {
+			nvlist_free(props);
 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
-			    "must be power of 2 from %u to %uk"),
-			    (uint_t)SPA_MINBLOCKSIZE,
-			    (uint_t)SPA_MAXBLOCKSIZE >> 10);
-			return (zfs_error(hdl, EZFS_BADPROP,
-			    dgettext(TEXT_DOMAIN,
-			    "bad volume block size '%s'"), blocksizestr));
+			    "volume size cannot be zero"));
+			return (zfs_error(hdl, EZFS_BADPROP, errbuf));
 		}
 
 		if (size % blocksize != 0) {
+			nvlist_free(props);
 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
-			    "must be a multiple of volume block size"));
-			return (zfs_error(hdl, EZFS_BADPROP,
-			    dgettext(TEXT_DOMAIN, "bad volume size '%s'"),
-			    sizestr));
+			    "volume size must be a multiple of volume block "
+			    "size"));
+			return (zfs_error(hdl, EZFS_BADPROP, errbuf));
 		}
-
-		zc.zc_volsize = size;
-		zc.zc_volblocksize = blocksize;
 	}
 
+	if (props &&
+	    zcmd_write_src_nvlist(hdl, &zc, props, NULL) != 0)
+		return (-1);
+	nvlist_free(props);
+
 	/* create the dataset */
 	ret = ioctl(hdl->libzfs_fd, ZFS_IOC_CREATE, &zc);
 
 	if (ret == 0 && type == ZFS_TYPE_VOLUME)
 		ret = zvol_create_link(hdl, path);
 
+	zcmd_free_nvlists(&zc);
+
 	/* check for failure */
 	if (ret != 0) {
 		char parent[ZFS_MAXNAMELEN];
@@ -1922,13 +2080,12 @@
 
 		case EDOM:
 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
-			    "must be power of 2 from %u to %uk"),
+			    "volume block size must be power of 2 from "
+			    "%u to %uk"),
 			    (uint_t)SPA_MINBLOCKSIZE,
 			    (uint_t)SPA_MAXBLOCKSIZE >> 10);
 
-			return (zfs_error(hdl, EZFS_BADPROP,
-			    dgettext(TEXT_DOMAIN, "bad block size '%s'"),
-			    blocksizestr ? blocksizestr : "<unknown>"));
+			return (zfs_error(hdl, EZFS_BADPROP, errbuf));
 
 #ifdef _ILP32
 		case EOVERFLOW:
@@ -1960,11 +2117,7 @@
 
 	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
 
-	/*
-	 * We use the check for 'zfs_volblocksize' instead of ZFS_TYPE_VOLUME
-	 * so that we do the right thing for snapshots of volumes.
-	 */
-	if (zhp->zfs_volblocksize != 0) {
+	if (ZFS_IS_VOLUME(zhp)) {
 		if (zvol_remove_link(zhp->zfs_hdl, zhp->zfs_name) != 0)
 			return (-1);
 
@@ -1997,9 +2150,9 @@
 	zfs_handle_t *szhp;
 	char name[ZFS_MAXNAMELEN];
 
-	(void) strcpy(name, zhp->zfs_name);
-	(void) strcat(name, "@");
-	(void) strcat(name, dd->snapname);
+	(void) strlcpy(name, zhp->zfs_name, sizeof (name));
+	(void) strlcat(name, "@", sizeof (name));
+	(void) strlcat(name, dd->snapname, sizeof (name));
 
 	szhp = make_dataset_handle(zhp->zfs_hdl, name);
 	if (szhp) {
@@ -2039,7 +2192,7 @@
 	}
 
 	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
-	(void) strlcpy(zc.zc_prop_value, snapname, sizeof (zc.zc_prop_value));
+	(void) strlcpy(zc.zc_value, snapname, sizeof (zc.zc_value));
 
 	ret = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_DESTROY_SNAPS, &zc);
 	if (ret != 0) {
@@ -2067,13 +2220,15 @@
  * Clones the given dataset.  The target must be of the same type as the source.
  */
 int
-zfs_clone(zfs_handle_t *zhp, const char *target)
+zfs_clone(zfs_handle_t *zhp, const char *target, nvlist_t *props)
 {
 	zfs_cmd_t zc = { 0 };
 	char parent[ZFS_MAXNAMELEN];
 	int ret;
 	char errbuf[1024];
 	libzfs_handle_t *hdl = zhp->zfs_hdl;
+	zfs_type_t type;
+	uint64_t zoned;
 
 	assert(zhp->zfs_type == ZFS_TYPE_SNAPSHOT);
 
@@ -2085,21 +2240,39 @@
 		return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
 
 	/* validate parents exist */
-	if (check_parents(zhp->zfs_hdl, target) != 0)
+	if (check_parents(hdl, target, &zoned) != 0)
 		return (-1);
 
 	(void) parent_name(target, parent, sizeof (parent));
 
 	/* do the clone */
-	if (zhp->zfs_volblocksize != 0)
+	if (ZFS_IS_VOLUME(zhp)) {
 		zc.zc_objset_type = DMU_OST_ZVOL;
-	else
+		type = ZFS_TYPE_FILESYSTEM;
+	} else {
 		zc.zc_objset_type = DMU_OST_ZFS;
+		type = ZFS_TYPE_VOLUME;
+	}
+
+	if (props) {
+		if ((props = zfs_validate_properties(hdl, type, props, zoned,
+		    zhp, errbuf)) == NULL)
+			return (-1);
+
+		if (zcmd_write_src_nvlist(hdl, &zc, props, NULL) != 0) {
+			nvlist_free(props);
+			return (-1);
+		}
+
+		nvlist_free(props);
+	}
 
 	(void) strlcpy(zc.zc_name, target, sizeof (zc.zc_name));
-	(void) strlcpy(zc.zc_filename, zhp->zfs_name, sizeof (zc.zc_filename));
+	(void) strlcpy(zc.zc_value, zhp->zfs_name, sizeof (zc.zc_value));
 	ret = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_CREATE, &zc);
 
+	zcmd_free_nvlists(&zc);
+
 	if (ret != 0) {
 		switch (errno) {
 
@@ -2127,7 +2300,7 @@
 			return (zfs_standard_error(zhp->zfs_hdl, errno,
 			    errbuf));
 		}
-	} else if (zhp->zfs_volblocksize != 0) {
+	} else if (ZFS_IS_VOLUME(zhp)) {
 		ret = zvol_create_link(zhp->zfs_hdl, target);
 	}
 
@@ -2153,12 +2326,12 @@
 		return (0);
 
 	/* Remove the device link if it's a zvol. */
-	if (zhp->zfs_volblocksize != 0)
+	if (ZFS_IS_VOLUME(zhp))
 		(void) zvol_remove_link(zhp->zfs_hdl, zhp->zfs_name);
 
 	/* Check for conflicting names */
-	(void) strcpy(snapname, pd->cb_target);
-	(void) strcat(snapname, strchr(zhp->zfs_name, '@'));
+	(void) strlcpy(snapname, pd->cb_target, sizeof (snapname));
+	(void) strlcat(snapname, strchr(zhp->zfs_name, '@'), sizeof (snapname));
 	szhp = make_dataset_handle(zhp->zfs_hdl, snapname);
 	if (szhp != NULL) {
 		zfs_close(szhp);
@@ -2181,7 +2354,7 @@
 		return (0);
 
 	/* Create the device link if it's a zvol. */
-	if (zhp->zfs_volblocksize != 0)
+	if (ZFS_IS_VOLUME(zhp))
 		(void) zvol_create_link(zhp->zfs_hdl, zhp->zfs_name);
 
 	return (0);
@@ -2211,7 +2384,7 @@
 		return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
 	}
 
-	(void) strcpy(parent, zhp->zfs_dmustats.dds_clone_of);
+	(void) strlcpy(parent, zhp->zfs_dmustats.dds_clone_of, sizeof (parent));
 	if (parent[0] == '\0') {
 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 		    "not a cloned filesystem"));
@@ -2240,8 +2413,8 @@
 	}
 
 	/* issue the ioctl */
-	(void) strlcpy(zc.zc_prop_value, zhp->zfs_dmustats.dds_clone_of,
-	    sizeof (zc.zc_prop_value));
+	(void) strlcpy(zc.zc_value, zhp->zfs_dmustats.dds_clone_of,
+	    sizeof (zc.zc_value));
 	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
 	ret = ioctl(hdl->libzfs_fd, ZFS_IOC_PROMOTE, &zc);
 
@@ -2278,13 +2451,14 @@
 zfs_create_link_cb(zfs_handle_t *zhp, void *arg)
 {
 	char *snapname = arg;
+	int ret;
 
 	if (zhp->zfs_type == ZFS_TYPE_VOLUME) {
 		char name[MAXPATHLEN];
 
-		(void) strcpy(name, zhp->zfs_name);
-		(void) strcat(name, "@");
-		(void) strcat(name, snapname);
+		(void) strlcpy(name, zhp->zfs_name, sizeof (name));
+		(void) strlcat(name, "@", sizeof (name));
+		(void) strlcat(name, snapname, sizeof (name));
 		(void) zvol_create_link(zhp->zfs_hdl, name);
 		/*
 		 * NB: this is simply a best-effort.  We don't want to
@@ -2292,7 +2466,12 @@
 		 * the volumes.
 		 */
 	}
-	return (zfs_iter_filesystems(zhp, zfs_create_link_cb, snapname));
+
+	ret = zfs_iter_filesystems(zhp, zfs_create_link_cb, snapname);
+
+	zfs_close(zhp);
+
+	return (ret);
 }
 
 /*
@@ -2329,7 +2508,7 @@
 	}
 
 	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
-	(void) strlcpy(zc.zc_prop_value, delim+1, sizeof (zc.zc_prop_value));
+	(void) strlcpy(zc.zc_value, delim+1, sizeof (zc.zc_value));
 	zc.zc_cookie = recursive;
 	ret = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_SNAPSHOT, &zc);
 
@@ -2338,7 +2517,7 @@
 	 * zc.zc_name.
 	 */
 	(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
-	    "cannot create snapshot '%s@%s'"), zc.zc_name, zc.zc_prop_value);
+	    "cannot create snapshot '%s@%s'"), zc.zc_name, zc.zc_value);
 	if (ret == 0 && recursive) {
 		(void) zfs_iter_filesystems(zhp,
 		    zfs_create_link_cb, (char *)delim+1);
@@ -2377,10 +2556,10 @@
 	/* do the ioctl() */
 	(void) strlcpy(zc.zc_name, zhp_to->zfs_name, sizeof (zc.zc_name));
 	if (zhp_from) {
-		(void) strlcpy(zc.zc_prop_value, zhp_from->zfs_name,
+		(void) strlcpy(zc.zc_value, zhp_from->zfs_name,
 		    sizeof (zc.zc_name));
 	} else {
-		zc.zc_prop_value[0] = '\0';
+		zc.zc_value[0] = '\0';
 	}
 	zc.zc_cookie = STDOUT_FILENO;
 
@@ -2437,7 +2616,7 @@
 	    "cannot receive"));
 
 	/* trim off snapname, if any */
-	(void) strcpy(zc.zc_name, tosnap);
+	(void) strlcpy(zc.zc_name, tosnap, sizeof (zc.zc_name));
 	cp = strchr(zc.zc_name, '@');
 	if (cp)
 		*cp = '\0';
@@ -2477,7 +2656,7 @@
 	/*
 	 * Determine name of destination snapshot.
 	 */
-	(void) strcpy(zc.zc_filename, tosnap);
+	(void) strlcpy(zc.zc_value, tosnap, sizeof (zc.zc_value));
 	if (isprefix) {
 		if (strchr(tosnap, '@') != NULL) {
 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
@@ -2491,8 +2670,8 @@
 		else
 			cp++;
 
-		(void) strcat(zc.zc_filename, "/");
-		(void) strcat(zc.zc_filename, cp);
+		(void) strcat(zc.zc_value, "/");
+		(void) strcat(zc.zc_value, cp);
 	} else if (strchr(tosnap, '@') == NULL) {
 		/*
 		 * they specified just a filesystem; tack on the
@@ -2501,7 +2680,7 @@
 		cp = strchr(drr.drr_u.drr_begin.drr_toname, '@');
 		if (cp == NULL || strlen(tosnap) + strlen(cp) >= MAXNAMELEN)
 			return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
-		(void) strcat(zc.zc_filename, cp);
+		(void) strcat(zc.zc_value, cp);
 	}
 
 	if (drrb->drr_fromguid) {
@@ -2509,7 +2688,7 @@
 		/* incremental backup stream */
 
 		/* do the ioctl to the containing fs */
-		(void) strcpy(zc.zc_name, zc.zc_filename);
+		(void) strlcpy(zc.zc_name, zc.zc_value, sizeof (zc.zc_name));
 		cp = strchr(zc.zc_name, '@');
 		*cp = '\0';
 
@@ -2540,7 +2719,7 @@
 	} else {
 		/* full backup stream */
 
-		(void) strcpy(zc.zc_name, zc.zc_filename);
+		(void) strlcpy(zc.zc_name, zc.zc_value, sizeof (zc.zc_name));
 
 		/* make sure they aren't trying to receive into the root */
 		if (strchr(zc.zc_name, '/') == NULL) {
@@ -2577,7 +2756,7 @@
 
 				opname = dgettext(TEXT_DOMAIN, "create");
 				if (zfs_create(hdl, zc.zc_name,
-				    ZFS_TYPE_FILESYSTEM, NULL, NULL) != 0) {
+				    ZFS_TYPE_FILESYSTEM, NULL) != 0) {
 					if (errno == EEXIST)
 						continue;
 					goto ancestorerr;
@@ -2623,16 +2802,14 @@
 		*cp = '\0';
 	}
 
-	(void) strcpy(zc.zc_prop_value, tosnap);
 	zc.zc_cookie = STDIN_FILENO;
-	zc.zc_intsz = isprefix;
-	zc.zc_numints = force;
+	zc.zc_guid = force;
 	if (verbose) {
 		(void) printf("%s %s stream of %s into %s\n",
 		    dryrun ? "would receive" : "receiving",
 		    drrb->drr_fromguid ? "incremental" : "full",
 		    drr.drr_u.drr_begin.drr_toname,
-		    zc.zc_filename);
+		    zc.zc_value);
 		(void) fflush(stdout);
 	}
 	if (dryrun)
@@ -2655,13 +2832,13 @@
 		case EEXIST:
 			if (drrb->drr_fromguid == 0) {
 				/* it's the containing fs that exists */
-				cp = strchr(zc.zc_filename, '@');
+				cp = strchr(zc.zc_value, '@');
 				*cp = '\0';
 			}
 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 			    "destination already exists"));
 			(void) zfs_error(hdl, EZFS_EXISTS, dgettext(TEXT_DOMAIN,
-			    "cannot restore to %s"), zc.zc_filename);
+			    "cannot restore to %s"), zc.zc_value);
 			break;
 		case EINVAL:
 			(void) zfs_error(hdl, EZFS_BADSTREAM, errbuf);
@@ -2683,12 +2860,12 @@
 	 * created). Also mount any children of the target filesystem
 	 * if we did an incremental receive.
 	 */
-	cp = strchr(zc.zc_filename, '@');
+	cp = strchr(zc.zc_value, '@');
 	if (cp && (ioctl_err == 0 || drrb->drr_fromguid)) {
 		zfs_handle_t *h;
 
 		*cp = '\0';
-		h = zfs_open(hdl, zc.zc_filename,
+		h = zfs_open(hdl, zc.zc_value,
 		    ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);
 		*cp = '@';
 		if (h) {
@@ -2696,7 +2873,7 @@
 				err = zvol_create_link(hdl, h->zfs_name);
 				if (err == 0 && ioctl_err == 0)
 					err = zvol_create_link(hdl,
-					    zc.zc_filename);
+					    zc.zc_value);
 			} else {
 				if (drrb->drr_fromguid) {
 					err = changelist_postfix(clp);
@@ -2794,7 +2971,7 @@
 
 	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
 
-	if (zhp->zfs_volblocksize != 0)
+	if (ZFS_IS_VOLUME(zhp))
 		zc.zc_objset_type = DMU_OST_ZVOL;
 	else
 		zc.zc_objset_type = DMU_OST_ZFS;
@@ -2973,8 +3150,10 @@
 	} else {
 		if (!zfs_validate_name(hdl, target, zhp->zfs_type))
 			return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
+		uint64_t unused;
+
 		/* validate parents */
-		if (check_parents(hdl, target) != 0)
+		if (check_parents(hdl, target, &unused) != 0)
 			return (-1);
 
 		(void) parent_name(target, parent, sizeof (parent));
@@ -3015,21 +3194,20 @@
 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 		    "child dataset with inherited mountpoint is used "
 		    "in a non-global zone"));
-		ret = zfs_error(hdl, EZFS_ZONED, errbuf);
+		(void) zfs_error(hdl, EZFS_ZONED, errbuf);
 		goto error;
 	}
 
 	if ((ret = changelist_prefix(cl)) != 0)
 		goto error;
 
-	if (zhp->zfs_volblocksize != 0)
+	if (ZFS_IS_VOLUME(zhp))
 		zc.zc_objset_type = DMU_OST_ZVOL;
 	else
 		zc.zc_objset_type = DMU_OST_ZFS;
 
 	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
-	(void) strlcpy(zc.zc_prop_value, target, sizeof (zc.zc_prop_value));
-
+	(void) strlcpy(zc.zc_value, target, sizeof (zc.zc_value));
 
 	if ((ret = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_RENAME, &zc)) != 0) {
 		(void) zfs_standard_error(zhp->zfs_hdl, errno, errbuf);
@@ -3128,3 +3306,259 @@
 
 	return (0);
 }
+
+nvlist_t *
+zfs_get_user_props(zfs_handle_t *zhp)
+{
+	return (zhp->zfs_user_props);
+}
+
+/*
+ * Given a comma-separated list of properties, contruct a property list
+ * containing both user-defined and native properties.  This function will
+ * return a NULL list if 'all' is specified, which can later be expanded on a
+ * per-dataset basis by zfs_expand_proplist().
+ */
+int
+zfs_get_proplist(libzfs_handle_t *hdl, char *fields, zfs_proplist_t **listp)
+{
+	int i;
+	size_t len;
+	char *s, *p;
+	char c;
+	zfs_prop_t prop;
+	zfs_proplist_t *entry;
+	zfs_proplist_t **last;
+
+	*listp = NULL;
+	last = listp;
+
+	/*
+	 * If 'all' is specified, return a NULL list.
+	 */
+	if (strcmp(fields, "all") == 0)
+		return (0);
+
+	/*
+	 * If no fields were specified, return an error.
+	 */
+	if (fields[0] == '\0') {
+		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+		    "no properties specified"));
+		return (zfs_error(hdl, EZFS_BADPROP, dgettext(TEXT_DOMAIN,
+		    "bad property list")));
+	}
+
+	/*
+	 * It would be nice to use getsubopt() here, but the inclusion of column
+	 * aliases makes this more effort than it's worth.
+	 */
+	s = fields;
+	while (*s != '\0') {
+		if ((p = strchr(s, ',')) == NULL) {
+			len = strlen(s);
+			p = s + len;
+		} else {
+			len = p - s;
+		}
+
+		/*
+		 * Check for empty options.
+		 */
+		if (len == 0) {
+			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+			    "empty property name"));
+			return (zfs_error(hdl, EZFS_BADPROP,
+			    dgettext(TEXT_DOMAIN, "bad property list")));
+		}
+
+		/*
+		 * Check all regular property names.
+		 */
+		c = s[len];
+		s[len] = '\0';
+		for (i = 0; i < ZFS_NPROP_ALL; i++) {
+			if ((prop = zfs_name_to_prop(s)) != ZFS_PROP_INVAL)
+				break;
+		}
+
+		/*
+		 * If no column is specified, and this isn't a user property,
+		 * return failure.
+		 */
+		if (i == ZFS_NPROP_ALL && !zfs_prop_user(s)) {
+			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+			    "invalid property '%s'"), s);
+			return (zfs_error(hdl, EZFS_BADPROP,
+			    dgettext(TEXT_DOMAIN, "bad property list")));
+		}
+
+		if ((entry = zfs_alloc(hdl, sizeof (zfs_proplist_t))) == NULL)
+			return (-1);
+
+		entry->pl_prop = prop;
+		if (prop == ZFS_PROP_INVAL) {
+			if ((entry->pl_user_prop =
+			    zfs_strdup(hdl, s)) == NULL) {
+				free(entry);
+				return (-1);
+			}
+			entry->pl_width = strlen(s);
+		} else {
+			entry->pl_width = zfs_prop_width(prop,
+			    &entry->pl_fixed);
+		}
+
+		*last = entry;
+		last = &entry->pl_next;
+
+		s = p;
+		if (c == ',')
+			s++;
+	}
+
+	return (0);
+}
+
+void
+zfs_free_proplist(zfs_proplist_t *pl)
+{
+	zfs_proplist_t *next;
+
+	while (pl != NULL) {
+		next = pl->pl_next;
+		free(pl->pl_user_prop);
+		free(pl);
+		pl = next;
+	}
+}
+
+/*
+ * This function is used by 'zfs list' to determine the exact set of columns to
+ * display, and their maximum widths.  This does two main things:
+ *
+ * 	- If this is a list of all properties, then expand the list to include
+ *	  all native properties, and set a flag so that for each dataset we look
+ *	  for new unique user properties and add them to the list.
+ *
+ * 	- For non fixed-width properties, keep track of the maximum width seen
+ *	  so that we can size the column appropriately.
+ */
+int
+zfs_expand_proplist(zfs_handle_t *zhp, zfs_proplist_t **plp)
+{
+	libzfs_handle_t *hdl = zhp->zfs_hdl;
+	zfs_prop_t prop;
+	zfs_proplist_t *entry;
+	zfs_proplist_t **last, **start;
+	nvlist_t *userprops, *propval;
+	nvpair_t *elem;
+	char *strval;
+	char buf[ZFS_MAXPROPLEN];
+
+	if (*plp == NULL) {
+		/*
+		 * If this is the very first time we've been called for an 'all'
+		 * specification, expand the list to include all native
+		 * properties.
+		 */
+		last = plp;
+		for (prop = 0; prop < ZFS_NPROP_VISIBLE; prop++) {
+			if ((entry = zfs_alloc(hdl,
+			    sizeof (zfs_proplist_t))) == NULL)
+				return (-1);
+
+			entry->pl_prop = prop;
+			entry->pl_width = zfs_prop_width(prop,
+			    &entry->pl_fixed);
+			entry->pl_all = B_TRUE;
+
+			*last = entry;
+			last = &entry->pl_next;
+		}
+
+		/*
+		 * Add 'name' to the beginning of the list, which is handled
+		 * specially.
+		 */
+		if ((entry = zfs_alloc(hdl,
+		    sizeof (zfs_proplist_t))) == NULL)
+			return (-1);
+
+		entry->pl_prop = ZFS_PROP_NAME;
+		entry->pl_width = zfs_prop_width(ZFS_PROP_NAME,
+		    &entry->pl_fixed);
+		entry->pl_all = B_TRUE;
+		entry->pl_next = *plp;
+		*plp = entry;
+	}
+
+	userprops = zfs_get_user_props(zhp);
+
+	entry = *plp;
+	if (entry->pl_all && nvlist_next_nvpair(userprops, NULL) != NULL) {
+		/*
+		 * Go through and add any user properties as necessary.  We
+		 * start by incrementing our list pointer to the first
+		 * non-native property.
+		 */
+		start = plp;
+		while (*start != NULL) {
+			if ((*start)->pl_prop == ZFS_PROP_INVAL)
+				break;
+			start = &(*start)->pl_next;
+		}
+
+		elem = NULL;
+		while ((elem = nvlist_next_nvpair(userprops, elem)) != NULL) {
+			/*
+			 * See if we've already found this property in our list.
+			 */
+			for (last = start; *last != NULL;
+			    last = &(*last)->pl_next) {
+				if (strcmp((*last)->pl_user_prop,
+				    nvpair_name(elem)) == 0)
+					break;
+			}
+
+			if (*last == NULL) {
+				if ((entry = zfs_alloc(hdl,
+				    sizeof (zfs_proplist_t))) == NULL ||
+				    ((entry->pl_user_prop = zfs_strdup(hdl,
+				    nvpair_name(elem)))) == NULL) {
+					free(entry);
+					return (-1);
+				}
+
+				entry->pl_prop = ZFS_PROP_INVAL;
+				entry->pl_width = strlen(nvpair_name(elem));
+				entry->pl_all = B_TRUE;
+				*last = entry;
+			}
+		}
+	}
+
+	/*
+	 * Now go through and check the width of any non-fixed columns
+	 */
+	for (entry = *plp; entry != NULL; entry = entry->pl_next) {
+		if (entry->pl_fixed)
+			continue;
+
+		if (entry->pl_prop != ZFS_PROP_INVAL) {
+			if (zfs_prop_get(zhp, entry->pl_prop,
+			    buf, sizeof (buf), NULL, NULL, 0, B_FALSE) == 0) {
+				if (strlen(buf) > entry->pl_width)
+					entry->pl_width = strlen(buf);
+			}
+		} else if (nvlist_lookup_nvlist(userprops,
+		    entry->pl_user_prop, &propval)  == 0) {
+			verify(nvlist_lookup_string(propval,
+			    ZFS_PROP_VALUE, &strval) == 0);
+			if (strlen(strval) > entry->pl_width)
+				entry->pl_width = strlen(strval);
+		}
+	}
+
+	return (0);
+}
--- a/usr/src/lib/libzfs/common/libzfs_graph.c	Mon Sep 04 19:32:13 2006 -0700
+++ b/usr/src/lib/libzfs/common/libzfs_graph.c	Tue Sep 05 11:37:36 2006 -0700
@@ -207,18 +207,15 @@
 		return (-1);
 
 	if (zvp->zv_edgecount == zvp->zv_edgealloc) {
-		zfs_edge_t **newedges = zfs_alloc(hdl, zvp->zv_edgealloc * 2 *
-		    sizeof (void *));
+		void *ptr;
 
-		if (newedges == NULL)
+		if ((ptr = zfs_realloc(hdl, zvp->zv_edges,
+		    zvp->zv_edgealloc * sizeof (void *),
+		    zvp->zv_edgealloc * 2 * sizeof (void *))) == NULL)
 			return (-1);
 
-		bcopy(zvp->zv_edges, newedges,
-		    zvp->zv_edgealloc * sizeof (void *));
-
+		zvp->zv_edges = ptr;
 		zvp->zv_edgealloc *= 2;
-		free(zvp->zv_edges);
-		zvp->zv_edges = newedges;
 	}
 
 	zvp->zv_edges[zvp->zv_edgecount++] = zep;
--- a/usr/src/lib/libzfs/common/libzfs_impl.h	Mon Sep 04 19:32:13 2006 -0700
+++ b/usr/src/lib/libzfs/common/libzfs_impl.h	Tue Sep 05 11:37:36 2006 -0700
@@ -60,14 +60,20 @@
 	char zfs_name[ZFS_MAXNAMELEN];
 	zfs_type_t zfs_type;
 	dmu_objset_stats_t zfs_dmustats;
+	zvol_stats_t zfs_volstats;
 	nvlist_t *zfs_props;
-	uint64_t zfs_volsize;
-	uint64_t zfs_volblocksize;
+	nvlist_t *zfs_user_props;
 	boolean_t zfs_mntcheck;
 	char *zfs_mntopts;
 	char zfs_root[MAXPATHLEN];
 };
 
+/*
+ * This is different from checking zfs_type, because it will also catch
+ * snapshots of volumes.
+ */
+#define	ZFS_IS_VOLUME(zhp) ((zhp)->zfs_volstats.zv_volblocksize != 0)
+
 struct zpool_handle {
 	libzfs_handle_t *zpool_hdl;
 	char zpool_name[ZPOOL_MAXNAMELEN];
@@ -82,6 +88,7 @@
 int zfs_error(libzfs_handle_t *, int, const char *, ...);
 void zfs_error_aux(libzfs_handle_t *, const char *, ...);
 void *zfs_alloc(libzfs_handle_t *, size_t);
+void *zfs_realloc(libzfs_handle_t *, void *, size_t, size_t);
 char *zfs_strdup(libzfs_handle_t *, const char *);
 int no_memory(libzfs_handle_t *);
 
@@ -93,6 +100,12 @@
 
 typedef struct prop_changelist prop_changelist_t;
 
+int zcmd_alloc_dst_nvlist(libzfs_handle_t *, zfs_cmd_t *, size_t);
+int zcmd_write_src_nvlist(libzfs_handle_t *, zfs_cmd_t *, nvlist_t *, size_t *);
+int zcmd_expand_dst_nvlist(libzfs_handle_t *, zfs_cmd_t *);
+int zcmd_read_dst_nvlist(libzfs_handle_t *, zfs_cmd_t *, nvlist_t **);
+void zcmd_free_nvlists(zfs_cmd_t *);
+
 int changelist_prefix(prop_changelist_t *);
 int changelist_postfix(prop_changelist_t *);
 void changelist_rename(prop_changelist_t *, const char *, const char *);
--- a/usr/src/lib/libzfs/common/libzfs_import.c	Mon Sep 04 19:32:13 2006 -0700
+++ b/usr/src/lib/libzfs/common/libzfs_import.c	Tue Sep 05 11:37:36 2006 -0700
@@ -382,7 +382,6 @@
 	char *name;
 	zfs_cmd_t zc = { 0 };
 	uint64_t version, guid;
-	char *packed;
 	size_t len;
 	int err;
 	uint_t children = 0;
@@ -575,50 +574,38 @@
 		/*
 		 * Try to do the import in order to get vdev state.
 		 */
-		if ((err = nvlist_size(config, &len, NV_ENCODE_NATIVE)) != 0)
-			goto nomem;
-
-		if ((packed = zfs_alloc(hdl, len)) == NULL)
-			goto nomem;
-
-		if ((err = nvlist_pack(config, &packed, &len,
-		    NV_ENCODE_NATIVE, 0)) != 0)
-			goto nomem;
+		if (zcmd_write_src_nvlist(hdl, &zc, config, &len) != 0)
+			goto error;
 
 		nvlist_free(config);
 		config = NULL;
 
-		zc.zc_config_src_size = len;
-		zc.zc_config_src = (uint64_t)(uintptr_t)packed;
-
-		zc.zc_config_dst_size = 2 * len;
-		if ((zc.zc_config_dst = (uint64_t)(uintptr_t)
-		    zfs_alloc(hdl, zc.zc_config_dst_size)) == NULL)
-			goto nomem;
+		if (zcmd_alloc_dst_nvlist(hdl, &zc, len * 2) != 0) {
+			zcmd_free_nvlists(&zc);
+			goto error;
+		}
 
 		while ((err = ioctl(hdl->libzfs_fd, ZFS_IOC_POOL_TRYIMPORT,
 		    &zc)) != 0 && errno == ENOMEM) {
-			free((void *)(uintptr_t)zc.zc_config_dst);
-			if ((zc.zc_config_dst = (uint64_t)(uintptr_t)
-			    zfs_alloc(hdl, zc.zc_config_dst_size)) == NULL)
-				goto nomem;
+			if (zcmd_expand_dst_nvlist(hdl, &zc) != 0) {
+				zcmd_free_nvlists(&zc);
+				goto error;
+			}
 		}
 
-		free(packed);
-
 		if (err) {
 			(void) zpool_standard_error(hdl, errno,
 			    dgettext(TEXT_DOMAIN, "cannot discover pools"));
-			free((void *)(uintptr_t)zc.zc_config_dst);
+			zcmd_free_nvlists(&zc);
 			goto error;
 		}
 
-		if (nvlist_unpack((void *)(uintptr_t)zc.zc_config_dst,
-		    zc.zc_config_dst_size, &config, 0) != 0) {
-			free((void *)(uintptr_t)zc.zc_config_dst);
-			goto nomem;
+		if (zcmd_read_dst_nvlist(hdl, &zc, &config) != 0) {
+			zcmd_free_nvlists(&zc);
+			goto error;
 		}
-		free((void *)(uintptr_t)zc.zc_config_dst);
+
+		zcmd_free_nvlists(&zc);
 
 		/*
 		 * Go through and update the paths for spares, now that we have
@@ -640,6 +627,8 @@
 		/*
 		 * Add this pool to the list of configs.
 		 */
+		verify(nvlist_lookup_string(config, ZPOOL_CONFIG_POOL_NAME,
+		    &name) == 0);
 		if (nvlist_add_nvlist(ret, name, config) != 0)
 			goto nomem;
 
--- a/usr/src/lib/libzfs/common/libzfs_mount.c	Mon Sep 04 19:32:13 2006 -0700
+++ b/usr/src/lib/libzfs/common/libzfs_mount.c	Tue Sep 05 11:37:36 2006 -0700
@@ -151,6 +151,40 @@
 }
 
 /*
+ * Returns true if the given dataset is mountable, false otherwise.  Returns the
+ * mountpoint in 'buf'.
+ */
+static boolean_t
+zfs_is_mountable(zfs_handle_t *zhp, char *buf, size_t buflen,
+    zfs_source_t *source)
+{
+	char sourceloc[ZFS_MAXNAMELEN];
+	zfs_source_t sourcetype;
+
+	if (!zfs_prop_valid_for_type(ZFS_PROP_MOUNTPOINT, zhp->zfs_type))
+		return (B_FALSE);
+
+	verify(zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, buf, buflen,
+	    &sourcetype, sourceloc, sizeof (sourceloc), B_FALSE) == 0);
+
+	if (strcmp(buf, ZFS_MOUNTPOINT_NONE) == 0 ||
+	    strcmp(buf, ZFS_MOUNTPOINT_LEGACY) == 0)
+		return (B_FALSE);
+
+	if (!zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT))
+		return (B_FALSE);
+
+	if (zfs_prop_get_int(zhp, ZFS_PROP_ZONED) &&
+	    getzoneid() == GLOBAL_ZONEID)
+		return (B_FALSE);
+
+	if (source)
+		*source = sourcetype;
+
+	return (B_TRUE);
+}
+
+/*
  * Mount the given filesystem.
  */
 int
@@ -166,22 +200,7 @@
 	else
 		(void) strlcpy(mntopts, options, sizeof (mntopts));
 
-	/* ignore non-filesystems */
-	if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
-	    sizeof (mountpoint), NULL, NULL, 0, B_FALSE) != 0)
-		return (0);
-
-	/* return success if there is no mountpoint set */
-	if (strcmp(mountpoint, ZFS_MOUNTPOINT_NONE) == 0 ||
-	    strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0)
-		return (0);
-
-	/*
-	 * If the 'zoned' property is set, and we're in the global zone, simply
-	 * return success.
-	 */
-	if (zfs_prop_get_int(zhp, ZFS_PROP_ZONED) &&
-	    getzoneid() == GLOBAL_ZONEID)
+	if (!zfs_is_mountable(zhp, mountpoint, sizeof (mountpoint), NULL))
 		return (0);
 
 	/* Create the directory if it doesn't already exist */
@@ -334,15 +353,7 @@
 	FILE *fp;
 	libzfs_handle_t *hdl = zhp->zfs_hdl;
 
-	/* ignore non-filesystems */
-	if (zfs_get_type(zhp) != ZFS_TYPE_FILESYSTEM)
-		return (0);
-
-	/* return success if there is no mountpoint set */
-	if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT,
-	    mountpoint, sizeof (mountpoint), NULL, NULL, 0, B_FALSE) != 0 ||
-	    strcmp(mountpoint, ZFS_MOUNTPOINT_NONE) == 0 ||
-	    strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0)
+	if (!zfs_is_mountable(zhp, mountpoint, sizeof (mountpoint), NULL))
 		return (0);
 
 	/* return success if there are no share options */
@@ -352,14 +363,12 @@
 		return (0);
 
 	/*
-	 * If the 'zoned' property is set, simply return success since:
-	 * 1. in a global zone, a dataset should not be shared if it's
-	 *    managed in a local zone.
-	 * 2. in a local zone, NFS server is not available.
+	 * If the 'zoned' property is set, then zfs_is_mountable() will have
+	 * already bailed out if we are in the global zone.  But local
+	 * zones cannot be NFS servers, so we ignore it for local zones as well.
 	 */
-	if (zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) {
+	if (zfs_prop_get_int(zhp, ZFS_PROP_ZONED))
 		return (0);
-	}
 
 	/*
 	 * Invoke the share(1M) command.  We always do this, even if it's
@@ -509,28 +518,19 @@
 remove_mountpoint(zfs_handle_t *zhp)
 {
 	char mountpoint[ZFS_MAXPROPLEN];
-	char source[ZFS_MAXNAMELEN];
-	zfs_source_t sourcetype;
-	int zoneid = getzoneid();
+	zfs_source_t source;
 
-	/* ignore non-filesystems */
-	if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
-	    sizeof (mountpoint), &sourcetype, source, sizeof (source),
-	    B_FALSE) != 0)
+	if (!zfs_is_mountable(zhp, mountpoint, sizeof (mountpoint),
+	    &source))
 		return;
 
-	if (strcmp(mountpoint, ZFS_MOUNTPOINT_NONE) != 0 &&
-	    strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) != 0 &&
-	    (sourcetype == ZFS_SRC_DEFAULT ||
-	    sourcetype == ZFS_SRC_INHERITED) &&
-	    (!zfs_prop_get_int(zhp, ZFS_PROP_ZONED) ||
-	    zoneid != GLOBAL_ZONEID)) {
-
+	if (source == ZFS_SRC_DEFAULT ||
+	    source == ZFS_SRC_INHERITED) {
 		/*
 		 * Try to remove the directory, silently ignoring any errors.
 		 * The filesystem may have since been removed or moved around,
-		 * and this isn't really useful to the administrator in any
-		 * way.
+		 * and this error isn't really useful to the administrator in
+		 * any way.
 		 */
 		(void) rmdir(mountpoint);
 	}
@@ -561,16 +561,15 @@
 	}
 
 	if (cbp->cb_alloc == cbp->cb_used) {
-		zfs_handle_t **datasets;
+		void *ptr;
 
-		if ((datasets = zfs_alloc(zhp->zfs_hdl, cbp->cb_alloc * 2 *
-		    sizeof (void *))) == NULL)
+		if ((ptr = zfs_realloc(zhp->zfs_hdl,
+		    cbp->cb_datasets, cbp->cb_alloc * sizeof (void *),
+		    cbp->cb_alloc * 2 * sizeof (void *))) == NULL)
 			return (-1);
+		cbp->cb_datasets = ptr;
 
-		(void) memcpy(cbp->cb_datasets, datasets,
-		    cbp->cb_alloc * sizeof (void *));
-		free(cbp->cb_datasets);
-		cbp->cb_datasets = datasets;
+		cbp->cb_alloc *= 2;
 	}
 
 	cbp->cb_datasets[cbp->cb_used++] = zhp;
@@ -706,23 +705,19 @@
 
 				alloc = 8;
 			} else {
-				char **dest;
+				void *ptr;
 
-				if ((dest = zfs_alloc(hdl,
+				if ((ptr = zfs_realloc(hdl, mountpoints,
+				    alloc * sizeof (void *),
 				    alloc * 2 * sizeof (void *))) == NULL)
 					goto out;
-				(void) memcpy(dest, mountpoints,
-				    alloc * sizeof (void *));
-				free(mountpoints);
-				mountpoints = dest;
+				mountpoints = ptr;
 
-				if ((dest = zfs_alloc(hdl,
+				if ((ptr = zfs_realloc(hdl, datasets,
+				    alloc * sizeof (void *),
 				    alloc * 2 * sizeof (void *))) == NULL)
 					goto out;
-				(void) memcpy(dest, datasets,
-				    alloc * sizeof (void *));
-				free(datasets);
-				datasets = (zfs_handle_t **)dest;
+				datasets = ptr;
 
 				alloc *= 2;
 			}
@@ -753,8 +748,7 @@
 	 */
 	for (i = 0; i < used; i++) {
 		if (is_shared(hdl, mountpoints[i]) &&
-		    unshare_one(hdl, datasets[i] ? datasets[i]->zfs_name :
-		    mountpoints[i], mountpoints[i]) != 0)
+		    unshare_one(hdl, mountpoints[i], mountpoints[i]) != 0)
 			goto out;
 	}
 
@@ -765,7 +759,9 @@
 	for (i = 0; i < used; i++) {
 		if (unmount_one(hdl, mountpoints[i], flags) != 0)
 			goto out;
+	}
 
+	for (i = 0; i < used; i++) {
 		if (datasets[i])
 			remove_mountpoint(datasets[i]);
 	}
--- a/usr/src/lib/libzfs/common/libzfs_pool.c	Mon Sep 04 19:32:13 2006 -0700
+++ b/usr/src/lib/libzfs/common/libzfs_pool.c	Tue Sep 05 11:37:36 2006 -0700
@@ -344,10 +344,10 @@
 
 	(void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
 	if (ioctl(zhp->zpool_hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) != 0 ||
-	    zc.zc_root[0] == '\0')
+	    zc.zc_value[0] == '\0')
 		return (-1);
 
-	(void) strlcpy(buf, zc.zc_root, buflen);
+	(void) strlcpy(buf, zc.zc_value, buflen);
 
 	return (0);
 }
@@ -371,8 +371,6 @@
     const char *altroot)
 {
 	zfs_cmd_t zc = { 0 };
-	char *packed;
-	size_t len;
 	char msg[1024];
 
 	(void) snprintf(msg, sizeof (msg), dgettext(TEXT_DOMAIN,
@@ -385,27 +383,16 @@
 		return (zfs_error(hdl, EZFS_BADPATH,
 		    dgettext(TEXT_DOMAIN, "bad alternate root '%s'"), altroot));
 
-	if (nvlist_size(nvroot, &len, NV_ENCODE_NATIVE) != 0)
-		return (no_memory(hdl));
-
-	if ((packed = zfs_alloc(hdl, len)) == NULL)
+	if (zcmd_write_src_nvlist(hdl, &zc, nvroot, NULL) != 0)
 		return (-1);
 
-	if (nvlist_pack(nvroot, &packed, &len,
-	    NV_ENCODE_NATIVE, 0) != 0) {
-		free(packed);
-		return (no_memory(hdl));
-	}
-
 	(void) strlcpy(zc.zc_name, pool, sizeof (zc.zc_name));
-	zc.zc_config_src = (uint64_t)(uintptr_t)packed;
-	zc.zc_config_src_size = len;
 
 	if (altroot != NULL)
-		(void) strlcpy(zc.zc_root, altroot, sizeof (zc.zc_root));
+		(void) strlcpy(zc.zc_value, altroot, sizeof (zc.zc_value));
 
 	if (ioctl(hdl->libzfs_fd, ZFS_IOC_POOL_CREATE, &zc) != 0) {
-		free(packed);
+		zcmd_free_nvlists(&zc);
 
 		switch (errno) {
 		case EBUSY:
@@ -447,17 +434,18 @@
 		}
 	}
 
-	free(packed);
+	zcmd_free_nvlists(&zc);
 
 	/*
 	 * If this is an alternate root pool, then we automatically set the
-	 * moutnpoint of the root dataset to be '/'.
+	 * mountpoint of the root dataset to be '/'.
 	 */
 	if (altroot != NULL) {
 		zfs_handle_t *zhp;
 
 		verify((zhp = zfs_open(hdl, pool, ZFS_TYPE_ANY)) != NULL);
-		verify(zfs_prop_set(zhp, ZFS_PROP_MOUNTPOINT, "/") == 0);
+		verify(zfs_prop_set(zhp, zfs_prop_to_name(ZFS_PROP_MOUNTPOINT),
+		    "/") == 0);
 
 		zfs_close(zhp);
 	}
@@ -519,9 +507,7 @@
 int
 zpool_add(zpool_handle_t *zhp, nvlist_t *nvroot)
 {
-	char *packed;
-	size_t len;
-	zfs_cmd_t zc;
+	zfs_cmd_t zc = { 0 };
 	int ret;
 	libzfs_handle_t *hdl = zhp->zpool_hdl;
 	char msg[1024];
@@ -539,16 +525,9 @@
 		return (zfs_error(hdl, EZFS_BADVERSION, msg));
 	}
 
-	verify(nvlist_size(nvroot, &len, NV_ENCODE_NATIVE) == 0);
-
-	if ((packed = zfs_alloc(zhp->zpool_hdl, len)) == NULL)
+	if (zcmd_write_src_nvlist(hdl, &zc, nvroot, NULL) != 0)
 		return (-1);
-
-	verify(nvlist_pack(nvroot, &packed, &len, NV_ENCODE_NATIVE, 0) == 0);
-
 	(void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
-	zc.zc_config_src = (uint64_t)(uintptr_t)packed;
-	zc.zc_config_src_size = len;
 
 	if (ioctl(zhp->zpool_hdl->libzfs_fd, ZFS_IOC_VDEV_ADD, &zc) != 0) {
 		switch (errno) {
@@ -598,7 +577,7 @@
 		ret = 0;
 	}
 
-	free(packed);
+	zcmd_free_nvlists(&zc);
 
 	return (ret);
 }
@@ -635,9 +614,7 @@
 zpool_import(libzfs_handle_t *hdl, nvlist_t *config, const char *newname,
     const char *altroot)
 {
-	zfs_cmd_t zc;
-	char *packed;
-	size_t len;
+	zfs_cmd_t zc = { 0 };
 	char *thename;
 	char *origname;
 	int ret;
@@ -663,23 +640,16 @@
 	(void) strlcpy(zc.zc_name, thename, sizeof (zc.zc_name));
 
 	if (altroot != NULL)
-		(void) strlcpy(zc.zc_root, altroot, sizeof (zc.zc_root));
+		(void) strlcpy(zc.zc_value, altroot, sizeof (zc.zc_value));
 	else
-		zc.zc_root[0] = '\0';
+		zc.zc_value[0] = '\0';
 
 	verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID,
 	    &zc.zc_guid) == 0);
 
-	verify(nvlist_size(config, &len, NV_ENCODE_NATIVE) == 0);
-
-	if ((packed = zfs_alloc(hdl, len)) == NULL)
+	if (zcmd_write_src_nvlist(hdl, &zc, config, NULL) != 0)
 		return (-1);
 
-	verify(nvlist_pack(config, &packed, &len, NV_ENCODE_NATIVE, 0) == 0);
-
-	zc.zc_config_src = (uint64_t)(uintptr_t)packed;
-	zc.zc_config_src_size = len;
-
 	ret = 0;
 	if (ioctl(hdl->libzfs_fd, ZFS_IOC_POOL_IMPORT, &zc) != 0) {
 		char desc[1024];
@@ -722,7 +692,7 @@
 		}
 	}
 
-	free(packed);
+	zcmd_free_nvlists(&zc);
 	return (ret);
 }
 
@@ -979,9 +949,7 @@
 {
 	zfs_cmd_t zc = { 0 };
 	char msg[1024];
-	char *packed;
 	int ret;
-	size_t len;
 	nvlist_t *tgt;
 	boolean_t avail_spare;
 	uint64_t val;
@@ -1045,19 +1013,12 @@
 		return (zfs_error(hdl, EZFS_BADTARGET, msg));
 	}
 
-	verify(nvlist_size(nvroot, &len, NV_ENCODE_NATIVE) == 0);
-
-	if ((packed = zfs_alloc(zhp->zpool_hdl, len)) == NULL)
+	if (zcmd_write_src_nvlist(hdl, &zc, nvroot, NULL) != 0)
 		return (-1);
 
-	verify(nvlist_pack(nvroot, &packed, &len, NV_ENCODE_NATIVE, 0) == 0);
-
-	zc.zc_config_src = (uint64_t)(uintptr_t)packed;
-	zc.zc_config_src_size = len;
-
 	ret = ioctl(zhp->zpool_hdl->libzfs_fd, ZFS_IOC_VDEV_ATTACH, &zc);
 
-	free(packed);
+	zcmd_free_nvlists(&zc);
 
 	if (ret == 0)
 		return (0);
@@ -1224,7 +1185,7 @@
 	if (path)
 		(void) snprintf(msg, sizeof (msg),
 		    dgettext(TEXT_DOMAIN, "cannot clear errors for %s"),
-		    zc.zc_prop_value);
+		    path);
 	else
 		(void) snprintf(msg, sizeof (msg),
 		    dgettext(TEXT_DOMAIN, "cannot clear errors for %s"),
@@ -1258,7 +1219,7 @@
 	 * We check for volblocksize intead of ZFS_TYPE_VOLUME so that we
 	 * correctly handle snapshots of volumes.
 	 */
-	if (zhp->zfs_volblocksize != 0) {
+	if (ZFS_IS_VOLUME(zhp)) {
 		if (linktype)
 			ret = zvol_create_link(zhp->zfs_hdl, zhp->zfs_name);
 		else
@@ -1384,7 +1345,7 @@
 	zfs_cmd_t zc = { 0 };
 
 	(void) strncpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
-	(void) strncpy(zc.zc_prop_value, path, sizeof (zc.zc_prop_value));
+	(void) strncpy(zc.zc_value, path, sizeof (zc.zc_value));
 	verify(nvlist_lookup_uint64(nv, ZPOOL_CONFIG_GUID,
 	    &zc.zc_guid) == 0);
 
@@ -1494,7 +1455,8 @@
 {
 	zfs_cmd_t zc = { 0 };
 	uint64_t count;
-	zbookmark_t *zb;
+	zbookmark_t *zb = NULL;
+	libzfs_handle_t *hdl = zhp->zpool_hdl;
 	int i, j;
 
 	if (zhp->zpool_error_log != NULL) {
@@ -1510,19 +1472,19 @@
 	 */
 	verify(nvlist_lookup_uint64(zhp->zpool_config, ZPOOL_CONFIG_ERRCOUNT,
 	    &count) == 0);
-	if ((zc.zc_config_dst = (uintptr_t)zfs_alloc(zhp->zpool_hdl,
+	if ((zc.zc_nvlist_dst = (uintptr_t)zfs_alloc(zhp->zpool_hdl,
 	    count * sizeof (zbookmark_t))) == NULL)
 		return (-1);
-	zc.zc_config_dst_size = count;
+	zc.zc_nvlist_dst_size = count;
 	(void) strcpy(zc.zc_name, zhp->zpool_name);
 	for (;;) {
 		if (ioctl(zhp->zpool_hdl->libzfs_fd, ZFS_IOC_ERROR_LOG,
 		    &zc) != 0) {
-			free((void *)(uintptr_t)zc.zc_config_dst);
+			free((void *)(uintptr_t)zc.zc_nvlist_dst);
 			if (errno == ENOMEM) {
-				if ((zc.zc_config_dst = (uintptr_t)
+				if ((zc.zc_nvlist_dst = (uintptr_t)
 				    zfs_alloc(zhp->zpool_hdl,
-				    zc.zc_config_dst_size)) == NULL)
+				    zc.zc_nvlist_dst_size)) == NULL)
 					return (-1);
 			} else {
 				return (-1);
@@ -1535,13 +1497,14 @@
 	/*
 	 * Sort the resulting bookmarks.  This is a little confusing due to the
 	 * implementation of ZFS_IOC_ERROR_LOG.  The bookmarks are copied last
-	 * to first, and 'zc_config_dst_size' indicates the number of boomarks
+	 * to first, and 'zc_nvlist_dst_size' indicates the number of boomarks
 	 * _not_ copied as part of the process.  So we point the start of our
 	 * array appropriate and decrement the total number of elements.
 	 */
-	zb = ((zbookmark_t *)(uintptr_t)zc.zc_config_dst) +
-	    zc.zc_config_dst_size;
-	count -= zc.zc_config_dst_size;
+	zb = ((zbookmark_t *)(uintptr_t)zc.zc_nvlist_dst) +
+	    zc.zc_nvlist_dst_size;
+	count -= zc.zc_nvlist_dst_size;
+	zc.zc_nvlist_dst = 0ULL;
 
 	qsort(zb, count, sizeof (zbookmark_t), zbookmark_compare);
 
@@ -1562,7 +1525,7 @@
 	 */
 	if (list == NULL) {
 		*nelem = j;
-		free((void *)(uintptr_t)zc.zc_config_dst);
+		free((void *)(uintptr_t)zc.zc_nvlist_dst);
 		return (0);
 	}
 
@@ -1573,7 +1536,7 @@
 	 */
 	if ((zhp->zpool_error_log = zfs_alloc(zhp->zpool_hdl,
 	    j * sizeof (nvlist_t *))) == NULL) {
-		free((void *)(uintptr_t)zc.zc_config_dst);
+		free((void *)(uintptr_t)zc.zc_nvlist_dst);
 		return (-1);
 	}
 
@@ -1589,55 +1552,72 @@
 		    sizeof (zbookmark_t)) == 0)
 			continue;
 
-		if (nvlist_alloc(&nv, NV_UNIQUE_NAME,
-		    0) != 0)
+		if (zcmd_alloc_dst_nvlist(hdl, &zc, 0) != 0)
 			goto nomem;
-		zhp->zpool_error_log[j] = nv;
 
 		zc.zc_bookmark = zb[i];
-		if (ioctl(zhp->zpool_hdl->libzfs_fd, ZFS_IOC_BOOKMARK_NAME,
-		    &zc) == 0) {
-			if (nvlist_add_string(nv, ZPOOL_ERR_DATASET,
-			    zc.zc_prop_name) != 0 ||
-			    nvlist_add_string(nv, ZPOOL_ERR_OBJECT,
-			    zc.zc_prop_value) != 0 ||
-			    nvlist_add_string(nv, ZPOOL_ERR_RANGE,
-			    zc.zc_filename) != 0)
-				goto nomem;
-		} else {
-			(void) snprintf(buf, sizeof (buf), "%llx",
-			    zb[i].zb_objset);
-			if (nvlist_add_string(nv,
-			    ZPOOL_ERR_DATASET, buf) != 0)
-				goto nomem;
-			(void) snprintf(buf, sizeof (buf), "%llx",
-			    zb[i].zb_object);
-			if (nvlist_add_string(nv, ZPOOL_ERR_OBJECT,
-			    buf) != 0)
-				goto nomem;
-			(void) snprintf(buf, sizeof (buf), "lvl=%u blkid=%llu",
-			    (int)zb[i].zb_level, (long long)zb[i].zb_blkid);
-			if (nvlist_add_string(nv, ZPOOL_ERR_RANGE,
-			    buf) != 0)
-				goto nomem;
+		for (;;) {
+			if (ioctl(zhp->zpool_hdl->libzfs_fd,
+			    ZFS_IOC_BOOKMARK_NAME, &zc) != 0) {
+				if (errno == ENOMEM) {
+					if (zcmd_expand_dst_nvlist(hdl, &zc)
+					    != 0) {
+						zcmd_free_nvlists(&zc);
+						goto nomem;
+					}
+
+					continue;
+				} else {
+					if (nvlist_alloc(&nv, NV_UNIQUE_NAME,
+					    0) != 0)
+						goto nomem;
+
+					zhp->zpool_error_log[j] = nv;
+					(void) snprintf(buf, sizeof (buf),
+					    "%llx", zb[i].zb_objset);
+					if (nvlist_add_string(nv,
+					    ZPOOL_ERR_DATASET, buf) != 0)
+						goto nomem;
+					(void) snprintf(buf, sizeof (buf),
+					    "%llx", zb[i].zb_object);
+					if (nvlist_add_string(nv,
+					    ZPOOL_ERR_OBJECT, buf) != 0)
+						goto nomem;
+					(void) snprintf(buf, sizeof (buf),
+					    "lvl=%u blkid=%llu",
+					    (int)zb[i].zb_level,
+					    (long long)zb[i].zb_blkid);
+					if (nvlist_add_string(nv,
+					    ZPOOL_ERR_RANGE, buf) != 0)
+						goto nomem;
+				}
+			} else {
+				if (zcmd_read_dst_nvlist(hdl, &zc,
+				    &zhp->zpool_error_log[j]) != 0) {
+					zcmd_free_nvlists(&zc);
+					goto nomem;
+				}
+			}
+
+			break;
 		}
 
+		zcmd_free_nvlists(&zc);
+
 		j++;
 	}
 
 	*list = zhp->zpool_error_log;
 	*nelem = zhp->zpool_error_count;
-
-	free((void *)(uintptr_t)zc.zc_config_dst);
+	free(zb);
 
 	return (0);
 
 nomem:
-	free((void *)(uintptr_t)zc.zc_config_dst);
-	for (i = 0; i < zhp->zpool_error_count; i++) {
-		if (zhp->zpool_error_log[i])
-			free(zhp->zpool_error_log[i]);
-	}
+	free(zb);
+	free((void *)(uintptr_t)zc.zc_nvlist_dst);
+	for (i = 0; i < zhp->zpool_error_count; i++)
+		nvlist_free(zhp->zpool_error_log[i]);
 	free(zhp->zpool_error_log);
 	zhp->zpool_error_log = NULL;
 	return (no_memory(zhp->zpool_hdl));
--- a/usr/src/lib/libzfs/common/libzfs_util.c	Mon Sep 04 19:32:13 2006 -0700
+++ b/usr/src/lib/libzfs/common/libzfs_util.c	Tue Sep 05 11:37:36 2006 -0700
@@ -363,6 +363,24 @@
 }
 
 /*
+ * A safe form of realloc(), which also zeroes newly allocated space.
+ */
+void *
+zfs_realloc(libzfs_handle_t *hdl, void *ptr, size_t oldsize, size_t newsize)
+{
+	void *ret;
+
+	if ((ret = realloc(ptr, newsize)) == NULL) {
+		(void) no_memory(hdl);
+		free(ptr);
+		return (NULL);
+	}
+
+	bzero((char *)ret + oldsize, (newsize - oldsize));
+	return (ret);
+}
+
+/*
  * A safe form of strdup() which will die if the allocation fails.
  */
 char *
@@ -475,3 +493,85 @@
 {
 	return (zhp->zfs_hdl);
 }
+
+/*
+ * Initialize the zc_nvlist_dst member to prepare for receiving an nvlist from
+ * an ioctl().
+ */
+int
+zcmd_alloc_dst_nvlist(libzfs_handle_t *hdl, zfs_cmd_t *zc, size_t len)
+{
+	if (len == 0)
+		len = 1024;
+	zc->zc_nvlist_dst_size = len;
+	if ((zc->zc_nvlist_dst = (uint64_t)(uintptr_t)
+	    zfs_alloc(hdl, zc->zc_nvlist_dst_size)) == NULL)
+		return (-1);
+
+	return (0);
+}
+
+/*
+ * Called when an ioctl() which returns an nvlist fails with ENOMEM.  This will
+ * expand the nvlist to the size specified in 'zc_nvlist_dst_size', which was
+ * filled in by the kernel to indicate the actual required size.
+ */
+int
+zcmd_expand_dst_nvlist(libzfs_handle_t *hdl, zfs_cmd_t *zc)
+{
+	free((void *)(uintptr_t)zc->zc_nvlist_dst);
+	if ((zc->zc_nvlist_dst = (uint64_t)(uintptr_t)
+	    zfs_alloc(hdl, zc->zc_nvlist_dst_size))
+	    == NULL)
+		return (-1);
+
+	return (0);
+}
+
+/*
+ * Called to free the destination nvlist stored in the command structure.  This
+ * is only needed if the caller must abort abnormally.  The various other
+ * zcmd_*() routines will free it on failure (or on success, for
+ * zcmd_read_nvlist).
+ */
+void
+zcmd_free_nvlists(zfs_cmd_t *zc)
+{
+	free((void *)(uintptr_t)zc->zc_nvlist_src);
+	free((void *)(uintptr_t)zc->zc_nvlist_dst);
+}
+
+int
+zcmd_write_src_nvlist(libzfs_handle_t *hdl, zfs_cmd_t *zc, nvlist_t *nvl,
+    size_t *size)
+{
+	char *packed;
+	size_t len;
+
+	verify(nvlist_size(nvl, &len, NV_ENCODE_NATIVE) == 0);
+
+	if ((packed = zfs_alloc(hdl, len)) == NULL)
+		return (-1);
+
+	verify(nvlist_pack(nvl, &packed, &len, NV_ENCODE_NATIVE, 0) == 0);
+
+	zc->zc_nvlist_src = (uint64_t)(uintptr_t)packed;
+	zc->zc_nvlist_src_size = len;
+
+	if (size)
+		*size = len;
+	return (0);
+}
+
+/*
+ * Unpacks an nvlist from the ZFS ioctl command structure.
+ */
+int
+zcmd_read_dst_nvlist(libzfs_handle_t *hdl, zfs_cmd_t *zc, nvlist_t **nvlp)
+{
+	if (nvlist_unpack((void *)(uintptr_t)zc->zc_nvlist_dst,
+	    zc->zc_nvlist_dst_size, nvlp, 0) != 0)
+		return (no_memory(hdl));
+
+	return (0);
+}
--- a/usr/src/lib/libzfs/common/mapfile-vers	Mon Sep 04 19:32:13 2006 -0700
+++ b/usr/src/lib/libzfs/common/mapfile-vers	Tue Sep 05 11:37:36 2006 -0700
@@ -38,8 +38,11 @@
 	zfs_create;
 	zfs_destroy;
 	zfs_destroy_snaps;
+	zfs_expand_proplist;
+	zfs_free_proplist;
 	zfs_get_handle;
 	zfs_get_name;
+	zfs_get_user_props;
 	zfs_get_proplist;
 	zfs_get_type;
 	zfs_is_mounted;
@@ -56,7 +59,7 @@
 	zfs_nicestrtonum;
 	zfs_open;
 	zfs_promote;
-	zfs_prop_column_format;
+	zfs_prop_align_right;
 	zfs_prop_column_name;
 	zfs_prop_default_numeric;
 	zfs_prop_default_string;
@@ -69,9 +72,10 @@
 	zfs_prop_readonly;
 	zfs_prop_set;
 	zfs_prop_to_name;
-	zfs_prop_validate;
+	zfs_prop_user;
 	zfs_prop_valid_for_type;
 	zfs_prop_values;
+	zfs_prop_width;
 	zfs_receive;
 	zfs_refresh_properties;
 	zfs_rename;
--- a/usr/src/uts/common/fs/zfs/dmu.c	Mon Sep 04 19:32:13 2006 -0700
+++ b/usr/src/uts/common/fs/zfs/dmu.c	Tue Sep 05 11:37:36 2006 -0700
@@ -1796,17 +1796,16 @@
  * human-readable format.
  */
 int
-spa_bookmark_name(spa_t *spa, zbookmark_t *zb, char *dsname, size_t dslen,
-    char *objname, size_t objlen, char *range, size_t rangelen)
+spa_bookmark_name(spa_t *spa, zbookmark_t *zb, nvlist_t *nvl)
 {
 	dsl_pool_t *dp;
 	dsl_dataset_t *ds = NULL;
 	objset_t *os = NULL;
 	dnode_t *dn = NULL;
 	int err, shift;
-
-	if (dslen < MAXNAMELEN || objlen < 32 || rangelen < 64)
-		return (ENOSPC);
+	char dsname[MAXNAMELEN];
+	char objname[32];
+	char range[64];
 
 	dp = spa_get_dsl(spa);
 	if (zb->zb_objset != 0) {
@@ -1832,9 +1831,9 @@
 
 
 	if (zb->zb_object == DMU_META_DNODE_OBJECT) {
-		(void) strncpy(objname, "mdn", objlen);
+		(void) strncpy(objname, "mdn", sizeof (objname));
 	} else {
-		(void) snprintf(objname, objlen, "%lld",
+		(void) snprintf(objname, sizeof (objname), "%lld",
 		    (longlong_t)zb->zb_object);
 	}
 
@@ -1844,10 +1843,15 @@
 
 	shift = (dn->dn_datablkshift?dn->dn_datablkshift:SPA_MAXBLOCKSHIFT) +
 	    zb->zb_level * (dn->dn_indblkshift - SPA_BLKPTRSHIFT);
-	(void) snprintf(range, rangelen, "%llu-%llu",
+	(void) snprintf(range, sizeof (range), "%llu-%llu",
 	    (u_longlong_t)(zb->zb_blkid << shift),
 	    (u_longlong_t)((zb->zb_blkid+1) << shift));
 
+	if ((err = nvlist_add_string(nvl, ZPOOL_ERR_DATASET, dsname)) != 0 ||
+	    (err = nvlist_add_string(nvl, ZPOOL_ERR_OBJECT, objname)) != 0 ||
+	    (err = nvlist_add_string(nvl, ZPOOL_ERR_RANGE, range)) != 0)
+		goto out;
+
 out:
 	if (dn)
 		dnode_rele(dn, FTAG);
--- a/usr/src/uts/common/fs/zfs/dsl_prop.c	Mon Sep 04 19:32:13 2006 -0700
+++ b/usr/src/uts/common/fs/zfs/dsl_prop.c	Tue Sep 05 11:37:36 2006 -0700
@@ -67,10 +67,13 @@
     int intsz, int numint, void *buf, char *setpoint)
 {
 	int err = ENOENT;
+	zfs_prop_t prop;
 
 	if (setpoint)
 		setpoint[0] = '\0';
 
+	prop = zfs_name_to_prop(propname);
+
 	/*
 	 * Note: dd may be NULL, therefore we shouldn't dereference it
 	 * ouside this loop.
@@ -85,6 +88,13 @@
 				dsl_dir_name(dd, setpoint);
 			break;
 		}
+
+		/*
+		 * Break out of this loop for non-inheritable properties.
+		 */
+		if (prop != ZFS_PROP_INVAL &&
+		    !zfs_prop_inheritable(prop))
+			break;
 	}
 	if (err == ENOENT)
 		err = dodefault(propname, intsz, numint, buf);
@@ -403,7 +413,8 @@
 	zap_attribute_t za;
 	char setpoint[MAXNAMELEN];
 	char *tmp;
-	nvlist_t *prop;
+	nvlist_t *propval;
+	zfs_prop_t prop;
 
 	if (dsl_dataset_is_snapshot(ds)) {
 		VERIFY(nvlist_alloc(nvp, NV_UNIQUE_NAME, KM_SLEEP) == 0);
@@ -422,10 +433,19 @@
 		for (zap_cursor_init(&zc, mos, dd->dd_phys->dd_props_zapobj);
 		    (err = zap_cursor_retrieve(&zc, &za)) == 0;
 		    zap_cursor_advance(&zc)) {
-			if (nvlist_lookup_nvlist(*nvp, za.za_name, &prop) == 0)
+			/*
+			 * Skip non-inheritable properties.
+			 */
+			if ((prop = zfs_name_to_prop(za.za_name)) !=
+			    ZFS_PROP_INVAL && !zfs_prop_inheritable(prop) &&
+			    dd != ds->ds_dir)
 				continue;
 
-			VERIFY(nvlist_alloc(&prop, NV_UNIQUE_NAME,
+			if (nvlist_lookup_nvlist(*nvp, za.za_name,
+			    &propval) == 0)
+				continue;
+
+			VERIFY(nvlist_alloc(&propval, NV_UNIQUE_NAME,
 			    KM_SLEEP) == 0);
 			if (za.za_integer_length == 1) {
 				/*
@@ -440,7 +460,7 @@
 					kmem_free(tmp, za.za_num_integers);
 					break;
 				}
-				VERIFY(nvlist_add_string(prop,
+				VERIFY(nvlist_add_string(propval,
 				    ZFS_PROP_VALUE, tmp) == 0);
 				kmem_free(tmp, za.za_num_integers);
 			} else {
@@ -448,15 +468,15 @@
 				 * Integer property
 				 */
 				ASSERT(za.za_integer_length == 8);
-				(void) nvlist_add_uint64(prop, ZFS_PROP_VALUE,
-				    za.za_first_integer);
+				(void) nvlist_add_uint64(propval,
+				    ZFS_PROP_VALUE, za.za_first_integer);
 			}
 
-			VERIFY(nvlist_add_string(prop,
+			VERIFY(nvlist_add_string(propval,
 			    ZFS_PROP_SOURCE, setpoint) == 0);
 			VERIFY(nvlist_add_nvlist(*nvp, za.za_name,
-			    prop) == 0);
-			nvlist_free(prop);
+			    propval) == 0);
+			nvlist_free(propval);
 		}
 		zap_cursor_fini(&zc);
 
--- a/usr/src/uts/common/fs/zfs/sys/spa.h	Mon Sep 04 19:32:13 2006 -0700
+++ b/usr/src/uts/common/fs/zfs/sys/spa.h	Tue Sep 05 11:37:36 2006 -0700
@@ -439,8 +439,8 @@
 extern void spa_errlog_rotate(spa_t *spa);
 extern void spa_errlog_drain(spa_t *spa);
 extern void spa_errlog_sync(spa_t *spa, uint64_t txg);
-extern int spa_bookmark_name(spa_t *spa, struct zbookmark *zb, char *ds,
-    size_t dsname, char *obj, size_t objname, char *range, size_t rangelen);
+extern int spa_bookmark_name(spa_t *spa, struct zbookmark *zb,
+    nvlist_t *nvl);
 extern void spa_get_errlists(spa_t *spa, avl_tree_t *last, avl_tree_t *scrub);
 
 /* Initialization and termination */
--- a/usr/src/uts/common/fs/zfs/sys/zfs_acl.h	Mon Sep 04 19:32:13 2006 -0700
+++ b/usr/src/uts/common/fs/zfs/sys/zfs_acl.h	Tue Sep 05 11:37:36 2006 -0700
@@ -77,11 +77,11 @@
  * whereas acl_inherit has secure instead of groupmask.
  */
 
-#define	DISCARD		0
-#define	NOALLOW		1
-#define	GROUPMASK	2
-#define	PASSTHROUGH	3
-#define	SECURE		4
+#define	ZFS_ACL_DISCARD		0
+#define	ZFS_ACL_NOALLOW		1
+#define	ZFS_ACL_GROUPMASK	2
+#define	ZFS_ACL_PASSTHROUGH	3
+#define	ZFS_ACL_SECURE		4
 
 struct znode;
 
--- a/usr/src/uts/common/fs/zfs/sys/zfs_ioctl.h	Mon Sep 04 19:32:13 2006 -0700
+++ b/usr/src/uts/common/fs/zfs/sys/zfs_ioctl.h	Tue Sep 05 11:37:36 2006 -0700
@@ -31,6 +31,7 @@
 #include <sys/cred.h>
 #include <sys/dmu.h>
 #include <sys/zio.h>
+#include <sys/zvol.h>
 
 #ifdef	__cplusplus
 extern "C" {
@@ -115,29 +116,29 @@
 
 typedef struct zfs_cmd {
 	char		zc_name[MAXPATHLEN];
-	char		zc_prop_name[MAXNAMELEN];
-	char		zc_prop_value[MAXPATHLEN];
-	char		zc_root[MAXPATHLEN];
-	char		zc_filename[MAXNAMELEN];
-	uint32_t	zc_intsz;
-	uint32_t	zc_numints;
+	char		zc_value[MAXPATHLEN];
 	uint64_t	zc_guid;
-	uint64_t	zc_config_src;	/* really (char *) */
-	uint64_t	zc_config_src_size;
-	uint64_t	zc_config_dst;	/* really (char *) */
-	uint64_t	zc_config_dst_size;
+	uint64_t	zc_nvlist_src;	/* really (char *) */
+	uint64_t	zc_nvlist_src_size;
+	uint64_t	zc_nvlist_dst;	/* really (char *) */
+	uint64_t	zc_nvlist_dst_size;
 	uint64_t	zc_cookie;
 	uint64_t	zc_cred;
 	uint64_t	zc_dev;
-	uint64_t	zc_volsize;
-	uint64_t	zc_volblocksize;
 	uint64_t	zc_objset_type;
 	dmu_objset_stats_t zc_objset_stats;
+	zvol_stats_t	zc_vol_stats;
 	struct drr_begin zc_begin_record;
 	zinject_record_t zc_inject_record;
 	zbookmark_t	zc_bookmark;
 } zfs_cmd_t;
 
+typedef struct zfs_create_data {
+	cred_t		*zc_cred;
+	dev_t		zc_dev;
+	nvlist_t	*zc_props;
+} zfs_create_data_t;
+
 #define	ZVOL_MAX_MINOR	(1 << 16)
 #define	ZFS_MIN_MINOR	(ZVOL_MAX_MINOR + 1)
 
@@ -145,30 +146,9 @@
 
 extern dev_info_t *zfs_dip;
 
-extern int zfs_secpolicy_write(const char *dataset, const char *, cred_t *cr);
+extern int zfs_secpolicy_write(const char *dataset, cred_t *cr);
 extern int zfs_busy(void);
 
-extern int zvol_check_volsize(zfs_cmd_t *zc, uint64_t blocksize);
-extern int zvol_check_volblocksize(zfs_cmd_t *zc);
-extern int zvol_get_stats(zfs_cmd_t *zc, objset_t *os);
-extern void zvol_create_cb(objset_t *os, void *arg, dmu_tx_t *tx);
-extern int zvol_create_minor(zfs_cmd_t *zc);
-extern int zvol_remove_minor(zfs_cmd_t *zc);
-extern int zvol_set_volsize(zfs_cmd_t *zc);
-extern int zvol_set_volblocksize(zfs_cmd_t *zc);
-extern int zvol_open(dev_t *devp, int flag, int otyp, cred_t *cr);
-extern int zvol_close(dev_t dev, int flag, int otyp, cred_t *cr);
-extern int zvol_strategy(buf_t *bp);
-extern int zvol_read(dev_t dev, uio_t *uiop, cred_t *cr);
-extern int zvol_write(dev_t dev, uio_t *uiop, cred_t *cr);
-extern int zvol_aread(dev_t dev, struct aio_req *aio, cred_t *cr);
-extern int zvol_awrite(dev_t dev, struct aio_req *aio, cred_t *cr);
-extern int zvol_ioctl(dev_t dev, int cmd, intptr_t arg, int flag, cred_t *cr,
-    int *rvalp);
-extern int zvol_busy(void);
-extern void zvol_init(void);
-extern void zvol_fini(void);
-
 #endif	/* _KERNEL */
 
 #ifdef	__cplusplus
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/fs/zfs/sys/zvol.h	Tue Sep 05 11:37:36 2006 -0700
@@ -0,0 +1,71 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef	_SYS_ZVOL_H
+#define	_SYS_ZVOL_H
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+#include <sys/zfs_context.h>
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+typedef struct zvol_stats {
+	uint64_t	zv_volsize;
+	uint64_t	zv_volblocksize;
+} zvol_stats_t;
+
+#ifdef _KERNEL
+extern int zvol_check_volsize(uint64_t volsize, uint64_t blocksize);
+extern int zvol_check_volblocksize(uint64_t volblocksize);
+extern int zvol_get_stats(objset_t *os, zvol_stats_t *zvs);
+extern void zvol_create_cb(objset_t *os, void *arg, dmu_tx_t *tx);
+extern int zvol_create_minor(const char *, dev_t);
+extern int zvol_remove_minor(const char *);
+extern int zvol_set_volsize(const char *, dev_t, uint64_t);
+extern int zvol_set_volblocksize(const char *, uint64_t);
+
+extern int zvol_open(dev_t *devp, int flag, int otyp, cred_t *cr);
+extern int zvol_close(dev_t dev, int flag, int otyp, cred_t *cr);
+extern int zvol_strategy(buf_t *bp);
+extern int zvol_read(dev_t dev, uio_t *uiop, cred_t *cr);
+extern int zvol_write(dev_t dev, uio_t *uiop, cred_t *cr);
+extern int zvol_aread(dev_t dev, struct aio_req *aio, cred_t *cr);
+extern int zvol_awrite(dev_t dev, struct aio_req *aio, cred_t *cr);
+extern int zvol_ioctl(dev_t dev, int cmd, intptr_t arg, int flag, cred_t *cr,
+    int *rvalp);
+extern int zvol_busy(void);
+extern void zvol_init(void);
+extern void zvol_fini(void);
+#endif
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* _SYS_ZVOL_H */
--- a/usr/src/uts/common/fs/zfs/zfs_acl.c	Mon Sep 04 19:32:13 2006 -0700
+++ b/usr/src/uts/common/fs/zfs/zfs_acl.c	Tue Sep 05 11:37:36 2006 -0700
@@ -788,7 +788,7 @@
 		}
 
 
-		if (zfsvfs->z_acl_mode == DISCARD) {
+		if (zfsvfs->z_acl_mode == ZFS_ACL_DISCARD) {
 			zfs_ace_remove(aclp, i);
 			continue;
 		}
@@ -820,7 +820,7 @@
 				 * This is only applicable when the acl_mode
 				 * property == groupmask.
 				 */
-				if (zfsvfs->z_acl_mode == GROUPMASK) {
+				if (zfsvfs->z_acl_mode == ZFS_ACL_GROUPMASK) {
 
 					reuse_deny = zfs_reuse_deny(acep, i);
 
@@ -902,7 +902,7 @@
 static void
 zfs_securemode_update(zfsvfs_t *zfsvfs, ace_t *acep)
 {
-	if ((zfsvfs->z_acl_inherit == SECURE) &&
+	if ((zfsvfs->z_acl_inherit == ZFS_ACL_SECURE) &&
 	    (acep->a_type == ALLOW))
 		acep->a_access_mask &= ~SECURE_CLEAR;
 }
@@ -924,10 +924,10 @@
 	i = j = 0;
 	pace_cnt = paclp->z_acl_count;
 	pacep = paclp->z_acl;
-	if (zfsvfs->z_acl_inherit != DISCARD) {
+	if (zfsvfs->z_acl_inherit != ZFS_ACL_DISCARD) {
 		for (i = 0; i != pace_cnt; i++) {
 
-			if (zfsvfs->z_acl_inherit == NOALLOW &&
+			if (zfsvfs->z_acl_inherit == ZFS_ACL_NOALLOW &&
 			    pacep[i].a_type == ALLOW)
 				continue;
 
@@ -941,12 +941,12 @@
 	}
 
 	aclp = zfs_acl_alloc(ace_cnt + OGE_PAD);
-	if (ace_cnt && zfsvfs->z_acl_inherit != DISCARD) {
+	if (ace_cnt && zfsvfs->z_acl_inherit != ZFS_ACL_DISCARD) {
 		acep = aclp->z_acl;
 		pacep = paclp->z_acl;
 		for (i = 0; i != pace_cnt; i++) {
 
-			if (zfsvfs->z_acl_inherit == NOALLOW &&
+			if (zfsvfs->z_acl_inherit == ZFS_ACL_NOALLOW &&
 			    pacep[i].a_type == ALLOW)
 				continue;
 
--- a/usr/src/uts/common/fs/zfs/zfs_ctldir.c	Mon Sep 04 19:32:13 2006 -0700
+++ b/usr/src/uts/common/fs/zfs/zfs_ctldir.c	Tue Sep 05 11:37:36 2006 -0700
@@ -513,7 +513,7 @@
 	err = zfsctl_snapshot_zname(sdvp, snm, MAXNAMELEN, from);
 	if (err)
 		return (err);
-	err = zfs_secpolicy_write(from, NULL, cr);
+	err = zfs_secpolicy_write(from, cr);
 	if (err)
 		return (err);
 
@@ -558,7 +558,7 @@
 	err = zfsctl_snapshot_zname(dvp, name, MAXNAMELEN, snapname);
 	if (err)
 		return (err);
-	err = zfs_secpolicy_write(snapname, NULL, cr);
+	err = zfs_secpolicy_write(snapname, cr);
 	if (err)
 		return (err);
 
--- a/usr/src/uts/common/fs/zfs/zfs_ioctl.c	Mon Sep 04 19:32:13 2006 -0700
+++ b/usr/src/uts/common/fs/zfs/zfs_ioctl.c	Tue Sep 05 11:37:36 2006 -0700
@@ -58,6 +58,7 @@
 #include <sys/zfs_ctldir.h>
 
 #include "zfs_namecheck.h"
+#include "zfs_prop.h"
 
 extern struct modlfs zfs_modlfs;
 
@@ -68,7 +69,7 @@
 dev_info_t *zfs_dip;
 
 typedef int zfs_ioc_func_t(zfs_cmd_t *);
-typedef int zfs_secpolicy_func_t(const char *, const char *, cred_t *);
+typedef int zfs_secpolicy_func_t(const char *, cred_t *);
 
 typedef struct zfs_ioc_vec {
 	zfs_ioc_func_t		*zvec_func;
@@ -122,7 +123,7 @@
  */
 /* ARGSUSED */
 static int
-zfs_secpolicy_none(const char *unused1, const char *unused2, cred_t *cr)
+zfs_secpolicy_none(const char *unused1, cred_t *cr)
 {
 	return (0);
 }
@@ -133,7 +134,7 @@
  */
 /* ARGSUSED */
 static int
-zfs_secpolicy_read(const char *dataset, const char *unused, cred_t *cr)
+zfs_secpolicy_read(const char *dataset, cred_t *cr)
 {
 	if (INGLOBALZONE(curproc) ||
 	    zone_dataset_visible(dataset, NULL))
@@ -184,9 +185,8 @@
  * Policy for dataset write operations (create children, set properties, etc).
  * Requires SYS_MOUNT privilege, and must be writable in the local zone.
  */
-/* ARGSUSED */
 int
-zfs_secpolicy_write(const char *dataset, const char *unused, cred_t *cr)
+zfs_secpolicy_write(const char *dataset, cred_t *cr)
 {
 	int error;
 
@@ -201,7 +201,7 @@
  * create, destroy, snapshot, clone, restore.
  */
 static int
-zfs_secpolicy_parent(const char *dataset, const char *unused, cred_t *cr)
+zfs_secpolicy_parent(const char *dataset, cred_t *cr)
 {
 	char parentname[MAXNAMELEN];
 	char *cp;
@@ -221,65 +221,7 @@
 
 	}
 
-	return (zfs_secpolicy_write(parentname, unused, cr));
-}
-
-/*
- * Policy for dataset write operations (create children, set properties, etc).
- * Requires SYS_MOUNT privilege, and must be writable in the local zone.
- */
-static int
-zfs_secpolicy_setprop(const char *dataset, const char *prop, cred_t *cr)
-{
-	int error;
-
-	if (error = zfs_dozonecheck(dataset, cr))
-		return (error);
-
-	if (strcmp(prop, "zoned") == 0) {
-		/*
-		 * Disallow setting of 'zoned' from within a local zone.
-		 */
-		if (!INGLOBALZONE(curproc))
-			return (EPERM);
-	}
-
-	return (secpolicy_zfs(cr));
-}
-
-/*
- * Security policy for setting the quota.  This is the same as
- * zfs_secpolicy_write, except that the local zone may not change the quota at
- * the zone-property setpoint.
- */
-/* ARGSUSED */
-static int
-zfs_secpolicy_quota(const char *dataset, const char *unused, cred_t *cr)
-{
-	int error;
-
-	if (error = zfs_dozonecheck(dataset, cr))
-		return (error);
-
-	if (!INGLOBALZONE(curproc)) {
-		uint64_t zoned;
-		char setpoint[MAXNAMELEN];
-		int dslen;
-		/*
-		 * Unprivileged users are allowed to modify the quota
-		 * on things *under* (ie. contained by) the thing they
-		 * own.
-		 */
-		if (dsl_prop_get_integer(dataset, "zoned", &zoned, setpoint))
-			return (EPERM);
-		if (!zoned) /* this shouldn't happen */
-			return (EPERM);
-		dslen = strlen(dataset);
-		if (dslen <= strlen(setpoint))
-			return (EPERM);
-	}
-
-	return (secpolicy_zfs(cr));
+	return (zfs_secpolicy_write(parentname, cr));
 }
 
 /*
@@ -288,7 +230,7 @@
  */
 /* ARGSUSED */
 static int
-zfs_secpolicy_config(const char *unused, const char *unused2, cred_t *cr)
+zfs_secpolicy_config(const char *unused, cred_t *cr)
 {
 	if (secpolicy_sys_config(cr, B_FALSE) != 0)
 		return (EPERM);
@@ -301,7 +243,7 @@
  */
 /* ARGSUSED */
 static int
-zfs_secpolicy_inject(const char *unused, const char *unused2, cred_t *cr)
+zfs_secpolicy_inject(const char *unused, cred_t *cr)
 {
 	return (secpolicy_zinject(cr));
 }
@@ -310,7 +252,7 @@
  * Returns the nvlist as specified by the user in the zfs_cmd_t.
  */
 static int
-get_config(zfs_cmd_t *zc, nvlist_t **nvp)
+get_nvlist(zfs_cmd_t *zc, nvlist_t **nvp)
 {
 	char *packed;
 	size_t size;
@@ -318,16 +260,14 @@
 	nvlist_t *config = NULL;
 
 	/*
-	 * Read in and unpack the user-supplied nvlist.  By this point, we know
-	 * that the user has the SYS_CONFIG privilege, so allocating arbitrary
-	 * sized regions of memory should not be a problem.
+	 * Read in and unpack the user-supplied nvlist.
 	 */
-	if ((size = zc->zc_config_src_size) == 0)
+	if ((size = zc->zc_nvlist_src_size) == 0)
 		return (EINVAL);
 
 	packed = kmem_alloc(size, KM_SLEEP);
 
-	if ((error = xcopyin((void *)(uintptr_t)zc->zc_config_src, packed,
+	if ((error = xcopyin((void *)(uintptr_t)zc->zc_nvlist_src, packed,
 	    size)) != 0) {
 		kmem_free(packed, size);
 		return (error);
@@ -345,16 +285,39 @@
 }
 
 static int
+put_nvlist(zfs_cmd_t *zc, nvlist_t *nvl)
+{
+	char *packed = NULL;
+	size_t size;
+	int error;
+
+	VERIFY(nvlist_size(nvl, &size, NV_ENCODE_NATIVE) == 0);
+
+	if (size > zc->zc_nvlist_dst_size) {
+		error = ENOMEM;
+	} else {
+		VERIFY(nvlist_pack(nvl, &packed, &size, NV_ENCODE_NATIVE,
+		    KM_SLEEP) == 0);
+		error = xcopyout(packed, (void *)(uintptr_t)zc->zc_nvlist_dst,
+		    size);
+		kmem_free(packed, size);
+	}
+
+	zc->zc_nvlist_dst_size = size;
+	return (error);
+}
+
+static int
 zfs_ioc_pool_create(zfs_cmd_t *zc)
 {
 	int error;
 	nvlist_t *config;
 
-	if ((error = get_config(zc, &config)) != 0)
+	if ((error = get_nvlist(zc, &config)) != 0)
 		return (error);
 
-	error = spa_create(zc->zc_name, config, zc->zc_root[0] == '\0' ?
-	    NULL : zc->zc_root);
+	error = spa_create(zc->zc_name, config, zc->zc_value[0] == '\0' ?
+	    NULL : zc->zc_value);
 
 	nvlist_free(config);
 
@@ -374,7 +337,7 @@
 	nvlist_t *config;
 	uint64_t guid;
 
-	if ((error = get_config(zc, &config)) != 0)
+	if ((error = get_nvlist(zc, &config)) != 0)
 		return (error);
 
 	if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID, &guid) != 0 ||
@@ -382,7 +345,7 @@
 		error = EINVAL;
 	else
 		error = spa_import(zc->zc_name, config,
-		    zc->zc_root[0] == '\0' ? NULL : zc->zc_root);
+		    zc->zc_value[0] == '\0' ? NULL : zc->zc_value);
 
 	nvlist_free(config);
 
@@ -399,25 +362,13 @@
 zfs_ioc_pool_configs(zfs_cmd_t *zc)
 {
 	nvlist_t *configs;
-	char *packed = NULL;
-	size_t size = 0;
 	int error;
 
 	if ((configs = spa_all_configs(&zc->zc_cookie)) == NULL)
 		return (EEXIST);
 
-	VERIFY(nvlist_pack(configs, &packed, &size, NV_ENCODE_NATIVE,
-	    KM_SLEEP) == 0);
+	error = put_nvlist(zc, configs);
 
-	if (size > zc->zc_config_dst_size)
-		error = ENOMEM;
-	else
-		error = xcopyout(packed, (void *)(uintptr_t)zc->zc_config_dst,
-		    size);
-
-	zc->zc_config_dst_size = size;
-
-	kmem_free(packed, size);
 	nvlist_free(configs);
 
 	return (error);
@@ -427,27 +378,14 @@
 zfs_ioc_pool_stats(zfs_cmd_t *zc)
 {
 	nvlist_t *config;
-	char *packed = NULL;
-	size_t size = 0;
 	int error;
 	int ret = 0;
 
-	error = spa_get_stats(zc->zc_name, &config, zc->zc_root,
-	    sizeof (zc->zc_root));
+	error = spa_get_stats(zc->zc_name, &config, zc->zc_value,
+	    sizeof (zc->zc_value));
 
 	if (config != NULL) {
-		VERIFY(nvlist_pack(config, &packed, &size,
-		    NV_ENCODE_NATIVE, KM_SLEEP) == 0);
-
-		if (size > zc->zc_config_dst_size)
-			ret = ENOMEM;
-		else if (xcopyout(packed, (void *)(uintptr_t)zc->zc_config_dst,
-		    size))
-			ret = EFAULT;
-
-		zc->zc_config_dst_size = size;
-
-		kmem_free(packed, size);
+		ret = put_nvlist(zc, config);
 		nvlist_free(config);
 
 		/*
@@ -471,11 +409,9 @@
 zfs_ioc_pool_tryimport(zfs_cmd_t *zc)
 {
 	nvlist_t *tryconfig, *config;
-	char *packed = NULL;
-	size_t size = 0;
 	int error;
 
-	if ((error = get_config(zc, &tryconfig)) != 0)
+	if ((error = get_nvlist(zc, &tryconfig)) != 0)
 		return (error);
 
 	config = spa_tryimport(tryconfig);
@@ -485,18 +421,7 @@
 	if (config == NULL)
 		return (EINVAL);
 
-	VERIFY(nvlist_pack(config, &packed, &size, NV_ENCODE_NATIVE,
-	    KM_SLEEP) == 0);
-
-	if (size > zc->zc_config_dst_size)
-		error = ENOMEM;
-	else
-		error = xcopyout(packed, (void *)(uintptr_t)zc->zc_config_dst,
-		    size);
-
-	zc->zc_config_dst_size = size;
-
-	kmem_free(packed, size);
+	error = put_nvlist(zc, config);
 	nvlist_free(config);
 
 	return (error);
@@ -555,7 +480,7 @@
 	if (error != 0)
 		return (error);
 
-	if ((error = get_config(zc, &config)) == 0) {
+	if ((error = get_nvlist(zc, &config)) == 0) {
 		error = spa_vdev_add(spa, config);
 		nvlist_free(config);
 	}
@@ -619,7 +544,7 @@
 	if (error != 0)
 		return (error);
 
-	if ((error = get_config(zc, &config)) == 0) {
+	if ((error = get_nvlist(zc, &config)) == 0) {
 		error = spa_vdev_attach(spa, zc->zc_guid, config, replacing);
 		nvlist_free(config);
 	}
@@ -648,7 +573,7 @@
 zfs_ioc_vdev_setpath(zfs_cmd_t *zc)
 {
 	spa_t *spa;
-	char *path = zc->zc_prop_value;
+	char *path = zc->zc_value;
 	uint64_t guid = zc->zc_guid;
 	int error;
 
@@ -668,8 +593,6 @@
 	objset_t *os = NULL;
 	int error;
 	nvlist_t *nv;
-	size_t sz;
-	char *buf;
 
 retry:
 	error = dmu_objset_open(zc->zc_name, DMU_OST_ANY,
@@ -691,27 +614,16 @@
 
 	dmu_objset_stats(os, &zc->zc_objset_stats);
 
-	if (zc->zc_config_src != NULL &&
+	if (zc->zc_nvlist_dst != NULL &&
 	    (error = dsl_prop_get_all(os, &nv)) == 0) {
-		VERIFY(nvlist_size(nv, &sz, NV_ENCODE_NATIVE) == 0);
-		if (sz > zc->zc_config_src_size) {
-			zc->zc_config_src_size = sz;
-			error = ENOMEM;
-		} else {
-			buf = kmem_alloc(sz, KM_SLEEP);
-			VERIFY(nvlist_pack(nv, &buf, &sz,
-			    NV_ENCODE_NATIVE, 0) == 0);
-			error = xcopyout(buf,
-			    (void *)(uintptr_t)zc->zc_config_src, sz);
-			kmem_free(buf, sz);
-		}
+		error = put_nvlist(zc, nv);
 		nvlist_free(nv);
 	}
 
 	if (!error && zc->zc_objset_stats.dds_type == DMU_OST_ZVOL)
-		error = zvol_get_stats(zc, os);
+		error = zvol_get_stats(os, &zc->zc_vol_stats);
 
-	spa_altroot(dmu_objset_spa(os), zc->zc_root, sizeof (zc->zc_root));
+	spa_altroot(dmu_objset_spa(os), zc->zc_value, sizeof (zc->zc_value));
 
 	dmu_objset_close(os);
 	return (error);
@@ -818,46 +730,215 @@
 }
 
 static int
-zfs_ioc_set_prop(zfs_cmd_t *zc)
+zfs_set_prop_nvlist(const char *name, dev_t dev, cred_t *cr, nvlist_t *nvl)
 {
-	return (dsl_prop_set(zc->zc_name, zc->zc_prop_name,
-	    zc->zc_intsz, zc->zc_numints, zc->zc_prop_value));
-}
+	nvpair_t *elem;
+	int error;
+	const char *propname;
+	zfs_prop_t prop;
+	uint64_t intval;
+	char *strval;
+
+	elem = NULL;
+	while ((elem = nvlist_next_nvpair(nvl, elem)) != NULL) {
+		propname = nvpair_name(elem);
+
+		if ((prop = zfs_name_to_prop(propname)) ==
+		    ZFS_PROP_INVAL) {
+			/*
+			 * If this is a user-defined property, it must be a
+			 * string, and there is no further validation to do.
+			 */
+			if (!zfs_prop_user(propname) ||
+			    nvpair_type(elem) != DATA_TYPE_STRING)
+				return (EINVAL);
+
+			VERIFY(nvpair_value_string(elem, &strval) == 0);
+			error = dsl_prop_set(name, propname, 1,
+			    strlen(strval) + 1, strval);
+			if (error == 0)
+				continue;
+			else
+				break;
+		}
+
+		/*
+		 * Check permissions for special properties.
+		 */
+		switch (prop) {
+		case ZFS_PROP_ZONED:
+			/*
+			 * Disallow setting of 'zoned' from within a local zone.
+			 */
+			if (!INGLOBALZONE(curproc))
+				return (EPERM);
+			break;
+
+		case ZFS_PROP_QUOTA:
+			if (error = zfs_dozonecheck(name, cr))
+				return (error);
+
+			if (!INGLOBALZONE(curproc)) {
+				uint64_t zoned;
+				char setpoint[MAXNAMELEN];
+				int dslen;
+				/*
+				 * Unprivileged users are allowed to modify the
+				 * quota on things *under* (ie. contained by)
+				 * the thing they own.
+				 */
+				if (dsl_prop_get_integer(name, "zoned", &zoned,
+				    setpoint))
+					return (EPERM);
+				if (!zoned) /* this shouldn't happen */
+					return (EPERM);
+				dslen = strlen(name);
+				if (dslen <= strlen(setpoint))
+					return (EPERM);
+			}
+		}
+
+		switch (prop) {
+		case ZFS_PROP_QUOTA:
+			if ((error = nvpair_value_uint64(elem, &intval)) != 0 ||
+			    (error = dsl_dir_set_quota(name,
+			    intval)) != 0)
+				return (error);
+			break;
+
+		case ZFS_PROP_RESERVATION:
+			if ((error = nvpair_value_uint64(elem, &intval)) != 0 ||
+			    (error = dsl_dir_set_reservation(name,
+			    intval)) != 0)
+				return (error);
+			break;
 
-static int
-zfs_ioc_set_quota(zfs_cmd_t *zc)
-{
-	return (dsl_dir_set_quota(zc->zc_name, zc->zc_cookie));
+		case ZFS_PROP_VOLSIZE:
+			if ((error = nvpair_value_uint64(elem, &intval)) != 0 ||
+			    (error = zvol_set_volsize(name, dev,
+			    intval)) != 0)
+				return (error);
+			break;
+
+		case ZFS_PROP_VOLBLOCKSIZE:
+			if ((error = nvpair_value_uint64(elem, &intval)) != 0 ||
+			    (error = zvol_set_volblocksize(name,
+			    intval)) != 0)
+				return (error);
+			break;
+
+		default:
+			if (nvpair_type(elem) == DATA_TYPE_STRING) {
+				if (zfs_prop_get_type(prop) !=
+				    prop_type_string)
+					return (EINVAL);
+				ASSERT(nvpair_value_string(elem, &strval) == 0);
+				error = dsl_prop_set(name,
+				    nvpair_name(elem), 1, strlen(strval) + 1,
+				    strval);
+			} else if (nvpair_type(elem) == DATA_TYPE_UINT64) {
+				ASSERT(nvpair_value_uint64(elem, &intval) == 0);
+
+				switch (zfs_prop_get_type(prop)) {
+				case prop_type_number:
+					break;
+				case prop_type_boolean:
+					if (intval > 1)
+						error = EINVAL;
+					break;
+				case prop_type_string:
+					error = EINVAL;
+					break;
+				case prop_type_index:
+					switch (prop) {
+					case ZFS_PROP_CHECKSUM:
+						if (intval >=
+						    ZIO_CHECKSUM_FUNCTIONS)
+							error = EINVAL;
+						break;
+					case ZFS_PROP_COMPRESSION:
+						if (intval >=
+						    ZIO_COMPRESS_FUNCTIONS)
+							error = EINVAL;
+						break;
+					case ZFS_PROP_SNAPDIR:
+						if (intval >
+						    ZFS_SNAPDIR_VISIBLE)
+							error = EINVAL;
+						break;
+					case ZFS_PROP_ACLMODE:
+						if (intval >
+						    ZFS_ACL_PASSTHROUGH)
+							error = EINVAL;
+						break;
+					case ZFS_PROP_ACLINHERIT:
+						if (intval > ZFS_ACL_SECURE ||
+						    intval == ZFS_ACL_GROUPMASK)
+							error = EINVAL;
+						break;
+					default:
+						cmn_err(CE_PANIC,
+						    "unknown index property");
+					}
+					break;
+				default:
+					cmn_err(CE_PANIC, "unknown property "
+					    "type");
+					break;
+				}
+
+				error = dsl_prop_set(name, propname, 8, 1,
+				    &intval);
+			} else {
+				return (EINVAL);
+			}
+			break;
+		}
+	}
+
+	return (0);
 }
 
 static int
-zfs_ioc_set_reservation(zfs_cmd_t *zc)
-{
-	return (dsl_dir_set_reservation(zc->zc_name, zc->zc_cookie));
-}
-
-static int
-zfs_ioc_set_volsize(zfs_cmd_t *zc)
+zfs_ioc_set_prop(zfs_cmd_t *zc)
 {
-	return (zvol_set_volsize(zc));
-}
+	nvlist_t *nvl;
+	int error;
+	zfs_prop_t prop;
 
-static int
-zfs_ioc_set_volblocksize(zfs_cmd_t *zc)
-{
-	return (zvol_set_volblocksize(zc));
+	/*
+	 * If zc_value is set, then this is an attempt to inherit a value.
+	 * Otherwise, zc_nvlist refers to a list of properties to set.
+	 */
+	if (zc->zc_value[0] != '\0') {
+		if (!zfs_prop_user(zc->zc_value) &&
+		    ((prop = zfs_name_to_prop(zc->zc_value)) ==
+		    ZFS_PROP_INVAL ||
+		    !zfs_prop_inheritable(prop)))
+			return (EINVAL);
+
+		return (dsl_prop_set(zc->zc_name, zc->zc_value, 0, 0, NULL));
+	}
+
+	if ((error = get_nvlist(zc, &nvl)) != 0)
+		return (error);
+
+	error = zfs_set_prop_nvlist(zc->zc_name, zc->zc_dev,
+	    (cred_t *)(uintptr_t)zc->zc_cred, nvl);
+	nvlist_free(nvl);
+	return (error);
 }
 
 static int
 zfs_ioc_create_minor(zfs_cmd_t *zc)
 {
-	return (zvol_create_minor(zc));
+	return (zvol_create_minor(zc->zc_name, zc->zc_dev));
 }
 
 static int
 zfs_ioc_remove_minor(zfs_cmd_t *zc)
 {
-	return (zvol_remove_minor(zc));
+	return (zvol_remove_minor(zc->zc_name));
 }
 
 /*
@@ -888,7 +969,7 @@
 static void
 zfs_create_cb(objset_t *os, void *arg, dmu_tx_t *tx)
 {
-	zfs_cmd_t *zc = arg;
+	zfs_create_data_t *zc = arg;
 	zfs_create_fs(os, (cred_t *)(uintptr_t)zc->zc_cred, tx);
 }
 
@@ -897,6 +978,7 @@
 {
 	objset_t *clone;
 	int error = 0;
+	zfs_create_data_t cbdata = { 0 };
 	void (*cbfunc)(objset_t *os, void *arg, dmu_tx_t *tx);
 	dmu_objset_type_t type = zc->zc_objset_type;
 
@@ -916,47 +998,93 @@
 	if (strchr(zc->zc_name, '@'))
 		return (EINVAL);
 
-	if (zc->zc_filename[0] != '\0') {
+	if (zc->zc_nvlist_src != NULL &&
+	    (error = get_nvlist(zc, &cbdata.zc_props)) != 0)
+		return (error);
+
+	cbdata.zc_cred = (cred_t *)(uintptr_t)zc->zc_cred;
+	cbdata.zc_dev = (dev_t)zc->zc_dev;
+
+	if (zc->zc_value[0] != '\0') {
 		/*
 		 * We're creating a clone of an existing snapshot.
 		 */
-		zc->zc_filename[sizeof (zc->zc_filename) - 1] = '\0';
-		if (dataset_namecheck(zc->zc_filename, NULL, NULL) != 0)
+		zc->zc_value[sizeof (zc->zc_value) - 1] = '\0';
+		if (dataset_namecheck(zc->zc_value, NULL, NULL) != 0) {
+			nvlist_free(cbdata.zc_props);
 			return (EINVAL);
+		}
 
-		error = dmu_objset_open(zc->zc_filename, type,
+		error = dmu_objset_open(zc->zc_value, type,
 		    DS_MODE_STANDARD | DS_MODE_READONLY, &clone);
-		if (error)
+		if (error) {
+			nvlist_free(cbdata.zc_props);
 			return (error);
+		}
 		error = dmu_objset_create(zc->zc_name, type, clone, NULL, NULL);
 		dmu_objset_close(clone);
 	} else {
-		if (cbfunc == NULL)
+		if (cbfunc == NULL) {
+			nvlist_free(cbdata.zc_props);
 			return (EINVAL);
-		/*
-		 * We're creating a new dataset.
-		 */
+		}
+
 		if (type == DMU_OST_ZVOL) {
+			uint64_t volsize, volblocksize;
+
+			if (cbdata.zc_props == NULL ||
+			    nvlist_lookup_uint64(cbdata.zc_props,
+			    zfs_prop_to_name(ZFS_PROP_VOLSIZE),
+			    &volsize) != 0) {
+				nvlist_free(cbdata.zc_props);
+				return (EINVAL);
+			}
+
+			if ((error = nvlist_lookup_uint64(cbdata.zc_props,
+			    zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE),
+			    &volblocksize)) != 0 && error != ENOENT) {
+				nvlist_free(cbdata.zc_props);
+				return (EINVAL);
+			}
 
-			if ((error = zvol_check_volblocksize(zc)) != 0)
+			if (error != 0)
+				volblocksize = zfs_prop_default_numeric(
+				    ZFS_PROP_VOLBLOCKSIZE);
+
+			if ((error = zvol_check_volblocksize(
+			    volblocksize)) != 0 ||
+			    (error = zvol_check_volsize(volsize,
+			    volblocksize)) != 0) {
+				nvlist_free(cbdata.zc_props);
 				return (error);
+			}
+		}
 
-			if ((error = zvol_check_volsize(zc,
-			    zc->zc_volblocksize)) != 0)
-				return (error);
-		}
-		error = dmu_objset_create(zc->zc_name, type, NULL, cbfunc, zc);
+		error = dmu_objset_create(zc->zc_name, type, NULL, cbfunc,
+		    &cbdata);
 	}
+
+	/*
+	 * It would be nice to do this atomically.
+	 */
+	if (error == 0) {
+		if ((error = zfs_set_prop_nvlist(zc->zc_name,
+		    zc->zc_dev, (cred_t *)(uintptr_t)zc->zc_cred,
+		    cbdata.zc_props)) != 0)
+			(void) dmu_objset_destroy(zc->zc_name);
+	}
+
+	nvlist_free(cbdata.zc_props);
 	return (error);
 }
 
 static int
 zfs_ioc_snapshot(zfs_cmd_t *zc)
 {
-	if (snapshot_namecheck(zc->zc_prop_value, NULL, NULL) != 0)
+	if (snapshot_namecheck(zc->zc_value, NULL, NULL) != 0)
 		return (EINVAL);
 	return (dmu_objset_snapshot(zc->zc_name,
-	    zc->zc_prop_value, zc->zc_cookie));
+	    zc->zc_value, zc->zc_cookie));
 }
 
 static int
@@ -1004,13 +1132,13 @@
 {
 	int err;
 
-	if (snapshot_namecheck(zc->zc_prop_value, NULL, NULL) != 0)
+	if (snapshot_namecheck(zc->zc_value, NULL, NULL) != 0)
 		return (EINVAL);
 	err = dmu_objset_find(zc->zc_name,
-	    zfs_unmount_snap, zc->zc_prop_value, DS_FIND_CHILDREN);
+	    zfs_unmount_snap, zc->zc_value, DS_FIND_CHILDREN);
 	if (err)
 		return (err);
-	return (dmu_snapshots_destroy(zc->zc_name, zc->zc_prop_value));
+	return (dmu_snapshots_destroy(zc->zc_name, zc->zc_value));
 }
 
 static int
@@ -1034,8 +1162,8 @@
 static int
 zfs_ioc_rename(zfs_cmd_t *zc)
 {
-	zc->zc_prop_value[sizeof (zc->zc_prop_value) - 1] = '\0';
-	if (dataset_namecheck(zc->zc_prop_value, NULL, NULL) != 0)
+	zc->zc_value[sizeof (zc->zc_value) - 1] = '\0';
+	if (dataset_namecheck(zc->zc_value, NULL, NULL) != 0)
 		return (EINVAL);
 
 	if (strchr(zc->zc_name, '@') != NULL &&
@@ -1045,7 +1173,7 @@
 			return (err);
 	}
 
-	return (dmu_objset_rename(zc->zc_name, zc->zc_prop_value));
+	return (dmu_objset_rename(zc->zc_name, zc->zc_value));
 }
 
 static int
@@ -1058,8 +1186,8 @@
 	fp = getf(fd);
 	if (fp == NULL)
 		return (EBADF);
-	error = dmu_recvbackup(zc->zc_filename, &zc->zc_begin_record,
-	    &zc->zc_cookie, (boolean_t)zc->zc_numints, fp->f_vnode,
+	error = dmu_recvbackup(zc->zc_value, &zc->zc_begin_record,
+	    &zc->zc_cookie, (boolean_t)zc->zc_guid, fp->f_vnode,
 	    fp->f_offset);
 	releasef(fd);
 	return (error);
@@ -1078,8 +1206,8 @@
 	if (error)
 		return (error);
 
-	if (zc->zc_prop_value[0] != '\0') {
-		error = dmu_objset_open(zc->zc_prop_value, DMU_OST_ANY,
+	if (zc->zc_value[0] != '\0') {
+		error = dmu_objset_open(zc->zc_value, DMU_OST_ANY,
 		    DS_MODE_STANDARD | DS_MODE_READONLY, &fromsnap);
 		if (error) {
 			dmu_objset_close(tosnap);
@@ -1143,17 +1271,17 @@
 {
 	spa_t *spa;
 	int error;
-	size_t count = (size_t)zc->zc_config_dst_size;
+	size_t count = (size_t)zc->zc_nvlist_dst_size;
 
 	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
 		return (error);
 
-	error = spa_get_errlog(spa, (void *)(uintptr_t)zc->zc_config_dst,
+	error = spa_get_errlog(spa, (void *)(uintptr_t)zc->zc_nvlist_dst,
 	    &count);
 	if (error == 0)
-		zc->zc_config_dst_size = count;
+		zc->zc_nvlist_dst_size = count;
 	else
-		zc->zc_config_dst_size = spa_get_errlog_size(spa);
+		zc->zc_nvlist_dst_size = spa_get_errlog_size(spa);
 
 	spa_close(spa, FTAG);
 
@@ -1172,9 +1300,9 @@
 
 	spa_config_enter(spa, RW_WRITER, FTAG);
 
-	if (zc->zc_prop_value[0] == '\0')
+	if (zc->zc_guid == 0) {
 		vd = NULL;
-	else if ((vd = spa_lookup_by_guid(spa, zc->zc_guid)) == NULL) {
+	} else if ((vd = spa_lookup_by_guid(spa, zc->zc_guid)) == NULL) {
 		spa_config_exit(spa, FTAG);
 		spa_close(spa, FTAG);
 		return (ENODEV);
@@ -1194,14 +1322,17 @@
 {
 	spa_t *spa;
 	int error;
+	nvlist_t *nvl;
 
 	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
 		return (error);
 
-	error = spa_bookmark_name(spa, &zc->zc_bookmark,
-	    zc->zc_prop_name, sizeof (zc->zc_prop_name), zc->zc_prop_value,
-	    sizeof (zc->zc_prop_value), zc->zc_filename,
-	    sizeof (zc->zc_filename));
+	VERIFY(nvlist_alloc(&nvl, NV_UNIQUE_NAME, KM_SLEEP) == 0);
+
+	error = spa_bookmark_name(spa, &zc->zc_bookmark, nvl);
+	if (error == 0)
+		error = put_nvlist(zc, nvl);
+	nvlist_free(nvl);
 
 	spa_close(spa, FTAG);
 
@@ -1217,10 +1348,10 @@
 	 * We don't need to unmount *all* the origin fs's snapshots, but
 	 * it's easier.
 	 */
-	cp = strchr(zc->zc_prop_value, '@');
+	cp = strchr(zc->zc_value, '@');
 	if (cp)
 		*cp = '\0';
-	(void) dmu_objset_find(zc->zc_prop_value,
+	(void) dmu_objset_find(zc->zc_value,
 	    zfs_unmount_snap, NULL, DS_FIND_SNAPSHOTS);
 	return (dsl_dataset_promote(zc->zc_name));
 }
@@ -1246,11 +1377,7 @@
 	{ zfs_ioc_objset_stats,		zfs_secpolicy_read,	dataset_name },
 	{ zfs_ioc_dataset_list_next,	zfs_secpolicy_read,	dataset_name },
 	{ zfs_ioc_snapshot_list_next,	zfs_secpolicy_read,	dataset_name },
-	{ zfs_ioc_set_prop,		zfs_secpolicy_setprop,	dataset_name },
-	{ zfs_ioc_set_quota,		zfs_secpolicy_quota,	dataset_name },
-	{ zfs_ioc_set_reservation,	zfs_secpolicy_write,	dataset_name },
-	{ zfs_ioc_set_volsize,		zfs_secpolicy_config,	dataset_name },
-	{ zfs_ioc_set_volblocksize,	zfs_secpolicy_config,	dataset_name },
+	{ zfs_ioc_set_prop,		zfs_secpolicy_write,	dataset_name },
 	{ zfs_ioc_create_minor,		zfs_secpolicy_config,	dataset_name },
 	{ zfs_ioc_remove_minor,		zfs_secpolicy_config,	dataset_name },
 	{ zfs_ioc_create,		zfs_secpolicy_parent,	dataset_name },
@@ -1292,8 +1419,7 @@
 	if (error == 0) {
 		zc->zc_cred = (uintptr_t)cr;
 		zc->zc_dev = dev;
-		error = zfs_ioc_vec[vec].zvec_secpolicy(zc->zc_name,
-		    zc->zc_prop_name, cr);
+		error = zfs_ioc_vec[vec].zvec_secpolicy(zc->zc_name, cr);
 	}
 
 	/*
@@ -1421,7 +1547,8 @@
 };
 
 static struct modldrv zfs_modldrv = {
-	&mod_driverops, "ZFS storage pool version 1", &zfs_dev_ops
+	&mod_driverops, "ZFS storage pool version " ZFS_VERSION_STRING,
+	    &zfs_dev_ops
 };
 
 static struct modlinkage modlinkage = {
--- a/usr/src/uts/common/fs/zfs/zfs_vfsops.c	Mon Sep 04 19:32:13 2006 -0700
+++ b/usr/src/uts/common/fs/zfs/zfs_vfsops.c	Tue Sep 05 11:37:36 2006 -0700
@@ -1237,5 +1237,5 @@
 };
 
 struct modlfs zfs_modlfs = {
-	&mod_fsops, "ZFS filesystem version 1", &vfw
+	&mod_fsops, "ZFS filesystem version " ZFS_VERSION_STRING, &vfw
 };
--- a/usr/src/uts/common/fs/zfs/zvol.c	Mon Sep 04 19:32:13 2006 -0700
+++ b/usr/src/uts/common/fs/zfs/zvol.c	Tue Sep 05 11:37:36 2006 -0700
@@ -116,27 +116,27 @@
 }
 
 int
-zvol_check_volsize(zfs_cmd_t *zc, uint64_t blocksize)
+zvol_check_volsize(uint64_t volsize, uint64_t blocksize)
 {
-	if (zc->zc_volsize == 0)
+	if (volsize == 0)
 		return (EINVAL);
 
-	if (zc->zc_volsize % blocksize != 0)
+	if (volsize % blocksize != 0)
 		return (EINVAL);
 
 #ifdef _ILP32
-	if (zc->zc_volsize - 1 > SPEC_MAXOFFSET_T)
+	if (volsize - 1 > SPEC_MAXOFFSET_T)
 		return (EOVERFLOW);
 #endif
 	return (0);
 }
 
 int
-zvol_check_volblocksize(zfs_cmd_t *zc)
+zvol_check_volblocksize(uint64_t volblocksize)
 {
-	if (zc->zc_volblocksize < SPA_MINBLOCKSIZE ||
-	    zc->zc_volblocksize > SPA_MAXBLOCKSIZE ||
-	    !ISP2(zc->zc_volblocksize))
+	if (volblocksize < SPA_MINBLOCKSIZE ||
+	    volblocksize > SPA_MAXBLOCKSIZE ||
+	    !ISP2(volblocksize))
 		return (EDOM);
 
 	return (0);
@@ -151,12 +151,12 @@
 }
 
 int
-zvol_get_stats(zfs_cmd_t *zc, objset_t *os)
+zvol_get_stats(objset_t *os, zvol_stats_t *zvs)
 {
 	int error;
 	dmu_object_info_t doi;
 
-	error = zap_lookup(os, ZVOL_ZAP_OBJ, "size", 8, 1, &zc->zc_volsize);
+	error = zap_lookup(os, ZVOL_ZAP_OBJ, "size", 8, 1, &zvs->zv_volsize);
 
 	if (error)
 		return (error);
@@ -164,7 +164,7 @@
 	error = dmu_object_info(os, ZVOL_OBJ, &doi);
 
 	if (error == 0)
-		zc->zc_volblocksize = doi.doi_data_block_size;
+		zvs->zv_volblocksize = doi.doi_data_block_size;
 
 	return (error);
 }
@@ -187,7 +187,7 @@
 }
 
 static zvol_state_t *
-zvol_minor_lookup(char *name)
+zvol_minor_lookup(const char *name)
 {
 	minor_t minor;
 	zvol_state_t *zv;
@@ -208,10 +208,26 @@
 void
 zvol_create_cb(objset_t *os, void *arg, dmu_tx_t *tx)
 {
-	zfs_cmd_t *zc = arg;
+	zfs_create_data_t *zc = arg;
 	int error;
+	uint64_t volblocksize, volsize;
 
-	error = dmu_object_claim(os, ZVOL_OBJ, DMU_OT_ZVOL, zc->zc_volblocksize,
+	VERIFY(nvlist_lookup_uint64(zc->zc_props,
+	    zfs_prop_to_name(ZFS_PROP_VOLSIZE), &volsize) == 0);
+	if (nvlist_lookup_uint64(zc->zc_props,
+	    zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE), &volblocksize) != 0)
+		volblocksize = zfs_prop_default_numeric(ZFS_PROP_VOLBLOCKSIZE);
+
+	/*
+	 * These properites must be removed from the list so the generic
+	 * property setting step won't apply to them.
+	 */
+	VERIFY(nvlist_remove_all(zc->zc_props,
+	    zfs_prop_to_name(ZFS_PROP_VOLSIZE)) == 0);
+	(void) nvlist_remove_all(zc->zc_props,
+	    zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE));
+
+	error = dmu_object_claim(os, ZVOL_OBJ, DMU_OT_ZVOL, volblocksize,
 	    DMU_OT_NONE, 0, tx);
 	ASSERT(error == 0);
 
@@ -219,7 +235,7 @@
 	    DMU_OT_NONE, 0, tx);
 	ASSERT(error == 0);
 
-	error = zap_update(os, ZVOL_ZAP_OBJ, "size", 8, 1, &zc->zc_volsize, tx);
+	error = zap_update(os, ZVOL_ZAP_OBJ, "size", 8, 1, &volsize, tx);
 	ASSERT(error == 0);
 }
 
@@ -284,10 +300,8 @@
  * Create a minor node for the specified volume.
  */
 int
-zvol_create_minor(zfs_cmd_t *zc)
+zvol_create_minor(const char *name, dev_t dev)
 {
-	char *name = zc->zc_name;
-	dev_t dev = zc->zc_dev;
 	zvol_state_t *zv;
 	objset_t *os;
 	uint64_t volsize;
@@ -377,7 +391,8 @@
 		return (EAGAIN);
 	}
 
-	(void) ddi_prop_update_string(minor, zfs_dip, ZVOL_PROP_NAME, name);
+	(void) ddi_prop_update_string(minor, zfs_dip, ZVOL_PROP_NAME,
+	    (char *)name);
 
 	(void) sprintf(chrbuf, "%uc,raw", minor);
 
@@ -431,14 +446,14 @@
  * Remove minor node for the specified volume.
  */
 int
-zvol_remove_minor(zfs_cmd_t *zc)
+zvol_remove_minor(const char *name)
 {
 	zvol_state_t *zv;
 	char namebuf[30];
 
 	mutex_enter(&zvol_state_lock);
 
-	if ((zv = zvol_minor_lookup(zc->zc_name)) == NULL) {
+	if ((zv = zvol_minor_lookup(name)) == NULL) {
 		mutex_exit(&zvol_state_lock);
 		return (ENXIO);
 	}
@@ -472,23 +487,23 @@
 }
 
 int
-zvol_set_volsize(zfs_cmd_t *zc)
+zvol_set_volsize(const char *name, dev_t dev, uint64_t volsize)
 {
 	zvol_state_t *zv;
-	dev_t dev = zc->zc_dev;
 	dmu_tx_t *tx;
 	int error;
 	dmu_object_info_t doi;
 
 	mutex_enter(&zvol_state_lock);
 
-	if ((zv = zvol_minor_lookup(zc->zc_name)) == NULL) {
+	if ((zv = zvol_minor_lookup(name)) == NULL) {
 		mutex_exit(&zvol_state_lock);
 		return (ENXIO);
 	}
 
 	if ((error = dmu_object_info(zv->zv_objset, ZVOL_OBJ, &doi)) != 0 ||
-	    (error = zvol_check_volsize(zc, doi.doi_data_block_size)) != 0) {
+	    (error = zvol_check_volsize(volsize,
+	    doi.doi_data_block_size)) != 0) {
 		mutex_exit(&zvol_state_lock);
 		return (error);
 	}
@@ -500,7 +515,7 @@
 
 	tx = dmu_tx_create(zv->zv_objset);
 	dmu_tx_hold_zap(tx, ZVOL_ZAP_OBJ, TRUE, NULL);
-	dmu_tx_hold_free(tx, ZVOL_OBJ, zc->zc_volsize, DMU_OBJECT_END);
+	dmu_tx_hold_free(tx, ZVOL_OBJ, volsize, DMU_OBJECT_END);
 	error = dmu_tx_assign(tx, TXG_WAIT);
 	if (error) {
 		dmu_tx_abort(tx);
@@ -509,16 +524,16 @@
 	}
 
 	error = zap_update(zv->zv_objset, ZVOL_ZAP_OBJ, "size", 8, 1,
-	    &zc->zc_volsize, tx);
+	    &volsize, tx);
 	if (error == 0) {
-		error = dmu_free_range(zv->zv_objset, ZVOL_OBJ, zc->zc_volsize,
+		error = dmu_free_range(zv->zv_objset, ZVOL_OBJ, volsize,
 		    DMU_OBJECT_END, tx);
 	}
 
 	dmu_tx_commit(tx);
 
 	if (error == 0) {
-		zv->zv_volsize = zc->zc_volsize;
+		zv->zv_volsize = volsize;
 		zvol_size_changed(zv, dev);
 	}
 
@@ -528,7 +543,7 @@
 }
 
 int
-zvol_set_volblocksize(zfs_cmd_t *zc)
+zvol_set_volblocksize(const char *name, uint64_t volblocksize)
 {
 	zvol_state_t *zv;
 	dmu_tx_t *tx;
@@ -536,7 +551,7 @@
 
 	mutex_enter(&zvol_state_lock);
 
-	if ((zv = zvol_minor_lookup(zc->zc_name)) == NULL) {
+	if ((zv = zvol_minor_lookup(name)) == NULL) {
 		mutex_exit(&zvol_state_lock);
 		return (ENXIO);
 	}
@@ -553,7 +568,7 @@
 		dmu_tx_abort(tx);
 	} else {
 		error = dmu_object_set_blocksize(zv->zv_objset, ZVOL_OBJ,
-		    zc->zc_volblocksize, 0, tx);
+		    volblocksize, 0, tx);
 		if (error == ENOTSUP)
 			error = EBUSY;
 		dmu_tx_commit(tx);
--- a/usr/src/uts/common/sys/fs/zfs.h	Mon Sep 04 19:32:13 2006 -0700
+++ b/usr/src/uts/common/sys/fs/zfs.h	Tue Sep 05 11:37:36 2006 -0700
@@ -84,6 +84,7 @@
 	ZFS_PROP_SNAPDIR,
 	ZFS_PROP_ACLMODE,
 	ZFS_PROP_ACLINHERIT,
+	ZFS_PROP_CANMOUNT,
 	/*
 	 * The following properties are not exposed to the user, but are
 	 * accessible by libzfs clients.
@@ -102,10 +103,12 @@
  * The following functions are shared between libzfs and the kernel.
  */
 zfs_prop_t zfs_name_to_prop(const char *);
+boolean_t zfs_prop_user(const char *);
 int zfs_prop_readonly(zfs_prop_t);
 const char *zfs_prop_default_string(zfs_prop_t);
+const char *zfs_prop_to_name(zfs_prop_t);
 uint64_t zfs_prop_default_numeric(zfs_prop_t);
-
+int zfs_prop_inheritable(zfs_prop_t);
 
 /*
  * On-disk version number.
@@ -114,6 +117,7 @@
 #define	ZFS_VERSION_2			2ULL
 #define	ZFS_VERSION_3			3ULL
 #define	ZFS_VERSION			ZFS_VERSION_3
+#define	ZFS_VERSION_STRING		"3"
 
 /*
  * Symbolic names for the changes that caused a ZFS_VERSION switch.
@@ -332,10 +336,6 @@
 	ZFS_IOC_DATASET_LIST_NEXT,
 	ZFS_IOC_SNAPSHOT_LIST_NEXT,
 	ZFS_IOC_SET_PROP,
-	ZFS_IOC_SET_QUOTA,
-	ZFS_IOC_SET_RESERVATION,
-	ZFS_IOC_SET_VOLSIZE,
-	ZFS_IOC_SET_VOLBLOCKSIZE,
 	ZFS_IOC_CREATE_MINOR,
 	ZFS_IOC_REMOVE_MINOR,
 	ZFS_IOC_CREATE,
@@ -365,6 +365,13 @@
 	SPA_LOAD_TRYIMPORT	/* tryimport in progress */
 } spa_load_state_t;
 
+/*
+ * Bookmark name values.
+ */
+#define	ZPOOL_ERR_DATASET	"dataset"
+#define	ZPOOL_ERR_OBJECT	"object"
+#define	ZPOOL_ERR_RANGE		"range"
+
 #ifdef	__cplusplus
 }
 #endif