/***************
* $Id: fat.c,v 1.8 2001/05/30 15:47:03 harbourn Exp $
* File allocation table processing module for fatback
***************
*/
#include <sys/types.h>
#include <stdlib.h>
#include <assert.h>
#include "vbr.h"
#include "output.h"
#include "util.h"
#include "input.h"
#include "fat.h"
static clust_t *read_fat12(unsigned long *, off_t, int);
static unsigned fat12_first_half(u_int8_t *);
static unsigned fat12_last_half(u_int8_t *);
static clust_t *read_fat16(unsigned long *, off_t, int);
static clust_t *read_fat32(unsigned long *, off_t, int);
/* these constants describe the conditions for clusters */
static const unsigned long RESERVED = 0xFFFFFF0L;
static const unsigned long BAD = 0xFFFFFF7L;
static const unsigned long LAST = 0xFFFFFFFL;
/*
* The main interface to this module. it sets up proper
* values based on the vbr and calls read_fat[12,16,32] to do the
* actual loading of the values into the array. the offset
* parameter is the offset of the beginning of the partition
*/
clust_t *build_fat(unsigned long *entries, off_t offset, vbr_t vbr)
{
unsigned long fat_size;
clust_t *clust_array;
clust_t *(*read_fat)(unsigned long *,off_t,int);
const int DIRENT_SIZE = 32;
assert(entries);
assert(vbr);
fat_size = vbr->bytes_per_sect;
fat_size *= vbr->sects_per_fat;
offset += vbr->reserved_sects * vbr->bytes_per_sect;
switch (get_fs_type(vbr)) {
case VBR_FAT12:
read_fat = &read_fat12;
break;
case VBR_FAT16:
read_fat = &read_fat16;
break;
case VBR_FAT32:
read_fat = &read_fat32;
break;
default:
read_fat = NULL;
}
if (!read_fat) {
display(NORMAL, "No valid fat filesystem found\n");
return NULL;
}
if (!(clust_array = (*read_fat)(entries, offset, fat_size))) {
display(NORMAL, "Unable to read fat filesystem at offset %l\n", offset);
return NULL;
}
/* Now fill the clusters structs with their offsets
* and other information */
{
unsigned long i;
off_t c2_off; /* The offset of the beginning cluster */
unsigned cluster_size;
cluster_size = vbr->bytes_per_sect * vbr->sects_per_clust;
c2_off = (vbr->fat_copies * vbr->sects_per_fat);
c2_off *= vbr->bytes_per_sect;
c2_off += vbr->max_rdir_entries * DIRENT_SIZE;
c2_off += offset;
for (i = 0; i < *entries; i++) {
if (i < 2)
continue;
clust_array[i].loc = c2_off + ((i-2) * cluster_size);
/* all clusters are assumed to be free in
* the beginning, so lets flag them as such */
clust_array[i].flags = CLUST_FREE;
clust_array[i].owner = NULL;
}
}
return clust_array;
}
/*
* These functions provide an abstract way to tell if a FAT entry
* is reserved, bad, or the end of a chain
*/
int clust_is_resvd(clust_t *entry)
{
return entry->fat_entry == RESERVED;
}
int clust_is_bad(clust_t *entry)
{
return entry->fat_entry == BAD;
}
int clust_is_end(clust_t *entry)
{
return entry->fat_entry == LAST;
}
/*
* Marks the clusters in the givin
* cluster chain with a flag
*/
void flag_chain(clust_t *clusts, unsigned long cnum, u_int8_t flag)
{
/* This function should not be used to flag something as
* deleted, because it doesnt check the existing flag */
do clusts[cnum].flags = flag;
while (!clust_is_end(&clusts[cnum])
&& (cnum = clusts[cnum].fat_entry));
}
/*
* Determine how long a chain is (in number of clusters)
*/
unsigned long chain_length(clust_t *clusts, unsigned long chain)
{
unsigned long retval = 1, cnum = chain;
if (chain < 2)
return 0;
while (!clust_is_end(&clusts[cnum]) &&
!clust_is_bad(&clusts[cnum]) &&
!clust_is_resvd(&clusts[cnum])) {
cnum = clusts[cnum].fat_entry;
retval++;
}
return retval;
}
/*
* Reads in the 12bit File Allocation table and puts its values into
* an array. it returns that array.
*/
static clust_t *
read_fat12(unsigned long *entries, off_t offset, int buf_size)
{
int i, fat_index = 0;
u_int8_t *buffer;
clust_t *fat_array;
/* See the note in read_fat16() for an explaination of these
* constants
*/
const int RESERVED_MIN = 0xFF0;
const int RESERVED_MAX = 0xFF6;
const int BAD12 = 0xFF7;
const int LAST_MIN = 0xFF8;
const int LAST_MAX = 0xFFF;
assert(entries);
if (!buf_size)
return NULL;
*entries = (buf_size * 2) / 3;
fat_array = emalloc(*entries * sizeof *fat_array);
buffer = emalloc(buf_size);
if (!read_data(buffer, offset, buf_size))
return NULL;
for (i = 0; i < buf_size; i += 3) {
fat_array[fat_index].fat_entry = fat12_first_half(buffer + i);
if ((RESERVED_MIN <= fat_array[fat_index].fat_entry) &&
(fat_array[fat_index].fat_entry <= RESERVED_MAX))
fat_array[fat_index].fat_entry = RESERVED;
else if ((LAST_MIN <= fat_array[fat_index].fat_entry) &&
(fat_array[fat_index].fat_entry <= LAST_MAX))
fat_array[fat_index].fat_entry = LAST;
else if (fat_array[fat_index].fat_entry == BAD12)
fat_array[fat_index].fat_entry = BAD;
fat_index++;
fat_array[fat_index].fat_entry = fat12_last_half(buffer + i);
if ((RESERVED_MIN <= fat_array[fat_index].fat_entry) &&
(fat_array[fat_index].fat_entry <= RESERVED_MAX))
fat_array[fat_index].fat_entry = RESERVED;
else if ((LAST_MIN <= fat_array[fat_index].fat_entry) &&
(fat_array[fat_index].fat_entry <= LAST_MAX))
fat_array[fat_index].fat_entry = LAST;
else if (fat_array[fat_index].fat_entry == BAD12)
fat_array[fat_index].fat_entry = BAD;
fat_index++;
}
free(buffer);
return fat_array;
}
/*
* Take a pointer to 24 bits of data and extract the first 12
* bits of data stored in little endian form.
*/
static unsigned fat12_first_half(u_int8_t *buf)
{
unsigned retval;
static const int FIRST_HALF_MASK = 0x0F;
assert(buf);
retval = buf[0];
retval += (buf[1] & FIRST_HALF_MASK) << 8;
return retval;
}
/*
* Take a pointer to 24 bits of data and extract the last 12
* bits of data stored in little endian form.
*/
static unsigned fat12_last_half(u_int8_t *buf)
{
unsigned retval;
static const int LAST_HALF_MASK = 0xF0;
assert(buf);
retval = (buf[1] & LAST_HALF_MASK) >> 4;
retval += buf[2] << 4;
return retval;
}
/*
* Reads in the 16bit File allocation table into a buffer then
* puts the values into an array in a byte order independant
* fashion.
*/
static clust_t *
read_fat16(unsigned long *entries, off_t offset, int buf_size)
{
int i, fat_index = 0;
u_int8_t *buffer;
clust_t *fat_array;
/* Reserved clusters are never used, and the last cluster
* marker is always 0xffff, but Scott Mueller's book
* says that it could be anywhere from 0xfff8 to 0xffff.
* because of this I have added the confusing mess of MIN
* and MAX values for the conditions.
*/
const unsigned int RESERVED_MIN = 0xFFF0;
const unsigned int RESERVED_MAX = 0xFFF6;
const unsigned int BAD16 = 0xFFF7;
const unsigned int LAST_MIN = 0xFFF8;
const unsigned int LAST_MAX = 0xFFFF;
assert(entries);
if (!buf_size)
return NULL;
*entries = buf_size / 2;
fat_array = emalloc(*entries * sizeof *fat_array);
buffer = emalloc(buf_size);
if (!read_data(buffer, offset, buf_size))
return NULL;
for (i = 0; i < buf_size; i += 2, fat_index++) {
fat_array[fat_index].fat_entry = little_endian_16(buffer + i);
if ((RESERVED_MIN <= fat_array[fat_index].fat_entry) &&
(fat_array[fat_index].fat_entry <= RESERVED_MAX))
fat_array[fat_index].fat_entry = RESERVED;
else if ((LAST_MIN <= fat_array[fat_index].fat_entry) &&
(fat_array[fat_index].fat_entry <= LAST_MAX))
fat_array[fat_index].fat_entry = LAST;
else if (fat_array[fat_index].fat_entry == BAD16)
fat_array[fat_index].fat_entry = BAD;
}
free(buffer);
return fat_array;
}
/*
* Reads in the 32bit File allocation table into a buffer then
* puts the values into an array in a byte order independant
* fashion.
*/
static clust_t *
read_fat32(unsigned long *entries, off_t offset, int buf_size)
{
int i, fat_index = 0;
u_int8_t *buffer;
clust_t *fat_array;
/* See the note in read_fat16() for explaination of these const's
*/
const unsigned long RESERVED_MIN = 0xFFFFFF0;
const unsigned long RESERVED_MAX = 0xFFFFFF6;
const unsigned long BAD32 = 0xFFFFFF7;
const unsigned long LAST_MIN = 0xFFFFFF8;
const unsigned long LAST_MAX = 0xFFFFFFF;
assert(entries);
if (!buf_size)
return NULL;
*entries = buf_size / 4;
fat_array = emalloc(*entries * sizeof *fat_array);
buffer = emalloc(buf_size);
if (!read_data(buffer, offset, buf_size))
return NULL;
for (i = 0; i < buf_size; i += 4, fat_index++) {
fat_array[fat_index].fat_entry = little_endian_32(buffer + i);
if ((RESERVED_MIN <= fat_array[fat_index].fat_entry) &&
(fat_array[fat_index].fat_entry <= RESERVED_MAX))
fat_array[fat_index].fat_entry = RESERVED;
else if ((LAST_MIN <= fat_array[fat_index].fat_entry) &&
(fat_array[fat_index].fat_entry <= LAST_MAX))
fat_array[fat_index].fat_entry = LAST;
else if (fat_array[fat_index].fat_entry == BAD32)
fat_array[fat_index].fat_entry = BAD;
}
free(buffer);
return fat_array;
}
syntax highlighted by Code2HTML, v. 0.9.1