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
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.
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]
23 * Copyright (c) 2011 Gary Mills
25 * Copyright (c) 1999,2000 by Sun Microsystems, Inc.
26 * All rights reserved.
30 * fsck_pcfs -- routines for manipulating the BPB (BIOS parameter block)
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"
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
;
60 computeFileAreaSize(void)
66 * Compute bytes/cluster for later reference
68 BytesPerCluster
= TheBIOSParameterBlock
.bpb
.sectors_per_cluster
*
69 TheBIOSParameterBlock
.bpb
.bytes_per_sector
;
72 * First we'll find total number of sectors in the file area...
74 if (TheBIOSParameterBlock
.bpb
.sectors_in_volume
> 0)
75 dataSectors
= TheBIOSParameterBlock
.bpb
.sectors_in_volume
;
78 TheBIOSParameterBlock
.bpb
.sectors_in_logical_volume
;
80 overhead
= TheBIOSParameterBlock
.bpb
.resv_sectors
;
82 RootDirSize
= TheBIOSParameterBlock
.bpb
.num_root_entries
*
83 sizeof (struct pcdir
);
84 overhead
+= RootDirSize
/ TheBIOSParameterBlock
.bpb
.bytes_per_sector
;
86 if (TheBIOSParameterBlock
.bpb
.sectors_per_fat
) {
88 * Good old FAT12 or FAT16
90 overhead
+= TheBIOSParameterBlock
.bpb
.num_fats
*
91 TheBIOSParameterBlock
.bpb
.sectors_per_fat
;
93 * Compute this for later - when we actually pull in a copy
96 FATSize
= TheBIOSParameterBlock
.bpb
.sectors_per_fat
*
97 TheBIOSParameterBlock
.bpb
.bytes_per_sector
;
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.
111 overhead
+= TheBIOSParameterBlock
.bpb
.num_fats
*
112 TheBIOSParameterBlock
.bpb32
.big_sectors_per_fat
;
114 * Compute this for later - when we actually pull in a copy
117 FATSize
= TheBIOSParameterBlock
.bpb32
.big_sectors_per_fat
*
118 TheBIOSParameterBlock
.bpb
.bytes_per_sector
;
122 * Now change sectors to clusters. The computed value for
123 * TotalClusters is persistent for the remainder of execution.
125 dataSectors
-= overhead
;
126 TotalClusters
= dataSectors
/
127 TheBIOSParameterBlock
.bpb
.sectors_per_cluster
;
130 * Also need to compute last cluster and offset of the first cluster
132 LastCluster
= TotalClusters
+ FIRST_CLUSTER
;
133 FirstClusterOffset
= overhead
*
134 TheBIOSParameterBlock
.bpb
.bytes_per_sector
;
135 FirstClusterOffset
+= PartitionOffset
;
138 * XXX this should probably be more sophisticated
143 if (TotalClusters
<= DOS_F12MAXC
)
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
);
166 * XXX - right now we aren't attempting to fix anything that looks bad,
167 * instead we just give up.
175 * The BPB is the first sector of the file system
177 if (lseek64(fd
, PartitionOffset
, SEEK_SET
) < 0) {
178 mountSanityCheckFails();
179 perror(gettext("Cannot seek to start of disk partition"));
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"));
193 if (ltohs(ubpb
.mb
.signature
) != BOOTSECSIG
) {
194 mountSanityCheckFails();
195 (void) fprintf(stderr
,
196 gettext("Bad signature on BPB. Giving up.\n"));
201 swap_pack_grabbpb(&TheBIOSParameterBlock
, &(ubpb
.bs
));
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
));
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.
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"));
223 if (!(ISP2(TheBIOSParameterBlock
.bpb
.sectors_per_cluster
) &&
224 IN_RANGE(TheBIOSParameterBlock
.bpb
.sectors_per_cluster
,
226 mountSanityCheckFails();
227 (void) fprintf(stderr
,
228 gettext("Bogus sectors per cluster value. Giving up.\n"));
232 if (TheBIOSParameterBlock
.bpb
.sectors_per_fat
== 0) {
234 swap_pack_grab32bpb(&TheBIOSParameterBlock
, &(ubpb
.bs
));
236 (void) memcpy(&(TheBIOSParameterBlock
.bpb32
),
237 &(ubpb
.bs32
.bs_bpb32
),
238 sizeof (TheBIOSParameterBlock
.bpb32
));
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. "
254 if (TheBIOSParameterBlock
.bpb
.num_root_entries
!= 0) {
255 mountSanityCheckFails();
256 (void) fprintf(stderr
,
257 gettext("Bogus number of root entries. "
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.
269 * XXX: Warn, but continue, if value is suspicious? (>2?)
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"));
278 computeFileAreaSize();