quinn-os/fat.c
2015-08-24 16:50:16 +10:00

1285 lines
34 KiB
C

#include "vfs.h"
#include "fat.h"
#include "memory.h"
#include "console.h"
int fat_load_superblock(struct vfs_device_t *device) {
unsigned char *buffer;
struct fat_data *fatData;
buffer = (char *)malloc(512);
if (!buffer) {
return 0;
}
fatData = (struct fat_data *)malloc(sizeof(struct fat_data));
if (!fatData) {
free(buffer);
return 0;
}
switch (device->device) {
case 1:
ramdisk_read_block(0, buffer, 512);
break;
}
memcpy(&fatData->superblock, buffer, sizeof(struct BPB));
if (buffer[510] == 0x55 && buffer[511] == 0xAA) {
free(buffer);
if (fatData->superblock.BPB_FATSz16 != 0) {
fatData->fat_size = fatData->superblock.BPB_FATSz16;
} else {
fatData->fat_size = fatData->superblock.FatType.Fat32.BPB_FATSz32;
}
fatData->root_dir_sectors = ((fatData->superblock.BPB_RootEntCnt * 32) + (fatData->superblock.BPB_BytesPerSec - 1)) / fatData->superblock.BPB_BytesPerSec;
fatData->first_data_sec = fatData->superblock.BPB_RsvdSecCnt + (fatData->superblock.BPB_NumFATs * fatData->fat_size) + fatData->root_dir_sectors;
if (fatData->superblock.BPB_TotSec16 != 0) {
fatData->totsec = fatData->superblock.BPB_TotSec16;
} else {
fatData->totsec = fatData->superblock.BPB_TotSec32;
}
fatData->data_sec = fatData->totsec - (fatData->superblock.BPB_RsvdSecCnt + (fatData->superblock.BPB_NumFATs * fatData->fat_size) + fatData->root_dir_sectors);
fatData->count_of_clusters = fatData->data_sec / fatData->superblock.BPB_SecPerClus;
if (fatData->count_of_clusters < 4085) {
fatData->fat = 0;
kprintf("FAT FS: Fat12\n");
} else if (fatData->count_of_clusters < 65525) {
fatData->fat = 1;
kprintf("FAT FS: Fat16\n");
} else {
fatData->fat = 2;
kprintf("FAT FS: Fat32\n");
}
device->fs_data = fatData;
return 1;
}
kprintf("FAT FS: Unrecognised Filesystem.\n");
free(buffer);
return 0;
}
unsigned int cluster_to_sec(struct vfs_device_t *device, unsigned int cluster) {
struct fat_data *data = (struct fat_data *)device->fs_data;
return (cluster - 2) * data->superblock.BPB_SecPerClus + data->first_data_sec;
}
unsigned int fat_get_cluster_entry(struct vfs_device_t *device, unsigned int cluster) {
struct fat_data *data = (struct fat_data *)device->fs_data;
unsigned int fatOffset;
if (data->fat != 0) {
if (data->fat == 1) {
fatOffset = cluster * 2;
} else if (data->fat == 2) {
fatOffset = cluster * 4;
}
unsigned int sector = data->superblock.BPB_RsvdSecCnt + (fatOffset / data->superblock.BPB_BytesPerSec);
unsigned int offset = fatOffset % data->superblock.BPB_BytesPerSec;
char *buffer = (char *)malloc(data->superblock.BPB_BytesPerSec);
if (!buffer) {
// fail.
}
switch (device->device) {
case 1:
ramdisk_read_block(sector, buffer, data->superblock.BPB_BytesPerSec);
break;
}
if (data->fat == 1) {
unsigned short fat16clusterEntryVal = *((unsigned short *)&buffer[offset]);
return (unsigned int)fat16clusterEntryVal;
} else if (data->fat == 2) {
unsigned int fat32clusterEntryVal = *((unsigned int *)&buffer[offset]);
return fat32clusterEntryVal;
}
} else {
fatOffset = cluster + (cluster / 2);
unsigned int sector = data->superblock.BPB_RsvdSecCnt + (fatOffset / data->superblock.BPB_BytesPerSec);
unsigned int offset = fatOffset % data->superblock.BPB_BytesPerSec;
char *buffer = (char *)malloc(data->superblock.BPB_BytesPerSec * 2);
if (!buffer) {
// fail
}
switch (device->device) {
case 1:
ramdisk_read_blocks(sector, 2, buffer, data->superblock.BPB_BytesPerSec);
break;
}
unsigned short fat12clusterEntryVal = *((unsigned short *)&buffer[offset]);
if (cluster & 0x0001) {
fat12clusterEntryVal = fat12clusterEntryVal >> 4;
} else {
fat12clusterEntryVal = fat12clusterEntryVal & 0x0fff;
}
return (unsigned int)fat12clusterEntryVal;
}
}
unsigned int fat_get_offset_for_cluster(struct vfs_device_t *device, unsigned int cluster) {
struct fat_data *data = (struct fat_data *)device->fs_data;
}
unsigned char is_EOC(struct vfs_device_t *device, unsigned int content) {
struct fat_data *data = (struct fat_data *)device->fs_data;
if (data->fat == 0) {
if (content >= 0x0ff8) {
return 1;
}
} else if (data->fat == 1) {
if (content >= 0xfff8) {
return 1;
}
} else if (data->fat == 2) {
if (content >= 0x0ffffff8) {
return 1;
}
}
return 0;
}
unsigned int fat_read_entire_file(struct vfs_device_t *device, unsigned int start_cluster, char **buffer) {
struct fat_data *data = (struct fat_data *)device->fs_data;
unsigned int *clusterchain = (unsigned int *)malloc(sizeof(unsigned int) * 2);
clusterchain[0] = start_cluster;
clusterchain[1] = fat_get_cluster_entry(device, start_cluster);
int i = 1;
while (!is_EOC(device, clusterchain[i])) {
i++;
clusterchain = (unsigned int *)realloc(clusterchain, sizeof(unsigned int) * (i + 1));
clusterchain[i] = fat_get_cluster_entry(device, clusterchain[i-1]);
}
int len = (i + 1) * data->superblock.BPB_SecPerClus * data->superblock.BPB_BytesPerSec;
*buffer = (char *)malloc(len);
unsigned int first_sec = cluster_to_sec(device, start_cluster);
int j;
char *buffer_loc;
for (j=0;j<i;j++) {
switch(device->device) {
case 1:
buffer_loc = *buffer;
ramdisk_read_blocks(cluster_to_sec(device, clusterchain[j]), data->superblock.BPB_SecPerClus, &buffer_loc[j * data->superblock.BPB_SecPerClus * data->superblock.BPB_BytesPerSec], data->superblock.BPB_BytesPerSec);
break;
}
}
free(clusterchain);
return len;
}
unsigned char fat_long_dir_chksum(unsigned char *shortfname) {
short FcbNameLen;
unsigned char Sum;
Sum = 0;
for (FcbNameLen=11;FcbNameLen != 0; FcbNameLen--) {
Sum = ((Sum & 1) ? 0x80 : 0) + (Sum >> 1) + *shortfname++;
}
return Sum;
}
struct fat_file_info *fat_find_sub_entry(struct vfs_device_t *device, unsigned int start_cluster, char **ptr, int part, int parts, int type) {
struct fat_data *data = (struct fat_data *)device->fs_data;
char *buffer;
int len;
int i;
int j;
char temp[13];
char long_filename[256];
unsigned char chksum = 0;
int k = 0;
int long_name_len = 0;
memset(long_filename, 0, 256);
len = fat_read_entire_file(device, start_cluster, &buffer);
for (i=0;i<len / 32;i++) {
struct fat_dir *dir = (struct fat_dir *)&buffer[i * 32];
if (dir->DIR_Attribute & ATTR_LONG_NAME) {
struct fat_long_dir *ldir = (struct fat_long_dir *)dir;
if (ldir->LDIR_Ord & 0x40) {
memset(long_filename, 0, 256, 0);
long_name_len = 0;
chksum = ldir->LDIR_Chksum;
// last entry
// check length
for (k=0;k<5;k++) {
if (ldir->LDIR_Name1[k] == 0x0000) {
long_name_len = 13 * (ldir->LDIR_Ord & ~(0x40) - 1) + k;
break;
}
}
if (!long_name_len) {
for (k=0;k<6;k++) {
if (ldir->LDIR_Name2[k] == 0x0000) {
long_name_len = 13 * (ldir->LDIR_Ord & ~(0x40) - 1) + k + 5;
break;
}
}
}
if (!long_name_len) {
for (k=0;k<2;k++) {
if (ldir->LDIR_Name3[k] == 0x0000) {
long_name_len = 13 * (ldir->LDIR_Ord & ~(0x40) - 1) + k + 5 + 6;
break;
}
}
}
int offset = 13 * (ldir->LDIR_Ord & ~(0x40) - 1);
for (k=0;k<5;k++) {
long_filename[offset + k] = (char)(ldir->LDIR_Name1[k] & 0xFF);
}
for (k=0;k<6;k++) {
long_filename[offset + 5 + k] = (char)(ldir->LDIR_Name2[k] & 0xFF);
}
for (k=0;k<2;k++) {
long_filename[offset + 11 + k] = (char)(ldir->LDIR_Name3[k] & 0xFF);
}
} else {
int offset = 13 * (ldir->LDIR_Ord - 1);
for (k=0;k<5;k++) {
long_filename[offset + k] = (char)(ldir->LDIR_Name1[k] & 0xFF);
}
for (k=0;k<6;k++) {
long_filename[offset + 5 + k] = (char)(ldir->LDIR_Name2[k] & 0xFF);
}
for (k=0;k<2;k++) {
long_filename[offset + 11 + k] = (char)(ldir->LDIR_Name3[k] & 0xFF);
}
}
} else {
memset(temp, 0, 13);
if (dir->DIR_Name[0] == 0xE5) {
continue;
}
if (dir->DIR_Name[0] == 0x00) {
break;
}
if (dir->DIR_Name[0] == 0x05) {
temp[0] = 0xE5;
} else {
temp[0] = dir->DIR_Name[0];
}
for (j=1;j<8;j++) {
if ( dir->DIR_Name[j] == ' ') {
break;
} else {
temp[j] = dir->DIR_Name[j];
}
}
if (dir->DIR_Name[8] != ' ') {
temp[8] = '.';
for (j=8;j<11;j++) {
if (dir->DIR_Name[j] == ' ') {
break;
} else {
temp[j + 1] == dir->DIR_Name[j];
}
}
}
if (parts == part + 1) {
if (chksum != fat_long_dir_chksum(dir->DIR_Name)) {
memset(long_filename, 0, 256);
}
if (strcmp(ptr[part], long_filename) == 0 || strcmp(ptr[part], temp) == 0) {
if (type == 0 && (dir->DIR_Attribute & ATTR_DIRECTORY)) {
free(buffer);
struct fat_file_info *info = (struct fat_file_info *)malloc(sizeof(struct fat_file_info));
if (!info) {
return NULL;
}
info->start_cluster = (dir->DIR_FirstClustHi << 0xffff) | dir->DIR_FirstClustLo;
info->file_size = dir->DIR_FileSize;
info->type = 0;
unsigned int thetime = get_time_since_epoch((dir->DIR_WrtDate & 0x7F) + 80, (dir->DIR_WrtDate >> 7) & 0xF, (dir->DIR_WrtDate >> 11) & 0x1F, dir->DIR_WrtTime & 0xF, (dir->DIR_WrtTime >> 4) & 0x3F, ((dir->DIR_WrtTime >> 11) & 0x1F) * 2);
info->atime = thetime;
info->ctime = thetime;
info->mtime = thetime;
return info;
} else if (type == 1 && !(dir->DIR_Attribute & ATTR_DIRECTORY)) {
free(buffer);
struct fat_file_info *info = (struct fat_file_info *)malloc(sizeof(struct fat_file_info));
if (!info) {
return NULL;
}
info->start_cluster = (dir->DIR_FirstClustHi << 0xffff) | dir->DIR_FirstClustLo;
info->file_size = dir->DIR_FileSize;
info->type = 1;
unsigned int thetime = get_time_since_epoch((dir->DIR_WrtDate & 0x7F) + 80, (dir->DIR_WrtDate >> 7) & 0xF, (dir->DIR_WrtDate >> 11) & 0x1F, dir->DIR_WrtTime & 0xF, (dir->DIR_WrtTime >> 4) & 0x3F, ((dir->DIR_WrtTime >> 11) & 0x1F) * 2);
info->atime = thetime;
info->ctime = thetime;
info->mtime = thetime;
return info;
} else if (type == -1){
free(buffer);
struct fat_file_info *info = (struct fat_file_info *)malloc(sizeof(struct fat_file_info));
if (!info) {
return NULL;
}
if (!(dir->DIR_Attribute & ATTR_DIRECTORY)) {
info->type = 1;
} else {
info->type = 0;
}
info->start_cluster = (dir->DIR_FirstClustHi << 0xffff) | dir->DIR_FirstClustLo;
info->file_size = dir->DIR_FileSize;
unsigned int thetime = get_time_since_epoch((dir->DIR_WrtDate & 0x7F) + 80, (dir->DIR_WrtDate >> 7) & 0xF, (dir->DIR_WrtDate >> 11) & 0x1F, dir->DIR_WrtTime & 0xF, (dir->DIR_WrtTime >> 4) & 0x3F, ((dir->DIR_WrtTime >> 11) & 0x1F) * 2);
info->atime = thetime;
info->ctime = thetime;
info->mtime = thetime;
return info;
} else {
return NULL;
}
}
} else {
if (chksum != fat_long_dir_chksum(dir->DIR_Name)) {
memset(long_filename, 0, 256);
}
if (strcmp(ptr[part], long_filename) == 0 || strcmp(ptr[part], temp) == 0) {
unsigned int start_cluster = (dir->DIR_FirstClustHi << 0xffff) | dir->DIR_FirstClustLo;
if (dir->DIR_Attribute & ATTR_DIRECTORY) {
free(buffer);
return fat_find_sub_entry(device, start_cluster, ptr, part + 1, parts, type);
} else {
free(buffer);
return NULL;
}
}
}
}
}
return NULL;
}
#if 0
int fat_create_dentry(struct vfs_device_t *device, char *path) {
char *path_copy;
char *directory;
char *basename;
char *buffer;
int i;
char shortname[11];
memset(shortname, ' ', 11);
path_copy = (char *)malloc(strlen(path) + 1);
strcpy(path_copy, path);
directory = path_copy;
for (i=strlen(path_copy) -1;i>=0;i--) {
if (path_copy[i] == '/') {
paht_copy[i] = '\0';
basename = &path_copy[i+1];
break;
}
}
struct fat_file_info *info = fat_check_if_exists(device, directory, 0);
if (info == NULL) {
return 0;
}
if (directory[0] == '/' && directory[1] == '\0') {
if (data->fat != 2) {
first_root_dir_sec = data->superblock.BPB_RsvdSecCnt + (data->superblock.BPB_NumFATs * data->superblock.BPB_FATSz16);
len = data->root_dir_sectors * data->superblock.BPB_BytesPerSec;
buffer = (char *)malloc(len);
switch(device->device) {
case 1:
ramdisk_read_blocks(first_root_dir_sec, data->root_dir_sectors, buffer, data->superblock.BPB_BytesPerSec);
break;
}
} else {
len = fat_read_entire_file(device, data->superblock.FatType.Fat32.BPB_RootClus, &buffer);
}
} else {
unsigned int cluster = info->start_cluster;
len = fat_read_entire_file(device, cluster, &buffer);
}
int tail = 1;
int highest_tail = 1;
int j;
int k;
for (i=0;i<len / 32;i++) {
struct fat_dir *dir = (struct fat_dir *)&buffer[i * 32];
if (!(dir->DIR_Attribute & ATTR_LONG_NAME)) {
for (j=0;j<8;j++) {
if (dir->DIR_Name[j] == '~') {
break;
}
}
if (j==8) {
continue;
}
for (k=0;k<j;k++) {
if (dir->DIR_Name[k] != toupper(basename[k])) {
break;
}
}
if (k!=j) {
continue;
}
int thetail = 0;
for (k=j+1;k<8;j++) {
thetail = thetail * 10 + (dir->DIR_Name[k] - '0');
}
if (thetail == tail) {
tail = highest_tail + 1;
}
if (thetail > highest_tail) {
highest_tail = thetail;
}
}
}
int nent = (basename / 13) + 1;
if (basename % 13 != 0) {
nent ++;
}
int n = 0;
int offset = -1;
for (i=0;i<len / 32;i++) {
struct fat_dir *dir = (struct fat_dir *)&buffer[i * 32];
if (dir->DIR_Name[0] == 0xE5 || dir->DIR_Name[0] == 0x00) {
if (offset == -1) {
offset = i;
}
n++;
if (n == nent) {
break;
}
} else {
n = 0;
offset = -1;
}
}
int extpos = strlen(basename);
for (i=strlen(basename) - 1;i > 0; i--) {
if (basename[i] == '.') {
extpos = i;
for (j=i+1;j<strlen(basename) && j < i + 4;j++) {
shortname[j-(i+1) + 9] = toupper(basename[j]);
}
break;
}
}
for (i=0;i<extpos && i < 8;i++) {
shortname[i] = toupper(basename[i]);
}
for (i=8;i>1;i--) {
shortname[i] = tail % 10
tail = tail / 10;
if (tail == 0) {
shortname[i-1] = '~';
break;
}
}
if (offset != -1) {
// found empty dentrys
int count = basename / 13;
if (basename % 13) {
count ++;
}
for (i=0;i<count;i++) {
struct fat_dir *dir = (struct fat_long_dir *)&buffer[(offset + i) * 32];
dir->LDIR_Ord = i;
dir->LDIR_Name1[0] = basename[i];
if ((i * 13) +1 > strlen(basename)) {
dir->LDIR_Name1[1] = '\0';
} else {
dir->LDIR_Name1[1] = basename[(i * 13)+1];
}
if ((i * 13)+2 > strlen(basename)) {
dir->LDIR_Name1[2] = '\0';
} else {
dir->LDIR_Name1[2] = basename[(i * 13)+2];
}
if ((i * 13)+3 > strlen(basename)) {
dir->LDIR_Name1[3] = '\0';
} else {
dir->LDIR_Name1[3] = basename[(i * 13)+3];
}
if ((i * 13)+4 > strlen(basename)) {
dir->LDIR_Name1[4] = '\0';
} else {
dir->LDIR_Name1[4] = basename[(i * 13)+4];
}
dir->LDIR_Attr = ATTR_LONG_NAME;
dir->LDIR_Type = 0;
dir->LDIR_Chksum = fat_long_dir_chksum(shortname);
if ((i * 13)+5 > strlen(basename)) {
dir->LDIR_Name2[0] = '\0';
} else {
dir->LDIR_Name2[0] = basename[(i * 13)+5];
}
if ((i * 13)+6 > strlen(basename)) {
dir->LDIR_Name2[1] = '\0';
} else {
dir->LDIR_Name2[1] = basename[(i * 13)+6];
}
if ((i * 13)+7 > strlen(basename)) {
dir->LDIR_Name2[2] = '\0';
} else {
dir->LDIR_Name2[2] = basename[(i * 13)+7];
}
if ((i * 13)+8 > strlen(basename)) {
dir->LDIR_Name2[3] = '\0';
} else {
dir->LDIR_Name2[3] = basename[(i * 13)+8];
}
if ((i * 13)+9 > strlen(basename)) {
dir->LDIR_Name2[4] = '\0';
} else {
dir->LDIR_Name2[4] = basename[(i * 13)+9];
}
if ((i * 13)+10 > strlen(basename)) {
dir->LDIR_Name2[5] = '\0';
} else {
dir->LDIR_Name2[5] = basename[(i * 13)+10];
}
dir->LDIR_FirstClustLo = 0;
if ((i * 13)+11 > strlen(basename)) {
dir->LDIR_Name3[0] = '\0';
} else {
dir->LDIR_Name3[0] = basename[(i * 13)+11];
if ((i * 13)+12 > strlen(basename)) {
dir->LDIR_Name3[1] = '\0';
} else {
dir->LDIR_Name3[1] = basename[(i * 13)+12];
}
}
struct fat_dir *dir = (struct fat_dir *)&buffer[(offset + count) * 32];
memcpy(dir->DIR_Name, shortname, 11);
dir->DIR_Attribute = 0;
dir->DIR_NTRes = 0;
dir->DIR_CrtTimeTenth = 0;
dir->DIR_FirstClustHi
} else {
// need to append dentry
}
}
#endif
struct fat_file_info *fat_check_if_exists(struct vfs_device_t *device, char *path, int type) {
unsigned int first_root_dir_sec;
struct fat_data *data = (struct fat_data *)device->fs_data;
char *buffer;
int len;
if (data->fat != 2) {
first_root_dir_sec = data->superblock.BPB_RsvdSecCnt + (data->superblock.BPB_NumFATs * data->superblock.BPB_FATSz16);
len = data->root_dir_sectors * data->superblock.BPB_BytesPerSec;
if (strcmp(path, "/") == 0) {
struct fat_file_info *info = (struct fat_file_info *)malloc(sizeof(struct fat_file_info));
if (!info) {
return NULL;
}
info->start_cluster = 0;
info->file_size = 0;
info->type = 0;
return info;
}
buffer = (char *)malloc(len);
switch(device->device) {
case 1:
ramdisk_read_blocks(first_root_dir_sec, data->root_dir_sectors, buffer, data->superblock.BPB_BytesPerSec);
break;
}
} else {
len = fat_read_entire_file(device, data->superblock.FatType.Fat32.BPB_RootClus, &buffer);
if (strcmp(path, "/") == 0) {
struct fat_file_info *info = (struct fat_file_info *)malloc(sizeof(struct fat_file_info));
if (!info) {
return NULL;
}
info->start_cluster = data->superblock.FatType.Fat32.BPB_RootClus;
info->file_size = len;
info->type = 0;
free(buffer);
return info;
}
}
// buffer contains the root directory contents...
int i;
int parts = 0;
char *path_copy = (char *)malloc(strlen(path) + 1);
strcpy(path_copy, path);
if (path_copy[strlen(path_copy) - 1] == '/') {
path[strlen(path_copy) - 1] = '\0';
}
parts = 1;
for (i=1;i<strlen(path_copy);i++) {
if (path_copy[i] == '/') {
parts++;
}
}
char **ptr = (char **)malloc(sizeof(char *) * parts );
ptr[0] = &path_copy[1];
int j = 0;
int k = strlen(path_copy);
for (i=1;i<k;i++) {
if (path_copy[i] == '/') {
ptr[++j] = &path_copy[i+1];
path_copy[i] = '\0';
}
}
char temp[13];
char long_filename[256];
unsigned char chksum = 0;
k = 0;
int long_name_len = 0;
memset(long_filename, 0, 256);
for (i=0;i<len / 32;i++) {
memset(temp, 0, 13);
struct fat_dir *dir = (struct fat_dir *)&buffer[i * 32];
if (dir->DIR_Attribute & ATTR_LONG_NAME) {
struct fat_long_dir *ldir = (struct fat_long_dir *)dir;
if (ldir->LDIR_Ord & 0x40) {
memset(long_filename, 0, 256, 0);
long_name_len = 0;
chksum = ldir->LDIR_Chksum;
// last entry
// check length
for (k=0;k<5;k++) {
if (ldir->LDIR_Name1[k] == 0x0000) {
long_name_len = 13 * (ldir->LDIR_Ord & ~(0x40) - 1) + k;
break;
}
}
if (!long_name_len) {
for (k=0;k<6;k++) {
if (ldir->LDIR_Name2[k] == 0x0000) {
long_name_len = 13 * (ldir->LDIR_Ord & ~(0x40) - 1) + k + 5;
break;
}
}
}
if (!long_name_len) {
for (k=0;k<2;k++) {
if (ldir->LDIR_Name3[k] == 0x0000) {
long_name_len = 13 * (ldir->LDIR_Ord & ~(0x40) - 1) + k + 5 + 6;
break;
}
}
}
int offset = 13 * (ldir->LDIR_Ord & ~(0x40) - 1);
for (k=0;k<5;k++) {
long_filename[offset + k] = (char)(ldir->LDIR_Name1[k] & 0xFF);
}
for (k=0;k<6;k++) {
long_filename[offset + 5 + k] = (char)(ldir->LDIR_Name2[k] & 0xFF);
}
for (k=0;k<2;k++) {
long_filename[offset + 11 + k] = (char)(ldir->LDIR_Name3[k] & 0xFF);
}
} else {
int offset = 13 * (ldir->LDIR_Ord - 1);
for (k=0;k<5;k++) {
long_filename[offset + k] = (char)(ldir->LDIR_Name1[k] & 0xFF);
}
for (k=0;k<6;k++) {
long_filename[offset + 5 + k] = (char)(ldir->LDIR_Name2[k] & 0xFF);
}
for (k=0;k<2;k++) {
long_filename[offset + 11 + k] = (char)(ldir->LDIR_Name3[k] & 0xFF);
}
}
} else {
if (dir->DIR_Name[0] == 0xE5) {
continue;
}
if (dir->DIR_Name[0] == 0x00) {
break;
}
if (dir->DIR_Name[0] == 0x05) {
temp[0] = 0xE5;
} else {
temp[0] = dir->DIR_Name[0];
}
for (j=1;j<8;j++) {
if ( dir->DIR_Name[j] == ' ') {
break;
} else {
temp[j] = dir->DIR_Name[j];
}
}
if (dir->DIR_Name[8] != ' ') {
temp[8] = '.';
for (j=8;j<11;j++) {
if (dir->DIR_Name[j] == ' ') {
break;
} else {
temp[j + 1] = dir->DIR_Name[j];
}
}
}
if (parts == 1) {
if (chksum != fat_long_dir_chksum(dir->DIR_Name)) {
memset(long_filename, 0, 256);
}
if (strcmp(ptr[0], long_filename) == 0 || strcmp(ptr[0], temp) == 0) {
if (type == 0 && (dir->DIR_Attribute & ATTR_DIRECTORY)) {
free(buffer);
struct fat_file_info *info = (struct fat_file_info *)malloc(sizeof(struct fat_file_info));
if (!info) {
return NULL;
}
info->start_cluster = (dir->DIR_FirstClustHi << 0xffff) | dir->DIR_FirstClustLo;
info->file_size = dir->DIR_FileSize;
info->type = 0;
unsigned int thetime = get_time_since_epoch((dir->DIR_WrtDate & 0x7F) + 80, (dir->DIR_WrtDate >> 7) & 0xF, (dir->DIR_WrtDate >> 11) & 0x1F, dir->DIR_WrtTime & 0xF, (dir->DIR_WrtTime >> 4) & 0x3F, ((dir->DIR_WrtTime >> 11) & 0x1F) * 2);
info->atime = thetime;
info->ctime = thetime;
info->mtime = thetime;
return info;
} else if (type == 1 && !(dir->DIR_Attribute & ATTR_DIRECTORY)) {
free(buffer);
struct fat_file_info *info = (struct fat_file_info *)malloc(sizeof(struct fat_file_info));
if (!info) {
return NULL;
}
info->start_cluster = (dir->DIR_FirstClustHi << 0xffff) | dir->DIR_FirstClustLo;
info->file_size = dir->DIR_FileSize;
info->type = 1;
unsigned int thetime = get_time_since_epoch((dir->DIR_WrtDate & 0x7F) + 80, (dir->DIR_WrtDate >> 7) & 0xF, (dir->DIR_WrtDate >> 11) & 0x1F, dir->DIR_WrtTime & 0xF, (dir->DIR_WrtTime >> 4) & 0x3F, ((dir->DIR_WrtTime >> 11) & 0x1F) * 2);
info->atime = thetime;
info->ctime = thetime;
info->mtime = thetime;
return info;
} else if (type == -1) {
free(buffer);
struct fat_file_info *info = (struct fat_file_info *)malloc(sizeof(struct fat_file_info));
if (!info) {
return NULL;
}
info->start_cluster = (dir->DIR_FirstClustHi << 0xffff) | dir->DIR_FirstClustLo;
info->file_size = dir->DIR_FileSize;
if (!(dir->DIR_Attribute & ATTR_DIRECTORY)) {
info->type = 1;
} else {
info->type = 0;
}
unsigned int thetime = get_time_since_epoch((dir->DIR_WrtDate & 0x7F) + 80, (dir->DIR_WrtDate >> 7) & 0xF, (dir->DIR_WrtDate >> 11) & 0x1F, dir->DIR_WrtTime & 0xF, (dir->DIR_WrtTime >> 4) & 0x3F, ((dir->DIR_WrtTime >> 11) & 0x1F) * 2);
info->atime = thetime;
info->ctime = thetime;
info->mtime = thetime;
return info;
}
free(buffer);
return NULL;
}
} else {
if (chksum != fat_long_dir_chksum(dir->DIR_Name)) {
memset(long_filename, 0, 256);
}
if (strcmp(ptr[0], long_filename) == 0 || strcmp(ptr[0], temp) == 0) {
unsigned int start_cluster = (dir->DIR_FirstClustHi << 0xffff) | dir->DIR_FirstClustLo;
if (dir->DIR_Attribute & ATTR_DIRECTORY) {
free(buffer);
return fat_find_sub_entry(device, start_cluster, ptr, 1, parts, type);
} else {
free(buffer);
return NULL;
}
}
}
}
}
return 0;
}
int fat_change_directory(struct vfs_device_t *device, char *path) {
if (path[0] == '/' && path[1] == '\0') {
return 1;
} else {
if (fat_check_if_exists(device, path, 0)) {
return 1;
} else {
return 0;
}
}
}
void fat_list_contents(struct vfs_device_t *device, char *cwd) {
struct fat_data *data = (struct fat_data *)device->fs_data;
char *buffer;
unsigned int len;
char temp[13];
char long_filename[256];
unsigned char chksum = 0;
int i;
int j;
int k = 0;
int long_name_len = 0;
unsigned int first_root_dir_sec;
if (cwd[0] == '/' && cwd[1] == '\0') {
if (data->fat != 2) {
first_root_dir_sec = data->superblock.BPB_RsvdSecCnt + (data->superblock.BPB_NumFATs * data->superblock.BPB_FATSz16);
len = data->root_dir_sectors * data->superblock.BPB_BytesPerSec;
buffer = (char *)malloc(len);
switch(device->device) {
case 1:
ramdisk_read_blocks(first_root_dir_sec, data->root_dir_sectors, buffer, data->superblock.BPB_BytesPerSec);
break;
}
} else {
len = fat_read_entire_file(device, data->superblock.FatType.Fat32.BPB_RootClus, &buffer);
}
} else {
unsigned int cluster = fat_check_if_exists(device, cwd, 0)->start_cluster;
len = fat_read_entire_file(device, cluster, &buffer);
}
for (i=0;i<len / 32; i++) {
memset(temp, 0, 13);
struct fat_dir *dir = (struct fat_dir *)&buffer[i * 32];
if (dir->DIR_Attribute & ATTR_LONG_NAME) {
struct fat_long_dir *ldir = (struct fat_long_dir *)dir;
if (ldir->LDIR_Ord & 0x40) {
memset(long_filename, 0, 256, 0);
long_name_len = 0;
chksum = ldir->LDIR_Chksum;
// last entry
// check length
for (k=0;k<5;k++) {
if (ldir->LDIR_Name1[k] == 0x0000) {
long_name_len = 13 * (ldir->LDIR_Ord & ~(0x40) - 1) + k;
break;
}
}
if (!long_name_len) {
for (k=0;k<6;k++) {
if (ldir->LDIR_Name2[k] == 0x0000) {
long_name_len = 13 * (ldir->LDIR_Ord & ~(0x40) - 1) + k + 5;
break;
}
}
}
if (!long_name_len) {
for (k=0;k<2;k++) {
if (ldir->LDIR_Name3[k] == 0x0000) {
long_name_len = 13 * (ldir->LDIR_Ord & ~(0x40) - 1) + k + 5 + 6;
break;
}
}
}
int offset = 13 * (ldir->LDIR_Ord & ~(0x40) - 1);
for (k=0;k<5;k++) {
long_filename[offset + k] = (char)(ldir->LDIR_Name1[k] & 0xFF);
}
for (k=0;k<6;k++) {
long_filename[offset + 5 + k] = (char)(ldir->LDIR_Name2[k] & 0xFF);
}
for (k=0;k<2;k++) {
long_filename[offset + 11 + k] = (char)(ldir->LDIR_Name3[k] & 0xFF);
}
} else {
int offset = 13 * (ldir->LDIR_Ord - 1);
for (k=0;k<5;k++) {
long_filename[offset + k] = (char)(ldir->LDIR_Name1[k] & 0xFF);
}
for (k=0;k<6;k++) {
long_filename[offset + 5 + k] = (char)(ldir->LDIR_Name2[k] & 0xFF);
}
for (k=0;k<2;k++) {
long_filename[offset + 11 + k] = (char)(ldir->LDIR_Name3[k] & 0xFF);
}
}
} else {
if (dir->DIR_Name[0] == 0xE5) {
continue;
}
if (dir->DIR_Name[0] == 0x00) {
break;
}
if (dir->DIR_Name[0] == 0x05) {
temp[0] = 0xE5;
} else {
temp[0] = dir->DIR_Name[0];
}
for (j=1;j<8;j++) {
if ( dir->DIR_Name[j] == ' ') {
break;
} else {
temp[j] = dir->DIR_Name[j];
}
}
if (dir->DIR_Name[8] != ' ') {
temp[8] = '.';
for (j=8;j<11;j++) {
if (dir->DIR_Name[j] == ' ') {
break;
} else {
temp[j + 1] = dir->DIR_Name[j];
}
}
}
if (chksum != fat_long_dir_chksum(dir->DIR_Name)) {
memset(long_filename, 0, 256);
}
if (strlen(long_filename) > 0) {
kprintf("%s\n", long_filename);
} else {
kprintf("%s\n", temp);
}
}
}
free(buffer);
}
int fat_read_data(struct vfs_device_t *device, struct fat_file_info *info, char *buffer, int len, int offset) {
struct fat_data *data = (struct fat_data *)device->fs_data;
if (info->type == 0) {
// it's a directory...
return 0;
}
if (len + offset > info->file_size) {
len = info->file_size - offset;
}
if (len > 0) {
unsigned int *clusterchain = (unsigned int *)malloc(sizeof(unsigned int) * 2);
clusterchain[0] = info->start_cluster;
clusterchain[1] = fat_get_cluster_entry(device, info->start_cluster);
int i = 1;
int j;
int blocks_read;
while (!is_EOC(device, clusterchain[i])) {
i++;
clusterchain = (unsigned int *)realloc(clusterchain, sizeof(unsigned int) * (i + 1));
clusterchain[i] = fat_get_cluster_entry(device, clusterchain[i-1]);
}
unsigned int cluster_start = offset / (data->superblock.BPB_SecPerClus * data->superblock.BPB_BytesPerSec);
unsigned int cluster_offset = offset % (data->superblock.BPB_SecPerClus * data->superblock.BPB_BytesPerSec);
unsigned int cluster_count = len + cluster_offset / (data->superblock.BPB_SecPerClus * data->superblock.BPB_BytesPerSec);
unsigned int buffer_at;
char *tempbuffer = (char *)malloc(data->superblock.BPB_SecPerClus * data->superblock.BPB_BytesPerSec);
if (len + cluster_offset % (data->superblock.BPB_SecPerClus * data->superblock.BPB_BytesPerSec) > 0) {
cluster_count += 1;
}
switch (device->device) {
case 1:
for (j=0;j<cluster_count;j++) {
blocks_read = ramdisk_read_blocks(cluster_to_sec(device, clusterchain[cluster_start + j]), data->superblock.BPB_SecPerClus, tempbuffer, data->superblock.BPB_BytesPerSec);
if (j==0) {
if (len + cluster_offset > data->superblock.BPB_SecPerClus * data->superblock.BPB_BytesPerSec) {
memcpy(buffer, &tempbuffer[cluster_offset], data->superblock.BPB_SecPerClus * data->superblock.BPB_BytesPerSec - cluster_offset);
buffer_at = data->superblock.BPB_SecPerClus * data->superblock.BPB_BytesPerSec - cluster_offset;
} else {
memcpy(buffer, &tempbuffer[cluster_offset], len);
free(tempbuffer);
free(clusterchain);
return len;
}
} else {
if (len - buffer_at >= data->superblock.BPB_SecPerClus * data->superblock.BPB_BytesPerSec) {
memcpy(&buffer[buffer_at], tempbuffer, data->superblock.BPB_SecPerClus * data->superblock.BPB_BytesPerSec);
buffer_at += data->superblock.BPB_SecPerClus * data->superblock.BPB_BytesPerSec;
} else {
memcpy(&buffer[buffer_at], tempbuffer, len - buffer_at);
free(tempbuffer);
free(clusterchain);
return len;
}
}
}
break;
}
free(clusterchain);
return len;
} else {
return 0;
}
}
int fat_get_dents(struct vfs_device_t *device, struct fat_file_info *info, char *buffer, int len, int offset) {
struct fat_data *data = (struct fat_data *)device->fs_data;
char *dir_buffer;
unsigned int bytesread;
char *buffer2;
if (info->start_cluster == 0) {
unsigned int first_root_dir_sec = data->superblock.BPB_RsvdSecCnt + (data->superblock.BPB_NumFATs * data->superblock.BPB_FATSz16);
unsigned int dirlen;
if (info->file_size == 0) {
dirlen = data->root_dir_sectors * data->superblock.BPB_BytesPerSec;
} else {
dirlen = info->file_size;
}
if (offset >= dirlen) {
return 0;
}
buffer2 = (char *)malloc(dirlen);
switch(device->device) {
case 1:
ramdisk_read_blocks(first_root_dir_sec, data->root_dir_sectors, buffer2, data->superblock.BPB_BytesPerSec);
break;
}
dir_buffer = (char *)malloc(data->root_dir_sectors * data->superblock.BPB_BytesPerSec - offset + 1);
memcpy(dir_buffer, &buffer2[offset], data->root_dir_sectors * data->superblock.BPB_BytesPerSec - offset);
free(buffer2);
bytesread = data->root_dir_sectors * data->superblock.BPB_BytesPerSec - offset;
} else {
unsigned int newsz;
int dirlen = fat_read_entire_file(device, info->start_cluster, &buffer2);
if (info->file_size == 0) {
newsz = dirlen;
} else {
newsz = info->file_size;
}
dir_buffer = (char *)malloc(newsz - offset + 1);
memcpy(dir_buffer, &buffer2[offset], newsz - offset);
free(buffer2);
bytesread = newsz - offset;
}
char temp[13];
char long_filename[256];
int long_name_len = 0;
int buff_offset = 0;
unsigned char chksum = 0;
int i;
int j;
int k = 0;
for (i=0;i<bytesread / 32;i++) {
memset(temp, 0, 13);
struct fat_dir *dir = (struct fat_dir *)&dir_buffer[i * 32];
if (dir->DIR_Attribute & ATTR_LONG_NAME) {
struct fat_long_dir *ldir = (struct fat_long_dir *)dir;
if (ldir->LDIR_Ord & 0x40) {
memset(long_filename, 0, 256, 0);
long_name_len = 0;
chksum = ldir->LDIR_Chksum;
// last entry
// check length
for (k=0;k<5;k++) {
if (ldir->LDIR_Name1[k] == 0x0000) {
long_name_len = 13 * (ldir->LDIR_Ord & ~(0x40) - 1) + k;
break;
}
}
if (!long_name_len) {
for (k=0;k<6;k++) {
if (ldir->LDIR_Name2[k] == 0x0000) {
long_name_len = 13 * (ldir->LDIR_Ord & ~(0x40) - 1) + k + 5;
break;
}
}
}
if (!long_name_len) {
for (k=0;k<2;k++) {
if (ldir->LDIR_Name3[k] == 0x0000) {
long_name_len = 13 * (ldir->LDIR_Ord & ~(0x40) - 1) + k + 5 + 6;
break;
}
}
}
int offset = 13 * (ldir->LDIR_Ord & ~(0x40) - 1);
for (k=0;k<5;k++) {
long_filename[offset + k] = (char)(ldir->LDIR_Name1[k] & 0xFF);
}
for (k=0;k<6;k++) {
long_filename[offset + 5 + k] = (char)(ldir->LDIR_Name2[k] & 0xFF);
}
for (k=0;k<2;k++) {
long_filename[offset + 11 + k] = (char)(ldir->LDIR_Name3[k] & 0xFF);
}
} else {
int offset = 13 * (ldir->LDIR_Ord - 1);
for (k=0;k<5;k++) {
long_filename[offset + k] = (char)(ldir->LDIR_Name1[k] & 0xFF);
}
for (k=0;k<6;k++) {
long_filename[offset + 5 + k] = (char)(ldir->LDIR_Name2[k] & 0xFF);
}
for (k=0;k<2;k++) {
long_filename[offset + 11 + k] = (char)(ldir->LDIR_Name3[k] & 0xFF);
}
}
} else {
if (dir->DIR_Name[0] == 0xE5) {
continue;
}
if (dir->DIR_Name[0] == 0x00) {
info->file_size = offset + buff_offset;
break;
}
if (dir->DIR_Name[0] == 0x05) {
temp[0] = 0xE5;
} else {
temp[0] = dir->DIR_Name[0];
}
for (j=1;j<8;j++) {
if ( dir->DIR_Name[j] == ' ') {
break;
} else {
temp[j] = dir->DIR_Name[j];
}
}
if (dir->DIR_Name[8] != ' ') {
temp[8] = '.';
for (j=8;j<11;j++) {
if (dir->DIR_Name[j] == ' ') {
break;
} else {
temp[j + 1] = dir->DIR_Name[j];
}
}
}
if (chksum != fat_long_dir_chksum(dir->DIR_Name)) {
memset(long_filename, 0, 256);
}
struct quinn_dirent_t *dent = (struct quinn_dirent_t *)(&buffer[buff_offset]);
if (strlen(long_filename) > 0) {
if (buff_offset + sizeof(struct quinn_dirent_t) + (strlen(long_filename)) > len) {
free(dir_buffer);
return buff_offset;
}
dent->name_len = strlen(long_filename);
dent->rec_len = sizeof(struct quinn_dirent_t) + strlen(long_filename);
dent->next_offset = buff_offset + dent->rec_len;
strcpy(dent->name, long_filename);
buff_offset = dent->next_offset;
} else {
if (buff_offset + sizeof(struct quinn_dirent_t) + (strlen(temp)) > len) {
free(dir_buffer);
return buff_offset;
}
dent->name_len = strlen(temp);
dent->rec_len = sizeof(struct quinn_dirent_t) + strlen(temp);
dent->next_offset = buff_offset + dent->rec_len;
strcpy(dent->name, temp);
buff_offset = dent->next_offset;
}
}
}
free(dir_buffer);
return buff_offset;
}