The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/fs/cd9660/cd9660_lookup.c

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

    1 /*      $NetBSD: cd9660_lookup.c,v 1.6 2003/08/07 16:31:34 agc Exp $    */
    2 
    3 /*-
    4  * Copyright (c) 1989, 1993, 1994
    5  *      The Regents of the University of California.  All rights reserved.
    6  *
    7  * This code is derived from software contributed to Berkeley
    8  * by Pace Willisson (pace@blitz.com).  The Rock Ridge Extension
    9  * Support code is derived from software contributed to Berkeley
   10  * by Atsushi Murai (amurai@spec.co.jp).
   11  *
   12  * Redistribution and use in source and binary forms, with or without
   13  * modification, are permitted provided that the following conditions
   14  * are met:
   15  * 1. Redistributions of source code must retain the above copyright
   16  *    notice, this list of conditions and the following disclaimer.
   17  * 2. Redistributions in binary form must reproduce the above copyright
   18  *    notice, this list of conditions and the following disclaimer in the
   19  *    documentation and/or other materials provided with the distribution.
   20  * 3. Neither the name of the University nor the names of its contributors
   21  *    may be used to endorse or promote products derived from this software
   22  *    without specific prior written permission.
   23  *
   24  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
   25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
   28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   34  * SUCH DAMAGE.
   35  *
   36  *      from: @(#)ufs_lookup.c  7.33 (Berkeley) 5/19/91
   37  *
   38  *      @(#)cd9660_lookup.c     8.5 (Berkeley) 5/27/95
   39  */
   40 
   41 #include <sys/cdefs.h>
   42 __KERNEL_RCSID(0, "$NetBSD: cd9660_lookup.c,v 1.6 2003/08/07 16:31:34 agc Exp $");
   43 
   44 #include <sys/param.h>
   45 #include <sys/namei.h>
   46 #include <sys/buf.h>
   47 #include <sys/file.h>
   48 #include <sys/vnode.h>
   49 #include <sys/mount.h>
   50 #include <sys/systm.h>
   51 
   52 #include <fs/cd9660/iso.h>
   53 #include <fs/cd9660/cd9660_extern.h>
   54 #include <fs/cd9660/cd9660_node.h>
   55 #include <fs/cd9660/iso_rrip.h>
   56 #include <fs/cd9660/cd9660_rrip.h>
   57 #include <fs/cd9660/cd9660_mount.h>
   58 
   59 struct  nchstats iso_nchstats;
   60 
   61 /*
   62  * Convert a component of a pathname into a pointer to a locked inode.
   63  * This is a very central and rather complicated routine.
   64  * If the file system is not maintained in a strict tree hierarchy,
   65  * this can result in a deadlock situation (see comments in code below).
   66  *
   67  * The flag argument is LOOKUP, CREATE, RENAME, or DELETE depending on
   68  * whether the name is to be looked up, created, renamed, or deleted.
   69  * When CREATE, RENAME, or DELETE is specified, information usable in
   70  * creating, renaming, or deleting a directory entry may be calculated.
   71  * If flag has LOCKPARENT or'ed into it and the target of the pathname
   72  * exists, lookup returns both the target and its parent directory locked.
   73  * When creating or renaming and LOCKPARENT is specified, the target may
   74  * not be ".".  When deleting and LOCKPARENT is specified, the target may
   75  * be "."., but the caller must check to ensure it does an vrele and iput
   76  * instead of two iputs.
   77  *
   78  * Overall outline of ufs_lookup:
   79  *
   80  *      check accessibility of directory
   81  *      look for name in cache, if found, then if at end of path
   82  *        and deleting or creating, drop it, else return name
   83  *      search for name in directory, to found or notfound
   84  * notfound:
   85  *      if creating, return locked directory, leaving info on available slots
   86  *      else return error
   87  * found:
   88  *      if at end of path and deleting, return information to allow delete
   89  *      if at end of path and rewriting (RENAME and LOCKPARENT), lock target
   90  *        inode and return info to allow rewrite
   91  *      if not at end, add name to cache; if at end and neither creating
   92  *        nor deleting, add name to cache
   93  *
   94  * NOTE: (LOOKUP | LOCKPARENT) currently returns the parent inode unlocked.
   95  */
   96 int
   97 cd9660_lookup(v)
   98         void *v;
   99 {
  100         struct vop_lookup_args /* {
  101                 struct vnode *a_dvp;
  102                 struct vnode **a_vpp;
  103                 struct componentname *a_cnp;
  104         } */ *ap = v;
  105         struct vnode *vdp;              /* vnode for directory being searched */
  106         struct iso_node *dp;            /* inode for directory being searched */
  107         struct iso_mnt *imp;            /* file system that directory is in */
  108         struct buf *bp;                 /* a buffer of directory entries */
  109         struct iso_directory_record *ep = NULL;
  110                                         /* the current directory entry */
  111         int entryoffsetinblock;         /* offset of ep in bp's buffer */
  112         int saveoffset = -1;            /* offset of last directory entry in dir */
  113         int numdirpasses;               /* strategy for directory search */
  114         doff_t endsearch;               /* offset to end directory search */
  115         struct vnode *pdp;              /* saved dp during symlink work */
  116         struct vnode *tdp;              /* returned by cd9660_vget_internal */
  117         u_long bmask;                   /* block offset mask */
  118         int lockparent;                 /* 1 => lockparent flag is set */
  119         int error;
  120         ino_t ino = 0;
  121         int reclen;
  122         u_short namelen;
  123         char altname[NAME_MAX];
  124         int res;
  125         int assoc, len;
  126         const char *name;
  127         struct vnode **vpp = ap->a_vpp;
  128         struct componentname *cnp = ap->a_cnp;
  129         struct ucred *cred = cnp->cn_cred;
  130         int flags;
  131         int nameiop = cnp->cn_nameiop;
  132         
  133         cnp->cn_flags &= ~PDIRUNLOCK;
  134         flags = cnp->cn_flags;
  135 
  136         bp = NULL;
  137         *vpp = NULL;
  138         vdp = ap->a_dvp;
  139         dp = VTOI(vdp);
  140         imp = dp->i_mnt;
  141         lockparent = flags & LOCKPARENT;
  142         
  143         /*
  144          * Check accessiblity of directory.
  145          */
  146         if ((error = VOP_ACCESS(vdp, VEXEC, cred, cnp->cn_proc)) != 0)
  147                 return (error);
  148 
  149         if ((flags & ISLASTCN) && (vdp->v_mount->mnt_flag & MNT_RDONLY) &&
  150             (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME))
  151                 return (EROFS);
  152 
  153         /*
  154          * We now have a segment name to search for, and a directory to search.
  155          *
  156          * Before tediously performing a linear scan of the directory,
  157          * check the name cache to see if the directory/name pair
  158          * we are looking for is known already.
  159          */
  160         if ((error = cache_lookup(vdp, vpp, cnp)) >= 0)
  161                 return (error);
  162         
  163         len = cnp->cn_namelen;
  164         name = cnp->cn_nameptr;
  165         /*
  166          * A leading `=' means, we are looking for an associated file
  167          */
  168         assoc = (imp->iso_ftype != ISO_FTYPE_RRIP && *name == ASSOCCHAR);
  169         if (assoc) {
  170                 len--;
  171                 name++;
  172         }
  173         
  174         /*
  175          * If there is cached information on a previous search of
  176          * this directory, pick up where we last left off.
  177          * We cache only lookups as these are the most common
  178          * and have the greatest payoff. Caching CREATE has little
  179          * benefit as it usually must search the entire directory
  180          * to determine that the entry does not exist. Caching the
  181          * location of the last DELETE or RENAME has not reduced
  182          * profiling time and hence has been removed in the interest
  183          * of simplicity.
  184          */
  185         bmask = imp->im_bmask;
  186         if (nameiop != LOOKUP || dp->i_diroff == 0 ||
  187             dp->i_diroff > dp->i_size) {
  188                 entryoffsetinblock = 0;
  189                 dp->i_offset = 0;
  190                 numdirpasses = 1;
  191         } else {
  192                 dp->i_offset = dp->i_diroff;
  193                 if ((entryoffsetinblock = dp->i_offset & bmask) &&
  194                     (error = VOP_BLKATOFF(vdp, (off_t)dp->i_offset, NULL, &bp)))
  195                                 return (error);
  196                 numdirpasses = 2;
  197                 iso_nchstats.ncs_2passes++;
  198         }
  199         endsearch = dp->i_size;
  200         
  201 searchloop:
  202         while (dp->i_offset < endsearch) {
  203                 /*
  204                  * If offset is on a block boundary,
  205                  * read the next directory block.
  206                  * Release previous if it exists.
  207                  */
  208                 if ((dp->i_offset & bmask) == 0) {
  209                         if (bp != NULL)
  210                                 brelse(bp);
  211                         error = VOP_BLKATOFF(vdp, (off_t)dp->i_offset,
  212                                              NULL, &bp);
  213                         if (error)
  214                                 return (error);
  215                         entryoffsetinblock = 0;
  216                 }
  217                 /*
  218                  * Get pointer to next entry.
  219                  */
  220                 ep = (struct iso_directory_record *)
  221                         ((char *)bp->b_data + entryoffsetinblock);
  222                 
  223                 reclen = isonum_711(ep->length);
  224                 if (reclen == 0) {
  225                         /* skip to next block, if any */
  226                         dp->i_offset =
  227                             (dp->i_offset & ~bmask) + imp->logical_block_size;
  228                         continue;
  229                 }
  230                 
  231                 if (reclen < ISO_DIRECTORY_RECORD_SIZE)
  232                         /* illegal entry, stop */
  233                         break;
  234                 
  235                 if (entryoffsetinblock + reclen > imp->logical_block_size)
  236                         /* entries are not allowed to cross boundaries */
  237                         break;
  238                 
  239                 namelen = isonum_711(ep->name_len);
  240                 
  241                 if (reclen < ISO_DIRECTORY_RECORD_SIZE + namelen)
  242                         /* illegal entry, stop */
  243                         break;
  244                 
  245                 /*
  246                  * Check for a name match.
  247                  */
  248                 switch (imp->iso_ftype) {
  249                 default:
  250                         if ((!(isonum_711(ep->flags)&4)) == !assoc) {
  251                                 if ((len == 1
  252                                      && *name == '.')
  253                                     || (flags & ISDOTDOT)) {
  254                                         if (namelen == 1
  255                                             && ep->name[0] == ((flags & ISDOTDOT) ? 1 : 0)) {
  256                                                 /*
  257                                                  * Save directory entry's inode number and
  258                                                  * release directory buffer.
  259                                                  */
  260                                                 dp->i_ino = isodirino(ep, imp);
  261                                                 goto found;
  262                                         }
  263                                         if (namelen != 1
  264                                             || ep->name[0] != 0)
  265                                                 goto notfound;
  266                                 } else if (!(res = isofncmp(name,len,
  267                                                    ep->name,namelen,
  268                                                    imp->im_joliet_level))) {
  269                                         if (isonum_711(ep->flags)&2)
  270                                                 ino = isodirino(ep, imp);
  271                                         else
  272                                                 ino = dbtob(bp->b_blkno)
  273                                                         + entryoffsetinblock;
  274                                         saveoffset = dp->i_offset;
  275                                 } else if (ino)
  276                                         goto foundino;
  277 #ifdef  NOSORTBUG       /* On some CDs directory entries are not sorted correctly */
  278                                 else if (res < 0)
  279                                         goto notfound;
  280                                 else if (res > 0 && numdirpasses == 2)
  281                                         numdirpasses++;
  282 #endif
  283                         }
  284                         break;
  285                 case ISO_FTYPE_RRIP:
  286                         if (isonum_711(ep->flags)&2)
  287                                 ino = isodirino(ep, imp);
  288                         else
  289                                 ino = dbtob(bp->b_blkno) + entryoffsetinblock;
  290                         dp->i_ino = ino;
  291                         cd9660_rrip_getname(ep,altname,&namelen,&dp->i_ino,imp);
  292                         if (namelen == cnp->cn_namelen) {
  293                                 if (imp->im_flags & ISOFSMNT_RRCASEINS) {
  294                                         if (strncasecmp(name, altname, namelen) == 0)
  295                                                 goto found;
  296                                 } else {
  297                                         if (memcmp(name, altname, namelen) == 0)
  298                                                 goto found;
  299                                 }
  300                         }
  301                         ino = 0;
  302                         break;
  303                 }
  304                 dp->i_offset += reclen;
  305                 entryoffsetinblock += reclen;
  306         }
  307         if (ino) {
  308 foundino:
  309                 dp->i_ino = ino;
  310                 if (saveoffset != dp->i_offset) {
  311                         if (lblkno(imp, dp->i_offset) !=
  312                             lblkno(imp, saveoffset)) {
  313                                 if (bp != NULL)
  314                                         brelse(bp);
  315                                 if ((error = VOP_BLKATOFF(vdp,
  316                                             (off_t)saveoffset, NULL, &bp)) != 0)
  317                                         return (error);
  318                         }
  319                         entryoffsetinblock = saveoffset & bmask;
  320                         ep = (struct iso_directory_record *)
  321                                 ((char *)bp->b_data + entryoffsetinblock);
  322                         dp->i_offset = saveoffset;
  323                 }
  324                 goto found;
  325         }
  326 notfound:
  327         /*
  328          * If we started in the middle of the directory and failed
  329          * to find our target, we must check the beginning as well.
  330          */
  331         if (numdirpasses == 2) {
  332                 numdirpasses--;
  333                 dp->i_offset = 0;
  334                 endsearch = dp->i_diroff;
  335                 goto searchloop;
  336         }
  337         if (bp != NULL)
  338                 brelse(bp);
  339 
  340         /*
  341          * Insert name into cache (as non-existent) if appropriate.
  342          */
  343         if (cnp->cn_flags & MAKEENTRY)
  344                 cache_enter(vdp, *vpp, cnp);
  345         if (nameiop == CREATE || nameiop == RENAME)
  346                 return (EROFS);
  347         return (ENOENT);
  348         
  349 found:
  350         if (numdirpasses == 2)
  351                 iso_nchstats.ncs_pass2++;
  352         
  353         /*
  354          * Found component in pathname.
  355          * If the final component of path name, save information
  356          * in the cache as to where the entry was found.
  357          */
  358         if ((flags & ISLASTCN) && nameiop == LOOKUP)
  359                 dp->i_diroff = dp->i_offset;
  360         
  361         /*
  362          * Step through the translation in the name.  We do not `iput' the
  363          * directory because we may need it again if a symbolic link
  364          * is relative to the current directory.  Instead we save it
  365          * unlocked as "pdp".  We must get the target inode before unlocking
  366          * the directory to insure that the inode will not be removed
  367          * before we get it.  We prevent deadlock by always fetching
  368          * inodes from the root, moving down the directory tree. Thus
  369          * when following backward pointers ".." we must unlock the
  370          * parent directory before getting the requested directory.
  371          * There is a potential race condition here if both the current
  372          * and parent directories are removed before the `iget' for the
  373          * inode associated with ".." returns.  We hope that this occurs
  374          * infrequently since we cannot avoid this race condition without
  375          * implementing a sophisticated deadlock detection algorithm.
  376          * Note also that this simple deadlock detection scheme will not
  377          * work if the file system has any hard links other than ".."
  378          * that point backwards in the directory structure.
  379          */
  380         pdp = vdp;
  381         /*
  382          * If ino is different from dp->i_ino,
  383          * it's a relocated directory.
  384          */
  385         if (flags & ISDOTDOT) {
  386                 VOP_UNLOCK(pdp, 0);     /* race to get the inode */
  387                 cnp->cn_flags |= PDIRUNLOCK;
  388                 error = cd9660_vget_internal(vdp->v_mount, dp->i_ino, &tdp,
  389                                              dp->i_ino != ino, ep);
  390                 brelse(bp);
  391                 if (error) {
  392                         if (vn_lock(pdp, LK_EXCLUSIVE | LK_RETRY) == 0)
  393                                 cnp->cn_flags &= ~PDIRUNLOCK;
  394                         return (error);
  395                 }
  396                 if (lockparent && (flags & ISLASTCN)) {
  397                         if ((error = vn_lock(pdp, LK_EXCLUSIVE))) {
  398                                 vput(tdp);
  399                                 return (error);
  400                         }
  401                         cnp->cn_flags &= ~PDIRUNLOCK;
  402                 }
  403                 *vpp = tdp;
  404         } else if (dp->i_number == dp->i_ino) {
  405                 brelse(bp);
  406                 VREF(vdp);      /* we want ourself, ie "." */
  407                 *vpp = vdp;
  408         } else {
  409                 error = cd9660_vget_internal(vdp->v_mount, dp->i_ino, &tdp,
  410                                              dp->i_ino != ino, ep);
  411                 brelse(bp);
  412                 if (error)
  413                         return (error);
  414                 if (!lockparent || !(flags & ISLASTCN)) {
  415                         VOP_UNLOCK(pdp, 0);
  416                         cnp->cn_flags |= PDIRUNLOCK;
  417                 }
  418                 *vpp = tdp;
  419         }
  420         
  421         /*
  422          * Insert name into cache if appropriate.
  423          */
  424         if (cnp->cn_flags & MAKEENTRY)
  425                 cache_enter(vdp, *vpp, cnp);
  426         return (0);
  427 }
  428 
  429 /*
  430  * Return buffer with the contents of block "offset" from the beginning of
  431  * directory "ip".  If "res" is non-zero, fill it in with a pointer to the
  432  * remaining space in the directory.
  433  */
  434 int
  435 cd9660_blkatoff(v)
  436         void *v;
  437 {
  438         struct vop_blkatoff_args /* {
  439                 struct vnode *a_vp;
  440                 off_t a_offset;
  441                 char **a_res;
  442                 struct buf **a_bpp;
  443         } */ *ap = v;
  444         struct iso_node *ip;
  445         struct iso_mnt *imp;
  446         struct buf *bp;
  447         daddr_t lbn;
  448         int bsize, error;
  449 
  450         ip = VTOI(ap->a_vp);
  451         imp = ip->i_mnt;
  452         lbn = lblkno(imp, ap->a_offset);
  453         bsize = blksize(imp, ip, lbn);
  454         
  455         if ((error = bread(ap->a_vp, lbn, bsize, NOCRED, &bp)) != 0) {
  456                 brelse(bp);
  457                 *ap->a_bpp = NULL;
  458                 return (error);
  459         }
  460         if (ap->a_res)
  461                 *ap->a_res = (char *)bp->b_data + blkoff(imp, ap->a_offset);
  462         *ap->a_bpp = bp;
  463         return (0);
  464 }

Cache object: fac769ff347786fafd6098b454762517


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.