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