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