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

Cache object: bbe25bbb02f284ace62e345c80035e1c


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