linux-fsdevel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/7] XFS: case-insensitive lookup and Unicode support
@ 2008-04-02  6:25 Barry Naujok
  2008-04-02  6:25 ` [PATCH 1/7] XFS: Name operation vector for hash and compare Barry Naujok
                   ` (7 more replies)
  0 siblings, 8 replies; 48+ messages in thread
From: Barry Naujok @ 2008-04-02  6:25 UTC (permalink / raw)
  To: xfs; +Cc: linux-fsdevel

The following sequence of patches (must be applied in order) implements
case-insensitive support in XFS in two ways:
  1. ASCII only case-insensitive
  2. Unicode case-insensitive mount
 
ASCII only case-insensitive support is a mkfs option and is primary
implemented to support existing IRIX filesystems migrating to Linux.
 
Unicode support is also a mkfs option, but the case-insensitive mode
is a mount time option.
 
The user space patches were posted back in January:
http://oss.sgi.com/archives/xfs/2008-01/msg00102.html

-- 

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

* [PATCH 1/7] XFS: Name operation vector for hash and compare
  2008-04-02  6:25 [PATCH 0/7] XFS: case-insensitive lookup and Unicode support Barry Naujok
@ 2008-04-02  6:25 ` Barry Naujok
  2008-04-03  0:22   ` Josef 'Jeff' Sipek
  2008-04-03  1:29   ` David Chinner
  2008-04-02  6:25 ` [PATCH 2/7] XFS: ASCII case-insensitive support Barry Naujok
                   ` (6 subsequent siblings)
  7 siblings, 2 replies; 48+ messages in thread
From: Barry Naujok @ 2008-04-02  6:25 UTC (permalink / raw)
  To: xfs; +Cc: linux-fsdevel

[-- Attachment #1: nameops.patch --]
[-- Type: text/plain, Size: 18517 bytes --]

Adds two pieces of functionality for the basis of case-insensitive
support in XFS:

1.  A comparison result enumerated type: xfs_dacmp_t. It represents an
    exact match, case-insensitive match or no match at all. This patch
    only implements different and exact results.

2.  xfs_nameops vector for specifying how to perform the hash generation
    of filenames and comparision methods. In this patch the hash vector
    points to the existing xfs_da_hashname function and the comparison
    method does a length compare, and if the same, does a memcmp and
    return the xfs_dacmp_t result.

All filename functions that use the hash (create, lookup remove, rename,
etc) now use the xfs_nameops.hashname function and all directory lookup
functions also use the xfs_nameops.compname function.

The lookup functions also handle case-insensitive results even though
the default comparison function cannot return that. And important
aspect of the lookup functions is that an exact match always has
precedence over a case-insensitive. So while a case-insensitive match
is found, we have to keep looking just in case there is an exact
match. In the meantime, the info for the first case-insensitive match
is retained if no exact match is found.

Signed-off-by: Barry Naujok <bnaujok@sgi.com>

---
 fs/xfs/xfs_da_btree.c   |   12 ++++++++++++
 fs/xfs/xfs_da_btree.h   |   28 ++++++++++++++++++++++++++++
 fs/xfs/xfs_dir2.c       |   12 +++++++-----
 fs/xfs/xfs_dir2.h       |    6 ++++++
 fs/xfs/xfs_dir2_block.c |   29 ++++++++++++++++++++++-------
 fs/xfs/xfs_dir2_data.c  |    3 ++-
 fs/xfs/xfs_dir2_leaf.c  |   47 ++++++++++++++++++++++++++++++++++++++++-------
 fs/xfs/xfs_dir2_node.c  |   45 +++++++++++++++++++++++++++++++--------------
 fs/xfs/xfs_dir2_sf.c    |   26 ++++++++++++++++----------
 fs/xfs/xfs_mount.h      |    2 ++
 10 files changed, 166 insertions(+), 44 deletions(-)

Index: kern_ci/fs/xfs/xfs_da_btree.c
===================================================================
--- kern_ci.orig/fs/xfs/xfs_da_btree.c
+++ kern_ci/fs/xfs/xfs_da_btree.c
@@ -1530,6 +1530,18 @@ xfs_da_hashname(const uchar_t *name, int
 	}
 }
 
+xfs_dacmp_t
+xfs_da_compname(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;
+}
+
+struct xfs_nameops xfs_default_nameops = {
+	.hashname	= xfs_da_hashname,
+	.compname	= xfs_da_compname
+};
+
 /*
  * Add a block to the btree ahead of the file.
  * Return the new block number to the caller.
Index: kern_ci/fs/xfs/xfs_da_btree.h
===================================================================
--- kern_ci.orig/fs/xfs/xfs_da_btree.h
+++ kern_ci/fs/xfs/xfs_da_btree.h
@@ -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,6 +211,19 @@ 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)(const uchar_t *, int);
+typedef xfs_dacmp_t	(*xfs_compname_t)(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__
 /*========================================================================
@@ -248,7 +271,12 @@ xfs_daddr_t	xfs_da_reada_buf(struct xfs_
 int	xfs_da_shrink_inode(xfs_da_args_t *args, xfs_dablk_t dead_blkno,
 					  xfs_dabuf_t *dead_buf);
 
+extern struct xfs_nameops xfs_default_nameops;
+
 uint xfs_da_hashname(const uchar_t *name_string, int name_length);
+xfs_dacmp_t xfs_da_compname(const uchar_t *name1, int len1,
+			    const uchar_t *name2, int len2);
+
 xfs_da_state_t *xfs_da_state_alloc(void);
 void xfs_da_state_free(xfs_da_state_t *state);
 
Index: kern_ci/fs/xfs/xfs_dir2.c
===================================================================
--- kern_ci.orig/fs/xfs/xfs_dir2.c
+++ kern_ci/fs/xfs/xfs_dir2.c
@@ -64,6 +64,7 @@ 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;
+	mp->m_dirnameops = &xfs_default_nameops;
 }
 
 /*
@@ -164,7 +165,7 @@ xfs_dir_createname(
 
 	args.name = name;
 	args.namelen = namelen;
-	args.hashval = xfs_da_hashname(name, namelen);
+	args.hashval = xfs_dir_hashname(dp, name, namelen);
 	args.inumber = inum;
 	args.dp = dp;
 	args.firstblock = first;
@@ -210,7 +211,7 @@ xfs_dir_lookup(
 
 	args.name = name;
 	args.namelen = namelen;
-	args.hashval = xfs_da_hashname(name, namelen);
+	args.hashval = xfs_dir_hashname(dp, name, namelen);
 	args.inumber = 0;
 	args.dp = dp;
 	args.firstblock = NULL;
@@ -220,6 +221,7 @@ xfs_dir_lookup(
 	args.trans = tp;
 	args.justcheck = args.addname = 0;
 	args.oknoent = 1;
+	args.cmpresult = XFS_CMP_DIFFERENT;
 
 	if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL)
 		rval = xfs_dir2_sf_lookup(&args);
@@ -263,7 +265,7 @@ xfs_dir_removename(
 
 	args.name = name;
 	args.namelen = namelen;
-	args.hashval = xfs_da_hashname(name, namelen);
+	args.hashval = xfs_dir_hashname(dp, name, namelen);
 	args.inumber = ino;
 	args.dp = dp;
 	args.firstblock = first;
@@ -347,7 +349,7 @@ xfs_dir_replace(
 
 	args.name = name;
 	args.namelen = namelen;
-	args.hashval = xfs_da_hashname(name, namelen);
+	args.hashval = xfs_dir_hashname(dp, name, namelen);
 	args.inumber = inum;
 	args.dp = dp;
 	args.firstblock = first;
@@ -390,7 +392,7 @@ xfs_dir_canenter(
 
 	args.name = name;
 	args.namelen = namelen;
-	args.hashval = xfs_da_hashname(name, namelen);
+	args.hashval = xfs_dir_hashname(dp, name, namelen);
 	args.inumber = 0;
 	args.dp = dp;
 	args.firstblock = NULL;
Index: kern_ci/fs/xfs/xfs_dir2.h
===================================================================
--- kern_ci.orig/fs/xfs/xfs_dir2.h
+++ kern_ci/fs/xfs/xfs_dir2.h
@@ -85,6 +85,12 @@ 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((n), (l)))
+
+#define xfs_dir_compname(dp, n1, l1, n2, l2) \
+		((dp)->i_mount->m_dirnameops->compname((n1), (l1), (n2), (l2)))
+
 /*
  * Utility routines for v2 directories.
  */
Index: kern_ci/fs/xfs/xfs_dir2_block.c
===================================================================
--- kern_ci.orig/fs/xfs/xfs_dir2_block.c
+++ kern_ci/fs/xfs/xfs_dir2_block.c
@@ -643,6 +643,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;
@@ -698,19 +699,33 @@ xfs_dir2_block_lookup_int(
 			((char *)block + xfs_dir2_dataptr_to_off(mp, addr));
 		/*
 		 * 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
 		 */
-		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_da_compname(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,7 +1202,7 @@ 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(
+		blp[2 + i].hashval = cpu_to_be32(xfs_dir_hashname(dp,
 					(char *)sfep->name, sfep->namelen));
 		blp[2 + i].address = cpu_to_be32(xfs_dir2_byte_to_dataptr(mp,
 						 (char *)dep - (char *)block));
Index: kern_ci/fs/xfs/xfs_dir2_data.c
===================================================================
--- kern_ci.orig/fs/xfs/xfs_dir2_data.c
+++ kern_ci/fs/xfs/xfs_dir2_data.c
@@ -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)
Index: kern_ci/fs/xfs/xfs_dir2_leaf.c
===================================================================
--- kern_ci.orig/fs/xfs/xfs_dir2_leaf.c
+++ kern_ci/fs/xfs/xfs_dir2_leaf.c
@@ -1331,6 +1331,8 @@ 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_dabuf_t		*cbp;		/* case match data buffer */
+	xfs_dacmp_t		cmp;		/* name compare result */
 
 	dp = args->dp;
 	tp = args->trans;
@@ -1354,6 +1356,7 @@ xfs_dir2_leaf_lookup_int(
 	 * Loop over all the entries with the right hash value
 	 * looking to match the name.
 	 */
+	cbp = NULL;
 	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++) {
@@ -1371,7 +1374,7 @@ xfs_dir2_leaf_lookup_int(
 		 * need to pitch the old one and read the new one.
 		 */
 		if (newdb != curdb) {
-			if (dbp)
+			if (dbp != cbp)
 				xfs_da_brelse(tp, dbp);
 			if ((error =
 			    xfs_da_read_buf(tp, dp,
@@ -1391,19 +1394,49 @@ xfs_dir2_leaf_lookup_int(
 		       xfs_dir2_dataptr_to_off(mp, be32_to_cpu(lep->address)));
 		/*
 		 * If it matches then return it.
+		 *
+		 * lookup case - use nameops;
+		 *
+		 * replace/remove case - as lookup has been already been
+		 * performed, look for an exact match using the fast method
 		 */
-		if (dep->namelen == args->namelen &&
-		    dep->name[0] == args->name[0] &&
-		    memcmp(dep->name, args->name, args->namelen) == 0) {
-			*dbpp = dbp;
+		cmp = args->oknoent ?
+			xfs_dir_compname(dp, dep->name, dep->namelen,
+						args->name, args->namelen) :
+			xfs_da_compname(dep->name, dep->namelen,
+						args->name, args->namelen);
+		if (cmp != XFS_CMP_DIFFERENT && cmp != args->cmpresult) {
+			args->cmpresult = cmp;
 			*indexp = index;
-			return 0;
+			if (cmp == XFS_CMP_EXACT) {
+				/*
+				 * case exact match: release the case-insens.
+				 * match buffer if it exists and return the
+				 * current data buffer.
+				 */
+				if (cbp && cbp != dbp)
+					xfs_da_brelse(tp, cbp);
+				*dbpp = dbp;
+				return 0;
+			}
+			cbp = dbp;
 		}
 	}
+	ASSERT(args->oknoent);
+	if (args->cmpresult == XFS_CMP_CASE) {
+		/*
+		 * case-insensitive match: release current buffer and
+		 * return the buffer with the case-insensitive match.
+		 */
+		if (cbp != dbp)
+			xfs_da_brelse(tp, dbp);
+		*dbpp = cbp;
+		return 0;
+	}
 	/*
 	 * No match found, return ENOENT.
 	 */
-	ASSERT(args->oknoent);
+	ASSERT(cbp == NULL);
 	if (dbp)
 		xfs_da_brelse(tp, dbp);
 	xfs_da_brelse(tp, lbp);
Index: kern_ci/fs/xfs/xfs_dir2_node.c
===================================================================
--- kern_ci.orig/fs/xfs/xfs_dir2_node.c
+++ kern_ci/fs/xfs/xfs_dir2_node.c
@@ -414,6 +414,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;
@@ -578,19 +579,27 @@ xfs_dir2_leafn_lookup_int(
 			/*
 			 * 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_da_compname(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.magic = XFS_DIR2_DATA_MAGIC;
-				return XFS_ERROR(EEXIST);
+				if (cmp == XFS_CMP_EXACT) {
+					state->extravalid = 1;
+					state->extrablk.blkno = curdb;
+					state->extrablk.index =
+						(int)((char *)dep -
+						      (char *)curbp->data);
+					state->extrablk.magic =
+						XFS_DIR2_DATA_MAGIC;
+					state->extrablk.bp = curbp;
+					return XFS_ERROR(EEXIST);
+				}
 			}
 		}
 	}
@@ -618,6 +627,14 @@ xfs_dir2_leafn_lookup_int(
 		}
 	}
 	/*
+	 * For lookup (where args->oknoent is set, and args->addname is not
+	 * set, the state->extrablk info is not used, just freed.
+	 */
+	if (args->cmpresult == XFS_CMP_CASE) {
+		ASSERT(!args->addname);
+		return XFS_ERROR(EEXIST);
+	}
+	/*
 	 * Return the final index, that will be the insertion point.
 	 */
 	*indexp = index;
@@ -823,9 +840,9 @@ 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;
Index: kern_ci/fs/xfs/xfs_dir2_sf.c
===================================================================
--- kern_ci.orig/fs/xfs/xfs_dir2_sf.c
+++ kern_ci/fs/xfs/xfs_dir2_sf.c
@@ -814,6 +814,7 @@ xfs_dir2_sf_lookup(
 	int			i;		/* entry index */
 	xfs_dir2_sf_entry_t	*sfep;		/* shortform directory entry */
 	xfs_dir2_sf_t		*sfp;		/* shortform structure */
+	xfs_dacmp_t		cmp;		/* comparison result */
 
 	xfs_dir2_trace_args("sf_lookup", args);
 	xfs_dir2_sf_check(args);
@@ -836,6 +837,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,6 +846,7 @@ 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);
 	}
 	/*
@@ -852,15 +855,19 @@ xfs_dir2_sf_lookup(
 	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) {
+		cmp = xfs_dir_compname(dp, sfep->name, sfep->namelen,
+					args->name, args->namelen);
+		if (cmp != XFS_CMP_DIFFERENT && cmp != args->cmpresult) {
+			args->cmpresult = cmp;
 			args->inumber =
 				xfs_dir2_sf_get_inumber(sfp,
 					xfs_dir2_sf_inumberp(sfep));
-			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 it.
 	 */
@@ -907,9 +914,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_da_compname(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 +1050,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_da_compname(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));
Index: kern_ci/fs/xfs/xfs_mount.h
===================================================================
--- kern_ci.orig/fs/xfs/xfs_mount.h
+++ kern_ci/fs/xfs/xfs_mount.h
@@ -61,6 +61,7 @@ struct xfs_bmap_free;
 struct xfs_extdelta;
 struct xfs_swapext;
 struct xfs_mru_cache;
+struct xfs_nameops;
 
 /*
  * Prototypes and functions for the Data Migration subsystem.
@@ -312,6 +313,7 @@ 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 */
+	struct xfs_nameops	*m_dirnameops;	/* vector of dir name ops */
 	int			m_dirblksize;	/* directory block sz--bytes */
 	int			m_dirblkfsbs;	/* directory block sz--fsbs */
 	xfs_dablk_t		m_dirdatablk;	/* blockno of dir data v2 */

-- 

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

* [PATCH 2/7] XFS: ASCII case-insensitive support
  2008-04-02  6:25 [PATCH 0/7] XFS: case-insensitive lookup and Unicode support Barry Naujok
  2008-04-02  6:25 ` [PATCH 1/7] XFS: Name operation vector for hash and compare Barry Naujok
@ 2008-04-02  6:25 ` Barry Naujok
  2008-04-03  0:35   ` Josef 'Jeff' Sipek
                     ` (2 more replies)
  2008-04-02  6:25 ` [PATCH 3/7] XFS: Refactor node format directory lookup/addname Barry Naujok
                   ` (5 subsequent siblings)
  7 siblings, 3 replies; 48+ messages in thread
From: Barry Naujok @ 2008-04-02  6:25 UTC (permalink / raw)
  To: xfs; +Cc: linux-fsdevel

[-- Attachment #1: ascii_ci.patch --]
[-- Type: text/plain, Size: 8694 bytes --]

Implement ASCII case-insensitive support. It's primary purpose 
is for supporting existing filesystems that already use this
case-insensitive mode migrated from IRIX. But, if you only need
ASCII-only case-insensitive support (ie. English only) and will
never use another language, then this mode is perfectly adequate.

ASCII-CI is implemented by generating hashes based on lower-case
letters and doing lower-case compares. It implements a new
xfs_nameops vector for doing the hashes and comparisons for
all filename operations.

It also overrides the Linux dentry cache operations with its
own hash and compare functions (the same as used in the xfs_nameops
vector).

To create a filesystem with this CI mode, use:
# mkfs.xfs -n version=ci <device>

Signed-off-by: Barry Naujok <bnaujok@sgi.com>

---
 fs/xfs/linux-2.6/xfs_iops.c  |   46 +++++++++++++++++++++++++++++++++++++-
 fs/xfs/linux-2.6/xfs_linux.h |    1
 fs/xfs/linux-2.6/xfs_super.c |    4 +++
 fs/xfs/xfs_dir2.c            |   52 ++++++++++++++++++++++++++++++++++++++++++-
 fs/xfs/xfs_fs.h              |    1
 fs/xfs/linux-2.6/xfs_iops.c  |   46 +++++++++++++++++++++++++++++++++++++-
 fs/xfs/linux-2.6/xfs_linux.h |    1 
 fs/xfs/linux-2.6/xfs_super.c |    4 +++
 fs/xfs/xfs_dir2.c            |   52 ++++++++++++++++++++++++++++++++++++++++++-
 fs/xfs/xfs_fs.h              |    1 
 fs/xfs/xfs_fsops.c           |    4 ++-
 fs/xfs/xfs_sb.h              |   10 +++++++-
 7 files changed, 114 insertions(+), 4 deletions(-)

Index: kern_ci/fs/xfs/linux-2.6/xfs_iops.c
===================================================================
--- kern_ci.orig/fs/xfs/linux-2.6/xfs_iops.c
+++ kern_ci/fs/xfs/linux-2.6/xfs_iops.c
@@ -47,6 +47,7 @@
 #include "xfs_buf_item.h"
 #include "xfs_utils.h"
 #include "xfs_vnodeops.h"
+#include "xfs_da_btree.h"
 
 #include <linux/capability.h>
 #include <linux/xattr.h>
@@ -54,6 +55,8 @@
 #include <linux/security.h>
 #include <linux/falloc.h>
 
+struct dentry_operations xfs_ci_dentry_operations;
+
 /*
  * Bring the atime in the XFS inode uptodate.
  * Used before logging the inode to disk or when the Linux inode goes away.
@@ -372,10 +375,15 @@ xfs_vn_lookup(
 {
 	struct xfs_inode *cip;
 	int		error;
+	struct xfs_mount *mp = XFS_I(dir)->i_mount;
+	struct dentry	*result;
 
 	if (dentry->d_name.len >= MAXNAMELEN)
 		return ERR_PTR(-ENAMETOOLONG);
 
+	if (xfs_sb_version_hasoldci(&mp->m_sb))
+		dentry->d_op = &xfs_ci_dentry_operations;
+
 	error = xfs_lookup(XFS_I(dir), dentry, &cip);
 	if (unlikely(error)) {
 		if (unlikely(error != ENOENT))
@@ -384,7 +392,10 @@ xfs_vn_lookup(
 		return NULL;
 	}
 
-	return d_splice_alias(cip->i_vnode, dentry);
+	result = d_splice_alias(cip->i_vnode, dentry);
+	if (result)
+		result->d_op = dentry->d_op;
+	return result;
 }
 
 STATIC int
@@ -887,3 +898,36 @@ const struct inode_operations xfs_symlin
 	.listxattr		= xfs_vn_listxattr,
 	.removexattr		= xfs_vn_removexattr,
 };
+
+STATIC int
+xfs_ci_dentry_hash(
+	struct dentry	*dir,
+	struct qstr	*this)
+{
+	this->hash = xfs_dir_hashname(XFS_I(dir->d_inode),
+				this->name, this->len);
+	return 0;
+}
+
+STATIC int
+xfs_ci_dentry_compare(
+	struct dentry	*dir,
+	struct qstr	*a,
+	struct qstr	*b)
+{
+	int	result = xfs_dir_compname(XFS_I(dir->d_inode), a->name, a->len,
+					b->name, b->len) == XFS_CMP_DIFFERENT;
+	/*
+	 * result == 0 if a match is found, and if so, copy the name in "b"
+	 * to "a" to cope with negative dentries getting the correct name.
+	 */
+	if (result == 0)
+		memcpy((unsigned char *)a->name, b->name, a->len);
+	return result;
+}
+
+struct dentry_operations xfs_ci_dentry_operations =
+{
+	.d_hash = xfs_ci_dentry_hash,
+	.d_compare = xfs_ci_dentry_compare,
+};
Index: kern_ci/fs/xfs/linux-2.6/xfs_linux.h
===================================================================
--- kern_ci.orig/fs/xfs/linux-2.6/xfs_linux.h
+++ kern_ci/fs/xfs/linux-2.6/xfs_linux.h
@@ -75,6 +75,7 @@
 #include <linux/delay.h>
 #include <linux/log2.h>
 #include <linux/spinlock.h>
+#include <linux/ctype.h>
 
 #include <asm/page.h>
 #include <asm/div64.h>
Index: kern_ci/fs/xfs/linux-2.6/xfs_super.c
===================================================================
--- kern_ci.orig/fs/xfs/linux-2.6/xfs_super.c
+++ kern_ci/fs/xfs/linux-2.6/xfs_super.c
@@ -67,6 +67,8 @@ static kmem_zone_t *xfs_vnode_zone;
 static kmem_zone_t *xfs_ioend_zone;
 mempool_t *xfs_ioend_pool;
 
+extern struct dentry_operations xfs_ci_dentry_operations;
+
 STATIC struct xfs_mount_args *
 xfs_args_allocate(
 	struct super_block	*sb,
@@ -1359,6 +1361,8 @@ xfs_fs_fill_super(
 		error = ENOMEM;
 		goto fail_vnrele;
 	}
+	if (xfs_sb_version_hasoldci(&mp->m_sb))
+		sb->s_root->d_op = &xfs_ci_dentry_operations;
 
 	mp->m_sync_work.w_syncer = xfs_sync_worker;
 	mp->m_sync_work.w_mount = mp;
Index: kern_ci/fs/xfs/xfs_dir2.c
===================================================================
--- kern_ci.orig/fs/xfs/xfs_dir2.c
+++ kern_ci/fs/xfs/xfs_dir2.c
@@ -45,6 +45,55 @@
 #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(
+	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(
+	const uchar_t	*name1,
+	int		len1,
+	const uchar_t	*name2,
+	int 		len2)
+{
+	xfs_dacmp_t	result;
+	int		i;
+
+	if (len1 != len2)
+		return XFS_CMP_DIFFERENT;
+
+	result = XFS_CMP_EXACT;
+	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 struct xfs_nameops xfs_ascii_ci_nameops = {
+	.hashname	= xfs_ascii_ci_hashname,
+	.compname	= xfs_ascii_ci_compname,
+};
+
 void
 xfs_dir_mount(
 	xfs_mount_t	*mp)
@@ -64,7 +113,8 @@ 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;
-	mp->m_dirnameops = &xfs_default_nameops;
+	mp->m_dirnameops = xfs_sb_version_hasoldci(&mp->m_sb) ?
+		&xfs_ascii_ci_nameops : &xfs_default_nameops;
 }
 
 /*
Index: kern_ci/fs/xfs/xfs_fs.h
===================================================================
--- kern_ci.orig/fs/xfs/xfs_fs.h
+++ kern_ci/fs/xfs/xfs_fs.h
@@ -239,6 +239,7 @@ typedef struct xfs_fsop_resblks {
 #define XFS_FSOP_GEOM_FLAGS_LOGV2	0x0100	/* log format version 2	*/
 #define XFS_FSOP_GEOM_FLAGS_SECTOR	0x0200	/* sector sizes >1BB	*/
 #define XFS_FSOP_GEOM_FLAGS_ATTR2	0x0400	/* inline attributes rework */
+#define XFS_FSOP_GEOM_FLAGS_DIRV2CI	0x1000	/* ASCII only CI names */
 #define XFS_FSOP_GEOM_FLAGS_LAZYSB	0x4000	/* lazy superblock counters */
 
 
Index: kern_ci/fs/xfs/xfs_fsops.c
===================================================================
--- kern_ci.orig/fs/xfs/xfs_fsops.c
+++ kern_ci/fs/xfs/xfs_fsops.c
@@ -95,6 +95,8 @@ xfs_fs_geometry(
 				XFS_FSOP_GEOM_FLAGS_DIRV2 : 0) |
 			(xfs_sb_version_hassector(&mp->m_sb) ?
 				XFS_FSOP_GEOM_FLAGS_SECTOR : 0) |
+			(xfs_sb_version_hasoldci(&mp->m_sb) ?
+				XFS_FSOP_GEOM_FLAGS_DIRV2CI : 0) |
 			(xfs_sb_version_haslazysbcount(&mp->m_sb) ?
 				XFS_FSOP_GEOM_FLAGS_LAZYSB : 0) |
 			(xfs_sb_version_hasattr2(&mp->m_sb) ?
@@ -629,7 +631,7 @@ xfs_fs_goingdown(
 			xfs_force_shutdown(mp, SHUTDOWN_FORCE_UMOUNT);
 			thaw_bdev(sb->s_bdev, sb);
 		}
-	
+
 		break;
 	}
 	case XFS_FSOP_GOING_FLAGS_LOGFLUSH:
Index: kern_ci/fs/xfs/xfs_sb.h
===================================================================
--- kern_ci.orig/fs/xfs/xfs_sb.h
+++ kern_ci/fs/xfs/xfs_sb.h
@@ -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 | \
@@ -436,6 +438,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);
+}
+
 static inline int xfs_sb_version_hasmorebits(xfs_sb_t *sbp)
 {
 	return (XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_4) && \

-- 

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

* [PATCH 3/7] XFS: Refactor node format directory lookup/addname
  2008-04-02  6:25 [PATCH 0/7] XFS: case-insensitive lookup and Unicode support Barry Naujok
  2008-04-02  6:25 ` [PATCH 1/7] XFS: Name operation vector for hash and compare Barry Naujok
  2008-04-02  6:25 ` [PATCH 2/7] XFS: ASCII case-insensitive support Barry Naujok
@ 2008-04-02  6:25 ` Barry Naujok
  2008-04-03  1:51   ` Josef 'Jeff' Sipek
  2008-04-03  4:33   ` David Chinner
  2008-04-02  6:25 ` [PATCH 4/7] XFS: Return case-insensitive match for dentry cache Barry Naujok
                   ` (4 subsequent siblings)
  7 siblings, 2 replies; 48+ messages in thread
From: Barry Naujok @ 2008-04-02  6:25 UTC (permalink / raw)
  To: xfs; +Cc: linux-fsdevel

