public inbox for linux-xfs@vger.kernel.org
 help / color / mirror / Atom feed
* [REVIEW 1/2] Case insensitive support for XFS - kernel patch
@ 2008-01-18  4:43 Barry Naujok
  2008-01-19  5:22 ` Eric Sandeen
  2008-01-19  5:30 ` Eric Sandeen
  0 siblings, 2 replies; 5+ messages in thread
From: Barry Naujok @ 2008-01-18  4:43 UTC (permalink / raw)
  To: xfs@oss.sgi.com; +Cc: xfs-dev

[-- Attachment #1: Type: text/plain, Size: 1319 bytes --]

This patch should apply to 2.6.24-rc6.

  Makefile               |    1
  linux-2.6/xfs_export.c |    2
  linux-2.6/xfs_iops.c   |  130 ++++++++++++
  linux-2.6/xfs_iops.h   |    1
  linux-2.6/xfs_ksyms.c  |    1
  linux-2.6/xfs_linux.h  |    8
  linux-2.6/xfs_super.c  |   31 ++
  xfs_attr.c             |   47 +++-
  xfs_attr_leaf.c        |  217 +++++++++++---------
  xfs_attr_leaf.h        |   12 +
  xfs_clnt.h             |    5
  xfs_da_btree.c         |   95 ++++++++
  xfs_da_btree.h         |   35 +++
  xfs_dir2.c             |  141 ++++++++++---
  xfs_dir2.h             |   10
  xfs_dir2_block.c       |   63 ++++-
  xfs_dir2_data.c        |    3
  xfs_dir2_leaf.c        |   48 +++-
  xfs_dir2_node.c        |   36 ++-
  xfs_dir2_sf.c          |   66 ++++--
  xfs_itable.c           |    2
  xfs_mount.c            |   26 ++
  xfs_mount.h            |   12 +
  xfs_rename.c           |    5
  xfs_sb.h               |   33 ++-
  xfs_types.h            |    5
  xfs_unicode.c          |  523  
+++++++++++++++++++++++++++++++++++++++++++++++++
  xfs_unicode.h          |   76 +++++++
  xfs_utils.c            |   10
  xfs_utils.h            |    2
  xfs_vfsops.c           |   40 +++
  xfs_vnodeops.c         |    7
  xfs_vnodeops.h         |    2
  33 files changed, 1495 insertions(+), 200 deletions(-)

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: ci_kernel.patch --]
[-- Type: text/x-patch; name=ci_kernel.patch, Size: 96466 bytes --]


===========================================================================
fs/xfs/Makefile
===========================================================================

--- a/fs/xfs/Makefile	2008-01-18 15:31:23.000000000 +1100
+++ b/fs/xfs/Makefile	2007-10-23 16:17:22.173903950 +1000
@@ -74,6 +74,7 @@ xfs-y				+= xfs_alloc.o \
 				   xfs_trans_extfree.o \
 				   xfs_trans_inode.o \
 				   xfs_trans_item.o \
+				   xfs_unicode.o \
 				   xfs_utils.o \
 				   xfs_vfsops.o \
 				   xfs_vnodeops.o \

===========================================================================
fs/xfs/linux-2.6/xfs_export.c
===========================================================================

--- a/fs/xfs/linux-2.6/xfs_export.c	2008-01-18 15:31:23.000000000 +1100
+++ b/fs/xfs/linux-2.6/xfs_export.c	2008-01-11 14:20:10.000000000 +1100
@@ -217,7 +217,7 @@ xfs_fs_get_parent(
 	struct dentry		*parent;
 
 	cvp = NULL;
-	error = xfs_lookup(XFS_I(child->d_inode), &dotdot, &cvp);
+	error = xfs_lookup(XFS_I(child->d_inode), &dotdot, &cvp, NULL, NULL);
 	if (unlikely(error))
 		return ERR_PTR(-error);
 

===========================================================================
fs/xfs/linux-2.6/xfs_iops.c
===========================================================================

--- a/fs/xfs/linux-2.6/xfs_iops.c	2008-01-18 15:31:23.000000000 +1100
+++ b/fs/xfs/linux-2.6/xfs_iops.c	2008-01-17 12:26:26.905427167 +1100
@@ -47,6 +47,8 @@
 #include "xfs_buf_item.h"
 #include "xfs_utils.h"
 #include "xfs_vnodeops.h"
+#include "xfs_da_btree.h"
+#include "xfs_unicode.h"
 
 #include <linux/capability.h>
 #include <linux/xattr.h>
@@ -388,7 +390,7 @@ xfs_vn_lookup(
 	if (dentry->d_name.len >= MAXNAMELEN)
 		return ERR_PTR(-ENAMETOOLONG);
 
-	error = xfs_lookup(XFS_I(dir), dentry, &cvp);
+	error = xfs_lookup(XFS_I(dir), dentry, &cvp, NULL, NULL);
 	if (unlikely(error)) {
 		if (unlikely(error != ENOENT))
 			return ERR_PTR(-error);
@@ -399,6 +401,113 @@ xfs_vn_lookup(
 	return d_splice_alias(vn_to_inode(cvp), dentry);
 }
 
+STATIC struct dentry *
+xfs_vn_ci_lookup(
+	struct inode	*dir,
+	struct dentry	*dentry,
+	struct nameidata *nd)
+{
+	bhv_vnode_t	*cvp;
+	int		error;
+	struct dentry	*result;
+	struct qstr	actual_name;
+	struct inode	*inode;
+
+	if (dentry->d_name.len >= MAXNAMELEN)
+		return ERR_PTR(-ENAMETOOLONG);
+
+	error = xfs_lookup(XFS_I(dir), dentry, &cvp, (char **)&actual_name.name,
+			&actual_name.len);
+	if (unlikely(error)) {
+		if (unlikely(error != ENOENT))
+			return ERR_PTR(-error);
+		d_add(dentry, NULL);
+		return NULL;
+	}
+	inode = vn_to_inode(cvp);
+
+	/* if exact match, just splice and exit */
+	if (!actual_name.name) {
+		result = d_splice_alias(inode, dentry);
+		return result;
+	}
+
+	/*
+	 * case-insensitive match, create a dentry to return and fill it
+	 * in with the correctly cased name. Parameter "dentry" is not
+	 * used anymore and the caller will free it.
+	 * Derived from fs/ntfs/namei.c
+	 */
+
+	actual_name.hash = full_name_hash(actual_name.name, actual_name.len);
+
+	/* Does an existing dentry match? */
+	result = d_lookup(dentry->d_parent, &actual_name);
+	if (!result) {
+		/* if not, create one */
+		result = d_alloc(dentry->d_parent, &actual_name);
+		xfs_free_unicode_nls_name((char *)actual_name.name);
+		if (!result)
+			return ERR_PTR(-ENOMEM);
+		dentry = d_splice_alias(inode, result);
+		if (dentry) {
+			dput(result);
+			return dentry;
+		}
+		return result;
+	}
+	xfs_free_unicode_nls_name((char *)actual_name.name);
+
+	/* an existing dentry matches, use it */
+
+	if (result->d_inode) {
+		/*
+		 * already an inode attached, deref the inode that was
+		 * refcounted with xfs_lookup and return the dentry.
+		 */
+		if (unlikely(result->d_inode != inode)) {
+			/* This can happen because bad inodes are unhashed. */
+			BUG_ON(!is_bad_inode(inode));
+			BUG_ON(!is_bad_inode(result->d_inode));
+		}
+		iput(inode);
+		return result;
+	}
+
+	if (!S_ISDIR(inode->i_mode)) {
+		/* not a directory, easy to handle */
+		d_instantiate(result, inode);
+		return result;
+	}
+
+	spin_lock(&dcache_lock);
+	if (list_empty(&inode->i_dentry)) {
+		/*
+		 * Directory without a 'disconnected' dentry; we need to do
+		 * d_instantiate() by hand because it takes dcache_lock which
+		 * we already hold.
+		 */
+		list_add(&result->d_alias, &inode->i_dentry);
+		result->d_inode = inode;
+		spin_unlock(&dcache_lock);
+		security_d_instantiate(result, inode);
+		return result;
+	}
+	/*
+	 * Directory with a 'disconnected' dentry; get a reference to the
+	 * 'disconnected' dentry.
+	 */
+	dentry = list_entry(inode->i_dentry.next, struct dentry, d_alias);
+	dget_locked(dentry);
+	spin_unlock(&dcache_lock);
+	security_d_instantiate(result, inode);
+	d_move(dentry, result);
+	iput(inode);
+	dput(result);
+	return dentry;
+}
+
+
 STATIC int
 xfs_vn_link(
 	struct dentry	*old_dentry,
@@ -868,6 +977,25 @@ const struct inode_operations xfs_dir_in
 	.removexattr		= xfs_vn_removexattr,
 };
 
+const struct inode_operations xfs_dir_ci_inode_operations = {
+	.create			= xfs_vn_create,
+	.lookup			= xfs_vn_ci_lookup,
+	.link			= xfs_vn_link,
+	.unlink			= xfs_vn_unlink,
+	.symlink		= xfs_vn_symlink,
+	.mkdir			= xfs_vn_mkdir,
+	.rmdir			= xfs_vn_rmdir,
+	.mknod			= xfs_vn_mknod,
+	.rename			= xfs_vn_rename,
+	.permission		= xfs_vn_permission,
+	.getattr		= xfs_vn_getattr,
+	.setattr		= xfs_vn_setattr,
+	.setxattr		= xfs_vn_setxattr,
+	.getxattr		= xfs_vn_getxattr,
+	.listxattr		= xfs_vn_listxattr,
+	.removexattr		= xfs_vn_removexattr,
+};
+
 const struct inode_operations xfs_symlink_inode_operations = {
 	.readlink		= generic_readlink,
 	.follow_link		= xfs_vn_follow_link,

===========================================================================
fs/xfs/linux-2.6/xfs_iops.h
===========================================================================

--- a/fs/xfs/linux-2.6/xfs_iops.h	2008-01-18 15:31:24.000000000 +1100
+++ b/fs/xfs/linux-2.6/xfs_iops.h	2007-10-26 13:19:11.702517171 +1000
@@ -20,6 +20,7 @@
 
 extern const struct inode_operations xfs_inode_operations;
 extern const struct inode_operations xfs_dir_inode_operations;
+extern const struct inode_operations xfs_dir_ci_inode_operations;
 extern const struct inode_operations xfs_symlink_inode_operations;
 
 extern const struct file_operations xfs_file_operations;

===========================================================================
fs/xfs/linux-2.6/xfs_ksyms.c
===========================================================================

--- a/fs/xfs/linux-2.6/xfs_ksyms.c	2008-01-18 15:31:24.000000000 +1100
+++ b/fs/xfs/linux-2.6/xfs_ksyms.c	2008-01-11 14:20:11.000000000 +1100
@@ -157,6 +157,7 @@ EXPORT_SYMBOL(kmem_zone_init);
 EXPORT_SYMBOL(kmem_zone_zalloc);
 EXPORT_SYMBOL(xfs_address_space_operations);
 EXPORT_SYMBOL(xfs_dir_inode_operations);
+EXPORT_SYMBOL(xfs_dir_ci_inode_operations);
 EXPORT_SYMBOL(xfs_dir_file_operations);
 EXPORT_SYMBOL(xfs_inode_operations);
 EXPORT_SYMBOL(xfs_file_operations);

===========================================================================
fs/xfs/linux-2.6/xfs_linux.h
===========================================================================

--- a/fs/xfs/linux-2.6/xfs_linux.h	2008-01-18 15:31:24.000000000 +1100
+++ b/fs/xfs/linux-2.6/xfs_linux.h	2008-01-11 14:49:16.537591564 +1100
@@ -75,6 +75,8 @@
 #include <linux/delay.h>
 #include <linux/log2.h>
 #include <linux/spinlock.h>
+#include <linux/ctype.h>
+#include <linux/nls.h>
 
 #include <asm/page.h>
 #include <asm/div64.h>
@@ -180,6 +182,12 @@
 #define howmany(x, y)	(((x)+((y)-1))/(y))
 
 /*
+ * NLS UTF-8 character set
+ */
+
+#define XFS_NLS_UTF8	"utf8"
+
+/*
  * Various platform dependent calls that don't fit anywhere else
  */
 #define xfs_sort(a,n,s,fn)	sort(a,n,s,fn,NULL)

===========================================================================
fs/xfs/linux-2.6/xfs_super.c
===========================================================================

--- a/fs/xfs/linux-2.6/xfs_super.c	2008-01-18 15:31:24.000000000 +1100
+++ b/fs/xfs/linux-2.6/xfs_super.c	2008-01-11 14:46:25.067566854 +1100
@@ -50,6 +50,7 @@
 #include "xfs_vnodeops.h"
 #include "xfs_vfsops.h"
 #include "xfs_version.h"
+#include "xfs_unicode.h"
 #include "xfs_log_priv.h"
 
 #include <linux/namei.h>
@@ -121,6 +122,9 @@ xfs_args_allocate(
 #define MNTOPT_ATTR2	"attr2"		/* do use attr2 attribute format */
 #define MNTOPT_NOATTR2	"noattr2"	/* do not use attr2 attribute format */
 #define MNTOPT_FILESTREAM  "filestreams" /* use filestreams allocator */
+#define MNTOPT_NLS	"nls"		/* NLS code page to use */
+#define MNTOPT_CILOOKUP	"ci"		/* case-insensitive dir names */
+#define MNTOPT_CIATTR	"ciattr"	/* case-insensitive attr names */
 #define MNTOPT_QUOTA	"quota"		/* disk quotas (user) */
 #define MNTOPT_NOQUOTA	"noquota"	/* no quotas */
 #define MNTOPT_USRQUOTA	"usrquota"	/* user quota enabled */
@@ -315,6 +319,18 @@ xfs_parseargs(
 			args->flags &= ~XFSMNT_ATTR2;
 		} else if (!strcmp(this_char, MNTOPT_FILESTREAM)) {
 			args->flags2 |= XFSMNT2_FILESTREAMS;
+		} else if (!strcmp(this_char, MNTOPT_NLS)) {
+			if (!value || !*value) {
+				cmn_err(CE_WARN,
+					"XFS: %s option requires an argument",
+					this_char);
+				return EINVAL;
+			}
+			strncpy(args->nls, value, MAXNAMELEN);
+		} else if (!strcmp(this_char, MNTOPT_CILOOKUP)) {
+			args->flags2 |= XFSMNT2_CILOOKUP;
+		} else if (!strcmp(this_char, MNTOPT_CIATTR)) {
+			args->flags2 |= XFSMNT2_CIATTR;
 		} else if (!strcmp(this_char, MNTOPT_NOQUOTA)) {
 			args->flags &= ~(XFSMNT_UQUOTAENF|XFSMNT_UQUOTA);
 			args->flags &= ~(XFSMNT_GQUOTAENF|XFSMNT_GQUOTA);
@@ -454,6 +470,8 @@ xfs_showargs(
 		{ XFS_MOUNT_OSYNCISOSYNC,	"," MNTOPT_OSYNCISOSYNC },
 		{ XFS_MOUNT_ATTR2,		"," MNTOPT_ATTR2 },
 		{ XFS_MOUNT_FILESTREAMS,	"," MNTOPT_FILESTREAM },
+		{ XFS_MOUNT_CI_LOOKUP,		"," MNTOPT_CILOOKUP },
+		{ XFS_MOUNT_CI_ATTR,		"," MNTOPT_CIATTR },
 		{ XFS_MOUNT_DMAPI,		"," MNTOPT_DMAPI },
 		{ XFS_MOUNT_GRPID,		"," MNTOPT_GRPID },
 		{ 0, NULL }
@@ -516,6 +534,13 @@ xfs_showargs(
 	if (!(mp->m_qflags & XFS_ALL_QUOTA_ACCT))
 		seq_puts(m, "," MNTOPT_NOQUOTA);
 
+	if (xfs_sb_version_hasunicode(&mp->m_sb)) {
+		if (mp->m_nls)
+			seq_printf(m, "," MNTOPT_NLS "=%s", mp->m_nls->charset);
+		else
+			seq_puts(m, "," MNTOPT_NLS "=" XFS_NLS_UTF8);
+	}
+
 	return 0;
 }
 __uint64_t
@@ -563,7 +588,11 @@ xfs_set_inodeops(
 		inode->i_mapping->a_ops = &xfs_address_space_operations;
 		break;
 	case S_IFDIR:
-		inode->i_op = &xfs_dir_inode_operations;
+		inode->i_op =
+			xfs_sb_version_hasoldci(&XFS_I(inode)->i_mount->m_sb) ||
+			(XFS_I(inode)->i_mount->m_flags & XFS_MOUNT_CI_LOOKUP) ?
+				&xfs_dir_ci_inode_operations :
+				&xfs_dir_inode_operations;
 		inode->i_fop = &xfs_dir_file_operations;
 		break;
 	case S_IFLNK:

===========================================================================
fs/xfs/xfs_attr.c
===========================================================================

--- a/fs/xfs/xfs_attr.c	2008-01-18 15:31:24.000000000 +1100
+++ b/fs/xfs/xfs_attr.c	2008-01-18 13:25:20.068339942 +1100
@@ -106,6 +106,17 @@ ktrace_t *xfs_attr_trace_buf;
  * Overall external interface routines.
  *========================================================================*/
 
+void
+xfs_attr_mount(struct xfs_mount *mp)
+{
+	mp->m_attr_magicpct = (mp->m_sb.sb_blocksize * 37) / 100;
+	if (xfs_sb_version_hasunicode(&mp->m_sb)) {
+		mp->m_attrnameops = (mp->m_flags & XFS_MOUNT_CI_ATTR) ?
+			&xfs_unicode_ci_nameops : &xfs_unicode_nameops;
+	} else
+		mp->m_attrnameops = &xfs_default_nameops;
+}
+
 int
 xfs_attr_fetch(xfs_inode_t *ip, const char *name, int namelen,
 	       char *value, int *valuelenp, int flags, struct cred *cred)
@@ -122,14 +133,14 @@ xfs_attr_fetch(xfs_inode_t *ip, const ch
 	 * Fill in the arg structure for this request.
 	 */
 	memset((char *)&args, 0, sizeof(args));
-	args.name = name;
-	args.namelen = namelen;
 	args.value = value;
 	args.valuelen = *valuelenp;
 	args.flags = flags;
-	args.hashval = xfs_da_hashname(args.name, args.namelen);
 	args.dp = ip;
 	args.whichfork = XFS_ATTR_FORK;
+	error = xfs_da_setup_name_and_hash(&args, name, namelen);
+	if (error)
+		return error;
 
 	/*
 	 * Decide on what work routines to call based on the inode size.
@@ -153,6 +164,7 @@ xfs_attr_fetch(xfs_inode_t *ip, const ch
 
 	if (error == EEXIST)
 		error = 0;
+	xfs_da_cleanup_name(&args, name);
 	return(error);
 }
 
@@ -181,6 +193,7 @@ xfs_attr_get(
 	xfs_ilock(ip, XFS_ILOCK_SHARED);
 	error = xfs_attr_fetch(ip, name, namelen, value, valuelenp, flags, cred);
 	xfs_iunlock(ip, XFS_ILOCK_SHARED);
+
 	return(error);
 }
 
@@ -219,18 +232,18 @@ xfs_attr_set_int(xfs_inode_t *dp, const 
 	 * Fill in the arg structure for this request.
 	 */
 	memset((char *)&args, 0, sizeof(args));
-	args.name = name;
-	args.namelen = namelen;
 	args.value = value;
 	args.valuelen = valuelen;
 	args.flags = flags;
-	args.hashval = xfs_da_hashname(args.name, args.namelen);
 	args.dp = dp;
 	args.firstblock = &firstblock;
 	args.flist = &flist;
 	args.whichfork = XFS_ATTR_FORK;
 	args.addname = 1;
 	args.oknoent = 1;
+	error = xfs_da_setup_name_and_hash(&args, name, namelen);
+	if (error)
+		return error;
 
 	/*
 	 * Determine space new attribute will use, and if it would be
@@ -282,6 +295,7 @@ xfs_attr_set_int(xfs_inode_t *dp, const 
 				      0, XFS_TRANS_PERM_LOG_RES,
 				      XFS_ATTRSET_LOG_COUNT))) {
 		xfs_trans_cancel(args.trans, 0);
+		xfs_da_cleanup_name(&args, name);
 		return(error);
 	}
 	xfs_ilock(dp, XFS_ILOCK_EXCL);
@@ -292,6 +306,7 @@ xfs_attr_set_int(xfs_inode_t *dp, const 
 	if (error) {
 		xfs_iunlock(dp, XFS_ILOCK_EXCL);
 		xfs_trans_cancel(args.trans, XFS_TRANS_RELEASE_LOG_RES);
+		xfs_da_cleanup_name(&args, name);
 		return (error);
 	}
 
@@ -342,6 +357,7 @@ xfs_attr_set_int(xfs_inode_t *dp, const 
 			if (!error && (flags & ATTR_KERNOTIME) == 0) {
 				xfs_ichgtime(dp, XFS_ICHGTIME_CHG);
 			}
+			xfs_da_cleanup_name(&args, name);
 			return(error == 0 ? err2 : error);
 		}
 
@@ -411,6 +427,7 @@ xfs_attr_set_int(xfs_inode_t *dp, const 
 		xfs_ichgtime(dp, XFS_ICHGTIME_CHG);
 	}
 
+	xfs_da_cleanup_name(&args, name);
 	return(error);
 
 out:
@@ -418,6 +435,7 @@ out:
 		xfs_trans_cancel(args.trans,
 			XFS_TRANS_RELEASE_LOG_RES|XFS_TRANS_ABORT);
 	xfs_iunlock(dp, XFS_ILOCK_EXCL);
+	xfs_da_cleanup_name(&args, name);
 	return(error);
 }
 
@@ -460,21 +478,23 @@ xfs_attr_remove_int(xfs_inode_t *dp, con
 	 * Fill in the arg structure for this request.
 	 */
 	memset((char *)&args, 0, sizeof(args));
-	args.name = name;
-	args.namelen = namelen;
 	args.flags = flags;
-	args.hashval = xfs_da_hashname(args.name, args.namelen);
 	args.dp = dp;
 	args.firstblock = &firstblock;
 	args.flist = &flist;
 	args.total = 0;
 	args.whichfork = XFS_ATTR_FORK;
+	error = xfs_da_setup_name_and_hash(&args, name, namelen);
+	if (error)
+		return error;
 
 	/*
 	 * Attach the dquots to the inode.
 	 */
-	if ((error = XFS_QM_DQATTACH(mp, dp, 0)))
+	if ((error = XFS_QM_DQATTACH(mp, dp, 0))) {
+		xfs_da_cleanup_name(&args, name);
 		return (error);
+	}
 
 	/*
 	 * Start our first transaction of the day.
@@ -502,6 +522,7 @@ xfs_attr_remove_int(xfs_inode_t *dp, con
 				      0, XFS_TRANS_PERM_LOG_RES,
 				      XFS_ATTRRM_LOG_COUNT))) {
 		xfs_trans_cancel(args.trans, 0);
+		xfs_da_cleanup_name(&args, name);
 		return(error);
 	}
 
@@ -559,6 +580,7 @@ xfs_attr_remove_int(xfs_inode_t *dp, con
 		xfs_ichgtime(dp, XFS_ICHGTIME_CHG);
 	}
 
+	xfs_da_cleanup_name(&args, name);
 	return(error);
 
 out:
@@ -566,6 +588,7 @@ out:
 		xfs_trans_cancel(args.trans,
 			XFS_TRANS_RELEASE_LOG_RES|XFS_TRANS_ABORT);
 	xfs_iunlock(dp, XFS_ILOCK_EXCL);
+	xfs_da_cleanup_name(&args, name);
 	return(error);
 }
 
@@ -634,7 +657,7 @@ xfs_attr_list_int(xfs_attr_list_context_
  */
 /*ARGSUSED*/
 STATIC int
-xfs_attr_put_listent(xfs_attr_list_context_t *context, attrnames_t *namesp,
+xfs_attr_user_list(xfs_attr_list_context_t *context, attrnames_t *namesp,
 		     char *name, int namelen,
 		     int valuelen, char *value)
 {
@@ -765,7 +788,7 @@ xfs_attr_list(
 		context.alist->al_count = 0;
 		context.alist->al_more = 0;
 		context.alist->al_offset[0] = context.bufsize;
-		context.put_listent = xfs_attr_put_listent;
+		context.put_listent = xfs_attr_user_list;
 	}
 
 	if (XFS_FORCED_SHUTDOWN(dp->i_mount))

===========================================================================
fs/xfs/xfs_attr_leaf.c
===========================================================================

--- a/fs/xfs/xfs_attr_leaf.c	2008-01-18 15:31:24.000000000 +1100
+++ b/fs/xfs/xfs_attr_leaf.c	2008-01-18 13:25:11.873394723 +1100
@@ -42,6 +42,7 @@
 #include "xfs_attr.h"
 #include "xfs_attr_leaf.h"
 #include "xfs_error.h"
+#include "xfs_unicode.h"
 
 /*
  * xfs_attr_leaf.c
@@ -90,6 +91,10 @@ STATIC void xfs_attr_leaf_moveents(xfs_a
 					 xfs_mount_t *mp);
 STATIC int xfs_attr_leaf_entsize(xfs_attr_leafblock_t *leaf, int index);
 
+STATIC int xfs_attr_put_listent(xfs_attr_list_context_t *context,
+					attrnames_t *namesp, char *name,
+					int namelen, int valuelen, char *value);
+
 /*========================================================================
  * Namespace helper routines
  *========================================================================*/
@@ -135,6 +140,38 @@ xfs_attr_namesp_match_overrides(int arg_
  * External routines when attribute fork size < XFS_LITINO(mp).
  *========================================================================*/
 
+STATIC xfs_attr_sf_entry_t *
+xfs_attr_shortform_find_ent(xfs_da_args_t *args)
+{
+	xfs_attr_shortform_t *sf;
+	xfs_attr_sf_entry_t *sfe;
+	int i;
+	xfs_attr_sf_entry_t *ci_sfe = NULL;
+
+	ASSERT(args->dp->i_afp->if_flags & XFS_IFINLINE);
+	sf = (xfs_attr_shortform_t *)args->dp->i_afp->if_u1.if_data;
+	sfe = &sf->list[0];
+
+	args->cmpresult = XFS_CMP_DIFFERENT;
+	for (i = 0; i < sf->hdr.count; sfe = XFS_ATTR_SF_NEXTENTRY(sfe), i++) {
+		if (!xfs_attr_namesp_match(args->flags, sfe->flags))
+			continue;
+		switch (xfs_attr_compname(args->dp, sfe->nameval, sfe->namelen,
+				args->name, args->namelen)) {
+		case XFS_CMP_EXACT:
+			args->cmpresult = XFS_CMP_EXACT;
+			return sfe;
+		case XFS_CMP_CASE:
+			if (!ci_sfe) {
+				args->cmpresult = XFS_CMP_CASE;
+				ci_sfe = sfe;
+			}
+		default:;
+		}
+	}
+	return ci_sfe;
+}
+
 /*
  * Query whether the requested number of additional bytes of extended
  * attribute space will be able to fit inline.
@@ -295,13 +332,10 @@ xfs_attr_shortform_add(xfs_da_args_t *ar
 	sfe = &sf->list[0];
 	for (i = 0; i < sf->hdr.count; sfe = XFS_ATTR_SF_NEXTENTRY(sfe), i++) {
 #ifdef DEBUG
-		if (sfe->namelen != args->namelen)
-			continue;
-		if (memcmp(args->name, sfe->nameval, args->namelen) != 0)
-			continue;
 		if (!xfs_attr_namesp_match(args->flags, sfe->flags))
 			continue;
-		ASSERT(0);
+		ASSERT(xfs_attr_compname(args->dp, args->name, args->namelen,
+			sfe->nameval, sfe->namelen) == XFS_CMP_DIFFERENT);
 #endif
 	}
 
@@ -331,29 +365,19 @@ xfs_attr_shortform_remove(xfs_da_args_t 
 {
 	xfs_attr_shortform_t *sf;
 	xfs_attr_sf_entry_t *sfe;
-	int base, size=0, end, totsize, i;
+	int base, size, end, totsize;
 	xfs_mount_t *mp;
 	xfs_inode_t *dp;
 
+	sfe = xfs_attr_shortform_find_ent(args);
+	if (!sfe)
+		return XFS_ERROR(ENOATTR);
+
 	dp = args->dp;
 	mp = dp->i_mount;
-	base = sizeof(xfs_attr_sf_hdr_t);
 	sf = (xfs_attr_shortform_t *)dp->i_afp->if_u1.if_data;
-	sfe = &sf->list[0];
-	end = sf->hdr.count;
-	for (i = 0; i < end; sfe = XFS_ATTR_SF_NEXTENTRY(sfe),
-					base += size, i++) {
 		size = XFS_ATTR_SF_ENTSIZE(sfe);
-		if (sfe->namelen != args->namelen)
-			continue;
-		if (memcmp(sfe->nameval, args->name, args->namelen) != 0)
-			continue;
-		if (!xfs_attr_namesp_match(args->flags, sfe->flags))
-			continue;
-		break;
-	}
-	if (i == end)
-		return(XFS_ERROR(ENOATTR));
+	base = (int)((char *)sfe - (char *)sf);
 
 	/*
 	 * Fix up the attribute fork data, covering the hole
@@ -412,26 +436,7 @@ xfs_attr_shortform_remove(xfs_da_args_t 
 int
 xfs_attr_shortform_lookup(xfs_da_args_t *args)
 {
-	xfs_attr_shortform_t *sf;
-	xfs_attr_sf_entry_t *sfe;
-	int i;
-	xfs_ifork_t *ifp;
-
-	ifp = args->dp->i_afp;
-	ASSERT(ifp->if_flags & XFS_IFINLINE);
-	sf = (xfs_attr_shortform_t *)ifp->if_u1.if_data;
-	sfe = &sf->list[0];
-	for (i = 0; i < sf->hdr.count;
-				sfe = XFS_ATTR_SF_NEXTENTRY(sfe), i++) {
-		if (sfe->namelen != args->namelen)
-			continue;
-		if (memcmp(args->name, sfe->nameval, args->namelen) != 0)
-			continue;
-		if (!xfs_attr_namesp_match(args->flags, sfe->flags))
-			continue;
-		return(XFS_ERROR(EEXIST));
-	}
-	return(XFS_ERROR(ENOATTR));
+	return XFS_ERROR(xfs_attr_shortform_find_ent(args) ? EEXIST : ENOATTR);
 }
 
 /*
@@ -441,35 +446,24 @@ xfs_attr_shortform_lookup(xfs_da_args_t 
 int
 xfs_attr_shortform_getvalue(xfs_da_args_t *args)
 {
-	xfs_attr_shortform_t *sf;
 	xfs_attr_sf_entry_t *sfe;
-	int i;
 
-	ASSERT(args->dp->i_d.di_aformat == XFS_IFINLINE);
-	sf = (xfs_attr_shortform_t *)args->dp->i_afp->if_u1.if_data;
-	sfe = &sf->list[0];
-	for (i = 0; i < sf->hdr.count;
-				sfe = XFS_ATTR_SF_NEXTENTRY(sfe), i++) {
-		if (sfe->namelen != args->namelen)
-			continue;
-		if (memcmp(args->name, sfe->nameval, args->namelen) != 0)
-			continue;
-		if (!xfs_attr_namesp_match(args->flags, sfe->flags))
-			continue;
+	sfe = xfs_attr_shortform_find_ent(args);
+	if (!sfe)
+		return XFS_ERROR(ENOATTR);
+
 		if (args->flags & ATTR_KERNOVAL) {
 			args->valuelen = sfe->valuelen;
-			return(XFS_ERROR(EEXIST));
+		return XFS_ERROR(EEXIST);
 		}
 		if (args->valuelen < sfe->valuelen) {
 			args->valuelen = sfe->valuelen;
-			return(XFS_ERROR(ERANGE));
+		return XFS_ERROR(ERANGE);
 		}
 		args->valuelen = sfe->valuelen;
-		memcpy(args->value, &sfe->nameval[args->namelen],
-						    args->valuelen);
-		return(XFS_ERROR(EEXIST));
-	}
-	return(XFS_ERROR(ENOATTR));
+	memcpy(args->value, &sfe->nameval[args->namelen], args->valuelen);
+
+	return XFS_ERROR(EEXIST);
 }
 
 /*
@@ -535,17 +529,18 @@ xfs_attr_shortform_to_leaf(xfs_da_args_t
 
 	sfe = &sf->list[0];
 	for (i = 0; i < sf->hdr.count; i++) {
-		nargs.name = (char *)sfe->nameval;
-		nargs.namelen = sfe->namelen;
-		nargs.value = (char *)&sfe->nameval[nargs.namelen];
+		nargs.value = (char *)&sfe->nameval[sfe->namelen];
 		nargs.valuelen = sfe->valuelen;
-		nargs.hashval = xfs_da_hashname((char *)sfe->nameval,
+		error = xfs_da_setup_name_and_hash(&nargs, sfe->nameval,
 						sfe->namelen);
+		if (error)
+			goto out;
 		nargs.flags = XFS_ATTR_NSP_ONDISK_TO_ARGS(sfe->flags);
 		error = xfs_attr_leaf_lookup_int(bp, &nargs); /* set a->index */
 		ASSERT(error == ENOATTR);
 		error = xfs_attr_leaf_add(bp, &nargs);
 		ASSERT(error != ENOSPC);
+		xfs_da_cleanup_name(&nargs, sfe->nameval);
 		if (error)
 			goto out;
 		sfe = XFS_ATTR_SF_NEXTENTRY(sfe);
@@ -631,7 +626,7 @@ xfs_attr_shortform_list(xfs_attr_list_co
 				continue;
 			}
 			namesp = xfs_attr_flags_namesp(sfe->flags);
-			error = context->put_listent(context,
+			error = xfs_attr_put_listent(context,
 					   namesp,
 					   (char *)sfe->nameval,
 					   (int)sfe->namelen,
@@ -734,7 +729,7 @@ xfs_attr_shortform_list(xfs_attr_list_co
 			cursor->hashval = sbp->hash;
 			cursor->offset = 0;
 		}
-		error = context->put_listent(context,
+		error = xfs_attr_put_listent(context,
 					namesp,
 					sbp->name,
 					sbp->namelen,
@@ -1960,6 +1955,7 @@ xfs_attr_leaf_lookup_int(xfs_dabuf_t *bp
 	xfs_attr_leaf_name_remote_t *name_rmt;
 	int probe, span;
 	xfs_dahash_t hashval;
+	xfs_dacmp_t cmp;
 
 	leaf = bp->data;
 	ASSERT(be16_to_cpu(leaf->hdr.info.magic) == XFS_ATTR_LEAF_MAGIC);
@@ -2008,6 +2004,7 @@ xfs_attr_leaf_lookup_int(xfs_dabuf_t *bp
 	/*
 	 * Duplicate keys may be present, so search all of them for a match.
 	 */
+	args->cmpresult = XFS_CMP_DIFFERENT;
 	for (  ; (probe < be16_to_cpu(leaf->hdr.count)) &&
 			(be32_to_cpu(entry->hashval) == hashval);
 			entry++, probe++) {
@@ -2019,35 +2016,40 @@ xfs_attr_leaf_lookup_int(xfs_dabuf_t *bp
 		 * If we are looking for complete entries, show only those.
 		 */
 		if ((args->flags & XFS_ATTR_INCOMPLETE) !=
-		    (entry->flags & XFS_ATTR_INCOMPLETE)) {
-			continue;
-		}
-		if (entry->flags & XFS_ATTR_LOCAL) {
-			name_loc = XFS_ATTR_LEAF_NAME_LOCAL(leaf, probe);
-			if (name_loc->namelen != args->namelen)
-				continue;
-			if (memcmp(args->name, (char *)name_loc->nameval, args->namelen) != 0)
+				(entry->flags & XFS_ATTR_INCOMPLETE))
 				continue;
 			if (!xfs_attr_namesp_match(args->flags, entry->flags))
 				continue;
+		if (entry->flags & XFS_ATTR_LOCAL) {
+			name_loc = XFS_ATTR_LEAF_NAME_LOCAL(leaf, probe);
+			cmp = xfs_attr_compname(args->dp, args->name, args->namelen,
+					name_loc->nameval, name_loc->namelen);
+			if (cmp != XFS_CMP_DIFFERENT && cmp != args->cmpresult) {
+				args->cmpresult = cmp;
 			args->index = probe;
-			return(XFS_ERROR(EEXIST));
+				args->rmtblkno = 0;
+				args->rmtblkcnt = 0;
+				if (cmp == XFS_CMP_EXACT)
+					return XFS_ERROR(EEXIST);
+			}
 		} else {
 			name_rmt = XFS_ATTR_LEAF_NAME_REMOTE(leaf, probe);
-			if (name_rmt->namelen != args->namelen)
-				continue;
-			if (memcmp(args->name, (char *)name_rmt->name,
-					     args->namelen) != 0)
-				continue;
-			if (!xfs_attr_namesp_match(args->flags, entry->flags))
-				continue;
+			cmp = xfs_attr_compname(args->dp, args->name, args->namelen,
+					name_rmt->name, name_rmt->namelen);
+			if (cmp != XFS_CMP_DIFFERENT && cmp != args->cmpresult) {
+				args->cmpresult = cmp;
 			args->index = probe;
 			args->rmtblkno = be32_to_cpu(name_rmt->valueblk);
 			args->rmtblkcnt = XFS_B_TO_FSB(args->dp->i_mount,
 						   be32_to_cpu(name_rmt->valuelen));
-			return(XFS_ERROR(EEXIST));
+				if (cmp == XFS_CMP_EXACT)
+					return XFS_ERROR(EEXIST);
+			}
 		}
 	}
+	if (args->cmpresult == XFS_CMP_CASE)
+		return XFS_ERROR(EEXIST);
+
 	args->index = probe;
 	return(XFS_ERROR(ENOATTR));
 }
@@ -2074,8 +2076,8 @@ xfs_attr_leaf_getvalue(xfs_dabuf_t *bp, 
 	entry = &leaf->entries[args->index];
 	if (entry->flags & XFS_ATTR_LOCAL) {
 		name_loc = XFS_ATTR_LEAF_NAME_LOCAL(leaf, args->index);
-		ASSERT(name_loc->namelen == args->namelen);
-		ASSERT(memcmp(args->name, name_loc->nameval, args->namelen) == 0);
+		ASSERT(xfs_attr_compname(args->dp, args->name, args->namelen,
+			name_loc->nameval, name_loc->namelen) != XFS_CMP_DIFFERENT);
 		valuelen = be16_to_cpu(name_loc->valuelen);
 		if (args->flags & ATTR_KERNOVAL) {
 			args->valuelen = valuelen;
@@ -2089,8 +2091,8 @@ xfs_attr_leaf_getvalue(xfs_dabuf_t *bp, 
 		memcpy(args->value, &name_loc->nameval[args->namelen], valuelen);
 	} else {
 		name_rmt = XFS_ATTR_LEAF_NAME_REMOTE(leaf, args->index);
-		ASSERT(name_rmt->namelen == args->namelen);
-		ASSERT(memcmp(args->name, name_rmt->name, args->namelen) == 0);
+		ASSERT(xfs_attr_compname(args->dp, args->name, args->namelen,
+			name_rmt->nameval, name_rmt->namelen) != XFS_CMP_DIFFERENT);
 		valuelen = be32_to_cpu(name_rmt->valuelen);
 		args->rmtblkno = be32_to_cpu(name_rmt->valueblk);
 		args->rmtblkcnt = XFS_B_TO_FSB(args->dp->i_mount, valuelen);
@@ -2418,7 +2420,7 @@ xfs_attr_leaf_list_int(xfs_dabuf_t *bp, 
 			xfs_attr_leaf_name_local_t *name_loc =
 				XFS_ATTR_LEAF_NAME_LOCAL(leaf, i);
 
-			retval = context->put_listent(context,
+			retval = xfs_attr_put_listent(context,
 						namesp,
 						(char *)name_loc->nameval,
 						(int)name_loc->namelen,
@@ -2445,7 +2447,7 @@ xfs_attr_leaf_list_int(xfs_dabuf_t *bp, 
 				retval = xfs_attr_rmtval_get(&args);
 				if (retval)
 					return retval;
-				retval = context->put_listent(context,
+				retval = xfs_attr_put_listent(context,
 						namesp,
 						(char *)name_rmt->name,
 						(int)name_rmt->namelen,
@@ -2454,7 +2456,7 @@ xfs_attr_leaf_list_int(xfs_dabuf_t *bp, 
 				kmem_free(args.value, valuelen);
 			}
 			else {
-				retval = context->put_listent(context,
+				retval = xfs_attr_put_listent(context,
 						namesp,
 						(char *)name_rmt->name,
 						(int)name_rmt->namelen,
@@ -2472,6 +2474,31 @@ xfs_attr_leaf_list_int(xfs_dabuf_t *bp, 
 	return(retval);
 }
 
+/*
+ * Do NLS name conversion if required for attribute name and call
+ * context's put_listent routine
+ */
+
+STATIC int
+xfs_attr_put_listent(xfs_attr_list_context_t *context, attrnames_t *namesp,
+	char *name, int namelen, int valuelen, char *value)
+{
+	xfs_mount_t *mp = context->dp->i_mount;
+	char *nls_name = NULL;
+	int rval;
+
+	if (!mp->m_nls)
+		return context->put_listent(context, namesp, name, namelen,
+				valuelen, value);
+
+	rval = xfs_unicode_to_nls(mp->m_nls, name, namelen, &nls_name);
+	if (rval < 0)
+		return -rval;
+	rval = context->put_listent(context, namesp, nls_name, rval,
+			valuelen, value);
+	xfs_free_unicode_nls_name(nls_name);
+	return rval;
+}
 
 /*========================================================================
  * Manage the INCOMPLETE flag in a leaf entry
@@ -2522,8 +2549,8 @@ xfs_attr_leaf_clearflag(xfs_da_args_t *a
 		name = (char *)name_rmt->name;
 	}
 	ASSERT(be32_to_cpu(entry->hashval) == args->hashval);
-	ASSERT(namelen == args->namelen);
-	ASSERT(memcmp(name, args->name, namelen) == 0);
+	ASSERT(xfs_attr_compname(args->dp, args->name, args->namelen,
+			name, namelen) != XFS_CMP_DIFFERENT);
 #endif /* DEBUG */
 
 	entry->flags &= ~XFS_ATTR_INCOMPLETE;
@@ -2674,8 +2701,8 @@ xfs_attr_leaf_flipflags(xfs_da_args_t *a
 		name2 = (char *)name_rmt->name;
 	}
 	ASSERT(be32_to_cpu(entry1->hashval) == be32_to_cpu(entry2->hashval));
-	ASSERT(namelen1 == namelen2);
-	ASSERT(memcmp(name1, name2, namelen1) == 0);
+	ASSERT(xfs_attr_compname(args->dp, name1, namelen1, name2, namelen2) !=
+			XFS_CMP_DIFFERENT);
 #endif /* DEBUG */
 
 	ASSERT(entry1->flags & XFS_ATTR_INCOMPLETE);

===========================================================================
fs/xfs/xfs_attr_leaf.h
===========================================================================

--- a/fs/xfs/xfs_attr_leaf.h	2008-01-18 15:31:24.000000000 +1100
+++ b/fs/xfs/xfs_attr_leaf.h	2008-01-11 14:16:44.268796245 +1100
@@ -36,6 +36,7 @@ struct xfs_da_args;
 struct xfs_da_state;
 struct xfs_da_state_blk;
 struct xfs_inode;
+struct xfs_mount;
 struct xfs_trans;
 
 /*========================================================================
@@ -204,6 +205,16 @@ static inline int xfs_attr_leaf_entsize_
 	return (((bsize) >> 1) + ((bsize) >> 2));
 }
 
+/*
+ * Do hash and name compare based on nameops
+ */
+#define xfs_attr_hashname(dp, n, l) \
+		((dp)->i_mount->m_attrnameops->hashname((dp), (n), (l)))
+
+#define xfs_attr_compname(dp, n1, l1, n2, l2) \
+		((dp)->i_mount->m_attrnameops->compname((dp), (n1), (l1), \
+							(n2), (l2)))
+
 
 /*========================================================================
  * Structure used to pass context around among the routines.
@@ -296,6 +307,7 @@ int	xfs_attr_root_inactive(struct xfs_tr
 /*
  * Utility routines.
  */
+void	xfs_attr_mount(struct xfs_mount *mp);
 xfs_dahash_t	xfs_attr_leaf_lasthash(struct xfs_dabuf *bp, int *count);
 int	xfs_attr_leaf_order(struct xfs_dabuf *leaf1_bp,
 				   struct xfs_dabuf *leaf2_bp);

===========================================================================
fs/xfs/xfs_clnt.h
===========================================================================

--- a/fs/xfs/xfs_clnt.h	2008-01-18 15:31:24.000000000 +1100
+++ b/fs/xfs/xfs_clnt.h	2007-11-01 13:16:55.000383139 +1100
@@ -48,6 +48,7 @@ struct xfs_mount_args {
 	char	rtname[MAXNAMELEN+1];	/* realtime device filename */
 	char	logname[MAXNAMELEN+1];	/* journal device filename */
 	char	mtpt[MAXNAMELEN+1];	/* filesystem mount point */
+	char	nls[MAXNAMELEN+1];	/* NLS code page to use */
 	int	sunit;		/* stripe unit (BBs) */
 	int	swidth;		/* stripe width (BBs), multiple of sunit */
 	uchar_t iosizelog;	/* log2 of the preferred I/O size */
@@ -100,5 +101,9 @@ struct xfs_mount_args {
 						 * I/O size in stat(2) */
 #define XFSMNT2_FILESTREAMS	0x00000002	/* enable the filestreams
 						 * allocator */
+#define XFSMNT2_CILOOKUP	0x00000004	/* enable case-insensitive
+						 * filename lookup */
+#define XFSMNT2_CIATTR		0x00000008	/* enable case-insensitive
+						 * extended attr names */
 
 #endif	/* __XFS_CLNT_H__ */

===========================================================================
fs/xfs/xfs_da_btree.c
===========================================================================

--- a/fs/xfs/xfs_da_btree.c	2008-01-18 15:31:24.000000000 +1100
+++ b/fs/xfs/xfs_da_btree.c	2007-10-31 16:04:16.463309546 +1100
@@ -46,6 +46,7 @@
 #include "xfs_dir2_block.h"
 #include "xfs_dir2_node.h"
 #include "xfs_error.h"
+#include "xfs_unicode.h"
 
 /*
  * xfs_da_btree.c
@@ -1530,6 +1531,100 @@ xfs_da_hashname(const uchar_t *name, int
 	}
 }
 
+
+static xfs_dahash_t
+xfs_default_hashname(xfs_inode_t *inode, const uchar_t *name, int namelen)
+{
+	return xfs_da_hashname(name, namelen);
+}
+
+xfs_dacmp_t
+xfs_default_compname(xfs_inode_t *inode, const uchar_t *name1, int len1,
+	const uchar_t *name2, int len2)
+{
+	return (len1 == len2 && memcmp(name1, name2, len1) == 0) ?
+			XFS_CMP_EXACT : XFS_CMP_DIFFERENT;
+}
+
+static xfs_dahash_t
+xfs_unicode_ci_hashname(
+	xfs_inode_t	*inode,
+	const uchar_t	*name,
+	int		namelen)
+{
+	return xfs_unicode_hash(inode->i_mount->m_cft, name, namelen);
+}
+
+static xfs_dacmp_t
+xfs_unicode_ci_compname(
+	xfs_inode_t	*inode,
+	const uchar_t	*name1,
+	int		len1,
+	const uchar_t	*name2,
+	int 		len2)
+{
+	if (len1 == len2 && memcmp(name1, name2, len1) == 0)
+		return XFS_CMP_EXACT;
+
+	return xfs_unicode_casecmp(inode->i_mount->m_cft, name1, len1,
+			name2, len2) == 0 ? XFS_CMP_CASE : XFS_CMP_DIFFERENT;
+}
+
+const struct xfs_nameops xfs_default_nameops = {
+	.hashname	= xfs_default_hashname,
+	.compname	= xfs_default_compname
+};
+
+const struct xfs_nameops xfs_unicode_nameops = {
+	.hashname	= xfs_unicode_ci_hashname,
+	.compname	= xfs_default_compname,
+};
+
+const struct xfs_nameops xfs_unicode_ci_nameops = {
+	.hashname	= xfs_unicode_ci_hashname,
+	.compname	= xfs_unicode_ci_compname,
+};
+
+int
+xfs_da_setup_name_and_hash(
+	xfs_da_args_t		*args,
+	const char 		*name,
+	int			namelen)
+{
+	xfs_mount_t		*mp = args->dp->i_mount;
+
+	if (mp->m_nls) {
+		args->name = NULL;
+		args->namelen = xfs_nls_to_unicode(mp->m_nls, name, namelen,
+				(char **)&args->name);
+		if (args->namelen < 0)
+			return -args->namelen;
+	} else {
+		if (xfs_sb_version_hasunicode(&mp->m_sb)) {
+			int	rval;
+			rval = xfs_unicode_validate(name, namelen);
+			if (rval < 0)
+				return -rval;
+		}
+		args->name = name;
+		args->namelen = namelen;
+	}
+	args->hashval = (args->whichfork == XFS_ATTR_FORK) ?
+		xfs_attr_hashname(args->dp, args->name, args->namelen) :
+		xfs_dir_hashname(args->dp, args->name, args->namelen);
+	return 0;
+}
+
+void
+xfs_da_cleanup_name(
+	xfs_da_args_t		*args,
+	const char		*name)
+{
+	if ((char *)args->name != name)
+		xfs_free_unicode_nls_name((char *)args->name);
+}
+
+
 /*
  * Add a block to the btree ahead of the file.
  * Return the new block number to the caller.

===========================================================================
fs/xfs/xfs_da_btree.h
===========================================================================

--- a/fs/xfs/xfs_da_btree.h	2008-01-18 15:31:24.000000000 +1100
+++ b/fs/xfs/xfs_da_btree.h	2008-01-14 12:12:32.917055949 +1100
@@ -99,6 +99,15 @@ typedef struct xfs_da_node_entry xfs_da_
  *========================================================================*/
 
 /*
+ * Search comparison results
+ */
+typedef enum {
+	XFS_CMP_DIFFERENT,	/* names are completely different */
+	XFS_CMP_EXACT,		/* names are exactly the same */
+	XFS_CMP_CASE		/* names are same but differ in case */
+} xfs_dacmp_t;
+
+/*
  * Structure to ease passing around component names.
  */
 typedef struct xfs_da_args {
@@ -127,6 +136,7 @@ typedef struct xfs_da_args {
 	unsigned char	rename;		/* T/F: this is an atomic rename op */
 	unsigned char	addname;	/* T/F: this is an add operation */
 	unsigned char	oknoent;	/* T/F: ok to return ENOENT, else die */
+	xfs_dacmp_t	cmpresult;	/* name compare result for lookups */
 } xfs_da_args_t;
 
 /*
@@ -201,12 +211,37 @@ typedef struct xfs_da_state {
 		(uint)(XFS_DA_LOGOFF(BASE, ADDR)), \
 		(uint)(XFS_DA_LOGOFF(BASE, ADDR)+(SIZE)-1)
 
+/*
+ * Name ops for directory and/or attr name operations
+ */
+
+typedef xfs_dahash_t	(*xfs_hashname_t)(struct xfs_inode *, const uchar_t *,
+					int);
+typedef xfs_dacmp_t	(*xfs_compname_t)(struct xfs_inode *, const uchar_t *,
+					int, const uchar_t *, int);
+
+typedef struct xfs_nameops {
+	xfs_hashname_t		hashname;
+	xfs_compname_t		compname;
+} xfs_nameops_t;
+
 
 #ifdef __KERNEL__
 /*========================================================================
  * Function prototypes for the kernel.
  *========================================================================*/
 
+extern const struct xfs_nameops xfs_default_nameops;
+extern const struct xfs_nameops xfs_unicode_nameops;
+extern const struct xfs_nameops xfs_unicode_ci_nameops;
+
+xfs_dacmp_t xfs_default_compname(struct xfs_inode *inode, const uchar_t *name1,
+				int len1, const uchar_t *name2, int len2);
+
+int	xfs_da_setup_name_and_hash(xfs_da_args_t *args, const char *name,
+				int namelen);
+void	xfs_da_cleanup_name(xfs_da_args_t *args, const char *name);
+
 /*
  * Routines used for growing the Btree.
  */

===========================================================================
fs/xfs/xfs_dir2.c
===========================================================================

--- a/fs/xfs/xfs_dir2.c	2008-01-18 15:31:24.000000000 +1100
+++ b/fs/xfs/xfs_dir2.c	2008-01-11 14:24:51.701973714 +1100
@@ -42,8 +42,56 @@
 #include "xfs_dir2_node.h"
 #include "xfs_dir2_trace.h"
 #include "xfs_error.h"
+#include "xfs_unicode.h"
 #include "xfs_vnodeops.h"
 
+/*
+ * V1 case-insensitive support for directories
+ */
+static xfs_dahash_t
+xfs_ascii_ci_hashname(
+	xfs_inode_t	*inode,
+	const uchar_t	*name,
+	int		namelen)
+{
+	xfs_dahash_t	hash;
+	int		i;
+
+	for (i = 0, hash = 0; i < namelen; i++)
+		hash = tolower(name[i]) ^ rol32(hash, 7);
+
+	return hash;
+}
+
+static xfs_dacmp_t
+xfs_ascii_ci_compname(
+	xfs_inode_t	*inode,
+	const uchar_t	*name1,
+	int		len1,
+	const uchar_t	*name2,
+	int 		len2)
+{
+	xfs_dacmp_t	result = XFS_CMP_EXACT;
+	int		i;
+
+	if (len1 != len2)
+		return XFS_CMP_DIFFERENT;
+
+	for (i = 0; i < len1; i++) {
+		if (name1[i] == name2[i])
+			continue;
+		if (tolower(name1[i]) != tolower(name2[i]))
+			return XFS_CMP_DIFFERENT;
+		result = XFS_CMP_CASE;
+	}
+
+	return result;
+}
+
+static const struct xfs_nameops xfs_ascii_ci_nameops = {
+	.hashname	= xfs_ascii_ci_hashname,
+	.compname	= xfs_ascii_ci_compname,
+};
 
 void
 xfs_dir_mount(
@@ -64,6 +112,13 @@ xfs_dir_mount(
 		(mp->m_dirblksize - (uint)sizeof(xfs_da_node_hdr_t)) /
 		(uint)sizeof(xfs_da_node_entry_t);
 	mp->m_dir_magicpct = (mp->m_dirblksize * 37) / 100;
+
+	if (xfs_sb_version_hasunicode(&mp->m_sb)) {
+		mp->m_dirnameops = (mp->m_flags & XFS_MOUNT_CI_LOOKUP) ?
+			&xfs_unicode_ci_nameops : &xfs_unicode_nameops;
+	} else
+		mp->m_dirnameops = (xfs_sb_version_hasoldci(&mp->m_sb)) ?
+			&xfs_ascii_ci_nameops : &xfs_default_nameops;
 }
 
 /*
@@ -140,7 +195,7 @@ xfs_dir_init(
 }
 
 /*
-  Enter a name in a directory.
+ * Enter a name in a directory.
  */
 int
 xfs_dir_createname(
@@ -162,9 +217,6 @@ xfs_dir_createname(
 		return rval;
 	XFS_STATS_INC(xs_dir_create);
 
-	args.name = name;
-	args.namelen = namelen;
-	args.hashval = xfs_da_hashname(name, namelen);
 	args.inumber = inum;
 	args.dp = dp;
 	args.firstblock = first;
@@ -174,32 +226,41 @@ xfs_dir_createname(
 	args.trans = tp;
 	args.justcheck = 0;
 	args.addname = args.oknoent = 1;
+	rval = xfs_da_setup_name_and_hash(&args, name, namelen);
+	if (rval)
+		return rval;
 
 	if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL)
 		rval = xfs_dir2_sf_addname(&args);
 	else if ((rval = xfs_dir2_isblock(tp, dp, &v)))
-		return rval;
+		goto out;
 	else if (v)
 		rval = xfs_dir2_block_addname(&args);
 	else if ((rval = xfs_dir2_isleaf(tp, dp, &v)))
-		return rval;
+		goto out;
 	else if (v)
 		rval = xfs_dir2_leaf_addname(&args);
 	else
 		rval = xfs_dir2_node_addname(&args);
+out:
+	xfs_da_cleanup_name(&args, name);
 	return rval;
 }
 
 /*
- * Lookup a name in a directory, give back the inode number.
+ * Lookup a name in a directory, give back the inode number and also
+ * actual name if case-insensitive match.
  */
+
 int
 xfs_dir_lookup(
 	xfs_trans_t	*tp,
 	xfs_inode_t	*dp,
 	char		*name,
 	int		namelen,
-	xfs_ino_t	*inum)		/* out: inode number */
+	xfs_ino_t	*inum,		/* out: inode number */
+	char		**actual_name,	/* out: actual name if different */
+	int		*actual_namelen)
 {
 	xfs_da_args_t	args;
 	int		rval;
@@ -208,9 +269,9 @@ xfs_dir_lookup(
 	ASSERT((dp->i_d.di_mode & S_IFMT) == S_IFDIR);
 	XFS_STATS_INC(xs_dir_lookup);
 
-	args.name = name;
-	args.namelen = namelen;
-	args.hashval = xfs_da_hashname(name, namelen);
+	if (actual_name)
+		*actual_name = NULL;
+
 	args.inumber = 0;
 	args.dp = dp;
 	args.firstblock = NULL;
@@ -220,23 +281,39 @@ xfs_dir_lookup(
 	args.trans = tp;
 	args.justcheck = args.addname = 0;
 	args.oknoent = 1;
+	args.value = NULL;	/* value may contain actual name on return */
+	args.valuelen = 0;
+	rval = xfs_da_setup_name_and_hash(&args, name, namelen);
+	if (rval)
+		return rval;
 
 	if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL)
 		rval = xfs_dir2_sf_lookup(&args);
 	else if ((rval = xfs_dir2_isblock(tp, dp, &v)))
-		return rval;
+		goto out;
 	else if (v)
 		rval = xfs_dir2_block_lookup(&args);
 	else if ((rval = xfs_dir2_isleaf(tp, dp, &v)))
-		return rval;
+		goto out;
 	else if (v)
 		rval = xfs_dir2_leaf_lookup(&args);
 	else
 		rval = xfs_dir2_node_lookup(&args);
 	if (rval == EEXIST)
 		rval = 0;
-	if (rval == 0)
+	if (rval == 0) {
 		*inum = args.inumber;
+		if (args.value) {
+			ASSERT(args->cmpresult == XFS_CMP_CASE);
+			if (actual_name) {
+				*actual_namelen = args.valuelen,
+				*actual_name = args.value;
+			} else
+				xfs_free_unicode_nls_name(args.value);
+		}
+	}
+out:
+	xfs_da_cleanup_name(&args, name);
 	return rval;
 }
 
@@ -261,9 +338,6 @@ xfs_dir_removename(
 	ASSERT((dp->i_d.di_mode & S_IFMT) == S_IFDIR);
 	XFS_STATS_INC(xs_dir_remove);
 
-	args.name = name;
-	args.namelen = namelen;
-	args.hashval = xfs_da_hashname(name, namelen);
 	args.inumber = ino;
 	args.dp = dp;
 	args.firstblock = first;
@@ -272,19 +346,24 @@ xfs_dir_removename(
 	args.whichfork = XFS_DATA_FORK;
 	args.trans = tp;
 	args.justcheck = args.addname = args.oknoent = 0;
+	rval = xfs_da_setup_name_and_hash(&args, name, namelen);
+	if (rval)
+		return rval;
 
 	if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL)
 		rval = xfs_dir2_sf_removename(&args);
 	else if ((rval = xfs_dir2_isblock(tp, dp, &v)))
-		return rval;
+		goto out;
 	else if (v)
 		rval = xfs_dir2_block_removename(&args);
 	else if ((rval = xfs_dir2_isleaf(tp, dp, &v)))
-		return rval;
+		goto out;
 	else if (v)
 		rval = xfs_dir2_leaf_removename(&args);
 	else
 		rval = xfs_dir2_node_removename(&args);
+out:
+	xfs_da_cleanup_name(&args, name);
 	return rval;
 }
 
@@ -345,9 +424,6 @@ xfs_dir_replace(
 	if ((rval = xfs_dir_ino_validate(tp->t_mountp, inum)))
 		return rval;
 
-	args.name = name;
-	args.namelen = namelen;
-	args.hashval = xfs_da_hashname(name, namelen);
 	args.inumber = inum;
 	args.dp = dp;
 	args.firstblock = first;
@@ -356,19 +432,24 @@ xfs_dir_replace(
 	args.whichfork = XFS_DATA_FORK;
 	args.trans = tp;
 	args.justcheck = args.addname = args.oknoent = 0;
+	rval = xfs_da_setup_name_and_hash(&args, name, namelen);
+	if (rval)
+		return rval;
 
 	if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL)
 		rval = xfs_dir2_sf_replace(&args);
 	else if ((rval = xfs_dir2_isblock(tp, dp, &v)))
-		return rval;
+		goto out;
 	else if (v)
 		rval = xfs_dir2_block_replace(&args);
 	else if ((rval = xfs_dir2_isleaf(tp, dp, &v)))
-		return rval;
+		goto out;
 	else if (v)
 		rval = xfs_dir2_leaf_replace(&args);
 	else
 		rval = xfs_dir2_node_replace(&args);
+out:
+	xfs_da_cleanup_name(&args, name);
 	return rval;
 }
 
@@ -388,9 +469,6 @@ xfs_dir_canenter(
 
 	ASSERT((dp->i_d.di_mode & S_IFMT) == S_IFDIR);
 
-	args.name = name;
-	args.namelen = namelen;
-	args.hashval = xfs_da_hashname(name, namelen);
 	args.inumber = 0;
 	args.dp = dp;
 	args.firstblock = NULL;
@@ -399,19 +477,24 @@ xfs_dir_canenter(
 	args.whichfork = XFS_DATA_FORK;
 	args.trans = tp;
 	args.justcheck = args.addname = args.oknoent = 1;
+	rval = xfs_da_setup_name_and_hash(&args, name, namelen);
+	if (rval)
+		return rval;
 
 	if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL)
 		rval = xfs_dir2_sf_addname(&args);
 	else if ((rval = xfs_dir2_isblock(tp, dp, &v)))
-		return rval;
+		goto out;
 	else if (v)
 		rval = xfs_dir2_block_addname(&args);
 	else if ((rval = xfs_dir2_isleaf(tp, dp, &v)))
-		return rval;
+		goto out;
 	else if (v)
 		rval = xfs_dir2_leaf_addname(&args);
 	else
 		rval = xfs_dir2_node_addname(&args);
+out:
+	xfs_da_cleanup_name(&args, name);
 	return rval;
 }
 

===========================================================================
fs/xfs/xfs_dir2.h
===========================================================================

--- a/fs/xfs/xfs_dir2.h	2008-01-18 15:31:24.000000000 +1100
+++ b/fs/xfs/xfs_dir2.h	2007-11-01 13:11:00.206583735 +1100
@@ -72,7 +72,8 @@ extern int xfs_dir_createname(struct xfs
 				xfs_fsblock_t *first,
 				struct xfs_bmap_free *flist, xfs_extlen_t tot);
 extern int xfs_dir_lookup(struct xfs_trans *tp, struct xfs_inode *dp,
-				char *name, int namelen, xfs_ino_t *inum);
+				char *name, int namelen, xfs_ino_t *inum,
+				char **actual_name, int *actual_namelen);
 extern int xfs_dir_removename(struct xfs_trans *tp, struct xfs_inode *dp,
 				char *name, int namelen, xfs_ino_t ino,
 				xfs_fsblock_t *first,
@@ -85,6 +86,13 @@ extern int xfs_dir_canenter(struct xfs_t
 				char *name, int namelen);
 extern int xfs_dir_ino_validate(struct xfs_mount *mp, xfs_ino_t ino);
 
+#define xfs_dir_hashname(dp, n, l) \
+		((dp)->i_mount->m_dirnameops->hashname((dp), (n), (l)))
+
+#define xfs_dir_compname(dp, n1, l1, n2, l2) \
+		((dp)->i_mount->m_dirnameops->compname((dp), (n1), (l1), \
+							(n2), (l2)))
+
 /*
  * Utility routines for v2 directories.
  */

===========================================================================
fs/xfs/xfs_dir2_block.c
===========================================================================

--- a/fs/xfs/xfs_dir2_block.c	2008-01-18 15:31:24.000000000 +1100
+++ b/fs/xfs/xfs_dir2_block.c	2008-01-11 14:28:44.763934272 +1100
@@ -38,6 +38,7 @@
 #include "xfs_dir2_block.h"
 #include "xfs_dir2_trace.h"
 #include "xfs_error.h"
+#include "xfs_unicode.h"
 
 /*
  * Local function prototypes.
@@ -450,6 +451,8 @@ xfs_dir2_block_getdents(
 	int			wantoff;	/* starting block offset */
 	xfs_ino_t		ino;
 	xfs_off_t		cook;
+	char			*nls_name = NULL; /* NLS name buffer */
+	int			nls_namelen = 0;
 
 	mp = dp->i_mount;
 	/*
@@ -481,6 +484,9 @@ xfs_dir2_block_getdents(
 	ptr = (char *)block->u;
 	endptr = (char *)xfs_dir2_block_leaf_p(btp);
 
+	if (mp->m_nls)
+		nls_name = xfs_alloc_unicode_nls_name();
+
 	/*
 	 * Loop over the data portion of the block.
 	 * Each object is a real entry (dep) or an unused one (dup).
@@ -513,15 +519,19 @@ xfs_dir2_block_getdents(
 #if XFS_BIG_INUMS
 		ino += mp->m_inoadd;
 #endif
+		if (mp->m_nls)
+			nls_namelen = xfs_unicode_to_nls(mp->m_nls, dep->name,
+					dep->namelen, &nls_name);
 
 		/*
 		 * If it didn't fit, set the final offset to here & return.
 		 */
-		if (filldir(dirent, dep->name, dep->namelen, cook,
-			    ino, DT_UNKNOWN)) {
+		if (filldir(dirent,
+				nls_namelen > 0 ? nls_name : (char *)dep->name,
+				nls_namelen > 0 ? nls_namelen : dep->namelen,
+				cook, ino, DT_UNKNOWN)) {
 			*offset = cook;
-			xfs_da_brelse(NULL, bp);
-			return 0;
+			goto out;
 		}
 	}
 
@@ -530,6 +540,9 @@ xfs_dir2_block_getdents(
 	 * Set the offset to a non-existent block 1 and return.
 	 */
 	*offset = xfs_dir2_db_off_to_dataptr(mp, mp->m_dirdatablk + 1, 0);
+out:
+	if (mp->m_nls)
+		xfs_free_unicode_nls_name(nls_name);
 	xfs_da_brelse(NULL, bp);
 	return 0;
 }
@@ -616,6 +629,14 @@ xfs_dir2_block_lookup(
 	 * Fill in inode number, release the block.
 	 */
 	args->inumber = be64_to_cpu(dep->inumber);
+	if (args->cmpresult == XFS_CMP_CASE) {
+		args->valuelen = xfs_unicode_to_nls(mp->m_nls, dep->name,
+				dep->namelen, (char **)&args->value);
+		if (args->valuelen < 0) {
+			xfs_da_brelse(args->trans, bp);
+			return XFS_ERROR(-args->valuelen);
+		}
+	}
 	xfs_da_brelse(args->trans, bp);
 	return XFS_ERROR(EEXIST);
 }
@@ -643,6 +664,7 @@ xfs_dir2_block_lookup_int(
 	int			mid;		/* binary search current idx */
 	xfs_mount_t		*mp;		/* filesystem mount point */
 	xfs_trans_t		*tp;		/* transaction pointer */
+	xfs_dacmp_t		cmp;		/* comparison result */
 
 	dp = args->dp;
 	tp = args->trans;
@@ -688,6 +710,7 @@ xfs_dir2_block_lookup_int(
 	 * Now loop forward through all the entries with the
 	 * right hash value looking for our name.
 	 */
+	args->cmpresult = XFS_CMP_DIFFERENT;
 	do {
 		if ((addr = be32_to_cpu(blp[mid].address)) == XFS_DIR2_NULL_DATAPTR)
 			continue;
@@ -697,20 +720,34 @@ xfs_dir2_block_lookup_int(
 		dep = (xfs_dir2_data_entry_t *)
 			((char *)block + xfs_dir2_dataptr_to_off(mp, addr));
 		/*
-		 * Compare, if it's right give back buffer & entry number.
-		 */
-		if (dep->namelen == args->namelen &&
-		    dep->name[0] == args->name[0] &&
-		    memcmp(dep->name, args->name, args->namelen) == 0) {
+		 * Compare, if it's right give back buffer & entry number:
+		 *
+		 * lookup case - use nameops;
+		 *
+		 * replace/remove case - as lookup has been already been
+		 * performed, look for an exact match using the fast method
+		 */
+		cmp = args->oknoent ?
+			xfs_dir_compname(dp, dep->name, dep->namelen,
+						args->name, args->namelen) :
+			xfs_default_compname(dp, dep->name, dep->namelen,
+						args->name, args->namelen);
+		if (cmp != XFS_CMP_DIFFERENT && cmp != args->cmpresult) {
+			args->cmpresult = cmp;
 			*bpp = bp;
 			*entno = mid;
+			if (cmp == XFS_CMP_EXACT)
 			return 0;
 		}
-	} while (++mid < be32_to_cpu(btp->count) && be32_to_cpu(blp[mid].hashval) == hash);
+	} while (++mid < be32_to_cpu(btp->count) &&
+			be32_to_cpu(blp[mid].hashval) == hash);
+
+	ASSERT(args->oknoent);
+	if (args->cmpresult == XFS_CMP_CASE)
+		return 0;
 	/*
 	 * No match, release the buffer and return ENOENT.
 	 */
-	ASSERT(args->oknoent);
 	xfs_da_brelse(tp, bp);
 	return XFS_ERROR(ENOENT);
 }
@@ -1187,8 +1224,8 @@ xfs_dir2_sf_to_block(
 		tagp = xfs_dir2_data_entry_tag_p(dep);
 		*tagp = cpu_to_be16((char *)dep - (char *)block);
 		xfs_dir2_data_log_entry(tp, bp, dep);
-		blp[2 + i].hashval = cpu_to_be32(xfs_da_hashname(
-					(char *)sfep->name, sfep->namelen));
+		blp[2 + i].hashval = cpu_to_be32(xfs_dir_hashname(dp,
+						sfep->name, sfep->namelen));
 		blp[2 + i].address = cpu_to_be32(xfs_dir2_byte_to_dataptr(mp,
 						 (char *)dep - (char *)block));
 		offset = (int)((char *)(tagp + 1) - (char *)block);

===========================================================================
fs/xfs/xfs_dir2_data.c
===========================================================================

--- a/fs/xfs/xfs_dir2_data.c	2008-01-18 15:31:24.000000000 +1100
+++ b/fs/xfs/xfs_dir2_data.c	2007-10-10 15:10:39.019079916 +1000
@@ -140,7 +140,8 @@ xfs_dir2_data_check(
 			addr = xfs_dir2_db_off_to_dataptr(mp, mp->m_dirdatablk,
 				(xfs_dir2_data_aoff_t)
 				((char *)dep - (char *)d));
-			hash = xfs_da_hashname((char *)dep->name, dep->namelen);
+			hash = xfs_dir_hashname(dp, (char *)dep->name,
+				dep->namelen);
 			for (i = 0; i < be32_to_cpu(btp->count); i++) {
 				if (be32_to_cpu(lep[i].address) == addr &&
 				    be32_to_cpu(lep[i].hashval) == hash)

===========================================================================
fs/xfs/xfs_dir2_leaf.c
===========================================================================

--- a/fs/xfs/xfs_dir2_leaf.c	2008-01-18 15:31:24.000000000 +1100
+++ b/fs/xfs/xfs_dir2_leaf.c	2008-01-17 17:06:07.795121260 +1100
@@ -40,6 +40,7 @@
 #include "xfs_dir2_node.h"
 #include "xfs_dir2_trace.h"
 #include "xfs_error.h"
+#include "xfs_unicode.h"
 
 /*
  * Local function declarations.
@@ -780,6 +781,8 @@ xfs_dir2_leaf_getdents(
 	int			ra_offset;	/* map entry offset for ra */
 	int			ra_want;	/* readahead count wanted */
 	xfs_ino_t		ino;
+	char			*nls_name = NULL; /* NLS name buffer */
+	int			nls_namelen = 0;
 
 	/*
 	 * If the offset is at or past the largest allowed value,
@@ -800,6 +803,9 @@ xfs_dir2_leaf_getdents(
 	map_valid = ra_index = ra_offset = ra_current = map_blocks = 0;
 	bp = NULL;
 
+	if (mp->m_nls)
+		nls_name = xfs_alloc_unicode_nls_name();
+
 	/*
 	 * Inside the loop we keep the main offset value as a byte offset
 	 * in the directory file.
@@ -1086,11 +1092,16 @@ xfs_dir2_leaf_getdents(
 #if XFS_BIG_INUMS
 		ino += mp->m_inoadd;
 #endif
+		if (mp->m_nls)
+			nls_namelen = xfs_unicode_to_nls(mp->m_nls, dep->name,
+					dep->namelen, &nls_name);
 
 		/*
 		 * Won't fit.  Return to caller.
 		 */
-		if (filldir(dirent, dep->name, dep->namelen,
+		if (filldir(dirent,
+				nls_namelen > 0 ? nls_name : (char *)dep->name,
+				nls_namelen > 0 ? nls_namelen : dep->namelen,
 			    xfs_dir2_byte_to_dataptr(mp, curoff),
 			    ino, DT_UNKNOWN))
 			break;
@@ -1113,6 +1124,8 @@ xfs_dir2_leaf_getdents(
 	kmem_free(map, map_size * sizeof(*map));
 	if (bp)
 		xfs_da_brelse(NULL, bp);
+	if (mp->m_nls)
+		xfs_free_unicode_nls_name(nls_name);
 	return error;
 }
 
@@ -1300,10 +1313,17 @@ xfs_dir2_leaf_lookup(
 	/*
 	 * Return the found inode number.
 	 */
+	error = EEXIST;
 	args->inumber = be64_to_cpu(dep->inumber);
+	if (args->cmpresult == XFS_CMP_CASE) {
+		args->valuelen = xfs_unicode_to_nls(args->dp->i_mount->m_nls,
+				dep->name, dep->namelen, (char **)&args->value);
+		if (args->valuelen < 0)
+			error = -args->valuelen;
+	}
 	xfs_da_brelse(tp, dbp);
 	xfs_da_brelse(tp, lbp);
-	return XFS_ERROR(EEXIST);
+	return XFS_ERROR(error);
 }
 
 /*
@@ -1331,6 +1351,7 @@ xfs_dir2_leaf_lookup_int(
 	xfs_mount_t		*mp;		/* filesystem mount point */
 	xfs_dir2_db_t		newdb;		/* new data block number */
 	xfs_trans_t		*tp;		/* transaction pointer */
+	xfs_dacmp_t		cmp;		/* name compare result */
 
 	dp = args->dp;
 	tp = args->trans;
@@ -1354,6 +1375,7 @@ xfs_dir2_leaf_lookup_int(
 	 * Loop over all the entries with the right hash value
 	 * looking to match the name.
 	 */
+	args->cmpresult = XFS_CMP_DIFFERENT;
 	for (lep = &leaf->ents[index], dbp = NULL, curdb = -1;
 	     index < be16_to_cpu(leaf->hdr.count) && be32_to_cpu(lep->hashval) == args->hashval;
 	     lep++, index++) {
@@ -1391,19 +1413,31 @@ xfs_dir2_leaf_lookup_int(
 		       xfs_dir2_dataptr_to_off(mp, be32_to_cpu(lep->address)));
 		/*
 		 * If it matches then return it.
-		 */
-		if (dep->namelen == args->namelen &&
-		    dep->name[0] == args->name[0] &&
-		    memcmp(dep->name, args->name, args->namelen) == 0) {
+		 *
+		 * lookup case - use nameops;
+		 *
+		 * replace/remove case - as lookup has been already been
+		 * performed, look for an exact match using the fast method
+		 */
+		cmp = args->oknoent ?
+			xfs_dir_compname(dp, dep->name, dep->namelen,
+						args->name, args->namelen) :
+			xfs_default_compname(dp, dep->name, dep->namelen,
+						args->name, args->namelen);
+		if (cmp != XFS_CMP_DIFFERENT && cmp != args->cmpresult) {
+			args->cmpresult = cmp;
 			*dbpp = dbp;
 			*indexp = index;
+			if (cmp == XFS_CMP_EXACT)
 			return 0;
 		}
 	}
+	ASSERT(args->oknoent);
+	if (args->cmpresult == XFS_CMP_CASE)
+		return 0;
 	/*
 	 * No match found, return ENOENT.
 	 */
-	ASSERT(args->oknoent);
 	if (dbp)
 		xfs_da_brelse(tp, dbp);
 	xfs_da_brelse(tp, lbp);

===========================================================================
fs/xfs/xfs_dir2_node.c
===========================================================================

--- a/fs/xfs/xfs_dir2_node.c	2008-01-18 15:31:25.000000000 +1100
+++ b/fs/xfs/xfs_dir2_node.c	2007-10-31 12:32:04.060201390 +1100
@@ -39,6 +39,7 @@
 #include "xfs_dir2_node.h"
 #include "xfs_dir2_trace.h"
 #include "xfs_error.h"
+#include "xfs_unicode.h"
 
 /*
  * Function declarations.
@@ -414,6 +415,7 @@ xfs_dir2_leafn_lookup_int(
 	xfs_dir2_db_t		newdb;		/* new data block number */
 	xfs_dir2_db_t		newfdb;		/* new free block number */
 	xfs_trans_t		*tp;		/* transaction pointer */
+	xfs_dacmp_t		cmp;		/* comparison result */
 
 	dp = args->dp;
 	tp = args->trans;
@@ -455,6 +457,7 @@ xfs_dir2_leafn_lookup_int(
 	/*
 	 * Loop over leaf entries with the right hash value.
 	 */
+	args->cmpresult = XFS_CMP_DIFFERENT;
 	for (lep = &leaf->ents[index];
 	     index < be16_to_cpu(leaf->hdr.count) && be32_to_cpu(lep->hashval) == args->hashval;
 	     lep++, index++) {
@@ -572,28 +575,34 @@ xfs_dir2_leafn_lookup_int(
 			/*
 			 * Point to the data entry.
 			 */
-			dep = (xfs_dir2_data_entry_t *)
-			      ((char *)curbp->data +
+			dep = (xfs_dir2_data_entry_t *)((char *)curbp->data +
 			       xfs_dir2_dataptr_to_off(mp, be32_to_cpu(lep->address)));
 			/*
 			 * Compare the entry, return it if it matches.
 			 */
-			if (dep->namelen == args->namelen &&
-			    dep->name[0] == args->name[0] &&
-			    memcmp(dep->name, args->name, args->namelen) == 0) {
+			cmp = args->oknoent ?
+				xfs_dir_compname(dp, dep->name, dep->namelen,
+						args->name, args->namelen):
+				xfs_default_compname(dp, dep->name, dep->namelen,
+						args->name, args->namelen);
+			if (cmp != XFS_CMP_DIFFERENT &&
+					cmp != args->cmpresult) {
+				args->cmpresult = cmp;
 				args->inumber = be64_to_cpu(dep->inumber);
 				*indexp = index;
 				state->extravalid = 1;
 				state->extrablk.bp = curbp;
 				state->extrablk.blkno = curdb;
-				state->extrablk.index =
-					(int)((char *)dep -
+				state->extrablk.index = (int)((char *)dep -
 					      (char *)curbp->data);
 				state->extrablk.magic = XFS_DIR2_DATA_MAGIC;
+				if (cmp == XFS_CMP_EXACT)
 				return XFS_ERROR(EEXIST);
 			}
 		}
 	}
+	if (args->cmpresult == XFS_CMP_CASE)
+		return XFS_ERROR(EEXIST);
 	/*
 	 * Didn't find a match.
 	 * If we are holding a buffer, give it back in case our caller
@@ -1768,6 +1777,19 @@ xfs_dir2_node_lookup(
 	if (error)
 		rval = error;
 	/*
+	 * If case-insens match, copy name out
+	 */
+	if (args->cmpresult == XFS_CMP_CASE) {
+		xfs_dir2_data_entry_t	*dep =
+				(xfs_dir2_data_entry_t *)
+					((char *)state->extrablk.bp->data +
+					state->extrablk.index);
+		args->valuelen = xfs_unicode_to_nls(args->dp->i_mount->m_nls,
+				dep->name, dep->namelen, (char **)&args->value);
+		if (args->valuelen < 0)
+			rval = -args->valuelen;
+	}
+	/*
 	 * Release the btree blocks and leaf block.
 	 */
 	for (i = 0; i < state->path.active; i++) {

===========================================================================
fs/xfs/xfs_dir2_sf.c
===========================================================================

--- a/fs/xfs/xfs_dir2_sf.c	2008-01-18 15:31:25.000000000 +1100
+++ b/fs/xfs/xfs_dir2_sf.c	2008-01-17 12:25:01.552398622 +1100
@@ -38,6 +38,7 @@
 #include "xfs_dir2_leaf.h"
 #include "xfs_dir2_block.h"
 #include "xfs_dir2_trace.h"
+#include "xfs_unicode.h"
 
 /*
  * Prototypes for internal functions.
@@ -708,6 +709,8 @@ xfs_dir2_sf_getdents(
 	xfs_dir2_dataptr_t	dot_offset;
 	xfs_dir2_dataptr_t	dotdot_offset;
 	xfs_ino_t		ino;
+	char			*nls_name = NULL; /* NLS name buffer */
+	int			nls_namelen = 0;
 
 	mp = dp->i_mount;
 
@@ -772,6 +775,9 @@ xfs_dir2_sf_getdents(
 		}
 	}
 
+	if (mp->m_nls)
+		nls_name = xfs_alloc_unicode_nls_name();
+
 	/*
 	 * Loop while there are more entries and put'ing works.
 	 */
@@ -789,16 +795,22 @@ xfs_dir2_sf_getdents(
 #if XFS_BIG_INUMS
 		ino += mp->m_inoadd;
 #endif
-
-		if (filldir(dirent, sfep->name, sfep->namelen,
+		if (mp->m_nls)
+			nls_namelen = xfs_unicode_to_nls(mp->m_nls, sfep->name,
+					sfep->namelen, &nls_name);
+		if (filldir(dirent,
+				nls_namelen > 0 ? nls_name : (char *)sfep->name,
+				nls_namelen > 0 ? nls_namelen : sfep->namelen,
 					    off, ino, DT_UNKNOWN)) {
 			*offset = off;
-			return 0;
+			goto out;
 		}
 		sfep = xfs_dir2_sf_nextentry(sfp, sfep);
 	}
-
 	*offset = xfs_dir2_db_off_to_dataptr(mp, mp->m_dirdatablk + 1, 0);
+out:
+	if (mp->m_nls)
+		xfs_free_unicode_nls_name(nls_name);
 	return 0;
 }
 
@@ -836,6 +848,7 @@ xfs_dir2_sf_lookup(
 	 */
 	if (args->namelen == 1 && args->name[0] == '.') {
 		args->inumber = dp->i_ino;
+		args->cmpresult = XFS_CMP_EXACT;
 		return XFS_ERROR(EEXIST);
 	}
 	/*
@@ -844,23 +857,43 @@ xfs_dir2_sf_lookup(
 	if (args->namelen == 2 &&
 	    args->name[0] == '.' && args->name[1] == '.') {
 		args->inumber = xfs_dir2_sf_get_inumber(sfp, &sfp->hdr.parent);
+		args->cmpresult = XFS_CMP_EXACT;
 		return XFS_ERROR(EEXIST);
 	}
 	/*
 	 * Loop over all the entries trying to match ours.
 	 */
-	for (i = 0, sfep = xfs_dir2_sf_firstentry(sfp);
-	     i < sfp->hdr.count;
+	args->cmpresult = XFS_CMP_DIFFERENT;
+	for (i = 0, sfep = xfs_dir2_sf_firstentry(sfp); i < sfp->hdr.count;
 	     i++, sfep = xfs_dir2_sf_nextentry(sfp, sfep)) {
-		if (sfep->namelen == args->namelen &&
-		    sfep->name[0] == args->name[0] &&
-		    memcmp(args->name, sfep->name, args->namelen) == 0) {
-			args->inumber =
-				xfs_dir2_sf_get_inumber(sfp,
+		switch (xfs_dir_compname(dp, sfep->name, sfep->namelen,
+				args->name, args->namelen)) {
+		case XFS_CMP_EXACT:
+			args->cmpresult = XFS_CMP_EXACT;
+			args->inumber = xfs_dir2_sf_get_inumber(sfp,
 					xfs_dir2_sf_inumberp(sfep));
+			if (args->value) {
+				xfs_free_unicode_nls_name(args->value);
+				args->value = NULL;
+			}
 			return XFS_ERROR(EEXIST);
+
+		case XFS_CMP_CASE:
+			if (!args->value) {
+				args->valuelen = xfs_unicode_to_nls(
+					args->dp->i_mount->m_nls, sfep->name,
+					sfep->namelen, (char **)&args->value);
+				if (args->valuelen < 0)
+					return XFS_ERROR(-args->valuelen);
+				args->cmpresult = XFS_CMP_CASE;
+				args->inumber = xfs_dir2_sf_get_inumber(sfp,
+						xfs_dir2_sf_inumberp(sfep));
+			}
+		default: ;
 		}
 	}
+	if (args->cmpresult == XFS_CMP_CASE)
+		return XFS_ERROR(EEXIST);
 	/*
 	 * Didn't find it.
 	 */
@@ -907,9 +940,8 @@ xfs_dir2_sf_removename(
 	for (i = 0, sfep = xfs_dir2_sf_firstentry(sfp);
 	     i < sfp->hdr.count;
 	     i++, sfep = xfs_dir2_sf_nextentry(sfp, sfep)) {
-		if (sfep->namelen == args->namelen &&
-		    sfep->name[0] == args->name[0] &&
-		    memcmp(sfep->name, args->name, args->namelen) == 0) {
+		if (xfs_default_compname(dp, sfep->name, sfep->namelen,
+				args->name, args->namelen) == XFS_CMP_EXACT) {
 			ASSERT(xfs_dir2_sf_get_inumber(sfp,
 					xfs_dir2_sf_inumberp(sfep)) ==
 				args->inumber);
@@ -1044,9 +1076,9 @@ xfs_dir2_sf_replace(
 		for (i = 0, sfep = xfs_dir2_sf_firstentry(sfp);
 		     i < sfp->hdr.count;
 		     i++, sfep = xfs_dir2_sf_nextentry(sfp, sfep)) {
-			if (sfep->namelen == args->namelen &&
-			    sfep->name[0] == args->name[0] &&
-			    memcmp(args->name, sfep->name, args->namelen) == 0) {
+			if (xfs_default_compname(dp, sfep->name,
+					sfep->namelen, args->name,
+					args->namelen) == XFS_CMP_EXACT) {
 #if XFS_BIG_INUMS || defined(DEBUG)
 				ino = xfs_dir2_sf_get_inumber(sfp,
 					xfs_dir2_sf_inumberp(sfep));

===========================================================================
fs/xfs/xfs_itable.c
===========================================================================

--- a/fs/xfs/xfs_itable.c	2008-01-18 15:31:25.000000000 +1100
+++ b/fs/xfs/xfs_itable.c	2008-01-18 13:45:10.740061749 +1100
@@ -45,6 +45,8 @@ xfs_internal_inum(
 	xfs_ino_t	ino)
 {
 	return (ino == mp->m_sb.sb_rbmino || ino == mp->m_sb.sb_rsumino ||
+		(xfs_sb_version_hasunicode(&mp->m_sb) &&
+		 ino == mp->m_sb.sb_cftino) ||
 		(XFS_SB_VERSION_HASQUOTA(&mp->m_sb) &&
 		 (ino == mp->m_sb.sb_uquotino || ino == mp->m_sb.sb_gquotino)));
 }

===========================================================================
fs/xfs/xfs_mount.c
===========================================================================

--- a/fs/xfs/xfs_mount.c	2008-01-18 15:31:25.000000000 +1100
+++ b/fs/xfs/xfs_mount.c	2008-01-17 17:10:29.777728874 +1100
@@ -25,6 +25,7 @@
 #include "xfs_sb.h"
 #include "xfs_ag.h"
 #include "xfs_dir2.h"
+#include "xfs_attr.h"
 #include "xfs_dmapi.h"
 #include "xfs_mount.h"
 #include "xfs_bmap_btree.h"
@@ -43,6 +44,9 @@
 #include "xfs_rw.h"
 #include "xfs_quota.h"
 #include "xfs_fsops.h"
+#include "xfs_da_btree.h"
+#include "xfs_attr_leaf.h"
+#include "xfs_unicode.h"
 
 STATIC void	xfs_mount_log_sbunit(xfs_mount_t *, __int64_t);
 STATIC int	xfs_uuid_mount(xfs_mount_t *);
@@ -119,6 +123,8 @@ static const struct {
     { offsetof(xfs_sb_t, sb_logsectsize),0 },
     { offsetof(xfs_sb_t, sb_logsunit),	 0 },
     { offsetof(xfs_sb_t, sb_features2),	 0 },
+    { offsetof(xfs_sb_t, sb_bad_features2), 0 },
+    { offsetof(xfs_sb_t, sb_cftino),	 0 },
     { sizeof(xfs_sb_t),			 0 }
 };
 
@@ -171,6 +177,9 @@ xfs_mount_free(
 			  sizeof(xfs_perag_t) * mp->m_sb.sb_agcount);
 	}
 
+	if (mp->m_cft)
+		xfs_unicode_free_cft(mp->m_cft);
+
 	spinlock_destroy(&mp->m_ail_lock);
 	spinlock_destroy(&mp->m_sb_lock);
 	mutex_destroy(&mp->m_ilock);
@@ -455,6 +464,8 @@ xfs_sb_from_disk(
 	to->sb_logsectsize = be16_to_cpu(from->sb_logsectsize);
 	to->sb_logsunit = be32_to_cpu(from->sb_logsunit);
 	to->sb_features2 = be32_to_cpu(from->sb_features2);
+	to->sb_bad_features2 = be32_to_cpu(from->sb_bad_features2);
+	to->sb_cftino = be64_to_cpu(from->sb_cftino);
 }
 
 /*
@@ -1062,7 +1073,7 @@ xfs_mountfs(
 	/*
 	 * Initialize the attribute manager's entries.
 	 */
-	mp->m_attr_magicpct = (mp->m_sb.sb_blocksize * 37) / 100;
+	xfs_attr_mount(mp);
 
 	/*
 	 * Initialize the precomputed transaction reservations values.
@@ -1165,6 +1176,17 @@ xfs_mountfs(
 	}
 
 	/*
+	 * Load in unicode case folding table from disk
+	 */
+	if (xfs_sb_version_hasunicode(&mp->m_sb)) {
+		error = xfs_unicode_read_cft(mp);
+		if (error) {
+			cmn_err(CE_WARN, "XFS: failed to read case folding table");
+			goto error4;
+		}
+	}
+
+	/*
 	 * If fs is not mounted readonly, then update the superblock
 	 * unit and width changes.
 	 */
@@ -1220,6 +1242,8 @@ xfs_mountfs(
 	 * Free up the root inode.
 	 */
 	VN_RELE(rvp);
+	if (mp->m_cft)
+		xfs_unicode_free_cft(mp->m_cft);
  error3:
 	xfs_log_unmount_dealloc(mp);
  error2:

===========================================================================
fs/xfs/xfs_mount.h
===========================================================================

--- a/fs/xfs/xfs_mount.h	2008-01-18 15:31:25.000000000 +1100
+++ b/fs/xfs/xfs_mount.h	2007-11-01 13:13:36.858098082 +1100
@@ -54,6 +54,7 @@ typedef struct xfs_trans_reservations {
 #else
 struct cred;
 struct log;
+struct nls_table;
 struct xfs_mount_args;
 struct xfs_inode;
 struct xfs_bmbt_irec;
@@ -61,6 +62,8 @@ struct xfs_bmap_free;
 struct xfs_extdelta;
 struct xfs_swapext;
 struct xfs_mru_cache;
+struct xfs_nameops;
+struct xfs_cft;
 
 /*
  * Prototypes and functions for the Data Migration subsystem.
@@ -306,6 +309,10 @@ typedef struct xfs_mount {
 	__uint8_t		m_inode_quiesce;/* call quiesce on new inodes.
 						   field governed by m_ilock */
 	__uint8_t		m_sectbb_log;	/* sectlog - BBSHIFT */
+	const struct xfs_nameops *m_dirnameops;	/* vector of dir name ops */
+	const struct xfs_nameops *m_attrnameops; /* vector of attr name ops */
+	const struct xfs_cft	*m_cft;		/* unicode case fold table */
+	struct nls_table 	*m_nls;		/* active NLS table */
 	int			m_dirblksize;	/* directory block sz--bytes */
 	int			m_dirblkfsbs;	/* directory block sz--fsbs */
 	xfs_dablk_t		m_dirdatablk;	/* blockno of dir data v2 */
@@ -371,7 +378,10 @@ typedef struct xfs_mount {
 						   counters */
 #define XFS_MOUNT_FILESTREAMS	(1ULL << 24)	/* enable the filestreams
 						   allocator */
-
+#define XFS_MOUNT_CI_LOOKUP	(1ULL << 25)	/* enable case-insensitive
+						 * file lookup */
+#define XFS_MOUNT_CI_ATTR	(1ULL << 26)	/* enable case-insensitive
+						   attribute names */
 
 /*
  * Default minimum read and write sizes.

===========================================================================
fs/xfs/xfs_rename.c
===========================================================================

--- a/fs/xfs/xfs_rename.c	2008-01-18 15:31:25.000000000 +1100
+++ b/fs/xfs/xfs_rename.c	2008-01-17 12:25:31.652529581 +1100
@@ -130,7 +130,9 @@ xfs_lock_for_rename(
 		lock_mode = xfs_ilock_map_shared(dp2);
 	}
 
-	error = xfs_dir_lookup_int(dp2, lock_mode, vname2, &inum2, &ip2);
+	error = xfs_dir_lookup_int(dp2, lock_mode, vname2, &inum2, &ip2,
+			NULL, NULL);
+
 	if (error == ENOENT) {		/* target does not need to exist. */
 		inum2 = 0;
 	} else if (error) {
@@ -214,6 +216,7 @@ xfs_lock_for_rename(
 	for (;i < 4; i++) {
 		i_tab[i] = NULL;
 	}
+
 	return 0;
 }
 

===========================================================================
fs/xfs/xfs_sb.h
===========================================================================

--- a/fs/xfs/xfs_sb.h	2008-01-18 15:31:25.000000000 +1100
+++ b/fs/xfs/xfs_sb.h	2007-10-23 16:55:47.440178601 +1000
@@ -46,10 +46,12 @@ struct xfs_mount;
 #define XFS_SB_VERSION_SECTORBIT	0x0800
 #define	XFS_SB_VERSION_EXTFLGBIT	0x1000
 #define	XFS_SB_VERSION_DIRV2BIT		0x2000
+#define XFS_SB_VERSION_OLDCIBIT		0x4000
 #define	XFS_SB_VERSION_MOREBITSBIT	0x8000
 #define	XFS_SB_VERSION_OKSASHFBITS	\
 	(XFS_SB_VERSION_EXTFLGBIT | \
-	 XFS_SB_VERSION_DIRV2BIT)
+	 XFS_SB_VERSION_DIRV2BIT | \
+	 XFS_SB_VERSION_OLDCIBIT)
 #define	XFS_SB_VERSION_OKREALFBITS	\
 	(XFS_SB_VERSION_ATTRBIT | \
 	 XFS_SB_VERSION_NLINKBIT | \
@@ -77,10 +79,12 @@ struct xfs_mount;
 #define XFS_SB_VERSION2_LAZYSBCOUNTBIT	0x00000002	/* Superblk counters */
 #define XFS_SB_VERSION2_RESERVED4BIT	0x00000004
 #define XFS_SB_VERSION2_ATTR2BIT	0x00000008	/* Inline attr rework */
+#define XFS_SB_VERSION2_UNICODEBIT	0x00000020	/* Unicode names */
 
 #define	XFS_SB_VERSION2_OKREALFBITS	\
 	(XFS_SB_VERSION2_LAZYSBCOUNTBIT	| \
-	 XFS_SB_VERSION2_ATTR2BIT)
+	 XFS_SB_VERSION2_ATTR2BIT | \
+	 XFS_SB_VERSION2_UNICODEBIT)
 #define	XFS_SB_VERSION2_OKSASHFBITS	\
 	(0)
 #define XFS_SB_VERSION2_OKREALBITS	\
@@ -145,6 +149,9 @@ typedef struct xfs_sb {
 	__uint16_t	sb_logsectsize;	/* sector size for the log, bytes */
 	__uint32_t	sb_logsunit;	/* stripe unit size for the log */
 	__uint32_t	sb_features2;	/* additional feature bits */
+	__uint32_t	sb_bad_features2; /* features2 could be here */
+	xfs_ino_t	sb_cftino;	/* unicode case folding table inode */
+	/* must be padded to 64 bit alignment */
 } xfs_sb_t;
 
 /*
@@ -205,6 +212,9 @@ typedef struct xfs_dsb {
 	__be16		sb_logsectsize;	/* sector size for the log, bytes */
 	__be32		sb_logsunit;	/* stripe unit size for the log */
 	__be32		sb_features2;	/* additional feature bits */
+	__be32		sb_bad_features2; /* features2 could be here */
+	__be64		sb_cftino;	/* unicode case folding table inode */
+	/* must be padded to 64 bit alignment */
 } xfs_dsb_t;
 
 /*
@@ -223,7 +233,7 @@ typedef enum {
 	XFS_SBS_GQUOTINO, XFS_SBS_QFLAGS, XFS_SBS_FLAGS, XFS_SBS_SHARED_VN,
 	XFS_SBS_INOALIGNMT, XFS_SBS_UNIT, XFS_SBS_WIDTH, XFS_SBS_DIRBLKLOG,
 	XFS_SBS_LOGSECTLOG, XFS_SBS_LOGSECTSIZE, XFS_SBS_LOGSUNIT,
-	XFS_SBS_FEATURES2,
+	XFS_SBS_FEATURES2, XFS_SBS_BAD_FEATURES2, XFS_SBS_CFTINO,
 	XFS_SBS_FIELDCOUNT
 } xfs_sb_field_t;
 
@@ -248,13 +258,15 @@ typedef enum {
 #define XFS_SB_IFREE		XFS_SB_MVAL(IFREE)
 #define XFS_SB_FDBLOCKS		XFS_SB_MVAL(FDBLOCKS)
 #define XFS_SB_FEATURES2	XFS_SB_MVAL(FEATURES2)
+#define XFS_SB_CASEFOLDINO	XFS_SB_MVAL(CASEFOLDINO)
 #define	XFS_SB_NUM_BITS		((int)XFS_SBS_FIELDCOUNT)
 #define	XFS_SB_ALL_BITS		((1LL << XFS_SB_NUM_BITS) - 1)
 #define	XFS_SB_MOD_BITS		\
 	(XFS_SB_UUID | XFS_SB_ROOTINO | XFS_SB_RBMINO | XFS_SB_RSUMINO | \
 	 XFS_SB_VERSIONNUM | XFS_SB_UQUOTINO | XFS_SB_GQUOTINO | \
 	 XFS_SB_QFLAGS | XFS_SB_SHARED_VN | XFS_SB_UNIT | XFS_SB_WIDTH | \
-	 XFS_SB_ICOUNT | XFS_SB_IFREE | XFS_SB_FDBLOCKS | XFS_SB_FEATURES2)
+	 XFS_SB_ICOUNT | XFS_SB_IFREE | XFS_SB_FDBLOCKS | XFS_SB_FEATURES2 | \
+	 XFS_SB_CFTINO)
 
 
 /*
@@ -463,6 +475,12 @@ static inline int xfs_sb_version_hassect
 		((sbp)->sb_versionnum & XFS_SB_VERSION_SECTORBIT);
 }
 
+static inline int xfs_sb_version_hasoldci(xfs_sb_t *sbp)
+{
+	return (XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_4) && \
+		((sbp)->sb_versionnum & XFS_SB_VERSION_OLDCIBIT);
+}
+
 #define XFS_SB_VERSION_HASMOREBITS(sbp)	xfs_sb_version_hasmorebits(sbp)
 static inline int xfs_sb_version_hasmorebits(xfs_sb_t *sbp)
 {
@@ -502,6 +520,13 @@ static inline void xfs_sb_version_addatt
 		((sbp)->sb_features2 | XFS_SB_VERSION2_ATTR2BIT)));
 }
 
+static inline int xfs_sb_version_hasunicode(xfs_sb_t *sbp)
+{
+	return (xfs_sb_version_hasmorebits(sbp) &&	\
+		((sbp)->sb_features2 & XFS_SB_VERSION2_UNICODEBIT));
+}
+
+
 /*
  * end of superblock version macros
  */

===========================================================================
fs/xfs/xfs_types.h
===========================================================================

--- a/fs/xfs/xfs_types.h	2008-01-18 15:31:25.000000000 +1100
+++ b/fs/xfs/xfs_types.h	2007-10-25 17:49:01.982895520 +1000
@@ -160,4 +160,9 @@ typedef enum {
 	XFS_BTNUM_MAX
 } xfs_btnum_t;
 
+typedef struct xfs_string {
+	const uchar_t	*name;
+	int		len;
+} xfs_string_t;
+
 #endif	/* __XFS_TYPES_H__ */

===========================================================================
fs/xfs/xfs_unicode.c
===========================================================================

--- a/fs/xfs/xfs_unicode.c	2006-06-17 00:58:24.000000000 +1000
+++ b/fs/xfs/xfs_unicode.c	2008-01-17 17:11:29.734083909 +1100
@@ -0,0 +1,523 @@
+/*
+ * Copyright (c) 2007 Silicon Graphics, Inc.
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include "xfs.h"
+#include "xfs_fs.h"
+#include "xfs_bit.h"
+#include "xfs_log.h"
+#include "xfs_inum.h"
+#include "xfs_clnt.h"
+#include "xfs_trans.h"
+#include "xfs_sb.h"
+#include "xfs_ag.h"
+#include "xfs_dir2.h"
+#include "xfs_alloc.h"
+#include "xfs_dmapi.h"
+#include "xfs_mount.h"
+#include "xfs_bmap_btree.h"
+#include "xfs_alloc_btree.h"
+#include "xfs_ialloc_btree.h"
+#include "xfs_dir2_sf.h"
+#include "xfs_attr_sf.h"
+#include "xfs_dinode.h"
+#include "xfs_inode.h"
+#include "xfs_btree.h"
+#include "xfs_ialloc.h"
+#include "xfs_itable.h"
+#include "xfs_rtalloc.h"
+#include "xfs_error.h"
+#include "xfs_bmap.h"
+#include "xfs_rw.h"
+#include "xfs_unicode.h"
+
+#define MAX_FOLD_CHARS	4
+
+static kmem_zone_t	*xfs_nls_uni_zone;
+
+static inline int
+xfs_casefold(
+	const xfs_cft_t *cft,
+	__uint16_t	c,
+	__uint16_t	*fc)
+{
+	__uint16_t	*table = XFS_CFT_PTR(cft, 0);
+	__uint16_t	tmp = table[c >> 8];
+	int		i;
+
+	if (!tmp) {
+		*fc = c;
+		return 1;
+	}
+	tmp = table[tmp + (c & 0xff)];
+	if ((tmp & 0xf000) != 0xe000) {
+		*fc = tmp;
+		return 1;
+	}
+	i = ((tmp >> 10) & 0x3) + 2;
+	ASSERT(i < cft->num_tables);
+	table = XFS_CFT_PTR(cft, i - 1) + ((tmp & 0x3ff) * i);
+
+	memcpy(fc, table, sizeof(__uint16_t) * i);
+
+	return i;
+}
+
+static inline int
+xfs_utf8_casefold(
+	const xfs_cft_t	*cft,
+	const uchar_t	**name,
+	int		*namelen,
+	__uint16_t	*fc)
+{
+	wchar_t		uc;
+
+	if (*namelen == 0)
+		return 0;
+
+	if (**name & 0x80) {
+		int	n = utf8_mbtowc(&uc, *name, *namelen);
+		if (n < 0) {
+			(*namelen)--;
+			*fc = *(*name)++;
+			return 1;
+		}
+		*name += n;
+		*namelen -= n;
+	} else {
+		uc = *(*name)++;
+		(*namelen)--;
+	}
+	return xfs_casefold(cft, uc, fc);
+}
+
+__uint32_t
+xfs_unicode_hash(
+	const xfs_cft_t	*cft,
+	const uchar_t	*name,
+	int		namelen)
+{
+	__uint32_t	hash = 0;
+	__uint16_t	fc[MAX_FOLD_CHARS];
+	int		nfc;
+	int		i;
+
+	while (namelen > 0) {
+		nfc = xfs_utf8_casefold(cft, &name, &namelen, fc);
+		for (i = 0; i < nfc; i++)
+			hash = fc[i] ^ rol32(hash, 7);
+	}
+	return hash;
+}
+
+int
+xfs_unicode_casecmp(
+	const xfs_cft_t	*cft,
+	const uchar_t	*name1,
+	int		len1,
+	const uchar_t	*name2,
+	int		len2)
+{
+	__uint16_t	fc1[MAX_FOLD_CHARS], fc2[MAX_FOLD_CHARS];
+	__uint16_t	*pfc1, *pfc2;
+	int		nfc1, nfc2;
+
+	nfc1 = xfs_utf8_casefold(cft, &name1, &len1, fc1);
+	pfc1 = fc1;
+	nfc2 = xfs_utf8_casefold(cft, &name2, &len2, fc2);
+	pfc2 = fc2;
+
+	while (nfc1 > 0 && nfc2 > 0) {
+		if (*pfc1 != *pfc2)
+			return (*pfc1 < *pfc2) ? -1 : 1;
+		if (!--nfc1) {
+			nfc1 = xfs_utf8_casefold(cft, &name1, &len1, fc1);
+			pfc1 = fc1;
+		} else
+			pfc1++;
+		if (!--nfc2) {
+			nfc2 = xfs_utf8_casefold(cft, &name2, &len2, fc2);
+			pfc2 = fc2;
+		} else
+			pfc2++;
+	}
+	if (nfc1 != nfc2)
+		return (nfc1 < nfc2) ? -1 : 1;
+	return 0;
+}
+
+
+char *
+xfs_alloc_unicode_nls_name(void)
+{
+	return kmem_zone_alloc(xfs_nls_uni_zone, KM_SLEEP);
+}
+
+
+void
+xfs_free_unicode_nls_name(
+	char		*name)
+{
+	kmem_zone_free(xfs_nls_uni_zone, name);
+}
+
+int
+xfs_nls_to_unicode(
+	struct nls_table *nls,
+	const char	*nls_name,
+	int		nls_namelen,
+	char		**uni_name)
+{
+	char		*n;
+	int		i, o;
+	wchar_t		uc;
+	int		nlen;
+	int		u8len;
+	int		rval;
+
+	n = *uni_name ? *uni_name : xfs_alloc_unicode_nls_name();
+
+	if (!nls) {
+		if (nls_namelen > MAXNAMELEN) {
+			rval = -ENAMETOOLONG;
+			goto err_out;
+		}
+		memcpy(n, nls_name, nls_namelen);
+		*uni_name = n;
+		return nls_namelen;
+	}
+
+	for (i = 0, o = 0; i < nls_namelen; i += nlen, o += u8len) {
+		nlen = nls->char2uni(nls_name + i, nls_namelen - i, &uc);
+		if (nlen < 0) {
+			rval = nlen;
+			goto err_out;
+		}
+		if (uc >= 0xfffe || (uc >= 0xd800 && uc <= 0xdfff)) {
+			rval = -EINVAL;	/* don't support chars outside BMP */
+			goto err_out;
+		}
+		u8len = utf8_wctomb(n + o, uc, MAXNAMELEN - o);
+		if (u8len <= 0) {
+			rval = (MAXNAMELEN - o < 3) ? -ENAMETOOLONG : -EINVAL;
+			goto err_out;
+		}
+	}
+	*uni_name = n;
+	return o;
+err_out:
+	if (*uni_name == NULL)
+		xfs_free_unicode_nls_name(n);
+	return rval;
+}
+
+int
+xfs_unicode_to_nls(
+	struct nls_table *nls,
+	const char	*uni_name,
+	int		uni_namelen,
+	char		**nls_name)
+{
+	char		*n;
+	int		i, o;
+	wchar_t		uc;
+	int		nlen;
+	int		u8len;
+	int		rval;
+
+	n = *nls_name ? *nls_name : xfs_alloc_unicode_nls_name();
+
+	if (!nls) {
+		if (uni_namelen > MAXNAMELEN) {
+			rval = -ENAMETOOLONG;
+			goto err_out;
+		}
+		memcpy(n, uni_name, uni_namelen);
+		*nls_name = n;
+		return uni_namelen;
+	}
+
+	for (i = 0, o = 0; i < uni_namelen && o < MAXNAMELEN;
+			i += u8len, o += nlen) {
+		u8len = utf8_mbtowc(&uc, uni_name + i, uni_namelen - i);
+		if (u8len < 0) {
+			rval = -EINVAL;
+			goto err_out;
+		}
+		nlen = nls->uni2char(uc, n + o, MAXNAMELEN - o);
+		if (nlen == -EINVAL) {
+			n[o] = '?';
+			nlen = 1;
+		} else if (nlen < 0) {
+			rval = nlen;
+			goto err_out;
+		}
+	}
+	if (i == uni_namelen) {
+		*nls_name = n;
+		return o;
+	}
+
+	rval = -ENAMETOOLONG;
+err_out:
+	if (*nls_name == NULL)
+		xfs_free_unicode_nls_name(n);
+	return rval;
+}
+
+int
+xfs_unicode_validate(
+	const uchar_t	*name,
+	int		namelen)
+{
+	wchar_t		uc;
+	int		i, nlen;
+
+	for (i = 0; i < namelen; i += nlen) {
+		if (*name >= 0xf0) {
+			cmn_err(CE_WARN, "xfs_unicode_validate: "
+					"UTF-8 char beyond U+FFFF\n");
+			return -EINVAL;
+		}
+		/* utf8_mbtowc must fail on overlong sequences too */
+		nlen = utf8_mbtowc(&uc, name + i, namelen - i);
+		if (nlen < 0) {
+			cmn_err(CE_WARN, "xfs_unicode_validate: "
+					"invalid UTF-8 sequence\n");
+			return -EILSEQ;
+		}
+		/* check for invalid/surrogate/private unicode chars */
+		if (uc >= 0xfffe || (uc >= 0xd800 && uc <= 0xf8ff)) {
+			cmn_err(CE_WARN, "xfs_unicode_validate: "
+					"unsupported UTF-8 char\n");
+			return -EINVAL;
+		}
+	}
+	return 0;
+}
+
+/*
+ * Unicode Case Fold Table management
+ */
+
+struct cft_item {
+	xfs_cft_t	*table;
+	int		size;
+	int		refcount;
+};
+
+static mutex_t		cft_lock;
+static int		cft_size;
+static struct cft_item	*cft_list;
+
+static xfs_cft_t *
+add_cft(
+	xfs_dcft_t	*dcft,
+	int		size)
+{
+	int		found = 0;
+	int		i, j;
+	xfs_cft_t	*cft;
+	__be16		*duc;
+	__uint16_t	*uc;
+
+	mutex_lock(&cft_lock);
+
+	for (i = 0; i < cft_size; i++) {
+		if (cft_list[i].size != size)
+			continue;
+		cft = cft_list[i].table;
+		if (cft->num_tables != be32_to_cpu(dcft->num_tables) ||
+				cft->flags != be32_to_cpu(dcft->flags))
+			continue;
+		found = 1;
+		for (j = 0; j < cft->num_tables; j++) {
+			if (cft->table_offset[j] !=
+					be32_to_cpu(dcft->table_offset[j])) {
+				found = 0;
+				break;
+			}
+		}
+		if (found) {
+			cft_list[i].refcount++;
+			mutex_unlock(&cft_lock);
+			return cft;
+		}
+	}
+
+	cft = vmalloc(size);
+	if (!cft) {
+		mutex_unlock(&cft_lock);
+		return NULL;
+	}
+	cft->magic = be32_to_cpu(dcft->magic);
+	cft->flags = be32_to_cpu(dcft->flags);
+	cft->num_tables = be32_to_cpu(dcft->num_tables);
+	ASSERT(cft->num_tables <= MAX_FOLD_CHARS);
+	for (i = 0; i < cft->num_tables; i++)
+		cft->table_offset[i] = be32_to_cpu(dcft->table_offset[i]);
+	j = (size - cft->table_offset[0]) / sizeof(__uint16_t);
+	uc = XFS_CFT_PTR(cft, 0);
+	duc = XFS_DCFT_PTR(dcft, 0);
+	for (i = 0; i < j; i++)
+		uc[i] = be16_to_cpu(duc[i]);
+
+	cft_list = kmem_realloc(cft_list,
+			(cft_size + 1) * sizeof(struct cft_item),
+			cft_size  * sizeof(struct cft_item), KM_SLEEP);
+	cft_list[cft_size].table = cft;
+	cft_list[cft_size].size = size;
+	cft_list[cft_size].refcount = 1;
+	cft_size++;
+
+	mutex_unlock(&cft_lock);
+
+	return cft;
+}
+
+static void
+remove_cft(
+	const xfs_cft_t	*cft)
+{
+	int		i;
+
+	mutex_lock(&cft_lock);
+
+	for (i = 0; i < cft_size; i++) {
+		if (cft_list[i].table == cft) {
+			ASSERT(cft_list[i].refcount > 0);
+			cft_list[i].refcount--;
+			break;
+		}
+	}
+
+	mutex_unlock(&cft_lock);
+}
+
+
+int
+xfs_unicode_read_cft(
+	xfs_mount_t	*mp)
+{
+	int		error;
+	xfs_inode_t	*cftip;
+	int		size;
+	int		nfsb;
+	int             nmap;
+	xfs_bmbt_irec_t *mapp;
+	int		n;
+	int		byte_cnt;
+	xfs_buf_t	*bp;
+	char		*table;
+	xfs_dcft_t	*dcft;
+
+	if (mp->m_sb.sb_cftino == NULLFSINO || mp->m_sb.sb_cftino == 0)
+		return EINVAL;
+	error = xfs_iget(mp, NULL, mp->m_sb.sb_cftino, 0, 0, &cftip, 0);
+	if (error)
+		return error;
+	ASSERT(cftip != NULL);
+
+	size = cftip->i_d.di_size;
+	nfsb = cftip->i_d.di_nblocks;
+
+	table = vmalloc(size);
+	if (!table) {
+		xfs_iput(cftip, 0);
+		return ENOMEM;
+	}
+	dcft = (xfs_dcft_t *)table;
+
+	nmap = nfsb;
+	mapp = kmem_alloc(nfsb * sizeof(xfs_bmbt_irec_t), KM_SLEEP);
+
+	error = xfs_bmapi(NULL, cftip, 0, nfsb, 0, NULL, 0, mapp, &nmap,
+			NULL, NULL);
+	if (error)
+		goto out;
+
+	for (n = 0; n < nmap; n++) {
+		byte_cnt = XFS_FSB_TO_B(mp, mapp[n].br_blockcount);
+
+		error = xfs_read_buf(mp, mp->m_ddev_targp,
+				XFS_FSB_TO_DADDR(mp, mapp[n].br_startblock),
+				BTOBB(byte_cnt), 0, &bp);
+		if (error)
+			goto out;
+
+		if (size < byte_cnt)
+			byte_cnt = size;
+		size -= byte_cnt;
+		memcpy(table, XFS_BUF_PTR(bp), byte_cnt);
+		table += byte_cnt;
+		xfs_buf_relse(bp);
+	}
+
+	/* verify case table read off disk */
+	if (!uuid_equal(&dcft->uuid, &mp->m_sb.sb_uuid)) {
+		error = EINVAL;
+		goto out;
+	}
+
+	/* clear UUID for in-memory copy/compare */
+	memset(&dcft->uuid, 0, sizeof(dcft->uuid));
+
+	mp->m_cft = add_cft(dcft, cftip->i_d.di_size);
+	if (mp->m_cft == NULL)
+		error = ENOMEM;
+
+out:
+	xfs_iput(cftip, 0);
+	kmem_free(mapp, nfsb * sizeof(xfs_bmbt_irec_t));
+	vfree(dcft);
+
+	return error;
+}
+
+void
+xfs_unicode_free_cft(
+	const xfs_cft_t	*cft)
+{
+	remove_cft(cft);
+}
+
+void
+xfs_unicode_init(void)
+{
+	mutex_init(&cft_lock);
+	xfs_nls_uni_zone = kmem_zone_init(MAXNAMELEN, "xfs_nls_uni");
+}
+
+void
+xfs_unicode_uninit(void)
+{
+	int		i;
+
+	mutex_lock(&cft_lock);
+
+	for (i = 0; i < cft_size; i++) {
+		ASSERT(cft_list[i].refcount == 0);
+		vfree(cft_list[i].table);
+	}
+	kmem_free(cft_list, cft_size * sizeof(struct cft_item));
+	cft_size = 0;
+	cft_list = NULL;
+
+	mutex_unlock(&cft_lock);
+
+	kmem_zone_destroy(xfs_nls_uni_zone);
+}

===========================================================================
fs/xfs/xfs_unicode.h
===========================================================================

--- a/fs/xfs/xfs_unicode.h	2006-06-17 00:58:24.000000000 +1000
+++ b/fs/xfs/xfs_unicode.h	2008-01-11 11:59:50.325468145 +1100
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2007 Silicon Graphics, Inc.
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+#ifndef __XFS_UNICODE_H__
+#define	__XFS_UNICODE_H__
+
+#define	XFS_CFT_MAGIC		0x58434654	/* 'XCFT' */
+#define XFS_CFT_FLAG_TURKIC	0x00000001
+#define XFS_CFT_FLAG_MAX	0x00000001
+
+/*
+ * Case Fold Table - on disk version. Must match the incore version below.
+ */
+typedef struct xfs_dcft {
+	__be32			magic;		/* validity check */
+	__be32			flags;
+	uuid_t			uuid;		/* UUID of the filesystem */
+	__be32			crc;		/* for future support */
+	__be32			num_tables;	/* single, double, etc */
+	__be32			table_offset[1];
+} xfs_dcft_t;
+
+/*
+ * Case Fold Table - in core version. Must match the ondisk version above.
+ */
+typedef struct xfs_cft {
+	__uint32_t		magic;
+	__uint32_t		flags;
+	uuid_t			uuid;		/* UUID of the filesystem */
+	__uint32_t		crc;
+	__uint32_t		num_tables;	/* single, double, etc */
+	__uint32_t		table_offset[1];/* num_tables sized */
+	/* 16-bit array tables immediately follow */
+} xfs_cft_t;
+
+#define XFS_CFT_PTR(t,n)	(__uint16_t *)(((char *)(t)) + \
+					(t)->table_offset[n])
+#define XFS_DCFT_PTR(t,n)	(__be16 *)(((char *)(t)) + \
+					be32_to_cpu((t)->table_offset[n]))
+
+void xfs_unicode_init(void);
+void xfs_unicode_uninit(void);
+
+__uint32_t xfs_unicode_hash(const xfs_cft_t *cft,
+				const uchar_t *name, int namelen);
+
+int xfs_unicode_casecmp(const xfs_cft_t *cft, const uchar_t *name1,
+				int len1, const uchar_t *name2, int len2);
+
+char *xfs_alloc_unicode_nls_name(void);
+void xfs_free_unicode_nls_name(char *name);
+int xfs_nls_to_unicode(struct nls_table *nls, const char *nls_name,
+			      int nls_namelen, char **uni_name);
+int xfs_unicode_to_nls(struct nls_table *nls, const char *uni_name,
+			      int uni_namelen, char **nls_name);
+
+int xfs_unicode_validate(const uchar_t *name, int namelen);
+
+int xfs_unicode_read_cft(struct xfs_mount *mp);
+void xfs_unicode_free_cft(const xfs_cft_t *cft);
+
+#endif /* __XFS_UNICODE_H__ */

===========================================================================
fs/xfs/xfs_utils.c
===========================================================================

--- a/fs/xfs/xfs_utils.c	2008-01-18 15:31:25.000000000 +1100
+++ b/fs/xfs/xfs_utils.c	2007-10-26 15:42:04.856766756 +1000
@@ -39,6 +39,7 @@
 #include "xfs_rw.h"
 #include "xfs_itable.h"
 #include "xfs_utils.h"
+#include "xfs_unicode.h"
 
 /*
  * xfs_get_dir_entry is used to get a reference to an inode given
@@ -69,13 +70,16 @@ xfs_dir_lookup_int(
 	uint		lock_mode,
 	bhv_vname_t	*dentry,
 	xfs_ino_t	*inum,
-	xfs_inode_t	**ipp)
+	xfs_inode_t	**ipp,
+	char		**actual_name,
+	int		*actual_namelen)
 {
 	int		error;
 
 	xfs_itrace_entry(dp);
 
-	error = xfs_dir_lookup(NULL, dp, VNAME(dentry), VNAMELEN(dentry), inum);
+	error = xfs_dir_lookup(NULL, dp, VNAME(dentry), VNAMELEN(dentry), inum,
+			actual_name, actual_namelen);
 	if (!error) {
 		/*
 		 * Unlock the directory. We do this because we can't
@@ -102,6 +106,8 @@ xfs_dir_lookup_int(
 			xfs_ilock(dp, lock_mode);
 			error = XFS_ERROR(ENOENT);
 		}
+		if (error && actual_name)
+			xfs_free_unicode_nls_name(*actual_name);
 	}
 	return error;
 }

===========================================================================
fs/xfs/xfs_utils.h
===========================================================================

--- a/fs/xfs/xfs_utils.h	2008-01-18 15:31:25.000000000 +1100
+++ b/fs/xfs/xfs_utils.h	2007-10-26 15:35:54.052564595 +1000
@@ -23,7 +23,7 @@
 
 extern int xfs_get_dir_entry (bhv_vname_t *, xfs_inode_t **);
 extern int xfs_dir_lookup_int (xfs_inode_t *, uint, bhv_vname_t *, xfs_ino_t *,
-				xfs_inode_t **);
+				xfs_inode_t **, char **, int *);
 extern int xfs_truncate_file (xfs_mount_t *, xfs_inode_t *);
 extern int xfs_dir_ialloc (xfs_trans_t **, xfs_inode_t *, mode_t, xfs_nlink_t,
 				xfs_dev_t, cred_t *, prid_t, int,

===========================================================================
fs/xfs/xfs_vfsops.c
===========================================================================

--- a/fs/xfs/xfs_vfsops.c	2008-01-18 15:31:25.000000000 +1100
+++ b/fs/xfs/xfs_vfsops.c	2008-01-11 14:47:36.214448806 +1100
@@ -56,7 +56,7 @@
 #include "xfs_fsops.h"
 #include "xfs_vnodeops.h"
 #include "xfs_vfsops.h"
-
+#include "xfs_unicode.h"
 
 int
 xfs_init(void)
@@ -81,6 +81,7 @@ xfs_init(void)
 	xfs_acl_zone_init(xfs_acl_zone, "xfs_acl");
 	xfs_mru_cache_init();
 	xfs_filestream_init();
+	xfs_unicode_init();
 
 	/*
 	 * The size of the zone allocated buf log item is the maximum
@@ -158,6 +159,7 @@ xfs_cleanup(void)
 	xfs_cleanup_procfs();
 	xfs_sysctl_unregister();
 	xfs_refcache_destroy();
+	xfs_unicode_uninit();
 	xfs_filestream_uninit();
 	xfs_mru_cache_uninit();
 	xfs_acl_zone_destroy(xfs_acl_zone);
@@ -247,7 +249,6 @@ xfs_start_flags(
 		mp->m_logname = kmem_alloc(strlen(ap->logname) + 1, KM_SLEEP);
 		strcpy(mp->m_logname, ap->logname);
 	}
-
 	if (ap->flags & XFSMNT_WSYNC)
 		mp->m_flags |= XFS_MOUNT_WSYNC;
 #if XFS_BIG_INUMS
@@ -404,6 +405,39 @@ xfs_finish_flags(
 			mp->m_qflags |= XFS_OQUOTA_ENFD;
 	}
 
+	if (xfs_sb_version_hasunicode(&mp->m_sb)) {
+		if (ap->flags2 & XFSMNT2_CILOOKUP)
+			mp->m_flags |= XFS_MOUNT_CI_LOOKUP;
+		if (ap->flags2 & XFSMNT2_CIATTR)
+			mp->m_flags |= XFS_MOUNT_CI_ATTR;
+
+		mp->m_nls = ap->nls[0] ? load_nls(ap->nls) : load_nls_default();
+		if (!mp->m_nls) {
+			cmn_err(CE_WARN,
+	"XFS: unable to load nls mapping \"%s\"\n", ap->nls);
+			return XFS_ERROR(EINVAL);
+		}
+		if (strcmp(mp->m_nls->charset, XFS_NLS_UTF8) == 0) {
+			/* special case utf8 - no translation required */
+			unload_nls(mp->m_nls);
+			mp->m_nls = NULL;
+		}
+	} else {
+		/*
+		 * Check for mount options which require a Unicode FS
+		 */
+		if (ap->flags2 & (XFSMNT2_CILOOKUP | XFSMNT2_CIATTR)) {
+			cmn_err(CE_WARN,
+	"XFS: can't do case-insensitive mount on non-utf8 filesystem");
+			return XFS_ERROR(EINVAL);
+
+		}
+		if (ap->nls[0]) {
+			cmn_err(CE_WARN,
+	"XFS: can't use nls mount option on non-utf8 filesystem");
+			return XFS_ERROR(EINVAL);
+		}
+	}
 	return 0;
 }
 
@@ -641,6 +675,8 @@ out:
 		xfs_unmountfs(mp, credp);
 		xfs_qmops_put(mp);
 		xfs_dmops_put(mp);
+		if (mp->m_nls)
+			unload_nls(mp->m_nls);
 		kmem_free(mp, sizeof(xfs_mount_t));
 	}
 

===========================================================================
fs/xfs/xfs_vnodeops.c
===========================================================================

--- a/fs/xfs/xfs_vnodeops.c	2008-01-18 15:31:25.000000000 +1100
+++ b/fs/xfs/xfs_vnodeops.c	2008-01-11 14:20:12.000000000 +1100
@@ -1793,7 +1793,9 @@ int
 xfs_lookup(
 	xfs_inode_t		*dp,
 	bhv_vname_t		*dentry,
-	bhv_vnode_t		**vpp)
+	bhv_vnode_t		**vpp,
+	char			**actual_name,
+	int			*actual_namelen)
 {
 	xfs_inode_t		*ip;
 	xfs_ino_t		e_inum;
@@ -1806,7 +1808,8 @@ xfs_lookup(
 		return XFS_ERROR(EIO);
 
 	lock_mode = xfs_ilock_map_shared(dp);
-	error = xfs_dir_lookup_int(dp, lock_mode, dentry, &e_inum, &ip);
+	error = xfs_dir_lookup_int(dp, lock_mode, dentry, &e_inum, &ip,
+			actual_name, actual_namelen);
 	if (!error) {
 		*vpp = XFS_ITOV(ip);
 		xfs_itrace_ref(ip);

===========================================================================
fs/xfs/xfs_vnodeops.h
===========================================================================

--- a/fs/xfs/xfs_vnodeops.h	2008-01-18 15:31:25.000000000 +1100
+++ b/fs/xfs/xfs_vnodeops.h	2008-01-11 14:20:12.000000000 +1100
@@ -25,7 +25,7 @@ int xfs_fsync(struct xfs_inode *ip, int 
 int xfs_release(struct xfs_inode *ip);
 int xfs_inactive(struct xfs_inode *ip);
 int xfs_lookup(struct xfs_inode *dp, bhv_vname_t *dentry,
-		bhv_vnode_t **vpp);
+		bhv_vnode_t **vpp, char **actual_name, int *actual_namelen);
 int xfs_create(struct xfs_inode *dp, bhv_vname_t *dentry, mode_t mode,
 		xfs_dev_t rdev, bhv_vnode_t **vpp, struct cred *credp);
 int xfs_remove(struct xfs_inode *dp, bhv_vname_t	*dentry);

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [REVIEW 1/2] Case insensitive support for XFS - kernel patch
  2008-01-18  4:43 [REVIEW 1/2] Case insensitive support for XFS - kernel patch Barry Naujok
@ 2008-01-19  5:22 ` Eric Sandeen
  2008-01-21  3:53   ` Barry Naujok
  2008-01-19  5:30 ` Eric Sandeen
  1 sibling, 1 reply; 5+ messages in thread
From: Eric Sandeen @ 2008-01-19  5:22 UTC (permalink / raw)
  To: Barry Naujok; +Cc: xfs@oss.sgi.com, xfs-dev


Barry Naujok wrote:
> This patch should apply to 2.6.24-rc6.

actually doesn't seem to; at least doesn't apply to rc8... mount
handling has moved, etc - well, easy enough to fix up.

The samba guys are excited about this, I bet :)

Lots of comments below; keep an open mind and a sense of humor, most
aren't demands but ideas, musings etc...


> ===========================================================================
> fs/xfs/Makefile
> ===========================================================================
> 
> --- a/fs/xfs/Makefile	2008-01-18 15:31:23.000000000 +1100
> +++ b/fs/xfs/Makefile	2007-10-23 16:17:22.173903950 +1000
> @@ -74,6 +74,7 @@ xfs-y				+= xfs_alloc.o \
>  				   xfs_trans_extfree.o \
>  				   xfs_trans_inode.o \
>  				   xfs_trans_item.o \
> +				   xfs_unicode.o \

obj-$(CONFIG_XFS_UNICODE)  perhaps?  It's a lot of new code that not
everyone needs?

>  				   xfs_utils.o \
>  				   xfs_vfsops.o \
>  				   xfs_vnodeops.o \
> 
...

> ===========================================================================
> fs/xfs/linux-2.6/xfs_iops.c
> ===========================================================================
> 
> --- a/fs/xfs/linux-2.6/xfs_iops.c	2008-01-18 15:31:23.000000000 +1100
> +++ b/fs/xfs/linux-2.6/xfs_iops.c	2008-01-17 12:26:26.905427167 +1100
> @@ -47,6 +47,8 @@
>  #include "xfs_buf_item.h"
>  #include "xfs_utils.h"
>  #include "xfs_vnodeops.h"
> +#include "xfs_da_btree.h"
> +#include "xfs_unicode.h"
>  
>  #include <linux/capability.h>
>  #include <linux/xattr.h>
> @@ -388,7 +390,7 @@ xfs_vn_lookup(
>  	if (dentry->d_name.len >= MAXNAMELEN)
>  		return ERR_PTR(-ENAMETOOLONG);
>  
> -	error = xfs_lookup(XFS_I(dir), dentry, &cvp);
> +	error = xfs_lookup(XFS_I(dir), dentry, &cvp, NULL, NULL);
>  	if (unlikely(error)) {
>  		if (unlikely(error != ENOENT))
>  			return ERR_PTR(-error);
> @@ -399,6 +401,113 @@ xfs_vn_lookup(
>  	return d_splice_alias(vn_to_inode(cvp), dentry);
>  }
>  
> +STATIC struct dentry *
> +xfs_vn_ci_lookup(
> +	struct inode	*dir,
> +	struct dentry	*dentry,
> +	struct nameidata *nd)
> +{
> +	bhv_vnode_t	*cvp;
> +	int		error;
> +	struct dentry	*result;
> +	struct qstr	actual_name;
> +	struct inode	*inode;
> +
> +	if (dentry->d_name.len >= MAXNAMELEN)
> +		return ERR_PTR(-ENAMETOOLONG);
> +
> +	error = xfs_lookup(XFS_I(dir), dentry, &cvp, (char **)&actual_name.name,
> +			&actual_name.len);
> +	if (unlikely(error)) {
> +		if (unlikely(error != ENOENT))
> +			return ERR_PTR(-error);
> +		d_add(dentry, NULL);
> +		return NULL;
> +	}
> +	inode = vn_to_inode(cvp);
> +
> +	/* if exact match, just splice and exit */
> +	if (!actual_name.name) {
> +		result = d_splice_alias(inode, dentry);
> +		return result;
> +	}
> +
> +	/*
> +	 * case-insensitive match, create a dentry to return and fill it
> +	 * in with the correctly cased name. Parameter "dentry" is not
> +	 * used anymore and the caller will free it.
> +	 * Derived from fs/ntfs/namei.c
> +	 */
> +
> +	actual_name.hash = full_name_hash(actual_name.name, actual_name.len);
> +
> +	/* Does an existing dentry match? */
> +	result = d_lookup(dentry->d_parent, &actual_name);
> +	if (!result) {
> +		/* if not, create one */
> +		result = d_alloc(dentry->d_parent, &actual_name);
> +		xfs_free_unicode_nls_name((char *)actual_name.name);
> +		if (!result)
> +			return ERR_PTR(-ENOMEM);
> +		dentry = d_splice_alias(inode, result);
> +		if (dentry) {
> +			dput(result);
> +			return dentry;
> +		}
> +		return result;
> +	}
> +	xfs_free_unicode_nls_name((char *)actual_name.name);
> +
> +	/* an existing dentry matches, use it */
> +
> +	if (result->d_inode) {
> +		/*
> +		 * already an inode attached, deref the inode that was
> +		 * refcounted with xfs_lookup and return the dentry.
> +		 */
> +		if (unlikely(result->d_inode != inode)) {
> +			/* This can happen because bad inodes are unhashed. */
> +			BUG_ON(!is_bad_inode(inode));
> +			BUG_ON(!is_bad_inode(result->d_inode));
> +		}
> +		iput(inode);
> +		return result;
> +	}
> +
> +	if (!S_ISDIR(inode->i_mode)) {
> +		/* not a directory, easy to handle */
> +		d_instantiate(result, inode);
> +		return result;
> +	}
> +
> +	spin_lock(&dcache_lock);
> +	if (list_empty(&inode->i_dentry)) {
> +		/*
> +		 * Directory without a 'disconnected' dentry; we need to do
> +		 * d_instantiate() by hand because it takes dcache_lock which
> +		 * we already hold.
> +		 */
> +		list_add(&result->d_alias, &inode->i_dentry);
> +		result->d_inode = inode;
> +		spin_unlock(&dcache_lock);
> +		security_d_instantiate(result, inode);
> +		return result;
> +	}
> +	/*
> +	 * Directory with a 'disconnected' dentry; get a reference to the
> +	 * 'disconnected' dentry.
> +	 */
> +	dentry = list_entry(inode->i_dentry.next, struct dentry, d_alias);
> +	dget_locked(dentry);
> +	spin_unlock(&dcache_lock);
> +	security_d_instantiate(result, inode);
> +	d_move(dentry, result);
> +	iput(inode);
> +	dput(result);
> +	return dentry;
> +}
> +
> +

It seems like it might be nice to make the CI code a compile-time
option?  Fairly big new chunks... if it could be done cleanly, might be
nice...

> ===========================================================================
> fs/xfs/linux-2.6/xfs_linux.h
> ===========================================================================
> 
> --- a/fs/xfs/linux-2.6/xfs_linux.h	2008-01-18 15:31:24.000000000 +1100
> +++ b/fs/xfs/linux-2.6/xfs_linux.h	2008-01-11 14:49:16.537591564 +1100
> @@ -75,6 +75,8 @@
>  #include <linux/delay.h>
>  #include <linux/log2.h>
>  #include <linux/spinlock.h>
> +#include <linux/ctype.h>
> +#include <linux/nls.h>
>  
>  #include <asm/page.h>
>  #include <asm/div64.h>
> @@ -180,6 +182,12 @@
>  #define howmany(x, y)	(((x)+((y)-1))/(y))
>  
>  /*
> + * NLS UTF-8 character set
> + */
> +
> +#define XFS_NLS_UTF8	"utf8"

I guess that had to go somewhere? :)

> +
> +/*
>   * Various platform dependent calls that don't fit anywhere else
>   */
>  #define xfs_sort(a,n,s,fn)	sort(a,n,s,fn,NULL)
> 
> ===========================================================================
> fs/xfs/linux-2.6/xfs_super.c
> ===========================================================================
> 
> --- a/fs/xfs/linux-2.6/xfs_super.c	2008-01-18 15:31:24.000000000 +1100
> +++ b/fs/xfs/linux-2.6/xfs_super.c	2008-01-11 14:46:25.067566854 +1100
> @@ -50,6 +50,7 @@
>  #include "xfs_vnodeops.h"
>  #include "xfs_vfsops.h"
>  #include "xfs_version.h"
> +#include "xfs_unicode.h"
>  #include "xfs_log_priv.h"
>  
>  #include <linux/namei.h>
> @@ -121,6 +122,9 @@ xfs_args_allocate(
>  #define MNTOPT_ATTR2	"attr2"		/* do use attr2 attribute format */
>  #define MNTOPT_NOATTR2	"noattr2"	/* do not use attr2 attribute format */
>  #define MNTOPT_FILESTREAM  "filestreams" /* use filestreams allocator */
> +#define MNTOPT_NLS	"nls"		/* NLS code page to use */
> +#define MNTOPT_CILOOKUP	"ci"		/* case-insensitive dir names */
> +#define MNTOPT_CIATTR	"ciattr"	/* case-insensitive attr names */
>  #define MNTOPT_QUOTA	"quota"		/* disk quotas (user) */
>  #define MNTOPT_NOQUOTA	"noquota"	/* no quotas */
>  #define MNTOPT_USRQUOTA	"usrquota"	/* user quota enabled */


Please document in Documentation/filesystems/xfs.txt too...

> @@ -315,6 +319,18 @@ xfs_parseargs(
>  			args->flags &= ~XFSMNT_ATTR2;
>  		} else if (!strcmp(this_char, MNTOPT_FILESTREAM)) {
>  			args->flags2 |= XFSMNT2_FILESTREAMS;
> +		} else if (!strcmp(this_char, MNTOPT_NLS)) {
> +			if (!value || !*value) {
> +				cmn_err(CE_WARN,
> +					"XFS: %s option requires an argument",
> +					this_char);
> +				return EINVAL;
> +			}
> +			strncpy(args->nls, value, MAXNAMELEN);
> +		} else if (!strcmp(this_char, MNTOPT_CILOOKUP)) {
> +			args->flags2 |= XFSMNT2_CILOOKUP;
> +		} else if (!strcmp(this_char, MNTOPT_CIATTR)) {
> +			args->flags2 |= XFSMNT2_CIATTR;
>  		} else if (!strcmp(this_char, MNTOPT_NOQUOTA)) {
>  			args->flags &= ~(XFSMNT_UQUOTAENF|XFSMNT_UQUOTA);
>  			args->flags &= ~(XFSMNT_GQUOTAENF|XFSMNT_GQUOTA);
> @@ -454,6 +470,8 @@ xfs_showargs(
>  		{ XFS_MOUNT_OSYNCISOSYNC,	"," MNTOPT_OSYNCISOSYNC },
>  		{ XFS_MOUNT_ATTR2,		"," MNTOPT_ATTR2 },
>  		{ XFS_MOUNT_FILESTREAMS,	"," MNTOPT_FILESTREAM },
> +		{ XFS_MOUNT_CI_LOOKUP,		"," MNTOPT_CILOOKUP },
> +		{ XFS_MOUNT_CI_ATTR,		"," MNTOPT_CIATTR },
>  		{ XFS_MOUNT_DMAPI,		"," MNTOPT_DMAPI },
>  		{ XFS_MOUNT_GRPID,		"," MNTOPT_GRPID },
>  		{ 0, NULL }
> @@ -516,6 +534,13 @@ xfs_showargs(
>  	if (!(mp->m_qflags & XFS_ALL_QUOTA_ACCT))
>  		seq_puts(m, "," MNTOPT_NOQUOTA);
>  
> +	if (xfs_sb_version_hasunicode(&mp->m_sb)) {
> +		if (mp->m_nls)
> +			seq_printf(m, "," MNTOPT_NLS "=%s", mp->m_nls->charset);
> +		else
> +			seq_puts(m, "," MNTOPT_NLS "=" XFS_NLS_UTF8);
> +	}
> +
>  	return 0;
>  }
>  __uint64_t
> @@ -563,7 +588,11 @@ xfs_set_inodeops(
>  		inode->i_mapping->a_ops = &xfs_address_space_operations;
>  		break;
>  	case S_IFDIR:
> -		inode->i_op = &xfs_dir_inode_operations;
> +		inode->i_op =
> +			xfs_sb_version_hasoldci(&XFS_I(inode)->i_mount->m_sb) ||
> +			(XFS_I(inode)->i_mount->m_flags & XFS_MOUNT_CI_LOOKUP) ?
> +				&xfs_dir_ci_inode_operations :
> +				&xfs_dir_inode_operations;

Do any linux filesystems in existence actually have
XFS_SB_VERSION_OLDCIBIT?  I'd think not - this patch is *adding* that
macro - what's this for?

>  		inode->i_fop = &xfs_dir_file_operations;
>  		break;
>  	case S_IFLNK:
> 
> ===========================================================================
> fs/xfs/xfs_attr.c
> ===========================================================================
> 
> --- a/fs/xfs/xfs_attr.c	2008-01-18 15:31:24.000000000 +1100
> +++ b/fs/xfs/xfs_attr.c	2008-01-18 13:25:20.068339942 +1100
> @@ -106,6 +106,17 @@ ktrace_t *xfs_attr_trace_buf;
>   * Overall external interface routines.
>   *========================================================================*/
>  
> +void
> +xfs_attr_mount(struct xfs_mount *mp)
> +{
> +	mp->m_attr_magicpct = (mp->m_sb.sb_blocksize * 37) / 100;
> +	if (xfs_sb_version_hasunicode(&mp->m_sb)) {
> +		mp->m_attrnameops = (mp->m_flags & XFS_MOUNT_CI_ATTR) ?
> +			&xfs_unicode_ci_nameops : &xfs_unicode_nameops;
> +	} else
> +		mp->m_attrnameops = &xfs_default_nameops;
> +}

Hm, I thought most little mount-helper subroutines went right in next to
xfs_mountfs() in xfs_mount.c; just a thought.  Then you wouldn't need
the prototype in the header either...

> +
>  int
>  xfs_attr_fetch(xfs_inode_t *ip, const char *name, int namelen,
>  	       char *value, int *valuelenp, int flags, struct cred *cred)
> @@ -122,14 +133,14 @@ xfs_attr_fetch(xfs_inode_t *ip, const ch
>  	 * Fill in the arg structure for this request.
>  	 */
>  	memset((char *)&args, 0, sizeof(args));
> -	args.name = name;
> -	args.namelen = namelen;
>  	args.value = value;
>  	args.valuelen = *valuelenp;
>  	args.flags = flags;
> -	args.hashval = xfs_da_hashname(args.name, args.namelen);
>  	args.dp = ip;
>  	args.whichfork = XFS_ATTR_FORK;
> +	error = xfs_da_setup_name_and_hash(&args, name, namelen);
> +	if (error)
> +		return error;

In the spirit of incremental-but-functional patches, which helps to
break down & review big patchsets like this, I think this addition of
xfs_da_setup_name_and_hash() could stand on its own, and add the nls
stuff in a subsequent patch?

>  	/*
>  	 * Decide on what work routines to call based on the inode size.
> @@ -153,6 +164,7 @@ xfs_attr_fetch(xfs_inode_t *ip, const ch
>  
>  	if (error == EEXIST)
>  		error = 0;
> +	xfs_da_cleanup_name(&args, name);

I'm being a little lazy here; under which circumstances would args->name
!= name, and need to be freed?

...

> ===========================================================================
> fs/xfs/xfs_attr_leaf.c
> ===========================================================================
> 
> --- a/fs/xfs/xfs_attr_leaf.c	2008-01-18 15:31:24.000000000 +1100
> +++ b/fs/xfs/xfs_attr_leaf.c	2008-01-18 13:25:11.873394723 +1100
> @@ -42,6 +42,7 @@
>  #include "xfs_attr.h"
>  #include "xfs_attr_leaf.h"
>  #include "xfs_error.h"
> +#include "xfs_unicode.h"
>  
>  /*
>   * xfs_attr_leaf.c
> @@ -90,6 +91,10 @@ STATIC void xfs_attr_leaf_moveents(xfs_a
>  					 xfs_mount_t *mp);
>  STATIC int xfs_attr_leaf_entsize(xfs_attr_leafblock_t *leaf, int index);
>  
> +STATIC int xfs_attr_put_listent(xfs_attr_list_context_t *context,
> +					attrnames_t *namesp, char *name,
> +					int namelen, int valuelen, char *value);
> +
>  /*========================================================================
>   * Namespace helper routines
>   *========================================================================*/
> @@ -135,6 +140,38 @@ xfs_attr_namesp_match_overrides(int arg_
>   * External routines when attribute fork size < XFS_LITINO(mp).
>   *========================================================================*/
>  
> +STATIC xfs_attr_sf_entry_t *
> +xfs_attr_shortform_find_ent(xfs_da_args_t *args)
> +{
> +	xfs_attr_shortform_t *sf;
> +	xfs_attr_sf_entry_t *sfe;
> +	int i;
> +	xfs_attr_sf_entry_t *ci_sfe = NULL;
> +
> +	ASSERT(args->dp->i_afp->if_flags & XFS_IFINLINE);
> +	sf = (xfs_attr_shortform_t *)args->dp->i_afp->if_u1.if_data;
> +	sfe = &sf->list[0];
> +
> +	args->cmpresult = XFS_CMP_DIFFERENT;
> +	for (i = 0; i < sf->hdr.count; sfe = XFS_ATTR_SF_NEXTENTRY(sfe), i++) {
> +		if (!xfs_attr_namesp_match(args->flags, sfe->flags))
> +			continue;
> +		switch (xfs_attr_compname(args->dp, sfe->nameval, sfe->namelen,
> +				args->name, args->namelen)) {
> +		case XFS_CMP_EXACT:
> +			args->cmpresult = XFS_CMP_EXACT;
> +			return sfe;
> +		case XFS_CMP_CASE:
> +			if (!ci_sfe) {
> +				args->cmpresult = XFS_CMP_CASE;
> +				ci_sfe = sfe;
> +			}
> +		default:;
> +		}
> +	}
> +	return ci_sfe;
> +}

Perhaps this helper could be a sub-patch too?  Just a thought.

>  /*
>   * Query whether the requested number of additional bytes of extended
>   * attribute space will be able to fit inline.
> @@ -295,13 +332,10 @@ xfs_attr_shortform_add(xfs_da_args_t *ar
>  	sfe = &sf->list[0];
>  	for (i = 0; i < sf->hdr.count; sfe = XFS_ATTR_SF_NEXTENTRY(sfe), i++) {
>  #ifdef DEBUG
> -		if (sfe->namelen != args->namelen)
> -			continue;
> -		if (memcmp(args->name, sfe->nameval, args->namelen) != 0)
> -			continue;
>  		if (!xfs_attr_namesp_match(args->flags, sfe->flags))
>  			continue;
> -		ASSERT(0);
> +		ASSERT(xfs_attr_compname(args->dp, args->name, args->namelen,
> +			sfe->nameval, sfe->namelen) == XFS_CMP_DIFFERENT);

Perhaps this helper too could come as a subpatch.

...


> +	if (!sfe)
> +		return XFS_ERROR(ENOATTR);
> +

Indentation from here on looks busted.  Are you missing braces?

>  		if (args->flags & ATTR_KERNOVAL) {
>  			args->valuelen = sfe->valuelen;
> -			return(XFS_ERROR(EEXIST));
> +		return XFS_ERROR(EEXIST);

need another tab here?

>  		}
>  		if (args->valuelen < sfe->valuelen) {
>  			args->valuelen = sfe->valuelen;
> -			return(XFS_ERROR(ERANGE));
> +		return XFS_ERROR(ERANGE);

and here?

>  		}
>  		args->valuelen = sfe->valuelen;
> -		memcpy(args->value, &sfe->nameval[args->namelen],
> -						    args->valuelen);
> -		return(XFS_ERROR(EEXIST));
> -	}
> -	return(XFS_ERROR(ENOATTR));
> +	memcpy(args->value, &sfe->nameval[args->namelen], args->valuelen);
> +
> +	return XFS_ERROR(EEXIST);
>  }
>  
>  /*
...


> @@ -631,7 +626,7 @@ xfs_attr_shortform_list(xfs_attr_list_co
>  				continue;
>  			}
>  			namesp = xfs_attr_flags_namesp(sfe->flags);
> -			error = context->put_listent(context,
> +			error = xfs_attr_put_listent(context,
>  					   namesp,
>  					   (char *)sfe->nameval,
>  					   (int)sfe->namelen,
> @@ -734,7 +729,7 @@ xfs_attr_shortform_list(xfs_attr_list_co
>  			cursor->hashval = sbp->hash;
>  			cursor->offset = 0;
>  		}
> -		error = context->put_listent(context,
> +		error = xfs_attr_put_listent(context,
>  					namesp,
>  					sbp->name,
>  					sbp->namelen,

maybe the introduction of xfs_attr_put_listent could be a small
prep-work patch too.  Or is this getting old ;)

> @@ -1960,6 +1955,7 @@ xfs_attr_leaf_lookup_int(xfs_dabuf_t *bp
>  	xfs_attr_leaf_name_remote_t *name_rmt;
>  	int probe, span;
>  	xfs_dahash_t hashval;
> +	xfs_dacmp_t cmp;
>  
>  	leaf = bp->data;
>  	ASSERT(be16_to_cpu(leaf->hdr.info.magic) == XFS_ATTR_LEAF_MAGIC);
> @@ -2008,6 +2004,7 @@ xfs_attr_leaf_lookup_int(xfs_dabuf_t *bp
>  	/*
>  	 * Duplicate keys may be present, so search all of them for a match.
>  	 */
> +	args->cmpresult = XFS_CMP_DIFFERENT;
>  	for (  ; (probe < be16_to_cpu(leaf->hdr.count)) &&
>  			(be32_to_cpu(entry->hashval) == hashval);
>  			entry++, probe++) {
> @@ -2019,35 +2016,40 @@ xfs_attr_leaf_lookup_int(xfs_dabuf_t *bp
>  		 * If we are looking for complete entries, show only those.
>  		 */
>  		if ((args->flags & XFS_ATTR_INCOMPLETE) !=
> -		    (entry->flags & XFS_ATTR_INCOMPLETE)) {
> -			continue;
> -		}
> -		if (entry->flags & XFS_ATTR_LOCAL) {
> -			name_loc = XFS_ATTR_LEAF_NAME_LOCAL(leaf, probe);
> -			if (name_loc->namelen != args->namelen)
> -				continue;
> -			if (memcmp(args->name, (char *)name_loc->nameval, args->namelen) != 0)
> +				(entry->flags & XFS_ATTR_INCOMPLETE))
>  				continue;
>  			if (!xfs_attr_namesp_match(args->flags, entry->flags))
>  				continue;
> +		if (entry->flags & XFS_ATTR_LOCAL) {
> +			name_loc = XFS_ATTR_LEAF_NAME_LOCAL(leaf, probe);
> +			cmp = xfs_attr_compname(args->dp, args->name, args->namelen,
> +					name_loc->nameval, name_loc->namelen);
> +			if (cmp != XFS_CMP_DIFFERENT && cmp != args->cmpresult) {
> +				args->cmpresult = cmp;
>  			args->index = probe;

weird indentation here too...

> -			return(XFS_ERROR(EEXIST));
> +				args->rmtblkno = 0;
> +				args->rmtblkcnt = 0;
> +				if (cmp == XFS_CMP_EXACT)
> +					return XFS_ERROR(EEXIST);
> +			}
>  		} else {
>  			name_rmt = XFS_ATTR_LEAF_NAME_REMOTE(leaf, probe);
> -			if (name_rmt->namelen != args->namelen)
> -				continue;
> -			if (memcmp(args->name, (char *)name_rmt->name,
> -					     args->namelen) != 0)
> -				continue;
> -			if (!xfs_attr_namesp_match(args->flags, entry->flags))
> -				continue;
> +			cmp = xfs_attr_compname(args->dp, args->name, args->namelen,
> +					name_rmt->name, name_rmt->namelen);
> +			if (cmp != XFS_CMP_DIFFERENT && cmp != args->cmpresult) {
> +				args->cmpresult = cmp;
>  			args->index = probe;
>  			args->rmtblkno = be32_to_cpu(name_rmt->valueblk);
>  			args->rmtblkcnt = XFS_B_TO_FSB(args->dp->i_mount,
>  						   be32_to_cpu(name_rmt->valuelen));
> -			return(XFS_ERROR(EEXIST));
> +				if (cmp == XFS_CMP_EXACT)
> +					return XFS_ERROR(EEXIST);

and here

> +			}
>  		}
>  	}
> +	if (args->cmpresult == XFS_CMP_CASE)
> +		return XFS_ERROR(EEXIST);
> +
>  	args->index = probe;
>  	return(XFS_ERROR(ENOATTR));
>  }
> @@ -2418,7 +2420,7 @@ xfs_attr_leaf_list_int(xfs_dabuf_t *bp, 
>  			xfs_attr_leaf_name_local_t *name_loc =
>  				XFS_ATTR_LEAF_NAME_LOCAL(leaf, i);
>  
> -			retval = context->put_listent(context,
> +			retval = xfs_attr_put_listent(context,

sub-patch? :)

...				(int)name_rmt->namelen,
> @@ -2472,6 +2474,31 @@ xfs_attr_leaf_list_int(xfs_dabuf_t *bp, 
>  	return(retval);
>  }
>  
> +/*
> + * Do NLS name conversion if required for attribute name and call
> + * context's put_listent routine
> + */
> +
> +STATIC int
> +xfs_attr_put_listent(xfs_attr_list_context_t *context, attrnames_t *namesp,
> +	char *name, int namelen, int valuelen, char *value)
> +{
> +	xfs_mount_t *mp = context->dp->i_mount;
> +	char *nls_name = NULL;
> +	int rval;
> +
> +	if (!mp->m_nls)
> +		return context->put_listent(context, namesp, name, namelen,
> +				valuelen, value);
> +
> +	rval = xfs_unicode_to_nls(mp->m_nls, name, namelen, &nls_name);
> +	if (rval < 0)
> +		return -rval;
> +	rval = context->put_listent(context, namesp, nls_name, rval,
> +			valuelen, value);
> +	xfs_free_unicode_nls_name(nls_name);
> +	return rval;
> +}
>  
>  /*========================================================================
>   * Manage the INCOMPLETE flag in a leaf entry
> ===========================================================================
> fs/xfs/xfs_attr_leaf.h
> ===========================================================================
> 
> --- a/fs/xfs/xfs_attr_leaf.h	2008-01-18 15:31:24.000000000 +1100
> +++ b/fs/xfs/xfs_attr_leaf.h	2008-01-11 14:16:44.268796245 +1100
> @@ -36,6 +36,7 @@ struct xfs_da_args;
>  struct xfs_da_state;
>  struct xfs_da_state_blk;
>  struct xfs_inode;
> +struct xfs_mount;
>  struct xfs_trans;
>  
>  /*========================================================================
> @@ -204,6 +205,16 @@ static inline int xfs_attr_leaf_entsize_
>  	return (((bsize) >> 1) + ((bsize) >> 2));
>  }
>  
> +/*
> + * Do hash and name compare based on nameops
> + */
> +#define xfs_attr_hashname(dp, n, l) \
> +		((dp)->i_mount->m_attrnameops->hashname((dp), (n), (l)))
> +
> +#define xfs_attr_compname(dp, n1, l1, n2, l2) \
> +		((dp)->i_mount->m_attrnameops->compname((dp), (n1), (l1), \
> +							(n2), (l2)))
> +
>  
>  /*========================================================================
>   * Structure used to pass context around among the routines.
> @@ -296,6 +307,7 @@ int	xfs_attr_root_inactive(struct xfs_tr
>  /*
>   * Utility routines.
>   */
> +void	xfs_attr_mount(struct xfs_mount *mp);

this could just go in xfs_mount.c and be static.

>  xfs_dahash_t	xfs_attr_leaf_lasthash(struct xfs_dabuf *bp, int *count);
>  int	xfs_attr_leaf_order(struct xfs_dabuf *leaf1_bp,
>  				   struct xfs_dabuf *leaf2_bp);
> 

> ===========================================================================
> fs/xfs/xfs_da_btree.c
> ===========================================================================
> 
> --- a/fs/xfs/xfs_da_btree.c	2008-01-18 15:31:24.000000000 +1100
> +++ b/fs/xfs/xfs_da_btree.c	2007-10-31 16:04:16.463309546 +1100
> @@ -46,6 +46,7 @@
>  #include "xfs_dir2_block.h"
>  #include "xfs_dir2_node.h"
>  #include "xfs_error.h"
> +#include "xfs_unicode.h"
>  
>  /*
>   * xfs_da_btree.c
> @@ -1530,6 +1531,100 @@ xfs_da_hashname(const uchar_t *name, int
>  	}
>  }
>  
> +
> +static xfs_dahash_t
> +xfs_default_hashname(xfs_inode_t *inode, const uchar_t *name, int namelen)
> +{
> +	return xfs_da_hashname(name, namelen);
> +}

Could these be rolled into one instead of the wrapper?  I see 3 other
direct callers of xfs_da_hashname... and does xfs_attr_shortform_list
need to use xfs_attr_hashname instead of xfs_da_hashname?  Do "." and
".." ever have different answers in different codepages?  Does that matter?

> +xfs_dacmp_t
> +xfs_default_compname(xfs_inode_t *inode, const uchar_t *name1, int len1,
> +	const uchar_t *name2, int len2)
> +{
> +	return (len1 == len2 && memcmp(name1, name2, len1) == 0) ?
> +			XFS_CMP_EXACT : XFS_CMP_DIFFERENT;
> +}
> +
> +static xfs_dahash_t
> +xfs_unicode_ci_hashname(
> +	xfs_inode_t	*inode,
> +	const uchar_t	*name,
> +	int		namelen)
> +{
> +	return xfs_unicode_hash(inode->i_mount->m_cft, name, namelen);
> +}

this wrapper is the only caller of xfs_unicode_hash?  Is it just to
conveniently get from inode to m_cft?

strikes me as a little interesting that while an inode is passed into a
xfs_hashname_t, it's never actually used directly - same for compname -
would it make any sense to just pass the xfs_cft in from the start?

...

> ===========================================================================
> fs/xfs/xfs_dir2.c
> ===========================================================================
> 
> --- a/fs/xfs/xfs_dir2.c	2008-01-18 15:31:24.000000000 +1100
> +++ b/fs/xfs/xfs_dir2.c	2008-01-11 14:24:51.701973714 +1100
> @@ -42,8 +42,56 @@
>  #include "xfs_dir2_node.h"
>  #include "xfs_dir2_trace.h"
>  #include "xfs_error.h"
> +#include "xfs_unicode.h"
>  #include "xfs_vnodeops.h"
>  
> +/*
> + * V1 case-insensitive support for directories
> + */

remind me, what's "V1?"

> +static xfs_dahash_t
> +xfs_ascii_ci_hashname(
> +	xfs_inode_t	*inode,
> +	const uchar_t	*name,
> +	int		namelen)
> +{
> +	xfs_dahash_t	hash;
> +	int		i;
> +
> +	for (i = 0, hash = 0; i < namelen; i++)
> +		hash = tolower(name[i]) ^ rol32(hash, 7);
> +
> +	return hash;
> +}
> +
> +static xfs_dacmp_t
> +xfs_ascii_ci_compname(
> +	xfs_inode_t	*inode,
> +	const uchar_t	*name1,
> +	int		len1,
> +	const uchar_t	*name2,
> +	int 		len2)
> +{
> +	xfs_dacmp_t	result = XFS_CMP_EXACT;
> +	int		i;
> +
> +	if (len1 != len2)
> +		return XFS_CMP_DIFFERENT;
> +
> +	for (i = 0; i < len1; i++) {
> +		if (name1[i] == name2[i])
> +			continue;
> +		if (tolower(name1[i]) != tolower(name2[i]))
> +			return XFS_CMP_DIFFERENT;
> +		result = XFS_CMP_CASE;
> +	}
> +
> +	return result;
> +}
> +
> +static const struct xfs_nameops xfs_ascii_ci_nameops = {
> +	.hashname	= xfs_ascii_ci_hashname,
> +	.compname	= xfs_ascii_ci_compname,
> +};
>  
>  void
>  xfs_dir_mount(
> @@ -64,6 +112,13 @@ xfs_dir_mount(
>  		(mp->m_dirblksize - (uint)sizeof(xfs_da_node_hdr_t)) /
>  		(uint)sizeof(xfs_da_node_entry_t);
>  	mp->m_dir_magicpct = (mp->m_dirblksize * 37) / 100;
> +
> +	if (xfs_sb_version_hasunicode(&mp->m_sb)) {
> +		mp->m_dirnameops = (mp->m_flags & XFS_MOUNT_CI_LOOKUP) ?
> +			&xfs_unicode_ci_nameops : &xfs_unicode_nameops;
> +	} else
> +		mp->m_dirnameops = (xfs_sb_version_hasoldci(&mp->m_sb)) ?
> +			&xfs_ascii_ci_nameops : &xfs_default_nameops;

Oh, "V1" is oldci - can you document it that way in the comment?  And if
that hasn't been seen in the wild on linux can this go away?

...

> ===========================================================================
> fs/xfs/xfs_dir2_block.c
> ===========================================================================
> 
> --- a/fs/xfs/xfs_dir2_block.c	2008-01-18 15:31:24.000000000 +1100
> +++ b/fs/xfs/xfs_dir2_block.c	2008-01-11 14:28:44.763934272 +1100
...

> @@ -697,20 +720,34 @@ xfs_dir2_block_lookup_int(
>  		dep = (xfs_dir2_data_entry_t *)
>  			((char *)block + xfs_dir2_dataptr_to_off(mp, addr));
>  		/*
> -		 * Compare, if it's right give back buffer & entry number.
> -		 */
> -		if (dep->namelen == args->namelen &&
> -		    dep->name[0] == args->name[0] &&
> -		    memcmp(dep->name, args->name, args->namelen) == 0) {
> +		 * Compare, if it's right give back buffer & entry number:
> +		 *
> +		 * lookup case - use nameops;
> +		 *
> +		 * replace/remove case - as lookup has been already been
> +		 * performed, look for an exact match using the fast method
> +		 */
> +		cmp = args->oknoent ?
> +			xfs_dir_compname(dp, dep->name, dep->namelen,
> +						args->name, args->namelen) :
> +			xfs_default_compname(dp, dep->name, dep->namelen,
> +						args->name, args->namelen);
> +		if (cmp != XFS_CMP_DIFFERENT && cmp != args->cmpresult) {
> +			args->cmpresult = cmp;
>  			*bpp = bp;
>  			*entno = mid;
> +			if (cmp == XFS_CMP_EXACT)
>  			return 0;
>  		}
> -	} while (++mid < be32_to_cpu(btp->count) && be32_to_cpu(blp[mid].hashval) == hash);
> +	} while (++mid < be32_to_cpu(btp->count) &&
> +			be32_to_cpu(blp[mid].hashval) == hash);

thanks... can you trim down the other > 80 char lines you've added, too? :)

> @@ -1300,10 +1313,17 @@ xfs_dir2_leaf_lookup(
>  	/*
>  	 * Return the found inode number.
>  	 */
> +	error = EEXIST;
>  	args->inumber = be64_to_cpu(dep->inumber);
> +	if (args->cmpresult == XFS_CMP_CASE) {
> +		args->valuelen = xfs_unicode_to_nls(args->dp->i_mount->m_nls,
> +				dep->name, dep->namelen, (char **)&args->value);
> +		if (args->valuelen < 0)
> +			error = -args->valuelen;

hm, error signs... new functions return negative, whereas old xfs code
returns positive errors?

...

> @@ -1391,19 +1413,31 @@ xfs_dir2_leaf_lookup_int(
>  		       xfs_dir2_dataptr_to_off(mp, be32_to_cpu(lep->address)));
>  		/*
>  		 * If it matches then return it.
> -		 */
> -		if (dep->namelen == args->namelen &&
> -		    dep->name[0] == args->name[0] &&
> -		    memcmp(dep->name, args->name, args->namelen) == 0) {
> +		 *
> +		 * lookup case - use nameops;
> +		 *
> +		 * replace/remove case - as lookup has been already been
> +		 * performed, look for an exact match using the fast method
> +		 */
> +		cmp = args->oknoent ?
> +			xfs_dir_compname(dp, dep->name, dep->namelen,
> +						args->name, args->namelen) :
> +			xfs_default_compname(dp, dep->name, dep->namelen,
> +						args->name, args->namelen);
> +		if (cmp != XFS_CMP_DIFFERENT && cmp != args->cmpresult) {
> +			args->cmpresult = cmp;
>  			*dbpp = dbp;
>  			*indexp = index;
> +			if (cmp == XFS_CMP_EXACT)
>  			return 0;

tab missing?


> ===========================================================================
> fs/xfs/xfs_dir2_node.c
> ===========================================================================
> 
> --- a/fs/xfs/xfs_dir2_node.c	2008-01-18 15:31:25.000000000 +1100
> +++ b/fs/xfs/xfs_dir2_node.c	2007-10-31 12:32:04.060201390 +1100
...

> @@ -572,28 +575,34 @@ xfs_dir2_leafn_lookup_int(
>  			/*
>  			 * Point to the data entry.
>  			 */
> -			dep = (xfs_dir2_data_entry_t *)
> -			      ((char *)curbp->data +
> +			dep = (xfs_dir2_data_entry_t *)((char *)curbp->data +
>  			       xfs_dir2_dataptr_to_off(mp, be32_to_cpu(lep->address)));
>  			/*
>  			 * Compare the entry, return it if it matches.
>  			 */
> -			if (dep->namelen == args->namelen &&
> -			    dep->name[0] == args->name[0] &&
> -			    memcmp(dep->name, args->name, args->namelen) == 0) {
> +			cmp = args->oknoent ?
> +				xfs_dir_compname(dp, dep->name, dep->namelen,
> +						args->name, args->namelen):
> +				xfs_default_compname(dp, dep->name, dep->namelen,
> +						args->name, args->namelen);
> +			if (cmp != XFS_CMP_DIFFERENT &&
> +					cmp != args->cmpresult) {
> +				args->cmpresult = cmp;
>  				args->inumber = be64_to_cpu(dep->inumber);
>  				*indexp = index;
>  				state->extravalid = 1;
>  				state->extrablk.bp = curbp;
>  				state->extrablk.blkno = curdb;
> -				state->extrablk.index =
> -					(int)((char *)dep -
> +				state->extrablk.index = (int)((char *)dep -
>  					      (char *)curbp->data);
>  				state->extrablk.magic = XFS_DIR2_DATA_MAGIC;
> +				if (cmp == XFS_CMP_EXACT)
>  				return XFS_ERROR(EEXIST);

tab...  Hmm wonder if I caught all of these...

> ===========================================================================
> fs/xfs/xfs_dir2_sf.c
> ===========================================================================
> 
> --- a/fs/xfs/xfs_dir2_sf.c	2008-01-18 15:31:25.000000000 +1100
> +++ b/fs/xfs/xfs_dir2_sf.c	2008-01-17 12:25:01.552398622 +1100
> @@ -38,6 +38,7 @@
>  #include "xfs_dir2_leaf.h"
>  #include "xfs_dir2_block.h"
>  #include "xfs_dir2_trace.h"
> +#include "xfs_unicode.h"
>  
>  /*
>   * Prototypes for internal functions.
> @@ -708,6 +709,8 @@ xfs_dir2_sf_getdents(
>  	xfs_dir2_dataptr_t	dot_offset;
>  	xfs_dir2_dataptr_t	dotdot_offset;
>  	xfs_ino_t		ino;
> +	char			*nls_name = NULL; /* NLS name buffer */
> +	int			nls_namelen = 0;
>  
>  	mp = dp->i_mount;
>  
> @@ -772,6 +775,9 @@ xfs_dir2_sf_getdents(
>  		}
>  	}
>  
> +	if (mp->m_nls)
> +		nls_name = xfs_alloc_unicode_nls_name();
> +
>  	/*
>  	 * Loop while there are more entries and put'ing works.
>  	 */
> @@ -789,16 +795,22 @@ xfs_dir2_sf_getdents(
>  #if XFS_BIG_INUMS
>  		ino += mp->m_inoadd;
>  #endif
> -
> -		if (filldir(dirent, sfep->name, sfep->namelen,
> +		if (mp->m_nls)
> +			nls_namelen = xfs_unicode_to_nls(mp->m_nls, sfep->name,
> +					sfep->namelen, &nls_name);
> +		if (filldir(dirent,
> +				nls_namelen > 0 ? nls_name : (char *)sfep->name,
> +				nls_namelen > 0 ? nls_namelen : sfep->namelen,
>  					    off, ino, DT_UNKNOWN)) {

Hmm we've seen the foo > 0 ? bar : baz stuff a few times, should this
get a helper?

...

> @@ -844,23 +857,43 @@ xfs_dir2_sf_lookup(
>  	if (args->namelen == 2 &&
>  	    args->name[0] == '.' && args->name[1] == '.') {
>  		args->inumber = xfs_dir2_sf_get_inumber(sfp, &sfp->hdr.parent);
> +		args->cmpresult = XFS_CMP_EXACT;
>  		return XFS_ERROR(EEXIST);
>  	}
>  	/*
>  	 * Loop over all the entries trying to match ours.
>  	 */
> -	for (i = 0, sfep = xfs_dir2_sf_firstentry(sfp);
> -	     i < sfp->hdr.count;
> +	args->cmpresult = XFS_CMP_DIFFERENT;
> +	for (i = 0, sfep = xfs_dir2_sf_firstentry(sfp); i < sfp->hdr.count;
>  	     i++, sfep = xfs_dir2_sf_nextentry(sfp, sfep)) {
> -		if (sfep->namelen == args->namelen &&
> -		    sfep->name[0] == args->name[0] &&
> -		    memcmp(args->name, sfep->name, args->namelen) == 0) {
> -			args->inumber =
> -				xfs_dir2_sf_get_inumber(sfp,
> +		switch (xfs_dir_compname(dp, sfep->name, sfep->namelen,
> +				args->name, args->namelen)) {
> +		case XFS_CMP_EXACT:
> +			args->cmpresult = XFS_CMP_EXACT;
> +			args->inumber = xfs_dir2_sf_get_inumber(sfp,
>  					xfs_dir2_sf_inumberp(sfep));
> +			if (args->value) {
> +				xfs_free_unicode_nls_name(args->value);
> +				args->value = NULL;
> +			}
>  			return XFS_ERROR(EEXIST);
> +
> +		case XFS_CMP_CASE:
> +			if (!args->value) {
> +				args->valuelen = xfs_unicode_to_nls(
> +					args->dp->i_mount->m_nls, sfep->name,
> +					sfep->namelen, (char **)&args->value);
> +				if (args->valuelen < 0)
> +					return XFS_ERROR(-args->valuelen);
> +				args->cmpresult = XFS_CMP_CASE;
> +				args->inumber = xfs_dir2_sf_get_inumber(sfp,
> +						xfs_dir2_sf_inumberp(sfep));
> +			}
> +		default: ;

Hmm that's a little funky, to my eyes anyway.

...

> ===========================================================================
> fs/xfs/xfs_mount.c
> ===========================================================================
> 
> --- a/fs/xfs/xfs_mount.c	2008-01-18 15:31:25.000000000 +1100
> +++ b/fs/xfs/xfs_mount.c	2008-01-17 17:10:29.777728874 +1100
> @@ -25,6 +25,7 @@
>  #include "xfs_sb.h"
>  #include "xfs_ag.h"
>  #include "xfs_dir2.h"
> +#include "xfs_attr.h"
>  #include "xfs_dmapi.h"
>  #include "xfs_mount.h"
>  #include "xfs_bmap_btree.h"
> @@ -43,6 +44,9 @@
>  #include "xfs_rw.h"
>  #include "xfs_quota.h"
>  #include "xfs_fsops.h"
> +#include "xfs_da_btree.h"
> +#include "xfs_attr_leaf.h"
> +#include "xfs_unicode.h"
>  
>  STATIC void	xfs_mount_log_sbunit(xfs_mount_t *, __int64_t);
>  STATIC int	xfs_uuid_mount(xfs_mount_t *);
> @@ -119,6 +123,8 @@ static const struct {
>      { offsetof(xfs_sb_t, sb_logsectsize),0 },
>      { offsetof(xfs_sb_t, sb_logsunit),	 0 },
>      { offsetof(xfs_sb_t, sb_features2),	 0 },
> +    { offsetof(xfs_sb_t, sb_bad_features2), 0 },

bad_features2?  what's this and what does it have to do w/ CI, and why
is it set but never used in xfs_sb_from_disk?

if features2 "could be here" shouldn't we be doing something with that?
 This could do with comments at least, somewhere.

...

> @@ -1165,6 +1176,17 @@ xfs_mountfs(
>  	}
>  
>  	/*
> +	 * Load in unicode case folding table from disk
> +	 */
> +	if (xfs_sb_version_hasunicode(&mp->m_sb)) {
> +		error = xfs_unicode_read_cft(mp);
> +		if (error) {
> +			cmn_err(CE_WARN, "XFS: failed to read case folding table");

80+ chars...

..

> ===========================================================================
> fs/xfs/xfs_sb.h
> ===========================================================================
> 
> --- a/fs/xfs/xfs_sb.h	2008-01-18 15:31:25.000000000 +1100
> +++ b/fs/xfs/xfs_sb.h	2007-10-23 16:55:47.440178601 +1000
> @@ -46,10 +46,12 @@ struct xfs_mount;
>  #define XFS_SB_VERSION_SECTORBIT	0x0800
>  #define	XFS_SB_VERSION_EXTFLGBIT	0x1000
>  #define	XFS_SB_VERSION_DIRV2BIT		0x2000
> +#define XFS_SB_VERSION_OLDCIBIT		0x4000
>  #define	XFS_SB_VERSION_MOREBITSBIT	0x8000
>  #define	XFS_SB_VERSION_OKSASHFBITS	\
>  	(XFS_SB_VERSION_EXTFLGBIT | \
> -	 XFS_SB_VERSION_DIRV2BIT)
> +	 XFS_SB_VERSION_DIRV2BIT | \
> +	 XFS_SB_VERSION_OLDCIBIT)

there's that oldcibit again...

>  #define	XFS_SB_VERSION_OKREALFBITS	\
>  	(XFS_SB_VERSION_ATTRBIT | \
>  	 XFS_SB_VERSION_NLINKBIT | \
> @@ -77,10 +79,12 @@ struct xfs_mount;
>  #define XFS_SB_VERSION2_LAZYSBCOUNTBIT	0x00000002	/* Superblk counters */
>  #define XFS_SB_VERSION2_RESERVED4BIT	0x00000004
>  #define XFS_SB_VERSION2_ATTR2BIT	0x00000008	/* Inline attr rework */
> +#define XFS_SB_VERSION2_UNICODEBIT	0x00000020	/* Unicode names */
>  
>  #define	XFS_SB_VERSION2_OKREALFBITS	\
>  	(XFS_SB_VERSION2_LAZYSBCOUNTBIT	| \
> -	 XFS_SB_VERSION2_ATTR2BIT)
> +	 XFS_SB_VERSION2_ATTR2BIT | \
> +	 XFS_SB_VERSION2_UNICODEBIT)
>  #define	XFS_SB_VERSION2_OKSASHFBITS	\
>  	(0)
>  #define XFS_SB_VERSION2_OKREALBITS	\
> @@ -145,6 +149,9 @@ typedef struct xfs_sb {
>  	__uint16_t	sb_logsectsize;	/* sector size for the log, bytes */
>  	__uint32_t	sb_logsunit;	/* stripe unit size for the log */
>  	__uint32_t	sb_features2;	/* additional feature bits */
> +	__uint32_t	sb_bad_features2; /* features2 could be here */

Ok, so...?

...

> @@ -463,6 +475,12 @@ static inline int xfs_sb_version_hassect
>  		((sbp)->sb_versionnum & XFS_SB_VERSION_SECTORBIT);
>  }
>  
> +static inline int xfs_sb_version_hasoldci(xfs_sb_t *sbp)
> +{
> +	return (XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_4) && \
> +		((sbp)->sb_versionnum & XFS_SB_VERSION_OLDCIBIT);
> +}

You forgot the uppercase indirection macro!   ;)

...


> ===========================================================================
> fs/xfs/xfs_unicode.c
> ===========================================================================
...

> +
> +char *
> +xfs_alloc_unicode_nls_name(void)
> +{
> +	return kmem_zone_alloc(xfs_nls_uni_zone, KM_SLEEP);
> +}

Why wrap/hide this?

> +
> +
> +void
> +xfs_free_unicode_nls_name(
> +	char		*name)
> +{
> +	kmem_zone_free(xfs_nls_uni_zone, name);
> +}

Or this?  Eh, maybe it's handy.

> +int
> +xfs_nls_to_unicode(
> +	struct nls_table *nls,
> +	const char	*nls_name,
> +	int		nls_namelen,
> +	char		**uni_name)
> +{
> +	char		*n;
> +	int		i, o;
> +	wchar_t		uc;
> +	int		nlen;
> +	int		u8len;
> +	int		rval;
> +
> +	n = *uni_name ? *uni_name : xfs_alloc_unicode_nls_name();
> +
> +	if (!nls) {
> +		if (nls_namelen > MAXNAMELEN) {
> +			rval = -ENAMETOOLONG;

The rest of core xfs code returns positive errors; why the shift in this
file?  Well, I guess because you want to return a length, but this
strikes me as a bit inconsistent... we've been burned by getting error
signs wrong in the past, this looks like an exception to the existing
sign conventions

...


> +int
> +xfs_unicode_validate(
> +	const uchar_t	*name,
> +	int		namelen)
> +{
> +	wchar_t		uc;
> +	int		i, nlen;
> +
> +	for (i = 0; i < namelen; i += nlen) {
> +		if (*name >= 0xf0) {
> +			cmn_err(CE_WARN, "xfs_unicode_validate: "
> +					"UTF-8 char beyond U+FFFF\n");
> +			return -EINVAL;
> +		}
> +		/* utf8_mbtowc must fail on overlong sequences too */
> +		nlen = utf8_mbtowc(&uc, name + i, namelen - i);
> +		if (nlen < 0) {
> +			cmn_err(CE_WARN, "xfs_unicode_validate: "
> +					"invalid UTF-8 sequence\n");
> +			return -EILSEQ;
> +		}
> +		/* check for invalid/surrogate/private unicode chars */
> +		if (uc >= 0xfffe || (uc >= 0xd800 && uc <= 0xf8ff)) {
> +			cmn_err(CE_WARN, "xfs_unicode_validate: "
> +					"unsupported UTF-8 char\n");
> +			return -EINVAL;
> +		}
> +	}
> +	return 0;
> +}

and now this leads to stuff like:

                        rval = xfs_unicode_validate(name, namelen);
                        if (rval < 0)
                                return -rval;

... this looks odd to me.

...

> +xfs_unicode_read_cft(
> +	xfs_mount_t	*mp)
> +{
> +	int		error;
> +	xfs_inode_t	*cftip;
> +	int		size;
> +	int		nfsb;
> +	int             nmap;
> +	xfs_bmbt_irec_t *mapp;
> +	int		n;
> +	int		byte_cnt;
> +	xfs_buf_t	*bp;
> +	char		*table;
> +	xfs_dcft_t	*dcft;
> +
> +	if (mp->m_sb.sb_cftino == NULLFSINO || mp->m_sb.sb_cftino == 0)
> +		return EINVAL;

oh, here it's positive? :)

...

> +void
> +xfs_unicode_free_cft(
> +	const xfs_cft_t	*cft)
> +{
> +	remove_cft(cft);
> +}

why the wrapper?

> +void
> +xfs_unicode_init(void)
> +{
> +	mutex_init(&cft_lock);
> +	xfs_nls_uni_zone = kmem_zone_init(MAXNAMELEN, "xfs_nls_uni");
> +}

Hm, no corresponding mutex_destroy

> +void
> +xfs_unicode_uninit(void)
> +{
> +	int		i;
> +
> +	mutex_lock(&cft_lock);
> +
> +	for (i = 0; i < cft_size; i++) {
> +		ASSERT(cft_list[i].refcount == 0);
> +		vfree(cft_list[i].table);
> +	}
> +	kmem_free(cft_list, cft_size * sizeof(struct cft_item));
> +	cft_size = 0;
> +	cft_list = NULL;
> +
> +	mutex_unlock(&cft_lock);

destroy mutex here too just for tidiness

> +	kmem_zone_destroy(xfs_nls_uni_zone);
> +}
> 

...

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [REVIEW 1/2] Case insensitive support for XFS - kernel patch
  2008-01-18  4:43 [REVIEW 1/2] Case insensitive support for XFS - kernel patch Barry Naujok
  2008-01-19  5:22 ` Eric Sandeen
@ 2008-01-19  5:30 ` Eric Sandeen
  1 sibling, 0 replies; 5+ messages in thread
From: Eric Sandeen @ 2008-01-19  5:30 UTC (permalink / raw)
  To: Barry Naujok; +Cc: xfs@oss.sgi.com, xfs-dev

Barry Naujok wrote:
> This patch should apply to 2.6.24-rc6.

Just FYI, on the stack picture, of the bigger stack users these are the
deltas on my x86_64 box:

-xfs_bulkstat 280
+xfs_bulkstat 296   :(

-xfs_dir2_leaf_getdents 136
+xfs_dir2_leaf_getdents 200

-xfs_dir2_sf_to_block 120
+xfs_dir2_sf_to_block 136

-xfs_dir_canenter 144
+xfs_dir_canenter 152

-xfs_dir_createname 152
+xfs_dir_createname 168

-xfs_dir_lookup 152
+xfs_dir_lookup 168

-xfs_dir_replace 152
+xfs_dir_replace 168

-Eric

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [REVIEW 1/2] Case insensitive support for XFS - kernel patch
  2008-01-19  5:22 ` Eric Sandeen
@ 2008-01-21  3:53   ` Barry Naujok
  2008-01-23  6:55     ` Christoph Hellwig
  0 siblings, 1 reply; 5+ messages in thread
From: Barry Naujok @ 2008-01-21  3:53 UTC (permalink / raw)
  To: Eric Sandeen; +Cc: xfs@oss.sgi.com, xfs-dev

[-- Attachment #1: Type: text/plain, Size: 392 bytes --]

On Sat, 19 Jan 2008 16:22:47 +1100, Eric Sandeen <sandeen@sandeen.net>  
wrote:

> Lots of comments below;

I have addressed the "real" ones :)

- xfs_unicode.c functions do not return negative error codes anymore.
- Fixed up the indenting in the patch.
- Added a few comments for things that weren't clear.
- Made sure my changes didn't exceed 80 char lines.

No sub-patches at this stage.


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: ci_kernel.patch --]
[-- Type: text/x-patch; name=ci_kernel.patch, Size: 101860 bytes --]


===========================================================================
fs/xfs/Makefile
===========================================================================

--- a/fs/xfs/Makefile	2008-01-21 14:42:50.000000000 +1100
+++ b/fs/xfs/Makefile	2007-10-23 16:17:22.173903950 +1000
@@ -74,6 +74,7 @@ xfs-y				+= xfs_alloc.o \
 				   xfs_trans_extfree.o \
 				   xfs_trans_inode.o \
 				   xfs_trans_item.o \
+				   xfs_unicode.o \
 				   xfs_utils.o \
 				   xfs_vfsops.o \
 				   xfs_vnodeops.o \
@@ -97,7 +98,7 @@ xfs-y				+= $(addprefix $(XFS_LINUX)/, \
 				   xfs_lrw.o \
 				   xfs_super.o \
 				   xfs_vnode.o \
-				   xfs_ksyms.o) 
+				   xfs_ksyms.o)
 
 # Objects in support/
 xfs-y				+= $(addprefix support/, \

===========================================================================
fs/xfs/linux-2.6/xfs_export.c
===========================================================================

--- a/fs/xfs/linux-2.6/xfs_export.c	2008-01-21 14:42:50.000000000 +1100
+++ b/fs/xfs/linux-2.6/xfs_export.c	2008-01-11 14:20:10.000000000 +1100
@@ -217,7 +217,7 @@ xfs_fs_get_parent(
 	struct dentry		*parent;
 
 	cvp = NULL;
-	error = xfs_lookup(XFS_I(child->d_inode), &dotdot, &cvp);
+	error = xfs_lookup(XFS_I(child->d_inode), &dotdot, &cvp, NULL, NULL);
 	if (unlikely(error))
 		return ERR_PTR(-error);
 

===========================================================================
fs/xfs/linux-2.6/xfs_iops.c
===========================================================================

--- a/fs/xfs/linux-2.6/xfs_iops.c	2008-01-21 14:42:50.000000000 +1100
+++ b/fs/xfs/linux-2.6/xfs_iops.c	2008-01-21 14:27:33.000000000 +1100
@@ -47,6 +47,8 @@
 #include "xfs_buf_item.h"
 #include "xfs_utils.h"
 #include "xfs_vnodeops.h"
+#include "xfs_da_btree.h"
+#include "xfs_unicode.h"
 
 #include <linux/capability.h>
 #include <linux/xattr.h>
@@ -388,7 +390,7 @@ xfs_vn_lookup(
 	if (dentry->d_name.len >= MAXNAMELEN)
 		return ERR_PTR(-ENAMETOOLONG);
 
-	error = xfs_lookup(XFS_I(dir), dentry, &cvp);
+	error = xfs_lookup(XFS_I(dir), dentry, &cvp, NULL, NULL);
 	if (unlikely(error)) {
 		if (unlikely(error != ENOENT))
 			return ERR_PTR(-error);
@@ -399,6 +401,113 @@ xfs_vn_lookup(
 	return d_splice_alias(vn_to_inode(cvp), dentry);
 }
 
+STATIC struct dentry *
+xfs_vn_ci_lookup(
+	struct inode	*dir,
+	struct dentry	*dentry,
+	struct nameidata *nd)
+{
+	bhv_vnode_t	*cvp;
+	int		error;
+	struct dentry	*result;
+	struct qstr	actual_name;
+	struct inode	*inode;
+
+	if (dentry->d_name.len >= MAXNAMELEN)
+		return ERR_PTR(-ENAMETOOLONG);
+
+	error = xfs_lookup(XFS_I(dir), dentry, &cvp, (char **)&actual_name.name,
+			&actual_name.len);
+	if (unlikely(error)) {
+		if (unlikely(error != ENOENT))
+			return ERR_PTR(-error);
+		d_add(dentry, NULL);
+		return NULL;
+	}
+	inode = vn_to_inode(cvp);
+
+	/* if exact match, just splice and exit */
+	if (!actual_name.name) {
+		result = d_splice_alias(inode, dentry);
+		return result;
+	}
+
+	/*
+	 * case-insensitive match, create a dentry to return and fill it
+	 * in with the correctly cased name. Parameter "dentry" is not
+	 * used anymore and the caller will free it.
+	 * Derived from fs/ntfs/namei.c
+	 */
+
+	actual_name.hash = full_name_hash(actual_name.name, actual_name.len);
+
+	/* Does an existing dentry match? */
+	result = d_lookup(dentry->d_parent, &actual_name);
+	if (!result) {
+		/* if not, create one */
+		result = d_alloc(dentry->d_parent, &actual_name);
+		xfs_free_unicode_nls_name((char *)actual_name.name);
+		if (!result)
+			return ERR_PTR(-ENOMEM);
+		dentry = d_splice_alias(inode, result);
+		if (dentry) {
+			dput(result);
+			return dentry;
+		}
+		return result;
+	}
+	xfs_free_unicode_nls_name((char *)actual_name.name);
+
+	/* an existing dentry matches, use it */
+
+	if (result->d_inode) {
+		/*
+		 * already an inode attached, deref the inode that was
+		 * refcounted with xfs_lookup and return the dentry.
+		 */
+		if (unlikely(result->d_inode != inode)) {
+			/* This can happen because bad inodes are unhashed. */
+			BUG_ON(!is_bad_inode(inode));
+			BUG_ON(!is_bad_inode(result->d_inode));
+		}
+		iput(inode);
+		return result;
+	}
+
+	if (!S_ISDIR(inode->i_mode)) {
+		/* not a directory, easy to handle */
+		d_instantiate(result, inode);
+		return result;
+	}
+
+	spin_lock(&dcache_lock);
+	if (list_empty(&inode->i_dentry)) {
+		/*
+		 * Directory without a 'disconnected' dentry; we need to do
+		 * d_instantiate() by hand because it takes dcache_lock which
+		 * we already hold.
+		 */
+		list_add(&result->d_alias, &inode->i_dentry);
+		result->d_inode = inode;
+		spin_unlock(&dcache_lock);
+		security_d_instantiate(result, inode);
+		return result;
+	}
+	/*
+	 * Directory with a 'disconnected' dentry; get a reference to the
+	 * 'disconnected' dentry.
+	 */
+	dentry = list_entry(inode->i_dentry.next, struct dentry, d_alias);
+	dget_locked(dentry);
+	spin_unlock(&dcache_lock);
+	security_d_instantiate(result, inode);
+	d_move(dentry, result);
+	iput(inode);
+	dput(result);
+	return dentry;
+}
+
+
 STATIC int
 xfs_vn_link(
 	struct dentry	*old_dentry,
@@ -887,6 +996,25 @@ const struct inode_operations xfs_dir_in
 	.removexattr		= xfs_vn_removexattr,
 };
 
+const struct inode_operations xfs_dir_ci_inode_operations = {
+	.create			= xfs_vn_create,
+	.lookup			= xfs_vn_ci_lookup,
+	.link			= xfs_vn_link,
+	.unlink			= xfs_vn_unlink,
+	.symlink		= xfs_vn_symlink,
+	.mkdir			= xfs_vn_mkdir,
+	.rmdir			= xfs_vn_rmdir,
+	.mknod			= xfs_vn_mknod,
+	.rename			= xfs_vn_rename,
+	.permission		= xfs_vn_permission,
+	.getattr		= xfs_vn_getattr,
+	.setattr		= xfs_vn_setattr,
+	.setxattr		= xfs_vn_setxattr,
+	.getxattr		= xfs_vn_getxattr,
+	.listxattr		= xfs_vn_listxattr,
+	.removexattr		= xfs_vn_removexattr,
+};
+
 const struct inode_operations xfs_symlink_inode_operations = {
 	.readlink		= generic_readlink,
 	.follow_link		= xfs_vn_follow_link,

===========================================================================
fs/xfs/linux-2.6/xfs_iops.h
===========================================================================

--- a/fs/xfs/linux-2.6/xfs_iops.h	2008-01-21 14:42:50.000000000 +1100
+++ b/fs/xfs/linux-2.6/xfs_iops.h	2007-10-26 13:19:11.702517171 +1000
@@ -20,6 +20,7 @@
 
 extern const struct inode_operations xfs_inode_operations;
 extern const struct inode_operations xfs_dir_inode_operations;
+extern const struct inode_operations xfs_dir_ci_inode_operations;
 extern const struct inode_operations xfs_symlink_inode_operations;
 
 extern const struct file_operations xfs_file_operations;

===========================================================================
fs/xfs/linux-2.6/xfs_ksyms.c
===========================================================================

--- a/fs/xfs/linux-2.6/xfs_ksyms.c	2008-01-21 14:42:50.000000000 +1100
+++ b/fs/xfs/linux-2.6/xfs_ksyms.c	2008-01-21 14:27:33.000000000 +1100
@@ -157,6 +157,7 @@ EXPORT_SYMBOL(kmem_zone_init);
 EXPORT_SYMBOL(kmem_zone_zalloc);
 EXPORT_SYMBOL(xfs_address_space_operations);
 EXPORT_SYMBOL(xfs_dir_inode_operations);
+EXPORT_SYMBOL(xfs_dir_ci_inode_operations);
 EXPORT_SYMBOL(xfs_dir_file_operations);
 EXPORT_SYMBOL(xfs_inode_operations);
 EXPORT_SYMBOL(xfs_file_operations);

===========================================================================
fs/xfs/linux-2.6/xfs_linux.h
===========================================================================

--- a/fs/xfs/linux-2.6/xfs_linux.h	2008-01-21 14:42:50.000000000 +1100
+++ b/fs/xfs/linux-2.6/xfs_linux.h	2008-01-11 14:49:16.537591564 +1100
@@ -75,6 +75,8 @@
 #include <linux/delay.h>
 #include <linux/log2.h>
 #include <linux/spinlock.h>
+#include <linux/ctype.h>
+#include <linux/nls.h>
 
 #include <asm/page.h>
 #include <asm/div64.h>
@@ -180,6 +182,12 @@
 #define howmany(x, y)	(((x)+((y)-1))/(y))
 
 /*
+ * NLS UTF-8 character set
+ */
+
+#define XFS_NLS_UTF8	"utf8"
+
+/*
  * Various platform dependent calls that don't fit anywhere else
  */
 #define xfs_sort(a,n,s,fn)	sort(a,n,s,fn,NULL)

===========================================================================
fs/xfs/linux-2.6/xfs_super.c
===========================================================================

--- a/fs/xfs/linux-2.6/xfs_super.c	2008-01-21 14:42:50.000000000 +1100
+++ b/fs/xfs/linux-2.6/xfs_super.c	2008-01-21 14:27:33.000000000 +1100
@@ -50,6 +50,7 @@
 #include "xfs_vnodeops.h"
 #include "xfs_vfsops.h"
 #include "xfs_version.h"
+#include "xfs_unicode.h"
 #include "xfs_log_priv.h"
 #include "xfs_trans_priv.h"
 
@@ -122,6 +123,9 @@ xfs_args_allocate(
 #define MNTOPT_ATTR2	"attr2"		/* do use attr2 attribute format */
 #define MNTOPT_NOATTR2	"noattr2"	/* do not use attr2 attribute format */
 #define MNTOPT_FILESTREAM  "filestreams" /* use filestreams allocator */
+#define MNTOPT_NLS	"nls"		/* NLS code page to use */
+#define MNTOPT_CILOOKUP	"ci"		/* case-insensitive dir names */
+#define MNTOPT_CIATTR	"ciattr"	/* case-insensitive attr names */
 #define MNTOPT_QUOTA	"quota"		/* disk quotas (user) */
 #define MNTOPT_NOQUOTA	"noquota"	/* no quotas */
 #define MNTOPT_USRQUOTA	"usrquota"	/* user quota enabled */
@@ -316,6 +320,18 @@ xfs_parseargs(
 			args->flags &= ~XFSMNT_ATTR2;
 		} else if (!strcmp(this_char, MNTOPT_FILESTREAM)) {
 			args->flags2 |= XFSMNT2_FILESTREAMS;
+		} else if (!strcmp(this_char, MNTOPT_NLS)) {
+			if (!value || !*value) {
+				cmn_err(CE_WARN,
+					"XFS: %s option requires an argument",
+					this_char);
+				return EINVAL;
+			}
+			strncpy(args->nls, value, MAXNAMELEN);
+		} else if (!strcmp(this_char, MNTOPT_CILOOKUP)) {
+			args->flags2 |= XFSMNT2_CILOOKUP;
+		} else if (!strcmp(this_char, MNTOPT_CIATTR)) {
+			args->flags2 |= XFSMNT2_CIATTR;
 		} else if (!strcmp(this_char, MNTOPT_NOQUOTA)) {
 			args->flags &= ~(XFSMNT_UQUOTAENF|XFSMNT_UQUOTA);
 			args->flags &= ~(XFSMNT_GQUOTAENF|XFSMNT_GQUOTA);
@@ -455,6 +471,8 @@ xfs_showargs(
 		{ XFS_MOUNT_OSYNCISOSYNC,	"," MNTOPT_OSYNCISOSYNC },
 		{ XFS_MOUNT_ATTR2,		"," MNTOPT_ATTR2 },
 		{ XFS_MOUNT_FILESTREAMS,	"," MNTOPT_FILESTREAM },
+		{ XFS_MOUNT_CI_LOOKUP,		"," MNTOPT_CILOOKUP },
+		{ XFS_MOUNT_CI_ATTR,		"," MNTOPT_CIATTR },
 		{ XFS_MOUNT_DMAPI,		"," MNTOPT_DMAPI },
 		{ XFS_MOUNT_GRPID,		"," MNTOPT_GRPID },
 		{ 0, NULL }
@@ -517,6 +535,13 @@ xfs_showargs(
 	if (!(mp->m_qflags & XFS_ALL_QUOTA_ACCT))
 		seq_puts(m, "," MNTOPT_NOQUOTA);
 
+	if (xfs_sb_version_hasunicode(&mp->m_sb)) {
+		if (mp->m_nls)
+			seq_printf(m, "," MNTOPT_NLS "=%s", mp->m_nls->charset);
+		else
+			seq_puts(m, "," MNTOPT_NLS "=" XFS_NLS_UTF8);
+	}
+
 	return 0;
 }
 __uint64_t
@@ -564,7 +589,11 @@ xfs_set_inodeops(
 		inode->i_mapping->a_ops = &xfs_address_space_operations;
 		break;
 	case S_IFDIR:
-		inode->i_op = &xfs_dir_inode_operations;
+		inode->i_op =
+			xfs_sb_version_hasoldci(&XFS_I(inode)->i_mount->m_sb) ||
+			(XFS_I(inode)->i_mount->m_flags & XFS_MOUNT_CI_LOOKUP) ?
+				&xfs_dir_ci_inode_operations :
+				&xfs_dir_inode_operations;
 		inode->i_fop = &xfs_dir_file_operations;
 		break;
 	case S_IFLNK:

===========================================================================
fs/xfs/xfs_attr.c
===========================================================================

--- a/fs/xfs/xfs_attr.c	2008-01-21 14:42:50.000000000 +1100
+++ b/fs/xfs/xfs_attr.c	2008-01-18 13:25:20.068339942 +1100
@@ -106,6 +106,17 @@ ktrace_t *xfs_attr_trace_buf;
  * Overall external interface routines.
  *========================================================================*/
 
+void
+xfs_attr_mount(struct xfs_mount *mp)
+{
+	mp->m_attr_magicpct = (mp->m_sb.sb_blocksize * 37) / 100;
+	if (xfs_sb_version_hasunicode(&mp->m_sb)) {
+		mp->m_attrnameops = (mp->m_flags & XFS_MOUNT_CI_ATTR) ?
+			&xfs_unicode_ci_nameops : &xfs_unicode_nameops;
+	} else
+		mp->m_attrnameops = &xfs_default_nameops;
+}
+
 int
 xfs_attr_fetch(xfs_inode_t *ip, const char *name, int namelen,
 	       char *value, int *valuelenp, int flags, struct cred *cred)
@@ -122,14 +133,14 @@ xfs_attr_fetch(xfs_inode_t *ip, const ch
 	 * Fill in the arg structure for this request.
 	 */
 	memset((char *)&args, 0, sizeof(args));
-	args.name = name;
-	args.namelen = namelen;
 	args.value = value;
 	args.valuelen = *valuelenp;
 	args.flags = flags;
-	args.hashval = xfs_da_hashname(args.name, args.namelen);
 	args.dp = ip;
 	args.whichfork = XFS_ATTR_FORK;
+	error = xfs_da_setup_name_and_hash(&args, name, namelen);
+	if (error)
+		return error;
 
 	/*
 	 * Decide on what work routines to call based on the inode size.
@@ -153,6 +164,7 @@ xfs_attr_fetch(xfs_inode_t *ip, const ch
 
 	if (error == EEXIST)
 		error = 0;
+	xfs_da_cleanup_name(&args, name);
 	return(error);
 }
 
@@ -181,6 +193,7 @@ xfs_attr_get(
 	xfs_ilock(ip, XFS_ILOCK_SHARED);
 	error = xfs_attr_fetch(ip, name, namelen, value, valuelenp, flags, cred);
 	xfs_iunlock(ip, XFS_ILOCK_SHARED);
+
 	return(error);
 }
 
@@ -219,18 +232,18 @@ xfs_attr_set_int(xfs_inode_t *dp, const 
 	 * Fill in the arg structure for this request.
 	 */
 	memset((char *)&args, 0, sizeof(args));
-	args.name = name;
-	args.namelen = namelen;
 	args.value = value;
 	args.valuelen = valuelen;
 	args.flags = flags;
-	args.hashval = xfs_da_hashname(args.name, args.namelen);
 	args.dp = dp;
 	args.firstblock = &firstblock;
 	args.flist = &flist;
 	args.whichfork = XFS_ATTR_FORK;
 	args.addname = 1;
 	args.oknoent = 1;
+	error = xfs_da_setup_name_and_hash(&args, name, namelen);
+	if (error)
+		return error;
 
 	/*
 	 * Determine space new attribute will use, and if it would be
@@ -282,6 +295,7 @@ xfs_attr_set_int(xfs_inode_t *dp, const 
 				      0, XFS_TRANS_PERM_LOG_RES,
 				      XFS_ATTRSET_LOG_COUNT))) {
 		xfs_trans_cancel(args.trans, 0);
+		xfs_da_cleanup_name(&args, name);
 		return(error);
 	}
 	xfs_ilock(dp, XFS_ILOCK_EXCL);
@@ -292,6 +306,7 @@ xfs_attr_set_int(xfs_inode_t *dp, const 
 	if (error) {
 		xfs_iunlock(dp, XFS_ILOCK_EXCL);
 		xfs_trans_cancel(args.trans, XFS_TRANS_RELEASE_LOG_RES);
+		xfs_da_cleanup_name(&args, name);
 		return (error);
 	}
 
@@ -342,6 +357,7 @@ xfs_attr_set_int(xfs_inode_t *dp, const 
 			if (!error && (flags & ATTR_KERNOTIME) == 0) {
 				xfs_ichgtime(dp, XFS_ICHGTIME_CHG);
 			}
+			xfs_da_cleanup_name(&args, name);
 			return(error == 0 ? err2 : error);
 		}
 
@@ -411,6 +427,7 @@ xfs_attr_set_int(xfs_inode_t *dp, const 
 		xfs_ichgtime(dp, XFS_ICHGTIME_CHG);
 	}
 
+	xfs_da_cleanup_name(&args, name);
 	return(error);
 
 out:
@@ -418,6 +435,7 @@ out:
 		xfs_trans_cancel(args.trans,
 			XFS_TRANS_RELEASE_LOG_RES|XFS_TRANS_ABORT);
 	xfs_iunlock(dp, XFS_ILOCK_EXCL);
+	xfs_da_cleanup_name(&args, name);
 	return(error);
 }
 
@@ -460,21 +478,23 @@ xfs_attr_remove_int(xfs_inode_t *dp, con
 	 * Fill in the arg structure for this request.
 	 */
 	memset((char *)&args, 0, sizeof(args));
-	args.name = name;
-	args.namelen = namelen;
 	args.flags = flags;
-	args.hashval = xfs_da_hashname(args.name, args.namelen);
 	args.dp = dp;
 	args.firstblock = &firstblock;
 	args.flist = &flist;
 	args.total = 0;
 	args.whichfork = XFS_ATTR_FORK;
+	error = xfs_da_setup_name_and_hash(&args, name, namelen);
+	if (error)
+		return error;
 
 	/*
 	 * Attach the dquots to the inode.
 	 */
-	if ((error = XFS_QM_DQATTACH(mp, dp, 0)))
+	if ((error = XFS_QM_DQATTACH(mp, dp, 0))) {
+		xfs_da_cleanup_name(&args, name);
 		return (error);
+	}
 
 	/*
 	 * Start our first transaction of the day.
@@ -502,6 +522,7 @@ xfs_attr_remove_int(xfs_inode_t *dp, con
 				      0, XFS_TRANS_PERM_LOG_RES,
 				      XFS_ATTRRM_LOG_COUNT))) {
 		xfs_trans_cancel(args.trans, 0);
+		xfs_da_cleanup_name(&args, name);
 		return(error);
 	}
 
@@ -559,6 +580,7 @@ xfs_attr_remove_int(xfs_inode_t *dp, con
 		xfs_ichgtime(dp, XFS_ICHGTIME_CHG);
 	}
 
+	xfs_da_cleanup_name(&args, name);
 	return(error);
 
 out:
@@ -566,6 +588,7 @@ out:
 		xfs_trans_cancel(args.trans,
 			XFS_TRANS_RELEASE_LOG_RES|XFS_TRANS_ABORT);
 	xfs_iunlock(dp, XFS_ILOCK_EXCL);
+	xfs_da_cleanup_name(&args, name);
 	return(error);
 }
 
@@ -634,7 +657,7 @@ xfs_attr_list_int(xfs_attr_list_context_
  */
 /*ARGSUSED*/
 STATIC int
-xfs_attr_put_listent(xfs_attr_list_context_t *context, attrnames_t *namesp,
+xfs_attr_user_list(xfs_attr_list_context_t *context, attrnames_t *namesp,
 		     char *name, int namelen,
 		     int valuelen, char *value)
 {
@@ -765,7 +788,7 @@ xfs_attr_list(
 		context.alist->al_count = 0;
 		context.alist->al_more = 0;
 		context.alist->al_offset[0] = context.bufsize;
-		context.put_listent = xfs_attr_put_listent;
+		context.put_listent = xfs_attr_user_list;
 	}
 
 	if (XFS_FORCED_SHUTDOWN(dp->i_mount))

===========================================================================
fs/xfs/xfs_attr_leaf.c
===========================================================================

--- a/fs/xfs/xfs_attr_leaf.c	2008-01-21 14:42:50.000000000 +1100
+++ b/fs/xfs/xfs_attr_leaf.c	2008-01-21 14:07:01.982908019 +1100
@@ -42,6 +42,7 @@
 #include "xfs_attr.h"
 #include "xfs_attr_leaf.h"
 #include "xfs_error.h"
+#include "xfs_unicode.h"
 
 /*
  * xfs_attr_leaf.c
@@ -90,6 +91,10 @@ STATIC void xfs_attr_leaf_moveents(xfs_a
 					 xfs_mount_t *mp);
 STATIC int xfs_attr_leaf_entsize(xfs_attr_leafblock_t *leaf, int index);
 
+STATIC int xfs_attr_put_listent(xfs_attr_list_context_t *context,
+					attrnames_t *namesp, char *name,
+					int namelen, int valuelen, char *value);
+
 /*========================================================================
  * Namespace helper routines
  *========================================================================*/
@@ -135,6 +140,38 @@ xfs_attr_namesp_match_overrides(int arg_
  * External routines when attribute fork size < XFS_LITINO(mp).
  *========================================================================*/
 
+STATIC xfs_attr_sf_entry_t *
+xfs_attr_shortform_find_ent(xfs_da_args_t *args)
+{
+	xfs_attr_shortform_t *sf;
+	xfs_attr_sf_entry_t *sfe;
+	int i;
+	xfs_attr_sf_entry_t *ci_sfe = NULL;
+
+	ASSERT(args->dp->i_afp->if_flags & XFS_IFINLINE);
+	sf = (xfs_attr_shortform_t *)args->dp->i_afp->if_u1.if_data;
+	sfe = &sf->list[0];
+
+	args->cmpresult = XFS_CMP_DIFFERENT;
+	for (i = 0; i < sf->hdr.count; sfe = XFS_ATTR_SF_NEXTENTRY(sfe), i++) {
+		if (!xfs_attr_namesp_match(args->flags, sfe->flags))
+			continue;
+		switch (xfs_attr_compname(args->dp, sfe->nameval, sfe->namelen,
+				args->name, args->namelen)) {
+		case XFS_CMP_EXACT:
+			args->cmpresult = XFS_CMP_EXACT;
+			return sfe;
+		case XFS_CMP_CASE:
+			if (!ci_sfe) {
+				args->cmpresult = XFS_CMP_CASE;
+				ci_sfe = sfe;
+			}
+		default:;
+		}
+	}
+	return ci_sfe;
+}
+
 /*
  * Query whether the requested number of additional bytes of extended
  * attribute space will be able to fit inline.
@@ -295,13 +332,10 @@ xfs_attr_shortform_add(xfs_da_args_t *ar
 	sfe = &sf->list[0];
 	for (i = 0; i < sf->hdr.count; sfe = XFS_ATTR_SF_NEXTENTRY(sfe), i++) {
 #ifdef DEBUG
-		if (sfe->namelen != args->namelen)
-			continue;
-		if (memcmp(args->name, sfe->nameval, args->namelen) != 0)
-			continue;
 		if (!xfs_attr_namesp_match(args->flags, sfe->flags))
 			continue;
-		ASSERT(0);
+		ASSERT(xfs_attr_compname(args->dp, args->name, args->namelen,
+			sfe->nameval, sfe->namelen) == XFS_CMP_DIFFERENT);
 #endif
 	}
 
@@ -331,29 +365,19 @@ xfs_attr_shortform_remove(xfs_da_args_t 
 {
 	xfs_attr_shortform_t *sf;
 	xfs_attr_sf_entry_t *sfe;
-	int base, size=0, end, totsize, i;
+	int base, size, end, totsize;
 	xfs_mount_t *mp;
 	xfs_inode_t *dp;
 
+	sfe = xfs_attr_shortform_find_ent(args);
+	if (!sfe)
+		return XFS_ERROR(ENOATTR);
+
 	dp = args->dp;
 	mp = dp->i_mount;
-	base = sizeof(xfs_attr_sf_hdr_t);
 	sf = (xfs_attr_shortform_t *)dp->i_afp->if_u1.if_data;
-	sfe = &sf->list[0];
-	end = sf->hdr.count;
-	for (i = 0; i < end; sfe = XFS_ATTR_SF_NEXTENTRY(sfe),
-					base += size, i++) {
-		size = XFS_ATTR_SF_ENTSIZE(sfe);
-		if (sfe->namelen != args->namelen)
-			continue;
-		if (memcmp(sfe->nameval, args->name, args->namelen) != 0)
-			continue;
-		if (!xfs_attr_namesp_match(args->flags, sfe->flags))
-			continue;
-		break;
-	}
-	if (i == end)
-		return(XFS_ERROR(ENOATTR));
+	size = XFS_ATTR_SF_ENTSIZE(sfe);
+	base = (int)((char *)sfe - (char *)sf);
 
 	/*
 	 * Fix up the attribute fork data, covering the hole
@@ -412,26 +436,7 @@ xfs_attr_shortform_remove(xfs_da_args_t 
 int
 xfs_attr_shortform_lookup(xfs_da_args_t *args)
 {
-	xfs_attr_shortform_t *sf;
-	xfs_attr_sf_entry_t *sfe;
-	int i;
-	xfs_ifork_t *ifp;
-
-	ifp = args->dp->i_afp;
-	ASSERT(ifp->if_flags & XFS_IFINLINE);
-	sf = (xfs_attr_shortform_t *)ifp->if_u1.if_data;
-	sfe = &sf->list[0];
-	for (i = 0; i < sf->hdr.count;
-				sfe = XFS_ATTR_SF_NEXTENTRY(sfe), i++) {
-		if (sfe->namelen != args->namelen)
-			continue;
-		if (memcmp(args->name, sfe->nameval, args->namelen) != 0)
-			continue;
-		if (!xfs_attr_namesp_match(args->flags, sfe->flags))
-			continue;
-		return(XFS_ERROR(EEXIST));
-	}
-	return(XFS_ERROR(ENOATTR));
+	return XFS_ERROR(xfs_attr_shortform_find_ent(args) ? EEXIST : ENOATTR);
 }
 
 /*
@@ -441,35 +446,24 @@ xfs_attr_shortform_lookup(xfs_da_args_t 
 int
 xfs_attr_shortform_getvalue(xfs_da_args_t *args)
 {
-	xfs_attr_shortform_t *sf;
 	xfs_attr_sf_entry_t *sfe;
-	int i;
 
-	ASSERT(args->dp->i_d.di_aformat == XFS_IFINLINE);
-	sf = (xfs_attr_shortform_t *)args->dp->i_afp->if_u1.if_data;
-	sfe = &sf->list[0];
-	for (i = 0; i < sf->hdr.count;
-				sfe = XFS_ATTR_SF_NEXTENTRY(sfe), i++) {
-		if (sfe->namelen != args->namelen)
-			continue;
-		if (memcmp(args->name, sfe->nameval, args->namelen) != 0)
-			continue;
-		if (!xfs_attr_namesp_match(args->flags, sfe->flags))
-			continue;
-		if (args->flags & ATTR_KERNOVAL) {
-			args->valuelen = sfe->valuelen;
-			return(XFS_ERROR(EEXIST));
-		}
-		if (args->valuelen < sfe->valuelen) {
-			args->valuelen = sfe->valuelen;
-			return(XFS_ERROR(ERANGE));
-		}
+	sfe = xfs_attr_shortform_find_ent(args);
+	if (!sfe)
+		return XFS_ERROR(ENOATTR);
+
+	if (args->flags & ATTR_KERNOVAL) {
 		args->valuelen = sfe->valuelen;
-		memcpy(args->value, &sfe->nameval[args->namelen],
-						    args->valuelen);
-		return(XFS_ERROR(EEXIST));
+		return XFS_ERROR(EEXIST);
 	}
-	return(XFS_ERROR(ENOATTR));
+	if (args->valuelen < sfe->valuelen) {
+		args->valuelen = sfe->valuelen;
+		return XFS_ERROR(ERANGE);
+	}
+	args->valuelen = sfe->valuelen;
+	memcpy(args->value, &sfe->nameval[args->namelen], args->valuelen);
+
+	return XFS_ERROR(EEXIST);
 }
 
 /*
@@ -535,17 +529,18 @@ xfs_attr_shortform_to_leaf(xfs_da_args_t
 
 	sfe = &sf->list[0];
 	for (i = 0; i < sf->hdr.count; i++) {
-		nargs.name = (char *)sfe->nameval;
-		nargs.namelen = sfe->namelen;
-		nargs.value = (char *)&sfe->nameval[nargs.namelen];
+		nargs.value = (char *)&sfe->nameval[sfe->namelen];
 		nargs.valuelen = sfe->valuelen;
-		nargs.hashval = xfs_da_hashname((char *)sfe->nameval,
-						sfe->namelen);
+		error = xfs_da_setup_name_and_hash(&nargs, sfe->nameval,
+				sfe->namelen);
+		if (error)
+			goto out;
 		nargs.flags = XFS_ATTR_NSP_ONDISK_TO_ARGS(sfe->flags);
 		error = xfs_attr_leaf_lookup_int(bp, &nargs); /* set a->index */
 		ASSERT(error == ENOATTR);
 		error = xfs_attr_leaf_add(bp, &nargs);
 		ASSERT(error != ENOSPC);
+		xfs_da_cleanup_name(&nargs, sfe->nameval);
 		if (error)
 			goto out;
 		sfe = XFS_ATTR_SF_NEXTENTRY(sfe);
@@ -631,7 +626,7 @@ xfs_attr_shortform_list(xfs_attr_list_co
 				continue;
 			}
 			namesp = xfs_attr_flags_namesp(sfe->flags);
-			error = context->put_listent(context,
+			error = xfs_attr_put_listent(context,
 					   namesp,
 					   (char *)sfe->nameval,
 					   (int)sfe->namelen,
@@ -734,7 +729,7 @@ xfs_attr_shortform_list(xfs_attr_list_co
 			cursor->hashval = sbp->hash;
 			cursor->offset = 0;
 		}
-		error = context->put_listent(context,
+		error = xfs_attr_put_listent(context,
 					namesp,
 					sbp->name,
 					sbp->namelen,
@@ -1960,6 +1955,7 @@ xfs_attr_leaf_lookup_int(xfs_dabuf_t *bp
 	xfs_attr_leaf_name_remote_t *name_rmt;
 	int probe, span;
 	xfs_dahash_t hashval;
+	xfs_dacmp_t cmp;
 
 	leaf = bp->data;
 	ASSERT(be16_to_cpu(leaf->hdr.info.magic) == XFS_ATTR_LEAF_MAGIC);
@@ -2008,6 +2004,7 @@ xfs_attr_leaf_lookup_int(xfs_dabuf_t *bp
 	/*
 	 * Duplicate keys may be present, so search all of them for a match.
 	 */
+	args->cmpresult = XFS_CMP_DIFFERENT;
 	for (  ; (probe < be16_to_cpu(leaf->hdr.count)) &&
 			(be32_to_cpu(entry->hashval) == hashval);
 			entry++, probe++) {
@@ -2019,35 +2016,46 @@ xfs_attr_leaf_lookup_int(xfs_dabuf_t *bp
 		 * If we are looking for complete entries, show only those.
 		 */
 		if ((args->flags & XFS_ATTR_INCOMPLETE) !=
-		    (entry->flags & XFS_ATTR_INCOMPLETE)) {
+				(entry->flags & XFS_ATTR_INCOMPLETE))
+			continue;
+		if (!xfs_attr_namesp_match(args->flags, entry->flags))
 			continue;
-		}
 		if (entry->flags & XFS_ATTR_LOCAL) {
 			name_loc = XFS_ATTR_LEAF_NAME_LOCAL(leaf, probe);
-			if (name_loc->namelen != args->namelen)
-				continue;
-			if (memcmp(args->name, (char *)name_loc->nameval, args->namelen) != 0)
-				continue;
-			if (!xfs_attr_namesp_match(args->flags, entry->flags))
-				continue;
-			args->index = probe;
-			return(XFS_ERROR(EEXIST));
+			cmp = xfs_attr_compname(args->dp, args->name,
+					args->namelen, name_loc->nameval,
+					name_loc->namelen);
+			if (cmp != XFS_CMP_DIFFERENT &&
+					cmp != args->cmpresult) {
+				args->cmpresult = cmp;
+				args->index = probe;
+				args->rmtblkno = 0;
+				args->rmtblkcnt = 0;
+				if (cmp == XFS_CMP_EXACT)
+					return XFS_ERROR(EEXIST);
+			}
 		} else {
 			name_rmt = XFS_ATTR_LEAF_NAME_REMOTE(leaf, probe);
-			if (name_rmt->namelen != args->namelen)
-				continue;
-			if (memcmp(args->name, (char *)name_rmt->name,
-					     args->namelen) != 0)
-				continue;
-			if (!xfs_attr_namesp_match(args->flags, entry->flags))
-				continue;
-			args->index = probe;
-			args->rmtblkno = be32_to_cpu(name_rmt->valueblk);
-			args->rmtblkcnt = XFS_B_TO_FSB(args->dp->i_mount,
-						   be32_to_cpu(name_rmt->valuelen));
-			return(XFS_ERROR(EEXIST));
+			cmp = xfs_attr_compname(args->dp, args->name,
+					args->namelen, name_rmt->name,
+					name_rmt->namelen);
+			if (cmp != XFS_CMP_DIFFERENT &&
+					cmp != args->cmpresult) {
+				args->cmpresult = cmp;
+				args->index = probe;
+				args->rmtblkno =
+					be32_to_cpu(name_rmt->valueblk);
+				args->rmtblkcnt =
+					XFS_B_TO_FSB(args->dp->i_mount,
+					    be32_to_cpu(name_rmt->valuelen));
+				if (cmp == XFS_CMP_EXACT)
+					return XFS_ERROR(EEXIST);
+			}
 		}
 	}
+	if (args->cmpresult == XFS_CMP_CASE)
+		return XFS_ERROR(EEXIST);
+
 	args->index = probe;
 	return(XFS_ERROR(ENOATTR));
 }
@@ -2074,8 +2082,9 @@ xfs_attr_leaf_getvalue(xfs_dabuf_t *bp, 
 	entry = &leaf->entries[args->index];
 	if (entry->flags & XFS_ATTR_LOCAL) {
 		name_loc = XFS_ATTR_LEAF_NAME_LOCAL(leaf, args->index);
-		ASSERT(name_loc->namelen == args->namelen);
-		ASSERT(memcmp(args->name, name_loc->nameval, args->namelen) == 0);
+		ASSERT(xfs_attr_compname(args->dp, args->name, args->namelen,
+			name_loc->nameval, name_loc->namelen) !=
+				XFS_CMP_DIFFERENT);
 		valuelen = be16_to_cpu(name_loc->valuelen);
 		if (args->flags & ATTR_KERNOVAL) {
 			args->valuelen = valuelen;
@@ -2086,11 +2095,13 @@ xfs_attr_leaf_getvalue(xfs_dabuf_t *bp, 
 			return(XFS_ERROR(ERANGE));
 		}
 		args->valuelen = valuelen;
-		memcpy(args->value, &name_loc->nameval[args->namelen], valuelen);
+		memcpy(args->value, &name_loc->nameval[args->namelen],
+				valuelen);
 	} else {
 		name_rmt = XFS_ATTR_LEAF_NAME_REMOTE(leaf, args->index);
-		ASSERT(name_rmt->namelen == args->namelen);
-		ASSERT(memcmp(args->name, name_rmt->name, args->namelen) == 0);
+		ASSERT(xfs_attr_compname(args->dp, args->name, args->namelen,
+			name_rmt->nameval, name_rmt->namelen) !=
+				XFS_CMP_DIFFERENT);
 		valuelen = be32_to_cpu(name_rmt->valuelen);
 		args->rmtblkno = be32_to_cpu(name_rmt->valueblk);
 		args->rmtblkcnt = XFS_B_TO_FSB(args->dp->i_mount, valuelen);
@@ -2298,7 +2309,8 @@ xfs_attr_leaf_lasthash(xfs_dabuf_t *bp, 
 		*count = be16_to_cpu(leaf->hdr.count);
 	if (!leaf->hdr.count)
 		return(0);
-	return be32_to_cpu(leaf->entries[be16_to_cpu(leaf->hdr.count)-1].hashval);
+	return be32_to_cpu(
+			leaf->entries[be16_to_cpu(leaf->hdr.count)-1].hashval);
 }
 
 /*
@@ -2316,7 +2328,7 @@ xfs_attr_leaf_entsize(xfs_attr_leafblock
 	if (leaf->entries[index].flags & XFS_ATTR_LOCAL) {
 		name_loc = XFS_ATTR_LEAF_NAME_LOCAL(leaf, index);
 		size = XFS_ATTR_LEAF_ENTSIZE_LOCAL(name_loc->namelen,
-						   be16_to_cpu(name_loc->valuelen));
+					be16_to_cpu(name_loc->valuelen));
 	} else {
 		name_rmt = XFS_ATTR_LEAF_NAME_REMOTE(leaf, index);
 		size = XFS_ATTR_LEAF_ENTSIZE_REMOTE(name_rmt->namelen);
@@ -2409,7 +2421,8 @@ xfs_attr_leaf_list_int(xfs_dabuf_t *bp, 
 
 		if (entry->flags & XFS_ATTR_INCOMPLETE)
 			continue;		/* skip incomplete entries */
-		if (!xfs_attr_namesp_match_overrides(context->flags, entry->flags))
+		if (!xfs_attr_namesp_match_overrides(context->flags,
+				entry->flags))
 			continue;
 
 		namesp = xfs_attr_flags_namesp(entry->flags);
@@ -2418,12 +2431,13 @@ xfs_attr_leaf_list_int(xfs_dabuf_t *bp, 
 			xfs_attr_leaf_name_local_t *name_loc =
 				XFS_ATTR_LEAF_NAME_LOCAL(leaf, i);
 
-			retval = context->put_listent(context,
+			retval = xfs_attr_put_listent(context,
 						namesp,
 						(char *)name_loc->nameval,
 						(int)name_loc->namelen,
 						be16_to_cpu(name_loc->valuelen),
-						(char *)&name_loc->nameval[name_loc->namelen]);
+						(char *)&name_loc->
+						    nameval[name_loc->namelen]);
 			if (retval)
 				return retval;
 		} else {
@@ -2441,11 +2455,12 @@ xfs_attr_leaf_list_int(xfs_dabuf_t *bp, 
 				args.valuelen = valuelen;
 				args.value = kmem_alloc(valuelen, KM_SLEEP);
 				args.rmtblkno = be32_to_cpu(name_rmt->valueblk);
-				args.rmtblkcnt = XFS_B_TO_FSB(args.dp->i_mount, valuelen);
+				args.rmtblkcnt = XFS_B_TO_FSB(args.dp->i_mount,
+						valuelen);
 				retval = xfs_attr_rmtval_get(&args);
 				if (retval)
 					return retval;
-				retval = context->put_listent(context,
+				retval = xfs_attr_put_listent(context,
 						namesp,
 						(char *)name_rmt->name,
 						(int)name_rmt->namelen,
@@ -2454,7 +2469,7 @@ xfs_attr_leaf_list_int(xfs_dabuf_t *bp, 
 				kmem_free(args.value, valuelen);
 			}
 			else {
-				retval = context->put_listent(context,
+				retval = xfs_attr_put_listent(context,
 						namesp,
 						(char *)name_rmt->name,
 						(int)name_rmt->namelen,
@@ -2472,6 +2487,33 @@ xfs_attr_leaf_list_int(xfs_dabuf_t *bp, 
 	return(retval);
 }
 
+/*
+ * Do NLS name conversion if required for attribute name and call
+ * context's put_listent routine
+ */
+
+STATIC int
+xfs_attr_put_listent(xfs_attr_list_context_t *context, attrnames_t *namesp,
+	char *name, int namelen, int valuelen, char *value)
+{
+	xfs_mount_t *mp = context->dp->i_mount;
+	char *nls_name = NULL;
+	int nls_namelen;
+	int rval;
+
+	if (!mp->m_nls)
+		return context->put_listent(context, namesp, name, namelen,
+				valuelen, value);
+
+	rval = xfs_unicode_to_nls(mp->m_nls, name, namelen, &nls_name,
+			&nls_namelen);
+	if (rval != 0)
+		return rval;
+	rval = context->put_listent(context, namesp, nls_name, nls_namelen,
+			valuelen, value);
+	xfs_free_unicode_nls_name(nls_name);
+	return rval;
+}
 
 /*========================================================================
  * Manage the INCOMPLETE flag in a leaf entry
@@ -2522,8 +2564,8 @@ xfs_attr_leaf_clearflag(xfs_da_args_t *a
 		name = (char *)name_rmt->name;
 	}
 	ASSERT(be32_to_cpu(entry->hashval) == args->hashval);
-	ASSERT(namelen == args->namelen);
-	ASSERT(memcmp(name, args->name, namelen) == 0);
+	ASSERT(xfs_attr_compname(args->dp, args->name, args->namelen,
+			name, namelen) != XFS_CMP_DIFFERENT);
 #endif /* DEBUG */
 
 	entry->flags &= ~XFS_ATTR_INCOMPLETE;
@@ -2674,8 +2716,8 @@ xfs_attr_leaf_flipflags(xfs_da_args_t *a
 		name2 = (char *)name_rmt->name;
 	}
 	ASSERT(be32_to_cpu(entry1->hashval) == be32_to_cpu(entry2->hashval));
-	ASSERT(namelen1 == namelen2);
-	ASSERT(memcmp(name1, name2, namelen1) == 0);
+	ASSERT(xfs_attr_compname(args->dp, name1, namelen1, name2, namelen2) !=
+			XFS_CMP_DIFFERENT);
 #endif /* DEBUG */
 
 	ASSERT(entry1->flags & XFS_ATTR_INCOMPLETE);
@@ -2834,7 +2876,8 @@ xfs_attr_node_inactive(xfs_trans_t **tra
 			if (be16_to_cpu(info->magic) == XFS_DA_NODE_MAGIC) {
 				error = xfs_attr_node_inactive(trans, dp,
 						child_bp, level+1);
-			} else if (be16_to_cpu(info->magic) == XFS_ATTR_LEAF_MAGIC) {
+			} else if (be16_to_cpu(info->magic) ==
+					XFS_ATTR_LEAF_MAGIC) {
 				error = xfs_attr_leaf_inactive(trans, dp,
 						child_bp);
 			} else {
@@ -2935,7 +2978,7 @@ xfs_attr_leaf_inactive(xfs_trans_t **tra
 			if (name_rmt->valueblk) {
 				lp->valueblk = be32_to_cpu(name_rmt->valueblk);
 				lp->valuelen = XFS_B_TO_FSB(dp->i_mount,
-						    be32_to_cpu(name_rmt->valuelen));
+					be32_to_cpu(name_rmt->valuelen));
 				lp++;
 			}
 		}

===========================================================================
fs/xfs/xfs_attr_leaf.h
===========================================================================

--- a/fs/xfs/xfs_attr_leaf.h	2008-01-21 14:42:50.000000000 +1100
+++ b/fs/xfs/xfs_attr_leaf.h	2008-01-11 14:16:44.268796245 +1100
@@ -36,6 +36,7 @@ struct xfs_da_args;
 struct xfs_da_state;
 struct xfs_da_state_blk;
 struct xfs_inode;
+struct xfs_mount;
 struct xfs_trans;
 
 /*========================================================================
@@ -204,6 +205,16 @@ static inline int xfs_attr_leaf_entsize_
 	return (((bsize) >> 1) + ((bsize) >> 2));
 }
 
+/*
+ * Do hash and name compare based on nameops
+ */
+#define xfs_attr_hashname(dp, n, l) \
+		((dp)->i_mount->m_attrnameops->hashname((dp), (n), (l)))
+
+#define xfs_attr_compname(dp, n1, l1, n2, l2) \
+		((dp)->i_mount->m_attrnameops->compname((dp), (n1), (l1), \
+							(n2), (l2)))
+
 
 /*========================================================================
  * Structure used to pass context around among the routines.
@@ -296,6 +307,7 @@ int	xfs_attr_root_inactive(struct xfs_tr
 /*
  * Utility routines.
  */
+void	xfs_attr_mount(struct xfs_mount *mp);
 xfs_dahash_t	xfs_attr_leaf_lasthash(struct xfs_dabuf *bp, int *count);
 int	xfs_attr_leaf_order(struct xfs_dabuf *leaf1_bp,
 				   struct xfs_dabuf *leaf2_bp);

===========================================================================
fs/xfs/xfs_clnt.h
===========================================================================

--- a/fs/xfs/xfs_clnt.h	2008-01-21 14:42:50.000000000 +1100
+++ b/fs/xfs/xfs_clnt.h	2007-11-01 13:16:55.000383139 +1100
@@ -48,6 +48,7 @@ struct xfs_mount_args {
 	char	rtname[MAXNAMELEN+1];	/* realtime device filename */
 	char	logname[MAXNAMELEN+1];	/* journal device filename */
 	char	mtpt[MAXNAMELEN+1];	/* filesystem mount point */
+	char	nls[MAXNAMELEN+1];	/* NLS code page to use */
 	int	sunit;		/* stripe unit (BBs) */
 	int	swidth;		/* stripe width (BBs), multiple of sunit */
 	uchar_t iosizelog;	/* log2 of the preferred I/O size */
@@ -100,5 +101,9 @@ struct xfs_mount_args {
 						 * I/O size in stat(2) */
 #define XFSMNT2_FILESTREAMS	0x00000002	/* enable the filestreams
 						 * allocator */
+#define XFSMNT2_CILOOKUP	0x00000004	/* enable case-insensitive
+						 * filename lookup */
+#define XFSMNT2_CIATTR		0x00000008	/* enable case-insensitive
+						 * extended attr names */
 
 #endif	/* __XFS_CLNT_H__ */

===========================================================================
fs/xfs/xfs_da_btree.c
===========================================================================

--- a/fs/xfs/xfs_da_btree.c	2008-01-21 14:42:50.000000000 +1100
+++ b/fs/xfs/xfs_da_btree.c	2008-01-21 14:23:59.544205024 +1100
@@ -46,6 +46,7 @@
 #include "xfs_dir2_block.h"
 #include "xfs_dir2_node.h"
 #include "xfs_error.h"
+#include "xfs_unicode.h"
 
 /*
  * xfs_da_btree.c
@@ -1530,6 +1531,106 @@ xfs_da_hashname(const uchar_t *name, int
 	}
 }
 
+
+static xfs_dahash_t
+xfs_default_hashname(xfs_inode_t *inode, const uchar_t *name, int namelen)
+{
+	return xfs_da_hashname(name, namelen);
+}
+
+xfs_dacmp_t
+xfs_default_compname(xfs_inode_t *inode, const uchar_t *name1, int len1,
+	const uchar_t *name2, int len2)
+{
+	return (len1 == len2 && memcmp(name1, name2, len1) == 0) ?
+			XFS_CMP_EXACT : XFS_CMP_DIFFERENT;
+}
+
+static xfs_dahash_t
+xfs_unicode_ci_hashname(
+	xfs_inode_t	*inode,
+	const uchar_t	*name,
+	int		namelen)
+{
+	return xfs_unicode_hash(inode->i_mount->m_cft, name, namelen);
+}
+
+static xfs_dacmp_t
+xfs_unicode_ci_compname(
+	xfs_inode_t	*inode,
+	const uchar_t	*name1,
+	int		len1,
+	const uchar_t	*name2,
+	int 		len2)
+{
+	if (len1 == len2 && memcmp(name1, name2, len1) == 0)
+		return XFS_CMP_EXACT;
+
+	return xfs_unicode_casecmp(inode->i_mount->m_cft, name1, len1,
+			name2, len2) == 0 ? XFS_CMP_CASE : XFS_CMP_DIFFERENT;
+}
+
+const struct xfs_nameops xfs_default_nameops = {
+	.hashname	= xfs_default_hashname,
+	.compname	= xfs_default_compname
+};
+
+const struct xfs_nameops xfs_unicode_nameops = {
+	.hashname	= xfs_unicode_ci_hashname,
+	.compname	= xfs_default_compname,
+};
+
+const struct xfs_nameops xfs_unicode_ci_nameops = {
+	.hashname	= xfs_unicode_ci_hashname,
+	.compname	= xfs_unicode_ci_compname,
+};
+
+int
+xfs_da_setup_name_and_hash(
+	xfs_da_args_t		*args,
+	const char 		*name,
+	int			namelen)
+{
+	xfs_mount_t		*mp = args->dp->i_mount;
+	int			error;
+
+	/*
+	 * xfs_nls_to_unicode will return an allocated string in
+	 * args->name. Therefore, all calls to xfs_da_setup_name_and_hash
+	 * needs a corresponding call to xfs_da_cleanup_name.
+	 */
+
+	if (mp->m_nls) {
+		args->name = NULL;
+		error = xfs_nls_to_unicode(mp->m_nls, name, namelen,
+				(char **)&args->name, &args->namelen);
+		if (error)
+			return error;
+	} else {
+		if (xfs_sb_version_hasunicode(&mp->m_sb)) {
+			error = xfs_unicode_validate(name, namelen);
+			if (error)
+				return error;
+		}
+		args->name = name;
+		args->namelen = namelen;
+	}
+	args->hashval = (args->whichfork == XFS_ATTR_FORK) ?
+		xfs_attr_hashname(args->dp, args->name, args->namelen) :
+		xfs_dir_hashname(args->dp, args->name, args->namelen);
+	return 0;
+}
+
+void
+xfs_da_cleanup_name(
+	xfs_da_args_t		*args,
+	const char		*name)
+{
+	if ((char *)args->name != name)
+		xfs_free_unicode_nls_name((char *)args->name);
+}
+
+
 /*
  * Add a block to the btree ahead of the file.
  * Return the new block number to the caller.

===========================================================================
fs/xfs/xfs_da_btree.h
===========================================================================

--- a/fs/xfs/xfs_da_btree.h	2008-01-21 14:42:50.000000000 +1100
+++ b/fs/xfs/xfs_da_btree.h	2008-01-14 12:12:32.917055949 +1100
@@ -99,12 +99,21 @@ typedef struct xfs_da_node_entry xfs_da_
  *========================================================================*/
 
 /*
+ * Search comparison results
+ */
+typedef enum {
+	XFS_CMP_DIFFERENT,	/* names are completely different */
+	XFS_CMP_EXACT,		/* names are exactly the same */
+	XFS_CMP_CASE		/* names are same but differ in case */
+} xfs_dacmp_t;
+
+/*
  * Structure to ease passing around component names.
  */
 typedef struct xfs_da_args {
 	const uchar_t	*name;		/* string (maybe not NULL terminated) */
 	int		namelen;	/* length of string (maybe no NULL) */
-	uchar_t		*value;		/* set of bytes (maybe contain NULLs) */
+	uchar_t		*value; 	/* set of bytes (maybe contain NULLs) */
 	int		valuelen;	/* length of value */
 	int		flags;		/* argument flags (eg: ATTR_NOCREATE) */
 	xfs_dahash_t	hashval;	/* hash value of name */
@@ -127,6 +136,7 @@ typedef struct xfs_da_args {
 	unsigned char	rename;		/* T/F: this is an atomic rename op */
 	unsigned char	addname;	/* T/F: this is an add operation */
 	unsigned char	oknoent;	/* T/F: ok to return ENOENT, else die */
+	xfs_dacmp_t	cmpresult;	/* name compare result for lookups */
 } xfs_da_args_t;
 
 /*
@@ -201,12 +211,37 @@ typedef struct xfs_da_state {
 		(uint)(XFS_DA_LOGOFF(BASE, ADDR)), \
 		(uint)(XFS_DA_LOGOFF(BASE, ADDR)+(SIZE)-1)
 
+/*
+ * Name ops for directory and/or attr name operations
+ */
+
+typedef xfs_dahash_t	(*xfs_hashname_t)(struct xfs_inode *, const uchar_t *,
+					int);
+typedef xfs_dacmp_t	(*xfs_compname_t)(struct xfs_inode *, const uchar_t *,
+					int, const uchar_t *, int);
+
+typedef struct xfs_nameops {
+	xfs_hashname_t		hashname;
+	xfs_compname_t		compname;
+} xfs_nameops_t;
+
 
 #ifdef __KERNEL__
 /*========================================================================
  * Function prototypes for the kernel.
  *========================================================================*/
 
+extern const struct xfs_nameops xfs_default_nameops;
+extern const struct xfs_nameops xfs_unicode_nameops;
+extern const struct xfs_nameops xfs_unicode_ci_nameops;
+
+xfs_dacmp_t xfs_default_compname(struct xfs_inode *inode, const uchar_t *name1,
+				int len1, const uchar_t *name2, int len2);
+
+int	xfs_da_setup_name_and_hash(xfs_da_args_t *args, const char *name,
+				int namelen);
+void	xfs_da_cleanup_name(xfs_da_args_t *args, const char *name);
+
 /*
  * Routines used for growing the Btree.
  */

===========================================================================
fs/xfs/xfs_dir2.c
===========================================================================

--- a/fs/xfs/xfs_dir2.c	2008-01-21 14:42:50.000000000 +1100
+++ b/fs/xfs/xfs_dir2.c	2008-01-21 13:21:20.375779062 +1100
@@ -42,8 +42,58 @@
 #include "xfs_dir2_node.h"
 #include "xfs_dir2_trace.h"
 #include "xfs_error.h"
+#include "xfs_unicode.h"
 #include "xfs_vnodeops.h"
 
+/*
+ * V1/OLDCI case-insensitive support for directories
+ *
+ * This is ASCII only case support, ie. A-Z.
+ */
+static xfs_dahash_t
+xfs_ascii_ci_hashname(
+	xfs_inode_t	*inode,
+	const uchar_t	*name,
+	int		namelen)
+{
+	xfs_dahash_t	hash;
+	int		i;
+
+	for (i = 0, hash = 0; i < namelen; i++)
+		hash = tolower(name[i]) ^ rol32(hash, 7);
+
+	return hash;
+}
+
+static xfs_dacmp_t
+xfs_ascii_ci_compname(
+	xfs_inode_t	*inode,
+	const uchar_t	*name1,
+	int		len1,
+	const uchar_t	*name2,
+	int 		len2)
+{
+	xfs_dacmp_t	result = XFS_CMP_EXACT;
+	int		i;
+
+	if (len1 != len2)
+		return XFS_CMP_DIFFERENT;
+
+	for (i = 0; i < len1; i++) {
+		if (name1[i] == name2[i])
+			continue;
+		if (tolower(name1[i]) != tolower(name2[i]))
+			return XFS_CMP_DIFFERENT;
+		result = XFS_CMP_CASE;
+	}
+
+	return result;
+}
+
+static const struct xfs_nameops xfs_ascii_ci_nameops = {
+	.hashname	= xfs_ascii_ci_hashname,
+	.compname	= xfs_ascii_ci_compname,
+};
 
 void
 xfs_dir_mount(
@@ -64,6 +114,13 @@ xfs_dir_mount(
 		(mp->m_dirblksize - (uint)sizeof(xfs_da_node_hdr_t)) /
 		(uint)sizeof(xfs_da_node_entry_t);
 	mp->m_dir_magicpct = (mp->m_dirblksize * 37) / 100;
+
+	if (xfs_sb_version_hasunicode(&mp->m_sb)) {
+		mp->m_dirnameops = (mp->m_flags & XFS_MOUNT_CI_LOOKUP) ?
+			&xfs_unicode_ci_nameops : &xfs_unicode_nameops;
+	} else
+		mp->m_dirnameops = (xfs_sb_version_hasoldci(&mp->m_sb)) ?
+			&xfs_ascii_ci_nameops : &xfs_default_nameops;
 }
 
 /*
@@ -140,7 +197,7 @@ xfs_dir_init(
 }
 
 /*
-  Enter a name in a directory.
+ * Enter a name in a directory.
  */
 int
 xfs_dir_createname(
@@ -162,9 +219,6 @@ xfs_dir_createname(
 		return rval;
 	XFS_STATS_INC(xs_dir_create);
 
-	args.name = name;
-	args.namelen = namelen;
-	args.hashval = xfs_da_hashname(name, namelen);
 	args.inumber = inum;
 	args.dp = dp;
 	args.firstblock = first;
@@ -174,32 +228,41 @@ xfs_dir_createname(
 	args.trans = tp;
 	args.justcheck = 0;
 	args.addname = args.oknoent = 1;
+	rval = xfs_da_setup_name_and_hash(&args, name, namelen);
+	if (rval)
+		return rval;
 
 	if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL)
 		rval = xfs_dir2_sf_addname(&args);
 	else if ((rval = xfs_dir2_isblock(tp, dp, &v)))
-		return rval;
+		goto out;
 	else if (v)
 		rval = xfs_dir2_block_addname(&args);
 	else if ((rval = xfs_dir2_isleaf(tp, dp, &v)))
-		return rval;
+		goto out;
 	else if (v)
 		rval = xfs_dir2_leaf_addname(&args);
 	else
 		rval = xfs_dir2_node_addname(&args);
+out:
+	xfs_da_cleanup_name(&args, name);
 	return rval;
 }
 
 /*
- * Lookup a name in a directory, give back the inode number.
+ * Lookup a name in a directory, give back the inode number and also
+ * actual name if case-insensitive match.
  */
+
 int
 xfs_dir_lookup(
 	xfs_trans_t	*tp,
 	xfs_inode_t	*dp,
 	char		*name,
 	int		namelen,
-	xfs_ino_t	*inum)		/* out: inode number */
+	xfs_ino_t	*inum,		/* out: inode number */
+	char		**actual_name,	/* out: actual name if different */
+	int		*actual_namelen)
 {
 	xfs_da_args_t	args;
 	int		rval;
@@ -208,9 +271,9 @@ xfs_dir_lookup(
 	ASSERT((dp->i_d.di_mode & S_IFMT) == S_IFDIR);
 	XFS_STATS_INC(xs_dir_lookup);
 
-	args.name = name;
-	args.namelen = namelen;
-	args.hashval = xfs_da_hashname(name, namelen);
+	if (actual_name)
+		*actual_name = NULL;
+
 	args.inumber = 0;
 	args.dp = dp;
 	args.firstblock = NULL;
@@ -220,23 +283,39 @@ xfs_dir_lookup(
 	args.trans = tp;
 	args.justcheck = args.addname = 0;
 	args.oknoent = 1;
+	args.value = NULL;	/* value may contain actual name on return */
+	args.valuelen = 0;
+	rval = xfs_da_setup_name_and_hash(&args, name, namelen);
+	if (rval)
+		return rval;
 
 	if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL)
 		rval = xfs_dir2_sf_lookup(&args);
 	else if ((rval = xfs_dir2_isblock(tp, dp, &v)))
-		return rval;
+		goto out;
 	else if (v)
 		rval = xfs_dir2_block_lookup(&args);
 	else if ((rval = xfs_dir2_isleaf(tp, dp, &v)))
-		return rval;
+		goto out;
 	else if (v)
 		rval = xfs_dir2_leaf_lookup(&args);
 	else
 		rval = xfs_dir2_node_lookup(&args);
 	if (rval == EEXIST)
 		rval = 0;
-	if (rval == 0)
+	if (rval == 0) {
 		*inum = args.inumber;
+		if (args.value) {
+			ASSERT(args->cmpresult == XFS_CMP_CASE);
+			if (actual_name) {
+				*actual_namelen = args.valuelen,
+				*actual_name = args.value;
+			} else
+				xfs_free_unicode_nls_name(args.value);
+		}
+	}
+out:
+	xfs_da_cleanup_name(&args, name);
 	return rval;
 }
 
@@ -261,9 +340,6 @@ xfs_dir_removename(
 	ASSERT((dp->i_d.di_mode & S_IFMT) == S_IFDIR);
 	XFS_STATS_INC(xs_dir_remove);
 
-	args.name = name;
-	args.namelen = namelen;
-	args.hashval = xfs_da_hashname(name, namelen);
 	args.inumber = ino;
 	args.dp = dp;
 	args.firstblock = first;
@@ -272,19 +348,24 @@ xfs_dir_removename(
 	args.whichfork = XFS_DATA_FORK;
 	args.trans = tp;
 	args.justcheck = args.addname = args.oknoent = 0;
+	rval = xfs_da_setup_name_and_hash(&args, name, namelen);
+	if (rval)
+		return rval;
 
 	if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL)
 		rval = xfs_dir2_sf_removename(&args);
 	else if ((rval = xfs_dir2_isblock(tp, dp, &v)))
-		return rval;
+		goto out;
 	else if (v)
 		rval = xfs_dir2_block_removename(&args);
 	else if ((rval = xfs_dir2_isleaf(tp, dp, &v)))
-		return rval;
+		goto out;
 	else if (v)
 		rval = xfs_dir2_leaf_removename(&args);
 	else
 		rval = xfs_dir2_node_removename(&args);
+out:
+	xfs_da_cleanup_name(&args, name);
 	return rval;
 }
 
@@ -345,9 +426,6 @@ xfs_dir_replace(
 	if ((rval = xfs_dir_ino_validate(tp->t_mountp, inum)))
 		return rval;
 
-	args.name = name;
-	args.namelen = namelen;
-	args.hashval = xfs_da_hashname(name, namelen);
 	args.inumber = inum;
 	args.dp = dp;
 	args.firstblock = first;
@@ -356,19 +434,24 @@ xfs_dir_replace(
 	args.whichfork = XFS_DATA_FORK;
 	args.trans = tp;
 	args.justcheck = args.addname = args.oknoent = 0;
+	rval = xfs_da_setup_name_and_hash(&args, name, namelen);
+	if (rval)
+		return rval;
 
 	if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL)
 		rval = xfs_dir2_sf_replace(&args);
 	else if ((rval = xfs_dir2_isblock(tp, dp, &v)))
-		return rval;
+		goto out;
 	else if (v)
 		rval = xfs_dir2_block_replace(&args);
 	else if ((rval = xfs_dir2_isleaf(tp, dp, &v)))
-		return rval;
+		goto out;
 	else if (v)
 		rval = xfs_dir2_leaf_replace(&args);
 	else
 		rval = xfs_dir2_node_replace(&args);
+out:
+	xfs_da_cleanup_name(&args, name);
 	return rval;
 }
 
@@ -388,9 +471,6 @@ xfs_dir_canenter(
 
 	ASSERT((dp->i_d.di_mode & S_IFMT) == S_IFDIR);
 
-	args.name = name;
-	args.namelen = namelen;
-	args.hashval = xfs_da_hashname(name, namelen);
 	args.inumber = 0;
 	args.dp = dp;
 	args.firstblock = NULL;
@@ -399,19 +479,24 @@ xfs_dir_canenter(
 	args.whichfork = XFS_DATA_FORK;
 	args.trans = tp;
 	args.justcheck = args.addname = args.oknoent = 1;
+	rval = xfs_da_setup_name_and_hash(&args, name, namelen);
+	if (rval)
+		return rval;
 
 	if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL)
 		rval = xfs_dir2_sf_addname(&args);
 	else if ((rval = xfs_dir2_isblock(tp, dp, &v)))
-		return rval;
+		goto out;
 	else if (v)
 		rval = xfs_dir2_block_addname(&args);
 	else if ((rval = xfs_dir2_isleaf(tp, dp, &v)))
-		return rval;
+		goto out;
 	else if (v)
 		rval = xfs_dir2_leaf_addname(&args);
 	else
 		rval = xfs_dir2_node_addname(&args);
+out:
+	xfs_da_cleanup_name(&args, name);
 	return rval;
 }
 

===========================================================================
fs/xfs/xfs_dir2.h
===========================================================================

--- a/fs/xfs/xfs_dir2.h	2008-01-21 14:42:50.000000000 +1100
+++ b/fs/xfs/xfs_dir2.h	2007-11-01 13:11:00.206583735 +1100
@@ -72,7 +72,8 @@ extern int xfs_dir_createname(struct xfs
 				xfs_fsblock_t *first,
 				struct xfs_bmap_free *flist, xfs_extlen_t tot);
 extern int xfs_dir_lookup(struct xfs_trans *tp, struct xfs_inode *dp,
-				char *name, int namelen, xfs_ino_t *inum);
+				char *name, int namelen, xfs_ino_t *inum,
+				char **actual_name, int *actual_namelen);
 extern int xfs_dir_removename(struct xfs_trans *tp, struct xfs_inode *dp,
 				char *name, int namelen, xfs_ino_t ino,
 				xfs_fsblock_t *first,
@@ -85,6 +86,13 @@ extern int xfs_dir_canenter(struct xfs_t
 				char *name, int namelen);
 extern int xfs_dir_ino_validate(struct xfs_mount *mp, xfs_ino_t ino);
 
+#define xfs_dir_hashname(dp, n, l) \
+		((dp)->i_mount->m_dirnameops->hashname((dp), (n), (l)))
+
+#define xfs_dir_compname(dp, n1, l1, n2, l2) \
+		((dp)->i_mount->m_dirnameops->compname((dp), (n1), (l1), \
+							(n2), (l2)))
+
 /*
  * Utility routines for v2 directories.
  */

===========================================================================
fs/xfs/xfs_dir2_block.c
===========================================================================

--- a/fs/xfs/xfs_dir2_block.c	2008-01-21 14:42:50.000000000 +1100
+++ b/fs/xfs/xfs_dir2_block.c	2008-01-21 13:50:21.175545312 +1100
@@ -38,6 +38,7 @@
 #include "xfs_dir2_block.h"
 #include "xfs_dir2_trace.h"
 #include "xfs_error.h"
+#include "xfs_unicode.h"
 
 /*
  * Local function prototypes.
@@ -450,6 +451,8 @@ xfs_dir2_block_getdents(
 	int			wantoff;	/* starting block offset */
 	xfs_ino_t		ino;
 	xfs_off_t		cook;
+	char			*nls_name = NULL; /* NLS name buffer */
+	int			nls_namelen = 0;
 
 	mp = dp->i_mount;
 	/*
@@ -481,6 +484,9 @@ xfs_dir2_block_getdents(
 	ptr = (char *)block->u;
 	endptr = (char *)xfs_dir2_block_leaf_p(btp);
 
+	if (mp->m_nls)
+		nls_name = xfs_alloc_unicode_nls_name();
+
 	/*
 	 * Loop over the data portion of the block.
 	 * Each object is a real entry (dep) or an unused one (dup).
@@ -513,15 +519,24 @@ xfs_dir2_block_getdents(
 #if XFS_BIG_INUMS
 		ino += mp->m_inoadd;
 #endif
+		if (mp->m_nls) {
+			error = xfs_unicode_to_nls(mp->m_nls, dep->name,
+					dep->namelen, &nls_name, &nls_namelen);
+			if (error) {
+				xfs_da_brelse(NULL, bp);
+				return error;
+			}
+		}
 
 		/*
 		 * If it didn't fit, set the final offset to here & return.
 		 */
-		if (filldir(dirent, dep->name, dep->namelen, cook,
-			    ino, DT_UNKNOWN)) {
+		if (filldir(dirent,
+				nls_namelen ? nls_name : (char *)dep->name,
+				nls_namelen ? nls_namelen : dep->namelen,
+				cook, ino, DT_UNKNOWN)) {
 			*offset = cook;
-			xfs_da_brelse(NULL, bp);
-			return 0;
+			goto out;
 		}
 	}
 
@@ -530,6 +545,9 @@ xfs_dir2_block_getdents(
 	 * Set the offset to a non-existent block 1 and return.
 	 */
 	*offset = xfs_dir2_db_off_to_dataptr(mp, mp->m_dirdatablk + 1, 0);
+out:
+	if (mp->m_nls)
+		xfs_free_unicode_nls_name(nls_name);
 	xfs_da_brelse(NULL, bp);
 	return 0;
 }
@@ -616,6 +634,14 @@ xfs_dir2_block_lookup(
 	 * Fill in inode number, release the block.
 	 */
 	args->inumber = be64_to_cpu(dep->inumber);
+	if (args->cmpresult == XFS_CMP_CASE) {
+		error = xfs_unicode_to_nls(mp->m_nls, dep->name, dep->namelen,
+				(char **)&args->value, &args->valuelen);
+		if (error) {
+			xfs_da_brelse(args->trans, bp);
+			return XFS_ERROR(error);
+		}
+	}
 	xfs_da_brelse(args->trans, bp);
 	return XFS_ERROR(EEXIST);
 }
@@ -643,6 +669,7 @@ xfs_dir2_block_lookup_int(
 	int			mid;		/* binary search current idx */
 	xfs_mount_t		*mp;		/* filesystem mount point */
 	xfs_trans_t		*tp;		/* transaction pointer */
+	xfs_dacmp_t		cmp;		/* comparison result */
 
 	dp = args->dp;
 	tp = args->trans;
@@ -688,6 +715,7 @@ xfs_dir2_block_lookup_int(
 	 * Now loop forward through all the entries with the
 	 * right hash value looking for our name.
 	 */
+	args->cmpresult = XFS_CMP_DIFFERENT;
 	do {
 		if ((addr = be32_to_cpu(blp[mid].address)) == XFS_DIR2_NULL_DATAPTR)
 			continue;
@@ -697,20 +725,34 @@ xfs_dir2_block_lookup_int(
 		dep = (xfs_dir2_data_entry_t *)
 			((char *)block + xfs_dir2_dataptr_to_off(mp, addr));
 		/*
-		 * Compare, if it's right give back buffer & entry number.
-		 */
-		if (dep->namelen == args->namelen &&
-		    dep->name[0] == args->name[0] &&
-		    memcmp(dep->name, args->name, args->namelen) == 0) {
+		 * Compare, if it's right give back buffer & entry number:
+		 *
+		 * lookup case - use nameops;
+		 *
+		 * replace/remove case - as lookup has been already been
+		 * performed, look for an exact match using the fast method
+		 */
+		cmp = args->oknoent ?
+			xfs_dir_compname(dp, dep->name, dep->namelen,
+						args->name, args->namelen) :
+			xfs_default_compname(dp, dep->name, dep->namelen,
+						args->name, args->namelen);
+		if (cmp != XFS_CMP_DIFFERENT && cmp != args->cmpresult) {
+			args->cmpresult = cmp;
 			*bpp = bp;
 			*entno = mid;
-			return 0;
+			if (cmp == XFS_CMP_EXACT)
+				return 0;
 		}
-	} while (++mid < be32_to_cpu(btp->count) && be32_to_cpu(blp[mid].hashval) == hash);
+	} while (++mid < be32_to_cpu(btp->count) &&
+			be32_to_cpu(blp[mid].hashval) == hash);
+
+	ASSERT(args->oknoent);
+	if (args->cmpresult == XFS_CMP_CASE)
+		return 0;
 	/*
 	 * No match, release the buffer and return ENOENT.
 	 */
-	ASSERT(args->oknoent);
 	xfs_da_brelse(tp, bp);
 	return XFS_ERROR(ENOENT);
 }
@@ -1187,8 +1229,8 @@ xfs_dir2_sf_to_block(
 		tagp = xfs_dir2_data_entry_tag_p(dep);
 		*tagp = cpu_to_be16((char *)dep - (char *)block);
 		xfs_dir2_data_log_entry(tp, bp, dep);
-		blp[2 + i].hashval = cpu_to_be32(xfs_da_hashname(
-					(char *)sfep->name, sfep->namelen));
+		blp[2 + i].hashval = cpu_to_be32(xfs_dir_hashname(dp,
+						sfep->name, sfep->namelen));
 		blp[2 + i].address = cpu_to_be32(xfs_dir2_byte_to_dataptr(mp,
 						 (char *)dep - (char *)block));
 		offset = (int)((char *)(tagp + 1) - (char *)block);

===========================================================================
fs/xfs/xfs_dir2_data.c
===========================================================================

--- a/fs/xfs/xfs_dir2_data.c	2008-01-21 14:42:50.000000000 +1100
+++ b/fs/xfs/xfs_dir2_data.c	2008-01-21 14:33:22.540095423 +1100
@@ -140,7 +140,8 @@ xfs_dir2_data_check(
 			addr = xfs_dir2_db_off_to_dataptr(mp, mp->m_dirdatablk,
 				(xfs_dir2_data_aoff_t)
 				((char *)dep - (char *)d));
-			hash = xfs_da_hashname((char *)dep->name, dep->namelen);
+			hash = xfs_dir_hashname(dp, (char *)dep->name,
+					dep->namelen);
 			for (i = 0; i < be32_to_cpu(btp->count); i++) {
 				if (be32_to_cpu(lep[i].address) == addr &&
 				    be32_to_cpu(lep[i].hashval) == hash)

===========================================================================
fs/xfs/xfs_dir2_leaf.c
===========================================================================

--- a/fs/xfs/xfs_dir2_leaf.c	2008-01-21 14:42:50.000000000 +1100
+++ b/fs/xfs/xfs_dir2_leaf.c	2008-01-21 14:16:01.621460279 +1100
@@ -40,6 +40,7 @@
 #include "xfs_dir2_node.h"
 #include "xfs_dir2_trace.h"
 #include "xfs_error.h"
+#include "xfs_unicode.h"
 
 /*
  * Local function declarations.
@@ -780,6 +781,8 @@ xfs_dir2_leaf_getdents(
 	int			ra_offset;	/* map entry offset for ra */
 	int			ra_want;	/* readahead count wanted */
 	xfs_ino_t		ino;
+	char			*nls_name = NULL; /* NLS name buffer */
+	int			nls_namelen = 0;
 
 	/*
 	 * If the offset is at or past the largest allowed value,
@@ -800,6 +803,9 @@ xfs_dir2_leaf_getdents(
 	map_valid = ra_index = ra_offset = ra_current = map_blocks = 0;
 	bp = NULL;
 
+	if (mp->m_nls)
+		nls_name = xfs_alloc_unicode_nls_name();
+
 	/*
 	 * Inside the loop we keep the main offset value as a byte offset
 	 * in the directory file.
@@ -1086,13 +1092,21 @@ xfs_dir2_leaf_getdents(
 #if XFS_BIG_INUMS
 		ino += mp->m_inoadd;
 #endif
+		if (mp->m_nls) {
+			error = xfs_unicode_to_nls(mp->m_nls, dep->name,
+					dep->namelen, &nls_name, &nls_namelen);
+			if (error)
+				break;
+		}
 
 		/*
 		 * Won't fit.  Return to caller.
 		 */
-		if (filldir(dirent, dep->name, dep->namelen,
-			    xfs_dir2_byte_to_dataptr(mp, curoff),
-			    ino, DT_UNKNOWN))
+		if (filldir(dirent,
+				nls_namelen ? nls_name : (char *)dep->name,
+				nls_namelen ? nls_namelen : dep->namelen,
+				xfs_dir2_byte_to_dataptr(mp, curoff),
+				ino, DT_UNKNOWN))
 			break;
 
 		/*
@@ -1113,6 +1127,8 @@ xfs_dir2_leaf_getdents(
 	kmem_free(map, map_size * sizeof(*map));
 	if (bp)
 		xfs_da_brelse(NULL, bp);
+	if (mp->m_nls)
+		xfs_free_unicode_nls_name(nls_name);
 	return error;
 }
 
@@ -1300,10 +1316,18 @@ xfs_dir2_leaf_lookup(
 	/*
 	 * Return the found inode number.
 	 */
+	error = EEXIST;
 	args->inumber = be64_to_cpu(dep->inumber);
+	if (args->cmpresult == XFS_CMP_CASE) {
+		error = xfs_unicode_to_nls(args->dp->i_mount->m_nls,
+				dep->name, dep->namelen,
+				(char **)&args->value, &args->valuelen);
+		if (!error)
+			error = EEXIST;
+	}
 	xfs_da_brelse(tp, dbp);
 	xfs_da_brelse(tp, lbp);
-	return XFS_ERROR(EEXIST);
+	return XFS_ERROR(error);
 }
 
 /*
@@ -1331,6 +1355,7 @@ xfs_dir2_leaf_lookup_int(
 	xfs_mount_t		*mp;		/* filesystem mount point */
 	xfs_dir2_db_t		newdb;		/* new data block number */
 	xfs_trans_t		*tp;		/* transaction pointer */
+	xfs_dacmp_t		cmp;		/* name compare result */
 
 	dp = args->dp;
 	tp = args->trans;
@@ -1354,6 +1379,7 @@ xfs_dir2_leaf_lookup_int(
 	 * Loop over all the entries with the right hash value
 	 * looking to match the name.
 	 */
+	args->cmpresult = XFS_CMP_DIFFERENT;
 	for (lep = &leaf->ents[index], dbp = NULL, curdb = -1;
 	     index < be16_to_cpu(leaf->hdr.count) && be32_to_cpu(lep->hashval) == args->hashval;
 	     lep++, index++) {
@@ -1391,19 +1417,31 @@ xfs_dir2_leaf_lookup_int(
 		       xfs_dir2_dataptr_to_off(mp, be32_to_cpu(lep->address)));
 		/*
 		 * If it matches then return it.
-		 */
-		if (dep->namelen == args->namelen &&
-		    dep->name[0] == args->name[0] &&
-		    memcmp(dep->name, args->name, args->namelen) == 0) {
+		 *
+		 * lookup case - use nameops;
+		 *
+		 * replace/remove case - as lookup has been already been
+		 * performed, look for an exact match using the fast method
+		 */
+		cmp = args->oknoent ?
+			xfs_dir_compname(dp, dep->name, dep->namelen,
+						args->name, args->namelen) :
+			xfs_default_compname(dp, dep->name, dep->namelen,
+						args->name, args->namelen);
+		if (cmp != XFS_CMP_DIFFERENT && cmp != args->cmpresult) {
+			args->cmpresult = cmp;
 			*dbpp = dbp;
 			*indexp = index;
-			return 0;
+			if (cmp == XFS_CMP_EXACT)
+				return 0;
 		}
 	}
+	ASSERT(args->oknoent);
+	if (args->cmpresult == XFS_CMP_CASE)
+		return 0;
 	/*
 	 * No match found, return ENOENT.
 	 */
-	ASSERT(args->oknoent);
 	if (dbp)
 		xfs_da_brelse(tp, dbp);
 	xfs_da_brelse(tp, lbp);

===========================================================================
fs/xfs/xfs_dir2_node.c
===========================================================================

--- a/fs/xfs/xfs_dir2_node.c	2008-01-21 14:42:50.000000000 +1100
+++ b/fs/xfs/xfs_dir2_node.c	2008-01-21 14:35:07.954595095 +1100
@@ -39,6 +39,7 @@
 #include "xfs_dir2_node.h"
 #include "xfs_dir2_trace.h"
 #include "xfs_error.h"
+#include "xfs_unicode.h"
 
 /*
  * Function declarations.
@@ -414,6 +415,7 @@ xfs_dir2_leafn_lookup_int(
 	xfs_dir2_db_t		newdb;		/* new data block number */
 	xfs_dir2_db_t		newfdb;		/* new free block number */
 	xfs_trans_t		*tp;		/* transaction pointer */
+	xfs_dacmp_t		cmp;		/* comparison result */
 
 	dp = args->dp;
 	tp = args->trans;
@@ -455,6 +457,7 @@ xfs_dir2_leafn_lookup_int(
 	/*
 	 * Loop over leaf entries with the right hash value.
 	 */
+	args->cmpresult = XFS_CMP_DIFFERENT;
 	for (lep = &leaf->ents[index];
 	     index < be16_to_cpu(leaf->hdr.count) && be32_to_cpu(lep->hashval) == args->hashval;
 	     lep++, index++) {
@@ -572,28 +575,36 @@ xfs_dir2_leafn_lookup_int(
 			/*
 			 * Point to the data entry.
 			 */
-			dep = (xfs_dir2_data_entry_t *)
-			      ((char *)curbp->data +
-			       xfs_dir2_dataptr_to_off(mp, be32_to_cpu(lep->address)));
+			dep = (xfs_dir2_data_entry_t *)((char *)curbp->data +
+				xfs_dir2_dataptr_to_off(mp,
+					be32_to_cpu(lep->address)));
 			/*
 			 * Compare the entry, return it if it matches.
 			 */
-			if (dep->namelen == args->namelen &&
-			    dep->name[0] == args->name[0] &&
-			    memcmp(dep->name, args->name, args->namelen) == 0) {
+			cmp = args->oknoent ?
+				xfs_dir_compname(dp, dep->name, dep->namelen,
+						args->name, args->namelen):
+				xfs_default_compname(dp, dep->name,
+						dep->namelen, args->name,
+						args->namelen);
+			if (cmp != XFS_CMP_DIFFERENT &&
+					cmp != args->cmpresult) {
+				args->cmpresult = cmp;
 				args->inumber = be64_to_cpu(dep->inumber);
 				*indexp = index;
 				state->extravalid = 1;
 				state->extrablk.bp = curbp;
 				state->extrablk.blkno = curdb;
-				state->extrablk.index =
-					(int)((char *)dep -
-					      (char *)curbp->data);
+				state->extrablk.index = (int)((char *)dep -
+							(char *)curbp->data);
 				state->extrablk.magic = XFS_DIR2_DATA_MAGIC;
-				return XFS_ERROR(EEXIST);
+				if (cmp == XFS_CMP_EXACT)
+					return XFS_ERROR(EEXIST);
 			}
 		}
 	}
+	if (args->cmpresult == XFS_CMP_CASE)
+		return XFS_ERROR(EEXIST);
 	/*
 	 * Didn't find a match.
 	 * If we are holding a buffer, give it back in case our caller
@@ -823,16 +834,17 @@ xfs_dir2_leafn_rebalance(
 	 */
 	if (!state->inleaf)
 		blk2->index = blk1->index - be16_to_cpu(leaf1->hdr.count);
-	
-	/* 
-	 * Finally sanity check just to make sure we are not returning a negative index 
+
+	/*
+	 * Finally sanity check just to make sure we are not returning a
+	 * negative index
 	 */
 	if(blk2->index < 0) {
 		state->inleaf = 1;
 		blk2->index = 0;
 		cmn_err(CE_ALERT,
-			"xfs_dir2_leafn_rebalance: picked the wrong leaf? reverting original leaf: "
-			"blk1->index %d\n",
+			"xfs_dir2_leafn_rebalance: picked the wrong leaf? "
+			"reverting original leaf: blk1->index %d\n",
 			blk1->index);
 	}
 }
@@ -1768,6 +1780,20 @@ xfs_dir2_node_lookup(
 	if (error)
 		rval = error;
 	/*
+	 * If case-insens match, copy name out
+	 */
+	if (args->cmpresult == XFS_CMP_CASE) {
+		xfs_dir2_data_entry_t	*dep =
+				(xfs_dir2_data_entry_t *)
+					((char *)state->extrablk.bp->data +
+					state->extrablk.index);
+		error = xfs_unicode_to_nls(args->dp->i_mount->m_nls,
+				dep->name, dep->namelen,
+				(char **)&args->value, &args->valuelen);
+		if (error)
+			rval = error;
+	}
+	/*
 	 * Release the btree blocks and leaf block.
 	 */
 	for (i = 0; i < state->path.active; i++) {

===========================================================================
fs/xfs/xfs_dir2_sf.c
===========================================================================

--- a/fs/xfs/xfs_dir2_sf.c	2008-01-21 14:42:51.000000000 +1100
+++ b/fs/xfs/xfs_dir2_sf.c	2008-01-21 14:21:42.201792024 +1100
@@ -38,6 +38,7 @@
 #include "xfs_dir2_leaf.h"
 #include "xfs_dir2_block.h"
 #include "xfs_dir2_trace.h"
+#include "xfs_unicode.h"
 
 /*
  * Prototypes for internal functions.
@@ -701,6 +702,7 @@ xfs_dir2_sf_getdents(
 	filldir_t		filldir)
 {
 	int			i;		/* shortform entry number */
+	int			error = 0;
 	xfs_mount_t		*mp;		/* filesystem mount point */
 	xfs_dir2_dataptr_t	off;		/* current entry's offset */
 	xfs_dir2_sf_entry_t	*sfep;		/* shortform directory entry */
@@ -708,6 +710,8 @@ xfs_dir2_sf_getdents(
 	xfs_dir2_dataptr_t	dot_offset;
 	xfs_dir2_dataptr_t	dotdot_offset;
 	xfs_ino_t		ino;
+	char			*nls_name = NULL; /* NLS name buffer */
+	int			nls_namelen = 0;
 
 	mp = dp->i_mount;
 
@@ -772,6 +776,9 @@ xfs_dir2_sf_getdents(
 		}
 	}
 
+	if (mp->m_nls)
+		nls_name = xfs_alloc_unicode_nls_name();
+
 	/*
 	 * Loop while there are more entries and put'ing works.
 	 */
@@ -789,17 +796,26 @@ xfs_dir2_sf_getdents(
 #if XFS_BIG_INUMS
 		ino += mp->m_inoadd;
 #endif
-
-		if (filldir(dirent, sfep->name, sfep->namelen,
-					    off, ino, DT_UNKNOWN)) {
+		if (mp->m_nls) {
+			error = xfs_unicode_to_nls(mp->m_nls, sfep->name,
+					sfep->namelen, &nls_name, &nls_namelen);
+			if (error)
+				goto out;
+		}
+		if (filldir(dirent,
+				nls_namelen ? nls_name : (char *)sfep->name,
+				nls_namelen ? nls_namelen : sfep->namelen,
+				off, ino, DT_UNKNOWN)) {
 			*offset = off;
-			return 0;
+			goto out;
 		}
 		sfep = xfs_dir2_sf_nextentry(sfp, sfep);
 	}
-
 	*offset = xfs_dir2_db_off_to_dataptr(mp, mp->m_dirdatablk + 1, 0);
-	return 0;
+out:
+	if (mp->m_nls)
+		xfs_free_unicode_nls_name(nls_name);
+	return error;
 }
 
 /*
@@ -812,6 +828,7 @@ xfs_dir2_sf_lookup(
 {
 	xfs_inode_t		*dp;		/* incore directory inode */
 	int			i;		/* entry index */
+	int			error;
 	xfs_dir2_sf_entry_t	*sfep;		/* shortform directory entry */
 	xfs_dir2_sf_t		*sfp;		/* shortform structure */
 
@@ -836,6 +853,7 @@ xfs_dir2_sf_lookup(
 	 */
 	if (args->namelen == 1 && args->name[0] == '.') {
 		args->inumber = dp->i_ino;
+		args->cmpresult = XFS_CMP_EXACT;
 		return XFS_ERROR(EEXIST);
 	}
 	/*
@@ -844,23 +862,44 @@ xfs_dir2_sf_lookup(
 	if (args->namelen == 2 &&
 	    args->name[0] == '.' && args->name[1] == '.') {
 		args->inumber = xfs_dir2_sf_get_inumber(sfp, &sfp->hdr.parent);
+		args->cmpresult = XFS_CMP_EXACT;
 		return XFS_ERROR(EEXIST);
 	}
 	/*
 	 * Loop over all the entries trying to match ours.
 	 */
-	for (i = 0, sfep = xfs_dir2_sf_firstentry(sfp);
-	     i < sfp->hdr.count;
-	     i++, sfep = xfs_dir2_sf_nextentry(sfp, sfep)) {
-		if (sfep->namelen == args->namelen &&
-		    sfep->name[0] == args->name[0] &&
-		    memcmp(args->name, sfep->name, args->namelen) == 0) {
-			args->inumber =
-				xfs_dir2_sf_get_inumber(sfp,
-					xfs_dir2_sf_inumberp(sfep));
+	args->cmpresult = XFS_CMP_DIFFERENT;
+	for (i = 0, sfep = xfs_dir2_sf_firstentry(sfp); i < sfp->hdr.count;
+			i++, sfep = xfs_dir2_sf_nextentry(sfp, sfep)) {
+		switch (xfs_dir_compname(dp, sfep->name, sfep->namelen,
+				args->name, args->namelen)) {
+		case XFS_CMP_EXACT:
+			args->cmpresult = XFS_CMP_EXACT;
+			args->inumber = xfs_dir2_sf_get_inumber(sfp,
+						xfs_dir2_sf_inumberp(sfep));
+			if (args->value) {
+				xfs_free_unicode_nls_name(args->value);
+				args->value = NULL;
+			}
 			return XFS_ERROR(EEXIST);
+
+		case XFS_CMP_CASE:
+			if (!args->value) {
+				error = xfs_unicode_to_nls(
+					args->dp->i_mount->m_nls, sfep->name,
+					sfep->namelen, (char **)&args->value,
+					&args->valuelen);
+				if (error)
+					return XFS_ERROR(error);
+				args->cmpresult = XFS_CMP_CASE;
+				args->inumber = xfs_dir2_sf_get_inumber(sfp,
+						xfs_dir2_sf_inumberp(sfep));
+			}
+		default: ;
 		}
 	}
+	if (args->cmpresult == XFS_CMP_CASE)
+		return XFS_ERROR(EEXIST);
 	/*
 	 * Didn't find it.
 	 */
@@ -907,9 +946,8 @@ xfs_dir2_sf_removename(
 	for (i = 0, sfep = xfs_dir2_sf_firstentry(sfp);
 	     i < sfp->hdr.count;
 	     i++, sfep = xfs_dir2_sf_nextentry(sfp, sfep)) {
-		if (sfep->namelen == args->namelen &&
-		    sfep->name[0] == args->name[0] &&
-		    memcmp(sfep->name, args->name, args->namelen) == 0) {
+		if (xfs_default_compname(dp, sfep->name, sfep->namelen,
+				args->name, args->namelen) == XFS_CMP_EXACT) {
 			ASSERT(xfs_dir2_sf_get_inumber(sfp,
 					xfs_dir2_sf_inumberp(sfep)) ==
 				args->inumber);
@@ -1044,9 +1082,9 @@ xfs_dir2_sf_replace(
 		for (i = 0, sfep = xfs_dir2_sf_firstentry(sfp);
 		     i < sfp->hdr.count;
 		     i++, sfep = xfs_dir2_sf_nextentry(sfp, sfep)) {
-			if (sfep->namelen == args->namelen &&
-			    sfep->name[0] == args->name[0] &&
-			    memcmp(args->name, sfep->name, args->namelen) == 0) {
+			if (xfs_default_compname(dp, sfep->name,
+					sfep->namelen, args->name,
+					args->namelen) == XFS_CMP_EXACT) {
 #if XFS_BIG_INUMS || defined(DEBUG)
 				ino = xfs_dir2_sf_get_inumber(sfp,
 					xfs_dir2_sf_inumberp(sfep));

===========================================================================
fs/xfs/xfs_itable.c
===========================================================================

--- a/fs/xfs/xfs_itable.c	2008-01-21 14:42:51.000000000 +1100
+++ b/fs/xfs/xfs_itable.c	2008-01-18 13:45:10.740061749 +1100
@@ -45,6 +45,8 @@ xfs_internal_inum(
 	xfs_ino_t	ino)
 {
 	return (ino == mp->m_sb.sb_rbmino || ino == mp->m_sb.sb_rsumino ||
+		(xfs_sb_version_hasunicode(&mp->m_sb) &&
+		 ino == mp->m_sb.sb_cftino) ||
 		(XFS_SB_VERSION_HASQUOTA(&mp->m_sb) &&
 		 (ino == mp->m_sb.sb_uquotino || ino == mp->m_sb.sb_gquotino)));
 }

===========================================================================
fs/xfs/xfs_mount.c
===========================================================================

--- a/fs/xfs/xfs_mount.c	2008-01-21 14:42:51.000000000 +1100
+++ b/fs/xfs/xfs_mount.c	2008-01-21 14:27:33.000000000 +1100
@@ -25,6 +25,7 @@
 #include "xfs_sb.h"
 #include "xfs_ag.h"
 #include "xfs_dir2.h"
+#include "xfs_attr.h"
 #include "xfs_dmapi.h"
 #include "xfs_mount.h"
 #include "xfs_bmap_btree.h"
@@ -43,6 +44,9 @@
 #include "xfs_rw.h"
 #include "xfs_quota.h"
 #include "xfs_fsops.h"
+#include "xfs_da_btree.h"
+#include "xfs_attr_leaf.h"
+#include "xfs_unicode.h"
 
 STATIC void	xfs_mount_log_sbunit(xfs_mount_t *, __int64_t);
 STATIC int	xfs_uuid_mount(xfs_mount_t *);
@@ -119,6 +123,8 @@ static const struct {
     { offsetof(xfs_sb_t, sb_logsectsize),0 },
     { offsetof(xfs_sb_t, sb_logsunit),	 0 },
     { offsetof(xfs_sb_t, sb_features2),	 0 },
+    { offsetof(xfs_sb_t, sb_bad_features2), 0 },
+    { offsetof(xfs_sb_t, sb_cftino),	 0 },
     { sizeof(xfs_sb_t),			 0 }
 };
 
@@ -165,6 +171,9 @@ xfs_mount_free(
 			  sizeof(xfs_perag_t) * mp->m_sb.sb_agcount);
 	}
 
+	if (mp->m_cft)
+		xfs_unicode_free_cft(mp->m_cft);
+
 	spinlock_destroy(&mp->m_ail_lock);
 	spinlock_destroy(&mp->m_sb_lock);
 	mutex_destroy(&mp->m_ilock);
@@ -449,6 +458,8 @@ xfs_sb_from_disk(
 	to->sb_logsectsize = be16_to_cpu(from->sb_logsectsize);
 	to->sb_logsunit = be32_to_cpu(from->sb_logsunit);
 	to->sb_features2 = be32_to_cpu(from->sb_features2);
+	to->sb_bad_features2 = be32_to_cpu(from->sb_bad_features2);
+	to->sb_cftino = be64_to_cpu(from->sb_cftino);
 }
 
 /*
@@ -1056,7 +1067,7 @@ xfs_mountfs(
 	/*
 	 * Initialize the attribute manager's entries.
 	 */
-	mp->m_attr_magicpct = (mp->m_sb.sb_blocksize * 37) / 100;
+	xfs_attr_mount(mp);
 
 	/*
 	 * Initialize the precomputed transaction reservations values.
@@ -1159,6 +1170,18 @@ xfs_mountfs(
 	}
 
 	/*
+	 * Load in unicode case folding table from disk
+	 */
+	if (xfs_sb_version_hasunicode(&mp->m_sb)) {
+		error = xfs_unicode_read_cft(mp);
+		if (error) {
+			cmn_err(CE_WARN,
+				"XFS: failed to read case folding table");
+			goto error4;
+		}
+	}
+
+	/*
 	 * If fs is not mounted readonly, then update the superblock
 	 * unit and width changes.
 	 */
@@ -1214,6 +1237,8 @@ xfs_mountfs(
 	 * Free up the root inode.
 	 */
 	VN_RELE(rvp);
+	if (mp->m_cft)
+		xfs_unicode_free_cft(mp->m_cft);
  error3:
 	xfs_log_unmount_dealloc(mp);
  error2:

===========================================================================
fs/xfs/xfs_mount.h
===========================================================================

--- a/fs/xfs/xfs_mount.h	2008-01-21 14:42:51.000000000 +1100
+++ b/fs/xfs/xfs_mount.h	2008-01-21 14:27:33.000000000 +1100
@@ -54,6 +54,7 @@ typedef struct xfs_trans_reservations {
 #else
 struct cred;
 struct log;
+struct nls_table;
 struct xfs_mount_args;
 struct xfs_inode;
 struct xfs_bmbt_irec;
@@ -61,6 +62,8 @@ struct xfs_bmap_free;
 struct xfs_extdelta;
 struct xfs_swapext;
 struct xfs_mru_cache;
+struct xfs_nameops;
+struct xfs_cft;
 
 /*
  * Prototypes and functions for the Data Migration subsystem.
@@ -312,6 +315,10 @@ typedef struct xfs_mount {
 	__uint8_t		m_inode_quiesce;/* call quiesce on new inodes.
 						   field governed by m_ilock */
 	__uint8_t		m_sectbb_log;	/* sectlog - BBSHIFT */
+	const struct xfs_nameops *m_dirnameops;	/* vector of dir name ops */
+	const struct xfs_nameops *m_attrnameops; /* vector of attr name ops */
+	const struct xfs_cft	*m_cft;		/* unicode case fold table */
+	struct nls_table 	*m_nls;		/* active NLS table */
 	int			m_dirblksize;	/* directory block sz--bytes */
 	int			m_dirblkfsbs;	/* directory block sz--fsbs */
 	xfs_dablk_t		m_dirdatablk;	/* blockno of dir data v2 */
@@ -377,7 +384,10 @@ typedef struct xfs_mount {
 						   counters */
 #define XFS_MOUNT_FILESTREAMS	(1ULL << 24)	/* enable the filestreams
 						   allocator */
-
+#define XFS_MOUNT_CI_LOOKUP	(1ULL << 25)	/* enable case-insensitive
+						 * file lookup */
+#define XFS_MOUNT_CI_ATTR	(1ULL << 26)	/* enable case-insensitive
+						   attribute names */
 
 /*
  * Default minimum read and write sizes.

===========================================================================
fs/xfs/xfs_rename.c
===========================================================================

--- a/fs/xfs/xfs_rename.c	2008-01-21 14:42:51.000000000 +1100
+++ b/fs/xfs/xfs_rename.c	2008-01-17 12:25:31.652529581 +1100
@@ -130,7 +130,9 @@ xfs_lock_for_rename(
 		lock_mode = xfs_ilock_map_shared(dp2);
 	}
 
-	error = xfs_dir_lookup_int(dp2, lock_mode, vname2, &inum2, &ip2);
+	error = xfs_dir_lookup_int(dp2, lock_mode, vname2, &inum2, &ip2,
+			NULL, NULL);
+
 	if (error == ENOENT) {		/* target does not need to exist. */
 		inum2 = 0;
 	} else if (error) {
@@ -214,6 +216,7 @@ xfs_lock_for_rename(
 	for (;i < 4; i++) {
 		i_tab[i] = NULL;
 	}
+
 	return 0;
 }
 

===========================================================================
fs/xfs/xfs_sb.h
===========================================================================

--- a/fs/xfs/xfs_sb.h	2008-01-21 14:42:51.000000000 +1100
+++ b/fs/xfs/xfs_sb.h	2008-01-21 14:41:29.721741469 +1100
@@ -46,10 +46,12 @@ struct xfs_mount;
 #define XFS_SB_VERSION_SECTORBIT	0x0800
 #define	XFS_SB_VERSION_EXTFLGBIT	0x1000
 #define	XFS_SB_VERSION_DIRV2BIT		0x2000
+#define XFS_SB_VERSION_OLDCIBIT		0x4000	/* ASCII only case-insens. */
 #define	XFS_SB_VERSION_MOREBITSBIT	0x8000
 #define	XFS_SB_VERSION_OKSASHFBITS	\
 	(XFS_SB_VERSION_EXTFLGBIT | \
-	 XFS_SB_VERSION_DIRV2BIT)
+	 XFS_SB_VERSION_DIRV2BIT | \
+	 XFS_SB_VERSION_OLDCIBIT)
 #define	XFS_SB_VERSION_OKREALFBITS	\
 	(XFS_SB_VERSION_ATTRBIT | \
 	 XFS_SB_VERSION_NLINKBIT | \
@@ -77,10 +79,12 @@ struct xfs_mount;
 #define XFS_SB_VERSION2_LAZYSBCOUNTBIT	0x00000002	/* Superblk counters */
 #define XFS_SB_VERSION2_RESERVED4BIT	0x00000004
 #define XFS_SB_VERSION2_ATTR2BIT	0x00000008	/* Inline attr rework */
+#define XFS_SB_VERSION2_UNICODEBIT	0x00000020	/* Unicode names */
 
 #define	XFS_SB_VERSION2_OKREALFBITS	\
 	(XFS_SB_VERSION2_LAZYSBCOUNTBIT	| \
-	 XFS_SB_VERSION2_ATTR2BIT)
+	 XFS_SB_VERSION2_ATTR2BIT | \
+	 XFS_SB_VERSION2_UNICODEBIT)
 #define	XFS_SB_VERSION2_OKSASHFBITS	\
 	(0)
 #define XFS_SB_VERSION2_OKREALBITS	\
@@ -145,6 +149,9 @@ typedef struct xfs_sb {
 	__uint16_t	sb_logsectsize;	/* sector size for the log, bytes */
 	__uint32_t	sb_logsunit;	/* stripe unit size for the log */
 	__uint32_t	sb_features2;	/* additional feature bits */
+	__uint32_t	sb_bad_features2; /* bad mkfs set features2 here */
+	xfs_ino_t	sb_cftino;	/* unicode case folding table inode */
+	/* must be padded to 64 bit alignment */
 } xfs_sb_t;
 
 /*
@@ -205,6 +212,9 @@ typedef struct xfs_dsb {
 	__be16		sb_logsectsize;	/* sector size for the log, bytes */
 	__be32		sb_logsunit;	/* stripe unit size for the log */
 	__be32		sb_features2;	/* additional feature bits */
+	__be32		sb_bad_features2; /* bad mkfs set features2 here */
+	__be64		sb_cftino;	/* unicode case folding table inode */
+	/* must be padded to 64 bit alignment */
 } xfs_dsb_t;
 
 /*
@@ -223,7 +233,7 @@ typedef enum {
 	XFS_SBS_GQUOTINO, XFS_SBS_QFLAGS, XFS_SBS_FLAGS, XFS_SBS_SHARED_VN,
 	XFS_SBS_INOALIGNMT, XFS_SBS_UNIT, XFS_SBS_WIDTH, XFS_SBS_DIRBLKLOG,
 	XFS_SBS_LOGSECTLOG, XFS_SBS_LOGSECTSIZE, XFS_SBS_LOGSUNIT,
-	XFS_SBS_FEATURES2,
+	XFS_SBS_FEATURES2, XFS_SBS_BAD_FEATURES2, XFS_SBS_CFTINO,
 	XFS_SBS_FIELDCOUNT
 } xfs_sb_field_t;
 
@@ -248,13 +258,15 @@ typedef enum {
 #define XFS_SB_IFREE		XFS_SB_MVAL(IFREE)
 #define XFS_SB_FDBLOCKS		XFS_SB_MVAL(FDBLOCKS)
 #define XFS_SB_FEATURES2	XFS_SB_MVAL(FEATURES2)
+#define XFS_SB_CASEFOLDINO	XFS_SB_MVAL(CASEFOLDINO)
 #define	XFS_SB_NUM_BITS		((int)XFS_SBS_FIELDCOUNT)
 #define	XFS_SB_ALL_BITS		((1LL << XFS_SB_NUM_BITS) - 1)
 #define	XFS_SB_MOD_BITS		\
 	(XFS_SB_UUID | XFS_SB_ROOTINO | XFS_SB_RBMINO | XFS_SB_RSUMINO | \
 	 XFS_SB_VERSIONNUM | XFS_SB_UQUOTINO | XFS_SB_GQUOTINO | \
 	 XFS_SB_QFLAGS | XFS_SB_SHARED_VN | XFS_SB_UNIT | XFS_SB_WIDTH | \
-	 XFS_SB_ICOUNT | XFS_SB_IFREE | XFS_SB_FDBLOCKS | XFS_SB_FEATURES2)
+	 XFS_SB_ICOUNT | XFS_SB_IFREE | XFS_SB_FDBLOCKS | XFS_SB_FEATURES2 | \
+	 XFS_SB_CFTINO)
 
 
 /*
@@ -463,6 +475,12 @@ static inline int xfs_sb_version_hassect
 		((sbp)->sb_versionnum & XFS_SB_VERSION_SECTORBIT);
 }
 
+static inline int xfs_sb_version_hasoldci(xfs_sb_t *sbp)
+{
+	return (XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_4) && \
+		((sbp)->sb_versionnum & XFS_SB_VERSION_OLDCIBIT);
+}
+
 #define XFS_SB_VERSION_HASMOREBITS(sbp)	xfs_sb_version_hasmorebits(sbp)
 static inline int xfs_sb_version_hasmorebits(xfs_sb_t *sbp)
 {
@@ -502,6 +520,13 @@ static inline void xfs_sb_version_addatt
 		((sbp)->sb_features2 | XFS_SB_VERSION2_ATTR2BIT)));
 }
 
+static inline int xfs_sb_version_hasunicode(xfs_sb_t *sbp)
+{
+	return (xfs_sb_version_hasmorebits(sbp) &&	\
+		((sbp)->sb_features2 & XFS_SB_VERSION2_UNICODEBIT));
+}
+
+
 /*
  * end of superblock version macros
  */

===========================================================================
fs/xfs/xfs_unicode.c
===========================================================================

--- a/fs/xfs/xfs_unicode.c	2006-06-17 00:58:24.000000000 +1000
+++ b/fs/xfs/xfs_unicode.c	2008-01-21 14:10:29.048257871 +1100
@@ -0,0 +1,530 @@
+/*
+ * Copyright (c) 2007 Silicon Graphics, Inc.
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include "xfs.h"
+#include "xfs_fs.h"
+#include "xfs_bit.h"
+#include "xfs_log.h"
+#include "xfs_inum.h"
+#include "xfs_clnt.h"
+#include "xfs_trans.h"
+#include "xfs_sb.h"
+#include "xfs_ag.h"
+#include "xfs_dir2.h"
+#include "xfs_alloc.h"
+#include "xfs_dmapi.h"
+#include "xfs_mount.h"
+#include "xfs_bmap_btree.h"
+#include "xfs_alloc_btree.h"
+#include "xfs_ialloc_btree.h"
+#include "xfs_dir2_sf.h"
+#include "xfs_attr_sf.h"
+#include "xfs_dinode.h"
+#include "xfs_inode.h"
+#include "xfs_btree.h"
+#include "xfs_ialloc.h"
+#include "xfs_itable.h"
+#include "xfs_rtalloc.h"
+#include "xfs_error.h"
+#include "xfs_bmap.h"
+#include "xfs_rw.h"
+#include "xfs_unicode.h"
+
+#define MAX_FOLD_CHARS	4
+
+static kmem_zone_t	*xfs_nls_uni_zone;
+
+static inline int
+xfs_casefold(
+	const xfs_cft_t *cft,
+	__uint16_t	c,
+	__uint16_t	*fc)
+{
+	__uint16_t	*table = XFS_CFT_PTR(cft, 0);
+	__uint16_t	tmp = table[c >> 8];
+	int		i;
+
+	if (!tmp) {
+		*fc = c;
+		return 1;
+	}
+	tmp = table[tmp + (c & 0xff)];
+	if ((tmp & 0xf000) != 0xe000) {
+		*fc = tmp;
+		return 1;
+	}
+	i = ((tmp >> 10) & 0x3) + 2;
+	ASSERT(i < cft->num_tables);
+	table = XFS_CFT_PTR(cft, i - 1) + ((tmp & 0x3ff) * i);
+
+	memcpy(fc, table, sizeof(__uint16_t) * i);
+
+	return i;
+}
+
+static inline int
+xfs_utf8_casefold(
+	const xfs_cft_t	*cft,
+	const uchar_t	**name,
+	int		*namelen,
+	__uint16_t	*fc)
+{
+	wchar_t		uc;
+
+	if (*namelen == 0)
+		return 0;
+
+	if (**name & 0x80) {
+		int	n = utf8_mbtowc(&uc, *name, *namelen);
+		if (n < 0) {
+			(*namelen)--;
+			*fc = *(*name)++;
+			return 1;
+		}
+		*name += n;
+		*namelen -= n;
+	} else {
+		uc = *(*name)++;
+		(*namelen)--;
+	}
+	return xfs_casefold(cft, uc, fc);
+}
+
+__uint32_t
+xfs_unicode_hash(
+	const xfs_cft_t	*cft,
+	const uchar_t	*name,
+	int		namelen)
+{
+	__uint32_t	hash = 0;
+	__uint16_t	fc[MAX_FOLD_CHARS];
+	int		nfc;
+	int		i;
+
+	while (namelen > 0) {
+		nfc = xfs_utf8_casefold(cft, &name, &namelen, fc);
+		for (i = 0; i < nfc; i++)
+			hash = fc[i] ^ rol32(hash, 7);
+	}
+	return hash;
+}
+
+int
+xfs_unicode_casecmp(
+	const xfs_cft_t	*cft,
+	const uchar_t	*name1,
+	int		len1,
+	const uchar_t	*name2,
+	int		len2)
+{
+	__uint16_t	fc1[MAX_FOLD_CHARS], fc2[MAX_FOLD_CHARS];
+	__uint16_t	*pfc1, *pfc2;
+	int		nfc1, nfc2;
+
+	nfc1 = xfs_utf8_casefold(cft, &name1, &len1, fc1);
+	pfc1 = fc1;
+	nfc2 = xfs_utf8_casefold(cft, &name2, &len2, fc2);
+	pfc2 = fc2;
+
+	while (nfc1 > 0 && nfc2 > 0) {
+		if (*pfc1 != *pfc2)
+			return (*pfc1 < *pfc2) ? -1 : 1;
+		if (!--nfc1) {
+			nfc1 = xfs_utf8_casefold(cft, &name1, &len1, fc1);
+			pfc1 = fc1;
+		} else
+			pfc1++;
+		if (!--nfc2) {
+			nfc2 = xfs_utf8_casefold(cft, &name2, &len2, fc2);
+			pfc2 = fc2;
+		} else
+			pfc2++;
+	}
+	if (nfc1 != nfc2)
+		return (nfc1 < nfc2) ? -1 : 1;
+	return 0;
+}
+
+
+char *
+xfs_alloc_unicode_nls_name(void)
+{
+	return kmem_zone_alloc(xfs_nls_uni_zone, KM_SLEEP);
+}
+
+
+void
+xfs_free_unicode_nls_name(
+	char		*name)
+{
+	kmem_zone_free(xfs_nls_uni_zone, name);
+}
+
+int
+xfs_nls_to_unicode(
+	struct nls_table *nls,
+	const char	*nls_name,
+	int		nls_namelen,
+	char		**uni_name,
+	int		*uni_namelen)
+{
+	char		*n;
+	int		i, o;
+	wchar_t		uc;
+	int		nlen;
+	int		u8len;
+	int		error = 0;
+
+	n = *uni_name ? *uni_name : xfs_alloc_unicode_nls_name();
+
+	if (!nls) {
+		if (nls_namelen > MAXNAMELEN) {
+			error = ENAMETOOLONG;
+			goto err_out;
+		}
+		memcpy(n, nls_name, nls_namelen);
+		*uni_name = n;
+		*uni_namelen = nls_namelen;
+		return 0;
+	}
+
+	for (i = 0, o = 0; i < nls_namelen; i += nlen, o += u8len) {
+		nlen = nls->char2uni(nls_name + i, nls_namelen - i, &uc);
+		if (nlen < 0) {
+			error = -nlen;
+			goto err_out;
+		}
+		if (uc >= 0xfffe || (uc >= 0xd800 && uc <= 0xdfff)) {
+			error = EINVAL;	/* don't support chars outside BMP */
+			goto err_out;
+		}
+		u8len = utf8_wctomb(n + o, uc, MAXNAMELEN - o);
+		if (u8len <= 0) {
+			error = (MAXNAMELEN - o < 3) ? ENAMETOOLONG : EINVAL;
+			goto err_out;
+		}
+	}
+	*uni_name = n;
+	*uni_namelen = o;
+	return 0;
+err_out:
+	if (*uni_name == NULL)
+		xfs_free_unicode_nls_name(n);
+	return error;
+}
+
+int
+xfs_unicode_to_nls(
+	struct nls_table *nls,
+	const char	*uni_name,
+	int		uni_namelen,
+	char		**nls_name,
+	int		*nls_namelen)
+{
+	char		*n;
+	int		i, o;
+	wchar_t		uc;
+	int		nlen;
+	int		u8len;
+	int		error = 0;
+
+	n = *nls_name ? *nls_name : xfs_alloc_unicode_nls_name();
+
+	if (!nls) {
+		if (uni_namelen > MAXNAMELEN) {
+			error = ENAMETOOLONG;
+			goto err_out;
+		}
+		memcpy(n, uni_name, uni_namelen);
+		*nls_name = n;
+		*nls_namelen = uni_namelen;
+		return 0;
+	}
+
+	for (i = 0, o = 0; i < uni_namelen && o < MAXNAMELEN;
+			i += u8len, o += nlen) {
+		u8len = utf8_mbtowc(&uc, uni_name + i, uni_namelen - i);
+		if (u8len < 0) {
+			error = EINVAL;
+			goto err_out;
+		}
+		nlen = nls->uni2char(uc, n + o, MAXNAMELEN - o);
+		if (nlen == -EINVAL) {
+			n[o] = '?';
+			nlen = 1;
+		} else if (nlen < 0) {
+			error = -nlen;
+			goto err_out;
+		}
+	}
+	if (i == uni_namelen) {
+		*nls_name = n;
+		*nls_namelen = o;
+		return 0;
+	}
+
+	error = ENAMETOOLONG;
+err_out:
+	if (*nls_name == NULL)
+		xfs_free_unicode_nls_name(n);
+	return error;
+}
+
+int
+xfs_unicode_validate(
+	const uchar_t	*name,
+	int		namelen)
+{
+	wchar_t		uc;
+	int		i, nlen;
+
+	for (i = 0; i < namelen; i += nlen) {
+		if (*name >= 0xf0) {
+			cmn_err(CE_WARN, "xfs_unicode_validate: "
+					"UTF-8 char beyond U+FFFF\n");
+			return EINVAL;
+		}
+		/* utf8_mbtowc must fail on overlong sequences too */
+		nlen = utf8_mbtowc(&uc, name + i, namelen - i);
+		if (nlen < 0) {
+			cmn_err(CE_WARN, "xfs_unicode_validate: "
+					"invalid UTF-8 sequence\n");
+			return EILSEQ;
+		}
+		/* check for invalid/surrogate/private unicode chars */
+		if (uc >= 0xfffe || (uc >= 0xd800 && uc <= 0xf8ff)) {
+			cmn_err(CE_WARN, "xfs_unicode_validate: "
+					"unsupported UTF-8 char\n");
+			return EINVAL;
+		}
+	}
+	return 0;
+}
+
+/*
+ * Unicode Case Fold Table management
+ */
+
+struct cft_item {
+	xfs_cft_t	*table;
+	int		size;
+	int		refcount;
+};
+
+static mutex_t		cft_lock;
+static int		cft_size;
+static struct cft_item	*cft_list;
+
+static xfs_cft_t *
+add_cft(
+	xfs_dcft_t	*dcft,
+	int		size)
+{
+	int		found = 0;
+	int		i, j;
+	xfs_cft_t	*cft;
+	__be16		*duc;
+	__uint16_t	*uc;
+
+	mutex_lock(&cft_lock);
+
+	for (i = 0; i < cft_size; i++) {
+		if (cft_list[i].size != size)
+			continue;
+		cft = cft_list[i].table;
+		if (cft->num_tables != be32_to_cpu(dcft->num_tables) ||
+				cft->flags != be32_to_cpu(dcft->flags))
+			continue;
+		found = 1;
+		for (j = 0; j < cft->num_tables; j++) {
+			if (cft->table_offset[j] !=
+					be32_to_cpu(dcft->table_offset[j])) {
+				found = 0;
+				break;
+			}
+		}
+		if (found) {
+			cft_list[i].refcount++;
+			mutex_unlock(&cft_lock);
+			return cft;
+		}
+	}
+
+	cft = vmalloc(size);
+	if (!cft) {
+		mutex_unlock(&cft_lock);
+		return NULL;
+	}
+	cft->magic = be32_to_cpu(dcft->magic);
+	cft->flags = be32_to_cpu(dcft->flags);
+	cft->num_tables = be32_to_cpu(dcft->num_tables);
+	ASSERT(cft->num_tables <= MAX_FOLD_CHARS);
+	for (i = 0; i < cft->num_tables; i++)
+		cft->table_offset[i] = be32_to_cpu(dcft->table_offset[i]);
+	j = (size - cft->table_offset[0]) / sizeof(__uint16_t);
+	uc = XFS_CFT_PTR(cft, 0);
+	duc = XFS_DCFT_PTR(dcft, 0);
+	for (i = 0; i < j; i++)
+		uc[i] = be16_to_cpu(duc[i]);
+
+	cft_list = kmem_realloc(cft_list,
+			(cft_size + 1) * sizeof(struct cft_item),
+			cft_size  * sizeof(struct cft_item), KM_SLEEP);
+	cft_list[cft_size].table = cft;
+	cft_list[cft_size].size = size;
+	cft_list[cft_size].refcount = 1;
+	cft_size++;
+
+	mutex_unlock(&cft_lock);
+
+	return cft;
+}
+
+static void
+remove_cft(
+	const xfs_cft_t	*cft)
+{
+	int		i;
+
+	mutex_lock(&cft_lock);
+
+	for (i = 0; i < cft_size; i++) {
+		if (cft_list[i].table == cft) {
+			ASSERT(cft_list[i].refcount > 0);
+			cft_list[i].refcount--;
+			break;
+		}
+	}
+
+	mutex_unlock(&cft_lock);
+}
+
+
+int
+xfs_unicode_read_cft(
+	xfs_mount_t	*mp)
+{
+	int		error;
+	xfs_inode_t	*cftip;
+	int		size;
+	int		nfsb;
+	int             nmap;
+	xfs_bmbt_irec_t *mapp;
+	int		n;
+	int		byte_cnt;
+	xfs_buf_t	*bp;
+	char		*table;
+	xfs_dcft_t	*dcft;
+
+	if (mp->m_sb.sb_cftino == NULLFSINO || mp->m_sb.sb_cftino == 0)
+		return EINVAL;
+	error = xfs_iget(mp, NULL, mp->m_sb.sb_cftino, 0, 0, &cftip, 0);
+	if (error)
+		return error;
+	ASSERT(cftip != NULL);
+
+	size = cftip->i_d.di_size;
+	nfsb = cftip->i_d.di_nblocks;
+
+	table = vmalloc(size);
+	if (!table) {
+		xfs_iput(cftip, 0);
+		return ENOMEM;
+	}
+	dcft = (xfs_dcft_t *)table;
+
+	nmap = nfsb;
+	mapp = kmem_alloc(nfsb * sizeof(xfs_bmbt_irec_t), KM_SLEEP);
+
+	error = xfs_bmapi(NULL, cftip, 0, nfsb, 0, NULL, 0, mapp, &nmap,
+			NULL, NULL);
+	if (error)
+		goto out;
+
+	for (n = 0; n < nmap; n++) {
+		byte_cnt = XFS_FSB_TO_B(mp, mapp[n].br_blockcount);
+
+		error = xfs_read_buf(mp, mp->m_ddev_targp,
+				XFS_FSB_TO_DADDR(mp, mapp[n].br_startblock),
+				BTOBB(byte_cnt), 0, &bp);
+		if (error)
+			goto out;
+
+		if (size < byte_cnt)
+			byte_cnt = size;
+		size -= byte_cnt;
+		memcpy(table, XFS_BUF_PTR(bp), byte_cnt);
+		table += byte_cnt;
+		xfs_buf_relse(bp);
+	}
+
+	/* verify case table read off disk */
+	if (!uuid_equal(&dcft->uuid, &mp->m_sb.sb_uuid)) {
+		error = EINVAL;
+		goto out;
+	}
+
+	/* clear UUID for in-memory copy/compare */
+	memset(&dcft->uuid, 0, sizeof(dcft->uuid));
+
+	mp->m_cft = add_cft(dcft, cftip->i_d.di_size);
+	if (mp->m_cft == NULL)
+		error = ENOMEM;
+
+out:
+	xfs_iput(cftip, 0);
+	kmem_free(mapp, nfsb * sizeof(xfs_bmbt_irec_t));
+	vfree(dcft);
+
+	return error;
+}
+
+void
+xfs_unicode_free_cft(
+	const xfs_cft_t	*cft)
+{
+	remove_cft(cft);
+}
+
+void
+xfs_unicode_init(void)
+{
+	mutex_init(&cft_lock);
+	xfs_nls_uni_zone = kmem_zone_init(MAXNAMELEN, "xfs_nls_uni");
+}
+
+void
+xfs_unicode_uninit(void)
+{
+	int		i;
+
+	mutex_lock(&cft_lock);
+
+	for (i = 0; i < cft_size; i++) {
+		ASSERT(cft_list[i].refcount == 0);
+		vfree(cft_list[i].table);
+	}
+	kmem_free(cft_list, cft_size * sizeof(struct cft_item));
+	cft_size = 0;
+	cft_list = NULL;
+
+	mutex_unlock(&cft_lock);
+	mutex_destroy(&cft_lock);
+
+	kmem_zone_destroy(xfs_nls_uni_zone);
+}

===========================================================================
fs/xfs/xfs_unicode.h
===========================================================================

--- a/fs/xfs/xfs_unicode.h	2006-06-17 00:58:24.000000000 +1000
+++ b/fs/xfs/xfs_unicode.h	2008-01-21 14:23:07.266899447 +1100
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2007 Silicon Graphics, Inc.
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+#ifndef __XFS_UNICODE_H__
+#define	__XFS_UNICODE_H__
+
+#define	XFS_CFT_MAGIC		0x58434654	/* 'XCFT' */
+#define XFS_CFT_FLAG_TURKIC	0x00000001
+#define XFS_CFT_FLAG_MAX	0x00000001
+
+/*
+ * Case Fold Table - on disk version. Must match the incore version below.
+ */
+typedef struct xfs_dcft {
+	__be32			magic;		/* validity check */
+	__be32			flags;
+	uuid_t			uuid;		/* UUID of the filesystem */
+	__be32			crc;		/* for future support */
+	__be32			num_tables;	/* single, double, etc */
+	__be32			table_offset[1];
+} xfs_dcft_t;
+
+/*
+ * Case Fold Table - in core version. Must match the ondisk version above.
+ */
+typedef struct xfs_cft {
+	__uint32_t		magic;
+	__uint32_t		flags;
+	uuid_t			uuid;		/* UUID of the filesystem */
+	__uint32_t		crc;
+	__uint32_t		num_tables;	/* single, double, etc */
+	__uint32_t		table_offset[1];/* num_tables sized */
+	/* 16-bit array tables immediately follow */
+} xfs_cft_t;
+
+#define XFS_CFT_PTR(t,n)	(__uint16_t *)(((char *)(t)) + \
+					(t)->table_offset[n])
+#define XFS_DCFT_PTR(t,n)	(__be16 *)(((char *)(t)) + \
+					be32_to_cpu((t)->table_offset[n]))
+
+void xfs_unicode_init(void);
+void xfs_unicode_uninit(void);
+
+__uint32_t xfs_unicode_hash(const xfs_cft_t *cft,
+				const uchar_t *name, int namelen);
+
+int xfs_unicode_casecmp(const xfs_cft_t *cft, const uchar_t *name1,
+				int len1, const uchar_t *name2, int len2);
+
+char *xfs_alloc_unicode_nls_name(void);
+void xfs_free_unicode_nls_name(char *name);
+int xfs_nls_to_unicode(struct nls_table *nls, const char *nls_name,
+			int nls_namelen, char **uni_name, int *uni_namelen);
+int xfs_unicode_to_nls(struct nls_table *nls, const char *uni_name,
+			int uni_namelen, char **nls_name, int *nls_namelen);
+
+int xfs_unicode_validate(const uchar_t *name, int namelen);
+
+int xfs_unicode_read_cft(struct xfs_mount *mp);
+void xfs_unicode_free_cft(const xfs_cft_t *cft);
+
+#endif /* __XFS_UNICODE_H__ */

===========================================================================
fs/xfs/xfs_utils.c
===========================================================================

--- a/fs/xfs/xfs_utils.c	2008-01-21 14:42:51.000000000 +1100
+++ b/fs/xfs/xfs_utils.c	2007-10-26 15:42:04.856766756 +1000
@@ -39,6 +39,7 @@
 #include "xfs_rw.h"
 #include "xfs_itable.h"
 #include "xfs_utils.h"
+#include "xfs_unicode.h"
 
 /*
  * xfs_get_dir_entry is used to get a reference to an inode given
@@ -69,13 +70,16 @@ xfs_dir_lookup_int(
 	uint		lock_mode,
 	bhv_vname_t	*dentry,
 	xfs_ino_t	*inum,
-	xfs_inode_t	**ipp)
+	xfs_inode_t	**ipp,
+	char		**actual_name,
+	int		*actual_namelen)
 {
 	int		error;
 
 	xfs_itrace_entry(dp);
 
-	error = xfs_dir_lookup(NULL, dp, VNAME(dentry), VNAMELEN(dentry), inum);
+	error = xfs_dir_lookup(NULL, dp, VNAME(dentry), VNAMELEN(dentry), inum,
+			actual_name, actual_namelen);
 	if (!error) {
 		/*
 		 * Unlock the directory. We do this because we can't
@@ -102,6 +106,8 @@ xfs_dir_lookup_int(
 			xfs_ilock(dp, lock_mode);
 			error = XFS_ERROR(ENOENT);
 		}
+		if (error && actual_name)
+			xfs_free_unicode_nls_name(*actual_name);
 	}
 	return error;
 }

===========================================================================
fs/xfs/xfs_utils.h
===========================================================================

--- a/fs/xfs/xfs_utils.h	2008-01-21 14:42:51.000000000 +1100
+++ b/fs/xfs/xfs_utils.h	2007-10-26 15:35:54.052564595 +1000
@@ -23,7 +23,7 @@
 
 extern int xfs_get_dir_entry (bhv_vname_t *, xfs_inode_t **);
 extern int xfs_dir_lookup_int (xfs_inode_t *, uint, bhv_vname_t *, xfs_ino_t *,
-				xfs_inode_t **);
+				xfs_inode_t **, char **, int *);
 extern int xfs_truncate_file (xfs_mount_t *, xfs_inode_t *);
 extern int xfs_dir_ialloc (xfs_trans_t **, xfs_inode_t *, mode_t, xfs_nlink_t,
 				xfs_dev_t, cred_t *, prid_t, int,

===========================================================================
fs/xfs/xfs_vfsops.c
===========================================================================

--- a/fs/xfs/xfs_vfsops.c	2008-01-21 14:42:51.000000000 +1100
+++ b/fs/xfs/xfs_vfsops.c	2008-01-11 14:47:36.214448806 +1100
@@ -56,7 +56,7 @@
 #include "xfs_fsops.h"
 #include "xfs_vnodeops.h"
 #include "xfs_vfsops.h"
-
+#include "xfs_unicode.h"
 
 int
 xfs_init(void)
@@ -81,6 +81,7 @@ xfs_init(void)
 	xfs_acl_zone_init(xfs_acl_zone, "xfs_acl");
 	xfs_mru_cache_init();
 	xfs_filestream_init();
+	xfs_unicode_init();
 
 	/*
 	 * The size of the zone allocated buf log item is the maximum
@@ -158,6 +159,7 @@ xfs_cleanup(void)
 	xfs_cleanup_procfs();
 	xfs_sysctl_unregister();
 	xfs_refcache_destroy();
+	xfs_unicode_uninit();
 	xfs_filestream_uninit();
 	xfs_mru_cache_uninit();
 	xfs_acl_zone_destroy(xfs_acl_zone);
@@ -247,7 +249,6 @@ xfs_start_flags(
 		mp->m_logname = kmem_alloc(strlen(ap->logname) + 1, KM_SLEEP);
 		strcpy(mp->m_logname, ap->logname);
 	}
-
 	if (ap->flags & XFSMNT_WSYNC)
 		mp->m_flags |= XFS_MOUNT_WSYNC;
 #if XFS_BIG_INUMS
@@ -404,6 +405,39 @@ xfs_finish_flags(
 			mp->m_qflags |= XFS_OQUOTA_ENFD;
 	}
 
+	if (xfs_sb_version_hasunicode(&mp->m_sb)) {
+		if (ap->flags2 & XFSMNT2_CILOOKUP)
+			mp->m_flags |= XFS_MOUNT_CI_LOOKUP;
+		if (ap->flags2 & XFSMNT2_CIATTR)
+			mp->m_flags |= XFS_MOUNT_CI_ATTR;
+
+		mp->m_nls = ap->nls[0] ? load_nls(ap->nls) : load_nls_default();
+		if (!mp->m_nls) {
+			cmn_err(CE_WARN,
+	"XFS: unable to load nls mapping \"%s\"\n", ap->nls);
+			return XFS_ERROR(EINVAL);
+		}
+		if (strcmp(mp->m_nls->charset, XFS_NLS_UTF8) == 0) {
+			/* special case utf8 - no translation required */
+			unload_nls(mp->m_nls);
+			mp->m_nls = NULL;
+		}
+	} else {
+		/*
+		 * Check for mount options which require a Unicode FS
+		 */
+		if (ap->flags2 & (XFSMNT2_CILOOKUP | XFSMNT2_CIATTR)) {
+			cmn_err(CE_WARN,
+	"XFS: can't do case-insensitive mount on non-utf8 filesystem");
+			return XFS_ERROR(EINVAL);
+
+		}
+		if (ap->nls[0]) {
+			cmn_err(CE_WARN,
+	"XFS: can't use nls mount option on non-utf8 filesystem");
+			return XFS_ERROR(EINVAL);
+		}
+	}
 	return 0;
 }
 
@@ -641,6 +675,8 @@ out:
 		xfs_unmountfs(mp, credp);
 		xfs_qmops_put(mp);
 		xfs_dmops_put(mp);
+		if (mp->m_nls)
+			unload_nls(mp->m_nls);
 		kmem_free(mp, sizeof(xfs_mount_t));
 	}
 

===========================================================================
fs/xfs/xfs_vnodeops.c
===========================================================================

--- a/fs/xfs/xfs_vnodeops.c	2008-01-21 14:42:51.000000000 +1100
+++ b/fs/xfs/xfs_vnodeops.c	2008-01-21 14:27:33.000000000 +1100
@@ -1772,7 +1772,9 @@ int
 xfs_lookup(
 	xfs_inode_t		*dp,
 	bhv_vname_t		*dentry,
-	bhv_vnode_t		**vpp)
+	bhv_vnode_t		**vpp,
+	char			**actual_name,
+	int			*actual_namelen)
 {
 	xfs_inode_t		*ip;
 	xfs_ino_t		e_inum;
@@ -1785,7 +1787,8 @@ xfs_lookup(
 		return XFS_ERROR(EIO);
 
 	lock_mode = xfs_ilock_map_shared(dp);
-	error = xfs_dir_lookup_int(dp, lock_mode, dentry, &e_inum, &ip);
+	error = xfs_dir_lookup_int(dp, lock_mode, dentry, &e_inum, &ip,
+			actual_name, actual_namelen);
 	if (!error) {
 		*vpp = XFS_ITOV(ip);
 		xfs_itrace_ref(ip);

===========================================================================
fs/xfs/xfs_vnodeops.h
===========================================================================

--- a/fs/xfs/xfs_vnodeops.h	2008-01-21 14:42:51.000000000 +1100
+++ b/fs/xfs/xfs_vnodeops.h	2008-01-21 14:27:33.000000000 +1100
@@ -24,7 +24,7 @@ int xfs_fsync(struct xfs_inode *ip, int 
 int xfs_release(struct xfs_inode *ip);
 int xfs_inactive(struct xfs_inode *ip);
 int xfs_lookup(struct xfs_inode *dp, bhv_vname_t *dentry,
-		bhv_vnode_t **vpp);
+		bhv_vnode_t **vpp, char **actual_name, int *actual_namelen);
 int xfs_create(struct xfs_inode *dp, bhv_vname_t *dentry, mode_t mode,
 		xfs_dev_t rdev, bhv_vnode_t **vpp, struct cred *credp);
 int xfs_remove(struct xfs_inode *dp, bhv_vname_t	*dentry);

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [REVIEW 1/2] Case insensitive support for XFS - kernel patch
  2008-01-21  3:53   ` Barry Naujok
@ 2008-01-23  6:55     ` Christoph Hellwig
  0 siblings, 0 replies; 5+ messages in thread
From: Christoph Hellwig @ 2008-01-23  6:55 UTC (permalink / raw)
  To: Barry Naujok; +Cc: Eric Sandeen, xfs@oss.sgi.com, xfs-dev

I'm currently sick in my bed, so just a very quick glance over the
patch and a few comments:

 - no need to export the new inode ops, they're not used by the quota
   or dmapi methods.
 - any chance you could rebase this ontop of my various vnode removals,
   this should make some things a little cleaner.
 - there's a lot of hair dcache internals in here, so please run this
   patch past linux-fsdevel@vger.kernel.org to get some broader review.

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2008-01-23  6:55 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-01-18  4:43 [REVIEW 1/2] Case insensitive support for XFS - kernel patch Barry Naujok
2008-01-19  5:22 ` Eric Sandeen
2008-01-21  3:53   ` Barry Naujok
2008-01-23  6:55     ` Christoph Hellwig
2008-01-19  5:30 ` Eric Sandeen

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox