123399Smckusick /* 237736Smckusick * Copyright (c) 1982, 1986, 1989 Regents of the University of California. 337736Smckusick * All rights reserved. 423399Smckusick * 544537Sbostic * %sccs.include.redist.c% 637736Smckusick * 7*51155Sbostic * @(#)lfs_inode.c 7.41 (Berkeley) 09/20/91 823399Smckusick */ 924Sbill 1017099Sbloom #include "param.h" 1117099Sbloom #include "systm.h" 1217099Sbloom #include "mount.h" 1339795Smckusick #include "proc.h" 1437736Smckusick #include "file.h" 1517099Sbloom #include "buf.h" 1637736Smckusick #include "vnode.h" 1717099Sbloom #include "kernel.h" 1831661Smckusick #include "malloc.h" 1924Sbill 20*51155Sbostic #include "../ufs/quota.h" 21*51155Sbostic #include "../ufs/inode.h" 22*51155Sbostic #include "../ufs/ufsmount.h" 23*51155Sbostic #include "lfs.h" 24*51155Sbostic #include "lfs_extern.h" 2547571Skarels 2616840Smckusick #define INOHSZ 512 277334Skre #if ((INOHSZ&(INOHSZ-1)) == 0) 287334Skre #define INOHASH(dev,ino) (((dev)+(ino))&(INOHSZ-1)) 297334Skre #else 3010852Ssam #define INOHASH(dev,ino) (((unsigned)((dev)+(ino)))%INOHSZ) 317334Skre #endif 3224Sbill 33*51155Sbostic union lfsihead { /* LFS */ 34*51155Sbostic union lfsihead *ih_head[2]; 357334Skre struct inode *ih_chain[2]; 36*51155Sbostic } lfsihead[INOHSZ]; 377334Skre 38*51155Sbostic /* LFS */ 39*51155Sbostic extern int prtactive; /* 1 => print out reclaim of active vnodes */ 4039574Smckusick 4124Sbill /* 4239392Smckusick * Initialize hash links for inodes. 4324Sbill */ 44*51155Sbostic lfs_init() 4524Sbill { 4624Sbill register int i; 47*51155Sbostic register union lfsihead *ih = lfsihead; 4824Sbill 49*51155Sbostic printf("lfs_init\n"); 5039492Smckusick #ifndef lint 5139392Smckusick if (VN_MAXPRIVATE < sizeof(struct inode)) 5239392Smckusick panic("ihinit: too small"); 5339492Smckusick #endif /* not lint */ 547334Skre for (i = INOHSZ; --i >= 0; ih++) { 557334Skre ih->ih_head[0] = ih; 567334Skre ih->ih_head[1] = ih; 577334Skre } 58*51155Sbostic #ifdef NOTLFS /* LFS */ 5941313Smckusick #ifdef QUOTA 6041313Smckusick dqinit(); 6141313Smckusick #endif /* QUOTA */ 62*51155Sbostic #endif 6324Sbill } 6424Sbill 65*51155Sbostic lfs_hqueue(ip) 66*51155Sbostic struct inode *ip; 67*51155Sbostic { 68*51155Sbostic union lfsihead *ih; 69*51155Sbostic 70*51155Sbostic printf("lfs_hqueue ino %d\n", ip->i_number); 71*51155Sbostic ih = &lfsihead[INOHASH(ip->i_dev, ip->i_number)]; 72*51155Sbostic insque(ip, ih); 73*51155Sbostic ILOCK(ip); 74*51155Sbostic } 75*51155Sbostic 76*51155Sbostic 7724Sbill /* 7849451Smckusick * Look up a UFS dinode number to find its incore vnode. 7949451Smckusick * If it is not in core, read it in from the specified device. 8049451Smckusick * If it is in core, wait for the lock bit to clear, then 8149451Smckusick * return the inode locked. Detection and handling of mount 8249451Smckusick * points must be done by the calling routine. 8324Sbill */ 84*51155Sbostic lfs_iget(xp, ino, ipp) 8537736Smckusick struct inode *xp; 864818Swnj ino_t ino; 8737736Smckusick struct inode **ipp; 8824Sbill { 8937736Smckusick dev_t dev = xp->i_dev; 9037736Smckusick struct mount *mntp = ITOV(xp)->v_mount; 91*51155Sbostic register struct lfs *fs = VFSTOUFS(mntp)->um_lfs; /* LFS */ 9239440Smckusick extern struct vnodeops ufs_vnodeops, spec_inodeops; 9337736Smckusick register struct inode *ip, *iq; 9437736Smckusick register struct vnode *vp; 9539440Smckusick struct vnode *nvp; 9637736Smckusick struct buf *bp; 97*51155Sbostic union lfsihead *ih; 9841313Smckusick int i, error; 9924Sbill 100*51155Sbostic printf("lfs_iget ino %d\n", ino); 101*51155Sbostic ih = &lfsihead[INOHASH(dev, ino)]; 10224Sbill loop: 10339392Smckusick for (ip = ih->ih_chain[0]; ip != (struct inode *)ih; ip = ip->i_forw) { 10439392Smckusick if (ino != ip->i_number || dev != ip->i_dev) 10539392Smckusick continue; 10639392Smckusick if ((ip->i_flag&ILOCKED) != 0) { 10739392Smckusick ip->i_flag |= IWANT; 10839392Smckusick sleep((caddr_t)ip, PINOD); 10939392Smckusick goto loop; 11039392Smckusick } 11139440Smckusick if (vget(ITOV(ip))) 11239440Smckusick goto loop; 11339392Smckusick *ipp = ip; 11439392Smckusick return(0); 11539392Smckusick } 116*51155Sbostic 117*51155Sbostic /* Allocate new vnode/inode. */ 118*51155Sbostic error = lfs_vcreate(mntp, ino, &nvp); 119*51155Sbostic if (error) { 12037736Smckusick *ipp = 0; 12137736Smckusick return (error); 12237736Smckusick } 12339440Smckusick ip = VTOI(nvp); 124*51155Sbostic 12537736Smckusick /* 12639440Smckusick * Put it onto its hash chain and lock it so that other requests for 12739440Smckusick * this inode will block if they arrive while we are sleeping waiting 12839440Smckusick * for old data structures to be purged or for the contents of the 12939440Smckusick * disk portion of this inode to be read. 13039440Smckusick */ 13139440Smckusick insque(ip, ih); 13239440Smckusick ILOCK(ip); 133*51155Sbostic 134*51155Sbostic /* Read in the disk contents for the inode, copy into the vnode. */ 135*51155Sbostic if (error = bread(VFSTOUFS(mntp)->um_devvp, itod(fs, ino), 136*51155Sbostic (int)fs->lfs_bsize, NOCRED, &bp)) { /* LFS */ 13737736Smckusick /* 13841334Smckusick * The inode does not contain anything useful, so it would 13941334Smckusick * be misleading to leave it on its hash chain. 14041334Smckusick * Iput() will take care of putting it back on the free list. 14141334Smckusick */ 14241334Smckusick remque(ip); 14341334Smckusick ip->i_forw = ip; 14441334Smckusick ip->i_back = ip; 14541334Smckusick /* 14639392Smckusick * Unlock and discard unneeded inode. 14737736Smckusick */ 14839440Smckusick iput(ip); 14937736Smckusick brelse(bp); 15037736Smckusick *ipp = 0; 15139440Smckusick return (error); 15237736Smckusick } 153*51155Sbostic ip->i_din = *lfs_ifind(fs, ino, bp->b_un.b_dino); 15439440Smckusick brelse(bp); 155*51155Sbostic 15637736Smckusick /* 15739440Smckusick * Initialize the associated vnode 15837736Smckusick */ 15939440Smckusick vp = ITOV(ip); 16039440Smckusick vp->v_type = IFTOVT(ip->i_mode); 16140289Smckusick if (vp->v_type == VFIFO) { 16240289Smckusick #ifdef FIFO 16340289Smckusick extern struct vnodeops fifo_inodeops; 16440289Smckusick vp->v_op = &fifo_inodeops; 16540289Smckusick #else 16640289Smckusick iput(ip); 16740289Smckusick *ipp = 0; 16840289Smckusick return (EOPNOTSUPP); 16940289Smckusick #endif /* FIFO */ 17040289Smckusick } 17139440Smckusick if (vp->v_type == VCHR || vp->v_type == VBLK) { 17239440Smckusick vp->v_op = &spec_inodeops; 17339617Smckusick if (nvp = checkalias(vp, ip->i_rdev, mntp)) { 17437736Smckusick /* 17539440Smckusick * Reinitialize aliased inode. 17637736Smckusick */ 17739440Smckusick vp = nvp; 17839440Smckusick iq = VTOI(vp); 17939440Smckusick iq->i_vnode = vp; 18039517Smckusick iq->i_flag = 0; 18139440Smckusick ILOCK(iq); 18239440Smckusick iq->i_din = ip->i_din; 18339440Smckusick iq->i_dev = dev; 18439440Smckusick iq->i_number = ino; 18539440Smckusick insque(iq, ih); 18637736Smckusick /* 18739440Smckusick * Discard unneeded vnode 18837736Smckusick */ 18939440Smckusick ip->i_mode = 0; 19039440Smckusick iput(ip); 19137736Smckusick ip = iq; 19237736Smckusick } 19337736Smckusick } 19439440Smckusick if (ino == ROOTINO) 19539440Smckusick vp->v_flag |= VROOT; 196*51155Sbostic 19738345Smckusick VREF(ip->i_devvp); 198*51155Sbostic 19937736Smckusick *ipp = ip; 20037736Smckusick return (0); 20137736Smckusick } 2027334Skre 20337736Smckusick /* 20439392Smckusick * Last reference to an inode, write the inode out and if necessary, 20539392Smckusick * truncate and deallocate the file. 20639392Smckusick */ 207*51155Sbostic lfs_inactive(vp, p) 20837736Smckusick struct vnode *vp; 20948037Smckusick struct proc *p; 2107118Smckusick { 21137736Smckusick register struct inode *ip = VTOI(vp); 21239392Smckusick int mode, error = 0; 21324Sbill 214*51155Sbostic printf("lfs_inactive: ino %d mode %d nlink %d\n", 215*51155Sbostic ip->i_number, ip->i_mode, ip->i_nlink); 216*51155Sbostic 21739816Smckusick if (prtactive && vp->v_usecount != 0) 21839676Smckusick vprint("ufs_inactive: pushing active", vp); 21938452Smckusick /* 22038452Smckusick * Get rid of inodes related to stale file handles. 22138452Smckusick */ 22239440Smckusick if (ip->i_mode == 0) { 22339676Smckusick if ((vp->v_flag & VXLOCK) == 0) 22439676Smckusick vgone(vp); 22539440Smckusick return (0); 22639440Smckusick } 22738226Smckusick ILOCK(ip); 22841397Smckusick if (ip->i_nlink <= 0 && (vp->v_mount->mnt_flag & MNT_RDONLY) == 0) { 22941313Smckusick #ifdef QUOTA 23041313Smckusick if (!getinoquota(ip)) 23141313Smckusick (void) chkiq(ip, -1, NOCRED, 0); 23241313Smckusick #endif 233*51155Sbostic error = lfs_itrunc(ip, (u_long)0, 0); /* LFS */ 23437736Smckusick mode = ip->i_mode; 23537736Smckusick ip->i_mode = 0; 23644893Strent ip->i_rdev = 0; 23737736Smckusick ip->i_flag |= IUPD|ICHG; 238*51155Sbostic #ifdef NOTLFS /* LFS */ 23937736Smckusick ifree(ip, ip->i_number, mode); 240*51155Sbostic #else 241*51155Sbostic lfs_ifree(ip); 242*51155Sbostic #endif 24337736Smckusick } 244*51155Sbostic ITIMES(ip, &time, &time); 24540033Smckusick IUNLOCK(ip); 24640033Smckusick ip->i_flag = 0; 24737736Smckusick /* 24839392Smckusick * If we are done with the inode, reclaim it 24939392Smckusick * so that it can be reused immediately. 25037736Smckusick */ 25141313Smckusick if (vp->v_usecount == 0 && ip->i_mode == 0) 25240033Smckusick vgone(vp); 25337736Smckusick return (error); 25424Sbill } 25524Sbill 25624Sbill /* 25749451Smckusick * Update the access, modified, and inode change times as specified 25849451Smckusick * by the IACC, IMOD, and ICHG flags respectively. The IUPD flag 25949451Smckusick * is used to specify that the inode needs to be updated but that 26049451Smckusick * the times have already been set. The access and modified times 26149451Smckusick * are taken from the second and third parameters; the inode change 26249451Smckusick * time is always taken from the current time. If waitfor is set, 26349451Smckusick * then wait for the disk write of the inode to complete. 26439392Smckusick */ 265*51155Sbostic lfs_iupdat(ip, ta, tm, waitfor) 2664818Swnj register struct inode *ip; 2678630Sroot struct timeval *ta, *tm; 2684818Swnj int waitfor; 26924Sbill { 27037736Smckusick struct buf *bp; 27137736Smckusick struct vnode *vp = ITOV(ip); 27224Sbill struct dinode *dp; 273*51155Sbostic register struct lfs *fs; /* LFS */ 27437736Smckusick int error; 27524Sbill 276*51155Sbostic printf("lfs_iupdat: ino %d\n", ip->i_number); 277*51155Sbostic fs = ip->i_lfs; /* LFS */ 27837736Smckusick if ((ip->i_flag & (IUPD|IACC|ICHG|IMOD)) == 0) 27937736Smckusick return (0); 28041397Smckusick if (vp->v_mount->mnt_flag & MNT_RDONLY) 28137736Smckusick return (0); 28237736Smckusick error = bread(ip->i_devvp, fsbtodb(fs, itod(fs, ip->i_number)), 283*51155Sbostic (int)fs->lfs_bsize, NOCRED, &bp); 28437736Smckusick if (error) { 28537736Smckusick brelse(bp); 28637736Smckusick return (error); 28724Sbill } 28837736Smckusick if (ip->i_flag&IACC) 28937736Smckusick ip->i_atime = ta->tv_sec; 29037736Smckusick if (ip->i_flag&IUPD) 29137736Smckusick ip->i_mtime = tm->tv_sec; 29237736Smckusick if (ip->i_flag&ICHG) 29337736Smckusick ip->i_ctime = time.tv_sec; 294*51155Sbostic ip->i_flag &= ~(IUPD|IACC|ICHG|IMOD); /* LFS */ 295*51155Sbostic *lfs_ifind(fs, ip->i_number, bp->b_un.b_dino) = ip->i_din; 29637736Smckusick if (waitfor) { 29737736Smckusick return (bwrite(bp)); 29837736Smckusick } else { 29937736Smckusick bdwrite(bp); 30037736Smckusick return (0); 30137736Smckusick } 30224Sbill } 30324Sbill 30410736Ssam #define SINGLE 0 /* index of single indirect block */ 30510736Ssam #define DOUBLE 1 /* index of double indirect block */ 30610736Ssam #define TRIPLE 2 /* index of triple indirect block */ 30724Sbill /* 30839392Smckusick * Truncate the inode ip to at most length size. Free affected disk 30939392Smckusick * blocks -- the blocks of the file are removed in reverse order. 31010736Ssam * 31110736Ssam * NB: triple indirect blocks are untested. 31224Sbill */ 313*51155Sbostic lfs_itrunc(oip, length, flags) 31417942Smckusick register struct inode *oip; 3159165Ssam u_long length; 31639676Smckusick int flags; 31724Sbill { 318*51155Sbostic #ifdef NOTLFS /* LFS */ 3199165Ssam register daddr_t lastblock; 32026272Skarels daddr_t bn, lbn, lastiblock[NIADDR]; 3216569Smckusic register struct fs *fs; 32210736Ssam register struct inode *ip; 32317942Smckusick struct buf *bp; 32437736Smckusick int offset, osize, size, level; 32537736Smckusick long count, nblocks, blocksreleased = 0; 32617942Smckusick register int i; 32739676Smckusick int aflags, error, allerror; 32810736Ssam struct inode tip; 3299165Ssam 33045721Smckusick vnode_pager_setsize(ITOV(oip), length); 33113000Ssam if (oip->i_size <= length) { 33213000Ssam oip->i_flag |= ICHG|IUPD; 333*51155Sbostic error = lfs_iupdat(oip, &time, &time, 1); 33437736Smckusick return (error); 33513000Ssam } 3361203Sbill /* 33710736Ssam * Calculate index into inode's block list of 33810736Ssam * last direct and indirect blocks (if any) 33910736Ssam * which we want to keep. Lastblock is -1 when 34010736Ssam * the file is truncated to 0. 3411203Sbill */ 34210736Ssam fs = oip->i_fs; 3439165Ssam lastblock = lblkno(fs, length + fs->fs_bsize - 1) - 1; 34410736Ssam lastiblock[SINGLE] = lastblock - NDADDR; 34510736Ssam lastiblock[DOUBLE] = lastiblock[SINGLE] - NINDIR(fs); 34610736Ssam lastiblock[TRIPLE] = lastiblock[DOUBLE] - NINDIR(fs) * NINDIR(fs); 34712645Ssam nblocks = btodb(fs->fs_bsize); 3486569Smckusic /* 34917942Smckusick * Update the size of the file. If the file is not being 35017942Smckusick * truncated to a block boundry, the contents of the 35117942Smckusick * partial block following the end of the file must be 35217942Smckusick * zero'ed in case it ever become accessable again because 35317942Smckusick * of subsequent file growth. 35417942Smckusick */ 35517942Smckusick osize = oip->i_size; 35617942Smckusick offset = blkoff(fs, length); 35717942Smckusick if (offset == 0) { 35817942Smckusick oip->i_size = length; 35917942Smckusick } else { 36017942Smckusick lbn = lblkno(fs, length); 36139676Smckusick aflags = B_CLRBUF; 36239676Smckusick if (flags & IO_SYNC) 36339676Smckusick aflags |= B_SYNC; 36441313Smckusick #ifdef QUOTA 36541313Smckusick if (error = getinoquota(oip)) 36641313Smckusick return (error); 36741313Smckusick #endif 36839676Smckusick if (error = balloc(oip, lbn, offset, &bp, aflags)) 36937736Smckusick return (error); 37017942Smckusick oip->i_size = length; 371*51155Sbostic size = blksize(fs); /* LFS */ 37245721Smckusick (void) vnode_pager_uncache(ITOV(oip)); 37326272Skarels bzero(bp->b_un.b_addr + offset, (unsigned)(size - offset)); 37445112Smckusick allocbuf(bp, size); 37539676Smckusick if (flags & IO_SYNC) 37639676Smckusick bwrite(bp); 37739676Smckusick else 37839676Smckusick bdwrite(bp); 37917942Smckusick } 38017942Smckusick /* 38117942Smckusick * Update file and block pointers 38210736Ssam * on disk before we start freeing blocks. 38310736Ssam * If we crash before free'ing blocks below, 38410736Ssam * the blocks will be returned to the free list. 38510736Ssam * lastiblock values are also normalized to -1 38610736Ssam * for calls to indirtrunc below. 3876569Smckusic */ 38810736Ssam tip = *oip; 38917942Smckusick tip.i_size = osize; 39010736Ssam for (level = TRIPLE; level >= SINGLE; level--) 39110736Ssam if (lastiblock[level] < 0) { 39210736Ssam oip->i_ib[level] = 0; 39310736Ssam lastiblock[level] = -1; 3949165Ssam } 39510736Ssam for (i = NDADDR - 1; i > lastblock; i--) 39610736Ssam oip->i_db[i] = 0; 39710736Ssam oip->i_flag |= ICHG|IUPD; 39839676Smckusick vinvalbuf(ITOV(oip), (length > 0)); 399*51155Sbostic allerror = lfs_iupdat(oip, &time, &time, MNT_WAIT); 40010736Ssam 4016569Smckusic /* 40210736Ssam * Indirect blocks first. 4036569Smckusic */ 40417942Smckusick ip = &tip; 40510736Ssam for (level = TRIPLE; level >= SINGLE; level--) { 40610736Ssam bn = ip->i_ib[level]; 4079165Ssam if (bn != 0) { 40837736Smckusick error = indirtrunc(ip, bn, lastiblock[level], level, 40937736Smckusick &count); 41037736Smckusick if (error) 41137736Smckusick allerror = error; 41237736Smckusick blocksreleased += count; 41310736Ssam if (lastiblock[level] < 0) { 41410736Ssam ip->i_ib[level] = 0; 41531402Smckusick blkfree(ip, bn, (off_t)fs->fs_bsize); 41610736Ssam blocksreleased += nblocks; 41710736Ssam } 41810736Ssam } 41910736Ssam if (lastiblock[level] >= 0) 42010736Ssam goto done; 4219165Ssam } 42210736Ssam 4236569Smckusic /* 42410736Ssam * All whole direct blocks or frags. 4256569Smckusic */ 4269165Ssam for (i = NDADDR - 1; i > lastblock; i--) { 42726359Skarels register off_t bsize; 4289165Ssam 4296569Smckusic bn = ip->i_db[i]; 4309165Ssam if (bn == 0) 43124Sbill continue; 4329165Ssam ip->i_db[i] = 0; 433*51155Sbostic bsize = (off_t)blksize(fs); /* LFS */ 43431402Smckusick blkfree(ip, bn, bsize); 43524525Sbloom blocksreleased += btodb(bsize); 43624Sbill } 43710736Ssam if (lastblock < 0) 43810736Ssam goto done; 43910736Ssam 4401203Sbill /* 4419165Ssam * Finally, look for a change in size of the 4429165Ssam * last direct block; release any frags. 4431203Sbill */ 44410736Ssam bn = ip->i_db[lastblock]; 44510736Ssam if (bn != 0) { 44626359Skarels off_t oldspace, newspace; 44710736Ssam 4489165Ssam /* 4499165Ssam * Calculate amount of space we're giving 4509165Ssam * back as old block size minus new block size. 4519165Ssam */ 452*51155Sbostic oldspace = blksize(fs); /* LFS */ 4539165Ssam ip->i_size = length; 454*51155Sbostic newspace = blksize(fs); /* LFS */ 45510736Ssam if (newspace == 0) 456*51155Sbostic panic("lfs_itrunc: newspace"); 45710736Ssam if (oldspace - newspace > 0) { 4589165Ssam /* 4599165Ssam * Block number of space to be free'd is 4609165Ssam * the old block # plus the number of frags 4619165Ssam * required for the storage we're keeping. 4629165Ssam */ 46310736Ssam bn += numfrags(fs, newspace); 46431402Smckusick blkfree(ip, bn, oldspace - newspace); 46512645Ssam blocksreleased += btodb(oldspace - newspace); 4669165Ssam } 4679165Ssam } 4689165Ssam done: 46910736Ssam /* BEGIN PARANOIA */ 47010736Ssam for (level = SINGLE; level <= TRIPLE; level++) 47110736Ssam if (ip->i_ib[level] != oip->i_ib[level]) 472*51155Sbostic panic("lfs_itrunc1"); 47310736Ssam for (i = 0; i < NDADDR; i++) 47410736Ssam if (ip->i_db[i] != oip->i_db[i]) 475*51155Sbostic panic("lfs_itrunc2"); 47610736Ssam /* END PARANOIA */ 47712645Ssam oip->i_blocks -= blocksreleased; 47812645Ssam if (oip->i_blocks < 0) /* sanity */ 47912645Ssam oip->i_blocks = 0; 48012645Ssam oip->i_flag |= ICHG; 4819165Ssam #ifdef QUOTA 48241313Smckusick if (!getinoquota(oip)) 48341313Smckusick (void) chkdq(oip, -blocksreleased, NOCRED, 0); 4849165Ssam #endif 48537736Smckusick return (allerror); 486*51155Sbostic #else 487*51155Sbostic /* LFS IMPLEMENT -- lfs_itrunc */ 488*51155Sbostic panic("lfs_itrunc not implemented"); 489*51155Sbostic #endif 49024Sbill } 49124Sbill 4929165Ssam /* 4939165Ssam * Release blocks associated with the inode ip and 4949165Ssam * stored in the indirect block bn. Blocks are free'd 4959165Ssam * in LIFO order up to (but not including) lastbn. If 49610736Ssam * level is greater than SINGLE, the block is an indirect 49710736Ssam * block and recursive calls to indirtrunc must be used to 49810736Ssam * cleanse other indirect blocks. 49910736Ssam * 50010736Ssam * NB: triple indirect blocks are untested. 5019165Ssam */ 502*51155Sbostic lfs_indirtrunc(ip, bn, lastbn, level, countp) 5036569Smckusic register struct inode *ip; 5049165Ssam daddr_t bn, lastbn; 50510736Ssam int level; 50637736Smckusick long *countp; 50724Sbill { 508*51155Sbostic #ifdef NOTLFS 5099165Ssam register int i; 51031661Smckusick struct buf *bp; 51131661Smckusick register struct fs *fs = ip->i_fs; 51224Sbill register daddr_t *bap; 51331661Smckusick daddr_t *copy, nb, last; 51437736Smckusick long blkcount, factor; 51537736Smckusick int nblocks, blocksreleased = 0; 51637736Smckusick int error, allerror = 0; 51724Sbill 51810736Ssam /* 51910736Ssam * Calculate index in current block of last 52010736Ssam * block to be kept. -1 indicates the entire 52110736Ssam * block so we need not calculate the index. 52210736Ssam */ 52310736Ssam factor = 1; 52410736Ssam for (i = SINGLE; i < level; i++) 52510736Ssam factor *= NINDIR(fs); 5269165Ssam last = lastbn; 52710736Ssam if (lastbn > 0) 52810736Ssam last /= factor; 52912645Ssam nblocks = btodb(fs->fs_bsize); 53010736Ssam /* 53110736Ssam * Get buffer of block pointers, zero those 53210736Ssam * entries corresponding to blocks to be free'd, 53310736Ssam * and update on disk copy first. 53410736Ssam */ 53538776Smckusick error = bread(ip->i_devvp, fsbtodb(fs, bn), (int)fs->fs_bsize, 53638776Smckusick NOCRED, &bp); 53737736Smckusick if (error) { 53810736Ssam brelse(bp); 53937736Smckusick *countp = 0; 54037736Smckusick return (error); 54110736Ssam } 54210736Ssam bap = bp->b_un.b_daddr; 54331661Smckusick MALLOC(copy, daddr_t *, fs->fs_bsize, M_TEMP, M_WAITOK); 54431661Smckusick bcopy((caddr_t)bap, (caddr_t)copy, (u_int)fs->fs_bsize); 54510736Ssam bzero((caddr_t)&bap[last + 1], 54610736Ssam (u_int)(NINDIR(fs) - (last + 1)) * sizeof (daddr_t)); 54739676Smckusick if (last == -1) 54839676Smckusick bp->b_flags |= B_INVAL; 54937736Smckusick error = bwrite(bp); 55037736Smckusick if (error) 55137736Smckusick allerror = error; 55231661Smckusick bap = copy; 55310736Ssam 55410736Ssam /* 55510736Ssam * Recursively free totally unused blocks. 55610736Ssam */ 5579165Ssam for (i = NINDIR(fs) - 1; i > last; i--) { 55824Sbill nb = bap[i]; 5599165Ssam if (nb == 0) 56024Sbill continue; 56137736Smckusick if (level > SINGLE) { 56237736Smckusick error = indirtrunc(ip, nb, (daddr_t)-1, level - 1, 56337736Smckusick &blkcount); 56437736Smckusick if (error) 56537736Smckusick allerror = error; 56637736Smckusick blocksreleased += blkcount; 56737736Smckusick } 56831402Smckusick blkfree(ip, nb, (off_t)fs->fs_bsize); 5699165Ssam blocksreleased += nblocks; 57024Sbill } 57110736Ssam 57210736Ssam /* 57310736Ssam * Recursively free last partial block. 57410736Ssam */ 57510736Ssam if (level > SINGLE && lastbn >= 0) { 57610736Ssam last = lastbn % factor; 5779165Ssam nb = bap[i]; 57837736Smckusick if (nb != 0) { 57937736Smckusick error = indirtrunc(ip, nb, last, level - 1, &blkcount); 58037736Smckusick if (error) 58137736Smckusick allerror = error; 58237736Smckusick blocksreleased += blkcount; 58337736Smckusick } 5849165Ssam } 58531661Smckusick FREE(copy, M_TEMP); 58637736Smckusick *countp = blocksreleased; 58737736Smckusick return (allerror); 588*51155Sbostic #else 589*51155Sbostic /* LFS IMPLEMENT -- lfs_indirtrunc */ 590*51155Sbostic panic("lfs_indirtrunc not implemented"); 591*51155Sbostic #endif 59224Sbill } 593