summaryrefslogtreecommitdiffstats
path: root/fs/xfs
diff options
context:
space:
mode:
authorBarry Naujok <bnaujok@sgi.com>2008-06-23 13:25:38 +1000
committerNiv Sardi <xaiki@debian.org>2008-07-28 16:59:01 +1000
commit90bb7ab077a63facbe3aa0b9e3763a0cb956a4c1 (patch)
treefae24e57f69a35c6de32910e2b5d75b7df0f3c04 /fs/xfs
parente5700704b2b0853c059e424284cceeff3032ea28 (diff)
downloadlinux-stable-90bb7ab077a63facbe3aa0b9e3763a0cb956a4c1.tar.gz
linux-stable-90bb7ab077a63facbe3aa0b9e3763a0cb956a4c1.tar.bz2
linux-stable-90bb7ab077a63facbe3aa0b9e3763a0cb956a4c1.zip
[XFS] Fix returning case-preserved name with CI node form directories
xfs_dir2_node_lookup() calls xfs_da_node_lookup_int() which iterates through leaf blocks containing the matching hash value for the name being looked up. Inside xfs_da_node_lookup_int(), it calls the xfs_dir2_leafn_lookup_for_entry() for each leaf block. xfs_dir2_leafn_lookup_for_entry() iterates through each matching hash/offset pair doing a name comparison to find the matching dirent. For CI mode, the state->extrablk retains the details of the block that has the CI match so xfs_dir2_node_lookup() can return the case-preserved name. The original implementation didn't retain the xfs_da_buf_t properly, so the lookup was returning a bogus name to be stored in the dentry. In the case of unlink, the bad name was passed and in debug mode, ASSERTed when it can't find the entry. SGI-PV: 983284 SGI-Modid: xfs-linux-melb:xfs-kern:31337a Signed-off-by: Barry Naujok <bnaujok@sgi.com> Signed-off-by: Christoph Hellwig <hch@infradead.org> Signed-off-by: Lachlan McIlroy <lachlan@sgi.com>
Diffstat (limited to 'fs/xfs')
-rw-r--r--fs/xfs/xfs_dir2_node.c69
1 files changed, 43 insertions, 26 deletions
diff --git a/fs/xfs/xfs_dir2_node.c b/fs/xfs/xfs_dir2_node.c
index 1b5430223461..fa6c3a5ddbc6 100644
--- a/fs/xfs/xfs_dir2_node.c
+++ b/fs/xfs/xfs_dir2_node.c
@@ -549,7 +549,6 @@ 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 = -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,7 +576,6 @@ 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.
@@ -602,17 +600,27 @@ xfs_dir2_leafn_lookup_for_entry(
*/
if (newdb != curdb) {
/*
- * If we had a block before, drop it.
+ * If we had a block before that we aren't saving
+ * for a CI name, drop it
*/
- if (curbp)
+ if (curbp && (args->cmpresult == XFS_CMP_DIFFERENT ||
+ curdb != state->extrablk.blkno))
xfs_da_brelse(tp, curbp);
/*
- * Read the data block.
+ * If needing the block that is saved with a CI match,
+ * use it otherwise read in the new data block.
*/
- error = xfs_da_read_buf(tp, dp, xfs_dir2_db_to_da(mp,
- newdb), -1, &curbp, XFS_DATA_FORK);
- if (error)
- return error;
+ if (args->cmpresult != XFS_CMP_DIFFERENT &&
+ newdb == state->extrablk.blkno) {
+ ASSERT(state->extravalid);
+ curbp = state->extrablk.bp;
+ } else {
+ error = xfs_da_read_buf(tp, dp,
+ xfs_dir2_db_to_da(mp, newdb),
+ -1, &curbp, XFS_DATA_FORK);
+ if (error)
+ return error;
+ }
xfs_dir2_data_check(dp, curbp);
curdb = newdb;
}
@@ -624,38 +632,47 @@ xfs_dir2_leafn_lookup_for_entry(
/*
* 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.
+ * match, store the block & inode number and continue looking.
*/
cmp = mp->m_dirnameops->compname(args, dep->name, dep->namelen);
if (cmp != XFS_CMP_DIFFERENT && cmp != args->cmpresult) {
+ /* If there is a CI match block, drop it */
+ if (args->cmpresult != XFS_CMP_DIFFERENT &&
+ curdb != state->extrablk.blkno)
+ xfs_da_brelse(tp, state->extrablk.bp);
args->cmpresult = cmp;
args->inumber = be64_to_cpu(dep->inumber);
- di = (int)((char *)dep - (char *)curbp->data);
- error = EEXIST;
+ *indexp = index;
+ state->extravalid = 1;
+ state->extrablk.bp = curbp;
+ state->extrablk.blkno = curdb;
+ state->extrablk.index = (int)((char *)dep -
+ (char *)curbp->data);
+ state->extrablk.magic = XFS_DIR2_DATA_MAGIC;
if (cmp == XFS_CMP_EXACT)
- goto out;
+ return XFS_ERROR(EEXIST);
}
}
- /* Didn't find an exact match. */
- error = ENOENT;
ASSERT(index == be16_to_cpu(leaf->hdr.count) ||
(args->op_flags & XFS_DA_OP_OKNOENT));
-out:
if (curbp) {
- /* Giving back a data block. */
- state->extravalid = 1;
- state->extrablk.bp = curbp;
- state->extrablk.index = di;
- state->extrablk.blkno = curdb;
- state->extrablk.magic = XFS_DIR2_DATA_MAGIC;
+ if (args->cmpresult == XFS_CMP_DIFFERENT) {
+ /* Giving back last used data block. */
+ state->extravalid = 1;
+ state->extrablk.bp = curbp;
+ state->extrablk.index = -1;
+ state->extrablk.blkno = curdb;
+ state->extrablk.magic = XFS_DIR2_DATA_MAGIC;
+ } else {
+ /* If the curbp is not the CI match block, drop it */
+ if (state->extrablk.bp != curbp)
+ xfs_da_brelse(tp, curbp);
+ }
} else {
state->extravalid = 0;
}
- /*
- * Return the index, that will be the deletion point for remove/replace.
- */
*indexp = index;
- return XFS_ERROR(error);
+ return XFS_ERROR(ENOENT);
}
/*