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