summaryrefslogtreecommitdiffstats
path: root/sys/ufs/ffs/ffs_balloc.c
diff options
context:
space:
mode:
authordillon <dillon@FreeBSD.org>2000-02-24 20:43:20 +0000
committerdillon <dillon@FreeBSD.org>2000-02-24 20:43:20 +0000
commit414d15acb810815261d32a3a1d8a81cb8ef567b2 (patch)
treeada5519e0f3a37d21b29c9c7067b6d7cf757f482 /sys/ufs/ffs/ffs_balloc.c
parent324686decc26e8f59696c307664285b44ad71ecb (diff)
downloadFreeBSD-src-414d15acb810815261d32a3a1d8a81cb8ef567b2.zip
FreeBSD-src-414d15acb810815261d32a3a1d8a81cb8ef567b2.tar.gz
Fix a 'freeing free block' panic in UFS. The problem occurs when the
filesystem fills up. If the first indirect block exists and FFS is able to allocate deeper indirect blocks, but is not able to allocate the data block, FFS improperly unwinds the indirect blocks and leaves a block pointer hanging to a freed block. This will cause a panic later when the file is removed. The solution is to properly account for the first block-pointer-to-an-indirect-block we had to create in a balloc operation and then unwind it if a failure occurs. Detective work by: Ian Dowse <iedowse@maths.tcd.ie> Reviewed by: mckusick, Ian Dowse <iedowse@maths.tcd.ie> Approved by: jkh
Diffstat (limited to 'sys/ufs/ffs/ffs_balloc.c')
-rw-r--r--sys/ufs/ffs/ffs_balloc.c25
1 files changed, 24 insertions, 1 deletions
diff --git a/sys/ufs/ffs/ffs_balloc.c b/sys/ufs/ffs/ffs_balloc.c
index a40915d..51b249a 100644
--- a/sys/ufs/ffs/ffs_balloc.c
+++ b/sys/ufs/ffs/ffs_balloc.c
@@ -77,6 +77,7 @@ ffs_balloc(ap)
ufs_daddr_t newb, *bap, pref;
int deallocated, osize, nsize, num, i, error;
ufs_daddr_t *allocib, *blkp, *allocblk, allociblk[NIADDR + 1];
+ int unwindidx = -1;
struct proc *p = curproc; /* XXX */
vp = ap->a_vp;
@@ -272,6 +273,8 @@ ffs_balloc(ap)
}
}
bap[indirs[i - 1].in_off] = nb;
+ if (allocib == NULL && unwindidx < 0)
+ unwindidx = i - 1;
/*
* If required, write synchronously, otherwise use
* delayed write.
@@ -349,8 +352,28 @@ fail:
ffs_blkfree(ip, *blkp, fs->fs_bsize);
deallocated += fs->fs_bsize;
}
- if (allocib != NULL)
+ if (allocib != NULL) {
*allocib = 0;
+ } else if (unwindidx >= 0) {
+ int r;
+
+ r = bread(vp, indirs[unwindidx].in_lbn,
+ (int)fs->fs_bsize, NOCRED, &bp);
+ if (r) {
+ panic("Could not unwind indirect block, error %d", r);
+ brelse(bp);
+ } else {
+ bap = (ufs_daddr_t *)bp->b_data;
+ bap[indirs[unwindidx].in_off] = 0;
+ if (flags & B_SYNC) {
+ bwrite(bp);
+ } else {
+ if (bp->b_bufsize == fs->fs_bsize)
+ bp->b_flags |= B_CLUSTEROK;
+ bdwrite(bp);
+ }
+ }
+ }
if (deallocated) {
#ifdef QUOTA
/*
OpenPOWER on IntegriCloud