6654808 FIGNORECASE lookups in a zfs xattr dir don't provide 'realname' data
authortimh
Sun, 27 Apr 2008 10:20:38 -0700
changeset 6492 903545192033
parent 6491 448e02e63395
child 6493 9a48da4ec444
6654808 FIGNORECASE lookups in a zfs xattr dir don't provide 'realname' data 6666748 System panic occurred when attempting to view .zfs snapshot directory from CIFS client. 6693201 libzpool needlessly defines u8_textprep_str() 6694236 case-insensitive zfs file system doesn't work when created on sparc and then imported on x86
usr/src/cmd/ztest/ztest.c
usr/src/lib/libzpool/common/kernel.c
usr/src/uts/common/fs/ctfs/ctfs_all.c
usr/src/uts/common/fs/ctfs/ctfs_latest.c
usr/src/uts/common/fs/ctfs/ctfs_tdir.c
usr/src/uts/common/fs/gfs.c
usr/src/uts/common/fs/lookup.c
usr/src/uts/common/fs/objfs/objfs_root.c
usr/src/uts/common/fs/xattr.c
usr/src/uts/common/fs/zfs/dmu_objset.c
usr/src/uts/common/fs/zfs/dmu_send.c
usr/src/uts/common/fs/zfs/dsl_dataset.c
usr/src/uts/common/fs/zfs/sys/dmu.h
usr/src/uts/common/fs/zfs/sys/dmu_objset.h
usr/src/uts/common/fs/zfs/sys/dsl_dataset.h
usr/src/uts/common/fs/zfs/sys/zfs_ioctl.h
usr/src/uts/common/fs/zfs/zap_micro.c
usr/src/uts/common/fs/zfs/zfs_ctldir.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_vnops.c
usr/src/uts/common/fs/zfs/zfs_znode.c
usr/src/uts/common/sys/gfs.h
usr/src/uts/common/sys/vnode.h
--- a/usr/src/cmd/ztest/ztest.c	Sat Apr 26 21:58:06 2008 -0700
+++ b/usr/src/cmd/ztest/ztest.c	Sun Apr 27 10:20:38 2008 -0700
@@ -1237,8 +1237,8 @@
 	/*
 	 * Verify that we can create a new dataset.
 	 */
-	error = dmu_objset_create(name, DMU_OST_OTHER, NULL, ztest_create_cb,
-	    NULL);
+	error = dmu_objset_create(name, DMU_OST_OTHER, NULL, 0,
+	    ztest_create_cb, NULL);
 	if (error) {
 		if (error == ENOSPC) {
 			ztest_record_enospc("dmu_objset_create");
@@ -1293,7 +1293,7 @@
 	/*
 	 * Verify that we cannot create an existing dataset.
 	 */
-	error = dmu_objset_create(name, DMU_OST_OTHER, NULL, NULL, NULL);
+	error = dmu_objset_create(name, DMU_OST_OTHER, NULL, 0, NULL, NULL);
 	if (error != EEXIST)
 		fatal(0, "created existing dataset, error = %d", error);
 
@@ -3219,7 +3219,7 @@
 			int test_future = FALSE;
 			(void) rw_rdlock(&ztest_shared->zs_name_lock);
 			(void) snprintf(name, 100, "%s/%s_%d", pool, pool, d);
-			error = dmu_objset_create(name, DMU_OST_OTHER, NULL,
+			error = dmu_objset_create(name, DMU_OST_OTHER, NULL, 0,
 			    ztest_create_cb, NULL);
 			if (error == EEXIST) {
 				test_future = TRUE;
--- a/usr/src/lib/libzpool/common/kernel.c	Sat Apr 26 21:58:06 2008 -0700
+++ b/usr/src/lib/libzpool/common/kernel.c	Sun Apr 27 10:20:38 2008 -0700
@@ -825,14 +825,6 @@
 	return (ret);
 }
 
-/*ARGSUSED*/
-size_t u8_textprep_str(char *i, size_t *il, char *o, size_t *ol, int nf,
-    size_t vers, int *err)
-{
-	*err = EINVAL;
-	return ((size_t)-1);
-}
-
 uid_t
 crgetuid(cred_t *cr)
 {
--- a/usr/src/uts/common/fs/ctfs/ctfs_all.c	Sat Apr 26 21:58:06 2008 -0700
+++ b/usr/src/uts/common/fs/ctfs/ctfs_all.c	Sun Apr 27 10:20:38 2008 -0700
@@ -19,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -51,7 +51,7 @@
 static int ctfs_adir_do_readdir(vnode_t *, void *, int *, offset_t *,
     offset_t *, void *, int);
 static int ctfs_adir_do_lookup(vnode_t *, const char *, vnode_t **, ino64_t *,
-    cred_t *);
+    cred_t *, int, int *, pathname_t *);
 
 /*
  * ctfs_create_adirnode
@@ -98,7 +98,7 @@
 /* ARGSUSED */
 static int
 ctfs_adir_do_lookup(vnode_t *vp, const char *nm, vnode_t **vpp, ino64_t *inop,
-    cred_t *cr)
+    cred_t *cr, int flags, int *deflags, pathname_t *rpnp)
 {
 	int i;
 	contract_t *ct;
@@ -127,9 +127,7 @@
 	ctid_t next;
 	struct dirent64 *odp = dp;
 
-	/* ctfs does not support V_RDDIR_ENTFLAGS */
-	if (flags & V_RDDIR_ENTFLAGS)
-		return (ENOTSUP);
+	ASSERT(!(flags & V_RDDIR_ENTFLAGS));
 
 	zuniqid = VTOZONE(vp)->zone_uniqid;
 	next = contract_lookup(zuniqid, *offp);
--- a/usr/src/uts/common/fs/ctfs/ctfs_latest.c	Sat Apr 26 21:58:06 2008 -0700
+++ b/usr/src/uts/common/fs/ctfs/ctfs_latest.c	Sun Apr 27 10:20:38 2008 -0700
@@ -19,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -77,7 +77,8 @@
 
 		gfs_file_set_index(cvp, -1);
 
-		VERIFY(gfs_dir_lookup(cvp, "status", &svp, cr) == 0);
+		VERIFY(gfs_dir_lookup(cvp, "status", &svp,
+		    cr, 0, NULL, NULL) == 0);
 
 		VN_RELE(cvp);
 
--- a/usr/src/uts/common/fs/ctfs/ctfs_tdir.c	Sat Apr 26 21:58:06 2008 -0700
+++ b/usr/src/uts/common/fs/ctfs/ctfs_tdir.c	Sun Apr 27 10:20:38 2008 -0700
@@ -19,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -59,7 +59,7 @@
 static int ctfs_tdir_do_readdir(vnode_t *, void *, int *, offset_t *,
     offset_t *, void *, int);
 static int ctfs_tdir_do_lookup(vnode_t *, const char *, vnode_t **, ino64_t *,
-    cred_t *);
+    cred_t *, int, int *, pathname_t *);
 static ino64_t ctfs_tdir_do_inode(vnode_t *, int);
 
 /*
@@ -115,9 +115,7 @@
 	ct_type_t *ty = ct_types[gfs_file_index(vp)];
 	struct dirent64 *odp = dp;
 
-	/* ctfs does not support V_RDDIR_ENTFLAGS */
-	if (flags & V_RDDIR_ENTFLAGS)
-		return (ENOTSUP);
+	ASSERT(!(flags & V_RDDIR_ENTFLAGS));
 
 	zuniqid = VTOZONE(vp)->zone_uniqid;
 	next = contract_type_lookup(ty, zuniqid, *offp);
@@ -138,7 +136,7 @@
 /* ARGSUSED */
 static int
 ctfs_tdir_do_lookup(vnode_t *vp, const char *nm, vnode_t **vpp, ino64_t *inop,
-    cred_t *cr)
+    cred_t *cr, int flags, int *deflags, pathname_t *rpnp)
 {
 	int i;
 	contract_t *ct;
--- a/usr/src/uts/common/fs/gfs.c	Sat Apr 26 21:58:06 2008 -0700
+++ b/usr/src/uts/common/fs/gfs.c	Sun Apr 27 10:20:38 2008 -0700
@@ -35,6 +35,7 @@
 #include <sys/mutex.h>
 #include <sys/sysmacros.h>
 #include <sys/systm.h>
+#include <sys/sunddi.h>
 #include <sys/uio.h>
 #include <sys/vmsystm.h>
 #include <sys/vfs.h>
@@ -706,43 +707,102 @@
 }
 
 /*
- * gfs_dir_lookup()
+ * gfs_dir_lookup_dynamic()
  *
- * Looks up the given name in the directory and returns the corresponding vnode,
- * if found.
+ * This routine looks up the provided name amongst the dynamic entries
+ * in the gfs directory and returns the corresponding vnode, if found.
  *
- * First, we search statically defined entries, if any.  If a match is found,
- * and GFS_CACHE_VNODE is set and the vnode exists, we simply return the
- * existing vnode.  Otherwise, we call the static entry's callback routine,
- * caching the result if necessary.
+ * The gfs directory is expected to be locked by the caller prior to
+ * calling this function.  The directory will be unlocked during the
+ * execution of this function, but will be locked upon return from the
+ * function.  This function returns 0 on success, non-zero on error.
  *
- * If no static entry is found, we invoke the lookup callback, if any.  The
- * arguments to this callback are:
+ * The dynamic lookups are performed by invoking the lookup
+ * callback, which is passed to this function as the first argument.
+ * The arguments to the callback are:
  *
- * int gfs_lookup_cb(vnode_t *pvp, const char *nm, vnode_t **vpp, cred_t *cr);
+ * int gfs_lookup_cb(vnode_t *pvp, const char *nm, vnode_t **vpp, cred_t *cr,
+ *     int flags, int *deflgs, pathname_t *rpnp);
  *
  *	pvp	- parent vnode
  *	nm	- name of entry
  *	vpp	- pointer to resulting vnode
  *	cr	- pointer to cred
+ *	flags	- flags value from lookup request
+ *		ignored here; currently only used to request
+ *		insensitive lookups
+ *	direntflgs - output parameter, directory entry flags
+ *		ignored here; currently only used to indicate a lookup
+ *		has more than one possible match when case is not considered
+ *	realpnp	- output parameter, real pathname
+ *		ignored here; when lookup was performed case-insensitively,
+ *		this field contains the "real" name of the file.
  *
  * 	Returns 0 on success, non-zero on error.
  */
-int
-gfs_dir_lookup(vnode_t *dvp, const char *nm, vnode_t **vpp, cred_t *cr)
+static int
+gfs_dir_lookup_dynamic(gfs_lookup_cb callback, gfs_dir_t *dp,
+    const char *nm, vnode_t *dvp, vnode_t **vpp, cred_t *cr, int flags,
+    int *direntflags, pathname_t *realpnp)
 {
-	int i;
+	gfs_file_t *fp;
+	ino64_t ino;
+	int ret;
+
+	ASSERT(GFS_DIR_LOCKED(dp));
+
+	/*
+	 * Drop the directory lock, as the lookup routine
+	 * will need to allocate memory, or otherwise deadlock on this
+	 * directory.
+	 */
+	gfs_dir_unlock(dp);
+	ret = callback(dvp, nm, vpp, &ino, cr, flags, direntflags, realpnp);
+	gfs_dir_lock(dp);
+
+	/*
+	 * The callback for extended attributes returns a vnode
+	 * with v_data from an underlying fs.
+	 */
+	if (ret == 0 && !IS_XATTRDIR(dvp)) {
+		fp = (gfs_file_t *)((*vpp)->v_data);
+		fp->gfs_index = -1;
+		fp->gfs_ino = ino;
+	}
+
+	return (ret);
+}
+
+/*
+ * gfs_dir_lookup_static()
+ *
+ * This routine looks up the provided name amongst the static entries
+ * in the gfs directory and returns the corresponding vnode, if found.
+ * The first argument to the function is a pointer to the comparison
+ * function this function should use to decide if names are a match.
+ *
+ * If a match is found, and GFS_CACHE_VNODE is set and the vnode
+ * exists, we simply return the existing vnode.  Otherwise, we call
+ * the static entry's callback routine, caching the result if
+ * necessary.  If the idx pointer argument is non-NULL, we use it to
+ * return the index of the matching static entry.
+ *
+ * The gfs directory is expected to be locked by the caller prior to calling
+ * this function.  The directory may be unlocked during the execution of
+ * this function, but will be locked upon return from the function.
+ *
+ * This function returns 0 if a match is found, ENOENT if not.
+ */
+static int
+gfs_dir_lookup_static(int (*compare)(const char *, const char *),
+    gfs_dir_t *dp, const char *nm, vnode_t *dvp, int *idx,
+    vnode_t **vpp, pathname_t *rpnp)
+{
 	gfs_dirent_t *ge;
-	vnode_t *vp;
-	gfs_dir_t *dp = dvp->v_data;
-	int ret = 0;
+	vnode_t *vp = NULL;
+	int i;
 
-	ASSERT(dvp->v_type == VDIR);
-
-	if (gfs_lookup_dot(vpp, dvp, dp->gfsd_file.gfs_parent, nm) == 0)
-		return (0);
-
-	gfs_dir_lock(dp);
+	ASSERT(GFS_DIR_LOCKED(dp));
 
 	/*
 	 * Search static entries.
@@ -750,12 +810,16 @@
 	for (i = 0; i < dp->gfsd_nstatic; i++) {
 		ge = &dp->gfsd_static[i];
 
-		if (strcmp(ge->gfse_name, nm) == 0) {
+		if (compare(ge->gfse_name, nm) == 0) {
+			if (rpnp)
+				(void) strlcpy(rpnp->pn_buf, ge->gfse_name,
+				    rpnp->pn_bufsize);
+
 			if (ge->gfse_vnode) {
 				ASSERT(ge->gfse_flags & GFS_CACHE_VNODE);
 				vp = ge->gfse_vnode;
 				VN_HOLD(vp);
-				goto out;
+				break;
 			}
 
 			/*
@@ -763,8 +827,8 @@
 			 * need to do KM_SLEEP allocations.  If we return from
 			 * the constructor only to find that a parallel
 			 * operation has completed, and GFS_CACHE_VNODE is set
-			 * for this entry, we discard the result in favor of the
-			 * cached vnode.
+			 * for this entry, we discard the result in favor of
+			 * the cached vnode.
 			 */
 			gfs_dir_unlock(dp);
 			vp = ge->gfse_ctor(dvp);
@@ -797,57 +861,94 @@
 					gfs_dir_lock(dp);
 				}
 			}
-
-			goto out;
+			break;
 		}
 	}
 
-	/*
-	 * See if there is a dynamic constructor.
-	 */
-	if (dp->gfsd_lookup) {
-		ino64_t ino;
-		gfs_file_t *fp;
+	if (vp == NULL)
+		return (ENOENT);
+	else if (idx)
+		*idx = i;
+	*vpp = vp;
+	return (0);
+}
 
