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