xref: /netbsd-src/sys/ufs/lfs/lfs_inode.c (revision f5ad84fdb3a97e4abb46400b440f948b88ef31eb)
1*f5ad84fdSad /*	$NetBSD: lfs_inode.c,v 1.160 2020/04/23 21:47:09 ad Exp $	*/
2fccfa11aScgd 
31b8f5ea3Sperseant /*-
4b397c875Sperseant  * Copyright (c) 1999, 2000, 2001, 2002, 2003 The NetBSD Foundation, Inc.
51b8f5ea3Sperseant  * All rights reserved.
61b8f5ea3Sperseant  *
71b8f5ea3Sperseant  * This code is derived from software contributed to The NetBSD Foundation
81b8f5ea3Sperseant  * by Konrad E. Schroder <perseant@hhhh.org>.
91b8f5ea3Sperseant  *
101b8f5ea3Sperseant  * Redistribution and use in source and binary forms, with or without
111b8f5ea3Sperseant  * modification, are permitted provided that the following conditions
121b8f5ea3Sperseant  * are met:
131b8f5ea3Sperseant  * 1. Redistributions of source code must retain the above copyright
141b8f5ea3Sperseant  *    notice, this list of conditions and the following disclaimer.
151b8f5ea3Sperseant  * 2. Redistributions in binary form must reproduce the above copyright
161b8f5ea3Sperseant  *    notice, this list of conditions and the following disclaimer in the
171b8f5ea3Sperseant  *    documentation and/or other materials provided with the distribution.
181b8f5ea3Sperseant  *
191b8f5ea3Sperseant  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
201b8f5ea3Sperseant  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
211b8f5ea3Sperseant  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
221b8f5ea3Sperseant  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
231b8f5ea3Sperseant  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
241b8f5ea3Sperseant  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
251b8f5ea3Sperseant  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
261b8f5ea3Sperseant  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
271b8f5ea3Sperseant  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
281b8f5ea3Sperseant  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
291b8f5ea3Sperseant  * POSSIBILITY OF SUCH DAMAGE.
301b8f5ea3Sperseant  */
31264b874cSmycroft /*
32264b874cSmycroft  * Copyright (c) 1986, 1989, 1991, 1993
33264b874cSmycroft  *	The Regents of the University of California.  All rights reserved.
34264b874cSmycroft  *
35264b874cSmycroft  * Redistribution and use in source and binary forms, with or without
36264b874cSmycroft  * modification, are permitted provided that the following conditions
37264b874cSmycroft  * are met:
38264b874cSmycroft  * 1. Redistributions of source code must retain the above copyright
39264b874cSmycroft  *    notice, this list of conditions and the following disclaimer.
40264b874cSmycroft  * 2. Redistributions in binary form must reproduce the above copyright
41264b874cSmycroft  *    notice, this list of conditions and the following disclaimer in the
42264b874cSmycroft  *    documentation and/or other materials provided with the distribution.
43aad01611Sagc  * 3. Neither the name of the University nor the names of its contributors
44264b874cSmycroft  *    may be used to endorse or promote products derived from this software
45264b874cSmycroft  *    without specific prior written permission.
46264b874cSmycroft  *
47264b874cSmycroft  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
48264b874cSmycroft  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
49264b874cSmycroft  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
50264b874cSmycroft  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
51264b874cSmycroft  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
52264b874cSmycroft  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
53264b874cSmycroft  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
54264b874cSmycroft  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
55264b874cSmycroft  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
56264b874cSmycroft  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
57264b874cSmycroft  * SUCH DAMAGE.
58264b874cSmycroft  *
59e5bc90f4Sfvdl  *	@(#)lfs_inode.c	8.9 (Berkeley) 5/8/95
60264b874cSmycroft  */
61264b874cSmycroft 
62ec624546Slukem #include <sys/cdefs.h>
63*f5ad84fdSad __KERNEL_RCSID(0, "$NetBSD: lfs_inode.c,v 1.160 2020/04/23 21:47:09 ad Exp $");
64ec624546Slukem 
6567afbd62Smrg #if defined(_KERNEL_OPT)
66d48f258fSscottr #include "opt_quota.h"
677171cca4Sscottr #endif
68d48f258fSscottr 
69264b874cSmycroft #include <sys/param.h>
70264b874cSmycroft #include <sys/systm.h>
71264b874cSmycroft #include <sys/mount.h>
72481da54fSperseant #include <sys/malloc.h>
73264b874cSmycroft #include <sys/proc.h>
74264b874cSmycroft #include <sys/file.h>
75264b874cSmycroft #include <sys/buf.h>
76264b874cSmycroft #include <sys/vnode.h>
77264b874cSmycroft #include <sys/kernel.h>
7839b86955Sperseant #include <sys/trace.h>
7939b86955Sperseant #include <sys/resourcevar.h>
80fc9422c9Selad #include <sys/kauth.h>
81264b874cSmycroft 
8215158895Sdholland #include <ufs/lfs/ulfs_quotacommon.h>
8315158895Sdholland #include <ufs/lfs/ulfs_inode.h>
8415158895Sdholland #include <ufs/lfs/ulfsmount.h>
8515158895Sdholland #include <ufs/lfs/ulfs_extern.h>
86264b874cSmycroft 
87264b874cSmycroft #include <ufs/lfs/lfs.h>
8834f0d74cSdholland #include <ufs/lfs/lfs_accessors.h>
89264b874cSmycroft #include <ufs/lfs/lfs_extern.h>
905bc8cc2bSdholland #include <ufs/lfs/lfs_kernel.h>
91264b874cSmycroft 
92481da54fSperseant static int lfs_update_seguse(struct lfs *, struct inode *ip, long, size_t);
93a3ff3a30Sfvdl static int lfs_indirtrunc(struct inode *, daddr_t, daddr_t,
9495a8d28cSdholland 			  daddr_t, int, daddr_t *, daddr_t *,
9595a8d28cSdholland 			  long *, size_t *);
96481da54fSperseant static int lfs_blkfree (struct lfs *, struct inode *, daddr_t, size_t, long *, size_t *);
974a780c9aSad static int lfs_vtruncbuf(struct vnode *, daddr_t, bool, int);
9839b86955Sperseant 
99264b874cSmycroft /* Search a block for a specific dinode. */
100b1828e0bSdholland union lfs_dinode *
lfs_ifind(struct lfs * fs,ino_t ino,struct buf * bp)1014e3fced9Sperseant lfs_ifind(struct lfs *fs, ino_t ino, struct buf *bp)
102264b874cSmycroft {
103b1828e0bSdholland 	union lfs_dinode *ldip;
104b1828e0bSdholland 	unsigned num, i;
105264b874cSmycroft 
1061ebfc508Sperseant 	ASSERT_NO_SEGLOCK(fs);
1074e3fced9Sperseant 	/*
10832ae84b1Sperseant 	 * Read the inode block backwards, since later versions of the
10932ae84b1Sperseant 	 * inode will supercede earlier ones.  Though it is unlikely, it is
11032ae84b1Sperseant 	 * possible that the same inode will appear in the same inode block.
1114e3fced9Sperseant 	 */
112b1828e0bSdholland 	num = LFS_INOPB(fs);
113b1828e0bSdholland 	for (i = num; i-- > 0; ) {
114c9cfc4bdSdholland 		ldip = DINO_IN_BLOCK(fs, bp->b_data, i);
115b1828e0bSdholland 		if (lfs_dino_getinumber(fs, ldip) == ino)
116264b874cSmycroft 			return (ldip);
117b1828e0bSdholland 	}
118264b874cSmycroft 
119c9cfc4bdSdholland 	printf("searched %u entries for %ju\n", num, (uintmax_t)ino);
120f59b8f4bSdholland 	printf("offset is 0x%jx (seg %d)\n", (uintmax_t)lfs_sb_getoffset(fs),
121f59b8f4bSdholland 	       lfs_dtosn(fs, lfs_sb_getoffset(fs)));
122f59b8f4bSdholland 	printf("block is 0x%jx (seg %d)\n",
123f59b8f4bSdholland 	       (uintmax_t)LFS_DBTOFSB(fs, bp->b_blkno),
124f59b8f4bSdholland 	       lfs_dtosn(fs, LFS_DBTOFSB(fs, bp->b_blkno)));
1258886b0f4Sperseant 
1268886b0f4Sperseant 	return NULL;
127264b874cSmycroft }
128264b874cSmycroft 
129264b874cSmycroft int
lfs_update(struct vnode * vp,const struct timespec * acc,const struct timespec * mod,int updflags)130a748ea88Syamt lfs_update(struct vnode *vp, const struct timespec *acc,
131a748ea88Syamt     const struct timespec *mod, int updflags)
1327bd9e243Schristos {
133264b874cSmycroft 	struct inode *ip;
13449bebcb4Sdholland 	struct lfs *fs = VFSTOULFS(vp->v_mount)->um_lfs;
135bc25b306Smycroft 	int flags;
136a9744ff0Sriastradh 	int error;
137264b874cSmycroft 
1381ebfc508Sperseant 	ASSERT_NO_SEGLOCK(fs);
139e5bc90f4Sfvdl 	if (vp->v_mount->mnt_flag & MNT_RDONLY)
140264b874cSmycroft 		return (0);
141e5bc90f4Sfvdl 	ip = VTOI(vp);
1421b8f5ea3Sperseant 
1431b8f5ea3Sperseant 	/*
1441b8f5ea3Sperseant 	 * If we are called from vinvalbuf, and the file's blocks have
1451b8f5ea3Sperseant 	 * already been scheduled for writing, but the writes have not
1461b8f5ea3Sperseant 	 * yet completed, lfs_vflush will not be called, and vinvalbuf
1471b8f5ea3Sperseant 	 * will cause a panic.	So, we must wait until any pending write
148f0728fdcSperseant 	 * for our inode completes, if we are called with UPDATE_WAIT set.
1491b8f5ea3Sperseant 	 */
150e225b7bdSrmind 	mutex_enter(vp->v_interlock);
151a748ea88Syamt 	while ((updflags & (UPDATE_WAIT|UPDATE_DIROP)) == UPDATE_WAIT &&
152f0728fdcSperseant 	    WRITEINPROG(vp)) {
1539cac905aSmaya 		DLOG((DLOG_SEG, "lfs_update: sleeping on ino %llu"
1549cac905aSmaya 		      " (in progress)\n", (unsigned long long) ip->i_number));
155e225b7bdSrmind 		cv_wait(&vp->v_cv, vp->v_interlock);
1561b8f5ea3Sperseant 	}
157e225b7bdSrmind 	mutex_exit(vp->v_interlock);
158a748ea88Syamt 	LFS_ITIMES(ip, acc, mod, NULL);
159a748ea88Syamt 	if (updflags & UPDATE_CLOSE)
1608f063ba0Smaya 		flags = ip->i_state & (IN_MODIFIED | IN_ACCESSED | IN_CLEANING);
161bc25b306Smycroft 	else
1628f063ba0Smaya 		flags = ip->i_state & (IN_MODIFIED | IN_CLEANING);
163bc25b306Smycroft 	if (flags == 0)
164264b874cSmycroft 		return (0);
165264b874cSmycroft 
166264b874cSmycroft 	/* If sync, push back the vnode and any dirty blocks it may have. */
167a748ea88Syamt 	if ((updflags & (UPDATE_WAIT|UPDATE_DIROP)) == UPDATE_WAIT) {
1687dad9f73Sad 		/* Avoid flushing VU_DIROP. */
1694a780c9aSad 		mutex_enter(&lfs_lock);
17088207e0cSperseant 		++fs->lfs_diropwait;
1717dad9f73Sad 		while (vp->v_uflag & VU_DIROP) {
1729cac905aSmaya 			DLOG((DLOG_DIROP, "lfs_update: sleeping on inode %llu "
1739cac905aSmaya 			      "(dirops)\n", (unsigned long long) ip->i_number));
1748f063ba0Smaya 			DLOG((DLOG_DIROP, "lfs_update: vflags 0x%x, i_state"
1754a780c9aSad 			      " 0x%x\n",
1764a780c9aSad 			      vp->v_iflag | vp->v_vflag | vp->v_uflag,
1778f063ba0Smaya 			      ip->i_state));
17888207e0cSperseant 			if (fs->lfs_dirops == 0)
179a9744ff0Sriastradh 				break;
18088207e0cSperseant 			else
1814a780c9aSad 				mtsleep(&fs->lfs_writer, PRIBIO+1, "lfs_fsync",
1824a780c9aSad 					0, &lfs_lock);
18388207e0cSperseant 			/* XXX KS - by falling out here, are we writing the vn
18488207e0cSperseant 			twice? */
18588207e0cSperseant 		}
18688207e0cSperseant 		--fs->lfs_diropwait;
187a9744ff0Sriastradh 		fs->lfs_writer++;
188a9744ff0Sriastradh 		if (vp->v_uflag & VU_DIROP) {
189a9744ff0Sriastradh 			KASSERT(fs->lfs_dirops == 0);
190a9744ff0Sriastradh 			lfs_flush_fs(fs, SEGM_SYNC);
191a9744ff0Sriastradh 		}
1924a780c9aSad 		mutex_exit(&lfs_lock);
193a9744ff0Sriastradh 		error = lfs_vflush(vp);
194a9744ff0Sriastradh 		mutex_enter(&lfs_lock);
195a9744ff0Sriastradh 		if (--fs->lfs_writer == 0)
196a9744ff0Sriastradh 			cv_broadcast(&fs->lfs_diropscv);
197a9744ff0Sriastradh 		mutex_exit(&lfs_lock);
198a9744ff0Sriastradh 		return error;
19988207e0cSperseant 	}
20088207e0cSperseant 	return 0;
201264b874cSmycroft }
202264b874cSmycroft 
20339b86955Sperseant #define	SINGLE	0	/* index of single indirect block */
20439b86955Sperseant #define	DOUBLE	1	/* index of double indirect block */
20539b86955Sperseant #define	TRIPLE	2	/* index of triple indirect block */
206264b874cSmycroft /*
20739b86955Sperseant  * Truncate the inode oip to at most length size, freeing the
20839b86955Sperseant  * disk blocks.
209264b874cSmycroft  */
21049bebcb4Sdholland /* VOP_BWRITE 1 + ULFS_NIADDR + lfs_balloc == 2 + 2*ULFS_NIADDR times */
21132ae84b1Sperseant 
212264b874cSmycroft int
lfs_truncate(struct vnode * ovp,off_t length,int ioflag,kauth_cred_t cred)213db06a930Spooka lfs_truncate(struct vnode *ovp, off_t length, int ioflag, kauth_cred_t cred)
2147bd9e243Schristos {
215a3ff3a30Sfvdl 	daddr_t lastblock;
21645a21b76Smycroft 	struct inode *oip = VTOI(ovp);
21749bebcb4Sdholland 	daddr_t bn, lbn, lastiblock[ULFS_NIADDR], indir_lbn[ULFS_NIADDR];
218da32f22cSdholland 	/* note: newblks is set but only actually used if DIAGNOSTIC */
219da32f22cSdholland 	daddr_t newblks[ULFS_NDADDR + ULFS_NIADDR] __diagused;
220264b874cSmycroft 	struct lfs *fs;
22139b86955Sperseant 	struct buf *bp;
22239b86955Sperseant 	int offset, size, level;
223d36f790bSdholland 	daddr_t count, rcount;
22495a8d28cSdholland 	daddr_t blocksreleased = 0, real_released = 0;
225a748ea88Syamt 	int i, nblocks;
22639b86955Sperseant 	int aflags, error, allerror = 0;
22739b86955Sperseant 	off_t osize;
22839b86955Sperseant 	long lastseg;
22939b86955Sperseant 	size_t bc;
230c3989871Sperseant 	int obufsize, odb;
231c364d884Sperseant 	int usepc;
232264b874cSmycroft 
2336a17dd42Syamt 	if (ovp->v_type == VCHR || ovp->v_type == VBLK ||
2346a17dd42Syamt 	    ovp->v_type == VFIFO || ovp->v_type == VSOCK) {
2356a17dd42Syamt 		KASSERT(oip->i_size == 0);
2366a17dd42Syamt 		return 0;
2376a17dd42Syamt 	}
2386a17dd42Syamt 
239eedb742eSperseant 	if (length < 0)
240eedb742eSperseant 		return (EINVAL);
241eedb742eSperseant 
242d3cfc0d3Sdholland 	fs = oip->i_lfs;
243d3cfc0d3Sdholland 
24439b86955Sperseant 	if (ovp->v_type == VLNK &&
245d3cfc0d3Sdholland 	    (oip->i_size < fs->um_maxsymlinklen ||
246d3cfc0d3Sdholland 	     (fs->um_maxsymlinklen == 0 &&
247da32f22cSdholland 	      lfs_dino_getblocks(fs, oip->i_din) == 0))) {
248a82e1fa8Sriastradh 		KASSERTMSG((length == 0),
249a82e1fa8Sriastradh 		    "partial truncate of symlink: %jd", (intmax_t)length);
25042614ed3Sfvdl 		memset((char *)SHORTLINK(oip), 0, (u_int)oip->i_size);
251da32f22cSdholland 		oip->i_size = 0;
252da32f22cSdholland 		lfs_dino_setsize(fs, oip->i_din, 0);
2538f063ba0Smaya 		oip->i_state |= IN_CHANGE | IN_UPDATE;
254a748ea88Syamt 		return (lfs_update(ovp, NULL, NULL, 0));
255264b874cSmycroft 	}
25642614ed3Sfvdl 	if (oip->i_size == length) {
257ea946371Smaya 		/* still do a uvm_vnp_setsize() as writesize may be larger */
258ea946371Smaya 		uvm_vnp_setsize(ovp, length);
2598f063ba0Smaya 		oip->i_state |= IN_CHANGE | IN_UPDATE;
260a748ea88Syamt 		return (lfs_update(ovp, NULL, NULL, 0));
26139b86955Sperseant 	}
2621b8f5ea3Sperseant 	lfs_imtime(fs);
26342614ed3Sfvdl 	osize = oip->i_size;
264daeb6c37Sperseant 	usepc = (ovp->v_type == VREG && ovp != fs->lfs_ivnode);
265264b874cSmycroft 
2661ebfc508Sperseant 	ASSERT_NO_SEGLOCK(fs);
26739b86955Sperseant 	/*
26839b86955Sperseant 	 * Lengthen the size of the file. We must ensure that the
26939b86955Sperseant 	 * last byte of the file is allocated. Since the smallest
27039b86955Sperseant 	 * value of osize is 0, length will be at least 1.
27139b86955Sperseant 	 */
27239b86955Sperseant 	if (osize < length) {
273d3cfc0d3Sdholland 		if (length > fs->um_maxfilesize)
274eedb742eSperseant 			return (EFBIG);
27539b86955Sperseant 		aflags = B_CLRBUF;
276bb174509Smycroft 		if (ioflag & IO_SYNC)
27739b86955Sperseant 			aflags |= B_SYNC;
278daeb6c37Sperseant 		if (usepc) {
279855bb66eSchristos 			if (lfs_lblkno(fs, osize) < ULFS_NDADDR &&
280855bb66eSchristos 			    lfs_lblkno(fs, osize) != lfs_lblkno(fs, length) &&
281855bb66eSchristos 			    lfs_blkroundup(fs, osize) != osize) {
282f3fbefe7Smycroft 				off_t eob;
283f3fbefe7Smycroft 
284855bb66eSchristos 				eob = lfs_blkroundup(fs, osize);
285da51d139Syamt 				uvm_vnp_setwritesize(ovp, eob);
28649bebcb4Sdholland 				error = ulfs_balloc_range(ovp, osize,
287a748ea88Syamt 				    eob - osize, cred, aflags);
288d6fd66feSbouyer 				if (error) {
289d6fd66feSbouyer 					(void) lfs_truncate(ovp, osize,
290d6fd66feSbouyer 						    ioflag & IO_SYNC, cred);
291daeb6c37Sperseant 					return error;
292d6fd66feSbouyer 				}
293bb174509Smycroft 				if (ioflag & IO_SYNC) {
294d2a0ebb6Sad 					rw_enter(ovp->v_uobj.vmobjlock, RW_WRITER);
295daeb6c37Sperseant 					VOP_PUTPAGES(ovp,
296adca8af5Sdholland 					    trunc_page(osize & lfs_sb_getbmask(fs)),
297f3fbefe7Smycroft 					    round_page(eob),
298daeb6c37Sperseant 					    PGO_CLEANIT | PGO_SYNCIO);
299daeb6c37Sperseant 				}
300daeb6c37Sperseant 			}
301da51d139Syamt 			uvm_vnp_setwritesize(ovp, length);
30249bebcb4Sdholland 			error = ulfs_balloc_range(ovp, length - 1, 1, cred,
303daeb6c37Sperseant 						 aflags);
304daeb6c37Sperseant 			if (error) {
305a748ea88Syamt 				(void) lfs_truncate(ovp, osize,
306db06a930Spooka 						    ioflag & IO_SYNC, cred);
307daeb6c37Sperseant 				return error;
308daeb6c37Sperseant 			}
309daeb6c37Sperseant 			uvm_vnp_setsize(ovp, length);
3108f063ba0Smaya 			oip->i_state |= IN_CHANGE | IN_UPDATE;
31142614ed3Sfvdl 			KASSERT(ovp->v_size == oip->i_size);
312f59b8f4bSdholland 			oip->i_lfs_hiblk = lfs_lblkno(fs, oip->i_size + lfs_sb_getbsize(fs) - 1) - 1;
313a748ea88Syamt 			return (lfs_update(ovp, NULL, NULL, 0));
314daeb6c37Sperseant 		} else {
31559be5399Syamt 			error = lfs_reserve(fs, ovp, NULL,
316adca8af5Sdholland 			    lfs_btofsb(fs, (ULFS_NIADDR + 2) << lfs_sb_getbshift(fs)));
3179c7f8050Sperseant 			if (error)
3189c7f8050Sperseant 				return (error);
319a748ea88Syamt 			error = lfs_balloc(ovp, length - 1, 1, cred,
320daeb6c37Sperseant 					   aflags, &bp);
32159be5399Syamt 			lfs_reserve(fs, ovp, NULL,
322adca8af5Sdholland 			    -lfs_btofsb(fs, (ULFS_NIADDR + 2) << lfs_sb_getbshift(fs)));
323eedb742eSperseant 			if (error)
324eedb742eSperseant 				return (error);
325da32f22cSdholland 			oip->i_size = length;
326da32f22cSdholland 			lfs_dino_setsize(fs, oip->i_din, oip->i_size);
32739b86955Sperseant 			uvm_vnp_setsize(ovp, length);
32849511bbaShannken 			(void) VOP_BWRITE(bp->b_vp, bp);
3298f063ba0Smaya 			oip->i_state |= IN_CHANGE | IN_UPDATE;
330f59b8f4bSdholland 			oip->i_lfs_hiblk = lfs_lblkno(fs, oip->i_size + lfs_sb_getbsize(fs) - 1) - 1;
331a748ea88Syamt 			return (lfs_update(ovp, NULL, NULL, 0));
332daeb6c37Sperseant 		}
333264b874cSmycroft 	}
334264b874cSmycroft 
33559be5399Syamt 	if ((error = lfs_reserve(fs, ovp, NULL,
336adca8af5Sdholland 	    lfs_btofsb(fs, (2 * ULFS_NIADDR + 3) << lfs_sb_getbshift(fs)))) != 0)
3379c7f8050Sperseant 		return (error);
3381b8f5ea3Sperseant 
339264b874cSmycroft 	/*
34039b86955Sperseant 	 * Shorten the size of the file. If the file is not being
34139b86955Sperseant 	 * truncated to a block boundary, the contents of the
34239b86955Sperseant 	 * partial block following the end of the file must be
34339b86955Sperseant 	 * zero'ed in case it ever becomes accessible again because
34439b86955Sperseant 	 * of subsequent file growth. Directories however are not
34539b86955Sperseant 	 * zero'ed as they should grow back initialized to empty.
346264b874cSmycroft 	 */
347855bb66eSchristos 	offset = lfs_blkoff(fs, length);
348264b874cSmycroft 	lastseg = -1;
34939b86955Sperseant 	bc = 0;
350c364d884Sperseant 
3512f695b54Sperseant 	if (ovp != fs->lfs_ivnode)
352c364d884Sperseant 		lfs_seglock(fs, SEGM_PROT);
35339b86955Sperseant 	if (offset == 0) {
354da32f22cSdholland 		oip->i_size = length;
355da32f22cSdholland 		lfs_dino_setsize(fs, oip->i_din, oip->i_size);
3568feb2c22Sperseant 	} else if (!usepc) {
357855bb66eSchristos 		lbn = lfs_lblkno(fs, length);
35839b86955Sperseant 		aflags = B_CLRBUF;
359bb174509Smycroft 		if (ioflag & IO_SYNC)
36039b86955Sperseant 			aflags |= B_SYNC;
361a748ea88Syamt 		error = lfs_balloc(ovp, length - 1, 1, cred, aflags, &bp);
3629c7f8050Sperseant 		if (error) {
36359be5399Syamt 			lfs_reserve(fs, ovp, NULL,
364adca8af5Sdholland 			    -lfs_btofsb(fs, (2 * ULFS_NIADDR + 3) << lfs_sb_getbshift(fs)));
365c364d884Sperseant 			goto errout;
3669c7f8050Sperseant 		}
367c3989871Sperseant 		obufsize = bp->b_bufsize;
368855bb66eSchristos 		odb = lfs_btofsb(fs, bp->b_bcount);
369da32f22cSdholland 		oip->i_size = length;
370da32f22cSdholland 		lfs_dino_setsize(fs, oip->i_din, oip->i_size);
371855bb66eSchristos 		size = lfs_blksize(fs, oip, lbn);
37239b86955Sperseant 		if (ovp->v_type != VDIR)
37339b86955Sperseant 			memset((char *)bp->b_data + offset, 0,
37439b86955Sperseant 			       (u_int)(size - offset));
37570f20a12Spk 		allocbuf(bp, size, 1);
376b2fa822aSad 		if ((bp->b_flags & B_LOCKED) != 0 && bp->b_iodone == NULL) {
3774a780c9aSad 			mutex_enter(&lfs_lock);
378c3989871Sperseant 			locked_queue_bytes -= obufsize - bp->b_bufsize;
3794a780c9aSad 			mutex_exit(&lfs_lock);
3801ebfc508Sperseant 		}
381f59b8f4bSdholland 		if (bp->b_oflags & BO_DELWRI) {
382f59b8f4bSdholland 			lfs_sb_addavail(fs, odb - lfs_btofsb(fs, size));
383f59b8f4bSdholland 			/* XXX shouldn't this wake up on lfs_availsleep? */
384f59b8f4bSdholland 		}
38549511bbaShannken 		(void) VOP_BWRITE(bp->b_vp, bp);
3868feb2c22Sperseant 	} else { /* vp->v_type == VREG && length < osize && offset != 0 */
387b397c875Sperseant 		/*
3888feb2c22Sperseant 		 * When truncating a regular file down to a non-block-aligned
3898feb2c22Sperseant 		 * size, we must zero the part of last block which is past
3908feb2c22Sperseant 		 * the new EOF.  We must synchronously flush the zeroed pages
3918feb2c22Sperseant 		 * to disk since the new pages will be invalidated as soon
3928feb2c22Sperseant 		 * as we inform the VM system of the new, smaller size.
393b397c875Sperseant 		 * We must do this before acquiring the GLOCK, since fetching
394b397c875Sperseant 		 * the pages will acquire the GLOCK internally.
395b397c875Sperseant 		 * So there is a window where another thread could see a whole
396b397c875Sperseant 		 * zeroed page past EOF, but that's life.
397b397c875Sperseant 		 */
398273df636Schristos 		daddr_t xlbn;
3994303882bSmycroft 		voff_t eoz;
4004303882bSmycroft 
401bb174509Smycroft 		aflags = ioflag & IO_SYNC ? B_SYNC : 0;
40249bebcb4Sdholland 		error = ulfs_balloc_range(ovp, length - 1, 1, cred, aflags);
403b397c875Sperseant 		if (error) {
40487d110abSoster 			lfs_reserve(fs, ovp, NULL,
405adca8af5Sdholland 				    -lfs_btofsb(fs, (2 * ULFS_NIADDR + 3) << lfs_sb_getbshift(fs)));
406c364d884Sperseant 			goto errout;
407b397c875Sperseant 		}
408855bb66eSchristos 		xlbn = lfs_lblkno(fs, length);
409855bb66eSchristos 		size = lfs_blksize(fs, oip, xlbn);
410855bb66eSchristos 		eoz = MIN(lfs_lblktosize(fs, xlbn) + size, osize);
411d296304eShannken 		ubc_zerorange(&ovp->v_uobj, length, eoz - length,
412*f5ad84fdSad 		    UBC_VNODE_FLAGS(ovp));
4134303882bSmycroft 		if (round_page(eoz) > round_page(length)) {
414d2a0ebb6Sad 			rw_enter(ovp->v_uobj.vmobjlock, RW_WRITER);
4154303882bSmycroft 			error = VOP_PUTPAGES(ovp, round_page(length),
4164303882bSmycroft 			    round_page(eoz),
417bb174509Smycroft 			    PGO_CLEANIT | PGO_DEACTIVATE |
418bb174509Smycroft 			    ((ioflag & IO_SYNC) ? PGO_SYNCIO : 0));
419b397c875Sperseant 			if (error) {
42087d110abSoster 				lfs_reserve(fs, ovp, NULL,
421adca8af5Sdholland 					    -lfs_btofsb(fs, (2 * ULFS_NIADDR + 3) << lfs_sb_getbshift(fs)));
422c364d884Sperseant 				goto errout;
423b397c875Sperseant 			}
424b397c875Sperseant 		}
4254303882bSmycroft 	}
426b397c875Sperseant 
427560c0c56Syamt 	genfs_node_wrlock(ovp);
428b397c875Sperseant 
429da32f22cSdholland 	oip->i_size = length;
430da32f22cSdholland 	lfs_dino_setsize(fs, oip->i_din, oip->i_size);
43139b86955Sperseant 	uvm_vnp_setsize(ovp, length);
43279748725Smlelstv 
43339b86955Sperseant 	/*
43439b86955Sperseant 	 * Calculate index into inode's block list of
43539b86955Sperseant 	 * last direct and indirect blocks (if any)
43639b86955Sperseant 	 * which we want to keep.  Lastblock is -1 when
43739b86955Sperseant 	 * the file is truncated to 0.
43839b86955Sperseant 	 */
4395f627fe9Sperseant 	/* Avoid sign overflow - XXX assumes that off_t is a quad_t. */
440f59b8f4bSdholland 	if (length > QUAD_MAX - lfs_sb_getbsize(fs))
441f59b8f4bSdholland 		lastblock = lfs_lblkno(fs, QUAD_MAX - lfs_sb_getbsize(fs));
4425f627fe9Sperseant 	else
443f59b8f4bSdholland 		lastblock = lfs_lblkno(fs, length + lfs_sb_getbsize(fs) - 1) - 1;
44449bebcb4Sdholland 	lastiblock[SINGLE] = lastblock - ULFS_NDADDR;
445855bb66eSchristos 	lastiblock[DOUBLE] = lastiblock[SINGLE] - LFS_NINDIR(fs);
446855bb66eSchristos 	lastiblock[TRIPLE] = lastiblock[DOUBLE] - LFS_NINDIR(fs) * LFS_NINDIR(fs);
447f59b8f4bSdholland 	nblocks = lfs_btofsb(fs, lfs_sb_getbsize(fs));
44839b86955Sperseant 	/*
44939b86955Sperseant 	 * Record changed file and block pointers before we start
45039b86955Sperseant 	 * freeing blocks.  lastiblock values are also normalized to -1
45139b86955Sperseant 	 * for calls to lfs_indirtrunc below.
45239b86955Sperseant 	 */
453da32f22cSdholland 	for (i=0; i<ULFS_NDADDR; i++) {
454da32f22cSdholland 		newblks[i] = lfs_dino_getdb(fs, oip->i_din, i);
455da32f22cSdholland 	}
456da32f22cSdholland 	for (i=0; i<ULFS_NIADDR; i++) {
457da32f22cSdholland 		newblks[ULFS_NDADDR + i] = lfs_dino_getib(fs, oip->i_din, i);
458da32f22cSdholland 	}
45939b86955Sperseant 	for (level = TRIPLE; level >= SINGLE; level--)
46039b86955Sperseant 		if (lastiblock[level] < 0) {
46149bebcb4Sdholland 			newblks[ULFS_NDADDR+level] = 0;
46239b86955Sperseant 			lastiblock[level] = -1;
46339b86955Sperseant 		}
46449bebcb4Sdholland 	for (i = ULFS_NDADDR - 1; i > lastblock; i--)
46539b86955Sperseant 		newblks[i] = 0;
466737dec82Sperseant 
467da32f22cSdholland 	oip->i_size = osize;
468da32f22cSdholland 	lfs_dino_setsize(fs, oip->i_din, oip->i_size);
4694a780c9aSad 	error = lfs_vtruncbuf(ovp, lastblock + 1, false, 0);
47039b86955Sperseant 	if (error && !allerror)
47139b86955Sperseant 		allerror = error;
472737dec82Sperseant 
47339b86955Sperseant 	/*
47439b86955Sperseant 	 * Indirect blocks first.
47539b86955Sperseant 	 */
47649bebcb4Sdholland 	indir_lbn[SINGLE] = -ULFS_NDADDR;
477855bb66eSchristos 	indir_lbn[DOUBLE] = indir_lbn[SINGLE] - LFS_NINDIR(fs) - 1;
478855bb66eSchristos 	indir_lbn[TRIPLE] = indir_lbn[DOUBLE] - LFS_NINDIR(fs) * LFS_NINDIR(fs) - 1;
47939b86955Sperseant 	for (level = TRIPLE; level >= SINGLE; level--) {
480da32f22cSdholland 		bn = lfs_dino_getib(fs, oip->i_din, level);
48139b86955Sperseant 		if (bn != 0) {
48239b86955Sperseant 			error = lfs_indirtrunc(oip, indir_lbn[level],
483ef2da504Sperseant 					       bn, lastiblock[level],
484ef2da504Sperseant 					       level, &count, &rcount,
485db06a930Spooka 					       &lastseg, &bc);
48639b86955Sperseant 			if (error)
48739b86955Sperseant 				allerror = error;
488ef2da504Sperseant 			real_released += rcount;
48939b86955Sperseant 			blocksreleased += count;
49039b86955Sperseant 			if (lastiblock[level] < 0) {
491da32f22cSdholland 				if (lfs_dino_getib(fs, oip->i_din, level) > 0)
492ef2da504Sperseant 					real_released += nblocks;
493ef2da504Sperseant 				blocksreleased += nblocks;
494da32f22cSdholland 				lfs_dino_setib(fs, oip->i_din, level, 0);
495f59b8f4bSdholland 				lfs_blkfree(fs, oip, bn, lfs_sb_getbsize(fs),
496481da54fSperseant 					    &lastseg, &bc);
4975ed792ecSperseant         			lfs_deregister_block(ovp, bn);
49839b86955Sperseant 			}
49939b86955Sperseant 		}
50039b86955Sperseant 		if (lastiblock[level] >= 0)
50139b86955Sperseant 			goto done;
502264b874cSmycroft 	}
503f058684bSperseant 
504f058684bSperseant 	/*
50539b86955Sperseant 	 * All whole direct blocks or frags.
506f058684bSperseant 	 */
50749bebcb4Sdholland 	for (i = ULFS_NDADDR - 1; i > lastblock; i--) {
50832ae84b1Sperseant 		long bsize, obsize;
50939b86955Sperseant 
510da32f22cSdholland 		bn = lfs_dino_getdb(fs, oip->i_din, i);
51139b86955Sperseant 		if (bn == 0)
51239b86955Sperseant 			continue;
513855bb66eSchristos 		bsize = lfs_blksize(fs, oip, i);
514da32f22cSdholland 		if (lfs_dino_getdb(fs, oip->i_din, i) > 0) {
51532ae84b1Sperseant 			/* Check for fragment size changes */
51632ae84b1Sperseant 			obsize = oip->i_lfs_fragsize[i];
517855bb66eSchristos 			real_released += lfs_btofsb(fs, obsize);
51832ae84b1Sperseant 			oip->i_lfs_fragsize[i] = 0;
51932ae84b1Sperseant 		} else
52032ae84b1Sperseant 			obsize = 0;
521855bb66eSchristos 		blocksreleased += lfs_btofsb(fs, bsize);
522da32f22cSdholland 		lfs_dino_setdb(fs, oip->i_din, i, 0);
523481da54fSperseant 		lfs_blkfree(fs, oip, bn, obsize, &lastseg, &bc);
5245ed792ecSperseant         	lfs_deregister_block(ovp, bn);
52539b86955Sperseant 	}
52639b86955Sperseant 	if (lastblock < 0)
52739b86955Sperseant 		goto done;
52839b86955Sperseant 
52939b86955Sperseant 	/*
53039b86955Sperseant 	 * Finally, look for a change in size of the
53139b86955Sperseant 	 * last direct block; release any frags.
53239b86955Sperseant 	 */
533da32f22cSdholland 	bn = lfs_dino_getdb(fs, oip->i_din, lastblock);
53439b86955Sperseant 	if (bn != 0) {
535761de734Ssimonb 		long oldspace, newspace;
536761de734Ssimonb #if 0
537761de734Ssimonb 		long olddspace;
538761de734Ssimonb #endif
53939b86955Sperseant 
54039b86955Sperseant 		/*
54139b86955Sperseant 		 * Calculate amount of space we're giving
54239b86955Sperseant 		 * back as old block size minus new block size.
54339b86955Sperseant 		 */
544855bb66eSchristos 		oldspace = lfs_blksize(fs, oip, lastblock);
545761de734Ssimonb #if 0
54632ae84b1Sperseant 		olddspace = oip->i_lfs_fragsize[lastblock];
547761de734Ssimonb #endif
54832ae84b1Sperseant 
549da32f22cSdholland 		oip->i_size = length;
550da32f22cSdholland 		lfs_dino_setsize(fs, oip->i_din, oip->i_size);
551855bb66eSchristos 		newspace = lfs_blksize(fs, oip, lastblock);
55239b86955Sperseant 		if (newspace == 0)
55339b86955Sperseant 			panic("itrunc: newspace");
55439b86955Sperseant 		if (oldspace - newspace > 0) {
555855bb66eSchristos 			blocksreleased += lfs_btofsb(fs, oldspace - newspace);
55639b86955Sperseant 		}
55732ae84b1Sperseant #if 0
55832ae84b1Sperseant 		if (bn > 0 && olddspace - newspace > 0) {
55932ae84b1Sperseant 			/* No segment accounting here, just vnode */
560855bb66eSchristos 			real_released += lfs_btofsb(fs, olddspace - newspace);
56132ae84b1Sperseant 		}
56232ae84b1Sperseant #endif
56339b86955Sperseant 	}
56439b86955Sperseant 
56539b86955Sperseant done:
56639b86955Sperseant 	/* Finish segment accounting corrections */
567481da54fSperseant 	lfs_update_seguse(fs, oip, lastseg, bc);
56839b86955Sperseant 	for (level = SINGLE; level <= TRIPLE; level++)
5694c213dd2Sriastradh 		KASSERTMSG(((newblks[ULFS_NDADDR + level] == 0) ==
5705d735856Sriastradh 			(lfs_dino_getib(fs, oip->i_din, level) == 0)),
5715d735856Sriastradh 		    "lfs itrunc1");
57249bebcb4Sdholland 	for (i = 0; i < ULFS_NDADDR; i++)
5734c213dd2Sriastradh 		KASSERTMSG(((newblks[i] == 0) ==
5745d735856Sriastradh 			(lfs_dino_getdb(fs, oip->i_din, i) == 0)),
5755d735856Sriastradh 		    "lfs itrunc2");
5765d735856Sriastradh 	KASSERTMSG((length != 0 || LIST_EMPTY(&ovp->v_cleanblkhd)),
5775d735856Sriastradh 	    "lfs itrunc3a");
5785d735856Sriastradh 	KASSERTMSG((length != 0 || LIST_EMPTY(&ovp->v_dirtyblkhd)),
5795d735856Sriastradh 	    "lfs itrunc3b");
5805d735856Sriastradh 
58139b86955Sperseant 	/*
58239b86955Sperseant 	 * Put back the real size.
58339b86955Sperseant 	 */
584da32f22cSdholland 	oip->i_size = length;
585da32f22cSdholland 	lfs_dino_setsize(fs, oip->i_din, oip->i_size);
586ef2da504Sperseant 	oip->i_lfs_effnblks -= blocksreleased;
587ef8d65beSmaya 
588ef8d65beSmaya 	mutex_enter(&lfs_lock);
589da32f22cSdholland 	lfs_dino_setblocks(fs, oip->i_din,
590da32f22cSdholland 	    lfs_dino_getblocks(fs, oip->i_din) - real_released);
591f59b8f4bSdholland 	lfs_sb_addbfree(fs, blocksreleased);
592a82e1fa8Sriastradh 
593a82e1fa8Sriastradh 	KASSERTMSG((oip->i_size != 0 ||
594a82e1fa8Sriastradh 		lfs_dino_getblocks(fs, oip->i_din) == 0),
5959cac905aSmaya 	    "ino %llu truncate to 0 but %jd blks/%jd effblks",
5969cac905aSmaya 	    (unsigned long long) oip->i_number,
5979cac905aSmaya 	    lfs_dino_getblocks(fs, oip->i_din), oip->i_lfs_effnblks);
598a82e1fa8Sriastradh 	KASSERTMSG((oip->i_size != 0 || oip->i_lfs_effnblks == 0),
5999cac905aSmaya 	    "ino %llu truncate to 0 but %jd blks/%jd effblks",
6009cac905aSmaya 	    (unsigned long long) oip->i_number,
6019cac905aSmaya 	    lfs_dino_getblocks(fs, oip->i_din), oip->i_lfs_effnblks);
60239ce23c1Sperseant 
60339ce23c1Sperseant 	/*
60439ce23c1Sperseant 	 * If we truncated to zero, take us off the paging queue.
60539ce23c1Sperseant 	 */
6068f063ba0Smaya 	if (oip->i_size == 0 && oip->i_state & IN_PAGING) {
6078f063ba0Smaya 		oip->i_state &= ~IN_PAGING;
60839ce23c1Sperseant 		TAILQ_REMOVE(&fs->lfs_pchainhd, oip, i_lfs_pchain);
60939ce23c1Sperseant 	}
6104a780c9aSad 	mutex_exit(&lfs_lock);
61139ce23c1Sperseant 
6128f063ba0Smaya 	oip->i_state |= IN_CHANGE;
61326f16a77Sdholland #if defined(LFS_QUOTA) || defined(LFS_QUOTA2)
6147c08dd66Sdholland 	(void) lfs_chkdq(oip, -blocksreleased, NOCRED, 0);
61539b86955Sperseant #endif
61659be5399Syamt 	lfs_reserve(fs, ovp, NULL,
617adca8af5Sdholland 	    -lfs_btofsb(fs, (2 * ULFS_NIADDR + 3) << lfs_sb_getbshift(fs)));
618560c0c56Syamt 	genfs_node_unlock(ovp);
619c364d884Sperseant   errout:
620f59b8f4bSdholland 	oip->i_lfs_hiblk = lfs_lblkno(fs, oip->i_size + lfs_sb_getbsize(fs) - 1) - 1;
6212f695b54Sperseant 	if (ovp != fs->lfs_ivnode)
622c364d884Sperseant 		lfs_segunlock(fs);
623c364d884Sperseant 	return (allerror ? allerror : error);
62439b86955Sperseant }
62539b86955Sperseant 
6265ed792ecSperseant /* Update segment and avail usage information when removing a block. */
62739b86955Sperseant static int
lfs_blkfree(struct lfs * fs,struct inode * ip,daddr_t daddr,size_t bsize,long * lastseg,size_t * num)628481da54fSperseant lfs_blkfree(struct lfs *fs, struct inode *ip, daddr_t daddr,
629481da54fSperseant 	    size_t bsize, long *lastseg, size_t *num)
630f058684bSperseant {
63139b86955Sperseant 	long seg;
63239b86955Sperseant 	int error = 0;
633f058684bSperseant 
6341ebfc508Sperseant 	ASSERT_SEGLOCK(fs);
635855bb66eSchristos 	bsize = lfs_fragroundup(fs, bsize);
63639b86955Sperseant 	if (daddr > 0) {
637855bb66eSchristos 		if (*lastseg != (seg = lfs_dtosn(fs, daddr))) {
638481da54fSperseant 			error = lfs_update_seguse(fs, ip, *lastseg, *num);
63939b86955Sperseant 			*num = bsize;
64039b86955Sperseant 			*lastseg = seg;
64139b86955Sperseant 		} else
64239b86955Sperseant 			*num += bsize;
64339b86955Sperseant 	}
6445ed792ecSperseant 
6457ea5ec67Sperseant 	return error;
646f058684bSperseant }
6477ea5ec67Sperseant 
64839b86955Sperseant /* Finish the accounting updates for a segment. */
64939b86955Sperseant static int
lfs_update_seguse(struct lfs * fs,struct inode * ip,long lastseg,size_t num)650481da54fSperseant lfs_update_seguse(struct lfs *fs, struct inode *ip, long lastseg, size_t num)
651f058684bSperseant {
652481da54fSperseant 	struct segdelta *sd;
65339b86955Sperseant 
6541ebfc508Sperseant 	ASSERT_SEGLOCK(fs);
65539b86955Sperseant 	if (lastseg < 0 || num == 0)
65639b86955Sperseant 		return 0;
65739b86955Sperseant 
658481da54fSperseant 	LIST_FOREACH(sd, &ip->i_lfs_segdhd, list)
659481da54fSperseant 		if (sd->segnum == lastseg)
660481da54fSperseant 			break;
661481da54fSperseant 	if (sd == NULL) {
662481da54fSperseant 		sd = malloc(sizeof(*sd), M_SEGMENT, M_WAITOK);
663481da54fSperseant 		sd->segnum = lastseg;
664481da54fSperseant 		sd->num = 0;
665481da54fSperseant 		LIST_INSERT_HEAD(&ip->i_lfs_segdhd, sd, list);
666737dec82Sperseant 	}
667481da54fSperseant 	sd->num += num;
668b397c875Sperseant 
669b397c875Sperseant 	return 0;
67039b86955Sperseant }
67139b86955Sperseant 
672481da54fSperseant static void
lfs_finalize_seguse(struct lfs * fs,void * v)673481da54fSperseant lfs_finalize_seguse(struct lfs *fs, void *v)
674481da54fSperseant {
675481da54fSperseant 	SEGUSE *sup;
676481da54fSperseant 	struct buf *bp;
677481da54fSperseant 	struct segdelta *sd;
678481da54fSperseant 	LIST_HEAD(, segdelta) *hd = v;
679481da54fSperseant 
680481da54fSperseant 	ASSERT_SEGLOCK(fs);
681481da54fSperseant 	while((sd = LIST_FIRST(hd)) != NULL) {
682481da54fSperseant 		LIST_REMOVE(sd, list);
683481da54fSperseant 		LFS_SEGENTRY(sup, fs, sd->segnum, bp);
684481da54fSperseant 		if (sd->num > sup->su_nbytes) {
685481da54fSperseant 			printf("lfs_finalize_seguse: segment %ld short by %ld\n",
686481da54fSperseant 				sd->segnum, (long)(sd->num - sup->su_nbytes));
687481da54fSperseant 			panic("lfs_finalize_seguse: negative bytes");
688481da54fSperseant 			sup->su_nbytes = sd->num;
689481da54fSperseant 		}
690481da54fSperseant 		sup->su_nbytes -= sd->num;
691481da54fSperseant 		LFS_WRITESEGENTRY(sup, fs, sd->segnum, bp);
692481da54fSperseant 		free(sd, M_SEGMENT);
693481da54fSperseant 	}
694481da54fSperseant }
695481da54fSperseant 
696481da54fSperseant /* Finish the accounting updates for a segment. */
697481da54fSperseant void
lfs_finalize_ino_seguse(struct lfs * fs,struct inode * ip)698481da54fSperseant lfs_finalize_ino_seguse(struct lfs *fs, struct inode *ip)
699481da54fSperseant {
700481da54fSperseant 	ASSERT_SEGLOCK(fs);
701481da54fSperseant 	lfs_finalize_seguse(fs, &ip->i_lfs_segdhd);
702481da54fSperseant }
703481da54fSperseant 
704481da54fSperseant /* Finish the accounting updates for a segment. */
705481da54fSperseant void
lfs_finalize_fs_seguse(struct lfs * fs)706481da54fSperseant lfs_finalize_fs_seguse(struct lfs *fs)
707481da54fSperseant {
708481da54fSperseant 	ASSERT_SEGLOCK(fs);
709481da54fSperseant 	lfs_finalize_seguse(fs, &fs->lfs_segdhd);
710481da54fSperseant }
711481da54fSperseant 
71239b86955Sperseant /*
71339b86955Sperseant  * Release blocks associated with the inode ip and stored in the indirect
71439b86955Sperseant  * block bn.  Blocks are free'd in LIFO order up to (but not including)
71539b86955Sperseant  * lastbn.  If level is greater than SINGLE, the block is an indirect block
71639b86955Sperseant  * and recursive calls to indirtrunc must be used to cleanse other indirect
71739b86955Sperseant  * blocks.
71839b86955Sperseant  *
71939b86955Sperseant  * NB: triple indirect blocks are untested.
72039b86955Sperseant  */
72139b86955Sperseant static int
lfs_indirtrunc(struct inode * ip,daddr_t lbn,daddr_t dbn,daddr_t lastbn,int level,daddr_t * countp,daddr_t * rcountp,long * lastsegp,size_t * bcp)722a3ff3a30Sfvdl lfs_indirtrunc(struct inode *ip, daddr_t lbn, daddr_t dbn,
72395a8d28cSdholland 	       daddr_t lastbn, int level, daddr_t *countp,
72495a8d28cSdholland 	       daddr_t *rcountp, long *lastsegp, size_t *bcp)
72539b86955Sperseant {
72639b86955Sperseant 	int i;
72739b86955Sperseant 	struct buf *bp;
72839b86955Sperseant 	struct lfs *fs = ip->i_lfs;
72998320d19Sdholland 	void *bap;
73098320d19Sdholland 	bool bap_needs_free;
73139b86955Sperseant 	struct vnode *vp;
732a3ff3a30Sfvdl 	daddr_t nb, nlbn, last;
73395a8d28cSdholland 	daddr_t blkcount, rblkcount, factor;
73495a8d28cSdholland 	int nblocks;
73595a8d28cSdholland 	daddr_t blocksreleased = 0, real_released = 0;
73639b86955Sperseant 	int error = 0, allerror = 0;
73739b86955Sperseant 
7381ebfc508Sperseant 	ASSERT_SEGLOCK(fs);
73939b86955Sperseant 	/*
74039b86955Sperseant 	 * Calculate index in current block of last
74139b86955Sperseant 	 * block to be kept.  -1 indicates the entire
74239b86955Sperseant 	 * block so we need not calculate the index.
74339b86955Sperseant 	 */
74439b86955Sperseant 	factor = 1;
74539b86955Sperseant 	for (i = SINGLE; i < level; i++)
746855bb66eSchristos 		factor *= LFS_NINDIR(fs);
74739b86955Sperseant 	last = lastbn;
74839b86955Sperseant 	if (lastbn > 0)
74939b86955Sperseant 		last /= factor;
750f59b8f4bSdholland 	nblocks = lfs_btofsb(fs, lfs_sb_getbsize(fs));
75139b86955Sperseant 	/*
75239b86955Sperseant 	 * Get buffer of block pointers, zero those entries corresponding
75339b86955Sperseant 	 * to blocks to be free'd, and update on disk copy first.  Since
75439b86955Sperseant 	 * double(triple) indirect before single(double) indirect, calls
75539b86955Sperseant 	 * to bmap on these blocks will fail.  However, we already have
75639b86955Sperseant 	 * the on disk address, so we have to set the b_blkno field
75739b86955Sperseant 	 * explicitly instead of letting bread do everything for us.
75839b86955Sperseant 	 */
75939b86955Sperseant 	vp = ITOV(ip);
760f59b8f4bSdholland 	bp = getblk(vp, lbn, lfs_sb_getbsize(fs), 0, 0);
7614a780c9aSad 	if (bp->b_oflags & (BO_DONE | BO_DELWRI)) {
76239b86955Sperseant 		/* Braces must be here in case trace evaluates to nothing. */
763f59b8f4bSdholland 		trace(TR_BREADHIT, pack(vp, lfs_sb_getbsize(fs)), lbn);
7647ea5ec67Sperseant 	} else {
765f59b8f4bSdholland 		trace(TR_BREADMISS, pack(vp, lfs_sb_getbsize(fs)), lbn);
766be04ac48Sad 		curlwp->l_ru.ru_inblock++; /* pay for read */
76739b86955Sperseant 		bp->b_flags |= B_READ;
76839b86955Sperseant 		if (bp->b_bcount > bp->b_bufsize)
76939b86955Sperseant 			panic("lfs_indirtrunc: bad buffer size");
770855bb66eSchristos 		bp->b_blkno = LFS_FSBTODB(fs, dbn);
7713db4e2acShannken 		VOP_STRATEGY(vp, bp);
77239b86955Sperseant 		error = biowait(bp);
77339b86955Sperseant 	}
77439b86955Sperseant 	if (error) {
7755c3b2b3fSad 		brelse(bp, 0);
776ef2da504Sperseant 		*countp = *rcountp = 0;
77739b86955Sperseant 		return (error);
77839b86955Sperseant 	}
77939b86955Sperseant 
78039b86955Sperseant 	if (lastbn >= 0) {
78198320d19Sdholland 		/*
78298320d19Sdholland 		 * We still need this block, so copy the data for
78398320d19Sdholland 		 * subsequent processing; then in the original block,
78498320d19Sdholland 		 * zero out the dying block pointers and send it off.
78598320d19Sdholland 		 */
78698320d19Sdholland 		bap = lfs_malloc(fs, lfs_sb_getbsize(fs), LFS_NB_IBLOCK);
78798320d19Sdholland 		memcpy(bap, bp->b_data, lfs_sb_getbsize(fs));
78898320d19Sdholland 		bap_needs_free = true;
78998320d19Sdholland 
79098320d19Sdholland 		for (i = last + 1; i < LFS_NINDIR(fs); i++) {
79198320d19Sdholland 			lfs_iblock_set(fs, bp->b_data, i, 0);
79298320d19Sdholland 		}
79349511bbaShannken 		error = VOP_BWRITE(bp->b_vp, bp);
79439b86955Sperseant 		if (error)
79539b86955Sperseant 			allerror = error;
79698320d19Sdholland 	} else {
79798320d19Sdholland 		bap = bp->b_data;
79898320d19Sdholland 		bap_needs_free = false;
79939b86955Sperseant 	}
80039b86955Sperseant 
80139b86955Sperseant 	/*
80239b86955Sperseant 	 * Recursively free totally unused blocks.
80339b86955Sperseant 	 */
804855bb66eSchristos 	for (i = LFS_NINDIR(fs) - 1, nlbn = lbn + 1 - i * factor; i > last;
80539b86955Sperseant 	    i--, nlbn += factor) {
80698320d19Sdholland 		nb = lfs_iblock_get(fs, bap, i);
80739b86955Sperseant 		if (nb == 0)
80839b86955Sperseant 			continue;
80939b86955Sperseant 		if (level > SINGLE) {
81039b86955Sperseant 			error = lfs_indirtrunc(ip, nlbn, nb,
811a3ff3a30Sfvdl 					       (daddr_t)-1, level - 1,
812ef2da504Sperseant 					       &blkcount, &rblkcount,
813db06a930Spooka 					       lastsegp, bcp);
81439b86955Sperseant 			if (error)
81539b86955Sperseant 				allerror = error;
81639b86955Sperseant 			blocksreleased += blkcount;
817ef2da504Sperseant 			real_released += rblkcount;
81839b86955Sperseant 		}
819f59b8f4bSdholland 		lfs_blkfree(fs, ip, nb, lfs_sb_getbsize(fs), lastsegp, bcp);
82098320d19Sdholland 		if (lfs_iblock_get(fs, bap, i) > 0)
821ef2da504Sperseant 			real_released += nblocks;
82239b86955Sperseant 		blocksreleased += nblocks;
82339b86955Sperseant 	}
82439b86955Sperseant 
82539b86955Sperseant 	/*
82639b86955Sperseant 	 * Recursively free last partial block.
82739b86955Sperseant 	 */
82839b86955Sperseant 	if (level > SINGLE && lastbn >= 0) {
82939b86955Sperseant 		last = lastbn % factor;
83098320d19Sdholland 		nb = lfs_iblock_get(fs, bap, i);
83139b86955Sperseant 		if (nb != 0) {
83239b86955Sperseant 			error = lfs_indirtrunc(ip, nlbn, nb,
83339b86955Sperseant 					       last, level - 1, &blkcount,
834db06a930Spooka 					       &rblkcount, lastsegp, bcp);
83539b86955Sperseant 			if (error)
83639b86955Sperseant 				allerror = error;
837ef2da504Sperseant 			real_released += rblkcount;
83839b86955Sperseant 			blocksreleased += blkcount;
83939b86955Sperseant 		}
84039b86955Sperseant 	}
84139b86955Sperseant 
84298320d19Sdholland 	if (bap_needs_free) {
84398320d19Sdholland 		lfs_free(fs, bap, LFS_NB_IBLOCK);
84439b86955Sperseant 	} else {
8454a780c9aSad 		mutex_enter(&bufcache_lock);
8464a780c9aSad 		if (bp->b_oflags & BO_DELWRI) {
847c3989871Sperseant 			LFS_UNLOCK_BUF(bp);
848f59b8f4bSdholland 			lfs_sb_addavail(fs, lfs_btofsb(fs, bp->b_bcount));
849f59b8f4bSdholland 			wakeup(&fs->lfs_availsleep);
850c3989871Sperseant 		}
8514a780c9aSad 		brelsel(bp, BC_INVAL);
8524a780c9aSad 		mutex_exit(&bufcache_lock);
853f058684bSperseant 	}
85439b86955Sperseant 
85539b86955Sperseant 	*countp = blocksreleased;
856ef2da504Sperseant 	*rcountp = real_released;
85739b86955Sperseant 	return (allerror);
85839b86955Sperseant }
85939b86955Sperseant 
8607ea5ec67Sperseant /*
86139b86955Sperseant  * Destroy any in core blocks past the truncation length.
86239b86955Sperseant  * Inlined from vtruncbuf, so that lfs_avail could be updated.
863c364d884Sperseant  * We take the seglock to prevent cleaning from occurring while we are
864b397c875Sperseant  * invalidating blocks.
8657ea5ec67Sperseant  */
86639b86955Sperseant static int
lfs_vtruncbuf(struct vnode * vp,daddr_t lbn,bool catch,int slptimeo)8674a780c9aSad lfs_vtruncbuf(struct vnode *vp, daddr_t lbn, bool catch, int slptimeo)
86839b86955Sperseant {
86939b86955Sperseant 	struct buf *bp, *nbp;
87041b7cd45Smaya 	int error = 0;
87139b86955Sperseant 	struct lfs *fs;
872b397c875Sperseant 	voff_t off;
873b397c875Sperseant 
874b397c875Sperseant 	off = round_page((voff_t)lbn << vp->v_mount->mnt_fs_bshift);
875d2a0ebb6Sad 	rw_enter(vp->v_uobj.vmobjlock, RW_WRITER);
876b397c875Sperseant 	error = VOP_PUTPAGES(vp, off, 0, PGO_FREE | PGO_SYNCIO);
877f3fbefe7Smycroft 	if (error)
878b397c875Sperseant 		return error;
87939b86955Sperseant 
88039b86955Sperseant 	fs = VTOI(vp)->i_lfs;
88139b86955Sperseant 
8821ebfc508Sperseant 	ASSERT_SEGLOCK(fs);
8834a780c9aSad 
8844a780c9aSad 	mutex_enter(&bufcache_lock);
88539b86955Sperseant restart:
88639b86955Sperseant 	for (bp = LIST_FIRST(&vp->v_cleanblkhd); bp; bp = nbp) {
88739b86955Sperseant 		nbp = LIST_NEXT(bp, b_vnbufs);
88839b86955Sperseant 		if (bp->b_lblkno < lbn)
88939b86955Sperseant 			continue;
890fb00b838Sad 		error = bbusy(bp, catch, slptimeo, NULL);
8914a780c9aSad 		if (error == EPASSTHROUGH)
8924a780c9aSad 			goto restart;
89341b7cd45Smaya 		if (error)
89441b7cd45Smaya 			goto exit;
89541b7cd45Smaya 
8964a780c9aSad 		mutex_enter(bp->b_objlock);
8974a780c9aSad 		if (bp->b_oflags & BO_DELWRI) {
8984a780c9aSad 			bp->b_oflags &= ~BO_DELWRI;
899f59b8f4bSdholland 			lfs_sb_addavail(fs, lfs_btofsb(fs, bp->b_bcount));
900f59b8f4bSdholland 			wakeup(&fs->lfs_availsleep);
9014446817aSperseant 		}
9024a780c9aSad 		mutex_exit(bp->b_objlock);
90331fc62d4Sperseant 		LFS_UNLOCK_BUF(bp);
9044a780c9aSad 		brelsel(bp, BC_INVAL | BC_VFLUSH);
90539b86955Sperseant 	}
90639b86955Sperseant 
90739b86955Sperseant 	for (bp = LIST_FIRST(&vp->v_dirtyblkhd); bp; bp = nbp) {
90839b86955Sperseant 		nbp = LIST_NEXT(bp, b_vnbufs);
90939b86955Sperseant 		if (bp->b_lblkno < lbn)
91039b86955Sperseant 			continue;
911fb00b838Sad 		error = bbusy(bp, catch, slptimeo, NULL);
9124a780c9aSad 		if (error == EPASSTHROUGH)
9134a780c9aSad 			goto restart;
91441b7cd45Smaya 		if (error)
91541b7cd45Smaya 			goto exit;
91641b7cd45Smaya 
9174a780c9aSad 		mutex_enter(bp->b_objlock);
9184a780c9aSad 		if (bp->b_oflags & BO_DELWRI) {
9194a780c9aSad 			bp->b_oflags &= ~BO_DELWRI;
920f59b8f4bSdholland 			lfs_sb_addavail(fs, lfs_btofsb(fs, bp->b_bcount));
921f59b8f4bSdholland 			wakeup(&fs->lfs_availsleep);
9224446817aSperseant 		}
9234a780c9aSad 		mutex_exit(bp->b_objlock);
92431fc62d4Sperseant 		LFS_UNLOCK_BUF(bp);
9254a780c9aSad 		brelsel(bp, BC_INVAL | BC_VFLUSH);
92639b86955Sperseant 	}
92741b7cd45Smaya exit:
9284a780c9aSad 	mutex_exit(&bufcache_lock);
92939b86955Sperseant 
93041b7cd45Smaya 	return error;
931f058684bSperseant }
93239b86955Sperseant 
933