Added filesystem module

This commit is contained in:
rxi
2016-09-29 19:22:29 +01:00
parent a20901c2ba
commit 2689bfc2fa
4 changed files with 501 additions and 5 deletions

460
src/filesystem.c Normal file
View File

@@ -0,0 +1,460 @@
/**
* Copyright (c) 2016 rxi
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the MIT license. See LICENSE for details.
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <dirent.h>
#include "luaobj.h"
#include "lib/microtar/microtar.h"
#include "lib/dmt/dmt.h"
#include "filesystem.h"
#define MAX_MOUNTS 8
#define MAX_PATH 256
enum {
FILESYSTEM_TNONE,
FILESYSTEM_TREG,
FILESYSTEM_TDIR,
};
typedef struct Mount Mount;
struct Mount {
void (*unmount)(Mount *mnt);
int (*exists)(Mount *mnt, const char *filename);
int (*isFile)(Mount *mnt, const char *filename);
int (*isDirectory)(Mount *mnt, const char *filename);
void *(*read)(Mount *mnt, const char *filename, int *size);
void *udata;
char path[MAX_PATH];
};
int filesystem_mountIdx;
Mount filesystem_mounts[MAX_MOUNTS];
#define FOREACH_MOUNT(var)\
for (Mount *var = &filesystem_mounts[filesystem_mountIdx - 1];\
var >= filesystem_mounts;\
var--)
static int get_file_type(const char *filename) {
/* The use of `stat` is intentionally avoided here, a stat call seems to
* block for a long time on DOS -- over 500ms in Dosbox at 26800 cycles */
DIR *dir = opendir(filename);
if (dir) {
closedir(dir);
return FILESYSTEM_TDIR;
}
FILE *fp = fopen(filename, "rb");
if (fp) {
fclose(fp);
return FILESYSTEM_TREG;
}
return FILESYSTEM_TNONE;
}
static int concat_path(char *dst, const char *dir, const char *filename) {
int dirlen = strlen(dir);
int filenamelen = strlen(filename);
/* Fail if the resultant path would overflow buffer */
if (dirlen + filenamelen + 2 > MAX_PATH) {
return FILESYSTEM_EFAILURE;
}
/* Write full name to buffer and return ok */
if ( dir[dirlen - 1] == '/' ) {
sprintf(dst, "%s%s", dir, filename);
} else {
sprintf(dst, "%s/%s", dir, filename);
}
return FILESYSTEM_ESUCCESS;
}
static int concat_and_get_file_type(const char *dir, const char *filename) {
char buf[MAX_PATH];
/* Make fullpath */
int err = concat_path(buf, dir, filename);
if (err) {
return err;
}
/* Stat */
return get_file_type(buf);
}
/*==================*/
/* Directory Mount */
/*==================*/
static void dir_unmount(Mount *mnt) {
/* Intentionally empty */
}
static int dir_exists(Mount *mnt, const char *filename) {
return concat_and_get_file_type(mnt->path, filename) != FILESYSTEM_TNONE;
}
static int dir_isFile(Mount *mnt, const char *filename) {
return concat_and_get_file_type(mnt->path, filename) == FILESYSTEM_TREG;
}
static int dir_isDirectory(Mount *mnt, const char *filename) {
return concat_and_get_file_type(mnt->path, filename) == FILESYSTEM_TDIR;
}
static void* dir_read(Mount *mnt, const char *filename, int *size) {
char buf[MAX_PATH];
/* Make fullpath */
int err = concat_path(buf, mnt->path, filename);
if (err) {
return NULL;
}
/* Open file */
FILE *fp = fopen(buf, "rb");
if (!fp) {
return NULL;
}
/* Get size */
fseek(fp, 0, SEEK_END);
*size = ftell(fp);
fseek(fp, 0, SEEK_SET);
/* Load data */
void *p = dmt_malloc(*size);
if (!p) {
return NULL;
}
fread(p, 1, *size, fp);
return p;
}
static int dir_mount(Mount *mnt, const char *path) {
/* Check the path is actually a directory */
if ( get_file_type(path) != FILESYSTEM_TDIR ) {
return FILESYSTEM_EFAILURE;
}
/* Init mount */
mnt->udata = NULL;
mnt->unmount = dir_unmount;
mnt->exists = dir_exists;
mnt->isFile = dir_isFile;
mnt->isDirectory = dir_isDirectory;
mnt->read = dir_read;
/* Return ok */
return FILESYSTEM_ESUCCESS;
}
/*==================*/
/* Tar Mount */
/*==================*/
static int tar_find(mtar_t *tar, const char *filename, mtar_header_t *h) {
int err;
char buf[MAX_PATH + 1];
/* Try to match */
err = mtar_find(tar, filename, h);
if (err != MTAR_ENOTFOUND) {
return err;
}
/* Try to match with "/" at the end */
strcpy(buf, filename);
strcat(buf, "/");
return mtar_find(tar, buf, h);
}
static void tar_unmount(Mount *mnt) {
mtar_t *tar = mnt->udata;
mtar_close(tar);
dmt_free(tar);
}
static int tar_exists(Mount *mnt, const char *filename) {
mtar_t *tar = mnt->udata;
return tar_find(tar, filename, NULL) == MTAR_ESUCCESS;
}
static int tar_isFile(Mount *mnt, const char *filename) {
mtar_t *tar = mnt->udata;
int err;
mtar_header_t h;
err = mtar_find(tar, filename, &h);
if (err) {
return 0;
}
return h.type == MTAR_TREG;
}
static int tar_isDirectory(Mount *mnt, const char *filename) {
mtar_t *tar = mnt->udata;
int err;
mtar_header_t h;
err = tar_find(tar, filename, &h);
if (err) {
return 0;
}
return h.type == MTAR_TDIR;
}
static void* tar_read(Mount *mnt, const char *filename, int *size) {
mtar_t *tar = mnt->udata;
int err;
mtar_header_t h;
/* Find and load header for file */
err = mtar_find(tar, filename, &h);
if (err) {
return 0;
}
/* Allocate and read data, set size and return */
char *p = dmt_malloc(h.size);
err = mtar_read_data(tar, p, h.size);
if (err) {
dmt_free(p);
return NULL;
}
*size = h.size;
return p;
}
static int tar_mount(Mount *mnt, const char *path) {
mtar_t *tar = dmt_malloc( sizeof(*tar) );
/* Try to init tar */
int err = mtar_open(tar, path, "r");
if (err) {
dmt_free(tar);
return FILESYSTEM_EFAILURE;
}
/* Init mount */
mnt->udata = tar;
mnt->unmount = tar_unmount;
mnt->exists = tar_exists;
mnt->isFile = tar_isFile;
mnt->isDirectory = tar_isDirectory;
mnt->read = tar_read;
/* Return ok */
return FILESYSTEM_ESUCCESS;
}
/*==================*/
/* Filesystem */
/*==================*/
const char* filesystem_strerror(int err) {
switch (err) {
case FILESYSTEM_ESUCCESS: return "success";
case FILESYSTEM_EFAILURE: return "failure";
}
return "unknown error";
}
void filesystem_deinit(void) {
FOREACH_MOUNT(mnt) {
mnt->unmount(mnt);
}
filesystem_mountIdx = 0;
}
int filesystem_mount(const char *path) {
/* Check path length is ok */
if ( strlen(path) >= MAX_PATH ) {
return FILESYSTEM_EFAILURE;
}
/* Check path isn't already mounted */
FOREACH_MOUNT(m) {
if ( !strcmp(m->path, path) ) {
return FILESYSTEM_EFAILURE;
}
}
/* Get mount slot */
if (filesystem_mountIdx >= MAX_MOUNTS) {
return FILESYSTEM_EFAILURE;
}
Mount *mnt = &filesystem_mounts[filesystem_mountIdx++];
/* Copy path name */
strcpy(mnt->path, path);
/* Try to mount path */
if ( tar_mount(mnt, path) == FILESYSTEM_ESUCCESS ) goto success;
if ( dir_mount(mnt, path) == FILESYSTEM_ESUCCESS ) goto success;
/* Fail */
filesystem_mountIdx--;
return FILESYSTEM_EFAILURE;
success:
return FILESYSTEM_ESUCCESS;
}
int filesystem_unmount(const char *path) {
FOREACH_MOUNT(mnt) {
if ( !strcmp(mnt->path, path) ) {
/* Unmount */
mnt->unmount(mnt);
/* Shift remaining mounts to fill gap and decrement idx */
int idx = mnt - filesystem_mounts;
memmove(mnt, mnt + 1, (filesystem_mountIdx - idx - 1) * sizeof(Mount));
filesystem_mountIdx--;
return FILESYSTEM_ESUCCESS;
}
}
return FILESYSTEM_EFAILURE;
}
int filesystem_exists(const char *filename) {
FOREACH_MOUNT(mnt) {
if ( mnt->exists(mnt, filename) ) {
return 1;
}
}
return 0;
}
int filesystem_isFile(const char *filename) {
FOREACH_MOUNT(mnt) {
if ( mnt->exists(mnt, filename) ) {
return mnt->isFile(mnt, filename);
}
}
return 0;
}
int filesystem_isDirectory(const char *filename) {
FOREACH_MOUNT(mnt) {
if ( mnt->exists(mnt, filename) ) {
return mnt->isDirectory(mnt, filename);
}
}
return 0;
}
void* filesystem_read(const char *filename, int *size) {
FOREACH_MOUNT(mnt) {
if ( mnt->exists(mnt, filename) && mnt->isFile(mnt, filename) ) {
return mnt->read(mnt, filename, size);
}
}
return NULL;
}
void filesystem_free(void *ptr) {
dmt_free(ptr);
}
/*==================*/
/* Lua Binds */
/*==================*/
int l_filesystem_mount(lua_State *L) {
const char *path = luaL_checkstring(L, 1);
int err = filesystem_mount(path);
if (err) {
lua_pushnil(L);
lua_pushstring(L, filesystem_strerror(err));
return 2;
}
lua_pushboolean(L, 1);
return 1;
}
int l_filesystem_unmount(lua_State *L) {
const char *path = luaL_checkstring(L, 1);
int err = filesystem_unmount(path);
if (err) {
lua_pushnil(L);
lua_pushstring(L, filesystem_strerror(err));
return 2;
}
lua_pushboolean(L, 1);
return 1;
}
int l_filesystem_exists(lua_State *L) {
const char *filename = luaL_checkstring(L, 1);
lua_pushboolean( L, filesystem_exists(filename) );
return 1;
}
int l_filesystem_isFile(lua_State *L) {
const char *filename = luaL_checkstring(L, 1);
lua_pushboolean( L, filesystem_isFile(filename) );
return 1;
}
int l_filesystem_isDirectory(lua_State *L) {
const char *filename = luaL_checkstring(L, 1);
lua_pushboolean( L, filesystem_isDirectory(filename) );
return 1;
}
int l_filesystem_read(lua_State *L) {
const char *filename = luaL_checkstring(L, 1);
int size;
void *data = filesystem_read(filename, &size);
if (!data) {
luaL_error(L, "could not read file");
}
lua_pushlstring(L, data, size);
filesystem_free(data);
return 1;
}
int luaopen_filesystem(lua_State *L) {
luaL_Reg reg[] = {
{ "mount", l_filesystem_mount },
{ "unmount", l_filesystem_unmount },
{ "exists", l_filesystem_exists },
{ "isFile", l_filesystem_isFile },
{ "isDirectory", l_filesystem_isDirectory },
{ "read", l_filesystem_read },
{ 0, 0 },
};
luaL_newlib(L, reg);
return 1;
}

30
src/filesystem.h Normal file
View File

@@ -0,0 +1,30 @@
/**
* Copyright (c) 2016 rxi
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the MIT license. See LICENSE for details.
*/
#ifndef FILESYSTEM_H
#define FILESYSTEM_H
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
enum {
FILESYSTEM_ESUCCESS = 0,
FILESYSTEM_EFAILURE = -1,
};
const char* filesystem_strerror(int err);
void filesystem_deinit(void);
int filesystem_mount(const char *path);
int filesystem_unmount(const char *path);
int filesystem_exists(const char *filename);
int filesystem_isFile(const char *filename);
int filesystem_isDirectory(const char *filename);
void* filesystem_read(const char *filename, int *size);
void filesystem_free(void *ptr);
#endif

View File

@@ -20,6 +20,7 @@ int luaopen_image(lua_State *L);
int luaopen_quad(lua_State *L);
int luaopen_font(lua_State *L);
int luaopen_system(lua_State *L);
int luaopen_filesystem(lua_State *L);
int luaopen_graphics(lua_State *L);
int luaopen_timer(lua_State *L);
int luaopen_keyboard(lua_State *L);
@@ -50,11 +51,12 @@ int luaopen_love(lua_State *L) {
/* Init submodules */
struct { char *name; int (*fn)(lua_State *L); } mods[] = {
{ "system", luaopen_system },
{ "graphics", luaopen_graphics },
{ "timer", luaopen_timer },
{ "keyboard", luaopen_keyboard },
{ "mouse", luaopen_mouse },
{ "system", luaopen_system },
{ "filesystem", luaopen_filesystem },
{ "graphics", luaopen_graphics },
{ "timer", luaopen_timer },
{ "keyboard", luaopen_keyboard },
{ "mouse", luaopen_mouse },
{ 0 },
};
for (i = 0; mods[i].name; i++) {

View File

@@ -15,6 +15,7 @@
#include "vga.h"
#include "luaobj.h"
#include "keyboard.h"
#include "filesystem.h"
#include "mouse.h"
#include "image.h"
#include "palette.h"
@@ -27,9 +28,11 @@ static void deinit(void) {
vga_deinit();
keyboard_deinit();
lua_close(L);
filesystem_deinit();
dmt_dump(stdout);
}
static int onLuaPanic(lua_State *L) {
vga_deinit();
const char *err = lua_tostring(L, -1);
@@ -44,6 +47,7 @@ int main(void) {
/* Init everything */
atexit(deinit);
filesystem_mount("."); /* Mount cwd: temporary */
vga_init();
palette_init();
keyboard_init();