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