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