aboutsummaryrefslogtreecommitdiffstats
path: root/fs/xfs
diff options
context:
space:
mode:
authorBarry Naujok <bnaujok@sgi.com>2008-05-21 02:41:01 -0400
committerNiv Sardi <xaiki@debian.org>2008-07-28 02:58:36 -0400
commit5163f95a08cbf058ae16452c2242c5600fedc32e (patch)
tree5d6b905f7031144a62fb1fa17ba3106d99268003 /fs/xfs
parent68f34d5107dbace3d14a1c2f060fc8941894879c (diff)
[XFS] Name operation vector for hash and compare
Adds two pieces of functionality for the basis of case-insensitive support in XFS: 1. A comparison result enumerated type: xfs_dacmp. 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 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. SGI-PV: 981519 SGI-Modid: xfs-linux-melb:xfs-kern:31205a Signed-off-by: Barry Naujok <bnaujok@sgi.com> Signed-off-by: Christoph Hellwig <hch@infradead.org>
Diffstat (limited to 'fs/xfs')
-rw-r--r--fs/xfs/xfs_da_btree.c22
-rw-r--r--fs/xfs/xfs_da_btree.h22
-rw-r--r--fs/xfs/xfs_dir2.c12
-rw-r--r--fs/xfs/xfs_dir2_block.c33
-rw-r--r--fs/xfs/xfs_dir2_data.c5
-rw-r--r--fs/xfs/xfs_dir2_leaf.c60
-rw-r--r--fs/xfs/xfs_dir2_node.c23
-rw-r--r--fs/xfs/xfs_dir2_sf.c62
-rw-r--r--fs/xfs/xfs_mount.h2
9 files changed, 174 insertions, 67 deletions
diff --git a/fs/xfs/xfs_da_btree.c b/fs/xfs/xfs_da_btree.c
index 294780427abb..ae4b18c7726b 100644
--- a/fs/xfs/xfs_da_btree.c
+++ b/fs/xfs/xfs_da_btree.c
@@ -1530,6 +1530,28 @@ xfs_da_hashname(const uchar_t *name, int namelen)
1530 } 1530 }
1531} 1531}
1532 1532
1533enum xfs_dacmp
1534xfs_da_compname(
1535 struct xfs_da_args *args,
1536 const char *name,
1537 int len)
1538{
1539 return (args->namelen == len && memcmp(args->name, name, len) == 0) ?
1540 XFS_CMP_EXACT : XFS_CMP_DIFFERENT;
1541}
1542
1543static xfs_dahash_t
1544xfs_default_hashname(
1545 struct xfs_name *name)
1546{
1547 return xfs_da_hashname(name->name, name->len);
1548}
1549
1550const struct xfs_nameops xfs_default_nameops = {
1551 .hashname = xfs_default_hashname,
1552 .compname = xfs_da_compname
1553};
1554
1533/* 1555/*
1534 * Add a block to the btree ahead of the file. 1556 * Add a block to the btree ahead of the file.
1535 * Return the new block number to the caller. 1557 * Return the new block number to the caller.
diff --git a/fs/xfs/xfs_da_btree.h b/fs/xfs/xfs_da_btree.h
index 7facf86f74f9..e64c6924996f 100644
--- a/fs/xfs/xfs_da_btree.h
+++ b/fs/xfs/xfs_da_btree.h
@@ -99,6 +99,15 @@ typedef struct xfs_da_node_entry xfs_da_node_entry_t;
99 *========================================================================*/ 99 *========================================================================*/
100 100
101/* 101/*
102 * Search comparison results
103 */
104enum xfs_dacmp {
105 XFS_CMP_DIFFERENT, /* names are completely different */
106 XFS_CMP_EXACT, /* names are exactly the same */
107 XFS_CMP_CASE /* names are same but differ in case */
108};
109
110/*
102 * Structure to ease passing around component names. 111 * Structure to ease passing around component names.
103 */ 112 */
104typedef struct xfs_da_args { 113typedef struct xfs_da_args {
@@ -127,6 +136,7 @@ typedef struct xfs_da_args {
127 unsigned char rename; /* T/F: this is an atomic rename op */ 136 unsigned char rename; /* T/F: this is an atomic rename op */
128 unsigned char addname; /* T/F: this is an add operation */ 137 unsigned char addname; /* T/F: this is an add operation */
129 unsigned char oknoent; /* T/F: ok to return ENOENT, else die */ 138 unsigned char oknoent; /* T/F: ok to return ENOENT, else die */
139 enum xfs_dacmp cmpresult; /* name compare result for lookups */
130} xfs_da_args_t; 140} xfs_da_args_t;
131 141
132/* 142/*
@@ -201,6 +211,14 @@ typedef struct xfs_da_state {
201 (uint)(XFS_DA_LOGOFF(BASE, ADDR)), \ 211 (uint)(XFS_DA_LOGOFF(BASE, ADDR)), \
202 (uint)(XFS_DA_LOGOFF(BASE, ADDR)+(SIZE)-1) 212 (uint)(XFS_DA_LOGOFF(BASE, ADDR)+(SIZE)-1)
203 213
214/*
215 * Name ops for directory and/or attr name operations
216 */
217struct xfs_nameops {
218 xfs_dahash_t (*hashname)(struct xfs_name *);
219 enum xfs_dacmp (*compname)(struct xfs_da_args *, const char *, int);
220};
221
204 222
205#ifdef __KERNEL__ 223#ifdef __KERNEL__
206/*======================================================================== 224/*========================================================================
@@ -249,6 +267,10 @@ int xfs_da_shrink_inode(xfs_da_args_t *args, xfs_dablk_t dead_blkno,
249 xfs_dabuf_t *dead_buf); 267 xfs_dabuf_t *dead_buf);
250 268
251uint xfs_da_hashname(const uchar_t *name_string, int name_length); 269uint xfs_da_hashname(const uchar_t *name_string, int name_length);
270enum xfs_dacmp xfs_da_compname(struct xfs_da_args *args,
271 const char *name, int len);
272
273
252xfs_da_state_t *xfs_da_state_alloc(void); 274xfs_da_state_t *xfs_da_state_alloc(void);
253void xfs_da_state_free(xfs_da_state_t *state); 275void xfs_da_state_free(xfs_da_state_t *state);
254 276
diff --git a/fs/xfs/xfs_dir2.c b/fs/xfs/xfs_dir2.c
index 0284af1734bd..675899bb7048 100644
--- a/fs/xfs/xfs_dir2.c
+++ b/fs/xfs/xfs_dir2.c
@@ -65,6 +65,7 @@ xfs_dir_mount(
65 (mp->m_dirblksize - (uint)sizeof(xfs_da_node_hdr_t)) / 65 (mp->m_dirblksize - (uint)sizeof(xfs_da_node_hdr_t)) /
66 (uint)sizeof(xfs_da_node_entry_t); 66 (uint)sizeof(xfs_da_node_entry_t);
67 mp->m_dir_magicpct = (mp->m_dirblksize * 37) / 100; 67 mp->m_dir_magicpct = (mp->m_dirblksize * 37) / 100;
68 mp->m_dirnameops = &xfs_default_nameops;
68} 69}
69 70
70/* 71/*
@@ -164,7 +165,7 @@ xfs_dir_createname(
164 165
165 args.name = name->name; 166 args.name = name->name;
166 args.namelen = name->len; 167 args.namelen = name->len;
167 args.hashval = xfs_da_hashname(name->name, name->len); 168 args.hashval = dp->i_mount->m_dirnameops->hashname(name);
168 args.inumber = inum; 169 args.inumber = inum;
169 args.dp = dp; 170 args.dp = dp;
170 args.firstblock = first; 171 args.firstblock = first;
@@ -210,11 +211,12 @@ xfs_dir_lookup(
210 211
211 args.name = name->name; 212 args.name = name->name;
212 args.namelen = name->len; 213 args.namelen = name->len;
213 args.hashval = xfs_da_hashname(name->name, name->len); 214 args.hashval = dp->i_mount->m_dirnameops->hashname(name);
214 args.dp = dp; 215 args.dp = dp;
215 args.whichfork = XFS_DATA_FORK; 216 args.whichfork = XFS_DATA_FORK;
216 args.trans = tp; 217 args.trans = tp;
217 args.oknoent = 1; 218 args.oknoent = 1;
219 args.cmpresult = XFS_CMP_DIFFERENT;
218 220
219 if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL) 221 if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL)
220 rval = xfs_dir2_sf_lookup(&args); 222 rval = xfs_dir2_sf_lookup(&args);
@@ -257,7 +259,7 @@ xfs_dir_removename(
257 259
258 args.name = name->name; 260 args.name = name->name;
259 args.namelen = name->len; 261 args.namelen = name->len;
260 args.hashval = xfs_da_hashname(name->name, name->len); 262 args.hashval = dp->i_mount->m_dirnameops->hashname(name);
261 args.inumber = ino; 263 args.inumber = ino;
262 args.dp = dp; 264 args.dp = dp;
263 args.firstblock = first; 265 args.firstblock = first;
@@ -340,7 +342,7 @@ xfs_dir_replace(
340 342
341 args.name = name->name; 343 args.name = name->name;
342 args.namelen = name->len; 344 args.namelen = name->len;
343 args.hashval = xfs_da_hashname(name->name, name->len); 345 args.hashval = dp->i_mount->m_dirnameops->hashname(name);
344 args.inumber = inum; 346 args.inumber = inum;
345 args.dp = dp; 347 args.dp = dp;
346 args.firstblock = first; 348 args.firstblock = first;
@@ -388,7 +390,7 @@ xfs_dir_canenter(
388 390
389 args.name = name->name; 391 args.name = name->name;
390 args.namelen = name->len; 392 args.namelen = name->len;
391 args.hashval = xfs_da_hashname(name->name, name->len); 393 args.hashval = dp->i_mount->m_dirnameops->hashname(name);
392 args.dp = dp; 394 args.dp = dp;
393 args.whichfork = XFS_DATA_FORK; 395 args.whichfork = XFS_DATA_FORK;
394 args.trans = tp; 396 args.trans = tp;
diff --git a/fs/xfs/xfs_dir2_block.c b/fs/xfs/xfs_dir2_block.c
index e8a7aca5fe23..98588491cb0e 100644
--- a/fs/xfs/xfs_dir2_block.c
+++ b/fs/xfs/xfs_dir2_block.c
@@ -643,6 +643,7 @@ xfs_dir2_block_lookup_int(
643 int mid; /* binary search current idx */ 643 int mid; /* binary search current idx */
644 xfs_mount_t *mp; /* filesystem mount point */ 644 xfs_mount_t *mp; /* filesystem mount point */
645 xfs_trans_t *tp; /* transaction pointer */ 645 xfs_trans_t *tp; /* transaction pointer */
646 enum xfs_dacmp cmp; /* comparison result */
646 647
647 dp = args->dp; 648 dp = args->dp;
648 tp = args->trans; 649 tp = args->trans;
@@ -697,20 +698,31 @@ xfs_dir2_block_lookup_int(
697 dep = (xfs_dir2_data_entry_t *) 698 dep = (xfs_dir2_data_entry_t *)
698 ((char *)block + xfs_dir2_dataptr_to_off(mp, addr)); 699 ((char *)block + xfs_dir2_dataptr_to_off(mp, addr));
699 /* 700 /*
700 * Compare, if it's right give back buffer & entry number. 701 * Compare name and if it's an exact match, return the index
702 * and buffer. If it's the first case-insensitive match, store
703 * the index and buffer and continue looking for an exact match.
701 */ 704 */
702 if (dep->namelen == args->namelen && 705 cmp = mp->m_dirnameops->compname(args, dep->name, dep->namelen);
703 dep->name[0] == args->name[0] && 706 if (cmp != XFS_CMP_DIFFERENT && cmp != args->cmpresult) {
704 memcmp(dep->name, args->name, args->namelen) == 0) { 707 args->cmpresult = cmp;
705 *bpp = bp; 708 *bpp = bp;
706 *entno = mid; 709 *entno = mid;
707 return 0; 710 if (cmp == XFS_CMP_EXACT)
711 return 0;
708 } 712 }
709 } while (++mid < be32_to_cpu(btp->count) && be32_to_cpu(blp[mid].hashval) == hash); 713 } while (++mid < be32_to_cpu(btp->count) &&
714 be32_to_cpu(blp[mid].hashval) == hash);
715
716 ASSERT(args->oknoent);
717 /*
718 * Here, we can only be doing a lookup (not a rename or replace).
719 * If a case-insensitive match was found earlier, return success.
720 */
721 if (args->cmpresult == XFS_CMP_CASE)
722 return 0;
710 /* 723 /*
711 * No match, release the buffer and return ENOENT. 724 * No match, release the buffer and return ENOENT.
712 */ 725 */
713 ASSERT(args->oknoent);
714 xfs_da_brelse(tp, bp); 726 xfs_da_brelse(tp, bp);
715 return XFS_ERROR(ENOENT); 727 return XFS_ERROR(ENOENT);
716} 728}
@@ -1033,6 +1045,7 @@ xfs_dir2_sf_to_block(
1033 xfs_dir2_sf_t *sfp; /* shortform structure */ 1045 xfs_dir2_sf_t *sfp; /* shortform structure */
1034 __be16 *tagp; /* end of data entry */ 1046 __be16 *tagp; /* end of data entry */
1035 xfs_trans_t *tp; /* transaction pointer */ 1047 xfs_trans_t *tp; /* transaction pointer */
1048 struct xfs_name name;
1036 1049
1037 xfs_dir2_trace_args("sf_to_block", args); 1050 xfs_dir2_trace_args("sf_to_block", args);
1038 dp = args->dp; 1051 dp = args->dp;
@@ -1187,8 +1200,10 @@ xfs_dir2_sf_to_block(
1187 tagp = xfs_dir2_data_entry_tag_p(dep); 1200 tagp = xfs_dir2_data_entry_tag_p(dep);
1188 *tagp = cpu_to_be16((char *)dep - (char *)block); 1201 *tagp = cpu_to_be16((char *)dep - (char *)block);
1189 xfs_dir2_data_log_entry(tp, bp, dep); 1202 xfs_dir2_data_log_entry(tp, bp, dep);
1190 blp[2 + i].hashval = cpu_to_be32(xfs_da_hashname( 1203 name.name = sfep->name;
1191 (char *)sfep->name, sfep->namelen)); 1204 name.len = sfep->namelen;
1205 blp[2 + i].hashval = cpu_to_be32(mp->m_dirnameops->
1206 hashname(&name));
1192 blp[2 + i].address = cpu_to_be32(xfs_dir2_byte_to_dataptr(mp, 1207 blp[2 + i].address = cpu_to_be32(xfs_dir2_byte_to_dataptr(mp,
1193 (char *)dep - (char *)block)); 1208 (char *)dep - (char *)block));
1194 offset = (int)((char *)(tagp + 1) - (char *)block); 1209 offset = (int)((char *)(tagp + 1) - (char *)block);
diff --git a/fs/xfs/xfs_dir2_data.c b/fs/xfs/xfs_dir2_data.c
index fb8c9e08b23d..498f8d694330 100644
--- a/fs/xfs/xfs_dir2_data.c
+++ b/fs/xfs/xfs_dir2_data.c
@@ -65,6 +65,7 @@ xfs_dir2_data_check(
65 xfs_mount_t *mp; /* filesystem mount point */ 65 xfs_mount_t *mp; /* filesystem mount point */
66 char *p; /* current data position */ 66 char *p; /* current data position */
67 int stale; /* count of stale leaves */ 67 int stale; /* count of stale leaves */
68 struct xfs_name name;
68 69
69 mp = dp->i_mount; 70 mp = dp->i_mount;
70 d = bp->data; 71 d = bp->data;
@@ -140,7 +141,9 @@ xfs_dir2_data_check(
140 addr = xfs_dir2_db_off_to_dataptr(mp, mp->m_dirdatablk, 141 addr = xfs_dir2_db_off_to_dataptr(mp, mp->m_dirdatablk,
141 (xfs_dir2_data_aoff_t) 142 (xfs_dir2_data_aoff_t)
142 ((char *)dep - (char *)d)); 143 ((char *)dep - (char *)d));
143 hash = xfs_da_hashname((char *)dep->name, dep->namelen); 144 name.name = dep->name;
145 name.len = dep->namelen;
146 hash = mp->m_dirnameops->hashname(&name);
144 for (i = 0; i < be32_to_cpu(btp->count); i++) { 147 for (i = 0; i < be32_to_cpu(btp->count); i++) {
145 if (be32_to_cpu(lep[i].address) == addr && 148 if (be32_to_cpu(lep[i].address) == addr &&
146 be32_to_cpu(lep[i].hashval) == hash) 149 be32_to_cpu(lep[i].hashval) == hash)
diff --git a/fs/xfs/xfs_dir2_leaf.c b/fs/xfs/xfs_dir2_leaf.c
index e33433408e4a..b52903bc0b14 100644
--- a/fs/xfs/xfs_dir2_leaf.c
+++ b/fs/xfs/xfs_dir2_leaf.c
@@ -1331,6 +1331,8 @@ xfs_dir2_leaf_lookup_int(
1331 xfs_mount_t *mp; /* filesystem mount point */ 1331 xfs_mount_t *mp; /* filesystem mount point */
1332 xfs_dir2_db_t newdb; /* new data block number */ 1332 xfs_dir2_db_t newdb; /* new data block number */
1333 xfs_trans_t *tp; /* transaction pointer */ 1333 xfs_trans_t *tp; /* transaction pointer */
1334 xfs_dabuf_t *cbp; /* case match data buffer */
1335 enum xfs_dacmp cmp; /* name compare result */
1334 1336
1335 dp = args->dp; 1337 dp = args->dp;
1336 tp = args->trans; 1338 tp = args->trans;
@@ -1354,9 +1356,11 @@ xfs_dir2_leaf_lookup_int(
1354 * Loop over all the entries with the right hash value 1356 * Loop over all the entries with the right hash value
1355 * looking to match the name. 1357 * looking to match the name.
1356 */ 1358 */
1359 cbp = NULL;
1357 for (lep = &leaf->ents[index], dbp = NULL, curdb = -1; 1360 for (lep = &leaf->ents[index], dbp = NULL, curdb = -1;
1358 index < be16_to_cpu(leaf->hdr.count) && be32_to_cpu(lep->hashval) == args->hashval; 1361 index < be16_to_cpu(leaf->hdr.count) &&
1359 lep++, index++) { 1362 be32_to_cpu(lep->hashval) == args->hashval;
1363 lep++, index++) {
1360 /* 1364 /*
1361 * Skip over stale leaf entries. 1365 * Skip over stale leaf entries.
1362 */ 1366 */
@@ -1371,12 +1375,12 @@ xfs_dir2_leaf_lookup_int(
1371 * need to pitch the old one and read the new one. 1375 * need to pitch the old one and read the new one.
1372 */ 1376 */
1373 if (newdb != curdb) { 1377 if (newdb != curdb) {
1374 if (dbp) 1378 if (dbp != cbp)
1375 xfs_da_brelse(tp, dbp); 1379 xfs_da_brelse(tp, dbp);
1376 if ((error = 1380 error = xfs_da_read_buf(tp, dp,
1377 xfs_da_read_buf(tp, dp, 1381 xfs_dir2_db_to_da(mp, newdb),
1378 xfs_dir2_db_to_da(mp, newdb), -1, &dbp, 1382 -1, &dbp, XFS_DATA_FORK);
1379 XFS_DATA_FORK))) { 1383 if (error) {
1380 xfs_da_brelse(tp, lbp); 1384 xfs_da_brelse(tp, lbp);
1381 return error; 1385 return error;
1382 } 1386 }
@@ -1386,24 +1390,46 @@ xfs_dir2_leaf_lookup_int(
1386 /* 1390 /*
1387 * Point to the data entry. 1391 * Point to the data entry.
1388 */ 1392 */
1389 dep = (xfs_dir2_data_entry_t *) 1393 dep = (xfs_dir2_data_entry_t *)((char *)dbp->data +
1390 ((char *)dbp->data + 1394 xfs_dir2_dataptr_to_off(mp, be32_to_cpu(lep->address)));
1391 xfs_dir2_dataptr_to_off(mp, be32_to_cpu(lep->address)));
1392 /* 1395 /*
1393 * If it matches then return it. 1396 * Compare name and if it's an exact match, return the index
1397 * and buffer. If it's the first case-insensitive match, store
1398 * the index and buffer and continue looking for an exact match.
1394 */ 1399 */
1395 if (dep->namelen == args->namelen && 1400 cmp = mp->m_dirnameops->compname(args, dep->name, dep->namelen);
1396 dep->name[0] == args->name[0] && 1401 if (cmp != XFS_CMP_DIFFERENT && cmp != args->cmpresult) {
1397 memcmp(dep->name, args->name, args->namelen) == 0) { 1402 args->cmpresult = cmp;
1398 *dbpp = dbp;
1399 *indexp = index; 1403 *indexp = index;
1400 return 0; 1404 /*
1405 * case exact match: release the stored CI buffer if it
1406 * exists and return the current buffer.
1407 */
1408 if (cmp == XFS_CMP_EXACT) {
1409 if (cbp && cbp != dbp)
1410 xfs_da_brelse(tp, cbp);
1411 *dbpp = dbp;
1412 return 0;
1413 }
1414 cbp = dbp;
1401 } 1415 }
1402 } 1416 }
1417 ASSERT(args->oknoent);
1418 /*
1419 * Here, we can only be doing a lookup (not a rename or replace).
1420 * If a case-insensitive match was found earlier, release the current
1421 * buffer and return the stored CI matching buffer.
1422 */
1423 if (args->cmpresult == XFS_CMP_CASE) {
1424 if (cbp != dbp)
1425 xfs_da_brelse(tp, dbp);
1426 *dbpp = cbp;
1427 return 0;
1428 }
1403 /* 1429 /*
1404 * No match found, return ENOENT. 1430 * No match found, return ENOENT.
1405 */ 1431 */
1406 ASSERT(args->oknoent); 1432 ASSERT(cbp == NULL);
1407 if (dbp) 1433 if (dbp)
1408 xfs_da_brelse(tp, dbp); 1434 xfs_da_brelse(tp, dbp);
1409 xfs_da_brelse(tp, lbp); 1435 xfs_da_brelse(tp, lbp);
diff --git a/fs/xfs/xfs_dir2_node.c b/fs/xfs/xfs_dir2_node.c
index e29b7c63e198..fedf8f976a10 100644
--- a/fs/xfs/xfs_dir2_node.c
+++ b/fs/xfs/xfs_dir2_node.c
@@ -556,6 +556,7 @@ xfs_dir2_leafn_lookup_for_entry(
556 xfs_mount_t *mp; /* filesystem mount point */ 556 xfs_mount_t *mp; /* filesystem mount point */
557 xfs_dir2_db_t newdb; /* new data block number */ 557 xfs_dir2_db_t newdb; /* new data block number */
558 xfs_trans_t *tp; /* transaction pointer */ 558 xfs_trans_t *tp; /* transaction pointer */
559 enum xfs_dacmp cmp; /* comparison result */
559 560
560 dp = args->dp; 561 dp = args->dp;
561 tp = args->trans; 562 tp = args->trans;
@@ -620,17 +621,21 @@ xfs_dir2_leafn_lookup_for_entry(
620 dep = (xfs_dir2_data_entry_t *)((char *)curbp->data + 621 dep = (xfs_dir2_data_entry_t *)((char *)curbp->data +
621 xfs_dir2_dataptr_to_off(mp, be32_to_cpu(lep->address))); 622 xfs_dir2_dataptr_to_off(mp, be32_to_cpu(lep->address)));
622 /* 623 /*
623 * Compare the entry, return it if it matches. 624 * Compare the entry and if it's an exact match, return
625 * EEXIST immediately. If it's the first case-insensitive
626 * match, store the inode number and continue looking.
624 */ 627 */
625 if (dep->namelen == args->namelen && memcmp(dep->name, 628 cmp = mp->m_dirnameops->compname(args, dep->name, dep->namelen);
626 args->name, args->namelen) == 0) { 629 if (cmp != XFS_CMP_DIFFERENT && cmp != args->cmpresult) {
630 args->cmpresult = cmp;
627 args->inumber = be64_to_cpu(dep->inumber); 631 args->inumber = be64_to_cpu(dep->inumber);
628 di = (int)((char *)dep - (char *)curbp->data); 632 di = (int)((char *)dep - (char *)curbp->data);
629 error = EEXIST; 633 error = EEXIST;
630 goto out; 634 if (cmp == XFS_CMP_EXACT)
635 goto out;
631 } 636 }
632 } 637 }
633 /* Didn't find a match. */ 638 /* Didn't find an exact match. */
634 error = ENOENT; 639 error = ENOENT;
635 di = -1; 640 di = -1;
636 ASSERT(index == be16_to_cpu(leaf->hdr.count) || args->oknoent); 641 ASSERT(index == be16_to_cpu(leaf->hdr.count) || args->oknoent);
@@ -1813,6 +1818,8 @@ xfs_dir2_node_lookup(
1813 error = xfs_da_node_lookup_int(state, &rval); 1818 error = xfs_da_node_lookup_int(state, &rval);
1814 if (error) 1819 if (error)
1815 rval = error; 1820 rval = error;
1821 else if (rval == ENOENT && args->cmpresult == XFS_CMP_CASE)
1822 rval = EEXIST; /* a case-insensitive match was found */
1816 /* 1823 /*
1817 * Release the btree blocks and leaf block. 1824 * Release the btree blocks and leaf block.
1818 */ 1825 */
@@ -1856,9 +1863,8 @@ xfs_dir2_node_removename(
1856 * Look up the entry we're deleting, set up the cursor. 1863 * Look up the entry we're deleting, set up the cursor.
1857 */ 1864 */
1858 error = xfs_da_node_lookup_int(state, &rval); 1865 error = xfs_da_node_lookup_int(state, &rval);
1859 if (error) { 1866 if (error)
1860 rval = error; 1867 rval = error;
1861 }
1862 /* 1868 /*
1863 * Didn't find it, upper layer screwed up. 1869 * Didn't find it, upper layer screwed up.
1864 */ 1870 */
@@ -1875,9 +1881,8 @@ xfs_dir2_node_removename(
1875 */ 1881 */
1876 error = xfs_dir2_leafn_remove(args, blk->bp, blk->index, 1882 error = xfs_dir2_leafn_remove(args, blk->bp, blk->index,
1877 &state->extrablk, &rval); 1883 &state->extrablk, &rval);
1878 if (error) { 1884 if (error)
1879 return error; 1885 return error;
1880 }
1881 /* 1886 /*
1882 * Fix the hash values up the btree. 1887 * Fix the hash values up the btree.
1883 */ 1888 */
diff --git a/fs/xfs/xfs_dir2_sf.c b/fs/xfs/xfs_dir2_sf.c
index ca33bc62edc2..dcd09cada43f 100644
--- a/fs/xfs/xfs_dir2_sf.c
+++ b/fs/xfs/xfs_dir2_sf.c
@@ -814,6 +814,7 @@ xfs_dir2_sf_lookup(
814 int i; /* entry index */ 814 int i; /* entry index */
815 xfs_dir2_sf_entry_t *sfep; /* shortform directory entry */ 815 xfs_dir2_sf_entry_t *sfep; /* shortform directory entry */
816 xfs_dir2_sf_t *sfp; /* shortform structure */ 816 xfs_dir2_sf_t *sfp; /* shortform structure */
817 enum xfs_dacmp cmp; /* comparison result */
817 818
818 xfs_dir2_trace_args("sf_lookup", args); 819 xfs_dir2_trace_args("sf_lookup", args);
819 xfs_dir2_sf_check(args); 820 xfs_dir2_sf_check(args);
@@ -836,6 +837,7 @@ xfs_dir2_sf_lookup(
836 */ 837 */
837 if (args->namelen == 1 && args->name[0] == '.') { 838 if (args->namelen == 1 && args->name[0] == '.') {
838 args->inumber = dp->i_ino; 839 args->inumber = dp->i_ino;
840 args->cmpresult = XFS_CMP_EXACT;
839 return XFS_ERROR(EEXIST); 841 return XFS_ERROR(EEXIST);
840 } 842 }
841 /* 843 /*
@@ -844,27 +846,39 @@ xfs_dir2_sf_lookup(
844 if (args->namelen == 2 && 846 if (args->namelen == 2 &&
845 args->name[0] == '.' && args->name[1] == '.') { 847 args->name[0] == '.' && args->name[1] == '.') {
846 args->inumber = xfs_dir2_sf_get_inumber(sfp, &sfp->hdr.parent); 848 args->inumber = xfs_dir2_sf_get_inumber(sfp, &sfp->hdr.parent);
849 args->cmpresult = XFS_CMP_EXACT;
847 return XFS_ERROR(EEXIST); 850 return XFS_ERROR(EEXIST);
848 } 851 }
849 /* 852 /*
850 * Loop over all the entries trying to match ours. 853 * Loop over all the entries trying to match ours.
851 */ 854 */
852 for (i = 0, sfep = xfs_dir2_sf_firstentry(sfp); 855 for (i = 0, sfep = xfs_dir2_sf_firstentry(sfp); i < sfp->hdr.count;
853 i < sfp->hdr.count; 856 i++, sfep = xfs_dir2_sf_nextentry(sfp, sfep)) {
854 i++, sfep = xfs_dir2_sf_nextentry(sfp, sfep)) { 857 /*
855 if (sfep->namelen == args->namelen && 858 * Compare name and if it's an exact match, return the inode
856 sfep->name[0] == args->name[0] && 859 * number. If it's the first case-insensitive match, store the
857 memcmp(args->name, sfep->name, args->namelen) == 0) { 860 * inode number and continue looking for an exact match.
858 args->inumber = 861 */
859 xfs_dir2_sf_get_inumber(sfp, 862 cmp = dp->i_mount->m_dirnameops->compname(args, sfep->name,
860 xfs_dir2_sf_inumberp(sfep)); 863 sfep->namelen);
861 return XFS_ERROR(EEXIST); 864 if (cmp != XFS_CMP_DIFFERENT && cmp != args->cmpresult) {
865 args->cmpresult = cmp;
866 args->inumber = xfs_dir2_sf_get_inumber(sfp,
867 xfs_dir2_sf_inumberp(sfep));
868 if (cmp == XFS_CMP_EXACT)
869 return XFS_ERROR(EEXIST);
862 } 870 }
863 } 871 }
872 ASSERT(args->oknoent);
873 /*
874 * Here, we can only be doing a lookup (not a rename or replace).
875 * If a case-insensitive match was found earlier, return "found".
876 */
877 if (args->cmpresult == XFS_CMP_CASE)
878 return XFS_ERROR(EEXIST);
864 /* 879 /*
865 * Didn't find it. 880 * Didn't find it.
866 */ 881 */
867 ASSERT(args->oknoent);
868 return XFS_ERROR(ENOENT); 882 return XFS_ERROR(ENOENT);
869} 883}
870 884
@@ -904,24 +918,21 @@ xfs_dir2_sf_removename(
904 * Loop over the old directory entries. 918 * Loop over the old directory entries.
905 * Find the one we're deleting. 919 * Find the one we're deleting.
906 */ 920 */
907 for (i = 0, sfep = xfs_dir2_sf_firstentry(sfp); 921 for (i = 0, sfep = xfs_dir2_sf_firstentry(sfp); i < sfp->hdr.count;
908 i < sfp->hdr.count; 922 i++, sfep = xfs_dir2_sf_nextentry(sfp, sfep)) {
909 i++, sfep = xfs_dir2_sf_nextentry(sfp, sfep)) { 923 if (xfs_da_compname(args, sfep->name, sfep->namelen) ==
910 if (sfep->namelen == args->namelen && 924 XFS_CMP_EXACT) {
911 sfep->name[0] == args->name[0] &&
912 memcmp(sfep->name, args->name, args->namelen) == 0) {
913 ASSERT(xfs_dir2_sf_get_inumber(sfp, 925 ASSERT(xfs_dir2_sf_get_inumber(sfp,
914 xfs_dir2_sf_inumberp(sfep)) == 926 xfs_dir2_sf_inumberp(sfep)) ==
915 args->inumber); 927 args->inumber);
916 break; 928 break;
917 } 929 }
918 } 930 }
919 /* 931 /*
920 * Didn't find it. 932 * Didn't find it.
921 */ 933 */
922 if (i == sfp->hdr.count) { 934 if (i == sfp->hdr.count)
923 return XFS_ERROR(ENOENT); 935 return XFS_ERROR(ENOENT);
924 }
925 /* 936 /*
926 * Calculate sizes. 937 * Calculate sizes.
927 */ 938 */
@@ -1042,11 +1053,10 @@ xfs_dir2_sf_replace(
1042 */ 1053 */
1043 else { 1054 else {
1044 for (i = 0, sfep = xfs_dir2_sf_firstentry(sfp); 1055 for (i = 0, sfep = xfs_dir2_sf_firstentry(sfp);
1045 i < sfp->hdr.count; 1056 i < sfp->hdr.count;
1046 i++, sfep = xfs_dir2_sf_nextentry(sfp, sfep)) { 1057 i++, sfep = xfs_dir2_sf_nextentry(sfp, sfep)) {
1047 if (sfep->namelen == args->namelen && 1058 if (xfs_da_compname(args, sfep->name, sfep->namelen) ==
1048 sfep->name[0] == args->name[0] && 1059 XFS_CMP_EXACT) {
1049 memcmp(args->name, sfep->name, args->namelen) == 0) {
1050#if XFS_BIG_INUMS || defined(DEBUG) 1060#if XFS_BIG_INUMS || defined(DEBUG)
1051 ino = xfs_dir2_sf_get_inumber(sfp, 1061 ino = xfs_dir2_sf_get_inumber(sfp,
1052 xfs_dir2_sf_inumberp(sfep)); 1062 xfs_dir2_sf_inumberp(sfep));
diff --git a/fs/xfs/xfs_mount.h b/fs/xfs/xfs_mount.h
index 96d8791e9e5a..2a75f1703b39 100644
--- a/fs/xfs/xfs_mount.h
+++ b/fs/xfs/xfs_mount.h
@@ -61,6 +61,7 @@ struct xfs_bmap_free;
61struct xfs_extdelta; 61struct xfs_extdelta;
62struct xfs_swapext; 62struct xfs_swapext;
63struct xfs_mru_cache; 63struct xfs_mru_cache;
64struct xfs_nameops;
64 65
65/* 66/*
66 * Prototypes and functions for the Data Migration subsystem. 67 * Prototypes and functions for the Data Migration subsystem.
@@ -315,6 +316,7 @@ typedef struct xfs_mount {
315 __uint8_t m_inode_quiesce;/* call quiesce on new inodes. 316 __uint8_t m_inode_quiesce;/* call quiesce on new inodes.
316 field governed by m_ilock */ 317 field governed by m_ilock */
317 __uint8_t m_sectbb_log; /* sectlog - BBSHIFT */ 318 __uint8_t m_sectbb_log; /* sectlog - BBSHIFT */
319 const struct xfs_nameops *m_dirnameops; /* vector of dir name ops */
318 int m_dirblksize; /* directory block sz--bytes */ 320 int m_dirblksize; /* directory block sz--bytes */
319 int m_dirblkfsbs; /* directory block sz--fsbs */ 321 int m_dirblkfsbs; /* directory block sz--fsbs */
320 xfs_dablk_t m_dirdatablk; /* blockno of dir data v2 */ 322 xfs_dablk_t m_dirdatablk; /* blockno of dir data v2 */