Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/fs/msdosfs/msdosfs_fat.c
39586 views
1
/* $NetBSD: msdosfs_fat.c,v 1.28 1997/11/17 15:36:49 ws Exp $ */
2
3
/*-
4
* SPDX-License-Identifier: BSD-4-Clause
5
*
6
* Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank.
7
* Copyright (C) 1994, 1995, 1997 TooLs GmbH.
8
* All rights reserved.
9
* Original code by Paul Popelka ([email protected]) (see below).
10
*
11
* Redistribution and use in source and binary forms, with or without
12
* modification, are permitted provided that the following conditions
13
* are met:
14
* 1. Redistributions of source code must retain the above copyright
15
* notice, this list of conditions and the following disclaimer.
16
* 2. Redistributions in binary form must reproduce the above copyright
17
* notice, this list of conditions and the following disclaimer in the
18
* documentation and/or other materials provided with the distribution.
19
* 3. All advertising materials mentioning features or use of this software
20
* must display the following acknowledgement:
21
* This product includes software developed by TooLs GmbH.
22
* 4. The name of TooLs GmbH may not be used to endorse or promote products
23
* derived from this software without specific prior written permission.
24
*
25
* THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
26
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
27
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
28
* IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
29
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
30
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
31
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
32
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
33
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
34
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35
*/
36
/*-
37
* Written by Paul Popelka ([email protected])
38
*
39
* You can do anything you want with this software, just don't say you wrote
40
* it, and don't remove this notice.
41
*
42
* This software is provided "as is".
43
*
44
* The author supplies this software to be publicly redistributed on the
45
* understanding that the author is not responsible for the correct
46
* functioning of this software in any circumstances and is not liable for
47
* any damages caused by this software.
48
*
49
* October 1992
50
*/
51
52
#include <sys/param.h>
53
#include <sys/systm.h>
54
#include <sys/buf.h>
55
#include <sys/mount.h>
56
#include <sys/vmmeter.h>
57
#include <sys/vnode.h>
58
59
#include <fs/msdosfs/bpb.h>
60
#include <fs/msdosfs/direntry.h>
61
#include <fs/msdosfs/denode.h>
62
#include <fs/msdosfs/fat.h>
63
#include <fs/msdosfs/msdosfsmount.h>
64
65
#define FULL_RUN ((u_int)0xffffffff)
66
67
static int chainalloc(struct msdosfsmount *pmp, u_long start,
68
u_long count, u_long fillwith, u_long *retcluster,
69
u_long *got);
70
static int chainlength(struct msdosfsmount *pmp, u_long start,
71
u_long count);
72
static void fatblock(struct msdosfsmount *pmp, u_long ofs, u_long *bnp,
73
u_long *sizep, u_long *bop);
74
static int fatchain(struct msdosfsmount *pmp, u_long start, u_long count,
75
u_long fillwith);
76
static void fc_lookup(struct denode *dep, u_long findcn, u_long *frcnp,
77
u_long *fsrcnp);
78
static void updatefats(struct msdosfsmount *pmp, struct buf *bp,
79
u_long fatbn);
80
static __inline void
81
usemap_alloc(struct msdosfsmount *pmp, u_long cn);
82
static int usemap_free(struct msdosfsmount *pmp, u_long cn);
83
static int clusteralloc1(struct msdosfsmount *pmp, u_long start,
84
u_long count, u_long fillwith, u_long *retcluster,
85
u_long *got);
86
87
static void
88
fatblock(struct msdosfsmount *pmp, u_long ofs, u_long *bnp, u_long *sizep,
89
u_long *bop)
90
{
91
u_long bn, size, fatblocksec;
92
93
fatblocksec = pmp->pm_fatblocksec;
94
if (FAT12(pmp) && fatblocksec % 3 != 0) {
95
fatblocksec *= 3;
96
if (fatblocksec % 6 == 0)
97
fatblocksec /= 2;
98
}
99
bn = ofs / pmp->pm_fatblocksize * pmp->pm_fatblocksec;
100
size = roundup(ulmin(fatblocksec, pmp->pm_FATsecs - bn) * DEV_BSIZE,
101
pmp->pm_BlkPerSec * DEV_BSIZE);
102
bn += pmp->pm_fatblk + pmp->pm_curfat * pmp->pm_FATsecs;
103
104
if (bnp)
105
*bnp = bn;
106
if (sizep)
107
*sizep = size;
108
if (bop)
109
*bop = ofs % pmp->pm_fatblocksize;
110
}
111
112
/*
113
* Map the logical cluster number of a file into a physical disk sector
114
* that is filesystem relative.
115
*
116
* dep - address of denode representing the file of interest
117
* findcn - file relative cluster whose filesystem relative cluster number
118
* and/or block number are/is to be found
119
* bnp - address of where to place the filesystem relative block number.
120
* If this pointer is null then don't return this quantity.
121
* cnp - address of where to place the filesystem relative cluster number.
122
* If this pointer is null then don't return this quantity.
123
* sp - pointer to returned block size
124
*
125
* NOTE: Either bnp or cnp must be non-null.
126
* This function has one side effect. If the requested file relative cluster
127
* is beyond the end of file, then the actual number of clusters in the file
128
* is returned in *cnp. This is useful for determining how long a directory is.
129
* If cnp is null, nothing is returned.
130
*/
131
int
132
pcbmap(struct denode *dep, u_long findcn, daddr_t *bnp, u_long *cnp, int *sp)
133
{
134
int error;
135
u_long i;
136
u_long cn;
137
u_long prevcn = 0; /* XXX: prevcn could be used unititialized */
138
u_long byteoffset;
139
u_long bn;
140
u_long bo;
141
struct buf *bp = NULL;
142
u_long bp_bn = -1;
143
struct msdosfsmount *pmp = dep->de_pmp;
144
u_long bsize;
145
146
KASSERT(bnp != NULL || cnp != NULL || sp != NULL,
147
("pcbmap: extra call"));
148
ASSERT_VOP_ELOCKED(DETOV(dep), "pcbmap");
149
150
cn = dep->de_StartCluster;
151
/*
152
* The "file" that makes up the root directory is contiguous,
153
* permanently allocated, of fixed size, and is not made up of
154
* clusters. If the cluster number is beyond the end of the root
155
* directory, then return the number of clusters in the file.
156
*/
157
if (cn == MSDOSFSROOT) {
158
if (dep->de_Attributes & ATTR_DIRECTORY) {
159
if (de_cn2off(pmp, findcn) >= dep->de_FileSize) {
160
if (cnp)
161
*cnp = de_bn2cn(pmp, pmp->pm_rootdirsize);
162
return (E2BIG);
163
}
164
if (bnp)
165
*bnp = pmp->pm_rootdirblk + de_cn2bn(pmp, findcn);
166
if (cnp)
167
*cnp = MSDOSFSROOT;
168
if (sp)
169
*sp = min(pmp->pm_bpcluster,
170
dep->de_FileSize - de_cn2off(pmp, findcn));
171
return (0);
172
} else { /* just an empty file */
173
if (cnp)
174
*cnp = 0;
175
return (E2BIG);
176
}
177
}
178
179
/*
180
* All other files do I/O in cluster sized blocks
181
*/
182
if (sp)
183
*sp = pmp->pm_bpcluster;
184
185
/*
186
* Rummage around in the FAT cache, maybe we can avoid tromping
187
* through every FAT entry for the file. And, keep track of how far
188
* off the cache was from where we wanted to be.
189
*/
190
i = 0;
191
fc_lookup(dep, findcn, &i, &cn);
192
193
/*
194
* Handle all other files or directories the normal way.
195
*/
196
for (; i < findcn; i++) {
197
/*
198
* Stop with all reserved clusters, not just with EOF.
199
*/
200
if ((cn | ~pmp->pm_fatmask) >= CLUST_RSRVD)
201
goto hiteof;
202
byteoffset = FATOFS(pmp, cn);
203
fatblock(pmp, byteoffset, &bn, &bsize, &bo);
204
if (bn != bp_bn) {
205
if (bp)
206
brelse(bp);
207
error = bread(pmp->pm_devvp, bn, bsize, NOCRED, &bp);
208
if (error) {
209
return (error);
210
}
211
bp_bn = bn;
212
}
213
prevcn = cn;
214
if (bo >= bsize) {
215
if (bp)
216
brelse(bp);
217
return (EIO);
218
}
219
if (FAT32(pmp))
220
cn = getulong(bp->b_data + bo);
221
else
222
cn = getushort(bp->b_data + bo);
223
if (FAT12(pmp) && (prevcn & 1))
224
cn >>= 4;
225
cn &= pmp->pm_fatmask;
226
227
/*
228
* Force the special cluster numbers
229
* to be the same for all cluster sizes
230
* to let the rest of msdosfs handle
231
* all cases the same.
232
*/
233
if ((cn | ~pmp->pm_fatmask) >= CLUST_RSRVD)
234
cn |= ~pmp->pm_fatmask;
235
}
236
237
if (!MSDOSFSEOF(pmp, cn)) {
238
if (bp)
239
brelse(bp);
240
if (bnp)
241
*bnp = cntobn(pmp, cn);
242
if (cnp)
243
*cnp = cn;
244
fc_setcache(dep, FC_LASTMAP, i, cn);
245
return (0);
246
}
247
248
hiteof:;
249
if (cnp)
250
*cnp = i;
251
if (bp)
252
brelse(bp);
253
/* update last file cluster entry in the FAT cache */
254
fc_setcache(dep, FC_LASTFC, i - 1, prevcn);
255
return (E2BIG);
256
}
257
258
/*
259
* Find the closest entry in the FAT cache to the cluster we are looking
260
* for.
261
*/
262
static void
263
fc_lookup(struct denode *dep, u_long findcn, u_long *frcnp, u_long *fsrcnp)
264
{
265
int i;
266
u_long cn;
267
struct fatcache *closest = NULL;
268
269
ASSERT_VOP_LOCKED(DETOV(dep), "fc_lookup");
270
271
for (i = 0; i < FC_SIZE; i++) {
272
cn = dep->de_fc[i].fc_frcn;
273
if (cn != FCE_EMPTY && cn <= findcn) {
274
if (closest == NULL || cn > closest->fc_frcn)
275
closest = &dep->de_fc[i];
276
}
277
}
278
if (closest) {
279
*frcnp = closest->fc_frcn;
280
*fsrcnp = closest->fc_fsrcn;
281
}
282
}
283
284
/*
285
* Purge the FAT cache in denode dep of all entries relating to file
286
* relative cluster frcn and beyond.
287
*/
288
void
289
fc_purge(struct denode *dep, u_int frcn)
290
{
291
int i;
292
struct fatcache *fcp;
293
294
ASSERT_VOP_ELOCKED(DETOV(dep), "fc_purge");
295
296
fcp = dep->de_fc;
297
for (i = 0; i < FC_SIZE; i++, fcp++) {
298
if (fcp->fc_frcn >= frcn)
299
fcp->fc_frcn = FCE_EMPTY;
300
}
301
}
302
303
/*
304
* Update the FAT.
305
* If mirroring the FAT, update all copies, with the first copy as last.
306
* Else update only the current FAT (ignoring the others).
307
*
308
* pmp - msdosfsmount structure for filesystem to update
309
* bp - addr of modified FAT block
310
* fatbn - block number relative to begin of filesystem of the modified FAT block.
311
*/
312
static void
313
updatefats(struct msdosfsmount *pmp, struct buf *bp, u_long fatbn)
314
{
315
struct buf *bpn;
316
int cleanfat, i;
317
318
#ifdef MSDOSFS_DEBUG
319
printf("updatefats(pmp %p, bp %p, fatbn %lu)\n", pmp, bp, fatbn);
320
#endif
321
322
if (pmp->pm_flags & MSDOSFS_FATMIRROR) {
323
/*
324
* Now copy the block(s) of the modified FAT to the other copies of
325
* the FAT and write them out. This is faster than reading in the
326
* other FATs and then writing them back out. This could tie up
327
* the FAT for quite a while. Preventing others from accessing it.
328
* To prevent us from going after the FAT quite so much we use
329
* delayed writes, unless they specified "synchronous" when the
330
* filesystem was mounted. If synch is asked for then use
331
* bwrite()'s and really slow things down.
332
*/
333
if (fatbn != pmp->pm_fatblk || FAT12(pmp))
334
cleanfat = 0;
335
else if (FAT16(pmp))
336
cleanfat = 16;
337
else
338
cleanfat = 32;
339
for (i = 1; i < pmp->pm_FATs; i++) {
340
fatbn += pmp->pm_FATsecs;
341
/* getblk() never fails */
342
bpn = getblk(pmp->pm_devvp, fatbn, bp->b_bcount,
343
0, 0, 0);
344
memcpy(bpn->b_data, bp->b_data, bp->b_bcount);
345
/* Force the clean bit on in the other copies. */
346
if (cleanfat == 16)
347
((uint8_t *)bpn->b_data)[3] |= 0x80;
348
else if (cleanfat == 32)
349
((uint8_t *)bpn->b_data)[7] |= 0x08;
350
if (pmp->pm_mountp->mnt_flag & MNT_SYNCHRONOUS)
351
bwrite(bpn);
352
else
353
bdwrite(bpn);
354
}
355
}
356
357
/*
358
* Write out the first (or current) FAT last.
359
*/
360
if (pmp->pm_mountp->mnt_flag & MNT_SYNCHRONOUS)
361
bwrite(bp);
362
else
363
bdwrite(bp);
364
}
365
366
/*
367
* Updating entries in 12 bit FATs is a pain in the butt.
368
*
369
* The following picture shows where nibbles go when moving from a 12 bit
370
* cluster number into the appropriate bytes in the FAT.
371
*
372
* byte m byte m+1 byte m+2
373
* +----+----+ +----+----+ +----+----+
374
* | 0 1 | | 2 3 | | 4 5 | FAT bytes
375
* +----+----+ +----+----+ +----+----+
376
*
377
* +----+----+----+ +----+----+----+
378
* | 3 0 1 | | 4 5 2 |
379
* +----+----+----+ +----+----+----+
380
* cluster n cluster n+1
381
*
382
* Where n is even. m = n + (n >> 2)
383
*
384
*/
385
static __inline void
386
usemap_alloc(struct msdosfsmount *pmp, u_long cn)
387
{
388
389
MSDOSFS_ASSERT_MP_LOCKED(pmp);
390
391
KASSERT(cn <= pmp->pm_maxcluster, ("cn too large %lu %lu", cn,
392
pmp->pm_maxcluster));
393
KASSERT((pmp->pm_flags & MSDOSFSMNT_RONLY) == 0,
394
("usemap_alloc on ro msdosfs mount"));
395
KASSERT((pmp->pm_inusemap[cn / N_INUSEBITS] &
396
(1U << (cn % N_INUSEBITS))) == 0,
397
("Allocating used sector %ld %ld %x", cn, cn % N_INUSEBITS,
398
(unsigned)pmp->pm_inusemap[cn / N_INUSEBITS]));
399
pmp->pm_inusemap[cn / N_INUSEBITS] |= 1U << (cn % N_INUSEBITS);
400
KASSERT(pmp->pm_freeclustercount > 0, ("usemap_alloc: too little"));
401
pmp->pm_freeclustercount--;
402
pmp->pm_flags |= MSDOSFS_FSIMOD;
403
}
404
405
static int
406
usemap_free(struct msdosfsmount *pmp, u_long cn)
407
{
408
409
MSDOSFS_ASSERT_MP_LOCKED(pmp);
410
411
KASSERT(cn <= pmp->pm_maxcluster, ("cn too large %lu %lu", cn,
412
pmp->pm_maxcluster));
413
KASSERT((pmp->pm_flags & MSDOSFSMNT_RONLY) == 0,
414
("usemap_free on ro msdosfs mount"));
415
if ((pmp->pm_inusemap[cn / N_INUSEBITS] &
416
(1U << (cn % N_INUSEBITS))) == 0) {
417
printf("%s: Freeing unused sector %ld %ld %x\n",
418
pmp->pm_mountp->mnt_stat.f_mntonname, cn, cn % N_INUSEBITS,
419
(unsigned)pmp->pm_inusemap[cn / N_INUSEBITS]);
420
msdosfs_integrity_error(pmp);
421
return (EINTEGRITY);
422
}
423
pmp->pm_freeclustercount++;
424
pmp->pm_flags |= MSDOSFS_FSIMOD;
425
pmp->pm_inusemap[cn / N_INUSEBITS] &= ~(1U << (cn % N_INUSEBITS));
426
return (0);
427
}
428
429
void
430
clusterfree(struct msdosfsmount *pmp, u_long cluster)
431
{
432
int error;
433
u_long oldcn;
434
435
error = fatentry(FAT_GET_AND_SET, pmp, cluster, &oldcn, MSDOSFSFREE);
436
if (error != 0)
437
return;
438
/*
439
* If the cluster was successfully marked free, then update
440
* the count of free clusters, and turn off the "allocated"
441
* bit in the "in use" cluster bit map.
442
*/
443
MSDOSFS_LOCK_MP(pmp);
444
error = usemap_free(pmp, cluster);
445
MSDOSFS_UNLOCK_MP(pmp);
446
}
447
448
/*
449
* Get or Set or 'Get and Set' the cluster'th entry in the FAT.
450
*
451
* function - whether to get or set a FAT entry
452
* pmp - address of the msdosfsmount structure for the filesystem
453
* whose FAT is to be manipulated.
454
* cn - which cluster is of interest
455
* oldcontents - address of a word that is to receive the contents of the
456
* cluster'th entry if this is a get function
457
* newcontents - the new value to be written into the cluster'th element of
458
* the FAT if this is a set function.
459
*
460
* This function can also be used to free a cluster by setting the FAT entry
461
* for a cluster to 0.
462
*
463
* All copies of the FAT are updated if this is a set function. NOTE: If
464
* fatentry() marks a cluster as free it does not update the inusemap in
465
* the msdosfsmount structure. This is left to the caller.
466
*/
467
int
468
fatentry(int function, struct msdosfsmount *pmp, u_long cn, u_long *oldcontents,
469
u_long newcontents)
470
{
471
int error;
472
u_long readcn;
473
u_long bn, bo, bsize, byteoffset;
474
struct buf *bp;
475
476
#ifdef MSDOSFS_DEBUG
477
printf("fatentry(func %d, pmp %p, clust %lu, oldcon %p, newcon %lx)\n",
478
function, pmp, cn, oldcontents, newcontents);
479
#endif
480
481
#ifdef DIAGNOSTIC
482
/*
483
* Be sure they asked us to do something.
484
*/
485
if ((function & (FAT_SET | FAT_GET)) == 0) {
486
#ifdef MSDOSFS_DEBUG
487
printf("fatentry(): function code doesn't specify get or set\n");
488
#endif
489
return (EINVAL);
490
}
491
492
/*
493
* If they asked us to return a cluster number but didn't tell us
494
* where to put it, give them an error.
495
*/
496
if ((function & FAT_GET) && oldcontents == NULL) {
497
#ifdef MSDOSFS_DEBUG
498
printf("fatentry(): get function with no place to put result\n");
499
#endif
500
return (EINVAL);
501
}
502
#endif
503
504
/*
505
* Be sure the requested cluster is in the filesystem.
506
*/
507
if (cn < CLUST_FIRST || cn > pmp->pm_maxcluster)
508
return (EINVAL);
509
510
byteoffset = FATOFS(pmp, cn);
511
fatblock(pmp, byteoffset, &bn, &bsize, &bo);
512
error = bread(pmp->pm_devvp, bn, bsize, NOCRED, &bp);
513
if (error) {
514
return (error);
515
}
516
517
if (function & FAT_GET) {
518
if (FAT32(pmp))
519
readcn = getulong(bp->b_data + bo);
520
else
521
readcn = getushort(bp->b_data + bo);
522
if (FAT12(pmp) & (cn & 1))
523
readcn >>= 4;
524
readcn &= pmp->pm_fatmask;
525
/* map reserved FAT entries to same values for all FATs */
526
if ((readcn | ~pmp->pm_fatmask) >= CLUST_RSRVD)
527
readcn |= ~pmp->pm_fatmask;
528
*oldcontents = readcn;
529
}
530
if (function & FAT_SET) {
531
switch (pmp->pm_fatmask) {
532
case FAT12_MASK:
533
readcn = getushort(bp->b_data + bo);
534
if (cn & 1) {
535
readcn &= 0x000f;
536
readcn |= newcontents << 4;
537
} else {
538
readcn &= 0xf000;
539
readcn |= newcontents & 0xfff;
540
}
541
putushort(bp->b_data + bo, readcn);
542
break;
543
case FAT16_MASK:
544
putushort(bp->b_data + bo, newcontents);
545
break;
546
case FAT32_MASK:
547
/*
548
* According to spec we have to retain the
549
* high order bits of the FAT entry.
550
*/
551
readcn = getulong(bp->b_data + bo);
552
readcn &= ~FAT32_MASK;
553
readcn |= newcontents & FAT32_MASK;
554
putulong(bp->b_data + bo, readcn);
555
break;
556
}
557
updatefats(pmp, bp, bn);
558
bp = NULL;
559
pmp->pm_fmod = 1;
560
}
561
if (bp)
562
brelse(bp);
563
return (0);
564
}
565
566
/*
567
* Update a contiguous cluster chain
568
*
569
* pmp - mount point
570
* start - first cluster of chain
571
* count - number of clusters in chain
572
* fillwith - what to write into FAT entry of last cluster
573
*/
574
static int
575
fatchain(struct msdosfsmount *pmp, u_long start, u_long count, u_long fillwith)
576
{
577
int error;
578
u_long bn, bo, bsize, byteoffset, readcn, newc;
579
struct buf *bp;
580
581
#ifdef MSDOSFS_DEBUG
582
printf("fatchain(pmp %p, start %lu, count %lu, fillwith %lx)\n",
583
pmp, start, count, fillwith);
584
#endif
585
/*
586
* Be sure the clusters are in the filesystem.
587
*/
588
if (start < CLUST_FIRST || start + count - 1 > pmp->pm_maxcluster)
589
return (EINVAL);
590
591
while (count > 0) {
592
byteoffset = FATOFS(pmp, start);
593
fatblock(pmp, byteoffset, &bn, &bsize, &bo);
594
error = bread(pmp->pm_devvp, bn, bsize, NOCRED, &bp);
595
if (error) {
596
return (error);
597
}
598
while (count > 0) {
599
start++;
600
newc = --count > 0 ? start : fillwith;
601
switch (pmp->pm_fatmask) {
602
case FAT12_MASK:
603
readcn = getushort(bp->b_data + bo);
604
if (start & 1) {
605
readcn &= 0xf000;
606
readcn |= newc & 0xfff;
607
} else {
608
readcn &= 0x000f;
609
readcn |= newc << 4;
610
}
611
putushort(bp->b_data + bo, readcn);
612
bo++;
613
if (!(start & 1))
614
bo++;
615
break;
616
case FAT16_MASK:
617
putushort(bp->b_data + bo, newc);
618
bo += 2;
619
break;
620
case FAT32_MASK:
621
readcn = getulong(bp->b_data + bo);
622
readcn &= ~pmp->pm_fatmask;
623
readcn |= newc & pmp->pm_fatmask;
624
putulong(bp->b_data + bo, readcn);
625
bo += 4;
626
break;
627
}
628
if (bo >= bsize)
629
break;
630
}
631
updatefats(pmp, bp, bn);
632
}
633
pmp->pm_fmod = 1;
634
return (0);
635
}
636
637
/*
638
* Check the length of a free cluster chain starting at start.
639
*
640
* pmp - mount point
641
* start - start of chain
642
* count - maximum interesting length
643
*/
644
static int
645
chainlength(struct msdosfsmount *pmp, u_long start, u_long count)
646
{
647
u_long idx, max_idx;
648
u_int map;
649
u_long len;
650
651
MSDOSFS_ASSERT_MP_LOCKED(pmp);
652
653
if (start > pmp->pm_maxcluster)
654
return (0);
655
max_idx = pmp->pm_maxcluster / N_INUSEBITS;
656
idx = start / N_INUSEBITS;
657
start %= N_INUSEBITS;
658
map = pmp->pm_inusemap[idx];
659
map &= ~((1U << start) - 1);
660
if (map) {
661
len = ffs(map) - 1 - start;
662
len = MIN(len, count);
663
if (start + len > pmp->pm_maxcluster)
664
len = pmp->pm_maxcluster - start + 1;
665
return (len);
666
}
667
len = N_INUSEBITS - start;
668
if (len >= count) {
669
len = count;
670
if (start + len > pmp->pm_maxcluster)
671
len = pmp->pm_maxcluster - start + 1;
672
return (len);
673
}
674
while (++idx <= max_idx) {
675
if (len >= count)
676
break;
677
map = pmp->pm_inusemap[idx];
678
if (map) {
679
len += ffs(map) - 1;
680
break;
681
}
682
len += N_INUSEBITS;
683
}
684
len = MIN(len, count);
685
if (start + len > pmp->pm_maxcluster)
686
len = pmp->pm_maxcluster - start + 1;
687
return (len);
688
}
689
690
/*
691
* Allocate contigous free clusters.
692
*
693
* pmp - mount point.
694
* start - start of cluster chain.
695
* count - number of clusters to allocate.
696
* fillwith - put this value into the FAT entry for the
697
* last allocated cluster.
698
* retcluster - put the first allocated cluster's number here.
699
* got - how many clusters were actually allocated.
700
*/
701
static int
702
chainalloc(struct msdosfsmount *pmp, u_long start, u_long count,
703
u_long fillwith, u_long *retcluster, u_long *got)
704
{
705
int error;
706
u_long cl, n;
707
708
MSDOSFS_ASSERT_MP_LOCKED(pmp);
709
KASSERT((pmp->pm_flags & MSDOSFSMNT_RONLY) == 0,
710
("chainalloc on ro msdosfs mount"));
711
712
for (cl = start, n = count; n-- > 0;)
713
usemap_alloc(pmp, cl++);
714
pmp->pm_nxtfree = start + count;
715
if (pmp->pm_nxtfree > pmp->pm_maxcluster)
716
pmp->pm_nxtfree = CLUST_FIRST;
717
pmp->pm_flags |= MSDOSFS_FSIMOD;
718
error = fatchain(pmp, start, count, fillwith);
719
if (error != 0) {
720
for (cl = start, n = count; n-- > 0;)
721
(void)usemap_free(pmp, cl++);
722
return (error);
723
}
724
#ifdef MSDOSFS_DEBUG
725
printf("clusteralloc(): allocated cluster chain at %lu (%lu clusters)\n",
726
start, count);
727
#endif
728
if (retcluster)
729
*retcluster = start;
730
if (got)
731
*got = count;
732
return (0);
733
}
734
735
/*
736
* Allocate contiguous free clusters.
737
*
738
* pmp - mount point.
739
* start - preferred start of cluster chain.
740
* count - number of clusters requested.
741
* fillwith - put this value into the FAT entry for the
742
* last allocated cluster.
743
* retcluster - put the first allocated cluster's number here.
744
* got - how many clusters were actually allocated.
745
*/
746
int
747
clusteralloc(struct msdosfsmount *pmp, u_long start, u_long count,
748
u_long fillwith, u_long *retcluster, u_long *got)
749
{
750
int error;
751
752
MSDOSFS_LOCK_MP(pmp);
753
error = clusteralloc1(pmp, start, count, fillwith, retcluster, got);
754
MSDOSFS_UNLOCK_MP(pmp);
755
return (error);
756
}
757
758
static int
759
clusteralloc1(struct msdosfsmount *pmp, u_long start, u_long count,
760
u_long fillwith, u_long *retcluster, u_long *got)
761
{
762
u_long idx;
763
u_long len, newst, foundl, cn, l;
764
u_long foundcn = 0; /* XXX: foundcn could be used unititialized */
765
u_int map;
766
767
MSDOSFS_ASSERT_MP_LOCKED(pmp);
768
769
#ifdef MSDOSFS_DEBUG
770
printf("clusteralloc(): find %lu clusters\n", count);
771
#endif
772
if (start) {
773
if ((len = chainlength(pmp, start, count)) >= count)
774
return (chainalloc(pmp, start, count, fillwith, retcluster, got));
775
} else
776
len = 0;
777
778
newst = pmp->pm_nxtfree;
779
foundl = 0;
780
781
for (cn = newst; cn <= pmp->pm_maxcluster;) {
782
idx = cn / N_INUSEBITS;
783
map = pmp->pm_inusemap[idx];
784
map |= (1U << (cn % N_INUSEBITS)) - 1;
785
if (map != FULL_RUN) {
786
cn = idx * N_INUSEBITS + ffs(map ^ FULL_RUN) - 1;
787
if ((l = chainlength(pmp, cn, count)) >= count)
788
return (chainalloc(pmp, cn, count, fillwith, retcluster, got));
789
if (l > foundl) {
790
foundcn = cn;
791
foundl = l;
792
}
793
cn += l + 1;
794
continue;
795
}
796
cn += N_INUSEBITS - cn % N_INUSEBITS;
797
}
798
for (cn = 0; cn < newst;) {
799
idx = cn / N_INUSEBITS;
800
map = pmp->pm_inusemap[idx];
801
map |= (1U << (cn % N_INUSEBITS)) - 1;
802
if (map != FULL_RUN) {
803
cn = idx * N_INUSEBITS + ffs(map ^ FULL_RUN) - 1;
804
if ((l = chainlength(pmp, cn, count)) >= count)
805
return (chainalloc(pmp, cn, count, fillwith, retcluster, got));
806
if (l > foundl) {
807
foundcn = cn;
808
foundl = l;
809
}
810
cn += l + 1;
811
continue;
812
}
813
cn += N_INUSEBITS - cn % N_INUSEBITS;
814
}
815
816
if (!foundl)
817
return (ENOSPC);
818
819
if (len)
820
return (chainalloc(pmp, start, len, fillwith, retcluster, got));
821
else
822
return (chainalloc(pmp, foundcn, foundl, fillwith, retcluster, got));
823
}
824
825
/*
826
* Free a chain of clusters.
827
*
828
* pmp - address of the msdosfs mount structure for the filesystem
829
* containing the cluster chain to be freed.
830
* startcluster - number of the 1st cluster in the chain of clusters to be
831
* freed.
832
*/
833
int
834
freeclusterchain(struct msdosfsmount *pmp, u_long cluster)
835
{
836
int error;
837
struct buf *bp = NULL;
838
u_long bn, bo, bsize, byteoffset;
839
u_long readcn, lbn = -1;
840
841
MSDOSFS_LOCK_MP(pmp);
842
while (cluster >= CLUST_FIRST && cluster <= pmp->pm_maxcluster) {
843
byteoffset = FATOFS(pmp, cluster);
844
fatblock(pmp, byteoffset, &bn, &bsize, &bo);
845
if (lbn != bn) {
846
if (bp)
847
updatefats(pmp, bp, lbn);
848
error = bread(pmp->pm_devvp, bn, bsize, NOCRED, &bp);
849
if (error) {
850
MSDOSFS_UNLOCK_MP(pmp);
851
return (error);
852
}
853
lbn = bn;
854
}
855
error = usemap_free(pmp, cluster);
856
if (error != 0) {
857
updatefats(pmp, bp, lbn);
858
MSDOSFS_UNLOCK_MP(pmp);
859
return (error);
860
}
861
switch (pmp->pm_fatmask) {
862
case FAT12_MASK:
863
readcn = getushort(bp->b_data + bo);
864
if (cluster & 1) {
865
cluster = readcn >> 4;
866
readcn &= 0x000f;
867
readcn |= MSDOSFSFREE << 4;
868
} else {
869
cluster = readcn;
870
readcn &= 0xf000;
871
readcn |= MSDOSFSFREE & 0xfff;
872
}
873
putushort(bp->b_data + bo, readcn);
874
break;
875
case FAT16_MASK:
876
cluster = getushort(bp->b_data + bo);
877
putushort(bp->b_data + bo, MSDOSFSFREE);
878
break;
879
case FAT32_MASK:
880
cluster = getulong(bp->b_data + bo);
881
putulong(bp->b_data + bo,
882
(MSDOSFSFREE & FAT32_MASK) | (cluster & ~FAT32_MASK));
883
break;
884
}
885
cluster &= pmp->pm_fatmask;
886
if ((cluster | ~pmp->pm_fatmask) >= CLUST_RSRVD)
887
cluster |= pmp->pm_fatmask;
888
}
889
if (bp)
890
updatefats(pmp, bp, bn);
891
MSDOSFS_UNLOCK_MP(pmp);
892
return (0);
893
}
894
895
/*
896
* Read in FAT blocks looking for free clusters. For every free cluster
897
* found turn off its corresponding bit in the pm_inusemap.
898
*/
899
int
900
fillinusemap(struct msdosfsmount *pmp)
901
{
902
struct buf *bp;
903
u_long bn, bo, bsize, byteoffset, cn, readcn;
904
int error;
905
906
MSDOSFS_ASSERT_MP_LOCKED(pmp);
907
bp = NULL;
908
909
/*
910
* Mark all clusters in use, we mark the free ones in the FAT scan
911
* loop further down.
912
*/
913
for (cn = 0; cn < (pmp->pm_maxcluster + N_INUSEBITS) / N_INUSEBITS; cn++)
914
pmp->pm_inusemap[cn] = FULL_RUN;
915
916
/*
917
* Figure how many free clusters are in the filesystem by ripping
918
* through the FAT counting the number of entries whose content is
919
* zero. These represent free clusters.
920
*/
921
pmp->pm_freeclustercount = 0;
922
for (cn = 0; cn <= pmp->pm_maxcluster; cn++) {
923
byteoffset = FATOFS(pmp, cn);
924
bo = byteoffset % pmp->pm_fatblocksize;
925
if (bo == 0) {
926
/* Read new FAT block */
927
if (bp != NULL)
928
brelse(bp);
929
fatblock(pmp, byteoffset, &bn, &bsize, NULL);
930
error = bread(pmp->pm_devvp, bn, bsize, NOCRED, &bp);
931
if (error != 0)
932
return (error);
933
}
934
if (FAT32(pmp))
935
readcn = getulong(bp->b_data + bo);
936
else
937
readcn = getushort(bp->b_data + bo);
938
if (FAT12(pmp) && (cn & 1))
939
readcn >>= 4;
940
readcn &= pmp->pm_fatmask;
941
942
/*
943
* Check if the FAT ID matches the BPB's media descriptor and
944
* all other bits are set to 1.
945
*/
946
if (cn == 0 && readcn != ((pmp->pm_fatmask & 0xffffff00) |
947
pmp->pm_bpb.bpbMedia)) {
948
#ifdef MSDOSFS_DEBUG
949
printf("mountmsdosfs(): Media descriptor in BPB"
950
"does not match FAT ID\n");
951
#endif
952
brelse(bp);
953
return (EINVAL);
954
} else if (readcn == CLUST_FREE) {
955
error = usemap_free(pmp, cn);
956
if (error != 0) {
957
brelse(bp);
958
return (error);
959
}
960
}
961
}
962
if (bp != NULL)
963
brelse(bp);
964
965
for (cn = pmp->pm_maxcluster + 1; cn < (pmp->pm_maxcluster +
966
N_INUSEBITS) / N_INUSEBITS; cn++)
967
pmp->pm_inusemap[cn / N_INUSEBITS] |= 1U << (cn % N_INUSEBITS);
968
969
return (0);
970
}
971
972
/*
973
* Allocate a new cluster and chain it onto the end of the file.
974
*
975
* dep - the file to extend
976
* count - number of clusters to allocate
977
* bpp - where to return the address of the buf header for the first new
978
* file block
979
* ncp - where to put cluster number of the first newly allocated cluster
980
* If this pointer is 0, do not return the cluster number.
981
* flags - see fat.h
982
*
983
* NOTE: This function is not responsible for turning on the DE_UPDATE bit of
984
* the de_flag field of the denode and it does not change the de_FileSize
985
* field. This is left for the caller to do.
986
*/
987
int
988
extendfile(struct denode *dep, u_long count, struct buf **bpp, u_long *ncp,
989
int flags)
990
{
991
int error;
992
u_long frcn;
993
u_long cn, got;
994
struct msdosfsmount *pmp = dep->de_pmp;
995
struct buf *bp;
996
daddr_t blkno;
997
998
/*
999
* Don't try to extend the root directory
1000
*/
1001
if (dep->de_StartCluster == MSDOSFSROOT
1002
&& (dep->de_Attributes & ATTR_DIRECTORY)) {
1003
#ifdef MSDOSFS_DEBUG
1004
printf("extendfile(): attempt to extend root directory\n");
1005
#endif
1006
return (ENOSPC);
1007
}
1008
1009
/*
1010
* If the "file's last cluster" cache entry is empty, and the file
1011
* is not empty, then fill the cache entry by calling pcbmap().
1012
*/
1013
if (dep->de_fc[FC_LASTFC].fc_frcn == FCE_EMPTY &&
1014
dep->de_StartCluster != 0) {
1015
error = pcbmap(dep, 0xffff, 0, &cn, 0);
1016
/* we expect it to return E2BIG */
1017
if (error != E2BIG)
1018
return (error);
1019
}
1020
1021
dep->de_fc[FC_NEXTTOLASTFC].fc_frcn =
1022
dep->de_fc[FC_LASTFC].fc_frcn;
1023
dep->de_fc[FC_NEXTTOLASTFC].fc_fsrcn =
1024
dep->de_fc[FC_LASTFC].fc_fsrcn;
1025
while (count > 0) {
1026
/*
1027
* Allocate a new cluster chain and cat onto the end of the
1028
* file. If the file is empty we make de_StartCluster point
1029
* to the new block. Note that de_StartCluster being 0 is
1030
* sufficient to be sure the file is empty since we exclude
1031
* attempts to extend the root directory above, and the root
1032
* dir is the only file with a startcluster of 0 that has
1033
* blocks allocated (sort of).
1034
*/
1035
if (dep->de_StartCluster == 0)
1036
cn = 0;
1037
else
1038
cn = dep->de_fc[FC_LASTFC].fc_fsrcn + 1;
1039
error = clusteralloc(pmp, cn, count, CLUST_EOFE, &cn, &got);
1040
if (error)
1041
return (error);
1042
1043
count -= got;
1044
1045
/*
1046
* Give them the filesystem relative cluster number if they want
1047
* it.
1048
*/
1049
if (ncp) {
1050
*ncp = cn;
1051
ncp = NULL;
1052
}
1053
1054
if (dep->de_StartCluster == 0) {
1055
dep->de_StartCluster = cn;
1056
frcn = 0;
1057
} else {
1058
error = fatentry(FAT_SET, pmp,
1059
dep->de_fc[FC_LASTFC].fc_fsrcn,
1060
0, cn);
1061
if (error) {
1062
clusterfree(pmp, cn);
1063
return (error);
1064
}
1065
frcn = dep->de_fc[FC_LASTFC].fc_frcn + 1;
1066
}
1067
1068
/*
1069
* Update the "last cluster of the file" entry in the
1070
* denode's FAT cache.
1071
*/
1072
fc_setcache(dep, FC_LASTFC, frcn + got - 1, cn + got - 1);
1073
1074
if (flags & DE_CLEAR) {
1075
while (got-- > 0) {
1076
/*
1077
* Get the buf header for the new block of the file.
1078
*/
1079
if (dep->de_Attributes & ATTR_DIRECTORY)
1080
bp = getblk(pmp->pm_devvp,
1081
cntobn(pmp, cn++),
1082
pmp->pm_bpcluster, 0, 0, 0);
1083
else {
1084
bp = getblk(DETOV(dep),
1085
frcn++,
1086
pmp->pm_bpcluster, 0, 0, 0);
1087
/*
1088
* Do the bmap now, as in msdosfs_write
1089
*/
1090
if (pcbmap(dep,
1091
bp->b_lblkno,
1092
&blkno, 0, 0))
1093
bp->b_blkno = -1;
1094
if (bp->b_blkno == -1)
1095
panic("extendfile: pcbmap");
1096
else
1097
bp->b_blkno = blkno;
1098
}
1099
clrbuf(bp);
1100
if (bpp) {
1101
*bpp = bp;
1102
bpp = NULL;
1103
} else {
1104
bdwrite(bp);
1105
}
1106
if (vm_page_count_severe() ||
1107
buf_dirty_count_severe())
1108
vn_fsync_buf(DETOV(dep), MNT_WAIT);
1109
}
1110
}
1111
}
1112
1113
return (0);
1114
}
1115
1116
/*-
1117
* Routine to mark a FAT16 or FAT32 volume as "clean" or "dirty" by
1118
* manipulating the upper bit of the FAT entry for cluster 1. Note that
1119
* this bit is not defined for FAT12 volumes, which are always assumed to
1120
* be clean.
1121
*
1122
* The fatentry() routine only works on cluster numbers that a file could
1123
* occupy, so it won't manipulate the entry for cluster 1. So we have to do
1124
* it here. The code was stolen from fatentry() and tailored for cluster 1.
1125
*
1126
* Inputs:
1127
* pmp The MS-DOS volume to mark
1128
* dirty Non-zero if the volume should be marked dirty; zero if it
1129
* should be marked clean
1130
*
1131
* Result:
1132
* 0 Success
1133
* EROFS Volume is read-only
1134
* ? (other errors from called routines)
1135
*/
1136
int
1137
markvoldirty_upgrade(struct msdosfsmount *pmp, bool dirty, bool rw_upgrade)
1138
{
1139
struct buf *bp;
1140
u_long bn, bo, bsize, byteoffset, fatval;
1141
int error;
1142
1143
/*
1144
* FAT12 does not support a "clean" bit, so don't do anything for
1145
* FAT12.
1146
*/
1147
if (FAT12(pmp))
1148
return (0);
1149
1150
/*
1151
* Can't change the bit on a read-only filesystem, except as part of
1152
* ro->rw upgrade.
1153
*/
1154
if ((pmp->pm_flags & MSDOSFSMNT_RONLY) != 0 && !rw_upgrade)
1155
return (EROFS);
1156
1157
/*
1158
* Fetch the block containing the FAT entry. It is given by the
1159
* pseudo-cluster 1.
1160
*/
1161
byteoffset = FATOFS(pmp, 1);
1162
fatblock(pmp, byteoffset, &bn, &bsize, &bo);
1163
error = bread(pmp->pm_devvp, bn, bsize, NOCRED, &bp);
1164
if (error)
1165
return (error);
1166
1167
/*
1168
* Get the current value of the FAT entry and set/clear the relevant
1169
* bit. Dirty means clear the "clean" bit; clean means set the
1170
* "clean" bit.
1171
*/
1172
if (FAT32(pmp)) {
1173
/* FAT32 uses bit 27. */
1174
fatval = getulong(&bp->b_data[bo]);
1175
if (dirty)
1176
fatval &= 0xF7FFFFFF;
1177
else
1178
fatval |= 0x08000000;
1179
putulong(&bp->b_data[bo], fatval);
1180
} else {
1181
/* Must be FAT16; use bit 15. */
1182
fatval = getushort(&bp->b_data[bo]);
1183
if (dirty)
1184
fatval &= 0x7FFF;
1185
else
1186
fatval |= 0x8000;
1187
putushort(&bp->b_data[bo], fatval);
1188
}
1189
1190
/*
1191
* The concern here is that a devvp may be readonly, without reporting
1192
* itself as such through the usual channels. In that case, we'd like
1193
* it if attempting to mount msdosfs rw didn't panic the system.
1194
*
1195
* markvoldirty is invoked as the first write on backing devvps when
1196
* either msdosfs is mounted for the first time, or a ro mount is
1197
* upgraded to rw.
1198
*
1199
* In either event, if a write error occurs dirtying the volume:
1200
* - No user data has been permitted to be written to cache yet.
1201
* - We can abort the high-level operation (mount, or ro->rw) safely.
1202
* - We don't derive any benefit from leaving a zombie dirty buf in
1203
* the cache that can not be cleaned or evicted.
1204
*
1205
* So, mark B_INVALONERR to have bwrite() -> brelse() detect that
1206
* condition and force-invalidate our write to the block if it occurs.
1207
*
1208
* PR 210316 provides more context on the discovery and diagnosis of
1209
* the problem, as well as earlier attempts to solve it.
1210
*/
1211
bp->b_flags |= B_INVALONERR;
1212
1213
/* Write out the modified FAT block synchronously. */
1214
return (bwrite(bp));
1215
}
1216
1217