-		/*
-		 * Once again, drop the directory lock, as the lookup routine
-		 * will need to allocate memory, or otherwise deadlock on this
-		 * directory.
-		 */
-		gfs_dir_unlock(dp);
-		ret = dp->gfsd_lookup(dvp, nm, &vp, &ino, cr);
-		gfs_dir_lock(dp);
-		if (ret != 0)
-			goto out;
+/*
+ * gfs_dir_lookup()
+ *
+ * Looks up the given name in the directory and returns the corresponding
+ * vnode, if found.
+ *
+ * First, we search statically defined entries, if any, with a call to
+ * gfs_dir_lookup_static().  If no static entry is found, and we have
+ * a callback function we try a dynamic lookup via gfs_dir_lookup_dynamic().
+ *
+ * This function returns 0 on success, non-zero on error.
+ */
+int
+gfs_dir_lookup(vnode_t *dvp, const char *nm, vnode_t **vpp, cred_t *cr,
+    int flags, int *direntflags, pathname_t *realpnp)
+{
+	gfs_dir_t *dp = dvp->v_data;
+	boolean_t casecheck;
+	vnode_t *dynvp = NULL;
+	vnode_t *vp = NULL;
+	int (*compare)(const char *, const char *);
+	int error, idx;
+
+	ASSERT(dvp->v_type == VDIR);
+
+	if (gfs_lookup_dot(vpp, dvp, dp->gfsd_file.gfs_parent, nm) == 0)
+		return (0);
 
-		/*
-		 * The lookup_cb might be returning a non-GFS vnode.
-		 * Currently this is true for extended attributes,
-		 * where we're returning a vnode with v_data from an
-		 * underlying fs.
-		 */
-		if ((dvp->v_flag & V_XATTRDIR) == 0) {
-			fp = (gfs_file_t *)vp->v_data;
-			fp->gfs_index = -1;
-			fp->gfs_ino = ino;
+	casecheck = (flags & FIGNORECASE) != 0 && direntflags != NULL;
+	if (vfs_has_feature(dvp->v_vfsp, VFSFT_NOCASESENSITIVE) ||
+	    (flags & FIGNORECASE))
+		compare = strcasecmp;
+	else
+		compare = strcmp;
+
+	gfs_dir_lock(dp);
+
+	error = gfs_dir_lookup_static(compare, dp, nm, dvp, &idx, &vp, realpnp);
+
+	if (vp && casecheck) {
+		gfs_dirent_t *ge;
+		int i;
+
+		for (i = idx + 1; i < dp->gfsd_nstatic; i++) {
+			ge = &dp->gfsd_static[i];
+
+			if (strcasecmp(ge->gfse_name, nm) == 0) {
+				*direntflags |= ED_CASE_CONFLICT;
+				goto out;
+			}
 		}
-	} else {
-		/*
-		 * No static entry found, and there is no lookup callback, so
-		 * return ENOENT.
-		 */
-		ret = ENOENT;
+	}
+
+	if ((error || casecheck) && dp->gfsd_lookup)
+		error = gfs_dir_lookup_dynamic(dp->gfsd_lookup, dp, nm, dvp,
+		    &dynvp, cr, flags, direntflags, vp ? NULL : realpnp);
+
+	if (vp && dynvp) {
+		/* static and dynamic entries are case-insensitive conflict */
+		ASSERT(casecheck);
+		*direntflags |= ED_CASE_CONFLICT;
+		VN_RELE(dynvp);
+	} else if (vp == NULL) {
+		vp = dynvp;
+	} else if (error == ENOENT) {
+		error = 0;
+	} else if (error) {
+		VN_RELE(vp);
+		vp = NULL;
 	}
 
 out:
 	gfs_dir_unlock(dp);
 
-	if (ret == 0)
-		*vpp = vp;
-	else
-		*vpp = NULL;
-
-	return (ret);
+	*vpp = vp;
+	return (error);
 }
 
 /*
@@ -964,7 +1065,7 @@
     int flags, vnode_t *rdir, cred_t *cr, caller_context_t *ct,
     int *direntflags, pathname_t *realpnp)
 {
-	return (gfs_dir_lookup(dvp, nm, vpp, cr));
+	return (gfs_dir_lookup(dvp, nm, vpp, cr, flags, direntflags, realpnp));
 }
 
 /*
--- a/usr/src/uts/common/fs/lookup.c	Sat Apr 26 21:58:06 2008 -0700
+++ b/usr/src/uts/common/fs/lookup.c	Sun Apr 27 10:20:38 2008 -0700
@@ -19,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -525,7 +525,7 @@
 				 * Return the case-preserved name
 				 * within the resolved path.
 				 */
-				error = copystr(pp->pn_path,
+				error = copystr(pp->pn_buf,
 				    rpnp->pn_path + rpnp->pn_pathlen,
 				    rpnp->pn_bufsize - rpnp->pn_pathlen, &len);
 			} else {
--- a/usr/src/uts/common/fs/objfs/objfs_root.c	Sat Apr 26 21:58:06 2008 -0700
+++ b/usr/src/uts/common/fs/objfs/objfs_root.c	Sun Apr 27 10:20:38 2008 -0700
@@ -19,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -39,7 +39,7 @@
 extern int last_module_id;
 
 static int objfs_root_do_lookup(vnode_t *, const char *, vnode_t **, ino64_t *,
-    cred_t *);
+    cred_t *, int, int *, pathname_t *);
 static int objfs_root_do_readdir(vnode_t *, void *, int *,
     offset_t *, offset_t *, void *, int);
 
@@ -72,7 +72,7 @@
 /* ARGSUSED */
 static int
 objfs_root_do_lookup(vnode_t *vp, const char *nm, vnode_t **vpp, ino64_t *inop,
-    cred_t *cr)
+    cred_t *cr, int flags, int *deflags, pathname_t *rpnp)
 {
 	int result = ENOENT;
 	struct modctl *mp;
@@ -112,9 +112,7 @@
 	struct modctl *mp = *mpp;
 	struct dirent64 *odp = dp;
 
-	/* objfs does not support V_RDDIR_ENTFLAGS */
-	if (flags & V_RDDIR_ENTFLAGS)
-		return (ENOTSUP);
+	ASSERT(!(flags & V_RDDIR_ENTFLAGS));
 
 	mutex_enter(&mod_lock);
 
--- a/usr/src/uts/common/fs/xattr.c	Sat Apr 26 21:58:06 2008 -0700
+++ b/usr/src/uts/common/fs/xattr.c	Sun Apr 27 10:20:38 2008 -0700
@@ -978,7 +978,7 @@
 	 * Don't allow creation of extended attributes with sysattr names.
 	 */
 	if (is_sattr_name(name)) {
-		return (gfs_dir_lookup(dvp, name, vpp, cr));
+		return (gfs_dir_lookup(dvp, name, vpp, cr, 0, NULL, NULL));
 	}
 
 	error = xattr_dir_realdir(dvp, &pvp, LOOKUP_XATTR|CREATE_XATTR_DIR,
@@ -1096,14 +1096,13 @@
 	int error;
 	vnode_t *vp;
 	struct pathname pn;
-	int flags = FIGNORECASE;
 
 	*eflags = 0;
 
 	error = pn_get(nm, UIO_SYSSPACE, &pn);
 	if (error == 0) {
-		error = VOP_LOOKUP(dvp, nm, &vp, &pn, LOOKUP_XATTR, rootvp,
-		    cr, ct, &flags, NULL);
+		error = VOP_LOOKUP(dvp, nm, &vp, &pn,
+		    FIGNORECASE, rootvp, cr, ct, NULL, NULL);
 		if (error == 0) {
 			*eflags = ED_CASE_CONFLICT;
 			VN_RELE(vp);
@@ -1170,13 +1169,14 @@
 		while ((error = gfs_readdir_pred(&gstate, uiop, &off)) == 0 &&
 		    !*eofp) {
 			if (off >= 0 && off < dp->gfsd_nstatic) {
-				int eflags = 0;
+				int eflags;
 
 				/*
 				 * Check to see if this sysattr set name has a
 				 * case-insensitive conflict with a real xattr
 				 * name.
 				 */
+				eflags = 0;
 				if ((flags & V_RDDIR_ENTFLAGS) && has_xattrs) {
 					error = readdir_xattr_casecmp(pvp,
 					    dp->gfsd_static[off].gfse_name,
@@ -1286,7 +1286,7 @@
 
 static int
 xattr_lookup_cb(vnode_t *vp, const char *nm, vnode_t **vpp, ino64_t *inop,
-    cred_t *cr)
+    cred_t *cr, int flags, int *deflags, pathname_t *rpnp)
 {
 	vnode_t *pvp;
 	struct pathname pn;
@@ -1310,8 +1310,8 @@
 
 	error = pn_get((char *)nm, UIO_SYSSPACE, &pn);
 	if (error == 0) {
-		error = VOP_LOOKUP(pvp, (char *)nm, vpp, &pn, 0, rootvp,
-		    cr, NULL, NULL, NULL);
+		error = VOP_LOOKUP(pvp, (char *)nm, vpp, &pn, flags, rootvp,
+		    cr, NULL, deflags, rpnp);
 		pn_free(&pn);
 	}
 	VN_RELE(pvp);
--- a/usr/src/uts/common/fs/zfs/dmu_objset.c	Sat Apr 26 21:58:06 2008 -0700
+++ b/usr/src/uts/common/fs/zfs/dmu_objset.c	Sun Apr 27 10:20:38 2008 -0700
@@ -481,6 +481,7 @@
 	dsl_dataset_t *clone_parent;
 	const char *lastname;
 	dmu_objset_type_t type;
+	uint64_t flags;
 };
 
 /*ARGSUSED*/
@@ -527,7 +528,7 @@
 	ASSERT(dmu_tx_is_syncing(tx));
 
 	dsobj = dsl_dataset_create_sync(dd, oa->lastname,
-	    oa->clone_parent, cr, tx);
+	    oa->clone_parent, oa->flags, cr, tx);
 
 	VERIFY(0 == dsl_dataset_open_obj(dd->dd_pool, dsobj, NULL,
 	    DS_MODE_STANDARD | DS_MODE_READONLY, FTAG, &ds));
@@ -551,7 +552,7 @@
 
 int
 dmu_objset_create(const char *name, dmu_objset_type_t type,
-    objset_t *clone_parent,
+    objset_t *clone_parent, uint64_t flags,
     void (*func)(objset_t *os, void *arg, cred_t *cr, dmu_tx_t *tx), void *arg)
 {
 	dsl_dir_t *pdd;
@@ -574,6 +575,7 @@
 	oa.userarg = arg;
 	oa.lastname = tail;
 	oa.type = type;
+	oa.flags = flags;
 
 	if (clone_parent != NULL) {
 		/*
@@ -946,6 +948,21 @@
 }
 
 int
+dmu_snapshot_realname(objset_t *os, char *name, char *real, int maxlen,
+    boolean_t *conflict)
+{
+	dsl_dataset_t *ds = os->os->os_dsl_dataset;
+	uint64_t ignored;
+
+	if (ds->ds_phys->ds_snapnames_zapobj == 0)
+		return (ENOENT);
+
+	return (zap_lookup_norm(ds->ds_dir->dd_pool->dp_meta_objset,
+	    ds->ds_phys->ds_snapnames_zapobj, name, 8, 1, &ignored, MT_FIRST,
+	    real, maxlen, conflict));
+}
+
+int
 dmu_snapshot_list_next(objset_t *os, int namelen, char *name,
     uint64_t *idp, uint64_t *offp, boolean_t *case_conflict)
 {
--- a/usr/src/uts/common/fs/zfs/dmu_send.c	Sat Apr 26 21:58:06 2008 -0700
+++ b/usr/src/uts/common/fs/zfs/dmu_send.c	Sun Apr 27 10:20:38 2008 -0700
@@ -269,6 +269,9 @@
 	if (fromorigin)
 		drr->drr_u.drr_begin.drr_flags |= DRR_FLAG_CLONE;
 	drr->drr_u.drr_begin.drr_toguid = ds->ds_phys->ds_guid;
+	if (ds->ds_phys->ds_flags & DS_FLAG_CI_DATASET)
+		drr->drr_u.drr_begin.drr_flags |= DRR_FLAG_CI_DATA;
+
 	if (fromds)
 		drr->drr_u.drr_begin.drr_fromguid = fromds->ds_phys->ds_guid;
 	dsl_dataset_name(ds, drr->drr_u.drr_begin.drr_toname);
@@ -322,6 +325,7 @@
 	dmu_objset_type_t type;
 	void *tag;
 	boolean_t force;
+	uint64_t dsflags;
 	char clonelastname[MAXNAMELEN];
 	dsl_dataset_t *ds; /* the ds to recv into; returned from the syncfunc */
 };
@@ -340,9 +344,6 @@
 		    ds, &ds->ds_phys->ds_bp, type, tx);
 	}
 
-	dmu_buf_will_dirty(ds->ds_dbuf, tx);
-	ds->ds_phys->ds_flags |= DS_FLAG_INCONSISTENT;
-
 	spa_history_internal_log(LOG_DS_REPLAY_FULL_SYNC,
 	    ds->ds_dir->dd_pool->dp_spa, tx, cr, "dataset = %lld",
 	    ds->ds_phys->ds_dir_obj);
@@ -385,9 +386,12 @@
 	dsl_dir_t *dd = arg1;
 	struct recvbeginsyncarg *rbsa = arg2;
 	uint64_t dsobj;
+	uint64_t flags = DS_FLAG_INCONSISTENT;
+
+	flags |= rbsa->dsflags;
 
 	dsobj = dsl_dataset_create_sync(dd, strrchr(rbsa->tofs, '/') + 1,
-	    rbsa->origin, cr, tx);
+	    rbsa->origin, flags, cr, tx);
 
 	rbsa->ds = recv_full_sync_impl(dd->dd_pool, dsobj,
 	    rbsa->origin ? DMU_OST_NONE : rbsa->type, cr, tx);
@@ -432,6 +436,9 @@
 	struct recvbeginsyncarg *rbsa = arg2;
 	dsl_dir_t *dd = ds->ds_dir;
 	uint64_t dsobj;
+	uint64_t flags = DS_FLAG_INCONSISTENT;
+
+	flags |= rbsa->dsflags;
 
 	/*
 	 * NB: caller must provide an extra hold on the dsl_dir_t, so it
@@ -440,7 +447,7 @@
 	 */
 	dsl_dataset_destroy_sync(ds, rbsa->tag, cr, tx);
 
-	dsobj = dsl_dataset_create_sync_impl(dd, rbsa->origin, tx);
+	dsobj = dsl_dataset_create_sync_impl(dd, rbsa->origin, flags, tx);
 
 	rbsa->ds = recv_full_sync_impl(dd->dd_pool, dsobj,
 	    rbsa->origin ? DMU_OST_NONE : rbsa->type, cr, tx);
@@ -495,12 +502,15 @@
 	dsl_pool_t *dp = ohds->ds_dir->dd_pool;
 	dsl_dataset_t *ods, *cds;
 	uint64_t dsobj;
+	uint64_t flags = DS_FLAG_INCONSISTENT;
+
+	flags |= rbsa->dsflags;
 
 	/* create the temporary clone */
 	VERIFY(0 == dsl_dataset_open_obj(dp, ohds->ds_phys->ds_prev_snap_obj,
 	    NULL, DS_MODE_STANDARD, FTAG, &ods));
 	dsobj = dsl_dataset_create_sync(ohds->ds_dir,
-	    rbsa->clonelastname, ods, cr, tx);
+	    rbsa->clonelastname, ods, flags, cr, tx);
 	dsl_dataset_close(ods, DS_MODE_STANDARD, FTAG);
 
 	/* open the temporary clone */
@@ -511,9 +521,6 @@
 	if (ohds->ds_quota > 0)
 		dsl_dataset_set_quota_sync(cds, &ohds->ds_quota, cr, tx);
 
-	dmu_buf_will_dirty(cds->ds_dbuf, tx);
-	cds->ds_phys->ds_flags |= DS_FLAG_INCONSISTENT;
-
 	rbsa->ds = cds;
 
 	spa_history_internal_log(LOG_DS_REPLAY_INC_SYNC,
@@ -563,6 +570,7 @@
 	rbsa.fromguid = drrb->drr_fromguid;
 	rbsa.type = drrb->drr_type;
 	rbsa.tag = FTAG;
+	rbsa.dsflags = 0;
 	version = drrb->drr_version;
 	flags = drrb->drr_flags;
 
@@ -578,6 +586,9 @@
 	    ((flags & DRR_FLAG_CLONE) && origin == NULL))
 		return (EINVAL);
 
+	if (flags & DRR_FLAG_CI_DATA)
+		rbsa.dsflags = DS_FLAG_CI_DATASET;
+
 	bzero(drc, sizeof (dmu_recv_cookie_t));
 	drc->drc_drrb = drrb;
 	drc->drc_tosnap = tosnap;
--- a/usr/src/uts/common/fs/zfs/dsl_dataset.c	Sat Apr 26 21:58:06 2008 -0700
+++ b/usr/src/uts/common/fs/zfs/dsl_dataset.c	Sun Apr 27 10:20:38 2008 -0700
@@ -290,6 +290,43 @@
 	return (err);
 }
 
+static int
+dsl_dataset_snap_lookup(objset_t *os, uint64_t flags,
+    uint64_t snapnames_zapobj, const char *name, uint64_t *value)
+{
+	matchtype_t mt;
+	int err;
+
+	if (flags & DS_FLAG_CI_DATASET)
+		mt = MT_FIRST;
+	else
+		mt = MT_EXACT;
+
+	err = zap_lookup_norm(os, snapnames_zapobj, name, 8, 1,
+	    value, mt, NULL, 0, NULL);
+	if (err == ENOTSUP && mt == MT_FIRST)
+		err = zap_lookup(os, snapnames_zapobj, name, 8, 1, value);
+	return (err);
+}
+
+static int
+dsl_dataset_snap_remove(objset_t *os, uint64_t flags,
+    uint64_t snapnames_zapobj, char *name, dmu_tx_t *tx)
+{
+	matchtype_t mt;
+	int err;
+
+	if (flags & DS_FLAG_CI_DATASET)
+		mt = MT_FIRST;
+	else
+		mt = MT_EXACT;
+
+	err = zap_remove_norm(os, snapnames_zapobj, name, mt, tx);
+	if (err == ENOTSUP && mt == MT_FIRST)
+		err = zap_remove(os, snapnames_zapobj, name, tx);
+	return (err);
+}
+
 int
 dsl_dataset_open_obj(dsl_pool_t *dp, uint64_t dsobj, const char *snapname,
     int mode, void *tag, dsl_dataset_t **dsp)
@@ -355,12 +392,14 @@
 				    ds->ds_dir->dd_phys->dd_head_dataset_obj,
 				    FTAG, &headdbuf);
 				if (err == 0) {
+					uint64_t foundobj;
+
 					headphys = headdbuf->db_data;
-					uint64_t foundobj;
-					err = zap_lookup(dp->dp_meta_objset,
+					err = dsl_dataset_snap_lookup(
+					    dp->dp_meta_objset,
+					    headphys->ds_flags,
 					    headphys->ds_snapnames_zapobj,
-					    snapname, sizeof (foundobj), 1,
-					    &foundobj);
+					    snapname, &foundobj);
 					ASSERT3U(foundobj, ==, dsobj);
 					dmu_buf_rele(headdbuf, FTAG);
 				}
@@ -469,11 +508,13 @@
 
 	if (tail != NULL) {
 		objset_t *mos = dp->dp_meta_objset;
+		uint64_t flags;
 
 		err = dsl_dataset_open_obj(dp, obj, NULL,
 		    DS_MODE_NONE, tag, &ds);
 		if (err)
 			goto out;
+		flags = ds->ds_phys->ds_flags;
 		obj = ds->ds_phys->ds_snapnames_zapobj;
 		dsl_dataset_close(ds, DS_MODE_NONE, tag);
 		ds = NULL;
@@ -490,7 +531,7 @@
 			goto out;
 		}
 		dprintf("looking for snapshot '%s'\n", tail);
-		err = zap_lookup(mos, obj, tail, 8, 1, &obj);
+		err = dsl_dataset_snap_lookup(mos, flags, obj, tail, &obj);
 		if (err)
 			goto out;
 	}
