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/fs/ext2fs/ext2_lookup.c

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

    1 /*-
    2  *  modified for Lites 1.1
    3  *
    4  *  Aug 1995, Godmar Back (gback@cs.utah.edu)
    5  *  University of Utah, Department of Computer Science
    6  */
    7 /*-
    8  * Copyright (c) 1989, 1993
    9  *      The Regents of the University of California.  All rights reserved.
   10  * (c) UNIX System Laboratories, Inc.
   11  * All or some portions of this file are derived from material licensed
   12  * to the University of California by American Telephone and Telegraph
   13  * Co. or Unix System Laboratories, Inc. and are reproduced herein with
   14  * the permission of UNIX System Laboratories, Inc.
   15  *
   16  * Redistribution and use in source and binary forms, with or without
   17  * modification, are permitted provided that the following conditions
   18  * are met:
   19  * 1. Redistributions of source code must retain the above copyright
   20  *    notice, this list of conditions and the following disclaimer.
   21  * 2. Redistributions in binary form must reproduce the above copyright
   22  *    notice, this list of conditions and the following disclaimer in the
   23  *    documentation and/or other materials provided with the distribution.
   24  * 4. Neither the name of the University nor the names of its contributors
   25  *    may be used to endorse or promote products derived from this software
   26  *    without specific prior written permission.
   27  *
   28  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
   29  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   30  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   31  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
   32  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   33  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   34  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   35  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   36  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   37  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   38  * SUCH DAMAGE.
   39  *
   40  *      @(#)ufs_lookup.c        8.6 (Berkeley) 4/1/94
   41  * $FreeBSD$
   42  */
   43 
   44 #include <sys/param.h>
   45 #include <sys/systm.h>
   46 #include <sys/namei.h>
   47 #include <sys/bio.h>
   48 #include <sys/buf.h>
   49 #include <sys/endian.h>
   50 #include <sys/mount.h>
   51 #include <sys/vnode.h>
   52 #include <sys/malloc.h>
   53 #include <sys/dirent.h>
   54 #include <sys/sysctl.h>
   55 
   56 #include <ufs/ufs/dir.h>
   57 
   58 #include <fs/ext2fs/inode.h>
   59 #include <fs/ext2fs/ext2_mount.h>
   60 #include <fs/ext2fs/ext2fs.h>
   61 #include <fs/ext2fs/ext2_dinode.h>
   62 #include <fs/ext2fs/ext2_dir.h>
   63 #include <fs/ext2fs/ext2_extern.h>
   64 
   65 #ifdef INVARIANTS
   66 static int dirchk = 1;
   67 #else
   68 static int dirchk = 0;
   69 #endif
   70 
   71 static SYSCTL_NODE(_vfs, OID_AUTO, e2fs, CTLFLAG_RD, 0, "EXT2FS filesystem");
   72 SYSCTL_INT(_vfs_e2fs, OID_AUTO, dircheck, CTLFLAG_RW, &dirchk, 0, "");
   73 
   74 /*
   75    DIRBLKSIZE in ffs is DEV_BSIZE (in most cases 512)
   76    while it is the native blocksize in ext2fs - thus, a #define
   77    is no longer appropriate
   78 */
   79 #undef  DIRBLKSIZ
   80 
   81 static u_char ext2_ft_to_dt[] = {
   82         DT_UNKNOWN,             /* EXT2_FT_UNKNOWN */
   83         DT_REG,                 /* EXT2_FT_REG_FILE */
   84         DT_DIR,                 /* EXT2_FT_DIR */
   85         DT_CHR,                 /* EXT2_FT_CHRDEV */
   86         DT_BLK,                 /* EXT2_FT_BLKDEV */
   87         DT_FIFO,                /* EXT2_FT_FIFO */
   88         DT_SOCK,                /* EXT2_FT_SOCK */
   89         DT_LNK,                 /* EXT2_FT_SYMLINK */
   90 };
   91 #define FTTODT(ft) \
   92     ((ft) < nitems(ext2_ft_to_dt) ? ext2_ft_to_dt[(ft)] : DT_UNKNOWN)
   93 
   94 static u_char dt_to_ext2_ft[] = {
   95         EXT2_FT_UNKNOWN,        /* DT_UNKNOWN */
   96         EXT2_FT_FIFO,           /* DT_FIFO */
   97         EXT2_FT_CHRDEV,         /* DT_CHR */
   98         EXT2_FT_UNKNOWN,        /* unused */
   99         EXT2_FT_DIR,            /* DT_DIR */
  100         EXT2_FT_UNKNOWN,        /* unused */
  101         EXT2_FT_BLKDEV,         /* DT_BLK */
  102         EXT2_FT_UNKNOWN,        /* unused */
  103         EXT2_FT_REG_FILE,       /* DT_REG */
  104         EXT2_FT_UNKNOWN,        /* unused */
  105         EXT2_FT_SYMLINK,        /* DT_LNK */
  106         EXT2_FT_UNKNOWN,        /* unused */
  107         EXT2_FT_SOCK,           /* DT_SOCK */
  108         EXT2_FT_UNKNOWN,        /* unused */
  109         EXT2_FT_UNKNOWN,        /* DT_WHT */
  110 };
  111 #define DTTOFT(dt) \
  112     ((dt) < nitems(dt_to_ext2_ft) ? dt_to_ext2_ft[(dt)] : EXT2_FT_UNKNOWN)
  113 
  114 static int      ext2_dirbadentry(struct vnode *dp, struct ext2fs_direct_2 *de,
  115                     int entryoffsetinblock);
  116 static int      ext2_is_dot_entry(struct componentname *cnp);
  117 static int      ext2_lookup_ino(struct vnode *vdp, struct vnode **vpp,
  118                     struct componentname *cnp, ino_t *dd_ino);
  119 
  120 static int
  121 ext2_is_dot_entry(struct componentname *cnp)
  122 {
  123         if (cnp->cn_namelen <= 2 && cnp->cn_nameptr[0] == '.' &&
  124             (cnp->cn_nameptr[1] == '.' || cnp->cn_nameptr[1] == '\0'))
  125                 return (1);
  126         return (0);
  127 }
  128 
  129 /*
  130  * Vnode op for reading directories.
  131  */
  132 int
  133 ext2_readdir(struct vop_readdir_args *ap)
  134 {
  135         struct vnode *vp = ap->a_vp;
  136         struct uio *uio = ap->a_uio;
  137         struct buf *bp;
  138         struct inode *ip;
  139         struct ext2fs_direct_2 *dp, *edp;
  140         u_long *cookies;
  141         struct dirent dstdp;
  142         off_t offset, startoffset;
  143         size_t readcnt, skipcnt;
  144         ssize_t startresid;
  145         int ncookies;
  146         int DIRBLKSIZ = VTOI(ap->a_vp)->i_e2fs->e2fs_bsize;
  147         int error;
  148 
  149         if (uio->uio_offset < 0)
  150                 return (EINVAL);
  151         ip = VTOI(vp);
  152         if (ap->a_ncookies != NULL) {
  153                 if (uio->uio_resid < 0)
  154                         ncookies = 0;
  155                 else
  156                         ncookies = uio->uio_resid;
  157                 if (uio->uio_offset >= ip->i_size)
  158                         ncookies = 0;
  159                 else if (ip->i_size - uio->uio_offset < ncookies)
  160                         ncookies = ip->i_size - uio->uio_offset;
  161                 ncookies = ncookies / (offsetof(struct ext2fs_direct_2,
  162                     e2d_namlen) + 4) + 1;
  163                 cookies = malloc(ncookies * sizeof(*cookies), M_TEMP, M_WAITOK);
  164                 *ap->a_ncookies = ncookies;
  165                 *ap->a_cookies = cookies;
  166         } else {
  167                 ncookies = 0;
  168                 cookies = NULL;
  169         }
  170         offset = startoffset = uio->uio_offset;
  171         startresid = uio->uio_resid;
  172         error = 0;
  173         while (error == 0 && uio->uio_resid > 0 &&
  174             uio->uio_offset < ip->i_size) {
  175                 error = ext2_blkatoff(vp, uio->uio_offset, NULL, &bp);
  176                 if (error)
  177                         break;
  178                 if (bp->b_offset + bp->b_bcount > ip->i_size)
  179                         readcnt = ip->i_size - bp->b_offset;
  180                 else
  181                         readcnt = bp->b_bcount;
  182                 skipcnt = (size_t)(uio->uio_offset - bp->b_offset) &
  183                     ~(size_t)(DIRBLKSIZ - 1);
  184                 offset = bp->b_offset + skipcnt;
  185                 dp = (struct ext2fs_direct_2 *)&bp->b_data[skipcnt];
  186                 edp = (struct ext2fs_direct_2 *)&bp->b_data[readcnt];
  187                 while (error == 0 && uio->uio_resid > 0 && dp < edp) {
  188                         if (dp->e2d_reclen <= offsetof(struct ext2fs_direct_2,
  189                             e2d_namlen) || (caddr_t)dp + dp->e2d_reclen >
  190                             (caddr_t)edp) {
  191                                 error = EIO;
  192                                 break;
  193                         }
  194                         /*-
  195                          * "New" ext2fs directory entries differ in 3 ways
  196                          * from ufs on-disk ones:
  197                          * - the name is not necessarily NUL-terminated.
  198                          * - the file type field always exists and always
  199                          *   follows the name length field.
  200                          * - the file type is encoded in a different way.
  201                          *
  202                          * "Old" ext2fs directory entries need no special
  203                          * conversions, since they are binary compatible
  204                          * with "new" entries having a file type of 0 (i.e.,
  205                          * EXT2_FT_UNKNOWN).  Splitting the old name length
  206                          * field didn't make a mess like it did in ufs,
  207                          * because ext2fs uses a machine-independent disk
  208                          * layout.
  209                          */
  210                         dstdp.d_namlen = dp->e2d_namlen;
  211                         dstdp.d_type = FTTODT(dp->e2d_type);
  212                         if (offsetof(struct ext2fs_direct_2, e2d_namlen) +
  213                             dstdp.d_namlen > dp->e2d_reclen) {
  214                                 error = EIO;
  215                                 break;
  216                         }
  217                         if (offset < startoffset || dp->e2d_ino == 0)
  218                                 goto nextentry;
  219                         dstdp.d_fileno = dp->e2d_ino;
  220                         dstdp.d_reclen = GENERIC_DIRSIZ(&dstdp);
  221                         bcopy(dp->e2d_name, dstdp.d_name, dstdp.d_namlen);
  222                         dirent_terminate(&dstdp);
  223                         if (dstdp.d_reclen > uio->uio_resid) {
  224                                 if (uio->uio_resid == startresid)
  225                                         error = EINVAL;
  226                                 else
  227                                         error = EJUSTRETURN;
  228                                 break;
  229                         }
  230                         /* Advance dp. */
  231                         error = uiomove((caddr_t)&dstdp, dstdp.d_reclen, uio);
  232                         if (error)
  233                                 break;
  234                         if (cookies != NULL) {
  235                                 KASSERT(ncookies > 0,
  236                                     ("ext2_readdir: cookies buffer too small"));
  237                                 *cookies = offset + dp->e2d_reclen;
  238                                 cookies++;
  239                                 ncookies--;
  240                         }
  241 nextentry:
  242                         offset += dp->e2d_reclen;
  243                         dp = (struct ext2fs_direct_2 *)((caddr_t)dp +
  244                             dp->e2d_reclen);
  245                 }
  246                 bqrelse(bp);
  247                 uio->uio_offset = offset;
  248         }
  249         /* We need to correct uio_offset. */
  250         uio->uio_offset = offset;
  251         if (error == EJUSTRETURN)
  252                 error = 0;
  253         if (ap->a_ncookies != NULL) {
  254                 if (error == 0) {
  255                         ap->a_ncookies -= ncookies;
  256                 } else {
  257                         free(*ap->a_cookies, M_TEMP);
  258                         *ap->a_ncookies = 0;
  259                         *ap->a_cookies = NULL;
  260                 }
  261         }
  262         if (error == 0 && ap->a_eofflag)
  263                 *ap->a_eofflag = ip->i_size <= uio->uio_offset;
  264         return (error);
  265 }
  266 
  267 /*
  268  * Convert a component of a pathname into a pointer to a locked inode.
  269  * This is a very central and rather complicated routine.
  270  * If the file system is not maintained in a strict tree hierarchy,
  271  * this can result in a deadlock situation (see comments in code below).
  272  *
  273  * The cnp->cn_nameiop argument is LOOKUP, CREATE, RENAME, or DELETE depending
  274  * on whether the name is to be looked up, created, renamed, or deleted.
  275  * When CREATE, RENAME, or DELETE is specified, information usable in
  276  * creating, renaming, or deleting a directory entry may be calculated.
  277  * If flag has LOCKPARENT or'ed into it and the target of the pathname
  278  * exists, lookup returns both the target and its parent directory locked.
  279  * When creating or renaming and LOCKPARENT is specified, the target may
  280  * not be ".".  When deleting and LOCKPARENT is specified, the target may
  281  * be "."., but the caller must check to ensure it does an vrele and vput
  282  * instead of two vputs.
  283  *
  284  * Overall outline of ext2_lookup:
  285  *
  286  *      search for name in directory, to found or notfound
  287  * notfound:
  288  *      if creating, return locked directory, leaving info on available slots
  289  *      else return error
  290  * found:
  291  *      if at end of path and deleting, return information to allow delete
  292  *      if at end of path and rewriting (RENAME and LOCKPARENT), lock target
  293  *        inode and return info to allow rewrite
  294  *      if not at end, add name to cache; if at end and neither creating
  295  *        nor deleting, add name to cache
  296  */
  297 int
  298 ext2_lookup(struct vop_cachedlookup_args *ap)
  299 {
  300 
  301         return (ext2_lookup_ino(ap->a_dvp, ap->a_vpp, ap->a_cnp, NULL));
  302 }
  303 
  304 static int
  305 ext2_lookup_ino(struct vnode *vdp, struct vnode **vpp, struct componentname *cnp,
  306     ino_t *dd_ino)
  307 {
  308         struct inode *dp;               /* inode for directory being searched */
  309         struct buf *bp;                 /* a buffer of directory entries */
  310         struct ext2fs_direct_2 *ep;     /* the current directory entry */
  311         int entryoffsetinblock;         /* offset of ep in bp's buffer */
  312         struct ext2fs_searchslot ss;
  313         doff_t i_diroff;                /* cached i_diroff value */
  314         doff_t i_offset;                /* cached i_offset value */
  315         int numdirpasses;               /* strategy for directory search */
  316         doff_t endsearch;               /* offset to end directory search */
  317         doff_t prevoff;                 /* prev entry dp->i_offset */
  318         struct vnode *pdp;              /* saved dp during symlink work */
  319         struct vnode *tdp;              /* returned by VFS_VGET */
  320         doff_t enduseful;               /* pointer past last used dir slot */
  321         u_long bmask;                   /* block offset mask */
  322         int error;
  323         struct ucred *cred = cnp->cn_cred;
  324         int flags = cnp->cn_flags;
  325         int nameiop = cnp->cn_nameiop;
  326         ino_t ino, ino1;
  327         int ltype;
  328         int entry_found = 0;
  329 
  330         int DIRBLKSIZ = VTOI(vdp)->i_e2fs->e2fs_bsize;
  331 
  332         if (vpp != NULL)
  333                 *vpp = NULL;
  334 
  335         dp = VTOI(vdp);
  336         bmask = VFSTOEXT2(vdp->v_mount)->um_mountp->mnt_stat.f_iosize - 1;
  337 restart:
  338         bp = NULL;
  339         ss.slotoffset = -1;
  340 
  341         /*
  342          * We now have a segment name to search for, and a directory to search.
  343          *
  344          * Suppress search for slots unless creating
  345          * file and at end of pathname, in which case
  346          * we watch for a place to put the new file in
  347          * case it doesn't already exist.
  348          */
  349         i_diroff = dp->i_diroff;
  350         ss.slotstatus = FOUND;
  351         ss.slotfreespace = ss.slotsize = ss.slotneeded = 0;
  352         if ((nameiop == CREATE || nameiop == RENAME) &&
  353             (flags & ISLASTCN)) {
  354                 ss.slotstatus = NONE;
  355                 ss.slotneeded = EXT2_DIR_REC_LEN(cnp->cn_namelen);
  356                 /*
  357                  * was ss.slotneeded = (sizeof(struct direct) - MAXNAMLEN +
  358                  * cnp->cn_namelen + 3) &~ 3;
  359                  */
  360         }
  361         /*
  362          * Try to lookup dir entry using htree directory index.
  363          *
  364          * If we got an error or we want to find '.' or '..' entry,
  365          * we will fall back to linear search.
  366          */
  367         if (!ext2_is_dot_entry(cnp) && ext2_htree_has_idx(dp)) {
  368                 numdirpasses = 1;
  369                 entryoffsetinblock = 0;
  370                 switch (ext2_htree_lookup(dp, cnp->cn_nameptr, cnp->cn_namelen,
  371                     &bp, &entryoffsetinblock, &i_offset, &prevoff,
  372                     &enduseful, &ss)) {
  373                 case 0:
  374                         ep = (struct ext2fs_direct_2 *)((char *)bp->b_data +
  375                             (i_offset & bmask));
  376                         goto foundentry;
  377                 case ENOENT:
  378                         i_offset = roundup2(dp->i_size, DIRBLKSIZ);
  379                         goto notfound;
  380                 default:
  381                         /*
  382                          * Something failed; just fallback to do a linear
  383                          * search.
  384                          */
  385                         break;
  386                 }
  387         }
  388 
  389         /*
  390          * If there is cached information on a previous search of
  391          * this directory, pick up where we last left off.
  392          * We cache only lookups as these are the most common
  393          * and have the greatest payoff. Caching CREATE has little
  394          * benefit as it usually must search the entire directory
  395          * to determine that the entry does not exist. Caching the
  396          * location of the last DELETE or RENAME has not reduced
  397          * profiling time and hence has been removed in the interest
  398          * of simplicity.
  399          */
  400         if (nameiop != LOOKUP || i_diroff == 0 ||
  401             i_diroff > dp->i_size) {
  402                 entryoffsetinblock = 0;
  403                 i_offset = 0;
  404                 numdirpasses = 1;
  405         } else {
  406                 i_offset = i_diroff;
  407                 if ((entryoffsetinblock = i_offset & bmask) &&
  408                     (error = ext2_blkatoff(vdp, (off_t)i_offset, NULL,
  409                     &bp)))
  410                         return (error);
  411                 numdirpasses = 2;
  412                 nchstats.ncs_2passes++;
  413         }
  414         prevoff = i_offset;
  415         endsearch = roundup2(dp->i_size, DIRBLKSIZ);
  416         enduseful = 0;
  417 
  418 searchloop:
  419         while (i_offset < endsearch) {
  420                 /*
  421                  * If necessary, get the next directory block.
  422                  */
  423                 if (bp != NULL)
  424                         brelse(bp);
  425                 error = ext2_blkatoff(vdp, (off_t)i_offset, NULL, &bp);
  426                 if (error != 0)
  427                         return (error);
  428                 entryoffsetinblock = 0;
  429                 /*
  430                  * If still looking for a slot, and at a DIRBLKSIZE
  431                  * boundary, have to start looking for free space again.
  432                  */
  433                 if (ss.slotstatus == NONE &&
  434                     (entryoffsetinblock & (DIRBLKSIZ - 1)) == 0) {
  435                         ss.slotoffset = -1;
  436                         ss.slotfreespace = 0;
  437                 }
  438                 error = ext2_search_dirblock(dp, bp->b_data, &entry_found,
  439                     cnp->cn_nameptr, cnp->cn_namelen,
  440                     &entryoffsetinblock, &i_offset, &prevoff,
  441                     &enduseful, &ss);
  442                 if (error != 0) {
  443                         brelse(bp);
  444                         return (error);
  445                 }
  446                 if (entry_found) {
  447                         ep = (struct ext2fs_direct_2 *)((char *)bp->b_data +
  448                             (entryoffsetinblock & bmask));
  449 foundentry:
  450                         ino = ep->e2d_ino;
  451                         goto found;
  452                 }
  453         }
  454 notfound:
  455         /*
  456          * If we started in the middle of the directory and failed
  457          * to find our target, we must check the beginning as well.
  458          */
  459         if (numdirpasses == 2) {
  460                 numdirpasses--;
  461                 i_offset = 0;
  462                 endsearch = i_diroff;
  463                 goto searchloop;
  464         }
  465         if (bp != NULL)
  466                 brelse(bp);
  467         /*
  468          * If creating, and at end of pathname and current
  469          * directory has not been removed, then can consider
  470          * allowing file to be created.
  471          */
  472         if ((nameiop == CREATE || nameiop == RENAME) &&
  473             (flags & ISLASTCN) && dp->i_nlink != 0) {
  474                 /*
  475                  * Access for write is interpreted as allowing
  476                  * creation of files in the directory.
  477                  */
  478                 if ((error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_thread)) != 0)
  479                         return (error);
  480                 /*
  481                  * Return an indication of where the new directory
  482                  * entry should be put.  If we didn't find a slot,
  483                  * then set dp->i_count to 0 indicating
  484                  * that the new slot belongs at the end of the
  485                  * directory. If we found a slot, then the new entry
  486                  * can be put in the range from dp->i_offset to
  487                  * dp->i_offset + dp->i_count.
  488                  */
  489                 if (ss.slotstatus == NONE) {
  490                         dp->i_offset = roundup2(dp->i_size, DIRBLKSIZ);
  491                         dp->i_count = 0;
  492                         enduseful = dp->i_offset;
  493                 } else {
  494                         dp->i_offset = ss.slotoffset;
  495                         dp->i_count = ss.slotsize;
  496                         if (enduseful < ss.slotoffset + ss.slotsize)
  497                                 enduseful = ss.slotoffset + ss.slotsize;
  498                 }
  499                 dp->i_endoff = roundup2(enduseful, DIRBLKSIZ);
  500                 /*
  501                  * We return with the directory locked, so that
  502                  * the parameters we set up above will still be
  503                  * valid if we actually decide to do a direnter().
  504                  * We return ni_vp == NULL to indicate that the entry
  505                  * does not currently exist; we leave a pointer to
  506                  * the (locked) directory inode in ndp->ni_dvp.
  507                  * The pathname buffer is saved so that the name
  508                  * can be obtained later.
  509                  *
  510                  * NB - if the directory is unlocked, then this
  511                  * information cannot be used.
  512                  */
  513                 cnp->cn_flags |= SAVENAME;
  514                 return (EJUSTRETURN);
  515         }
  516         /*
  517          * Insert name into cache (as non-existent) if appropriate.
  518          */
  519         if ((cnp->cn_flags & MAKEENTRY) != 0)
  520                 cache_enter(vdp, NULL, cnp);
  521         return (ENOENT);
  522 
  523 found:
  524         if (dd_ino != NULL)
  525                 *dd_ino = ino;
  526         if (numdirpasses == 2)
  527                 nchstats.ncs_pass2++;
  528         /*
  529          * Check that directory length properly reflects presence
  530          * of this entry.
  531          */
  532         if (entryoffsetinblock + EXT2_DIR_REC_LEN(ep->e2d_namlen)
  533             > dp->i_size) {
  534                 ext2_dirbad(dp, i_offset, "i_size too small");
  535                 dp->i_size = entryoffsetinblock + EXT2_DIR_REC_LEN(ep->e2d_namlen);
  536                 dp->i_flag |= IN_CHANGE | IN_UPDATE;
  537         }
  538         brelse(bp);
  539 
  540         /*
  541          * Found component in pathname.
  542          * If the final component of path name, save information
  543          * in the cache as to where the entry was found.
  544          */
  545         if ((flags & ISLASTCN) && nameiop == LOOKUP)
  546                 dp->i_diroff = rounddown2(i_offset, DIRBLKSIZ);
  547         /*
  548          * If deleting, and at end of pathname, return
  549          * parameters which can be used to remove file.
  550          */
  551         if (nameiop == DELETE && (flags & ISLASTCN)) {
  552                 if (flags & LOCKPARENT)
  553                         ASSERT_VOP_ELOCKED(vdp, __FUNCTION__);
  554                 /*
  555                  * Write access to directory required to delete files.
  556                  */
  557                 if ((error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_thread)) != 0)
  558                         return (error);
  559                 /*
  560                  * Return pointer to current entry in dp->i_offset,
  561                  * and distance past previous entry (if there
  562                  * is a previous entry in this block) in dp->i_count.
  563                  * Save directory inode pointer in ndp->ni_dvp for dirremove().
  564                  *
  565                  * Technically we shouldn't be setting these in the
  566                  * WANTPARENT case (first lookup in rename()), but any
  567                  * lookups that will result in directory changes will
  568                  * overwrite these.
  569                  */
  570                 dp->i_offset = i_offset;
  571                 if ((dp->i_offset & (DIRBLKSIZ - 1)) == 0)
  572                         dp->i_count = 0;
  573                 else
  574                         dp->i_count = dp->i_offset - prevoff;
  575                 if (dd_ino != NULL)
  576                         return (0);
  577                 if (dp->i_number == ino) {
  578                         VREF(vdp);
  579                         *vpp = vdp;
  580                         return (0);
  581                 }
  582                 if ((error = VFS_VGET(vdp->v_mount, ino, LK_EXCLUSIVE,
  583                     &tdp)) != 0)
  584                         return (error);
  585                 /*
  586                  * If directory is "sticky", then user must own
  587                  * the directory, or the file in it, else she
  588                  * may not delete it (unless she's root). This
  589                  * implements append-only directories.
  590                  */
  591                 if ((dp->i_mode & ISVTX) &&
  592                     cred->cr_uid != 0 &&
  593                     cred->cr_uid != dp->i_uid &&
  594                     VTOI(tdp)->i_uid != cred->cr_uid) {
  595                         vput(tdp);
  596                         return (EPERM);
  597                 }
  598                 *vpp = tdp;
  599                 return (0);
  600         }
  601 
  602         /*
  603          * If rewriting (RENAME), return the inode and the
  604          * information required to rewrite the present directory
  605          * Must get inode of directory entry to verify it's a
  606          * regular file, or empty directory.
  607          */
  608         if (nameiop == RENAME && (flags & ISLASTCN)) {
  609                 if ((error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_thread)) != 0)
  610                         return (error);
  611                 /*
  612                  * Careful about locking second inode.
  613                  * This can only occur if the target is ".".
  614                  */
  615                 dp->i_offset = i_offset;
  616                 if (dp->i_number == ino)
  617                         return (EISDIR);
  618                 if (dd_ino != NULL)
  619                         return (0);
  620                 if ((error = VFS_VGET(vdp->v_mount, ino, LK_EXCLUSIVE,
  621                     &tdp)) != 0)
  622                         return (error);
  623                 *vpp = tdp;
  624                 cnp->cn_flags |= SAVENAME;
  625                 return (0);
  626         }
  627         if (dd_ino != NULL)
  628                 return (0);
  629 
  630         /*
  631          * Step through the translation in the name.  We do not `vput' the
  632          * directory because we may need it again if a symbolic link
  633          * is relative to the current directory.  Instead we save it
  634          * unlocked as "pdp".  We must get the target inode before unlocking
  635          * the directory to insure that the inode will not be removed
  636          * before we get it.  We prevent deadlock by always fetching
  637          * inodes from the root, moving down the directory tree. Thus
  638          * when following backward pointers ".." we must unlock the
  639          * parent directory before getting the requested directory.
  640          * There is a potential race condition here if both the current
  641          * and parent directories are removed before the VFS_VGET for the
  642          * inode associated with ".." returns.  We hope that this occurs
  643          * infrequently since we cannot avoid this race condition without
  644          * implementing a sophisticated deadlock detection algorithm.
  645          * Note also that this simple deadlock detection scheme will not
  646          * work if the file system has any hard links other than ".."
  647          * that point backwards in the directory structure.
  648          */
  649         pdp = vdp;
  650         if (flags & ISDOTDOT) {
  651                 error = vn_vget_ino(pdp, ino, cnp->cn_lkflags, &tdp);
  652                 if (pdp->v_iflag & VI_DOOMED) {
  653                         if (error == 0)
  654                                 vput(tdp);
  655                         error = ENOENT;
  656                 }
  657                 if (error)
  658                         return (error);
  659                 /*
  660                  * Recheck that ".." entry in the vdp directory points
  661                  * to the inode we looked up before vdp lock was
  662                  * dropped.
  663                  */
  664                 error = ext2_lookup_ino(pdp, NULL, cnp, &ino1);
  665                 if (error) {
  666                         vput(tdp);
  667                         return (error);
  668                 }
  669                 if (ino1 != ino) {
  670                         vput(tdp);
  671                         goto restart;
  672                 }
  673                 *vpp = tdp;
  674         } else if (dp->i_number == ino) {
  675                 VREF(vdp);      /* we want ourself, ie "." */
  676                 /*
  677                  * When we lookup "." we still can be asked to lock it
  678                  * differently.
  679                  */
  680                 ltype = cnp->cn_lkflags & LK_TYPE_MASK;
  681                 if (ltype != VOP_ISLOCKED(vdp)) {
  682                         if (ltype == LK_EXCLUSIVE)
  683                                 vn_lock(vdp, LK_UPGRADE | LK_RETRY);
  684                         else    /* if (ltype == LK_SHARED) */
  685                                 vn_lock(vdp, LK_DOWNGRADE | LK_RETRY);
  686                 }
  687                 *vpp = vdp;
  688         } else {
  689                 if ((error = VFS_VGET(vdp->v_mount, ino, cnp->cn_lkflags,
  690                     &tdp)) != 0)
  691                         return (error);
  692                 *vpp = tdp;
  693         }
  694 
  695         /*
  696          * Insert name into cache if appropriate.
  697          */
  698         if (cnp->cn_flags & MAKEENTRY)
  699                 cache_enter(vdp, *vpp, cnp);
  700         return (0);
  701 }
  702 
  703 int
  704 ext2_search_dirblock(struct inode *ip, void *data, int *foundp,
  705     const char *name, int namelen, int *entryoffsetinblockp,
  706     doff_t *offp, doff_t *prevoffp, doff_t *endusefulp,
  707     struct ext2fs_searchslot *ssp)
  708 {
  709         struct vnode *vdp;
  710         struct ext2fs_direct_2 *ep, *top;
  711         uint32_t bsize = ip->i_e2fs->e2fs_bsize;
  712         int offset = *entryoffsetinblockp;
  713         int namlen;
  714 
  715         vdp = ITOV(ip);
  716 
  717         ep = (struct ext2fs_direct_2 *)((char *)data + offset);
  718         top = (struct ext2fs_direct_2 *)((char *)data +
  719             bsize - EXT2_DIR_REC_LEN(0));
  720 
  721         while (ep < top) {
  722                 /*
  723                  * Full validation checks are slow, so we only check
  724                  * enough to insure forward progress through the
  725                  * directory. Complete checks can be run by setting
  726                  * "vfs.e2fs.dirchk" to be true.
  727                  */
  728                 if (ep->e2d_reclen == 0 ||
  729                     (dirchk && ext2_dirbadentry(vdp, ep, offset))) {
  730                         int i;
  731 
  732                         ext2_dirbad(ip, *offp, "mangled entry");
  733                         i = bsize - (offset & (bsize - 1));
  734                         *offp += i;
  735                         offset += i;
  736                         continue;
  737                 }
  738 
  739                 /*
  740                  * If an appropriate sized slot has not yet been found,
  741                  * check to see if one is available. Also accumulate space
  742                  * in the current block so that we can determine if
  743                  * compaction is viable.
  744                  */
  745                 if (ssp->slotstatus != FOUND) {
  746                         int size = ep->e2d_reclen;
  747 
  748                         if (ep->e2d_ino != 0)
  749                                 size -= EXT2_DIR_REC_LEN(ep->e2d_namlen);
  750                         if (size > 0) {
  751                                 if (size >= ssp->slotneeded) {
  752                                         ssp->slotstatus = FOUND;
  753                                         ssp->slotoffset = *offp;
  754                                         ssp->slotsize = ep->e2d_reclen;
  755                                 } else if (ssp->slotstatus == NONE) {
  756                                         ssp->slotfreespace += size;
  757                                         if (ssp->slotoffset == -1)
  758                                                 ssp->slotoffset = *offp;
  759                                         if (ssp->slotfreespace >= ssp->slotneeded) {
  760                                                 ssp->slotstatus = COMPACT;
  761                                                 ssp->slotsize = *offp +
  762                                                     ep->e2d_reclen -
  763                                                     ssp->slotoffset;
  764                                         }
  765                                 }
  766                         }
  767                 }
  768                 /*
  769                  * Check for a name match.
  770                  */
  771                 if (ep->e2d_ino) {
  772                         namlen = ep->e2d_namlen;
  773                         if (namlen == namelen &&
  774                             !bcmp(name, ep->e2d_name, (unsigned)namlen)) {
  775                                 /*
  776                                  * Save directory entry's inode number and
  777                                  * reclen in ndp->ni_ufs area, and release
  778                                  * directory buffer.
  779                                  */
  780                                 *foundp = 1;
  781                                 return (0);
  782                         }
  783                 }
  784                 *prevoffp = *offp;
  785                 *offp += ep->e2d_reclen;
  786                 offset += ep->e2d_reclen;
  787                 *entryoffsetinblockp = offset;
  788                 if (ep->e2d_ino)
  789                         *endusefulp = *offp;
  790                 /*
  791                  * Get pointer to the next entry.
  792                  */
  793                 ep = (struct ext2fs_direct_2 *)((char *)data + offset);
  794         }
  795 
  796         return (0);
  797 }
  798 
  799 void
  800 ext2_dirbad(struct inode *ip, doff_t offset, char *how)
  801 {
  802         struct mount *mp;
  803 
  804         mp = ITOV(ip)->v_mount;
  805         if ((mp->mnt_flag & MNT_RDONLY) == 0)
  806                 panic("ext2_dirbad: %s: bad dir ino %ju at offset %ld: %s\n",
  807                     mp->mnt_stat.f_mntonname, (uintmax_t)ip->i_number,
  808                     (long)offset, how);
  809         else
  810                 (void)printf("%s: bad dir ino %ju at offset %ld: %s\n",
  811                     mp->mnt_stat.f_mntonname, (uintmax_t)ip->i_number,
  812                     (long)offset, how);
  813 
  814 }
  815 
  816 /*
  817  * Do consistency checking on a directory entry:
  818  *      record length must be multiple of 4
  819  *      entry must fit in rest of its DIRBLKSIZ block
  820  *      record must be large enough to contain entry
  821  *      name is not longer than MAXNAMLEN
  822  *      name must be as long as advertised, and null terminated
  823  */
  824 /*
  825  *      changed so that it confirms to ext2_check_dir_entry
  826  */
  827 static int
  828 ext2_dirbadentry(struct vnode *dp, struct ext2fs_direct_2 *de,
  829     int entryoffsetinblock)
  830 {
  831         int DIRBLKSIZ = VTOI(dp)->i_e2fs->e2fs_bsize;
  832 
  833         char *error_msg = NULL;
  834 
  835         if (de->e2d_reclen < EXT2_DIR_REC_LEN(1))
  836                 error_msg = "rec_len is smaller than minimal";
  837         else if (de->e2d_reclen % 4 != 0)
  838                 error_msg = "rec_len % 4 != 0";
  839         else if (de->e2d_reclen < EXT2_DIR_REC_LEN(de->e2d_namlen))
  840                 error_msg = "reclen is too small for name_len";
  841         else if (entryoffsetinblock + de->e2d_reclen > DIRBLKSIZ)
  842                 error_msg = "directory entry across blocks";
  843         /* else LATER
  844              if (de->inode > dir->i_sb->u.ext2_sb.s_es->s_inodes_count)
  845                 error_msg = "inode out of bounds";
  846         */
  847 
  848         if (error_msg != NULL) {
  849                 printf("bad directory entry: %s\n", error_msg);
  850                 printf("offset=%d, inode=%lu, rec_len=%u, name_len=%u\n",
  851                         entryoffsetinblock, (unsigned long)de->e2d_ino,
  852                         de->e2d_reclen, de->e2d_namlen);
  853         }
  854         return error_msg == NULL ? 0 : 1;
  855 }
  856 
  857 /*
  858  * Write a directory entry after a call to namei, using the parameters
  859  * that it left in nameidata.  The argument ip is the inode which the new
  860  * directory entry will refer to.  Dvp is a pointer to the directory to
  861  * be written, which was left locked by namei. Remaining parameters
  862  * (dp->i_offset, dp->i_count) indicate how the space for the new
  863  * entry is to be obtained.
  864  */
  865 int
  866 ext2_direnter(struct inode *ip, struct vnode *dvp, struct componentname *cnp)
  867 {
  868         struct inode *dp;
  869         struct ext2fs_direct_2 newdir;
  870         struct iovec aiov;
  871         struct uio auio;
  872         int error, newentrysize;
  873         int DIRBLKSIZ = ip->i_e2fs->e2fs_bsize;
  874 
  875 
  876 #ifdef INVARIANTS
  877         if ((cnp->cn_flags & SAVENAME) == 0)
  878                 panic("ext2_direnter: missing name");
  879 #endif
  880         dp = VTOI(dvp);
  881         newdir.e2d_ino = ip->i_number;
  882         newdir.e2d_namlen = cnp->cn_namelen;
  883         if (EXT2_HAS_INCOMPAT_FEATURE(ip->i_e2fs,
  884             EXT2F_INCOMPAT_FTYPE))
  885                 newdir.e2d_type = DTTOFT(IFTODT(ip->i_mode));
  886         else
  887                 newdir.e2d_type = EXT2_FT_UNKNOWN;
  888         bcopy(cnp->cn_nameptr, newdir.e2d_name, (unsigned)cnp->cn_namelen + 1);
  889         newentrysize = EXT2_DIR_REC_LEN(newdir.e2d_namlen);
  890 
  891         if (ext2_htree_has_idx(dp)) {
  892                 error = ext2_htree_add_entry(dvp, &newdir, cnp);
  893                 if (error) {
  894                         dp->i_flag &= ~IN_E3INDEX;
  895                         dp->i_flag |= IN_CHANGE | IN_UPDATE;
  896                 }
  897                 return (error);
  898         }
  899 
  900         if (EXT2_HAS_COMPAT_FEATURE(ip->i_e2fs, EXT2F_COMPAT_DIRHASHINDEX) &&
  901             !ext2_htree_has_idx(dp)) {
  902                 if ((dp->i_size / DIRBLKSIZ) == 1 &&
  903                     dp->i_offset == DIRBLKSIZ) {
  904                         /*
  905                          * Making indexed directory when one block is not
  906                          * enough to save all entries.
  907                          */
  908                         return ext2_htree_create_index(dvp, cnp, &newdir);
  909                 }
  910         }
  911 
  912         if (dp->i_count == 0) {
  913                 /*
  914                  * If dp->i_count is 0, then namei could find no
  915                  * space in the directory. Here, dp->i_offset will
  916                  * be on a directory block boundary and we will write the
  917                  * new entry into a fresh block.
  918                  */
  919                 if (dp->i_offset & (DIRBLKSIZ - 1))
  920                         panic("ext2_direnter: newblk");
  921                 auio.uio_offset = dp->i_offset;
  922                 newdir.e2d_reclen = DIRBLKSIZ;
  923                 auio.uio_resid = newentrysize;
  924                 aiov.iov_len = newentrysize;
  925                 aiov.iov_base = (caddr_t)&newdir;
  926                 auio.uio_iov = &aiov;
  927                 auio.uio_iovcnt = 1;
  928                 auio.uio_rw = UIO_WRITE;
  929                 auio.uio_segflg = UIO_SYSSPACE;
  930                 auio.uio_td = (struct thread *)0;
  931                 error = VOP_WRITE(dvp, &auio, IO_SYNC, cnp->cn_cred);
  932                 if (DIRBLKSIZ >
  933                     VFSTOEXT2(dvp->v_mount)->um_mountp->mnt_stat.f_bsize)
  934                         /* XXX should grow with balloc() */
  935                         panic("ext2_direnter: frag size");
  936                 else if (!error) {
  937                         dp->i_size = roundup2(dp->i_size, DIRBLKSIZ);
  938                         dp->i_flag |= IN_CHANGE;
  939                 }
  940                 return (error);
  941         }
  942 
  943         error = ext2_add_entry(dvp, &newdir);
  944         if (!error && dp->i_endoff && dp->i_endoff < dp->i_size)
  945                 error = ext2_truncate(dvp, (off_t)dp->i_endoff, IO_SYNC,
  946                     cnp->cn_cred, cnp->cn_thread);
  947         return (error);
  948 }
  949 
  950 /*
  951  * Insert an entry into the directory block.
  952  * Compact the contents.
  953  */
  954 int
  955 ext2_add_entry(struct vnode *dvp, struct ext2fs_direct_2 *entry)
  956 {
  957         struct ext2fs_direct_2 *ep, *nep;
  958         struct inode *dp;
  959         struct buf *bp;
  960         u_int dsize;
  961         int error, loc, newentrysize, spacefree;
  962         char *dirbuf;
  963 
  964         dp = VTOI(dvp);
  965 
  966         /*
  967          * If dp->i_count is non-zero, then namei found space
  968          * for the new entry in the range dp->i_offset to
  969          * dp->i_offset + dp->i_count in the directory.
  970          * To use this space, we may have to compact the entries located
  971          * there, by copying them together towards the beginning of the
  972          * block, leaving the free space in one usable chunk at the end.
  973          */
  974 
  975         /*
  976          * Increase size of directory if entry eats into new space.
  977          * This should never push the size past a new multiple of
  978          * DIRBLKSIZE.
  979          *
  980          * N.B. - THIS IS AN ARTIFACT OF 4.2 AND SHOULD NEVER HAPPEN.
  981          */
  982         if (dp->i_offset + dp->i_count > dp->i_size)
  983                 dp->i_size = dp->i_offset + dp->i_count;
  984         /*
  985          * Get the block containing the space for the new directory entry.
  986          */
  987         if ((error = ext2_blkatoff(dvp, (off_t)dp->i_offset, &dirbuf,
  988             &bp)) != 0)
  989                 return (error);
  990         /*
  991          * Find space for the new entry. In the simple case, the entry at
  992          * offset base will have the space. If it does not, then namei
  993          * arranged that compacting the region dp->i_offset to
  994          * dp->i_offset + dp->i_count would yield the
  995          * space.
  996          */
  997         newentrysize = EXT2_DIR_REC_LEN(entry->e2d_namlen);
  998         ep = (struct ext2fs_direct_2 *)dirbuf;
  999         dsize = EXT2_DIR_REC_LEN(ep->e2d_namlen);
 1000         spacefree = ep->e2d_reclen - dsize;
 1001         for (loc = ep->e2d_reclen; loc < dp->i_count; ) {
 1002                 nep = (struct ext2fs_direct_2 *)(dirbuf + loc);
 1003                 if (ep->e2d_ino) {
 1004                         /* trim the existing slot */
 1005                         ep->e2d_reclen = dsize;
 1006                         ep = (struct ext2fs_direct_2 *)((char *)ep + dsize);
 1007                 } else {
 1008                         /* overwrite; nothing there; header is ours */
 1009                         spacefree += dsize;
 1010                 }
 1011                 dsize = EXT2_DIR_REC_LEN(nep->e2d_namlen);
 1012                 spacefree += nep->e2d_reclen - dsize;
 1013                 loc += nep->e2d_reclen;
 1014                 bcopy((caddr_t)nep, (caddr_t)ep, dsize);
 1015         }
 1016         /*
 1017          * Update the pointer fields in the previous entry (if any),
 1018          * copy in the new entry, and write out the block.
 1019          */
 1020         if (ep->e2d_ino == 0) {
 1021                 if (spacefree + dsize < newentrysize)
 1022                         panic("ext2_direnter: compact1");
 1023                 entry->e2d_reclen = spacefree + dsize;
 1024         } else {
 1025                 if (spacefree < newentrysize)
 1026                         panic("ext2_direnter: compact2");
 1027                 entry->e2d_reclen = spacefree;
 1028                 ep->e2d_reclen = dsize;
 1029                 ep = (struct ext2fs_direct_2 *)((char *)ep + dsize);
 1030         }
 1031         bcopy((caddr_t)entry, (caddr_t)ep, (u_int)newentrysize);
 1032         if (DOINGASYNC(dvp)) {
 1033                 bdwrite(bp);
 1034                 error = 0;
 1035         } else {
 1036                 error = bwrite(bp);
 1037         }
 1038         dp->i_flag |= IN_CHANGE | IN_UPDATE;
 1039         return (error);
 1040 }
 1041 
 1042 /*
 1043  * Remove a directory entry after a call to namei, using
 1044  * the parameters which it left in nameidata. The entry
 1045  * dp->i_offset contains the offset into the directory of the
 1046  * entry to be eliminated.  The dp->i_count field contains the
 1047  * size of the previous record in the directory.  If this
 1048  * is 0, the first entry is being deleted, so we need only
 1049  * zero the inode number to mark the entry as free.  If the
 1050  * entry is not the first in the directory, we must reclaim
 1051  * the space of the now empty record by adding the record size
 1052  * to the size of the previous entry.
 1053  */
 1054 int
 1055 ext2_dirremove(struct vnode *dvp, struct componentname *cnp)
 1056 {
 1057         struct inode *dp;
 1058         struct ext2fs_direct_2 *ep, *rep;
 1059         struct buf *bp;
 1060         int error;
 1061 
 1062         dp = VTOI(dvp);
 1063         if (dp->i_count == 0) {
 1064                 /*
 1065                  * First entry in block: set d_ino to zero.
 1066                  */
 1067                 if ((error =
 1068                     ext2_blkatoff(dvp, (off_t)dp->i_offset, (char **)&ep,
 1069                     &bp)) != 0)
 1070                         return (error);
 1071                 ep->e2d_ino = 0;
 1072                 error = bwrite(bp);
 1073                 dp->i_flag |= IN_CHANGE | IN_UPDATE;
 1074                 return (error);
 1075         }
 1076         /*
 1077          * Collapse new free space into previous entry.
 1078          */
 1079         if ((error = ext2_blkatoff(dvp, (off_t)(dp->i_offset - dp->i_count),
 1080             (char **)&ep, &bp)) != 0)
 1081                 return (error);
 1082 
 1083         /* Set 'rep' to the entry being removed. */
 1084         if (dp->i_count == 0)
 1085                 rep = ep;
 1086         else
 1087                 rep = (struct ext2fs_direct_2 *)((char *)ep + ep->e2d_reclen);
 1088         ep->e2d_reclen += rep->e2d_reclen;
 1089         if (DOINGASYNC(dvp) && dp->i_count != 0)
 1090                 bdwrite(bp);
 1091         else
 1092                 error = bwrite(bp);
 1093         dp->i_flag |= IN_CHANGE | IN_UPDATE;
 1094         return (error);
 1095 }
 1096 
 1097 /*
 1098  * Rewrite an existing directory entry to point at the inode
 1099  * supplied.  The parameters describing the directory entry are
 1100  * set up by a call to namei.
 1101  */
 1102 int
 1103 ext2_dirrewrite(struct inode *dp, struct inode *ip, struct componentname *cnp)
 1104 {
 1105         struct buf *bp;
 1106         struct ext2fs_direct_2 *ep;
 1107         struct vnode *vdp = ITOV(dp);
 1108         int error;
 1109 
 1110         if ((error = ext2_blkatoff(vdp, (off_t)dp->i_offset, (char **)&ep,
 1111             &bp)) != 0)
 1112                 return (error);
 1113         ep->e2d_ino = ip->i_number;
 1114         if (EXT2_HAS_INCOMPAT_FEATURE(ip->i_e2fs,
 1115             EXT2F_INCOMPAT_FTYPE))
 1116                 ep->e2d_type = DTTOFT(IFTODT(ip->i_mode));
 1117         else
 1118                 ep->e2d_type = EXT2_FT_UNKNOWN;
 1119         error = bwrite(bp);
 1120         dp->i_flag |= IN_CHANGE | IN_UPDATE;
 1121         return (error);
 1122 }
 1123 
 1124 /*
 1125  * Check if a directory is empty or not.
 1126  * Inode supplied must be locked.
 1127  *
 1128  * Using a struct dirtemplate here is not precisely
 1129  * what we want, but better than using a struct direct.
 1130  *
 1131  * NB: does not handle corrupted directories.
 1132  */
 1133 int
 1134 ext2_dirempty(struct inode *ip, ino_t parentino, struct ucred *cred)
 1135 {
 1136         off_t off;
 1137         struct dirtemplate dbuf;
 1138         struct ext2fs_direct_2 *dp = (struct ext2fs_direct_2 *)&dbuf;
 1139         int error, namlen;
 1140         ssize_t count;
 1141 #define MINDIRSIZ (sizeof(struct dirtemplate) / 2)
 1142 
 1143         for (off = 0; off < ip->i_size; off += dp->e2d_reclen) {
 1144                 error = vn_rdwr(UIO_READ, ITOV(ip), (caddr_t)dp, MINDIRSIZ,
 1145                     off, UIO_SYSSPACE, IO_NODELOCKED | IO_NOMACCHECK, cred,
 1146                     NOCRED, &count, (struct thread *)0);
 1147                 /*
 1148                  * Since we read MINDIRSIZ, residual must
 1149                  * be 0 unless we're at end of file.
 1150                  */
 1151                 if (error || count != 0)
 1152                         return (0);
 1153                 /* avoid infinite loops */
 1154                 if (dp->e2d_reclen == 0)
 1155                         return (0);
 1156                 /* skip empty entries */
 1157                 if (dp->e2d_ino == 0)
 1158                         continue;
 1159                 /* accept only "." and ".." */
 1160                 namlen = dp->e2d_namlen;
 1161                 if (namlen > 2)
 1162                         return (0);
 1163                 if (dp->e2d_name[0] != '.')
 1164                         return (0);
 1165                 /*
 1166                  * At this point namlen must be 1 or 2.
 1167                  * 1 implies ".", 2 implies ".." if second
 1168                  * char is also "."
 1169                  */
 1170                 if (namlen == 1)
 1171                         continue;
 1172                 if (dp->e2d_name[1] == '.' && dp->e2d_ino == parentino)
 1173                         continue;
 1174                 return (0);
 1175         }
 1176         return (1);
 1177 }
 1178 
 1179 /*
 1180  * Check if source directory is in the path of the target directory.
 1181  * Target is supplied locked, source is unlocked.
 1182  * The target is always vput before returning.
 1183  */
 1184 int
 1185 ext2_checkpath(struct inode *source, struct inode *target, struct ucred *cred)
 1186 {
 1187         struct vnode *vp;
 1188         int error, namlen;
 1189         struct dirtemplate dirbuf;
 1190 
 1191         vp = ITOV(target);
 1192         if (target->i_number == source->i_number) {
 1193                 error = EEXIST;
 1194                 goto out;
 1195         }
 1196         if (target->i_number == EXT2_ROOTINO) {
 1197                 error = 0;
 1198                 goto out;
 1199         }
 1200 
 1201         for (;;) {
 1202                 if (vp->v_type != VDIR) {
 1203                         error = ENOTDIR;
 1204                         break;
 1205                 }
 1206                 error = vn_rdwr(UIO_READ, vp, (caddr_t)&dirbuf,
 1207                     sizeof(struct dirtemplate), (off_t)0, UIO_SYSSPACE,
 1208                     IO_NODELOCKED | IO_NOMACCHECK, cred, NOCRED, NULL,
 1209                     NULL);
 1210                 if (error != 0)
 1211                         break;
 1212                 namlen = dirbuf.dotdot_type;    /* like ufs little-endian */
 1213                 if (namlen != 2 ||
 1214                     dirbuf.dotdot_name[0] != '.' ||
 1215                     dirbuf.dotdot_name[1] != '.') {
 1216                         error = ENOTDIR;
 1217                         break;
 1218                 }
 1219                 if (dirbuf.dotdot_ino == source->i_number) {
 1220                         error = EINVAL;
 1221                         break;
 1222                 }
 1223                 if (dirbuf.dotdot_ino == EXT2_ROOTINO)
 1224                         break;
 1225                 vput(vp);
 1226                 if ((error = VFS_VGET(vp->v_mount, dirbuf.dotdot_ino,
 1227                     LK_EXCLUSIVE, &vp)) != 0) {
 1228                         vp = NULL;
 1229                         break;
 1230                 }
 1231         }
 1232 
 1233 out:
 1234         if (error == ENOTDIR)
 1235                 printf("checkpath: .. not a directory\n");
 1236         if (vp != NULL)
 1237                 vput(vp);
 1238         return (error);
 1239 }

Cache object: 86ad45421006160a27af794989c09c34


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