quinn-os/fat.c

2309 lines
68 KiB
C

#include "vfs.h"
#include "fat.h"
#include "memory.h"
#include "console.h"
#include "hd.h"
#include "rtc.h"
#include "string.h"
#include "ramdisk.h"
unsigned int cluster_to_sec(struct vfs_device_t *device, unsigned int cluster);
void fat_set_cluster_entry(struct vfs_device_t *device, unsigned int cluster, unsigned int value);
unsigned int fat_alloc_cluster(struct vfs_device_t *device, unsigned int prevcluster) {
struct fat_data *data = (struct fat_data *)device->fs_data;
char *buffer;
unsigned int start_search = 2;
unsigned int i;
if (data->fat == 2) {
buffer = (char *)dbmalloc(data->superblock.BPB_BytesPerSec, "fat_alloc_cluster");
if (device->device == 1) {
ramdisk_read_block(data->superblock.FatType.Fat32.BPB_FSInfo, buffer, data->superblock.BPB_BytesPerSec);
} else if ((device->device & 0xff00) == 0x100) {
hd_read_block((device->device & 0xff), data->superblock.FatType.Fat32.BPB_FSInfo, buffer, data->superblock.BPB_BytesPerSec);
}
struct fat_fsinfo *fsinfo = (struct fat_fsinfo *)buffer;
if (fsinfo->FSI_LeadSig == 0x41615252 && fsinfo->FSI_StructSig == 0x61417272 && fsinfo->FSI_TailSig == 0xAA550000) {
start_search = fsinfo->FSI_Nxt_Free;
}
free(buffer);
}
unsigned int sector;
unsigned int offset;
unsigned int last_sector = -1;
buffer = (char *)dbmalloc(data->superblock.BPB_BytesPerSec, "fat_alloc_cluster");
search_again:
for (i=start_search;i<data->count_of_clusters;i++) {
unsigned int fatOffset;
if (data->fat != 0) {
if (data->fat == 1) {
fatOffset = i * 2;
} else if (data->fat == 2) {
fatOffset = i * 4;
}
sector = data->superblock.BPB_RsvdSecCnt + (fatOffset / data->superblock.BPB_BytesPerSec);
offset = fatOffset % data->superblock.BPB_BytesPerSec;
if (last_sector != sector) {
if (device->device == 1) {
ramdisk_read_block(sector, buffer, data->superblock.BPB_BytesPerSec);
} else if ((device->device & 0xff00) == 0x100) {
hd_read_block((device->device & 0xff), sector, buffer, data->superblock.BPB_BytesPerSec);
}
}
if (data->fat == 1) {
unsigned short fat16clusterEntryVal = *((unsigned short *)&buffer[offset]);
if (fat16clusterEntryVal == 0) {
break;
}
} else if (data->fat == 2) {
unsigned int fat32clusterEntryVal = *((unsigned int *)&buffer[offset]);
if (fat32clusterEntryVal == 0) {
break;
}
}
} else {
fatOffset = i + (i / 2);
sector = data->superblock.BPB_RsvdSecCnt + (fatOffset / data->superblock.BPB_BytesPerSec);
int offset = fatOffset % data->superblock.BPB_BytesPerSec;
if (last_sector != sector) {
if (device->device == 1) {
ramdisk_read_blocks(sector, 2, buffer, data->superblock.BPB_BytesPerSec);
} else if ((device->device & 0xff00) == 0x100) {
hd_read_blocks((device->device & 0xff), sector, 2, buffer, data->superblock.BPB_BytesPerSec);
}
}
unsigned short fat12clusterEntryVal = *((unsigned short *)&buffer[offset]);
if (i & 0x0001) {
fat12clusterEntryVal = fat12clusterEntryVal >> 4;
} else {
fat12clusterEntryVal = fat12clusterEntryVal & 0x0fff;
}
if (fat12clusterEntryVal == 0) {
break;
}
}
}
if (i == data->count_of_clusters) {
if (start_search > 2) {
start_search = 2;
goto search_again;
}
free(buffer);
return 0;
} else {
if (prevcluster) {
fat_set_cluster_entry(device, prevcluster, i);
}
unsigned int value;
if (data->fat == 0) {
value = 0xfff;
} else if (data->fat == 1) {
value = 0xffff;
} else {
value = 0x0fffffff;
}
fat_set_cluster_entry(device, i, value);
// zero out cluster
buffer = (char *)dbmalloc(data->superblock.BPB_SecPerClus * data->superblock.BPB_BytesPerSec, "fat_alloc_cluster");
memset(buffer, 0, data->superblock.BPB_SecPerClus * data->superblock.BPB_BytesPerSec);
if (device->device == 1) {
ramdisk_write_blocks(cluster_to_sec(device, i), data->superblock.BPB_SecPerClus, buffer, data->superblock.BPB_BytesPerSec);
} else if ((device->device & 0xff00) == 0x100) {
hd_write_blocks((device->device & 0xff), cluster_to_sec(device, i), data->superblock.BPB_SecPerClus, buffer, data->superblock.BPB_BytesPerSec);
}
free(buffer);
return i;
}
}
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;
}
if (device->device == 1) {
ramdisk_read_block(0, buffer, 512);
} else if ((device->device & 0xff00) == 0x100) {
hd_read_block((device->device & 0xff), 0, buffer, 512);
}
memcpy((char *)&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);
free(fatData);
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;
}
void fat_set_cluster_entry(struct vfs_device_t *device, unsigned int cluster, unsigned int value) {
struct fat_data *data = (struct fat_data *)device->fs_data;
unsigned int fatOffset;
unsigned int fatsz;
int i;
for (i=0;i<data->superblock.BPB_NumFATs;i++) {
if (data->fat != 0) {
if (data->fat == 1) {
fatOffset = cluster * 2;
fatsz = data->superblock.BPB_FATSz16;
} else if (data->fat == 2) {
fatOffset = cluster * 4;
fatsz = data->superblock.FatType.Fat32.BPB_FATSz32;
}
unsigned int sector = data->superblock.BPB_RsvdSecCnt + (i * fatsz) + (fatOffset / data->superblock.BPB_BytesPerSec);
unsigned int offset = fatOffset % data->superblock.BPB_BytesPerSec;
char *buffer = (char *)dbmalloc(data->superblock.BPB_BytesPerSec, "fat_set_cluster_entry");
if (!buffer) {
// fail.
}
if (device->device == 1) {
ramdisk_read_block(sector, buffer, data->superblock.BPB_BytesPerSec);
} else if ((device->device & 0xff00) == 0x100) {
hd_read_block((device->device & 0xff), sector, buffer, data->superblock.BPB_BytesPerSec);
}
if (data->fat == 1) {
*((unsigned short *)&buffer[offset]) = (unsigned short)value;
} else if (data->fat == 2) {
value = value & 0x0fffffff;
*((unsigned int *)&buffer[offset]) = *((unsigned int *)&buffer[offset]) & 0xf0000000;
*((unsigned int *)&buffer[offset]) = *((unsigned int *)&buffer[offset]) | value;
}
if (device->device == 1) {
ramdisk_write_block(sector, buffer, data->superblock.BPB_BytesPerSec);
} else if ((device->device & 0xff00) == 0x100) {
hd_write_block((device->device & 0xff), sector, buffer, data->superblock.BPB_BytesPerSec);
}
free(buffer);
} else {
fatsz = data->superblock.BPB_FATSz16;
fatOffset = cluster + (cluster / 2);
unsigned int sector = data->superblock.BPB_RsvdSecCnt + (i * fatsz) + (fatOffset / data->superblock.BPB_BytesPerSec);
unsigned int offset = fatOffset % data->superblock.BPB_BytesPerSec;
char *buffer = (char *)dbmalloc(data->superblock.BPB_BytesPerSec * 2, "fat_set_cluster_entry");
if (!buffer) {
// fail
}
if (device->device == 1) {
ramdisk_read_blocks(sector, 2, buffer, data->superblock.BPB_BytesPerSec);
} else if ((device->device & 0xff00) == 0x100) {
hd_read_blocks((device->device & 0xff), sector, 2, buffer, data->superblock.BPB_BytesPerSec);
}
unsigned short fat12clusterEntryVal;
if (cluster & 0x0001) {
value = value << 4;
*(unsigned short *)&buffer[offset] = (*((unsigned short *)&buffer[offset])) & 0x000f;
} else {
value = value & 0x0fff;
*(unsigned short *)&buffer[offset] = (*((unsigned short *)&buffer[offset])) & 0xf000;
}
*(unsigned short *)&buffer[offset] = (*((unsigned short *)&buffer[offset])) | (unsigned short)value;
if (device->device == 1) {
ramdisk_write_blocks(sector, 2, buffer, data->superblock.BPB_BytesPerSec);
} else if ((device->device & 0xff00) == 0x100) {
hd_write_blocks((device->device & 0xff), sector, 2, buffer, data->superblock.BPB_BytesPerSec);
}
free(buffer);
}
}
}
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 *)dbmalloc(data->superblock.BPB_BytesPerSec, "fat_get_cluster_entry");
if (!buffer) {
// fail.
kprintf("Failed to allocate memory\n");
}
if (device->device == 1) {
ramdisk_read_block(sector, buffer, data->superblock.BPB_BytesPerSec);
} else if ((device->device & 0xff00) == 0x100) {
hd_read_block((device->device & 0xff), sector, buffer, data->superblock.BPB_BytesPerSec);
}
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 *)dbmalloc(data->superblock.BPB_BytesPerSec * 2, "fat_get_cluster_entry");
if (!buffer) {
// fail
kprintf("Failed to allocate memory\n");
}
if (device->device == 1) {
ramdisk_read_blocks(sector, 2, buffer, data->superblock.BPB_BytesPerSec);
} else if ((device->device & 0xff00) == 0x100) {
hd_read_blocks((device->device & 0xff), sector, 2, buffer, data->superblock.BPB_BytesPerSec);
}
unsigned short fat12clusterEntryVal = *((unsigned short *)&buffer[offset]);
if (cluster & 0x0001) {
fat12clusterEntryVal = fat12clusterEntryVal >> 4;
} else {
fat12clusterEntryVal = fat12clusterEntryVal & 0x0fff;
}
return (unsigned int)fat12clusterEntryVal;
}
}
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;
}
void fat_write_entire_file(struct vfs_device_t *device, unsigned int start_cluster, char *buffer, unsigned int len) {
struct fat_data *data = (struct fat_data *)device->fs_data;
unsigned int *clusterchain = (unsigned int *)dbmalloc(sizeof(unsigned int) * 2, "fat_write_entire_file");
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 j;
for (j=0;j<i;j++) {
if (device->device == 1) {
ramdisk_write_blocks(cluster_to_sec(device, clusterchain[j]), data->superblock.BPB_SecPerClus, &buffer[j * data->superblock.BPB_SecPerClus * data->superblock.BPB_BytesPerSec], data->superblock.BPB_BytesPerSec);
}else if ((device->device & 0xff00) == 0x100) {
hd_write_blocks((device->device & 0xff),cluster_to_sec(device, clusterchain[j]), data->superblock.BPB_SecPerClus, &buffer[j * data->superblock.BPB_SecPerClus * data->superblock.BPB_BytesPerSec], data->superblock.BPB_BytesPerSec);
}
}
free(clusterchain);
return;
}
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 *)dbmalloc(sizeof(unsigned int) * 2, "fat_read_entire_file 1");
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 *)dbmalloc(len, "fat_read_entire_file 2");
int j;
char *buffer_loc = *buffer;
for (j=0;j<i;j++) {
if (device->device == 1) {
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);
}else if ((device->device & 0xff00) == 0x100) {
hd_read_blocks((device->device & 0xff),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);
}
}
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 bad;
int badcharacters[] = {0x22, 0x2A, 0x2B, 0x2C, 0x2E, 0x2F, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x5B, 0x5C, 0x5D, 0x7C};
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 == 0xE5) {
continue;
}
if (ldir->LDIR_Ord == 0x00) {
break;
}
if (ldir->LDIR_Ord & 0x40) {
memset(long_filename, 0, 256);
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 & 0x3F) - 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 & 0x3F) - 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 & 0x3F) - 1) + k + 5 + 6;
break;
}
}
}
int offset = 13 * ((ldir->LDIR_Ord & 0x3F) - 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;
}
bad = 0;
for (j=0;j<11;j++) {
if (dir->DIR_Name[j] < 0x20 && dir->DIR_Name[j] != 0x05) {
bad = 1;
break;
}
if (!(j == 0 && dir->DIR_Name[j] == ' ')) {
for (k=0;k<16;k++) {
if (dir->DIR_Name[j] == badcharacters[k]) {
bad = 1;
break;
}
}
} else {
bad = 1;
}
if (bad == 1) {
break;
}
}
if (bad == 1) {
continue;
}
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 (k=8;k<11;k++) {
if (dir->DIR_Name[k] == ' ') {
break;
} else {
temp[j + 1 + (k - 8)] = 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 *)dbmalloc(sizeof(struct fat_file_info), "fat_find_sub_entry");
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;
info->clusterchain = (void *)0;
return info;
} else if (type == 1 && !(dir->DIR_Attribute & ATTR_DIRECTORY)) {
free(buffer);
struct fat_file_info *info = (struct fat_file_info *)dbmalloc(sizeof(struct fat_file_info), "fat_find_sub_entry");
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;
info->clusterchain = (void *)0;
return info;
} else if (type == -1){
free(buffer);
struct fat_file_info *info = (struct fat_file_info *)dbmalloc(sizeof(struct fat_file_info), "fat_find_sub_entry");
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;
info->clusterchain = (void *)0;
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;
}
int fat_find_dentry_offset(char *basename, char *buffer, int len, int *last_flag) {
int i;
int nent = (strlen(basename) / 13) + 1;
if (strlen(basename) % 13 != 0) {
nent ++;
}
int n = 0;
int offset = -1;
if (basename[0] == '.') {
nent = 1;
}
for (i=0;i<len / 32;i++) {
struct fat_dir *dir = (struct fat_dir *)&buffer[i * 32];
if (dir->DIR_Name[0] == 0xE5) {
if (offset == -1) {
offset = i;
}
n++;
if (n == nent) {
*last_flag = 0;
break;
}
} else if (dir->DIR_Name[0] == 0x00) {
if (offset == -1) {
offset = i;
*last_flag = 1;
}
if (offset + nent + 1 > len / 32) {
offset = -1;
}
break;
} else {
n = 0;
offset = -1;
}
}
return offset;
}
int fat_create_dentry(struct vfs_device_t *device, char *path, int start_cluster, int type) {
char *path_copy;
char *directory;
char *basename;
char *buffer;
int i;
unsigned int len;
char shortname[11];
struct fat_data *data = (struct fat_data *)device->fs_data;
unsigned int first_root_dir_sec;
unsigned int clusterchain;
memset(shortname, ' ', 11);
path_copy = (char *)dbmalloc(strlen(path) + 1, "fat_create_dentry");
strcpy(path_copy, path);
directory = path_copy;
for (i=strlen(path_copy) -1;i>=0;i--) {
if (path_copy[i] == '/') {
path_copy[i] = '\0';
basename = &path_copy[i+1];
break;
}
}
struct fat_file_info *info = NULL;
if (directory[0] == '\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 *)dbmalloc(len, "fat_create_dentry");
if (device->device == 1) {
ramdisk_read_blocks(first_root_dir_sec, data->root_dir_sectors, buffer, data->superblock.BPB_BytesPerSec);
} else if ((device->device & 0xf00) == 0x100) {
hd_read_blocks((device->device & 0xff), first_root_dir_sec, data->root_dir_sectors, buffer, data->superblock.BPB_BytesPerSec);
}
} else {
len = fat_read_entire_file(device, data->superblock.FatType.Fat32.BPB_RootClus, &buffer);
}
} else {
info = fat_check_if_exists(device, directory, 0);
unsigned int cluster = info->start_cluster;
len = fat_read_entire_file(device, cluster, &buffer);
}
int tail = 0;
int highest_tail = 0;
int j;
int k;
int last_flag;
int flag_cont = 0;
while (1) {
if (basename[0] != '.') {
for (i=0;i<len / 32;i++) {
struct fat_dir *dir = (struct fat_dir *)&buffer[i * 32];
if (!(dir->DIR_Attribute & ATTR_LONG_NAME)) {
flag_cont = 0;
for (j=0;j<8;j++) {
if (j > strlen(basename) && dir->DIR_Name[j] != ' ') {
flag_cont = 1;
break;
}
if (dir->DIR_Name[j] != toupper(basename[j])) {
flag_cont = 1;
break;
}
}
if (flag_cont) {
continue;
}
for (j=0;j<8;j++) {
if (dir->DIR_Name[j] == '~') {
break;
}
}
if (j==8) {
tail = 1;
highest_tail = 1;
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 extpos = strlen(basename);
if (type != 0) {
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) + 8] = toupper(basename[j]);
}
break;
}
}
}
for (i=0;i<extpos && i < 8;i++) {
shortname[i] = toupper(basename[i]);
}
if (tail > 0) {
for (i=7;i>1;i--) {
shortname[i] = (tail % 10) + '0';
tail = tail / 10;
if (tail == 0) {
shortname[i-1] = '~';
break;
}
}
}
} else {
shortname[0] = basename[0];
shortname[1] = (basename[1] == '.' ? '.' : ' ');
shortname[2] = ' ';
shortname[3] = ' ';
shortname[4] = ' ';
shortname[5] = ' ';
shortname[6] = ' ';
shortname[7] = ' ';
shortname[8] = ' ';
shortname[9] = ' ';
shortname[10] = ' ';
}
unsigned int offset = fat_find_dentry_offset(basename, buffer, len, &last_flag);
if (offset != -1) {
// found empty dentrys
int count = 0;
if (basename[0] != '.') {
count = strlen(basename) / 13;
if (strlen(basename) % 13) {
count ++;
}
for (i=count - 1;i>=0;i--) {
struct fat_long_dir *ldir = (struct fat_long_dir *)&buffer[(offset + i) * 32];
ldir->LDIR_Ord = i + 1;
if (i==count -1) {
ldir->LDIR_Ord |= 0x40;
}
if ((i * 13) +1 > strlen(basename) + 1) {
ldir->LDIR_Name1[0] = 0xffff;
} else {
ldir->LDIR_Name1[0] = basename[(i * 13)];
}
if ((i * 13) +1 > strlen(basename) + 1) {
ldir->LDIR_Name1[1] = 0xffff;
} else {
ldir->LDIR_Name1[1] = basename[(i * 13)+1];
}
if ((i * 13)+2 > strlen(basename) + 1) {
ldir->LDIR_Name1[2] = 0xffff;
} else {
ldir->LDIR_Name1[2] = basename[(i * 13)+2];
}
if ((i * 13)+3 > strlen(basename) + 1) {
ldir->LDIR_Name1[3] = 0xffff;
} else {
ldir->LDIR_Name1[3] = basename[(i * 13)+3];
}
if ((i * 13)+4 > strlen(basename) + 1) {
ldir->LDIR_Name1[4] = 0xffff;
} else {
ldir->LDIR_Name1[4] = basename[(i * 13)+4];
}
ldir->LDIR_Attrib = ATTR_LONG_NAME;
ldir->LDIR_Type = 0;
ldir->LDIR_Chksum = fat_long_dir_chksum(shortname);
if ((i * 13)+5 > strlen(basename) + 1) {
ldir->LDIR_Name2[0] = 0xffff;
} else {
ldir->LDIR_Name2[0] = basename[(i * 13)+5];
}
if ((i * 13)+6 > strlen(basename) + 1) {
ldir->LDIR_Name2[1] = 0xffff;
} else {
ldir->LDIR_Name2[1] = basename[(i * 13)+6];
}
if ((i * 13)+7 > strlen(basename) + 1) {
ldir->LDIR_Name2[2] = 0xffff;
} else {
ldir->LDIR_Name2[2] = basename[(i * 13)+7];
}
if ((i * 13)+8 > strlen(basename) + 1) {
ldir->LDIR_Name2[3] = 0xffff;
} else {
ldir->LDIR_Name2[3] = basename[(i * 13)+8];
}
if ((i * 13)+9 > strlen(basename) + 1) {
ldir->LDIR_Name2[4] = 0xffff;
} else {
ldir->LDIR_Name2[4] = basename[(i * 13)+9];
}
if ((i * 13)+10 > strlen(basename) + 1) {
ldir->LDIR_Name2[5] = 0xffff;
} else {
ldir->LDIR_Name2[5] = basename[(i * 13)+10];
}
if ((i * 13)+11 > strlen(basename) + 1) {
ldir->LDIR_Name3[0] = 0xffff;
} else {
ldir->LDIR_Name3[0] = basename[(i * 13)+11];
}
if ((i * 13)+12 > strlen(basename) + 1) {
ldir->LDIR_Name3[1] = 0xffff;
} else {
ldir->LDIR_Name3[1] = basename[(i * 13)+12];
}
memcpy(&buffer[(offset + i) * 32], (char *)ldir, sizeof(struct fat_long_dir));
}
}
struct fat_dir *dir = (struct fat_dir *)&buffer[(offset + count) * 32];
memcpy(dir->DIR_Name, shortname, 11);
if (type == 0) {
dir->DIR_Attribute = ATTR_DIRECTORY;
} else {
dir->DIR_Attribute = 0;
}
dir->DIR_NTRes = 0;
dir->DIR_CrtTimeTenth = 0;
if (data->fat != 2) {
dir->DIR_FirstClustHi = 0;
} else {
dir->DIR_FirstClustHi = (start_cluster & 0x0fff0000) >> 16;
}
dir->DIR_WrtTime = rtc_get_fat_time();
dir->DIR_WrtDate = rtc_get_fat_date();
if (data->fat == 0) {
dir->DIR_FirstClustLo = start_cluster & 0x0fff;
} else {
dir->DIR_FirstClustLo = start_cluster & 0xffff;
}
dir->DIR_FileSize = 0;
if (last_flag == 1) {
char *zeroend = (char *)&buffer[(offset + count + 2) * 32];
memset(zeroend, 0, len -((offset + count + 2) * 32));
}
// write out file
if (directory[0] == '\0') {
if (data->fat != 2) {
if (device->device == 1) {
ramdisk_write_blocks(first_root_dir_sec, data->root_dir_sectors, buffer, data->superblock.BPB_BytesPerSec);
} else if ((device->device & 0xff00) == 0x100) {
hd_write_blocks((device->device & 0xff), first_root_dir_sec, data->root_dir_sectors, buffer, data->superblock.BPB_BytesPerSec);
}
free(buffer);
return 0;
}
clusterchain = data->superblock.FatType.Fat32.BPB_RootClus;
} else {
clusterchain = info->start_cluster;
}
unsigned int lastcluster;
int i = 0;
do {
if (device->device == 1) {
ramdisk_write_blocks(cluster_to_sec(device, clusterchain), data->superblock.BPB_SecPerClus, &buffer[i * data->superblock.BPB_SecPerClus * data->superblock.BPB_BytesPerSec] , data->superblock.BPB_BytesPerSec);
} else if ((device->device & 0xff00) == 0x100) {
hd_write_blocks((device->device & 0xff), cluster_to_sec(device, clusterchain), data->superblock.BPB_SecPerClus, &buffer[i * data->superblock.BPB_SecPerClus * data->superblock.BPB_BytesPerSec], data->superblock.BPB_BytesPerSec);
}
lastcluster = clusterchain;
i++;
clusterchain = fat_get_cluster_entry(device, lastcluster);
} while (!is_EOC(device, lastcluster));
if (info != NULL) free(info);
return 0;
} else {
// need to append dentry
if (directory[0] == '\0') {
if (data->fat != 2) {
// out of space in the root directory...
free(buffer);
return -1;
}
clusterchain = data->superblock.FatType.Fat32.BPB_RootClus;
} else {
clusterchain = info->start_cluster;
}
unsigned int lastcluster;
while (!is_EOC(device, clusterchain)) {
lastcluster = clusterchain;
clusterchain = fat_get_cluster_entry(device, lastcluster);
}
unsigned int new_cluster = fat_alloc_cluster(device, lastcluster);
if (!new_cluster) {
free(buffer);
free(info);
return -1;
}
len = len + data->superblock.BPB_SecPerClus * data->superblock.BPB_BytesPerSec;
buffer = (char *)realloc(buffer, len);
}
}
}
int fat_create_file(struct vfs_device_t *device, char *path) {
fat_create_dentry(device, path, 0, 1);
}
int fat_create_directory(struct vfs_device_t *device, char *path) {
char *dotentry = (char *)dbmalloc(strlen(path) + 3, "fat_create_directory");
char *dotdotentry = (char *)dbmalloc(strlen(path) + 4, "fat_create_directory");
struct fat_data *data = (struct fat_data *)device->fs_data;
char *path_copy = (char *)dbmalloc(strlen(path) + 1, "fat_create_directory");
int i;
int root = 0;
struct fat_file_info *info = NULL;
int ret;
strcpy(path_copy, path);
for (i=strlen(path_copy)-1;i >= 0; i--) {
if (path_copy[i] == '/') {
path_copy[i] = '\0';
break;
}
}
if (strlen(path_copy) == 0) {
root = 1;
free(path_copy);
} else {
info = fat_check_if_exists(device, path_copy, 0);
if (!info) {
free(dotentry);
free(dotdotentry);
free(path_copy);
return -1;
}
}
strcpy(dotentry, path);
dotentry[strlen(path)] = '/';
dotentry[strlen(path) + 1] = '.';
dotentry[strlen(path) + 2] = '\0';
strcpy(dotdotentry, dotentry);
dotdotentry[strlen(path) + 2] = '.';
dotdotentry[strlen(path) + 3] = '\0';
unsigned int new_cluster = fat_alloc_cluster(device, 0);
// clear out cluster
char *buffer = (char *)dbmalloc(data->superblock.BPB_BytesPerSec * data->superblock.BPB_SecPerClus, "fat_create_directory");
memset(buffer, 0, data->superblock.BPB_BytesPerSec * data->superblock.BPB_SecPerClus);
if (device->device == 1) {
ramdisk_write_blocks(cluster_to_sec(device, new_cluster), data->superblock.BPB_SecPerClus, buffer, data->superblock.BPB_BytesPerSec);
} else if ((device->device & 0xff00) == 0x100) {
hd_write_blocks((device->device & 0xff), cluster_to_sec(device, new_cluster), data->superblock.BPB_SecPerClus, buffer, data->superblock.BPB_BytesPerSec);
}
free(buffer);
ret = fat_create_dentry(device, path, new_cluster, 0);
if (root == 1) {
fat_create_dentry(device, dotentry, 0, 0);
fat_create_dentry(device, dotdotentry, 0, 0);
} else {
fat_create_dentry(device, dotentry, new_cluster, 0);
fat_create_dentry(device, dotdotentry, info->start_cluster, 0);
}
if (info != NULL) {
free(info);
}
free(dotentry);
free(dotdotentry);
return 0;
}
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;
int bad;
int badcharacters[] = {0x22, 0x2A, 0x2B, 0x2C, 0x2E, 0x2F, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x5B, 0x5C, 0x5D, 0x7C};
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 *)dbmalloc(sizeof(struct fat_file_info), "fat_check_if_exists");
if (!info) {
return NULL;
}
info->start_cluster = 0;
info->file_size = 0;
info->type = 0;
info->clusterchain = (void *)0;
return info;
}
buffer = (char *)dbmalloc(len, "fat_check_if_exists");
if (device->device == 1) {
ramdisk_read_blocks(first_root_dir_sec, data->root_dir_sectors, buffer, data->superblock.BPB_BytesPerSec);
} else if ((device->device & 0xff00) == 0x100) {
hd_read_blocks(device->device & 0xff, first_root_dir_sec, data->root_dir_sectors, buffer, data->superblock.BPB_BytesPerSec);
}
} 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 *)dbmalloc(sizeof(struct fat_file_info), "fat_check_if_exists");
if (!info) {
return NULL;
}
info->start_cluster = data->superblock.FatType.Fat32.BPB_RootClus;
info->file_size = len;
info->type = 0;
info->clusterchain = (void *)0;
free(buffer);
return info;
}
}
// buffer contains the root directory contents...
int i;
int parts = 0;
char *path_copy = (char *)dbmalloc(strlen(path) + 1, "fat_check_if_exists");
strcpy(path_copy, path);
if (path_copy[strlen(path_copy) - 1] == '/') {
path_copy[strlen(path_copy) - 1] = '\0';
}
parts = 1;
for (i=1;i<strlen(path_copy);i++) {
if (path_copy[i] == '/') {
parts++;
}
}
char **ptr = (char **)dbmalloc(sizeof(char *) * parts , "fat_check_if_exists");
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 == 0xE5) {
continue;
}
if (ldir->LDIR_Ord == 0x00) {
break;
}
if (ldir->LDIR_Ord & 0x40) {
memset(long_filename, 0, 256);
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 & 0x3F) - 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 & 0x3F) - 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 & 0x3F) - 1) + k + 5 + 6;
break;
}
}
}
int offset = 13 * ((ldir->LDIR_Ord & 0x3F) - 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;
}
bad = 0;
for (j=0;j<11;j++) {
if (dir->DIR_Name[j] < 0x20 && dir->DIR_Name[j] != 0x05) {
bad = 1;
break;
}
if (!(j == 0 && dir->DIR_Name[j] == ' ')) {
for (k=0;k<16;k++) {
if (dir->DIR_Name[j] == badcharacters[k]) {
bad = 1;
break;
}
}
} else {
bad = 1;
}
if (bad == 1) {
break;
}
}
if (bad == 1) {
continue;
}
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 (k=8;k<11;k++) {
if (dir->DIR_Name[k] == ' ') {
break;
} else {
temp[j + 1 + (k - 8)] = 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 *)dbmalloc(sizeof(struct fat_file_info), "fat_check_if_exists");
if (!info) {
return NULL;
}
info->start_cluster = ((unsigned int)dir->DIR_FirstClustHi << (unsigned int)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;
info->clusterchain = (void *)0;
return info;
} else if (type == 1 && !(dir->DIR_Attribute & ATTR_DIRECTORY)) {
free(buffer);
struct fat_file_info *info = (struct fat_file_info *)dbmalloc(sizeof(struct fat_file_info), "fat_check_if_exists");
if (!info) {
return NULL;
}
info->start_cluster = ((unsigned int)dir->DIR_FirstClustHi << (unsigned int)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;
info->clusterchain = (void *)0;
return info;
} else if (type == -1) {
free(buffer);
struct fat_file_info *info = (struct fat_file_info *)dbmalloc(sizeof(struct fat_file_info), "fat_check_if_exists");
if (!info) {
return NULL;
}
info->start_cluster = ((unsigned int)dir->DIR_FirstClustHi << (unsigned int)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;
info->clusterchain = (void *)0;
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_clear_cluster_chain(struct vfs_device_t *device, unsigned int start_cluster) {
int i;
unsigned int *clusterchain = (unsigned int *)dbmalloc(sizeof(unsigned int) * 2, "fat_clear_cluster_chain");
clusterchain[0] = start_cluster;
clusterchain[1] = fat_get_cluster_entry(device, start_cluster);
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 j;
for (j=0;j<i;j++) {
fat_set_cluster_entry(device, clusterchain[j], 0);
}
free(clusterchain);
}
int fat_trunc_file(struct vfs_device_t *device, struct fat_file_info *info, char *filename) {
fat_clear_cluster_chain(device, info->start_cluster);
struct fat_data *data = (struct fat_data *)device->fs_data;
int j, i;
char *temp_buff = (char *)dbmalloc(strlen(filename) + 1, "fat_trunc_file");
char *fname;
strcpy(temp_buff, filename);
for (j=strlen(temp_buff)-1;j>=0;j++) {
if (temp_buff[j] == '/') {
temp_buff[j] = '\0';
fname = &temp_buff[j+1];
break;
}
}
int dirlen;
struct fat_file_info *dirinfo;
unsigned int first_root_dir_sec;
char *buffer2;
if (strlen(temp_buff) == 0) {
// root directory
if (data->fat != 2) {
first_root_dir_sec = data->superblock.BPB_RsvdSecCnt + (data->superblock.BPB_NumFATs * data->superblock.BPB_FATSz16);
dirlen = data->root_dir_sectors * data->superblock.BPB_BytesPerSec;
buffer2 = (char *)dbmalloc(dirlen, "fat_trunc_file");
if (device->device == 1) {
ramdisk_read_blocks(first_root_dir_sec, data->root_dir_sectors, buffer2, data->superblock.BPB_BytesPerSec);
} else if ((device->device & 0xff00) == 0x100) {
hd_read_blocks(device->device & 0xff, first_root_dir_sec, data->root_dir_sectors, buffer2, data->superblock.BPB_BytesPerSec);
}
} else {
dirlen = fat_read_entire_file(device, data->superblock.FatType.Fat32.BPB_RootClus, &buffer2);
}
} else {
// other directory
dirinfo = fat_check_if_exists(device, temp_buff, 0);
dirlen = fat_read_entire_file(device, dirinfo->start_cluster, &buffer2);
}
char long_filename[256];
char temp[13];
unsigned char chksum = 0;
int badcharacters[] = {0x22, 0x2A, 0x2B, 0x2C, 0x2E, 0x2F, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x5B, 0x5C, 0x5D, 0x7C};
int k = 0;
int long_name_len = 0;
int bad;
for (i=0;i<dirlen / 32;i++) {
memset(temp, 0, 13);
struct fat_dir *dir = (struct fat_dir *)&buffer2[i * 32];
if (dir->DIR_Attribute & ATTR_LONG_NAME) {
struct fat_long_dir *ldir = (struct fat_long_dir *)dir;
if (ldir->LDIR_Ord == 0xE5) {
continue;
}
if (ldir->LDIR_Ord == 0x00) {
break;
}
if (ldir->LDIR_Ord & 0x40) {
memset(long_filename, 0, 256);
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 & 0x3F) - 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 & 0x3F) - 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 & 0x3F) - 1) + k + 5 + 6;
break;
}
}
}
int offset = 13 * ((ldir->LDIR_Ord & 0x3F) - 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;
}
bad = 0;
for (j=0;j<11;j++) {
if (dir->DIR_Name[j] < 0x20 && dir->DIR_Name[j] != 0x05) {
bad = 1;
break;
}
if (!(j == 0 && dir->DIR_Name[j] == ' ')) {
for (k=0;k<16;k++) {
if (dir->DIR_Name[j] == badcharacters[k]) {
bad = 1;
break;
}
}
} else {
bad = 1;
}
if (bad == 1) {
break;
}
}
if (bad == 1) {
continue;
}
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 (k=8;k<11;k++) {
if (dir->DIR_Name[k] == ' ') {
break;
} else {
temp[j + 1 + (k - 8)] = dir->DIR_Name[j];
}
}
}
if (((strlen(long_filename) > 0) && (strcmp(long_filename, fname) == 0)) || (strcmp(temp, fname) == 0)) {
if (data->fat == 2) {
dir->DIR_FirstClustHi = 0x0;
dir->DIR_FirstClustLo = 0x0;
} else if (data->fat == 1) {
dir->DIR_FirstClustHi = 0;
dir->DIR_FirstClustLo = 0x0;
} else {
dir->DIR_FirstClustHi = 0;
dir->DIR_FirstClustLo = 0x0;
}
if (strlen(temp_buff) == 0) {
// root directory
if (data->fat != 2) {
first_root_dir_sec = data->superblock.BPB_RsvdSecCnt + (data->superblock.BPB_NumFATs * data->superblock.BPB_FATSz16);
dirlen = data->root_dir_sectors * data->superblock.BPB_BytesPerSec;
if (device->device == 1) {
ramdisk_write_blocks(first_root_dir_sec, data->root_dir_sectors, buffer2, data->superblock.BPB_BytesPerSec);
} else if ((device->device & 0xff00) == 0x100) {
hd_write_blocks(device->device & 0xff, first_root_dir_sec, data->root_dir_sectors, buffer2, data->superblock.BPB_BytesPerSec);
}
} else {
fat_write_entire_file(device, data->superblock.FatType.Fat32.BPB_RootClus, buffer2, dirlen);
}
} else {
// other directory
fat_write_entire_file(device, dirinfo->start_cluster, buffer2, dirlen);
}
free(buffer2);
break;
}
}
}
}
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;
}
}
}
int fat_write_data(struct vfs_device_t *device, struct fat_file_info *info, char *filename, char *buffer, int len, unsigned long long offset) {
struct fat_data *data = (struct fat_data *)device->fs_data;
char *buffer2;
int i = 1;
int j;
int new_start_cluster = 0;
if (info->type == 0) {
// it's a directory...
return 0;
}
if (len > 0) {
if (info->clusterchain != (void *)0) {
free(info->clusterchain);
info->clusterchain = (void *)0;
}
unsigned int *clusterchain;
if (info->start_cluster != 0x0) {
clusterchain = (unsigned int *)dbmalloc(sizeof(unsigned int) * 2, "fat_write_file");
clusterchain[0] = info->start_cluster;
clusterchain[1] = fat_get_cluster_entry(device, info->start_cluster);
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]);
}
if (len + offset > info->file_size + (info->file_size % (data->superblock.BPB_BytesPerSec * data->superblock.BPB_SecPerClus))) {
int needed_clusters = ((len + offset) - (info->file_size + (info->file_size % (data->superblock.BPB_BytesPerSec * data->superblock.BPB_SecPerClus)))) / (data->superblock.BPB_BytesPerSec * data->superblock.BPB_SecPerClus);
if (((len + offset) - (info->file_size + (info->file_size % (data->superblock.BPB_BytesPerSec * data->superblock.BPB_SecPerClus)))) % (data->superblock.BPB_BytesPerSec * data->superblock.BPB_SecPerClus)) {
needed_clusters++;
}
clusterchain = (unsigned int *)realloc(clusterchain, sizeof(unsigned int) * (i + needed_clusters));
for (j=i;j<needed_clusters + i - 1;j++) {
clusterchain[j + 1] = fat_alloc_cluster(device, clusterchain[j]);
}
i = j;
}
} else {
int needed_clusters = (len + offset) / (data->superblock.BPB_BytesPerSec * data->superblock.BPB_SecPerClus);
if ((len + offset) % (data->superblock.BPB_BytesPerSec * data->superblock.BPB_SecPerClus)) {
needed_clusters++;
}
clusterchain = (unsigned int *)dbmalloc(sizeof(unsigned int) * needed_clusters, "fat_write_file");
clusterchain[0] = fat_alloc_cluster(device, 0);
new_start_cluster = clusterchain[0];
info->start_cluster = new_start_cluster;
for (j=0;j<needed_clusters-1;j++) {
clusterchain[j + 1] = fat_alloc_cluster(device, clusterchain[j]);
}
i = j;
}
if (len + offset > info->file_size) {
info->file_size = len + offset;
}
int start_cluster;
int end_cluster;
start_cluster = offset / (data->superblock.BPB_SecPerClus * data->superblock.BPB_BytesPerSec);
end_cluster = offset + len / (data->superblock.BPB_SecPerClus * data->superblock.BPB_BytesPerSec);
if (offset + len % (data->superblock.BPB_SecPerClus * data->superblock.BPB_BytesPerSec)) {
end_cluster++;
}
buffer2 = (char *)dbmalloc((end_cluster - start_cluster) * (data->superblock.BPB_SecPerClus * data->superblock.BPB_BytesPerSec), "fat_write_file");
// read in clusters
for (j=start_cluster;j<end_cluster;j++) {
if (device->device == 1) {
ramdisk_read_blocks(cluster_to_sec(device, clusterchain[j]), data->superblock.BPB_SecPerClus, &buffer2[(j - start_cluster) * data->superblock.BPB_SecPerClus * data->superblock.BPB_BytesPerSec], data->superblock.BPB_BytesPerSec);
} else if ((device->device & 0xff00) == 0x100) {
hd_read_blocks(device->device & 0xff, cluster_to_sec(device, clusterchain[j]), data->superblock.BPB_SecPerClus, &buffer2[(j - start_cluster) * data->superblock.BPB_SecPerClus * data->superblock.BPB_BytesPerSec], data->superblock.BPB_BytesPerSec);
}
}
// modify data
memcpy(&buffer2[offset - (start_cluster * (data->superblock.BPB_SecPerClus * data->superblock.BPB_BytesPerSec))], buffer, len);
// write out clusters
for (j=start_cluster;j<end_cluster;j++) {
if (device->device == 1) {
ramdisk_write_blocks(cluster_to_sec(device, clusterchain[j]), data->superblock.BPB_SecPerClus, &buffer2[(j - start_cluster) * data->superblock.BPB_SecPerClus * data->superblock.BPB_BytesPerSec], data->superblock.BPB_BytesPerSec);
} else if ((device->device & 0xff00) == 0x100) {
hd_write_blocks(device->device & 0xff, cluster_to_sec(device, clusterchain[j]), data->superblock.BPB_SecPerClus, &buffer2[(j - start_cluster) * data->superblock.BPB_SecPerClus * data->superblock.BPB_BytesPerSec], data->superblock.BPB_BytesPerSec);
}
}
free(buffer2);
// update dentry
char *temp_buff = (char *)dbmalloc(strlen(filename) + 1, "fat_write_file");
char *fname;
strcpy(temp_buff, filename);
for (j=strlen(temp_buff)-1;j>=0;j--) {
if (temp_buff[j] == '/') {
temp_buff[j] = '\0';
fname = &temp_buff[j+1];
break;
}
}
int dirlen;
struct fat_file_info *dirinfo;
unsigned int first_root_dir_sec;
if (strlen(temp_buff) == 0) {
// root directory
if (data->fat != 2) {
first_root_dir_sec = data->superblock.BPB_RsvdSecCnt + (data->superblock.BPB_NumFATs * data->superblock.BPB_FATSz16);
dirlen = data->root_dir_sectors * data->superblock.BPB_BytesPerSec;
buffer2 = (char *)dbmalloc(dirlen, "fat_write_file");
if (device->device == 1) {
ramdisk_read_blocks(first_root_dir_sec, data->root_dir_sectors, buffer2, data->superblock.BPB_BytesPerSec);
} else if ((device->device & 0xff00) == 0x100) {
hd_read_blocks(device->device & 0xff, first_root_dir_sec, data->root_dir_sectors, buffer2, data->superblock.BPB_BytesPerSec);
}
} else {
dirlen = fat_read_entire_file(device, data->superblock.FatType.Fat32.BPB_RootClus, &buffer2);
}
} else {
// other directory
dirinfo = fat_check_if_exists(device, temp_buff, 0);
dirlen = fat_read_entire_file(device, dirinfo->start_cluster, &buffer2);
}
char long_filename[256];
char temp[13];
unsigned char chksum = 0;
int badcharacters[] = {0x22, 0x2A, 0x2B, 0x2C, 0x2E, 0x2F, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x5B, 0x5C, 0x5D, 0x7C};
int k = 0;
int long_name_len = 0;
int bad;
for (i=0;i<dirlen / 32;i++) {
memset(temp, 0, 13);
struct fat_dir *dir = (struct fat_dir *)&buffer2[i * 32];
if (dir->DIR_Attribute & ATTR_LONG_NAME) {
struct fat_long_dir *ldir = (struct fat_long_dir *)dir;
if (ldir->LDIR_Ord == 0xE5) {
continue;
}
if (ldir->LDIR_Ord == 0x00) {
free(buffer2);
break;
}
if (ldir->LDIR_Ord & 0x40) {
memset(long_filename, 0, 256);
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 & 0x3F) - 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 & 0x3F) - 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 & 0x3F) - 1) + k + 5 + 6;
break;
}
}
}
int offset = 13 * ((ldir->LDIR_Ord & 0x3F) - 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) {
free(buffer2);
break;
}
bad = 0;
for (j=0;j<11;j++) {
if (dir->DIR_Name[j] < 0x20 && dir->DIR_Name[j] != 0x05) {
bad = 1;
break;
}
if (!(j == 0 && dir->DIR_Name[j] == ' ')) {
for (k=0;k<16;k++) {
if (dir->DIR_Name[j] == badcharacters[k]) {
bad = 1;
break;
}
}
} else {
bad = 1;
}
if (bad == 1) {
break;
}
}
if (bad == 1) {
continue;
}
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[j] = '.';
for (k=8;k<11;k++) {
if (dir->DIR_Name[k] == ' ') {
break;
} else {
temp[j + 1 + (k - 8)] = dir->DIR_Name[j];
}
}
}
if (((strlen(long_filename) > 0) && (strcmp(long_filename, fname) == 0)) || (strcmp(temp, fname) == 0)) {
dir->DIR_FileSize = info->file_size;
dir->DIR_WrtTime = rtc_get_fat_time();
dir->DIR_WrtDate = rtc_get_fat_date();
if (new_start_cluster != 0) {
kprintf("Updating start cluster...\n");
if (data->fat == 2) {
dir->DIR_FirstClustHi = (0x0fff0000 & new_start_cluster) >> 16;
dir->DIR_FirstClustLo = 0xffff & new_start_cluster;
} else if (data->fat == 1) {
dir->DIR_FirstClustHi = 0;
dir->DIR_FirstClustLo = 0xffff & new_start_cluster;
} else {
dir->DIR_FirstClustHi = 0;
dir->DIR_FirstClustLo = 0x0fff & new_start_cluster;
}
}
if (strlen(temp_buff) == 0) {
// root directory
if (data->fat != 2) {
first_root_dir_sec = data->superblock.BPB_RsvdSecCnt + (data->superblock.BPB_NumFATs * data->superblock.BPB_FATSz16);
dirlen = data->root_dir_sectors * data->superblock.BPB_BytesPerSec;
if (device->device == 1) {
ramdisk_write_blocks(first_root_dir_sec, data->root_dir_sectors, buffer2, data->superblock.BPB_BytesPerSec);
} else if ((device->device & 0xff00) == 0x100) {
hd_write_blocks(device->device & 0xff, first_root_dir_sec, data->root_dir_sectors, buffer2, data->superblock.BPB_BytesPerSec);
}
} else {
fat_write_entire_file(device, data->superblock.FatType.Fat32.BPB_RootClus, buffer2, dirlen);
}
} else {
// other directory
fat_write_entire_file(device, dirinfo->start_cluster, buffer2, dirlen);
}
free(buffer2);
break;
}
}
}
}
return len;
}
int fat_read_data(struct vfs_device_t *device, struct fat_file_info *info, char *buffer, int len, unsigned long long 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) {
int i = 1;
int j;
int blocks_read;
if (info->clusterchain == (void *)0) {
info->clusterchain = (unsigned int *)dbmalloc(sizeof(unsigned int) * 2, "fat_read_data 1");
info->clusterchain[0] = info->start_cluster;
info->clusterchain[1] = fat_get_cluster_entry(device, info->start_cluster);
while (!is_EOC(device, info->clusterchain[i])) {
i++;
info->clusterchain = (unsigned int *)realloc(info->clusterchain, sizeof(unsigned int) * (i + 1));
info->clusterchain[i] = fat_get_cluster_entry(device, info->clusterchain[i-1]);
}
}
unsigned int *clusterchain = info->clusterchain;
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 = 0;
char *tempbuffer = (char *)dbmalloc(data->superblock.BPB_SecPerClus * data->superblock.BPB_BytesPerSec, "fat_read_data 2");
if (len + cluster_offset % (data->superblock.BPB_SecPerClus * data->superblock.BPB_BytesPerSec) > 0) {
cluster_count += 1;
}
if (device->device == 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);
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);
return len;
}
}
}
} else if ((device->device & 0xff00) == 0x100) {
for (j=0;j<cluster_count;j++) {
blocks_read = hd_read_blocks((device->device & 0xff), 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);
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);
return len;
}
}
}
}
return len;
} else {
return 0;
}
}
int fat_get_dents(struct vfs_device_t *device, struct fat_file_info *info, char *buffer, int len, unsigned long long 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 *)dbmalloc(dirlen, "fat_get_dents");
if (device->device == 1) {
ramdisk_read_blocks(first_root_dir_sec, data->root_dir_sectors, buffer2, data->superblock.BPB_BytesPerSec);
} else if ((device->device & 0xff00) == 0x100) {
hd_read_blocks((device->device & 0xff), first_root_dir_sec, data->root_dir_sectors, buffer2, data->superblock.BPB_BytesPerSec);
}
dir_buffer = (char *)dbmalloc(data->root_dir_sectors * data->superblock.BPB_BytesPerSec - offset + 1, "fat_get_dents");
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;
}
if (offset >= newsz) {
free(buffer2);
return 0;
}
dir_buffer = (char *)dbmalloc(newsz - offset + 1, "fat_get_dents");
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;
int bad;
int badcharacters[] = {0x22, 0x2A, 0x2B, 0x2C, 0x2E, 0x2F, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x5B, 0x5C, 0x5D, 0x7C};
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 == 0xE5) {
continue;
}
if (ldir->LDIR_Ord == 0x00) {
info->file_size = offset + (i * 32);
break;
}
if (ldir->LDIR_Ord & 0x40) {
memset(long_filename, 0, 256);
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 & 0x3F) - 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 & 0x3F) - 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 & 0x3F) - 1) + k + 5 + 6;
break;
}
}
}
if (!long_name_len) {
13 * ((ldir->LDIR_Ord & 0x3F) - 1) + k + 5 + 6 + 2;
}
int offset = 13 * ((ldir->LDIR_Ord & 0x3F) - 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;
}
bad = 0;
for (j=0;j<11;j++) {
if (dir->DIR_Name[j] < 0x20 && dir->DIR_Name[j] != 0x05) {
bad = 1;
break;
}
if (!(j == 0 && dir->DIR_Name[j] == ' ')) {
for (k=0;k<16;k++) {
if (dir->DIR_Name[j] == badcharacters[k]) {
bad = 1;
break;
}
}
} else {
bad = 1;
}
if (bad == 1) {
break;
}
}
if (bad == 1) {
continue;
}
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 (k=8;k<11;k++) {
if (dir->DIR_Name[k] == ' ') {
break;
} else {
temp[j + 1 + (k - 8)] = 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;
}