more complete fs impl

This commit is contained in:
2026-03-30 18:05:16 +02:00
parent 2b72e47ba5
commit 6086208879
4 changed files with 294 additions and 36 deletions

View File

@@ -382,6 +382,8 @@ int main(int argc, char **argv) {
nn_Component *eepromCard = nn_createVEEPROM(u, "eeprom", &veeprom, &nn_defaultEEPROMs[3]);
nn_Component *managedfs = ncl_createFilesystem(u, "mainFS", "data/OpenOS", &nn_defaultFilesystems[3], true);
size_t ramTotal = 0;
ramTotal += nn_ramSizes[5];
@@ -426,6 +428,7 @@ restart:;
nn_mountComponent(c, ocelotCard, -1);
nn_mountComponent(c, eepromCard, 0);
nn_mountComponent(c, managedfs, 1);
while(true) {
if(WindowShouldClose()) break;

View File

@@ -2,7 +2,6 @@
#include "ncomplib.h"
#include <stdlib.h>
#include <string.h>
#include <stdatomic.h>
static bool ncl_defaultHandler(ncl_VFSRequest *request);
@@ -74,15 +73,19 @@ bool ncl_defaultHandler(ncl_VFSRequest *request) {
return true;
}
if(request->action == NCL_VFS_READDIR) {
while(1) {
DIR *d = request->readdir.dir;
struct dirent *ent = readdir(d);
if(ent == NULL) {
request->readdir.name = NULL;
} else if(strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0) {
continue;
} else {
strncpy(request->readdir.name, ent->d_name, NN_MAX_PATH-1);
}
return true;
}
}
if(request->action == NCL_VFS_STAT) {
struct stat s;
if(stat(request->stat.path, &s) != 0) {
@@ -358,10 +361,10 @@ typedef struct ncl_FSState {
size_t spaceUsed;
// if 0, needs to be recomputed
size_t realSpaceUsed;
atomic_size_t usage;
size_t usage;
bool isReadonly;
// all the arrays
FILE *fds[NN_MAX_OPENFILES];
void *fds[NN_MAX_OPENFILES];
void *dirs[NN_MAX_OPENFILES];
char label[NN_MAX_LABEL];
size_t labellen;
@@ -372,7 +375,7 @@ typedef struct ncl_DriveState {
nn_Lock *lock;
nn_Drive conf;
bool isReadonly;
atomic_size_t usage;
size_t usage;
size_t lastSector;
char *path;
FILE *file;
@@ -385,22 +388,18 @@ typedef struct ncl_EEState {
nn_Lock *lock;
nn_EEPROM conf;
bool isReadonly;
atomic_size_t usage;
size_t usage;
char *codepath;
char *datapath;
char label[NN_MAX_LABEL];
size_t labellen;
} ncl_EEState;
static void ncl_fixPath(ncl_VFS vfs, const char *path, char buf[NN_MAX_PATH]) {
size_t len = 0;
while(path[len]) {
if(len == NN_MAX_PATH) break;
if(path[len] == '/') buf[len] = vfs.pathsep;
else buf[len] = path[len];
len++;
static void ncl_fixPath(ncl_FSState *fs, const char *path, char buf[NN_MAX_PATH]) {
snprintf(buf, NN_MAX_PATH, "%s%c%s", fs->path, fs->vfs.pathsep, path);
for(size_t i = 0; buf[i]; i++) {
if(buf[i] == '/') buf[i] = fs->vfs.pathsep;
}
buf[len] = '\0';
}
// assumes locked
@@ -420,6 +419,19 @@ static size_t ncl_fsGetRealUsage(ncl_FSState *fs) {
return fs->realSpaceUsed;
}
// -1 on too many
static int ncl_findFileDesc(void *fds[NN_MAX_OPENFILES]) {
for(int i = 0; i < NN_MAX_OPENFILES; i++) {
if(fds[i] == NULL) return i;
}
return -1;
}
static void *ncl_getFile(void *fds[NN_MAX_OPENFILES], int fd) {
if(fd < 0 || fd > NN_MAX_OPENFILES) return NULL;
return fds[fd];
}
static nn_Exit ncl_fsHandler(nn_FSRequest *req) {
ncl_FSState *state = req->state;
nn_Context *ctx = req->ctx;
@@ -431,22 +443,243 @@ static nn_Exit ncl_fsHandler(nn_FSRequest *req) {
if(state->fds[i] != NULL) ncl_closefile(state->vfs, state->fds[i]);
if(state->dirs[i] != NULL) ncl_closedir(state->vfs, state->dirs[i]);
}
nn_destroyLock(ctx, state->lock);
nn_strfree(ctx, state->path);
nn_free(ctx, state, sizeof(*state));
return NN_OK;
}
if(req->action == NN_FS_SPACEUSED) {
nn_lock(ctx, state->lock);
state->usage++;
req->spaceUsed = ncl_fsGetUsage(state);
nn_unlock(ctx, state->lock);
return NN_OK;
}
if(req->action == NN_FS_GETLABEL) {
nn_lock(ctx, state->lock);
state->usage++;
size_t len = state->labellen;
if(len > req->getlabel.len) len = req->getlabel.len;
memcpy(req->getlabel.buf, state->label, len);
req->getlabel.len = len;
nn_unlock(ctx, state->lock);
return NN_OK;
}
if(req->action == NN_FS_SETLABEL) {
nn_lock(ctx, state->lock);
state->usage++;
size_t len = req->setlabel.len;
if(len > NN_MAX_LABEL) len = NN_MAX_LABEL;
memcpy(state->label, req->setlabel.buf, len);
state->labellen = len;
nn_unlock(ctx, state->lock);
return NN_OK;
}
if(req->action == NN_FS_ISRO) {
req->isReadonly = state->isReadonly;
return NN_OK;
}
if(req->action == NN_FS_OPEN) {
nn_lock(ctx, state->lock);
state->usage++;
int fd = ncl_findFileDesc(state->fds);
if(fd < 0) {
nn_unlock(ctx, state->lock);
nn_setError(C, "too many files");
return NN_EBADCALL;
}
const char *mode = req->open.mode;
if(mode[0] != 'r' && state->isReadonly) {
nn_unlock(ctx, state->lock);
nn_setError(C, "is readonly");
return NN_EBADCALL;
}
char path[NN_MAX_PATH];
ncl_fixPath(state, req->open.path, path);
void *file = ncl_openfile(state->vfs, path, mode);
if(file == NULL) {
nn_unlock(ctx, state->lock);
nn_setError(C, req->open.path);
return NN_EBADCALL;
}
state->fds[fd] = file;
req->fd = fd;
nn_unlock(ctx, state->lock);
return NN_OK;
}
if(req->action == NN_FS_CLOSE) {
nn_lock(ctx, state->lock);
int fd = req->fd;
void *file = ncl_getFile(state->fds, fd);
if(file == NULL) {
nn_unlock(ctx, state->lock);
nn_setError(C, "bad file descriptor");
return NN_EBADCALL;
}
state->fds[fd] = NULL;
volatile ncl_VFS vfs = state->vfs;
nn_unlock(ctx, state->lock);
// out of lock for the most minimal of performance
ncl_closefile(vfs, file);
return NN_OK;
}
if(req->action == NN_FS_READ) {
nn_lock(ctx, state->lock);
state->usage++;
void *file = ncl_getFile(state->fds, req->fd);
if(file == NULL) {
nn_unlock(ctx, state->lock);
nn_setError(C, "bad file descriptor");
return NN_EBADCALL;
}
if(!ncl_readfile(state->vfs, file, req->read.buf, &req->read.len)) {
req->read.buf = NULL;
}
nn_unlock(ctx, state->lock);
return NN_OK;
}
if(req->action == NN_FS_WRITE) {
nn_lock(ctx, state->lock);
state->usage++;
void *file = ncl_getFile(state->fds, req->fd);
if(file == NULL) {
nn_unlock(ctx, state->lock);
nn_setError(C, "bad file descriptor");
return NN_EBADCALL;
}
size_t spaceRemaining = state->conf.spaceTotal - ncl_fsGetUsage(state);
// inaccurate...
if(spaceRemaining < req->write.len) {
nn_unlock(ctx, state->lock);
nn_setError(C, "out of space");
return NN_EBADCALL;
}
bool ok = ncl_writefile(state->vfs, file, req->write.buf, req->write.len);
nn_unlock(ctx, state->lock);
if(ok) return NN_OK;
nn_setError(C, "write failed");
return NN_EBADCALL;
}
if(req->action == NN_FS_SEEK) {
nn_lock(ctx, state->lock);
void *file = ncl_getFile(state->fds, req->fd);
if(file == NULL) {
nn_unlock(ctx, state->lock);
nn_setError(C, "bad file descriptor");
return NN_EBADCALL;
}
bool ok = ncl_seekfile(state->vfs, file, req->seek.whence, &req->seek.off);
nn_unlock(ctx, state->lock);
if(ok) return NN_OK;
nn_setError(C, "seek failed");
return NN_EBADCALL;
}
if(req->action == NN_FS_OPENDIR) {
nn_lock(ctx, state->lock);
state->usage++;
int fd = ncl_findFileDesc(state->dirs);
if(fd < 0) {
nn_unlock(ctx, state->lock);
nn_setError(C, "too many directories listed simultaneously");
return NN_EBADCALL;
}
char path[NN_MAX_PATH];
ncl_fixPath(state, req->open.path, path);
void *dir = ncl_opendir(state->vfs, path);
if(dir == NULL) {
nn_unlock(ctx, state->lock);
nn_setError(C, req->opendir);
return NN_EBADCALL;
}
state->dirs[fd] = dir;
req->fd = fd;
nn_unlock(ctx, state->lock);
return NN_OK;
}
if(req->action == NN_FS_CLOSEDIR) {
nn_lock(ctx, state->lock);
int fd = req->fd;
void *file = ncl_getFile(state->dirs, fd);
if(file == NULL) {
nn_unlock(ctx, state->lock);
nn_setError(C, "bad file descriptor");
return NN_EBADCALL;
}
state->dirs[fd] = NULL;
volatile ncl_VFS vfs = state->vfs;
nn_unlock(ctx, state->lock);
// out of lock for the most minimal of performance
ncl_closedir(vfs, file);
return NN_OK;
}
if(req->action == NN_FS_READDIR) {
nn_lock(ctx, state->lock);
int fd = req->fd;
void *dir = ncl_getFile(state->dirs, fd);
if(dir == NULL) {
nn_unlock(ctx, state->lock);
nn_setError(C, "bad file descriptor");
return NN_EBADCALL;
}
char name[NN_MAX_PATH];
if(!ncl_readdir(state->vfs, dir, name)) {
nn_unlock(ctx, state->lock);
req->readdir.buf = NULL;
return NN_OK;
}
char path[NN_MAX_PATH];
snprintf(path, NN_MAX_PATH, "%s%c%s%c%s", state->path, state->vfs.pathsep, req->readdir.dirpath, state->vfs.pathsep, name);
ncl_Stat s;
if(!ncl_stat(state->vfs, path, &s)) s.isDirectory = false;
if(s.isDirectory) snprintf(req->readdir.buf, req->readdir.len, "%s/", name);
else snprintf(req->readdir.buf, req->readdir.len, "%s", name);
req->readdir.len = strlen(req->readdir.buf);
nn_unlock(ctx, state->lock);
return NN_OK;
}
if(C) nn_setError(C, "not implemented yet");
return NN_EBADCALL;
}
nn_Component *ncl_createFilesystem(nn_Universe *universe, const char *address, const char *path, const nn_Filesystem *fs, bool isReadonly);
nn_Component *ncl_createFilesystem(nn_Universe *universe, const char *address, const char *path, const nn_Filesystem *fs, bool isReadonly) {
nn_Context *ctx = nn_getUniverseContext(universe);
ncl_FSState *state = nn_alloc(ctx, sizeof(*state));
if(state == NULL) return NULL;
state->ctx = ctx;
state->lock = nn_createLock(ctx);
if(state->lock == NULL) {
nn_free(ctx, state, sizeof(*state));
return NULL;
}
state->path = nn_strdup(ctx, path);
if(state->path == NULL) {
nn_destroyLock(ctx, state->lock);
nn_free(ctx, state, sizeof(*state));
return NULL;
}
state->vfs = ncl_defaultFS;
state->usage = 0;
state->isReadonly = isReadonly;
state->conf = *fs;
state->labellen = 0;
state->realSpaceUsed = 0;
state->spaceUsed = 0;
for(size_t i = 0; i < NN_MAX_OPENFILES; i++) {
state->fds[i] = NULL;
state->dirs[i] = NULL;
}
nn_Component *c = nn_createFilesystem(universe, address, fs, state, ncl_fsHandler);
if(c == NULL) {
nn_strfree(ctx, state->path);
nn_destroyLock(ctx, state->lock);
nn_free(ctx, state, sizeof(*state));
return NULL;
}
return c;
}
nn_Component *ncl_createDrive(nn_Universe *universe, const char *address, const char *path, const nn_Drive *drive, bool isReadonly);
nn_Component *ncl_createEEPROM(nn_Universe *universe, const char *address, const char *codepath, const char *datapath, bool isReadonly);
@@ -619,8 +852,10 @@ bool ncl_makeReadonly(nn_Component *component) {
void *state = nn_getComponentState(component);
if(strcmp(ty, NCL_FS) == 0) {
ncl_FSState *fs = state;
nn_lock(fs->ctx, fs->lock);
fs->isReadonly = true;
fs->usage++;
nn_unlock(fs->ctx, fs->lock);
return true;
}
if(strcmp(ty, NCL_DRIVE) == 0) {

View File

@@ -777,6 +777,7 @@ typedef struct nn_Component {
char *type;
char *internalID;
void *state;
void *classState;
nn_ComponentHandler *handler;
nn_Arena methodArena;
nn_HashMap methodsMap;
@@ -1457,6 +1458,7 @@ void nn_dropComponentN(nn_Component *c, size_t n) {
nn_ComponentRequest req;
req.state = c->state;
req.classState = c->classState;
req.ctx = ctx;
req.computer = NULL;
req.action = NN_COMP_DROP;
@@ -1477,6 +1479,10 @@ void nn_setComponentState(nn_Component *c, void *state) {
c->state = state;
}
void nn_setComponentClassState(nn_Component *c, void *state) {
c->classState = state;
}
nn_Exit nn_setComponentMethods(nn_Component *c, const nn_Method *methods) {
size_t len = 0;
while(methods[len].name != NULL) len++;
@@ -1529,6 +1535,10 @@ void *nn_getComponentState(nn_Component *c) {
return c->state;
}
void *nn_getComponentClassState(nn_Component *c) {
return c->classState;
}
// counts how many methods are registered. May return too many if some of them are not enabled.
size_t nn_countComponentMethods(nn_Component *c) {
return c->methodCount;
@@ -1697,11 +1707,13 @@ nn_Exit nn_invokeComponent(nn_Computer *computer, const char *compAddress, const
req.ctx = &c->universe->ctx;
req.computer = computer;
req.state = c->state;
req.classState = c->classState;
req.action = NN_COMP_INVOKE;
req.methodIdx = m->idx;
req.returnCount = 0;
nn_Exit e = c->handler(&req);
if(e) {
if(e != NN_EBADCALL) nn_setErrorFromExit(computer, e);
nn_clearstack(computer);
return e;
}
@@ -1723,6 +1735,7 @@ nn_Exit nn_signalComponent(nn_Component *component, nn_Computer *computer, const
req.ctx = &component->universe->ctx;
req.computer = computer;
req.state = component->state;
req.classState = component->classState;
req.action = NN_COMP_SIGNAL;
req.signal = signal;
return component->handler(&req);
@@ -3125,18 +3138,17 @@ typedef enum nn_EENum {
typedef struct nn_EEState {
nn_Context *ctx;
nn_EEPROM eeprom;
void *state;
nn_EEPROMHandler *handler;
} nn_EEState;
static nn_Exit nn_eepromHandler(nn_ComponentRequest *req) {
if(req->action == NN_COMP_SIGNAL) return NN_OK;
if(req->action == NN_COMP_CHECKMETHOD) return NN_OK;
nn_EEState *state = req->state;
nn_EEState *state = req->classState;
nn_EEPROMRequest ereq;
ereq.ctx = req->ctx;
ereq.computer = req->computer;
ereq.state = state->state;
ereq.state = req->state;
ereq.eeprom = &state->eeprom;
nn_EEPROM eeprom = state->eeprom;
if(req->action == NN_COMP_DROP) {
@@ -3277,9 +3289,9 @@ nn_Component *nn_createEEPROM(nn_Universe *universe, const char *address, const
}
eestate->ctx = ctx;
eestate->eeprom = *eeprom;
eestate->state = state;
eestate->handler = handler;
nn_setComponentState(c, eestate);
nn_setComponentState(c, state);
nn_setComponentClassState(c, eestate);
nn_setComponentHandler(c, nn_eepromHandler);
return c;
}
@@ -3337,8 +3349,8 @@ static nn_Exit nn_veepromHandler(nn_EEPROMRequest *request) {
}
if(request->action == NN_EEPROM_SETLABEL) {
if(request->buflen > NN_MAX_LABEL) request->buflen = NN_MAX_LABEL;
state->datalen = request->buflen;
nn_memcpy(state->data, request->robuf, state->datalen);
state->labellen = request->buflen;
nn_memcpy(state->label, request->robuf, state->labellen);
return NN_OK;
}
if(request->action == NN_EEPROM_SETARCH) {
@@ -3370,6 +3382,8 @@ nn_Component *nn_createVEEPROM(nn_Universe *universe, const char *address, const
state->data = data;
nn_memcpy(data, veeprom->data, veeprom->datalen);
state->datalen = veeprom->datalen;
nn_memcpy(state->label, veeprom->label, veeprom->labellen);
state->labellen = veeprom->labellen;
nn_Component *c = nn_createEEPROM(universe, address, eeprom, state, nn_veepromHandler);
if(c == NULL) goto fail;
@@ -3416,7 +3430,6 @@ typedef enum nn_FSNum {
typedef struct nn_FSState {
nn_Context *ctx;
nn_Filesystem fs;
void *state;
nn_FSHandler *handler;
} nn_FSState;
@@ -3433,11 +3446,11 @@ static nn_Exit nn_fsPathCheck(nn_Computer *C, char buf[NN_MAX_PATH], const char
static nn_Exit nn_fsHandler(nn_ComponentRequest *req) {
if(req->action == NN_COMP_SIGNAL) return NN_OK;
if(req->action == NN_COMP_CHECKMETHOD) return NN_OK;
nn_FSState *state = req->state;
nn_FSState *state = req->classState;
nn_FSRequest freq;
freq.ctx = req->ctx;
freq.computer = req->computer;
freq.state = state->state;
freq.state = req->state;
freq.fs = &state->fs;
if(req->action == NN_COMP_DROP) {
freq.action = NN_FS_DROP;
@@ -3513,7 +3526,7 @@ static nn_Exit nn_fsHandler(nn_ComponentRequest *req) {
if(nn_checknumber(C, 1, "bad argument #2 (number expected)")) return NN_EBADCALL;
double requested = nn_tonumber(C, 1);
if(requested > NN_MAX_READ) requested = NN_MAX_READ;
freq.action = NN_FS_CLOSE;
freq.action = NN_FS_READ;
freq.fd = nn_tointeger(C, 0);
char buf[NN_MAX_READ];
freq.read.buf = buf;
@@ -3522,7 +3535,7 @@ static nn_Exit nn_fsHandler(nn_ComponentRequest *req) {
if(e) return e;
if(freq.read.buf == NULL) return NN_OK;
req->returnCount = 1;
return nn_pushbool(C, true);
return nn_pushlstring(C, buf, freq.read.len);
}
if(method == NN_FSNUM_WRITE) {
if(nn_checkinteger(C, 0, "bad argument #1 (fd expected)")) return NN_EBADCALL;
@@ -3587,6 +3600,7 @@ static nn_Exit nn_fsHandler(nn_ComponentRequest *req) {
char name[NN_MAX_PATH];
freq.action = NN_FS_READDIR;
freq.fd = dirfd;
freq.readdir.dirpath = truepath;
freq.readdir.buf = name;
freq.readdir.len = NN_MAX_PATH;
e = state->handler(&freq);
@@ -3747,9 +3761,9 @@ nn_Component *nn_createFilesystem(nn_Universe *universe, const char *address, co
}
fsstate->ctx = ctx;
fsstate->fs = *fs;
fsstate->state = state;
fsstate->handler = handler;
nn_setComponentState(c, fsstate);
nn_setComponentState(c, state);
nn_setComponentClassState(c, fsstate);
nn_setComponentHandler(c, nn_fsHandler);
return c;
}

View File

@@ -547,6 +547,7 @@ typedef struct nn_ComponentRequest {
nn_Context *ctx;
nn_Computer *computer;
void *state;
void *classState;
nn_ComponentAction action;
// method index
unsigned int methodIdx;
@@ -573,6 +574,7 @@ void nn_dropComponentN(nn_Component *c, size_t n);
// configure the state
void nn_setComponentHandler(nn_Component *c, nn_ComponentHandler *handler);
void nn_setComponentState(nn_Component *c, void *state);
void nn_setComponentClassState(nn_Component *c, void *state);
// sets the methods, same implications as setComponentMethodsArray.
// methods is NULL-terminated, as in, it is terminated by a method with a NULL name.
nn_Exit nn_setComponentMethods(nn_Component *c, const nn_Method *methods);
@@ -587,6 +589,8 @@ nn_Exit nn_setComponentTypeID(nn_Component *c, const char *internalTypeID);
// get component state
void *nn_getComponentState(nn_Component *c);
// get component class state
void *nn_getComponentClassState(nn_Component *c);
// counts how many methods are registered. May return too many if some of them are not enabled.
size_t nn_countComponentMethods(nn_Component *c);
// will fill the methodnames array with the names of the *enabled* methods.
@@ -934,10 +938,10 @@ typedef enum nn_FSAction {
// for file I/O
NN_FS_OPEN,
NN_FS_CLOSE,
NN_FS_READ,
NN_FS_WRITE,
NN_FS_SEEK,
NN_FS_CLOSE,
// for list
NN_FS_OPENDIR,
@@ -986,6 +990,8 @@ typedef struct nn_FSRequest {
} seek;
const char *opendir;
struct {
// directory path, as a reminder if need be
const char *dirpath;
char *buf;
// set to length of entry name
size_t len;