xref: /csrg-svn/sys/ufs/ffs/ffs_inode.c (revision 68846)
123399Smckusick /*
263371Sbostic  * Copyright (c) 1982, 1986, 1989, 1993
363371Sbostic  *	The Regents of the University of California.  All rights reserved.
423399Smckusick  *
544537Sbostic  * %sccs.include.redist.c%
637736Smckusick  *
7*68846Smckusick  *	@(#)ffs_inode.c	8.13 (Berkeley) 04/21/95
823399Smckusick  */
924Sbill 
1051472Sbostic #include <sys/param.h>
1151472Sbostic #include <sys/systm.h>
1251472Sbostic #include <sys/mount.h>
1351472Sbostic #include <sys/proc.h>
1451472Sbostic #include <sys/file.h>
1551472Sbostic #include <sys/buf.h>
1651472Sbostic #include <sys/vnode.h>
1751472Sbostic #include <sys/kernel.h>
1851472Sbostic #include <sys/malloc.h>
1956481Smargo #include <sys/trace.h>
2056481Smargo #include <sys/resourcevar.h>
2124Sbill 
2253471Smckusick #include <vm/vm.h>
2353471Smckusick 
2451472Sbostic #include <ufs/ufs/quota.h>
2551472Sbostic #include <ufs/ufs/inode.h>
2651472Sbostic #include <ufs/ufs/ufsmount.h>
2751472Sbostic #include <ufs/ufs/ufs_extern.h>
2847571Skarels 
2951472Sbostic #include <ufs/ffs/fs.h>
3051472Sbostic #include <ufs/ffs/ffs_extern.h>
3124Sbill 
3268554Smckusick static int ffs_indirtrunc __P((struct inode *, ufs_daddr_t, ufs_daddr_t,
3368554Smckusick 	    ufs_daddr_t, int, long *));
347334Skre 
3524Sbill /*
3664510Sbostic  * Update the access, modified, and inode change times as specified by the
3764510Sbostic  * IACCESS, IUPDATE, and ICHANGE flags respectively. The IMODIFIED flag is
3864510Sbostic  * used to specify that the inode needs to be updated but that the times have
3964510Sbostic  * already been set. The access and modified times are taken from the second
4064510Sbostic  * and third parameters; the inode change time is always taken from the current
4164510Sbostic  * time. If waitfor is set, then wait for the disk write of the inode to
4264510Sbostic  * complete.
4339392Smckusick  */
4451472Sbostic int
ffs_update(ap)4554656Smckusick ffs_update(ap)
4654656Smckusick 	struct vop_update_args /* {
4754656Smckusick 		struct vnode *a_vp;
4864419Sbostic 		struct timeval *a_access;
4964419Sbostic 		struct timeval *a_modify;
5054656Smckusick 		int a_waitfor;
5154656Smckusick 	} */ *ap;
5224Sbill {
5364419Sbostic 	register struct fs *fs;
5437736Smckusick 	struct buf *bp;
5551544Smckusick 	struct inode *ip;
5637736Smckusick 	int error;
5724Sbill 
5856941Storek 	ip = VTOI(ap->a_vp);
5956744Smckusick 	if (ap->a_vp->v_mount->mnt_flag & MNT_RDONLY) {
6064604Sbostic 		ip->i_flag &=
6164604Sbostic 		    ~(IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE);
6251544Smckusick 		return (0);
6356744Smckusick 	}
6464604Sbostic 	if ((ip->i_flag &
6564604Sbostic 	    (IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE)) == 0)
6637736Smckusick 		return (0);
6764604Sbostic 	if (ip->i_flag & IN_ACCESS)
6868554Smckusick 		ip->i_atime = ap->a_access->tv_sec;
6964604Sbostic 	if (ip->i_flag & IN_UPDATE) {
7068554Smckusick 		ip->i_mtime = ap->a_modify->tv_sec;
7154130Smckusick 		ip->i_modrev++;
7252015Smckusick 	}
7364604Sbostic 	if (ip->i_flag & IN_CHANGE)
7468554Smckusick 		ip->i_ctime = time.tv_sec;
7564604Sbostic 	ip->i_flag &= ~(IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE);
7653912Smckusick 	fs = ip->i_fs;
7753472Smckusick 	/*
7853472Smckusick 	 * Ensure that uid and gid are correct. This is a temporary
7953472Smckusick 	 * fix until fsck has been changed to do the update.
8053472Smckusick 	 */
8153912Smckusick 	if (fs->fs_inodefmt < FS_44INODEFMT) {		/* XXX */
8253912Smckusick 		ip->i_din.di_ouid = ip->i_uid;		/* XXX */
8353912Smckusick 		ip->i_din.di_ogid = ip->i_gid;		/* XXX */
8453912Smckusick 	}						/* XXX */
8564604Sbostic 	if (error = bread(ip->i_devvp,
8664604Sbostic 	    fsbtodb(fs, ino_to_fsba(fs, ip->i_number)),
8751544Smckusick 		(int)fs->fs_bsize, NOCRED, &bp)) {
8851544Smckusick 		brelse(bp);
8951544Smckusick 		return (error);
9051544Smckusick 	}
9164604Sbostic 	*((struct dinode *)bp->b_data +
9264604Sbostic 	    ino_to_fsbo(fs, ip->i_number)) = ip->i_din;
9368542Smckusick 	if (ap->a_waitfor && (ap->a_vp->v_mount->mnt_flag & MNT_ASYNC) == 0)
9437736Smckusick 		return (bwrite(bp));
9551544Smckusick 	else {
9637736Smckusick 		bdwrite(bp);
9737736Smckusick 		return (0);
9837736Smckusick 	}
9924Sbill }
10024Sbill 
10110736Ssam #define	SINGLE	0	/* index of single indirect block */
10210736Ssam #define	DOUBLE	1	/* index of double indirect block */
10310736Ssam #define	TRIPLE	2	/* index of triple indirect block */
10424Sbill /*
10564604Sbostic  * Truncate the inode oip to at most length size, freeing the
10664604Sbostic  * disk blocks.
10724Sbill  */
10854656Smckusick ffs_truncate(ap)
10954656Smckusick 	struct vop_truncate_args /* {
11054656Smckusick 		struct vnode *a_vp;
11154656Smckusick 		off_t a_length;
11254656Smckusick 		int a_flags;
11354656Smckusick 		struct ucred *a_cred;
11454656Smckusick 		struct proc *a_p;
11554656Smckusick 	} */ *ap;
11624Sbill {
11753865Sheideman 	register struct vnode *ovp = ap->a_vp;
11868554Smckusick 	ufs_daddr_t lastblock;
11951544Smckusick 	register struct inode *oip;
12068554Smckusick 	ufs_daddr_t bn, lbn, lastiblock[NIADDR], indir_lbn[NIADDR];
12168554Smckusick 	ufs_daddr_t oldblks[NDADDR + NIADDR], newblks[NDADDR + NIADDR];
12254656Smckusick 	off_t length = ap->a_length;
1236569Smckusic 	register struct fs *fs;
12417942Smckusick 	struct buf *bp;
12553234Smckusick 	int offset, size, level;
12656445Smargo 	long count, nblocks, vflags, blocksreleased = 0;
12754763Smckusick 	struct timeval tv;
12817942Smckusick 	register int i;
12939676Smckusick 	int aflags, error, allerror;
13053234Smckusick 	off_t osize;
1319165Ssam 
13268113Smckusick 	if (length < 0)
13368113Smckusick 		return (EINVAL);
13467838Smckusick 	oip = VTOI(ovp);
13554763Smckusick 	tv = time;
13657425Smckusick 	if (ovp->v_type == VLNK &&
13757425Smckusick 	    oip->i_size < ovp->v_mount->mnt_maxsymlinklen) {
13854532Smckusick #ifdef DIAGNOSTIC
13954656Smckusick 		if (length != 0)
14054532Smckusick 			panic("ffs_truncate: partial truncate of symlink");
14154532Smckusick #endif
14254532Smckusick 		bzero((char *)&oip->i_shortlink, (u_int)oip->i_size);
14354532Smckusick 		oip->i_size = 0;
14464604Sbostic 		oip->i_flag |= IN_CHANGE | IN_UPDATE;
14554763Smckusick 		return (VOP_UPDATE(ovp, &tv, &tv, 1));
14654532Smckusick 	}
14759114Smckusick 	if (oip->i_size == length) {
14864604Sbostic 		oip->i_flag |= IN_CHANGE | IN_UPDATE;
14958933Smckusick 		return (VOP_UPDATE(ovp, &tv, &tv, 0));
15013000Ssam 	}
15160916Smckusick #ifdef QUOTA
15260916Smckusick 	if (error = getinoquota(oip))
15360916Smckusick 		return (error);
15460916Smckusick #endif
15568113Smckusick 	fs = oip->i_fs;
15660916Smckusick 	osize = oip->i_size;
1571203Sbill 	/*
15860916Smckusick 	 * Lengthen the size of the file. We must ensure that the
15960916Smckusick 	 * last byte of the file is allocated. Since the smallest
16068113Smckusick 	 * value of osize is 0, length will be at least 1.
16160916Smckusick 	 */
16260916Smckusick 	if (osize < length) {
16368113Smckusick 		if (length > fs->fs_maxfilesize)
16468113Smckusick 			return (EFBIG);
16560916Smckusick 		offset = blkoff(fs, length - 1);
16660916Smckusick 		lbn = lblkno(fs, length - 1);
16760916Smckusick 		aflags = B_CLRBUF;
16860916Smckusick 		if (ap->a_flags & IO_SYNC)
16960916Smckusick 			aflags |= B_SYNC;
17060916Smckusick 		if (error = ffs_balloc(oip, lbn, offset + 1, ap->a_cred, &bp,
17160916Smckusick 		    aflags))
17260916Smckusick 			return (error);
17360916Smckusick 		oip->i_size = length;
174*68846Smckusick 		vnode_pager_setsize(ovp, (u_long)length);
17560916Smckusick 		(void) vnode_pager_uncache(ovp);
17667272Smckusick 		if (aflags & B_SYNC)
17760916Smckusick 			bwrite(bp);
17860916Smckusick 		else
17960916Smckusick 			bawrite(bp);
18064604Sbostic 		oip->i_flag |= IN_CHANGE | IN_UPDATE;
18160916Smckusick 		return (VOP_UPDATE(ovp, &tv, &tv, 1));
18260916Smckusick 	}
18360916Smckusick 	/*
18460916Smckusick 	 * Shorten the size of the file. If the file is not being
18517942Smckusick 	 * truncated to a block boundry, the contents of the
18617942Smckusick 	 * partial block following the end of the file must be
18717942Smckusick 	 * zero'ed in case it ever become accessable again because
18817942Smckusick 	 * of subsequent file growth.
18917942Smckusick 	 */
19054656Smckusick 	offset = blkoff(fs, length);
19160916Smckusick 	if (offset == 0) {
19254656Smckusick 		oip->i_size = length;
19317942Smckusick 	} else {
19454656Smckusick 		lbn = lblkno(fs, length);
19539676Smckusick 		aflags = B_CLRBUF;
19653585Sheideman 		if (ap->a_flags & IO_SYNC)
19739676Smckusick 			aflags |= B_SYNC;
19860916Smckusick 		if (error = ffs_balloc(oip, lbn, offset, ap->a_cred, &bp,
19960916Smckusick 		    aflags))
20041313Smckusick 			return (error);
20154656Smckusick 		oip->i_size = length;
20217942Smckusick 		size = blksize(fs, oip, lbn);
20353865Sheideman 		(void) vnode_pager_uncache(ovp);
20464510Sbostic 		bzero((char *)bp->b_data + offset, (u_int)(size - offset));
20560916Smckusick 		allocbuf(bp, size);
20667272Smckusick 		if (aflags & B_SYNC)
20739676Smckusick 			bwrite(bp);
20839676Smckusick 		else
20958317Smckusick 			bawrite(bp);
21017942Smckusick 	}
211*68846Smckusick 	vnode_pager_setsize(ovp, (u_long)length);
21217942Smckusick 	/*
21359114Smckusick 	 * Calculate index into inode's block list of
21459114Smckusick 	 * last direct and indirect blocks (if any)
21559114Smckusick 	 * which we want to keep.  Lastblock is -1 when
21659114Smckusick 	 * the file is truncated to 0.
21759114Smckusick 	 */
21859114Smckusick 	lastblock = lblkno(fs, length + fs->fs_bsize - 1) - 1;
21959114Smckusick 	lastiblock[SINGLE] = lastblock - NDADDR;
22059114Smckusick 	lastiblock[DOUBLE] = lastiblock[SINGLE] - NINDIR(fs);
22159114Smckusick 	lastiblock[TRIPLE] = lastiblock[DOUBLE] - NINDIR(fs) * NINDIR(fs);
22259114Smckusick 	nblocks = btodb(fs->fs_bsize);
22359114Smckusick 	/*
22451472Sbostic 	 * Update file and block pointers on disk before we start freeing
22551472Sbostic 	 * blocks.  If we crash before free'ing blocks below, the blocks
22651472Sbostic 	 * will be returned to the free list.  lastiblock values are also
22751472Sbostic 	 * normalized to -1 for calls to ffs_indirtrunc below.
2286569Smckusic 	 */
22958317Smckusick 	bcopy((caddr_t)&oip->i_db[0], (caddr_t)oldblks, sizeof oldblks);
23010736Ssam 	for (level = TRIPLE; level >= SINGLE; level--)
23110736Ssam 		if (lastiblock[level] < 0) {
23210736Ssam 			oip->i_ib[level] = 0;
23310736Ssam 			lastiblock[level] = -1;
2349165Ssam 		}
23510736Ssam 	for (i = NDADDR - 1; i > lastblock; i--)
23610736Ssam 		oip->i_db[i] = 0;
23764604Sbostic 	oip->i_flag |= IN_CHANGE | IN_UPDATE;
23858317Smckusick 	if (error = VOP_UPDATE(ovp, &tv, &tv, MNT_WAIT))
23958317Smckusick 		allerror = error;
24058317Smckusick 	/*
24158317Smckusick 	 * Having written the new inode to disk, save its new configuration
24258317Smckusick 	 * and put back the old block pointers long enough to process them.
24358317Smckusick 	 * Note that we save the new block configuration so we can check it
24458317Smckusick 	 * when we are done.
24558317Smckusick 	 */
24658317Smckusick 	bcopy((caddr_t)&oip->i_db[0], (caddr_t)newblks, sizeof newblks);
24758317Smckusick 	bcopy((caddr_t)oldblks, (caddr_t)&oip->i_db[0], sizeof oldblks);
24858317Smckusick 	oip->i_size = osize;
24956445Smargo 	vflags = ((length > 0) ? V_SAVE : 0) | V_SAVEMETA;
25057800Smckusick 	allerror = vinvalbuf(ovp, vflags, ap->a_cred, ap->a_p, 0, 0);
25110736Ssam 
2526569Smckusic 	/*
25310736Ssam 	 * Indirect blocks first.
2546569Smckusic 	 */
25556445Smargo 	indir_lbn[SINGLE] = -NDADDR;
25656445Smargo 	indir_lbn[DOUBLE] = indir_lbn[SINGLE] - NINDIR(fs) - 1;
25756445Smargo 	indir_lbn[TRIPLE] = indir_lbn[DOUBLE] - NINDIR(fs) * NINDIR(fs) - 1;
25810736Ssam 	for (level = TRIPLE; level >= SINGLE; level--) {
25958317Smckusick 		bn = oip->i_ib[level];
2609165Ssam 		if (bn != 0) {
26158317Smckusick 			error = ffs_indirtrunc(oip, indir_lbn[level],
26256481Smargo 			    fsbtodb(fs, bn), lastiblock[level], level, &count);
26337736Smckusick 			if (error)
26437736Smckusick 				allerror = error;
26537736Smckusick 			blocksreleased += count;
26610736Ssam 			if (lastiblock[level] < 0) {
26758317Smckusick 				oip->i_ib[level] = 0;
26858317Smckusick 				ffs_blkfree(oip, bn, fs->fs_bsize);
26910736Ssam 				blocksreleased += nblocks;
27010736Ssam 			}
27110736Ssam 		}
27210736Ssam 		if (lastiblock[level] >= 0)
27310736Ssam 			goto done;
2749165Ssam 	}
27510736Ssam 
2766569Smckusic 	/*
27710736Ssam 	 * All whole direct blocks or frags.
2786569Smckusic 	 */
2799165Ssam 	for (i = NDADDR - 1; i > lastblock; i--) {
28053234Smckusick 		register long bsize;
2819165Ssam 
28258317Smckusick 		bn = oip->i_db[i];
2839165Ssam 		if (bn == 0)
28424Sbill 			continue;
28558317Smckusick 		oip->i_db[i] = 0;
28658317Smckusick 		bsize = blksize(fs, oip, i);
28758317Smckusick 		ffs_blkfree(oip, bn, bsize);
28824525Sbloom 		blocksreleased += btodb(bsize);
28924Sbill 	}
29010736Ssam 	if (lastblock < 0)
29110736Ssam 		goto done;
29210736Ssam 
2931203Sbill 	/*
2949165Ssam 	 * Finally, look for a change in size of the
2959165Ssam 	 * last direct block; release any frags.
2961203Sbill 	 */
29758317Smckusick 	bn = oip->i_db[lastblock];
29810736Ssam 	if (bn != 0) {
29953234Smckusick 		long oldspace, newspace;
30010736Ssam 
3019165Ssam 		/*
3029165Ssam 		 * Calculate amount of space we're giving
3039165Ssam 		 * back as old block size minus new block size.
3049165Ssam 		 */
30558317Smckusick 		oldspace = blksize(fs, oip, lastblock);
30658317Smckusick 		oip->i_size = length;
30758317Smckusick 		newspace = blksize(fs, oip, lastblock);
30810736Ssam 		if (newspace == 0)
30910736Ssam 			panic("itrunc: newspace");
31010736Ssam 		if (oldspace - newspace > 0) {
3119165Ssam 			/*
3129165Ssam 			 * Block number of space to be free'd is
3139165Ssam 			 * the old block # plus the number of frags
3149165Ssam 			 * required for the storage we're keeping.
3159165Ssam 			 */
31610736Ssam 			bn += numfrags(fs, newspace);
31758317Smckusick 			ffs_blkfree(oip, bn, oldspace - newspace);
31812645Ssam 			blocksreleased += btodb(oldspace - newspace);
3199165Ssam 		}
3209165Ssam 	}
3219165Ssam done:
32256619Smckusick #ifdef DIAGNOSTIC
32310736Ssam 	for (level = SINGLE; level <= TRIPLE; level++)
32458317Smckusick 		if (newblks[NDADDR + level] != oip->i_ib[level])
32510736Ssam 			panic("itrunc1");
32610736Ssam 	for (i = 0; i < NDADDR; i++)
32758317Smckusick 		if (newblks[i] != oip->i_db[i])
32810736Ssam 			panic("itrunc2");
32956619Smckusick 	if (length == 0 &&
33065235Smckusick 	    (ovp->v_dirtyblkhd.lh_first || ovp->v_cleanblkhd.lh_first))
33156619Smckusick 		panic("itrunc3");
33256619Smckusick #endif /* DIAGNOSTIC */
33358317Smckusick 	/*
33458317Smckusick 	 * Put back the real size.
33558317Smckusick 	 */
33658317Smckusick 	oip->i_size = length;
33712645Ssam 	oip->i_blocks -= blocksreleased;
33812645Ssam 	if (oip->i_blocks < 0)			/* sanity */
33912645Ssam 		oip->i_blocks = 0;
34064604Sbostic 	oip->i_flag |= IN_CHANGE;
3419165Ssam #ifdef QUOTA
34260916Smckusick 	(void) chkdq(oip, -blocksreleased, NOCRED, 0);
3439165Ssam #endif
34437736Smckusick 	return (allerror);
34524Sbill }
34624Sbill 
3479165Ssam /*
34851472Sbostic  * Release blocks associated with the inode ip and stored in the indirect
34951472Sbostic  * block bn.  Blocks are free'd in LIFO order up to (but not including)
35051472Sbostic  * lastbn.  If level is greater than SINGLE, the block is an indirect block
35151472Sbostic  * and recursive calls to indirtrunc must be used to cleanse other indirect
35251472Sbostic  * blocks.
35310736Ssam  *
35410736Ssam  * NB: triple indirect blocks are untested.
3559165Ssam  */
35651472Sbostic static int
ffs_indirtrunc(ip,lbn,dbn,lastbn,level,countp)35756481Smargo ffs_indirtrunc(ip, lbn, dbn, lastbn, level, countp)
3586569Smckusic 	register struct inode *ip;
35968554Smckusick 	ufs_daddr_t lbn, lastbn;
36068554Smckusick 	ufs_daddr_t dbn;
36110736Ssam 	int level;
36237736Smckusick 	long *countp;
36324Sbill {
3649165Ssam 	register int i;
36531661Smckusick 	struct buf *bp;
36631661Smckusick 	register struct fs *fs = ip->i_fs;
36768554Smckusick 	register ufs_daddr_t *bap;
36856481Smargo 	struct vnode *vp;
36968554Smckusick 	ufs_daddr_t *copy, nb, nlbn, last;
37037736Smckusick 	long blkcount, factor;
37137736Smckusick 	int nblocks, blocksreleased = 0;
37256481Smargo 	int error = 0, allerror = 0;
37324Sbill 
37410736Ssam 	/*
37510736Ssam 	 * Calculate index in current block of last
37610736Ssam 	 * block to be kept.  -1 indicates the entire
37710736Ssam 	 * block so we need not calculate the index.
37810736Ssam 	 */
37910736Ssam 	factor = 1;
38010736Ssam 	for (i = SINGLE; i < level; i++)
38110736Ssam 		factor *= NINDIR(fs);
3829165Ssam 	last = lastbn;
38310736Ssam 	if (lastbn > 0)
38410736Ssam 		last /= factor;
38512645Ssam 	nblocks = btodb(fs->fs_bsize);
38610736Ssam 	/*
38756481Smargo 	 * Get buffer of block pointers, zero those entries corresponding
38856481Smargo 	 * to blocks to be free'd, and update on disk copy first.  Since
38956481Smargo 	 * double(triple) indirect before single(double) indirect, calls
39056481Smargo 	 * to bmap on these blocks will fail.  However, we already have
39156481Smargo 	 * the on disk address, so we have to set the b_blkno field
39256481Smargo 	 * explicitly instead of letting bread do everything for us.
39310736Ssam 	 */
39456481Smargo 	vp = ITOV(ip);
39557800Smckusick 	bp = getblk(vp, lbn, (int)fs->fs_bsize, 0, 0);
39656481Smargo 	if (bp->b_flags & (B_DONE | B_DELWRI)) {
39756481Smargo 		/* Braces must be here in case trace evaluates to nothing. */
39856481Smargo 		trace(TR_BREADHIT, pack(vp, fs->fs_bsize), lbn);
39956481Smargo 	} else {
40056481Smargo 		trace(TR_BREADMISS, pack(vp, fs->fs_bsize), lbn);
40156481Smargo 		curproc->p_stats->p_ru.ru_inblock++;	/* pay for read */
40256481Smargo 		bp->b_flags |= B_READ;
40356481Smargo 		if (bp->b_bcount > bp->b_bufsize)
40456481Smargo 			panic("ffs_indirtrunc: bad buffer size");
40556481Smargo 		bp->b_blkno = dbn;
40656481Smargo 		VOP_STRATEGY(bp);
40756481Smargo 		error = biowait(bp);
40856481Smargo 	}
40937736Smckusick 	if (error) {
41010736Ssam 		brelse(bp);
41137736Smckusick 		*countp = 0;
41237736Smckusick 		return (error);
41310736Ssam 	}
41456481Smargo 
41568554Smckusick 	bap = (ufs_daddr_t *)bp->b_data;
41668554Smckusick 	MALLOC(copy, ufs_daddr_t *, fs->fs_bsize, M_TEMP, M_WAITOK);
41731661Smckusick 	bcopy((caddr_t)bap, (caddr_t)copy, (u_int)fs->fs_bsize);
41810736Ssam 	bzero((caddr_t)&bap[last + 1],
41968554Smckusick 	  (u_int)(NINDIR(fs) - (last + 1)) * sizeof (ufs_daddr_t));
42039676Smckusick 	if (last == -1)
42139676Smckusick 		bp->b_flags |= B_INVAL;
42237736Smckusick 	error = bwrite(bp);
42337736Smckusick 	if (error)
42437736Smckusick 		allerror = error;
42531661Smckusick 	bap = copy;
42610736Ssam 
42710736Ssam 	/*
42810736Ssam 	 * Recursively free totally unused blocks.
42910736Ssam 	 */
43056445Smargo 	for (i = NINDIR(fs) - 1, nlbn = lbn + 1 - i * factor; i > last;
43156481Smargo 	    i--, nlbn += factor) {
43224Sbill 		nb = bap[i];
4339165Ssam 		if (nb == 0)
43424Sbill 			continue;
43537736Smckusick 		if (level > SINGLE) {
43668554Smckusick 			if (error = ffs_indirtrunc(ip, nlbn, fsbtodb(fs, nb),
43768554Smckusick 			    (ufs_daddr_t)-1, level - 1, &blkcount))
43837736Smckusick 				allerror = error;
43937736Smckusick 			blocksreleased += blkcount;
44037736Smckusick 		}
44153234Smckusick 		ffs_blkfree(ip, nb, fs->fs_bsize);
4429165Ssam 		blocksreleased += nblocks;
44324Sbill 	}
44410736Ssam 
44510736Ssam 	/*
44610736Ssam 	 * Recursively free last partial block.
44710736Ssam 	 */
44810736Ssam 	if (level > SINGLE && lastbn >= 0) {
44910736Ssam 		last = lastbn % factor;
4509165Ssam 		nb = bap[i];
45137736Smckusick 		if (nb != 0) {
45256481Smargo 			if (error = ffs_indirtrunc(ip, nlbn, fsbtodb(fs, nb),
45356481Smargo 			    last, level - 1, &blkcount))
45437736Smckusick 				allerror = error;
45537736Smckusick 			blocksreleased += blkcount;
45637736Smckusick 		}
4579165Ssam 	}
45831661Smckusick 	FREE(copy, M_TEMP);
45937736Smckusick 	*countp = blocksreleased;
46037736Smckusick 	return (allerror);
46124Sbill }
462