if (offset != 0) {
if (offset == 0) { oip->i_e2fs_size = length; } else { lbn = lblkno(fs, length); aflags = B_CLRBUF; if (flags & IO_SYNC) aflags |= B_SYNC; error = ext2fs_buf_alloc(oip, lbn, offset, cred, &bp, aflags); if (error) return (error); oip->i_e2fs_size = length;
ext2fs_truncate(struct inode *oip, off_t length, int flags, struct ucred *cred){	struct vnode *ovp = ITOV(oip);	ufs_daddr_t lastblock;	ufs_daddr_t bn, lastiblock[NIADDR], indir_lbn[NIADDR];	ufs_daddr_t oldblks[NDADDR + NIADDR], newblks[NDADDR + NIADDR];	struct m_ext2fs *fs;	int offset, size, level;	long count, nblocks, vflags, blocksreleased = 0;	int i;	int error, allerror;	off_t osize;	if (length < 0)		return (EINVAL);	if (ovp->v_type != VREG &&	    ovp->v_type != VDIR &&	    ovp->v_type != VLNK)		return (0);	if (ovp->v_type == VLNK &&		(oip->i_e2fs_size < ovp->v_mount->mnt_maxsymlinklen ||		 (ovp->v_mount->mnt_maxsymlinklen == 0 &&		  oip->i_e2fs_nblock == 0))) {#ifdef DIAGNOSTIC		if (length != 0)			panic("ext2fs_truncate: partial truncate of symlink");#endif		bzero((char *)&oip->i_din.e2fs_din.e2di_shortlink,			(u_int)oip->i_e2fs_size);		oip->i_e2fs_size = 0;		oip->i_flag |= IN_CHANGE | IN_UPDATE;		return (ext2fs_update(oip, NULL, NULL, 1));	}	if (oip->i_e2fs_size == length) {		oip->i_flag |= IN_CHANGE | IN_UPDATE;		return (ext2fs_update(oip, NULL, NULL, 0));	}	fs = oip->i_e2fs;	osize = oip->i_e2fs_size;	/*	 * Lengthen the size of the file. We must ensure that the	 * last byte of the file is allocated. Since the smallest	 * value of osize is 0, length will be at least 1.	 */	if (osize < length) {#if 0 /* XXX */		if (length > fs->fs_maxfilesize)			return (EFBIG);#endif		ext2fs_balloc_range(ovp, length - 1, 1, cred,		    flags & IO_SYNC ? B_SYNC : 0);		oip->i_flag |= IN_CHANGE | IN_UPDATE;		return (ext2fs_update(oip, NULL, NULL, 1));	}	/*	 * Shorten the size of the file. If the file is not being	 * truncated to a block boundry, the contents of the	 * partial block following the end of the file must be	 * zero'ed in case it ever become accessible again because	 * of subsequent file growth.	 */	offset = blkoff(fs, length);	if (offset != 0) {		size = fs->e2fs_bsize;		/* XXXUBC we should handle more than just VREG */		uvm_vnp_zerorange(ovp, length, size - offset);	}	oip->i_e2fs_size = length;	uvm_vnp_setsize(ovp, length);	/*	 * Calculate index into inode's block list of	 * last direct and indirect blocks (if any)	 * which we want to keep.  Lastblock is -1 when	 * the file is truncated to 0.	 */	lastblock = lblkno(fs, length + fs->e2fs_bsize - 1) - 1;	lastiblock[SINGLE] = lastblock - NDADDR;	lastiblock[DOUBLE] = lastiblock[SINGLE] - NINDIR(fs);	lastiblock[TRIPLE] = lastiblock[DOUBLE] - NINDIR(fs) * NINDIR(fs);	nblocks = btodb(fs->e2fs_bsize);	/*	 * Update file and block pointers on disk before we start freeing	 * blocks.  If we crash before free'ing blocks below, the blocks	 * will be returned to the free list.  lastiblock values are also	 * normalized to -1 for calls to ext2fs_indirtrunc below.	 */	memcpy((caddr_t)oldblks, (caddr_t)&oip->i_e2fs_blocks[0], sizeof oldblks);	for (level = TRIPLE; level >= SINGLE; level--)		if (lastiblock[level] < 0) {			oip->i_e2fs_blocks[NDADDR + level] = 0;			lastiblock[level] = -1;		}	for (i = NDADDR - 1; i > lastblock; i--)		oip->i_e2fs_blocks[i] = 0;	oip->i_flag |= IN_CHANGE | IN_UPDATE;	if ((error = ext2fs_update(oip, NULL, NULL, 1)) != 0)		allerror = error;	/*	 * Having written the new inode to disk, save its new configuration	 * and put back the old block pointers long enough to process them.	 * Note that we save the new block configuration so we can check it	 * when we are done.	 */	bcopy((caddr_t)&oip->i_e2fs_blocks[0], (caddr_t)newblks, sizeof newblks);	bcopy((caddr_t)oldblks, (caddr_t)&oip->i_e2fs_blocks[0], sizeof oldblks);	oip->i_e2fs_size = osize;	vflags = ((length > 0) ? V_SAVE : 0) | V_SAVEMETA;	allerror = vinvalbuf(ovp, vflags, cred, curproc, 0, 0);	/*	 * Indirect blocks first.	 */	indir_lbn[SINGLE] = -NDADDR;	indir_lbn[DOUBLE] = indir_lbn[SINGLE] - NINDIR(fs) -1;	indir_lbn[TRIPLE] = indir_lbn[DOUBLE] - NINDIR(fs) * NINDIR(fs) - 1;	for (level = TRIPLE; level >= SINGLE; level--) {		bn = fs2h32(oip->i_e2fs_blocks[NDADDR + level]);		if (bn != 0) {			error = ext2fs_indirtrunc(oip, indir_lbn[level],			    fsbtodb(fs, bn), lastiblock[level], level, &count);			if (error)				allerror = error;			blocksreleased += count;			if (lastiblock[level] < 0) {				oip->i_e2fs_blocks[NDADDR + level] = 0;				ext2fs_blkfree(oip, bn);				blocksreleased += nblocks;			}		}		if (lastiblock[level] >= 0)			goto done;	}	/*	 * All whole direct blocks or frags.	 */	for (i = NDADDR - 1; i > lastblock; i--) {		bn = fs2h32(oip->i_e2fs_blocks[i]);		if (bn == 0)			continue;		oip->i_e2fs_blocks[i] = 0;		ext2fs_blkfree(oip, bn);		blocksreleased += btodb(fs->e2fs_bsize);	}done:#ifdef DIAGNOSTIC	for (level = SINGLE; level <= TRIPLE; level++)		if (newblks[NDADDR + level] !=		    oip->i_e2fs_blocks[NDADDR + level])			panic("ext2fs_truncate1");	for (i = 0; i < NDADDR; i++)		if (newblks[i] != oip->i_e2fs_blocks[i])			panic("ext2fs_truncate2");	if (length == 0 &&	    (!LIST_EMPTY(&ovp->v_cleanblkhd) ||	     !LIST_EMPTY(&ovp->v_dirtyblkhd)))		panic("ext2fs_truncate3");#endif /* DIAGNOSTIC */	/*	 * Put back the real size.	 */	oip->i_e2fs_size = length;	oip->i_e2fs_nblock -= blocksreleased;	if (oip->i_e2fs_nblock < 0)			/* sanity */		oip->i_e2fs_nblock = 0;	oip->i_flag |= IN_CHANGE;	return (allerror);}