diff options
Diffstat (limited to 'sys/ufs/lfs/lfs_balloc.c')
-rw-r--r-- | sys/ufs/lfs/lfs_balloc.c | 155 |
1 files changed, 124 insertions, 31 deletions
diff --git a/sys/ufs/lfs/lfs_balloc.c b/sys/ufs/lfs/lfs_balloc.c index 72c1fb4ee52..f00ee4b9203 100644 --- a/sys/ufs/lfs/lfs_balloc.c +++ b/sys/ufs/lfs/lfs_balloc.c @@ -1,4 +1,4 @@ -/* $OpenBSD: lfs_balloc.c,v 1.2 1996/02/27 07:13:20 niklas Exp $ */ +/* $OpenBSD: lfs_balloc.c,v 1.3 1996/07/01 07:41:48 downsj Exp $ */ /* $NetBSD: lfs_balloc.c,v 1.3 1996/02/09 22:28:48 christos Exp $ */ /* @@ -33,7 +33,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)lfs_balloc.c 8.1 (Berkeley) 6/11/93 + * @(#)lfs_balloc.c 8.4 (Berkeley) 5/8/95 */ #include <sys/param.h> #include <sys/systm.h> @@ -55,18 +55,20 @@ #include <ufs/lfs/lfs_extern.h> int -lfs_balloc(vp, iosize, lbn, bpp) +lfs_balloc(vp, offset, iosize, lbn, bpp) struct vnode *vp; + int offset; u_long iosize; - daddr_t lbn; + ufs_daddr_t lbn; struct buf **bpp; { struct buf *ibp, *bp; struct inode *ip; struct lfs *fs; struct indir indirs[NIADDR+2]; - daddr_t daddr; - int bb, error, i, num; + ufs_daddr_t daddr, lastblock; + int bb; /* number of disk blocks in a block disk blocks */ + int error, frags, i, nsize, osize, num; ip = VTOI(vp); fs = ip->i_lfs; @@ -79,6 +81,9 @@ lfs_balloc(vp, iosize, lbn, bpp) * or written earlier). If it did, make sure we don't count it as a * new block or zero out its contents. If it did not, make sure * we allocate any necessary indirect blocks. + * If we are writing a block beyond the end of the file, we need to + * check if the old last block was a fragment. If it was, we need + * to rewrite it. */ *bpp = NULL; @@ -86,37 +91,81 @@ lfs_balloc(vp, iosize, lbn, bpp) if (error) return (error); - *bpp = bp = getblk(vp, lbn, fs->lfs_bsize, 0, 0); + /* Check for block beyond end of file and fragment extension needed. */ + lastblock = lblkno(fs, ip->i_size); + if (lastblock < NDADDR && lastblock < lbn) { + osize = blksize(fs, ip, lastblock); + if (osize < fs->lfs_bsize && osize > 0) { + if (error = lfs_fragextend(vp, osize, fs->lfs_bsize, + lastblock, &bp)) + return(error); + ip->i_size = (lastblock + 1) * fs->lfs_bsize; + vnode_pager_setsize(vp, (u_long)ip->i_size); + ip->i_flag |= IN_CHANGE | IN_UPDATE; + VOP_BWRITE(bp); + } + } + bb = VFSTOUFS(vp->v_mount)->um_seqinc; if (daddr == UNASSIGNED) /* May need to allocate indirect blocks */ for (i = 1; i < num; ++i) if (!indirs[i].in_exists) { - ibp = - getblk(vp, indirs[i].in_lbn, fs->lfs_bsize, - 0, 0); - if (!(ibp->b_flags & (B_DONE | B_DELWRI))) { - if (!ISSPACE(fs, bb, curproc->p_ucred)){ - ibp->b_flags |= B_INVAL; - brelse(ibp); - error = ENOSPC; - } else { - ip->i_blocks += bb; - ip->i_lfs->lfs_bfree -= bb; - clrbuf(ibp); - error = VOP_BWRITE(ibp); - } - } else + ibp = getblk(vp, indirs[i].in_lbn, fs->lfs_bsize, + 0, 0); + if ((ibp->b_flags & (B_DONE | B_DELWRI))) panic ("Indirect block should not exist"); - } - if (error) { - if (bp) - brelse(bp); - return(error); - } + if (!ISSPACE(fs, bb, curproc->p_ucred)){ + ibp->b_flags |= B_INVAL; + brelse(ibp); + return(ENOSPC); + } else { + ip->i_blocks += bb; + ip->i_lfs->lfs_bfree -= bb; + clrbuf(ibp); + if(error = VOP_BWRITE(ibp)) + return(error); + } + } + + /* + * If the block we are writing is a direct block, it's the last + * block in the file, and offset + iosize is less than a full + * block, we can write one or more fragments. There are two cases: + * the block is brand new and we should allocate it the correct + * size or it already exists and contains some fragments and + * may need to extend it. + */ + if (lbn < NDADDR && lblkno(fs, ip->i_size) == lbn) { + nsize = fragroundup(fs, offset + iosize); + frags = numfrags(fs, nsize); + bb = fragstodb(fs, frags); + if (lblktosize(fs, lbn) == ip->i_size) + /* Brand new block or fragment */ + *bpp = bp = getblk(vp, lbn, nsize, 0, 0); + else { + /* Extend existing block */ + if (error = lfs_fragextend(vp, (int)blksize(fs, ip, lbn), + nsize, lbn, &bp)) + return(error); + *bpp = bp; + } + } else { + /* + * Get the existing block from the cache either because the + * block is 1) not a direct block or because it's not the last + * block in the file. + */ + frags = dbtofrags(fs, bb); + *bpp = bp = getblk(vp, lbn, blksize(fs, ip, lbn), 0, 0); + } - /* Now, we may need to allocate the data block */ + /* + * The block we are writing may be a brand new block + * in which case we need to do accounting (i.e. check + * for free space and update the inode number of blocks. + */ if (!(bp->b_flags & (B_CACHE | B_DONE | B_DELWRI))) { if (daddr == UNASSIGNED) if (!ISSPACE(fs, bb, curproc->p_ucred)) { @@ -130,13 +179,57 @@ lfs_balloc(vp, iosize, lbn, bpp) clrbuf(bp); } else if (iosize == fs->lfs_bsize) - bp->b_blkno = daddr; /* Skip the I/O */ + /* Optimization: I/O is unnecessary. */ + bp->b_blkno = daddr; else { + /* + * We need to read the block to preserve the + * existing bytes. + */ bp->b_blkno = daddr; bp->b_flags |= B_READ; VOP_STRATEGY(bp); return(biowait(bp)); } } - return (error); + return (0); +} + +lfs_fragextend(vp, osize, nsize, lbn, bpp) + struct vnode *vp; + int osize; + int nsize; + daddr_t lbn; + struct buf **bpp; +{ + struct inode *ip; + struct lfs *fs; + long bb; + int error; + + ip = VTOI(vp); + fs = ip->i_lfs; + bb = (long)fragstodb(fs, numfrags(fs, nsize - osize)); + if (!ISSPACE(fs, bb, curproc->p_ucred)) { + return(ENOSPC); + } + + error = bread(vp, lbn, osize, NOCRED, bpp); + if (error != 0) { + brelse(*bpp); + return(error); + } +#ifdef QUOTA + error = chkdq(ip, bb, curproc->p_ucred, 0); + if (error != 0) { + brelse(*bpp); + return (error); + } +#endif + ip->i_blocks += bb; + ip->i_flag |= IN_CHANGE | IN_UPDATE; + fs->lfs_bfree -= fragstodb(fs, numfrags(fs, (nsize - osize))); + allocbuf(*bpp, nsize); + bzero((char *)((*bpp)->b_data) + osize, (u_int)(nsize - osize)); + return(0); } |