@@ -632,7 +673,8 @@
 	(void) random_get_pseudo_bytes((void*)&dsphys->ds_guid,
 	    sizeof (dsphys->ds_guid));
 	dsphys->ds_snapnames_zapobj =
-	    zap_create(mos, DMU_OT_DSL_DS_SNAP_MAP, DMU_OT_NONE, 0, tx);
+	    zap_create_norm(mos, U8_TEXTPREP_TOUPPER, DMU_OT_DSL_DS_SNAP_MAP,
+	    DMU_OT_NONE, 0, tx);
 	dsphys->ds_creation_time = gethrestime_sec();
 	dsphys->ds_creation_txg = tx->tx_txg;
 	dsphys->ds_deadlist_obj =
@@ -653,7 +695,8 @@
 }
 
 uint64_t
-dsl_dataset_create_sync_impl(dsl_dir_t *dd, dsl_dataset_t *origin, dmu_tx_t *tx)
+dsl_dataset_create_sync_impl(dsl_dir_t *dd, dsl_dataset_t *origin,
+    uint64_t flags, dmu_tx_t *tx)
 {
 	dsl_pool_t *dp = dd->dd_pool;
 	dmu_buf_t *dbuf;
@@ -672,17 +715,17 @@
 	dmu_buf_will_dirty(dbuf, tx);
 	dsphys = dbuf->db_data;
 	dsphys->ds_dir_obj = dd->dd_object;
+	dsphys->ds_flags = flags;
 	dsphys->ds_fsid_guid = unique_create();
 	(void) random_get_pseudo_bytes((void*)&dsphys->ds_guid,
 	    sizeof (dsphys->ds_guid));
 	dsphys->ds_snapnames_zapobj =
-	    zap_create(mos, DMU_OT_DSL_DS_SNAP_MAP, DMU_OT_NONE, 0, tx);
+	    zap_create_norm(mos, U8_TEXTPREP_TOUPPER, DMU_OT_DSL_DS_SNAP_MAP,
+	    DMU_OT_NONE, 0, tx);
 	dsphys->ds_creation_time = gethrestime_sec();
 	dsphys->ds_creation_txg = tx->tx_txg;
 	dsphys->ds_deadlist_obj =
 	    bplist_create(mos, DSL_DEADLIST_BLOCKSIZE, tx);
-	if (spa_version(dp->dp_spa) >= SPA_VERSION_UNIQUE_ACCURATE)
-		dsphys->ds_flags |= DS_FLAG_UNIQUE_ACCURATE;
 
 	if (origin) {
 		dsphys->ds_prev_snap_obj = origin->ds_object;
@@ -695,6 +738,7 @@
 		dsphys->ds_uncompressed_bytes =
 		    origin->ds_phys->ds_uncompressed_bytes;
 		dsphys->ds_bp = origin->ds_phys->ds_bp;
+		dsphys->ds_flags = origin->ds_phys->ds_flags;
 
 		dmu_buf_will_dirty(origin->ds_dbuf, tx);
 		origin->ds_phys->ds_num_children++;
@@ -702,6 +746,10 @@
 		dmu_buf_will_dirty(dd->dd_dbuf, tx);
 		dd->dd_phys->dd_origin_obj = origin->ds_object;
 	}
+
+	if (spa_version(dp->dp_spa) >= SPA_VERSION_UNIQUE_ACCURATE)
+		dsphys->ds_flags |= DS_FLAG_UNIQUE_ACCURATE;
+
 	dmu_buf_rele(dbuf, FTAG);
 
 	dmu_buf_will_dirty(dd->dd_dbuf, tx);
@@ -711,8 +759,8 @@
 }
 
 uint64_t
-dsl_dataset_create_sync(dsl_dir_t *pdd,
-    const char *lastname, dsl_dataset_t *origin, cred_t *cr, dmu_tx_t *tx)
+dsl_dataset_create_sync(dsl_dir_t *pdd, const char *lastname,
+    dsl_dataset_t *origin, uint64_t flags, cred_t *cr, dmu_tx_t *tx)
 {
 	dsl_pool_t *dp = pdd->dd_pool;
 	uint64_t dsobj, ddobj;
@@ -723,7 +771,7 @@
 	ddobj = dsl_dir_create_sync(pdd, lastname, tx);
 	VERIFY(0 == dsl_dir_open_obj(dp, ddobj, lastname, FTAG, &dd));
 
-	dsobj = dsl_dataset_create_sync_impl(dd, origin, tx);
+	dsobj = dsl_dataset_create_sync_impl(dd, origin, flags, tx);
 
 	dsl_deleg_set_create_perms(dd, tx, cr);
 
@@ -1518,14 +1566,18 @@
 #ifdef ZFS_DEBUG
 		{
 			uint64_t val;
-			err = zap_lookup(mos,
+
+			err = dsl_dataset_snap_lookup(mos,
+			    ds_head->ds_phys->ds_flags,
 			    ds_head->ds_phys->ds_snapnames_zapobj,
-			    ds->ds_snapname, 8, 1, &val);
+			    ds->ds_snapname, &val);
 			ASSERT3U(err, ==, 0);
 			ASSERT3U(val, ==, obj);
 		}
 #endif
-		err = zap_remove(mos, ds_head->ds_phys->ds_snapnames_zapobj,
+		err = dsl_dataset_snap_remove(mos,
+		    ds_head->ds_phys->ds_flags,
+		    ds_head->ds_phys->ds_snapnames_zapobj,
 		    ds->ds_snapname, tx);
 		ASSERT(err == 0);
 		dsl_dataset_close(ds_head, DS_MODE_NONE, FTAG);
@@ -1590,8 +1642,8 @@
 	/*
 	 * Check for conflicting name snapshot name.
 	 */
-	err = zap_lookup(mos, ds->ds_phys->ds_snapnames_zapobj,
-	    snapname, 8, 1, &value);
+	err = dsl_dataset_snap_lookup(mos, ds->ds_phys->ds_flags,
+	    ds->ds_phys->ds_snapnames_zapobj, snapname, &value);
 	if (err == 0)
 		return (EEXIST);
 	if (err != ENOENT)
@@ -1840,8 +1892,8 @@
 		return (err);
 
 	/* new name better not be in use */
-	err = zap_lookup(mos, hds->ds_phys->ds_snapnames_zapobj,
-	    newsnapname, 8, 1, &val);
+	err = dsl_dataset_snap_lookup(mos, hds->ds_phys->ds_flags,
+	    hds->ds_phys->ds_snapnames_zapobj, newsnapname, &val);
 	dsl_dataset_close(hds, DS_MODE_NONE, FTAG);
 
 	if (err == 0)
@@ -1873,8 +1925,8 @@
 	    dd->dd_phys->dd_head_dataset_obj, NULL, DS_MODE_NONE, FTAG, &hds));
 
 	VERIFY(0 == dsl_dataset_get_snapname(ds));
