xref: /openbsd/sys/ufs/ffs/ffs_balloc.c (revision b080ad39)
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