177 lines
5.5 KiB
C
177 lines
5.5 KiB
C
/*
|
|
* Copyright (c) 2015 Steve White
|
|
*
|
|
* Use of this source code is governed by a MIT-style
|
|
* license that can be found in the LICENSE file or at
|
|
* https://opensource.org/licenses/MIT
|
|
*/
|
|
|
|
#include <lk/err.h>
|
|
#include <lib/bio.h>
|
|
#include <lib/fs.h>
|
|
#include <lk/trace.h>
|
|
#include <lk/debug.h>
|
|
#include <malloc.h>
|
|
#include <string.h>
|
|
#include <endian.h>
|
|
|
|
#include "fat32_priv.h"
|
|
#include "fat_fs.h"
|
|
|
|
static void fat32_dump(fat_fs_t *fat) {
|
|
printf("bytes_per_sector=%i\n", fat->bytes_per_sector);
|
|
printf("sectors_per_cluster=%i\n", fat->sectors_per_cluster);
|
|
printf("bytes_per_cluster=%i\n", fat->bytes_per_cluster);
|
|
printf("reserved_sectors=%i\n", fat->reserved_sectors);
|
|
printf("fat_bits=%i\n", fat->fat_bits);
|
|
printf("fat_count=%i\n", fat->fat_count);
|
|
printf("sectors_per_fat=%i\n", fat->sectors_per_fat);
|
|
printf("total_sectors=%i\n", fat->total_sectors);
|
|
printf("active_fat=%i\n", fat->active_fat);
|
|
printf("data_start=%i\n", fat->data_start);
|
|
printf("total_clusters=%i\n", fat->total_clusters);
|
|
printf("root_cluster=%i\n", fat->root_cluster);
|
|
printf("root_entries=%i\n", fat->root_entries);
|
|
printf("root_start=%i\n", fat->root_start);
|
|
}
|
|
|
|
status_t fat32_mount(bdev_t *dev, fscookie **cookie) {
|
|
status_t result = NO_ERROR;
|
|
|
|
if (!dev)
|
|
return ERR_NOT_VALID;
|
|
|
|
uint8_t *bs = malloc(512);
|
|
int err = bio_read(dev, bs, 1024, 512);
|
|
if (err < 0) {
|
|
result = ERR_GENERIC;
|
|
goto end;
|
|
}
|
|
|
|
if (((bs[0x1fe] != 0x55) || (bs[0x1ff] != 0xaa)) && (bs[0x15] == 0xf8)) {
|
|
printf("missing boot signature\n");
|
|
result = ERR_NOT_VALID;
|
|
goto end;
|
|
}
|
|
|
|
fat_fs_t *fat = malloc(sizeof(fat_fs_t));
|
|
fat->lba_start = 1024;
|
|
fat->dev = dev;
|
|
|
|
fat->bytes_per_sector = fat_read16(bs,0xb);
|
|
if ((fat->bytes_per_sector != 0x200) && (fat->bytes_per_sector != 0x400) && (fat->bytes_per_sector != 0x800)) {
|
|
printf("unsupported sector size (%x)\n", fat->bytes_per_sector);
|
|
result = ERR_NOT_VALID;
|
|
goto end;
|
|
}
|
|
|
|
fat->sectors_per_cluster = bs[0xd];
|
|
switch (fat->sectors_per_cluster) {
|
|
case 1:
|
|
case 2:
|
|
case 4:
|
|
case 8:
|
|
case 0x10:
|
|
case 0x20:
|
|
case 0x40:
|
|
case 0x80:
|
|
break;
|
|
default:
|
|
printf("unsupported sectors/cluster (%x)\n", fat->sectors_per_cluster);
|
|
result = ERR_NOT_VALID;
|
|
goto end;
|
|
}
|
|
|
|
fat->reserved_sectors = fat_read16(bs, 0xe);
|
|
fat->fat_count = bs[0x10];
|
|
|
|
if ((fat->fat_count == 0) || (fat->fat_count > 8)) {
|
|
printf("unreasonable FAT count (%x)\n", fat->fat_count);
|
|
result = ERR_NOT_VALID;
|
|
goto end;
|
|
}
|
|
|
|
if (bs[0x15] != 0xf8) {
|
|
printf("unsupported media descriptor byte (%x)\n", bs[0x15]);
|
|
result = ERR_NOT_VALID;
|
|
goto end;
|
|
}
|
|
|
|
fat->sectors_per_fat = fat_read16(bs, 0x16);
|
|
if (fat->sectors_per_fat == 0) {
|
|
fat->fat_bits = 32;
|
|
fat->sectors_per_fat = fat_read32(bs,0x24);
|
|
fat->total_sectors = fat_read32(bs,0x20);
|
|
fat->active_fat = (bs[0x28] & 0x80) ? 0 : (bs[0x28] & 0xf);
|
|
fat->data_start = fat->reserved_sectors + (fat->fat_count * fat->sectors_per_fat);
|
|
fat->total_clusters = (fat->total_sectors - fat->data_start) / fat->sectors_per_cluster;
|
|
|
|
// In FAT32, root directory appears in data area on given cluster and can be a cluster chain.
|
|
fat->root_cluster = fat_read32(bs,0x2c);
|
|
fat->root_start = 0;
|
|
if (fat->root_cluster >= fat->total_clusters) {
|
|
printf("root cluster too large (%x > %x)\n", fat->root_cluster, fat->total_clusters);
|
|
result = ERR_NOT_VALID;
|
|
goto end;
|
|
}
|
|
fat->root_entries = 0;
|
|
} else {
|
|
if (fat->fat_count != 2) {
|
|
printf("illegal FAT count (%x)\n", fat->fat_count);
|
|
result = ERR_NOT_VALID;
|
|
goto end;
|
|
}
|
|
|
|
// On a FAT 12 or FAT 16 volumes the root directory is at a fixed position immediately after the File Allocation Tables
|
|
fat->root_cluster = 0;
|
|
fat->root_entries = fat_read16(bs,0x11);
|
|
if (fat->root_entries % (fat->bytes_per_sector / 0x20)) {
|
|
printf("illegal number of root entries (%x)\n", fat->root_entries);
|
|
result = ERR_NOT_VALID;
|
|
goto end;
|
|
}
|
|
|
|
fat->total_sectors = fat_read16(bs,0x13);
|
|
if (fat->total_sectors == 0) {
|
|
fat->total_sectors = fat_read32(bs,0x20);
|
|
}
|
|
|
|
fat->root_start = fat->reserved_sectors + fat->fat_count * fat->sectors_per_fat;
|
|
fat->data_start = fat->root_start + fat->root_entries * 0x20 / fat->bytes_per_sector;
|
|
fat->total_clusters = (fat->total_sectors - fat->data_start) / fat->sectors_per_cluster;
|
|
|
|
if (fat->total_clusters < 0xff2) {
|
|
printf("small FAT12, not supported\n");
|
|
result = ERR_NOT_VALID;
|
|
goto end;
|
|
}
|
|
fat->fat_bits = 16;
|
|
}
|
|
|
|
fat->bytes_per_cluster = fat->sectors_per_cluster * fat->bytes_per_sector;
|
|
fat->cache = bcache_create(fat->dev, fat->bytes_per_sector, 4);
|
|
|
|
*cookie = (fscookie *)fat;
|
|
end:
|
|
free(bs);
|
|
return result;
|
|
}
|
|
|
|
status_t fat32_unmount(fscookie *cookie) {
|
|
fat_fs_t *fat = (fat_fs_t *)cookie;
|
|
bcache_destroy(fat->cache);
|
|
free(fat);
|
|
return NO_ERROR;
|
|
}
|
|
|
|
static const struct fs_api fat32_api = {
|
|
.mount = fat32_mount,
|
|
.unmount = fat32_unmount,
|
|
.open = fat32_open_file,
|
|
.stat = fat32_stat_file,
|
|
.read = fat32_read_file,
|
|
.close = fat32_close_file,
|
|
};
|
|
|
|
STATIC_FS_IMPL(fat32, &fat32_api);
|