1 |
|
|
/* $OpenBSD: boot.c,v 1.24 2016/10/10 00:34:50 bluhm Exp $ */ |
2 |
|
|
/* $NetBSD: boot.c,v 1.5 1997/10/17 11:19:23 ws Exp $ */ |
3 |
|
|
|
4 |
|
|
/* |
5 |
|
|
* Copyright (C) 1995, 1997 Wolfgang Solfrank |
6 |
|
|
* Copyright (c) 1995 Martin Husemann |
7 |
|
|
* |
8 |
|
|
* Redistribution and use in source and binary forms, with or without |
9 |
|
|
* modification, are permitted provided that the following conditions |
10 |
|
|
* are met: |
11 |
|
|
* 1. Redistributions of source code must retain the above copyright |
12 |
|
|
* notice, this list of conditions and the following disclaimer. |
13 |
|
|
* 2. Redistributions in binary form must reproduce the above copyright |
14 |
|
|
* notice, this list of conditions and the following disclaimer in the |
15 |
|
|
* documentation and/or other materials provided with the distribution. |
16 |
|
|
* |
17 |
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR |
18 |
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
19 |
|
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
20 |
|
|
* IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
21 |
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
22 |
|
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
23 |
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
24 |
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
25 |
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
26 |
|
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
27 |
|
|
*/ |
28 |
|
|
|
29 |
|
|
#include <sys/param.h> /* DEV_BSIZE powerof2 */ |
30 |
|
|
#include <sys/disklabel.h> |
31 |
|
|
|
32 |
|
|
#include <stdlib.h> |
33 |
|
|
#include <string.h> |
34 |
|
|
#include <ctype.h> |
35 |
|
|
#include <stdio.h> |
36 |
|
|
#include <unistd.h> |
37 |
|
|
|
38 |
|
|
#include "ext.h" |
39 |
|
|
|
40 |
|
|
int |
41 |
|
|
readboot(int dosfs, struct bootblock *boot) |
42 |
|
|
{ |
43 |
|
|
u_char *block = NULL; |
44 |
|
|
u_char *fsinfo = NULL; |
45 |
|
|
u_char *backup = NULL; |
46 |
|
|
int ret = FSOK, secsize = lab.d_secsize, fsinfosz; |
47 |
|
|
off_t o; |
48 |
|
|
ssize_t n; |
49 |
|
|
|
50 |
|
|
if (secsize < DOSBOOTBLOCKSIZE) { |
51 |
|
|
xperror("sector size < DOSBOOTBLOCKSIZE"); |
52 |
|
|
goto fail; |
53 |
|
|
} |
54 |
|
|
if (DOSBOOTBLOCKSIZE != DEV_BSIZE) { |
55 |
|
|
xperror("DOSBOOTBLOCKSIZE != DEV_BSIZE"); |
56 |
|
|
goto fail; |
57 |
|
|
} |
58 |
|
|
|
59 |
|
|
block = malloc(secsize); |
60 |
|
|
if (block == NULL) { |
61 |
|
|
xperror("could not malloc boot block"); |
62 |
|
|
goto fail; |
63 |
|
|
} |
64 |
|
|
|
65 |
|
|
if ((o = lseek(dosfs, 0, SEEK_SET)) == -1) { |
66 |
|
|
xperror("could not seek boot block"); |
67 |
|
|
goto fail; |
68 |
|
|
} |
69 |
|
|
|
70 |
|
|
n = read(dosfs, block, secsize); |
71 |
|
|
if (n == -1 || n != secsize) { |
72 |
|
|
xperror("could not read boot block"); |
73 |
|
|
goto fail; |
74 |
|
|
} |
75 |
|
|
|
76 |
|
|
if (block[510] != 0x55 || block[511] != 0xaa) { |
77 |
|
|
pfatal("Invalid signature in boot block: %02x%02x\n", |
78 |
|
|
block[511], block[510]); |
79 |
|
|
} |
80 |
|
|
|
81 |
|
|
memset(boot, 0, sizeof *boot); |
82 |
|
|
boot->ValidFat = -1; |
83 |
|
|
|
84 |
|
|
/* decode bios parameter block */ |
85 |
|
|
boot->BytesPerSec = block[11] + (block[12] << 8); |
86 |
|
|
if (boot->BytesPerSec == 0 || boot->BytesPerSec != secsize) { |
87 |
|
|
pfatal("Invalid sector size: %u\n", boot->BytesPerSec); |
88 |
|
|
goto fail; |
89 |
|
|
} |
90 |
|
|
boot->SecPerClust = block[13]; |
91 |
|
|
if (boot->SecPerClust == 0 || !powerof2(boot->SecPerClust)) { |
92 |
|
|
pfatal("Invalid cluster size: %u\n", boot->SecPerClust); |
93 |
|
|
goto fail; |
94 |
|
|
} |
95 |
|
|
boot->ResSectors = block[14] + (block[15] << 8); |
96 |
|
|
boot->FATs = block[16]; |
97 |
|
|
if (boot->FATs == 0) { |
98 |
|
|
pfatal("Invalid number of FATs: %u\n", boot->FATs); |
99 |
|
|
goto fail; |
100 |
|
|
} |
101 |
|
|
boot->RootDirEnts = block[17] + (block[18] << 8); |
102 |
|
|
boot->Sectors = block[19] + (block[20] << 8); |
103 |
|
|
boot->Media = block[21]; |
104 |
|
|
boot->FATsmall = block[22] + (block[23] << 8); |
105 |
|
|
boot->SecPerTrack = block[24] + (block[25] << 8); |
106 |
|
|
boot->Heads = block[26] + (block[27] << 8); |
107 |
|
|
boot->HiddenSecs = block[28] + (block[29] << 8) + (block[30] << 16) + (block[31] << 24); |
108 |
|
|
boot->HugeSectors = block[32] + (block[33] << 8) + (block[34] << 16) + (block[35] << 24); |
109 |
|
|
|
110 |
|
|
boot->FATsecs = boot->FATsmall; |
111 |
|
|
|
112 |
|
|
if (!boot->RootDirEnts) { |
113 |
|
|
boot->flags |= FAT32; |
114 |
|
|
boot->FATsecs = block[36] + (block[37] << 8) |
115 |
|
|
+ (block[38] << 16) + (block[39] << 24); |
116 |
|
|
if (block[40] & 0x80) |
117 |
|
|
boot->ValidFat = block[40] & 0x0f; |
118 |
|
|
|
119 |
|
|
/* check version number: */ |
120 |
|
|
if (block[42] || block[43]) { |
121 |
|
|
/* Correct? XXX */ |
122 |
|
|
pfatal("Unknown filesystem version: %x.%x\n", |
123 |
|
|
block[43], block[42]); |
124 |
|
|
goto fail; |
125 |
|
|
} |
126 |
|
|
boot->RootCl = block[44] + (block[45] << 8) |
127 |
|
|
+ (block[46] << 16) + (block[47] << 24); |
128 |
|
|
boot->FSInfo = block[48] + (block[49] << 8); |
129 |
|
|
boot->Backup = block[50] + (block[51] << 8); |
130 |
|
|
|
131 |
|
|
o = lseek(dosfs, boot->FSInfo * secsize, SEEK_SET); |
132 |
|
|
if (o == -1 || o != boot->FSInfo * secsize) { |
133 |
|
|
xperror("could not seek fsinfo block"); |
134 |
|
|
goto fail; |
135 |
|
|
} |
136 |
|
|
|
137 |
|
|
if ((2 * DOSBOOTBLOCKSIZE) < secsize) |
138 |
|
|
fsinfosz = secsize; |
139 |
|
|
else |
140 |
|
|
fsinfosz = 2 * secsize; |
141 |
|
|
fsinfo = malloc(fsinfosz); |
142 |
|
|
if (fsinfo == NULL) { |
143 |
|
|
xperror("could not malloc fsinfo"); |
144 |
|
|
goto fail; |
145 |
|
|
} |
146 |
|
|
n = read(dosfs, fsinfo, fsinfosz); |
147 |
|
|
if (n == -1 || n != fsinfosz) { |
148 |
|
|
xperror("could not read fsinfo block"); |
149 |
|
|
goto fail; |
150 |
|
|
} |
151 |
|
|
|
152 |
|
|
if (memcmp(fsinfo, "RRaA", 4) |
153 |
|
|
|| memcmp(fsinfo + 0x1e4, "rrAa", 4) |
154 |
|
|
|| fsinfo[0x1fc] |
155 |
|
|
|| fsinfo[0x1fd] |
156 |
|
|
|| fsinfo[0x1fe] != 0x55 |
157 |
|
|
|| fsinfo[0x1ff] != 0xaa |
158 |
|
|
|| fsinfo[0x3fc] |
159 |
|
|
|| fsinfo[0x3fd] |
160 |
|
|
|| fsinfo[0x3fe] != 0x55 |
161 |
|
|
|| fsinfo[0x3ff] != 0xaa) { |
162 |
|
|
pwarn("Invalid signature in fsinfo block\n"); |
163 |
|
|
if (ask(0, "fix")) { |
164 |
|
|
memcpy(fsinfo, "RRaA", 4); |
165 |
|
|
memcpy(fsinfo + 0x1e4, "rrAa", 4); |
166 |
|
|
fsinfo[0x1fc] = fsinfo[0x1fd] = 0; |
167 |
|
|
fsinfo[0x1fe] = 0x55; |
168 |
|
|
fsinfo[0x1ff] = 0xaa; |
169 |
|
|
fsinfo[0x3fc] = fsinfo[0x3fd] = 0; |
170 |
|
|
fsinfo[0x3fe] = 0x55; |
171 |
|
|
fsinfo[0x3ff] = 0xaa; |
172 |
|
|
|
173 |
|
|
o = lseek(dosfs, boot->FSInfo * secsize, |
174 |
|
|
SEEK_SET); |
175 |
|
|
if (o == -1 || o != boot->FSInfo * secsize) { |
176 |
|
|
xperror("Unable to seek FSInfo"); |
177 |
|
|
goto fail; |
178 |
|
|
} |
179 |
|
|
n = write(dosfs, fsinfo, fsinfosz); |
180 |
|
|
if (n == -1 || n != fsinfosz) { |
181 |
|
|
xperror("Unable to write FSInfo"); |
182 |
|
|
goto fail; |
183 |
|
|
} |
184 |
|
|
ret = FSBOOTMOD; |
185 |
|
|
} else |
186 |
|
|
boot->FSInfo = 0; |
187 |
|
|
} |
188 |
|
|
if (boot->FSInfo) { |
189 |
|
|
boot->FSFree = fsinfo[0x1e8] + (fsinfo[0x1e9] << 8) |
190 |
|
|
+ (fsinfo[0x1ea] << 16) |
191 |
|
|
+ (fsinfo[0x1eb] << 24); |
192 |
|
|
boot->FSNext = fsinfo[0x1ec] + (fsinfo[0x1ed] << 8) |
193 |
|
|
+ (fsinfo[0x1ee] << 16) |
194 |
|
|
+ (fsinfo[0x1ef] << 24); |
195 |
|
|
} |
196 |
|
|
|
197 |
|
|
o = lseek(dosfs, boot->Backup * secsize, SEEK_SET); |
198 |
|
|
if (o == -1 || o != boot->Backup * secsize) { |
199 |
|
|
xperror("could not seek backup bootblock"); |
200 |
|
|
goto fail; |
201 |
|
|
} |
202 |
|
|
backup = malloc(2 * secsize); /* In case we check fsinfo. */ |
203 |
|
|
if (backup == NULL) { |
204 |
|
|
xperror("could not malloc backup boot block"); |
205 |
|
|
goto fail; |
206 |
|
|
} |
207 |
|
|
n = read(dosfs, backup, secsize); |
208 |
|
|
if (n == -1 || n != secsize) { |
209 |
|
|
xperror("could not read backup bootblock"); |
210 |
|
|
goto fail; |
211 |
|
|
} |
212 |
|
|
|
213 |
|
|
/* |
214 |
|
|
* Check that the backup boot block matches the primary one. |
215 |
|
|
* We don't check every byte, since some vendor utilities |
216 |
|
|
* seem to overwrite the boot code when they feel like it, |
217 |
|
|
* without changing the backup block. Specifically, we check |
218 |
|
|
* the two-byte signature at the end, the BIOS parameter |
219 |
|
|
* block (which starts after the 3-byte JMP and the 8-byte |
220 |
|
|
* OEM name/version) and the filesystem information that |
221 |
|
|
* follows the BPB (bsBPB[53] and bsExt[26] for FAT32, so we |
222 |
|
|
* check 79 bytes). |
223 |
|
|
*/ |
224 |
|
|
if (backup[510] != 0x55 || backup[511] != 0xaa) { |
225 |
|
|
pfatal("Invalid signature in backup boot block: %02x%02x\n", backup[511], backup[510]); |
226 |
|
|
} |
227 |
|
|
if (memcmp(block + 11, backup + 11, 79)) { |
228 |
|
|
pfatal("backup doesn't compare to primary bootblock\n"); |
229 |
|
|
goto fail; |
230 |
|
|
} |
231 |
|
|
/* Check backup FSInfo? XXX */ |
232 |
|
|
} |
233 |
|
|
|
234 |
|
|
if (boot->FATsecs == 0) { |
235 |
|
|
pfatal("Invalid number of FAT sectors: %u\n", boot->FATsecs); |
236 |
|
|
goto fail; |
237 |
|
|
} |
238 |
|
|
|
239 |
|
|
boot->ClusterOffset = (boot->RootDirEnts * 32 + secsize - 1) |
240 |
|
|
/ secsize |
241 |
|
|
+ boot->ResSectors |
242 |
|
|
+ boot->FATs * boot->FATsecs |
243 |
|
|
- CLUST_FIRST * boot->SecPerClust; |
244 |
|
|
|
245 |
|
|
if (boot->Sectors) { |
246 |
|
|
boot->HugeSectors = 0; |
247 |
|
|
boot->NumSectors = boot->Sectors; |
248 |
|
|
} else |
249 |
|
|
boot->NumSectors = boot->HugeSectors; |
250 |
|
|
|
251 |
|
|
if (boot->ClusterOffset > boot->NumSectors) { |
252 |
|
|
pfatal("Cluster offset too large (%u clusters)\n", |
253 |
|
|
boot->ClusterOffset); |
254 |
|
|
goto fail; |
255 |
|
|
} |
256 |
|
|
boot->NumClusters = (boot->NumSectors - boot->ClusterOffset) / boot->SecPerClust; |
257 |
|
|
|
258 |
|
|
if (boot->flags&FAT32) |
259 |
|
|
boot->ClustMask = CLUST32_MASK; |
260 |
|
|
else if (boot->NumClusters < (CLUST_RSRVD&CLUST12_MASK)) |
261 |
|
|
boot->ClustMask = CLUST12_MASK; |
262 |
|
|
else if (boot->NumClusters < (CLUST_RSRVD&CLUST16_MASK)) |
263 |
|
|
boot->ClustMask = CLUST16_MASK; |
264 |
|
|
else { |
265 |
|
|
pfatal("Filesystem too big (%u clusters) for non-FAT32 partition\n", |
266 |
|
|
boot->NumClusters); |
267 |
|
|
goto fail; |
268 |
|
|
} |
269 |
|
|
|
270 |
|
|
switch (boot->ClustMask) { |
271 |
|
|
case CLUST32_MASK: |
272 |
|
|
boot->NumFatEntries = (boot->FATsecs * secsize) / 4; |
273 |
|
|
break; |
274 |
|
|
case CLUST16_MASK: |
275 |
|
|
boot->NumFatEntries = (boot->FATsecs * secsize) / 2; |
276 |
|
|
break; |
277 |
|
|
default: |
278 |
|
|
boot->NumFatEntries = (boot->FATsecs * secsize * 2) / 3; |
279 |
|
|
break; |
280 |
|
|
} |
281 |
|
|
|
282 |
|
|
if (boot->NumFatEntries < boot->NumClusters) { |
283 |
|
|
pfatal("FAT size too small, %u entries won't fit into %u sectors\n", |
284 |
|
|
boot->NumClusters, boot->FATsecs); |
285 |
|
|
goto fail; |
286 |
|
|
} |
287 |
|
|
boot->ClusterSize = boot->SecPerClust * secsize; |
288 |
|
|
|
289 |
|
|
boot->NumFiles = 1; |
290 |
|
|
boot->NumFree = 0; |
291 |
|
|
|
292 |
|
|
free(backup); |
293 |
|
|
free(block); |
294 |
|
|
free(fsinfo); |
295 |
|
|
return ret; |
296 |
|
|
fail: |
297 |
|
|
free(backup); |
298 |
|
|
free(block); |
299 |
|
|
free(fsinfo); |
300 |
|
|
return FSFATAL; |
301 |
|
|
} |
302 |
|
|
|
303 |
|
|
int |
304 |
|
|
writefsinfo(int dosfs, struct bootblock *boot) |
305 |
|
|
{ |
306 |
|
|
u_char *fsinfo = NULL; |
307 |
|
|
int secsize = lab.d_secsize, fsinfosz; |
308 |
|
|
off_t o; |
309 |
|
|
ssize_t n; |
310 |
|
|
|
311 |
|
|
if ((2 * DOSBOOTBLOCKSIZE) < secsize) |
312 |
|
|
fsinfosz = secsize; |
313 |
|
|
else |
314 |
|
|
fsinfosz = 2 * secsize; |
315 |
|
|
|
316 |
|
|
fsinfo = malloc(fsinfosz); |
317 |
|
|
if (fsinfo == NULL) { |
318 |
|
|
xperror("could not malloc fsinfo block"); |
319 |
|
|
goto fail; |
320 |
|
|
} |
321 |
|
|
|
322 |
|
|
o = lseek(dosfs, boot->FSInfo * secsize, SEEK_SET); |
323 |
|
|
if (o == -1 || o != boot->FSInfo * secsize) { |
324 |
|
|
xperror("could not seek fsinfo block"); |
325 |
|
|
goto fail; |
326 |
|
|
} |
327 |
|
|
|
328 |
|
|
n = read(dosfs, fsinfo, fsinfosz); |
329 |
|
|
if (n == -1 || n != fsinfosz) { |
330 |
|
|
xperror("could not read fsinfo block"); |
331 |
|
|
goto fail; |
332 |
|
|
} |
333 |
|
|
|
334 |
|
|
fsinfo[0x1e8] = (u_char)boot->FSFree; |
335 |
|
|
fsinfo[0x1e9] = (u_char)(boot->FSFree >> 8); |
336 |
|
|
fsinfo[0x1ea] = (u_char)(boot->FSFree >> 16); |
337 |
|
|
fsinfo[0x1eb] = (u_char)(boot->FSFree >> 24); |
338 |
|
|
fsinfo[0x1ec] = (u_char)boot->FSNext; |
339 |
|
|
fsinfo[0x1ed] = (u_char)(boot->FSNext >> 8); |
340 |
|
|
fsinfo[0x1ee] = (u_char)(boot->FSNext >> 16); |
341 |
|
|
fsinfo[0x1ef] = (u_char)(boot->FSNext >> 24); |
342 |
|
|
|
343 |
|
|
o = lseek(dosfs, o, SEEK_SET); |
344 |
|
|
if (o == -1 || o != boot->FSInfo * boot->BytesPerSec) { |
345 |
|
|
xperror("Unable to seek FSInfo"); |
346 |
|
|
goto fail; |
347 |
|
|
} |
348 |
|
|
n = write(dosfs, fsinfo, fsinfosz); |
349 |
|
|
if (n == -1 || n != fsinfosz) { |
350 |
|
|
xperror("Unable to write FSInfo"); |
351 |
|
|
goto fail; |
352 |
|
|
} |
353 |
|
|
|
354 |
|
|
free(fsinfo); |
355 |
|
|
|
356 |
|
|
/* |
357 |
|
|
* Technically, we should return FSBOOTMOD here. |
358 |
|
|
* |
359 |
|
|
* However, since Win95 OSR2 (the first M$ OS that has |
360 |
|
|
* support for FAT32) doesn't maintain the FSINFO block |
361 |
|
|
* correctly, it has to be fixed pretty often. |
362 |
|
|
* |
363 |
|
|
* Therefore, we handle the FSINFO block only informally, |
364 |
|
|
* fixing it if necessary, but otherwise ignoring the |
365 |
|
|
* fact that it was incorrect. |
366 |
|
|
*/ |
367 |
|
|
return 0; |
368 |
|
|
fail: |
369 |
|
|
free(fsinfo); |
370 |
|
|
return FSFATAL; |
371 |
|
|
} |