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