Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sbin/fsck_msdosfs/boot.c
39475 views
1
/*-
2
* SPDX-License-Identifier: BSD-2-Clause
3
*
4
* Copyright (C) 1995, 1997 Wolfgang Solfrank
5
* Copyright (c) 1995 Martin Husemann
6
*
7
* Redistribution and use in source and binary forms, with or without
8
* modification, are permitted provided that the following conditions
9
* are met:
10
* 1. Redistributions of source code must retain the above copyright
11
* notice, this list of conditions and the following disclaimer.
12
* 2. Redistributions in binary form must reproduce the above copyright
13
* notice, this list of conditions and the following disclaimer in the
14
* documentation and/or other materials provided with the distribution.
15
*
16
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
17
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19
* IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
20
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
*/
27
28
29
#include <sys/cdefs.h>
30
#ifndef lint
31
__RCSID("$NetBSD: boot.c,v 1.22 2020/01/11 16:29:07 christos Exp $");
32
#endif /* not lint */
33
34
#include <sys/param.h>
35
36
#include <stdint.h>
37
#include <stdlib.h>
38
#include <string.h>
39
#include <stdio.h>
40
#include <unistd.h>
41
42
#include "ext.h"
43
#include "fsutil.h"
44
45
int
46
readboot(int dosfs, struct bootblock *boot)
47
{
48
u_char block[DOSBOOTBLOCKSIZE];
49
u_char fsinfo[2 * DOSBOOTBLOCKSIZE];
50
int ret = FSOK;
51
52
if ((size_t)read(dosfs, block, sizeof block) != sizeof block) {
53
perr("could not read boot block");
54
return FSFATAL;
55
}
56
57
if (block[510] != 0x55 || block[511] != 0xaa) {
58
pfatal("Invalid signature in boot block: %02x%02x",
59
block[511], block[510]);
60
return FSFATAL;
61
}
62
63
memset(boot, 0, sizeof *boot);
64
boot->ValidFat = -1;
65
66
/* Decode BIOS Parameter Block */
67
68
/* Bytes per sector: can only be 512, 1024, 2048 and 4096. */
69
boot->bpbBytesPerSec = block[11] + (block[12] << 8);
70
if (boot->bpbBytesPerSec < DOSBOOTBLOCKSIZE_REAL ||
71
boot->bpbBytesPerSec > DOSBOOTBLOCKSIZE ||
72
!powerof2(boot->bpbBytesPerSec)) {
73
pfatal("Invalid sector size: %u", boot->bpbBytesPerSec);
74
return FSFATAL;
75
}
76
77
/* Sectors per cluster: can only be: 1, 2, 4, 8, 16, 32, 64, 128. */
78
boot->bpbSecPerClust = block[13];
79
if (boot->bpbSecPerClust == 0 || !powerof2(boot->bpbSecPerClust)) {
80
pfatal("Invalid cluster size: %u", boot->bpbSecPerClust);
81
return FSFATAL;
82
}
83
84
/* Reserved sectors: must be non-zero */
85
boot->bpbResSectors = block[14] + (block[15] << 8);
86
if (boot->bpbResSectors < 1) {
87
pfatal("Invalid reserved sectors: %u",
88
boot->bpbResSectors);
89
return FSFATAL;
90
}
91
92
/* Number of FATs */
93
boot->bpbFATs = block[16];
94
if (boot->bpbFATs == 0) {
95
pfatal("Invalid number of FATs: %u", boot->bpbFATs);
96
return FSFATAL;
97
}
98
99
/* Root directory entries for FAT12 and FAT16 */
100
boot->bpbRootDirEnts = block[17] + (block[18] << 8);
101
if (!boot->bpbRootDirEnts) {
102
/* bpbRootDirEnts = 0 suggests that we are FAT32 */
103
boot->flags |= FAT32;
104
}
105
106
/* Total sectors (16 bits) */
107
boot->bpbSectors = block[19] + (block[20] << 8);
108
if (boot->bpbSectors != 0 && (boot->flags & FAT32)) {
109
pfatal("Invalid 16-bit total sector count on FAT32: %u",
110
boot->bpbSectors);
111
return FSFATAL;
112
}
113
114
/* Media type: ignored */
115
boot->bpbMedia = block[21];
116
117
/* FAT12/FAT16: 16-bit count of sectors per FAT */
118
boot->bpbFATsmall = block[22] + (block[23] << 8);
119
if (boot->bpbFATsmall != 0 && (boot->flags & FAT32)) {
120
pfatal("Invalid 16-bit FAT sector count on FAT32: %u",
121
boot->bpbFATsmall);
122
return FSFATAL;
123
}
124
125
/* Legacy CHS geometry numbers: ignored */
126
boot->SecPerTrack = block[24] + (block[25] << 8);
127
boot->bpbHeads = block[26] + (block[27] << 8);
128
129
/* Hidden sectors: ignored */
130
boot->bpbHiddenSecs = block[28] + (block[29] << 8) +
131
(block[30] << 16) + (block[31] << 24);
132
133
/* Total sectors (32 bits) */
134
boot->bpbHugeSectors = block[32] + (block[33] << 8) +
135
(block[34] << 16) + (block[35] << 24);
136
if (boot->bpbHugeSectors == 0) {
137
if (boot->flags & FAT32) {
138
pfatal("FAT32 with sector count of zero");
139
return FSFATAL;
140
} else if (boot->bpbSectors == 0) {
141
pfatal("FAT with sector count of zero");
142
return FSFATAL;
143
}
144
boot->NumSectors = boot->bpbSectors;
145
} else {
146
if (boot->bpbSectors != 0) {
147
pfatal("Invalid FAT sector count");
148
return FSFATAL;
149
}
150
boot->NumSectors = boot->bpbHugeSectors;
151
}
152
153
if (boot->flags & FAT32) {
154
/* If the OEM Name field is EXFAT, it's not FAT32, so bail */
155
if (!memcmp(&block[3], "EXFAT ", 8)) {
156
pfatal("exFAT filesystem is not supported.");
157
return FSFATAL;
158
}
159
160
/* 32-bit count of sectors per FAT */
161
boot->FATsecs = block[36] + (block[37] << 8)
162
+ (block[38] << 16) + (block[39] << 24);
163
164
if (block[40] & 0x80)
165
boot->ValidFat = block[40] & 0x0f;
166
167
/* FAT32 version, bail out if not 0.0 */
168
if (block[42] || block[43]) {
169
pfatal("Unknown file system version: %x.%x",
170
block[43], block[42]);
171
return FSFATAL;
172
}
173
174
/*
175
* Cluster number of the first cluster of root directory.
176
*
177
* Should be 2 but do not require it.
178
*/
179
boot->bpbRootClust = block[44] + (block[45] << 8)
180
+ (block[46] << 16) + (block[47] << 24);
181
182
/* Sector number of the FSInfo structure, usually 1 */
183
boot->bpbFSInfo = block[48] + (block[49] << 8);
184
185
/* Sector number of the backup boot block, ignored */
186
boot->bpbBackup = block[50] + (block[51] << 8);
187
188
/* Check basic parameters */
189
if (boot->bpbFSInfo == 0) {
190
/*
191
* Either the BIOS Parameter Block has been corrupted,
192
* or this is not a FAT32 filesystem, most likely an
193
* exFAT filesystem.
194
*/
195
pfatal("Invalid FAT32 Extended BIOS Parameter Block");
196
return FSFATAL;
197
}
198
199
/* Read in and verify the FSInfo block */
200
if (lseek(dosfs, boot->bpbFSInfo * boot->bpbBytesPerSec,
201
SEEK_SET) != boot->bpbFSInfo * boot->bpbBytesPerSec
202
|| read(dosfs, fsinfo, sizeof fsinfo) != sizeof fsinfo) {
203
perr("could not read fsinfo block");
204
return FSFATAL;
205
}
206
if (memcmp(fsinfo, "RRaA", 4)
207
|| memcmp(fsinfo + 0x1e4, "rrAa", 4)
208
|| fsinfo[0x1fc]
209
|| fsinfo[0x1fd]
210
|| fsinfo[0x1fe] != 0x55
211
|| fsinfo[0x1ff] != 0xaa
212
|| fsinfo[0x3fc]
213
|| fsinfo[0x3fd]
214
|| fsinfo[0x3fe] != 0x55
215
|| fsinfo[0x3ff] != 0xaa) {
216
pwarn("Invalid signature in fsinfo block\n");
217
if (ask(0, "Fix")) {
218
memcpy(fsinfo, "RRaA", 4);
219
memcpy(fsinfo + 0x1e4, "rrAa", 4);
220
fsinfo[0x1fc] = fsinfo[0x1fd] = 0;
221
fsinfo[0x1fe] = 0x55;
222
fsinfo[0x1ff] = 0xaa;
223
fsinfo[0x3fc] = fsinfo[0x3fd] = 0;
224
fsinfo[0x3fe] = 0x55;
225
fsinfo[0x3ff] = 0xaa;
226
if (lseek(dosfs, boot->bpbFSInfo *
227
boot->bpbBytesPerSec, SEEK_SET)
228
!= boot->bpbFSInfo * boot->bpbBytesPerSec
229
|| write(dosfs, fsinfo, sizeof fsinfo)
230
!= sizeof fsinfo) {
231
perr("Unable to write bpbFSInfo");
232
return FSFATAL;
233
}
234
ret = FSBOOTMOD;
235
} else
236
boot->bpbFSInfo = 0;
237
} else {
238
/* We appear to have a valid FSInfo block, decode */
239
boot->FSFree = fsinfo[0x1e8] + (fsinfo[0x1e9] << 8)
240
+ (fsinfo[0x1ea] << 16)
241
+ (fsinfo[0x1eb] << 24);
242
boot->FSNext = fsinfo[0x1ec] + (fsinfo[0x1ed] << 8)
243
+ (fsinfo[0x1ee] << 16)
244
+ (fsinfo[0x1ef] << 24);
245
}
246
} else {
247
/* !FAT32: FAT12/FAT16 */
248
boot->FATsecs = boot->bpbFATsmall;
249
}
250
251
if (boot->FATsecs < 1 || boot->FATsecs > UINT32_MAX / boot->bpbFATs) {
252
pfatal("Invalid FATs(%u) with FATsecs(%zu)",
253
boot->bpbFATs, (size_t)boot->FATsecs);
254
return FSFATAL;
255
}
256
257
boot->FirstCluster = (boot->bpbRootDirEnts * 32 +
258
boot->bpbBytesPerSec - 1) / boot->bpbBytesPerSec +
259
boot->bpbResSectors + boot->bpbFATs * boot->FATsecs;
260
261
if (boot->FirstCluster + boot->bpbSecPerClust > boot->NumSectors) {
262
pfatal("Cluster offset too large (%u clusters)\n",
263
boot->FirstCluster);
264
return FSFATAL;
265
}
266
267
/*
268
* The number of clusters is derived from available data sectors,
269
* divided by sectors per cluster.
270
*/
271
boot->NumClusters =
272
(boot->NumSectors - boot->FirstCluster) / boot->bpbSecPerClust;
273
274
if (boot->flags & FAT32) {
275
if (boot->NumClusters > (CLUST_RSRVD & CLUST32_MASK)) {
276
pfatal("Filesystem too big (%u clusters) for FAT32 partition",
277
boot->NumClusters);
278
return FSFATAL;
279
}
280
if (boot->NumClusters < (CLUST_RSRVD & CLUST16_MASK)) {
281
pfatal("Filesystem too small (%u clusters) for FAT32 partition",
282
boot->NumClusters);
283
return FSFATAL;
284
}
285
boot->ClustMask = CLUST32_MASK;
286
287
if (boot->bpbRootClust < CLUST_FIRST ||
288
boot->bpbRootClust >= boot->NumClusters) {
289
pfatal("Root directory starts with cluster out of range(%u)",
290
boot->bpbRootClust);
291
return FSFATAL;
292
}
293
} else if (boot->NumClusters < (CLUST_RSRVD&CLUST12_MASK)) {
294
boot->ClustMask = CLUST12_MASK;
295
} else if (boot->NumClusters < (CLUST_RSRVD&CLUST16_MASK)) {
296
boot->ClustMask = CLUST16_MASK;
297
} else {
298
pfatal("Filesystem too big (%u clusters) for non-FAT32 partition",
299
boot->NumClusters);
300
return FSFATAL;
301
}
302
303
switch (boot->ClustMask) {
304
case CLUST32_MASK:
305
boot->NumFatEntries = (boot->FATsecs * boot->bpbBytesPerSec) / 4;
306
break;
307
case CLUST16_MASK:
308
boot->NumFatEntries = (boot->FATsecs * boot->bpbBytesPerSec) / 2;
309
break;
310
default:
311
boot->NumFatEntries = (boot->FATsecs * boot->bpbBytesPerSec * 2) / 3;
312
break;
313
}
314
315
if (boot->NumFatEntries < boot->NumClusters) {
316
pfatal("FAT size too small, %u entries won't fit into %u sectors\n",
317
boot->NumClusters, boot->FATsecs);
318
return FSFATAL;
319
}
320
321
/*
322
* There are two reserved clusters. To avoid adding CLUST_FIRST every
323
* time we perform boundary checks, we increment the NumClusters by 2,
324
* which is CLUST_FIRST to denote the first out-of-range cluster number.
325
*/
326
boot->NumClusters += CLUST_FIRST;
327
328
boot->ClusterSize = boot->bpbBytesPerSec * boot->bpbSecPerClust;
329
330
boot->NumFiles = 1;
331
boot->NumFree = 0;
332
333
return ret;
334
}
335
336
int
337
writefsinfo(int dosfs, struct bootblock *boot)
338
{
339
u_char fsinfo[2 * DOSBOOTBLOCKSIZE];
340
341
if (lseek(dosfs, boot->bpbFSInfo * boot->bpbBytesPerSec, SEEK_SET)
342
!= boot->bpbFSInfo * boot->bpbBytesPerSec
343
|| read(dosfs, fsinfo, sizeof fsinfo) != sizeof fsinfo) {
344
perr("could not read fsinfo block");
345
return FSFATAL;
346
}
347
fsinfo[0x1e8] = (u_char)boot->FSFree;
348
fsinfo[0x1e9] = (u_char)(boot->FSFree >> 8);
349
fsinfo[0x1ea] = (u_char)(boot->FSFree >> 16);
350
fsinfo[0x1eb] = (u_char)(boot->FSFree >> 24);
351
fsinfo[0x1ec] = (u_char)boot->FSNext;
352
fsinfo[0x1ed] = (u_char)(boot->FSNext >> 8);
353
fsinfo[0x1ee] = (u_char)(boot->FSNext >> 16);
354
fsinfo[0x1ef] = (u_char)(boot->FSNext >> 24);
355
if (lseek(dosfs, boot->bpbFSInfo * boot->bpbBytesPerSec, SEEK_SET)
356
!= boot->bpbFSInfo * boot->bpbBytesPerSec
357
|| write(dosfs, fsinfo, sizeof fsinfo)
358
!= sizeof fsinfo) {
359
perr("Unable to write bpbFSInfo");
360
return FSFATAL;
361
}
362
/*
363
* Technically, we should return FSBOOTMOD here.
364
*
365
* However, since Win95 OSR2 (the first M$ OS that has
366
* support for FAT32) doesn't maintain the FSINFO block
367
* correctly, it has to be fixed pretty often.
368
*
369
* Therefore, we handle the FSINFO block only informally,
370
* fixing it if necessary, but otherwise ignoring the
371
* fact that it was incorrect.
372
*/
373
return 0;
374
}
375
376