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