diff --git a/include/fat12.h b/include/fat12.h new file mode 100644 index 0000000..76c1548 --- /dev/null +++ b/include/fat12.h @@ -0,0 +1,14 @@ +#ifndef PANG_O_LIN_FAT12_H__ +#define PANG_O_LIN_FAT12_H__ + +#include + +void fat12_write_u16(void *ptr, uint16_t val); +uint16_t fat12_read_u16(void *ptr); +void fat12_set_cluster(void *fat, uint32_t cluster, uint32_t value); +uint32_t fat12_get_cluster(void *fat, uint32_t cluster); + +int fat12_defrag(int fd); +int fat12_mkfs(int fd, uint32_t bytes); + +#endif /* PANG_O_LIN_FAT12_H__ */ \ No newline at end of file diff --git a/include/io.h b/include/io.h new file mode 100644 index 0000000..9d08cc3 --- /dev/null +++ b/include/io.h @@ -0,0 +1,14 @@ +#ifndef PANG_O_LIN_IO_H__ +#define PANG_O_LIN_IO_H__ + +#include + +#ifdef unix +#include +#endif + +ssize_t pang_write(int fd, const void *buf, size_t count); +ssize_t pang_read(int fd, void *buf, size_t count); +off_t pang_seek(int fd, off_t offset, int whence); + +#endif /* PANG_O_LIN_IO_H__ */ \ No newline at end of file diff --git a/src/defrag.c b/src/defrag.c new file mode 100644 index 0000000..d9ddc34 --- /dev/null +++ b/src/defrag.c @@ -0,0 +1,45 @@ + +#include "io.h" + +#define try(x) do { int i = (x); if (i == -1) { return -1 } } while (0) + +int fat12_defrag(int fd) { + uint8_t buf[512]; + + // Read in MBR and figure out where the FAT begins and ends + try(pang_seek(fd, 0, SEEK_SET)); + + // Actually read in the MBR + try(pang_read(fd, buf, sizeof(buf))); + + // We only operate on 512-byte sector sizes + if (fat12_read_u16(buf + 0x0b) != 512) { + return -2; + } + + // We require the cluster size to be 4096 (8 * 512) + if (buf[0x0d] != 8) { + return -2; + } + + // To ensure the erase block is correct, require 4096-bytes + // of reserved data + if (fat12_read_u16(buf + 0x0e) != 4096) { + return -2; + } + + // Also, we only support 1 FAT + if (buf[0x10] != 1) { + return -2; + } + + // 32 root entries keeps it padded correctly. + if (fat12_read_u16(mbr + 0x11, 32) != 32) { + return -2; + } + + // Total number of 512-byte sectors + fat12_write_u16(mbr + 0x13, bytes / sector_size); + + return 0; +} \ No newline at end of file diff --git a/src/fat12.c b/src/fat12.c new file mode 100644 index 0000000..98b0de8 --- /dev/null +++ b/src/fat12.c @@ -0,0 +1,41 @@ +#include "fat12.h" + +void fat12_write_u16(void *ptr, uint16_t val) { + ((uint8_t *)ptr)[0] = (val >> 0) & 0xff; + ((uint8_t *)ptr)[1] = (val >> 8) & 0xff; +} + +uint16_t fat12_read_u16(void *ptr) { + uint16_t val = 0; + val |= (((uint8_t *)ptr)[0] << 0) & 0x00ff; + val |= (((uint8_t *)ptr)[1] << 8) & 0xff00; + return val; +} + +void fat12_set_cluster(void *fat, uint32_t cluster, uint32_t value) { + uint32_t offset = (cluster * 3) / 2; + if (cluster&1) { + ((uint8_t *)fat)[offset] = (((uint8_t *)fat)[offset] & 0x0f) | ((value << 4) & 0xf0); + ((uint8_t *)fat)[offset + 1] = value >> 4; + } + else { + ((uint8_t *)fat)[offset] = value; + ((uint8_t *)fat)[offset + 1] = (((uint8_t *)fat)[offset] & 0xf0) | ((value >> 8) & 0x0f); + } +} + +uint32_t fat12_get_cluster(void *fat, uint32_t cluster) { + uint32_t offset = (cluster * 3) / 2; + uint32_t value = 0; + + if (cluster&1) { + value |= (((uint8_t *)fat)[offset] >> 4) & 0x00f; + value |= (((uint8_t *)fat)[offset + 1] << 4) & 0xff0; + } + else { + value |= ((uint8_t *)fat)[offset]; + value |= (((uint8_t *)fat)[offset + 1] << 8) & 0xf00; + } + + return value; +} \ No newline at end of file diff --git a/src/io.c b/src/io.c new file mode 100644 index 0000000..223377b --- /dev/null +++ b/src/io.c @@ -0,0 +1,15 @@ +#include "io.h" + +#ifdef unix +ssize_t pang_write(int fd, const void *buf, size_t count) { + return write(fd, buf, count); +} + +ssize_t pang_read(int fd, void *buf, size_t count) { + return read(fd, buf, count); +} + +off_t pang_seek(int fd, off_t offset, int whence) { + return lseek(fd, offset, whence); +} +#endif \ No newline at end of file diff --git a/src/mkfat.c b/src/mkfat.c new file mode 100644 index 0000000..1c07a33 --- /dev/null +++ b/src/mkfat.c @@ -0,0 +1,127 @@ +#include +#include +#include + +#include "io.h" + +struct directory_entry { + uint8_t file_name[8]; + uint8_t extension[3]; + uint8_t file_attributes; + uint8_t reserved1; + uint8_t ctime_ms; + uint8_t ctime_hms[2]; + uint8_t ctime_date[2]; + uint8_t atime_date[2]; + uint8_t ea_index[2]; + uint8_t mtime_hms[2]; + uint8_t mtime_date[2]; + uint8_t first_cluster[2]; + uint8_t file_size[4]; +}; + +int fat12_mkfs(int fd, uint32_t bytes) { + uint8_t mbr[512]; + const uint16_t sector_size = 512; + const uint8_t cluster_size = 8; // 4 kiB cluster sizes match flash erase block size + + // XXX What happens if this is an even multiple of sectors? + uint32_t fat_size = 1 + ((((bytes * 3) / sector_size) / cluster_size) / 2); + + memset(mbr, 0, sizeof(mbr)); + + // jmp instruction + mbr[0] = 0xeb; + mbr[1] = 0x3c; + mbr[2] = 0x90; + + // OEM name + memcpy(mbr+3, "Fomu MBR", 8); + + // Bytes per sector (512) + fat12_write_u16(mbr + 0x0b, sector_size); + + // Sectors per cluster (8), so cluster is 512 * 8 + mbr[0x0d] = cluster_size; + + // Reserved sector count (8 sectors, to pad things to 4096-byte boundaries) + fat12_write_u16(mbr + 0x0e, cluster_size); + + // Number of FATs (1) + mbr[0x10] = 1; + + // Maximum number of root directory entries (16) + fat12_write_u16(mbr + 0x11, 32); + + // Total number of 512-byte sectors + fat12_write_u16(mbr + 0x13, bytes / sector_size); + + // Type of media (0xf8 == fixed disk) + mbr[0x15] = 0xf8; + + // Sectors per FAT + fat12_write_u16(mbr + 0x16, fat_size); + + // Sectors per track + fat12_write_u16(mbr + 0x18, 32); + + // Number of heads + fat12_write_u16(mbr + 0x1a, 64); + + // Hidden sectors + mbr[0x1c] = 0x00; + mbr[0x1d] = 0x00; + mbr[0x1e] = 0x00; + mbr[0x1f] = 0x00; + + // Total number of sectors (unused, since we put it in 0x13) + mbr[0x20] = 0; + mbr[0x21] = 0; + mbr[0x22] = 0; + mbr[0x23] = 0; + + // BIOS drive + mbr[0x24] = 0x80; + + // Unused + mbr[0x25] = 0; + + // Boot signature + mbr[0x26] = 0x29; + + // Serial number + mbr[0x27] = 0xf0; + mbr[0x28] = 0xb0; + mbr[0x29] = 0x00; + mbr[0x2a] = 0x07; + + // Volume Name + memcpy(mbr + 0x2b, "Fomu ", 11); + + // Filesystem + memcpy(mbr + 0x36, "FAT12 ", 8); + + // Signature + mbr[0xfe] = 0x55; + mbr[0xff] = 0xaa; + + // Write MBR to disk + if (pang_write(fd, mbr, sizeof(mbr)) != sizeof(mbr)) { + return -1; + } + + memset(mbr, 0, sizeof(mbr)); + + // The first two clusters in the FAT are reserved, and must be 0xff + fat12_set_cluster(mbr, 0, 0xff8); // 0xff8 instead of 0xfff indicates "Hard Disk" + fat12_set_cluster(mbr, 1, 0xfff); // 0xfff is reserved value + + // Root directory is only one cluster + fat12_set_cluster(mbr, 2, 0xfff); + + if (pang_write(fd, mbr, sizeof(mbr)) != sizeof(mbr)) { + return -1; + } + + return 0; +} \ No newline at end of file diff --git a/tests/test_get_set_cluster.c b/tests/test_get_set_cluster.c new file mode 100644 index 0000000..1a6ecde --- /dev/null +++ b/tests/test_get_set_cluster.c @@ -0,0 +1,41 @@ +#include +#include + +#include "fat12.h" + +static uint8_t sample_fat12[] = { + 0xF0, 0xFF, 0xFF, 0x03, 0x40, 0x00, 0x05, 0x60, + 0x00, 0x07, 0x80, 0x00, 0xFF, 0xAF, 0x00, 0x14, + 0xC0, 0x00, 0x0D, 0xE0, 0x00, 0x0F, 0x00, 0x01, + 0x11, 0xF0, 0xFF, 0x00, 0xF0, 0xFF, 0x15, 0x60, + 0x01, 0x19, 0x70, 0xFF, 0xF7, 0xAF, 0x01, 0xFF, + 0x0F, 0x00, 0x00, 0x70, 0xFF, 0x00, 0x00, 0x00, +}; + +static uint16_t fat12_compare[] = { + 0xff0, 0xfff, 0x003, 0x004, 0x005, 0x006, 0x007, 0x008, + 0xfff, 0x00a, 0x014, 0x00c, 0x00d, 0x00e, 0x00f, 0x010, + 0x011, 0xfff, 0x000, 0xfff, 0x015, 0x016, 0x019, 0xff7, + 0xff7, 0x01a, 0xfff, 0x000, 0x000, 0xff7, 0x000, 0x000, +}; + +int main(int argc, char **argv) { + int i; + uint8_t generated_fat12[sizeof(sample_fat12)]; + + for (i = 0; i < sizeof(fat12_compare) / sizeof(*fat12_compare); i++) { + uint32_t cluster = fat12_get_cluster(sample_fat12, i); + fprintf(stderr, "GET: %2d: %03x (%03x) %s\n", i, cluster, fat12_compare[i], cluster == fat12_compare[i] ? "Ok" : "Error"); + } + + for (i = 0; i < sizeof(fat12_compare) / sizeof(*fat12_compare); i++) { + fat12_set_cluster(generated_fat12, i, fat12_compare[i]); + } + + for (i = 0; i < sizeof(sample_fat12) / sizeof(*sample_fat12); i++) { + fprintf(stderr, "PUT: %2d: %02x (%02x) %s\n", + i, + generated_fat12[i], sample_fat12[i], + generated_fat12[i] == sample_fat12[i] ? "Ok" : "Error"); + } +} \ No newline at end of file