diff options
Diffstat (limited to 'src/user/fs')
-rw-r--r-- | src/user/fs/misc.c | 168 | ||||
-rw-r--r-- | src/user/fs/misc.h | 16 | ||||
-rw-r--r-- | src/user/fs/tar.c | 162 | ||||
-rw-r--r-- | src/user/fs/tar.h | 4 |
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); |