The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/ufs/ufs/ufs_lookup.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

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

Cache object: b695ec85c6d29eb9b5415a830cc9c700


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.