]> CyberLeo.Net >> Repos - FreeBSD/releng/7.2.git/blob - sys/ufs/ufs/ufs_lookup.c
Create releng/7.2 from stable/7 in preparation for 7.2-RELEASE.
[FreeBSD/releng/7.2.git] / sys / ufs / ufs / ufs_lookup.c
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  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 4. Neither the name of the University nor the names of its contributors
19  *    may be used to endorse or promote products derived from this software
20  *    without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  *
34  *      @(#)ufs_lookup.c        8.15 (Berkeley) 6/16/95
35  */
36
37 #include <sys/cdefs.h>
38 __FBSDID("$FreeBSD$");
39
40 #include "opt_ffs_broken_fixme.h"
41 #include "opt_ufs.h"
42 #include "opt_quota.h"
43
44 #include <sys/param.h>
45 #include <sys/systm.h>
46 #include <sys/kernel.h>
47 #include <sys/namei.h>
48 #include <sys/bio.h>
49 #include <sys/buf.h>
50 #include <sys/proc.h>
51 #include <sys/stat.h>
52 #include <sys/mount.h>
53 #include <sys/vnode.h>
54 #include <sys/sysctl.h>
55
56 #include <vm/vm.h>
57 #include <vm/vm_extern.h>
58
59 #include <ufs/ufs/extattr.h>
60 #include <ufs/ufs/quota.h>
61 #include <ufs/ufs/inode.h>
62 #include <ufs/ufs/dir.h>
63 #ifdef UFS_DIRHASH
64 #include <ufs/ufs/dirhash.h>
65 #endif
66 #include <ufs/ufs/ufsmount.h>
67 #include <ufs/ufs/ufs_extern.h>
68
69 #ifdef DIAGNOSTIC
70 static int      dirchk = 1;
71 #else
72 static int      dirchk = 0;
73 #endif
74
75 SYSCTL_INT(_debug, OID_AUTO, dircheck, CTLFLAG_RW, &dirchk, 0, "");
76
77 /* true if old FS format...*/
78 #define OFSFMT(vp)      ((vp)->v_mount->mnt_maxsymlinklen <= 0)
79
80 /*
81  * Convert a component of a pathname into a pointer to a locked inode.
82  * This is a very central and rather complicated routine.
83  * If the filesystem is not maintained in a strict tree hierarchy,
84  * this can result in a deadlock situation (see comments in code below).
85  *
86  * The cnp->cn_nameiop argument is LOOKUP, CREATE, RENAME, or DELETE depending
87  * on whether the name is to be looked up, created, renamed, or deleted.
88  * When CREATE, RENAME, or DELETE is specified, information usable in
89  * creating, renaming, or deleting a directory entry may be calculated.
90  * If flag has LOCKPARENT or'ed into it and the target of the pathname
91  * exists, lookup returns both the target and its parent directory locked.
92  * When creating or renaming and LOCKPARENT is specified, the target may
93  * not be ".".  When deleting and LOCKPARENT is specified, the target may
94  * be "."., but the caller must check to ensure it does an vrele and vput
95  * instead of two vputs.
96  *
97  * This routine is actually used as VOP_CACHEDLOOKUP method, and the
98  * filesystem employs the generic vfs_cache_lookup() as VOP_LOOKUP
99  * method.
100  *
101  * vfs_cache_lookup() performs the following for us:
102  *      check that it is a directory
103  *      check accessibility of directory
104  *      check for modification attempts on read-only mounts
105  *      if name found in cache
106  *          if at end of path and deleting or creating
107  *              drop it
108  *           else
109  *              return name.
110  *      return VOP_CACHEDLOOKUP()
111  *
112  * Overall outline of ufs_lookup:
113  *
114  *      search for name in directory, to found or notfound
115  * notfound:
116  *      if creating, return locked directory, leaving info on available slots
117  *      else return error
118  * found:
119  *      if at end of path and deleting, return information to allow delete
120  *      if at end of path and rewriting (RENAME and LOCKPARENT), lock target
121  *        inode and return info to allow rewrite
122  *      if not at end, add name to cache; if at end and neither creating
123  *        nor deleting, add name to cache
124  */
125 int
126 ufs_lookup(ap)
127         struct vop_cachedlookup_args /* {
128                 struct vnode *a_dvp;
129                 struct vnode **a_vpp;
130                 struct componentname *a_cnp;
131         } */ *ap;
132 {
133         struct vnode *vdp;              /* vnode for directory being searched */
134         struct inode *dp;               /* inode for directory being searched */
135         struct buf *bp;                 /* a buffer of directory entries */
136         struct direct *ep;              /* the current directory entry */
137         int entryoffsetinblock;         /* offset of ep in bp's buffer */
138         enum {NONE, COMPACT, FOUND} slotstatus;
139         doff_t slotoffset;              /* offset of area with free space */
140         doff_t i_diroff;                /* cached i_diroff value. */
141         doff_t i_offset;                /* cached i_offset value. */
142         int slotsize;                   /* size of area at slotoffset */
143         int slotfreespace;              /* amount of space free in slot */
144         int slotneeded;                 /* size of the entry we're seeking */
145         int numdirpasses;               /* strategy for directory search */
146         doff_t endsearch;               /* offset to end directory search */
147         doff_t prevoff;                 /* prev entry dp->i_offset */
148         struct vnode *pdp;              /* saved dp during symlink work */
149         struct vnode *tdp;              /* returned by VFS_VGET */
150         doff_t enduseful;               /* pointer past last used dir slot */
151         u_long bmask;                   /* block offset mask */
152         int namlen, error;
153         struct vnode **vpp = ap->a_vpp;
154         struct componentname *cnp = ap->a_cnp;
155         struct ucred *cred = cnp->cn_cred;
156         int flags = cnp->cn_flags;
157         int nameiop = cnp->cn_nameiop;
158         struct thread *td = cnp->cn_thread;
159         ino_t ino;
160         int ltype;
161
162         bp = NULL;
163         slotoffset = -1;
164 /*
165  *  XXX there was a soft-update diff about this I couldn't merge.
166  * I think this was the equiv.
167  */
168         *vpp = NULL;
169
170         vdp = ap->a_dvp;
171         dp = VTOI(vdp);
172
173         /*
174          * Create a vm object if vmiodirenable is enabled.
175          * Alternatively we could call vnode_create_vobject
176          * in VFS_VGET but we could end up creating objects
177          * that are never used.
178          */
179         vnode_create_vobject(vdp, DIP(dp, i_size), cnp->cn_thread);
180
181         /*
182          * We now have a segment name to search for, and a directory to search.
183          *
184          * Suppress search for slots unless creating
185          * file and at end of pathname, in which case
186          * we watch for a place to put the new file in
187          * case it doesn't already exist.
188          */
189         ino = 0;
190         i_diroff = dp->i_diroff;
191         slotstatus = FOUND;
192         slotfreespace = slotsize = slotneeded = 0;
193         if ((nameiop == CREATE || nameiop == RENAME) &&
194             (flags & ISLASTCN)) {
195                 slotstatus = NONE;
196                 slotneeded = DIRECTSIZ(cnp->cn_namelen);
197         }
198         bmask = VFSTOUFS(vdp->v_mount)->um_mountp->mnt_stat.f_iosize - 1;
199
200 #ifdef UFS_DIRHASH
201         /*
202          * Use dirhash for fast operations on large directories. The logic
203          * to determine whether to hash the directory is contained within
204          * ufsdirhash_build(); a zero return means that it decided to hash
205          * this directory and it successfully built up the hash table.
206          */
207         if (ufsdirhash_build(dp) == 0) {
208                 /* Look for a free slot if needed. */
209                 enduseful = dp->i_size;
210                 if (slotstatus != FOUND) {
211                         slotoffset = ufsdirhash_findfree(dp, slotneeded,
212                             &slotsize);
213                         if (slotoffset >= 0) {
214                                 slotstatus = COMPACT;
215                                 enduseful = ufsdirhash_enduseful(dp);
216                                 if (enduseful < 0)
217                                         enduseful = dp->i_size;
218                         }
219                 }
220                 /* Look up the component. */
221                 numdirpasses = 1;
222                 entryoffsetinblock = 0; /* silence compiler warning */
223                 switch (ufsdirhash_lookup(dp, cnp->cn_nameptr, cnp->cn_namelen,
224                     &i_offset, &bp, nameiop == DELETE ? &prevoff : NULL)) {
225                 case 0:
226                         ep = (struct direct *)((char *)bp->b_data +
227                             (i_offset & bmask));
228                         goto foundentry;
229                 case ENOENT:
230                         i_offset = roundup2(dp->i_size, DIRBLKSIZ);
231                         goto notfound;
232                 default:
233                         /* Something failed; just do a linear search. */
234                         break;
235                 }
236         }
237 #endif /* UFS_DIRHASH */
238         /*
239          * If there is cached information on a previous search of
240          * this directory, pick up where we last left off.
241          * We cache only lookups as these are the most common
242          * and have the greatest payoff. Caching CREATE has little
243          * benefit as it usually must search the entire directory
244          * to determine that the entry does not exist. Caching the
245          * location of the last DELETE or RENAME has not reduced
246          * profiling time and hence has been removed in the interest
247          * of simplicity.
248          */
249         if (nameiop != LOOKUP || i_diroff == 0 || i_diroff >= dp->i_size) {
250                 entryoffsetinblock = 0;
251                 i_offset = 0;
252                 numdirpasses = 1;
253         } else {
254                 i_offset = i_diroff;
255                 if ((entryoffsetinblock = i_offset & bmask) &&
256                     (error = UFS_BLKATOFF(vdp, (off_t)i_offset, NULL, &bp)))
257                         return (error);
258                 numdirpasses = 2;
259                 nchstats.ncs_2passes++;
260         }
261         prevoff = i_offset;
262         endsearch = roundup2(dp->i_size, DIRBLKSIZ);
263         enduseful = 0;
264
265 searchloop:
266         while (i_offset < endsearch) {
267                 /*
268                  * If necessary, get the next directory block.
269                  */
270                 if ((i_offset & bmask) == 0) {
271                         if (bp != NULL)
272                                 brelse(bp);
273                         error =
274                             UFS_BLKATOFF(vdp, (off_t)i_offset, NULL, &bp);
275                         if (error)
276                                 return (error);
277                         entryoffsetinblock = 0;
278                 }
279                 /*
280                  * If still looking for a slot, and at a DIRBLKSIZE
281                  * boundary, have to start looking for free space again.
282                  */
283                 if (slotstatus == NONE &&
284                     (entryoffsetinblock & (DIRBLKSIZ - 1)) == 0) {
285                         slotoffset = -1;
286                         slotfreespace = 0;
287                 }
288                 /*
289                  * Get pointer to next entry.
290                  * Full validation checks are slow, so we only check
291                  * enough to insure forward progress through the
292                  * directory. Complete checks can be run by patching
293                  * "dirchk" to be true.
294                  */
295                 ep = (struct direct *)((char *)bp->b_data + entryoffsetinblock);
296                 if (ep->d_reclen == 0 || ep->d_reclen >
297                     DIRBLKSIZ - (entryoffsetinblock & (DIRBLKSIZ - 1)) ||
298                     (dirchk && ufs_dirbadentry(vdp, ep, entryoffsetinblock))) {
299                         int i;
300
301                         ufs_dirbad(dp, i_offset, "mangled entry");
302                         i = DIRBLKSIZ - (entryoffsetinblock & (DIRBLKSIZ - 1));
303                         i_offset += i;
304                         entryoffsetinblock += i;
305                         continue;
306                 }
307
308                 /*
309                  * If an appropriate sized slot has not yet been found,
310                  * check to see if one is available. Also accumulate space
311                  * in the current block so that we can determine if
312                  * compaction is viable.
313                  */
314                 if (slotstatus != FOUND) {
315                         int size = ep->d_reclen;
316
317                         if (ep->d_ino != 0)
318                                 size -= DIRSIZ(OFSFMT(vdp), ep);
319                         if (size > 0) {
320                                 if (size >= slotneeded) {
321                                         slotstatus = FOUND;
322                                         slotoffset = i_offset;
323                                         slotsize = ep->d_reclen;
324                                 } else if (slotstatus == NONE) {
325                                         slotfreespace += size;
326                                         if (slotoffset == -1)
327                                                 slotoffset = i_offset;
328                                         if (slotfreespace >= slotneeded) {
329                                                 slotstatus = COMPACT;
330                                                 slotsize = i_offset +
331                                                       ep->d_reclen - slotoffset;
332                                         }
333                                 }
334                         }
335                 }
336
337                 /*
338                  * Check for a name match.
339                  */
340                 if (ep->d_ino) {
341 #                       if (BYTE_ORDER == LITTLE_ENDIAN)
342                                 if (OFSFMT(vdp))
343                                         namlen = ep->d_type;
344                                 else
345                                         namlen = ep->d_namlen;
346 #                       else
347                                 namlen = ep->d_namlen;
348 #                       endif
349                         if (namlen == cnp->cn_namelen &&
350                                 (cnp->cn_nameptr[0] == ep->d_name[0]) &&
351                             !bcmp(cnp->cn_nameptr, ep->d_name,
352                                 (unsigned)namlen)) {
353 #ifdef UFS_DIRHASH
354 foundentry:
355 #endif
356                                 /*
357                                  * Save directory entry's inode number and
358                                  * reclen in ndp->ni_ufs area, and release
359                                  * directory buffer.
360                                  */
361                                 if (vdp->v_mount->mnt_maxsymlinklen > 0 &&
362                                     ep->d_type == DT_WHT) {
363                                         slotstatus = FOUND;
364                                         slotoffset = i_offset;
365                                         slotsize = ep->d_reclen;
366                                         enduseful = dp->i_size;
367                                         ap->a_cnp->cn_flags |= ISWHITEOUT;
368                                         numdirpasses--;
369                                         goto notfound;
370                                 }
371                                 ino = ep->d_ino;
372                                 goto found;
373                         }
374                 }
375                 prevoff = i_offset;
376                 i_offset += ep->d_reclen;
377                 entryoffsetinblock += ep->d_reclen;
378                 if (ep->d_ino)
379                         enduseful = i_offset;
380         }
381 notfound:
382         /*
383          * If we started in the middle of the directory and failed
384          * to find our target, we must check the beginning as well.
385          */
386         if (numdirpasses == 2) {
387                 numdirpasses--;
388                 i_offset = 0;
389                 endsearch = i_diroff;
390                 goto searchloop;
391         }
392         if (bp != NULL)
393                 brelse(bp);
394         /*
395          * If creating, and at end of pathname and current
396          * directory has not been removed, then can consider
397          * allowing file to be created.
398          */
399         if ((nameiop == CREATE || nameiop == RENAME ||
400              (nameiop == DELETE &&
401               (ap->a_cnp->cn_flags & DOWHITEOUT) &&
402               (ap->a_cnp->cn_flags & ISWHITEOUT))) &&
403             (flags & ISLASTCN) && dp->i_effnlink != 0) {
404                 /*
405                  * Access for write is interpreted as allowing
406                  * creation of files in the directory.
407                  */
408                 error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_thread);
409                 if (error)
410                         return (error);
411                 /*
412                  * Return an indication of where the new directory
413                  * entry should be put.  If we didn't find a slot,
414                  * then set dp->i_count to 0 indicating
415                  * that the new slot belongs at the end of the
416                  * directory. If we found a slot, then the new entry
417                  * can be put in the range from dp->i_offset to
418                  * dp->i_offset + dp->i_count.
419                  */
420                 if (slotstatus == NONE) {
421                         dp->i_offset = roundup2(dp->i_size, DIRBLKSIZ);
422                         dp->i_count = 0;
423                         enduseful = dp->i_offset;
424                 } else if (nameiop == DELETE) {
425                         dp->i_offset = slotoffset;
426                         if ((dp->i_offset & (DIRBLKSIZ - 1)) == 0)
427                                 dp->i_count = 0;
428                         else
429                                 dp->i_count = dp->i_offset - prevoff;
430                 } else {
431                         dp->i_offset = slotoffset;
432                         dp->i_count = slotsize;
433                         if (enduseful < slotoffset + slotsize)
434                                 enduseful = slotoffset + slotsize;
435                 }
436                 dp->i_endoff = roundup2(enduseful, DIRBLKSIZ);
437                 /*
438                  * We return with the directory locked, so that
439                  * the parameters we set up above will still be
440                  * valid if we actually decide to do a direnter().
441                  * We return ni_vp == NULL to indicate that the entry
442                  * does not currently exist; we leave a pointer to
443                  * the (locked) directory inode in ndp->ni_dvp.
444                  * The pathname buffer is saved so that the name
445                  * can be obtained later.
446                  *
447                  * NB - if the directory is unlocked, then this
448                  * information cannot be used.
449                  */
450                 cnp->cn_flags |= SAVENAME;
451                 return (EJUSTRETURN);
452         }
453         /*
454          * Insert name into cache (as non-existent) if appropriate.
455          */
456         if ((cnp->cn_flags & MAKEENTRY) && nameiop != CREATE)
457                 cache_enter(vdp, *vpp, cnp);
458         return (ENOENT);
459
460 found:
461         if (numdirpasses == 2)
462                 nchstats.ncs_pass2++;
463         /*
464          * Check that directory length properly reflects presence
465          * of this entry.
466          */
467         if (i_offset + DIRSIZ(OFSFMT(vdp), ep) > dp->i_size) {
468                 ufs_dirbad(dp, i_offset, "i_size too small");
469                 dp->i_size = i_offset + DIRSIZ(OFSFMT(vdp), ep);
470                 DIP_SET(dp, i_size, dp->i_size);
471                 dp->i_flag |= IN_CHANGE | IN_UPDATE;
472         }
473         brelse(bp);
474
475         /*
476          * Found component in pathname.
477          * If the final component of path name, save information
478          * in the cache as to where the entry was found.
479          */
480         if ((flags & ISLASTCN) && nameiop == LOOKUP)
481                 dp->i_diroff = i_offset &~ (DIRBLKSIZ - 1);
482
483         /*
484          * If deleting, and at end of pathname, return
485          * parameters which can be used to remove file.
486          */
487         if (nameiop == DELETE && (flags & ISLASTCN)) {
488                 if (flags & LOCKPARENT)
489                         ASSERT_VOP_ELOCKED(vdp, __FUNCTION__);
490                 /*
491                  * Write access to directory required to delete files.
492                  */
493                 error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_thread);
494                 if (error)
495                         return (error);
496                 /*
497                  * Return pointer to current entry in dp->i_offset,
498                  * and distance past previous entry (if there
499                  * is a previous entry in this block) in dp->i_count.
500                  * Save directory inode pointer in ndp->ni_dvp for dirremove().
501                  *
502                  * Technically we shouldn't be setting these in the
503                  * WANTPARENT case (first lookup in rename()), but any
504                  * lookups that will result in directory changes will
505                  * overwrite these.
506                  */
507                 dp->i_offset = i_offset;
508                 if ((dp->i_offset & (DIRBLKSIZ - 1)) == 0)
509                         dp->i_count = 0;
510                 else
511                         dp->i_count = dp->i_offset - prevoff;
512                 if (dp->i_number == ino) {
513                         VREF(vdp);
514                         *vpp = vdp;
515                         return (0);
516                 }
517                 if ((error = VFS_VGET(vdp->v_mount, ino,
518                     LK_EXCLUSIVE, &tdp)) != 0)
519                         return (error);
520                 /*
521                  * If directory is "sticky", then user must own
522                  * the directory, or the file in it, else she
523                  * may not delete it (unless she's root). This
524                  * implements append-only directories.
525                  */
526                 if ((dp->i_mode & ISVTX) &&
527                     VOP_ACCESS(vdp, VADMIN, cred, cnp->cn_thread) &&
528                     VOP_ACCESS(tdp, VADMIN, cred, cnp->cn_thread)) {
529                         vput(tdp);
530                         return (EPERM);
531                 }
532                 *vpp = tdp;
533                 return (0);
534         }
535
536         /*
537          * If rewriting (RENAME), return the inode and the
538          * information required to rewrite the present directory
539          * Must get inode of directory entry to verify it's a
540          * regular file, or empty directory.
541          */
542         if (nameiop == RENAME && (flags & ISLASTCN)) {
543                 if ((error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_thread)))
544                         return (error);
545                 /*
546                  * Careful about locking second inode.
547                  * This can only occur if the target is ".".
548                  */
549                 dp->i_offset = i_offset;
550                 if (dp->i_number == ino)
551                         return (EISDIR);
552                 if ((error = VFS_VGET(vdp->v_mount, ino,
553                     LK_EXCLUSIVE, &tdp)) != 0)
554                         return (error);
555                 *vpp = tdp;
556                 cnp->cn_flags |= SAVENAME;
557                 return (0);
558         }
559
560         /*
561          * Step through the translation in the name.  We do not `vput' the
562          * directory because we may need it again if a symbolic link
563          * is relative to the current directory.  Instead we save it
564          * unlocked as "pdp".  We must get the target inode before unlocking
565          * the directory to insure that the inode will not be removed
566          * before we get it.  We prevent deadlock by always fetching
567          * inodes from the root, moving down the directory tree. Thus
568          * when following backward pointers ".." we must unlock the
569          * parent directory before getting the requested directory.
570          * There is a potential race condition here if both the current
571          * and parent directories are removed before the VFS_VGET for the
572          * inode associated with ".." returns.  We hope that this occurs
573          * infrequently since we cannot avoid this race condition without
574          * implementing a sophisticated deadlock detection algorithm.
575          * Note also that this simple deadlock detection scheme will not
576          * work if the filesystem has any hard links other than ".."
577          * that point backwards in the directory structure.
578          */
579         pdp = vdp;
580         if (flags & ISDOTDOT) {
581                 error = vn_vget_ino(pdp, ino, cnp->cn_lkflags, &tdp);
582                 if (error)
583                         return (error);
584                 *vpp = tdp;
585         } else if (dp->i_number == ino) {
586                 VREF(vdp);      /* we want ourself, ie "." */
587                 /*
588                  * When we lookup "." we still can be asked to lock it
589                  * differently.
590                  */
591                 ltype = cnp->cn_lkflags & LK_TYPE_MASK;
592                 if (ltype != VOP_ISLOCKED(vdp, td)) {
593                         if (ltype == LK_EXCLUSIVE)
594                                 vn_lock(vdp, LK_UPGRADE | LK_RETRY, td);
595                         else /* if (ltype == LK_SHARED) */
596                                 vn_lock(vdp, LK_DOWNGRADE | LK_RETRY, td);
597                 }
598                 *vpp = vdp;
599         } else {
600                 error = VFS_VGET(pdp->v_mount, ino, cnp->cn_lkflags, &tdp);
601                 if (error)
602                         return (error);
603                 *vpp = tdp;
604         }
605
606         /*
607          * Insert name into cache if appropriate.
608          */
609         if (cnp->cn_flags & MAKEENTRY)
610                 cache_enter(vdp, *vpp, cnp);
611         return (0);
612 }
613
614 void
615 ufs_dirbad(ip, offset, how)
616         struct inode *ip;
617         doff_t offset;
618         char *how;
619 {
620         struct mount *mp;
621
622         mp = ITOV(ip)->v_mount;
623         if ((mp->mnt_flag & MNT_RDONLY) == 0)
624                 panic("ufs_dirbad: %s: bad dir ino %lu at offset %ld: %s",
625                     mp->mnt_stat.f_mntonname, (u_long)ip->i_number, (long)offset, how);
626         else
627                 (void)printf("%s: bad dir ino %lu at offset %ld: %s\n",
628                     mp->mnt_stat.f_mntonname, (u_long)ip->i_number, (long)offset, how);
629 }
630
631 /*
632  * Do consistency checking on a directory entry:
633  *      record length must be multiple of 4
634  *      entry must fit in rest of its DIRBLKSIZ block
635  *      record must be large enough to contain entry
636  *      name is not longer than MAXNAMLEN
637  *      name must be as long as advertised, and null terminated
638  */
639 int
640 ufs_dirbadentry(dp, ep, entryoffsetinblock)
641         struct vnode *dp;
642         struct direct *ep;
643         int entryoffsetinblock;
644 {
645         int i, namlen;
646
647 #       if (BYTE_ORDER == LITTLE_ENDIAN)
648                 if (OFSFMT(dp))
649                         namlen = ep->d_type;
650                 else
651                         namlen = ep->d_namlen;
652 #       else
653                 namlen = ep->d_namlen;
654 #       endif
655         if ((ep->d_reclen & 0x3) != 0 ||
656             ep->d_reclen > DIRBLKSIZ - (entryoffsetinblock & (DIRBLKSIZ - 1)) ||
657             ep->d_reclen < DIRSIZ(OFSFMT(dp), ep) || namlen > MAXNAMLEN) {
658                 /*return (1); */
659                 printf("First bad\n");
660                 goto bad;
661         }
662         if (ep->d_ino == 0)
663                 return (0);
664         for (i = 0; i < namlen; i++)
665                 if (ep->d_name[i] == '\0') {
666                         /*return (1); */
667                         printf("Second bad\n");
668                         goto bad;
669                 }
670         if (ep->d_name[i])
671                 goto bad;
672         return (0);
673 bad:
674         return (1);
675 }
676
677 /*
678  * Construct a new directory entry after a call to namei, using the
679  * parameters that it left in the componentname argument cnp. The
680  * argument ip is the inode to which the new directory entry will refer.
681  */
682 void
683 ufs_makedirentry(ip, cnp, newdirp)
684         struct inode *ip;
685         struct componentname *cnp;
686         struct direct *newdirp;
687 {
688
689 #ifdef INVARIANTS
690         if ((cnp->cn_flags & SAVENAME) == 0)
691                 panic("ufs_makedirentry: missing name");
692 #endif
693         newdirp->d_ino = ip->i_number;
694         newdirp->d_namlen = cnp->cn_namelen;
695         bcopy(cnp->cn_nameptr, newdirp->d_name, (unsigned)cnp->cn_namelen + 1);
696         if (ITOV(ip)->v_mount->mnt_maxsymlinklen > 0)
697                 newdirp->d_type = IFTODT(ip->i_mode);
698         else {
699                 newdirp->d_type = 0;
700 #               if (BYTE_ORDER == LITTLE_ENDIAN)
701                         { u_char tmp = newdirp->d_namlen;
702                         newdirp->d_namlen = newdirp->d_type;
703                         newdirp->d_type = tmp; }
704 #               endif
705         }
706 }
707
708 /*
709  * Write a directory entry after a call to namei, using the parameters
710  * that it left in nameidata. The argument dirp is the new directory
711  * entry contents. Dvp is a pointer to the directory to be written,
712  * which was left locked by namei. Remaining parameters (dp->i_offset, 
713  * dp->i_count) indicate how the space for the new entry is to be obtained.
714  * Non-null bp indicates that a directory is being created (for the
715  * soft dependency code).
716  */
717 int
718 ufs_direnter(dvp, tvp, dirp, cnp, newdirbp)
719         struct vnode *dvp;
720         struct vnode *tvp;
721         struct direct *dirp;
722         struct componentname *cnp;
723         struct buf *newdirbp;
724 {
725         struct ucred *cr;
726         struct thread *td;
727         int newentrysize;
728         struct inode *dp;
729         struct buf *bp;
730         u_int dsize;
731         struct direct *ep, *nep;
732         int error, ret, blkoff, loc, spacefree, flags, namlen;
733         char *dirbuf;
734
735         td = curthread; /* XXX */
736         cr = td->td_ucred;
737
738         dp = VTOI(dvp);
739         newentrysize = DIRSIZ(OFSFMT(dvp), dirp);
740
741         if (dp->i_count == 0) {
742                 /*
743                  * If dp->i_count is 0, then namei could find no
744                  * space in the directory. Here, dp->i_offset will
745                  * be on a directory block boundary and we will write the
746                  * new entry into a fresh block.
747                  */
748                 if (dp->i_offset & (DIRBLKSIZ - 1))
749                         panic("ufs_direnter: newblk");
750                 flags = BA_CLRBUF;
751                 if (!DOINGSOFTDEP(dvp) && !DOINGASYNC(dvp))
752                         flags |= IO_SYNC;
753 #ifdef QUOTA
754                 if ((error = getinoquota(dp)) != 0) {
755                         if (DOINGSOFTDEP(dvp) && newdirbp != NULL)
756                                 bdwrite(newdirbp);
757                         return (error);
758                 }
759 #endif
760                 if ((error = UFS_BALLOC(dvp, (off_t)dp->i_offset, DIRBLKSIZ,
761                     cr, flags, &bp)) != 0) {
762                         if (DOINGSOFTDEP(dvp) && newdirbp != NULL)
763                                 bdwrite(newdirbp);
764                         return (error);
765                 }
766                 dp->i_size = dp->i_offset + DIRBLKSIZ;
767                 DIP_SET(dp, i_size, dp->i_size);
768                 dp->i_flag |= IN_CHANGE | IN_UPDATE;
769                 vnode_pager_setsize(dvp, (u_long)dp->i_size);
770                 dirp->d_reclen = DIRBLKSIZ;
771                 blkoff = dp->i_offset &
772                     (VFSTOUFS(dvp->v_mount)->um_mountp->mnt_stat.f_iosize - 1);
773                 bcopy((caddr_t)dirp, (caddr_t)bp->b_data + blkoff,newentrysize);
774 #ifdef UFS_DIRHASH
775                 if (dp->i_dirhash != NULL) {
776                         ufsdirhash_newblk(dp, dp->i_offset);
777                         ufsdirhash_add(dp, dirp, dp->i_offset);
778                         ufsdirhash_checkblock(dp, (char *)bp->b_data + blkoff,
779                             dp->i_offset);
780                 }
781 #endif
782                 if (DOINGSOFTDEP(dvp)) {
783                         /*
784                          * Ensure that the entire newly allocated block is a
785                          * valid directory so that future growth within the
786                          * block does not have to ensure that the block is
787                          * written before the inode.
788                          */
789                         blkoff += DIRBLKSIZ;
790                         while (blkoff < bp->b_bcount) {
791                                 ((struct direct *)
792                                    (bp->b_data + blkoff))->d_reclen = DIRBLKSIZ;
793                                 blkoff += DIRBLKSIZ;
794                         }
795                         if (softdep_setup_directory_add(bp, dp, dp->i_offset,
796                             dirp->d_ino, newdirbp, 1) == 0) {
797                                 bdwrite(bp);
798                                 return (UFS_UPDATE(dvp, 0));
799                         }
800                         /* We have just allocated a directory block in an
801                          * indirect block. Rather than tracking when it gets
802                          * claimed by the inode, we simply do a VOP_FSYNC
803                          * now to ensure that it is there (in case the user
804                          * does a future fsync). Note that we have to unlock
805                          * the inode for the entry that we just entered, as
806                          * the VOP_FSYNC may need to lock other inodes which
807                          * can lead to deadlock if we also hold a lock on
808                          * the newly entered node.
809                          */
810                         if ((error = bwrite(bp)))
811                                 return (error);
812                         if (tvp != NULL)
813                                 VOP_UNLOCK(tvp, 0, td);
814                         error = VOP_FSYNC(dvp, MNT_WAIT, td);
815                         if (tvp != NULL)
816                                 vn_lock(tvp, LK_EXCLUSIVE | LK_RETRY, td);
817                         return (error);
818                 }
819                 if (DOINGASYNC(dvp)) {
820                         bdwrite(bp);
821                         return (UFS_UPDATE(dvp, 0));
822                 }
823                 error = bwrite(bp);
824                 ret = UFS_UPDATE(dvp, 1);
825                 if (error == 0)
826                         return (ret);
827                 return (error);
828         }
829
830         /*
831          * If dp->i_count is non-zero, then namei found space for the new
832          * entry in the range dp->i_offset to dp->i_offset + dp->i_count
833          * in the directory. To use this space, we may have to compact
834          * the entries located there, by copying them together towards the
835          * beginning of the block, leaving the free space in one usable
836          * chunk at the end.
837          */
838
839         /*
840          * Increase size of directory if entry eats into new space.
841          * This should never push the size past a new multiple of
842          * DIRBLKSIZE.
843          *
844          * N.B. - THIS IS AN ARTIFACT OF 4.2 AND SHOULD NEVER HAPPEN.
845          */
846         if (dp->i_offset + dp->i_count > dp->i_size) {
847                 dp->i_size = dp->i_offset + dp->i_count;
848                 DIP_SET(dp, i_size, dp->i_size);
849         }
850         /*
851          * Get the block containing the space for the new directory entry.
852          */
853         error = UFS_BLKATOFF(dvp, (off_t)dp->i_offset, &dirbuf, &bp);
854         if (error) {
855                 if (DOINGSOFTDEP(dvp) && newdirbp != NULL)
856                         bdwrite(newdirbp);
857                 return (error);
858         }
859         /*
860          * Find space for the new entry. In the simple case, the entry at
861          * offset base will have the space. If it does not, then namei
862          * arranged that compacting the region dp->i_offset to
863          * dp->i_offset + dp->i_count would yield the space.
864          */
865         ep = (struct direct *)dirbuf;
866         dsize = ep->d_ino ? DIRSIZ(OFSFMT(dvp), ep) : 0;
867         spacefree = ep->d_reclen - dsize;
868         for (loc = ep->d_reclen; loc < dp->i_count; ) {
869                 nep = (struct direct *)(dirbuf + loc);
870
871                 /* Trim the existing slot (NB: dsize may be zero). */
872                 ep->d_reclen = dsize;
873                 ep = (struct direct *)((char *)ep + dsize);
874
875                 /* Read nep->d_reclen now as the bcopy() may clobber it. */
876                 loc += nep->d_reclen;
877                 if (nep->d_ino == 0) {
878                         /*
879                          * A mid-block unused entry. Such entries are
880                          * never created by the kernel, but fsck_ffs
881                          * can create them (and it doesn't fix them).
882                          *
883                          * Add up the free space, and initialise the
884                          * relocated entry since we don't bcopy it.
885                          */
886                         spacefree += nep->d_reclen;
887                         ep->d_ino = 0;
888                         dsize = 0;
889                         continue;
890                 }
891                 dsize = DIRSIZ(OFSFMT(dvp), nep);
892                 spacefree += nep->d_reclen - dsize;
893 #ifdef UFS_DIRHASH
894                 if (dp->i_dirhash != NULL)
895                         ufsdirhash_move(dp, nep,
896                             dp->i_offset + ((char *)nep - dirbuf),
897                             dp->i_offset + ((char *)ep - dirbuf));
898 #endif
899                 if (DOINGSOFTDEP(dvp))
900                         softdep_change_directoryentry_offset(dp, dirbuf,
901                             (caddr_t)nep, (caddr_t)ep, dsize); 
902                 else
903                         bcopy((caddr_t)nep, (caddr_t)ep, dsize);
904         }
905         /*
906          * Here, `ep' points to a directory entry containing `dsize' in-use
907          * bytes followed by `spacefree' unused bytes. If ep->d_ino == 0,
908          * then the entry is completely unused (dsize == 0). The value
909          * of ep->d_reclen is always indeterminate.
910          *
911          * Update the pointer fields in the previous entry (if any),
912          * copy in the new entry, and write out the block.
913          */
914 #       if (BYTE_ORDER == LITTLE_ENDIAN)
915                 if (OFSFMT(dvp))
916                         namlen = ep->d_type;
917                 else
918                         namlen = ep->d_namlen;
919 #       else
920                 namlen = ep->d_namlen;
921 #       endif
922         if (ep->d_ino == 0 ||
923             (ep->d_ino == WINO && namlen == dirp->d_namlen &&
924              bcmp(ep->d_name, dirp->d_name, dirp->d_namlen) == 0)) {
925                 if (spacefree + dsize < newentrysize)
926                         panic("ufs_direnter: compact1");
927                 dirp->d_reclen = spacefree + dsize;
928         } else {
929                 if (spacefree < newentrysize)
930                         panic("ufs_direnter: compact2");
931                 dirp->d_reclen = spacefree;
932                 ep->d_reclen = dsize;
933                 ep = (struct direct *)((char *)ep + dsize);
934         }
935 #ifdef UFS_DIRHASH
936         if (dp->i_dirhash != NULL && (ep->d_ino == 0 ||
937             dirp->d_reclen == spacefree))
938                 ufsdirhash_add(dp, dirp, dp->i_offset + ((char *)ep - dirbuf));
939 #endif
940         bcopy((caddr_t)dirp, (caddr_t)ep, (u_int)newentrysize);
941 #ifdef UFS_DIRHASH
942         if (dp->i_dirhash != NULL)
943                 ufsdirhash_checkblock(dp, dirbuf -
944                     (dp->i_offset & (DIRBLKSIZ - 1)),
945                     dp->i_offset & ~(DIRBLKSIZ - 1));
946 #endif
947
948         if (DOINGSOFTDEP(dvp)) {
949                 (void) softdep_setup_directory_add(bp, dp,
950                     dp->i_offset + (caddr_t)ep - dirbuf,
951                     dirp->d_ino, newdirbp, 0);
952                 bdwrite(bp);
953         } else {
954                 if (DOINGASYNC(dvp)) {
955                         bdwrite(bp);
956                         error = 0;
957                 } else {
958                         error = bwrite(bp);
959                 }
960         }
961         dp->i_flag |= IN_CHANGE | IN_UPDATE;
962         /*
963          * If all went well, and the directory can be shortened, proceed
964          * with the truncation. Note that we have to unlock the inode for
965          * the entry that we just entered, as the truncation may need to
966          * lock other inodes which can lead to deadlock if we also hold a
967          * lock on the newly entered node.
968          */
969         if (error == 0 && dp->i_endoff && dp->i_endoff < dp->i_size) {
970                 if (tvp != NULL)
971                         VOP_UNLOCK(tvp, 0, td);
972 #ifdef UFS_DIRHASH
973                 if (dp->i_dirhash != NULL)
974                         ufsdirhash_dirtrunc(dp, dp->i_endoff);
975 #endif
976                 (void) UFS_TRUNCATE(dvp, (off_t)dp->i_endoff,
977                     IO_NORMAL | IO_SYNC, cr, td);
978                 if (tvp != NULL)
979                         vn_lock(tvp, LK_EXCLUSIVE | LK_RETRY, td);
980         }
981         return (error);
982 }
983
984 /*
985  * Remove a directory entry after a call to namei, using
986  * the parameters which it left in nameidata. The entry
987  * dp->i_offset contains the offset into the directory of the
988  * entry to be eliminated.  The dp->i_count field contains the
989  * size of the previous record in the directory.  If this
990  * is 0, the first entry is being deleted, so we need only
991  * zero the inode number to mark the entry as free.  If the
992  * entry is not the first in the directory, we must reclaim
993  * the space of the now empty record by adding the record size
994  * to the size of the previous entry.
995  */
996 int
997 ufs_dirremove(dvp, ip, flags, isrmdir)
998         struct vnode *dvp;
999         struct inode *ip;
1000         int flags;
1001         int isrmdir;
1002 {
1003         struct inode *dp;
1004         struct direct *ep, *rep;
1005         struct buf *bp;
1006         int error;
1007
1008         dp = VTOI(dvp);
1009
1010         if (flags & DOWHITEOUT) {
1011                 /*
1012                  * Whiteout entry: set d_ino to WINO.
1013                  */
1014                 if ((error =
1015                     UFS_BLKATOFF(dvp, (off_t)dp->i_offset, (char **)&ep, &bp)) != 0)
1016                         return (error);
1017                 ep->d_ino = WINO;
1018                 ep->d_type = DT_WHT;
1019                 goto out;
1020         }
1021
1022         if ((error = UFS_BLKATOFF(dvp,
1023             (off_t)(dp->i_offset - dp->i_count), (char **)&ep, &bp)) != 0)
1024                 return (error);
1025
1026         /* Set 'rep' to the entry being removed. */
1027         if (dp->i_count == 0)
1028                 rep = ep;
1029         else
1030                 rep = (struct direct *)((char *)ep + ep->d_reclen);
1031 #ifdef UFS_DIRHASH
1032         /*
1033          * Remove the dirhash entry. This is complicated by the fact
1034          * that `ep' is the previous entry when dp->i_count != 0.
1035          */
1036         if (dp->i_dirhash != NULL)
1037                 ufsdirhash_remove(dp, rep, dp->i_offset);
1038 #endif
1039         if (dp->i_count == 0) {
1040                 /*
1041                  * First entry in block: set d_ino to zero.
1042                  */
1043                 ep->d_ino = 0;
1044         } else {
1045                 /*
1046                  * Collapse new free space into previous entry.
1047                  */
1048                 ep->d_reclen += rep->d_reclen;
1049         }
1050 #ifdef UFS_DIRHASH
1051         if (dp->i_dirhash != NULL)
1052                 ufsdirhash_checkblock(dp, (char *)ep -
1053                     ((dp->i_offset - dp->i_count) & (DIRBLKSIZ - 1)),
1054                     dp->i_offset & ~(DIRBLKSIZ - 1));
1055 #endif
1056 out:
1057         if (DOINGSOFTDEP(dvp)) {
1058                 if (ip) {
1059                         ip->i_effnlink--;
1060                         softdep_change_linkcnt(ip);
1061                         softdep_setup_remove(bp, dp, ip, isrmdir);
1062                 }
1063                 if (softdep_slowdown(dvp)) {
1064                         error = bwrite(bp);
1065                 } else {
1066                         bdwrite(bp);
1067                         error = 0;
1068                 }
1069         } else {
1070                 if (ip) {
1071                         ip->i_effnlink--;
1072                         ip->i_nlink--;
1073                         DIP_SET(ip, i_nlink, ip->i_nlink);
1074                         ip->i_flag |= IN_CHANGE;
1075                 }
1076                 if (flags & DOWHITEOUT)
1077                         error = bwrite(bp);
1078                 else if (DOINGASYNC(dvp) && dp->i_count != 0) {
1079                         bdwrite(bp);
1080                         error = 0;
1081                 } else
1082                         error = bwrite(bp);
1083         }
1084         dp->i_flag |= IN_CHANGE | IN_UPDATE;
1085         /*
1086          * If the last named reference to a snapshot goes away,
1087          * drop its snapshot reference so that it will be reclaimed
1088          * when last open reference goes away.
1089          */
1090 #if defined(FFS) || defined(IFS)
1091         if (ip != 0 && (ip->i_flags & SF_SNAPSHOT) != 0 && ip->i_effnlink == 0)
1092                 ffs_snapgone(ip);
1093 #endif
1094         return (error);
1095 }
1096
1097 /*
1098  * Rewrite an existing directory entry to point at the inode
1099  * supplied.  The parameters describing the directory entry are
1100  * set up by a call to namei.
1101  */
1102 int
1103 ufs_dirrewrite(dp, oip, newinum, newtype, isrmdir)
1104         struct inode *dp, *oip;
1105         ino_t newinum;
1106         int newtype;
1107         int isrmdir;
1108 {
1109         struct buf *bp;
1110         struct direct *ep;
1111         struct vnode *vdp = ITOV(dp);
1112         int error;
1113
1114         error = UFS_BLKATOFF(vdp, (off_t)dp->i_offset, (char **)&ep, &bp);
1115         if (error)
1116                 return (error);
1117         ep->d_ino = newinum;
1118         if (!OFSFMT(vdp))
1119                 ep->d_type = newtype;
1120         oip->i_effnlink--;
1121         if (DOINGSOFTDEP(vdp)) {
1122                 softdep_change_linkcnt(oip);
1123                 softdep_setup_directory_change(bp, dp, oip, newinum, isrmdir);
1124                 bdwrite(bp);
1125         } else {
1126                 oip->i_nlink--;
1127                 DIP_SET(oip, i_nlink, oip->i_nlink);
1128                 oip->i_flag |= IN_CHANGE;
1129                 if (DOINGASYNC(vdp)) {
1130                         bdwrite(bp);
1131                         error = 0;
1132                 } else {
1133                         error = bwrite(bp);
1134                 }
1135         }
1136         dp->i_flag |= IN_CHANGE | IN_UPDATE;
1137         /*
1138          * If the last named reference to a snapshot goes away,
1139          * drop its snapshot reference so that it will be reclaimed
1140          * when last open reference goes away.
1141          */
1142 #if defined(FFS) || defined(IFS)
1143         if ((oip->i_flags & SF_SNAPSHOT) != 0 && oip->i_effnlink == 0)
1144                 ffs_snapgone(oip);
1145 #endif
1146         return (error);
1147 }
1148
1149 /*
1150  * Check if a directory is empty or not.
1151  * Inode supplied must be locked.
1152  *
1153  * Using a struct dirtemplate here is not precisely
1154  * what we want, but better than using a struct direct.
1155  *
1156  * NB: does not handle corrupted directories.
1157  */
1158 int
1159 ufs_dirempty(ip, parentino, cred)
1160         struct inode *ip;
1161         ino_t parentino;
1162         struct ucred *cred;
1163 {
1164         doff_t off;
1165         struct dirtemplate dbuf;
1166         struct direct *dp = (struct direct *)&dbuf;
1167         int error, count, namlen;
1168 #define MINDIRSIZ (sizeof (struct dirtemplate) / 2)
1169
1170         for (off = 0; off < ip->i_size; off += dp->d_reclen) {
1171                 error = vn_rdwr(UIO_READ, ITOV(ip), (caddr_t)dp, MINDIRSIZ,
1172                     off, UIO_SYSSPACE, IO_NODELOCKED | IO_NOMACCHECK, cred,
1173                     NOCRED, &count, (struct thread *)0);
1174                 /*
1175                  * Since we read MINDIRSIZ, residual must
1176                  * be 0 unless we're at end of file.
1177                  */
1178                 if (error || count != 0)
1179                         return (0);
1180                 /* avoid infinite loops */
1181                 if (dp->d_reclen == 0)
1182                         return (0);
1183                 /* skip empty entries */
1184                 if (dp->d_ino == 0 || dp->d_ino == WINO)
1185                         continue;
1186                 /* accept only "." and ".." */
1187 #               if (BYTE_ORDER == LITTLE_ENDIAN)
1188                         if (OFSFMT(ITOV(ip)))
1189                                 namlen = dp->d_type;
1190                         else
1191                                 namlen = dp->d_namlen;
1192 #               else
1193                         namlen = dp->d_namlen;
1194 #               endif
1195                 if (namlen > 2)
1196                         return (0);
1197                 if (dp->d_name[0] != '.')
1198                         return (0);
1199                 /*
1200                  * At this point namlen must be 1 or 2.
1201                  * 1 implies ".", 2 implies ".." if second
1202                  * char is also "."
1203                  */
1204                 if (namlen == 1 && dp->d_ino == ip->i_number)
1205                         continue;
1206                 if (dp->d_name[1] == '.' && dp->d_ino == parentino)
1207                         continue;
1208                 return (0);
1209         }
1210         return (1);
1211 }
1212
1213 /*
1214  * Check if source directory is in the path of the target directory.
1215  * Target is supplied locked, source is unlocked.
1216  * The target is always vput before returning.
1217  */
1218 int
1219 ufs_checkpath(source, target, cred)
1220         struct inode *source, *target;
1221         struct ucred *cred;
1222 {
1223         struct vnode *vp;
1224         int error, namlen;
1225         ino_t rootino;
1226         struct dirtemplate dirbuf;
1227
1228         vp = ITOV(target);
1229         if (target->i_number == source->i_number) {
1230                 error = EEXIST;
1231                 goto out;
1232         }
1233         rootino = ROOTINO;
1234         error = 0;
1235         if (target->i_number == rootino)
1236                 goto out;
1237
1238         for (;;) {
1239                 if (vp->v_type != VDIR) {
1240                         error = ENOTDIR;
1241                         break;
1242                 }
1243                 error = vn_rdwr(UIO_READ, vp, (caddr_t)&dirbuf,
1244                         sizeof (struct dirtemplate), (off_t)0, UIO_SYSSPACE,
1245                         IO_NODELOCKED | IO_NOMACCHECK, cred, NOCRED, (int *)0,
1246                         (struct thread *)0);
1247                 if (error != 0)
1248                         break;
1249 #               if (BYTE_ORDER == LITTLE_ENDIAN)
1250                         if (OFSFMT(vp))
1251                                 namlen = dirbuf.dotdot_type;
1252                         else
1253                                 namlen = dirbuf.dotdot_namlen;
1254 #               else
1255                         namlen = dirbuf.dotdot_namlen;
1256 #               endif
1257                 if (namlen != 2 ||
1258                     dirbuf.dotdot_name[0] != '.' ||
1259                     dirbuf.dotdot_name[1] != '.') {
1260                         error = ENOTDIR;
1261                         break;
1262                 }
1263                 if (dirbuf.dotdot_ino == source->i_number) {
1264                         error = EINVAL;
1265                         break;
1266                 }
1267                 if (dirbuf.dotdot_ino == rootino)
1268                         break;
1269                 vput(vp);
1270                 error = VFS_VGET(vp->v_mount, dirbuf.dotdot_ino,
1271                     LK_EXCLUSIVE, &vp);
1272                 if (error) {
1273                         vp = NULL;
1274                         break;
1275                 }
1276         }
1277
1278 out:
1279         if (error == ENOTDIR)
1280                 printf("checkpath: .. not a directory\n");
1281         if (vp != NULL)
1282                 vput(vp);
1283         return (error);
1284 }