Newer
Older
tree-os / src / kernel / memory / fileSystem.c
#include <alloc.h>
#include <fileSystem.h>
#include <stdio.h>
#include <util.h>

FileSystemFolder rootFolder = {
    .name = "/", .type = FS_FOLDER, .children = NULL};

void printFolderTree(FileSystemFolder *folder, char *prefix) {
    char *nextPrefix = stringCombine(prefix, "  ");
    for (ListElement *current = folder->children; current;
         current = current->next) {
        FileSystemTreeNode *child = current->data;
        printf("%s%s\n", prefix, child->name);
        if (child->type == FS_FOLDER) {
            if (child->name[0] == '.') {
                continue;
            }
            printFolderTree(current->data, nextPrefix);
        }
    }
    free(nextPrefix);
}

void printFileSystemTree() { printFolderTree(&rootFolder, ""); }

void setupFatInfo(FatInfo *info, FatBootSector *boot, HardDrive *hardDrive) {
    info->bootSector = boot;
    info->rootDirectorySize =
        (boot->rootEntryCount * 32 + boot->sectorSize - 1) / boot->sectorSize;
    info->fatSize = boot->fatSize16Bit ? boot->fatSize16Bit : 0;
    info->totalSectorCount = boot->totalSectorCount16Bit
                                 ? boot->totalSectorCount16Bit
                                 : boot->totalSectorCount32Bit;
    uint32_t dataSectorCount =
        info->totalSectorCount -
        (boot->reservedSectorCount + (boot->FATCount * info->fatSize) +
         info->rootDirectorySize);
    uint32_t clusterCount = dataSectorCount / boot->sectorsPerCluster;
    if (clusterCount < 4085) {
        info->version = 12;
    } else if (clusterCount < 65525) {
        info->version = 16;
    } else {
        info->version = 32;
    }
    info->firstDataSector = boot->reservedSectorCount +
                            (boot->FATCount * info->fatSize) +
                            info->rootDirectorySize;
    info->firstFatSector = boot->reservedSectorCount;
    info->firstRootSector = info->firstDataSector - info->rootDirectorySize;
    info->fat = malloc(info->fatSize * boot->sectorSize);
    info->hardDrive = hardDrive;
    hardDrive->access(hardDrive, info->firstFatSector, info->fat, info->fatSize,
                      0);
    info->knownFolders = malloc(info->fatSize * info->bootSector->sectorSize /
                                sizeof(void *) * info->version / 8);
    info->clusterSize =
        info->bootSector->sectorSize * info->bootSector->sectorsPerCluster;
}

uint32_t getNextCluster(FatInfo *info, uint32_t cluster) {
    switch (info->version) {
    case 16:
        return ((uint16_t *)info->fat)[cluster];
    }
    return 00;
}

bool isClusterEnd(FatInfo *info, uint32_t cluster) {
    switch (info->version) {
    case 16:
        return cluster > 0xFF00;
    }
    return true;
}

char *getFullString(LongNameEntry *name) {
    char *string = malloc(14);
    string[14] = 0;
    for (uint8_t i = 0; i < 5; i++) {
        string[i] = name->name1[i * 2];
    }
    for (uint8_t i = 0; i < 6; i++) {
        string[5 + i] = name->name2[i * 2];
    }
    for (uint8_t i = 0; i < 2; i++) {
        string[11 + i] = name->name3[i * 2];
    }
    return string;
}

char *getCombinedName(ListElement **longName) {
    char *current = "", *new;
    while (*longName != NULL) {
        new = stringCombine(popBeginning(longName), current);
        if (strlen(current)) {
            free(current);
        }
        current = new;
    }
    return current;
}

char *getName(ListElement **longName, FatDirectoryEntry *directory) {
    if (listCount(*longName)) {
        return getCombinedName(longName);
    }
    char *name = malloc(12); // 8:3 short name
    name[11] = 0;
    for (uint8_t i = 0; i < 11; i++) {
        name[i] = directory->name[i];
    }
    return name;
}

uint32_t getClusterSector(FatInfo *info, uint32_t cluster) {
    return info->firstDataSector +
           ((cluster - 2) * info->bootSector->sectorsPerCluster);
}

void scanFolder(FatInfo *info, void *data, FileSystemFolder *parent);

uint16_t getClusterChainLength(FatInfo *info, uint32_t firstCluster) {
    uint16_t i = 0;
    uint32_t currentCluster = firstCluster;
    while (!isClusterEnd(info, currentCluster)) {
        currentCluster = getNextCluster(info, currentCluster);
        i++;
    }
    return i;
}

