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