summaryrefslogtreecommitdiff
path: root/src/user/fs
diff options
context:
space:
mode:
Diffstat (limited to 'src/user/fs')
-rw-r--r--src/user/fs/misc.c168
-rw-r--r--src/user/fs/misc.h16
-rw-r--r--src/user/fs/tar.c162
-rw-r--r--src/user/fs/tar.h4
4 files changed, 350 insertions, 0 deletions
diff --git a/src/user/fs/misc.c b/src/user/fs/misc.c
new file mode 100644
index 0000000..bf09718
--- /dev/null
+++ b/src/user/fs/misc.c
@@ -0,0 +1,168 @@
+#include <user/fs/misc.h>
+#include <user/lib/stdlib.h>
+#include <shared/flags.h>
+#include <shared/mem.h>
+#include <shared/syscalls.h>
+#include <stdbool.h>
+
+bool fork2_n_mount(const char *path) {
+ handle_t h;
+ if (_syscall_fork(FORK_NEWFS, &h) > 0) { /* parent */
+ _syscall_mount(h, path, strlen(path));
+ _syscall_close(h);
+ return true;
+ }
+ return false;
+}
+
+void fs_passthru(const char *prefix) {
+ struct fs_wait_response res;
+ const size_t buf_len = 1024;
+ char *buf = malloc(buf_len);
+ int prefix_len = prefix ? strlen(prefix) : 0;
+ if (!buf) _syscall_exit(1);
+
+ while (!_syscall_fs_wait(buf, buf_len, &res)) {
+ switch (res.op) {
+ case VFSOP_OPEN:
+ if (prefix) {
+ if (prefix_len + res.len > buf_len) {
+ _syscall_fs_respond(NULL, -1, 0);
+ break;
+ }
+
+ // TODO memmove
+ char tmp[64];
+ memcpy(tmp, buf, res.len);
+ memcpy(buf + prefix_len, tmp, res.len);
+ memcpy(buf, prefix, prefix_len);
+ res.len += prefix_len;
+ }
+ _syscall_fs_respond(NULL, _syscall_open(buf, res.len, res.flags), FSR_DELEGATE);
+ break;
+
+ default:
+ _syscall_fs_respond(NULL, -1, 0);
+ break;
+ }
+ }
+ _syscall_exit(0);
+}
+
+void fs_whitelist(const char **list) {
+ struct fs_wait_response res;
+ const size_t buf_len = 1024;
+ char *buf = malloc(buf_len);
+ bool allow;
+ if (!buf) _syscall_exit(1);
+
+ while (!_syscall_fs_wait(buf, buf_len, &res)) {
+ switch (res.op) {
+ case VFSOP_OPEN:
+ allow = false;
+ // TODO reverse dir_inject
+ for (const char **iter = list; *iter; iter++) {
+ size_t len = strlen(*iter); // inefficient, whatever
+ if (len <= res.len && !memcmp(buf, *iter, len)) {
+ allow = true;
+ break;
+ }
+ }
+ _syscall_fs_respond(NULL, allow ? _syscall_open(buf, res.len, res.flags) : -1, FSR_DELEGATE);
+ break;
+
+ default:
+ _syscall_fs_respond(NULL, -1, 0);
+ break;
+ }
+ }
+ _syscall_exit(0);
+}
+
+
+void fs_dir_inject(const char *path) {
+ struct fs_dir_handle {
+ const char *inject;
+ int delegate, inject_len;
+ };
+
+ const size_t path_len = strlen(path);
+ struct fs_wait_response res;
+ struct fs_dir_handle *data;
+ const size_t buf_len = 1024;
+ char *buf = malloc(buf_len);
+ int ret, inject_len;
+
+ if (!buf) _syscall_exit(1);
+
+ while (!_syscall_fs_wait(buf, buf_len, &res)) {
+ data = res.id;
+ switch (res.op) {
+ case VFSOP_OPEN:
+ if (buf[res.len - 1] == '/' &&
+ res.len < path_len && !memcmp(path, buf, res.len))
+ {
+ /* opening a directory that we're injecting into */
+
+ if (res.flags & OPEN_CREATE) {
+ _syscall_fs_respond(NULL, -1, 0);
+ break;
+ }
+
+ data = malloc(sizeof *data);
+ data->delegate = _syscall_open(buf, res.len, res.flags);
+ data->inject = path + res.len;
+
+ /* inject up to the next slash */
+ inject_len = 0;
+ while (data->inject[inject_len] && data->inject[inject_len] != '/')
+ inject_len++;
+ if (data->inject[inject_len] == '/')
+ inject_len++;
+ data->inject_len = inject_len;
+
+ _syscall_fs_respond(data, 0, 0);
+ } else {
+ _syscall_fs_respond(NULL, _syscall_open(buf, res.len, res.flags), FSR_DELEGATE);
+ }
+ break;
+
+ case VFSOP_CLOSE:
+ if (data->delegate >= 0)
+ _syscall_close(data->delegate);
+ _syscall_fs_respond(NULL, 0, 0);
+ break;
+
+ case VFSOP_READ:
+ if (res.offset > 0) _syscall_fs_respond(NULL, 0, 0); // TODO working offsets
+
+ int out_len = data->inject_len;
+ memcpy(buf, data->inject, out_len);
+ buf[out_len++] = '\0';
+
+ if (data->delegate >= 0) {
+ int to_read = res.capacity < buf_len ? res.capacity : buf_len;
+ to_read -= out_len;
+ ret = _syscall_read(data->delegate, buf + out_len, to_read, 0);
+ if (ret > 0) out_len += ret;
+ // TODO deduplicate entries
+ }
+
+ _syscall_fs_respond(buf, out_len, 0);
+ break;
+
+ case VFSOP_WRITE:
+ if (data->delegate >= 0)
+ ret = _syscall_write(data->delegate, buf, res.len, res.offset);
+ else
+ ret = -1;
+ _syscall_fs_respond(NULL, ret, 0);
+ break;
+
+ default:
+ _syscall_fs_respond(NULL, -1, 0);
+ break;
+ }
+ }
+ _syscall_exit(0);
+}
diff --git a/src/user/fs/misc.h b/src/user/fs/misc.h
new file mode 100644
index 0000000..3a8b071
--- /dev/null
+++ b/src/user/fs/misc.h
@@ -0,0 +1,16 @@
+#pragma once
+#include <user/lib/stdlib.h>
+#include <stdbool.h>
+
+bool fork2_n_mount(const char *path);
+
+void fs_passthru(const char *prefix);
+void fs_whitelist(const char **list);
+
+void fs_dir_inject(const char *path);
+
+/** Mounts something and injects its path into the fs */
+// TODO path needs to have a trailing slash
+#define MOUNT(path, impl) \
+ if (!fork2_n_mount(path)) {_klogf("impl %s", path); impl;} \
+ if (!fork2_n_mount("/")) {_klogf("dir for %s", path); fs_dir_inject(path);}
diff --git a/src/user/fs/tar.c b/src/user/fs/tar.c
new file mode 100644
index 0000000..4b4a9a3
--- /dev/null
+++ b/src/user/fs/tar.c
@@ -0,0 +1,162 @@
+#include <user/lib/stdlib.h>
+#include <shared/flags.h>
+#include <shared/syscalls.h>
+#include <stdint.h>
+
+#define BUF_SIZE 64
+
+static void *tar_open(const char *path, int len, void *base, size_t base_len);
+static void tar_read(struct fs_wait_response *res, void *base, size_t base_len);
+static int tar_size(void *sector);
+static void *tar_find(const char *path, size_t path_len, void *base, size_t base_len);
+static int oct_parse(char *str, size_t len);
+
+
+static const char *root_fakemeta = ""; /* see comment in tar_open */
+
+
+void tar_driver(void *base) {
+ static char buf[BUF_SIZE];
+ struct fs_wait_response res;
+ void *ptr;
+ while (!_syscall_fs_wait(buf, BUF_SIZE, &res)) {
+ switch (res.op) {
+ case VFSOP_OPEN:
+ if (res.flags & OPEN_CREATE) {
+ _syscall_fs_respond(NULL, -1, 0);
+ break;
+ }
+ ptr = tar_open(buf, res.len, base, ~0);
+ _syscall_fs_respond(ptr, ptr ? 0 : -1, 0);
+ break;
+
+ case VFSOP_READ:
+ tar_read(&res, base, ~0);
+ break;
+
+ default:
+ _syscall_fs_respond(NULL, -1, 0); // unsupported
+ break;
+ }
+ }
+ _syscall_exit(0);
+}
+
+static void *tar_open(const char *path, int len, void *base, size_t base_len) {
+ if (len <= 0) return NULL;
+ path += 1; // skip the leading slash
+ len -= 1;
+
+ /* TAR archives don't (seem to) contain an entry for the root dir, so i'm
+ * returning a fake one. this isn't a full entry because i'm currently too
+ * lazy to create a full one - thus, it has to be special cased in tar_read */
+ if (len == 0)
+ return (void*)root_fakemeta;
+
+ return tar_find(path, len, base, base_len);
+}
+
+static void tar_read(struct fs_wait_response *res, void *base, size_t base_len) {
+ void *meta = (void*)res->id;
+ char type = *(char*)(meta + 156);
+ size_t meta_len;
+ int size;
+
+ static char buf[BUF_SIZE]; // TODO reuse a single buffer
+ size_t buf_pos = 0;
+
+ if (meta == root_fakemeta) type = '5'; /* see comment in tar_open() */
+
+ switch (type) {
+ case '\0':
+ case '0': /* normal files */
+ size = tar_size(meta);
+ if (res->offset < 0 || res->offset > size) {
+ // TODO support negative offsets
+ _syscall_fs_respond(NULL, -1, 0);
+ } else {
+ _syscall_fs_respond(meta + 512 + res->offset, size - res->offset, 0);
+ }
+ break;
+
+ case '5': /* directory */
+ meta_len = strlen(meta);
+ size_t to_skip = res->offset;
+
+ /* find files in dir */
+ for (size_t off = 0; off < base_len;) {
+ if (0 != memcmp(base + off + 257, "ustar", 5))
+ break; // not a metadata sector
+ // TODO more meaningful variable names and clean code up
+
+ /* check if prefix matches */
+ if (0 == memcmp(base + off, meta, meta_len) &&
+ *(char*)(base + off + meta_len) != '\0') {
+ char *suffix = base + off + meta_len;
+ size_t suffix_len = strlen(suffix);
+
+ /* check if the path contains any non-trailing slashes */
+ char *next = suffix;
+ while (*next && *next != '/') next++;
+ if (*next == '/') next++;
+ if (*next == '\0') {
+ if (to_skip > suffix_len) {
+ to_skip -= suffix_len;
+ } else {
+ suffix += to_skip;
+ suffix_len -= to_skip;
+ to_skip = 0;
+
+ /* it doesn't - so let's add it to the result */
+ memcpy(buf + buf_pos, suffix, suffix_len);
+ buf[buf_pos + suffix_len] = '\0';
+ buf_pos += suffix_len + 1;
+ // TODO no buffer overrun check
+ }
+ }
+ }
+
+ size = tar_size(base + off);
+ off += 512; // skip this metadata sector
+ off += (size + 511) & ~511; // skip the data sectors
+ }
+
+ _syscall_fs_respond(buf, buf_pos, 0);
+ break;
+
+ default:
+ _syscall_fs_respond(NULL, -1, 0);
+ break;
+ }
+}
+
+static int tar_size(void *sector) {
+ return oct_parse(sector + 124, 11);
+}
+
+static void *tar_find(const char *path, size_t path_len, void *base, size_t base_len) {
+ int size;
+ if (path_len > 100) return NULL; // illegal path
+
+ for (size_t off = 0; off < base_len;) {
+ if (0 != memcmp(base + off + 257, "ustar", 5))
+ break; // not a metadata sector
+ if (0 == memcmp(base + off, path, path_len) &&
+ *(char*)(base + off + path_len) == '\0')
+ return base + off; // file found, quit
+
+ size = tar_size(base + off);
+ off += 512; // skip this metadata sector
+ off += (size + 511) & ~511; // skip the data sectors
+ }
+ return NULL;
+}
+
+static int oct_parse(char *str, size_t len) {
+ int res = 0;
+ for (size_t i = 0; i < len; i++) {
+ res *= 8;
+ res += str[i] - '0'; // no format checking
+ }
+ return res;
+}
diff --git a/src/user/fs/tar.h b/src/user/fs/tar.h
new file mode 100644
index 0000000..c1dee78
--- /dev/null
+++ b/src/user/fs/tar.h
@@ -0,0 +1,4 @@
+#pragma once
+#include <shared/types.h>
+
+_Noreturn void tar_driver(void *base);