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