1*b080ad39Scsapuntz /* $OpenBSD: ffs_balloc.c,v 1.13 2001/06/23 02:07:53 csapuntz Exp $ */ 2d28910b8Sniklas /* $NetBSD: ffs_balloc.c,v 1.3 1996/02/09 22:22:21 christos Exp $ */ 3df930be7Sderaadt 4df930be7Sderaadt /* 5df930be7Sderaadt * Copyright (c) 1982, 1986, 1989, 1993 6df930be7Sderaadt * The Regents of the University of California. All rights reserved. 7df930be7Sderaadt * 8df930be7Sderaadt * Redistribution and use in source and binary forms, with or without 9df930be7Sderaadt * modification, are permitted provided that the following conditions 10df930be7Sderaadt * are met: 11df930be7Sderaadt * 1. Redistributions of source code must retain the above copyright 12df930be7Sderaadt * notice, this list of conditions and the following disclaimer. 13df930be7Sderaadt * 2. Redistributions in binary form must reproduce the above copyright 14df930be7Sderaadt * notice, this list of conditions and the following disclaimer in the 15df930be7Sderaadt * documentation and/or other materials provided with the distribution. 16df930be7Sderaadt * 3. All advertising materials mentioning features or use of this software 17df930be7Sderaadt * must display the following acknowledgement: 18df930be7Sderaadt * This product includes software developed by the University of 19df930be7Sderaadt * California, Berkeley and its contributors. 20df930be7Sderaadt * 4. Neither the name of the University nor the names of its contributors 21df930be7Sderaadt * may be used to endorse or promote products derived from this software 22df930be7Sderaadt * without specific prior written permission. 23df930be7Sderaadt * 24df930be7Sderaadt * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25df930be7Sderaadt * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26df930be7Sderaadt * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27df930be7Sderaadt * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28df930be7Sderaadt * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29df930be7Sderaadt * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30df930be7Sderaadt * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31df930be7Sderaadt * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32df930be7Sderaadt * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33df930be7Sderaadt * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34df930be7Sderaadt * SUCH DAMAGE. 35df930be7Sderaadt * 36df930be7Sderaadt * @(#)ffs_balloc.c 8.4 (Berkeley) 9/23/93 37df930be7Sderaadt */ 38df930be7Sderaadt 39df930be7Sderaadt #include <sys/param.h> 40df930be7Sderaadt #include <sys/systm.h> 41df930be7Sderaadt #include <sys/buf.h> 42df930be7Sderaadt #include <sys/proc.h> 43df930be7Sderaadt #include <sys/file.h> 4407feb63cScsapuntz #include <sys/mount.h> 45df930be7Sderaadt #include <sys/vnode.h> 46df930be7Sderaadt 47df930be7Sderaadt #include <vm/vm.h> 48df930be7Sderaadt 496348b7ebSart #if defined(UVM) 506348b7ebSart #include <uvm/uvm_extern.h> 516348b7ebSart #endif 526348b7ebSart 53df930be7Sderaadt #include <ufs/ufs/quota.h> 54df930be7Sderaadt #include <ufs/ufs/inode.h> 55df930be7Sderaadt #include <ufs/ufs/ufs_extern.h> 56df930be7Sderaadt 57df930be7Sderaadt #include <ufs/ffs/fs.h> 58df930be7Sderaadt #include <ufs/ffs/ffs_extern.h> 59df930be7Sderaadt 60df930be7Sderaadt /* 61df930be7Sderaadt * Balloc defines the structure of file system storage 62df930be7Sderaadt * by allocating the physical blocks on a device given 63df930be7Sderaadt * the inode and the logical block number in a file. 64df930be7Sderaadt */ 65d28910b8Sniklas int 66*b080ad39Scsapuntz ffs_balloc(struct inode *ip, off_t startoffset, int size, struct ucred *cred, 67*b080ad39Scsapuntz int flags, struct buf **bpp) 6807feb63cScsapuntz { 6907feb63cScsapuntz daddr_t lbn; 7007feb63cScsapuntz struct fs *fs; 7107feb63cScsapuntz daddr_t nb; 72df930be7Sderaadt struct buf *bp, *nbp; 7307feb63cScsapuntz struct vnode *vp; 74df930be7Sderaadt struct indir indirs[NIADDR + 2]; 7507feb63cScsapuntz daddr_t newb, *bap, pref; 7607feb63cScsapuntz int deallocated, osize, nsize, num, i, error; 7707feb63cScsapuntz daddr_t *allocib, *blkp, *allocblk, allociblk[NIADDR+1]; 786cd4677cSart int unwindidx = -1; 79df930be7Sderaadt 80*b080ad39Scsapuntz vp = ITOV(ip); 81f6d35f95Sderaadt fs = ip->i_fs; 82*b080ad39Scsapuntz lbn = lblkno(fs, startoffset); 83*b080ad39Scsapuntz size = blkoff(fs, startoffset) + size; 8407feb63cScsapuntz if (size > fs->fs_bsize) 85405e338eSart panic("ffs_balloc: blk too big"); 86*b080ad39Scsapuntz *bpp = NULL; 8707feb63cScsapuntz if (lbn < 0) 8807feb63cScsapuntz return (EFBIG); 89df930be7Sderaadt 90df930be7Sderaadt /* 91df930be7Sderaadt * If the next write will extend the file into a new block, 92df930be7Sderaadt * and the file is currently composed of a fragment 93df930be7Sderaadt * this fragment has to be extended to be a full block. 94df930be7Sderaadt */ 957dc61945Sdownsj nb = lblkno(fs, ip->i_ffs_size); 9607feb63cScsapuntz if (nb < NDADDR && nb < lbn) { 97df930be7Sderaadt osize = blksize(fs, ip, nb); 98df930be7Sderaadt if (osize < fs->fs_bsize && osize > 0) { 99df930be7Sderaadt error = ffs_realloccg(ip, nb, 1007dc61945Sdownsj ffs_blkpref(ip, nb, (int)nb, &ip->i_ffs_db[0]), 101df930be7Sderaadt osize, (int)fs->fs_bsize, cred, &bp); 102df930be7Sderaadt if (error) 103df930be7Sderaadt return (error); 10407feb63cScsapuntz if (DOINGSOFTDEP(vp)) 10507feb63cScsapuntz softdep_setup_allocdirect(ip, nb, 10607feb63cScsapuntz dbtofsb(fs, bp->b_blkno), ip->i_ffs_db[nb], 10707feb63cScsapuntz fs->fs_bsize, osize, bp); 10807feb63cScsapuntz 10976018e64Sart ip->i_ffs_size = lblktosize(fs, nb + 1); 1106348b7ebSart #if defined(UVM) 1116348b7ebSart uvm_vnp_setsize(vp, ip->i_ffs_size); 1126348b7ebSart #else 1137dc61945Sdownsj vnode_pager_setsize(vp, (u_long)ip->i_ffs_size); 1146348b7ebSart #endif 1157dc61945Sdownsj ip->i_ffs_db[nb] = dbtofsb(fs, bp->b_blkno); 116df930be7Sderaadt ip->i_flag |= IN_CHANGE | IN_UPDATE; 117df930be7Sderaadt if (flags & B_SYNC) 118df930be7Sderaadt bwrite(bp); 119df930be7Sderaadt else 120df930be7Sderaadt bawrite(bp); 121df930be7Sderaadt } 122df930be7Sderaadt } 123df930be7Sderaadt /* 124df930be7Sderaadt * The first NDADDR blocks are direct blocks 125df930be7Sderaadt */ 12607feb63cScsapuntz if (lbn < NDADDR) { 12707feb63cScsapuntz nb = ip->i_ffs_db[lbn]; 12876018e64Sart if (nb != 0 && ip->i_ffs_size >= lblktosize(fs, lbn + 1)) { 12907feb63cScsapuntz error = bread(vp, lbn, fs->fs_bsize, NOCRED, &bp); 130df930be7Sderaadt if (error) { 131df930be7Sderaadt brelse(bp); 132df930be7Sderaadt return (error); 133df930be7Sderaadt } 134*b080ad39Scsapuntz *bpp = bp; 135df930be7Sderaadt return (0); 136df930be7Sderaadt } 137df930be7Sderaadt if (nb != 0) { 138df930be7Sderaadt /* 139df930be7Sderaadt * Consider need to reallocate a fragment. 140df930be7Sderaadt */ 1417dc61945Sdownsj osize = fragroundup(fs, blkoff(fs, ip->i_ffs_size)); 142df930be7Sderaadt nsize = fragroundup(fs, size); 143df930be7Sderaadt if (nsize <= osize) { 14407feb63cScsapuntz error = bread(vp, lbn, osize, NOCRED, &bp); 145df930be7Sderaadt if (error) { 146df930be7Sderaadt brelse(bp); 147df930be7Sderaadt return (error); 148df930be7Sderaadt } 149df930be7Sderaadt } else { 15007feb63cScsapuntz error = ffs_realloccg(ip, lbn, 15107feb63cScsapuntz ffs_blkpref(ip, lbn, (int)lbn, 15207feb63cScsapuntz &ip->i_ffs_db[0]), 153df930be7Sderaadt osize, nsize, cred, &bp); 154df930be7Sderaadt if (error) 155df930be7Sderaadt return (error); 15607feb63cScsapuntz if (DOINGSOFTDEP(vp)) 15707feb63cScsapuntz softdep_setup_allocdirect(ip, lbn, 15807feb63cScsapuntz dbtofsb(fs, bp->b_blkno), nb, 15907feb63cScsapuntz nsize, osize, bp); 160df930be7Sderaadt } 161df930be7Sderaadt } else { 16276018e64Sart if (ip->i_ffs_size < lblktosize(fs, lbn + 1)) 163df930be7Sderaadt nsize = fragroundup(fs, size); 164df930be7Sderaadt else 165df930be7Sderaadt nsize = fs->fs_bsize; 16607feb63cScsapuntz error = ffs_alloc(ip, lbn, 16707feb63cScsapuntz ffs_blkpref(ip, lbn, (int)lbn, &ip->i_ffs_db[0]), 168df930be7Sderaadt nsize, cred, &newb); 169df930be7Sderaadt if (error) 170df930be7Sderaadt return (error); 17107feb63cScsapuntz bp = getblk(vp, lbn, nsize, 0, 0); 172df930be7Sderaadt bp->b_blkno = fsbtodb(fs, newb); 173df930be7Sderaadt if (flags & B_CLRBUF) 174df930be7Sderaadt clrbuf(bp); 17507feb63cScsapuntz if (DOINGSOFTDEP(vp)) 17607feb63cScsapuntz softdep_setup_allocdirect(ip, lbn, newb, 0, 17707feb63cScsapuntz nsize, 0, bp); 178df930be7Sderaadt } 17907feb63cScsapuntz ip->i_ffs_db[lbn] = dbtofsb(fs, bp->b_blkno); 180df930be7Sderaadt ip->i_flag |= IN_CHANGE | IN_UPDATE; 181*b080ad39Scsapuntz *bpp = bp; 182df930be7Sderaadt return (0); 183df930be7Sderaadt } 184df930be7Sderaadt /* 185df930be7Sderaadt * Determine the number of levels of indirection. 186df930be7Sderaadt */ 187df930be7Sderaadt pref = 0; 18807feb63cScsapuntz if ((error = ufs_getlbns(vp, lbn, indirs, &num)) != 0) 189df930be7Sderaadt return(error); 190df930be7Sderaadt #ifdef DIAGNOSTIC 191df930be7Sderaadt if (num < 1) 19230ada397Smillert panic ("ffs_balloc: ufs_bmaparray returned indirect block"); 193df930be7Sderaadt #endif 194df930be7Sderaadt /* 195df930be7Sderaadt * Fetch the first indirect block allocating if necessary. 196df930be7Sderaadt */ 197df930be7Sderaadt --num; 1987dc61945Sdownsj nb = ip->i_ffs_ib[indirs[0].in_off]; 19907feb63cScsapuntz 20007feb63cScsapuntz allocib = NULL; 20107feb63cScsapuntz allocblk = allociblk; 202df930be7Sderaadt if (nb == 0) { 203df930be7Sderaadt pref = ffs_blkpref(ip, lbn, 0, (daddr_t *)0); 204d28910b8Sniklas error = ffs_alloc(ip, lbn, pref, (int)fs->fs_bsize, 205d28910b8Sniklas cred, &newb); 206d28910b8Sniklas if (error) 207df930be7Sderaadt return (error); 208df930be7Sderaadt nb = newb; 20907feb63cScsapuntz 21007feb63cScsapuntz *allocblk++ = nb; 211df930be7Sderaadt bp = getblk(vp, indirs[1].in_lbn, fs->fs_bsize, 0, 0); 21207feb63cScsapuntz bp->b_blkno = fsbtodb(fs, nb); 213df930be7Sderaadt clrbuf(bp); 21407feb63cScsapuntz 21507feb63cScsapuntz if (DOINGSOFTDEP(vp)) { 21607feb63cScsapuntz softdep_setup_allocdirect(ip, NDADDR + indirs[0].in_off, 21707feb63cScsapuntz newb, 0, fs->fs_bsize, 0, bp); 21807feb63cScsapuntz bdwrite(bp); 21907feb63cScsapuntz } else { 220df930be7Sderaadt /* 221df930be7Sderaadt * Write synchronously so that indirect blocks 222df930be7Sderaadt * never point at garbage. 223df930be7Sderaadt */ 22407feb63cScsapuntz if ((error = bwrite(bp)) != 0) 22507feb63cScsapuntz goto fail; 226df930be7Sderaadt } 22707feb63cScsapuntz allocib = &ip->i_ffs_ib[indirs[0].in_off]; 22807feb63cScsapuntz *allocib = nb; 229df930be7Sderaadt ip->i_flag |= IN_CHANGE | IN_UPDATE; 230df930be7Sderaadt } 231df930be7Sderaadt /* 232df930be7Sderaadt * Fetch through the indirect blocks, allocating as necessary. 233df930be7Sderaadt */ 234df930be7Sderaadt for (i = 1;;) { 235df930be7Sderaadt error = bread(vp, 236df930be7Sderaadt indirs[i].in_lbn, (int)fs->fs_bsize, NOCRED, &bp); 237df930be7Sderaadt if (error) { 238df930be7Sderaadt brelse(bp); 23907feb63cScsapuntz goto fail; 240df930be7Sderaadt } 241df930be7Sderaadt bap = (daddr_t *)bp->b_data; 242df930be7Sderaadt nb = bap[indirs[i].in_off]; 243df930be7Sderaadt if (i == num) 244df930be7Sderaadt break; 2453853cac8Sart i++; 246df930be7Sderaadt if (nb != 0) { 247df930be7Sderaadt brelse(bp); 248df930be7Sderaadt continue; 249df930be7Sderaadt } 250df930be7Sderaadt if (pref == 0) 251df930be7Sderaadt pref = ffs_blkpref(ip, lbn, 0, (daddr_t *)0); 252d28910b8Sniklas error = ffs_alloc(ip, lbn, pref, (int)fs->fs_bsize, cred, 253d28910b8Sniklas &newb); 254d28910b8Sniklas if (error) { 255df930be7Sderaadt brelse(bp); 25607feb63cScsapuntz goto fail; 257df930be7Sderaadt } 258df930be7Sderaadt nb = newb; 25907feb63cScsapuntz *allocblk++ = nb; 260df930be7Sderaadt nbp = getblk(vp, indirs[i].in_lbn, fs->fs_bsize, 0, 0); 261df930be7Sderaadt nbp->b_blkno = fsbtodb(fs, nb); 262df930be7Sderaadt clrbuf(nbp); 26307feb63cScsapuntz 26407feb63cScsapuntz if (DOINGSOFTDEP(vp)) { 26507feb63cScsapuntz softdep_setup_allocindir_meta(nbp, ip, bp, 26607feb63cScsapuntz indirs[i - 1].in_off, nb); 26707feb63cScsapuntz bdwrite(nbp); 26807feb63cScsapuntz } else { 269df930be7Sderaadt /* 270df930be7Sderaadt * Write synchronously so that indirect blocks 271df930be7Sderaadt * never point at garbage. 272df930be7Sderaadt */ 273d28910b8Sniklas if ((error = bwrite(nbp)) != 0) { 274df930be7Sderaadt brelse(bp); 27507feb63cScsapuntz goto fail; 27607feb63cScsapuntz } 277df930be7Sderaadt } 278df930be7Sderaadt bap[indirs[i - 1].in_off] = nb; 2796cd4677cSart if (allocib == NULL && unwindidx < 0) 2806cd4677cSart unwindidx = i - 1; 281df930be7Sderaadt /* 282df930be7Sderaadt * If required, write synchronously, otherwise use 283df930be7Sderaadt * delayed write. 284df930be7Sderaadt */ 285df930be7Sderaadt if (flags & B_SYNC) { 286df930be7Sderaadt bwrite(bp); 287df930be7Sderaadt } else { 288df930be7Sderaadt bdwrite(bp); 289df930be7Sderaadt } 290df930be7Sderaadt } 291df930be7Sderaadt /* 292df930be7Sderaadt * Get the data block, allocating if necessary. 293df930be7Sderaadt */ 294df930be7Sderaadt if (nb == 0) { 295df930be7Sderaadt pref = ffs_blkpref(ip, lbn, indirs[i].in_off, &bap[0]); 296d28910b8Sniklas error = ffs_alloc(ip, lbn, pref, (int)fs->fs_bsize, cred, 297d28910b8Sniklas &newb); 298d28910b8Sniklas if (error) { 299df930be7Sderaadt brelse(bp); 30007feb63cScsapuntz goto fail; 301df930be7Sderaadt } 302df930be7Sderaadt nb = newb; 30307feb63cScsapuntz *allocblk++ = nb; 304df930be7Sderaadt nbp = getblk(vp, lbn, fs->fs_bsize, 0, 0); 305df930be7Sderaadt nbp->b_blkno = fsbtodb(fs, nb); 306df930be7Sderaadt if (flags & B_CLRBUF) 307df930be7Sderaadt clrbuf(nbp); 30807feb63cScsapuntz if (DOINGSOFTDEP(vp)) 30907feb63cScsapuntz softdep_setup_allocindir_page(ip, lbn, bp, 31007feb63cScsapuntz indirs[i].in_off, nb, 0, nbp); 311df930be7Sderaadt bap[indirs[i].in_off] = nb; 312df930be7Sderaadt /* 313df930be7Sderaadt * If required, write synchronously, otherwise use 314df930be7Sderaadt * delayed write. 315df930be7Sderaadt */ 316df930be7Sderaadt if (flags & B_SYNC) { 317df930be7Sderaadt bwrite(bp); 318df930be7Sderaadt } else { 319df930be7Sderaadt bdwrite(bp); 320df930be7Sderaadt } 321*b080ad39Scsapuntz *bpp = nbp; 322df930be7Sderaadt return (0); 323df930be7Sderaadt } 324df930be7Sderaadt brelse(bp); 325df930be7Sderaadt if (flags & B_CLRBUF) { 326df930be7Sderaadt error = bread(vp, lbn, (int)fs->fs_bsize, NOCRED, &nbp); 327df930be7Sderaadt if (error) { 328df930be7Sderaadt brelse(nbp); 32907feb63cScsapuntz goto fail; 330df930be7Sderaadt } 331df930be7Sderaadt } else { 332df930be7Sderaadt nbp = getblk(vp, lbn, fs->fs_bsize, 0, 0); 333df930be7Sderaadt nbp->b_blkno = fsbtodb(fs, nb); 334df930be7Sderaadt } 335*b080ad39Scsapuntz *bpp = nbp; 336df930be7Sderaadt return (0); 33707feb63cScsapuntz 33807feb63cScsapuntz fail: 33907feb63cScsapuntz /* 34007feb63cScsapuntz * If we have failed part way through block allocation, we 34107feb63cScsapuntz * have to deallocate any indirect blocks that we have allocated. 34207feb63cScsapuntz */ 34307feb63cScsapuntz for (deallocated = 0, blkp = allociblk; blkp < allocblk; blkp++) { 34407feb63cScsapuntz ffs_blkfree(ip, *blkp, fs->fs_bsize); 34507feb63cScsapuntz deallocated += fs->fs_bsize; 34607feb63cScsapuntz } 3476cd4677cSart if (allocib != NULL) { 34807feb63cScsapuntz *allocib = 0; 3496cd4677cSart } else if (unwindidx >= 0) { 3506cd4677cSart int r; 3516cd4677cSart 3526cd4677cSart r = bread(vp, indirs[unwindidx].in_lbn, 3536cd4677cSart (int)fs->fs_bsize, NOCRED, &bp); 3546cd4677cSart if (r) 3556cd4677cSart panic("Could not unwind indirect block, error %d", r); 3566cd4677cSart bap = (ufs_daddr_t *)bp->b_data; 3576cd4677cSart bap[indirs[unwindidx].in_off] = 0; 3586cd4677cSart if (flags & B_SYNC) { 3596cd4677cSart bwrite(bp); 3606cd4677cSart } else { 3616cd4677cSart bdwrite(bp); 3626cd4677cSart } 3636cd4677cSart } 36407feb63cScsapuntz if (deallocated) { 36507feb63cScsapuntz #ifdef QUOTA 36607feb63cScsapuntz /* 36707feb63cScsapuntz * Restore user's disk quota because allocation failed. 36807feb63cScsapuntz */ 36907feb63cScsapuntz (void)chkdq(ip, (long)-btodb(deallocated), cred, FORCE); 37007feb63cScsapuntz #endif 37107feb63cScsapuntz ip->i_ffs_blocks -= btodb(deallocated); 37207feb63cScsapuntz ip->i_flag |= IN_CHANGE | IN_UPDATE; 37307feb63cScsapuntz } 37407feb63cScsapuntz 3756cd4677cSart return (error); 376df930be7Sderaadt } 377