# 2.11BSD#431 - /usr/src/sys/sys/ufs_namei.c

##### Functions

    blkatoff          X
    cacheinvalall     X
    checkpath         X
    dirbad            X
    dirbadentry       X
    dirempty          X
    direnter          X
    dirremove         X
    dirrewrite        X
    namei             X
    nchinit           X
    nchinval          X

##### Variables

    dirchk            X
    nchash            X
    nchhead           X
    nchstats          X
    nchtail           X

##### Macros

    MINDIRSIZ         X
    NCHHASH           X
    NHASH             X
    nch_back          X
    nch_forw          X

##### unions's

    nchash            X

       1: /*
       2:  * Copyright (c) 1982 Regents of the University of California.
       3:  * All rights reserved.  The Berkeley software License Agreement
       4:  * specifies the terms and conditions for redistribution.
       5:  *
       6:  *	@(#)ufs_namei.c	1.5 (2.11BSD GTE) 1997/1/30
       7:  */
       8: #include "param.h"
       9: #include "../machine/seg.h"
      10:
      11: #include "systm.h"
      12: #include "dir.h"
      13: #include "inode.h"
      14: #include "fs.h"
      15: #include "mount.h"
      16: #include "user.h"
      17: #include "buf.h"
      18: #include "namei.h"
      19:
      20: struct  buf *blkatoff();
      21: int dirchk = 0;
      22:
      23: /*
      24:  * Structures associated with name cacheing.
      25:  */
      26: #define NCHHASH     16  /* size of hash table */
      27:
      28: #if ((NCHHASH)&((NCHHASH)-1)) != 0
      29: #define NHASH(h, i, d)  ((unsigned)((h) + (i) + 13 * (int)(d)) % (NCHHASH))
      30: #else
      31: #define NHASH(h, i, d)  ((unsigned)((h) + (i) + 13 * (int)(d)) & ((NCHHASH)-1))
      32: #endif
      33:
      34: union nchash {
      35:     union   nchash *nch_head[2];
      36:     struct  namecache *nch_chain[2];
      37: } nchash[NCHHASH];
      38: #define nch_forw    nch_chain[0]
      39: #define nch_back    nch_chain[1]
      40:
      41: struct  namecache *nchhead, **nchtail;  /* LRU chain pointers */
      42: struct  nchstats nchstats;      /* cache effectiveness statistics */
      43:
      44: /*
      45:  * Convert a pathname into a pointer to a locked inode.
      46:  * This is a very central and rather complicated routine.
      47:  * If the file system is not maintained in a strict tree hierarchy,
      48:  * this can result in a deadlock situation (see comments in code below).
      49:  *
      50:  * The flag argument is LOOKUP, CREATE, or DELETE depending on whether
      51:  * the name is to be looked up, created, or deleted. When CREATE or
      52:  * DELETE is specified, information usable in creating or deleteing a
      53:  * directory entry is also calculated. If flag has LOCKPARENT or'ed
      54:  * into it and the target of the pathname exists, namei returns both
      55:  * the target and its parent directory locked. When creating and
      56:  * LOCKPARENT is specified, the target may not be ".".  When deleting
      57:  * and LOCKPARENT is specified, the target may be ".", but the caller
      58:  * must check to insure it does an irele and iput instead of two iputs.
      59:  *
      60:  * The FOLLOW flag is set when symbolic links are to be followed
      61:  * when they occur at the end of the name translation process.
      62:  * Symbolic links are always followed for all other pathname
      63:  * components other than the last.
      64:  *
      65:  * Name caching works as follows:
      66:  *
      67:  * Names found by directory scans are retained in a cache
      68:  * for future reference.  It is managed LRU, so frequently
      69:  * used names will hang around.  Cache is indexed by hash value
      70:  * obtained from (ino,dev,name) where ino & dev refer to the
      71:  * directory containing name.
      72:  *
      73:  * For simplicity (and economy of storage), names longer than
      74:  * a maximum length of NCHNAMLEN are not cached; they occur
      75:  * infrequently in any case, and are almost never of interest.
      76:  *
      77:  * Upon reaching the last segment of a path, if the reference
      78:  * is for DELETE, or NOCACHE is set (rewrite), and the
      79:  * name is located in the cache, it will be dropped.
      80:  *
      81:  * Overall outline of namei:
      82:  *
      83:  *	copy in name
      84:  *	get starting directory
      85:  * dirloop:
      86:  *	check accessibility of directory
      87:  * dirloop2:
      88:  *	copy next component of name to ndp->ni_dent
      89:  *	handle degenerate case where name is null string
      90:  *	look for name in cache, if found, then if at end of path
      91:  *	  and deleting or creating, drop it, else to haveino
      92:  *	search for name in directory, to found or notfound
      93:  * notfound:
      94:  *	if creating, return locked directory, leaving info on avail. slots
      95:  *	else return error
      96:  * found:
      97:  *	if at end of path and deleting, return information to allow delete
      98:  *	if at end of path and rewriting (CREATE and LOCKPARENT), lock target
      99:  *	  inode and return info to allow rewrite
     100:  *	if .. and on mounted filesys, look in mount table for parent
     101:  *	if not at end, add name to cache; if at end and neither creating
     102:  *	  nor deleting, add name to cache
     103:  * haveino:
     104:  *	if symbolic link, massage name in buffer and continue at dirloop
     105:  *	if more components of name, do next level at dirloop
     106:  *	return the answer as locked inode
     107:  *
     108:  * NOTE: (LOOKUP | LOCKPARENT) currently returns the parent inode,
     109:  *	 but unlocked.
     110:  */
     111: struct inode *
     112: namei(ndp)
     113:     register struct nameidata *ndp;
     114: {
     115:     register char *cp;      /* pointer into pathname argument */
     116: /* these variables refer to things which must be freed or unlocked */
     117:     struct inode *dp = 0;       /* the directory we are searching */
     118:     struct namecache *ncp;      /* cache slot for entry */
     119:     struct fs *fs;          /* file system that directory is in */
     120:     struct buf *bp = 0;     /* a buffer of directory entries */
     121:     struct direct *ep;      /* the current directory entry */
     122:     int  entryoffsetinblock;    /* offset of ep in bp's buffer */
     123: /* these variables hold information about the search for a slot */
     124:     enum {NONE, COMPACT, FOUND} slotstatus;
     125:     off_t slotoffset = -1;      /* offset of area with free space */
     126:     int slotsize;           /* size of area at slotoffset */
     127:     int slotfreespace;      /* amount of space free in slot */
     128:     int slotneeded;         /* size of the entry we're seeking */
     129: /* */
     130:     int numdirpasses;       /* strategy for directory search */
     131:     off_t endsearch;        /* offset to end directory search */
     132:     off_t prevoff;          /* ndp->ni_offset of previous entry */
     133:     int nlink = 0;          /* number of symbolic links taken */
     134:     struct inode *pdp;      /* saved dp during symlink work */
     135: register int i;
     136:     int error;
     137:     int lockparent;
     138:     int docache;            /* == 0 do not cache last component */
     139:     int makeentry;          /* != 0 if name to be added to cache */
     140:     unsigned hash;          /* value of name hash for entry */
     141:     union nchash *nhp;      /* cache chain head for entry */
     142:     int isdotdot;           /* != 0 if current name is ".." */
     143:     int flag;           /* op ie, LOOKUP, CREATE, or DELETE */
     144:     off_t enduseful;        /* pointer past last used dir slot */
     145:     char    path[MAXPATHLEN];   /* current path */
     146:     segm    seg5;           /* save area for kernel seg5 */
     147:
     148:     lockparent = ndp->ni_nameiop & LOCKPARENT;
     149:     docache = (ndp->ni_nameiop & NOCACHE) ^ NOCACHE;
     150:     flag = ndp->ni_nameiop &~ (LOCKPARENT|NOCACHE|FOLLOW);
     151:     if (flag == DELETE || lockparent)
     152:         docache = 0;
     153:     /*
     154: 	 * Copy the name into the buffer.
     155: 	 */
     156:     if (ndp->ni_segflg == UIO_SYSSPACE)
     157:         error = copystr(ndp->ni_dirp, path, MAXPATHLEN,
     158:             (u_int *)0);
     159:     else
     160:         error = copyinstr(ndp->ni_dirp, path, MAXPATHLEN,
     161:             (u_int *)0);
     162:     if (error) {
     163:         u.u_error = error;
     164:         goto retNULL;
     165:     }
     166:
     167:     /*
     168: 	 * Get starting directory.
     169: 	 */
     170:     cp = path;
     171:     if (*cp == '/') {
     172:         while (*cp == '/')
     173:             cp++;
     174:         if ((dp = u.u_rdir) == NULL)
     175:             dp = rootdir;
     176:     } else
     177:         dp = u.u_cdir;
     178:     fs = dp->i_fs;
     179:     ILOCK(dp);
     180:     dp->i_count++;
     181:     ndp->ni_endoff = 0;
     182:
     183:     /*
     184: 	 * We come to dirloop to search a new directory.
     185: 	 * The directory must be locked so that it can be
     186: 	 * iput, and fs must be already set to dp->i_fs.
     187: 	 */
     188: dirloop:
     189:     /*
     190: 	 * Check accessibility of directory.
     191: 	 */
     192:     if ((dp->i_mode&IFMT) != IFDIR) {
     193:         u.u_error = ENOTDIR;
     194:         goto bad;
     195:     }
     196:     if (access(dp, IEXEC))
     197:         goto bad;
     198:
     199: dirloop2:
     200:     /*
     201: 	 * Copy next component of name to ndp->ni_dent.
     202: 	 */
     203:     hash = 0;
     204:     for (i = 0; *cp != 0 && *cp != '/'; cp++) {
     205:         if (i >= MAXNAMLEN) {
     206:             u.u_error = ENAMETOOLONG;
     207:             goto bad;
     208:         }
     209:         if (*cp & 0200)
     210:             if ((*cp&0377) == ('/'|0200) || flag != DELETE) {
     211:                 u.u_error = EINVAL;
     212:                 goto bad;
     213:             }
     214:         ndp->ni_dent.d_name[i++] = *cp;
     215:         hash += (unsigned char)*cp * i;
     216:     }
     217:     ndp->ni_dent.d_namlen = i;
     218:     ndp->ni_dent.d_name[i] = '\0';
     219:     isdotdot = (i == 2 &&
     220:         ndp->ni_dent.d_name[0] == '.' && ndp->ni_dent.d_name[1] == '.');
     221:     makeentry = 1;
     222:     if (*cp == '\0' && docache == 0)
     223:         makeentry = 0;
     224:
     225:     /*
     226: 	 * Check for degenerate name (e.g. / or "")
     227: 	 * which is a way of talking about a directory,
     228: 	 * e.g. like "/." or ".".
     229: 	 */
     230:      if (ndp->ni_dent.d_name[0] == '\0') {
     231:         if (flag != LOOKUP || lockparent) {
     232:             u.u_error = EISDIR;
     233:             goto bad;
     234:         }
     235:         goto retDP;
     236:     }
     237:
     238:     /*
     239: 	 * We now have a segment name to search for, and a directory to search.
     240: 	 *
     241: 	 * Before tediously performing a linear scan of the directory,
     242: 	 * check the name cache to see if the directory/name pair
     243: 	 * we are looking for is known already.  We don't do this
     244: 	 * if the segment name is long, simply so the cache can avoid
     245: 	 * holding long names (which would either waste space, or
     246: 	 * add greatly to the complexity).
     247: 	 */
     248:     saveseg5(seg5);
     249:     mapseg5(nmidesc.se_addr, nmidesc.se_desc);
     250:     if (ndp->ni_dent.d_namlen > NCHNAMLEN) {
     251:         nchstats.ncs_long++;
     252:         makeentry = 0;
     253:     } else {
     254:         nhp = &nchash[NHASH(hash, dp->i_number, dp->i_dev)];
     255:         for (ncp = nhp->nch_forw; ncp != (struct namecache *)nhp;
     256:             ncp = ncp->nc_forw) {
     257:             if (ncp->nc_ino == dp->i_number &&
     258:                 ncp->nc_dev == dp->i_dev &&
     259:                 ncp->nc_nlen == ndp->ni_dent.d_namlen &&
     260:                 !bcmp(ncp->nc_name, ndp->ni_dent.d_name,
     261:                 (unsigned)ncp->nc_nlen))
     262:                 break;
     263:         }
     264:         if (ncp == (struct namecache *)nhp) {
     265:             nchstats.ncs_miss++;
     266:             ncp = NULL;
     267:         } else {
     268:             if (ncp->nc_id != ncp->nc_ip->i_id)
     269:                 nchstats.ncs_falsehits++;
     270:             else if (!makeentry)
     271:                 nchstats.ncs_badhits++;
     272:             else {
     273:                 /*
     274: 				 * move this slot to end of LRU
     275: 				 * chain, if not already there
     276: 				 */
     277:                 if (ncp->nc_nxt) {
     278:                     /* remove from LRU chain */
     279:                     *ncp->nc_prev = ncp->nc_nxt;
     280:                     ncp->nc_nxt->nc_prev = ncp->nc_prev;
     281:
     282:                     /* and replace at end of it */
     283:                     ncp->nc_nxt = NULL;
     284:                     ncp->nc_prev = nchtail;
     285:                     *nchtail = ncp;
     286:                     nchtail = &ncp->nc_nxt;
     287:                 }
     288:
     289:                 /*
     290: 				 * Get the next inode in the path.
     291: 				 * See comment above other `IUNLOCK' code for
     292: 				 * an explaination of the locking protocol.
     293: 				 */
     294:                 pdp = dp;
     295:                 if (!isdotdot || dp != u.u_rdir)
     296:                     dp = ncp->nc_ip;
     297:                 if (dp == NULL)
     298:                     panic("namei: null cache ino");
     299:                 if (pdp == dp)
     300:                     dp->i_count++;
     301:                 else if (isdotdot) {
     302:                     restorseg5(seg5);
     303:                     IUNLOCK(pdp);
     304:                     igrab(dp);
     305:                     mapseg5(nmidesc.se_addr,nmidesc.se_desc);
     306:                 } else {
     307:                     restorseg5(seg5);
     308:                     igrab(dp);
     309:                     IUNLOCK(pdp);
     310:                     mapseg5(nmidesc.se_addr,nmidesc.se_desc);
     311:                 }
     312:
     313:                 /*
     314: 				 * Verify that the inode that we got
     315: 				 * did not change while we were waiting
     316: 				 * for it to be locked.
     317: 				 */
     318:                 if (ncp->nc_id != ncp->nc_ip->i_id) {
     319:                     restorseg5(seg5);
     320:                     iput(dp);
     321:                     ILOCK(pdp);
     322:                     mapseg5(nmidesc.se_addr,nmidesc.se_desc);
     323:                     dp = pdp;
     324:                     nchstats.ncs_falsehits++;
     325:                 } else {
     326:                     ndp->ni_dent.d_ino = dp->i_number;
     327:                     /* ni_dent.d_reclen is garbage ... */
     328:                     nchstats.ncs_goodhits++;
     329:                     restorseg5(seg5);
     330:                     goto haveino;
     331:                 }
     332:             }
     333:
     334:             /*
     335: 			 * Last component and we are renaming or deleting,
     336: 			 * the cache entry is invalid, or otherwise don't
     337: 			 * want cache entry to exist.
     338: 			 */
     339:             /* remove from LRU chain */
     340:             *ncp->nc_prev = ncp->nc_nxt;
     341:             if (ncp->nc_nxt)
     342:                 ncp->nc_nxt->nc_prev = ncp->nc_prev;
     343:             else
     344:                 nchtail = ncp->nc_prev;
     345:             remque(ncp);        /* remove from hash chain */
     346:             /* insert at head of LRU list (first to grab) */
     347:             ncp->nc_nxt = nchhead;
     348:             ncp->nc_prev = &nchhead;
     349:             nchhead->nc_prev = &ncp->nc_nxt;
     350:             nchhead = ncp;
     351:             /* and make a dummy hash chain */
     352:             ncp->nc_forw = ncp;
     353:             ncp->nc_back = ncp;
     354:             ncp = NULL;
     355:         }
     356:     }
     357:     restorseg5(seg5);
     358:
     359:     /*
     360: 	 * Suppress search for slots unless creating
     361: 	 * file and at end of pathname, in which case
     362: 	 * we watch for a place to put the new file in
     363: 	 * case it doesn't already exist.
     364: 	 */
     365:     slotstatus = FOUND;
     366:     if (flag == CREATE && *cp == 0) {
     367:         slotstatus = NONE;
     368:         slotfreespace = 0;
     369:         slotneeded = DIRSIZ(&ndp->ni_dent);
     370:     }
     371:     /*
     372: 	 * If this is the same directory that this process
     373: 	 * previously searched, pick up where we last left off.
     374: 	 * We cache only lookups as these are the most common
     375: 	 * and have the greatest payoff. Caching CREATE has little
     376: 	 * benefit as it usually must search the entire directory
     377: 	 * to determine that the entry does not exist. Caching the
     378: 	 * location of the last DELETE has not reduced profiling time
     379: 	 * and hence has been removed in the interest of simplicity.
     380: 	 */
     381:     if (flag != LOOKUP || dp->i_number != u.u_ncache.nc_inumber ||
     382:         dp->i_dev != u.u_ncache.nc_dev) {
     383:             ndp->ni_offset = 0;
     384:             numdirpasses = 1;
     385:     } else {
     386:         if (u.u_ncache.nc_prevoffset > dp->i_size)
     387:             u.u_ncache.nc_prevoffset = 0;
     388:         ndp->ni_offset = u.u_ncache.nc_prevoffset;
     389:         entryoffsetinblock = blkoff(ndp->ni_offset);
     390:         if (entryoffsetinblock != 0) {
     391:             bp = blkatoff(dp, ndp->ni_offset, (char **)0);
     392:             if (bp == 0)
     393:                 goto bad;
     394:         }
     395:         numdirpasses = 2;
     396:         nchstats.ncs_2passes++;
     397:     }
     398:     endsearch = roundup(dp->i_size, DIRBLKSIZ);
     399:     enduseful = 0;
     400:
     401: searchloop:
     402:     while (ndp->ni_offset < endsearch) {
     403:         /*
     404: 		 * If offset is on a block boundary,
     405: 		 * read the next directory block.
     406: 		 * Release previous if it exists.
     407: 		 */
     408:         if (blkoff(ndp->ni_offset) == 0) {
     409:             if (bp != NULL) {
     410:                 mapout(bp);
     411:                 brelse(bp);
     412:             }
     413:             bp = blkatoff(dp, ndp->ni_offset, (char **)0);
     414:             if (bp == 0)
     415:                 goto bad;
     416:             entryoffsetinblock = 0;
     417:         }
     418:         /*
     419: 		 * If still looking for a slot, and at a DIRBLKSIZE
     420: 		 * boundary, have to start looking for free space again.
     421: 		 */
     422:         if (slotstatus == NONE &&
     423:             (entryoffsetinblock&(DIRBLKSIZ-1)) == 0) {
     424:             slotoffset = -1;
     425:             slotfreespace = 0;
     426:         }
     427:         /*
     428: 		 * Get pointer to next entry.
     429: 		 * Full validation checks are slow, so we only check
     430: 		 * enough to insure forward progress through the
     431: 		 * directory. Complete checks can be run by patching
     432: 		 * "dirchk" to be true.
     433: 		 */
     434:         mapout(bp); /* XXX - avoid double mapin */
     435:         ep = (struct direct *)((caddr_t)mapin(bp)+ entryoffsetinblock);
     436:         if (ep->d_reclen == 0 ||
     437:             dirchk && dirbadentry(ep, entryoffsetinblock)) {
     438:             dirbad(dp, ndp->ni_offset, "mangled entry");
     439:             i = DIRBLKSIZ - (entryoffsetinblock & (DIRBLKSIZ - 1));
     440:             ndp->ni_offset += i;
     441:             entryoffsetinblock += i;
     442:             continue;
     443:         }
     444:
     445:         /*
     446: 		 * If an appropriate sized slot has not yet been found,
     447: 		 * check to see if one is available. Also accumulate space
     448: 		 * in the current block so that we can determine if
     449: 		 * compaction is viable.
     450: 		 */
     451:         if (slotstatus != FOUND) {
     452:             int size = ep->d_reclen;
     453:
     454:             if (ep->d_ino != 0)
     455:                 size -= DIRSIZ(ep);
     456:             if (size > 0) {
     457:                 if (size >= slotneeded) {
     458:                     slotstatus = FOUND;
     459:                     slotoffset = ndp->ni_offset;
     460:                     slotsize = ep->d_reclen;
     461:                 } else if (slotstatus == NONE) {
     462:                     slotfreespace += size;
     463:                     if (slotoffset == -1)
     464:                         slotoffset = ndp->ni_offset;
     465:                     if (slotfreespace >= slotneeded) {
     466:                         slotstatus = COMPACT;
     467:                         slotsize = ndp->ni_offset +
     468:                               ep->d_reclen - slotoffset;
     469:                     }
     470:                 }
     471:             }
     472:         }
     473:
     474:         /*
     475: 		 * Check for a name match.
     476: 		 */
     477:         if (ep->d_ino) {
     478:             if (ep->d_namlen == ndp->ni_dent.d_namlen &&
     479:                 !bcmp(ndp->ni_dent.d_name, ep->d_name,
     480:                 (unsigned)ep->d_namlen))
     481:                 goto found;
     482:         }
     483:         prevoff = ndp->ni_offset;
     484:         ndp->ni_offset += ep->d_reclen;
     485:         entryoffsetinblock += ep->d_reclen;
     486:         if (ep->d_ino)
     487:             enduseful = ndp->ni_offset;
     488:     }
     489: /* notfound: */
     490:     /*
     491: 	 * If we started in the middle of the directory and failed
     492: 	 * to find our target, we must check the beginning as well.
     493: 	 */
     494:     if (numdirpasses == 2) {
     495:         numdirpasses--;
     496:         ndp->ni_offset = 0;
     497:         endsearch = u.u_ncache.nc_prevoffset;
     498:         goto searchloop;
     499:     }
     500:     /*
     501: 	 * If creating, and at end of pathname and current
     502: 	 * directory has not been removed, then can consider
     503: 	 * allowing file to be created.
     504: 	 */
     505:     if (flag == CREATE && *cp == 0 && dp->i_nlink != 0) {
     506:         /*
     507: 		 * Access for write is interpreted as allowing
     508: 		 * creation of files in the directory.
     509: 		 */
     510:         if (access(dp, IWRITE))
     511:             goto bad;
     512:         /*
     513: 		 * Return an indication of where the new directory
     514: 		 * entry should be put.  If we didn't find a slot,
     515: 		 * then set ndp->ni_count to 0 indicating that the new
     516: 		 * slot belongs at the end of the directory. If we found
     517: 		 * a slot, then the new entry can be put in the range
     518: 		 * [ndp->ni_offset .. ndp->ni_offset + ndp->ni_count)
     519: 		 */
     520:         if (slotstatus == NONE) {
     521:             ndp->ni_offset = roundup(dp->i_size, DIRBLKSIZ);
     522:             ndp->ni_count = 0;
     523:             enduseful = ndp->ni_offset;
     524:         } else {
     525:             ndp->ni_offset = slotoffset;
     526:             ndp->ni_count = slotsize;
     527:             if (enduseful < slotoffset + slotsize)
     528:                 enduseful = slotoffset + slotsize;
     529:         }
     530:         ndp->ni_endoff = roundup(enduseful, DIRBLKSIZ);
     531:         dp->i_flag |= IUPD|ICHG;
     532:         if (bp) {
     533:             mapout(bp);
     534:             brelse(bp);
     535:         }
     536:         /*
     537: 		 * We return with the directory locked, so that
     538: 		 * the parameters we set up above will still be
     539: 		 * valid if we actually decide to do a direnter().
     540: 		 * We return NULL to indicate that the entry doesn't
     541: 		 * currently exist, leaving a pointer to the (locked)
     542: 		 * directory inode in ndp->ni_pdir.
     543: 		 */
     544:         ndp->ni_pdir = dp;
     545:         goto retNULL;
     546:     }
     547:     u.u_error = ENOENT;
     548:     goto bad;
     549: found:
     550:     if (numdirpasses == 2)
     551:         nchstats.ncs_pass2++;
     552:     /*
     553: 	 * Check that directory length properly reflects presence
     554: 	 * of this entry.
     555: 	 */
     556:     if (entryoffsetinblock + DIRSIZ(ep) > dp->i_size) {
     557:         dirbad(dp, ndp->ni_offset, "i_size too small");
     558:         dp->i_size = entryoffsetinblock + DIRSIZ(ep);
     559:         dp->i_flag |= IUPD|ICHG;
     560:     }
     561:
     562:     /*
     563: 	 * Found component in pathname.
     564: 	 * If the final component of path name, save information
     565: 	 * in the cache as to where the entry was found.
     566: 	 */
     567:     if (*cp == '\0' && flag == LOOKUP) {
     568:         u.u_ncache.nc_prevoffset = ndp->ni_offset &~ (DIRBLKSIZ - 1);
     569:         u.u_ncache.nc_inumber = dp->i_number;
     570:         u.u_ncache.nc_dev = dp->i_dev;
     571:     }
     572:     /*
     573: 	 * Save directory entry's inode number and reclen in ndp->ni_dent,
     574: 	 * and release directory buffer.
     575: 	 */
     576:     ndp->ni_dent.d_ino = ep->d_ino;
     577:     ndp->ni_dent.d_reclen = ep->d_reclen;
     578:     mapout(bp);
     579:     brelse(bp);
     580:     bp = NULL;
     581:
     582:     /*
     583: 	 * If deleting, and at end of pathname, return
     584: 	 * parameters which can be used to remove file.
     585: 	 * If the lockparent flag isn't set, we return only
     586: 	 * the directory (in ndp->ni_pdir), otherwise we go
     587: 	 * on and lock the inode, being careful with ".".
     588: 	 */
     589:     if (flag == DELETE && *cp == 0) {
     590:         /*
     591: 		 * Write access to directory required to delete files.
     592: 		 */
     593:         if (access(dp, IWRITE))
     594:             goto bad;
     595:         ndp->ni_pdir = dp;      /* for dirremove() */
     596:         /*
     597: 		 * Return pointer to current entry in ndp->ni_offset,
     598: 		 * and distance past previous entry (if there
     599: 		 * is a previous entry in this block) in ndp->ni_count.
     600: 		 * Save directory inode pointer in ndp->ni_pdir for dirremove().
     601: 		 */
     602:         if ((ndp->ni_offset&(DIRBLKSIZ-1)) == 0)
     603:             ndp->ni_count = 0;
     604:         else
     605:             ndp->ni_count = ndp->ni_offset - prevoff;
     606:         if (lockparent) {
     607:             if (dp->i_number == ndp->ni_dent.d_ino)
     608:                 dp->i_count++;
     609:             else {
     610:                 dp = iget(dp->i_dev, fs, ndp->ni_dent.d_ino);
     611:                 if (dp == NULL) {
     612:                     iput(ndp->ni_pdir);
     613:                     goto bad;
     614:                 }
     615:                 /*
     616: 				 * If directory is "sticky", then user must own
     617: 				 * the directory, or the file in it, else he
     618: 				 * may not delete it (unless he's root). This
     619: 				 * implements append-only directories.
     620: 				 */
     621:                 if ((ndp->ni_pdir->i_mode & ISVTX) &&
     622:                     u.u_uid != 0 &&
     623:                     u.u_uid != ndp->ni_pdir->i_uid &&
     624:                     dp->i_uid != u.u_uid) {
     625:                     iput(ndp->ni_pdir);
     626:                     u.u_error = EPERM;
     627:                     goto bad;
     628:                 }
     629:             }
     630:         }
     631:         goto retDP;
     632:     }
     633:
     634:     /*
     635: 	 * Special handling for ".." allowing chdir out of mounted
     636: 	 * file system: indirect .. in root inode to reevaluate
     637: 	 * in directory file system was mounted on.
     638: 	 */
     639:     if (isdotdot) {
     640:         if (dp == u.u_rdir) {
     641:             ndp->ni_dent.d_ino = dp->i_number;
     642:             makeentry = 0;
     643:         } else if (ndp->ni_dent.d_ino == ROOTINO &&
     644:             dp->i_number == ROOTINO) {
     645:             register struct mount *mp;
     646:             register dev_t d;
     647:
     648:             d = dp->i_dev;
     649:             for (mp = &mount[1]; mp < &mount[NMOUNT]; mp++)
     650:                 if (mp->m_inodp && mp->m_dev == d) {
     651:                     iput(dp);
     652:                     dp = mp->m_inodp;
     653:                     ILOCK(dp);
     654:                     dp->i_count++;
     655:                     fs = dp->i_fs;
     656:                     cp -= 2;    /* back over .. */
     657:                     goto dirloop2;
     658:                 }
     659:         }
     660:     }
     661:
     662:     /*
     663: 	 * If rewriting (rename), return the inode and the
     664: 	 * information required to rewrite the present directory
     665: 	 * Must get inode of directory entry to verify it's a
     666: 	 * regular file, or empty directory.
     667: 	 */
     668:     if ((flag == CREATE && lockparent) && *cp == 0) {
     669:         if (access(dp, IWRITE))
     670:             goto bad;
     671:         ndp->ni_pdir = dp;      /* for dirrewrite() */
     672:         /*
     673: 		 * Careful about locking second inode.
     674: 		 * This can only occur if the target is ".".
     675: 		 */
     676:         if (dp->i_number == ndp->ni_dent.d_ino) {
     677:             u.u_error = EISDIR;     /* XXX */
     678:             goto bad;
     679:         }
     680:         dp = iget(dp->i_dev, fs, ndp->ni_dent.d_ino);
     681:         if (dp == NULL) {
     682:             iput(ndp->ni_pdir);
     683:             goto bad;
     684:         }
     685:         goto retDP;
     686:     }
     687:
     688:     /*
     689: 	 * Check for symbolic link, which may require us to massage the
     690: 	 * name before we continue translation.  We do not `iput' the
     691: 	 * directory because we may need it again if the symbolic link
     692: 	 * is relative to the current directory.  Instead we save it
     693: 	 * unlocked as "pdp".  We must get the target inode before unlocking
     694: 	 * the directory to insure that the inode will not be removed
     695: 	 * before we get it.  We prevent deadlock by always fetching
     696: 	 * inodes from the root, moving down the directory tree. Thus
     697: 	 * when following backward pointers ".." we must unlock the
     698: 	 * parent directory before getting the requested directory.
     699: 	 * There is a potential race condition here if both the current
     700: 	 * and parent directories are removed before the `iget' for the
     701: 	 * inode associated with ".." returns.  We hope that this occurs
     702: 	 * infrequently since we cannot avoid this race condition without
     703: 	 * implementing a sophisticated deadlock detection algorithm.
     704: 	 * Note also that this simple deadlock detection scheme will not
     705: 	 * work if the file system has any hard links other than ".."
     706: 	 * that point backwards in the directory structure.
     707: 	 */
     708:     pdp = dp;
     709:     if (isdotdot) {
     710:         IUNLOCK(pdp);   /* race to get the inode */
     711:         dp = iget(dp->i_dev, fs, ndp->ni_dent.d_ino);
     712:         if (dp == NULL)
     713:             goto bad2;
     714:     } else if (dp->i_number == ndp->ni_dent.d_ino) {
     715:         dp->i_count++;  /* we want ourself, ie "." */
     716:     } else {
     717:         dp = iget(dp->i_dev, fs, ndp->ni_dent.d_ino);
     718:         IUNLOCK(pdp);
     719:         if (dp == NULL)
     720:             goto bad2;
     721:     }
     722:
     723:     /*
     724: 	 * Insert name into cache if appropriate.
     725: 	 */
     726:     if (makeentry) {
     727:         if (ncp != NULL)
     728:             panic("namei: duplicating cache");
     729:         /*
     730: 		 * Free the cache slot at head of lru chain.
     731: 		 */
     732:         if (ncp = nchhead) {
     733:             saveseg5(seg5);
     734:             mapseg5(nmidesc.se_addr, nmidesc.se_desc);
     735:             /* remove from lru chain */
     736:             *ncp->nc_prev = ncp->nc_nxt;
     737:             if (ncp->nc_nxt)
     738:                 ncp->nc_nxt->nc_prev = ncp->nc_prev;
     739:             else
     740:                 nchtail = ncp->nc_prev;
     741:             remque(ncp);        /* remove from old hash chain */
     742:             /* grab the inode we just found */
     743:             ncp->nc_ip = dp;
     744:             /* fill in cache info */
     745:             ncp->nc_ino = pdp->i_number;    /* parents inum */
     746:             ncp->nc_dev = pdp->i_dev;   /* & device */
     747:             ncp->nc_idev = dp->i_dev;   /* our device */
     748:             ncp->nc_id = dp->i_id;      /* identifier */
     749:             ncp->nc_nlen = ndp->ni_dent.d_namlen;
     750:             bcopy(ndp->ni_dent.d_name, ncp->nc_name,
     751:                 (unsigned)ncp->nc_nlen);
     752:             /* link at end of lru chain */
     753:             ncp->nc_nxt = NULL;
     754:             ncp->nc_prev = nchtail;
     755:             *nchtail = ncp;
     756:             nchtail = &ncp->nc_nxt;
     757:             /* and insert on hash chain */
     758:             insque(ncp, nhp);
     759:             restorseg5(seg5);
     760:         }
     761:     }
     762:
     763: haveino:
     764:     fs = dp->i_fs;
     765:
     766:     /*
     767: 	 * Check for symbolic link
     768: 	 */
     769:     if ((dp->i_mode & IFMT) == IFLNK &&
     770:         ((ndp->ni_nameiop & FOLLOW) || *cp == '/')) {
     771:         u_int pathlen = strlen(cp) + 1;
     772:
     773:         if (dp->i_size + pathlen >= MAXPATHLEN - 1) {
     774:             u.u_error = ENAMETOOLONG;
     775:             goto bad2;
     776:         }
     777:         if (++nlink > MAXSYMLINKS) {
     778:             u.u_error = ELOOP;
     779:             goto bad2;
     780:         }
     781:
     782:         bp = bread(dp->i_dev, bmap(dp, (daddr_t)0, B_READ, 0));
     783:         if (bp->b_flags & B_ERROR) {
     784:             brelse(bp);
     785:             bp = NULL;
     786:             goto bad2;
     787:         }
     788:         /*
     789: 		 * Shift the rest of path further down the buffer, then
     790: 		 * copy link path into the first part of the buffer.
     791: 		 */
     792:         bcopy(cp, path + (u_int)dp->i_size, pathlen);
     793:         bcopy(mapin(bp), path, (u_int)dp->i_size);
     794:         mapout(bp);
     795:         brelse(bp);
     796:         bp = NULL;
     797:         cp = path;
     798:         iput(dp);
     799:         if (*cp == '/') {
     800:             irele(pdp);
     801:             while (*cp == '/')
     802:                 cp++;
     803:             if ((dp = u.u_rdir) == NULL)
     804:                 dp = rootdir;
     805:             ILOCK(dp);
     806:             dp->i_count++;
     807:         } else {
     808:             dp = pdp;
     809:             ILOCK(dp);
     810:         }
     811:         fs = dp->i_fs;
     812:         goto dirloop;
     813:     }
     814:
     815:     /*
     816: 	 * Not a symbolic link.  If more pathname,
     817: 	 * continue at next component, else return.
     818: 	 */
     819:     if (*cp == '/') {
     820:         while (*cp == '/')
     821:             cp++;
     822:         irele(pdp);
     823:         goto dirloop;
     824:     }
     825:     if (lockparent)
     826:         ndp->ni_pdir = pdp;
     827:     else
     828:         irele(pdp);
     829: retDP:
     830:     ndp->ni_ip = dp;
     831:     return (dp);
     832:
     833: bad2:
     834:     irele(pdp);
     835: bad:
     836:     if (bp) {
     837:         mapout(bp);
     838:         brelse(bp);
     839:     }
     840:     if (dp)
     841:         iput(dp);
     842: retNULL:
     843:     ndp->ni_ip = NULL;
     844:     return (NULL);
     845: }
     846:
     847: dirbad(ip, offset, how)
     848:     struct inode *ip;
     849:     off_t offset;
     850:     char *how;
     851: {
     852:
     853:     printf("%s: bad dir I=%u off %ld: %s\n",
     854:         ip->i_fs->fs_fsmnt, ip->i_number, offset, how);
     855: }
     856:
     857: /*
     858:  * Do consistency checking on a directory entry:
     859:  *	record length must be multiple of 4
     860:  *	entry must fit in rest of its DIRBLKSIZ block
     861:  *	record must be large enough to contain entry
     862:  *	name is not longer than MAXNAMLEN
     863:  *	name must be as long as advertised, and null terminated
     864:  */
     865: dirbadentry(ep, entryoffsetinblock)
     866:     register struct direct *ep;
     867:     int entryoffsetinblock;
     868: {
     869:     register int i;
     870:
     871:     if ((ep->d_reclen & 0x3) != 0 ||
     872:         ep->d_reclen > DIRBLKSIZ - (entryoffsetinblock & (DIRBLKSIZ - 1)) ||
     873:         ep->d_reclen < DIRSIZ(ep) || ep->d_namlen > MAXNAMLEN)
     874:         return (1);
     875:     for (i = 0; i < ep->d_namlen; i++)
     876:         if (ep->d_name[i] == '\0')
     877:             return (1);
     878:     return (ep->d_name[i]);
     879: }
     880:
     881: /*
     882:  * Write a directory entry after a call to namei, using the parameters
     883:  * which it left in the u. area.  The argument ip is the inode which
     884:  * the new directory entry will refer to.  The u. area field ndp->ni_pdir is
     885:  * a pointer to the directory to be written, which was left locked by
     886:  * namei.  Remaining parameters (ndp->ni_offset, ndp->ni_count) indicate
     887:  * how the space for the new entry is to be gotten.
     888:  */
     889: direnter(ip, ndp)
     890:     struct inode *ip;
     891:     register struct nameidata *ndp;
     892: {
     893:     register struct direct *ep, *nep;
     894:     register struct inode *dp = ndp->ni_pdir;
     895:     struct buf *bp;
     896:     int loc, spacefree, error = 0;
     897:     u_int dsize;
     898:     int newentrysize;
     899:     char *dirbuf;
     900:
     901:     ndp->ni_dent.d_ino = ip->i_number;
     902:     newentrysize = DIRSIZ(&ndp->ni_dent);
     903:     if (ndp->ni_count == 0) {
     904:         /*
     905: 		 * If ndp->ni_count is 0, then namei could find no space in the
     906: 		 * directory. In this case ndp->ni_offset will be on a directory
     907: 		 * block boundary and we will write the new entry into a fresh
     908: 		 * block.
     909: 		 */
     910:         if (ndp->ni_offset&(DIRBLKSIZ-1))
     911:             panic("wdir: newblk");
     912:         ndp->ni_dent.d_reclen = DIRBLKSIZ;
     913:         error = rdwri(UIO_WRITE, dp, (caddr_t)&ndp->ni_dent,
     914:                     newentrysize, ndp->ni_offset, UIO_SYSSPACE,
     915:                 IO_UNIT|IO_SYNC, (int *)0);
     916:         dp->i_size = roundup(dp->i_size, DIRBLKSIZ);
     917:         iput(dp);
     918:         return (error);
     919:     }
     920:
     921:     /*
     922: 	 * If ndp->ni_count is non-zero, then namei found space for the new
     923: 	 * entry in the range ndp->ni_offset to ndp->ni_offset + ndp->ni_count.
     924: 	 * in the directory.  To use this space, we may have to compact
     925: 	 * the entries located there, by copying them together towards
     926: 	 * the beginning of the block, leaving the free space in
     927: 	 * one usable chunk at the end.
     928: 	 */
     929:
     930:     /*
     931: 	 * Increase size of directory if entry eats into new space.
     932: 	 * This should never push the size past a new multiple of
     933: 	 * DIRBLKSIZE.
     934: 	 *
     935: 	 * N.B. - THIS IS AN ARTIFACT OF 4.2 AND SHOULD NEVER HAPPEN.
     936: 	 */
     937:     if (ndp->ni_offset + ndp->ni_count > dp->i_size)
     938:         dp->i_size = ndp->ni_offset + ndp->ni_count;
     939:     /*
     940: 	 * Get the block containing the space for the new directory
     941: 	 * entry.  Should return error by result instead of u.u_error.
     942: 	 */
     943:     bp = blkatoff(dp, ndp->ni_offset, (char **)&dirbuf);
     944:     if (bp == 0) {
     945:         iput(dp);
     946:         return (u.u_error);
     947:     }
     948:     /*
     949: 	 * Find space for the new entry.  In the simple case, the
     950: 	 * entry at offset base will have the space.  If it does
     951: 	 * not, then namei arranged that compacting the region
     952: 	 * ndp->ni_offset to ndp->ni_offset+ndp->ni_count would yield the space.
     953: 	 */
     954:     ep = (struct direct *)dirbuf;
     955:     dsize = DIRSIZ(ep);
     956:     spacefree = ep->d_reclen - dsize;
     957:     for (loc = ep->d_reclen; loc < ndp->ni_count; ) {
     958:         nep = (struct direct *)(dirbuf + loc);
     959:         if (ep->d_ino) {
     960:             /* trim the existing slot */
     961:             ep->d_reclen = dsize;
     962:             ep = (struct direct *)((char *)ep + dsize);
     963:         } else {
     964:             /* overwrite; nothing there; header is ours */
     965:             spacefree += dsize;
     966:         }
     967:         dsize = DIRSIZ(nep);
     968:         spacefree += nep->d_reclen - dsize;
     969:         loc += nep->d_reclen;
     970:         bcopy((caddr_t)nep, (caddr_t)ep, dsize);
     971:     }
     972:     /*
     973: 	 * Update the pointer fields in the previous entry (if any),
     974: 	 * copy in the new entry, and write out the block.
     975: 	 */
     976:     if (ep->d_ino == 0) {
     977:         if (spacefree + dsize < newentrysize)
     978:             panic("wdir: compact1");
     979:         ndp->ni_dent.d_reclen = spacefree + dsize;
     980:     } else {
     981:         if (spacefree < newentrysize)
     982:             panic("wdir: compact2");
     983:         ndp->ni_dent.d_reclen = spacefree;
     984:         ep->d_reclen = dsize;
     985:         ep = (struct direct *)((char *)ep + dsize);
     986:     }
     987:     bcopy((caddr_t)&ndp->ni_dent, (caddr_t)ep, (u_int)newentrysize);
     988:     mapout(bp);
     989:     bwrite(bp);
     990:     dp->i_flag |= IUPD|ICHG;
     991:     if (ndp->ni_endoff && ndp->ni_endoff < dp->i_size)
     992:         itrunc(dp, (u_long)ndp->ni_endoff, 0);
     993:     iput(dp);
     994:     return (error);
     995: }
     996:
     997: /*
     998:  * Remove a directory entry after a call to namei, using the
     999:  * parameters which it left in the u. area.  The u. entry
    1000:  * ni_offset contains the offset into the directory of the
    1001:  * entry to be eliminated.  The ni_count field contains the
    1002:  * size of the previous record in the directory.  If this
    1003:  * is 0, the first entry is being deleted, so we need only
    1004:  * zero the inode number to mark the entry as free.  If the
    1005:  * entry isn't the first in the directory, we must reclaim
    1006:  * the space of the now empty record by adding the record size
    1007:  * to the size of the previous entry.
    1008:  */
    1009: dirremove(ndp)
    1010:     register struct nameidata *ndp;
    1011: {
    1012:     register struct inode *dp = ndp->ni_pdir;
    1013:     register struct buf *bp;
    1014:     struct direct *ep;
    1015:
    1016:     if (ndp->ni_count == 0) {
    1017:         /*
    1018: 		 * First entry in block: set d_ino to zero.
    1019: 		 */
    1020:         ndp->ni_dent.d_ino = 0;
    1021:         (void) rdwri(UIO_WRITE, dp, (caddr_t)&ndp->ni_dent,
    1022:                     (int)DIRSIZ(&ndp->ni_dent), ndp->ni_offset,
    1023:                 UIO_SYSSPACE, IO_UNIT|IO_SYNC, (int *)0);
    1024:     } else {
    1025:         /*
    1026: 		 * Collapse new free space into previous entry.
    1027: 		 */
    1028:         bp = blkatoff(dp, ndp->ni_offset - ndp->ni_count, (char **)&ep);
    1029:         if (bp == 0)
    1030:             return (0);
    1031:         ep->d_reclen += ndp->ni_dent.d_reclen;
    1032:         mapout(bp);
    1033:         bwrite(bp);
    1034:         dp->i_flag |= IUPD|ICHG;
    1035:     }
    1036:     return (1);
    1037: }
    1038:
    1039: /*
    1040:  * Rewrite an existing directory entry to point at the inode
    1041:  * supplied.  The parameters describing the directory entry are
    1042:  * set up by a call to namei.
    1043:  */
    1044: dirrewrite(dp, ip, ndp)
    1045:     register struct inode *dp;
    1046:     struct inode *ip;
    1047:     register struct nameidata *ndp;
    1048: {
    1049:
    1050:     ndp->ni_dent.d_ino = ip->i_number;
    1051:     u.u_error = rdwri(UIO_WRITE, dp, (caddr_t)&ndp->ni_dent,
    1052:             (int)DIRSIZ(&ndp->ni_dent), ndp->ni_offset,
    1053:             UIO_SYSSPACE, IO_UNIT|IO_SYNC, (int *)0);
    1054:     iput(dp);
    1055: }
    1056:
    1057: /*
    1058:  * Return buffer with contents of block "offset"
    1059:  * from the beginning of directory "ip".  If "res"
    1060:  * is non-zero, fill it in with a pointer to the
    1061:  * remaining space in the directory.
    1062:  *
    1063:  * A mapin() of the buffer is done even if "res" is zero so that the
    1064:  * mapout() done later will have something to work with.
    1065:  */
    1066: struct buf *
    1067: blkatoff(ip, offset, res)
    1068:     struct inode *ip;
    1069:     off_t offset;
    1070:     char **res;
    1071: {
    1072:     register struct fs *fs = ip->i_fs;
    1073:     daddr_t lbn = lblkno(offset);
    1074:     register struct buf *bp;
    1075:     daddr_t bn;
    1076:     char *junk;
    1077:
    1078:     bn = bmap(ip, lbn, B_READ, 0);
    1079:     if (u.u_error)
    1080:         return (0);
    1081:     if (bn == (daddr_t)-1) {
    1082:         dirbad(ip, offset, "hole in dir");
    1083:         return (0);
    1084:     }
    1085:     bp = bread(ip->i_dev, bn);
    1086:     if (bp->b_flags & B_ERROR) {
    1087:         brelse(bp);
    1088:         return (0);
    1089:     }
    1090:     junk = (caddr_t)mapin(bp);
    1091:     if (res)
    1092:         *res = junk + (u_int)blkoff(offset);
    1093:     return (bp);
    1094: }
    1095:
    1096: /*
    1097:  * Check if a directory is empty or not.
    1098:  * Inode supplied must be locked.
    1099:  *
    1100:  * Using a struct dirtemplate here is not precisely
    1101:  * what we want, but better than using a struct direct.
    1102:  *
    1103:  * NB: does not handle corrupted directories.
    1104:  */
    1105: dirempty(ip, parentino)
    1106:     register struct inode *ip;
    1107:     ino_t parentino;
    1108: {
    1109:     register off_t off;
    1110:     struct dirtemplate dbuf;
    1111:     register struct direct *dp = (struct direct *)&dbuf;
    1112:     int error, count;
    1113: #define MINDIRSIZ (sizeof (struct dirtemplate) / 2)
    1114:
    1115:     for (off = 0; off < ip->i_size; off += dp->d_reclen) {
    1116:         error = rdwri(UIO_READ, ip, (caddr_t)dp, MINDIRSIZ,
    1117:             off, UIO_SYSSPACE, IO_UNIT, &count);
    1118:         /*
    1119: 		 * Since we read MINDIRSIZ, residual must
    1120: 		 * be 0 unless we're at end of file.
    1121: 		 */
    1122:         if (error || count != 0)
    1123:             return (0);
    1124:         /* avoid infinite loops */
    1125:         if (dp->d_reclen == 0)
    1126:             return (0);
    1127:         /* skip empty entries */
    1128:         if (dp->d_ino == 0)
    1129:             continue;
    1130:         /* accept only "." and ".." */
    1131:         if (dp->d_namlen > 2)
    1132:             return (0);
    1133:         if (dp->d_name[0] != '.')
    1134:             return (0);
    1135:         /*
    1136: 		 * At this point d_namlen must be 1 or 2.
    1137: 		 * 1 implies ".", 2 implies ".." if second
    1138: 		 * char is also "."
    1139: 		 */
    1140:         if (dp->d_namlen == 1)
    1141:             continue;
    1142:         if (dp->d_name[1] == '.' && dp->d_ino == parentino)
    1143:             continue;
    1144:         return (0);
    1145:     }
    1146:     return (1);
    1147: }
    1148:
    1149: /*
    1150:  * Check if source directory is in the path of the target directory.
    1151:  * Target is supplied locked, source is unlocked.
    1152:  * The target is always iput() before returning.
    1153:  */
    1154: checkpath(source, target)
    1155:     struct inode *source, *target;
    1156: {
    1157:     struct dirtemplate dirbuf;
    1158:     register struct inode *ip;
    1159:     register int error = 0;
    1160:
    1161:     ip = target;
    1162:     if (ip->i_number == source->i_number) {
    1163:         error = EEXIST;
    1164:         goto out;
    1165:     }
    1166:     if (ip->i_number == ROOTINO)
    1167:         goto out;
    1168:
    1169:     for (;;) {
    1170:         if ((ip->i_mode&IFMT) != IFDIR) {
    1171:             error = ENOTDIR;
    1172:             break;
    1173:         }
    1174:         error = rdwri(UIO_READ, ip, (caddr_t)&dirbuf,
    1175:                 sizeof(struct dirtemplate), (off_t)0,
    1176:                 UIO_SYSSPACE, IO_UNIT, (int *)0);
    1177:         if (error != 0)
    1178:             break;
    1179:         if (dirbuf.dotdot_namlen != 2 ||
    1180:             dirbuf.dotdot_name[0] != '.' ||
    1181:             dirbuf.dotdot_name[1] != '.') {
    1182:             error = ENOTDIR;
    1183:             break;
    1184:         }
    1185:         if (dirbuf.dotdot_ino == source->i_number) {
    1186:             error = EINVAL;
    1187:             break;
    1188:         }
    1189:         if (dirbuf.dotdot_ino == ROOTINO)
    1190:             break;
    1191:         iput(ip);
    1192:         ip = iget(ip->i_dev, ip->i_fs, dirbuf.dotdot_ino);
    1193:         if (ip == NULL) {
    1194:             error = u.u_error;
    1195:             break;
    1196:         }
    1197:     }
    1198:
    1199: out:
    1200:     if (error == ENOTDIR)
    1201:         printf("checkpath: .. !dir\n");
    1202:     if (ip != NULL)
    1203:         iput(ip);
    1204:     return (error);
    1205: }
    1206:
    1207: /*
    1208:  * Name cache initialization, from main() when we are booting
    1209:  */
    1210: nchinit()
    1211: {
    1212:     register union nchash *nchp;
    1213:     register struct namecache *ncp;
    1214:     segm    seg5;
    1215:
    1216:     saveseg5(seg5);
    1217:     mapseg5(nmidesc.se_addr,nmidesc.se_desc);
    1218:     nchhead = 0;
    1219:     nchtail = &nchhead;
    1220:     for (ncp = namecache; ncp < &namecache[nchsize]; ncp++) {
    1221:         ncp->nc_forw = ncp;         /* hash chain */
    1222:         ncp->nc_back = ncp;
    1223:         ncp->nc_nxt = NULL;         /* lru chain */
    1224:         *nchtail = ncp;
    1225:         ncp->nc_prev = nchtail;
    1226:         nchtail = &ncp->nc_nxt;
    1227:         /* all else is zero already */
    1228:     }
    1229:     for (nchp = nchash; nchp < &nchash[NCHHASH]; nchp++) {
    1230:         nchp->nch_head[0] = nchp;
    1231:         nchp->nch_head[1] = nchp;
    1232:     }
    1233:     restorseg5(seg5);
    1234: }
    1235:
    1236: /*
    1237:  * Cache flush, called when filesys is umounted to
    1238:  * remove entries that would now be invalid
    1239:  *
    1240:  * The line "nxtcp = nchhead" near the end is to avoid potential problems
    1241:  * if the cache lru chain is modified while we are dumping the
    1242:  * inode.  This makes the algorithm O(n^2), but do you think I care?
    1243:  */
    1244: nchinval(dev)
    1245:     register dev_t dev;
    1246: {
    1247:     register struct namecache *ncp, *nxtcp;
    1248:     segm    seg5;
    1249:
    1250:     saveseg5(seg5);
    1251:     mapseg5(nmidesc.se_addr,nmidesc.se_desc);
    1252:     for (ncp = nchhead; ncp; ncp = nxtcp) {
    1253:         nxtcp = ncp->nc_nxt;
    1254:         if (ncp->nc_ip == NULL ||
    1255:             (ncp->nc_idev != dev && ncp->nc_dev != dev))
    1256:             continue;
    1257:         /* free the resources we had */
    1258:         ncp->nc_idev = NODEV;
    1259:         ncp->nc_dev = NODEV;
    1260:         ncp->nc_id = NULL;
    1261:         ncp->nc_ino = 0;
    1262:         ncp->nc_ip = NULL;
    1263:         remque(ncp);        /* remove entry from its hash chain */
    1264:         ncp->nc_forw = ncp; /* and make a dummy one */
    1265:         ncp->nc_back = ncp;
    1266:         /* delete this entry from LRU chain */
    1267:         *ncp->nc_prev = nxtcp;
    1268:         if (nxtcp)
    1269:             nxtcp->nc_prev = ncp->nc_prev;
    1270:         else
    1271:             nchtail = ncp->nc_prev;
    1272:         /* cause rescan of list, it may have altered */
    1273:         nxtcp = nchhead;
    1274:         /* put the now-free entry at head of LRU */
    1275:         ncp->nc_nxt = nxtcp;
    1276:         ncp->nc_prev = &nchhead;
    1277:         nxtcp->nc_prev = &ncp->nc_nxt;
    1278:         nchhead = ncp;
    1279:     }
    1280:     restorseg5(seg5);
    1281: }
    1282:
    1283: /*
    1284:  * Name cache invalidation of all entries.
    1285:  */
    1286: cacheinvalall()
    1287: {
    1288:     register struct namecache *ncp, *encp = &namecache[nchsize];
    1289:     segm    seg5;
    1290:
    1291:     saveseg5(seg5);
    1292:     mapseg5(nmidesc.se_addr, nmidesc.se_desc);
    1293:     for (ncp = namecache; ncp < encp; ncp++)
    1294:         ncp->nc_id = 0;
    1295:     restorseg5(seg5);
    1296: }

