+ struct buf *bp;
+ int offset, osize, size, level;
+ long count, nblocks, blocksreleased = 0;
+ register int i;
+ int aflags, error, allerror;
+ struct inode tip;
+
+ vnode_pager_setsize(ITOV(oip), length);
+ if (oip->i_size <= length) {
+ oip->i_flag |= ICHG|IUPD;
+ error = iupdat(oip, &time, &time, 1);
+ return (error);
+ }
+ /*
+ * 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.
+ */
+ fs = oip->i_fs;
+ 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 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 accessable again because
+ * of subsequent file growth.
+ */
+ osize = oip->i_size;
+ offset = blkoff(fs, length);
+ if (offset == 0) {
+ oip->i_size = length;
+ } else {
+ lbn = lblkno(fs, length);
+ aflags = B_CLRBUF;
+ if (flags & IO_SYNC)
+ aflags |= B_SYNC;
+#ifdef QUOTA
+ if (error = getinoquota(oip))
+ return (error);
+#endif
+ if (error = balloc(oip, lbn, offset, &bp, aflags))
+ return (error);
+ oip->i_size = length;
+ size = blksize(fs, oip, lbn);
+ (void) vnode_pager_uncache(ITOV(oip));
+ bzero(bp->b_un.b_addr + offset, (unsigned)(size - offset));
+ allocbuf(bp, size);
+ if (flags & IO_SYNC)
+ bwrite(bp);
+ else
+ bdwrite(bp);
+ }
+ /*
+ * 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 indirtrunc below.
+ */
+ tip = *oip;
+ tip.i_size = osize;
+ for (level = TRIPLE; level >= SINGLE; level--)
+ if (lastiblock[level] < 0) {
+ oip->i_ib[level] = 0;
+ lastiblock[level] = -1;
+ }
+ for (i = NDADDR - 1; i > lastblock; i--)
+ oip->i_db[i] = 0;
+ oip->i_flag |= ICHG|IUPD;
+ vinvalbuf(ITOV(oip), (length > 0));
+ allerror = iupdat(oip, &time, &time, MNT_WAIT);