6622831 normalization properties are not preserved by "zfs send"
authortimh
Tue, 20 Nov 2007 05:42:50 -0800
changeset 5498 334b476844ca
parent 5497 913e555b2de5
child 5499 6bfd7633d2ab
6622831 normalization properties are not preserved by "zfs send" 6625194 delegate 'clone' privilege broken 6626152 zfs should initialize the fuid lock it uses Contributed by Pawel Dawidek
deleted_files/usr/src/uts/common/fs/zfs/sys/zfs_i18n.h
usr/src/common/zfs/zfs_prop.c
usr/src/lib/libzfs/common/libzfs_dataset.c
usr/src/lib/libzpool/common/sys/zfs_context.h
usr/src/uts/common/fs/zfs/sys/zap_leaf.h
usr/src/uts/common/fs/zfs/sys/zfs_i18n.h
usr/src/uts/common/fs/zfs/sys/zfs_ioctl.h
usr/src/uts/common/fs/zfs/sys/zfs_vfsops.h
usr/src/uts/common/fs/zfs/sys/zfs_znode.h
usr/src/uts/common/fs/zfs/zap.c
usr/src/uts/common/fs/zfs/zap_leaf.c
usr/src/uts/common/fs/zfs/zap_micro.c
usr/src/uts/common/fs/zfs/zfs_dir.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/zfs_vnops.c
usr/src/uts/common/fs/zfs/zfs_znode.c
usr/src/uts/common/sys/fs/zfs.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/deleted_files/usr/src/uts/common/fs/zfs/sys/zfs_i18n.h	Tue Nov 20 05:42:50 2007 -0800
@@ -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 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _SYS_ZFS_I18N_H
+#define	_SYS_ZFS_I18N_H
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+#include <sys/sunddi.h>
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+/*
+ * z_case behaviors
+ *	The first two describe the extent of case insensitivity.
+ *	The third describes matching behavior when mixed sensitivity
+ *	is allowed.
+ */
+#define	ZFS_CI_ONLY	0x01		/* all lookups case-insensitive */
+#define	ZFS_CI_MIXD	0x02		/* some lookups case-insensitive */
+
+/*
+ * ZFS_UTF8_ONLY
+ *	If set, the file system should reject non-utf8 characters in names.
+ */
+#define	ZFS_UTF8_ONLY	0x04
+
+enum zfs_case {
+	ZFS_CASE_SENSITIVE,
+	ZFS_CASE_INSENSITIVE,
+	ZFS_CASE_MIXED
+};
+
+enum zfs_normal {
+	ZFS_NORMALIZE_NONE,
+	ZFS_NORMALIZE_D,
+	ZFS_NORMALIZE_KC,
+	ZFS_NORMALIZE_C,
+	ZFS_NORMALIZE_KD
+};
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* _SYS_ZFS_I18N_H */
--- a/usr/src/common/zfs/zfs_prop.c	Mon Nov 19 21:07:12 2007 -0800
+++ b/usr/src/common/zfs/zfs_prop.c	Tue Nov 20 05:42:50 2007 -0800
@@ -27,10 +27,10 @@
 
 #include <sys/zio.h>
 #include <sys/spa.h>
+#include <sys/u8_textprep.h>
 #include <sys/zfs_acl.h>
 #include <sys/zfs_ioctl.h>
 #include <sys/zfs_znode.h>
-#include <sys/zfs_i18n.h>
 
 #include "zfs_prop.h"
 #include "zfs_deleg.h"
@@ -115,12 +115,17 @@
 		{ NULL }
 	};
 
+	/*
+	 * Use the unique flags we have to send to u8_strcmp() and/or
+	 * u8_textprep() to represent the various normalization property
+	 * values.
+	 */
 	static zprop_index_t normalize_table[] = {
-		{ "none",	ZFS_NORMALIZE_NONE },
-		{ "formD",	ZFS_NORMALIZE_D },
-		{ "formKC",	ZFS_NORMALIZE_KC },
-		{ "formC",	ZFS_NORMALIZE_C },
-		{ "formKD",	ZFS_NORMALIZE_KD },
+		{ "none",	0 },
+		{ "formD",	U8_TEXTPREP_NFD },
+		{ "formKC",	U8_TEXTPREP_NFKC },
+		{ "formC",	U8_TEXTPREP_NFC },
+		{ "formKD",	U8_TEXTPREP_NFKD },
 		{ NULL }
 	};
 
@@ -202,7 +207,7 @@
 	    ZFS_TYPE_FILESYSTEM, "yes | no", "MOUNTED", boolean_table);
 
 	/* set once index properties */
-	register_index(ZFS_PROP_NORMALIZE, "normalization", ZFS_NORMALIZE_NONE,
+	register_index(ZFS_PROP_NORMALIZE, "normalization", 0,
 	    PROP_ONETIME, ZFS_TYPE_FILESYSTEM | ZFS_TYPE_SNAPSHOT,
 	    "none | formC | formD | formKC | formKD", "NORMALIZATION",
 	    normalize_table);
--- a/usr/src/lib/libzfs/common/libzfs_dataset.c	Mon Nov 19 21:07:12 2007 -0800
+++ b/usr/src/lib/libzfs/common/libzfs_dataset.c	Tue Nov 20 05:42:50 2007 -0800
@@ -51,7 +51,6 @@
 
 #include <sys/spa.h>
 #include <sys/zap.h>
-#include <sys/zfs_i18n.h>
 #include <libzfs.h>
 
 #include "zfs_namecheck.h"
@@ -480,7 +479,6 @@
 	char *strval;
 	zfs_prop_t prop;
 	nvlist_t *ret;
-	int chosen_sense = -1;
 	int chosen_normal = -1;
 	int chosen_utf = -1;
 
@@ -748,9 +746,6 @@
 			}
 
 			break;
-		case ZFS_PROP_CASE:
-			chosen_sense = (int)intval;
-			break;
 		case ZFS_PROP_UTF8ONLY:
 			chosen_utf = (int)intval;
 			break;
@@ -810,32 +805,19 @@
 	}
 
 	/*
-	 * Temporarily disallow any non-default settings for
-	 * casesensitivity, normalization, and/or utf8only.
-	 */
-	if (chosen_sense > ZFS_CASE_SENSITIVE || chosen_utf > 0 ||
-	    chosen_normal > ZFS_NORMALIZE_NONE) {
-		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
-		    "Non-default values for casesensitivity, utf8only, and "
-		    "normalization are (temporarily) disabled"));
-		(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
-		goto error;
-	}
-
-	/*
 	 * If normalization was chosen, but no UTF8 choice was made,
 	 * enforce rejection of non-UTF8 names.
 	 *
 	 * If normalization was chosen, but rejecting non-UTF8 names
 	 * was explicitly not chosen, it is an error.
 	 */
-	if (chosen_normal > ZFS_NORMALIZE_NONE && chosen_utf < 0) {
+	if (chosen_normal > 0 && chosen_utf < 0) {
 		if (nvlist_add_uint64(ret,
 		    zfs_prop_to_name(ZFS_PROP_UTF8ONLY), 1) != 0) {
 			(void) no_memory(hdl);
 			goto error;
 		}
-	} else if (chosen_normal > ZFS_NORMALIZE_NONE && chosen_utf == 0) {
+	} else if (chosen_normal > 0 && chosen_utf == 0) {
 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 		    "'%s' must be set 'on' if normalization chosen"),
 		    zfs_prop_to_name(ZFS_PROP_UTF8ONLY));
@@ -1880,6 +1862,7 @@
     char **source, uint64_t *val)
 {
 	zfs_cmd_t zc = { 0 };
+	nvlist_t *zplprops;
 	struct mnttab mnt;
 	char *mntopt_on = NULL;
 	char *mntopt_off = NULL;
@@ -2001,17 +1984,32 @@
 		break;
 
 	case ZFS_PROP_VERSION:
-		if (!zfs_prop_valid_for_type(prop, zhp->zfs_head_type))
+	case ZFS_PROP_NORMALIZE:
+	case ZFS_PROP_UTF8ONLY:
+	case ZFS_PROP_CASE:
+		if (!zfs_prop_valid_for_type(prop, zhp->zfs_head_type) ||
+		    zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0)
 			return (-1);
 		(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
-		if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_OBJSET_VERSION, &zc) ||
-		    (zc.zc_cookie == 0)) {
+		if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_OBJSET_ZPLPROPS, &zc)) {
+			zcmd_free_nvlists(&zc);
 			zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
-			    "unable to get version property"));
+			    "unable to get %s property"),
+			    zfs_prop_to_name(prop));
 			return (zfs_error(zhp->zfs_hdl, EZFS_BADVERSION,
 			    dgettext(TEXT_DOMAIN, "internal error")));
 		}
