aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBarry Naujok <bnaujok@sgi.com>2007-02-10 02:35:58 -0500
committerTim Shimmin <tes@sgi.com>2007-02-10 02:35:58 -0500
commite5889e90dda328443161e9512f1123c9814d03de (patch)
tree36c0fd406520251c8ec2f419cad45f800a2e2f3d
parent7666ab5fb378678a9d5eb3c0dc8d3170e274e7a4 (diff)
[XFS] Fix attr2 corruption with btree data extents
SGI-PV: 958747 SGI-Modid: xfs-linux-melb:xfs-kern:27792a Signed-off-by: Barry Naujok <bnaujok@sgi.com> Signed-off-by: Russell Cattelan <cattelan@thebarn.com> Signed-off-by: Tim Shimmin <tes@sgi.com>
-rw-r--r--fs/xfs/xfs_attr.c19
-rw-r--r--fs/xfs/xfs_attr_leaf.c45
-rw-r--r--fs/xfs/xfs_bmap.c1
3 files changed, 54 insertions, 11 deletions
diff --git a/fs/xfs/xfs_attr.c b/fs/xfs/xfs_attr.c
index c6c2596e827a..a5cb0972bb8a 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,
199 return (error); 199 return (error);
200 200
201 /* 201 /*
202 * Determine space new attribute will use, and if it would be
203 * "local" or "remote" (note: local != inline).
204 */
205 size = xfs_attr_leaf_newentsize(namelen, valuelen,
206 mp->m_sb.sb_blocksize, &local);
207
208 /*
209 * If the inode doesn't have an attribute fork, add one. 202 * If the inode doesn't have an attribute fork, add one.
210 * (inode must not be locked when we call this routine) 203 * (inode must not be locked when we call this routine)
211 */ 204 */
212 if (XFS_IFORK_Q(dp) == 0) { 205 if (XFS_IFORK_Q(dp) == 0) {
213 if ((error = xfs_bmap_add_attrfork(dp, size, rsvd))) 206 int sf_size = sizeof(xfs_attr_sf_hdr_t) +
207 XFS_ATTR_SF_ENTSIZE_BYNAME(namelen, valuelen);
208
209 if ((error = xfs_bmap_add_attrfork(dp, sf_size, rsvd)))
214 return(error); 210 return(error);
215 } 211 }
216 212
@@ -231,6 +227,13 @@ xfs_attr_set_int(xfs_inode_t *dp, const char *name, int namelen,
231 args.addname = 1; 227 args.addname = 1;
232 args.oknoent = 1; 228 args.oknoent = 1;
233 229
230 /*
231 * Determine space new attribute will use, and if it would be
232 * "local" or "remote" (note: local != inline).
233 */
234 size = xfs_attr_leaf_newentsize(namelen, valuelen,
235 mp->m_sb.sb_blocksize, &local);
236
234 nblks = XFS_DAENTER_SPACE_RES(mp, XFS_ATTR_FORK); 237 nblks = XFS_DAENTER_SPACE_RES(mp, XFS_ATTR_FORK);
235 if (local) { 238 if (local) {
236 if (size > (mp->m_sb.sb_blocksize >> 1)) { 239 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 cc4ffa413da4..e4664136843d 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)
150 int offset; 150 int offset;
151 int minforkoff; /* lower limit on valid forkoff locations */ 151 int minforkoff; /* lower limit on valid forkoff locations */
152 int maxforkoff; /* upper limit on valid forkoff locations */ 152 int maxforkoff; /* upper limit on valid forkoff locations */
153 int dsize;
153 xfs_mount_t *mp = dp->i_mount; 154 xfs_mount_t *mp = dp->i_mount;
154 155
155 offset = (XFS_LITINO(mp) - bytes) >> 3; /* rounded down */ 156 offset = (XFS_LITINO(mp) - bytes) >> 3; /* rounded down */
@@ -169,8 +170,43 @@ xfs_attr_shortform_bytesfit(xfs_inode_t *dp, int bytes)
169 return 0; 170 return 0;
170 } 171 }
171 172
172 /* data fork btree root can have at least this many key/ptr pairs */ 173 dsize = dp->i_df.if_bytes;
173 minforkoff = MAX(dp->i_df.if_bytes, XFS_BMDR_SPACE_CALC(MINDBTPTRS)); 174
175 switch (dp->i_d.di_format) {
176 case XFS_DINODE_FMT_EXTENTS:
177 /*
178 * If there is no attr fork and the data fork is extents,
179 * determine if creating the default attr fork will result
180 * in the extents form migrating to btree. If so, the
181 * minimum offset only needs to be the space required for
182 * the btree root.
183 */
184 if (!dp->i_d.di_forkoff && dp->i_df.if_bytes > mp->m_attroffset)
185 dsize = XFS_BMDR_SPACE_CALC(MINDBTPTRS);
186 break;
187
188 case XFS_DINODE_FMT_BTREE:
189 /*
190 * If have data btree then keep forkoff if we have one,
191 * otherwise we are adding a new attr, so then we set
192 * minforkoff to where the btree root can finish so we have
193 * plenty of room for attrs
194 */
195 if (dp->i_d.di_forkoff) {
196 if (offset < dp->i_d.di_forkoff)
197 return 0;
198 else
199 return dp->i_d.di_forkoff;
200 } else
201 dsize = XFS_BMAP_BROOT_SPACE(dp->i_df.if_broot);
202 break;
203 }
204
205 /*
206 * A data fork btree root must have space for at least
207 * MINDBTPTRS key/ptr pairs if the data fork is small or empty.
208 */
209 minforkoff = MAX(dsize, XFS_BMDR_SPACE_CALC(MINDBTPTRS));
174 minforkoff = roundup(minforkoff, 8) >> 3; 210 minforkoff = roundup(minforkoff, 8) >> 3;
175 211
176 /* attr fork btree root can have at least this many key/ptr pairs */ 212 /* 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)
336 */ 372 */
337 totsize -= size; 373 totsize -= size;
338 if (totsize == sizeof(xfs_attr_sf_hdr_t) && !args->addname && 374 if (totsize == sizeof(xfs_attr_sf_hdr_t) && !args->addname &&
339 (mp->m_flags & XFS_MOUNT_ATTR2)) { 375 (mp->m_flags & XFS_MOUNT_ATTR2) &&
376 (dp->i_d.di_format != XFS_DINODE_FMT_BTREE)) {
340 /* 377 /*
341 * Last attribute now removed, revert to original 378 * Last attribute now removed, revert to original
342 * inode format making all literal area available 379 * inode format making all literal area available
@@ -748,6 +785,7 @@ xfs_attr_shortform_allfit(xfs_dabuf_t *bp, xfs_inode_t *dp)
748 + be16_to_cpu(name_loc->valuelen); 785 + be16_to_cpu(name_loc->valuelen);
749 } 786 }
750 if ((dp->i_mount->m_flags & XFS_MOUNT_ATTR2) && 787 if ((dp->i_mount->m_flags & XFS_MOUNT_ATTR2) &&
788 (dp->i_d.di_format != XFS_DINODE_FMT_BTREE) &&
751 (bytes == sizeof(struct xfs_attr_sf_hdr))) 789 (bytes == sizeof(struct xfs_attr_sf_hdr)))
752 return(-1); 790 return(-1);
753 return(xfs_attr_shortform_bytesfit(dp, bytes)); 791 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)
786 824
787 if (forkoff == -1) { 825 if (forkoff == -1) {
788 ASSERT(dp->i_mount->m_flags & XFS_MOUNT_ATTR2); 826 ASSERT(dp->i_mount->m_flags & XFS_MOUNT_ATTR2);
827 ASSERT(dp->i_d.di_format != XFS_DINODE_FMT_BTREE);
789 828
790 /* 829 /*
791 * Last attribute was removed, revert to original 830 * Last attribute was removed, revert to original
diff --git a/fs/xfs/xfs_bmap.c b/fs/xfs/xfs_bmap.c
index 498ad50d1f45..024d8452c3d3 100644
--- a/fs/xfs/xfs_bmap.c
+++ b/fs/xfs/xfs_bmap.c
@@ -3543,6 +3543,7 @@ xfs_bmap_forkoff_reset(
3543 if (whichfork == XFS_ATTR_FORK && 3543 if (whichfork == XFS_ATTR_FORK &&
3544 (ip->i_d.di_format != XFS_DINODE_FMT_DEV) && 3544 (ip->i_d.di_format != XFS_DINODE_FMT_DEV) &&
3545 (ip->i_d.di_format != XFS_DINODE_FMT_UUID) && 3545 (ip->i_d.di_format != XFS_DINODE_FMT_UUID) &&
3546 (ip->i_d.di_format != XFS_DINODE_FMT_BTREE) &&
3546 ((mp->m_attroffset >> 3) > ip->i_d.di_forkoff)) { 3547 ((mp->m_attroffset >> 3) > ip->i_d.di_forkoff)) {
3547 ip->i_d.di_forkoff = mp->m_attroffset >> 3; 3548 ip->i_d.di_forkoff = mp->m_attroffset >> 3;
3548 ip->i_df.if_ext_max = XFS_IFORK_DSIZE(ip) / 3549 ip->i_df.if_ext_max = XFS_IFORK_DSIZE(ip) /