[BACK]Return to msdosfs_lookup.c CVS log [TXT][DIR] Up to [local] / sys / msdosfs

Annotation of sys/msdosfs/msdosfs_lookup.c, Revision 1.1.1.1

1.1       nbrk        1: /*     $OpenBSD: msdosfs_lookup.c,v 1.19 2007/06/02 02:04:21 deraadt 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/mount.h>
                     57: #include <sys/dirent.h>
                     58:
                     59: #include <msdosfs/bpb.h>
                     60: #include <msdosfs/direntry.h>
                     61: #include <msdosfs/denode.h>
                     62: #include <msdosfs/msdosfsmount.h>
                     63: #include <msdosfs/fat.h>
                     64:
                     65: /*
                     66:  * When we search a directory the blocks containing directory entries are
                     67:  * read and examined.  The directory entries contain information that would
                     68:  * normally be in the inode of a unix filesystem.  This means that some of
                     69:  * a directory's contents may also be in memory resident denodes (sort of
                     70:  * an inode).  This can cause problems if we are searching while some other
                     71:  * process is modifying a directory.  To prevent one process from accessing
                     72:  * incompletely modified directory information we depend upon being the
                     73:  * sole owner of a directory block.  bread/brelse provide this service.
                     74:  * This being the case, when a process modifies a directory it must first
                     75:  * acquire the disk block that contains the directory entry to be modified.
                     76:  * Then update the disk block and the denode, and then write the disk block
                     77:  * out to disk.  This way disk blocks containing directory entries and in
                     78:  * memory denode's will be in synch.
                     79:  */
                     80: int
                     81: msdosfs_lookup(v)
                     82:        void *v;
                     83: {
                     84:        struct vop_lookup_args *ap = v;
                     85:        struct vnode *vdp = ap->a_dvp;
                     86:        struct vnode **vpp = ap->a_vpp;
                     87:        struct componentname *cnp = ap->a_cnp;
                     88:        struct proc *p = cnp->cn_proc;
                     89:        daddr64_t bn;
                     90:        int error;
                     91:        int lockparent;
                     92:        int wantparent;
                     93:        int slotcount;
                     94:        int slotoffset = 0;
                     95:        int frcn;
                     96:        uint32_t cluster;
                     97:        int blkoff;
                     98:        int diroff;
                     99:        int blsize;
                    100:        int isadir;             /* ~0 if found direntry is a directory   */
                    101:        uint32_t scn;           /* starting cluster number               */
                    102:        struct vnode *pdp;
                    103:        struct denode *dp;
                    104:        struct denode *tdp;
                    105:        struct msdosfsmount *pmp;
                    106:        struct buf *bp = 0;
                    107:        struct direntry *dep;
                    108:        u_char dosfilename[12];
                    109:        u_char *adjp;
                    110:        int adjlen;
                    111:        int flags;
                    112:        int nameiop = cnp->cn_nameiop;
                    113:        int wincnt = 1;
                    114:        int chksum = -1, chksum_ok;
                    115:        int olddos = 1;
                    116:
                    117:        cnp->cn_flags &= ~PDIRUNLOCK; /* XXX why this ?? */
                    118:        flags = cnp->cn_flags;
                    119:
                    120: #ifdef MSDOSFS_DEBUG
                    121:        printf("msdosfs_lookup(): looking for %s\n", cnp->cn_nameptr);
                    122: #endif
                    123:        dp = VTODE(vdp);
                    124:        pmp = dp->de_pmp;
                    125:        *vpp = NULL;
                    126:        lockparent = flags & LOCKPARENT;
                    127:        wantparent = flags & (LOCKPARENT | WANTPARENT);
                    128: #ifdef MSDOSFS_DEBUG
                    129:        printf("msdosfs_lookup(): vdp %08x, dp %08x, Attr %02x\n",
                    130:            vdp, dp, dp->de_Attributes);
                    131: #endif
                    132:
                    133:        /*
                    134:         * Check accessiblity of directory.
                    135:         */
                    136:        if ((dp->de_Attributes & ATTR_DIRECTORY) == 0)
                    137:                return (ENOTDIR);
                    138:        if ((error = VOP_ACCESS(vdp, VEXEC, cnp->cn_cred, cnp->cn_proc)) != 0)
                    139:                return (error);
                    140:
                    141:        /*
                    142:         * We now have a segment name to search for, and a directory to search.
                    143:         *
                    144:         * Before tediously performing a linear scan of the directory,
                    145:         * check the name cache to see if the directory/name pair
                    146:         * we are looking for is known already.
                    147:         */
                    148:        if ((error = cache_lookup(vdp, vpp, cnp)) >= 0)
                    149:                return (error);
                    150:
                    151:        /*
                    152:         * If they are going after the . or .. entry in the root directory,
                    153:         * they won't find it.  DOS filesystems don't have them in the root
                    154:         * directory.  So, we fake it. deget() is in on this scam too.
                    155:         */
                    156:        if ((vdp->v_flag & VROOT) && cnp->cn_nameptr[0] == '.' &&
                    157:            (cnp->cn_namelen == 1 ||
                    158:                (cnp->cn_namelen == 2 && cnp->cn_nameptr[1] == '.'))) {
                    159:                isadir = ATTR_DIRECTORY;
                    160:                scn = MSDOSFSROOT;
                    161: #ifdef MSDOSFS_DEBUG
                    162:                printf("msdosfs_lookup(): looking for . or .. in root directory\n");
                    163: #endif
                    164:                cluster = MSDOSFSROOT;
                    165:                blkoff = MSDOSFSROOT_OFS;
                    166:                goto foundroot;
                    167:        }
                    168:
                    169:        switch (unix2dosfn((u_char *)cnp->cn_nameptr, dosfilename, cnp->cn_namelen, 0)) {
                    170:        case 0:
                    171:                return (EINVAL);
                    172:        case 1:
                    173:                break;
                    174:        case 2:
                    175:                wincnt = winSlotCnt((u_char *)cnp->cn_nameptr, cnp->cn_namelen) + 1;
                    176:                break;
                    177:        case 3:
                    178:                olddos = 0;
                    179:                wincnt = winSlotCnt((u_char *)cnp->cn_nameptr, cnp->cn_namelen) + 1;
                    180:                break;
                    181:        }
                    182:        if (pmp->pm_flags & MSDOSFSMNT_SHORTNAME)
                    183:                wincnt = 1;
                    184:
                    185:        /*
                    186:         * Suppress search for slots unless creating
                    187:         * file and at end of pathname, in which case
                    188:         * we watch for a place to put the new file in
                    189:         * case it doesn't already exist.
                    190:         */
                    191:        slotcount = wincnt;
                    192:        if ((nameiop == CREATE || nameiop == RENAME) &&
                    193:            (flags & ISLASTCN))
                    194:                slotcount = 0;
                    195:
                    196: #ifdef MSDOSFS_DEBUG
                    197:        printf("msdosfs_lookup(): dos version of filename %s, length %d\n",
                    198:            dosfilename, cnp->cn_namelen);
                    199: #endif
                    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:        adjp = cnp->cn_nameptr;
                    214:        adjlen = cnp->cn_namelen;
                    215:
                    216:        for (adjp += adjlen; adjlen > 0; adjlen--)
                    217:                if (*--adjp != ' ' && *adjp != '.')
                    218:                        break;
                    219:
                    220:        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:        for (frcn = 0;; frcn++) {
                    230:                if ((error = pcbmap(dp, frcn, &bn, &cluster, &blsize)) != 0) {
                    231:                        if (error == E2BIG)
                    232:                                break;
                    233:                        return (error);
                    234:                }
                    235:                error = bread(pmp->pm_devvp, bn, blsize, NOCRED, &bp);
                    236:                if (error) {
                    237:                        brelse(bp);
                    238:                        return (error);
                    239:                }
                    240:                for (blkoff = 0; blkoff < blsize;
                    241:                     blkoff += sizeof(struct direntry),
                    242:                     diroff += sizeof(struct direntry)) {
                    243:                        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:                        if (dep->deName[0] == SLOT_EMPTY ||
                    254:                            dep->deName[0] == SLOT_DELETED) {
                    255:                                /*
                    256:                                 * Drop memory of previous long matches
                    257:                                 */
                    258:                                chksum = -1;
                    259:
                    260:                                if (slotcount < wincnt) {
                    261:                                        slotcount++;
                    262:                                        slotoffset = diroff;
                    263:                                }
                    264:                                if (dep->deName[0] == SLOT_EMPTY) {
                    265:                                        brelse(bp);
                    266:                                        goto notfound;
                    267:                                }
                    268:                        } else {
                    269:                                /*
                    270:                                 * If there wasn't enough space for our winentries,
                    271:                                 * forget about the empty space
                    272:                                 */
                    273:                                if (slotcount < wincnt)
                    274:                                        slotcount = 0;
                    275:
                    276:                                /*
                    277:                                 * Check for Win95 long filename entry
                    278:                                 */
                    279:                                if (dep->deAttributes == ATTR_WIN95) {
                    280:                                        if (pmp->pm_flags & MSDOSFSMNT_SHORTNAME)
                    281:                                                continue;
                    282:
                    283:                                        chksum = winChkName((u_char *)cnp->cn_nameptr,
                    284:                                                            adjlen,
                    285:                                                            (struct winentry *)dep,
                    286:                                                            chksum);
                    287:                                        continue;
                    288:                                }
                    289:
                    290:                                /*
                    291:                                 * Ignore volume labels (anywhere, not just
                    292:                                 * the root directory).
                    293:                                 */
                    294:                                if (dep->deAttributes & ATTR_VOLUME) {
                    295:                                        chksum = -1;
                    296:                                        continue;
                    297:                                }
                    298:
                    299:                                /*
                    300:                                 * Check for a checksum or name match
                    301:                                 */
                    302:                                chksum_ok = (chksum == winChksum(dep->deName));
                    303:                                if (!chksum_ok
                    304:                                    && (!olddos || bcmp(dosfilename, dep->deName, 11))) {
                    305:                                        chksum = -1;
                    306:                                        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:                                dp->de_fndoffset = diroff;
                    318:                                if (chksum_ok && nameiop == RENAME) {
                    319:                                        /*
                    320:                                         * Target had correct long name
                    321:                                         * directory entries, reuse them as
                    322:                                         * needed.
                    323:                                         */
                    324:                                        dp->de_fndcnt = wincnt - 1;
                    325:                                } else {
                    326:                                        /*
                    327:                                         * Long name directory entries not
                    328:                                         * present or corrupt, can only reuse
                    329:                                         * dos directory entry.
                    330:                                         */
                    331:                                        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:                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:        if (!slotcount) {
                    354:                slotcount = 1;
                    355:                slotoffset = diroff;
                    356:        }
                    357:        if (wincnt > slotcount)
                    358:                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 %d\n",
                    367:            nameiop, dp->de_refcnt);
                    368:        printf("               slotcount %d, slotoffset %d\n",
                    369:            slotcount, slotoffset);
                    370: #endif
                    371:        if ((nameiop == CREATE || nameiop == RENAME) &&
                    372:            (flags & ISLASTCN) && dp->de_refcnt != 0) {
                    373:                /*
                    374:                 * Access for write is interpreted as allowing
                    375:                 * creation of files in the directory.
                    376:                 */
                    377:                error = VOP_ACCESS(vdp, VWRITE, cnp->cn_cred, cnp->cn_proc);
                    378:                if (error)
                    379:                        return (error);
                    380:                /*
                    381:                 * Return an indication of where the new directory
                    382:                 * entry should be put.
                    383:                 */
                    384:                dp->de_fndoffset = slotoffset;
                    385:                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:                cnp->cn_flags |= SAVENAME;
                    401:                if (!lockparent) {
                    402:                        VOP_UNLOCK(vdp, 0, p);
                    403:                        cnp->cn_flags |= PDIRUNLOCK;
                    404:                }
                    405:                return (EJUSTRETURN);
                    406:        }
                    407:        /*
                    408:         * Insert name into cache (as non-existent) if appropriate.
                    409:         */
                    410:        if ((cnp->cn_flags & MAKEENTRY) && nameiop != CREATE)
                    411:                cache_enter(vdp, *vpp, cnp);
                    412:        return (ENOENT);
                    413:
                    414: found:;
                    415:        /*
                    416:         * NOTE:  We still have the buffer with matched directory entry at
                    417:         * this point.
                    418:         */
                    419:        isadir = dep->deAttributes & ATTR_DIRECTORY;
                    420:        scn = getushort(dep->deStartCluster);
                    421:        if (FAT32(pmp)) {
                    422:                scn |= getushort(dep->deHighClust) << 16;
                    423:                if (scn == pmp->pm_rootdirblk) {
                    424:                        /*
                    425:                         * There should actually be 0 here.
                    426:                         * Just ignore the error.
                    427:                         */
                    428:                        scn = MSDOSFSROOT;
                    429:                }
                    430:        }
                    431:
                    432:        if (cluster == MSDOSFSROOT)
                    433:                blkoff = diroff;
                    434:
                    435:        if (isadir) {
                    436:                cluster = scn;
                    437:                if (cluster == MSDOSFSROOT)
                    438:                        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:        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:        if (FAT32(pmp) && scn == MSDOSFSROOT)
                    457:                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:        if (nameiop == DELETE && (flags & ISLASTCN)) {
                    467:                /*
                    468:                 * Don't allow deleting the root.
                    469:                 */
                    470:                if (blkoff == MSDOSFSROOT_OFS)
                    471:                        return EROFS;                           /* really? XXX */
                    472:
                    473:                /*
                    474:                 * Write access to directory required to delete files.
                    475:                 */
                    476:                error = VOP_ACCESS(vdp, VWRITE, cnp->cn_cred, cnp->cn_proc);
                    477:                if (error)
                    478:                        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:                if (dp->de_StartCluster == scn && isadir) {     /* "." */
                    485:                        VREF(vdp);
                    486:                        *vpp = vdp;
                    487:                        return (0);
                    488:                }
                    489:                if ((error = deget(pmp, cluster, blkoff, &tdp)) != 0)
                    490:                        return (error);
                    491:                *vpp = DETOV(tdp);
                    492:                if (!lockparent) {
                    493:                        VOP_UNLOCK(vdp, 0, p);
                    494:                        cnp->cn_flags |= PDIRUNLOCK;
                    495:                }
                    496:                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:        if (nameiop == RENAME && wantparent &&
                    506:            (flags & ISLASTCN)) {
                    507:                if (blkoff == MSDOSFSROOT_OFS)
                    508:                        return EROFS;                           /* really? XXX */
                    509:
                    510:                error = VOP_ACCESS(vdp, VWRITE, cnp->cn_cred, cnp->cn_proc);
                    511:                if (error)
                    512:                        return (error);
                    513:
                    514:                /*
                    515:                 * Careful about locking second inode.
                    516:                 * This can only occur if the target is ".".
                    517:                 */
                    518:                if (dp->de_StartCluster == scn && isadir)
                    519:                        return (EISDIR);
                    520:
                    521:                if ((error = deget(pmp, cluster, blkoff, &tdp)) != 0)
                    522:                        return (error);
                    523:                *vpp = DETOV(tdp);
                    524:                cnp->cn_flags |= SAVENAME;
                    525:                if (!lockparent)
                    526:                        VOP_UNLOCK(vdp, 0, p);
                    527:                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:        if (flags & ISDOTDOT) {
                    551:                VOP_UNLOCK(pdp, 0, p);  /* race to get the inode */
                    552:                cnp->cn_flags |= PDIRUNLOCK;
                    553:                if ((error = deget(pmp, cluster, blkoff, &tdp)) != 0) {
                    554:                        if (vn_lock(pdp, LK_EXCLUSIVE | LK_RETRY, p) == 0)
                    555:                                cnp->cn_flags &= ~PDIRUNLOCK;
                    556:                        return (error);
                    557:                }
                    558:                if (lockparent && (flags & ISLASTCN)) {
                    559:                        if ((error = vn_lock(pdp, LK_EXCLUSIVE | LK_RETRY,
                    560:                            p))) {
                    561:                                vput(DETOV(tdp));
                    562:                                return (error);
                    563:                        }
                    564:                        cnp->cn_flags &= ~PDIRUNLOCK;
                    565:                }
                    566:                *vpp = DETOV(tdp);
                    567:        } else if (dp->de_StartCluster == scn && isadir) {
                    568:                VREF(vdp);      /* we want ourself, ie "." */
                    569:                *vpp = vdp;
                    570:        } else {
                    571:                if ((error = deget(pmp, cluster, blkoff, &tdp)) != 0)
                    572:                        return (error);
                    573:                if (!lockparent || !(flags & ISLASTCN)) {
                    574:                        VOP_UNLOCK(pdp, 0, p);
                    575:                        cnp->cn_flags |= PDIRUNLOCK;
                    576:                }
                    577:                *vpp = DETOV(tdp);
                    578:        }
                    579:
                    580:        /*
                    581:         * Insert name into cache if appropriate.
                    582:         */
                    583:        if (cnp->cn_flags & MAKEENTRY)
                    584:                cache_enter(vdp, *vpp, cnp);
                    585:        return (0);
                    586: }
                    587:
                    588: /*
                    589:  * dep  - directory entry to copy into the directory
                    590:  * ddep - directory to add to
                    591:  * depp - return the address of the denode for the created directory entry
                    592:  *       if depp != 0
                    593:  * cnp  - componentname needed for Win95 long filenames
                    594:  */
                    595: int
                    596: createde(dep, ddep, depp, cnp)
                    597:        struct denode *dep;
                    598:        struct denode *ddep;
                    599:        struct denode **depp;
                    600:        struct componentname *cnp;
                    601: {
                    602:        int error;
                    603:        uint32_t dirclust, diroffset;
                    604:        struct direntry *ndep;
                    605:        struct msdosfsmount *pmp = ddep->de_pmp;
                    606:        struct buf *bp;
                    607:        daddr64_t bn;
                    608:        int blsize;
                    609:
                    610: #ifdef MSDOSFS_DEBUG
                    611:        printf("createde(dep %08x, ddep %08x, depp %08x, cnp %08x)\n",
                    612:            dep, ddep, depp, cnp);
                    613: #endif
                    614:
                    615:        /*
                    616:         * If no space left in the directory then allocate another cluster
                    617:         * and chain it onto the end of the file.  There is one exception
                    618:         * to this.  That is, if the root directory has no more space it
                    619:         * can NOT be expanded.  extendfile() checks for and fails attempts
                    620:         * to extend the root directory.  We just return an error in that
                    621:         * case.
                    622:         */
                    623:        if (ddep->de_fndoffset >= ddep->de_FileSize) {
                    624:                diroffset = ddep->de_fndoffset + sizeof(struct direntry)
                    625:                    - ddep->de_FileSize;
                    626:                dirclust = de_clcount(pmp, diroffset);
                    627:                if ((error = extendfile(ddep, dirclust, 0, 0, DE_CLEAR)) != 0) {
                    628:                        (void)detrunc(ddep, ddep->de_FileSize, 0, NOCRED, NULL);
                    629:                        return error;
                    630:                }
                    631:
                    632:                /*
                    633:                 * Update the size of the directory
                    634:                 */
                    635:                ddep->de_FileSize += de_cn2off(pmp, dirclust);
                    636:        }
                    637:
                    638:        /*
                    639:         * We just read in the cluster with space.  Copy the new directory
                    640:         * entry in.  Then write it to disk. NOTE:  DOS directories
                    641:         * do not get smaller as clusters are emptied.
                    642:         */
                    643:        error = pcbmap(ddep, de_cluster(pmp, ddep->de_fndoffset),
                    644:                       &bn, &dirclust, &blsize);
                    645:        if (error)
                    646:                return error;
                    647:        diroffset = ddep->de_fndoffset;
                    648:        if (dirclust != MSDOSFSROOT)
                    649:                diroffset &= pmp->pm_crbomask;
                    650:        if ((error = bread(pmp->pm_devvp, bn, blsize, NOCRED, &bp)) != 0) {
                    651:                brelse(bp);
                    652:                return error;
                    653:        }
                    654:        ndep = bptoep(pmp, bp, ddep->de_fndoffset);
                    655:
                    656:        DE_EXTERNALIZE(ndep, dep);
                    657:
                    658:        /*
                    659:         * Now write the Win95 long name
                    660:         */
                    661:        if (ddep->de_fndcnt > 0) {
                    662:                u_int8_t chksum = winChksum(ndep->deName);
                    663:                u_char *un = (u_char *)cnp->cn_nameptr;
                    664:                int unlen = cnp->cn_namelen;
                    665:                int cnt = 1;
                    666:
                    667:                while (--ddep->de_fndcnt >= 0) {
                    668:                        if (!(ddep->de_fndoffset & pmp->pm_crbomask)) {
                    669:                                if ((error = bwrite(bp)) != 0)
                    670:                                        return error;
                    671:
                    672:                                ddep->de_fndoffset -= sizeof(struct direntry);
                    673:                                error = pcbmap(ddep,
                    674:                                               de_cluster(pmp,
                    675:                                                          ddep->de_fndoffset),
                    676:                                               &bn, 0, &blsize);
                    677:                                if (error)
                    678:                                        return error;
                    679:
                    680:                                error = bread(pmp->pm_devvp, bn, blsize,
                    681:                                              NOCRED, &bp);
                    682:                                if (error) {
                    683:                                        brelse(bp);
                    684:                                        return error;
                    685:                                }
                    686:                                ndep = bptoep(pmp, bp, ddep->de_fndoffset);
                    687:                        } else {
                    688:                                ndep--;
                    689:                                ddep->de_fndoffset -= sizeof(struct direntry);
                    690:                        }
                    691:                        if (!unix2winfn(un, unlen, (struct winentry *)ndep, cnt++, chksum))
                    692:                                break;
                    693:                }
                    694:        }
                    695:
                    696:        if ((error = bwrite(bp)) != 0)
                    697:                return error;
                    698:
                    699:        /*
                    700:         * If they want us to return with the denode gotten.
                    701:         */
                    702:        if (depp) {
                    703:                if (dep->de_Attributes & ATTR_DIRECTORY) {
                    704:                        dirclust = dep->de_StartCluster;
                    705:                        if (FAT32(pmp) && dirclust == pmp->pm_rootdirblk)
                    706:                                dirclust = MSDOSFSROOT;
                    707:                        if (dirclust == MSDOSFSROOT)
                    708:                                diroffset = MSDOSFSROOT_OFS;
                    709:                        else
                    710:                                diroffset = 0;
                    711:                }
                    712:                return deget(pmp, dirclust, diroffset, depp);
                    713:        }
                    714:
                    715:        return 0;
                    716: }
                    717:
                    718: /*
                    719:  * Be sure a directory is empty except for "." and "..". Return 1 if empty,
                    720:  * return 0 if not empty or error.
                    721:  */
                    722: int
                    723: dosdirempty(dep)
                    724:        struct denode *dep;
                    725: {
                    726:        int blsize;
                    727:        int error;
                    728:        uint32_t cn;
                    729:        daddr64_t bn;
                    730:        struct buf *bp;
                    731:        struct msdosfsmount *pmp = dep->de_pmp;
                    732:        struct direntry *dentp;
                    733:
                    734:        /*
                    735:         * Since the filesize field in directory entries for a directory is
                    736:         * zero, we just have to feel our way through the directory until
                    737:         * we hit end of file.
                    738:         */
                    739:        for (cn = 0;; cn++) {
                    740:                if ((error = pcbmap(dep, cn, &bn, 0, &blsize)) != 0) {
                    741:                        if (error == E2BIG)
                    742:                                return (1);     /* it's empty */
                    743:                        return (0);
                    744:                }
                    745:                error = bread(pmp->pm_devvp, bn, blsize, NOCRED, &bp);
                    746:                if (error) {
                    747:                        brelse(bp);
                    748:                        return (0);
                    749:                }
                    750:                for (dentp = (struct direntry *)bp->b_data;
                    751:                     (char *)dentp < bp->b_data + blsize;
                    752:                     dentp++) {
                    753:                        if (dentp->deName[0] != SLOT_DELETED &&
                    754:                            (dentp->deAttributes & ATTR_VOLUME) == 0) {
                    755:                                /*
                    756:                                 * In dos directories an entry whose name
                    757:                                 * starts with SLOT_EMPTY (0) starts the
                    758:                                 * beginning of the unused part of the
                    759:                                 * directory, so we can just return that it
                    760:                                 * is empty.
                    761:                                 */
                    762:                                if (dentp->deName[0] == SLOT_EMPTY) {
                    763:                                        brelse(bp);
                    764:                                        return (1);
                    765:                                }
                    766:                                /*
                    767:                                 * Any names other than "." and ".." in a
                    768:                                 * directory mean it is not empty.
                    769:                                 */
                    770:                                if (bcmp(dentp->deName, ".          ", 11) &&
                    771:                                    bcmp(dentp->deName, "..         ", 11)) {
                    772:                                        brelse(bp);
                    773: #ifdef MSDOSFS_DEBUG
                    774:                                        printf("dosdirempty(): entry found %02x, %02x\n",
                    775:                                            dentp->deName[0], dentp->deName[1]);
                    776: #endif
                    777:                                        return (0);     /* not empty */
                    778:                                }
                    779:                        }
                    780:                }
                    781:                brelse(bp);
                    782:        }
                    783:        /* NOTREACHED */
                    784: }
                    785:
                    786: /*
                    787:  * Check to see if the directory described by target is in some
                    788:  * subdirectory of source.  This prevents something like the following from
                    789:  * succeeding and leaving a bunch or files and directories orphaned. mv
                    790:  * /a/b/c /a/b/c/d/e/f Where c and f are directories.
                    791:  *
                    792:  * source - the inode for /a/b/c
                    793:  * target - the inode for /a/b/c/d/e/f
                    794:  *
                    795:  * Returns 0 if target is NOT a subdirectory of source.
                    796:  * Otherwise returns a non-zero error number.
                    797:  * The target inode is always unlocked on return.
                    798:  */
                    799: int
                    800: doscheckpath(source, target)
                    801:        struct denode *source;
                    802:        struct denode *target;
                    803: {
                    804:        uint32_t scn;
                    805:        struct msdosfsmount *pmp;
                    806:        struct direntry *ep;
                    807:        struct denode *dep;
                    808:        struct buf *bp = NULL;
                    809:        int error = 0;
                    810:
                    811:        dep = target;
                    812:        if ((target->de_Attributes & ATTR_DIRECTORY) == 0 ||
                    813:            (source->de_Attributes & ATTR_DIRECTORY) == 0) {
                    814:                error = ENOTDIR;
                    815:                goto out;
                    816:        }
                    817:        if (dep->de_StartCluster == source->de_StartCluster) {
                    818:                error = EEXIST;
                    819:                goto out;
                    820:        }
                    821:        if (dep->de_StartCluster == MSDOSFSROOT)
                    822:                goto out;
                    823:        pmp = dep->de_pmp;
                    824: #ifdef DIAGNOSTIC
                    825:        if (pmp != source->de_pmp)
                    826:                panic("doscheckpath: source and target on different filesystems");
                    827: #endif
                    828:        if (FAT32(pmp) && dep->de_StartCluster == pmp->pm_rootdirblk)
                    829:                goto out;
                    830:
                    831:        for (;;) {
                    832:                if ((dep->de_Attributes & ATTR_DIRECTORY) == 0) {
                    833:                        error = ENOTDIR;
                    834:                        break;
                    835:                }
                    836:                scn = dep->de_StartCluster;
                    837:                error = bread(pmp->pm_devvp, cntobn(pmp, scn),
                    838:                              pmp->pm_bpcluster, NOCRED, &bp);
                    839:                if (error)
                    840:                        break;
                    841:
                    842:                ep = (struct direntry *) bp->b_data + 1;
                    843:                if ((ep->deAttributes & ATTR_DIRECTORY) == 0 ||
                    844:                    bcmp(ep->deName, "..         ", 11) != 0) {
                    845:                        error = ENOTDIR;
                    846:                        break;
                    847:                }
                    848:                scn = getushort(ep->deStartCluster);
                    849:                if (FAT32(pmp))
                    850:                        scn |= getushort(ep->deHighClust) << 16;
                    851:
                    852:                if (scn == source->de_StartCluster) {
                    853:                        error = EINVAL;
                    854:                        break;
                    855:                }
                    856:                if (scn == MSDOSFSROOT)
                    857:                        break;
                    858:                if (FAT32(pmp) && scn == pmp->pm_rootdirblk) {
                    859:                        /*
                    860:                         * scn should be 0 in this case,
                    861:                         * but we silently ignore the error.
                    862:                         */
                    863:                        break;
                    864:                }
                    865:
                    866:                vput(DETOV(dep));
                    867:                brelse(bp);
                    868:                bp = NULL;
                    869:                /* NOTE: deget() clears dep on error */
                    870:                if ((error = deget(pmp, scn, 0, &dep)) != 0)
                    871:                        break;
                    872:        }
                    873: out:;
                    874:        if (bp)
                    875:                brelse(bp);
                    876:        if (error == ENOTDIR)
                    877:                printf("doscheckpath(): .. not a directory?\n");
                    878:        if (dep != NULL)
                    879:                vput(DETOV(dep));
                    880:        return (error);
                    881: }
                    882:
                    883: /*
                    884:  * Read in the disk block containing the directory entry (dirclu, dirofs)
                    885:  * and return the address of the buf header, and the address of the
                    886:  * directory entry within the block.
                    887:  */
                    888: int
                    889: readep(pmp, dirclust, diroffset, bpp, epp)
                    890:        struct msdosfsmount *pmp;
                    891:        uint32_t dirclust, diroffset;
                    892:        struct buf **bpp;
                    893:        struct direntry **epp;
                    894: {
                    895:        int error;
                    896:        daddr64_t bn;
                    897:        int blsize;
                    898:        uint32_t boff;
                    899:
                    900:        boff = diroffset & ~pmp->pm_crbomask;
                    901:        blsize = pmp->pm_bpcluster;
                    902:        if (dirclust == MSDOSFSROOT
                    903:            && de_blk(pmp, diroffset + blsize) > pmp->pm_rootdirsize)
                    904:                blsize = de_bn2off(pmp, pmp->pm_rootdirsize) & pmp->pm_crbomask;
                    905:        bn = detobn(pmp, dirclust, diroffset);
                    906:        if ((error = bread(pmp->pm_devvp, bn, blsize, NOCRED, bpp)) != 0) {
                    907:                brelse(*bpp);
                    908:                *bpp = NULL;
                    909:                return (error);
                    910:        }
                    911:        if (epp)
                    912:                *epp = bptoep(pmp, *bpp, diroffset);
                    913:        return (0);
                    914: }
                    915:
                    916: /*
                    917:  * Read in the disk block containing the directory entry dep came from and
                    918:  * return the address of the buf header, and the address of the directory
                    919:  * entry within the block.
                    920:  */
                    921: int
                    922: readde(dep, bpp, epp)
                    923:        struct denode *dep;
                    924:        struct buf **bpp;
                    925:        struct direntry **epp;
                    926: {
                    927:
                    928:        return (readep(dep->de_pmp, dep->de_dirclust, dep->de_diroffset,
                    929:            bpp, epp));
                    930: }
                    931:
                    932: /*
                    933:  * Remove a directory entry. At this point the file represented by the
                    934:  * directory entry to be removed is still full length until noone has it
                    935:  * open.  When the file no longer being used msdosfs_inactive() is called
                    936:  * and will truncate the file to 0 length.  When the vnode containing the
                    937:  * denode is needed for some other purpose by VFS it will call
                    938:  * msdosfs_reclaim() which will remove the denode from the denode cache.
                    939:  */
                    940: int
                    941: removede(pdep, dep)
                    942:        struct denode *pdep;    /* directory where the entry is removed */
                    943:        struct denode *dep;     /* file to be removed */
                    944: {
                    945:        int error;
                    946:        struct direntry *ep;
                    947:        struct buf *bp;
                    948:        daddr64_t bn;
                    949:        int blsize;
                    950:        struct msdosfsmount *pmp = pdep->de_pmp;
                    951:        uint32_t offset = pdep->de_fndoffset;
                    952:
                    953: #ifdef MSDOSFS_DEBUG
                    954:        printf("removede(): filename %s, dep %08x, offset %08x\n",
                    955:            dep->de_Name, dep, offset);
                    956: #endif
                    957:
                    958:        dep->de_refcnt--;
                    959:        offset += sizeof(struct direntry);
                    960:        do {
                    961:                offset -= sizeof(struct direntry);
                    962:                error = pcbmap(pdep, de_cluster(pmp, offset), &bn, 0, &blsize);
                    963:                if (error)
                    964:                        return error;
                    965:                error = bread(pmp->pm_devvp, bn, blsize, NOCRED, &bp);
                    966:                if (error) {
                    967:                        brelse(bp);
                    968:                        return error;
                    969:                }
                    970:                ep = bptoep(pmp, bp, offset);
                    971:                /*
                    972:                 * Check whether, if we came here the second time, i.e.
                    973:                 * when underflowing into the previous block, the last
                    974:                 * entry in this block is a longfilename entry, too.
                    975:                 */
                    976:                if (ep->deAttributes != ATTR_WIN95
                    977:                    && offset != pdep->de_fndoffset) {
                    978:                        brelse(bp);
                    979:                        break;
                    980:                }
                    981:                offset += sizeof(struct direntry);
                    982:                while (1) {
                    983:                        /*
                    984:                         * We are a bit agressive here in that we delete any Win95
                    985:                         * entries preceding this entry, not just the ones we "own".
                    986:                         * Since these presumably aren't valid anyway,
                    987:                         * there should be no harm.
                    988:                         */
                    989:                        offset -= sizeof(struct direntry);
                    990:                        ep--->deName[0] = SLOT_DELETED;
                    991:                        if ((pmp->pm_flags & MSDOSFSMNT_NOWIN95)
                    992:                            || !(offset & pmp->pm_crbomask)
                    993:                            || ep->deAttributes != ATTR_WIN95)
                    994:                                break;
                    995:                }
                    996:                if ((error = bwrite(bp)) != 0)
                    997:                        return error;
                    998:        } while (!(pmp->pm_flags & MSDOSFSMNT_NOWIN95)
                    999:            && !(offset & pmp->pm_crbomask)
                   1000:            && offset);
                   1001:        return 0;
                   1002: }
                   1003:
                   1004: /*
                   1005:  * Create a unique DOS name in dvp
                   1006:  */
                   1007: int
                   1008: uniqdosname(dep, cnp, cp)
                   1009:        struct denode *dep;
                   1010:        struct componentname *cnp;
                   1011:        u_char *cp;
                   1012: {
                   1013:        struct msdosfsmount *pmp = dep->de_pmp;
                   1014:        struct direntry *dentp;
                   1015:        int gen;
                   1016:        int blsize;
                   1017:        uint32_t cn;
                   1018:        daddr64_t bn;
                   1019:        struct buf *bp;
                   1020:        int error;
                   1021:
                   1022:        for (gen = 1;; gen++) {
                   1023:                /*
                   1024:                 * Generate DOS name with generation number
                   1025:                 */
                   1026:                if (!unix2dosfn((u_char *)cnp->cn_nameptr, cp, cnp->cn_namelen, gen))
                   1027:                        return gen == 1 ? EINVAL : EEXIST;
                   1028:
                   1029:                /*
                   1030:                 * Now look for a dir entry with this exact name
                   1031:                 */
                   1032:                for (cn = error = 0; !error; cn++) {
                   1033:                        if ((error = pcbmap(dep, cn, &bn, 0, &blsize)) != 0) {
                   1034:                                if (error == E2BIG)     /* EOF reached and not found */
                   1035:                                        return 0;
                   1036:                                return error;
                   1037:                        }
                   1038:                        error = bread(pmp->pm_devvp, bn, blsize, NOCRED, &bp);
                   1039:                        if (error) {
                   1040:                                brelse(bp);
                   1041:                                return error;
                   1042:                        }
                   1043:                        for (dentp = (struct direntry *)bp->b_data;
                   1044:                             (char *)dentp < bp->b_data + blsize;
                   1045:                             dentp++) {
                   1046:                                if (dentp->deName[0] == SLOT_EMPTY) {
                   1047:                                        /*
                   1048:                                         * Last used entry and not found
                   1049:                                         */
                   1050:                                        brelse(bp);
                   1051:                                        return 0;
                   1052:                                }
                   1053:                                /*
                   1054:                                 * Ignore volume labels and Win95 entries
                   1055:                                 */
                   1056:                                if (dentp->deAttributes & ATTR_VOLUME)
                   1057:                                        continue;
                   1058:                                if (!bcmp(dentp->deName, cp, 11)) {
                   1059:                                        error = EEXIST;
                   1060:                                        break;
                   1061:                                }
                   1062:                        }
                   1063:                        brelse(bp);
                   1064:                }
                   1065:        }
                   1066:
                   1067:        return (EEXIST);
                   1068: }
                   1069:
                   1070: /*
                   1071:  * Find any Win'95 long filename entry in directory dep
                   1072:  */
                   1073: int
                   1074: findwin95(dep)
                   1075:        struct denode *dep;
                   1076: {
                   1077:        struct msdosfsmount *pmp = dep->de_pmp;
                   1078:        struct direntry *dentp;
                   1079:        int blsize;
                   1080:        uint32_t cn;
                   1081:        daddr64_t bn;
                   1082:        struct buf *bp;
                   1083:
                   1084:        /*
                   1085:         * Read through the directory looking for Win'95 entries
                   1086:         * Note: Error currently handled just as EOF                    XXX
                   1087:         */
                   1088:        for (cn = 0;; cn++) {
                   1089:                if (pcbmap(dep, cn, &bn, 0, &blsize))
                   1090:                        return 0;
                   1091:                if (bread(pmp->pm_devvp, bn, blsize, NOCRED, &bp)) {
                   1092:                        brelse(bp);
                   1093:                        return 0;
                   1094:                }
                   1095:                for (dentp = (struct direntry *)bp->b_data;
                   1096:                     (char *)dentp < bp->b_data + blsize;
                   1097:                     dentp++) {
                   1098:                        if (dentp->deName[0] == SLOT_EMPTY) {
                   1099:                                /*
                   1100:                                 * Last used entry and not found
                   1101:                                 */
                   1102:                                brelse(bp);
                   1103:                                return 0;
                   1104:                        }
                   1105:                        if (dentp->deName[0] == SLOT_DELETED) {
                   1106:                                /*
                   1107:                                 * Ignore deleted files
                   1108:                                 * Note: might be an indication of Win'95 anyway        XXX
                   1109:                                 */
                   1110:                                continue;
                   1111:                        }
                   1112:                        if (dentp->deAttributes == ATTR_WIN95) {
                   1113:                                brelse(bp);
                   1114:                                return 1;
                   1115:                        }
                   1116:                }
                   1117:                brelse(bp);
                   1118:        }
                   1119: }

CVSweb