:I think I've found it. Here's an easy way to repeat the problem to
:start with:
:
: mount_mfs -T fd1440 none /mnt
: cd /mnt
: dd if=/dev/zero bs=4k of=test seek=1036 count=1
: dd if=/dev/zero bs=4k of=test1 count=1
: dd if=/dev/zero bs=4k of=test2
: rm test1
: dd if=/dev/zero bs=4k of=test seek=8000 count=1
: echo > test
:
:It looks as if the problem is in ffs_balloc(), and occurs as follows:
: - ffs_balloc() is asked to allocate a doubly indirect block.
: - The first-level indirection block already exists
: - The second-level indirection block does not exist, but is
: successfully allocated.
: - This block is linked into the first-level indirection block by
: the line:
:
: bap[indirs[i - 1].in_off] = nb;
:
: - Allocation of the data block fails.
: - All allocated blocks are then released, but there is still
: a link in the first-level indirection block to what is
: now a free block.
:
:The fix should be relatively straightforward - either the code should
:avoid linking new indirection blocks until all allocations succeed,
:or it should back out the changes on failure.
:
:Ian
Great detective work! The 'allocib' variable is used to unwind the
'first' new indirect block allocation, but it is *only* initialized
for the Level 0 block. So the unwinding only works when the Level 0
indirect block had to be allocated. When the Level 0 indirect block
is valid and a deeper level indirect block had to be allocated,
allocib is not initialized and the association is not unwound even
though the deeper indirect block is freed when the data block allocation
fails.
Unfortunately, we can't simply initialize 'allocib' here, because it
would point into the middle of a buffer. The fix is thus not entirely
trivial.
This is what I think we need to do, but THIS PATCH IS NOT TESTED AND
COULD BE COMPLETELY WRONG. Any filesystem gurus out there who can look
at this and tell me if it's correct?
-Matt
Matthew Dillon
<[EMAIL PROTECTED]>
Index: ffs_balloc.c
===================================================================
RCS file: /home/ncvs/src/sys/ufs/ffs/ffs_balloc.c,v
retrieving revision 1.25
diff -u -r1.25 ffs_balloc.c
--- ffs_balloc.c 2000/01/11 08:27:00 1.25
+++ ffs_balloc.c 2000/02/24 02:25:53
@@ -77,6 +77,7 @@
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 @@
}
}
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,26 @@
ffs_blkfree(ip, *blkp, fs->fs_bsize);
deallocated += fs->fs_bsize;
}
- if (allocib != NULL)
+ if (allocib != NULL) {
*allocib = 0;
+ } else if (unwindidx >= 0) {
+ error = bread(vp,
+ indirs[unwindidx].in_lbn, (int)fs->fs_bsize, NOCRED, &bp);
+ if (error) {
+ panic("Could not unwind indirect block, error %d", error);
+ 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
/*
To Unsubscribe: send mail to [EMAIL PROTECTED]
with "unsubscribe freebsd-current" in the body of the message