xref: /netbsd-src/sys/fs/msdosfs/msdosfs_denode.c (revision 0df165c04d0a9ca1adde9ed2b890344c937954a6)
1 /*	$NetBSD: msdosfs_denode.c,v 1.27 2007/11/26 19:01:46 pooka Exp $	*/
2 
3 /*-
4  * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank.
5  * Copyright (C) 1994, 1995, 1997 TooLs GmbH.
6  * All rights reserved.
7  * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below).
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  * 3. All advertising materials mentioning features or use of this software
18  *    must display the following acknowledgement:
19  *	This product includes software developed by TooLs GmbH.
20  * 4. The name of TooLs GmbH may not be used to endorse or promote products
21  *    derived from this software without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
24  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
25  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
26  * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
27  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
28  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
29  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
30  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
31  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
32  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33  */
34 /*
35  * Written by Paul Popelka (paulp@uts.amdahl.com)
36  *
37  * You can do anything you want with this software, just don't say you wrote
38  * it, and don't remove this notice.
39  *
40  * This software is provided "as is".
41  *
42  * The author supplies this software to be publicly redistributed on the
43  * understanding that the author is not responsible for the correct
44  * functioning of this software in any circumstances and is not liable for
45  * any damages caused by this software.
46  *
47  * October 1992
48  */
49 
50 #include <sys/cdefs.h>
51 __KERNEL_RCSID(0, "$NetBSD: msdosfs_denode.c,v 1.27 2007/11/26 19:01:46 pooka Exp $");
52 
53 #include <sys/param.h>
54 #include <sys/systm.h>
55 #include <sys/mount.h>
56 #include <sys/malloc.h>
57 #include <sys/pool.h>
58 #include <sys/proc.h>
59 #include <sys/buf.h>
60 #include <sys/vnode.h>
61 #include <sys/kernel.h>		/* defines "time" */
62 #include <sys/dirent.h>
63 #include <sys/namei.h>
64 #include <sys/kauth.h>
65 
66 #include <uvm/uvm_extern.h>
67 
68 #include <fs/msdosfs/bpb.h>
69 #include <fs/msdosfs/msdosfsmount.h>
70 #include <fs/msdosfs/direntry.h>
71 #include <fs/msdosfs/denode.h>
72 #include <fs/msdosfs/fat.h>
73 
74 LIST_HEAD(ihashhead, denode) *dehashtbl;
75 u_long dehash;			/* size of hash table - 1 */
76 #define	DEHASH(dev, dcl, doff) \
77     (((dev) + (dcl) + (doff) / sizeof(struct direntry)) & dehash)
78 
79 struct simplelock msdosfs_ihash_slock;
80 
81 struct pool msdosfs_denode_pool;
82 
83 extern int prtactive;
84 
85 static const struct genfs_ops msdosfs_genfsops = {
86 	.gop_size = genfs_size,
87 	.gop_alloc = msdosfs_gop_alloc,
88 	.gop_write = genfs_gop_write,
89 	.gop_markupdate = msdosfs_gop_markupdate,
90 };
91 
92 static struct denode *msdosfs_hashget(dev_t, u_long, u_long);
93 static void msdosfs_hashins(struct denode *);
94 static void msdosfs_hashrem(struct denode *);
95 
96 MALLOC_DECLARE(M_MSDOSFSFAT);
97 
98 void
99 msdosfs_init()
100 {
101 
102 	malloc_type_attach(M_MSDOSFSMNT);
103 	malloc_type_attach(M_MSDOSFSFAT);
104 	malloc_type_attach(M_MSDOSFSTMP);
105 	pool_init(&msdosfs_denode_pool, sizeof(struct denode), 0, 0, 0,
106 	    "msdosnopl", &pool_allocator_nointr, IPL_NONE);
107 	dehashtbl = hashinit(desiredvnodes / 2, HASH_LIST, M_MSDOSFSMNT,
108 	    M_WAITOK, &dehash);
109 	simple_lock_init(&msdosfs_ihash_slock);
110 }
111 
112 /*
113  * Reinitialize inode hash table.
114  */
115 
116 void
117 msdosfs_reinit()
118 {
119 	struct denode *dep;
120 	struct ihashhead *oldhash, *hash;
121 	u_long oldmask, mask, val;
122 	int i;
123 
124 	hash = hashinit(desiredvnodes / 2, HASH_LIST, M_MSDOSFSMNT, M_WAITOK,
125 	    &mask);
126 
127 	simple_lock(&msdosfs_ihash_slock);
128 	oldhash = dehashtbl;
129 	oldmask = dehash;
130 	dehashtbl = hash;
131 	dehash = mask;
132 	for (i = 0; i <= oldmask; i++) {
133 		while ((dep = LIST_FIRST(&oldhash[i])) != NULL) {
134 			LIST_REMOVE(dep, de_hash);
135 			val = DEHASH(dep->de_dev, dep->de_dirclust,
136 			    dep->de_diroffset);
137 			LIST_INSERT_HEAD(&hash[val], dep, de_hash);
138 		}
139 	}
140 	simple_unlock(&msdosfs_ihash_slock);
141 	hashdone(oldhash, M_MSDOSFSMNT);
142 }
143 
144 void
145 msdosfs_done()
146 {
147 	hashdone(dehashtbl, M_MSDOSFSMNT);
148 	pool_destroy(&msdosfs_denode_pool);
149 	malloc_type_detach(M_MSDOSFSTMP);
150 	malloc_type_detach(M_MSDOSFSFAT);
151 	malloc_type_detach(M_MSDOSFSMNT);
152 }
153 
154 static struct denode *
155 msdosfs_hashget(dev, dirclust, diroff)
156 	dev_t dev;
157 	u_long dirclust;
158 	u_long diroff;
159 {
160 	struct denode *dep;
161 	struct vnode *vp;
162 
163 loop:
164 	simple_lock(&msdosfs_ihash_slock);
165 	LIST_FOREACH(dep, &dehashtbl[DEHASH(dev, dirclust, diroff)], de_hash) {
166 		if (dirclust == dep->de_dirclust &&
167 		    diroff == dep->de_diroffset &&
168 		    dev == dep->de_dev &&
169 		    dep->de_refcnt != 0) {
170 			vp = DETOV(dep);
171 			simple_lock(&vp->v_interlock);
172 			simple_unlock(&msdosfs_ihash_slock);
173 			if (vget(vp, LK_EXCLUSIVE | LK_INTERLOCK))
174 				goto loop;
175 			return (dep);
176 		}
177 	}
178 	simple_unlock(&msdosfs_ihash_slock);
179 	return (NULL);
180 }
181 
182 static void
183 msdosfs_hashins(dep)
184 	struct denode *dep;
185 {
186 	struct ihashhead *depp;
187 	int val;
188 
189 	simple_lock(&msdosfs_ihash_slock);
190 	val = DEHASH(dep->de_dev, dep->de_dirclust, dep->de_diroffset);
191 	depp = &dehashtbl[val];
192 	LIST_INSERT_HEAD(depp, dep, de_hash);
193 	simple_unlock(&msdosfs_ihash_slock);
194 }
195 
196 static void
197 msdosfs_hashrem(dep)
198 	struct denode *dep;
199 {
200 	simple_lock(&msdosfs_ihash_slock);
201 	LIST_REMOVE(dep, de_hash);
202 	simple_unlock(&msdosfs_ihash_slock);
203 }
204 
205 /*
206  * If deget() succeeds it returns with the gotten denode locked().
207  *
208  * pmp	     - address of msdosfsmount structure of the filesystem containing
209  *	       the denode of interest.  The pm_dev field and the address of
210  *	       the msdosfsmount structure are used.
211  * dirclust  - which cluster bp contains, if dirclust is 0 (root directory)
212  *	       diroffset is relative to the beginning of the root directory,
213  *	       otherwise it is cluster relative.
214  * diroffset - offset past begin of cluster of denode we want
215  * depp	     - returns the address of the gotten denode.
216  */
217 int
218 deget(pmp, dirclust, diroffset, depp)
219 	struct msdosfsmount *pmp;	/* so we know the maj/min number */
220 	u_long dirclust;		/* cluster this dir entry came from */
221 	u_long diroffset;		/* index of entry within the cluster */
222 	struct denode **depp;		/* returns the addr of the gotten denode */
223 {
224 	int error;
225 	extern int (**msdosfs_vnodeop_p)(void *);
226 	struct direntry *direntptr;
227 	struct denode *ldep;
228 	struct vnode *nvp;
229 	struct buf *bp;
230 
231 #ifdef MSDOSFS_DEBUG
232 	printf("deget(pmp %p, dirclust %lu, diroffset %lx, depp %p)\n",
233 	    pmp, dirclust, diroffset, depp);
234 #endif
235 
236 	/*
237 	 * On FAT32 filesystems, root is a (more or less) normal
238 	 * directory
239 	 */
240 	if (FAT32(pmp) && dirclust == MSDOSFSROOT)
241 		dirclust = pmp->pm_rootdirblk;
242 
243 	/*
244 	 * See if the denode is in the denode cache. Use the location of
245 	 * the directory entry to compute the hash value. For subdir use
246 	 * address of "." entry. For root dir (if not FAT32) use cluster
247 	 * MSDOSFSROOT, offset MSDOSFSROOT_OFS
248 	 *
249 	 * NOTE: The check for de_refcnt > 0 below insures the denode being
250 	 * examined does not represent an unlinked but still open file.
251 	 * These files are not to be accessible even when the directory
252 	 * entry that represented the file happens to be reused while the
253 	 * deleted file is still open.
254 	 */
255 	ldep = msdosfs_hashget(pmp->pm_dev, dirclust, diroffset);
256 	if (ldep) {
257 		*depp = ldep;
258 		return (0);
259 	}
260 
261 	/*
262 	 * Directory entry was not in cache, have to create a vnode and
263 	 * copy it from the passed disk buffer.
264 	 */
265 	/* getnewvnode() does a VREF() on the vnode */
266 	error = getnewvnode(VT_MSDOSFS, pmp->pm_mountp,
267 			    msdosfs_vnodeop_p, &nvp);
268 	if (error) {
269 		*depp = 0;
270 		return (error);
271 	}
272 	ldep = pool_get(&msdosfs_denode_pool, PR_WAITOK);
273 	memset(ldep, 0, sizeof *ldep);
274 	nvp->v_data = ldep;
275 	ldep->de_vnode = nvp;
276 	ldep->de_flag = 0;
277 	ldep->de_devvp = 0;
278 	ldep->de_lockf = 0;
279 	ldep->de_dev = pmp->pm_dev;
280 	ldep->de_dirclust = dirclust;
281 	ldep->de_diroffset = diroffset;
282 	fc_purge(ldep, 0);	/* init the fat cache for this denode */
283 
284 	/*
285 	 * Insert the denode into the hash queue and lock the denode so it
286 	 * can't be accessed until we've read it in and have done what we
287 	 * need to it.
288 	 */
289 	vn_lock(nvp, LK_EXCLUSIVE | LK_RETRY);
290 	genfs_node_init(nvp, &msdosfs_genfsops);
291 	msdosfs_hashins(ldep);
292 
293 	ldep->de_pmp = pmp;
294 	ldep->de_devvp = pmp->pm_devvp;
295 	ldep->de_refcnt = 1;
296 	/*
297 	 * Copy the directory entry into the denode area of the vnode.
298 	 */
299 	if ((dirclust == MSDOSFSROOT
300 	     || (FAT32(pmp) && dirclust == pmp->pm_rootdirblk))
301 	    && diroffset == MSDOSFSROOT_OFS) {
302 		/*
303 		 * Directory entry for the root directory. There isn't one,
304 		 * so we manufacture one. We should probably rummage
305 		 * through the root directory and find a label entry (if it
306 		 * exists), and then use the time and date from that entry
307 		 * as the time and date for the root denode.
308 		 */
309 		nvp->v_vflag |= VV_ROOT; /* should be further down XXX */
310 
311 		ldep->de_Attributes = ATTR_DIRECTORY;
312 		if (FAT32(pmp))
313 			ldep->de_StartCluster = pmp->pm_rootdirblk;
314 			/* de_FileSize will be filled in further down */
315 		else {
316 			ldep->de_StartCluster = MSDOSFSROOT;
317 			ldep->de_FileSize = pmp->pm_rootdirsize * pmp->pm_BytesPerSec;
318 		}
319 		/*
320 		 * fill in time and date so that dos2unixtime() doesn't
321 		 * spit up when called from msdosfs_getattr() with root
322 		 * denode
323 		 */
324 		ldep->de_CHun = 0;
325 		ldep->de_CTime = 0x0000;	/* 00:00:00	 */
326 		ldep->de_CDate = (0 << DD_YEAR_SHIFT) | (1 << DD_MONTH_SHIFT)
327 		    | (1 << DD_DAY_SHIFT);
328 		/* Jan 1, 1980	 */
329 		ldep->de_ADate = ldep->de_CDate;
330 		ldep->de_MTime = ldep->de_CTime;
331 		ldep->de_MDate = ldep->de_CDate;
332 		/* leave the other fields as garbage */
333 	} else {
334 		error = readep(pmp, dirclust, diroffset, &bp, &direntptr);
335 		if (error) {
336 			ldep->de_devvp = NULL;
337 			ldep->de_Name[0] = SLOT_DELETED;
338 			vput(nvp);
339 			return (error);
340 		}
341 		DE_INTERNALIZE(ldep, direntptr);
342 		brelse(bp, 0);
343 	}
344 
345 	/*
346 	 * Fill in a few fields of the vnode and finish filling in the
347 	 * denode.  Then return the address of the found denode.
348 	 */
349 	if (ldep->de_Attributes & ATTR_DIRECTORY) {
350 		/*
351 		 * Since DOS directory entries that describe directories
352 		 * have 0 in the filesize field, we take this opportunity
353 		 * to find out the length of the directory and plug it into
354 		 * the denode structure.
355 		 */
356 		u_long size;
357 
358 		nvp->v_type = VDIR;
359 		if (ldep->de_StartCluster != MSDOSFSROOT) {
360 			error = pcbmap(ldep, CLUST_END, 0, &size, 0);
361 			if (error == E2BIG) {
362 				ldep->de_FileSize = de_cn2off(pmp, size);
363 				error = 0;
364 			} else
365 				printf("deget(): pcbmap returned %d\n", error);
366 		}
367 	} else
368 		nvp->v_type = VREG;
369 	VREF(ldep->de_devvp);
370 	*depp = ldep;
371 	uvm_vnp_setsize(nvp, ldep->de_FileSize);
372 	return (0);
373 }
374 
375 int
376 deupdat(dep, waitfor)
377 	struct denode *dep;
378 	int waitfor;
379 {
380 
381 	return (msdosfs_update(DETOV(dep), NULL, NULL,
382 	    waitfor ? UPDATE_WAIT : 0));
383 }
384 
385 /*
386  * Truncate the file described by dep to the length specified by length.
387  */
388 int
389 detrunc(struct denode *dep, u_long length, int flags, kauth_cred_t cred)
390 {
391 	int error;
392 	int allerror;
393 	u_long eofentry;
394 	u_long chaintofree = 0;
395 	daddr_t bn, lastblock;
396 	int boff;
397 	int isadir = dep->de_Attributes & ATTR_DIRECTORY;
398 	struct buf *bp;
399 	struct msdosfsmount *pmp = dep->de_pmp;
400 
401 #ifdef MSDOSFS_DEBUG
402 	printf("detrunc(): file %s, length %lu, flags %x\n", dep->de_Name, length, flags);
403 #endif
404 
405 	/*
406 	 * Disallow attempts to truncate the root directory since it is of
407 	 * fixed size.  That's just the way dos filesystems are.  We use
408 	 * the VROOT bit in the vnode because checking for the directory
409 	 * bit and a startcluster of 0 in the denode is not adequate to
410 	 * recognize the root directory at this point in a file or
411 	 * directory's life.
412 	 */
413 	if ((DETOV(dep)->v_vflag & VV_ROOT) && !FAT32(pmp)) {
414 		printf("detrunc(): can't truncate root directory, clust %ld, offset %ld\n",
415 		    dep->de_dirclust, dep->de_diroffset);
416 		return (EINVAL);
417 	}
418 
419 	uvm_vnp_setsize(DETOV(dep), length);
420 
421 	if (dep->de_FileSize < length)
422 		return (deextend(dep, length, cred));
423 	lastblock = de_clcount(pmp, length) - 1;
424 
425 	/*
426 	 * If the desired length is 0 then remember the starting cluster of
427 	 * the file and set the StartCluster field in the directory entry
428 	 * to 0.  If the desired length is not zero, then get the number of
429 	 * the last cluster in the shortened file.  Then get the number of
430 	 * the first cluster in the part of the file that is to be freed.
431 	 * Then set the next cluster pointer in the last cluster of the
432 	 * file to CLUST_EOFE.
433 	 */
434 	if (length == 0) {
435 		chaintofree = dep->de_StartCluster;
436 		dep->de_StartCluster = 0;
437 		eofentry = ~0;
438 	} else {
439 		error = pcbmap(dep, lastblock, 0, &eofentry, 0);
440 		if (error) {
441 #ifdef MSDOSFS_DEBUG
442 			printf("detrunc(): pcbmap fails %d\n", error);
443 #endif
444 			return (error);
445 		}
446 	}
447 
448 	fc_purge(dep, lastblock + 1);
449 
450 	/*
451 	 * If the new length is not a multiple of the cluster size then we
452 	 * must zero the tail end of the new last cluster in case it
453 	 * becomes part of the file again because of a seek.
454 	 */
455 	if ((boff = length & pmp->pm_crbomask) != 0) {
456 		if (isadir) {
457 			bn = cntobn(pmp, eofentry);
458 			error = bread(pmp->pm_devvp, de_bn2kb(pmp, bn),
459 			    pmp->pm_bpcluster, NOCRED, &bp);
460 			if (error) {
461 				brelse(bp, 0);
462 #ifdef MSDOSFS_DEBUG
463 				printf("detrunc(): bread fails %d\n", error);
464 #endif
465 				return (error);
466 			}
467 			memset((char *)bp->b_data + boff, 0,
468 			    pmp->pm_bpcluster - boff);
469 			if (flags & IO_SYNC)
470 				bwrite(bp);
471 			else
472 				bdwrite(bp);
473 		} else {
474 			uvm_vnp_zerorange(DETOV(dep), length,
475 					  pmp->pm_bpcluster - boff);
476 		}
477 	}
478 
479 	/*
480 	 * Write out the updated directory entry.  Even if the update fails
481 	 * we free the trailing clusters.
482 	 */
483 	dep->de_FileSize = length;
484 	if (!isadir)
485 		dep->de_flag |= DE_UPDATE|DE_MODIFIED;
486 	vtruncbuf(DETOV(dep), lastblock + 1, 0, 0);
487 	allerror = deupdat(dep, 1);
488 #ifdef MSDOSFS_DEBUG
489 	printf("detrunc(): allerror %d, eofentry %lu\n",
490 	       allerror, eofentry);
491 #endif
492 
493 	/*
494 	 * If we need to break the cluster chain for the file then do it
495 	 * now.
496 	 */
497 	if (eofentry != ~0) {
498 		error = fatentry(FAT_GET_AND_SET, pmp, eofentry,
499 				 &chaintofree, CLUST_EOFE);
500 		if (error) {
501 #ifdef MSDOSFS_DEBUG
502 			printf("detrunc(): fatentry errors %d\n", error);
503 #endif
504 			return (error);
505 		}
506 		fc_setcache(dep, FC_LASTFC, de_cluster(pmp, length - 1),
507 			    eofentry);
508 	}
509 
510 	/*
511 	 * Now free the clusters removed from the file because of the
512 	 * truncation.
513 	 */
514 	if (chaintofree != 0 && !MSDOSFSEOF(chaintofree, pmp->pm_fatmask))
515 		freeclusterchain(pmp, chaintofree);
516 
517 	return (allerror);
518 }
519 
520 /*
521  * Extend the file described by dep to length specified by length.
522  */
523 int
524 deextend(dep, length, cred)
525 	struct denode *dep;
526 	u_long length;
527 	kauth_cred_t cred;
528 {
529 	struct msdosfsmount *pmp = dep->de_pmp;
530 	u_long count, osize;
531 	int error;
532 
533 	/*
534 	 * The root of a DOS filesystem cannot be extended.
535 	 */
536 	if ((DETOV(dep)->v_vflag & VV_ROOT) && !FAT32(pmp))
537 		return (EINVAL);
538 
539 	/*
540 	 * Directories cannot be extended.
541 	 */
542 	if (dep->de_Attributes & ATTR_DIRECTORY)
543 		return (EISDIR);
544 
545 	if (length <= dep->de_FileSize)
546 		panic("deextend: file too large");
547 
548 	/*
549 	 * Compute the number of clusters to allocate.
550 	 */
551 	count = de_clcount(pmp, length) - de_clcount(pmp, dep->de_FileSize);
552 	if (count > 0) {
553 		if (count > pmp->pm_freeclustercount)
554 			return (ENOSPC);
555 		error = extendfile(dep, count, NULL, NULL, DE_CLEAR);
556 		if (error) {
557 			/* truncate the added clusters away again */
558 			(void) detrunc(dep, dep->de_FileSize, 0, cred);
559 			return (error);
560 		}
561 	}
562 
563 	osize = dep->de_FileSize;
564 	dep->de_FileSize = length;
565 	uvm_vnp_setsize(DETOV(dep), (voff_t)dep->de_FileSize);
566 	dep->de_flag |= DE_UPDATE|DE_MODIFIED;
567 	uvm_vnp_zerorange(DETOV(dep), (off_t)osize,
568 	    (size_t)(dep->de_FileSize - osize));
569 	return (deupdat(dep, 1));
570 }
571 
572 /*
573  * Move a denode to its correct hash queue after the file it represents has
574  * been moved to a new directory.
575  */
576 void
577 reinsert(dep)
578 	struct denode *dep;
579 {
580 	/*
581 	 * Fix up the denode cache.  If the denode is for a directory,
582 	 * there is nothing to do since the hash is based on the starting
583 	 * cluster of the directory file and that hasn't changed.  If for a
584 	 * file the hash is based on the location of the directory entry,
585 	 * so we must remove it from the cache and re-enter it with the
586 	 * hash based on the new location of the directory entry.
587 	 */
588 	if (dep->de_Attributes & ATTR_DIRECTORY)
589 		return;
590 	msdosfs_hashrem(dep);
591 	msdosfs_hashins(dep);
592 }
593 
594 int
595 msdosfs_reclaim(v)
596 	void *v;
597 {
598 	struct vop_reclaim_args /* {
599 		struct vnode *a_vp;
600 	} */ *ap = v;
601 	struct vnode *vp = ap->a_vp;
602 	struct denode *dep = VTODE(vp);
603 
604 #ifdef MSDOSFS_DEBUG
605 	printf("msdosfs_reclaim(): dep %p, file %s, refcnt %ld\n",
606 	    dep, dep->de_Name, dep->de_refcnt);
607 #endif
608 
609 	if (prtactive && vp->v_usecount != 0)
610 		vprint("msdosfs_reclaim(): pushing active", vp);
611 	/*
612 	 * Remove the denode from its hash chain.
613 	 */
614 	msdosfs_hashrem(dep);
615 	/*
616 	 * Purge old data structures associated with the denode.
617 	 */
618 	cache_purge(vp);
619 	if (dep->de_devvp) {
620 		vrele(dep->de_devvp);
621 		dep->de_devvp = 0;
622 	}
623 #if 0 /* XXX */
624 	dep->de_flag = 0;
625 #endif
626 	genfs_node_destroy(vp);
627 	pool_put(&msdosfs_denode_pool, dep);
628 	vp->v_data = NULL;
629 	return (0);
630 }
631 
632 int
633 msdosfs_inactive(v)
634 	void *v;
635 {
636 	struct vop_inactive_args /* {
637 		struct vnode *a_vp;
638 	} */ *ap = v;
639 	struct vnode *vp = ap->a_vp;
640 	struct denode *dep = VTODE(vp);
641 	int error = 0;
642 
643 #ifdef MSDOSFS_DEBUG
644 	printf("msdosfs_inactive(): dep %p, de_Name[0] %x\n", dep, dep->de_Name[0]);
645 #endif
646 
647 	if (prtactive && vp->v_usecount != 0)
648 		vprint("msdosfs_inactive(): pushing active", vp);
649 
650 	/*
651 	 * Get rid of denodes related to stale file handles.
652 	 */
653 	if (dep->de_Name[0] == SLOT_DELETED)
654 		goto out;
655 
656 	/*
657 	 * If the file has been deleted and it is on a read/write
658 	 * filesystem, then truncate the file, and mark the directory slot
659 	 * as empty.  (This may not be necessary for the dos filesystem.)
660 	 */
661 #ifdef MSDOSFS_DEBUG
662 	printf("msdosfs_inactive(): dep %p, refcnt %ld, mntflag %x %s\n",
663 	       dep, dep->de_refcnt, vp->v_mount->mnt_flag,
664 		(vp->v_mount->mnt_flag & MNT_RDONLY) ? "MNT_RDONLY" : "");
665 #endif
666 	if (dep->de_refcnt <= 0 && (vp->v_mount->mnt_flag & MNT_RDONLY) == 0) {
667 		if (dep->de_FileSize != 0) {
668 			error = detrunc(dep, (u_long)0, 0, NOCRED);
669 		}
670 		dep->de_Name[0] = SLOT_DELETED;
671 	}
672 	deupdat(dep, 0);
673 out:
674 	VOP_UNLOCK(vp, 0);
675 	/*
676 	 * If we are done with the denode, reclaim it
677 	 * so that it can be reused immediately.
678 	 */
679 #ifdef MSDOSFS_DEBUG
680 	printf("msdosfs_inactive(): v_usecount %d, de_Name[0] %x\n",
681 		vp->v_usecount, dep->de_Name[0]);
682 #endif
683 	if (dep->de_Name[0] == SLOT_DELETED)
684 		vrecycle(vp, (struct simplelock *)0, curlwp);
685 	return (error);
686 }
687 
688 int
689 msdosfs_gop_alloc(struct vnode *vp, off_t off,
690     off_t len, int flags, kauth_cred_t cred)
691 {
692 	return 0;
693 }
694 
695 void
696 msdosfs_gop_markupdate(struct vnode *vp, int flags)
697 {
698 	u_long mask = 0;
699 
700 	if ((flags & GOP_UPDATE_ACCESSED) != 0) {
701 		mask = DE_ACCESS;
702 	}
703 	if ((flags & GOP_UPDATE_MODIFIED) != 0) {
704 		mask |= DE_UPDATE;
705 	}
706 	if (mask) {
707 		struct denode *dep = VTODE(vp);
708 
709 		dep->de_flag |= mask;
710 	}
711 }
712