X-Git-Url: https://git.subgeniuskitty.com/unix-history/.git/blobdiff_plain/5485e062213aa576f431ef766f639e248716dfe8..82161bc863f8bfc002a6ffd03a59c3548161874f:/usr/src/sys/ufs/lfs/lfs_inode.c diff --git a/usr/src/sys/ufs/lfs/lfs_inode.c b/usr/src/sys/ufs/lfs/lfs_inode.c index 67ed51b810..bf589b073f 100644 --- a/usr/src/sys/ufs/lfs/lfs_inode.c +++ b/usr/src/sys/ufs/lfs/lfs_inode.c @@ -1,419 +1,651 @@ -/* lfs_inode.c 4.9 82/02/27 */ - -#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/ino.h" -#include "../h/filsys.h" -#include "../h/conf.h" -#include "../h/buf.h" -#include "../h/inline.h" - -#define INOHSZ 63 -#define INOHASH(dev,ino) (((dev)+(ino))%INOHSZ) -short inohash[INOHSZ]; -short ifreel; - /* - * Initialize hash links for inodes - * and build inode free list. + * Copyright (c) 1982, 1986, 1989 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the University of California, Berkeley. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * @(#)lfs_inode.c 7.32 (Berkeley) %G% */ -ihinit() -{ - register int i; - register struct inode *ip = inode; - ifreel = 0; - for (i = 0; i < ninode-1; i++, ip++) - ip->i_hlink = i+1; - ip->i_hlink = -1; - for (i = 0; i < INOHSZ; i++) - inohash[i] = -1; -} +#include "param.h" +#include "systm.h" +#include "mount.h" +#include "user.h" +#include "proc.h" +#include "file.h" +#include "buf.h" +#include "cmap.h" +#include "vnode.h" +#include "../ufs/quota.h" +#include "../ufs/inode.h" +#include "../ufs/fs.h" +#include "../ufs/ufsmount.h" +#include "kernel.h" +#include "malloc.h" + +#define INOHSZ 512 +#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 { + union ihead *ih_head[2]; + struct inode *ih_chain[2]; +} ihead[INOHSZ]; + +int prtactive; /* 1 => print out reclaim of active vnodes */ /* - * Find an inode if it is incore. - * This is the equivalent, for inodes, - * of ``incore'' in bio.c or ``pfind'' in subr.c. + * Initialize hash links for inodes. */ -struct inode * -ifind(dev, ino) - dev_t dev; - ino_t ino; +ufs_init() { - register struct inode *ip; - - for (ip = &inode[inohash[INOHASH(dev,ino)]]; ip != &inode[-1]; - ip = &inode[ip->i_hlink]) - if (ino==ip->i_number && dev==ip->i_dev) - return (ip); - return ((struct inode *)0); + register int i; + 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; + } +#ifdef QUOTA + dqinit(); +#endif /* QUOTA */ } /* - * 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, ino) - dev_t dev; +iget(xp, ino, ipp) + struct inode *xp; ino_t ino; + struct inode **ipp; { - register struct inode *ip; - register struct mount *mp; - register struct buf *bp; - register struct dinode *dp; - register int slot; + 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; + ih = &ihead[INOHASH(dev, ino)]; loop: - slot = INOHASH(dev, ino); - ip = &inode[inohash[slot]]; - while (ip != &inode[-1]) { - 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; - ino = ROOTINO; - goto loop; - } - panic("no imt"); - } - ip->i_count++; - ip->i_flag |= ILOCK; - return(ip); + 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; } - ip = &inode[ip->i_hlink]; + if (vget(ITOV(ip))) + goto loop; + *ipp = ip; + return(0); } - if (ifreel < 0) { - tablefull("inode"); - u.u_error = ENFILE; - return(NULL); + /* + * Allocate a new inode. + */ + if (error = getnewvnode(VT_UFS, mntp, &ufs_vnodeops, &nvp)) { + *ipp = 0; + return (error); } - ip = &inode[ifreel]; - ifreel = ip->i_hlink; - ip->i_hlink = inohash[slot]; - inohash[slot] = ip - inode; + ip = VTOI(nvp); + ip->i_vnode = nvp; + ip->i_flag = 0; + ip->i_devvp = 0; + ip->i_mode = 0; + ip->i_diroff = 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. + */ ip->i_dev = dev; ip->i_number = ino; - ip->i_flag = ILOCK; - ip->i_count++; - ip->i_un.i_lastr = 0; - bp = bread(dev, itod(ino)); + 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 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. + */ + remque(ip); + ip->i_forw = ip; + ip->i_back = ip; + /* + * Unlock and discard unneeded inode. + */ iput(ip); - return(NULL); + brelse(bp); + *ipp = 0; + return (error); } dp = bp->b_un.b_dino; - dp += itoo(ino); - iexpand(ip, dp); + dp += itoo(fs, ino); + 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); } -iexpand(ip, dp) +/* + * Unlock and decrement the reference count of an inode structure. + */ +iput(ip) register struct inode *ip; - register struct dinode *dp; { - register char *p1, *p2; - register int i; - ip->i_mode = dp->di_mode; - ip->i_nlink = dp->di_nlink; - ip->i_uid = dp->di_uid; - ip->i_gid = dp->di_gid; - ip->i_size = dp->di_size; - p1 = (char *)ip->i_un.i_addr; - p2 = (char *)dp->di_addr; - for(i=0; ii_flag & ILOCKED) == 0) + panic("iput"); + IUNLOCK(ip); + vrele(ITOV(ip)); } /* - * Decrement reference count of - * an inode structure. - * On the last reference, - * write the inode out and if necessary, + * Last reference to an inode, write the inode out and if necessary, * truncate and deallocate the file. */ -iput(ip) - register struct inode *ip; +ufs_inactive(vp) + struct vnode *vp; { - register int i, x; - register struct inode *jp; + register struct inode *ip = VTOI(vp); + int mode, error = 0; - if (ip->i_count == 1) { - ip->i_flag |= ILOCK; - if (ip->i_nlink <= 0) { - itrunc(ip); - ip->i_mode = 0; - ip->i_flag |= IUPD|ICHG; - ifree(ip->i_dev, ip->i_number); - } - IUPDAT(ip, &time, &time, 0); - irele(ip); - i = INOHASH(ip->i_dev, ip->i_number); - x = ip - inode; - if (inohash[i] == x) { - inohash[i] = ip->i_hlink; - } else { - for (jp = &inode[inohash[i]]; jp != &inode[-1]; - jp = &inode[jp->i_hlink]) - if (jp->i_hlink == x) { - jp->i_hlink = ip->i_hlink; - goto done; - } - panic("iput"); + 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_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; } -done: - ip->i_hlink = ifreel; - ifreel = x; - ip->i_flag = 0; - ip->i_number = 0; - } else - irele(ip); - 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 char *p1, *p2; - register int i; - - if ((ip->i_flag&(IUPD|IACC|ICHG)) != 0) { - if (getfs(ip->i_dev)->s_ronly) - return; - bp = bread(ip->i_dev, itod(ip->i_number)); - if (bp->b_flags & B_ERROR) { - brelse(bp); - return; - } - dp = bp->b_un.b_dino; - dp += itoo(ip->i_number); - dp->di_mode = ip->i_mode; - dp->di_nlink = ip->i_nlink; - dp->di_uid = ip->i_uid; - dp->di_gid = ip->i_gid; - dp->di_size = ip->i_size; - p1 = (char *)dp->di_addr; - p2 = (char *)ip->i_un.i_addr; - for(i=0; i 2^24\n"); - } - if (ip->i_flag&IACC) - dp->di_atime = *ta; - if (ip->i_flag&IUPD) - dp->di_mtime = *tm; - if (ip->i_flag&ICHG) - dp->di_ctime = time; - ip->i_flag &= ~(IUPD|IACC|ICHG); - 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; - - i = ip->i_mode & IFMT; - if (i!=IFREG && i!=IFDIR && i!=IFLNK) - return; + register daddr_t lastblock; + daddr_t bn, lbn, lastiblock[NIADDR]; + register struct fs *fs; + 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; + if (oip->i_size <= length) { + oip->i_flag |= ICHG|IUPD; + error = iupdat(oip, &time, &time, 1); + return (error); + } /* - * Clean inode on disk before freeing blocks - * to insure no duplicates if system crashes. + * 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. */ - itmp = *ip; - itmp.i_size = 0; - for (i = 0; i < NADDR; i++) - itmp.i_un.i_addr[i] = 0; - itmp.i_flag |= ICHG|IUPD; - iupdat(&itmp, &time, &time, 1); - ip->i_flag &= ~(IUPD|IACC|ICHG); - + 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); /* - * Now return blocks to free list... if machine - * crashes, they will be harmless MISSING blocks. + * 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. */ - dev = ip->i_dev; - for(i=NADDR-1; i>=0; i--) { - bn = ip->i_un.i_addr[i]; - if (bn == (daddr_t)0) - continue; - ip->i_un.i_addr[i] = (daddr_t)0; - switch(i) { - - default: - free(dev, bn); - break; - - case NADDR-3: - tloop(dev, bn, 0, 0); - break; - - case NADDR-2: - tloop(dev, bn, 1, 0); - break; + 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); + bn = bp->b_blkno; + count = howmany(size, CLBYTES); + munhash(oip->i_devvp, bn + i * CLBYTES / DEV_BSIZE); + bzero(bp->b_un.b_addr + offset, (unsigned)(size - offset)); + brealloc(bp, size); + if (flags & IO_SYNC) + bwrite(bp); + else + bdwrite(bp); + } + /* + * 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. + */ + 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); - case NADDR-1: - tloop(dev, bn, 1, 1); + /* + * 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; } - 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(dev, bn, f1, f2) -dev_t dev; -daddr_t bn; -{ - register i; - register struct buf *bp; - register daddr_t *bap; - daddr_t nb; + bn = ip->i_db[i]; + if (bn == 0) + continue; + ip->i_db[i] = 0; + bsize = (off_t)blksize(fs, ip, i); + blkfree(ip, bn, bsize); + blocksreleased += btodb(bsize); + } + if (lastblock < 0) + goto done; - bp = NULL; - for(i=NINDIR-1; i>=0; i--) { - if (bp == NULL) { - bp = bread(dev, bn); - if (bp->b_flags & B_ERROR) { - brelse(bp); - return; - } - bap = bp->b_un.b_daddr; + /* + * 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); } - nb = bap[i]; - if (nb == (daddr_t)0) - continue; - if (f1) { - brelse(bp); - bp = NULL; - tloop(dev, nb, f2, 0); - } else - free(dev, nb); } - if (bp != NULL) - brelse(bp); - free(dev, bn); +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); } /* - * Make a new file. + * 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. */ -struct inode * -maknode(mode) -{ +indirtrunc(ip, bn, lastbn, level, countp) register struct inode *ip; + daddr_t bn, lastbn; + int level; + long *countp; +{ + register int i; + struct buf *bp; + 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; - ip = ialloc(u.u_pdir->i_dev); - if (ip == NULL) { - iput(u.u_pdir); - return(NULL); + /* + * 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); } - 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; + 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; /* - * Make sure inode goes to disk before directory entry. + * Recursively free totally unused blocks. */ - iupdat(ip, &time, &time, 1); - - wdir(ip); - return(ip); -} - -/* - * Write a directory entry with - * parameters left as side effects - * to a call to namei. - */ -wdir(ip) - struct inode *ip; -{ + 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; + } + blkfree(ip, nb, (off_t)fs->fs_bsize); + blocksreleased += nblocks; + } - u.u_dent.d_ino = ip->i_number; - bcopy((caddr_t)u.u_dbuf, (caddr_t)u.u_dent.d_name, DIRSIZ); - u.u_count = sizeof(struct direct); - u.u_segflg = 1; - u.u_base = (caddr_t)&u.u_dent; - writei(u.u_pdir); - iput(u.u_pdir); + /* + * 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; + } + } + FREE(copy, M_TEMP); + *countp = blocksreleased; + return (allerror); } -#ifdef ilock -#undef ilock -#endif -#ifdef irele -#undef irele -#endif /* * Lock an inode. If its already locked, set the WANT bit and sleep. */ @@ -421,21 +653,31 @@ 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 == u.u_procp->p_pid) + panic("locking against myself"); + ip->i_spare1 = u.u_procp->p_pid; + (void) sleep((caddr_t)ip, PINOD); } - ip->i_flag |= ILOCK; + ip->i_spare1 = 0; + ip->i_spare0 = u.u_procp->p_pid; + u.u_spare[0]++; + ip->i_flag |= ILOCKED; } /* * Unlock an inode. If WANT bit is on, wakeup. */ -irele(ip) +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; + u.u_spare[0]--; + ip->i_flag &= ~ILOCKED; if (ip->i_flag&IWANT) { ip->i_flag &= ~IWANT; wakeup((caddr_t)ip);