-	err = zap_remove(mos, hds->ds_phys->ds_snapnames_zapobj,
-	    ds->ds_snapname, tx);
+	err = dsl_dataset_snap_remove(mos, hds->ds_phys->ds_flags,
+	    hds->ds_phys->ds_snapnames_zapobj, ds->ds_snapname, tx);
 	ASSERT3U(err, ==, 0);
 	mutex_enter(&ds->ds_lock);
 	(void) strcpy(ds->ds_snapname, newsnapname);
@@ -2069,7 +2121,7 @@
 
 struct promotearg {
 	uint64_t used, comp, uncomp, unique;
-	uint64_t newnext_obj, snapnames_obj;
+	uint64_t ds_flags, newnext_obj, snapnames_obj;
 };
 
 /* ARGSUSED */
@@ -2110,6 +2162,7 @@
 		    odd->dd_phys->dd_head_dataset_obj,
 		    NULL, DS_MODE_NONE, FTAG, &phds))
 			goto out;
+		pa->ds_flags = phds->ds_phys->ds_flags;
 		pa->snapnames_obj = phds->ds_phys->ds_snapnames_zapobj;
 		dsl_dataset_close(phds, DS_MODE_NONE, FTAG);
 	}
@@ -2153,9 +2206,9 @@
 
 		/* Check that the snapshot name does not conflict */
 		dsl_dataset_name(ds, name);
-		err = zap_lookup(dd->dd_pool->dp_meta_objset,
-		    hds->ds_phys->ds_snapnames_zapobj, ds->ds_snapname,
-		    8, 1, &val);
+		err = dsl_dataset_snap_lookup(dd->dd_pool->dp_meta_objset,
+		    hds->ds_phys->ds_flags, hds->ds_phys->ds_snapnames_zapobj,
+		    ds->ds_snapname, &val);
 		if (err != ENOENT) {
 			if (err == 0)
 				err = EEXIST;
@@ -2245,8 +2298,8 @@
 
 		/* move snap name entry */
 		dsl_dataset_name(ds, name);
-		VERIFY(0 == zap_remove(dp->dp_meta_objset,
-		    pa->snapnames_obj, ds->ds_snapname, tx));
+		VERIFY(0 == dsl_dataset_snap_remove(dp->dp_meta_objset,
+		    pa->ds_flags, pa->snapnames_obj, ds->ds_snapname, tx));
 		VERIFY(0 == zap_add(dp->dp_meta_objset,
 		    hds->ds_phys->ds_snapnames_zapobj, ds->ds_snapname,
 		    8, 1, &ds->ds_object, tx));
--- a/usr/src/uts/common/fs/zfs/sys/dmu.h	Sat Apr 26 21:58:06 2008 -0700
+++ b/usr/src/uts/common/fs/zfs/sys/dmu.h	Sun Apr 27 10:20:38 2008 -0700
@@ -166,7 +166,7 @@
 void dmu_objset_close(objset_t *os);
 int dmu_objset_evict_dbufs(objset_t *os);
 int dmu_objset_create(const char *name, dmu_objset_type_t type,
-    objset_t *clone_parent,
+    objset_t *clone_parent, uint64_t flags,
     void (*func)(objset_t *os, void *arg, cred_t *cr, dmu_tx_t *tx), void *arg);
 int dmu_objset_destroy(const char *name);
 int dmu_snapshots_destroy(char *fsname, char *snapname);
@@ -540,6 +540,8 @@
 extern uint64_t dmu_objset_id(objset_t *os);
 extern int dmu_snapshot_list_next(objset_t *os, int namelen, char *name,
     uint64_t *id, uint64_t *offp, boolean_t *case_conflict);
+extern int dmu_snapshot_realname(objset_t *os, char *name, char *real,
+    int maxlen, boolean_t *conflict);
 extern int dmu_dir_list_next(objset_t *os, int namelen, char *name,
     uint64_t *idp, uint64_t *offp);
 extern void dmu_objset_set_user(objset_t *os, void *user_ptr);
--- a/usr/src/uts/common/fs/zfs/sys/dmu_objset.h	Sat Apr 26 21:58:06 2008 -0700
+++ b/usr/src/uts/common/fs/zfs/sys/dmu_objset.h	Sun Apr 27 10:20:38 2008 -0700
@@ -19,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -99,7 +99,7 @@
     objset_t **osp);
 void dmu_objset_close(objset_t *os);
 int dmu_objset_create(const char *name, dmu_objset_type_t type,
-    objset_t *clone_parent,
+    objset_t *clone_parent, uint64_t flags,
     void (*func)(objset_t *os, void *arg, cred_t *cr, dmu_tx_t *tx), void *arg);
 int dmu_objset_destroy(const char *name);
 int dmu_objset_rollback(objset_t *os);
--- a/usr/src/uts/common/fs/zfs/sys/dsl_dataset.h	Sat Apr 26 21:58:06 2008 -0700
+++ b/usr/src/uts/common/fs/zfs/sys/dsl_dataset.h	Sun Apr 27 10:20:38 2008 -0700
@@ -62,6 +62,12 @@
  */
 #define	DS_FLAG_UNIQUE_ACCURATE	(1ULL<<2)
 
+/*
+ * DS_FLAG_CI_DATASET is set if the dataset contains a file system whose
+ * name lookups should be performed case-insensitively.
+ */
+#define	DS_FLAG_CI_DATASET	(1ULL<<16)
+
 typedef struct dsl_dataset_phys {
 	uint64_t ds_dir_obj;
 	uint64_t ds_prev_snap_obj;
@@ -145,9 +151,10 @@
 void dsl_dataset_downgrade(dsl_dataset_t *ds, int oldmode, int newmode);
 boolean_t dsl_dataset_tryupgrade(dsl_dataset_t *ds, int oldmode, int newmode);
 uint64_t dsl_dataset_create_sync_impl(dsl_dir_t *dd, dsl_dataset_t *origin,
-    dmu_tx_t *tx);
+    uint64_t flags, dmu_tx_t *tx);
 uint64_t dsl_dataset_create_sync(dsl_dir_t *pds,
-    const char *lastname, dsl_dataset_t *origin, cred_t *, dmu_tx_t *);
+    const char *lastname, dsl_dataset_t *origin, uint64_t flags,
+    cred_t *, dmu_tx_t *);
 int dsl_dataset_destroy(dsl_dataset_t *ds, void *tag);
 int dsl_snapshots_destroy(char *fsname, char *snapname);
 dsl_checkfunc_t dsl_dataset_destroy_check;
@@ -199,6 +206,7 @@
 void dsl_dataset_set_quota_sync(void *arg1, void *arg2, cred_t *cr,
     dmu_tx_t *tx);
 int dsl_dataset_set_reservation(const char *dsname, uint64_t reservation);
+void dsl_dataset_set_flags(dsl_dataset_t *ds, uint64_t flags);
 
 #ifdef ZFS_DEBUG
 #define	dprintf_ds(ds, fmt, ...) do { \
--- a/usr/src/uts/common/fs/zfs/sys/zfs_ioctl.h	Sat Apr 26 21:58:06 2008 -0700
+++ b/usr/src/uts/common/fs/zfs/sys/zfs_ioctl.h	Sun Apr 27 10:20:38 2008 -0700
@@ -19,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -51,7 +51,8 @@
 #define	DMU_BACKUP_HEADER_VERSION (2ULL)
 #define	DMU_BACKUP_MAGIC 0x2F5bacbacULL
 
-#define	DRR_FLAG_CLONE (1<<0)
+#define	DRR_FLAG_CLONE		(1<<0)
+#define	DRR_FLAG_CI_DATA	(1<<1)
 
 /*
  * zfs ioctl command structure
--- a/usr/src/uts/common/fs/zfs/zap_micro.c	Sat Apr 26 21:58:06 2008 -0700
+++ b/usr/src/uts/common/fs/zfs/zap_micro.c	Sun Apr 27 10:20:38 2008 -0700
@@ -19,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -139,6 +139,7 @@
 	int i, max;
 	buf->mz_block_type = BSWAP_64(buf->mz_block_type);
 	buf->mz_salt = BSWAP_64(buf->mz_salt);
+	buf->mz_normflags = BSWAP_64(buf->mz_normflags);
 	max = (size / MZAP_ENT_LEN) - 1;
 	for (i = 0; i < max; i++) {
 		buf->mz_chunk[i].mze_value =
--- a/usr/src/uts/common/fs/zfs/zfs_ctldir.c	Sat Apr 26 21:58:06 2008 -0700
+++ b/usr/src/uts/common/fs/zfs/zfs_ctldir.c	Sun Apr 27 10:20:38 2008 -0700
@@ -76,6 +76,7 @@
 #include <sys/dmu.h>
 #include <sys/dsl_deleg.h>
 #include <sys/mount.h>
+#include <sys/sunddi.h>
 
 typedef struct zfsctl_node {
 	gfs_dir_t	zc_gfs_private;
@@ -399,7 +400,8 @@
 	if (strcmp(nm, "..") == 0) {
 		err = VFS_ROOT(dvp->v_vfsp, vpp);
 	} else {
-		err = gfs_dir_lookup(dvp, nm, vpp, cr);
+		err = gfs_vop_lookup(dvp, nm, vpp, pnp, flags, rdir,
+		    cr, ct, direntflags, realpnp);
 	}
 
 	ZFS_EXIT(zfsvfs);
@@ -527,20 +529,36 @@
 {
 	zfsctl_snapdir_t *sdp = sdvp->v_data;
 	zfs_snapentry_t search, *sep;
+	zfsvfs_t *zfsvfs;
 	avl_index_t where;
 	char from[MAXNAMELEN], to[MAXNAMELEN];
+	char real[MAXNAMELEN];
 	int err;
 
+	zfsvfs = sdvp->v_vfsp->vfs_data;
+	ZFS_ENTER(zfsvfs);
+
+	if ((flags & FIGNORECASE) || zfsvfs->z_case == ZFS_CASE_INSENSITIVE) {
+		err = dmu_snapshot_realname(zfsvfs->z_os, snm, real,
+		    MAXNAMELEN, NULL);
+		if (err == 0) {
+			snm = real;
+		} else if (err != ENOTSUP) {
+			ZFS_EXIT(zfsvfs);
+			return (err);
+		}
+	}
+
+	ZFS_EXIT(zfsvfs);
+
 	err = zfsctl_snapshot_zname(sdvp, snm, MAXNAMELEN, from);
+	if (!err)
+		err = zfsctl_snapshot_zname(tdvp, tnm, MAXNAMELEN, to);
+	if (!err)
+		err = zfs_secpolicy_rename_perms(from, to, cr);
 	if (err)
 		return (err);
 
-	err = zfsctl_snapshot_zname(tdvp, tnm, MAXNAMELEN, to);
-	if (err)
-		return (err);
-
-	if (err = zfs_secpolicy_rename_perms(from, to, cr))
-		return (err);
 	/*
 	 * Cannot move snapshots out of the snapdir.
 	 */
@@ -575,16 +593,34 @@
 	zfsctl_snapdir_t *sdp = dvp->v_data;
 	zfs_snapentry_t *sep;
 	zfs_snapentry_t search;
+	zfsvfs_t *zfsvfs;
 	char snapname[MAXNAMELEN];
+	char real[MAXNAMELEN];
 	int err;
 
+	zfsvfs = dvp->v_vfsp->vfs_data;
+	ZFS_ENTER(zfsvfs);
+
+	if ((flags & FIGNORECASE) || zfsvfs->z_case == ZFS_CASE_INSENSITIVE) {
+
+		err = dmu_snapshot_realname(zfsvfs->z_os, name, real,
+		    MAXNAMELEN, NULL);
+		if (err == 0) {
+			name = real;
+		} else if (err != ENOTSUP) {
+			ZFS_EXIT(zfsvfs);
+			return (err);
+		}
+	}
+
+	ZFS_EXIT(zfsvfs);
+
 	err = zfsctl_snapshot_zname(dvp, name, MAXNAMELEN, snapname);
+	if (!err)
+		err = zfs_secpolicy_destroy_perms(snapname, cr);
 	if (err)
 		return (err);
 
-	if (err = zfs_secpolicy_destroy_perms(snapname, cr))
-		return (err);
-
 	mutex_enter(&sdp->sd_lock);
 
 	search.se_name = name;
@@ -651,6 +687,7 @@
 	zfsctl_snapdir_t *sdp = dvp->v_data;
 	objset_t *snap;
 	char snapname[MAXNAMELEN];
+	char real[MAXNAMELEN];
 	char *mountpoint;
 	zfs_snapentry_t *sep, search;
 	struct mounta margs;
@@ -682,6 +719,24 @@
 
 	ZFS_ENTER(zfsvfs);
 