-		*val = zc.zc_cookie;
+		if (zcmd_read_dst_nvlist(zhp->zfs_hdl, &zc, &zplprops) != 0 ||
+		    nvlist_lookup_uint64(zplprops, zfs_prop_to_name(prop),
+		    val) != 0) {
+			zcmd_free_nvlists(&zc);
+			zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
+			    "unable to get %s property"),
+			    zfs_prop_to_name(prop));
+			return (zfs_error(zhp->zfs_hdl, EZFS_NOMEM,
+			    dgettext(TEXT_DOMAIN, "internal error")));
+		}
+		zcmd_free_nvlists(&zc);
 		break;
 
 	default:
--- a/usr/src/lib/libzpool/common/sys/zfs_context.h	Mon Nov 19 21:07:12 2007 -0800
+++ b/usr/src/lib/libzpool/common/sys/zfs_context.h	Tue Nov 20 05:42:50 2007 -0800
@@ -73,6 +73,7 @@
 #include <sys/zfs_debug.h>
 #include <sys/sdt.h>
 #include <sys/kstat.h>
+#include <sys/u8_textprep.h>
 #include <sys/sysevent/eventdefs.h>
 
 /*
@@ -520,25 +521,6 @@
 extern int zfs_secpolicy_destroy_perms(const char *name, cred_t *cr);
 extern zoneid_t getzoneid(void);
 
-/*
- * UTF-8 text preparation functions and their macros.
- * (sunddi.h)
- */
-#define	U8_STRCMP_CS			0x00000001
-#define	U8_STRCMP_CI_UPPER		0x00000002
-#define	U8_STRCMP_CI_LOWER		0x00000004
-
-#define	U8_TEXTPREP_TOUPPER		U8_STRCMP_CI_UPPER
-#define	U8_TEXTPREP_TOLOWER		U8_STRCMP_CI_LOWER
-#define	U8_TEXTPREP_IGNORE_NULL		0x00010000
-
-#define	U8_UNICODE_320			(0)
-#define	U8_UNICODE_500			(1)
-#define	U8_UNICODE_LATEST		U8_UNICODE_500
-
-extern size_t u8_textprep_str(char *, size_t *, char *, size_t *, int, size_t,
-	int *);
-
 #ifdef	__cplusplus
 }
 #endif
--- a/usr/src/uts/common/fs/zfs/sys/zap_leaf.h	Mon Nov 19 21:07:12 2007 -0800
+++ b/usr/src/uts/common/fs/zfs/sys/zap_leaf.h	Tue Nov 20 05:42:50 2007 -0800
@@ -232,9 +232,9 @@
  * Other stuff.
  */
 
-extern void zap_leaf_init(zap_leaf_t *l, int version);
+extern void zap_leaf_init(zap_leaf_t *l, boolean_t sort);
 extern void zap_leaf_byteswap(zap_leaf_phys_t *buf, int len);
-extern void zap_leaf_split(zap_leaf_t *l, zap_leaf_t *nl, int version);
+extern void zap_leaf_split(zap_leaf_t *l, zap_leaf_t *nl, boolean_t sort);
 extern void zap_leaf_stats(zap_t *zap, zap_leaf_t *l, zap_stats_t *zs);
 
 #ifdef	__cplusplus
--- a/usr/src/uts/common/fs/zfs/sys/zfs_i18n.h	Mon Nov 19 21:07:12 2007 -0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,71 +0,0 @@
-/*
- * 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 2007 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
- */
-
-#ifndef _SYS_ZFS_I18N_H
-#define	_SYS_ZFS_I18N_H
-
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
-#include <sys/sunddi.h>
-
-#ifdef	__cplusplus
-extern "C" {
-#endif
-
-/*
- * z_case behaviors
- *	The first two describe the extent of case insensitivity.
- *	The third describes matching behavior when mixed sensitivity
- *	is allowed.
- */
-#define	ZFS_CI_ONLY	0x01		/* all lookups case-insensitive */
-#define	ZFS_CI_MIXD	0x02		/* some lookups case-insensitive */
-
-/*
- * ZFS_UTF8_ONLY
- *	If set, the file system should reject non-utf8 characters in names.
- */
-#define	ZFS_UTF8_ONLY	0x04
-
-enum zfs_case {
-	ZFS_CASE_SENSITIVE,
-	ZFS_CASE_INSENSITIVE,
-	ZFS_CASE_MIXED
-};
-
-enum zfs_normal {
-	ZFS_NORMALIZE_NONE,
-	ZFS_NORMALIZE_D,
-	ZFS_NORMALIZE_KC,
-	ZFS_NORMALIZE_C,
-	ZFS_NORMALIZE_KD
-};
-
-#ifdef	__cplusplus
-}
-#endif
-
-#endif	/* _SYS_ZFS_I18N_H */
--- a/usr/src/uts/common/fs/zfs/sys/zfs_ioctl.h	Mon Nov 19 21:07:12 2007 -0800
+++ b/usr/src/uts/common/fs/zfs/sys/zfs_ioctl.h	Tue Nov 20 05:42:50 2007 -0800
@@ -131,6 +131,18 @@
 	uint64_t	z_sharemax;  /* max length of share string */
 } zfs_share_t;
 
