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