xref: /netbsd-src/sys/fs/msdosfs/msdosfs_lookup.c (revision 6478b40555af74e0026fe65da2becaaefc2c3b08)
1*6478b405Sandvar /*	$NetBSD: msdosfs_lookup.c,v 1.41 2022/08/06 18:26:42 andvar Exp $	*/
298d58548Sjdolecek 
398d58548Sjdolecek /*-
498d58548Sjdolecek  * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank.
598d58548Sjdolecek  * Copyright (C) 1994, 1995, 1997 TooLs GmbH.
698d58548Sjdolecek  * All rights reserved.
798d58548Sjdolecek  * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below).
898d58548Sjdolecek  *
998d58548Sjdolecek  * Redistribution and use in source and binary forms, with or without
1098d58548Sjdolecek  * modification, are permitted provided that the following conditions
1198d58548Sjdolecek  * are met:
1298d58548Sjdolecek  * 1. Redistributions of source code must retain the above copyright
1398d58548Sjdolecek  *    notice, this list of conditions and the following disclaimer.
1498d58548Sjdolecek  * 2. Redistributions in binary form must reproduce the above copyright
1598d58548Sjdolecek  *    notice, this list of conditions and the following disclaimer in the
1698d58548Sjdolecek  *    documentation and/or other materials provided with the distribution.
1798d58548Sjdolecek  * 3. All advertising materials mentioning features or use of this software
1898d58548Sjdolecek  *    must display the following acknowledgement:
1998d58548Sjdolecek  *	This product includes software developed by TooLs GmbH.
2098d58548Sjdolecek  * 4. The name of TooLs GmbH may not be used to endorse or promote products
2198d58548Sjdolecek  *    derived from this software without specific prior written permission.
2298d58548Sjdolecek  *
2398d58548Sjdolecek  * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
2498d58548Sjdolecek  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
2598d58548Sjdolecek  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2698d58548Sjdolecek  * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
2798d58548Sjdolecek  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
2898d58548Sjdolecek  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
2998d58548Sjdolecek  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
3098d58548Sjdolecek  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
3198d58548Sjdolecek  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
3298d58548Sjdolecek  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3398d58548Sjdolecek  */
3498d58548Sjdolecek /*
3598d58548Sjdolecek  * Written by Paul Popelka (paulp@uts.amdahl.com)
3698d58548Sjdolecek  *
3798d58548Sjdolecek  * You can do anything you want with this software, just don't say you wrote
3898d58548Sjdolecek  * it, and don't remove this notice.
3998d58548Sjdolecek  *
4098d58548Sjdolecek  * This software is provided "as is".
4198d58548Sjdolecek  *
4298d58548Sjdolecek  * The author supplies this software to be publicly redistributed on the
4398d58548Sjdolecek  * understanding that the author is not responsible for the correct
4498d58548Sjdolecek  * functioning of this software in any circumstances and is not liable for
4598d58548Sjdolecek  * any damages caused by this software.
4698d58548Sjdolecek  *
4798d58548Sjdolecek  * October 1992
4898d58548Sjdolecek  */
4998d58548Sjdolecek 
50929f8943Schristos #if HAVE_NBTOOL_CONFIG_H
51929f8943Schristos #include "nbtool_config.h"
52929f8943Schristos #endif
53929f8943Schristos 
5498d58548Sjdolecek #include <sys/cdefs.h>
55*6478b405Sandvar __KERNEL_RCSID(0, "$NetBSD: msdosfs_lookup.c,v 1.41 2022/08/06 18:26:42 andvar Exp $");
5698d58548Sjdolecek 
5798d58548Sjdolecek #include <sys/param.h>
58691bcd5dSchristos 
59691bcd5dSchristos #ifdef _KERNEL
6098d58548Sjdolecek #include <sys/systm.h>
61929f8943Schristos #include <sys/mount.h>
62929f8943Schristos #include <sys/kauth.h>
6398d58548Sjdolecek #include <sys/namei.h>
64929f8943Schristos #include <sys/dirent.h>
6598d58548Sjdolecek #include <sys/buf.h>
6698d58548Sjdolecek #include <sys/vnode.h>
67798256c9Shannken #include <sys/atomic.h>
68929f8943Schristos #else
69929f8943Schristos #include <ffs/buf.h>
70929f8943Schristos #endif /* _KERNEL */
7198d58548Sjdolecek 
7298d58548Sjdolecek #include <fs/msdosfs/bpb.h>
7398d58548Sjdolecek #include <fs/msdosfs/direntry.h>
7498d58548Sjdolecek #include <fs/msdosfs/denode.h>
7598d58548Sjdolecek #include <fs/msdosfs/msdosfsmount.h>
7698d58548Sjdolecek #include <fs/msdosfs/fat.h>
7798d58548Sjdolecek 
78929f8943Schristos 
79929f8943Schristos #ifdef _KERNEL
8098d58548Sjdolecek /*
8198d58548Sjdolecek  * When we search a directory the blocks containing directory entries are
8298d58548Sjdolecek  * read and examined.  The directory entries contain information that would
8398d58548Sjdolecek  * normally be in the inode of a unix filesystem.  This means that some of
8498d58548Sjdolecek  * a directory's contents may also be in memory resident denodes (sort of
8598d58548Sjdolecek  * an inode).  This can cause problems if we are searching while some other
8698d58548Sjdolecek  * process is modifying a directory.  To prevent one process from accessing
8798d58548Sjdolecek  * incompletely modified directory information we depend upon being the
8898d58548Sjdolecek  * sole owner of a directory block.  bread/brelse provide this service.
8998d58548Sjdolecek  * This being the case, when a process modifies a directory it must first
9098d58548Sjdolecek  * acquire the disk block that contains the directory entry to be modified.
9198d58548Sjdolecek  * Then update the disk block and the denode, and then write the disk block
9298d58548Sjdolecek  * out to disk.  This way disk blocks containing directory entries and in
9398d58548Sjdolecek  * memory denode's will be in synch.
9498d58548Sjdolecek  */
9598d58548Sjdolecek int
msdosfs_lookup(void * v)96454af1c0Sdsl msdosfs_lookup(void *v)
9798d58548Sjdolecek {
9897834f7bShannken 	struct vop_lookup_v2_args /* {
9998d58548Sjdolecek 		struct vnode *a_dvp;
10098d58548Sjdolecek 		struct vnode **a_vpp;
10198d58548Sjdolecek 		struct componentname *a_cnp;
10298d58548Sjdolecek 	} */ *ap = v;
10398d58548Sjdolecek 	struct vnode *vdp = ap->a_dvp;
10498d58548Sjdolecek 	struct vnode **vpp = ap->a_vpp;
10598d58548Sjdolecek 	struct componentname *cnp = ap->a_cnp;
10698d58548Sjdolecek 	daddr_t bn;
10798d58548Sjdolecek 	int error;
10898d58548Sjdolecek 	int slotcount;
10998d58548Sjdolecek 	int slotoffset = 0;
11098d58548Sjdolecek 	int frcn;
11198d58548Sjdolecek 	u_long cluster;
11298d58548Sjdolecek 	int blkoff;
11398d58548Sjdolecek 	int diroff;
11498d58548Sjdolecek 	int blsize;
11598d58548Sjdolecek 	int isadir;		/* ~0 if found direntry is a directory	 */
11698d58548Sjdolecek 	u_long scn;		/* starting cluster number		 */
11798d58548Sjdolecek 	struct denode *dp;
11898d58548Sjdolecek 	struct msdosfsmount *pmp;
11998d58548Sjdolecek 	struct buf *bp = 0;
12098d58548Sjdolecek 	struct direntry *dep;
12198d58548Sjdolecek 	u_char dosfilename[12];
12298d58548Sjdolecek 	int flags;
12398d58548Sjdolecek 	int nameiop = cnp->cn_nameiop;
12498d58548Sjdolecek 	int wincnt = 1;
12598d58548Sjdolecek 	int chksum = -1, chksum_ok;
12698d58548Sjdolecek 	int olddos = 1;
12798d58548Sjdolecek 
12898d58548Sjdolecek 	flags = cnp->cn_flags;
12998d58548Sjdolecek 
13098d58548Sjdolecek #ifdef MSDOSFS_DEBUG
13198d58548Sjdolecek 	printf("msdosfs_lookup(): looking for %.*s\n",
13298d58548Sjdolecek 		(int)cnp->cn_namelen, cnp->cn_nameptr);
13398d58548Sjdolecek #endif
13498d58548Sjdolecek 	dp = VTODE(vdp);
13598d58548Sjdolecek 	pmp = dp->de_pmp;
13698d58548Sjdolecek 	*vpp = NULL;
13798d58548Sjdolecek #ifdef MSDOSFS_DEBUG
13898d58548Sjdolecek 	printf("msdosfs_lookup(): vdp %p, dp %p, Attr %02x\n",
13998d58548Sjdolecek 	    vdp, dp, dp->de_Attributes);
14098d58548Sjdolecek #endif
14198d58548Sjdolecek 
14298d58548Sjdolecek 	/*
143*6478b405Sandvar 	 * Check accessibility of directory.
14498d58548Sjdolecek 	 */
14561e8303eSpooka 	if ((error = VOP_ACCESS(vdp, VEXEC, cnp->cn_cred)) != 0)
14698d58548Sjdolecek 		return (error);
14798d58548Sjdolecek 
14898d58548Sjdolecek 	if ((flags & ISLASTCN) && (vdp->v_mount->mnt_flag & MNT_RDONLY) &&
14998d58548Sjdolecek 	    (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME))
15098d58548Sjdolecek 		return (EROFS);
15198d58548Sjdolecek 
15298d58548Sjdolecek 	/*
15398d58548Sjdolecek 	 * We now have a segment name to search for, and a directory to search.
15498d58548Sjdolecek 	 *
15598d58548Sjdolecek 	 * Before tediously performing a linear scan of the directory,
15698d58548Sjdolecek 	 * check the name cache to see if the directory/name pair
15798d58548Sjdolecek 	 * we are looking for is known already.
15898d58548Sjdolecek 	 */
15935ed6905Sdholland 	if (cache_lookup(vdp, cnp->cn_nameptr, cnp->cn_namelen,
16035ed6905Sdholland 			 cnp->cn_nameiop, cnp->cn_flags, NULL, vpp)) {
1611617a81dSdholland 		return *vpp == NULLVP ? ENOENT: 0;
1621617a81dSdholland 	}
16398d58548Sjdolecek 
164c90f9c8cSad 	/* May need to restart the lookup with an exclusive lock. */
165c90f9c8cSad 	if (VOP_ISLOCKED(vdp) != LK_EXCLUSIVE)
166c90f9c8cSad 		return ENOLCK;
167c90f9c8cSad 
16898d58548Sjdolecek 	/*
16998d58548Sjdolecek 	 * If they are going after the . or .. entry in the root directory,
17098d58548Sjdolecek 	 * they won't find it.  DOS filesystems don't have them in the root
17198d58548Sjdolecek 	 * directory.  So, we fake it. deget() is in on this scam too.
17298d58548Sjdolecek 	 */
1737dad9f73Sad 	if ((vdp->v_vflag & VV_ROOT) && cnp->cn_nameptr[0] == '.' &&
17498d58548Sjdolecek 	    (cnp->cn_namelen == 1 ||
17598d58548Sjdolecek 		(cnp->cn_namelen == 2 && cnp->cn_nameptr[1] == '.'))) {
17698d58548Sjdolecek 		isadir = ATTR_DIRECTORY;
17798d58548Sjdolecek 		scn = MSDOSFSROOT;
17898d58548Sjdolecek #ifdef MSDOSFS_DEBUG
17998d58548Sjdolecek 		printf("msdosfs_lookup(): looking for . or .. in root directory\n");
18098d58548Sjdolecek #endif
18198d58548Sjdolecek 		cluster = MSDOSFSROOT;
18298d58548Sjdolecek 		blkoff = MSDOSFSROOT_OFS;
18398d58548Sjdolecek 		goto foundroot;
18498d58548Sjdolecek 	}
18598d58548Sjdolecek 
1868086f46eSthorpej 	switch (msdosfs_unix2dosfn((const u_char *)cnp->cn_nameptr, dosfilename,
18798d58548Sjdolecek 	    cnp->cn_namelen, 0)) {
18898d58548Sjdolecek 	case 0:
18998d58548Sjdolecek 		return (EINVAL);
19098d58548Sjdolecek 	case 1:
19198d58548Sjdolecek 		break;
19298d58548Sjdolecek 	case 2:
1938086f46eSthorpej 		wincnt = msdosfs_winSlotCnt((const u_char *)cnp->cn_nameptr,
194223c7df5Smlelstv 		    cnp->cn_namelen, pmp->pm_flags & MSDOSFSMNT_UTF8) + 1;
19598d58548Sjdolecek 		break;
19698d58548Sjdolecek 	case 3:
19798d58548Sjdolecek 		olddos = 0;
1988086f46eSthorpej 		wincnt = msdosfs_winSlotCnt((const u_char *)cnp->cn_nameptr,
199223c7df5Smlelstv 		    cnp->cn_namelen, pmp->pm_flags & MSDOSFSMNT_UTF8) + 1;
20098d58548Sjdolecek 		break;
20198d58548Sjdolecek 	}
20298d58548Sjdolecek 	if (pmp->pm_flags & MSDOSFSMNT_SHORTNAME)
20398d58548Sjdolecek 		wincnt = 1;
20498d58548Sjdolecek 
20598d58548Sjdolecek 	/*
20698d58548Sjdolecek 	 * Suppress search for slots unless creating
20798d58548Sjdolecek 	 * file and at end of pathname, in which case
20898d58548Sjdolecek 	 * we watch for a place to put the new file in
20998d58548Sjdolecek 	 * case it doesn't already exist.
21098d58548Sjdolecek 	 */
21198d58548Sjdolecek 	slotcount = wincnt;
21298d58548Sjdolecek 	if ((nameiop == CREATE || nameiop == RENAME) &&
21398d58548Sjdolecek 	    (flags & ISLASTCN))
21498d58548Sjdolecek 		slotcount = 0;
21598d58548Sjdolecek 
21698d58548Sjdolecek #ifdef MSDOSFS_DEBUG
21798d58548Sjdolecek 	printf("msdosfs_lookup(): dos filename: %s\n", dosfilename);
21898d58548Sjdolecek #endif
21998d58548Sjdolecek 	/*
22098d58548Sjdolecek 	 * Search the directory pointed at by vdp for the name pointed at
22198d58548Sjdolecek 	 * by cnp->cn_nameptr.
22298d58548Sjdolecek 	 */
223798256c9Shannken 
22498d58548Sjdolecek 	/*
22598d58548Sjdolecek 	 * The outer loop ranges over the clusters that make up the
22698d58548Sjdolecek 	 * directory.  Note that the root directory is different from all
22798d58548Sjdolecek 	 * other directories.  It has a fixed number of blocks that are not
22898d58548Sjdolecek 	 * part of the pool of allocatable clusters.  So, we treat it a
22998d58548Sjdolecek 	 * little differently. The root directory starts at "cluster" 0.
23098d58548Sjdolecek 	 */
23198d58548Sjdolecek 	diroff = 0;
23298d58548Sjdolecek 	for (frcn = 0; diroff < dp->de_FileSize; frcn++) {
2338086f46eSthorpej 		if ((error = msdosfs_pcbmap(dp, frcn, &bn, &cluster,
2348086f46eSthorpej 		    &blsize)) != 0) {
23598d58548Sjdolecek 			if (error == E2BIG)
23698d58548Sjdolecek 				break;
23798d58548Sjdolecek 			return (error);
23898d58548Sjdolecek 		}
2396e392401Smaxv 		error = bread(pmp->pm_devvp, de_bn2kb(pmp, bn), blsize,
2405d2bff06Shannken 		    0, &bp);
24198d58548Sjdolecek 		if (error) {
24298d58548Sjdolecek 			return (error);
24398d58548Sjdolecek 		}
24498d58548Sjdolecek 		for (blkoff = 0; blkoff < blsize;
24598d58548Sjdolecek 		     blkoff += sizeof(struct direntry),
24698d58548Sjdolecek 		     diroff += sizeof(struct direntry)) {
24753524e44Schristos 			dep = (struct direntry *)((char *)bp->b_data + blkoff);
24898d58548Sjdolecek 			/*
24998d58548Sjdolecek 			 * If the slot is empty and we are still looking
25098d58548Sjdolecek 			 * for an empty then remember this one.  If the
25198d58548Sjdolecek 			 * slot is not empty then check to see if it
25298d58548Sjdolecek 			 * matches what we are looking for.  If the slot
25398d58548Sjdolecek 			 * has never been filled with anything, then the
25498d58548Sjdolecek 			 * remainder of the directory has never been used,
25598d58548Sjdolecek 			 * so there is no point in searching it.
25698d58548Sjdolecek 			 */
25798d58548Sjdolecek 			if (dep->deName[0] == SLOT_EMPTY ||
25898d58548Sjdolecek 			    dep->deName[0] == SLOT_DELETED) {
25998d58548Sjdolecek 				/*
26098d58548Sjdolecek 				 * Drop memory of previous long matches
26198d58548Sjdolecek 				 */
26298d58548Sjdolecek 				chksum = -1;
26398d58548Sjdolecek 
26498d58548Sjdolecek 				if (slotcount < wincnt) {
26598d58548Sjdolecek 					slotcount++;
26698d58548Sjdolecek 					slotoffset = diroff;
26798d58548Sjdolecek 				}
26898d58548Sjdolecek 				if (dep->deName[0] == SLOT_EMPTY) {
2699f56dfa5Sad 					brelse(bp, 0);
27098d58548Sjdolecek 					goto notfound;
27198d58548Sjdolecek 				}
27298d58548Sjdolecek 			} else {
27398d58548Sjdolecek 				/*
27498d58548Sjdolecek 				 * If there wasn't enough space for our
27598d58548Sjdolecek 				 * winentries, forget about the empty space
27698d58548Sjdolecek 				 */
27798d58548Sjdolecek 				if (slotcount < wincnt)
27898d58548Sjdolecek 					slotcount = 0;
27998d58548Sjdolecek 
28098d58548Sjdolecek 				/*
28198d58548Sjdolecek 				 * Check for Win95 long filename entry
28298d58548Sjdolecek 				 */
28398d58548Sjdolecek 				if (dep->deAttributes == ATTR_WIN95) {
28498d58548Sjdolecek 					if (pmp->pm_flags & MSDOSFSMNT_SHORTNAME)
28598d58548Sjdolecek 						continue;
28698d58548Sjdolecek 
2878086f46eSthorpej 					chksum = msdosfs_winChkName((const u_char *)cnp->cn_nameptr,
28898d58548Sjdolecek 							    cnp->cn_namelen,
28998d58548Sjdolecek 							    (struct winentry *)dep,
290223c7df5Smlelstv 							    chksum,
291223c7df5Smlelstv 							    pmp->pm_flags & MSDOSFSMNT_UTF8);
29298d58548Sjdolecek 					continue;
29398d58548Sjdolecek 				}
29498d58548Sjdolecek 
29598d58548Sjdolecek 				/*
29698d58548Sjdolecek 				 * Ignore volume labels (anywhere, not just
29798d58548Sjdolecek 				 * the root directory).
29898d58548Sjdolecek 				 */
29998d58548Sjdolecek 				if (dep->deAttributes & ATTR_VOLUME) {
30098d58548Sjdolecek 					chksum = -1;
30198d58548Sjdolecek 					continue;
30298d58548Sjdolecek 				}
30398d58548Sjdolecek 
30498d58548Sjdolecek 				/*
30598d58548Sjdolecek 				 * Check for a checksum or name match
30698d58548Sjdolecek 				 */
3078086f46eSthorpej 				chksum_ok =
3088086f46eSthorpej 				    (chksum == msdosfs_winChksum(dep->deName));
309266e035aSmlelstv 				if (!chksum_ok && (
310266e035aSmlelstv 					!olddos ||
311266e035aSmlelstv 					memcmp(&dosfilename[0],dep->deName,8) ||
312266e035aSmlelstv 					memcmp(&dosfilename[8],dep->deExtension,3))) {
31398d58548Sjdolecek 					chksum = -1;
31498d58548Sjdolecek 					continue;
31598d58548Sjdolecek 				}
31698d58548Sjdolecek #ifdef MSDOSFS_DEBUG
31798d58548Sjdolecek 				printf("msdosfs_lookup(): match blkoff %d, diroff %d\n",
31898d58548Sjdolecek 				    blkoff, diroff);
31998d58548Sjdolecek #endif
32098d58548Sjdolecek 				/*
32198d58548Sjdolecek 				 * Remember where this directory
32298d58548Sjdolecek 				 * entry came from for whoever did
32398d58548Sjdolecek 				 * this lookup.
32498d58548Sjdolecek 				 */
32525fae635Shannken 				dp->de_crap.mlr_fndoffset = diroff;
32698d58548Sjdolecek 				if (chksum_ok && nameiop == RENAME) {
32798d58548Sjdolecek 					/*
32898d58548Sjdolecek 					 * Target had correct long name
32998d58548Sjdolecek 					 * directory entries, reuse them
33098d58548Sjdolecek 					 * as needed.
33198d58548Sjdolecek 					 */
33225fae635Shannken 					dp->de_crap.mlr_fndcnt = wincnt - 1;
33398d58548Sjdolecek 				} else {
33498d58548Sjdolecek 					/*
33598d58548Sjdolecek 					 * Long name directory entries
33698d58548Sjdolecek 					 * not present or corrupt, can only
33798d58548Sjdolecek 					 * reuse dos directory entry.
33898d58548Sjdolecek 					 */
33925fae635Shannken 					dp->de_crap.mlr_fndcnt = 0;
34098d58548Sjdolecek 				}
34198d58548Sjdolecek 
34298d58548Sjdolecek 				goto found;
34398d58548Sjdolecek 			}
34498d58548Sjdolecek 		}	/* for (blkoff = 0; .... */
34598d58548Sjdolecek 		/*
34698d58548Sjdolecek 		 * Release the buffer holding the directory cluster just
34798d58548Sjdolecek 		 * searched.
34898d58548Sjdolecek 		 */
3499f56dfa5Sad 		brelse(bp, 0);
35098d58548Sjdolecek 	}	/* for (frcn = 0; ; frcn++) */
35198d58548Sjdolecek 
35298d58548Sjdolecek notfound:
35398d58548Sjdolecek 	/*
35498d58548Sjdolecek 	 * We hold no disk buffers at this point.
35598d58548Sjdolecek 	 */
35698d58548Sjdolecek 
35798d58548Sjdolecek 	/*
35898d58548Sjdolecek 	 * If we get here we didn't find the entry we were looking for. But
35998d58548Sjdolecek 	 * that's ok if we are creating or renaming and are at the end of
36098d58548Sjdolecek 	 * the pathname and the directory hasn't been removed.
36198d58548Sjdolecek 	 */
36298d58548Sjdolecek #ifdef MSDOSFS_DEBUG
36398d58548Sjdolecek 	printf("msdosfs_lookup(): op %d, refcnt %ld, slotcount %d, slotoffset %d\n",
36498d58548Sjdolecek 	    nameiop, dp->de_refcnt, slotcount, slotoffset);
36598d58548Sjdolecek #endif
36698d58548Sjdolecek 	if ((nameiop == CREATE || nameiop == RENAME) &&
36798d58548Sjdolecek 	    (flags & ISLASTCN) && dp->de_refcnt != 0) {
36898d58548Sjdolecek 		/*
36998d58548Sjdolecek 		 * Access for write is interpreted as allowing
37098d58548Sjdolecek 		 * creation of files in the directory.
37198d58548Sjdolecek 		 */
37261e8303eSpooka 		error = VOP_ACCESS(vdp, VWRITE, cnp->cn_cred);
37398d58548Sjdolecek 		if (error)
37498d58548Sjdolecek 			return (error);
37598d58548Sjdolecek 
37698d58548Sjdolecek 		/*
37798d58548Sjdolecek 		 * Fixup the slot description to point to the place where
37898d58548Sjdolecek 		 * we might put the new DOS direntry (putting the Win95
37998d58548Sjdolecek 		 * long name entries before that)
38098d58548Sjdolecek 		 */
38198d58548Sjdolecek 		if (!slotcount) {
38298d58548Sjdolecek 			slotcount = 1;
38398d58548Sjdolecek 			slotoffset = diroff;
38498d58548Sjdolecek 		}
38598d58548Sjdolecek 		if (wincnt > slotcount) {
38698d58548Sjdolecek 			slotoffset +=
38798d58548Sjdolecek 				sizeof(struct direntry) * (wincnt - slotcount);
38898d58548Sjdolecek 		}
38998d58548Sjdolecek 
39098d58548Sjdolecek 		/*
39198d58548Sjdolecek 		 * Return an indication of where the new directory
39298d58548Sjdolecek 		 * entry should be put.
39398d58548Sjdolecek 		 */
39425fae635Shannken 		dp->de_crap.mlr_fndoffset = slotoffset;
39525fae635Shannken 		dp->de_crap.mlr_fndcnt = wincnt - 1;
39698d58548Sjdolecek 
39798d58548Sjdolecek 		/*
39898d58548Sjdolecek 		 * We return with the directory locked, so that
39998d58548Sjdolecek 		 * the parameters we set up above will still be
40098d58548Sjdolecek 		 * valid if we actually decide to do a direnter().
40198d58548Sjdolecek 		 * We return ni_vp == NULL to indicate that the entry
40298d58548Sjdolecek 		 * does not currently exist; we leave a pointer to
40398d58548Sjdolecek 		 * the (locked) directory inode in ndp->ni_dvp.
40498d58548Sjdolecek 		 *
40598d58548Sjdolecek 		 * NB - if the directory is unlocked, then this
40698d58548Sjdolecek 		 * information cannot be used.
40798d58548Sjdolecek 		 */
40898d58548Sjdolecek 		return (EJUSTRETURN);
40998d58548Sjdolecek 	}
41098d58548Sjdolecek 
411fe968d17Schristos #if 0
41298d58548Sjdolecek 	/*
41398d58548Sjdolecek 	 * Insert name into cache (as non-existent) if appropriate.
414fe968d17Schristos 	 *
415fe968d17Schristos 	 * XXX Negative caching is broken for msdosfs because the name
416fe968d17Schristos 	 * cache doesn't understand peculiarities such as case insensitivity
417fe968d17Schristos 	 * and 8.3 filenames.  Hence, it may not invalidate all negative
418fe968d17Schristos 	 * entries if a file with this name is later created.
419a74b7aeaSsoda 	 * e.g. creating a file 'foo' won't invalidate a negative entry
420a74b7aeaSsoda 	 * for 'FOO'.
42198d58548Sjdolecek 	 */
422d65753d9Srmind 	if (nameiop != CREATE)
42335ed6905Sdholland 		cache_enter(vdp, *vpp, cnp->cn_nameptr, cnp->cn_namelen,
42435ed6905Sdholland 			    cnp->cn_flags);
425fe968d17Schristos #endif
42698d58548Sjdolecek 
42798d58548Sjdolecek 	return (ENOENT);
42898d58548Sjdolecek 
42998d58548Sjdolecek found:
43098d58548Sjdolecek 	/*
43198d58548Sjdolecek 	 * NOTE:  We still have the buffer with matched directory entry at
43298d58548Sjdolecek 	 * this point.
43398d58548Sjdolecek 	 */
43498d58548Sjdolecek 	isadir = dep->deAttributes & ATTR_DIRECTORY;
43598d58548Sjdolecek 	scn = getushort(dep->deStartCluster);
43698d58548Sjdolecek 	if (FAT32(pmp)) {
43798d58548Sjdolecek 		scn |= getushort(dep->deHighClust) << 16;
43898d58548Sjdolecek 		if (scn == pmp->pm_rootdirblk) {
43998d58548Sjdolecek 			/*
44098d58548Sjdolecek 			 * There should actually be 0 here.
44198d58548Sjdolecek 			 * Just ignore the error.
44298d58548Sjdolecek 			 */
44398d58548Sjdolecek 			scn = MSDOSFSROOT;
44498d58548Sjdolecek 		}
44598d58548Sjdolecek 	}
44698d58548Sjdolecek 
44798d58548Sjdolecek 	if (isadir) {
44898d58548Sjdolecek 		cluster = scn;
44998d58548Sjdolecek 		if (cluster == MSDOSFSROOT)
45098d58548Sjdolecek 			blkoff = MSDOSFSROOT_OFS;
45198d58548Sjdolecek 		else
45298d58548Sjdolecek 			blkoff = 0;
45398d58548Sjdolecek 	} else if (cluster == MSDOSFSROOT)
45498d58548Sjdolecek 		blkoff = diroff;
45598d58548Sjdolecek 
45698d58548Sjdolecek 	/*
45798d58548Sjdolecek 	 * Now release buf to allow deget to read the entry again.
45898d58548Sjdolecek 	 * Reserving it here and giving it to deget could result
45998d58548Sjdolecek 	 * in a deadlock.
46098d58548Sjdolecek 	 */
4619f56dfa5Sad 	brelse(bp, 0);
46298d58548Sjdolecek 
46398d58548Sjdolecek foundroot:
46498d58548Sjdolecek 	/*
46598d58548Sjdolecek 	 * If we entered at foundroot, then we are looking for the . or ..
46698d58548Sjdolecek 	 * entry of the filesystems root directory.  isadir and scn were
46798d58548Sjdolecek 	 * setup before jumping here.  And, bp is already null.
46898d58548Sjdolecek 	 */
46998d58548Sjdolecek 	if (FAT32(pmp) && scn == MSDOSFSROOT)
47098d58548Sjdolecek 		scn = pmp->pm_rootdirblk;
47198d58548Sjdolecek 
47298d58548Sjdolecek 	/*
47398d58548Sjdolecek 	 * If deleting, and at end of pathname, return
47498d58548Sjdolecek 	 * parameters which can be used to remove file.
475c398ae97Schs 	 * Lock the inode, being careful with ".".
47698d58548Sjdolecek 	 */
47798d58548Sjdolecek 	if (nameiop == DELETE && (flags & ISLASTCN)) {
47898d58548Sjdolecek 		/*
47998d58548Sjdolecek 		 * Don't allow deleting the root.
48098d58548Sjdolecek 		 */
48198d58548Sjdolecek 		if (blkoff == MSDOSFSROOT_OFS)
4822a392db8Smlelstv 			return EINVAL;
48398d58548Sjdolecek 
48498d58548Sjdolecek 		/*
48598d58548Sjdolecek 		 * Write access to directory required to delete files.
48698d58548Sjdolecek 		 */
48761e8303eSpooka 		error = VOP_ACCESS(vdp, VWRITE, cnp->cn_cred);
48898d58548Sjdolecek 		if (error)
48998d58548Sjdolecek 			return (error);
49098d58548Sjdolecek 
49198d58548Sjdolecek 		/*
49298d58548Sjdolecek 		 * Return pointer to current entry in dp->i_offset.
49398d58548Sjdolecek 		 * Save directory inode pointer in ndp->ni_dvp for dirremove().
49498d58548Sjdolecek 		 */
49598d58548Sjdolecek 		if (dp->de_StartCluster == scn && isadir) {	/* "." */
496c3183f32Spooka 			vref(vdp);
49798d58548Sjdolecek 			*vpp = vdp;
49898d58548Sjdolecek 			return (0);
49998d58548Sjdolecek 		}
5008086f46eSthorpej 		error = msdosfs_deget(pmp, cluster, blkoff, vpp);
501798256c9Shannken 		return error;
50298d58548Sjdolecek 	}
50398d58548Sjdolecek 
50498d58548Sjdolecek 	/*
50598d58548Sjdolecek 	 * If rewriting (RENAME), return the inode and the
50698d58548Sjdolecek 	 * information required to rewrite the present directory
50798d58548Sjdolecek 	 * Must get inode of directory entry to verify it's a
50898d58548Sjdolecek 	 * regular file, or empty directory.
50998d58548Sjdolecek 	 */
510c398ae97Schs 	if (nameiop == RENAME && (flags & ISLASTCN)) {
51198d58548Sjdolecek 
51298d58548Sjdolecek 		if (vdp->v_mount->mnt_flag & MNT_RDONLY)
51398d58548Sjdolecek 			return (EROFS);
51498d58548Sjdolecek 
51598d58548Sjdolecek 		if (blkoff == MSDOSFSROOT_OFS)
5162a392db8Smlelstv 			return EINVAL;
51798d58548Sjdolecek 
51861e8303eSpooka 		error = VOP_ACCESS(vdp, VWRITE, cnp->cn_cred);
51998d58548Sjdolecek 		if (error)
52098d58548Sjdolecek 			return (error);
52198d58548Sjdolecek 
52298d58548Sjdolecek 		/*
52398d58548Sjdolecek 		 * Careful about locking second inode.
52498d58548Sjdolecek 		 * This can only occur if the target is ".".
52598d58548Sjdolecek 		 */
52698d58548Sjdolecek 		if (dp->de_StartCluster == scn && isadir)
52798d58548Sjdolecek 			return (EISDIR);
52898d58548Sjdolecek 
5298086f46eSthorpej 		error = msdosfs_deget(pmp, cluster, blkoff, vpp);
530c398ae97Schs 		return error;
53198d58548Sjdolecek 	}
532798256c9Shannken 
533798256c9Shannken 	if (dp->de_StartCluster == scn && isadir) {
534c3183f32Spooka 		vref(vdp);	/* we want ourself, ie "." */
53598d58548Sjdolecek 		*vpp = vdp;
5368086f46eSthorpej 	} else if ((error = msdosfs_deget(pmp, cluster, blkoff, vpp)) != 0) {
537798256c9Shannken 		return error;
53898d58548Sjdolecek 	}
53998d58548Sjdolecek 
54098d58548Sjdolecek 	/*
54198d58548Sjdolecek 	 * Insert name into cache if appropriate.
54298d58548Sjdolecek 	 */
54335ed6905Sdholland 	cache_enter(vdp, *vpp, cnp->cn_nameptr, cnp->cn_namelen, cnp->cn_flags);
54498d58548Sjdolecek 
545d65753d9Srmind 	return 0;
54698d58548Sjdolecek }
547929f8943Schristos #endif /* _KERNEL */
54898d58548Sjdolecek 
54998d58548Sjdolecek /*
55098d58548Sjdolecek  * dep  - directory entry to copy into the directory
55198d58548Sjdolecek  * ddep - directory to add to
55298d58548Sjdolecek  * depp - return the address of the denode for the created directory entry
55398d58548Sjdolecek  *	  if depp != 0
55498d58548Sjdolecek  * cnp  - componentname needed for Win95 long filenames
55598d58548Sjdolecek  */
55698d58548Sjdolecek int
msdosfs_createde(struct denode * dep,struct denode * ddep,const struct msdosfs_lookup_results * mlr,struct denode ** depp,struct componentname * cnp)5578086f46eSthorpej msdosfs_createde(struct denode *dep, struct denode *ddep,
55825fae635Shannken     const struct msdosfs_lookup_results *mlr,
55925fae635Shannken     struct denode **depp, struct componentname *cnp)
56098d58548Sjdolecek {
56198d58548Sjdolecek 	int error, rberror;
56298d58548Sjdolecek 	u_long dirclust, clusoffset;
563929f8943Schristos 	u_long fndoffset, havecnt = 0, wcnt = 1, i;
56498d58548Sjdolecek 	struct direntry *ndep;
56598d58548Sjdolecek 	struct msdosfsmount *pmp = ddep->de_pmp;
56698d58548Sjdolecek 	struct buf *bp;
56798d58548Sjdolecek 	daddr_t bn;
568929f8943Schristos 	int blsize;
569929f8943Schristos #ifdef _KERNEL
57098d58548Sjdolecek 	int async = ddep->de_pmp->pm_mountp->mnt_flag & MNT_ASYNC;
571929f8943Schristos #else
572929f8943Schristos #define async 0
573929f8943Schristos #endif
57498d58548Sjdolecek 
57598d58548Sjdolecek #ifdef MSDOSFS_DEBUG
57698d58548Sjdolecek 	printf("createde(dep %p, ddep %p, depp %p, cnp %p)\n",
57798d58548Sjdolecek 	    dep, ddep, depp, cnp);
57898d58548Sjdolecek #endif
57998d58548Sjdolecek 
58098d58548Sjdolecek 	/*
58198d58548Sjdolecek 	 * If no space left in the directory then allocate another cluster
58298d58548Sjdolecek 	 * and chain it onto the end of the file.  There is one exception
58398d58548Sjdolecek 	 * to this.  That is, if the root directory has no more space it
58498d58548Sjdolecek 	 * can NOT be expanded.  extendfile() checks for and fails attempts
58598d58548Sjdolecek 	 * to extend the root directory.  We just return an error in that
58698d58548Sjdolecek 	 * case.
58798d58548Sjdolecek 	 */
58825fae635Shannken 	if (mlr->mlr_fndoffset >= ddep->de_FileSize) {
58925fae635Shannken 		u_long needlen = ddep->de_crap.mlr_fndoffset
59025fae635Shannken 		    + sizeof(struct direntry) - ddep->de_FileSize;
59198d58548Sjdolecek 		dirclust = de_clcount(pmp, needlen);
5928086f46eSthorpej 		if ((error = msdosfs_extendfile(ddep, dirclust, 0, 0,
5938086f46eSthorpej 		    DE_CLEAR)) != 0) {
5948086f46eSthorpej 			(void)msdosfs_detrunc(ddep, ddep->de_FileSize, 0,
5958086f46eSthorpej 			    NOCRED);
59698d58548Sjdolecek 			goto err_norollback;
59798d58548Sjdolecek 		}
59898d58548Sjdolecek 
59998d58548Sjdolecek 		/*
60098d58548Sjdolecek 		 * Update the size of the directory
60198d58548Sjdolecek 		 */
60298d58548Sjdolecek 		ddep->de_FileSize += de_cn2off(pmp, dirclust);
60398d58548Sjdolecek 	}
60498d58548Sjdolecek 
60598d58548Sjdolecek 	/*
60698d58548Sjdolecek 	 * We just read in the cluster with space.  Copy the new directory
60798d58548Sjdolecek 	 * entry in.  Then write it to disk. NOTE:  DOS directories
60898d58548Sjdolecek 	 * do not get smaller as clusters are emptied.
60998d58548Sjdolecek 	 */
6108086f46eSthorpej 	error = msdosfs_pcbmap(ddep, de_cluster(pmp, mlr->mlr_fndoffset),
61198d58548Sjdolecek 		       &bn, &dirclust, &blsize);
61298d58548Sjdolecek 	if (error)
61398d58548Sjdolecek 		goto err_norollback;
61425fae635Shannken 	clusoffset = mlr->mlr_fndoffset;
61598d58548Sjdolecek 	if (dirclust != MSDOSFSROOT)
61698d58548Sjdolecek 		clusoffset &= pmp->pm_crbomask;
6176e392401Smaxv 	if ((error = bread(pmp->pm_devvp, de_bn2kb(pmp, bn), blsize,
6185d2bff06Shannken 	    B_MODIFY, &bp)) != 0) {
61998d58548Sjdolecek 		goto err_norollback;
62098d58548Sjdolecek 	}
62198d58548Sjdolecek 	ndep = bptoep(pmp, bp, clusoffset);
62298d58548Sjdolecek 
62398d58548Sjdolecek 	DE_EXTERNALIZE(ndep, dep);
62498d58548Sjdolecek 
62598d58548Sjdolecek 	/*
62698d58548Sjdolecek 	 * Now write the Win95 long name
62798d58548Sjdolecek 	 */
62825fae635Shannken 	if (mlr->mlr_fndcnt > 0) {
6298086f46eSthorpej 		u_int8_t chksum = msdosfs_winChksum(ndep->deName);
63098d58548Sjdolecek 		const u_char *un = (const u_char *)cnp->cn_nameptr;
63198d58548Sjdolecek 		int unlen = cnp->cn_namelen;
632a29d4b25Schristos 		u_long xhavecnt;
63398d58548Sjdolecek 
63425fae635Shannken 		fndoffset = mlr->mlr_fndoffset;
63525fae635Shannken 		xhavecnt = mlr->mlr_fndcnt + 1;
63698d58548Sjdolecek 
637a29d4b25Schristos 		for(; wcnt < xhavecnt; wcnt++) {
63898d58548Sjdolecek 			if ((fndoffset & pmp->pm_crbomask) == 0) {
63998d58548Sjdolecek 				/* we should never get here if ddep is root
64098d58548Sjdolecek 				 * directory */
64198d58548Sjdolecek 
64298d58548Sjdolecek 				if (async)
64398d58548Sjdolecek 					(void) bdwrite(bp);
64498d58548Sjdolecek 				else if ((error = bwrite(bp)) != 0)
64598d58548Sjdolecek 					goto rollback;
64698d58548Sjdolecek 
64798d58548Sjdolecek 				fndoffset -= sizeof(struct direntry);
6488086f46eSthorpej 				error = msdosfs_pcbmap(ddep,
64998d58548Sjdolecek 					       de_cluster(pmp, fndoffset),
65098d58548Sjdolecek 					       &bn, 0, &blsize);
65198d58548Sjdolecek 				if (error)
65298d58548Sjdolecek 					goto rollback;
65398d58548Sjdolecek 
6544f0ca272Sscw 				error = bread(pmp->pm_devvp, de_bn2kb(pmp, bn),
6556e392401Smaxv 				    blsize, B_MODIFY, &bp);
65698d58548Sjdolecek 				if (error) {
65798d58548Sjdolecek 					goto rollback;
65898d58548Sjdolecek 				}
65998d58548Sjdolecek 				ndep = bptoep(pmp, bp,
66098d58548Sjdolecek 						fndoffset & pmp->pm_crbomask);
66198d58548Sjdolecek 			} else {
66298d58548Sjdolecek 				ndep--;
66398d58548Sjdolecek 				fndoffset -= sizeof(struct direntry);
66498d58548Sjdolecek 			}
6658086f46eSthorpej 			if (!msdosfs_unix2winfn(un, unlen,
6668086f46eSthorpej 					(struct winentry *)ndep,
667223c7df5Smlelstv 					wcnt, chksum,
668223c7df5Smlelstv 					ddep->de_pmp->pm_flags & MSDOSFSMNT_UTF8))
66998d58548Sjdolecek 				break;
67098d58548Sjdolecek 		}
67198d58548Sjdolecek 	}
67298d58548Sjdolecek 
67398d58548Sjdolecek 	if (async)
67498d58548Sjdolecek 		bdwrite(bp);
67598d58548Sjdolecek 	else if ((error = bwrite(bp)) != 0)
67698d58548Sjdolecek 		goto rollback;
67798d58548Sjdolecek 
67898d58548Sjdolecek 	/*
67998d58548Sjdolecek 	 * If they want us to return with the denode gotten.
68098d58548Sjdolecek 	 */
68198d58548Sjdolecek 	if (depp) {
68298d58548Sjdolecek 		u_long diroffset = clusoffset;
683798256c9Shannken 
68498d58548Sjdolecek 		if (dep->de_Attributes & ATTR_DIRECTORY) {
68598d58548Sjdolecek 			dirclust = dep->de_StartCluster;
68698d58548Sjdolecek 			if (FAT32(pmp) && dirclust == pmp->pm_rootdirblk)
68798d58548Sjdolecek 				dirclust = MSDOSFSROOT;
68898d58548Sjdolecek 			if (dirclust == MSDOSFSROOT)
68998d58548Sjdolecek 				diroffset = MSDOSFSROOT_OFS;
69098d58548Sjdolecek 			else
69198d58548Sjdolecek 				diroffset = 0;
69298d58548Sjdolecek 		}
693798256c9Shannken #ifdef MAKEFS
6948086f46eSthorpej 		error = msdosfs_deget(pmp, dirclust, diroffset, depp);
695798256c9Shannken #else
696798256c9Shannken 		struct vnode *vp;
697798256c9Shannken 
6988086f46eSthorpej 		error = msdosfs_deget(pmp, dirclust, diroffset, &vp);
69904c776e5Shannken 		if (error == 0)
700798256c9Shannken 			*depp = VTODE(vp);
701798256c9Shannken 		else
702798256c9Shannken 			*depp = NULL;
70304c776e5Shannken #endif
70404c776e5Shannken 		return error;
70598d58548Sjdolecek 	}
70698d58548Sjdolecek 
70798d58548Sjdolecek 	return 0;
70898d58548Sjdolecek 
70998d58548Sjdolecek     rollback:
71098d58548Sjdolecek 	/*
71198d58548Sjdolecek 	 * Mark all slots modified so far as deleted. Note that we
71298d58548Sjdolecek 	 * can't just call removede(), since directory is not in
71398d58548Sjdolecek 	 * consistent state.
71498d58548Sjdolecek 	 */
71525fae635Shannken 	fndoffset = mlr->mlr_fndoffset;
7168086f46eSthorpej 	rberror = msdosfs_pcbmap(ddep, de_cluster(pmp, fndoffset),
71798d58548Sjdolecek 	       &bn, NULL, &blsize);
71898d58548Sjdolecek 	if (rberror)
71998d58548Sjdolecek 		goto err_norollback;
7206e392401Smaxv 	if ((rberror = bread(pmp->pm_devvp, de_bn2kb(pmp, bn), blsize,
7215d2bff06Shannken 	    B_MODIFY, &bp)) != 0) {
72298d58548Sjdolecek 		goto err_norollback;
72398d58548Sjdolecek 	}
72498d58548Sjdolecek 	ndep = bptoep(pmp, bp, clusoffset);
72598d58548Sjdolecek 
72625fae635Shannken 	havecnt = mlr->mlr_fndcnt + 1;
72798d58548Sjdolecek 	for(i = wcnt; i <= havecnt; i++) {
72898d58548Sjdolecek 		/* mark entry as deleted */
72998d58548Sjdolecek 		ndep->deName[0] = SLOT_DELETED;
73098d58548Sjdolecek 
73198d58548Sjdolecek 		if ((fndoffset & pmp->pm_crbomask) == 0) {
73298d58548Sjdolecek 			/* we should never get here if ddep is root
73398d58548Sjdolecek 			 * directory */
73498d58548Sjdolecek 
73598d58548Sjdolecek 			if (async)
73698d58548Sjdolecek 				bdwrite(bp);
73798d58548Sjdolecek 			else if ((rberror = bwrite(bp)) != 0)
73898d58548Sjdolecek 				goto err_norollback;
73998d58548Sjdolecek 
74098d58548Sjdolecek 			fndoffset -= sizeof(struct direntry);
7418086f46eSthorpej 			rberror = msdosfs_pcbmap(ddep,
74298d58548Sjdolecek 				       de_cluster(pmp, fndoffset),
74398d58548Sjdolecek 				       &bn, 0, &blsize);
74498d58548Sjdolecek 			if (rberror)
74598d58548Sjdolecek 				goto err_norollback;
74698d58548Sjdolecek 
7474f0ca272Sscw 			rberror = bread(pmp->pm_devvp, de_bn2kb(pmp, bn),
7486e392401Smaxv 			    blsize, B_MODIFY, &bp);
74998d58548Sjdolecek 			if (rberror) {
75098d58548Sjdolecek 				goto err_norollback;
75198d58548Sjdolecek 			}
75298d58548Sjdolecek 			ndep = bptoep(pmp, bp, fndoffset);
75398d58548Sjdolecek 		} else {
75498d58548Sjdolecek 			ndep--;
75598d58548Sjdolecek 			fndoffset -= sizeof(struct direntry);
75698d58548Sjdolecek 		}
75798d58548Sjdolecek 	}
75898d58548Sjdolecek 
75998d58548Sjdolecek 	/* ignore any further error */
76098d58548Sjdolecek 	if (async)
76198d58548Sjdolecek 		(void) bdwrite(bp);
76298d58548Sjdolecek 	else
76398d58548Sjdolecek 		(void) bwrite(bp);
76498d58548Sjdolecek 
76598d58548Sjdolecek     err_norollback:
76698d58548Sjdolecek 	return error;
76798d58548Sjdolecek }
76898d58548Sjdolecek 
76998d58548Sjdolecek /*
77098d58548Sjdolecek  * Be sure a directory is empty except for "." and "..". Return 1 if empty,
77198d58548Sjdolecek  * return 0 if not empty or error.
77298d58548Sjdolecek  */
77398d58548Sjdolecek int
msdosfs_dosdirempty(struct denode * dep)7748086f46eSthorpej msdosfs_dosdirempty(struct denode *dep)
77598d58548Sjdolecek {
77698d58548Sjdolecek 	int blsize;
77798d58548Sjdolecek 	int error;
77898d58548Sjdolecek 	u_long cn;
77998d58548Sjdolecek 	daddr_t bn;
78098d58548Sjdolecek 	struct buf *bp;
78198d58548Sjdolecek 	struct msdosfsmount *pmp = dep->de_pmp;
78298d58548Sjdolecek 	struct direntry *dentp;
78398d58548Sjdolecek 
78498d58548Sjdolecek 	/*
78598d58548Sjdolecek 	 * Since the filesize field in directory entries for a directory is
78698d58548Sjdolecek 	 * zero, we just have to feel our way through the directory until
78798d58548Sjdolecek 	 * we hit end of file.
78898d58548Sjdolecek 	 */
78998d58548Sjdolecek 	for (cn = 0;; cn++) {
7908086f46eSthorpej 		if ((error = msdosfs_pcbmap(dep, cn, &bn, 0, &blsize)) != 0) {
79198d58548Sjdolecek 			if (error == E2BIG)
79298d58548Sjdolecek 				return (1);	/* it's empty */
79398d58548Sjdolecek 			return (0);
79498d58548Sjdolecek 		}
7956e392401Smaxv 		error = bread(pmp->pm_devvp, de_bn2kb(pmp, bn), blsize,
7965d2bff06Shannken 		    0, &bp);
79798d58548Sjdolecek 		if (error) {
79898d58548Sjdolecek 			return (0);
79998d58548Sjdolecek 		}
80098d58548Sjdolecek 		for (dentp = (struct direntry *)bp->b_data;
80153524e44Schristos 		     (char *)dentp < (char *)bp->b_data + blsize;
80298d58548Sjdolecek 		     dentp++) {
80398d58548Sjdolecek 			if (dentp->deName[0] != SLOT_DELETED &&
80498d58548Sjdolecek 			    (dentp->deAttributes & ATTR_VOLUME) == 0) {
80598d58548Sjdolecek 				/*
80698d58548Sjdolecek 				 * In dos directories an entry whose name
80798d58548Sjdolecek 				 * starts with SLOT_EMPTY (0) starts the
80898d58548Sjdolecek 				 * beginning of the unused part of the
80998d58548Sjdolecek 				 * directory, so we can just return that it
81098d58548Sjdolecek 				 * is empty.
81198d58548Sjdolecek 				 */
81298d58548Sjdolecek 				if (dentp->deName[0] == SLOT_EMPTY) {
8139f56dfa5Sad 					brelse(bp, 0);
81498d58548Sjdolecek 					return (1);
81598d58548Sjdolecek 				}
81698d58548Sjdolecek 				/*
81798d58548Sjdolecek 				 * Any names other than "." and ".." in a
81898d58548Sjdolecek 				 * directory mean it is not empty.
81998d58548Sjdolecek 				 */
82098d58548Sjdolecek 				if (memcmp(dentp->deName, ".          ", 11) &&
82198d58548Sjdolecek 				    memcmp(dentp->deName, "..         ", 11)) {
8229f56dfa5Sad 					brelse(bp, 0);
82398d58548Sjdolecek #ifdef MSDOSFS_DEBUG
82498d58548Sjdolecek 					printf("dosdirempty(): found %.11s, %d, %d\n",
82598d58548Sjdolecek 					    dentp->deName, dentp->deName[0],
82698d58548Sjdolecek 						dentp->deName[1]);
82798d58548Sjdolecek #endif
82898d58548Sjdolecek 					return (0);	/* not empty */
82998d58548Sjdolecek 				}
83098d58548Sjdolecek 			}
83198d58548Sjdolecek 		}
8329f56dfa5Sad 		brelse(bp, 0);
83398d58548Sjdolecek 	}
83498d58548Sjdolecek 	/* NOTREACHED */
83598d58548Sjdolecek }
83698d58548Sjdolecek 
83798d58548Sjdolecek /*
83898d58548Sjdolecek  * Read in the disk block containing the directory entry (dirclu, dirofs)
83998d58548Sjdolecek  * and return the address of the buf header, and the address of the
84098d58548Sjdolecek  * directory entry within the block.
84198d58548Sjdolecek  */
84298d58548Sjdolecek int
msdosfs_readep(struct msdosfsmount * pmp,u_long dirclust,u_long diroffset,struct buf ** bpp,struct direntry ** epp)8438086f46eSthorpej msdosfs_readep(struct msdosfsmount *pmp, u_long dirclust, u_long diroffset,
8448086f46eSthorpej     struct buf **bpp, struct direntry **epp)
84598d58548Sjdolecek {
84698d58548Sjdolecek 	int error;
84798d58548Sjdolecek 	daddr_t bn;
84898d58548Sjdolecek 	int blsize;
84998d58548Sjdolecek 
85098d58548Sjdolecek 	blsize = pmp->pm_bpcluster;
85198d58548Sjdolecek 	if (dirclust == MSDOSFSROOT
85298d58548Sjdolecek 	    && de_blk(pmp, diroffset + blsize) > pmp->pm_rootdirsize)
85398d58548Sjdolecek 		blsize = de_bn2off(pmp, pmp->pm_rootdirsize) & pmp->pm_crbomask;
85498d58548Sjdolecek 	bn = detobn(pmp, dirclust, diroffset);
8556e392401Smaxv 	if ((error = bread(pmp->pm_devvp, de_bn2kb(pmp, bn), blsize,
8565d2bff06Shannken 	    0, bpp)) != 0) {
85798d58548Sjdolecek 		*bpp = NULL;
85898d58548Sjdolecek 		return (error);
85998d58548Sjdolecek 	}
86098d58548Sjdolecek 	if (epp)
86198d58548Sjdolecek 		*epp = bptoep(pmp, *bpp, diroffset);
86298d58548Sjdolecek 	return (0);
86398d58548Sjdolecek }
86498d58548Sjdolecek 
86598d58548Sjdolecek /*
86698d58548Sjdolecek  * Read in the disk block containing the directory entry dep came from and
86798d58548Sjdolecek  * return the address of the buf header, and the address of the directory
86898d58548Sjdolecek  * entry within the block.
86998d58548Sjdolecek  */
87098d58548Sjdolecek int
msdosfs_readde(struct denode * dep,struct buf ** bpp,struct direntry ** epp)8718086f46eSthorpej msdosfs_readde(struct denode *dep, struct buf **bpp, struct direntry **epp)
87298d58548Sjdolecek {
8738086f46eSthorpej 	return (msdosfs_readep(dep->de_pmp, dep->de_dirclust, dep->de_diroffset,
87498d58548Sjdolecek 			bpp, epp));
87598d58548Sjdolecek }
87698d58548Sjdolecek 
87798d58548Sjdolecek /*
87898d58548Sjdolecek  * Remove a directory entry. At this point the file represented by the
87998d58548Sjdolecek  * directory entry to be removed is still full length until noone has it
88098d58548Sjdolecek  * open.  When the file no longer being used msdosfs_inactive() is called
88198d58548Sjdolecek  * and will truncate the file to 0 length.  When the vnode containing the
88298d58548Sjdolecek  * denode is needed for some other purpose by VFS it will call
88398d58548Sjdolecek  * msdosfs_reclaim() which will remove the denode from the denode cache.
88498d58548Sjdolecek  */
88598d58548Sjdolecek int
msdosfs_removede(struct denode * pdep,struct denode * dep,const struct msdosfs_lookup_results * mlr)8868086f46eSthorpej msdosfs_removede(struct denode *pdep, struct denode *dep,
88725fae635Shannken     const struct msdosfs_lookup_results *mlr)
88882357f6dSdsl 	/* pdep:	 directory where the entry is removed */
88982357f6dSdsl 	/* dep:	 file to be removed */
89025fae635Shannken 	/* mlr:	 position of dep in pdep from lookup */
89198d58548Sjdolecek {
89298d58548Sjdolecek 	int error;
89398d58548Sjdolecek 	struct direntry *ep;
89498d58548Sjdolecek 	struct buf *bp;
89598d58548Sjdolecek 	daddr_t bn;
89698d58548Sjdolecek 	int blsize;
89798d58548Sjdolecek 	struct msdosfsmount *pmp = pdep->de_pmp;
89825fae635Shannken 	u_long offset = mlr->mlr_fndoffset;
899929f8943Schristos #ifdef _KERNEL
90098d58548Sjdolecek 	int async = pdep->de_pmp->pm_mountp->mnt_flag & MNT_ASYNC;
901929f8943Schristos #else
902929f8943Schristos #define async 0
903929f8943Schristos #endif
90498d58548Sjdolecek 
90598d58548Sjdolecek #ifdef MSDOSFS_DEBUG
90698d58548Sjdolecek 	printf("removede(): filename %s, dep %p, offset %08lx\n",
90798d58548Sjdolecek 	    dep->de_Name, dep, offset);
90898d58548Sjdolecek #endif
90998d58548Sjdolecek 
910798256c9Shannken 	if (--dep->de_refcnt == 0) {
911798256c9Shannken #ifndef MAKEFS
912798256c9Shannken 		struct denode_key old_key = dep->de_key;
913798256c9Shannken 		struct denode_key new_key = dep->de_key;
914798256c9Shannken 
915798256c9Shannken 		KASSERT(new_key.dk_dirgen == NULL);
916798256c9Shannken 		new_key.dk_dirgen = dep;
917798256c9Shannken 		vcache_rekey_enter(pmp->pm_mountp, DETOV(dep), &old_key,
918798256c9Shannken 		    sizeof(old_key), &new_key, sizeof(new_key));
919798256c9Shannken 		dep->de_key = new_key;
920798256c9Shannken 		vcache_rekey_exit(pmp->pm_mountp, DETOV(dep), &old_key,
921798256c9Shannken 		    sizeof(old_key), &dep->de_key, sizeof(dep->de_key));
922798256c9Shannken #endif
923798256c9Shannken 	}
92498d58548Sjdolecek 	offset += sizeof(struct direntry);
92598d58548Sjdolecek 	do {
92698d58548Sjdolecek 		offset -= sizeof(struct direntry);
9278086f46eSthorpej 		error = msdosfs_pcbmap(pdep, de_cluster(pmp, offset), &bn, 0,
9288086f46eSthorpej 		    &blsize);
92998d58548Sjdolecek 		if (error)
93098d58548Sjdolecek 			return error;
9316e392401Smaxv 		error = bread(pmp->pm_devvp, de_bn2kb(pmp, bn), blsize,
9325d2bff06Shannken 		    B_MODIFY, &bp);
93398d58548Sjdolecek 		if (error) {
93498d58548Sjdolecek 			return error;
93598d58548Sjdolecek 		}
93698d58548Sjdolecek 		ep = bptoep(pmp, bp, offset);
93798d58548Sjdolecek 		/*
93898d58548Sjdolecek 		 * Check whether, if we came here the second time, i.e.
93998d58548Sjdolecek 		 * when underflowing into the previous block, the last
94098d58548Sjdolecek 		 * entry in this block is a longfilename entry, too.
94198d58548Sjdolecek 		 */
94298d58548Sjdolecek 		if (ep->deAttributes != ATTR_WIN95
94325fae635Shannken 		    && offset != mlr->mlr_fndoffset) {
9449f56dfa5Sad 			brelse(bp, 0);
94598d58548Sjdolecek 			break;
94698d58548Sjdolecek 		}
94798d58548Sjdolecek 		offset += sizeof(struct direntry);
94898d58548Sjdolecek 		while (1) {
94998d58548Sjdolecek 			/*
9507991f5a7Sandvar 			 * We are a bit aggressive here in that we delete any Win95
95198d58548Sjdolecek 			 * entries preceding this entry, not just the ones we "own".
95298d58548Sjdolecek 			 * Since these presumably aren't valid anyway,
95398d58548Sjdolecek 			 * there should be no harm.
95498d58548Sjdolecek 			 */
95598d58548Sjdolecek 			offset -= sizeof(struct direntry);
95698d58548Sjdolecek 			ep--->deName[0] = SLOT_DELETED;
95798d58548Sjdolecek 			if ((pmp->pm_flags & MSDOSFSMNT_NOWIN95)
95898d58548Sjdolecek 			    || !(offset & pmp->pm_crbomask)
95998d58548Sjdolecek 			    || ep->deAttributes != ATTR_WIN95)
96098d58548Sjdolecek 				break;
96198d58548Sjdolecek 		}
96298d58548Sjdolecek 		if (async)
96398d58548Sjdolecek 			bdwrite(bp);
96498d58548Sjdolecek 		else if ((error = bwrite(bp)) != 0)
96598d58548Sjdolecek 			return error;
96698d58548Sjdolecek 	} while (!(pmp->pm_flags & MSDOSFSMNT_NOWIN95)
96798d58548Sjdolecek 	    && !(offset & pmp->pm_crbomask)
96898d58548Sjdolecek 	    && offset);
96998d58548Sjdolecek 	return 0;
97098d58548Sjdolecek }
97198d58548Sjdolecek 
97298d58548Sjdolecek /*
97398d58548Sjdolecek  * Create a unique DOS name in dvp
97498d58548Sjdolecek  */
97598d58548Sjdolecek int
msdosfs_uniqdosname(struct denode * dep,struct componentname * cnp,u_char * cp)9768086f46eSthorpej msdosfs_uniqdosname(struct denode *dep, struct componentname *cnp, u_char *cp)
97798d58548Sjdolecek {
97898d58548Sjdolecek 	struct msdosfsmount *pmp = dep->de_pmp;
97998d58548Sjdolecek 	struct direntry *dentp;
98098d58548Sjdolecek 	int gen;
98198d58548Sjdolecek 	int blsize;
98298d58548Sjdolecek 	u_long cn;
98398d58548Sjdolecek 	daddr_t bn;
98498d58548Sjdolecek 	struct buf *bp;
98598d58548Sjdolecek 	int error;
98698d58548Sjdolecek 
98798d58548Sjdolecek 	for (gen = 1;; gen++) {
98898d58548Sjdolecek 		/*
98998d58548Sjdolecek 		 * Generate DOS name with generation number
99098d58548Sjdolecek 		 */
9918086f46eSthorpej 		if (!msdosfs_unix2dosfn((const u_char *)cnp->cn_nameptr, cp,
99298d58548Sjdolecek 		    cnp->cn_namelen, gen))
99398d58548Sjdolecek 			return gen == 1 ? EINVAL : EEXIST;
99498d58548Sjdolecek 
99598d58548Sjdolecek 		/*
99698d58548Sjdolecek 		 * Now look for a dir entry with this exact name
99798d58548Sjdolecek 		 */
99898d58548Sjdolecek 		for (cn = error = 0; !error; cn++) {
9998086f46eSthorpej 			if ((error = msdosfs_pcbmap(dep, cn, &bn, 0,
10008086f46eSthorpej 			    &blsize)) != 0) {
100198d58548Sjdolecek 				if (error == E2BIG)	/* EOF reached and not found */
100298d58548Sjdolecek 					return 0;
100398d58548Sjdolecek 				return error;
100498d58548Sjdolecek 			}
10054f0ca272Sscw 			error = bread(pmp->pm_devvp, de_bn2kb(pmp, bn), blsize,
10066e392401Smaxv 			    0, &bp);
100798d58548Sjdolecek 			if (error) {
100898d58548Sjdolecek 				return error;
100998d58548Sjdolecek 			}
101098d58548Sjdolecek 			for (dentp = (struct direntry *)bp->b_data;
101153524e44Schristos 			     (char *)dentp < (char *)bp->b_data + blsize;
101298d58548Sjdolecek 			     dentp++) {
101398d58548Sjdolecek 				if (dentp->deName[0] == SLOT_EMPTY) {
101498d58548Sjdolecek 					/*
101598d58548Sjdolecek 					 * Last used entry and not found
101698d58548Sjdolecek 					 */
10179f56dfa5Sad 					brelse(bp, 0);
101898d58548Sjdolecek 					return 0;
101998d58548Sjdolecek 				}
102098d58548Sjdolecek 				/*
102198d58548Sjdolecek 				 * Ignore volume labels and Win95 entries
102298d58548Sjdolecek 				 */
102398d58548Sjdolecek 				if (dentp->deAttributes & ATTR_VOLUME)
102498d58548Sjdolecek 					continue;
102598d58548Sjdolecek 				if (!memcmp(dentp->deName, cp, 11)) {
102698d58548Sjdolecek 					error = EEXIST;
102798d58548Sjdolecek 					break;
102898d58548Sjdolecek 				}
102998d58548Sjdolecek 			}
10309f56dfa5Sad 			brelse(bp, 0);
103198d58548Sjdolecek 		}
103298d58548Sjdolecek 	}
103398d58548Sjdolecek }
103498d58548Sjdolecek 
103598d58548Sjdolecek /*
103698d58548Sjdolecek  * Find any Win'95 long filename entry in directory dep
103798d58548Sjdolecek  */
103898d58548Sjdolecek int
msdosfs_findwin95(struct denode * dep)10398086f46eSthorpej msdosfs_findwin95(struct denode *dep)
104098d58548Sjdolecek {
104198d58548Sjdolecek 	struct msdosfsmount *pmp = dep->de_pmp;
104298d58548Sjdolecek 	struct direntry *dentp;
1043a85de651Sjmcneill 	int blsize, win95;
104498d58548Sjdolecek 	u_long cn;
104598d58548Sjdolecek 	daddr_t bn;
104698d58548Sjdolecek 	struct buf *bp;
104798d58548Sjdolecek 
1048a85de651Sjmcneill 	win95 = 1;
104998d58548Sjdolecek 	/*
105098d58548Sjdolecek 	 * Read through the directory looking for Win'95 entries
105198d58548Sjdolecek 	 * XXX Note: Error currently handled just as EOF
105298d58548Sjdolecek 	 */
105398d58548Sjdolecek 	for (cn = 0;; cn++) {
10548086f46eSthorpej 		if (msdosfs_pcbmap(dep, cn, &bn, 0, &blsize))
1055a85de651Sjmcneill 			return win95;
10566e392401Smaxv 		if (bread(pmp->pm_devvp, de_bn2kb(pmp, bn), blsize,
10575d2bff06Shannken 		    0, &bp)) {
1058a85de651Sjmcneill 			return win95;
105998d58548Sjdolecek 		}
106098d58548Sjdolecek 		for (dentp = (struct direntry *)bp->b_data;
106153524e44Schristos 		     (char *)dentp < (char *)bp->b_data + blsize;
106298d58548Sjdolecek 		     dentp++) {
106398d58548Sjdolecek 			if (dentp->deName[0] == SLOT_EMPTY) {
106498d58548Sjdolecek 				/*
106598d58548Sjdolecek 				 * Last used entry and not found
106698d58548Sjdolecek 				 */
10679f56dfa5Sad 				brelse(bp, 0);
1068a85de651Sjmcneill 				return win95;
106998d58548Sjdolecek 			}
107098d58548Sjdolecek 			if (dentp->deName[0] == SLOT_DELETED) {
107198d58548Sjdolecek 				/*
107298d58548Sjdolecek 				 * Ignore deleted files
107398d58548Sjdolecek 				 * Note: might be an indication of Win'95
107498d58548Sjdolecek 				 * anyway	XXX
107598d58548Sjdolecek 				 */
107698d58548Sjdolecek 				continue;
107798d58548Sjdolecek 			}
107898d58548Sjdolecek 			if (dentp->deAttributes == ATTR_WIN95) {
10799f56dfa5Sad 				brelse(bp, 0);
108098d58548Sjdolecek 				return 1;
108198d58548Sjdolecek 			}
1082a85de651Sjmcneill 			win95 = 0;
108398d58548Sjdolecek 		}
10849f56dfa5Sad 		brelse(bp, 0);
108598d58548Sjdolecek 	}
108698d58548Sjdolecek }
1087