xref: /freebsd/sys/fs/msdosfs/msdosfs_lookup.c (revision e4e9813eb92cd7c4d4b819a8fbed5cbd3d92f5d8)
1 /* $FreeBSD$ */
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  */
80 int
81 msdosfs_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));
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 
323 notfound:
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 #if 0
384 	/*
385 	 * Insert name into cache (as non-existent) if appropriate.
386 	 *
387 	 * XXX Negative caching is broken for msdosfs because the name
388 	 * cache doesn't understand peculiarities such as case insensitivity
389 	 * and 8.3 filenames.  Hence, it may not invalidate all negative
390 	 * entries if a file with this name is later created.
391 	 */
392 	if ((cnp->cn_flags & MAKEENTRY) && nameiop != CREATE)
393 		cache_enter(vdp, *vpp, cnp);
394 #endif
395 	return (ENOENT);
396 
397 found:
398 	/*
399 	 * NOTE:  We still have the buffer with matched directory entry at
400 	 * this point.
401 	 */
402 	isadir = dep->deAttributes & ATTR_DIRECTORY;
403 	scn = getushort(dep->deStartCluster);
404 	if (FAT32(pmp)) {
405 		scn |= getushort(dep->deHighClust) << 16;
406 		if (scn == pmp->pm_rootdirblk) {
407 			/*
408 			 * There should actually be 0 here.
409 			 * Just ignore the error.
410 			 */
411 			scn = MSDOSFSROOT;
412 		}
413 	}
414 
415 	if (isadir) {
416 		cluster = scn;
417 		if (cluster == MSDOSFSROOT)
418 			blkoff = MSDOSFSROOT_OFS;
419 		else
420 			blkoff = 0;
421 	} else if (cluster == MSDOSFSROOT)
422 		blkoff = diroff;
423 
424 	/*
425 	 * Now release buf to allow deget to read the entry again.
426 	 * Reserving it here and giving it to deget could result
427 	 * in a deadlock.
428 	 */
429 	brelse(bp);
430 	bp = 0;
431 
432 foundroot:
433 	/*
434 	 * If we entered at foundroot, then we are looking for the . or ..
435 	 * entry of the filesystems root directory.  isadir and scn were
436 	 * setup before jumping here.  And, bp is already null.
437 	 */
438 	if (FAT32(pmp) && scn == MSDOSFSROOT)
439 		scn = pmp->pm_rootdirblk;
440 
441 	/*
442 	 * If deleting, and at end of pathname, return
443 	 * parameters which can be used to remove file.
444 	 */
445 	if (nameiop == DELETE && (flags & ISLASTCN)) {
446 		/*
447 		 * Don't allow deleting the root.
448 		 */
449 		if (blkoff == MSDOSFSROOT_OFS)
450 			return EROFS;				/* really? XXX */
451 
452 		/*
453 		 * Write access to directory required to delete files.
454 		 */
455 		error = VOP_ACCESS(vdp, VWRITE, cnp->cn_cred, cnp->cn_thread);
456 		if (error)
457 			return (error);
458 
459 		/*
460 		 * Return pointer to current entry in dp->i_offset.
461 		 * Save directory inode pointer in ndp->ni_dvp for dirremove().
462 		 */
463 		if (dp->de_StartCluster == scn && isadir) {	/* "." */
464 			VREF(vdp);
465 			*vpp = vdp;
466 			return (0);
467 		}
468 		error = deget(pmp, cluster, blkoff, &tdp);
469 		if (error)
470 			return (error);
471 		*vpp = DETOV(tdp);
472 		return (0);
473 	}
474 
475 	/*
476 	 * If rewriting (RENAME), return the inode and the
477 	 * information required to rewrite the present directory
478 	 * Must get inode of directory entry to verify it's a
479 	 * regular file, or empty directory.
480 	 */
481 	if (nameiop == RENAME && (flags & ISLASTCN)) {
482 		if (blkoff == MSDOSFSROOT_OFS)
483 			return EROFS;				/* really? XXX */
484 
485 		error = VOP_ACCESS(vdp, VWRITE, cnp->cn_cred, cnp->cn_thread);
486 		if (error)
487 			return (error);
488 
489 		/*
490 		 * Careful about locking second inode.
491 		 * This can only occur if the target is ".".
492 		 */
493 		if (dp->de_StartCluster == scn && isadir)
494 			return (EISDIR);
495 
496 		if ((error = deget(pmp, cluster, blkoff, &tdp)) != 0)
497 			return (error);
498 		*vpp = DETOV(tdp);
499 		cnp->cn_flags |= SAVENAME;
500 		return (0);
501 	}
502 
503 	/*
504 	 * Step through the translation in the name.  We do not `vput' the
505 	 * directory because we may need it again if a symbolic link
506 	 * is relative to the current directory.  Instead we save it
507 	 * unlocked as "pdp".  We must get the target inode before unlocking
508 	 * the directory to insure that the inode will not be removed
509 	 * before we get it.  We prevent deadlock by always fetching
510 	 * inodes from the root, moving down the directory tree. Thus
511 	 * when following backward pointers ".." we must unlock the
512 	 * parent directory before getting the requested directory.
513 	 * There is a potential race condition here if both the current
514 	 * and parent directories are removed before the VFS_VGET for the
515 	 * inode associated with ".." returns.  We hope that this occurs
516 	 * infrequently since we cannot avoid this race condition without
517 	 * implementing a sophisticated deadlock detection algorithm.
518 	 * Note also that this simple deadlock detection scheme will not
519 	 * work if the filesystem has any hard links other than ".."
520 	 * that point backwards in the directory structure.
521 	 */
522 	pdp = vdp;
523 	if (flags & ISDOTDOT) {
524 		VOP_UNLOCK(pdp, 0, td);
525 		error = deget(pmp, cluster, blkoff,  &tdp);
526 		vn_lock(pdp, LK_EXCLUSIVE | LK_RETRY, td);
527 		if (error)
528 			return (error);
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  */
554 int
555 createde(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);
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  */
683 int
684 dosdirempty(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  */
760 int
761 doscheckpath(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 	}
834 out:;
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  */
849 int
850 readep(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  */
880 int
881 readde(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  */
899 int
900 removede(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  */
966 int
967 uniqdosname(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  */
1035 int
1036 findwin95(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