Sampler
Some sample c code (hey, I almost did it in Haskell) to print out the contents of a fat up to a (short) root directory.
Size 16.3 kB - File type text/x-csrcFile contents
/*
* compile with gcc -o newfat -Wall --pedantic newfat.c
*
* this code is not pretty.
* some of the problems arise from it being a quick and dirty one afternoon
* coding session, some are actually deliberate decisions made because it is
* intended as code for students to look at
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/file.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <sys/mman.h>
#include <string.h>
typedef unsigned char BYTE ;
typedef unsigned char SHORT [2] ;
typedef unsigned char INT [4] ;
/*
* utility macros
* note: + in the conversions was changed to bitwise or because
* signs were propagating
* also since the macros were being used in places with different
* values for "x", extra parens were added to resolve syntax errors
*/
#define SHORT2int(x) ((x[0]) | ((x[1])<<8))
#define INT2int(x) ((x[0]) | ((x[1])<<8) | ((x[2])<<16) | ((x[3])<<24))
#define BYTE2int(x) (x)
#define MASK(x) (x & 0xFF)
#define SM(x,s) MASK(x >> s)
/*
* these have not been tested
*/
#define int2BYTE(x,y) (y[0] = SM(x,0))
#define int2SHORT(x,y) (y[0] = SM(x,0), y[1] = SM(x,8))
#define int2INT(x,y) (y[0] = SM(x,0), y[1] = SM(x,8), y[2]=SM(x,16), y[3]=SM(x,24))
#define bit(i,x) ((i)>>(x) & 0x1)
/*
* time and date utility macros
*/
#define hours(i) (((i)>>11)&0x1F)
#define minutes(i) (((i)>>5)&0x2F)
#define seconds(i) (2*(((i)>>5)&0x1F))
#define year(i) (1980+(((i)>>9)&0x7F))
#define month(i) (((i)>>5)&0xF)
#define day(i) (((i)>>5)&0x1F)
/*
* boot sector definition
*/
struct fat_bs
{
BYTE jmp[3] ;
BYTE oem[8] ;
SHORT bytes_per_sector ;
BYTE sectors_per_cluster ;
SHORT reserved_sectors ;
BYTE fat_count ;
SHORT max_root_dirs ;
SHORT total_sectors_1 ;
BYTE media_descriptor ;
SHORT sectors_per_fat_12_16 ;
SHORT sectors_per_track ;
SHORT head_count ;
INT hidden_sectors ;
INT total_sectors_2 ;
INT sectors_per_fat ;
SHORT fat_flags ;
SHORT version ;
INT root_cluster_start ;
SHORT fs_info_sector ;
SHORT boot_sector_copy ;
BYTE reserved[12] ;
BYTE drive_number ;
BYTE reserved1 ;
BYTE sig ;
INT id ;
BYTE volume_label[11] ;
BYTE fat_type[8] ;
BYTE boot_code[420] ;
BYTE boot_sig[2] ;
} ;
/*
* directory entry structure
*/
struct dir_entry
{
BYTE filename[8] ;
BYTE file_ext[3] ;
BYTE attrs ;
BYTE reserved ;
BYTE creat_time_fine ;
SHORT creat_time_coarse ;
SHORT creat_date ;
SHORT last_access_date ;
SHORT high_2_cluster_number ;
SHORT last_modified_time ;
SHORT last_modified_date ;
SHORT low_2_cluster_number ;
INT filesize ;
} ;
/*
* digest structure - extract interesting info and keep here
* only the really essential stuff is kept here - everything
* else is either used only rarely, or set at initialization time
* and then pretty much ignored.
*
* everything could be done without this, but having it makes things
* a bit easier
*
* my next step (if I continued this code) would be to add an endianness-fixed
* single copy of the fat itself as an array and a write-back function
* to copy it to both locations in the data structure
*/
struct fat_digest
{
unsigned char *fat_whole ;
struct fat_bs *fat_bs ;
unsigned char *fat_fat1 ;
unsigned char *fat_fat2 ;
unsigned char *fat_data ;
int bytes_per_sector ;
int bytes_per_cluster ;
} ;
/*
* convenience accessor/setter macros.
* none of the setter macros are used (or even tested) but were
* generated automatically
*/
#define get_bs_jmp(bs,buf) (getbytes(bs->jmp, buf,3))
#define set_bs_jmp(bs,val) (setbytes(val, bs->jmp,3))
#define get_bs_oem(bs,buf) (getbytes(bs->oem, buf,8))
#define set_bs_oem(bs,val) (setbytes(val, bs->oem,8))
#define get_bs_bytes_per_sector(bs) (SHORT2int(bs->bytes_per_sector))
#define set_bs_bytes_per_sector(bs,val) (int2SHORT(val, &(bs->bytes_per_sector)))
#define get_bs_sectors_per_cluster(bs) (BYTE2int(bs->sectors_per_cluster))
#define set_bs_sectors_per_cluster(bs,val) (int2BYTE(val, &(bs->sectors_per_cluster)))
#define get_bs_reserved_sectors(bs) (SHORT2int(bs->reserved_sectors))
#define set_bs_reserved_sectors(bs,val) (int2SHORT(val, &(bs->reserved_sectors)))
#define get_bs_fat_count(bs) (BYTE2int(bs->fat_count))
#define set_bs_fat_count(bs,val) (int2BYTE(val, &(bs->fat_count)))
#define get_bs_max_root_dirs(bs) (SHORT2int(bs->max_root_dirs))
#define set_bs_max_root_dirs(bs,val) (int2SHORT(val, &(bs->max_root_dirs)))
#define get_bs_total_sectors_1(bs) (SHORT2int(bs->total_sectors_1))
#define set_bs_total_sectors_1(bs,val) (int2SHORT(val, &(bs->total_sectors_1)))
#define get_bs_media_descriptor(bs) (BYTE2int(bs->media_descriptor))
#define set_bs_media_descriptor(bs,val) (int2BYTE(val, &(bs->media_descriptor)))
#define get_bs_sectors_per_fat_12_16(bs) (SHORT2int(bs->sectors_per_fat_12_16))
#define set_bs_sectors_per_fat_12_16(bs,val) (int2SHORT(val, &(bs->sectors_per_fat_12_16)))
#define get_bs_sectors_per_track(bs) (SHORT2int(bs->sectors_per_track))
#define set_bs_sectors_per_track(bs,val) (int2SHORT(val, &(bs->sectors_per_track)))
#define get_bs_head_count(bs) (SHORT2int(bs->head_count))
#define set_bs_head_count(bs,val) (int2SHORT(val, &(bs->head_count)))
#define get_bs_hidden_sectors(bs) (INT2int(bs->hidden_sectors))
#define set_bs_hidden_sectors(bs,val) (int2INT(val, &(bs->hidden_sectors)))
#define get_bs_total_sectors_2(bs) (INT2int(bs->total_sectors_2))
#define set_bs_total_sectors_2(bs,val) (int2INT(val, &(bs->total_sectors_2)))
#define get_bs_sectors_per_fat(bs) (INT2int(bs->sectors_per_fat))
#define set_bs_sectors_per_fat(bs,val) (int2INT(val, &(bs->sectors_per_fat)))
#define get_bs_fat_flags(bs) (SHORT2int(bs->fat_flags))
#define set_bs_fat_flags(bs,val) (int2SHORT(val, &(bs->fat_flags)))
#define get_bs_version(bs) (SHORT2int(bs->version))
#define set_bs_version(bs,val) (int2SHORT(val, &(bs->version)))
#define get_bs_root_cluster_start(bs) (INT2int(bs->root_cluster_start))
#define set_bs_root_cluster_start(bs,val) (int2INT(val, &(bs->root_cluster_start)))
#define get_bs_fs_info_sector(bs) (SHORT2int(bs->fs_info_sector))
#define set_bs_fs_info_sector(bs,val) (int2SHORT(val, &(bs->fs_info_sector)))
#define get_bs_boot_sector_copy(bs) (SHORT2int(bs->boot_sector_copy))
#define set_bs_boot_sector_copy(bs,val) (int2SHORT(val, &(bs->boot_sector_copy)))
#define get_bs_reserved(bs,buf) (getbytes(bs->reserved,buf, 12))
#define set_bs_reserved(bs,val) (setbytes(val, bs->reserved,12))
#define get_bs_drive_number(bs) (BYTE2int(bs->drive_number))
#define set_bs_drive_number(bs,val) (int2BYTE(val, &(bs->drive_number)))
#define get_bs_reserved1(bs) (BYTE2int(bs->reserved1))
#define set_bs_reserved1(bs,val) (int2BYTE(val, &(bs->reserved1)))
#define get_bs_id(bs) (INT2int(bs->id))
#define set_bs_id(bs,val) (int2INT(val, &(bs->id)))
#define get_bs_sig(bs) (BYTE2int(bs->id))
#define set_bs_sig(bs,val) (int2BYTE(val, &(bs->id)))
#define get_bs_volume_label(bs,buf) (getbytes(bs->volume_label,buf, 11))
#define set_bs_volume_label(bs,val) (setbytes(val, bs->volume_label,11))
#define get_bs_fat_type(bs,buf) (getbytes(bs->fat_type, buf,8))
#define set_bs_fat_type(bs,val) (setbytes(val, bs->fat_type8))
#define get_bs_boot_code(bs,buf) (getbytes(bs->boot_code, buf, 420))
#define set_bs_boot_code(bs,val) (setbytes(val, bs->boot_code,420))
#define get_bs_boot_sig(bs,buf) (getbytes(bs->boot_sig,buf,2))
#define set_bs_boot_sig(bs,val) (setbytes(val, bs->boot_sig,2))
/*
* these two bytes functions could almost (modulo the trailing 0 byte
* in getbytes) be just memcpy's
* this way is just a tad more consistent
*
* these are here precisely to handle the trailing 0 byte
*/
void getbytes(unsigned char *, unsigned char *, int) ;
void setbytes(unsigned char *, unsigned char *, int) ;
/*
* more prototypes
*/
void print_fat_bs(struct fat_bs *bs);
unsigned char *get_cluster_start_at_fat_number(struct fat_digest *, int ) ;
void print_fat(struct fat_digest *f) ;
void print_root_dir(struct fat_digest *f) ;
void print_dir(struct fat_digest *, int) ;
struct fat_digest *gen_fat_digest(unsigned char *f) ;
int get_cluster_size(struct fat_bs *) ;
int get_fat_start(struct fat_bs *bs) ;
int get_fat_size(struct fat_bs *bs) ;
int get_data_area_offset(struct fat_bs *bs) ;
int get_next_cluster_at_fat(struct fat_digest *f, int fatno) ;
unsigned char *get_cluster_start_at_fat_number(struct fat_digest *f, int fatno) ;
int
main(int argc, char **argv)
{
int fd = open("sample.fat", O_RDONLY) ;
struct stat statbuf ;
struct fat_digest *fat ;
unsigned char *f ;
fstat(fd, &statbuf) ;
f = mmap(0, statbuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0) ;
if ((int)f == -1)
{
perror("failed to mmap\n") ;
exit(1) ;
}
fat = gen_fat_digest(f) ;
print_fat_bs(fat->fat_bs) ;
print_fat(fat) ;
print_root_dir(fat) ;
exit(0) ;
}
/*
* initialize the fat_digest
*/
struct fat_digest *gen_fat_digest(unsigned char *f)
{
struct fat_digest *fd = malloc(sizeof (struct fat_digest)) ;
fd -> fat_whole = f ;
fd -> fat_bs = (struct fat_bs *) f ;
fd -> bytes_per_sector = get_bs_bytes_per_sector(fd->fat_bs) ;
fd -> bytes_per_cluster = get_cluster_size(fd->fat_bs) ;
fd -> fat_fat1 = f + get_fat_start(fd->fat_bs) ;
fd -> fat_fat2 = fd -> fat_fat1 + get_fat_size(fd -> fat_bs) ;
fd -> fat_data = fd -> fat_fat1 + 2*get_fat_size(fd -> fat_bs) ;
return fd ;
}
void print_fat_bs(struct fat_bs *bs)
{
unsigned char buf[500] ;
printf("printing fat_bs at %08x\n", (int) bs) ;
get_bs_jmp(bs, buf);
printf(" jmp -> %s\n", buf);
printf(" jmp(hex bytes) -> %02x %02x %02x\n",
bs->jmp[0], bs->jmp[1], bs->jmp[2]) ;
get_bs_oem(bs, buf);
printf(" oem -> %s\n", buf);
printf(" bytes_per_sector-> %d\n", get_bs_bytes_per_sector(bs));
printf(" sectors_per_cluster-> %d\n", get_bs_sectors_per_cluster(bs));
printf(" reserved_sectors-> %d\n", get_bs_reserved_sectors(bs));
printf(" fat_count-> %d\n", get_bs_fat_count(bs));
printf(" max_root_dirs-> %d\n", get_bs_max_root_dirs(bs));
printf(" total_sectors_1-> %d\n", get_bs_total_sectors_1(bs));
printf(" media_descriptor-> %d\n", get_bs_media_descriptor(bs));
printf(" sectors_per_fat_12_16-> %d\n", get_bs_sectors_per_fat_12_16(bs));
printf(" sectors_per_track-> %d\n", get_bs_sectors_per_track(bs));
printf(" head_count-> %d\n", get_bs_head_count(bs));
printf(" hidden_sectors-> %d\n", get_bs_hidden_sectors(bs));
printf(" total_sectors_2-> %d\n", get_bs_total_sectors_2(bs));
printf(" sectors_per_fat-> %d\n", get_bs_sectors_per_fat(bs));
printf(" fat_flags-> %d\n", get_bs_fat_flags(bs));
printf(" version-> %d\n", get_bs_version(bs));
printf(" root_cluster_start-> %d\n", get_bs_root_cluster_start(bs));
printf(" fs_info_sector-> %d\n", get_bs_fs_info_sector(bs));
printf(" boot_sector_copy-> %d\n", get_bs_boot_sector_copy(bs));
get_bs_reserved(bs, buf);
printf(" reserved -> %s\n", buf);
printf(" drive_number-> %d\n", get_bs_drive_number(bs));
printf(" reserved1-> %d\n", (int) get_bs_reserved1(bs));
printf(" sig-> %d\n", (int) get_bs_sig(bs));
printf(" id-> %d\n", get_bs_id(bs));
get_bs_volume_label(bs, buf) ;
printf(" volume_label -> %s\n", buf);
get_bs_fat_type(bs, buf);
printf(" fat_type -> %s\n", buf);
#if 0
get_bs_boot_code(bs, buf);
printf(" boot_code -> %s\n", buf);
#endif
get_bs_boot_sig(bs, buf);
printf(" boot_sig -> %02x%02x\n", buf[0], MASK(buf[1]));
}
void getbytes(unsigned char *from, unsigned char *to, int count)
{
memcpy(to,from, count) ;
to[count] = '\0' ;
}
void setbytes(unsigned char *from, unsigned char *to, int count)
{
memcpy(to,from, count) ;
}
void print_dir_entry(struct dir_entry *d)
{
unsigned char buf[100] ;
short date ;
short low_cluster_start, high_cluster_start ;
int filesize ;
printf("directory entry at %08x\n", (int)d) ;
getbytes(d->filename, buf, 8) ;
printf("first byte=%08x\n", buf[0]) ;
if (buf[0] == '\0')
{
printf("empty\n") ;
return ;
}
else
{
printf("filename = [[%s.", buf) ;
getbytes(d->file_ext, buf, 3) ;
printf("%s]]\n", buf) ;
printf("attrs=%d%d%d%d%d%d%d%d\n", bit(d->attrs,0),
bit(d->attrs,1),
bit(d->attrs,2),
bit(d->attrs,3),
bit(d->attrs,4),
bit(d->attrs,5),
bit(d->attrs,6),
bit(d->attrs,7)) ;
printf("create time (ms) = %d\n", d->creat_time_fine) ;
date = SHORT2int(d->creat_time_coarse) ;
printf("create time= %d:%d:%d (%04x)\n",
hours(date), minutes(date), seconds(date), date);
date = SHORT2int(d->creat_date) ;
printf("create date= %d:%d:%d\n",
year(date), month(date), day(date));
date = SHORT2int(d->last_access_date) ;
printf("access date= %d:%d:%d\n",
year(date), month(date), day(date));
high_cluster_start = SHORT2int(d->high_2_cluster_number) ;
date = SHORT2int(d->last_modified_time) ;
printf("mod time= %d:%d:%d\n",
hours(date), minutes(date), seconds(date));
date = SHORT2int(d->last_modified_date) ;
printf(" mod date= %d:%d:%d\n",
year(date), month(date), day(date));
low_cluster_start = SHORT2int(d->low_2_cluster_number) ;
printf("low cluster start=%d high_cluster start=%d cluster start=%d\n",
low_cluster_start, high_cluster_start,
low_cluster_start | (high_cluster_start << 16));
filesize = INT2int(d->filesize) ;
printf("filesize = %d\n", filesize) ;
}
}
void print_fat (struct fat_digest *f)
{
struct fat_bs *fbs = f -> fat_bs ;
int sector_size = f -> bytes_per_sector ;
unsigned char *fat1 = f -> fat_fat1 ;
int fat_entry_count = get_bs_sectors_per_fat(fbs)*sector_size/4 ;
int i ;
int fe ;
unsigned char *fp ;
printf("fbs at %08x fat at %08x fat offset=%08x\n",
(int) fbs, (int)fat1, (int)get_fat_start(fbs)) ;
for (i = 0 ; i < fat_entry_count ; i+=4)
{
fp = fat1 + i ;
fe = INT2int(fp) ;
if (fe != 0)
{
printf("fat entry at offset (fat) %08x (start) %08x => %08x\n",
i, (int)fp-(int)fbs, (int) fe) ;
}
}
}
void print_root_dir(struct fat_digest *f)
{
print_dir(f, get_bs_root_cluster_start(f -> fat_bs)) ;
}
/*
* doesn't handle directories longer than one cluster
*/
void print_dir(struct fat_digest *f, int cluster)
{
struct fat_bs *fbs = f -> fat_bs ;
int i ;
struct dir_entry *rds = (struct dir_entry *) get_cluster_start_at_fat_number(f, cluster);
printf("dir at %08x (offset from start=%08x)\n", (int)rds, (int)rds - (int) f) ;
printf("cluster start = %d\n", get_bs_root_cluster_start(fbs)) ;
printf("%d entries??\n", f -> bytes_per_cluster/sizeof(struct dir_entry)) ;
printf("cluster size=%d dir_entry size = %d\n",
f->bytes_per_cluster,
sizeof(struct dir_entry)) ;
for (i = 0 ; i < f->bytes_per_cluster / sizeof(struct dir_entry) ; i++)
{
if ((rds+i)->filename[0] == 0) break ;
print_dir_entry(rds+i) ;
}
/*
* untested
*/
i = get_next_cluster_at_fat(f, cluster) ;
if (i > 0 && i < 0xfffffff8 )
{
print_dir(f, i);
}
}
/*
* convenience functions. Could well be macros (and the macros at
* the top could mostly be functions). Making them functions alleviates
* macro weirdness
*/
int get_cluster_size(struct fat_bs *bs)
{
return get_bs_bytes_per_sector(bs)*get_bs_sectors_per_cluster(bs) ;
}
int get_fat_start(struct fat_bs *bs)
{
return get_bs_bytes_per_sector(bs)*get_bs_reserved_sectors(bs);
}
int get_fat_size(struct fat_bs *bs)
{
return get_bs_bytes_per_sector(bs)*get_bs_sectors_per_fat(bs);
}
int get_data_area_offset(struct fat_bs *bs)
{
return get_fat_start(bs) + get_fat_size(bs) * get_bs_fat_count(bs) ;
}
int get_next_cluster_at_fat(struct fat_digest *f, int fatno)
{
unsigned char *entry_addr = f -> fat_fat1 + 4*(fatno - 2) ;
return INT2int(entry_addr) ;
}
unsigned char *get_cluster_start_at_fat_number(struct fat_digest *f, int fatno)
{
return (f -> fat_data + (fatno - 2)*f->bytes_per_cluster) ;
}
Click here to get the file