+/*
+ * ZFS file systems may behave the usual, POSIX-compliant way, where
+ * name lookups are case-sensitive.  They may also be set up so that
+ * all the name lookups are case-insensitive, or so that only some
+ * lookups, the ones that set an FIGNORECASE flag, are case-insensitive.
+ */
+typedef enum zfs_case {
+	ZFS_CASE_SENSITIVE,
+	ZFS_CASE_INSENSITIVE,
+	ZFS_CASE_MIXED
+} zfs_case_t;
+
 typedef struct zfs_cmd {
 	char		zc_name[MAXPATHLEN];
 	char		zc_value[MAXPATHLEN * 2];
@@ -161,7 +173,7 @@
 #ifdef _KERNEL
 
 typedef struct zfs_creat {
-	int		zct_norm;
+	nvlist_t	*zct_zplprops;
 	nvlist_t	*zct_props;
 } zfs_creat_t;
 
--- a/usr/src/uts/common/fs/zfs/sys/zfs_vfsops.h	Mon Nov 19 21:07:12 2007 -0800
+++ b/usr/src/uts/common/fs/zfs/sys/zfs_vfsops.h	Tue Nov 20 05:42:50 2007 -0800
@@ -34,6 +34,7 @@
 #include <sys/vfs.h>
 #include <sys/zil.h>
 #include <sys/rrwlock.h>
+#include <sys/zfs_ioctl.h>
 
 #ifdef	__cplusplus
 extern "C" {
@@ -58,7 +59,8 @@
 	zilog_t		*z_log;		/* intent log pointer */
 	uint_t		z_acl_mode;	/* acl chmod/mode behavior */
 	uint_t		z_acl_inherit;	/* acl inheritance behavior */
-	uint_t		z_case;		/* case-insensitive behavior */
+	zfs_case_t	z_case;		/* case-sense */
+	boolean_t	z_utf8;		/* utf8-only */
 	int		z_norm;		/* normalization flags */
 	boolean_t	z_atime;	/* enable atimes mount option */
 	boolean_t	z_unmounted;	/* unmounted */
--- a/usr/src/uts/common/fs/zfs/sys/zfs_znode.h	Mon Nov 19 21:07:12 2007 -0800
+++ b/usr/src/uts/common/fs/zfs/sys/zfs_znode.h	Tue Nov 20 05:42:50 2007 -0800
@@ -102,7 +102,6 @@
 #define	ZPL_VERSION_STR		"VERSION"
 #define	ZFS_FUID_TABLES		"FUID"
 
-
 #define	ZFS_MAX_BLOCKSIZE	(SPA_MAXBLOCKSIZE)
 
 /* Path component length */
@@ -293,7 +292,7 @@
 
 extern int	zfs_init_fs(zfsvfs_t *, znode_t **, cred_t *);
 extern void	zfs_set_dataprop(objset_t *);
-extern void	zfs_create_fs(objset_t *os, cred_t *cr, uint64_t, int,
+extern void	zfs_create_fs(objset_t *os, cred_t *cr, nvlist_t *,
     dmu_tx_t *tx);
 extern void	zfs_time_stamper(znode_t *, uint_t, dmu_tx_t *);
 extern void	zfs_time_stamper_locked(znode_t *, uint_t, dmu_tx_t *);
@@ -310,7 +309,7 @@
 extern int	zfs_create_op_tables();
 extern int	zfs_sync(vfs_t *vfsp, short flag, cred_t *cr);
 extern dev_t	zfs_cmpldev(uint64_t);
-extern int	zfs_get_version(objset_t *os, uint64_t *version);
+extern int	zfs_get_zplprop(objset_t *os, zfs_prop_t prop, uint64_t *value);
 extern int	zfs_set_version(const char *name, uint64_t newvers);
 extern int	zfs_get_stats(objset_t *os, nvlist_t *nv);
 
--- a/usr/src/uts/common/fs/zfs/zap.c	Mon Nov 19 21:07:12 2007 -0800
+++ b/usr/src/uts/common/fs/zfs/zap.c	Tue Nov 20 05:42:50 2007 -0800
@@ -44,6 +44,7 @@
 #include <sys/spa.h>
 #include <sys/dmu.h>
 #include <sys/zfs_context.h>
+#include <sys/zfs_znode.h>
 #include <sys/zap.h>
 #include <sys/refcount.h>
 #include <sys/zap_impl.h>
@@ -119,7 +120,7 @@
 	l->l_dbuf = db;
 	l->l_phys = db->db_data;
 
-	zap_leaf_init(l, spa_version(dmu_objset_spa(zap->zap_objset)));
+	zap_leaf_init(l, zp->zap_normflags != 0);
 
 	kmem_free(l, sizeof (zap_leaf_t));
 	dmu_buf_rele(db, FTAG);
@@ -399,7 +400,7 @@
 	ASSERT(winner == NULL);
 	dmu_buf_will_dirty(l->l_dbuf, tx);
 
-	zap_leaf_init(l, spa_version(dmu_objset_spa(zap->zap_objset)));
+	zap_leaf_init(l, zap->zap_normflags != 0);
 
 	zap->zap_f.zap_phys->zap_num_leafs++;
 
@@ -646,7 +647,7 @@
 	}
 
 	nl = zap_create_leaf(zap, tx);
-	zap_leaf_split(l, nl, spa_version(dmu_objset_spa(zap->zap_objset)));
+	zap_leaf_split(l, nl, zap->zap_normflags != 0);
 
 	/* set sibling pointers */
 	for (i = 0; i < (1ULL<<prefix_diff); i++) {
--- a/usr/src/uts/common/fs/zfs/zap_leaf.c	Mon Nov 19 21:07:12 2007 -0800
+++ b/usr/src/uts/common/fs/zfs/zap_leaf.c	Tue Nov 20 05:42:50 2007 -0800
@@ -152,7 +152,7 @@
 }
 
 void
-zap_leaf_init(zap_leaf_t *l, int version)
+zap_leaf_init(zap_leaf_t *l, boolean_t sort)
 {
 	int i;
 
@@ -167,7 +167,7 @@
 	l->l_phys->l_hdr.lh_block_type = ZBT_LEAF;
 	l->l_phys->l_hdr.lh_magic = ZAP_LEAF_MAGIC;
 	l->l_phys->l_hdr.lh_nfree = ZAP_LEAF_NUMCHUNKS(l);
-	if (version >= SPA_VERSION_NORMALIZATION)
+	if (sort)
 		l->l_phys->l_hdr.lh_flags |= ZLF_ENTRIES_CDSORTED;
 }
 
@@ -769,7 +769,7 @@
  * Transfer the entries whose hash prefix ends in 1 to the new leaf.
  */
 void
-zap_leaf_split(zap_leaf_t *l, zap_leaf_t *nl, int version)
+zap_leaf_split(zap_leaf_t *l, zap_leaf_t *nl, boolean_t sort)
 {
 	int i;
 	int bit = 64 - 1 - l->l_phys->l_hdr.lh_prefix_len;
@@ -783,7 +783,7 @@
 	/* break existing hash chains */
 	zap_memset(l->l_phys->l_hash, CHAIN_END, 2*ZAP_LEAF_HASH_NUMENTRIES(l));
 
-	if (version >= SPA_VERSION_NORMALIZATION)
+	if (sort)
 		l->l_phys->l_hdr.lh_flags |= ZLF_ENTRIES_CDSORTED;
 
 	/*
@@ -838,7 +838,7 @@
 
 			n = 1 + ZAP_LEAF_ARRAY_NCHUNKS(le->le_name_length) +
 			    ZAP_LEAF_ARRAY_NCHUNKS(le->le_value_length *
-				le->le_int_size);
+			    le->le_int_size);
 			n = MIN(n, ZAP_HISTOGRAM_SIZE-1);
 			zs->zs_entries_using_n_chunks[n]++;
 
--- a/usr/src/uts/common/fs/zfs/zap_micro.c	Mon Nov 19 21:07:12 2007 -0800
+++ b/usr/src/uts/common/fs/zfs/zap_micro.c	Tue Nov 20 05:42:50 2007 -0800
@@ -33,7 +33,10 @@
 #include <sys/zap_impl.h>
 #include <sys/zap_leaf.h>
 #include <sys/avl.h>
-#include <sys/zfs_i18n.h>
+
+#ifdef _KERNEL
+#include <sys/sunddi.h>
+#endif
 
 static int mzap_upgrade(zap_t **zapp, dmu_tx_t *tx);
 
@@ -492,8 +495,6 @@
 	dmu_buf_t *db;
 	mzap_phys_t *zp;
 
-	ASSERT(normflags == 0 ||
-	    spa_version(dmu_objset_spa(os)) >= SPA_VERSION_NORMALIZATION);
 	VERIFY(0 == dmu_buf_hold(os, obj, 0, FTAG, &db));
 
 #ifdef ZFS_DEBUG
--- a/usr/src/uts/common/fs/zfs/zfs_dir.c	Mon Nov 19 21:07:12 2007 -0800
+++ b/usr/src/uts/common/fs/zfs/zfs_dir.c	Tue Nov 20 05:42:50 2007 -0800
@@ -42,6 +42,7 @@
 #include <sys/errno.h>
 #include <sys/stat.h>
 #include <sys/unistd.h>
+#include <sys/sunddi.h>
 #include <sys/random.h>
 #include <sys/policy.h>
 #include <sys/zfs_dir.h>
@@ -55,7 +56,6 @@
 #include <sys/zfs_fuid.h>
 #include <sys/dnlc.h>
 #include <sys/extdirent.h>
-#include <sys/zfs_i18n.h>
 
 /*
  * zfs_match_find() is used by zfs_dirent_lock() to peform zap lookups
@@ -177,8 +177,9 @@
 	 * a zap lookup on file systems supporting case-insensitive
 	 * access.
 	 */
-	exact = ((zfsvfs->z_case & ZFS_CI_ONLY) && (flag & ZCIEXACT)) ||
-	    ((zfsvfs->z_case & ZFS_CI_MIXD) && !(flag & ZCILOOK));
+	exact =
+	    ((zfsvfs->z_case == ZFS_CASE_INSENSITIVE) && (flag & ZCIEXACT)) ||
+	    ((zfsvfs->z_case == ZFS_CASE_MIXED) && !(flag & ZCILOOK));
 
 	/*
 	 * Only look in or update the DNLC if we are looking for the
@@ -191,7 +192,7 @@
 	 * case for performance improvement?
 	 */
 	update = !zfsvfs->z_norm ||
-	    ((zfsvfs->z_case & ZFS_CI_MIXD) &&
+	    ((zfsvfs->z_case == ZFS_CASE_MIXED) &&
 	    !(zfsvfs->z_norm & ~U8_TEXTPREP_TOUPPER) && !(flag & ZCILOOK));
 
 	/*
@@ -761,9 +762,9 @@
 	mutex_exit(&dzp->z_lock);
 
 	if (zp->z_zfsvfs->z_norm) {
-		if (((zp->z_zfsvfs->z_case & ZFS_CI_ONLY) &&
+		if (((zp->z_zfsvfs->z_case == ZFS_CASE_INSENSITIVE) &&
 		    (flag & ZCIEXACT)) ||
-		    ((zp->z_zfsvfs->z_case & ZFS_CI_MIXD) &&
+		    ((zp->z_zfsvfs->z_case == ZFS_CASE_MIXED) &&
 		    !(flag & ZCILOOK)))
 			error = zap_remove_norm(zp->z_zfsvfs->z_os,
 			    dzp->z_id, dl->dl_name, MT_EXACT, tx);
--- a/usr/src/uts/common/fs/zfs/zfs_ioctl.c	Mon Nov 19 21:07:12 2007 -0800
+++ b/usr/src/uts/common/fs/zfs/zfs_ioctl.c	Tue Nov 20 05:42:50 2007 -0800
@@ -38,7 +38,6 @@
 #include <sys/cmn_err.h>
 #include <sys/stat.h>
 #include <sys/zfs_ioctl.h>
-#include <sys/zfs_i18n.h>
 #include <sys/zfs_znode.h>
 #include <sys/zap.h>
 #include <sys/spa.h>
@@ -65,8 +64,6 @@
 #include <sys/zfs_dir.h>
 #include <sys/zvol.h>
 #include <sharefs/share.h>
-#include <sys/zfs_znode.h>
-#include <sys/zfs_vfsops.h>
 #include <sys/dmu_objset.h>
 
 #include "zfs_namecheck.h"
@@ -1095,6 +1092,30 @@
 	return (error);
 }
 
+static int
+zfs_os_open_retry(char *name, objset_t **os)
+{
+	int error;
+
+retry:
+	error = dmu_objset_open(name, DMU_OST_ANY,
+	    DS_MODE_STANDARD | DS_MODE_READONLY, os);
+	if (error != 0) {
+		/*
+		 * This is ugly: dmu_objset_open() can return EBUSY if
+		 * the objset is held exclusively. Fortunately this hold is
+		 * only for a short while, so we retry here.
+		 * This avoids user code having to handle EBUSY,
+		 * for example for a "zfs list".
+		 */
+		if (error == EBUSY) {
+			delay(1);
+			goto retry;
+		}
+	}
+	return (error);
+}
+
 /*
  * inputs:
  * zc_name		name of filesystem
@@ -1113,23 +1134,8 @@
 	int error;
 	nvlist_t *nv;
 
-retry:
-	error = dmu_objset_open(zc->zc_name, DMU_OST_ANY,
-	    DS_MODE_STANDARD | DS_MODE_READONLY, &os);
-	if (error != 0) {
-		/*
-		 * This is ugly: dmu_objset_open() can return EBUSY if
-		 * the objset is held exclusively. Fortunately this hold is
-		 * only for a short while, so we retry here.
-		 * This avoids user code having to handle EBUSY,
-		 * for example for a "zfs list".
-		 */
-		if (error == EBUSY) {
-			delay(1);
-			goto retry;
-		}
+	if ((error = zfs_os_open_retry(zc->zc_name, &os)) != 0)
 		return (error);
-	}
 
 	dmu_objset_fast_stat(os, &zc->zc_objset_stats);
 
@@ -1156,6 +1162,66 @@
 	return (error);
 }
 
