- 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, DEV_BSIZE);
- dev = oip->i_dev;
- s = splimp();
- for (i = 0; i < count; i += CLSIZE)
- if (mfind(dev, bn + i))
- munhash(dev, bn + i);
- splx(s);
- 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;
- }
-
- /*
- * All whole direct blocks or frags.
- */
- for (i = NDADDR - 1; i > lastblock; i--) {
- register int bsize;
-
- bn = ip->i_db[i];
- if (bn == 0)
- continue;
- ip->i_db[i] = 0;
- bsize = (off_t)blksize(fs, ip, i);
- free(ip, 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 = ip->i_db[lastblock];
- if (bn != 0) {
- int oldspace, newspace;
-
- /*
- * Calculate amount of space we're giving
- * back as old block size minus new block size.
- */
- oldspace = blksize(fs, ip, lastblock);
- ip->i_size = length;
- newspace = blksize(fs, ip, lastblock);
- if (newspace == 0)
- panic("itrunc: 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);
- free(ip, bn, oldspace - newspace);
- blocksreleased += btodb(oldspace - newspace);
- }
- }
-done:
-/* BEGIN PARANOIA */
- for (level = SINGLE; level <= TRIPLE; level++)
- if (ip->i_ib[level] != oip->i_ib[level])
- panic("itrunc1");
- for (i = 0; i < NDADDR; i++)
- if (ip->i_db[i] != oip->i_db[i])
- panic("itrunc2");
-/* END PARANOIA */
- oip->i_blocks -= blocksreleased;
- if (oip->i_blocks < 0) /* sanity */
- oip->i_blocks = 0;
- oip->i_flag |= ICHG;
-#ifdef QUOTA
- (void) chkdq(oip, -blocksreleased, 0);
-#endif