[-- Attachment #1: refactor_leafn_lookup.patch --]
[-- Type: text/plain, Size: 15716 bytes --]

The next step for case-insensitive support is to avoid polution of
the dentry cache with entries pointing to the same inode, but with
names that only differ in case.

To perform this, we will need to pass the actual filename that
matched backup to the XFS/VFS interface and make sure the dentry
cache only contains entries with the actual case-sensitive name.

But, before we can do this, it was found that the directory lookup
code with multiple leaves was shared with code adding a name to
that directory. Most of xfs_dir2_leafn_lookup_int() could be broken
into two functions determined by if (args->addname) { } else { }.

For the following patch, only the lookup case needs to handle the
various xfs_nameops, with case-insensitive match handling in
addition to returning the actual name.

So, this patch separates xfs_dir2_leafn_lookup_int() into
xfs_dir2_leafn_lookup_for_addname() and xfs_dir2_leafn_lookup_for_entry().

xfs_dir2_leafn_lookup_for_addname() iterates through the data blocks looking
for a suitable empty space to insert the name while
xfs_dir2_leafn_lookup_for_entry() uses the xfs_nameops to find the entry.

xfs_dir2_leafn_lookup_for_entry() path also retains the data block where
the first case-insensitive match occured as in the next patch which will
return the name, the name is obtained from that block.

Signed-off-by: Barry Naujok <bnaujok@sgi.com>

---
 fs/xfs/xfs_dir2_node.c |  373 +++++++++++++++++++++++++++++--------------------
 1 file changed, 225 insertions(+), 148 deletions(-)

Index: kern_ci/fs/xfs/xfs_dir2_node.c
===================================================================
--- kern_ci.orig/fs/xfs/xfs_dir2_node.c
+++ kern_ci/fs/xfs/xfs_dir2_node.c
@@ -387,12 +387,11 @@ xfs_dir2_leafn_lasthash(
 }
 
 /*
- * Look up a leaf entry in a node-format leaf block.
- * If this is an addname then the extrablk in state is a freespace block,
- * otherwise it's a data block.
+ * Look up a leaf entry for space to add a name in a node-format leaf block.
+ * The extrablk in state is a freespace block.
  */
-int
-xfs_dir2_leafn_lookup_int(
+static int
+xfs_dir2_leafn_lookup_for_addname(
 	xfs_dabuf_t		*bp,		/* leaf buffer */
 	xfs_da_args_t		*args,		/* operation arguments */
 	int			*indexp,	/* out: leaf entry index */
@@ -401,7 +400,6 @@ xfs_dir2_leafn_lookup_int(
 	xfs_dabuf_t		*curbp;		/* current data/free buffer */
 	xfs_dir2_db_t		curdb;		/* current data block number */
 	xfs_dir2_db_t		curfdb;		/* current free block number */
-	xfs_dir2_data_entry_t	*dep;		/* data block entry */
 	xfs_inode_t		*dp;		/* incore directory inode */
 	int			error;		/* error return value */
 	int			fi;		/* free entry index */
@@ -414,7 +412,6 @@ 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;
@@ -432,27 +429,15 @@ xfs_dir2_leafn_lookup_int(
 	/*
 	 * Do we have a buffer coming in?
 	 */
-	if (state->extravalid)
-		curbp = state->extrablk.bp;
-	else
-		curbp = NULL;
+	curbp = state->extravalid ? state->extrablk.bp : NULL;
 	/*
 	 * For addname, it's a free block buffer, get the block number.
 	 */
-	if (args->addname) {
-		curfdb = curbp ? state->extrablk.blkno : -1;
-		curdb = -1;
-		length = xfs_dir2_data_entsize(args->namelen);
-		if ((free = (curbp ? curbp->data : NULL)))
-			ASSERT(be32_to_cpu(free->hdr.magic) == XFS_DIR2_FREE_MAGIC);
-	}
-	/*
-	 * For others, it's a data block buffer, get the block number.
-	 */
-	else {
-		curfdb = -1;
-		curdb = curbp ? state->extrablk.blkno : -1;
-	}
+	curfdb = curbp ? state->extrablk.blkno : -1;
+	free = curbp ? curbp->data : NULL;
+	curdb = -1;
+	length = xfs_dir2_data_entsize(args->namelen);
+	ASSERT(!free || be32_to_cpu(free->hdr.magic) == XFS_DIR2_FREE_MAGIC);
 	/*
 	 * Loop over leaf entries with the right hash value.
 	 */
@@ -472,134 +457,69 @@ xfs_dir2_leafn_lookup_int(
 		 * For addname, we're looking for a place to put the new entry.
 		 * We want to use a data block with an entry of equal
 		 * hash value to ours if there is one with room.
+		 *
+		 * If this block isn't the data block we already have
+		 * in hand, take a look at it.
 		 */
-		if (args->addname) {
+		if (newdb != curdb) {
+			curdb = newdb;
 			/*
-			 * If this block isn't the data block we already have
-			 * in hand, take a look at it.
+			 * Convert the data block to the free block
+			 * holding its freespace information.
 			 */
-			if (newdb != curdb) {
-				curdb = newdb;
-				/*
-				 * Convert the data block to the free block
-				 * holding its freespace information.
-				 */
-				newfdb = xfs_dir2_db_to_fdb(mp, newdb);
-				/*
-				 * If it's not the one we have in hand,
-				 * read it in.
-				 */
-				if (newfdb != curfdb) {
-					/*
-					 * If we had one before, drop it.
-					 */
-					if (curbp)
-						xfs_da_brelse(tp, curbp);
-					/*
-					 * Read the free block.
-					 */
-					if ((error = xfs_da_read_buf(tp, dp,
-							xfs_dir2_db_to_da(mp,
-								newfdb),
-							-1, &curbp,
-							XFS_DATA_FORK))) {
-						return error;
-					}
-					free = curbp->data;
-					ASSERT(be32_to_cpu(free->hdr.magic) ==
-					       XFS_DIR2_FREE_MAGIC);
-					ASSERT((be32_to_cpu(free->hdr.firstdb) %
-						XFS_DIR2_MAX_FREE_BESTS(mp)) ==
-					       0);
-					ASSERT(be32_to_cpu(free->hdr.firstdb) <= curdb);
-					ASSERT(curdb <
-					       be32_to_cpu(free->hdr.firstdb) +
-					       be32_to_cpu(free->hdr.nvalid));
-				}
-				/*
-				 * Get the index for our entry.
-				 */
-				fi = xfs_dir2_db_to_fdindex(mp, curdb);
-				/*
-				 * If it has room, return it.
-				 */
-				if (unlikely(be16_to_cpu(free->bests[fi]) == NULLDATAOFF)) {
-					XFS_ERROR_REPORT("xfs_dir2_leafn_lookup_int",
-							 XFS_ERRLEVEL_LOW, mp);
-					if (curfdb != newfdb)
-						xfs_da_brelse(tp, curbp);
-					return XFS_ERROR(EFSCORRUPTED);
-				}
-				curfdb = newfdb;
-				if (be16_to_cpu(free->bests[fi]) >= length) {
-					*indexp = index;
-					state->extravalid = 1;
-					state->extrablk.bp = curbp;
-					state->extrablk.blkno = curfdb;
-					state->extrablk.index = fi;
-					state->extrablk.magic =
-						XFS_DIR2_FREE_MAGIC;
-					ASSERT(args->oknoent);
-					return XFS_ERROR(ENOENT);
-				}
-			}
-		}
-		/*
-		 * Not adding a new entry, so we really want to find
-		 * the name given to us.
-		 */
-		else {
+			newfdb = xfs_dir2_db_to_fdb(mp, newdb);
 			/*
-			 * If it's a different data block, go get it.
+			 * If it's not the one we have in hand,
+			 * read it in.
 			 */
-			if (newdb != curdb) {
+			if (newfdb != curfdb) {
 				/*
-				 * If we had a block before, drop it.
+				 * If we had one before, drop it.
 				 */
 				if (curbp)
 					xfs_da_brelse(tp, curbp);
 				/*
-				 * Read the data block.
+				 * Read the free block.
 				 */
-				if ((error =
-				    xfs_da_read_buf(tp, dp,
-					    xfs_dir2_db_to_da(mp, newdb), -1,
-					    &curbp, XFS_DATA_FORK))) {
+				error = xfs_da_read_buf(tp, dp,
+						xfs_dir2_db_to_da(mp, newfdb),
+						-1, &curbp, XFS_DATA_FORK);
+				if (error)
 					return error;
-				}
-				xfs_dir2_data_check(dp, curbp);
-				curdb = newdb;
+
+				free = curbp->data;
+				ASSERT(be32_to_cpu(free->hdr.magic) ==
+				       XFS_DIR2_FREE_MAGIC);
+				ASSERT((be32_to_cpu(free->hdr.firstdb) %
+					XFS_DIR2_MAX_FREE_BESTS(mp)) == 0);
+				ASSERT(be32_to_cpu(free->hdr.firstdb) <= curdb);
+				ASSERT(curdb < be32_to_cpu(free->hdr.firstdb) +
+				       be32_to_cpu(free->hdr.nvalid));
 			}
 			/*
-			 * Point to the data entry.
+			 * Get the index for our entry.
+			 */
+			fi = xfs_dir2_db_to_fdindex(mp, curdb);
+			/*
+			 * If it has room, return it.
 			 */
-			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.
-			 */
-			cmp = args->oknoent ?
-				xfs_dir_compname(dp, dep->name, dep->namelen,
-						args->name, args->namelen):
-				xfs_da_compname(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);
+			if (unlikely(be16_to_cpu(free->bests[fi]) == NULLDATAOFF)) {
+				XFS_ERROR_REPORT("xfs_dir2_leafn_lookup_int",
+						 XFS_ERRLEVEL_LOW, mp);
+				if (curfdb != newfdb)
+					xfs_da_brelse(tp, curbp);
+				return XFS_ERROR(EFSCORRUPTED);
+			}
+			curfdb = newfdb;
+			if (be16_to_cpu(free->bests[fi]) >= length) {
 				*indexp = index;
-				if (cmp == XFS_CMP_EXACT) {
-					state->extravalid = 1;
-					state->extrablk.blkno = curdb;
-					state->extrablk.index =
-						(int)((char *)dep -
-						      (char *)curbp->data);
-					state->extrablk.magic =
-						XFS_DIR2_DATA_MAGIC;
-					state->extrablk.bp = curbp;
-					return XFS_ERROR(EEXIST);
-				}
+				state->extravalid = 1;
+				state->extrablk.bp = curbp;
+				state->extrablk.blkno = curfdb;
+				state->extrablk.index = fi;
+				state->extrablk.magic = XFS_DIR2_FREE_MAGIC;
+				ASSERT(args->oknoent);
+				return XFS_ERROR(ENOENT);
 			}
 		}
 	}
@@ -608,31 +528,166 @@ xfs_dir2_leafn_lookup_int(
 	 * If we are holding a buffer, give it back in case our caller
 	 * finds it useful.
 	 */
-	if ((state->extravalid = (curbp != NULL))) {
+	if (curbp != NULL) {
+		state->extravalid = 1;
 		state->extrablk.bp = curbp;
 		state->extrablk.index = -1;
 		/*
 		 * For addname, giving back a free block.
 		 */
-		if (args->addname) {
-			state->extrablk.blkno = curfdb;
-			state->extrablk.magic = XFS_DIR2_FREE_MAGIC;
+		state->extrablk.blkno = curfdb;
+		state->extrablk.magic = XFS_DIR2_FREE_MAGIC;
+	}
+	/*
+	 * Return the final index, that will be the insertion point.
+	 */
+	*indexp = index;
+	ASSERT(index == be16_to_cpu(leaf->hdr.count) || args->oknoent);
+	return XFS_ERROR(ENOENT);
+}
+
+/*
+ * Look up a leaf entry in a node-format leaf block.
+ * The extrablk in state a data block.
+ */
+static int
+xfs_dir2_leafn_lookup_for_entry(
+	xfs_dabuf_t		*bp,		/* leaf buffer */
+	xfs_da_args_t		*args,		/* operation arguments */
+	int			*indexp,	/* out: leaf entry index */
+	xfs_da_state_t		*state)		/* state to fill in */
+{
+	xfs_dabuf_t		*curbp;		/* current data/free buffer */
+	xfs_dir2_db_t		curdb;		/* current data block number */
+	xfs_dir2_data_entry_t	*dep;		/* data block entry */
+	xfs_inode_t		*dp;		/* incore directory inode */
+	int			error;		/* error return value */
+	int			index;		/* leaf entry index */
+	xfs_dir2_leaf_t		*leaf;		/* leaf structure */
+	xfs_dir2_leaf_entry_t	*lep;		/* leaf entry */
+	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;		/* comparison result */
+	xfs_dabuf_t		*ci_bp = NULL;	/* buffer with CI match */
+
+	dp = args->dp;
+	tp = args->trans;
+	mp = dp->i_mount;
+	leaf = bp->data;
+	ASSERT(be16_to_cpu(leaf->hdr.info.magic) == XFS_DIR2_LEAFN_MAGIC);
+#ifdef __KERNEL__
+	ASSERT(be16_to_cpu(leaf->hdr.count) > 0);
+#endif
+	xfs_dir2_leafn_check(dp, bp);
+	/*
+	 * Look up the hash value in the leaf entries.
+	 */
+	index = xfs_dir2_leaf_search_hash(args, bp);
+	/*
+	 * Do we have a buffer coming in?
+	 */
+	if (state->extravalid) {
+		curbp = state->extrablk.bp;
+		curdb = state->extrablk.blkno;
+		if (args->cmpresult == XFS_CMP_CASE)
+			ci_bp = curbp;
+	} else {
+		curbp = NULL;
+		curdb = -1;
+	}
+	/*
+	 * Loop over leaf entries with the right hash value.
+	 */
+	for (lep = &leaf->ents[index];
+	     index < be16_to_cpu(leaf->hdr.count) &&
+			be32_to_cpu(lep->hashval) == args->hashval;
+	     lep++, index++) {
+		/*
+		 * Skip stale leaf entries.
+		 */
+		if (be32_to_cpu(lep->address) == XFS_DIR2_NULL_DATAPTR)
+			continue;
+		/*
+		 * Pull the data block number from the entry.
+		 */
+		newdb = xfs_dir2_dataptr_to_db(mp, be32_to_cpu(lep->address));
+		/*
+		 * Not adding a new entry, so we really want to find
+		 * the name given to us.
+		 *
+		 * If it's a different data block, go get it.
+		 */
+		if (newdb != curdb) {
+			/*
+			 * If we had a block before, drop it (unless it
+			 * contains a case-insensitive match).
+			 */
+			if (curbp && curbp != ci_bp)
+				xfs_da_brelse(tp, curbp);
+			/*
+			 * Read the data block.
+			 */
+			error = xfs_da_read_buf(tp, dp,
+					xfs_dir2_db_to_da(mp, newdb), -1,
+					&curbp, XFS_DATA_FORK);
+			if (error)
+				return error;
+			xfs_dir2_data_check(dp, curbp);
+			curdb = newdb;
 		}
 		/*
-		 * For other callers, giving back a data block.
+		 * Point to the data entry.
 		 */
-		else {
+		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.
+		 */
+		cmp = args->oknoent ?
+			xfs_dir_compname(dp, dep->name, dep->namelen,
+					args->name, args->namelen):
+			xfs_da_compname(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;
+			if (ci_bp && ci_bp != curbp)
+				xfs_da_brelse(tp, ci_bp);
+			state->extravalid = 1;
 			state->extrablk.blkno = curdb;
+			state->extrablk.index = (int)((char *)dep -
+					(char *)curbp->data);
 			state->extrablk.magic = XFS_DIR2_DATA_MAGIC;
+			state->extrablk.bp = curbp;
+			if (cmp == XFS_CMP_EXACT)
+				return XFS_ERROR(EEXIST);
 		}
 	}
 	/*
-	 * For lookup (where args->oknoent is set, and args->addname is not
-	 * set, the state->extrablk info is not used, just freed.
+	 * if we have a case-insensitive match, we have to return ENOENT
+	 * so xfs_da_node_lookup_int() can try the next leaf if one exists
+	 * for the hash that may have an exact match.
+	 * xfs_dir2_node_lookup() below handles the ENOENT and args->cmpresult
+	 * to find the case-insensitive match and returns EEXIST.
 	 */
-	if (args->cmpresult == XFS_CMP_CASE) {
-		ASSERT(!args->addname);
-		return XFS_ERROR(EEXIST);
+	if (args->cmpresult == XFS_CMP_CASE)
+		return XFS_ERROR(ENOENT);
+	/*
+	 * Didn't find a match.
+	 * If we are holding a buffer, give it back in case our caller
+	 * finds it useful.
+	 */
+	if (curbp != NULL) {
+		state->extravalid = 1;
+		state->extrablk.bp = curbp;
+		state->extrablk.index = -1;
+		/*
+		 * Giving back a data block.
+		 */
+		state->extrablk.blkno = curdb;
+		state->extrablk.magic = XFS_DIR2_DATA_MAGIC;
 	}
 	/*
 	 * Return the final index, that will be the insertion point.
@@ -643,6 +698,23 @@ xfs_dir2_leafn_lookup_int(
 }
 
 /*
+ * Look up a leaf entry in a node-format leaf block.
+ * If this is an addname then the extrablk in state is a freespace block,
+ * otherwise it's a data block.
+ */
+int
+xfs_dir2_leafn_lookup_int(
+	xfs_dabuf_t		*bp,		/* leaf buffer */
+	xfs_da_args_t		*args,		/* operation arguments */
+	int			*indexp,	/* out: leaf entry index */
+	xfs_da_state_t		*state)		/* state to fill in */
+{
+	return args->addname ?
+		xfs_dir2_leafn_lookup_for_addname(bp, args, indexp, state) :
+		xfs_dir2_leafn_lookup_for_entry(bp, args, indexp, state);
+}
+
+/*
  * Move count leaf entries from source to destination leaf.
  * Log entries and headers.  Stale entries are preserved.
  */
@@ -1785,6 +1857,11 @@ xfs_dir2_node_lookup(
 	if (error)
 		rval = error;
 	/*
+	 * If case-insensitive match was found in a leaf, return EEXIST.
+	 */
+	else if (rval == ENOENT && args->cmpresult == XFS_CMP_CASE)
+		rval = EEXIST;
+	/*
 	 * Release the btree blocks and leaf block.
 	 */
 	for (i = 0; i < state->path.active; i++) {

-- 

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

* [PATCH 4/7] XFS: Return case-insensitive match for dentry cache
  2008-04-02  6:25 [PATCH 0/7] XFS: case-insensitive lookup and Unicode support Barry Naujok
                   ` (2 preceding siblings ...)
  2008-04-02  6:25 ` [PATCH 3/7] XFS: Refactor node format directory lookup/addname Barry Naujok
@ 2008-04-02  6:25 ` Barry Naujok
  2008-04-03  2:34   ` Josef 'Jeff' Sipek
                     ` (2 more replies)
  2008-04-02  6:25 ` [PATCH 5/7] XFS: Unicode case-insensitive lookup implementation Barry Naujok
                   ` (3 subsequent siblings)
  7 siblings, 3 replies; 48+ messages in thread
From: Barry Naujok @ 2008-04-02  6:25 UTC (permalink / raw)
  To: xfs; +Cc: linux-fsdevel

[-- Attachment #1: return_name.patch --]
[-- Type: text/plain, Size: 22910 bytes --]

This implements the code to store the actual filename found
during a lookup in the dentry cache and to avoid multiple entries
in the dcache pointing to the same inode.

It also introduces a new type, xfs_name, which is similar to the
dentry cache's qstr type. It contains a pointer to a zone allocated
string (MAXNAMELEN sized) and the length of the actual name. This
string does not need to be NULL terminated (a counted string).

xfs_name_t is only used in the lookup path for this patch, but may
be used in other locations too if desired. It maybe desirable not
to use xfs_name_t at all in the lookup functions but stick to
separate parameters (which will mean 7 instead of 5 arguments).

To avoid polluting the dcache, we implement a new directory inode
operations for lookup. xfs_vn_ci_lookup() interacts directly with
the dcache and the code was derived from ntfs_lookup() in
fs/ntfs/namei.c. The dentry hash and compare overrides introduced
in the ASCII-CI patch has been removed.

The "actual name" is only allocated and returned for a case-
insensitive match and not an actual match.

Signed-off-by: Barry Naujok <bnaujok@sgi.com>

---
 fs/xfs/linux-2.6/xfs_export.c |    2 
 fs/xfs/linux-2.6/xfs_iops.c   |  165 +++++++++++++++++++++++++++++++-----------
 fs/xfs/linux-2.6/xfs_iops.h   |    1 
 fs/xfs/linux-2.6/xfs_super.c  |    5 +
 fs/xfs/linux-2.6/xfs_vnode.h  |    1 
 fs/xfs/xfs_da_btree.c         |   16 ++++
 fs/xfs/xfs_da_btree.h         |   13 +++
 fs/xfs/xfs_dir2.c             |   28 +++++--
 fs/xfs/xfs_dir2.h             |    4 -
 fs/xfs/xfs_dir2_block.c       |    9 ++
 fs/xfs/xfs_dir2_leaf.c        |    9 ++
 fs/xfs/xfs_dir2_node.c        |   20 ++++-
 fs/xfs/xfs_dir2_sf.c          |   13 +++
 fs/xfs/xfs_rename.c           |    5 +
 fs/xfs/xfs_utils.c            |   12 ++-
 fs/xfs/xfs_utils.h            |    6 +
 fs/xfs/xfs_vfsops.c           |    2 
 fs/xfs/xfs_vnodeops.c         |   15 +++
 fs/xfs/xfs_vnodeops.h         |    4 -
 19 files changed, 264 insertions(+), 66 deletions(-)

Index: kern_ci/fs/xfs/linux-2.6/xfs_export.c
===================================================================
--- kern_ci.orig/fs/xfs/linux-2.6/xfs_export.c
+++ kern_ci/fs/xfs/linux-2.6/xfs_export.c
@@ -216,7 +216,7 @@ xfs_fs_get_parent(
 	struct xfs_inode	*cip;
 	struct dentry		*parent;
 
-	error = xfs_lookup(XFS_I(child->d_inode), &dotdot, &cip);
+	error = xfs_lookup(XFS_I(child->d_inode), &dotdot.d_name, &cip, NULL);
 	if (unlikely(error))
 		return ERR_PTR(-error);
 
Index: kern_ci/fs/xfs/linux-2.6/xfs_iops.c
===================================================================
--- kern_ci.orig/fs/xfs/linux-2.6/xfs_iops.c
+++ kern_ci/fs/xfs/linux-2.6/xfs_iops.c
@@ -375,27 +375,125 @@ xfs_vn_lookup(
 {
 	struct xfs_inode *cip;
 	int		error;
-	struct xfs_mount *mp = XFS_I(dir)->i_mount;
+
+	if (dentry->d_name.len >= MAXNAMELEN)
+		return ERR_PTR(-ENAMETOOLONG);
+
+	error = xfs_lookup(XFS_I(dir), &dentry->d_name, &cip, NULL);
+	if (unlikely(error)) {
+		if (unlikely(error != ENOENT))
+			return ERR_PTR(-error);
+		d_add(dentry, NULL);
+		return NULL;
+	}
+
+	return d_splice_alias(cip->i_vnode, dentry);
+}
+
+STATIC struct dentry *
+xfs_vn_ci_lookup(
+	struct inode	*dir,
+	struct dentry	*dentry,
+	struct nameidata *nd)
+{
+	struct xfs_inode *cip;
+	int		error;
 	struct dentry	*result;
+	struct qstr	ci_name = {0, 0, NULL};
+	struct inode	*inode;
 
 	if (dentry->d_name.len >= MAXNAMELEN)
 		return ERR_PTR(-ENAMETOOLONG);
 
-	if (xfs_sb_version_hasoldci(&mp->m_sb))
-		dentry->d_op = &xfs_ci_dentry_operations;
+	error = xfs_lookup(XFS_I(dir), &dentry->d_name, &cip, &ci_name);
 
-	error = xfs_lookup(XFS_I(dir), dentry, &cip);
 	if (unlikely(error)) {
 		if (unlikely(error != ENOENT))
 			return ERR_PTR(-error);
 		d_add(dentry, NULL);
 		return NULL;
 	}
+	inode = cip->i_vnode;
+
+	/* if exact match, just splice and exit */
+	if (!ci_name.name) {
+		result = d_splice_alias(inode, dentry);
+		return result;
+	}
 
-	result = d_splice_alias(cip->i_vnode, dentry);
-	if (result)
-		result->d_op = dentry->d_op;
-	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
+	 */
+
+	ci_name.hash = full_name_hash(ci_name.name, ci_name.len);
+
+	/* Does an existing dentry match? */
+	result = d_lookup(dentry->d_parent, &ci_name);
+	if (!result) {
+		/* if not, create one */
+		result = d_alloc(dentry->d_parent, &ci_name);
+		xfs_da_name_free((char *)ci_name.name);
+		if (!result)
+			return ERR_PTR(-ENOMEM);
+		dentry = d_splice_alias(inode, result);
+		if (dentry) {
+			dput(result);
+			return dentry;
+		}
+		return result;
+	}
+	xfs_da_name_free((char *)ci_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
@@ -886,6 +984,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,
@@ -899,35 +1016,3 @@ const struct inode_operations xfs_symlin
 	.removexattr		= xfs_vn_removexattr,
 };
 
-STATIC int
-xfs_ci_dentry_hash(
-	struct dentry	*dir,
-	struct qstr	*this)
-{
-	this->hash = xfs_dir_hashname(XFS_I(dir->d_inode),
-				this->name, this->len);
-	return 0;
-}
-
-STATIC int
-xfs_ci_dentry_compare(
-	struct dentry	*dir,
-	struct qstr	*a,
-	struct qstr	*b)
-{
-	int	result = xfs_dir_compname(XFS_I(dir->d_inode), a->name, a->len,
-					b->name, b->len) == XFS_CMP_DIFFERENT;
-	/*
-	 * result == 0 if a match is found, and if so, copy the name in "b"
-	 * to "a" to cope with negative dentries getting the correct name.
-	 */
-	if (result == 0)
-		memcpy((unsigned char *)a->name, b->name, a->len);
-	return result;
-}
-
-struct dentry_operations xfs_ci_dentry_operations =
-{
-	.d_hash = xfs_ci_dentry_hash,
-	.d_compare = xfs_ci_dentry_compare,
-};
Index: kern_ci/fs/xfs/linux-2.6/xfs_iops.h
===================================================================
--- kern_ci.orig/fs/xfs/linux-2.6/xfs_iops.h
+++ kern_ci/fs/xfs/linux-2.6/xfs_iops.h
@@ -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;
Index: kern_ci/fs/xfs/linux-2.6/xfs_super.c
===================================================================
--- kern_ci.orig/fs/xfs/linux-2.6/xfs_super.c
+++ kern_ci/fs/xfs/linux-2.6/xfs_super.c
@@ -566,7 +566,10 @@ 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_dir_ci_inode_operations :
+				&xfs_dir_inode_operations;
 		inode->i_fop = &xfs_dir_file_operations;
 		break;
 	case S_IFLNK:
Index: kern_ci/fs/xfs/linux-2.6/xfs_vnode.h
===================================================================
--- kern_ci.orig/fs/xfs/linux-2.6/xfs_vnode.h
+++ kern_ci/fs/xfs/linux-2.6/xfs_vnode.h
@@ -26,6 +26,7 @@ struct attrlist_cursor_kern;
 typedef struct dentry	bhv_vname_t;
 typedef __u64		bhv_vnumber_t;
 typedef struct inode	bhv_vnode_t;
+typedef struct qstr	bhv_vstr_t;
 
 #define VN_ISLNK(vp)	S_ISLNK((vp)->i_mode)
 #define VN_ISREG(vp)	S_ISREG((vp)->i_mode)
Index: kern_ci/fs/xfs/xfs_da_btree.c
===================================================================
--- kern_ci.orig/fs/xfs/xfs_da_btree.c
+++ kern_ci/fs/xfs/xfs_da_btree.c
@@ -2176,6 +2176,22 @@ xfs_da_reada_buf(
 		return rval;
 }
 
+
+kmem_zone_t	*xfs_da_name_zone;
+
+uchar_t *
+xfs_da_name_alloc(void)
+{
+	return kmem_zone_zalloc(xfs_da_name_zone, KM_SLEEP);
+}
+
+void
+xfs_da_name_free(const uchar_t *name)
+{
+	kmem_zone_free(xfs_da_name_zone, (void *)name);
+}
+
+
 kmem_zone_t *xfs_da_state_zone;	/* anchor for state struct zone */
 kmem_zone_t *xfs_dabuf_zone;		/* dabuf zone */
 
Index: kern_ci/fs/xfs/xfs_da_btree.h
===================================================================
--- kern_ci.orig/fs/xfs/xfs_da_btree.h
+++ kern_ci/fs/xfs/xfs_da_btree.h
@@ -224,6 +224,14 @@ typedef struct xfs_nameops {
 	xfs_compname_t		compname;
 } xfs_nameops_t;
 
+/*
+ * Counted string for names, *name should be allocated and freed with
+ * xfs_da_name_alloc and xfs_da_name_free. len must not exceed MAXNAMELEN.
+ */
+typedef struct xfs_name {
+	const uchar_t	*name;
+	int		len;
+} xfs_name_t;
 
 #ifdef __KERNEL__
 /*========================================================================
@@ -277,6 +285,11 @@ uint xfs_da_hashname(const uchar_t *name
 xfs_dacmp_t xfs_da_compname(const uchar_t *name1, int len1,
 			    const uchar_t *name2, int len2);
 
+/* returns/frees a MAXNAMELEN buffer from a zone */
+extern struct kmem_zone *xfs_da_name_zone;
+uchar_t *xfs_da_name_alloc(void);
+void xfs_da_name_free(const uchar_t *name);
+
 xfs_da_state_t *xfs_da_state_alloc(void);
 void xfs_da_state_free(xfs_da_state_t *state);
 
Index: kern_ci/fs/xfs/xfs_dir2.c
===================================================================
--- kern_ci.orig/fs/xfs/xfs_dir2.c
+++ kern_ci/fs/xfs/xfs_dir2.c
@@ -242,15 +242,16 @@ xfs_dir_createname(
 }
 
 /*
- * Lookup a name in a directory, give back the inode number.
+ * Lookup a name in a directory, give back the inode number and also
+ * the actual name if a 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_name_t	*name,
+	xfs_ino_t	*inum,		/* out: inode number */
+	xfs_name_t	*ci_name)	/* out: actual name if different */
 {
 	xfs_da_args_t	args;
 	int		rval;
@@ -259,9 +260,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_dir_hashname(dp, name, namelen);
+	args.name = name->name;
+	args.namelen = name->len;
+	args.hashval = xfs_dir_hashname(dp, name->name, name->len);
 	args.inumber = 0;
 	args.dp = dp;
 	args.firstblock = NULL;
@@ -272,6 +273,8 @@ xfs_dir_lookup(
 	args.justcheck = args.addname = 0;
 	args.oknoent = 1;
 	args.cmpresult = XFS_CMP_DIFFERENT;
+	args.value = NULL;
+	args.valuelen = 0;
 
 	if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL)
 		rval = xfs_dir2_sf_lookup(&args);
@@ -287,8 +290,17 @@ xfs_dir_lookup(
 		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 (ci_name) {
+				ci_name->name = args.value;
+				ci_name->len = args.valuelen;
+			} else
+				xfs_da_name_free(args.value);
+		}
+	}
 	return rval;
 }
 
Index: kern_ci/fs/xfs/xfs_dir2.h
===================================================================
--- kern_ci.orig/fs/xfs/xfs_dir2.h
+++ kern_ci/fs/xfs/xfs_dir2.h
@@ -26,6 +26,7 @@ struct xfs_bmap_free;
 struct xfs_inode;
 struct xfs_mount;
 struct xfs_trans;
+struct xfs_name;
 
 /*
  * Directory version 2.
@@ -72,7 +73,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);
+				struct xfs_name *name, xfs_ino_t *inum,
+				struct xfs_name *ci_name);
 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,
Index: kern_ci/fs/xfs/xfs_dir2_block.c
===================================================================
--- kern_ci.orig/fs/xfs/xfs_dir2_block.c
+++ kern_ci/fs/xfs/xfs_dir2_block.c
@@ -616,6 +616,15 @@ xfs_dir2_block_lookup(
 	 * Fill in inode number, release the block.
 	 */
 	args->inumber = be64_to_cpu(dep->inumber);
+	/*
+	 * If a case-insensitive match, allocate a buffer and copy the actual
+	 * name into the buffer. Return it via args->value.
+	 */
+	if (args->cmpresult == XFS_CMP_CASE) {
+		args->value = xfs_da_name_alloc();
+		memcpy(args->value, dep->name, dep->namelen);
+		args->valuelen = dep->namelen;
+	}
 	xfs_da_brelse(args->trans, bp);
 	return XFS_ERROR(EEXIST);
 }
Index: kern_ci/fs/xfs/xfs_dir2_leaf.c
===================================================================
--- kern_ci.orig/fs/xfs/xfs_dir2_leaf.c
+++ kern_ci/fs/xfs/xfs_dir2_leaf.c
@@ -1301,6 +1301,15 @@ xfs_dir2_leaf_lookup(
 	 * Return the found inode number.
 	 */
 	args->inumber = be64_to_cpu(dep->inumber);
+	/*
+	 * If a case-insensitive match, allocate a buffer and copy the actual
+	 * name into the buffer. Return it via args->value.
+	 */
+	if (args->cmpresult == XFS_CMP_CASE) {
+		args->value = xfs_da_name_alloc();
+		memcpy(args->value, dep->name, dep->namelen);
+		args->valuelen = dep->namelen;
+	}
 	xfs_da_brelse(tp, dbp);
 	xfs_da_brelse(tp, lbp);
 	return XFS_ERROR(EEXIST);
Index: kern_ci/fs/xfs/xfs_dir2_node.c
===================================================================
--- kern_ci.orig/fs/xfs/xfs_dir2_node.c
+++ kern_ci/fs/xfs/xfs_dir2_node.c
@@ -643,6 +643,8 @@ xfs_dir2_leafn_lookup_for_entry(
 			xfs_dir2_dataptr_to_off(mp, be32_to_cpu(lep->address)));
 		/*
 		 * Compare the entry, return it if it matches.
+		 * "oknoent" is set for lookup and clear for
+		 * remove and replace.
 		 */
 		cmp = args->oknoent ?
 			xfs_dir_compname(dp, dep->name, dep->namelen,
@@ -1857,10 +1859,22 @@ xfs_dir2_node_lookup(
 	if (error)
 		rval = error;
 	/*
-	 * If case-insensitive match was found in a leaf, return EEXIST.
-	 */
-	else if (rval == ENOENT && args->cmpresult == XFS_CMP_CASE)
+	 * If case-insensitive match was found (xfs_dir2_leafn_lookup_int
+	 * returns ENOENT for a case-insensitive match, but sets
+	 * args->cmpresult to XFS_CMP_CASE):
+	 *   - Allocate a buffer and copy the actual name into the buffer and
+	 *       return it via args->value.
+	 *   - set rval to EEXIST
+	 */
+	else if (rval == ENOENT && 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->value = xfs_da_name_alloc();
+		memcpy(args->value, dep->name, dep->namelen);
+		args->valuelen = dep->namelen;
 		rval = EEXIST;
+	}
 	/*
 	 * Release the btree blocks and leaf block.
 	 */
Index: kern_ci/fs/xfs/xfs_dir2_sf.c
===================================================================
--- kern_ci.orig/fs/xfs/xfs_dir2_sf.c
+++ kern_ci/fs/xfs/xfs_dir2_sf.c
@@ -815,6 +815,7 @@ xfs_dir2_sf_lookup(
 	xfs_dir2_sf_entry_t	*sfep;		/* shortform directory entry */
 	xfs_dir2_sf_t		*sfp;		/* shortform structure */
 	xfs_dacmp_t		cmp;		/* comparison result */
+	xfs_dir2_sf_entry_t	*ci_sfep;	/* case-insens. entry */
 
 	xfs_dir2_trace_args("sf_lookup", args);
 	xfs_dir2_sf_check(args);
@@ -852,6 +853,7 @@ xfs_dir2_sf_lookup(
 	/*
 	 * Loop over all the entries trying to match ours.
 	 */
+	ci_sfep = NULL;
 	for (i = 0, sfep = xfs_dir2_sf_firstentry(sfp);
 	     i < sfp->hdr.count;
 	     i++, sfep = xfs_dir2_sf_nextentry(sfp, sfep)) {
@@ -864,10 +866,19 @@ xfs_dir2_sf_lookup(
 					xfs_dir2_sf_inumberp(sfep));
 			if (cmp == XFS_CMP_EXACT)
 				return XFS_ERROR(EEXIST);
+			ci_sfep = sfep;
 		}
 	}
-	if (args->cmpresult == XFS_CMP_CASE)
+	if (args->cmpresult == XFS_CMP_CASE) {
+		/*
+		 * If a case-insensitive match, allocate a buffer and copy the
+		 * actual name into the buffer and return it via args->value.
+		 */
+		args->value = xfs_da_name_alloc();
+		memcpy(args->value, ci_sfep->name, ci_sfep->namelen);
+		args->valuelen = ci_sfep->namelen;
 		return XFS_ERROR(EEXIST);
+	}
 	/*
 	 * Didn't find it.
 	 */
Index: kern_ci/fs/xfs/xfs_rename.c
===================================================================
--- kern_ci.orig/fs/xfs/xfs_rename.c
+++ kern_ci/fs/xfs/xfs_rename.c
@@ -100,6 +100,7 @@ xfs_lock_for_rename(
 	int			i, j;
 	uint			lock_mode;
 	int			diff_dirs = (dp1 != dp2);
+	xfs_name_t		name2;
 
 	ip2 = NULL;
 
@@ -125,7 +126,9 @@ xfs_lock_for_rename(
 		lock_mode = xfs_ilock_map_shared(dp2);
 	}
 
-	error = xfs_dir_lookup_int(dp2, lock_mode, vname2, &inum2, &ip2);
+	name2.name = VNAME(vname2);
+	name2.len = VNAMELEN(vname2);
+	error = xfs_dir_lookup_int(dp2, lock_mode, &name2, &inum2, &ip2, NULL);
 	if (error == ENOENT) {		/* target does not need to exist. */
 		inum2 = 0;
 	} else if (error) {
Index: kern_ci/fs/xfs/xfs_utils.c
===================================================================
--- kern_ci.orig/fs/xfs/xfs_utils.c
+++ kern_ci/fs/xfs/xfs_utils.c
@@ -24,6 +24,7 @@
 #include "xfs_trans.h"
 #include "xfs_sb.h"
 #include "xfs_ag.h"
+#include "xfs_da_btree.h"
 #include "xfs_dir2.h"
 #include "xfs_dmapi.h"
 #include "xfs_mount.h"
@@ -45,15 +46,16 @@ int
 xfs_dir_lookup_int(
 	xfs_inode_t	*dp,
 	uint		lock_mode,
-	bhv_vname_t	*dentry,
+	xfs_name_t	*name,
 	xfs_ino_t	*inum,
-	xfs_inode_t	**ipp)
+	xfs_inode_t	**ipp,
+	xfs_name_t	*ci_name)
 {
 	int		error;
 
 	xfs_itrace_entry(dp);
 
-	error = xfs_dir_lookup(NULL, dp, VNAME(dentry), VNAMELEN(dentry), inum);
+	error = xfs_dir_lookup(NULL, dp, name, inum, ci_name);
 	if (!error) {
 		/*
 		 * Unlock the directory. We do this because we can't
@@ -80,6 +82,10 @@ xfs_dir_lookup_int(
 			xfs_ilock(dp, lock_mode);
 			error = XFS_ERROR(ENOENT);
 		}
+		if (error && ci_name && ci_name->name) {
+			xfs_da_name_free(ci_name->name);
+			ci_name->name = NULL;
+		}
 	}
 	return error;
 }
Index: kern_ci/fs/xfs/xfs_utils.h
===================================================================
--- kern_ci.orig/fs/xfs/xfs_utils.h
+++ kern_ci/fs/xfs/xfs_utils.h
@@ -18,11 +18,13 @@
 #ifndef __XFS_UTILS_H__
 #define __XFS_UTILS_H__
 
+struct xfs_name;
+
 #define IRELE(ip)	VN_RELE(XFS_ITOV(ip))
 #define IHOLD(ip)	VN_HOLD(XFS_ITOV(ip))
 
-extern int xfs_dir_lookup_int (xfs_inode_t *, uint, bhv_vname_t *, xfs_ino_t *,
-				xfs_inode_t **);
+extern int xfs_dir_lookup_int (xfs_inode_t *, uint, struct xfs_name *,
+				xfs_ino_t *, xfs_inode_t **, struct xfs_name *);
 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,
Index: kern_ci/fs/xfs/xfs_vfsops.c
===================================================================
--- kern_ci.orig/fs/xfs/xfs_vfsops.c
+++ kern_ci/fs/xfs/xfs_vfsops.c
@@ -74,6 +74,7 @@ xfs_init(void)
 	xfs_btree_cur_zone = kmem_zone_init(sizeof(xfs_btree_cur_t),
 					    "xfs_btree_cur");
 	xfs_trans_zone = kmem_zone_init(sizeof(xfs_trans_t), "xfs_trans");
+	xfs_da_name_zone = kmem_zone_init(MAXNAMELEN, "xfs_da_name");
 	xfs_da_state_zone =
 		kmem_zone_init(sizeof(xfs_da_state_t), "xfs_da_state");
 	xfs_dabuf_zone = kmem_zone_init(sizeof(xfs_dabuf_t), "xfs_dabuf");
@@ -177,6 +178,7 @@ xfs_cleanup(void)
 	kmem_zone_destroy(xfs_btree_cur_zone);
 	kmem_zone_destroy(xfs_inode_zone);
 	kmem_zone_destroy(xfs_trans_zone);
+	kmem_zone_destroy(xfs_da_name_zone);
 	kmem_zone_destroy(xfs_da_state_zone);
 	kmem_zone_destroy(xfs_dabuf_zone);
 	kmem_zone_destroy(xfs_buf_item_zone);
Index: kern_ci/fs/xfs/xfs_vnodeops.c
===================================================================
--- kern_ci.orig/fs/xfs/xfs_vnodeops.c
+++ kern_ci/fs/xfs/xfs_vnodeops.c
@@ -1762,24 +1762,33 @@ xfs_inactive(
 int
 xfs_lookup(
 	xfs_inode_t		*dp,
-	bhv_vname_t		*dentry,
-	xfs_inode_t		**ipp)
+	bhv_vstr_t		*d_name,
+	xfs_inode_t		**ipp,
+	bhv_vstr_t		*ci_name)
 {
 	xfs_inode_t		*ip;
 	xfs_ino_t		e_inum;
 	int			error;
 	uint			lock_mode;
+	xfs_name_t		name, rname;
 
 	xfs_itrace_entry(dp);
 
 	if (XFS_FORCED_SHUTDOWN(dp->i_mount))
 		return XFS_ERROR(EIO);
 
+	name.name = (uchar_t *)d_name->name;
+	name.len = d_name->len;
+	rname.name = NULL;
 	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, &name, &e_inum, &ip, &rname);
 	if (!error) {
 		*ipp = ip;
 		xfs_itrace_ref(ip);
+		if (rname.name) {
+			ci_name->name = rname.name;
+			ci_name->len = rname.len;
+		}
 	}
 	xfs_iunlock_map_shared(dp, lock_mode);
 	return error;
Index: kern_ci/fs/xfs/xfs_vnodeops.h
===================================================================
--- kern_ci.orig/fs/xfs/xfs_vnodeops.h
+++ kern_ci/fs/xfs/xfs_vnodeops.h
@@ -23,8 +23,8 @@ int xfs_fsync(struct xfs_inode *ip, int 
 		xfs_off_t stop);
 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,
-		struct xfs_inode **ipp);
+int xfs_lookup(struct xfs_inode *dp, bhv_vstr_t *d_name,
+		struct xfs_inode **ipp, bhv_vstr_t *ci_name);
 int xfs_create(struct xfs_inode *dp, bhv_vname_t *dentry, mode_t mode,
 		xfs_dev_t rdev, struct xfs_inode **ipp, struct cred *credp);
 int xfs_remove(struct xfs_inode *dp, bhv_vname_t	*dentry);

-- 

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

* [PATCH 5/7] XFS: Unicode case-insensitive lookup implementation
  2008-04-02  6:25 [PATCH 0/7] XFS: case-insensitive lookup and Unicode support Barry Naujok
                   ` (3 preceding siblings ...)
  2008-04-02  6:25 ` [PATCH 4/7] XFS: Return case-insensitive match for dentry cache Barry Naujok
@ 2008-04-02  6:25 ` Barry Naujok
  2008-04-03  8:31   ` David Chinner
  2008-04-03 17:14   ` Christoph Hellwig
  2008-04-02  6:25 ` [PATCH 6/7] XFS: Native Language Support for Unicode in XFS Barry Naujok
                   ` (2 subsequent siblings)
  7 siblings, 2 replies; 48+ messages in thread
From: Barry Naujok @ 2008-04-02  6:25 UTC (permalink / raw)
  To: xfs; +Cc: linux-fsdevel

[-- Attachment #1: unicode_ci.patch --]
[-- Type: text/plain, Size: 41044 bytes --]

This is the core of the case-insensitive support - supporting and
enforcing UTF-8 (Unicode) filenames. All filename and user-level
extended attribute names are checked for UTF-8 compliance and the
hashes generated are always case-insensitive by utilising the
Unicode 5.0 standard case-folding table from:
http://www.unicode.org/Public/UNIDATA/CaseFolding.txt

As the hash is always case-insensitive, this allows the user to
mkfs.xfs the filesystem once and enable or disable (default)
case-insensitive support by a mount option "-o ci". The mount
option specifies which xfs_nameops.compname function to use.

Also, the Unicode support is a CONFIG option so users who do
not required this functionality can CONFIG it to N.

As the case-folding table is stored on disk, this allows
backwards and forwards compatibility and languages like Turkic
to support true case-insensitivity with I and i.

To create a Unicode filesystem with case-insensitive mount
support, run:
# mkfs.xfs -n utf8[=default|turkic] <device>

The final patches implement NLS support for XFS Unicode.

Signed-off-by: Barry Naujok <bnaujok@sgi.com>

---
 fs/xfs/Kconfig               |   17 +
 fs/xfs/Makefile              |    4 
 fs/xfs/linux-2.6/xfs_linux.h |    1 
 fs/xfs/linux-2.6/xfs_super.c |   14 +
 fs/xfs/linux-2.6/xfs_super.h |    7 
 fs/xfs/xfs_attr.c            |   24 ++
 fs/xfs/xfs_clnt.h            |    2 
 fs/xfs/xfs_da_btree.c        |   18 +
 fs/xfs/xfs_da_btree.h        |   16 -
 fs/xfs/xfs_dir2.c            |   13 -
 fs/xfs/xfs_dir2.h            |    5 
 fs/xfs/xfs_fs.h              |    1 
 fs/xfs/xfs_fsops.c           |    4 
 fs/xfs/xfs_itable.c          |    2 
 fs/xfs/xfs_mount.c           |   21 +
 fs/xfs/xfs_mount.h           |    7 
 fs/xfs/xfs_rename.c          |    9 
 fs/xfs/xfs_sb.h              |   29 ++
 fs/xfs/xfs_unicode.c         |  499 +++++++++++++++++++++++++++++++++++++++++++
 fs/xfs/xfs_unicode.h         |   81 ++++++
 fs/xfs/xfs_vfsops.c          |   16 +
 fs/xfs/xfs_vnodeops.c        |   52 ++++
 22 files changed, 808 insertions(+), 34 deletions(-)

Index: kern_ci/fs/xfs/Kconfig
===================================================================
--- kern_ci.orig/fs/xfs/Kconfig
+++ kern_ci/fs/xfs/Kconfig
@@ -72,6 +72,21 @@ config XFS_POSIX_ACL
 
 	  If you don't know what Access Control Lists are, say N.
 
+config XFS_UNICODE
+	bool "XFS Unicode support"
+	depends on XFS_FS
+	help
+	  Unicode support enforces UTF-8 filenames and user extended
+	  attribute names. This option is required for filesystems
+	  mkfs'ed with UTF-8 support. A Unicode filesystem guarantees
+	  that filenames will be the same regardless of the user's
+	  locale. For UTF-8 locales, no conversion is required.
+
+	  Unicode filesystems also allow the filesystem to be mounted with
+	  case-insensitive lookup support with the "-o ci" mount option.
+
+	  If you don't require UTF-8 enforcement, say N.
+
 config XFS_RT
 	bool "XFS Realtime subvolume support"
 	depends on XFS_FS
@@ -107,7 +122,7 @@ config XFS_TRACE
 	bool "XFS Tracing support (EXPERIMENTAL)"
 	depends on XFS_FS && EXPERIMENTAL
 	help
-	  Say Y here to get an XFS build with activity tracing enabled.	
+	  Say Y here to get an XFS build with activity tracing enabled.
 	  Enabling this option will attach historical information to XFS
 	  inodes, buffers, certain locks, the log, the IO path, and a
 	  few other key areas within XFS.  These traces can be examined
Index: kern_ci/fs/xfs/Makefile
===================================================================
--- kern_ci.orig/fs/xfs/Makefile
+++ kern_ci/fs/xfs/Makefile
@@ -30,11 +30,11 @@ obj-$(CONFIG_XFS_DMAPI)		+= dmapi/
 
 xfs-$(CONFIG_XFS_RT)		+= xfs_rtalloc.o
 xfs-$(CONFIG_XFS_POSIX_ACL)	+= xfs_acl.o
+xfs-$(CONFIG_XFS_UNICODE)	+= xfs_unicode.o
 xfs-$(CONFIG_PROC_FS)		+= $(XFS_LINUX)/xfs_stats.o
 xfs-$(CONFIG_SYSCTL)		+= $(XFS_LINUX)/xfs_sysctl.o
 xfs-$(CONFIG_COMPAT)		+= $(XFS_LINUX)/xfs_ioctl32.o
 
-
 xfs-y				+= xfs_alloc.o \
 				   xfs_alloc_btree.o \
 				   xfs_attr.o \
@@ -97,7 +97,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/, \
Index: kern_ci/fs/xfs/linux-2.6/xfs_linux.h
===================================================================
--- kern_ci.orig/fs/xfs/linux-2.6/xfs_linux.h
+++ kern_ci/fs/xfs/linux-2.6/xfs_linux.h
@@ -76,6 +76,7 @@
 #include <linux/log2.h>
 #include <linux/spinlock.h>
 #include <linux/ctype.h>
+#include <linux/nls.h>
 
 #include <asm/page.h>
 #include <asm/div64.h>
Index: kern_ci/fs/xfs/linux-2.6/xfs_super.c
===================================================================
--- kern_ci.orig/fs/xfs/linux-2.6/xfs_super.c
+++ kern_ci/fs/xfs/linux-2.6/xfs_super.c
@@ -46,6 +46,7 @@
 #include "xfs_acl.h"
 #include "xfs_attr.h"
 #include "xfs_buf_item.h"
+#include "xfs_unicode.h"
 #include "xfs_utils.h"
 #include "xfs_vnodeops.h"
 #include "xfs_vfsops.h"
@@ -124,6 +125,7 @@ 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_CILOOKUP	"ci"		/* case-insensitive dir lookup */
 #define MNTOPT_QUOTA	"quota"		/* disk quotas (user) */
 #define MNTOPT_NOQUOTA	"noquota"	/* no quotas */
 #define MNTOPT_USRQUOTA	"usrquota"	/* user quota enabled */
@@ -318,6 +320,14 @@ xfs_parseargs(
 			args->flags &= ~XFSMNT_ATTR2;
 		} else if (!strcmp(this_char, MNTOPT_FILESTREAM)) {
 			args->flags2 |= XFSMNT2_FILESTREAMS;
+		} else if (!strcmp(this_char, MNTOPT_CILOOKUP)) {
+			args->flags2 |= XFSMNT2_CILOOKUP;
+#ifndef CONFIG_XFS_UNICODE
+			cmn_err(CE_WARN,
+				"XFS: %s option requires Unicode support",
+				this_char);
+			return EINVAL;
+#endif
 		} else if (!strcmp(this_char, MNTOPT_NOQUOTA)) {
 			args->flags &= ~(XFSMNT_UQUOTAENF|XFSMNT_UQUOTA);
 			args->flags &= ~(XFSMNT_GQUOTAENF|XFSMNT_GQUOTA);
@@ -458,6 +468,7 @@ xfs_showargs(
 		{ XFS_MOUNT_OSYNCISOSYNC,	"," MNTOPT_OSYNCISOSYNC },
 		{ XFS_MOUNT_ATTR2,		"," MNTOPT_ATTR2 },
 		{ XFS_MOUNT_FILESTREAMS,	"," MNTOPT_FILESTREAM },
+		{ XFS_MOUNT_CILOOKUP,		"," MNTOPT_CILOOKUP },
 		{ XFS_MOUNT_DMAPI,		"," MNTOPT_DMAPI },
 		{ XFS_MOUNT_GRPID,		"," MNTOPT_GRPID },
 		{ 0, NULL }
@@ -567,7 +578,8 @@ xfs_set_inodeops(
 		break;
 	case S_IFDIR:
 		inode->i_op =
-			xfs_sb_version_hasoldci(&XFS_I(inode)->i_mount->m_sb) ?
+			xfs_sb_version_hasoldci(&XFS_I(inode)->i_mount->m_sb) ||
+			(XFS_I(inode)->i_mount->m_flags & XFS_MOUNT_CILOOKUP) ?
 				&xfs_dir_ci_inode_operations :
 				&xfs_dir_inode_operations;
 		inode->i_fop = &xfs_dir_file_operations;
Index: kern_ci/fs/xfs/linux-2.6/xfs_super.h
===================================================================
--- kern_ci.orig/fs/xfs/linux-2.6/xfs_super.h
+++ kern_ci/fs/xfs/linux-2.6/xfs_super.h
@@ -36,6 +36,12 @@
 # define ENOSECURITY		EOPNOTSUPP
 #endif
 
+#ifdef CONFIG_XFS_UNICODE
+# define XFS_UNICODE_STRING	"Unicode, "
+#else
+# define XFS_UNICODE_STRING
+#endif
+
 #ifdef CONFIG_XFS_RT
 # define XFS_REALTIME_STRING	"realtime, "
 #else
@@ -66,6 +72,7 @@
 
 #define XFS_BUILD_OPTIONS	XFS_ACL_STRING \
 				XFS_SECURITY_STRING \
+				XFS_UNICODE_STRING \
 				XFS_REALTIME_STRING \
 				XFS_BIGFS_STRING \
 				XFS_TRACE_STRING \
Index: kern_ci/fs/xfs/xfs_attr.c
===================================================================
--- kern_ci.orig/fs/xfs/xfs_attr.c
+++ kern_ci/fs/xfs/xfs_attr.c
@@ -50,6 +50,7 @@
 #include "xfs_acl.h"
 #include "xfs_rw.h"
 #include "xfs_vnodeops.h"
+#include "xfs_unicode.h"
 
 /*
  * xfs_attr.c
@@ -175,6 +176,13 @@ xfs_attr_get(
 	if (namelen >= MAXNAMELEN)
 		return(EFAULT);		/* match IRIX behaviour */
 
+	/* Enforce UTF-8 only for user attr names */
+	if (xfs_sb_version_hasunicode(&ip->i_mount->m_sb) &&
+			(flags & (ATTR_ROOT | ATTR_SECURE)) == 0) {
+		error = xfs_unicode_validate(name, namelen);
+		if (error)
+			return error;
+	}
 	if (XFS_FORCED_SHUTDOWN(ip->i_mount))
 		return(EIO);
 
@@ -435,6 +443,14 @@ xfs_attr_set(
 	if (namelen >= MAXNAMELEN)
 		return EFAULT;		/* match IRIX behaviour */
 
+	/* Enforce UTF-8 only for user attr names */
+	if (xfs_sb_version_hasunicode(&dp->i_mount->m_sb) &&
+			(flags & (ATTR_ROOT | ATTR_SECURE)) == 0) {
+		int error = xfs_unicode_validate(name, namelen);
+		if (error)
+			return error;
+	}
+
 	XFS_STATS_INC(xs_attr_set);
 
 	if (XFS_FORCED_SHUTDOWN(dp->i_mount))
@@ -581,6 +597,14 @@ xfs_attr_remove(
 	if (namelen >= MAXNAMELEN)
 		return EFAULT;		/* match IRIX behaviour */
 
+	/* Enforce UTF-8 only for user attr names */
+	if (xfs_sb_version_hasunicode(&dp->i_mount->m_sb) &&
+			(flags & (ATTR_ROOT | ATTR_SECURE)) == 0) {
+		int error = xfs_unicode_validate(name, namelen);
+		if (error)
+			return error;
+	}
+
 	XFS_STATS_INC(xs_attr_remove);
 
 	if (XFS_FORCED_SHUTDOWN(dp->i_mount))
Index: kern_ci/fs/xfs/xfs_clnt.h
===================================================================
--- kern_ci.orig/fs/xfs/xfs_clnt.h
+++ kern_ci/fs/xfs/xfs_clnt.h
@@ -100,5 +100,7 @@ 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 */
 
 #endif	/* __XFS_CLNT_H__ */
Index: kern_ci/fs/xfs/xfs_da_btree.c
===================================================================
--- kern_ci.orig/fs/xfs/xfs_da_btree.c
+++ kern_ci/fs/xfs/xfs_da_btree.c
@@ -1530,16 +1530,22 @@ xfs_da_hashname(const uchar_t *name, int
 	}
 }
 
-xfs_dacmp_t
-xfs_da_compname(const uchar_t *name1, int len1, const uchar_t *name2, int len2)
+static xfs_dahash_t
+xfs_default_hashname(xfs_inode_t *inode, const uchar_t *name, int namelen)
 {
-	return (len1 == len2 && memcmp(name1, name2, len1) == 0) ?
-			XFS_CMP_EXACT : XFS_CMP_DIFFERENT;
+	return xfs_da_hashname(name, namelen);
+}
+
+static xfs_dacmp_t
+xfs_default_compname(xfs_inode_t *inode, const uchar_t *name1, int namelen1,
+		     const uchar_t *name2, int namelen2)
+{
+	return xfs_da_compname(name1, namelen1, name2, namelen2);
 }
 
 struct xfs_nameops xfs_default_nameops = {
-	.hashname	= xfs_da_hashname,
-	.compname	= xfs_da_compname
+	.hashname	= xfs_default_hashname,
+	.compname	= xfs_default_compname
 };
 
 /*
Index: kern_ci/fs/xfs/xfs_da_btree.h
===================================================================
--- kern_ci.orig/fs/xfs/xfs_da_btree.h
+++ kern_ci/fs/xfs/xfs_da_btree.h
@@ -215,9 +215,10 @@ typedef struct xfs_da_state {
  * Name ops for directory and/or attr name operations
  */
 
-typedef xfs_dahash_t	(*xfs_hashname_t)(const uchar_t *, int);
-typedef xfs_dacmp_t	(*xfs_compname_t)(const uchar_t *, int,
-					  const uchar_t *, int);
+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;
@@ -282,8 +283,13 @@ int	xfs_da_shrink_inode(xfs_da_args_t *a
 extern struct xfs_nameops xfs_default_nameops;
 
 uint xfs_da_hashname(const uchar_t *name_string, int name_length);
-xfs_dacmp_t xfs_da_compname(const uchar_t *name1, int len1,
-			    const uchar_t *name2, int len2);
+
+static inline xfs_dacmp_t
+xfs_da_compname(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;
+}
 
 /* returns/frees a MAXNAMELEN buffer from a zone */
 extern struct kmem_zone *xfs_da_name_zone;
Index: kern_ci/fs/xfs/xfs_dir2.c
===================================================================
--- kern_ci.orig/fs/xfs/xfs_dir2.c
+++ kern_ci/fs/xfs/xfs_dir2.c
@@ -43,7 +43,7 @@
 #include "xfs_dir2_trace.h"
 #include "xfs_error.h"
 #include "xfs_vnodeops.h"
-
+#include "xfs_unicode.h"
 
 /*
  * V1/OLDCI case-insensitive support for directories
@@ -52,6 +52,7 @@
  */
 static xfs_dahash_t
 xfs_ascii_ci_hashname(
+	xfs_inode_t	*inode,
 	const uchar_t	*name,
 	int		namelen)
 {
@@ -66,6 +67,7 @@ xfs_ascii_ci_hashname(
 
 static xfs_dacmp_t
 xfs_ascii_ci_compname(
+	xfs_inode_t	*inode,
 	const uchar_t	*name1,
 	int		len1,
 	const uchar_t	*name2,
@@ -113,8 +115,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;
-	mp->m_dirnameops = xfs_sb_version_hasoldci(&mp->m_sb) ?
-		&xfs_ascii_ci_nameops : &xfs_default_nameops;
+
+	if (xfs_sb_version_hasunicode(&mp->m_sb)) {
+		mp->m_dirnameops = (mp->m_flags & XFS_MOUNT_CILOOKUP) ?
+			&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;
 }
 
 /*
Index: kern_ci/fs/xfs/xfs_dir2.h
===================================================================
--- kern_ci.orig/fs/xfs/xfs_dir2.h
+++ kern_ci/fs/xfs/xfs_dir2.h
@@ -88,10 +88,11 @@ extern int xfs_dir_canenter(struct xfs_t
 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((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((n1), (l1), (n2), (l2)))
+		((dp)->i_mount->m_dirnameops->compname((dp), (n1), (l1), \
+							(n2), (l2)))
 
 /*
  * Utility routines for v2 directories.
Index: kern_ci/fs/xfs/xfs_fs.h
===================================================================
--- kern_ci.orig/fs/xfs/xfs_fs.h
+++ kern_ci/fs/xfs/xfs_fs.h
@@ -241,6 +241,7 @@ typedef struct xfs_fsop_resblks {
 #define XFS_FSOP_GEOM_FLAGS_ATTR2	0x0400	/* inline attributes rework */
 #define XFS_FSOP_GEOM_FLAGS_DIRV2CI	0x1000	/* ASCII only CI names */
 #define XFS_FSOP_GEOM_FLAGS_LAZYSB	0x4000	/* lazy superblock counters */
+#define XFS_FSOP_GEOM_FLAGS_UNICODE	0x10000	/* unicode filenames */
 
 
 /*
Index: kern_ci/fs/xfs/xfs_fsops.c
===================================================================
--- kern_ci.orig/fs/xfs/xfs_fsops.c
+++ kern_ci/fs/xfs/xfs_fsops.c
@@ -100,7 +100,9 @@ xfs_fs_geometry(
 			(xfs_sb_version_haslazysbcount(&mp->m_sb) ?
 				XFS_FSOP_GEOM_FLAGS_LAZYSB : 0) |
 			(xfs_sb_version_hasattr2(&mp->m_sb) ?
-				XFS_FSOP_GEOM_FLAGS_ATTR2 : 0);
+				XFS_FSOP_GEOM_FLAGS_ATTR2 : 0) |
+			(xfs_sb_version_hasunicode(&mp->m_sb) ?
+				XFS_FSOP_GEOM_FLAGS_UNICODE : 0);
 		geo->logsectsize = xfs_sb_version_hassector(&mp->m_sb) ?
 				mp->m_sb.sb_logsectsize : BBSIZE;
 		geo->rtsectsize = mp->m_sb.sb_blocksize;
Index: kern_ci/fs/xfs/xfs_itable.c
===================================================================
--- kern_ci.orig/fs/xfs/xfs_itable.c
+++ kern_ci/fs/xfs/xfs_itable.c
@@ -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)));
 }
Index: kern_ci/fs/xfs/xfs_mount.c
===================================================================
--- kern_ci.orig/fs/xfs/xfs_mount.c
+++ kern_ci/fs/xfs/xfs_mount.c
@@ -44,6 +44,7 @@
 #include "xfs_quota.h"
 #include "xfs_fsops.h"
 #include "xfs_utils.h"
+#include "xfs_unicode.h"
 
 STATIC void	xfs_mount_log_sb(xfs_mount_t *, __int64_t);
 STATIC int	xfs_uuid_mount(xfs_mount_t *);
@@ -121,6 +122,7 @@ static const struct {
     { 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 }
 };
 
@@ -167,6 +169,7 @@ xfs_mount_free(
 			  sizeof(xfs_perag_t) * mp->m_sb.sb_agcount);
 	}
 
+	xfs_unicode_free_cft(mp->m_cft);
 	spinlock_destroy(&mp->m_ail_lock);
 	spinlock_destroy(&mp->m_sb_lock);
 	mutex_destroy(&mp->m_ilock);
@@ -452,6 +455,7 @@ xfs_sb_from_disk(
 	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);
 }
 
 /*
@@ -1175,6 +1179,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 changes.
 	 */
 	if (update_flags && !(mp->m_flags & XFS_MOUNT_RDONLY))
@@ -1229,7 +1245,8 @@ xfs_mountfs(
 	 * Free up the root inode.
 	 */
 	IRELE(rip);
- error3:
+	xfs_unicode_free_cft(mp->m_cft);
+error3:
 	xfs_log_unmount_dealloc(mp);
  error2:
 	for (agno = 0; agno < sbp->sb_agcount; agno++)
@@ -1956,7 +1973,7 @@ xfs_mount_log_sb(
  * 	3. accurate counter sync requires m_sb_lock + per cpu locks
  * 	4. modifying per-cpu counters requires holding per-cpu lock
  * 	5. modifying global counters requires holding m_sb_lock
- *	6. enabling or disabling a counter requires holding the m_sb_lock 
+ *	6. enabling or disabling a counter requires holding the m_sb_lock
  *	   and _none_ of the per-cpu locks.
  *
  * Disabled counters are only ever re-enabled by a balance operation
Index: kern_ci/fs/xfs/xfs_mount.h
===================================================================
--- kern_ci.orig/fs/xfs/xfs_mount.h
+++ kern_ci/fs/xfs/xfs_mount.h
@@ -62,6 +62,7 @@ struct xfs_extdelta;
 struct xfs_swapext;
 struct xfs_mru_cache;
 struct xfs_nameops;
+struct xfs_cft;
 
 /*
  * Prototypes and functions for the Data Migration subsystem.
@@ -314,6 +315,7 @@ typedef struct xfs_mount {
 						   field governed by m_ilock */
 	__uint8_t		m_sectbb_log;	/* sectlog - BBSHIFT */
 	struct xfs_nameops	*m_dirnameops;	/* vector of dir name ops */
+	struct xfs_cft		*m_cft;		/* unicode case fold 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 */
@@ -379,7 +381,8 @@ typedef struct xfs_mount {
 						   counters */
 #define XFS_MOUNT_FILESTREAMS	(1ULL << 24)	/* enable the filestreams
 						   allocator */
-
+#define XFS_MOUNT_CILOOKUP	(1ULL << 25)	/* enable case-insensitive
+						   file lookup */
 
 /*
  * Default minimum read and write sizes.
@@ -403,7 +406,7 @@ typedef struct xfs_mount {
 
 /*
  * Allow large block sizes to be reported to userspace programs if the
- * "largeio" mount option is used. 
+ * "largeio" mount option is used.
  *
  * If compatibility mode is specified, simply return the basic unit of caching
  * so that we don't get inefficient read/modify/write I/O from user apps.
Index: kern_ci/fs/xfs/xfs_rename.c
===================================================================
--- kern_ci.orig/fs/xfs/xfs_rename.c
+++ kern_ci/fs/xfs/xfs_rename.c
@@ -39,6 +39,7 @@
 #include "xfs_utils.h"
 #include "xfs_trans_space.h"
 #include "xfs_vnodeops.h"
+#include "xfs_unicode.h"
 
 
 /*
@@ -248,6 +249,14 @@ xfs_rename(
 	xfs_itrace_entry(src_dp);
 	xfs_itrace_entry(target_dp);
 
+	if (xfs_sb_version_hasunicode(&mp->m_sb)) {
+		error = xfs_unicode_validate(src_name, src_namelen);
+		if (error)
+			return error;
+		error = xfs_unicode_validate(target_name, target_namelen);
+		if (error)
+			return error;
+	}
 	if (DM_EVENT_ENABLED(src_dp, DM_EVENT_RENAME) ||
 	    DM_EVENT_ENABLED(target_dp, DM_EVENT_RENAME)) {
 		error = XFS_SEND_NAMESP(mp, DM_EVENT_RENAME,
Index: kern_ci/fs/xfs/xfs_sb.h
===================================================================
--- kern_ci.orig/fs/xfs/xfs_sb.h
+++ kern_ci/fs/xfs/xfs_sb.h
@@ -79,10 +79,18 @@ 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	\
+#ifdef CONFIG_XFS_UNICODE
+# define XFS_SB_VERSION2_OKREALFBITS	\
 	(XFS_SB_VERSION2_LAZYSBCOUNTBIT	| \
+	 XFS_SB_VERSION2_UNICODEBIT | \
 	 XFS_SB_VERSION2_ATTR2BIT)
+#else
+# define XFS_SB_VERSION2_OKREALFBITS	\
+	(XFS_SB_VERSION2_LAZYSBCOUNTBIT	| \
+	 XFS_SB_VERSION2_ATTR2BIT)
+#endif
 #define	XFS_SB_VERSION2_OKSASHFBITS	\
 	(0)
 #define XFS_SB_VERSION2_OKREALBITS	\
@@ -156,6 +164,7 @@ typedef struct xfs_sb {
 	 * it for anything else.
 	 */
 	__uint32_t	sb_bad_features2;
+	xfs_ino_t	sb_cftino;	/* unicode case folding table inode */
 
 	/* must be padded to 64 bit alignment */
 } xfs_sb_t;
@@ -225,7 +234,8 @@ typedef struct xfs_dsb {
 	 * for features2 bits. Easiest just to mark it bad and not use
 	 * it for anything else.
 	 */
-	__be32	sb_bad_features2;
+	__be32		sb_bad_features2;
+	__be64		sb_cftino;	/* unicode case folding table inode */
 
 	/* must be padded to 64 bit alignment */
 } xfs_dsb_t;
@@ -246,7 +256,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_BAD_FEATURES2,
+	XFS_SBS_FEATURES2, XFS_SBS_BAD_FEATURES2, XFS_SBS_CFTINO,
 	XFS_SBS_FIELDCOUNT
 } xfs_sb_field_t;
 
@@ -272,6 +282,7 @@ typedef enum {
 #define XFS_SB_FDBLOCKS		XFS_SB_MVAL(FDBLOCKS)
 #define XFS_SB_FEATURES2	XFS_SB_MVAL(FEATURES2)
 #define XFS_SB_BAD_FEATURES2	XFS_SB_MVAL(BAD_FEATURES2)
+#define XFS_SB_CFTINO		XFS_SB_MVAL(CFTINO)
 #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		\
@@ -279,7 +290,7 @@ typedef enum {
 	 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_BAD_FEATURES2)
+	 XFS_SB_BAD_FEATURES2 | XFS_SB_CFTINO)
 
 
 /*
@@ -480,6 +491,16 @@ static inline void xfs_sb_version_addatt
 		((sbp)->sb_features2 | XFS_SB_VERSION2_ATTR2BIT)));
 }
 
+#ifdef CONFIG_XFS_UNICODE
+static inline int xfs_sb_version_hasunicode(xfs_sb_t *sbp)
+{
+	return (xfs_sb_version_hasmorebits(sbp) &&	\
+		((sbp)->sb_features2 & XFS_SB_VERSION2_UNICODEBIT));
+}
+#else
+# define xfs_sb_version_hasunicode(sbp)	(0)
+#endif
+
 /*
  * end of superblock version macros
  */
Index: kern_ci/fs/xfs/xfs_unicode.c
===================================================================
--- /dev/null
+++ kern_ci/fs/xfs/xfs_unicode.c
@@ -0,0 +1,499 @@
+/*
+ * Copyright (c) 2007-2008 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_da_btree.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 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);
+}
+
+/*
+ * always generate a case-folded hash to allow mount-time selection of
+ * case-insensitive lookup (rather than mkfs time).
+ */
+xfs_dahash_t
+xfs_unicode_hashname(
+	xfs_inode_t	*inode,
+	const uchar_t	*name,
+	int		namelen)
+{
+	xfs_dahash_t	hash = 0;
+	__uint16_t	fc[MAX_FOLD_CHARS];
+	int		nfc;
+	int		i;
+
+	while (namelen > 0) {
+		nfc = xfs_utf8_casefold(inode->i_mount->m_cft, &name, &namelen,
+				fc);
+		for (i = 0; i < nfc; i++)
+			hash = fc[i] ^ rol32(hash, 7);
+	}
+	return hash;
+}
+
+/*
+ * Perform a case-folding case-insensitive string comparison,
+ * returns either XFS_CMP_CASE or XFS_CMP_DIFFERENT.
+ */
+static xfs_dacmp_t
+xfs_unicode_casecmp(
+	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 XFS_CMP_DIFFERENT;
+		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 XFS_CMP_DIFFERENT;
+	return XFS_CMP_CASE;
+
+}
+
+/*
+ * Compare two UTF-8 names to see if they are exactly the same or
+ * case-insensitive match.
+ */
+xfs_dacmp_t
+xfs_unicode_compname(
+	xfs_inode_t	*inode,
+	const uchar_t	*name1,
+	int		len1,
+	const uchar_t	*name2,
+	int		len2)
+{
+	wchar_t		uc1, uc2;
+	int		n;
+
+	/*
+	 * If the lengths are different, go straight to the case-insensitive
+	 * comparison
+	 */
+	if (len1 != len2)
+		return xfs_unicode_casecmp(inode->i_mount->m_cft,
+					name1, len1, name2, len2);
+
+	/*
+	 * Start by comparing one-to-one UTF-8 chars. If we have a mismatch,
+	 * downgrade to case-insensitive comparison on the rest of the names.
+	 * At this stage, we only need to maintain one length variable.
+	 */
+	while (len1) {
+		/*
+		 * first do a direct compare, if different, try the
+		 * case-insensitive comparison on the remainder.
+		 */
+		if (*name1 != *name2)
+			return xfs_unicode_casecmp(inode->i_mount->m_cft,
+						name1, len1, name2, len1);
+		/*
+		 * if we are working on a UTF-8 sequence, take in all
+		 * appropriate chars and then compare.
+		 */
+		if (*name1 >= 0x80) {
+			n = utf8_mbtowc(&uc1, name1, len1);
+			if (n < 0)
+				return XFS_CMP_DIFFERENT; /* invalid */
+			utf8_mbtowc(&uc2, name2, len1);
+			/*
+			 * no need to check "n" here as the first char
+			 * determines the length of a UTF-8 sequence.
+			 */
+			if (uc1 != uc2)
+				return xfs_unicode_casecmp(
+						inode->i_mount->m_cft,
+						name1, len1, name2, len1);
+		} else {
+			n = 1;
+		}
+		name1 += n;
+		name2 += n;
+		len1 -= n;
+	}
+	/*
+	 * to get here, all chars must have matched
+	 */
+	return XFS_CMP_EXACT;
+}
+
+static xfs_dacmp_t
+xfs_default_compname(
+	xfs_inode_t	*inode,
+	const uchar_t	*name1,
+	int		namelen1,
+	const uchar_t	*name2,
+	int		namelen2)
+{
+	return xfs_da_compname(name1, namelen1, name2, namelen2);
+}
+
+struct xfs_nameops xfs_unicode_nameops = {
+	.hashname	= xfs_unicode_hashname,
+	.compname	= xfs_default_compname,
+};
+
+struct xfs_nameops xfs_unicode_ci_nameops = {
+	.hashname	= xfs_unicode_hashname,
+	.compname	= xfs_unicode_compname,
+};
+
+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)
+{
+	if (cft)
+		remove_cft(cft);
+}
+
+void
+xfs_unicode_init(void)
+{
+	mutex_init(&cft_lock);
+}
+
+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);
+}
Index: kern_ci/fs/xfs/xfs_unicode.h
===================================================================
--- /dev/null
+++ kern_ci/fs/xfs/xfs_unicode.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2007-2008 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]))
+
+#ifdef CONFIG_XFS_UNICODE
+
+extern struct xfs_nameops xfs_unicode_nameops;
+extern struct xfs_nameops xfs_unicode_ci_nameops;
+
+void xfs_unicode_init(void);
+void xfs_unicode_uninit(void);
+
+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);
+
+#else
+
+#define xfs_unicode_nameops		xfs_default_nameops
+#define xfs_unicode_ci_nameops		xfs_default_nameops
+
+#define xfs_unicode_init()
+#define xfs_unicode_uninit()
+#define xfs_unicode_validate(n,l)	0
+#define xfs_unicode_read_cft(mp)	(EOPNOTSUPP)
+#define xfs_unicode_free_cft(cft)
+
+#endif	/* CONFIG_XFS_UNICODE */
+
+#endif /* __XFS_UNICODE_H__ */
Index: kern_ci/fs/xfs/xfs_vfsops.c
===================================================================
--- kern_ci.orig/fs/xfs/xfs_vfsops.c
+++ kern_ci/fs/xfs/xfs_vfsops.c
@@ -56,6 +56,7 @@
 #include "xfs_vnodeops.h"
 #include "xfs_vfsops.h"
 #include "xfs_utils.h"
+#include "xfs_unicode.h"
 
 
 int __init
@@ -82,6 +83,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
@@ -157,6 +159,7 @@ xfs_cleanup(void)
 	xfs_filestream_uninit();
 	xfs_mru_cache_uninit();
 	xfs_acl_zone_destroy(xfs_acl_zone);
+	xfs_unicode_uninit();
 
 #ifdef XFS_DIR2_TRACE
 	ktrace_free(xfs_dir2_trace_buf);
@@ -399,6 +402,19 @@ 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_CILOOKUP;
+	} else {
+		/*
+		 * Check for mount options which require a Unicode FS
+		 */
+		if (ap->flags2 & XFSMNT2_CILOOKUP) {
+			cmn_err(CE_WARN,
+	"XFS: can't do case-insensitive mount on non-utf8 filesystem");
+			return XFS_ERROR(EINVAL);
+		}
+	}
 	return 0;
 }
 
Index: kern_ci/fs/xfs/xfs_vnodeops.c
===================================================================
--- kern_ci.orig/fs/xfs/xfs_vnodeops.c
+++ kern_ci/fs/xfs/xfs_vnodeops.c
@@ -52,6 +52,7 @@
 #include "xfs_log_priv.h"
 #include "xfs_filestream.h"
 #include "xfs_vnodeops.h"
+#include "xfs_unicode.h"
 
 int
 xfs_open(
@@ -1777,6 +1778,12 @@ xfs_lookup(
 	if (XFS_FORCED_SHUTDOWN(dp->i_mount))
 		return XFS_ERROR(EIO);
 
+	if (xfs_sb_version_hasunicode(&dp->i_mount->m_sb)) {
+		error = xfs_unicode_validate(d_name->name, d_name->len);
+		if (error)
+			return error;
+	}
+
 	name.name = (uchar_t *)d_name->name;
 	name.len = d_name->len;
 	rname.name = NULL;
@@ -1822,8 +1829,17 @@ xfs_create(
 	ASSERT(!*ipp);
 	xfs_itrace_entry(dp);
 
+	if (XFS_FORCED_SHUTDOWN(mp))
+		return XFS_ERROR(EIO);
+
 	namelen = VNAMELEN(dentry);
 
+	if (xfs_sb_version_hasunicode(&mp->m_sb)) {
+		error = xfs_unicode_validate(name, namelen);
+		if (error)
+			return error;
+	}
+
 	if (DM_EVENT_ENABLED(dp, DM_EVENT_CREATE)) {
 		error = XFS_SEND_NAMESP(mp, DM_EVENT_CREATE,
 				dp, DM_RIGHT_NULL, NULL,
@@ -1835,9 +1851,6 @@ xfs_create(
 		dm_event_sent = 1;
 	}
 
-	if (XFS_FORCED_SHUTDOWN(mp))
-		return XFS_ERROR(EIO);
-
 	/* Return through std_return after this point. */
 
 	udqp = gdqp = NULL;
@@ -2282,7 +2295,7 @@ xfs_remove(
 	xfs_inode_t             *ip = VNAME_TO_INODE(dentry);
 	int			namelen = VNAMELEN(dentry);
 	xfs_trans_t             *tp = NULL;
-	int                     error = 0;
+	int                     error;
 	xfs_bmap_free_t         free_list;
 	xfs_fsblock_t           first_block;
 	int			cancel_flags;
@@ -2295,6 +2308,12 @@ xfs_remove(
 	if (XFS_FORCED_SHUTDOWN(mp))
 		return XFS_ERROR(EIO);
 
+	if (xfs_sb_version_hasunicode(&mp->m_sb)) {
+		error = xfs_unicode_validate(name, namelen);
+		if (error)
+			return error;
+	}
+
 	if (DM_EVENT_ENABLED(dp, DM_EVENT_REMOVE)) {
 		error = XFS_SEND_NAMESP(mp, DM_EVENT_REMOVE, dp,
 					DM_RIGHT_NULL, NULL, DM_RIGHT_NULL,
@@ -2504,6 +2523,12 @@ xfs_link(
 	if (XFS_FORCED_SHUTDOWN(mp))
 		return XFS_ERROR(EIO);
 
+	if (xfs_sb_version_hasunicode(&mp->m_sb)) {
+		error = xfs_unicode_validate(target_name, target_namelen);
+		if (error)
+			return error;
+	}
+
 	if (DM_EVENT_ENABLED(tdp, DM_EVENT_LINK)) {
 		error = XFS_SEND_NAMESP(mp, DM_EVENT_LINK,
 					tdp, DM_RIGHT_NULL,
@@ -2661,6 +2686,12 @@ xfs_mkdir(
 	if (XFS_FORCED_SHUTDOWN(mp))
 		return XFS_ERROR(EIO);
 
+	if (xfs_sb_version_hasunicode(&mp->m_sb)) {
+		error = xfs_unicode_validate(dir_name, dir_namelen);
+		if (error)
+			return error;
+	}
+
 	tp = NULL;
 
 	if (DM_EVENT_ENABLED(dp, DM_EVENT_CREATE)) {
@@ -2869,6 +2900,12 @@ xfs_rmdir(
 	if (XFS_FORCED_SHUTDOWN(mp))
 		return XFS_ERROR(EIO);
 
+	if (xfs_sb_version_hasunicode(&mp->m_sb)) {
+		error = xfs_unicode_validate(name, namelen);
+		if (error)
+			return error;
+	}
+
 	if (DM_EVENT_ENABLED(dp, DM_EVENT_REMOVE)) {
 		error = XFS_SEND_NAMESP(mp, DM_EVENT_REMOVE,
 					dp, DM_RIGHT_NULL,
@@ -3097,7 +3134,6 @@ xfs_symlink(
 	int			link_namelen;
 
 	*ipp = NULL;
-	error = 0;
 	ip = NULL;
 	tp = NULL;
 
@@ -3108,6 +3144,12 @@ xfs_symlink(
 
 	link_namelen = VNAMELEN(dentry);
 
+	if (xfs_sb_version_hasunicode(&mp->m_sb)) {
+		error = xfs_unicode_validate(link_name, link_namelen);
+		if (error)
+			return error;
+	}
+
 	/*
 	 * Check component lengths of the target path name.
 	 */

-- 

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

* [PATCH 6/7] XFS: Native Language Support for Unicode in XFS
  2008-04-02  6:25 [PATCH 0/7] XFS: case-insensitive lookup and Unicode support Barry Naujok
                   ` (4 preceding siblings ...)
  2008-04-02  6:25 ` [PATCH 5/7] XFS: Unicode case-insensitive lookup implementation Barry Naujok
@ 2008-04-02  6:25 ` Barry Naujok
  2008-04-04  0:05   ` David Chinner
  2008-04-02  6:25 ` [PATCH 7/7] XFS: NLS config option Barry Naujok
  2008-04-08 11:45 ` [PATCH 0/7] XFS: case-insensitive lookup and Unicode support Christoph Hellwig
  7 siblings, 1 reply; 48+ messages in thread
From: Barry Naujok @ 2008-04-02  6:25 UTC (permalink / raw)
  To: xfs; +Cc: linux-fsdevel

[-- Attachment #1: nls_support.patch --]
[-- Type: TEXT/PLAIN, Size: 37718 bytes --]

Implement the "-o nls=<charset>" mount option and required conversion 
of older style charater sets to/from UTF-8 to support non-UTF8 locales. 
This option is compatible with other Linux filesystems supporting
the "nls" mount option.

NLS conversion is performed on filename operations including readdir and
also user domain extended attribute names. The name zone defined in
the "return name" patch is used for temporarily holding the converted
name.

If Unicode is not configed Y, then the NLS support is virtually a no-op.

Signed-off-by: Barry Naujok <bnaujok@sgi.com>

---
 fs/xfs/linux-2.6/xfs_linux.h |    5 +
 fs/xfs/linux-2.6/xfs_super.c |   21 ++++++
 fs/xfs/xfs_attr.c            |   78 +++++++++++++++---------
 fs/xfs/xfs_attr.h            |    6 -
 fs/xfs/xfs_attr_leaf.c       |   74 ++++++++++++++++-------
 fs/xfs/xfs_clnt.h            |    1 
 fs/xfs/xfs_dir2_block.c      |   14 +++-
 fs/xfs/xfs_dir2_leaf.c       |   15 ++++
 fs/xfs/xfs_dir2_sf.c         |   12 +++
 fs/xfs/xfs_mount.h           |    2 
 fs/xfs/xfs_rename.c          |   12 +++
 fs/xfs/xfs_unicode.c         |  137 +++++++++++++++++++++++++++++++++++++++++++
 fs/xfs/xfs_unicode.h         |   16 +++++
 fs/xfs/xfs_vfsops.c          |   21 ++++++
 fs/xfs/xfs_vnodeops.c        |  117 +++++++++++++++++++++++++-----------
 15 files changed, 429 insertions(+), 102 deletions(-)

Index: kern_ci/fs/xfs/linux-2.6/xfs_linux.h
===================================================================
--- kern_ci.orig/fs/xfs/linux-2.6/xfs_linux.h
+++ kern_ci/fs/xfs/linux-2.6/xfs_linux.h
@@ -181,6 +181,11 @@
 #define howmany(x, y)	(((x)+((y)-1))/(y))
 
 /*
+ * NLS UTF-8 (unicode) 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)
Index: kern_ci/fs/xfs/linux-2.6/xfs_super.c
===================================================================
--- kern_ci.orig/fs/xfs/linux-2.6/xfs_super.c
+++ kern_ci/fs/xfs/linux-2.6/xfs_super.c
@@ -126,6 +126,7 @@ xfs_args_allocate(
 #define MNTOPT_NOATTR2	"noattr2"	/* do not use attr2 attribute format */
 #define MNTOPT_FILESTREAM  "filestreams" /* use filestreams allocator */
 #define MNTOPT_CILOOKUP	"ci"		/* case-insensitive dir lookup */
+#define MNTOPT_NLS	"nls"		/* NLS code page to use */
 #define MNTOPT_QUOTA	"quota"		/* disk quotas (user) */
 #define MNTOPT_NOQUOTA	"noquota"	/* no quotas */
 #define MNTOPT_USRQUOTA	"usrquota"	/* user quota enabled */
@@ -320,9 +321,20 @@ xfs_parseargs(
 			args->flags &= ~XFSMNT_ATTR2;
 		} else if (!strcmp(this_char, MNTOPT_FILESTREAM)) {
 			args->flags2 |= XFSMNT2_FILESTREAMS;
+#ifdef CONFIG_XFS_UNICODE
 		} else if (!strcmp(this_char, MNTOPT_CILOOKUP)) {
 			args->flags2 |= XFSMNT2_CILOOKUP;
-#ifndef CONFIG_XFS_UNICODE
+		} 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
+		} else if (!strcmp(this_char, MNTOPT_CILOOKUP) ||
+			   !strcmp(this_char, MNTOPT_NLS)) {
 			cmn_err(CE_WARN,
 				"XFS: %s option requires Unicode support",
 				this_char);
@@ -530,6 +542,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
Index: kern_ci/fs/xfs/xfs_attr.c
===================================================================
--- kern_ci.orig/fs/xfs/xfs_attr.c
+++ kern_ci/fs/xfs/xfs_attr.c
@@ -108,7 +108,7 @@ ktrace_t *xfs_attr_trace_buf;
  *========================================================================*/
 
 int
-xfs_attr_fetch(xfs_inode_t *ip, const char *name, int namelen,
+xfs_attr_fetch(xfs_inode_t *ip, const uchar_t *name, int namelen,
 	       char *value, int *valuelenp, int flags, struct cred *cred)
 {
 	xfs_da_args_t   args;
@@ -167,6 +167,7 @@ xfs_attr_get(
 	cred_t		*cred)
 {
 	int		error, namelen;
+	const uchar_t	*uni_name;
 
 	XFS_STATS_INC(xs_attr_get);
 
@@ -176,24 +177,29 @@ xfs_attr_get(
 	if (namelen >= MAXNAMELEN)
 		return(EFAULT);		/* match IRIX behaviour */
 
+	if (XFS_FORCED_SHUTDOWN(ip->i_mount))
+		return(EIO);
+
 	/* Enforce UTF-8 only for user attr names */
 	if (xfs_sb_version_hasunicode(&ip->i_mount->m_sb) &&
 			(flags & (ATTR_ROOT | ATTR_SECURE)) == 0) {
-		error = xfs_unicode_validate(name, namelen);
+		error = xfs_nls_to_unicode(ip->i_mount, name, namelen,
+				&uni_name, &namelen);
 		if (error)
 			return error;
-	}
-	if (XFS_FORCED_SHUTDOWN(ip->i_mount))
-		return(EIO);
+	} else
+		uni_name = name;
 
 	xfs_ilock(ip, XFS_ILOCK_SHARED);
-	error = xfs_attr_fetch(ip, name, namelen, value, valuelenp, flags, cred);
+	error = xfs_attr_fetch(ip, uni_name, namelen, value, valuelenp,
+				flags, cred);
 	xfs_iunlock(ip, XFS_ILOCK_SHARED);
+	xfs_unicode_nls_free(name, uni_name);
 	return(error);
 }
 
 int
-xfs_attr_set_int(xfs_inode_t *dp, const char *name, int namelen,
+xfs_attr_set_int(xfs_inode_t *dp, const uchar_t *name, int namelen,
 		 char *value, int valuelen, int flags)
 {
 	xfs_da_args_t	args;
@@ -437,26 +443,31 @@ xfs_attr_set(
 	int		valuelen,
 	int		flags)
 {
-	int             namelen;
+	int             error, namelen;
+	const uchar_t	*uni_name;
 
 	namelen = strlen(name);
 	if (namelen >= MAXNAMELEN)
 		return EFAULT;		/* match IRIX behaviour */
 
+	XFS_STATS_INC(xs_attr_set);
+
+	if (XFS_FORCED_SHUTDOWN(dp->i_mount))
+		return (EIO);
+
 	/* Enforce UTF-8 only for user attr names */
 	if (xfs_sb_version_hasunicode(&dp->i_mount->m_sb) &&
 			(flags & (ATTR_ROOT | ATTR_SECURE)) == 0) {
-		int error = xfs_unicode_validate(name, namelen);
+		error = xfs_nls_to_unicode(dp->i_mount, name, namelen,
+				&uni_name, &namelen);
 		if (error)
 			return error;
-	}
-
-	XFS_STATS_INC(xs_attr_set);
-
-	if (XFS_FORCED_SHUTDOWN(dp->i_mount))
-		return (EIO);
+	} else
+		uni_name = name;
 
-	return xfs_attr_set_int(dp, name, namelen, value, valuelen, flags);
+	error = xfs_attr_set_int(dp, uni_name, namelen, value, valuelen, flags);
+	xfs_unicode_nls_free(name, uni_name);
+	return error;
 }
 
 /*
@@ -464,7 +475,8 @@ xfs_attr_set(
  * Transitions attribute list from Btree to shortform as necessary.
  */
 int
-xfs_attr_remove_int(xfs_inode_t *dp, const char *name, int namelen, int flags)
+xfs_attr_remove_int(xfs_inode_t *dp, const uchar_t *name, int namelen,
+		    int flags)
 {
 	xfs_da_args_t	args;
 	xfs_fsblock_t	firstblock;
@@ -591,35 +603,41 @@ xfs_attr_remove(
 	const char	*name,
 	int		flags)
 {
-	int		namelen;
+	int		error, namelen;
+	const uchar_t	*uni_name;
 
 	namelen = strlen(name);
 	if (namelen >= MAXNAMELEN)
 		return EFAULT;		/* match IRIX behaviour */
 
+	XFS_STATS_INC(xs_attr_remove);
+
+	if (XFS_FORCED_SHUTDOWN(dp->i_mount))
+		return (EIO);
+
 	/* Enforce UTF-8 only for user attr names */
 	if (xfs_sb_version_hasunicode(&dp->i_mount->m_sb) &&
 			(flags & (ATTR_ROOT | ATTR_SECURE)) == 0) {
-		int error = xfs_unicode_validate(name, namelen);
+		error = xfs_nls_to_unicode(dp->i_mount, name, namelen,
+				&uni_name, &namelen);
 		if (error)
 			return error;
-	}
-
-	XFS_STATS_INC(xs_attr_remove);
-
-	if (XFS_FORCED_SHUTDOWN(dp->i_mount))
-		return (EIO);
+	} else
+		uni_name = name;
 
 	xfs_ilock(dp, XFS_ILOCK_SHARED);
 	if (XFS_IFORK_Q(dp) == 0 ||
 		   (dp->i_d.di_aformat == XFS_DINODE_FMT_EXTENTS &&
 		    dp->i_d.di_anextents == 0)) {
 		xfs_iunlock(dp, XFS_ILOCK_SHARED);
+		xfs_unicode_nls_free(name, uni_name);
 		return(XFS_ERROR(ENOATTR));
 	}
 	xfs_iunlock(dp, XFS_ILOCK_SHARED);
 
-	return xfs_attr_remove_int(dp, name, namelen, flags);
+	error = xfs_attr_remove_int(dp, uni_name, namelen, flags);
+	xfs_unicode_nls_free(name, uni_name);
+	return error;
 }
 
 int								/* error */
@@ -658,9 +676,9 @@ xfs_attr_list_int(xfs_attr_list_context_
  */
 /*ARGSUSED*/
 STATIC int
-xfs_attr_put_listent(xfs_attr_list_context_t *context, attrnames_t *namesp,
-		     char *name, int namelen,
-		     int valuelen, char *value)
+xfs_attr_user_list(xfs_attr_list_context_t *context, attrnames_t *namesp,
+		   char *name, int namelen,
+		   int valuelen, char *value)
 {
 	attrlist_ent_t *aep;
 	int arraytop;
@@ -789,7 +807,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))
Index: kern_ci/fs/xfs/xfs_attr.h
===================================================================
--- kern_ci.orig/fs/xfs/xfs_attr.h
+++ kern_ci/fs/xfs/xfs_attr.h
@@ -158,13 +158,13 @@ struct xfs_da_args;
 /*
  * Overall external interface routines.
  */
-int xfs_attr_set_int(struct xfs_inode *, const char *, int, char *, int, int);
-int xfs_attr_remove_int(struct xfs_inode *, const char *, int, int);
+int xfs_attr_set_int(struct xfs_inode *, const uchar_t *, int, char *, int, int);
+int xfs_attr_remove_int(struct xfs_inode *, const uchar_t *, int, int);
 int xfs_attr_list_int(struct xfs_attr_list_context *);
 int xfs_attr_inactive(struct xfs_inode *dp);
 
 int xfs_attr_shortform_getvalue(struct xfs_da_args *);
-int xfs_attr_fetch(struct xfs_inode *, const char *, int,
+int xfs_attr_fetch(struct xfs_inode *, const uchar_t *, int,
 			char *, int *, int, struct cred *);
 int xfs_attr_rmtval_get(struct xfs_da_args *args);
 
Index: kern_ci/fs/xfs/xfs_attr_leaf.c
===================================================================
--- kern_ci.orig/fs/xfs/xfs_attr_leaf.c
+++ kern_ci/fs/xfs/xfs_attr_leaf.c
@@ -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
@@ -89,6 +90,9 @@ STATIC void xfs_attr_leaf_moveents(xfs_a
 					 int dst_start, int move_count,
 					 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
@@ -150,7 +154,7 @@ xfs_attr_shortform_bytesfit(xfs_inode_t 
 	int offset;
 	int minforkoff;	/* lower limit on valid forkoff locations */
 	int maxforkoff;	/* upper limit on valid forkoff locations */
-	int dsize;	
+	int dsize;
 	xfs_mount_t *mp = dp->i_mount;
 
 	offset = (XFS_LITINO(mp) - bytes) >> 3; /* rounded down */
@@ -171,39 +175,39 @@ xfs_attr_shortform_bytesfit(xfs_inode_t 
 	}
 
 	dsize = dp->i_df.if_bytes;
-	
+
 	switch (dp->i_d.di_format) {
 	case XFS_DINODE_FMT_EXTENTS:
-		/* 
-		 * If there is no attr fork and the data fork is extents, 
-		 * determine if creating the default attr fork will result 
-		 * in the extents form migrating to btree. If so, the 
-		 * minimum offset only needs to be the space required for 
+		/*
+		 * If there is no attr fork and the data fork is extents,
+		 * determine if creating the default attr fork will result
+		 * in the extents form migrating to btree. If so, the
+		 * minimum offset only needs to be the space required for
 		 * the btree root.
-		 */ 
+		 */
 		if (!dp->i_d.di_forkoff && dp->i_df.if_bytes > mp->m_attroffset)
 			dsize = XFS_BMDR_SPACE_CALC(MINDBTPTRS);
 		break;
-		
+
 	case XFS_DINODE_FMT_BTREE:
 		/*
 		 * If have data btree then keep forkoff if we have one,
-		 * otherwise we are adding a new attr, so then we set 
-		 * minforkoff to where the btree root can finish so we have 
+		 * otherwise we are adding a new attr, so then we set
+		 * minforkoff to where the btree root can finish so we have
 		 * plenty of room for attrs
 		 */
 		if (dp->i_d.di_forkoff) {
-			if (offset < dp->i_d.di_forkoff) 
+			if (offset < dp->i_d.di_forkoff)
 				return 0;
-			else 
+			else
 				return dp->i_d.di_forkoff;
 		} else
 			dsize = XFS_BMAP_BROOT_SPACE(dp->i_df.if_broot);
 		break;
 	}
-	
-	/* 
-	 * A data fork btree root must have space for at least 
+
+	/*
+	 * A data fork btree root must have space for at least
 	 * MINDBTPTRS key/ptr pairs if the data fork is small or empty.
 	 */
 	minforkoff = MAX(dsize, XFS_BMDR_SPACE_CALC(MINDBTPTRS));
@@ -370,7 +374,7 @@ xfs_attr_shortform_remove(xfs_da_args_t 
 	 */
 	totsize -= size;
 	if (totsize == sizeof(xfs_attr_sf_hdr_t) && !args->addname &&
-	    (mp->m_flags & XFS_MOUNT_ATTR2) && 
+	    (mp->m_flags & XFS_MOUNT_ATTR2) &&
 	    (dp->i_d.di_format != XFS_DINODE_FMT_BTREE)) {
 		/*
 		 * Last attribute now removed, revert to original
@@ -631,7 +635,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 +738,7 @@ xfs_attr_shortform_list(xfs_attr_list_co
 			cursor->hashval = sbp->hash;
 			cursor->offset = 0;
 		}
-		error = context->put_listent(context,
+		error = á(context,
 					namesp,
 					sbp->name,
 					sbp->namelen,
@@ -2418,7 +2422,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 +2449,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 +2458,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 +2476,32 @@ xfs_attr_leaf_list_int(xfs_dabuf_t *bp, 
 	return(retval);
 }
 
+/*
+ * Do NLS name conversion if required for user attribute names 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)
+{
+	char *nls_name;
+	int nls_namelen;
+	int error;
+
+	if (xfs_is_using_nls(context->dp->i_mount) && namesp == attr_user) {
+		error = xfs_unicode_to_nls(context->dp->i_mount, name, namelen,
+				&nls_name, &nls_namelen);
+		if (error)
+			return error;
+		error = context->put_listent(context, namesp, nls_name,
+				nls_namelen, valuelen, value);
+		xfs_unicode_nls_free(name, nls_name);
+		return error;
+	} else
+		return context->put_listent(context, namesp, name, namelen,
+				valuelen, value);
+}
 
 /*========================================================================
  * Manage the INCOMPLETE flag in a leaf entry
Index: kern_ci/fs/xfs/xfs_clnt.h
===================================================================
--- kern_ci.orig/fs/xfs/xfs_clnt.h
+++ kern_ci/fs/xfs/xfs_clnt.h
@@ -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 character set 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 */
Index: kern_ci/fs/xfs/xfs_dir2_block.c
===================================================================
--- kern_ci.orig/fs/xfs/xfs_dir2_block.c
+++ kern_ci/fs/xfs/xfs_dir2_block.c
@@ -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;
+	const uchar_t		*nls_name;
+	int			nls_namelen;
 
 	mp = dp->i_mount;
 	/*
@@ -513,16 +516,21 @@ xfs_dir2_block_getdents(
 #if XFS_BIG_INUMS
 		ino += mp->m_inoadd;
 #endif
-
+		error = xfs_unicode_to_nls(mp, dep->name, dep->namelen,
+				&nls_name, &nls_namelen);
+		if (error)
+			break;
 		/*
 		 * If it didn't fit, set the final offset to here & return.
 		 */
-		if (filldir(dirent, dep->name, dep->namelen, cook,
+		if (filldir(dirent, nls_name, nls_namelen, cook,
 			    ino, DT_UNKNOWN)) {
 			*offset = cook;
+			xfs_unicode_nls_free(dep->name, nls_name);
 			xfs_da_brelse(NULL, bp);
 			return 0;
 		}
+		xfs_unicode_nls_free(dep->name, nls_name);
 	}
 
 	/*
@@ -531,7 +539,7 @@ xfs_dir2_block_getdents(
 	 */
 	*offset = xfs_dir2_db_off_to_dataptr(mp, mp->m_dirdatablk + 1, 0);
 	xfs_da_brelse(NULL, bp);
-	return 0;
+	return error;
 }
 
 /*
Index: kern_ci/fs/xfs/xfs_dir2_leaf.c
===================================================================
--- kern_ci.orig/fs/xfs/xfs_dir2_leaf.c
+++ kern_ci/fs/xfs/xfs_dir2_leaf.c
@@ -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;
+	const uchar_t		*nls_name;	/* NLS name buffer */
+	int			nls_namelen;
 
 	/*
 	 * If the offset is at or past the largest allowed value,
@@ -1087,13 +1090,21 @@ xfs_dir2_leaf_getdents(
 		ino += mp->m_inoadd;
 #endif
 
+		error = xfs_unicode_to_nls(mp, dep->name, dep->namelen,
+				&nls_name, &nls_namelen);
+		if (error)
+			break;
+
 		/*
 		 * Won't fit.  Return to caller.
 		 */
-		if (filldir(dirent, dep->name, dep->namelen,
+		if (filldir(dirent, nls_name, nls_namelen,
 			    xfs_dir2_byte_to_dataptr(mp, curoff),
-			    ino, DT_UNKNOWN))
+			    ino, DT_UNKNOWN)) {
+			xfs_unicode_nls_free(dep->name, nls_name);
 			break;
+		}
+		xfs_unicode_nls_free(dep->name, nls_name);
 
 		/*
 		 * Advance to next entry in the block.
Index: kern_ci/fs/xfs/xfs_dir2_sf.c
===================================================================
--- kern_ci.orig/fs/xfs/xfs_dir2_sf.c
+++ kern_ci/fs/xfs/xfs_dir2_sf.c
@@ -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.
@@ -700,6 +701,7 @@ xfs_dir2_sf_getdents(
 	xfs_off_t		*offset,
 	filldir_t		filldir)
 {
+	int			error;
 	int			i;		/* shortform entry number */
 	xfs_mount_t		*mp;		/* filesystem mount point */
 	xfs_dir2_dataptr_t	off;		/* current entry's offset */
@@ -708,6 +710,8 @@ xfs_dir2_sf_getdents(
 	xfs_dir2_dataptr_t	dot_offset;
 	xfs_dir2_dataptr_t	dotdot_offset;
 	xfs_ino_t		ino;
+	const uchar_t		*nls_name;	/* NLS name buffer */
+	int			nls_namelen;
 
 	mp = dp->i_mount;
 
@@ -789,12 +793,18 @@ xfs_dir2_sf_getdents(
 #if XFS_BIG_INUMS
 		ino += mp->m_inoadd;
 #endif
+		error = xfs_unicode_to_nls(mp, sfep->name, sfep->namelen,
+				&nls_name, &nls_namelen);
+		if (error)
+			return error;
 
-		if (filldir(dirent, sfep->name, sfep->namelen,
+		if (filldir(dirent, nls_name, nls_namelen,
 					    off, ino, DT_UNKNOWN)) {
 			*offset = off;
+			xfs_unicode_nls_free(sfep->name, nls_name);
 			return 0;
 		}
+		xfs_unicode_nls_free(sfep->name, nls_name);
 		sfep = xfs_dir2_sf_nextentry(sfp, sfep);
 	}
 
Index: kern_ci/fs/xfs/xfs_mount.h
===================================================================
--- kern_ci.orig/fs/xfs/xfs_mount.h
+++ kern_ci/fs/xfs/xfs_mount.h
@@ -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;
@@ -316,6 +317,7 @@ typedef struct xfs_mount {
 	__uint8_t		m_sectbb_log;	/* sectlog - BBSHIFT */
 	struct xfs_nameops	*m_dirnameops;	/* vector of dir name ops */
 	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 */
Index: kern_ci/fs/xfs/xfs_rename.c
===================================================================
--- kern_ci.orig/fs/xfs/xfs_rename.c
+++ kern_ci/fs/xfs/xfs_rename.c
@@ -250,10 +250,14 @@ xfs_rename(
 	xfs_itrace_entry(target_dp);
 
 	if (xfs_sb_version_hasunicode(&mp->m_sb)) {
-		error = xfs_unicode_validate(src_name, src_namelen);
+		error = xfs_nls_to_unicode(mp,
+				VNAME(src_vname), VNAMELEN(src_vname),
+				(const uchar_t **)&src_name, &src_namelen);
 		if (error)
 			return error;
-		error = xfs_unicode_validate(target_name, target_namelen);
+		error = xfs_nls_to_unicode(mp,
+				VNAME(target_vname), VNAMELEN(target_vname),
+				(const uchar_t **)&target_name, &target_namelen);
 		if (error)
 			return error;
 	}
@@ -265,6 +269,8 @@ xfs_rename(
 					src_name, target_name,
 					0, 0, 0);
 		if (error) {
+			xfs_unicode_nls_free(VNAME(src_vname), src_name);
+			xfs_unicode_nls_free(VNAME(target_vname), target_name);
 			return error;
 		}
 	}
@@ -598,6 +604,8 @@ std_return:
 					src_name, target_name,
 					0, error, 0);
 	}
+	xfs_unicode_nls_free(VNAME(src_vname), src_name);
+	xfs_unicode_nls_free(VNAME(target_vname), target_name);
 	return error;
 
  abort_return:
Index: kern_ci/fs/xfs/xfs_unicode.c
===================================================================
--- kern_ci.orig/fs/xfs/xfs_unicode.c
+++ kern_ci/fs/xfs/xfs_unicode.c
@@ -497,3 +497,140 @@ xfs_unicode_uninit(void)
 	mutex_unlock(&cft_lock);
 	mutex_destroy(&cft_lock);
 }
+
+/*
+ * Convert UTF-8 (Unicode) string into the specified character set in "nls".
+ * If no NLS conversion is required (mp->m_nls = NULL), the pointers are
+ * return as is. Otherwise, a new buffer is allocated and returned.
+ * xfs_unicode_nls_free() must be called with the source uni_name and returned
+ * nls_name so it can free the buffer if required.
+ */
+int
+xfs_unicode_to_nls(
+	xfs_mount_t	*mp,
+	const uchar_t	*uni_name,
+	int		uni_namelen,
+	const uchar_t	**nls_name,
+	int		*nls_namelen)
+{
+	char		*n;
+	int		i, o;
+	wchar_t		uc;
+	int		nlen;
+	int		u8len;
+	int		error;
+
+	if (!xfs_is_using_nls(mp)) {
+		*nls_name = uni_name;
+		*nls_namelen = uni_namelen;
+		return 0;
+	}
+
+	n = xfs_da_name_alloc();
+	if (!n)
+		return ENOMEM;
+
+	error = 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 = mp->m_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:
+	xfs_da_name_free(n);
+	return error;
+}
+
+/*
+ * Convert the "nls" specified charset string into UTF-8 (Unicode).
+ * If no NLS conversion is required (mp->m_nls = NULL), the pointers are
+ * return as is. Otherwise, a new buffer is allocated and returned.
+ * xfs_unicode_nls_free() must be called with the source uni_name and returned
+ * nls_name so it can free the buffer if required.
+ *
+ * As this is used for all strings coming in from outside XFS, if NLS
+ * conversion is not used, validate the string as properly formed UTF-8.
+ */
+int
+xfs_nls_to_unicode(
+	xfs_mount_t	*mp,
+	const uchar_t	*nls_name,
+	int		nls_namelen,
+	const uchar_t	**uni_name,
+	int		*uni_namelen)
+{
+	char		*n;
+	int		i, o;
+	wchar_t		uc;
+	int		nlen;
+	int		u8len;
+	int		error;
+
+	if (!xfs_is_using_nls(mp)) {
+		error = xfs_unicode_validate(nls_name, nls_namelen);
+		if (error)
+			return error;
+		*uni_name = nls_name;
+		*uni_namelen = nls_namelen;
+		return 0;
+	}
+
+	n = xfs_da_name_alloc();
+	if (!n)
+		return ENOMEM;
+
+	error = 0;
+	for (i = 0, o = 0; i < nls_namelen; i += nlen, o += u8len) {
+		nlen = mp->m_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:
+	xfs_da_name_free(n);
+	return error;
+
+}
+
+/*
+ * free the buffer that MAY have been allocated by xfs_unicode_to_nls()
+ * or xfs_nls_to_unicode().
+ */
+void
+xfs_unicode_nls_free(
+	const uchar_t		*src_name,
+	const uchar_t		*conv_name)
+{
+	if (src_name != conv_name)
+		xfs_da_name_free((uchar_t *)conv_name);
+}
Index: kern_ci/fs/xfs/xfs_unicode.h
===================================================================
--- kern_ci.orig/fs/xfs/xfs_unicode.h
+++ kern_ci/fs/xfs/xfs_unicode.h
@@ -65,6 +65,14 @@ int xfs_unicode_validate(const uchar_t *
 int xfs_unicode_read_cft(struct xfs_mount *mp);
 void xfs_unicode_free_cft(const xfs_cft_t *cft);
 
+#define xfs_is_using_nls(mp)	((mp)->m_nls != NULL)
+
+int xfs_unicode_to_nls(struct xfs_mount *mp, const uchar_t *uni_name,
+		int uni_namelen, const uchar_t **nls_name, int *nls_namelen);
+int xfs_nls_to_unicode(struct xfs_mount *mp, const uchar_t *nls_name,
+		int nls_namelen, const uchar_t **uni_name, int *uni_namelen);
+void xfs_unicode_nls_free(const uchar_t *src_name, const uchar_t *conv_name);
+
 #else
 
 #define xfs_unicode_nameops		xfs_default_nameops
@@ -76,6 +84,14 @@ void xfs_unicode_free_cft(const xfs_cft_
 #define xfs_unicode_read_cft(mp)	(EOPNOTSUPP)
 #define xfs_unicode_free_cft(cft)
 
+#define xfs_is_using_nls(mp)		0
+
+#define xfs_unicode_to_nls(mp, uname, ulen, pnname, pnlen) \
+		((*(pnname)) = (uname), (*(pnlen)) = (ulen), 0)
+#define xfs_nls_to_unicode(mp, nname, nlen, puname, pulen) \
+		((*(puname)) = (nname), (*(pulen)) = (nlen), 0)
+#define xfs_unicode_nls_free(sname, cname)
+
 #endif	/* CONFIG_XFS_UNICODE */
 
 #endif /* __XFS_UNICODE_H__ */
Index: kern_ci/fs/xfs/xfs_vfsops.c
===================================================================
--- kern_ci.orig/fs/xfs/xfs_vfsops.c
+++ kern_ci/fs/xfs/xfs_vfsops.c
@@ -405,13 +405,30 @@ xfs_finish_flags(
 	if (xfs_sb_version_hasunicode(&mp->m_sb)) {
 		if (ap->flags2 & XFSMNT2_CILOOKUP)
 			mp->m_flags |= XFS_MOUNT_CILOOKUP;
+
+		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) {
 			cmn_err(CE_WARN,
-	"XFS: can't do case-insensitive mount on non-utf8 filesystem");
+	"XFS: can't do case-insensitive mount on non-Unicode filesystem");
+			return XFS_ERROR(EINVAL);
+		}
+		if (ap->nls[0]) {
+			cmn_err(CE_WARN,
+	"XFS: can't use nls mount option on non-Unicode filesystem");
 			return XFS_ERROR(EINVAL);
 		}
 	}
@@ -647,6 +664,8 @@ out:
 		xfs_unmountfs(mp, credp);
 		xfs_qmops_put(mp);
 		xfs_dmops_put(mp);
+		if (xfs_is_using_nls(mp))
+			unload_nls(mp->m_nls);
 		kmem_free(mp, sizeof(xfs_mount_t));
 	}
 
Index: kern_ci/fs/xfs/xfs_vnodeops.c
===================================================================
--- kern_ci.orig/fs/xfs/xfs_vnodeops.c
+++ kern_ci/fs/xfs/xfs_vnodeops.c
@@ -1779,13 +1779,14 @@ xfs_lookup(
 		return XFS_ERROR(EIO);
 
 	if (xfs_sb_version_hasunicode(&dp->i_mount->m_sb)) {
-		error = xfs_unicode_validate(d_name->name, d_name->len);
+		error = xfs_nls_to_unicode(dp->i_mount, d_name->name,
+				d_name->len, &name.name, &name.len);
 		if (error)
 			return error;
+	} else {
+		name.name = d_name->name;
+		name.len = d_name->len;
 	}
-
-	name.name = (uchar_t *)d_name->name;
-	name.len = d_name->len;
 	rname.name = NULL;
 	lock_mode = xfs_ilock_map_shared(dp);
 	error = xfs_dir_lookup_int(dp, lock_mode, &name, &e_inum, &ip, &rname);
@@ -1793,11 +1794,15 @@ xfs_lookup(
 		*ipp = ip;
 		xfs_itrace_ref(ip);
 		if (rname.name) {
-			ci_name->name = rname.name;
-			ci_name->len = rname.len;
+			error = xfs_unicode_to_nls(dp->i_mount,
+					rname.name, rname.len,
+					&ci_name->name, &ci_name->len);
+			/* free rname.name if conversion occurred or error */
+			xfs_unicode_nls_free(ci_name->name, rname.name);
 		}
 	}
 	xfs_iunlock_map_shared(dp, lock_mode);
+	xfs_unicode_nls_free(d_name->name, name.name);
 	return error;
 }
 
@@ -1810,7 +1815,7 @@ xfs_create(
 	xfs_inode_t		**ipp,
 	cred_t			*credp)
 {
-	char			*name = VNAME(dentry);
+	char			*name;
 	xfs_mount_t	        *mp = dp->i_mount;
 	xfs_inode_t		*ip;
 	xfs_trans_t		*tp;
@@ -1832,12 +1837,14 @@ xfs_create(
 	if (XFS_FORCED_SHUTDOWN(mp))
 		return XFS_ERROR(EIO);
 
-	namelen = VNAMELEN(dentry);
-
 	if (xfs_sb_version_hasunicode(&mp->m_sb)) {
-		error = xfs_unicode_validate(name, namelen);
+		error = xfs_nls_to_unicode(mp, VNAME(dentry), VNAMELEN(dentry),
+				(const uchar_t **)&name, &namelen);
 		if (error)
 			return error;
+	} else {
+		name = VNAME(dentry);
+		namelen = VNAMELEN(dentry);
 	}
 
 	if (DM_EVENT_ENABLED(dp, DM_EVENT_CREATE)) {
@@ -1846,8 +1853,10 @@ xfs_create(
 				DM_RIGHT_NULL, name, NULL,
 				mode, 0, 0);
 
-		if (error)
+		if (error) {
+			xfs_unicode_nls_free(VNAME(dentry), name);
 			return error;
+		}
 		dm_event_sent = 1;
 	}
 
@@ -1999,6 +2008,7 @@ std_return:
 			DM_RIGHT_NULL, name, NULL,
 			mode, error, 0);
 	}
+	xfs_unicode_nls_free(VNAME(dentry), name);
 	return error;
 
  abort_return:
@@ -2290,10 +2300,10 @@ xfs_remove(
 	xfs_inode_t             *dp,
 	bhv_vname_t		*dentry)
 {
-	char			*name = VNAME(dentry);
+	char			*name;
 	xfs_mount_t		*mp = dp->i_mount;
 	xfs_inode_t             *ip = VNAME_TO_INODE(dentry);
-	int			namelen = VNAMELEN(dentry);
+	int			namelen;
 	xfs_trans_t             *tp = NULL;
 	int                     error;
 	xfs_bmap_free_t         free_list;
@@ -2309,17 +2319,23 @@ xfs_remove(
 		return XFS_ERROR(EIO);
 
 	if (xfs_sb_version_hasunicode(&mp->m_sb)) {
-		error = xfs_unicode_validate(name, namelen);
+		error = xfs_nls_to_unicode(mp, VNAME(dentry), VNAMELEN(dentry),
+				(const uchar_t **)&name, &namelen);
 		if (error)
 			return error;
+	} else {
+		name = VNAME(dentry);
+		namelen = VNAMELEN(dentry);
 	}
 
 	if (DM_EVENT_ENABLED(dp, DM_EVENT_REMOVE)) {
 		error = XFS_SEND_NAMESP(mp, DM_EVENT_REMOVE, dp,
 					DM_RIGHT_NULL, NULL, DM_RIGHT_NULL,
 					name, NULL, ip->i_d.di_mode, 0, 0);
-		if (error)
+		if (error) {
+			xfs_unicode_nls_free(VNAME(dentry), name);
 			return error;
+		}
 	}
 
 	/*
@@ -2472,6 +2488,7 @@ xfs_remove(
 				NULL, DM_RIGHT_NULL,
 				name, NULL, ip->i_d.di_mode, error, 0);
 	}
+	xfs_unicode_nls_free(VNAME(dentry), name);
 	return error;
 
  error1:
@@ -2511,22 +2528,26 @@ xfs_link(
 	int			cancel_flags;
 	int			committed;
 	int			resblks;
-	char			*target_name = VNAME(dentry);
+	char			*target_name;
 	int			target_namelen;
 
 	xfs_itrace_entry(tdp);
 	xfs_itrace_entry(sip);
 
-	target_namelen = VNAMELEN(dentry);
 	ASSERT(!S_ISDIR(sip->i_d.di_mode));
 
 	if (XFS_FORCED_SHUTDOWN(mp))
 		return XFS_ERROR(EIO);
 
 	if (xfs_sb_version_hasunicode(&mp->m_sb)) {
-		error = xfs_unicode_validate(target_name, target_namelen);
+		error = xfs_nls_to_unicode(mp, VNAME(dentry), VNAMELEN(dentry),
+					(const uchar_t **)&target_name,
+					&target_namelen);
 		if (error)
 			return error;
+	} else {
+		target_name = VNAME(dentry);
+		target_namelen = VNAMELEN(dentry);
 	}
 
 	if (DM_EVENT_ENABLED(tdp, DM_EVENT_LINK)) {
@@ -2534,8 +2555,10 @@ xfs_link(
 					tdp, DM_RIGHT_NULL,
 					sip, DM_RIGHT_NULL,
 					target_name, NULL, 0, 0, 0);
-		if (error)
+		if (error) {
+			xfs_unicode_nls_free(VNAME(dentry), target_name);
 			return error;
+		}
 	}
 
 	/* Return through std_return after this point. */
@@ -2646,6 +2669,7 @@ std_return:
 				sip, DM_RIGHT_NULL,
 				target_name, NULL, 0, error, 0);
 	}
+	xfs_unicode_nls_free(VNAME(dentry), target_name);
 	return error;
 
  abort_return:
@@ -2666,8 +2690,8 @@ xfs_mkdir(
 	xfs_inode_t		**ipp,
 	cred_t			*credp)
 {
-	char			*dir_name = VNAME(dentry);
-	int			dir_namelen = VNAMELEN(dentry);
+	char			*dir_name;
+	int			dir_namelen;
 	xfs_mount_t		*mp = dp->i_mount;
 	xfs_inode_t		*cdp;	/* inode of created dir */
 	xfs_trans_t		*tp;
@@ -2687,9 +2711,13 @@ xfs_mkdir(
 		return XFS_ERROR(EIO);
 
 	if (xfs_sb_version_hasunicode(&mp->m_sb)) {
-		error = xfs_unicode_validate(dir_name, dir_namelen);
+		error = xfs_nls_to_unicode(mp, VNAME(dentry), VNAMELEN(dentry),
+				(const uchar_t **)&dir_name, &dir_namelen);
 		if (error)
 			return error;
+	} else {
+		dir_name = VNAME(dentry);
+		dir_namelen = VNAMELEN(dentry);
 	}
 
 	tp = NULL;
@@ -2699,8 +2727,10 @@ xfs_mkdir(
 					dp, DM_RIGHT_NULL, NULL,
 					DM_RIGHT_NULL, dir_name, NULL,
 					mode, 0, 0);
-		if (error)
+		if (error) {
+			xfs_unicode_nls_free(VNAME(dentry), dir_name);
 			return error;
+		}
 		dm_event_sent = 1;
 	}
 
@@ -2858,6 +2888,7 @@ std_return:
 					dir_name, NULL,
 					mode, error, 0);
 	}
+	xfs_unicode_nls_free(VNAME(dentry), dir_name);
 	return error;
 
  error2:
@@ -2882,8 +2913,8 @@ xfs_rmdir(
 	bhv_vname_t		*dentry)
 {
 	bhv_vnode_t		*dir_vp = XFS_ITOV(dp);
-	char			*name = VNAME(dentry);
-	int			namelen = VNAMELEN(dentry);
+	char			*name;
+	int			namelen;
 	xfs_mount_t		*mp = dp->i_mount;
   	xfs_inode_t             *cdp = VNAME_TO_INODE(dentry);
 	xfs_trans_t             *tp;
@@ -2901,9 +2932,13 @@ xfs_rmdir(
 		return XFS_ERROR(EIO);
 
 	if (xfs_sb_version_hasunicode(&mp->m_sb)) {
-		error = xfs_unicode_validate(name, namelen);
+		error = xfs_nls_to_unicode(mp, VNAME(dentry), VNAMELEN(dentry),
+				(const uchar_t **)&name, &namelen);
 		if (error)
 			return error;
+	} else {
+		name = VNAME(dentry);
+		namelen = VNAMELEN(dentry);
 	}
 
 	if (DM_EVENT_ENABLED(dp, DM_EVENT_REMOVE)) {
@@ -2911,8 +2946,10 @@ xfs_rmdir(
 					dp, DM_RIGHT_NULL,
 					NULL, DM_RIGHT_NULL,
 					name, NULL, cdp->i_d.di_mode, 0, 0);
-		if (error)
+		if (error) {
+			xfs_unicode_nls_free(VNAME(dentry), name);
 			return XFS_ERROR(error);
+		}
 	}
 
 	/*
@@ -3087,6 +3124,7 @@ xfs_rmdir(
 					name, NULL, cdp->i_d.di_mode,
 					error, 0);
 	}
+	xfs_unicode_nls_free(VNAME(dentry), name);
 	return error;
 
  error1:
@@ -3130,7 +3168,7 @@ xfs_symlink(
 	xfs_prid_t		prid;
 	struct xfs_dquot	*udqp, *gdqp;
 	uint			resblks;
-	char			*link_name = VNAME(dentry);
+	char			*link_name;
 	int			link_namelen;
 
 	*ipp = NULL;
@@ -3142,14 +3180,6 @@ xfs_symlink(
 	if (XFS_FORCED_SHUTDOWN(mp))
 		return XFS_ERROR(EIO);
 
-	link_namelen = VNAMELEN(dentry);
-
-	if (xfs_sb_version_hasunicode(&mp->m_sb)) {
-		error = xfs_unicode_validate(link_name, link_namelen);
-		if (error)
-			return error;
-	}
-
 	/*
 	 * Check component lengths of the target path name.
 	 */
@@ -3182,12 +3212,24 @@ xfs_symlink(
 		}
 	}
 
+	if (xfs_sb_version_hasunicode(&mp->m_sb)) {
+		error = xfs_nls_to_unicode(mp, VNAME(dentry), VNAMELEN(dentry),
+				(const uchar_t **)&link_name, &link_namelen);
+		if (error)
+			return error;
+	} else {
+		link_name = VNAME(dentry);
+		link_namelen = VNAMELEN(dentry);
+	}
+
 	if (DM_EVENT_ENABLED(dp, DM_EVENT_SYMLINK)) {
 		error = XFS_SEND_NAMESP(mp, DM_EVENT_SYMLINK, dp,
 					DM_RIGHT_NULL, NULL, DM_RIGHT_NULL,
 					link_name, target_path, 0, 0, 0);
-		if (error)
+		if (error) {
+			xfs_unicode_nls_free(VNAME(dentry), link_name);
 			return error;
+		}
 	}
 
 	/* Return through std_return after this point. */
@@ -3395,6 +3437,7 @@ std_return:
 
 	if (!error)
 		*ipp = ip;
+	xfs_unicode_nls_free(VNAME(dentry), link_name);
 	return error;
 
  error2:

-- 
--
To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* [PATCH 7/7] XFS: NLS config option
  2008-04-02  6:25 [PATCH 0/7] XFS: case-insensitive lookup and Unicode support Barry Naujok
                   ` (5 preceding siblings ...)
  2008-04-02  6:25 ` [PATCH 6/7] XFS: Native Language Support for Unicode in XFS Barry Naujok
@ 2008-04-02  6:25 ` Barry Naujok
  2008-04-03  1:26   ` Josef 'Jeff' Sipek
  2008-04-08 11:45 ` [PATCH 0/7] XFS: case-insensitive lookup and Unicode support Christoph Hellwig
  7 siblings, 1 reply; 48+ messages in thread
From: Barry Naujok @ 2008-04-02  6:25 UTC (permalink / raw)
  To: xfs; +Cc: linux-fsdevel

[-- Attachment #1: config_nls.patch --]
[-- Type: text/plain, Size: 2817 bytes --]

This optional patch implements the NLS support as a CONFIG option.

Signed-off-by: Barry Naujok <bnaujok@sgi.com>

---
 fs/xfs/Kconfig       |   10 ++++++++++
 fs/xfs/xfs_unicode.c |    4 ++++
 fs/xfs/xfs_unicode.h |   17 ++++++++++++++++-
 3 files changed, 30 insertions(+), 1 deletion(-)

Index: kern_ci/fs/xfs/Kconfig
===================================================================
--- kern_ci.orig/fs/xfs/Kconfig
+++ kern_ci/fs/xfs/Kconfig
@@ -87,6 +87,16 @@ config XFS_UNICODE
 
 	  If you don't require UTF-8 enforcement, say N.
 
+config XFS_UNICODE_NLS
+	bool "XFS NLS Unicode support
+	depends on XFS_UNICODE
+	help
+	  NLS (Native Language Support) allows non-UTF8 locales to
+	  interact with XFS Unicode support. To specify the character
+	  set being used, use the "-n nls=<charset>" mount option.
+
+	  If you don't require NLS conversion in XFS, say N.
+
 config XFS_RT
 	bool "XFS Realtime subvolume support"
 	depends on XFS_FS
Index: kern_ci/fs/xfs/xfs_unicode.c
===================================================================
--- kern_ci.orig/fs/xfs/xfs_unicode.c
+++ kern_ci/fs/xfs/xfs_unicode.c
@@ -498,6 +498,8 @@ xfs_unicode_uninit(void)
 	mutex_destroy(&cft_lock);
 }
 
+#ifdef CONFIG_XFS_UNICODE_NLS
+
 /*
  * Convert UTF-8 (Unicode) string into the specified character set in "nls".
  * If no NLS conversion is required (mp->m_nls = NULL), the pointers are
@@ -634,3 +636,5 @@ xfs_unicode_nls_free(
 	if (src_name != conv_name)
 		xfs_da_name_free((uchar_t *)conv_name);
 }
+
+#endif /* CONFIG_XFS_UNICODE_NLS */
Index: kern_ci/fs/xfs/xfs_unicode.h
===================================================================
--- kern_ci.orig/fs/xfs/xfs_unicode.h
+++ kern_ci/fs/xfs/xfs_unicode.h
@@ -65,6 +65,8 @@ int xfs_unicode_validate(const uchar_t *
 int xfs_unicode_read_cft(struct xfs_mount *mp);
 void xfs_unicode_free_cft(const xfs_cft_t *cft);
 
+#ifdef CONFIG_XFS_UNICODE_NLS
+
 #define xfs_is_using_nls(mp)	((mp)->m_nls != NULL)
 
 int xfs_unicode_to_nls(struct xfs_mount *mp, const uchar_t *uni_name,
@@ -73,7 +75,20 @@ int xfs_nls_to_unicode(struct xfs_mount 
 		int nls_namelen, const uchar_t **uni_name, int *uni_namelen);
 void xfs_unicode_nls_free(const uchar_t *src_name, const uchar_t *conv_name);
 
-#else
+#else /* CONFIG_XFS_UNICODE_NLS */
+
+#define xfs_is_using_nls(mp)		0
+
+#define xfs_unicode_to_nls(mp, uname, ulen, pnname, pnlen) \
+		((*(pnname)) = (uname), (*(pnlen)) = (ulen), 0)
+#define xfs_nls_to_unicode(mp, nname, nlen, puname, pulen) \
+		((*(puname)) = (nname), (*(pulen)) = (nlen), \
+		 xfs_unicode_validate(nname, nlen))
+#define xfs_unicode_nls_free(sname, cname)
+
+#endif /* CONFIG_XFS_UNICODE_NLS */
+
+#else /* CONFIG_XFS_UNICODE */
 
 #define xfs_unicode_nameops		xfs_default_nameops
 #define xfs_unicode_ci_nameops		xfs_default_nameops

-- 

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

* Re: [PATCH 1/7] XFS: Name operation vector for hash and compare
  2008-04-02  6:25 ` [PATCH 1/7] XFS: Name operation vector for hash and compare Barry Naujok
@ 2008-04-03  0:22   ` Josef 'Jeff' Sipek
  2008-04-03  4:50     ` Barry Naujok
  2008-04-03  1:29   ` David Chinner
  1 sibling, 1 reply; 48+ messages in thread
From: Josef 'Jeff' Sipek @ 2008-04-03  0:22 UTC (permalink / raw)
  To: Barry Naujok; +Cc: xfs, linux-fsdevel

On Wed, Apr 02, 2008 at 04:25:09PM +1000, Barry Naujok wrote:
...
> Index: kern_ci/fs/xfs/xfs_da_btree.h
> ===================================================================
> --- kern_ci.orig/fs/xfs/xfs_da_btree.h
> +++ kern_ci/fs/xfs/xfs_da_btree.h
> @@ -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;

It is somewhat unfortunate that the "matches" case has multiple values.

memcmp, strcmp, etc. return 0 if the two match, and you make >0 a match, and
0 if they don't.  This is really a nitpick, and I don't think there is a way
around...if everyone uses the enum all should be fine.

...
> +/*
> + * Name ops for directory and/or attr name operations
> + */
> +
> +typedef xfs_dahash_t	(*xfs_hashname_t)(const uchar_t *, int);
> +typedef xfs_dacmp_t	(*xfs_compname_t)(const uchar_t *, int,
> +					  const uchar_t *, int);

Why have typedefs for function pointers? Sometimes, they even cause problems
(I remember Eric finding a nasty 64-bit bug related to a function pointer
typedef).

Since IRIX isn't on the supported OS list anymore, what's the policy with
coding style within XFS?

...
> Index: kern_ci/fs/xfs/xfs_dir2.h
> ===================================================================
> --- kern_ci.orig/fs/xfs/xfs_dir2.h
> +++ kern_ci/fs/xfs/xfs_dir2.h
> @@ -85,6 +85,12 @@ 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((n), (l)))
> +
> +#define xfs_dir_compname(dp, n1, l1, n2, l2) \
> +		((dp)->i_mount->m_dirnameops->compname((n1), (l1), (n2), (l2)))

#define vs. static inline...

I guess this comes back to my question before...what is the coding style
direction you want XFS to go in? More Linux-like (static inline)? or keep it
more IRIX-like (#define)?

...
> --- kern_ci.orig/fs/xfs/xfs_dir2_block.c
> +++ kern_ci/fs/xfs/xfs_dir2_block.c
...
> @@ -698,19 +699,33 @@ xfs_dir2_block_lookup_int(
>  			((char *)block + xfs_dir2_dataptr_to_off(mp, addr));
>  		/*
>  		 * 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
>  		 */
> -		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_da_compname(dep->name, dep->namelen,
> +						args->name, args->namelen);

Initial reaction: What's going on here?

if oknoent:
	use the mount-determined cmp function
else:
	use case-sensitive

That combined with the comment above makes it understandable...but what does
"oknoent" have to do with the whole thing? Wouldn't "exact_match" be a
better name?

Aside from the oknoent rename, I might even turn the ?: into a if-else.

> +		if (cmp != XFS_CMP_DIFFERENT && cmp != args->cmpresult) {
> +			args->cmpresult = cmp;
>  			*bpp = bp;
>  			*entno = mid;
> -			return 0;
> +			if (cmp == XFS_CMP_EXACT)
> +				return 0;
>  		}

I'd put a comment above the above block...reminding whoever that if you get
XFS_CMP_CASE, you keep scanning to make sure you don't get XFS_CMP_EXACT.

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

Same as above.

This code is very similar to the above...maybe they should be factored out
in some cleanup patch series.

...
> @@ -578,19 +579,27 @@ xfs_dir2_leafn_lookup_int(
>  			/*
>  			 * 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_da_compname(dep->name, dep->namelen,
> +						args->name, args->namelen);

And again, the same applies. :)

...
> +				}
>  			}
>  		}
>  	}

Side note: That's a lot of nesting...yuck :)

Josef 'Jeff' Sipek.

-- 
UNIX is user-friendly ... it's just selective about who it's friends are

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

* Re: [PATCH 2/7] XFS: ASCII case-insensitive support
  2008-04-02  6:25 ` [PATCH 2/7] XFS: ASCII case-insensitive support Barry Naujok
@ 2008-04-03  0:35   ` Josef 'Jeff' Sipek
  2008-04-03  1:53   ` David Chinner
  2008-04-03 22:55   ` Christoph Hellwig
  2 siblings, 0 replies; 48+ messages in thread
From: Josef 'Jeff' Sipek @ 2008-04-03  0:35 UTC (permalink / raw)
  To: Barry Naujok; +Cc: xfs, linux-fsdevel

On Wed, Apr 02, 2008 at 04:25:10PM +1000, Barry Naujok wrote:
> Implement ASCII case-insensitive support. It's primary purpose 
> is for supporting existing filesystems that already use this
> case-insensitive mode migrated from IRIX. But, if you only need
> ASCII-only case-insensitive support (ie. English only) and will
> never use another language, then this mode is perfectly adequate.
> 
> ASCII-CI is implemented by generating hashes based on lower-case
> letters and doing lower-case compares. It implements a new
> xfs_nameops vector for doing the hashes and comparisons for
> all filename operations.
> 
> It also overrides the Linux dentry cache operations with its
> own hash and compare functions (the same as used in the xfs_nameops
> vector).
> 
> To create a filesystem with this CI mode, use:
> # mkfs.xfs -n version=ci <device>
 
Since you have to mkfs anyway, why not just use the unicode mkfs option, and
the ci mount option. Then, you can just drop this patch :)

Josef 'Jeff' Sipek.

-- 
Ready; T=0.01/0.01 20:32:39

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

* Re: [PATCH 7/7] XFS: NLS config option
  2008-04-02  6:25 ` [PATCH 7/7] XFS: NLS config option Barry Naujok
@ 2008-04-03  1:26   ` Josef 'Jeff' Sipek
  2008-04-03  1:38     ` Barry Naujok
  0 siblings, 1 reply; 48+ messages in thread
From: Josef 'Jeff' Sipek @ 2008-04-03  1:26 UTC (permalink / raw)
  To: Barry Naujok; +Cc: xfs, linux-fsdevel

On Wed, Apr 02, 2008 at 04:25:15PM +1000, Barry Naujok wrote:
> This optional patch implements the NLS support as a CONFIG option.

Any reason this is a separate patch, and not part of the previous patches?

...
> --- kern_ci.orig/fs/xfs/Kconfig
> +++ kern_ci/fs/xfs/Kconfig
> @@ -87,6 +87,16 @@ config XFS_UNICODE
>  
>  	  If you don't require UTF-8 enforcement, say N.
>  
> +config XFS_UNICODE_NLS
> +	bool "XFS NLS Unicode support
> +	depends on XFS_UNICODE
> +	help
> +	  NLS (Native Language Support) allows non-UTF8 locales to
> +	  interact with XFS Unicode support. To specify the character
> +	  set being used, use the "-n nls=<charset>" mount option.

"mount option"? Or was that supposed to say mkfs?


from mount(8) manpage:

       -n     Mount without writing in /etc/mtab.  This is necessary for
              example when /etc is on a read-only file system.

...
> Index: kern_ci/fs/xfs/xfs_unicode.h
> ===================================================================
> --- kern_ci.orig/fs/xfs/xfs_unicode.h
> +++ kern_ci/fs/xfs/xfs_unicode.h
> @@ -65,6 +65,8 @@ int xfs_unicode_validate(const uchar_t *
>  int xfs_unicode_read_cft(struct xfs_mount *mp);
>  void xfs_unicode_free_cft(const xfs_cft_t *cft);
>  
> +#ifdef CONFIG_XFS_UNICODE_NLS
> +
>  #define xfs_is_using_nls(mp)	((mp)->m_nls != NULL)
>  
>  int xfs_unicode_to_nls(struct xfs_mount *mp, const uchar_t *uni_name,
> @@ -73,7 +75,20 @@ int xfs_nls_to_unicode(struct xfs_mount 
>  		int nls_namelen, const uchar_t **uni_name, int *uni_namelen);
>  void xfs_unicode_nls_free(const uchar_t *src_name, const uchar_t *conv_name);
>  
> -#else
> +#else /* CONFIG_XFS_UNICODE_NLS */
> +
> +#define xfs_is_using_nls(mp)		0
> +
> +#define xfs_unicode_to_nls(mp, uname, ulen, pnname, pnlen) \
> +		((*(pnname)) = (uname), (*(pnlen)) = (ulen), 0)
> +#define xfs_nls_to_unicode(mp, nname, nlen, puname, pulen) \
> +		((*(puname)) = (nname), (*(pulen)) = (nlen), \
> +		 xfs_unicode_validate(nname, nlen))

While I commend your use of the comma operator, I really think those should
be static inlines :)

Josef 'Jeff' Sipek.

-- 
Only two things are infinite, the universe and human stupidity, and I'm not
sure about the former.
		- Albert Einstein

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

* Re: [PATCH 1/7] XFS: Name operation vector for hash and compare
  2008-04-02  6:25 ` [PATCH 1/7] XFS: Name operation vector for hash and compare Barry Naujok
  2008-04-03  0:22   ` Josef 'Jeff' Sipek
@ 2008-04-03  1:29   ` David Chinner
  2008-04-03  1:45     ` Barry Naujok
  2008-04-03 22:51     ` Christoph Hellwig
  1 sibling, 2 replies; 48+ messages in thread
From: David Chinner @ 2008-04-03  1:29 UTC (permalink / raw)
  To: Barry Naujok; +Cc: xfs, linux-fsdevel

On Wed, Apr 02, 2008 at 04:25:09PM +1000, Barry Naujok wrote:
> Adds two pieces of functionality for the basis of case-insensitive
> support in XFS:
> 
> 1.  A comparison result enumerated type: xfs_dacmp_t. It represents an
>     exact match, case-insensitive match or no match at all. This patch
>     only implements different and exact results.
> 
> 2.  xfs_nameops vector for specifying how to perform the hash generation
>     of filenames and comparision methods. In this patch the hash vector
>     points to the existing xfs_da_hashname function and the comparison
>     method does a length compare, and if the same, does a memcmp and
>     return the xfs_dacmp_t result.
> 
> All filename functions that use the hash (create, lookup remove, rename,
> etc) now use the xfs_nameops.hashname function and all directory lookup
> functions also use the xfs_nameops.compname function.

Ok, so internally I see this is not the case. I'll comment on that
inline.

> The lookup functions also handle case-insensitive results even though
> the default comparison function cannot return that. And important
> aspect of the lookup functions is that an exact match always has
> precedence over a case-insensitive. So while a case-insensitive match
> is found, we have to keep looking just in case there is an exact
> match. In the meantime, the info for the first case-insensitive match
> is retained if no exact match is found.
> 
> Signed-off-by: Barry Naujok <bnaujok@sgi.com>
......
>  }
>  
> +xfs_dacmp_t
> +xfs_da_compname(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;
> +}
> +
> +struct xfs_nameops xfs_default_nameops = {

const.

>  #ifdef __KERNEL__
>  /*========================================================================
> @@ -248,7 +271,12 @@ xfs_daddr_t	xfs_da_reada_buf(struct xfs_
>  int	xfs_da_shrink_inode(xfs_da_args_t *args, xfs_dablk_t dead_blkno,
>  					  xfs_dabuf_t *dead_buf);
>  
> +extern struct xfs_nameops xfs_default_nameops;

Does this need global visibility? It's only needed in xfs_dir_mount(),
right?

> Index: kern_ci/fs/xfs/xfs_dir2.h
> ===================================================================
> --- kern_ci.orig/fs/xfs/xfs_dir2.h
> +++ kern_ci/fs/xfs/xfs_dir2.h
> @@ -85,6 +85,12 @@ 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((n), (l)))
> +
> +#define xfs_dir_compname(dp, n1, l1, n2, l2) \
> +		((dp)->i_mount->m_dirnameops->compname((n1), (l1), (n2), (l2)))
> +

Static inline functions, please.

>  /*
>   * Utility routines for v2 directories.
>   */
> Index: kern_ci/fs/xfs/xfs_dir2_block.c
> ===================================================================
> --- kern_ci.orig/fs/xfs/xfs_dir2_block.c
> +++ kern_ci/fs/xfs/xfs_dir2_block.c
> @@ -643,6 +643,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;
> @@ -698,19 +699,33 @@ xfs_dir2_block_lookup_int(
>  			((char *)block + xfs_dir2_dataptr_to_off(mp, addr));
>  		/*
>  		 * 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
>  		 */
> -		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_da_compname(dep->name, dep->namelen,
> +						args->name, args->namelen);

Why add this "fast path"? All you're saving here is a few
instructions but making the code much harder to follow.

		cmp = xfs_dir_compname(dp, dep->name, dep->namelen,
						args->name, args->namelen);

Will do exactly the same thing and I'd much prefer readable code
over prematurely optimised code any day of the week....

> +		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;

So if we find multiple case matches, we'll take the last we find?

>  	/*
>  	 * No match, release the buffer and return ENOENT.
>  	 */
> -	ASSERT(args->oknoent);
>  	xfs_da_brelse(tp, bp);
>  	return XFS_ERROR(ENOENT);

Should we really be promoting that assert to before we return a successful
case match?

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

Same again.

> +		if (cmp != XFS_CMP_DIFFERENT && cmp != args->cmpresult) {
> +			args->cmpresult = cmp;
>  			*indexp = index;
> -			return 0;
> +			if (cmp == XFS_CMP_EXACT) {
> +				/*
> +				 * case exact match: release the case-insens.
> +				 * match buffer if it exists and return the
> +				 * current data buffer.
> +				 */
> +				if (cbp && cbp != dbp)
> +					xfs_da_brelse(tp, cbp);
> +				*dbpp = dbp;
> +				return 0;
> +			}
> +			cbp = dbp;
>  		}
>  	}
> +	ASSERT(args->oknoent);
> +	if (args->cmpresult == XFS_CMP_CASE) {
> +		/*
> +		 * case-insensitive match: release current buffer and
> +		 * return the buffer with the case-insensitive match.
> +		 */
> +		if (cbp != dbp)
> +			xfs_da_brelse(tp, dbp);
> +		*dbpp = cbp;
> +		return 0;
> +	}
>  	/*
>  	 * No match found, return ENOENT.
>  	 */
> -	ASSERT(args->oknoent);

Same question about promoting the assert....

> @@ -578,19 +579,27 @@ xfs_dir2_leafn_lookup_int(
>  			/*
>  			 * 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_da_compname(dep->name, dep->namelen,
> +						args->name, args->namelen);

Same again.

> @@ -907,9 +914,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_da_compname(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);

This only checks for an exact match - what is supposed to happen
with a XFS_CMP_CASE return?

> @@ -1044,9 +1050,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_da_compname(sfep->name, sfep->namelen,
> +					    args->name, args->namelen) ==
> +					XFS_CMP_EXACT) {

ditto.

Cheers,

Dave.
-- 
Dave Chinner
Principal Engineer
SGI Australian Software Group

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

* Re: [PATCH 7/7] XFS: NLS config option
  2008-04-03  1:26   ` Josef 'Jeff' Sipek
@ 2008-04-03  1:38     ` Barry Naujok
  0 siblings, 0 replies; 48+ messages in thread
From: Barry Naujok @ 2008-04-03  1:38 UTC (permalink / raw)
  To: Josef 'Jeff' Sipek; +Cc: xfs, linux-fsdevel

On Thu, 03 Apr 2008 11:26:10 +1000, Josef 'Jeff' Sipek  
<jeffpc@josefsipek.net> wrote:

> On Wed, Apr 02, 2008 at 04:25:15PM +1000, Barry Naujok wrote:
>> This optional patch implements the NLS support as a CONFIG option.
>
> Any reason this is a separate patch, and not part of the previous  
> patches?
>
> ...
>> --- kern_ci.orig/fs/xfs/Kconfig
>> +++ kern_ci/fs/xfs/Kconfig
>> @@ -87,6 +87,16 @@ config XFS_UNICODE
>>
>>  	  If you don't require UTF-8 enforcement, say N.
>>
>> +config XFS_UNICODE_NLS
>> +	bool "XFS NLS Unicode support
>> +	depends on XFS_UNICODE
>> +	help
>> +	  NLS (Native Language Support) allows non-UTF8 locales to
>> +	  interact with XFS Unicode support. To specify the character
>> +	  set being used, use the "-n nls=<charset>" mount option.
>
> "mount option"? Or was that supposed to say mkfs?

Hmm... typo: "-o nls=<charset>" in mount :)

> from mount(8) manpage:
>
>        -n     Mount without writing in /etc/mtab.  This is necessary for
>               example when /etc is on a read-only file system.
>
> ...
>> Index: kern_ci/fs/xfs/xfs_unicode.h
>> ===================================================================
>> --- kern_ci.orig/fs/xfs/xfs_unicode.h
>> +++ kern_ci/fs/xfs/xfs_unicode.h
>> @@ -65,6 +65,8 @@ int xfs_unicode_validate(const uchar_t *
>>  int xfs_unicode_read_cft(struct xfs_mount *mp);
>>  void xfs_unicode_free_cft(const xfs_cft_t *cft);
>>
>> +#ifdef CONFIG_XFS_UNICODE_NLS
>> +
>>  #define xfs_is_using_nls(mp)	((mp)->m_nls != NULL)
>>
>>  int xfs_unicode_to_nls(struct xfs_mount *mp, const uchar_t *uni_name,
>> @@ -73,7 +75,20 @@ int xfs_nls_to_unicode(struct xfs_mount
>>  		int nls_namelen, const uchar_t **uni_name, int *uni_namelen);
>>  void xfs_unicode_nls_free(const uchar_t *src_name, const uchar_t  
>> *conv_name);
>>
>> -#else
>> +#else /* CONFIG_XFS_UNICODE_NLS */
>> +
>> +#define xfs_is_using_nls(mp)		0
>> +
>> +#define xfs_unicode_to_nls(mp, uname, ulen, pnname, pnlen) \
>> +		((*(pnname)) = (uname), (*(pnlen)) = (ulen), 0)
>> +#define xfs_nls_to_unicode(mp, nname, nlen, puname, pulen) \
>> +		((*(puname)) = (nname), (*(pulen)) = (nlen), \
>> +		 xfs_unicode_validate(nname, nlen))
>
> While I commend your use of the comma operator, I really think those  
> should
> be static inlines :)
>
> Josef 'Jeff' Sipek.
>



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

* Re: [PATCH 1/7] XFS: Name operation vector for hash and compare
  2008-04-03  1:29   ` David Chinner
@ 2008-04-03  1:45     ` Barry Naujok
  2008-04-03 22:51     ` Christoph Hellwig
  1 sibling, 0 replies; 48+ messages in thread
From: Barry Naujok @ 2008-04-03  1:45 UTC (permalink / raw)
  To: David Chinner; +Cc: xfs, linux-fsdevel

On Thu, 03 Apr 2008 11:29:12 +1000, David Chinner <dgc@sgi.com> wrote:

> On Wed, Apr 02, 2008 at 04:25:09PM +1000, Barry Naujok wrote:
>> Adds two pieces of functionality for the basis of case-insensitive
>> support in XFS:
>>
>> 1.  A comparison result enumerated type: xfs_dacmp_t. It represents an
>>     exact match, case-insensitive match or no match at all. This patch
>>     only implements different and exact results.
>>
>> 2.  xfs_nameops vector for specifying how to perform the hash generation
>>     of filenames and comparision methods. In this patch the hash vector
>>     points to the existing xfs_da_hashname function and the comparison
>>     method does a length compare, and if the same, does a memcmp and
>>     return the xfs_dacmp_t result.
>>
>> All filename functions that use the hash (create, lookup remove, rename,
>> etc) now use the xfs_nameops.hashname function and all directory lookup
>> functions also use the xfs_nameops.compname function.
>
> Ok, so internally I see this is not the case. I'll comment on that
> inline.

Ah yes. Remove and rename rely on an exact match. Forgot about that
when documenting this patch.

>> The lookup functions also handle case-insensitive results even though
>> the default comparison function cannot return that. And important
>> aspect of the lookup functions is that an exact match always has
>> precedence over a case-insensitive. So while a case-insensitive match
>> is found, we have to keep looking just in case there is an exact
>> match. In the meantime, the info for the first case-insensitive match
>> is retained if no exact match is found.
>>
>> Signed-off-by: Barry Naujok <bnaujok@sgi.com>
> ......
>>  }
>>
>> +xfs_dacmp_t
>> +xfs_da_compname(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;
>> +}
>> +
>> +struct xfs_nameops xfs_default_nameops = {
>
> const.
>
>>  #ifdef __KERNEL__
>>  /*========================================================================
>> @@ -248,7 +271,12 @@ xfs_daddr_t	xfs_da_reada_buf(struct xfs_
>>  int	xfs_da_shrink_inode(xfs_da_args_t *args, xfs_dablk_t dead_blkno,
>>  					  xfs_dabuf_t *dead_buf);
>>
>> +extern struct xfs_nameops xfs_default_nameops;
>
> Does this need global visibility? It's only needed in xfs_dir_mount(),
> right?

Good point, I'll fix this.

>> Index: kern_ci/fs/xfs/xfs_dir2.h
>> ===================================================================
>> --- kern_ci.orig/fs/xfs/xfs_dir2.h
>> +++ kern_ci/fs/xfs/xfs_dir2.h
>> @@ -85,6 +85,12 @@ 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((n), (l)))
>> +
>> +#define xfs_dir_compname(dp, n1, l1, n2, l2) \
>> +		((dp)->i_mount->m_dirnameops->compname((n1), (l1), (n2), (l2)))
>> +
>
> Static inline functions, please.

Ok.

>>  /*
>>   * Utility routines for v2 directories.
>>   */
>> Index: kern_ci/fs/xfs/xfs_dir2_block.c
>> ===================================================================
>> --- kern_ci.orig/fs/xfs/xfs_dir2_block.c
>> +++ kern_ci/fs/xfs/xfs_dir2_block.c
>> @@ -643,6 +643,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;
>> @@ -698,19 +699,33 @@ xfs_dir2_block_lookup_int(
>>  			((char *)block + xfs_dir2_dataptr_to_off(mp, addr));
>>  		/*
>>  		 * 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
>>  		 */
>> -		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_da_compname(dep->name, dep->namelen,
>> +						args->name, args->namelen);
>
> Why add this "fast path"? All you're saving here is a few
> instructions but making the code much harder to follow.
>
> 		cmp = xfs_dir_compname(dp, dep->name, dep->namelen,
> 						args->name, args->namelen);
>
> Will do exactly the same thing and I'd much prefer readable code
> over prematurely optimised code any day of the week....

Ok, I'll change that code (might make it more CONFIG_XFS_CI capable ;) )

>> +		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;
>
> So if we find multiple case matches, we'll take the last we find?

No, the first as *bpp and *entno is only set for the first
case-insensitive match or overriden for an exact match.

>>  	/*
>>  	 * No match, release the buffer and return ENOENT.
>>  	 */
>> -	ASSERT(args->oknoent);
>>  	xfs_da_brelse(tp, bp);
>>  	return XFS_ERROR(ENOENT);
>
> Should we really be promoting that assert to before we return a  
> successful
> case match?

Yes as a !args->oknoent has to find an exact match. It's a big
failure otherwise (ie. remove/rename case).

>> @@ -907,9 +914,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_da_compname(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);
>
> This only checks for an exact match - what is supposed to happen
> with a XFS_CMP_CASE return?
>
>> @@ -1044,9 +1050,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_da_compname(sfep->name, sfep->namelen,
>> +					    args->name, args->namelen) ==
>> +					XFS_CMP_EXACT) {
>
> ditto.

Like I stated above, remove/rename (replace) require an exact match.


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

* Re: [PATCH 3/7] XFS: Refactor node format directory lookup/addname
  2008-04-02  6:25 ` [PATCH 3/7] XFS: Refactor node format directory lookup/addname Barry Naujok
@ 2008-04-03  1:51   ` Josef 'Jeff' Sipek
  2008-04-03  4:04     ` Barry Naujok
  2008-04-03  4:33   ` David Chinner
  1 sibling, 1 reply; 48+ messages in thread
From: Josef 'Jeff' Sipek @ 2008-04-03  1:51 UTC (permalink / raw)
  To: Barry Naujok; +Cc: xfs, linux-fsdevel

On Wed, Apr 02, 2008 at 04:25:11PM +1000, Barry Naujok wrote:
...
> --- kern_ci.orig/fs/xfs/xfs_dir2_node.c
> +++ kern_ci/fs/xfs/xfs_dir2_node.c
...
> @@ -432,27 +429,15 @@ xfs_dir2_leafn_lookup_int(
>  	/*
>  	 * Do we have a buffer coming in?
>  	 */
> -	if (state->extravalid)
> -		curbp = state->extrablk.bp;
> -	else
> -		curbp = NULL;
> +	curbp = state->extravalid ? state->extrablk.bp : NULL;
>  	/*
>  	 * For addname, it's a free block buffer, get the block number.
>  	 */
> -	if (args->addname) {
> -		curfdb = curbp ? state->extrablk.blkno : -1;
> -		curdb = -1;
> -		length = xfs_dir2_data_entsize(args->namelen);
> -		if ((free = (curbp ? curbp->data : NULL)))
> -			ASSERT(be32_to_cpu(free->hdr.magic) == XFS_DIR2_FREE_MAGIC);
> -	}
> -	/*
> -	 * For others, it's a data block buffer, get the block number.
> -	 */
> -	else {
> -		curfdb = -1;
> -		curdb = curbp ? state->extrablk.blkno : -1;
> -	}
> +	curfdb = curbp ? state->extrablk.blkno : -1;
> +	free = curbp ? curbp->data : NULL;

The previous 3 lines can be cleaned up as:

if (state->extravalid)
	curbp = state->extrablk.bp;
else
	curbp = NULL;

if (curbp) {
	curfdb = state->extrablk.blkno;
	free = curbp->data;
} else {
	curfdb = -1;
	free = NULL;
}


or, if (state->extravalid && state->extrablk.bp == NULL) is _ALWAYS_ false
(which seems to be the case), you can do:


if (state->extravalid) {
	curbp = state->extrablk.bp;
	curfdb = state->extrablk.blkno;
	free = curbp->data;
} else {
	curbp = NULL;
	curfdb = -1;
	free = NULL;
}

...
> +static int
> +xfs_dir2_leafn_lookup_for_entry(
> +	xfs_dabuf_t		*bp,		/* leaf buffer */
> +	xfs_da_args_t		*args,		/* operation arguments */
> +	int			*indexp,	/* out: leaf entry index */
> +	xfs_da_state_t		*state)		/* state to fill in */
> +{
> +	xfs_dabuf_t		*curbp;		/* current data/free buffer */
> +	xfs_dir2_db_t		curdb;		/* current data block number */
> +	xfs_dir2_data_entry_t	*dep;		/* data block entry */
> +	xfs_inode_t		*dp;		/* incore directory inode */
> +	int			error;		/* error return value */
> +	int			index;		/* leaf entry index */
> +	xfs_dir2_leaf_t		*leaf;		/* leaf structure */
> +	xfs_dir2_leaf_entry_t	*lep;		/* leaf entry */
> +	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;		/* comparison result */
> +	xfs_dabuf_t		*ci_bp = NULL;	/* buffer with CI match */

Did you try to check the stack usage (scripts/checkstack.pl)?

> +	dp = args->dp;
> +	tp = args->trans;
> +	mp = dp->i_mount;
> +	leaf = bp->data;
> +	ASSERT(be16_to_cpu(leaf->hdr.info.magic) == XFS_DIR2_LEAFN_MAGIC);
> +#ifdef __KERNEL__
> +	ASSERT(be16_to_cpu(leaf->hdr.count) > 0);
> +#endif

What's this #ifdef for?

> +		dep = (xfs_dir2_data_entry_t *)((char *)curbp->data +
> +			xfs_dir2_dataptr_to_off(mp, be32_to_cpu(lep->address)));

Perhaps a static inline to do this calculation more cleanly (assuming it's
done elsewhere as well).

...
> +		/*
> +		 * Compare the entry, return it if it matches.
> +		 */
> +		cmp = args->oknoent ?
> +			xfs_dir_compname(dp, dep->name, dep->namelen,
> +					args->name, args->namelen):
> +			xfs_da_compname(dep->name, dep->namelen,
> +					args->name, args->namelen);

same as comment for 1/7.

Josef 'Jeff' Sipek.

-- 
My public GPG key can be found at
http://www.josefsipek.net/gpg/public-0xC7958FFE.txt

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

* Re: [PATCH 2/7] XFS: ASCII case-insensitive support
  2008-04-02  6:25 ` [PATCH 2/7] XFS: ASCII case-insensitive support Barry Naujok
  2008-04-03  0:35   ` Josef 'Jeff' Sipek
@ 2008-04-03  1:53   ` David Chinner
  2008-04-03 17:09     ` Christoph Hellwig
  2008-04-03 22:55   ` Christoph Hellwig
  2 siblings, 1 reply; 48+ messages in thread
From: David Chinner @ 2008-04-03  1:53 UTC (permalink / raw)
  To: Barry Naujok; +Cc: xfs, linux-fsdevel

On Wed, Apr 02, 2008 at 04:25:10PM +1000, Barry Naujok wrote:
> Implement ASCII case-insensitive support. It's primary purpose 
> is for supporting existing filesystems that already use this
> case-insensitive mode migrated from IRIX. But, if you only need
> ASCII-only case-insensitive support (ie. English only) and will
> never use another language, then this mode is perfectly adequate.
> 
> ASCII-CI is implemented by generating hashes based on lower-case
> letters and doing lower-case compares. It implements a new
> xfs_nameops vector for doing the hashes and comparisons for
> all filename operations.
> 
> It also overrides the Linux dentry cache operations with its
> own hash and compare functions (the same as used in the xfs_nameops
> vector).
> 
> To create a filesystem with this CI mode, use:
> # mkfs.xfs -n version=ci <device>
> 
> Signed-off-by: Barry Naujok <bnaujok@sgi.com>
> 
> ---
>  fs/xfs/linux-2.6/xfs_iops.c  |   46 +++++++++++++++++++++++++++++++++++++-
>  fs/xfs/linux-2.6/xfs_linux.h |    1
>  fs/xfs/linux-2.6/xfs_super.c |    4 +++
>  fs/xfs/xfs_dir2.c            |   52 ++++++++++++++++++++++++++++++++++++++++++-
>  fs/xfs/xfs_fs.h              |    1
>  fs/xfs/linux-2.6/xfs_iops.c  |   46 +++++++++++++++++++++++++++++++++++++-
>  fs/xfs/linux-2.6/xfs_linux.h |    1 
>  fs/xfs/linux-2.6/xfs_super.c |    4 +++
>  fs/xfs/xfs_dir2.c            |   52 ++++++++++++++++++++++++++++++++++++++++++-
>  fs/xfs/xfs_fs.h              |    1 
>  fs/xfs/xfs_fsops.c           |    4 ++-
>  fs/xfs/xfs_sb.h              |   10 +++++++-
>  7 files changed, 114 insertions(+), 4 deletions(-)
> 
> Index: kern_ci/fs/xfs/linux-2.6/xfs_iops.c
> ===================================================================
> --- kern_ci.orig/fs/xfs/linux-2.6/xfs_iops.c
> +++ kern_ci/fs/xfs/linux-2.6/xfs_iops.c
> @@ -47,6 +47,7 @@
>  #include "xfs_buf_item.h"
>  #include "xfs_utils.h"
>  #include "xfs_vnodeops.h"
> +#include "xfs_da_btree.h"
>  
>  #include <linux/capability.h>
>  #include <linux/xattr.h>
> @@ -54,6 +55,8 @@
>  #include <linux/security.h>
>  #include <linux/falloc.h>
>  
> +struct dentry_operations xfs_ci_dentry_operations;

static?

> +
> +STATIC int
> +xfs_ci_dentry_hash(
> +	struct dentry	*dir,
> +	struct qstr	*this)
> +{
> +	this->hash = xfs_dir_hashname(XFS_I(dir->d_inode),
> +				this->name, this->len);
> +	return 0;
> +}
> +
> +STATIC int
> +xfs_ci_dentry_compare(
> +	struct dentry	*dir,
> +	struct qstr	*a,
> +	struct qstr	*b)
> +{
> +	int	result = xfs_dir_compname(XFS_I(dir->d_inode), a->name, a->len,
> +					b->name, b->len) == XFS_CMP_DIFFERENT;
> +	/*
> +	 * result == 0 if a match is found, and if so, copy the name in "b"
> +	 * to "a" to cope with negative dentries getting the correct name.
> +	 */
> +	if (result == 0)
> +		memcpy((unsigned char *)a->name, b->name, a->len);
> +	return result;
> +}

large comment in the middle of a 5 line function? Move it above
the function. Also should not need a cast in memcpy()....

/*
 * xfs_dir_compname will return 0 if a match is found. If so, we
 * need to copy the name in "b" to "a" to cope with negative dentries
 * getting the correct name.
 */
STATIC int
xfs_ci_dentry_compare(
	struct dentry	*dir,
	struct qstr	*a,
	struct qstr	*b)
{
	int	result;
	
	result = xfs_dir_compname(XFS_I(dir->d_inode), a->name, a->len,
					b->name, b->len) == XFS_CMP_DIFFERENT;
	if (!result)
		memcpy(a->name, b->name, a->len);
	return result;
}

> +
> +struct dentry_operations xfs_ci_dentry_operations =
> +{
> +	.d_hash = xfs_ci_dentry_hash,
> +	.d_compare = xfs_ci_dentry_compare,
> +};

static.

You should probably move these functions and declarations to before
xfs_ci_dentry_operations is used so you can avoid the forward
declaration....

> ===================================================================
> --- kern_ci.orig/fs/xfs/linux-2.6/xfs_super.c
> +++ kern_ci/fs/xfs/linux-2.6/xfs_super.c
> @@ -67,6 +67,8 @@ static kmem_zone_t *xfs_vnode_zone;
>  static kmem_zone_t *xfs_ioend_zone;
>  mempool_t *xfs_ioend_pool;
>  
> +extern struct dentry_operations xfs_ci_dentry_operations;
> +
>  STATIC struct xfs_mount_args *
>  xfs_args_allocate(
>  	struct super_block	*sb,
> @@ -1359,6 +1361,8 @@ xfs_fs_fill_super(
>  		error = ENOMEM;
>  		goto fail_vnrele;
>  	}
> +	if (xfs_sb_version_hasoldci(&mp->m_sb))
> +		sb->s_root->d_op = &xfs_ci_dentry_operations;

Write a helper function for this: xfs_set_ci_dentry_ops(mp, dentry)
rather than exporting the xfs_ci_dentry_operations structure.

>  
> +/*
> + * V1/OLDCI case-insensitive support for directories
> + *
> + * This is ASCII only case support, ie. A-Z.
> + */

I'd mention that this is legacy code for supporting the Irix
format CI.

> @@ -629,7 +631,7 @@ xfs_fs_goingdown(
>  			xfs_force_shutdown(mp, SHUTDOWN_FORCE_UMOUNT);
>  			thaw_bdev(sb->s_bdev, sb);
>  		}
> -	
> +
>  		break;

random whitespace change?

> Index: kern_ci/fs/xfs/xfs_sb.h
> ===================================================================
> --- kern_ci.orig/fs/xfs/xfs_sb.h
> +++ kern_ci/fs/xfs/xfs_sb.h
> @@ -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	\

Whitespace. 

But it's a shame you're being sensible about this - I kinda liked
the Irix name for this feature (XFS_SB_VERSION_BORGBIT). :)

Cheers,

Dave.
-- 
Dave Chinner
Principal Engineer
SGI Australian Software Group

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

* Re: [PATCH 4/7] XFS: Return case-insensitive match for dentry cache
  2008-04-02  6:25 ` [PATCH 4/7] XFS: Return case-insensitive match for dentry cache Barry Naujok
@ 2008-04-03  2:34   ` Josef 'Jeff' Sipek
  2008-04-03  5:22   ` David Chinner
  2008-04-03 23:06   ` Christoph Hellwig
  2 siblings, 0 replies; 48+ messages in thread
From: Josef 'Jeff' Sipek @ 2008-04-03  2:34 UTC (permalink / raw)
  To: Barry Naujok; +Cc: xfs, linux-fsdevel

On Wed, Apr 02, 2008 at 04:25:12PM +1000, Barry Naujok wrote:
...
> +	/*
> +	 * Directory with a 'disconnected' dentry; get a reference to the
> +	 * 'disconnected' dentry.
> +	 */
> +	dentry = list_entry(inode->i_dentry.next, struct dentry, d_alias);

list_first_entry does the .next for you.

...
> --- kern_ci.orig/fs/xfs/xfs_da_btree.c
> +++ kern_ci/fs/xfs/xfs_da_btree.c
> @@ -2176,6 +2176,22 @@ xfs_da_reada_buf(
>  		return rval;
>  }
>  
> +
> +kmem_zone_t	*xfs_da_name_zone;
> +
> +uchar_t *
> +xfs_da_name_alloc(void)
> +{
> +	return kmem_zone_zalloc(xfs_da_name_zone, KM_SLEEP);
> +}
> +
> +void
> +xfs_da_name_free(const uchar_t *name)

Since you don't care about the type anyway, you might want to make it void*,
and remove the cast from the lookup_ci code.

> +{
> +	kmem_zone_free(xfs_da_name_zone, (void *)name);

No need for the cast.

> --- kern_ci.orig/fs/xfs/xfs_dir2_leaf.c
> +++ kern_ci/fs/xfs/xfs_dir2_leaf.c
> @@ -1301,6 +1301,15 @@ xfs_dir2_leaf_lookup(
>  	 * Return the found inode number.
>  	 */
>  	args->inumber = be64_to_cpu(dep->inumber);
> +	/*
> +	 * If a case-insensitive match, allocate a buffer and copy the actual
> +	 * name into the buffer. Return it via args->value.
> +	 */
> +	if (args->cmpresult == XFS_CMP_CASE) {
> +		args->value = xfs_da_name_alloc();
> +		memcpy(args->value, dep->name, dep->namelen);
> +		args->valuelen = dep->namelen;

Perhaps having a static inline xfs_da_name_dup(...) would be useful...

...
> --- kern_ci.orig/fs/xfs/xfs_vnodeops.c
> +++ kern_ci/fs/xfs/xfs_vnodeops.c
> @@ -1762,24 +1762,33 @@ xfs_inactive(
>  int
>  xfs_lookup(
>  	xfs_inode_t		*dp,
> -	bhv_vname_t		*dentry,
> -	xfs_inode_t		**ipp)
> +	bhv_vstr_t		*d_name,
> +	xfs_inode_t		**ipp,
> +	bhv_vstr_t		*ci_name)
>  {
>  	xfs_inode_t		*ip;
>  	xfs_ino_t		e_inum;
>  	int			error;
>  	uint			lock_mode;
> +	xfs_name_t		name, rname;
>  
>  	xfs_itrace_entry(dp);
>  
>  	if (XFS_FORCED_SHUTDOWN(dp->i_mount))
>  		return XFS_ERROR(EIO);
>  
> +	name.name = (uchar_t *)d_name->name;

d_name->name is: const unsigned char*
name.name is:	 const uchar_t*

Is there any reason why you use uchar_t - beyond the other parts of XFS use
it? (I guess this is the same question that I asked before - coding style.)

xfs_types.h defines uchar_t as unsigned char...

Josef 'Jeff' Sipek.

-- 
Defenestration n. (formal or joc.):
  The act of removing Windows from your computer in disgust, usually
  followed by the installation of Linux or some other Unix-like operating
  system.

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

* Re: [PATCH 3/7] XFS: Refactor node format directory lookup/addname
  2008-04-03  1:51   ` Josef 'Jeff' Sipek
@ 2008-04-03  4:04     ` Barry Naujok
  2008-04-03  4:10       ` Barry Naujok
  0 siblings, 1 reply; 48+ messages in thread
From: Barry Naujok @ 2008-04-03  4:04 UTC (permalink / raw)
  To: Josef 'Jeff' Sipek; +Cc: xfs, linux-fsdevel

On Thu, 03 Apr 2008 11:51:22 +1000, Josef 'Jeff' Sipek  
<jeffpc@josefsipek.net> wrote:

>> +static int
>> +xfs_dir2_leafn_lookup_for_entry(
>> +	xfs_dabuf_t		*bp,		/* leaf buffer */
>> +	xfs_da_args_t		*args,		/* operation arguments */
>> +	int			*indexp,	/* out: leaf entry index */
>> +	xfs_da_state_t		*state)		/* state to fill in */
>> +{
>> +	xfs_dabuf_t		*curbp;		/* current data/free buffer */
>> +	xfs_dir2_db_t		curdb;		/* current data block number */
>> +	xfs_dir2_data_entry_t	*dep;		/* data block entry */
>> +	xfs_inode_t		*dp;		/* incore directory inode */
>> +	int			error;		/* error return value */
>> +	int			index;		/* leaf entry index */
>> +	xfs_dir2_leaf_t		*leaf;		/* leaf structure */
>> +	xfs_dir2_leaf_entry_t	*lep;		/* leaf entry */
>> +	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;		/* comparison result */
>> +	xfs_dabuf_t		*ci_bp = NULL;	/* buffer with CI match */
>
> Did you try to check the stack usage (scripts/checkstack.pl)?

on x86_64:

nameops.patch
   -> no difference

ascii_ci.patch
   -> no difference

refactor_leafn_lookup.patch (this one)
   -> no difference

return_name.patch
   -> xfs_dir_lookup from 152 down to 144 :)

unicode_ci.patch
   -> xfs_mkdir from 152 down to 136 :)
   -> new xfs_unicode_read_cft @ 120

nls_support.patch
   -> xfs_dir2_leaf_getdents from 136 up to 200 (ouch!)
   -> xfs_mkdir from 136 back to 152!
   -> xfs_create from 152 up to 168
   -> xfs_rmdir from 104 down to < 100?

This seems to be better than the stack usage Eric posted back in
response to my last patch set.



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

* Re: [PATCH 3/7] XFS: Refactor node format directory lookup/addname
  2008-04-03  4:04     ` Barry Naujok
@ 2008-04-03  4:10       ` Barry Naujok
  0 siblings, 0 replies; 48+ messages in thread
From: Barry Naujok @ 2008-04-03  4:10 UTC (permalink / raw)
  To: Barry Naujok, Josef 'Jeff' Sipek; +Cc: xfs, linux-fsdevel

On Thu, 03 Apr 2008 14:04:05 +1000, Barry Naujok <bnaujok@sgi.com> wrote:

> On Thu, 03 Apr 2008 11:51:22 +1000, Josef 'Jeff' Sipek  
> <jeffpc@josefsipek.net> wrote:
>
>>> +static int
>>> +xfs_dir2_leafn_lookup_for_entry(
>>> +	xfs_dabuf_t		*bp,		/* leaf buffer */
>>> +	xfs_da_args_t		*args,		/* operation arguments */
>>> +	int			*indexp,	/* out: leaf entry index */
>>> +	xfs_da_state_t		*state)		/* state to fill in */
>>> +{
>>> +	xfs_dabuf_t		*curbp;		/* current data/free buffer */
>>> +	xfs_dir2_db_t		curdb;		/* current data block number */
>>> +	xfs_dir2_data_entry_t	*dep;		/* data block entry */
>>> +	xfs_inode_t		*dp;		/* incore directory inode */
>>> +	int			error;		/* error return value */
>>> +	int			index;		/* leaf entry index */
>>> +	xfs_dir2_leaf_t		*leaf;		/* leaf structure */
>>> +	xfs_dir2_leaf_entry_t	*lep;		/* leaf entry */
>>> +	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;		/* comparison result */
>>> +	xfs_dabuf_t		*ci_bp = NULL;	/* buffer with CI match */
>>
>> Did you try to check the stack usage (scripts/checkstack.pl)?
>
> on x86_64:
>
> nameops.patch
>    -> no difference
>
> ascii_ci.patch
>    -> no difference
>
> refactor_leafn_lookup.patch (this one)
>    -> no difference
>
> return_name.patch
>    -> xfs_dir_lookup from 152 down to 144 :)
>
> unicode_ci.patch
>    -> xfs_mkdir from 152 down to 136 :)
>    -> new xfs_unicode_read_cft @ 120
>
> nls_support.patch
>    -> xfs_dir2_leaf_getdents from 136 up to 200 (ouch!)

BTW. The CONFIG_XFS_UNICODE_NLS patch, setting that to "N"
brings this back to 136 and no other changes.

>    -> xfs_mkdir from 136 back to 152!
>    -> xfs_create from 152 up to 168
>    -> xfs_rmdir from 104 down to < 100?
>
> This seems to be better than the stack usage Eric posted back in
> response to my last patch set.
>
>
>
>



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

* Re: [PATCH 3/7] XFS: Refactor node format directory lookup/addname
  2008-04-02  6:25 ` [PATCH 3/7] XFS: Refactor node format directory lookup/addname Barry Naujok
  2008-04-03  1:51   ` Josef 'Jeff' Sipek
@ 2008-04-03  4:33   ` David Chinner
  1 sibling, 0 replies; 48+ messages in thread
From: David Chinner @ 2008-04-03  4:33 UTC (permalink / raw)
  To: Barry Naujok; +Cc: xfs, linux-fsdevel

On Wed, Apr 02, 2008 at 04:25:11PM +1000, Barry Naujok wrote:
> The next step for case-insensitive support is to avoid polution of
> the dentry cache with entries pointing to the same inode, but with
> names that only differ in case.
> 
> To perform this, we will need to pass the actual filename that
> matched backup to the XFS/VFS interface and make sure the dentry
> cache only contains entries with the actual case-sensitive name.
> 
> But, before we can do this, it was found that the directory lookup
> code with multiple leaves was shared with code adding a name to
> that directory. Most of xfs_dir2_leafn_lookup_int() could be broken
> into two functions determined by if (args->addname) { } else { }.
> 
> For the following patch, only the lookup case needs to handle the
> various xfs_nameops, with case-insensitive match handling in
> addition to returning the actual name.
> 
> So, this patch separates xfs_dir2_leafn_lookup_int() into
> xfs_dir2_leafn_lookup_for_addname() and xfs_dir2_leafn_lookup_for_entry().
> 
> xfs_dir2_leafn_lookup_for_addname() iterates through the data blocks looking
> for a suitable empty space to insert the name while
> xfs_dir2_leafn_lookup_for_entry() uses the xfs_nameops to find the entry.
> 
> xfs_dir2_leafn_lookup_for_entry() path also retains the data block where
> the first case-insensitive match occured as in the next patch which will
> return the name, the name is obtained from that block.
> 
> Signed-off-by: Barry Naujok <bnaujok@sgi.com>
> 
> ---
>  fs/xfs/xfs_dir2_node.c |  373 +++++++++++++++++++++++++++++--------------------
>  1 file changed, 225 insertions(+), 148 deletions(-)
> 
> Index: kern_ci/fs/xfs/xfs_dir2_node.c
> ===================================================================
> --- kern_ci.orig/fs/xfs/xfs_dir2_node.c
> +++ kern_ci/fs/xfs/xfs_dir2_node.c
> @@ -387,12 +387,11 @@ xfs_dir2_leafn_lasthash(
>  }
>  
>  /*
> - * Look up a leaf entry in a node-format leaf block.
> - * If this is an addname then the extrablk in state is a freespace block,
> - * otherwise it's a data block.
> + * Look up a leaf entry for space to add a name in a node-format leaf block.
> + * The extrablk in state is a freespace block.
>   */
> -int
> -xfs_dir2_leafn_lookup_int(
> +static int

STATIC

(and for the other new function)

> +xfs_dir2_leafn_lookup_for_addname(
>  	xfs_dabuf_t		*bp,		/* leaf buffer */
>  	xfs_da_args_t		*args,		/* operation arguments */
>  	int			*indexp,	/* out: leaf entry index */
....

> @@ -1785,6 +1857,11 @@ xfs_dir2_node_lookup(
>  	if (error)
>  		rval = error;
>  	/*
> +	 * If case-insensitive match was found in a leaf, return EEXIST.
> +	 */
> +	else if (rval == ENOENT && args->cmpresult == XFS_CMP_CASE)
> +		rval = EEXIST;

Can you put the comment inside the if branch?

	if (error) {
		rval = error;
	} else if (rval == ENOENT && args->cmpresult == XFS_CMP_CASE) {
		/* found a case-insensitive match in a leaf */
		rval = EEXIST;
	}

I think Josef got the others...

Cheers,

Dave.
-- 
Dave Chinner
Principal Engineer
SGI Australian Software Group

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

* Re: [PATCH 1/7] XFS: Name operation vector for hash and compare
  2008-04-03  0:22   ` Josef 'Jeff' Sipek
@ 2008-04-03  4:50     ` Barry Naujok
  0 siblings, 0 replies; 48+ messages in thread
From: Barry Naujok @ 2008-04-03  4:50 UTC (permalink / raw)
  To: Josef 'Jeff' Sipek; +Cc: xfs, linux-fsdevel

On Thu, 03 Apr 2008 10:22:46 +1000, Josef 'Jeff' Sipek  
<jeffpc@josefsipek.net> wrote:

> On Wed, Apr 02, 2008 at 04:25:09PM +1000, Barry Naujok wrote:
> ...
>> +/*
>> + * Name ops for directory and/or attr name operations
>> + */
>> +
>> +typedef xfs_dahash_t	(*xfs_hashname_t)(const uchar_t *, int);
>> +typedef xfs_dacmp_t	(*xfs_compname_t)(const uchar_t *, int,
>> +					  const uchar_t *, int);
>
> Why have typedefs for function pointers? Sometimes, they even cause  
> problems
> (I remember Eric finding a nasty 64-bit bug related to a function pointer
> typedef).
>
> Since IRIX isn't on the supported OS list anymore, what's the policy with
> coding style within XFS?

Ok, I have fixed it:

+/*
+ * Name ops for directory and/or attr name operations
+ */
+struct xfs_nameops {
+	xfs_dahash_t (*hashname)(const uchar_t *, int);
+	xfs_dacmp_t  (*compname)(const uchar_t *, int, const uchar_t *, int);
+};

> ...
>> Index: kern_ci/fs/xfs/xfs_dir2.h
>> ===================================================================
>> --- kern_ci.orig/fs/xfs/xfs_dir2.h
>> +++ kern_ci/fs/xfs/xfs_dir2.h
>> @@ -85,6 +85,12 @@ 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((n), (l)))
>> +
>> +#define xfs_dir_compname(dp, n1, l1, n2, l2) \
>> +		((dp)->i_mount->m_dirnameops->compname((n1), (l1), (n2), (l2)))
>
> #define vs. static inline...
>
> I guess this comes back to my question before...what is the coding style
> direction you want XFS to go in? More Linux-like (static inline)? or  
> keep it
> more IRIX-like (#define)?

Nasty gotcha in this scenario, I have added a comment before them:

+/*
+ * Macros are used calling for the xfs_inode's xfs_mount's name  
operations as
+ * in most cases, xfs_dir2.h is included before xfs_inode.h and  
xfs_mount.h.
+ */
+#define xfs_dir_hashname(dp, n, l) \
+		((dp)->i_mount->m_dirnameops->hashname((n), (l)))
+
+#define xfs_dir_compname(dp, n1, l1, n2, l2) \
+		((dp)->i_mount->m_dirnameops->compname((n1), (l1), (n2), (l2)))

The alternative is reorganising the #includes in most of the .c files!



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

* Re: [PATCH 4/7] XFS: Return case-insensitive match for dentry cache
  2008-04-02  6:25 ` [PATCH 4/7] XFS: Return case-insensitive match for dentry cache Barry Naujok
  2008-04-03  2:34   ` Josef 'Jeff' Sipek
@ 2008-04-03  5:22   ` David Chinner
  2008-04-03  5:41     ` Stephen Rothwell
  2008-04-03 23:06   ` Christoph Hellwig
  2 siblings, 1 reply; 48+ messages in thread
From: David Chinner @ 2008-04-03  5:22 UTC (permalink / raw)
  To: Barry Naujok; +Cc: xfs, linux-fsdevel

On Wed, Apr 02, 2008 at 04:25:12PM +1000, Barry Naujok wrote:
> This implements the code to store the actual filename found
> during a lookup in the dentry cache and to avoid multiple entries
> in the dcache pointing to the same inode.
> 
> It also introduces a new type, xfs_name, which is similar to the
> dentry cache's qstr type. It contains a pointer to a zone allocated
> string (MAXNAMELEN sized) and the length of the actual name. This
> string does not need to be NULL terminated (a counted string).
> 
> xfs_name_t is only used in the lookup path for this patch, but may
> be used in other locations too if desired. It maybe desirable not
> to use xfs_name_t at all in the lookup functions but stick to
> separate parameters (which will mean 7 instead of 5 arguments).
> 
> To avoid polluting the dcache, we implement a new directory inode
> operations for lookup. xfs_vn_ci_lookup() interacts directly with
> the dcache and the code was derived from ntfs_lookup() in
> fs/ntfs/namei.c. The dentry hash and compare overrides introduced
> in the ASCII-CI patch has been removed.
> 
> The "actual name" is only allocated and returned for a case-
> insensitive match and not an actual match.

> +STATIC struct dentry *
> +xfs_vn_ci_lookup(
> +	struct inode	*dir,
> +	struct dentry	*dentry,
> +	struct nameidata *nd)
> +{
> +	struct xfs_inode *cip;
> +	int		error;
>  	struct dentry	*result;
> +	struct qstr	ci_name = {0, 0, NULL};
> +	struct inode	*inode;
>  
>  	if (dentry->d_name.len >= MAXNAMELEN)
>  		return ERR_PTR(-ENAMETOOLONG);
>  
> -	if (xfs_sb_version_hasoldci(&mp->m_sb))
> -		dentry->d_op = &xfs_ci_dentry_operations;
> +	error = xfs_lookup(XFS_I(dir), &dentry->d_name, &cip, &ci_name);

Bit confusing with cip = "child inode" and ci_name = "case insensitive".
i.e. same prefix, different meanings...

>  
> -	error = xfs_lookup(XFS_I(dir), dentry, &cip);
>  	if (unlikely(error)) {
>  		if (unlikely(error != ENOENT))
>  			return ERR_PTR(-error);
>  		d_add(dentry, NULL);
>  		return NULL;
>  	}
> +	inode = cip->i_vnode;
> +
> +	/* if exact match, just splice and exit */
> +	if (!ci_name.name) {
> +		result = d_splice_alias(inode, dentry);
> +		return result;
> +	}

	if (!ci_name.name)
		return d_splice_alias(inode, dentry);

>  
> -	result = d_splice_alias(cip->i_vnode, dentry);
> -	if (result)
> -		result->d_op = dentry->d_op;
> -	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
> +	 */
> +
> +	ci_name.hash = full_name_hash(ci_name.name, ci_name.len);
> +
> +	/* Does an existing dentry match? */
> +	result = d_lookup(dentry->d_parent, &ci_name);
> +	if (!result) {
> +		/* if not, create one */
> +		result = d_alloc(dentry->d_parent, &ci_name);
> +		xfs_da_name_free((char *)ci_name.name);
> +		if (!result)
> +			return ERR_PTR(-ENOMEM);
> +		dentry = d_splice_alias(inode, result);
> +		if (dentry) {
> +			dput(result);
> +			return dentry;
> +		}
> +		return result;
> +	}

This looks like it came from the ntfs code - i find that much easier
to follow with "real_dent" and "new_dent" instead of "result" and "dentry"
respectively.

> +	xfs_da_name_free((char *)ci_name.name);
> +
> +	/* an existing dentry matches, use it */

Ah, I see the rest of this is basically a copy and paste of the ntfs code
(without some of the useful comments). I think a generic helper function
is in order here that contains all the coments from the ntfs code....

> +
> +	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));

Bit drastic - how about failing the lookup and returning EIO in this case?

> +		}
> +		iput(inode);
> +		return result;
> +	}

.....
> Index: kern_ci/fs/xfs/linux-2.6/xfs_super.c
> ===================================================================
> --- kern_ci.orig/fs/xfs/linux-2.6/xfs_super.c
> +++ kern_ci/fs/xfs/linux-2.6/xfs_super.c
> @@ -566,7 +566,10 @@ 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_sb_version_hasoldci(&XFS_M(inode->i_sb)->m_sb) ?
	
> +	xfs_ino_t	*inum,		/* out: inode number */
> +	xfs_name_t	*ci_name)	/* out: actual name if different */
>  {
>  	xfs_da_args_t	args;
>  	int		rval;
> @@ -259,9 +260,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_dir_hashname(dp, name, namelen);
> +	args.name = name->name;
> +	args.namelen = name->len;
> +	args.hashval = xfs_dir_hashname(dp, name->name, name->len);
>  	args.inumber = 0;
>  	args.dp = dp;
>  	args.firstblock = NULL;
> @@ -272,6 +273,8 @@ xfs_dir_lookup(
>  	args.justcheck = args.addname = 0;
>  	args.oknoent = 1;
>  	args.cmpresult = XFS_CMP_DIFFERENT;
> +	args.value = NULL;
> +	args.valuelen = 0;

Rather than initialising more of the args to zero (already 7 members
explicitly initialised to zero or NULL), change it to:

	memset(&args, 0, sizeof(xfs_da_args_t));
	args.name = name->name;
	args.namelen = name->len;
	args.hashval = xfs_dir_hashname(dp, name->name, name->len);
	args.dp = dp;
	args.whichfork = XFS_DATA_FORK;
	args.trans = tp;
	args.oknoent = 1;
	args.cmpresult = XFS_CMP_DIFFERENT;

>  
>  	if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL)
>  		rval = xfs_dir2_sf_lookup(&args);
> @@ -287,8 +290,17 @@ xfs_dir_lookup(
>  		rval = xfs_dir2_node_lookup(&args);
>  	if (rval == EEXIST)
>  		rval = 0;
> -	if (rval == 0)
> +	if (rval == 0) {

	if (!rval) {
> Index: kern_ci/fs/xfs/xfs_dir2_block.c
> ===================================================================
> --- kern_ci.orig/fs/xfs/xfs_dir2_block.c
> +++ kern_ci/fs/xfs/xfs_dir2_block.c
> @@ -616,6 +616,15 @@ xfs_dir2_block_lookup(
>  	 * Fill in inode number, release the block.
>  	 */
>  	args->inumber = be64_to_cpu(dep->inumber);
> +	/*
> +	 * If a case-insensitive match, allocate a buffer and copy the actual
> +	 * name into the buffer. Return it via args->value.
> +	 */
> +	if (args->cmpresult == XFS_CMP_CASE) {
> +		args->value = xfs_da_name_alloc();
> +		memcpy(args->value, dep->name, dep->namelen);
> +		args->valuelen = dep->namelen;

		xfs_da_ci_name_dup();
> +	}
>  	xfs_da_brelse(args->trans, bp);
>  	return XFS_ERROR(EEXIST);
>  }
> Index: kern_ci/fs/xfs/xfs_dir2_leaf.c
> ===================================================================
> --- kern_ci.orig/fs/xfs/xfs_dir2_leaf.c
> +++ kern_ci/fs/xfs/xfs_dir2_leaf.c
> @@ -1301,6 +1301,15 @@ xfs_dir2_leaf_lookup(
>  	 * Return the found inode number.
>  	 */
>  	args->inumber = be64_to_cpu(dep->inumber);
> +	/*
> +	 * If a case-insensitive match, allocate a buffer and copy the actual
> +	 * name into the buffer. Return it via args->value.
> +	 */
> +	if (args->cmpresult == XFS_CMP_CASE) {
> +		args->value = xfs_da_name_alloc();
> +		memcpy(args->value, dep->name, dep->namelen);
> +		args->valuelen = dep->namelen;

		xfs_da_ci_name_dup();
> +	}
>  	xfs_da_brelse(tp, dbp);
>  	xfs_da_brelse(tp, lbp);
>  	return XFS_ERROR(EEXIST);
> Index: kern_ci/fs/xfs/xfs_dir2_node.c
> ===================================================================
> --- kern_ci.orig/fs/xfs/xfs_dir2_node.c
> +++ kern_ci/fs/xfs/xfs_dir2_node.c
> @@ -643,6 +643,8 @@ xfs_dir2_leafn_lookup_for_entry(
>  			xfs_dir2_dataptr_to_off(mp, be32_to_cpu(lep->address)));
>  		/*
>  		 * Compare the entry, return it if it matches.
> +		 * "oknoent" is set for lookup and clear for
> +		 * remove and replace.
>  		 */

That should have been in an earlier patch....

>  		cmp = args->oknoent ?
>  			xfs_dir_compname(dp, dep->name, dep->namelen,
> @@ -1857,10 +1859,22 @@ xfs_dir2_node_lookup(
>  	if (error)
>  		rval = error;
>  	/*
> -	 * If case-insensitive match was found in a leaf, return EEXIST.
> -	 */
> -	else if (rval == ENOENT && args->cmpresult == XFS_CMP_CASE)
> +	 * If case-insensitive match was found (xfs_dir2_leafn_lookup_int
> +	 * returns ENOENT for a case-insensitive match, but sets
> +	 * args->cmpresult to XFS_CMP_CASE):
> +	 *   - Allocate a buffer and copy the actual name into the buffer and
> +	 *       return it via args->value.
> +	 *   - set rval to EEXIST
> +	 */
> +	else if (rval == ENOENT && 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->value = xfs_da_name_alloc();
> +		memcpy(args->value, dep->name, dep->namelen);
> +		args->valuelen = dep->namelen;
>  		rval = EEXIST;
> +	}

Yeah, more reason to move the comment inside the if block....

oh, and xfs_da_ci_name_dup()....

> -	if (args->cmpresult == XFS_CMP_CASE)
> +	if (args->cmpresult == XFS_CMP_CASE) {
> +		/*
> +		 * If a case-insensitive match, allocate a buffer and copy the
> +		 * actual name into the buffer and return it via args->value.
> +		 */
> +		args->value = xfs_da_name_alloc();
> +		memcpy(args->value, ci_sfep->name, ci_sfep->namelen);
> +		args->valuelen = ci_sfep->namelen;

		xfs_da_ci_name_dup()

> --- kern_ci.orig/fs/xfs/xfs_utils.c
> +++ kern_ci/fs/xfs/xfs_utils.c
> @@ -24,6 +24,7 @@
>  #include "xfs_trans.h"
>  #include "xfs_sb.h"
>  #include "xfs_ag.h"
> +#include "xfs_da_btree.h"

What's that needed for? What ever it is, i think you've put it
in the wrong header file....

Cheers,

Dave.
-- 
Dave Chinner
Principal Engineer
SGI Australian Software Group

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

* Re: [PATCH 4/7] XFS: Return case-insensitive match for dentry cache
  2008-04-03  5:22   ` David Chinner
@ 2008-04-03  5:41     ` Stephen Rothwell
  2008-04-03 14:56       ` Christoph Hellwig
  0 siblings, 1 reply; 48+ messages in thread
From: Stephen Rothwell @ 2008-04-03  5:41 UTC (permalink / raw)
  To: David Chinner; +Cc: Barry Naujok, xfs, linux-fsdevel

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

Hi all,

On Thu, 3 Apr 2008 15:22:09 +1000 David Chinner <dgc@sgi.com> wrote:
>
> On Wed, Apr 02, 2008 at 04:25:12PM +1000, Barry Naujok wrote:
> > This implements the code to store the actual filename found
> > during a lookup in the dentry cache and to avoid multiple entries
> > in the dcache pointing to the same inode.

I haven't really been following this, but I was wondering if this could
be made generic and used in the CIFS code as well.  They currently (I
think) have an awefull hack where they update the name in the dentry
(which throws a warning about dropping a const attribute in the memcpy).

-- 
Cheers,
Stephen Rothwell                    sfr@canb.auug.org.au
http://www.canb.auug.org.au/~sfr/

[-- Attachment #2: Type: application/pgp-signature, Size: 189 bytes --]

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

* Re: [PATCH 5/7] XFS: Unicode case-insensitive lookup implementation
  2008-04-02  6:25 ` [PATCH 5/7] XFS: Unicode case-insensitive lookup implementation Barry Naujok
@ 2008-04-03  8:31   ` David Chinner
  2008-04-17  5:38     ` Barry Naujok
  2008-04-03 17:14   ` Christoph Hellwig
  1 sibling, 1 reply; 48+ messages in thread
From: David Chinner @ 2008-04-03  8:31 UTC (permalink / raw)
  To: Barry Naujok; +Cc: xfs, linux-fsdevel

On Wed, Apr 02, 2008 at 04:25:13PM +1000, Barry Naujok wrote:
> This is the core of the case-insensitive support - supporting and
> enforcing UTF-8 (Unicode) filenames. All filename and user-level
> extended attribute names are checked for UTF-8 compliance and the
> hashes generated are always case-insensitive by utilising the
> Unicode 5.0 standard case-folding table from:
> http://www.unicode.org/Public/UNIDATA/CaseFolding.txt
> 
> As the hash is always case-insensitive, this allows the user to
> mkfs.xfs the filesystem once and enable or disable (default)
> case-insensitive support by a mount option "-o ci". The mount
> option specifies which xfs_nameops.compname function to use.
> 
> Also, the Unicode support is a CONFIG option so users who do
> not required this functionality can CONFIG it to N.
> 
> As the case-folding table is stored on disk, this allows
> backwards and forwards compatibility and languages like Turkic
> to support true case-insensitivity with I and i.
> 
> To create a Unicode filesystem with case-insensitive mount
> support, run:
> # mkfs.xfs -n utf8[=default|turkic] <device>
> 
> The final patches implement NLS support for XFS Unicode.
> 
> Signed-off-by: Barry Naujok <bnaujok@sgi.com>
.....

> +config XFS_UNICODE
> +	bool "XFS Unicode support"
> +	depends on XFS_FS
> +	help
> +	  Unicode support enforces UTF-8 filenames and user extended

Not sure that "enforces" is the right word. Perhaps 

	  Unicode support encodes filenames and user extended attribute
	  names in UTF-8 format on disk....

> +	  attribute names. This option is required for filesystems
> +	  mkfs'ed with UTF-8 support. A Unicode filesystem guarantees
> +	  that filenames will be the same regardless of the user's
> +	  locale. For UTF-8 locales, no conversion is required.
> +
> +	  Unicode filesystems also allow the filesystem to be mounted with
> +	  case-insensitive lookup support with the "-o ci" mount option.
> +
> +	  If you don't require UTF-8 enforcement, say N.
                                     ^^^^^^^^^^^ support

> Index: kern_ci/fs/xfs/linux-2.6/xfs_super.c
> ===================================================================
> --- kern_ci.orig/fs/xfs/linux-2.6/xfs_super.c
> +++ kern_ci/fs/xfs/linux-2.6/xfs_super.c
> @@ -46,6 +46,7 @@
>  #include "xfs_acl.h"
>  #include "xfs_attr.h"
>  #include "xfs_buf_item.h"
> +#include "xfs_unicode.h"
>  #include "xfs_utils.h"
>  #include "xfs_vnodeops.h"
>  #include "xfs_vfsops.h"
> @@ -124,6 +125,7 @@ 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_CILOOKUP	"ci"		/* case-insensitive dir lookup */
>  #define MNTOPT_QUOTA	"quota"		/* disk quotas (user) */
>  #define MNTOPT_NOQUOTA	"noquota"	/* no quotas */
>  #define MNTOPT_USRQUOTA	"usrquota"	/* user quota enabled */
> @@ -318,6 +320,14 @@ xfs_parseargs(
>  			args->flags &= ~XFSMNT_ATTR2;
>  		} else if (!strcmp(this_char, MNTOPT_FILESTREAM)) {
>  			args->flags2 |= XFSMNT2_FILESTREAMS;
> +		} else if (!strcmp(this_char, MNTOPT_CILOOKUP)) {
> +			args->flags2 |= XFSMNT2_CILOOKUP;
> +#ifndef CONFIG_XFS_UNICODE
> +			cmn_err(CE_WARN,
> +				"XFS: %s option requires Unicode support",
> +				this_char);
> +			return EINVAL;
> +#endif

Hmmmm - I don't like having CONFIG_XFS_... stuff in here. Use
a function that is only compiled in when CONFIG_XFS_UNICODE is not
defined for this....

The other way of doing this is to accept the mount option here
and then once we've read the superblock we can abort the mount
if the unicode feature is not present in the superblock. Of course,
if we don't compile in unicode support, that superblock check will
fail....


> @@ -567,7 +578,8 @@ xfs_set_inodeops(
>  		break;
>  	case S_IFDIR:
>  		inode->i_op =
> -			xfs_sb_version_hasoldci(&XFS_I(inode)->i_mount->m_sb) ?
> +			xfs_sb_version_hasoldci(&XFS_I(inode)->i_mount->m_sb) ||
> +			(XFS_I(inode)->i_mount->m_flags & XFS_MOUNT_CILOOKUP) ?
>  				&xfs_dir_ci_inode_operations :
>  				&xfs_dir_inode_operations;

Please turn that into a regular if..else 

Also, from the last patch, use XFS_M(inode->i_sb) instead of
XFS_I(inode)->i_mount....


> @@ -175,6 +176,13 @@ xfs_attr_get(
>  	if (namelen >= MAXNAMELEN)
>  		return(EFAULT);		/* match IRIX behaviour */
>  
> +	/* Enforce UTF-8 only for user attr names */
> +	if (xfs_sb_version_hasunicode(&ip->i_mount->m_sb) &&
> +			(flags & (ATTR_ROOT | ATTR_SECURE)) == 0) {
> +		error = xfs_unicode_validate(name, namelen);
> +		if (error)
> +			return error;
> +	}

I'd wrap this up completely into a helper, so the code here does
not need comments and becomes:

	error = xfs_attr_unicode_validate(ip, flags, name, namelen);
	if (error)
		return error;

>  	if (XFS_FORCED_SHUTDOWN(ip->i_mount))
>  		return(EIO);
>  
> @@ -435,6 +443,14 @@ xfs_attr_set(
>  	if (namelen >= MAXNAMELEN)
>  		return EFAULT;		/* match IRIX behaviour */
>  
> +	/* Enforce UTF-8 only for user attr names */
> +	if (xfs_sb_version_hasunicode(&dp->i_mount->m_sb) &&
> +			(flags & (ATTR_ROOT | ATTR_SECURE)) == 0) {
> +		int error = xfs_unicode_validate(name, namelen);
> +		if (error)
> +			return error;
> +	}

ditto.

> +
>  	XFS_STATS_INC(xs_attr_set);
>  
>  	if (XFS_FORCED_SHUTDOWN(dp->i_mount))
> @@ -581,6 +597,14 @@ xfs_attr_remove(
>  	if (namelen >= MAXNAMELEN)
>  		return EFAULT;		/* match IRIX behaviour */
>  
> +	/* Enforce UTF-8 only for user attr names */
> +	if (xfs_sb_version_hasunicode(&dp->i_mount->m_sb) &&
> +			(flags & (ATTR_ROOT | ATTR_SECURE)) == 0) {
> +		int error = xfs_unicode_validate(name, namelen);
> +		if (error)
> +			return error;
> +	}

ditto.

> @@ -52,6 +52,7 @@
>   */
>  static xfs_dahash_t
>  xfs_ascii_ci_hashname(
> +	xfs_inode_t	*inode,
>  	const uchar_t	*name,
>  	int		namelen)
>  {
> @@ -66,6 +67,7 @@ xfs_ascii_ci_hashname(
>  
>  static xfs_dacmp_t
>  xfs_ascii_ci_compname(
> +	xfs_inode_t	*inode,
>  	const uchar_t	*name1,
>  	int		len1,
>  	const uchar_t	*name2,
> @@ -113,8 +115,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;
> -	mp->m_dirnameops = xfs_sb_version_hasoldci(&mp->m_sb) ?
> -		&xfs_ascii_ci_nameops : &xfs_default_nameops;
> +
> +	if (xfs_sb_version_hasunicode(&mp->m_sb)) {
> +		mp->m_dirnameops = (mp->m_flags & XFS_MOUNT_CILOOKUP) ?
> +			&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;

Can I suggest that if you find xfs_sb_version_hasoldci() at mount,
then we should set mp->m_flags |= XFS_MOUNT_CILOOKUP?

That way we can use a single method of looking up whether CI is active,
and old vs new canbe determined by xfs_sb_version_hasunicode()....

> @@ -241,6 +241,7 @@ typedef struct xfs_fsop_resblks {
>  #define XFS_FSOP_GEOM_FLAGS_ATTR2	0x0400	/* inline attributes rework */
>  #define XFS_FSOP_GEOM_FLAGS_DIRV2CI	0x1000	/* ASCII only CI names */
>  #define XFS_FSOP_GEOM_FLAGS_LAZYSB	0x4000	/* lazy superblock counters */
> +#define XFS_FSOP_GEOM_FLAGS_UNICODE	0x10000	/* unicode filenames */

Can you reformat the list so that eveything lines up correctly
and has leading zeros for the full 32 bit flag space? like:

#define XFS_FSOP_GEOM_FLAGS_ATTR2	0x00000400 /* inline attributes rework */
#define XFS_FSOP_GEOM_FLAGS_DIRV2CI	0x00001000 /* ASCII only CI names */
#define XFS_FSOP_GEOM_FLAGS_LAZYSB	0x00004000 /* lazy superblock counters */
#define XFS_FSOP_GEOM_FLAGS_UNICODE	0x00010000 /* unicode filenames */

>  #include "xfs_utils.h"
> +#include "xfs_unicode.h"
>  
>  STATIC void	xfs_mount_log_sb(xfs_mount_t *, __int64_t);
>  STATIC int	xfs_uuid_mount(xfs_mount_t *);
> @@ -121,6 +122,7 @@ static const struct {
>      { 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 }

whitespace.
>  	if (update_flags && !(mp->m_flags & XFS_MOUNT_RDONLY))
> @@ -1229,7 +1245,8 @@ xfs_mountfs(
>  	 * Free up the root inode.
>  	 */
>  	IRELE(rip);
> - error3:
> +	xfs_unicode_free_cft(mp->m_cft);
> +error3:
>  	xfs_log_unmount_dealloc(mp);
>   error2:
>  	for (agno = 0; agno < sbp->sb_agcount; agno++)

Please line up all the other labels while you are there ;)

> --- kern_ci.orig/fs/xfs/xfs_sb.h
> +++ kern_ci/fs/xfs/xfs_sb.h
> @@ -79,10 +79,18 @@ 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	\
> +#ifdef CONFIG_XFS_UNICODE
> +# define XFS_SB_VERSION2_OKREALFBITS	\
>  	(XFS_SB_VERSION2_LAZYSBCOUNTBIT	| \
> +	 XFS_SB_VERSION2_UNICODEBIT | \
>  	 XFS_SB_VERSION2_ATTR2BIT)
> +#else
> +# define XFS_SB_VERSION2_OKREALFBITS	\
> +	(XFS_SB_VERSION2_LAZYSBCOUNTBIT	| \
> +	 XFS_SB_VERSION2_ATTR2BIT)
> +#endif

Regardless of whether CONFIG_XFS_UNICODE is defined or not, we should
be defining this as a valid bit. What we want is xfs_sb_version_hasunicode()
to say "not supported" when CONFIG_XFS_UNICODE is not defined.

IOWs, if the sb_cftino field is defined in the superblock,
XFS_SB_VERSION2_UNICODEBIT must be defined as well.

> +#ifdef CONFIG_XFS_UNICODE
> +static inline int xfs_sb_version_hasunicode(xfs_sb_t *sbp)
> +{
> +	return (xfs_sb_version_hasmorebits(sbp) &&	\
> +		((sbp)->sb_features2 & XFS_SB_VERSION2_UNICODEBIT));
> +}
> +#else
> +# define xfs_sb_version_hasunicode(sbp)	(0)

static inline int xfs_sb_version_hasunicode(xfs_sb_t *sbp) { return 0; }

> Index: kern_ci/fs/xfs/xfs_unicode.c
> ===================================================================
> --- /dev/null
> +++ kern_ci/fs/xfs/xfs_unicode.c
> @@ -0,0 +1,499 @@
....
> +#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

Why is this set to 4?

> +
> +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) {

lots of magic numbers here. Can you either use #defines that
make a little sense, or wrap it in a function that describes
the comparison?

Can we get a comment explaining how the case folding works?
i.e. the table layout, the transforms needed to find the
entry that matches, what the magic upper and lower bits are, etc.
This code by itself is not understandable or reviewable....

> +		*fc = tmp;
> +		return 1;
> +	}
> +	i = ((tmp >> 10) & 0x3) + 2;
> +	ASSERT(i < cft->num_tables);
> +	table = XFS_CFT_PTR(cft, i - 1) + ((tmp & 0x3ff) * i);

and it only gets worse :/

> +
> +	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);
> +}

Comments, please.

> +/*
> + * Perform a case-folding case-insensitive string comparison,
> + * returns either XFS_CMP_CASE or XFS_CMP_DIFFERENT.
> + */
> +static xfs_dacmp_t
> +xfs_unicode_casecmp(
> +	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;

Please put the "p" for pointer at the end of the variable.

> +/*
> + * 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)
> +{
....
> +
> +	cft_list = kmem_realloc(cft_list,
> +			(cft_size + 1) * sizeof(struct cft_item),
> +			cft_size  * sizeof(struct cft_item), KM_SLEEP);

KM_MAYFAIL and handle the allocation error.

> +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;
> +		}
> +	}

What happens when the refcount falls to zero?

We just leave it there consuming memory?

> +
> +	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;

ENOENT?

> +	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);

I think what you want here is the number of extents, not the number
of blocks for this. How big are these tables?

> +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));

Ah, we free unused tables on module unload. This won't ever occur
in many configurations (e.g. XFS on root fs). Perhaps it's better to
free them whenthe refcount falls to zero (otherwise what's the point
of refcounting them?)...

> +#ifdef CONFIG_XFS_UNICODE
> +
> +extern struct xfs_nameops xfs_unicode_nameops;
> +extern struct xfs_nameops xfs_unicode_ci_nameops;
> +
> +void xfs_unicode_init(void);
> +void xfs_unicode_uninit(void);
> +
> +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);
> +
> +#else
> +
> +#define xfs_unicode_nameops		xfs_default_nameops
> +#define xfs_unicode_ci_nameops		xfs_default_nameops
> +
> +#define xfs_unicode_init()
> +#define xfs_unicode_uninit()
> +#define xfs_unicode_validate(n,l)	0
> +#define xfs_unicode_read_cft(mp)	(EOPNOTSUPP)
> +#define xfs_unicode_free_cft(cft)

Same as before - static inlines where possible...

> @@ -399,6 +402,19 @@ 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_CILOOKUP;
> +	} else {
> +		/*
> +		 * Check for mount options which require a Unicode FS
> +		 */
> +		if (ap->flags2 & XFSMNT2_CILOOKUP) {
> +			cmn_err(CE_WARN,
> +	"XFS: can't do case-insensitive mount on non-utf8 filesystem");

Except if it has oldci defined.... ;)

> @@ -1777,6 +1778,12 @@ xfs_lookup(
>  	if (XFS_FORCED_SHUTDOWN(dp->i_mount))
>  		return XFS_ERROR(EIO);
>  
> +	if (xfs_sb_version_hasunicode(&dp->i_mount->m_sb)) {
> +		error = xfs_unicode_validate(d_name->name, d_name->len);
> +		if (error)
> +			return error;
> +	}

Seen that somewhere before - a wrapper perhaps?

/me snips the next 6 almost identical code fragments. I think a wrapper
that has the sb check inside it is the way to go here...

Cheers,

Dave.
-- 
Dave Chinner
Principal Engineer
SGI Australian Software Group

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

* Re: [PATCH 4/7] XFS: Return case-insensitive match for dentry cache
  2008-04-03  5:41     ` Stephen Rothwell
@ 2008-04-03 14:56       ` Christoph Hellwig
  0 siblings, 0 replies; 48+ messages in thread
From: Christoph Hellwig @ 2008-04-03 14:56 UTC (permalink / raw)
  To: Stephen Rothwell; +Cc: David Chinner, Barry Naujok, xfs, linux-fsdevel

On Thu, Apr 03, 2008 at 04:41:20PM +1100, Stephen Rothwell wrote:
> I haven't really been following this, but I was wondering if this could
> be made generic and used in the CIFS code as well.  They currently (I
> think) have an awefull hack where they update the name in the dentry
> (which throws a warning about dropping a const attribute in the memcpy).

yes, it should.  the new lookup higher level code added here should
probably be a helper in dcache.c although that'll need a new abstraction
for the unicode table handling.


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

* Re: [PATCH 2/7] XFS: ASCII case-insensitive support
  2008-04-03  1:53   ` David Chinner
@ 2008-04-03 17:09     ` Christoph Hellwig
  0 siblings, 0 replies; 48+ messages in thread
From: Christoph Hellwig @ 2008-04-03 17:09 UTC (permalink / raw)
  To: David Chinner; +Cc: Barry Naujok, xfs, linux-fsdevel

On Thu, Apr 03, 2008 at 11:53:31AM +1000, David Chinner wrote:
> > +	if (xfs_sb_version_hasoldci(&mp->m_sb))
> > +		sb->s_root->d_op = &xfs_ci_dentry_operations;
> 
> Write a helper function for this: xfs_set_ci_dentry_ops(mp, dentry)
> rather than exporting the xfs_ci_dentry_operations structure.

yes, please.  also the export ops calling d_alloc_anon need to update
the dentry ops aswel and should be using this one.

> > +#define XFS_SB_VERSION_OLDCIBIT		0x4000	/* ASCII only case-insens. */
> >  #define	XFS_SB_VERSION_MOREBITSBIT	0x8000
> >  #define	XFS_SB_VERSION_OKSASHFBITS	\
> 
> Whitespace. 
> 
> But it's a shame you're being sensible about this - I kinda liked
> the Irix name for this feature (XFS_SB_VERSION_BORGBIT). :)

So what exactly prevents us from using it in Linux?  please use the
old name.

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

* Re: [PATCH 5/7] XFS: Unicode case-insensitive lookup implementation
  2008-04-02  6:25 ` [PATCH 5/7] XFS: Unicode case-insensitive lookup implementation Barry Naujok
  2008-04-03  8:31   ` David Chinner
@ 2008-04-03 17:14   ` Christoph Hellwig
  2008-04-03 17:24     ` Jeremy Allison
  2008-04-03 22:20     ` David Chinner
  1 sibling, 2 replies; 48+ messages in thread
From: Christoph Hellwig @ 2008-04-03 17:14 UTC (permalink / raw)
  To: Barry Naujok; +Cc: xfs, linux-fsdevel

Validating file names is not the filesystem job.  In fact it's utterly
stupid, a unix filename is a sequence of bytes without special meaning
except for ., .., / and \0


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

* Re: [PATCH 5/7] XFS: Unicode case-insensitive lookup implementation
  2008-04-03 17:14   ` Christoph Hellwig
@ 2008-04-03 17:24     ` Jeremy Allison
  2008-04-03 18:09       ` Josef 'Jeff' Sipek
                         ` (2 more replies)
  2008-04-03 22:20     ` David Chinner
  1 sibling, 3 replies; 48+ messages in thread
From: Jeremy Allison @ 2008-04-03 17:24 UTC (permalink / raw)
  To: Christoph Hellwig; +Cc: Barry Naujok, xfs, linux-fsdevel

On Thu, Apr 03, 2008 at 01:14:50PM -0400, Christoph Hellwig wrote:
> Validating file names is not the filesystem job.  In fact it's utterly
> stupid, a unix filename is a sequence of bytes without special meaning
> except for ., .., / and \0

This patch will be extremely useful for users who are serving
Windows clients using Samba. It allow admins to turn off the
userspace case insensitivity we have to emulate and be a significant
speed increase.

Jeremy.

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

* Re: [PATCH 5/7] XFS: Unicode case-insensitive lookup implementation
  2008-04-03 17:24     ` Jeremy Allison
@ 2008-04-03 18:09       ` Josef 'Jeff' Sipek
  2008-04-03 18:11       ` Eric Sandeen
  2008-04-03 18:43       ` Christoph Hellwig
  2 siblings, 0 replies; 48+ messages in thread
From: Josef 'Jeff' Sipek @ 2008-04-03 18:09 UTC (permalink / raw)
  To: Jeremy Allison; +Cc: Christoph Hellwig, Barry Naujok, xfs, linux-fsdevel

On Thu, Apr 03, 2008 at 10:24:00AM -0700, Jeremy Allison wrote:
> On Thu, Apr 03, 2008 at 01:14:50PM -0400, Christoph Hellwig wrote:
> > Validating file names is not the filesystem job.  In fact it's utterly
> > stupid, a unix filename is a sequence of bytes without special meaning
> > except for ., .., / and \0
> 
> This patch will be extremely useful for users who are serving
> Windows clients using Samba. It allow admins to turn off the
> userspace case insensitivity we have to emulate and be a significant
> speed increase.

IIRC, the last version of this patch series didn't help on the performance
scale.  Any updates as to whether that got fixed up?

Josef 'Jeff' Sipek.

-- 
In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like
that.
		- Linus Torvalds

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

* Re: [PATCH 5/7] XFS: Unicode case-insensitive lookup implementation
  2008-04-03 17:24     ` Jeremy Allison
  2008-04-03 18:09       ` Josef 'Jeff' Sipek
@ 2008-04-03 18:11       ` Eric Sandeen
  2008-04-03 18:22         ` Jeremy Allison
  2008-04-04  0:00         ` Mark Goodwin
  2008-04-03 18:43       ` Christoph Hellwig
  2 siblings, 2 replies; 48+ messages in thread
From: Eric Sandeen @ 2008-04-03 18:11 UTC (permalink / raw)
  To: Jeremy Allison; +Cc: Christoph Hellwig, Barry Naujok, xfs, linux-fsdevel

Jeremy Allison wrote:
> On Thu, Apr 03, 2008 at 01:14:50PM -0400, Christoph Hellwig wrote:
>> Validating file names is not the filesystem job.  In fact it's utterly
>> stupid, a unix filename is a sequence of bytes without special meaning
>> except for ., .., / and \0
> 
> This patch will be extremely useful for users who are serving
> Windows clients using Samba. It allow admins to turn off the
> userspace case insensitivity we have to emulate and be a significant
> speed increase.

I'd like to see the numbers... Simo tested an earlier version of this
patch, and it was not faster.... Jeremy, what would be a representative
test setup to use?

Thanks,
-Eric

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

* Re: [PATCH 5/7] XFS: Unicode case-insensitive lookup implementation
  2008-04-03 18:11       ` Eric Sandeen
@ 2008-04-03 18:22         ` Jeremy Allison
  2008-04-04  0:00         ` Mark Goodwin
  1 sibling, 0 replies; 48+ messages in thread
From: Jeremy Allison @ 2008-04-03 18:22 UTC (permalink / raw)
  To: Eric Sandeen
  Cc: Jeremy Allison, Christoph Hellwig, Barry Naujok, xfs,
	linux-fsdevel

On Thu, Apr 03, 2008 at 01:11:42PM -0500, Eric Sandeen wrote:
> Jeremy Allison wrote:
> > On Thu, Apr 03, 2008 at 01:14:50PM -0400, Christoph Hellwig wrote:
> >> Validating file names is not the filesystem job.  In fact it's utterly
> >> stupid, a unix filename is a sequence of bytes without special meaning
> >> except for ., .., / and \0
> > 
> > This patch will be extremely useful for users who are serving
> > Windows clients using Samba. It allow admins to turn off the
> > userspace case insensitivity we have to emulate and be a significant
> > speed increase.
> 
> I'd like to see the numbers... Simo tested an earlier version of this
> patch, and it was not faster.... Jeremy, what would be a representative
> test setup to use?

It very much depends on the usage case. We have many users who have
large numbers of files per directory, and not having to search these
in userspace when we get a stat cache miss is helpful. Just running a
generic "netbench" test won't show any difference, as that test
uses separate directories for each client with small numbers of
files per directory.

There's a reason I wrote this HOWTO (having to use an alternate
link as samba.org seems to be down right now):

http://man.chinaunix.net/newsoft/samba/docs/man/Samba-HOWTO-Collection/largefile.html

Jeremy.

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

* Re: [PATCH 5/7] XFS: Unicode case-insensitive lookup implementation
  2008-04-03 17:24     ` Jeremy Allison
  2008-04-03 18:09       ` Josef 'Jeff' Sipek
  2008-04-03 18:11       ` Eric Sandeen
@ 2008-04-03 18:43       ` Christoph Hellwig
  2008-04-03 18:47         ` Jeremy Allison
  2 siblings, 1 reply; 48+ messages in thread
From: Christoph Hellwig @ 2008-04-03 18:43 UTC (permalink / raw)
  To: Jeremy Allison; +Cc: Christoph Hellwig, Barry Naujok, xfs, linux-fsdevel

On Thu, Apr 03, 2008 at 10:24:00AM -0700, Jeremy Allison wrote:
> On Thu, Apr 03, 2008 at 01:14:50PM -0400, Christoph Hellwig wrote:
> > Validating file names is not the filesystem job.  In fact it's utterly
> > stupid, a unix filename is a sequence of bytes without special meaning
> > except for ., .., / and \0
> 
> This patch will be extremely useful for users who are serving
> Windows clients using Samba. It allow admins to turn off the
> userspace case insensitivity we have to emulate and be a significant
> speed increase.

CI filenames can work perfectly fine without adding validation of file
names by treating non-conformant bytestreams as not having lower/upper
case variants.

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

* Re: [PATCH 5/7] XFS: Unicode case-insensitive lookup implementation
  2008-04-03 18:43       ` Christoph Hellwig
@ 2008-04-03 18:47         ` Jeremy Allison
  2008-04-03 18:55           ` Christoph Hellwig
  0 siblings, 1 reply; 48+ messages in thread
From: Jeremy Allison @ 2008-04-03 18:47 UTC (permalink / raw)
  To: Christoph Hellwig; +Cc: Jeremy Allison, Barry Naujok, xfs, linux-fsdevel

On Thu, Apr 03, 2008 at 02:43:33PM -0400, Christoph Hellwig wrote:
> On Thu, Apr 03, 2008 at 10:24:00AM -0700, Jeremy Allison wrote:
> > On Thu, Apr 03, 2008 at 01:14:50PM -0400, Christoph Hellwig wrote:
> > > Validating file names is not the filesystem job.  In fact it's utterly
> > > stupid, a unix filename is a sequence of bytes without special meaning
> > > except for ., .., / and \0
> > 
> > This patch will be extremely useful for users who are serving
> > Windows clients using Samba. It allow admins to turn off the
> > userspace case insensitivity we have to emulate and be a significant
> > speed increase.
> 
> CI filenames can work perfectly fine without adding validation of file
> names by treating non-conformant bytestreams as not having lower/upper
> case variants.

Sorry, then I'm not understanding your objection to this patch (and I
don't think I understood that sentence :-).

Jeremy.

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

* Re: [PATCH 5/7] XFS: Unicode case-insensitive lookup implementation
  2008-04-03 18:47         ` Jeremy Allison
@ 2008-04-03 18:55           ` Christoph Hellwig
  2008-04-03 18:57             ` Christoph Hellwig
  0 siblings, 1 reply; 48+ messages in thread
From: Christoph Hellwig @ 2008-04-03 18:55 UTC (permalink / raw)
  To: Jeremy Allison; +Cc: Christoph Hellwig, Barry Naujok, xfs, linux-fsdevel

On Thu, Apr 03, 2008 at 11:47:39AM -0700, Jeremy Allison wrote:
> > CI filenames can work perfectly fine without adding validation of file
> > names by treating non-conformant bytestreams as not having lower/upper
> > case variants.
> 
> Sorry, then I'm not understanding your objection to this patch (and I
> don't think I understood that sentence :-).

I objected to the part of the patch I've quoted (and the bitsrelated to
it), not all of it.  That how we do reviews in kernel land, not sure
how samba handles it if you have a binary object/don't object policy..


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

* Re: [PATCH 5/7] XFS: Unicode case-insensitive lookup implementation
  2008-04-03 18:55           ` Christoph Hellwig
@ 2008-04-03 18:57             ` Christoph Hellwig
  2008-04-03 22:34               ` Jeremy Allison
  0 siblings, 1 reply; 48+ messages in thread
From: Christoph Hellwig @ 2008-04-03 18:57 UTC (permalink / raw)
  To: Jeremy Allison; +Cc: Christoph Hellwig, Barry Naujok, xfs, linux-fsdevel

On Thu, Apr 03, 2008 at 02:55:26PM -0400, Christoph Hellwig wrote:
> On Thu, Apr 03, 2008 at 11:47:39AM -0700, Jeremy Allison wrote:
> > > CI filenames can work perfectly fine without adding validation of file
> > > names by treating non-conformant bytestreams as not having lower/upper
> > > case variants.
> > 
> > Sorry, then I'm not understanding your objection to this patch (and I
> > don't think I understood that sentence :-).
> 
> I objected to the part of the patch I've quoted (and the bitsrelated to
> it), not all of it.  That how we do reviews in kernel land, not sure
> how samba handles it if you have a binary object/don't object policy..

oops, look like the quote actually got deleted accidentally.  sorry
I'ltake that comment back.

The part I object to are the various calls to xfs_unicode_validate
in the namespace operations.

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

* Re: [PATCH 5/7] XFS: Unicode case-insensitive lookup implementation
  2008-04-03 17:14   ` Christoph Hellwig
  2008-04-03 17:24     ` Jeremy Allison
@ 2008-04-03 22:20     ` David Chinner
  2008-04-03 22:31       ` Christoph Hellwig
  2008-04-03 23:00       ` Jamie Lokier
  1 sibling, 2 replies; 48+ messages in thread
From: David Chinner @ 2008-04-03 22:20 UTC (permalink / raw)
  To: Christoph Hellwig; +Cc: Barry Naujok, xfs, linux-fsdevel

On Thu, Apr 03, 2008 at 01:14:50PM -0400, Christoph Hellwig wrote:
> Validating file names is not the filesystem job.  In fact it's utterly
> stupid, a unix filename is a sequence of bytes without special meaning
> except for ., .., / and \0

So you're suggesting that we should rely on userspace being bug free
and always only using valid unicode sequences in it's names?

Cheers,

Dave.
-- 
Dave Chinner
Principal Engineer
SGI Australian Software Group

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

* Re: [PATCH 5/7] XFS: Unicode case-insensitive lookup implementation
  2008-04-03 22:20     ` David Chinner
@ 2008-04-03 22:31       ` Christoph Hellwig
  2008-04-03 23:00       ` Jamie Lokier
  1 sibling, 0 replies; 48+ messages in thread
From: Christoph Hellwig @ 2008-04-03 22:31 UTC (permalink / raw)
  To: David Chinner; +Cc: Christoph Hellwig, Barry Naujok, xfs, linux-fsdevel

On Fri, Apr 04, 2008 at 08:20:59AM +1000, David Chinner wrote:
> On Thu, Apr 03, 2008 at 01:14:50PM -0400, Christoph Hellwig wrote:
> > Validating file names is not the filesystem job.  In fact it's utterly
> > stupid, a unix filename is a sequence of bytes without special meaning
> > except for ., .., / and \0
> 
> So you're suggesting that we should rely on userspace being bug free
> and always only using valid unicode sequences in it's names?

just treat invalid unicode sequences as plain blob.

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

* Re: [PATCH 5/7] XFS: Unicode case-insensitive lookup implementation
  2008-04-03 18:57             ` Christoph Hellwig
@ 2008-04-03 22:34               ` Jeremy Allison
  0 siblings, 0 replies; 48+ messages in thread
From: Jeremy Allison @ 2008-04-03 22:34 UTC (permalink / raw)
  To: Christoph Hellwig; +Cc: Jeremy Allison, Barry Naujok, xfs, linux-fsdevel

On Thu, Apr 03, 2008 at 02:57:17PM -0400, Christoph Hellwig wrote:
> On Thu, Apr 03, 2008 at 02:55:26PM -0400, Christoph Hellwig wrote:
> > On Thu, Apr 03, 2008 at 11:47:39AM -0700, Jeremy Allison wrote:
> > > > CI filenames can work perfectly fine without adding validation of file
> > > > names by treating non-conformant bytestreams as not having lower/upper
> > > > case variants.
> > > 
> > > Sorry, then I'm not understanding your objection to this patch (and I
> > > don't think I understood that sentence :-).
> > 
> > I objected to the part of the patch I've quoted (and the bitsrelated to
> > it), not all of it.  That how we do reviews in kernel land, not sure
> > how samba handles it if you have a binary object/don't object policy..
> 
> oops, look like the quote actually got deleted accidentally.  sorry
> I'ltake that comment back.
> 
> The part I object to are the various calls to xfs_unicode_validate
> in the namespace operations.

Ah, ok - now I understand - thanks ! We also have a finer-grained
objection system than entire patch btw.

Jeremy.

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

* Re: [PATCH 1/7] XFS: Name operation vector for hash and compare
  2008-04-03  1:29   ` David Chinner
  2008-04-03  1:45     ` Barry Naujok
@ 2008-04-03 22:51     ` Christoph Hellwig
  1 sibling, 0 replies; 48+ messages in thread
From: Christoph Hellwig @ 2008-04-03 22:51 UTC (permalink / raw)
  To: David Chinner; +Cc: Barry Naujok, xfs, linux-fsdevel

On Thu, Apr 03, 2008 at 11:29:12AM +1000, David Chinner wrote:
> > +#define xfs_dir_hashname(dp, n, l) \
> > +		((dp)->i_mount->m_dirnameops->hashname((n), (l)))
> > +
> > +#define xfs_dir_compname(dp, n1, l1, n2, l2) \
> > +		((dp)->i_mount->m_dirnameops->compname((n1), (l1), (n2), (l2)))
> > +
> 
> Static inline functions, please.

Or kill them completely.  I find the common Linux style that jut
opencodes method invocations a lot more readable.


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

* Re: [PATCH 2/7] XFS: ASCII case-insensitive support
  2008-04-02  6:25 ` [PATCH 2/7] XFS: ASCII case-insensitive support Barry Naujok
  2008-04-03  0:35   ` Josef 'Jeff' Sipek
  2008-04-03  1:53   ` David Chinner
@ 2008-04-03 22:55   ` Christoph Hellwig
  2 siblings, 0 replies; 48+ messages in thread
From: Christoph Hellwig @ 2008-04-03 22:55 UTC (permalink / raw)
  To: Barry Naujok; +Cc: xfs, linux-fsdevel

On Wed, Apr 02, 2008 at 04:25:10PM +1000, Barry Naujok wrote:
> +	struct qstr	*a,
> +	struct qstr	*b)
> +{
> +	int	result = xfs_dir_compname(XFS_I(dir->d_inode), a->name, a->len,
> +					b->name, b->len) == XFS_CMP_DIFFERENT;
> +	/*
> +	 * result == 0 if a match is found, and if so, copy the name in "b"
> +	 * to "a" to cope with negative dentries getting the correct name.
> +	 */
> +	if (result == 0)
> +		memcpy((unsigned char *)a->name, b->name, a->len);
> +	return result;

qstr->name is marked const for a reason, please don't overwrite
it after it's creation.

> +struct dentry_operations xfs_ci_dentry_operations =
> +{

struct dentry_operations xfs_ci_dentry_operations = {
> +static xfs_dahash_t
> +xfs_ascii_ci_hashname(

is the use of STATIC now officially phased out for XFS?

> +		((sbp)->sb_versionnum & XFS_SB_VERSION_OLDCIBIT);

no need for the braces around sbp

>

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

* Re: [PATCH 5/7] XFS: Unicode case-insensitive lookup implementation
  2008-04-03 22:20     ` David Chinner
  2008-04-03 22:31       ` Christoph Hellwig
@ 2008-04-03 23:00       ` Jamie Lokier
  1 sibling, 0 replies; 48+ messages in thread
From: Jamie Lokier @ 2008-04-03 23:00 UTC (permalink / raw)
  To: David Chinner; +Cc: Christoph Hellwig, Barry Naujok, xfs, linux-fsdevel

David Chinner wrote:
> On Thu, Apr 03, 2008 at 01:14:50PM -0400, Christoph Hellwig wrote:
> > Validating file names is not the filesystem job.  In fact it's utterly
> > stupid, a unix filename is a sequence of bytes without special meaning
> > except for ., .., / and \0
> 
> So you're suggesting that we should rely on userspace being bug free
> and always only using valid unicode sequences in it's names?

You can bet that one person's "invalid unicode sequence" will have
been used as a valid filename on someone else's filesystem even using
unicode.  Meanings of "invalid unicode sequence" vary (a lot)
depending on which standard you follow.  Annoyance will come when they
try to copy the directory.

Such things are no great surprise on Windows and MacOS (both of which
do even worse things than rejecting sequences), but in POSIX land we
expect things like that to work.

The rules for ., .., / and \0, and accepting the rest as a blob, have
been well defined and understood for a long time.

-- Jamie

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

* Re: [PATCH 4/7] XFS: Return case-insensitive match for dentry cache
  2008-04-02  6:25 ` [PATCH 4/7] XFS: Return case-insensitive match for dentry cache Barry Naujok
  2008-04-03  2:34   ` Josef 'Jeff' Sipek
  2008-04-03  5:22   ` David Chinner
@ 2008-04-03 23:06   ` Christoph Hellwig
  2 siblings, 0 replies; 48+ messages in thread
From: Christoph Hellwig @ 2008-04-03 23:06 UTC (permalink / raw)
  To: Barry Naujok; +Cc: xfs, linux-fsdevel

On Wed, Apr 02, 2008 at 04:25:12PM +1000, Barry Naujok wrote:
> It also introduces a new type, xfs_name, which is similar to the
> dentry cache's qstr type. It contains a pointer to a zone allocated
> string (MAXNAMELEN sized) and the length of the actual name. This
> string does not need to be NULL terminated (a counted string).
> 
> xfs_name_t is only used in the lookup path for this patch, but may
> be used in other locations too if desired. It maybe desirable not
> to use xfs_name_t at all in the lookup functions but stick to
> separate parameters (which will mean 7 instead of 5 arguments).

wouldn't it be a lot cleaner to just pass the struct qstr all the way
down instead of inventing an almost the same type?

> To avoid polluting the dcache, we implement a new directory inode
> operations for lookup. xfs_vn_ci_lookup() interacts directly with
> the dcache and the code was derived from ntfs_lookup() in
> fs/ntfs/namei.c. The dentry hash and compare overrides introduced
> in the ASCII-CI patch has been removed.

So maybe we should introduce the method in that patch already?
Especially as the old one is buggy and the new one should be
one ore more helpers in generic code it could easily be a patch of
it's own.

> +		/*
> +		 * 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);

please add a documented __d_instanciated helper in common code that
is used by this and d_instanciate.

> +typedef struct qstr	bhv_vstr_t;

we try to get rid of the bhv_v* names, they're ugly amd their naming is
outdated.  Just use struct qstr until some port comes up with s
conflicting one.

> +	return kmem_zone_zalloc(xfs_da_name_zone, KM_SLEEP);

please don't introduce new KM_SLEEP allocations.

> +	kmem_zone_free(xfs_da_name_zone, (void *)name);

this cast should not be needed.


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

* Re: [PATCH 5/7] XFS: Unicode case-insensitive lookup implementation
  2008-04-03 18:11       ` Eric Sandeen
  2008-04-03 18:22         ` Jeremy Allison
@ 2008-04-04  0:00         ` Mark Goodwin
  1 sibling, 0 replies; 48+ messages in thread
From: Mark Goodwin @ 2008-04-04  0:00 UTC (permalink / raw)
  To: Eric Sandeen
  Cc: Jeremy Allison, Christoph Hellwig, Barry Naujok, xfs,
	linux-fsdevel



Eric Sandeen wrote:
> Jeremy Allison wrote:
>> On Thu, Apr 03, 2008 at 01:14:50PM -0400, Christoph Hellwig wrote:
>>> Validating file names is not the filesystem job.  In fact it's utterly
>>> stupid, a unix filename is a sequence of bytes without special meaning
>>> except for ., .., / and \0
>> This patch will be extremely useful for users who are serving
>> Windows clients using Samba. It allow admins to turn off the
>> userspace case insensitivity we have to emulate and be a significant
>> speed increase.
> 
> I'd like to see the numbers... Simo tested an earlier version of this
> patch, and it was not faster.... 

Yes I'd like to see some numbers too. Simo, can you post details of your
testing (framework, methodology, results, etc).

>
> Jeremy, what would be a representative test setup to use?

Cheers
-- Mark

-- 

  Mark Goodwin                                  markgw@sgi.com
  Engineering Manager for XFS and PCP    Phone: +61-3-99631937
  SGI Australian Software Group           Cell: +61-4-18969583
-------------------------------------------------------------


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

* Re: [PATCH 6/7] XFS: Native Language Support for Unicode in XFS
  2008-04-02  6:25 ` [PATCH 6/7] XFS: Native Language Support for Unicode in XFS Barry Naujok
@ 2008-04-04  0:05   ` David Chinner
  0 siblings, 0 replies; 48+ messages in thread
From: David Chinner @ 2008-04-04  0:05 UTC (permalink / raw)
  To: Barry Naujok; +Cc: xfs, linux-fsdevel

On Wed, Apr 02, 2008 at 04:25:14PM +1000, Barry Naujok wrote:
> Implement the "-o nls=<charset>" mount option and required conversion 
> of older style charater sets to/from UTF-8 to support non-UTF8 locales. 
> This option is compatible with other Linux filesystems supporting
> the "nls" mount option.
> 
> NLS conversion is performed on filename operations including readdir and
> also user domain extended attribute names. The name zone defined in
> the "return name" patch is used for temporarily holding the converted
> name.
> 
> If Unicode is not configed Y, then the NLS support is virtually a no-op.
> 
> Signed-off-by: Barry Naujok <bnaujok@sgi.com>
> 
> ---
>  fs/xfs/linux-2.6/xfs_linux.h |    5 +
>  fs/xfs/linux-2.6/xfs_super.c |   21 ++++++
>  fs/xfs/xfs_attr.c            |   78 +++++++++++++++---------
>  fs/xfs/xfs_attr.h            |    6 -
>  fs/xfs/xfs_attr_leaf.c       |   74 ++++++++++++++++-------
>  fs/xfs/xfs_clnt.h            |    1 
>  fs/xfs/xfs_dir2_block.c      |   14 +++-
>  fs/xfs/xfs_dir2_leaf.c       |   15 ++++
>  fs/xfs/xfs_dir2_sf.c         |   12 +++
>  fs/xfs/xfs_mount.h           |    2 
>  fs/xfs/xfs_rename.c          |   12 +++
>  fs/xfs/xfs_unicode.c         |  137 +++++++++++++++++++++++++++++++++++++++++++
>  fs/xfs/xfs_unicode.h         |   16 +++++
>  fs/xfs/xfs_vfsops.c          |   21 ++++++
>  fs/xfs/xfs_vnodeops.c        |  117 +++++++++++++++++++++++++-----------
>  15 files changed, 429 insertions(+), 102 deletions(-)
> 
> Index: kern_ci/fs/xfs/linux-2.6/xfs_linux.h
> ===================================================================
> --- kern_ci.orig/fs/xfs/linux-2.6/xfs_linux.h
> +++ kern_ci/fs/xfs/linux-2.6/xfs_linux.h
> @@ -181,6 +181,11 @@
>  #define howmany(x, y)	(((x)+((y)-1))/(y))
>  
>  /*
> + * NLS UTF-8 (unicode) 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)
> Index: kern_ci/fs/xfs/linux-2.6/xfs_super.c
> ===================================================================
> --- kern_ci.orig/fs/xfs/linux-2.6/xfs_super.c
> +++ kern_ci/fs/xfs/linux-2.6/xfs_super.c
> @@ -126,6 +126,7 @@ xfs_args_allocate(
>  #define MNTOPT_NOATTR2	"noattr2"	/* do not use attr2 attribute format */
>  #define MNTOPT_FILESTREAM  "filestreams" /* use filestreams allocator */
>  #define MNTOPT_CILOOKUP	"ci"		/* case-insensitive dir lookup */
> +#define MNTOPT_NLS	"nls"		/* NLS code page to use */
>  #define MNTOPT_QUOTA	"quota"		/* disk quotas (user) */
>  #define MNTOPT_NOQUOTA	"noquota"	/* no quotas */
>  #define MNTOPT_USRQUOTA	"usrquota"	/* user quota enabled */
> @@ -320,9 +321,20 @@ xfs_parseargs(
>  			args->flags &= ~XFSMNT_ATTR2;
>  		} else if (!strcmp(this_char, MNTOPT_FILESTREAM)) {
>  			args->flags2 |= XFSMNT2_FILESTREAMS;
> +#ifdef CONFIG_XFS_UNICODE
>  		} else if (!strcmp(this_char, MNTOPT_CILOOKUP)) {
>  			args->flags2 |= XFSMNT2_CILOOKUP;
> -#ifndef CONFIG_XFS_UNICODE
> +		} 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
> +		} else if (!strcmp(this_char, MNTOPT_CILOOKUP) ||
> +			   !strcmp(this_char, MNTOPT_NLS)) {
>  			cmn_err(CE_WARN,
>  				"XFS: %s option requires Unicode support",
>  				this_char);

Parse these options unconditionally - reject them later once we
have all the info we need off disk (as has been previously suggested).

>   *========================================================================*/
>  
>  int
> -xfs_attr_fetch(xfs_inode_t *ip, const char *name, int namelen,
> +xfs_attr_fetch(xfs_inode_t *ip, const uchar_t *name, int namelen,
>  	       char *value, int *valuelenp, int flags, struct cred *cred)

May as well change these to the standard XFS function format....

>  {
>  	xfs_da_args_t   args;
> @@ -167,6 +167,7 @@ xfs_attr_get(
>  	cred_t		*cred)
>  {
>  	int		error, namelen;
> +	const uchar_t	*uni_name;
>  
>  	XFS_STATS_INC(xs_attr_get);
>  
> @@ -176,24 +177,29 @@ xfs_attr_get(
>  	if (namelen >= MAXNAMELEN)
>  		return(EFAULT);		/* match IRIX behaviour */
>  
> +	if (XFS_FORCED_SHUTDOWN(ip->i_mount))
> +		return(EIO);
> +
>  	/* Enforce UTF-8 only for user attr names */
>  	if (xfs_sb_version_hasunicode(&ip->i_mount->m_sb) &&
>  			(flags & (ATTR_ROOT | ATTR_SECURE)) == 0) {
> -		error = xfs_unicode_validate(name, namelen);
> +		error = xfs_nls_to_unicode(ip->i_mount, name, namelen,
> +				&uni_name, &namelen);

Ok, so you are replacing the validation now with conversion.

>  		if (error)
>  			return error;
> -	}
> -	if (XFS_FORCED_SHUTDOWN(ip->i_mount))
> -		return(EIO);
> +	} else
> +		uni_name = name;
>  
>  	xfs_ilock(ip, XFS_ILOCK_SHARED);
> -	error = xfs_attr_fetch(ip, name, namelen, value, valuelenp, flags, cred);
> +	error = xfs_attr_fetch(ip, uni_name, namelen, value, valuelenp,
> +				flags, cred);
>  	xfs_iunlock(ip, XFS_ILOCK_SHARED);
> +	xfs_unicode_nls_free(name, uni_name);
>  	return(error);

kill the () in the return statement while you are there....

>  	xfs_ilock(dp, XFS_ILOCK_SHARED);
>  	if (XFS_IFORK_Q(dp) == 0 ||
>  		   (dp->i_d.di_aformat == XFS_DINODE_FMT_EXTENTS &&
>  		    dp->i_d.di_anextents == 0)) {
>  		xfs_iunlock(dp, XFS_ILOCK_SHARED);
> +		xfs_unicode_nls_free(name, uni_name);
>  		return(XFS_ERROR(ENOATTR));

Stack the error conditions at the end of the function and jump to
them. i.e. do this here:

		error = ENOATTR;
		goto out_error;
>  	}
>  	xfs_iunlock(dp, XFS_ILOCK_SHARED);
>  
> -	return xfs_attr_remove_int(dp, name, namelen, flags);
> +	error = xfs_attr_remove_int(dp, uni_name, namelen, flags);

out_error:

> +	xfs_unicode_nls_free(name, uni_name);
> +	return error;
>  }
>  
>  int								/* error */
> @@ -658,9 +676,9 @@ xfs_attr_list_int(xfs_attr_list_context_
>   */
>  /*ARGSUSED*/
>  STATIC int
> -xfs_attr_put_listent(xfs_attr_list_context_t *context, attrnames_t *namesp,
> -		     char *name, int namelen,
> -		     int valuelen, char *value)
> +xfs_attr_user_list(xfs_attr_list_context_t *context, attrnames_t *namesp,
> +		   char *name, int namelen,
> +		   int valuelen, char *value)

Formatting.

[snip a shiteload of whitespace fixes]

>  		if (dp->i_d.di_forkoff) {
> -			if (offset < dp->i_d.di_forkoff) 
> +			if (offset < dp->i_d.di_forkoff)
>  				return 0;
> -			else 
> +			else
>  				return dp->i_d.di_forkoff;

just kill the else there.

[more whitespace]

> @@ -734,7 +738,7 @@ xfs_attr_shortform_list(xfs_attr_list_co
>  			cursor->hashval = sbp->hash;
>  			cursor->offset = 0;
>  		}
> -		error = context->put_listent(context,
> +		error = á(context,
>  					namesp,
>  					sbp->name,
>  					sbp->namelen,

That looks completely busted ;)

> +/*
> + * Do NLS name conversion if required for user attribute names 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)
> +{
> +	char *nls_name;
> +	int nls_namelen;
> +	int error;
> +
> +	if (xfs_is_using_nls(context->dp->i_mount) && namesp == attr_user) {
> +		error = xfs_unicode_to_nls(context->dp->i_mount, name, namelen,
> +				&nls_name, &nls_namelen);
> +		if (error)
> +			return error;
> +		error = context->put_listent(context, namesp, nls_name,
> +				nls_namelen, valuelen, value);
> +		xfs_unicode_nls_free(name, nls_name);
> +		return error;
> +	} else
> +		return context->put_listent(context, namesp, name, namelen,
> +				valuelen, value);

Kill the else.

> @@ -513,16 +516,21 @@ xfs_dir2_block_getdents(
>  #if XFS_BIG_INUMS
>  		ino += mp->m_inoadd;
>  #endif
> -
> +		error = xfs_unicode_to_nls(mp, dep->name, dep->namelen,
> +				&nls_name, &nls_namelen);
> +		if (error)
> +			break;
>  		/*
>  		 * If it didn't fit, set the final offset to here & return.
>  		 */
> -		if (filldir(dirent, dep->name, dep->namelen, cook,
> +		if (filldir(dirent, nls_name, nls_namelen, cook,
>  			    ino, DT_UNKNOWN)) {
>  			*offset = cook;
> +			xfs_unicode_nls_free(dep->name, nls_name);
>  			xfs_da_brelse(NULL, bp);
>  			return 0;
>  		}
> +		xfs_unicode_nls_free(dep->name, nls_name);

This whole chunk is repeated inmany places with only slight
variations in error handling. A wrapper function that encompasses
this filldir callback section would be appropriate. say
xfs_dir_filldir()?

> @@ -1087,13 +1090,21 @@ xfs_dir2_leaf_getdents(
>  		ino += mp->m_inoadd;
>  #endif
>  
> +		error = xfs_unicode_to_nls(mp, dep->name, dep->namelen,
> +				&nls_name, &nls_namelen);
> +		if (error)
> +			break;
> +
>  		/*
>  		 * Won't fit.  Return to caller.
>  		 */
> -		if (filldir(dirent, dep->name, dep->namelen,
> +		if (filldir(dirent, nls_name, nls_namelen,
>  			    xfs_dir2_byte_to_dataptr(mp, curoff),
> -			    ino, DT_UNKNOWN))
> +			    ino, DT_UNKNOWN)) {
> +			xfs_unicode_nls_free(dep->name, nls_name);
>  			break;
> +		}
> +		xfs_unicode_nls_free(dep->name, nls_name);

xfs_dir_filldir()

> @@ -789,12 +793,18 @@ xfs_dir2_sf_getdents(
>  #if XFS_BIG_INUMS
>  		ino += mp->m_inoadd;
>  #endif
> +		error = xfs_unicode_to_nls(mp, sfep->name, sfep->namelen,
> +				&nls_name, &nls_namelen);
> +		if (error)
> +			return error;
>  
> -		if (filldir(dirent, sfep->name, sfep->namelen,
> +		if (filldir(dirent, nls_name, nls_namelen,
>  					    off, ino, DT_UNKNOWN)) {
>  			*offset = off;
> +			xfs_unicode_nls_free(sfep->name, nls_name);
>  			return 0;
>  		}
> +		xfs_unicode_nls_free(sfep->name, nls_name);

xfs_dir_filldir()

> @@ -250,10 +250,14 @@ xfs_rename(
>  	xfs_itrace_entry(target_dp);
>  
>  	if (xfs_sb_version_hasunicode(&mp->m_sb)) {
> -		error = xfs_unicode_validate(src_name, src_namelen);
> +		error = xfs_nls_to_unicode(mp,
> +				VNAME(src_vname), VNAMELEN(src_vname),
> +				(const uchar_t **)&src_name, &src_namelen);
>  		if (error)
>  			return error;
> -		error = xfs_unicode_validate(target_name, target_namelen);
> +		error = xfs_nls_to_unicode(mp,
> +				VNAME(target_vname), VNAMELEN(target_vname),
> +				(const uchar_t **)&target_name, &target_namelen);

This is kinda messy with all those macros and casts. I think I
mentioned before that the mess should be hidden in a helper function....

>  		if (error)
>  			return error;
>  	}
> @@ -265,6 +269,8 @@ xfs_rename(
>  					src_name, target_name,
>  					0, 0, 0);
>  		if (error) {
> +			xfs_unicode_nls_free(VNAME(src_vname), src_name);
> +			xfs_unicode_nls_free(VNAME(target_vname), target_name);
>  			return error;

			goto out_error;

>  		}
>  	}
> @@ -598,6 +604,8 @@ std_return:
>  					src_name, target_name,
>  					0, error, 0);
>  	}

out_error:

> +	xfs_unicode_nls_free(VNAME(src_vname), src_name);
> +	xfs_unicode_nls_free(VNAME(target_vname), target_name);
>  	return error;
>  
>   abort_return:

> --- kern_ci.orig/fs/xfs/xfs_unicode.c
> +++ kern_ci/fs/xfs/xfs_unicode.c
.....
> +	}
> +	if (i == uni_namelen) {
> +		*nls_name = n;
> +		*nls_namelen = o;
> +		return 0;
> +	}
> +	error = ENAMETOOLONG;
> +err_out:
> +	xfs_da_name_free(n);
> +	return error;
> +}

That's kind of convoluted - took me a moment to work out where the
successful return was coming from.

	if (i != uni_namelen) {
		error = ENAMETOOLONG;
		goto out_err;
	}

	*nls_name = n;
	*nls_namelen = o;
	return 0;
err_out:
	xfs_da_name_free(n);
	return error;
}

> @@ -76,6 +84,14 @@ void xfs_unicode_free_cft(const xfs_cft_
>  #define xfs_unicode_read_cft(mp)	(EOPNOTSUPP)
>  #define xfs_unicode_free_cft(cft)
>  
> +#define xfs_is_using_nls(mp)		0
> +
> +#define xfs_unicode_to_nls(mp, uname, ulen, pnname, pnlen) \
> +		((*(pnname)) = (uname), (*(pnlen)) = (ulen), 0)
> +#define xfs_nls_to_unicode(mp, nname, nlen, puname, pulen) \
> +		((*(puname)) = (nname), (*(pulen)) = (nlen), 0)
> +#define xfs_unicode_nls_free(sname, cname)
> +

static inline where possible.

> --- kern_ci.orig/fs/xfs/xfs_vfsops.c
> +++ kern_ci/fs/xfs/xfs_vfsops.c
> @@ -405,13 +405,30 @@ xfs_finish_flags(
>  	if (xfs_sb_version_hasunicode(&mp->m_sb)) {
>  		if (ap->flags2 & XFSMNT2_CILOOKUP)
>  			mp->m_flags |= XFS_MOUNT_CILOOKUP;
> +
> +		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;
> +		}

Can you push this off into a xfs_nls_load() helper function?

> @@ -647,6 +664,8 @@ out:
>  		xfs_unmountfs(mp, credp);
>  		xfs_qmops_put(mp);
>  		xfs_dmops_put(mp);
> +		if (xfs_is_using_nls(mp))
> +			unload_nls(mp->m_nls);

and an unload function as well (put those two lines inside it).

>  	if (xfs_sb_version_hasunicode(&dp->i_mount->m_sb)) {
> -		error = xfs_unicode_validate(d_name->name, d_name->len);
> +		error = xfs_nls_to_unicode(dp->i_mount, d_name->name,
> +				d_name->len, &name.name, &name.len);
>  		if (error)
>  			return error;
> +	} else {
> +		name.name = d_name->name;
> +		name.len = d_name->len;
>  	}

wrapper function.

> -	namelen = VNAMELEN(dentry);
> -
>  	if (xfs_sb_version_hasunicode(&mp->m_sb)) {
> -		error = xfs_unicode_validate(name, namelen);
> +		error = xfs_nls_to_unicode(mp, VNAME(dentry), VNAMELEN(dentry),
> +				(const uchar_t **)&name, &namelen);
>  		if (error)
>  			return error;
> +	} else {
> +		name = VNAME(dentry);
> +		namelen = VNAMELEN(dentry);
>  	}

same wrapper

>  
>  	if (DM_EVENT_ENABLED(dp, DM_EVENT_CREATE)) {
> @@ -1846,8 +1853,10 @@ xfs_create(
>  				DM_RIGHT_NULL, name, NULL,
>  				mode, 0, 0);
>  
> -		if (error)
> +		if (error) {
> +			xfs_unicode_nls_free(VNAME(dentry), name);
>  			return error;
> +		}

goto out_error;

>  		dm_event_sent = 1;
>  	}
>  
> @@ -1999,6 +2008,7 @@ std_return:
>  			DM_RIGHT_NULL, name, NULL,
>  			mode, error, 0);
>  	}

out_error:

> +	xfs_unicode_nls_free(VNAME(dentry), name);
>  	return error;
>  
>   abort_return:
.....

fix up all the other functions that have the same mods.


Cheers,

Dave.
-- 
Dave Chinner
Principal Engineer
SGI Australian Software Group
--
To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* Re: [PATCH 0/7] XFS: case-insensitive lookup and Unicode support
  2008-04-02  6:25 [PATCH 0/7] XFS: case-insensitive lookup and Unicode support Barry Naujok
                   ` (6 preceding siblings ...)
  2008-04-02  6:25 ` [PATCH 7/7] XFS: NLS config option Barry Naujok
@ 2008-04-08 11:45 ` Christoph Hellwig
  2008-04-09  1:53   ` Barry Naujok
  7 siblings, 1 reply; 48+ messages in thread
From: Christoph Hellwig @ 2008-04-08 11:45 UTC (permalink / raw)
  To: Barry Naujok; +Cc: xfs, linux-fsdevel

btw, a little comment on how to proceed.  After your xfs_name patch it
would be good to get the nameops and leafn cleanup in as almost
independent cleanups and then the ascii only CI but already using the
complex and correct lookup method.  I think unicocde will take a bit
longer to sort out and getting the simpler bit in first would be
helpful.

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

* Re: [PATCH 0/7] XFS: case-insensitive lookup and Unicode support
  2008-04-08 11:45 ` [PATCH 0/7] XFS: case-insensitive lookup and Unicode support Christoph Hellwig
@ 2008-04-09  1:53   ` Barry Naujok
  0 siblings, 0 replies; 48+ messages in thread
From: Barry Naujok @ 2008-04-09  1:53 UTC (permalink / raw)
  To: Christoph Hellwig; +Cc: xfs, linux-fsdevel

On Tue, 08 Apr 2008 21:45:13 +1000, Christoph Hellwig <hch@infradead.org>  
wrote:

> btw, a little comment on how to proceed.  After your xfs_name patch it
> would be good to get the nameops and leafn cleanup in as almost
> independent cleanups and then the ascii only CI but already using the
> complex and correct lookup method.  I think unicocde will take a bit
> longer to sort out and getting the simpler bit in first would be
> helpful.

Yep, that is how I was proceeding when working on the return name patch
with the xfs_vn_ci_lookup code.

So, it will be:
1 xfs_name
2 leafn refactor
3 nameops
4 ascii ci
5 unicode ci
6 nls

With NLS, it would be very nice if the Linux VFS or some layer above
always pushed UTF-8 strings down rather than handling it in the
filesystems that require it (there are about half a dozen at least
that I'm aware of).

Barry.

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

* Re: [PATCH 5/7] XFS: Unicode case-insensitive lookup implementation
  2008-04-03  8:31   ` David Chinner
@ 2008-04-17  5:38     ` Barry Naujok
  2008-04-17  8:49       ` David Chinner
  0 siblings, 1 reply; 48+ messages in thread
From: Barry Naujok @ 2008-04-17  5:38 UTC (permalink / raw)
  To: David Chinner; +Cc: xfs, linux-fsdevel

On Thu, 03 Apr 2008 18:31:51 +1000, David Chinner <dgc@sgi.com> wrote:

> On Wed, Apr 02, 2008 at 04:25:13PM +1000, Barry Naujok wrote:
>> --- kern_ci.orig/fs/xfs/xfs_sb.h
>> +++ kern_ci/fs/xfs/xfs_sb.h
>> @@ -79,10 +79,18 @@ 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	\
>> +#ifdef CONFIG_XFS_UNICODE
>> +# define XFS_SB_VERSION2_OKREALFBITS	\
>>  	(XFS_SB_VERSION2_LAZYSBCOUNTBIT	| \
>> +	 XFS_SB_VERSION2_UNICODEBIT | \
>>  	 XFS_SB_VERSION2_ATTR2BIT)
>> +#else
>> +# define XFS_SB_VERSION2_OKREALFBITS	\
>> +	(XFS_SB_VERSION2_LAZYSBCOUNTBIT	| \
>> +	 XFS_SB_VERSION2_ATTR2BIT)
>> +#endif
>
> Regardless of whether CONFIG_XFS_UNICODE is defined or not, we should
> be defining this as a valid bit. What we want is  
> xfs_sb_version_hasunicode()
> to say "not supported" when CONFIG_XFS_UNICODE is not defined.
>
> IOWs, if the sb_cftino field is defined in the superblock,
> XFS_SB_VERSION2_UNICODEBIT must be defined as well.

Either this or xfs_sb_good_version() has to be modified to support
CONFIG_XFS_UNICODE=n correctly and that such a kernel will refuse to
mount a Unicode XFS filesystem.

Barry.

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

* Re: [PATCH 5/7] XFS: Unicode case-insensitive lookup implementation
  2008-04-17  5:38     ` Barry Naujok
@ 2008-04-17  8:49       ` David Chinner
  0 siblings, 0 replies; 48+ messages in thread
From: David Chinner @ 2008-04-17  8:49 UTC (permalink / raw)
  To: Barry Naujok; +Cc: David Chinner, xfs, linux-fsdevel

On Thu, Apr 17, 2008 at 03:38:27PM +1000, Barry Naujok wrote:
> On Thu, 03 Apr 2008 18:31:51 +1000, David Chinner <dgc@sgi.com> wrote:
> 
> >On Wed, Apr 02, 2008 at 04:25:13PM +1000, Barry Naujok wrote:
> >>--- kern_ci.orig/fs/xfs/xfs_sb.h
> >>+++ kern_ci/fs/xfs/xfs_sb.h
> >>@@ -79,10 +79,18 @@ 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	\
> >>+#ifdef CONFIG_XFS_UNICODE
> >>+# define XFS_SB_VERSION2_OKREALFBITS	\
> >> 	(XFS_SB_VERSION2_LAZYSBCOUNTBIT	| \
> >>+	 XFS_SB_VERSION2_UNICODEBIT | \
> >> 	 XFS_SB_VERSION2_ATTR2BIT)
> >>+#else
> >>+# define XFS_SB_VERSION2_OKREALFBITS	\
> >>+	(XFS_SB_VERSION2_LAZYSBCOUNTBIT	| \
> >>+	 XFS_SB_VERSION2_ATTR2BIT)
> >>+#endif
> >
> >Regardless of whether CONFIG_XFS_UNICODE is defined or not, we should
> >be defining this as a valid bit. What we want is  
> >xfs_sb_version_hasunicode()
> >to say "not supported" when CONFIG_XFS_UNICODE is not defined.
> >
> >IOWs, if the sb_cftino field is defined in the superblock,
> >XFS_SB_VERSION2_UNICODEBIT must be defined as well.
> 
> Either this or xfs_sb_good_version() has to be modified to support
> CONFIG_XFS_UNICODE=n correctly and that such a kernel will refuse to
> mount a Unicode XFS filesystem.

Do the check in the same place we checkfor block size > page size.
i.e. in xfs_mount_validate_sb() add:

#ifndef CONFIG_XFS_UNICODE
	if (xfs_sb_version_hasunicode()) {
		/* big fat warning to syslog */
		return XFS_ERROR(ENOSYS);
	}
#endif

Cheers,

Dave.
-- 
Dave Chinner
Principal Engineer
SGI Australian Software Group

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

end of thread, other threads:[~2008-04-17  8:49 UTC | newest]

Thread overview: 48+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-04-02  6:25 [PATCH 0/7] XFS: case-insensitive lookup and Unicode support Barry Naujok
2008-04-02  6:25 ` [PATCH 1/7] XFS: Name operation vector for hash and compare Barry Naujok
2008-04-03  0:22   ` Josef 'Jeff' Sipek
2008-04-03  4:50     ` Barry Naujok
2008-04-03  1:29   ` David Chinner
2008-04-03  1:45     ` Barry Naujok
2008-04-03 22:51     ` Christoph Hellwig
2008-04-02  6:25 ` [PATCH 2/7] XFS: ASCII case-insensitive support Barry Naujok
2008-04-03  0:35   ` Josef 'Jeff' Sipek
2008-04-03  1:53   ` David Chinner
2008-04-03 17:09     ` Christoph Hellwig
2008-04-03 22:55   ` Christoph Hellwig
2008-04-02  6:25 ` [PATCH 3/7] XFS: Refactor node format directory lookup/addname Barry Naujok
2008-04-03  1:51   ` Josef 'Jeff' Sipek
2008-04-03  4:04     ` Barry Naujok
2008-04-03  4:10       ` Barry Naujok
2008-04-03  4:33   ` David Chinner
2008-04-02  6:25 ` [PATCH 4/7] XFS: Return case-insensitive match for dentry cache Barry Naujok
2008-04-03  2:34   ` Josef 'Jeff' Sipek
2008-04-03  5:22   ` David Chinner
2008-04-03  5:41     ` Stephen Rothwell
2008-04-03 14:56       ` Christoph Hellwig
2008-04-03 23:06   ` Christoph Hellwig
2008-04-02  6:25 ` [PATCH 5/7] XFS: Unicode case-insensitive lookup implementation Barry Naujok
2008-04-03  8:31   ` David Chinner
2008-04-17  5:38     ` Barry Naujok
2008-04-17  8:49       ` David Chinner
2008-04-03 17:14   ` Christoph Hellwig
2008-04-03 17:24     ` Jeremy Allison
2008-04-03 18:09       ` Josef 'Jeff' Sipek
2008-04-03 18:11       ` Eric Sandeen
2008-04-03 18:22         ` Jeremy Allison
2008-04-04  0:00         ` Mark Goodwin
2008-04-03 18:43       ` Christoph Hellwig
2008-04-03 18:47         ` Jeremy Allison
2008-04-03 18:55           ` Christoph Hellwig
2008-04-03 18:57             ` Christoph Hellwig
2008-04-03 22:34               ` Jeremy Allison
2008-04-03 22:20     ` David Chinner
2008-04-03 22:31       ` Christoph Hellwig
2008-04-03 23:00       ` Jamie Lokier
2008-04-02  6:25 ` [PATCH 6/7] XFS: Native Language Support for Unicode in XFS Barry Naujok
2008-04-04  0:05   ` David Chinner
2008-04-02  6:25 ` [PATCH 7/7] XFS: NLS config option Barry Naujok
2008-04-03  1:26   ` Josef 'Jeff' Sipek
2008-04-03  1:38     ` Barry Naujok
2008-04-08 11:45 ` [PATCH 0/7] XFS: case-insensitive lookup and Unicode support Christoph Hellwig
2008-04-09  1:53   ` Barry Naujok

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).