Skip to content

Instantly share code, notes, and snippets.

@jeremysinger
Created March 7, 2017 12:46
Show Gist options
  • Save jeremysinger/161a77f2e5932fb706f5c8b664cf60a0 to your computer and use it in GitHub Desktop.
Save jeremysinger/161a77f2e5932fb706f5c8b664cf60a0 to your computer and use it in GitHub Desktop.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BOOT_SECTOR_OFFSET 0
typedef struct {
unsigned char jmp[3];
char oem[8];
unsigned short sector_size;
unsigned char sectors_per_cluster;
unsigned short reserved_sectors;
unsigned char number_of_fats;
unsigned short root_dir_entries;
unsigned short total_sectors_short; // if zero, later field is used
unsigned char media_descriptor;
unsigned short fat_size_sectors;
unsigned short sectors_per_track;
unsigned short number_of_heads;
unsigned int hidden_sectors;
unsigned int total_sectors_long;
unsigned char drive_number;
unsigned char current_head;
unsigned char boot_signature;
unsigned int volume_id;
char volume_label[11];
char fs_type[8];
char boot_code[448];
unsigned short boot_sector_signature;
} __attribute((packed)) Fat16BootSector;
typedef struct {
unsigned char filename[8];
unsigned char ext[3];
unsigned char attributes;
unsigned char reserved[10];
unsigned short modify_time;
unsigned short modify_date;
unsigned short starting_cluster;
unsigned int file_size;
} __attribute((packed)) Fat16Entry;
// Below is a file that fits in a single cluster
/* char *name = "limerick"; */
/* char *ext = "txt"; */
/* char *short_poem = "The Owl and the Pussy-cat went to sea \ */
/* In a beautiful pea green boat, \ */
/* They took some honey, and plenty of money, \ */
/* Wrapped up in a five pound note."; */
// Below is a file that spans two clusters
char *name = "sonnet18";
char *ext = "txt";
char *poem =
"Shall I compare thee to a summer's day? \
Thou art more lovely and more temperate: \
Rough winds do shake the darling buds of May, \
And summer's lease hath all too short a date: \
Sometime too hot the eye of heaven shines, \
And often is his gold complexion dimm'd; \
And every fair from fair sometime declines, \
By chance, or nature's changing course, untrimm'd: \
But thy eternal summer shall not fade, \
Nor lose possession of that fair thou ow'st; \
Nor shall Death brag thou wander'st in his shade, \
When in eternal lines to time thou grow'st: \
So long as men can breathe, or eyes can see, \
So long lives this, and this gives life to thee.";
void print_file_info(Fat16Entry *entry) {
switch(entry->filename[0]) {
case 0x00:
return; // unused entry
case 0xE5:
printf("Deleted file: [?%.7s.%.3s]\n", entry->filename+1, entry->ext);
return;
case 0x05:
printf("File starting with 0xE5: [%c%.7s.%.3s]\n", 0xE5, entry->filename+1, entry->ext);
break;
case 0x2E:
printf("Directory: [%.8s.%.3s]\n", entry->filename, entry->ext);
break;
default:
printf("File: [%.8s.%.3s]\n", entry->filename, entry->ext);
}
printf(" Modified: %04d-%02d-%02d %02d:%02d.%02d Start: [%04X] Size: %d\n",
1980 + (entry->modify_date >> 9), (entry->modify_date >> 5) & 0xF, entry->modify_date & 0x1F,
(entry->modify_time >> 11), (entry->modify_time >> 5) & 0x3F, entry->modify_time & 0x1F,
entry->starting_cluster, entry->file_size);
}
int main() {
FILE * in = fopen("example.img", "r+b"); // r+ means read and write
int i, n;
char fat_ptr[2];
Fat16BootSector bs;
Fat16Entry *entry;
printf("boot sector struct size %lu\n", sizeof(Fat16BootSector));
fseek(in, BOOT_SECTOR_OFFSET, SEEK_SET);
fread(&bs, sizeof(Fat16BootSector), 1, in);
printf("Now at 0x%lX, sector size %d, FAT size %d sectors, %d FATs\n\n",
ftell(in), bs.sector_size, bs.fat_size_sectors, bs.number_of_fats);
// Now let's add a multicluster file ...
// add a new entry as 1st file in root directory
// (1) first create FAT root dir entry
printf("creating root dir entry\n");
entry = (Fat16Entry*)calloc(1, sizeof(Fat16Entry));
memcpy(entry->filename, name, 8);
memcpy(entry->ext, ext, 3);
entry->attributes = '\0';
entry->starting_cluster = (unsigned short)2;
entry->file_size = strlen(poem);
// move to place in root dir and write entry
fseek(in, (bs.reserved_sectors + bs.fat_size_sectors * bs.number_of_fats) *
bs.sector_size, SEEK_SET);
fwrite(entry, sizeof(Fat16Entry), 1, in);
printf("written root dir entry\n");
// (2) then allocate file into consecutive empty clusters in the
// data region, starting at cluster 2
// write poem string here...
printf("writing to data cluster\n");
fseek(in, (1/*rootdir*/ + bs.reserved_sectors + bs.fat_size_sectors * bs.number_of_fats) *bs.sector_size, SEEK_SET);
fwrite(poem, sizeof(char), strlen(poem), in);
printf("written data\n");
// (3) then update FAT entries for this file
// Entry is for cluster 2, and consecutive clusters
fseek(in, sizeof(bs) + 2*2*sizeof(char), SEEK_SET); // seek past end of boot sector, into FAT
for (n = 2; n < 2 + (strlen(poem)/(bs.sector_size*bs.sectors_per_cluster)); n++) {
printf("updating FAT cluster entry %d\n", n);
fat_ptr[0] = (char)(n+1); // assume n<256
fat_ptr[1] = (char)0;
fwrite(fat_ptr, sizeof(char), 2, in);
printf("updated FAT cluster entry %d with value %d\n", n, n+1);
//fseek(in, n*2*sizeof(char), SEEK_CUR); // seek to correct FAT entry
// no need for subsequent fseek since fwrite bumps file pointer?
}
// now write -1 in final cluster entry in FAT
printf("updating FAT cluster entry %d\n", n);
fat_ptr[0] = (char)0xff;
fat_ptr[1] = (char)0xff;
fwrite(fat_ptr, sizeof(char), 2, in);
printf("updated FAT cluster entry %d with value 0xffff\n", n);
fclose(in);
return 0;
}
#!/usr/bin/python
# gen_fat_fs.py
# Jeremy Singer
# 26 Feb 2017
## simple python script to create an example FAT
## filesystem image, from scratch
image = 'example.img'
# create (or overwrite) the file and start writing bytes into it
f = open(image, 'wb')
# first three bytes of boot sector are 'magic value'
f.write( bytearray([0xeb, 0x3c, 0x90]) )
# next 8 bytes are manufacturer name, in ASCII
f.write( 'GLASGOW '.encode('ascii') )
# next 2 bytes are bytes per block - 512 is standard
# this is in little endian format - so 0x200 is 0x00, 0x02
# A block == A sector in FAT terminology
f.write( bytearray([0x00, 0x02]) )
# next byte, number of blocks per allocation unit - we say 1 for simplicity
# An allocation unit == A cluster in FAT terminology
f.write( bytearray([0x01]) )
# next two bytes, number of reserved blocks - we
# say 1 for boot sector only
f.write( bytearray([0x01, 0x00]) )
# next byte, number of File Allocation tables - can have multiple
# tables for redundancy - we'll stick with 1 for now
f.write( bytearray([0x01]) )
# next two bytes, number of root directory entries - including blanks
# let's say 16 files in root for now - which means the root directory
# will occupy a single sector
f.write( bytearray([0x10, 0x00]) )
# next two bytes, number of blocks in the entire disk - we want a 4 meg disk,
# so need 8192 0.5K blocks == 2^13 == 0x00 0x20
f.write( bytearray([0x00, 0x20]) )
# single byte media descriptor - magic value 0xf8??
f.write( bytearray([0xf8]) )
# next two bytes, number of blocks for FAT
# FAT needs two bytes per block, we have 8192 blocks on disk
# 512 bytes per block - i.e. can store FAT metadata for 256 blocks in
# a single block, so need 8192/256 blocks == 2^13/2^8 == 2^5 == 32
f.write( bytearray([0x20, 0x00]) )
# next 8 bytes are legacy values, can all be 0
f.write( bytearray([0,0,0,0,0,0,0,0]) )
# next 4 bytes are total number of blocks in entire disk - if it overflows
# previous 2 byte entry ... otherwise leave at 0
f.write( bytearray([0x00, 0x00, 0x00, 0x00]) )
# next 2 bytes are legacy values, can all be 0
f.write( bytearray([0x80,0]) )
# magic value 29 - for FAT16 extended signature
f.write( bytearray([0x29]) )
# next 4 bytes are volume serial number (unique id)
f.write( bytearray([0x41,0x42,0x43,0x44]) )
# next 11 bytes are volume label (name) - pad with trailing spaces
f.write( "TEST_DISK ".encode('ascii'))
# next 8 bytes are file system identifier - pad with trailing spaces
f.write( "FAT16 ".encode('ascii'))
for i in range(0,0x1c0):
f.write( bytearray([0]) )
# end of boot sector magic marker
f.write( bytearray([0x55, 0xaa]) )
###########
# next lot of sectors is file allocation table
# each entry has 2 bytes, we need 8192 entries (== 32 blocks)
# first two entries are magic value (media descriptor) then three 0xff values
f.write( bytearray([0xf8, 0xff]) )
f.write( bytearray([0xff, 0xff]) )
# subsequent entries are 0
for i in range(2, 8192):
f.write( bytearray([0x00, 0x00]) )
############
# next sector is root directory
###########
# each file entry occupies 32 bytes - we just no entries for now - all zeros.
for i in range(0, 16):
for j in range(0,32):
f.write( bytearray([0]) )
##### 8192 blank sectors (after root)
for i in range(0,8192):
f.write( bytearray([0x00] * 512))
f.close()
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BOOT_SECTOR_OFFSET 0
typedef struct {
unsigned char jmp[3];
char oem[8];
unsigned short sector_size;
unsigned char sectors_per_cluster;
unsigned short reserved_sectors;
unsigned char number_of_fats;
unsigned short root_dir_entries;
unsigned short total_sectors_short; // if zero, later field is used
unsigned char media_descriptor;
unsigned short fat_size_sectors;
unsigned short sectors_per_track;
unsigned short number_of_heads;
unsigned int hidden_sectors;
unsigned int total_sectors_long;
unsigned char drive_number;
unsigned char current_head;
unsigned char boot_signature;
unsigned int volume_id;
char volume_label[11];
char fs_type[8];
char boot_code[448];
unsigned short boot_sector_signature;
} __attribute((packed)) Fat16BootSector;
typedef struct {
unsigned char filename[8];
unsigned char ext[3];
unsigned char attributes;
unsigned char reserved[10];
unsigned short modify_time;
unsigned short modify_date;
unsigned short starting_cluster;
unsigned int file_size;
} __attribute((packed)) Fat16Entry;
// Below is a file that fits in a single cluster
/* char *name = "limerick"; */
/* char *ext = "txt"; */
/* char *short_poem = "The Owl and the Pussy-cat went to sea \ */
/* In a beautiful pea green boat, \ */
/* They took some honey, and plenty of money, \ */
/* Wrapped up in a five pound note."; */
// Below is a file that spans two clusters
char *name = "sonnet18";
char *ext = "txt";
char *poem =
"Shall I compare thee to a summer's day? \
Thou art more lovely and more temperate: \
Rough winds do shake the darling buds of May, \
And summer's lease hath all too short a date: \
Sometime too hot the eye of heaven shines, \
And often is his gold complexion dimm'd; \
And every fair from fair sometime declines, \
By chance, or nature's changing course, untrimm'd: \
But thy eternal summer shall not fade, \
Nor lose possession of that fair thou ow'st; \
Nor shall Death brag thou wander'st in his shade, \
When in eternal lines to time thou grow'st: \
So long as men can breathe, or eyes can see, \
So long lives this, and this gives life to thee.";
void print_file_info(Fat16Entry *entry) {
switch(entry->filename[0]) {
case 0x00:
return; // unused entry
case 0xE5:
printf("Deleted file: [?%.7s.%.3s]\n", entry->filename+1, entry->ext);
return;
case 0x05:
printf("File starting with 0xE5: [%c%.7s.%.3s]\n", 0xE5, entry->filename+1, entry->ext);
break;
case 0x2E:
printf("Directory: [%.8s.%.3s]\n", entry->filename, entry->ext);
break;
default:
printf("File: [%.8s.%.3s]\n", entry->filename, entry->ext);
}
printf(" Modified: %04d-%02d-%02d %02d:%02d.%02d Start: [%04X] Size: %d\n",
1980 + (entry->modify_date >> 9), (entry->modify_date >> 5) & 0xF, entry->modify_date & 0x1F,
(entry->modify_time >> 11), (entry->modify_time >> 5) & 0x3F, entry->modify_time & 0x1F,
entry->starting_cluster, entry->file_size);
}
int main() {
FILE * in = fopen("example.img", "r+b"); // r+ means read and write
int i, n;
char fat_ptr[2];
Fat16BootSector bs;
Fat16Entry *entry;
printf("boot sector struct size %lu\n", sizeof(Fat16BootSector));
fseek(in, BOOT_SECTOR_OFFSET, SEEK_SET);
fread(&bs, sizeof(Fat16BootSector), 1, in);
printf("Now at 0x%lX, sector size %d, FAT size %d sectors, %d FATs\n\n",
ftell(in), bs.sector_size, bs.fat_size_sectors, bs.number_of_fats);
// Now let's add a multicluster file ...
// add a new entry as 1st file in root directory
// (1) first create FAT root dir entry
printf("creating root dir entry\n");
entry = (Fat16Entry*)calloc(1, sizeof(Fat16Entry));
memcpy(entry->filename, name, 8);
memcpy(entry->ext, ext, 3);
entry->attributes = '\0';
entry->starting_cluster = (unsigned short)2;
entry->file_size = strlen(poem);
// move to place in root dir and write entry
fseek(in, (bs.reserved_sectors + bs.fat_size_sectors * bs.number_of_fats) *
bs.sector_size, SEEK_SET);
fwrite(entry, sizeof(Fat16Entry), 1, in);
printf("written root dir entry\n");
// (2) then allocate file into consecutive empty clusters in the
// data region, starting at cluster 2
// write poem string here...
printf("writing to data cluster\n");
fseek(in, (1/*rootdir*/ + bs.reserved_sectors + bs.fat_size_sectors * bs.number_of_fats) *bs.sector_size, SEEK_SET);
fwrite(poem, sizeof(char), strlen(poem), in);
printf("written data\n");
// (3) then update FAT entries for this file
// Entry is for cluster 2, and consecutive clusters
fseek(in, sizeof(bs) + 2*2*sizeof(char), SEEK_SET); // seek past end of boot sector, into FAT
for (n = 2; n < 2 + (strlen(poem)/(bs.sector_size*bs.sectors_per_cluster)); n++) {
printf("updating FAT cluster entry %d\n", n);
fat_ptr[0] = (char)(n+1); // assume n<256
fat_ptr[1] = (char)0;
fwrite(fat_ptr, sizeof(char), 2, in);
printf("updated FAT cluster entry %d with value %d\n", n, n+1);
//fseek(in, n*2*sizeof(char), SEEK_CUR); // seek to correct FAT entry
// no need for subsequent fseek since fwrite bumps file pointer?
}
// now write -1 in final cluster entry in FAT
printf("updating FAT cluster entry %d\n", n);
fat_ptr[0] = (char)0xff;
fat_ptr[1] = (char)0xff;
fwrite(fat_ptr, sizeof(char), 2, in);
printf("updated FAT cluster entry %d with value 0xffff\n", n);
fclose(in);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment