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
cacheinvalall defined in line 1286; never used
checkpath defined in line 1154; used 1 times
dirbad defined in line 847; used 3 times
dirbadentry defined in line 865; used 1 times
dirempty defined in line 1105; used 2 times
direnter defined in line 889; used 4 times
dirremove defined in line 1009; used 3 times
dirrewrite defined in line 1044; used 1 times
nchinit defined in line 1210; used 1 times
nchinval defined in line 1244; used 1 times

Defined variables

dirchk defined in line 21; used 1 times
nchash defined in line 37; used 3 times
nchhead defined in line 41; used 11 times
nchstats defined in line 42; used 8 times
nchtail defined in line 41; used 13 times

Defined union's

nchash defined in line 34; used 6 times

Defined macros

MINDIRSIZ defined in line 1113; used 1 times
NCHHASH defined in line 26; used 6 times
NHASH defined in line 31; used 1 times
nch_back defined in line 39; never used
nch_forw defined in line 38; used 1 times
Last modified: 1997-01-30
Generated: 2016-12-26
Generated by src2html V0.67
page hit count: 5525
Valid CSS Valid XHTML 1.0 Strict