xref: /openbsd-src/sys/msdosfs/msdosfs_denode.c (revision 50b7afb2c2c0993b0894d4e34bf857cb13ed9c80)
1 /*	$OpenBSD: msdosfs_denode.c,v 1.49 2014/07/12 18:50:41 tedu Exp $	*/
2 /*	$NetBSD: msdosfs_denode.c,v 1.23 1997/10/17 11:23:58 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/mount.h>
54 #include <sys/malloc.h>
55 #include <sys/proc.h>
56 #include <sys/buf.h>
57 #include <sys/vnode.h>
58 #include <sys/kernel.h>		/* defines "time" */
59 #include <sys/dirent.h>
60 #include <sys/namei.h>
61 
62 #include <msdosfs/bpb.h>
63 #include <msdosfs/msdosfsmount.h>
64 #include <msdosfs/direntry.h>
65 #include <msdosfs/denode.h>
66 #include <msdosfs/fat.h>
67 
68 struct denode **dehashtbl;
69 u_long dehash;			/* size of hash table - 1 */
70 #define	DEHASH(dev, dcl, doff)	(((dev) + (dcl) + (doff) / sizeof(struct direntry)) \
71 				 & dehash)
72 
73 static struct denode *msdosfs_hashget(dev_t, uint32_t, uint32_t);
74 static int msdosfs_hashins(struct denode *);
75 static void msdosfs_hashrem(struct denode *);
76 
77 /*ARGSUSED*/
78 int
79 msdosfs_init(struct vfsconf *vfsp)
80 {
81 	dehashtbl = hashinit(desiredvnodes/2, M_MSDOSFSMNT, M_WAITOK, &dehash);
82 	return (0);
83 }
84 
85 static struct denode *
86 msdosfs_hashget(dev_t dev, uint32_t dirclust, uint32_t diroff)
87 {
88 	struct denode *dep;
89 	struct proc *p = curproc; /* XXX */
90 
91 	for (;;)
92 		for (dep = dehashtbl[DEHASH(dev, dirclust, diroff)]; ;
93 		     dep = dep->de_next) {
94 			if (dep == NULL)
95 				return (NULL);
96 			if (dirclust == dep->de_dirclust &&
97 			    diroff == dep->de_diroffset &&
98 			    dev == dep->de_dev &&
99 			    dep->de_refcnt != 0) {
100 				struct vnode *vp = DETOV(dep);
101 
102 				if (!vget(vp, LK_EXCLUSIVE, p))
103 					return (dep);
104 				break;
105 			}
106 		}
107 	/* NOTREACHED */
108 }
109 
110 static int
111 msdosfs_hashins(struct denode *dep)
112 {
113 	struct denode **depp, *deq;
114 
115 	depp = &dehashtbl[DEHASH(dep->de_dev, dep->de_dirclust,
116 				 dep->de_diroffset)];
117 
118 	for (deq = *depp; deq; deq = deq->de_next) {
119 		if (dep->de_dirclust == deq->de_dirclust &&
120 		    dep->de_diroffset == deq->de_diroffset &&
121 		    dep->de_dev == deq->de_dev &&
122 		    deq->de_refcnt != 0) {
123 			return (EEXIST);
124 		}
125 	}
126 
127 	if ((deq = *depp) != NULL)
128 		deq->de_prev = &dep->de_next;
129 	dep->de_next = deq;
130 	dep->de_prev = depp;
131 	*depp = dep;
132 	return (0);
133 }
134 
135 static void
136 msdosfs_hashrem(struct denode *dep)
137 {
138 	struct denode *deq;
139 
140 	if (dep->de_prev == NULL)
141 		return;
142 
143 	if ((deq = dep->de_next) != NULL)
144 		deq->de_prev = dep->de_prev;
145 	*dep->de_prev = deq;
146 #ifdef DIAGNOSTIC
147 	dep->de_next = NULL;
148 	dep->de_prev = NULL;
149 #endif
150 }
151 
152 /*
153  * If deget() succeeds it returns with the gotten denode locked().
154  *
155  * pmp	     - address of msdosfsmount structure of the filesystem containing
156  *	       the denode of interest.  The pm_dev field and the address of
157  *	       the msdosfsmount structure are used.
158  * dirclust  - which cluster bp contains, if dirclust is 0 (root directory)
159  *	       diroffset is relative to the beginning of the root directory,
160  *	       otherwise it is cluster relative.
161  * diroffset - offset past begin of cluster of denode we want
162  * depp	     - returns the address of the gotten denode.
163  */
164 int
165 deget(struct msdosfsmount *pmp, uint32_t dirclust, uint32_t diroffset,
166     struct denode **depp)
167 {
168 	int error;
169 	extern struct vops msdosfs_vops;
170 	struct direntry *direntptr;
171 	struct denode *ldep;
172 	struct vnode *nvp;
173 	struct buf *bp;
174 	struct proc *p = curproc; /* XXX */
175 
176 #ifdef MSDOSFS_DEBUG
177 	printf("deget(pmp %08x, dirclust %d, diroffset %x, depp %08x)\n",
178 	    pmp, dirclust, diroffset, depp);
179 #endif
180 
181 	/*
182 	 * On FAT32 filesystems, root is a (more or less) normal
183 	 * directory
184 	 */
185 	if (FAT32(pmp) && dirclust == MSDOSFSROOT)
186 		dirclust = pmp->pm_rootdirblk;
187 
188 	/*
189 	 * See if the denode is in the denode cache. Use the location of
190 	 * the directory entry to compute the hash value. For subdir use
191 	 * address of "." entry. For root dir (if not FAT32) use cluster
192 	 * MSDOSFSROOT, offset MSDOSFSROOT_OFS
193 	 *
194 	 * NOTE: The check for de_refcnt > 0 below insures the denode being
195 	 * examined does not represent an unlinked but still open file.
196 	 * These files are not to be accessible even when the directory
197 	 * entry that represented the file happens to be reused while the
198 	 * deleted file is still open.
199 	 */
200 retry:
201 	ldep = msdosfs_hashget(pmp->pm_dev, dirclust, diroffset);
202 	if (ldep) {
203 		*depp = ldep;
204 		return (0);
205 	}
206 
207 	/*
208 	 * Directory entry was not in cache, have to create a vnode and
209 	 * copy it from the passed disk buffer.
210 	 */
211 	/* getnewvnode() does a vref() on the vnode */
212 	error = getnewvnode(VT_MSDOSFS, pmp->pm_mountp, &msdosfs_vops, &nvp);
213 	if (error) {
214 		*depp = 0;
215 		return (error);
216 	}
217 	ldep = malloc(sizeof(*ldep), M_MSDOSFSNODE, M_WAITOK | M_ZERO);
218 	lockinit(&ldep->de_lock, PINOD, "denode", 0, 0);
219 	nvp->v_data = ldep;
220 	ldep->de_vnode = nvp;
221 	ldep->de_flag = 0;
222 	ldep->de_devvp = 0;
223 	ldep->de_lockf = 0;
224 	ldep->de_dev = pmp->pm_dev;
225 	ldep->de_dirclust = dirclust;
226 	ldep->de_diroffset = diroffset;
227 	fc_purge(ldep, 0);	/* init the fat cache for this denode */
228 
229 	/*
230 	 * Insert the denode into the hash queue and lock the denode so it
231 	 * can't be accessed until we've read it in and have done what we
232 	 * need to it.
233 	 */
234 	vn_lock(nvp, LK_EXCLUSIVE | LK_RETRY, p);
235 	error = msdosfs_hashins(ldep);
236 
237 	if (error) {
238 		vput (nvp);
239 
240 		if (error == EEXIST)
241 			goto retry;
242 
243 		return (error);
244 	}
245 
246 	ldep->de_pmp = pmp;
247 	ldep->de_devvp = pmp->pm_devvp;
248 	ldep->de_refcnt = 1;
249 	/*
250 	 * Copy the directory entry into the denode area of the vnode.
251 	 */
252 	if ((dirclust == MSDOSFSROOT
253 	     || (FAT32(pmp) && dirclust == pmp->pm_rootdirblk))
254 	    && diroffset == MSDOSFSROOT_OFS) {
255 		/*
256 		 * Directory entry for the root directory. There isn't one,
257 		 * so we manufacture one. We should probably rummage
258 		 * through the root directory and find a label entry (if it
259 		 * exists), and then use the time and date from that entry
260 		 * as the time and date for the root denode.
261 		 */
262 	        nvp->v_flag |= VROOT; /* should be further down         XXX */
263 
264 		ldep->de_Attributes = ATTR_DIRECTORY;
265 		if (FAT32(pmp))
266 		        ldep->de_StartCluster = pmp->pm_rootdirblk;
267 		        /* de_FileSize will be filled in further down */
268 		else {
269 		        ldep->de_StartCluster = MSDOSFSROOT;
270 		        ldep->de_FileSize = pmp->pm_rootdirsize * pmp->pm_BytesPerSec;
271 		}
272 		/*
273 		 * fill in time and date so that dos2unixtime() doesn't
274 		 * spit up when called from msdosfs_getattr() with root
275 		 * denode
276 		 */
277 		ldep->de_CTime = 0x0000;	/* 00:00:00	 */
278 		ldep->de_CTimeHundredth = 0;
279 		ldep->de_CDate = (0 << DD_YEAR_SHIFT) | (1 << DD_MONTH_SHIFT)
280 		    | (1 << DD_DAY_SHIFT);
281 		/* Jan 1, 1980	 */
282 		ldep->de_ADate = ldep->de_CDate;
283 		ldep->de_MTime = ldep->de_CTime;
284 		ldep->de_MDate = ldep->de_CDate;
285 		/* leave the other fields as garbage */
286 	} else {
287 		error = readep(pmp, dirclust, diroffset, &bp, &direntptr);
288 		if (error)
289 			return (error);
290 		DE_INTERNALIZE(ldep, direntptr);
291 		brelse(bp);
292 	}
293 
294 	/*
295 	 * Fill in a few fields of the vnode and finish filling in the
296 	 * denode.  Then return the address of the found denode.
297 	 */
298 	if (ldep->de_Attributes & ATTR_DIRECTORY) {
299 		/*
300 		 * Since DOS directory entries that describe directories
301 		 * have 0 in the filesize field, we take this opportunity
302 		 * to find out the length of the directory and plug it into
303 		 * the denode structure.
304 		 */
305 		uint32_t size;
306 
307 		nvp->v_type = VDIR;
308 		if (ldep->de_StartCluster != MSDOSFSROOT) {
309 			error = pcbmap(ldep, 0xffff, 0, &size, 0);
310 			if (error == E2BIG) {
311 				ldep->de_FileSize = de_cn2off(pmp, size);
312 				error = 0;
313 			} else if (error) {
314 				printf("deget(): pcbmap returned %d\n", error);
315 				return (error);
316 			}
317 		}
318 	} else
319 		nvp->v_type = VREG;
320 	vref(ldep->de_devvp);
321 	*depp = ldep;
322 	return (0);
323 }
324 
325 int
326 deupdat(struct denode *dep, int waitfor)
327 {
328 	struct buf *bp;
329 	struct direntry *dirp;
330 	int error;
331 	struct timespec ts;
332 
333 	if (DETOV(dep)->v_mount->mnt_flag & MNT_RDONLY)
334 		return (0);
335 	getnanotime(&ts);
336 	DETIMES(dep, &ts, &ts, &ts);
337 	if ((dep->de_flag & DE_MODIFIED) == 0)
338 		return (0);
339 	dep->de_flag &= ~DE_MODIFIED;
340 	if (dep->de_Attributes & ATTR_DIRECTORY)
341 		return (0);
342 	if (dep->de_refcnt <= 0)
343 		return (0);
344 	error = readde(dep, &bp, &dirp);
345 	if (error)
346 		return (error);
347 	DE_EXTERNALIZE(dirp, dep);
348 	if (waitfor)
349 		return (bwrite(bp));
350 	else {
351 		bdwrite(bp);
352 		return (0);
353 	}
354 }
355 
356 /*
357  * Truncate the file described by dep to the length specified by length.
358  */
359 int
360 detrunc(struct denode *dep, uint32_t length, int flags, struct ucred *cred,
361     struct proc *p)
362 {
363 	int error;
364 	int allerror;
365 	int vflags;
366 	uint32_t eofentry;
367 	uint32_t chaintofree = 0;
368 	daddr_t bn;
369 	int boff;
370 	int isadir = dep->de_Attributes & ATTR_DIRECTORY;
371 	struct buf *bp;
372 	struct msdosfsmount *pmp = dep->de_pmp;
373 
374 #ifdef MSDOSFS_DEBUG
375 	printf("detrunc(): file %.11s, length %ld, flags %d\n",
376 	    dep->de_Name, length, flags);
377 #endif
378 
379 	/*
380 	 * Disallow attempts to truncate the root directory since it is of
381 	 * fixed size.  That's just the way dos filesystems are.  We use
382 	 * the VROOT bit in the vnode because checking for the directory
383 	 * bit and a startcluster of 0 in the denode is not adequate to
384 	 * recognize the root directory at this point in a file or
385 	 * directory's life.
386 	 */
387 	if ((DETOV(dep)->v_flag & VROOT) && !FAT32(pmp)) {
388 		printf("detrunc(): can't truncate root directory, clust %u, offset %u\n",
389 		    dep->de_dirclust, dep->de_diroffset);
390 		return (EINVAL);
391 	}
392 
393 	uvm_vnp_setsize(DETOV(dep), length);
394 
395 	if (dep->de_FileSize < length)
396 		return (deextend(dep, length, cred));
397 
398 	/*
399 	 * If the desired length is 0 then remember the starting cluster of
400 	 * the file and set the StartCluster field in the directory entry
401 	 * to 0.  If the desired length is not zero, then get the number of
402 	 * the last cluster in the shortened file.  Then get the number of
403 	 * the first cluster in the part of the file that is to be freed.
404 	 * Then set the next cluster pointer in the last cluster of the
405 	 * file to CLUST_EOFE.
406 	 */
407 	if (length == 0) {
408 		chaintofree = dep->de_StartCluster;
409 		dep->de_StartCluster = 0;
410 		eofentry = ~0;
411 	} else {
412 		error = pcbmap(dep, de_clcount(pmp, length) - 1, 0,
413 			       &eofentry, 0);
414 		if (error) {
415 #ifdef MSDOSFS_DEBUG
416 			printf("detrunc(): pcbmap fails %d\n", error);
417 #endif
418 			return (error);
419 		}
420 	}
421 
422 	fc_purge(dep, de_clcount(pmp, length));
423 
424 	/*
425 	 * If the new length is not a multiple of the cluster size then we
426 	 * must zero the tail end of the new last cluster in case it
427 	 * becomes part of the file again because of a seek.
428 	 */
429 	if ((boff = length & pmp->pm_crbomask) != 0) {
430 		if (isadir) {
431 			bn = cntobn(pmp, eofentry);
432 			error = bread(pmp->pm_devvp, bn, pmp->pm_bpcluster, &bp);
433 		} else {
434 			bn = de_blk(pmp, length);
435 			error = bread(DETOV(dep), bn, pmp->pm_bpcluster, &bp);
436 		}
437 		if (error) {
438 			brelse(bp);
439 #ifdef MSDOSFS_DEBUG
440 			printf("detrunc(): bread fails %d\n", error);
441 #endif
442 			return (error);
443 		}
444 		uvm_vnp_uncache(DETOV(dep));
445 		/*
446 		 * is this the right place for it?
447 		 */
448 		bzero(bp->b_data + boff, pmp->pm_bpcluster - boff);
449 		if (flags & IO_SYNC)
450 			bwrite(bp);
451 		else
452 			bdwrite(bp);
453 	}
454 
455 	/*
456 	 * Write out the updated directory entry.  Even if the update fails
457 	 * we free the trailing clusters.
458 	 */
459 	dep->de_FileSize = length;
460 	if (!isadir)
461 		dep->de_flag |= DE_UPDATE|DE_MODIFIED;
462 	vflags = (length > 0 ? V_SAVE : 0) | V_SAVEMETA;
463 	vinvalbuf(DETOV(dep), vflags, cred, p, 0, 0);
464 	allerror = deupdat(dep, 1);
465 #ifdef MSDOSFS_DEBUG
466 	printf("detrunc(): allerror %d, eofentry %d\n",
467 	       allerror, eofentry);
468 #endif
469 
470 	/*
471 	 * If we need to break the cluster chain for the file then do it
472 	 * now.
473 	 */
474 	if (eofentry != ~0) {
475 		error = fatentry(FAT_GET_AND_SET, pmp, eofentry,
476 				 &chaintofree, CLUST_EOFE);
477 		if (error) {
478 #ifdef MSDOSFS_DEBUG
479 			printf("detrunc(): fatentry errors %d\n", error);
480 #endif
481 			return (error);
482 		}
483 		fc_setcache(dep, FC_LASTFC, de_cluster(pmp, length - 1),
484 			    eofentry);
485 	}
486 
487 	/*
488 	 * Now free the clusters removed from the file because of the
489 	 * truncation.
490 	 */
491 	if (chaintofree != 0 && !MSDOSFSEOF(pmp, chaintofree))
492 		freeclusterchain(pmp, chaintofree);
493 
494 	return (allerror);
495 }
496 
497 /*
498  * Extend the file described by dep to length specified by length.
499  */
500 int
501 deextend(struct denode *dep, uint32_t length, struct ucred *cred)
502 {
503 	struct msdosfsmount *pmp = dep->de_pmp;
504 	uint32_t count;
505 	int error;
506 
507 	/*
508 	 * The root of a DOS filesystem cannot be extended.
509 	 */
510 	if ((DETOV(dep)->v_flag & VROOT) && !FAT32(pmp))
511 		return (EINVAL);
512 
513 	/*
514 	 * Directories cannot be extended.
515 	 */
516 	if (dep->de_Attributes & ATTR_DIRECTORY)
517 		return (EISDIR);
518 
519 	if (length <= dep->de_FileSize)
520 		panic("deextend: file too large");
521 
522 	/*
523 	 * Compute the number of clusters to allocate.
524 	 */
525 	count = de_clcount(pmp, length) - de_clcount(pmp, dep->de_FileSize);
526 	if (count > 0) {
527 		if (count > pmp->pm_freeclustercount)
528 			return (ENOSPC);
529 		error = extendfile(dep, count, NULL, NULL, DE_CLEAR);
530 		if (error) {
531 			/* truncate the added clusters away again */
532 			(void) detrunc(dep, dep->de_FileSize, 0, cred, NULL);
533 			return (error);
534 		}
535 	}
536 
537 	dep->de_FileSize = length;
538 	dep->de_flag |= DE_UPDATE|DE_MODIFIED;
539 	return (deupdat(dep, 1));
540 }
541 
542 /*
543  * Move a denode to its correct hash queue after the file it represents has
544  * been moved to a new directory.
545  */
546 void
547 reinsert(struct denode *dep)
548 {
549 	/*
550 	 * Fix up the denode cache.  If the denode is for a directory,
551 	 * there is nothing to do since the hash is based on the starting
552 	 * cluster of the directory file and that hasn't changed.  If for a
553 	 * file the hash is based on the location of the directory entry,
554 	 * so we must remove it from the cache and re-enter it with the
555 	 * hash based on the new location of the directory entry.
556 	 */
557 	if (dep->de_Attributes & ATTR_DIRECTORY)
558 		return;
559 	msdosfs_hashrem(dep);
560 	msdosfs_hashins(dep);
561 }
562 
563 int
564 msdosfs_reclaim(void *v)
565 {
566 	struct vop_reclaim_args *ap = v;
567 	struct vnode *vp = ap->a_vp;
568 	struct denode *dep = VTODE(vp);
569 #ifdef DIAGNOSTIC
570 	extern int prtactive;
571 
572 	if (prtactive && vp->v_usecount != 0)
573 		vprint("msdosfs_reclaim(): pushing active", vp);
574 #endif
575 
576 #ifdef MSDOSFS_DEBUG
577 	printf("msdosfs_reclaim(): dep %08x, file %.11s, refcnt %d\n",
578 	    dep, dep->de_Name, dep->de_refcnt);
579 #endif
580 
581 	/*
582 	 * Remove the denode from its hash chain.
583 	 */
584 	msdosfs_hashrem(dep);
585 	/*
586 	 * Purge old data structures associated with the denode.
587 	 */
588 	cache_purge(vp);
589 	if (dep->de_devvp) {
590 		vrele(dep->de_devvp);
591 		dep->de_devvp = 0;
592 	}
593 #if 0 /* XXX */
594 	dep->de_flag = 0;
595 #endif
596 	free(dep, M_MSDOSFSNODE, 0);
597 	vp->v_data = NULL;
598 	return (0);
599 }
600 
601 int
602 msdosfs_inactive(void *v)
603 {
604 	struct vop_inactive_args *ap = v;
605 	struct vnode *vp = ap->a_vp;
606 	struct denode *dep = VTODE(vp);
607 	struct proc *p = ap->a_p;
608 	int error;
609 #ifdef DIAGNOSTIC
610 	extern int prtactive;
611 
612 	if (prtactive && vp->v_usecount != 0)
613 		vprint("msdosfs_inactive(): pushing active", vp);
614 #endif
615 
616 #ifdef MSDOSFS_DEBUG
617 	printf("msdosfs_inactive(): dep %08x, de_Name[0] %x\n", dep,
618 	    dep->de_Name[0]);
619 #endif
620 
621 	error = 0;
622 
623 	/*
624 	 * Get rid of denodes related to stale file handles.
625 	 */
626 	if (dep->de_Name[0] == SLOT_DELETED)
627 		goto out;
628 
629 	/*
630 	 * If the file has been deleted and it is on a read/write
631 	 * filesystem, then truncate the file, and mark the directory slot
632 	 * as empty.  (This may not be necessary for the dos filesystem.)
633 	 */
634 #ifdef MSDOSFS_DEBUG
635 	printf("msdosfs_inactive(): dep %08x, refcnt %d, mntflag %x, MNT_RDONLY %x\n",
636 	       dep, dep->de_refcnt, vp->v_mount->mnt_flag, MNT_RDONLY);
637 #endif
638 	if (dep->de_refcnt <= 0 && (vp->v_mount->mnt_flag & MNT_RDONLY) == 0) {
639 		error = detrunc(dep, (uint32_t)0, 0, NOCRED, NULL);
640 		dep->de_Name[0] = SLOT_DELETED;
641 	}
642 	deupdat(dep, 0);
643 
644 out:
645 	VOP_UNLOCK(vp, 0, p);
646 	/*
647 	 * If we are done with the denode, reclaim it
648 	 * so that it can be reused immediately.
649 	 */
650 #ifdef MSDOSFS_DEBUG
651 	printf("msdosfs_inactive(): v_usecount %d, de_Name[0] %x\n",
652 	    vp->v_usecount, dep->de_Name[0]);
653 #endif
654 	if (dep->de_Name[0] == SLOT_DELETED)
655 		vrecycle(vp, p);
656 	return (error);
657 }
658