150477Speter/* $FreeBSD$ */
233548Sjkh/*	$NetBSD: msdosfs_denode.c,v 1.28 1998/02/10 14:10:00 mrg Exp $	*/
32893Sdfr
42893Sdfr/*-
533548Sjkh * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank.
633548Sjkh * Copyright (C) 1994, 1995, 1997 TooLs GmbH.
72893Sdfr * All rights reserved.
82893Sdfr * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below).
92893Sdfr *
102893Sdfr * Redistribution and use in source and binary forms, with or without
112893Sdfr * modification, are permitted provided that the following conditions
122893Sdfr * are met:
132893Sdfr * 1. Redistributions of source code must retain the above copyright
142893Sdfr *    notice, this list of conditions and the following disclaimer.
152893Sdfr * 2. Redistributions in binary form must reproduce the above copyright
162893Sdfr *    notice, this list of conditions and the following disclaimer in the
172893Sdfr *    documentation and/or other materials provided with the distribution.
182893Sdfr * 3. All advertising materials mentioning features or use of this software
192893Sdfr *    must display the following acknowledgement:
202893Sdfr *	This product includes software developed by TooLs GmbH.
212893Sdfr * 4. The name of TooLs GmbH may not be used to endorse or promote products
222893Sdfr *    derived from this software without specific prior written permission.
232893Sdfr *
242893Sdfr * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
252893Sdfr * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
262893Sdfr * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
272893Sdfr * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
282893Sdfr * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
292893Sdfr * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
302893Sdfr * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
312893Sdfr * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
322893Sdfr * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
332893Sdfr * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
342893Sdfr */
35139776Simp/*-
362893Sdfr * Written by Paul Popelka (paulp@uts.amdahl.com)
378876Srgrimes *
382893Sdfr * You can do anything you want with this software, just don't say you wrote
392893Sdfr * it, and don't remove this notice.
408876Srgrimes *
412893Sdfr * This software is provided "as is".
428876Srgrimes *
432893Sdfr * The author supplies this software to be publicly redistributed on the
442893Sdfr * understanding that the author is not responsible for the correct
452893Sdfr * functioning of this software in any circumstances and is not liable for
462893Sdfr * any damages caused by this software.
478876Srgrimes *
482893Sdfr * October 1992
492893Sdfr */
502893Sdfr
512893Sdfr#include <sys/param.h>
522893Sdfr#include <sys/systm.h>
53171750Sbde#include <sys/buf.h>
54171750Sbde#include <sys/clock.h>
5541059Speter#include <sys/kernel.h>
56171750Sbde#include <sys/malloc.h>
572893Sdfr#include <sys/mount.h>
582893Sdfr#include <sys/vnode.h>
592893Sdfr
603152Sphk#include <vm/vm.h>
6112662Sdg#include <vm/vm_extern.h>
623152Sphk
6377162Sru#include <fs/msdosfs/bpb.h>
6477162Sru#include <fs/msdosfs/direntry.h>
6577162Sru#include <fs/msdosfs/denode.h>
6677162Sru#include <fs/msdosfs/fat.h>
67171750Sbde#include <fs/msdosfs/msdosfsmount.h>
682893Sdfr
69151897Srwatsonstatic MALLOC_DEFINE(M_MSDOSFSNODE, "msdosfs_node", "MSDOSFS vnode private part");
7030309Sphk
71144740Sphkstatic int
72144740Sphkde_vncmpf(struct vnode *vp, void *arg)
73144740Sphk{
74144740Sphk	struct denode *de;
75144740Sphk	uint64_t *a;
762893Sdfr
77144740Sphk	a = arg;
78144740Sphk	de = VTODE(vp);
79144740Sphk	return (de->de_inode != *a);
80144740Sphk}
81144740Sphk
822893Sdfr/*
838876Srgrimes * If deget() succeeds it returns with the gotten denode locked().
842893Sdfr *
852893Sdfr * pmp	     - address of msdosfsmount structure of the filesystem containing
86143667Sphk *	       the denode of interest.  The address of
878876Srgrimes *	       the msdosfsmount structure are used.
882893Sdfr * dirclust  - which cluster bp contains, if dirclust is 0 (root directory)
892893Sdfr *	       diroffset is relative to the beginning of the root directory,
908876Srgrimes *	       otherwise it is cluster relative.
918876Srgrimes * diroffset - offset past begin of cluster of denode we want
922893Sdfr * depp	     - returns the address of the gotten denode.
932893Sdfr */
942893Sdfrint
9533548Sjkhdeget(pmp, dirclust, diroffset, depp)
962893Sdfr	struct msdosfsmount *pmp;	/* so we know the maj/min number */
972893Sdfr	u_long dirclust;		/* cluster this dir entry came from */
982893Sdfr	u_long diroffset;		/* index of entry within the cluster */
992893Sdfr	struct denode **depp;		/* returns the addr of the gotten denode */
1002893Sdfr{
1012893Sdfr	int error;
102144740Sphk	uint64_t inode;
1032893Sdfr	struct mount *mntp = pmp->pm_mountp;
10433548Sjkh	struct direntry *direntptr;
1052893Sdfr	struct denode *ldep;
106143570Sphk	struct vnode *nvp, *xvp;
1072893Sdfr	struct buf *bp;
1082893Sdfr
1092893Sdfr#ifdef MSDOSFS_DEBUG
11033548Sjkh	printf("deget(pmp %p, dirclust %lu, diroffset %lx, depp %p)\n",
11133548Sjkh	    pmp, dirclust, diroffset, depp);
1122893Sdfr#endif
1132893Sdfr
1142893Sdfr	/*
11533548Sjkh	 * On FAT32 filesystems, root is a (more or less) normal
11633548Sjkh	 * directory
1172893Sdfr	 */
11833548Sjkh	if (FAT32(pmp) && dirclust == MSDOSFSROOT)
11933548Sjkh		dirclust = pmp->pm_rootdirblk;
1202893Sdfr
1212893Sdfr	/*
1222893Sdfr	 * See if the denode is in the denode cache. Use the location of
1232893Sdfr	 * the directory entry to compute the hash value. For subdir use
12433548Sjkh	 * address of "." entry. For root dir (if not FAT32) use cluster
12533548Sjkh	 * MSDOSFSROOT, offset MSDOSFSROOT_OFS
1268876Srgrimes	 *
1272893Sdfr	 * NOTE: The check for de_refcnt > 0 below insures the denode being
1282893Sdfr	 * examined does not represent an unlinked but still open file.
1292893Sdfr	 * These files are not to be accessible even when the directory
1302893Sdfr	 * entry that represented the file happens to be reused while the
1312893Sdfr	 * deleted file is still open.
1322893Sdfr	 */
133149850Sobrien	inode = (uint64_t)pmp->pm_bpcluster * dirclust + diroffset;
134144740Sphk
135144740Sphk	error = vfs_hash_get(mntp, inode, LK_EXCLUSIVE, curthread, &nvp,
136144740Sphk	    de_vncmpf, &inode);
137143570Sphk	if (error)
138171759Sbde		return (error);
139143570Sphk	if (nvp != NULL) {
140143570Sphk		*depp = VTODE(nvp);
141144740Sphk		KASSERT((*depp)->de_dirclust == dirclust, ("wrong dirclust"));
142144740Sphk		KASSERT((*depp)->de_diroffset == diroffset, ("wrong diroffset"));
14333548Sjkh		return (0);
1442893Sdfr	}
145203826Skib	ldep = malloc(sizeof(struct denode), M_MSDOSFSNODE, M_WAITOK | M_ZERO);
1462893Sdfr
1472893Sdfr	/*
1482893Sdfr	 * Directory entry was not in cache, have to create a vnode and
1492893Sdfr	 * copy it from the passed disk buffer.
1502893Sdfr	 */
1512893Sdfr	/* getnewvnode() does a VREF() on the vnode */
152138290Sphk	error = getnewvnode("msdosfs", mntp, &msdosfs_vnodeops, &nvp);
1533152Sphk	if (error) {
15416312Sdg		*depp = NULL;
155184205Sdes		free(ldep, M_MSDOSFSNODE);
1562893Sdfr		return error;
1572893Sdfr	}
1582893Sdfr	nvp->v_data = ldep;
1592893Sdfr	ldep->de_vnode = nvp;
1602893Sdfr	ldep->de_flag = 0;
1612893Sdfr	ldep->de_dirclust = dirclust;
1622893Sdfr	ldep->de_diroffset = diroffset;
163144740Sphk	ldep->de_inode = inode;
164204466Skib	lockmgr(nvp->v_vnlock, LK_EXCLUSIVE, NULL);
1652893Sdfr	fc_purge(ldep, 0);	/* init the fat cache for this denode */
166167497Stegge	error = insmntque(nvp, mntp);
167167497Stegge	if (error != 0) {
168184205Sdes		free(ldep, M_MSDOSFSNODE);
169167497Stegge		*depp = NULL;
170167497Stegge		return (error);
171167497Stegge	}
172175635Sattilio	error = vfs_hash_insert(nvp, inode, LK_EXCLUSIVE, curthread, &xvp,
173144740Sphk	    de_vncmpf, &inode);
174143570Sphk	if (error) {
175143570Sphk		*depp = NULL;
176143570Sphk		return (error);
177143570Sphk	}
178143570Sphk	if (xvp != NULL) {
179204469Skib		*depp = xvp->v_data;
180204469Skib		return (0);
181143570Sphk	}
1822893Sdfr
18333548Sjkh	ldep->de_pmp = pmp;
18433548Sjkh	ldep->de_refcnt = 1;
1852893Sdfr	/*
1862893Sdfr	 * Copy the directory entry into the denode area of the vnode.
1872893Sdfr	 */
18833548Sjkh	if ((dirclust == MSDOSFSROOT
18933548Sjkh	     || (FAT32(pmp) && dirclust == pmp->pm_rootdirblk))
19033548Sjkh	    && diroffset == MSDOSFSROOT_OFS) {
1912893Sdfr		/*
1922893Sdfr		 * Directory entry for the root directory. There isn't one,
1932893Sdfr		 * so we manufacture one. We should probably rummage
1942893Sdfr		 * through the root directory and find a label entry (if it
1952893Sdfr		 * exists), and then use the time and date from that entry
1962893Sdfr		 * as the time and date for the root denode.
1972893Sdfr		 */
198101308Sjeff		nvp->v_vflag |= VV_ROOT; /* should be further down XXX */
19933548Sjkh
2002893Sdfr		ldep->de_Attributes = ATTR_DIRECTORY;
20141275Sdt		ldep->de_LowerCase = 0;
20233548Sjkh		if (FAT32(pmp))
20333548Sjkh			ldep->de_StartCluster = pmp->pm_rootdirblk;
20433548Sjkh			/* de_FileSize will be filled in further down */
20533548Sjkh		else {
20633548Sjkh			ldep->de_StartCluster = MSDOSFSROOT;
20756674Snyan			ldep->de_FileSize = pmp->pm_rootdirsize * DEV_BSIZE;
20833548Sjkh		}
2092893Sdfr		/*
210163647Sphk		 * fill in time and date so that fattime2timespec() doesn't
2112893Sdfr		 * spit up when called from msdosfs_getattr() with root
2122893Sdfr		 * denode
2132893Sdfr		 */
21433548Sjkh		ldep->de_CHun = 0;
21533548Sjkh		ldep->de_CTime = 0x0000;	/* 00:00:00	 */
21633548Sjkh		ldep->de_CDate = (0 << DD_YEAR_SHIFT) | (1 << DD_MONTH_SHIFT)
2172893Sdfr		    | (1 << DD_DAY_SHIFT);
2182893Sdfr		/* Jan 1, 1980	 */
21933548Sjkh		ldep->de_ADate = ldep->de_CDate;
22033548Sjkh		ldep->de_MTime = ldep->de_CTime;
22133548Sjkh		ldep->de_MDate = ldep->de_CDate;
2222893Sdfr		/* leave the other fields as garbage */
2232893Sdfr	} else {
22433548Sjkh		error = readep(pmp, dirclust, diroffset, &bp, &direntptr);
22536123Sbde		if (error) {
22636123Sbde			/*
22736123Sbde			 * The denode does not contain anything useful, so
22836123Sbde			 * it would be wrong to leave it on its hash chain.
22936123Sbde			 * Arrange for vput() to just forget about it.
23036123Sbde			 */
23136123Sbde			ldep->de_Name[0] = SLOT_DELETED;
23236123Sbde
23336123Sbde			vput(nvp);
23436123Sbde			*depp = NULL;
23533548Sjkh			return (error);
23636123Sbde		}
237213771Srpaulo		(void)DE_INTERNALIZE(ldep, direntptr);
23833548Sjkh		brelse(bp);
2392893Sdfr	}
2402893Sdfr
2412893Sdfr	/*
2422893Sdfr	 * Fill in a few fields of the vnode and finish filling in the
2432893Sdfr	 * denode.  Then return the address of the found denode.
2442893Sdfr	 */
2452893Sdfr	if (ldep->de_Attributes & ATTR_DIRECTORY) {
2462893Sdfr		/*
2472893Sdfr		 * Since DOS directory entries that describe directories
2482893Sdfr		 * have 0 in the filesize field, we take this opportunity
2492893Sdfr		 * to find out the length of the directory and plug it into
2502893Sdfr		 * the denode structure.
2512893Sdfr		 */
2522893Sdfr		u_long size;
2532893Sdfr
254101967Strhodes		/*
255171759Sbde		 * XXX it sometimes happens that the "." entry has cluster
256171759Sbde		 * number 0 when it shouldn't.  Use the actual cluster number
257101967Strhodes		 * instead of what is written in directory entry.
258101967Strhodes		 */
259171759Sbde		if (diroffset == 0 && ldep->de_StartCluster != dirclust) {
260227817Skib#ifdef MSDOSFS_DEBUG
261171759Sbde			printf("deget(): \".\" entry at clust %lu != %lu\n",
262171759Sbde			    dirclust, ldep->de_StartCluster);
263227817Skib#endif
264101967Strhodes			ldep->de_StartCluster = dirclust;
265101967Strhodes		}
266101967Strhodes
2672893Sdfr		nvp->v_type = VDIR;
26833548Sjkh		if (ldep->de_StartCluster != MSDOSFSROOT) {
26933548Sjkh			error = pcbmap(ldep, 0xffff, 0, &size, 0);
2702893Sdfr			if (error == E2BIG) {
27133548Sjkh				ldep->de_FileSize = de_cn2off(pmp, size);
2722893Sdfr				error = 0;
273227817Skib			} else {
274227817Skib#ifdef MSDOSFS_DEBUG
2752893Sdfr				printf("deget(): pcbmap returned %d\n", error);
276227817Skib#endif
277227817Skib			}
2782893Sdfr		}
2792893Sdfr	} else
2802893Sdfr		nvp->v_type = VREG;
281134899Sphk	ldep->de_modrev = init_va_filerev();
2822893Sdfr	*depp = ldep;
28333548Sjkh	return (0);
2842893Sdfr}
2852893Sdfr
2862893Sdfrint
28733548Sjkhdeupdat(dep, waitfor)
2882893Sdfr	struct denode *dep;
2892893Sdfr	int waitfor;
2902893Sdfr{
291250193Skib	struct direntry dir;
292250193Skib	struct timespec ts;
2932893Sdfr	struct buf *bp;
2942893Sdfr	struct direntry *dirp;
295250193Skib	int error;
2962893Sdfr
297250193Skib	if (DETOV(dep)->v_mount->mnt_flag & MNT_RDONLY) {
298250193Skib		dep->de_flag &= ~(DE_UPDATE | DE_CREATE | DE_ACCESS |
299250193Skib		    DE_MODIFIED);
30033548Sjkh		return (0);
301250193Skib	}
30234901Sphk	getnanotime(&ts);
30333548Sjkh	DETIMES(dep, &ts, &ts, &ts);
304250193Skib	if ((dep->de_flag & DE_MODIFIED) == 0 && waitfor == 0)
30533548Sjkh		return (0);
30633548Sjkh	dep->de_flag &= ~DE_MODIFIED;
307254627Sken	if (DETOV(dep)->v_vflag & VV_ROOT)
308254627Sken		return (EINVAL);
30933548Sjkh	if (dep->de_refcnt <= 0)
31033548Sjkh		return (0);
3113152Sphk	error = readde(dep, &bp, &dirp);
3128876Srgrimes	if (error)
31333548Sjkh		return (error);
314250193Skib	DE_EXTERNALIZE(&dir, dep);
315250193Skib	if (bcmp(dirp, &dir, sizeof(dir)) == 0) {
316250193Skib		if (waitfor == 0 || (bp->b_flags & B_DELWRI) == 0) {
317250193Skib			brelse(bp);
318250193Skib			return (0);
319250193Skib		}
320250193Skib	} else
321250193Skib		*dirp = dir;
322250193Skib	if ((DETOV(dep)->v_mount->mnt_flag & MNT_NOCLUSTERW) == 0)
323250193Skib		bp->b_flags |= B_CLUSTEROK;
3242893Sdfr	if (waitfor)
325250193Skib		error = bwrite(bp);
326250193Skib	else if (vm_page_count_severe() || buf_dirty_count_severe())
327250193Skib		bawrite(bp);
328250193Skib	else
3292893Sdfr		bdwrite(bp);
330250193Skib	return (error);
3312893Sdfr}
3322893Sdfr
3332893Sdfr/*
3342893Sdfr * Truncate the file described by dep to the length specified by length.
3352893Sdfr */
3362893Sdfrint
337234605Straszdetrunc(dep, length, flags, cred)
3382893Sdfr	struct denode *dep;
3392893Sdfr	u_long length;
3402893Sdfr	int flags;
3412893Sdfr	struct ucred *cred;
3422893Sdfr{
3432893Sdfr	int error;
3442893Sdfr	int allerror;
3452893Sdfr	u_long eofentry;
3462893Sdfr	u_long chaintofree;
3472893Sdfr	daddr_t bn;
3482893Sdfr	int boff;
3492893Sdfr	int isadir = dep->de_Attributes & ATTR_DIRECTORY;
3502893Sdfr	struct buf *bp;
3512893Sdfr	struct msdosfsmount *pmp = dep->de_pmp;
3522893Sdfr
3532893Sdfr#ifdef MSDOSFS_DEBUG
35433548Sjkh	printf("detrunc(): file %s, length %lu, flags %x\n", dep->de_Name, length, flags);
3552893Sdfr#endif
3562893Sdfr
3572893Sdfr	/*
3582893Sdfr	 * Disallow attempts to truncate the root directory since it is of
3592893Sdfr	 * fixed size.  That's just the way dos filesystems are.  We use
3602893Sdfr	 * the VROOT bit in the vnode because checking for the directory
3612893Sdfr	 * bit and a startcluster of 0 in the denode is not adequate to
3622893Sdfr	 * recognize the root directory at this point in a file or
3632893Sdfr	 * directory's life.
3642893Sdfr	 */
365101308Sjeff	if ((DETOV(dep)->v_vflag & VV_ROOT) && !FAT32(pmp)) {
366227817Skib#ifdef MSDOSFS_DEBUG
36733548Sjkh		printf("detrunc(): can't truncate root directory, clust %ld, offset %ld\n",
3682893Sdfr		    dep->de_dirclust, dep->de_diroffset);
369227817Skib#endif
37033548Sjkh		return (EINVAL);
3712893Sdfr	}
3722893Sdfr
37313490Sdyson	if (dep->de_FileSize < length) {
37413490Sdyson		vnode_pager_setsize(DETOV(dep), length);
3752893Sdfr		return deextend(dep, length, cred);
37613490Sdyson	}
3772893Sdfr
3782893Sdfr	/*
3792893Sdfr	 * If the desired length is 0 then remember the starting cluster of
3802893Sdfr	 * the file and set the StartCluster field in the directory entry
3812893Sdfr	 * to 0.  If the desired length is not zero, then get the number of
3822893Sdfr	 * the last cluster in the shortened file.  Then get the number of
3832893Sdfr	 * the first cluster in the part of the file that is to be freed.
3842893Sdfr	 * Then set the next cluster pointer in the last cluster of the
3852893Sdfr	 * file to CLUST_EOFE.
3862893Sdfr	 */
3872893Sdfr	if (length == 0) {
3882893Sdfr		chaintofree = dep->de_StartCluster;
3892893Sdfr		dep->de_StartCluster = 0;
3902893Sdfr		eofentry = ~0;
3912893Sdfr	} else {
39233548Sjkh		error = pcbmap(dep, de_clcount(pmp, length) - 1, 0,
39333548Sjkh			       &eofentry, 0);
3943152Sphk		if (error) {
3952893Sdfr#ifdef MSDOSFS_DEBUG
3962893Sdfr			printf("detrunc(): pcbmap fails %d\n", error);
3972893Sdfr#endif
39833548Sjkh			return (error);
3992893Sdfr		}
4002893Sdfr	}
4012893Sdfr
40233548Sjkh	fc_purge(dep, de_clcount(pmp, length));
4032893Sdfr
4042893Sdfr	/*
4052893Sdfr	 * If the new length is not a multiple of the cluster size then we
4062893Sdfr	 * must zero the tail end of the new last cluster in case it
4072893Sdfr	 * becomes part of the file again because of a seek.
4082893Sdfr	 */
4092893Sdfr	if ((boff = length & pmp->pm_crbomask) != 0) {
4102893Sdfr		if (isadir) {
4112893Sdfr			bn = cntobn(pmp, eofentry);
4122893Sdfr			error = bread(pmp->pm_devvp, bn, pmp->pm_bpcluster,
4132893Sdfr			    NOCRED, &bp);
414116917Strhodes			if (error) {
415116917Strhodes				brelse(bp);
4162893Sdfr#ifdef MSDOSFS_DEBUG
417116917Strhodes				printf("detrunc(): bread fails %d\n", error);
4182893Sdfr#endif
419116917Strhodes				return (error);
420116917Strhodes			}
421116917Strhodes			bzero(bp->b_data + boff, pmp->pm_bpcluster - boff);
422116917Strhodes			if (flags & IO_SYNC)
423116917Strhodes				bwrite(bp);
424116917Strhodes			else
425116917Strhodes				bdwrite(bp);
4262893Sdfr		}
4272893Sdfr	}
4282893Sdfr
4292893Sdfr	/*
4302893Sdfr	 * Write out the updated directory entry.  Even if the update fails
4312893Sdfr	 * we free the trailing clusters.
4322893Sdfr	 */
4332893Sdfr	dep->de_FileSize = length;
43433548Sjkh	if (!isadir)
435171759Sbde		dep->de_flag |= DE_UPDATE | DE_MODIFIED;
436234605Strasz	allerror = vtruncbuf(DETOV(dep), cred, length, pmp->pm_bpcluster);
4372893Sdfr#ifdef MSDOSFS_DEBUG
43840717Speter	if (allerror)
43940717Speter		printf("detrunc(): vtruncbuf error %d\n", allerror);
44040717Speter#endif
441231998Skib	error = deupdat(dep, !DOINGASYNC((DETOV(dep))));
442171759Sbde	if (error != 0 && allerror == 0)
44340717Speter		allerror = error;
44440717Speter#ifdef MSDOSFS_DEBUG
44533548Sjkh	printf("detrunc(): allerror %d, eofentry %lu\n",
4462893Sdfr	       allerror, eofentry);
4472893Sdfr#endif
4482893Sdfr
4492893Sdfr	/*
4502893Sdfr	 * If we need to break the cluster chain for the file then do it
4512893Sdfr	 * now.
4522893Sdfr	 */
4532893Sdfr	if (eofentry != ~0) {
4542893Sdfr		error = fatentry(FAT_GET_AND_SET, pmp, eofentry,
4552893Sdfr				 &chaintofree, CLUST_EOFE);
4562893Sdfr		if (error) {
4572893Sdfr#ifdef MSDOSFS_DEBUG
4582893Sdfr			printf("detrunc(): fatentry errors %d\n", error);
4592893Sdfr#endif
46033548Sjkh			return (error);
4612893Sdfr		}
46233548Sjkh		fc_setcache(dep, FC_LASTFC, de_cluster(pmp, length - 1),
4632893Sdfr			    eofentry);
4642893Sdfr	}
4652893Sdfr
4662893Sdfr	/*
4672893Sdfr	 * Now free the clusters removed from the file because of the
4682893Sdfr	 * truncation.
4692893Sdfr	 */
47033548Sjkh	if (chaintofree != 0 && !MSDOSFSEOF(pmp, chaintofree))
4712893Sdfr		freeclusterchain(pmp, chaintofree);
4722893Sdfr
47333548Sjkh	return (allerror);
4742893Sdfr}
4752893Sdfr
4762893Sdfr/*
4772893Sdfr * Extend the file described by dep to length specified by length.
4782893Sdfr */
4792893Sdfrint
4802893Sdfrdeextend(dep, length, cred)
4812893Sdfr	struct denode *dep;
48233548Sjkh	u_long length;
4832893Sdfr	struct ucred *cred;
4842893Sdfr{
4852893Sdfr	struct msdosfsmount *pmp = dep->de_pmp;
4862893Sdfr	u_long count;
4872893Sdfr	int error;
4888876Srgrimes
4892893Sdfr	/*
4902893Sdfr	 * The root of a DOS filesystem cannot be extended.
4912893Sdfr	 */
492101308Sjeff	if ((DETOV(dep)->v_vflag & VV_ROOT) && !FAT32(pmp))
49333548Sjkh		return (EINVAL);
4942893Sdfr
4952893Sdfr	/*
49633548Sjkh	 * Directories cannot be extended.
4972893Sdfr	 */
49833548Sjkh	if (dep->de_Attributes & ATTR_DIRECTORY)
49933548Sjkh		return (EISDIR);
5002893Sdfr
5012893Sdfr	if (length <= dep->de_FileSize)
5022893Sdfr		panic("deextend: file too large");
5038876Srgrimes
5042893Sdfr	/*
5052893Sdfr	 * Compute the number of clusters to allocate.
5062893Sdfr	 */
5072893Sdfr	count = de_clcount(pmp, length) - de_clcount(pmp, dep->de_FileSize);
5082893Sdfr	if (count > 0) {
5092893Sdfr		if (count > pmp->pm_freeclustercount)
51033548Sjkh			return (ENOSPC);
5113152Sphk		error = extendfile(dep, count, NULL, NULL, DE_CLEAR);
5123152Sphk		if (error) {
5132893Sdfr			/* truncate the added clusters away again */
514234605Strasz			(void) detrunc(dep, dep->de_FileSize, 0, cred);
51533548Sjkh			return (error);
5162893Sdfr		}
5172893Sdfr	}
5182893Sdfr	dep->de_FileSize = length;
519171759Sbde	dep->de_flag |= DE_UPDATE | DE_MODIFIED;
520231998Skib	return (deupdat(dep, !DOINGASYNC(DETOV(dep))));
5212893Sdfr}
5222893Sdfr
5232893Sdfr/*
5242893Sdfr * Move a denode to its correct hash queue after the file it represents has
5252893Sdfr * been moved to a new directory.
5262893Sdfr */
52733548Sjkhvoid
52833548Sjkhreinsert(dep)
5292893Sdfr	struct denode *dep;
5302893Sdfr{
531143570Sphk	struct vnode *vp;
532143570Sphk
5332893Sdfr	/*
5342893Sdfr	 * Fix up the denode cache.  If the denode is for a directory,
5352893Sdfr	 * there is nothing to do since the hash is based on the starting
5362893Sdfr	 * cluster of the directory file and that hasn't changed.  If for a
5372893Sdfr	 * file the hash is based on the location of the directory entry,
5382893Sdfr	 * so we must remove it from the cache and re-enter it with the
5392893Sdfr	 * hash based on the new location of the directory entry.
5402893Sdfr	 */
541144740Sphk#if 0
54233548Sjkh	if (dep->de_Attributes & ATTR_DIRECTORY)
54333548Sjkh		return;
544144740Sphk#endif
545143570Sphk	vp = DETOV(dep);
546149850Sobrien	dep->de_inode = (uint64_t)dep->de_pmp->pm_bpcluster * dep->de_dirclust +
547171759Sbde	    dep->de_diroffset;
548144740Sphk	vfs_hash_rehash(vp, dep->de_inode);
5492893Sdfr}
5502893Sdfr
5512893Sdfrint
5522893Sdfrmsdosfs_reclaim(ap)
5532893Sdfr	struct vop_reclaim_args /* {
5542893Sdfr		struct vnode *a_vp;
5552893Sdfr	} */ *ap;
5562893Sdfr{
5572893Sdfr	struct vnode *vp = ap->a_vp;
5582893Sdfr	struct denode *dep = VTODE(vp);
5598876Srgrimes
5602893Sdfr#ifdef MSDOSFS_DEBUG
5613498Sphk	printf("msdosfs_reclaim(): dep %p, file %s, refcnt %ld\n",
5622893Sdfr	    dep, dep->de_Name, dep->de_refcnt);
5632893Sdfr#endif
5642893Sdfr
5652893Sdfr	/*
566154487Salfred	 * Destroy the vm object and flush associated pages.
567154487Salfred	 */
568154487Salfred	vnode_destroy_vobject(vp);
569154487Salfred	/*
57033548Sjkh	 * Remove the denode from its hash chain.
5712893Sdfr	 */
572143570Sphk	vfs_hash_remove(vp);
5732893Sdfr	/*
57433548Sjkh	 * Purge old data structures associated with the denode.
5752893Sdfr	 */
57633548Sjkh#if 0 /* XXX */
5772893Sdfr	dep->de_flag = 0;
57833548Sjkh#endif
579184205Sdes	free(dep, M_MSDOSFSNODE);
5802893Sdfr	vp->v_data = NULL;
5818876Srgrimes
58233548Sjkh	return (0);
5832893Sdfr}
5842893Sdfr
5852893Sdfrint
5862893Sdfrmsdosfs_inactive(ap)
5872893Sdfr	struct vop_inactive_args /* {
5882893Sdfr		struct vnode *a_vp;
58983366Sjulian		struct thread *a_td;
5902893Sdfr	} */ *ap;
5912893Sdfr{
5922893Sdfr	struct vnode *vp = ap->a_vp;
5932893Sdfr	struct denode *dep = VTODE(vp);
5942893Sdfr	int error = 0;
5958876Srgrimes
5962893Sdfr#ifdef MSDOSFS_DEBUG
5973498Sphk	printf("msdosfs_inactive(): dep %p, de_Name[0] %x\n", dep, dep->de_Name[0]);
5982893Sdfr#endif
5992893Sdfr
6002893Sdfr	/*
60133548Sjkh	 * Ignore denodes related to stale file handles.
6022893Sdfr	 */
603204468Skib	if (dep->de_Name[0] == SLOT_DELETED || dep->de_Name[0] == SLOT_EMPTY)
60423134Sbde		goto out;
6052893Sdfr
6062893Sdfr	/*
6072893Sdfr	 * If the file has been deleted and it is on a read/write
6082893Sdfr	 * filesystem, then truncate the file, and mark the directory slot
6092893Sdfr	 * as empty.  (This may not be necessary for the dos filesystem.)
6102893Sdfr	 */
6112893Sdfr#ifdef MSDOSFS_DEBUG
6123498Sphk	printf("msdosfs_inactive(): dep %p, refcnt %ld, mntflag %x, MNT_RDONLY %x\n",
6132893Sdfr	       dep, dep->de_refcnt, vp->v_mount->mnt_flag, MNT_RDONLY);
6142893Sdfr#endif
6152893Sdfr	if (dep->de_refcnt <= 0 && (vp->v_mount->mnt_flag & MNT_RDONLY) == 0) {
616234605Strasz		error = detrunc(dep, (u_long) 0, 0, NOCRED);
6172893Sdfr		dep->de_flag |= DE_UPDATE;
6182893Sdfr		dep->de_Name[0] = SLOT_DELETED;
6192893Sdfr	}
62033548Sjkh	deupdat(dep, 0);
62133548Sjkh
62223134Sbdeout:
6232893Sdfr	/*
62433548Sjkh	 * If we are done with the denode, reclaim it
62533548Sjkh	 * so that it can be reused immediately.
6262893Sdfr	 */
6272893Sdfr#ifdef MSDOSFS_DEBUG
628103936Sjeff	printf("msdosfs_inactive(): v_usecount %d, de_Name[0] %x\n",
629103936Sjeff	       vrefcnt(vp), dep->de_Name[0]);
6302893Sdfr#endif
631204468Skib	if (dep->de_Name[0] == SLOT_DELETED || dep->de_Name[0] == SLOT_EMPTY)
632234607Strasz		vrecycle(vp);
63333548Sjkh	return (error);
6342893Sdfr}
635