xref: /openbsd/sys/ufs/ffs/ffs_balloc.c (revision 76018e64)
1*76018e64Sart /*	$OpenBSD: ffs_balloc.c,v 1.9 1999/05/27 20:36:21 art 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
6607feb63cScsapuntz ffs_balloc(v)
6707feb63cScsapuntz 	void *v;
6807feb63cScsapuntz {
6907feb63cScsapuntz 	struct vop_balloc_args /* {
7007feb63cScsapuntz 		struct vnode *a_vp;
7107feb63cScsapuntz 		off_t a_startpoint;
7207feb63cScsapuntz 		int a_size;
7307feb63cScsapuntz 		struct ucred *a_cred;
7407feb63cScsapuntz 		int a_flags;
7507feb63cScsapuntz 		struct buf *a_bpp;
7607feb63cScsapuntz         } */ *ap = v;
7707feb63cScsapuntz 
7807feb63cScsapuntz 	struct inode *ip;
7907feb63cScsapuntz 	daddr_t lbn;
80df930be7Sderaadt 	int size;
81df930be7Sderaadt 	struct ucred *cred;
82df930be7Sderaadt 	int flags;
8307feb63cScsapuntz 	struct fs *fs;
8407feb63cScsapuntz 	daddr_t nb;
85df930be7Sderaadt 	struct buf *bp, *nbp;
8607feb63cScsapuntz 	struct vnode *vp;
87df930be7Sderaadt 	struct indir indirs[NIADDR + 2];
8807feb63cScsapuntz 	daddr_t newb, *bap, pref;
8907feb63cScsapuntz 	int deallocated, osize, nsize, num, i, error;
9007feb63cScsapuntz 	daddr_t *allocib, *blkp, *allocblk, allociblk[NIADDR+1];
91df930be7Sderaadt 
9207feb63cScsapuntz 	vp = ap->a_vp;
9307feb63cScsapuntz 	ip = VTOI(vp);
94f6d35f95Sderaadt 	fs = ip->i_fs;
9507feb63cScsapuntz 	lbn = lblkno(fs, ap->a_startoffset);
9607feb63cScsapuntz 	size = blkoff(fs, ap->a_startoffset) + ap->a_size;
9707feb63cScsapuntz 	if (size > fs->fs_bsize)
9807feb63cScsapuntz 		panic("ffs_balloc; blk too big");
9907feb63cScsapuntz 	*ap->a_bpp = NULL;
10007feb63cScsapuntz 	if (lbn < 0)
10107feb63cScsapuntz 		return (EFBIG);
10207feb63cScsapuntz 	cred = ap->a_cred;
10307feb63cScsapuntz 	flags = ap->a_flags;
104df930be7Sderaadt 
105df930be7Sderaadt 	/*
106df930be7Sderaadt 	 * If the next write will extend the file into a new block,
107df930be7Sderaadt 	 * and the file is currently composed of a fragment
108df930be7Sderaadt 	 * this fragment has to be extended to be a full block.
109df930be7Sderaadt 	 */
1107dc61945Sdownsj 	nb = lblkno(fs, ip->i_ffs_size);
11107feb63cScsapuntz 	if (nb < NDADDR && nb < lbn) {
112df930be7Sderaadt 		osize = blksize(fs, ip, nb);
113df930be7Sderaadt 		if (osize < fs->fs_bsize && osize > 0) {
114df930be7Sderaadt 			error = ffs_realloccg(ip, nb,
1157dc61945Sdownsj 				ffs_blkpref(ip, nb, (int)nb, &ip->i_ffs_db[0]),
116df930be7Sderaadt 				osize, (int)fs->fs_bsize, cred, &bp);
117df930be7Sderaadt 			if (error)
118df930be7Sderaadt 				return (error);
11907feb63cScsapuntz 			if (DOINGSOFTDEP(vp))
12007feb63cScsapuntz 				softdep_setup_allocdirect(ip, nb,
12107feb63cScsapuntz 				    dbtofsb(fs, bp->b_blkno), ip->i_ffs_db[nb],
12207feb63cScsapuntz 				    fs->fs_bsize, osize, bp);
12307feb63cScsapuntz 
124*76018e64Sart 			ip->i_ffs_size = lblktosize(fs, nb + 1);
1256348b7ebSart #if defined(UVM)
1266348b7ebSart 			uvm_vnp_setsize(vp, ip->i_ffs_size);
1276348b7ebSart #else
1287dc61945Sdownsj 			vnode_pager_setsize(vp, (u_long)ip->i_ffs_size);
1296348b7ebSart #endif
1307dc61945Sdownsj 			ip->i_ffs_db[nb] = dbtofsb(fs, bp->b_blkno);
131df930be7Sderaadt 			ip->i_flag |= IN_CHANGE | IN_UPDATE;
132df930be7Sderaadt 			if (flags & B_SYNC)
133df930be7Sderaadt 				bwrite(bp);
134df930be7Sderaadt 			else
135df930be7Sderaadt 				bawrite(bp);
136df930be7Sderaadt 		}
137df930be7Sderaadt 	}
138df930be7Sderaadt 	/*
139df930be7Sderaadt 	 * The first NDADDR blocks are direct blocks
140df930be7Sderaadt 	 */
14107feb63cScsapuntz 	if (lbn < NDADDR) {
14207feb63cScsapuntz 		nb = ip->i_ffs_db[lbn];
143*76018e64Sart 		if (nb != 0 && ip->i_ffs_size >= lblktosize(fs, lbn + 1)) {
14407feb63cScsapuntz 			error = bread(vp, lbn, fs->fs_bsize, NOCRED, &bp);
145df930be7Sderaadt 			if (error) {
146df930be7Sderaadt 				brelse(bp);
147df930be7Sderaadt 				return (error);
148df930be7Sderaadt 			}
14907feb63cScsapuntz 			*ap->a_bpp = bp;
150df930be7Sderaadt 			return (0);
151df930be7Sderaadt 		}
152df930be7Sderaadt 		if (nb != 0) {
153df930be7Sderaadt 			/*
154df930be7Sderaadt 			 * Consider need to reallocate a fragment.
155df930be7Sderaadt 			 */
1567dc61945Sdownsj 			osize = fragroundup(fs, blkoff(fs, ip->i_ffs_size));
157df930be7Sderaadt 			nsize = fragroundup(fs, size);
158df930be7Sderaadt 			if (nsize <= osize) {
15907feb63cScsapuntz 				error = bread(vp, lbn, osize, NOCRED, &bp);
160df930be7Sderaadt 				if (error) {
161df930be7Sderaadt 					brelse(bp);
162df930be7Sderaadt 					return (error);
163df930be7Sderaadt 				}
164df930be7Sderaadt 			} else {
16507feb63cScsapuntz 				error = ffs_realloccg(ip, lbn,
16607feb63cScsapuntz 				    ffs_blkpref(ip, lbn, (int)lbn,
16707feb63cScsapuntz 					&ip->i_ffs_db[0]),
168df930be7Sderaadt 				    osize, nsize, cred, &bp);
169df930be7Sderaadt 				if (error)
170df930be7Sderaadt 					return (error);
17107feb63cScsapuntz 				if (DOINGSOFTDEP(vp))
17207feb63cScsapuntz 					softdep_setup_allocdirect(ip, lbn,
17307feb63cScsapuntz 					    dbtofsb(fs, bp->b_blkno), nb,
17407feb63cScsapuntz                                             nsize, osize, bp);
175df930be7Sderaadt 			}
176df930be7Sderaadt 		} else {
177*76018e64Sart 			if (ip->i_ffs_size < lblktosize(fs, lbn + 1))
178df930be7Sderaadt 				nsize = fragroundup(fs, size);
179df930be7Sderaadt 			else
180df930be7Sderaadt 				nsize = fs->fs_bsize;
18107feb63cScsapuntz 			error = ffs_alloc(ip, lbn,
18207feb63cScsapuntz 			    ffs_blkpref(ip, lbn, (int)lbn, &ip->i_ffs_db[0]),
183df930be7Sderaadt 			    nsize, cred, &newb);
184df930be7Sderaadt 			if (error)
185df930be7Sderaadt 				return (error);
18607feb63cScsapuntz 			bp = getblk(vp, lbn, nsize, 0, 0);
187df930be7Sderaadt 			bp->b_blkno = fsbtodb(fs, newb);
188df930be7Sderaadt 			if (flags & B_CLRBUF)
189df930be7Sderaadt 				clrbuf(bp);
19007feb63cScsapuntz 			if (DOINGSOFTDEP(vp))
19107feb63cScsapuntz 				softdep_setup_allocdirect(ip, lbn, newb, 0,
19207feb63cScsapuntz 				    nsize, 0, bp);
19307feb63cScsapuntz 
194df930be7Sderaadt 		}
19507feb63cScsapuntz 		ip->i_ffs_db[lbn] = dbtofsb(fs, bp->b_blkno);
196df930be7Sderaadt 		ip->i_flag |= IN_CHANGE | IN_UPDATE;
19707feb63cScsapuntz 		*ap->a_bpp = bp;
198df930be7Sderaadt 		return (0);
199df930be7Sderaadt 	}
200df930be7Sderaadt 	/*
201df930be7Sderaadt 	 * Determine the number of levels of indirection.
202df930be7Sderaadt 	 */
203df930be7Sderaadt 	pref = 0;
20407feb63cScsapuntz 	if ((error = ufs_getlbns(vp, lbn, indirs, &num)) != 0)
205df930be7Sderaadt 		return(error);
206df930be7Sderaadt #ifdef DIAGNOSTIC
207df930be7Sderaadt 	if (num < 1)
20830ada397Smillert 		panic ("ffs_balloc: ufs_bmaparray returned indirect block");
209df930be7Sderaadt #endif
210df930be7Sderaadt 	/*
211df930be7Sderaadt 	 * Fetch the first indirect block allocating if necessary.
212df930be7Sderaadt 	 */
213df930be7Sderaadt 	--num;
2147dc61945Sdownsj 	nb = ip->i_ffs_ib[indirs[0].in_off];
21507feb63cScsapuntz 
21607feb63cScsapuntz 	allocib = NULL;
21707feb63cScsapuntz 	allocblk = allociblk;
218df930be7Sderaadt 	if (nb == 0) {
219df930be7Sderaadt 		pref = ffs_blkpref(ip, lbn, 0, (daddr_t *)0);
220d28910b8Sniklas 	        error = ffs_alloc(ip, lbn, pref, (int)fs->fs_bsize,
221d28910b8Sniklas 				  cred, &newb);
222d28910b8Sniklas 		if (error)
223df930be7Sderaadt 			return (error);
224df930be7Sderaadt 		nb = newb;
22507feb63cScsapuntz 
22607feb63cScsapuntz 		*allocblk++ = nb;
227df930be7Sderaadt 		bp = getblk(vp, indirs[1].in_lbn, fs->fs_bsize, 0, 0);
22807feb63cScsapuntz 		bp->b_blkno = fsbtodb(fs, nb);
229df930be7Sderaadt 		clrbuf(bp);
23007feb63cScsapuntz 
23107feb63cScsapuntz                 if (DOINGSOFTDEP(vp)) {
23207feb63cScsapuntz                         softdep_setup_allocdirect(ip, NDADDR + indirs[0].in_off,
23307feb63cScsapuntz                             newb, 0, fs->fs_bsize, 0, bp);
23407feb63cScsapuntz                         bdwrite(bp);
23507feb63cScsapuntz                 } else {
236df930be7Sderaadt                         /*
237df930be7Sderaadt                          * Write synchronously so that indirect blocks
238df930be7Sderaadt                          * never point at garbage.
239df930be7Sderaadt                          */
24007feb63cScsapuntz                         if ((error = bwrite(bp)) != 0)
24107feb63cScsapuntz                                 goto fail;
242df930be7Sderaadt                 }
24307feb63cScsapuntz 		allocib = &ip->i_ffs_ib[indirs[0].in_off];
24407feb63cScsapuntz 		*allocib = nb;
245df930be7Sderaadt 		ip->i_flag |= IN_CHANGE | IN_UPDATE;
246df930be7Sderaadt 	}
247df930be7Sderaadt 	/*
248df930be7Sderaadt 	 * Fetch through the indirect blocks, allocating as necessary.
249df930be7Sderaadt 	 */
250df930be7Sderaadt 	for (i = 1;;) {
251df930be7Sderaadt 		error = bread(vp,
252df930be7Sderaadt 		    indirs[i].in_lbn, (int)fs->fs_bsize, NOCRED, &bp);
253df930be7Sderaadt 		if (error) {
254df930be7Sderaadt 			brelse(bp);
25507feb63cScsapuntz 			goto fail;
256df930be7Sderaadt 		}
257df930be7Sderaadt 		bap = (daddr_t *)bp->b_data;
258df930be7Sderaadt 		nb = bap[indirs[i].in_off];
259df930be7Sderaadt 		if (i == num)
260df930be7Sderaadt 			break;
261df930be7Sderaadt 		i += 1;
262df930be7Sderaadt 		if (nb != 0) {
263df930be7Sderaadt 			brelse(bp);
264df930be7Sderaadt 			continue;
265df930be7Sderaadt 		}
266df930be7Sderaadt 		if (pref == 0)
267df930be7Sderaadt 			pref = ffs_blkpref(ip, lbn, 0, (daddr_t *)0);
268d28910b8Sniklas 		error = ffs_alloc(ip, lbn, pref, (int)fs->fs_bsize, cred,
269d28910b8Sniklas 				  &newb);
270d28910b8Sniklas 		if (error) {
271df930be7Sderaadt 			brelse(bp);
27207feb63cScsapuntz 			goto fail;
273df930be7Sderaadt 		}
274df930be7Sderaadt 		nb = newb;
27507feb63cScsapuntz 		*allocblk++ = nb;
276df930be7Sderaadt 		nbp = getblk(vp, indirs[i].in_lbn, fs->fs_bsize, 0, 0);
277df930be7Sderaadt 		nbp->b_blkno = fsbtodb(fs, nb);
278df930be7Sderaadt 		clrbuf(nbp);
27907feb63cScsapuntz 
28007feb63cScsapuntz                 if (DOINGSOFTDEP(vp)) {
28107feb63cScsapuntz                         softdep_setup_allocindir_meta(nbp, ip, bp,
28207feb63cScsapuntz                             indirs[i - 1].in_off, nb);
28307feb63cScsapuntz                         bdwrite(nbp);
28407feb63cScsapuntz                 } else {
285df930be7Sderaadt                         /*
286df930be7Sderaadt                          * Write synchronously so that indirect blocks
287df930be7Sderaadt                          * never point at garbage.
288df930be7Sderaadt                          */
289d28910b8Sniklas                         if ((error = bwrite(nbp)) != 0) {
290df930be7Sderaadt                                 brelse(bp);
29107feb63cScsapuntz                                 goto fail;
29207feb63cScsapuntz                         }
293df930be7Sderaadt 		}
294df930be7Sderaadt 		bap[indirs[i - 1].in_off] = nb;
295df930be7Sderaadt 		/*
296df930be7Sderaadt 		 * If required, write synchronously, otherwise use
297df930be7Sderaadt 		 * delayed write.
298df930be7Sderaadt 		 */
299df930be7Sderaadt 		if (flags & B_SYNC) {
300df930be7Sderaadt 			bwrite(bp);
301df930be7Sderaadt 		} else {
302df930be7Sderaadt 			bdwrite(bp);
303df930be7Sderaadt 		}
304df930be7Sderaadt 	}
305df930be7Sderaadt 	/*
306df930be7Sderaadt 	 * Get the data block, allocating if necessary.
307df930be7Sderaadt 	 */
308df930be7Sderaadt 	if (nb == 0) {
309df930be7Sderaadt 		pref = ffs_blkpref(ip, lbn, indirs[i].in_off, &bap[0]);
310d28910b8Sniklas 		error = ffs_alloc(ip, lbn, pref, (int)fs->fs_bsize, cred,
311d28910b8Sniklas 				  &newb);
312d28910b8Sniklas 		if (error) {
313df930be7Sderaadt 			brelse(bp);
31407feb63cScsapuntz 			goto fail;
315df930be7Sderaadt 		}
316df930be7Sderaadt 		nb = newb;
31707feb63cScsapuntz 		*allocblk++ = nb;
318df930be7Sderaadt 		nbp = getblk(vp, lbn, fs->fs_bsize, 0, 0);
319df930be7Sderaadt 		nbp->b_blkno = fsbtodb(fs, nb);
320df930be7Sderaadt 		if (flags & B_CLRBUF)
321df930be7Sderaadt 			clrbuf(nbp);
32207feb63cScsapuntz 		if (DOINGSOFTDEP(vp))
32307feb63cScsapuntz 			softdep_setup_allocindir_page(ip, lbn, bp,
32407feb63cScsapuntz 			    indirs[i].in_off, nb, 0, nbp);
325df930be7Sderaadt 		bap[indirs[i].in_off] = nb;
326df930be7Sderaadt 		/*
327df930be7Sderaadt 		 * If required, write synchronously, otherwise use
328df930be7Sderaadt 		 * delayed write.
329df930be7Sderaadt 		 */
330df930be7Sderaadt 		if (flags & B_SYNC) {
331df930be7Sderaadt 			bwrite(bp);
332df930be7Sderaadt 		} else {
333df930be7Sderaadt 			bdwrite(bp);
334df930be7Sderaadt 		}
33507feb63cScsapuntz 		*ap->a_bpp = nbp;
336df930be7Sderaadt 		return (0);
337df930be7Sderaadt 	}
338df930be7Sderaadt 	brelse(bp);
339df930be7Sderaadt 	if (flags & B_CLRBUF) {
340df930be7Sderaadt 		error = bread(vp, lbn, (int)fs->fs_bsize, NOCRED, &nbp);
341df930be7Sderaadt 		if (error) {
342df930be7Sderaadt 			brelse(nbp);
34307feb63cScsapuntz 			goto fail;
344df930be7Sderaadt 		}
345df930be7Sderaadt 	} else {
346df930be7Sderaadt 		nbp = getblk(vp, lbn, fs->fs_bsize, 0, 0);
347df930be7Sderaadt 		nbp->b_blkno = fsbtodb(fs, nb);
348df930be7Sderaadt 	}
34907feb63cScsapuntz 	*ap->a_bpp = nbp;
350df930be7Sderaadt 	return (0);
35107feb63cScsapuntz 
35207feb63cScsapuntz fail:
35307feb63cScsapuntz 	/*
35407feb63cScsapuntz 	 * If we have failed part way through block allocation, we
35507feb63cScsapuntz 	 * have to deallocate any indirect blocks that we have allocated.
35607feb63cScsapuntz 	 */
35707feb63cScsapuntz 	for (deallocated = 0, blkp = allociblk; blkp < allocblk; blkp++) {
35807feb63cScsapuntz 		ffs_blkfree(ip, *blkp, fs->fs_bsize);
35907feb63cScsapuntz 		deallocated += fs->fs_bsize;
36007feb63cScsapuntz 	}
36107feb63cScsapuntz 	if (allocib != NULL)
36207feb63cScsapuntz 		*allocib = 0;
36307feb63cScsapuntz 	if (deallocated) {
36407feb63cScsapuntz #ifdef QUOTA
36507feb63cScsapuntz 		/*
36607feb63cScsapuntz 		 * Restore user's disk quota because allocation failed.
36707feb63cScsapuntz 		 */
36807feb63cScsapuntz 		(void) chkdq(ip, (long)-btodb(deallocated), cred, FORCE);
36907feb63cScsapuntz #endif
37007feb63cScsapuntz 		ip->i_ffs_blocks -= btodb(deallocated);
37107feb63cScsapuntz 		ip->i_flag |= IN_CHANGE | IN_UPDATE;
37207feb63cScsapuntz 	}
37307feb63cScsapuntz 	return (error);
37407feb63cScsapuntz 
375df930be7Sderaadt }
376