pack buffer size into trace record for buffer cache
[unix-history] / usr / src / sys / kern / vfs_lookup.c
CommitLineData
84ef30ec 1/* vfs_lookup.c 6.3 83/12/03 */
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;
10873320 18/*
6bd0bb92
BJ
19 * Convert a pathname into a pointer to a locked inode,
20 * with side effects usable in creating and removing files.
21 * This is a very central and rather complicated routine.
22 *
23 * The func argument gives the routine which returns successive
4f083fd7
SL
24 * characters of the name to be translated.
25 *
26 * The flag argument is (LOOKUP, CREATE, DELETE) depending on whether
27 * the name is to be (looked up, created, deleted). If flag has
28 * LOCKPARENT or'ed into it and the target of the pathname exists,
29 * namei returns both the target and its parent directory locked.
30 * If the file system is not maintained in a strict tree hierarchy,
31 * this can result in a deadlock situation. When creating and
32 * LOCKPARENT is specified, the target may not be ".". When deleting
33 * and LOCKPARENT is specified, the target may be ".", but the caller
34 * must check to insure it does an irele and iput instead of two iputs.
35 *
36 * The follow argument is 1 when symbolic links are to be followed
37 * when they occur at the end of the name translation process.
10873320 38 *
6bd0bb92
BJ
39 * Overall outline:
40 *
41 * copy in name
42 * get starting directory
43 * dirloop:
44 * check accessibility of directory
45 * dirloop2:
46 * copy next component of name to u.u_dent
47 * handle degenerate case where name is null string
48 * search for name in directory, to found or notfound
49 * notfound:
4f083fd7 50 * if creating, return locked directory, leaving info on avail. slots
6bd0bb92
BJ
51 * else return error
52 * found:
53 * if at end of path and deleting, return information to allow delete
4f083fd7
SL
54 * if at end of path and rewriting (create and LOCKPARENT), lock targe
55 * inode and return info to allow rewrite
6bd0bb92
BJ
56 * if .. and on mounted filesys, look in mount table for parent
57 * if symbolic link, massage name in buffer and continue at dirloop
58 * if more components of name, do next level at dirloop
59 * return the answer as locked inode
4f083fd7
SL
60 *
61 * NOTE: (LOOKUP | LOCKPARENT) currently returns the parent inode,
62 * but unlocked.
10873320
BJ
63 */
64struct inode *
f5039631
BJ
65namei(func, flag, follow)
66 int (*func)(), flag, follow;
10873320 67{
6bd0bb92
BJ
68 register char *cp; /* pointer into pathname argument */
69/* these variables refer to things which must be freed or unlocked */
70 register struct inode *dp = 0; /* the directory we are searching */
71 register struct fs *fs; /* file system that directory is in */
72 register struct buf *bp = 0; /* a buffer of directory entries */
73 register struct direct *ep; /* the current directory entry */
74 int entryoffsetinblock; /* offset of ep in bp's buffer */
75 register struct buf *nbp; /* buffer storing path name argument */
76/* these variables hold information about the search for a slot */
77 enum {NONE, COMPACT, FOUND} slotstatus;
78 int slotoffset = -1; /* offset of area with free space */
79 int slotsize; /* size of area at slotoffset */
80 int slotfreespace; /* amount of space free in slot */
81 int slotneeded; /* size of the entry we're seeking */
82/* */
84ef30ec
KM
83 int numdirpasses; /* strategy for directory search */
84 int endsearch; /* offset to end directory search */
6bd0bb92
BJ
85 int prevoff; /* u.u_offset of previous entry */
86 int nlink = 0; /* number of symbolic links taken */
87 struct inode *pdp; /* saved dp during symlink work */
88 int i;
4f083fd7 89 int lockparent;
10873320 90
4f083fd7
SL
91 lockparent = flag & LOCKPARENT;
92 flag &= ~LOCKPARENT;
f5039631 93 /*
6bd0bb92
BJ
94 * Get a buffer for the name to be translated, and copy the
95 * name into the buffer.
f5039631 96 */
6459ebe0 97 nbp = geteblk(MAXPATHLEN);
6bd0bb92
BJ
98 for (cp = nbp->b_un.b_addr; *cp = (*func)(); ) {
99 if ((*cp&0377) == ('/'|0200) || (*cp&0200) && flag != 2) {
a21ae242 100 u.u_error = EPERM;
6bd0bb92 101 goto bad;
a21ae242 102 }
a21ae242 103 cp++;
6459ebe0 104 if (cp >= nbp->b_un.b_addr + MAXPATHLEN) {
f5039631 105 u.u_error = ENOENT;
6bd0bb92 106 goto bad;
f5039631
BJ
107 }
108 }
6bd0bb92
BJ
109 if (u.u_error)
110 goto bad;
111
10873320 112 /*
6bd0bb92 113 * Get starting directory.
10873320 114 */
6bd0bb92 115 cp = nbp->b_un.b_addr;
f5039631
BJ
116 if (*cp == '/') {
117 while (*cp == '/')
118 cp++;
10873320
BJ
119 if ((dp = u.u_rdir) == NULL)
120 dp = rootdir;
6bd0bb92
BJ
121 } else
122 dp = u.u_cdir;
123 fs = dp->i_fs;
f5039631
BJ
124 ilock(dp);
125 dp->i_count++;
6bd0bb92
BJ
126 u.u_pdir = (struct inode *)0xc0000000; /* illegal */
127
10873320 128 /*
6bd0bb92
BJ
129 * We come to dirloop to search a new directory.
130 * The directory must be locked so that it can be
131 * iput, and fs must be already set to dp->i_fs.
10873320 132 */
6bd0bb92 133dirloop:
a0aead5a 134 /*
6bd0bb92 135 * Check accessiblity of directory.
a0aead5a 136 */
6bd0bb92 137 if ((dp->i_mode&IFMT) != IFDIR) {
10873320 138 u.u_error = ENOTDIR;
6bd0bb92
BJ
139 goto bad;
140 }
141 if (access(dp, IEXEC))
142 goto bad;
143
6f004ab5 144dirloop2:
6bd0bb92
BJ
145 /*
146 * Copy next component of name to u.u_dent.
147 */
148 for (i = 0; *cp != 0 && *cp != '/'; cp++) {
6459ebe0 149 if (i >= MAXNAMLEN) {
f5039631 150 u.u_error = ENOENT;
6bd0bb92 151 goto bad;
6459ebe0 152 }
6bd0bb92 153 u.u_dent.d_name[i++] = *cp;
6459ebe0
KM
154 }
155 u.u_dent.d_namlen = i;
6bd0bb92
BJ
156 u.u_dent.d_name[i] = 0;
157
158 /*
159 * Check for degenerate name (e.g. / or "")
160 * which is a way of talking about a directory,
161 * e.g. like "/." or ".".
162 */
163 if (u.u_dent.d_name[0] == 0) {
c41d8c5c 164 if (flag || lockparent) {
78abd7aa 165 u.u_error = EISDIR;
6bd0bb92 166 goto bad;
f5039631 167 }
6459ebe0
KM
168 brelse(nbp);
169 return (dp);
f5039631 170 }
6bd0bb92 171
6459ebe0 172 /*
6bd0bb92
BJ
173 * Suppress search for slots unless creating
174 * file and at end of pathname, in which case
175 * we watch for a place to put the new file in
176 * case it doesn't already exist.
6459ebe0 177 */
6bd0bb92 178 slotstatus = FOUND;
4f083fd7 179 if (flag == CREATE && *cp == 0) {
6bd0bb92
BJ
180 slotstatus = NONE;
181 slotfreespace = 0;
182 slotneeded = DIRSIZ(&u.u_dent);
183 }
84ef30ec
KM
184 /*
185 * If this is the same directory that this process
186 * previously searched, pick up where we last left off.
187 * We cache only lookups as these are the most common,
188 * and have the greatest payoff. Caching CREATE has little
189 * benefit as it usually must search the entire directory
190 * to determine that the entry does not exist. Caching the
191 * location of the last DELETE has not reduced profiling time
192 * and hence has been removed in the interest of simplicity.
193 */
194 if (flag != LOOKUP || dp->i_number != u.u_ncache.nc_inumber ||
195 dp->i_dev != u.u_ncache.nc_dev) {
196 u.u_offset = 0;
197 numdirpasses = 1;
198 } else {
199 if (dp->i_mtime >= u.u_ncache.nc_time) {
200 u.u_ncache.nc_prevoffset &= ~(DIRBLKSIZ - 1);
201 u.u_ncache.nc_time = time.tv_sec;
202 }
203 u.u_offset = u.u_ncache.nc_prevoffset;
204 entryoffsetinblock = blkoff(fs, u.u_offset);
205 if (entryoffsetinblock != 0) {
206 bp = blkatoff(dp, u.u_offset, (char **)0);
207 if (bp == 0)
208 goto bad;
209 }
210 numdirpasses = 2;
211 }
212 endsearch = roundup(dp->i_size, DIRBLKSIZ);
6bd0bb92 213
84ef30ec
KM
214searchloop:
215 while (u.u_offset < endsearch) {
f5039631
BJ
216 /*
217 * If offset is on a block boundary,
218 * read the next directory block.
219 * Release previous if it exists.
220 */
6459ebe0 221 if (blkoff(fs, u.u_offset) == 0) {
f5039631
BJ
222 if (bp != NULL)
223 brelse(bp);
4a0415d6 224 bp = blkatoff(dp, u.u_offset, (char **)0);
6bd0bb92
BJ
225 if (bp == 0)
226 goto bad;
227 entryoffsetinblock = 0;
6459ebe0 228 }
6bd0bb92 229
6459ebe0 230 /*
6bd0bb92
BJ
231 * If still looking for a slot, and at a DIRBLKSIZE
232 * boundary, have to start looking for free space
233 * again.
6459ebe0 234 */
6bd0bb92
BJ
235 if (slotstatus == NONE &&
236 (entryoffsetinblock&(DIRBLKSIZ-1)) == 0) {
237 slotoffset = -1;
238 slotfreespace = 0;
239 }
240
241 /*
242 * Get pointer to next entry, and do consistency checking:
243 * record length must be multiple of 4
4a0415d6 244 * record length must not be zero
6bd0bb92
BJ
245 * entry must fit in rest of this DIRBLKSIZ block
246 * record must be large enough to contain name
4a0415d6
SL
247 * When dirchk is set we also check:
248 * name is not longer than MAXNAMLEN
6bd0bb92 249 * name must be as long as advertised, and null terminated
4a0415d6
SL
250 * Checking last two conditions is done only when dirchk is
251 * set, to save time.
6bd0bb92
BJ
252 */
253 ep = (struct direct *)(bp->b_un.b_addr + entryoffsetinblock);
254 i = DIRBLKSIZ - (entryoffsetinblock & (DIRBLKSIZ - 1));
4a0415d6
SL
255 if ((ep->d_reclen & 0x3) || ep->d_reclen == 0 ||
256 ep->d_reclen > i || DIRSIZ(ep) > ep->d_reclen ||
257 dirchk && (ep->d_namlen > MAXNAMLEN || dirbadname(ep))) {
6bd0bb92 258 dirbad(dp, "mangled entry");
6459ebe0 259 u.u_offset += i;
6bd0bb92 260 entryoffsetinblock += i;
6459ebe0
KM
261 continue;
262 }
6bd0bb92 263
6459ebe0 264 /*
6bd0bb92 265 * If an appropriate sized slot has not yet been found,
6459ebe0
KM
266 * check to see if one is available. Also accumulate space
267 * in the current block so that we can determine if
268 * compaction is viable.
269 */
6bd0bb92
BJ
270 if (slotstatus != FOUND) {
271 int size = ep->d_reclen;
272
6459ebe0
KM
273 if (ep->d_ino != 0)
274 size -= DIRSIZ(ep);
275 if (size > 0) {
6bd0bb92
BJ
276 if (size >= slotneeded) {
277 slotstatus = FOUND;
278 slotoffset = u.u_offset;
279 slotsize = ep->d_reclen;
280 } else if (slotstatus == NONE) {
281 slotfreespace += size;
282 if (slotoffset == -1)
283 slotoffset = u.u_offset;
284 if (slotfreespace >= slotneeded) {
285 slotstatus = COMPACT;
286 slotsize =
287 u.u_offset+ep->d_reclen -
288 slotoffset;
289 }
6459ebe0 290 }
f5039631 291 }
f5039631 292 }
6bd0bb92 293
f5039631 294 /*
6bd0bb92 295 * Check for a name match.
f5039631 296 */
6bd0bb92
BJ
297 if (ep->d_ino) {
298 if (ep->d_namlen == u.u_dent.d_namlen &&
299 !bcmp(u.u_dent.d_name, ep->d_name, ep->d_namlen))
300 goto found;
301 }
302 prevoff = u.u_offset;
6459ebe0 303 u.u_offset += ep->d_reclen;
6bd0bb92
BJ
304 entryoffsetinblock += ep->d_reclen;
305 }
84ef30ec
KM
306 /*
307 * If we started in the middle of the directory and failed
308 * to find our target, we must check the beginning as well.
309 */
310 if (numdirpasses == 2) {
311 numdirpasses--;
312 u.u_offset = 0;
313 endsearch = u.u_ncache.nc_prevoffset;
314 goto searchloop;
315 }
6bd0bb92
BJ
316/* notfound: */
317 /*
318 * If creating, and at end of pathname and current
4f083fd7
SL
319 * directory has not been removed, then can consider
320 * allowing file to be created.
6bd0bb92 321 */
4f083fd7 322 if (flag == CREATE && *cp == 0 && dp->i_nlink != 0) {
f5039631 323 /*
6bd0bb92
BJ
324 * Access for write is interpreted as allowing
325 * creation of files in the directory.
f5039631 326 */
6bd0bb92
BJ
327 if (access(dp, IWRITE))
328 goto bad;
f5039631 329 /*
6bd0bb92
BJ
330 * Return an indication of where the new directory
331 * entry should be put. If we didn't find a slot,
332 * then set u.u_count to 0 indicating that the
333 * new slot belongs at the end of the directory.
334 * If we found a slot, then the new entry can be
335 * put in the range [u.u_offset..u.u_offset+u.u_count)
f5039631 336 */
84ef30ec
KM
337 if (slotstatus == NONE) {
338 u.u_offset = roundup(dp->i_size, DIRBLKSIZ);
6bd0bb92 339 u.u_count = 0;
84ef30ec 340 } else {
6bd0bb92
BJ
341 u.u_offset = slotoffset;
342 u.u_count = slotsize;
5485e062 343 }
6bd0bb92
BJ
344 dp->i_flag |= IUPD|ICHG;
345 if (bp)
346 brelse(bp);
347 brelse(nbp);
f5039631 348 /*
6bd0bb92
BJ
349 * We return with the directory locked, so that
350 * the parameters we set up above will still be
351 * valid if we actually decide to do a direnter().
352 * We return NULL to indicate that the entry doesn't
353 * currently exist, leaving a pointer to the (locked)
354 * directory inode in u.u_pdir.
f5039631 355 */
6bd0bb92
BJ
356 u.u_pdir = dp;
357 return (NULL);
358 }
359 u.u_error = ENOENT;
360 goto bad;
361found:
362 /*
363 * Check that directory length properly reflects presence
364 * of this entry.
365 */
4a0415d6 366 if (entryoffsetinblock + DIRSIZ(ep) > dp->i_size) {
6bd0bb92 367 dirbad(dp, "i_size too small");
4a0415d6 368 dp->i_size = entryoffsetinblock + DIRSIZ(ep);
6bd0bb92
BJ
369 dp->i_flag |= IUPD|ICHG;
370 }
371
372 /*
84ef30ec
KM
373 * Found component in pathname.
374 * If final component of path name, save information
375 * in the cache as to where the entry was found.
376 */
377 if (*cp == '\0' && flag == LOOKUP) {
378 u.u_ncache.nc_prevoffset = u.u_offset;
379 u.u_ncache.nc_inumber = dp->i_number;
380 u.u_ncache.nc_dev = dp->i_dev;
381 u.u_ncache.nc_time = time.tv_sec;
382 }
383 /*
384 * Save directory entry in u.u_dent,
385 * and release directory buffer.
6bd0bb92 386 */
9f024ffc 387 bcopy((caddr_t)ep, (caddr_t)&u.u_dent, (u_int)DIRSIZ(ep));
6bd0bb92
BJ
388 brelse(bp);
389 bp = NULL;
390
391 /*
392 * If deleting, and at end of pathname, return
393 * parameters which can be used to remove file.
4f083fd7
SL
394 * If the lockparent flag isn't set, we return only
395 * the directory (in u.u_pdir), otherwise we go
396 * on and lock the inode, being careful with ".".
6bd0bb92 397 */
4f083fd7 398 if (flag == DELETE && *cp == 0) {
6bd0bb92
BJ
399 /*
400 * Write access to directory required to delete files.
401 */
402 if (access(dp, IWRITE))
403 goto bad;
4f083fd7 404 u.u_pdir = dp; /* for dirremove() */
6bd0bb92
BJ
405 /*
406 * Return pointer to current entry in u.u_offset,
407 * and distance past previous entry (if there
408 * is a previous entry in this block) in u.u_count.
409 * Save directory inode pointer in u.u_pdir for dirremove().
410 */
411 if ((u.u_offset&(DIRBLKSIZ-1)) == 0)
412 u.u_count = 0;
413 else
414 u.u_count = u.u_offset - prevoff;
4f083fd7
SL
415 if (lockparent) {
416 if (dp->i_number == u.u_dent.d_ino)
417 dp->i_count++;
418 else {
419 dp = iget(dp->i_dev, fs, u.u_dent.d_ino);
420 if (dp == NULL) {
421 iput(u.u_pdir);
422 goto bad;
423 }
424 }
425 }
6bd0bb92 426 brelse(nbp);
6bd0bb92
BJ
427 return (dp);
428 }
429
430 /*
431 * Special handling for ".." allowing chdir out of mounted
432 * file system: indirect .. in root inode to reevaluate
433 * in directory file system was mounted on.
434 */
435 if (u.u_dent.d_name[0] == '.' && u.u_dent.d_name[1] == '.' &&
436 u.u_dent.d_name[2] == '\0') {
437 if (dp == u.u_rdir)
438 u.u_dent.d_ino = dp->i_number;
439 else if (u.u_dent.d_ino == ROOTINO &&
440 dp->i_number == ROOTINO) {
441 for (i = 1; i < NMOUNT; i++)
442 if (mount[i].m_bufp != NULL &&
443 mount[i].m_dev == dp->i_dev) {
6459ebe0 444 iput(dp);
6bd0bb92 445 dp = mount[i].m_inodp;
f5039631
BJ
446 ilock(dp);
447 dp->i_count++;
6bd0bb92
BJ
448 fs = dp->i_fs;
449 cp -= 2; /* back over .. */
450 goto dirloop2;
f5039631 451 }
10873320 452 }
6bd0bb92
BJ
453 }
454
4f083fd7
SL
455 /*
456 * If rewriting (rename), return the inode and the
457 * information required to rewrite the present directory
458 * Must get inode of directory entry to verify it's a
459 * regular file, or empty directory.
460 */
461 if ((flag == CREATE && lockparent) && *cp == 0) {
462 if (access(dp, IWRITE))
463 goto bad;
464 u.u_pdir = dp; /* for dirrewrite() */
465 /*
466 * Careful about locking second inode.
467 * This can only occur if the target is ".".
468 */
469 if (dp->i_number == u.u_dent.d_ino) {
470 u.u_error = EISDIR; /* XXX */
471 goto bad;
472 }
473 dp = iget(dp->i_dev, fs, u.u_dent.d_ino);
474 if (dp == NULL) {
475 iput(u.u_pdir);
476 goto bad;
477 }
478 brelse(nbp);
479 return (dp);
480 }
481
6bd0bb92 482 /*
bde63aa5
KM
483 * Check for symbolic link, which may require us to massage the
484 * name before we continue translation. We do not `iput' the
485 * directory because we may need it again if the symbolic link
486 * is relative to the current directory. Instead we save it
487 * unlocked as "pdp". We must get the target inode before unlocking
488 * the directory to insure that the inode will not be removed
489 * before we get it. We prevent deadlock by always fetching
490 * inodes from the root, moving down the directory tree. Thus
491 * when following backward pointers ".." we must unlock the
492 * parent directory before getting the requested directory.
493 * There is a potential race condition here if both the current
494 * and parent directories are removed before the `iget' for the
495 * inode associated with ".." returns. We hope that this occurs
496 * infrequently since we cannot avoid this race condition without
a4358a25 497 * implementing a sophisticated deadlock detection algorithm.
bde63aa5
KM
498 * Note also that this simple deadlock detection scheme will not
499 * work if the file system has any hard links other than ".."
500 * that point backwards in the directory structure.
6bd0bb92
BJ
501 */
502 pdp = dp;
bde63aa5
KM
503 if (bcmp(u.u_dent.d_name, "..", 3) == 0) {
504 iunlock(pdp); /* race to get the inode */
505 dp = iget(dp->i_dev, fs, u.u_dent.d_ino);
506 if (dp == NULL)
507 goto bad2;
508 } else if (dp->i_number == u.u_dent.d_ino) {
509 dp->i_count++; /* we want ourself, ie "." */
510 } else {
511 dp = iget(dp->i_dev, fs, u.u_dent.d_ino);
512 iunlock(pdp);
513 if (dp == NULL)
514 goto bad2;
515 }
6bd0bb92
BJ
516 fs = dp->i_fs;
517
518 /*
519 * Check for symbolic link
520 */
521 if ((dp->i_mode & IFMT) == IFLNK && (follow || *cp == '/')) {
9f024ffc 522 u_int pathlen = strlen(cp) + 1;
6bd0bb92
BJ
523
524 if (dp->i_size + pathlen >= MAXPATHLEN - 1 ||
525 ++nlink > MAXSYMLINKS) {
526 u.u_error = ELOOP;
527 goto bad2;
528 }
d36096be 529 ovbcopy(cp, nbp->b_un.b_addr + dp->i_size, pathlen);
a6b6f679 530 u.u_error =
4f083fd7 531 rdwri(UIO_READ, dp, nbp->b_un.b_addr, (int)dp->i_size,
9f024ffc 532 0, 1, (int *)0);
6bd0bb92
BJ
533 if (u.u_error)
534 goto bad2;
535 cp = nbp->b_un.b_addr;
536 iput(dp);
f5039631 537 if (*cp == '/') {
6bd0bb92 538 irele(pdp);
f5039631
BJ
539 while (*cp == '/')
540 cp++;
6bd0bb92
BJ
541 if ((dp = u.u_rdir) == NULL)
542 dp = rootdir;
543 ilock(dp);
544 dp->i_count++;
545 } else {
546 dp = pdp;
547 ilock(dp);
f5039631 548 }
6bd0bb92
BJ
549 fs = dp->i_fs;
550 goto dirloop;
10873320 551 }
6bd0bb92 552
10873320 553 /*
6bd0bb92
BJ
554 * Not a symbolic link. If more pathname,
555 * continue at next component, else return.
10873320 556 */
6bd0bb92
BJ
557 if (*cp == '/') {
558 while (*cp == '/')
559 cp++;
4f083fd7 560 irele(pdp);
6bd0bb92 561 goto dirloop;
10873320 562 }
6bd0bb92 563 brelse(nbp);
4f083fd7
SL
564 if (lockparent)
565 u.u_pdir = pdp;
566 else
567 irele(pdp);
6bd0bb92
BJ
568 return (dp);
569bad2:
570 irele(pdp);
571bad:
572 if (bp)
573 brelse(bp);
574 if (dp)
575 iput(dp);
f5039631 576 brelse(nbp);
6459ebe0 577 return (NULL);
10873320
BJ
578}
579
6bd0bb92
BJ
580dirbad(ip, how)
581 struct inode *ip;
582 char *how;
583{
584
585 printf("%s: bad dir ino %d at offset %d: %s\n",
586 ip->i_fs->fs_fsmnt, ip->i_number, u.u_offset, how);
587}
588
589dirbadname(ep)
590 register struct direct *ep;
591{
6bd0bb92
BJ
592 register int i;
593
594 for (i = 0; i < ep->d_namlen; i++)
595 if (ep->d_name[i] == 0)
596 return (1);
597 return (ep->d_name[i]);
598}
599
6bd0bb92
BJ
600/*
601 * Write a directory entry after a call to namei, using the parameters
602 * which it left in the u. area. The argument ip is the inode which
603 * the new directory entry will refer to. The u. area field u.u_pdir is
604 * a pointer to the directory to be written, which was left locked by
605 * namei. Remaining parameters (u.u_offset, u.u_count) indicate
606 * how the space for the new entry is to be gotten.
607 */
608direnter(ip)
609 struct inode *ip;
f5039631 610{
6bd0bb92 611 register struct direct *ep, *nep;
6bd0bb92 612 struct buf *bp;
efa3a91c 613 int loc, spacefree, error = 0;
b32450f4
BJ
614 u_int dsize;
615 int newentrysize;
6bd0bb92 616 char *dirbuf;
f5039631 617
6bd0bb92
BJ
618 u.u_dent.d_ino = ip->i_number;
619 u.u_segflg = 1;
620 newentrysize = DIRSIZ(&u.u_dent);
621 if (u.u_count == 0) {
622 /*
623 * If u.u_count is 0, then namei could find no space in the
624 * directory. In this case u.u_offset will be on a directory
625 * block boundary and we will write the new entry into a fresh
626 * block.
627 */
628 if (u.u_offset&(DIRBLKSIZ-1))
629 panic("wdir: newblk");
630 u.u_dent.d_reclen = DIRBLKSIZ;
f2a5ad78 631 error = rdwri(UIO_WRITE, u.u_pdir, (caddr_t)&u.u_dent,
b32450f4 632 newentrysize, u.u_offset, 1, (int *)0);
6bd0bb92 633 iput(u.u_pdir);
f2a5ad78 634 return (error);
6bd0bb92
BJ
635 }
636
637 /*
638 * If u.u_count is non-zero, then namei found space for the
639 * new entry in the range u.u_offset to u.u_offset+u.u_count.
640 * in the directory. To use this space, we may have to compact
641 * the entries located there, by copying them together towards
642 * the beginning of the block, leaving the free space in
643 * one usable chunk at the end.
644 */
645
646 /*
647 * Increase size of directory if entry eats into new space.
648 * This should never push the size past a new multiple of
649 * DIRBLKSIZE.
650 */
4f083fd7 651 if (u.u_offset + u.u_count > u.u_pdir->i_size)
6bd0bb92 652 u.u_pdir->i_size = u.u_offset + u.u_count;
6bd0bb92
BJ
653
654 /*
655 * Get the block containing the space for the new directory
f2a5ad78 656 * entry. Should return error by result instead of u.u_error.
6bd0bb92 657 */
4a0415d6 658 bp = blkatoff(u.u_pdir, u.u_offset, (char **)&dirbuf);
4f083fd7
SL
659 if (bp == 0) {
660 iput(u.u_pdir);
f2a5ad78 661 return (u.u_error);
4f083fd7 662 }
6bd0bb92
BJ
663
664 /*
665 * Find space for the new entry. In the simple case, the
666 * entry at offset base will have the space. If it does
667 * not, then namei arranged that compacting the region
668 * u.u_offset to u.u_offset+u.u_count would yield the space.
669 */
670 ep = (struct direct *)dirbuf;
671 dsize = DIRSIZ(ep);
efa3a91c 672 spacefree = ep->d_reclen - dsize;
6bd0bb92
BJ
673 for (loc = ep->d_reclen; loc < u.u_count; ) {
674 nep = (struct direct *)(dirbuf + loc);
675 if (ep->d_ino) {
676 /* trim the existing slot */
677 ep->d_reclen = dsize;
678 ep = (struct direct *)((char *)ep + dsize);
679 } else {
680 /* overwrite; nothing there; header is ours */
efa3a91c 681 spacefree += dsize;
6bd0bb92
BJ
682 }
683 dsize = DIRSIZ(nep);
efa3a91c 684 spacefree += nep->d_reclen - dsize;
6bd0bb92 685 loc += nep->d_reclen;
9f024ffc 686 bcopy((caddr_t)nep, (caddr_t)ep, dsize);
6bd0bb92
BJ
687 }
688 /*
689 * Update the pointer fields in the previous entry (if any),
690 * copy in the new entry, and write out the block.
691 */
692 if (ep->d_ino == 0) {
efa3a91c 693 if (spacefree + dsize < newentrysize)
6bd0bb92 694 panic("wdir: compact1");
efa3a91c 695 u.u_dent.d_reclen = spacefree + dsize;
6bd0bb92 696 } else {
efa3a91c 697 if (spacefree < newentrysize)
6bd0bb92 698 panic("wdir: compact2");
efa3a91c 699 u.u_dent.d_reclen = spacefree;
6bd0bb92
BJ
700 ep->d_reclen = dsize;
701 ep = (struct direct *)((char *)ep + dsize);
702 }
3fd23f5c 703 bcopy((caddr_t)&u.u_dent, (caddr_t)ep, (u_int)newentrysize);
6bd0bb92
BJ
704 bwrite(bp);
705 u.u_pdir->i_flag |= IUPD|ICHG;
706 iput(u.u_pdir);
f2a5ad78 707 return (error);
6bd0bb92
BJ
708}
709
4f083fd7
SL
710/*
711 * Remove a directory entry after a call to namei, using the
712 * parameters which it left in the u. area. The u. entry
713 * u_offset contains the offset into the directory of the
714 * entry to be eliminated. The u_count field contains the
715 * size of the previous record in the directory. If this
716 * is 0, the first entry is being deleted, so we need only
717 * zero the inode number to mark the entry as free. If the
718 * entry isn't the first in the directory, we must reclaim
719 * the space of the now empty record by adding the record size
720 * to the size of the previous entry.
721 */
6bd0bb92
BJ
722dirremove()
723{
724 register struct inode *dp = u.u_pdir;
6bd0bb92
BJ
725 register struct buf *bp;
726 struct direct *ep;
727
36d4ed87 728 if (u.u_count == 0) {
6bd0bb92
BJ
729 /*
730 * First entry in block: set d_ino to zero.
731 */
36d4ed87 732 u.u_dent.d_ino = 0;
954ce9b1 733 (void) rdwri(UIO_WRITE, dp, (caddr_t)&u.u_dent,
b32450f4 734 (int)DIRSIZ(&u.u_dent), u.u_offset, 1, (int *)0);
36d4ed87 735 } else {
6bd0bb92
BJ
736 /*
737 * Collapse new free space into previous entry.
738 */
9f024ffc 739 bp = blkatoff(dp, (int)(u.u_offset - u.u_count), (char **)&ep);
6bd0bb92
BJ
740 if (bp == 0)
741 return (0);
742 ep->d_reclen += u.u_dent.d_reclen;
6bd0bb92
BJ
743 bwrite(bp);
744 dp->i_flag |= IUPD|ICHG;
745 }
746 return (1);
10873320 747}
6459ebe0 748
4f083fd7
SL
749/*
750 * Rewrite an existing directory entry to point at the inode
751 * supplied. The parameters describing the directory entry are
752 * set up by a call to namei.
753 */
754dirrewrite(dp, ip)
755 struct inode *dp, *ip;
756{
757
758 u.u_dent.d_ino = ip->i_number;
759 u.u_error = rdwri(UIO_WRITE, dp, (caddr_t)&u.u_dent,
760 (int)DIRSIZ(&u.u_dent), u.u_offset, 1, (int *)0);
761 iput(dp);
762}
763
4a0415d6
SL
764/*
765 * Return buffer with contents of block "offset"
766 * from the beginning of directory "ip". If "res"
767 * is non-zero, fill it in with a pointer to the
768 * remaining space in the directory.
769 */
6bd0bb92 770struct buf *
4a0415d6 771blkatoff(ip, offset, res)
6bd0bb92
BJ
772 struct inode *ip;
773 off_t offset;
774 char **res;
6459ebe0 775{
6bd0bb92 776 register struct fs *fs = ip->i_fs;
3fd23f5c 777 daddr_t lbn = lblkno(fs, offset);
6bd0bb92
BJ
778 int base = blkoff(fs, offset);
779 int bsize = blksize(fs, ip, lbn);
3fd23f5c 780 daddr_t bn = fsbtodb(fs, bmap(ip, lbn, B_WRITE, base, bsize));
6bd0bb92 781 register struct buf *bp;
6459ebe0 782
6bd0bb92
BJ
783 if (u.u_error)
784 return (0);
785 bp = bread(ip->i_dev, bn, bsize);
786 if (bp->b_flags & B_ERROR) {
787 brelse(bp);
788 return (0);
789 }
790 if (res)
791 *res = bp->b_un.b_addr + base;
6bd0bb92 792 return (bp);
6459ebe0 793}
4f083fd7
SL
794
795/*
796 * Check if a directory is empty or not.
797 * Inode supplied must be locked.
17d11193
SL
798 *
799 * Using a struct dirtemplate here is not precisely
800 * what we want, but better than using a struct direct.
801 *
802 * NB: does not handle corrupted directories.
4f083fd7
SL
803 */
804dirempty(ip)
05d2bbce 805 register struct inode *ip;
4f083fd7
SL
806{
807 register off_t off;
17d11193
SL
808 struct dirtemplate dbuf;
809 register struct direct *dp = (struct direct *)&dbuf;
05d2bbce 810 int error, count;
17d11193 811#define MINDIRSIZ (sizeof (struct dirtemplate) / 2)
4f083fd7
SL
812
813 for (off = 0; off < ip->i_size; off += dp->d_reclen) {
17d11193
SL
814 error = rdwri(UIO_READ, ip, (caddr_t)dp, MINDIRSIZ,
815 off, 1, &count);
816 /*
817 * Since we read MINDIRSIZ, residual must
818 * be 0 unless we're at end of file.
819 */
820 if (error || count != 0)
4f083fd7 821 return (0);
17d11193 822 /* skip empty entries */
4f083fd7
SL
823 if (dp->d_ino == 0)
824 continue;
17d11193
SL
825 /* accept only "." and ".." */
826 if (dp->d_namlen > 2)
827 return (0);
4f083fd7
SL
828 if (dp->d_name[0] != '.')
829 return (0);
17d11193
SL
830 /*
831 * At this point d_namlen must be 1 or 2.
832 * 1 implies ".", 2 implies ".." if second
833 * char is also "."
834 */
835 if (dp->d_namlen == 1 || dp->d_name[1] == '.')
4f083fd7
SL
836 continue;
837 return (0);
838 }
839 return (1);
840}
b1aa93b9
KM
841
842/*
843 * Check if source directory is in the path of the target directory.
844 * Target is supplied locked, source is unlocked.
845 * The target is always iput() before returning.
846 */
847checkpath(source, target)
848 struct inode *source, *target;
849{
850 struct dirtemplate dirbuf;
851 register struct inode *ip;
852 int error = 0;
853
854 ip = target;
855 if (ip->i_number == source->i_number) {
856 error = EEXIST;
857 goto out;
858 }
859 if (ip->i_number == ROOTINO)
860 goto out;
861
862 for (;;) {
863 if ((ip->i_mode&IFMT) != IFDIR) {
864 error = ENOTDIR;
865 break;
866 }
867 error = rdwri(UIO_READ, ip, (caddr_t)&dirbuf,
868 sizeof (struct dirtemplate), (off_t)0, 1, (int *)0);
869 if (error != 0)
870 break;
871 if (dirbuf.dotdot_namlen != 2 ||
872 bcmp(dirbuf.dotdot_name, "..", 3) != 0) {
873 error = ENOTDIR;
874 break;
875 }
876 if (dirbuf.dotdot_ino == source->i_number) {
877 error = EINVAL;
878 break;
879 }
880 if (dirbuf.dotdot_ino == ROOTINO)
881 break;
882 iput(ip);
883 ip = iget(ip->i_dev, ip->i_fs, dirbuf.dotdot_ino);
884 if (ip == NULL) {
885 error = u.u_error;
886 break;
887 }
888 }
889
890out:
891 if (error == ENOTDIR)
892 printf("checkpath: .. not a directory\n");
893 if (ip != NULL)
894 iput(ip);
895 return (error);
896}