#include #include #include #include #include #include #include #include #include #include #include #include typedef struct { hid_t reqh; char *path; long len; int flags; } Delegate; static void delegate_thread(void *p) { // TODO don't share file descriptors with parent Delegate *d = p; _sys_fs_respond(d->reqh, NULL, _sys_open(d->path, d->len, d->flags), FSR_DELEGATE); free(d->path); free(d); } void forward_open(hid_t reqh, const char *path, long len, int flags) { Delegate *d = malloc(sizeof(Delegate)); d->reqh = reqh; d->path = strdup(path); d->len = len; d->flags = flags; thread_create(FORK_NOREAP, delegate_thread, d); } 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; } // TODO resolve union directories in a thread 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'; } 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(-1, path, strlen(path)); } return ret; }