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 * Copyright (c) 2011 Gary Mills
26 * Copyright 2024 MNX Cloud, Inc.
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 if (TheBIOSParameterBlock.bpb.bytes_per_sector != 512 &&
209 TheBIOSParameterBlock.bpb.bytes_per_sector != 1024 &&
210 TheBIOSParameterBlock.bpb.bytes_per_sector != 2048 &&
211 TheBIOSParameterBlock.bpb.bytes_per_sector != 4096) {
212 mountSanityCheckFails();
213 (void) fprintf(stderr,
214 gettext("Bogus bytes per sector value. Giving up.\n"));
215 exit(2);
216 }
217 if (!(ISP2(TheBIOSParameterBlock.bpb.sectors_per_cluster) &&
218 IN_RANGE(TheBIOSParameterBlock.bpb.sectors_per_cluster,
219 1, 128))) {
220 mountSanityCheckFails();
221 (void) fprintf(stderr,
222 gettext("Bogus sectors per cluster value. Giving up.\n"));
223 (void) close(fd);
224 exit(6);
225 }
226 if (TheBIOSParameterBlock.bpb.sectors_per_fat == 0) {
227 #ifdef _BIG_ENDIAN
228 swap_pack_grab32bpb(&TheBIOSParameterBlock, &(ubpb.bs));
229 #else
230 (void) memcpy(&(TheBIOSParameterBlock.bpb32),
231 &(ubpb.bs32.bs_bpb32),
232 sizeof (TheBIOSParameterBlock.bpb32));
233 #endif
234 IsFAT32 = 1;
235 }
236 if (!IsFAT32) {
237 if ((TheBIOSParameterBlock.bpb.num_root_entries == 0) ||
238 ((TheBIOSParameterBlock.bpb.num_root_entries *
239 sizeof (struct pcdir)) %
240 TheBIOSParameterBlock.bpb.bytes_per_sector) != 0) {
241 mountSanityCheckFails();
242 (void) fprintf(stderr,
243 gettext("Bogus number of root entries. "
244 "Giving up.\n"));
245 exit(2);
246 }
247 } else {
248 if (TheBIOSParameterBlock.bpb.num_root_entries != 0) {
249 mountSanityCheckFails();
250 (void) fprintf(stderr,
251 gettext("Bogus number of root entries. "
252 "Giving up.\n"));
253 exit(2);
254 }
255 }
256 /*
257 * In general, we would expect the number of FATs field to
258 * equal 2. Our mkfs and Windows have this as a default
259 * value. I suppose someone could override the default,
260 * though, so we'll sort of arbitrarily accept any number
261 * between 1 and 4 inclusive as reasonable values.
262 *
263 * XXX: Warn, but continue, if value is suspicious? (>2?)
264 */
265 if (TheBIOSParameterBlock.bpb.num_fats > 4 ||
266 TheBIOSParameterBlock.bpb.num_fats < 1) {
267 mountSanityCheckFails();
268 (void) fprintf(stderr,
269 gettext("Bogus number of FATs. Giving up.\n"));
270 exit(2);
271 }
272 computeFileAreaSize();
273 }
274