+	if (flags & FIGNORECASE) {
+		boolean_t conflict = B_FALSE;
+
+		err = dmu_snapshot_realname(zfsvfs->z_os, nm, real,
+		    MAXNAMELEN, &conflict);
+		if (err == 0) {
+			nm = real;
+		} else if (err != ENOTSUP) {
+			ZFS_EXIT(zfsvfs);
+			return (err);
+		}
+		if (realpnp)
+			(void) strlcpy(realpnp->pn_buf, nm,
+			    realpnp->pn_bufsize);
+		if (conflict && direntflags)
+			*direntflags = ED_CASE_CONFLICT;
+	}
+
 	mutex_enter(&sdp->sd_lock);
 	search.se_name = (char *)nm;
 	if ((sep = avl_find(&sdp->sd_snaps, &search, &where)) != NULL) {
@@ -938,7 +993,7 @@
 	zfs_snapentry_t *sep, *next;
 	vnode_t *dvp;
 
-	VERIFY(gfs_dir_lookup(vp, "..", &dvp, cr) == 0);
+	VERIFY(gfs_dir_lookup(vp, "..", &dvp, cr, 0, NULL, NULL) == 0);
 	sdp = dvp->v_data;
 
 	mutex_enter(&sdp->sd_lock);
--- a/usr/src/uts/common/fs/zfs/zfs_dir.c	Sat Apr 26 21:58:06 2008 -0700
+++ b/usr/src/uts/common/fs/zfs/zfs_dir.c	Sun Apr 27 10:20:38 2008 -0700
@@ -74,7 +74,7 @@
 		char *buf = NULL;
 
 		if (rpnp) {
-			buf = rpnp->pn_path;
+			buf = rpnp->pn_buf;
 			bufsz = rpnp->pn_bufsize;
 		}
 		if (exact)
@@ -85,7 +85,7 @@
 		 */
 		error = zap_lookup_norm(zfsvfs->z_os, dzp->z_id, name, 8, 1,
 		    zoid, mt, buf, bufsz, &conflict);
-		if (deflags)
+		if (!error && deflags)
 			*deflags = conflict ? ED_CASE_CONFLICT : 0;
 	} else {
 		error = zap_lookup(zfsvfs->z_os, dzp->z_id, name, 8, 1, zoid);
@@ -401,8 +401,8 @@
 		rpnp = NULL;
 	}
 
-	if ((flags & FIGNORECASE) && rpnp)
-		(void) strlcpy(rpnp->pn_path, name, rpnp->pn_bufsize);
+	if ((flags & FIGNORECASE) && rpnp && !error)
+		(void) strlcpy(rpnp->pn_buf, name, rpnp->pn_bufsize);
 
 	return (error);
 }
--- a/usr/src/uts/common/fs/zfs/zfs_ioctl.c	Sat Apr 26 21:58:06 2008 -0700
+++ b/usr/src/uts/common/fs/zfs/zfs_ioctl.c	Sun Apr 27 10:20:38 2008 -0700
@@ -1811,7 +1811,7 @@
  */
 static int
 zfs_fill_zplprops(const char *dataset, nvlist_t *createprops,
-    nvlist_t *zplprops, uint64_t zplver)
+    nvlist_t *zplprops, uint64_t zplver, boolean_t *is_ci)
 {
 	objset_t *os;
 	char parentname[MAXNAMELEN];
@@ -1891,6 +1891,9 @@
 	VERIFY(nvlist_add_uint64(zplprops,
 	    zfs_prop_to_name(ZFS_PROP_CASE), sense) == 0);
 
+	if (is_ci)
+		*is_ci = (sense == ZFS_CASE_INSENSITIVE);
+
 	dmu_objset_close(os);
 	return (0);
 }
@@ -1956,7 +1959,9 @@
 			nvlist_free(nvprops);
 			return (error);
 		}
-		error = dmu_objset_create(zc->zc_name, type, clone, NULL, NULL);
+
+		error = dmu_objset_create(zc->zc_name, type, clone, 0,
+		    NULL, NULL);
 		if (error) {
 			dmu_objset_close(clone);
 			nvlist_free(nvprops);
@@ -1964,6 +1969,8 @@
 		}
 		dmu_objset_close(clone);
 	} else {
+		boolean_t is_insensitive = B_FALSE;
+
 		if (cbfunc == NULL) {
 			nvlist_free(nvprops);
 			return (EINVAL);
@@ -2038,17 +2045,16 @@
 			VERIFY(nvlist_alloc(&zct.zct_zplprops,
 			    NV_UNIQUE_NAME, KM_SLEEP) == 0);
 			error = zfs_fill_zplprops(zc->zc_name, nvprops,
-			    zct.zct_zplprops, version);
+			    zct.zct_zplprops, version, &is_insensitive);
 			if (error != 0) {
 				nvlist_free(nvprops);
 				nvlist_free(zct.zct_zplprops);
 				return (error);
 			}
 		}
-		error = dmu_objset_create(zc->zc_name, type, NULL, cbfunc,
-		    &zct);
+		error = dmu_objset_create(zc->zc_name, type, NULL,
+		    is_insensitive ? DS_FLAG_CI_DATASET : 0, cbfunc, &zct);
 		nvlist_free(zct.zct_zplprops);
-
 	}
 
 	/*
--- a/usr/src/uts/common/fs/zfs/zfs_vnops.c	Sat Apr 26 21:58:06 2008 -0700
+++ b/usr/src/uts/common/fs/zfs/zfs_vnops.c	Sun Apr 27 10:20:38 2008 -0700
@@ -1421,7 +1421,7 @@
 	vnevent_remove(vp, dvp, name, ct);
 
 	if (realnmp)
-		dnlc_remove(dvp, realnmp->pn_path);
+		dnlc_remove(dvp, realnmp->pn_buf);
 	else
 		dnlc_remove(dvp, name);
 
--- a/usr/src/uts/common/fs/zfs/zfs_znode.c	Sat Apr 26 21:58:06 2008 -0700
+++ b/usr/src/uts/common/fs/zfs/zfs_znode.c	Sun Apr 27 10:20:38 2008 -0700
@@ -37,6 +37,7 @@
 #include <sys/mntent.h>
 #include <sys/mkdev.h>
 #include <sys/u8_textprep.h>
+#include <sys/dsl_dataset.h>
 #include <sys/vfs.h>
 #include <sys/vfs_opreg.h>
 #include <sys/vnode.h>
--- a/usr/src/uts/common/sys/gfs.h	Sat Apr 26 21:58:06 2008 -0700
+++ b/usr/src/uts/common/sys/gfs.h	Sun Apr 27 10:20:38 2008 -0700
@@ -20,7 +20,7 @@
  */
 
 /*
- * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -78,7 +78,7 @@
 typedef int (*gfs_readdir_cb)(vnode_t *, void *, int *, offset_t *,
     offset_t *, void *, int);
 typedef int (*gfs_lookup_cb)(vnode_t *, const char *, vnode_t **, ino64_t *,
-    cred_t *);
+    cred_t *, int, int *, pathname_t *);
 typedef ino64_t (*gfs_inode_cb)(vnode_t *, int);
 
 typedef struct gfs_dir {
@@ -105,12 +105,16 @@
 extern void *gfs_file_inactive(vnode_t *);
 extern void *gfs_dir_inactive(vnode_t *);
 
-extern int gfs_dir_lookup(vnode_t *, const char *, vnode_t **, cred_t *);
+extern int gfs_dir_case_lookup(vnode_t *, const char *, vnode_t **, cred_t *,
+    int, int *, pathname_t *);
+extern int gfs_dir_lookup(vnode_t *, const char *, vnode_t **, cred_t *,
+    int, int *, pathname_t *);
 extern int gfs_dir_readdir(vnode_t *, uio_t *, int *, void *, cred_t *,
     caller_context_t *, int flags);
 
 #define	gfs_dir_lock(gd)	mutex_enter(&(gd)->gfsd_lock)
 #define	gfs_dir_unlock(gd)	mutex_exit(&(gd)->gfsd_lock)
+#define	GFS_DIR_LOCKED(gd)	MUTEX_HELD(&(gd)->gfsd_lock)
 
 #define	gfs_file_parent(vp)	(((gfs_file_t *)(vp)->v_data)->gfs_parent)
 
--- a/usr/src/uts/common/sys/vnode.h	Sat Apr 26 21:58:06 2008 -0700
+++ b/usr/src/uts/common/sys/vnode.h	Sun Apr 27 10:20:38 2008 -0700
@@ -321,6 +321,8 @@
 
 #define	V_XATTRDIR	0x4000	/* attribute unnamed directory */
 
+#define	IS_XATTRDIR(vp)	((vp)->v_flag & V_XATTRDIR)
+
 #define	V_LOCALITY	0x8000	/* whether locality aware */
 
 /*