diff options
-rw-r--r-- | src/user/lib/fs/misc.c | 81 | ||||
-rw-r--r-- | src/user/lib/fs/whitelist.c | 109 |
2 files changed, 110 insertions, 80 deletions
diff --git a/src/user/lib/fs/misc.c b/src/user/lib/fs/misc.c index 7a66b80..f660f6f 100644 --- a/src/user/lib/fs/misc.c +++ b/src/user/lib/fs/misc.c @@ -22,8 +22,7 @@ bool fork2_n_mount(const char *path) { } static int dir_seglen(const char *path) { - /* in human terms: - * if path contains /, return its position + 1 + /* if path contains /, return its position + 1 * otherwise, return strlen */ int len = 0; while (path[len]) { @@ -85,84 +84,6 @@ void fs_passthru(const char *prefix) { exit(0); } -void fs_whitelist(const char **list) { - const size_t buflen = 1024; - char *buf = malloc(buflen); - if (!buf) exit(1); - for (;;) { - struct ufs_request res; - handle_t reqh = _syscall_fs_wait(buf, buflen, &res); - if (reqh < 0) break; - - char *ipath = res.id; - size_t blen; - bool passthru, inject; - struct dirbuild db; - - switch (res.op) { - case VFSOP_OPEN: - passthru = false; - inject = false; - - for (const char **iter = list; *iter; iter++) { - size_t len = strlen(*iter); - bool ro = false; - if (len >= 3 && !memcmp(*iter + len - 3, ":ro", 3)) { - ro = true; - len -= 3; - } - if (len <= res.len && !memcmp(buf, *iter, len)) { - if (ro) res.flags |= OPEN_RO; - passthru = true; - break; - } - if (res.len < len && - buf[res.len - 1] == '/' && - !memcmp(buf, *iter, res.len)) - { - inject = true; - } - } - if (passthru) { - forward_open(reqh, buf, res.len, res.flags); - } else if (inject) { - // TODO all the inject points could be precomputed - ipath = malloc(res.len + 1); - memcpy(ipath, buf, res.len); - ipath[res.len] = '\0'; - _syscall_fs_respond(reqh, ipath, 0, 0); - } else { - _syscall_fs_respond(reqh, NULL, -1, 0); - } - break; - - case VFSOP_READ: - case VFSOP_GETSIZE: - blen = strlen(ipath); - char *target = res.op == VFSOP_READ ? buf : NULL; - dir_start(&db, res.offset, target, buflen); - for (const char **iter = list; *iter; iter++) { - // TODO could be precomputed too - size_t len = strlen(*iter); // inefficient, whatever - if (blen < len && !memcmp(ipath, *iter, blen)) - dir_appendl(&db, *iter + blen, dir_seglen(*iter + blen)); - } - _syscall_fs_respond(reqh, target, dir_finish(&db), 0); - break; - - case VFSOP_CLOSE: - free(ipath); - _syscall_fs_respond(reqh, NULL, 0, 0); - break; - - default: - _syscall_fs_respond(reqh, NULL, -1, 0); - break; - } - } - exit(0); -} - void fs_union(const char **list) { struct ufs_request res; diff --git a/src/user/lib/fs/whitelist.c b/src/user/lib/fs/whitelist.c new file mode 100644 index 0000000..676b36f --- /dev/null +++ b/src/user/lib/fs/whitelist.c @@ -0,0 +1,109 @@ +#include <camellia/flags.h> +#include <camellia/syscalls.h> +#include <stdlib.h> +#include <string.h> +#include <user/lib/fs/dir.h> +#include <user/lib/fs/misc.h> + +static int dir_seglen2(const char *path, size_t len) { + /* returns the length of the first segment of the path, including the trailing / (if any). */ + for (size_t i = 0; i < len; i++) { + if (path[i] == '/') + return i + 1; + } + return len; +} + +/** @return the length of the path w/o suffixes */ +static size_t suffix_parse(const char *path, size_t len, bool *ro_ptr) { + bool ro = false; + if (len >= 3 && !memcmp(path + len - 3, ":ro", 3)) { + ro = true; + len -= 3; + } + if (ro_ptr) *ro_ptr = ro; + return len; +} + +/** Check if a path is a prefix of another path. */ +// TODO move to libc; tests +static bool prefix_match(const char *prefix, size_t plen, const char *full, size_t flen) { + if (flen < plen) return false; + if (flen == plen) + return memcmp(full, prefix, flen) == 0; + return plen >= 1 + && prefix[plen - 1] == '/' /* prefixes must point to one of the parent directories */ + && memcmp(full, prefix, plen) == 0; +} + +void fs_whitelist(const char **whitelist) { + const size_t buflen = 1024; + char *buf = malloc(buflen); + if (!buf) exit(1); + for (;;) { + struct ufs_request res; + handle_t reqh = _syscall_fs_wait(buf, buflen, &res); + if (reqh < 0) break; + + char *ipath = res.id; /* the path of the open()ed directory */ + + switch (res.op) { + case VFSOP_OPEN: { + bool passthru = false; + bool inject = false; + + for (const char **entry = whitelist; *entry; entry++) { + bool ro = false; + size_t entry_len = suffix_parse(*entry, strlen(*entry), &ro); + /* If *entry is a prefix of the opened path, pass the open() through. */ + if (prefix_match(*entry, entry_len, buf, res.len)) { + if (ro) res.flags |= OPEN_RO; + passthru = true; + break; + } + /* If the path is a prefix of *entry, we might need to inject a directory. */ + if (prefix_match(buf, res.len, *entry, entry_len)) { + inject = true; + } + } + if (passthru) { + forward_open(reqh, buf, res.len, res.flags); + } else if (inject) { + // TODO all the inject points could be precomputed + ipath = malloc(res.len + 1); + memcpy(ipath, buf, res.len); + ipath[res.len] = '\0'; + _syscall_fs_respond(reqh, ipath, 0, 0); + } else { + _syscall_fs_respond(reqh, NULL, -1, 0); + } + break; + } + case VFSOP_READ: + case VFSOP_GETSIZE: { + struct dirbuild db; + size_t ilen = strlen(ipath); + char *target = res.op == VFSOP_READ ? buf : NULL; + dir_start(&db, res.offset, target, buflen); + for (const char **entry = whitelist; *entry; entry++) { + // TODO could be precomputed too + size_t elen = suffix_parse(*entry, strlen(*entry), NULL); + if (ilen < elen && !memcmp(ipath, *entry, ilen)) + dir_appendl(&db, *entry + ilen, dir_seglen2(*entry + ilen, elen - ilen)); + } + _syscall_fs_respond(reqh, target, dir_finish(&db), 0); + break; + } + case VFSOP_CLOSE: { + free(ipath); + _syscall_fs_respond(reqh, NULL, 0, 0); + break; + } + default: { + _syscall_fs_respond(reqh, NULL, -1, 0); + break; + } + } + } + exit(0); +} |