root/ufs/ext2fs/ext2fs_lookup.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. ext2fs_dirconv2ffs
  2. ext2fs_readdir
  3. ext2fs_lookup
  4. ext2fs_dirbadentry
  5. ext2fs_direnter
  6. ext2fs_dirremove
  7. ext2fs_dirrewrite
  8. ext2fs_dirempty
  9. ext2fs_checkpath

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

/* [<][>][^][v][top][bottom][index][help] */