123399Smckusick /* 237736Smckusick * Copyright (c) 1982, 1986, 1989 Regents of the University of California. 337736Smckusick * All rights reserved. 423399Smckusick * 537736Smckusick * Redistribution and use in source and binary forms are permitted 637736Smckusick * provided that the above copyright notice and this paragraph are 737736Smckusick * duplicated in all such forms and that any documentation, 837736Smckusick * advertising materials, and other materials related to such 937736Smckusick * distribution and use acknowledge that the software was developed 1037736Smckusick * by the University of California, Berkeley. The name of the 1137736Smckusick * University may not be used to endorse or promote products derived 1237736Smckusick * from this software without specific prior written permission. 1337736Smckusick * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 1437736Smckusick * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 1537736Smckusick * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 1637736Smckusick * 17*39517Smckusick * @(#)ufs_inode.c 7.19 (Berkeley) 11/12/89 1823399Smckusick */ 1924Sbill 2017099Sbloom #include "param.h" 2117099Sbloom #include "systm.h" 2217099Sbloom #include "mount.h" 2317099Sbloom #include "user.h" 2437736Smckusick #include "file.h" 2517099Sbloom #include "buf.h" 2624525Sbloom #include "cmap.h" 2737736Smckusick #include "vnode.h" 2837736Smckusick #include "../ufs/inode.h" 2937736Smckusick #include "../ufs/fs.h" 3037736Smckusick #include "../ufs/ufsmount.h" 317651Ssam #ifdef QUOTA 3237736Smckusick #include "../ufs/quota.h" 337504Sroot #endif 3417099Sbloom #include "kernel.h" 3531661Smckusick #include "malloc.h" 3624Sbill 3716840Smckusick #define INOHSZ 512 387334Skre #if ((INOHSZ&(INOHSZ-1)) == 0) 397334Skre #define INOHASH(dev,ino) (((dev)+(ino))&(INOHSZ-1)) 407334Skre #else 4110852Ssam #define INOHASH(dev,ino) (((unsigned)((dev)+(ino)))%INOHSZ) 427334Skre #endif 4324Sbill 4439392Smckusick union ihead { 457334Skre union ihead *ih_head[2]; 467334Skre struct inode *ih_chain[2]; 477334Skre } ihead[INOHSZ]; 487334Skre 4924Sbill /* 5039392Smckusick * Initialize hash links for inodes. 5124Sbill */ 5239440Smckusick ufs_init() 5324Sbill { 5424Sbill register int i; 5539392Smckusick register union ihead *ih = ihead; 5624Sbill 5739492Smckusick #ifndef lint 5839392Smckusick if (VN_MAXPRIVATE < sizeof(struct inode)) 5939392Smckusick panic("ihinit: too small"); 6039492Smckusick #endif /* not lint */ 617334Skre for (i = INOHSZ; --i >= 0; ih++) { 627334Skre ih->ih_head[0] = ih; 637334Skre ih->ih_head[1] = ih; 647334Skre } 6524Sbill } 6624Sbill 6724Sbill /* 6837736Smckusick * Look up an vnode/inode by device,inumber. 6924Sbill * If it is in core (in the inode structure), 7024Sbill * honor the locking protocol. 7124Sbill * If it is not in core, read it in from the 7224Sbill * specified device. 7337736Smckusick * Callers must check for mount points!! 7424Sbill * In all cases, a pointer to a locked 7524Sbill * inode structure is returned. 7624Sbill */ 7737736Smckusick iget(xp, ino, ipp) 7837736Smckusick struct inode *xp; 794818Swnj ino_t ino; 8037736Smckusick struct inode **ipp; 8124Sbill { 8237736Smckusick dev_t dev = xp->i_dev; 8337736Smckusick struct mount *mntp = ITOV(xp)->v_mount; 8437736Smckusick register struct fs *fs = VFSTOUFS(mntp)->um_fs; 8539440Smckusick extern struct vnodeops ufs_vnodeops, spec_inodeops; 8637736Smckusick register struct inode *ip, *iq; 8737736Smckusick register struct vnode *vp; 8839440Smckusick struct vnode *nvp; 8937736Smckusick struct buf *bp; 9039440Smckusick struct dinode *dp; 9137736Smckusick union ihead *ih; 9237736Smckusick int error; 9324Sbill 9439440Smckusick ih = &ihead[INOHASH(dev, ino)]; 9524Sbill loop: 9639392Smckusick for (ip = ih->ih_chain[0]; ip != (struct inode *)ih; ip = ip->i_forw) { 9739392Smckusick if (ino != ip->i_number || dev != ip->i_dev) 9839392Smckusick continue; 9939392Smckusick if ((ip->i_flag&ILOCKED) != 0) { 10039392Smckusick ip->i_flag |= IWANT; 10139392Smckusick sleep((caddr_t)ip, PINOD); 10239392Smckusick goto loop; 10339392Smckusick } 10439440Smckusick if (vget(ITOV(ip))) 10539440Smckusick goto loop; 10639392Smckusick *ipp = ip; 10739392Smckusick return(0); 10839392Smckusick } 10939440Smckusick /* 11039440Smckusick * Allocate a new inode. 11139440Smckusick */ 11239440Smckusick if (error = getnewvnode(VT_UFS, mntp, &ufs_vnodeops, &nvp)) { 11337736Smckusick *ipp = 0; 11437736Smckusick return (error); 11537736Smckusick } 11639440Smckusick ip = VTOI(nvp); 11739440Smckusick ip->i_vnode = nvp; 11839440Smckusick ip->i_flag = 0; 11939440Smckusick ip->i_devvp = 0; 12039440Smckusick ip->i_lastr = 0; 12139440Smckusick ip->i_mode = 0; 12239440Smckusick #ifdef QUOTA 12339440Smckusick ip->i_dquot = NODQUOT; 12439440Smckusick #endif 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 ip->i_dev = dev; 13239440Smckusick ip->i_number = ino; 13339440Smckusick insque(ip, ih); 13439440Smckusick ILOCK(ip); 13539440Smckusick /* 13637736Smckusick * Read in the disk contents for the inode. 13737736Smckusick */ 13837736Smckusick if (error = bread(VFSTOUFS(mntp)->um_devvp, fsbtodb(fs, itod(fs, ino)), 13938776Smckusick (int)fs->fs_bsize, NOCRED, &bp)) { 14037736Smckusick /* 14139392Smckusick * Unlock and discard unneeded inode. 14237736Smckusick */ 14339440Smckusick iput(ip); 14437736Smckusick brelse(bp); 14537736Smckusick *ipp = 0; 14639440Smckusick return (error); 14737736Smckusick } 14839440Smckusick dp = bp->b_un.b_dino; 14939440Smckusick dp += itoo(fs, ino); 15039440Smckusick ip->i_din = *dp; 15139440Smckusick brelse(bp); 15237736Smckusick /* 15339440Smckusick * Initialize the associated vnode 15437736Smckusick */ 15539440Smckusick vp = ITOV(ip); 15639440Smckusick vp->v_type = IFTOVT(ip->i_mode); 15739440Smckusick if (vp->v_type == VCHR || vp->v_type == VBLK) { 15839440Smckusick vp->v_rdev = ip->i_rdev; 15939440Smckusick vp->v_op = &spec_inodeops; 16039440Smckusick if (nvp = checkalias(vp, mntp)) { 16137736Smckusick /* 16239440Smckusick * Reinitialize aliased inode. 16337736Smckusick */ 16439440Smckusick vp = nvp; 16539440Smckusick iq = VTOI(vp); 16639440Smckusick iq->i_vnode = vp; 16739440Smckusick iq->i_lastr = 0; 168*39517Smckusick iq->i_flag = 0; 16939440Smckusick ILOCK(iq); 17039440Smckusick iq->i_din = ip->i_din; 17139440Smckusick iq->i_dev = dev; 17239440Smckusick iq->i_number = ino; 17339440Smckusick insque(iq, ih); 17437736Smckusick /* 17539440Smckusick * Discard unneeded vnode 17637736Smckusick */ 17739440Smckusick ip->i_mode = 0; 17839440Smckusick iput(ip); 17937736Smckusick ip = iq; 18037736Smckusick } 18137736Smckusick } 18239440Smckusick if (ino == ROOTINO) 18339440Smckusick vp->v_flag |= VROOT; 18437736Smckusick /* 18537736Smckusick * Finish inode initialization. 18637736Smckusick */ 18737736Smckusick ip->i_fs = fs; 18837736Smckusick ip->i_devvp = VFSTOUFS(mntp)->um_devvp; 18938345Smckusick VREF(ip->i_devvp); 19037736Smckusick #ifdef QUOTA 19137736Smckusick if (ip->i_mode != 0) 19237736Smckusick ip->i_dquot = inoquota(ip); 19337736Smckusick #endif 19438256Smckusick /* 19538256Smckusick * Set up a generation number for this inode if it does not 19638256Smckusick * already have one. This should only happen on old filesystems. 19738256Smckusick */ 19838256Smckusick if (ip->i_gen == 0) { 19938256Smckusick if (++nextgennumber < (u_long)time.tv_sec) 20038256Smckusick nextgennumber = time.tv_sec; 20138256Smckusick ip->i_gen = nextgennumber; 20238256Smckusick if ((vp->v_mount->m_flag & M_RDONLY) == 0) 20338256Smckusick ip->i_flag |= IMOD; 20438256Smckusick } 20537736Smckusick *ipp = ip; 20637736Smckusick return (0); 20737736Smckusick } 2087334Skre 20937736Smckusick /* 21039392Smckusick * Unlock and decrement the reference count of an inode structure. 21124Sbill */ 21224Sbill iput(ip) 2134818Swnj register struct inode *ip; 21424Sbill { 2157118Smckusick 2168452Sroot if ((ip->i_flag & ILOCKED) == 0) 2177118Smckusick panic("iput"); 21816665Smckusick IUNLOCK(ip); 21937736Smckusick vrele(ITOV(ip)); 2207118Smckusick } 2217118Smckusick 22239392Smckusick /* 22339392Smckusick * Last reference to an inode, write the inode out and if necessary, 22439392Smckusick * truncate and deallocate the file. 22539392Smckusick */ 22637736Smckusick ufs_inactive(vp) 22737736Smckusick struct vnode *vp; 2287118Smckusick { 22937736Smckusick register struct inode *ip = VTOI(vp); 23039392Smckusick int mode, error = 0; 23124Sbill 23239364Smckusick if (vp->v_count != 0) 23339440Smckusick printf("ufs_inactive: pushing active ino %d dev 0x%x\n", 23439440Smckusick ip->i_number, ip->i_dev); 23538452Smckusick /* 23638452Smckusick * Get rid of inodes related to stale file handles. 23738452Smckusick */ 23839440Smckusick if (ip->i_mode == 0) { 23939440Smckusick vgone(vp); 24039440Smckusick return (0); 24139440Smckusick } 24238226Smckusick ILOCK(ip); 24339364Smckusick if (ip->i_nlink <= 0 && (vp->v_mount->m_flag & M_RDONLY) == 0) { 24437736Smckusick error = itrunc(ip, (u_long)0); 24537736Smckusick mode = ip->i_mode; 24637736Smckusick ip->i_mode = 0; 24737736Smckusick ip->i_rdev = 0; 24837736Smckusick ip->i_flag |= IUPD|ICHG; 24937736Smckusick ifree(ip, ip->i_number, mode); 2507651Ssam #ifdef QUOTA 25137736Smckusick (void) chkiq(ip->i_dev, ip, ip->i_uid, 0); 25237736Smckusick dqrele(ip->i_dquot); 25337736Smckusick ip->i_dquot = NODQUOT; 2547492Skre #endif 25537736Smckusick } 25637736Smckusick IUPDAT(ip, &time, &time, 0); 25737736Smckusick IUNLOCK(ip); 25837736Smckusick ip->i_flag = 0; 25937736Smckusick /* 26039392Smckusick * If we are done with the inode, reclaim it 26139392Smckusick * so that it can be reused immediately. 26237736Smckusick */ 26339440Smckusick if (vp->v_count == 0 && ip->i_mode == 0) 26439440Smckusick vgone(vp); 26537736Smckusick return (error); 26624Sbill } 26724Sbill 26824Sbill /* 26939392Smckusick * Reclaim an inode so that it can be used for other purposes. 27024Sbill */ 27139392Smckusick ufs_reclaim(vp) 27239392Smckusick register struct vnode *vp; 27339392Smckusick { 27439492Smckusick register struct inode *ip = VTOI(vp); 27539392Smckusick 27639392Smckusick if (vp->v_count != 0) 27739440Smckusick printf("ufs_reclaim: pushing active ino %d dev 0x%x\n", 27839440Smckusick ip->i_number, ip->i_dev); 27939392Smckusick /* 28039392Smckusick * Remove the inode from its hash chain. 28139392Smckusick */ 28239392Smckusick remque(ip); 28339392Smckusick ip->i_forw = ip; 28439392Smckusick ip->i_back = ip; 28539392Smckusick /* 28639392Smckusick * Purge old data structures associated with the inode. 28739392Smckusick */ 28839392Smckusick cache_purge(vp); 28939392Smckusick if (ip->i_devvp) { 29039392Smckusick vrele(ip->i_devvp); 29139392Smckusick ip->i_devvp = 0; 29239392Smckusick } 29339392Smckusick #ifdef QUOTA 29439392Smckusick dqrele(ip->i_dquot); 29539392Smckusick ip->i_dquot = NODQUOT; 29639392Smckusick #endif 29739392Smckusick ip->i_flag = 0; 29839392Smckusick return (0); 29939392Smckusick } 30039392Smckusick 30139392Smckusick /* 30239392Smckusick * Check accessed and update flags on an inode structure. 30339392Smckusick * If any is on, update the inode with the current time. 30439392Smckusick * If waitfor is given, then must ensure I/O order, 30539392Smckusick * so wait for write to complete. 30639392Smckusick */ 3071203Sbill iupdat(ip, ta, tm, waitfor) 3084818Swnj register struct inode *ip; 3098630Sroot struct timeval *ta, *tm; 3104818Swnj int waitfor; 31124Sbill { 31237736Smckusick struct buf *bp; 31337736Smckusick struct vnode *vp = ITOV(ip); 31424Sbill struct dinode *dp; 31530749Skarels register struct fs *fs; 31637736Smckusick int error; 31724Sbill 31830749Skarels fs = ip->i_fs; 31937736Smckusick if ((ip->i_flag & (IUPD|IACC|ICHG|IMOD)) == 0) 32037736Smckusick return (0); 32137736Smckusick if (vp->v_mount->m_flag & M_RDONLY) 32237736Smckusick return (0); 32337736Smckusick error = bread(ip->i_devvp, fsbtodb(fs, itod(fs, ip->i_number)), 32438776Smckusick (int)fs->fs_bsize, NOCRED, &bp); 32537736Smckusick if (error) { 32637736Smckusick brelse(bp); 32737736Smckusick return (error); 32824Sbill } 32937736Smckusick if (ip->i_flag&IACC) 33037736Smckusick ip->i_atime = ta->tv_sec; 33137736Smckusick if (ip->i_flag&IUPD) 33237736Smckusick ip->i_mtime = tm->tv_sec; 33337736Smckusick if (ip->i_flag&ICHG) 33437736Smckusick ip->i_ctime = time.tv_sec; 33537736Smckusick ip->i_flag &= ~(IUPD|IACC|ICHG|IMOD); 33637736Smckusick dp = bp->b_un.b_dino + itoo(fs, ip->i_number); 33739392Smckusick *dp = ip->i_din; 33837736Smckusick if (waitfor) { 33937736Smckusick return (bwrite(bp)); 34037736Smckusick } else { 34137736Smckusick bdwrite(bp); 34237736Smckusick return (0); 34337736Smckusick } 34424Sbill } 34524Sbill 34610736Ssam #define SINGLE 0 /* index of single indirect block */ 34710736Ssam #define DOUBLE 1 /* index of double indirect block */ 34810736Ssam #define TRIPLE 2 /* index of triple indirect block */ 34924Sbill /* 35039392Smckusick * Truncate the inode ip to at most length size. Free affected disk 35139392Smckusick * blocks -- the blocks of the file are removed in reverse order. 35210736Ssam * 35310736Ssam * NB: triple indirect blocks are untested. 35424Sbill */ 35510736Ssam itrunc(oip, length) 35617942Smckusick register struct inode *oip; 3579165Ssam u_long length; 35824Sbill { 3599165Ssam register daddr_t lastblock; 36026272Skarels daddr_t bn, lbn, lastiblock[NIADDR]; 3616569Smckusic register struct fs *fs; 36210736Ssam register struct inode *ip; 36317942Smckusick struct buf *bp; 36437736Smckusick int offset, osize, size, level; 36537736Smckusick long count, nblocks, blocksreleased = 0; 36617942Smckusick register int i; 36737736Smckusick int error, allerror = 0; 36810736Ssam struct inode tip; 3699165Ssam 37013000Ssam if (oip->i_size <= length) { 37113000Ssam oip->i_flag |= ICHG|IUPD; 37237736Smckusick error = iupdat(oip, &time, &time, 1); 37337736Smckusick return (error); 37413000Ssam } 3751203Sbill /* 37610736Ssam * Calculate index into inode's block list of 37710736Ssam * last direct and indirect blocks (if any) 37810736Ssam * which we want to keep. Lastblock is -1 when 37910736Ssam * the file is truncated to 0. 3801203Sbill */ 38110736Ssam fs = oip->i_fs; 3829165Ssam lastblock = lblkno(fs, length + fs->fs_bsize - 1) - 1; 38310736Ssam lastiblock[SINGLE] = lastblock - NDADDR; 38410736Ssam lastiblock[DOUBLE] = lastiblock[SINGLE] - NINDIR(fs); 38510736Ssam lastiblock[TRIPLE] = lastiblock[DOUBLE] - NINDIR(fs) * NINDIR(fs); 38612645Ssam nblocks = btodb(fs->fs_bsize); 3876569Smckusic /* 38817942Smckusick * Update the size of the file. If the file is not being 38917942Smckusick * truncated to a block boundry, the contents of the 39017942Smckusick * partial block following the end of the file must be 39117942Smckusick * zero'ed in case it ever become accessable again because 39217942Smckusick * of subsequent file growth. 39317942Smckusick */ 39417942Smckusick osize = oip->i_size; 39517942Smckusick offset = blkoff(fs, length); 39617942Smckusick if (offset == 0) { 39717942Smckusick oip->i_size = length; 39817942Smckusick } else { 39917942Smckusick lbn = lblkno(fs, length); 40037736Smckusick error = balloc(oip, lbn, offset, &bn, B_CLRBUF); 40137736Smckusick if (error) 40237736Smckusick return (error); 40337736Smckusick if ((long)bn < 0) 40437736Smckusick panic("itrunc: hole"); 40517942Smckusick oip->i_size = length; 40617942Smckusick size = blksize(fs, oip, lbn); 40730749Skarels count = howmany(size, CLBYTES); 40830749Skarels for (i = 0; i < count; i++) 40937736Smckusick munhash(oip->i_devvp, bn + i * CLBYTES / DEV_BSIZE); 41038776Smckusick error = bread(oip->i_devvp, bn, size, NOCRED, &bp); 41137736Smckusick if (error) { 41217942Smckusick oip->i_size = osize; 41317942Smckusick brelse(bp); 41437736Smckusick return (error); 41517942Smckusick } 41626272Skarels bzero(bp->b_un.b_addr + offset, (unsigned)(size - offset)); 41717942Smckusick bdwrite(bp); 41817942Smckusick } 41917942Smckusick /* 42017942Smckusick * Update file and block pointers 42110736Ssam * on disk before we start freeing blocks. 42210736Ssam * If we crash before free'ing blocks below, 42310736Ssam * the blocks will be returned to the free list. 42410736Ssam * lastiblock values are also normalized to -1 42510736Ssam * for calls to indirtrunc below. 4266569Smckusic */ 42710736Ssam tip = *oip; 42817942Smckusick tip.i_size = osize; 42910736Ssam for (level = TRIPLE; level >= SINGLE; level--) 43010736Ssam if (lastiblock[level] < 0) { 43110736Ssam oip->i_ib[level] = 0; 43210736Ssam lastiblock[level] = -1; 4339165Ssam } 43410736Ssam for (i = NDADDR - 1; i > lastblock; i--) 43510736Ssam oip->i_db[i] = 0; 43610736Ssam oip->i_flag |= ICHG|IUPD; 43737736Smckusick allerror = syncip(oip); 43810736Ssam 4396569Smckusic /* 44010736Ssam * Indirect blocks first. 4416569Smckusic */ 44217942Smckusick ip = &tip; 44310736Ssam for (level = TRIPLE; level >= SINGLE; level--) { 44410736Ssam bn = ip->i_ib[level]; 4459165Ssam if (bn != 0) { 44637736Smckusick error = indirtrunc(ip, bn, lastiblock[level], level, 44737736Smckusick &count); 44837736Smckusick if (error) 44937736Smckusick allerror = error; 45037736Smckusick blocksreleased += count; 45110736Ssam if (lastiblock[level] < 0) { 45210736Ssam ip->i_ib[level] = 0; 45331402Smckusick blkfree(ip, bn, (off_t)fs->fs_bsize); 45410736Ssam blocksreleased += nblocks; 45510736Ssam } 45610736Ssam } 45710736Ssam if (lastiblock[level] >= 0) 45810736Ssam goto done; 4599165Ssam } 46010736Ssam 4616569Smckusic /* 46210736Ssam * All whole direct blocks or frags. 4636569Smckusic */ 4649165Ssam for (i = NDADDR - 1; i > lastblock; i--) { 46526359Skarels register off_t bsize; 4669165Ssam 4676569Smckusic bn = ip->i_db[i]; 4689165Ssam if (bn == 0) 46924Sbill continue; 4709165Ssam ip->i_db[i] = 0; 47124525Sbloom bsize = (off_t)blksize(fs, ip, i); 47231402Smckusick blkfree(ip, bn, bsize); 47324525Sbloom blocksreleased += btodb(bsize); 47424Sbill } 47510736Ssam if (lastblock < 0) 47610736Ssam goto done; 47710736Ssam 4781203Sbill /* 4799165Ssam * Finally, look for a change in size of the 4809165Ssam * last direct block; release any frags. 4811203Sbill */ 48210736Ssam bn = ip->i_db[lastblock]; 48310736Ssam if (bn != 0) { 48426359Skarels off_t oldspace, newspace; 48510736Ssam 4869165Ssam /* 4879165Ssam * Calculate amount of space we're giving 4889165Ssam * back as old block size minus new block size. 4899165Ssam */ 49010736Ssam oldspace = blksize(fs, ip, lastblock); 4919165Ssam ip->i_size = length; 49210736Ssam newspace = blksize(fs, ip, lastblock); 49310736Ssam if (newspace == 0) 49410736Ssam panic("itrunc: newspace"); 49510736Ssam if (oldspace - newspace > 0) { 4969165Ssam /* 4979165Ssam * Block number of space to be free'd is 4989165Ssam * the old block # plus the number of frags 4999165Ssam * required for the storage we're keeping. 5009165Ssam */ 50110736Ssam bn += numfrags(fs, newspace); 50231402Smckusick blkfree(ip, bn, oldspace - newspace); 50312645Ssam blocksreleased += btodb(oldspace - newspace); 5049165Ssam } 5059165Ssam } 5069165Ssam done: 50710736Ssam /* BEGIN PARANOIA */ 50810736Ssam for (level = SINGLE; level <= TRIPLE; level++) 50910736Ssam if (ip->i_ib[level] != oip->i_ib[level]) 51010736Ssam panic("itrunc1"); 51110736Ssam for (i = 0; i < NDADDR; i++) 51210736Ssam if (ip->i_db[i] != oip->i_db[i]) 51310736Ssam panic("itrunc2"); 51410736Ssam /* END PARANOIA */ 51512645Ssam oip->i_blocks -= blocksreleased; 51612645Ssam if (oip->i_blocks < 0) /* sanity */ 51712645Ssam oip->i_blocks = 0; 51812645Ssam oip->i_flag |= ICHG; 5199165Ssam #ifdef QUOTA 52012645Ssam (void) chkdq(oip, -blocksreleased, 0); 5219165Ssam #endif 52237736Smckusick return (allerror); 52324Sbill } 52424Sbill 5259165Ssam /* 5269165Ssam * Release blocks associated with the inode ip and 5279165Ssam * stored in the indirect block bn. Blocks are free'd 5289165Ssam * in LIFO order up to (but not including) lastbn. If 52910736Ssam * level is greater than SINGLE, the block is an indirect 53010736Ssam * block and recursive calls to indirtrunc must be used to 53110736Ssam * cleanse other indirect blocks. 53210736Ssam * 53310736Ssam * NB: triple indirect blocks are untested. 5349165Ssam */ 53537736Smckusick indirtrunc(ip, bn, lastbn, level, countp) 5366569Smckusic register struct inode *ip; 5379165Ssam daddr_t bn, lastbn; 53810736Ssam int level; 53937736Smckusick long *countp; 54024Sbill { 5419165Ssam register int i; 54231661Smckusick struct buf *bp; 54331661Smckusick register struct fs *fs = ip->i_fs; 54424Sbill register daddr_t *bap; 54531661Smckusick daddr_t *copy, nb, last; 54637736Smckusick long blkcount, factor; 54737736Smckusick int nblocks, blocksreleased = 0; 54837736Smckusick int error, allerror = 0; 54924Sbill 55010736Ssam /* 55110736Ssam * Calculate index in current block of last 55210736Ssam * block to be kept. -1 indicates the entire 55310736Ssam * block so we need not calculate the index. 55410736Ssam */ 55510736Ssam factor = 1; 55610736Ssam for (i = SINGLE; i < level; i++) 55710736Ssam factor *= NINDIR(fs); 5589165Ssam last = lastbn; 55910736Ssam if (lastbn > 0) 56010736Ssam last /= factor; 56112645Ssam nblocks = btodb(fs->fs_bsize); 56210736Ssam /* 56310736Ssam * Get buffer of block pointers, zero those 56410736Ssam * entries corresponding to blocks to be free'd, 56510736Ssam * and update on disk copy first. 56610736Ssam */ 56738776Smckusick error = bread(ip->i_devvp, fsbtodb(fs, bn), (int)fs->fs_bsize, 56838776Smckusick NOCRED, &bp); 56937736Smckusick if (error) { 57010736Ssam brelse(bp); 57137736Smckusick *countp = 0; 57237736Smckusick return (error); 57310736Ssam } 57410736Ssam bap = bp->b_un.b_daddr; 57531661Smckusick MALLOC(copy, daddr_t *, fs->fs_bsize, M_TEMP, M_WAITOK); 57631661Smckusick bcopy((caddr_t)bap, (caddr_t)copy, (u_int)fs->fs_bsize); 57710736Ssam bzero((caddr_t)&bap[last + 1], 57810736Ssam (u_int)(NINDIR(fs) - (last + 1)) * sizeof (daddr_t)); 57937736Smckusick error = bwrite(bp); 58037736Smckusick if (error) 58137736Smckusick allerror = error; 58231661Smckusick bap = copy; 58310736Ssam 58410736Ssam /* 58510736Ssam * Recursively free totally unused blocks. 58610736Ssam */ 5879165Ssam for (i = NINDIR(fs) - 1; i > last; i--) { 58824Sbill nb = bap[i]; 5899165Ssam if (nb == 0) 59024Sbill continue; 59137736Smckusick if (level > SINGLE) { 59237736Smckusick error = indirtrunc(ip, nb, (daddr_t)-1, level - 1, 59337736Smckusick &blkcount); 59437736Smckusick if (error) 59537736Smckusick allerror = error; 59637736Smckusick blocksreleased += blkcount; 59737736Smckusick } 59831402Smckusick blkfree(ip, nb, (off_t)fs->fs_bsize); 5999165Ssam blocksreleased += nblocks; 60024Sbill } 60110736Ssam 60210736Ssam /* 60310736Ssam * Recursively free last partial block. 60410736Ssam */ 60510736Ssam if (level > SINGLE && lastbn >= 0) { 60610736Ssam last = lastbn % factor; 6079165Ssam nb = bap[i]; 60837736Smckusick if (nb != 0) { 60937736Smckusick error = indirtrunc(ip, nb, last, level - 1, &blkcount); 61037736Smckusick if (error) 61137736Smckusick allerror = error; 61237736Smckusick blocksreleased += blkcount; 61337736Smckusick } 6149165Ssam } 61531661Smckusick FREE(copy, M_TEMP); 61637736Smckusick *countp = blocksreleased; 61737736Smckusick return (allerror); 61824Sbill } 61924Sbill 62024Sbill /* 6214818Swnj * Lock an inode. If its already locked, set the WANT bit and sleep. 6223617Sroot */ 6234818Swnj ilock(ip) 6244818Swnj register struct inode *ip; 6253617Sroot { 6263617Sroot 62737736Smckusick while (ip->i_flag & ILOCKED) { 62837736Smckusick ip->i_flag |= IWANT; 62937736Smckusick (void) sleep((caddr_t)ip, PINOD); 63037736Smckusick } 63137736Smckusick ip->i_flag |= ILOCKED; 6323617Sroot } 6333617Sroot 6343617Sroot /* 6354818Swnj * Unlock an inode. If WANT bit is on, wakeup. 6363617Sroot */ 6377118Smckusick iunlock(ip) 6384818Swnj register struct inode *ip; 6393617Sroot { 6403617Sroot 64137736Smckusick if ((ip->i_flag & ILOCKED) == 0) 64237736Smckusick printf("unlocking unlocked inode %d on dev 0x%x\n", 64337736Smckusick ip->i_number, ip->i_dev); 64437736Smckusick ip->i_flag &= ~ILOCKED; 64537736Smckusick if (ip->i_flag&IWANT) { 64637736Smckusick ip->i_flag &= ~IWANT; 64737736Smckusick wakeup((caddr_t)ip); 64837736Smckusick } 6493617Sroot } 65037736Smckusick 65137736Smckusick /* 65237736Smckusick * Check mode permission on inode pointer. Mode is READ, WRITE or EXEC. 65337736Smckusick * The mode is shifted to select the owner/group/other fields. The 65437736Smckusick * super user is granted all permissions. 65537736Smckusick * 65637736Smckusick * NB: Called from vnode op table. It seems this could all be done 65737736Smckusick * using vattr's but... 65837736Smckusick */ 65937736Smckusick iaccess(ip, mode, cred) 66037736Smckusick register struct inode *ip; 66137736Smckusick register int mode; 66237736Smckusick struct ucred *cred; 66337736Smckusick { 66437736Smckusick register gid_t *gp; 66537736Smckusick int i; 66637736Smckusick 66737736Smckusick /* 66839392Smckusick * If you're the super-user, you always get access. 66937736Smckusick */ 67037736Smckusick if (cred->cr_uid == 0) 67137736Smckusick return (0); 67237736Smckusick /* 67337736Smckusick * Access check is based on only one of owner, group, public. 67437736Smckusick * If not owner, then check group. If not a member of the 67537736Smckusick * group, then check public access. 67637736Smckusick */ 67737736Smckusick if (cred->cr_uid != ip->i_uid) { 67837736Smckusick mode >>= 3; 67937736Smckusick gp = cred->cr_groups; 68037736Smckusick for (i = 0; i < cred->cr_ngroups; i++, gp++) 68137736Smckusick if (ip->i_gid == *gp) 68237736Smckusick goto found; 68337736Smckusick mode >>= 3; 68437736Smckusick found: 68537736Smckusick ; 68637736Smckusick } 68737736Smckusick if ((ip->i_mode & mode) != 0) 68837736Smckusick return (0); 68937736Smckusick return (EACCES); 69037736Smckusick } 691