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

          Line data    Source code
       1             : /*      $OpenBSD: msdosfs_lookup.c,v 1.32 2018/05/02 02:24:56 visa Exp $        */
       2             : /*      $NetBSD: msdosfs_lookup.c,v 1.34 1997/10/18 22:12:27 ws Exp $   */
       3             : 
       4             : /*-
       5             :  * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank.
       6             :  * Copyright (C) 1994, 1995, 1997 TooLs GmbH.
       7             :  * All rights reserved.
       8             :  * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below).
       9             :  *
      10             :  * Redistribution and use in source and binary forms, with or without
      11             :  * modification, are permitted provided that the following conditions
      12             :  * are met:
      13             :  * 1. Redistributions of source code must retain the above copyright
      14             :  *    notice, this list of conditions and the following disclaimer.
      15             :  * 2. Redistributions in binary form must reproduce the above copyright
      16             :  *    notice, this list of conditions and the following disclaimer in the
      17             :  *    documentation and/or other materials provided with the distribution.
      18             :  * 3. All advertising materials mentioning features or use of this software
      19             :  *    must display the following acknowledgement:
      20             :  *      This product includes software developed by TooLs GmbH.
      21             :  * 4. The name of TooLs GmbH may not be used to endorse or promote products
      22             :  *    derived from this software without specific prior written permission.
      23             :  *
      24             :  * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
      25             :  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
      26             :  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
      27             :  * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
      28             :  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
      29             :  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
      30             :  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
      31             :  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
      32             :  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
      33             :  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
      34             :  */
      35             : /*
      36             :  * Written by Paul Popelka (paulp@uts.amdahl.com)
      37             :  *
      38             :  * You can do anything you want with this software, just don't say you wrote
      39             :  * it, and don't remove this notice.
      40             :  *
      41             :  * This software is provided "as is".
      42             :  *
      43             :  * The author supplies this software to be publicly redistributed on the
      44             :  * understanding that the author is not responsible for the correct
      45             :  * functioning of this software in any circumstances and is not liable for
      46             :  * any damages caused by this software.
      47             :  *
      48             :  * October 1992
      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/vnode.h>
      56             : #include <sys/lock.h>
      57             : #include <sys/mount.h>
      58             : #include <sys/dirent.h>
      59             : 
      60             : #include <msdosfs/bpb.h>
      61             : #include <msdosfs/direntry.h>
      62             : #include <msdosfs/denode.h>
      63             : #include <msdosfs/msdosfsmount.h>
      64             : #include <msdosfs/fat.h>
      65             : 
      66             : /*
      67             :  * When we search a directory the blocks containing directory entries are
      68             :  * read and examined.  The directory entries contain information that would
      69             :  * normally be in the inode of a unix filesystem.  This means that some of
      70             :  * a directory's contents may also be in memory resident denodes (sort of
      71             :  * an inode).  This can cause problems if we are searching while some other
      72             :  * process is modifying a directory.  To prevent one process from accessing
      73             :  * incompletely modified directory information we depend upon being the
      74             :  * sole owner of a directory block.  bread/brelse provide this service.
      75             :  * This being the case, when a process modifies a directory it must first
      76             :  * acquire the disk block that contains the directory entry to be modified.
      77             :  * Then update the disk block and the denode, and then write the disk block
      78             :  * out to disk.  This way disk blocks containing directory entries and in
      79             :  * memory denode's will be in synch.
      80             :  */
      81             : int
      82           0 : msdosfs_lookup(void *v)
      83             : {
      84           0 :         struct vop_lookup_args *ap = v;
      85           0 :         struct vnode *vdp = ap->a_dvp;
      86           0 :         struct vnode **vpp = ap->a_vpp;
      87           0 :         struct componentname *cnp = ap->a_cnp;
      88           0 :         daddr_t bn;
      89             :         int error;
      90             :         int lockparent;
      91             :         int wantparent;
      92             :         int slotcount;
      93             :         int slotoffset = 0;
      94             :         int frcn;
      95           0 :         uint32_t cluster;
      96             :         int blkoff;
      97             :         int diroff;
      98           0 :         int blsize;
      99             :         int isadir;             /* ~0 if found direntry is a directory   */
     100             :         uint32_t scn;           /* starting cluster number               */
     101             :         struct vnode *pdp;
     102             :         struct denode *dp;
     103           0 :         struct denode *tdp;
     104             :         struct msdosfsmount *pmp;
     105           0 :         struct buf *bp = 0;
     106             :         struct direntry *dep;
     107           0 :         u_char dosfilename[11];
     108             :         u_char *adjp;
     109             :         int adjlen;
     110             :         int flags;
     111           0 :         int nameiop = cnp->cn_nameiop;
     112             :         int wincnt = 1;
     113             :         int chksum = -1, chksum_ok;
     114             :         int olddos = 1;
     115             : 
     116           0 :         cnp->cn_flags &= ~PDIRUNLOCK; /* XXX why this ?? */
     117           0 :         flags = cnp->cn_flags;
     118             : 
     119             : #ifdef MSDOSFS_DEBUG
     120             :         printf("msdosfs_lookup(): looking for %s\n", cnp->cn_nameptr);
     121             : #endif
     122           0 :         dp = VTODE(vdp);
     123           0 :         pmp = dp->de_pmp;
     124           0 :         *vpp = NULL;
     125           0 :         lockparent = flags & LOCKPARENT;
     126           0 :         wantparent = flags & (LOCKPARENT | WANTPARENT);
     127             : #ifdef MSDOSFS_DEBUG
     128             :         printf("msdosfs_lookup(): vdp %p, dp %p, Attr %02x\n",
     129             :             vdp, dp, dp->de_Attributes);
     130             : #endif
     131             : 
     132             :         /*
     133             :          * Check accessiblity of directory.
     134             :          */
     135           0 :         if ((dp->de_Attributes & ATTR_DIRECTORY) == 0)
     136           0 :                 return (ENOTDIR);
     137           0 :         if ((error = VOP_ACCESS(vdp, VEXEC, cnp->cn_cred, cnp->cn_proc)) != 0)
     138           0 :                 return (error);
     139             : 
     140             :         /*
     141             :          * We now have a segment name to search for, and a directory to search.
     142             :          *
     143             :          * Before tediously performing a linear scan of the directory,
     144             :          * check the name cache to see if the directory/name pair
     145             :          * we are looking for is known already.
     146             :          */
     147           0 :         if ((error = cache_lookup(vdp, vpp, cnp)) >= 0)
     148           0 :                 return (error);
     149             : 
     150             :         /*
     151             :          * If they are going after the . or .. entry in the root directory,
     152             :          * they won't find it.  DOS filesystems don't have them in the root
     153             :          * directory.  So, we fake it. deget() is in on this scam too.
     154             :          */
     155           0 :         if ((vdp->v_flag & VROOT) && cnp->cn_nameptr[0] == '.' &&
     156           0 :             (cnp->cn_namelen == 1 ||
     157           0 :                 (cnp->cn_namelen == 2 && cnp->cn_nameptr[1] == '.'))) {
     158             :                 isadir = ATTR_DIRECTORY;
     159             :                 scn = MSDOSFSROOT;
     160             : #ifdef MSDOSFS_DEBUG
     161             :                 printf("msdosfs_lookup(): looking for . or .. in root directory\n");
     162             : #endif
     163           0 :                 cluster = MSDOSFSROOT;
     164             :                 blkoff = MSDOSFSROOT_OFS;
     165           0 :                 goto foundroot;
     166             :         }
     167             : 
     168           0 :         switch (unix2dosfn((u_char *)cnp->cn_nameptr, dosfilename, cnp->cn_namelen, 0)) {
     169             :         case 0:
     170           0 :                 return (EINVAL);
     171             :         case 1:
     172             :                 break;
     173             :         case 2:
     174           0 :                 wincnt = winSlotCnt((u_char *)cnp->cn_nameptr, cnp->cn_namelen) + 1;
     175           0 :                 break;
     176             :         case 3:
     177             :                 olddos = 0;
     178           0 :                 wincnt = winSlotCnt((u_char *)cnp->cn_nameptr, cnp->cn_namelen) + 1;
     179           0 :                 break;
     180             :         }
     181           0 :         if (pmp->pm_flags & MSDOSFSMNT_SHORTNAME)
     182           0 :                 wincnt = 1;
     183             : 
     184             :         /*
     185             :          * Suppress search for slots unless creating
     186             :          * file and at end of pathname, in which case
     187             :          * we watch for a place to put the new file in
     188             :          * case it doesn't already exist.
     189             :          */
     190             :         slotcount = wincnt;
     191           0 :         if ((nameiop == CREATE || nameiop == RENAME) &&
     192           0 :             (flags & ISLASTCN))
     193           0 :                 slotcount = 0;
     194             : 
     195             : #ifdef MSDOSFS_DEBUG
     196             :         printf("msdosfs_lookup(): dos version of filename '%.11s', "
     197             :             "length %ld\n", dosfilename, cnp->cn_namelen);
     198             : #endif
     199             : 
     200             :         /*
     201             :          * We want to search the directory pointed to by vdp for the name
     202             :          * pointed to by cnp->cn_nameptr.
     203             :          *
     204             :          * XXX UNIX allows filenames with trailing dots and blanks; we don't.
     205             :          *     Most of the routines in msdosfs_conv.c adjust for this, but
     206             :          *     winChkName() does not, so we do it here.  Otherwise, a file
     207             :          *     such as ".foobar." cannot be retrieved properly.
     208             :          *
     209             :          *     (Note that this is also faster: perform the adjustment once,
     210             :          *     rather than on each call to winChkName.  However, it is still
     211             :          *     a nasty hack.)
     212             :          */
     213           0 :         adjp = cnp->cn_nameptr;
     214           0 :         adjlen = cnp->cn_namelen;
     215             : 
     216           0 :         for (adjp += adjlen; adjlen > 0; adjlen--)
     217           0 :                 if (*--adjp != ' ' && *adjp != '.')
     218             :                         break;
     219             : 
     220           0 :         tdp = NULL;
     221             :         /*
     222             :          * The outer loop ranges over the clusters that make up the
     223             :          * directory.  Note that the root directory is different from all
     224             :          * other directories.  It has a fixed number of blocks that are not
     225             :          * part of the pool of allocatable clusters.  So, we treat it a
     226             :          * little differently. The root directory starts at "cluster" 0.
     227             :          */
     228             :         diroff = 0;
     229           0 :         for (frcn = 0;; frcn++) {
     230           0 :                 if ((error = pcbmap(dp, frcn, &bn, &cluster, &blsize)) != 0) {
     231           0 :                         if (error == E2BIG)
     232             :                                 break;
     233           0 :                         return (error);
     234             :                 }
     235           0 :                 error = bread(pmp->pm_devvp, bn, blsize, &bp);
     236           0 :                 if (error) {
     237           0 :                         brelse(bp);
     238           0 :                         return (error);
     239             :                 }
     240           0 :                 for (blkoff = 0; blkoff < blsize;
     241           0 :                      blkoff += sizeof(struct direntry),
     242           0 :                      diroff += sizeof(struct direntry)) {
     243           0 :                         dep = (struct direntry *)(bp->b_data + blkoff);
     244             :                         /*
     245             :                          * If the slot is empty and we are still looking
     246             :                          * for an empty then remember this one.  If the
     247             :                          * slot is not empty then check to see if it
     248             :                          * matches what we are looking for.  If the slot
     249             :                          * has never been filled with anything, then the
     250             :                          * remainder of the directory has never been used,
     251             :                          * so there is no point in searching it.
     252             :                          */
     253           0 :                         if (dep->deName[0] == SLOT_EMPTY ||
     254           0 :                             dep->deName[0] == SLOT_DELETED) {
     255             :                                 /*
     256             :                                  * Drop memory of previous long matches
     257             :                                  */
     258             :                                 chksum = -1;
     259             : 
     260           0 :                                 if (slotcount < wincnt) {
     261           0 :                                         slotcount++;
     262             :                                         slotoffset = diroff;
     263           0 :                                 }
     264           0 :                                 if (dep->deName[0] == SLOT_EMPTY) {
     265           0 :                                         brelse(bp);
     266           0 :                                         goto notfound;
     267             :                                 }
     268             :                         } else {
     269             :                                 /*
     270             :                                  * If there wasn't enough space for our winentries,
     271             :                                  * forget about the empty space
     272             :                                  */
     273           0 :                                 if (slotcount < wincnt)
     274           0 :                                         slotcount = 0;
     275             : 
     276             :                                 /*
     277             :                                  * Check for Win95 long filename entry
     278             :                                  */
     279           0 :                                 if (dep->deAttributes == ATTR_WIN95) {
     280           0 :                                         if (pmp->pm_flags & MSDOSFSMNT_SHORTNAME)
     281             :                                                 continue;
     282             : 
     283           0 :                                         chksum = winChkName((u_char *)cnp->cn_nameptr,
     284             :                                                             adjlen,
     285           0 :                                                             (struct winentry *)dep,
     286             :                                                             chksum);
     287           0 :                                         continue;
     288             :                                 }
     289             : 
     290             :                                 /*
     291             :                                  * Ignore volume labels (anywhere, not just
     292             :                                  * the root directory).
     293             :                                  */
     294           0 :                                 if (dep->deAttributes & ATTR_VOLUME) {
     295             :                                         chksum = -1;
     296           0 :                                         continue;
     297             :                                 }
     298             : 
     299             :                                 /*
     300             :                                  * Check for a checksum or name match
     301             :                                  */
     302           0 :                                 chksum_ok = (chksum == winChksum(dep->deName));
     303           0 :                                 if (!chksum_ok
     304           0 :                                     && (!olddos || bcmp(dosfilename, dep->deName, 11))) {
     305             :                                         chksum = -1;
     306           0 :                                         continue;
     307             :                                 }
     308             : #ifdef MSDOSFS_DEBUG
     309             :                                 printf("msdosfs_lookup(): match blkoff %d, diroff %d\n",
     310             :                                     blkoff, diroff);
     311             : #endif
     312             :                                 /*
     313             :                                  * Remember where this directory
     314             :                                  * entry came from for whoever did
     315             :                                  * this lookup.
     316             :                                  */
     317           0 :                                 dp->de_fndoffset = diroff;
     318           0 :                                 if (chksum_ok && nameiop == RENAME) {
     319             :                                         /*
     320             :                                          * Target had correct long name
     321             :                                          * directory entries, reuse them as
     322             :                                          * needed.
     323             :                                          */
     324           0 :                                         dp->de_fndcnt = wincnt - 1;
     325           0 :                                 } else {
     326             :                                         /*
     327             :                                          * Long name directory entries not
     328             :                                          * present or corrupt, can only reuse
     329             :                                          * dos directory entry.
     330             :                                          */
     331           0 :                                         dp->de_fndcnt = 0;
     332             :                                 }
     333             :                                 goto found;
     334             :                         }
     335             :                 }       /* for (blkoff = 0; .... */
     336             :                 /*
     337             :                  * Release the buffer holding the directory cluster just
     338             :                  * searched.
     339             :                  */
     340           0 :                 brelse(bp);
     341             :         }       /* for (frcn = 0; ; frcn++) */
     342             : 
     343             : notfound:;
     344             :         /*
     345             :          * We hold no disk buffers at this point.
     346             :          */
     347             : 
     348             :         /*
     349             :          * Fixup the slot description to point to the place where
     350             :          * we might put the new DOS direntry (putting the Win95
     351             :          * long name entries before that)
     352             :          */
     353           0 :         if (!slotcount) {
     354             :                 slotcount = 1;
     355             :                 slotoffset = diroff;
     356           0 :         }
     357           0 :         if (wincnt > slotcount)
     358           0 :                 slotoffset += sizeof(struct direntry) * (wincnt - slotcount);
     359             : 
     360             :         /*
     361             :          * If we get here we didn't find the entry we were looking for. But
     362             :          * that's ok if we are creating or renaming and are at the end of
     363             :          * the pathname and the directory hasn't been removed.
     364             :          */
     365             : #ifdef MSDOSFS_DEBUG
     366             :         printf("msdosfs_lookup(): op %d, refcnt %ld\n",
     367             :             nameiop, dp->de_refcnt);
     368             :         printf("               slotcount %d, slotoffset %d\n",
     369             :             slotcount, slotoffset);
     370             : #endif
     371           0 :         if ((nameiop == CREATE || nameiop == RENAME) &&
     372           0 :             (flags & ISLASTCN) && dp->de_refcnt != 0) {
     373             :                 /*
     374             :                  * Access for write is interpreted as allowing
     375             :                  * creation of files in the directory.
     376             :                  */
     377           0 :                 error = VOP_ACCESS(vdp, VWRITE, cnp->cn_cred, cnp->cn_proc);
     378           0 :                 if (error)
     379           0 :                         return (error);
     380             :                 /*
     381             :                  * Return an indication of where the new directory
     382             :                  * entry should be put.
     383             :                  */
     384           0 :                 dp->de_fndoffset = slotoffset;
     385           0 :                 dp->de_fndcnt = wincnt - 1;
     386             : 
     387             :                 /*
     388             :                  * We return with the directory locked, so that
     389             :                  * the parameters we set up above will still be
     390             :                  * valid if we actually decide to do a direnter().
     391             :                  * We return ni_vp == NULL to indicate that the entry
     392             :                  * does not currently exist; we leave a pointer to
     393             :                  * the (locked) directory inode in ndp->ni_dvp.
     394             :                  * The pathname buffer is saved so that the name
     395             :                  * can be obtained later.
     396             :                  *
     397             :                  * NB - if the directory is unlocked, then this
     398             :                  * information cannot be used.
     399             :                  */
     400           0 :                 cnp->cn_flags |= SAVENAME;
     401           0 :                 if (!lockparent) {
     402           0 :                         VOP_UNLOCK(vdp);
     403           0 :                         cnp->cn_flags |= PDIRUNLOCK;
     404           0 :                 }
     405           0 :                 return (EJUSTRETURN);
     406             :         }
     407             :         /*
     408             :          * Insert name into cache (as non-existent) if appropriate.
     409             :          */
     410           0 :         if ((cnp->cn_flags & MAKEENTRY) && nameiop != CREATE)
     411           0 :                 cache_enter(vdp, *vpp, cnp);
     412           0 :         return (ENOENT);
     413             : 
     414             : found:;
     415             :         /*
     416             :          * NOTE:  We still have the buffer with matched directory entry at
     417             :          * this point.
     418             :          */
     419           0 :         isadir = dep->deAttributes & ATTR_DIRECTORY;
     420           0 :         scn = getushort(dep->deStartCluster);
     421           0 :         if (FAT32(pmp)) {
     422           0 :                 scn |= getushort(dep->deHighClust) << 16;
     423           0 :                 if (scn == pmp->pm_rootdirblk) {
     424             :                         /*
     425             :                          * There should actually be 0 here.
     426             :                          * Just ignore the error.
     427             :                          */
     428             :                         scn = MSDOSFSROOT;
     429             :                 }
     430           0 :         }
     431             : 
     432           0 :         if (cluster == MSDOSFSROOT)
     433           0 :                 blkoff = diroff;
     434             : 
     435           0 :         if (isadir) {
     436           0 :                 cluster = scn;
     437           0 :                 if (cluster == MSDOSFSROOT)
     438           0 :                         blkoff = MSDOSFSROOT_OFS;
     439             :                 else
     440             :                         blkoff = 0;
     441             :         }
     442             : 
     443             :         /*
     444             :          * Now release buf to allow deget to read the entry again.
     445             :          * Reserving it here and giving it to deget could result
     446             :          * in a deadlock.
     447             :          */
     448           0 :         brelse(bp);
     449             : 
     450             : foundroot:;
     451             :         /*
     452             :          * If we entered at foundroot, then we are looking for the . or ..
     453             :          * entry of the filesystems root directory.  isadir and scn were
     454             :          * setup before jumping here.  And, bp is already null.
     455             :          */
     456           0 :         if (FAT32(pmp) && scn == MSDOSFSROOT)
     457           0 :                 scn = pmp->pm_rootdirblk;
     458             : 
     459             :         /*
     460             :          * If deleting, and at end of pathname, return
     461             :          * parameters which can be used to remove file.
     462             :          * If the wantparent flag isn't set, we return only
     463             :          * the directory (in ndp->ni_dvp), otherwise we go
     464             :          * on and lock the inode, being careful with ".".
     465             :          */
     466           0 :         if (nameiop == DELETE && (flags & ISLASTCN)) {
     467             :                 /*
     468             :                  * Don't allow deleting the root.
     469             :                  */
     470           0 :                 if (blkoff == MSDOSFSROOT_OFS)
     471           0 :                         return EROFS;                           /* really? XXX */
     472             : 
     473             :                 /*
     474             :                  * Write access to directory required to delete files.
     475             :                  */
     476           0 :                 error = VOP_ACCESS(vdp, VWRITE, cnp->cn_cred, cnp->cn_proc);
     477           0 :                 if (error)
     478           0 :                         return (error);
     479             : 
     480             :                 /*
     481             :                  * Return pointer to current entry in dp->i_offset.
     482             :                  * Save directory inode pointer in ndp->ni_dvp for dirremove().
     483             :                  */
     484           0 :                 if (dp->de_StartCluster == scn && isadir) {  /* "." */
     485           0 :                         vref(vdp);
     486           0 :                         *vpp = vdp;
     487           0 :                         return (0);
     488             :                 }
     489           0 :                 if ((error = deget(pmp, cluster, blkoff, &tdp)) != 0)
     490           0 :                         return (error);
     491           0 :                 *vpp = DETOV(tdp);
     492           0 :                 if (!lockparent) {
     493           0 :                         VOP_UNLOCK(vdp);
     494           0 :                         cnp->cn_flags |= PDIRUNLOCK;
     495           0 :                 }
     496           0 :                 return (0);
     497             :         }
     498             : 
     499             :         /*
     500             :          * If rewriting (RENAME), return the inode and the
     501             :          * information required to rewrite the present directory
     502             :          * Must get inode of directory entry to verify it's a
     503             :          * regular file, or empty directory.
     504             :          */
     505           0 :         if (nameiop == RENAME && wantparent &&
     506           0 :             (flags & ISLASTCN)) {
     507           0 :                 if (blkoff == MSDOSFSROOT_OFS)
     508           0 :                         return EROFS;                           /* really? XXX */
     509             : 
     510           0 :                 error = VOP_ACCESS(vdp, VWRITE, cnp->cn_cred, cnp->cn_proc);
     511           0 :                 if (error)
     512           0 :                         return (error);
     513             : 
     514             :                 /*
     515             :                  * Careful about locking second inode.
     516             :                  * This can only occur if the target is ".".
     517             :                  */
     518           0 :                 if (dp->de_StartCluster == scn && isadir)
     519           0 :                         return (EISDIR);
     520             : 
     521           0 :                 if ((error = deget(pmp, cluster, blkoff, &tdp)) != 0)
     522           0 :                         return (error);
     523           0 :                 *vpp = DETOV(tdp);
     524           0 :                 cnp->cn_flags |= SAVENAME;
     525           0 :                 if (!lockparent)
     526           0 :                         VOP_UNLOCK(vdp);
     527           0 :                 return (0);
     528             :         }
     529             : 
     530             :         /*
     531             :          * Step through the translation in the name.  We do not `vput' the
     532             :          * directory because we may need it again if a symbolic link
     533             :          * is relative to the current directory.  Instead we save it
     534             :          * unlocked as "pdp".  We must get the target inode before unlocking
     535             :          * the directory to insure that the inode will not be removed
     536             :          * before we get it.  We prevent deadlock by always fetching
     537             :          * inodes from the root, moving down the directory tree. Thus
     538             :          * when following backward pointers ".." we must unlock the
     539             :          * parent directory before getting the requested directory.
     540             :          * There is a potential race condition here if both the current
     541             :          * and parent directories are removed before the VFS_VGET for the
     542             :          * inode associated with ".." returns.  We hope that this occurs
     543             :          * infrequently since we cannot avoid this race condition without
     544             :          * implementing a sophisticated deadlock detection algorithm.
     545             :          * Note also that this simple deadlock detection scheme will not
     546             :          * work if the file system has any hard links other than ".."
     547             :          * that point backwards in the directory structure.
     548             :          */
     549             :         pdp = vdp;
     550           0 :         if (flags & ISDOTDOT) {
     551           0 :                 VOP_UNLOCK(pdp);        /* race to get the inode */
     552           0 :                 cnp->cn_flags |= PDIRUNLOCK;
     553           0 :                 if ((error = deget(pmp, cluster, blkoff, &tdp)) != 0) {
     554           0 :                         if (vn_lock(pdp, LK_EXCLUSIVE | LK_RETRY) == 0)
     555           0 :                                 cnp->cn_flags &= ~PDIRUNLOCK;
     556           0 :                         return (error);
     557             :                 }
     558           0 :                 if (lockparent && (flags & ISLASTCN)) {
     559           0 :                         if ((error = vn_lock(pdp, LK_EXCLUSIVE | LK_RETRY))) {
     560           0 :                                 vput(DETOV(tdp));
     561           0 :                                 return (error);
     562             :                         }
     563           0 :                         cnp->cn_flags &= ~PDIRUNLOCK;
     564           0 :                 }
     565           0 :                 *vpp = DETOV(tdp);
     566           0 :         } else if (dp->de_StartCluster == scn && isadir) {
     567           0 :                 vref(vdp);      /* we want ourself, ie "." */
     568           0 :                 *vpp = vdp;
     569           0 :         } else {
     570           0 :                 if ((error = deget(pmp, cluster, blkoff, &tdp)) != 0)
     571           0 :                         return (error);
     572           0 :                 if (!lockparent || !(flags & ISLASTCN)) {
     573           0 :                         VOP_UNLOCK(pdp);
     574           0 :                         cnp->cn_flags |= PDIRUNLOCK;
     575           0 :                 }
     576           0 :                 *vpp = DETOV(tdp);
     577             :         }
     578             : 
     579             :         /*
     580             :          * Insert name into cache if appropriate.
     581             :          */
     582           0 :         if (cnp->cn_flags & MAKEENTRY)
     583           0 :                 cache_enter(vdp, *vpp, cnp);
     584           0 :         return (0);
     585           0 : }
     586             : 
     587             : /*
     588             :  * dep  - directory entry to copy into the directory
     589             :  * ddep - directory to add to
     590             :  * depp - return the address of the denode for the created directory entry
     591             :  *        if depp != 0
     592             :  * cnp  - componentname needed for Win95 long filenames
     593             :  */
     594             : int
     595           0 : createde(struct denode *dep, struct denode *ddep, struct denode **depp,
     596             :     struct componentname *cnp)
     597             : {
     598             :         int error;
     599           0 :         uint32_t dirclust, diroffset;
     600             :         struct direntry *ndep;
     601           0 :         struct msdosfsmount *pmp = ddep->de_pmp;
     602           0 :         struct buf *bp;
     603           0 :         daddr_t bn;
     604           0 :         int blsize;
     605             : 
     606             : #ifdef MSDOSFS_DEBUG
     607             :         printf("createde(dep %p, ddep %p, depp %p, cnp %p)\n",
     608             :             dep, ddep, depp, cnp);
     609             : #endif
     610             : 
     611             :         /*
     612             :          * If no space left in the directory then allocate another cluster
     613             :          * and chain it onto the end of the file.  There is one exception
     614             :          * to this.  That is, if the root directory has no more space it
     615             :          * can NOT be expanded.  extendfile() checks for and fails attempts
     616             :          * to extend the root directory.  We just return an error in that
     617             :          * case.
     618             :          */
     619           0 :         if (ddep->de_fndoffset >= ddep->de_FileSize) {
     620           0 :                 diroffset = ddep->de_fndoffset + sizeof(struct direntry)
     621           0 :                     - ddep->de_FileSize;
     622           0 :                 dirclust = de_clcount(pmp, diroffset);
     623           0 :                 if ((error = extendfile(ddep, dirclust, 0, 0, DE_CLEAR)) != 0) {
     624           0 :                         (void)detrunc(ddep, ddep->de_FileSize, 0, NOCRED, NULL);
     625           0 :                         return error;
     626             :                 }
     627             : 
     628             :                 /*
     629             :                  * Update the size of the directory
     630             :                  */
     631           0 :                 ddep->de_FileSize += de_cn2off(pmp, dirclust);
     632           0 :         }
     633             : 
     634             :         /*
     635             :          * We just read in the cluster with space.  Copy the new directory
     636             :          * entry in.  Then write it to disk. NOTE:  DOS directories
     637             :          * do not get smaller as clusters are emptied.
     638             :          */
     639           0 :         error = pcbmap(ddep, de_cluster(pmp, ddep->de_fndoffset),
     640             :                        &bn, &dirclust, &blsize);
     641           0 :         if (error)
     642           0 :                 return error;
     643           0 :         diroffset = ddep->de_fndoffset;
     644           0 :         if (dirclust != MSDOSFSROOT)
     645           0 :                 diroffset &= pmp->pm_crbomask;
     646           0 :         if ((error = bread(pmp->pm_devvp, bn, blsize, &bp)) != 0) {
     647           0 :                 brelse(bp);
     648           0 :                 return error;
     649             :         }
     650           0 :         ndep = bptoep(pmp, bp, ddep->de_fndoffset);
     651             : 
     652           0 :         DE_EXTERNALIZE(ndep, dep);
     653             : 
     654             :         /*
     655             :          * Now write the Win95 long name
     656             :          */
     657           0 :         if (ddep->de_fndcnt > 0) {
     658           0 :                 u_int8_t chksum = winChksum(ndep->deName);
     659           0 :                 u_char *un = (u_char *)cnp->cn_nameptr;
     660           0 :                 int unlen = cnp->cn_namelen;
     661             :                 int cnt = 1;
     662             : 
     663           0 :                 while (--ddep->de_fndcnt >= 0) {
     664           0 :                         if (!(ddep->de_fndoffset & pmp->pm_crbomask)) {
     665           0 :                                 if ((error = bwrite(bp)) != 0)
     666           0 :                                         return error;
     667             : 
     668           0 :                                 ddep->de_fndoffset -= sizeof(struct direntry);
     669           0 :                                 error = pcbmap(ddep,
     670           0 :                                                de_cluster(pmp,
     671             :                                                           ddep->de_fndoffset),
     672             :                                                &bn, 0, &blsize);
     673           0 :                                 if (error)
     674           0 :                                         return error;
     675             : 
     676           0 :                                 error = bread(pmp->pm_devvp, bn, blsize, &bp);
     677           0 :                                 if (error) {
     678           0 :                                         brelse(bp);
     679           0 :                                         return error;
     680             :                                 }
     681           0 :                                 ndep = bptoep(pmp, bp, ddep->de_fndoffset);
     682           0 :                         } else {
     683           0 :                                 ndep--;
     684           0 :                                 ddep->de_fndoffset -= sizeof(struct direntry);
     685             :                         }
     686           0 :                         if (!unix2winfn(un, unlen, (struct winentry *)ndep, cnt++, chksum))
     687             :                                 break;
     688             :                 }
     689           0 :         }
     690             : 
     691           0 :         if ((error = bwrite(bp)) != 0)
     692           0 :                 return error;
     693             : 
     694             :         /*
     695             :          * If they want us to return with the denode gotten.
     696             :          */
     697           0 :         if (depp) {
     698           0 :                 if (dep->de_Attributes & ATTR_DIRECTORY) {
     699           0 :                         dirclust = dep->de_StartCluster;
     700           0 :                         if (FAT32(pmp) && dirclust == pmp->pm_rootdirblk)
     701           0 :                                 dirclust = MSDOSFSROOT;
     702           0 :                         if (dirclust == MSDOSFSROOT)
     703           0 :                                 diroffset = MSDOSFSROOT_OFS;
     704             :                         else
     705             :                                 diroffset = 0;
     706             :                 }
     707           0 :                 return deget(pmp, dirclust, diroffset, depp);
     708             :         }
     709             : 
     710           0 :         return 0;
     711           0 : }
     712             : 
     713             : /*
     714             :  * Be sure a directory is empty except for "." and "..". Return 1 if empty,
     715             :  * return 0 if not empty or error.
     716             :  */
     717             : int
     718           0 : dosdirempty(struct denode *dep)
     719             : {
     720           0 :         int blsize;
     721             :         int error;
     722             :         uint32_t cn;
     723           0 :         daddr_t bn;
     724           0 :         struct buf *bp;
     725           0 :         struct msdosfsmount *pmp = dep->de_pmp;
     726             :         struct direntry *dentp;
     727             : 
     728             :         /*
     729             :          * Since the filesize field in directory entries for a directory is
     730             :          * zero, we just have to feel our way through the directory until
     731             :          * we hit end of file.
     732             :          */
     733           0 :         for (cn = 0;; cn++) {
     734           0 :                 if ((error = pcbmap(dep, cn, &bn, 0, &blsize)) != 0) {
     735           0 :                         if (error == E2BIG)
     736           0 :                                 return (1);     /* it's empty */
     737           0 :                         return (0);
     738             :                 }
     739           0 :                 error = bread(pmp->pm_devvp, bn, blsize, &bp);
     740           0 :                 if (error) {
     741           0 :                         brelse(bp);
     742           0 :                         return (0);
     743             :                 }
     744           0 :                 for (dentp = (struct direntry *)bp->b_data;
     745           0 :                      (char *)dentp < bp->b_data + blsize;
     746           0 :                      dentp++) {
     747           0 :                         if (dentp->deName[0] != SLOT_DELETED &&
     748           0 :                             (dentp->deAttributes & ATTR_VOLUME) == 0) {
     749             :                                 /*
     750             :                                  * In dos directories an entry whose name
     751             :                                  * starts with SLOT_EMPTY (0) starts the
     752             :                                  * beginning of the unused part of the
     753             :                                  * directory, so we can just return that it
     754             :                                  * is empty.
     755             :                                  */
     756           0 :                                 if (dentp->deName[0] == SLOT_EMPTY) {
     757           0 :                                         brelse(bp);
     758           0 :                                         return (1);
     759             :                                 }
     760             :                                 /*
     761             :                                  * Any names other than "." and ".." in a
     762             :                                  * directory mean it is not empty.
     763             :                                  */
     764           0 :                                 if (bcmp(dentp->deName, ".          ", 11) &&
     765           0 :                                     bcmp(dentp->deName, "..         ", 11)) {
     766           0 :                                         brelse(bp);
     767             : #ifdef MSDOSFS_DEBUG
     768             :                                         printf("dosdirempty(): entry found %02x, %02x\n",
     769             :                                             dentp->deName[0], dentp->deName[1]);
     770             : #endif
     771           0 :                                         return (0);     /* not empty */
     772             :                                 }
     773             :                         }
     774             :                 }
     775           0 :                 brelse(bp);
     776             :         }
     777             :         /* NOTREACHED */
     778           0 : }
     779             : 
     780             : /*
     781             :  * Check to see if the directory described by target is in some
     782             :  * subdirectory of source.  This prevents something like the following from
     783             :  * succeeding and leaving a bunch or files and directories orphaned. mv
     784             :  * /a/b/c /a/b/c/d/e/f Where c and f are directories.
     785             :  *
     786             :  * source - the inode for /a/b/c
     787             :  * target - the inode for /a/b/c/d/e/f
     788             :  *
     789             :  * Returns 0 if target is NOT a subdirectory of source.
     790             :  * Otherwise returns a non-zero error number.
     791             :  * The target inode is always unlocked on return.
     792             :  */
     793             : int
     794           0 : doscheckpath(struct denode *source, struct denode *target)
     795             : {
     796             :         uint32_t scn;
     797             :         struct msdosfsmount *pmp;
     798             :         struct direntry *ep;
     799           0 :         struct denode *dep;
     800           0 :         struct buf *bp = NULL;
     801             :         int error = 0;
     802             : 
     803           0 :         dep = target;
     804           0 :         if ((target->de_Attributes & ATTR_DIRECTORY) == 0 ||
     805           0 :             (source->de_Attributes & ATTR_DIRECTORY) == 0) {
     806             :                 error = ENOTDIR;
     807           0 :                 goto out;
     808             :         }
     809           0 :         if (dep->de_StartCluster == source->de_StartCluster) {
     810             :                 error = EEXIST;
     811           0 :                 goto out;
     812             :         }
     813           0 :         if (dep->de_StartCluster == MSDOSFSROOT)
     814             :                 goto out;
     815           0 :         pmp = dep->de_pmp;
     816             : #ifdef  DIAGNOSTIC
     817           0 :         if (pmp != source->de_pmp)
     818           0 :                 panic("doscheckpath: source and target on different filesystems");
     819             : #endif
     820           0 :         if (FAT32(pmp) && dep->de_StartCluster == pmp->pm_rootdirblk)
     821             :                 goto out;
     822             : 
     823           0 :         for (;;) {
     824           0 :                 if ((dep->de_Attributes & ATTR_DIRECTORY) == 0) {
     825             :                         error = ENOTDIR;
     826           0 :                         break;
     827             :                 }
     828           0 :                 scn = dep->de_StartCluster;
     829           0 :                 error = bread(pmp->pm_devvp, cntobn(pmp, scn),
     830           0 :                               pmp->pm_bpcluster, &bp);
     831           0 :                 if (error)
     832             :                         break;
     833             : 
     834           0 :                 ep = (struct direntry *) bp->b_data + 1;
     835           0 :                 if ((ep->deAttributes & ATTR_DIRECTORY) == 0 ||
     836           0 :                     bcmp(ep->deName, "..         ", 11) != 0) {
     837             :                         error = ENOTDIR;
     838           0 :                         break;
     839             :                 }
     840           0 :                 scn = getushort(ep->deStartCluster);
     841           0 :                 if (FAT32(pmp))
     842           0 :                         scn |= getushort(ep->deHighClust) << 16;
     843             : 
     844           0 :                 if (scn == source->de_StartCluster) {
     845             :                         error = EINVAL;
     846           0 :                         break;
     847             :                 }
     848           0 :                 if (scn == MSDOSFSROOT)
     849             :                         break;
     850           0 :                 if (FAT32(pmp) && scn == pmp->pm_rootdirblk) {
     851             :                         /*
     852             :                          * scn should be 0 in this case,
     853             :                          * but we silently ignore the error.
     854             :                          */
     855             :                         break;
     856             :                 }
     857             : 
     858           0 :                 vput(DETOV(dep));
     859           0 :                 brelse(bp);
     860           0 :                 bp = NULL;
     861             :                 /* NOTE: deget() clears dep on error */
     862           0 :                 if ((error = deget(pmp, scn, 0, &dep)) != 0)
     863             :                         break;
     864             :         }
     865             : out:;
     866           0 :         if (bp)
     867           0 :                 brelse(bp);
     868           0 :         if (error == ENOTDIR)
     869           0 :                 printf("doscheckpath(): .. not a directory?\n");
     870           0 :         if (dep != NULL)
     871           0 :                 vput(DETOV(dep));
     872           0 :         return (error);
     873           0 : }
     874             : 
     875             : /*
     876             :  * Read in the disk block containing the directory entry (dirclu, dirofs)
     877             :  * and return the address of the buf header, and the address of the
     878             :  * directory entry within the block.
     879             :  */
     880             : int
     881           0 : readep(struct msdosfsmount *pmp, uint32_t dirclust, uint32_t diroffset,
     882             :     struct buf **bpp, struct direntry **epp)
     883             : {
     884             :         int error;
     885             :         daddr_t bn;
     886             :         int blsize;
     887             : 
     888           0 :         blsize = pmp->pm_bpcluster;
     889           0 :         if (dirclust == MSDOSFSROOT
     890           0 :             && de_blk(pmp, diroffset + blsize) > pmp->pm_rootdirsize)
     891           0 :                 blsize = de_bn2off(pmp, pmp->pm_rootdirsize) & pmp->pm_crbomask;
     892           0 :         bn = detobn(pmp, dirclust, diroffset);
     893           0 :         if ((error = bread(pmp->pm_devvp, bn, blsize, bpp)) != 0) {
     894           0 :                 brelse(*bpp);
     895           0 :                 *bpp = NULL;
     896           0 :                 return (error);
     897             :         }
     898           0 :         if (epp)
     899           0 :                 *epp = bptoep(pmp, *bpp, diroffset);
     900           0 :         return (0);
     901           0 : }
     902             : 
     903             : /*
     904             :  * Read in the disk block containing the directory entry dep came from and
     905             :  * return the address of the buf header, and the address of the directory
     906             :  * entry within the block.
     907             :  */
     908             : int
     909           0 : readde(struct denode *dep, struct buf **bpp, struct direntry **epp)
     910             : {
     911             : 
     912           0 :         return (readep(dep->de_pmp, dep->de_dirclust, dep->de_diroffset,
     913             :             bpp, epp));
     914             : }
     915             : 
     916             : /*
     917             :  * Remove a directory entry. At this point the file represented by the
     918             :  * directory entry to be removed is still full length until noone has it
     919             :  * open.  When the file no longer being used msdosfs_inactive() is called
     920             :  * and will truncate the file to 0 length.  When the vnode containing the
     921             :  * denode is needed for some other purpose by VFS it will call
     922             :  * msdosfs_reclaim() which will remove the denode from the denode cache.
     923             :  *
     924             :  * pdep - directory where the entry is removed
     925             :  * dep - file to be removed
     926             :  */
     927             : int
     928           0 : removede(struct denode *pdep, struct denode *dep)
     929             : {
     930             :         int error;
     931             :         struct direntry *ep;
     932           0 :         struct buf *bp;
     933           0 :         daddr_t bn;
     934           0 :         int blsize;
     935           0 :         struct msdosfsmount *pmp = pdep->de_pmp;
     936           0 :         uint32_t offset = pdep->de_fndoffset;
     937             : 
     938             : #ifdef MSDOSFS_DEBUG
     939             :         printf("removede(): filename %.11s, dep %p, offset %x\n",
     940             :             dep->de_Name, dep, offset);
     941             : #endif
     942             : 
     943           0 :         dep->de_refcnt--;
     944           0 :         offset += sizeof(struct direntry);
     945           0 :         do {
     946           0 :                 offset -= sizeof(struct direntry);
     947           0 :                 error = pcbmap(pdep, de_cluster(pmp, offset), &bn, 0, &blsize);
     948           0 :                 if (error)
     949           0 :                         return error;
     950           0 :                 error = bread(pmp->pm_devvp, bn, blsize, &bp);
     951           0 :                 if (error) {
     952           0 :                         brelse(bp);
     953           0 :                         return error;
     954             :                 }
     955           0 :                 ep = bptoep(pmp, bp, offset);
     956             :                 /*
     957             :                  * Check whether, if we came here the second time, i.e.
     958             :                  * when underflowing into the previous block, the last
     959             :                  * entry in this block is a longfilename entry, too.
     960             :                  */
     961           0 :                 if (ep->deAttributes != ATTR_WIN95
     962           0 :                     && offset != pdep->de_fndoffset) {
     963           0 :                         brelse(bp);
     964           0 :                         break;
     965             :                 }
     966           0 :                 offset += sizeof(struct direntry);
     967           0 :                 while (1) {
     968             :                         /*
     969             :                          * We are a bit aggressive here in that we delete any Win95
     970             :                          * entries preceding this entry, not just the ones we "own".
     971             :                          * Since these presumably aren't valid anyway,
     972             :                          * there should be no harm.
     973             :                          */
     974           0 :                         offset -= sizeof(struct direntry);
     975           0 :                         ep--->deName[0] = SLOT_DELETED;
     976           0 :                         if ((pmp->pm_flags & MSDOSFSMNT_NOWIN95)
     977           0 :                             || !(offset & pmp->pm_crbomask)
     978           0 :                             || ep->deAttributes != ATTR_WIN95)
     979             :                                 break;
     980             :                 }
     981           0 :                 if ((error = bwrite(bp)) != 0)
     982           0 :                         return error;
     983           0 :         } while (!(pmp->pm_flags & MSDOSFSMNT_NOWIN95)
     984           0 :             && !(offset & pmp->pm_crbomask)
     985           0 :             && offset);
     986           0 :         return 0;
     987           0 : }
     988             : 
     989             : /*
     990             :  * Create a unique DOS name in dvp
     991             :  */
     992             : int
     993           0 : uniqdosname(struct denode *dep, struct componentname *cnp, u_char *cp)
     994             : {
     995           0 :         struct msdosfsmount *pmp = dep->de_pmp;
     996             :         struct direntry *dentp;
     997             :         int gen;
     998           0 :         int blsize;
     999             :         uint32_t cn;
    1000           0 :         daddr_t bn;
    1001           0 :         struct buf *bp;
    1002             :         int error;
    1003             : 
    1004           0 :         for (gen = 1;; gen++) {
    1005             :                 /*
    1006             :                  * Generate DOS name with generation number
    1007             :                  */
    1008           0 :                 if (!unix2dosfn((u_char *)cnp->cn_nameptr, cp, cnp->cn_namelen, gen))
    1009           0 :                         return gen == 1 ? EINVAL : EEXIST;
    1010             : 
    1011             :                 /*
    1012             :                  * Now look for a dir entry with this exact name
    1013             :                  */
    1014           0 :                 for (cn = error = 0; !error; cn++) {
    1015           0 :                         if ((error = pcbmap(dep, cn, &bn, 0, &blsize)) != 0) {
    1016           0 :                                 if (error == E2BIG)     /* EOF reached and not found */
    1017           0 :                                         return 0;
    1018           0 :                                 return error;
    1019             :                         }
    1020           0 :                         error = bread(pmp->pm_devvp, bn, blsize, &bp);
    1021           0 :                         if (error) {
    1022           0 :                                 brelse(bp);
    1023           0 :                                 return error;
    1024             :                         }
    1025           0 :                         for (dentp = (struct direntry *)bp->b_data;
    1026           0 :                              (char *)dentp < bp->b_data + blsize;
    1027           0 :                              dentp++) {
    1028           0 :                                 if (dentp->deName[0] == SLOT_EMPTY) {
    1029             :                                         /*
    1030             :                                          * Last used entry and not found
    1031             :                                          */
    1032           0 :                                         brelse(bp);
    1033           0 :                                         return 0;
    1034             :                                 }
    1035             :                                 /*
    1036             :                                  * Ignore volume labels and Win95 entries
    1037             :                                  */
    1038           0 :                                 if (dentp->deAttributes & ATTR_VOLUME)
    1039             :                                         continue;
    1040           0 :                                 if (!bcmp(dentp->deName, cp, 11)) {
    1041             :                                         error = EEXIST;
    1042           0 :                                         break;
    1043             :                                 }
    1044             :                         }
    1045           0 :                         brelse(bp);
    1046             :                 }
    1047             :         }
    1048             : 
    1049             :         return (EEXIST);
    1050           0 : }
    1051             : 
    1052             : /*
    1053             :  * Find any Win'95 long filename entry in directory dep
    1054             :  */
    1055             : int
    1056           0 : findwin95(struct denode *dep)
    1057             : {
    1058           0 :         struct msdosfsmount *pmp = dep->de_pmp;
    1059             :         struct direntry *dentp;
    1060           0 :         int blsize;
    1061             :         uint32_t cn;
    1062           0 :         daddr_t bn;
    1063           0 :         struct buf *bp;
    1064             : 
    1065             :         /*
    1066             :          * Read through the directory looking for Win'95 entries
    1067             :          * Note: Error currently handled just as EOF                    XXX
    1068             :          */
    1069           0 :         for (cn = 0;; cn++) {
    1070           0 :                 if (pcbmap(dep, cn, &bn, 0, &blsize))
    1071           0 :                         return 0;
    1072           0 :                 if (bread(pmp->pm_devvp, bn, blsize, &bp)) {
    1073           0 :                         brelse(bp);
    1074           0 :                         return 0;
    1075             :                 }
    1076           0 :                 for (dentp = (struct direntry *)bp->b_data;
    1077           0 :                      (char *)dentp < bp->b_data + blsize;
    1078           0 :                      dentp++) {
    1079           0 :                         if (dentp->deName[0] == SLOT_EMPTY) {
    1080             :                                 /*
    1081             :                                  * Last used entry and not found
    1082             :                                  */
    1083           0 :                                 brelse(bp);
    1084           0 :                                 return 0;
    1085             :                         }
    1086           0 :                         if (dentp->deName[0] == SLOT_DELETED) {
    1087             :                                 /*
    1088             :                                  * Ignore deleted files
    1089             :                                  * Note: might be an indication of Win'95 anyway        XXX
    1090             :                                  */
    1091             :                                 continue;
    1092             :                         }
    1093           0 :                         if (dentp->deAttributes == ATTR_WIN95) {
    1094           0 :                                 brelse(bp);
    1095           0 :                                 return 1;
    1096             :                         }
    1097             :                 }
    1098           0 :                 brelse(bp);
    1099             :         }
    1100           0 : }

Generated by: LCOV version 1.13