#### Defined functions

blkatoff defined in line 1066; used 5 times

- in line 20, 391, 413, 943, 1028

cacheinvalall defined in line 1286; never used
checkpath defined in line 1154; used 1 times

- in /usr/src/sys/sys/ufs_syscalls.c line 896

dirbad defined in line 847; used 3 times

- in line 438, 557, 1082

dirbadentry defined in line 865; used 1 times

- in line 437

dirempty defined in line 1105; used 2 times

- in /usr/src/sys/sys/ufs_syscalls.c line 960, 1305

direnter defined in line 889; used 4 times

- in /usr/src/sys/sys/ufs_syscalls.c line 282, 928, 1124, 1236

dirremove defined in line 1009; used 3 times

- in /usr/src/sys/sys/ufs_syscalls.c line 368, 1051, 1318

dirrewrite defined in line 1044; used 1 times

- in /usr/src/sys/sys/ufs_syscalls.c line 973

namei defined in line 111; used 34 times

- in /usr/src/sys/h/inode.h line 175
- in /usr/src/sys/sys/init_main.c line 353
- in /usr/src/sys/sys/kern_exec.c line 68, 179
- in /usr/src/sys/sys/kern_sig.c line 647
- in /usr/src/sys/sys/quota_kern.c line 710
- in /usr/src/sys/sys/sys_kern.c line 110, 143
- in /usr/src/sys/sys/ufs_mount.c line 48, 334
- in /usr/src/sys/sys/ufs_syscalls.c line 83, 208, 251, 269, 317, 349, 400, 446, 470, 498, 548, 619, 722, 802, 861, 899, 1003, 1165, 1240, 1274
- in /usr/src/sys/sys/ufs_syscalls2.c line 32, 280
- in /usr/src/sys/sys/vfs_vnops.c line 78, 101

