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

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

Version: -  FREEBSD  -  FREEBSD8  -  FREEBSD7  -  FREEBSD72  -  FREEBSD71  -  FREEBSD70  -  FREEBSD6  -  FREEBSD64  -  FREEBSD63  -  FREEBSD62  -  FREEBSD61  -  FREEBSD60  -  FREEBSD5  -  FREEBSD55  -  FREEBSD54  -  FREEBSD53  -  FREEBSD52  -  FREEBSD51  -  FREEBSD50  -  FREEBSD4  -  FREEBSD3  -  FREEBSD22  -  linux-2.6  -  linux-2.4.22  -  MK83  -  MK84  -  PLAN9  -  DFBSD  -  NETBSD  -  NETBSD5  -  NETBSD4  -  NETBSD3  -  NETBSD20  -  OPENBSD  -  xnu-517  -  xnu-792  -  xnu-792.6.70  -  xnu-1228  -  xnu-1456.1.26  -  OPENSOLARIS  -  minix-3-1-1  -  FREEBSD-LIBC  -  FREEBSD7-LIBC  -  FREEBSD6-LIBC  -  GLIBC27 
SearchContext: -  none  -  3  -  10 

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

Cache object: cb253cadba9b9d37a2a132d281d1e8d2


[ 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.