Gitweb:     
http://git.kernel.org/git/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commit;h=e5889e90dda328443161e9512f1123c9814d03de
Commit:     e5889e90dda328443161e9512f1123c9814d03de
Parent:     7666ab5fb378678a9d5eb3c0dc8d3170e274e7a4
Author:     Barry Naujok <[EMAIL PROTECTED]>
AuthorDate: Sat Feb 10 18:35:58 2007 +1100
Committer:  Tim Shimmin <[EMAIL PROTECTED]>
CommitDate: Sat Feb 10 18:35:58 2007 +1100

    [XFS] Fix attr2 corruption with btree data extents
    
    SGI-PV: 958747
    SGI-Modid: xfs-linux-melb:xfs-kern:27792a
    
    Signed-off-by: Barry Naujok <[EMAIL PROTECTED]>
    Signed-off-by: Russell Cattelan <[EMAIL PROTECTED]>
    Signed-off-by: Tim Shimmin <[EMAIL PROTECTED]>
---
 fs/xfs/xfs_attr.c      |   19 +++++++++++--------
 fs/xfs/xfs_attr_leaf.c |   45 ++++++++++++++++++++++++++++++++++++++++++---
 fs/xfs/xfs_bmap.c      |    1 +
 3 files changed, 54 insertions(+), 11 deletions(-)

