mirror of
https://github.com/NeoFlock/neonucleus.git
synced 2025-09-24 09:03:32 +02:00
638 lines
19 KiB
C
638 lines
19 KiB
C
#include "../neonucleus.h"
|
|
|
|
// Data structures
|
|
|
|
typedef struct nn_vfnode {
|
|
struct nn_vfilesystem *fs;
|
|
char name[NN_MAX_PATH];
|
|
struct nn_vfnode *parent;
|
|
nn_bool_t isDirectory;
|
|
union {
|
|
// if directory
|
|
struct nn_vfnode **entries;
|
|
// if file
|
|
char *data;
|
|
};
|
|
nn_size_t len;
|
|
nn_size_t cap;
|
|
nn_timestamp_t lastModified;
|
|
// this is used to block deleting
|
|
nn_refc handleCount;
|
|
} nn_vfnode;
|
|
|
|
typedef enum nn_vfmode {
|
|
NN_VFMODE_READ,
|
|
NN_VFMODE_WRITE,
|
|
NN_VFMODE_APPEND,
|
|
} nn_vfmode;
|
|
|
|
typedef struct nn_vfhandle {
|
|
nn_vfnode *node;
|
|
nn_intptr_t position;
|
|
nn_vfmode mode;
|
|
} nn_vfhandle;
|
|
|
|
typedef struct nn_vfilesystem {
|
|
nn_Context ctx;
|
|
nn_vfilesystemOptions opts;
|
|
double birthday;
|
|
nn_vfnode *root;
|
|
} nn_vfilesystem;
|
|
|
|
// virtual node helpers
|
|
|
|
nn_timestamp_t nn_vf_now(nn_vfilesystem *fs) {
|
|
nn_Clock c = fs->ctx.clock;
|
|
double time = c.proc(c.userdata);
|
|
double elapsed = time - fs->birthday;
|
|
nn_timestamp_t elapsedMS = elapsed * 1000;
|
|
return fs->opts.creationTime + elapsedMS;
|
|
}
|
|
|
|
nn_vfnode *nn_vf_allocFile(nn_vfilesystem *fs, const char *name) {
|
|
nn_Alloc *alloc = &fs->ctx.allocator;
|
|
nn_vfnode *node = nn_alloc(alloc, sizeof(nn_vfnode));
|
|
if(node == NULL) return NULL;
|
|
*node = (nn_vfnode) {
|
|
.fs = fs,
|
|
.lastModified = nn_vf_now(fs),
|
|
.parent = NULL,
|
|
.isDirectory = false,
|
|
.data = NULL,
|
|
.len = 0,
|
|
.cap = 0,
|
|
.handleCount = 0,
|
|
};
|
|
// we pray
|
|
nn_strcpy(node->name, name);
|
|
return node;
|
|
}
|
|
|
|
nn_vfnode *nn_vf_allocDirectory(nn_vfilesystem *fs, const char *name) {
|
|
nn_Alloc *alloc = &fs->ctx.allocator;
|
|
nn_vfnode *node = nn_alloc(alloc, sizeof(nn_vfnode));
|
|
if(node == NULL) return NULL;
|
|
nn_vfnode **buffer = nn_alloc(alloc, sizeof(nn_vfnode *));
|
|
if(buffer == NULL) {
|
|
nn_dealloc(alloc, node, sizeof(nn_vfnode));
|
|
return NULL;
|
|
}
|
|
*node = (nn_vfnode) {
|
|
.fs = fs,
|
|
.lastModified = nn_vf_now(fs),
|
|
.parent = NULL,
|
|
.isDirectory = true,
|
|
.entries = buffer,
|
|
.len = 0,
|
|
.cap = fs->opts.maxDirEntries,
|
|
.handleCount = 0,
|
|
};
|
|
// we pray
|
|
nn_strcpy(node->name, name);
|
|
return node;
|
|
}
|
|
|
|
void nn_vf_freeNode(nn_vfnode *node) {
|
|
nn_Alloc *alloc = &node->fs->ctx.allocator;
|
|
|
|
if(node->isDirectory) {
|
|
for(nn_size_t i = 0; i < node->len; i++) {
|
|
nn_vf_freeNode(node->entries[i]);
|
|
}
|
|
nn_dealloc(alloc, node->entries, sizeof(nn_vfnode *) * node->cap);
|
|
} else {
|
|
nn_dealloc(alloc, node->data, node->cap);
|
|
}
|
|
|
|
nn_dealloc(alloc, node, sizeof(nn_vfnode));
|
|
}
|
|
|
|
nn_size_t nn_vf_spaceUsedByNode(nn_vfnode *node) {
|
|
if(node->isDirectory) {
|
|
nn_size_t sum = 0;
|
|
for(nn_size_t i = 0; i < node->len; i++) {
|
|
sum += nn_vf_spaceUsedByNode(node->entries[i]);
|
|
}
|
|
return sum;
|
|
} else {
|
|
return node->len;
|
|
}
|
|
}
|
|
|
|
nn_vfnode *nn_vf_find(nn_vfnode *parent, const char *name) {
|
|
if(!parent->isDirectory) return NULL;
|
|
for(nn_size_t i = 0; i < parent->len; i++) {
|
|
nn_vfnode *entry = parent->entries[i];
|
|
|
|
if(nn_strcmp(entry->name, name) == 0) {
|
|
return entry;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
nn_bool_t nn_vf_ensureFileCapacity(nn_vfnode *file, nn_size_t capacity) {
|
|
if(file->isDirectory) return false;
|
|
nn_Alloc *alloc = &file->fs->ctx.allocator;
|
|
|
|
if(file->cap >= capacity) return true; // already at that point
|
|
|
|
char *newData = nn_resize(alloc, file->data, file->cap, capacity);
|
|
if(newData == NULL) {
|
|
return false; // OOM
|
|
}
|
|
file->data = newData;
|
|
file->cap = capacity;
|
|
|
|
return true;
|
|
}
|
|
|
|
// this is used to compute exponential backoff
|
|
// TODO: add an option to select either a slower growth rate or
|
|
// linear backoff to reduce memory usage at the cost of speed
|
|
nn_size_t nn_vf_getIdealCapacity(nn_vfnode *file, nn_size_t spaceNeeded) {
|
|
nn_size_t cap = file->cap;
|
|
if(cap == 0) cap = 1;
|
|
while(cap < spaceNeeded) cap *= 2; // this would mean a file with 1,048,577 bytes takes up 2,097,152 bytes, potentially wasting 1,048,575 bytes
|
|
return cap;
|
|
}
|
|
|
|
void nn_vf_clampHandlePosition(nn_vfhandle *handle) {
|
|
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 > len) handle->position = len;
|
|
}
|
|
|
|
nn_vfnode *nn_vf_resolvePathFromNode(nn_vfnode *node, const char *path) {
|
|
if(path[0] == 0) {
|
|
return node;
|
|
}
|
|
char firstDirectory[NN_MAX_PATH];
|
|
char subpath[NN_MAX_PATH];
|
|
if(nn_path_firstName(path, firstDirectory, subpath)) {
|
|
return 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);
|
|
}
|
|
|
|
nn_vfnode *nn_vf_resolvePath(nn_vfilesystem *fs, const char *path) {
|
|
return nn_vf_resolvePathFromNode(fs->root, path);
|
|
}
|
|
|
|
nn_size_t nn_vf_countTree(nn_vfnode *node) {
|
|
if(!node->isDirectory) return 1;
|
|
nn_size_t n = 1;
|
|
for(nn_size_t i = 0; i < node->len; i++) {
|
|
n += nn_vf_countTree(node->entries[i]);
|
|
}
|
|
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;
|
|
}
|
|
|
|
void nn_vf_appendNode(nn_vfnode *parent, nn_vfnode *node) {
|
|
if(!parent->isDirectory) return;
|
|
if(parent->len == parent->cap) return;
|
|
parent->entries[parent->len] = node;
|
|
parent->len++;
|
|
node->parent = parent; // just to be sure
|
|
}
|
|
|
|
void nn_vf_removeNode(nn_vfnode *parent, nn_vfnode *node) {
|
|
if(!parent->isDirectory) return;
|
|
nn_size_t j = 0;
|
|
for(nn_size_t i = 0; i < parent->len; i++) {
|
|
if(parent->entries[i] != node) {
|
|
parent->entries[j] = parent->entries[i];
|
|
j++;
|
|
}
|
|
}
|
|
parent->len = j;
|
|
}
|
|
|
|
// methods
|
|
|
|
void nn_vfs_deinit(nn_vfilesystem *fs) {
|
|
nn_Context ctx = fs->ctx;
|
|
|
|
nn_vf_freeNode(fs->root);
|
|
nn_dealloc(&ctx.allocator, fs, sizeof(nn_vfilesystem));
|
|
}
|
|
|
|
void nn_vfs_getLabel(nn_vfilesystem *fs, char *buf, nn_size_t *buflen) {
|
|
*buflen = fs->opts.labelLen;
|
|
nn_memcpy(buf, fs->opts.label, fs->opts.labelLen);
|
|
}
|
|
|
|
void nn_vfs_setLabel(nn_vfilesystem *fs, const char *buf, nn_size_t buflen) {
|
|
nn_memcpy(fs->opts.label, buf, buflen);
|
|
fs->opts.labelLen = buflen;
|
|
}
|
|
|
|
nn_size_t nn_vfs_spaceUsed(nn_vfilesystem *fs) {
|
|
return nn_vf_spaceUsedByNode(fs->root);
|
|
}
|
|
|
|
nn_bool_t nn_vfs_isReadOnly(nn_vfilesystem *fs) {
|
|
return fs->opts.isReadOnly;
|
|
}
|
|
|
|
nn_size_t nn_vfs_size(nn_vfilesystem *fs, const char *path, nn_errorbuf_t err) {
|
|
nn_vfnode *node = nn_vf_resolvePath(fs, path);
|
|
if(node == NULL) {
|
|
nn_error_write(err, "No such file");
|
|
return 0;
|
|
}
|
|
if(node->isDirectory) return 0;
|
|
return node->len;
|
|
}
|
|
|
|
nn_size_t nn_vfs_remove(nn_vfilesystem *fs, const char *path, nn_errorbuf_t err) {
|
|
nn_vfnode *node = nn_vf_resolvePath(fs, path);
|
|
if(node == NULL) {
|
|
nn_error_write(err, "No such file");
|
|
return 0;
|
|
}
|
|
nn_vfnode *parent = node->parent;
|
|
if(parent == NULL) {
|
|
// root, can't delete
|
|
nn_error_write(err, "Unable to delete root");
|
|
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);
|
|
// it is super easy to delete a tree
|
|
nn_vf_removeNode(parent, node);
|
|
parent->lastModified = nn_vf_now(fs);
|
|
nn_vf_freeNode(node);
|
|
return removed;
|
|
}
|
|
|
|
nn_timestamp_t nn_vfs_lastModified(nn_vfilesystem *fs, const char *path, nn_errorbuf_t err) {
|
|
nn_vfnode *node = nn_vf_resolvePath(fs, path);
|
|
if(node == NULL) {
|
|
nn_error_write(err, "No such file");
|
|
return 0;
|
|
}
|
|
return node->lastModified;
|
|
}
|
|
|
|
nn_size_t nn_vfs_rename(nn_vfilesystem *fs, const char *from, const char *to, nn_errorbuf_t err) {
|
|
// TODO: implement rename
|
|
nn_error_write(err, "Unsupported operation");
|
|
nn_vfnode *srcNode = nn_vf_resolvePath(fs, from);
|
|
if(srcNode == NULL) {
|
|
nn_error_write(err, "No such file");
|
|
return 0;
|
|
}
|
|
nn_vfnode *srcParent = srcNode->parent;
|
|
if(srcParent == NULL) {
|
|
// root, can't move
|
|
nn_error_write(err, "Unable to move root");
|
|
return 0;
|
|
}
|
|
if(nn_vf_treeHasHandles(srcNode)) {
|
|
nn_error_write(err, "Files are pinned by handles");
|
|
return 0;
|
|
}
|
|
|
|
char name[NN_MAX_PATH];
|
|
char parentPath[NN_MAX_PATH];
|
|
nn_bool_t rootOut = nn_path_lastName(to, name, parentPath);
|
|
nn_vfnode *destParent = rootOut ? fs->root : nn_vf_resolvePath(fs, parentPath);
|
|
if(destParent == NULL) {
|
|
nn_error_write(err, "No such directory");
|
|
return 0;
|
|
}
|
|
if(!destParent->isDirectory) {
|
|
nn_error_write(err, "Is a file");
|
|
return 0;
|
|
}
|
|
if(nn_vf_find(destParent, name) != NULL) {
|
|
nn_error_write(err, "Already exists");
|
|
return 0;
|
|
}
|
|
|
|
if(destParent->len == destParent->cap) {
|
|
nn_error_write(err, "Too many entries");
|
|
return 0;
|
|
}
|
|
nn_size_t moved = nn_vf_countTree(srcNode);
|
|
// super efficient moving
|
|
nn_vf_removeNode(srcParent, srcNode);
|
|
nn_vf_appendNode(destParent, srcNode);
|
|
return moved;
|
|
}
|
|
|
|
nn_bool_t nn_vfs_exists(nn_vfilesystem *fs, const char *path, nn_errorbuf_t err) {
|
|
nn_vfnode *node = nn_vf_resolvePath(fs, path);
|
|
return node != NULL;
|
|
}
|
|
|
|
nn_bool_t nn_vfs_isDirectory(nn_vfilesystem *fs, const char *path, nn_errorbuf_t err) {
|
|
nn_vfnode *node = nn_vf_resolvePath(fs, path);
|
|
if(node == NULL) {
|
|
nn_error_write(err, "No such file");
|
|
return 0;
|
|
}
|
|
return node->isDirectory;
|
|
}
|
|
|
|
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);
|
|
if(node != NULL) {
|
|
if(node->isDirectory) {
|
|
return true;
|
|
}
|
|
nn_error_write(err, "Is a file");
|
|
return false;
|
|
}
|
|
char name[NN_MAX_PATH];
|
|
char parentPath[NN_MAX_PATH];
|
|
nn_bool_t isRootDir = nn_path_lastName(path, name, parentPath);
|
|
|
|
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;
|
|
}
|
|
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) {
|
|
nn_vfnode *node = nn_vf_resolvePath(fs, path);
|
|
if(node == NULL) {
|
|
nn_error_write(err, "No such file");
|
|
return NULL;
|
|
}
|
|
if(!node->isDirectory) {
|
|
nn_error_write(err, "Not a directory");
|
|
return NULL;
|
|
}
|
|
nn_size_t count = node->len;
|
|
*len = count;
|
|
char **buf = nn_alloc(alloc, sizeof(char *) * count);
|
|
if(buf == NULL) {
|
|
nn_error_write(err, "Out of memory");
|
|
return NULL;
|
|
}
|
|
for(nn_size_t i = 0; i < count; i++) {
|
|
nn_vfnode *entry = node->entries[i];
|
|
char *s = NULL;
|
|
if(entry->isDirectory) {
|
|
nn_size_t l = nn_strlen(entry->name);
|
|
s = nn_alloc(alloc, l + 2);
|
|
if(s != NULL) {
|
|
nn_memcpy(s, entry->name, l);
|
|
s[l] = '/';
|
|
s[l+1] = 0;
|
|
}
|
|
} else {
|
|
s = nn_strdup(alloc, entry->name);
|
|
}
|
|
if(s == NULL) {
|
|
for(nn_size_t j = 0; j < i; j++) {
|
|
nn_deallocStr(alloc, buf[j]);
|
|
}
|
|
nn_error_write(err, "Out of memory");
|
|
return NULL;
|
|
}
|
|
buf[i] = s;
|
|
}
|
|
return buf;
|
|
}
|
|
|
|
nn_vfhandle *nn_vfs_open(nn_vfilesystem *fs, const char *path, const char *mode, nn_errorbuf_t err) {
|
|
// TODO: complete
|
|
char m = mode[0];
|
|
nn_vfmode fmode = NN_VFMODE_READ;
|
|
if(m == 'w') fmode = NN_VFMODE_WRITE;
|
|
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;
|
|
}
|
|
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;
|
|
}
|
|
|
|
typedef struct nn_vfilesystemImage {
|
|
nn_vfilesystemImageNode *nodes;
|
|
nn_size_t ptr;
|
|
} nn_vfilesystemImage;
|
|
|
|
static nn_vfilesystemImageNode nni_vfsimg_nextNode(nn_vfilesystemImage *stream) {
|
|
nn_vfilesystemImageNode node = stream->nodes[stream->ptr];
|
|
stream->ptr++;
|
|
return node;
|
|
}
|
|
|
|
static nn_vfnode *nni_vfsimg_parseNode(nn_vfilesystem *fs, nn_vfilesystemImage *stream) {
|
|
// TODO: make this handle OOMs
|
|
nn_vfilesystemImageNode node = nni_vfsimg_nextNode(stream);
|
|
if(node.data == NULL) {
|
|
// directory!!!!!
|
|
nn_vfnode *dir = nn_vf_allocDirectory(fs, node.name);
|
|
dir->len = node.len;
|
|
for(int i = 0; i < node.len; i++) {
|
|
nn_vfnode *entry = nni_vfsimg_parseNode(fs, stream);
|
|
dir->entries[i] = entry;
|
|
}
|
|
return dir;
|
|
}
|
|
// file!!!!!
|
|
nn_vfnode *file = nn_vf_allocFile(fs, node.name);
|
|
nn_vf_ensureFileCapacity(file, node.len);
|
|
file->len = node.len;
|
|
nn_memcpy(file->data, node.data, node.len);
|
|
return file;
|
|
}
|
|
|
|
// constructor
|
|
|
|
nn_filesystem *nn_volatileFilesystem(nn_Context *context, nn_vfilesystemOptions opts, nn_filesystemControl control) {
|
|
// TODO: handle OOM
|
|
nn_vfilesystem *fs = nn_alloc(&context->allocator, sizeof(nn_vfilesystem));
|
|
fs->ctx = *context;
|
|
nn_Clock c = fs->ctx.clock;
|
|
double time = c.proc(c.userdata);
|
|
fs->birthday = time;
|
|
fs->opts = opts;
|
|
fs->root = nn_vf_allocDirectory(fs, "/");
|
|
|
|
if(opts.image != NULL) {
|
|
nn_vfilesystemImage stream = {
|
|
.nodes = opts.image,
|
|
.ptr = 0,
|
|
};
|
|
// we got supplied an image, shit
|
|
fs->root->len = opts.rootEntriesInImage;
|
|
for(int i = 0; i < opts.rootEntriesInImage; i++) {
|
|
nn_vfnode *entry = nni_vfsimg_parseNode(fs, &stream);
|
|
fs->root->entries[i] = entry;
|
|
}
|
|
}
|
|
|
|
nn_filesystemTable table = {
|
|
.userdata = fs,
|
|
.deinit = (void *)nn_vfs_deinit,
|
|
.getLabel = (void *)nn_vfs_getLabel,
|
|
.setLabel = (void *)nn_vfs_setLabel,
|
|
.spaceUsed = (void *)nn_vfs_spaceUsed,
|
|
.spaceTotal = opts.capacity,
|
|
.isReadOnly = (void *)nn_vfs_isReadOnly,
|
|
.size = (void *)nn_vfs_size,
|
|
.remove = (void *)nn_vfs_remove,
|
|
.lastModified = (void *)nn_vfs_lastModified,
|
|
.rename = (void *)nn_vfs_rename,
|
|
.exists = (void *)nn_vfs_exists,
|
|
.isDirectory = (void *)nn_vfs_isDirectory,
|
|
.makeDirectory = (void *)nn_vfs_makeDirectory,
|
|
.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);
|
|
}
|