17c478bd9Sstevel@tonic-gate /* 27c478bd9Sstevel@tonic-gate * CDDL HEADER START 37c478bd9Sstevel@tonic-gate * 47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the 57c478bd9Sstevel@tonic-gate * Common Development and Distribution License, Version 1.0 only 67c478bd9Sstevel@tonic-gate * (the "License"). You may not use this file except in compliance 77c478bd9Sstevel@tonic-gate * with the License. 87c478bd9Sstevel@tonic-gate * 97c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 107c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 117c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions 127c478bd9Sstevel@tonic-gate * and limitations under the License. 137c478bd9Sstevel@tonic-gate * 147c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 157c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 167c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 177c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 187c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 197c478bd9Sstevel@tonic-gate * 207c478bd9Sstevel@tonic-gate * CDDL HEADER END 217c478bd9Sstevel@tonic-gate */ 227c478bd9Sstevel@tonic-gate /* 23*db92b35aSGary Mills * Copyright (c) 2011 Gary Mills 24*db92b35aSGary Mills * 257c478bd9Sstevel@tonic-gate * Copyright (c) 1999,2000 by Sun Microsystems, Inc. 267c478bd9Sstevel@tonic-gate * All rights reserved. 277c478bd9Sstevel@tonic-gate */ 287c478bd9Sstevel@tonic-gate 297c478bd9Sstevel@tonic-gate /* 307c478bd9Sstevel@tonic-gate * fsck_pcfs -- routines for manipulating the BPB (BIOS parameter block) 317c478bd9Sstevel@tonic-gate * of the file system. 327c478bd9Sstevel@tonic-gate */ 337c478bd9Sstevel@tonic-gate #include <stdio.h> 347c478bd9Sstevel@tonic-gate #include <stdlib.h> 357c478bd9Sstevel@tonic-gate #include <string.h> 367c478bd9Sstevel@tonic-gate #include <unistd.h> 377c478bd9Sstevel@tonic-gate #include <libintl.h> 387c478bd9Sstevel@tonic-gate #include <sys/types.h> 397c478bd9Sstevel@tonic-gate #include <sys/dktp/fdisk.h> 407c478bd9Sstevel@tonic-gate #include <sys/fs/pc_fs.h> 417c478bd9Sstevel@tonic-gate #include <sys/fs/pc_dir.h> 427c478bd9Sstevel@tonic-gate #include <sys/fs/pc_label.h> 437c478bd9Sstevel@tonic-gate #include "pcfs_common.h" 447c478bd9Sstevel@tonic-gate #include "fsck_pcfs.h" 457c478bd9Sstevel@tonic-gate #include "pcfs_bpb.h" 467c478bd9Sstevel@tonic-gate 477c478bd9Sstevel@tonic-gate extern off64_t FirstClusterOffset; 487c478bd9Sstevel@tonic-gate extern off64_t PartitionOffset; 497c478bd9Sstevel@tonic-gate extern int32_t BytesPerCluster; 507c478bd9Sstevel@tonic-gate extern int32_t TotalClusters; 517c478bd9Sstevel@tonic-gate extern int32_t LastCluster; 527c478bd9Sstevel@tonic-gate extern int32_t RootDirSize; 537c478bd9Sstevel@tonic-gate extern int32_t FATSize; 547c478bd9Sstevel@tonic-gate extern short FATEntrySize; 557c478bd9Sstevel@tonic-gate extern bpb_t TheBIOSParameterBlock; 567c478bd9Sstevel@tonic-gate extern int IsFAT32; 577c478bd9Sstevel@tonic-gate extern int Verbose; 587c478bd9Sstevel@tonic-gate 597c478bd9Sstevel@tonic-gate static void 607c478bd9Sstevel@tonic-gate computeFileAreaSize(void) 617c478bd9Sstevel@tonic-gate { 627c478bd9Sstevel@tonic-gate int32_t dataSectors; 637c478bd9Sstevel@tonic-gate int32_t overhead; 647c478bd9Sstevel@tonic-gate 657c478bd9Sstevel@tonic-gate /* 667c478bd9Sstevel@tonic-gate * Compute bytes/cluster for later reference 677c478bd9Sstevel@tonic-gate */ 687c478bd9Sstevel@tonic-gate BytesPerCluster = TheBIOSParameterBlock.bpb.sectors_per_cluster * 697c478bd9Sstevel@tonic-gate TheBIOSParameterBlock.bpb.bytes_per_sector; 707c478bd9Sstevel@tonic-gate 717c478bd9Sstevel@tonic-gate /* 727c478bd9Sstevel@tonic-gate * First we'll find total number of sectors in the file area... 737c478bd9Sstevel@tonic-gate */ 747c478bd9Sstevel@tonic-gate if (TheBIOSParameterBlock.bpb.sectors_in_volume > 0) 757c478bd9Sstevel@tonic-gate dataSectors = TheBIOSParameterBlock.bpb.sectors_in_volume; 767c478bd9Sstevel@tonic-gate else 777c478bd9Sstevel@tonic-gate dataSectors = 787c478bd9Sstevel@tonic-gate TheBIOSParameterBlock.bpb.sectors_in_logical_volume; 797c478bd9Sstevel@tonic-gate 807c478bd9Sstevel@tonic-gate overhead = TheBIOSParameterBlock.bpb.resv_sectors; 817c478bd9Sstevel@tonic-gate 827c478bd9Sstevel@tonic-gate RootDirSize = TheBIOSParameterBlock.bpb.num_root_entries * 837c478bd9Sstevel@tonic-gate sizeof (struct pcdir); 847c478bd9Sstevel@tonic-gate overhead += RootDirSize / TheBIOSParameterBlock.bpb.bytes_per_sector; 857c478bd9Sstevel@tonic-gate 867c478bd9Sstevel@tonic-gate if (TheBIOSParameterBlock.bpb.sectors_per_fat) { 877c478bd9Sstevel@tonic-gate /* 887c478bd9Sstevel@tonic-gate * Good old FAT12 or FAT16 897c478bd9Sstevel@tonic-gate */ 907c478bd9Sstevel@tonic-gate overhead += TheBIOSParameterBlock.bpb.num_fats * 917c478bd9Sstevel@tonic-gate TheBIOSParameterBlock.bpb.sectors_per_fat; 927c478bd9Sstevel@tonic-gate /* 937c478bd9Sstevel@tonic-gate * Compute this for later - when we actually pull in a copy 947c478bd9Sstevel@tonic-gate * of the FAT 957c478bd9Sstevel@tonic-gate */ 967c478bd9Sstevel@tonic-gate FATSize = TheBIOSParameterBlock.bpb.sectors_per_fat * 977c478bd9Sstevel@tonic-gate TheBIOSParameterBlock.bpb.bytes_per_sector; 987c478bd9Sstevel@tonic-gate } else { 997c478bd9Sstevel@tonic-gate /* 1007c478bd9Sstevel@tonic-gate * FAT32 1017c478bd9Sstevel@tonic-gate * I'm unsure if this is always going to work. At one 1027c478bd9Sstevel@tonic-gate * point during the creation of this program and mkfs_pcfs 1037c478bd9Sstevel@tonic-gate * it seemed that Windows had created an fs where it had 1047c478bd9Sstevel@tonic-gate * rounded big_sectors_per_fat up to a cluster boundary. 1057c478bd9Sstevel@tonic-gate * Later, though, I encountered a problem where I wasn't 1067c478bd9Sstevel@tonic-gate * finding the root directory because I was looking in the 1077c478bd9Sstevel@tonic-gate * wrong place by doing that same roundup. So, for now, 1087c478bd9Sstevel@tonic-gate * I'm backing off on the cluster boundary thing and just 1097c478bd9Sstevel@tonic-gate * believing what I am told. 1107c478bd9Sstevel@tonic-gate */ 1117c478bd9Sstevel@tonic-gate overhead += TheBIOSParameterBlock.bpb.num_fats * 1127c478bd9Sstevel@tonic-gate TheBIOSParameterBlock.bpb32.big_sectors_per_fat; 1137c478bd9Sstevel@tonic-gate /* 1147c478bd9Sstevel@tonic-gate * Compute this for later - when we actually pull in a copy 1157c478bd9Sstevel@tonic-gate * of the FAT 1167c478bd9Sstevel@tonic-gate */ 1177c478bd9Sstevel@tonic-gate FATSize = TheBIOSParameterBlock.bpb32.big_sectors_per_fat * 1187c478bd9Sstevel@tonic-gate TheBIOSParameterBlock.bpb.bytes_per_sector; 1197c478bd9Sstevel@tonic-gate } 1207c478bd9Sstevel@tonic-gate 1217c478bd9Sstevel@tonic-gate /* 1227c478bd9Sstevel@tonic-gate * Now change sectors to clusters. The computed value for 1237c478bd9Sstevel@tonic-gate * TotalClusters is persistent for the remainder of execution. 1247c478bd9Sstevel@tonic-gate */ 1257c478bd9Sstevel@tonic-gate dataSectors -= overhead; 1267c478bd9Sstevel@tonic-gate TotalClusters = dataSectors / 1277c478bd9Sstevel@tonic-gate TheBIOSParameterBlock.bpb.sectors_per_cluster; 1287c478bd9Sstevel@tonic-gate 1297c478bd9Sstevel@tonic-gate /* 1307c478bd9Sstevel@tonic-gate * Also need to compute last cluster and offset of the first cluster 1317c478bd9Sstevel@tonic-gate */ 1327c478bd9Sstevel@tonic-gate LastCluster = TotalClusters + FIRST_CLUSTER; 1337c478bd9Sstevel@tonic-gate FirstClusterOffset = overhead * 1347c478bd9Sstevel@tonic-gate TheBIOSParameterBlock.bpb.bytes_per_sector; 1357c478bd9Sstevel@tonic-gate FirstClusterOffset += PartitionOffset; 1367c478bd9Sstevel@tonic-gate 1377c478bd9Sstevel@tonic-gate /* 1387c478bd9Sstevel@tonic-gate * XXX this should probably be more sophisticated 1397c478bd9Sstevel@tonic-gate */ 1407c478bd9Sstevel@tonic-gate if (IsFAT32) 1417c478bd9Sstevel@tonic-gate FATEntrySize = 32; 1427c478bd9Sstevel@tonic-gate else { 1437c478bd9Sstevel@tonic-gate if (TotalClusters <= DOS_F12MAXC) 1447c478bd9Sstevel@tonic-gate FATEntrySize = 12; 1457c478bd9Sstevel@tonic-gate else 1467c478bd9Sstevel@tonic-gate FATEntrySize = 16; 1477c478bd9Sstevel@tonic-gate } 1487c478bd9Sstevel@tonic-gate 1497c478bd9Sstevel@tonic-gate if (Verbose) { 1507c478bd9Sstevel@tonic-gate (void) fprintf(stderr, 1517c478bd9Sstevel@tonic-gate gettext("Disk has a file area of %d " 1527c478bd9Sstevel@tonic-gate "allocation units,\neach with %d sectors = %llu " 1537c478bd9Sstevel@tonic-gate "bytes.\n"), TotalClusters, 1547c478bd9Sstevel@tonic-gate TheBIOSParameterBlock.bpb.sectors_per_cluster, 1557c478bd9Sstevel@tonic-gate (uint64_t)TotalClusters * 1567c478bd9Sstevel@tonic-gate TheBIOSParameterBlock.bpb.sectors_per_cluster * 1577c478bd9Sstevel@tonic-gate TheBIOSParameterBlock.bpb.bytes_per_sector); 1587c478bd9Sstevel@tonic-gate (void) fprintf(stderr, 1597c478bd9Sstevel@tonic-gate gettext("File system overhead of %d sectors.\n"), overhead); 1607c478bd9Sstevel@tonic-gate (void) fprintf(stderr, 1617c478bd9Sstevel@tonic-gate gettext("The last cluster is %d\n"), LastCluster); 1627c478bd9Sstevel@tonic-gate } 1637c478bd9Sstevel@tonic-gate } 1647c478bd9Sstevel@tonic-gate 1657c478bd9Sstevel@tonic-gate /* 1667c478bd9Sstevel@tonic-gate * XXX - right now we aren't attempting to fix anything that looks bad, 1677c478bd9Sstevel@tonic-gate * instead we just give up. 1687c478bd9Sstevel@tonic-gate */ 1697c478bd9Sstevel@tonic-gate void 1707c478bd9Sstevel@tonic-gate readBPB(int fd) 1717c478bd9Sstevel@tonic-gate { 1727c478bd9Sstevel@tonic-gate boot_sector_t ubpb; 1737c478bd9Sstevel@tonic-gate 1747c478bd9Sstevel@tonic-gate /* 1757c478bd9Sstevel@tonic-gate * The BPB is the first sector of the file system 1767c478bd9Sstevel@tonic-gate */ 1777c478bd9Sstevel@tonic-gate if (lseek64(fd, PartitionOffset, SEEK_SET) < 0) { 1787c478bd9Sstevel@tonic-gate mountSanityCheckFails(); 1797c478bd9Sstevel@tonic-gate perror(gettext("Cannot seek to start of disk partition")); 1807c478bd9Sstevel@tonic-gate (void) close(fd); 1817c478bd9Sstevel@tonic-gate exit(7); 1827c478bd9Sstevel@tonic-gate } 1837c478bd9Sstevel@tonic-gate if (Verbose) 1847c478bd9Sstevel@tonic-gate (void) fprintf(stderr, 1857c478bd9Sstevel@tonic-gate gettext("Reading BIOS parameter block\n")); 1867c478bd9Sstevel@tonic-gate if (read(fd, ubpb.buf, BPSEC) < BPSEC) { 1877c478bd9Sstevel@tonic-gate mountSanityCheckFails(); 1887c478bd9Sstevel@tonic-gate perror(gettext("Read BIOS parameter block")); 1897c478bd9Sstevel@tonic-gate (void) close(fd); 1907c478bd9Sstevel@tonic-gate exit(2); 1917c478bd9Sstevel@tonic-gate } 1927c478bd9Sstevel@tonic-gate 1937c478bd9Sstevel@tonic-gate if (ltohs(ubpb.mb.signature) != BOOTSECSIG) { 1947c478bd9Sstevel@tonic-gate mountSanityCheckFails(); 1957c478bd9Sstevel@tonic-gate (void) fprintf(stderr, 1967c478bd9Sstevel@tonic-gate gettext("Bad signature on BPB. Giving up.\n")); 1977c478bd9Sstevel@tonic-gate exit(2); 1987c478bd9Sstevel@tonic-gate } 1997c478bd9Sstevel@tonic-gate 2007c478bd9Sstevel@tonic-gate #ifdef _BIG_ENDIAN 2017c478bd9Sstevel@tonic-gate swap_pack_grabbpb(&TheBIOSParameterBlock, &(ubpb.bs)); 2027c478bd9Sstevel@tonic-gate #else 2037c478bd9Sstevel@tonic-gate (void) memcpy(&(TheBIOSParameterBlock.bpb), &(ubpb.bs.bs_front.bs_bpb), 2047c478bd9Sstevel@tonic-gate sizeof (TheBIOSParameterBlock.bpb)); 2057c478bd9Sstevel@tonic-gate (void) memcpy(&(TheBIOSParameterBlock.ebpb), &(ubpb.bs.bs_ebpb), 2067c478bd9Sstevel@tonic-gate sizeof (TheBIOSParameterBlock.ebpb)); 2077c478bd9Sstevel@tonic-gate #endif 2087c478bd9Sstevel@tonic-gate /* 2097c478bd9Sstevel@tonic-gate * In general, we would expect the bytes per sector to 2107c478bd9Sstevel@tonic-gate * equal 256 (BPSEC). I personally have yet to see a file 2117c478bd9Sstevel@tonic-gate * system where this isn't true but apparently some weird media 2127c478bd9Sstevel@tonic-gate * have different sector sizes. So we'll accept a couple of 2137c478bd9Sstevel@tonic-gate * other small multiples of 256 as valid sizes. 2147c478bd9Sstevel@tonic-gate */ 2157c478bd9Sstevel@tonic-gate if (TheBIOSParameterBlock.bpb.bytes_per_sector != BPSEC && 2167c478bd9Sstevel@tonic-gate TheBIOSParameterBlock.bpb.bytes_per_sector != 2 * BPSEC && 2177c478bd9Sstevel@tonic-gate TheBIOSParameterBlock.bpb.bytes_per_sector != 4 * BPSEC) { 2187c478bd9Sstevel@tonic-gate mountSanityCheckFails(); 2197c478bd9Sstevel@tonic-gate (void) fprintf(stderr, 2207c478bd9Sstevel@tonic-gate gettext("Bogus bytes per sector value. Giving up.\n")); 2217c478bd9Sstevel@tonic-gate exit(2); 2227c478bd9Sstevel@tonic-gate } 223*db92b35aSGary Mills if (!(ISP2(TheBIOSParameterBlock.bpb.sectors_per_cluster) && 224*db92b35aSGary Mills IN_RANGE(TheBIOSParameterBlock.bpb.sectors_per_cluster, 225*db92b35aSGary Mills 1, 128))) { 2267c478bd9Sstevel@tonic-gate mountSanityCheckFails(); 2277c478bd9Sstevel@tonic-gate (void) fprintf(stderr, 2287c478bd9Sstevel@tonic-gate gettext("Bogus sectors per cluster value. Giving up.\n")); 2297c478bd9Sstevel@tonic-gate (void) close(fd); 2307c478bd9Sstevel@tonic-gate exit(6); 2317c478bd9Sstevel@tonic-gate } 2327c478bd9Sstevel@tonic-gate if (TheBIOSParameterBlock.bpb.sectors_per_fat == 0) { 2337c478bd9Sstevel@tonic-gate #ifdef _BIG_ENDIAN 2347c478bd9Sstevel@tonic-gate swap_pack_grab32bpb(&TheBIOSParameterBlock, &(ubpb.bs)); 2357c478bd9Sstevel@tonic-gate #else 2367c478bd9Sstevel@tonic-gate (void) memcpy(&(TheBIOSParameterBlock.bpb32), 2377c478bd9Sstevel@tonic-gate &(ubpb.bs32.bs_bpb32), 2387c478bd9Sstevel@tonic-gate sizeof (TheBIOSParameterBlock.bpb32)); 2397c478bd9Sstevel@tonic-gate #endif 2407c478bd9Sstevel@tonic-gate IsFAT32 = 1; 2417c478bd9Sstevel@tonic-gate } 2427c478bd9Sstevel@tonic-gate if (!IsFAT32) { 2437c478bd9Sstevel@tonic-gate if ((TheBIOSParameterBlock.bpb.num_root_entries == 0) || 2447c478bd9Sstevel@tonic-gate ((TheBIOSParameterBlock.bpb.num_root_entries * 2457c478bd9Sstevel@tonic-gate sizeof (struct pcdir)) % 2467c478bd9Sstevel@tonic-gate TheBIOSParameterBlock.bpb.bytes_per_sector) != 0) { 2477c478bd9Sstevel@tonic-gate mountSanityCheckFails(); 2487c478bd9Sstevel@tonic-gate (void) fprintf(stderr, 2497c478bd9Sstevel@tonic-gate gettext("Bogus number of root entries. " 2507c478bd9Sstevel@tonic-gate "Giving up.\n")); 2517c478bd9Sstevel@tonic-gate exit(2); 2527c478bd9Sstevel@tonic-gate } 2537c478bd9Sstevel@tonic-gate } else { 2547c478bd9Sstevel@tonic-gate if (TheBIOSParameterBlock.bpb.num_root_entries != 0) { 2557c478bd9Sstevel@tonic-gate mountSanityCheckFails(); 2567c478bd9Sstevel@tonic-gate (void) fprintf(stderr, 2577c478bd9Sstevel@tonic-gate gettext("Bogus number of root entries. " 2587c478bd9Sstevel@tonic-gate "Giving up.\n")); 2597c478bd9Sstevel@tonic-gate exit(2); 2607c478bd9Sstevel@tonic-gate } 2617c478bd9Sstevel@tonic-gate } 2627c478bd9Sstevel@tonic-gate /* 2637c478bd9Sstevel@tonic-gate * In general, we would expect the number of FATs field to 2647c478bd9Sstevel@tonic-gate * equal 2. Our mkfs and Windows have this as a default 2657c478bd9Sstevel@tonic-gate * value. I suppose someone could override the default, 2667c478bd9Sstevel@tonic-gate * though, so we'll sort of arbitrarily accept any number 2677c478bd9Sstevel@tonic-gate * between 1 and 4 inclusive as reasonable values. 2687c478bd9Sstevel@tonic-gate * 2697c478bd9Sstevel@tonic-gate * XXX: Warn, but continue, if value is suspicious? (>2?) 2707c478bd9Sstevel@tonic-gate */ 2717c478bd9Sstevel@tonic-gate if (TheBIOSParameterBlock.bpb.num_fats > 4 || 2727c478bd9Sstevel@tonic-gate TheBIOSParameterBlock.bpb.num_fats < 1) { 2737c478bd9Sstevel@tonic-gate mountSanityCheckFails(); 2747c478bd9Sstevel@tonic-gate (void) fprintf(stderr, 2757c478bd9Sstevel@tonic-gate gettext("Bogus number of FATs. Giving up.\n")); 2767c478bd9Sstevel@tonic-gate exit(2); 2777c478bd9Sstevel@tonic-gate } 2787c478bd9Sstevel@tonic-gate computeFileAreaSize(); 2797c478bd9Sstevel@tonic-gate } 280