summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sys/ufs/ext2fs/ext2fs_balloc.c149
1 files changed, 100 insertions, 49 deletions
diff --git a/sys/ufs/ext2fs/ext2fs_balloc.c b/sys/ufs/ext2fs/ext2fs_balloc.c
index f105424b2a9..3696f565f6d 100644
--- a/sys/ufs/ext2fs/ext2fs_balloc.c
+++ b/sys/ufs/ext2fs/ext2fs_balloc.c
@@ -1,5 +1,5 @@
-/* $OpenBSD: ext2fs_balloc.c,v 1.5 2001/06/23 02:07:50 csapuntz Exp $ */
-/* $NetBSD: ext2fs_balloc.c,v 1.1 1997/06/11 09:33:44 bouyer Exp $ */
+/* $OpenBSD: ext2fs_balloc.c,v 1.6 2001/09/17 23:31:39 art Exp $ */
+/* $NetBSD: ext2fs_balloc.c,v 1.10 2001/07/04 21:16:01 chs Exp $ */
/*
* Copyright (c) 1997 Manuel Bouyer.
@@ -64,12 +64,15 @@ ext2fs_buf_alloc(struct inode *ip, daddr_t bn, int size, struct ucred *cred,
struct buf **bpp, int flags)
{
struct m_ext2fs *fs;
- daddr_t nb;
+ ufs_daddr_t nb;
struct buf *bp, *nbp;
struct vnode *vp = ITOV(ip);
struct indir indirs[NIADDR + 2];
- daddr_t newb, lbn, *bap, pref;
+ ufs_daddr_t newb, lbn, *bap, pref;
int num, i, error;
+ u_int deallocated;
+ ufs_daddr_t *allocib, *blkp, *allocblk, allociblk[NIADDR + 1];
+ int unwindidx = -1;
*bpp = NULL;
if (bn < 0)
@@ -81,7 +84,7 @@ ext2fs_buf_alloc(struct inode *ip, daddr_t bn, int size, struct ucred *cred,
* The first NDADDR blocks are direct blocks
*/
if (bn < NDADDR) {
- nb = ip->i_e2fs_blocks[bn];
+ nb = fs2h32(ip->i_e2fs_blocks[bn]);
if (nb != 0) {
error = bread(vp, bn, fs->e2fs_bsize, NOCRED, &bp);
if (error) {
@@ -90,21 +93,24 @@ ext2fs_buf_alloc(struct inode *ip, daddr_t bn, int size, struct ucred *cred,
}
*bpp = bp;
return (0);
- } else {
- error = ext2fs_alloc(ip, bn,
- ext2fs_blkpref(ip, bn, (int)bn, &ip->i_e2fs_blocks[0]),
- cred, &newb);
- if (error)
- return (error);
- ip->i_e2fs_last_lblk = lbn;
- ip->i_e2fs_last_blk = newb;
- bp = getblk(vp, bn, fs->e2fs_bsize, 0, 0);
- bp->b_blkno = fsbtodb(fs, newb);
- if (flags & B_CLRBUF)
- clrbuf(bp);
}
- ip->i_e2fs_blocks[bn] = dbtofsb(fs, bp->b_blkno);
+
+ /*
+ * allocate a new direct block.
+ */
+ error = ext2fs_alloc(ip, bn,
+ ext2fs_blkpref(ip, bn, (int)bn, &ip->i_e2fs_blocks[0]),
+ cred, &newb);
+ if (error)
+ return (error);
+ ip->i_e2fs_last_lblk = lbn;
+ ip->i_e2fs_last_blk = newb;
+ ip->i_e2fs_blocks[bn] = h2fs32(newb);
ip->i_flag |= IN_CHANGE | IN_UPDATE;
+ bp = getblk(vp, bn, fs->e2fs_bsize, 0, 0);
+ bp->b_blkno = fsbtodb(fs, newb);
+ if (flags & B_CLRBUF)
+ clrbuf(bp);
*bpp = bp;
return (0);
}
@@ -122,14 +128,16 @@ ext2fs_buf_alloc(struct inode *ip, daddr_t bn, int size, struct ucred *cred,
* Fetch the first indirect block allocating if necessary.
*/
--num;
- nb = ip->i_e2fs_blocks[NDADDR + indirs[0].in_off];
+ nb = fs2h32(ip->i_e2fs_blocks[NDADDR + indirs[0].in_off]);
+ allocib = NULL;
+ allocblk = allociblk;
if (nb == 0) {
- pref = ext2fs_blkpref(ip, lbn, 0, (daddr_t *)0);
- error = ext2fs_alloc(ip, lbn, pref,
- cred, &newb);
+ pref = ext2fs_blkpref(ip, lbn, 0, (ufs_daddr_t *)0);
+ error = ext2fs_alloc(ip, lbn, pref, cred, &newb);
if (error)
return (error);
nb = newb;
+ *allocblk++ = nb;
ip->i_e2fs_last_blk = newb;
bp = getblk(vp, indirs[1].in_lbn, fs->e2fs_bsize, 0, 0);
bp->b_blkno = fsbtodb(fs, newb);
@@ -138,11 +146,11 @@ ext2fs_buf_alloc(struct inode *ip, daddr_t bn, int size, struct ucred *cred,
* Write synchronously so that indirect blocks
* never point at garbage.
*/
- if ((error = bwrite(bp)) != 0) {
- ext2fs_blkfree(ip, nb);
- return (error);
- }
- ip->i_e2fs_blocks[NDADDR + indirs[0].in_off] = newb;
+ if ((error = bwrite(bp)) != 0)
+ goto fail;
+ unwindidx = 0;
+ allocib = &ip->i_e2fs_blocks[NDADDR + indirs[0].in_off];
+ *allocib = h2fs32(newb);
ip->i_flag |= IN_CHANGE | IN_UPDATE;
}
/*
@@ -150,28 +158,28 @@ ext2fs_buf_alloc(struct inode *ip, daddr_t bn, int size, struct ucred *cred,
*/
for (i = 1;;) {
error = bread(vp,
- indirs[i].in_lbn, (int)fs->e2fs_bsize, NOCRED, &bp);
+ indirs[i].in_lbn, (int)fs->e2fs_bsize, NOCRED, &bp);
if (error) {
brelse(bp);
- return (error);
+ goto fail;
}
- bap = (daddr_t *)bp->b_data;
- nb = bap[indirs[i].in_off];
+ bap = (ufs_daddr_t *)bp->b_data;
+ nb = fs2h32(bap[indirs[i].in_off]);
if (i == num)
break;
- i += 1;
+ i++;
if (nb != 0) {
brelse(bp);
continue;
}
- pref = ext2fs_blkpref(ip, lbn, 0, (daddr_t *)0);
- error = ext2fs_alloc(ip, lbn, pref, cred,
- &newb);
+ pref = ext2fs_blkpref(ip, lbn, 0, (ufs_daddr_t *)0);
+ error = ext2fs_alloc(ip, lbn, pref, cred, &newb);
if (error) {
brelse(bp);
- return (error);
+ goto fail;
}
nb = newb;
+ *allocblk++ = nb;
ip->i_e2fs_last_blk = newb;
nbp = getblk(vp, indirs[i].in_lbn, fs->e2fs_bsize, 0, 0);
nbp->b_blkno = fsbtodb(fs, nb);
@@ -181,11 +189,12 @@ ext2fs_buf_alloc(struct inode *ip, daddr_t bn, int size, struct ucred *cred,
* never point at garbage.
*/
if ((error = bwrite(nbp)) != 0) {
- ext2fs_blkfree(ip, nb);
brelse(bp);
- return (error);
+ goto fail;
}
- bap[indirs[i - 1].in_off] = nb;
+ if (unwindidx < 0)
+ unwindidx = i - 1;
+ bap[indirs[i - 1].in_off] = h2fs32(nb);
/*
* If required, write synchronously, otherwise use
* delayed write.
@@ -200,21 +209,17 @@ ext2fs_buf_alloc(struct inode *ip, daddr_t bn, int size, struct ucred *cred,
* Get the data block, allocating if necessary.
*/
if (nb == 0) {
- pref = ext2fs_blkpref(ip, lbn, indirs[i].in_off, &bap[0]);
- error = ext2fs_alloc(ip, lbn, pref, cred,
- &newb);
+ pref = ext2fs_blkpref(ip, lbn, indirs[num].in_off, &bap[0]);
+ error = ext2fs_alloc(ip, lbn, pref, cred, &newb);
if (error) {
brelse(bp);
- return (error);
+ goto fail;
}
nb = newb;
+ *allocblk++ = nb;
ip->i_e2fs_last_lblk = lbn;
ip->i_e2fs_last_blk = newb;
- nbp = getblk(vp, lbn, fs->e2fs_bsize, 0, 0);
- nbp->b_blkno = fsbtodb(fs, nb);
- if (flags & B_CLRBUF)
- clrbuf(nbp);
- bap[indirs[i].in_off] = nb;
+ bap[indirs[num].in_off] = h2fs32(nb);
/*
* If required, write synchronously, otherwise use
* delayed write.
@@ -224,6 +229,10 @@ ext2fs_buf_alloc(struct inode *ip, daddr_t bn, int size, struct ucred *cred,
} else {
bdwrite(bp);
}
+ nbp = getblk(vp, lbn, fs->e2fs_bsize, 0, 0);
+ nbp->b_blkno = fsbtodb(fs, nb);
+ if (flags & B_CLRBUF)
+ clrbuf(nbp);
*bpp = nbp;
return (0);
}
@@ -232,12 +241,54 @@ ext2fs_buf_alloc(struct inode *ip, daddr_t bn, int size, struct ucred *cred,
error = bread(vp, lbn, (int)fs->e2fs_bsize, NOCRED, &nbp);
if (error) {
brelse(nbp);
- return (error);
+ goto fail;
}
} else {
nbp = getblk(vp, lbn, fs->e2fs_bsize, 0, 0);
nbp->b_blkno = fsbtodb(fs, nb);
}
+
*bpp = nbp;
return (0);
+fail:
+ /*
+ * If we have failed part way through block allocation, we
+ * have to deallocate any indirect blocks that we have allocated.
+ */
+ for (deallocated = 0, blkp = allociblk; blkp < allocblk; blkp++) {
+ ext2fs_blkfree(ip, *blkp);
+ deallocated += fs->e2fs_bsize;
+ }
+ if (unwindidx >= 0) {
+ if (unwindidx == 0) {
+ *allocib = 0;
+ } else {
+ int r;
+
+ r = bread(vp, indirs[unwindidx].in_lbn,
+ (int)fs->e2fs_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
+ bdwrite(bp);
+ }
+ }
+ for (i = unwindidx + 1; i <= num; i++) {
+ bp = getblk(vp, indirs[i].in_lbn, (int)fs->e2fs_bsize,
+ 0, 0);
+ bp->b_flags |= B_INVAL;
+ brelse(bp);
+ }
+ }
+ if (deallocated) {
+ ip->i_e2fs_nblock -= btodb(deallocated);
+ ip->i_e2fs_flags |= IN_CHANGE | IN_UPDATE;
+ }
+ return error;
}