1 |
|
|
/* $OpenBSD: msdosfs_fat.c,v 1.4 2016/12/17 16:43:30 krw Exp $ */ |
2 |
|
|
/* $NetBSD: msdosfs_fat.c,v 1.31 2016/05/07 16:43:02 mlelstv Exp $ */ |
3 |
|
|
|
4 |
|
|
/*- |
5 |
|
|
* Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank. |
6 |
|
|
* Copyright (C) 1994, 1995, 1997 TooLs GmbH. |
7 |
|
|
* All rights reserved. |
8 |
|
|
* Original code by Paul Popelka (paulp@uts.amdahl.com) (see below). |
9 |
|
|
* |
10 |
|
|
* Redistribution and use in source and binary forms, with or without |
11 |
|
|
* modification, are permitted provided that the following conditions |
12 |
|
|
* are met: |
13 |
|
|
* 1. Redistributions of source code must retain the above copyright |
14 |
|
|
* notice, this list of conditions and the following disclaimer. |
15 |
|
|
* 2. Redistributions in binary form must reproduce the above copyright |
16 |
|
|
* notice, this list of conditions and the following disclaimer in the |
17 |
|
|
* documentation and/or other materials provided with the distribution. |
18 |
|
|
* 3. All advertising materials mentioning features or use of this software |
19 |
|
|
* must display the following acknowledgement: |
20 |
|
|
* This product includes software developed by TooLs GmbH. |
21 |
|
|
* 4. The name of TooLs GmbH may not be used to endorse or promote products |
22 |
|
|
* derived from this software without specific prior written permission. |
23 |
|
|
* |
24 |
|
|
* THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR |
25 |
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
26 |
|
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
27 |
|
|
* IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
28 |
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
29 |
|
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; |
30 |
|
|
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, |
31 |
|
|
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR |
32 |
|
|
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF |
33 |
|
|
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
34 |
|
|
*/ |
35 |
|
|
/* |
36 |
|
|
* Written by Paul Popelka (paulp@uts.amdahl.com) |
37 |
|
|
* |
38 |
|
|
* You can do anything you want with this software, just don't say you wrote |
39 |
|
|
* it, and don't remove this notice. |
40 |
|
|
* |
41 |
|
|
* This software is provided "as is". |
42 |
|
|
* |
43 |
|
|
* The author supplies this software to be publicly redistributed on the |
44 |
|
|
* understanding that the author is not responsible for the correct |
45 |
|
|
* functioning of this software in any circumstances and is not liable for |
46 |
|
|
* any damages caused by this software. |
47 |
|
|
* |
48 |
|
|
* October 1992 |
49 |
|
|
*/ |
50 |
|
|
|
51 |
|
|
/* |
52 |
|
|
* kernel include files. |
53 |
|
|
*/ |
54 |
|
|
#include <sys/param.h> |
55 |
|
|
#include <sys/file.h> |
56 |
|
|
|
57 |
|
|
#include "ffs/buf.h" |
58 |
|
|
|
59 |
|
|
/* |
60 |
|
|
* msdosfs include files. |
61 |
|
|
*/ |
62 |
|
|
#include <msdosfs/bpb.h> |
63 |
|
|
#include "msdos/msdosfsmount.h" |
64 |
|
|
#include "msdos/direntry.h" |
65 |
|
|
#include "msdos/denode.h" |
66 |
|
|
#include "msdos/fat.h" |
67 |
|
|
|
68 |
|
|
/* |
69 |
|
|
* Fat cache stats. |
70 |
|
|
*/ |
71 |
|
|
int fc_fileextends; /* # of file extends */ |
72 |
|
|
int fc_lfcempty; /* # of time last file cluster cache entry |
73 |
|
|
* was empty */ |
74 |
|
|
int fc_bmapcalls; /* # of times pcbmap was called */ |
75 |
|
|
|
76 |
|
|
#define LMMAX 20 |
77 |
|
|
int fc_lmdistance[LMMAX]; /* counters for how far off the last |
78 |
|
|
* cluster mapped entry was. */ |
79 |
|
|
int fc_largedistance; /* off by more than LMMAX */ |
80 |
|
|
int fc_wherefrom, fc_whereto, fc_lastclust; |
81 |
|
|
int pm_fatblocksize; |
82 |
|
|
|
83 |
|
|
#ifdef MSDOSFS_DEBUG |
84 |
|
|
#define DPRINTF(a) printf a |
85 |
|
|
#else |
86 |
|
|
#define DPRINTF(a) |
87 |
|
|
#endif |
88 |
|
|
|
89 |
|
|
static void fatblock(struct msdosfsmount *, u_long, u_long *, u_long *, |
90 |
|
|
u_long *); |
91 |
|
|
void updatefats(struct msdosfsmount *, struct mkfsbuf *, u_long); |
92 |
|
|
static inline void usemap_free(struct msdosfsmount *, u_long); |
93 |
|
|
static inline void usemap_alloc(struct msdosfsmount *, u_long); |
94 |
|
|
static int fatchain(struct msdosfsmount *, u_long, u_long, u_long); |
95 |
|
|
int chainlength(struct msdosfsmount *, u_long, u_long); |
96 |
|
|
int chainalloc(struct msdosfsmount *, u_long, u_long, u_long, u_long *, |
97 |
|
|
u_long *); |
98 |
|
|
|
99 |
|
|
static void |
100 |
|
|
fatblock(struct msdosfsmount *pmp, u_long ofs, u_long *bnp, u_long *sizep, u_long *bop) |
101 |
|
|
{ |
102 |
|
|
u_long bn, size; |
103 |
|
|
|
104 |
|
|
bn = ofs / pmp->pm_fatblocksize * pmp->pm_fatblocksec; |
105 |
|
|
size = min(pmp->pm_fatblocksec, pmp->pm_FATsecs - bn) |
106 |
|
|
* pmp->pm_BytesPerSec; |
107 |
|
|
bn += pmp->pm_fatblk + pmp->pm_curfat * pmp->pm_FATsecs; |
108 |
|
|
|
109 |
|
|
DPRINTF(("%s(ofs=%lu bn=%lu, size=%lu, bo=%lu)\n", __func__, ofs, bn, |
110 |
|
|
size, ofs % pmp->pm_fatblocksize)); |
111 |
|
|
if (bnp) |
112 |
|
|
*bnp = bn; |
113 |
|
|
if (sizep) |
114 |
|
|
*sizep = size; |
115 |
|
|
if (bop) |
116 |
|
|
*bop = ofs % pmp->pm_fatblocksize; |
117 |
|
|
|
118 |
|
|
pm_fatblocksize = pmp->pm_fatblocksize; |
119 |
|
|
} |
120 |
|
|
|
121 |
|
|
/* |
122 |
|
|
* Map the logical cluster number of a file into a physical disk sector |
123 |
|
|
* that is filesystem relative. |
124 |
|
|
* |
125 |
|
|
* dep - address of denode representing the file of interest |
126 |
|
|
* findcn - file relative cluster whose filesystem relative cluster number |
127 |
|
|
* and/or block number are/is to be found |
128 |
|
|
* bnp - address of where to place the file system relative block number. |
129 |
|
|
* If this pointer is null then don't return this quantity. |
130 |
|
|
* cnp - address of where to place the file system relative cluster number. |
131 |
|
|
* If this pointer is null then don't return this quantity. |
132 |
|
|
* |
133 |
|
|
* NOTE: Either bnp or cnp must be non-null. |
134 |
|
|
* This function has one side effect. If the requested file relative cluster |
135 |
|
|
* is beyond the end of file, then the actual number of clusters in the file |
136 |
|
|
* is returned in *cnp. This is useful for determining how long a directory is. |
137 |
|
|
* If cnp is null, nothing is returned. |
138 |
|
|
*/ |
139 |
|
|
int |
140 |
|
|
pcbmap(struct denode *dep, u_long findcn, daddr_t *bnp, u_long *cnp, int *sp) |
141 |
|
|
/* findcn: file relative cluster to get */ |
142 |
|
|
/* bnp: returned filesys rel sector number */ |
143 |
|
|
/* cnp: returned cluster number */ |
144 |
|
|
/* sp: returned block size */ |
145 |
|
|
{ |
146 |
|
|
int error; |
147 |
|
|
u_long i; |
148 |
|
|
u_long cn; |
149 |
|
|
u_long prevcn = 0; /* XXX: prevcn could be used unititialized */ |
150 |
|
|
u_long byteoffset; |
151 |
|
|
u_long bn; |
152 |
|
|
u_long bo; |
153 |
|
|
struct mkfsbuf *bp = NULL; |
154 |
|
|
u_long bp_bn = -1; |
155 |
|
|
struct msdosfsmount *pmp = dep->de_pmp; |
156 |
|
|
u_long bsize; |
157 |
|
|
|
158 |
|
|
fc_bmapcalls++; |
159 |
|
|
|
160 |
|
|
/* |
161 |
|
|
* If they don't give us someplace to return a value then don't |
162 |
|
|
* bother doing anything. |
163 |
|
|
*/ |
164 |
|
|
if (bnp == NULL && cnp == NULL && sp == NULL) |
165 |
|
|
return (0); |
166 |
|
|
|
167 |
|
|
cn = dep->de_StartCluster; |
168 |
|
|
DPRINTF(("%s(start cluster=%lu)\n", __func__, cn)); |
169 |
|
|
/* |
170 |
|
|
* The "file" that makes up the root directory is contiguous, |
171 |
|
|
* permanently allocated, of fixed size, and is not made up of |
172 |
|
|
* clusters. If the cluster number is beyond the end of the root |
173 |
|
|
* directory, then return the number of clusters in the file. |
174 |
|
|
*/ |
175 |
|
|
if (cn == MSDOSFSROOT) { |
176 |
|
|
if (dep->de_Attributes & ATTR_DIRECTORY) { |
177 |
|
|
if (de_cn2off(pmp, findcn) >= dep->de_FileSize) { |
178 |
|
|
if (cnp) |
179 |
|
|
*cnp = de_bn2cn(pmp, pmp->pm_rootdirsize); |
180 |
|
|
DPRINTF(("%s(root, %lu ETOOBIG)\n", __func__, |
181 |
|
|
de_cn2off(pmp, findcn))); |
182 |
|
|
return (E2BIG); |
183 |
|
|
} |
184 |
|
|
if (bnp) |
185 |
|
|
*bnp = pmp->pm_rootdirblk + de_cn2bn(pmp, findcn); |
186 |
|
|
if (cnp) |
187 |
|
|
*cnp = MSDOSFSROOT; |
188 |
|
|
if (sp) |
189 |
|
|
*sp = min(pmp->pm_bpcluster, |
190 |
|
|
dep->de_FileSize - de_cn2off(pmp, findcn)); |
191 |
|
|
DPRINTF(("%s(root, bn=%lu, cn=%u)\n", __func__, |
192 |
|
|
pmp->pm_rootdirblk + de_cn2bn(pmp, findcn), |
193 |
|
|
MSDOSFSROOT)); |
194 |
|
|
return (0); |
195 |
|
|
} else { /* just an empty file */ |
196 |
|
|
if (cnp) |
197 |
|
|
*cnp = 0; |
198 |
|
|
DPRINTF(("%s(root, empty ETOOBIG)\n", __func__)); |
199 |
|
|
return (E2BIG); |
200 |
|
|
} |
201 |
|
|
} |
202 |
|
|
|
203 |
|
|
/* |
204 |
|
|
* All other files do I/O in cluster sized blocks |
205 |
|
|
*/ |
206 |
|
|
if (sp) |
207 |
|
|
*sp = pmp->pm_bpcluster; |
208 |
|
|
|
209 |
|
|
/* |
210 |
|
|
* Rummage around in the FAT cache, maybe we can avoid tromping |
211 |
|
|
* thru every FAT entry for the file. And, keep track of how far |
212 |
|
|
* off the cache was from where we wanted to be. |
213 |
|
|
*/ |
214 |
|
|
i = 0; |
215 |
|
|
fc_lookup(dep, findcn, &i, &cn); |
216 |
|
|
DPRINTF(("%s(bpcluster=%lu i=%lu cn=%lu\n", __func__, pmp->pm_bpcluster, |
217 |
|
|
i, cn)); |
218 |
|
|
if ((bn = findcn - i) >= LMMAX) { |
219 |
|
|
fc_largedistance++; |
220 |
|
|
fc_wherefrom = i; |
221 |
|
|
fc_whereto = findcn; |
222 |
|
|
fc_lastclust = dep->de_fc[FC_LASTFC].fc_frcn; |
223 |
|
|
} else |
224 |
|
|
fc_lmdistance[bn]++; |
225 |
|
|
|
226 |
|
|
/* |
227 |
|
|
* Handle all other files or directories the normal way. |
228 |
|
|
*/ |
229 |
|
|
for (; i < findcn; i++) { |
230 |
|
|
/* |
231 |
|
|
* Stop with all reserved clusters, not just with EOF. |
232 |
|
|
*/ |
233 |
|
|
if (cn >= (CLUST_RSRVD & pmp->pm_fatmask)) |
234 |
|
|
goto hiteof; |
235 |
|
|
|
236 |
|
|
/* |
237 |
|
|
* Also stop when cluster is not in the filesystem |
238 |
|
|
*/ |
239 |
|
|
if (cn < CLUST_FIRST || cn > pmp->pm_maxcluster) { |
240 |
|
|
DPRINTF(("%s(cn, %lu not in %lu..%lu)\n", __func__, |
241 |
|
|
cn, (u_long)CLUST_FIRST, pmp->pm_maxcluster)); |
242 |
|
|
if (bp) |
243 |
|
|
brelse(bp, 0); |
244 |
|
|
return (EINVAL); |
245 |
|
|
} |
246 |
|
|
|
247 |
|
|
byteoffset = FATOFS(pmp, cn); |
248 |
|
|
fatblock(pmp, byteoffset, &bn, &bsize, &bo); |
249 |
|
|
if (bn != bp_bn) { |
250 |
|
|
if (bp) |
251 |
|
|
brelse(bp, 0); |
252 |
|
|
error = bread(pmp->pm_devvp, de_bn2kb(pmp, bn), bsize, |
253 |
|
|
0, &bp); |
254 |
|
|
if (error) { |
255 |
|
|
DPRINTF(("%s(bread, %d)\n", __func__, error)); |
256 |
|
|
return (error); |
257 |
|
|
} |
258 |
|
|
bp_bn = bn; |
259 |
|
|
} |
260 |
|
|
prevcn = cn; |
261 |
|
|
if (bo >= bsize) { |
262 |
|
|
if (bp) |
263 |
|
|
brelse(bp, 0); |
264 |
|
|
DPRINTF(("%s(block, %lu >= %lu)\n", __func__, bo, |
265 |
|
|
bsize)); |
266 |
|
|
return (EIO); |
267 |
|
|
} |
268 |
|
|
KASSERT(bp != NULL); |
269 |
|
|
if (FAT32(pmp)) |
270 |
|
|
cn = getulong((char *)bp->b_data + bo); |
271 |
|
|
else |
272 |
|
|
cn = getushort((char *)bp->b_data + bo); |
273 |
|
|
if (FAT12(pmp) && (prevcn & 1)) |
274 |
|
|
cn >>= 4; |
275 |
|
|
DPRINTF(("%s(cn=%lu masked=%lu)\n", __func__, cn, |
276 |
|
|
cn & pmp->pm_fatmask)); |
277 |
|
|
cn &= pmp->pm_fatmask; |
278 |
|
|
} |
279 |
|
|
|
280 |
|
|
if (!MSDOSFSEOF(cn, pmp->pm_fatmask)) { |
281 |
|
|
if (bp) |
282 |
|
|
brelse(bp, 0); |
283 |
|
|
if (bnp) |
284 |
|
|
*bnp = cntobn(pmp, cn); |
285 |
|
|
if (cnp) |
286 |
|
|
*cnp = cn; |
287 |
|
|
DPRINTF(("%s(bn=%lu, cn=%lu)\n", __func__, cntobn(pmp, cn), |
288 |
|
|
cn)); |
289 |
|
|
fc_setcache(dep, FC_LASTMAP, i, cn); |
290 |
|
|
return (0); |
291 |
|
|
} |
292 |
|
|
|
293 |
|
|
hiteof:; |
294 |
|
|
if (cnp) |
295 |
|
|
*cnp = i; |
296 |
|
|
if (bp) |
297 |
|
|
brelse(bp, 0); |
298 |
|
|
/* update last file cluster entry in the FAT cache */ |
299 |
|
|
fc_setcache(dep, FC_LASTFC, i - 1, prevcn); |
300 |
|
|
DPRINTF(("%s(eof, %lu)\n", __func__, i)); |
301 |
|
|
return (E2BIG); |
302 |
|
|
} |
303 |
|
|
|
304 |
|
|
/* |
305 |
|
|
* Find the closest entry in the FAT cache to the cluster we are looking |
306 |
|
|
* for. |
307 |
|
|
*/ |
308 |
|
|
void |
309 |
|
|
fc_lookup(struct denode *dep, u_long findcn, u_long *frcnp, u_long *fsrcnp) |
310 |
|
|
{ |
311 |
|
|
int i; |
312 |
|
|
u_long cn; |
313 |
|
|
struct fatcache *closest = 0; |
314 |
|
|
|
315 |
|
|
for (i = 0; i < FC_SIZE; i++) { |
316 |
|
|
cn = dep->de_fc[i].fc_frcn; |
317 |
|
|
if (cn != FCE_EMPTY && cn <= findcn) { |
318 |
|
|
if (closest == 0 || cn > closest->fc_frcn) |
319 |
|
|
closest = &dep->de_fc[i]; |
320 |
|
|
} |
321 |
|
|
} |
322 |
|
|
if (closest) { |
323 |
|
|
*frcnp = closest->fc_frcn; |
324 |
|
|
*fsrcnp = closest->fc_fsrcn; |
325 |
|
|
} |
326 |
|
|
} |
327 |
|
|
|
328 |
|
|
/* |
329 |
|
|
* Purge the FAT cache in denode dep of all entries relating to file |
330 |
|
|
* relative cluster frcn and beyond. |
331 |
|
|
*/ |
332 |
|
|
void |
333 |
|
|
fc_purge(struct denode *dep, u_int frcn) |
334 |
|
|
{ |
335 |
|
|
int i; |
336 |
|
|
struct fatcache *fcp; |
337 |
|
|
|
338 |
|
|
fcp = dep->de_fc; |
339 |
|
|
for (i = 0; i < FC_SIZE; i++, fcp++) { |
340 |
|
|
if (fcp->fc_frcn >= frcn) |
341 |
|
|
fcp->fc_frcn = FCE_EMPTY; |
342 |
|
|
} |
343 |
|
|
} |
344 |
|
|
|
345 |
|
|
/* |
346 |
|
|
* Update the FAT. |
347 |
|
|
* If mirroring the FAT, update all copies, with the first copy as last. |
348 |
|
|
* Else update only the current FAT (ignoring the others). |
349 |
|
|
* |
350 |
|
|
* pmp - msdosfsmount structure for filesystem to update |
351 |
|
|
* bp - addr of modified FAT block |
352 |
|
|
* fatbn - block number relative to begin of filesystem of the modified FAT block. |
353 |
|
|
*/ |
354 |
|
|
void |
355 |
|
|
updatefats(struct msdosfsmount *pmp, struct mkfsbuf *bp, u_long fatbn) |
356 |
|
|
{ |
357 |
|
|
int i, error; |
358 |
|
|
struct mkfsbuf *bpn; |
359 |
|
|
|
360 |
|
|
DPRINTF(("%s(pmp %p, bp %p, fatbn %lu)\n", __func__, pmp, bp, fatbn)); |
361 |
|
|
|
362 |
|
|
/* |
363 |
|
|
* If we have an FSInfo block, update it. |
364 |
|
|
*/ |
365 |
|
|
if (pmp->pm_fsinfo) { |
366 |
|
|
u_long cn = pmp->pm_nxtfree; |
367 |
|
|
|
368 |
|
|
if (pmp->pm_freeclustercount |
369 |
|
|
&& (pmp->pm_inusemap[cn / N_INUSEBITS] |
370 |
|
|
& (1 << (cn % N_INUSEBITS)))) { |
371 |
|
|
/* |
372 |
|
|
* The cluster indicated in FSInfo isn't free |
373 |
|
|
* any longer. Got get a new free one. |
374 |
|
|
*/ |
375 |
|
|
for (cn = 0; cn < pmp->pm_maxcluster; cn++) |
376 |
|
|
if (pmp->pm_inusemap[cn / N_INUSEBITS] != (u_int)-1) |
377 |
|
|
break; |
378 |
|
|
pmp->pm_nxtfree = cn |
379 |
|
|
+ ffs(pmp->pm_inusemap[cn / N_INUSEBITS] |
380 |
|
|
^ (u_int)-1) - 1; |
381 |
|
|
} |
382 |
|
|
/* |
383 |
|
|
* XXX If the fsinfo block is stored on media with |
384 |
|
|
* 2KB or larger sectors, is the fsinfo structure |
385 |
|
|
* padded at the end or in the middle? |
386 |
|
|
*/ |
387 |
|
|
if (bread(pmp->pm_devvp, de_bn2kb(pmp, pmp->pm_fsinfo), |
388 |
|
|
pmp->pm_BytesPerSec, B_MODIFY, &bpn) != 0) { |
389 |
|
|
/* |
390 |
|
|
* Ignore the error, but turn off FSInfo update for the future. |
391 |
|
|
*/ |
392 |
|
|
pmp->pm_fsinfo = 0; |
393 |
|
|
} else { |
394 |
|
|
struct fsinfo *fp = (struct fsinfo *)bpn->b_data; |
395 |
|
|
|
396 |
|
|
putulong(fp->fsinfree, pmp->pm_freeclustercount); |
397 |
|
|
putulong(fp->fsinxtfree, pmp->pm_nxtfree); |
398 |
|
|
if (pmp->pm_flags & MSDOSFSMNT_WAITONFAT) |
399 |
|
|
bwrite(bpn); |
400 |
|
|
else |
401 |
|
|
bdwrite(bpn); |
402 |
|
|
} |
403 |
|
|
} |
404 |
|
|
|
405 |
|
|
if (pmp->pm_flags & MSDOSFS_FATMIRROR) { |
406 |
|
|
/* |
407 |
|
|
* Now copy the block(s) of the modified FAT to the other copies of |
408 |
|
|
* the FAT and write them out. This is faster than reading in the |
409 |
|
|
* other FATs and then writing them back out. This could tie up |
410 |
|
|
* the FAT for quite a while. Preventing others from accessing it. |
411 |
|
|
* To prevent us from going after the FAT quite so much we use |
412 |
|
|
* delayed writes, unless they specified "synchronous" when the |
413 |
|
|
* filesystem was mounted. If synch is asked for then use |
414 |
|
|
* bwrite()'s and really slow things down. |
415 |
|
|
*/ |
416 |
|
|
for (i = 1; i < pmp->pm_FATs; i++) { |
417 |
|
|
fatbn += pmp->pm_FATsecs; |
418 |
|
|
/* getblk() never fails */ |
419 |
|
|
bpn = getblk(pmp->pm_devvp, de_bn2kb(pmp, fatbn), |
420 |
|
|
bp->b_bcount, 0, 0); |
421 |
|
|
memcpy(bpn->b_data, bp->b_data, bp->b_bcount); |
422 |
|
|
if (pmp->pm_flags & MSDOSFSMNT_WAITONFAT) { |
423 |
|
|
error = bwrite(bpn); |
424 |
|
|
if (error) |
425 |
|
|
printf("%s: copy FAT %d (error=%d)\n", |
426 |
|
|
__func__, i, error); |
427 |
|
|
} else |
428 |
|
|
bdwrite(bpn); |
429 |
|
|
} |
430 |
|
|
} |
431 |
|
|
|
432 |
|
|
/* |
433 |
|
|
* Write out the first (or current) FAT last. |
434 |
|
|
*/ |
435 |
|
|
if (pmp->pm_flags & MSDOSFSMNT_WAITONFAT) { |
436 |
|
|
error = bwrite(bp); |
437 |
|
|
if (error) |
438 |
|
|
printf("%s: write FAT (error=%d)\n", |
439 |
|
|
__func__, error); |
440 |
|
|
} else |
441 |
|
|
bdwrite(bp); |
442 |
|
|
/* |
443 |
|
|
* Maybe update fsinfo sector here? |
444 |
|
|
*/ |
445 |
|
|
} |
446 |
|
|
|
447 |
|
|
/* |
448 |
|
|
* Updating entries in 12 bit FATs is a pain in the butt. |
449 |
|
|
* |
450 |
|
|
* The following picture shows where nibbles go when moving from a 12 bit |
451 |
|
|
* cluster number into the appropriate bytes in the FAT. |
452 |
|
|
* |
453 |
|
|
* byte m byte m+1 byte m+2 |
454 |
|
|
* +----+----+ +----+----+ +----+----+ |
455 |
|
|
* | 0 1 | | 2 3 | | 4 5 | FAT bytes |
456 |
|
|
* +----+----+ +----+----+ +----+----+ |
457 |
|
|
* |
458 |
|
|
* +----+----+----+ +----+----+----+ |
459 |
|
|
* | 3 0 1 | | 4 5 2 | |
460 |
|
|
* +----+----+----+ +----+----+----+ |
461 |
|
|
* cluster n cluster n+1 |
462 |
|
|
* |
463 |
|
|
* Where n is even. m = n + (n >> 2) |
464 |
|
|
* |
465 |
|
|
*/ |
466 |
|
|
static inline void |
467 |
|
|
usemap_alloc(struct msdosfsmount *pmp, u_long cn) |
468 |
|
|
{ |
469 |
|
|
|
470 |
|
|
pmp->pm_inusemap[cn / N_INUSEBITS] |= 1 << (cn % N_INUSEBITS); |
471 |
|
|
pmp->pm_freeclustercount--; |
472 |
|
|
} |
473 |
|
|
|
474 |
|
|
static inline void |
475 |
|
|
usemap_free(struct msdosfsmount *pmp, u_long cn) |
476 |
|
|
{ |
477 |
|
|
|
478 |
|
|
pmp->pm_freeclustercount++; |
479 |
|
|
pmp->pm_inusemap[cn / N_INUSEBITS] &= ~(1 << (cn % N_INUSEBITS)); |
480 |
|
|
} |
481 |
|
|
|
482 |
|
|
int |
483 |
|
|
clusterfree(struct msdosfsmount *pmp, u_long cluster, u_long *oldcnp) |
484 |
|
|
{ |
485 |
|
|
int error; |
486 |
|
|
u_long oldcn; |
487 |
|
|
|
488 |
|
|
usemap_free(pmp, cluster); |
489 |
|
|
error = fatentry(FAT_GET_AND_SET, pmp, cluster, &oldcn, MSDOSFSFREE); |
490 |
|
|
if (error) { |
491 |
|
|
usemap_alloc(pmp, cluster); |
492 |
|
|
return (error); |
493 |
|
|
} |
494 |
|
|
/* |
495 |
|
|
* If the cluster was successfully marked free, then update |
496 |
|
|
* the count of free clusters, and turn off the "allocated" |
497 |
|
|
* bit in the "in use" cluster bit map. |
498 |
|
|
*/ |
499 |
|
|
if (oldcnp) |
500 |
|
|
*oldcnp = oldcn; |
501 |
|
|
return (0); |
502 |
|
|
} |
503 |
|
|
|
504 |
|
|
/* |
505 |
|
|
* Get or Set or 'Get and Set' the cluster'th entry in the FAT. |
506 |
|
|
* |
507 |
|
|
* function - whether to get or set a fat entry |
508 |
|
|
* pmp - address of the msdosfsmount structure for the filesystem |
509 |
|
|
* whose FAT is to be manipulated. |
510 |
|
|
* cn - which cluster is of interest |
511 |
|
|
* oldcontents - address of a word that is to receive the contents of the |
512 |
|
|
* cluster'th entry if this is a get function |
513 |
|
|
* newcontents - the new value to be written into the cluster'th element of |
514 |
|
|
* the FAT if this is a set function. |
515 |
|
|
* |
516 |
|
|
* This function can also be used to free a cluster by setting the FAT entry |
517 |
|
|
* for a cluster to 0. |
518 |
|
|
* |
519 |
|
|
* All copies of the FAT are updated if this is a set function. NOTE: If |
520 |
|
|
* fatentry() marks a cluster as free it does not update the inusemap in |
521 |
|
|
* the msdosfsmount structure. This is left to the caller. |
522 |
|
|
*/ |
523 |
|
|
int |
524 |
|
|
fatentry(int function, struct msdosfsmount *pmp, u_long cn, u_long *oldcontents, u_long newcontents) |
525 |
|
|
{ |
526 |
|
|
int error; |
527 |
|
|
u_long readcn; |
528 |
|
|
u_long bn, bo, bsize, byteoffset; |
529 |
|
|
struct mkfsbuf *bp; |
530 |
|
|
|
531 |
|
|
DPRINTF(("%s(func %d, pmp %p, clust %lu, oldcon %p, newcon " "%lx)\n", |
532 |
|
|
__func__, function, pmp, cn, oldcontents, newcontents)); |
533 |
|
|
|
534 |
|
|
#ifdef DIAGNOSTIC |
535 |
|
|
/* |
536 |
|
|
* Be sure they asked us to do something. |
537 |
|
|
*/ |
538 |
|
|
if ((function & (FAT_SET | FAT_GET)) == 0) { |
539 |
|
|
DPRINTF(("%s(): function code doesn't specify get or set\n", |
540 |
|
|
__func__)); |
541 |
|
|
return (EINVAL); |
542 |
|
|
} |
543 |
|
|
|
544 |
|
|
/* |
545 |
|
|
* If they asked us to return a cluster number but didn't tell us |
546 |
|
|
* where to put it, give them an error. |
547 |
|
|
*/ |
548 |
|
|
if ((function & FAT_GET) && oldcontents == NULL) { |
549 |
|
|
DPRINTF(("%s(): get function with no place to put result\n", |
550 |
|
|
__func__)); |
551 |
|
|
return (EINVAL); |
552 |
|
|
} |
553 |
|
|
#endif |
554 |
|
|
|
555 |
|
|
/* |
556 |
|
|
* Be sure the requested cluster is in the filesystem. |
557 |
|
|
*/ |
558 |
|
|
if (cn < CLUST_FIRST || cn > pmp->pm_maxcluster) |
559 |
|
|
return (EINVAL); |
560 |
|
|
|
561 |
|
|
byteoffset = FATOFS(pmp, cn); |
562 |
|
|
fatblock(pmp, byteoffset, &bn, &bsize, &bo); |
563 |
|
|
if ((error = bread(pmp->pm_devvp, de_bn2kb(pmp, bn), bsize, |
564 |
|
|
0, &bp)) != 0) { |
565 |
|
|
return (error); |
566 |
|
|
} |
567 |
|
|
|
568 |
|
|
if (function & FAT_GET) { |
569 |
|
|
if (FAT32(pmp)) |
570 |
|
|
readcn = getulong((char *)bp->b_data + bo); |
571 |
|
|
else |
572 |
|
|
readcn = getushort((char *)bp->b_data + bo); |
573 |
|
|
if (FAT12(pmp) & (cn & 1)) |
574 |
|
|
readcn >>= 4; |
575 |
|
|
readcn &= pmp->pm_fatmask; |
576 |
|
|
*oldcontents = readcn; |
577 |
|
|
} |
578 |
|
|
if (function & FAT_SET) { |
579 |
|
|
switch (pmp->pm_fatmask) { |
580 |
|
|
case FAT12_MASK: |
581 |
|
|
readcn = getushort((char *)bp->b_data + bo); |
582 |
|
|
if (cn & 1) { |
583 |
|
|
readcn &= 0x000f; |
584 |
|
|
readcn |= newcontents << 4; |
585 |
|
|
} else { |
586 |
|
|
readcn &= 0xf000; |
587 |
|
|
readcn |= newcontents & 0xfff; |
588 |
|
|
} |
589 |
|
|
putushort((char *)bp->b_data + bo, readcn); |
590 |
|
|
break; |
591 |
|
|
case FAT16_MASK: |
592 |
|
|
putushort((char *)bp->b_data + bo, newcontents); |
593 |
|
|
break; |
594 |
|
|
case FAT32_MASK: |
595 |
|
|
/* |
596 |
|
|
* According to spec we have to retain the |
597 |
|
|
* high order bits of the FAT entry. |
598 |
|
|
*/ |
599 |
|
|
readcn = getulong((char *)bp->b_data + bo); |
600 |
|
|
readcn &= ~FAT32_MASK; |
601 |
|
|
readcn |= newcontents & FAT32_MASK; |
602 |
|
|
putulong((char *)bp->b_data + bo, readcn); |
603 |
|
|
break; |
604 |
|
|
} |
605 |
|
|
updatefats(pmp, bp, bn); |
606 |
|
|
bp = NULL; |
607 |
|
|
pmp->pm_fmod = 1; |
608 |
|
|
} |
609 |
|
|
if (bp) |
610 |
|
|
brelse(bp, 0); |
611 |
|
|
return (0); |
612 |
|
|
} |
613 |
|
|
|
614 |
|
|
/* |
615 |
|
|
* Update a contiguous cluster chain |
616 |
|
|
* |
617 |
|
|
* pmp - mount point |
618 |
|
|
* start - first cluster of chain |
619 |
|
|
* count - number of clusters in chain |
620 |
|
|
* fillwith - what to write into FAT entry of last cluster |
621 |
|
|
*/ |
622 |
|
|
static int |
623 |
|
|
fatchain(struct msdosfsmount *pmp, u_long start, u_long count, u_long fillwith) |
624 |
|
|
{ |
625 |
|
|
int error; |
626 |
|
|
u_long bn, bo, bsize, byteoffset, readcn, newc; |
627 |
|
|
struct mkfsbuf *bp; |
628 |
|
|
|
629 |
|
|
DPRINTF(("%s(pmp %p, start %lu, count %lu, fillwith %lx)\n", __func__, |
630 |
|
|
pmp, start, count, fillwith)); |
631 |
|
|
/* |
632 |
|
|
* Be sure the clusters are in the filesystem. |
633 |
|
|
*/ |
634 |
|
|
if (start < CLUST_FIRST || start + count - 1 > pmp->pm_maxcluster) |
635 |
|
|
return (EINVAL); |
636 |
|
|
|
637 |
|
|
while (count > 0) { |
638 |
|
|
byteoffset = FATOFS(pmp, start); |
639 |
|
|
fatblock(pmp, byteoffset, &bn, &bsize, &bo); |
640 |
|
|
error = bread(pmp->pm_devvp, de_bn2kb(pmp, bn), bsize, |
641 |
|
|
B_MODIFY, &bp); |
642 |
|
|
if (error) { |
643 |
|
|
return (error); |
644 |
|
|
} |
645 |
|
|
while (count > 0) { |
646 |
|
|
start++; |
647 |
|
|
newc = --count > 0 ? start : fillwith; |
648 |
|
|
switch (pmp->pm_fatmask) { |
649 |
|
|
case FAT12_MASK: |
650 |
|
|
readcn = getushort((char *)bp->b_data + bo); |
651 |
|
|
if (start & 1) { |
652 |
|
|
readcn &= 0xf000; |
653 |
|
|
readcn |= newc & 0xfff; |
654 |
|
|
} else { |
655 |
|
|
readcn &= 0x000f; |
656 |
|
|
readcn |= newc << 4; |
657 |
|
|
} |
658 |
|
|
putushort((char *)bp->b_data + bo, readcn); |
659 |
|
|
bo++; |
660 |
|
|
if (!(start & 1)) |
661 |
|
|
bo++; |
662 |
|
|
break; |
663 |
|
|
case FAT16_MASK: |
664 |
|
|
putushort((char *)bp->b_data + bo, newc); |
665 |
|
|
bo += 2; |
666 |
|
|
break; |
667 |
|
|
case FAT32_MASK: |
668 |
|
|
readcn = getulong((char *)bp->b_data + bo); |
669 |
|
|
readcn &= ~pmp->pm_fatmask; |
670 |
|
|
readcn |= newc & pmp->pm_fatmask; |
671 |
|
|
putulong((char *)bp->b_data + bo, readcn); |
672 |
|
|
bo += 4; |
673 |
|
|
break; |
674 |
|
|
} |
675 |
|
|
if (bo >= bsize) |
676 |
|
|
break; |
677 |
|
|
} |
678 |
|
|
updatefats(pmp, bp, bn); |
679 |
|
|
} |
680 |
|
|
pmp->pm_fmod = 1; |
681 |
|
|
return (0); |
682 |
|
|
} |
683 |
|
|
|
684 |
|
|
/* |
685 |
|
|
* Check the length of a free cluster chain starting at start. |
686 |
|
|
* |
687 |
|
|
* pmp - mount point |
688 |
|
|
* start - start of chain |
689 |
|
|
* count - maximum interesting length |
690 |
|
|
*/ |
691 |
|
|
int |
692 |
|
|
chainlength(struct msdosfsmount *pmp, u_long start, u_long count) |
693 |
|
|
{ |
694 |
|
|
u_long idx, max_idx; |
695 |
|
|
u_int map; |
696 |
|
|
u_long len; |
697 |
|
|
|
698 |
|
|
max_idx = pmp->pm_maxcluster / N_INUSEBITS; |
699 |
|
|
idx = start / N_INUSEBITS; |
700 |
|
|
start %= N_INUSEBITS; |
701 |
|
|
map = pmp->pm_inusemap[idx]; |
702 |
|
|
map &= ~((1 << start) - 1); |
703 |
|
|
if (map) { |
704 |
|
|
len = ffs(map) - 1 - start; |
705 |
|
|
return (len > count ? count : len); |
706 |
|
|
} |
707 |
|
|
len = N_INUSEBITS - start; |
708 |
|
|
if (len >= count) |
709 |
|
|
return (count); |
710 |
|
|
while (++idx <= max_idx) { |
711 |
|
|
if (len >= count) |
712 |
|
|
break; |
713 |
|
|
if ((map = pmp->pm_inusemap[idx]) != 0) { |
714 |
|
|
len += ffs(map) - 1; |
715 |
|
|
break; |
716 |
|
|
} |
717 |
|
|
len += N_INUSEBITS; |
718 |
|
|
} |
719 |
|
|
return (len > count ? count : len); |
720 |
|
|
} |
721 |
|
|
|
722 |
|
|
/* |
723 |
|
|
* Allocate contigous free clusters. |
724 |
|
|
* |
725 |
|
|
* pmp - mount point. |
726 |
|
|
* start - start of cluster chain. |
727 |
|
|
* count - number of clusters to allocate. |
728 |
|
|
* fillwith - put this value into the FAT entry for the |
729 |
|
|
* last allocated cluster. |
730 |
|
|
* retcluster - put the first allocated cluster's number here. |
731 |
|
|
* got - how many clusters were actually allocated. |
732 |
|
|
*/ |
733 |
|
|
int |
734 |
|
|
chainalloc(struct msdosfsmount *pmp, u_long start, u_long count, u_long fillwith, u_long *retcluster, u_long *got) |
735 |
|
|
{ |
736 |
|
|
int error; |
737 |
|
|
u_long cl, n; |
738 |
|
|
|
739 |
|
|
for (cl = start, n = count; n-- > 0;) |
740 |
|
|
usemap_alloc(pmp, cl++); |
741 |
|
|
if ((error = fatchain(pmp, start, count, fillwith)) != 0) |
742 |
|
|
return (error); |
743 |
|
|
|
744 |
|
|
DPRINTF(("%s(): allocated cluster chain at %lu (%lu clusters)\n", |
745 |
|
|
__func__, start, count)); |
746 |
|
|
if (retcluster) |
747 |
|
|
*retcluster = start; |
748 |
|
|
if (got) |
749 |
|
|
*got = count; |
750 |
|
|
return (0); |
751 |
|
|
} |
752 |
|
|
|
753 |
|
|
/* |
754 |
|
|
* Allocate contiguous free clusters. |
755 |
|
|
* |
756 |
|
|
* pmp - mount point. |
757 |
|
|
* start - preferred start of cluster chain. |
758 |
|
|
* count - number of clusters requested. |
759 |
|
|
* fillwith - put this value into the FAT entry for the |
760 |
|
|
* last allocated cluster. |
761 |
|
|
* retcluster - put the first allocated cluster's number here. |
762 |
|
|
* got - how many clusters were actually allocated. |
763 |
|
|
*/ |
764 |
|
|
int |
765 |
|
|
clusteralloc(struct msdosfsmount *pmp, u_long start, u_long count, u_long *retcluster, u_long *got) |
766 |
|
|
{ |
767 |
|
|
u_long idx; |
768 |
|
|
u_long len, newst, foundl, cn, l; |
769 |
|
|
u_long foundcn = 0; /* XXX: foundcn could be used unititialized */ |
770 |
|
|
u_long fillwith = CLUST_EOFE; |
771 |
|
|
u_int map; |
772 |
|
|
|
773 |
|
|
DPRINTF(("%s(): find %lu clusters\n", __func__, count)); |
774 |
|
|
if (start) { |
775 |
|
|
if ((len = chainlength(pmp, start, count)) >= count) |
776 |
|
|
return (chainalloc(pmp, start, count, fillwith, retcluster, got)); |
777 |
|
|
} else { |
778 |
|
|
/* |
779 |
|
|
* This is a new file, initialize start |
780 |
|
|
*/ |
781 |
|
|
struct timeval tv; |
782 |
|
|
|
783 |
|
|
microtime(&tv); |
784 |
|
|
start = (tv.tv_usec >> 10) | tv.tv_usec; |
785 |
|
|
len = 0; |
786 |
|
|
} |
787 |
|
|
|
788 |
|
|
/* |
789 |
|
|
* Start at a (pseudo) random place to maximize cluster runs |
790 |
|
|
* under multiple writers. |
791 |
|
|
*/ |
792 |
|
|
newst = (start * 1103515245 + 12345) % (pmp->pm_maxcluster + 1); |
793 |
|
|
foundl = 0; |
794 |
|
|
|
795 |
|
|
for (cn = newst; cn <= pmp->pm_maxcluster;) { |
796 |
|
|
idx = cn / N_INUSEBITS; |
797 |
|
|
map = pmp->pm_inusemap[idx]; |
798 |
|
|
map |= (1 << (cn % N_INUSEBITS)) - 1; |
799 |
|
|
if (map != (u_int)-1) { |
800 |
|
|
cn = idx * N_INUSEBITS + ffs(map^(u_int)-1) - 1; |
801 |
|
|
if ((l = chainlength(pmp, cn, count)) >= count) |
802 |
|
|
return (chainalloc(pmp, cn, count, fillwith, retcluster, got)); |
803 |
|
|
if (l > foundl) { |
804 |
|
|
foundcn = cn; |
805 |
|
|
foundl = l; |
806 |
|
|
} |
807 |
|
|
cn += l + 1; |
808 |
|
|
continue; |
809 |
|
|
} |
810 |
|
|
cn += N_INUSEBITS - cn % N_INUSEBITS; |
811 |
|
|
} |
812 |
|
|
for (cn = 0; cn < newst;) { |
813 |
|
|
idx = cn / N_INUSEBITS; |
814 |
|
|
map = pmp->pm_inusemap[idx]; |
815 |
|
|
map |= (1 << (cn % N_INUSEBITS)) - 1; |
816 |
|
|
if (map != (u_int)-1) { |
817 |
|
|
cn = idx * N_INUSEBITS + ffs(map^(u_int)-1) - 1; |
818 |
|
|
if ((l = chainlength(pmp, cn, count)) >= count) |
819 |
|
|
return (chainalloc(pmp, cn, count, fillwith, retcluster, got)); |
820 |
|
|
if (l > foundl) { |
821 |
|
|
foundcn = cn; |
822 |
|
|
foundl = l; |
823 |
|
|
} |
824 |
|
|
cn += l + 1; |
825 |
|
|
continue; |
826 |
|
|
} |
827 |
|
|
cn += N_INUSEBITS - cn % N_INUSEBITS; |
828 |
|
|
} |
829 |
|
|
|
830 |
|
|
if (!foundl) |
831 |
|
|
return (ENOSPC); |
832 |
|
|
|
833 |
|
|
if (len) |
834 |
|
|
return (chainalloc(pmp, start, len, fillwith, retcluster, got)); |
835 |
|
|
else |
836 |
|
|
return (chainalloc(pmp, foundcn, foundl, fillwith, retcluster, got)); |
837 |
|
|
} |
838 |
|
|
|
839 |
|
|
|
840 |
|
|
/* |
841 |
|
|
* Free a chain of clusters. |
842 |
|
|
* |
843 |
|
|
* pmp - address of the msdosfs mount structure for the filesystem |
844 |
|
|
* containing the cluster chain to be freed. |
845 |
|
|
* startcluster - number of the 1st cluster in the chain of clusters to be |
846 |
|
|
* freed. |
847 |
|
|
*/ |
848 |
|
|
int |
849 |
|
|
freeclusterchain(struct msdosfsmount *pmp, u_long cluster) |
850 |
|
|
{ |
851 |
|
|
int error; |
852 |
|
|
struct mkfsbuf *bp = NULL; |
853 |
|
|
u_long bn, bo, bsize, byteoffset; |
854 |
|
|
u_long readcn, lbn = -1; |
855 |
|
|
|
856 |
|
|
while (cluster >= CLUST_FIRST && cluster <= pmp->pm_maxcluster) { |
857 |
|
|
byteoffset = FATOFS(pmp, cluster); |
858 |
|
|
fatblock(pmp, byteoffset, &bn, &bsize, &bo); |
859 |
|
|
if (lbn != bn) { |
860 |
|
|
if (bp) |
861 |
|
|
updatefats(pmp, bp, lbn); |
862 |
|
|
error = bread(pmp->pm_devvp, de_bn2kb(pmp, bn), bsize, |
863 |
|
|
B_MODIFY, &bp); |
864 |
|
|
if (error) { |
865 |
|
|
return (error); |
866 |
|
|
} |
867 |
|
|
lbn = bn; |
868 |
|
|
} |
869 |
|
|
usemap_free(pmp, cluster); |
870 |
|
|
KASSERT(bp != NULL); |
871 |
|
|
switch (pmp->pm_fatmask) { |
872 |
|
|
case FAT12_MASK: |
873 |
|
|
readcn = getushort((char *)bp->b_data + bo); |
874 |
|
|
if (cluster & 1) { |
875 |
|
|
cluster = readcn >> 4; |
876 |
|
|
readcn &= 0x000f; |
877 |
|
|
readcn |= MSDOSFSFREE << 4; |
878 |
|
|
} else { |
879 |
|
|
cluster = readcn; |
880 |
|
|
readcn &= 0xf000; |
881 |
|
|
readcn |= MSDOSFSFREE & 0xfff; |
882 |
|
|
} |
883 |
|
|
putushort((char *)bp->b_data + bo, readcn); |
884 |
|
|
break; |
885 |
|
|
case FAT16_MASK: |
886 |
|
|
cluster = getushort((char *)bp->b_data + bo); |
887 |
|
|
putushort((char *)bp->b_data + bo, MSDOSFSFREE); |
888 |
|
|
break; |
889 |
|
|
case FAT32_MASK: |
890 |
|
|
cluster = getulong((char *)bp->b_data + bo); |
891 |
|
|
putulong((char *)bp->b_data + bo, |
892 |
|
|
(MSDOSFSFREE & FAT32_MASK) | (cluster & ~FAT32_MASK)); |
893 |
|
|
break; |
894 |
|
|
} |
895 |
|
|
cluster &= pmp->pm_fatmask; |
896 |
|
|
} |
897 |
|
|
if (bp) |
898 |
|
|
updatefats(pmp, bp, bn); |
899 |
|
|
return (0); |
900 |
|
|
} |
901 |
|
|
|
902 |
|
|
/* |
903 |
|
|
* Read in FAT blocks looking for free clusters. For every free cluster |
904 |
|
|
* found turn off its corresponding bit in the pm_inusemap. |
905 |
|
|
*/ |
906 |
|
|
int |
907 |
|
|
fillinusemap(struct msdosfsmount *pmp) |
908 |
|
|
{ |
909 |
|
|
struct mkfsbuf *bp = NULL; |
910 |
|
|
u_long cn, readcn; |
911 |
|
|
int error; |
912 |
|
|
u_long bn, bo, bsize, byteoffset; |
913 |
|
|
|
914 |
|
|
/* |
915 |
|
|
* Mark all clusters in use, we mark the free ones in the FAT scan |
916 |
|
|
* loop further down. |
917 |
|
|
*/ |
918 |
|
|
for (cn = 0; cn < (pmp->pm_maxcluster + N_INUSEBITS) / N_INUSEBITS; cn++) |
919 |
|
|
pmp->pm_inusemap[cn] = (u_int)-1; |
920 |
|
|
|
921 |
|
|
/* |
922 |
|
|
* Figure how many free clusters are in the filesystem by ripping |
923 |
|
|
* through the FAT counting the number of entries whose content is |
924 |
|
|
* zero. These represent free clusters. |
925 |
|
|
*/ |
926 |
|
|
pmp->pm_freeclustercount = 0; |
927 |
|
|
for (cn = CLUST_FIRST; cn <= pmp->pm_maxcluster; cn++) { |
928 |
|
|
byteoffset = FATOFS(pmp, cn); |
929 |
|
|
bo = byteoffset % pmp->pm_fatblocksize; |
930 |
|
|
if (!bo || !bp) { |
931 |
|
|
/* Read new FAT block */ |
932 |
|
|
if (bp) |
933 |
|
|
brelse(bp, 0); |
934 |
|
|
fatblock(pmp, byteoffset, &bn, &bsize, NULL); |
935 |
|
|
error = bread(pmp->pm_devvp, de_bn2kb(pmp, bn), bsize, |
936 |
|
|
0, &bp); |
937 |
|
|
if (error) { |
938 |
|
|
return (error); |
939 |
|
|
} |
940 |
|
|
} |
941 |
|
|
if (FAT32(pmp)) |
942 |
|
|
readcn = getulong((char *)bp->b_data + bo); |
943 |
|
|
else |
944 |
|
|
readcn = getushort((char *)bp->b_data + bo); |
945 |
|
|
if (FAT12(pmp) && (cn & 1)) |
946 |
|
|
readcn >>= 4; |
947 |
|
|
readcn &= pmp->pm_fatmask; |
948 |
|
|
|
949 |
|
|
if (readcn == 0) |
950 |
|
|
usemap_free(pmp, cn); |
951 |
|
|
} |
952 |
|
|
if (bp) |
953 |
|
|
brelse(bp, 0); |
954 |
|
|
return (0); |
955 |
|
|
} |
956 |
|
|
|
957 |
|
|
/* |
958 |
|
|
* Allocate a new cluster and chain it onto the end of the file. |
959 |
|
|
* |
960 |
|
|
* dep - the file to extend |
961 |
|
|
* count - number of clusters to allocate |
962 |
|
|
* bpp - where to return the address of the buf header for the first new |
963 |
|
|
* file block |
964 |
|
|
* ncp - where to put cluster number of the first newly allocated cluster |
965 |
|
|
* If this pointer is 0, do not return the cluster number. |
966 |
|
|
* flags - see fat.h |
967 |
|
|
* |
968 |
|
|
* NOTE: This function is not responsible for turning on the DE_UPDATE bit of |
969 |
|
|
* the de_flag field of the denode and it does not change the de_FileSize |
970 |
|
|
* field. This is left for the caller to do. |
971 |
|
|
*/ |
972 |
|
|
|
973 |
|
|
int |
974 |
|
|
extendfile(struct denode *dep, u_long count, struct mkfsbuf **bpp, u_long *ncp, int flags) |
975 |
|
|
{ |
976 |
|
|
int error; |
977 |
|
|
u_long frcn = 0, cn, got; |
978 |
|
|
struct msdosfsmount *pmp = dep->de_pmp; |
979 |
|
|
struct mkfsbuf *bp; |
980 |
|
|
|
981 |
|
|
/* |
982 |
|
|
* Don't try to extend the root directory |
983 |
|
|
*/ |
984 |
|
|
if (dep->de_StartCluster == MSDOSFSROOT |
985 |
|
|
&& (dep->de_Attributes & ATTR_DIRECTORY)) { |
986 |
|
|
DPRINTF(("%s(): attempt to extend root directory\n", __func__)); |
987 |
|
|
return (ENOSPC); |
988 |
|
|
} |
989 |
|
|
|
990 |
|
|
/* |
991 |
|
|
* If the "file's last cluster" cache entry is empty, and the file |
992 |
|
|
* is not empty, then fill the cache entry by calling pcbmap(). |
993 |
|
|
*/ |
994 |
|
|
fc_fileextends++; |
995 |
|
|
if (dep->de_fc[FC_LASTFC].fc_frcn == FCE_EMPTY && |
996 |
|
|
dep->de_StartCluster != 0) { |
997 |
|
|
fc_lfcempty++; |
998 |
|
|
error = pcbmap(dep, CLUST_END, 0, &cn, 0); |
999 |
|
|
/* we expect it to return E2BIG */ |
1000 |
|
|
if (error != E2BIG) |
1001 |
|
|
return (error); |
1002 |
|
|
} |
1003 |
|
|
|
1004 |
|
|
fc_last_to_nexttolast(dep); |
1005 |
|
|
|
1006 |
|
|
while (count > 0) { |
1007 |
|
|
|
1008 |
|
|
/* |
1009 |
|
|
* Allocate a new cluster chain and cat onto the end of the |
1010 |
|
|
* file. If the file is empty we make de_StartCluster point |
1011 |
|
|
* to the new block. Note that de_StartCluster being 0 is |
1012 |
|
|
* sufficient to be sure the file is empty since we exclude |
1013 |
|
|
* attempts to extend the root directory above, and the root |
1014 |
|
|
* dir is the only file with a startcluster of 0 that has |
1015 |
|
|
* blocks allocated (sort of). |
1016 |
|
|
*/ |
1017 |
|
|
|
1018 |
|
|
if (dep->de_StartCluster == 0) |
1019 |
|
|
cn = 0; |
1020 |
|
|
else |
1021 |
|
|
cn = dep->de_fc[FC_LASTFC].fc_fsrcn + 1; |
1022 |
|
|
error = clusteralloc(pmp, cn, count, &cn, &got); |
1023 |
|
|
if (error) |
1024 |
|
|
return (error); |
1025 |
|
|
|
1026 |
|
|
count -= got; |
1027 |
|
|
|
1028 |
|
|
/* |
1029 |
|
|
* Give them the filesystem relative cluster number if they want |
1030 |
|
|
* it. |
1031 |
|
|
*/ |
1032 |
|
|
if (ncp) { |
1033 |
|
|
*ncp = cn; |
1034 |
|
|
ncp = NULL; |
1035 |
|
|
} |
1036 |
|
|
|
1037 |
|
|
if (dep->de_StartCluster == 0) { |
1038 |
|
|
dep->de_StartCluster = cn; |
1039 |
|
|
frcn = 0; |
1040 |
|
|
} else { |
1041 |
|
|
error = fatentry(FAT_SET, pmp, |
1042 |
|
|
dep->de_fc[FC_LASTFC].fc_fsrcn, |
1043 |
|
|
0, cn); |
1044 |
|
|
if (error) { |
1045 |
|
|
clusterfree(pmp, cn, NULL); |
1046 |
|
|
return (error); |
1047 |
|
|
} |
1048 |
|
|
frcn = dep->de_fc[FC_LASTFC].fc_frcn + 1; |
1049 |
|
|
} |
1050 |
|
|
|
1051 |
|
|
/* |
1052 |
|
|
* Update the "last cluster of the file" entry in the |
1053 |
|
|
* denode's FAT cache. |
1054 |
|
|
*/ |
1055 |
|
|
|
1056 |
|
|
fc_setcache(dep, FC_LASTFC, frcn + got - 1, cn + got - 1); |
1057 |
|
|
if ((flags & DE_CLEAR) && |
1058 |
|
|
(dep->de_Attributes & ATTR_DIRECTORY)) { |
1059 |
|
|
while (got-- > 0) { |
1060 |
|
|
bp = getblk(pmp->pm_devvp, |
1061 |
|
|
de_bn2kb(pmp, cntobn(pmp, cn++)), |
1062 |
|
|
pmp->pm_bpcluster, 0, 0); |
1063 |
|
|
clrbuf(bp); |
1064 |
|
|
if (bpp) { |
1065 |
|
|
*bpp = bp; |
1066 |
|
|
bpp = NULL; |
1067 |
|
|
} else { |
1068 |
|
|
bdwrite(bp); |
1069 |
|
|
} |
1070 |
|
|
} |
1071 |
|
|
} |
1072 |
|
|
} |
1073 |
|
|
|
1074 |
|
|
return (0); |
1075 |
|
|
} |