#include <kernel/kernel.h> #include <kernel/driver.h> #include <kernel/fs.h> #include <errno.h> #include <stdlib.h> #include <wchar.h> #include <string.h> #include <ctype.h> #include <os/blkdev.h> #include <os/fs.h> #include "fat.h" //#define DEBUG #include <kernel/debug.h> //! \ingroup fat //@{ typedef struct fat_root_t fat_root_t; struct fat_root_t { device_t dev; device_t *disk; fat_bootsector_t boot_sector; byte *fat; byte fat_bits; dword bytes_per_cluster; dword data_start, root_start; }; typedef struct fat_file_t fat_file_t; struct fat_file_t { file_t file; fat_dirent_t entry; qword cached_pos; dword cached_cluster; }; #define FAT_AVAILABLE 0 #define FAT_RESERVED_START 0xfff0 #define FAT_RESERVED_END 0xfff6 #define FAT_BAD 0xfff7 #define FAT_EOC_START 0xfff8 #define FAT_EOC_END 0xffff #define IS_EOC_CLUSTER(c) ((c) >= FAT_EOC_START && (c) <= FAT_EOC_END) void dump(const byte* buf, size_t size) { int j; for (j = 0; j < size; j++) wprintf(L"%02x ", buf[j]); _cputws(L"\n"); } dword fatGetNextCluster(fat_root_t* root, dword cluster) { dword FATOffset; word w; byte* b; assert(cluster < root->boot_sector.sectors / root->boot_sector.sectors_per_cluster); switch (root->fat_bits) { case 12: FATOffset = cluster + cluster / 2; break; case 16: FATOffset = cluster * 2; break; } b = root->fat + FATOffset; w = *(word*) b; //dump(b - 2, 16); if (root->fat_bits == 12) { if (cluster & 1) // cluster is odd w >>= 4; else // cluster is even w &= 0xfff; if (w >= 0xff0) w |= 0xf000; } /*wprintf(L"FATOffset = 0x%x = 0x%x, w = 0x%04x = 0x%02x%02x\n", FATOffset, FATOffset + root->boot_sector.reserved_sectors * root->boot_sector.bytes_per_sector, w, b[1], b[0]);*/ return w; } bool fatLookupEntry(fat_root_t* root, dword cluster, const wchar_t* filename, bool is_root, fat_dirent_t* entry) { union { fat_dirent_t di; fat_lfnslot_t lfn; } u[512 / sizeof(fat_dirent_t)]; wchar_t name[MAX_PATH], temp[14], *nameptr; size_t length; int i, j, k; qword pos; if (is_root) cluster = 0; while (true) { if (is_root) pos = (root->root_start + cluster) * root->boot_sector.bytes_per_sector; else pos = root->data_start * root->boot_sector.bytes_per_sector + (cluster - 2) * root->bytes_per_cluster; //wprintf(L"fatLookupEntry%s: cluster = %d pos = 0x%x\n", //is_root ? L"(root)" : L"", cluster, (dword) pos); length = sizeof(u); if (devReadSync(root->disk, pos, u, &length)) { wprintf(L"fatLookupEntry: disk read failed\n"); return false; } for (j = 0; j < countof(u); j++) { if (u[j].di.name[0] == 0) { wprintf(L"fatLookupEntry: end of directory\n"); return false; } if (u[j].di.name[0] != 0xe5) { memset(name, 0, sizeof(name)); if ((u[j].di.attribs & ATTR_LONG_NAME) == 0) { for (i = 0; i < 8; i++) { if (u[j].di.name[i] == ' ') { name[i] = 0; break; } else if (iswupper(u[j].di.name[i])) name[i] = towlower(u[j].di.name[i]); else name[i] = u[j].di.name[i]; } if (u[j].di.extension[0] != ' ') { wcscat(name, L"."); k = wcslen(name); for (i = 0; i < 3; i++) { if (u[j].di.extension[i] == ' ') { name[i + k] = 0; break; } else if (iswupper(u[j].di.extension[i])) name[i + k] = towlower(u[j].di.extension[i]); else name[i + k] = u[j].di.extension[i]; } } } else { //int lfn_start = j; while ((u[j].di.attribs & ATTR_LONG_NAME) == ATTR_LONG_NAME) { //wprintf(L"<lfn seq=%2x> ", u[j].di.name[0]); if (u[j].di.name[0] != 0xe5) { memset(temp, 0, sizeof(temp)); nameptr = temp; for (i = 0; i < 5; i++) { *nameptr = u[j].lfn.name0_4[i]; nameptr++; } for (i = 0; i < 6; i++) { *nameptr = u[j].lfn.name5_10[i]; nameptr++; } for (i = 0; i < 2; i++) { *nameptr = u[j].lfn.name11_12[i]; nameptr++; } i = wcslen(temp); memmove(name + i, name, wcslen(name) * sizeof(wchar_t)); memcpy(name, temp, i * sizeof(wchar_t)); } j++; } //j = lfn_start; } if (name[0]) { TRACE2("%s\t\t%x\n", name, u[j].di.first_cluster); if (wcsicmp(name, filename) == 0) { *entry = u[j].di; TRACE2("%s: found at cluster %x\n", name, u[j].di.first_cluster); return true; } } } } if (is_root) { cluster++; if (cluster >= (root->boot_sector.num_root_entries * 32) / root->boot_sector.bytes_per_sector) { wprintf(L"fatLookupEntry(root): end of chain\n"); return false; } } else { cluster = fatGetNextCluster(root, cluster); if (IS_EOC_CLUSTER(cluster)) { wprintf(L"fatLookupEntry: end of chain\n"); return false; } } } return false; } dword fatFindCluster(fat_file_t* file, qword pos) { fat_root_t *root = (fat_root_t*) file->file.fsd; qword ptr; dword cluster; cluster = (dword) file->entry.first_cluster; //wprintf(L"fatFindCluster: first_cluster = 0x%x\n", file->entry.first_cluster); ptr = root->bytes_per_cluster; while (ptr <= pos) { if (IS_EOC_CLUSTER(cluster)) return -1; ptr += root->bytes_per_cluster; cluster = fatGetNextCluster(root, cluster); } return cluster; } bool fatOpenFile(fat_root_t* root, request_t* req) { wchar_t *ch, component[MAX_PATH]; const wchar_t* path; dword cluster; fat_file_t *fd; bool is_root; fat_dirent_t entry; path = req->params.fs_open.name + 1; cluster = 0; is_root = true; while ((ch = wcschr(path, '/'))) { wcsncpy(component, path, ch - path); path += wcslen(component) + 1; //wprintf(L"fatLookupEntry: %s @ %d\n", component, cluster); if (!fatLookupEntry(root, cluster, component, is_root, &entry)) { req->result = ENOTFOUND; return false; } is_root = false; cluster = entry.first_cluster; } if (!fatLookupEntry(root, cluster, path, is_root, &entry)) { req->result = ENOTFOUND; return false; } fd = hndAlloc(sizeof(fat_file_t), NULL); fd->file.fsd = &root->dev; fd->file.pos = 0; fd->entry = entry; fd->cached_pos = 0; fd->cached_cluster = entry.first_cluster; req->params.fs_open.fd = &fd->file; hndSignal(req->event, true); return true; } bool fatReadFile(fat_root_t* root, request_t* req) { fat_file_t* file; dword cluster, diff; qword cluster_pos, user_pos; size_t length, this_cluster; status_t hr; file = (fat_file_t*) req->params.fs_read.fd; if (file->file.pos != file->cached_pos) file->cached_cluster = fatFindCluster(file, file->file.pos); cluster = file->cached_cluster; req->user_length = req->params.fs_read.length; req->params.fs_read.length = 0; this_cluster = 0; TRACE1("[%x] ", cluster); while (req->params.fs_read.length < req->user_length) { user_pos = file->file.pos + req->params.fs_read.length; user_pos &= -root->bytes_per_cluster; diff = file->file.pos + req->params.fs_read.length - user_pos; cluster_pos = root->data_start * root->boot_sector.bytes_per_sector + (cluster - 2) * root->bytes_per_cluster + this_cluster + diff; TRACE1("(%lu) ", (unsigned long) cluster_pos); length = min(req->user_length - req->params.fs_read.length, root->bytes_per_cluster); /*if (length < root->boot_sector.bytes_per_sector) length = root->boot_sector.bytes_per_sector;*/ //wprintf(L"fatReadFile: pos = %d cluster = 0x%x length = %d\n", //(dword) file->file.pos, //cluster, //length); hr = devReadSync(root->disk, cluster_pos, (byte*) req->params.fs_read.buffer + req->params.fs_read.length, &length); if (hr || length == 0) { wprintf(L"fatReadFile: disk read failed at %u\n", (unsigned long) cluster_pos); req->result = hr; file->cached_pos = file->file.pos; file->cached_cluster = cluster; return false; } req->params.fs_read.length += length; file->file.pos += length; if (IS_EOC_CLUSTER(cluster)) break; this_cluster += length; if (this_cluster >= root->bytes_per_cluster) { cluster = fatGetNextCluster(root, cluster); this_cluster -= root->bytes_per_cluster; } TRACE2("read %d bytes; next cluster = %x\n", length, cluster); } //wprintf(L"fat: finished read\n"); file->cached_pos = file->file.pos; file->cached_cluster = cluster; hndSignal(req->event, true); return true; } bool fatRequest(device_t* dev, request_t* req) { fat_root_t* root = (fat_root_t*) dev; switch (req->code) { case FS_CLOSE: hndFree(req->params.fs_close.fd); case DEV_OPEN: case DEV_CLOSE: hndSignal(req->event, true); return true; case FS_OPEN: return fatOpenFile(root, req); case FS_READ: return fatReadFile(root, req); case FS_GETLENGTH: { fat_file_t *fd = (fat_file_t*) req->params.fs_getlength.fd; req->params.fs_getlength.length = fd->entry.file_length; hndSignal(req->event, true); return true; } } req->code = ENOTIMPL; return false; } device_t* fatMountFs(driver_t* driver, const wchar_t* path, device_t* dev) { fat_root_t *root; size_t length; dword RootDirSectors, FatSectors; //byte* temp; block_size_t size; request_t req; root = hndAlloc(sizeof(fat_root_t), NULL); root->dev.driver = driver; root->dev.request = fatRequest; root->disk = dev; size.total_blocks = 0; req.code = BLK_GETSIZE; req.params.buffered.buffer = (addr_t) &size; req.params.buffered.length = sizeof(size); if (devRequestSync(root->disk, &req) != 0 || size.total_blocks > 20740) { TRACE1("Total blocks = %d, using FAT16\n", size.total_blocks); root->fat_bits = 16; } else { TRACE1("Total blocks = %d, using FAT12\n", size.total_blocks); root->fat_bits = 12; } length = sizeof(fat_bootsector_t); if (devReadSync(root->disk, 0, &root->boot_sector, &length) || length < sizeof(fat_bootsector_t)) { hndFree(root); return NULL; } assert(root->boot_sector.sectors_per_fat != 0); assert(root->boot_sector.bytes_per_sector != 0); root->bytes_per_cluster = root->boot_sector.bytes_per_sector * root->boot_sector.sectors_per_cluster; root->fat = malloc(root->boot_sector.sectors_per_fat * root->boot_sector.bytes_per_sector); assert(root->fat != NULL); TRACE2("FAT starts at sector %d = 0x%x\n", root->boot_sector.reserved_sectors, root->boot_sector.reserved_sectors * root->boot_sector.bytes_per_sector); length = root->boot_sector.sectors_per_fat * root->boot_sector.bytes_per_sector; if (devReadSync(root->disk, root->boot_sector.reserved_sectors * root->boot_sector.bytes_per_sector, root->fat, &length)) { free(root->fat); hndFree(root); return NULL; } RootDirSectors = (root->boot_sector.num_root_entries * 32) / root->boot_sector.bytes_per_sector; FatSectors = root->boot_sector.num_fats * root->boot_sector.sectors_per_fat; root->data_start = root->boot_sector.reserved_sectors + FatSectors + RootDirSectors; root->root_start = root->boot_sector.reserved_sectors + root->boot_sector.hidden_sectors + root->boot_sector.sectors_per_fat * root->boot_sector.num_fats; /*length = root->boot_sector.num_root_entries * 32; temp = malloc(length); devReadSync(root->disk, root->root_start * root->boot_sector.bytes_per_sector, temp, &length); free(temp);*/ return &root->dev; } bool STDCALL drvInit(driver_t* drv) { drv->add_device = NULL; drv->mount_fs = fatMountFs; return true; } //@}