xfs: more swap extent fixes for dynamic fork offsets

Previous message: [thread] [date] [author]
Next message: [thread] [date] [author]
From: Linux Kernel Mailing List
Date: Tuesday, April 27, 2010 - 9:59 am

Gitweb:     http://git.kernel.org/linus/dd77ef924c835c9813c3f4dc7e9c72e9cd88d238
Commit:     dd77ef924c835c9813c3f4dc7e9c72e9cd88d238
Parent:     b91ce4d14a21fc04d165be30319541e0f9204f15
Author:     Dave Chinner <dchinner@redhat.com>
AuthorDate: Tue Apr 20 17:00:37 2010 +1000
Committer:  Alex Elder <aelder@sgi.com>
CommitDate: Mon Apr 26 12:38:51 2010 -0500

    xfs: more swap extent fixes for dynamic fork offsets
    
    A new xfsqa test (226) with a prototype xfs_fsr change to try to
    handle dynamic fork offsets better triggers an assertion failure
    where the inode data fork is in btree format, yet there is room in
    the inode for it to be in extent format. The two inodes look like:
    
    before: ino 0x101 (target), num_extents 11, Max in-fork extents 6, broot size 40, fork offset 96
    before: ino 0x115 (temp),  num_extents 5, Max in-fork extents 3, broot size 40, fork offset 56
    after: ino 0x101 (target), num_extents 5, Max in-fork extents 6, broot size 40, fork offset 96
    after: ino 0x115 (temp), num_extents 11, Max in-fork extents 3, broot size 40, fork offset 56
    
    Basically the target inode ends up with 5 extents in btree format,
    but it had space for 6 extents in extent format, so ends up
    incorrect. Notably here the broot size is the same, and that is
    where the kernel code is going wrong - the btree root will fit, so
    it lets the swap go ahead.
    
    The check should not allow the swap to take place if the number of
    extents while in btree format is less than the number of extents
    that can fit in the inode in extent format. Adding that check will
    prevent this swap and corruption from occurring.
    
    Signed-off-by: Dave Chinner <dchinner@redhat.com>
    Reviewed-by: Christoph Hellwig <hch@lst.de>
---
 fs/xfs/xfs_dfrag.c |   22 ++++++++++++++++------
 1 files changed, 16 insertions(+), 6 deletions(-)

diff --git a/fs/xfs/xfs_dfrag.c b/fs/xfs/xfs_dfrag.c
index cd27c9d..5bba29a 100644
--- a/fs/xfs/xfs_dfrag.c
+++ b/fs/xfs/xfs_dfrag.c
@@ -177,16 +177,26 @@ xfs_swap_extents_check_format(
 	    XFS_IFORK_NEXTENTS(ip, XFS_DATA_FORK) > tip->i_df.if_ext_max)
 		return EINVAL;
 
-	/* Check root block of temp in btree form to max in target */
+	/*
+	 * If we are in a btree format, check that the temp root block will fit
+	 * in the target and that it has enough extents to be in btree format
+	 * in the target.
+	 *
+	 * Note that we have to be careful to allow btree->extent conversions
+	 * (a common defrag case) which will occur when the temp inode is in
+	 * extent format...
+	 */
 	if (tip->i_d.di_format == XFS_DINODE_FMT_BTREE &&
-	    XFS_IFORK_BOFF(ip) &&
-	    tip->i_df.if_broot_bytes > XFS_IFORK_BOFF(ip))
+	    ((XFS_IFORK_BOFF(ip) &&
+	      tip->i_df.if_broot_bytes > XFS_IFORK_BOFF(ip)) ||
+	     XFS_IFORK_NEXTENTS(tip, XFS_DATA_FORK) <= ip->i_df.if_ext_max))
 		return EINVAL;
 
-	/* Check root block of target in btree form to max in temp */
+	/* Reciprocal target->temp btree format checks */
 	if (ip->i_d.di_format == XFS_DINODE_FMT_BTREE &&
-	    XFS_IFORK_BOFF(tip) &&
-	    ip->i_df.if_broot_bytes > XFS_IFORK_BOFF(tip))
+	    ((XFS_IFORK_BOFF(tip) &&
+	      ip->i_df.if_broot_bytes > XFS_IFORK_BOFF(tip)) ||
+	     XFS_IFORK_NEXTENTS(ip, XFS_DATA_FORK) <= tip->i_df.if_ext_max))
 		return EINVAL;
 
 	return 0;
--
To unsubscribe from this list: send the line "unsubscribe git-commits-head" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Previous message: [thread] [date] [author]
Next message: [thread] [date] [author]

Messages in current thread:
xfs: more swap extent fixes for dynamic fork offsets, Linux Kernel Mailing ..., (Tue Apr 27, 9:59 am)