msdosfs_lookup.c revision 144298
1/* $FreeBSD: head/sys/fs/msdosfs/msdosfs_lookup.c 144298 2005-03-29 13:04:00Z jeff $ */
2/*	$NetBSD: msdosfs_lookup.c,v 1.37 1997/11/17 15:36:54 ws Exp $	*/
3
4/*-
5 * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank.
6 * Copyright (C) 1994, 1995, 1997 TooLs GmbH.
7 * All rights reserved.
8 * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below).
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 *    must display the following acknowledgement:
20 *	This product includes software developed by TooLs GmbH.
21 * 4. The name of TooLs GmbH may not be used to endorse or promote products
22 *    derived from this software without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
25 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
26 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
27 * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
29 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
30 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
31 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
32 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
33 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 */
35/*-
36 * Written by Paul Popelka (paulp@uts.amdahl.com)
37 *
38 * You can do anything you want with this software, just don't say you wrote
39 * it, and don't remove this notice.
40 *
41 * This software is provided "as is".
42 *
43 * The author supplies this software to be publicly redistributed on the
44 * understanding that the author is not responsible for the correct
45 * functioning of this software in any circumstances and is not liable for
46 * any damages caused by this software.
47 *
48 * October 1992
49 */
50
51#include <sys/param.h>
52#include <sys/systm.h>
53#include <sys/namei.h>
54#include <sys/bio.h>
55#include <sys/buf.h>
56#include <sys/vnode.h>
57#include <sys/mount.h>
58
59#include <fs/msdosfs/bpb.h>
60#include <fs/msdosfs/msdosfsmount.h>
61#include <fs/msdosfs/direntry.h>
62#include <fs/msdosfs/denode.h>
63#include <fs/msdosfs/fat.h>
64
65/*
66 * When we search a directory the blocks containing directory entries are
67 * read and examined.  The directory entries contain information that would
68 * normally be in the inode of a unix filesystem.  This means that some of
69 * a directory's contents may also be in memory resident denodes (sort of
70 * an inode).  This can cause problems if we are searching while some other
71 * process is modifying a directory.  To prevent one process from accessing
72 * incompletely modified directory information we depend upon being the
73 * sole owner of a directory block.  bread/brelse provide this service.
74 * This being the case, when a process modifies a directory it must first
75 * acquire the disk block that contains the directory entry to be modified.
76 * Then update the disk block and the denode, and then write the disk block
77 * out to disk.  This way disk blocks containing directory entries and in
78 * memory denode's will be in synch.
79 */
80int
81msdosfs_lookup(ap)
82	struct vop_cachedlookup_args /* {
83		struct vnode *a_dvp;
84		struct vnode **a_vpp;
85		struct componentname *a_cnp;
86	} */ *ap;
87{
88	struct vnode *vdp = ap->a_dvp;
89	struct vnode **vpp = ap->a_vpp;
90	struct componentname *cnp = ap->a_cnp;
91	daddr_t bn;
92	int error;
93	int slotcount;
94	int slotoffset = 0;
95	int frcn;
96	u_long cluster;
97	int blkoff;
98	int diroff;
99	int blsize;
100	int isadir;		/* ~0 if found direntry is a directory	 */
101	u_long scn;		/* starting cluster number		 */
102	struct vnode *pdp;
103	struct denode *dp;
104	struct denode *tdp;
105	struct msdosfsmount *pmp;
106	struct buf *bp = 0;
107	struct direntry *dep = NULL;
108	u_char dosfilename[12];
109	int flags = cnp->cn_flags;
110	int nameiop = cnp->cn_nameiop;
111	struct thread *td = cnp->cn_thread;
112	int unlen;
113
114	int wincnt = 1;
115	int chksum = -1, chksum_ok;
116	int olddos = 1;
117
118#ifdef MSDOSFS_DEBUG
119	printf("msdosfs_lookup(): looking for %s\n", cnp->cn_nameptr);
120#endif
121	dp = VTODE(vdp);
122	pmp = dp->de_pmp;
123	*vpp = NULL;
124#ifdef MSDOSFS_DEBUG
125	printf("msdosfs_lookup(): vdp %p, dp %p, Attr %02x\n",
126	    vdp, dp, dp->de_Attributes);
127#endif
128
129	/*
130	 * If they are going after the . or .. entry in the root directory,
131	 * they won't find it.  DOS filesystems don't have them in the root
132	 * directory.  So, we fake it. deget() is in on this scam too.
133	 */
134	if ((vdp->v_vflag & VV_ROOT) && cnp->cn_nameptr[0] == '.' &&
135	    (cnp->cn_namelen == 1 ||
136		(cnp->cn_namelen == 2 && cnp->cn_nameptr[1] == '.'))) {
137		isadir = ATTR_DIRECTORY;
138		scn = MSDOSFSROOT;
139#ifdef MSDOSFS_DEBUG
140		printf("msdosfs_lookup(): looking for . or .. in root directory\n");
141#endif
142		cluster = MSDOSFSROOT;
143		blkoff = MSDOSFSROOT_OFS;
144		goto foundroot;
145	}
146
147	switch (unix2dosfn((const u_char *)cnp->cn_nameptr, dosfilename,
148	    cnp->cn_namelen, 0, pmp)) {
149	case 0:
150		return (EINVAL);
151	case 1:
152		break;
153	case 2:
154		wincnt = winSlotCnt((const u_char *)cnp->cn_nameptr,
155		    cnp->cn_namelen, pmp) + 1;
156		break;
157	case 3:
158		olddos = 0;
159		wincnt = winSlotCnt((const u_char *)cnp->cn_nameptr,
160		    cnp->cn_namelen, pmp) + 1;
161		break;
162	}
163	if (pmp->pm_flags & MSDOSFSMNT_SHORTNAME) {
164		wincnt = 1;
165		olddos = 1;
166	}
167	unlen = winLenFixup(cnp->cn_nameptr, cnp->cn_namelen);
168
169	/*
170	 * Suppress search for slots unless creating
171	 * file and at end of pathname, in which case
172	 * we watch for a place to put the new file in
173	 * case it doesn't already exist.
174	 */
175	slotcount = wincnt;
176	if ((nameiop == CREATE || nameiop == RENAME) &&
177	    (flags & ISLASTCN))
178		slotcount = 0;
179
180#ifdef MSDOSFS_DEBUG
181	printf("msdosfs_lookup(): dos version of filename %s, length %ld\n",
182	    dosfilename, cnp->cn_namelen);
183#endif
184	/*
185	 * Search the directory pointed at by vdp for the name pointed at
186	 * by cnp->cn_nameptr.
187	 */
188	tdp = NULL;
189	mbnambuf_init();
190	/*
191	 * The outer loop ranges over the clusters that make up the
192	 * directory.  Note that the root directory is different from all
193	 * other directories.  It has a fixed number of blocks that are not
194	 * part of the pool of allocatable clusters.  So, we treat it a
195	 * little differently. The root directory starts at "cluster" 0.
196	 */
197	diroff = 0;
198	for (frcn = 0;; frcn++) {
199		error = pcbmap(dp, frcn, &bn, &cluster, &blsize);
200		if (error) {
201			if (error == E2BIG)
202				break;
203			return (error);
204		}
205		error = bread(pmp->pm_devvp, bn, blsize, NOCRED, &bp);
206		if (error) {
207			brelse(bp);
208			return (error);
209		}
210		for (blkoff = 0; blkoff < blsize;
211		     blkoff += sizeof(struct direntry),
212		     diroff += sizeof(struct direntry)) {
213			dep = (struct direntry *)(bp->b_data + blkoff);
214			/*
215			 * If the slot is empty and we are still looking
216			 * for an empty then remember this one.  If the
217			 * slot is not empty then check to see if it
218			 * matches what we are looking for.  If the slot
219			 * has never been filled with anything, then the
220			 * remainder of the directory has never been used,
221			 * so there is no point in searching it.
222			 */
223			if (dep->deName[0] == SLOT_EMPTY ||
224			    dep->deName[0] == SLOT_DELETED) {
225				/*
226				 * Drop memory of previous long matches
227				 */
228				chksum = -1;
229				mbnambuf_init();
230
231				if (slotcount < wincnt) {
232					slotcount++;
233					slotoffset = diroff;
234				}
235				if (dep->deName[0] == SLOT_EMPTY) {
236					brelse(bp);
237					goto notfound;
238				}
239			} else {
240				/*
241				 * If there wasn't enough space for our winentries,
242				 * forget about the empty space
243				 */
244				if (slotcount < wincnt)
245					slotcount = 0;
246
247				/*
248				 * Check for Win95 long filename entry
249				 */
250				if (dep->deAttributes == ATTR_WIN95) {
251					if (pmp->pm_flags & MSDOSFSMNT_SHORTNAME)
252						continue;
253
254					chksum = win2unixfn((struct winentry *)dep,
255							    chksum,
256							    pmp);
257					continue;
258				}
259
260				chksum = winChkName((const u_char *)cnp->cn_nameptr,
261						    unlen,
262						    chksum,
263						    pmp);
264				if (chksum == -2) {
265					chksum = -1;
266					continue;
267				}
268
269				/*
270				 * Ignore volume labels (anywhere, not just
271				 * the root directory).
272				 */
273				if (dep->deAttributes & ATTR_VOLUME) {
274					chksum = -1;
275					continue;
276				}
277
278				/*
279				 * Check for a checksum or name match
280				 */
281				chksum_ok = (chksum == winChksum(dep->deName));
282				if (!chksum_ok
283				    && (!olddos || bcmp(dosfilename, dep->deName, 11))) {
284					chksum = -1;
285					continue;
286				}
287#ifdef MSDOSFS_DEBUG
288				printf("msdosfs_lookup(): match blkoff %d, diroff %d\n",
289				    blkoff, diroff);
290#endif
291				/*
292				 * Remember where this directory
293				 * entry came from for whoever did
294				 * this lookup.
295				 */
296				dp->de_fndoffset = diroff;
297				if (chksum_ok && nameiop == RENAME) {
298					/*
299					 * Target had correct long name
300					 * directory entries, reuse them
301					 * as needed.
302					 */
303					dp->de_fndcnt = wincnt - 1;
304				} else {
305					/*
306					 * Long name directory entries
307					 * not present or corrupt, can only
308					 * reuse dos directory entry.
309					 */
310					dp->de_fndcnt = 0;
311				}
312
313				goto found;
314			}
315		}	/* for (blkoff = 0; .... */
316		/*
317		 * Release the buffer holding the directory cluster just
318		 * searched.
319		 */
320		brelse(bp);
321	}	/* for (frcn = 0; ; frcn++) */
322
323notfound:
324	/*
325	 * We hold no disk buffers at this point.
326	 */
327
328	/*
329	 * Fixup the slot description to point to the place where
330	 * we might put the new DOS direntry (putting the Win95
331	 * long name entries before that)
332	 */
333	if (!slotcount) {
334		slotcount = 1;
335		slotoffset = diroff;
336	}
337	if (wincnt > slotcount)
338		slotoffset += sizeof(struct direntry) * (wincnt - slotcount);
339
340	/*
341	 * If we get here we didn't find the entry we were looking for. But
342	 * that's ok if we are creating or renaming and are at the end of
343	 * the pathname and the directory hasn't been removed.
344	 */
345#ifdef MSDOSFS_DEBUG
346	printf("msdosfs_lookup(): op %d, refcnt %ld\n",
347	    nameiop, dp->de_refcnt);
348	printf("               slotcount %d, slotoffset %d\n",
349	       slotcount, slotoffset);
350#endif
351	if ((nameiop == CREATE || nameiop == RENAME) &&
352	    (flags & ISLASTCN) && dp->de_refcnt != 0) {
353		/*
354		 * Access for write is interpreted as allowing
355		 * creation of files in the directory.
356		 */
357		error = VOP_ACCESS(vdp, VWRITE, cnp->cn_cred, cnp->cn_thread);
358		if (error)
359			return (error);
360		/*
361		 * Return an indication of where the new directory
362		 * entry should be put.
363		 */
364		dp->de_fndoffset = slotoffset;
365		dp->de_fndcnt = wincnt - 1;
366
367		/*
368		 * We return with the directory locked, so that
369		 * the parameters we set up above will still be
370		 * valid if we actually decide to do a direnter().
371		 * We return ni_vp == NULL to indicate that the entry
372		 * does not currently exist; we leave a pointer to
373		 * the (locked) directory inode in ndp->ni_dvp.
374		 * The pathname buffer is saved so that the name
375		 * can be obtained later.
376		 *
377		 * NB - if the directory is unlocked, then this
378		 * information cannot be used.
379		 */
380		cnp->cn_flags |= SAVENAME;
381		return (EJUSTRETURN);
382	}
383	/*
384	 * Insert name into cache (as non-existent) if appropriate.
385	 */
386	if ((cnp->cn_flags & MAKEENTRY) && nameiop != CREATE)
387		cache_enter(vdp, *vpp, cnp);
388	return (ENOENT);
389
390found:
391	/*
392	 * NOTE:  We still have the buffer with matched directory entry at
393	 * this point.
394	 */
395	isadir = dep->deAttributes & ATTR_DIRECTORY;
396	scn = getushort(dep->deStartCluster);
397	if (FAT32(pmp)) {
398		scn |= getushort(dep->deHighClust) << 16;
399		if (scn == pmp->pm_rootdirblk) {
400			/*
401			 * There should actually be 0 here.
402			 * Just ignore the error.
403			 */
404			scn = MSDOSFSROOT;
405		}
406	}
407
408	if (isadir) {
409		cluster = scn;
410		if (cluster == MSDOSFSROOT)
411			blkoff = MSDOSFSROOT_OFS;
412		else
413			blkoff = 0;
414	} else if (cluster == MSDOSFSROOT)
415		blkoff = diroff;
416
417	/*
418	 * Now release buf to allow deget to read the entry again.
419	 * Reserving it here and giving it to deget could result
420	 * in a deadlock.
421	 */
422	brelse(bp);
423	bp = 0;
424
425foundroot:
426	/*
427	 * If we entered at foundroot, then we are looking for the . or ..
428	 * entry of the filesystems root directory.  isadir and scn were
429	 * setup before jumping here.  And, bp is already null.
430	 */
431	if (FAT32(pmp) && scn == MSDOSFSROOT)
432		scn = pmp->pm_rootdirblk;
433
434	/*
435	 * If deleting, and at end of pathname, return
436	 * parameters which can be used to remove file.
437	 */
438	if (nameiop == DELETE && (flags & ISLASTCN)) {
439		/*
440		 * Don't allow deleting the root.
441		 */
442		if (blkoff == MSDOSFSROOT_OFS)
443			return EROFS;				/* really? XXX */
444
445		/*
446		 * Write access to directory required to delete files.
447		 */
448		error = VOP_ACCESS(vdp, VWRITE, cnp->cn_cred, cnp->cn_thread);
449		if (error)
450			return (error);
451
452		/*
453		 * Return pointer to current entry in dp->i_offset.
454		 * Save directory inode pointer in ndp->ni_dvp for dirremove().
455		 */
456		if (dp->de_StartCluster == scn && isadir) {	/* "." */
457			VREF(vdp);
458			*vpp = vdp;
459			return (0);
460		}
461		error = deget(pmp, cluster, blkoff, &tdp);
462		if (error)
463			return (error);
464		*vpp = DETOV(tdp);
465		return (0);
466	}
467
468	/*
469	 * If rewriting (RENAME), return the inode and the
470	 * information required to rewrite the present directory
471	 * Must get inode of directory entry to verify it's a
472	 * regular file, or empty directory.
473	 */
474	if (nameiop == RENAME && (flags & ISLASTCN)) {
475		if (blkoff == MSDOSFSROOT_OFS)
476			return EROFS;				/* really? XXX */
477
478		error = VOP_ACCESS(vdp, VWRITE, cnp->cn_cred, cnp->cn_thread);
479		if (error)
480			return (error);
481
482		/*
483		 * Careful about locking second inode.
484		 * This can only occur if the target is ".".
485		 */
486		if (dp->de_StartCluster == scn && isadir)
487			return (EISDIR);
488
489		if ((error = deget(pmp, cluster, blkoff, &tdp)) != 0)
490			return (error);
491		*vpp = DETOV(tdp);
492		cnp->cn_flags |= SAVENAME;
493		return (0);
494	}
495
496	/*
497	 * Step through the translation in the name.  We do not `vput' the
498	 * directory because we may need it again if a symbolic link
499	 * is relative to the current directory.  Instead we save it
500	 * unlocked as "pdp".  We must get the target inode before unlocking
501	 * the directory to insure that the inode will not be removed
502	 * before we get it.  We prevent deadlock by always fetching
503	 * inodes from the root, moving down the directory tree. Thus
504	 * when following backward pointers ".." we must unlock the
505	 * parent directory before getting the requested directory.
506	 * There is a potential race condition here if both the current
507	 * and parent directories are removed before the VFS_VGET for the
508	 * inode associated with ".." returns.  We hope that this occurs
509	 * infrequently since we cannot avoid this race condition without
510	 * implementing a sophisticated deadlock detection algorithm.
511	 * Note also that this simple deadlock detection scheme will not
512	 * work if the filesystem has any hard links other than ".."
513	 * that point backwards in the directory structure.
514	 */
515	pdp = vdp;
516	if (flags & ISDOTDOT) {
517		VOP_UNLOCK(pdp, 0, td);
518		error = deget(pmp, cluster, blkoff,  &tdp);
519		if (error) {
520			vn_lock(pdp, LK_EXCLUSIVE | LK_RETRY, td);
521			return (error);
522		}
523		*vpp = DETOV(tdp);
524	} else if (dp->de_StartCluster == scn && isadir) {
525		VREF(vdp);	/* we want ourself, ie "." */
526		*vpp = vdp;
527	} else {
528		if ((error = deget(pmp, cluster, blkoff, &tdp)) != 0)
529			return (error);
530		*vpp = DETOV(tdp);
531	}
532
533	/*
534	 * Insert name into cache if appropriate.
535	 */
536	if (cnp->cn_flags & MAKEENTRY)
537		cache_enter(vdp, *vpp, cnp);
538	return (0);
539}
540
541/*
542 * dep  - directory entry to copy into the directory
543 * ddep - directory to add to
544 * depp - return the address of the denode for the created directory entry
545 *	  if depp != 0
546 * cnp  - componentname needed for Win95 long filenames
547 */
548int
549createde(dep, ddep, depp, cnp)
550	struct denode *dep;
551	struct denode *ddep;
552	struct denode **depp;
553	struct componentname *cnp;
554{
555	int error;
556	u_long dirclust, diroffset;
557	struct direntry *ndep;
558	struct msdosfsmount *pmp = ddep->de_pmp;
559	struct buf *bp;
560	daddr_t bn;
561	int blsize;
562
563#ifdef MSDOSFS_DEBUG
564	printf("createde(dep %p, ddep %p, depp %p, cnp %p)\n",
565	    dep, ddep, depp, cnp);
566#endif
567
568	/*
569	 * If no space left in the directory then allocate another cluster
570	 * and chain it onto the end of the file.  There is one exception
571	 * to this.  That is, if the root directory has no more space it
572	 * can NOT be expanded.  extendfile() checks for and fails attempts
573	 * to extend the root directory.  We just return an error in that
574	 * case.
575	 */
576	if (ddep->de_fndoffset >= ddep->de_FileSize) {
577		diroffset = ddep->de_fndoffset + sizeof(struct direntry)
578		    - ddep->de_FileSize;
579		dirclust = de_clcount(pmp, diroffset);
580		error = extendfile(ddep, dirclust, 0, 0, DE_CLEAR);
581		if (error) {
582			(void)detrunc(ddep, ddep->de_FileSize, 0, NOCRED, NULL);
583			return error;
584		}
585
586		/*
587		 * Update the size of the directory
588		 */
589		ddep->de_FileSize += de_cn2off(pmp, dirclust);
590	}
591
592	/*
593	 * We just read in the cluster with space.  Copy the new directory
594	 * entry in.  Then write it to disk. NOTE:  DOS directories
595	 * do not get smaller as clusters are emptied.
596	 */
597	error = pcbmap(ddep, de_cluster(pmp, ddep->de_fndoffset),
598		       &bn, &dirclust, &blsize);
599	if (error)
600		return error;
601	diroffset = ddep->de_fndoffset;
602	if (dirclust != MSDOSFSROOT)
603		diroffset &= pmp->pm_crbomask;
604	if ((error = bread(pmp->pm_devvp, bn, blsize, NOCRED, &bp)) != 0) {
605		brelse(bp);
606		return error;
607	}
608	ndep = bptoep(pmp, bp, ddep->de_fndoffset);
609
610	DE_EXTERNALIZE(ndep, dep);
611
612	/*
613	 * Now write the Win95 long name
614	 */
615	if (ddep->de_fndcnt > 0) {
616		u_int8_t chksum = winChksum(ndep->deName);
617		const u_char *un = (const u_char *)cnp->cn_nameptr;
618		int unlen = cnp->cn_namelen;
619		int cnt = 1;
620
621		while (--ddep->de_fndcnt >= 0) {
622			if (!(ddep->de_fndoffset & pmp->pm_crbomask)) {
623				if ((error = bwrite(bp)) != 0)
624					return error;
625
626				ddep->de_fndoffset -= sizeof(struct direntry);
627				error = pcbmap(ddep,
628					       de_cluster(pmp,
629							  ddep->de_fndoffset),
630					       &bn, 0, &blsize);
631				if (error)
632					return error;
633
634				error = bread(pmp->pm_devvp, bn, blsize,
635					      NOCRED, &bp);
636				if (error) {
637					brelse(bp);
638					return error;
639				}
640				ndep = bptoep(pmp, bp, ddep->de_fndoffset);
641			} else {
642				ndep--;
643				ddep->de_fndoffset -= sizeof(struct direntry);
644			}
645			if (!unix2winfn(un, unlen, (struct winentry *)ndep,
646					cnt++, chksum, pmp))
647				break;
648		}
649	}
650
651	if ((error = bwrite(bp)) != 0)
652		return error;
653
654	/*
655	 * If they want us to return with the denode gotten.
656	 */
657	if (depp) {
658		if (dep->de_Attributes & ATTR_DIRECTORY) {
659			dirclust = dep->de_StartCluster;
660			if (FAT32(pmp) && dirclust == pmp->pm_rootdirblk)
661				dirclust = MSDOSFSROOT;
662			if (dirclust == MSDOSFSROOT)
663				diroffset = MSDOSFSROOT_OFS;
664			else
665				diroffset = 0;
666		}
667		return deget(pmp, dirclust, diroffset, depp);
668	}
669
670	return 0;
671}
672
673/*
674 * Be sure a directory is empty except for "." and "..". Return 1 if empty,
675 * return 0 if not empty or error.
676 */
677int
678dosdirempty(dep)
679	struct denode *dep;
680{
681	int blsize;
682	int error;
683	u_long cn;
684	daddr_t bn;
685	struct buf *bp;
686	struct msdosfsmount *pmp = dep->de_pmp;
687	struct direntry *dentp;
688
689	/*
690	 * Since the filesize field in directory entries for a directory is
691	 * zero, we just have to feel our way through the directory until
692	 * we hit end of file.
693	 */
694	for (cn = 0;; cn++) {
695		if ((error = pcbmap(dep, cn, &bn, 0, &blsize)) != 0) {
696			if (error == E2BIG)
697				return (1);	/* it's empty */
698			return (0);
699		}
700		error = bread(pmp->pm_devvp, bn, blsize, NOCRED, &bp);
701		if (error) {
702			brelse(bp);
703			return (0);
704		}
705		for (dentp = (struct direntry *)bp->b_data;
706		     (char *)dentp < bp->b_data + blsize;
707		     dentp++) {
708			if (dentp->deName[0] != SLOT_DELETED &&
709			    (dentp->deAttributes & ATTR_VOLUME) == 0) {
710				/*
711				 * In dos directories an entry whose name
712				 * starts with SLOT_EMPTY (0) starts the
713				 * beginning of the unused part of the
714				 * directory, so we can just return that it
715				 * is empty.
716				 */
717				if (dentp->deName[0] == SLOT_EMPTY) {
718					brelse(bp);
719					return (1);
720				}
721				/*
722				 * Any names other than "." and ".." in a
723				 * directory mean it is not empty.
724				 */
725				if (bcmp(dentp->deName, ".          ", 11) &&
726				    bcmp(dentp->deName, "..         ", 11)) {
727					brelse(bp);
728#ifdef MSDOSFS_DEBUG
729					printf("dosdirempty(): entry found %02x, %02x\n",
730					    dentp->deName[0], dentp->deName[1]);
731#endif
732					return (0);	/* not empty */
733				}
734			}
735		}
736		brelse(bp);
737	}
738	/* NOTREACHED */
739}
740
741/*
742 * Check to see if the directory described by target is in some
743 * subdirectory of source.  This prevents something like the following from
744 * succeeding and leaving a bunch or files and directories orphaned. mv
745 * /a/b/c /a/b/c/d/e/f Where c and f are directories.
746 *
747 * source - the inode for /a/b/c
748 * target - the inode for /a/b/c/d/e/f
749 *
750 * Returns 0 if target is NOT a subdirectory of source.
751 * Otherwise returns a non-zero error number.
752 * The target inode is always unlocked on return.
753 */
754int
755doscheckpath(source, target)
756	struct denode *source;
757	struct denode *target;
758{
759	daddr_t scn;
760	struct msdosfsmount *pmp;
761	struct direntry *ep;
762	struct denode *dep;
763	struct buf *bp = NULL;
764	int error = 0;
765
766	dep = target;
767	if ((target->de_Attributes & ATTR_DIRECTORY) == 0 ||
768	    (source->de_Attributes & ATTR_DIRECTORY) == 0) {
769		error = ENOTDIR;
770		goto out;
771	}
772	if (dep->de_StartCluster == source->de_StartCluster) {
773		error = EEXIST;
774		goto out;
775	}
776	if (dep->de_StartCluster == MSDOSFSROOT)
777		goto out;
778	pmp = dep->de_pmp;
779#ifdef	DIAGNOSTIC
780	if (pmp != source->de_pmp)
781		panic("doscheckpath: source and target on different filesystems");
782#endif
783	if (FAT32(pmp) && dep->de_StartCluster == pmp->pm_rootdirblk)
784		goto out;
785
786	for (;;) {
787		if ((dep->de_Attributes & ATTR_DIRECTORY) == 0) {
788			error = ENOTDIR;
789			break;
790		}
791		scn = dep->de_StartCluster;
792		error = bread(pmp->pm_devvp, cntobn(pmp, scn),
793			      pmp->pm_bpcluster, NOCRED, &bp);
794		if (error)
795			break;
796
797		ep = (struct direntry *) bp->b_data + 1;
798		if ((ep->deAttributes & ATTR_DIRECTORY) == 0 ||
799		    bcmp(ep->deName, "..         ", 11) != 0) {
800			error = ENOTDIR;
801			break;
802		}
803		scn = getushort(ep->deStartCluster);
804		if (FAT32(pmp))
805			scn |= getushort(ep->deHighClust) << 16;
806
807		if (scn == source->de_StartCluster) {
808			error = EINVAL;
809			break;
810		}
811		if (scn == MSDOSFSROOT)
812			break;
813		if (FAT32(pmp) && scn == pmp->pm_rootdirblk) {
814			/*
815			 * scn should be 0 in this case,
816			 * but we silently ignore the error.
817			 */
818			break;
819		}
820
821		vput(DETOV(dep));
822		brelse(bp);
823		bp = NULL;
824		/* NOTE: deget() clears dep on error */
825		if ((error = deget(pmp, scn, 0, &dep)) != 0)
826			break;
827	}
828out:;
829	if (bp)
830		brelse(bp);
831	if (error == ENOTDIR)
832		printf("doscheckpath(): .. not a directory?\n");
833	if (dep != NULL)
834		vput(DETOV(dep));
835	return (error);
836}
837
838/*
839 * Read in the disk block containing the directory entry (dirclu, dirofs)
840 * and return the address of the buf header, and the address of the
841 * directory entry within the block.
842 */
843int
844readep(pmp, dirclust, diroffset, bpp, epp)
845	struct msdosfsmount *pmp;
846	u_long dirclust, diroffset;
847	struct buf **bpp;
848	struct direntry **epp;
849{
850	int error;
851	daddr_t bn;
852	int blsize;
853
854	blsize = pmp->pm_bpcluster;
855	if (dirclust == MSDOSFSROOT
856	    && de_blk(pmp, diroffset + blsize) > pmp->pm_rootdirsize)
857		blsize = de_bn2off(pmp, pmp->pm_rootdirsize) & pmp->pm_crbomask;
858	bn = detobn(pmp, dirclust, diroffset);
859	if ((error = bread(pmp->pm_devvp, bn, blsize, NOCRED, bpp)) != 0) {
860		brelse(*bpp);
861		*bpp = NULL;
862		return (error);
863	}
864	if (epp)
865		*epp = bptoep(pmp, *bpp, diroffset);
866	return (0);
867}
868
869/*
870 * Read in the disk block containing the directory entry dep came from and
871 * return the address of the buf header, and the address of the directory
872 * entry within the block.
873 */
874int
875readde(dep, bpp, epp)
876	struct denode *dep;
877	struct buf **bpp;
878	struct direntry **epp;
879{
880
881	return (readep(dep->de_pmp, dep->de_dirclust, dep->de_diroffset,
882	    bpp, epp));
883}
884
885/*
886 * Remove a directory entry. At this point the file represented by the
887 * directory entry to be removed is still full length until noone has it
888 * open.  When the file no longer being used msdosfs_inactive() is called
889 * and will truncate the file to 0 length.  When the vnode containing the
890 * denode is needed for some other purpose by VFS it will call
891 * msdosfs_reclaim() which will remove the denode from the denode cache.
892 */
893int
894removede(pdep, dep)
895	struct denode *pdep;	/* directory where the entry is removed */
896	struct denode *dep;	/* file to be removed */
897{
898	int error;
899	struct direntry *ep;
900	struct buf *bp;
901	daddr_t bn;
902	int blsize;
903	struct msdosfsmount *pmp = pdep->de_pmp;
904	u_long offset = pdep->de_fndoffset;
905
906#ifdef MSDOSFS_DEBUG
907	printf("removede(): filename %s, dep %p, offset %08lx\n",
908	    dep->de_Name, dep, offset);
909#endif
910
911	dep->de_refcnt--;
912	offset += sizeof(struct direntry);
913	do {
914		offset -= sizeof(struct direntry);
915		error = pcbmap(pdep, de_cluster(pmp, offset), &bn, 0, &blsize);
916		if (error)
917			return error;
918		error = bread(pmp->pm_devvp, bn, blsize, NOCRED, &bp);
919		if (error) {
920			brelse(bp);
921			return error;
922		}
923		ep = bptoep(pmp, bp, offset);
924		/*
925		 * Check whether, if we came here the second time, i.e.
926		 * when underflowing into the previous block, the last
927		 * entry in this block is a longfilename entry, too.
928		 */
929		if (ep->deAttributes != ATTR_WIN95
930		    && offset != pdep->de_fndoffset) {
931			brelse(bp);
932			break;
933		}
934		offset += sizeof(struct direntry);
935		while (1) {
936			/*
937			 * We are a bit agressive here in that we delete any Win95
938			 * entries preceding this entry, not just the ones we "own".
939			 * Since these presumably aren't valid anyway,
940			 * there should be no harm.
941			 */
942			offset -= sizeof(struct direntry);
943			ep--->deName[0] = SLOT_DELETED;
944			if ((pmp->pm_flags & MSDOSFSMNT_NOWIN95)
945			    || !(offset & pmp->pm_crbomask)
946			    || ep->deAttributes != ATTR_WIN95)
947				break;
948		}
949		if ((error = bwrite(bp)) != 0)
950			return error;
951	} while (!(pmp->pm_flags & MSDOSFSMNT_NOWIN95)
952	    && !(offset & pmp->pm_crbomask)
953	    && offset);
954	return 0;
955}
956
957/*
958 * Create a unique DOS name in dvp
959 */
960int
961uniqdosname(dep, cnp, cp)
962	struct denode *dep;
963	struct componentname *cnp;
964	u_char *cp;
965{
966	struct msdosfsmount *pmp = dep->de_pmp;
967	struct direntry *dentp;
968	int gen;
969	int blsize;
970	u_long cn;
971	daddr_t bn;
972	struct buf *bp;
973	int error;
974
975	if (pmp->pm_flags & MSDOSFSMNT_SHORTNAME)
976		return (unix2dosfn((const u_char *)cnp->cn_nameptr, cp,
977		    cnp->cn_namelen, 0, pmp) ? 0 : EINVAL);
978
979	for (gen = 1;; gen++) {
980		/*
981		 * Generate DOS name with generation number
982		 */
983		if (!unix2dosfn((const u_char *)cnp->cn_nameptr, cp,
984		    cnp->cn_namelen, gen, pmp))
985			return gen == 1 ? EINVAL : EEXIST;
986
987		/*
988		 * Now look for a dir entry with this exact name
989		 */
990		for (cn = error = 0; !error; cn++) {
991			if ((error = pcbmap(dep, cn, &bn, 0, &blsize)) != 0) {
992				if (error == E2BIG)	/* EOF reached and not found */
993					return 0;
994				return error;
995			}
996			error = bread(pmp->pm_devvp, bn, blsize, NOCRED, &bp);
997			if (error) {
998				brelse(bp);
999				return error;
1000			}
1001			for (dentp = (struct direntry *)bp->b_data;
1002			     (char *)dentp < bp->b_data + blsize;
1003			     dentp++) {
1004				if (dentp->deName[0] == SLOT_EMPTY) {
1005					/*
1006					 * Last used entry and not found
1007					 */
1008					brelse(bp);
1009					return 0;
1010				}
1011				/*
1012				 * Ignore volume labels and Win95 entries
1013				 */
1014				if (dentp->deAttributes & ATTR_VOLUME)
1015					continue;
1016				if (!bcmp(dentp->deName, cp, 11)) {
1017					error = EEXIST;
1018					break;
1019				}
1020			}
1021			brelse(bp);
1022		}
1023	}
1024}
1025
1026/*
1027 * Find any Win'95 long filename entry in directory dep
1028 */
1029int
1030findwin95(dep)
1031	struct denode *dep;
1032{
1033	struct msdosfsmount *pmp = dep->de_pmp;
1034	struct direntry *dentp;
1035	int blsize, win95;
1036	u_long cn;
1037	daddr_t bn;
1038	struct buf *bp;
1039
1040	win95 = 1;
1041	/*
1042	 * Read through the directory looking for Win'95 entries
1043	 * Note: Error currently handled just as EOF			XXX
1044	 */
1045	for (cn = 0;; cn++) {
1046		if (pcbmap(dep, cn, &bn, 0, &blsize))
1047			return (win95);
1048		if (bread(pmp->pm_devvp, bn, blsize, NOCRED, &bp)) {
1049			brelse(bp);
1050			return (win95);
1051		}
1052		for (dentp = (struct direntry *)bp->b_data;
1053		     (char *)dentp < bp->b_data + blsize;
1054		     dentp++) {
1055			if (dentp->deName[0] == SLOT_EMPTY) {
1056				/*
1057				 * Last used entry and not found
1058				 */
1059				brelse(bp);
1060				return (win95);
1061			}
1062			if (dentp->deName[0] == SLOT_DELETED) {
1063				/*
1064				 * Ignore deleted files
1065				 * Note: might be an indication of Win'95 anyway	XXX
1066				 */
1067				continue;
1068			}
1069			if (dentp->deAttributes == ATTR_WIN95) {
1070				brelse(bp);
1071				return 1;
1072			}
1073			win95 = 0;
1074		}
1075		brelse(bp);
1076	}
1077}
1078