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) 1999,2000 by Sun Microsystems, Inc.
24 * All rights reserved.
25 */
26
27 #pragma ident "%Z%%M% %I% %E% SMI"
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 (!(is_z_a_power_of_x_le_y(2, 128,
224 TheBIOSParameterBlock.bpb.sectors_per_cluster))) {
225 mountSanityCheckFails();
226 (void) fprintf(stderr,
227 gettext("Bogus sectors per cluster value. Giving up.\n"));
228 (void) close(fd);
229 exit(6);
230 }
231 if (TheBIOSParameterBlock.bpb.sectors_per_fat == 0) {
232 #ifdef _BIG_ENDIAN
233 swap_pack_grab32bpb(&TheBIOSParameterBlock, &(ubpb.bs));
234 #else
235 (void) memcpy(&(TheBIOSParameterBlock.bpb32),
236 &(ubpb.bs32.bs_bpb32),
237 sizeof (TheBIOSParameterBlock.bpb32));
238 #endif
239 IsFAT32 = 1;
240 }
241 if (!IsFAT32) {
242 if ((TheBIOSParameterBlock.bpb.num_root_entries == 0) ||
243 ((TheBIOSParameterBlock.bpb.num_root_entries *
244 sizeof (struct pcdir)) %
245 TheBIOSParameterBlock.bpb.bytes_per_sector) != 0) {
246 mountSanityCheckFails();
247 (void) fprintf(stderr,
248 gettext("Bogus number of root entries. "
249 "Giving up.\n"));
250 exit(2);
251 }
252 } else {
253 if (TheBIOSParameterBlock.bpb.num_root_entries != 0) {
254 mountSanityCheckFails();
255 (void) fprintf(stderr,
256 gettext("Bogus number of root entries. "
257 "Giving up.\n"));
258 exit(2);
259 }
260 }
261 /*
262 * In general, we would expect the number of FATs field to
263 * equal 2. Our mkfs and Windows have this as a default
264 * value. I suppose someone could override the default,
265 * though, so we'll sort of arbitrarily accept any number
266 * between 1 and 4 inclusive as reasonable values.
267 *
268 * XXX: Warn, but continue, if value is suspicious? (>2?)
269 */
270 if (TheBIOSParameterBlock.bpb.num_fats > 4 ||
271 TheBIOSParameterBlock.bpb.num_fats < 1) {
272 mountSanityCheckFails();
273 (void) fprintf(stderr,
274 gettext("Bogus number of FATs. Giving up.\n"));
275 exit(2);
276 }
277 computeFileAreaSize();
278 }
279