tmpfs is almost fully done

This commit is contained in:
IonutParau 2025-07-18 14:06:43 +02:00
parent 361c1b29e3
commit bb61776b44
6 changed files with 221 additions and 21 deletions

View File

@ -8,7 +8,7 @@
# Parity with Vanilla OC (only the stuff that makes sense for an emulator) # Parity with Vanilla OC (only the stuff that makes sense for an emulator)
- in-memory version of `filesystem` - support `rename` in tmpfs
- complete the GPU implementation (screen buffers and missing methods) - complete the GPU implementation (screen buffers and missing methods)
- complete the screen implementation (bunch of missing methods) - complete the screen implementation (bunch of missing methods)
- `hologram` component - `hologram` component
@ -26,6 +26,7 @@
- Do a huge audit at some point - Do a huge audit at some point
- `nn_unicode_charWidth` appears to be bugged, look into that. - `nn_unicode_charWidth` appears to be bugged, look into that.
- validate against moving `a/b` into `a` or moving `a` into `a/b` in the filesystem component.
# The extra components # The extra components

View File

@ -307,8 +307,20 @@ void nn_fs_rename(nn_filesystem *fs, nn_componentMethod *_, nn_component *compon
return; return;
} }
// TODO: validate against cases such as a/b -> a or a -> a/b
nn_errorbuf_t err = ""; nn_errorbuf_t err = "";
nn_lock(&fs->ctx, fs->lock); nn_lock(&fs->ctx, fs->lock);
if(!fs->table.exists(fs->table.userdata, canonicalFrom, err)) {
nn_unlock(&fs->ctx, fs->lock);
nn_setCError(computer, "No such file or directory");
return;
}
if(fs->table.exists(fs->table.userdata, canonicalTo, err)) {
nn_unlock(&fs->ctx, fs->lock);
nn_setCError(computer, "Destination exists");
return;
}
nn_size_t movedCount = fs->table.rename(fs->table.userdata, canonicalFrom, canonicalTo, err); nn_size_t movedCount = fs->table.rename(fs->table.userdata, canonicalFrom, canonicalTo, err);
nn_unlock(&fs->ctx, fs->lock); nn_unlock(&fs->ctx, fs->lock);
if(!nn_error_isEmpty(err)) { if(!nn_error_isEmpty(err)) {
@ -454,7 +466,7 @@ void nn_fs_open(nn_filesystem *fs, nn_componentMethod *_, nn_component *componen
nn_errorbuf_t err = ""; nn_errorbuf_t err = "";
nn_lock(&fs->ctx, fs->lock); nn_lock(&fs->ctx, fs->lock);
// technically wrongfully // technically wrongfully
if(!fs->table.exists(fs->table.userdata, path, err)) { if(!fs->table.exists(fs->table.userdata, canonical, err)) {
nn_fs_createCost(fs, 1, component); nn_fs_createCost(fs, 1, component);
} }
if(!nn_error_isEmpty(err)) { if(!nn_error_isEmpty(err)) {

View File

@ -83,7 +83,7 @@ nn_vfnode *nn_vf_allocDirectory(nn_vfilesystem *fs, const char *name) {
.fs = fs, .fs = fs,
.lastModified = nn_vf_now(fs), .lastModified = nn_vf_now(fs),
.parent = NULL, .parent = NULL,
.isDirectory = false, .isDirectory = true,
.entries = buffer, .entries = buffer,
.len = 0, .len = 0,
.cap = fs->opts.maxDirEntries, .cap = fs->opts.maxDirEntries,
@ -122,7 +122,7 @@ nn_size_t nn_vf_spaceUsedByNode(nn_vfnode *node) {
} }
nn_vfnode *nn_vf_find(nn_vfnode *parent, const char *name) { nn_vfnode *nn_vf_find(nn_vfnode *parent, const char *name) {
if(parent->isDirectory) return NULL; if(!parent->isDirectory) return NULL;
for(nn_size_t i = 0; i < parent->len; i++) { for(nn_size_t i = 0; i < parent->len; i++) {
nn_vfnode *entry = parent->entries[i]; nn_vfnode *entry = parent->entries[i];
@ -161,12 +161,18 @@ nn_size_t nn_vf_getIdealCapacity(nn_vfnode *file, nn_size_t spaceNeeded) {
void nn_vf_clampHandlePosition(nn_vfhandle *handle) { void nn_vf_clampHandlePosition(nn_vfhandle *handle) {
nn_size_t len = handle->node->len; nn_size_t len = handle->node->len;
if(handle->mode == NN_VFMODE_APPEND) {
handle->position = len;
return;
}
if(handle->position < 0) handle->position = 0; if(handle->position < 0) handle->position = 0;
if(handle->position > len) handle->position = len; if(handle->position > len) handle->position = len;
} }
nn_vfnode *nn_vf_resolvePathFromNode(nn_vfnode *node, const char *path) { nn_vfnode *nn_vf_resolvePathFromNode(nn_vfnode *node, const char *path) {
if(path[0] == 0) return node; if(path[0] == 0) {
return node;
}
char firstDirectory[NN_MAX_PATH]; char firstDirectory[NN_MAX_PATH];
char subpath[NN_MAX_PATH]; char subpath[NN_MAX_PATH];
if(nn_path_firstName(path, firstDirectory, subpath)) { if(nn_path_firstName(path, firstDirectory, subpath)) {
@ -174,6 +180,8 @@ nn_vfnode *nn_vf_resolvePathFromNode(nn_vfnode *node, const char *path) {
} }
nn_vfnode *dir = nn_vf_find(node, firstDirectory); nn_vfnode *dir = nn_vf_find(node, firstDirectory);
if(dir == NULL) return NULL;
if(!dir->isDirectory) return NULL;
return nn_vf_resolvePathFromNode(dir, subpath); return nn_vf_resolvePathFromNode(dir, subpath);
} }
@ -190,6 +198,14 @@ nn_size_t nn_vf_countTree(nn_vfnode *node) {
return n; return n;
} }
nn_bool_t nn_vf_treeHasHandles(nn_vfnode *node) {
if(!node->isDirectory) return node->handleCount > 0;
for(nn_size_t i = 0; i < node->len; i++) {
if(nn_vf_treeHasHandles(node->entries[i])) return true;
}
return false;
}
// methods // methods
void nn_vfs_deinit(nn_vfilesystem *fs) { void nn_vfs_deinit(nn_vfilesystem *fs) {
@ -239,6 +255,10 @@ nn_size_t nn_vfs_remove(nn_vfilesystem *fs, const char *path, nn_errorbuf_t err)
nn_error_write(err, "Unable to delete root"); nn_error_write(err, "Unable to delete root");
return 0; return 0;
} }
if(nn_vf_treeHasHandles(node)) {
nn_error_write(err, "Files are pinned by handles");
return 0;
}
nn_size_t removed = nn_vf_countTree(node); nn_size_t removed = nn_vf_countTree(node);
// it is super easy to delete a tree // it is super easy to delete a tree
nn_size_t j = 0; nn_size_t j = 0;
@ -283,17 +303,47 @@ nn_bool_t nn_vfs_isDirectory(nn_vfilesystem *fs, const char *path, nn_errorbuf_t
return node->isDirectory; return node->isDirectory;
} }
nn_bool_t nn_vfs_makeDirectory(nn_vfilesystem *fs, const char *path, nn_errorbuf_t err) { static nn_bool_t nn_vfs_recursiveMkdir(nn_vfilesystem *fs, const char *path, nn_errorbuf_t err) {
// this code is horribly unoptimized, with a time complexity of O(F^U),
// where F is 8.1 * 10^53 (monsterous) and U is 7 * 10^27 (very human)
// TODO: burn it with fire and make something good
nn_vfnode *node = nn_vf_resolvePath(fs, path); nn_vfnode *node = nn_vf_resolvePath(fs, path);
if(node != NULL) { if(node != NULL) {
nn_error_write(err, "File already exists"); if(node->isDirectory) {
return 0; return true;
}
nn_error_write(err, "Is a file");
return false;
} }
char name[NN_MAX_PATH]; char name[NN_MAX_PATH];
char parentPath[NN_MAX_PATH]; char parentPath[NN_MAX_PATH];
nn_path_lastName(path, name, parentPath); nn_bool_t isRootDir = nn_path_lastName(path, name, parentPath);
nn_vfnode *parent = nn_vf_resolvePath(fs, path);
if(!isRootDir) {
if(!nn_vfs_recursiveMkdir(fs, parentPath, err)) return false;
}
nn_vfnode *parent = isRootDir ? fs->root : nn_vf_resolvePath(fs, parentPath);
if(parent == NULL) {
nn_error_write(err, "Bad state"); // just a sanity check
return false; return false;
}
if(parent->len == parent->cap) {
nn_error_write(err, "Too many entries");
return false;
}
nn_vfnode *dir = nn_vf_allocDirectory(fs, name);
if(dir == NULL) {
nn_error_write(err, "Out of memory");
return false;
}
dir->parent = parent;
parent->entries[parent->len] = dir;
parent->len++;
return true;
}
nn_bool_t nn_vfs_makeDirectory(nn_vfilesystem *fs, const char *path, nn_errorbuf_t err) {
return nn_vfs_recursiveMkdir(fs, path, err);
} }
char **nn_vfs_list(nn_Alloc *alloc, nn_vfilesystem *fs, const char *path, nn_size_t *len, nn_errorbuf_t err) { char **nn_vfs_list(nn_Alloc *alloc, nn_vfilesystem *fs, const char *path, nn_size_t *len, nn_errorbuf_t err) {
@ -321,8 +371,8 @@ char **nn_vfs_list(nn_Alloc *alloc, nn_vfilesystem *fs, const char *path, nn_siz
s = nn_alloc(alloc, l + 2); s = nn_alloc(alloc, l + 2);
if(s != NULL) { if(s != NULL) {
nn_memcpy(s, entry->name, l); nn_memcpy(s, entry->name, l);
entry->name[l] = '/'; s[l] = '/';
entry->name[l+1] = 0; s[l+1] = 0;
} }
} else { } else {
s = nn_strdup(alloc, entry->name); s = nn_strdup(alloc, entry->name);
@ -339,13 +389,117 @@ char **nn_vfs_list(nn_Alloc *alloc, nn_vfilesystem *fs, const char *path, nn_siz
return buf; return buf;
} }
void *nn_vfs_open(nn_vfilesystem *fs, const char *path, const char *mode, nn_errorbuf_t err) { nn_vfhandle *nn_vfs_open(nn_vfilesystem *fs, const char *path, const char *mode, nn_errorbuf_t err) {
// TODO: complete // TODO: complete
char m = mode[0]; char m = mode[0];
nn_vfmode fmode = NN_VFMODE_READ; nn_vfmode fmode = NN_VFMODE_READ;
if(m == 'w') fmode = NN_VFMODE_WRITE; if(m == 'w') fmode = NN_VFMODE_WRITE;
if(m == 'a') fmode = NN_VFMODE_APPEND; if(m == 'a') fmode = NN_VFMODE_APPEND;
nn_vfnode *node = nn_vf_resolvePath(fs, path);
if(node == NULL && fmode != NN_VFMODE_READ && !fs->opts.isReadOnly) {
char parentPath[NN_MAX_PATH];
char name[NN_MAX_PATH];
nn_bool_t isRootFile = nn_path_lastName(path, name, parentPath);
nn_vfnode *parent = isRootFile ? fs->root : nn_vf_resolvePath(fs, parentPath);
if(parent == NULL) {
nn_error_write(err, "Missing parent directory");
return NULL; return NULL;
}
if(!parent->isDirectory) {
nn_error_write(err, "Parent is not a directory");
return NULL;
}
if(parent->len == parent->cap) {
nn_error_write(err, "Too many entries");
return NULL;
}
node = nn_vf_allocFile(fs, name);
if(node == NULL) {
nn_error_write(err, "Out of memory");
return NULL;
}
node->parent = parent;
parent->entries[parent->len] = node;
parent->len++;
}
if(node == NULL) {
nn_error_write(err, "No such file");
return NULL;
}
if(fs->opts.isReadOnly && fmode != NN_VFMODE_READ) {
nn_error_write(err, "readonly");
return NULL;
}
if(node->isDirectory) {
nn_error_write(err, "Is a directory");
return NULL;
}
if(fmode == NN_VFMODE_WRITE) {
node->len = 0;
node->lastModified = nn_vf_now(fs);
}
nn_vfhandle *handle = nn_alloc(&fs->ctx.allocator, sizeof(nn_vfhandle));
if(handle == NULL) {
nn_error_write(err, "Out of memory");
return NULL;
}
handle->mode = fmode;
handle->node = node;
handle->position = 0;
node->handleCount++;
return handle;
}
nn_bool_t nn_vfs_close(nn_vfilesystem *fs, nn_vfhandle *handle, nn_errorbuf_t err) {
handle->node->handleCount--;
nn_dealloc(&fs->ctx.allocator, handle, sizeof(nn_vfhandle));
return true;
}
nn_bool_t nn_vfs_write(nn_vfilesystem *fs, nn_vfhandle *handle, const char *buf, nn_size_t len, nn_errorbuf_t err) {
nn_vf_clampHandlePosition(handle);
if(!nn_vf_ensureFileCapacity(handle->node, handle->position + len)) {
nn_error_write(err, "Out of memory");
return false;
}
nn_memcpy(handle->node->data + handle->position, buf, len);
handle->position += len;
if(handle->node->len < handle->position) handle->node->len = handle->position;
return true;
}
nn_size_t nn_vfs_read(nn_vfilesystem *fs, nn_vfhandle *handle, char *buf, nn_size_t required, nn_errorbuf_t err) {
nn_size_t remaining = handle->node->len - handle->position;
if(required > remaining) required = remaining;
if(required == 0) return 0;
nn_memcpy(buf, handle->node->data + handle->position, required);
handle->position += required;
return required;
}
nn_size_t nn_vfs_seek(nn_vfilesystem *fs, nn_vfhandle *handle, const char *whence, int off, nn_errorbuf_t err) {
if(handle->mode == NN_VFMODE_APPEND) {
nn_error_write(err, "Bad file descriptor");
return handle->node->len;
}
nn_intptr_t ptr = handle->position;
if(nn_strcmp(whence, "set") == 0) {
ptr = off;
}
if(nn_strcmp(whence, "cur") == 0) {
ptr += off;
}
if(nn_strcmp(whence, "end") == 0) {
ptr = handle->node->len - off;
}
handle->position = ptr;
nn_vf_clampHandlePosition(handle);
return handle->position;
} }
// main funciton // main funciton
@ -353,9 +507,9 @@ void *nn_vfs_open(nn_vfilesystem *fs, const char *path, const char *mode, nn_err
nn_filesystem *nn_volatileFilesystem(nn_Context *context, nn_vfilesystemOptions opts, nn_filesystemControl control) { nn_filesystem *nn_volatileFilesystem(nn_Context *context, nn_vfilesystemOptions opts, nn_filesystemControl control) {
// TODO: handle OOM // TODO: handle OOM
nn_vfilesystem *fs = nn_alloc(&context->allocator, sizeof(nn_vfilesystem)); nn_vfilesystem *fs = nn_alloc(&context->allocator, sizeof(nn_vfilesystem));
fs->ctx = *context;
nn_Clock c = fs->ctx.clock; nn_Clock c = fs->ctx.clock;
double time = c.proc(c.userdata); double time = c.proc(c.userdata);
fs->ctx = *context;
fs->birthday = time; fs->birthday = time;
fs->opts = opts; fs->opts = opts;
fs->root = nn_vf_allocDirectory(fs, "/"); fs->root = nn_vf_allocDirectory(fs, "/");
@ -369,10 +523,17 @@ nn_filesystem *nn_volatileFilesystem(nn_Context *context, nn_vfilesystemOptions
.isReadOnly = (void *)nn_vfs_isReadOnly, .isReadOnly = (void *)nn_vfs_isReadOnly,
.size = (void *)nn_vfs_size, .size = (void *)nn_vfs_size,
.remove = (void *)nn_vfs_remove, .remove = (void *)nn_vfs_remove,
.lastModified = (void *)nn_vfs_lastModified,
.rename = (void *)nn_vfs_rename,
.exists = (void *)nn_vfs_exists, .exists = (void *)nn_vfs_exists,
.isDirectory = (void *)nn_vfs_isDirectory, .isDirectory = (void *)nn_vfs_isDirectory,
.makeDirectory = (void *)nn_vfs_makeDirectory, .makeDirectory = (void *)nn_vfs_makeDirectory,
.list = (void *)nn_vfs_list, .list = (void *)nn_vfs_list,
.open = (void *)nn_vfs_open,
.close = (void *)nn_vfs_close,
.write = (void *)nn_vfs_write,
.read = (void *)nn_vfs_read,
.seek = (void *)nn_vfs_seek,
}; };
return nn_newFilesystem(context, table, control); return nn_newFilesystem(context, table, control);
} }

View File

@ -673,6 +673,20 @@ int main() {
nn_filesystem *genericFS = nn_newFilesystem(&ctx, genericFSTable, ne_fs_ctrl); nn_filesystem *genericFS = nn_newFilesystem(&ctx, genericFSTable, ne_fs_ctrl);
nn_addFileSystem(computer, NULL, 1, genericFS); nn_addFileSystem(computer, NULL, 1, genericFS);
nn_vfilesystemOptions tmpfsOpts = {
.isReadOnly = false,
.capacity = 64*1024,
.label = "tmpfs",
.labelLen = 5,
.creationTime = 0, // we are at the start of time
.maxDirEntries = 64,
};
nn_filesystem *tmpFS = nn_volatileFilesystem(&ctx, tmpfsOpts, ne_fs_ctrl);
nn_component *tmpfsComp = nn_addFileSystem(computer, NULL, 2, tmpFS);
nn_setTmpAddress(computer, nn_getComponentAddress(tmpfsComp));
nn_vdriveOptions vdriveOpts = { nn_vdriveOptions vdriveOpts = {
.sectorSize = 512, .sectorSize = 512,
.capacity = 1*1024*1024, .capacity = 1*1024*1024,

View File

@ -213,6 +213,7 @@ char *nn_strcpy(char *dest, const char *src);
const char *nn_strchr(const char *str, int ch); const char *nn_strchr(const char *str, int ch);
int nn_strcmp(const char *a, const char *b); int nn_strcmp(const char *a, const char *b);
nn_size_t nn_strlen(const char *a); nn_size_t nn_strlen(const char *a);
nn_bool_t nn_strbegin(const char *s, const char *prefix);
#ifndef NN_BAREMETAL #ifndef NN_BAREMETAL
nn_Alloc nn_libcAllocator(void); nn_Alloc nn_libcAllocator(void);

View File

@ -176,7 +176,7 @@ static double nni_realTime(void) {
#endif #endif
static double nni_realTimeClock(void *_) { double nni_realTimeClock(void *_) {
return nni_realTime(); return nni_realTime();
} }
@ -246,6 +246,7 @@ void nn_memset(void *buf, unsigned char byte, nn_size_t len) {
void nn_memcpy(void *dest, const void *src, nn_size_t len) { void nn_memcpy(void *dest, const void *src, nn_size_t len) {
if(dest == NULL) return; if(dest == NULL) return;
if(src == NULL) return; if(src == NULL) return;
if(len == 0) return;
char *destBytes = dest; char *destBytes = dest;
const char *srcBytes = src; const char *srcBytes = src;
@ -301,6 +302,16 @@ nn_size_t nn_strlen(const char *a) {
return l; return l;
} }
nn_bool_t nn_strbegin(const char *s, const char *prefix) {
nn_size_t i = 0;
while(true) {
if(prefix[i] == 0) return true; // prefix over, it matched
if(s[i] == 0) return false; // string over, it didn't match
if(s[i] != prefix[i]) return false;
i++;
}
}
nn_bool_t nn_error_isEmpty(nn_errorbuf_t buf) { nn_bool_t nn_error_isEmpty(nn_errorbuf_t buf) {
if(buf == NULL) return true; if(buf == NULL) return true;
return buf[0] == 0; return buf[0] == 0;
@ -350,7 +361,7 @@ nn_bool_t nn_path_firstName(const char *path, char firstDirectory[NN_MAX_PATH],
if(!nn_path_hasSlash(path)) { if(!nn_path_hasSlash(path)) {
nn_strcpy(firstDirectory, path); nn_strcpy(firstDirectory, path);
nn_strcpy(subpath, ""); nn_strcpy(subpath, "");
return false; // end return true; // end
} }
nn_size_t slash = nn_path_firstSlash(path); nn_size_t slash = nn_path_firstSlash(path);
@ -358,7 +369,7 @@ nn_bool_t nn_path_firstName(const char *path, char firstDirectory[NN_MAX_PATH],
firstDirectory[slash] = 0; firstDirectory[slash] = 0;
nn_strcpy(subpath, path + slash + 1); nn_strcpy(subpath, path + slash + 1);
return true; return false;
} }
// returns whether it is the only name // returns whether it is the only name
@ -366,7 +377,7 @@ nn_bool_t nn_path_lastName(const char *path, char name[NN_MAX_PATH], char parent
if(!nn_path_hasSlash(path)) { if(!nn_path_hasSlash(path)) {
nn_strcpy(name, path); nn_strcpy(name, path);
nn_strcpy(parent, ""); nn_strcpy(parent, "");
return false; // end return true; // end
} }
nn_size_t slash = nn_path_lastSlash(path); nn_size_t slash = nn_path_lastSlash(path);
@ -374,7 +385,7 @@ nn_bool_t nn_path_lastName(const char *path, char name[NN_MAX_PATH], char parent
nn_memcpy(parent, path, slash); nn_memcpy(parent, path, slash);
parent[slash] = 0; parent[slash] = 0;
return true; return false;
} }
const char *nn_path_illegal = "\"\\:*?<>|"; const char *nn_path_illegal = "\"\\:*?<>|";