diff options
Diffstat (limited to 'src/user/lib/fs')
-rw-r--r-- | src/user/lib/fs/dir.c | 84 | ||||
-rw-r--r-- | src/user/lib/fs/dirinject.c | 129 | ||||
-rw-r--r-- | src/user/lib/fs/misc.c | 160 | ||||
-rw-r--r-- | src/user/lib/fs/whitelist.c | 114 |
4 files changed, 0 insertions, 487 deletions
diff --git a/src/user/lib/fs/dir.c b/src/user/lib/fs/dir.c deleted file mode 100644 index b7f840d..0000000 --- a/src/user/lib/fs/dir.c +++ /dev/null @@ -1,84 +0,0 @@ -#include <camellia/syscalls.h> -#include <errno.h> -#include <limits.h> -#include <string.h> -#include <camellia/fs/dir.h> - -void dir_start(struct dirbuild *db, long offset, char *buf, size_t buflen) { - db->offset = offset; - db->buf = buf; - db->bpos = 0; - db->blen = buflen; - db->error = 0; - - // TODO decide how negative directory offsets should be handled - if (offset < 0) db->error = -ENOSYS; -} - -bool dir_append(struct dirbuild *db, const char *name) { - return dir_appendl(db, name, strlen(name)); -} - -bool dir_appendl(struct dirbuild *db, const char *name, size_t len) { - if (db->error) return true; - if (len > (size_t)LONG_MAX) { - db->error = -1; - return true; - } - - len++; // account for the null byte - - if (db->offset < (long)len) { - name += db->offset; - len -= db->offset; - db->offset = 0; - - if (db->buf) { - // TODO no buffer overrun check - memcpy(db->buf + db->bpos, name, len - 1); - db->buf[db->bpos + len - 1] = '\0'; - } - db->bpos += len; - } else { - db->offset -= len; - } - return false; -} - -bool dir_append_from(struct dirbuild *db, hid_t h) { - if (db->error) return true; - if (db->buf && db->bpos == db->blen) return false; - - int ret; - if (db->buf) { - ret = _sys_read(h, db->buf + db->bpos, db->blen - db->bpos, db->offset); - if (ret < 0) { - db->error = ret; - return true; - } else if (ret > 0) { - /* not eof */ - db->offset = 0; - db->bpos += ret; - return false; - } /* else ret == 0, EOF, need getsize */ - } - - ret = _sys_getsize(h); - if (ret < 0) { - db->error = ret; - return true; - } - if (db->offset < ret) { - /* should only occur when !buf, otherwise leaks previous data from buf. - * TODO consider impact */ - db->bpos += ret - db->offset; - db->offset = 0; - } else { - db->offset -= ret; - } - return false; -} - -long dir_finish(struct dirbuild *db) { - return db->error ? db->error : db->bpos; -} diff --git a/src/user/lib/fs/dirinject.c b/src/user/lib/fs/dirinject.c deleted file mode 100644 index 9b08756..0000000 --- a/src/user/lib/fs/dirinject.c +++ /dev/null @@ -1,129 +0,0 @@ -#include <assert.h> -#include <camellia/fs/dir.h> -#include <camellia/fs/misc.h> -#include <camellia/syscalls.h> -#include <errno.h> -#include <string.h> -#include <sys/param.h> -#include <unistd.h> - -typedef struct Handle { - int delegate; - int plen; - char path[]; -} Handle; - -static int -dir_seglen(const char *path) -{ - /* if path contains /, return its position + 1 - * otherwise, return strlen */ - int len = 0; - while (path[len]) { - if (path[len] == '/') { - len++; - break; - } - len++; - } - return len; -} - -static int -find_injects(const char *injects[], const char *path, int plen, struct dirbuild *db) -{ - // TODO deduplicate - const char *inj; - int matches = 0; - assert(plen >= 1); - assert(path[plen-1] == '/'); - - while ((inj = *injects++)) { - int ilen = strlen(inj); - if (plen < ilen && memcmp(path, inj, plen) == 0) { - if (db) { - /* inj[plen-1] == '/' */ - const char *ent = inj + plen; - dir_appendl(db, ent, dir_seglen(ent)); - } - matches++; - } - } - return matches; -} - -void -fs_dirinject2(const char *injects[]) -{ - const size_t buflen = 4096; - char *buf = malloc(buflen); - if (!buf) exit(1); - - for (;;) { - struct ufs_request req; - hid_t reqh = _sys_fs_wait(buf, buflen, &req); - if (reqh < 0) break; - Handle *hndl = req.id; - switch (req.op) { - case VFSOP_OPEN: { - if (buf[req.len - 1] == '/') { - if (find_injects(injects, buf, req.len, NULL) > 0) { - /* opening a directory that we're injecting into */ - hndl = malloc(sizeof(Handle) + req.len); - if (hndl == NULL) { - _sys_fs_respond(reqh, NULL, -EGENERIC, 0); - break; - } - /* ignore errors from _sys_open */ - hndl->delegate = _sys_open(buf, req.len, req.flags); - hndl->plen = req.len; - memcpy(hndl->path, buf, hndl->plen); - _sys_fs_respond(reqh, hndl, 0, 0); - break; - } - } - /* default behaviour */ - forward_open(reqh, buf, req.len, req.flags); - break; - } - - case VFSOP_CLOSE: { - if (hndl->delegate >= 0) { - close(hndl->delegate); - } - free(hndl); - _sys_fs_respond(reqh, NULL, 0, 0); - break; - } - - case VFSOP_READ: - case VFSOP_GETSIZE: { - struct dirbuild db; - char *target = NULL; - if (req.op == VFSOP_READ) { - target = buf; - } - req.capacity = MIN(req.capacity, buflen); - - dir_start(&db, req.offset, target, req.capacity); - find_injects(injects, hndl->path, hndl->plen, &db); - if (hndl->delegate >= 0) { - dir_append_from(&db, hndl->delegate); - } - _sys_fs_respond(reqh, target, dir_finish(&db), 0); - break; - } - - default: { - _sys_fs_respond(reqh, NULL, -1, 0); - break; - } - } - } - exit(0); -} - -void -fs_dirinject(const char *path) { - fs_dirinject2((const char*[]){ path, NULL }); -} diff --git a/src/user/lib/fs/misc.c b/src/user/lib/fs/misc.c deleted file mode 100644 index 30e5ab4..0000000 --- a/src/user/lib/fs/misc.c +++ /dev/null @@ -1,160 +0,0 @@ -#include <camellia/flags.h> -#include <camellia/syscalls.h> -#include <errno.h> -#include <stdbool.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> -#include <camellia/compat.h> -#include <camellia/fs/dir.h> -#include <camellia/fs/misc.h> - -void forward_open(hid_t reqh, const char *path, long len, int flags) { - // TODO use threads - // TODO solve for more complex cases, e.g. fs_union - /* done in a separate thread/process because open() can block, - * but that should only hold the caller back, and not the fs driver. - * - * for example, running `httpd` in one term would prevent you from doing - * basically anything on the second term, because fs_dirinject would be - * stuck on open()ing the socket */ - if (!_sys_fork(FORK_NOREAP, NULL)) { - _sys_fs_respond(reqh, NULL, _sys_open(path, len, flags), FSR_DELEGATE); - exit(0); - } - close(reqh); -} - -void fs_passthru(const char *prefix) { - const size_t buflen = 1024; - char *buf = malloc(buflen); - int prefix_len = prefix ? strlen(prefix) : 0; - if (!buf) exit(1); - - for (;;) { - struct ufs_request res; - hid_t reqh = _sys_fs_wait(buf, buflen, &res); - if (reqh < 0) break; - switch (res.op) { - case VFSOP_OPEN: - if (prefix) { - if (prefix_len + res.len > buflen) { - _sys_fs_respond(reqh, NULL, -1, 0); - break; - } - - memmove(buf + prefix_len, buf, res.len); - memcpy(buf, prefix, prefix_len); - res.len += prefix_len; - } - forward_open(reqh, buf, res.len, res.flags); - break; - - default: - _sys_fs_respond(reqh, NULL, -1, 0); - break; - } - } - exit(0); -} - -void fs_union(const char **list) { - struct ufs_request res; - - /* the buffer is split into two halves: - * the second one is filled out with the path by fs_wait - * the first one is used for the prepended paths */ - const size_t buflen = 1024; - const size_t prelen = 512; - const size_t postlen = buflen - prelen; - char *pre = malloc(buflen); - char *post = pre + prelen; - long ret; - struct dirbuild db; - if (!pre) exit(1); - - while (!c0_fs_wait(post, postlen, &res)) { - switch (res.op) { - case VFSOP_OPEN: - if (res.len == 1) { /* root directory */ - c0_fs_respond(NULL, 0, 0); - break; - } - - ret = -1; - for (size_t i = 0; ret < 0 && list[i]; i++) { - const char *prefix = list[i]; - size_t prefixlen = strlen(prefix); // TODO cache - if (prefixlen > prelen) continue; - char *path = post - prefixlen; - memcpy(path, prefix, prefixlen); - - ret = _sys_open(path, prefixlen + res.len, res.flags); - - post[res.len] = '\0'; - } - if (ret < 0) ret = -1; - c0_fs_respond(NULL, ret, FSR_DELEGATE); - break; - - case VFSOP_READ: - case VFSOP_GETSIZE: - if (res.capacity > buflen) - res.capacity = buflen; - bool end = false; - char *target = res.op == VFSOP_READ ? pre : NULL; - dir_start(&db, res.offset, target, res.capacity); - for (size_t i = 0; !end && list[i]; i++) { - const char *prefix = list[i]; - size_t prefixlen = strlen(prefix); - // TODO only open the directories once - // TODO ensure trailing slash - hid_t h = _sys_open(prefix, prefixlen, OPEN_READ); - if (h < 0) continue; - end = end || dir_append_from(&db, h); - _sys_close(h); - } - c0_fs_respond(target, dir_finish(&db), 0); - break; - - default: - c0_fs_respond(NULL, -1, 0); - break; - } - } - exit(0); -} - -hid_t ufs_wait(char *buf, size_t len, struct ufs_request *req) { - hid_t reqh; - for (;;) { - reqh = _sys_fs_wait(buf, len, req); - if (reqh < 0) break; - if (req->op == VFSOP_OPEN) { - if (req->len == len) { - _sys_fs_respond(reqh, NULL, -ENAMETOOLONG, 0); - continue; - } - buf[req->len] = '\0'; - // TODO ensure passed paths don't have null bytes in them in the kernel - } - break; - } - return reqh; -} - -int mount_at(const char *path) { - hid_t h; - int ret = _sys_fork(FORK_NEWFS, &h); - if (ret == 0) { - _klogf("%s: impl", path); - setproctitle("%s", path); - } else if (ret > 0) { - _sys_mount(h, path, strlen(path)); - close(h); - } else { - _sys_mount(HANDLE_NULLFS, path, strlen(path)); - } - return ret; -} diff --git a/src/user/lib/fs/whitelist.c b/src/user/lib/fs/whitelist.c deleted file mode 100644 index 54a79c3..0000000 --- a/src/user/lib/fs/whitelist.c +++ /dev/null @@ -1,114 +0,0 @@ -#include <camellia/flags.h> -#include <camellia/syscalls.h> -#include <errno.h> -#include <stdlib.h> -#include <string.h> -#include <camellia/fs/dir.h> -#include <camellia/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; - hid_t reqh = _sys_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 error = false; - 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)) { - passthru = true; - if (ro && OPEN_WRITEABLE(res.flags)) - error = 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 (error) { - _sys_fs_respond(reqh, NULL, -EACCES, 0); - } else 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'; - _sys_fs_respond(reqh, ipath, 0, 0); - } else { - _sys_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)); - } - _sys_fs_respond(reqh, target, dir_finish(&db), 0); - break; - } - case VFSOP_CLOSE: { - free(ipath); - _sys_fs_respond(reqh, NULL, 0, 0); - break; - } - default: { - _sys_fs_respond(reqh, NULL, -1, 0); - break; - } - } - } - exit(0); -} |