diff --git a/sys/fs/msdosfs/fat.h b/sys/fs/msdosfs/fat.h
index bb0f128eaa87..b28a9bed1356 100644
--- a/sys/fs/msdosfs/fat.h
+++ b/sys/fs/msdosfs/fat.h
@@ -1,115 +1,115 @@
 /* $FreeBSD$ */
 /*	$NetBSD: fat.h,v 1.12 1997/11/17 15:36:36 ws Exp $	*/
 
 /*-
  * SPDX-License-Identifier: BSD-4-Clause
  *
  * Copyright (C) 1994, 1997 Wolfgang Solfrank.
  * Copyright (C) 1994, 1997 TooLs GmbH.
  * All rights reserved.
  * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below).
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  * are met:
  * 1. Redistributions of source code must retain the above copyright
  *    notice, this list of conditions and the following disclaimer.
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
  * 3. All advertising materials mentioning features or use of this software
  *    must display the following acknowledgement:
  *	This product includes software developed by TooLs GmbH.
  * 4. The name of TooLs GmbH may not be used to endorse or promote products
  *    derived from this software without specific prior written permission.
  *
  * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 /*-
  * Written by Paul Popelka (paulp@uts.amdahl.com)
  *
  * You can do anything you want with this software, just don't say you wrote
  * it, and don't remove this notice.
  *
  * This software is provided "as is".
  *
  * The author supplies this software to be publicly redistributed on the
  * understanding that the author is not responsible for the correct
  * functioning of this software in any circumstances and is not liable for
  * any damages caused by this software.
  *
  * October 1992
  */
 
 #ifndef _FS_MSDOSFS_FAT_H_
 #define	_FS_MSDOSFS_FAT_H_
 /*
  * Some useful cluster numbers.
  */
 #define	MSDOSFSROOT	0		/* cluster 0 means the root dir */
 #define	CLUST_FREE	0		/* cluster 0 also means a free cluster */
 #define	MSDOSFSFREE	CLUST_FREE
 #define	CLUST_FIRST	2		/* first legal cluster number */
 #define	CLUST_RSRVD	0xfffffff6	/* reserved cluster range */
 #define	CLUST_BAD	0xfffffff7	/* a cluster with a defect */
 #define	CLUST_EOFS	0xfffffff8	/* start of eof cluster range */
 #define	CLUST_EOFE	0xffffffff	/* end of eof cluster range */
 
 #define	FAT12_MASK	0x00000fff	/* mask for 12 bit cluster numbers */
 #define	FAT16_MASK	0x0000ffff	/* mask for 16 bit cluster numbers */
 #define	FAT32_MASK	0x0fffffff	/* mask for FAT32 cluster numbers */
 
 /*
  * MSDOSFS:
  * Return true if filesystem uses 12 bit FATs. Microsoft Programmer's
  * Reference says if the maximum cluster number in a filesystem is greater
  * than 4084 ((CLUST_RSRVD - CLUST_FIRST) & FAT12_MASK) then we've got a
  * 16 bit FAT filesystem. While mounting, the result of this test is stored
  * in pm_fatentrysize.
  */
 #define	FAT12(pmp)	(pmp->pm_fatmask == FAT12_MASK)
 #define	FAT16(pmp)	(pmp->pm_fatmask == FAT16_MASK)
 #define	FAT32(pmp)	(pmp->pm_fatmask == FAT32_MASK)
 
 #define	MSDOSFSEOF(pmp, cn)	((((cn) | ~(pmp)->pm_fatmask) & CLUST_EOFS) == CLUST_EOFS)
 
 #if defined (_KERNEL) || defined(MAKEFS)
 /*
  * These are the values for the function argument to the function
  * fatentry().
  */
 #define	FAT_GET		0x0001	/* get a FAT entry */
 #define	FAT_SET		0x0002	/* set a FAT entry */
 #define	FAT_GET_AND_SET	(FAT_GET | FAT_SET)
 
 /*
  * Flags to extendfile:
  */
 #define	DE_CLEAR	1	/* Zero out the blocks allocated */
 
 int pcbmap(struct denode *dep, u_long findcn, daddr_t *bnp, u_long *cnp, int* sp);
-int clusterfree(struct msdosfsmount *pmp, u_long cn, u_long *oldcnp);
+void clusterfree(struct msdosfsmount *pmp, u_long cn);
 int clusteralloc(struct msdosfsmount *pmp, u_long start, u_long count, u_long fillwith, u_long *retcluster, u_long *got);
 int fatentry(int function, struct msdosfsmount *pmp, u_long cluster, u_long *oldcontents, u_long newcontents);
 int freeclusterchain(struct msdosfsmount *pmp, u_long startchain);
 int extendfile(struct denode *dep, u_long count, struct buf **bpp, u_long *ncp, int flags);
 void fc_purge(struct denode *dep, u_int frcn);
 int markvoldirty_upgrade(struct msdosfsmount *pmp, bool dirty, bool rw_upgrade);
 
 static inline int
 markvoldirty(struct msdosfsmount *pmp, bool dirty)
 {
 	return (markvoldirty_upgrade(pmp, dirty, false));
 }
 
 #endif	/* _KERNEL || MAKEFS */
 #endif	/* !_FS_MSDOSFS_FAT_H_ */
diff --git a/sys/fs/msdosfs/msdosfs_fat.c b/sys/fs/msdosfs/msdosfs_fat.c
index 202feeef0102..fc1db705989b 100644
--- a/sys/fs/msdosfs/msdosfs_fat.c
+++ b/sys/fs/msdosfs/msdosfs_fat.c
@@ -1,1212 +1,1209 @@
 /* $FreeBSD$ */
 /*	$NetBSD: msdosfs_fat.c,v 1.28 1997/11/17 15:36:49 ws Exp $	*/
 
 /*-
  * SPDX-License-Identifier: BSD-4-Clause
  *
  * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank.
  * Copyright (C) 1994, 1995, 1997 TooLs GmbH.
  * All rights reserved.
  * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below).
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  * are met:
  * 1. Redistributions of source code must retain the above copyright
  *    notice, this list of conditions and the following disclaimer.
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
  * 3. All advertising materials mentioning features or use of this software
  *    must display the following acknowledgement:
  *	This product includes software developed by TooLs GmbH.
  * 4. The name of TooLs GmbH may not be used to endorse or promote products
  *    derived from this software without specific prior written permission.
  *
  * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 /*-
  * Written by Paul Popelka (paulp@uts.amdahl.com)
  *
  * You can do anything you want with this software, just don't say you wrote
  * it, and don't remove this notice.
  *
  * This software is provided "as is".
  *
  * The author supplies this software to be publicly redistributed on the
  * understanding that the author is not responsible for the correct
  * functioning of this software in any circumstances and is not liable for
  * any damages caused by this software.
  *
  * October 1992
  */
 
 #include <sys/param.h>
 #include <sys/systm.h>
 #include <sys/buf.h>
 #include <sys/mount.h>
 #include <sys/vmmeter.h>
 #include <sys/vnode.h>
 
 #include <fs/msdosfs/bpb.h>
 #include <fs/msdosfs/direntry.h>
 #include <fs/msdosfs/denode.h>
 #include <fs/msdosfs/fat.h>
 #include <fs/msdosfs/msdosfsmount.h>
 
 #define	FULL_RUN	((u_int)0xffffffff)
 
 static int	chainalloc(struct msdosfsmount *pmp, u_long start,
 		    u_long count, u_long fillwith, u_long *retcluster,
 		    u_long *got);
 static int	chainlength(struct msdosfsmount *pmp, u_long start,
 		    u_long count);
 static void	fatblock(struct msdosfsmount *pmp, u_long ofs, u_long *bnp,
 		    u_long *sizep, u_long *bop);
 static int	fatchain(struct msdosfsmount *pmp, u_long start, u_long count,
 		    u_long fillwith);
 static void	fc_lookup(struct denode *dep, u_long findcn, u_long *frcnp,
 		    u_long *fsrcnp);
 static void	updatefats(struct msdosfsmount *pmp, struct buf *bp,
 		    u_long fatbn);
 static __inline void
 		usemap_alloc(struct msdosfsmount *pmp, u_long cn);
 static int	usemap_free(struct msdosfsmount *pmp, u_long cn);
 static int	clusteralloc1(struct msdosfsmount *pmp, u_long start,
 		    u_long count, u_long fillwith, u_long *retcluster,
 		    u_long *got);
 
 static void
 fatblock(struct msdosfsmount *pmp, u_long ofs, u_long *bnp, u_long *sizep,
     u_long *bop)
 {
 	u_long bn, size;
 
 	bn = ofs / pmp->pm_fatblocksize * pmp->pm_fatblocksec;
 	size = min(pmp->pm_fatblocksec, pmp->pm_FATsecs - bn)
 	    * DEV_BSIZE;
 	bn += pmp->pm_fatblk + pmp->pm_curfat * pmp->pm_FATsecs;
 
 	if (bnp)
 		*bnp = bn;
 	if (sizep)
 		*sizep = size;
 	if (bop)
 		*bop = ofs % pmp->pm_fatblocksize;
 }
 
 /*
  * Map the logical cluster number of a file into a physical disk sector
  * that is filesystem relative.
  *
  * dep	  - address of denode representing the file of interest
  * findcn - file relative cluster whose filesystem relative cluster number
  *	    and/or block number are/is to be found
  * bnp	  - address of where to place the filesystem relative block number.
  *	    If this pointer is null then don't return this quantity.
  * cnp	  - address of where to place the filesystem relative cluster number.
  *	    If this pointer is null then don't return this quantity.
  * sp     - pointer to returned block size
  *
  * NOTE: Either bnp or cnp must be non-null.
  * This function has one side effect.  If the requested file relative cluster
  * is beyond the end of file, then the actual number of clusters in the file
  * is returned in *cnp.  This is useful for determining how long a directory is.
  *  If cnp is null, nothing is returned.
  */
 int
 pcbmap(struct denode *dep, u_long findcn, daddr_t *bnp, u_long *cnp, int *sp)
 {
 	int error;
 	u_long i;
 	u_long cn;
 	u_long prevcn = 0; /* XXX: prevcn could be used unititialized */
 	u_long byteoffset;
 	u_long bn;
 	u_long bo;
 	struct buf *bp = NULL;
 	u_long bp_bn = -1;
 	struct msdosfsmount *pmp = dep->de_pmp;
 	u_long bsize;
 
 	KASSERT(bnp != NULL || cnp != NULL || sp != NULL,
 	    ("pcbmap: extra call"));
 	ASSERT_VOP_ELOCKED(DETOV(dep), "pcbmap");
 
 	cn = dep->de_StartCluster;
 	/*
 	 * The "file" that makes up the root directory is contiguous,
 	 * permanently allocated, of fixed size, and is not made up of
 	 * clusters.  If the cluster number is beyond the end of the root
 	 * directory, then return the number of clusters in the file.
 	 */
 	if (cn == MSDOSFSROOT) {
 		if (dep->de_Attributes & ATTR_DIRECTORY) {
 			if (de_cn2off(pmp, findcn) >= dep->de_FileSize) {
 				if (cnp)
 					*cnp = de_bn2cn(pmp, pmp->pm_rootdirsize);
 				return (E2BIG);
 			}
 			if (bnp)
 				*bnp = pmp->pm_rootdirblk + de_cn2bn(pmp, findcn);
 			if (cnp)
 				*cnp = MSDOSFSROOT;
 			if (sp)
 				*sp = min(pmp->pm_bpcluster,
 				    dep->de_FileSize - de_cn2off(pmp, findcn));
 			return (0);
 		} else {		/* just an empty file */
 			if (cnp)
 				*cnp = 0;
 			return (E2BIG);
 		}
 	}
 
 	/*
 	 * All other files do I/O in cluster sized blocks
 	 */
 	if (sp)
 		*sp = pmp->pm_bpcluster;
 
 	/*
 	 * Rummage around in the FAT cache, maybe we can avoid tromping
 	 * through every FAT entry for the file. And, keep track of how far
 	 * off the cache was from where we wanted to be.
 	 */
 	i = 0;
 	fc_lookup(dep, findcn, &i, &cn);
 
 	/*
 	 * Handle all other files or directories the normal way.
 	 */
 	for (; i < findcn; i++) {
 		/*
 		 * Stop with all reserved clusters, not just with EOF.
 		 */
 		if ((cn | ~pmp->pm_fatmask) >= CLUST_RSRVD)
 			goto hiteof;
 		byteoffset = FATOFS(pmp, cn);
 		fatblock(pmp, byteoffset, &bn, &bsize, &bo);
 		if (bn != bp_bn) {
 			if (bp)
 				brelse(bp);
 			error = bread(pmp->pm_devvp, bn, bsize, NOCRED, &bp);
 			if (error) {
 				return (error);
 			}
 			bp_bn = bn;
 		}
 		prevcn = cn;
 		if (bo >= bsize) {
 			if (bp)
 				brelse(bp);
 			return (EIO);
 		}
 		if (FAT32(pmp))
 			cn = getulong(bp->b_data + bo);
 		else
 			cn = getushort(bp->b_data + bo);
 		if (FAT12(pmp) && (prevcn & 1))
 			cn >>= 4;
 		cn &= pmp->pm_fatmask;
 
 		/*
 		 * Force the special cluster numbers
 		 * to be the same for all cluster sizes
 		 * to let the rest of msdosfs handle
 		 * all cases the same.
 		 */
 		if ((cn | ~pmp->pm_fatmask) >= CLUST_RSRVD)
 			cn |= ~pmp->pm_fatmask;
 	}
 
 	if (!MSDOSFSEOF(pmp, cn)) {
 		if (bp)
 			brelse(bp);
 		if (bnp)
 			*bnp = cntobn(pmp, cn);
 		if (cnp)
 			*cnp = cn;
 		fc_setcache(dep, FC_LASTMAP, i, cn);
 		return (0);
 	}
 
 hiteof:;
 	if (cnp)
 		*cnp = i;
 	if (bp)
 		brelse(bp);
 	/* update last file cluster entry in the FAT cache */
 	fc_setcache(dep, FC_LASTFC, i - 1, prevcn);
 	return (E2BIG);
 }
 
 /*
  * Find the closest entry in the FAT cache to the cluster we are looking
  * for.
  */
 static void
 fc_lookup(struct denode *dep, u_long findcn, u_long *frcnp, u_long *fsrcnp)
 {
 	int i;
 	u_long cn;
 	struct fatcache *closest = NULL;
 
 	ASSERT_VOP_LOCKED(DETOV(dep), "fc_lookup");
 
 	for (i = 0; i < FC_SIZE; i++) {
 		cn = dep->de_fc[i].fc_frcn;
 		if (cn != FCE_EMPTY && cn <= findcn) {
 			if (closest == NULL || cn > closest->fc_frcn)
 				closest = &dep->de_fc[i];
 		}
 	}
 	if (closest) {
 		*frcnp = closest->fc_frcn;
 		*fsrcnp = closest->fc_fsrcn;
 	}
 }
 
 /*
  * Purge the FAT cache in denode dep of all entries relating to file
  * relative cluster frcn and beyond.
  */
 void
 fc_purge(struct denode *dep, u_int frcn)
 {
 	int i;
 	struct fatcache *fcp;
 
 	ASSERT_VOP_ELOCKED(DETOV(dep), "fc_purge");
 
 	fcp = dep->de_fc;
 	for (i = 0; i < FC_SIZE; i++, fcp++) {
 		if (fcp->fc_frcn >= frcn)
 			fcp->fc_frcn = FCE_EMPTY;
 	}
 }
 
 /*
  * Update the FAT.
  * If mirroring the FAT, update all copies, with the first copy as last.
  * Else update only the current FAT (ignoring the others).
  *
  * pmp	 - msdosfsmount structure for filesystem to update
  * bp	 - addr of modified FAT block
  * fatbn - block number relative to begin of filesystem of the modified FAT block.
  */
 static void
 updatefats(struct msdosfsmount *pmp, struct buf *bp, u_long fatbn)
 {
 	struct buf *bpn;
 	int cleanfat, i;
 
 #ifdef MSDOSFS_DEBUG
 	printf("updatefats(pmp %p, bp %p, fatbn %lu)\n", pmp, bp, fatbn);
 #endif
 
 	if (pmp->pm_flags & MSDOSFS_FATMIRROR) {
 		/*
 		 * Now copy the block(s) of the modified FAT to the other copies of
 		 * the FAT and write them out.  This is faster than reading in the
 		 * other FATs and then writing them back out.  This could tie up
 		 * the FAT for quite a while. Preventing others from accessing it.
 		 * To prevent us from going after the FAT quite so much we use
 		 * delayed writes, unless they specified "synchronous" when the
 		 * filesystem was mounted.  If synch is asked for then use
 		 * bwrite()'s and really slow things down.
 		 */
 		if (fatbn != pmp->pm_fatblk || FAT12(pmp))
 			cleanfat = 0;
 		else if (FAT16(pmp))
 			cleanfat = 16;
 		else
 			cleanfat = 32;
 		for (i = 1; i < pmp->pm_FATs; i++) {
 			fatbn += pmp->pm_FATsecs;
 			/* getblk() never fails */
 			bpn = getblk(pmp->pm_devvp, fatbn, bp->b_bcount,
 			    0, 0, 0);
 			memcpy(bpn->b_data, bp->b_data, bp->b_bcount);
 			/* Force the clean bit on in the other copies. */
 			if (cleanfat == 16)
 				((uint8_t *)bpn->b_data)[3] |= 0x80;
 			else if (cleanfat == 32)
 				((uint8_t *)bpn->b_data)[7] |= 0x08;
 			if (pmp->pm_mountp->mnt_flag & MNT_SYNCHRONOUS)
 				bwrite(bpn);
 			else
 				bdwrite(bpn);
 		}
 	}
 
 	/*
 	 * Write out the first (or current) FAT last.
 	 */
 	if (pmp->pm_mountp->mnt_flag & MNT_SYNCHRONOUS)
 		bwrite(bp);
 	else
 		bdwrite(bp);
 }
 
 /*
  * Updating entries in 12 bit FATs is a pain in the butt.
  *
  * The following picture shows where nibbles go when moving from a 12 bit
  * cluster number into the appropriate bytes in the FAT.
  *
  *	byte m        byte m+1      byte m+2
  *	+----+----+   +----+----+   +----+----+
  *	|  0    1 |   |  2    3 |   |  4    5 |   FAT bytes
  *	+----+----+   +----+----+   +----+----+
  *
  *	+----+----+----+   +----+----+----+
  *	|  3    0    1 |   |  4    5    2 |
  *	+----+----+----+   +----+----+----+
  *	cluster n  	   cluster n+1
  *
  * Where n is even. m = n + (n >> 2)
  *
  */
 static __inline void
 usemap_alloc(struct msdosfsmount *pmp, u_long cn)
 {
 
 	MSDOSFS_ASSERT_MP_LOCKED(pmp);
 
 	KASSERT(cn <= pmp->pm_maxcluster, ("cn too large %lu %lu", cn,
 	    pmp->pm_maxcluster));
 	KASSERT((pmp->pm_flags & MSDOSFSMNT_RONLY) == 0,
 	    ("usemap_alloc on ro msdosfs mount"));
 	KASSERT((pmp->pm_inusemap[cn / N_INUSEBITS] &
 	    (1U << (cn % N_INUSEBITS))) == 0,
 	    ("Allocating used sector %ld %ld %x", cn, cn % N_INUSEBITS,
 	    (unsigned)pmp->pm_inusemap[cn / N_INUSEBITS]));
 	pmp->pm_inusemap[cn / N_INUSEBITS] |= 1U << (cn % N_INUSEBITS);
 	KASSERT(pmp->pm_freeclustercount > 0, ("usemap_alloc: too little"));
 	pmp->pm_freeclustercount--;
 	pmp->pm_flags |= MSDOSFS_FSIMOD;
 }
 
 static int
 usemap_free(struct msdosfsmount *pmp, u_long cn)
 {
 
 	MSDOSFS_ASSERT_MP_LOCKED(pmp);
 
 	KASSERT(cn <= pmp->pm_maxcluster, ("cn too large %lu %lu", cn,
 	    pmp->pm_maxcluster));
 	KASSERT((pmp->pm_flags & MSDOSFSMNT_RONLY) == 0,
 	    ("usemap_free on ro msdosfs mount"));
 	if ((pmp->pm_inusemap[cn / N_INUSEBITS] &
 	    (1U << (cn % N_INUSEBITS))) == 0) {
 		printf("%s: Freeing unused sector %ld %ld %x\n",
 		    pmp->pm_mountp->mnt_stat.f_mntonname, cn, cn % N_INUSEBITS,
 		    (unsigned)pmp->pm_inusemap[cn / N_INUSEBITS]);
 		return (EINTEGRITY);
 	}
 	pmp->pm_freeclustercount++;
 	pmp->pm_flags |= MSDOSFS_FSIMOD;
 	pmp->pm_inusemap[cn / N_INUSEBITS] &= ~(1U << (cn % N_INUSEBITS));
 	return (0);
 }
 
-int
-clusterfree(struct msdosfsmount *pmp, u_long cluster, u_long *oldcnp)
+void
+clusterfree(struct msdosfsmount *pmp, u_long cluster)
 {
 	int error;
 	u_long oldcn;
 
 	error = fatentry(FAT_GET_AND_SET, pmp, cluster, &oldcn, MSDOSFSFREE);
-	if (error)
-		return (error);
+	if (error != 0)
+		return;
 	/*
 	 * If the cluster was successfully marked free, then update
 	 * the count of free clusters, and turn off the "allocated"
 	 * bit in the "in use" cluster bit map.
 	 */
 	MSDOSFS_LOCK_MP(pmp);
 	error = usemap_free(pmp, cluster);
 	MSDOSFS_UNLOCK_MP(pmp);
-	if (error == 0 && oldcnp != NULL)
-		*oldcnp = oldcn;
-	return (error);
 }
 
 /*
  * Get or Set or 'Get and Set' the cluster'th entry in the FAT.
  *
  * function	- whether to get or set a FAT entry
  * pmp		- address of the msdosfsmount structure for the filesystem
  *		  whose FAT is to be manipulated.
  * cn		- which cluster is of interest
  * oldcontents	- address of a word that is to receive the contents of the
  *		  cluster'th entry if this is a get function
  * newcontents	- the new value to be written into the cluster'th element of
  *		  the FAT if this is a set function.
  *
  * This function can also be used to free a cluster by setting the FAT entry
  * for a cluster to 0.
  *
  * All copies of the FAT are updated if this is a set function. NOTE: If
  * fatentry() marks a cluster as free it does not update the inusemap in
  * the msdosfsmount structure. This is left to the caller.
  */
 int
 fatentry(int function, struct msdosfsmount *pmp, u_long cn, u_long *oldcontents,
     u_long newcontents)
 {
 	int error;
 	u_long readcn;
 	u_long bn, bo, bsize, byteoffset;
 	struct buf *bp;
 
 #ifdef	MSDOSFS_DEBUG
 	printf("fatentry(func %d, pmp %p, clust %lu, oldcon %p, newcon %lx)\n",
 	    function, pmp, cn, oldcontents, newcontents);
 #endif
 
 #ifdef DIAGNOSTIC
 	/*
 	 * Be sure they asked us to do something.
 	 */
 	if ((function & (FAT_SET | FAT_GET)) == 0) {
 #ifdef MSDOSFS_DEBUG
 		printf("fatentry(): function code doesn't specify get or set\n");
 #endif
 		return (EINVAL);
 	}
 
 	/*
 	 * If they asked us to return a cluster number but didn't tell us
 	 * where to put it, give them an error.
 	 */
 	if ((function & FAT_GET) && oldcontents == NULL) {
 #ifdef MSDOSFS_DEBUG
 		printf("fatentry(): get function with no place to put result\n");
 #endif
 		return (EINVAL);
 	}
 #endif
 
 	/*
 	 * Be sure the requested cluster is in the filesystem.
 	 */
 	if (cn < CLUST_FIRST || cn > pmp->pm_maxcluster)
 		return (EINVAL);
 
 	byteoffset = FATOFS(pmp, cn);
 	fatblock(pmp, byteoffset, &bn, &bsize, &bo);
 	error = bread(pmp->pm_devvp, bn, bsize, NOCRED, &bp);
 	if (error) {
 		return (error);
 	}
 
 	if (function & FAT_GET) {
 		if (FAT32(pmp))
 			readcn = getulong(bp->b_data + bo);
 		else
 			readcn = getushort(bp->b_data + bo);
 		if (FAT12(pmp) & (cn & 1))
 			readcn >>= 4;
 		readcn &= pmp->pm_fatmask;
 		/* map reserved FAT entries to same values for all FATs */
 		if ((readcn | ~pmp->pm_fatmask) >= CLUST_RSRVD)
 			readcn |= ~pmp->pm_fatmask;
 		*oldcontents = readcn;
 	}
 	if (function & FAT_SET) {
 		switch (pmp->pm_fatmask) {
 		case FAT12_MASK:
 			readcn = getushort(bp->b_data + bo);
 			if (cn & 1) {
 				readcn &= 0x000f;
 				readcn |= newcontents << 4;
 			} else {
 				readcn &= 0xf000;
 				readcn |= newcontents & 0xfff;
 			}
 			putushort(bp->b_data + bo, readcn);
 			break;
 		case FAT16_MASK:
 			putushort(bp->b_data + bo, newcontents);
 			break;
 		case FAT32_MASK:
 			/*
 			 * According to spec we have to retain the
 			 * high order bits of the FAT entry.
 			 */
 			readcn = getulong(bp->b_data + bo);
 			readcn &= ~FAT32_MASK;
 			readcn |= newcontents & FAT32_MASK;
 			putulong(bp->b_data + bo, readcn);
 			break;
 		}
 		updatefats(pmp, bp, bn);
 		bp = NULL;
 		pmp->pm_fmod = 1;
 	}
 	if (bp)
 		brelse(bp);
 	return (0);
 }
 
 /*
  * Update a contiguous cluster chain
  *
  * pmp	    - mount point
  * start    - first cluster of chain
  * count    - number of clusters in chain
  * fillwith - what to write into FAT entry of last cluster
  */
 static int
 fatchain(struct msdosfsmount *pmp, u_long start, u_long count, u_long fillwith)
 {
 	int error;
 	u_long bn, bo, bsize, byteoffset, readcn, newc;
 	struct buf *bp;
 
 #ifdef MSDOSFS_DEBUG
 	printf("fatchain(pmp %p, start %lu, count %lu, fillwith %lx)\n",
 	    pmp, start, count, fillwith);
 #endif
 	/*
 	 * Be sure the clusters are in the filesystem.
 	 */
 	if (start < CLUST_FIRST || start + count - 1 > pmp->pm_maxcluster)
 		return (EINVAL);
 
 	while (count > 0) {
 		byteoffset = FATOFS(pmp, start);
 		fatblock(pmp, byteoffset, &bn, &bsize, &bo);
 		error = bread(pmp->pm_devvp, bn, bsize, NOCRED, &bp);
 		if (error) {
 			return (error);
 		}
 		while (count > 0) {
 			start++;
 			newc = --count > 0 ? start : fillwith;
 			switch (pmp->pm_fatmask) {
 			case FAT12_MASK:
 				readcn = getushort(bp->b_data + bo);
 				if (start & 1) {
 					readcn &= 0xf000;
 					readcn |= newc & 0xfff;
 				} else {
 					readcn &= 0x000f;
 					readcn |= newc << 4;
 				}
 				putushort(bp->b_data + bo, readcn);
 				bo++;
 				if (!(start & 1))
 					bo++;
 				break;
 			case FAT16_MASK:
 				putushort(bp->b_data + bo, newc);
 				bo += 2;
 				break;
 			case FAT32_MASK:
 				readcn = getulong(bp->b_data + bo);
 				readcn &= ~pmp->pm_fatmask;
 				readcn |= newc & pmp->pm_fatmask;
 				putulong(bp->b_data + bo, readcn);
 				bo += 4;
 				break;
 			}
 			if (bo >= bsize)
 				break;
 		}
 		updatefats(pmp, bp, bn);
 	}
 	pmp->pm_fmod = 1;
 	return (0);
 }
 
 /*
  * Check the length of a free cluster chain starting at start.
  *
  * pmp	 - mount point
  * start - start of chain
  * count - maximum interesting length
  */
 static int
 chainlength(struct msdosfsmount *pmp, u_long start, u_long count)
 {
 	u_long idx, max_idx;
 	u_int map;
 	u_long len;
 
 	MSDOSFS_ASSERT_MP_LOCKED(pmp);
 
 	if (start > pmp->pm_maxcluster)
 		return (0);
 	max_idx = pmp->pm_maxcluster / N_INUSEBITS;
 	idx = start / N_INUSEBITS;
 	start %= N_INUSEBITS;
 	map = pmp->pm_inusemap[idx];
 	map &= ~((1U << start) - 1);
 	if (map) {
 		len = ffs(map) - 1 - start;
 		len = MIN(len, count);
 		if (start + len > pmp->pm_maxcluster)
 			len = pmp->pm_maxcluster - start + 1;
 		return (len);
 	}
 	len = N_INUSEBITS - start;
 	if (len >= count) {
 		len = count;
 		if (start + len > pmp->pm_maxcluster)
 			len = pmp->pm_maxcluster - start + 1;
 		return (len);
 	}
 	while (++idx <= max_idx) {
 		if (len >= count)
 			break;
 		map = pmp->pm_inusemap[idx];
 		if (map) {
 			len += ffs(map) - 1;
 			break;
 		}
 		len += N_INUSEBITS;
 	}
 	len = MIN(len, count);
 	if (start + len > pmp->pm_maxcluster)
 		len = pmp->pm_maxcluster - start + 1;
 	return (len);
 }
 
 /*
  * Allocate contigous free clusters.
  *
  * pmp	      - mount point.
  * start      - start of cluster chain.
  * count      - number of clusters to allocate.
  * fillwith   - put this value into the FAT entry for the
  *		last allocated cluster.
  * retcluster - put the first allocated cluster's number here.
  * got	      - how many clusters were actually allocated.
  */
 static int
 chainalloc(struct msdosfsmount *pmp, u_long start, u_long count,
     u_long fillwith, u_long *retcluster, u_long *got)
 {
 	int error;
 	u_long cl, n;
 
 	MSDOSFS_ASSERT_MP_LOCKED(pmp);
 	KASSERT((pmp->pm_flags & MSDOSFSMNT_RONLY) == 0,
 	    ("chainalloc on ro msdosfs mount"));
 
 	for (cl = start, n = count; n-- > 0;)
 		usemap_alloc(pmp, cl++);
 	pmp->pm_nxtfree = start + count;
 	if (pmp->pm_nxtfree > pmp->pm_maxcluster)
 		pmp->pm_nxtfree = CLUST_FIRST;
 	pmp->pm_flags |= MSDOSFS_FSIMOD;
 	error = fatchain(pmp, start, count, fillwith);
 	if (error != 0) {
 		for (cl = start, n = count; n-- > 0;)
 			(void)usemap_free(pmp, cl++);
 		return (error);
 	}
 #ifdef MSDOSFS_DEBUG
 	printf("clusteralloc(): allocated cluster chain at %lu (%lu clusters)\n",
 	    start, count);
 #endif
 	if (retcluster)
 		*retcluster = start;
 	if (got)
 		*got = count;
 	return (0);
 }
 
 /*
  * Allocate contiguous free clusters.
  *
  * pmp	      - mount point.
  * start      - preferred start of cluster chain.
  * count      - number of clusters requested.
  * fillwith   - put this value into the FAT entry for the
  *		last allocated cluster.
  * retcluster - put the first allocated cluster's number here.
  * got	      - how many clusters were actually allocated.
  */
 int
 clusteralloc(struct msdosfsmount *pmp, u_long start, u_long count,
     u_long fillwith, u_long *retcluster, u_long *got)
 {
 	int error;
 
 	MSDOSFS_LOCK_MP(pmp);
 	error = clusteralloc1(pmp, start, count, fillwith, retcluster, got);
 	MSDOSFS_UNLOCK_MP(pmp);
 	return (error);
 }
 
 static int
 clusteralloc1(struct msdosfsmount *pmp, u_long start, u_long count,
     u_long fillwith, u_long *retcluster, u_long *got)
 {
 	u_long idx;
 	u_long len, newst, foundl, cn, l;
 	u_long foundcn = 0; /* XXX: foundcn could be used unititialized */
 	u_int map;
 
 	MSDOSFS_ASSERT_MP_LOCKED(pmp);
 
 #ifdef MSDOSFS_DEBUG
 	printf("clusteralloc(): find %lu clusters\n", count);
 #endif
 	if (start) {
 		if ((len = chainlength(pmp, start, count)) >= count)
 			return (chainalloc(pmp, start, count, fillwith, retcluster, got));
 	} else
 		len = 0;
 
 	newst = pmp->pm_nxtfree;
 	foundl = 0;
 
 	for (cn = newst; cn <= pmp->pm_maxcluster;) {
 		idx = cn / N_INUSEBITS;
 		map = pmp->pm_inusemap[idx];
 		map |= (1U << (cn % N_INUSEBITS)) - 1;
 		if (map != FULL_RUN) {
 			cn = idx * N_INUSEBITS + ffs(map ^ FULL_RUN) - 1;
 			if ((l = chainlength(pmp, cn, count)) >= count)
 				return (chainalloc(pmp, cn, count, fillwith, retcluster, got));
 			if (l > foundl) {
 				foundcn = cn;
 				foundl = l;
 			}
 			cn += l + 1;
 			continue;
 		}
 		cn += N_INUSEBITS - cn % N_INUSEBITS;
 	}
 	for (cn = 0; cn < newst;) {
 		idx = cn / N_INUSEBITS;
 		map = pmp->pm_inusemap[idx];
 		map |= (1U << (cn % N_INUSEBITS)) - 1;
 		if (map != FULL_RUN) {
 			cn = idx * N_INUSEBITS + ffs(map ^ FULL_RUN) - 1;
 			if ((l = chainlength(pmp, cn, count)) >= count)
 				return (chainalloc(pmp, cn, count, fillwith, retcluster, got));
 			if (l > foundl) {
 				foundcn = cn;
 				foundl = l;
 			}
 			cn += l + 1;
 			continue;
 		}
 		cn += N_INUSEBITS - cn % N_INUSEBITS;
 	}
 
 	if (!foundl)
 		return (ENOSPC);
 
 	if (len)
 		return (chainalloc(pmp, start, len, fillwith, retcluster, got));
 	else
 		return (chainalloc(pmp, foundcn, foundl, fillwith, retcluster, got));
 }
 
 /*
  * Free a chain of clusters.
  *
  * pmp		- address of the msdosfs mount structure for the filesystem
  *		  containing the cluster chain to be freed.
  * startcluster - number of the 1st cluster in the chain of clusters to be
  *		  freed.
  */
 int
 freeclusterchain(struct msdosfsmount *pmp, u_long cluster)
 {
 	int error;
 	struct buf *bp = NULL;
 	u_long bn, bo, bsize, byteoffset;
 	u_long readcn, lbn = -1;
 
 	MSDOSFS_LOCK_MP(pmp);
 	while (cluster >= CLUST_FIRST && cluster <= pmp->pm_maxcluster) {
 		byteoffset = FATOFS(pmp, cluster);
 		fatblock(pmp, byteoffset, &bn, &bsize, &bo);
 		if (lbn != bn) {
 			if (bp)
 				updatefats(pmp, bp, lbn);
 			error = bread(pmp->pm_devvp, bn, bsize, NOCRED, &bp);
 			if (error) {
 				MSDOSFS_UNLOCK_MP(pmp);
 				return (error);
 			}
 			lbn = bn;
 		}
 		error = usemap_free(pmp, cluster);
 		if (error != 0) {
 			updatefats(pmp, bp, lbn);
 			MSDOSFS_UNLOCK_MP(pmp);
 			return (error);
 		}
 		switch (pmp->pm_fatmask) {
 		case FAT12_MASK:
 			readcn = getushort(bp->b_data + bo);
 			if (cluster & 1) {
 				cluster = readcn >> 4;
 				readcn &= 0x000f;
 				readcn |= MSDOSFSFREE << 4;
 			} else {
 				cluster = readcn;
 				readcn &= 0xf000;
 				readcn |= MSDOSFSFREE & 0xfff;
 			}
 			putushort(bp->b_data + bo, readcn);
 			break;
 		case FAT16_MASK:
 			cluster = getushort(bp->b_data + bo);
 			putushort(bp->b_data + bo, MSDOSFSFREE);
 			break;
 		case FAT32_MASK:
 			cluster = getulong(bp->b_data + bo);
 			putulong(bp->b_data + bo,
 				 (MSDOSFSFREE & FAT32_MASK) | (cluster & ~FAT32_MASK));
 			break;
 		}
 		cluster &= pmp->pm_fatmask;
 		if ((cluster | ~pmp->pm_fatmask) >= CLUST_RSRVD)
 			cluster |= pmp->pm_fatmask;
 	}
 	if (bp)
 		updatefats(pmp, bp, bn);
 	MSDOSFS_UNLOCK_MP(pmp);
 	return (0);
 }
 
 /*
  * Read in FAT blocks looking for free clusters. For every free cluster
  * found turn off its corresponding bit in the pm_inusemap.
  */
 int
 fillinusemap(struct msdosfsmount *pmp)
 {
 	struct buf *bp;
 	u_long bn, bo, bsize, byteoffset, cn, readcn;
 	int error;
 
 	MSDOSFS_ASSERT_MP_LOCKED(pmp);
 	bp = NULL;
 
 	/*
 	 * Mark all clusters in use, we mark the free ones in the FAT scan
 	 * loop further down.
 	 */
 	for (cn = 0; cn < (pmp->pm_maxcluster + N_INUSEBITS) / N_INUSEBITS; cn++)
 		pmp->pm_inusemap[cn] = FULL_RUN;
 
 	/*
 	 * Figure how many free clusters are in the filesystem by ripping
 	 * through the FAT counting the number of entries whose content is
 	 * zero.  These represent free clusters.
 	 */
 	pmp->pm_freeclustercount = 0;
 	for (cn = 0; cn <= pmp->pm_maxcluster; cn++) {
 		byteoffset = FATOFS(pmp, cn);
 		bo = byteoffset % pmp->pm_fatblocksize;
 		if (bo == 0) {
 			/* Read new FAT block */
 			if (bp != NULL)
 				brelse(bp);
 			fatblock(pmp, byteoffset, &bn, &bsize, NULL);
 			error = bread(pmp->pm_devvp, bn, bsize, NOCRED, &bp);
 			if (error != 0)
 				return (error);
 		}
 		if (FAT32(pmp))
 			readcn = getulong(bp->b_data + bo);
 		else
 			readcn = getushort(bp->b_data + bo);
 		if (FAT12(pmp) && (cn & 1))
 			readcn >>= 4;
 		readcn &= pmp->pm_fatmask;
 
 		/*
 		 * Check if the FAT ID matches the BPB's media descriptor and
 		 * all other bits are set to 1.
 		 */
 		if (cn == 0 && readcn != ((pmp->pm_fatmask & 0xffffff00) |
 		    pmp->pm_bpb.bpbMedia)) {
 #ifdef MSDOSFS_DEBUG
 			printf("mountmsdosfs(): Media descriptor in BPB"
 			    "does not match FAT ID\n");
 #endif
 			brelse(bp);
 			return (EINVAL);
 		} else if (readcn == CLUST_FREE) {
 			error = usemap_free(pmp, cn);
 			if (error != 0) {
 				brelse(bp);
 				return (error);
 			}
 		}
 	}
 	if (bp != NULL)
 		brelse(bp);
 
 	for (cn = pmp->pm_maxcluster + 1; cn < (pmp->pm_maxcluster +
 	    N_INUSEBITS) / N_INUSEBITS; cn++)
 		pmp->pm_inusemap[cn / N_INUSEBITS] |= 1U << (cn % N_INUSEBITS);
 
 	return (0);
 }
 
 /*
  * Allocate a new cluster and chain it onto the end of the file.
  *
  * dep	 - the file to extend
  * count - number of clusters to allocate
  * bpp	 - where to return the address of the buf header for the first new
  *	   file block
  * ncp	 - where to put cluster number of the first newly allocated cluster
  *	   If this pointer is 0, do not return the cluster number.
  * flags - see fat.h
  *
  * NOTE: This function is not responsible for turning on the DE_UPDATE bit of
  * the de_flag field of the denode and it does not change the de_FileSize
  * field.  This is left for the caller to do.
  */
 int
 extendfile(struct denode *dep, u_long count, struct buf **bpp, u_long *ncp,
     int flags)
 {
 	int error;
 	u_long frcn;
 	u_long cn, got;
 	struct msdosfsmount *pmp = dep->de_pmp;
 	struct buf *bp;
 	daddr_t blkno;
 
 	/*
 	 * Don't try to extend the root directory
 	 */
 	if (dep->de_StartCluster == MSDOSFSROOT
 	    && (dep->de_Attributes & ATTR_DIRECTORY)) {
 #ifdef MSDOSFS_DEBUG
 		printf("extendfile(): attempt to extend root directory\n");
 #endif
 		return (ENOSPC);
 	}
 
 	/*
 	 * If the "file's last cluster" cache entry is empty, and the file
 	 * is not empty, then fill the cache entry by calling pcbmap().
 	 */
 	if (dep->de_fc[FC_LASTFC].fc_frcn == FCE_EMPTY &&
 	    dep->de_StartCluster != 0) {
 		error = pcbmap(dep, 0xffff, 0, &cn, 0);
 		/* we expect it to return E2BIG */
 		if (error != E2BIG)
 			return (error);
 	}
 
 	dep->de_fc[FC_NEXTTOLASTFC].fc_frcn =
 	    dep->de_fc[FC_LASTFC].fc_frcn;
 	dep->de_fc[FC_NEXTTOLASTFC].fc_fsrcn =
 	    dep->de_fc[FC_LASTFC].fc_fsrcn;
 	while (count > 0) {
 		/*
 		 * Allocate a new cluster chain and cat onto the end of the
 		 * file.  If the file is empty we make de_StartCluster point
 		 * to the new block.  Note that de_StartCluster being 0 is
 		 * sufficient to be sure the file is empty since we exclude
 		 * attempts to extend the root directory above, and the root
 		 * dir is the only file with a startcluster of 0 that has
 		 * blocks allocated (sort of).
 		 */
 		if (dep->de_StartCluster == 0)
 			cn = 0;
 		else
 			cn = dep->de_fc[FC_LASTFC].fc_fsrcn + 1;
 		error = clusteralloc(pmp, cn, count, CLUST_EOFE, &cn, &got);
 		if (error)
 			return (error);
 
 		count -= got;
 
 		/*
 		 * Give them the filesystem relative cluster number if they want
 		 * it.
 		 */
 		if (ncp) {
 			*ncp = cn;
 			ncp = NULL;
 		}
 
 		if (dep->de_StartCluster == 0) {
 			dep->de_StartCluster = cn;
 			frcn = 0;
 		} else {
 			error = fatentry(FAT_SET, pmp,
 					 dep->de_fc[FC_LASTFC].fc_fsrcn,
 					 0, cn);
 			if (error) {
-				clusterfree(pmp, cn, NULL);
+				clusterfree(pmp, cn);
 				return (error);
 			}
 			frcn = dep->de_fc[FC_LASTFC].fc_frcn + 1;
 		}
 
 		/*
 		 * Update the "last cluster of the file" entry in the
 		 * denode's FAT cache.
 		 */
 		fc_setcache(dep, FC_LASTFC, frcn + got - 1, cn + got - 1);
 
 		if (flags & DE_CLEAR) {
 			while (got-- > 0) {
 				/*
 				 * Get the buf header for the new block of the file.
 				 */
 				if (dep->de_Attributes & ATTR_DIRECTORY)
 					bp = getblk(pmp->pm_devvp,
 					    cntobn(pmp, cn++),
 					    pmp->pm_bpcluster, 0, 0, 0);
 				else {
 					bp = getblk(DETOV(dep),
 					    frcn++,
 					    pmp->pm_bpcluster, 0, 0, 0);
 					/*
 					 * Do the bmap now, as in msdosfs_write
 					 */
 					if (pcbmap(dep,
 					    bp->b_lblkno,
 					    &blkno, 0, 0))
 						bp->b_blkno = -1;
 					if (bp->b_blkno == -1)
 						panic("extendfile: pcbmap");
 					else
 						bp->b_blkno = blkno;
 				}
 				clrbuf(bp);
 				if (bpp) {
 					*bpp = bp;
 					bpp = NULL;
 				} else {
 					bdwrite(bp);
 				}
 				if (vm_page_count_severe() ||
 				    buf_dirty_count_severe())
 					vn_fsync_buf(DETOV(dep), MNT_WAIT);
 			}
 		}
 	}
 
 	return (0);
 }
 
 /*-
  * Routine to mark a FAT16 or FAT32 volume as "clean" or "dirty" by
  * manipulating the upper bit of the FAT entry for cluster 1.  Note that
  * this bit is not defined for FAT12 volumes, which are always assumed to
  * be clean.
  *
  * The fatentry() routine only works on cluster numbers that a file could
  * occupy, so it won't manipulate the entry for cluster 1.  So we have to do
  * it here.  The code was stolen from fatentry() and tailored for cluster 1.
  *
  * Inputs:
  *	pmp	The MS-DOS volume to mark
  *	dirty	Non-zero if the volume should be marked dirty; zero if it
  *		should be marked clean
  *
  * Result:
  *	0	Success
  *	EROFS	Volume is read-only
  *	?	(other errors from called routines)
  */
 int
 markvoldirty_upgrade(struct msdosfsmount *pmp, bool dirty, bool rw_upgrade)
 {
 	struct buf *bp;
 	u_long bn, bo, bsize, byteoffset, fatval;
 	int error;
 
 	/*
 	 * FAT12 does not support a "clean" bit, so don't do anything for
 	 * FAT12.
 	 */
 	if (FAT12(pmp))
 		return (0);
 
 	/*
 	 * Can't change the bit on a read-only filesystem, except as part of
 	 * ro->rw upgrade.
 	 */
 	if ((pmp->pm_flags & MSDOSFSMNT_RONLY) != 0 && !rw_upgrade)
 		return (EROFS);
 
 	/*
 	 * Fetch the block containing the FAT entry.  It is given by the
 	 * pseudo-cluster 1.
 	 */
 	byteoffset = FATOFS(pmp, 1);
 	fatblock(pmp, byteoffset, &bn, &bsize, &bo);
 	error = bread(pmp->pm_devvp, bn, bsize, NOCRED, &bp);
 	if (error)
 		return (error);
 
 	/*
 	 * Get the current value of the FAT entry and set/clear the relevant
 	 * bit.  Dirty means clear the "clean" bit; clean means set the
 	 * "clean" bit.
 	 */
 	if (FAT32(pmp)) {
 		/* FAT32 uses bit 27. */
 		fatval = getulong(&bp->b_data[bo]);
 		if (dirty)
 			fatval &= 0xF7FFFFFF;
 		else
 			fatval |= 0x08000000;
 		putulong(&bp->b_data[bo], fatval);
 	} else {
 		/* Must be FAT16; use bit 15. */
 		fatval = getushort(&bp->b_data[bo]);
 		if (dirty)
 			fatval &= 0x7FFF;
 		else
 			fatval |= 0x8000;
 		putushort(&bp->b_data[bo], fatval);
 	}
 
 	/*
 	 * The concern here is that a devvp may be readonly, without reporting
 	 * itself as such through the usual channels.  In that case, we'd like
 	 * it if attempting to mount msdosfs rw didn't panic the system.
 	 *
 	 * markvoldirty is invoked as the first write on backing devvps when
 	 * either msdosfs is mounted for the first time, or a ro mount is
 	 * upgraded to rw.
 	 *
 	 * In either event, if a write error occurs dirtying the volume:
 	 *   - No user data has been permitted to be written to cache yet.
 	 *   - We can abort the high-level operation (mount, or ro->rw) safely.
 	 *   - We don't derive any benefit from leaving a zombie dirty buf in
 	 *   the cache that can not be cleaned or evicted.
 	 *
 	 * So, mark B_INVALONERR to have bwrite() -> brelse() detect that
 	 * condition and force-invalidate our write to the block if it occurs.
 	 *
 	 * PR 210316 provides more context on the discovery and diagnosis of
 	 * the problem, as well as earlier attempts to solve it.
 	 */
 	bp->b_flags |= B_INVALONERR;
 
 	/* Write out the modified FAT block synchronously. */
 	return (bwrite(bp));
 }
diff --git a/sys/fs/msdosfs/msdosfs_vnops.c b/sys/fs/msdosfs/msdosfs_vnops.c
index d64dcf4266bb..58f7f2876523 100644
--- a/sys/fs/msdosfs/msdosfs_vnops.c
+++ b/sys/fs/msdosfs/msdosfs_vnops.c
@@ -1,2005 +1,2005 @@
 /* $FreeBSD$ */
 /*	$NetBSD: msdosfs_vnops.c,v 1.68 1998/02/10 14:10:04 mrg Exp $	*/
 
 /*-
  * SPDX-License-Identifier: BSD-4-Clause
  *
  * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank.
  * Copyright (C) 1994, 1995, 1997 TooLs GmbH.
  * All rights reserved.
  * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below).
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  * are met:
  * 1. Redistributions of source code must retain the above copyright
  *    notice, this list of conditions and the following disclaimer.
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
  * 3. All advertising materials mentioning features or use of this software
  *    must display the following acknowledgement:
  *	This product includes software developed by TooLs GmbH.
  * 4. The name of TooLs GmbH may not be used to endorse or promote products
  *    derived from this software without specific prior written permission.
  *
  * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 /*-
  * Written by Paul Popelka (paulp@uts.amdahl.com)
  *
  * You can do anything you want with this software, just don't say you wrote
  * it, and don't remove this notice.
  *
  * This software is provided "as is".
  *
  * The author supplies this software to be publicly redistributed on the
  * understanding that the author is not responsible for the correct
  * functioning of this software in any circumstances and is not liable for
  * any damages caused by this software.
  *
  * October 1992
  */
 
 #include <sys/param.h>
 #include <sys/systm.h>
 #include <sys/bio.h>
 #include <sys/buf.h>
 #include <sys/clock.h>
 #include <sys/dirent.h>
 #include <sys/lock.h>
 #include <sys/lockf.h>
 #include <sys/malloc.h>
 #include <sys/mount.h>
 #include <sys/mutex.h>
 #include <sys/namei.h>
 #include <sys/priv.h>
 #include <sys/stat.h>
 #include <sys/sysctl.h>
 #include <sys/unistd.h>
 #include <sys/vmmeter.h>
 #include <sys/vnode.h>
 
 #include <vm/vm.h>
 #include <vm/vm_extern.h>
 #include <vm/vnode_pager.h>
 
 #include <fs/msdosfs/bpb.h>
 #include <fs/msdosfs/direntry.h>
 #include <fs/msdosfs/denode.h>
 #include <fs/msdosfs/fat.h>
 #include <fs/msdosfs/msdosfsmount.h>
 
 /*
  * Prototypes for MSDOSFS vnode operations
  */
 static vop_create_t	msdosfs_create;
 static vop_mknod_t	msdosfs_mknod;
 static vop_open_t	msdosfs_open;
 static vop_close_t	msdosfs_close;
 static vop_access_t	msdosfs_access;
 static vop_getattr_t	msdosfs_getattr;
 static vop_setattr_t	msdosfs_setattr;
 static vop_read_t	msdosfs_read;
 static vop_write_t	msdosfs_write;
 static vop_fsync_t	msdosfs_fsync;
 static vop_remove_t	msdosfs_remove;
 static vop_link_t	msdosfs_link;
 static vop_rename_t	msdosfs_rename;
 static vop_mkdir_t	msdosfs_mkdir;
 static vop_rmdir_t	msdosfs_rmdir;
 static vop_symlink_t	msdosfs_symlink;
 static vop_readdir_t	msdosfs_readdir;
 static vop_bmap_t	msdosfs_bmap;
 static vop_getpages_t	msdosfs_getpages;
 static vop_strategy_t	msdosfs_strategy;
 static vop_print_t	msdosfs_print;
 static vop_pathconf_t	msdosfs_pathconf;
 static vop_vptofh_t	msdosfs_vptofh;
 
 /*
  * Some general notes:
  *
  * In the ufs filesystem the inodes, superblocks, and indirect blocks are
  * read/written using the vnode for the filesystem. Blocks that represent
  * the contents of a file are read/written using the vnode for the file
  * (including directories when they are read/written as files). This
  * presents problems for the dos filesystem because data that should be in
  * an inode (if dos had them) resides in the directory itself.  Since we
  * must update directory entries without the benefit of having the vnode
  * for the directory we must use the vnode for the filesystem.  This means
  * that when a directory is actually read/written (via read, write, or
  * readdir, or seek) we must use the vnode for the filesystem instead of
  * the vnode for the directory as would happen in ufs. This is to insure we
  * retrieve the correct block from the buffer cache since the hash value is
  * based upon the vnode address and the desired block number.
  */
 
 /*
  * Create a regular file. On entry the directory to contain the file being
  * created is locked.  We must release before we return. We must also free
  * the pathname buffer pointed at by cnp->cn_pnbuf, always on error, or
  * only if the SAVESTART bit in cn_flags is clear on success.
  */
 static int
 msdosfs_create(struct vop_create_args *ap)
 {
 	struct componentname *cnp = ap->a_cnp;
 	struct denode ndirent;
 	struct denode *dep;
 	struct denode *pdep = VTODE(ap->a_dvp);
 	struct timespec ts;
 	int error;
 
 #ifdef MSDOSFS_DEBUG
 	printf("msdosfs_create(cnp %p, vap %p\n", cnp, ap->a_vap);
 #endif
 
 	/*
 	 * If this is the root directory and there is no space left we
 	 * can't do anything.  This is because the root directory can not
 	 * change size.
 	 */
 	if (pdep->de_StartCluster == MSDOSFSROOT
 	    && pdep->de_fndoffset >= pdep->de_FileSize) {
 		error = ENOSPC;
 		goto bad;
 	}
 
 	/*
 	 * Create a directory entry for the file, then call createde() to
 	 * have it installed. NOTE: DOS files are always executable.  We
 	 * use the absence of the owner write bit to make the file
 	 * readonly.
 	 */
 #ifdef DIAGNOSTIC
 	if ((cnp->cn_flags & HASBUF) == 0)
 		panic("msdosfs_create: no name");
 #endif
 	memset(&ndirent, 0, sizeof(ndirent));
 	error = uniqdosname(pdep, cnp, ndirent.de_Name);
 	if (error)
 		goto bad;
 
 	ndirent.de_Attributes = ATTR_ARCHIVE;
 	ndirent.de_LowerCase = 0;
 	ndirent.de_StartCluster = 0;
 	ndirent.de_FileSize = 0;
 	ndirent.de_pmp = pdep->de_pmp;
 	ndirent.de_flag = DE_ACCESS | DE_CREATE | DE_UPDATE;
 	vfs_timestamp(&ts);
 	DETIMES(&ndirent, &ts, &ts, &ts);
 	error = createde(&ndirent, pdep, &dep, cnp);
 	if (error)
 		goto bad;
 	*ap->a_vpp = DETOV(dep);
 	if ((cnp->cn_flags & MAKEENTRY) != 0)
 		cache_enter(ap->a_dvp, *ap->a_vpp, cnp);
 	return (0);
 
 bad:
 	return (error);
 }
 
 static int
 msdosfs_mknod(struct vop_mknod_args *ap)
 {
 
     return (EINVAL);
 }
 
 static int
 msdosfs_open(struct vop_open_args *ap)
 {
 	struct denode *dep = VTODE(ap->a_vp);
 	vnode_create_vobject(ap->a_vp, dep->de_FileSize, ap->a_td);
 	return 0;
 }
 
 static int
 msdosfs_close(struct vop_close_args *ap)
 {
 	struct vnode *vp = ap->a_vp;
 	struct denode *dep = VTODE(vp);
 	struct timespec ts;
 
 	VI_LOCK(vp);
 	if (vp->v_usecount > 1) {
 		vfs_timestamp(&ts);
 		DETIMES(dep, &ts, &ts, &ts);
 	}
 	VI_UNLOCK(vp);
 	return 0;
 }
 
 static int
 msdosfs_access(struct vop_access_args *ap)
 {
 	struct vnode *vp = ap->a_vp;
 	struct denode *dep = VTODE(ap->a_vp);
 	struct msdosfsmount *pmp = dep->de_pmp;
 	mode_t file_mode;
 	accmode_t accmode = ap->a_accmode;
 
 	file_mode = S_IRWXU|S_IRWXG|S_IRWXO;
 	file_mode &= (vp->v_type == VDIR ? pmp->pm_dirmask : pmp->pm_mask);
 
 	/*
 	 * Disallow writing to directories and regular files if the
 	 * filesystem is read-only.
 	 */
 	if (accmode & VWRITE) {
 		switch (vp->v_type) {
 		case VREG:
 		case VDIR:
 			if (vp->v_mount->mnt_flag & MNT_RDONLY)
 				return (EROFS);
 			break;
 		default:
 			break;
 		}
 	}
 
 	return (vaccess(vp->v_type, file_mode, pmp->pm_uid, pmp->pm_gid,
 	    ap->a_accmode, ap->a_cred));
 }
 
 static int
 msdosfs_getattr(struct vop_getattr_args *ap)
 {
 	struct denode *dep = VTODE(ap->a_vp);
 	struct msdosfsmount *pmp = dep->de_pmp;
 	struct vattr *vap = ap->a_vap;
 	mode_t mode;
 	struct timespec ts;
 	u_long dirsperblk = pmp->pm_BytesPerSec / sizeof(struct direntry);
 	uint64_t fileid;
 
 	vfs_timestamp(&ts);
 	DETIMES(dep, &ts, &ts, &ts);
 	vap->va_fsid = dev2udev(pmp->pm_dev);
 	/*
 	 * The following computation of the fileid must be the same as that
 	 * used in msdosfs_readdir() to compute d_fileno. If not, pwd
 	 * doesn't work.
 	 */
 	if (dep->de_Attributes & ATTR_DIRECTORY) {
 		fileid = (uint64_t)cntobn(pmp, dep->de_StartCluster) *
 		    dirsperblk;
 		if (dep->de_StartCluster == MSDOSFSROOT)
 			fileid = 1;
 	} else {
 		fileid = (uint64_t)cntobn(pmp, dep->de_dirclust) *
 		    dirsperblk;
 		if (dep->de_dirclust == MSDOSFSROOT)
 			fileid = (uint64_t)roottobn(pmp, 0) * dirsperblk;
 		fileid += (uoff_t)dep->de_diroffset / sizeof(struct direntry);
 	}
 	vap->va_fileid = fileid;
 
 	mode = S_IRWXU|S_IRWXG|S_IRWXO;
 	if (dep->de_Attributes & ATTR_READONLY)
 		mode &= ~(S_IWUSR|S_IWGRP|S_IWOTH);
 	vap->va_mode = mode &
 	    (ap->a_vp->v_type == VDIR ? pmp->pm_dirmask : pmp->pm_mask);
 	vap->va_uid = pmp->pm_uid;
 	vap->va_gid = pmp->pm_gid;
 	vap->va_nlink = 1;
 	vap->va_rdev = NODEV;
 	vap->va_size = dep->de_FileSize;
 	fattime2timespec(dep->de_MDate, dep->de_MTime, 0, 0, &vap->va_mtime);
 	vap->va_ctime = vap->va_mtime;
 	if (pmp->pm_flags & MSDOSFSMNT_LONGNAME) {
 		fattime2timespec(dep->de_ADate, 0, 0, 0, &vap->va_atime);
 		fattime2timespec(dep->de_CDate, dep->de_CTime, dep->de_CHun,
 		    0, &vap->va_birthtime);
 	} else {
 		vap->va_atime = vap->va_mtime;
 		vap->va_birthtime.tv_sec = -1;
 		vap->va_birthtime.tv_nsec = 0;
 	}
 	vap->va_flags = 0;
 	if (dep->de_Attributes & ATTR_ARCHIVE)
 		vap->va_flags |= UF_ARCHIVE;
 	if (dep->de_Attributes & ATTR_HIDDEN)
 		vap->va_flags |= UF_HIDDEN;
 	if (dep->de_Attributes & ATTR_READONLY)
 		vap->va_flags |= UF_READONLY;
 	if (dep->de_Attributes & ATTR_SYSTEM)
 		vap->va_flags |= UF_SYSTEM;
 	vap->va_gen = 0;
 	vap->va_blocksize = pmp->pm_bpcluster;
 	vap->va_bytes =
 	    (dep->de_FileSize + pmp->pm_crbomask) & ~pmp->pm_crbomask;
 	vap->va_type = ap->a_vp->v_type;
 	vap->va_filerev = dep->de_modrev;
 	return (0);
 }
 
 static int
 msdosfs_setattr(struct vop_setattr_args *ap)
 {
 	struct vnode *vp = ap->a_vp;
 	struct denode *dep = VTODE(ap->a_vp);
 	struct msdosfsmount *pmp = dep->de_pmp;
 	struct vattr *vap = ap->a_vap;
 	struct ucred *cred = ap->a_cred;
 	struct thread *td = curthread;
 	int error = 0;
 
 #ifdef MSDOSFS_DEBUG
 	printf("msdosfs_setattr(): vp %p, vap %p, cred %p\n",
 	    ap->a_vp, vap, cred);
 #endif
 
 	/*
 	 * Check for unsettable attributes.
 	 */
 	if ((vap->va_type != VNON) || (vap->va_nlink != VNOVAL) ||
 	    (vap->va_fsid != VNOVAL) || (vap->va_fileid != VNOVAL) ||
 	    (vap->va_blocksize != VNOVAL) || (vap->va_rdev != VNOVAL) ||
 	    (vap->va_bytes != VNOVAL) || (vap->va_gen != VNOVAL)) {
 #ifdef MSDOSFS_DEBUG
 		printf("msdosfs_setattr(): returning EINVAL\n");
 		printf("    va_type %d, va_nlink %llx, va_fsid %llx, va_fileid %llx\n",
 		    vap->va_type, (unsigned long long)vap->va_nlink,
 		    (unsigned long long)vap->va_fsid,
 		    (unsigned long long)vap->va_fileid);
 		printf("    va_blocksize %lx, va_rdev %llx, va_bytes %llx, va_gen %lx\n",
 		    vap->va_blocksize, (unsigned long long)vap->va_rdev,
 		    (unsigned long long)vap->va_bytes, vap->va_gen);
 		printf("    va_uid %x, va_gid %x\n",
 		    vap->va_uid, vap->va_gid);
 #endif
 		return (EINVAL);
 	}
 
 	/*
 	 * We don't allow setting attributes on the root directory.
 	 * The special case for the root directory is because before
 	 * FAT32, the root directory didn't have an entry for itself
 	 * (and was otherwise special).  With FAT32, the root
 	 * directory is not so special, but still doesn't have an
 	 * entry for itself.
 	 */
 	if (vp->v_vflag & VV_ROOT)
 		return (EINVAL);
 
 	if (vap->va_flags != VNOVAL) {
 		if (vp->v_mount->mnt_flag & MNT_RDONLY)
 			return (EROFS);
 		if (cred->cr_uid != pmp->pm_uid) {
 			error = priv_check_cred(cred, PRIV_VFS_ADMIN);
 			if (error)
 				return (error);
 		}
 		/*
 		 * We are very inconsistent about handling unsupported
 		 * attributes.  We ignored the access time and the
 		 * read and execute bits.  We were strict for the other
 		 * attributes.
 		 */
 		if (vap->va_flags & ~(UF_ARCHIVE | UF_HIDDEN | UF_READONLY |
 		    UF_SYSTEM))
 			return EOPNOTSUPP;
 		if (vap->va_flags & UF_ARCHIVE)
 			dep->de_Attributes |= ATTR_ARCHIVE;
 		else
 			dep->de_Attributes &= ~ATTR_ARCHIVE;
 		if (vap->va_flags & UF_HIDDEN)
 			dep->de_Attributes |= ATTR_HIDDEN;
 		else
 			dep->de_Attributes &= ~ATTR_HIDDEN;
 		/* We don't allow changing the readonly bit on directories. */
 		if (vp->v_type != VDIR) {
 			if (vap->va_flags & UF_READONLY)
 				dep->de_Attributes |= ATTR_READONLY;
 			else
 				dep->de_Attributes &= ~ATTR_READONLY;
 		}
 		if (vap->va_flags & UF_SYSTEM)
 			dep->de_Attributes |= ATTR_SYSTEM;
 		else
 			dep->de_Attributes &= ~ATTR_SYSTEM;
 		dep->de_flag |= DE_MODIFIED;
 	}
 
 	if (vap->va_uid != (uid_t)VNOVAL || vap->va_gid != (gid_t)VNOVAL) {
 		uid_t uid;
 		gid_t gid;
 
 		if (vp->v_mount->mnt_flag & MNT_RDONLY)
 			return (EROFS);
 		uid = vap->va_uid;
 		if (uid == (uid_t)VNOVAL)
 			uid = pmp->pm_uid;
 		gid = vap->va_gid;
 		if (gid == (gid_t)VNOVAL)
 			gid = pmp->pm_gid;
 		if (cred->cr_uid != pmp->pm_uid || uid != pmp->pm_uid ||
 		    (gid != pmp->pm_gid && !groupmember(gid, cred))) {
 			error = priv_check_cred(cred, PRIV_VFS_CHOWN);
 			if (error)
 				return (error);
 		}
 		if (uid != pmp->pm_uid || gid != pmp->pm_gid)
 			return EINVAL;
 	}
 
 	if (vap->va_size != VNOVAL) {
 		switch (vp->v_type) {
 		case VDIR:
 			return (EISDIR);
 		case VREG:
 			/*
 			 * Truncation is only supported for regular files,
 			 * Disallow it if the filesystem is read-only.
 			 */
 			if (vp->v_mount->mnt_flag & MNT_RDONLY)
 				return (EROFS);
 			break;
 		default:
 			/*
 			 * According to POSIX, the result is unspecified
 			 * for file types other than regular files,
 			 * directories and shared memory objects.  We
 			 * don't support any file types except regular
 			 * files and directories in this file system, so
 			 * this (default) case is unreachable and can do
 			 * anything.  Keep falling through to detrunc()
 			 * for now.
 			 */
 			break;
 		}
 		error = detrunc(dep, vap->va_size, 0, cred);
 		if (error)
 			return error;
 	}
 	if (vap->va_atime.tv_sec != VNOVAL || vap->va_mtime.tv_sec != VNOVAL) {
 		if (vp->v_mount->mnt_flag & MNT_RDONLY)
 			return (EROFS);
 		error = vn_utimes_perm(vp, vap, cred, td);
 		if (error != 0)
 			return (error);
 		if ((pmp->pm_flags & MSDOSFSMNT_NOWIN95) == 0 &&
 		    vap->va_atime.tv_sec != VNOVAL) {
 			dep->de_flag &= ~DE_ACCESS;
 			timespec2fattime(&vap->va_atime, 0,
 			    &dep->de_ADate, NULL, NULL);
 		}
 		if (vap->va_mtime.tv_sec != VNOVAL) {
 			dep->de_flag &= ~DE_UPDATE;
 			timespec2fattime(&vap->va_mtime, 0,
 			    &dep->de_MDate, &dep->de_MTime, NULL);
 		}
 		/*
 		 * We don't set the archive bit when modifying the time of
 		 * a directory to emulate the Windows/DOS behavior.
 		 */
 		if (vp->v_type != VDIR)
 			dep->de_Attributes |= ATTR_ARCHIVE;
 		dep->de_flag |= DE_MODIFIED;
 	}
 	/*
 	 * DOS files only have the ability to have their writability
 	 * attribute set, so we use the owner write bit to set the readonly
 	 * attribute.
 	 */
 	if (vap->va_mode != (mode_t)VNOVAL) {
 		if (vp->v_mount->mnt_flag & MNT_RDONLY)
 			return (EROFS);
 		if (cred->cr_uid != pmp->pm_uid) {
 			error = priv_check_cred(cred, PRIV_VFS_ADMIN);
 			if (error)
 				return (error);
 		}
 		if (vp->v_type != VDIR) {
 			/* We ignore the read and execute bits. */
 			if (vap->va_mode & S_IWUSR)
 				dep->de_Attributes &= ~ATTR_READONLY;
 			else
 				dep->de_Attributes |= ATTR_READONLY;
 			dep->de_Attributes |= ATTR_ARCHIVE;
 			dep->de_flag |= DE_MODIFIED;
 		}
 	}
 	return (deupdat(dep, 0));
 }
 
 static int
 msdosfs_read(struct vop_read_args *ap)
 {
 	int error = 0;
 	int blsize;
 	int isadir;
 	ssize_t orig_resid;
 	u_int n;
 	u_long diff;
 	u_long on;
 	daddr_t lbn;
 	daddr_t rablock;
 	int rasize;
 	int seqcount;
 	struct buf *bp;
 	struct vnode *vp = ap->a_vp;
 	struct denode *dep = VTODE(vp);
 	struct msdosfsmount *pmp = dep->de_pmp;
 	struct uio *uio = ap->a_uio;
 
 	/*
 	 * If they didn't ask for any data, then we are done.
 	 */
 	orig_resid = uio->uio_resid;
 	if (orig_resid == 0)
 		return (0);
 
 	/*
 	 * The caller is supposed to ensure that
 	 * uio->uio_offset >= 0 and uio->uio_resid >= 0.
 	 * We don't need to check for large offsets as in ffs because
 	 * dep->de_FileSize <= MSDOSFS_FILESIZE_MAX < OFF_MAX, so large
 	 * offsets cannot cause overflow even in theory.
 	 */
 
 	seqcount = ap->a_ioflag >> IO_SEQSHIFT;
 
 	isadir = dep->de_Attributes & ATTR_DIRECTORY;
 	do {
 		if (uio->uio_offset >= dep->de_FileSize)
 			break;
 		lbn = de_cluster(pmp, uio->uio_offset);
 		rablock = lbn + 1;
 		blsize = pmp->pm_bpcluster;
 		on = uio->uio_offset & pmp->pm_crbomask;
 		/*
 		 * If we are operating on a directory file then be sure to
 		 * do i/o with the vnode for the filesystem instead of the
 		 * vnode for the directory.
 		 */
 		if (isadir) {
 			/* convert cluster # to block # */
 			error = pcbmap(dep, lbn, &lbn, 0, &blsize);
 			if (error == E2BIG) {
 				error = EINVAL;
 				break;
 			} else if (error)
 				break;
 			error = bread(pmp->pm_devvp, lbn, blsize, NOCRED, &bp);
 		} else if (de_cn2off(pmp, rablock) >= dep->de_FileSize) {
 			error = bread(vp, lbn, blsize, NOCRED, &bp);
 		} else if ((vp->v_mount->mnt_flag & MNT_NOCLUSTERR) == 0) {
 			error = cluster_read(vp, dep->de_FileSize, lbn, blsize,
 			    NOCRED, on + uio->uio_resid, seqcount, 0, &bp);
 		} else if (seqcount > 1) {
 			rasize = blsize;
 			error = breadn(vp, lbn,
 			    blsize, &rablock, &rasize, 1, NOCRED, &bp);
 		} else {
 			error = bread(vp, lbn, blsize, NOCRED, &bp);
 		}
 		if (error) {
 			brelse(bp);
 			break;
 		}
 		diff = pmp->pm_bpcluster - on;
 		n = diff > uio->uio_resid ? uio->uio_resid : diff;
 		diff = dep->de_FileSize - uio->uio_offset;
 		if (diff < n)
 			n = diff;
 		diff = blsize - bp->b_resid;
 		if (diff < n)
 			n = diff;
 		error = vn_io_fault_uiomove(bp->b_data + on, (int) n, uio);
 		brelse(bp);
 	} while (error == 0 && uio->uio_resid > 0 && n != 0);
 	if (!isadir && (error == 0 || uio->uio_resid != orig_resid) &&
 	    (vp->v_mount->mnt_flag & (MNT_NOATIME | MNT_RDONLY)) == 0)
 		dep->de_flag |= DE_ACCESS;
 	return (error);
 }
 
 /*
  * Write data to a file or directory.
  */
 static int
 msdosfs_write(struct vop_write_args *ap)
 {
 	int n;
 	int croffset;
 	ssize_t resid;
 	u_long osize;
 	int error = 0;
 	u_long count;
 	int seqcount;
 	daddr_t bn, lastcn;
 	struct buf *bp;
 	int ioflag = ap->a_ioflag;
 	struct uio *uio = ap->a_uio;
 	struct vnode *vp = ap->a_vp;
 	struct vnode *thisvp;
 	struct denode *dep = VTODE(vp);
 	struct msdosfsmount *pmp = dep->de_pmp;
 	struct ucred *cred = ap->a_cred;
 
 #ifdef MSDOSFS_DEBUG
 	printf("msdosfs_write(vp %p, uio %p, ioflag %x, cred %p\n",
 	    vp, uio, ioflag, cred);
 	printf("msdosfs_write(): diroff %lu, dirclust %lu, startcluster %lu\n",
 	    dep->de_diroffset, dep->de_dirclust, dep->de_StartCluster);
 #endif
 
 	switch (vp->v_type) {
 	case VREG:
 		if (ioflag & IO_APPEND)
 			uio->uio_offset = dep->de_FileSize;
 		thisvp = vp;
 		break;
 	case VDIR:
 		return EISDIR;
 	default:
 		panic("msdosfs_write(): bad file type");
 	}
 
 	/*
 	 * This is needed (unlike in ffs_write()) because we extend the
 	 * file outside of the loop but we don't want to extend the file
 	 * for writes of 0 bytes.
 	 */
 	if (uio->uio_resid == 0)
 		return (0);
 
 	/*
 	 * The caller is supposed to ensure that
 	 * uio->uio_offset >= 0 and uio->uio_resid >= 0.
 	 */
 	if ((uoff_t)uio->uio_offset + uio->uio_resid > MSDOSFS_FILESIZE_MAX)
 		return (EFBIG);
 
 	/*
 	 * If they've exceeded their filesize limit, tell them about it.
 	 */
 	if (vn_rlimit_fsize(vp, uio, uio->uio_td))
 		return (EFBIG);
 
 	/*
 	 * If the offset we are starting the write at is beyond the end of
 	 * the file, then they've done a seek.  Unix filesystems allow
 	 * files with holes in them, DOS doesn't so we must fill the hole
 	 * with zeroed blocks.
 	 */
 	if (uio->uio_offset > dep->de_FileSize) {
 		error = deextend(dep, uio->uio_offset, cred);
 		if (error)
 			return (error);
 	}
 
 	/*
 	 * Remember some values in case the write fails.
 	 */
 	resid = uio->uio_resid;
 	osize = dep->de_FileSize;
 
 	/*
 	 * If we write beyond the end of the file, extend it to its ultimate
 	 * size ahead of the time to hopefully get a contiguous area.
 	 */
 	if (uio->uio_offset + resid > osize) {
 		count = de_clcount(pmp, uio->uio_offset + resid) -
 			de_clcount(pmp, osize);
 		error = extendfile(dep, count, NULL, NULL, 0);
 		if (error &&  (error != ENOSPC || (ioflag & IO_UNIT)))
 			goto errexit;
 		lastcn = dep->de_fc[FC_LASTFC].fc_frcn;
 	} else
 		lastcn = de_clcount(pmp, osize) - 1;
 
 	seqcount = ioflag >> IO_SEQSHIFT;
 	do {
 		if (de_cluster(pmp, uio->uio_offset) > lastcn) {
 			error = ENOSPC;
 			break;
 		}
 
 		croffset = uio->uio_offset & pmp->pm_crbomask;
 		n = min(uio->uio_resid, pmp->pm_bpcluster - croffset);
 		if (uio->uio_offset + n > dep->de_FileSize) {
 			dep->de_FileSize = uio->uio_offset + n;
 			/* The object size needs to be set before buffer is allocated */
 			vnode_pager_setsize(vp, dep->de_FileSize);
 		}
 
 		bn = de_cluster(pmp, uio->uio_offset);
 		if ((uio->uio_offset & pmp->pm_crbomask) == 0
 		    && (de_cluster(pmp, uio->uio_offset + uio->uio_resid)
 			> de_cluster(pmp, uio->uio_offset)
 			|| uio->uio_offset + uio->uio_resid >= dep->de_FileSize)) {
 			/*
 			 * If either the whole cluster gets written,
 			 * or we write the cluster from its start beyond EOF,
 			 * then no need to read data from disk.
 			 */
 			bp = getblk(thisvp, bn, pmp->pm_bpcluster, 0, 0, 0);
 			/*
 			 * This call to vfs_bio_clrbuf() ensures that
 			 * even if vn_io_fault_uiomove() below faults,
 			 * garbage from the newly instantiated buffer
 			 * is not exposed to the userspace via mmap().
 			 */
 			vfs_bio_clrbuf(bp);
 			/*
 			 * Do the bmap now, since pcbmap needs buffers
 			 * for the FAT table. (see msdosfs_strategy)
 			 */
 			if (bp->b_blkno == bp->b_lblkno) {
 				error = pcbmap(dep, bp->b_lblkno, &bn, 0, 0);
 				if (error)
 					bp->b_blkno = -1;
 				else
 					bp->b_blkno = bn;
 			}
 			if (bp->b_blkno == -1) {
 				brelse(bp);
 				if (!error)
 					error = EIO;		/* XXX */
 				break;
 			}
 		} else {
 			/*
 			 * The block we need to write into exists, so read it in.
 			 */
 			error = bread(thisvp, bn, pmp->pm_bpcluster, cred, &bp);
 			if (error) {
 				break;
 			}
 		}
 
 		/*
 		 * Should these vnode_pager_* functions be done on dir
 		 * files?
 		 */
 
 		/*
 		 * Copy the data from user space into the buf header.
 		 */
 		error = vn_io_fault_uiomove(bp->b_data + croffset, n, uio);
 		if (error) {
 			brelse(bp);
 			break;
 		}
 
 		/* Prepare for clustered writes in some else clauses. */
 		if ((vp->v_mount->mnt_flag & MNT_NOCLUSTERW) == 0)
 			bp->b_flags |= B_CLUSTEROK;
 
 		/*
 		 * If IO_SYNC, then each buffer is written synchronously.
 		 * Otherwise, if we have a severe page deficiency then
 		 * write the buffer asynchronously.  Otherwise, if on a
 		 * cluster boundary then write the buffer asynchronously,
 		 * combining it with contiguous clusters if permitted and
 		 * possible, since we don't expect more writes into this
 		 * buffer soon.  Otherwise, do a delayed write because we
 		 * expect more writes into this buffer soon.
 		 */
 		if (ioflag & IO_SYNC)
 			(void)bwrite(bp);
 		else if (vm_page_count_severe() || buf_dirty_count_severe())
 			bawrite(bp);
 		else if (n + croffset == pmp->pm_bpcluster) {
 			if ((vp->v_mount->mnt_flag & MNT_NOCLUSTERW) == 0)
 				cluster_write(vp, bp, dep->de_FileSize,
 				    seqcount, 0);
 			else
 				bawrite(bp);
 		} else
 			bdwrite(bp);
 		dep->de_flag |= DE_UPDATE;
 	} while (error == 0 && uio->uio_resid > 0);
 
 	/*
 	 * If the write failed and they want us to, truncate the file back
 	 * to the size it was before the write was attempted.
 	 */
 errexit:
 	if (error) {
 		if (ioflag & IO_UNIT) {
 			detrunc(dep, osize, ioflag & IO_SYNC, NOCRED);
 			uio->uio_offset -= resid - uio->uio_resid;
 			uio->uio_resid = resid;
 		} else {
 			detrunc(dep, dep->de_FileSize, ioflag & IO_SYNC, NOCRED);
 			if (uio->uio_resid != resid)
 				error = 0;
 		}
 	} else if (ioflag & IO_SYNC)
 		error = deupdat(dep, 1);
 	return (error);
 }
 
 /*
  * Flush the blocks of a file to disk.
  */
 static int
 msdosfs_fsync(struct vop_fsync_args *ap)
 {
 	struct vnode *devvp;
 	int allerror, error;
 
 	vop_stdfsync(ap);
 
 	/*
 	* If the syncing request comes from fsync(2), sync the entire
 	* FAT and any other metadata that happens to be on devvp.  We
 	* need this mainly for the FAT.  We write the FAT sloppily, and
 	* syncing it all now is the best we can easily do to get all
 	* directory entries associated with the file (not just the file)
 	* fully synced.  The other metadata includes critical metadata
 	* for all directory entries, but only in the MNT_ASYNC case.  We
 	* will soon sync all metadata in the file's directory entry.
 	* Non-critical metadata for associated directory entries only
 	* gets synced accidentally, as in most file systems.
 	*/
 	if (ap->a_waitfor != MNT_NOWAIT) {
 		devvp = VTODE(ap->a_vp)->de_pmp->pm_devvp;
 		vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY);
 		allerror = VOP_FSYNC(devvp, MNT_WAIT, ap->a_td);
 		VOP_UNLOCK(devvp);
 	} else
 		allerror = 0;
 
 	error = deupdat(VTODE(ap->a_vp), ap->a_waitfor != MNT_NOWAIT);
 	if (allerror == 0)
 		allerror = error;
 	return (allerror);
 }
 
 static int
 msdosfs_remove(struct vop_remove_args *ap)
 {
 	struct denode *dep = VTODE(ap->a_vp);
 	struct denode *ddep = VTODE(ap->a_dvp);
 	int error;
 
 	if (ap->a_vp->v_type == VDIR)
 		error = EPERM;
 	else
 		error = removede(ddep, dep);
 #ifdef MSDOSFS_DEBUG
 	printf("msdosfs_remove(), dep %p, v_usecount %d\n", dep, ap->a_vp->v_usecount);
 #endif
 	return (error);
 }
 
 /*
  * DOS filesystems don't know what links are.
  */
 static int
 msdosfs_link(struct vop_link_args *ap)
 {
 	return (EOPNOTSUPP);
 }
 
 /*
  * Renames on files require moving the denode to a new hash queue since the
  * denode's location is used to compute which hash queue to put the file
  * in. Unless it is a rename in place.  For example "mv a b".
  *
  * What follows is the basic algorithm:
  *
  * if (file move) {
  *	if (dest file exists) {
  *		remove dest file
  *	}
  *	if (dest and src in same directory) {
  *		rewrite name in existing directory slot
  *	} else {
  *		write new entry in dest directory
  *		update offset and dirclust in denode
  *		move denode to new hash chain
  *		clear old directory entry
  *	}
  * } else {
  *	directory move
  *	if (dest directory exists) {
  *		if (dest is not empty) {
  *			return ENOTEMPTY
  *		}
  *		remove dest directory
  *	}
  *	if (dest and src in same directory) {
  *		rewrite name in existing entry
  *	} else {
  *		be sure dest is not a child of src directory
  *		write entry in dest directory
  *		update "." and ".." in moved directory
  *		clear old directory entry for moved directory
  *	}
  * }
  *
  * On entry:
  *	source's parent directory is unlocked
  *	source file or directory is unlocked
  *	destination's parent directory is locked
  *	destination file or directory is locked if it exists
  *
  * On exit:
  *	all denodes should be released
  */
 static int
 msdosfs_rename(struct vop_rename_args *ap)
 {
 	struct vnode *fdvp, *fvp, *tdvp, *tvp, *vp;
 	struct componentname *fcnp, *tcnp;
 	struct denode *fdip, *fip, *tdip, *tip, *nip;
 	u_char toname[12], oldname[11];
 	u_long to_diroffset;
 	bool checkpath_locked, doingdirectory, newparent;
 	int error;
 	u_long cn, pcl, blkoff;
 	daddr_t bn, wait_scn, scn;
 	struct msdosfsmount *pmp;
 	struct direntry *dotdotp;
 	struct buf *bp;
 
 	tdvp = ap->a_tdvp;
 	fvp = ap->a_fvp;
 	fdvp = ap->a_fdvp;
 	tvp = ap->a_tvp;
 	tcnp = ap->a_tcnp;
 	fcnp = ap->a_fcnp;
 	pmp = VFSTOMSDOSFS(fdvp->v_mount);
 
 #ifdef DIAGNOSTIC
 	if ((tcnp->cn_flags & HASBUF) == 0 ||
 	    (fcnp->cn_flags & HASBUF) == 0)
 		panic("msdosfs_rename: no name");
 #endif
 	/*
 	 * Check for cross-device rename.
 	 */
 	if (fvp->v_mount != tdvp->v_mount ||
 	    (tvp != NULL && fvp->v_mount != tvp->v_mount)) {
 		error = EXDEV;
 		goto abortit;
 	}
 
 	/*
 	 * If source and dest are the same, do nothing.
 	 */
 	if (tvp == fvp) {
 		error = 0;
 		goto abortit;
 	}
 
 	/*
 	 * When the target exists, both the directory
 	 * and target vnodes are passed locked.
 	 */
 	VOP_UNLOCK(tdvp);
 	if (tvp != NULL && tvp != tdvp)
 		VOP_UNLOCK(tvp);
 
 	checkpath_locked = false;
 
 relock:
 	doingdirectory = newparent = false;
 
 	error = vn_lock(fdvp, LK_EXCLUSIVE);
 	if (error != 0)
 		goto releout;
 	if (vn_lock(tdvp, LK_EXCLUSIVE | LK_NOWAIT) != 0) {
 		VOP_UNLOCK(fdvp);
 		error = vn_lock(tdvp, LK_EXCLUSIVE);
 		if (error != 0)
 			goto releout;
 		VOP_UNLOCK(tdvp);
 		goto relock;
 	}
 
 	error = msdosfs_lookup_ino(fdvp, NULL, fcnp, &scn, &blkoff);
 	if (error != 0) {
 		VOP_UNLOCK(fdvp);
 		VOP_UNLOCK(tdvp);
 		goto releout;
 	}
 	error = deget(pmp, scn, blkoff, LK_EXCLUSIVE | LK_NOWAIT, &nip);
 	if (error != 0) {
 		VOP_UNLOCK(fdvp);
 		VOP_UNLOCK(tdvp);
 		if (error != EBUSY)
 			goto releout;
 		error = deget(pmp, scn, blkoff, LK_EXCLUSIVE, &nip);
 		if (error != 0)
 			goto releout;
 		vp = fvp;
 		fvp = DETOV(nip);
 		VOP_UNLOCK(fvp);
 		vrele(vp);
 		goto relock;
 	}
 	vrele(fvp);
 	fvp = DETOV(nip);
 
 	error = msdosfs_lookup_ino(tdvp, NULL, tcnp, &scn, &blkoff);
 	if (error != 0 && error != EJUSTRETURN) {
 		VOP_UNLOCK(fdvp);
 		VOP_UNLOCK(tdvp);
 		VOP_UNLOCK(fvp);
 		goto releout;
 	}
 	if (error == EJUSTRETURN && tvp != NULL) {
 		vrele(tvp);
 		tvp = NULL;
 	}
 	if (error == 0) {
 		nip = NULL;
 		error = deget(pmp, scn, blkoff, LK_EXCLUSIVE | LK_NOWAIT,
 		    &nip);
 		if (tvp != NULL) {
 			vrele(tvp);
 			tvp = NULL;
 		}
 		if (error != 0) {
 			VOP_UNLOCK(fdvp);
 			VOP_UNLOCK(tdvp);
 			VOP_UNLOCK(fvp);
 			if (error != EBUSY)
 				goto releout;
 			error = deget(pmp, scn, blkoff, LK_EXCLUSIVE,
 			    &nip);
 			if (error != 0)
 				goto releout;
 			vput(DETOV(nip));
 			goto relock;
 		}
 		tvp = DETOV(nip);
 	}
 
 	fdip = VTODE(fdvp);
 	fip = VTODE(fvp);
 	tdip = VTODE(tdvp);
 	tip = tvp != NULL ? VTODE(tvp) : NULL;
 
 	/*
 	 * Remember direntry place to use for destination
 	 */
 	to_diroffset = tdip->de_fndoffset;
 
 	/*
 	 * Be sure we are not renaming ".", "..", or an alias of ".". This
 	 * leads to a crippled directory tree.  It's pretty tough to do a
 	 * "ls" or "pwd" with the "." directory entry missing, and "cd .."
 	 * doesn't work if the ".." entry is missing.
 	 */
 	if ((fip->de_Attributes & ATTR_DIRECTORY) != 0) {
 		/*
 		 * Avoid ".", "..", and aliases of "." for obvious reasons.
 		 */
 		if ((fcnp->cn_namelen == 1 && fcnp->cn_nameptr[0] == '.') ||
 		    fdip == fip ||
 		    (fcnp->cn_flags & ISDOTDOT) != 0 ||
 		    (tcnp->cn_flags & ISDOTDOT) != 0) {
 			error = EINVAL;
 			goto unlock;
 		}
 		doingdirectory = true;
 	}
 
 	/*
 	 * If ".." must be changed (ie the directory gets a new
 	 * parent) then the source directory must not be in the
 	 * directory hierarchy above the target, as this would
 	 * orphan everything below the source directory. Also
 	 * the user must have write permission in the source so
 	 * as to be able to change "..". We must repeat the call
 	 * to namei, as the parent directory is unlocked by the
 	 * call to doscheckpath().
 	 */
 	error = VOP_ACCESS(fvp, VWRITE, tcnp->cn_cred, tcnp->cn_thread);
 	if (fdip->de_StartCluster != tdip->de_StartCluster)
 		newparent = true;
 	if (doingdirectory && newparent) {
 		if (error != 0)	/* write access check above */
 			goto unlock;
 		lockmgr(&pmp->pm_checkpath_lock, LK_EXCLUSIVE, NULL);
 		checkpath_locked = true;
 		error = doscheckpath(fip, tdip, &wait_scn);
 		if (wait_scn != 0) {
 			lockmgr(&pmp->pm_checkpath_lock, LK_RELEASE, NULL);
 			checkpath_locked = false;
 			VOP_UNLOCK(fdvp);
 			VOP_UNLOCK(tdvp);
 			VOP_UNLOCK(fvp);
 			if (tvp != NULL && tvp != tdvp)
 				VOP_UNLOCK(tvp);
 			error = deget(pmp, wait_scn, 0, LK_EXCLUSIVE,
 			    &nip);
 			if (error == 0) {
 				vput(DETOV(nip));
 				goto relock;
 			}
 		}
 		if (error != 0)
 			goto unlock;
 		if ((tcnp->cn_flags & SAVESTART) == 0)
 			panic("msdosfs_rename: lost to startdir");
 	}
 
 	if (tip != NULL) {
 		/*
 		 * Target must be empty if a directory and have no links
 		 * to it. Also, ensure source and target are compatible
 		 * (both directories, or both not directories).
 		 */
 		if ((tip->de_Attributes & ATTR_DIRECTORY) != 0) {
 			if (!dosdirempty(tip)) {
 				error = ENOTEMPTY;
 				goto unlock;
 			}
 			if (!doingdirectory) {
 				error = ENOTDIR;
 				goto unlock;
 			}
 			cache_purge(tdvp);
 		} else if (doingdirectory) {
 			error = EISDIR;
 			goto unlock;
 		}
 		error = msdosfs_lookup_ino(tdvp, NULL, tcnp, &scn, &blkoff);
 		MPASS(error == 0);
 		error = removede(tdip, tip);
 		if (error != 0)
 			goto unlock;
 		vput(tvp);
 		tvp = NULL;
 		tip = NULL;
 	}
 
 	/*
 	 * Convert the filename in tcnp into a dos filename. We copy this
 	 * into the denode and directory entry for the destination
 	 * file/directory.
 	 */
 	error = uniqdosname(tdip, tcnp, toname);
 	if (error != 0)
 		goto unlock;
 
 	/*
 	 * First write a new entry in the destination
 	 * directory and mark the entry in the source directory
 	 * as deleted.  Then move the denode to the correct hash
 	 * chain for its new location in the filesystem.  And, if
 	 * we moved a directory, then update its .. entry to point
 	 * to the new parent directory.
 	 */
 	memcpy(oldname, fip->de_Name, 11);
 	memcpy(fip->de_Name, toname, 11);	/* update denode */
 	error = msdosfs_lookup_ino(tdvp, NULL, tcnp, &scn, &blkoff);
 	MPASS(error == EJUSTRETURN);
 	error = createde(fip, tdip, NULL, tcnp);
 	if (error != 0) {
 		memcpy(fip->de_Name, oldname, 11);
 		goto unlock;
 	}
 
 	/*
 	 * If fip is for a directory, then its name should always
 	 * be "." since it is for the directory entry in the
 	 * directory itself (msdosfs_lookup() always translates
 	 * to the "." entry so as to get a unique denode, except
 	 * for the root directory there are different
 	 * complications).  However, we just corrupted its name
 	 * to pass the correct name to createde().  Undo this.
 	 */
 	if ((fip->de_Attributes & ATTR_DIRECTORY) != 0)
 		memcpy(fip->de_Name, oldname, 11);
 	fip->de_refcnt++;
 	error = msdosfs_lookup_ino(fdvp, NULL, fcnp, &scn, &blkoff);
 	MPASS(error == 0);
 	error = removede(fdip, fip);
 	if (error != 0) {
 		/* XXX should downgrade to ro here, fs is corrupt */
 		goto unlock;
 	}
 	if (!doingdirectory) {
 		error = pcbmap(tdip, de_cluster(pmp, to_diroffset), 0,
 		    &fip->de_dirclust, 0);
 		if (error != 0) {
 			/*
 			 * XXX should downgrade to ro here,
 			 * fs is corrupt
 			 */
 			goto unlock;
 		}
 		if (fip->de_dirclust == MSDOSFSROOT)
 			fip->de_diroffset = to_diroffset;
 		else
 			fip->de_diroffset = to_diroffset & pmp->pm_crbomask;
 	}
 	reinsert(fip);
 
 	/*
 	 * If we moved a directory to a new parent directory, then we must
 	 * fixup the ".." entry in the moved directory.
 	 */
 	if (doingdirectory && newparent) {
 		cn = fip->de_StartCluster;
 		if (cn == MSDOSFSROOT) {
 			/* this should never happen */
 			panic("msdosfs_rename(): updating .. in root directory?");
 		} else
 			bn = cntobn(pmp, cn);
 		error = bread(pmp->pm_devvp, bn, pmp->pm_bpcluster,
 		    NOCRED, &bp);
 		if (error != 0) {
 			/* XXX should downgrade to ro here, fs is corrupt */
 			goto unlock;
 		}
 		dotdotp = (struct direntry *)bp->b_data + 1;
 		pcl = tdip->de_StartCluster;
 		if (FAT32(pmp) && pcl == pmp->pm_rootdirblk)
 			pcl = MSDOSFSROOT;
 		putushort(dotdotp->deStartCluster, pcl);
 		if (FAT32(pmp))
 			putushort(dotdotp->deHighClust, pcl >> 16);
 		if (DOINGASYNC(fvp))
 			bdwrite(bp);
 		else if ((error = bwrite(bp)) != 0) {
 			/* XXX should downgrade to ro here, fs is corrupt */
 			goto unlock;
 		}
 	}
 
 	/*
 	 * The msdosfs lookup is case insensitive. Several aliases may
 	 * be inserted for a single directory entry. As a consequnce,
 	 * name cache purge done by lookup for fvp when DELETE op for
 	 * namei is specified, might be not enough to expunge all
 	 * namecache entries that were installed for this direntry.
 	 */
 	cache_purge(fvp);
 
 unlock:
 	if (checkpath_locked)
 		lockmgr(&pmp->pm_checkpath_lock, LK_RELEASE, NULL);
 	vput(fdvp);
 	vput(fvp);
 	if (tvp != NULL) {
 		if (tvp != tdvp)
 			vput(tvp);
 		else
 			vrele(tvp);
 	}
 	vput(tdvp);
 	return (error);
 releout:
 	MPASS(!checkpath_locked);
 	vrele(tdvp);
 	if (tvp != NULL)
 		vrele(tvp);
 	vrele(fdvp);
 	vrele(fvp);
 	return (error);
 abortit:
 	if (tdvp == tvp)
 		vrele(tdvp);
 	else
 		vput(tdvp);
 	if (tvp != NULL)
 		vput(tvp);
 	vrele(fdvp);
 	vrele(fvp);
 	return (error);
 }
 
 static struct {
 	struct direntry dot;
 	struct direntry dotdot;
 } dosdirtemplate = {
 	{	".          ",				/* the . entry */
 		ATTR_DIRECTORY,				/* file attribute */
 		0,					/* reserved */
 		0, { 0, 0 }, { 0, 0 },			/* create time & date */
 		{ 0, 0 },				/* access date */
 		{ 0, 0 },				/* high bits of start cluster */
 		{ 210, 4 }, { 210, 4 },			/* modify time & date */
 		{ 0, 0 },				/* startcluster */
 		{ 0, 0, 0, 0 }				/* filesize */
 	},
 	{	"..         ",				/* the .. entry */
 		ATTR_DIRECTORY,				/* file attribute */
 		0,					/* reserved */
 		0, { 0, 0 }, { 0, 0 },			/* create time & date */
 		{ 0, 0 },				/* access date */
 		{ 0, 0 },				/* high bits of start cluster */
 		{ 210, 4 }, { 210, 4 },			/* modify time & date */
 		{ 0, 0 },				/* startcluster */
 		{ 0, 0, 0, 0 }				/* filesize */
 	}
 };
 
 static int
 msdosfs_mkdir(struct vop_mkdir_args *ap)
 {
 	struct componentname *cnp = ap->a_cnp;
 	struct denode *dep;
 	struct denode *pdep = VTODE(ap->a_dvp);
 	struct direntry *denp;
 	struct msdosfsmount *pmp = pdep->de_pmp;
 	struct buf *bp;
 	u_long newcluster, pcl;
 	int bn;
 	int error;
 	struct denode ndirent;
 	struct timespec ts;
 
 	/*
 	 * If this is the root directory and there is no space left we
 	 * can't do anything.  This is because the root directory can not
 	 * change size.
 	 */
 	if (pdep->de_StartCluster == MSDOSFSROOT
 	    && pdep->de_fndoffset >= pdep->de_FileSize) {
 		error = ENOSPC;
 		goto bad2;
 	}
 
 	/*
 	 * Allocate a cluster to hold the about to be created directory.
 	 */
 	error = clusteralloc(pmp, 0, 1, CLUST_EOFE, &newcluster, NULL);
 	if (error)
 		goto bad2;
 
 	memset(&ndirent, 0, sizeof(ndirent));
 	ndirent.de_pmp = pmp;
 	ndirent.de_flag = DE_ACCESS | DE_CREATE | DE_UPDATE;
 	vfs_timestamp(&ts);
 	DETIMES(&ndirent, &ts, &ts, &ts);
 
 	/*
 	 * Now fill the cluster with the "." and ".." entries. And write
 	 * the cluster to disk.  This way it is there for the parent
 	 * directory to be pointing at if there were a crash.
 	 */
 	bn = cntobn(pmp, newcluster);
 	/* always succeeds */
 	bp = getblk(pmp->pm_devvp, bn, pmp->pm_bpcluster, 0, 0, 0);
 	memset(bp->b_data, 0, pmp->pm_bpcluster);
 	memcpy(bp->b_data, &dosdirtemplate, sizeof dosdirtemplate);
 	denp = (struct direntry *)bp->b_data;
 	putushort(denp[0].deStartCluster, newcluster);
 	putushort(denp[0].deCDate, ndirent.de_CDate);
 	putushort(denp[0].deCTime, ndirent.de_CTime);
 	denp[0].deCHundredth = ndirent.de_CHun;
 	putushort(denp[0].deADate, ndirent.de_ADate);
 	putushort(denp[0].deMDate, ndirent.de_MDate);
 	putushort(denp[0].deMTime, ndirent.de_MTime);
 	pcl = pdep->de_StartCluster;
 	/*
 	 * Although the root directory has a non-magic starting cluster
 	 * number for FAT32, chkdsk and fsck_msdosfs still require
 	 * references to it in dotdot entries to be magic.
 	 */
 	if (FAT32(pmp) && pcl == pmp->pm_rootdirblk)
 		pcl = MSDOSFSROOT;
 	putushort(denp[1].deStartCluster, pcl);
 	putushort(denp[1].deCDate, ndirent.de_CDate);
 	putushort(denp[1].deCTime, ndirent.de_CTime);
 	denp[1].deCHundredth = ndirent.de_CHun;
 	putushort(denp[1].deADate, ndirent.de_ADate);
 	putushort(denp[1].deMDate, ndirent.de_MDate);
 	putushort(denp[1].deMTime, ndirent.de_MTime);
 	if (FAT32(pmp)) {
 		putushort(denp[0].deHighClust, newcluster >> 16);
 		putushort(denp[1].deHighClust, pcl >> 16);
 	}
 
 	if (DOINGASYNC(ap->a_dvp))
 		bdwrite(bp);
 	else if ((error = bwrite(bp)) != 0)
 		goto bad;
 
 	/*
 	 * Now build up a directory entry pointing to the newly allocated
 	 * cluster.  This will be written to an empty slot in the parent
 	 * directory.
 	 */
 #ifdef DIAGNOSTIC
 	if ((cnp->cn_flags & HASBUF) == 0)
 		panic("msdosfs_mkdir: no name");
 #endif
 	error = uniqdosname(pdep, cnp, ndirent.de_Name);
 	if (error)
 		goto bad;
 
 	ndirent.de_Attributes = ATTR_DIRECTORY;
 	ndirent.de_LowerCase = 0;
 	ndirent.de_StartCluster = newcluster;
 	ndirent.de_FileSize = 0;
 	error = createde(&ndirent, pdep, &dep, cnp);
 	if (error)
 		goto bad;
 	*ap->a_vpp = DETOV(dep);
 	return (0);
 
 bad:
-	clusterfree(pmp, newcluster, NULL);
+	clusterfree(pmp, newcluster);
 bad2:
 	return (error);
 }
 
 static int
 msdosfs_rmdir(struct vop_rmdir_args *ap)
 {
 	struct vnode *vp = ap->a_vp;
 	struct vnode *dvp = ap->a_dvp;
 	struct componentname *cnp = ap->a_cnp;
 	struct denode *ip, *dp;
 	int error;
 
 	ip = VTODE(vp);
 	dp = VTODE(dvp);
 
 	/*
 	 * Verify the directory is empty (and valid).
 	 * (Rmdir ".." won't be valid since
 	 *  ".." will contain a reference to
 	 *  the current directory and thus be
 	 *  non-empty.)
 	 */
 	error = 0;
 	if (!dosdirempty(ip)) {
 		error = ENOTEMPTY;
 		goto out;
 	}
 	/*
 	 * Delete the entry from the directory.  For dos filesystems this
 	 * gets rid of the directory entry on disk, the in memory copy
 	 * still exists but the de_refcnt is <= 0.  This prevents it from
 	 * being found by deget().  When the vput() on dep is done we give
 	 * up access and eventually msdosfs_reclaim() will be called which
 	 * will remove it from the denode cache.
 	 */
 	error = removede(dp, ip);
 	if (error)
 		goto out;
 	/*
 	 * This is where we decrement the link count in the parent
 	 * directory.  Since dos filesystems don't do this we just purge
 	 * the name cache.
 	 */
 	cache_purge(dvp);
 	/*
 	 * Truncate the directory that is being deleted.
 	 */
 	error = detrunc(ip, (u_long)0, IO_SYNC, cnp->cn_cred);
 	cache_purge(vp);
 
 out:
 	return (error);
 }
 
 /*
  * DOS filesystems don't know what symlinks are.
  */
 static int
 msdosfs_symlink(struct vop_symlink_args *ap)
 {
 	return (EOPNOTSUPP);
 }
 
 static int
 msdosfs_readdir(struct vop_readdir_args *ap)
 {
 	struct mbnambuf nb;
 	int error = 0;
 	int diff;
 	long n;
 	int blsize;
 	long on;
 	u_long cn;
 	u_long dirsperblk;
 	long bias = 0;
 	daddr_t bn, lbn;
 	struct buf *bp;
 	struct denode *dep = VTODE(ap->a_vp);
 	struct msdosfsmount *pmp = dep->de_pmp;
 	struct direntry *dentp;
 	struct dirent dirbuf;
 	struct uio *uio = ap->a_uio;
 	u_long *cookies = NULL;
 	int ncookies = 0;
 	off_t offset, off;
 	int chksum = -1;
 
 #ifdef MSDOSFS_DEBUG
 	printf("msdosfs_readdir(): vp %p, uio %p, cred %p, eofflagp %p\n",
 	    ap->a_vp, uio, ap->a_cred, ap->a_eofflag);
 #endif
 
 	/*
 	 * msdosfs_readdir() won't operate properly on regular files since
 	 * it does i/o only with the filesystem vnode, and hence can
 	 * retrieve the wrong block from the buffer cache for a plain file.
 	 * So, fail attempts to readdir() on a plain file.
 	 */
 	if ((dep->de_Attributes & ATTR_DIRECTORY) == 0)
 		return (ENOTDIR);
 
 	/*
 	 * To be safe, initialize dirbuf
 	 */
 	memset(dirbuf.d_name, 0, sizeof(dirbuf.d_name));
 
 	/*
 	 * If the user buffer is smaller than the size of one dos directory
 	 * entry or the file offset is not a multiple of the size of a
 	 * directory entry, then we fail the read.
 	 */
 	off = offset = uio->uio_offset;
 	if (uio->uio_resid < sizeof(struct direntry) ||
 	    (offset & (sizeof(struct direntry) - 1)))
 		return (EINVAL);
 
 	if (ap->a_ncookies) {
 		ncookies = uio->uio_resid / 16;
 		cookies = malloc(ncookies * sizeof(u_long), M_TEMP,
 		       M_WAITOK);
 		*ap->a_cookies = cookies;
 		*ap->a_ncookies = ncookies;
 	}
 
 	dirsperblk = pmp->pm_BytesPerSec / sizeof(struct direntry);
 
 	/*
 	 * If they are reading from the root directory then, we simulate
 	 * the . and .. entries since these don't exist in the root
 	 * directory.  We also set the offset bias to make up for having to
 	 * simulate these entries. By this I mean that at file offset 64 we
 	 * read the first entry in the root directory that lives on disk.
 	 */
 	if (dep->de_StartCluster == MSDOSFSROOT
 	    || (FAT32(pmp) && dep->de_StartCluster == pmp->pm_rootdirblk)) {
 #if 0
 		printf("msdosfs_readdir(): going after . or .. in root dir, offset %d\n",
 		    offset);
 #endif
 		bias = 2 * sizeof(struct direntry);
 		if (offset < bias) {
 			for (n = (int)offset / sizeof(struct direntry);
 			     n < 2; n++) {
 				dirbuf.d_fileno = FAT32(pmp) ?
 				    (uint64_t)cntobn(pmp, pmp->pm_rootdirblk) *
 				    dirsperblk : 1;
 				dirbuf.d_type = DT_DIR;
 				switch (n) {
 				case 0:
 					dirbuf.d_namlen = 1;
 					dirbuf.d_name[0] = '.';
 					break;
 				case 1:
 					dirbuf.d_namlen = 2;
 					dirbuf.d_name[0] = '.';
 					dirbuf.d_name[1] = '.';
 					break;
 				}
 				dirbuf.d_reclen = GENERIC_DIRSIZ(&dirbuf);
 				/* NOTE: d_off is the offset of the *next* entry. */
 				dirbuf.d_off = offset + sizeof(struct direntry);
 				dirent_terminate(&dirbuf);
 				if (uio->uio_resid < dirbuf.d_reclen)
 					goto out;
 				error = uiomove(&dirbuf, dirbuf.d_reclen, uio);
 				if (error)
 					goto out;
 				offset += sizeof(struct direntry);
 				off = offset;
 				if (cookies) {
 					*cookies++ = offset;
 					if (--ncookies <= 0)
 						goto out;
 				}
 			}
 		}
 	}
 
 	mbnambuf_init(&nb);
 	off = offset;
 	while (uio->uio_resid > 0) {
 		lbn = de_cluster(pmp, offset - bias);
 		on = (offset - bias) & pmp->pm_crbomask;
 		n = min(pmp->pm_bpcluster - on, uio->uio_resid);
 		diff = dep->de_FileSize - (offset - bias);
 		if (diff <= 0)
 			break;
 		n = min(n, diff);
 		error = pcbmap(dep, lbn, &bn, &cn, &blsize);
 		if (error)
 			break;
 		error = bread(pmp->pm_devvp, bn, blsize, NOCRED, &bp);
 		if (error) {
 			return (error);
 		}
 		n = min(n, blsize - bp->b_resid);
 		if (n == 0) {
 			brelse(bp);
 			return (EIO);
 		}
 
 		/*
 		 * Convert from dos directory entries to fs-independent
 		 * directory entries.
 		 */
 		for (dentp = (struct direntry *)(bp->b_data + on);
 		     (char *)dentp < bp->b_data + on + n;
 		     dentp++, offset += sizeof(struct direntry)) {
 #if 0
 			printf("rd: dentp %08x prev %08x crnt %08x deName %02x attr %02x\n",
 			    dentp, prev, crnt, dentp->deName[0], dentp->deAttributes);
 #endif
 			/*
 			 * If this is an unused entry, we can stop.
 			 */
 			if (dentp->deName[0] == SLOT_EMPTY) {
 				brelse(bp);
 				goto out;
 			}
 			/*
 			 * Skip deleted entries.
 			 */
 			if (dentp->deName[0] == SLOT_DELETED) {
 				chksum = -1;
 				mbnambuf_init(&nb);
 				continue;
 			}
 
 			/*
 			 * Handle Win95 long directory entries
 			 */
 			if (dentp->deAttributes == ATTR_WIN95) {
 				if (pmp->pm_flags & MSDOSFSMNT_SHORTNAME)
 					continue;
 				chksum = win2unixfn(&nb,
 				    (struct winentry *)dentp, chksum, pmp);
 				continue;
 			}
 
 			/*
 			 * Skip volume labels
 			 */
 			if (dentp->deAttributes & ATTR_VOLUME) {
 				chksum = -1;
 				mbnambuf_init(&nb);
 				continue;
 			}
 			/*
 			 * This computation of d_fileno must match
 			 * the computation of va_fileid in
 			 * msdosfs_getattr.
 			 */
 			if (dentp->deAttributes & ATTR_DIRECTORY) {
 				cn = getushort(dentp->deStartCluster);
 				if (FAT32(pmp)) {
 					cn |= getushort(dentp->deHighClust) <<
 					    16;
 					if (cn == MSDOSFSROOT)
 						cn = pmp->pm_rootdirblk;
 				}
 				if (cn == MSDOSFSROOT && !FAT32(pmp))
 					dirbuf.d_fileno = 1;
 				else
 					dirbuf.d_fileno = cntobn(pmp, cn) *
 					    dirsperblk;
 				dirbuf.d_type = DT_DIR;
 			} else {
 				dirbuf.d_fileno = (uoff_t)offset /
 				    sizeof(struct direntry);
 				dirbuf.d_type = DT_REG;
 			}
 
 			if (chksum != winChksum(dentp->deName)) {
 				dirbuf.d_namlen = dos2unixfn(dentp->deName,
 				    (u_char *)dirbuf.d_name,
 				    dentp->deLowerCase |
 					((pmp->pm_flags & MSDOSFSMNT_SHORTNAME) ?
 					(LCASE_BASE | LCASE_EXT) : 0),
 				    pmp);
 				mbnambuf_init(&nb);
 			} else
 				mbnambuf_flush(&nb, &dirbuf);
 			chksum = -1;
 			dirbuf.d_reclen = GENERIC_DIRSIZ(&dirbuf);
 			/* NOTE: d_off is the offset of the *next* entry. */
 			dirbuf.d_off = offset + sizeof(struct direntry);
 			dirent_terminate(&dirbuf);
 			if (uio->uio_resid < dirbuf.d_reclen) {
 				brelse(bp);
 				goto out;
 			}
 			error = uiomove(&dirbuf, dirbuf.d_reclen, uio);
 			if (error) {
 				brelse(bp);
 				goto out;
 			}
 			if (cookies) {
 				*cookies++ = offset + sizeof(struct direntry);
 				if (--ncookies <= 0) {
 					brelse(bp);
 					goto out;
 				}
 			}
 			off = offset + sizeof(struct direntry);
 		}
 		brelse(bp);
 	}
 out:
 	/* Subtract unused cookies */
 	if (ap->a_ncookies)
 		*ap->a_ncookies -= ncookies;
 
 	uio->uio_offset = off;
 
 	/*
 	 * Set the eofflag (NFS uses it)
 	 */
 	if (ap->a_eofflag) {
 		if (dep->de_FileSize - (offset - bias) <= 0)
 			*ap->a_eofflag = 1;
 		else
 			*ap->a_eofflag = 0;
 	}
 	return (error);
 }
 
 /*-
  * a_vp   - pointer to the file's vnode
  * a_bn   - logical block number within the file (cluster number for us)
  * a_bop  - where to return the bufobj of the special file containing the fs
  * a_bnp  - where to return the "physical" block number corresponding to a_bn
  *          (relative to the special file; units are blocks of size DEV_BSIZE)
  * a_runp - where to return the "run past" a_bn.  This is the count of logical
  *          blocks whose physical blocks (together with a_bn's physical block)
  *          are contiguous.
  * a_runb - where to return the "run before" a_bn.
  */
 static int
 msdosfs_bmap(struct vop_bmap_args *ap)
 {
 	struct fatcache savefc;
 	struct denode *dep;
 	struct mount *mp;
 	struct msdosfsmount *pmp;
 	struct vnode *vp;
 	daddr_t runbn;
 	u_long cn;
 	int bnpercn, error, maxio, maxrun, run;
 
 	vp = ap->a_vp;
 	dep = VTODE(vp);
 	pmp = dep->de_pmp;
 	if (ap->a_bop != NULL)
 		*ap->a_bop = &pmp->pm_devvp->v_bufobj;
 	if (ap->a_bnp == NULL)
 		return (0);
 	if (ap->a_runp != NULL)
 		*ap->a_runp = 0;
 	if (ap->a_runb != NULL)
 		*ap->a_runb = 0;
 	cn = ap->a_bn;
 	if (cn != ap->a_bn)
 		return (EFBIG);
 	error = pcbmap(dep, cn, ap->a_bnp, NULL, NULL);
 	if (error != 0 || (ap->a_runp == NULL && ap->a_runb == NULL))
 		return (error);
 
 	/*
 	 * Prepare to back out updates of the fatchain cache after the one
 	 * for the first block done by pcbmap() above.  Without the backout,
 	 * then whenever the caller doesn't do i/o to all of the blocks that
 	 * we find, the single useful cache entry would be too far in advance
 	 * of the actual i/o to work for the next sequential i/o.  Then the
 	 * FAT would be searched from the beginning.  With the backout, the
 	 * FAT is searched starting at most a few blocks early.  This wastes
 	 * much less time.  Time is also wasted finding more blocks than the
 	 * caller will do i/o to.  This is necessary because the runlength
 	 * parameters are output-only.
 	 */
 	savefc = dep->de_fc[FC_LASTMAP];
 
 	mp = vp->v_mount;
 	maxio = mp->mnt_iosize_max / mp->mnt_stat.f_iosize;
 	bnpercn = de_cn2bn(pmp, 1);
 	if (ap->a_runp != NULL) {
 		maxrun = ulmin(maxio - 1, pmp->pm_maxcluster - cn);
 		for (run = 1; run <= maxrun; run++) {
 			if (pcbmap(dep, cn + run, &runbn, NULL, NULL) != 0 ||
 			    runbn != *ap->a_bnp + run * bnpercn)
 				break;
 		}
 		*ap->a_runp = run - 1;
 	}
 	if (ap->a_runb != NULL) {
 		maxrun = ulmin(maxio - 1, cn);
 		for (run = 1; run < maxrun; run++) {
 			if (pcbmap(dep, cn - run, &runbn, NULL, NULL) != 0 ||
 			    runbn != *ap->a_bnp - run * bnpercn)
 				break;
 		}
 		*ap->a_runb = run - 1;
 	}
 	dep->de_fc[FC_LASTMAP] = savefc;
 	return (0);
 }
 
 SYSCTL_NODE(_vfs, OID_AUTO, msdosfs, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
     "msdos filesystem");
 static int use_buf_pager = 1;
 SYSCTL_INT(_vfs_msdosfs, OID_AUTO, use_buf_pager, CTLFLAG_RWTUN,
     &use_buf_pager, 0,
     "Use buffer pager instead of bmap");
 
 static daddr_t
 msdosfs_gbp_getblkno(struct vnode *vp, vm_ooffset_t off)
 {
 
 	return (de_cluster(VTODE(vp)->de_pmp, off));
 }
 
 static int
 msdosfs_gbp_getblksz(struct vnode *vp, daddr_t lbn, long *sz)
 {
 
 	*sz = VTODE(vp)->de_pmp->pm_bpcluster;
 	return (0);
 }
 
 static int
 msdosfs_getpages(struct vop_getpages_args *ap)
 {
 
 	if (use_buf_pager)
 		return (vfs_bio_getpages(ap->a_vp, ap->a_m, ap->a_count,
 		    ap->a_rbehind, ap->a_rahead, msdosfs_gbp_getblkno,
 		    msdosfs_gbp_getblksz));
 	return (vnode_pager_generic_getpages(ap->a_vp, ap->a_m, ap->a_count,
 	    ap->a_rbehind, ap->a_rahead, NULL, NULL));
 }
 
 static int
 msdosfs_strategy(struct vop_strategy_args *ap)
 {
 	struct buf *bp = ap->a_bp;
 	struct denode *dep = VTODE(ap->a_vp);
 	struct bufobj *bo;
 	int error = 0;
 	daddr_t blkno;
 
 	/*
 	 * If we don't already know the filesystem relative block number
 	 * then get it using pcbmap().  If pcbmap() returns the block
 	 * number as -1 then we've got a hole in the file.  DOS filesystems
 	 * don't allow files with holes, so we shouldn't ever see this.
 	 */
 	if (bp->b_blkno == bp->b_lblkno) {
 		error = pcbmap(dep, bp->b_lblkno, &blkno, 0, 0);
 		bp->b_blkno = blkno;
 		if (error) {
 			bp->b_error = error;
 			bp->b_ioflags |= BIO_ERROR;
 			bufdone(bp);
 			return (0);
 		}
 		if ((long)bp->b_blkno == -1)
 			vfs_bio_clrbuf(bp);
 	}
 	if (bp->b_blkno == -1) {
 		bufdone(bp);
 		return (0);
 	}
 	/*
 	 * Read/write the block from/to the disk that contains the desired
 	 * file block.
 	 */
 	bp->b_iooffset = dbtob(bp->b_blkno);
 	bo = dep->de_pmp->pm_bo;
 	BO_STRATEGY(bo, bp);
 	return (0);
 }
 
 static int
 msdosfs_print(struct vop_print_args *ap)
 {
 	struct denode *dep = VTODE(ap->a_vp);
 
 	printf("\tstartcluster %lu, dircluster %lu, diroffset %lu, ",
 	       dep->de_StartCluster, dep->de_dirclust, dep->de_diroffset);
 	printf("on dev %s\n", devtoname(dep->de_pmp->pm_dev));
 	return (0);
 }
 
 static int
 msdosfs_pathconf(struct vop_pathconf_args *ap)
 {
 	struct msdosfsmount *pmp = VTODE(ap->a_vp)->de_pmp;
 
 	switch (ap->a_name) {
 	case _PC_FILESIZEBITS:
 		*ap->a_retval = 32;
 		return (0);
 	case _PC_LINK_MAX:
 		*ap->a_retval = 1;
 		return (0);
 	case _PC_NAME_MAX:
 		*ap->a_retval = pmp->pm_flags & MSDOSFSMNT_LONGNAME ? WIN_MAXLEN : 12;
 		return (0);
 	case _PC_CHOWN_RESTRICTED:
 		*ap->a_retval = 1;
 		return (0);
 	case _PC_NO_TRUNC:
 		*ap->a_retval = 0;
 		return (0);
 	default:
 		return (vop_stdpathconf(ap));
 	}
 	/* NOTREACHED */
 }
 
 static int
 msdosfs_vptofh(struct vop_vptofh_args *ap)
 {
 	struct denode *dep;
 	struct defid *defhp;
 
 	dep = VTODE(ap->a_vp);
 	defhp = (struct defid *)ap->a_fhp;
 	defhp->defid_len = sizeof(struct defid);
 	defhp->defid_dirclust = dep->de_dirclust;
 	defhp->defid_dirofs = dep->de_diroffset;
 	/* defhp->defid_gen = dep->de_gen; */
 	return (0);
 }
 
 /* Global vfs data structures for msdosfs */
 struct vop_vector msdosfs_vnodeops = {
 	.vop_default =		&default_vnodeops,
 
 	.vop_access =		msdosfs_access,
 	.vop_bmap =		msdosfs_bmap,
 	.vop_getpages =		msdosfs_getpages,
 	.vop_cachedlookup =	msdosfs_lookup,
 	.vop_open =		msdosfs_open,
 	.vop_close =		msdosfs_close,
 	.vop_create =		msdosfs_create,
 	.vop_fsync =		msdosfs_fsync,
 	.vop_fdatasync =	vop_stdfdatasync_buf,
 	.vop_getattr =		msdosfs_getattr,
 	.vop_inactive =		msdosfs_inactive,
 	.vop_link =		msdosfs_link,
 	.vop_lookup =		vfs_cache_lookup,
 	.vop_mkdir =		msdosfs_mkdir,
 	.vop_mknod =		msdosfs_mknod,
 	.vop_pathconf =		msdosfs_pathconf,
 	.vop_print =		msdosfs_print,
 	.vop_read =		msdosfs_read,
 	.vop_readdir =		msdosfs_readdir,
 	.vop_reclaim =		msdosfs_reclaim,
 	.vop_remove =		msdosfs_remove,
 	.vop_rename =		msdosfs_rename,
 	.vop_rmdir =		msdosfs_rmdir,
 	.vop_setattr =		msdosfs_setattr,
 	.vop_strategy =		msdosfs_strategy,
 	.vop_symlink =		msdosfs_symlink,
 	.vop_write =		msdosfs_write,
 	.vop_vptofh =		msdosfs_vptofh,
 };
 VFS_VOP_VECTOR_REGISTER(msdosfs_vnodeops);
diff --git a/usr.sbin/makefs/msdos/msdosfs_fat.c b/usr.sbin/makefs/msdos/msdosfs_fat.c
index eacc448b09de..63410b8b7d1f 100644
--- a/usr.sbin/makefs/msdos/msdosfs_fat.c
+++ b/usr.sbin/makefs/msdos/msdosfs_fat.c
@@ -1,1057 +1,1054 @@
 /* $FreeBSD$ */
 /*	$NetBSD: msdosfs_fat.c,v 1.28 1997/11/17 15:36:49 ws Exp $	*/
 
 /*-
  * SPDX-License-Identifier: BSD-4-Clause
  *
  * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank.
  * Copyright (C) 1994, 1995, 1997 TooLs GmbH.
  * All rights reserved.
  * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below).
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  * are met:
  * 1. Redistributions of source code must retain the above copyright
  *    notice, this list of conditions and the following disclaimer.
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
  * 3. All advertising materials mentioning features or use of this software
  *    must display the following acknowledgement:
  *	This product includes software developed by TooLs GmbH.
  * 4. The name of TooLs GmbH may not be used to endorse or promote products
  *    derived from this software without specific prior written permission.
  *
  * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 /*-
  * Written by Paul Popelka (paulp@uts.amdahl.com)
  *
  * You can do anything you want with this software, just don't say you wrote
  * it, and don't remove this notice.
  *
  * This software is provided "as is".
  *
  * The author supplies this software to be publicly redistributed on the
  * understanding that the author is not responsible for the correct
  * functioning of this software in any circumstances and is not liable for
  * any damages caused by this software.
  *
  * October 1992
  */
 
 #include <sys/param.h>
 #include <sys/errno.h>
 
 #include <assert.h>
 #include <stdbool.h>
 #include <stdio.h>
 #include <string.h>
 #include <strings.h>
 
 #include "ffs/buf.h"
 
 #include <fs/msdosfs/bpb.h>
 #include "msdos/direntry.h"
 #include <fs/msdosfs/denode.h>
 #include <fs/msdosfs/fat.h>
 #include <fs/msdosfs/msdosfsmount.h>
 
 #include "makefs.h"
 #include "msdos.h"
 
 #define	FULL_RUN	((u_int)0xffffffff)
 #define	SYNCHRONOUS_WRITES(pmp)	1
 
 static int	chainalloc(struct msdosfsmount *pmp, u_long start,
 		    u_long count, u_long fillwith, u_long *retcluster,
 		    u_long *got);
 static int	chainlength(struct msdosfsmount *pmp, u_long start,
 		    u_long count);
 static void	fatblock(struct msdosfsmount *pmp, u_long ofs, u_long *bnp,
 		    u_long *sizep, u_long *bop);
 static int	fatchain(struct msdosfsmount *pmp, u_long start, u_long count,
 		    u_long fillwith);
 static void	fc_lookup(struct denode *dep, u_long findcn, u_long *frcnp,
 		    u_long *fsrcnp);
 static void	updatefats(struct msdosfsmount *pmp, struct buf *bp,
 		    u_long fatbn);
 static __inline void
 		usemap_alloc(struct msdosfsmount *pmp, u_long cn);
 static __inline void
 		usemap_free(struct msdosfsmount *pmp, u_long cn);
 static int	clusteralloc1(struct msdosfsmount *pmp, u_long start,
 		    u_long count, u_long fillwith, u_long *retcluster,
 		    u_long *got);
 
 static void
 fatblock(struct msdosfsmount *pmp, u_long ofs, u_long *bnp, u_long *sizep,
     u_long *bop)
 {
 	u_long bn, size;
 
 	bn = ofs / pmp->pm_fatblocksize * pmp->pm_fatblocksec;
 	size = MIN(pmp->pm_fatblocksec, pmp->pm_FATsecs - bn)
 	    * DEV_BSIZE;
 	bn += pmp->pm_fatblk + pmp->pm_curfat * pmp->pm_FATsecs;
 
 	if (bnp)
 		*bnp = bn;
 	if (sizep)
 		*sizep = size;
 	if (bop)
 		*bop = ofs % pmp->pm_fatblocksize;
 }
 
 /*
  * Map the logical cluster number of a file into a physical disk sector
  * that is filesystem relative.
  *
  * dep	  - address of denode representing the file of interest
  * findcn - file relative cluster whose filesystem relative cluster number
  *	    and/or block number are/is to be found
  * bnp	  - address of where to place the filesystem relative block number.
  *	    If this pointer is null then don't return this quantity.
  * cnp	  - address of where to place the filesystem relative cluster number.
  *	    If this pointer is null then don't return this quantity.
  * sp     - pointer to returned block size
  *
  * NOTE: Either bnp or cnp must be non-null.
  * This function has one side effect.  If the requested file relative cluster
  * is beyond the end of file, then the actual number of clusters in the file
  * is returned in *cnp.  This is useful for determining how long a directory is.
  *  If cnp is null, nothing is returned.
  */
 int
 pcbmap(struct denode *dep, u_long findcn, daddr_t *bnp, u_long *cnp, int *sp)
 {
 	int error;
 	u_long i;
 	u_long cn;
 	u_long prevcn = 0; /* XXX: prevcn could be used unititialized */
 	u_long byteoffset;
 	u_long bn;
 	u_long bo;
 	struct buf *bp = NULL;
 	u_long bp_bn = -1;
 	struct msdosfsmount *pmp = dep->de_pmp;
 	u_long bsize;
 
 	assert(bnp != NULL || cnp != NULL || sp != NULL);
 
 	cn = dep->de_StartCluster;
 	/*
 	 * The "file" that makes up the root directory is contiguous,
 	 * permanently allocated, of fixed size, and is not made up of
 	 * clusters.  If the cluster number is beyond the end of the root
 	 * directory, then return the number of clusters in the file.
 	 */
 	if (cn == MSDOSFSROOT) {
 		if (dep->de_Attributes & ATTR_DIRECTORY) {
 			if (de_cn2off(pmp, findcn) >= dep->de_FileSize) {
 				if (cnp)
 					*cnp = de_bn2cn(pmp, pmp->pm_rootdirsize);
 				return (E2BIG);
 			}
 			if (bnp)
 				*bnp = pmp->pm_rootdirblk + de_cn2bn(pmp, findcn);
 			if (cnp)
 				*cnp = MSDOSFSROOT;
 			if (sp)
 				*sp = MIN(pmp->pm_bpcluster,
 				    dep->de_FileSize - de_cn2off(pmp, findcn));
 			return (0);
 		} else {		/* just an empty file */
 			if (cnp)
 				*cnp = 0;
 			return (E2BIG);
 		}
 	}
 
 	/*
 	 * All other files do I/O in cluster sized blocks
 	 */
 	if (sp)
 		*sp = pmp->pm_bpcluster;
 
 	/*
 	 * Rummage around in the FAT cache, maybe we can avoid tromping
 	 * through every FAT entry for the file. And, keep track of how far
 	 * off the cache was from where we wanted to be.
 	 */
 	i = 0;
 	fc_lookup(dep, findcn, &i, &cn);
 
 	/*
 	 * Handle all other files or directories the normal way.
 	 */
 	for (; i < findcn; i++) {
 		/*
 		 * Stop with all reserved clusters, not just with EOF.
 		 */
 		if ((cn | ~pmp->pm_fatmask) >= CLUST_RSRVD)
 			goto hiteof;
 		byteoffset = FATOFS(pmp, cn);
 		fatblock(pmp, byteoffset, &bn, &bsize, &bo);
 		if (bn != bp_bn) {
 			if (bp)
 				brelse(bp);
 			error = bread(pmp->pm_devvp, bn, bsize, NOCRED, &bp);
 			if (error) {
 				brelse(bp);
 				return (error);
 			}
 			bp_bn = bn;
 		}
 		prevcn = cn;
 		if (bo >= bsize) {
 			if (bp)
 				brelse(bp);
 			return (EIO);
 		}
 		if (FAT32(pmp))
 			cn = getulong(bp->b_data + bo);
 		else
 			cn = getushort(bp->b_data + bo);
 		if (FAT12(pmp) && (prevcn & 1))
 			cn >>= 4;
 		cn &= pmp->pm_fatmask;
 
 		/*
 		 * Force the special cluster numbers
 		 * to be the same for all cluster sizes
 		 * to let the rest of msdosfs handle
 		 * all cases the same.
 		 */
 		if ((cn | ~pmp->pm_fatmask) >= CLUST_RSRVD)
 			cn |= ~pmp->pm_fatmask;
 	}
 
 	if (!MSDOSFSEOF(pmp, cn)) {
 		if (bp)
 			brelse(bp);
 		if (bnp)
 			*bnp = cntobn(pmp, cn);
 		if (cnp)
 			*cnp = cn;
 		fc_setcache(dep, FC_LASTMAP, i, cn);
 		return (0);
 	}
 
 hiteof:;
 	if (cnp)
 		*cnp = i;
 	if (bp)
 		brelse(bp);
 	/* update last file cluster entry in the FAT cache */
 	fc_setcache(dep, FC_LASTFC, i - 1, prevcn);
 	return (E2BIG);
 }
 
 /*
  * Find the closest entry in the FAT cache to the cluster we are looking
  * for.
  */
 static void
 fc_lookup(struct denode *dep, u_long findcn, u_long *frcnp, u_long *fsrcnp)
 {
 	int i;
 	u_long cn;
 	struct fatcache *closest = NULL;
 
 	for (i = 0; i < FC_SIZE; i++) {
 		cn = dep->de_fc[i].fc_frcn;
 		if (cn != FCE_EMPTY && cn <= findcn) {
 			if (closest == NULL || cn > closest->fc_frcn)
 				closest = &dep->de_fc[i];
 		}
 	}
 	if (closest) {
 		*frcnp = closest->fc_frcn;
 		*fsrcnp = closest->fc_fsrcn;
 	}
 }
 
 /*
  * Purge the FAT cache in denode dep of all entries relating to file
  * relative cluster frcn and beyond.
  */
 void
 fc_purge(struct denode *dep, u_int frcn)
 {
 	int i;
 	struct fatcache *fcp;
 
 	fcp = dep->de_fc;
 	for (i = 0; i < FC_SIZE; i++, fcp++) {
 		if (fcp->fc_frcn >= frcn)
 			fcp->fc_frcn = FCE_EMPTY;
 	}
 }
 
 /*
  * Update the FAT.
  * If mirroring the FAT, update all copies, with the first copy as last.
  * Else update only the current FAT (ignoring the others).
  *
  * pmp	 - msdosfsmount structure for filesystem to update
  * bp	 - addr of modified FAT block
  * fatbn - block number relative to begin of filesystem of the modified FAT block.
  */
 static void
 updatefats(struct msdosfsmount *pmp, struct buf *bp, u_long fatbn)
 {
 	struct buf *bpn;
 	int cleanfat, i;
 
 #ifdef MSDOSFS_DEBUG
 	printf("updatefats(pmp %p, bp %p, fatbn %lu)\n", pmp, bp, fatbn);
 #endif
 
 	if (pmp->pm_flags & MSDOSFS_FATMIRROR) {
 		/*
 		 * Now copy the block(s) of the modified FAT to the other copies of
 		 * the FAT and write them out.  This is faster than reading in the
 		 * other FATs and then writing them back out.  This could tie up
 		 * the FAT for quite a while. Preventing others from accessing it.
 		 * To prevent us from going after the FAT quite so much we use
 		 * delayed writes, unless they specified "synchronous" when the
 		 * filesystem was mounted.  If synch is asked for then use
 		 * bwrite()'s and really slow things down.
 		 */
 		if (fatbn != pmp->pm_fatblk || FAT12(pmp))
 			cleanfat = 0;
 		else if (FAT16(pmp))
 			cleanfat = 16;
 		else
 			cleanfat = 32;
 		for (i = 1; i < pmp->pm_FATs; i++) {
 			fatbn += pmp->pm_FATsecs;
 			/* getblk() never fails */
 			bpn = getblk(pmp->pm_devvp, fatbn, bp->b_bcount,
 			    0, 0, 0);
 			memcpy(bpn->b_data, bp->b_data, bp->b_bcount);
 			/* Force the clean bit on in the other copies. */
 			if (cleanfat == 16)
 				((uint8_t *)bpn->b_data)[3] |= 0x80;
 			else if (cleanfat == 32)
 				((uint8_t *)bpn->b_data)[7] |= 0x08;
 			if (SYNCHRONOUS_WRITES(pmp))
 				bwrite(bpn);
 			else
 				bdwrite(bpn);
 		}
 	}
 
 	/*
 	 * Write out the first (or current) FAT last.
 	 */
 	if (SYNCHRONOUS_WRITES(pmp))
 		bwrite(bp);
 	else
 		bdwrite(bp);
 }
 
 /*
  * Updating entries in 12 bit FATs is a pain in the butt.
  *
  * The following picture shows where nibbles go when moving from a 12 bit
  * cluster number into the appropriate bytes in the FAT.
  *
  *	byte m        byte m+1      byte m+2
  *	+----+----+   +----+----+   +----+----+
  *	|  0    1 |   |  2    3 |   |  4    5 |   FAT bytes
  *	+----+----+   +----+----+   +----+----+
  *
  *	+----+----+----+   +----+----+----+
  *	|  3    0    1 |   |  4    5    2 |
  *	+----+----+----+   +----+----+----+
  *	cluster n	   cluster n+1
  *
  * Where n is even. m = n + (n >> 2)
  *
  */
 static __inline void
 usemap_alloc(struct msdosfsmount *pmp, u_long cn)
 {
 
 	assert(cn <= pmp->pm_maxcluster);
 	assert((pmp->pm_flags & MSDOSFSMNT_RONLY) == 0);
 	assert((pmp->pm_inusemap[cn / N_INUSEBITS] & (1 << (cn % N_INUSEBITS)))
 	    == 0);
 	assert(pmp->pm_freeclustercount > 0);
 
 	pmp->pm_inusemap[cn / N_INUSEBITS] |= 1U << (cn % N_INUSEBITS);
 	pmp->pm_freeclustercount--;
 	pmp->pm_flags |= MSDOSFS_FSIMOD;
 }
 
 static __inline void
 usemap_free(struct msdosfsmount *pmp, u_long cn)
 {
 
 	assert(cn <= pmp->pm_maxcluster);
 	assert((pmp->pm_flags & MSDOSFSMNT_RONLY) == 0);
 	assert((pmp->pm_inusemap[cn / N_INUSEBITS] & (1 << (cn % N_INUSEBITS)))
 	    != 0);
 
 	pmp->pm_freeclustercount++;
 	pmp->pm_flags |= MSDOSFS_FSIMOD;
 	pmp->pm_inusemap[cn / N_INUSEBITS] &= ~(1U << (cn % N_INUSEBITS));
 }
 
-int
-clusterfree(struct msdosfsmount *pmp, u_long cluster, u_long *oldcnp)
+void
+clusterfree(struct msdosfsmount *pmp, u_long cluster)
 {
 	int error;
 	u_long oldcn;
 
 	error = fatentry(FAT_GET_AND_SET, pmp, cluster, &oldcn, MSDOSFSFREE);
-	if (error)
-		return (error);
+	if (error != 0)
+		return;
 	/*
 	 * If the cluster was successfully marked free, then update
 	 * the count of free clusters, and turn off the "allocated"
 	 * bit in the "in use" cluster bit map.
 	 */
 	usemap_free(pmp, cluster);
-	if (oldcnp)
-		*oldcnp = oldcn;
-	return (0);
 }
 
 /*
  * Get or Set or 'Get and Set' the cluster'th entry in the FAT.
  *
  * function	- whether to get or set a FAT entry
  * pmp		- address of the msdosfsmount structure for the filesystem
  *		  whose FAT is to be manipulated.
  * cn		- which cluster is of interest
  * oldcontents	- address of a word that is to receive the contents of the
  *		  cluster'th entry if this is a get function
  * newcontents	- the new value to be written into the cluster'th element of
  *		  the FAT if this is a set function.
  *
  * This function can also be used to free a cluster by setting the FAT entry
  * for a cluster to 0.
  *
  * All copies of the FAT are updated if this is a set function. NOTE: If
  * fatentry() marks a cluster as free it does not update the inusemap in
  * the msdosfsmount structure. This is left to the caller.
  */
 int
 fatentry(int function, struct msdosfsmount *pmp, u_long cn, u_long *oldcontents,
     u_long newcontents)
 {
 	int error;
 	u_long readcn;
 	u_long bn, bo, bsize, byteoffset;
 	struct buf *bp;
 
 #ifdef	MSDOSFS_DEBUG
 	printf("fatentry(func %d, pmp %p, clust %lu, oldcon %p, newcon %lx)\n",
 	    function, pmp, cn, oldcontents, newcontents);
 #endif
 
 #ifdef DIAGNOSTIC
 	/*
 	 * Be sure they asked us to do something.
 	 */
 	if ((function & (FAT_SET | FAT_GET)) == 0) {
 #ifdef MSDOSFS_DEBUG
 		printf("fatentry(): function code doesn't specify get or set\n");
 #endif
 		return (EINVAL);
 	}
 
 	/*
 	 * If they asked us to return a cluster number but didn't tell us
 	 * where to put it, give them an error.
 	 */
 	if ((function & FAT_GET) && oldcontents == NULL) {
 #ifdef MSDOSFS_DEBUG
 		printf("fatentry(): get function with no place to put result\n");
 #endif
 		return (EINVAL);
 	}
 #endif
 
 	/*
 	 * Be sure the requested cluster is in the filesystem.
 	 */
 	if (cn < CLUST_FIRST || cn > pmp->pm_maxcluster)
 		return (EINVAL);
 
 	byteoffset = FATOFS(pmp, cn);
 	fatblock(pmp, byteoffset, &bn, &bsize, &bo);
 	error = bread(pmp->pm_devvp, bn, bsize, NOCRED, &bp);
 	if (error) {
 		brelse(bp);
 		return (error);
 	}
 
 	if (function & FAT_GET) {
 		if (FAT32(pmp))
 			readcn = getulong(bp->b_data + bo);
 		else
 			readcn = getushort(bp->b_data + bo);
 		if (FAT12(pmp) & (cn & 1))
 			readcn >>= 4;
 		readcn &= pmp->pm_fatmask;
 		/* map reserved FAT entries to same values for all FATs */
 		if ((readcn | ~pmp->pm_fatmask) >= CLUST_RSRVD)
 			readcn |= ~pmp->pm_fatmask;
 		*oldcontents = readcn;
 	}
 	if (function & FAT_SET) {
 		switch (pmp->pm_fatmask) {
 		case FAT12_MASK:
 			readcn = getushort(bp->b_data + bo);
 			if (cn & 1) {
 				readcn &= 0x000f;
 				readcn |= newcontents << 4;
 			} else {
 				readcn &= 0xf000;
 				readcn |= newcontents & 0xfff;
 			}
 			putushort(bp->b_data + bo, readcn);
 			break;
 		case FAT16_MASK:
 			putushort(bp->b_data + bo, newcontents);
 			break;
 		case FAT32_MASK:
 			/*
 			 * According to spec we have to retain the
 			 * high order bits of the FAT entry.
 			 */
 			readcn = getulong(bp->b_data + bo);
 			readcn &= ~FAT32_MASK;
 			readcn |= newcontents & FAT32_MASK;
 			putulong(bp->b_data + bo, readcn);
 			break;
 		}
 		updatefats(pmp, bp, bn);
 		bp = NULL;
 		pmp->pm_fmod = 1;
 	}
 	if (bp)
 		brelse(bp);
 	return (0);
 }
 
 /*
  * Update a contiguous cluster chain
  *
  * pmp	    - mount point
  * start    - first cluster of chain
  * count    - number of clusters in chain
  * fillwith - what to write into FAT entry of last cluster
  */
 static int
 fatchain(struct msdosfsmount *pmp, u_long start, u_long count, u_long fillwith)
 {
 	int error;
 	u_long bn, bo, bsize, byteoffset, readcn, newc;
 	struct buf *bp;
 
 #ifdef MSDOSFS_DEBUG
 	printf("fatchain(pmp %p, start %lu, count %lu, fillwith %lx)\n",
 	    pmp, start, count, fillwith);
 #endif
 	/*
 	 * Be sure the clusters are in the filesystem.
 	 */
 	if (start < CLUST_FIRST || start + count - 1 > pmp->pm_maxcluster)
 		return (EINVAL);
 
 	while (count > 0) {
 		byteoffset = FATOFS(pmp, start);
 		fatblock(pmp, byteoffset, &bn, &bsize, &bo);
 		error = bread(pmp->pm_devvp, bn, bsize, NOCRED, &bp);
 		if (error) {
 			brelse(bp);
 			return (error);
 		}
 		while (count > 0) {
 			start++;
 			newc = --count > 0 ? start : fillwith;
 			switch (pmp->pm_fatmask) {
 			case FAT12_MASK:
 				readcn = getushort(bp->b_data + bo);
 				if (start & 1) {
 					readcn &= 0xf000;
 					readcn |= newc & 0xfff;
 				} else {
 					readcn &= 0x000f;
 					readcn |= newc << 4;
 				}
 				putushort(bp->b_data + bo, readcn);
 				bo++;
 				if (!(start & 1))
 					bo++;
 				break;
 			case FAT16_MASK:
 				putushort(bp->b_data + bo, newc);
 				bo += 2;
 				break;
 			case FAT32_MASK:
 				readcn = getulong(bp->b_data + bo);
 				readcn &= ~pmp->pm_fatmask;
 				readcn |= newc & pmp->pm_fatmask;
 				putulong(bp->b_data + bo, readcn);
 				bo += 4;
 				break;
 			}
 			if (bo >= bsize)
 				break;
 		}
 		updatefats(pmp, bp, bn);
 	}
 	pmp->pm_fmod = 1;
 	return (0);
 }
 
 /*
  * Check the length of a free cluster chain starting at start.
  *
  * pmp	 - mount point
  * start - start of chain
  * count - maximum interesting length
  */
 static int
 chainlength(struct msdosfsmount *pmp, u_long start, u_long count)
 {
 	u_long idx, max_idx;
 	u_int map;
 	u_long len;
 
 	if (start > pmp->pm_maxcluster)
 		return (0);
 	max_idx = pmp->pm_maxcluster / N_INUSEBITS;
 	idx = start / N_INUSEBITS;
 	start %= N_INUSEBITS;
 	map = pmp->pm_inusemap[idx];
 	map &= ~((1 << start) - 1);
 	if (map) {
 		len = ffs(map) - 1 - start;
 		len = MIN(len, count);
 		if (start + len > pmp->pm_maxcluster)
 			len = pmp->pm_maxcluster - start + 1;
 		return (len);
 	}
 	len = N_INUSEBITS - start;
 	if (len >= count) {
 		len = count;
 		if (start + len > pmp->pm_maxcluster)
 			len = pmp->pm_maxcluster - start + 1;
 		return (len);
 	}
 	while (++idx <= max_idx) {
 		if (len >= count)
 			break;
 		map = pmp->pm_inusemap[idx];
 		if (map) {
 			len += ffs(map) - 1;
 			break;
 		}
 		len += N_INUSEBITS;
 	}
 	len = MIN(len, count);
 	if (start + len > pmp->pm_maxcluster)
 		len = pmp->pm_maxcluster - start + 1;
 	return (len);
 }
 
 /*
  * Allocate contigous free clusters.
  *
  * pmp	      - mount point.
  * start      - start of cluster chain.
  * count      - number of clusters to allocate.
  * fillwith   - put this value into the FAT entry for the
  *		last allocated cluster.
  * retcluster - put the first allocated cluster's number here.
  * got	      - how many clusters were actually allocated.
  */
 static int
 chainalloc(struct msdosfsmount *pmp, u_long start, u_long count,
     u_long fillwith, u_long *retcluster, u_long *got)
 {
 	int error;
 	u_long cl, n;
 
 	assert((pmp->pm_flags & MSDOSFSMNT_RONLY) == 0);
 
 	for (cl = start, n = count; n-- > 0;)
 		usemap_alloc(pmp, cl++);
 	pmp->pm_nxtfree = start + count;
 	if (pmp->pm_nxtfree > pmp->pm_maxcluster)
 		pmp->pm_nxtfree = CLUST_FIRST;
 	pmp->pm_flags |= MSDOSFS_FSIMOD;
 	error = fatchain(pmp, start, count, fillwith);
 	if (error != 0) {
 		for (cl = start, n = count; n-- > 0;)
 			usemap_free(pmp, cl++);
 		return (error);
 	}
 #ifdef MSDOSFS_DEBUG
 	printf("clusteralloc(): allocated cluster chain at %lu (%lu clusters)\n",
 	    start, count);
 #endif
 	if (retcluster)
 		*retcluster = start;
 	if (got)
 		*got = count;
 	return (0);
 }
 
 /*
  * Allocate contiguous free clusters.
  *
  * pmp	      - mount point.
  * start      - preferred start of cluster chain.
  * count      - number of clusters requested.
  * fillwith   - put this value into the FAT entry for the
  *		last allocated cluster.
  * retcluster - put the first allocated cluster's number here.
  * got	      - how many clusters were actually allocated.
  */
 int
 clusteralloc(struct msdosfsmount *pmp, u_long start, u_long count,
     u_long fillwith, u_long *retcluster, u_long *got)
 {
 	int error;
 
 	error = clusteralloc1(pmp, start, count, fillwith, retcluster, got);
 	return (error);
 }
 
 static int
 clusteralloc1(struct msdosfsmount *pmp, u_long start, u_long count,
     u_long fillwith, u_long *retcluster, u_long *got)
 {
 	u_long idx;
 	u_long len, newst, foundl, cn, l;
 	u_long foundcn = 0; /* XXX: foundcn could be used unititialized */
 	u_int map;
 
 	MSDOSFS_DPRINTF(("clusteralloc(): find %lu clusters\n", count));
 
 	if (start) {
 		if ((len = chainlength(pmp, start, count)) >= count)
 			return (chainalloc(pmp, start, count, fillwith, retcluster, got));
 	} else
 		len = 0;
 
 	newst = pmp->pm_nxtfree;
 	foundl = 0;
 
 	for (cn = newst; cn <= pmp->pm_maxcluster;) {
 		idx = cn / N_INUSEBITS;
 		map = pmp->pm_inusemap[idx];
 		map |= (1U << (cn % N_INUSEBITS)) - 1;
 		if (map != FULL_RUN) {
 			cn = idx * N_INUSEBITS + ffs(map ^ FULL_RUN) - 1;
 			if ((l = chainlength(pmp, cn, count)) >= count)
 				return (chainalloc(pmp, cn, count, fillwith, retcluster, got));
 			if (l > foundl) {
 				foundcn = cn;
 				foundl = l;
 			}
 			cn += l + 1;
 			continue;
 		}
 		cn += N_INUSEBITS - cn % N_INUSEBITS;
 	}
 	for (cn = 0; cn < newst;) {
 		idx = cn / N_INUSEBITS;
 		map = pmp->pm_inusemap[idx];
 		map |= (1U << (cn % N_INUSEBITS)) - 1;
 		if (map != FULL_RUN) {
 			cn = idx * N_INUSEBITS + ffs(map ^ FULL_RUN) - 1;
 			if ((l = chainlength(pmp, cn, count)) >= count)
 				return (chainalloc(pmp, cn, count, fillwith, retcluster, got));
 			if (l > foundl) {
 				foundcn = cn;
 				foundl = l;
 			}
 			cn += l + 1;
 			continue;
 		}
 		cn += N_INUSEBITS - cn % N_INUSEBITS;
 	}
 
 	if (!foundl)
 		return (ENOSPC);
 
 	if (len)
 		return (chainalloc(pmp, start, len, fillwith, retcluster, got));
 	else
 		return (chainalloc(pmp, foundcn, foundl, fillwith, retcluster, got));
 }
 
 
 /*
  * Free a chain of clusters.
  *
  * pmp		- address of the msdosfs mount structure for the filesystem
  *		  containing the cluster chain to be freed.
  * startcluster - number of the 1st cluster in the chain of clusters to be
  *		  freed.
  */
 int
 freeclusterchain(struct msdosfsmount *pmp, u_long cluster)
 {
 	int error;
 	struct buf *bp = NULL;
 	u_long bn, bo, bsize, byteoffset;
 	u_long readcn, lbn = -1;
 
 	while (cluster >= CLUST_FIRST && cluster <= pmp->pm_maxcluster) {
 		byteoffset = FATOFS(pmp, cluster);
 		fatblock(pmp, byteoffset, &bn, &bsize, &bo);
 		if (lbn != bn) {
 			if (bp)
 				updatefats(pmp, bp, lbn);
 			error = bread(pmp->pm_devvp, bn, bsize, NOCRED, &bp);
 			if (error) {
 				brelse(bp);
 				return (error);
 			}
 			lbn = bn;
 		}
 		usemap_free(pmp, cluster);
 		switch (pmp->pm_fatmask) {
 		case FAT12_MASK:
 			readcn = getushort(bp->b_data + bo);
 			if (cluster & 1) {
 				cluster = readcn >> 4;
 				readcn &= 0x000f;
 				readcn |= MSDOSFSFREE << 4;
 			} else {
 				cluster = readcn;
 				readcn &= 0xf000;
 				readcn |= MSDOSFSFREE & 0xfff;
 			}
 			putushort(bp->b_data + bo, readcn);
 			break;
 		case FAT16_MASK:
 			cluster = getushort(bp->b_data + bo);
 			putushort(bp->b_data + bo, MSDOSFSFREE);
 			break;
 		case FAT32_MASK:
 			cluster = getulong(bp->b_data + bo);
 			putulong(bp->b_data + bo,
 				 (MSDOSFSFREE & FAT32_MASK) | (cluster & ~FAT32_MASK));
 			break;
 		}
 		cluster &= pmp->pm_fatmask;
 		if ((cluster | ~pmp->pm_fatmask) >= CLUST_RSRVD)
 			cluster |= pmp->pm_fatmask;
 	}
 	if (bp)
 		updatefats(pmp, bp, bn);
 	return (0);
 }
 
 /*
  * Read in FAT blocks looking for free clusters. For every free cluster
  * found turn off its corresponding bit in the pm_inusemap.
  */
 int
 fillinusemap(struct msdosfsmount *pmp)
 {
 	struct buf *bp;
 	u_long bn, bo, bsize, byteoffset, cn, readcn;
 	int error;
 
 	bp = NULL;
 
 	/*
 	 * Mark all clusters in use, we mark the free ones in the FAT scan
 	 * loop further down.
 	 */
 	for (cn = 0; cn < (pmp->pm_maxcluster + N_INUSEBITS) / N_INUSEBITS; cn++)
 		pmp->pm_inusemap[cn] = FULL_RUN;
 
 	/*
 	 * Figure how many free clusters are in the filesystem by ripping
 	 * through the FAT counting the number of entries whose content is
 	 * zero.  These represent free clusters.
 	 */
 	pmp->pm_freeclustercount = 0;
 	for (cn = 0; cn <= pmp->pm_maxcluster; cn++) {
 		byteoffset = FATOFS(pmp, cn);
 		bo = byteoffset % pmp->pm_fatblocksize;
 		if (bo == 0) {
 			/* Read new FAT block */
 			if (bp != NULL)
 				brelse(bp);
 			fatblock(pmp, byteoffset, &bn, &bsize, NULL);
 			error = bread(pmp->pm_devvp, bn, bsize, NOCRED, &bp);
 			if (error != 0)
 				return (error);
 		}
 		if (FAT32(pmp))
 			readcn = getulong(bp->b_data + bo);
 		else
 			readcn = getushort(bp->b_data + bo);
 		if (FAT12(pmp) && (cn & 1))
 			readcn >>= 4;
 		readcn &= pmp->pm_fatmask;
 
 		/*
 		 * Check if the FAT ID matches the BPB's media descriptor and
 		 * all other bits are set to 1.
 		 */
 		if (cn == 0 && readcn != ((pmp->pm_fatmask & 0xffffff00) |
 		    pmp->pm_bpb.bpbMedia)) {
 #ifdef MSDOSFS_DEBUG
 			printf("mountmsdosfs(): Media descriptor in BPB"
 			    "does not match FAT ID\n");
 #endif
 			brelse(bp);
 			return (EINVAL);
 		} else if (readcn == CLUST_FREE)
 			usemap_free(pmp, cn);
 	}
 	if (bp != NULL)
 		brelse(bp);
 
 	for (cn = pmp->pm_maxcluster + 1; cn < (pmp->pm_maxcluster +
 	    N_INUSEBITS) / N_INUSEBITS; cn++)
 		pmp->pm_inusemap[cn / N_INUSEBITS] |= 1U << (cn % N_INUSEBITS);
 
 	return (0);
 }
 
 /*
  * Allocate a new cluster and chain it onto the end of the file.
  *
  * dep	 - the file to extend
  * count - number of clusters to allocate
  * bpp	 - where to return the address of the buf header for the first new
  *	   file block
  * ncp	 - where to put cluster number of the first newly allocated cluster
  *	   If this pointer is 0, do not return the cluster number.
  * flags - see fat.h
  *
  * NOTE: This function is not responsible for turning on the DE_UPDATE bit of
  * the de_flag field of the denode and it does not change the de_FileSize
  * field.  This is left for the caller to do.
  */
 int
 extendfile(struct denode *dep, u_long count, struct buf **bpp, u_long *ncp,
     int flags)
 {
 	int error;
 	u_long frcn;
 	u_long cn, got;
 	struct msdosfsmount *pmp = dep->de_pmp;
 	struct buf *bp;
 
 	/*
 	 * Don't try to extend the root directory
 	 */
 	if (dep->de_StartCluster == MSDOSFSROOT
 	    && (dep->de_Attributes & ATTR_DIRECTORY)) {
 #ifdef MSDOSFS_DEBUG
 		printf("extendfile(): attempt to extend root directory\n");
 #endif
 		return (ENOSPC);
 	}
 
 	/*
 	 * If the "file's last cluster" cache entry is empty, and the file
 	 * is not empty, then fill the cache entry by calling pcbmap().
 	 */
 	if (dep->de_fc[FC_LASTFC].fc_frcn == FCE_EMPTY &&
 	    dep->de_StartCluster != 0) {
 		error = pcbmap(dep, 0xffff, 0, &cn, 0);
 		/* we expect it to return E2BIG */
 		if (error != E2BIG)
 			return (error);
 	}
 
 	dep->de_fc[FC_NEXTTOLASTFC].fc_frcn =
 	    dep->de_fc[FC_LASTFC].fc_frcn;
 	dep->de_fc[FC_NEXTTOLASTFC].fc_fsrcn =
 	    dep->de_fc[FC_LASTFC].fc_fsrcn;
 	while (count > 0) {
 		/*
 		 * Allocate a new cluster chain and cat onto the end of the
 		 * file.  If the file is empty we make de_StartCluster point
 		 * to the new block.  Note that de_StartCluster being 0 is
 		 * sufficient to be sure the file is empty since we exclude
 		 * attempts to extend the root directory above, and the root
 		 * dir is the only file with a startcluster of 0 that has
 		 * blocks allocated (sort of).
 		 */
 		if (dep->de_StartCluster == 0)
 			cn = 0;
 		else
 			cn = dep->de_fc[FC_LASTFC].fc_fsrcn + 1;
 		error = clusteralloc(pmp, cn, count, CLUST_EOFE, &cn, &got);
 		if (error)
 			return (error);
 
 		count -= got;
 
 		/*
 		 * Give them the filesystem relative cluster number if they want
 		 * it.
 		 */
 		if (ncp) {
 			*ncp = cn;
 			ncp = NULL;
 		}
 
 		if (dep->de_StartCluster == 0) {
 			dep->de_StartCluster = cn;
 			frcn = 0;
 		} else {
 			error = fatentry(FAT_SET, pmp,
 					 dep->de_fc[FC_LASTFC].fc_fsrcn,
 					 0, cn);
 			if (error) {
-				clusterfree(pmp, cn, NULL);
+				clusterfree(pmp, cn);
 				return (error);
 			}
 			frcn = dep->de_fc[FC_LASTFC].fc_frcn + 1;
 		}
 
 		/*
 		 * Update the "last cluster of the file" entry in the
 		 * denode's FAT cache.
 		 */
 		fc_setcache(dep, FC_LASTFC, frcn + got - 1, cn + got - 1);
 
 		if ((flags & DE_CLEAR) &&
 		    (dep->de_Attributes & ATTR_DIRECTORY)) {
 			while (got-- > 0) {
 				bp = getblk(pmp->pm_devvp,
 				    cntobn(pmp, cn++),
 				    pmp->pm_bpcluster, 0, 0, 0);
 				clrbuf(bp);
 				if (bpp) {
 					*bpp = bp;
 					bpp = NULL;
 				} else {
 					bdwrite(bp);
 				}
 			}
 		}
 	}
 
 	return (0);
 }
diff --git a/usr.sbin/makefs/msdos/msdosfs_vnops.c b/usr.sbin/makefs/msdos/msdosfs_vnops.c
index 8bd4d3d498d8..6f1275ee125d 100644
--- a/usr.sbin/makefs/msdos/msdosfs_vnops.c
+++ b/usr.sbin/makefs/msdos/msdosfs_vnops.c
@@ -1,644 +1,644 @@
 /*	$NetBSD: msdosfs_vnops.c,v 1.19 2017/04/13 17:10:12 christos Exp $ */
 
 /*-
  * SPDX-License-Identifier: BSD-4-Clause
  *
  * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank.
  * Copyright (C) 1994, 1995, 1997 TooLs GmbH.
  * All rights reserved.
  * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below).
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  * are met:
  * 1. Redistributions of source code must retain the above copyright
  *    notice, this list of conditions and the following disclaimer.
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
  * 3. All advertising materials mentioning features or use of this software
  *    must display the following acknowledgement:
  *	This product includes software developed by TooLs GmbH.
  * 4. The name of TooLs GmbH may not be used to endorse or promote products
  *    derived from this software without specific prior written permission.
  *
  * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 /*-
  * Written by Paul Popelka (paulp@uts.amdahl.com)
  *
  * You can do anything you want with this software, just don't say you wrote
  * it, and don't remove this notice.
  *
  * This software is provided "as is".
  *
  * The author supplies this software to be publicly redistributed on the
  * understanding that the author is not responsible for the correct
  * functioning of this software in any circumstances and is not liable for
  * any damages caused by this software.
  *
  * October 1992
  */
 
 #include <sys/cdefs.h>
 __FBSDID("$FreeBSD$");
 
 #include <sys/param.h>
 #include <sys/errno.h>
 #include <sys/mman.h>
 #include <sys/time.h>
 
 #include <fcntl.h>
 #include <stdbool.h>
 #include <stdio.h>
 #include <string.h>
 #include <time.h>
 #include <unistd.h>
 
 #include "ffs/buf.h"
 #include <fs/msdosfs/bpb.h>
 #include "msdos/direntry.h"
 #include <fs/msdosfs/denode.h>
 #include <fs/msdosfs/fat.h>
 #include <fs/msdosfs/msdosfsmount.h>
 
 #include "makefs.h"
 #include "msdos.h"
 
 /*
  * Some general notes:
  *
  * In the ufs filesystem the inodes, superblocks, and indirect blocks are
  * read/written using the vnode for the filesystem. Blocks that represent
  * the contents of a file are read/written using the vnode for the file
  * (including directories when they are read/written as files). This
  * presents problems for the dos filesystem because data that should be in
  * an inode (if dos had them) resides in the directory itself.	Since we
  * must update directory entries without the benefit of having the vnode
  * for the directory we must use the vnode for the filesystem.	This means
  * that when a directory is actually read/written (via read, write, or
  * readdir, or seek) we must use the vnode for the filesystem instead of
  * the vnode for the directory as would happen in ufs. This is to insure we
  * retrieve the correct block from the buffer cache since the hash value is
  * based upon the vnode address and the desired block number.
  */
 
 static int msdosfs_wfile(const char *, struct denode *, fsnode *);
 static void unix2fattime(const struct timespec *tsp, uint16_t *ddp,
     uint16_t *dtp);
 
 static void
 msdosfs_times(struct denode *dep, const struct stat *st)
 {
 	if (stampst.st_ino)
 		st = &stampst;
 
 #ifdef HAVE_STRUCT_STAT_BIRTHTIME
 	unix2fattime(&st->st_birthtim, &dep->de_CDate, &dep->de_CTime);
 #else
 	unix2fattime(&st->st_ctim, &dep->de_CDate, &dep->de_CTime);
 #endif
 	unix2fattime(&st->st_atim, &dep->de_ADate, NULL);
 	unix2fattime(&st->st_mtim, &dep->de_MDate, &dep->de_MTime);
 }
 
 static void
 unix2fattime(const struct timespec *tsp, uint16_t *ddp, uint16_t *dtp)
 {
 	time_t t1;
 	struct tm lt = {0};
 
 	t1 = tsp->tv_sec;
 	localtime_r(&t1, &lt);
 
 	unsigned long fat_time = ((lt.tm_year - 80) << 25) |
             ((lt.tm_mon + 1) << 21) |
             (lt.tm_mday << 16) |
             (lt.tm_hour << 11) |
             (lt.tm_min << 5) |
             (lt.tm_sec >> 1);
 
 	if (ddp != NULL)
 		*ddp = (uint16_t)(fat_time >> 16);
 	if (dtp != NULL)
 		*dtp = (uint16_t)fat_time;
 }
 
 /*
  * When we search a directory the blocks containing directory entries are
  * read and examined.  The directory entries contain information that would
  * normally be in the inode of a unix filesystem.  This means that some of
  * a directory's contents may also be in memory resident denodes (sort of
  * an inode).  This can cause problems if we are searching while some other
  * process is modifying a directory.  To prevent one process from accessing
  * incompletely modified directory information we depend upon being the
  * sole owner of a directory block.  bread/brelse provide this service.
  * This being the case, when a process modifies a directory it must first
  * acquire the disk block that contains the directory entry to be modified.
  * Then update the disk block and the denode, and then write the disk block
  * out to disk.	 This way disk blocks containing directory entries and in
  * memory denode's will be in synch.
  */
 static int
 msdosfs_findslot(struct denode *dp, struct componentname *cnp)
 {
 	daddr_t bn;
 	int error;
 	int slotcount;
 	int slotoffset = 0;
 	int frcn;
 	u_long cluster;
 	int blkoff;
 	u_int diroff;
 	int blsize;
 	struct msdosfsmount *pmp;
 	struct buf *bp = 0;
 	struct direntry *dep;
 	u_char dosfilename[12];
 	int wincnt = 1;
 	int chksum = -1, chksum_ok;
 	int olddos = 1;
 
 	pmp = dp->de_pmp;
 
 	switch (unix2dosfn((const u_char *)cnp->cn_nameptr, dosfilename,
 	    cnp->cn_namelen, 0)) {
 	case 0:
 		return (EINVAL);
 	case 1:
 		break;
 	case 2:
 		wincnt = winSlotCnt((const u_char *)cnp->cn_nameptr,
 		    cnp->cn_namelen) + 1;
 		break;
 	case 3:
 		olddos = 0;
 		wincnt = winSlotCnt((const u_char *)cnp->cn_nameptr,
 		    cnp->cn_namelen) + 1;
 		break;
 	}
 
 	if (pmp->pm_flags & MSDOSFSMNT_SHORTNAME)
 		wincnt = 1;
 
 	/*
 	 * Suppress search for slots unless creating
 	 * file and at end of pathname, in which case
 	 * we watch for a place to put the new file in
 	 * case it doesn't already exist.
 	 */
 	slotcount = 0;
 	MSDOSFS_DPRINTF(("%s(): dos filename: %s\n", __func__, dosfilename));
 	/*
 	 * Search the directory pointed at by vdp for the name pointed at
 	 * by cnp->cn_nameptr.
 	 */
 	/*
 	 * The outer loop ranges over the clusters that make up the
 	 * directory.  Note that the root directory is different from all
 	 * other directories.  It has a fixed number of blocks that are not
 	 * part of the pool of allocatable clusters.  So, we treat it a
 	 * little differently. The root directory starts at "cluster" 0.
 	 */
 	diroff = 0;
 	for (frcn = 0; diroff < dp->de_FileSize; frcn++) {
 		if ((error = pcbmap(dp, frcn, &bn, &cluster, &blsize)) != 0) {
 			if (error == E2BIG)
 				break;
 			return (error);
 		}
 		error = bread(pmp->pm_devvp, bn, blsize, 0, &bp);
 		if (error) {
 			return (error);
 		}
 		for (blkoff = 0; blkoff < blsize;
 		     blkoff += sizeof(struct direntry),
 		     diroff += sizeof(struct direntry)) {
 			dep = (struct direntry *)(bp->b_data + blkoff);
 			/*
 			 * If the slot is empty and we are still looking
 			 * for an empty then remember this one.	 If the
 			 * slot is not empty then check to see if it
 			 * matches what we are looking for.  If the slot
 			 * has never been filled with anything, then the
 			 * remainder of the directory has never been used,
 			 * so there is no point in searching it.
 			 */
 			if (dep->deName[0] == SLOT_EMPTY ||
 			    dep->deName[0] == SLOT_DELETED) {
 				/*
 				 * Drop memory of previous long matches
 				 */
 				chksum = -1;
 
 				if (slotcount < wincnt) {
 					slotcount++;
 					slotoffset = diroff;
 				}
 				if (dep->deName[0] == SLOT_EMPTY) {
 					brelse(bp);
 					goto notfound;
 				}
 			} else {
 				/*
 				 * If there wasn't enough space for our
 				 * winentries, forget about the empty space
 				 */
 				if (slotcount < wincnt)
 					slotcount = 0;
 
 				/*
 				 * Check for Win95 long filename entry
 				 */
 				if (dep->deAttributes == ATTR_WIN95) {
 					if (pmp->pm_flags & MSDOSFSMNT_SHORTNAME)
 						continue;
 
 					chksum = winChkName(
 					    (const u_char *)cnp->cn_nameptr,
 					    cnp->cn_namelen,
 					    (struct winentry *)dep, chksum);
 					continue;
 				}
 
 				/*
 				 * Ignore volume labels (anywhere, not just
 				 * the root directory).
 				 */
 				if (dep->deAttributes & ATTR_VOLUME) {
 					chksum = -1;
 					continue;
 				}
 
 				/*
 				 * Check for a checksum or name match
 				 */
 				chksum_ok = (chksum == winChksum(dep->deName));
 				if (!chksum_ok
 				    && (!olddos || memcmp(dosfilename, dep->deName, 11))) {
 					chksum = -1;
 					continue;
 				}
 				MSDOSFS_DPRINTF(("%s(): match blkoff %d, diroff %u\n",
 				    __func__, blkoff, diroff));
 				/*
 				 * Remember where this directory
 				 * entry came from for whoever did
 				 * this lookup.
 				 */
 				dp->de_fndoffset = diroff;
 				dp->de_fndcnt = 0;
 
 				return EEXIST;
 			}
 		}	/* for (blkoff = 0; .... */
 		/*
 		 * Release the buffer holding the directory cluster just
 		 * searched.
 		 */
 		brelse(bp);
 	}	/* for (frcn = 0; ; frcn++) */
 
 notfound:
 	/*
 	 * We hold no disk buffers at this point.
 	 */
 
 	/*
 	 * If we get here we didn't find the entry we were looking for. But
 	 * that's ok if we are creating or renaming and are at the end of
 	 * the pathname and the directory hasn't been removed.
 	 */
 	MSDOSFS_DPRINTF(("%s(): refcnt %ld, slotcount %d, slotoffset %d\n",
 	    __func__, dp->de_refcnt, slotcount, slotoffset));
 	/*
 	 * Fixup the slot description to point to the place where
 	 * we might put the new DOS direntry (putting the Win95
 	 * long name entries before that)
 	 */
 	if (!slotcount) {
 		slotcount = 1;
 		slotoffset = diroff;
 	}
 	if (wincnt > slotcount) {
 		slotoffset += sizeof(struct direntry) * (wincnt - slotcount);
 	}
 
 	/*
 	 * Return an indication of where the new directory
 	 * entry should be put.
 	 */
 	dp->de_fndoffset = slotoffset;
 	dp->de_fndcnt = wincnt - 1;
 
 	/*
 	 * We return with the directory locked, so that
 	 * the parameters we set up above will still be
 	 * valid if we actually decide to do a direnter().
 	 * We return ni_vp == NULL to indicate that the entry
 	 * does not currently exist; we leave a pointer to
 	 * the (locked) directory inode in ndp->ni_dvp.
 	 *
 	 * NB - if the directory is unlocked, then this
 	 * information cannot be used.
 	 */
 	return 0;
 }
 
 /*
  * Create a regular file. On entry the directory to contain the file being
  * created is locked.  We must release before we return.
  */
 struct denode *
 msdosfs_mkfile(const char *path, struct denode *pdep, fsnode *node)
 {
 	struct componentname cn;
 	struct denode ndirent;
 	struct denode *dep;
 	int error;
 	struct stat *st = &node->inode->st;
 
 	cn.cn_nameptr = node->name;
 	cn.cn_namelen = strlen(node->name);
 
 	MSDOSFS_DPRINTF(("%s(name %s, mode 0%o size %zu)\n",
 	    __func__, node->name, st->st_mode, (size_t)st->st_size));
 
 	/*
 	 * If this is the root directory and there is no space left we
 	 * can't do anything.  This is because the root directory can not
 	 * change size.
 	 */
 	if (pdep->de_StartCluster == MSDOSFSROOT
 	    && pdep->de_fndoffset >= pdep->de_FileSize) {
 		error = ENOSPC;
 		goto bad;
 	}
 
 	/*
 	 * Create a directory entry for the file, then call createde() to
 	 * have it installed. NOTE: DOS files are always executable.  We
 	 * use the absence of the owner write bit to make the file
 	 * readonly.
 	 */
 	memset(&ndirent, 0, sizeof(ndirent));
 	if ((error = uniqdosname(pdep, &cn, ndirent.de_Name)) != 0)
 		goto bad;
 
 	ndirent.de_Attributes = (st->st_mode & S_IWUSR) ?
 				ATTR_ARCHIVE : ATTR_ARCHIVE | ATTR_READONLY;
 	ndirent.de_StartCluster = 0;
 	ndirent.de_FileSize = 0;
 	ndirent.de_pmp = pdep->de_pmp;
 	ndirent.de_flag = DE_ACCESS | DE_CREATE | DE_UPDATE;
 	msdosfs_times(&ndirent, &node->inode->st);
 
 	if ((error = msdosfs_findslot(pdep, &cn)) != 0)
 		goto bad;
 	if ((error = createde(&ndirent, pdep, &dep, &cn)) != 0)
 		goto bad;
 	if ((error = msdosfs_wfile(path, dep, node)) != 0)
 		goto bad;
 	return dep;
 
 bad:
 	errno = error;
 	return NULL;
 }
 static int
 msdosfs_updatede(struct denode *dep)
 {
 	struct buf *bp;
 	struct direntry *dirp;
 	int error;
 
 	dep->de_flag &= ~DE_MODIFIED;
 	error = readde(dep, &bp, &dirp);
 	if (error)
 		return error;
 	DE_EXTERNALIZE(dirp, dep);
 	error = bwrite(bp);
 	return error;
 }
 
 /*
  * Write data to a file or directory.
  */
 static int
 msdosfs_wfile(const char *path, struct denode *dep, fsnode *node)
 {
 	int error, fd;
 	size_t osize = dep->de_FileSize;
 	struct stat *st = &node->inode->st;
 	size_t nsize, offs;
 	struct msdosfsmount *pmp = dep->de_pmp;
 	struct buf *bp;
 	char *dat;
 	u_long cn = 0;
 
 	error = 0;	/* XXX: gcc/vax */
 	MSDOSFS_DPRINTF(("%s(diroff %lu, dirclust %lu, startcluster %lu)\n",
 	    __func__, dep->de_diroffset, dep->de_dirclust,
 	    dep->de_StartCluster));
 	if (st->st_size == 0)
 		return 0;
 
 	/* Don't bother to try to write files larger than the fs limit */
 	if (st->st_size > MSDOSFS_FILESIZE_MAX)
 		return EFBIG;
 
 	nsize = st->st_size;
 	MSDOSFS_DPRINTF(("%s(nsize=%zu, osize=%zu)\n", __func__, nsize, osize));
 	if (nsize > osize) {
 		if ((error = deextend(dep, nsize, NULL)) != 0)
 			return error;
 		if ((error = msdosfs_updatede(dep)) != 0)
 			return error;
 	}
 
 	if ((fd = open(path, O_RDONLY)) == -1) {
 		error = errno;
 		fprintf(stderr, "open %s: %s\n", path, strerror(error));
 		return error;
 	}
 
 	if ((dat = mmap(0, nsize, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0))
 	    == MAP_FAILED) {
 		error = errno;
 		fprintf(stderr, "%s: mmap %s: %s\n", __func__, node->name,
 		    strerror(error));
 		close(fd);
 		goto out;
 	}
 	close(fd);
 
 	for (offs = 0; offs < nsize;) {
 		int blsize, cpsize;
 		daddr_t bn;
 		u_long on = offs & pmp->pm_crbomask;
 
 		if ((error = pcbmap(dep, cn++, &bn, NULL, &blsize)) != 0) {
 			MSDOSFS_DPRINTF(("%s: pcbmap %lu",
 			    __func__, (unsigned long)bn));
 			goto out;
 		}
 
 		MSDOSFS_DPRINTF(("%s(cn=%lu, bn=%llu, blsize=%d)\n",
 		    __func__, cn, (unsigned long long)bn, blsize));
 		if ((error = bread(pmp->pm_devvp, bn, blsize, 0, &bp)) != 0) {
 			MSDOSFS_DPRINTF(("bread %d\n", error));
 			goto out;
 		}
 		cpsize = MIN((nsize - offs), blsize - on);
 		memcpy(bp->b_data + on, dat + offs, cpsize);
 		bwrite(bp);
 		offs += cpsize;
 	}
 
 	munmap(dat, nsize);
 	return 0;
 out:
 	munmap(dat, nsize);
 	return error;
 }
 
 static const struct {
 	struct direntry dot;
 	struct direntry dotdot;
 } dosdirtemplate = {
 	{	".          ",				/* the . entry */
 		ATTR_DIRECTORY,				/* file attribute */
 		0,					/* reserved */
 		0, { 0, 0 }, { 0, 0 },			/* create time & date */
 		{ 0, 0 },				/* access date */
 		{ 0, 0 },				/* high bits of start cluster */
 		{ 210, 4 }, { 210, 4 },			/* modify time & date */
 		{ 0, 0 },				/* startcluster */
 		{ 0, 0, 0, 0 }				/* filesize */
 	},
 	{	"..         ",				/* the .. entry */
 		ATTR_DIRECTORY,				/* file attribute */
 		0,					/* reserved */
 		0, { 0, 0 }, { 0, 0 },			/* create time & date */
 		{ 0, 0 },				/* access date */
 		{ 0, 0 },				/* high bits of start cluster */
 		{ 210, 4 }, { 210, 4 },			/* modify time & date */
 		{ 0, 0 },				/* startcluster */
 		{ 0, 0, 0, 0 }				/* filesize */
 	}
 };
 
 struct denode *
 msdosfs_mkdire(const char *path, struct denode *pdep, fsnode *node) {
 	struct denode ndirent;
 	struct denode *dep;
 	struct componentname cn;
 	struct msdosfsmount *pmp = pdep->de_pmp;
 	int error;
 	u_long newcluster, pcl, bn;
 	struct direntry *denp;
 	struct buf *bp;
 
 	cn.cn_nameptr = node->name;
 	cn.cn_namelen = strlen(node->name);
 	/*
 	 * If this is the root directory and there is no space left we
 	 * can't do anything.  This is because the root directory can not
 	 * change size.
 	 */
 	if (pdep->de_StartCluster == MSDOSFSROOT
 	    && pdep->de_fndoffset >= pdep->de_FileSize) {
 		error = ENOSPC;
 		goto bad2;
 	}
 
 	/*
 	 * Allocate a cluster to hold the about to be created directory.
 	 */
 	error = clusteralloc(pmp, 0, 1, CLUST_EOFE, &newcluster, NULL);
 	if (error)
 		goto bad2;
 
 	memset(&ndirent, 0, sizeof(ndirent));
 	ndirent.de_pmp = pmp;
 	ndirent.de_flag = DE_ACCESS | DE_CREATE | DE_UPDATE;
 	msdosfs_times(&ndirent, &node->inode->st);
 
 	/*
 	 * Now fill the cluster with the "." and ".." entries. And write
 	 * the cluster to disk.	 This way it is there for the parent
 	 * directory to be pointing at if there were a crash.
 	 */
 	bn = cntobn(pmp, newcluster);
 	MSDOSFS_DPRINTF(("%s(newcluster %lu, bn=%lu)\n",
 	    __func__, newcluster, bn));
 	/* always succeeds */
 	bp = getblk(pmp->pm_devvp, bn, pmp->pm_bpcluster, 0, 0, 0);
 	memset(bp->b_data, 0, pmp->pm_bpcluster);
 	memcpy(bp->b_data, &dosdirtemplate, sizeof dosdirtemplate);
 	denp = (struct direntry *)bp->b_data;
 	putushort(denp[0].deStartCluster, newcluster);
 	putushort(denp[0].deCDate, ndirent.de_CDate);
 	putushort(denp[0].deCTime, ndirent.de_CTime);
 	denp[0].deCHundredth = ndirent.de_CHun;
 	putushort(denp[0].deADate, ndirent.de_ADate);
 	putushort(denp[0].deMDate, ndirent.de_MDate);
 	putushort(denp[0].deMTime, ndirent.de_MTime);
 	pcl = pdep->de_StartCluster;
 	MSDOSFS_DPRINTF(("%s(pcl %lu, rootdirblk=%lu)\n", __func__, pcl,
 	    pmp->pm_rootdirblk));
 	if (FAT32(pmp) && pcl == pmp->pm_rootdirblk)
 		pcl = 0;
 	putushort(denp[1].deStartCluster, pcl);
 	putushort(denp[1].deCDate, ndirent.de_CDate);
 	putushort(denp[1].deCTime, ndirent.de_CTime);
 	denp[1].deCHundredth = ndirent.de_CHun;
 	putushort(denp[1].deADate, ndirent.de_ADate);
 	putushort(denp[1].deMDate, ndirent.de_MDate);
 	putushort(denp[1].deMTime, ndirent.de_MTime);
 	if (FAT32(pmp)) {
 		putushort(denp[0].deHighClust, newcluster >> 16);
 		putushort(denp[1].deHighClust, pdep->de_StartCluster >> 16);
 	} else {
 		putushort(denp[0].deHighClust, 0);
 		putushort(denp[1].deHighClust, 0);
 	}
 
 	if ((error = bwrite(bp)) != 0)
 		goto bad;
 
 	/*
 	 * Now build up a directory entry pointing to the newly allocated
 	 * cluster.  This will be written to an empty slot in the parent
 	 * directory.
 	 */
 	if ((error = uniqdosname(pdep, &cn, ndirent.de_Name)) != 0)
 		goto bad;
 
 	ndirent.de_Attributes = ATTR_DIRECTORY;
 	ndirent.de_StartCluster = newcluster;
 	ndirent.de_FileSize = 0;
 	ndirent.de_pmp = pdep->de_pmp;
 	if ((error = msdosfs_findslot(pdep, &cn)) != 0)
 		goto bad;
 	if ((error = createde(&ndirent, pdep, &dep, &cn)) != 0)
 		goto bad;
 	if ((error = msdosfs_updatede(dep)) != 0)
 		goto bad;
 	return dep;
 
 bad:
-	clusterfree(pmp, newcluster, NULL);
+	clusterfree(pmp, newcluster);
 bad2:
 	errno = error;
 	return NULL;
 }