+static int
+nvl_add_zplprop(objset_t *os, nvlist_t *props, zfs_prop_t prop)
+{
+	uint64_t value;
+	int error;
+
+	/*
+	 * zfs_get_zplprop() will either find a value or give us
+	 * the default value (if there is one).
+	 */
+	if ((error = zfs_get_zplprop(os, prop, &value)) != 0)
+		return (error);
+	VERIFY(nvlist_add_uint64(props, zfs_prop_to_name(prop), value) == 0);
+	return (0);
+}
+
+/*
+ * inputs:
+ * zc_name		name of filesystem
+ * zc_nvlist_dst_size	size of buffer for zpl property nvlist
+ *
+ * outputs:
+ * zc_nvlist_dst	zpl property nvlist
+ * zc_nvlist_dst_size	size of zpl property nvlist
+ */
+static int
+zfs_ioc_objset_zplprops(zfs_cmd_t *zc)
+{
+	objset_t *os;
+	int err;
+
+	if ((err = zfs_os_open_retry(zc->zc_name, &os)) != 0)
+		return (err);
+
+	dmu_objset_fast_stat(os, &zc->zc_objset_stats);
+
+	/*
+	 * NB: nvl_add_zplprop() will read the objset contents,
+	 * which we aren't supposed to do with a DS_MODE_STANDARD
+	 * open, because it could be inconsistent.
+	 */
+	if (zc->zc_nvlist_dst != NULL &&
+	    !zc->zc_objset_stats.dds_inconsistent &&
+	    dmu_objset_type(os) == DMU_OST_ZFS) {
+		nvlist_t *nv;
+
+		VERIFY(nvlist_alloc(&nv, NV_UNIQUE_NAME, KM_SLEEP) == 0);
+		if ((err = nvl_add_zplprop(os, nv, ZFS_PROP_VERSION)) == 0 &&
+		    (err = nvl_add_zplprop(os, nv, ZFS_PROP_NORMALIZE)) == 0 &&
+		    (err = nvl_add_zplprop(os, nv, ZFS_PROP_UTF8ONLY)) == 0 &&
+		    (err = nvl_add_zplprop(os, nv, ZFS_PROP_CASE)) == 0)
+			err = put_nvlist(zc, nv);
+		nvlist_free(nv);
+	} else {
+		err = ENOENT;
+	}
+	dmu_objset_close(os);
+	return (err);
+}
+
 /*
  * inputs:
  * zc_name		name of filesystem
@@ -1170,68 +1236,13 @@
  * zc_value		alternate root
  */
 static int
