return (EINVAL);
return (0);
ffs_truncate(struct inode *oip, off_t length, int flags, struct ucred *cred){	struct vnode *ovp;	daddr_t lastblock;	daddr_t bn, lbn, lastiblock[NIADDR], indir_lbn[NIADDR];	daddr_t oldblks[NDADDR + NIADDR], newblks[NDADDR + NIADDR];	struct fs *fs;	struct buf *bp;	int offset, size, level;	long count, nblocks, vflags, blocksreleased = 0;	register int i;	int aflags, error, allerror;	off_t osize;	if (length < 0)		return (EINVAL);	ovp = ITOV(oip);	if (ovp->v_type != VREG &&	    ovp->v_type != VDIR &&	    ovp->v_type != VLNK)		return (EINVAL);	if (oip->i_ffs_size == length)		return (0);	if (ovp->v_type == VLNK &&	    (oip->i_ffs_size < ovp->v_mount->mnt_maxsymlinklen ||	     (ovp->v_mount->mnt_maxsymlinklen == 0 &&	      oip->i_din.ffs_din.di_blocks == 0))) {#ifdef DIAGNOSTIC		if (length != 0)			panic("ffs_truncate: partial truncate of symlink");#endif		bzero((char *)&oip->i_ffs_shortlink, (u_int)oip->i_ffs_size);		oip->i_ffs_size = 0;		oip->i_flag |= IN_CHANGE | IN_UPDATE;		return (UFS_UPDATE(oip, MNT_WAIT));	}#ifdef QUOTA	if ((error = getinoquota(oip)) != 0)		return (error);#endif	uvm_vnp_setsize(ovp, length);	oip->i_ci.ci_lasta = oip->i_ci.ci_clen 	    = oip->i_ci.ci_cstart = oip->i_ci.ci_lastw = 0;	if (DOINGSOFTDEP(ovp)) {		if (length > 0 || softdep_slowdown(ovp)) {			/*			 * If a file is only partially truncated, then			 * we have to clean up the data structures			 * describing the allocation past the truncation			 * point. Finding and deallocating those structures			 * is a lot of work. Since partial truncation occurs			 * rarely, we solve the problem by syncing the file			 * so that it will have no data structures left.			 */			if ((error = VOP_FSYNC(ovp, cred, MNT_WAIT,					       curproc)) != 0)				return (error);		} else {#ifdef QUOTA			(void) chkdq(oip, -oip->i_ffs_blocks, NOCRED, 0);#endif			softdep_setup_freeblocks(oip, length);			(void) vinvalbuf(ovp, 0, cred, curproc, 0, 0);			oip->i_flag |= IN_CHANGE | IN_UPDATE;			return (UFS_UPDATE(oip, 0));		}	}	fs = oip->i_fs;	osize = oip->i_ffs_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 (length > fs->fs_maxfilesize)			return (EFBIG);		aflags = B_CLRBUF;		if (flags & IO_SYNC)			aflags |= B_SYNC;		error = UFS_BUF_ALLOC(oip, length - 1, 1, 				   cred, aflags, &bp);		if (error)			return (error);		oip->i_ffs_size = length;		uvm_vnp_setsize(ovp, length);		(void) uvm_vnp_uncache(ovp);		if (aflags & B_SYNC)			bwrite(bp);		else			bawrite(bp);		oip->i_flag |= IN_CHANGE | IN_UPDATE;		return (UFS_UPDATE(oip, MNT_WAIT));	}	uvm_vnp_setsize(ovp, length);	/*	 * Shorten the size of the file. If the file is not being	 * truncated to a block boundary, the contents of the	 * partial block following the end of the file must be	 * zero'ed in case it ever becomes accessible again because	 * of subsequent file growth. Directories however are not	 * zero'ed as they should grow back initialized to empty.	 */	offset = blkoff(fs, length);	if (offset == 0) {		oip->i_ffs_size = length;	} else {		lbn = lblkno(fs, length);		aflags = B_CLRBUF;		if (flags & IO_SYNC)			aflags |= B_SYNC;		error = UFS_BUF_ALLOC(oip, length - 1, 1,				   cred, aflags, &bp);		if (error)			return (error);		oip->i_ffs_size = length;		size = blksize(fs, oip, lbn);		(void) uvm_vnp_uncache(ovp);		if (ovp->v_type != VDIR)			bzero((char *)bp->b_data + offset,			      (u_int)(size - offset));		allocbuf(bp, size);		if (aflags & B_SYNC)			bwrite(bp);		else			bawrite(bp);	}	/*	 * 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->fs_bsize - 1) - 1;	lastiblock[SINGLE] = lastblock - NDADDR;	lastiblock[DOUBLE] = lastiblock[SINGLE] - NINDIR(fs);	lastiblock[TRIPLE] = lastiblock[DOUBLE] - NINDIR(fs) * NINDIR(fs);	nblocks = btodb(fs->fs_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 ffs_indirtrunc below.	 */	bcopy((caddr_t)&oip->i_ffs_db[0], (caddr_t)oldblks, sizeof oldblks);	for (level = TRIPLE; level >= SINGLE; level--)		if (lastiblock[level] < 0) {			oip->i_ffs_ib[level] = 0;			lastiblock[level] = -1;		}	for (i = NDADDR - 1; i > lastblock; i--)		oip->i_ffs_db[i] = 0;	oip->i_flag |= IN_CHANGE | IN_UPDATE;	if ((error = UFS_UPDATE(oip, MNT_WAIT)) != 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_ffs_db[0], (caddr_t)newblks, sizeof newblks);	bcopy((caddr_t)oldblks, (caddr_t)&oip->i_ffs_db[0], sizeof oldblks);	oip->i_ffs_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 = oip->i_ffs_ib[level];		if (bn != 0) {			error = ffs_indirtrunc(oip, indir_lbn[level],			    fsbtodb(fs, bn), lastiblock[level], level, &count);			if (error)				allerror = error;			blocksreleased += count;			if (lastiblock[level] < 0) {				oip->i_ffs_ib[level] = 0;				ffs_blkfree(oip, bn, fs->fs_bsize);				blocksreleased += nblocks;			}		}		if (lastiblock[level] >= 0)			goto done;	}	/*	 * All whole direct blocks or frags.	 */	for (i = NDADDR - 1; i > lastblock; i--) {		register long bsize;		bn = oip->i_ffs_db[i];		if (bn == 0)			continue;		oip->i_ffs_db[i] = 0;		bsize = blksize(fs, oip, i);		ffs_blkfree(oip, bn, bsize);		blocksreleased += btodb(bsize);	}	if (lastblock < 0)		goto done;	/*	 * Finally, look for a change in size of the	 * last direct block; release any frags.	 */	bn = oip->i_ffs_db[lastblock];	if (bn != 0) {		long oldspace, newspace;		/*		 * Calculate amount of space we're giving		 * back as old block size minus new block size.		 */		oldspace = blksize(fs, oip, lastblock);		oip->i_ffs_size = length;		newspace = blksize(fs, oip, lastblock);		if (newspace == 0)			panic("ffs_truncate: newspace");		if (oldspace - newspace > 0) {			/*			 * Block number of space to be free'd is			 * the old block # plus the number of frags			 * required for the storage we're keeping.			 */			bn += numfrags(fs, newspace);			ffs_blkfree(oip, bn, oldspace - newspace);			blocksreleased += btodb(oldspace - newspace);		}	}done:#ifdef DIAGNOSTIC	for (level = SINGLE; level <= TRIPLE; level++)		if (newblks[NDADDR + level] != oip->i_ffs_ib[level])			panic("ffs_truncate1");	for (i = 0; i < NDADDR; i++)		if (newblks[i] != oip->i_ffs_db[i])			panic("ffs_truncate2");#endif /* DIAGNOSTIC */	/*	 * Put back the real size.	 */	oip->i_ffs_size = length;	oip->i_ffs_blocks -= blocksreleased;	if (oip->i_ffs_blocks < 0)			/* sanity */		oip->i_ffs_blocks = 0;	oip->i_flag |= IN_CHANGE;#ifdef QUOTA	(void) chkdq(oip, -blocksreleased, NOCRED, 0);#endif	return (allerror);}