+ register int i;
+ struct buf *bp;
+ register struct fs *fs = ip->i_fs;
+ register daddr_t *bap;
+ daddr_t *copy, nb, last;
+ long blkcount, factor;
+ int nblocks, blocksreleased = 0;
+ int error, allerror = 0;
+
+ /*
+ * Calculate index in current block of last
+ * block to be kept. -1 indicates the entire
+ * block so we need not calculate the index.
+ */
+ factor = 1;
+ for (i = SINGLE; i < level; i++)
+ factor *= NINDIR(fs);
+ last = lastbn;
+ if (lastbn > 0)
+ last /= factor;
+ nblocks = btodb(fs->fs_bsize);
+ /*
+ * Get buffer of block pointers, zero those
+ * entries corresponding to blocks to be free'd,
+ * and update on disk copy first.
+ */
+#ifdef SECSIZE
+ bp = bread(ip->i_dev, fsbtodb(fs, bn), (int)fs->fs_bsize,
+ fs->fs_dbsize);
+#else SECSIZE
+ error = bread(ip->i_devvp, fsbtodb(fs, bn), (int)fs->fs_bsize,
+ NOCRED, &bp);
+ if (error) {
+ brelse(bp);
+ *countp = 0;
+ return (error);
+ }
+ bap = bp->b_un.b_daddr;
+ MALLOC(copy, daddr_t *, fs->fs_bsize, M_TEMP, M_WAITOK);
+ bcopy((caddr_t)bap, (caddr_t)copy, (u_int)fs->fs_bsize);
+ bzero((caddr_t)&bap[last + 1],
+ (u_int)(NINDIR(fs) - (last + 1)) * sizeof (daddr_t));
+ if (last == -1)
+ bp->b_flags |= B_INVAL;
+ error = bwrite(bp);
+ if (error)
+ allerror = error;
+ bap = copy;
+
+ /*
+ * Recursively free totally unused blocks.
+ */
+ for (i = NINDIR(fs) - 1; i > last; i--) {
+ nb = bap[i];
+ if (nb == 0)
+ continue;
+ if (level > SINGLE) {
+ error = indirtrunc(ip, nb, (daddr_t)-1, level - 1,
+ &blkcount);
+ if (error)
+ allerror = error;
+ blocksreleased += blkcount;
+ }
+ blkfree(ip, nb, (off_t)fs->fs_bsize);
+ blocksreleased += nblocks;
+ }