Implement filesystem component

This commit is contained in:
Łukasz Magiera 2016-01-09 19:39:48 +01:00
parent 54631f1e79
commit 03f4203fa2
12 changed files with 520 additions and 51 deletions

View File

@ -1,5 +1,5 @@
CC=gcc
CFLAGS=-g -std=c99 -Isrc/lib/lua -Iinclude
CFLAGS=-g -std=gnu99 -Isrc/lib/lua -Iinclude
BUILD = bin/
SOURCE = src/c/

View File

@ -5,6 +5,7 @@ extern char lua_component[];
extern char lua_computer[];
extern char lua_eepromDefault[];
extern char lua_eeprom[];
extern char lua_filesystem[];
extern char lua_init[];
extern char lua_sandbox[];
extern char lua_textgpu[];

View File

@ -1,19 +1,279 @@
#define _XOPEN_SOURCE 500
#include "lupi.h"
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <dirent.h>
#include <ftw.h>
#include <limits.h>
#include <linux/kd.h>
static int l_sleep (lua_State *L) {
int t = lua_tonumber(L, 1);
unsigned int t = lua_tonumber(L, 1);
usleep(t);
return 0;
}
// Filesystem methods
static int l_fs_exists (lua_State *L) {
const char* fname = lua_tostring(L, 1);
if( access( fname, F_OK ) != -1 ) {
lua_pushboolean(L, 1);
} else {
lua_pushboolean(L, 0);
}
return 1;
}
static int l_fs_mkdir (lua_State *L) {
const char* fname = lua_tostring(L, 1);
if( mkdir( fname, 0755 ) != -1 ) {
lua_pushboolean(L, 1);
} else {
lua_pushboolean(L, 0);
}
return 1;
}
static int l_fs_isdir (lua_State *L) {
const char* fname = lua_tostring(L, 1);
struct stat s;
int err = stat(fname, &s);
if(-1 == err) {
lua_pushboolean(L, 0);
} else {
if(S_ISDIR(s.st_mode)) {
lua_pushboolean(L, 1);
} else {
lua_pushboolean(L, 0);
}
}
return 1;
}
static int l_fs_spaceUsed (lua_State *L) {
const char* fname = lua_tostring(L, 1);
struct statvfs s;
if( statvfs(fname, &s) != -1 ) {
lua_pushnumber(L, s.f_bsize * s.f_bfree);
} else {
lua_pushnumber(L, -1);
}
return 1;
}
static int l_fs_open (lua_State *L) {
const char* fname = lua_tostring(L, 1);
const char* mode = lua_tostring(L, 2);
int m = 0;
if(mode[0] == 'r') m = O_RDONLY;
else if(mode[0] == 'w') m = O_WRONLY | O_CREAT /*| O_DIRECT*/;
else if(mode[0] == 'a') m = O_WRONLY | O_APPEND | O_CREAT /*| O_DIRECT*/;
else return 0;
int fd = open(fname, m, 644);
if(fd == -1) return 0;
lua_pushnumber(L, fd);
return 1;
}
static int l_fs_seek (lua_State *L) {
int fd = lua_tonumber(L, 1);
int whence = lua_tonumber(L, 2);
long offset = lua_tonumber(L, 3);
int w = 0;
if(whence == 0) w = SEEK_CUR;
else if(whence == 1) w = SEEK_SET;
else if(whence == 2) w = SEEK_END;
else return 0;
int res = lseek(fd, w, offset);
lua_pushnumber(L, res);
return 1;
}
static int l_fs_write (lua_State *L) {
int fd = lua_tonumber(L, 1);
size_t len = 0;
const char* data = lua_tolstring(L, 2, &len);
//TODO: May not all data be written?
if(write(fd, data, len) == -1) {
lua_pushboolean(L, 0);
} else {
lua_pushboolean(L, 1);
}
return 1;
}
static int l_fs_spaceTotal (lua_State *L) {
const char* fname = lua_tostring(L, 1);
struct statvfs s;
if( statvfs(fname, &s) != -1 ) {
lua_pushnumber(L, s.f_frsize * s.f_blocks);
} else {
lua_pushnumber(L, -1);
}
return 1;
}
static int l_fs_rename (lua_State *L) {
const char* from = lua_tostring(L, 1);
const char* to = lua_tostring(L, 1);
if( rename( from, to ) != -1 ) {
lua_pushboolean(L, 1);
} else {
lua_pushboolean(L, 0);
}
return 1;
}
static int l_fs_list (lua_State *L) {
const char* path = lua_tostring(L, 1);
DIR *dir;
struct dirent *ent;
if ((dir = opendir(path)) != NULL) {
lua_newtable(L);
int n = 1;
while ((ent = readdir(dir)) != NULL) { //TODO: Check if it should be freed
lua_pushstring(L, ent->d_name);
lua_rawseti(L, -2, n++);
}
closedir(dir);
} else {
return 0;
}
return 1;
}
static int l_fs_lastModified (lua_State *L) {
const char* path = lua_tostring(L, 1);
struct stat s;
if( stat(path, &s) != -1 ) {
lua_pushnumber(L, s.st_mtime);
} else {
return 0; //TODO: No error?
}
return 1;
}
static int rm(const char *path, const struct stat *s, int flag, struct FTW *f) {
int status;
int (*rm_func)(const char *);
switch(flag) {
default: rm_func = unlink; break;
case FTW_DP: rm_func = rmdir;
}
if(status = rm_func(path), status != 0)
perror(path);
return status;
}
static int l_fs_remove (lua_State *L) {
const char* path = lua_tostring(L, 1);
if( nftw( path, rm, FOPEN_MAX, FTW_DEPTH )) {
lua_pushboolean(L, 0);
} else {
lua_pushboolean(L, 1);
}
return 1;
}
static int l_fs_close (lua_State *L) {
int fd = lua_tonumber(L, 1);
if(close(fd) == -1) {
lua_pushboolean(L, 0);
} else {
lua_pushboolean(L, 1);
}
return 1;
}
static int l_fs_size (lua_State *L) {
const char* path = lua_tostring(L, 1);
struct stat s;
if( stat(path, &s) != -1 ) {
lua_pushnumber(L, s.st_size);
} else {
return 0; //TODO: No error?
}
return 1;
}
static int l_fs_read (lua_State *L) {
int fd = lua_tonumber(L, 1);
int count = lua_tonumber(L, 2);
void* buf = malloc(count);
size_t res = read(fd, buf, count);
if(res != -1) {
lua_pushlstring(L, buf, res);
free(buf);
return 1;
}
free(buf);
return 0;
}
#ifndef CLOCK_TICK_RATE
#define CLOCK_TICK_RATE 1193180
#endif
//Filesystem end
static int l_beep (lua_State *L) {
int freq = lua_tonumber(L, 1);
int btime = lua_tonumber(L, 2);
int console_fd = -1;
if((console_fd = open("/dev/console", O_WRONLY)) == -1) {
//fprintf(stderr, "Could not open /dev/console for writing.\n");
printf("\a");
return 0;
}
if(ioctl(console_fd, KIOCSOUND, (int)(CLOCK_TICK_RATE/freq)) < 0) {
printf("\a");
return 0;
}
usleep(1000 * btime);
ioctl(console_fd, KIOCSOUND, 0);
close(console_fd);
return 0;
}
void luanative_start(lua_State *L) {
lua_createtable (L, 0, 1);
pushctuple(L, "sleep", l_sleep);
pushctuple(L, "fs_exists", l_fs_exists);
pushctuple(L, "fs_mkdir", l_fs_mkdir);
pushctuple(L, "fs_isdir", l_fs_isdir);
pushctuple(L, "fs_spaceUsed", l_fs_spaceUsed);
pushctuple(L, "fs_open", l_fs_open);
pushctuple(L, "fs_seek", l_fs_seek);
pushctuple(L, "fs_write", l_fs_write);
pushctuple(L, "fs_spaceTotal", l_fs_spaceTotal);
pushctuple(L, "fs_rename", l_fs_rename);
pushctuple(L, "fs_lastModified", l_fs_lastModified);
pushctuple(L, "fs_remove", l_fs_remove);
pushctuple(L, "fs_close", l_fs_close);
pushctuple(L, "fs_size", l_fs_size);
pushctuple(L, "fs_read", l_fs_read);
pushctuple(L, "beep", l_beep);
lua_setglobal(L, "native");
}

