public inbox for linux-xfs@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH] XFS: Fix returning case-preserved name with CI node form directories
@ 2008-06-18  9:07 Barry Naujok
  2008-06-19  8:32 ` Christoph Hellwig
  0 siblings, 1 reply; 3+ messages in thread
From: Barry Naujok @ 2008-06-18  9:07 UTC (permalink / raw)
  To: xfs@oss.sgi.com

[-- Attachment #1: Type: text/plain, Size: 5035 bytes --]

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.


---
  fs/xfs/xfs_dir2_node.c |   69  
++++++++++++++++++++++++++++++-------------------
  1 file changed, 43 insertions(+), 26 deletions(-)

Index: 2.6.x-xfs/fs/xfs/xfs_dir2_node.c
===================================================================
--- 2.6.x-xfs.orig/fs/xfs/xfs_dir2_node.c
+++ 2.6.x-xfs/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);
  }

  /*

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: fix_node_form_dir_ci_name_return.patch --]
[-- Type: text/x-patch; name=fix_node_form_dir_ci_name_return.patch, Size: 5067 bytes --]

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.


---
 fs/xfs/xfs_dir2_node.c |   69 ++++++++++++++++++++++++++++++-------------------
 1 file changed, 43 insertions(+), 26 deletions(-)

Index: 2.6.x-xfs/fs/xfs/xfs_dir2_node.c
===================================================================
--- 2.6.x-xfs.orig/fs/xfs/xfs_dir2_node.c
+++ 2.6.x-xfs/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);
 }
 
 /*

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [PATCH] XFS: Fix returning case-preserved name with CI node form directories
  2008-06-18  9:07 [PATCH] XFS: Fix returning case-preserved name with CI node form directories Barry Naujok
@ 2008-06-19  8:32 ` Christoph Hellwig
  2008-06-19  8:39   ` Barry Naujok
  0 siblings, 1 reply; 3+ messages in thread
From: Christoph Hellwig @ 2008-06-19  8:32 UTC (permalink / raw)
  To: Barry Naujok; +Cc: xfs@oss.sgi.com

On Wed, Jun 18, 2008 at 07:07:59PM +1000, Barry Naujok wrote:
> 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.

Looks good to me, although I don't really like how much of a mess
xfs_dir2_leafn_lookup_for_entry has become. No idea for a nice why
to write it, though.

Btw, any chance we could get some CI tests in xfsqa?  Looks like you
have quite a few testcases that found issues so far.

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [PATCH] XFS: Fix returning case-preserved name with CI node form directories
  2008-06-19  8:32 ` Christoph Hellwig
@ 2008-06-19  8:39   ` Barry Naujok
  0 siblings, 0 replies; 3+ messages in thread
From: Barry Naujok @ 2008-06-19  8:39 UTC (permalink / raw)
  To: Christoph Hellwig; +Cc: xfs@oss.sgi.com

On Thu, 19 Jun 2008 18:32:43 +1000, Christoph Hellwig <hch@infradead.org>  
wrote:

> On Wed, Jun 18, 2008 at 07:07:59PM +1000, Barry Naujok wrote:
>> 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.
>
> Looks good to me, although I don't really like how much of a mess
> xfs_dir2_leafn_lookup_for_entry has become. No idea for a nice why
> to write it, though.

Yeah, bit of a problem with that. I can un-optimise the original code
by not keeping the current block between calls to
xfs_dir2_leafn_lookup_for_entry() and always freeing it. That will
clean it up a lot.

> Btw, any chance we could get some CI tests in xfsqa?  Looks like you
> have quite a few testcases that found issues so far.

Yes, they are coming.

Barry.

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2008-06-19  8:36 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-06-18  9:07 [PATCH] XFS: Fix returning case-preserved name with CI node form directories Barry Naujok
2008-06-19  8:32 ` Christoph Hellwig
2008-06-19  8:39   ` Barry Naujok

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox