xref: /openbsd-src/sys/msdosfs/msdosfs_fat.c (revision 962873417674167d31ba9ef08dd8e7bf20468941)
1*96287341Ssf /*	$OpenBSD: msdosfs_fat.c,v 1.36 2023/06/16 08:42:08 sf Exp $	*/
2b099d67bSprovos /*	$NetBSD: msdosfs_fat.c,v 1.26 1997/10/17 11:24:02 ws Exp $	*/
3df930be7Sderaadt 
4df930be7Sderaadt /*-
5b099d67bSprovos  * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank.
6b099d67bSprovos  * Copyright (C) 1994, 1995, 1997 TooLs GmbH.
7df930be7Sderaadt  * All rights reserved.
8df930be7Sderaadt  * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below).
9df930be7Sderaadt  *
10df930be7Sderaadt  * Redistribution and use in source and binary forms, with or without
11df930be7Sderaadt  * modification, are permitted provided that the following conditions
12df930be7Sderaadt  * are met:
13df930be7Sderaadt  * 1. Redistributions of source code must retain the above copyright
14df930be7Sderaadt  *    notice, this list of conditions and the following disclaimer.
15df930be7Sderaadt  * 2. Redistributions in binary form must reproduce the above copyright
16df930be7Sderaadt  *    notice, this list of conditions and the following disclaimer in the
17df930be7Sderaadt  *    documentation and/or other materials provided with the distribution.
18df930be7Sderaadt  * 3. All advertising materials mentioning features or use of this software
19df930be7Sderaadt  *    must display the following acknowledgement:
20df930be7Sderaadt  *	This product includes software developed by TooLs GmbH.
21df930be7Sderaadt  * 4. The name of TooLs GmbH may not be used to endorse or promote products
22df930be7Sderaadt  *    derived from this software without specific prior written permission.
23df930be7Sderaadt  *
24df930be7Sderaadt  * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
25df930be7Sderaadt  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
26df930be7Sderaadt  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
27df930be7Sderaadt  * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28df930be7Sderaadt  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
29df930be7Sderaadt  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
30df930be7Sderaadt  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
31df930be7Sderaadt  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
32df930be7Sderaadt  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
33df930be7Sderaadt  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34df930be7Sderaadt  */
35df930be7Sderaadt /*
36df930be7Sderaadt  * Written by Paul Popelka (paulp@uts.amdahl.com)
37df930be7Sderaadt  *
38df930be7Sderaadt  * You can do anything you want with this software, just don't say you wrote
39df930be7Sderaadt  * it, and don't remove this notice.
40df930be7Sderaadt  *
41df930be7Sderaadt  * This software is provided "as is".
42df930be7Sderaadt  *
43df930be7Sderaadt  * The author supplies this software to be publicly redistributed on the
44df930be7Sderaadt  * understanding that the author is not responsible for the correct
45df930be7Sderaadt  * functioning of this software in any circumstances and is not liable for
46df930be7Sderaadt  * any damages caused by this software.
47df930be7Sderaadt  *
48df930be7Sderaadt  * October 1992
49df930be7Sderaadt  */
50df930be7Sderaadt 
51df930be7Sderaadt /*
52df930be7Sderaadt  * kernel include files.
53df930be7Sderaadt  */
54df930be7Sderaadt #include <sys/param.h>
55df930be7Sderaadt #include <sys/systm.h>
56df930be7Sderaadt #include <sys/buf.h>
57df930be7Sderaadt #include <sys/namei.h>
58df930be7Sderaadt #include <sys/mount.h>		/* to define statfs structure */
59df930be7Sderaadt #include <sys/vnode.h>		/* to define vattr structure */
60fde894e5Stedu #include <sys/lock.h>
61df930be7Sderaadt #include <sys/errno.h>
6216bf7bd1Sderaadt #include <sys/dirent.h>
63df930be7Sderaadt 
64df930be7Sderaadt /*
65df930be7Sderaadt  * msdosfs include files.
66df930be7Sderaadt  */
67df930be7Sderaadt #include <msdosfs/bpb.h>
68df930be7Sderaadt #include <msdosfs/msdosfsmount.h>
69df930be7Sderaadt #include <msdosfs/direntry.h>
70df930be7Sderaadt #include <msdosfs/denode.h>
71df930be7Sderaadt #include <msdosfs/fat.h>
72df930be7Sderaadt 
73df930be7Sderaadt /*
74df930be7Sderaadt  * Fat cache stats.
75df930be7Sderaadt  */
76df930be7Sderaadt int fc_fileextends;		/* # of file extends			 */
77df930be7Sderaadt int fc_lfcempty;		/* # of time last file cluster cache entry
78df930be7Sderaadt 				 * was empty */
79df930be7Sderaadt int fc_bmapcalls;		/* # of times pcbmap was called		 */
80df930be7Sderaadt 
81df930be7Sderaadt #define	LMMAX	20
82df930be7Sderaadt int fc_lmdistance[LMMAX];	/* counters for how far off the last
83df930be7Sderaadt 				 * cluster mapped entry was. */
84df930be7Sderaadt int fc_largedistance;		/* off by more than LMMAX		 */
85df930be7Sderaadt 
8682fa9538Stedu static void fatblock(struct msdosfsmount *, uint32_t, uint32_t *, uint32_t *,
8782fa9538Stedu 			  uint32_t *);
8882fa9538Stedu void updatefats(struct msdosfsmount *, struct buf *, uint32_t);
8982fa9538Stedu static __inline void usemap_free(struct msdosfsmount *, uint32_t);
9082fa9538Stedu static __inline void usemap_alloc(struct msdosfsmount *, uint32_t);
9182fa9538Stedu static int fatchain(struct msdosfsmount *, uint32_t, uint32_t, uint32_t);
9282fa9538Stedu int chainlength(struct msdosfsmount *, uint32_t, uint32_t);
9382fa9538Stedu int chainalloc(struct msdosfsmount *, uint32_t, uint32_t, uint32_t, uint32_t *,
9482fa9538Stedu 		    uint32_t *);
95879b3eabSniklas 
96df930be7Sderaadt static void
fatblock(struct msdosfsmount * pmp,uint32_t ofs,uint32_t * bnp,uint32_t * sizep,uint32_t * bop)977d80fe84Sjasper fatblock(struct msdosfsmount *pmp, uint32_t ofs, uint32_t *bnp, uint32_t *sizep,
987d80fe84Sjasper     uint32_t *bop)
99df930be7Sderaadt {
10082fa9538Stedu 	uint32_t bn, size;
101df930be7Sderaadt 
102df930be7Sderaadt 	bn = ofs / pmp->pm_fatblocksize * pmp->pm_fatblocksec;
103402c79f7Skrw 	size = min(pmp->pm_fatblocksec, pmp->pm_FATsecs - bn) * DEV_BSIZE;
104b099d67bSprovos 	bn += pmp->pm_fatblk + pmp->pm_curfat * pmp->pm_FATsecs;
105b099d67bSprovos 
106df930be7Sderaadt 	if (bnp)
107df930be7Sderaadt 		*bnp = bn;
108df930be7Sderaadt 	if (sizep)
109df930be7Sderaadt 		*sizep = size;
110df930be7Sderaadt 	if (bop)
111df930be7Sderaadt 		*bop = ofs % pmp->pm_fatblocksize;
112df930be7Sderaadt }
113df930be7Sderaadt 
114df930be7Sderaadt /*
115df930be7Sderaadt  * Map the logical cluster number of a file into a physical disk sector
116df930be7Sderaadt  * that is filesystem relative.
117df930be7Sderaadt  *
118df930be7Sderaadt  * dep	  - address of denode representing the file of interest
119df930be7Sderaadt  * findcn - file relative cluster whose filesystem relative cluster number
120df930be7Sderaadt  *	    and/or block number are/is to be found
121df930be7Sderaadt  * bnp	  - address of where to place the file system relative block number.
122df930be7Sderaadt  *	    If this pointer is null then don't return this quantity.
123df930be7Sderaadt  * cnp	  - address of where to place the file system relative cluster number.
124df930be7Sderaadt  *	    If this pointer is null then don't return this quantity.
125cc40aa10Ssf  * sp	  - address of where to place the block size for the file/dir
126cc40aa10Ssf  *	    If this pointer is null then don't return this quantity.
127df930be7Sderaadt  *
128df930be7Sderaadt  * NOTE: Either bnp or cnp must be non-null.
129df930be7Sderaadt  * This function has one side effect.  If the requested file relative cluster
130df930be7Sderaadt  * is beyond the end of file, then the actual number of clusters in the file
131df930be7Sderaadt  * is returned in *cnp.  This is useful for determining how long a directory is.
132df930be7Sderaadt  *  If cnp is null, nothing is returned.
133df930be7Sderaadt  */
134df930be7Sderaadt int
pcbmap(struct denode * dep,uint32_t findcn,daddr_t * bnp,uint32_t * cnp,int * sp)1351abdbfdeSderaadt pcbmap(struct denode *dep, uint32_t findcn, daddr_t *bnp, uint32_t *cnp,
1367d80fe84Sjasper     int *sp)
137df930be7Sderaadt {
138df930be7Sderaadt 	int error;
13982fa9538Stedu 	uint32_t i;
14082fa9538Stedu 	uint32_t cn;
14182fa9538Stedu 	uint32_t prevcn = 0; /* XXX: prevcn could be used uninitialized */
14282fa9538Stedu 	uint32_t byteoffset;
14382fa9538Stedu 	uint32_t bn;
14482fa9538Stedu 	uint32_t bo;
145df930be7Sderaadt 	struct buf *bp = NULL;
14682fa9538Stedu 	uint32_t bp_bn = -1;
147df930be7Sderaadt 	struct msdosfsmount *pmp = dep->de_pmp;
14882fa9538Stedu 	uint32_t bsize;
149df930be7Sderaadt 
150df930be7Sderaadt 	fc_bmapcalls++;
151df930be7Sderaadt 
152df930be7Sderaadt 	/*
153df930be7Sderaadt 	 * If they don't give us someplace to return a value then don't
154df930be7Sderaadt 	 * bother doing anything.
155df930be7Sderaadt 	 */
156df930be7Sderaadt 	if (bnp == NULL && cnp == NULL && sp == NULL)
157df930be7Sderaadt 		return (0);
158df930be7Sderaadt 
159df930be7Sderaadt 	cn = dep->de_StartCluster;
160df930be7Sderaadt 	/*
161df930be7Sderaadt 	 * The "file" that makes up the root directory is contiguous,
162df930be7Sderaadt 	 * permanently allocated, of fixed size, and is not made up of
163df930be7Sderaadt 	 * clusters.  If the cluster number is beyond the end of the root
164df930be7Sderaadt 	 * directory, then return the number of clusters in the file.
165df930be7Sderaadt 	 */
166df930be7Sderaadt 	if (cn == MSDOSFSROOT) {
167df930be7Sderaadt 		if (dep->de_Attributes & ATTR_DIRECTORY) {
16816bf7bd1Sderaadt 			if (de_cn2off(pmp, findcn) >= dep->de_FileSize) {
169df930be7Sderaadt 				if (cnp)
17016bf7bd1Sderaadt 					*cnp = de_bn2cn(pmp, pmp->pm_rootdirsize);
171df930be7Sderaadt 				return (E2BIG);
172df930be7Sderaadt 			}
173df930be7Sderaadt 			if (bnp)
17416bf7bd1Sderaadt 				*bnp = pmp->pm_rootdirblk + de_cn2bn(pmp, findcn);
175df930be7Sderaadt 			if (cnp)
176df930be7Sderaadt 				*cnp = MSDOSFSROOT;
177df930be7Sderaadt 			if (sp)
178df930be7Sderaadt 				*sp = min(pmp->pm_bpcluster,
17916bf7bd1Sderaadt 				    dep->de_FileSize - de_cn2off(pmp, findcn));
180df930be7Sderaadt 			return (0);
181df930be7Sderaadt 		} else {		/* just an empty file */
182df930be7Sderaadt 			if (cnp)
183df930be7Sderaadt 				*cnp = 0;
184df930be7Sderaadt 			return (E2BIG);
185df930be7Sderaadt 		}
186df930be7Sderaadt 	}
187df930be7Sderaadt 
188df930be7Sderaadt 	/*
189df930be7Sderaadt 	 * All other files do I/O in cluster sized blocks
190df930be7Sderaadt 	 */
191df930be7Sderaadt 	if (sp)
192df930be7Sderaadt 		*sp = pmp->pm_bpcluster;
193df930be7Sderaadt 
194df930be7Sderaadt 	/*
195df930be7Sderaadt 	 * Rummage around in the fat cache, maybe we can avoid tromping
196df930be7Sderaadt 	 * thru every fat entry for the file. And, keep track of how far
197df930be7Sderaadt 	 * off the cache was from where we wanted to be.
198df930be7Sderaadt 	 */
199df930be7Sderaadt 	i = 0;
200df930be7Sderaadt 	fc_lookup(dep, findcn, &i, &cn);
201df930be7Sderaadt 	if ((bn = findcn - i) >= LMMAX)
202df930be7Sderaadt 		fc_largedistance++;
203df930be7Sderaadt 	else
204df930be7Sderaadt 		fc_lmdistance[bn]++;
205df930be7Sderaadt 
206df930be7Sderaadt 	/*
207df930be7Sderaadt 	 * Handle all other files or directories the normal way.
208df930be7Sderaadt 	 */
209df930be7Sderaadt 	for (; i < findcn; i++) {
210b099d67bSprovos 		/*
211b099d67bSprovos 		 * Stop with all reserved clusters, not just with EOF.
212b099d67bSprovos 		 */
213b099d67bSprovos 		if ((cn | ~pmp->pm_fatmask) >= CLUST_RSRVD)
214df930be7Sderaadt 			goto hiteof;
215df930be7Sderaadt 		byteoffset = FATOFS(pmp, cn);
216df930be7Sderaadt 		fatblock(pmp, byteoffset, &bn, &bsize, &bo);
217df930be7Sderaadt 		if (bn != bp_bn) {
218df930be7Sderaadt 			if (bp)
219df930be7Sderaadt 				brelse(bp);
22093f62a9eStedu 			error = bread(pmp->pm_devvp, bn, bsize, &bp);
221879b3eabSniklas 			if (error) {
22216bf7bd1Sderaadt 				brelse(bp);
223df930be7Sderaadt 				return (error);
22416bf7bd1Sderaadt 			}
225df930be7Sderaadt 			bp_bn = bn;
226df930be7Sderaadt 		}
227df930be7Sderaadt 		prevcn = cn;
22802368b83Stedu 		if (bo >= bsize) {
22902368b83Stedu 			if (bp)
23002368b83Stedu 				brelse(bp);
23102368b83Stedu 			return (EIO);
23202368b83Stedu 		}
233b099d67bSprovos 		if (FAT32(pmp))
234b099d67bSprovos 			cn = getulong(&bp->b_data[bo]);
235b099d67bSprovos 		else
236df930be7Sderaadt 			cn = getushort(&bp->b_data[bo]);
237b099d67bSprovos 		if (FAT12(pmp) && (prevcn & 1))
238df930be7Sderaadt 			cn >>= 4;
239b099d67bSprovos 		cn &= pmp->pm_fatmask;
240b099d67bSprovos 
241df930be7Sderaadt 		/*
242b099d67bSprovos 		 * Force the special cluster numbers
243b099d67bSprovos 		 * to be the same for all cluster sizes
244b099d67bSprovos 		 * to let the rest of msdosfs handle
245b099d67bSprovos 		 * all cases the same.
246df930be7Sderaadt 		 */
247b099d67bSprovos 		if ((cn | ~pmp->pm_fatmask) >= CLUST_RSRVD)
248b099d67bSprovos 			cn |= ~pmp->pm_fatmask;
249df930be7Sderaadt 	}
250df930be7Sderaadt 
251b099d67bSprovos 	if (!MSDOSFSEOF(pmp, cn)) {
252df930be7Sderaadt 		if (bp)
253df930be7Sderaadt 			brelse(bp);
254df930be7Sderaadt 		if (bnp)
255df930be7Sderaadt 			*bnp = cntobn(pmp, cn);
256df930be7Sderaadt 		if (cnp)
257df930be7Sderaadt 			*cnp = cn;
258df930be7Sderaadt 		fc_setcache(dep, FC_LASTMAP, i, cn);
259df930be7Sderaadt 		return (0);
260df930be7Sderaadt 	}
261df930be7Sderaadt 
262cc40aa10Ssf hiteof:
263df930be7Sderaadt 	if (cnp)
264df930be7Sderaadt 		*cnp = i;
265df930be7Sderaadt 	if (bp)
266df930be7Sderaadt 		brelse(bp);
267df930be7Sderaadt 	/* update last file cluster entry in the fat cache */
268df930be7Sderaadt 	fc_setcache(dep, FC_LASTFC, i - 1, prevcn);
269df930be7Sderaadt 	return (E2BIG);
270df930be7Sderaadt }
271df930be7Sderaadt 
272df930be7Sderaadt /*
273df930be7Sderaadt  * Find the closest entry in the fat cache to the cluster we are looking
274df930be7Sderaadt  * for.
275df930be7Sderaadt  */
276df930be7Sderaadt void
fc_lookup(struct denode * dep,uint32_t findcn,uint32_t * frcnp,uint32_t * fsrcnp)2777d80fe84Sjasper fc_lookup(struct denode *dep, uint32_t findcn, uint32_t *frcnp, uint32_t *fsrcnp)
278df930be7Sderaadt {
279df930be7Sderaadt 	int i;
28082fa9538Stedu 	uint32_t cn;
281df930be7Sderaadt 	struct fatcache *closest = 0;
282df930be7Sderaadt 
283df930be7Sderaadt 	for (i = 0; i < FC_SIZE; i++) {
284df930be7Sderaadt 		cn = dep->de_fc[i].fc_frcn;
285df930be7Sderaadt 		if (cn != FCE_EMPTY && cn <= findcn) {
286df930be7Sderaadt 			if (closest == 0 || cn > closest->fc_frcn)
287df930be7Sderaadt 				closest = &dep->de_fc[i];
288df930be7Sderaadt 		}
289df930be7Sderaadt 	}
290df930be7Sderaadt 	if (closest) {
291df930be7Sderaadt 		*frcnp = closest->fc_frcn;
292df930be7Sderaadt 		*fsrcnp = closest->fc_fsrcn;
293df930be7Sderaadt 	}
294df930be7Sderaadt }
295df930be7Sderaadt 
296df930be7Sderaadt /*
297df930be7Sderaadt  * Purge the fat cache in denode dep of all entries relating to file
298df930be7Sderaadt  * relative cluster frcn and beyond.
299df930be7Sderaadt  */
300df930be7Sderaadt void
fc_purge(struct denode * dep,u_int frcn)3017d80fe84Sjasper fc_purge(struct denode *dep, u_int frcn)
302df930be7Sderaadt {
303df930be7Sderaadt 	int i;
304df930be7Sderaadt 	struct fatcache *fcp;
305df930be7Sderaadt 
306df930be7Sderaadt 	fcp = dep->de_fc;
307df930be7Sderaadt 	for (i = 0; i < FC_SIZE; i++, fcp++) {
308df930be7Sderaadt 		if (fcp->fc_frcn >= frcn)
309df930be7Sderaadt 			fcp->fc_frcn = FCE_EMPTY;
310df930be7Sderaadt 	}
311df930be7Sderaadt }
312df930be7Sderaadt 
313df930be7Sderaadt /*
314b099d67bSprovos  * Update the fat.
315b099d67bSprovos  * If mirroring the fat, update all copies, with the first copy as last.
316b099d67bSprovos  * Else update only the current fat (ignoring the others).
317df930be7Sderaadt  *
318df930be7Sderaadt  * pmp	 - msdosfsmount structure for filesystem to update
319df930be7Sderaadt  * bp	 - addr of modified fat block
320df930be7Sderaadt  * fatbn - block number relative to begin of filesystem of the modified fat block.
321df930be7Sderaadt  */
322df930be7Sderaadt void
updatefats(struct msdosfsmount * pmp,struct buf * bp,uint32_t fatbn)3237d80fe84Sjasper updatefats(struct msdosfsmount *pmp, struct buf *bp, uint32_t fatbn)
324df930be7Sderaadt {
325df930be7Sderaadt 	int i;
326df930be7Sderaadt 	struct buf *bpn;
327df930be7Sderaadt 
328df930be7Sderaadt #ifdef MSDOSFS_DEBUG
32981ad5374Skrw 	printf("updatefats(pmp %p, buf %p, fatbn %d)\n", pmp, bp, fatbn);
330df930be7Sderaadt #endif
331df930be7Sderaadt 
332df930be7Sderaadt 	/*
333b099d67bSprovos 	 * If we have an FSInfo block, update it.
334b099d67bSprovos 	 */
335b099d67bSprovos 	if (pmp->pm_fsinfo) {
33693f62a9eStedu 		if (bread(pmp->pm_devvp, pmp->pm_fsinfo, fsi_size(pmp),
337402c79f7Skrw 		    &bpn) != 0) {
338b099d67bSprovos 			/*
339b099d67bSprovos 			 * Ignore the error, but turn off FSInfo update for the future.
340b099d67bSprovos 			 */
341b099d67bSprovos 			pmp->pm_fsinfo = 0;
342b099d67bSprovos 			brelse(bpn);
343b099d67bSprovos 		} else {
344b099d67bSprovos 			struct fsinfo *fp = (struct fsinfo *)bpn->b_data;
345b099d67bSprovos 
346b099d67bSprovos 			putulong(fp->fsinfree, pmp->pm_freeclustercount);
347b099d67bSprovos 			if (pmp->pm_flags & MSDOSFSMNT_WAITONFAT)
348b099d67bSprovos 				bwrite(bpn);
349b099d67bSprovos 			else
350b099d67bSprovos 				bdwrite(bpn);
351b099d67bSprovos 		}
352b099d67bSprovos 	}
353b099d67bSprovos 
354b099d67bSprovos 	if (pmp->pm_flags & MSDOSFS_FATMIRROR) {
355b099d67bSprovos 		/*
356df930be7Sderaadt 		 * Now copy the block(s) of the modified fat to the other copies of
357df930be7Sderaadt 		 * the fat and write them out.  This is faster than reading in the
358df930be7Sderaadt 		 * other fats and then writing them back out.  This could tie up
359df930be7Sderaadt 		 * the fat for quite a while. Preventing others from accessing it.
360df930be7Sderaadt 		 * To prevent us from going after the fat quite so much we use
361b66b9ef8Sjsg 		 * delayed writes, unless they specified "synchronous" when the
362df930be7Sderaadt 		 * filesystem was mounted.  If synch is asked for then use
363df930be7Sderaadt 		 * bwrite()'s and really slow things down.
364df930be7Sderaadt 		 */
365df930be7Sderaadt 		for (i = 1; i < pmp->pm_FATs; i++) {
366df930be7Sderaadt 			fatbn += pmp->pm_FATsecs;
367df930be7Sderaadt 			/* getblk() never fails */
368570df5c4Scheloha 			bpn = getblk(pmp->pm_devvp, fatbn, bp->b_bcount, 0,
369570df5c4Scheloha 			    INFSLP);
370df930be7Sderaadt 			bcopy(bp->b_data, bpn->b_data, bp->b_bcount);
37116bf7bd1Sderaadt 			if (pmp->pm_flags & MSDOSFSMNT_WAITONFAT)
372df930be7Sderaadt 				bwrite(bpn);
373df930be7Sderaadt 			else
374df930be7Sderaadt 				bdwrite(bpn);
375df930be7Sderaadt 		}
376b099d67bSprovos 	}
377b099d67bSprovos 
378df930be7Sderaadt 	/*
379b099d67bSprovos 	 * Write out the first (or current) fat last.
380df930be7Sderaadt 	 */
38116bf7bd1Sderaadt 	if (pmp->pm_flags & MSDOSFSMNT_WAITONFAT)
382df930be7Sderaadt 		bwrite(bp);
383df930be7Sderaadt 	else
384df930be7Sderaadt 		bdwrite(bp);
385b099d67bSprovos 	/*
386b099d67bSprovos 	 * Maybe update fsinfo sector here?
387b099d67bSprovos 	 */
388df930be7Sderaadt }
389df930be7Sderaadt 
390df930be7Sderaadt /*
391df930be7Sderaadt  * Updating entries in 12 bit fats is a pain in the butt.
392df930be7Sderaadt  *
393df930be7Sderaadt  * The following picture shows where nibbles go when moving from a 12 bit
394df930be7Sderaadt  * cluster number into the appropriate bytes in the FAT.
395df930be7Sderaadt  *
396df930be7Sderaadt  *	byte m        byte m+1      byte m+2
397df930be7Sderaadt  *	+----+----+   +----+----+   +----+----+
398df930be7Sderaadt  *	|  0    1 |   |  2    3 |   |  4    5 |   FAT bytes
399df930be7Sderaadt  *	+----+----+   +----+----+   +----+----+
400df930be7Sderaadt  *
401df930be7Sderaadt  *	+----+----+----+   +----+----+----+
402df930be7Sderaadt  *	|  3    0    1 |   |  4    5    2 |
403df930be7Sderaadt  *	+----+----+----+   +----+----+----+
404df930be7Sderaadt  *	cluster n	   cluster n+1
405df930be7Sderaadt  *
406df930be7Sderaadt  * Where n is even. m = n + (n >> 2)
407df930be7Sderaadt  *
408df930be7Sderaadt  */
409df930be7Sderaadt static __inline void
usemap_alloc(struct msdosfsmount * pmp,uint32_t cn)4107d80fe84Sjasper usemap_alloc(struct msdosfsmount *pmp, uint32_t cn)
411df930be7Sderaadt {
41273083da7Ssf 	KASSERT(cn <= pmp->pm_maxcluster);
413df930be7Sderaadt 
414*96287341Ssf 	pmp->pm_inusemap[cn / N_INUSEBITS] |= 1U << (cn % N_INUSEBITS);
415df930be7Sderaadt 	pmp->pm_freeclustercount--;
416df930be7Sderaadt }
417df930be7Sderaadt 
418df930be7Sderaadt static __inline void
usemap_free(struct msdosfsmount * pmp,uint32_t cn)4197d80fe84Sjasper usemap_free(struct msdosfsmount *pmp, uint32_t cn)
420df930be7Sderaadt {
42173083da7Ssf 	KASSERT(cn <= pmp->pm_maxcluster);
422df930be7Sderaadt 
423df930be7Sderaadt 	pmp->pm_freeclustercount++;
424*96287341Ssf 	pmp->pm_inusemap[cn / N_INUSEBITS] &= ~(1U << (cn % N_INUSEBITS));
425df930be7Sderaadt }
426df930be7Sderaadt 
427df930be7Sderaadt int
clusterfree(struct msdosfsmount * pmp,uint32_t cluster,uint32_t * oldcnp)4287d80fe84Sjasper clusterfree(struct msdosfsmount *pmp, uint32_t cluster, uint32_t *oldcnp)
429df930be7Sderaadt {
430df930be7Sderaadt 	int error;
43182fa9538Stedu 	uint32_t oldcn;
432df930be7Sderaadt 
433b099d67bSprovos 	usemap_free(pmp, cluster);
434879b3eabSniklas 	error = fatentry(FAT_GET_AND_SET, pmp, cluster, &oldcn, MSDOSFSFREE);
435b099d67bSprovos 	if (error) {
436b099d67bSprovos 		usemap_alloc(pmp, cluster);
437df930be7Sderaadt 		return (error);
438b099d67bSprovos 	}
439df930be7Sderaadt 	/*
440df930be7Sderaadt 	 * If the cluster was successfully marked free, then update
441df930be7Sderaadt 	 * the count of free clusters, and turn off the "allocated"
442df930be7Sderaadt 	 * bit in the "in use" cluster bit map.
443df930be7Sderaadt 	 */
444df930be7Sderaadt 	if (oldcnp)
445df930be7Sderaadt 		*oldcnp = oldcn;
446df930be7Sderaadt 	return (0);
447df930be7Sderaadt }
448df930be7Sderaadt 
449df930be7Sderaadt /*
450df930be7Sderaadt  * Get or Set or 'Get and Set' the cluster'th entry in the fat.
451df930be7Sderaadt  *
452df930be7Sderaadt  * function	- whether to get or set a fat entry
453df930be7Sderaadt  * pmp		- address of the msdosfsmount structure for the filesystem
454df930be7Sderaadt  *		  whose fat is to be manipulated.
455df930be7Sderaadt  * cn		- which cluster is of interest
456df930be7Sderaadt  * oldcontents	- address of a word that is to receive the contents of the
457df930be7Sderaadt  *		  cluster'th entry if this is a get function
458df930be7Sderaadt  * newcontents	- the new value to be written into the cluster'th element of
459df930be7Sderaadt  *		  the fat if this is a set function.
460df930be7Sderaadt  *
461df930be7Sderaadt  * This function can also be used to free a cluster by setting the fat entry
462df930be7Sderaadt  * for a cluster to 0.
463df930be7Sderaadt  *
464df930be7Sderaadt  * All copies of the fat are updated if this is a set function. NOTE: If
465df930be7Sderaadt  * fatentry() marks a cluster as free it does not update the inusemap in
466df930be7Sderaadt  * the msdosfsmount structure. This is left to the caller.
467df930be7Sderaadt  */
468df930be7Sderaadt int
fatentry(int function,struct msdosfsmount * pmp,uint32_t cn,uint32_t * oldcontents,uint32_t newcontents)4697d80fe84Sjasper fatentry(int function, struct msdosfsmount *pmp, uint32_t cn, uint32_t *oldcontents,
4707d80fe84Sjasper     uint32_t newcontents)
471df930be7Sderaadt {
472df930be7Sderaadt 	int error;
47382fa9538Stedu 	uint32_t readcn;
47482fa9538Stedu 	uint32_t bn, bo, bsize, byteoffset;
475df930be7Sderaadt 	struct buf *bp;
476df930be7Sderaadt 
477b099d67bSprovos #ifdef MSDOSFS_DEBUG
47881ad5374Skrw 	 printf("fatentry(func %d, pmp %p, clust %d, oldcon %p, "
47981ad5374Skrw 	     "newcon %d)\n", function, pmp, cn, oldcontents, newcontents);
48078b4a1c4Smillert #endif
481df930be7Sderaadt 
482df930be7Sderaadt #ifdef DIAGNOSTIC
483df930be7Sderaadt 	/*
484df930be7Sderaadt 	 * Be sure they asked us to do something.
485df930be7Sderaadt 	 */
486df930be7Sderaadt 	if ((function & (FAT_SET | FAT_GET)) == 0) {
487df930be7Sderaadt 		printf("fatentry(): function code doesn't specify get or set\n");
488df930be7Sderaadt 		return (EINVAL);
489df930be7Sderaadt 	}
490df930be7Sderaadt 
491df930be7Sderaadt 	/*
492df930be7Sderaadt 	 * If they asked us to return a cluster number but didn't tell us
493df930be7Sderaadt 	 * where to put it, give them an error.
494df930be7Sderaadt 	 */
495df930be7Sderaadt 	if ((function & FAT_GET) && oldcontents == NULL) {
496df930be7Sderaadt 		printf("fatentry(): get function with no place to put result\n");
497df930be7Sderaadt 		return (EINVAL);
498df930be7Sderaadt 	}
499df930be7Sderaadt #endif
500df930be7Sderaadt 
501df930be7Sderaadt 	/*
502df930be7Sderaadt 	 * Be sure the requested cluster is in the filesystem.
503df930be7Sderaadt 	 */
504df930be7Sderaadt 	if (cn < CLUST_FIRST || cn > pmp->pm_maxcluster)
505df930be7Sderaadt 		return (EINVAL);
506df930be7Sderaadt 
507df930be7Sderaadt 	byteoffset = FATOFS(pmp, cn);
508df930be7Sderaadt 	fatblock(pmp, byteoffset, &bn, &bsize, &bo);
50993f62a9eStedu 	if ((error = bread(pmp->pm_devvp, bn, bsize, &bp)) != 0) {
51016bf7bd1Sderaadt 		brelse(bp);
511df930be7Sderaadt 		return (error);
51216bf7bd1Sderaadt 	}
513df930be7Sderaadt 
514df930be7Sderaadt 	if (function & FAT_GET) {
515b099d67bSprovos 		if (FAT32(pmp))
516b099d67bSprovos 			readcn = getulong(&bp->b_data[bo]);
517b099d67bSprovos 		else
518df930be7Sderaadt 			readcn = getushort(&bp->b_data[bo]);
519662fc744Sderaadt 		if (FAT12(pmp) && (cn & 1))
520df930be7Sderaadt 			readcn >>= 4;
521b099d67bSprovos 		readcn &= pmp->pm_fatmask;
522b099d67bSprovos 		/* map reserved fat entries to same values for all fats */
523b099d67bSprovos 		if ((readcn | ~pmp->pm_fatmask) >= CLUST_RSRVD)
524b099d67bSprovos 			readcn |= ~pmp->pm_fatmask;
525df930be7Sderaadt 		*oldcontents = readcn;
526df930be7Sderaadt 	}
527df930be7Sderaadt 	if (function & FAT_SET) {
528b099d67bSprovos 		switch (pmp->pm_fatmask) {
529b099d67bSprovos 		case FAT12_MASK:
530df930be7Sderaadt 			readcn = getushort(&bp->b_data[bo]);
531df930be7Sderaadt 			if (cn & 1) {
532df930be7Sderaadt 				readcn &= 0x000f;
533df930be7Sderaadt 				readcn |= newcontents << 4;
534df930be7Sderaadt 			} else {
535df930be7Sderaadt 				readcn &= 0xf000;
536df930be7Sderaadt 				readcn |= newcontents & 0xfff;
537df930be7Sderaadt 			}
538df930be7Sderaadt 			putushort(&bp->b_data[bo], readcn);
539b099d67bSprovos 			break;
540b099d67bSprovos 		case FAT16_MASK:
541df930be7Sderaadt 			putushort(&bp->b_data[bo], newcontents);
542b099d67bSprovos 			break;
543b099d67bSprovos 		case FAT32_MASK:
544b099d67bSprovos 			/*
545b099d67bSprovos 			 * According to spec we have to retain the
546b099d67bSprovos 			 * high order bits of the fat entry.
547b099d67bSprovos 			 */
548b099d67bSprovos 			readcn = getulong(&bp->b_data[bo]);
549b099d67bSprovos 			readcn &= ~FAT32_MASK;
550b099d67bSprovos 			readcn |= newcontents & FAT32_MASK;
551b099d67bSprovos 			putulong(&bp->b_data[bo], readcn);
552b099d67bSprovos 			break;
553b099d67bSprovos 		}
554df930be7Sderaadt 		updatefats(pmp, bp, bn);
555df930be7Sderaadt 		bp = NULL;
556df930be7Sderaadt 		pmp->pm_fmod = 1;
557df930be7Sderaadt 	}
558df930be7Sderaadt 	if (bp)
559df930be7Sderaadt 		brelse(bp);
560df930be7Sderaadt 	return (0);
561df930be7Sderaadt }
562df930be7Sderaadt 
563df930be7Sderaadt /*
564df930be7Sderaadt  * Update a contiguous cluster chain
565df930be7Sderaadt  *
566df930be7Sderaadt  * pmp	    - mount point
567df930be7Sderaadt  * start    - first cluster of chain
568df930be7Sderaadt  * count    - number of clusters in chain
569df930be7Sderaadt  * fillwith - what to write into fat entry of last cluster
570df930be7Sderaadt  */
571df930be7Sderaadt static int
fatchain(struct msdosfsmount * pmp,uint32_t start,uint32_t count,uint32_t fillwith)5727d80fe84Sjasper fatchain(struct msdosfsmount *pmp, uint32_t start, uint32_t count, uint32_t fillwith)
573df930be7Sderaadt {
574df930be7Sderaadt 	int error;
57582fa9538Stedu 	uint32_t bn, bo, bsize, byteoffset, readcn, newc;
576df930be7Sderaadt 	struct buf *bp;
577df930be7Sderaadt 
578df930be7Sderaadt #ifdef MSDOSFS_DEBUG
57981ad5374Skrw 	printf("fatchain(pmp %p, start %d, count %d, fillwith %d)\n",
580df930be7Sderaadt 	    pmp, start, count, fillwith);
581df930be7Sderaadt #endif
582df930be7Sderaadt 	/*
583df930be7Sderaadt 	 * Be sure the clusters are in the filesystem.
584df930be7Sderaadt 	 */
585df930be7Sderaadt 	if (start < CLUST_FIRST || start + count - 1 > pmp->pm_maxcluster)
586df930be7Sderaadt 		return (EINVAL);
587df930be7Sderaadt 
588df930be7Sderaadt 	while (count > 0) {
589df930be7Sderaadt 		byteoffset = FATOFS(pmp, start);
590df930be7Sderaadt 		fatblock(pmp, byteoffset, &bn, &bsize, &bo);
59193f62a9eStedu 		error = bread(pmp->pm_devvp, bn, bsize, &bp);
592879b3eabSniklas 		if (error) {
59316bf7bd1Sderaadt 			brelse(bp);
594df930be7Sderaadt 			return (error);
59516bf7bd1Sderaadt 		}
596df930be7Sderaadt 		while (count > 0) {
597df930be7Sderaadt 			start++;
598df930be7Sderaadt 			newc = --count > 0 ? start : fillwith;
599b099d67bSprovos 			switch (pmp->pm_fatmask) {
600b099d67bSprovos 			case FAT12_MASK:
601df930be7Sderaadt 				readcn = getushort(&bp->b_data[bo]);
602df930be7Sderaadt 				if (start & 1) {
603df930be7Sderaadt 					readcn &= 0xf000;
604df930be7Sderaadt 					readcn |= newc & 0xfff;
605df930be7Sderaadt 				} else {
606df930be7Sderaadt 					readcn &= 0x000f;
607df930be7Sderaadt 					readcn |= newc << 4;
608df930be7Sderaadt 				}
609df930be7Sderaadt 				putushort(&bp->b_data[bo], readcn);
610df930be7Sderaadt 				bo++;
611df930be7Sderaadt 				if (!(start & 1))
612df930be7Sderaadt 					bo++;
613b099d67bSprovos 				break;
614b099d67bSprovos 			case FAT16_MASK:
615df930be7Sderaadt 				putushort(&bp->b_data[bo], newc);
616df930be7Sderaadt 				bo += 2;
617b099d67bSprovos 				break;
618b099d67bSprovos 			case FAT32_MASK:
619b099d67bSprovos 				readcn = getulong(&bp->b_data[bo]);
620b099d67bSprovos 				readcn &= ~pmp->pm_fatmask;
621b099d67bSprovos 				readcn |= newc & pmp->pm_fatmask;
622b099d67bSprovos 				putulong(&bp->b_data[bo], readcn);
623b099d67bSprovos 				bo += 4;
624b099d67bSprovos 				break;
625df930be7Sderaadt 			}
626df930be7Sderaadt 			if (bo >= bsize)
627df930be7Sderaadt 				break;
628df930be7Sderaadt 		}
629df930be7Sderaadt 		updatefats(pmp, bp, bn);
630df930be7Sderaadt 	}
631df930be7Sderaadt 	pmp->pm_fmod = 1;
632df930be7Sderaadt 	return (0);
633df930be7Sderaadt }
634df930be7Sderaadt 
635df930be7Sderaadt /*
636df930be7Sderaadt  * Check the length of a free cluster chain starting at start.
637df930be7Sderaadt  *
638df930be7Sderaadt  * pmp	 - mount point
639df930be7Sderaadt  * start - start of chain
640df930be7Sderaadt  * count - maximum interesting length
641df930be7Sderaadt  */
642df930be7Sderaadt int
chainlength(struct msdosfsmount * pmp,uint32_t start,uint32_t count)6437d80fe84Sjasper chainlength(struct msdosfsmount *pmp, uint32_t start, uint32_t count)
644df930be7Sderaadt {
64582fa9538Stedu 	uint32_t idx, max_idx;
646df930be7Sderaadt 	u_int map;
64782fa9538Stedu 	uint32_t len;
648df930be7Sderaadt 
64973083da7Ssf 	if (start > pmp->pm_maxcluster)
65073083da7Ssf 	    return (0);
651df930be7Sderaadt 	max_idx = pmp->pm_maxcluster / N_INUSEBITS;
652df930be7Sderaadt 	idx = start / N_INUSEBITS;
653df930be7Sderaadt 	start %= N_INUSEBITS;
654df930be7Sderaadt 	map = pmp->pm_inusemap[idx];
655*96287341Ssf 	map &= ~((1U << start) - 1);
656df930be7Sderaadt 	if (map) {
657df930be7Sderaadt 		len = ffs(map) - 1 - start;
65873083da7Ssf 		len = MIN(len, count);
65973083da7Ssf 		len = MIN(len, pmp->pm_maxcluster - start + 1);
66073083da7Ssf 		return (len);
661df930be7Sderaadt 	}
662df930be7Sderaadt 	len = N_INUSEBITS - start;
66373083da7Ssf 	if (len >= count) {
66473083da7Ssf 		len = MIN(count, pmp->pm_maxcluster - start + 1);
66573083da7Ssf 		return (len);
66673083da7Ssf 	}
667df930be7Sderaadt 	while (++idx <= max_idx) {
668df930be7Sderaadt 		if (len >= count)
669df930be7Sderaadt 			break;
6706301d40aSitojun 		if ((map = pmp->pm_inusemap[idx]) != 0) {
671df930be7Sderaadt 			len +=  ffs(map) - 1;
672df930be7Sderaadt 			break;
673df930be7Sderaadt 		}
674df930be7Sderaadt 		len += N_INUSEBITS;
675df930be7Sderaadt 	}
67673083da7Ssf 	len = MIN(len, count);
67773083da7Ssf 	len = MIN(len, pmp->pm_maxcluster - start + 1);
67873083da7Ssf 	return (len);
679df930be7Sderaadt }
680df930be7Sderaadt 
681df930be7Sderaadt /*
682b66b9ef8Sjsg  * Allocate contiguous free clusters.
683df930be7Sderaadt  *
684df930be7Sderaadt  * pmp	      - mount point.
685df930be7Sderaadt  * start      - start of cluster chain.
686df930be7Sderaadt  * count      - number of clusters to allocate.
687df930be7Sderaadt  * fillwith   - put this value into the fat entry for the
688df930be7Sderaadt  *		last allocated cluster.
689df930be7Sderaadt  * retcluster - put the first allocated cluster's number here.
690df930be7Sderaadt  * got	      - how many clusters were actually allocated.
691df930be7Sderaadt  */
692df930be7Sderaadt int
chainalloc(struct msdosfsmount * pmp,uint32_t start,uint32_t count,uint32_t fillwith,uint32_t * retcluster,uint32_t * got)6937d80fe84Sjasper chainalloc(struct msdosfsmount *pmp, uint32_t start, uint32_t count,
6947d80fe84Sjasper     uint32_t fillwith, uint32_t *retcluster, uint32_t *got)
695df930be7Sderaadt {
696df930be7Sderaadt 	int error;
69782fa9538Stedu 	uint32_t cl, n;
698df930be7Sderaadt 
699b099d67bSprovos 	for (cl = start, n = count; n-- > 0;)
700b099d67bSprovos 		usemap_alloc(pmp, cl++);
701879b3eabSniklas 	if ((error = fatchain(pmp, start, count, fillwith)) != 0)
702df930be7Sderaadt 		return (error);
703df930be7Sderaadt #ifdef MSDOSFS_DEBUG
704df930be7Sderaadt 	printf("clusteralloc(): allocated cluster chain at %d (%d clusters)\n",
705df930be7Sderaadt 	    start, count);
706df930be7Sderaadt #endif
707df930be7Sderaadt 	if (retcluster)
708df930be7Sderaadt 		*retcluster = start;
709df930be7Sderaadt 	if (got)
710df930be7Sderaadt 		*got = count;
711df930be7Sderaadt 	return (0);
712df930be7Sderaadt }
713df930be7Sderaadt 
714df930be7Sderaadt /*
715df930be7Sderaadt  * Allocate contiguous free clusters.
716df930be7Sderaadt  *
717df930be7Sderaadt  * pmp	      - mount point.
718df930be7Sderaadt  * start      - preferred start of cluster chain.
719df930be7Sderaadt  * count      - number of clusters requested.
720df930be7Sderaadt  * fillwith   - put this value into the fat entry for the
721df930be7Sderaadt  *		last allocated cluster.
722df930be7Sderaadt  * retcluster - put the first allocated cluster's number here.
723df930be7Sderaadt  * got	      - how many clusters were actually allocated.
724df930be7Sderaadt  */
725df930be7Sderaadt int
clusteralloc(struct msdosfsmount * pmp,uint32_t start,uint32_t count,uint32_t * retcluster,uint32_t * got)7267d80fe84Sjasper clusteralloc(struct msdosfsmount *pmp, uint32_t start, uint32_t count,
727d3889036Ssf     uint32_t *retcluster, uint32_t *got)
728df930be7Sderaadt {
72982fa9538Stedu 	uint32_t idx;
73082fa9538Stedu 	uint32_t len, newst, foundl, cn, l;
73182fa9538Stedu 	uint32_t foundcn = 0; /* XXX: foundcn could be used uninitialized */
732d3889036Ssf 	uint32_t fillwith = CLUST_EOFE;
733df930be7Sderaadt 	u_int map;
734df930be7Sderaadt 
735df930be7Sderaadt #ifdef MSDOSFS_DEBUG
736df930be7Sderaadt 	printf("clusteralloc(): find %d clusters\n",count);
737df930be7Sderaadt #endif
738df930be7Sderaadt 	if (start) {
739df930be7Sderaadt 		if ((len = chainlength(pmp, start, count)) >= count)
740df930be7Sderaadt 			return (chainalloc(pmp, start, count, fillwith, retcluster, got));
741df930be7Sderaadt 	} else {
742df930be7Sderaadt 		/*
743df930be7Sderaadt 		 * This is a new file, initialize start
744df930be7Sderaadt 		 */
745df930be7Sderaadt 		struct timeval tv;
746df930be7Sderaadt 
747df930be7Sderaadt 		microtime(&tv);
748df930be7Sderaadt 		start = (tv.tv_usec >> 10) | tv.tv_usec;
749df930be7Sderaadt 		len = 0;
750df930be7Sderaadt 	}
751df930be7Sderaadt 
752df930be7Sderaadt 	/*
753df930be7Sderaadt 	 * Start at a (pseudo) random place to maximize cluster runs
754df930be7Sderaadt 	 * under multiple writers.
755df930be7Sderaadt 	 */
756df930be7Sderaadt 	newst = (start * 1103515245 + 12345) % (pmp->pm_maxcluster + 1);
757df930be7Sderaadt 	foundl = 0;
758df930be7Sderaadt 
759df930be7Sderaadt 	for (cn = newst; cn <= pmp->pm_maxcluster;) {
760df930be7Sderaadt 		idx = cn / N_INUSEBITS;
761df930be7Sderaadt 		map = pmp->pm_inusemap[idx];
762*96287341Ssf 		map |= (1U << (cn % N_INUSEBITS)) - 1;
763df930be7Sderaadt 		if (map != (u_int)-1) {
764df930be7Sderaadt 			cn = idx * N_INUSEBITS + ffs(map^(u_int)-1) - 1;
765df930be7Sderaadt 			if ((l = chainlength(pmp, cn, count)) >= count)
766df930be7Sderaadt 				return (chainalloc(pmp, cn, count, fillwith, retcluster, got));
767df930be7Sderaadt 			if (l > foundl) {
768df930be7Sderaadt 				foundcn = cn;
769df930be7Sderaadt 				foundl = l;
770df930be7Sderaadt 			}
771df930be7Sderaadt 			cn += l + 1;
772df930be7Sderaadt 			continue;
773df930be7Sderaadt 		}
774df930be7Sderaadt 		cn += N_INUSEBITS - cn % N_INUSEBITS;
775df930be7Sderaadt 	}
776df930be7Sderaadt 	for (cn = 0; cn < newst;) {
777df930be7Sderaadt 		idx = cn / N_INUSEBITS;
778df930be7Sderaadt 		map = pmp->pm_inusemap[idx];
779*96287341Ssf 		map |= (1U << (cn % N_INUSEBITS)) - 1;
780df930be7Sderaadt 		if (map != (u_int)-1) {
781df930be7Sderaadt 			cn = idx * N_INUSEBITS + ffs(map^(u_int)-1) - 1;
782df930be7Sderaadt 			if ((l = chainlength(pmp, cn, count)) >= count)
783df930be7Sderaadt 				return (chainalloc(pmp, cn, count, fillwith, retcluster, got));
784df930be7Sderaadt 			if (l > foundl) {
785df930be7Sderaadt 				foundcn = cn;
786df930be7Sderaadt 				foundl = l;
787df930be7Sderaadt 			}
788df930be7Sderaadt 			cn += l + 1;
789df930be7Sderaadt 			continue;
790df930be7Sderaadt 		}
791df930be7Sderaadt 		cn += N_INUSEBITS - cn % N_INUSEBITS;
792df930be7Sderaadt 	}
793df930be7Sderaadt 
794df930be7Sderaadt 	if (!foundl)
795df930be7Sderaadt 		return (ENOSPC);
796df930be7Sderaadt 
797df930be7Sderaadt 	if (len)
798df930be7Sderaadt 		return (chainalloc(pmp, start, len, fillwith, retcluster, got));
799df930be7Sderaadt 	else
800df930be7Sderaadt 		return (chainalloc(pmp, foundcn, foundl, fillwith, retcluster, got));
801df930be7Sderaadt }
802df930be7Sderaadt 
803df930be7Sderaadt 
804df930be7Sderaadt /*
805df930be7Sderaadt  * Free a chain of clusters.
806df930be7Sderaadt  *
807df930be7Sderaadt  * pmp		- address of the msdosfs mount structure for the filesystem
808df930be7Sderaadt  *		  containing the cluster chain to be freed.
809df930be7Sderaadt  * startcluster - number of the 1st cluster in the chain of clusters to be
810df930be7Sderaadt  *		  freed.
811df930be7Sderaadt  */
812df930be7Sderaadt int
freeclusterchain(struct msdosfsmount * pmp,uint32_t cluster)8137d80fe84Sjasper freeclusterchain(struct msdosfsmount *pmp, uint32_t cluster)
814df930be7Sderaadt {
815df930be7Sderaadt 	int error;
816df930be7Sderaadt 	struct buf *bp = NULL;
81782fa9538Stedu 	uint32_t bn, bo, bsize, byteoffset;
81882fa9538Stedu 	uint32_t readcn, lbn = -1;
819df930be7Sderaadt 
820df930be7Sderaadt 	while (cluster >= CLUST_FIRST && cluster <= pmp->pm_maxcluster) {
821df930be7Sderaadt 		byteoffset = FATOFS(pmp, cluster);
822df930be7Sderaadt 		fatblock(pmp, byteoffset, &bn, &bsize, &bo);
823df930be7Sderaadt 		if (lbn != bn) {
824df930be7Sderaadt 			if (bp)
825df930be7Sderaadt 				updatefats(pmp, bp, lbn);
82693f62a9eStedu 			error = bread(pmp->pm_devvp, bn, bsize, &bp);
827879b3eabSniklas 			if (error) {
82816bf7bd1Sderaadt 				brelse(bp);
829df930be7Sderaadt 				return (error);
83016bf7bd1Sderaadt 			}
831df930be7Sderaadt 			lbn = bn;
832df930be7Sderaadt 		}
833df930be7Sderaadt 		usemap_free(pmp, cluster);
834b099d67bSprovos 		switch (pmp->pm_fatmask) {
835b099d67bSprovos 		case FAT12_MASK:
836df930be7Sderaadt 			readcn = getushort(&bp->b_data[bo]);
837df930be7Sderaadt 			if (cluster & 1) {
838df930be7Sderaadt 				cluster = readcn >> 4;
839df930be7Sderaadt 				readcn &= 0x000f;
840df930be7Sderaadt 				readcn |= MSDOSFSFREE << 4;
841df930be7Sderaadt 			} else {
842df930be7Sderaadt 				cluster = readcn;
843df930be7Sderaadt 				readcn &= 0xf000;
844df930be7Sderaadt 				readcn |= MSDOSFSFREE & 0xfff;
845df930be7Sderaadt 			}
846df930be7Sderaadt 			putushort(&bp->b_data[bo], readcn);
847b099d67bSprovos 			break;
848b099d67bSprovos 		case FAT16_MASK:
849b099d67bSprovos 			cluster = getushort(&bp->b_data[bo]);
850df930be7Sderaadt 			putushort(&bp->b_data[bo], MSDOSFSFREE);
851b099d67bSprovos 			break;
852b099d67bSprovos 		case FAT32_MASK:
853b099d67bSprovos 			cluster = getulong(&bp->b_data[bo]);
854b099d67bSprovos 			putulong(&bp->b_data[bo],
855b099d67bSprovos 				 (MSDOSFSFREE & FAT32_MASK) | (cluster & ~FAT32_MASK));
856b099d67bSprovos 			break;
857df930be7Sderaadt 		}
858b099d67bSprovos 		cluster &= pmp->pm_fatmask;
859b099d67bSprovos 		if ((cluster | ~pmp->pm_fatmask) >= CLUST_RSRVD)
860b099d67bSprovos 			cluster |= pmp->pm_fatmask;
861df930be7Sderaadt 	}
862df930be7Sderaadt 	if (bp)
863df930be7Sderaadt 		updatefats(pmp, bp, bn);
864df930be7Sderaadt 	return (0);
865df930be7Sderaadt }
866df930be7Sderaadt 
867df930be7Sderaadt /*
868df930be7Sderaadt  * Read in fat blocks looking for free clusters. For every free cluster
869df930be7Sderaadt  * found turn off its corresponding bit in the pm_inusemap.
870df930be7Sderaadt  */
871df930be7Sderaadt int
fillinusemap(struct msdosfsmount * pmp)8727d80fe84Sjasper fillinusemap(struct msdosfsmount *pmp)
873df930be7Sderaadt {
874df930be7Sderaadt 	struct buf *bp = NULL;
87582fa9538Stedu 	uint32_t cn, readcn;
876df930be7Sderaadt 	int error;
87782fa9538Stedu 	uint32_t bn, bo, bsize, byteoffset;
878df930be7Sderaadt 
879df930be7Sderaadt 	/*
880df930be7Sderaadt 	 * Mark all clusters in use, we mark the free ones in the fat scan
881df930be7Sderaadt 	 * loop further down.
882df930be7Sderaadt 	 */
8832a90c622Stobias 	for (cn = 0; cn < howmany(pmp->pm_maxcluster + 1, N_INUSEBITS); cn++)
884df930be7Sderaadt 		pmp->pm_inusemap[cn] = (u_int)-1;
885df930be7Sderaadt 
886df930be7Sderaadt 	/*
887df930be7Sderaadt 	 * Figure how many free clusters are in the filesystem by ripping
888df930be7Sderaadt 	 * through the fat counting the number of entries whose content is
889df930be7Sderaadt 	 * zero.  These represent free clusters.
890df930be7Sderaadt 	 */
891df930be7Sderaadt 	pmp->pm_freeclustercount = 0;
892df930be7Sderaadt 	for (cn = CLUST_FIRST; cn <= pmp->pm_maxcluster; cn++) {
893df930be7Sderaadt 		byteoffset = FATOFS(pmp, cn);
894df930be7Sderaadt 		bo = byteoffset % pmp->pm_fatblocksize;
895df930be7Sderaadt 		if (!bo || !bp) {
896df930be7Sderaadt 			/* Read new FAT block */
897df930be7Sderaadt 			if (bp)
898df930be7Sderaadt 				brelse(bp);
899df930be7Sderaadt 			fatblock(pmp, byteoffset, &bn, &bsize, NULL);
90093f62a9eStedu 			error = bread(pmp->pm_devvp, bn, bsize, &bp);
901879b3eabSniklas 			if (error) {
90216bf7bd1Sderaadt 				brelse(bp);
903df930be7Sderaadt 				return (error);
904df930be7Sderaadt 			}
90516bf7bd1Sderaadt 		}
906b099d67bSprovos 		if (FAT32(pmp))
907b099d67bSprovos 			readcn = getulong(&bp->b_data[bo]);
908b099d67bSprovos 		else
909df930be7Sderaadt 			readcn = getushort(&bp->b_data[bo]);
910b099d67bSprovos 		if (FAT12(pmp) && (cn & 1))
911df930be7Sderaadt 			readcn >>= 4;
912b099d67bSprovos 		readcn &= pmp->pm_fatmask;
913df930be7Sderaadt 
914df930be7Sderaadt 		if (readcn == 0)
915df930be7Sderaadt 			usemap_free(pmp, cn);
916df930be7Sderaadt 	}
917df930be7Sderaadt 	brelse(bp);
918df930be7Sderaadt 	return (0);
919df930be7Sderaadt }
920df930be7Sderaadt 
921df930be7Sderaadt /*
922df930be7Sderaadt  * Allocate a new cluster and chain it onto the end of the file.
923df930be7Sderaadt  *
924df930be7Sderaadt  * dep	 - the file to extend
925df930be7Sderaadt  * count - number of clusters to allocate
926df930be7Sderaadt  * bpp	 - where to return the address of the buf header for the first new
927df930be7Sderaadt  *	   file block
928df930be7Sderaadt  * ncp	 - where to put cluster number of the first newly allocated cluster
929df930be7Sderaadt  *	   If this pointer is 0, do not return the cluster number.
930df930be7Sderaadt  * flags - see fat.h
931df930be7Sderaadt  *
932df930be7Sderaadt  * NOTE: This function is not responsible for turning on the DE_UPDATE bit of
933df930be7Sderaadt  * the de_flag field of the denode and it does not change the de_FileSize
934df930be7Sderaadt  * field.  This is left for the caller to do.
935df930be7Sderaadt  */
936df930be7Sderaadt int
extendfile(struct denode * dep,uint32_t count,struct buf ** bpp,uint32_t * ncp,int flags)9377d80fe84Sjasper extendfile(struct denode *dep, uint32_t count, struct buf **bpp, uint32_t *ncp,
9387d80fe84Sjasper     int flags)
939df930be7Sderaadt {
940df930be7Sderaadt 	int error;
94182fa9538Stedu 	uint32_t frcn;
94282fa9538Stedu 	uint32_t cn, got;
943df930be7Sderaadt 	struct msdosfsmount *pmp = dep->de_pmp;
944df930be7Sderaadt 	struct buf *bp;
945df930be7Sderaadt 
946df930be7Sderaadt 	/*
947df930be7Sderaadt 	 * Don't try to extend the root directory
948df930be7Sderaadt 	 */
949b099d67bSprovos 	if (dep->de_StartCluster == MSDOSFSROOT
950b099d67bSprovos 	    && (dep->de_Attributes & ATTR_DIRECTORY)) {
951df930be7Sderaadt 		printf("extendfile(): attempt to extend root directory\n");
952df930be7Sderaadt 		return (ENOSPC);
953df930be7Sderaadt 	}
954df930be7Sderaadt 
955df930be7Sderaadt 	/*
956df930be7Sderaadt 	 * If the "file's last cluster" cache entry is empty, and the file
957df930be7Sderaadt 	 * is not empty, then fill the cache entry by calling pcbmap().
958df930be7Sderaadt 	 */
959df930be7Sderaadt 	fc_fileextends++;
960df930be7Sderaadt 	if (dep->de_fc[FC_LASTFC].fc_frcn == FCE_EMPTY &&
961df930be7Sderaadt 	    dep->de_StartCluster != 0) {
962df930be7Sderaadt 		fc_lfcempty++;
963d3889036Ssf 		error = pcbmap(dep, CLUST_END, 0, &cn, 0);
964df930be7Sderaadt 		/* we expect it to return E2BIG */
965df930be7Sderaadt 		if (error != E2BIG)
966df930be7Sderaadt 			return (error);
967df930be7Sderaadt 	}
968df930be7Sderaadt 
96951bdbb7dSmikeb 	/*
97051bdbb7dSmikeb 	 * Preserve value for the last cluster before extending the file
97151bdbb7dSmikeb 	 * to speed up further lookups.
97251bdbb7dSmikeb 	 */
97351bdbb7dSmikeb 	fc_setcache(dep, FC_OLASTFC, dep->de_fc[FC_LASTFC].fc_frcn,
97451bdbb7dSmikeb 	    dep->de_fc[FC_LASTFC].fc_fsrcn);
97551bdbb7dSmikeb 
976df930be7Sderaadt 	while (count > 0) {
977df930be7Sderaadt 		/*
978879b3eabSniklas 		 * Allocate a new cluster chain and cat onto the end of the
979879b3eabSniklas 		 * file.  * If the file is empty we make de_StartCluster point
980879b3eabSniklas 		 * to the new block.  Note that de_StartCluster being 0 is
981879b3eabSniklas 		 * sufficient to be sure the file is empty since we exclude
982879b3eabSniklas 		 * attempts to extend the root directory above, and the root
983879b3eabSniklas 		 * dir is the only file with a startcluster of 0 that has
984879b3eabSniklas 		 * blocks allocated (sort of).
985df930be7Sderaadt 		 */
986df930be7Sderaadt 		if (dep->de_StartCluster == 0)
987df930be7Sderaadt 			cn = 0;
988df930be7Sderaadt 		else
989df930be7Sderaadt 			cn = dep->de_fc[FC_LASTFC].fc_fsrcn + 1;
990d3889036Ssf 		error = clusteralloc(pmp, cn, count, &cn, &got);
991879b3eabSniklas 		if (error)
992df930be7Sderaadt 			return (error);
993df930be7Sderaadt 
994df930be7Sderaadt 		count -= got;
995df930be7Sderaadt 
996df930be7Sderaadt 		/*
997df930be7Sderaadt 		 * Give them the filesystem relative cluster number if they want
998df930be7Sderaadt 		 * it.
999df930be7Sderaadt 		 */
1000df930be7Sderaadt 		if (ncp) {
1001df930be7Sderaadt 			*ncp = cn;
1002df930be7Sderaadt 			ncp = NULL;
1003df930be7Sderaadt 		}
1004df930be7Sderaadt 
1005df930be7Sderaadt 		if (dep->de_StartCluster == 0) {
1006df930be7Sderaadt 			dep->de_StartCluster = cn;
1007df930be7Sderaadt 			frcn = 0;
1008df930be7Sderaadt 		} else {
1009879b3eabSniklas 			error = fatentry(FAT_SET, pmp,
1010df930be7Sderaadt 					 dep->de_fc[FC_LASTFC].fc_fsrcn,
1011879b3eabSniklas 					 0, cn);
1012879b3eabSniklas 			if (error) {
1013df930be7Sderaadt 				clusterfree(pmp, cn, NULL);
1014df930be7Sderaadt 				return (error);
1015df930be7Sderaadt 			}
1016df930be7Sderaadt 			frcn = dep->de_fc[FC_LASTFC].fc_frcn + 1;
1017df930be7Sderaadt 		}
1018df930be7Sderaadt 
1019df930be7Sderaadt 		/*
10201414b0faSart 		 * Update the "last cluster of the file" entry in the denode's fat
10211414b0faSart 		 * cache.
1022df930be7Sderaadt 		 */
10235af79db2Sart 		fc_setcache(dep, FC_LASTFC, frcn + got - 1, cn + got - 1);
10241414b0faSart 
10251414b0faSart 		if (flags & DE_CLEAR) {
1026df930be7Sderaadt 			while (got-- > 0) {
10271414b0faSart 				/*
10281414b0faSart 				 * Get the buf header for the new block of the file.
10291414b0faSart 				 */
10301414b0faSart 				if (dep->de_Attributes & ATTR_DIRECTORY)
1031df930be7Sderaadt 					bp = getblk(pmp->pm_devvp, cntobn(pmp, cn++),
1032570df5c4Scheloha 					    pmp->pm_bpcluster, 0, INFSLP);
10331414b0faSart 				else {
103490aa86e4Smpi 					bp = getblk(DETOV(dep), frcn++,
1035570df5c4Scheloha 					    pmp->pm_bpcluster, 0, INFSLP);
10361414b0faSart 					/*
10371414b0faSart 					 * Do the bmap now, as in msdosfs_write
10381414b0faSart 					 */
103990aa86e4Smpi 					if (pcbmap(dep, bp->b_lblkno, &bp->b_blkno, 0, 0))
10401414b0faSart 						bp->b_blkno = -1;
10411414b0faSart 					if (bp->b_blkno == -1)
10421414b0faSart 						panic("extendfile: pcbmap");
10431414b0faSart 				}
1044df930be7Sderaadt 				clrbuf(bp);
1045df930be7Sderaadt 				if (bpp) {
1046df930be7Sderaadt 					*bpp = bp;
1047df930be7Sderaadt 					bpp = NULL;
10481414b0faSart 				} else
1049df930be7Sderaadt 					bdwrite(bp);
1050df930be7Sderaadt 			}
1051df930be7Sderaadt 		}
1052df930be7Sderaadt 	}
1053df930be7Sderaadt 
1054df930be7Sderaadt 	return (0);
1055df930be7Sderaadt }
1056