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