CeDOS - Commit 0876d504

fat: Separating FAT12 driver and file interface The basic FAT12 functions are very different from the file functions exposed to OS and user. Separation of concerns suggest to keep them separate.
Celina Sophie Kalus
Tue, 05 Dec 2023 23:34:04 +0100
4 files changed, 287 insertions(+), 183 deletions(-)
M boot/fat.cboot/fat.c

@@ -134,7 +134,9 @@ continue;

} if (index == 0 && (dir_entry->file_attr & 0x08) && dir_entry->file_size == 0) { - // volume label + // volume label + index++; + continue; } else if ((dir_entry->file_attr & 0x10) && dir_entry->file_size == 0) { // subdirectory

@@ -179,4 +181,3 @@ uint16_t high = (uint16_t)(sect[1] & 0x0F) << 8;

return low | high; } } -
M kernel/fat.ckernel/fat.c

@@ -5,6 +5,7 @@ #include "assert.h"

#include "core.h" #include "memory.h" +#include "fat12.h" #include <stdint.h>

@@ -18,198 +19,41 @@ FAT_lseek, /* lseek */

FAT_tell /* tell */ }; -typedef struct { - char jmp[3]; - char oemname[8]; - - uint16_t bytes_per_sect; - uint8_t sect_per_cluster; - uint16_t num_reserved_sectors; - uint8_t num_FAT; - uint16_t max_root_dir_entries; - uint16_t total_log_sectors; - uint8_t media_desc; - uint16_t log_sect_per_fat; -} __attribute__((packed)) BOOT_SECT; - -typedef struct { - char name[8]; - char ext[3]; - uint8_t file_attr; - uint8_t user_attr; - uint8_t del_char; - uint16_t create_time; - uint16_t create_date; - uint16_t last_access_date; - uint16_t access_rights; - uint16_t last_modified_time; - uint16_t last_modified_date; - uint16_t start_of_clusters; - uint32_t file_size; -} __attribute__((packed)) DIR_ENTRY; - -typedef struct { - uint8_t seq_num; - uint16_t part_1[5]; - uint8_t file_attr; - uint8_t user_attr; - uint8_t del_char; - uint16_t part_2[6]; - uint16_t start_of_clusters; - uint16_t part_3[2]; -} __attribute__((packed)) VFAT_LFN_ENTRY; - - -void *FAT_addr; -BOOT_SECT *boot_sect; - -uint32_t FAT1_lba; -uint32_t FAT2_lba; -uint32_t root_lba; -uint32_t data_lba; - uint8_t *cluster_buffer; -uint32_t cluster_size; -void FAT_init() { - // open image file - FAT_addr = (void*)(0x10000); +FAT12_descriptor_t *fat_desc; - boot_sect = (BOOT_SECT*)(FAT_addr); +void *FAT12_read(uint32_t lba, uint32_t *offset, size_t size, void *buffer) { + void *FAT_addr = (void*)(0x10000); - FAT1_lba = boot_sect->num_reserved_sectors; - FAT2_lba = FAT1_lba + boot_sect->log_sect_per_fat; - root_lba = FAT1_lba + (boot_sect->log_sect_per_fat * boot_sect->num_FAT); - - long root_dir_size = boot_sect->max_root_dir_entries * sizeof(DIR_ENTRY); - data_lba = root_lba + (root_dir_size / boot_sect->bytes_per_sect); - - cluster_size = boot_sect->bytes_per_sect * boot_sect->sect_per_cluster; - cluster_buffer = malloc(cluster_size); -} + if (offset != NULL) { + lba += (*offset) / fat_desc->bytes_per_sect; + *offset = (*offset) % fat_desc->bytes_per_sect; -void *FAT_read_sector_offset(uint32_t lba, uint32_t *offset) { - if (offset != NULL) { - lba += (*offset) / boot_sect->bytes_per_sect; - *offset = (*offset) % boot_sect->bytes_per_sect; + void *ptr = (void*)((long)(FAT_addr) + (long)(lba * fat_desc->bytes_per_sect)); + return memcpy(buffer, ptr + *offset, size); + } else { + void *ptr = (void*)((long)(FAT_addr) + (long)(lba * fat_desc->bytes_per_sect)); + return memcpy(buffer, ptr, size); } - - return (void*)((long)(FAT_addr) + (long)(lba * boot_sect->bytes_per_sect)); } -void *FAT_read_cluster(uint16_t cluster, void *buffer) { - // TODO: perform memcpy - void *addr = FAT_read_sector_offset(data_lba + ((cluster - 2) * boot_sect->sect_per_cluster), NULL); - uint32_t cluster_size = boot_sect->bytes_per_sect * boot_sect->sect_per_cluster; +void FAT_init() { + fat_desc = malloc(sizeof(FAT12_descriptor_t)); - memcpy(buffer, addr, cluster_size); + FAT12_init(fat_desc); - return (void*)((uint8_t*)(buffer) + cluster_size); + cluster_buffer = malloc(fat_desc->cluster_size); } -int FAT_root_dir_next(int index, char *fname_buffer, uint16_t *first_cluster, uint32_t *file_size) { - //memset(fname_buffer, 0, sizeof(fname_buffer)); - - while (1) { - // index overflow - if (index >= boot_sect->max_root_dir_entries) { - return -1; - } - - uint32_t offset = index * sizeof(DIR_ENTRY); - void *sect = FAT_read_sector_offset(root_lba, &offset); - DIR_ENTRY *dir_entry = (DIR_ENTRY *)((uint32_t)(sect) + offset); - - // if first character of name is 0, then no subsequent entry is in use - if (dir_entry->name[0] == 0x00) { - return -1; - } - - // deleted file - if (dir_entry->name[0] == (char)(0xE5)) { - index++; - continue; - } - - // VFAT LFN entry - if (dir_entry->file_attr == 0x0F && dir_entry->start_of_clusters == 0 && dir_entry->file_size != 0) { - VFAT_LFN_ENTRY *lfn_entry = (VFAT_LFN_ENTRY*)(dir_entry); - - int offset = 13 * ((lfn_entry->seq_num & 0x3F) - 1); - - // read long file name - for (int i = 0; i < 5; i++) { - fname_buffer[offset++] = lfn_entry->part_1[i]; - } - - for (int i = 0; i < 6; i++) { - fname_buffer[offset++] = lfn_entry->part_2[i]; - } - - for (int i = 0; i < 2; i++) { - fname_buffer[offset++] = lfn_entry->part_3[i]; - } - - index++; - continue; - } - - if (index == 0 && (dir_entry->file_attr & 0x08) && dir_entry->file_size == 0) { - // volume label - index++; - continue; - - } else if ((dir_entry->file_attr & 0x10) && dir_entry->file_size == 0) { - // subdirectory - - } else { - // regular file - - } - - *file_size = dir_entry->file_size; - *first_cluster = dir_entry->start_of_clusters; - - // if no VFAT LFN exists, use DOS name - if (fname_buffer[0] == 0) { - for (int i = 0; i < 8; i++) { - fname_buffer[i] = dir_entry->name[i]; - } - fname_buffer[8] = '.'; - for (int i = 0; i < 3; i++) { - fname_buffer[i + 9] = dir_entry->ext[i]; - } - fname_buffer[12] = 0; - } - - return index + 1; - } -} int FAT_dir_next(file_t *file, int index, char *fname_buffer) { uint16_t first_cluster; uint32_t file_size; // TODO: subdirectories - return FAT_root_dir_next(index, fname_buffer, &first_cluster, &file_size); -} - -uint16_t FAT_next_cluster(uint16_t cluster) { - // assuming FAT12 - uint32_t *offset = (cluster >> 1) * 3; - uint8_t *sect = FAT_read_sector_offset(FAT1_lba, &offset); - sect += (uint32_t)(offset); - - if (cluster & 0x01) { - uint16_t high = (uint16_t)(sect[2]); - uint16_t low = (uint16_t)(sect[1] & 0xF0) >> 4; - return (high << 4) | low; - } else { - uint16_t low = (uint16_t)(sect[0]); - uint16_t high = (uint16_t)(sect[1] & 0x0F) << 8; - return low | high; - } + return FAT12_root_dir_next(fat_desc, index, fname_buffer, &first_cluster, &file_size); } int FAT_openat(file_t *root, file_t *handle, const char *fname, int flags) {

@@ -221,7 +65,7 @@ while (1) {

char buffer[832]; uint32_t file_size; - i = FAT_root_dir_next(i, buffer, &first_cluster, &file_size); + i = FAT12_root_dir_next(fat_desc, i, buffer, &first_cluster, &file_size); if (i <= 0) { return -1; } if (strcmp(buffer, fname) == 0) {

@@ -245,22 +89,22 @@ if (offset + count > file_size) {

count = file_size - offset; } - while (offset >= cluster_size) { - cluster = FAT_next_cluster(cluster); + while (offset >= fat_desc->cluster_size) { + cluster = FAT12_next_cluster(fat_desc, cluster); if (cluster == 0xFFF || cluster == 0x000) { return -1; } - offset -= cluster_size; + offset -= fat_desc->cluster_size; } while (count > 0) { if (cluster == 0xFFF || cluster == 0x000) { break; } - FAT_read_cluster(cluster, cluster_buffer); - cluster = FAT_next_cluster(cluster); + FAT12_read_cluster(fat_desc, cluster, cluster_buffer); + cluster = FAT12_next_cluster(fat_desc, cluster); uint32_t memcpy_size; - if (offset + count > cluster_size) { - memcpy_size = (cluster_size - offset); + if (offset + count > fat_desc->cluster_size) { + memcpy_size = (fat_desc->cluster_size - offset); } else { memcpy_size = count; }
A kernel/fat12.c

@@ -0,0 +1,178 @@

+#include "fat12.h" + +#include <stdint.h> +#include <stddef.h> + +#include "string.h" + +typedef struct { + char jmp[3]; + char oemname[8]; + + uint16_t bytes_per_sect; + uint8_t sect_per_cluster; + uint16_t num_reserved_sectors; + uint8_t num_FAT; + uint16_t max_root_dir_entries; + uint16_t total_log_sectors; + uint8_t media_desc; + uint16_t log_sect_per_fat; +} __attribute__((packed)) BOOT_SECT; + +typedef struct { + char name[8]; + char ext[3]; + uint8_t file_attr; + uint8_t user_attr; + uint8_t del_char; + uint16_t create_time; + uint16_t create_date; + uint16_t last_access_date; + uint16_t access_rights; + uint16_t last_modified_time; + uint16_t last_modified_date; + uint16_t start_of_clusters; + uint32_t file_size; +} __attribute__((packed)) DIR_ENTRY; + +typedef struct { + uint8_t seq_num; + uint16_t part_1[5]; + uint8_t file_attr; + uint8_t user_attr; + uint8_t del_char; + uint16_t part_2[6]; + uint16_t start_of_clusters; + uint16_t part_3[2]; +} __attribute__((packed)) VFAT_LFN_ENTRY; + + +/** + * @brief +*/ +void FAT12_init(FAT12_descriptor_t *fat) { + BOOT_SECT boot_sect; + FAT12_read(0, NULL, sizeof(boot_sect), &boot_sect); + + // copy FAT parameters from boot sector into fat descriptor + fat->bytes_per_sect = boot_sect.bytes_per_sect; + fat->log_sect_per_fat = boot_sect.log_sect_per_fat; + fat->max_root_dir_entries = boot_sect.max_root_dir_entries; + fat->media_desc = boot_sect.media_desc; + fat->num_FAT = boot_sect.num_FAT; + fat->num_reserved_sectors = boot_sect.num_reserved_sectors; + fat->sect_per_cluster = boot_sect.sect_per_cluster; + fat->total_log_sectors = boot_sect.total_log_sectors; + + // calculate starting addresses of FAT regions + fat->FAT1_lba = fat->num_reserved_sectors; + fat->FAT2_lba = fat->FAT1_lba + fat->log_sect_per_fat; + fat->root_lba = fat->FAT1_lba + (fat->log_sect_per_fat * fat->num_FAT); + + long root_dir_size = fat->max_root_dir_entries * sizeof(DIR_ENTRY); + fat->data_lba = fat->root_lba + (root_dir_size / fat->bytes_per_sect); + + // calculate cluster size + fat->cluster_size = fat->bytes_per_sect * fat->sect_per_cluster; +} + +void *FAT12_read_cluster(FAT12_descriptor_t *fat, uint16_t cluster, void *buffer) { + return FAT12_read(fat->data_lba + ((cluster - 2) * fat->sect_per_cluster), NULL, fat->cluster_size, buffer); +} + +int FAT12_root_dir_next(FAT12_descriptor_t *fat, int index, char *fname_buffer, uint16_t *first_cluster, uint32_t *file_size) { + memset(fname_buffer, 0, sizeof(fname_buffer)); + + while (1) { + //printk("%i\n", index); + // index overflow + if (index >= fat->max_root_dir_entries) { + return -1; + } + + uint32_t offset = index * sizeof(DIR_ENTRY); + DIR_ENTRY dir_entry; + FAT12_read(fat->root_lba, &offset, sizeof(DIR_ENTRY), &dir_entry); + + // if first character of name is 0, then no subsequent entry is in use + if (dir_entry.name[0] == 0x00) { + return -1; + } + + // deleted file + if (dir_entry.name[0] == (char)(0xE5)) { + index++; + continue; + } + + // VFAT LFN entry + if (dir_entry.file_attr == 0x0F && dir_entry.start_of_clusters == 0 && dir_entry.file_size != 0) { + VFAT_LFN_ENTRY *lfn_entry = (VFAT_LFN_ENTRY*)(&dir_entry); + + int offset = 13 * ((lfn_entry->seq_num & 0x3F) - 1); + + // read long file name + for (int i = 0; i < 5; i++) { + fname_buffer[offset++] = lfn_entry->part_1[i]; + } + + for (int i = 0; i < 6; i++) { + fname_buffer[offset++] = lfn_entry->part_2[i]; + } + + for (int i = 0; i < 2; i++) { + fname_buffer[offset++] = lfn_entry->part_3[i]; + } + + index++; + continue; + } + + if (index == 0 && (dir_entry.file_attr & 0x08) && dir_entry.file_size == 0) { + // volume label + index++; + continue; + + } else if ((dir_entry.file_attr & 0x10) && dir_entry.file_size == 0) { + // subdirectory + + } else { + // regular file + + } + + *file_size = dir_entry.file_size; + *first_cluster = dir_entry.start_of_clusters; + + // if no VFAT LFN exists, use DOS name + if (fname_buffer[0] == 0) { + for (int i = 0; i < 8; i++) { + fname_buffer[i] = dir_entry.name[i]; + } + fname_buffer[8] = '.'; + for (int i = 0; i < 3; i++) { + fname_buffer[i + 9] = dir_entry.ext[i]; + } + fname_buffer[12] = 0; + } + + return index + 1; + } +} + +uint16_t FAT12_next_cluster(FAT12_descriptor_t *fat, uint16_t cluster) { + // assuming FAT12 + uint32_t offset = (cluster >> 1) * 3; + uint8_t sect[3]; + FAT12_read(fat->FAT1_lba, &offset, sizeof(sect), sect); + + if (cluster & 0x01) { + uint16_t high = (uint16_t)(sect[2]); + uint16_t low = (uint16_t)(sect[1] & 0xF0) >> 4; + return (high << 4) | low; + } else { + uint16_t low = (uint16_t)(sect[0]); + uint16_t high = (uint16_t)(sect[1] & 0x0F) << 8; + return low | high; + } +}
A kernel/fat12.h

@@ -0,0 +1,81 @@

+#ifndef FAT12_H +#define FAT12_H + +#include <stdint.h> + +typedef struct { + // logical block addresses of FAT regions + uint32_t FAT1_lba; + uint32_t FAT2_lba; + uint32_t root_lba; + uint32_t data_lba; + + // FAT parameters, read out from boot sector + uint32_t bytes_per_sect; + uint32_t sect_per_cluster; + uint32_t num_reserved_sectors; + uint32_t num_FAT; + uint32_t max_root_dir_entries; + uint32_t total_log_sectors; + uint32_t media_desc; + uint32_t log_sect_per_fat; + + // calculated cluster size in bytes + uint32_t cluster_size; +} FAT12_descriptor_t; + + +/** + * @brief Read out a portion from the raw FAT12 image. + * This function has to be defined by whichever code is including this driver. + */ +extern void *FAT12_read(uint32_t lba, uint32_t *offset, size_t size, void *buffer); + + +/** + * @brief Initialize FAT12 descriptor structure + * + * The FAT12 descriptor contains read-out and pre-computed + * information about a given FAT12 image or disk. + * FAT12_init initializes the descriptor \p fat based on the FAT + * device that is accessible through the FAT12_read function. + * + * @param fat FAT descriptor to be initialized. +*/ +void FAT12_init(FAT12_descriptor_t *fat); + +/** + * @brief Read a single cluster from the FAT image. + * + * @param fat FAT descriptor of the FAT device. The driver user has to + * ensure that the descriptor corresponds to the FAT device accessible + * through the FAT12_read function. + * @param cluster Index of the cluster to read. + * @param buffer Pointer to a cluster-sized buffer. +*/ +void *FAT12_read_cluster(FAT12_descriptor_t *fat, uint16_t cluster, void *buffer); + +/** + * @brief Iterate through entries in the root directory and return the next file name, the first cluster index, and file size. + * + * @param fat FAT descriptor of the FAT device. + * @param index Index within the root directory table at which to start. + * @param fname_buffer Pointer to a buffer for the file name. + * @param first_cluster Pointer to a uint16_t to be filled with the index of the first cluster of the file. + * @param file_size Pointer to a uint32_t to be filled with the file size. + * + * @return -1 if the search has reached an end; Index within the directory table otherwise. +*/ +int FAT12_root_dir_next(FAT12_descriptor_t *fat, int index, char *fname_buffer, uint16_t *first_cluster, uint32_t *file_size); + +/** + * @brief Given a cluster index, return the value of the FAT table. + * + * @param fat FAT descriptor of the FAT device. + * @param cluster Cluster index. + * + * @return Value of the FAT at the given cluster index. +*/ +uint16_t FAT12_next_cluster(FAT12_descriptor_t *fat, uint16_t cluster); + +#endif