Commit 90bb7ab0 authored by Barry Naujok's avatar Barry Naujok Committed by Niv Sardi

[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: default avatarBarry Naujok <bnaujok@sgi.com>
Signed-off-by: default avatarChristoph Hellwig <hch@infradead.org>
Signed-off-by: default avatarLachlan McIlroy <lachlan@sgi.com>
parent e5700704
...@@ -549,7 +549,6 @@ xfs_dir2_leafn_lookup_for_entry( ...@@ -549,7 +549,6 @@ xfs_dir2_leafn_lookup_for_entry(
xfs_dir2_data_entry_t *dep; /* data block entry */ xfs_dir2_data_entry_t *dep; /* data block entry */
xfs_inode_t *dp; /* incore directory inode */ xfs_inode_t *dp; /* incore directory inode */
int error; /* error return value */ int error; /* error return value */
int di = -1; /* data entry index */
int index; /* leaf entry index */ int index; /* leaf entry index */
xfs_dir2_leaf_t *leaf; /* leaf structure */ xfs_dir2_leaf_t *leaf; /* leaf structure */
xfs_dir2_leaf_entry_t *lep; /* leaf entry */ xfs_dir2_leaf_entry_t *lep; /* leaf entry */
...@@ -577,7 +576,6 @@ xfs_dir2_leafn_lookup_for_entry( ...@@ -577,7 +576,6 @@ xfs_dir2_leafn_lookup_for_entry(
if (state->extravalid) { if (state->extravalid) {
curbp = state->extrablk.bp; curbp = state->extrablk.bp;
curdb = state->extrablk.blkno; curdb = state->extrablk.blkno;
di = state->extrablk.index;
} }
/* /*
* Loop over leaf entries with the right hash value. * Loop over leaf entries with the right hash value.
...@@ -602,17 +600,27 @@ xfs_dir2_leafn_lookup_for_entry( ...@@ -602,17 +600,27 @@ xfs_dir2_leafn_lookup_for_entry(
*/ */
if (newdb != curdb) { 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); 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, if (args->cmpresult != XFS_CMP_DIFFERENT &&
newdb), -1, &curbp, XFS_DATA_FORK); 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) if (error)
return error; return error;
}
xfs_dir2_data_check(dp, curbp); xfs_dir2_data_check(dp, curbp);
curdb = newdb; curdb = newdb;
} }
...@@ -624,38 +632,47 @@ xfs_dir2_leafn_lookup_for_entry( ...@@ -624,38 +632,47 @@ xfs_dir2_leafn_lookup_for_entry(
/* /*
* Compare the entry and if it's an exact match, return * Compare the entry and if it's an exact match, return
* EEXIST immediately. If it's the first case-insensitive * 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); cmp = mp->m_dirnameops->compname(args, dep->name, dep->namelen);
if (cmp != XFS_CMP_DIFFERENT && cmp != args->cmpresult) { 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->cmpresult = cmp;
args->inumber = be64_to_cpu(dep->inumber); args->inumber = be64_to_cpu(dep->inumber);
di = (int)((char *)dep - (char *)curbp->data); *indexp = index;
error = EEXIST; 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) 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) || ASSERT(index == be16_to_cpu(leaf->hdr.count) ||
(args->op_flags & XFS_DA_OP_OKNOENT)); (args->op_flags & XFS_DA_OP_OKNOENT));
out:
if (curbp) { if (curbp) {
/* Giving back a data block. */ if (args->cmpresult == XFS_CMP_DIFFERENT) {
/* Giving back last used data block. */
state->extravalid = 1; state->extravalid = 1;
state->extrablk.bp = curbp; state->extrablk.bp = curbp;
state->extrablk.index = di; state->extrablk.index = -1;
state->extrablk.blkno = curdb; state->extrablk.blkno = curdb;
state->extrablk.magic = XFS_DIR2_DATA_MAGIC; 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 { } else {
state->extravalid = 0; state->extravalid = 0;
} }
/*
* Return the index, that will be the deletion point for remove/replace.
*/
*indexp = index; *indexp = index;
return XFS_ERROR(error); return XFS_ERROR(ENOENT);
} }
/* /*
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment