xref: /openbsd/sbin/fsck_msdos/boot.c (revision ba4520cc)
1*ba4520ccSbluhm /*	$OpenBSD: boot.c,v 1.24 2016/10/10 00:34:50 bluhm Exp $	*/
2b099d67bSprovos /*	$NetBSD: boot.c,v 1.5 1997/10/17 11:19:23 ws Exp $	*/
39646ab25Sderaadt 
49646ab25Sderaadt /*
5b099d67bSprovos  * Copyright (C) 1995, 1997 Wolfgang Solfrank
69646ab25Sderaadt  * Copyright (c) 1995 Martin Husemann
79646ab25Sderaadt  *
89646ab25Sderaadt  * Redistribution and use in source and binary forms, with or without
99646ab25Sderaadt  * modification, are permitted provided that the following conditions
109646ab25Sderaadt  * are met:
119646ab25Sderaadt  * 1. Redistributions of source code must retain the above copyright
129646ab25Sderaadt  *    notice, this list of conditions and the following disclaimer.
139646ab25Sderaadt  * 2. Redistributions in binary form must reproduce the above copyright
149646ab25Sderaadt  *    notice, this list of conditions and the following disclaimer in the
159646ab25Sderaadt  *    documentation and/or other materials provided with the distribution.
169646ab25Sderaadt  *
179646ab25Sderaadt  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
189646ab25Sderaadt  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
199646ab25Sderaadt  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
209646ab25Sderaadt  * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
219646ab25Sderaadt  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
229646ab25Sderaadt  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
239646ab25Sderaadt  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
249646ab25Sderaadt  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
259646ab25Sderaadt  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
269646ab25Sderaadt  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
279646ab25Sderaadt  */
289646ab25Sderaadt 
29b9fc9a72Sderaadt #include <sys/param.h>	/* DEV_BSIZE powerof2 */
3098fba8a9Skrw #include <sys/disklabel.h>
3198fba8a9Skrw 
329646ab25Sderaadt #include <stdlib.h>
339646ab25Sderaadt #include <string.h>
349646ab25Sderaadt #include <ctype.h>
359646ab25Sderaadt #include <stdio.h>
369646ab25Sderaadt #include <unistd.h>
379646ab25Sderaadt 
389646ab25Sderaadt #include "ext.h"
399646ab25Sderaadt 
409646ab25Sderaadt int
readboot(int dosfs,struct bootblock * boot)414e95fccfSderaadt readboot(int dosfs, struct bootblock *boot)
429646ab25Sderaadt {
43fb704eb6Stobias 	u_char *block = NULL;
44fb704eb6Stobias 	u_char *fsinfo = NULL;
45fb704eb6Stobias 	u_char *backup = NULL;
4698fba8a9Skrw 	int ret = FSOK, secsize = lab.d_secsize, fsinfosz;
47553e4131Sderaadt 	off_t o;
48553e4131Sderaadt 	ssize_t n;
499646ab25Sderaadt 
5098fba8a9Skrw 	if (secsize < DOSBOOTBLOCKSIZE) {
5198fba8a9Skrw 		xperror("sector size < DOSBOOTBLOCKSIZE");
52fb704eb6Stobias 		goto fail;
5398fba8a9Skrw 	}
5498fba8a9Skrw 	if (DOSBOOTBLOCKSIZE != DEV_BSIZE) {
5598fba8a9Skrw 		xperror("DOSBOOTBLOCKSIZE != DEV_BSIZE");
56fb704eb6Stobias 		goto fail;
5798fba8a9Skrw 	}
5898fba8a9Skrw 
5998fba8a9Skrw 	block = malloc(secsize);
6098fba8a9Skrw 	if (block == NULL) {
6198fba8a9Skrw 		xperror("could not malloc boot block");
62fb704eb6Stobias 		goto fail;
6398fba8a9Skrw 	}
6498fba8a9Skrw 
6598fba8a9Skrw 	if ((o = lseek(dosfs, 0, SEEK_SET)) == -1) {
6698fba8a9Skrw 		xperror("could not seek boot block");
67fb704eb6Stobias 		goto fail;
6898fba8a9Skrw 	}
6998fba8a9Skrw 
7098fba8a9Skrw 	n = read(dosfs, block, secsize);
7198fba8a9Skrw 	if (n == -1 || n != secsize) {
72dd133929Sthib 		xperror("could not read boot block");
73fb704eb6Stobias 		goto fail;
749646ab25Sderaadt 	}
759646ab25Sderaadt 
76b099d67bSprovos 	if (block[510] != 0x55 || block[511] != 0xaa) {
7798fba8a9Skrw 		pfatal("Invalid signature in boot block: %02x%02x\n",
7898fba8a9Skrw 		    block[511], block[510]);
79b099d67bSprovos 	}
80b099d67bSprovos 
81b099d67bSprovos 	memset(boot, 0, sizeof *boot);
82b099d67bSprovos 	boot->ValidFat = -1;
83b099d67bSprovos 
849646ab25Sderaadt 	/* decode bios parameter block */
859646ab25Sderaadt 	boot->BytesPerSec = block[11] + (block[12] << 8);
8698fba8a9Skrw 	if (boot->BytesPerSec == 0 || boot->BytesPerSec != secsize) {
8798fba8a9Skrw 		pfatal("Invalid sector size: %u\n", boot->BytesPerSec);
88fb704eb6Stobias 		goto fail;
8998fba8a9Skrw 	}
909646ab25Sderaadt 	boot->SecPerClust = block[13];
915f989d62Stobias 	if (boot->SecPerClust == 0 || !powerof2(boot->SecPerClust)) {
9298fba8a9Skrw 		pfatal("Invalid cluster size: %u\n", boot->SecPerClust);
93fb704eb6Stobias 		goto fail;
9498fba8a9Skrw 	}
959646ab25Sderaadt 	boot->ResSectors = block[14] + (block[15] << 8);
969646ab25Sderaadt 	boot->FATs = block[16];
973bd633b3Stobias 	if (boot->FATs == 0) {
983bd633b3Stobias 		pfatal("Invalid number of FATs: %u\n", boot->FATs);
993bd633b3Stobias 		goto fail;
1003bd633b3Stobias 	}
1019646ab25Sderaadt 	boot->RootDirEnts = block[17] + (block[18] << 8);
1029646ab25Sderaadt 	boot->Sectors = block[19] + (block[20] << 8);
1039646ab25Sderaadt 	boot->Media = block[21];
104b099d67bSprovos 	boot->FATsmall = block[22] + (block[23] << 8);
1059646ab25Sderaadt 	boot->SecPerTrack = block[24] + (block[25] << 8);
1069646ab25Sderaadt 	boot->Heads = block[26] + (block[27] << 8);
1079646ab25Sderaadt 	boot->HiddenSecs = block[28] + (block[29] << 8) + (block[30] << 16) + (block[31] << 24);
1089646ab25Sderaadt 	boot->HugeSectors = block[32] + (block[33] << 8) + (block[34] << 16) + (block[35] << 24);
109b099d67bSprovos 
110b099d67bSprovos 	boot->FATsecs = boot->FATsmall;
111b099d67bSprovos 
11298fba8a9Skrw 	if (!boot->RootDirEnts) {
113b099d67bSprovos 		boot->flags |= FAT32;
114b099d67bSprovos 		boot->FATsecs = block[36] + (block[37] << 8)
115b099d67bSprovos 				+ (block[38] << 16) + (block[39] << 24);
116b099d67bSprovos 		if (block[40] & 0x80)
117b099d67bSprovos 			boot->ValidFat = block[40] & 0x0f;
118b099d67bSprovos 
119b099d67bSprovos 		/* check version number: */
120b099d67bSprovos 		if (block[42] || block[43]) {
121b099d67bSprovos 			/* Correct?				XXX */
12262d85a42Sian 			pfatal("Unknown filesystem version: %x.%x\n",
123b099d67bSprovos 			       block[43], block[42]);
124fb704eb6Stobias 			goto fail;
125b099d67bSprovos 		}
126b099d67bSprovos 		boot->RootCl = block[44] + (block[45] << 8)
127b099d67bSprovos 			       + (block[46] << 16) + (block[47] << 24);
128b099d67bSprovos 		boot->FSInfo = block[48] + (block[49] << 8);
129b099d67bSprovos 		boot->Backup = block[50] + (block[51] << 8);
130b099d67bSprovos 
13198fba8a9Skrw 		o = lseek(dosfs, boot->FSInfo * secsize, SEEK_SET);
13298fba8a9Skrw 		if (o == -1 || o != boot->FSInfo * secsize) {
13398fba8a9Skrw 			xperror("could not seek fsinfo block");
134fb704eb6Stobias 			goto fail;
13598fba8a9Skrw 		}
13698fba8a9Skrw 
13798fba8a9Skrw 		if ((2 * DOSBOOTBLOCKSIZE) < secsize)
13898fba8a9Skrw 			fsinfosz = secsize;
13998fba8a9Skrw 		else
14098fba8a9Skrw 			fsinfosz = 2 * secsize;
14198fba8a9Skrw 		fsinfo = malloc(fsinfosz);
14298fba8a9Skrw 		if (fsinfo == NULL) {
14398fba8a9Skrw 			xperror("could not malloc fsinfo");
144fb704eb6Stobias 			goto fail;
14598fba8a9Skrw 		}
14698fba8a9Skrw 		n = read(dosfs, fsinfo, fsinfosz);
14798fba8a9Skrw 		if (n == -1 || n != fsinfosz) {
148dd133929Sthib 			xperror("could not read fsinfo block");
149fb704eb6Stobias 			goto fail;
150b099d67bSprovos 		}
15198fba8a9Skrw 
152b099d67bSprovos 		if (memcmp(fsinfo, "RRaA", 4)
153b099d67bSprovos 		    || memcmp(fsinfo + 0x1e4, "rrAa", 4)
154b099d67bSprovos 		    || fsinfo[0x1fc]
155b099d67bSprovos 		    || fsinfo[0x1fd]
156b099d67bSprovos 		    || fsinfo[0x1fe] != 0x55
157b099d67bSprovos 		    || fsinfo[0x1ff] != 0xaa
158b099d67bSprovos 		    || fsinfo[0x3fc]
159b099d67bSprovos 		    || fsinfo[0x3fd]
160b099d67bSprovos 		    || fsinfo[0x3fe] != 0x55
161b099d67bSprovos 		    || fsinfo[0x3ff] != 0xaa) {
162ee7788d4Srapha 			pwarn("Invalid signature in fsinfo block\n");
163b099d67bSprovos 			if (ask(0, "fix")) {
164b099d67bSprovos 				memcpy(fsinfo, "RRaA", 4);
165b099d67bSprovos 				memcpy(fsinfo + 0x1e4, "rrAa", 4);
166b099d67bSprovos 				fsinfo[0x1fc] = fsinfo[0x1fd] = 0;
167b099d67bSprovos 				fsinfo[0x1fe] = 0x55;
168b099d67bSprovos 				fsinfo[0x1ff] = 0xaa;
169b099d67bSprovos 				fsinfo[0x3fc] = fsinfo[0x3fd] = 0;
170b099d67bSprovos 				fsinfo[0x3fe] = 0x55;
171b099d67bSprovos 				fsinfo[0x3ff] = 0xaa;
172553e4131Sderaadt 
17398fba8a9Skrw 				o = lseek(dosfs, boot->FSInfo * secsize,
17498fba8a9Skrw 				    SEEK_SET);
17598fba8a9Skrw 				if (o == -1 || o != boot->FSInfo * secsize) {
17698fba8a9Skrw 					xperror("Unable to seek FSInfo");
177fb704eb6Stobias 					goto fail;
17898fba8a9Skrw 				}
1795c7c5611Stobias 				n = write(dosfs, fsinfo, fsinfosz);
18098fba8a9Skrw 				if (n == -1 || n != fsinfosz) {
181dd133929Sthib 					xperror("Unable to write FSInfo");
182fb704eb6Stobias 					goto fail;
183b099d67bSprovos 				}
184b099d67bSprovos 				ret = FSBOOTMOD;
185b099d67bSprovos 			} else
186b099d67bSprovos 				boot->FSInfo = 0;
187b099d67bSprovos 		}
188b099d67bSprovos 		if (boot->FSInfo) {
189b099d67bSprovos 			boot->FSFree = fsinfo[0x1e8] + (fsinfo[0x1e9] << 8)
190b099d67bSprovos 				       + (fsinfo[0x1ea] << 16)
191b099d67bSprovos 				       + (fsinfo[0x1eb] << 24);
192b099d67bSprovos 			boot->FSNext = fsinfo[0x1ec] + (fsinfo[0x1ed] << 8)
193b099d67bSprovos 				       + (fsinfo[0x1ee] << 16)
194b099d67bSprovos 				       + (fsinfo[0x1ef] << 24);
195b099d67bSprovos 		}
196b099d67bSprovos 
19798fba8a9Skrw 		o = lseek(dosfs, boot->Backup * secsize, SEEK_SET);
19898fba8a9Skrw 		if (o == -1 || o != boot->Backup * secsize) {
19998fba8a9Skrw 			xperror("could not seek backup bootblock");
200fb704eb6Stobias 			goto fail;
20198fba8a9Skrw 		}
20298fba8a9Skrw 		backup = malloc(2 * secsize); /* In case we check fsinfo. */
20398fba8a9Skrw 		if (backup == NULL) {
20498fba8a9Skrw 			xperror("could not malloc backup boot block");
205fb704eb6Stobias 			goto fail;
20698fba8a9Skrw 		}
20798fba8a9Skrw 		n = read(dosfs, backup, secsize);
20898fba8a9Skrw 		if (n == -1 || n != secsize) {
209dd133929Sthib 			xperror("could not read backup bootblock");
210fb704eb6Stobias 			goto fail;
211b099d67bSprovos 		}
212a4168683Stom 
213a4168683Stom 		/*
214a4168683Stom 		 * Check that the backup boot block matches the primary one.
215a4168683Stom 		 * We don't check every byte, since some vendor utilities
216a4168683Stom 		 * seem to overwrite the boot code when they feel like it,
217a4168683Stom 		 * without changing the backup block.  Specifically, we check
218a4168683Stom 		 * the two-byte signature at the end, the BIOS parameter
219a4168683Stom 		 * block (which starts after the 3-byte JMP and the 8-byte
220a4168683Stom 		 * OEM name/version) and the filesystem information that
221*ba4520ccSbluhm 		 * follows the BPB (bsBPB[53] and bsExt[26] for FAT32, so we
222a4168683Stom 		 * check 79 bytes).
223a4168683Stom 		 */
224a4168683Stom 		if (backup[510] != 0x55 || backup[511] != 0xaa) {
225a4168683Stom 			pfatal("Invalid signature in backup boot block: %02x%02x\n", backup[511], backup[510]);
226a4168683Stom 		}
227a4168683Stom 		if (memcmp(block + 11, backup + 11, 79)) {
22862d85a42Sian 			pfatal("backup doesn't compare to primary bootblock\n");
229fb704eb6Stobias 			goto fail;
230b099d67bSprovos 		}
231b099d67bSprovos 		/* Check backup FSInfo?					XXX */
232b099d67bSprovos 	}
233b099d67bSprovos 
2345f989d62Stobias 	if (boot->FATsecs == 0) {
2355f989d62Stobias 		pfatal("Invalid number of FAT sectors: %u\n", boot->FATsecs);
2365f989d62Stobias 		goto fail;
2375f989d62Stobias 	}
2385f989d62Stobias 
23998fba8a9Skrw 	boot->ClusterOffset = (boot->RootDirEnts * 32 + secsize - 1)
24098fba8a9Skrw 	    / secsize
24188636e1fSaaron 	    + boot->ResSectors
24288636e1fSaaron 	    + boot->FATs * boot->FATsecs
24388636e1fSaaron 	    - CLUST_FIRST * boot->SecPerClust;
24488636e1fSaaron 
2459646ab25Sderaadt 	if (boot->Sectors) {
2469646ab25Sderaadt 		boot->HugeSectors = 0;
2479646ab25Sderaadt 		boot->NumSectors = boot->Sectors;
2489646ab25Sderaadt 	} else
2499646ab25Sderaadt 		boot->NumSectors = boot->HugeSectors;
2505f989d62Stobias 
2515f989d62Stobias 	if (boot->ClusterOffset > boot->NumSectors) {
2525f989d62Stobias 		pfatal("Cluster offset too large (%u clusters)\n",
2535f989d62Stobias 		    boot->ClusterOffset);
2545f989d62Stobias 		goto fail;
2555f989d62Stobias 	}
2569646ab25Sderaadt 	boot->NumClusters = (boot->NumSectors - boot->ClusterOffset) / boot->SecPerClust;
2579646ab25Sderaadt 
258b099d67bSprovos 	if (boot->flags&FAT32)
259b099d67bSprovos 		boot->ClustMask = CLUST32_MASK;
260b099d67bSprovos 	else if (boot->NumClusters < (CLUST_RSRVD&CLUST12_MASK))
261b099d67bSprovos 		boot->ClustMask = CLUST12_MASK;
262b099d67bSprovos 	else if (boot->NumClusters < (CLUST_RSRVD&CLUST16_MASK))
263b099d67bSprovos 		boot->ClustMask = CLUST16_MASK;
264b099d67bSprovos 	else {
26562d85a42Sian 		pfatal("Filesystem too big (%u clusters) for non-FAT32 partition\n",
266b099d67bSprovos 		       boot->NumClusters);
267fb704eb6Stobias 		goto fail;
268b099d67bSprovos 	}
269b099d67bSprovos 
270b099d67bSprovos 	switch (boot->ClustMask) {
271b099d67bSprovos 	case CLUST32_MASK:
27298fba8a9Skrw 		boot->NumFatEntries = (boot->FATsecs * secsize) / 4;
273b099d67bSprovos 		break;
274b099d67bSprovos 	case CLUST16_MASK:
27598fba8a9Skrw 		boot->NumFatEntries = (boot->FATsecs * secsize) / 2;
276b099d67bSprovos 		break;
277b099d67bSprovos 	default:
27898fba8a9Skrw 		boot->NumFatEntries = (boot->FATsecs * secsize * 2) / 3;
279b099d67bSprovos 		break;
280b099d67bSprovos 	}
281b099d67bSprovos 
2829646ab25Sderaadt 	if (boot->NumFatEntries < boot->NumClusters) {
283b099d67bSprovos 		pfatal("FAT size too small, %u entries won't fit into %u sectors\n",
2849646ab25Sderaadt 		       boot->NumClusters, boot->FATsecs);
285fb704eb6Stobias 		goto fail;
2869646ab25Sderaadt 	}
28798fba8a9Skrw 	boot->ClusterSize = boot->SecPerClust * secsize;
2889646ab25Sderaadt 
2899646ab25Sderaadt 	boot->NumFiles = 1;
2909646ab25Sderaadt 	boot->NumFree = 0;
2919646ab25Sderaadt 
292fb704eb6Stobias 	free(backup);
293fb704eb6Stobias 	free(block);
294fb704eb6Stobias 	free(fsinfo);
295b099d67bSprovos 	return ret;
296fb704eb6Stobias fail:
297fb704eb6Stobias 	free(backup);
298fb704eb6Stobias 	free(block);
299fb704eb6Stobias 	free(fsinfo);
300fb704eb6Stobias 	return FSFATAL;
301b099d67bSprovos }
302b099d67bSprovos 
303b099d67bSprovos int
writefsinfo(int dosfs,struct bootblock * boot)3044e95fccfSderaadt writefsinfo(int dosfs, struct bootblock *boot)
305b099d67bSprovos {
306fb704eb6Stobias 	u_char *fsinfo = NULL;
30798fba8a9Skrw 	int secsize = lab.d_secsize, fsinfosz;
308553e4131Sderaadt 	off_t o;
309553e4131Sderaadt 	ssize_t n;
310b099d67bSprovos 
31198fba8a9Skrw 	if ((2 * DOSBOOTBLOCKSIZE) < secsize)
31298fba8a9Skrw 		fsinfosz = secsize;
31398fba8a9Skrw 	else
31498fba8a9Skrw 		fsinfosz = 2 * secsize;
31598fba8a9Skrw 
31698fba8a9Skrw 	fsinfo = malloc(fsinfosz);
31798fba8a9Skrw 	if (fsinfo == NULL) {
31898fba8a9Skrw 		xperror("could not malloc fsinfo block");
319fb704eb6Stobias 		goto fail;
32098fba8a9Skrw 	}
32198fba8a9Skrw 
32298fba8a9Skrw 	o = lseek(dosfs, boot->FSInfo * secsize, SEEK_SET);
32398fba8a9Skrw 	if (o == -1 || o != boot->FSInfo * secsize) {
32498fba8a9Skrw 		xperror("could not seek fsinfo block");
325fb704eb6Stobias 		goto fail;
32698fba8a9Skrw 	}
32798fba8a9Skrw 
32898fba8a9Skrw 	n = read(dosfs, fsinfo, fsinfosz);
32998fba8a9Skrw 	if (n == -1 || n != fsinfosz) {
330dd133929Sthib 		xperror("could not read fsinfo block");
331fb704eb6Stobias 		goto fail;
332b099d67bSprovos 	}
33398fba8a9Skrw 
334b099d67bSprovos 	fsinfo[0x1e8] = (u_char)boot->FSFree;
335b099d67bSprovos 	fsinfo[0x1e9] = (u_char)(boot->FSFree >> 8);
336b099d67bSprovos 	fsinfo[0x1ea] = (u_char)(boot->FSFree >> 16);
337b099d67bSprovos 	fsinfo[0x1eb] = (u_char)(boot->FSFree >> 24);
338b099d67bSprovos 	fsinfo[0x1ec] = (u_char)boot->FSNext;
339b099d67bSprovos 	fsinfo[0x1ed] = (u_char)(boot->FSNext >> 8);
340b099d67bSprovos 	fsinfo[0x1ee] = (u_char)(boot->FSNext >> 16);
341b099d67bSprovos 	fsinfo[0x1ef] = (u_char)(boot->FSNext >> 24);
342553e4131Sderaadt 
34398fba8a9Skrw 	o = lseek(dosfs, o, SEEK_SET);
34498fba8a9Skrw 	if (o == -1 || o != boot->FSInfo * boot->BytesPerSec) {
34598fba8a9Skrw 		xperror("Unable to seek FSInfo");
346fb704eb6Stobias 		goto fail;
34798fba8a9Skrw 	}
34898fba8a9Skrw 	n = write(dosfs, fsinfo, fsinfosz);
34998fba8a9Skrw 	if (n == -1 || n != fsinfosz) {
350dd133929Sthib 		xperror("Unable to write FSInfo");
351fb704eb6Stobias 		goto fail;
352b099d67bSprovos 	}
35398fba8a9Skrw 
354fb704eb6Stobias 	free(fsinfo);
355fb704eb6Stobias 
3569399f920Sespie 	/*
3579399f920Sespie 	 * Technically, we should return FSBOOTMOD here.
3589399f920Sespie 	 *
3599399f920Sespie 	 * However, since Win95 OSR2 (the first M$ OS that has
3609399f920Sespie 	 * support for FAT32) doesn't maintain the FSINFO block
3619399f920Sespie 	 * correctly, it has to be fixed pretty often.
3629399f920Sespie 	 *
363553e4131Sderaadt 	 * Therefore, we handle the FSINFO block only informally,
364ed5470abSdavid 	 * fixing it if necessary, but otherwise ignoring the
3659399f920Sespie 	 * fact that it was incorrect.
3669399f920Sespie 	 */
3679399f920Sespie 	return 0;
368fb704eb6Stobias fail:
369fb704eb6Stobias 	free(fsinfo);
370fb704eb6Stobias 	return FSFATAL;
3719646ab25Sderaadt }
372