add proc pointers to appropriate VOP and vnode operations
[unix-history] / usr / src / sys / ufs / ufs / ufs_lookup.c
... / ...
CommitLineData
1/*
2 * Copyright (c) 1989 The Regents of the University of California.
3 * All rights reserved.
4 *
5 * %sccs.include.redist.c%
6 *
7 * @(#)ufs_lookup.c 7.29 (Berkeley) %G%
8 */
9
10#include "param.h"
11#include "namei.h"
12#include "buf.h"
13#include "file.h"
14#include "vnode.h"
15
16#include "quota.h"
17#include "inode.h"
18#include "fs.h"
19
20struct nchstats nchstats;
21int dirchk = 1;
22
23/*
24 * Convert a component of a pathname into a pointer to a locked inode.
25 * This is a very central and rather complicated routine.
26 * If the file system is not maintained in a strict tree hierarchy,
27 * this can result in a deadlock situation (see comments in code below).
28 *
29 * The flag argument is LOOKUP, CREATE, RENAME, or DELETE depending on
30 * whether the name is to be looked up, created, renamed, or deleted.
31 * When CREATE, RENAME, or DELETE is specified, information usable in
32 * creating, renaming, or deleting a directory entry may be calculated.
33 * If flag has LOCKPARENT or'ed into it and the target of the pathname
34 * exists, lookup returns both the target and its parent directory locked.
35 * When creating or renaming and LOCKPARENT is specified, the target may
36 * not be ".". When deleting and LOCKPARENT is specified, the target may
37 * be "."., but the caller must check to ensure it does an vrele and iput
38 * instead of two iputs.
39 *
40 * Overall outline of ufs_lookup:
41 *
42 * check accessibility of directory
43 * look for name in cache, if found, then if at end of path
44 * and deleting or creating, drop it, else return name
45 * search for name in directory, to found or notfound
46 * notfound:
47 * if creating, return locked directory, leaving info on available slots
48 * else return error
49 * found:
50 * if at end of path and deleting, return information to allow delete
51 * if at end of path and rewriting (RENAME and LOCKPARENT), lock target
52 * inode and return info to allow rewrite
53 * if not at end, add name to cache; if at end and neither creating
54 * nor deleting, add name to cache
55 *
56 * NOTE: (LOOKUP | LOCKPARENT) currently returns the parent inode unlocked.
57 */
58ufs_lookup(vdp, ndp, p)
59 register struct vnode *vdp;
60 register struct nameidata *ndp;
61 struct proc *p;
62{
63 register struct inode *dp; /* the directory we are searching */
64 register struct fs *fs; /* file system that directory is in */
65 struct buf *bp = 0; /* a buffer of directory entries */
66 register struct direct *ep; /* the current directory entry */
67 int entryoffsetinblock; /* offset of ep in bp's buffer */
68 enum {NONE, COMPACT, FOUND} slotstatus;
69 int slotoffset = -1; /* offset of area with free space */
70 int slotsize; /* size of area at slotoffset */
71 int slotfreespace; /* amount of space free in slot */
72 int slotneeded; /* size of the entry we're seeking */
73 int numdirpasses; /* strategy for directory search */
74 int endsearch; /* offset to end directory search */
75 int prevoff; /* ndp->ni_offset of previous entry */
76 struct inode *pdp; /* saved dp during symlink work */
77 struct inode *tdp; /* returned by iget */
78 off_t enduseful; /* pointer past last used dir slot */
79 int flag; /* LOOKUP, CREATE, RENAME, or DELETE */
80 int lockparent; /* 1 => lockparent flag is set */
81 int wantparent; /* 1 => wantparent or lockparent flag */
82 int error;
83
84 ndp->ni_dvp = vdp;
85 ndp->ni_vp = NULL;
86 dp = VTOI(vdp);
87 fs = dp->i_fs;
88 lockparent = ndp->ni_nameiop & LOCKPARENT;
89 flag = ndp->ni_nameiop & OPMASK;
90 wantparent = ndp->ni_nameiop & (LOCKPARENT|WANTPARENT);
91
92 /*
93 * Check accessiblity of directory.
94 */
95 if ((dp->i_mode&IFMT) != IFDIR)
96 return (ENOTDIR);
97 if (error = ufs_access(vdp, VEXEC, ndp->ni_cred, p))
98 return (error);
99
100 /*
101 * We now have a segment name to search for, and a directory to search.
102 *
103 * Before tediously performing a linear scan of the directory,
104 * check the name cache to see if the directory/name pair
105 * we are looking for is known already.
106 */
107 if (error = cache_lookup(ndp)) {
108 int vpid; /* capability number of vnode */
109
110 if (error == ENOENT)
111 return (error);
112#ifdef PARANOID
113 if (vdp == ndp->ni_rdir && ndp->ni_isdotdot)
114 panic("ufs_lookup: .. through root");
115#endif
116 /*
117 * Get the next vnode in the path.
118 * See comment below starting `Step through' for
119 * an explaination of the locking protocol.
120 */
121 pdp = dp;
122 dp = VTOI(ndp->ni_vp);
123 vdp = ndp->ni_vp;
124 vpid = vdp->v_id;
125 if (pdp == dp) {
126 VREF(vdp);
127 error = 0;
128 } else if (ndp->ni_isdotdot) {
129 IUNLOCK(pdp);
130 error = vget(vdp);
131 if (!error && lockparent && *ndp->ni_next == '\0')
132 ILOCK(pdp);
133 } else {
134 error = vget(vdp);
135 if (!lockparent || error || *ndp->ni_next != '\0')
136 IUNLOCK(pdp);
137 }
138 /*
139 * Check that the capability number did not change
140 * while we were waiting for the lock.
141 */
142 if (!error) {
143 if (vpid == vdp->v_id)
144 return (0);
145 iput(dp);
146 if (lockparent && pdp != dp && *ndp->ni_next == '\0')
147 IUNLOCK(pdp);
148 }
149 ILOCK(pdp);
150 dp = pdp;
151 vdp = ITOV(dp);
152 ndp->ni_vp = NULL;
153 }
154
155 /*
156 * Suppress search for slots unless creating
157 * file and at end of pathname, in which case
158 * we watch for a place to put the new file in
159 * case it doesn't already exist.
160 */
161 slotstatus = FOUND;
162 if ((flag == CREATE || flag == RENAME) && *ndp->ni_next == 0) {
163 slotstatus = NONE;
164 slotfreespace = 0;
165 slotneeded = DIRSIZ(&ndp->ni_dent);
166 }
167
168 /*
169 * If there is cached information on a previous search of
170 * this directory, pick up where we last left off.
171 * We cache only lookups as these are the most common
172 * and have the greatest payoff. Caching CREATE has little
173 * benefit as it usually must search the entire directory
174 * to determine that the entry does not exist. Caching the
175 * location of the last DELETE or RENAME has not reduced
176 * profiling time and hence has been removed in the interest
177 * of simplicity.
178 */
179 if (flag != LOOKUP || dp->i_diroff == 0 || dp->i_diroff > dp->i_size) {
180 ndp->ni_offset = 0;
181 numdirpasses = 1;
182 } else {
183 ndp->ni_offset = dp->i_diroff;
184 entryoffsetinblock = blkoff(fs, ndp->ni_offset);
185 if (entryoffsetinblock != 0) {
186 error = blkatoff(dp, ndp->ni_offset, (char **)0, &bp);
187 if (error)
188 return (error);
189 }
190 numdirpasses = 2;
191 nchstats.ncs_2passes++;
192 }
193 endsearch = roundup(dp->i_size, DIRBLKSIZ);
194 enduseful = 0;
195
196searchloop:
197 while (ndp->ni_offset < endsearch) {
198 /*
199 * If offset is on a block boundary,
200 * read the next directory block.
201 * Release previous if it exists.
202 */
203 if (blkoff(fs, ndp->ni_offset) == 0) {
204 if (bp != NULL)
205 brelse(bp);
206 error = blkatoff(dp, ndp->ni_offset, (char **)0, &bp);
207 if (error)
208 return (error);
209 entryoffsetinblock = 0;
210 }
211 /*
212 * If still looking for a slot, and at a DIRBLKSIZE
213 * boundary, have to start looking for free space again.
214 */
215 if (slotstatus == NONE &&
216 (entryoffsetinblock & (DIRBLKSIZ - 1)) == 0) {
217 slotoffset = -1;
218 slotfreespace = 0;
219 }
220 /*
221 * Get pointer to next entry.
222 * Full validation checks are slow, so we only check
223 * enough to insure forward progress through the
224 * directory. Complete checks can be run by patching
225 * "dirchk" to be true.
226 */
227 ep = (struct direct *)(bp->b_un.b_addr + entryoffsetinblock);
228 if (ep->d_reclen == 0 ||
229 dirchk && dirbadentry(ep, entryoffsetinblock)) {
230 int i;
231
232 dirbad(dp, ndp->ni_offset, "mangled entry");
233 i = DIRBLKSIZ - (entryoffsetinblock & (DIRBLKSIZ - 1));
234 ndp->ni_offset += i;
235 entryoffsetinblock += i;
236 continue;
237 }
238
239 /*
240 * If an appropriate sized slot has not yet been found,
241 * check to see if one is available. Also accumulate space
242 * in the current block so that we can determine if
243 * compaction is viable.
244 */
245 if (slotstatus != FOUND) {
246 int size = ep->d_reclen;
247
248 if (ep->d_ino != 0)
249 size -= DIRSIZ(ep);
250 if (size > 0) {
251 if (size >= slotneeded) {
252 slotstatus = FOUND;
253 slotoffset = ndp->ni_offset;
254 slotsize = ep->d_reclen;
255 } else if (slotstatus == NONE) {
256 slotfreespace += size;
257 if (slotoffset == -1)
258 slotoffset = ndp->ni_offset;
259 if (slotfreespace >= slotneeded) {
260 slotstatus = COMPACT;
261 slotsize = ndp->ni_offset +
262 ep->d_reclen - slotoffset;
263 }
264 }
265 }
266 }
267
268 /*
269 * Check for a name match.
270 */
271 if (ep->d_ino) {
272 if (ep->d_namlen == ndp->ni_dent.d_namlen &&
273 !bcmp(ndp->ni_ptr, ep->d_name,
274 (unsigned)ep->d_namlen)) {
275 /*
276 * Save directory entry's inode number and
277 * reclen in ndp->ni_dent, and release
278 * directory buffer.
279 */
280 ndp->ni_dent.d_ino = ep->d_ino;
281 ndp->ni_dent.d_reclen = ep->d_reclen;
282 brelse(bp);
283 goto found;
284 }
285 }
286 prevoff = ndp->ni_offset;
287 ndp->ni_offset += ep->d_reclen;
288 entryoffsetinblock += ep->d_reclen;
289 if (ep->d_ino)
290 enduseful = ndp->ni_offset;
291 }
292/* notfound: */
293 /*
294 * If we started in the middle of the directory and failed
295 * to find our target, we must check the beginning as well.
296 */
297 if (numdirpasses == 2) {
298 numdirpasses--;
299 ndp->ni_offset = 0;
300 endsearch = dp->i_diroff;
301 goto searchloop;
302 }
303 if (bp != NULL)
304 brelse(bp);
305 /*
306 * If creating, and at end of pathname and current
307 * directory has not been removed, then can consider
308 * allowing file to be created.
309 */
310 if ((flag == CREATE || flag == RENAME) &&
311 *ndp->ni_next == 0 && dp->i_nlink != 0) {
312 /*
313 * Access for write is interpreted as allowing
314 * creation of files in the directory.
315 */
316 if (error = ufs_access(vdp, VWRITE, ndp->ni_cred, p))
317 return (error);
318 /*
319 * Return an indication of where the new directory
320 * entry should be put. If we didn't find a slot,
321 * then set ndp->ni_count to 0 indicating that the new
322 * slot belongs at the end of the directory. If we found
323 * a slot, then the new entry can be put in the range
324 * [ndp->ni_offset .. ndp->ni_offset + ndp->ni_count)
325 */
326 if (slotstatus == NONE) {
327 ndp->ni_offset = roundup(dp->i_size, DIRBLKSIZ);
328 ndp->ni_count = 0;
329 enduseful = ndp->ni_offset;
330 } else {
331 ndp->ni_offset = slotoffset;
332 ndp->ni_count = slotsize;
333 if (enduseful < slotoffset + slotsize)
334 enduseful = slotoffset + slotsize;
335 }
336 ndp->ni_endoff = roundup(enduseful, DIRBLKSIZ);
337 dp->i_flag |= IUPD|ICHG;
338 /*
339 * We return with the directory locked, so that
340 * the parameters we set up above will still be
341 * valid if we actually decide to do a direnter().
342 * We return ni_vp == NULL to indicate that the entry
343 * does not currently exist; we leave a pointer to
344 * the (locked) directory inode in ndp->ni_dvp.
345 *
346 * NB - if the directory is unlocked, then this
347 * information cannot be used.
348 */
349 if (!lockparent)
350 IUNLOCK(dp);
351 }
352 /*
353 * Insert name into cache (as non-existent) if appropriate.
354 */
355 if (ndp->ni_makeentry && flag != CREATE)
356 cache_enter(ndp);
357 return (ENOENT);
358
359found:
360 if (numdirpasses == 2)
361 nchstats.ncs_pass2++;
362 /*
363 * Check that directory length properly reflects presence
364 * of this entry.
365 */
366 if (entryoffsetinblock + DIRSIZ(ep) > dp->i_size) {
367 dirbad(dp, ndp->ni_offset, "i_size too small");
368 dp->i_size = entryoffsetinblock + DIRSIZ(ep);
369 dp->i_flag |= IUPD|ICHG;
370 }
371
372 /*
373 * Found component in pathname.
374 * If the final component of path name, save information
375 * in the cache as to where the entry was found.
376 */
377 if (*ndp->ni_next == '\0' && flag == LOOKUP)
378 dp->i_diroff = ndp->ni_offset &~ (DIRBLKSIZ - 1);
379
380 /*
381 * If deleting, and at end of pathname, return
382 * parameters which can be used to remove file.
383 * If the wantparent flag isn't set, we return only
384 * the directory (in ndp->ni_dvp), otherwise we go
385 * on and lock the inode, being careful with ".".
386 */
387 if (flag == DELETE && *ndp->ni_next == 0) {
388 /*
389 * Write access to directory required to delete files.
390 */
391 if (error = ufs_access(vdp, VWRITE, ndp->ni_cred, p))
392 return (error);
393 /*
394 * Return pointer to current entry in ndp->ni_offset,
395 * and distance past previous entry (if there
396 * is a previous entry in this block) in ndp->ni_count.
397 * Save directory inode pointer in ndp->ni_dvp for dirremove().
398 */
399 if ((ndp->ni_offset&(DIRBLKSIZ-1)) == 0)
400 ndp->ni_count = 0;
401 else
402 ndp->ni_count = ndp->ni_offset - prevoff;
403 if (dp->i_number == ndp->ni_dent.d_ino) {
404 VREF(vdp);
405 ndp->ni_vp = vdp;
406 return (0);
407 }
408 if (error = iget(dp, ndp->ni_dent.d_ino, &tdp))
409 return (error);
410 /*
411 * If directory is "sticky", then user must own
412 * the directory, or the file in it, else she
413 * may not delete it (unless she's root). This
414 * implements append-only directories.
415 */
416 if ((dp->i_mode & ISVTX) &&
417 ndp->ni_cred->cr_uid != 0 &&
418 ndp->ni_cred->cr_uid != dp->i_uid &&
419 tdp->i_uid != ndp->ni_cred->cr_uid) {
420 iput(tdp);
421 return (EPERM);
422 }
423 ndp->ni_vp = ITOV(tdp);
424 if (!lockparent)
425 IUNLOCK(dp);
426 return (0);
427 }
428
429 /*
430 * If rewriting (RENAME), return the inode and the
431 * information required to rewrite the present directory
432 * Must get inode of directory entry to verify it's a
433 * regular file, or empty directory.
434 */
435 if (flag == RENAME && wantparent && *ndp->ni_next == 0) {
436 if (error = ufs_access(vdp, VWRITE, ndp->ni_cred, p))
437 return (error);
438 /*
439 * Careful about locking second inode.
440 * This can only occur if the target is ".".
441 */
442 if (dp->i_number == ndp->ni_dent.d_ino)
443 return (EISDIR);
444 if (error = iget(dp, ndp->ni_dent.d_ino, &tdp))
445 return (error);
446 ndp->ni_vp = ITOV(tdp);
447 if (!lockparent)
448 IUNLOCK(dp);
449 return (0);
450 }
451
452 /*
453 * Step through the translation in the name. We do not `iput' the
454 * directory because we may need it again if a symbolic link
455 * is relative to the current directory. Instead we save it
456 * unlocked as "pdp". We must get the target inode before unlocking
457 * the directory to insure that the inode will not be removed
458 * before we get it. We prevent deadlock by always fetching
459 * inodes from the root, moving down the directory tree. Thus
460 * when following backward pointers ".." we must unlock the
461 * parent directory before getting the requested directory.
462 * There is a potential race condition here if both the current
463 * and parent directories are removed before the `iget' for the
464 * inode associated with ".." returns. We hope that this occurs
465 * infrequently since we cannot avoid this race condition without
466 * implementing a sophisticated deadlock detection algorithm.
467 * Note also that this simple deadlock detection scheme will not
468 * work if the file system has any hard links other than ".."
469 * that point backwards in the directory structure.
470 */
471 pdp = dp;
472 if (ndp->ni_isdotdot) {
473 IUNLOCK(pdp); /* race to get the inode */
474 if (error = iget(dp, ndp->ni_dent.d_ino, &tdp)) {
475 ILOCK(pdp);
476 return (error);
477 }
478 if (lockparent && *ndp->ni_next == '\0')
479 ILOCK(pdp);
480 ndp->ni_vp = ITOV(tdp);
481 } else if (dp->i_number == ndp->ni_dent.d_ino) {
482 VREF(vdp); /* we want ourself, ie "." */
483 ndp->ni_vp = vdp;
484 } else {
485 if (error = iget(dp, ndp->ni_dent.d_ino, &tdp))
486 return (error);
487 if (!lockparent || *ndp->ni_next != '\0')
488 IUNLOCK(pdp);
489 ndp->ni_vp = ITOV(tdp);
490 }
491
492 /*
493 * Insert name into cache if appropriate.
494 */
495 if (ndp->ni_makeentry)
496 cache_enter(ndp);
497 return (0);
498}
499
500
501dirbad(ip, offset, how)
502 struct inode *ip;
503 off_t offset;
504 char *how;
505{
506
507 printf("%s: bad dir ino %d at offset %d: %s\n",
508 ip->i_fs->fs_fsmnt, ip->i_number, offset, how);
509 panic("bad dir");
510}
511
512/*
513 * Do consistency checking on a directory entry:
514 * record length must be multiple of 4
515 * entry must fit in rest of its DIRBLKSIZ block
516 * record must be large enough to contain entry
517 * name is not longer than MAXNAMLEN
518 * name must be as long as advertised, and null terminated
519 */
520dirbadentry(ep, entryoffsetinblock)
521 register struct direct *ep;
522 int entryoffsetinblock;
523{
524 register int i;
525
526 if ((ep->d_reclen & 0x3) != 0 ||
527 ep->d_reclen > DIRBLKSIZ - (entryoffsetinblock & (DIRBLKSIZ - 1)) ||
528 ep->d_reclen < DIRSIZ(ep) || ep->d_namlen > MAXNAMLEN)
529 return (1);
530 for (i = 0; i < ep->d_namlen; i++)
531 if (ep->d_name[i] == '\0')
532 return (1);
533 return (ep->d_name[i]);
534}
535
536/*
537 * Write a directory entry after a call to namei, using the parameters
538 * which it left in nameidata. The argument ip is the inode which the
539 * new directory entry will refer to. The nameidata field ndp->ni_dvp
540 * is a pointer to the directory to be written, which was left locked by
541 * namei. Remaining parameters (ndp->ni_offset, ndp->ni_count) indicate
542 * how the space for the new entry is to be gotten.
543 */
544direnter(ip, ndp)
545 struct inode *ip;
546 register struct nameidata *ndp;
547{
548 register struct direct *ep, *nep;
549 register struct inode *dp = VTOI(ndp->ni_dvp);
550 struct buf *bp;
551 int loc, spacefree, error = 0;
552 u_int dsize;
553 int newentrysize;
554 char *dirbuf;
555
556 ndp->ni_dent.d_ino = ip->i_number;
557 newentrysize = DIRSIZ(&ndp->ni_dent);
558 if (ndp->ni_count == 0) {
559 /*
560 * If ndp->ni_count is 0, then namei could find no space in the
561 * directory. In this case ndp->ni_offset will be on a directory
562 * block boundary and we will write the new entry into a fresh
563 * block.
564 */
565 if (ndp->ni_offset&(DIRBLKSIZ-1))
566 panic("wdir: newblk");
567 ndp->ni_dent.d_reclen = DIRBLKSIZ;
568 ndp->ni_count = newentrysize;
569 ndp->ni_resid = newentrysize;
570 ndp->ni_base = (caddr_t)&ndp->ni_dent;
571 ndp->ni_iov = &ndp->ni_nd.nd_iovec;
572 ndp->ni_iovcnt = 1;
573 ndp->ni_rw = UIO_WRITE;
574 ndp->ni_uioseg = UIO_SYSSPACE;
575 error =
576 ufs_write(ndp->ni_dvp, &ndp->ni_uio, IO_SYNC, ndp->ni_cred);
577 if (DIRBLKSIZ > dp->i_fs->fs_fsize) {
578 panic("wdir: blksize"); /* XXX - should grow w/balloc */
579 } else {
580 dp->i_size = roundup(dp->i_size, DIRBLKSIZ);
581 dp->i_flag |= ICHG;
582 }
583 return (error);
584 }
585
586 /*
587 * If ndp->ni_count is non-zero, then namei found space for the new
588 * entry in the range ndp->ni_offset to ndp->ni_offset + ndp->ni_count.
589 * in the directory. To use this space, we may have to compact
590 * the entries located there, by copying them together towards
591 * the beginning of the block, leaving the free space in
592 * one usable chunk at the end.
593 */
594
595 /*
596 * Increase size of directory if entry eats into new space.
597 * This should never push the size past a new multiple of
598 * DIRBLKSIZE.
599 *
600 * N.B. - THIS IS AN ARTIFACT OF 4.2 AND SHOULD NEVER HAPPEN.
601 */
602 if (ndp->ni_offset + ndp->ni_count > dp->i_size)
603 dp->i_size = ndp->ni_offset + ndp->ni_count;
604 /*
605 * Get the block containing the space for the new directory entry.
606 */
607 if (error = blkatoff(dp, ndp->ni_offset, (char **)&dirbuf, &bp))
608 return (error);
609 /*
610 * Find space for the new entry. In the simple case, the
611 * entry at offset base will have the space. If it does
612 * not, then namei arranged that compacting the region
613 * ndp->ni_offset to ndp->ni_offset+ndp->ni_count would yield the space.
614 */
615 ep = (struct direct *)dirbuf;
616 dsize = DIRSIZ(ep);
617 spacefree = ep->d_reclen - dsize;
618 for (loc = ep->d_reclen; loc < ndp->ni_count; ) {
619 nep = (struct direct *)(dirbuf + loc);
620 if (ep->d_ino) {
621 /* trim the existing slot */
622 ep->d_reclen = dsize;
623 ep = (struct direct *)((char *)ep + dsize);
624 } else {
625 /* overwrite; nothing there; header is ours */
626 spacefree += dsize;
627 }
628 dsize = DIRSIZ(nep);
629 spacefree += nep->d_reclen - dsize;
630 loc += nep->d_reclen;
631 bcopy((caddr_t)nep, (caddr_t)ep, dsize);
632 }
633 /*
634 * Update the pointer fields in the previous entry (if any),
635 * copy in the new entry, and write out the block.
636 */
637 if (ep->d_ino == 0) {
638 if (spacefree + dsize < newentrysize)
639 panic("wdir: compact1");
640 ndp->ni_dent.d_reclen = spacefree + dsize;
641 } else {
642 if (spacefree < newentrysize)
643 panic("wdir: compact2");
644 ndp->ni_dent.d_reclen = spacefree;
645 ep->d_reclen = dsize;
646 ep = (struct direct *)((char *)ep + dsize);
647 }
648 bcopy((caddr_t)&ndp->ni_dent, (caddr_t)ep, (u_int)newentrysize);
649 error = bwrite(bp);
650 dp->i_flag |= IUPD|ICHG;
651 if (!error && ndp->ni_endoff && ndp->ni_endoff < dp->i_size)
652 error = itrunc(dp, (u_long)ndp->ni_endoff, IO_SYNC);
653 return (error);
654}
655
656/*
657 * Remove a directory entry after a call to namei, using
658 * the parameters which it left in nameidata. The entry
659 * ni_offset contains the offset into the directory of the
660 * entry to be eliminated. The ni_count field contains the
661 * size of the previous record in the directory. If this
662 * is 0, the first entry is being deleted, so we need only
663 * zero the inode number to mark the entry as free. If the
664 * entry isn't the first in the directory, we must reclaim
665 * the space of the now empty record by adding the record size
666 * to the size of the previous entry.
667 */
668dirremove(ndp)
669 register struct nameidata *ndp;
670{
671 register struct inode *dp = VTOI(ndp->ni_dvp);
672 struct direct *ep;
673 struct buf *bp;
674 int error;
675
676 if (ndp->ni_count == 0) {
677 /*
678 * First entry in block: set d_ino to zero.
679 */
680 ndp->ni_dent.d_ino = 0;
681 ndp->ni_count = ndp->ni_resid = DIRSIZ(&ndp->ni_dent);
682 ndp->ni_base = (caddr_t)&ndp->ni_dent;
683 ndp->ni_iov = &ndp->ni_nd.nd_iovec;
684 ndp->ni_iovcnt = 1;
685 ndp->ni_rw = UIO_WRITE;
686 ndp->ni_uioseg = UIO_SYSSPACE;
687 error =
688 ufs_write(ndp->ni_dvp, &ndp->ni_uio, IO_SYNC, ndp->ni_cred);
689 } else {
690 /*
691 * Collapse new free space into previous entry.
692 */
693 if (error = blkatoff(dp, ndp->ni_offset - ndp->ni_count,
694 (char **)&ep, &bp)) {
695 return (error);
696 }
697 ep->d_reclen += ndp->ni_dent.d_reclen;
698 error = bwrite(bp);
699 dp->i_flag |= IUPD|ICHG;
700 }
701 return (error);
702}
703
704/*
705 * Rewrite an existing directory entry to point at the inode
706 * supplied. The parameters describing the directory entry are
707 * set up by a call to namei.
708 */
709dirrewrite(dp, ip, ndp)
710 struct inode *dp, *ip;
711 struct nameidata *ndp;
712{
713
714 ndp->ni_dent.d_ino = ip->i_number;
715 ndp->ni_count = ndp->ni_resid = DIRSIZ(&ndp->ni_dent);
716 ndp->ni_base = (caddr_t)&ndp->ni_dent;
717 ndp->ni_iov = &ndp->ni_nd.nd_iovec;
718 ndp->ni_iovcnt = 1;
719 ndp->ni_rw = UIO_WRITE;
720 ndp->ni_uioseg = UIO_SYSSPACE;
721 return (ufs_write(ITOV(dp), &ndp->ni_uio, IO_SYNC, ndp->ni_cred));
722}
723
724/*
725 * Return buffer with contents of block "offset"
726 * from the beginning of directory "ip". If "res"
727 * is non-zero, fill it in with a pointer to the
728 * remaining space in the directory.
729 */
730blkatoff(ip, offset, res, bpp)
731 struct inode *ip;
732 off_t offset;
733 char **res;
734 struct buf **bpp;
735{
736 register struct fs *fs = ip->i_fs;
737 daddr_t lbn = lblkno(fs, offset);
738 int bsize = blksize(fs, ip, lbn);
739 struct buf *bp;
740 daddr_t bn;
741 int error;
742
743 *bpp = 0;
744 if (error = bread(ITOV(ip), lbn, bsize, NOCRED, &bp)) {
745 brelse(bp);
746 return (error);
747 }
748 if (res)
749 *res = bp->b_un.b_addr + blkoff(fs, offset);
750 *bpp = bp;
751 return (0);
752}
753
754/*
755 * Check if a directory is empty or not.
756 * Inode supplied must be locked.
757 *
758 * Using a struct dirtemplate here is not precisely
759 * what we want, but better than using a struct direct.
760 *
761 * NB: does not handle corrupted directories.
762 */
763dirempty(ip, parentino, cred)
764 register struct inode *ip;
765 ino_t parentino;
766 struct ucred *cred;
767{
768 register off_t off;
769 struct dirtemplate dbuf;
770 register struct direct *dp = (struct direct *)&dbuf;
771 int error, count;
772#define MINDIRSIZ (sizeof (struct dirtemplate) / 2)
773
774 for (off = 0; off < ip->i_size; off += dp->d_reclen) {
775 error = vn_rdwr(UIO_READ, ITOV(ip), (caddr_t)dp, MINDIRSIZ, off,
776 UIO_SYSSPACE, IO_NODELOCKED, cred, &count, (struct proc *)0);
777 /*
778 * Since we read MINDIRSIZ, residual must
779 * be 0 unless we're at end of file.
780 */
781 if (error || count != 0)
782 return (0);
783 /* avoid infinite loops */
784 if (dp->d_reclen == 0)
785 return (0);
786 /* skip empty entries */
787 if (dp->d_ino == 0)
788 continue;
789 /* accept only "." and ".." */
790 if (dp->d_namlen > 2)
791 return (0);
792 if (dp->d_name[0] != '.')
793 return (0);
794 /*
795 * At this point d_namlen must be 1 or 2.
796 * 1 implies ".", 2 implies ".." if second
797 * char is also "."
798 */
799 if (dp->d_namlen == 1)
800 continue;
801 if (dp->d_name[1] == '.' && dp->d_ino == parentino)
802 continue;
803 return (0);
804 }
805 return (1);
806}
807
808/*
809 * Check if source directory is in the path of the target directory.
810 * Target is supplied locked, source is unlocked.
811 * The target is always iput() before returning.
812 */
813checkpath(source, target, cred)
814 struct inode *source, *target;
815 struct ucred *cred;
816{
817 struct dirtemplate dirbuf;
818 struct inode *ip;
819 int error = 0;
820
821 ip = target;
822 if (ip->i_number == source->i_number) {
823 error = EEXIST;
824 goto out;
825 }
826 if (ip->i_number == ROOTINO)
827 goto out;
828
829 for (;;) {
830 if ((ip->i_mode&IFMT) != IFDIR) {
831 error = ENOTDIR;
832 break;
833 }
834 error = vn_rdwr(UIO_READ, ITOV(ip), (caddr_t)&dirbuf,
835 sizeof (struct dirtemplate), (off_t)0, UIO_SYSSPACE,
836 IO_NODELOCKED, cred, (int *)0, (struct proc *)0);
837 if (error != 0)
838 break;
839 if (dirbuf.dotdot_namlen != 2 ||
840 dirbuf.dotdot_name[0] != '.' ||
841 dirbuf.dotdot_name[1] != '.') {
842 error = ENOTDIR;
843 break;
844 }
845 if (dirbuf.dotdot_ino == source->i_number) {
846 error = EINVAL;
847 break;
848 }
849 if (dirbuf.dotdot_ino == ROOTINO)
850 break;
851 iput(ip);
852 if (error = iget(ip, dirbuf.dotdot_ino, &ip))
853 break;
854 }
855
856out:
857 if (error == ENOTDIR)
858 printf("checkpath: .. not a directory\n");
859 if (ip != NULL)
860 iput(ip);
861 return (error);
862}