/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2011 Gary Mills * * Copyright (c) 1999,2000 by Sun Microsystems, Inc. * All rights reserved. */ /* * fsck_pcfs -- routines for manipulating the BPB (BIOS parameter block) * of the file system. */ #include #include #include #include #include #include #include #include #include #include #include "pcfs_common.h" #include "fsck_pcfs.h" #include "pcfs_bpb.h" extern off64_t FirstClusterOffset; extern off64_t PartitionOffset; extern int32_t BytesPerCluster; extern int32_t TotalClusters; extern int32_t LastCluster; extern int32_t RootDirSize; extern int32_t FATSize; extern short FATEntrySize; extern bpb_t TheBIOSParameterBlock; extern int IsFAT32; extern int Verbose; static void computeFileAreaSize(void) { int32_t dataSectors; int32_t overhead; /* * Compute bytes/cluster for later reference */ BytesPerCluster = TheBIOSParameterBlock.bpb.sectors_per_cluster * TheBIOSParameterBlock.bpb.bytes_per_sector; /* * First we'll find total number of sectors in the file area... */ if (TheBIOSParameterBlock.bpb.sectors_in_volume > 0) dataSectors = TheBIOSParameterBlock.bpb.sectors_in_volume; else dataSectors = TheBIOSParameterBlock.bpb.sectors_in_logical_volume; overhead = TheBIOSParameterBlock.bpb.resv_sectors; RootDirSize = TheBIOSParameterBlock.bpb.num_root_entries * sizeof (struct pcdir); overhead += RootDirSize / TheBIOSParameterBlock.bpb.bytes_per_sector; if (TheBIOSParameterBlock.bpb.sectors_per_fat) { /* * Good old FAT12 or FAT16 */ overhead += TheBIOSParameterBlock.bpb.num_fats * TheBIOSParameterBlock.bpb.sectors_per_fat; /* * Compute this for later - when we actually pull in a copy * of the FAT */ FATSize = TheBIOSParameterBlock.bpb.sectors_per_fat * TheBIOSParameterBlock.bpb.bytes_per_sector; } else { /* * FAT32 * I'm unsure if this is always going to work. At one * point during the creation of this program and mkfs_pcfs * it seemed that Windows had created an fs where it had * rounded big_sectors_per_fat up to a cluster boundary. * Later, though, I encountered a problem where I wasn't * finding the root directory because I was looking in the * wrong place by doing that same roundup. So, for now, * I'm backing off on the cluster boundary thing and just * believing what I am told. */ overhead += TheBIOSParameterBlock.bpb.num_fats * TheBIOSParameterBlock.bpb32.big_sectors_per_fat; /* * Compute this for later - when we actually pull in a copy * of the FAT */ FATSize = TheBIOSParameterBlock.bpb32.big_sectors_per_fat * TheBIOSParameterBlock.bpb.bytes_per_sector; } /* * Now change sectors to clusters. The computed value for * TotalClusters is persistent for the remainder of execution. */ dataSectors -= overhead; TotalClusters = dataSectors / TheBIOSParameterBlock.bpb.sectors_per_cluster; /* * Also need to compute last cluster and offset of the first cluster */ LastCluster = TotalClusters + FIRST_CLUSTER; FirstClusterOffset = overhead * TheBIOSParameterBlock.bpb.bytes_per_sector; FirstClusterOffset += PartitionOffset; /* * XXX this should probably be more sophisticated */ if (IsFAT32) FATEntrySize = 32; else { if (TotalClusters <= DOS_F12MAXC) FATEntrySize = 12; else FATEntrySize = 16; } if (Verbose) { (void) fprintf(stderr, gettext("Disk has a file area of %d " "allocation units,\neach with %d sectors = %llu " "bytes.\n"), TotalClusters, TheBIOSParameterBlock.bpb.sectors_per_cluster, (uint64_t)TotalClusters * TheBIOSParameterBlock.bpb.sectors_per_cluster * TheBIOSParameterBlock.bpb.bytes_per_sector); (void) fprintf(stderr, gettext("File system overhead of %d sectors.\n"), overhead); (void) fprintf(stderr, gettext("The last cluster is %d\n"), LastCluster); } } /* * XXX - right now we aren't attempting to fix anything that looks bad, * instead we just give up. */ void readBPB(int fd) { boot_sector_t ubpb; /* * The BPB is the first sector of the file system */ if (lseek64(fd, PartitionOffset, SEEK_SET) < 0) { mountSanityCheckFails(); perror(gettext("Cannot seek to start of disk partition")); (void) close(fd); exit(7); } if (Verbose) (void) fprintf(stderr, gettext("Reading BIOS parameter block\n")); if (read(fd, ubpb.buf, BPSEC) < BPSEC) { mountSanityCheckFails(); perror(gettext("Read BIOS parameter block")); (void) close(fd); exit(2); } if (ltohs(ubpb.mb.signature) != BOOTSECSIG) { mountSanityCheckFails(); (void) fprintf(stderr, gettext("Bad signature on BPB. Giving up.\n")); exit(2); } #ifdef _BIG_ENDIAN swap_pack_grabbpb(&TheBIOSParameterBlock, &(ubpb.bs)); #else (void) memcpy(&(TheBIOSParameterBlock.bpb), &(ubpb.bs.bs_front.bs_bpb), sizeof (TheBIOSParameterBlock.bpb)); (void) memcpy(&(TheBIOSParameterBlock.ebpb), &(ubpb.bs.bs_ebpb), sizeof (TheBIOSParameterBlock.ebpb)); #endif /* * In general, we would expect the bytes per sector to * equal 256 (BPSEC). I personally have yet to see a file * system where this isn't true but apparently some weird media * have different sector sizes. So we'll accept a couple of * other small multiples of 256 as valid sizes. */ if (TheBIOSParameterBlock.bpb.bytes_per_sector != BPSEC && TheBIOSParameterBlock.bpb.bytes_per_sector != 2 * BPSEC && TheBIOSParameterBlock.bpb.bytes_per_sector != 4 * BPSEC) { mountSanityCheckFails(); (void) fprintf(stderr, gettext("Bogus bytes per sector value. Giving up.\n")); exit(2); } if (!(ISP2(TheBIOSParameterBlock.bpb.sectors_per_cluster) && IN_RANGE(TheBIOSParameterBlock.bpb.sectors_per_cluster, 1, 128))) { mountSanityCheckFails(); (void) fprintf(stderr, gettext("Bogus sectors per cluster value. Giving up.\n")); (void) close(fd); exit(6); } if (TheBIOSParameterBlock.bpb.sectors_per_fat == 0) { #ifdef _BIG_ENDIAN swap_pack_grab32bpb(&TheBIOSParameterBlock, &(ubpb.bs)); #else (void) memcpy(&(TheBIOSParameterBlock.bpb32), &(ubpb.bs32.bs_bpb32), sizeof (TheBIOSParameterBlock.bpb32)); #endif IsFAT32 = 1; } if (!IsFAT32) { if ((TheBIOSParameterBlock.bpb.num_root_entries == 0) || ((TheBIOSParameterBlock.bpb.num_root_entries * sizeof (struct pcdir)) % TheBIOSParameterBlock.bpb.bytes_per_sector) != 0) { mountSanityCheckFails(); (void) fprintf(stderr, gettext("Bogus number of root entries. " "Giving up.\n")); exit(2); } } else { if (TheBIOSParameterBlock.bpb.num_root_entries != 0) { mountSanityCheckFails(); (void) fprintf(stderr, gettext("Bogus number of root entries. " "Giving up.\n")); exit(2); } } /* * In general, we would expect the number of FATs field to * equal 2. Our mkfs and Windows have this as a default * value. I suppose someone could override the default, * though, so we'll sort of arbitrarily accept any number * between 1 and 4 inclusive as reasonable values. * * XXX: Warn, but continue, if value is suspicious? (>2?) */ if (TheBIOSParameterBlock.bpb.num_fats > 4 || TheBIOSParameterBlock.bpb.num_fats < 1) { mountSanityCheckFails(); (void) fprintf(stderr, gettext("Bogus number of FATs. Giving up.\n")); exit(2); } computeFileAreaSize(); }