msdosfs_lookup.c revision 144298
150477Speter/* $FreeBSD: head/sys/fs/msdosfs/msdosfs_lookup.c 144298 2005-03-29 13:04:00Z jeff $ */
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>
532893Sdfr#include <sys/namei.h>
5460041Sphk#include <sys/bio.h>
552893Sdfr#include <sys/buf.h>
562893Sdfr#include <sys/vnode.h>
572893Sdfr#include <sys/mount.h>
582893Sdfr
5977162Sru#include <fs/msdosfs/bpb.h>
60120492Sfjoe#include <fs/msdosfs/msdosfsmount.h>
6177162Sru#include <fs/msdosfs/direntry.h>
6277162Sru#include <fs/msdosfs/denode.h>
6377162Sru#include <fs/msdosfs/fat.h>
642893Sdfr
652893Sdfr/*
662893Sdfr * When we search a directory the blocks containing directory entries are
672893Sdfr * read and examined.  The directory entries contain information that would
682893Sdfr * normally be in the inode of a unix filesystem.  This means that some of
692893Sdfr * a directory's contents may also be in memory resident denodes (sort of
702893Sdfr * an inode).  This can cause problems if we are searching while some other
712893Sdfr * process is modifying a directory.  To prevent one process from accessing
722893Sdfr * incompletely modified directory information we depend upon being the
7329286Sphk * sole owner of a directory block.  bread/brelse provide this service.
742893Sdfr * This being the case, when a process modifies a directory it must first
752893Sdfr * acquire the disk block that contains the directory entry to be modified.
762893Sdfr * Then update the disk block and the denode, and then write the disk block
772893Sdfr * out to disk.  This way disk blocks containing directory entries and in
782893Sdfr * memory denode's will be in synch.
792893Sdfr */
802893Sdfrint
812893Sdfrmsdosfs_lookup(ap)
8228787Sphk	struct vop_cachedlookup_args /* {
832893Sdfr		struct vnode *a_dvp;
842893Sdfr		struct vnode **a_vpp;
852893Sdfr		struct componentname *a_cnp;
862893Sdfr	} */ *ap;
872893Sdfr{
882893Sdfr	struct vnode *vdp = ap->a_dvp;
892893Sdfr	struct vnode **vpp = ap->a_vpp;
902893Sdfr	struct componentname *cnp = ap->a_cnp;
912893Sdfr	daddr_t bn;
922893Sdfr	int error;
9333548Sjkh	int slotcount;
9433548Sjkh	int slotoffset = 0;
952893Sdfr	int frcn;
962893Sdfr	u_long cluster;
9733548Sjkh	int blkoff;
982893Sdfr	int diroff;
9933548Sjkh	int blsize;
1002893Sdfr	int isadir;		/* ~0 if found direntry is a directory	 */
1012893Sdfr	u_long scn;		/* starting cluster number		 */
1022893Sdfr	struct vnode *pdp;
1032893Sdfr	struct denode *dp;
1042893Sdfr	struct denode *tdp;
1052893Sdfr	struct msdosfsmount *pmp;
1062893Sdfr	struct buf *bp = 0;
1072893Sdfr	struct direntry *dep = NULL;
1082893Sdfr	u_char dosfilename[12];
1092893Sdfr	int flags = cnp->cn_flags;
1102893Sdfr	int nameiop = cnp->cn_nameiop;
11183366Sjulian	struct thread *td = cnp->cn_thread;
11233848Smsmith	int unlen;
1132893Sdfr
11433548Sjkh	int wincnt = 1;
115134942Stjr	int chksum = -1, chksum_ok;
11633548Sjkh	int olddos = 1;
11733548Sjkh
1182893Sdfr#ifdef MSDOSFS_DEBUG
1192893Sdfr	printf("msdosfs_lookup(): looking for %s\n", cnp->cn_nameptr);
1202893Sdfr#endif
1212893Sdfr	dp = VTODE(vdp);
1222893Sdfr	pmp = dp->de_pmp;
1232893Sdfr	*vpp = NULL;
1242893Sdfr#ifdef MSDOSFS_DEBUG
12533548Sjkh	printf("msdosfs_lookup(): vdp %p, dp %p, Attr %02x\n",
12633548Sjkh	    vdp, dp, dp->de_Attributes);
1272893Sdfr#endif
1282893Sdfr
1292893Sdfr	/*
1302893Sdfr	 * If they are going after the . or .. entry in the root directory,
1312893Sdfr	 * they won't find it.  DOS filesystems don't have them in the root
1322893Sdfr	 * directory.  So, we fake it. deget() is in on this scam too.
1332893Sdfr	 */
134101308Sjeff	if ((vdp->v_vflag & VV_ROOT) && cnp->cn_nameptr[0] == '.' &&
1352893Sdfr	    (cnp->cn_namelen == 1 ||
1362893Sdfr		(cnp->cn_namelen == 2 && cnp->cn_nameptr[1] == '.'))) {
1372893Sdfr		isadir = ATTR_DIRECTORY;
1382893Sdfr		scn = MSDOSFSROOT;
1392893Sdfr#ifdef MSDOSFS_DEBUG
1402893Sdfr		printf("msdosfs_lookup(): looking for . or .. in root directory\n");
1412893Sdfr#endif
1422893Sdfr		cluster = MSDOSFSROOT;
14333548Sjkh		blkoff = MSDOSFSROOT_OFS;
1442893Sdfr		goto foundroot;
1452893Sdfr	}
1462893Sdfr
14733548Sjkh	switch (unix2dosfn((const u_char *)cnp->cn_nameptr, dosfilename,
148120492Sfjoe	    cnp->cn_namelen, 0, pmp)) {
14933548Sjkh	case 0:
15033548Sjkh		return (EINVAL);
15133548Sjkh	case 1:
15233548Sjkh		break;
15333548Sjkh	case 2:
15433548Sjkh		wincnt = winSlotCnt((const u_char *)cnp->cn_nameptr,
155120492Sfjoe		    cnp->cn_namelen, pmp) + 1;
15633548Sjkh		break;
15733548Sjkh	case 3:
15833548Sjkh		olddos = 0;
15933548Sjkh		wincnt = winSlotCnt((const u_char *)cnp->cn_nameptr,
160120492Sfjoe		    cnp->cn_namelen, pmp) + 1;
16133548Sjkh		break;
16233548Sjkh	}
16336133Sdt	if (pmp->pm_flags & MSDOSFSMNT_SHORTNAME) {
16433548Sjkh		wincnt = 1;
16536133Sdt		olddos = 1;
16636133Sdt	}
16733848Smsmith	unlen = winLenFixup(cnp->cn_nameptr, cnp->cn_namelen);
16833548Sjkh
1692893Sdfr	/*
17033548Sjkh	 * Suppress search for slots unless creating
17133548Sjkh	 * file and at end of pathname, in which case
17233548Sjkh	 * we watch for a place to put the new file in
17333548Sjkh	 * case it doesn't already exist.
1742893Sdfr	 */
17533548Sjkh	slotcount = wincnt;
17633548Sjkh	if ((nameiop == CREATE || nameiop == RENAME) &&
17733548Sjkh	    (flags & ISLASTCN))
17833548Sjkh		slotcount = 0;
1792893Sdfr
1802893Sdfr#ifdef MSDOSFS_DEBUG
18133548Sjkh	printf("msdosfs_lookup(): dos version of filename %s, length %ld\n",
18233548Sjkh	    dosfilename, cnp->cn_namelen);
1832893Sdfr#endif
1842893Sdfr	/*
1852893Sdfr	 * Search the directory pointed at by vdp for the name pointed at
1862893Sdfr	 * by cnp->cn_nameptr.
1872893Sdfr	 */
1882893Sdfr	tdp = NULL;
189120492Sfjoe	mbnambuf_init();
1902893Sdfr	/*
1912893Sdfr	 * The outer loop ranges over the clusters that make up the
1922893Sdfr	 * directory.  Note that the root directory is different from all
1932893Sdfr	 * other directories.  It has a fixed number of blocks that are not
1942893Sdfr	 * part of the pool of allocatable clusters.  So, we treat it a
1952893Sdfr	 * little differently. The root directory starts at "cluster" 0.
1962893Sdfr	 */
19733548Sjkh	diroff = 0;
1982893Sdfr	for (frcn = 0;; frcn++) {
19933548Sjkh		error = pcbmap(dp, frcn, &bn, &cluster, &blsize);
2003152Sphk		if (error) {
2012893Sdfr			if (error == E2BIG)
2022893Sdfr				break;
20333548Sjkh			return (error);
2042893Sdfr		}
20533548Sjkh		error = bread(pmp->pm_devvp, bn, blsize, NOCRED, &bp);
20633548Sjkh		if (error) {
20733548Sjkh			brelse(bp);
20833548Sjkh			return (error);
20933548Sjkh		}
21033548Sjkh		for (blkoff = 0; blkoff < blsize;
21133548Sjkh		     blkoff += sizeof(struct direntry),
21233548Sjkh		     diroff += sizeof(struct direntry)) {
21333548Sjkh			dep = (struct direntry *)(bp->b_data + blkoff);
2142893Sdfr			/*
2152893Sdfr			 * If the slot is empty and we are still looking
2162893Sdfr			 * for an empty then remember this one.  If the
2172893Sdfr			 * slot is not empty then check to see if it
2182893Sdfr			 * matches what we are looking for.  If the slot
2192893Sdfr			 * has never been filled with anything, then the
2202893Sdfr			 * remainder of the directory has never been used,
2212893Sdfr			 * so there is no point in searching it.
2222893Sdfr			 */
2232893Sdfr			if (dep->deName[0] == SLOT_EMPTY ||
2242893Sdfr			    dep->deName[0] == SLOT_DELETED) {
22533548Sjkh				/*
22633548Sjkh				 * Drop memory of previous long matches
22733548Sjkh				 */
22833548Sjkh				chksum = -1;
229120492Sfjoe				mbnambuf_init();
23033548Sjkh
23133548Sjkh				if (slotcount < wincnt) {
23233548Sjkh					slotcount++;
23333548Sjkh					slotoffset = diroff;
2342893Sdfr				}
2352893Sdfr				if (dep->deName[0] == SLOT_EMPTY) {
2362893Sdfr					brelse(bp);
2372893Sdfr					goto notfound;
2382893Sdfr				}
2392893Sdfr			} else {
2402893Sdfr				/*
24133548Sjkh				 * If there wasn't enough space for our winentries,
24233548Sjkh				 * forget about the empty space
24333548Sjkh				 */
24433548Sjkh				if (slotcount < wincnt)
24533548Sjkh					slotcount = 0;
24633548Sjkh
24733548Sjkh				/*
24833548Sjkh				 * Check for Win95 long filename entry
24933548Sjkh				 */
25033548Sjkh				if (dep->deAttributes == ATTR_WIN95) {
25133548Sjkh					if (pmp->pm_flags & MSDOSFSMNT_SHORTNAME)
25233548Sjkh						continue;
25333548Sjkh
254120492Sfjoe					chksum = win2unixfn((struct winentry *)dep,
25533750Sache							    chksum,
256120492Sfjoe							    pmp);
25733548Sjkh					continue;
25833548Sjkh				}
25933548Sjkh
260120492Sfjoe				chksum = winChkName((const u_char *)cnp->cn_nameptr,
261120492Sfjoe						    unlen,
262120492Sfjoe						    chksum,
263120492Sfjoe						    pmp);
264120492Sfjoe				if (chksum == -2) {
265120492Sfjoe					chksum = -1;
266120492Sfjoe					continue;
267120492Sfjoe				}
268120492Sfjoe
26933548Sjkh				/*
2702893Sdfr				 * Ignore volume labels (anywhere, not just
2712893Sdfr				 * the root directory).
2722893Sdfr				 */
27333548Sjkh				if (dep->deAttributes & ATTR_VOLUME) {
27433548Sjkh					chksum = -1;
27533548Sjkh					continue;
27633548Sjkh				}
27733548Sjkh
27833548Sjkh				/*
27933548Sjkh				 * Check for a checksum or name match
28033548Sjkh				 */
281134942Stjr				chksum_ok = (chksum == winChksum(dep->deName));
282134942Stjr				if (!chksum_ok
28333548Sjkh				    && (!olddos || bcmp(dosfilename, dep->deName, 11))) {
28433548Sjkh					chksum = -1;
28533548Sjkh					continue;
28633548Sjkh				}
2872893Sdfr#ifdef MSDOSFS_DEBUG
28833548Sjkh				printf("msdosfs_lookup(): match blkoff %d, diroff %d\n",
28933548Sjkh				    blkoff, diroff);
2902893Sdfr#endif
29133548Sjkh				/*
29233548Sjkh				 * Remember where this directory
29333548Sjkh				 * entry came from for whoever did
29433548Sjkh				 * this lookup.
29533548Sjkh				 */
29633548Sjkh				dp->de_fndoffset = diroff;
297134942Stjr				if (chksum_ok && nameiop == RENAME) {
298134942Stjr					/*
299134942Stjr					 * Target had correct long name
300134942Stjr					 * directory entries, reuse them
301134942Stjr					 * as needed.
302134942Stjr					 */
303134942Stjr					dp->de_fndcnt = wincnt - 1;
304134942Stjr				} else {
305134942Stjr					/*
306134942Stjr					 * Long name directory entries
307134942Stjr					 * not present or corrupt, can only
308134942Stjr					 * reuse dos directory entry.
309134942Stjr					 */
310134942Stjr					dp->de_fndcnt = 0;
311134942Stjr				}
31233548Sjkh
31333548Sjkh				goto found;
3142893Sdfr			}
31533548Sjkh		}	/* for (blkoff = 0; .... */
3162893Sdfr		/*
3172893Sdfr		 * Release the buffer holding the directory cluster just
3182893Sdfr		 * searched.
3192893Sdfr		 */
3202893Sdfr		brelse(bp);
32133548Sjkh	}	/* for (frcn = 0; ; frcn++) */
32233548Sjkh
32333548Sjkhnotfound:
3242893Sdfr	/*
3252893Sdfr	 * We hold no disk buffers at this point.
3262893Sdfr	 */
3272893Sdfr
3282893Sdfr	/*
32933548Sjkh	 * Fixup the slot description to point to the place where
33033548Sjkh	 * we might put the new DOS direntry (putting the Win95
33133548Sjkh	 * long name entries before that)
33233548Sjkh	 */
33333548Sjkh	if (!slotcount) {
33433548Sjkh		slotcount = 1;
33533548Sjkh		slotoffset = diroff;
33633548Sjkh	}
33733548Sjkh	if (wincnt > slotcount)
33833548Sjkh		slotoffset += sizeof(struct direntry) * (wincnt - slotcount);
33933548Sjkh
34033548Sjkh	/*
3412893Sdfr	 * If we get here we didn't find the entry we were looking for. But
3422893Sdfr	 * that's ok if we are creating or renaming and are at the end of
3432893Sdfr	 * the pathname and the directory hasn't been removed.
3442893Sdfr	 */
3452893Sdfr#ifdef MSDOSFS_DEBUG
34633548Sjkh	printf("msdosfs_lookup(): op %d, refcnt %ld\n",
34733548Sjkh	    nameiop, dp->de_refcnt);
34833548Sjkh	printf("               slotcount %d, slotoffset %d\n",
34933548Sjkh	       slotcount, slotoffset);
3502893Sdfr#endif
3512893Sdfr	if ((nameiop == CREATE || nameiop == RENAME) &&
3522893Sdfr	    (flags & ISLASTCN) && dp->de_refcnt != 0) {
35333548Sjkh		/*
35433548Sjkh		 * Access for write is interpreted as allowing
35533548Sjkh		 * creation of files in the directory.
35633548Sjkh		 */
35783366Sjulian		error = VOP_ACCESS(vdp, VWRITE, cnp->cn_cred, cnp->cn_thread);
3588386Sbde		if (error)
35933548Sjkh			return (error);
36033548Sjkh		/*
36133548Sjkh		 * Return an indication of where the new directory
36233548Sjkh		 * entry should be put.
36333548Sjkh		 */
36433548Sjkh		dp->de_fndoffset = slotoffset;
36533548Sjkh		dp->de_fndcnt = wincnt - 1;
36633548Sjkh
36733548Sjkh		/*
36833548Sjkh		 * We return with the directory locked, so that
36933548Sjkh		 * the parameters we set up above will still be
37033548Sjkh		 * valid if we actually decide to do a direnter().
37133548Sjkh		 * We return ni_vp == NULL to indicate that the entry
37233548Sjkh		 * does not currently exist; we leave a pointer to
37333548Sjkh		 * the (locked) directory inode in ndp->ni_dvp.
37433548Sjkh		 * The pathname buffer is saved so that the name
37533548Sjkh		 * can be obtained later.
37633548Sjkh		 *
37733548Sjkh		 * NB - if the directory is unlocked, then this
37833548Sjkh		 * information cannot be used.
37933548Sjkh		 */
3802893Sdfr		cnp->cn_flags |= SAVENAME;
38133548Sjkh		return (EJUSTRETURN);
3822893Sdfr	}
3832893Sdfr	/*
38433548Sjkh	 * Insert name into cache (as non-existent) if appropriate.
3852893Sdfr	 */
3862893Sdfr	if ((cnp->cn_flags & MAKEENTRY) && nameiop != CREATE)
3872893Sdfr		cache_enter(vdp, *vpp, cnp);
38833548Sjkh	return (ENOENT);
3892893Sdfr
39033548Sjkhfound:
3912893Sdfr	/*
3922893Sdfr	 * NOTE:  We still have the buffer with matched directory entry at
3932893Sdfr	 * this point.
3942893Sdfr	 */
3952893Sdfr	isadir = dep->deAttributes & ATTR_DIRECTORY;
3962893Sdfr	scn = getushort(dep->deStartCluster);
39733548Sjkh	if (FAT32(pmp)) {
39833548Sjkh		scn |= getushort(dep->deHighClust) << 16;
39933548Sjkh		if (scn == pmp->pm_rootdirblk) {
40033548Sjkh			/*
40133548Sjkh			 * There should actually be 0 here.
40233548Sjkh			 * Just ignore the error.
40333548Sjkh			 */
40433548Sjkh			scn = MSDOSFSROOT;
40533548Sjkh		}
40633548Sjkh	}
4072893Sdfr
40833548Sjkh	if (isadir) {
40933548Sjkh		cluster = scn;
41033548Sjkh		if (cluster == MSDOSFSROOT)
41133548Sjkh			blkoff = MSDOSFSROOT_OFS;
41233548Sjkh		else
41333548Sjkh			blkoff = 0;
41433548Sjkh	} else if (cluster == MSDOSFSROOT)
41533548Sjkh		blkoff = diroff;
41633548Sjkh
4172893Sdfr	/*
41833548Sjkh	 * Now release buf to allow deget to read the entry again.
41933548Sjkh	 * Reserving it here and giving it to deget could result
42033548Sjkh	 * in a deadlock.
42133548Sjkh	 */
42233548Sjkh	brelse(bp);
42333548Sjkh	bp = 0;
42433548Sjkh
42533548Sjkhfoundroot:
42633548Sjkh	/*
4272893Sdfr	 * If we entered at foundroot, then we are looking for the . or ..
4282893Sdfr	 * entry of the filesystems root directory.  isadir and scn were
42933548Sjkh	 * setup before jumping here.  And, bp is already null.
4302893Sdfr	 */
43133548Sjkh	if (FAT32(pmp) && scn == MSDOSFSROOT)
43233548Sjkh		scn = pmp->pm_rootdirblk;
4332893Sdfr
4342893Sdfr	/*
43533548Sjkh	 * If deleting, and at end of pathname, return
43633548Sjkh	 * parameters which can be used to remove file.
4372893Sdfr	 */
4382893Sdfr	if (nameiop == DELETE && (flags & ISLASTCN)) {
43933548Sjkh		/*
44033548Sjkh		 * Don't allow deleting the root.
44133548Sjkh		 */
44233548Sjkh		if (blkoff == MSDOSFSROOT_OFS)
44333548Sjkh			return EROFS;				/* really? XXX */
44433548Sjkh
44533548Sjkh		/*
44633548Sjkh		 * Write access to directory required to delete files.
44733548Sjkh		 */
44883366Sjulian		error = VOP_ACCESS(vdp, VWRITE, cnp->cn_cred, cnp->cn_thread);
44933548Sjkh		if (error)
45033548Sjkh			return (error);
45133548Sjkh
45233548Sjkh		/*
45333548Sjkh		 * Return pointer to current entry in dp->i_offset.
45433548Sjkh		 * Save directory inode pointer in ndp->ni_dvp for dirremove().
45533548Sjkh		 */
4562893Sdfr		if (dp->de_StartCluster == scn && isadir) {	/* "." */
4572893Sdfr			VREF(vdp);
4582893Sdfr			*vpp = vdp;
45933548Sjkh			return (0);
4602893Sdfr		}
46133548Sjkh		error = deget(pmp, cluster, blkoff, &tdp);
46233548Sjkh		if (error)
46333548Sjkh			return (error);
4642893Sdfr		*vpp = DETOV(tdp);
46533548Sjkh		return (0);
4662893Sdfr	}
4672893Sdfr
4682893Sdfr	/*
46933548Sjkh	 * If rewriting (RENAME), return the inode and the
47033548Sjkh	 * information required to rewrite the present directory
47133548Sjkh	 * Must get inode of directory entry to verify it's a
47233548Sjkh	 * regular file, or empty directory.
4732893Sdfr	 */
474144298Sjeff	if (nameiop == RENAME && (flags & ISLASTCN)) {
47533548Sjkh		if (blkoff == MSDOSFSROOT_OFS)
47633548Sjkh			return EROFS;				/* really? XXX */
47733548Sjkh
47883366Sjulian		error = VOP_ACCESS(vdp, VWRITE, cnp->cn_cred, cnp->cn_thread);
47933548Sjkh		if (error)
48033548Sjkh			return (error);
48133548Sjkh
48233548Sjkh		/*
48333548Sjkh		 * Careful about locking second inode.
48433548Sjkh		 * This can only occur if the target is ".".
48533548Sjkh		 */
48633548Sjkh		if (dp->de_StartCluster == scn && isadir)
48733548Sjkh			return (EISDIR);
48833548Sjkh
48933548Sjkh		if ((error = deget(pmp, cluster, blkoff, &tdp)) != 0)
49033548Sjkh			return (error);
4912893Sdfr		*vpp = DETOV(tdp);
4922893Sdfr		cnp->cn_flags |= SAVENAME;
49333548Sjkh		return (0);
4942893Sdfr	}
4952893Sdfr
4962893Sdfr	/*
49733548Sjkh	 * Step through the translation in the name.  We do not `vput' the
49833548Sjkh	 * directory because we may need it again if a symbolic link
49933548Sjkh	 * is relative to the current directory.  Instead we save it
50033548Sjkh	 * unlocked as "pdp".  We must get the target inode before unlocking
50133548Sjkh	 * the directory to insure that the inode will not be removed
50233548Sjkh	 * before we get it.  We prevent deadlock by always fetching
50333548Sjkh	 * inodes from the root, moving down the directory tree. Thus
50433548Sjkh	 * when following backward pointers ".." we must unlock the
50533548Sjkh	 * parent directory before getting the requested directory.
50633548Sjkh	 * There is a potential race condition here if both the current
50733548Sjkh	 * and parent directories are removed before the VFS_VGET for the
50833548Sjkh	 * inode associated with ".." returns.  We hope that this occurs
50933548Sjkh	 * infrequently since we cannot avoid this race condition without
51033548Sjkh	 * implementing a sophisticated deadlock detection algorithm.
51133548Sjkh	 * Note also that this simple deadlock detection scheme will not
51296755Strhodes	 * work if the filesystem has any hard links other than ".."
51333548Sjkh	 * that point backwards in the directory structure.
5142893Sdfr	 */
5152893Sdfr	pdp = vdp;
5162893Sdfr	if (flags & ISDOTDOT) {
51783366Sjulian		VOP_UNLOCK(pdp, 0, td);
51833548Sjkh		error = deget(pmp, cluster, blkoff,  &tdp);
5192893Sdfr		if (error) {
52083366Sjulian			vn_lock(pdp, LK_EXCLUSIVE | LK_RETRY, td);
52133548Sjkh			return (error);
5222893Sdfr		}
5232893Sdfr		*vpp = DETOV(tdp);
52433548Sjkh	} else if (dp->de_StartCluster == scn && isadir) {
52533548Sjkh		VREF(vdp);	/* we want ourself, ie "." */
5262893Sdfr		*vpp = vdp;
5272893Sdfr	} else {
52833548Sjkh		if ((error = deget(pmp, cluster, blkoff, &tdp)) != 0)
52933548Sjkh			return (error);
5302893Sdfr		*vpp = DETOV(tdp);
5312893Sdfr	}
5322893Sdfr
5332893Sdfr	/*
53433548Sjkh	 * Insert name into cache if appropriate.
5352893Sdfr	 */
5362893Sdfr	if (cnp->cn_flags & MAKEENTRY)
5372893Sdfr		cache_enter(vdp, *vpp, cnp);
53833548Sjkh	return (0);
5392893Sdfr}
5402893Sdfr
5412893Sdfr/*
5422893Sdfr * dep  - directory entry to copy into the directory
5432893Sdfr * ddep - directory to add to
5442893Sdfr * depp - return the address of the denode for the created directory entry
5452893Sdfr *	  if depp != 0
54633548Sjkh * cnp  - componentname needed for Win95 long filenames
5472893Sdfr */
5482893Sdfrint
54933548Sjkhcreatede(dep, ddep, depp, cnp)
5502893Sdfr	struct denode *dep;
5512893Sdfr	struct denode *ddep;
5522893Sdfr	struct denode **depp;
55333548Sjkh	struct componentname *cnp;
5542893Sdfr{
5552893Sdfr	int error;
5562893Sdfr	u_long dirclust, diroffset;
5572893Sdfr	struct direntry *ndep;
5582893Sdfr	struct msdosfsmount *pmp = ddep->de_pmp;
5592893Sdfr	struct buf *bp;
56033548Sjkh	daddr_t bn;
56133548Sjkh	int blsize;
5622893Sdfr
5632893Sdfr#ifdef MSDOSFS_DEBUG
56433548Sjkh	printf("createde(dep %p, ddep %p, depp %p, cnp %p)\n",
56533548Sjkh	    dep, ddep, depp, cnp);
5662893Sdfr#endif
5672893Sdfr
5682893Sdfr	/*
5692893Sdfr	 * If no space left in the directory then allocate another cluster
5702893Sdfr	 * and chain it onto the end of the file.  There is one exception
5712893Sdfr	 * to this.  That is, if the root directory has no more space it
5722893Sdfr	 * can NOT be expanded.  extendfile() checks for and fails attempts
5732893Sdfr	 * to extend the root directory.  We just return an error in that
5742893Sdfr	 * case.
5752893Sdfr	 */
57633548Sjkh	if (ddep->de_fndoffset >= ddep->de_FileSize) {
57733548Sjkh		diroffset = ddep->de_fndoffset + sizeof(struct direntry)
57833548Sjkh		    - ddep->de_FileSize;
57933548Sjkh		dirclust = de_clcount(pmp, diroffset);
58033548Sjkh		error = extendfile(ddep, dirclust, 0, 0, DE_CLEAR);
58133548Sjkh		if (error) {
58233548Sjkh			(void)detrunc(ddep, ddep->de_FileSize, 0, NOCRED, NULL);
5832893Sdfr			return error;
58433548Sjkh		}
58533548Sjkh
5862893Sdfr		/*
5872893Sdfr		 * Update the size of the directory
5882893Sdfr		 */
58933548Sjkh		ddep->de_FileSize += de_cn2off(pmp, dirclust);
5902893Sdfr	}
5912893Sdfr
5922893Sdfr	/*
59333548Sjkh	 * We just read in the cluster with space.  Copy the new directory
59433548Sjkh	 * entry in.  Then write it to disk. NOTE:  DOS directories
59533548Sjkh	 * do not get smaller as clusters are emptied.
5962893Sdfr	 */
59733548Sjkh	error = pcbmap(ddep, de_cluster(pmp, ddep->de_fndoffset),
59833548Sjkh		       &bn, &dirclust, &blsize);
59933548Sjkh	if (error)
6002893Sdfr		return error;
60133548Sjkh	diroffset = ddep->de_fndoffset;
60233548Sjkh	if (dirclust != MSDOSFSROOT)
60333548Sjkh		diroffset &= pmp->pm_crbomask;
60433548Sjkh	if ((error = bread(pmp->pm_devvp, bn, blsize, NOCRED, &bp)) != 0) {
60533548Sjkh		brelse(bp);
60633548Sjkh		return error;
6072893Sdfr	}
60833548Sjkh	ndep = bptoep(pmp, bp, ddep->de_fndoffset);
6092893Sdfr
61033548Sjkh	DE_EXTERNALIZE(ndep, dep);
6112893Sdfr
61233548Sjkh	/*
61333548Sjkh	 * Now write the Win95 long name
61433548Sjkh	 */
61533548Sjkh	if (ddep->de_fndcnt > 0) {
61633548Sjkh		u_int8_t chksum = winChksum(ndep->deName);
61733548Sjkh		const u_char *un = (const u_char *)cnp->cn_nameptr;
61833548Sjkh		int unlen = cnp->cn_namelen;
61933548Sjkh		int cnt = 1;
6202893Sdfr
62133548Sjkh		while (--ddep->de_fndcnt >= 0) {
62233548Sjkh			if (!(ddep->de_fndoffset & pmp->pm_crbomask)) {
62333548Sjkh				if ((error = bwrite(bp)) != 0)
62433548Sjkh					return error;
6252893Sdfr
62633548Sjkh				ddep->de_fndoffset -= sizeof(struct direntry);
62733548Sjkh				error = pcbmap(ddep,
62833548Sjkh					       de_cluster(pmp,
62933548Sjkh							  ddep->de_fndoffset),
63033548Sjkh					       &bn, 0, &blsize);
63133548Sjkh				if (error)
63233548Sjkh					return error;
6332893Sdfr
63433548Sjkh				error = bread(pmp->pm_devvp, bn, blsize,
63533548Sjkh					      NOCRED, &bp);
63633548Sjkh				if (error) {
63733548Sjkh					brelse(bp);
63833548Sjkh					return error;
63933548Sjkh				}
64033548Sjkh				ndep = bptoep(pmp, bp, ddep->de_fndoffset);
64133548Sjkh			} else {
64233548Sjkh				ndep--;
64333548Sjkh				ddep->de_fndoffset -= sizeof(struct direntry);
64433548Sjkh			}
64533747Sache			if (!unix2winfn(un, unlen, (struct winentry *)ndep,
646120492Sfjoe					cnt++, chksum, pmp))
64733548Sjkh				break;
64833548Sjkh		}
64933548Sjkh	}
65033548Sjkh
65133548Sjkh	if ((error = bwrite(bp)) != 0)
65233548Sjkh		return error;
65333548Sjkh
6542893Sdfr	/*
65533548Sjkh	 * If they want us to return with the denode gotten.
6562893Sdfr	 */
65733548Sjkh	if (depp) {
65833548Sjkh		if (dep->de_Attributes & ATTR_DIRECTORY) {
65933548Sjkh			dirclust = dep->de_StartCluster;
66033548Sjkh			if (FAT32(pmp) && dirclust == pmp->pm_rootdirblk)
66133548Sjkh				dirclust = MSDOSFSROOT;
66233548Sjkh			if (dirclust == MSDOSFSROOT)
66333548Sjkh				diroffset = MSDOSFSROOT_OFS;
66433548Sjkh			else
66533548Sjkh				diroffset = 0;
66633548Sjkh		}
66733548Sjkh		return deget(pmp, dirclust, diroffset, depp);
66833548Sjkh	}
6692893Sdfr
67033548Sjkh	return 0;
6712893Sdfr}
6722893Sdfr
6732893Sdfr/*
6742893Sdfr * Be sure a directory is empty except for "." and "..". Return 1 if empty,
6752893Sdfr * return 0 if not empty or error.
6762893Sdfr */
6772893Sdfrint
6782893Sdfrdosdirempty(dep)
6792893Sdfr	struct denode *dep;
6802893Sdfr{
68133548Sjkh	int blsize;
6822893Sdfr	int error;
6832893Sdfr	u_long cn;
6842893Sdfr	daddr_t bn;
6852893Sdfr	struct buf *bp;
6862893Sdfr	struct msdosfsmount *pmp = dep->de_pmp;
6872893Sdfr	struct direntry *dentp;
6882893Sdfr
6892893Sdfr	/*
6902893Sdfr	 * Since the filesize field in directory entries for a directory is
6912893Sdfr	 * zero, we just have to feel our way through the directory until
6922893Sdfr	 * we hit end of file.
6932893Sdfr	 */
6942893Sdfr	for (cn = 0;; cn++) {
69533548Sjkh		if ((error = pcbmap(dep, cn, &bn, 0, &blsize)) != 0) {
69633548Sjkh			if (error == E2BIG)
69733548Sjkh				return (1);	/* it's empty */
69833548Sjkh			return (0);
69933548Sjkh		}
70033548Sjkh		error = bread(pmp->pm_devvp, bn, blsize, NOCRED, &bp);
70133548Sjkh		if (error) {
70233548Sjkh			brelse(bp);
70333548Sjkh			return (0);
70433548Sjkh		}
70533548Sjkh		for (dentp = (struct direntry *)bp->b_data;
70633548Sjkh		     (char *)dentp < bp->b_data + blsize;
70733548Sjkh		     dentp++) {
70833548Sjkh			if (dentp->deName[0] != SLOT_DELETED &&
70933548Sjkh			    (dentp->deAttributes & ATTR_VOLUME) == 0) {
7102893Sdfr				/*
7112893Sdfr				 * In dos directories an entry whose name
7122893Sdfr				 * starts with SLOT_EMPTY (0) starts the
7132893Sdfr				 * beginning of the unused part of the
7142893Sdfr				 * directory, so we can just return that it
7152893Sdfr				 * is empty.
7162893Sdfr				 */
7172893Sdfr				if (dentp->deName[0] == SLOT_EMPTY) {
7182893Sdfr					brelse(bp);
71933548Sjkh					return (1);
7202893Sdfr				}
7212893Sdfr				/*
7222893Sdfr				 * Any names other than "." and ".." in a
7232893Sdfr				 * directory mean it is not empty.
7242893Sdfr				 */
7252893Sdfr				if (bcmp(dentp->deName, ".          ", 11) &&
7262893Sdfr				    bcmp(dentp->deName, "..         ", 11)) {
7272893Sdfr					brelse(bp);
7282893Sdfr#ifdef MSDOSFS_DEBUG
72933548Sjkh					printf("dosdirempty(): entry found %02x, %02x\n",
73033548Sjkh					    dentp->deName[0], dentp->deName[1]);
7312893Sdfr#endif
73233548Sjkh					return (0);	/* not empty */
7332893Sdfr				}
7342893Sdfr			}
7352893Sdfr		}
7362893Sdfr		brelse(bp);
7372893Sdfr	}
7382893Sdfr	/* NOTREACHED */
7392893Sdfr}
7402893Sdfr
7412893Sdfr/*
7422893Sdfr * Check to see if the directory described by target is in some
7432893Sdfr * subdirectory of source.  This prevents something like the following from
7442893Sdfr * succeeding and leaving a bunch or files and directories orphaned. mv
7452893Sdfr * /a/b/c /a/b/c/d/e/f Where c and f are directories.
7462893Sdfr *
7472893Sdfr * source - the inode for /a/b/c
7482893Sdfr * target - the inode for /a/b/c/d/e/f
7492893Sdfr *
7502893Sdfr * Returns 0 if target is NOT a subdirectory of source.
7512893Sdfr * Otherwise returns a non-zero error number.
7522893Sdfr * The target inode is always unlocked on return.
7532893Sdfr */
7542893Sdfrint
7552893Sdfrdoscheckpath(source, target)
7562893Sdfr	struct denode *source;
7572893Sdfr	struct denode *target;
7582893Sdfr{
7592893Sdfr	daddr_t scn;
7602893Sdfr	struct msdosfsmount *pmp;
7612893Sdfr	struct direntry *ep;
7622893Sdfr	struct denode *dep;
7632893Sdfr	struct buf *bp = NULL;
7642893Sdfr	int error = 0;
7652893Sdfr
7662893Sdfr	dep = target;
7672893Sdfr	if ((target->de_Attributes & ATTR_DIRECTORY) == 0 ||
7682893Sdfr	    (source->de_Attributes & ATTR_DIRECTORY) == 0) {
7692893Sdfr		error = ENOTDIR;
7702893Sdfr		goto out;
7712893Sdfr	}
7722893Sdfr	if (dep->de_StartCluster == source->de_StartCluster) {
7732893Sdfr		error = EEXIST;
7742893Sdfr		goto out;
7752893Sdfr	}
7762893Sdfr	if (dep->de_StartCluster == MSDOSFSROOT)
7772893Sdfr		goto out;
77833548Sjkh	pmp = dep->de_pmp;
77933548Sjkh#ifdef	DIAGNOSTIC
78033548Sjkh	if (pmp != source->de_pmp)
78133548Sjkh		panic("doscheckpath: source and target on different filesystems");
78233548Sjkh#endif
78333548Sjkh	if (FAT32(pmp) && dep->de_StartCluster == pmp->pm_rootdirblk)
78433548Sjkh		goto out;
78533548Sjkh
7862893Sdfr	for (;;) {
7872893Sdfr		if ((dep->de_Attributes & ATTR_DIRECTORY) == 0) {
7882893Sdfr			error = ENOTDIR;
78933548Sjkh			break;
7902893Sdfr		}
7912893Sdfr		scn = dep->de_StartCluster;
7922893Sdfr		error = bread(pmp->pm_devvp, cntobn(pmp, scn),
79333548Sjkh			      pmp->pm_bpcluster, NOCRED, &bp);
79433548Sjkh		if (error)
7952893Sdfr			break;
79633548Sjkh
7972893Sdfr		ep = (struct direntry *) bp->b_data + 1;
7982893Sdfr		if ((ep->deAttributes & ATTR_DIRECTORY) == 0 ||
7992893Sdfr		    bcmp(ep->deName, "..         ", 11) != 0) {
8002893Sdfr			error = ENOTDIR;
8012893Sdfr			break;
8022893Sdfr		}
8032893Sdfr		scn = getushort(ep->deStartCluster);
80433548Sjkh		if (FAT32(pmp))
80533548Sjkh			scn |= getushort(ep->deHighClust) << 16;
80633548Sjkh
8072893Sdfr		if (scn == source->de_StartCluster) {
8082893Sdfr			error = EINVAL;
8092893Sdfr			break;
8102893Sdfr		}
8112893Sdfr		if (scn == MSDOSFSROOT)
8122893Sdfr			break;
81333548Sjkh		if (FAT32(pmp) && scn == pmp->pm_rootdirblk) {
81433548Sjkh			/*
81533548Sjkh			 * scn should be 0 in this case,
81633548Sjkh			 * but we silently ignore the error.
81733548Sjkh			 */
81833548Sjkh			break;
81933548Sjkh		}
82033548Sjkh
8212893Sdfr		vput(DETOV(dep));
8222893Sdfr		brelse(bp);
8232893Sdfr		bp = NULL;
82433548Sjkh		/* NOTE: deget() clears dep on error */
82533548Sjkh		if ((error = deget(pmp, scn, 0, &dep)) != 0)
8262893Sdfr			break;
8272893Sdfr	}
82833548Sjkhout:;
8292893Sdfr	if (bp)
8302893Sdfr		brelse(bp);
8312893Sdfr	if (error == ENOTDIR)
8322893Sdfr		printf("doscheckpath(): .. not a directory?\n");
8332893Sdfr	if (dep != NULL)
8342893Sdfr		vput(DETOV(dep));
83533548Sjkh	return (error);
8362893Sdfr}
8372893Sdfr
8382893Sdfr/*
8392893Sdfr * Read in the disk block containing the directory entry (dirclu, dirofs)
8402893Sdfr * and return the address of the buf header, and the address of the
8412893Sdfr * directory entry within the block.
8422893Sdfr */
8432893Sdfrint
84433548Sjkhreadep(pmp, dirclust, diroffset, bpp, epp)
8452893Sdfr	struct msdosfsmount *pmp;
84633548Sjkh	u_long dirclust, diroffset;
8472893Sdfr	struct buf **bpp;
8482893Sdfr	struct direntry **epp;
8492893Sdfr{
8502893Sdfr	int error;
8512893Sdfr	daddr_t bn;
85233548Sjkh	int blsize;
8532893Sdfr
85433548Sjkh	blsize = pmp->pm_bpcluster;
85533548Sjkh	if (dirclust == MSDOSFSROOT
85633548Sjkh	    && de_blk(pmp, diroffset + blsize) > pmp->pm_rootdirsize)
85733548Sjkh		blsize = de_bn2off(pmp, pmp->pm_rootdirsize) & pmp->pm_crbomask;
85833548Sjkh	bn = detobn(pmp, dirclust, diroffset);
85933548Sjkh	if ((error = bread(pmp->pm_devvp, bn, blsize, NOCRED, bpp)) != 0) {
86033548Sjkh		brelse(*bpp);
8612893Sdfr		*bpp = NULL;
86233548Sjkh		return (error);
8632893Sdfr	}
8642893Sdfr	if (epp)
86533548Sjkh		*epp = bptoep(pmp, *bpp, diroffset);
86633548Sjkh	return (0);
8672893Sdfr}
8682893Sdfr
8692893Sdfr/*
8702893Sdfr * Read in the disk block containing the directory entry dep came from and
8712893Sdfr * return the address of the buf header, and the address of the directory
8722893Sdfr * entry within the block.
8732893Sdfr */
8742893Sdfrint
8752893Sdfrreadde(dep, bpp, epp)
8762893Sdfr	struct denode *dep;
8772893Sdfr	struct buf **bpp;
8782893Sdfr	struct direntry **epp;
8792893Sdfr{
88033548Sjkh
88133548Sjkh	return (readep(dep->de_pmp, dep->de_dirclust, dep->de_diroffset,
88233548Sjkh	    bpp, epp));
8832893Sdfr}
88433548Sjkh
88533548Sjkh/*
88633548Sjkh * Remove a directory entry. At this point the file represented by the
88733548Sjkh * directory entry to be removed is still full length until noone has it
88833548Sjkh * open.  When the file no longer being used msdosfs_inactive() is called
88933548Sjkh * and will truncate the file to 0 length.  When the vnode containing the
89033548Sjkh * denode is needed for some other purpose by VFS it will call
89133548Sjkh * msdosfs_reclaim() which will remove the denode from the denode cache.
89233548Sjkh */
89333548Sjkhint
89433548Sjkhremovede(pdep, dep)
89533548Sjkh	struct denode *pdep;	/* directory where the entry is removed */
89633548Sjkh	struct denode *dep;	/* file to be removed */
89733548Sjkh{
89833548Sjkh	int error;
89933548Sjkh	struct direntry *ep;
90033548Sjkh	struct buf *bp;
90133548Sjkh	daddr_t bn;
90233548Sjkh	int blsize;
90333548Sjkh	struct msdosfsmount *pmp = pdep->de_pmp;
90433548Sjkh	u_long offset = pdep->de_fndoffset;
90533548Sjkh
90633548Sjkh#ifdef MSDOSFS_DEBUG
90733548Sjkh	printf("removede(): filename %s, dep %p, offset %08lx\n",
90833548Sjkh	    dep->de_Name, dep, offset);
90933548Sjkh#endif
91033548Sjkh
91133548Sjkh	dep->de_refcnt--;
91233548Sjkh	offset += sizeof(struct direntry);
91333548Sjkh	do {
91433548Sjkh		offset -= sizeof(struct direntry);
91533548Sjkh		error = pcbmap(pdep, de_cluster(pmp, offset), &bn, 0, &blsize);
91633548Sjkh		if (error)
91733548Sjkh			return error;
91833548Sjkh		error = bread(pmp->pm_devvp, bn, blsize, NOCRED, &bp);
91933548Sjkh		if (error) {
92033548Sjkh			brelse(bp);
92133548Sjkh			return error;
92233548Sjkh		}
92333548Sjkh		ep = bptoep(pmp, bp, offset);
92433548Sjkh		/*
92533548Sjkh		 * Check whether, if we came here the second time, i.e.
92633548Sjkh		 * when underflowing into the previous block, the last
92733548Sjkh		 * entry in this block is a longfilename entry, too.
92833548Sjkh		 */
92933548Sjkh		if (ep->deAttributes != ATTR_WIN95
93033548Sjkh		    && offset != pdep->de_fndoffset) {
93133548Sjkh			brelse(bp);
93233548Sjkh			break;
93333548Sjkh		}
93433548Sjkh		offset += sizeof(struct direntry);
93533548Sjkh		while (1) {
93633548Sjkh			/*
93733548Sjkh			 * We are a bit agressive here in that we delete any Win95
93833548Sjkh			 * entries preceding this entry, not just the ones we "own".
93933548Sjkh			 * Since these presumably aren't valid anyway,
94033548Sjkh			 * there should be no harm.
94133548Sjkh			 */
94233548Sjkh			offset -= sizeof(struct direntry);
94333548Sjkh			ep--->deName[0] = SLOT_DELETED;
94433548Sjkh			if ((pmp->pm_flags & MSDOSFSMNT_NOWIN95)
94533548Sjkh			    || !(offset & pmp->pm_crbomask)
94633548Sjkh			    || ep->deAttributes != ATTR_WIN95)
94733548Sjkh				break;
94833548Sjkh		}
94933548Sjkh		if ((error = bwrite(bp)) != 0)
95033548Sjkh			return error;
95133548Sjkh	} while (!(pmp->pm_flags & MSDOSFSMNT_NOWIN95)
95233548Sjkh	    && !(offset & pmp->pm_crbomask)
95333548Sjkh	    && offset);
95433548Sjkh	return 0;
95533548Sjkh}
95633548Sjkh
95733548Sjkh/*
95833548Sjkh * Create a unique DOS name in dvp
95933548Sjkh */
96033548Sjkhint
96133548Sjkhuniqdosname(dep, cnp, cp)
96233548Sjkh	struct denode *dep;
96333548Sjkh	struct componentname *cnp;
96433548Sjkh	u_char *cp;
96533548Sjkh{
96633548Sjkh	struct msdosfsmount *pmp = dep->de_pmp;
96733548Sjkh	struct direntry *dentp;
96833548Sjkh	int gen;
96933548Sjkh	int blsize;
97033548Sjkh	u_long cn;
97133548Sjkh	daddr_t bn;
97233548Sjkh	struct buf *bp;
97333548Sjkh	int error;
97436133Sdt
97536154Sdt	if (pmp->pm_flags & MSDOSFSMNT_SHORTNAME)
97636133Sdt		return (unix2dosfn((const u_char *)cnp->cn_nameptr, cp,
977120492Sfjoe		    cnp->cn_namelen, 0, pmp) ? 0 : EINVAL);
97833548Sjkh
97933548Sjkh	for (gen = 1;; gen++) {
98033548Sjkh		/*
98133548Sjkh		 * Generate DOS name with generation number
98233548Sjkh		 */
98333548Sjkh		if (!unix2dosfn((const u_char *)cnp->cn_nameptr, cp,
984120492Sfjoe		    cnp->cn_namelen, gen, pmp))
98533548Sjkh			return gen == 1 ? EINVAL : EEXIST;
98633548Sjkh
98733548Sjkh		/*
98833548Sjkh		 * Now look for a dir entry with this exact name
98933548Sjkh		 */
99033548Sjkh		for (cn = error = 0; !error; cn++) {
99133548Sjkh			if ((error = pcbmap(dep, cn, &bn, 0, &blsize)) != 0) {
99233548Sjkh				if (error == E2BIG)	/* EOF reached and not found */
99333548Sjkh					return 0;
99433548Sjkh				return error;
99533548Sjkh			}
99633548Sjkh			error = bread(pmp->pm_devvp, bn, blsize, NOCRED, &bp);
99733548Sjkh			if (error) {
99833548Sjkh				brelse(bp);
99933548Sjkh				return error;
100033548Sjkh			}
100133548Sjkh			for (dentp = (struct direntry *)bp->b_data;
100233548Sjkh			     (char *)dentp < bp->b_data + blsize;
100333548Sjkh			     dentp++) {
100433548Sjkh				if (dentp->deName[0] == SLOT_EMPTY) {
100533548Sjkh					/*
100633548Sjkh					 * Last used entry and not found
100733548Sjkh					 */
100833548Sjkh					brelse(bp);
100933548Sjkh					return 0;
101033548Sjkh				}
101133548Sjkh				/*
101233548Sjkh				 * Ignore volume labels and Win95 entries
101333548Sjkh				 */
101433548Sjkh				if (dentp->deAttributes & ATTR_VOLUME)
101533548Sjkh					continue;
101633548Sjkh				if (!bcmp(dentp->deName, cp, 11)) {
101733548Sjkh					error = EEXIST;
101833548Sjkh					break;
101933548Sjkh				}
102033548Sjkh			}
102133548Sjkh			brelse(bp);
102233548Sjkh		}
102333548Sjkh	}
102433548Sjkh}
102533548Sjkh
102633548Sjkh/*
102733548Sjkh * Find any Win'95 long filename entry in directory dep
102833548Sjkh */
102933548Sjkhint
103033548Sjkhfindwin95(dep)
103133548Sjkh	struct denode *dep;
103233548Sjkh{
103333548Sjkh	struct msdosfsmount *pmp = dep->de_pmp;
103433548Sjkh	struct direntry *dentp;
103542252Sdt	int blsize, win95;
103633548Sjkh	u_long cn;
103733548Sjkh	daddr_t bn;
103833548Sjkh	struct buf *bp;
103933548Sjkh
104042252Sdt	win95 = 1;
104133548Sjkh	/*
104233548Sjkh	 * Read through the directory looking for Win'95 entries
104333548Sjkh	 * Note: Error currently handled just as EOF			XXX
104433548Sjkh	 */
104533548Sjkh	for (cn = 0;; cn++) {
104633548Sjkh		if (pcbmap(dep, cn, &bn, 0, &blsize))
104742252Sdt			return (win95);
104833548Sjkh		if (bread(pmp->pm_devvp, bn, blsize, NOCRED, &bp)) {
104933548Sjkh			brelse(bp);
105042252Sdt			return (win95);
105133548Sjkh		}
105233548Sjkh		for (dentp = (struct direntry *)bp->b_data;
105333548Sjkh		     (char *)dentp < bp->b_data + blsize;
105433548Sjkh		     dentp++) {
105533548Sjkh			if (dentp->deName[0] == SLOT_EMPTY) {
105633548Sjkh				/*
105733548Sjkh				 * Last used entry and not found
105833548Sjkh				 */
105933548Sjkh				brelse(bp);
106042252Sdt				return (win95);
106133548Sjkh			}
106233548Sjkh			if (dentp->deName[0] == SLOT_DELETED) {
106333548Sjkh				/*
106433548Sjkh				 * Ignore deleted files
106533548Sjkh				 * Note: might be an indication of Win'95 anyway	XXX
106633548Sjkh				 */
106733548Sjkh				continue;
106833548Sjkh			}
106933548Sjkh			if (dentp->deAttributes == ATTR_WIN95) {
107033548Sjkh				brelse(bp);
107133548Sjkh				return 1;
107233548Sjkh			}
107342252Sdt			win95 = 0;
107433548Sjkh		}
107533548Sjkh		brelse(bp);
107633548Sjkh	}
107733548Sjkh}
1078