fat: Initial FAT16 implementation

This commit is contained in:
mintsuki 2021-03-13 05:07:18 +01:00
parent 23838a4e84
commit 1822839506
3 changed files with 114 additions and 30 deletions

View File

@ -6,7 +6,7 @@ DESTDIR =
PATH := $(shell pwd)/toolchain/bin:$(PATH)
.PHONY: all clean install distclean limine-bios limine-uefi limine-bios-clean limine-uefi-clean stage23-bios stage23-bios-clean stage23-uefi stage23-uefi-clean decompressor decompressor-clean toolchain test.hdd echfs-test ext2-test fat32-test iso9660-test
.PHONY: all clean install distclean limine-bios limine-uefi limine-bios-clean limine-uefi-clean stage23-bios stage23-bios-clean stage23-uefi stage23-uefi-clean decompressor decompressor-clean toolchain test.hdd echfs-test ext2-test fat16-test fat32-test iso9660-test pxe-test uefi-test
all:
$(MAKE) limine-uefi
@ -138,6 +138,27 @@ ext2-test:
bin/limine-install test.hdd
qemu-system-x86_64 -net none -smp 4 -enable-kvm -cpu host -hda test.hdd -debugcon stdio
fat16-test:
$(MAKE) test-clean
$(MAKE) test.hdd
$(MAKE) limine-bios
$(MAKE) bin/limine-install
$(MAKE) -C test
rm -rf test_image/
mkdir test_image
sudo losetup -Pf --show test.hdd > loopback_dev
sudo partprobe `cat loopback_dev`
sudo mkfs.fat -F 16 `cat loopback_dev`p1
sudo mount `cat loopback_dev`p1 test_image
sudo mkdir test_image/boot
sudo cp -rv bin/* test/* test_image/boot/
sync
sudo umount test_image/
sudo losetup -d `cat loopback_dev`
rm -rf test_image loopback_dev
bin/limine-install test.hdd
qemu-system-x86_64 -net none -smp 4 -enable-kvm -cpu host -hda test.hdd -debugcon stdio
fat32-test:
$(MAKE) test-clean
$(MAKE) test.hdd

View File

@ -6,14 +6,18 @@
struct fat32_context {
struct volume *part;
int type;
uint8_t sectors_per_cluster;
uint16_t reserved_sectors;
uint8_t number_of_fats;
uint32_t hidden_sectors;
uint32_t sectors_per_fat;
uint32_t root_directory_cluster;
uint32_t fat_start_lba;
uint32_t data_start_lba;
uint32_t root_directory_cluster;
uint16_t root_entries;
uint32_t root_start;
uint32_t root_size;
};
struct fat32_file_handle {

View File

@ -12,6 +12,7 @@
#define FAT32_VALID_SIGNATURE_1 0x28
#define FAT32_VALID_SIGNATURE_2 0x29
#define FAT32_VALID_SYSTEM_IDENTIFIER "FAT32 "
#define FAT16_VALID_SYSTEM_IDENTIFIER "FAT16 "
#define FAT32_SECTOR_SIZE 512
#define FAT32_ATTRIBUTE_SUBDIRECTORY 0x10
#define FAT32_LFN_ATTRIBUTE 0x0F
@ -73,50 +74,81 @@ static int fat32_init_context(struct fat32_context* context, struct volume *part
struct fat32_bpb bpb;
volume_read(context->part, &bpb, 0, sizeof(struct fat32_bpb));
if (bpb.signature != FAT32_VALID_SIGNATURE_1 && bpb.signature != FAT32_VALID_SIGNATURE_2) {
return 1;
if (strncmp(bpb.system_identifier, FAT32_VALID_SYSTEM_IDENTIFIER, SIZEOF_ARRAY(bpb.system_identifier)) == 0) {
if (bpb.signature == FAT32_VALID_SIGNATURE_1 || bpb.signature == FAT32_VALID_SIGNATURE_2) {
context->type = 32;
goto valid;
}
}
if (strncmp(bpb.system_identifier, FAT32_VALID_SYSTEM_IDENTIFIER, SIZEOF_ARRAY(bpb.system_identifier)) != 0) {
return 1;
if (strncmp((((void *)&bpb) + 0x36), FAT16_VALID_SYSTEM_IDENTIFIER, SIZEOF_ARRAY(bpb.system_identifier)) == 0) {
context->type = 16;
goto valid;
}
return 1;
valid:
context->sectors_per_cluster = bpb.sectors_per_cluster;
context->reserved_sectors = bpb.reserved_sectors;
context->number_of_fats = bpb.fats_count;
context->hidden_sectors = bpb.hidden_sectors_count;
context->sectors_per_fat = bpb.sectors_per_fat_32;
context->sectors_per_fat = context->type == 32 ? bpb.sectors_per_fat_32 : bpb.sectors_per_fat_16;
context->root_directory_cluster = bpb.root_directory_cluster;
context->fat_start_lba = bpb.reserved_sectors;
context->data_start_lba = context->fat_start_lba + bpb.fats_count * bpb.sectors_per_fat_32;
context->root_entries = bpb.directory_entries_count;
context->root_start = context->reserved_sectors + context->number_of_fats * context->sectors_per_fat;
context->root_size = DIV_ROUNDUP(context->root_entries * sizeof(struct fat32_directory_entry), FAT32_SECTOR_SIZE);
switch (context->type) {
case 16:
context->data_start_lba = context->root_start + context->root_size;
break;
case 32:
context->data_start_lba = context->root_start;
break;
default:
__builtin_unreachable();
}
return 0;
}
static int fat32_read_cluster_from_map(struct fat32_context* context, uint32_t cluster, uint32_t* out) {
volume_read(context->part, out, context->fat_start_lba * FAT32_SECTOR_SIZE + cluster * sizeof(uint32_t), sizeof(uint32_t));
static int read_cluster_from_map(struct fat32_context *context, uint32_t cluster, uint32_t *out) {
switch (context->type) {
case 16:
*out = 0;
volume_read(context->part, out, context->fat_start_lba * FAT32_SECTOR_SIZE + cluster * sizeof(uint16_t), sizeof(uint16_t));
break;
case 32:
volume_read(context->part, out, context->fat_start_lba * FAT32_SECTOR_SIZE + cluster * sizeof(uint32_t), sizeof(uint32_t));
*out &= 0x0fffffff;
break;
default:
__builtin_unreachable();
}
*out &= 0x0fffffff;
return 0;
}
static uint32_t *cache_cluster_chain(struct fat32_context *context,
uint32_t initial_cluster,
size_t *_chain_length) {
if (initial_cluster < 0x2 || initial_cluster > 0xfffffef)
uint32_t cluster_limit = (context->type == 16 ? 0xffef : 0)
| (context->type == 32 ? 0xfffffef : 0);
if (initial_cluster < 0x2 || initial_cluster > cluster_limit)
return NULL;
uint32_t cluster = initial_cluster;
size_t chain_length;
for (chain_length = 1; ; chain_length++) {
fat32_read_cluster_from_map(context, cluster, &cluster);
if (cluster < 0x2 || cluster > 0xfffffef)
read_cluster_from_map(context, cluster, &cluster);
if (cluster < 0x2 || cluster > cluster_limit)
break;
}
uint32_t *cluster_chain = ext_mem_alloc(chain_length * sizeof(uint32_t));
cluster = initial_cluster;
for (size_t i = 0; i < chain_length; i++) {
cluster_chain[i] = cluster;
fat32_read_cluster_from_map(context, cluster, &cluster);
read_cluster_from_map(context, cluster, &cluster);
}
*_chain_length = chain_length;
return cluster_chain;
@ -134,7 +166,7 @@ static bool read_cluster_chain(struct fat32_context *context,
if (chunk > block_size - offset)
chunk = block_size - offset;
uint64_t base = (context->data_start_lba + (cluster_chain[block] - 2) * context->sectors_per_cluster) * FAT32_SECTOR_SIZE;
uint64_t base = ((uint64_t)context->data_start_lba + (cluster_chain[block] - 2) * context->sectors_per_cluster) * FAT32_SECTOR_SIZE;
volume_read(context->part, buf + progress, base + offset, chunk);
progress += chunk;
@ -183,16 +215,28 @@ static int fat32_open_in(struct fat32_context* context, struct fat32_directory_e
size_t block_size = context->sectors_per_cluster * FAT32_SECTOR_SIZE;
char current_lfn[FAT32_LFN_MAX_FILENAME_LENGTH] = {0};
uint32_t current_cluster_number = directory->cluster_num_high << 16 | directory->cluster_num_low;
size_t dir_chain_len;
uint32_t *directory_cluster_chain = cache_cluster_chain(context, current_cluster_number, &dir_chain_len);
size_t dir_chain_len;
struct fat32_directory_entry *directory_entries;
if (directory_cluster_chain == NULL)
return -1;
if (directory != NULL) {
uint32_t current_cluster_number = directory->cluster_num_high << 16 | directory->cluster_num_low;
struct fat32_directory_entry *directory_entries = ext_mem_alloc(dir_chain_len * block_size);
if (!read_cluster_chain(context, directory_cluster_chain, directory_entries, 0, dir_chain_len * block_size))
return -1;
uint32_t *directory_cluster_chain = cache_cluster_chain(context, current_cluster_number, &dir_chain_len);
if (directory_cluster_chain == NULL)
return -1;
directory_entries = ext_mem_alloc(dir_chain_len * block_size);
if (!read_cluster_chain(context, directory_cluster_chain, directory_entries, 0, dir_chain_len * block_size))
return -1;
} else {
dir_chain_len = DIV_ROUNDUP(context->root_entries * sizeof(struct fat32_directory_entry), block_size);
directory_entries = ext_mem_alloc(dir_chain_len * block_size);
volume_read(context->part, directory_entries, context->root_start * FAT32_SECTOR_SIZE, context->root_entries * sizeof(struct fat32_directory_entry));
}
for (size_t i = 0; i < (dir_chain_len * block_size) / sizeof(struct fat32_directory_entry); i++) {
if (directory_entries[i].file_name_and_ext[0] == 0x00) {
@ -234,7 +278,10 @@ static int fat32_open_in(struct fat32_context* context, struct fat32_directory_e
}
} else {
char fn[8+3];
if (fat32_filename_to_8_3(fn, name) && !strncmp(directory_entries[i].file_name_and_ext, fn, 8+3)) {
if (!fat32_filename_to_8_3(fn, name)) {
continue;
}
if (!strncmp(directory_entries[i].file_name_and_ext, fn, 8+3)) {
*file = directory_entries[i];
return 0;
}
@ -259,7 +306,8 @@ int fat32_open(struct fat32_file_handle* ret, struct volume *part, const char* p
return r;
}
struct fat32_directory_entry current_directory;
struct fat32_directory_entry _current_directory;
struct fat32_directory_entry *current_directory;
struct fat32_directory_entry current_file;
unsigned int current_index = 0;
char current_part[FAT32_LFN_MAX_FILENAME_LENGTH];
@ -270,8 +318,18 @@ int fat32_open(struct fat32_file_handle* ret, struct volume *part, const char* p
}
// walk down the directory tree
current_directory.cluster_num_low = context.root_directory_cluster & 0xFFFF;
current_directory.cluster_num_high = context.root_directory_cluster >> 16;
switch (context.type) {
case 16:
current_directory = NULL;
break;
case 32:
_current_directory.cluster_num_low = context.root_directory_cluster & 0xFFFF;
_current_directory.cluster_num_high = context.root_directory_cluster >> 16;
current_directory = &_current_directory;
break;
default:
__builtin_unreachable();
}
for (;;) {
bool expect_directory = false;
@ -293,12 +351,13 @@ int fat32_open(struct fat32_file_handle* ret, struct volume *part, const char* p
}
}
if ((r = fat32_open_in(&context, &current_directory, &current_file, current_part)) != 0) {
if ((r = fat32_open_in(&context, current_directory, &current_file, current_part)) != 0) {
return r;
}
if (expect_directory) {
current_directory = current_file;
_current_directory = current_file;
current_directory = &_current_directory;
} else {
ret->context = context;
ret->first_cluster = current_file.cluster_num_high << 16 | current_file.cluster_num_low;