View File

@ -13,6 +13,7 @@ void setup_modules(lua_State *L) {
pushstuple(L, "computer", lua_computer);
pushstuple(L, "eeprom", lua_eeprom);
pushstuple(L, "eepromDefault", lua_eepromDefault);
pushstuple(L, "filesystem", lua_filesystem);
pushstuple(L, "sandbox", lua_sandbox);
pushstuple(L, "textgpu", lua_textgpu);
pushstuple(L, "color", lua_util_color);

View File

@ -5,6 +5,15 @@
#include <lauxlib.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
void run_init() {
lua_State *L;
L = luaL_newstate();
@ -14,7 +23,8 @@ void run_init() {
luanative_start (L);
termutils_start (L);
int status = luaL_loadstring(L, lua_init);
//int status = luaL_loadstring(L, lua_init);
int status = luaL_loadbuffer(L, lua_init, strlen(lua_init), "=INIT");
if (status) {
fprintf(stderr, "Couldn't load init: %s\n", lua_tostring(L, -1));
exit(1);

View File

@ -4,39 +4,41 @@ function boot.boot()
local gpu = modules.component.api.proxy(modules.component.api.list("gpu", true)())
local w, h = gpu.getResolution()
local function bsod(err)
local function bsod(...)
gpu.setBackground(0x0000FF)
gpu.setForeground(0xFFFFFF)
gpu.fill(0, 0, w, h, " ")
gpu.fill(1, 1, w, h, " ")
gpu.set(2, 2, "CRITICAL ERROR OCCURED")
gpu.set(2, 3, "Lua BIOS Has failed:")
gpu.set(2, 5, tostring(err))
io.flush()
native.sleep(2000000)
gpu.set(2, 3, "Lua BIOS has failed:")
for n, v in pairs({...}) do
gpu.set(2, 4 + n, tostring(v))
end
gpu.set(2, h-1, "SYSTEM WILL STOP")
gpu.setForeground(0xFFFFFF)
gpu.setBackground(0x000000)
native.sleep(4000000)
os.exit(1)
end
print("r= " .. tostring(w) .. " " .. tostring(h))
gpu.fill(0, 0, w, h, " ")
gpu.set(10, 5, "HHHHHHHHHHHHH")
gpu.set(11, 11, "VVVVVVVVVVVVVVVVV", true)
print("LuPI L2 INIT")
gpu.fill(1, 1, w, h, " ")
gpu.set(1, h - 2, "LuPI L2 INIT")
local code = modules.component.api.invoke(modules.component.api.list("eeprom", true)(), "get")
if not code then
print("No bootcode")
error("No bootcode")
bsod("No bootcode")
end
local f, reason = load(code, "=BIOS", nil, modules.sandbox)
local f, reason = load(code, "=USERBIOS", nil, modules.sandbox)
if not f then
print(reason)
else
local e, reason = pcall(f)
if not e then
bsod(reason)
else
xpcall(f, function(e)
local trace = {}
for s in string.gmatch(debug.traceback(e, 2), "[^\r\n]+") do
trace[#trace + 1] = s
end
print("System quit, Panic")
bsod("System crashed", "Stack traceback:", table.unpack(trace))
end)
bsod("System quit")
end
end

View File

@ -35,7 +35,7 @@ function api.register(address, ctype, proxy, doc)
end
end
modules.computer.api.pushSignal("component_added", address, ctype)
return true
return address
end
function api.unregister(address)
@ -65,7 +65,7 @@ function api.invoke(address, method, ...)
error("No such component")
end
if not components[address].rawproxy[method] then
error("No such method")
error("No such method: " .. tostring(components[address].type) .. "." .. tostring(method))
end
return components[address].rawproxy[method](...)
end

View File

@ -10,4 +10,10 @@ function api.pushSignal(...)
--FIXME: ASAP: Implement
end
function api.beep(freq, time)
if not freq then freq = 1000 end
if not time then time = 0.2 end
native.beep(freq, time * 1000)
end
return computer

158
src/lua/core/filesystem.lua Normal file
View File

@ -0,0 +1,158 @@
--Native side at lnative.c
local filesystem = {}
local function segments(path)
path = path:gsub("\\", "/")
repeat local n; path, n = path:gsub("//", "/") until n == 0
local parts = {}
for part in path:gmatch("[^/]+") do
table.insert(parts, part)
end
local i = 1
while i <= #parts do
if parts[i] == "." then
table.remove(parts, i)
elseif parts[i] == ".." then
table.remove(parts, i)
i = i - 1
if i > 0 then
table.remove(parts, i)
else
i = 1
end
else
i = i + 1
end
end
return parts
end
local function canonical(path)
local result = table.concat(segments(path), "/")
if string.sub(path, 1, 1) == "/" then
return "/" .. result
else
return result
end
end
local function concat(pathA, pathB, ...)
checkArg(1, pathA, "string")
checkArg(2, pathB, "string", "nil")
local function _concat(n, a, b, ...)
if not b then
return a
end
checkArg(n, b, "string")
return _concat(n + 1, a .. "/" .. b, ...)
end
return canonical(_concat(2, pathA, pathB, ...))
end
function filesystem.register(basePath)
checkArg(1, basePath, "string")
if not native.fs_exists(basePath) then
native.fs_mkdir(basePath)
end
if not native.fs_isdir(basePath) then
error("Filesystem root is not a directory!")
end
local function realpath(path)
checkArg(1, path, "string")
return concat(basePath, path)
end
--TODO: checkArg all
local fs = {}
function fs.spaceUsed()
return native.fs_spaceUsed(basePath)
end
function fs.open(path, mode)
checkArg(1, path, "string")
checkArg(2, path, "string", "nil")
local m = "r"
if mode == "a" then m = "a"
elseif mode == "w" then m = "w" end
local fd = native.fs_open(realpath(path), m)
if not fd then
return nil, path
end
return fd
end
function fs.seek(handle, whence, offset) --TODO: Test
checkArg(1, handle, "number")
checkArg(2, whence, "string")
checkArg(3, offset, "number")
local w = 0
if whence == "cur" then w = 0
elseif whence == "set" then w = 1
elseif whence == "end" then w = 2
else error("Invalid whence") end
return native.fs_seek(handle, w, offset or 0)
end
function fs.makeDirectory(path) --TODO: check if creates parrent dirs
checkArg(1, path, "string")
return native.fs_mkdir(realpath(path))
end
function fs.exists(path)
checkArg(1, path, "string")
return native.fs_exists(realpath(path))
end
function fs.isReadOnly()
return false --TODO: Implement
end
function fs.write(handle, value) --TODO: check behaviour on invalid FDs
checkArg(1, handle, "number")
checkArg(2, value, "string")
return native.fs_write(handle, value)
end
function fs.spaceTotal()
return native.fs_spaceTotal(basePath)
end
function fs.isDirectory(path)
checkArg(1, path, "string")
return native.fs_isdir(realpath(path))
end
function fs.rename(from, to)
checkArg(1, from, "string")
checkArg(2, to, "string")
return native.fs_rename(realpath(from), realpath(to))
end
function fs.list(path)
checkArg(1, path, "string")
return native.fs_list(realpath(path)) --TODO: Test, check if dirs get / at end
end
function fs.lastModified(path)
checkArg(1, path, "string")
return native.fs_lastModified(realpath(path))
end
function fs.getLabel()
return --TODO: Implement, use real labels
end
function fs.remove(path) --TODO: TEST!!
checkArg(1, path, "string")
return native.fs_remove(realpath(path))
end
function fs.close(handle)
checkArg(1, handle, "number")
return native.fs_close()
end
function fs.size(path)
checkArg(1, path, "string")
return native.fs_size(realpath(path))
end
function fs.read(handle, count) --FIXME: Hudgeread, fix in general
checkArg(1, handle, "number")
checkArg(2, count, "number")
return native.fs_read(handle, count)
end
function fs.setLabel(value)
checkArg(1, value, "number")
return value --TODO: Implement, use real labels
end
return modules.component.api.register(nil, "filesystem", fs)
end
return filesystem

View File

@ -18,6 +18,7 @@ end
print("LuPI L1 INIT")
modules = {}
deadhooks = {}
local function loadModule(name)
print("LuPI L1 INIT > Load module > " .. name)
@ -26,7 +27,7 @@ local function loadModule(name)
if not moduleCode[name] then
error("No code for module " .. tostring(name))
end
local code, reason = load(moduleCode[name])
local code, reason = load(moduleCode[name], "=Module "..name)
if not code then
print("Failed loading module " .. name .. ": " .. reason)
else
@ -34,6 +35,7 @@ local function loadModule(name)
end
end
function main()
--Load modules
--Utils
loadModule("random")
@ -46,6 +48,7 @@ loadModule("computer")
--Components
loadModule("eeprom")
loadModule("textgpu")
loadModule("filesystem")
--Userspace
loadModule("sandbox")
@ -56,6 +59,23 @@ modules.component.prepare()
modules.computer.prepare()
modules.eeprom.register()
modules.filesystem.register("root")
modules.textgpu.start()
modules.boot.boot()
end
local state, cause = pcall(main)
if not state then
print("LuPI finished with following error:")
print(cause)
end
print("Running shutdown hooks")
for k, hook in ipairs(deadhooks) do
local state, cause = pcall(hook)
if not state then
print("Shutdown hook with following error:")
print(cause)
end
end

View File

@ -160,7 +160,8 @@ sandbox = {
len = utf8.len,
offset = utf8.offset
},
checkArg = checkArg
checkArg = checkArg,
og = _G
}
sandbox._G = sandbox

View File

@ -25,7 +25,8 @@ function textgpu.start()
return --TODO: Maybe?
end
background = modules.color.nearest(color, mapping)
io.write("\x1b[4" .. background .. "m")
io.write("\x1b[4" .. math.floor(background) .. "m")
io.flush()
end
function gpu.setForeground(color, isPaletteIndex)
checkArg(1, color, "number")
@ -33,8 +34,9 @@ function textgpu.start()
if isPaletteIndex then
return --TODO: Maybe?
end
background = modules.color.nearest(color, mapping)
io.write("\x1b[3" .. background .. "m")
foreground = modules.color.nearest(color, mapping)
io.write("\x1b[3" .. math.floor(foreground) .. "m")
io.flush()
end
function gpu.getBackground()
return mapping[background], false
@ -79,6 +81,8 @@ function textgpu.start()
checkArg(2, y, "number")
checkArg(3, value, "string")
checkArg(4, vertical, "boolean", "nil")
x = math.floor(x)
y = math.floor(y)
if not vertical then
io.write("\x1b[" .. y .. ";" .. x .. "H" .. value)
else
@ -87,6 +91,7 @@ function textgpu.start()
io.write(c .. "\x1b[D\x1b[B")
end)
end
io.flush()
return true
end
function gpu.copy(x, y, w, h, tx, ty)
@ -112,10 +117,15 @@ function textgpu.start()
return true
end
io.write("\x1b[?25l") --Disable cursor
gpu.setForeground(0xFFFFFF)
gpu.setBackground(0x000000)
modules.component.api.register(nil, "gpu", gpu)
deadhooks[#deadhooks + 1] = function()
io.write("\x1b[?25h") --Enable cursor on quit
end
end
return textgpu