diff options
| author | Barry Naujok <bnaujok@sgi.com> | 2008-06-23 13:25:38 +1000 | 
|---|---|---|
| committer | Niv Sardi <xaiki@debian.org> | 2008-07-28 16:59:01 +1000 | 
| commit | 90bb7ab077a63facbe3aa0b9e3763a0cb956a4c1 (patch) | |
| tree | fae24e57f69a35c6de32910e2b5d75b7df0f3c04 /fs/xfs/xfs_dir2_node.c | |
| parent | e5700704b2b0853c059e424284cceeff3032ea28 (diff) | |
| download | olio-linux-3.10-90bb7ab077a63facbe3aa0b9e3763a0cb956a4c1.tar.xz olio-linux-3.10-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/xfs_dir2_node.c')
| -rw-r--r-- | fs/xfs/xfs_dir2_node.c | 69 | 
1 files changed, 43 insertions, 26 deletions
diff --git a/fs/xfs/xfs_dir2_node.c b/fs/xfs/xfs_dir2_node.c index 1b543022346..fa6c3a5ddbc 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);  }  /*  |