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