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