1/*
2 * Copyright (C) 1995, 1997 Wolfgang Solfrank
3 * Copyright (c) 1995 Martin Husemann
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17 * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
18 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26
27#include <stdlib.h>
28#include <string.h>
29#include <stdio.h>
30#include <unistd.h>
31
32#include "ext.h"
33
34int
35readboot(int dosfs, struct bootblock *boot)
36{
37	u_char block[DOSBOOTBLOCKSIZE];
38	u_char fsinfo[2 * DOSBOOTBLOCKSIZE];
39	u_char backup[DOSBOOTBLOCKSIZE];
40	int ret = FSOK;
41	int i;
42
43	if ((size_t)read(dosfs, block, sizeof block) != sizeof block) {
44		perr("could not read boot block");
45		return FSFATAL;
46	}
47
48	if (block[510] != 0x55 || block[511] != 0xaa) {
49		pfatal("Invalid signature in boot block: %02x%02x",
50		    block[511], block[510]);
51		return FSFATAL;
52	}
53
54	memset(boot, 0, sizeof *boot);
55	boot->ValidFat = -1;
56
57	/* decode bios parameter block */
58	boot->bpbBytesPerSec = block[11] + (block[12] << 8);
59	boot->bpbSecPerClust = block[13];
60	boot->bpbResSectors = block[14] + (block[15] << 8);
61	boot->bpbFATs = block[16];
62	boot->bpbRootDirEnts = block[17] + (block[18] << 8);
63	boot->bpbSectors = block[19] + (block[20] << 8);
64	boot->bpbMedia = block[21];
65	boot->bpbFATsmall = block[22] + (block[23] << 8);
66	boot->SecPerTrack = block[24] + (block[25] << 8);
67	boot->bpbHeads = block[26] + (block[27] << 8);
68	boot->bpbHiddenSecs = block[28] + (block[29] << 8) +
69	    (block[30] << 16) + (block[31] << 24);
70	boot->bpbHugeSectors = block[32] + (block[33] << 8) +
71	    (block[34] << 16) + (block[35] << 24);
72
73	boot->FATsecs = boot->bpbFATsmall;
74
75	if (!boot->bpbRootDirEnts)
76		boot->flags |= FAT32;
77	if (boot->flags & FAT32) {
78		boot->FATsecs = block[36] + (block[37] << 8)
79				+ (block[38] << 16) + (block[39] << 24);
80		if (block[40] & 0x80)
81			boot->ValidFat = block[40] & 0x0f;
82
83		/* check version number: */
84		if (block[42] || block[43]) {
85			/* Correct?				XXX */
86			pfatal("Unknown file system version: %x.%x",
87			       block[43], block[42]);
88			return FSFATAL;
89		}
90		boot->bpbRootClust = block[44] + (block[45] << 8)
91			       + (block[46] << 16) + (block[47] << 24);
92		boot->bpbFSInfo = block[48] + (block[49] << 8);
93		boot->bpbBackup = block[50] + (block[51] << 8);
94
95		if (lseek(dosfs, boot->bpbFSInfo * boot->bpbBytesPerSec,
96		    SEEK_SET) != boot->bpbFSInfo * boot->bpbBytesPerSec
97		    || read(dosfs, fsinfo, sizeof fsinfo) != sizeof fsinfo) {
98			perr("could not read fsinfo block");
99			return FSFATAL;
100		}
101		if (memcmp(fsinfo, "RRaA", 4)
102		    || memcmp(fsinfo + 0x1e4, "rrAa", 4)
103		    || fsinfo[0x1fc]
104		    || fsinfo[0x1fd]
105		    || fsinfo[0x1fe] != 0x55
106		    || fsinfo[0x1ff] != 0xaa
107		    || fsinfo[0x3fc]
108		    || fsinfo[0x3fd]
109		    || fsinfo[0x3fe] != 0x55
110		    || fsinfo[0x3ff] != 0xaa) {
111			pwarn("Invalid signature in fsinfo block\n");
112			if (ask(0, "Fix")) {
113				memcpy(fsinfo, "RRaA", 4);
114				memcpy(fsinfo + 0x1e4, "rrAa", 4);
115				fsinfo[0x1fc] = fsinfo[0x1fd] = 0;
116				fsinfo[0x1fe] = 0x55;
117				fsinfo[0x1ff] = 0xaa;
118				fsinfo[0x3fc] = fsinfo[0x3fd] = 0;
119				fsinfo[0x3fe] = 0x55;
120				fsinfo[0x3ff] = 0xaa;
121				if (lseek(dosfs, boot->bpbFSInfo *
122				    boot->bpbBytesPerSec, SEEK_SET)
123				    != boot->bpbFSInfo * boot->bpbBytesPerSec
124				    || write(dosfs, fsinfo, sizeof fsinfo)
125				    != sizeof fsinfo) {
126					perr("Unable to write bpbFSInfo");
127					return FSFATAL;
128				}
129				ret = FSBOOTMOD;
130			} else
131				boot->bpbFSInfo = 0;
132		}
133		if (boot->bpbFSInfo) {
134			boot->FSFree = fsinfo[0x1e8] + (fsinfo[0x1e9] << 8)
135				       + (fsinfo[0x1ea] << 16)
136				       + (fsinfo[0x1eb] << 24);
137			boot->FSNext = fsinfo[0x1ec] + (fsinfo[0x1ed] << 8)
138				       + (fsinfo[0x1ee] << 16)
139				       + (fsinfo[0x1ef] << 24);
140		}
141
142		if (lseek(dosfs, boot->bpbBackup * boot->bpbBytesPerSec,
143		    SEEK_SET)
144		    != boot->bpbBackup * boot->bpbBytesPerSec
145		    || read(dosfs, backup, sizeof backup) != sizeof  backup) {
146			perr("could not read backup bootblock");
147			return FSFATAL;
148		}
149		backup[65] = block[65];				/* XXX */
150		if (memcmp(block + 11, backup + 11, 79)) {
151			/*
152			 * XXX We require a reference that explains
153			 * that these bytes need to match, or should
154			 * drop the check.  gdt@NetBSD has observed
155			 * filesystems that work fine under Windows XP
156			 * and NetBSD that do not match, so the
157			 * requirement is suspect.  For now, just
158			 * print out useful information and continue.
159			 */
160			pfatal("backup (block %d) mismatch with primary bootblock:\n",
161			        boot->bpbBackup);
162			for (i = 11; i < 11 + 90; i++) {
163				if (block[i] != backup[i])
164					pfatal("\ti=%d\tprimary 0x%02x\tbackup 0x%02x\n",
165					       i, block[i], backup[i]);
166			}
167		}
168		/* Check backup bpbFSInfo?					XXX */
169	}
170
171	boot->ClusterOffset = (boot->bpbRootDirEnts * 32 +
172	    boot->bpbBytesPerSec - 1) / boot->bpbBytesPerSec +
173	    boot->bpbResSectors + boot->bpbFATs * boot->FATsecs -
174	    CLUST_FIRST * boot->bpbSecPerClust;
175
176	if (boot->bpbBytesPerSec % DOSBOOTBLOCKSIZE_REAL != 0) {
177		pfatal("Invalid sector size: %u", boot->bpbBytesPerSec);
178		return FSFATAL;
179	}
180	if (boot->bpbSecPerClust == 0) {
181		pfatal("Invalid cluster size: %u", boot->bpbSecPerClust);
182		return FSFATAL;
183	}
184	if (boot->bpbSectors) {
185		boot->bpbHugeSectors = 0;
186		boot->NumSectors = boot->bpbSectors;
187	} else
188		boot->NumSectors = boot->bpbHugeSectors;
189	boot->NumClusters = (boot->NumSectors - boot->ClusterOffset) /
190	    boot->bpbSecPerClust;
191
192	if (boot->flags&FAT32)
193		boot->ClustMask = CLUST32_MASK;
194	else if (boot->NumClusters < (CLUST_RSRVD&CLUST12_MASK))
195		boot->ClustMask = CLUST12_MASK;
196	else if (boot->NumClusters < (CLUST_RSRVD&CLUST16_MASK))
197		boot->ClustMask = CLUST16_MASK;
198	else {
199		pfatal("Filesystem too big (%u clusters) for non-FAT32 partition",
200		       boot->NumClusters);
201		return FSFATAL;
202	}
203
204	switch (boot->ClustMask) {
205	case CLUST32_MASK:
206		boot->NumFatEntries = (boot->FATsecs * boot->bpbBytesPerSec) / 4;
207		break;
208	case CLUST16_MASK:
209		boot->NumFatEntries = (boot->FATsecs * boot->bpbBytesPerSec) / 2;
210		break;
211	default:
212		boot->NumFatEntries = (boot->FATsecs * boot->bpbBytesPerSec * 2) / 3;
213		break;
214	}
215
216	if (boot->NumFatEntries < boot->NumClusters - CLUST_FIRST) {
217		pfatal("FAT size too small, %u entries won't fit into %u sectors\n",
218		       boot->NumClusters, boot->FATsecs);
219		return FSFATAL;
220	}
221	boot->ClusterSize = boot->bpbBytesPerSec * boot->bpbSecPerClust;
222
223	boot->NumFiles = 1;
224	boot->NumFree = 0;
225
226	// @localmod begin
227	// The BSD FAT driver follows the FAT specification of the "dirty bit"
228	// exactly. The Linux FAT driver, however, marks a bit in the boot sector as
229	// "dirty", which is outside the typical FAT specification. For maximum
230	// compatibility, this modification allows fsck to detect (and clean) the
231	// unspecified Linux dirty bit.
232	int dirtybyte = boot->flags&FAT32 ? 0x41 : 0x25;
233	if (block[dirtybyte] & 0x01) {
234		if (ask(1, "MARK BOOT BLOCK CLEAN") == 0) {
235			pwarn("\n***** BOOT BLOCK IS LEFT MARKED AS DIRTY *****\n");
236			return FSERROR;
237		}
238		pwarn("MARKING BOOT BLOCK CLEAN\n");
239		block[dirtybyte] &= ~0x01;
240		if (lseek(dosfs, 0, SEEK_SET) != 0
241			|| write(dosfs, block, sizeof block) != sizeof block) {
242			perr("could not write boot block");
243			return FSFATAL;
244		}
245	}
246	// @localmod end
247
248	return ret;
249}
250
251int
252writefsinfo(int dosfs, struct bootblock *boot)
253{
254	u_char fsinfo[2 * DOSBOOTBLOCKSIZE];
255
256	if (lseek(dosfs, boot->bpbFSInfo * boot->bpbBytesPerSec, SEEK_SET)
257	    != boot->bpbFSInfo * boot->bpbBytesPerSec
258	    || read(dosfs, fsinfo, sizeof fsinfo) != sizeof fsinfo) {
259		perr("could not read fsinfo block");
260		return FSFATAL;
261	}
262	fsinfo[0x1e8] = (u_char)boot->FSFree;
263	fsinfo[0x1e9] = (u_char)(boot->FSFree >> 8);
264	fsinfo[0x1ea] = (u_char)(boot->FSFree >> 16);
265	fsinfo[0x1eb] = (u_char)(boot->FSFree >> 24);
266	fsinfo[0x1ec] = (u_char)boot->FSNext;
267	fsinfo[0x1ed] = (u_char)(boot->FSNext >> 8);
268	fsinfo[0x1ee] = (u_char)(boot->FSNext >> 16);
269	fsinfo[0x1ef] = (u_char)(boot->FSNext >> 24);
270	if (lseek(dosfs, boot->bpbFSInfo * boot->bpbBytesPerSec, SEEK_SET)
271	    != boot->bpbFSInfo * boot->bpbBytesPerSec
272	    || write(dosfs, fsinfo, sizeof fsinfo)
273	    != sizeof fsinfo) {
274		perr("Unable to write bpbFSInfo");
275		return FSFATAL;
276	}
277	/*
278	 * Technically, we should return FSBOOTMOD here.
279	 *
280	 * However, since Win95 OSR2 (the first M$ OS that has
281	 * support for FAT32) doesn't maintain the FSINFO block
282	 * correctly, it has to be fixed pretty often.
283	 *
284	 * Therefor, we handle the FSINFO block only informally,
285	 * fixing it if necessary, but otherwise ignoring the
286	 * fact that it was incorrect.
287	 */
288	return 0;
289}
290