* [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 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