diff options
Diffstat (limited to 'src/init/fs')
-rw-r--r-- | src/init/fs/misc.c | 225 |
1 files changed, 74 insertions, 151 deletions
diff --git a/src/init/fs/misc.c b/src/init/fs/misc.c index 701db9f..a218bf5 100644 --- a/src/init/fs/misc.c +++ b/src/init/fs/misc.c @@ -15,103 +15,30 @@ bool fork2_n_mount(const char *path) { return false; } -static void fs_respond_delegate(struct fs_wait_response *res, handle_t delegate, const char *og_buf) { - /* The idea behind this function is that many fs implementations (e.g. for - * overlay fs) will want to forward received requests to the original fs - * implementation. - * - * Having some special support for this in the kernel makes sense - it would - * avoid a lot of unnecessary copies. However, it wouldn't be necessary for - * a working kernel - and since I want the "base" of this system to be a small - * as possible, I want to have an alternative implementation of this in the - * userland which works in exactly the same way. The libc would then choose - * the best available implementation. - * - * (note: this wouldn't be an issue if i treated everything like functions - * in the ruby prototype) - * - * This function was supposed to avoid unnecessary copies on delegated reads. - * It is being executed /after/ the write read, though. I don't currently see - * a good way to fix that. Here are a few loose ideas: - * - a fs_wait flag which requires a separate syscall to get the write copy - * - a way to "mark" returned handles - * - no write copy mark - * - mark with delegate handle - * every vfs op would go directly to that delegate handle - * probably the fastest way - * - treat open() just like in the ruby prototype - * Instead of just returning a handle, the driver would return a list - * of functions. Here's how it would work, in pseudocode: - * ``` - * # for full delegation - * respond_new_handle({ - * read: {DELEGATE, handle}, // extract the read function from `handle` - * write: {DELEGATE, handle}, - * close: {DELEGATE, handle} - * }); - * ``` - */ - static char buf[1024]; - int size; - int ret; - - switch (res->op) { - case VFSOP_READ: - if (_syscall_fork(FORK_NOREAP, NULL)) { - // handle reads in a child - // this is a HORRIBLE workaround for making concurrent IO work without proper delegates - break; - } - // TODO instead of truncating the size, allocate a bigger buffer - size = res->capacity < sizeof(buf) ? res->capacity : sizeof(buf); - ret = _syscall_read(delegate, buf, size, res->offset); - _syscall_fs_respond(buf, ret, 0); - _syscall_exit(0); - break; - - // TODO proper writing (see above) - case VFSOP_WRITE: - ret = _syscall_write(delegate, og_buf, res->len, res->offset); - _syscall_fs_respond(NULL, ret, 0); - break; - - case VFSOP_CLOSE: - _syscall_close(delegate); - // isn't it kinda weird that i even have to respond to close()s? - // i suppose it makes the API more consistent - _syscall_fs_respond(NULL, 0, 0); - break; - - default: - /* unsupported / unexpected */ - _syscall_fs_respond(NULL, -1, 0); - break; - } -} - void fs_passthru(const char *prefix) { struct fs_wait_response res; - static char buf[1024]; - int ret, prefix_len; - if (prefix) prefix_len = strlen(prefix); + 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, sizeof(buf), &res)) { + while (!_syscall_fs_wait(buf, buf_len, &res)) { switch (res.op) { case VFSOP_OPEN: if (prefix) { - /* special case: rewriting the path */ - if (prefix_len + res.len <= sizeof(buf)) { - // TODO memmove - char tmp[64]; - memcpy(tmp, buf, res.len); - memcpy(buf, prefix, prefix_len); - memcpy(buf + prefix_len, tmp, res.len); - ret = _syscall_open(buf, res.len + prefix_len, res.flags); - } else ret = -1; - } else { - ret = _syscall_open(buf, res.len, res.flags); + 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, ret, FSR_DELEGATE); + _syscall_fs_respond(NULL, _syscall_open(buf, res.len, res.flags), FSR_DELEGATE); break; default: @@ -124,10 +51,12 @@ void fs_passthru(const char *prefix) { void fs_whitelist(const char **list) { struct fs_wait_response res; - static char buf[1024]; + const size_t buf_len = 1024; + char *buf = malloc(buf_len); bool allow; + if (!buf) _syscall_exit(1); - while (!_syscall_fs_wait(buf, sizeof(buf), &res)) { + while (!_syscall_fs_wait(buf, buf_len, &res)) { switch (res.op) { case VFSOP_OPEN: allow = false; @@ -153,92 +82,86 @@ void fs_whitelist(const char **list) { void fs_dir_inject(const char *path) { struct fs_dir_handle { - bool taken; - int delegate; const char *inject; + int delegate, inject_len; }; const size_t path_len = strlen(path); struct fs_wait_response res; - struct fs_dir_handle handles[16] = {0}; // TODO hardcoded FD_MAX - use malloc instead - static char buf[1024]; - int ret; + 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, sizeof buf, &res)) { + while (!_syscall_fs_wait(buf, buf_len, &res)) { + data = res.id; switch (res.op) { case VFSOP_OPEN: - int hid = -1; - for (int hid2 = 0; hid2 * sizeof(*handles) < sizeof(handles); hid2++) { - if (!handles[hid2].taken) { - hid = hid2; + 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; } - } - if (hid < 0) _syscall_fs_respond(NULL, -2, 0); // we ran out of handles - ret = _syscall_open(buf, res.len, res.flags); /* errors handled in inject handler */ - handles[hid].delegate = ret; - handles[hid].inject = NULL; - handles[hid].taken = true; + data = malloc(sizeof *data); + data->delegate = _syscall_open(buf, res.len, res.flags); + data->inject = path + res.len; - if (buf[res.len - 1] == '/' && - res.len < path_len && !memcmp(path, buf, res.len)) { - /* we're injecting */ - handles[hid].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; - /* if we're making up the opened directory, disallow create */ - if (ret < 0 && (res.flags & OPEN_CREATE)) hid = ret; - _syscall_fs_respond((void*)hid, hid, 0); + _syscall_fs_respond(data, 0, 0); } else { - /* not injecting, don't allow opening nonexistent stuff */ - - handles[hid].taken = false; - _syscall_fs_respond(NULL, ret, FSR_DELEGATE); + _syscall_fs_respond(NULL, _syscall_open(buf, res.len, res.flags), FSR_DELEGATE); } break; case VFSOP_CLOSE: - if (handles[(int)res.id].delegate >= 0) - _syscall_close(handles[(int)res.id].delegate); - handles[(int)res.id].taken = false; + if (data->delegate >= 0) + _syscall_close(data->delegate); _syscall_fs_respond(NULL, 0, 0); break; case VFSOP_READ: - if (handles[(int)res.id].inject) { - if (res.offset > 0) _syscall_fs_respond(NULL, 0, 0); // TODO working offsets - struct fs_dir_handle h = handles[(int)res.id]; - - int out_len = 0; - while (h.inject[out_len] && h.inject[out_len] != '/') - out_len++; - if (h.inject[out_len] == '/') - out_len++; - memcpy(buf, h.inject, out_len); - buf[out_len++] = '\0'; - - if (h.delegate >= 0) { - int to_read = res.capacity < sizeof(buf) ? res.capacity : sizeof(buf); - to_read -= out_len; - ret = _syscall_read(h.delegate, buf + out_len, to_read, 0); - if (ret > 0) out_len += ret; - // TODO deduplicate entries - } - - _syscall_fs_respond(buf, out_len, 0); - break; + 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 } - /* fallthrough */ + _syscall_fs_respond(buf, out_len, 0); + break; - default: { - struct fs_dir_handle h = handles[(int)res.id]; - if (h.delegate < 0) - _syscall_fs_respond(NULL, -1, 0); + case VFSOP_WRITE: + if (data->delegate >= 0) + ret = _syscall_write(data->delegate, buf, res.len, res.offset); else - fs_respond_delegate(&res, h.delegate, buf); + ret = -1; + _syscall_fs_respond(NULL, ret, 0); + break; + + default: + _syscall_fs_respond(NULL, -1, 0); break; - } } } _syscall_exit(0); |