diff options
author | Barry Naujok <bnaujok@sgi.com> | 2007-02-10 02:35:58 -0500 |
---|---|---|
committer | Tim Shimmin <tes@sgi.com> | 2007-02-10 02:35:58 -0500 |
commit | e5889e90dda328443161e9512f1123c9814d03de (patch) | |
tree | 36c0fd406520251c8ec2f419cad45f800a2e2f3d /fs/xfs | |
parent | 7666ab5fb378678a9d5eb3c0dc8d3170e274e7a4 (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>
Diffstat (limited to 'fs/xfs')
-rw-r--r-- | fs/xfs/xfs_attr.c | 19 | ||||
-rw-r--r-- | fs/xfs/xfs_attr_leaf.c | 45 | ||||
-rw-r--r-- | 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 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) / |