bpb.c revision db92b35af583bf73fcbca649f84420ea778db26b
/*
* 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <libintl.h>
#include <sys/types.h>
#include <sys/dktp/fdisk.h>
#include <sys/fs/pc_fs.h>
#include <sys/fs/pc_dir.h>
#include <sys/fs/pc_label.h>
#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();
}