summaryrefslogtreecommitdiff
path: root/src/libc/fs/misc.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libc/fs/misc.c')
-rw-r--r--src/libc/fs/misc.c160
1 files changed, 160 insertions, 0 deletions
diff --git a/src/libc/fs/misc.c b/src/libc/fs/misc.c
new file mode 100644
index 0000000..30e5ab4
--- /dev/null
+++ b/src/libc/fs/misc.c
@@ -0,0 +1,160 @@
+#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;
+}