void *readClusterChain(FatInfo *info, uint32_t firstCluster) {
    uint16_t length = getClusterChainLength(info, firstCluster);
    void *data = malloc(length * info->clusterSize + 1);
    ((uint8_t *)data)[length * info->clusterSize] = 0;
    uint32_t currentCluster = firstCluster;
    for (uint16_t i = 0; i < length; i++) {
        info->hardDrive->access(info->hardDrive,
                                getClusterSector(info, currentCluster),
                                data + i * info->clusterSize,
                                info->bootSector->sectorsPerCluster, 0);
    }
    return data;
}

void setupFolder(char *name, FatDirectoryEntry *directory, FatInfo *info,
                 FileSystemFolder *parent) {
    FileSystemFolder *folder = malloc(sizeof(FileSystemFolder));
    folder->name = name;
    folder->type = FS_FOLDER;
    uint32_t cluster = directory->firstDataClusterLower |
                       directory->fistDataClusterUpper << 16;
    folder->cluster = cluster;
    listAdd(&parent->children, folder);
    if (info->knownFolders[cluster]) {
        folder->children = *info->knownFolders[cluster];
        return;
    }
    if (cluster == 0) {
        folder->children = rootFolder.children;
        return;
    }
    folder->children = malloc(sizeof(ListElement **));
    info->knownFolders[cluster] = &folder->children;
    folder->children = NULL;
    void *data = readClusterChain(info, cluster);
    scanFolder(info, data, folder);
    free(data);
}

void setupFile(char *name, FatDirectoryEntry *directory,
               FileSystemFolder *parent) {
    FileSystemFile *file = malloc(sizeof(FileSystemFile));
    uint32_t cluster = directory->firstDataClusterLower |
                       directory->fistDataClusterUpper << 16;
    file->cluster = cluster;
    file->name = name;
    file->type = FS_FILE;
    file->subType = 0;
    file->size = directory->size;
    listAdd(&parent->children, file);
}

void setupChild(char *name, FatDirectoryEntry *directoryEntry, FatInfo *info,
                FileSystemFolder *parent) {
    if (directoryEntry->attributes & FAT_ATTRIBUTE_VOLUME_ID) {
        info->volumeID = name;
        return;
    }
    if (directoryEntry->attributes & FAT_ATTRIBUTE_DIRECTORY) {
        setupFolder(name, directoryEntry, info, parent);
    } else {
        setupFile(name, directoryEntry, parent);
    }
}

void scanFolder(FatInfo *info, void *data, FileSystemFolder *parent) {
    FatDirectoryEntry *directory = data;
    ListElement *longName = NULL;
    uint32_t i;
    for (i = 0; i < info->bootSector->sectorSize *
                        info->bootSector->sectorsPerCluster /
                        sizeof(FatDirectoryEntry);
         i++) {
        if (directory[i].name[0] == 0) {
            break;
        }
        if (directory[i].name[0] == (char)0xE5 ||
            directory[i].name[0] == (char)0xFF) {
            continue;
        }
        if (directory[i].attributes == FAT_ATTRIBUTE_LONG_NAME) {
            listAdd(&longName, getFullString((void *)&(directory[i])));
            continue;
        }
        setupChild(getName(&longName, &directory[i]), &directory[i], info,
                   parent);
    }
    listClear(&longName, true);
}

FatInfo *info;

void mountDisk(HardDrive *drive) {
    if (!drive) {
        printf("no drive available to mount\n");
        yields();
        return;
    }
    void *bootSector = malloc(512);
    drive->access(drive, 0, bootSector, 1, 0);
    FatBootSector *boot = bootSector;
    info = malloc(sizeof(FatInfo));
    setupFatInfo(info, boot, drive);
    rootFolder.children = 0;
    void *data = malloc(info->clusterSize * info->rootDirectorySize);
    info->hardDrive->access(info->hardDrive, info->firstRootSector, data,
                            info->rootDirectorySize, 0);
    scanFolder(info, data, &rootFolder);
    free(data);
}

File *readFile(char *path) {
    if (path[0] != '/') {
        printf("relative file paths not implemented yet!\n");
        return NULL;
    }
    if (rootFolder.children == NULL) {
        return NULL;
    }
    char *currentName = path + 1;
    FileSystemFolder *currentFolder = &rootFolder;
    while (strlen(currentName)) {
        foreach (currentFolder->children) {
            FileSystemTreeNode *node = current->data;
            uint16_t i;
            for (i = 0; currentName[i] != '/' && currentName[i] != 0; i++) {
                if (node->name[i] != currentName[i]) {
                    i = 0;
                    break;
                }
            }
            if (i != strlen(node->name) || i == 0) {
                continue;
            }
            if (currentName + i == path + strlen(path)) {
                File *file = malloc(sizeof(File));
                file->file = (void *)node;
                file->absolutePath = path;
                file->path = path;
                file->data = readClusterChain(info, file->file->cluster);
                return file;
            }
            currentFolder = (void *)node;
            currentName += strlen(node->name) + 1;
        }
    }
    return NULL;
}