root/ufs/ufs/ufs_lookup.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. ufs_lookup
  2. ufs_dirbad
  3. ufs_dirbadentry
  4. ufs_makedirentry
  5. ufs_direnter
  6. ufs_dirremove
  7. ufs_dirrewrite
  8. ufs_dirempty
  9. ufs_checkpath

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

/* [<][>][^][v][top][bottom][index][help] */