#include "src/kernel/fat16.h" #include "src/kernel/ata.h" #include "src/kernel/fs.h" #include "src/kernel/panic.h" #include "src/kernel/stream.h" #include "src/lib/memory.h" #include "src/lib/string.h" #include "src/lib/syscall.h" #include "src/lib/util.h" #define SECTOR_SIZE 512 #define FIRST_PARTITION_SECTOR 2048 #define ATTRIBUTE_SUBDIRECTORY 0x10 typedef struct __attribute__((packed)) { uint16_t bytes_per_sector; uint8_t sectors_per_cluster; uint16_t reserved_sectors; uint8_t fats_count; uint16_t root_entry_count; uint16_t total_sectors_16; uint8_t media_type; uint16_t sectors_per_fat; } fat16_bpb_t; typedef struct __attribute__((packed)) { char name[8]; char ext[3]; uint8_t attributes; uint8_t reserved[10]; uint16_t modified_time; uint16_t modified_date; uint16_t first_cluster; uint32_t size; } fat16_dir_entry_t; // Assuming one persistently mounted partition. static fat16_bpb_t *bpb = NUL; static uint32_t data_start; static uint16_t *fat = NUL; typedef struct { fs_node_t base; uint16_t dir_cluster; uint16_t dir_index; uint16_t first_cluster; } fat16_node_t; #define MAX_NODES 256 static fat16_node_t nodes[MAX_NODES]; static fs_node_t *node_create(uint16_t dir_cluster, uint16_t dir_index, const char *name, uint8_t is_dir, uint16_t first_cluster, uint32_t size) { for (uint64_t i = 0; i < MAX_NODES; i++) { if (!nodes[i].base.refs) { nodes[i].base.type = FAT16; nodes[i].base.refs = 1; memory_copy(name, FILENAME_SIZE_LIMIT + 1, nodes[i].base.name); nodes[i].base.size = size; nodes[i].base.is_dir = is_dir; nodes[i].base.removed = 0; nodes[i].dir_cluster = dir_cluster; nodes[i].dir_index = dir_index; nodes[i].first_cluster = first_cluster; return (fs_node_t *)&nodes[i]; } } return NUL; } static fs_node_t *node_get(uint32_t dir_cluster, uint16_t dir_index) { for (uint64_t i = 0; i < MAX_NODES; i++) { if (nodes[i].base.refs && nodes[i].dir_cluster == dir_cluster && nodes[i].dir_index == dir_index) { return (fs_node_t *)&nodes[i]; } } return NUL; } static fs_node_t *node_use(uint16_t dir_cluster, uint16_t dir_index, const char *name, uint8_t is_dir, uint16_t first_cluster, uint32_t size) { fs_node_t *result = node_get(dir_cluster, dir_index); if (!result) { result = node_create(dir_cluster, dir_index, name, is_dir, first_cluster, size); } else { ASSERT(result->refs < UINT32_MAX, "node_use: too many references"); result->refs++; } return result; } static void node_free(fs_node_t *node) { if (node->refs) { node->refs--; } } static void to_8_3(const char *name, char *output) { memory_set(' ', 11, output); output[11] = '\0'; const char *c = name; uint64_t i = 0; uint8_t ext = 0; while (*c) { if (*c == '.') { i = 8; ext = 1; } else if (i < (!ext ? 8 : 11)) { output[i++] = *c >= 'a' && *c <= 'z' ? *c - 32 : *c; } c++; } } static void from_8_3(const char *name, const char *extension, char *output) { memory_set(0, 13, output); uint16_t ni = 0, ei = 0, oi = 0; while (ni < 8 && name[ni] != ' ') { output[oi++] = name[ni++]; } if (extension[ei] != ' ') { output[oi++] = '.'; while (ei < 3 && extension[ei] != ' ') { output[oi++] = extension[ei++]; } } } static void ensure_bpb() { if (bpb) { return; } uint8_t sector[512]; ata_read_sectors(FIRST_PARTITION_SECTOR, 1, §or); bpb = memory_allocate(sizeof(fat16_bpb_t)); memory_copy(sector + 11, sizeof(fat16_bpb_t), bpb); data_start = FIRST_PARTITION_SECTOR + bpb->reserved_sectors + bpb->sectors_per_fat * bpb->fats_count + (bpb->root_entry_count * sizeof(fat16_dir_entry_t) + SECTOR_SIZE - 1) / SECTOR_SIZE; } static void ensure_fat() { if (fat) { return; } ensure_bpb(); ASSERT(bpb->sectors_per_fat < 256, "ensure_fat: big FAT not implemented"); fat = memory_allocate(bpb->sectors_per_fat * SECTOR_SIZE); ata_read_sectors(FIRST_PARTITION_SECTOR + bpb->reserved_sectors, (uint8_t)bpb->sectors_per_fat, fat); } static uint16_t allocate_cluster() { ensure_fat(); for (uint16_t i = 2; i < bpb->sectors_per_fat * SECTOR_SIZE / 2; i++) { if (fat[i] == 0x0000) { fat[i] = 0xFFFF; return i; } } return 0; } static void flush_fat() { ensure_fat(); ata_write_sectors(FIRST_PARTITION_SECTOR + bpb->reserved_sectors, (uint8_t)bpb->sectors_per_fat, fat); } static uint16_t read_directory(uint16_t dir_cluster, fat16_dir_entry_t **entries) { ensure_bpb(); if (!dir_cluster) { uint8_t sectors = (uint8_t)((sizeof(fat16_dir_entry_t) * bpb->root_entry_count + SECTOR_SIZE - 1) / SECTOR_SIZE); *entries = memory_allocate(sectors * SECTOR_SIZE); ata_read_sectors(FIRST_PARTITION_SECTOR + bpb->reserved_sectors + bpb->fats_count * bpb->sectors_per_fat, sectors, *entries); return bpb->root_entry_count; } else { ensure_fat(); uint16_t next_cluster = dir_cluster; uint16_t cluster_count = 0; while (next_cluster < 0xFFF8) { cluster_count++; next_cluster = fat[next_cluster]; } *entries = memory_allocate(cluster_count * bpb->sectors_per_cluster * SECTOR_SIZE); uint8_t *chunk = (uint8_t *)*entries; next_cluster = dir_cluster; while (next_cluster < 0xFFF8) { ata_read_sectors(data_start + bpb->sectors_per_cluster * (next_cluster - 2), bpb->sectors_per_cluster, chunk); chunk += bpb->sectors_per_cluster * SECTOR_SIZE; next_cluster = fat[next_cluster]; } return cluster_count * bpb->sectors_per_cluster * SECTOR_SIZE / sizeof(fat16_dir_entry_t); } return (uint16_t)-1; } static fs_node_t *open_entry(uint16_t dir_cluster, const fat16_dir_entry_t *entries, uint16_t index) { const fat16_dir_entry_t *entry = &entries[index]; if (!entry->name[0]) { return NUL; } char name[FILENAME_SIZE_LIMIT + 1]; from_8_3(entry->name, entry->ext, name); return node_use(dir_cluster, index, name, entry->attributes & ATTRIBUTE_SUBDIRECTORY, entry->first_cluster, entry->size); } static void write_directory(uint16_t dir_cluster, const fat16_dir_entry_t *entries, uint16_t count) { ensure_bpb(); ASSERT((count * sizeof(fat16_dir_entry_t)) % (bpb->sectors_per_cluster * SECTOR_SIZE) == 0, "write_directory: entries must be aligned to clusters"); if (!dir_cluster) { ata_write_sectors(FIRST_PARTITION_SECTOR + bpb->reserved_sectors + bpb->fats_count * bpb->sectors_per_fat, (uint8_t)((sizeof(fat16_dir_entry_t) * bpb->root_entry_count + SECTOR_SIZE - 1) / SECTOR_SIZE), entries); } else { ensure_fat(); uint8_t *chunk = (uint8_t *)entries; uint16_t next_cluster = dir_cluster; while (count) { ata_write_sectors(data_start + bpb->sectors_per_cluster * (next_cluster - 2), bpb->sectors_per_cluster, chunk); chunk += bpb->sectors_per_cluster * SECTOR_SIZE; count -= bpb->sectors_per_cluster * SECTOR_SIZE / sizeof(fat16_dir_entry_t); if (count && fat[next_cluster] >= 0xFFF8) { fat[next_cluster] = allocate_cluster(); } next_cluster = fat[next_cluster]; } flush_fat(); } } fs_node_t *fat16_open_root() { ensure_bpb(); return node_use(0, UINT16_MAX, "", 1, 0, sizeof(fat16_dir_entry_t) * bpb->root_entry_count); } fs_node_t *fat16_open_by(const fs_node_t *directory, const char *name, uint64_t flags) { if (!directory || directory->type != FAT16 || !directory->is_dir || directory->removed) { return NUL; } uint16_t dir_cluster = ((fat16_node_t *)directory)->first_cluster; char name_8_3[12]; to_8_3(name, name_8_3); fat16_dir_entry_t *entries; uint16_t entries_count = read_directory(dir_cluster, &entries); fat16_dir_entry_t *entry = entries; uint16_t index = 0; while (index < entries_count && entry->name[0]) { if ((uint8_t)entry->name[0] != 0xE5 && (uint8_t)entry->attributes != 0x0F && bytes_equal(name_8_3, (char *)entry, 11)) { break; } entry++; index++; } fs_node_t *result = open_entry(dir_cluster, entries, index); if (result && (flags & OPEN_EXCLUSIVE)) { memory_free(entries); return NUL; } if (!result && (flags & OPEN_CREATE)) { index = 0; while (index < entries_count && entries[index].name[0]) { index++; } if (index >= entries_count) { if (!dir_cluster) { memory_free(entries); return NUL; } else { uint16_t new_entries_count = entries_count + bpb->sectors_per_cluster * SECTOR_SIZE / sizeof(fat16_dir_entry_t); fat16_dir_entry_t *new_entries = memory_allocate(new_entries_count * sizeof(fat16_dir_entry_t)); memory_copy(entries, entries_count * sizeof(fat16_dir_entry_t), new_entries); memory_free(entries); entries_count = new_entries_count; entries = new_entries; } } memory_copy(name_8_3, 11, &entries[index]); if (flags & OPEN_DIRECTORY && !(flags & OPEN_FILE)) { entries[index].attributes = ATTRIBUTE_SUBDIRECTORY; entries[index].first_cluster = allocate_cluster(); fat16_dir_entry_t *inner_entries = memory_allocate(bpb->sectors_per_cluster * SECTOR_SIZE); memory_copy(". ", 11, inner_entries[0].name); inner_entries[0].attributes = ATTRIBUTE_SUBDIRECTORY; inner_entries[0].first_cluster = entries[index].first_cluster; memory_copy(".. ", 11, inner_entries[1].name); inner_entries[1].attributes = ATTRIBUTE_SUBDIRECTORY; inner_entries[1].first_cluster = dir_cluster; write_directory(entries[index].first_cluster, inner_entries, bpb->sectors_per_cluster * SECTOR_SIZE / sizeof(fat16_dir_entry_t)); memory_free(inner_entries); } write_directory(dir_cluster, entries, entries_count); result = open_entry(dir_cluster, entries, index); } memory_free(entries); if (!result) { return NUL; } if (result->is_dir && !(flags & OPEN_DIRECTORY)) { return NUL; } if (!result->is_dir && !(flags & OPEN_FILE)) { return NUL; } return result; } fs_node_t *fat16_open_at(const fs_node_t *directory, uint16_t index, uint64_t flags) { if (!directory || directory->type != FAT16 || !directory->is_dir || directory->removed) { return NUL; } uint16_t dir_cluster = ((fat16_node_t *)directory)->first_cluster; fat16_dir_entry_t *entries; uint16_t entries_count = read_directory(dir_cluster, &entries); uint16_t ei = 0, vi = 0; while (ei < entries_count && entries[ei].name[0]) { if ((uint8_t)entries[ei].name[0] != 0xE5 && (uint8_t)entries[ei].attributes != 0x0F) { if (vi == index) { break; } vi++; } ei++; } fs_node_t *result = open_entry(dir_cluster, entries, ei); memory_free(entries); if (result && (flags & OPEN_EXCLUSIVE)) { return NUL; } if (!result) { return NUL; } if (result->is_dir && !(flags & OPEN_DIRECTORY)) { return NUL; } if (!result->is_dir && !(flags & OPEN_FILE)) { return NUL; } return result; } fs_node_t *fat16_open_again(fs_node_t *source) { ASSERT(source->refs < UINT32_MAX, "fat16_open_again: too many references"); source->refs++; return source; } uint64_t fat16_read(const fs_node_t *file, uint32_t offset, uint32_t bytes, void *to) { if (!file || file->type != FAT16 || file->is_dir) { return (uint64_t)-1; } if (file->removed) { return 0; } if (offset >= file->size) { return 0; } if (offset + bytes >= file->size) { bytes = file->size - offset; } ensure_fat(); uint32_t cluster_size = bpb->sectors_per_cluster * SECTOR_SIZE; uint32_t next_cluster = ((fat16_node_t *)file)->first_cluster; while (offset >= cluster_size) { next_cluster = fat[next_cluster]; offset -= cluster_size; } uint32_t readden = 0; uint8_t *cursor = to; uint8_t *tmp = memory_allocate(cluster_size); ata_read_sectors(data_start + bpb->sectors_per_cluster * (next_cluster - 2), bpb->sectors_per_cluster, tmp); uint32_t prefix_size = bytes <= cluster_size - offset ? bytes : cluster_size - offset; memory_copy(tmp + offset, prefix_size, cursor); bytes -= prefix_size; readden += prefix_size; cursor += prefix_size; next_cluster = fat[next_cluster]; while (bytes >= cluster_size) { ata_read_sectors(data_start + bpb->sectors_per_cluster * (next_cluster - 2), bpb->sectors_per_cluster, cursor); bytes -= cluster_size; readden += cluster_size; cursor += cluster_size; next_cluster = fat[next_cluster]; } if (bytes) { ata_read_sectors(data_start + bpb->sectors_per_cluster * (next_cluster - 2), bpb->sectors_per_cluster, tmp); memory_copy(tmp, bytes, cursor); readden += bytes; } memory_free(tmp); return readden; } uint64_t fat16_write(fs_node_t *file, uint32_t offset, const void *from, uint32_t bytes) { if (!file || file->type != FAT16 || file->is_dir) { return (uint64_t)-1; } if (file->removed) { return 0; } ensure_fat(); fat16_node_t *fat_file = (fat16_node_t *)file; if (!fat_file->first_cluster) { fat_file->first_cluster = allocate_cluster(); } uint32_t cluster_size = bpb->sectors_per_cluster * SECTOR_SIZE; uint32_t next_cluster = fat_file->first_cluster; uint32_t allocated = cluster_size; while (allocated < offset + bytes) { if (fat[next_cluster] >= 0xFFF8) { fat[next_cluster] = allocate_cluster(); } next_cluster = fat[next_cluster]; allocated += cluster_size; } flush_fat(); cluster_size = bpb->sectors_per_cluster * SECTOR_SIZE; next_cluster = fat_file->first_cluster; while (offset >= cluster_size) { next_cluster = fat[next_cluster]; offset -= cluster_size; } uint32_t written = 0; const uint8_t *cursor = from; uint8_t *tmp = memory_allocate(cluster_size); ata_read_sectors(data_start + bpb->sectors_per_cluster * (next_cluster - 2), bpb->sectors_per_cluster, tmp); uint32_t prefix_size = bytes <= cluster_size - offset ? bytes : cluster_size - offset; memory_copy(cursor, prefix_size, tmp + offset); ata_write_sectors(data_start + bpb->sectors_per_cluster * (next_cluster - 2), bpb->sectors_per_cluster, tmp); bytes -= prefix_size; written += prefix_size; cursor += prefix_size; next_cluster = fat[next_cluster]; while (bytes >= cluster_size) { ata_write_sectors(data_start + bpb->sectors_per_cluster * (next_cluster - 2), bpb->sectors_per_cluster, cursor); bytes -= cluster_size; written += cluster_size; cursor += cluster_size; next_cluster = fat[next_cluster]; } if (bytes) { ata_read_sectors(data_start + bpb->sectors_per_cluster * (next_cluster - 2), bpb->sectors_per_cluster, tmp); memory_copy(cursor, bytes, tmp); ata_write_sectors(data_start + bpb->sectors_per_cluster * (next_cluster - 2), bpb->sectors_per_cluster, tmp); written += bytes; } memory_free(tmp); if (offset + written > file->size) { file->size = offset + written; fat16_dir_entry_t *entries; uint16_t entries_count = read_directory(fat_file->dir_cluster, &entries); entries[fat_file->dir_index].first_cluster = fat_file->first_cluster; entries[fat_file->dir_index].size = file->size; write_directory(fat_file->dir_cluster, entries, entries_count); memory_free(entries); } return written; } uint64_t fat16_truncate(fs_node_t *file, uint32_t size) { if (!file || file->type != FAT16 || file->is_dir) { return (uint64_t)-1; } if (file->removed) { return 0; } if (file->size == size) { return size; } else if (file->size < size) { uint32_t diff = size - file->size; uint8_t *tmp = memory_allocate(diff); uint64_t written = fat16_write(file, file->size, tmp, diff); memory_free(tmp); return written == (uint64_t)-1 ? (uint64_t)-1 : file->size + written; } else if (file->size > size) { fat16_node_t *fat_file = (fat16_node_t *)file; uint32_t cluster_size = bpb->sectors_per_cluster * SECTOR_SIZE; uint32_t prev_cluster = 0; uint32_t next_cluster = fat_file->first_cluster; ensure_fat(); uint32_t allocated = 0; while (allocated < size) { prev_cluster = next_cluster; next_cluster = fat[next_cluster]; allocated += cluster_size; } if (prev_cluster) { fat[prev_cluster] = 0xFFFF; } else { fat_file->first_cluster = 0; } do { uint32_t swap = fat[next_cluster]; fat[next_cluster] = 0; next_cluster = swap; } while (next_cluster < 0xFFF8); flush_fat(); file->size = size; fat16_dir_entry_t *entries; uint16_t entries_count = read_directory(fat_file->dir_cluster, &entries); entries[fat_file->dir_index].first_cluster = fat_file->first_cluster; entries[fat_file->dir_index].size = file->size; write_directory(fat_file->dir_cluster, entries, entries_count); memory_free(entries); return size; } return (uint64_t)-1; } uint64_t fat16_remove(fs_node_t *file) { if (!file || file->type != FAT16) { return (uint64_t)-1; } if (file->removed) { return 0; } fat16_node_t *fat_file = (fat16_node_t *)file; if (file->is_dir) { fat16_dir_entry_t *subentries; uint16_t subentries_count = read_directory(fat_file->first_cluster, &subentries); uint16_t ei = 0, vi = 0; while (ei < subentries_count && subentries[ei].name[0]) { if ((uint8_t)subentries[ei].name[0] != 0xE5 && (uint8_t)subentries[ei].attributes != 0x0F) { vi++; } ei++; } memory_free(subentries); if (vi > 2) { return (uint64_t)-1; } } file->removed = 1; if (fat_file->first_cluster) { ensure_fat(); uint64_t next_cluster = fat_file->first_cluster; do { uint32_t swap = fat[next_cluster]; fat[next_cluster] = 0; next_cluster = swap; } while (next_cluster < 0xFFF8); flush_fat(); } fat16_dir_entry_t *entries; uint16_t entries_count = read_directory(fat_file->dir_cluster, &entries); entries[fat_file->dir_index].name[0] = 0xE5; entries[fat_file->dir_index].first_cluster = 0; entries[fat_file->dir_index].size = 0; write_directory(fat_file->dir_cluster, entries, entries_count); memory_free(entries); return 1; } typedef struct { stream_t stream; fs_node_t *node; uint32_t offset; } fat16_file_stream_t; static uint64_t file_stream_write(stream_t *self, const char *from, uint64_t bytes) { if (!self) { return (uint64_t)-1; } fat16_file_stream_t *ffs = (fat16_file_stream_t *)self; uint64_t written = fat16_write(ffs->node, ffs->offset, from, bytes > UINT32_MAX ? UINT32_MAX : (uint32_t)bytes); if (written != (uint64_t)-1) { ffs->offset += written; } return written; } static uint64_t file_stream_read(stream_t *self, uint64_t max, char *to) { if (!self) { return (uint64_t)-1; } fat16_file_stream_t *ffs = (fat16_file_stream_t *)self; uint64_t readden = fat16_read(ffs->node, ffs->offset, max > UINT32_MAX ? UINT32_MAX : (uint32_t)max, to); if (readden != (uint64_t)-1) { ffs->offset += readden; } return readden; } static uint64_t file_stream_truncate(stream_t *self, uint64_t size) { if (!self) { return (uint64_t)-1; } fat16_file_stream_t *ffs = (fat16_file_stream_t *)self; uint64_t resized = fat16_truncate(ffs->node, size > UINT32_MAX ? UINT32_MAX : (uint32_t)size); if (resized != (uint64_t)-1 && resized < ffs->offset) { ffs->offset = (uint32_t)resized; } return resized; } static void file_stream_close(stream_t *self) { if (!self) { return; } fat16_file_stream_t *ffs = (fat16_file_stream_t *)self; fat16_close(ffs->node); memory_free(self); } typedef struct { stream_t stream; fs_node_t *node; uint16_t index; } fat16_directory_stream_t; static uint64_t directory_stream_write(__attribute__((unused)) stream_t *self, __attribute__((unused)) const char *from, __attribute__((unused)) uint64_t bytes) { return (uint64_t)-1; } static uint64_t directory_stream_read(stream_t *self, uint64_t max, char *to) { if (!self) { return (uint64_t)-1; } fat16_directory_stream_t *fds = (fat16_directory_stream_t *)self; fs_node_t *node = fat16_open_at(fds->node, fds->index++, OPEN_FILE | OPEN_DIRECTORY); if (!node) { return 0; } uint64_t length = string_length(node->name); uint64_t to_read = length > max ? max : length; memory_copy(node->name, to_read, to); return to_read; } static uint64_t directory_stream_truncate(__attribute__((unused)) stream_t *self, __attribute__((unused)) uint64_t size) { return (uint64_t)-1; } static void directory_stream_close(stream_t *self) { if (!self) { return; } fat16_directory_stream_t *fds = (fat16_directory_stream_t *)self; fat16_close(fds->node); memory_free(self); } stream_t *fat16_open_stream(fs_node_t *source) { if (!source) { return NUL; } if (!source->is_dir) { fat16_file_stream_t *result = memory_allocate(sizeof(fat16_file_stream_t)); result->stream.read = file_stream_read; result->stream.write = file_stream_write; result->stream.truncate = file_stream_truncate; result->stream.close = file_stream_close; result->node = fat16_open_again(source); result->offset = 0; return (stream_t *)result; } else { fat16_directory_stream_t *result = memory_allocate(sizeof(fat16_directory_stream_t)); result->stream.read = directory_stream_read; result->stream.write = directory_stream_write; result->stream.truncate = directory_stream_truncate; result->stream.close = directory_stream_close; result->node = fat16_open_again(source); result->index = 0; return (stream_t *)result; } } void fat16_close(fs_node_t *node) { if (!node || node->type != FAT16) { return; } node_free(node); }