/* $Id: vbr.c,v 1.11 2001/05/30 15:47:04 harbourn Exp $
* VBR processing module for fatback
*/
#include <sys/types.h>
#include <assert.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
#include <math.h>
#include "vbr.h"
#include "fatback.h"
#include "util.h"
#include "sig.h"
#include "output.h"
#include "input.h"
#include "vars.h"
static vbr_t read_vbr(off_t);
static int scheck_vbr(vbr_t, sig_t);
enum { OEM_NAME_LEN = 8,
LABEL_LEN = 11,
FS_ID_LEN = 8
};
/*
* this is the initial interface called. it reads in a vbr and
* sets the Sector_size variable for use by almost all the functions in
* this module.
*/
vbr_t build_vbr(off_t offset)
{
vbr_t vbr;
if (!(vbr = read_vbr(offset))) {
display(NORMAL, "Unable to read Volume Boot Record\n");
return NULL;
}
return vbr;
}
/*
* Reads in a Volume Boot Record, and returns a pointer to it. this
* function calles emalloc(), and thus objects created by it need to
* be freed. Viva Datum!
*/
static vbr_t read_vbr(off_t offset)
{
vbr_t vbr = emalloc(sizeof *vbr);
u_int8_t *buffer;
sig_t signature;
int fat32_flag = 0;
fbvar_t *sectsize_var;
unsigned sectsize;
off_t serial_num_off, fs_id_off;
int i;
enum { OEM_NAME_OFF = 3,
BYTES_PER_SECT_OFF = 11,
SECTS_PER_CLUST_OFF = 13,
RESERVED_SECTS_OFF = 14,
FAT_COPIES_OFF = 16,
MAX_RDIR_ENTRIES_OFF = 17,
TOTAL_SECTS_S_OFF = 19,
MEDIA_DESCRIPTOR_OFF = 21,
SECTS_PER_FAT_OFF = 22,
SECTS_PER_TRACK_OFF = 24,
NUM_HEADS_OFF = 26,
HIDDEN_SECTS_OFF = 28,
TOTAL_SECTS_L_OFF = 32,
DRIVE_NUMBER_OFF = 36,
SECTS_PERFAT32_OFF = 36, /* FAT32 Specific offset */
RDIR_CLUST_OFF = 44, /* " " */
SERIAL_NUM32_OFF = 67, /* " " */
FS_ID32_OFF = 82, /* " " */
EXT_BOOT_REC_SIG_OFF = 38, /* FAT12/16 Specific offset */
SERIAL_NUM_OFF = 39, /* " " */
LABEL_OFF = 43, /* " " */
FS_ID_OFF = 54 /* " " */
};
/* get the sector size from the global fatback variable table */
sectsize_var = get_fbvar("sectsize");
if (!sectsize_var->val.ival) {
display(NORMAL, "Error: sectsize is set to 0!\n");
free(sectsize_var);
return NULL;
}
sectsize = sectsize_var->val.ival;
free(sectsize_var);
buffer = emalloc(sectsize);
if(!read_data(buffer, offset, sectsize))
return NULL;
/* load the oem name into a string */
vbr->oem_name = emalloc(OEM_NAME_LEN + 1); /* add for null terminator */
strncpy(vbr->oem_name, &buffer[OEM_NAME_OFF], OEM_NAME_LEN);
vbr->oem_name[OEM_NAME_LEN] = '\0'; /* null terminate the string*/
/* load other numeric values */
vbr->bytes_per_sect = little_endian_16(buffer + BYTES_PER_SECT_OFF);
vbr->sects_per_clust = little_endian_8(buffer + SECTS_PER_CLUST_OFF);
vbr->reserved_sects = little_endian_16(buffer + RESERVED_SECTS_OFF);
vbr->fat_copies = little_endian_8(buffer + FAT_COPIES_OFF);
vbr->max_rdir_entries = little_endian_16(buffer + MAX_RDIR_ENTRIES_OFF);
/* if max_rdir_entries is null, well proceed assuming this is FAT32 */
if (!vbr->max_rdir_entries)
fat32_flag++;
vbr->total_sects_s = little_endian_16(buffer + TOTAL_SECTS_S_OFF);
vbr->media_descriptor = little_endian_8(buffer + MEDIA_DESCRIPTOR_OFF);
vbr->sects_per_fat = little_endian_16(buffer + SECTS_PER_FAT_OFF);
vbr->sects_per_track = little_endian_16(buffer + SECTS_PER_TRACK_OFF);
vbr->num_heads = little_endian_8(buffer + NUM_HEADS_OFF);
vbr->hidden_sects = little_endian_32(buffer + HIDDEN_SECTS_OFF);
vbr->total_sects_l = little_endian_32(buffer + TOTAL_SECTS_L_OFF);
if (fat32_flag) {
vbr->sects_per_fat = little_endian_32(buffer + SECTS_PERFAT32_OFF);
vbr->d.fat32.root_dir_clust = little_endian_32(buffer + RDIR_CLUST_OFF);
serial_num_off = SERIAL_NUM32_OFF;
fs_id_off = FS_ID32_OFF;
} else {
vbr->d.fat1216.drive_number = little_endian_8(buffer + DRIVE_NUMBER_OFF);
vbr->d.fat1216.ext_boot_rec_sig = little_endian_8(buffer + EXT_BOOT_REC_SIG_OFF);
/* determine offset of root directory */
vbr->d.fat1216.root_dir_loc = vbr->reserved_sects;
vbr->d.fat1216.root_dir_loc += vbr->fat_copies * vbr->sects_per_fat;
vbr->d.fat1216.root_dir_loc *= vbr->bytes_per_sect;
vbr->d.fat1216.root_dir_loc += offset;
/* load the volume label into a string */
/* add for null terminator */
vbr->d.fat1216.label = emalloc(LABEL_LEN + 1);
strncpy(vbr->d.fat1216.label, &buffer[LABEL_OFF],
LABEL_LEN);
/* null terminate the string */
vbr->d.fat1216.label[LABEL_LEN] = '\0';
serial_num_off = SERIAL_NUM_OFF;
fs_id_off = FS_ID_OFF;
}
vbr->serial_num = little_endian_32(buffer + serial_num_off);
/* load the fs id into a string */
/* first test to make sure that it is a valid ascii string */
for (i = 0; i < FS_ID_LEN && isascii(buffer[fs_id_off + i]); i++)
;
if (i == FS_ID_LEN) {
vbr->fs_id = emalloc(fs_id_off + 1); /* add for null terminator */
strncpy(vbr->fs_id, &buffer[fs_id_off], FS_ID_LEN);
vbr->fs_id[FS_ID_LEN] = '\0'; /* null terminate the string */
} else
vbr->fs_id = NULL;
/* read in the boot sector signature */
signature = read_sig(&buffer[sectsize - 2]);
free(buffer);
/* run the VBR through a quick sanity check */
if (!scheck_vbr(vbr, signature)) {
display(NORMAL, "No valid VBR found at offset %d\n", offset);
return NULL;
}
return vbr;
}
/*
* Sanity check the Volume Boot Record
*/
static int scheck_vbr(vbr_t vbr, sig_t signature)
{
unsigned i, invalid = 0;
if (!vbr || !signature)
return 0;
/* Make sure all 8 char's of OEM name are ascii */
for (i = 0; i < OEM_NAME_LEN; i++)
if (!vbr->oem_name[i] || !isascii(vbr->oem_name[i]))
invalid++;
if (invalid) {
display(VERBOSE, "%d characters of the OEM name in the VBR are invalid\n", invalid);
/* return 0; */
}
/* Make sure sectors per cluster is a power of two, or 1 */
{
unsigned long x = vbr->sects_per_clust;
if ((x != 1) && (x != 2) && (x != 4) &&
(x != 8) && (x != 16) &&
(x != 32) && (x != 64) &&
(x != 128) && (x != 256)) {
display(VERBOSE, "Sectors per cluster byte is not a power of 2\n");
return 0;
}
}
/* Make sure FAT copies are non-zero, and report if > 2 */
if (!vbr->fat_copies) {
display(VERBOSE, "The VBR reports zero FAT copies\n");
return 0;
}
if (vbr->fat_copies > 2)
display(VERBOSE, "The VBR reports that FAT copies is greater than 2\n");
/* report if Media Descriptor byte is zero */
if (!vbr->media_descriptor)
display(VERBOSE, "The Media Descriptor byte in the VBR is zero\n");
/* make sure that reserved sectors is !0, and report if !1 */
if (!vbr->reserved_sects) {
display(VERBOSE, "The VBR reports 0 reserved sectors\n");
return 0;
}
if (!vbr->hidden_sects)
display(VERBOSE, "The VBR reports no hidden sectors\n");
/* report if the volume serial number is non-null */
if (!vbr->serial_num)
display(VERBOSE, "The VBR contains a null serial number\n");
/* ensure that total_sects_s and total_sects_t are mutually exclusive */
if (vbr->total_sects_s && vbr->total_sects_l) {
display(VERBOSE, "Total sectors 16bit and 32bit values exist in VBR\n");
return 0;
}
/* also ensure that at least one has a value */
if (!vbr->total_sects_s && !vbr->total_sects_l) {
display(VERBOSE, "No value in VBR for Total Sectors\n");
return 0;
}
/* if we have made it here, all must be good! */
return 1;
}
/*
* Find out which filesystem it is. (fat12,16, or 32?)
*/
fs_id_t get_fs_type(vbr_t vbr)
{
unsigned long total_sects, total_clusts;
enum { FAT12_MAX_CLUSTS = 0xFFF,
FAT16_MAX_CLUSTS = 0xFFFF,
FAT32_MAX_CLUSTS = 0xFFFFFFFFL
};
assert(vbr);
total_sects = vbr->total_sects_s ? vbr->total_sects_s :
vbr->total_sects_l;
total_clusts = total_sects / vbr->sects_per_clust;
if (total_clusts <= FAT12_MAX_CLUSTS)
return VBR_FAT12;
if (total_clusts <= FAT16_MAX_CLUSTS)
return VBR_FAT16;
return VBR_FAT32;
}
/*
* Determine what cluster the the root directory begins.
*/
unsigned long get_root_loc(off_t offset, vbr_t vbr)
{
assert(vbr);
if (vbr->max_rdir_entries)
return 0;
else
return vbr->d.fat32.root_dir_clust;
}
/*
* Log the Volume Boot Record information to the display log
*/
void log_vbr(vbr_t vbr)
{
assert(vbr);
display(VERBOSE, "oem_name: %s\n", vbr->oem_name);
display(VERBOSE, "bytes_per_sect: %u\n", vbr->bytes_per_sect);
display(VERBOSE, "reserved_sects: %u\n", vbr->reserved_sects);
display(VERBOSE, "fat_copies: %u\n", vbr->fat_copies);
display(VERBOSE, "max_rdir_entries: %u\n", vbr->max_rdir_entries);
display(VERBOSE, "total_sects_s: %u\n", vbr->total_sects_s);
display(VERBOSE, "media_descriptor: %x\n", vbr->media_descriptor);
display(VERBOSE, "sects_per_fat: %u\n", vbr->sects_per_fat);
display(VERBOSE, "sects_per_track: %u\n", vbr->sects_per_track);
display(VERBOSE, "num_heads: %u\n", vbr->num_heads);
display(VERBOSE, "hidden_sects: %lu\n", vbr->hidden_sects);
display(VERBOSE, "total_sects_l: %lu\n", vbr->total_sects_l);
display(VERBOSE, "serial_num: %lx\n", vbr->serial_num);
display(VERBOSE, "fs_id: %s\n", vbr->fs_id ? vbr->fs_id : "(invalid)");
display(VERBOSE, "Filesystem type is ");
switch (get_fs_type(vbr)) {
case VBR_FAT12:
display(VERBOSE, "FAT12\n");
break;
case VBR_FAT16:
display(VERBOSE, "FAT16\n");
break;
case VBR_FAT32:
display(VERBOSE, "FAT32\n");
break;
default:
display(VERBOSE, "(invalid)\n");
break;
}
}
syntax highlighted by Code2HTML, v. 0.9.1