* [PATCH 0/4] XFS: case-insensitive lookup and Unicode support
@ 2008-04-21 8:31 Barry Naujok
2008-04-21 8:31 ` [PATCH 1/4] XFS: Name operation vector for hash and compare Barry Naujok
` (3 more replies)
0 siblings, 4 replies; 16+ messages in thread
From: Barry Naujok @ 2008-04-21 8:31 UTC (permalink / raw)
To: xfs; +Cc: linux-fsdevel
Latest set of patches based on the last set of feedback received.
Some cleanup patches have been checked in and this patch set has
been based on those patches.
The NLS support patch is not included this time, it still needs
a bit of work to integrate with these patches.
Barry.
--
^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH 1/4] XFS: Name operation vector for hash and compare
2008-04-21 8:31 [PATCH 0/4] XFS: case-insensitive lookup and Unicode support Barry Naujok
@ 2008-04-21 8:31 ` Barry Naujok
2008-04-21 8:42 ` Christoph Hellwig
2008-04-21 8:31 ` [PATCH 2/4] XFS: Return case-insensitive match for dentry cache Barry Naujok
` (2 subsequent siblings)
3 siblings, 1 reply; 16+ messages in thread
From: Barry Naujok @ 2008-04-21 8:31 UTC (permalink / raw)
To: xfs; +Cc: linux-fsdevel
[-- Attachment #1: nameops.patch --]
[-- Type: text/plain, Size: 20309 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 | 19 ++++++++++++++
fs/xfs/xfs_da_btree.h | 22 ++++++++++++++++
fs/xfs/xfs_dir2.c | 12 +++++----
fs/xfs/xfs_dir2_block.c | 34 ++++++++++++++++++-------
fs/xfs/xfs_dir2_data.c | 5 +++
fs/xfs/xfs_dir2_leaf.c | 61 +++++++++++++++++++++++++++++++++------------
fs/xfs/xfs_dir2_node.c | 26 ++++++++++++-------
fs/xfs/xfs_dir2_sf.c | 64 ++++++++++++++++++++++++++++--------------------
fs/xfs/xfs_mount.h | 2 +
9 files changed, 177 insertions(+), 68 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,25 @@ xfs_da_hashname(const uchar_t *name, int
}
}
+xfs_dacmp_t
+xfs_da_compname(const char *name1, int len1, const char *name2, int len2)
+{
+ return (len1 == len2 && memcmp(name1, name2, len1) == 0) ?
+ XFS_CMP_EXACT : XFS_CMP_DIFFERENT;
+}
+
+static xfs_dahash_t
+xfs_default_hashname(
+ struct xfs_name *name)
+{
+ return xfs_da_hashname(name->name, name->len);
+}
+
+const struct xfs_nameops xfs_default_nameops = {
+ .hashname = xfs_default_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,14 @@ 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
+ */
+struct xfs_nameops {
+ xfs_dahash_t (*hashname)(struct xfs_name *);
+ xfs_dacmp_t (*compname)(const char *, int, const char *, int);
+};
+
#ifdef __KERNEL__
/*========================================================================
@@ -249,6 +267,10 @@ int xfs_da_shrink_inode(xfs_da_args_t *a
xfs_dabuf_t *dead_buf);
uint xfs_da_hashname(const uchar_t *name_string, int name_length);
+xfs_dacmp_t xfs_da_compname(const char *name1, int len1,
+ const char *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
@@ -67,6 +67,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;
}
/*
@@ -166,7 +167,7 @@ xfs_dir_createname(
args.name = name->name;
args.namelen = name->len;
- args.hashval = xfs_da_hashname(name->name, name->len);
+ args.hashval = dp->i_mount->m_dirnameops->hashname(name);
args.inumber = inum;
args.dp = dp;
args.firstblock = first;
@@ -212,11 +213,12 @@ xfs_dir_lookup(
args.name = name->name;
args.namelen = name->len;
- args.hashval = xfs_da_hashname(name->name, name->len);
+ args.hashval = dp->i_mount->m_dirnameops->hashname(name);
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);
@@ -259,7 +261,7 @@ xfs_dir_removename(
args.name = name->name;
args.namelen = name->len;
- args.hashval = xfs_da_hashname(name->name, name->len);
+ args.hashval = dp->i_mount->m_dirnameops->hashname(name);
args.inumber = ino;
args.dp = dp;
args.firstblock = first;
@@ -342,7 +344,7 @@ xfs_dir_replace(
args.name = name->name;
args.namelen = name->len;
- args.hashval = xfs_da_hashname(name->name, name->len);
+ args.hashval = dp->i_mount->m_dirnameops->hashname(name);
args.inumber = inum;
args.dp = dp;
args.firstblock = first;
@@ -390,7 +392,7 @@ xfs_dir_canenter(
args.name = name->name;
args.namelen = name->len;
- args.hashval = xfs_da_hashname(name->name, name->len);
+ args.hashval = dp->i_mount->m_dirnameops->hashname(name);
args.dp = dp;
args.whichfork = XFS_DATA_FORK;
args.trans = tp;
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;
@@ -697,20 +698,32 @@ xfs_dir2_block_lookup_int(
dep = (xfs_dir2_data_entry_t *)
((char *)block + xfs_dir2_dataptr_to_off(mp, addr));
/*
- * Compare, if it's right give back buffer & entry number.
+ * Compare name and if it's an exact match, return the index
+ * and buffer. If it's the first case-insensitive match, store
+ * the index and buffer and continue looking for an exact match.
*/
- if (dep->namelen == args->namelen &&
- dep->name[0] == args->name[0] &&
- memcmp(dep->name, args->name, args->namelen) == 0) {
+ cmp = mp->m_dirnameops->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);
+ /*
+ * Here, we can only be doing a lookup (not a rename or replace).
+ * If a case-insensitive match was found earlier, return success.
+ */
+ 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);
}
@@ -1033,6 +1046,7 @@ xfs_dir2_sf_to_block(
xfs_dir2_sf_t *sfp; /* shortform structure */
__be16 *tagp; /* end of data entry */
xfs_trans_t *tp; /* transaction pointer */
+ struct xfs_name name;
xfs_dir2_trace_args("sf_to_block", args);
dp = args->dp;
@@ -1187,8 +1201,10 @@ xfs_dir2_sf_to_block(
tagp = xfs_dir2_data_entry_tag_p(dep);
*tagp = cpu_to_be16((char *)dep - (char *)block);
xfs_dir2_data_log_entry(tp, bp, dep);
- blp[2 + i].hashval = cpu_to_be32(xfs_da_hashname(
- (char *)sfep->name, sfep->namelen));
+ name.name = sfep->name;
+ name.len = sfep->namelen;
+ blp[2 + i].hashval = cpu_to_be32(mp->m_dirnameops->
+ hashname(&name));
blp[2 + i].address = cpu_to_be32(xfs_dir2_byte_to_dataptr(mp,
(char *)dep - (char *)block));
offset = (int)((char *)(tagp + 1) - (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
@@ -65,6 +65,7 @@ xfs_dir2_data_check(
xfs_mount_t *mp; /* filesystem mount point */
char *p; /* current data position */
int stale; /* count of stale leaves */
+ struct xfs_name name;
mp = dp->i_mount;
d = bp->data;
@@ -140,7 +141,9 @@ 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);
+ name.name = dep->name;
+ name.len = dep->namelen;
+ hash = mp->m_dirnameops->hashname(&name);
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,9 +1356,11 @@ 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++) {
+ index < be16_to_cpu(leaf->hdr.count) &&
+ be32_to_cpu(lep->hashval) == args->hashval;
+ lep++, index++) {
/*
* Skip over stale leaf entries.
*/
@@ -1371,12 +1375,12 @@ 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,
- xfs_dir2_db_to_da(mp, newdb), -1, &dbp,
- XFS_DATA_FORK))) {
+ error = xfs_da_read_buf(tp, dp,
+ xfs_dir2_db_to_da(mp, newdb),
+ -1, &dbp, XFS_DATA_FORK);
+ if (error) {
xfs_da_brelse(tp, lbp);
return error;
}
@@ -1386,24 +1390,47 @@ xfs_dir2_leaf_lookup_int(
/*
* Point to the data entry.
*/
- dep = (xfs_dir2_data_entry_t *)
- ((char *)dbp->data +
- xfs_dir2_dataptr_to_off(mp, be32_to_cpu(lep->address)));
+ dep = (xfs_dir2_data_entry_t *)((char *)dbp->data +
+ xfs_dir2_dataptr_to_off(mp, be32_to_cpu(lep->address)));
/*
- * If it matches then return it.
+ * Compare name and if it's an exact match, return the index
+ * and buffer. If it's the first case-insensitive match, store
+ * the index and buffer and continue looking for an exact match.
*/
- if (dep->namelen == args->namelen &&
- dep->name[0] == args->name[0] &&
- memcmp(dep->name, args->name, args->namelen) == 0) {
- *dbpp = dbp;
+ cmp = mp->m_dirnameops->compname(dep->name, dep->namelen,
+ args->name, args->namelen);
+ if (cmp != XFS_CMP_DIFFERENT && cmp != args->cmpresult) {
+ args->cmpresult = cmp;
*indexp = index;
- return 0;
+ /*
+ * case exact match: release the stored CI buffer if it
+ * exists and return the current buffer.
+ */
+ if (cmp == XFS_CMP_EXACT) {
+ if (cbp && cbp != dbp)
+ xfs_da_brelse(tp, cbp);
+ *dbpp = dbp;
+ return 0;
+ }
+ cbp = dbp;
}
}
+ ASSERT(args->oknoent);
+ /*
+ * Here, we can only be doing a lookup (not a rename or replace).
+ * If a case-insensitive match was found earlier, release the current
+ * buffer and return the stored CI matching buffer.
+ */
+ if (args->cmpresult == XFS_CMP_CASE) {
+ 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
@@ -556,6 +556,7 @@ xfs_dir2_leafn_lookup_for_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 */
dp = args->dp;
tp = args->trans;
@@ -620,17 +621,22 @@ xfs_dir2_leafn_lookup_for_entry(
dep = (xfs_dir2_data_entry_t *)((char *)curbp->data +
xfs_dir2_dataptr_to_off(mp, be32_to_cpu(lep->address)));
/*
- * Compare the entry, return it if it matches.
- */
- if (dep->namelen == args->namelen && memcmp(dep->name,
- args->name, args->namelen) == 0) {
+ * Compare the entry and if it's an exact match, return
+ * EEXIST immediately. If it's the first case-insensitive
+ * match, store the inode number and continue looking.
+ */
+ cmp = mp->m_dirnameops->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);
di = (int)((char *)dep - (char *)curbp->data);
error = EEXIST;
- goto out;
+ if (cmp == XFS_CMP_EXACT)
+ goto out;
}
}
- /* Didn't find a match. */
+ /* Didn't find an exact match. */
error = ENOENT;
di = -1;
ASSERT(index == be16_to_cpu(leaf->hdr.count) || args->oknoent);
@@ -1813,6 +1819,8 @@ xfs_dir2_node_lookup(
error = xfs_da_node_lookup_int(state, &rval);
if (error)
rval = error;
+ else if (rval == ENOENT && args->cmpresult == XFS_CMP_CASE)
+ rval = EEXIST; /* a case-insensitive match was found */
/*
* Release the btree blocks and leaf block.
*/
@@ -1856,9 +1864,8 @@ xfs_dir2_node_removename(
* Look up the entry we're deleting, set up the cursor.
*/
error = xfs_da_node_lookup_int(state, &rval);
- if (error) {
+ if (error)
rval = error;
- }
/*
* Didn't find it, upper layer screwed up.
*/
@@ -1875,9 +1882,8 @@ xfs_dir2_node_removename(
*/
error = xfs_dir2_leafn_remove(args, blk->bp, blk->index,
&state->extrablk, &rval);
- if (error) {
+ if (error)
return error;
- }
/*
* Fix the hash values up the btree.
*/
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,27 +846,40 @@ xfs_dir2_sf_lookup(
if (args->namelen == 2 &&
args->name[0] == '.' && args->name[1] == '.') {
args->inumber = xfs_dir2_sf_get_inumber(sfp, &sfp->hdr.parent);
+ args->cmpresult = XFS_CMP_EXACT;
return XFS_ERROR(EEXIST);
}
/*
* Loop over all the entries trying to match ours.
*/
- for (i = 0, sfep = xfs_dir2_sf_firstentry(sfp);
- i < sfp->hdr.count;
- i++, sfep = xfs_dir2_sf_nextentry(sfp, sfep)) {
- if (sfep->namelen == args->namelen &&
- sfep->name[0] == args->name[0] &&
- memcmp(args->name, sfep->name, args->namelen) == 0) {
- args->inumber =
- xfs_dir2_sf_get_inumber(sfp,
- xfs_dir2_sf_inumberp(sfep));
- return XFS_ERROR(EEXIST);
+ for (i = 0, sfep = xfs_dir2_sf_firstentry(sfp); i < sfp->hdr.count;
+ i++, sfep = xfs_dir2_sf_nextentry(sfp, sfep)) {
+ /*
+ * Compare name and if it's an exact match, return the inode
+ * number. If it's the first case-insensitive match, store the
+ * inode number and continue looking for an exact match.
+ */
+ cmp = dp->i_mount->m_dirnameops->compname(
+ 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));
+ if (cmp == XFS_CMP_EXACT)
+ return XFS_ERROR(EEXIST);
}
}
+ ASSERT(args->oknoent);
+ /*
+ * Here, we can only be doing a lookup (not a rename or replace).
+ * If a case-insensitive match was found earlier, return "found".
+ */
+ if (args->cmpresult == XFS_CMP_CASE)
+ return XFS_ERROR(EEXIST);
/*
* Didn't find it.
*/
- ASSERT(args->oknoent);
return XFS_ERROR(ENOENT);
}
@@ -904,24 +919,21 @@ xfs_dir2_sf_removename(
* Loop over the old directory entries.
* Find the one we're deleting.
*/
- 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) {
+ for (i = 0, sfep = xfs_dir2_sf_firstentry(sfp); i < sfp->hdr.count;
+ i++, sfep = xfs_dir2_sf_nextentry(sfp, sfep)) {
+ 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);
+ xfs_dir2_sf_inumberp(sfep)) ==
+ args->inumber);
break;
}
}
/*
* Didn't find it.
*/
- if (i == sfp->hdr.count) {
+ if (i == sfp->hdr.count)
return XFS_ERROR(ENOENT);
- }
/*
* Calculate sizes.
*/
@@ -1042,11 +1054,11 @@ xfs_dir2_sf_replace(
*/
else {
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) {
+ i < sfp->hdr.count;
+ i++, sfep = xfs_dir2_sf_nextentry(sfp, sfep)) {
+ 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 */
+ const 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] 16+ messages in thread
* [PATCH 2/4] XFS: Return case-insensitive match for dentry cache
2008-04-21 8:31 [PATCH 0/4] XFS: case-insensitive lookup and Unicode support Barry Naujok
2008-04-21 8:31 ` [PATCH 1/4] XFS: Name operation vector for hash and compare Barry Naujok
@ 2008-04-21 8:31 ` Barry Naujok
2008-04-21 8:59 ` Christoph Hellwig
2008-04-21 8:31 ` [PATCH 3/4] XFS: ASCII case-insensitive support Barry Naujok
2008-04-21 8:31 ` [PATCH 4/4] XFS: Unicode case-insensitive lookup implementation Barry Naujok
3 siblings, 1 reply; 16+ messages in thread
From: Barry Naujok @ 2008-04-21 8:31 UTC (permalink / raw)
To: xfs; +Cc: linux-fsdevel
[-- Attachment #1: return_name.patch --]
[-- Type: text/plain, Size: 17802 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.
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 "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 | 156 +++++++++++++++++++++++++++++++++++++++++-
fs/xfs/linux-2.6/xfs_iops.h | 1
fs/xfs/xfs_dir2.c | 24 +++++-
fs/xfs/xfs_dir2.h | 32 ++++++++
fs/xfs/xfs_dir2_block.c | 11 ++
fs/xfs/xfs_dir2_leaf.c | 7 +
fs/xfs/xfs_dir2_node.c | 21 ++++-
fs/xfs/xfs_dir2_sf.c | 7 +
fs/xfs/xfs_vfsops.c | 2
fs/xfs/xfs_vnodeops.c | 17 +++-
fs/xfs/xfs_vnodeops.h | 2
12 files changed, 264 insertions(+), 18 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
@@ -215,7 +215,7 @@ xfs_fs_get_parent(
struct xfs_inode *cip;
struct dentry *parent;
- error = xfs_lookup(XFS_I(child->d_inode), &xfs_name_dotdot, &cip);
+ error = xfs_lookup(XFS_I(child->d_inode), &xfs_name_dotdot, &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
@@ -2,6 +2,8 @@
* Copyright (c) 2000-2005 Silicon Graphics, Inc.
* All Rights Reserved.
*
+ * Portions Copyright (c) 2001-2006 Anton Altaparmakov
+ *
* 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.
@@ -389,7 +391,7 @@ xfs_vn_lookup(
return ERR_PTR(-ENAMETOOLONG);
xfs_dentry_to_name(&name, dentry);
- error = xfs_lookup(XFS_I(dir), &name, &cip);
+ error = xfs_lookup(XFS_I(dir), &name, &cip, NULL);
if (unlikely(error)) {
if (unlikely(error != ENOENT))
return ERR_PTR(-error);
@@ -400,6 +402,139 @@ xfs_vn_lookup(
return d_splice_alias(cip->i_vnode, dentry);
}
+STATIC struct dentry *
+xfs_ci_dentry_update(
+ struct dentry *dent,
+ struct inode *dent_inode,
+ struct xfs_name *name)
+{
+ int err;
+ struct dentry *real_dent;
+ struct dentry *new_dent;
+ struct qstr nls_name;
+
+ nls_name.name = name->name;
+ nls_name.len = name->len;
+
+ /*
+ * following code from ntfs_lookup() in fs/ntfs/namei.c
+ */
+
+ nls_name.hash = full_name_hash(nls_name.name, nls_name.len);
+
+ /* Does a dentry matching the nls_name exist already? */
+ real_dent = d_lookup(dent->d_parent, &nls_name);
+ /* If not, create it now. */
+ if (!real_dent) {
+ real_dent = d_alloc(dent->d_parent, &nls_name);
+ if (!real_dent) {
+ err = -ENOMEM;
+ goto err_out;
+ }
+ new_dent = d_splice_alias(dent_inode, real_dent);
+ if (new_dent)
+ dput(real_dent);
+ else
+ new_dent = real_dent;
+ return new_dent;
+ }
+ /* Matching dentry exists, check if it is negative. */
+ if (real_dent->d_inode) {
+ if (unlikely(real_dent->d_inode != dent_inode)) {
+ /* This can happen because bad inodes are unhashed. */
+ BUG_ON(!is_bad_inode(dent_inode));
+ BUG_ON(!is_bad_inode(real_dent->d_inode));
+ }
+ /*
+ * Already have the inode and the dentry attached, decrement
+ * the reference count to balance the xfs_lookup() we did
+ * earlier on. We found the dentry using d_lookup() so it
+ * cannot be disconnected and thus we do not need to worry
+ * about any NFS/disconnectedness issues here.
+ */
+ iput(dent_inode);
+ return real_dent;
+ }
+ /*
+ * Negative dentry: instantiate it unless the inode is a directory and
+ * has a 'disconnected' dentry (i.e. IS_ROOT and DCACHE_DISCONNECTED),
+ * in which case d_move() that in place of the found dentry.
+ */
+ if (!S_ISDIR(dent_inode->i_mode)) {
+ /* Not a directory; everything is easy. */
+ d_instantiate(real_dent, dent_inode);
+ return real_dent;
+ }
+ spin_lock(&dcache_lock);
+ if (list_empty(&dent_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(&real_dent->d_alias, &dent_inode->i_dentry);
+ real_dent->d_inode = dent_inode;
+ spin_unlock(&dcache_lock);
+ security_d_instantiate(real_dent, dent_inode);
+ return real_dent;
+ }
+ /*
+ * Directory with a 'disconnected' dentry; get a reference to the
+ * 'disconnected' dentry.
+ */
+ new_dent = list_entry(dent_inode->i_dentry.next, struct dentry,
+ d_alias);
+ dget_locked(new_dent);
+ spin_unlock(&dcache_lock);
+ /* Do security vodoo. */
+ security_d_instantiate(real_dent, dent_inode);
+ /* Move new_dent in place of real_dent. */
+ d_move(new_dent, real_dent);
+ /* Balance the xfs_lookup() we did above. */
+ iput(dent_inode);
+ /* Throw away real_dent. */
+ dput(real_dent);
+ /* Use new_dent as the actual dentry. */
+ return new_dent;
+
+err_out:
+ iput(dent_inode);
+ return ERR_PTR(err);
+}
+
+STATIC struct dentry *
+xfs_vn_ci_lookup(
+ struct inode *dir,
+ struct dentry *dentry,
+ struct nameidata *nd)
+{
+ struct xfs_inode *ip;
+ struct xfs_name name;
+ int ci_match = 0;
+ int error;
+
+ if (dentry->d_name.len >= MAXNAMELEN)
+ return ERR_PTR(-ENAMETOOLONG);
+
+ xfs_dentry_to_name(&name, dentry);
+ error = xfs_lookup(XFS_I(dir), &name, &ip, &ci_match);
+ if (unlikely(error)) {
+ if (unlikely(error != ENOENT))
+ return ERR_PTR(-error);
+ d_add(dentry, NULL);
+ return NULL;
+ }
+
+ /* if exact match, just splice and exit */
+ if (!ci_match)
+ return d_splice_alias(ip->i_vnode, dentry);
+
+ /* else case-insensitive match... */
+ dentry = xfs_ci_dentry_update(dentry, ip->i_vnode, &name);
+ xfs_name_free(name.name);
+ return dentry;
+}
+
STATIC int
xfs_vn_link(
struct dentry *old_dentry,
@@ -911,6 +1046,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,
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/xfs_dir2.c
===================================================================
--- kern_ci.orig/fs/xfs/xfs_dir2.c
+++ kern_ci/fs/xfs/xfs_dir2.c
@@ -46,6 +46,8 @@
struct xfs_name xfs_name_dotdot = {"..", 2};
+kmem_zone_t *xfs_name_zone;
+
extern const struct xfs_nameops xfs_default_nameops;
void
@@ -195,13 +197,16 @@ xfs_dir_createname(
/*
* Lookup a name in a directory, give back the inode number.
+ * If ci_match is not NULL, sets whether a CI match occurred of not, and
+ * if so, return the actual name in name.
*/
int
xfs_dir_lookup(
xfs_trans_t *tp,
xfs_inode_t *dp,
struct xfs_name *name,
- xfs_ino_t *inum) /* out: inode number */
+ xfs_ino_t *inum, /* out: inode number */
+ int *ci_match) /* out: CI match occurred */
{
xfs_da_args_t args;
int rval;
@@ -234,8 +239,23 @@ xfs_dir_lookup(
rval = xfs_dir2_node_lookup(&args);
if (rval == EEXIST)
rval = 0;
- if (rval == 0)
+ if (!rval) {
*inum = args.inumber;
+ if (ci_match) {
+ *ci_match = args.cmpresult == XFS_CMP_CASE;
+ if (*ci_match) {
+ if (!args.value)
+ return ENOMEM;
+ name->name = args.value;
+ name->len = args.valuelen;
+ } else {
+ ASSERT(args.value == NULL);
+ }
+ } else {
+ if (args.value)
+ xfs_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
@@ -74,7 +74,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,
- struct xfs_name *name, xfs_ino_t *inum);
+ struct xfs_name *name, xfs_ino_t *inum,
+ int *ci_match);
extern int xfs_dir_removename(struct xfs_trans *tp, struct xfs_inode *dp,
struct xfs_name *name, xfs_ino_t ino,
xfs_fsblock_t *first,
@@ -99,4 +100,33 @@ extern int xfs_dir2_isleaf(struct xfs_tr
extern int xfs_dir2_shrink_inode(struct xfs_da_args *args, xfs_dir2_db_t db,
struct xfs_dabuf *bp);
+/*
+ * Name handling routines for directories and attrs
+ */
+
+extern struct kmem_zone *xfs_name_zone;
+
+static inline char *
+xfs_name_alloc(void)
+{
+ return kmem_zone_zalloc(xfs_name_zone, KM_MAYFAIL);
+}
+
+static inline void
+xfs_name_free(const char *name)
+{
+ kmem_zone_free(xfs_name_zone, (void *)name);
+}
+
+static inline char *
+xfs_name_dup(const char *src_name, int src_len, int *dest_len)
+{
+ char *dest_name = xfs_name_alloc();
+ if (dest_name) {
+ memcpy(dest_name, src_name, src_len);
+ *dest_len = src_len;
+ }
+ return dest_name;
+}
+
#endif /* __XFS_DIR2_H__ */
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
@@ -610,12 +610,19 @@ xfs_dir2_block_lookup(
/*
* Get the offset from the leaf entry, to point to the data.
*/
- dep = (xfs_dir2_data_entry_t *)
- ((char *)block + xfs_dir2_dataptr_to_off(mp, be32_to_cpu(blp[ent].address)));
+ dep = (xfs_dir2_data_entry_t *)((char *)block +
+ xfs_dir2_dataptr_to_off(mp, be32_to_cpu(blp[ent].address)));
/*
* 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_name_dup(dep->name, dep->namelen,
+ &args->valuelen);
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,13 @@ 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_name_dup(dep->name, dep->namelen,
+ &args->valuelen);
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
@@ -549,7 +549,7 @@ xfs_dir2_leafn_lookup_for_entry(
xfs_dir2_data_entry_t *dep; /* data block entry */
xfs_inode_t *dp; /* incore directory inode */
int error; /* error return value */
- int di; /* data entry index */
+ int di = -1; /* data entry index */
int index; /* leaf entry index */
xfs_dir2_leaf_t *leaf; /* leaf structure */
xfs_dir2_leaf_entry_t *lep; /* leaf entry */
@@ -577,6 +577,7 @@ xfs_dir2_leafn_lookup_for_entry(
if (state->extravalid) {
curbp = state->extrablk.bp;
curdb = state->extrablk.blkno;
+ di = state->extrablk.index;
}
/*
* Loop over leaf entries with the right hash value.
@@ -638,7 +639,6 @@ xfs_dir2_leafn_lookup_for_entry(
}
/* Didn't find an exact match. */
error = ENOENT;
- di = -1;
ASSERT(index == be16_to_cpu(leaf->hdr.count) || args->oknoent);
out:
if (curbp) {
@@ -652,7 +652,7 @@ out:
state->extravalid = 0;
}
/*
- * Return the index, that will be the insertion point.
+ * Return the index, that will be the deletion point for remove/replace.
*/
*indexp = index;
return XFS_ERROR(error);
@@ -1819,8 +1819,19 @@ xfs_dir2_node_lookup(
error = xfs_da_node_lookup_int(state, &rval);
if (error)
rval = error;
- else if (rval == ENOENT && args->cmpresult == XFS_CMP_CASE)
- rval = EEXIST; /* a case-insensitive match was found */
+ else if (rval == ENOENT && args->cmpresult == XFS_CMP_CASE) {
+ /*
+ * A case-insensitive match was found: return the actual
+ * name in args->value and return EEXIST
+ */
+ xfs_dir2_data_entry_t *dep;
+
+ dep = (xfs_dir2_data_entry_t *)((char *)state->extrablk.bp->
+ data + state->extrablk.index);
+ args->value = xfs_name_dup(dep->name, dep->namelen,
+ &args->valuelen);
+ 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)) {
/*
@@ -875,8 +877,11 @@ xfs_dir2_sf_lookup(
* Here, we can only be doing a lookup (not a rename or replace).
* If a case-insensitive match was found earlier, return "found".
*/
- if (args->cmpresult == XFS_CMP_CASE)
+ if (args->cmpresult == XFS_CMP_CASE) {
+ args->value = xfs_name_dup(ci_sfep->name, ci_sfep->namelen,
+ &args->valuelen);
return XFS_ERROR(EEXIST);
+ }
/*
* Didn't find it.
*/
Index: kern_ci/fs/xfs/xfs_vfsops.c
===================================================================
--- kern_ci.orig/fs/xfs/xfs_vfsops.c
+++ kern_ci/fs/xfs/xfs_vfsops.c
@@ -80,6 +80,7 @@ xfs_init(void)
xfs_dabuf_zone = kmem_zone_init(sizeof(xfs_dabuf_t), "xfs_dabuf");
xfs_ifork_zone = kmem_zone_init(sizeof(xfs_ifork_t), "xfs_ifork");
xfs_trans_zone = kmem_zone_init(sizeof(xfs_trans_t), "xfs_trans");
+ xfs_name_zone = kmem_zone_init(MAXNAMELEN, "xfs_name");
xfs_acl_zone_init(xfs_acl_zone, "xfs_acl");
xfs_mru_cache_init();
xfs_filestream_init();
@@ -179,6 +180,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_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
@@ -1629,12 +1629,18 @@ xfs_inactive(
return VN_INACTIVE_CACHE;
}
-
+/*
+ * Lookups up an inode from "name". If ci_match is not NULL, then if a
+ * CI match is found, name->name can be replaced and ci_match is set to 1.
+ * The caller of xfs_lookup must call xfs_name_free(name->name) if
+ * ci_match in non-NULL and the value is non-zero.
+ */
int
xfs_lookup(
xfs_inode_t *dp,
struct xfs_name *name,
- xfs_inode_t **ipp)
+ xfs_inode_t **ipp,
+ int *ci_match)
{
xfs_ino_t inum;
int error;
@@ -1646,15 +1652,18 @@ xfs_lookup(
return XFS_ERROR(EIO);
lock_mode = xfs_ilock_map_shared(dp);
- error = xfs_dir_lookup(NULL, dp, name, &inum);
+ error = xfs_dir_lookup(NULL, dp, name, &inum, ci_match);
xfs_iunlock_map_shared(dp, lock_mode);
if (error)
goto out;
error = xfs_iget(dp->i_mount, NULL, inum, 0, 0, ipp, 0);
- if (error)
+ if (error) {
+ if (ci_match && *ci_match)
+ xfs_name_free(name->name);
goto out;
+ }
xfs_itrace_ref(*ipp);
return 0;
Index: kern_ci/fs/xfs/xfs_vnodeops.h
===================================================================
--- kern_ci.orig/fs/xfs/xfs_vnodeops.h
+++ kern_ci/fs/xfs/xfs_vnodeops.h
@@ -23,7 +23,7 @@ int xfs_fsync(struct xfs_inode *ip, int
int xfs_release(struct xfs_inode *ip);
int xfs_inactive(struct xfs_inode *ip);
int xfs_lookup(struct xfs_inode *dp, struct xfs_name *name,
- struct xfs_inode **ipp);
+ struct xfs_inode **ipp, int *ci_match);
int xfs_create(struct xfs_inode *dp, struct xfs_name *name, mode_t mode,
xfs_dev_t rdev, struct xfs_inode **ipp, struct cred *credp);
int xfs_remove(struct xfs_inode *dp, struct xfs_name *name,
--
^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH 3/4] XFS: ASCII case-insensitive support
2008-04-21 8:31 [PATCH 0/4] XFS: case-insensitive lookup and Unicode support Barry Naujok
2008-04-21 8:31 ` [PATCH 1/4] XFS: Name operation vector for hash and compare Barry Naujok
2008-04-21 8:31 ` [PATCH 2/4] XFS: Return case-insensitive match for dentry cache Barry Naujok
@ 2008-04-21 8:31 ` Barry Naujok
2008-04-21 9:03 ` Christoph Hellwig
2008-04-21 8:31 ` [PATCH 4/4] XFS: Unicode case-insensitive lookup implementation Barry Naujok
3 siblings, 1 reply; 16+ messages in thread
From: Barry Naujok @ 2008-04-21 8:31 UTC (permalink / raw)
To: xfs; +Cc: linux-fsdevel
[-- Attachment #1: ascii_ci.patch --]
[-- Type: text/plain, Size: 6248 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.
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_linux.h | 1
fs/xfs/linux-2.6/xfs_super.c | 7 ++++-
fs/xfs/xfs_dir2.c | 53 ++++++++++++++++++++++++++++++++++++++++++-
fs/xfs/xfs_fs.h | 1
fs/xfs/xfs_fsops.c | 4 ++-
fs/xfs/xfs_sb.h | 10 +++++++-
6 files changed, 72 insertions(+), 4 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
@@ -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,
@@ -564,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;
+ if (xfs_sb_version_hasoldci(&XFS_M(inode->i_sb)->m_sb))
+ inode->i_op = &xfs_dir_ci_inode_operations;
+ else
+ inode->i_op = &xfs_dir_inode_operations;
inode->i_fop = &xfs_dir_file_operations;
break;
case S_IFLNK:
Index: kern_ci/fs/xfs/xfs_dir2.c
===================================================================
--- kern_ci.orig/fs/xfs/xfs_dir2.c
+++ kern_ci/fs/xfs/xfs_dir2.c
@@ -50,6 +50,54 @@ kmem_zone_t *xfs_name_zone;
extern const struct xfs_nameops xfs_default_nameops;
+/*
+ * V1/OLDCI case-insensitive support for directories that was used in IRIX.
+ *
+ * This is ASCII only case support, ie. A-Z.
+ */
+STATIC xfs_dahash_t
+xfs_ascii_ci_hashname(
+ struct xfs_name *name)
+{
+ xfs_dahash_t hash;
+ int i;
+
+ for (i = 0, hash = 0; i < name->len; i++)
+ hash = tolower(name->name[i]) ^ rol32(hash, 7);
+
+ return hash;
+}
+
+STATIC xfs_dacmp_t
+xfs_ascii_ci_compname(
+ const char *name1,
+ int len1,
+ const char *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)
@@ -69,7 +117,10 @@ 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;
+ if (xfs_sb_version_hasoldci(&mp->m_sb))
+ mp->m_dirnameops = &xfs_ascii_ci_nameops;
+ else
+ mp->m_dirnameops = &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_BORGBIT 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_BORGBIT)
#define XFS_SB_VERSION_OKREALFBITS \
(XFS_SB_VERSION_ATTRBIT | \
XFS_SB_VERSION_NLINKBIT | \
@@ -437,6 +439,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_BORGBIT);
+}
+
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] 16+ messages in thread
* [PATCH 4/4] XFS: Unicode case-insensitive lookup implementation
2008-04-21 8:31 [PATCH 0/4] XFS: case-insensitive lookup and Unicode support Barry Naujok
` (2 preceding siblings ...)
2008-04-21 8:31 ` [PATCH 3/4] XFS: ASCII case-insensitive support Barry Naujok
@ 2008-04-21 8:31 ` Barry Naujok
2008-04-21 9:15 ` Christoph Hellwig
3 siblings, 1 reply; 16+ messages in thread
From: Barry Naujok @ 2008-04-21 8:31 UTC (permalink / raw)
To: xfs; +Cc: linux-fsdevel
[-- Attachment #1: unicode_ci.patch --]
[-- Type: text/plain, Size: 50397 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>
A following patch will implement Linux NLS support for XFS Unicode.
Signed-off-by: Barry Naujok <bnaujok@sgi.com>
---
fs/xfs/Kconfig | 20 +
fs/xfs/Makefile | 4
fs/xfs/linux-2.6/xfs_iops.c | 48 ++-
fs/xfs/linux-2.6/xfs_linux.h | 1
fs/xfs/linux-2.6/xfs_super.c | 6
fs/xfs/linux-2.6/xfs_super.h | 7
fs/xfs/xfs_attr.c | 15 -
fs/xfs/xfs_clnt.h | 2
fs/xfs/xfs_da_btree.c | 24 +
fs/xfs/xfs_da_btree.h | 14 -
fs/xfs/xfs_dir2.c | 32 +-
fs/xfs/xfs_dir2_block.c | 4
fs/xfs/xfs_dir2_leaf.c | 2
fs/xfs/xfs_dir2_node.c | 2
fs/xfs/xfs_dir2_sf.c | 2
fs/xfs/xfs_fs.h | 27 +
fs/xfs/xfs_fsops.c | 4
fs/xfs/xfs_itable.c | 2
fs/xfs/xfs_mount.c | 37 ++
fs/xfs/xfs_mount.h | 5
fs/xfs/xfs_sb.h | 23 +
fs/xfs/xfs_unicode.c | 584 +++++++++++++++++++++++++++++++++++++++++++
fs/xfs/xfs_unicode.h | 78 +++++
fs/xfs/xfs_vfsops.c | 15 +
24 files changed, 886 insertions(+), 72 deletions(-)
Index: kern_ci/fs/xfs/Kconfig
===================================================================
--- kern_ci.orig/fs/xfs/Kconfig
+++ kern_ci/fs/xfs/Kconfig
@@ -60,6 +60,24 @@ 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.
+
+ Note: Unicode UTF-8 enforcement, like case-insensitive lookup,
+ is not POSIX compliant.
+
+ If you don't require UTF-8 enforcement, say N.
+
config XFS_RT
bool "XFS Realtime subvolume support"
depends on XFS_FS
@@ -95,7 +113,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_iops.c
===================================================================
--- kern_ci.orig/fs/xfs/linux-2.6/xfs_iops.c
+++ kern_ci/fs/xfs/linux-2.6/xfs_iops.c
@@ -49,6 +49,7 @@
#include "xfs_buf_item.h"
#include "xfs_utils.h"
#include "xfs_vnodeops.h"
+#include "xfs_unicode.h"
#include <linux/capability.h>
#include <linux/xattr.h>
@@ -241,13 +242,18 @@ xfs_init_security(
return error;
}
-static void
+static int
xfs_dentry_to_name(
+ struct inode *dir,
struct xfs_name *namep,
struct dentry *dentry)
{
namep->name = dentry->d_name.name;
namep->len = dentry->d_name.len;
+
+ if (xfs_sb_version_hasunicode(&XFS_M(dir->i_sb)->m_sb))
+ return xfs_unicode_validate(namep);
+ return 0;
}
STATIC void
@@ -264,7 +270,7 @@ xfs_cleanup_inode(
* xfs_init_security we must back out.
* ENOSPC can hit here, among other things.
*/
- xfs_dentry_to_name(&teardown, dentry);
+ xfs_dentry_to_name(dir, &teardown, dentry);
if (S_ISDIR(mode))
xfs_rmdir(XFS_I(dir), &teardown, XFS_I(inode));
@@ -304,7 +310,9 @@ xfs_vn_mknod(
}
}
- xfs_dentry_to_name(&name, dentry);
+ error = xfs_dentry_to_name(dir, &name, dentry);
+ if (unlikely(error))
+ goto out_free_acl;
if (IS_POSIXACL(dir) && !default_acl)
mode &= ~current->fs->umask;
@@ -390,7 +398,10 @@ xfs_vn_lookup(
if (dentry->d_name.len >= MAXNAMELEN)
return ERR_PTR(-ENAMETOOLONG);
- xfs_dentry_to_name(&name, dentry);
+ error = xfs_dentry_to_name(dir, &name, dentry);
+ if (unlikely(error))
+ return ERR_PTR(-error);
+
error = xfs_lookup(XFS_I(dir), &name, &cip, NULL);
if (unlikely(error)) {
if (unlikely(error != ENOENT))
@@ -516,7 +527,10 @@ xfs_vn_ci_lookup(
if (dentry->d_name.len >= MAXNAMELEN)
return ERR_PTR(-ENAMETOOLONG);
- xfs_dentry_to_name(&name, dentry);
+ error = xfs_dentry_to_name(dir, &name, dentry);
+ if (unlikely(error))
+ return ERR_PTR(-error);
+
error = xfs_lookup(XFS_I(dir), &name, &ip, &ci_match);
if (unlikely(error)) {
if (unlikely(error != ENOENT))
@@ -546,7 +560,9 @@ xfs_vn_link(
int error;
inode = old_dentry->d_inode;
- xfs_dentry_to_name(&name, dentry);
+ error = xfs_dentry_to_name(dir, &name, dentry);
+ if (unlikely(error))
+ return -error;
igrab(inode);
error = xfs_link(XFS_I(dir), XFS_I(inode), &name);
@@ -571,7 +587,9 @@ xfs_vn_unlink(
int error;
inode = dentry->d_inode;
- xfs_dentry_to_name(&name, dentry);
+ error = xfs_dentry_to_name(dir, &name, dentry);
+ if (unlikely(error))
+ return -error;
error = xfs_remove(XFS_I(dir), &name, XFS_I(inode));
if (likely(!error)) {
@@ -595,7 +613,9 @@ xfs_vn_symlink(
mode = S_IFLNK |
(irix_symlink_mode ? 0777 & ~current->fs->umask : S_IRWXUGO);
- xfs_dentry_to_name(&name, dentry);
+ error = xfs_dentry_to_name(dir, &name, dentry);
+ if (unlikely(error))
+ goto out;
error = xfs_symlink(XFS_I(dir), &name, symname, mode, &cip, NULL);
if (unlikely(error))
@@ -627,7 +647,9 @@ xfs_vn_rmdir(
struct xfs_name name;
int error;
- xfs_dentry_to_name(&name, dentry);
+ error = xfs_dentry_to_name(dir, &name, dentry);
+ if (unlikely(error))
+ return -error;
error = xfs_rmdir(XFS_I(dir), &name, XFS_I(inode));
if (likely(!error)) {
@@ -649,8 +671,12 @@ xfs_vn_rename(
struct xfs_name nname;
int error;
- xfs_dentry_to_name(&oname, odentry);
- xfs_dentry_to_name(&nname, ndentry);
+ error = xfs_dentry_to_name(odir, &oname, odentry);
+ if (unlikely(error))
+ return -error;
+ error = xfs_dentry_to_name(ndir, &nname, ndentry);
+ if (unlikely(error))
+ return -error;
error = xfs_rename(XFS_I(odir), &oname, XFS_I(odentry->d_inode),
XFS_I(ndir), &nname, new_inode ?
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
@@ -124,6 +124,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 +319,8 @@ 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;
} else if (!strcmp(this_char, MNTOPT_NOQUOTA)) {
args->flags &= ~(XFSMNT_UQUOTAENF|XFSMNT_UQUOTA);
args->flags &= ~(XFSMNT_GQUOTAENF|XFSMNT_GQUOTA);
@@ -458,6 +461,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 }
@@ -566,7 +570,7 @@ xfs_set_inodeops(
inode->i_mapping->a_ops = &xfs_address_space_operations;
break;
case S_IFDIR:
- if (xfs_sb_version_hasoldci(&XFS_M(inode->i_sb)->m_sb))
+ if (XFS_M(inode->i_sb)->m_flags & XFS_MOUNT_CILOOKUP)
inode->i_op = &xfs_dir_ci_inode_operations;
else
inode->i_op = &xfs_dir_inode_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
@@ -30,6 +30,12 @@
#define XFS_SECURITY_STRING "security attributes, "
+#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
@@ -60,6 +66,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
@@ -104,7 +105,9 @@ ktrace_t *xfs_attr_trace_buf;
STATIC int
xfs_attr_name_to_xname(
struct xfs_name *xname,
- const char *aname)
+ const char *aname,
+ xfs_inode_t *ip,
+ int flags)
{
if (!aname)
return EINVAL;
@@ -113,6 +116,10 @@ xfs_attr_name_to_xname(
if (xname->len >= MAXNAMELEN)
return EFAULT; /* match IRIX behaviour */
+ /* only enforce unicode on user namespace attr names */
+ if (xfs_sb_version_hasunicode(&ip->i_mount->m_sb) &&
+ (flags & (ATTR_ROOT | ATTR_SECURE)) == 0)
+ return xfs_unicode_validate(xname);
return 0;
}
@@ -186,7 +193,7 @@ xfs_attr_get(
if (XFS_FORCED_SHUTDOWN(ip->i_mount))
return(EIO);
- error = xfs_attr_name_to_xname(&xname, name);
+ error = xfs_attr_name_to_xname(&xname, name, ip, flags);
if (error)
return error;
@@ -449,7 +456,7 @@ xfs_attr_set(
if (XFS_FORCED_SHUTDOWN(dp->i_mount))
return (EIO);
- error = xfs_attr_name_to_xname(&xname, name);
+ error = xfs_attr_name_to_xname(&xname, name, dp, flags);
if (error)
return error;
@@ -596,7 +603,7 @@ xfs_attr_remove(
if (XFS_FORCED_SHUTDOWN(dp->i_mount))
return (EIO);
- error = xfs_attr_name_to_xname(&xname, name);
+ error = xfs_attr_name_to_xname(&xname, name, dp, flags);
if (error)
return error;
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,23 +1530,29 @@ xfs_da_hashname(const uchar_t *name, int
}
}
-xfs_dacmp_t
-xfs_da_compname(const char *name1, int len1, const char *name2, int len2)
-{
- return (len1 == len2 && memcmp(name1, name2, len1) == 0) ?
- XFS_CMP_EXACT : XFS_CMP_DIFFERENT;
-}
-
-static xfs_dahash_t
+STATIC xfs_dahash_t
xfs_default_hashname(
+ struct xfs_inode *inode,
struct xfs_name *name)
{
return xfs_da_hashname(name->name, name->len);
}
+STATIC xfs_dacmp_t
+xfs_default_compname(
+ struct xfs_inode *inode,
+ const char *name1,
+ int len1,
+ const char *name2,
+ int len2)
+{
+ return xfs_da_compname(name1, len1, name2, len2);
+}
+
+
const struct xfs_nameops xfs_default_nameops = {
.hashname = xfs_default_hashname,
- .compname = xfs_da_compname
+ .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,8 +215,9 @@ typedef struct xfs_da_state {
* Name ops for directory and/or attr name operations
*/
struct xfs_nameops {
- xfs_dahash_t (*hashname)(struct xfs_name *);
- xfs_dacmp_t (*compname)(const char *, int, const char *, int);
+ xfs_dahash_t (*hashname)(struct xfs_inode *, struct xfs_name *);
+ xfs_dacmp_t (*compname)(struct xfs_inode *, const char *, int,
+ const char *, int);
};
@@ -267,8 +268,13 @@ int xfs_da_shrink_inode(xfs_da_args_t *a
xfs_dabuf_t *dead_buf);
uint xfs_da_hashname(const uchar_t *name_string, int name_length);
-xfs_dacmp_t xfs_da_compname(const char *name1, int len1,
- const char *name2, int len2);
+
+static inline xfs_dacmp_t
+xfs_da_compname(const char *name1, int len1, const char *name2, int len2)
+{
+ return (len1 == len2 && memcmp(name1, name2, len1) == 0) ?
+ XFS_CMP_EXACT : XFS_CMP_DIFFERENT;
+}
xfs_da_state_t *xfs_da_state_alloc(void);
Index: kern_ci/fs/xfs/xfs_dir2.c
===================================================================
--- kern_ci.orig/fs/xfs/xfs_dir2.c
+++ kern_ci/fs/xfs/xfs_dir2.c
@@ -43,12 +43,15 @@
#include "xfs_dir2_trace.h"
#include "xfs_error.h"
#include "xfs_vnodeops.h"
+#include "xfs_unicode.h"
struct xfs_name xfs_name_dotdot = {"..", 2};
kmem_zone_t *xfs_name_zone;
extern const struct xfs_nameops xfs_default_nameops;
+extern const struct xfs_nameops xfs_unicode_nameops;
+extern const struct xfs_nameops xfs_unicode_ci_nameops;
/*
* V1/OLDCI case-insensitive support for directories that was used in IRIX.
@@ -57,6 +60,7 @@ extern const struct xfs_nameops xfs_defa
*/
STATIC xfs_dahash_t
xfs_ascii_ci_hashname(
+ struct xfs_inode *inode,
struct xfs_name *name)
{
xfs_dahash_t hash;
@@ -70,6 +74,7 @@ xfs_ascii_ci_hashname(
STATIC xfs_dacmp_t
xfs_ascii_ci_compname(
+ struct xfs_inode *inode,
const char *name1,
int len1,
const char *name2,
@@ -93,7 +98,7 @@ xfs_ascii_ci_compname(
return result;
}
-static struct xfs_nameops xfs_ascii_ci_nameops = {
+static const struct xfs_nameops xfs_ascii_ci_nameops = {
.hashname = xfs_ascii_ci_hashname,
.compname = xfs_ascii_ci_compname,
};
@@ -117,10 +122,17 @@ xfs_dir_mount(
(mp->m_dirblksize - (uint)sizeof(xfs_da_node_hdr_t)) /
(uint)sizeof(xfs_da_node_entry_t);
mp->m_dir_magicpct = (mp->m_dirblksize * 37) / 100;
- if (xfs_sb_version_hasoldci(&mp->m_sb))
- mp->m_dirnameops = &xfs_ascii_ci_nameops;
- else
- mp->m_dirnameops = &xfs_default_nameops;
+ if (xfs_sb_version_hasunicode(&mp->m_sb)) {
+ if (mp->m_flags & XFS_MOUNT_CILOOKUP)
+ mp->m_dirnameops = &xfs_unicode_ci_nameops;
+ else
+ mp->m_dirnameops = &xfs_unicode_nameops;
+ } else {
+ if (mp->m_flags & XFS_MOUNT_CILOOKUP)
+ mp->m_dirnameops = &xfs_ascii_ci_nameops;
+ else
+ mp->m_dirnameops = &xfs_default_nameops;
+ }
}
/*
@@ -220,7 +232,7 @@ xfs_dir_createname(
args.name = name->name;
args.namelen = name->len;
- args.hashval = dp->i_mount->m_dirnameops->hashname(name);
+ args.hashval = dp->i_mount->m_dirnameops->hashname(dp, name);
args.inumber = inum;
args.dp = dp;
args.firstblock = first;
@@ -269,7 +281,7 @@ xfs_dir_lookup(
args.name = name->name;
args.namelen = name->len;
- args.hashval = dp->i_mount->m_dirnameops->hashname(name);
+ args.hashval = dp->i_mount->m_dirnameops->hashname(dp, name);
args.dp = dp;
args.whichfork = XFS_DATA_FORK;
args.trans = tp;
@@ -332,7 +344,7 @@ xfs_dir_removename(
args.name = name->name;
args.namelen = name->len;
- args.hashval = dp->i_mount->m_dirnameops->hashname(name);
+ args.hashval = dp->i_mount->m_dirnameops->hashname(dp, name);
args.inumber = ino;
args.dp = dp;
args.firstblock = first;
@@ -415,7 +427,7 @@ xfs_dir_replace(
args.name = name->name;
args.namelen = name->len;
- args.hashval = dp->i_mount->m_dirnameops->hashname(name);
+ args.hashval = dp->i_mount->m_dirnameops->hashname(dp, name);
args.inumber = inum;
args.dp = dp;
args.firstblock = first;
@@ -463,7 +475,7 @@ xfs_dir_canenter(
args.name = name->name;
args.namelen = name->len;
- args.hashval = dp->i_mount->m_dirnameops->hashname(name);
+ args.hashval = dp->i_mount->m_dirnameops->hashname(dp, name);
args.dp = dp;
args.whichfork = XFS_DATA_FORK;
args.trans = tp;
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
@@ -709,7 +709,7 @@ xfs_dir2_block_lookup_int(
* and buffer. If it's the first case-insensitive match, store
* the index and buffer and continue looking for an exact match.
*/
- cmp = mp->m_dirnameops->compname(dep->name, dep->namelen,
+ cmp = mp->m_dirnameops->compname(dp, dep->name, dep->namelen,
args->name, args->namelen);
if (cmp != XFS_CMP_DIFFERENT && cmp != args->cmpresult) {
args->cmpresult = cmp;
@@ -1211,7 +1211,7 @@ xfs_dir2_sf_to_block(
name.name = sfep->name;
name.len = sfep->namelen;
blp[2 + i].hashval = cpu_to_be32(mp->m_dirnameops->
- hashname(&name));
+ hashname(dp, &name));
blp[2 + i].address = cpu_to_be32(xfs_dir2_byte_to_dataptr(mp,
(char *)dep - (char *)block));
offset = (int)((char *)(tagp + 1) - (char *)block);
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
@@ -1404,7 +1404,7 @@ xfs_dir2_leaf_lookup_int(
* and buffer. If it's the first case-insensitive match, store
* the index and buffer and continue looking for an exact match.
*/
- cmp = mp->m_dirnameops->compname(dep->name, dep->namelen,
+ cmp = mp->m_dirnameops->compname(dp, dep->name, dep->namelen,
args->name, args->namelen);
if (cmp != XFS_CMP_DIFFERENT && cmp != args->cmpresult) {
args->cmpresult = cmp;
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
@@ -626,7 +626,7 @@ xfs_dir2_leafn_lookup_for_entry(
* EEXIST immediately. If it's the first case-insensitive
* match, store the inode number and continue looking.
*/
- cmp = mp->m_dirnameops->compname(dep->name, dep->namelen,
+ cmp = mp->m_dirnameops->compname(dp, dep->name, dep->namelen,
args->name, args->namelen);
if (cmp != XFS_CMP_DIFFERENT && cmp != args->cmpresult) {
args->cmpresult = cmp;
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
@@ -861,7 +861,7 @@ xfs_dir2_sf_lookup(
* number. If it's the first case-insensitive match, store the
* inode number and continue looking for an exact match.
*/
- cmp = dp->i_mount->m_dirnameops->compname(
+ cmp = dp->i_mount->m_dirnameops->compname(dp,
sfep->name, sfep->namelen,
args->name, args->namelen);
if (cmp != XFS_CMP_DIFFERENT && cmp != args->cmpresult) {
Index: kern_ci/fs/xfs/xfs_fs.h
===================================================================
--- kern_ci.orig/fs/xfs/xfs_fs.h
+++ kern_ci/fs/xfs/xfs_fs.h
@@ -228,19 +228,20 @@ typedef struct xfs_fsop_resblks {
#define XFS_FSOP_GEOM_VERSION 0
-#define XFS_FSOP_GEOM_FLAGS_ATTR 0x0001 /* attributes in use */
-#define XFS_FSOP_GEOM_FLAGS_NLINK 0x0002 /* 32-bit nlink values */
-#define XFS_FSOP_GEOM_FLAGS_QUOTA 0x0004 /* quotas enabled */
-#define XFS_FSOP_GEOM_FLAGS_IALIGN 0x0008 /* inode alignment */
-#define XFS_FSOP_GEOM_FLAGS_DALIGN 0x0010 /* large data alignment */
-#define XFS_FSOP_GEOM_FLAGS_SHARED 0x0020 /* read-only shared */
-#define XFS_FSOP_GEOM_FLAGS_EXTFLG 0x0040 /* special extent flag */
-#define XFS_FSOP_GEOM_FLAGS_DIRV2 0x0080 /* directory version 2 */
-#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 */
+#define XFS_FSOP_GEOM_FLAGS_ATTR 0x00000001 /* attributes in use */
+#define XFS_FSOP_GEOM_FLAGS_NLINK 0x00000002 /* 32-bit nlink values */
+#define XFS_FSOP_GEOM_FLAGS_QUOTA 0x00000004 /* quotas enabled */
+#define XFS_FSOP_GEOM_FLAGS_IALIGN 0x00000008 /* inode alignment */
+#define XFS_FSOP_GEOM_FLAGS_DALIGN 0x00000010 /* large data alignment */
+#define XFS_FSOP_GEOM_FLAGS_SHARED 0x00000020 /* read-only shared */
+#define XFS_FSOP_GEOM_FLAGS_EXTFLG 0x00000040 /* special extent flag */
+#define XFS_FSOP_GEOM_FLAGS_DIRV2 0x00000080 /* directory version 2 */
+#define XFS_FSOP_GEOM_FLAGS_LOGV2 0x00000100 /* log format version 2 */
+#define XFS_FSOP_GEOM_FLAGS_SECTOR 0x00000200 /* sector sizes >1BB */
+#define XFS_FSOP_GEOM_FLAGS_ATTR2 0x00000400 /* inline attr rework */
+#define XFS_FSOP_GEOM_FLAGS_DIRV2CI 0x00001000 /* ASCII only CI names */
+#define XFS_FSOP_GEOM_FLAGS_LAZYSB 0x00004000 /* lazy superblock cntrs */
+#define XFS_FSOP_GEOM_FLAGS_UNICODE 0x00010000 /* 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 int 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);
@@ -320,7 +323,18 @@ xfs_mount_validate_sb(
PAGE_SIZE);
return XFS_ERROR(ENOSYS);
}
-
+#ifndef CONFIG_XFS_UNICODE
+ /*
+ * If Unicode config is set to N, don't mount Unicode filesystems.
+ */
+ if (xfs_sb_version_hasmorebits(sbp) &&
+ (sbp->sb_features2 & XFS_SB_VERSION2_UNICODEBIT)) {
+ xfs_fs_mount_cmn_err(flags,
+ "file system is marked as Unicode but support is "
+ "not enabled in the driver.");
+ return XFS_ERROR(ENOSYS);
+ }
+#endif
return 0;
}
@@ -452,6 +466,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);
}
/*
@@ -1187,6 +1202,15 @@ xfs_mountfs(
}
/*
+ * Load in unicode case folding table from disk
+ */
+ 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)) {
@@ -1244,14 +1268,15 @@ xfs_mountfs(
return 0;
- error4:
+error4:
/*
* Free up the root inode.
*/
IRELE(rip);
- error3:
+ xfs_unicode_free_cft(mp->m_cft);
+error3:
xfs_log_unmount_dealloc(mp);
- error2:
+error2:
for (agno = 0; agno < sbp->sb_agcount; agno++)
if (mp->m_perag[agno].pagb_list)
kmem_free(mp->m_perag[agno].pagb_list,
@@ -1259,7 +1284,7 @@ xfs_mountfs(
kmem_free(mp->m_perag, sbp->sb_agcount * sizeof(xfs_perag_t));
mp->m_perag = NULL;
/* FALLTHROUGH */
- error1:
+error1:
if (uuid_mounted)
xfs_uuid_unmount(mp);
xfs_freesb(mp);
@@ -1985,7 +2010,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 */
const struct xfs_nameops *m_dirnameops; /* vector of dir name ops */
+ struct xfs_cft *m_cft; /* unicode case folding 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.
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,12 @@ struct xfs_mount;
#define XFS_SB_VERSION2_LAZYSBCOUNTBIT 0x00000002 /* Superblk counters */
#define XFS_SB_VERSION2_RESERVED4BIT 0x00000004
#define XFS_SB_VERSION2_ATTR2BIT 0x00000008 /* Inline attr rework */
+#define XFS_SB_VERSION2_UNICODEBIT 0x00000020 /* Unicode names */
#define XFS_SB_VERSION2_OKREALFBITS \
(XFS_SB_VERSION2_LAZYSBCOUNTBIT | \
- XFS_SB_VERSION2_ATTR2BIT)
+ XFS_SB_VERSION2_ATTR2BIT | \
+ XFS_SB_VERSION2_UNICODEBIT)
#define XFS_SB_VERSION2_OKSASHFBITS \
(0)
#define XFS_SB_VERSION2_OKREALBITS \
@@ -156,6 +158,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 +228,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 +250,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 +276,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 +284,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)
/*
@@ -481,6 +486,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
+static inline int xfs_sb_version_hasunicode(xfs_sb_t *sbp) { return 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,584 @@
+/*
+ * 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"
+
+/*
+ * XFS Unicode performs case folding for hash generation for the on-disk
+ * directory information using the Unicode 5.0 standard locale independent
+ * case-folding table http://www.unicode.org/Public/UNIDATA/CaseFolding.txt
+ *
+ * More info also at http://unicode.org/reports/tr21/tr21-5.html
+ *
+ * XFS Unicode only supports the Basic Multilingual Plane (BMP) of the
+ * Unicode standard. The other planes currently are only used for
+ * ancient/obsolete languages and various symbols which aren't really
+ * appropriate for filenames (and it keeps the implementation simpler,
+ * especially as wchar_t can be only 2 bytes).
+ *
+ * As we don't need to map every character in the entire BMP, we map chunks
+ * in 256 character blobs that contain characters to be case-folded. At the
+ * moment, only 14 of these chunks have characters that are case-folded.
+ *
+ * So, the first 256 characters in table encodes the most significant byte
+ * to the folded character in the table. If it is zero, then there is no
+ * folding for that character.
+ * -> folded char = table[table[char / 256] + char % 256]
+ *
+ * As this table uses a 1:n mapping for folding characters, and n is
+ * currently no more than 3, MAX_FOLD_CHARS is set to 4 to support minor
+ * changes to the table that may occur in future versions of unicode.
+ *
+ * As XFS Unicode only supports the BMP, UTF-16 surrogates U+D800 to U+DFFF
+ * are currently invalid, but reserved for possible support beyond the BMP.
+ * The private use area U+E000 to U+F8FF is also invalid for filenames.
+ *
+ * As U+E000 to U+F8FF will never be used for filenames, we can use the
+ * space from U+E000 to U+EFFF to refer to the 1:n mapping on-disk.
+ * So, this range on-disk is used to specify the addition multi-character
+ * case-folding conversions. Each value of "n" in the 1:n format can
+ * store up to 1024 sequences, and currently up to 4 tables. As mentioned
+ * above, only two additional tables currently exist - 1:2 and 1:3. The
+ * lower 10 bits is the index to the multi-character folding in each of
+ * these tables (byte index = (char & 0x3ff) * n * 2).
+ *
+ * So, in summary, the following Unicode ranges have special purposes:
+ * U+D800 - U+DFFF : UTF-16 surrogates - unsupported
+ * U+E000 - U+E3FF : index to a two character sequence
+ * U+E400 - U+E7FF : index to a three character sequence
+ * U+E800 - U+EBFF : index to a four character sequence (currently none)
+ * U+EC00 - U+EFFF : index to a five character sequence (unsupported ATM)
+ * U+F000 - U+F8FF : reserved for future use
+ *
+ * Like the other data structures in XFS, the 2-byte (UTF-16) casefolding
+ * characters are in big-endian format.
+ */
+
+#define CHARS_PER_CHUNK 256 /* table chunk size */
+
+#define MAX_FOLD_CHARS 4 /* maximum single sequence supported (1:n) */
+
+/* multi-character sequence encoding values */
+#define MC_MASK 0xf000 /* bits in character to test for MC sequence */
+#define MC_MAGIC 0xe000 /* if masked value is this, then MC sequence */
+#define MC_PER_TABLE 1024 /* max # of MC chars per sequence table */
+
+static __uint16_t *
+xfs_cft_ptr(
+ const struct xfs_cft *cft,
+ int index)
+{
+ return (__uint16_t *)((char *)cft + cft->table_offset[index]);
+}
+
+/*
+ * xfs_casefold takes the 2-byte unicode character and converts it into a
+ * locale independent case folded character sequence. Returns the number of
+ * characters in the folded sequence.
+ */
+static int
+xfs_casefold(
+ const struct xfs_cft *cft,
+ __uint16_t c,
+ __uint16_t *fc)
+{
+ __uint16_t *table = xfs_cft_ptr(cft, 0);
+ __uint16_t tmp = table[c / CHARS_PER_CHUNK];
+ int n;
+
+ if (!tmp) { /* if no table index, no mapping */
+ *fc = c;
+ return 1;
+ }
+ tmp = table[tmp + c % CHARS_PER_CHUNK];
+ if ((tmp & MC_MASK) != MC_MAGIC) {
+ /* 1:1 character mapping if not U+Exxx */
+ *fc = tmp;
+ return 1;
+ }
+ /* 1:n character mapping if tmp is U+Exxx */
+ n = ((tmp & ~MC_MASK) / MC_PER_TABLE) + 2;
+ ASSERT(n < cft->num_tables);
+ table = xfs_cft_ptr(cft, n - 1) + ((tmp % MC_PER_TABLE) * n);
+
+ memcpy(fc, table, sizeof(__uint16_t) * n);
+
+ return n;
+}
+
+/*
+ * xfs_utf8_casefold converts a single UTF-8 sequence into a wide character
+ * and calls xfs_casefold to convert that character into a case-folded
+ * sequence for comparing and hash generation.
+ */
+static int
+xfs_utf8_casefold(
+ const struct xfs_cft *cft,
+ const char **name,
+ int *namelen,
+ __uint16_t *fc)
+{
+ wchar_t uc;
+
+ if (*namelen == 0)
+ return 0;
+
+ if (**name & 0x80) {
+ /* All extended UTF-8 sequences have the high-bit set */
+ int n = utf8_mbtowc(&uc, *name, *namelen);
+ if (n < 0) {
+ (*namelen)--;
+ *fc = *(*name)++;
+ return 1;
+ }
+ *name += n;
+ *namelen -= n;
+ } else {
+ /* otherwise, standard ASCII */
+ 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).
+ */
+STATIC xfs_dahash_t
+xfs_unicode_hashname(
+ xfs_inode_t *inode,
+ struct xfs_name *name)
+{
+ const char *n = name->name;
+ int len = name->len;
+ xfs_dahash_t hash = 0;
+ __uint16_t fc[MAX_FOLD_CHARS];
+ int nfc;
+ int i;
+
+ while (len > 0) {
+ nfc = xfs_utf8_casefold(inode->i_mount->m_cft, &n, &len, 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(
+ const struct xfs_cft *cft,
+ const char *name1,
+ int len1,
+ const char *name2,
+ int len2)
+{
+ __uint16_t fc1[MAX_FOLD_CHARS], fc2[MAX_FOLD_CHARS];
+ __uint16_t *fc1p, *fc2p;
+ int nfc1, nfc2;
+
+ nfc1 = xfs_utf8_casefold(cft, &name1, &len1, fc1);
+ fc1p = fc1;
+ nfc2 = xfs_utf8_casefold(cft, &name2, &len2, fc2);
+ fc2p = fc2;
+
+ while (nfc1 > 0 && nfc2 > 0) {
+ if (*fc1p != *fc2p)
+ return XFS_CMP_DIFFERENT;
+ if (!--nfc1) {
+ nfc1 = xfs_utf8_casefold(cft, &name1, &len1, fc1);
+ fc1p = fc1;
+ } else
+ fc1p++;
+ if (!--nfc2) {
+ nfc2 = xfs_utf8_casefold(cft, &name2, &len2, fc2);
+ fc2p = fc2;
+ } else
+ fc2p++;
+ }
+ 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.
+ */
+STATIC xfs_dacmp_t
+xfs_unicode_compname(
+ xfs_inode_t *inode,
+ const char *name1,
+ int len1,
+ const char *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 char *name1,
+ int namelen1,
+ const char *name2,
+ int namelen2)
+{
+ return xfs_da_compname(name1, namelen1, name2, namelen2);
+}
+
+const struct xfs_nameops xfs_unicode_nameops = {
+ .hashname = xfs_unicode_hashname,
+ .compname = xfs_default_compname,
+};
+
+const struct xfs_nameops xfs_unicode_ci_nameops = {
+ .hashname = xfs_unicode_hashname,
+ .compname = xfs_unicode_compname,
+};
+
+int
+xfs_unicode_validate(
+ const struct xfs_name *name)
+{
+ wchar_t uc;
+ int i, nlen;
+
+ for (i = 0; i < name->len; i += nlen) {
+ if (name->name[i] >= 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->name + i, name->len - 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 {
+ struct xfs_cft *table;
+ int size;
+ int refcount;
+};
+
+static mutex_t cft_lock;
+static int cft_size;
+static struct cft_item *cft_list;
+
+STATIC struct xfs_cft *
+add_cft(
+ struct xfs_dcft *dcft,
+ int size)
+{
+ int found = 0;
+ int i, j;
+ struct xfs_cft *cft = NULL;
+ __be16 *duc;
+ __uint16_t *uc;
+ struct cft_item *tmp_list;
+
+ 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++;
+ goto out;
+ }
+ }
+
+ tmp_list = kmem_realloc(cft_list,
+ (cft_size + 1) * sizeof(struct cft_item),
+ cft_size * sizeof(struct cft_item), KM_MAYFAIL);
+ if (!tmp_list)
+ goto out;
+ cft_list = tmp_list;
+
+ cft = vmalloc(size);
+ if (!cft)
+ goto out;
+ 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 = (__be16 *)((char *)dcft + be32_to_cpu(dcft->table_offset[0]));
+ for (i = 0; i < j; i++)
+ uc[i] = be16_to_cpu(duc[i]);
+
+ cft_list[cft_size].table = cft;
+ cft_list[cft_size].size = size;
+ cft_list[cft_size].refcount = 1;
+ cft_size++;
+out:
+ mutex_unlock(&cft_lock);
+ return cft;
+}
+
+STATIC void
+remove_cft(
+ const struct xfs_cft *cft)
+{
+ int i;
+
+ mutex_lock(&cft_lock);
+
+ for (i = 0; i < cft_size; i++) {
+ if (cft_list[i].table != cft)
+ continue;
+ ASSERT(cft_list[i].refcount > 0);
+ cft_list[i].refcount--;
+ if (cft_list[i].refcount != 0)
+ break;
+ /* no more users of the table, free it */
+ vfree(cft_list[i].table);
+ cft_size--;
+ if (i < cft_size)
+ memmove(cft_list + i, cft_list + i + 1,
+ sizeof(struct cft_item) * (cft_size - i));
+ break;
+ }
+
+ mutex_unlock(&cft_lock);
+}
+
+
+int
+xfs_unicode_read_cft(
+ xfs_mount_t *mp)
+{
+ int error;
+ xfs_inode_t *cftip;
+ int size;
+ int next;
+ int nmap;
+ xfs_bmbt_irec_t *mapp = NULL;
+ int n;
+ int byte_cnt;
+ xfs_buf_t *bp;
+ struct xfs_dcft *dcft = NULL;
+ char *tmp;
+
+ if (!xfs_sb_version_hasunicode(&mp->m_sb))
+ return 0; /* not needed for this filesystem */
+
+ 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;
+ next = cftip->i_d.di_nextents;
+
+ error = ENOMEM;
+ dcft = vmalloc(size);
+ if (!dcft)
+ goto out;
+
+ nmap = next;
+ mapp = kmem_alloc(next * sizeof(xfs_bmbt_irec_t), KM_MAYFAIL);
+ if (!mapp)
+ goto out;
+
+ error = xfs_bmapi(NULL, cftip, 0, next, 0, NULL, 0, mapp, &nmap,
+ NULL, NULL);
+ if (error)
+ goto out;
+
+ tmp = (char *)dcft;
+ 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(tmp, XFS_BUF_PTR(bp), byte_cnt);
+ tmp += 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, next * sizeof(xfs_bmbt_irec_t));
+ vfree(dcft);
+
+ return error;
+}
+
+void
+xfs_unicode_free_cft(
+ const struct xfs_cft *cft)
+{
+ if (cft)
+ remove_cft(cft);
+}
+
+void
+xfs_unicode_init(void)
+{
+ mutex_init(&cft_lock);
+}
+
+void
+xfs_unicode_uninit(void)
+{
+ ASSERT(cft_size == 0);
+
+ kmem_free(cft_list, cft_size * sizeof(struct cft_item));
+ cft_list = NULL;
+
+ mutex_destroy(&cft_lock);
+}
Index: kern_ci/fs/xfs/xfs_unicode.h
===================================================================
--- /dev/null
+++ kern_ci/fs/xfs/xfs_unicode.h
@@ -0,0 +1,78 @@
+/*
+ * 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.
+ */
+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];/* offsets to tables from
+ start of this structure */
+};
+
+/*
+ * Case Fold Table - in core version. Must match the ondisk version above.
+ */
+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];/* offsets to tables from
+ start of this structure */
+ /* 16-bit array tables immediately follow */
+};
+
+#ifdef CONFIG_XFS_UNICODE
+
+void xfs_unicode_init(void);
+void xfs_unicode_uninit(void);
+
+int xfs_unicode_validate(const struct xfs_name *name);
+
+int xfs_unicode_read_cft(struct xfs_mount *mp);
+void xfs_unicode_free_cft(const struct xfs_cft *cft);
+
+#else
+
+static inline void xfs_unicode_init(void) {}
+static inline void xfs_unicode_uninit(void) {}
+static inline int xfs_unicode_validate(const struct xfs_name *name)
+{
+ return 0;
+}
+static inline int xfs_unicode_read_cft(struct xfs_mount *mp)
+{
+ return EOPNOTSUPP;
+}
+static inline void xfs_unicode_free_cft(const struct xfs_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
@@ -84,6 +85,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
@@ -159,6 +161,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);
@@ -401,6 +404,18 @@ xfs_finish_flags(
mp->m_qflags |= XFS_OQUOTA_ENFD;
}
+ if (xfs_sb_version_hasoldci(&mp->m_sb))
+ mp->m_flags |= XFS_MOUNT_CILOOKUP;
+ if (xfs_sb_version_hasunicode(&mp->m_sb)) {
+ if (ap->flags2 & XFSMNT2_CILOOKUP)
+ mp->m_flags |= XFS_MOUNT_CILOOKUP;
+ } else {
+ if (ap->flags2 & XFSMNT2_CILOOKUP) {
+ cmn_err(CE_WARN,
+ "XFS: can't do case-insensitive mount on non-Unicode filesystem");
+ return XFS_ERROR(EINVAL);
+ }
+ }
return 0;
}
--
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH 1/4] XFS: Name operation vector for hash and compare
2008-04-21 8:31 ` [PATCH 1/4] XFS: Name operation vector for hash and compare Barry Naujok
@ 2008-04-21 8:42 ` Christoph Hellwig
0 siblings, 0 replies; 16+ messages in thread
From: Christoph Hellwig @ 2008-04-21 8:42 UTC (permalink / raw)
To: Barry Naujok; +Cc: xfs, linux-fsdevel
> /*
> + * 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;
Please don't add a typedef for this one, just use the enum type
directly.
Except for this this patch looks good to me.
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH 2/4] XFS: Return case-insensitive match for dentry cache
2008-04-21 8:31 ` [PATCH 2/4] XFS: Return case-insensitive match for dentry cache Barry Naujok
@ 2008-04-21 8:59 ` Christoph Hellwig
2008-04-21 9:46 ` Christoph Hellwig
` (2 more replies)
0 siblings, 3 replies; 16+ messages in thread
From: Christoph Hellwig @ 2008-04-21 8:59 UTC (permalink / raw)
To: Barry Naujok; +Cc: xfs, linux-fsdevel
> +STATIC struct dentry *
> +xfs_ci_dentry_update(
> + struct dentry *dent,
> + struct inode *dent_inode,
> + struct xfs_name *name)
> +{
> + int err;
> + struct dentry *real_dent;
> + struct dentry *new_dent;
> + struct qstr nls_name;
This helper should go into fs/dcache.c with a slightly move descritive
name (d_add_ci?).
Also the naming is rather odd, please replace every occurance of
dent with dentry, and the variable dent_inode should be just inode.
Also when moving this to dcache.c please provide a nice big kerneldoc
comment describing it like Anton did for the ntfs lookup instance.
of the
> + /*
> + * following code from ntfs_lookup() in fs/ntfs/namei.c
> + */
Not a very helpful comment :)
> + new_dent = d_splice_alias(dent_inode, real_dent);
> + if (new_dent)
> + dput(real_dent);
> + else
> + new_dent = real_dent;
> + return new_dent;
I think this would be more readable as
if (new_dentry) {
dput(real_dentry);
return new_dentry;
}
return real_dentry;
> + /* Matching dentry exists, check if it is negative. */
> + if (real_dent->d_inode) {
> + if (unlikely(real_dent->d_inode != dent_inode)) {
> + /* This can happen because bad inodes are unhashed. */
> + BUG_ON(!is_bad_inode(dent_inode));
> + BUG_ON(!is_bad_inode(real_dent->d_inode));
> + }
Shouldn't this be a can't above?
> + * Already have the inode and the dentry attached, decrement
> + * the reference count to balance the xfs_lookup() we did
> + * earlier on. We found the dentry using d_lookup() so it
> + * cannot be disconnected and thus we do not need to worry
> + * about any NFS/disconnectedness issues here.
s/xfs_lookup/iget/
> +kmem_zone_t *xfs_name_zone;
What about just using kmalloc here? We know the length of the name
anyway, so there is no point of allocating the maximum possible size.
> error = xfs_iget(dp->i_mount, NULL, inum, 0, 0, ipp, 0);
> - if (error)
> + if (error) {
> + if (ci_match && *ci_match)
> + xfs_name_free(name->name);
> goto out;
> + }
All the allocation and freeing for ci_match looks odd and error prone
to me. I think the low-level directory code should never allocate
args->value unless it's explicitly asked for a CI match. That way
there's only one place in xfs_ci_lookup to free it either.
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH 3/4] XFS: ASCII case-insensitive support
2008-04-21 8:31 ` [PATCH 3/4] XFS: ASCII case-insensitive support Barry Naujok
@ 2008-04-21 9:03 ` Christoph Hellwig
0 siblings, 0 replies; 16+ messages in thread
From: Christoph Hellwig @ 2008-04-21 9:03 UTC (permalink / raw)
To: Barry Naujok; +Cc: xfs, linux-fsdevel
> +extern struct dentry_operations xfs_ci_dentry_operations;
this one is not used nor does the variable exist. Just kill it :)
> +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_BORGBIT);
> +}
Lots of superflous braces here. Should be:
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_BORGBIT);
}
(and yes, the other xfs_sb_version_ helpers need fixups like that
aswell)
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH 4/4] XFS: Unicode case-insensitive lookup implementation
2008-04-21 8:31 ` [PATCH 4/4] XFS: Unicode case-insensitive lookup implementation Barry Naujok
@ 2008-04-21 9:15 ` Christoph Hellwig
0 siblings, 0 replies; 16+ messages in thread
From: Christoph Hellwig @ 2008-04-21 9:15 UTC (permalink / raw)
To: Barry Naujok; +Cc: xfs, linux-fsdevel
The patch looks quite clean, but there still is the fundamental issue
of rejecting perfectly fine UNIX filenames in xfs_unicode_validate,
as discussed in the last round a Unix filename is any valid
NULL-terminated bytestream with only ".", "..", and "/" special-cased
and this patch breaks this assumptions.
In addition to that I think xfs_unicode.[ch] should be split into
a small xfs-specific part for reading the on-disk case-folding table
and implementing the actual nameops, and the generic code for case
folding and compares and case-folded strings should be moved into lib/
as it's quite generic.
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH 2/4] XFS: Return case-insensitive match for dentry cache
2008-04-21 8:59 ` Christoph Hellwig
@ 2008-04-21 9:46 ` Christoph Hellwig
2008-04-22 3:54 ` Barry Naujok
2008-04-21 21:13 ` David Chinner
2008-04-22 2:07 ` Barry Naujok
2 siblings, 1 reply; 16+ messages in thread
From: Christoph Hellwig @ 2008-04-21 9:46 UTC (permalink / raw)
To: Barry Naujok; +Cc: xfs, linux-fsdevel
> > +kmem_zone_t *xfs_name_zone;
>
> What about just using kmalloc here? We know the length of the name
> anyway, so there is no point of allocating the maximum possible size.
>
> > error = xfs_iget(dp->i_mount, NULL, inum, 0, 0, ipp, 0);
> > - if (error)
> > + if (error) {
> > + if (ci_match && *ci_match)
> > + xfs_name_free(name->name);
> > goto out;
> > + }
>
> All the allocation and freeing for ci_match looks odd and error prone
> to me. I think the low-level directory code should never allocate
> args->value unless it's explicitly asked for a CI match. That way
> there's only one place in xfs_ci_lookup to free it either.
Also the low-level name duplication code could be factored out a little
more ala:
/*
* If a case-insensitive match, allocate a buffer and copy the actual
* name into the buffer. Return it via args->value.
*/
void xfs_copy_ci_name(struct xfs_da_args *args, const char *name, int namelen)
{
if (args->return_ci_name && args->cmpresult == XFS_CMP_CASE) {
args->valuelen = namelen;
args->value = kmemdup(name, namelen, GFP_NOFS);
/* error handled in higher layers */
}
}
Instead of adding args->return_ci_name it might make sense to just
replace the last four chars there with an unsigned short lookup_flags
and just set bits in it.
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH 2/4] XFS: Return case-insensitive match for dentry cache
2008-04-21 8:59 ` Christoph Hellwig
2008-04-21 9:46 ` Christoph Hellwig
@ 2008-04-21 21:13 ` David Chinner
2008-04-22 4:53 ` Christoph Hellwig
2008-04-22 2:07 ` Barry Naujok
2 siblings, 1 reply; 16+ messages in thread
From: David Chinner @ 2008-04-21 21:13 UTC (permalink / raw)
To: Christoph Hellwig; +Cc: Barry Naujok, xfs, linux-fsdevel
On Mon, Apr 21, 2008 at 04:59:47AM -0400, Christoph Hellwig wrote:
> > +STATIC struct dentry *
> > +xfs_ci_dentry_update(
> > + struct dentry *dent,
> > + struct inode *dent_inode,
> > + struct xfs_name *name)
> > +{
> > + int err;
> > + struct dentry *real_dent;
> > + struct dentry *new_dent;
> > + struct qstr nls_name;
>
> This helper should go into fs/dcache.c with a slightly move descritive
> name (d_add_ci?).
>
> Also the naming is rather odd, please replace every occurance of
> dent with dentry, and the variable dent_inode should be just inode.
>
> Also when moving this to dcache.c please provide a nice big kerneldoc
> comment describing it like Anton did for the ntfs lookup instance.
Perhaps this can be done (moving to dcache.c) as a followup patch
series that also removes the duplicate code from ntfs and other
places. Similarly for all the NLS stuff...
Cheers,
Dave.
--
Dave Chinner
Principal Engineer
SGI Australian Software Group
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH 2/4] XFS: Return case-insensitive match for dentry cache
2008-04-21 8:59 ` Christoph Hellwig
2008-04-21 9:46 ` Christoph Hellwig
2008-04-21 21:13 ` David Chinner
@ 2008-04-22 2:07 ` Barry Naujok
2008-04-22 4:55 ` Christoph Hellwig
2 siblings, 1 reply; 16+ messages in thread
From: Barry Naujok @ 2008-04-22 2:07 UTC (permalink / raw)
To: Christoph Hellwig; +Cc: xfs, linux-fsdevel
On Mon, 21 Apr 2008 18:59:47 +1000, Christoph Hellwig <hch@infradead.org>
wrote:
>> +STATIC struct dentry *
>> +xfs_ci_dentry_update(
>> + struct dentry *dent,
>> + struct inode *dent_inode,
>> + struct xfs_name *name)
>> +{
>> + int err;
>> + struct dentry *real_dent;
>> + struct dentry *new_dent;
>> + struct qstr nls_name;
>
> This helper should go into fs/dcache.c with a slightly move descritive
> name (d_add_ci?).
>
> Also the naming is rather odd, please replace every occurance of
> dent with dentry, and the variable dent_inode should be just inode.
>
> Also when moving this to dcache.c please provide a nice big kerneldoc
> comment describing it like Anton did for the ntfs lookup instance.
> of the
>
>> + /*
>> + * following code from ntfs_lookup() in fs/ntfs/namei.c
>> + */
>
> Not a very helpful comment :)
>
>> + new_dent = d_splice_alias(dent_inode, real_dent);
>> + if (new_dent)
>> + dput(real_dent);
>> + else
>> + new_dent = real_dent;
>> + return new_dent;
>
> I think this would be more readable as
>
> if (new_dentry) {
> dput(real_dentry);
> return new_dentry;
> }
> return real_dentry;
>
>> + /* Matching dentry exists, check if it is negative. */
>> + if (real_dent->d_inode) {
>> + if (unlikely(real_dent->d_inode != dent_inode)) {
>> + /* This can happen because bad inodes are unhashed. */
>> + BUG_ON(!is_bad_inode(dent_inode));
>> + BUG_ON(!is_bad_inode(real_dent->d_inode));
>> + }
>
> Shouldn't this be a can't above?
>
>> + * Already have the inode and the dentry attached, decrement
>> + * the reference count to balance the xfs_lookup() we did
>> + * earlier on. We found the dentry using d_lookup() so it
>> + * cannot be disconnected and thus we do not need to worry
>> + * about any NFS/disconnectedness issues here.
>
> s/xfs_lookup/iget/
This whole function (other than the xfs_blah) is a direct copy of
Anton's code, I left the variable names etc the same.
>> +kmem_zone_t *xfs_name_zone;
>
> What about just using kmalloc here? We know the length of the name
> anyway, so there is no point of allocating the maximum possible size.
Not with the CI/NLS, the length could be different.
>> error = xfs_iget(dp->i_mount, NULL, inum, 0, 0, ipp, 0);
>> - if (error)
>> + if (error) {
>> + if (ci_match && *ci_match)
>> + xfs_name_free(name->name);
>> goto out;
>> + }
>
> All the allocation and freeing for ci_match looks odd and error prone
> to me. I think the low-level directory code should never allocate
> args->value unless it's explicitly asked for a CI match. That way
> there's only one place in xfs_ci_lookup to free it either.
Yes, I could do that, it is a neater, cleaner implementation. I did it
this way to only alloc as required rather than an alloc for every lookup.
Barry.
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH 2/4] XFS: Return case-insensitive match for dentry cache
2008-04-21 9:46 ` Christoph Hellwig
@ 2008-04-22 3:54 ` Barry Naujok
2008-04-22 4:52 ` Christoph Hellwig
0 siblings, 1 reply; 16+ messages in thread
From: Barry Naujok @ 2008-04-22 3:54 UTC (permalink / raw)
To: Christoph Hellwig; +Cc: xfs, linux-fsdevel
On Mon, 21 Apr 2008 19:46:41 +1000, Christoph Hellwig <hch@infradead.org>
wrote:
>> > +kmem_zone_t *xfs_name_zone;
>>
>> What about just using kmalloc here? We know the length of the name
>> anyway, so there is no point of allocating the maximum possible size.
>>
>> > error = xfs_iget(dp->i_mount, NULL, inum, 0, 0, ipp, 0);
>> > - if (error)
>> > + if (error) {
>> > + if (ci_match && *ci_match)
>> > + xfs_name_free(name->name);
>> > goto out;
>> > + }
>>
>> All the allocation and freeing for ci_match looks odd and error prone
>> to me. I think the low-level directory code should never allocate
>> args->value unless it's explicitly asked for a CI match. That way
>> there's only one place in xfs_ci_lookup to free it either.
>
> Also the low-level name duplication code could be factored out a little
> more ala:
>
> /*
> * If a case-insensitive match, allocate a buffer and copy the actual
> * name into the buffer. Return it via args->value.
> */
> void xfs_copy_ci_name(struct xfs_da_args *args, const char *name, int
> namelen)
> {
> if (args->return_ci_name && args->cmpresult == XFS_CMP_CASE) {
> args->valuelen = namelen;
> args->value = kmemdup(name, namelen, GFP_NOFS);
> /* error handled in higher layers */
> }
> }
>
> Instead of adding args->return_ci_name it might make sense to just
> replace the last four chars there with an unsigned short lookup_flags
> and just set bits in it.
There's already a "flags" field in xfs_da_args and only the low 16 bits
are currently used for attr operations.
We could use 5 of the high bits for these flags, or just add another
flags field (which are used for things other than lookup).
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH 2/4] XFS: Return case-insensitive match for dentry cache
2008-04-22 3:54 ` Barry Naujok
@ 2008-04-22 4:52 ` Christoph Hellwig
0 siblings, 0 replies; 16+ messages in thread
From: Christoph Hellwig @ 2008-04-22 4:52 UTC (permalink / raw)
To: Barry Naujok; +Cc: Christoph Hellwig, xfs, linux-fsdevel
On Tue, Apr 22, 2008 at 01:54:28PM +1000, Barry Naujok wrote:
> There's already a "flags" field in xfs_da_args and only the low 16 bits
> are currently used for attr operations.
>
> We could use 5 of the high bits for these flags, or just add another
> flags field (which are used for things other than lookup).
Either way is fine for me. The problem with the flags field is
that the values in there seem to be passed down from higher up, e.g.
the attr code so we'd have to be careful not to use any value twice.
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH 2/4] XFS: Return case-insensitive match for dentry cache
2008-04-21 21:13 ` David Chinner
@ 2008-04-22 4:53 ` Christoph Hellwig
0 siblings, 0 replies; 16+ messages in thread
From: Christoph Hellwig @ 2008-04-22 4:53 UTC (permalink / raw)
To: David Chinner; +Cc: Christoph Hellwig, Barry Naujok, xfs, linux-fsdevel
On Tue, Apr 22, 2008 at 07:13:01AM +1000, David Chinner wrote:
> Perhaps this can be done (moving to dcache.c) as a followup patch
> series that also removes the duplicate code from ntfs and other
> places. Similarly for all the NLS stuff...
It's not like this one is changing core code, just adding a single
function should be fine from the XFS tree without any bigger
implications. Switching over ntfs should be a separate patch of course.
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH 2/4] XFS: Return case-insensitive match for dentry cache
2008-04-22 2:07 ` Barry Naujok
@ 2008-04-22 4:55 ` Christoph Hellwig
0 siblings, 0 replies; 16+ messages in thread
From: Christoph Hellwig @ 2008-04-22 4:55 UTC (permalink / raw)
To: Barry Naujok; +Cc: Christoph Hellwig, xfs, linux-fsdevel
On Tue, Apr 22, 2008 at 12:07:01PM +1000, Barry Naujok wrote:
>> What about just using kmalloc here? We know the length of the name
>> anyway, so there is no point of allocating the maximum possible size.
>
> Not with the CI/NLS, the length could be different.
Well, even then we know the length to allocate because it needs to
be passed up in the nls_name. For the utf8 case the allocated space
for the string might just be different from the number of characters.
^ permalink raw reply [flat|nested] 16+ messages in thread
end of thread, other threads:[~2008-04-22 4:55 UTC | newest]
Thread overview: 16+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-04-21 8:31 [PATCH 0/4] XFS: case-insensitive lookup and Unicode support Barry Naujok
2008-04-21 8:31 ` [PATCH 1/4] XFS: Name operation vector for hash and compare Barry Naujok
2008-04-21 8:42 ` Christoph Hellwig
2008-04-21 8:31 ` [PATCH 2/4] XFS: Return case-insensitive match for dentry cache Barry Naujok
2008-04-21 8:59 ` Christoph Hellwig
2008-04-21 9:46 ` Christoph Hellwig
2008-04-22 3:54 ` Barry Naujok
2008-04-22 4:52 ` Christoph Hellwig
2008-04-21 21:13 ` David Chinner
2008-04-22 4:53 ` Christoph Hellwig
2008-04-22 2:07 ` Barry Naujok
2008-04-22 4:55 ` Christoph Hellwig
2008-04-21 8:31 ` [PATCH 3/4] XFS: ASCII case-insensitive support Barry Naujok
2008-04-21 9:03 ` Christoph Hellwig
2008-04-21 8:31 ` [PATCH 4/4] XFS: Unicode case-insensitive lookup implementation Barry Naujok
2008-04-21 9:15 ` Christoph Hellwig
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).