X-Git-Url: https://git.subgeniuskitty.com/unix-history/.git/blobdiff_plain/23de9f204f2e6d2b17fd99eb756f5ca80ecb715a..cc1730771008ddc8f1bccc5ed3aa8a987856137e:/usr/src/sys/ufs/ffs/ufs_lookup.c diff --git a/usr/src/sys/ufs/ffs/ufs_lookup.c b/usr/src/sys/ufs/ffs/ufs_lookup.c index fff6c92b70..124a0ca411 100644 --- a/usr/src/sys/ufs/ffs/ufs_lookup.c +++ b/usr/src/sys/ufs/ffs/ufs_lookup.c @@ -1,361 +1,164 @@ -/* ufs_lookup.c 6.20 85/02/22 */ - -#include "param.h" -#include "systm.h" -#include "inode.h" -#include "fs.h" -#include "mount.h" -#include "dir.h" -#include "user.h" -#include "buf.h" -#include "conf.h" -#include "uio.h" -#include "kernel.h" - -struct buf *blkatoff(); -struct buf *freenamebuf; -int dirchk = 0; - /* - * Structures associated with name cacheing. + * Copyright (c) 1989 The Regents of the University of California. + * All rights reserved. + * + * %sccs.include.redist.c% + * + * @(#)ufs_lookup.c 7.36 (Berkeley) %G% */ -#define NCHHASH 32 /* size of hash table */ -#if ((NCHHASH)&((NCHHASH)-1)) != 0 -#define NHASH(h, i, d) ((unsigned)((h) + (i) + 13 * (int)(d)) % (NCHHASH)) +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +struct nchstats nchstats; +#ifdef DIAGNOSTIC +int dirchk = 1; #else -#define NHASH(h, i, d) ((unsigned)((h) + (i) + 13 * (int)(d)) & ((NCHHASH)-1)) +int dirchk = 0; #endif -union nchash { - union nchash *nch_head[2]; - struct nch *nch_chain[2]; -} nchash[NCHHASH]; -#define nch_forw nch_chain[0] -#define nch_back nch_chain[1] - -struct nch *nchhead, **nchtail; /* LRU chain pointers */ -struct nchstats nchstats; /* cache effectiveness statistics */ - /* - * Convert a pathname into a pointer to a locked inode, - * with side effects usable in creating and removing files. + * Convert a component of a pathname into a pointer to a locked inode. * This is a very central and rather complicated routine. - * - * The segflg defines whether the name is to be copied from user - * space or kernel space. - * - * The flag argument is (LOOKUP, CREATE, DELETE) depending on whether - * the name is to be (looked up, created, deleted). If flag has - * LOCKPARENT or'ed into it and the target of the pathname exists, - * namei returns both the target and its parent directory locked. * If the file system is not maintained in a strict tree hierarchy, - * this can result in a deadlock situation. When creating and - * LOCKPARENT is specified, the target may not be ".". When deleting - * and LOCKPARENT is specified, the target may be ".", but the caller - * must check to insure it does an irele and iput instead of two iputs. - * - * The FOLLOW flag is set when symbolic links are to be followed - * when they occur at the end of the name translation process. - * - * Name caching works as follows: - * - * names found by directory scans are retained in a cache - * for future reference. It is managed LRU, so frequently - * used names will hang around. Cache is indexed by hash value - * obtained from (ino,dev,name) where ino & dev refer to the - * directory containing name. + * this can result in a deadlock situation (see comments in code below). * - * For simplicity (and economy of storage), names longer than - * some (small) maximum length are not cached, they occur - * infrequently in any case, and are almost never of interest. + * The flag argument is LOOKUP, CREATE, RENAME, or DELETE depending on + * whether the name is to be looked up, created, renamed, or deleted. + * When CREATE, RENAME, or DELETE is specified, information usable in + * creating, renaming, or deleting a directory entry may be calculated. + * If flag has LOCKPARENT or'ed into it and the target of the pathname + * exists, lookup returns both the target and its parent directory locked. + * When creating or renaming and LOCKPARENT is specified, the target may + * not be ".". When deleting and LOCKPARENT is specified, the target may + * be "."., but the caller must check to ensure it does an vrele and iput + * instead of two iputs. * - * Upon reaching the last segment of a path, if the reference - * is for DELETE, or NOCACHE is set (rewrite), and the - * name is located in the cache, it will be dropped. + * Overall outline of ufs_lookup: * - * We must be sure never to enter the name ".." into the cache - * because of the extremely kludgey way that rename() alters - * ".." in a situation like - * mv a/x b/x - * where x is a directory, and x/.. is the ".." in question. - * - * Overall outline of namei: - * - * copy in name - * get starting directory - * dirloop: * check accessibility of directory - * dirloop2: - * copy next component of name to ndp->ni_dent - * handle degenerate case where name is null string * look for name in cache, if found, then if at end of path - * and deleting or creating, drop it, else to haveino + * and deleting or creating, drop it, else return name * search for name in directory, to found or notfound * notfound: - * if creating, return locked directory, leaving info on avail. slots + * if creating, return locked directory, leaving info on available slots * else return error * found: * if at end of path and deleting, return information to allow delete - * if at end of path and rewriting (create and LOCKPARENT), lock target + * if at end of path and rewriting (RENAME and LOCKPARENT), lock target * inode and return info to allow rewrite - * if .. and on mounted filesys, look in mount table for parent - * if not at end, if neither creating nor deleting, add name to cache - * haveino: - * if symbolic link, massage name in buffer and continue at dirloop - * if more components of name, do next level at dirloop - * return the answer as locked inode + * if not at end, add name to cache; if at end and neither creating + * nor deleting, add name to cache * - * NOTE: (LOOKUP | LOCKPARENT) currently returns the parent inode, - * but unlocked. + * NOTE: (LOOKUP | LOCKPARENT) currently returns the parent inode unlocked. */ -struct inode * -namei(ndp) +int +ufs_lookup(vdp, ndp, p) + register struct vnode *vdp; register struct nameidata *ndp; + struct proc *p; { - register char *cp; /* pointer into pathname argument */ -/* these variables refer to things which must be freed or unlocked */ - register struct inode *dp = 0; /* the directory we are searching */ - register struct nch *ncp; /* cache slot for entry */ - register struct fs *fs; /* file system that directory is in */ - register struct buf *bp = 0; /* a buffer of directory entries */ + register struct inode *dp; /* the directory we are searching */ + struct buf *bp; /* a buffer of directory entries */ register struct direct *ep; /* the current directory entry */ int entryoffsetinblock; /* offset of ep in bp's buffer */ - register struct buf *nbp; /* buffer storing path name argument */ -/* these variables hold information about the search for a slot */ enum {NONE, COMPACT, FOUND} slotstatus; - int slotoffset = -1; /* offset of area with free space */ + int slotoffset; /* offset of area with free space */ int slotsize; /* size of area at slotoffset */ int slotfreespace; /* amount of space free in slot */ int slotneeded; /* size of the entry we're seeking */ -/* */ int numdirpasses; /* strategy for directory search */ int endsearch; /* offset to end directory search */ - int prevoff; /* ndp->ni_offset of previous entry */ - int nlink = 0; /* number of symbolic links taken */ + int prevoff; /* prev entry ndp->ni_ufs.ufs_offset */ struct inode *pdp; /* saved dp during symlink work */ - int error, i; - int lockparent; - int docache; - unsigned hash; /* value of name hash for entry */ - union nchash *nhp; /* cache chain head for entry */ - int isdotdot; /* != 0 if current name is ".." */ - int flag; /* op ie, LOOKUP, CREATE, or DELETE */ + struct vnode *tdp; /* returned by VOP_VGET */ off_t enduseful; /* pointer past last used dir slot */ + u_long bmask; /* block offset mask */ + int flag; /* LOOKUP, CREATE, RENAME, or DELETE */ + int lockparent; /* 1 => lockparent flag is set */ + int wantparent; /* 1 => wantparent or lockparent flag */ + int error; + bp = NULL; + slotoffset = -1; + ndp->ni_dvp = vdp; + ndp->ni_vp = NULL; + dp = VTOI(vdp); lockparent = ndp->ni_nameiop & LOCKPARENT; - docache = (ndp->ni_nameiop & NOCACHE) ^ NOCACHE; - flag = ndp->ni_nameiop &~ (LOCKPARENT|NOCACHE|FOLLOW); - if (flag == DELETE) - docache = 0; - /* - * Get a buffer for the name to be translated, and copy the - * name into the buffer. - */ - nbp = freenamebuf; - if (nbp == NULL) - nbp = geteblk(MAXPATHLEN); - else - freenamebuf = nbp->av_forw; - if (ndp->ni_segflg == UIO_SYSSPACE) - error = copystr(ndp->ni_dirp, nbp->b_un.b_addr, MAXPATHLEN, - (u_int *)0); - else - error = copyinstr(ndp->ni_dirp, nbp->b_un.b_addr, MAXPATHLEN, - (u_int *)0); - if (error) { - u.u_error = error; - goto bad; - } + flag = ndp->ni_nameiop & OPMASK; + wantparent = ndp->ni_nameiop & (LOCKPARENT|WANTPARENT); - /* - * Get starting directory. - */ - cp = nbp->b_un.b_addr; - if (*cp == '/') { - while (*cp == '/') - cp++; - if ((dp = u.u_rdir) == NULL) - dp = rootdir; - } else - dp = u.u_cdir; - fs = dp->i_fs; - ILOCK(dp); - dp->i_count++; - ndp->ni_pdir = (struct inode *)0xc0000000; /* illegal */ - ndp->ni_endoff = 0; - - /* - * We come to dirloop to search a new directory. - * The directory must be locked so that it can be - * iput, and fs must be already set to dp->i_fs. - */ -dirloop: /* * Check accessiblity of directory. */ - if ((dp->i_mode&IFMT) != IFDIR) { - u.u_error = ENOTDIR; - goto bad; - } - if (access(dp, IEXEC)) - goto bad; - -dirloop2: - /* - * Copy next component of name to ndp->ni_dent. - */ - hash = 0; - for (i = 0; *cp != 0 && *cp != '/'; cp++) { - if (i >= MAXNAMLEN) { - u.u_error = ENOENT; - goto bad; - } - if ((*cp&0377) == ('/'|0200) || (*cp&0200) && flag != DELETE) { - u.u_error = EPERM; - goto bad; - } - ndp->ni_dent.d_name[i++] = *cp; - hash += (unsigned char)*cp * i; - } - ndp->ni_dent.d_namlen = i; - ndp->ni_dent.d_name[i] = '\0'; - isdotdot = (i == 2 && - ndp->ni_dent.d_name[0] == '.' && ndp->ni_dent.d_name[1] == '.'); - - /* - * Check for degenerate name (e.g. / or "") - * which is a way of talking about a directory, - * e.g. like "/." or ".". - */ - if (ndp->ni_dent.d_name[0] == '\0') { - if (flag != LOOKUP || lockparent) { - u.u_error = EISDIR; - goto bad; - } - nbp->av_forw = freenamebuf; - freenamebuf = nbp; - return (dp); - } + if ((dp->i_mode&IFMT) != IFDIR) + return (ENOTDIR); + if (error = ufs_access(vdp, VEXEC, ndp->ni_cred, p)) + return (error); /* * We now have a segment name to search for, and a directory to search. * * Before tediously performing a linear scan of the directory, * check the name cache to see if the directory/name pair - * we are looking for is known already. We don't do this - * if the segment name is long, simply so the cache can avoid - * holding long names (which would either waste space, or - * add greatly to the complexity). + * we are looking for is known already. */ - if (ndp->ni_dent.d_namlen > NCHNAMLEN) { - nchstats.ncs_long++; - docache = 0; - } else { - nhp = &nchash[NHASH(hash, dp->i_number, dp->i_dev)]; - for (ncp = nhp->nch_forw; ncp != (struct nch *)nhp; - ncp = ncp->nc_forw) { - if (ncp->nc_ino == dp->i_number && - ncp->nc_dev == dp->i_dev && - ncp->nc_nlen == ndp->ni_dent.d_namlen && - !bcmp(ncp->nc_name, ndp->ni_dent.d_name, - ncp->nc_nlen)) - break; - } - - if (ncp == (struct nch *)nhp) { - nchstats.ncs_miss++; - ncp = NULL; + if (error = cache_lookup(ndp)) { + int vpid; /* capability number of vnode */ + + if (error == ENOENT) + return (error); +#ifdef PARANOID + if (vdp == ndp->ni_rdir && ndp->ni_isdotdot) + panic("ufs_lookup: .. through root"); +#endif + /* + * Get the next vnode in the path. + * See comment below starting `Step through' for + * an explaination of the locking protocol. + */ + pdp = dp; + dp = VTOI(ndp->ni_vp); + vdp = ndp->ni_vp; + vpid = vdp->v_id; + if (pdp == dp) { + VREF(vdp); + error = 0; + } else if (ndp->ni_isdotdot) { + IUNLOCK(pdp); + error = vget(vdp); + if (!error && lockparent && *ndp->ni_next == '\0') + ILOCK(pdp); } else { - if (ncp->nc_id != ncp->nc_ip->i_id) { - nchstats.ncs_falsehits++; - } else if (*cp == '\0' && !docache) { - nchstats.ncs_badhits++; - } else { - - /* - * move this slot to end of LRU - * chain, if not already there - */ - if (ncp->nc_nxt) { - /* remove from LRU chain */ - *ncp->nc_prev = ncp->nc_nxt; - ncp->nc_nxt->nc_prev = ncp->nc_prev; - - /* and replace at end of it */ - ncp->nc_nxt = NULL; - ncp->nc_prev = nchtail; - *nchtail = ncp; - nchtail = &ncp->nc_nxt; - } - - /* - * Get the next inode in the path. - * See comment above other `IUNLOCK' code for - * an explaination of the locking protocol. - */ - pdp = dp; - dp = ncp->nc_ip; - if (dp == NULL) - panic("nami: null cache ino"); - if (pdp == dp) - dp->i_count++; - else { - if (isdotdot) { - IUNLOCK(pdp); - igrab(dp); - } else { - igrab(dp); - IUNLOCK(pdp); - } - } - - /* - * Verify that the inode that we got - * did not change while we were waiting - * for it to be locked. - */ - if (ncp->nc_id != ncp->nc_ip->i_id) { - iput(dp); - ILOCK(pdp); - dp = pdp; - nchstats.ncs_falsehits++; - } else { - ndp->ni_dent.d_ino = dp->i_number; - /* ni_dent.d_reclen is garbage ... */ - nchstats.ncs_goodhits++; - goto haveino; - } - } - - /* - * Last component and we are renaming or deleting, - * the cache entry is invalid, or otherwise don't - * want cache entry to exist. - */ - - /* remove from LRU chain */ - *ncp->nc_prev = ncp->nc_nxt; - if (ncp->nc_nxt) - ncp->nc_nxt->nc_prev = ncp->nc_prev; - else - nchtail = ncp->nc_prev; - - /* remove from hash chain */ - remque(ncp); - - /* insert at head of LRU list (first to grab) */ - ncp->nc_nxt = nchhead; - ncp->nc_prev = &nchhead; - nchhead->nc_prev = &ncp->nc_nxt; - nchhead = ncp; - - /* and make a dummy hash chain */ - ncp->nc_forw = ncp; - ncp->nc_back = ncp; - - ncp = NULL; + error = vget(vdp); + if (!lockparent || error || *ndp->ni_next != '\0') + IUNLOCK(pdp); } + /* + * Check that the capability number did not change + * while we were waiting for the lock. + */ + if (!error) { + if (vpid == vdp->v_id) + return (0); + ufs_iput(dp); + if (lockparent && pdp != dp && *ndp->ni_next == '\0') + IUNLOCK(pdp); + } + ILOCK(pdp); + dp = pdp; + vdp = ITOV(dp); + ndp->ni_vp = NULL; } /* @@ -365,40 +168,34 @@ dirloop2: * case it doesn't already exist. */ slotstatus = FOUND; - if (flag == CREATE && *cp == 0) { + if ((flag == CREATE || flag == RENAME) && *ndp->ni_next == 0) { slotstatus = NONE; slotfreespace = 0; - slotneeded = DIRSIZ(&ndp->ni_dent); + slotneeded = ((sizeof (struct direct) - (MAXNAMLEN + 1)) + + ((ndp->ni_namelen + 1 + 3) &~ 3)); } + /* - * If this is the same directory that this process - * previously searched, pick up where we last left off. + * If there is cached information on a previous search of + * this directory, pick up where we last left off. * We cache only lookups as these are the most common * and have the greatest payoff. Caching CREATE has little * benefit as it usually must search the entire directory * to determine that the entry does not exist. Caching the - * location of the last DELETE has not reduced profiling time - * and hence has been removed in the interest of simplicity. + * location of the last DELETE or RENAME has not reduced + * profiling time and hence has been removed in the interest + * of simplicity. */ - if (flag != LOOKUP || dp->i_number != u.u_ncache.nc_inumber || - dp->i_dev != u.u_ncache.nc_dev) { - ndp->ni_offset = 0; + bmask = VFSTOUFS(vdp->v_mount)->um_mountp->mnt_stat.f_iosize - 1; + if (flag != LOOKUP || dp->i_diroff == 0 || dp->i_diroff > dp->i_size) { + ndp->ni_ufs.ufs_offset = 0; numdirpasses = 1; } else { - if ((dp->i_flag & ICHG) || dp->i_ctime >= u.u_ncache.nc_time) { - if (u.u_ncache.nc_prevoffset > dp->i_size) - u.u_ncache.nc_prevoffset = 0; - else - u.u_ncache.nc_prevoffset &= ~(DIRBLKSIZ - 1); - u.u_ncache.nc_time = time.tv_sec; - } - ndp->ni_offset = u.u_ncache.nc_prevoffset; - entryoffsetinblock = blkoff(fs, ndp->ni_offset); - if (entryoffsetinblock != 0) { - bp = blkatoff(dp, ndp->ni_offset, (char **)0); - if (bp == 0) - goto bad; - } + ndp->ni_ufs.ufs_offset = dp->i_diroff; + if ((entryoffsetinblock = ndp->ni_ufs.ufs_offset & bmask) && + (error = VOP_BLKATOFF(vdp, ndp->ni_ufs.ufs_offset, NULL, + &bp))) + return (error); numdirpasses = 2; nchstats.ncs_2passes++; } @@ -406,31 +203,28 @@ dirloop2: enduseful = 0; searchloop: - while (ndp->ni_offset < endsearch) { + while (ndp->ni_ufs.ufs_offset < endsearch) { /* - * If offset is on a block boundary, - * read the next directory block. - * Release previous if it exists. + * If offset is on a block boundary, read the next directory + * block. Release previous if it exists. */ - if (blkoff(fs, ndp->ni_offset) == 0) { + if ((ndp->ni_ufs.ufs_offset & bmask) == 0) { if (bp != NULL) brelse(bp); - bp = blkatoff(dp, ndp->ni_offset, (char **)0); - if (bp == 0) - goto bad; + if (error = VOP_BLKATOFF(vdp, ndp->ni_ufs.ufs_offset, + NULL, &bp)) + return (error); entryoffsetinblock = 0; } - /* * If still looking for a slot, and at a DIRBLKSIZE * boundary, have to start looking for free space again. */ if (slotstatus == NONE && - (entryoffsetinblock&(DIRBLKSIZ-1)) == 0) { + (entryoffsetinblock & (DIRBLKSIZ - 1)) == 0) { slotoffset = -1; slotfreespace = 0; } - /* * Get pointer to next entry. * Full validation checks are slow, so we only check @@ -439,11 +233,13 @@ searchloop: * "dirchk" to be true. */ ep = (struct direct *)(bp->b_un.b_addr + entryoffsetinblock); - if (ep->d_reclen <= 0 || - dirchk && dirbadentry(ep, entryoffsetinblock)) { - dirbad(dp, ndp->ni_offset, "mangled entry"); + if (ep->d_reclen == 0 || + dirchk && ufs_dirbadentry(ep, entryoffsetinblock)) { + int i; + + ufs_dirbad(dp, ndp->ni_ufs.ufs_offset, "mangled entry"); i = DIRBLKSIZ - (entryoffsetinblock & (DIRBLKSIZ - 1)); - ndp->ni_offset += i; + ndp->ni_ufs.ufs_offset += i; entryoffsetinblock += i; continue; } @@ -462,15 +258,17 @@ searchloop: if (size > 0) { if (size >= slotneeded) { slotstatus = FOUND; - slotoffset = ndp->ni_offset; + slotoffset = ndp->ni_ufs.ufs_offset; slotsize = ep->d_reclen; } else if (slotstatus == NONE) { slotfreespace += size; if (slotoffset == -1) - slotoffset = ndp->ni_offset; + slotoffset = + ndp->ni_ufs.ufs_offset; if (slotfreespace >= slotneeded) { slotstatus = COMPACT; - slotsize = ndp->ni_offset + + slotsize = + ndp->ni_ufs.ufs_offset + ep->d_reclen - slotoffset; } } @@ -481,16 +279,25 @@ searchloop: * Check for a name match. */ if (ep->d_ino) { - if (ep->d_namlen == ndp->ni_dent.d_namlen && - !bcmp(ndp->ni_dent.d_name, ep->d_name, - ep->d_namlen)) + if (ep->d_namlen == ndp->ni_namelen && + !bcmp(ndp->ni_ptr, ep->d_name, + (unsigned)ep->d_namlen)) { + /* + * Save directory entry's inode number and + * reclen in ndp->ni_ufs area, and release + * directory buffer. + */ + ndp->ni_ufs.ufs_ino = ep->d_ino; + ndp->ni_ufs.ufs_reclen = ep->d_reclen; + brelse(bp); goto found; + } } - prevoff = ndp->ni_offset; - ndp->ni_offset += ep->d_reclen; + prevoff = ndp->ni_ufs.ufs_offset; + ndp->ni_ufs.ufs_offset += ep->d_reclen; entryoffsetinblock += ep->d_reclen; if (ep->d_ino) - enduseful = ndp->ni_offset; + enduseful = ndp->ni_ufs.ufs_offset; } /* notfound: */ /* @@ -499,59 +306,70 @@ searchloop: */ if (numdirpasses == 2) { numdirpasses--; - ndp->ni_offset = 0; - endsearch = u.u_ncache.nc_prevoffset; + ndp->ni_ufs.ufs_offset = 0; + endsearch = dp->i_diroff; goto searchloop; } + if (bp != NULL) + brelse(bp); /* * If creating, and at end of pathname and current * directory has not been removed, then can consider * allowing file to be created. */ - if (flag == CREATE && *cp == 0 && dp->i_nlink != 0) { + if ((flag == CREATE || flag == RENAME) && + *ndp->ni_next == 0 && dp->i_nlink != 0) { /* * Access for write is interpreted as allowing * creation of files in the directory. */ - if (access(dp, IWRITE)) - goto bad; + if (error = ufs_access(vdp, VWRITE, ndp->ni_cred, p)) + return (error); /* * Return an indication of where the new directory * entry should be put. If we didn't find a slot, - * then set ndp->ni_count to 0 indicating that the new - * slot belongs at the end of the directory. If we found - * a slot, then the new entry can be put in the range - * [ndp->ni_offset .. ndp->ni_offset + ndp->ni_count) + * then set ndp->ni_ufs.ufs_count to 0 indicating + * that the new slot belongs at the end of the + * directory. If we found a slot, then the new entry + * can be put in the range from ndp->ni_ufs.ufs_offset + * to ndp->ni_ufs.ufs_offset + ndp->ni_ufs.ufs_count. */ if (slotstatus == NONE) { - ndp->ni_offset = roundup(dp->i_size, DIRBLKSIZ); - ndp->ni_count = 0; - enduseful = ndp->ni_offset; + ndp->ni_ufs.ufs_offset = roundup(dp->i_size, DIRBLKSIZ); + ndp->ni_ufs.ufs_count = 0; + enduseful = ndp->ni_ufs.ufs_offset; } else { - ndp->ni_offset = slotoffset; - ndp->ni_count = slotsize; + ndp->ni_ufs.ufs_offset = slotoffset; + ndp->ni_ufs.ufs_count = slotsize; if (enduseful < slotoffset + slotsize) enduseful = slotoffset + slotsize; } - ndp->ni_endoff = roundup(enduseful, DIRBLKSIZ); + ndp->ni_ufs.ufs_endoff = roundup(enduseful, DIRBLKSIZ); dp->i_flag |= IUPD|ICHG; - if (bp) - brelse(bp); - nbp->av_forw = freenamebuf; - freenamebuf = nbp; /* * We return with the directory locked, so that * the parameters we set up above will still be * valid if we actually decide to do a direnter(). - * We return NULL to indicate that the entry doesn't - * currently exist, leaving a pointer to the (locked) - * directory inode in ndp->ni_pdir. + * We return ni_vp == NULL to indicate that the entry + * does not currently exist; we leave a pointer to + * the (locked) directory inode in ndp->ni_dvp. + * The pathname buffer is saved so that the name + * can be obtained later. + * + * NB - if the directory is unlocked, then this + * information cannot be used. */ - ndp->ni_pdir = dp; - return (NULL); + ndp->ni_nameiop |= SAVENAME; + if (!lockparent) + IUNLOCK(dp); } - u.u_error = ENOENT; - goto bad; + /* + * Insert name into cache (as non-existent) if appropriate. + */ + if (ndp->ni_makeentry && flag != CREATE) + cache_enter(ndp); + return (ENOENT); + found: if (numdirpasses == 2) nchstats.ncs_pass2++; @@ -560,7 +378,7 @@ found: * of this entry. */ if (entryoffsetinblock + DIRSIZ(ep) > dp->i_size) { - dirbad(dp, ndp->ni_offset, "i_size too small"); + ufs_dirbad(dp, ndp->ni_ufs.ufs_offset, "i_size too small"); dp->i_size = entryoffsetinblock + DIRSIZ(ep); dp->i_flag |= IUPD|ICHG; } @@ -570,130 +388,86 @@ found: * If the final component of path name, save information * in the cache as to where the entry was found. */ - if (*cp == '\0' && flag == LOOKUP) { - u.u_ncache.nc_prevoffset = ndp->ni_offset; - u.u_ncache.nc_inumber = dp->i_number; - u.u_ncache.nc_dev = dp->i_dev; - u.u_ncache.nc_time = time.tv_sec; - } - /* - * Save directory entry in ndp->ni_dent, - * and release directory buffer. - */ - bcopy((caddr_t)ep, (caddr_t)&ndp->ni_dent, (u_int)DIRSIZ(ep)); - brelse(bp); - bp = NULL; + if (*ndp->ni_next == '\0' && flag == LOOKUP) + dp->i_diroff = ndp->ni_ufs.ufs_offset &~ (DIRBLKSIZ - 1); /* * If deleting, and at end of pathname, return * parameters which can be used to remove file. - * If the lockparent flag isn't set, we return only - * the directory (in ndp->ni_pdir), otherwise we go + * If the wantparent flag isn't set, we return only + * the directory (in ndp->ni_dvp), otherwise we go * on and lock the inode, being careful with ".". */ - if (flag == DELETE && *cp == 0) { + if (flag == DELETE && *ndp->ni_next == 0) { /* * Write access to directory required to delete files. */ - if (access(dp, IWRITE)) - goto bad; - ndp->ni_pdir = dp; /* for dirremove() */ + if (error = ufs_access(vdp, VWRITE, ndp->ni_cred, p)) + return (error); /* - * Return pointer to current entry in ndp->ni_offset, + * Return pointer to current entry in ndp->ni_ufs.ufs_offset, * and distance past previous entry (if there - * is a previous entry in this block) in ndp->ni_count. - * Save directory inode pointer in ndp->ni_pdir for dirremove(). + * is a previous entry in this block) in ndp->ni_ufs.ufs_count. + * Save directory inode pointer in ndp->ni_dvp for dirremove(). */ - if ((ndp->ni_offset&(DIRBLKSIZ-1)) == 0) - ndp->ni_count = 0; + if ((ndp->ni_ufs.ufs_offset&(DIRBLKSIZ-1)) == 0) + ndp->ni_ufs.ufs_count = 0; else - ndp->ni_count = ndp->ni_offset - prevoff; - if (lockparent) { - if (dp->i_number == ndp->ni_dent.d_ino) - dp->i_count++; - else { - dp = iget(dp->i_dev, fs, ndp->ni_dent.d_ino); - if (dp == NULL) { - iput(ndp->ni_pdir); - goto bad; - } - /* - * If directory is "sticky", then user must own - * the directory, or the file in it, else he - * may not delete it (unless he's root). This - * implements append-only directories. - */ - if ((ndp->ni_pdir->i_mode & ISVTX) && - u.u_uid != 0 && - u.u_uid != ndp->ni_pdir->i_uid && - dp->i_uid != u.u_uid) { - iput(ndp->ni_pdir); - u.u_error = EPERM; - goto bad; - } - } + ndp->ni_ufs.ufs_count = + ndp->ni_ufs.ufs_offset - prevoff; + if (dp->i_number == ndp->ni_ufs.ufs_ino) { + VREF(vdp); + ndp->ni_vp = vdp; + return (0); } - nbp->av_forw = freenamebuf; - freenamebuf = nbp; - return (dp); - } - - /* - * Special handling for ".." allowing chdir out of mounted - * file system: indirect .. in root inode to reevaluate - * in directory file system was mounted on. - */ - if (isdotdot) { - if (dp == u.u_rdir) - ndp->ni_dent.d_ino = dp->i_number; - else if (ndp->ni_dent.d_ino == ROOTINO && - dp->i_number == ROOTINO) { - for (i = 1; i < NMOUNT; i++) - if (mount[i].m_bufp != NULL && - mount[i].m_dev == dp->i_dev) { - iput(dp); - dp = mount[i].m_inodp; - ILOCK(dp); - dp->i_count++; - fs = dp->i_fs; - cp -= 2; /* back over .. */ - goto dirloop2; - } + if (error = VOP_VGET(vdp, ndp->ni_ufs.ufs_ino, &tdp)) + return (error); + /* + * If directory is "sticky", then user must own + * the directory, or the file in it, else she + * may not delete it (unless she's root). This + * implements append-only directories. + */ + if ((dp->i_mode & ISVTX) && + ndp->ni_cred->cr_uid != 0 && + ndp->ni_cred->cr_uid != dp->i_uid && + VTOI(tdp)->i_uid != ndp->ni_cred->cr_uid) { + vput(tdp); + return (EPERM); } + ndp->ni_vp = tdp; + if (!lockparent) + IUNLOCK(dp); + return (0); } /* - * If rewriting (rename), return the inode and the + * If rewriting (RENAME), return the inode and the * information required to rewrite the present directory * Must get inode of directory entry to verify it's a - * regular file, or empty directory. + * regular file, or empty directory. */ - if ((flag == CREATE && lockparent) && *cp == 0) { - if (access(dp, IWRITE)) - goto bad; - ndp->ni_pdir = dp; /* for dirrewrite() */ + if (flag == RENAME && wantparent && *ndp->ni_next == 0) { + if (error = ufs_access(vdp, VWRITE, ndp->ni_cred, p)) + return (error); /* - * Careful about locking second inode. - * This can only occur if the target is ".". + * Careful about locking second inode. + * This can only occur if the target is ".". */ - if (dp->i_number == ndp->ni_dent.d_ino) { - u.u_error = EISDIR; /* XXX */ - goto bad; - } - dp = iget(dp->i_dev, fs, ndp->ni_dent.d_ino); - if (dp == NULL) { - iput(ndp->ni_pdir); - goto bad; - } - nbp->av_forw = freenamebuf; - freenamebuf = nbp; - return (dp); + if (dp->i_number == ndp->ni_ufs.ufs_ino) + return (EISDIR); + if (error = VOP_VGET(vdp, ndp->ni_ufs.ufs_ino, &tdp)) + return (error); + ndp->ni_vp = tdp; + ndp->ni_nameiop |= SAVENAME; + if (!lockparent) + IUNLOCK(dp); + return (0); } /* - * Check for symbolic link, which may require us to massage the - * name before we continue translation. We do not `iput' the - * directory because we may need it again if the symbolic link + * Step through the translation in the name. We do not `iput' the + * directory because we may need it again if a symbolic link * is relative to the current directory. Instead we save it * unlocked as "pdp". We must get the target inode before unlocking * the directory to insure that the inode will not be removed @@ -711,219 +485,158 @@ found: * that point backwards in the directory structure. */ pdp = dp; - if (isdotdot) { + if (ndp->ni_isdotdot) { IUNLOCK(pdp); /* race to get the inode */ - dp = iget(dp->i_dev, fs, ndp->ni_dent.d_ino); - if (dp == NULL) - goto bad2; - } else if (dp->i_number == ndp->ni_dent.d_ino) { - dp->i_count++; /* we want ourself, ie "." */ - } else { - dp = iget(dp->i_dev, fs, ndp->ni_dent.d_ino); - IUNLOCK(pdp); - if (dp == NULL) - goto bad2; - } - - /* - * insert name into cache (if we want it, and it isn't "." or "..") - * - * all other cases where making a cache entry would be wrong - * have already departed from the code sequence somewhere above. - */ - if (docache) { - if (ncp != NULL) - panic("nami: duplicating cache"); - - /* - * free the cache slot at head of lru chain - */ - if (ncp = nchhead) { - /* remove from lru chain */ - *ncp->nc_prev = ncp->nc_nxt; - if (ncp->nc_nxt) - ncp->nc_nxt->nc_prev = ncp->nc_prev; - else - nchtail = ncp->nc_prev; - - /* remove from old hash chain */ - remque(ncp); - - /* grab the inode we just found */ - ncp->nc_ip = dp; - - /* fill in cache info */ - ncp->nc_ino = pdp->i_number; /* parents inum */ - ncp->nc_dev = pdp->i_dev; /* & device */ - ncp->nc_idev = dp->i_dev; /* our device */ - ncp->nc_id = dp->i_id; /* identifier */ - ncp->nc_nlen = ndp->ni_dent.d_namlen; - bcopy(ndp->ni_dent.d_name, ncp->nc_name, ncp->nc_nlen); - - /* link at end of lru chain */ - ncp->nc_nxt = NULL; - ncp->nc_prev = nchtail; - *nchtail = ncp; - nchtail = &ncp->nc_nxt; - - /* and insert on hash chain */ - insque(ncp, nhp); - } - } - -haveino: - fs = dp->i_fs; - - /* - * Check for symbolic link - */ - if ((dp->i_mode & IFMT) == IFLNK && - ((ndp->ni_nameiop & FOLLOW) || *cp == '/')) { - u_int pathlen = strlen(cp) + 1; - - if (dp->i_size + pathlen >= MAXPATHLEN - 1 || - ++nlink > MAXSYMLINKS) { - u.u_error = ELOOP; - goto bad2; - } - ovbcopy(cp, nbp->b_un.b_addr + dp->i_size, pathlen); - u.u_error = - rdwri(UIO_READ, dp, nbp->b_un.b_addr, (int)dp->i_size, - 0, 1, (int *)0); - if (u.u_error) - goto bad2; - cp = nbp->b_un.b_addr; - iput(dp); - if (*cp == '/') { - irele(pdp); - while (*cp == '/') - cp++; - if ((dp = u.u_rdir) == NULL) - dp = rootdir; - ILOCK(dp); - dp->i_count++; - } else { - dp = pdp; - ILOCK(dp); + if (error = VOP_VGET(vdp, ndp->ni_ufs.ufs_ino, &tdp)) { + ILOCK(pdp); + return (error); } - fs = dp->i_fs; - goto dirloop; + if (lockparent && *ndp->ni_next == '\0') + ILOCK(pdp); + ndp->ni_vp = tdp; + } else if (dp->i_number == ndp->ni_ufs.ufs_ino) { + VREF(vdp); /* we want ourself, ie "." */ + ndp->ni_vp = vdp; + } else { + if (error = VOP_VGET(vdp, ndp->ni_ufs.ufs_ino, &tdp)) + return (error); + if (!lockparent || *ndp->ni_next != '\0') + IUNLOCK(pdp); + ndp->ni_vp = tdp; } /* - * Not a symbolic link. If more pathname, - * continue at next component, else return. + * Insert name into cache if appropriate. */ - if (*cp == '/') { - while (*cp == '/') - cp++; - irele(pdp); - goto dirloop; - } - nbp->av_forw = freenamebuf; - freenamebuf = nbp; - if (lockparent) - ndp->ni_pdir = pdp; - else - irele(pdp); - return (dp); -bad2: - irele(pdp); -bad: - if (bp) - brelse(bp); - if (dp) - iput(dp); - nbp->av_forw = freenamebuf; - freenamebuf = nbp; - return (NULL); + if (ndp->ni_makeentry) + cache_enter(ndp); + return (0); } - -dirbad(ip, offset, how) +void +ufs_dirbad(ip, offset, how) struct inode *ip; off_t offset; char *how; { + struct mount *mp; - printf("%s: bad dir ino %d at offset %d: %s\n", - ip->i_fs->fs_fsmnt, ip->i_number, offset, how); + mp = ITOV(ip)->v_mount; + (void)printf("%s: bad dir ino %d at offset %d: %s\n", + mp->mnt_stat.f_mntonname, ip->i_number, offset, how); + if ((mp->mnt_stat.f_flags & MNT_RDONLY) == 0) + panic("bad dir"); } /* * Do consistency checking on a directory entry: * record length must be multiple of 4 - * record length must not be non-negative * entry must fit in rest of its DIRBLKSIZ block * record must be large enough to contain entry * name is not longer than MAXNAMLEN * name must be as long as advertised, and null terminated */ -dirbadentry(ep, entryoffsetinblock) +int +ufs_dirbadentry(ep, entryoffsetinblock) register struct direct *ep; int entryoffsetinblock; { register int i; - if ((ep->d_reclen & 0x3) != 0 || ep->d_reclen <= 0 || + if ((ep->d_reclen & 0x3) != 0 || ep->d_reclen > DIRBLKSIZ - (entryoffsetinblock & (DIRBLKSIZ - 1)) || - ep->d_reclen < DIRSIZ(ep) || ep->d_namlen > MAXNAMLEN) - return (1); + ep->d_reclen < DIRSIZ(ep) || ep->d_namlen > MAXNAMLEN) { + /*return (1); */ + printf("First bad\n"); + goto bad; + } for (i = 0; i < ep->d_namlen; i++) - if (ep->d_name[i] == '\0') - return (1); + if (ep->d_name[i] == '\0') { + /*return (1); */ + printf("Second bad\n"); + goto bad; + } + if (ep->d_name[i]) + goto bad; return (ep->d_name[i]); +bad: +printf("ufs_dirbadentry: jumping out: reclen: %d namlen %d ino %d name %s\n", + ep->d_reclen, ep->d_namlen, ep->d_ino, ep->d_name ); + return(1); } /* * Write a directory entry after a call to namei, using the parameters - * which it left in the u. area. The argument ip is the inode which - * the new directory entry will refer to. The u. area field ndp->ni_pdir is - * a pointer to the directory to be written, which was left locked by - * namei. Remaining parameters (ndp->ni_offset, ndp->ni_count) indicate - * how the space for the new entry is to be gotten. + * that it left in nameidata. The argument ip is the inode which the new + * directory entry will refer to. The nameidata field ndp->ni_dvp is a + * pointer to the directory to be written, which was left locked by namei. + * Remaining parameters (ndp->ni_ufs.ufs_offset, ndp->ni_ufs.ufs_count) + * indicate how the space for the new entry is to be obtained. */ -direnter(ip, ndp) +int +ufs_direnter(ip, ndp) struct inode *ip; register struct nameidata *ndp; { register struct direct *ep, *nep; - register struct inode *dp = ndp->ni_pdir; + register struct inode *dp; + register struct vnode *dvp; struct buf *bp; - int loc, spacefree, error = 0; + struct direct newdir; + struct iovec aiov; + struct uio auio; u_int dsize; - int newentrysize; + int error, loc, newentrysize, spacefree; char *dirbuf; - ndp->ni_dent.d_ino = ip->i_number; - newentrysize = DIRSIZ(&ndp->ni_dent); - if (ndp->ni_count == 0) { +#ifdef DIAGNOSTIC + if ((ndp->ni_nameiop & SAVENAME) == 0) + panic("direnter: missing name"); +#endif + dvp = ndp->ni_dvp; + dp = VTOI(dvp); + newdir.d_ino = ip->i_number; + newdir.d_namlen = ndp->ni_namelen; + bcopy(ndp->ni_ptr, newdir.d_name, (unsigned)ndp->ni_namelen + 1); + newentrysize = DIRSIZ(&newdir); + if (ndp->ni_ufs.ufs_count == 0) { /* - * If ndp->ni_count is 0, then namei could find no space in the - * directory. In this case ndp->ni_offset will be on a directory - * block boundary and we will write the new entry into a fresh - * block. + * If ndp->ni_ufs.ufs_count is 0, then namei could find no + * space in the directory. Here, ndp->ni_ufs.ufs_offset will + * be on a directory block boundary and we will write the + * new entry into a fresh block. */ - if (ndp->ni_offset&(DIRBLKSIZ-1)) + if (ndp->ni_ufs.ufs_offset & (DIRBLKSIZ - 1)) panic("wdir: newblk"); - ndp->ni_dent.d_reclen = DIRBLKSIZ; - error = rdwri(UIO_WRITE, dp, (caddr_t)&ndp->ni_dent, - newentrysize, ndp->ni_offset, 1, (int *)0); - if (DIRBLKSIZ > dp->i_fs->fs_fsize) - panic("wdir: blksize"); /* XXX - should grow w/bmap() */ - else + auio.uio_offset = ndp->ni_ufs.ufs_offset; + newdir.d_reclen = DIRBLKSIZ; + auio.uio_resid = newentrysize; + aiov.iov_len = newentrysize; + aiov.iov_base = (caddr_t)&newdir; + auio.uio_iov = &aiov; + auio.uio_iovcnt = 1; + auio.uio_rw = UIO_WRITE; + auio.uio_segflg = UIO_SYSSPACE; + auio.uio_procp = (struct proc *)0; + error = VOP_WRITE(dvp, &auio, IO_SYNC, ndp->ni_cred); + if (DIRBLKSIZ > + VFSTOUFS(dvp->v_mount)->um_mountp->mnt_stat.f_bsize) + /* XXX should grow with balloc() */ + panic("ufs_direnter: frag size"); + else if (!error) { dp->i_size = roundup(dp->i_size, DIRBLKSIZ); - iput(dp); + dp->i_flag |= ICHG; + } return (error); } /* - * If ndp->ni_count is non-zero, then namei found space for the new - * entry in the range ndp->ni_offset to ndp->ni_offset + ndp->ni_count. - * in the directory. To use this space, we may have to compact - * the entries located there, by copying them together towards - * the beginning of the block, leaving the free space in - * one usable chunk at the end. + * If ndp->ni_ufs.ufs_count is non-zero, then namei found space + * for the new entry in the range ndp->ni_ufs.ufs_offset to + * ndp->ni_ufs.ufs_offset + ndp->ni_ufs.ufs_count in the directory. + * To use this space, we may have to compact the entries located + * there, by copying them together towards the beginning of the + * block, leaving the free space in one usable chunk at the end. */ /* @@ -933,29 +646,24 @@ direnter(ip, ndp) * * N.B. - THIS IS AN ARTIFACT OF 4.2 AND SHOULD NEVER HAPPEN. */ - if (ndp->ni_offset + ndp->ni_count > dp->i_size) - dp->i_size = ndp->ni_offset + ndp->ni_count; - + if (ndp->ni_ufs.ufs_offset + ndp->ni_ufs.ufs_count > dp->i_size) + dp->i_size = ndp->ni_ufs.ufs_offset + ndp->ni_ufs.ufs_count; /* - * Get the block containing the space for the new directory - * entry. Should return error by result instead of u.u_error. + * Get the block containing the space for the new directory entry. */ - bp = blkatoff(dp, ndp->ni_offset, (char **)&dirbuf); - if (bp == 0) { - iput(dp); - return (u.u_error); - } - + if (error = VOP_BLKATOFF(dvp, ndp->ni_ufs.ufs_offset, &dirbuf, &bp)) + return (error); /* - * Find space for the new entry. In the simple case, the - * entry at offset base will have the space. If it does - * not, then namei arranged that compacting the region - * ndp->ni_offset to ndp->ni_offset+ndp->ni_count would yield the space. + * Find space for the new entry. In the simple case, the entry at + * offset base will have the space. If it does not, then namei + * arranged that compacting the region ndp->ni_ufs.ufs_offset to + * ndp->ni_ufs.ufs_offset + ndp->ni_ufs.ufs_count would yield the + * space. */ ep = (struct direct *)dirbuf; dsize = DIRSIZ(ep); spacefree = ep->d_reclen - dsize; - for (loc = ep->d_reclen; loc < ndp->ni_count; ) { + for (loc = ep->d_reclen; loc < ndp->ni_ufs.ufs_count; ) { nep = (struct direct *)(dirbuf + loc); if (ep->d_ino) { /* trim the existing slot */ @@ -963,7 +671,7 @@ direnter(ip, ndp) ep = (struct direct *)((char *)ep + dsize); } else { /* overwrite; nothing there; header is ours */ - spacefree += dsize; + spacefree += dsize; } dsize = DIRSIZ(nep); spacefree += nep->d_reclen - dsize; @@ -977,62 +685,68 @@ direnter(ip, ndp) if (ep->d_ino == 0) { if (spacefree + dsize < newentrysize) panic("wdir: compact1"); - ndp->ni_dent.d_reclen = spacefree + dsize; + newdir.d_reclen = spacefree + dsize; } else { if (spacefree < newentrysize) panic("wdir: compact2"); - ndp->ni_dent.d_reclen = spacefree; + newdir.d_reclen = spacefree; ep->d_reclen = dsize; ep = (struct direct *)((char *)ep + dsize); } - bcopy((caddr_t)&ndp->ni_dent, (caddr_t)ep, (u_int)newentrysize); - bwrite(bp); + bcopy((caddr_t)&newdir, (caddr_t)ep, (u_int)newentrysize); + error = VOP_BWRITE(bp); dp->i_flag |= IUPD|ICHG; - if (ndp->ni_endoff && ndp->ni_endoff < dp->i_size) - itrunc(dp, ndp->ni_endoff); - iput(dp); + if (!error && ndp->ni_ufs.ufs_endoff && + ndp->ni_ufs.ufs_endoff < dp->i_size) + error = VOP_TRUNCATE(dvp, (u_long)ndp->ni_ufs.ufs_endoff, + IO_SYNC); return (error); } /* - * Remove a directory entry after a call to namei, using the - * parameters which it left in the u. area. The u. entry - * ni_offset contains the offset into the directory of the - * entry to be eliminated. The ni_count field contains the + * Remove a directory entry after a call to namei, using + * the parameters which it left in nameidata. The entry + * ni_ufs.ufs_offset contains the offset into the directory of the + * entry to be eliminated. The ni_ufs.ufs_count field contains the * size of the previous record in the directory. If this * is 0, the first entry is being deleted, so we need only * zero the inode number to mark the entry as free. If the - * entry isn't the first in the directory, we must reclaim + * entry is not the first in the directory, we must reclaim * the space of the now empty record by adding the record size * to the size of the previous entry. */ -dirremove(ndp) +int +ufs_dirremove(ndp) register struct nameidata *ndp; { - register struct inode *dp = ndp->ni_pdir; - register struct buf *bp; + register struct inode *dp; struct direct *ep; + struct buf *bp; + int error; - if (ndp->ni_count == 0) { + dp = VTOI(ndp->ni_dvp); + if (ndp->ni_ufs.ufs_count == 0) { /* * First entry in block: set d_ino to zero. */ - ndp->ni_dent.d_ino = 0; - (void) rdwri(UIO_WRITE, dp, (caddr_t)&ndp->ni_dent, - (int)DIRSIZ(&ndp->ni_dent), ndp->ni_offset, 1, (int *)0); - } else { - /* - * Collapse new free space into previous entry. - */ - bp = blkatoff(dp, (int)(ndp->ni_offset - ndp->ni_count), - (char **)&ep); - if (bp == 0) - return (0); - ep->d_reclen += ndp->ni_dent.d_reclen; - bwrite(bp); + if (error = VOP_BLKATOFF(ndp->ni_dvp, ndp->ni_ufs.ufs_offset, + (char **)&ep, &bp)) + return (error); + ep->d_ino = 0; + error = VOP_BWRITE(bp); dp->i_flag |= IUPD|ICHG; + return (error); } - return (1); + /* + * Collapse new free space into previous entry. + */ + if (error = VOP_BLKATOFF(ndp->ni_dvp, + ndp->ni_ufs.ufs_offset - ndp->ni_ufs.ufs_count, (char **)&ep, &bp)) + return (error); + ep->d_reclen += ndp->ni_ufs.ufs_reclen; + error = VOP_BWRITE(bp); + dp->i_flag |= IUPD|ICHG; + return (error); } /* @@ -1040,46 +754,22 @@ dirremove(ndp) * supplied. The parameters describing the directory entry are * set up by a call to namei. */ -dirrewrite(dp, ip, ndp) +int +ufs_dirrewrite(dp, ip, ndp) struct inode *dp, *ip; struct nameidata *ndp; { + struct buf *bp; + struct direct *ep; + int error; - ndp->ni_dent.d_ino = ip->i_number; - u.u_error = rdwri(UIO_WRITE, dp, (caddr_t)&ndp->ni_dent, - (int)DIRSIZ(&ndp->ni_dent), ndp->ni_offset, 1, (int *)0); - iput(dp); -} - -/* - * Return buffer with contents of block "offset" - * from the beginning of directory "ip". If "res" - * is non-zero, fill it in with a pointer to the - * remaining space in the directory. - */ -struct buf * -blkatoff(ip, offset, res) - struct inode *ip; - off_t offset; - char **res; -{ - register struct fs *fs = ip->i_fs; - daddr_t lbn = lblkno(fs, offset); - int base = blkoff(fs, offset); - int bsize = blksize(fs, ip, lbn); - daddr_t bn = fsbtodb(fs, bmap(ip, lbn, B_WRITE, base, bsize)); - register struct buf *bp; - - if (u.u_error) - return (0); - bp = bread(ip->i_dev, bn, bsize); - if (bp->b_flags & B_ERROR) { - brelse(bp); - return (0); - } - if (res) - *res = bp->b_un.b_addr + base; - return (bp); + if (error = VOP_BLKATOFF(ITOV(dp), ndp->ni_ufs.ufs_offset, + (char **)&ep, &bp)) + return (error); + ep->d_ino = ip->i_number; + error = VOP_BWRITE(bp); + dp->i_flag |= IUPD|ICHG; + return (error); } /* @@ -1091,9 +781,11 @@ blkatoff(ip, offset, res) * * NB: does not handle corrupted directories. */ -dirempty(ip, parentino) +int +ufs_dirempty(ip, parentino, cred) register struct inode *ip; ino_t parentino; + struct ucred *cred; { register off_t off; struct dirtemplate dbuf; @@ -1102,16 +794,17 @@ dirempty(ip, parentino) #define MINDIRSIZ (sizeof (struct dirtemplate) / 2) for (off = 0; off < ip->i_size; off += dp->d_reclen) { - if (dp->d_reclen <= 0) - return (0); - error = rdwri(UIO_READ, ip, (caddr_t)dp, MINDIRSIZ, - off, 1, &count); + error = vn_rdwr(UIO_READ, ITOV(ip), (caddr_t)dp, MINDIRSIZ, off, + UIO_SYSSPACE, IO_NODELOCKED, cred, &count, (struct proc *)0); /* * Since we read MINDIRSIZ, residual must * be 0 unless we're at end of file. */ if (error || count != 0) return (0); + /* avoid infinite loops */ + if (dp->d_reclen == 0) + return (0); /* skip empty entries */ if (dp->d_ino == 0) continue; @@ -1137,21 +830,26 @@ dirempty(ip, parentino) /* * Check if source directory is in the path of the target directory. * Target is supplied locked, source is unlocked. - * The target is always iput() before returning. + * The target is always iput before returning. */ -checkpath(source, target) +int +ufs_checkpath(source, target, cred) struct inode *source, *target; + struct ucred *cred; { struct dirtemplate dirbuf; register struct inode *ip; - int error = 0; + struct vnode *vp; + int error, rootino; ip = target; if (ip->i_number == source->i_number) { error = EEXIST; goto out; } - if (ip->i_number == ROOTINO) + rootino = ROOTINO; + error = 0; + if (ip->i_number == rootino) goto out; for (;;) { @@ -1159,8 +857,10 @@ checkpath(source, target) error = ENOTDIR; break; } - error = rdwri(UIO_READ, ip, (caddr_t)&dirbuf, - sizeof (struct dirtemplate), (off_t)0, 1, (int *)0); + vp = ITOV(ip); + error = vn_rdwr(UIO_READ, vp, (caddr_t)&dirbuf, + sizeof (struct dirtemplate), (off_t)0, UIO_SYSSPACE, + IO_NODELOCKED, cred, (int *)0, (struct proc *)0); if (error != 0) break; if (dirbuf.dotdot_namlen != 2 || @@ -1173,112 +873,18 @@ checkpath(source, target) error = EINVAL; break; } - if (dirbuf.dotdot_ino == ROOTINO) + if (dirbuf.dotdot_ino == rootino) break; - iput(ip); - ip = iget(ip->i_dev, ip->i_fs, dirbuf.dotdot_ino); - if (ip == NULL) { - error = u.u_error; + ufs_iput(ip); + if (error = VOP_VGET(vp, dirbuf.dotdot_ino, &vp)) break; - } + ip = VTOI(vp); } out: if (error == ENOTDIR) printf("checkpath: .. not a directory\n"); if (ip != NULL) - iput(ip); + ufs_iput(ip); return (error); } - -/* - * Name cache initialization, from main() when we are booting - */ -nchinit() -{ - register union nchash *nchp; - register struct nch *ncp; - - nchhead = 0; - nchtail = &nchhead; - - for (ncp = nch; ncp < &nch[nchsize]; ncp++) { - ncp->nc_forw = ncp; /* hash chain */ - ncp->nc_back = ncp; - - ncp->nc_nxt = NULL; /* lru chain */ - *nchtail = ncp; - ncp->nc_prev = nchtail; - nchtail = &ncp->nc_nxt; - - /* all else is zero already */ - } - - for (nchp = nchash; nchp < &nchash[NCHHASH]; nchp++) { - nchp->nch_head[0] = nchp; - nchp->nch_head[1] = nchp; - } -} - -/* - * Cache flush, called when filesys is umounted to - * remove entries that would now be invalid - * - * The line "nxtcp = nchhead" near the end is to avoid potential problems - * if the cache lru chain is modified while we are dumping the - * inode. This makes the algorithm O(n^2), but do you think I care? - */ -nchinval(dev) - register dev_t dev; -{ - register struct nch *ncp, *nxtcp; - - for (ncp = nchhead; ncp; ncp = nxtcp) { - nxtcp = ncp->nc_nxt; - - if (ncp->nc_ip == NULL || - (ncp->nc_idev != dev && ncp->nc_dev != dev)) - continue; - - /* free the resources we had */ - ncp->nc_idev = NODEV; - ncp->nc_dev = NODEV; - ncp->nc_id = NULL; - ncp->nc_ino = 0; - ncp->nc_ip = NULL; - - - /* remove the entry from its hash chain */ - remque(ncp); - /* and make a dummy one */ - ncp->nc_forw = ncp; - ncp->nc_back = ncp; - - /* delete this entry from LRU chain */ - *ncp->nc_prev = nxtcp; - if (nxtcp) - nxtcp->nc_prev = ncp->nc_prev; - else - nchtail = ncp->nc_prev; - - /* cause rescan of list, it may have altered */ - nxtcp = nchhead; - /* put the now-free entry at head of LRU */ - ncp->nc_nxt = nxtcp; - ncp->nc_prev = &nchhead; - nxtcp->nc_prev = &ncp->nc_nxt; - nchhead = ncp; - } -} - -/* - * Name cache invalidation of all entries. - */ -cacheinvalall() -{ - register struct nch *ncp; - - for (ncp = nch; ncp < &nch[nchsize]; ncp++) { - ncp->nc_id = 0; - } -}