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