X-Git-Url: https://git.subgeniuskitty.com/unix-history/.git/blobdiff_plain/c39ea692ce2ede0ec345503854d7c4c824ca8cc1..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 bcfb3ef403..e1954e4548 100644 --- a/usr/src/sys/ufs/ufs/ufs_inode.c +++ b/usr/src/sys/ufs/ufs/ufs_inode.c @@ -1,600 +1,642 @@ -/* ufs_inode.c 4.16 82/07/01 */ - -#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" -#include "../h/inline.h" - -#define INOHSZ 63 +/* + * 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" + +#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&ILOCK) != 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 |= ILOCK; - 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. + */ + 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 + 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. */ - remque(ip); - insque(ip, ih); ip->i_dev = dev; - ip->i_fs = fs; ip->i_number = ino; - ip->i_flag = ILOCK; - 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; 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); - 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; { - if ((ip->i_flag & ILOCK) == 0) + 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; { - register int i, x; - register struct inode *jp; - int mode; - - if (ip->i_count == 1) { - ip->i_flag |= ILOCK; - if (ip->i_nlink <= 0) { - itrunc(ip); - mode = ip->i_mode; - ip->i_mode = 0; - ip->i_flag |= IUPD|ICHG; - ifree(ip, ip->i_number, mode); - } - IUPDAT(ip, &time, &time, 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; + register struct inode *ip = VTOI(vp); + int mode, error = 0; + + 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 + 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; } - 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; - 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 */ /* - * Free all the disk blocks associated - * with the specified inode structure. - * The blocks of the file are removed - * in reverse order. This FILO - * algorithm will tend to maintain - * a contiguous free list much longer - * than FIFO. + * 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) - register struct inode *ip; +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; - - /* - * Clean inode on disk before freeing blocks - * to insure no duplicates if system crashes. - */ - itmp = *ip; - itmp.i_size = 0; - 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, &time, 1); - ip->i_flag &= ~(IUPD|IACC|ICHG); - - /* - * Only plain files, directories and symbolic - * links contain blocks. - */ - i = ip->i_mode & IFMT; - if (i != IFREG && i != IFDIR && i != IFLNK) - return; + 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. */ - dev = ip->i_dev; - 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; - tloop(ip, bn, 1); + 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 + if (error = getinoquota(oip)) + return (error); +#endif + 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; - 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); + /* - * finally release direct blocks + * Indirect blocks first. */ - for (i = NDADDR - 1; i>=0; i--) { - bn = ip->i_db[i]; - if (bn == (daddr_t)0) - continue; - ip->i_db[i] = (daddr_t)0; - fre(ip, bn, (off_t)blksize(fs, ip, i)); + 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; } - ip->i_size = 0; + /* - * Inode was written and flags updated above. - * No need to modify flags here. + * All whole direct blocks or frags. */ -} + for (i = NDADDR - 1; i > lastblock; i--) { + register off_t bsize; -tloop(ip, bn, indflg) - register struct inode *ip; - daddr_t bn; - int indflg; -{ - register i; - register struct buf *bp; - register daddr_t *bap; - register struct fs *fs; - daddr_t nb; - - 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; - } - nb = bap[i]; - if (nb == (daddr_t)0) + bn = ip->i_db[i]; + if (bn == 0) continue; - if (indflg) - tloop(ip, nb, 0); - else - fre(ip, nb, fs->fs_bsize); - } - if (bp != NULL) - brelse(bp); - fre(ip, bn, fs->fs_bsize); -} - -/* - * Make a new file. - */ -struct inode * -maknode(mode) - int mode; -{ - register struct inode *ip; - ino_t ipref; - - if ((mode & IFMT) == IFDIR) - ipref = dirpref(u.u_pdir->i_fs); - else - ipref = u.u_pdir->i_number; - ip = ialloc(u.u_pdir, ipref, mode); - if (ip == NULL) { - iput(u.u_pdir); - return(NULL); + ip->i_db[i] = 0; + bsize = (off_t)blksize(fs, ip, i); + blkfree(ip, bn, bsize); + blocksreleased += btodb(bsize); } - ip->i_flag |= IACC|IUPD|ICHG; - if ((mode & IFMT) == 0) - mode |= IFREG; - ip->i_mode = mode & ~u.u_cmask; - ip->i_nlink = 1; - ip->i_uid = u.u_uid; - ip->i_gid = u.u_pdir->i_gid; + if (lastblock < 0) + goto done; /* - * Make sure inode goes to disk before directory entry. + * Finally, look for a change in size of the + * last direct block; release any frags. */ - iupdat(ip, &time, &time, 1); - wdir(ip); - if (u.u_error) { + bn = ip->i_db[lastblock]; + if (bn != 0) { + off_t oldspace, newspace; + /* - * write error occurred trying to update directory - * so must deallocate the inode + * Calculate amount of space we're giving + * back as old block size minus new block size. */ - ip->i_nlink = 0; - ip->i_flag |= ICHG; - iput(ip); - return(NULL); + 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); + } } - return(ip); +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 + if (!getinoquota(oip)) + (void) chkdq(oip, -blocksreleased, NOCRED, 0); +#endif + return (allerror); } /* - * Write a directory entry with - * parameters left as side effects - * to a call to namei. + * 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. */ -wdir(ip) - struct inode *ip; +indirtrunc(ip, bn, lastbn, level, countp) + register struct inode *ip; + daddr_t bn, lastbn; + int level; + long *countp; { - register struct direct *dp, *ndp; - struct fs *fs; + register int i; struct buf *bp; - int lbn, bn, base; - int loc, dsize, spccnt, newsize; - char *dirbuf; + register struct fs *fs = ip->i_fs; + register daddr_t *bap; + daddr_t *copy, nb, last; + long blkcount, factor; + int nblocks, blocksreleased = 0; + int error, allerror = 0; - u.u_dent.d_ino = ip->i_number; - u.u_segflg = 1; - newsize = DIRSIZ(&u.u_dent); /* - * if u.u_count == 0, a new directory block must be allocated. + * Calculate index in current block of last + * block to be kept. -1 indicates the entire + * block so we need not calculate the index. */ - if (u.u_count == 0) { - u.u_dent.d_reclen = DIRBLKSIZ; - u.u_count = newsize; - u.u_base = (caddr_t)&u.u_dent; - writei(u.u_pdir); - iput(u.u_pdir); - return; - } + factor = 1; + for (i = SINGLE; i < level; i++) + factor *= NINDIR(fs); + last = lastbn; + if (lastbn > 0) + last /= factor; + nblocks = btodb(fs->fs_bsize); /* - * must read in an existing directory block - * to prepare to place the new entry into it. + * Get buffer of block pointers, zero those + * entries corresponding to blocks to be free'd, + * and update on disk copy first. */ - fs = u.u_pdir->i_fs; - lbn = lblkno(fs, u.u_offset); - base = blkoff(fs, u.u_offset); - bn = fsbtodb(fs, bmap(u.u_pdir, lbn, B_WRITE, base + u.u_count)); - if (u.u_offset + u.u_count > u.u_pdir->i_size) - u.u_pdir->i_size = u.u_offset + u.u_count; - bp = bread(u.u_pdir->i_dev, bn, blksize(fs, u.u_pdir, lbn)); - if (bp->b_flags & B_ERROR) { +#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); - return; + *countp = 0; + return (error); } - dirbuf = bp->b_un.b_addr + base; - dp = (struct direct *)dirbuf; - dsize = DIRSIZ(dp); - spccnt = dp->d_reclen - dsize; + 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; + /* - * if there is insufficient room to make an entry at this point - * namei insures that compacting from u.u_offset for u.u_count - * bytes will provide the necessary space. + * Recursively free totally unused blocks. */ - for (loc = dp->d_reclen; loc < u.u_count; ) { - ndp = (struct direct *)(dirbuf + loc); - if (dp->d_ino == 0) { - spccnt += dsize; - } else { - dp->d_reclen = dsize; - dp = (struct direct *)((char *)dp + dsize); + for (i = NINDIR(fs) - 1; i > last; i--) { + nb = bap[i]; + if (nb == 0) + continue; + if (level > SINGLE) { + error = indirtrunc(ip, nb, (daddr_t)-1, level - 1, + &blkcount); + if (error) + allerror = error; + blocksreleased += blkcount; } - dsize = DIRSIZ(ndp); - spccnt += ndp->d_reclen - dsize; - loc += ndp->d_reclen; - bcopy(ndp, dp, dsize); + blkfree(ip, nb, (off_t)fs->fs_bsize); + blocksreleased += nblocks; } + /* - * Update the pointer fields in the previous entry (if any), - * copy in the new entry, and write out the block. + * Recursively free last partial block. */ - if (dp->d_ino == 0) { - if (spccnt + dsize < newsize) - panic("wdir: compact failed"); - u.u_dent.d_reclen = spccnt + dsize; - } else { - if (spccnt < newsize) - panic("wdir: compact failed"); - u.u_dent.d_reclen = spccnt; - dp->d_reclen = dsize; - dp = (struct direct *)((char *)dp + dsize); - } - bcopy(&u.u_dent, dp, newsize); - bwrite(bp); - u.u_pdir->i_flag |= IUPD|ICHG; - iput(u.u_pdir); -} - -/* - * 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 - */ -iflush(dev) - dev_t dev; -{ - register struct inode *ip; - register open = 0; - - for (ip = inode; ip < inodeNINODE; ip++) { - if (ip->i_dev == dev) - 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. - */ - } - else if (ip->i_count && (ip->i_mode&IFMT)==IFBLK && - ip->i_rdev == dev) - open++; + 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); } -#ifdef ilock -#undef ilock -#endif -#ifdef iunlock -#undef iunlock -#endif /* * Lock an inode. If its already locked, set the WANT bit and sleep. */ @@ -602,11 +644,16 @@ ilock(ip) register struct inode *ip; { - while (ip->i_flag&ILOCK) { + while (ip->i_flag & ILOCKED) { ip->i_flag |= IWANT; - sleep((caddr_t)ip, PINOD); + 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_flag |= ILOCK; + ip->i_spare1 = 0; + ip->i_spare0 = curproc->p_pid; + ip->i_flag |= ILOCKED; } /* @@ -616,7 +663,10 @@ iunlock(ip) register struct inode *ip; { - ip->i_flag &= ~ILOCK; + 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);