summaryrefslogtreecommitdiffstats
path: root/sys/ufs
diff options
context:
space:
mode:
authorkib <kib@FreeBSD.org>2007-12-29 13:31:27 +0000
committerkib <kib@FreeBSD.org>2007-12-29 13:31:27 +0000
commitc9fb56ffc84b6f60b46e1e1bf352c0d8282d1a0b (patch)
tree52e56fa503564626194a5a63e7972afa982baf5f /sys/ufs
parent48aadec12af905550d9901443487a1008758317c (diff)
downloadFreeBSD-src-c9fb56ffc84b6f60b46e1e1bf352c0d8282d1a0b.zip
FreeBSD-src-c9fb56ffc84b6f60b46e1e1bf352c0d8282d1a0b.tar.gz
The ffs_balloc() routines, whan allocating the indirect blocks for
the inode, do the rollback in case the allocation failed (due to insufficient free space or quota limits). But, the code does leaves the buffers corresponding to the inoirect blocks on the vnode bufobj list. This causes several assertion failures (for instance, "ffs_truncate3" in ffs_truncate()) to fail, and could result in the indirect block aliasing problem, like writing the context of such blocks to random disk location. Remove the buffers from the bufobj properly. Reported and tested by: Peter Holm Reviewed by: tegge MFC after: 3 weeks
Diffstat (limited to 'sys/ufs')
-rw-r--r--sys/ufs/ffs/ffs_balloc.c44
1 files changed, 42 insertions, 2 deletions
diff --git a/sys/ufs/ffs/ffs_balloc.c b/sys/ufs/ffs/ffs_balloc.c
index 0cc1d26..d74b00c 100644
--- a/sys/ufs/ffs/ffs_balloc.c
+++ b/sys/ufs/ffs/ffs_balloc.c
@@ -102,6 +102,7 @@ ffs_balloc_ufs1(struct vnode *vp, off_t startoffset, int size,
ufs2_daddr_t newb;
ufs1_daddr_t *bap, pref;
ufs1_daddr_t *allocib, *blkp, *allocblk, allociblk[NIADDR + 1];
+ ufs2_daddr_t *lbns_remfree, lbns[NIADDR + 1];
int unwindidx = -1;
ip = VTOI(vp);
@@ -231,6 +232,7 @@ ffs_balloc_ufs1(struct vnode *vp, off_t startoffset, int size,
nb = dp->di_ib[indirs[0].in_off];
allocib = NULL;
allocblk = allociblk;
+ lbns_remfree = lbns;
if (nb == 0) {
UFS_LOCK(ump);
pref = ffs_blkpref_ufs1(ip, lbn, 0, (ufs1_daddr_t *)0);
@@ -239,6 +241,7 @@ ffs_balloc_ufs1(struct vnode *vp, off_t startoffset, int size,
return (error);
nb = newb;
*allocblk++ = nb;
+ *lbns_remfree++ = indirs[1].in_lbn;
bp = getblk(vp, indirs[1].in_lbn, fs->fs_bsize, 0, 0, 0);
bp->b_blkno = fsbtodb(fs, nb);
vfs_bio_clrbuf(bp);
@@ -289,6 +292,7 @@ ffs_balloc_ufs1(struct vnode *vp, off_t startoffset, int size,
}
nb = newb;
*allocblk++ = nb;
+ *lbns_remfree++ = indirs[i].in_lbn;
nbp = getblk(vp, indirs[i].in_lbn, fs->fs_bsize, 0, 0, 0);
nbp->b_blkno = fsbtodb(fs, nb);
vfs_bio_clrbuf(nbp);
@@ -342,6 +346,7 @@ ffs_balloc_ufs1(struct vnode *vp, off_t startoffset, int size,
}
nb = newb;
*allocblk++ = nb;
+ *lbns_remfree++ = lbn;
nbp = getblk(vp, lbn, fs->fs_bsize, 0, 0, 0);
nbp->b_blkno = fsbtodb(fs, nb);
if (flags & BA_CLRBUF)
@@ -403,7 +408,22 @@ fail:
* have an error to return to the user.
*/
(void) ffs_syncvnode(vp, MNT_WAIT);
- for (deallocated = 0, blkp = allociblk; blkp < allocblk; blkp++) {
+ for (deallocated = 0, blkp = allociblk, lbns_remfree = lbns;
+ blkp < allocblk; blkp++, lbns_remfree++) {
+ /*
+ * We shall not leave the freed blocks on the vnode
+ * buffer object lists.
+ */
+ bp = getblk(vp, *lbns_remfree, fs->fs_bsize, 0, 0, GB_NOCREAT);
+ if (bp != NULL) {
+ bp->b_flags |= (B_INVAL | B_RELBUF);
+ bp->b_flags &= ~B_ASYNC;
+ brelse(bp);
+ }
+
+ /*
+ * After the buffer is invalidated, free the block.
+ */
ffs_blkfree(ump, fs, ip->i_devvp, *blkp, fs->fs_bsize,
ip->i_number);
deallocated += fs->fs_bsize;
@@ -464,6 +484,7 @@ ffs_balloc_ufs2(struct vnode *vp, off_t startoffset, int size,
struct indir indirs[NIADDR + 2];
ufs2_daddr_t nb, newb, *bap, pref;
ufs2_daddr_t *allocib, *blkp, *allocblk, allociblk[NIADDR + 1];
+ ufs2_daddr_t *lbns_remfree, lbns[NIADDR + 1];
int deallocated, osize, nsize, num, i, error;
int unwindidx = -1;
@@ -703,6 +724,7 @@ ffs_balloc_ufs2(struct vnode *vp, off_t startoffset, int size,
nb = dp->di_ib[indirs[0].in_off];
allocib = NULL;
allocblk = allociblk;
+ lbns_remfree = lbns;
if (nb == 0) {
UFS_LOCK(ump);
pref = ffs_blkpref_ufs2(ip, lbn, 0, (ufs2_daddr_t *)0);
@@ -711,6 +733,7 @@ ffs_balloc_ufs2(struct vnode *vp, off_t startoffset, int size,
return (error);
nb = newb;
*allocblk++ = nb;
+ *lbns_remfree++ = indirs[1].in_lbn;
bp = getblk(vp, indirs[1].in_lbn, fs->fs_bsize, 0, 0, 0);
bp->b_blkno = fsbtodb(fs, nb);
vfs_bio_clrbuf(bp);
@@ -761,6 +784,7 @@ ffs_balloc_ufs2(struct vnode *vp, off_t startoffset, int size,
}
nb = newb;
*allocblk++ = nb;
+ *lbns_remfree++ = indirs[i].in_lbn;
nbp = getblk(vp, indirs[i].in_lbn, fs->fs_bsize, 0, 0, 0);
nbp->b_blkno = fsbtodb(fs, nb);
vfs_bio_clrbuf(nbp);
@@ -814,6 +838,7 @@ ffs_balloc_ufs2(struct vnode *vp, off_t startoffset, int size,
}
nb = newb;
*allocblk++ = nb;
+ *lbns_remfree++ = lbn;
nbp = getblk(vp, lbn, fs->fs_bsize, 0, 0, 0);
nbp->b_blkno = fsbtodb(fs, nb);
if (flags & BA_CLRBUF)
@@ -881,7 +906,22 @@ fail:
* have an error to return to the user.
*/
(void) ffs_syncvnode(vp, MNT_WAIT);
- for (deallocated = 0, blkp = allociblk; blkp < allocblk; blkp++) {
+ for (deallocated = 0, blkp = allociblk, lbns_remfree = lbns;
+ blkp < allocblk; blkp++, lbns_remfree++) {
+ /*
+ * We shall not leave the freed blocks on the vnode
+ * buffer object lists.
+ */
+ bp = getblk(vp, *lbns_remfree, fs->fs_bsize, 0, 0, GB_NOCREAT);
+ if (bp != NULL) {
+ bp->b_flags |= (B_INVAL | B_RELBUF);
+ bp->b_flags &= ~B_ASYNC;
+ brelse(bp);
+ }
+
+ /*
+ * After the buffer is invalidated, free the block.
+ */
ffs_blkfree(ump, fs, ip->i_devvp, *blkp, fs->fs_bsize,
ip->i_number);
deallocated += fs->fs_bsize;
OpenPOWER on IntegriCloud