+ }
+ /*
+ * 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);
+ bn = fsbtodb(fs, bmap(oip, lbn, B_WRITE, offset));
+ if (u.u_error || (long)bn < 0)
+ return;
+ oip->i_size = length;
+ size = blksize(fs, oip, lbn);
+ count = howmany(size, CLBYTES);
+ dev = oip->i_dev;
+ for (i = 0; i < count; i++)
+#ifdef SECSIZE
+ munhash(dev, bn + i * CLBYTES / fs->fs_dbsize);
+#else SECSIZE
+ munhash(dev, bn + i * CLBYTES / DEV_BSIZE);
+#endif SECSIZE
+ bp = bread(dev, bn, size);
+ if (bp->b_flags & B_ERROR) {
+ u.u_error = EIO;
+ oip->i_size = osize;
+ brelse(bp);
+ return;
+ }
+ bzero(bp->b_un.b_addr + offset, (unsigned)(size - offset));
+ 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;
+ syncip(oip);
+
+ /*
+ * Indirect blocks first.
+ */
+ ip = &tip;
+ for (level = TRIPLE; level >= SINGLE; level--) {
+ bn = ip->i_ib[level];
+ if (bn != 0) {
+ blocksreleased +=
+ indirtrunc(ip, bn, lastiblock[level], level);
+ if (lastiblock[level] < 0) {
+ ip->i_ib[level] = 0;
+ free(ip, bn, (off_t)fs->fs_bsize);
+ blocksreleased += nblocks;
+ }
+ }
+ if (lastiblock[level] >= 0)
+ goto done;
+ }