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