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