diff --git a/fs/xfs/xfs_attr.c b/fs/xfs/xfs_attr.c
index c6c2596..a5cb097 100644
--- a/fs/xfs/xfs_attr.c
+++ b/fs/xfs/xfs_attr.c
@@ -199,18 +199,14 @@ xfs_attr_set_int(xfs_inode_t *dp, const char *name, int 
namelen,
                return (error);
 
        /*
-        * Determine space new attribute will use, and if it would be
-        * "local" or "remote" (note: local != inline).
-        */
-       size = xfs_attr_leaf_newentsize(namelen, valuelen,
-                                       mp->m_sb.sb_blocksize, &local);
-
-       /*
         * If the inode doesn't have an attribute fork, add one.
         * (inode must not be locked when we call this routine)
         */
        if (XFS_IFORK_Q(dp) == 0) {
-               if ((error = xfs_bmap_add_attrfork(dp, size, rsvd)))
+               int sf_size = sizeof(xfs_attr_sf_hdr_t) +
+                             XFS_ATTR_SF_ENTSIZE_BYNAME(namelen, valuelen);
+
+               if ((error = xfs_bmap_add_attrfork(dp, sf_size, rsvd)))
                        return(error);
        }
 
@@ -231,6 +227,13 @@ xfs_attr_set_int(xfs_inode_t *dp, const char *name, int 
namelen,
        args.addname = 1;
        args.oknoent = 1;
 
+       /*
+        * Determine space new attribute will use, and if it would be
+        * "local" or "remote" (note: local != inline).
+        */
+       size = xfs_attr_leaf_newentsize(namelen, valuelen,
+                                       mp->m_sb.sb_blocksize, &local);
+
        nblks = XFS_DAENTER_SPACE_RES(mp, XFS_ATTR_FORK);
        if (local) {
                if (size > (mp->m_sb.sb_blocksize >> 1)) {
diff --git a/fs/xfs/xfs_attr_leaf.c b/fs/xfs/xfs_attr_leaf.c
index cc4ffa4..e466413 100644
--- a/fs/xfs/xfs_attr_leaf.c
+++ b/fs/xfs/xfs_attr_leaf.c
@@ -150,6 +150,7 @@ xfs_attr_shortform_bytesfit(xfs_inode_t *dp, int bytes)
        int offset;
        int minforkoff; /* lower limit on valid forkoff locations */
        int maxforkoff; /* upper limit on valid forkoff locations */
+       int dsize;      
        xfs_mount_t *mp = dp->i_mount;
 
        offset = (XFS_LITINO(mp) - bytes) >> 3; /* rounded down */
@@ -169,8 +170,43 @@ xfs_attr_shortform_bytesfit(xfs_inode_t *dp, int bytes)
                return 0;
        }
 
-       /* data fork btree root can have at least this many key/ptr pairs */
-       minforkoff = MAX(dp->i_df.if_bytes, XFS_BMDR_SPACE_CALC(MINDBTPTRS));
+       dsize = dp->i_df.if_bytes;
+       
+       switch (dp->i_d.di_format) {
+       case XFS_DINODE_FMT_EXTENTS:
+               /* 
+                * If there is no attr fork and the data fork is extents, 
+                * determine if creating the default attr fork will result 
+                * in the extents form migrating to btree. If so, the 
+                * minimum offset only needs to be the space required for 
+                * the btree root.
+                */ 
+               if (!dp->i_d.di_forkoff && dp->i_df.if_bytes > mp->m_attroffset)
+                       dsize = XFS_BMDR_SPACE_CALC(MINDBTPTRS);
+               break;
+               
+       case XFS_DINODE_FMT_BTREE:
+               /*
+                * If have data btree then keep forkoff if we have one,
+                * otherwise we are adding a new attr, so then we set 
+                * minforkoff to where the btree root can finish so we have 
+                * plenty of room for attrs
+                */
+               if (dp->i_d.di_forkoff) {
+                       if (offset < dp->i_d.di_forkoff) 
+                               return 0;
+                       else 
+                               return dp->i_d.di_forkoff;
+               } else
+                       dsize = XFS_BMAP_BROOT_SPACE(dp->i_df.if_broot);
+               break;
+       }
+       
+       /* 
+        * A data fork btree root must have space for at least 
+        * MINDBTPTRS key/ptr pairs if the data fork is small or empty.
+        */
+       minforkoff = MAX(dsize, XFS_BMDR_SPACE_CALC(MINDBTPTRS));
        minforkoff = roundup(minforkoff, 8) >> 3;
 
        /* attr fork btree root can have at least this many key/ptr pairs */
@@ -336,7 +372,8 @@ xfs_attr_shortform_remove(xfs_da_args_t *args)
         */
        totsize -= size;
        if (totsize == sizeof(xfs_attr_sf_hdr_t) && !args->addname &&
-           (mp->m_flags & XFS_MOUNT_ATTR2)) {
+           (mp->m_flags & XFS_MOUNT_ATTR2) && 
+           (dp->i_d.di_format != XFS_DINODE_FMT_BTREE)) {
                /*
                 * Last attribute now removed, revert to original
                 * inode format making all literal area available
@@ -748,6 +785,7 @@ xfs_attr_shortform_allfit(xfs_dabuf_t *bp, xfs_inode_t *dp)
                                + be16_to_cpu(name_loc->valuelen);
        }
        if ((dp->i_mount->m_flags & XFS_MOUNT_ATTR2) &&
+           (dp->i_d.di_format != XFS_DINODE_FMT_BTREE) &&
            (bytes == sizeof(struct xfs_attr_sf_hdr)))
                return(-1);
        return(xfs_attr_shortform_bytesfit(dp, bytes));
@@ -786,6 +824,7 @@ xfs_attr_leaf_to_shortform(xfs_dabuf_t *bp, xfs_da_args_t 
*args, int forkoff)
 
        if (forkoff == -1) {
                ASSERT(dp->i_mount->m_flags & XFS_MOUNT_ATTR2);
+               ASSERT(dp->i_d.di_format != XFS_DINODE_FMT_BTREE);
 
                /*
                 * Last attribute was removed, revert to original
diff --git a/fs/xfs/xfs_bmap.c b/fs/xfs/xfs_bmap.c
index 498ad50..024d845 100644
--- a/fs/xfs/xfs_bmap.c
+++ b/fs/xfs/xfs_bmap.c
@@ -3543,6 +3543,7 @@ xfs_bmap_forkoff_reset(
        if (whichfork == XFS_ATTR_FORK &&
            (ip->i_d.di_format != XFS_DINODE_FMT_DEV) &&
            (ip->i_d.di_format != XFS_DINODE_FMT_UUID) &&
+           (ip->i_d.di_format != XFS_DINODE_FMT_BTREE) &&
            ((mp->m_attroffset >> 3) > ip->i_d.di_forkoff)) {
                ip->i_d.di_forkoff = mp->m_attroffset >> 3;
                ip->i_df.if_ext_max = XFS_IFORK_DSIZE(ip) /
-
To unsubscribe from this list: send the line "unsubscribe git-commits-head" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to