xref: /illumos-gate/usr/src/cmd/fs.d/pcfs/fsck/bpb.c (revision db92b35a)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright (c) 2011 Gary Mills
24  *
25  * Copyright (c) 1999,2000 by Sun Microsystems, Inc.
26  * All rights reserved.
27  */
28 
29 /*
30  * fsck_pcfs -- routines for manipulating the BPB (BIOS parameter block)
31  * of the file system.
32  */
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <unistd.h>
37 #include <libintl.h>
38 #include <sys/types.h>
39 #include <sys/dktp/fdisk.h>
40 #include <sys/fs/pc_fs.h>
41 #include <sys/fs/pc_dir.h>
42 #include <sys/fs/pc_label.h>
43 #include "pcfs_common.h"
44 #include "fsck_pcfs.h"
45 #include "pcfs_bpb.h"
46 
47 extern	off64_t	FirstClusterOffset;
48 extern	off64_t	PartitionOffset;
49 extern	int32_t	BytesPerCluster;
50 extern	int32_t	TotalClusters;
51 extern	int32_t	LastCluster;
52 extern	int32_t	RootDirSize;
53 extern	int32_t	FATSize;
54 extern	short	FATEntrySize;
55 extern	bpb_t	TheBIOSParameterBlock;
56 extern	int	IsFAT32;
57 extern	int	Verbose;
58 
59 static void
computeFileAreaSize(void)60 computeFileAreaSize(void)
61 {
62 	int32_t	dataSectors;
63 	int32_t	overhead;
64 
65 	/*
66 	 * Compute bytes/cluster for later reference
67 	 */
68 	BytesPerCluster =  TheBIOSParameterBlock.bpb.sectors_per_cluster *
69 	    TheBIOSParameterBlock.bpb.bytes_per_sector;
70 
71 	/*
72 	 * First we'll find total number of sectors in the file area...
73 	 */
74 	if (TheBIOSParameterBlock.bpb.sectors_in_volume > 0)
75 		dataSectors = TheBIOSParameterBlock.bpb.sectors_in_volume;
76 	else
77 		dataSectors =
78 		    TheBIOSParameterBlock.bpb.sectors_in_logical_volume;
79 
80 	overhead = TheBIOSParameterBlock.bpb.resv_sectors;
81 
82 	RootDirSize = TheBIOSParameterBlock.bpb.num_root_entries *
83 	    sizeof (struct pcdir);
84 	overhead += RootDirSize / TheBIOSParameterBlock.bpb.bytes_per_sector;
85 
86 	if (TheBIOSParameterBlock.bpb.sectors_per_fat) {
87 		/*
88 		 * Good old FAT12 or FAT16
89 		 */
90 		overhead += TheBIOSParameterBlock.bpb.num_fats *
91 		    TheBIOSParameterBlock.bpb.sectors_per_fat;
92 		/*
93 		 * Compute this for later - when we actually pull in a copy
94 		 * of the FAT
95 		 */
96 		FATSize = TheBIOSParameterBlock.bpb.sectors_per_fat *
97 		    TheBIOSParameterBlock.bpb.bytes_per_sector;
98 	} else {
99 		/*
100 		 *  FAT32
101 		 *  I'm unsure if this is always going to work.  At one
102 		 *  point during the creation of this program and mkfs_pcfs
103 		 *  it seemed that Windows had created an fs where it had
104 		 *  rounded big_sectors_per_fat up to a cluster boundary.
105 		 *  Later, though, I encountered a problem where I wasn't
106 		 *  finding the root directory because I was looking in the
107 		 *  wrong place by doing that same roundup.  So, for now,
108 		 *  I'm backing off on the cluster boundary thing and just
109 		 *  believing what I am told.
110 		 */
111 		overhead += TheBIOSParameterBlock.bpb.num_fats *
112 		    TheBIOSParameterBlock.bpb32.big_sectors_per_fat;
113 		/*
114 		 * Compute this for later - when we actually pull in a copy
115 		 * of the FAT
116 		 */
117 		FATSize = TheBIOSParameterBlock.bpb32.big_sectors_per_fat *
118 		    TheBIOSParameterBlock.bpb.bytes_per_sector;
119 	}
120 
121 	/*
122 	 * Now change sectors to clusters.  The computed value for
123 	 * TotalClusters is persistent for the remainder of execution.
124 	 */
125 	dataSectors -= overhead;
126 	TotalClusters = dataSectors /
127 	    TheBIOSParameterBlock.bpb.sectors_per_cluster;
128 
129 	/*
130 	 *  Also need to compute last cluster and offset of the first cluster
131 	 */
132 	LastCluster = TotalClusters + FIRST_CLUSTER;
133 	FirstClusterOffset = overhead *
134 	    TheBIOSParameterBlock.bpb.bytes_per_sector;
135 	FirstClusterOffset += PartitionOffset;
136 
137 	/*
138 	 * XXX this should probably be more sophisticated
139 	 */
140 	if (IsFAT32)
141 		FATEntrySize = 32;
142 	else {
143 		if (TotalClusters <= DOS_F12MAXC)
144 			FATEntrySize = 12;
145 		else
146 			FATEntrySize = 16;
147 	}
148 
149 	if (Verbose) {
150 		(void) fprintf(stderr,
151 		    gettext("Disk has a file area of %d "
152 		    "allocation units,\neach with %d sectors = %llu "
153 		    "bytes.\n"), TotalClusters,
154 		    TheBIOSParameterBlock.bpb.sectors_per_cluster,
155 		    (uint64_t)TotalClusters *
156 		    TheBIOSParameterBlock.bpb.sectors_per_cluster *
157 		    TheBIOSParameterBlock.bpb.bytes_per_sector);
158 		(void) fprintf(stderr,
159 		    gettext("File system overhead of %d sectors.\n"), overhead);
160 		(void) fprintf(stderr,
161 		    gettext("The last cluster is %d\n"), LastCluster);
162 	}
163 }
164 
165 /*
166  *  XXX - right now we aren't attempting to fix anything that looks bad,
167  *	instead we just give up.
168  */
169 void
readBPB(int fd)170 readBPB(int fd)
171 {
172 	boot_sector_t ubpb;
173 
174 	/*
175 	 *  The BPB is the first sector of the file system
176 	 */
177 	if (lseek64(fd, PartitionOffset, SEEK_SET) < 0) {
178 		mountSanityCheckFails();
179 		perror(gettext("Cannot seek to start of disk partition"));
180 		(void) close(fd);
181 		exit(7);
182 	}
183 	if (Verbose)
184 		(void) fprintf(stderr,
185 		    gettext("Reading BIOS parameter block\n"));
186 	if (read(fd, ubpb.buf, BPSEC) < BPSEC) {
187 		mountSanityCheckFails();
188 		perror(gettext("Read BIOS parameter block"));
189 		(void) close(fd);
190 		exit(2);
191 	}
192 
193 	if (ltohs(ubpb.mb.signature) != BOOTSECSIG) {
194 		mountSanityCheckFails();
195 		(void) fprintf(stderr,
196 		    gettext("Bad signature on BPB. Giving up.\n"));
197 		exit(2);
198 	}
199 
200 #ifdef _BIG_ENDIAN
201 	swap_pack_grabbpb(&TheBIOSParameterBlock, &(ubpb.bs));
202 #else
203 	(void) memcpy(&(TheBIOSParameterBlock.bpb), &(ubpb.bs.bs_front.bs_bpb),
204 	    sizeof (TheBIOSParameterBlock.bpb));
205 	(void) memcpy(&(TheBIOSParameterBlock.ebpb), &(ubpb.bs.bs_ebpb),
206 	    sizeof (TheBIOSParameterBlock.ebpb));
207 #endif
208 	/*
209 	 * In general, we would expect the bytes per sector to
210 	 * equal 256 (BPSEC).  I personally have yet to see a file
211 	 * system where this isn't true but apparently some weird media
212 	 * have different sector sizes.  So we'll accept a couple of
213 	 * other small multiples of 256 as valid sizes.
214 	 */
215 	if (TheBIOSParameterBlock.bpb.bytes_per_sector != BPSEC &&
216 	    TheBIOSParameterBlock.bpb.bytes_per_sector != 2 * BPSEC &&
217 	    TheBIOSParameterBlock.bpb.bytes_per_sector != 4 * BPSEC) {
218 		mountSanityCheckFails();
219 		(void) fprintf(stderr,
220 		    gettext("Bogus bytes per sector value.  Giving up.\n"));
221 		exit(2);
222 	}
223 	if (!(ISP2(TheBIOSParameterBlock.bpb.sectors_per_cluster) &&
224 	    IN_RANGE(TheBIOSParameterBlock.bpb.sectors_per_cluster,
225 	    1, 128))) {
226 		mountSanityCheckFails();
227 		(void) fprintf(stderr,
228 		    gettext("Bogus sectors per cluster value.  Giving up.\n"));
229 		(void) close(fd);
230 		exit(6);
231 	}
232 	if (TheBIOSParameterBlock.bpb.sectors_per_fat == 0) {
233 #ifdef _BIG_ENDIAN
234 		swap_pack_grab32bpb(&TheBIOSParameterBlock, &(ubpb.bs));
235 #else
236 		(void) memcpy(&(TheBIOSParameterBlock.bpb32),
237 		    &(ubpb.bs32.bs_bpb32),
238 		    sizeof (TheBIOSParameterBlock.bpb32));
239 #endif
240 		IsFAT32 = 1;
241 	}
242 	if (!IsFAT32) {
243 		if ((TheBIOSParameterBlock.bpb.num_root_entries == 0) ||
244 		    ((TheBIOSParameterBlock.bpb.num_root_entries *
245 		    sizeof (struct pcdir)) %
246 		    TheBIOSParameterBlock.bpb.bytes_per_sector) != 0) {
247 			mountSanityCheckFails();
248 			(void) fprintf(stderr,
249 			    gettext("Bogus number of root entries.  "
250 			    "Giving up.\n"));
251 			exit(2);
252 		}
253 	} else {
254 		if (TheBIOSParameterBlock.bpb.num_root_entries != 0) {
255 			mountSanityCheckFails();
256 			(void) fprintf(stderr,
257 			    gettext("Bogus number of root entries.  "
258 			    "Giving up.\n"));
259 			exit(2);
260 		}
261 	}
262 	/*
263 	 * In general, we would expect the number of FATs field to
264 	 * equal 2.  Our mkfs and Windows have this as a default
265 	 * value.  I suppose someone could override the default,
266 	 * though, so we'll sort of arbitrarily accept any number
267 	 * between 1 and 4 inclusive as reasonable values.
268 	 *
269 	 * XXX: Warn, but continue, if value is suspicious? (>2?)
270 	 */
271 	if (TheBIOSParameterBlock.bpb.num_fats > 4 ||
272 	    TheBIOSParameterBlock.bpb.num_fats < 1) {
273 		mountSanityCheckFails();
274 		(void) fprintf(stderr,
275 		    gettext("Bogus number of FATs.  Giving up.\n"));
276 		exit(2);
277 	}
278 	computeFileAreaSize();
279 }
280