nchinit defined in line 1210; used 1 times

- in /usr/src/sys/sys/init_main.c line 108

nchinval defined in line 1244; used 1 times

- in /usr/src/sys/sys/ufs_mount.c line 283

#### Defined variables

dirchk defined in line 21; used 1 times

- in line 437

nchash defined in line 37; used 3 times

- in line 254, 1229(2)

nchhead defined in line 41; used 11 times

- in line 347-350(4), 732, 1218-1219(2), 1252, 1273-1278(3)

nchstats defined in line 42; used 8 times

- in line 251, 265-271(3), 324-328(2), 396, 551

nchtail defined in line 41; used 13 times

- in line 284-286(3), 344, 740, 754-756(3), 1219-1226(4), 1271

#### Defined union's

nchash defined in line 34; used 6 times

- in line 35(2), 141(2), 1212(2)

#### Defined macros

MINDIRSIZ defined in line 1113; used 1 times

- in line 1116

NCHHASH defined in line 26; used 6 times

- in line 28-31(4), 37, 1229

NHASH defined in line 31; used 1 times

- in line 254

nch_back defined in line 39; never used
nch_forw defined in line 38; used 1 times

- in line 255

Last modified: 1997-01-30 Generated: 2016-12-26 | Generated by src2html V0.67 page hit count: 2320 |