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

Cache object: d212240389f206d4cf4e95590dfb5fdf


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