LCOV - code coverage report
Current view: top level - ufs/ext2fs - ext2fs_lookup.c (source / functions) Hit Total Coverage
Test: 6.4 Lines: 0 472 0.0 %
Date: 2018-10-19 03:25:38 Functions: 0 10 0.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13