- struct buf *bp;
- int offset, osize, size, level;
- long count, nblocks, blocksreleased = 0;
- register int i;
- int error, allerror = 0;
- struct inode tip;
-
- 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);
- error = balloc(oip, lbn, offset, &bn, B_CLRBUF);
- if (error)
- return (error);
- if ((long)bn < 0)
- panic("itrunc: hole");
- oip->i_size = length;
- size = blksize(fs, oip, lbn);
- count = howmany(size, CLBYTES);
- munhash(oip->i_devvp, bn + i * CLBYTES / DEV_BSIZE);
- error = bread(oip->i_devvp, bn, size, &bp);
- if (error) {
- oip->i_size = osize;
- brelse(bp);
- return (error);
- }
- 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;
- allerror = syncip(oip);
-
- /*
- * Indirect blocks first.
- */
- ip = &tip;
- for (level = TRIPLE; level >= SINGLE; level--) {
- bn = ip->i_ib[level];
- if (bn != 0) {
- error = indirtrunc(ip, bn, lastiblock[level], level,
- &count);
- if (error)
- allerror = error;
- blocksreleased += count;
- if (lastiblock[level] < 0) {
- ip->i_ib[level] = 0;
- blkfree(ip, bn, (off_t)fs->fs_bsize);
- blocksreleased += nblocks;
- }
- }
- if (lastiblock[level] >= 0)
- goto done;
- }