-zfs_ioc_objset_version(zfs_cmd_t *zc)
-{
-	objset_t *os = NULL;
-	int error;
-
-retry:
-	error = dmu_objset_open(zc->zc_name, DMU_OST_ANY,
-	    DS_MODE_STANDARD | DS_MODE_READONLY, &os);
-	if (error != 0) {
-		/*
-		 * This is ugly: dmu_objset_open() can return EBUSY if
-		 * the objset is held exclusively. Fortunately this hold is
-		 * only for a short while, so we retry here.
-		 * This avoids user code having to handle EBUSY,
-		 * for example for a "zfs list".
-		 */
-		if (error == EBUSY) {
-			delay(1);
-			goto retry;
-		}
-		return (error);
-	}
-
-	dmu_objset_fast_stat(os, &zc->zc_objset_stats);
-
-	/*
-	 * NB: zfs_get_version() will read the objset contents,
-	 * which we aren't supposed to do with a
-	 * DS_MODE_STANDARD open, because it could be
-	 * inconsistent.  So this is a bit of a workaround...
-	 */
-	zc->zc_cookie = 0;
-	if (!zc->zc_objset_stats.dds_inconsistent)
-		if (dmu_objset_type(os) == DMU_OST_ZFS)
-			(void) zfs_get_version(os, &zc->zc_cookie);
-
-	dmu_objset_close(os);
-	return (0);
-}
-
-static int
 zfs_ioc_dataset_list_next(zfs_cmd_t *zc)
 {
 	objset_t *os;
 	int error;
 	char *p;
 
-retry:
-	error = dmu_objset_open(zc->zc_name, DMU_OST_ANY,
-	    DS_MODE_STANDARD | DS_MODE_READONLY, &os);
-	if (error != 0) {
-		/*
-		 * This is ugly: dmu_objset_open() can return EBUSY if
-		 * the objset is held exclusively. Fortunately this hold is
-		 * only for a short while, so we retry here.
-		 * This avoids user code having to handle EBUSY,
-		 * for example for a "zfs list".
-		 */
-		if (error == EBUSY) {
-			delay(1);
-			goto retry;
-		}
+	if ((error = zfs_os_open_retry(zc->zc_name, &os)) != 0) {
 		if (error == ENOENT)
 			error = ESRCH;
 		return (error);
@@ -1281,21 +1292,7 @@
 	objset_t *os;
 	int error;
 
-retry:
-	error = dmu_objset_open(zc->zc_name, DMU_OST_ANY,
-	    DS_MODE_STANDARD | DS_MODE_READONLY, &os);
-	if (error != 0) {
-		/*
-		 * This is ugly: dmu_objset_open() can return EBUSY if
-		 * the objset is held exclusively. Fortunately this hold is
-		 * only for a short while, so we retry here.
-		 * This avoids user code having to handle EBUSY,
-		 * for example for a "zfs list".
-		 */
-		if (error == EBUSY) {
-			delay(1);
-			goto retry;
-		}
+	if ((error = zfs_os_open_retry(zc->zc_name, &os)) != 0) {
 		if (error == ENOENT)
 			error = ESRCH;
 		return (error);
@@ -1385,12 +1382,6 @@
 			if (zfs_check_version(name, SPA_VERSION_DITTO_BLOCKS))
 				return (ENOTSUP);
 			break;
-		case ZFS_PROP_NORMALIZE:
-		case ZFS_PROP_UTF8ONLY:
-		case ZFS_PROP_CASE:
-			if (zfs_check_version(name, SPA_VERSION_NORMALIZATION))
-				return (ENOTSUP);
-
 		}
 		if ((error = zfs_secpolicy_setprop(name, prop, CRED())) != 0)
 			return (error);
@@ -1759,172 +1750,112 @@
 zfs_create_cb(objset_t *os, void *arg, cred_t *cr, dmu_tx_t *tx)
 {
 	zfs_creat_t *zct = arg;
-	uint64_t version;
-
-	if (spa_version(dmu_objset_spa(os)) >= SPA_VERSION_FUID)
-		version = ZPL_VERSION;
-	else
-		version = ZPL_VERSION_FUID - 1;
-
-	(void) nvlist_lookup_uint64(zct->zct_props,
-	    zfs_prop_to_name(ZFS_PROP_VERSION), &version);
-
-	zfs_create_fs(os, cr, version, zct->zct_norm, tx);
+
+	zfs_create_fs(os, cr, zct->zct_zplprops, tx);
 }
 
+#define	ZFS_PROP_UNDEFINED	((uint64_t)-1)
+
 /*
- * zfs_prop_lookup()
+ * inputs:
+ * createprops	list of properties requested by creator
+ * dataset	name of dataset we are creating
  *
- * Look for the property first in the existing property nvlist.  If
- * it's already present, you're done.  If it's not there, attempt to
- * find the property value from a parent dataset.  If that fails, fall
- * back to the property's default value.  In either of these two
- * cases, if update is TRUE, add a value for the property to the
- * property nvlist.
- *
- * If the rval pointer is non-NULL, copy the discovered value to rval.
- *
- * If we get any unexpected errors, bail and return the error number
- * to the caller.
+ * outputs:
+ * zplprops	values for the zplprops we attach to the master node object
  *
- * If we succeed, return 0.
- */
-static int
-zfs_prop_lookup(const char *parentname, zfs_prop_t propnum,
-    nvlist_t *proplist, uint64_t *rval, boolean_t update)
-{
-	const char *propname;
-	uint64_t value;
-	int error = ENOENT;
-
-	propname = zfs_prop_to_name(propnum);
-	if (proplist != NULL)
-		error = nvlist_lookup_uint64(proplist, propname, &value);
-	if (error == ENOENT) {
-		error = dsl_prop_get_integer(parentname, propname,
-		    &value, NULL);
-		if (error == ENOENT)
-			value = zfs_prop_default_numeric(propnum);
-		else if (error != 0)
-			return (error);
-		if (update) {
-			ASSERT(proplist != NULL);
-			error = nvlist_add_uint64(proplist, propname, value);
-		}
-	}
-	if (error == 0 && rval)
-		*rval = value;
-	return (error);
-}
-
-/*
- * zfs_normalization_get
- *
- * Get the normalization flag value.  If the properties have
- * non-default values, make sure the pool version is recent enough to
- * support these choices.
+ * Determine the settings for utf8only, normalization and
+ * casesensitivity.  Specific values may have been requested by the
+ * creator and/or we can inherit values from the parent dataset.  If
+ * the file system is of too early a vintage, a creator can not
+ * request settings for these properties, even if the requested
+ * setting is the default value.  We don't actually want to create dsl
+ * properties for these, so remove them from the source nvlist after
+ * processing.
  */
 static int
-zfs_normalization_get(const char *dataset, nvlist_t *proplist, int *norm,
-    boolean_t update)
+zfs_fill_zplprops(const char *dataset, nvlist_t *createprops,
+    nvlist_t *zplprops, uint64_t zplver)
 {
+	objset_t *os;
 	char parentname[MAXNAMELEN];
-	char poolname[MAXNAMELEN];
 	char *cp;
-	uint64_t value;
-	int check = 0;
-	int error;
-
-	ASSERT(norm != NULL);
-	*norm = 0;
-
-	(void) strncpy(parentname, dataset, sizeof (parentname));
-	cp = strrchr(parentname, '@');
-	if (cp != NULL) {
-		cp[0] = '\0';
-	} else {
-		cp = strrchr(parentname, '/');
-		if (cp == NULL)
-			return (ENOENT);
-		cp[0] = '\0';
-	}
-
-	(void) strncpy(poolname, dataset, sizeof (poolname));
-	cp = strchr(poolname, '/');
-	if (cp != NULL)
-		cp[0] = '\0';
+	uint64_t sense = ZFS_PROP_UNDEFINED;
+	uint64_t norm = ZFS_PROP_UNDEFINED;
+	uint64_t u8 = ZFS_PROP_UNDEFINED;
+	int error = 0;
+
+	ASSERT(zplprops != NULL);
+
+	(void) strlcpy(parentname, dataset, sizeof (parentname));
+	cp = strrchr(parentname, '/');
+	ASSERT(cp != NULL);
+	cp[0] = '\0';
 
 	/*
-	 * Make sure pool is of new enough vintage to support normalization.
+	 * Pull out creator prop choices, if any.
 	 */
-	if (zfs_check_version(poolname, SPA_VERSION_NORMALIZATION))
-		return (0);
-
-	error = zfs_prop_lookup(parentname, ZFS_PROP_UTF8ONLY,
-	    proplist, &value, update);
-	if (error != 0)
-		return (error);
-	if (value != zfs_prop_default_numeric(ZFS_PROP_UTF8ONLY))
-		check = 1;
-
-	error = zfs_prop_lookup(parentname, ZFS_PROP_NORMALIZE,
-	    proplist, &value, update);
-	if (error != 0)
-		return (error);
-	if (value != zfs_prop_default_numeric(ZFS_PROP_NORMALIZE)) {
-		check = 1;
-		switch ((int)value) {
-		case ZFS_NORMALIZE_NONE:
-			break;
-		case ZFS_NORMALIZE_C:
-			*norm |= U8_TEXTPREP_NFC;
-			break;
-		case ZFS_NORMALIZE_D:
-			*norm |= U8_TEXTPREP_NFD;
-			break;
-		case ZFS_NORMALIZE_KC:
-			*norm |= U8_TEXTPREP_NFKC;
-			break;
-		case ZFS_NORMALIZE_KD:
-			*norm |= U8_TEXTPREP_NFKD;
-			break;
-		default:
-			ASSERT((int)value >= ZFS_NORMALIZE_NONE);
-			ASSERT((int)value <= ZFS_NORMALIZE_KD);
-			break;
-		}
-	}
-
-	error = zfs_prop_lookup(parentname, ZFS_PROP_CASE,
-	    proplist, &value, update);
-	if (error != 0)
-		return (error);
-	if (value != zfs_prop_default_numeric(ZFS_PROP_CASE)) {
-		check = 1;
-		switch ((int)value) {
-		case ZFS_CASE_SENSITIVE:
-			break;
-		case ZFS_CASE_INSENSITIVE:
-			*norm |= U8_TEXTPREP_TOUPPER;
-			break;
-		case ZFS_CASE_MIXED:
-			*norm |= U8_TEXTPREP_TOUPPER;
-			break;
-		default:
-			ASSERT((int)value >= ZFS_CASE_SENSITIVE);
-			ASSERT((int)value <= ZFS_CASE_MIXED);
-			break;
-		}
+	if (createprops) {
+		(void) nvlist_lookup_uint64(createprops,
+		    zfs_prop_to_name(ZFS_PROP_NORMALIZE), &norm);
+		(void) nvlist_remove_all(createprops,
+		    zfs_prop_to_name(ZFS_PROP_NORMALIZE));
+		(void) nvlist_lookup_uint64(createprops,
+		    zfs_prop_to_name(ZFS_PROP_UTF8ONLY), &u8);
+		(void) nvlist_remove_all(createprops,
+		    zfs_prop_to_name(ZFS_PROP_UTF8ONLY));
+		(void) nvlist_lookup_uint64(createprops,
+		    zfs_prop_to_name(ZFS_PROP_CASE), &sense);
+		(void) nvlist_remove_all(createprops,
+		    zfs_prop_to_name(ZFS_PROP_CASE));
 	}
 
 	/*
-	 * At the moment we are disabling non-default values for these
-	 * properties because they cannot be preserved properly with a
-	 * zfs send.
+	 * If the file system or pool is version is too "young" to
+	 * support normalization and the creator tried to set a value
+	 * for one of the props, error out.  We only need check the
+	 * ZPL version because we've already checked by now that the
+	 * SPA version is compatible with the selected ZPL version.
+	 */
+	if (zplver < ZPL_VERSION_NORMALIZATION &&
+	    (norm != ZFS_PROP_UNDEFINED || u8 != ZFS_PROP_UNDEFINED ||
+	    sense != ZFS_PROP_UNDEFINED))
+		return (ENOTSUP);
+
+	/*
+	 * Put the version in the zplprops
+	 */
+	VERIFY(nvlist_add_uint64(zplprops,
+	    zfs_prop_to_name(ZFS_PROP_VERSION), zplver) == 0);
+
+	/*
+	 * Open parent object set so we can inherit zplprop values if
+	 * necessary.
 	 */
-	if (check == 1)
-		return (ENOTSUP);
-
+	if ((error = zfs_os_open_retry(parentname, &os)) != 0)
+		return (error);
+
+	if (norm == ZFS_PROP_UNDEFINED)
+		VERIFY(zfs_get_zplprop(os, ZFS_PROP_NORMALIZE, &norm) == 0);
+	VERIFY(nvlist_add_uint64(zplprops,
+	    zfs_prop_to_name(ZFS_PROP_NORMALIZE), norm) == 0);
+
+	/*
+	 * If we're normalizing, names must always be valid UTF-8 strings.
+	 */
+	if (norm)
+		u8 = 1;
+	if (u8 == ZFS_PROP_UNDEFINED)
+		VERIFY(zfs_get_zplprop(os, ZFS_PROP_UTF8ONLY, &u8) == 0);
+	VERIFY(nvlist_add_uint64(zplprops,
+	    zfs_prop_to_name(ZFS_PROP_UTF8ONLY), u8) == 0);
+
+	if (sense == ZFS_PROP_UNDEFINED)
+		VERIFY(zfs_get_zplprop(os, ZFS_PROP_CASE, &sense) == 0);
+	VERIFY(nvlist_add_uint64(zplprops,
+	    zfs_prop_to_name(ZFS_PROP_CASE), sense) == 0);
+
+	dmu_objset_close(os);
 	return (0);
 }
 
@@ -1935,7 +1866,7 @@
  * zc_value		name of snapshot to clone from (may be empty)
  * zc_nvlist_src{_size}	nvlist of properties to apply
  *
- * outputs:		none
+ * outputs: none
  */
 static int
 zfs_ioc_create(zfs_cmd_t *zc)
@@ -1969,7 +1900,7 @@
 	    &nvprops)) != 0)
 		return (error);
 
-	zct.zct_norm = 0;
+	zct.zct_zplprops = NULL;
 	zct.zct_props = nvprops;
 
 	if (zc->zc_value[0] != '\0') {
@@ -1994,29 +1925,6 @@
 			nvlist_free(nvprops);
 			return (error);
 		}
-		/*
-		 * If caller did not provide any properties, allocate
-		 * an nvlist for properties, as we will be adding our set-once
-		 * properties to it.  This carries the choices made on the
-		 * original file system into the clone.
-		 */
-		if (nvprops == NULL)
-			VERIFY(nvlist_alloc(&nvprops,
-			    NV_UNIQUE_NAME, KM_SLEEP) == 0);
-
-		/*
-		 * We have to have normalization and case-folding
-		 * flags correct when we do the file system creation,
-		 * so go figure them out now.  All we really care about
-		 * here is getting these values into the property list.
-		 */
-		error = zfs_normalization_get(zc->zc_value, nvprops,
-		    &zct.zct_norm, B_TRUE);
-		if (error != 0) {
-			dmu_objset_close(clone);
-			nvlist_free(nvprops);
-			return (error);
-		}
 		dmu_objset_close(clone);
 	} else {
 		if (cbfunc == NULL) {
@@ -2057,15 +1965,29 @@
 			uint64_t version;
 			int error;
 
-			error = nvlist_lookup_uint64(nvprops,
+			/*
+			 * Default ZPL version to non-FUID capable if the
+			 * pool is not upgraded to support FUIDs.
+			 */
+			if (zfs_check_version(zc->zc_name, SPA_VERSION_FUID))
+				version = ZPL_VERSION_FUID - 1;
+			else
+				version = ZPL_VERSION;
+
+			/*
+			 * Potentially override default ZPL version based
+			 * on creator's request.
+			 */
+			(void) nvlist_lookup_uint64(nvprops,
 			    zfs_prop_to_name(ZFS_PROP_VERSION), &version);
 
-			if (error == 0 && (version < ZPL_VERSION_INITIAL ||
-			    version > ZPL_VERSION)) {
-				nvlist_free(nvprops);
-				return (ENOTSUP);
-			} else if (error == 0 && version >= ZPL_VERSION_FUID &&
-			    zfs_check_version(zc->zc_name, SPA_VERSION_FUID)) {
+			/*
+			 * Make sure version we ended up with is kosher
+			 */
+			if ((version < ZPL_VERSION_INITIAL ||
+			    version > ZPL_VERSION) ||
+			    (version >= ZPL_VERSION_FUID &&
+			    zfs_check_version(zc->zc_name, SPA_VERSION_FUID))) {
 				nvlist_free(nvprops);
 				return (ENOTSUP);
 			}
@@ -2074,19 +1996,21 @@
 			 * We have to have normalization and
 			 * case-folding flags correct when we do the
 			 * file system creation, so go figure them out
-			 * now.  The final argument to zfs_normalization_get()
-			 * tells that routine not to update the nvprops
-			 * list.
+			 * now.
 			 */
-			error = zfs_normalization_get(zc->zc_name, nvprops,
-			    &zct.zct_norm, B_FALSE);
+			VERIFY(nvlist_alloc(&zct.zct_zplprops,
+			    NV_UNIQUE_NAME, KM_SLEEP) == 0);
+			error = zfs_fill_zplprops(zc->zc_name, nvprops,
+			    zct.zct_zplprops, version);
 			if (error != 0) {
 				nvlist_free(nvprops);
+				nvlist_free(zct.zct_zplprops);
 				return (error);
 			}
 		}
 		error = dmu_objset_create(zc->zc_name, type, NULL, cbfunc,
 		    &zct);
+		nvlist_free(zct.zct_zplprops);
 	}
 
 	/*
@@ -2793,7 +2717,7 @@
 	{ zfs_ioc_vdev_detach, zfs_secpolicy_config, POOL_NAME, B_TRUE },
 	{ zfs_ioc_vdev_setpath,	zfs_secpolicy_config, POOL_NAME, B_FALSE },
 	{ zfs_ioc_objset_stats,	zfs_secpolicy_read, DATASET_NAME, B_FALSE },
-	{ zfs_ioc_objset_version, zfs_secpolicy_read, DATASET_NAME, B_FALSE },
+	{ zfs_ioc_objset_zplprops, zfs_secpolicy_read, DATASET_NAME, B_FALSE },
 	{ zfs_ioc_dataset_list_next, zfs_secpolicy_read,
 	    DATASET_NAME, B_FALSE },
 	{ zfs_ioc_snapshot_list_next, zfs_secpolicy_read,
--- a/usr/src/uts/common/fs/zfs/zfs_vfsops.c	Mon Nov 19 21:07:12 2007 -0800
+++ b/usr/src/uts/common/fs/zfs/zfs_vfsops.c	Tue Nov 20 05:42:50 2007 -0800
@@ -40,7 +40,6 @@
 #include "fs/fs_subr.h"
 #include <sys/zfs_znode.h>
 #include <sys/zfs_dir.h>
-#include <sys/zfs_i18n.h>
 #include <sys/zil.h>
 #include <sys/fs/zfs.h>
 #include <sys/dmu.h>
@@ -385,73 +384,6 @@
 }
 
 static int
-zfs_normalization_set(char *osname, zfsvfs_t *zfsvfs)
-{
-	uint64_t pval;
-	int error;
-
-	if (zfsvfs->z_version < ZPL_VERSION_FUID)
-		return (0);
-
-	error = dsl_prop_get_integer(osname, "normalization", &pval, NULL);
-	if (error)
-		goto normquit;
-	switch ((int)pval) {
-	case ZFS_NORMALIZE_NONE:
-		break;
-	case ZFS_NORMALIZE_C:
-		zfsvfs->z_norm |= U8_TEXTPREP_NFC;
-		break;
-	case ZFS_NORMALIZE_KC:
-		zfsvfs->z_norm |= U8_TEXTPREP_NFKC;
-		break;
-	case ZFS_NORMALIZE_D:
-		zfsvfs->z_norm |= U8_TEXTPREP_NFD;
-		break;
-	case ZFS_NORMALIZE_KD:
-		zfsvfs->z_norm |= U8_TEXTPREP_NFKD;
-		break;
-	default:
-		ASSERT(pval <= ZFS_NORMALIZE_KD);
-		break;
-	}
-
-	error = dsl_prop_get_integer(osname, "utf8only", &pval, NULL);
-	if (error)
-		goto normquit;
-	if (pval)
-		zfsvfs->z_case |= ZFS_UTF8_ONLY;
-	else
-		zfsvfs->z_case &= ~ZFS_UTF8_ONLY;
-
-	error = dsl_prop_get_integer(osname, "casesensitivity", &pval, NULL);
-	if (error)
-		goto normquit;
-	vfs_set_feature(zfsvfs->z_vfs, VFSFT_DIRENTFLAGS);
-	switch ((int)pval) {
-	case ZFS_CASE_SENSITIVE:
-		break;
-	case ZFS_CASE_INSENSITIVE:
-		zfsvfs->z_norm |= U8_TEXTPREP_TOUPPER;
-		zfsvfs->z_case |= ZFS_CI_ONLY;
-		vfs_set_feature(zfsvfs->z_vfs, VFSFT_CASEINSENSITIVE);
-		vfs_set_feature(zfsvfs->z_vfs, VFSFT_NOCASESENSITIVE);
-		break;
-	case ZFS_CASE_MIXED:
-		zfsvfs->z_norm |= U8_TEXTPREP_TOUPPER;
-		zfsvfs->z_case |= ZFS_CI_MIXD;
-		vfs_set_feature(zfsvfs->z_vfs, VFSFT_CASEINSENSITIVE);
-		break;
-	default:
-		ASSERT(pval <= ZFS_CASE_MIXED);
-		break;
-	}
-
-normquit:
-	return (error);
-}
-
-static int
 zfs_register_callbacks(vfs_t *vfsp)
 {
 	struct dsl_dataset *ds = NULL;
@@ -722,6 +654,7 @@
 	    offsetof(znode_t, z_link_node));
 	rrw_init(&zfsvfs->z_teardown_lock);
 	rw_init(&zfsvfs->z_teardown_inactive_lock, NULL, RW_DEFAULT, NULL);
+	rw_init(&zfsvfs->z_fuid_lock, NULL, RW_DEFAULT, NULL);
 
 	/* Initialize the generic filesystem structure. */
 	vfsp->vfs_bcount = 0;
@@ -776,15 +709,14 @@
 		vfs_set_feature(vfsp, VFSFT_ACEMASKONACCESS);
 		vfs_set_feature(vfsp, VFSFT_ACLONCREATE);
 	}
-
-	/*
-	 * Set normalization regardless of whether or not the object
-	 * set is a snapshot.  Snapshots and clones need to have
-	 * identical normalization as did the file system they
-	 * originated from.
-	 */
-	if ((error = zfs_normalization_set(osname, zfsvfs)) != 0)
-		goto out;
+	if (zfsvfs->z_case == ZFS_CASE_INSENSITIVE) {
+		vfs_set_feature(vfsp, VFSFT_DIRENTFLAGS);
+		vfs_set_feature(vfsp, VFSFT_CASEINSENSITIVE);
+		vfs_set_feature(vfsp, VFSFT_NOCASESENSITIVE);
+	} else if (zfsvfs->z_case == ZFS_CASE_MIXED) {
+		vfs_set_feature(vfsp, VFSFT_DIRENTFLAGS);
+		vfs_set_feature(vfsp, VFSFT_CASEINSENSITIVE);
+	}
 
 	if (dmu_objset_is_snapshot(zfsvfs->z_os)) {
 		uint64_t pval;
@@ -810,6 +742,7 @@
 		list_destroy(&zfsvfs->z_all_znodes);
 		rrw_destroy(&zfsvfs->z_teardown_lock);
 		rw_destroy(&zfsvfs->z_teardown_inactive_lock);
+		rw_destroy(&zfsvfs->z_fuid_lock);
 		kmem_free(zfsvfs, sizeof (zfsvfs_t));
 	} else {
 		atomic_add_32(&zfs_active_fs_count, 1);
@@ -1562,6 +1495,7 @@
 	rrw_destroy(&zfsvfs->z_teardown_lock);
 	rw_destroy(&zfsvfs->z_teardown_inactive_lock);
 	zfs_fuid_destroy(zfsvfs);
+	rw_destroy(&zfsvfs->z_fuid_lock);
 	kmem_free(zfsvfs, sizeof (zfsvfs_t));
 
 	atomic_add_32(&zfs_active_fs_count, -1);
@@ -1637,15 +1571,6 @@
 }
 
 int
-zfs_get_version(objset_t *os, uint64_t *version)
-{
-	int error;
-
-	error = zap_lookup(os, MASTER_NODE_OBJ, ZPL_VERSION_STR, 8, 1, version);
-	return (error);
-}
-
-int
 zfs_set_version(const char *name, uint64_t newvers)
 {
 	int error;
@@ -1696,6 +1621,49 @@
 	return (error);
 }
 
+/*
+ * Read a property stored within the master node.
+ */
+int
+zfs_get_zplprop(objset_t *os, zfs_prop_t prop, uint64_t *value)
+{
+	const char *pname;
+	int error;
+
+	/*
+	 * Look up the file system's value for the property.  For the
+	 * version property, we look up a slightly different string.
+	 * Also, there is no default VERSION value, so if we don't
+	 * find it, return the error.
+	 */
+	if (prop == ZFS_PROP_VERSION)
+		pname = ZPL_VERSION_STR;
+	else
+		pname = zfs_prop_to_name(prop);
+
+	error = zap_lookup(os, MASTER_NODE_OBJ, pname, 8, 1, value);
+
+	if (!error) {
+		return (0);
+	} else if (prop == ZFS_PROP_VERSION || error != ENOENT) {
+		return (error);
+	} else {
+		/* No value set, use the default value */
+		switch (prop) {
+		case ZFS_PROP_NORMALIZE:
+		case ZFS_PROP_UTF8ONLY:
+			*value = 0;
+			break;
+		case ZFS_PROP_CASE:
+			*value = ZFS_CASE_SENSITIVE;
+			break;
+		default:
+			return (ENOENT);
+		}
+	}
+	return (0);
+}
+
 static vfsdef_t vfw = {
 	VFSDEF_VERSION,
 	MNTTYPE_ZFS,
--- a/usr/src/uts/common/fs/zfs/zfs_vnops.c	Mon Nov 19 21:07:12 2007 -0800
+++ b/usr/src/uts/common/fs/zfs/zfs_vnops.c	Tue Nov 20 05:42:50 2007 -0800
@@ -52,11 +52,9 @@
 #include <sys/cmn_err.h>
 #include <sys/errno.h>
 #include <sys/unistd.h>
-#include <sys/zfs_vfsops.h>
 #include <sys/zfs_dir.h>
 #include <sys/zfs_acl.h>
 #include <sys/zfs_ioctl.h>
-#include <sys/zfs_i18n.h>
 #include <sys/fs/zfs.h>
 #include <sys/dmu.h>
 #include <sys/spa.h>
@@ -1056,7 +1054,7 @@
 		return (error);
 	}
 
-	if (zfsvfs->z_case & ZFS_UTF8_ONLY && u8_validate(nm, strlen(nm),
+	if (zfsvfs->z_utf8 && u8_validate(nm, strlen(nm),
 	    NULL, U8_VALIDATE_ENTIRE, &error) < 0) {
 		ZFS_EXIT(zfsvfs);
 		return (EILSEQ);
@@ -1139,7 +1137,7 @@
 	os = zfsvfs->z_os;
 	zilog = zfsvfs->z_log;
 
-	if (zfsvfs->z_case & ZFS_UTF8_ONLY && u8_validate(name, strlen(name),
+	if (zfsvfs->z_utf8 && u8_validate(name, strlen(name),
 	    NULL, U8_VALIDATE_ENTIRE, &error) < 0) {
 		ZFS_EXIT(zfsvfs);
 		return (EILSEQ);
@@ -1589,7 +1587,7 @@
 		return (EINVAL);
 	}
 
-	if (zfsvfs->z_case & ZFS_UTF8_ONLY && u8_validate(dirname,
+	if (zfsvfs->z_utf8 && u8_validate(dirname,
 	    strlen(dirname), NULL, U8_VALIDATE_ENTIRE, &error) < 0) {
 		ZFS_EXIT(zfsvfs);
 		return (EILSEQ);
@@ -2849,7 +2847,7 @@
 
 	tdzp = VTOZ(tdvp);
 	ZFS_VERIFY_ZP(tdzp);
-	if (zfsvfs->z_case & ZFS_UTF8_ONLY && u8_validate(tnm,
+	if (zfsvfs->z_utf8 && u8_validate(tnm,
 	    strlen(tnm), NULL, U8_VALIDATE_ENTIRE, &error) < 0) {
 		ZFS_EXIT(zfsvfs);
 		return (EILSEQ);
@@ -2892,7 +2890,7 @@
 		int nofold = (zfsvfs->z_norm & ~U8_TEXTPREP_TOUPPER);
 
 		cmp = u8_strcmp(snm, tnm, 0, nofold, U8_UNICODE_LATEST, &error);
-		ASSERT(error == 0 || !(zfsvfs->z_case & ZFS_UTF8_ONLY));
+		ASSERT(error == 0 || !zfsvfs->z_utf8);
 		if (cmp == 0) {
 			/*
 			 * POSIX: "If the old argument and the new argument
@@ -2921,8 +2919,9 @@
 		 * is an exact match, we will allow this to proceed as
 		 * a name-change request.
 		 */
-		if ((zfsvfs->z_case & ZFS_CI_ONLY ||
-		    (zfsvfs->z_case & ZFS_CI_MIXD && flags & FIGNORECASE)) &&
+		if ((zfsvfs->z_case == ZFS_CASE_INSENSITIVE ||
+		    (zfsvfs->z_case == ZFS_CASE_MIXED &&
+		    flags & FIGNORECASE)) &&
 		    u8_strcmp(snm, tnm, 0, zfsvfs->z_norm, U8_UNICODE_LATEST,
 		    &error) == 0) {
 			/*
@@ -3130,7 +3129,7 @@
 	ZFS_VERIFY_ZP(dzp);
 	zilog = zfsvfs->z_log;
 
-	if (zfsvfs->z_case & ZFS_UTF8_ONLY && u8_validate(name, strlen(name),
+	if (zfsvfs->z_utf8 && u8_validate(name, strlen(name),
 	    NULL, U8_VALIDATE_ENTIRE, &error) < 0) {
 		ZFS_EXIT(zfsvfs);
 		return (EILSEQ);
@@ -3339,7 +3338,7 @@
 	szp = VTOZ(svp);
 	ZFS_VERIFY_ZP(szp);
 
-	if (zfsvfs->z_case & ZFS_UTF8_ONLY && u8_validate(name,
+	if (zfsvfs->z_utf8 && u8_validate(name,
 	    strlen(name), NULL, U8_VALIDATE_ENTIRE, &error) < 0) {
 		ZFS_EXIT(zfsvfs);
 		return (EILSEQ);
@@ -4288,7 +4287,6 @@
 
 	case _PC_SATTR_ENABLED:
 	case _PC_SATTR_EXISTS:
-		zp = VTOZ(vp);
 		*valp = vfs_has_feature(vp->v_vfsp, VFSFT_XVATTR) &&
 		    (vp->v_type == VREG || vp->v_type == VDIR);
 		return (0);
--- a/usr/src/uts/common/fs/zfs/zfs_znode.c	Mon Nov 19 21:07:12 2007 -0800
+++ b/usr/src/uts/common/fs/zfs/zfs_znode.c	Tue Nov 20 05:42:50 2007 -0800
@@ -36,6 +36,7 @@
 #include <sys/resource.h>
 #include <sys/mntent.h>
 #include <sys/mkdev.h>
+#include <sys/u8_textprep.h>
 #include <sys/vfs.h>
 #include <sys/vfs_opreg.h>
 #include <sys/vnode.h>
@@ -52,7 +53,6 @@
 #include <sys/zfs_ioctl.h>
 #include <sys/zfs_rlock.h>
 #include <sys/zfs_fuid.h>
-#include <sys/zfs_i18n.h>
 #include <sys/fs/zfs.h>
 #include <sys/kidmap.h>
 #endif /* _KERNEL */
@@ -63,6 +63,8 @@
 #include <sys/zap.h>
 #include <sys/zfs_znode.h>
 
+#include "zfs_prop.h"
+
 /*
  * Functions needed for userland (ie: libzpool) are not put under
  * #ifdef_KERNEL; the rest of the functions have dependencies
@@ -255,6 +257,7 @@
 	int		i, error;
 	dmu_object_info_t doi;
 	uint64_t fsid_guid;
+	uint64_t zval;
 
 	*zpp = NULL;
 
@@ -265,6 +268,7 @@
 	if (dmu_object_info(os, MASTER_NODE_OBJ, &doi) == ENOENT) {
 		dmu_tx_t *tx = dmu_tx_create(os);
 		uint64_t zpl_version;
+		nvlist_t *zprops;
 
 		dmu_tx_hold_zap(tx, DMU_NEW_OBJECT, TRUE, NULL); /* master */
 		dmu_tx_hold_zap(tx, DMU_NEW_OBJECT, TRUE, NULL); /* del queue */
@@ -275,12 +279,16 @@
 			zpl_version = ZPL_VERSION;
 		else
 			zpl_version = ZPL_VERSION_FUID - 1;
-		zfs_create_fs(os, cr, zpl_version, 0, tx);
+
+		VERIFY(nvlist_alloc(&zprops, NV_UNIQUE_NAME, KM_SLEEP) == 0);
+		VERIFY(nvlist_add_uint64(zprops,
+		    zfs_prop_to_name(ZFS_PROP_VERSION), zpl_version) == 0);
+		zfs_create_fs(os, cr, zprops, tx);
+		nvlist_free(zprops);
 		dmu_tx_commit(tx);
 	}
 
-	error = zap_lookup(os, MASTER_NODE_OBJ, ZPL_VERSION_STR, 8, 1,
-	    &zfsvfs->z_version);
+	error = zfs_get_zplprop(os, ZFS_PROP_VERSION, &zfsvfs->z_version);
 	if (error) {
 		return (error);
 	} else if (zfsvfs->z_version > ZPL_VERSION) {
@@ -291,6 +299,23 @@
 		return (ENOTSUP);
 	}
 
+	if ((error = zfs_get_zplprop(os, ZFS_PROP_NORMALIZE, &zval)) != 0)
+		return (error);
+	zfsvfs->z_norm = (int)zval;
+	if ((error = zfs_get_zplprop(os, ZFS_PROP_UTF8ONLY, &zval)) != 0)
+		return (error);
+	zfsvfs->z_utf8 = (zval != 0);
+	if ((error = zfs_get_zplprop(os, ZFS_PROP_CASE, &zval)) != 0)
+		return (error);
+	zfsvfs->z_case = (uint_t)zval;
+	/*
+	 * Fold case on file systems that are always or sometimes case
+	 * insensitive.
+	 */
+	if (zfsvfs->z_case == ZFS_CASE_INSENSITIVE ||
+	    zfsvfs->z_case == ZFS_CASE_MIXED)
+		zfsvfs->z_norm |= U8_TEXTPREP_TOUPPER;
+
 	/*
 	 * The fsid is 64 bits, composed of an 8-bit fs type, which
 	 * separates our fsid from any other filesystem types, and a
@@ -1170,11 +1195,14 @@
 }
 
 void
-zfs_create_fs(objset_t *os, cred_t *cr, uint64_t version,
-    int norm, dmu_tx_t *tx)
+zfs_create_fs(objset_t *os, cred_t *cr, nvlist_t *zplprops, dmu_tx_t *tx)
 {
 	zfsvfs_t	zfsvfs;
 	uint64_t	moid, doid;
+	uint64_t	version = 0;
+	uint64_t	sense = ZFS_CASE_SENSITIVE;
+	uint64_t	norm = 0;
+	nvpair_t	*elem;
 	int		error;
 	znode_t		*rootzp = NULL;
 	vnode_t		*vp;
@@ -1196,9 +1224,29 @@
 	/*
 	 * Set starting attributes.
 	 */
+	elem = NULL;
+	while ((elem = nvlist_next_nvpair(zplprops, elem)) != NULL) {
+		/* For the moment we expect all zpl props to be uint64_ts */
+		uint64_t val;
+		char *name;
 
-	error = zap_update(os, moid, ZPL_VERSION_STR, 8, 1, &version, tx);
-	ASSERT(error == 0);
+		ASSERT(nvpair_type(elem) == DATA_TYPE_UINT64);
+		ASSERT(nvpair_value_uint64(elem, &val) == 0);
+		name = nvpair_name(elem);
+		if (strcmp(name, zfs_prop_to_name(ZFS_PROP_VERSION)) == 0) {
+			version = val;
+			error = zap_update(os, moid, ZPL_VERSION_STR,
+			    8, 1, &version, tx);
+		} else {
+			error = zap_update(os, moid, name, 8, 1, &val, tx);
+		}
+		ASSERT(error == 0);
+		if (strcmp(name, zfs_prop_to_name(ZFS_PROP_NORMALIZE)) == 0)
+			norm = val;
+		else if (strcmp(name, zfs_prop_to_name(ZFS_PROP_CASE)) == 0)
+			sense = val;
+	}
+	ASSERT(version != 0);
 
 	/*
 	 * Create a delete queue.
@@ -1235,6 +1283,12 @@
 	zfsvfs.z_version = version;
 	zfsvfs.z_use_fuids = USE_FUIDS(version, os);
 	zfsvfs.z_norm = norm;
+	/*
+	 * Fold case on file systems that are always or sometimes case
+	 * insensitive.
+	 */
+	if (sense == ZFS_CASE_INSENSITIVE || sense == ZFS_CASE_MIXED)
+		zfsvfs.z_norm |= U8_TEXTPREP_TOUPPER;
 
 	mutex_init(&zfsvfs.z_znodes_lock, NULL, MUTEX_DEFAULT, NULL);
 	list_create(&zfsvfs.z_all_znodes, sizeof (znode_t),
--- a/usr/src/uts/common/sys/fs/zfs.h	Mon Nov 19 21:07:12 2007 -0800
+++ b/usr/src/uts/common/sys/fs/zfs.h	Tue Nov 20 05:42:50 2007 -0800
@@ -253,7 +253,6 @@
 #define	SPA_VERSION_SLOGS		SPA_VERSION_7
 #define	SPA_VERSION_DELEGATED_PERMS	SPA_VERSION_8
 #define	SPA_VERSION_FUID		SPA_VERSION_9
-#define	SPA_VERSION_NORMALIZATION	SPA_VERSION_9
 #define	SPA_VERSION_REFRESERVATION	SPA_VERSION_9
 #define	SPA_VERSION_REFQUOTA		SPA_VERSION_9
 #define	SPA_VERSION_UNIQUE_ACCURATE	SPA_VERSION_9
@@ -276,6 +275,7 @@
 #define	ZPL_VERSION_INITIAL		ZPL_VERSION_1
 #define	ZPL_VERSION_DIRENT_TYPE		ZPL_VERSION_2
 #define	ZPL_VERSION_FUID		ZPL_VERSION_3
+#define	ZPL_VERSION_NORMALIZATION	ZPL_VERSION_3
 #define	ZPL_VERSION_SYSATTR		ZPL_VERSION_3
 
 /*
@@ -497,7 +497,7 @@
 	ZFS_IOC_VDEV_DETACH,
 	ZFS_IOC_VDEV_SETPATH,
 	ZFS_IOC_OBJSET_STATS,
-	ZFS_IOC_OBJSET_VERSION,
+	ZFS_IOC_OBJSET_ZPLPROPS,
 	ZFS_IOC_DATASET_LIST_NEXT,
 	ZFS_IOC_SNAPSHOT_LIST_NEXT,
 	ZFS_IOC_SET_PROP,