From 9d4a6d9680ffde39f60958eee38bf4909cbe8894 Mon Sep 17 00:00:00 2001 From: CSRG Date: Sat, 26 Mar 1983 23:02:32 -0800 Subject: [PATCH] BSD 4_1c_2 development Work on file a/sys/sys/ufs_inode.c Synthesized-from: CSRG/cd1/4.1c.2 --- a/sys/sys/ufs_inode.c | 647 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 647 insertions(+) create mode 100644 a/sys/sys/ufs_inode.c diff --git a/a/sys/sys/ufs_inode.c b/a/sys/sys/ufs_inode.c new file mode 100644 index 0000000000..aea8addf25 --- /dev/null +++ b/a/sys/sys/ufs_inode.c @@ -0,0 +1,647 @@ +/* ufs_inode.c 4.34 83/03/15 */ + +#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" + +#define INOHSZ 63 +#if ((INOHSZ&(INOHSZ-1)) == 0) +#define INOHASH(dev,ino) (((dev)+(ino))&(INOHSZ-1)) +#else +#define INOHASH(dev,ino) (((unsigned)((dev)+(ino)))%INOHSZ) +#endif + +union ihead { /* inode LRU cache, Chris Maltby */ + union ihead *ih_head[2]; + struct inode *ih_chain[2]; +} ihead[INOHSZ]; + +struct inode *ifreeh, **ifreet; + +/* + * Initialize hash links for inodes + * and build inode free list. + */ +ihinit() +{ + register int i; + register struct inode *ip = inode; + register union ihead *ih = ihead; + + 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); +} +#endif notdef + +/* + * Look up an 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. + * 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; + ino_t ino; +{ + register struct inode *ip; + register union ihead *ih; + register struct mount *mp; + register struct buf *bp; + register struct dinode *dp; + register struct inode *iq; + +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); + } + + if ((ip = ifreeh) == NULL) { + tablefull("inode"); + u.u_error = ENFILE; + return(NULL); + } + 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 + */ + remque(ip); + insque(ip, ih); +#ifdef QUOTA + dqrele(ip->i_dquot); +#endif + 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)), (int)fs->fs_bsize); + /* + * Check I/O errors + */ + if ((bp->b_flags&B_ERROR) != 0) { + brelse(bp); + /* + * the inode doesn't 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. + */ + 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) + */ + ip->i_number = 0; +#ifdef QUOTA + ip->i_dquot = NODQUOT; +#endif + iput(ip); + return(NULL); + } + dp = bp->b_un.b_dino; + dp += itoo(fs, ino); + ip->i_ic = dp->di_ic; + brelse(bp); +#ifdef QUOTA + if (ip->i_mode == 0) + ip->i_dquot = NODQUOT; + else + ip->i_dquot = inoquota(ip); +#endif + return (ip); +} + +/* + * Decrement reference count of + * an inode structure. + * On the last reference, + * write the inode out and if necessary, + * truncate and deallocate the file. + */ +iput(ip) + register struct inode *ip; +{ + + if ((ip->i_flag & ILOCKED) == 0) + panic("iput"); + iunlock(ip); + irele(ip); +} + +irele(ip) + register struct inode *ip; +{ + int mode; + + if (ip->i_count == 1) { + ip->i_flag |= ILOCKED; + if (ip->i_nlink <= 0) { + itrunc(ip, (u_long)0); + mode = ip->i_mode; + ip->i_mode = 0; + ip->i_rdev = 0; + ip->i_flag |= IUPD|ICHG; + ifree(ip, ip->i_number, mode); +#ifdef QUOTA + (void)chkiq(ip->i_dev, ip, ip->i_uid, 0); + dqrele(ip->i_dquot); + ip->i_dquot = NODQUOT; +#endif + } + 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; + } + ip->i_freef = NULL; + ifreet = &ip->i_freef; + } + ip->i_count--; +} + +/* + * 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. + */ +iupdat(ip, ta, tm, waitfor) + register struct inode *ip; + struct timeval *ta, *tm; + int waitfor; +{ + register struct buf *bp; + 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)), + (int)fp->fs_bsize); + if (bp->b_flags & B_ERROR) { + brelse(bp); + return; + } + 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); + dp = bp->b_un.b_dino + itoo(fp, ip->i_number); + dp->di_ic = ip->i_ic; + if (waitfor) + bwrite(bp); + else + bdwrite(bp); + } +} + +#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. + * + * NB: triple indirect blocks are untested. + */ +itrunc(oip, length) + struct inode *oip; + u_long length; +{ + register i; + register daddr_t lastblock; + daddr_t bn, lastiblock[NIADDR]; + register struct fs *fs; + register struct inode *ip; + struct inode tip; + int level; +#ifdef QUOTA + long blocksreleased = 0, nblocks; + long indirtrunc(); +#endif + + if (oip->i_size <= length) + return; + /* + * 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 = 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); +#ifdef QUOTA + nblocks = fs->fs_bsize / DEV_BSIZE; +#endif + /* + * Update size of 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. + * (? fsck doesn't check validity of pointers in indirect blocks) + */ + tip = *oip; + 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_size = length; + oip->i_flag |= ICHG|IUPD; + iupdat(oip, &time, &time, 1); + ip = &tip; + + /* + * Indirect blocks first. + */ + for (level = TRIPLE; level >= SINGLE; level--) { + bn = ip->i_ib[level]; + if (bn != 0) { +#ifdef QUOTA + blocksreleased += +#endif + indirtrunc(ip, bn, lastiblock[level], level); + if (lastiblock[level] < 0) { + ip->i_ib[level] = 0; + free(ip, bn, (off_t)fs->fs_bsize); +#ifdef QUOTA + blocksreleased += nblocks; +#endif + } + } + if (lastiblock[level] >= 0) + goto done; + } + + /* + * All whole direct blocks or frags. + */ + for (i = NDADDR - 1; i > lastblock; i--) { + register int size; + + bn = ip->i_db[i]; + if (bn == 0) + continue; + ip->i_db[i] = 0; + size = (off_t)blksize(fs, ip, i); + free(ip, bn, size); +#ifdef QUOTA + blocksreleased += size / DEV_BSIZE; +#endif + } + if (lastblock < 0) + goto done; + + /* + * Finally, look for a change in size of the + * last direct block; release any frags. + */ + bn = ip->i_db[lastblock]; + if (bn != 0) { + int oldspace, newspace; + + /* + * Calculate amount of space we're giving + * back as old block size minus new block size. + */ + oldspace = blksize(fs, ip, lastblock); + ip->i_size = length; + newspace = blksize(fs, ip, lastblock); + if (newspace == 0) + panic("itrunc: newspace"); + if (oldspace - newspace > 0) { + /* + * Block number of space to be free'd is + * the old block # plus the number of frags + * required for the storage we're keeping. + */ + bn += numfrags(fs, newspace); + free(ip, bn, oldspace - newspace); +#ifdef QUOTA + blocksreleased += (oldspace - newspace) / DEV_BSIZE; +#endif + } + } +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 */ +#ifdef QUOTA + (void) chkdq(ip, -blocksreleased, 0); +#endif +} + +/* + * 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. + */ +#ifdef QUOTA +long +#endif +indirtrunc(ip, bn, lastbn, level) + register struct inode *ip; + daddr_t bn, lastbn; + int level; +{ + register int i; + struct buf *bp, *copy; + register daddr_t *bap; + register struct fs *fs = ip->i_fs; + daddr_t nb, last; + long factor; +#ifdef QUOTA + int blocksreleased = 0, nblocks; +#endif + + /* + * 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; +#ifdef QUOTA + nblocks = fs->fs_bsize / DEV_BSIZE; +#endif + /* + * Get buffer of block pointers, zero those + * entries corresponding to blocks to be free'd, + * and update on disk copy first. + */ + copy = geteblk((int)fs->fs_bsize); + bp = bread(ip->i_dev, fsbtodb(fs, bn), (int)fs->fs_bsize); + if (bp->b_flags&B_ERROR) { + brelse(copy); + brelse(bp); +#ifdef QUOTA + return (0); +#else + return; +#endif + } + bap = bp->b_un.b_daddr; + bcopy((caddr_t)bap, (caddr_t)copy->b_un.b_daddr, (u_int)fs->fs_bsize); + bzero((caddr_t)&bap[last + 1], + (u_int)(NINDIR(fs) - (last + 1)) * sizeof (daddr_t)); + bwrite(bp); + bp = copy, bap = bp->b_un.b_daddr; + + /* + * Recursively free totally unused blocks. + */ + for (i = NINDIR(fs) - 1; i > last; i--) { + nb = bap[i]; + if (nb == 0) + continue; + if (level > SINGLE) +#ifdef QUOTA + blocksreleased += +#endif + indirtrunc(ip, nb, (daddr_t)-1, level - 1); + free(ip, nb, (int)fs->fs_bsize); +#ifdef QUOTA + blocksreleased += nblocks; +#endif + } + + /* + * Recursively free last partial block. + */ + if (level > SINGLE && lastbn >= 0) { + last = lastbn % factor; + nb = bap[i]; + if (nb != 0) +#ifdef QUOTA + blocksreleased += +#endif + indirtrunc(ip, nb, last, level - 1); + } + brelse(bp); +#ifdef QUOTA + return (blocksreleased); +#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++; + } + return (open); +} + +/* + * Lock an inode. If its already locked, set the WANT bit and sleep. + */ +ilock(ip) + register struct inode *ip; +{ + + ILOCK(ip); +} + +/* + * Unlock an inode. If WANT bit is on, wakeup. + */ +iunlock(ip) + register struct inode *ip; +{ + + IUNLOCK(ip); +} -- 2.20.1