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