150477Speter/* $FreeBSD$ */ 233548Sjkh/* $NetBSD: msdosfs_lookup.c,v 1.37 1997/11/17 15:36:54 ws Exp $ */ 32893Sdfr 42893Sdfr/*- 533548Sjkh * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank. 633548Sjkh * Copyright (C) 1994, 1995, 1997 TooLs GmbH. 72893Sdfr * All rights reserved. 82893Sdfr * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below). 92893Sdfr * 102893Sdfr * Redistribution and use in source and binary forms, with or without 112893Sdfr * modification, are permitted provided that the following conditions 122893Sdfr * are met: 132893Sdfr * 1. Redistributions of source code must retain the above copyright 142893Sdfr * notice, this list of conditions and the following disclaimer. 152893Sdfr * 2. Redistributions in binary form must reproduce the above copyright 162893Sdfr * notice, this list of conditions and the following disclaimer in the 172893Sdfr * documentation and/or other materials provided with the distribution. 182893Sdfr * 3. All advertising materials mentioning features or use of this software 192893Sdfr * must display the following acknowledgement: 202893Sdfr * This product includes software developed by TooLs GmbH. 212893Sdfr * 4. The name of TooLs GmbH may not be used to endorse or promote products 222893Sdfr * derived from this software without specific prior written permission. 232893Sdfr * 242893Sdfr * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR 252893Sdfr * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 262893Sdfr * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 272893Sdfr * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 282893Sdfr * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 292893Sdfr * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 302893Sdfr * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 312893Sdfr * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 322893Sdfr * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 332893Sdfr * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 342893Sdfr */ 35139776Simp/*- 362893Sdfr * Written by Paul Popelka (paulp@uts.amdahl.com) 378876Srgrimes * 382893Sdfr * You can do anything you want with this software, just don't say you wrote 392893Sdfr * it, and don't remove this notice. 408876Srgrimes * 412893Sdfr * This software is provided "as is". 428876Srgrimes * 432893Sdfr * The author supplies this software to be publicly redistributed on the 442893Sdfr * understanding that the author is not responsible for the correct 452893Sdfr * functioning of this software in any circumstances and is not liable for 462893Sdfr * any damages caused by this software. 478876Srgrimes * 482893Sdfr * October 1992 492893Sdfr */ 502893Sdfr 512893Sdfr#include <sys/param.h> 5248425Speter#include <sys/systm.h> 53171750Sbde#include <sys/buf.h> 54171750Sbde#include <sys/mount.h> 552893Sdfr#include <sys/namei.h> 562893Sdfr#include <sys/vnode.h> 572893Sdfr 5877162Sru#include <fs/msdosfs/bpb.h> 5977162Sru#include <fs/msdosfs/direntry.h> 6077162Sru#include <fs/msdosfs/denode.h> 6177162Sru#include <fs/msdosfs/fat.h> 62171750Sbde#include <fs/msdosfs/msdosfsmount.h> 632893Sdfr 64204474Skibstatic int msdosfs_lookup_(struct vnode *vdp, struct vnode **vpp, 65204474Skib struct componentname *cnp, u_int64_t *inum); 66204474Skib 67204474Skibint 68204474Skibmsdosfs_lookup(struct vop_cachedlookup_args *ap) 69204474Skib{ 70204474Skib 71204474Skib return (msdosfs_lookup_(ap->a_dvp, ap->a_vpp, ap->a_cnp, NULL)); 72204474Skib} 73204474Skib 74269165Skibstruct deget_dotdot { 75269165Skib u_long cluster; 76269165Skib int blkoff; 77269165Skib}; 78269165Skib 79269165Skibstatic int 80269165Skibmsdosfs_deget_dotdot(struct mount *mp, void *arg, int lkflags, 81269165Skib struct vnode **rvp) 82269165Skib{ 83269165Skib struct deget_dotdot *dd_arg; 84269165Skib struct denode *rdp; 85269165Skib struct msdosfsmount *pmp; 86269165Skib int error; 87269165Skib 88269165Skib pmp = VFSTOMSDOSFS(mp); 89269165Skib dd_arg = arg; 90269165Skib error = deget(pmp, dd_arg->cluster, dd_arg->blkoff, &rdp); 91269165Skib if (error == 0) 92269165Skib *rvp = DETOV(rdp); 93269165Skib return (error); 94269165Skib} 95269165Skib 962893Sdfr/* 972893Sdfr * When we search a directory the blocks containing directory entries are 982893Sdfr * read and examined. The directory entries contain information that would 992893Sdfr * normally be in the inode of a unix filesystem. This means that some of 1002893Sdfr * a directory's contents may also be in memory resident denodes (sort of 1012893Sdfr * an inode). This can cause problems if we are searching while some other 1022893Sdfr * process is modifying a directory. To prevent one process from accessing 1032893Sdfr * incompletely modified directory information we depend upon being the 10429286Sphk * sole owner of a directory block. bread/brelse provide this service. 1052893Sdfr * This being the case, when a process modifies a directory it must first 1062893Sdfr * acquire the disk block that contains the directory entry to be modified. 1072893Sdfr * Then update the disk block and the denode, and then write the disk block 1082893Sdfr * out to disk. This way disk blocks containing directory entries and in 1092893Sdfr * memory denode's will be in synch. 1102893Sdfr */ 111204474Skibstatic int 112204474Skibmsdosfs_lookup_(struct vnode *vdp, struct vnode **vpp, 113204474Skib struct componentname *cnp, u_int64_t *dd_inum) 1142893Sdfr{ 115172027Sbde struct mbnambuf nb; 1162893Sdfr daddr_t bn; 1172893Sdfr int error; 11833548Sjkh int slotcount; 11933548Sjkh int slotoffset = 0; 1202893Sdfr int frcn; 1212893Sdfr u_long cluster; 12233548Sjkh int blkoff; 1232893Sdfr int diroff; 12433548Sjkh int blsize; 1252893Sdfr int isadir; /* ~0 if found direntry is a directory */ 1262893Sdfr u_long scn; /* starting cluster number */ 1272893Sdfr struct vnode *pdp; 1282893Sdfr struct denode *dp; 1292893Sdfr struct denode *tdp; 1302893Sdfr struct msdosfsmount *pmp; 131238697Skevlo struct buf *bp = NULL; 1322893Sdfr struct direntry *dep = NULL; 133269165Skib struct deget_dotdot dd_arg; 1342893Sdfr u_char dosfilename[12]; 1352893Sdfr int flags = cnp->cn_flags; 1362893Sdfr int nameiop = cnp->cn_nameiop; 13733848Smsmith int unlen; 138204474Skib u_int64_t inode1; 1392893Sdfr 14033548Sjkh int wincnt = 1; 141134942Stjr int chksum = -1, chksum_ok; 14233548Sjkh int olddos = 1; 14333548Sjkh 1442893Sdfr#ifdef MSDOSFS_DEBUG 1452893Sdfr printf("msdosfs_lookup(): looking for %s\n", cnp->cn_nameptr); 1462893Sdfr#endif 1472893Sdfr dp = VTODE(vdp); 1482893Sdfr pmp = dp->de_pmp; 1492893Sdfr#ifdef MSDOSFS_DEBUG 15033548Sjkh printf("msdosfs_lookup(): vdp %p, dp %p, Attr %02x\n", 15133548Sjkh vdp, dp, dp->de_Attributes); 1522893Sdfr#endif 1532893Sdfr 154204474Skib restart: 155204675Skib if (vpp != NULL) 156204675Skib *vpp = NULL; 1572893Sdfr /* 1582893Sdfr * If they are going after the . or .. entry in the root directory, 1592893Sdfr * they won't find it. DOS filesystems don't have them in the root 1602893Sdfr * directory. So, we fake it. deget() is in on this scam too. 1612893Sdfr */ 162101308Sjeff if ((vdp->v_vflag & VV_ROOT) && cnp->cn_nameptr[0] == '.' && 1632893Sdfr (cnp->cn_namelen == 1 || 1642893Sdfr (cnp->cn_namelen == 2 && cnp->cn_nameptr[1] == '.'))) { 1652893Sdfr isadir = ATTR_DIRECTORY; 1662893Sdfr scn = MSDOSFSROOT; 1672893Sdfr#ifdef MSDOSFS_DEBUG 1682893Sdfr printf("msdosfs_lookup(): looking for . or .. in root directory\n"); 1692893Sdfr#endif 1702893Sdfr cluster = MSDOSFSROOT; 17133548Sjkh blkoff = MSDOSFSROOT_OFS; 1722893Sdfr goto foundroot; 1732893Sdfr } 1742893Sdfr 17533548Sjkh switch (unix2dosfn((const u_char *)cnp->cn_nameptr, dosfilename, 176120492Sfjoe cnp->cn_namelen, 0, pmp)) { 17733548Sjkh case 0: 17833548Sjkh return (EINVAL); 17933548Sjkh case 1: 18033548Sjkh break; 18133548Sjkh case 2: 18233548Sjkh wincnt = winSlotCnt((const u_char *)cnp->cn_nameptr, 183120492Sfjoe cnp->cn_namelen, pmp) + 1; 18433548Sjkh break; 18533548Sjkh case 3: 18633548Sjkh olddos = 0; 18733548Sjkh wincnt = winSlotCnt((const u_char *)cnp->cn_nameptr, 188120492Sfjoe cnp->cn_namelen, pmp) + 1; 18933548Sjkh break; 19033548Sjkh } 19136133Sdt if (pmp->pm_flags & MSDOSFSMNT_SHORTNAME) { 19233548Sjkh wincnt = 1; 19336133Sdt olddos = 1; 19436133Sdt } 19533848Smsmith unlen = winLenFixup(cnp->cn_nameptr, cnp->cn_namelen); 19633548Sjkh 1972893Sdfr /* 19833548Sjkh * Suppress search for slots unless creating 19933548Sjkh * file and at end of pathname, in which case 20033548Sjkh * we watch for a place to put the new file in 20133548Sjkh * case it doesn't already exist. 2022893Sdfr */ 20333548Sjkh slotcount = wincnt; 20433548Sjkh if ((nameiop == CREATE || nameiop == RENAME) && 20533548Sjkh (flags & ISLASTCN)) 20633548Sjkh slotcount = 0; 2072893Sdfr 2082893Sdfr#ifdef MSDOSFS_DEBUG 20933548Sjkh printf("msdosfs_lookup(): dos version of filename %s, length %ld\n", 21033548Sjkh dosfilename, cnp->cn_namelen); 2112893Sdfr#endif 2122893Sdfr /* 2132893Sdfr * Search the directory pointed at by vdp for the name pointed at 2142893Sdfr * by cnp->cn_nameptr. 2152893Sdfr */ 2162893Sdfr tdp = NULL; 217172027Sbde mbnambuf_init(&nb); 2182893Sdfr /* 2192893Sdfr * The outer loop ranges over the clusters that make up the 2202893Sdfr * directory. Note that the root directory is different from all 2212893Sdfr * other directories. It has a fixed number of blocks that are not 2222893Sdfr * part of the pool of allocatable clusters. So, we treat it a 2232893Sdfr * little differently. The root directory starts at "cluster" 0. 2242893Sdfr */ 22533548Sjkh diroff = 0; 2262893Sdfr for (frcn = 0;; frcn++) { 22733548Sjkh error = pcbmap(dp, frcn, &bn, &cluster, &blsize); 2283152Sphk if (error) { 2292893Sdfr if (error == E2BIG) 2302893Sdfr break; 23133548Sjkh return (error); 2322893Sdfr } 23333548Sjkh error = bread(pmp->pm_devvp, bn, blsize, NOCRED, &bp); 23433548Sjkh if (error) { 23533548Sjkh brelse(bp); 23633548Sjkh return (error); 23733548Sjkh } 23833548Sjkh for (blkoff = 0; blkoff < blsize; 23933548Sjkh blkoff += sizeof(struct direntry), 24033548Sjkh diroff += sizeof(struct direntry)) { 24133548Sjkh dep = (struct direntry *)(bp->b_data + blkoff); 2422893Sdfr /* 2432893Sdfr * If the slot is empty and we are still looking 2442893Sdfr * for an empty then remember this one. If the 2452893Sdfr * slot is not empty then check to see if it 2462893Sdfr * matches what we are looking for. If the slot 2472893Sdfr * has never been filled with anything, then the 2482893Sdfr * remainder of the directory has never been used, 2492893Sdfr * so there is no point in searching it. 2502893Sdfr */ 2512893Sdfr if (dep->deName[0] == SLOT_EMPTY || 2522893Sdfr dep->deName[0] == SLOT_DELETED) { 25333548Sjkh /* 25433548Sjkh * Drop memory of previous long matches 25533548Sjkh */ 25633548Sjkh chksum = -1; 257172027Sbde mbnambuf_init(&nb); 25833548Sjkh 25933548Sjkh if (slotcount < wincnt) { 26033548Sjkh slotcount++; 26133548Sjkh slotoffset = diroff; 2622893Sdfr } 2632893Sdfr if (dep->deName[0] == SLOT_EMPTY) { 2642893Sdfr brelse(bp); 2652893Sdfr goto notfound; 2662893Sdfr } 2672893Sdfr } else { 2682893Sdfr /* 26933548Sjkh * If there wasn't enough space for our winentries, 27033548Sjkh * forget about the empty space 27133548Sjkh */ 27233548Sjkh if (slotcount < wincnt) 27333548Sjkh slotcount = 0; 27433548Sjkh 27533548Sjkh /* 27633548Sjkh * Check for Win95 long filename entry 27733548Sjkh */ 27833548Sjkh if (dep->deAttributes == ATTR_WIN95) { 27933548Sjkh if (pmp->pm_flags & MSDOSFSMNT_SHORTNAME) 28033548Sjkh continue; 28133548Sjkh 282172027Sbde chksum = win2unixfn(&nb, 283172027Sbde (struct winentry *)dep, chksum, 284172027Sbde pmp); 28533548Sjkh continue; 28633548Sjkh } 28733548Sjkh 288172027Sbde chksum = winChkName(&nb, 289172027Sbde (const u_char *)cnp->cn_nameptr, unlen, 290172027Sbde chksum, pmp); 291120492Sfjoe if (chksum == -2) { 292120492Sfjoe chksum = -1; 293120492Sfjoe continue; 294120492Sfjoe } 295120492Sfjoe 29633548Sjkh /* 2972893Sdfr * Ignore volume labels (anywhere, not just 2982893Sdfr * the root directory). 2992893Sdfr */ 30033548Sjkh if (dep->deAttributes & ATTR_VOLUME) { 30133548Sjkh chksum = -1; 30233548Sjkh continue; 30333548Sjkh } 30433548Sjkh 30533548Sjkh /* 30633548Sjkh * Check for a checksum or name match 30733548Sjkh */ 308203827Skib chksum_ok = (chksum == winChksum(dep->deName)); 309134942Stjr if (!chksum_ok 31033548Sjkh && (!olddos || bcmp(dosfilename, dep->deName, 11))) { 31133548Sjkh chksum = -1; 31233548Sjkh continue; 31333548Sjkh } 3142893Sdfr#ifdef MSDOSFS_DEBUG 31533548Sjkh printf("msdosfs_lookup(): match blkoff %d, diroff %d\n", 31633548Sjkh blkoff, diroff); 3172893Sdfr#endif 31833548Sjkh /* 31933548Sjkh * Remember where this directory 32033548Sjkh * entry came from for whoever did 32133548Sjkh * this lookup. 32233548Sjkh */ 32333548Sjkh dp->de_fndoffset = diroff; 324134942Stjr if (chksum_ok && nameiop == RENAME) { 325134942Stjr /* 326134942Stjr * Target had correct long name 327134942Stjr * directory entries, reuse them 328134942Stjr * as needed. 329134942Stjr */ 330134942Stjr dp->de_fndcnt = wincnt - 1; 331134942Stjr } else { 332134942Stjr /* 333134942Stjr * Long name directory entries 334134942Stjr * not present or corrupt, can only 335134942Stjr * reuse dos directory entry. 336134942Stjr */ 337134942Stjr dp->de_fndcnt = 0; 338134942Stjr } 33933548Sjkh 34033548Sjkh goto found; 3412893Sdfr } 34233548Sjkh } /* for (blkoff = 0; .... */ 3432893Sdfr /* 3442893Sdfr * Release the buffer holding the directory cluster just 3452893Sdfr * searched. 3462893Sdfr */ 3472893Sdfr brelse(bp); 34833548Sjkh } /* for (frcn = 0; ; frcn++) */ 34933548Sjkh 35033548Sjkhnotfound: 3512893Sdfr /* 3522893Sdfr * We hold no disk buffers at this point. 3532893Sdfr */ 3542893Sdfr 3552893Sdfr /* 35633548Sjkh * Fixup the slot description to point to the place where 35733548Sjkh * we might put the new DOS direntry (putting the Win95 35833548Sjkh * long name entries before that) 35933548Sjkh */ 36033548Sjkh if (!slotcount) { 36133548Sjkh slotcount = 1; 36233548Sjkh slotoffset = diroff; 36333548Sjkh } 36433548Sjkh if (wincnt > slotcount) 36533548Sjkh slotoffset += sizeof(struct direntry) * (wincnt - slotcount); 36633548Sjkh 36733548Sjkh /* 3682893Sdfr * If we get here we didn't find the entry we were looking for. But 3692893Sdfr * that's ok if we are creating or renaming and are at the end of 3702893Sdfr * the pathname and the directory hasn't been removed. 3712893Sdfr */ 3722893Sdfr#ifdef MSDOSFS_DEBUG 37333548Sjkh printf("msdosfs_lookup(): op %d, refcnt %ld\n", 37433548Sjkh nameiop, dp->de_refcnt); 37533548Sjkh printf(" slotcount %d, slotoffset %d\n", 37633548Sjkh slotcount, slotoffset); 3772893Sdfr#endif 3782893Sdfr if ((nameiop == CREATE || nameiop == RENAME) && 3792893Sdfr (flags & ISLASTCN) && dp->de_refcnt != 0) { 38033548Sjkh /* 38133548Sjkh * Access for write is interpreted as allowing 38233548Sjkh * creation of files in the directory. 38333548Sjkh */ 38483366Sjulian error = VOP_ACCESS(vdp, VWRITE, cnp->cn_cred, cnp->cn_thread); 3858386Sbde if (error) 38633548Sjkh return (error); 38733548Sjkh /* 38833548Sjkh * Return an indication of where the new directory 38933548Sjkh * entry should be put. 39033548Sjkh */ 39133548Sjkh dp->de_fndoffset = slotoffset; 39233548Sjkh dp->de_fndcnt = wincnt - 1; 39333548Sjkh 39433548Sjkh /* 39533548Sjkh * We return with the directory locked, so that 39633548Sjkh * the parameters we set up above will still be 39733548Sjkh * valid if we actually decide to do a direnter(). 39833548Sjkh * We return ni_vp == NULL to indicate that the entry 39933548Sjkh * does not currently exist; we leave a pointer to 40033548Sjkh * the (locked) directory inode in ndp->ni_dvp. 40133548Sjkh * The pathname buffer is saved so that the name 40233548Sjkh * can be obtained later. 40333548Sjkh * 40433548Sjkh * NB - if the directory is unlocked, then this 40533548Sjkh * information cannot be used. 40633548Sjkh */ 4072893Sdfr cnp->cn_flags |= SAVENAME; 40833548Sjkh return (EJUSTRETURN); 4092893Sdfr } 410145174Sdas#if 0 4112893Sdfr /* 41233548Sjkh * Insert name into cache (as non-existent) if appropriate. 413145174Sdas * 414145174Sdas * XXX Negative caching is broken for msdosfs because the name 415145174Sdas * cache doesn't understand peculiarities such as case insensitivity 416145174Sdas * and 8.3 filenames. Hence, it may not invalidate all negative 417145174Sdas * entries if a file with this name is later created. 4182893Sdfr */ 419276500Skib if ((cnp->cn_flags & MAKEENTRY) != 0) 4202893Sdfr cache_enter(vdp, *vpp, cnp); 421145174Sdas#endif 42233548Sjkh return (ENOENT); 4232893Sdfr 42433548Sjkhfound: 4252893Sdfr /* 4262893Sdfr * NOTE: We still have the buffer with matched directory entry at 4272893Sdfr * this point. 4282893Sdfr */ 4292893Sdfr isadir = dep->deAttributes & ATTR_DIRECTORY; 4302893Sdfr scn = getushort(dep->deStartCluster); 43133548Sjkh if (FAT32(pmp)) { 43233548Sjkh scn |= getushort(dep->deHighClust) << 16; 43333548Sjkh if (scn == pmp->pm_rootdirblk) { 43433548Sjkh /* 43533548Sjkh * There should actually be 0 here. 43633548Sjkh * Just ignore the error. 43733548Sjkh */ 43833548Sjkh scn = MSDOSFSROOT; 43933548Sjkh } 44033548Sjkh } 4412893Sdfr 44233548Sjkh if (isadir) { 44333548Sjkh cluster = scn; 44433548Sjkh if (cluster == MSDOSFSROOT) 44533548Sjkh blkoff = MSDOSFSROOT_OFS; 44633548Sjkh else 44733548Sjkh blkoff = 0; 44833548Sjkh } else if (cluster == MSDOSFSROOT) 44933548Sjkh blkoff = diroff; 45033548Sjkh 4512893Sdfr /* 45233548Sjkh * Now release buf to allow deget to read the entry again. 45333548Sjkh * Reserving it here and giving it to deget could result 45433548Sjkh * in a deadlock. 45533548Sjkh */ 45633548Sjkh brelse(bp); 45733548Sjkh bp = 0; 45833548Sjkh 45933548Sjkhfoundroot: 46033548Sjkh /* 4612893Sdfr * If we entered at foundroot, then we are looking for the . or .. 4622893Sdfr * entry of the filesystems root directory. isadir and scn were 46333548Sjkh * setup before jumping here. And, bp is already null. 4642893Sdfr */ 46533548Sjkh if (FAT32(pmp) && scn == MSDOSFSROOT) 46633548Sjkh scn = pmp->pm_rootdirblk; 4672893Sdfr 468204474Skib if (dd_inum != NULL) { 469204474Skib *dd_inum = (uint64_t)pmp->pm_bpcluster * scn + blkoff; 470204474Skib return (0); 471204474Skib } 472204474Skib 4732893Sdfr /* 47433548Sjkh * If deleting, and at end of pathname, return 47533548Sjkh * parameters which can be used to remove file. 4762893Sdfr */ 4772893Sdfr if (nameiop == DELETE && (flags & ISLASTCN)) { 47833548Sjkh /* 47933548Sjkh * Don't allow deleting the root. 48033548Sjkh */ 48133548Sjkh if (blkoff == MSDOSFSROOT_OFS) 482220014Skib return (EBUSY); 48333548Sjkh 48433548Sjkh /* 48533548Sjkh * Write access to directory required to delete files. 48633548Sjkh */ 48783366Sjulian error = VOP_ACCESS(vdp, VWRITE, cnp->cn_cred, cnp->cn_thread); 48833548Sjkh if (error) 48933548Sjkh return (error); 49033548Sjkh 49133548Sjkh /* 49233548Sjkh * Return pointer to current entry in dp->i_offset. 49333548Sjkh * Save directory inode pointer in ndp->ni_dvp for dirremove(). 49433548Sjkh */ 4952893Sdfr if (dp->de_StartCluster == scn && isadir) { /* "." */ 4962893Sdfr VREF(vdp); 4972893Sdfr *vpp = vdp; 49833548Sjkh return (0); 4992893Sdfr } 50033548Sjkh error = deget(pmp, cluster, blkoff, &tdp); 50133548Sjkh if (error) 50233548Sjkh return (error); 5032893Sdfr *vpp = DETOV(tdp); 50433548Sjkh return (0); 5052893Sdfr } 5062893Sdfr 5072893Sdfr /* 50833548Sjkh * If rewriting (RENAME), return the inode and the 50933548Sjkh * information required to rewrite the present directory 51033548Sjkh * Must get inode of directory entry to verify it's a 51133548Sjkh * regular file, or empty directory. 5122893Sdfr */ 513144298Sjeff if (nameiop == RENAME && (flags & ISLASTCN)) { 51433548Sjkh if (blkoff == MSDOSFSROOT_OFS) 515220014Skib return (EBUSY); 51633548Sjkh 51783366Sjulian error = VOP_ACCESS(vdp, VWRITE, cnp->cn_cred, cnp->cn_thread); 51833548Sjkh if (error) 51933548Sjkh return (error); 52033548Sjkh 52133548Sjkh /* 52233548Sjkh * Careful about locking second inode. 52333548Sjkh * This can only occur if the target is ".". 52433548Sjkh */ 52533548Sjkh if (dp->de_StartCluster == scn && isadir) 52633548Sjkh return (EISDIR); 52733548Sjkh 52833548Sjkh if ((error = deget(pmp, cluster, blkoff, &tdp)) != 0) 52933548Sjkh return (error); 5302893Sdfr *vpp = DETOV(tdp); 5312893Sdfr cnp->cn_flags |= SAVENAME; 53233548Sjkh return (0); 5332893Sdfr } 5342893Sdfr 5352893Sdfr /* 53633548Sjkh * Step through the translation in the name. We do not `vput' the 53733548Sjkh * directory because we may need it again if a symbolic link 53833548Sjkh * is relative to the current directory. Instead we save it 53933548Sjkh * unlocked as "pdp". We must get the target inode before unlocking 54033548Sjkh * the directory to insure that the inode will not be removed 54133548Sjkh * before we get it. We prevent deadlock by always fetching 54233548Sjkh * inodes from the root, moving down the directory tree. Thus 54333548Sjkh * when following backward pointers ".." we must unlock the 54433548Sjkh * parent directory before getting the requested directory. 5452893Sdfr */ 5462893Sdfr pdp = vdp; 5472893Sdfr if (flags & ISDOTDOT) { 548269165Skib dd_arg.cluster = cluster; 549269165Skib dd_arg.blkoff = blkoff; 550269165Skib error = vn_vget_ino_gen(vdp, msdosfs_deget_dotdot, 551269165Skib &dd_arg, cnp->cn_lkflags, vpp); 552269165Skib if (error != 0) { 553204675Skib *vpp = NULL; 55433548Sjkh return (error); 555204675Skib } 556204474Skib /* 557204474Skib * Recheck that ".." still points to the inode we 558204474Skib * looked up before pdp lock was dropped. 559204474Skib */ 560204474Skib error = msdosfs_lookup_(pdp, NULL, cnp, &inode1); 561204474Skib if (error) { 562204474Skib vput(*vpp); 563204675Skib *vpp = NULL; 564204474Skib return (error); 565204474Skib } 566204474Skib if (VTODE(*vpp)->de_inode != inode1) { 567204474Skib vput(*vpp); 568204474Skib goto restart; 569204474Skib } 57033548Sjkh } else if (dp->de_StartCluster == scn && isadir) { 57133548Sjkh VREF(vdp); /* we want ourself, ie "." */ 5722893Sdfr *vpp = vdp; 5732893Sdfr } else { 57433548Sjkh if ((error = deget(pmp, cluster, blkoff, &tdp)) != 0) 57533548Sjkh return (error); 5762893Sdfr *vpp = DETOV(tdp); 5772893Sdfr } 5782893Sdfr 5792893Sdfr /* 58033548Sjkh * Insert name into cache if appropriate. 5812893Sdfr */ 5822893Sdfr if (cnp->cn_flags & MAKEENTRY) 5832893Sdfr cache_enter(vdp, *vpp, cnp); 58433548Sjkh return (0); 5852893Sdfr} 5862893Sdfr 5872893Sdfr/* 5882893Sdfr * dep - directory entry to copy into the directory 5892893Sdfr * ddep - directory to add to 5902893Sdfr * depp - return the address of the denode for the created directory entry 5912893Sdfr * if depp != 0 59233548Sjkh * cnp - componentname needed for Win95 long filenames 5932893Sdfr */ 5942893Sdfrint 59533548Sjkhcreatede(dep, ddep, depp, cnp) 5962893Sdfr struct denode *dep; 5972893Sdfr struct denode *ddep; 5982893Sdfr struct denode **depp; 59933548Sjkh struct componentname *cnp; 6002893Sdfr{ 6012893Sdfr int error; 6022893Sdfr u_long dirclust, diroffset; 6032893Sdfr struct direntry *ndep; 6042893Sdfr struct msdosfsmount *pmp = ddep->de_pmp; 6052893Sdfr struct buf *bp; 60633548Sjkh daddr_t bn; 60733548Sjkh int blsize; 6082893Sdfr 6092893Sdfr#ifdef MSDOSFS_DEBUG 61033548Sjkh printf("createde(dep %p, ddep %p, depp %p, cnp %p)\n", 61133548Sjkh dep, ddep, depp, cnp); 6122893Sdfr#endif 6132893Sdfr 6142893Sdfr /* 6152893Sdfr * If no space left in the directory then allocate another cluster 6162893Sdfr * and chain it onto the end of the file. There is one exception 6172893Sdfr * to this. That is, if the root directory has no more space it 6182893Sdfr * can NOT be expanded. extendfile() checks for and fails attempts 6192893Sdfr * to extend the root directory. We just return an error in that 6202893Sdfr * case. 6212893Sdfr */ 62233548Sjkh if (ddep->de_fndoffset >= ddep->de_FileSize) { 62333548Sjkh diroffset = ddep->de_fndoffset + sizeof(struct direntry) 62433548Sjkh - ddep->de_FileSize; 62533548Sjkh dirclust = de_clcount(pmp, diroffset); 62633548Sjkh error = extendfile(ddep, dirclust, 0, 0, DE_CLEAR); 62733548Sjkh if (error) { 628234605Strasz (void)detrunc(ddep, ddep->de_FileSize, 0, NOCRED); 6292893Sdfr return error; 63033548Sjkh } 63133548Sjkh 6322893Sdfr /* 6332893Sdfr * Update the size of the directory 6342893Sdfr */ 63533548Sjkh ddep->de_FileSize += de_cn2off(pmp, dirclust); 6362893Sdfr } 6372893Sdfr 6382893Sdfr /* 63933548Sjkh * We just read in the cluster with space. Copy the new directory 64033548Sjkh * entry in. Then write it to disk. NOTE: DOS directories 64133548Sjkh * do not get smaller as clusters are emptied. 6422893Sdfr */ 64333548Sjkh error = pcbmap(ddep, de_cluster(pmp, ddep->de_fndoffset), 64433548Sjkh &bn, &dirclust, &blsize); 64533548Sjkh if (error) 6462893Sdfr return error; 64733548Sjkh diroffset = ddep->de_fndoffset; 64833548Sjkh if (dirclust != MSDOSFSROOT) 64933548Sjkh diroffset &= pmp->pm_crbomask; 65033548Sjkh if ((error = bread(pmp->pm_devvp, bn, blsize, NOCRED, &bp)) != 0) { 65133548Sjkh brelse(bp); 65233548Sjkh return error; 6532893Sdfr } 65433548Sjkh ndep = bptoep(pmp, bp, ddep->de_fndoffset); 6552893Sdfr 65633548Sjkh DE_EXTERNALIZE(ndep, dep); 6572893Sdfr 65833548Sjkh /* 65933548Sjkh * Now write the Win95 long name 66033548Sjkh */ 66133548Sjkh if (ddep->de_fndcnt > 0) { 662203827Skib u_int8_t chksum = winChksum(ndep->deName); 66333548Sjkh const u_char *un = (const u_char *)cnp->cn_nameptr; 66433548Sjkh int unlen = cnp->cn_namelen; 66533548Sjkh int cnt = 1; 6662893Sdfr 66733548Sjkh while (--ddep->de_fndcnt >= 0) { 66833548Sjkh if (!(ddep->de_fndoffset & pmp->pm_crbomask)) { 669231998Skib if (DOINGASYNC(DETOV(ddep))) 670172798Sbde bdwrite(bp); 671172798Sbde else if ((error = bwrite(bp)) != 0) 67233548Sjkh return error; 6732893Sdfr 67433548Sjkh ddep->de_fndoffset -= sizeof(struct direntry); 67533548Sjkh error = pcbmap(ddep, 67633548Sjkh de_cluster(pmp, 67733548Sjkh ddep->de_fndoffset), 67833548Sjkh &bn, 0, &blsize); 67933548Sjkh if (error) 68033548Sjkh return error; 6812893Sdfr 68233548Sjkh error = bread(pmp->pm_devvp, bn, blsize, 68333548Sjkh NOCRED, &bp); 68433548Sjkh if (error) { 68533548Sjkh brelse(bp); 68633548Sjkh return error; 68733548Sjkh } 68833548Sjkh ndep = bptoep(pmp, bp, ddep->de_fndoffset); 68933548Sjkh } else { 69033548Sjkh ndep--; 69133548Sjkh ddep->de_fndoffset -= sizeof(struct direntry); 69233548Sjkh } 69333747Sache if (!unix2winfn(un, unlen, (struct winentry *)ndep, 694120492Sfjoe cnt++, chksum, pmp)) 69533548Sjkh break; 69633548Sjkh } 69733548Sjkh } 69833548Sjkh 699231998Skib if (DOINGASYNC(DETOV(ddep))) 700172798Sbde bdwrite(bp); 701172798Sbde else if ((error = bwrite(bp)) != 0) 70233548Sjkh return error; 70333548Sjkh 7042893Sdfr /* 70533548Sjkh * If they want us to return with the denode gotten. 7062893Sdfr */ 70733548Sjkh if (depp) { 70833548Sjkh if (dep->de_Attributes & ATTR_DIRECTORY) { 70933548Sjkh dirclust = dep->de_StartCluster; 71033548Sjkh if (FAT32(pmp) && dirclust == pmp->pm_rootdirblk) 71133548Sjkh dirclust = MSDOSFSROOT; 71233548Sjkh if (dirclust == MSDOSFSROOT) 71333548Sjkh diroffset = MSDOSFSROOT_OFS; 71433548Sjkh else 71533548Sjkh diroffset = 0; 71633548Sjkh } 71733548Sjkh return deget(pmp, dirclust, diroffset, depp); 71833548Sjkh } 7192893Sdfr 72033548Sjkh return 0; 7212893Sdfr} 7222893Sdfr 7232893Sdfr/* 7242893Sdfr * Be sure a directory is empty except for "." and "..". Return 1 if empty, 7252893Sdfr * return 0 if not empty or error. 7262893Sdfr */ 7272893Sdfrint 7282893Sdfrdosdirempty(dep) 7292893Sdfr struct denode *dep; 7302893Sdfr{ 73133548Sjkh int blsize; 7322893Sdfr int error; 7332893Sdfr u_long cn; 7342893Sdfr daddr_t bn; 7352893Sdfr struct buf *bp; 7362893Sdfr struct msdosfsmount *pmp = dep->de_pmp; 7372893Sdfr struct direntry *dentp; 7382893Sdfr 7392893Sdfr /* 7402893Sdfr * Since the filesize field in directory entries for a directory is 7412893Sdfr * zero, we just have to feel our way through the directory until 7422893Sdfr * we hit end of file. 7432893Sdfr */ 7442893Sdfr for (cn = 0;; cn++) { 74533548Sjkh if ((error = pcbmap(dep, cn, &bn, 0, &blsize)) != 0) { 74633548Sjkh if (error == E2BIG) 74733548Sjkh return (1); /* it's empty */ 74833548Sjkh return (0); 74933548Sjkh } 75033548Sjkh error = bread(pmp->pm_devvp, bn, blsize, NOCRED, &bp); 75133548Sjkh if (error) { 75233548Sjkh brelse(bp); 75333548Sjkh return (0); 75433548Sjkh } 75533548Sjkh for (dentp = (struct direntry *)bp->b_data; 75633548Sjkh (char *)dentp < bp->b_data + blsize; 75733548Sjkh dentp++) { 75833548Sjkh if (dentp->deName[0] != SLOT_DELETED && 75933548Sjkh (dentp->deAttributes & ATTR_VOLUME) == 0) { 7602893Sdfr /* 7612893Sdfr * In dos directories an entry whose name 7622893Sdfr * starts with SLOT_EMPTY (0) starts the 7632893Sdfr * beginning of the unused part of the 7642893Sdfr * directory, so we can just return that it 7652893Sdfr * is empty. 7662893Sdfr */ 7672893Sdfr if (dentp->deName[0] == SLOT_EMPTY) { 7682893Sdfr brelse(bp); 76933548Sjkh return (1); 7702893Sdfr } 7712893Sdfr /* 7722893Sdfr * Any names other than "." and ".." in a 7732893Sdfr * directory mean it is not empty. 7742893Sdfr */ 7752893Sdfr if (bcmp(dentp->deName, ". ", 11) && 7762893Sdfr bcmp(dentp->deName, ".. ", 11)) { 7772893Sdfr brelse(bp); 7782893Sdfr#ifdef MSDOSFS_DEBUG 77933548Sjkh printf("dosdirempty(): entry found %02x, %02x\n", 78033548Sjkh dentp->deName[0], dentp->deName[1]); 7812893Sdfr#endif 78233548Sjkh return (0); /* not empty */ 7832893Sdfr } 7842893Sdfr } 7852893Sdfr } 7862893Sdfr brelse(bp); 7872893Sdfr } 7882893Sdfr /* NOTREACHED */ 7892893Sdfr} 7902893Sdfr 7912893Sdfr/* 7922893Sdfr * Check to see if the directory described by target is in some 7932893Sdfr * subdirectory of source. This prevents something like the following from 7942893Sdfr * succeeding and leaving a bunch or files and directories orphaned. mv 7952893Sdfr * /a/b/c /a/b/c/d/e/f Where c and f are directories. 7962893Sdfr * 7972893Sdfr * source - the inode for /a/b/c 7982893Sdfr * target - the inode for /a/b/c/d/e/f 7992893Sdfr * 8002893Sdfr * Returns 0 if target is NOT a subdirectory of source. 8012893Sdfr * Otherwise returns a non-zero error number. 8022893Sdfr * The target inode is always unlocked on return. 8032893Sdfr */ 8042893Sdfrint 8052893Sdfrdoscheckpath(source, target) 8062893Sdfr struct denode *source; 8072893Sdfr struct denode *target; 8082893Sdfr{ 8092893Sdfr daddr_t scn; 8102893Sdfr struct msdosfsmount *pmp; 8112893Sdfr struct direntry *ep; 8122893Sdfr struct denode *dep; 8132893Sdfr struct buf *bp = NULL; 8142893Sdfr int error = 0; 8152893Sdfr 8162893Sdfr dep = target; 8172893Sdfr if ((target->de_Attributes & ATTR_DIRECTORY) == 0 || 8182893Sdfr (source->de_Attributes & ATTR_DIRECTORY) == 0) { 8192893Sdfr error = ENOTDIR; 8202893Sdfr goto out; 8212893Sdfr } 8222893Sdfr if (dep->de_StartCluster == source->de_StartCluster) { 8232893Sdfr error = EEXIST; 8242893Sdfr goto out; 8252893Sdfr } 8262893Sdfr if (dep->de_StartCluster == MSDOSFSROOT) 8272893Sdfr goto out; 82833548Sjkh pmp = dep->de_pmp; 82933548Sjkh#ifdef DIAGNOSTIC 83033548Sjkh if (pmp != source->de_pmp) 83133548Sjkh panic("doscheckpath: source and target on different filesystems"); 83233548Sjkh#endif 83333548Sjkh if (FAT32(pmp) && dep->de_StartCluster == pmp->pm_rootdirblk) 83433548Sjkh goto out; 83533548Sjkh 8362893Sdfr for (;;) { 8372893Sdfr if ((dep->de_Attributes & ATTR_DIRECTORY) == 0) { 8382893Sdfr error = ENOTDIR; 83933548Sjkh break; 8402893Sdfr } 8412893Sdfr scn = dep->de_StartCluster; 8422893Sdfr error = bread(pmp->pm_devvp, cntobn(pmp, scn), 84333548Sjkh pmp->pm_bpcluster, NOCRED, &bp); 84433548Sjkh if (error) 8452893Sdfr break; 84633548Sjkh 8472893Sdfr ep = (struct direntry *) bp->b_data + 1; 8482893Sdfr if ((ep->deAttributes & ATTR_DIRECTORY) == 0 || 8492893Sdfr bcmp(ep->deName, ".. ", 11) != 0) { 8502893Sdfr error = ENOTDIR; 8512893Sdfr break; 8522893Sdfr } 8532893Sdfr scn = getushort(ep->deStartCluster); 85433548Sjkh if (FAT32(pmp)) 85533548Sjkh scn |= getushort(ep->deHighClust) << 16; 85633548Sjkh 8572893Sdfr if (scn == source->de_StartCluster) { 8582893Sdfr error = EINVAL; 8592893Sdfr break; 8602893Sdfr } 8612893Sdfr if (scn == MSDOSFSROOT) 8622893Sdfr break; 86333548Sjkh if (FAT32(pmp) && scn == pmp->pm_rootdirblk) { 86433548Sjkh /* 86533548Sjkh * scn should be 0 in this case, 86633548Sjkh * but we silently ignore the error. 86733548Sjkh */ 86833548Sjkh break; 86933548Sjkh } 87033548Sjkh 8712893Sdfr vput(DETOV(dep)); 8722893Sdfr brelse(bp); 8732893Sdfr bp = NULL; 87433548Sjkh /* NOTE: deget() clears dep on error */ 87533548Sjkh if ((error = deget(pmp, scn, 0, &dep)) != 0) 8762893Sdfr break; 8772893Sdfr } 87833548Sjkhout:; 8792893Sdfr if (bp) 8802893Sdfr brelse(bp); 881227817Skib#ifdef MSDOSFS_DEBUG 8822893Sdfr if (error == ENOTDIR) 8832893Sdfr printf("doscheckpath(): .. not a directory?\n"); 884227817Skib#endif 8852893Sdfr if (dep != NULL) 8862893Sdfr vput(DETOV(dep)); 88733548Sjkh return (error); 8882893Sdfr} 8892893Sdfr 8902893Sdfr/* 8912893Sdfr * Read in the disk block containing the directory entry (dirclu, dirofs) 8922893Sdfr * and return the address of the buf header, and the address of the 8932893Sdfr * directory entry within the block. 8942893Sdfr */ 8952893Sdfrint 89633548Sjkhreadep(pmp, dirclust, diroffset, bpp, epp) 8972893Sdfr struct msdosfsmount *pmp; 89833548Sjkh u_long dirclust, diroffset; 8992893Sdfr struct buf **bpp; 9002893Sdfr struct direntry **epp; 9012893Sdfr{ 9022893Sdfr int error; 9032893Sdfr daddr_t bn; 90433548Sjkh int blsize; 9052893Sdfr 90633548Sjkh blsize = pmp->pm_bpcluster; 90733548Sjkh if (dirclust == MSDOSFSROOT 90833548Sjkh && de_blk(pmp, diroffset + blsize) > pmp->pm_rootdirsize) 90933548Sjkh blsize = de_bn2off(pmp, pmp->pm_rootdirsize) & pmp->pm_crbomask; 91033548Sjkh bn = detobn(pmp, dirclust, diroffset); 91133548Sjkh if ((error = bread(pmp->pm_devvp, bn, blsize, NOCRED, bpp)) != 0) { 91233548Sjkh brelse(*bpp); 9132893Sdfr *bpp = NULL; 91433548Sjkh return (error); 9152893Sdfr } 9162893Sdfr if (epp) 91733548Sjkh *epp = bptoep(pmp, *bpp, diroffset); 91833548Sjkh return (0); 9192893Sdfr} 9202893Sdfr 9212893Sdfr/* 9222893Sdfr * Read in the disk block containing the directory entry dep came from and 9232893Sdfr * return the address of the buf header, and the address of the directory 9242893Sdfr * entry within the block. 9252893Sdfr */ 9262893Sdfrint 9272893Sdfrreadde(dep, bpp, epp) 9282893Sdfr struct denode *dep; 9292893Sdfr struct buf **bpp; 9302893Sdfr struct direntry **epp; 9312893Sdfr{ 93233548Sjkh 93333548Sjkh return (readep(dep->de_pmp, dep->de_dirclust, dep->de_diroffset, 93433548Sjkh bpp, epp)); 9352893Sdfr} 93633548Sjkh 93733548Sjkh/* 93833548Sjkh * Remove a directory entry. At this point the file represented by the 93933548Sjkh * directory entry to be removed is still full length until noone has it 94033548Sjkh * open. When the file no longer being used msdosfs_inactive() is called 94133548Sjkh * and will truncate the file to 0 length. When the vnode containing the 94233548Sjkh * denode is needed for some other purpose by VFS it will call 94333548Sjkh * msdosfs_reclaim() which will remove the denode from the denode cache. 94433548Sjkh */ 94533548Sjkhint 94633548Sjkhremovede(pdep, dep) 94733548Sjkh struct denode *pdep; /* directory where the entry is removed */ 94833548Sjkh struct denode *dep; /* file to be removed */ 94933548Sjkh{ 95033548Sjkh int error; 95133548Sjkh struct direntry *ep; 95233548Sjkh struct buf *bp; 95333548Sjkh daddr_t bn; 95433548Sjkh int blsize; 95533548Sjkh struct msdosfsmount *pmp = pdep->de_pmp; 95633548Sjkh u_long offset = pdep->de_fndoffset; 95733548Sjkh 95833548Sjkh#ifdef MSDOSFS_DEBUG 95933548Sjkh printf("removede(): filename %s, dep %p, offset %08lx\n", 96033548Sjkh dep->de_Name, dep, offset); 96133548Sjkh#endif 96233548Sjkh 96333548Sjkh dep->de_refcnt--; 96433548Sjkh offset += sizeof(struct direntry); 96533548Sjkh do { 96633548Sjkh offset -= sizeof(struct direntry); 96733548Sjkh error = pcbmap(pdep, de_cluster(pmp, offset), &bn, 0, &blsize); 96833548Sjkh if (error) 96933548Sjkh return error; 97033548Sjkh error = bread(pmp->pm_devvp, bn, blsize, NOCRED, &bp); 97133548Sjkh if (error) { 97233548Sjkh brelse(bp); 97333548Sjkh return error; 97433548Sjkh } 97533548Sjkh ep = bptoep(pmp, bp, offset); 97633548Sjkh /* 97733548Sjkh * Check whether, if we came here the second time, i.e. 97833548Sjkh * when underflowing into the previous block, the last 97933548Sjkh * entry in this block is a longfilename entry, too. 98033548Sjkh */ 98133548Sjkh if (ep->deAttributes != ATTR_WIN95 98233548Sjkh && offset != pdep->de_fndoffset) { 98333548Sjkh brelse(bp); 98433548Sjkh break; 98533548Sjkh } 98633548Sjkh offset += sizeof(struct direntry); 98733548Sjkh while (1) { 98833548Sjkh /* 98933548Sjkh * We are a bit agressive here in that we delete any Win95 99033548Sjkh * entries preceding this entry, not just the ones we "own". 99133548Sjkh * Since these presumably aren't valid anyway, 99233548Sjkh * there should be no harm. 99333548Sjkh */ 99433548Sjkh offset -= sizeof(struct direntry); 99533548Sjkh ep--->deName[0] = SLOT_DELETED; 99633548Sjkh if ((pmp->pm_flags & MSDOSFSMNT_NOWIN95) 99733548Sjkh || !(offset & pmp->pm_crbomask) 99833548Sjkh || ep->deAttributes != ATTR_WIN95) 99933548Sjkh break; 100033548Sjkh } 1001231998Skib if (DOINGASYNC(DETOV(pdep))) 1002172798Sbde bdwrite(bp); 1003172798Sbde else if ((error = bwrite(bp)) != 0) 100433548Sjkh return error; 100533548Sjkh } while (!(pmp->pm_flags & MSDOSFSMNT_NOWIN95) 100633548Sjkh && !(offset & pmp->pm_crbomask) 100733548Sjkh && offset); 100833548Sjkh return 0; 100933548Sjkh} 101033548Sjkh 101133548Sjkh/* 101233548Sjkh * Create a unique DOS name in dvp 101333548Sjkh */ 101433548Sjkhint 101533548Sjkhuniqdosname(dep, cnp, cp) 101633548Sjkh struct denode *dep; 101733548Sjkh struct componentname *cnp; 101833548Sjkh u_char *cp; 101933548Sjkh{ 102033548Sjkh struct msdosfsmount *pmp = dep->de_pmp; 102133548Sjkh struct direntry *dentp; 102233548Sjkh int gen; 102333548Sjkh int blsize; 102433548Sjkh u_long cn; 102533548Sjkh daddr_t bn; 102633548Sjkh struct buf *bp; 102733548Sjkh int error; 102836133Sdt 102936154Sdt if (pmp->pm_flags & MSDOSFSMNT_SHORTNAME) 103036133Sdt return (unix2dosfn((const u_char *)cnp->cn_nameptr, cp, 1031120492Sfjoe cnp->cn_namelen, 0, pmp) ? 0 : EINVAL); 103233548Sjkh 103333548Sjkh for (gen = 1;; gen++) { 103433548Sjkh /* 103533548Sjkh * Generate DOS name with generation number 103633548Sjkh */ 103733548Sjkh if (!unix2dosfn((const u_char *)cnp->cn_nameptr, cp, 1038120492Sfjoe cnp->cn_namelen, gen, pmp)) 103933548Sjkh return gen == 1 ? EINVAL : EEXIST; 104033548Sjkh 104133548Sjkh /* 104233548Sjkh * Now look for a dir entry with this exact name 104333548Sjkh */ 104433548Sjkh for (cn = error = 0; !error; cn++) { 104533548Sjkh if ((error = pcbmap(dep, cn, &bn, 0, &blsize)) != 0) { 104633548Sjkh if (error == E2BIG) /* EOF reached and not found */ 104733548Sjkh return 0; 104833548Sjkh return error; 104933548Sjkh } 105033548Sjkh error = bread(pmp->pm_devvp, bn, blsize, NOCRED, &bp); 105133548Sjkh if (error) { 105233548Sjkh brelse(bp); 105333548Sjkh return error; 105433548Sjkh } 105533548Sjkh for (dentp = (struct direntry *)bp->b_data; 105633548Sjkh (char *)dentp < bp->b_data + blsize; 105733548Sjkh dentp++) { 105833548Sjkh if (dentp->deName[0] == SLOT_EMPTY) { 105933548Sjkh /* 106033548Sjkh * Last used entry and not found 106133548Sjkh */ 106233548Sjkh brelse(bp); 106333548Sjkh return 0; 106433548Sjkh } 106533548Sjkh /* 106633548Sjkh * Ignore volume labels and Win95 entries 106733548Sjkh */ 106833548Sjkh if (dentp->deAttributes & ATTR_VOLUME) 106933548Sjkh continue; 107033548Sjkh if (!bcmp(dentp->deName, cp, 11)) { 107133548Sjkh error = EEXIST; 107233548Sjkh break; 107333548Sjkh } 107433548Sjkh } 107533548Sjkh brelse(bp); 107633548Sjkh } 107733548Sjkh } 107833548Sjkh} 107933548Sjkh 108033548Sjkh/* 108133548Sjkh * Find any Win'95 long filename entry in directory dep 108233548Sjkh */ 108333548Sjkhint 108433548Sjkhfindwin95(dep) 108533548Sjkh struct denode *dep; 108633548Sjkh{ 108733548Sjkh struct msdosfsmount *pmp = dep->de_pmp; 108833548Sjkh struct direntry *dentp; 108942252Sdt int blsize, win95; 109033548Sjkh u_long cn; 109133548Sjkh daddr_t bn; 109233548Sjkh struct buf *bp; 109333548Sjkh 109442252Sdt win95 = 1; 109533548Sjkh /* 109633548Sjkh * Read through the directory looking for Win'95 entries 109733548Sjkh * Note: Error currently handled just as EOF XXX 109833548Sjkh */ 109933548Sjkh for (cn = 0;; cn++) { 110033548Sjkh if (pcbmap(dep, cn, &bn, 0, &blsize)) 110142252Sdt return (win95); 110233548Sjkh if (bread(pmp->pm_devvp, bn, blsize, NOCRED, &bp)) { 110333548Sjkh brelse(bp); 110442252Sdt return (win95); 110533548Sjkh } 110633548Sjkh for (dentp = (struct direntry *)bp->b_data; 110733548Sjkh (char *)dentp < bp->b_data + blsize; 110833548Sjkh dentp++) { 110933548Sjkh if (dentp->deName[0] == SLOT_EMPTY) { 111033548Sjkh /* 111133548Sjkh * Last used entry and not found 111233548Sjkh */ 111333548Sjkh brelse(bp); 111442252Sdt return (win95); 111533548Sjkh } 111633548Sjkh if (dentp->deName[0] == SLOT_DELETED) { 111733548Sjkh /* 111833548Sjkh * Ignore deleted files 111933548Sjkh * Note: might be an indication of Win'95 anyway XXX 112033548Sjkh */ 112133548Sjkh continue; 112233548Sjkh } 112333548Sjkh if (dentp->deAttributes == ATTR_WIN95) { 112433548Sjkh brelse(bp); 112533548Sjkh return 1; 112633548Sjkh } 112742252Sdt win95 = 0; 112833548Sjkh } 112933548Sjkh brelse(bp); 113033548Sjkh } 113133548Sjkh} 1132