diff --git a/src/main.c b/src/main.c index 0e8d707..94ba957 100644 --- a/src/main.c +++ b/src/main.c @@ -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; diff --git a/src/ncomplib.c b/src/ncomplib.c index 935f52f..934c587 100644 --- a/src/ncomplib.c +++ b/src/ncomplib.c @@ -2,7 +2,6 @@ #include "ncomplib.h" #include #include -#include static bool ncl_defaultHandler(ncl_VFSRequest *request); @@ -74,14 +73,18 @@ bool ncl_defaultHandler(ncl_VFSRequest *request) { return true; } if(request->action == NCL_VFS_READDIR) { - DIR *d = request->readdir.dir; - struct dirent *ent = readdir(d); - if(ent == NULL) { - request->readdir.name = NULL; - } else { - strncpy(request->readdir.name, ent->d_name, NN_MAX_PATH-1); + 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; } - return true; } if(request->action == NCL_VFS_STAT) { struct stat s; @@ -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) { diff --git a/src/neonucleus.c b/src/neonucleus.c index b7590f2..e171fc0 100644 --- a/src/neonucleus.c +++ b/src/neonucleus.c @@ -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; } diff --git a/src/neonucleus.h b/src/neonucleus.h index 2a0f00b..56b8044 100644 --- a/src/neonucleus.h +++ b/src/neonucleus.h @@ -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;