diff --git a/CMakeLists.txt b/CMakeLists.txt index 64d8ef7..b6e1ceb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,4 +8,6 @@ add_library(embpart embpart.c mbr/mbr.h ../test/main.c fs/fat32/fat32.c - fs/fat32/fat32.h) \ No newline at end of file + fs/fat32/fat32.h + MassStorage.c + MassStorage.h) \ No newline at end of file diff --git a/MassStorage.c b/MassStorage.c new file mode 100644 index 0000000..5d29832 --- /dev/null +++ b/MassStorage.c @@ -0,0 +1,5 @@ +// +// Created by epagris on 2023.10.01.. +// + +#include "MassStorage.h" diff --git a/MassStorage.h b/MassStorage.h new file mode 100644 index 0000000..947c05a --- /dev/null +++ b/MassStorage.h @@ -0,0 +1,16 @@ +#ifndef EMBPART_MASSSTORAGE_H +#define EMBPART_MASSSTORAGE_H + +#include + +typedef struct _MassStorage{ + uint32_t sectorSize; // sector size + void * data; // some arbitrary data + int (*read_sector)(const struct _MassStorage * mstg, uint32_t idx, uint8_t * data); // read specific sector + int (*write_sector)(const struct _MassStorage * mstg, uint32_t idx, const uint8_t * data); // read specific sector +} MassStorage; + +#define MSTG_READ(mstg, idx, buffer) (mstg)->read_sector((mstg), (idx), (buffer)) +#define MSTG_WRITE(mstg, idx, buffer) (mstg)->write_sector((mstg), (idx), (buffer)) + +#endif //EMBPART_MASSSTORAGE_H diff --git a/fs/fat32/fat32.c b/fs/fat32/fat32.c index 37cf962..bb9756d 100644 --- a/fs/fat32/fat32.c +++ b/fs/fat32/fat32.c @@ -3,12 +3,31 @@ // #include +#include #include "fat32.h" +#include "../../MassStorage.h" + +#define MIN(a,b) (((a) < (b)) ? (a) : (b)) + +#define SECTOR_SIZE (512) +static uint8_t buffer[2 * SECTOR_SIZE]; // TODO access to this variable should be protected! + +static inline void fat32_copy_second_to_first_buffer() { + memcpy(buffer, buffer + SECTOR_SIZE, SECTOR_SIZE); +} + +static inline uint32_t fat32_get_first_sector_of_cluster(const Fat32_CtrlBlock * ctrl, uint32_t cluster) { + return (cluster - 2) * ctrl->sectors_per_cluster + ctrl->data_s; +} + +int fat32_load(Fat32_CtrlBlock *ctrl, uint32_t bpb_s, const MassStorage * mstg) { + ctrl->mstg = mstg; -int fat32_load(Fat32_CtrlBlock *ctrl, const uint8_t *bpb, uint32_t bpb_s) { ctrl->bpb_s = bpb_s; - const Bpb_Fat32 * bpb_obj = (Bpb_Fat32 *)bpb; + // load Boot Parameter Block + mstg->read_sector(mstg, bpb_s, buffer); + const Bpb_Fat32 * bpb_obj = (Bpb_Fat32 *)buffer; ctrl->bytes_per_sector = bpb_obj->bytes_per_sector; ctrl->sectors_per_cluster = bpb_obj->sectors_per_cluster; @@ -19,7 +38,204 @@ int fat32_load(Fat32_CtrlBlock *ctrl, const uint8_t *bpb, uint32_t bpb_s) { ctrl->fat_s = ctrl->bpb_s + bpb_obj->n_reserved_sectors; // FATs are stored on the hidden area right after the reserved sectors ctrl->data_s = ctrl->fat_s + ctrl->fat_copies * ctrl->sectors_per_fat; // data region begins right after the FAT copies - ctrl->root_s = bpb_obj->root_first_cluster * ctrl->sectors_per_cluster + ctrl->data_s; // first sector of root directory is determined using the cluster number as an offset + ctrl->root_s = fat32_get_first_sector_of_cluster(ctrl, bpb_obj->root_first_cluster); // first sector of root directory is determined using the cluster number as an offset return 0; } + +#define FETCH_MSTG const MassStorage * mstg = ctrl->mstg +#define READ_MSTG_TO_FIRST_BUFFER(idx) MSTG_READ(mstg, (idx), buffer) +#define READ_MSTG_TO_SECOND_BUFFER(idx) MSTG_READ(mstg, (idx), buffer + SECTOR_SIZE) + +// TODO some dirty function to chop the high byte from the unicode charcode... +static inline uint8_t fat32_unicode_to_utf8(uint16_t unicode) { + return unicode & 0x00FF; +} + +uint32_t fat32_trim_str_right(char * str) { + uint32_t len = 0; + while (*str > ' ') { + str++; + len++; + } + *str = '\0'; + return len; +} + +// TODO loop unroll! +uint16_t fat32_extract_lfn_text(const Fat32_LongFileNameEntry * lfnEntry, char * str) { + uint16_t idx = 0; + for (uint8_t i = 0; i < 5; i++) { // first block + str[idx++] = (char) fat32_unicode_to_utf8(((uint8_t *)lfnEntry->ustr04)[2 * i]); + } + for (uint8_t i = 0; i < 6; i++) { // second block + str[idx++] = (char) fat32_unicode_to_utf8(((uint8_t *)lfnEntry->ustr5A)[2 * i]); + } + for (uint8_t i = 0; i < 2; i++) { // third block + str[idx++] = (char) fat32_unicode_to_utf8(((uint8_t *)lfnEntry->ustrBC)[2 * i]); + } + return idx; +} + +#define LONG_FILE_NAME_ATTRIB_TEST(a) ((a) & (1 << 3)) && ((a) & (1 << 2)) && ((a) & (1 << 1)) && ((a) & (1)) +#define LINELENGTH (32) + +uint16_t fat32_get_file_entry(Fat32_FileEntry ** const entry, char * entry_name) { + //static char entry_name[261]; + bool done = false; + bool reading_lfn = false; + uint8_t rewind = 0; + uint8_t attribs; + + // look for the first non-LFN entry and count the LFNs + while (!done) { + attribs = (*entry)->attributes; + if (LONG_FILE_NAME_ATTRIB_TEST(attribs)) { // if this is an LFN-entry + rewind++; + reading_lfn = true; + (*entry)++; // advance in the table + } else { // if this is NOT an LFN-entry + done = true; + } + } + + Fat32_FileEntry * const finalEntry = *(entry); // final, non-LFN entry + attribs = finalEntry->attributes; + uint16_t name_len = 0; + + // print the name of the entry + if (reading_lfn) { // if the entry has a long name + const Fat32_LongFileNameEntry * lfnEntry = (const Fat32_LongFileNameEntry *)(finalEntry - 1); // by order first (by address last) LFN entry + + // fetch long name + for (uint8_t i = 0; i < rewind; i++) { + name_len += fat32_extract_lfn_text(lfnEntry, entry_name + name_len); + lfnEntry--; // retreat entry + } + entry_name[name_len] = '\0'; // terminate string + name_len = fat32_trim_str_right(entry_name); // trim residual whitespaces + } else { // if the entry has only a short name + memcpy(entry_name, finalEntry->short_fname, 8); // copy basename + name_len = fat32_trim_str_right(entry_name); // trim whitespaces + if (!((attribs & FAT32_FATT_VOLUME) || (attribs & FAT32_FATT_DIRECTORY))) { // omit dot if volume label or directory + entry_name[name_len++] = '.'; // insert dot + } + memcpy(entry_name + name_len, finalEntry->extension, 3); // copy extension + name_len = fat32_trim_str_right(entry_name); // trim whitespaces + if (entry_name[name_len - 1] == '.') { // chop dot if no extension + name_len--; + } + entry_name[name_len] = '\0'; // terminate string + } + + return name_len; +} + +int fat32_print_file_entry(Fat32_FileEntry ** const entry) { + static char entry_name[261]; + uint16_t name_len = fat32_get_file_entry(entry, entry_name); + uint8_t attribs = (*entry)->attributes; + + // print entry type + char entry_type[5] = { 'F', ' ', ' ', ' ', '\0' }; // file, by default + if (attribs & FAT32_FATT_DIRECTORY) { + entry_type[0] = 'D'; + } else if (attribs & FAT32_FATT_VOLUME) { + entry_type[0] = 'V'; + } else if (attribs & FAT32_FATT_HIDDEN) { + entry_type[1] = 'H'; + } else if (attribs & FAT32_FATT_READ_ONLY) { + entry_type[2] = 'R'; + } else if (attribs & FAT32_FATT_SYSTEM) { + entry_type[3] = 'S'; + } + + uint16_t padding = 1; + if (name_len < LINELENGTH){ + padding = LINELENGTH - name_len; + } + + MSG("%s%*c%s %u\n", entry_name, padding, ' ', entry_type, (*entry)->size); + + return 0; +} + +#define FAT32_UNUSED_FILE_ENTRY (0xE5) +#define FAT32_DIRECTORY_SEPARATOR ('/') +#define FAT32_MAX_BASENAME_LENGTH (47) + +static inline uint16_t fat32_extract_next_basename_length(const char * path) { + uint16_t i; + for (i = 0; path[i] != FAT32_DIRECTORY_SEPARATOR && path[i] != '\0'; i++) {} + return i; +} + +int fat32_locate_file(const Fat32_CtrlBlock *ctrl, const char *path) { + FETCH_MSTG; + + // start search in the root directory + uint32_t sector = ctrl->root_s; + READ_MSTG_TO_FIRST_BUFFER(sector); + READ_MSTG_TO_SECOND_BUFFER(sector + 1); + + bool file_found = false; // file is not found in the beginning + bool no_such_file = false; // assume that there is such a file + const char * path_iter = path; // path is being fetched from its beginning + char basename[FAT32_MAX_BASENAME_LENGTH + 1]; // basename + basename[FAT32_MAX_BASENAME_LENGTH] = '\0'; // NULL-termination + + while ((!file_found) && (!no_such_file)) { + // extract first basename + uint16_t basename_len = fat32_extract_next_basename_length(path_iter); // get basename length + uint16_t copy_len = MIN(basename_len, FAT32_MAX_BASENAME_LENGTH); + memcpy(basename, path_iter, copy_len); // extract basename + basename[copy_len] = '\0'; // NULL-termination + path_iter += basename_len + 1; // advance basename + } + + return 0; +} + +int fat32_list_dir(const Fat32_CtrlBlock *ctrl, const char *dir) { + // TODO find directory + + // read root directory + FETCH_MSTG; + uint32_t sector = ctrl->root_s; + READ_MSTG_TO_FIRST_BUFFER(sector); + READ_MSTG_TO_SECOND_BUFFER(sector + 1); + + // acquire pointer to the beginning of the file entry table + Fat32_FileEntry * entry = (Fat32_FileEntry *)buffer; + while (entry->short_fname[0] != 0x00) { + if (entry->short_fname[0] != FAT32_UNUSED_FILE_ENTRY) { + fat32_print_file_entry(&entry); + } + entry++; + + // if we crossed the first buffer - second buffer border, then + // copy the second buffer to the first one and load the next sector + // to the second buffer + if ((void *)entry > (void *)(buffer + SECTOR_SIZE)) { + // copy the second buffer to the first one + fat32_copy_second_to_first_buffer(); + sector++; + READ_MSTG_TO_SECOND_BUFFER(sector + 1); + + // modify the entry pointer, since it's been moved + entry = (Fat32_FileEntry *)(((uint8_t *)(entry)) - SECTOR_SIZE); + } + } + + return 0; +} + +static inline uint32_t fat32_get_fat_entry_sector_by_cluster_index(const Fat32_CtrlBlock * ctrl, uint32_t cluster) { + return cluster * sizeof(Fat32_FatTableEntry) / ctrl->bytes_per_sector + ctrl->fat_s; +} + +//int fat32_read_file(const Fat32_CtrlBlock * ctrl, uint32_t pos, uint32_t len) { +// FETCH_MSTG; +// uint32_t fae_sec = +// Fat32_FatTableEntry fae = +//} \ No newline at end of file diff --git a/fs/fat32/fat32.h b/fs/fat32/fat32.h index a3695ae..0c32805 100644 --- a/fs/fat32/fat32.h +++ b/fs/fat32/fat32.h @@ -3,6 +3,13 @@ #include +#ifdef __linux +#include +#include "../../MassStorage.h" + +#define MSG(...) printf(__VA_ARGS__) +#endif + typedef struct { uint8_t boot_jump[3]; // jump instruction used for bootable volume uint8_t formatter_name[8]; // identification of application or OS formatted this device @@ -34,6 +41,7 @@ typedef struct { } __attribute__((packed)) Bpb_Fat32; typedef struct { + const MassStorage * mstg; // mass storage holding the partition uint32_t bpb_s; // sector of bios parameter block uint32_t fat_s; // first sector of the FAT area uint32_t root_s; // first sector of root directory @@ -46,6 +54,48 @@ typedef struct { uint8_t volume_label[11]; // pointer to volume label } Fat32_CtrlBlock; -int fat32_load(Fat32_CtrlBlock *ctrl, const uint8_t *bpb, uint32_t bpb_s); +typedef uint32_t Fat32_FatTableEntry; + +#define FAT32_FATT_READ_ONLY (1) +#define FAT32_FATT_HIDDEN (1 << 1) +#define FAT32_FATT_SYSTEM (1 << 2) +#define FAT32_FATT_VOLUME (1 << 3) +#define FAT32_FATT_DIRECTORY (1 << 4) +#define FAT32_FATT_ARCHIVE (1 << 5) + +typedef struct { + uint8_t short_fname[8]; // short filename + uint8_t extension[3]; // file extension + uint8_t attributes; // entry attributes + uint8_t _reserved0; + uint8_t creat_time_hs; // file creation time, hundredth of a second (0-199) + uint16_t creat_time_hms; // file creation time, hour, minute, second + uint16_t create_date_ymd; // file creation date year, month, day + uint16_t lastacc_data_ymd; // ... + uint16_t first_cluster_high; // high word of first cluster + uint16_t lastmod_time_hms; // ... + uint16_t lastmod_date_ymd; // ... + uint16_t first_cluster_low; // high word of first cluster + uint32_t size; // file size in bytes +} __attribute__((packed)) Fat32_FileEntry; + +typedef struct { + uint8_t index; // order of this entry in the series of LFN entries + uint16_t ustr04[5]; // characters 0-4 + uint8_t attributes; // must have bits 0-3 set + uint8_t type; // should be zero + uint8_t checksum; // string checksum + uint16_t ustr5A[6]; // characters 5-10 + uint16_t cluster; // should be zero + uint16_t ustrBC[2]; // characters 11-12 +} __attribute__((packed)) Fat32_LongFileNameEntry; + +int fat32_load(Fat32_CtrlBlock *ctrl, uint32_t bpb_s, const MassStorage * mstg); + +int fat32_list_dir(const Fat32_CtrlBlock *ctrl, const char * dir); + +int fat32_read_file(const Fat32_CtrlBlock * ctrl, uint32_t pos, uint32_t len); + +int fat32_locate_file(const Fat32_CtrlBlock *ctrl, const char *path); #endif //EMBPART_FAT32_H