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