1/*
2 * Copyright (c) 2000, 2005-2008 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23/*
24 * Copyright (C) 1995, 1997 Wolfgang Solfrank
25 * Copyright (c) 1995 Martin Husemann
26 *
27 * Redistribution and use in source and binary forms, with or without
28 * modification, are permitted provided that the following conditions
29 * are met:
30 * 1. Redistributions of source code must retain the above copyright
31 *    notice, this list of conditions and the following disclaimer.
32 * 2. Redistributions in binary form must reproduce the above copyright
33 *    notice, this list of conditions and the following disclaimer in the
34 *    documentation and/or other materials provided with the distribution.
35 * 3. All advertising materials mentioning features or use of this software
36 *    must display the following acknowledgement:
37 *	This product includes software developed by Martin Husemann
38 *	and Wolfgang Solfrank.
39 * 4. Neither the name of the University nor the names of its contributors
40 *    may be used to endorse or promote products derived from this software
41 *    without specific prior written permission.
42 *
43 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
44 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
45 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
46 * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
47 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
48 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
49 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
50 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
51 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
52 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
53 */
54
55
56#include <sys/cdefs.h>
57
58#include <stdlib.h>
59#include <string.h>
60#include <ctype.h>
61#include <stdio.h>
62#include <unistd.h>
63#include <errno.h>
64
65#include "ext.h"
66#include "fsutil.h"
67
68int
69readboot(dosfs, boot)
70	int dosfs;
71	struct bootblock *boot;
72{
73	u_char block[MAX_SECTOR_SIZE];
74	u_char fsinfo[MAX_SECTOR_SIZE];
75	int ret = FSOK;
76
77        /*
78         * [2734381] Some devices have sector sizes greater than 512 bytes.  These devices
79         * tend to return errors if you try to read less than a sector, so we try reading
80         * the maximum sector size (which may end up reading more than one sector).
81         */
82	if (read(dosfs, block, MAX_SECTOR_SIZE) != MAX_SECTOR_SIZE) {
83		perr("could not read boot block");
84		return FSFATAL;
85	}
86
87	/* [2699033]
88	*
89	* The first three bytes are an Intel x86 jump instruction.  It should be one
90	* of the following forms:
91	*    0xE9 0x?? 0x??
92	*    0xEB 0x?? 0x90
93	*
94	* [5016947]
95	*
96	* Windows doesn't actually check the third byte if the first byte is 0xEB,
97	* so we don't either
98	*/
99	if (block[0] != 0xE9 && block[0] != 0xEB)
100	{
101		pfatal("Invalid BS_jmpBoot in boot block: %02x%02x%02x\n", block[0], block[1], block[2]);
102		return FSFATAL;
103	}
104
105	memset(boot, 0, sizeof *boot);
106	boot->ValidFat = -1;
107
108	/* decode bios parameter block */
109	boot->BytesPerSec = block[11] + (block[12] << 8);
110	boot->SecPerClust = block[13];
111	boot->ResSectors = block[14] + (block[15] << 8);
112	boot->FATs = block[16];
113	boot->RootDirEnts = block[17] + (block[18] << 8);
114	boot->Sectors = block[19] + (block[20] << 8);
115	boot->Media = block[21];
116	boot->FATsmall = block[22] + (block[23] << 8);
117	boot->SecPerTrack = block[24] + (block[25] << 8);
118	boot->Heads = block[26] + (block[27] << 8);
119	boot->HiddenSecs = block[28] + (block[29] << 8) + (block[30] << 16) + (block[31] << 24);
120	boot->HugeSectors = block[32] + (block[33] << 8) + (block[34] << 16) + (block[35] << 24);
121
122	boot->FATsecs = boot->FATsmall;
123	boot->ClusterSize = boot->BytesPerSec * boot->SecPerClust;
124
125	/*
126	 * Make sure the sector size is a power of two, and in the range
127	 * 512..MAX_SECTOR_SIZE.
128	 */
129	if (boot->BytesPerSec < DOSBOOTBLOCKSIZE || boot->BytesPerSec > MAX_SECTOR_SIZE ||
130	    (boot->BytesPerSec & (boot->BytesPerSec - 1)) != 0)
131	{
132		pfatal("Invalid sector size: %u\n", boot->BytesPerSec);
133		return FSFATAL;
134	}
135
136	/*
137	 * Make sure the sectors per cluster is reasonable.  It must be a
138	 * non-zero power of two.
139	 *
140	 * The FAT specification warns that the bytes per cluster shouldn't be
141	 * allowed to be greater than 32KB (with 64KB supported on some systems),
142	 * but we don't actually enforce that here.
143	 */
144	if (boot->SecPerClust == 0 ||
145	    (boot->SecPerClust & (boot->SecPerClust - 1)) != 0)
146	{
147		pfatal("Invalid sectors per cluster: %u\n", boot->SecPerClust);
148		return FSFATAL;
149	}
150
151	if (!boot->RootDirEnts)
152		boot->flags |= FAT32;
153	if (boot->flags & FAT32) {
154		boot->FATsecs = block[36] + (block[37] << 8)
155				+ (block[38] << 16) + (block[39] << 24);
156		if (block[40] & 0x80)
157			boot->ValidFat = block[40] & 0x0f;
158
159		/* check version number: */
160		if (block[42] || block[43]) {
161			/* Correct?				XXX */
162			pfatal("Unknown filesystem version: %x.%x\n",
163			       block[43], block[42]);
164			return FSFATAL;
165		}
166		boot->RootCl = block[44] + (block[45] << 8)
167			       + (block[46] << 16) + (block[47] << 24);
168		boot->FSInfo = block[48] + (block[49] << 8);
169		boot->Backup = block[50] + (block[51] << 8);
170
171		if (lseek(dosfs, boot->FSInfo * boot->BytesPerSec, SEEK_SET)
172		    != boot->FSInfo * boot->BytesPerSec
173		    || read(dosfs, fsinfo, boot->BytesPerSec)
174		    != boot->BytesPerSec) {
175			perr("could not read fsinfo block");
176			return FSFATAL;
177		}
178		if (memcmp(fsinfo, "RRaA", 4)
179		    || memcmp(fsinfo + 0x1e4, "rrAa", 4)
180		    || fsinfo[0x1fc]
181		    || fsinfo[0x1fd]
182		    || fsinfo[0x1fe] != 0x55
183		    || fsinfo[0x1ff] != 0xaa) {
184			pwarn("Invalid signature in fsinfo block\n");
185			if (ask(0, "fix")) {
186				memcpy(fsinfo, "RRaA", 4);
187				memcpy(fsinfo + 0x1e4, "rrAa", 4);
188				fsinfo[0x1fc] = fsinfo[0x1fd] = 0;
189				fsinfo[0x1fe] = 0x55;
190				fsinfo[0x1ff] = 0xaa;
191				fsinfo[0x3fc] = fsinfo[0x3fd] = 0;
192				fsinfo[0x3fe] = 0x55;
193				fsinfo[0x3ff] = 0xaa;
194				if (lseek(dosfs, boot->FSInfo * boot->BytesPerSec, SEEK_SET)
195				    != boot->FSInfo * boot->BytesPerSec
196				    || write(dosfs, fsinfo, boot->BytesPerSec)
197				    != boot->BytesPerSec) {
198					perr("Unable to write FSInfo");
199					return FSFATAL;
200				}
201				ret = FSBOOTMOD;
202			} else {
203				boot->FSInfo = 0;
204				ret = FSERROR;
205			}
206		}
207		if (boot->FSInfo) {
208			boot->FSFree = fsinfo[0x1e8] + (fsinfo[0x1e9] << 8)
209				       + (fsinfo[0x1ea] << 16)
210				       + (fsinfo[0x1eb] << 24);
211			boot->FSNext = fsinfo[0x1ec] + (fsinfo[0x1ed] << 8)
212				       + (fsinfo[0x1ee] << 16)
213				       + (fsinfo[0x1ef] << 24);
214		}
215	}
216
217	boot->ClusterOffset = (boot->RootDirEnts * 32 + boot->BytesPerSec - 1)
218	    / boot->BytesPerSec
219	    + boot->ResSectors
220	    + boot->FATs * boot->FATsecs
221	    - CLUST_FIRST * boot->SecPerClust;
222
223	if (boot->Sectors) {
224		boot->HugeSectors = 0;
225		boot->NumSectors = boot->Sectors;
226	} else
227		boot->NumSectors = boot->HugeSectors;
228
229        /*
230         * Note: NumClusters isn't actually the *number* (or count) of clusters.  It is really
231         * the maximum cluster number plus one (which is the number of clusters plus two;
232         * it is also the number of valid FAT entries).  It is meant to be used
233         * for looping over cluster numbers, or range checking cluster numbers.
234         */
235	boot->NumClusters = (boot->NumSectors - boot->ClusterOffset) / boot->SecPerClust;
236
237        /* Since NumClusters is off by two, use constants that are off by two also. */
238	if (boot->flags&FAT32)
239		boot->ClustMask = CLUST32_MASK;
240	else if (boot->NumClusters < (4085+2))
241		boot->ClustMask = CLUST12_MASK;
242	else if (boot->NumClusters < (65526+2))		/* Windows allows 65525 clusters, so we should, too */
243		boot->ClustMask = CLUST16_MASK;
244	else {
245		pfatal("Filesystem too big (%u clusters) for non-FAT32 partition\n",
246		       boot->NumClusters-2);
247		return FSFATAL;
248	}
249
250	switch (boot->ClustMask) {
251	case CLUST32_MASK:
252		boot->NumFatEntries = (boot->FATsecs * boot->BytesPerSec) / 4;
253		break;
254	case CLUST16_MASK:
255		boot->NumFatEntries = (boot->FATsecs * boot->BytesPerSec) / 2;
256		break;
257	default:
258		boot->NumFatEntries = (boot->FATsecs * boot->BytesPerSec * 2) / 3;
259		break;
260	}
261
262	/*
263	 * Verify that the FAT is large enough to hold the number of clusters
264	 * that we think the volume has.  Some digital cameras, and our own
265	 * newfs_msdos, can create volumes whose total sector count is too large.
266	 */
267	if (boot->NumFatEntries < boot->NumClusters) {
268		pwarn("FAT size too small, %u entries won't fit into %u sectors\n",
269		       boot->NumClusters, boot->FATsecs);
270		boot->NumClusters = boot->NumFatEntries;
271		if (ask(0, "Fix total sectors")) {
272			/* Need to recompute sectors based on clusters */
273			boot->NumSectors = (boot->NumClusters * boot->SecPerClust) + boot->ClusterOffset;
274			if (boot->Sectors) {
275				boot->Sectors = boot->NumSectors;
276				block[19] = boot->NumSectors & 0xFF;
277				block[20] = (boot->NumSectors >> 8) & 0xFF;
278			} else {
279				boot->HugeSectors = boot->NumSectors;
280				block[32] = boot->NumSectors & 0xFF;
281				block[33] = (boot->NumSectors >> 8) & 0xFF;
282				block[34] = (boot->NumSectors >> 16) & 0xFF;
283				block[35] = (boot->NumSectors >> 24) & 0xFF;
284			}
285			if (lseek(dosfs, 0, SEEK_SET) != 0 ||
286				write(dosfs, block, boot->BytesPerSec) != boot->BytesPerSec)
287			{
288				perr("could not write boot sector");
289				return FSFATAL;
290			}
291			ret |= FSBOOTMOD;	/* This flag is currently ignored by checkfilesys() */
292		} else {
293			pwarn("Continuing, assuming %u clusters\n", boot->NumFatEntries-2);
294			/*
295			 * We don't return an error here, so Mac OS X will automatically
296			 * mount the volume without attempting to repair the disk just
297			 * because of this problem (though it will end up fixing this
298			 * problem if there was some other problem that had to be repaired
299			 * before mounting).
300			 */
301		}
302	}
303
304	boot->NumFree = 0;
305
306	return ret;
307}
308
309int
310writefsinfo(dosfs, boot)
311	int dosfs;
312	struct bootblock *boot;
313{
314	u_char fsinfo[MAX_SECTOR_SIZE];
315
316	if (lseek(dosfs, boot->FSInfo * boot->BytesPerSec, SEEK_SET)
317	    != boot->FSInfo * boot->BytesPerSec
318	    || read(dosfs, fsinfo, boot->BytesPerSec) != boot->BytesPerSec) {
319		perr("could not read fsinfo block");
320		return FSFATAL;
321	}
322	fsinfo[0x1e8] = (u_char)boot->FSFree;
323	fsinfo[0x1e9] = (u_char)(boot->FSFree >> 8);
324	fsinfo[0x1ea] = (u_char)(boot->FSFree >> 16);
325	fsinfo[0x1eb] = (u_char)(boot->FSFree >> 24);
326	fsinfo[0x1ec] = (u_char)boot->FSNext;
327	fsinfo[0x1ed] = (u_char)(boot->FSNext >> 8);
328	fsinfo[0x1ee] = (u_char)(boot->FSNext >> 16);
329	fsinfo[0x1ef] = (u_char)(boot->FSNext >> 24);
330	if (lseek(dosfs, boot->FSInfo * boot->BytesPerSec, SEEK_SET)
331	    != boot->FSInfo * boot->BytesPerSec
332	    || write(dosfs, fsinfo, boot->BytesPerSec)
333	    != boot->BytesPerSec) {
334		perr("Unable to write FSInfo");
335		return FSFATAL;
336	}
337	/*
338	 * Technically, we should return FSBOOTMOD here.
339	 *
340	 * However, since Win95 OSR2 (the first M$ OS that has
341	 * support for FAT32) doesn't maintain the FSINFO block
342	 * correctly, it has to be fixed pretty often.
343	 *
344	 * Therefor, we handle the FSINFO block only informally,
345	 * fixing it if neccessary, but otherwise ignoring the
346	 * fact that it was incorrect.
347	 */
348	return 0;
349}
350