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 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 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