X-Git-Url: https://git.subgeniuskitty.com/unix-history/.git/blobdiff_plain/a8d3bf7f08061516a44629fbaf4c7bb727122742..28708b7290eb3aa4837b4ce01932e3a4c63f078f:/usr/src/sys/ufs/ufs/ufs_inode.c diff --git a/usr/src/sys/ufs/ufs/ufs_inode.c b/usr/src/sys/ufs/ufs/ufs_inode.c index 4c2db27e54..e1954e4548 100644 --- a/usr/src/sys/ufs/ufs/ufs_inode.c +++ b/usr/src/sys/ufs/ufs/ufs_inode.c @@ -1,222 +1,220 @@ -/* ufs_inode.c 4.26 82/10/17 */ - -#include "../h/param.h" -#include "../h/systm.h" -#include "../h/mount.h" -#include "../h/dir.h" -#include "../h/user.h" -#include "../h/inode.h" -#include "../h/fs.h" -#include "../h/conf.h" -#include "../h/buf.h" -#ifdef QUOTA -#include "../h/quota.h" -#endif -#include "../h/kernel.h" +/* + * Copyright (c) 1982, 1986, 1989 Regents of the University of California. + * All rights reserved. + * + * %sccs.include.redist.c% + * + * @(#)ufs_inode.c 7.39 (Berkeley) %G% + */ + +#include "param.h" +#include "systm.h" +#include "mount.h" +#include "proc.h" +#include "file.h" +#include "buf.h" +#include "vnode.h" +#include "kernel.h" +#include "malloc.h" -#define INOHSZ 63 +#include "quota.h" +#include "inode.h" +#include "fs.h" +#include "ufsmount.h" + +#define INOHSZ 512 #if ((INOHSZ&(INOHSZ-1)) == 0) #define INOHASH(dev,ino) (((dev)+(ino))&(INOHSZ-1)) #else -#define INOHASH(dev,ino) (((dev)+(ino))%INOHSZ) +#define INOHASH(dev,ino) (((unsigned)((dev)+(ino)))%INOHSZ) #endif -union ihead { /* inode LRU cache, Chris Maltby */ +union ihead { union ihead *ih_head[2]; struct inode *ih_chain[2]; } ihead[INOHSZ]; -struct inode *ifreeh, **ifreet; +int prtactive; /* 1 => print out reclaim of active vnodes */ /* - * Initialize hash links for inodes - * and build inode free list. + * Initialize hash links for inodes. */ -ihinit() +ufs_init() { register int i; - register struct inode *ip = inode; - register union ihead *ih = ihead; + register union ihead *ih = ihead; +#ifndef lint + if (VN_MAXPRIVATE < sizeof(struct inode)) + panic("ihinit: too small"); +#endif /* not lint */ for (i = INOHSZ; --i >= 0; ih++) { ih->ih_head[0] = ih; ih->ih_head[1] = ih; } - ifreeh = ip; - ifreet = &ip->i_freef; - ip->i_freeb = &ifreeh; - ip->i_forw = ip; - ip->i_back = ip; - for (i = ninode; --i > 0; ) { - ++ip; - ip->i_forw = ip; - ip->i_back = ip; - *ifreet = ip; - ip->i_freeb = ifreet; - ifreet = &ip->i_freef; - } - ip->i_freef = NULL; -} - -#ifdef notdef -/* - * Find an inode if it is incore. - * This is the equivalent, for inodes, - * of ``incore'' in bio.c or ``pfind'' in subr.c. - */ -struct inode * -ifind(dev, ino) - dev_t dev; - ino_t ino; -{ - register struct inode *ip; - register union ihead *ih; - - ih = &ihead[INOHASH(dev, ino)]; - for (ip = ih->ih_chain[0]; ip != (struct inode *)ih; ip = ip->i_forw) - if (ino==ip->i_number && dev==ip->i_dev) - return (ip); - return ((struct inode *)0); +#ifdef QUOTA + dqinit(); +#endif /* QUOTA */ } -#endif notdef /* - * Look up an inode by device,inumber. + * Look up an vnode/inode by device,inumber. * If it is in core (in the inode structure), * honor the locking protocol. * If it is not in core, read it in from the * specified device. - * If the inode is mounted on, perform - * the indicated indirection. + * Callers must check for mount points!! * In all cases, a pointer to a locked * inode structure is returned. - * - * panic: no imt -- if the mounted file - * system is not in the mount table. - * "cannot happen" */ -struct inode * -iget(dev, fs, ino) - dev_t dev; - register struct fs *fs; +iget(xp, ino, ipp) + struct inode *xp; ino_t ino; + struct inode **ipp; { - register struct inode *ip; - register union ihead *ih; - register struct mount *mp; - register struct buf *bp; - register struct dinode *dp; - register struct inode *iq; + dev_t dev = xp->i_dev; + struct mount *mntp = ITOV(xp)->v_mount; + register struct fs *fs = VFSTOUFS(mntp)->um_fs; + extern struct vnodeops ufs_vnodeops, spec_inodeops; + register struct inode *ip, *iq; + register struct vnode *vp; + struct vnode *nvp; + struct buf *bp; + struct dinode *dp; + union ihead *ih; + int i, error; -loop: - if (getfs(dev) != fs) - panic("iget: bad fs"); ih = &ihead[INOHASH(dev, ino)]; - for (ip = ih->ih_chain[0]; ip != (struct inode *)ih; ip = ip->i_forw) - if (ino == ip->i_number && dev == ip->i_dev) { - if ((ip->i_flag&ILOCKED) != 0) { - ip->i_flag |= IWANT; - sleep((caddr_t)ip, PINOD); - goto loop; - } - if ((ip->i_flag&IMOUNT) != 0) { - for (mp = &mount[0]; mp < &mount[NMOUNT]; mp++) - if(mp->m_inodp == ip) { - dev = mp->m_dev; - fs = mp->m_bufp->b_un.b_fs; - ino = ROOTINO; - goto loop; - } - panic("no imt"); - } - if (ip->i_count == 0) { /* ino on free list */ - if (iq = ip->i_freef) - iq->i_freeb = ip->i_freeb; - else - ifreet = ip->i_freeb; - *ip->i_freeb = iq; - ip->i_freef = NULL; - ip->i_freeb = NULL; - } - ip->i_count++; - ip->i_flag |= ILOCKED; - return(ip); +loop: + for (ip = ih->ih_chain[0]; ip != (struct inode *)ih; ip = ip->i_forw) { + if (ino != ip->i_number || dev != ip->i_dev) + continue; + if ((ip->i_flag&ILOCKED) != 0) { + ip->i_flag |= IWANT; + sleep((caddr_t)ip, PINOD); + goto loop; } - - if ((ip = ifreeh) == NULL) { - tablefull("inode"); - u.u_error = ENFILE; - return(NULL); + if (vget(ITOV(ip))) + goto loop; + *ipp = ip; + return(0); } - if (iq = ip->i_freef) - iq->i_freeb = &ifreeh; - ifreeh = iq; - ip->i_freef = NULL; - ip->i_freeb = NULL; /* - * Now to take inode off the hash chain it was on - * (initially, or after an iflush, it is on a "hash chain" - * consisting entirely of itself, and pointed to by no-one, - * but that doesn't matter), and put it on the chain for - * its new (ino, dev) pair + * Allocate a new inode. */ - remque(ip); - insque(ip, ih); + if (error = getnewvnode(VT_UFS, mntp, &ufs_vnodeops, &nvp)) { + *ipp = 0; + return (error); + } + ip = VTOI(nvp); + ip->i_vnode = nvp; + ip->i_flag = 0; + ip->i_devvp = 0; + ip->i_mode = 0; + ip->i_diroff = 0; + ip->i_lockf = 0; #ifdef QUOTA - dqrele(ip->i_dquot); + for (i = 0; i < MAXQUOTAS; i++) + ip->i_dquot[i] = NODQUOT; #endif + /* + * Put it onto its hash chain and lock it so that other requests for + * this inode will block if they arrive while we are sleeping waiting + * for old data structures to be purged or for the contents of the + * disk portion of this inode to be read. + */ ip->i_dev = dev; - ip->i_fs = fs; ip->i_number = ino; - ip->i_flag = ILOCKED; - ip->i_count++; - ip->i_lastr = 0; - bp = bread(dev, fsbtodb(fs, itod(fs, ino)), fs->fs_bsize); + insque(ip, ih); + ILOCK(ip); /* - * Check I/O errors + * Read in the disk contents for the inode. */ - if ((bp->b_flags&B_ERROR) != 0) { - brelse(bp); + if (error = bread(VFSTOUFS(mntp)->um_devvp, fsbtodb(fs, itod(fs, ino)), + (int)fs->fs_bsize, NOCRED, &bp)) { /* - * the inode doesn't contain anything useful, so it would + * The inode does not contain anything useful, so it would * be misleading to leave it on its hash chain. - * 'iput' will take care of putting it back on the free list. + * Iput() will take care of putting it back on the free list. */ remque(ip); ip->i_forw = ip; ip->i_back = ip; /* - * we also loose its inumber, just in case (as iput - * doesn't do that any more) - but as it isn't on its - * hash chain, I doubt if this is really necessary .. kre - * (probably the two methods are interchangable) + * Unlock and discard unneeded inode. */ - ip->i_number = 0; -#ifdef QUOTA - ip->i_dquot = NODQUOT; -#endif iput(ip); - return(NULL); + brelse(bp); + *ipp = 0; + return (error); } dp = bp->b_un.b_dino; dp += itoo(fs, ino); - ip->i_ic = dp->di_ic; + ip->i_din = *dp; brelse(bp); -#ifdef QUOTA - if (ip->i_mode == 0) - ip->i_dquot = NODQUOT; - else - ip->i_dquot = inoquota(ip); -#endif - return (ip); + /* + * Initialize the associated vnode + */ + vp = ITOV(ip); + vp->v_type = IFTOVT(ip->i_mode); + if (vp->v_type == VFIFO) { +#ifdef FIFO + extern struct vnodeops fifo_inodeops; + vp->v_op = &fifo_inodeops; +#else + iput(ip); + *ipp = 0; + return (EOPNOTSUPP); +#endif /* FIFO */ + } + if (vp->v_type == VCHR || vp->v_type == VBLK) { + vp->v_op = &spec_inodeops; + if (nvp = checkalias(vp, ip->i_rdev, mntp)) { + /* + * Reinitialize aliased inode. + */ + vp = nvp; + iq = VTOI(vp); + iq->i_vnode = vp; + iq->i_flag = 0; + ILOCK(iq); + iq->i_din = ip->i_din; + iq->i_dev = dev; + iq->i_number = ino; + insque(iq, ih); + /* + * Discard unneeded vnode + */ + ip->i_mode = 0; + iput(ip); + ip = iq; + } + } + if (ino == ROOTINO) + vp->v_flag |= VROOT; + /* + * Finish inode initialization. + */ + ip->i_fs = fs; + ip->i_devvp = VFSTOUFS(mntp)->um_devvp; + VREF(ip->i_devvp); + /* + * Set up a generation number for this inode if it does not + * already have one. This should only happen on old filesystems. + */ + if (ip->i_gen == 0) { + if (++nextgennumber < (u_long)time.tv_sec) + nextgennumber = time.tv_sec; + ip->i_gen = nextgennumber; + if ((vp->v_mount->mnt_flag & MNT_RDONLY) == 0) + ip->i_flag |= IMOD; + } + *ipp = ip; + return (0); } /* - * Decrement reference count of - * an inode structure. - * On the last reference, - * write the inode out and if necessary, - * truncate and deallocate the file. + * Unlock and decrement the reference count of an inode structure. */ iput(ip) register struct inode *ip; @@ -224,306 +222,419 @@ iput(ip) if ((ip->i_flag & ILOCKED) == 0) panic("iput"); - iunlock(ip); - irele(ip); + IUNLOCK(ip); + vrele(ITOV(ip)); } -irele(ip) - register struct inode *ip; +/* + * Last reference to an inode, write the inode out and if necessary, + * truncate and deallocate the file. + */ +ufs_inactive(vp, p) + struct vnode *vp; + struct proc *p; { - int mode; + register struct inode *ip = VTOI(vp); + int mode, error = 0; - if (ip->i_count == 1) { - ip->i_flag |= ILOCKED; - if (ip->i_nlink <= 0) { - itrunc(ip, 0); - mode = ip->i_mode; - ip->i_mode = 0; - ip->i_rdev = 0; - ip->i_flag |= IUPD|ICHG; - ifree(ip, ip->i_number, mode); + if (prtactive && vp->v_usecount != 0) + vprint("ufs_inactive: pushing active", vp); + /* + * Get rid of inodes related to stale file handles. + */ + if (ip->i_mode == 0) { + if ((vp->v_flag & VXLOCK) == 0) + vgone(vp); + return (0); + } + ILOCK(ip); + if (ip->i_nlink <= 0 && (vp->v_mount->mnt_flag & MNT_RDONLY) == 0) { #ifdef QUOTA - chkiq(ip->i_dev, ip, ip->i_uid, 0); - dqrele(ip->i_dquot); - ip->i_dquot = NODQUOT; + if (!getinoquota(ip)) + (void) chkiq(ip, -1, NOCRED, 0); #endif + error = itrunc(ip, (u_long)0, 0); + mode = ip->i_mode; + ip->i_mode = 0; + ip->i_rdev = 0; + ip->i_flag |= IUPD|ICHG; + ifree(ip, ip->i_number, mode); + } + IUPDAT(ip, &time, &time, 0); + IUNLOCK(ip); + ip->i_flag = 0; + /* + * If we are done with the inode, reclaim it + * so that it can be reused immediately. + */ + if (vp->v_usecount == 0 && ip->i_mode == 0) + vgone(vp); + return (error); +} + +/* + * Reclaim an inode so that it can be used for other purposes. + */ +ufs_reclaim(vp) + register struct vnode *vp; +{ + register struct inode *ip = VTOI(vp); + int i; + + if (prtactive && vp->v_usecount != 0) + vprint("ufs_reclaim: pushing active", vp); + /* + * Remove the inode from its hash chain. + */ + remque(ip); + ip->i_forw = ip; + ip->i_back = ip; + /* + * Purge old data structures associated with the inode. + */ + cache_purge(vp); + if (ip->i_devvp) { + vrele(ip->i_devvp); + ip->i_devvp = 0; + } +#ifdef QUOTA + for (i = 0; i < MAXQUOTAS; i++) { + if (ip->i_dquot[i] != NODQUOT) { + dqrele(vp, ip->i_dquot[i]); + ip->i_dquot[i] = NODQUOT; } - IUPDAT(ip, &time.tv_sec, &time.tv_sec, 0); - iunlock(ip); - ip->i_flag = 0; - /* - * Put the inode on the end of the free list. - * Possibly in some cases it would be better to - * put the inode at the head of the free list, - * (eg: where i_mode == 0 || i_number == 0) - * but I will think about that later .. kre - * (i_number is rarely 0 - only after an i/o error in iget, - * where i_mode == 0, the inode will probably be wanted - * again soon for an ialloc, so possibly we should keep it) - */ - if (ifreeh) { - *ifreet = ip; - ip->i_freeb = ifreet; - } else { - ifreeh = ip; - ip->i_freeb = &ifreeh; - } - ip->i_freef = NULL; - ifreet = &ip->i_freef; } - ip->i_count--; +#endif + ip->i_flag = 0; + return (0); } /* - * Check accessed and update flags on - * an inode structure. - * If any is on, update the inode - * with the current time. - * If waitfor is given, then must insure - * i/o order so wait for write to complete. + * Check accessed and update flags on an inode structure. + * If any is on, update the inode with the current time. + * If waitfor is given, then must ensure I/O order, + * so wait for write to complete. */ iupdat(ip, ta, tm, waitfor) register struct inode *ip; - time_t *ta, *tm; + struct timeval *ta, *tm; int waitfor; { - register struct buf *bp; + struct buf *bp; + struct vnode *vp = ITOV(ip); struct dinode *dp; - register struct fs *fp; - - fp = ip->i_fs; - if ((ip->i_flag & (IUPD|IACC|ICHG)) != 0) { - if (fp->fs_ronly) - return; - bp = bread(ip->i_dev, fsbtodb(fp, itod(fp, ip->i_number)), - fp->fs_bsize); - if (bp->b_flags & B_ERROR) { - brelse(bp); - return; - } - if (ip->i_flag&IACC) - ip->i_atime = *ta; - if (ip->i_flag&IUPD) - ip->i_mtime = *tm; - if (ip->i_flag&ICHG) - ip->i_ctime = time.tv_sec; - ip->i_flag &= ~(IUPD|IACC|ICHG); - dp = bp->b_un.b_dino + itoo(fp, ip->i_number); - dp->di_ic = ip->i_ic; - if (waitfor) - bwrite(bp); - else - bdwrite(bp); + register struct fs *fs; + + fs = ip->i_fs; + if ((ip->i_flag & (IUPD|IACC|ICHG|IMOD)) == 0) + return (0); + if (vp->v_mount->mnt_flag & MNT_RDONLY) + return (0); + error = bread(ip->i_devvp, fsbtodb(fs, itod(fs, ip->i_number)), + (int)fs->fs_bsize, NOCRED, &bp); + if (error) { + brelse(bp); + return (error); + } + if (ip->i_flag&IACC) + ip->i_atime = ta->tv_sec; + if (ip->i_flag&IUPD) + ip->i_mtime = tm->tv_sec; + if (ip->i_flag&ICHG) + ip->i_ctime = time.tv_sec; + ip->i_flag &= ~(IUPD|IACC|ICHG|IMOD); + dp = bp->b_un.b_dino + itoo(fs, ip->i_number); + *dp = ip->i_din; + if (waitfor) { + return (bwrite(bp)); + } else { + bdwrite(bp); + return (0); } } +#define SINGLE 0 /* index of single indirect block */ +#define DOUBLE 1 /* index of double indirect block */ +#define TRIPLE 2 /* index of triple indirect block */ /* - * Truncate the inode ip to at most - * length size. Free affected disk - * blocks -- the blocks of the file - * are removed in reverse order. + * Truncate the inode ip to at most length size. Free affected disk + * blocks -- the blocks of the file are removed in reverse order. + * + * NB: triple indirect blocks are untested. */ -itrunc(ip, length) - register struct inode *ip; - register int length; +itrunc(oip, length, flags) + register struct inode *oip; + u_long length; + int flags; { - register i; - dev_t dev; - daddr_t bn; - struct inode itmp; + register daddr_t lastblock; + daddr_t bn, lbn, lastiblock[NIADDR]; register struct fs *fs; -#ifdef QUOTA - register long cnt = 0; - long tloop(); -#endif - /* - * Only plain files, directories and symbolic - * links contain blocks. - */ - i = ip->i_mode & IFMT; - if (i != IFREG && i != IFDIR && i != IFLNK) - return; - if (ip->i_size <= length) - return; - - /* - * Clean inode on disk before freeing blocks - * to insure no duplicates if system crashes. - */ - itmp = *ip; - itmp.i_size = length; - for (i = 0; i < NDADDR; i++) - itmp.i_db[i] = 0; - for (i = 0; i < NIADDR; i++) - itmp.i_ib[i] = 0; - itmp.i_flag |= ICHG|IUPD; - iupdat(&itmp, &time.tv_sec, &time.tv_sec, 1); - ip->i_flag &= ~(IUPD|IACC|ICHG); + register struct inode *ip; + struct buf *bp; + int offset, osize, size, level; + long count, nblocks, blocksreleased = 0; + register int i; + int aflags, error, allerror; + struct inode tip; + vnode_pager_setsize(ITOV(oip), length); + if (oip->i_size <= length) { + oip->i_flag |= ICHG|IUPD; + error = iupdat(oip, &time, &time, 1); + return (error); + } /* - * Now return blocks to free list... if machine - * crashes, they will be harmless MISSING blocks. + * 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 = ip->i_fs; + 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); /* - * release double indirect block first + * 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. */ - bn = ip->i_ib[NIADDR-1]; - if (bn != (daddr_t)0) { - ip->i_ib[NIADDR - 1] = (daddr_t)0; + osize = oip->i_size; + offset = blkoff(fs, length); + if (offset == 0) { + oip->i_size = length; + } else { + lbn = lblkno(fs, length); + aflags = B_CLRBUF; + if (flags & IO_SYNC) + aflags |= B_SYNC; #ifdef QUOTA - cnt += + if (error = getinoquota(oip)) + return (error); #endif - tloop(ip, bn, 1); + if (error = balloc(oip, lbn, offset, &bp, aflags)) + return (error); + oip->i_size = length; + size = blksize(fs, oip, lbn); + (void) vnode_pager_uncache(ITOV(oip)); + bzero(bp->b_un.b_addr + offset, (unsigned)(size - offset)); + allocbuf(bp, size); + if (flags & IO_SYNC) + bwrite(bp); + else + bdwrite(bp); } /* - * release single indirect blocks second + * 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. */ - for (i = NIADDR - 2; i >= 0; i--) { - bn = ip->i_ib[i]; - if (bn != (daddr_t)0) { - ip->i_ib[i] = (daddr_t)0; -#ifdef QUOTA - cnt += -#endif - tloop(ip, bn, 0); + 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; + vinvalbuf(ITOV(oip), (length > 0)); + allerror = iupdat(oip, &time, &time, MNT_WAIT); + + /* + * 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; } + /* - * finally release direct blocks + * All whole direct blocks or frags. */ - for (i = NDADDR - 1; i>=0; i--) { - register size; + for (i = NDADDR - 1; i > lastblock; i--) { + register off_t bsize; bn = ip->i_db[i]; - if (bn == (daddr_t)0) + if (bn == 0) continue; - ip->i_db[i] = (daddr_t)0; -#ifndef QUOTA - fre(ip, bn, (off_t)blksize(fs, ip, i)); -#else - fre(ip, bn, size = (off_t)blksize(fs, ip, i)); - cnt += size / DEV_BSIZE; -#endif + ip->i_db[i] = 0; + bsize = (off_t)blksize(fs, ip, i); + blkfree(ip, bn, bsize); + blocksreleased += btodb(bsize); } - ip->i_size = 0; + if (lastblock < 0) + goto done; + /* - * Inode was written and flags updated above. - * No need to modify flags here. + * Finally, look for a change in size of the + * last direct block; release any frags. */ + bn = ip->i_db[lastblock]; + if (bn != 0) { + off_t 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); + blkfree(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(ip, -cnt, 0); + if (!getinoquota(oip)) + (void) chkdq(oip, -blocksreleased, NOCRED, 0); #endif + return (allerror); } -#ifdef QUOTA -long -#endif -tloop(ip, bn, indflg) +/* + * Release blocks associated with the inode ip and + * stored in the indirect block bn. Blocks are free'd + * in LIFO order up to (but not including) lastbn. If + * level is greater than SINGLE, the block is an indirect + * block and recursive calls to indirtrunc must be used to + * cleanse other indirect blocks. + * + * NB: triple indirect blocks are untested. + */ +indirtrunc(ip, bn, lastbn, level, countp) register struct inode *ip; - daddr_t bn; - int indflg; + daddr_t bn, lastbn; + int level; + long *countp; { - register i; - register struct buf *bp; + register int i; + struct buf *bp; + register struct fs *fs = ip->i_fs; register daddr_t *bap; - register struct fs *fs; - daddr_t nb; -#ifdef QUOTA - register long cnt = 0; -#endif + daddr_t *copy, nb, last; + long blkcount, factor; + int nblocks, blocksreleased = 0; + int error, allerror = 0; - bp = NULL; - fs = ip->i_fs; - for (i = NINDIR(fs) - 1; i >= 0; i--) { - if (bp == NULL) { - bp = bread(ip->i_dev, fsbtodb(fs, bn), fs->fs_bsize); - if (bp->b_flags & B_ERROR) { - brelse(bp); - return; - } - bap = bp->b_un.b_daddr; - } + /* + * 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 == (daddr_t)0) + if (nb == 0) continue; - if (indflg) { -#ifdef QUOTA - cnt += -#endif - tloop(ip, nb, 0); - } else { - fre(ip, nb, fs->fs_bsize); -#ifdef QUOTA - cnt += fs->fs_bsize / DEV_BSIZE; -#endif + 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; } - if (bp != NULL) - brelse(bp); - fre(ip, bn, fs->fs_bsize); -#ifdef QUOTA - cnt += fs->fs_bsize / DEV_BSIZE; - return(cnt); -#endif -} - -/* - * remove any inodes in the inode cache belonging to dev - * - * There should not be any active ones, return error if any are found - * (nb: this is a user error, not a system err) - * - * Also, count the references to dev by block devices - this really - * has nothing to do with the object of the procedure, but as we have - * to scan the inode table here anyway, we might as well get the - * extra benefit. - * - * this is called from sumount()/sys3.c when dev is being unmounted - */ -#ifdef QUOTA -iflush(dev, iq) - dev_t dev; - struct inode *iq; -#else -iflush(dev) - dev_t dev; -#endif -{ - register struct inode *ip; - register open = 0; - for (ip = inode; ip < inodeNINODE; ip++) { -#ifdef QUOTA - if (ip != iq && ip->i_dev == dev) -#else - if (ip->i_dev == dev) -#endif - if (ip->i_count) - return(-1); - else { - remque(ip); - ip->i_forw = ip; - ip->i_back = ip; - /* - * as i_count == 0, the inode was on the free - * list already, just leave it there, it will - * fall off the bottom eventually. We could - * perhaps move it to the head of the free - * list, but as umounts are done so - * infrequently, we would gain very little, - * while making the code bigger. - */ -#ifdef QUOTA - dqrele(ip->i_dquot); - ip->i_dquot = NODQUOT; -#endif - } - else if (ip->i_count && (ip->i_mode&IFMT)==IFBLK && - ip->i_rdev == dev) - open++; + /* + * Recursively free last partial block. + */ + if (level > SINGLE && lastbn >= 0) { + last = lastbn % factor; + nb = bap[i]; + if (nb != 0) { + error = indirtrunc(ip, nb, last, level - 1, &blkcount); + if (error) + allerror = error; + blocksreleased += blkcount; + } } - return (open); + FREE(copy, M_TEMP); + *countp = blocksreleased; + return (allerror); } /* @@ -533,7 +644,16 @@ ilock(ip) register struct inode *ip; { - ILOCK(ip); + while (ip->i_flag & ILOCKED) { + ip->i_flag |= IWANT; + if (ip->i_spare0 == curproc->p_pid) + panic("locking against myself"); + ip->i_spare1 = curproc->p_pid; + (void) sleep((caddr_t)ip, PINOD); + } + ip->i_spare1 = 0; + ip->i_spare0 = curproc->p_pid; + ip->i_flag |= ILOCKED; } /* @@ -543,5 +663,12 @@ iunlock(ip) register struct inode *ip; { - IUNLOCK(ip); + if ((ip->i_flag & ILOCKED) == 0) + vprint("iunlock: unlocked inode", ITOV(ip)); + ip->i_spare0 = 0; + ip->i_flag &= ~ILOCKED; + if (ip->i_flag&IWANT) { + ip->i_flag &= ~IWANT; + wakeup((caddr_t)ip); + } }