diff options
Diffstat (limited to 'src/bin')
25 files changed, 1169 insertions, 193 deletions
diff --git a/src/bin/include/bin.h b/src/bin/include/bin.h index a5528c5..34558f9 100644 --- a/src/bin/include/bin.h +++ b/src/bin/include/bin.h @@ -20,8 +20,10 @@ #define BIN_H #include <sys/types.h> +#include <fs/fs.h> -int bin_exec(const char *path, const char *const *argv, uid_t uid, gid_t gid); +int bin_exec(const char *path, const char *const *argv, + const struct fs_stdstreams *ss, pid_t parent, uid_t uid, gid_t gid); int bin_update(void); #endif diff --git a/src/bin/private_include/bin/proc.h b/src/bin/private_include/bin/proc.h index cf0c04e..92f1566 100644 --- a/src/bin/private_include/bin/proc.h +++ b/src/bin/private_include/bin/proc.h @@ -43,6 +43,8 @@ int bin_proc_s_pop(void *dst, size_t n, void *user); int bin_proc_s_read(size_t offset, void *dst, size_t n, void *user); int bin_proc_s_write(size_t offset, const void *dst, size_t n, void *user); size_t bin_proc_s_ptr(void *user); +struct bin_proc_fd *bin_proc_fd_find(const struct bin_proc *p, int fd); +int bin_proc_fd_alloc(struct bin_proc *p, const struct fs_fd *fd, int flags); extern const struct nw_inst_cfg bin_proc_cfg; diff --git a/src/bin/private_include/bin/types.h b/src/bin/private_include/bin/types.h index 2a498ed..6f231ae 100644 --- a/src/bin/private_include/bin/types.h +++ b/src/bin/private_include/bin/types.h @@ -28,7 +28,7 @@ #include <sys/types.h> #include <stdbool.h> -enum {BIN_IMPORTS = 9}; +enum {BIN_IMPORTS = 10}; struct bin_dbg { @@ -54,18 +54,25 @@ struct bin_global size_t sz; }; +struct bin_proc_fd +{ + int fd, flags; + struct fs_fd fs_fd; +}; + struct bin_proc { const struct bin_mod *mod; struct nw_inst instance; - union nw_value args[7]; + union nw_value args[6]; struct bin_dbg dbg; struct caio *caio; char *path, **argv; size_t argc; - pid_t pid; + pid_t pid, parent; uid_t uid; gid_t gid; + mode_t umask; char header[sizeof "asm"]; size_t i; long retval; @@ -73,7 +80,8 @@ struct bin_proc struct bin_global global; void *import; struct page *linear; - int *fds; + struct fs_stdstreams ss; + struct bin_proc_fd *fds; size_t n_fds; struct bin_proc *prev, *next; }; diff --git a/src/bin/private_include/bin/wasi/errno.h b/src/bin/private_include/bin/wasi/errno.h deleted file mode 100644 index 442e877..0000000 --- a/src/bin/private_include/bin/wasi/errno.h +++ /dev/null @@ -1,105 +0,0 @@ -/* - * wnix, a Unix-like operating system for WebAssembly applications. - * Copyright (C) 2025 Xavier Del Campo Romero - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <https://www.gnu.org/licenses/>. - */ - -#ifndef WASI_ERRNO_H -#define WASI_ERRNO_H - -enum wasi_errno -{ - WASI_SUCCESS, - WASI_2BIG, - WASI_ACCES, - WASI_ADDRINUSE, - WASI_ADDRNOTAVAIL, - WASI_AFNOSUPPORT, - WASI_AGAIN, - WASI_ALREADY, - WASI_BADF, - WASI_BADMSG, - WASI_BUSY, - WASI_CANCELED, - WASI_CHILD, - WASI_CONNABORTED, - WASI_CONNREFUSED, - WASI_CONNRESET, - WASI_DEADLK, - WASI_DESTADDRREQ, - WASI_DOM, - WASI_DQUOT, - WASI_EXIST, - WASI_FAULT, - WASI_FBIG, - WASI_HOSTUNREACH, - WASI_IDRM, - WASI_ILSEQ, - WASI_INPROGRESS, - WASI_INTR, - WASI_INVAL, - WASI_IO, - WASI_ISCONN, - WASI_ISDIR, - WASI_LOOP, - WASI_MFILE, - WASI_MLINK, - WASI_MSGSIZE, - WASI_MULTIHOP, - WASI_NAMETOOLONG, - WASI_NETDOWN, - WASI_NETRESET, - WASI_NETUNREACH, - WASI_NFILE, - WASI_NOBUFS, - WASI_NODEV, - WASI_NOENT, - WASI_NOEXEC, - WASI_NOLCK, - WASI_NOLINK, - WASI_NOMEM, - WASI_NOMSG, - WASI_NOPROTOOPT, - WASI_NOSPC, - WASI_NOSYS, - WASI_NOTCONN, - WASI_NOTDIR, - WASI_NOTEMPTY, - WASI_NOTRECOVERABLE, - WASI_NOTSOCK, - WASI_NOTSUP, - WASI_NOTTY, - WASI_NXIO, - WASI_OVERFLOW, - WASI_OWNERDEAD, - WASI_PERM, - WASI_PIPE, - WASI_PROTO, - WASI_PROTONOSUPPORT, - WASI_PROTOTYPE, - WASI_RANGE, - WASI_ROFS, - WASI_SPIPE, - WASI_SRCH, - WASI_STALE, - WASI_TIMEDOUT, - WASI_TXTBSY, - WASI_XDEV, - WASI_NOTCAPABLE -}; - -int wasi_errno(int error, enum wasi_errno *out); - -#endif diff --git a/src/bin/private_include/bin/wnix.h b/src/bin/private_include/bin/wnix.h index 07e7131..f3844c5 100644 --- a/src/bin/private_include/bin/wnix.h +++ b/src/bin/private_include/bin/wnix.h @@ -33,5 +33,13 @@ enum nw_state bin_wnix_mkdir(const union nw_value *params, union nw_value *ret, void *user, struct nw_next *next); enum nw_state bin_wnix_mount(const union nw_value *params, union nw_value *ret, void *user, struct nw_next *next); +enum nw_state bin_wnix_write(const union nw_value *params, + union nw_value *ret, void *user, struct nw_next *next); +enum nw_state bin_wnix_umask(const union nw_value *params, + union nw_value *ret, void *user, struct nw_next *next); +enum nw_state bin_wnix_open(const union nw_value *params, + union nw_value *ret, void *user, struct nw_next *next); +enum nw_state bin_wnix_close(const union nw_value *params, + union nw_value *ret, void *user, struct nw_next *next); #endif diff --git a/src/bin/private_include/bin/wnix/routines.h b/src/bin/private_include/bin/wnix/routines.h index aa488fb..3ba5118 100644 --- a/src/bin/private_include/bin/wnix/routines.h +++ b/src/bin/private_include/bin/wnix/routines.h @@ -23,7 +23,22 @@ #include <bin/types.h> #include <nanowasm/nw.h> -int bin_wnix_set_errno(struct bin_proc *p, int error, unsigned long addr, - struct nw_next *next); +struct bin_readstr +{ + unsigned long addr; + struct bin_proc *p; + char **str; + int (*error)(struct bin_proc *, int, struct nw_next *); +}; + +struct bin_set_errno +{ + int error; + unsigned long addr; + struct bin_proc *p; +}; + +int bin_wnix_readstr(const struct bin_readstr *cfg, struct nw_next *next); +int bin_wnix_set_errno(const struct bin_set_errno *cfg, struct nw_next *next); #endif diff --git a/src/bin/src/exec.c b/src/bin/src/exec.c index 83c0e5f..1796af9 100644 --- a/src/bin/src/exec.c +++ b/src/bin/src/exec.c @@ -30,6 +30,7 @@ #include <nanowasm/nw.h> #include <nanowasm/dbg.h> #include <fcntl.h> +#include <sys/stat.h> #include <sys/types.h> #include <stdlib.h> #include <string.h> @@ -318,7 +319,8 @@ failure: } int bin_exec(const char *const path, const char *const *const argv, - const uid_t uid, const gid_t gid) + const struct fs_stdstreams *const ss, const pid_t parent, const uid_t uid, + const gid_t gid) { const pid_t pid = bin_pid(); char *pathdup = NULL; @@ -353,9 +355,12 @@ int bin_exec(const char *const path, const char *const *const argv, { .path = pathdup, .dbg.aio = serial, + .parent = parent, .pid = pid, .uid = uid, - .gid = gid + .gid = gid, + .ss = *ss, + .umask = S_IWGRP | S_IWOTH }; if (copy_args(p, path, argv) diff --git a/src/bin/src/mod/cfg.c b/src/bin/src/mod/cfg.c index 8c78a32..1be26ca 100644 --- a/src/bin/src/mod/cfg.c +++ b/src/bin/src/mod/cfg.c @@ -88,6 +88,50 @@ static const struct nw_import imports[BIN_IMPORTS] = .fn = bin_wnix_mount, .signature = "i(iiiiii)" } + }, + + { + .kind = NW_KIND_FUNCTION, + .module = "wnix", + .field = "write", + .u.function = + { + .fn = bin_wnix_write, + .signature = "i(iiii)" + } + }, + + { + .kind = NW_KIND_FUNCTION, + .module = "wnix", + .field = "umask", + .u.function = + { + .fn = bin_wnix_umask, + .signature = "i(i)" + } + }, + + { + .kind = NW_KIND_FUNCTION, + .module = "wnix", + .field = "open", + .u.function = + { + .fn = bin_wnix_open, + .signature = "i(iiii)" + } + }, + + { + .kind = NW_KIND_FUNCTION, + .module = "wnix", + .field = "close", + .u.function = + { + .fn = bin_wnix_close, + .signature = "i(ii)" + } } }; diff --git a/src/bin/src/proc/CMakeLists.txt b/src/bin/src/proc/CMakeLists.txt index 47df502..e9a1d1c 100644 --- a/src/bin/src/proc/CMakeLists.txt +++ b/src/bin/src/proc/CMakeLists.txt @@ -17,6 +17,8 @@ target_sources(bin PRIVATE cfg.c eof.c + fd_alloc.c + fd_find.c free.c init.c lock.c diff --git a/src/bin/src/proc/fd_alloc.c b/src/bin/src/proc/fd_alloc.c new file mode 100644 index 0000000..3db5e17 --- /dev/null +++ b/src/bin/src/proc/fd_alloc.c @@ -0,0 +1,54 @@ +/* + * wnix, a Unix-like operating system for WebAssembly applications. + * Copyright (C) 2025 Xavier Del Campo Romero + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <bin/proc.h> +#include <bin/types.h> +#include <fs/fs.h> +#include <stddef.h> +#include <stdlib.h> + +int bin_proc_fd_alloc(struct bin_proc *const p, const struct fs_fd *const fs_fd, + const int flags) +{ + int fd = 0; + + for (size_t i = 0; i < p->n_fds;) + if (fd == p->fds[i].fd) + { + fd++; + i = 0; + } + else + i++; + + const size_t n = p->n_fds + 1; + struct bin_proc_fd *const fds = realloc(p->fds, n * sizeof *fds); + + if (!fds) + return -1; + + fds[p->n_fds++] = (const struct bin_proc_fd) + { + .fd = fd, + .flags = flags, + .fs_fd = *fs_fd + }; + + p->fds = fds; + return fd; +} diff --git a/src/bin/src/proc/fd_find.c b/src/bin/src/proc/fd_find.c new file mode 100644 index 0000000..b729e26 --- /dev/null +++ b/src/bin/src/proc/fd_find.c @@ -0,0 +1,38 @@ +/* + * wnix, a Unix-like operating system for WebAssembly applications. + * Copyright (C) 2025 Xavier Del Campo Romero + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <bin/proc.h> +#include <bin/types.h> +#include <fcntl.h> +#include <stddef.h> + +struct bin_proc_fd *bin_proc_fd_find(const struct bin_proc *const p, const int fd) +{ + if (fd < 0) + return NULL; + + for (size_t i = 0; i < p->n_fds; i++) + { + struct bin_proc_fd *const pfd = &p->fds[i]; + + if (pfd->fd == fd && pfd->flags & O_WRONLY) + return pfd; + } + + return NULL; +} diff --git a/src/bin/src/proc/free.c b/src/bin/src/proc/free.c index a5bdea1..6876749 100644 --- a/src/bin/src/proc/free.c +++ b/src/bin/src/proc/free.c @@ -30,6 +30,9 @@ void bin_proc_free(struct bin_proc *const p) struct bin_dbg *const d = &p->dbg; + for (char **arg = p->argv; *arg; arg++) + free(*arg); + dynstr_free(&d->dstr); free(d->bkpt); aio_free(d->aio); diff --git a/src/bin/src/proc/pc.c b/src/bin/src/proc/pc.c index 28b2f44..29bdd7f 100644 --- a/src/bin/src/proc/pc.c +++ b/src/bin/src/proc/pc.c @@ -643,6 +643,7 @@ enum nw_state bin_proc_pc(const long offset, struct nw_next *const next, return NW_AGAIN; } else + { for (size_t i = 0; i < d->n_bkpt; i++) if (offset == d->bkpt[i]) { @@ -653,5 +654,23 @@ enum nw_state bin_proc_pc(const long offset, struct nw_next *const next, return NW_AGAIN; } + const struct fs_read r = + { + .fd = &d->fd, + .buf = &d->b, + .n = sizeof d->b + }; + + const int n = aio_read_nb(&r); + + if (n < 0) + { + kprintf("%s: aio_read_nb failed\n", __func__); + return NW_FATAL; + } + else if (n && d->b == -1) + d->running = false; + } + return NW_OK; } diff --git a/src/bin/src/update.c b/src/bin/src/update.c index 3f03ae7..f5ce1c6 100644 --- a/src/bin/src/update.c +++ b/src/bin/src/update.c @@ -35,10 +35,10 @@ static int load(void) switch (nw_load(&m->mod, &m->mod_out)) { - case STATE_AGAIN: + case NW_AGAIN: break; - case STATE_FATAL: + case NW_FATAL: /* TODO: call nw_loadexc. */ kprintf("Module %s failed to load\n", m->path); @@ -48,7 +48,7 @@ static int load(void) bin_mod_free(m); break; - case STATE_OK: + case NW_OK: kprintf("Module %s loaded successfully\n", m->path); if (bin_start(m)) diff --git a/src/bin/src/wnix/CMakeLists.txt b/src/bin/src/wnix/CMakeLists.txt index b4868f2..5a92246 100644 --- a/src/bin/src/wnix/CMakeLists.txt +++ b/src/bin/src/wnix/CMakeLists.txt @@ -18,8 +18,13 @@ target_sources(bin PRIVATE argc.c argcopy.c arglen.c + close.c exit.c mkdir.c mount.c - set_errno.c + open.c + umask.c + write.c ) + +add_subdirectory(routines) diff --git a/src/bin/src/wnix/argcopy.c b/src/bin/src/wnix/argcopy.c index cedb26e..f712016 100644 --- a/src/bin/src/wnix/argcopy.c +++ b/src/bin/src/wnix/argcopy.c @@ -21,7 +21,6 @@ #include <bin/wnix/routines.h> #include <bin/proc.h> #include <bin/types.h> -#include <endian.h> #include <nanowasm/nw.h> #include <nanowasm/linear.h> #include <errno.h> @@ -66,7 +65,14 @@ static int set_errno(struct bin_proc *const p, const int error, .user = p }; - return bin_wnix_set_errno(p, error, c->errno_addr, next); + const struct bin_set_errno cfg = + { + .addr = c->errno_addr, + .error = error, + .p = p + }; + + return bin_wnix_set_errno(&cfg, next); } static enum nw_state read_arg(void *const user, struct nw_next *const next) @@ -112,6 +118,8 @@ static enum nw_state check(void *const user, struct nw_next *const next) else *next = (const struct nw_next){.fn = read_arg, .user = user}; + return NW_AGAIN; + failure: free_argcopy(c); return NW_FATAL; @@ -137,7 +145,7 @@ enum nw_state bin_wnix_argcopy(const union nw_value *const params, struct bin_proc *const p = user; - *next = (const struct nw_next){.fn = check, .user = user}; p->import = c; + *next = (const struct nw_next){.fn = check, .user = user}; return NW_AGAIN; } diff --git a/src/bin/src/wnix/close.c b/src/bin/src/wnix/close.c new file mode 100644 index 0000000..7e1754f --- /dev/null +++ b/src/bin/src/wnix/close.c @@ -0,0 +1,32 @@ +/* + * wnix, a Unix-like operating system for WebAssembly applications. + * Copyright (C) 2025 Xavier Del Campo Romero + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <bin.h> +#include <bin/wnix.h> +#include <bin/proc.h> +#include <bin/types.h> +#include <nanowasm/nw.h> + +enum nw_state bin_wnix_close(const union nw_value *const params, + union nw_value *const ret, void *const user, + struct nw_next *const next) +{ + /* TODO */ + ret->i32 = -1; + return NW_OK; +} diff --git a/src/bin/src/wnix/mkdir.c b/src/bin/src/wnix/mkdir.c index 30a274a..8b26a2c 100644 --- a/src/bin/src/wnix/mkdir.c +++ b/src/bin/src/wnix/mkdir.c @@ -21,18 +21,215 @@ #include <bin/wnix/routines.h> #include <bin/proc.h> #include <bin/types.h> -#include <endian.h> +#include <aio.h> +#include <fs/fs.h> +#include <loop.h> #include <nanowasm/nw.h> #include <nanowasm/linear.h> #include <errno.h> #include <stdlib.h> -#include <string.h> + +struct mkdir +{ + char *path; + unsigned long path_addr, errno_addr, mode; + union nw_value *ret; + struct aio *aio; + int error; +}; + +static void free_mkdir(struct mkdir *const m) +{ + if (!m) + return; + + aio_free(m->aio); + free(m->path); + free(m); +} + +static enum nw_state fail(void *const user, struct nw_next *const next) +{ + struct bin_proc *const p = user; + struct mkdir *const m = p->import; + + m->ret->i32 = -1; + free_mkdir(m); + return NW_OK; +} + +static int set_errno(struct bin_proc *const p, const int error, + struct nw_next *const next) +{ + struct mkdir *const m = p->import; + + if (m->errno_addr) + { + const struct bin_set_errno cfg = + { + .addr = m->errno_addr, + .error = error, + .p = p + }; + + *next = (const struct nw_next) + { + .fn = fail, + .user = p + }; + + if (bin_wnix_set_errno(&cfg, next)) + { + free_mkdir(m); + return -1; + } + } + else + return fail(p, next); + + return 0; +} + +static enum nw_state check_mkdir(void *const user, struct nw_next *const next) +{ + struct bin_proc *const p = user; + struct mkdir *const m = p->import; + enum nw_state ret = NW_OK; + + if (m->error) + { + if (set_errno(p, m->error, next)) + ret = NW_FATAL; + else + return NW_AGAIN; + } + + free_mkdir(m); + return ret; +} + +static int mkdir_done(const enum state state, void *const args) +{ + struct bin_proc *const p = args; + struct mkdir *const m = p->import; + + m->error = state ? errno : 0; + + if (bin_proc_unlock(p) || loop_rm_aio(m->aio)) + { + free_mkdir(m); + return -1; + } + + aio_free(m->aio); + m->aio = NULL; + return 0; +} + +static enum nw_state do_mkdir(void *const user, struct nw_next *const next) +{ + struct bin_proc *const p = user; + struct mkdir *const m = p->import; + const struct aio_done d = + { + .f = mkdir_done, + .args = p + }; + + const struct fs_mkdir mk = + { + .mode = m->mode, + .path = m->path, + .uid = p->uid, + .gid = p->gid + }; + + struct aio *const aio = aio_mkdir(&mk, &d); + + if (!aio || bin_proc_lock(p) || loop_add_aio(aio)) + { + aio_free(aio); + free_mkdir(m); + return NW_FATAL; + } + + m->aio = aio; + *next = (const struct nw_next){.fn = check_mkdir, .user = user}; + return NW_AGAIN; +} + +static enum nw_state read_path(void *const user, struct nw_next *const next) +{ + struct bin_proc *const p = user; + struct mkdir *const m = p->import; + const struct bin_readstr cfg = + { + .error = set_errno, + .addr = m->path_addr, + .str = &m->path, + .p = p + }; + + *next = (const struct nw_next){.fn = do_mkdir, .user = user}; + + if (bin_wnix_readstr(&cfg, next)) + { + free_mkdir(m); + return NW_FATAL; + } + + return NW_AGAIN; +} + +static enum nw_state check(void *const user, struct nw_next *const next) +{ + struct bin_proc *const p = user; + struct mkdir *const m = p->import; + + if (!m->path_addr || !m->errno_addr) + { + if (m->errno_addr) + { + if (set_errno(p, EINVAL, next)) + goto failure; + } + else + { + m->ret->i32 = -1; + return NW_OK; + } + } + else + *next = (const struct nw_next){.fn = read_path, .user = user}; + + return NW_AGAIN; + +failure: + free_mkdir(m); + return NW_FATAL; +} enum nw_state bin_wnix_mkdir(const union nw_value *const params, union nw_value *const ret, void *const user, struct nw_next *const next) { - /* TODO */ - ret->i32 = -1; - return NW_OK; + enum {PATH, MODE, ERRNO}; + struct mkdir *const m = malloc(sizeof *m); + + if (!m) + return NW_FATAL; + + *m = (const struct mkdir) + { + .path_addr = params[PATH].i32, + .mode = params[MODE].i32, + .errno_addr = params[ERRNO].i32, + .ret = ret + }; + + struct bin_proc *const p = user; + + *next = (const struct nw_next){.fn = check, .user = user}; + p->import = m; + return NW_AGAIN; } diff --git a/src/bin/src/wnix/mount.c b/src/bin/src/wnix/mount.c index 562f7d9..f3acea6 100644 --- a/src/bin/src/wnix/mount.c +++ b/src/bin/src/wnix/mount.c @@ -78,7 +78,14 @@ static int set_errno(struct bin_proc *const p, const int error, .user = p }; - return bin_wnix_set_errno(p, error, m->errno_addr, next); + const struct bin_set_errno cfg = + { + .addr = m->errno_addr, + .error = error, + .p = p + }; + + return bin_wnix_set_errno(&cfg, next); } static enum nw_state mounted(void *const user, struct nw_next *const next) @@ -160,55 +167,23 @@ failure: return NW_FATAL; } -static enum nw_state read_str(struct bin_proc *const p, - struct mountstr *const ms) +static enum nw_state read_string(struct bin_proc *const p, + struct mountstr *const ms, struct nw_next *const next) { - char b; struct mount *const m = p->import; - struct nw_sm_io io = {.buf = &b, .n = sizeof b}; - const enum nw_state n = nw_linear_load(&p->instance, &io, ms->addr + m->i); - - if (n) - return n; - - char *const s = realloc(ms->str, m->i + 1); - if (!s) - return NW_FATAL; - - s[m->i++] = b; - ms->str = s; - return b ? NW_AGAIN : NW_OK; -} - -static enum nw_state check_read_str(struct bin_proc *const p, - struct mountstr *const ms, const struct nw_next *const innext, - struct nw_next *const outnext) -{ - struct mount *const m = p->import; - enum {PATHMAX = 128}; - - if (m->i >= PATHMAX) + const struct bin_readstr cfg = { - free_mount(m); - /* TODO: assign errno. */ - m->ret->i32 = -1; - return NW_OK; - } + .error = set_errno, + .addr = ms->addr, + .str = &ms->str, + .p = p + }; - switch (read_str(p, ms)) + if (bin_wnix_readstr(&cfg, next)) { - case NW_FATAL: - free_mount(m); - return NW_FATAL; - - case NW_OK: - m->i = 0; - *outnext = *innext; - break; - - case NW_AGAIN: - break; + free_mount(m); + return NW_FATAL; } return NW_AGAIN; @@ -218,27 +193,27 @@ static enum nw_state read_type(void *const user, struct nw_next *const next) { struct bin_proc *const p = user; struct mount *const m = p->import; - const struct nw_next rnext = {.fn = do_mount, .user = p}; - return check_read_str(p, &m->type, &rnext, next); + *next = (const struct nw_next){.fn = do_mount, .user = user}; + return read_string(p, &m->type, next); } static enum nw_state read_tgt(void *const user, struct nw_next *const next) { struct bin_proc *const p = user; struct mount *const m = p->import; - const struct nw_next rnext = {.fn = read_type, .user = p}; - return check_read_str(p, &m->tgt, &rnext, next); + *next = (const struct nw_next){.fn = read_type, .user = user}; + return read_string(p, &m->tgt, next); } static enum nw_state read_src(void *const user, struct nw_next *const next) { struct bin_proc *const p = user; struct mount *const m = p->import; - const struct nw_next rnext = {.fn = read_tgt, .user = p}; - return check_read_str(p, &m->src, &rnext, next); + *next = (const struct nw_next){.fn = read_tgt, .user = user}; + return read_string(p, &m->src, next); } static enum nw_state check(void *const user, struct nw_next *const next) diff --git a/src/bin/src/wnix/open.c b/src/bin/src/wnix/open.c new file mode 100644 index 0000000..fdab80a --- /dev/null +++ b/src/bin/src/wnix/open.c @@ -0,0 +1,222 @@ +/* + * wnix, a Unix-like operating system for WebAssembly applications. + * Copyright (C) 2025 Xavier Del Campo Romero + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <bin.h> +#include <bin/wnix.h> +#include <bin/wnix/routines.h> +#include <bin/proc.h> +#include <bin/types.h> +#include <aio.h> +#include <loop.h> +#include <nanowasm/nw.h> +#include <errno.h> +#include <stdlib.h> + +struct open +{ + char *path; + struct fs_fd fd; + unsigned long path_addr, errno_addr; + long flags, mode; + union nw_value *ret; + struct aio *aio; + int error; +}; + +static void free_open(struct open *const op) +{ + if (!op) + return; + + aio_free(op->aio); + free(op->path); + free(op); +} + +static enum nw_state fail(void *const user, struct nw_next *const next) +{ + struct bin_proc *const p = user; + struct open *const op = p->import; + + op->ret->i32 = -1; + free_open(op); + return NW_OK; +} + +static int set_errno(struct bin_proc *const p, const int error, + struct nw_next *const next) +{ + struct open *const op = p->import; + + if (op->errno_addr) + { + const struct bin_set_errno cfg = + { + .addr = op->errno_addr, + .error = error, + .p = p + }; + + *next = (const struct nw_next) + { + .fn = fail, + .user = p + }; + + if (bin_wnix_set_errno(&cfg, next)) + { + free_open(op); + return -1; + } + } + else + return fail(p, next); + + return 0; +} + +static enum nw_state check_open(void *const user, struct nw_next *const next) +{ + struct bin_proc *const p = user; + struct open *const op = p->import; + int fd; + + if (op->error) + return set_errno(p, op->error, next); + else if ((fd = bin_proc_fd_alloc(p, &op->fd, op->flags)) < 0) + return set_errno(p, errno, next); + + op->ret->i32 = fd; + free_open(op); + return NW_OK; +} + +static int opened(const enum state state, void *const args) +{ + struct bin_proc *const p = args; + struct open *const op = p->import; + + op->error = state ? errno : 0; + + if (bin_proc_unlock(p) || loop_rm_aio(op->aio)) + { + free_open(op); + return -1; + } + + aio_free(op->aio); + op->aio = NULL; + return 0; +} + +static enum nw_state do_open(void *const user, struct nw_next *const next) +{ + struct bin_proc *const p = user; + struct open *const op = p->import; + const struct aio_done d = + { + .f = opened, + .args = p + }; + + const struct fs_open fo = + { + .flags = op->flags, + .mode = op->mode, + .path = op->path, + .fd = &op->fd, + .uid = p->uid, + .gid = p->gid, + .ss = p->ss + }; + + struct aio *const aio = aio_open(&fo, &d); + + if (!aio || bin_proc_lock(p) || loop_add_aio(aio)) + { + aio_free(aio); + free_open(op); + return NW_FATAL; + } + + op->aio = aio; + *next = (const struct nw_next){.fn = check_open, .user = user}; + return NW_AGAIN; +} + +static enum nw_state read_path(void *const user, struct nw_next *const next) +{ + struct bin_proc *const p = user; + struct open *const op = p->import; + const struct bin_readstr cfg = + { + .error = set_errno, + .addr = op->path_addr, + .str = &op->path, + .p = p + }; + + *next = (const struct nw_next){.fn = do_open, .user = user}; + + if (bin_wnix_readstr(&cfg, next)) + { + free_open(op); + return NW_FATAL; + } + + return NW_AGAIN; +} + +static enum nw_state check(void *const user, struct nw_next *const next) +{ + struct bin_proc *const p = user; + struct open *const op = p->import; + + if (!op->path_addr || !op->errno_addr) + return set_errno(p, EINVAL, next); + else + *next = (const struct nw_next){.fn = read_path, .user = user}; + + return NW_AGAIN; +} + +enum nw_state bin_wnix_open(const union nw_value *const params, + union nw_value *const ret, void *const user, + struct nw_next *const next) +{ + enum {PATH, FLAGS, MODE, ERRNO}; + struct open *const op = malloc(sizeof *op); + + if (!op) + return NW_FATAL; + + *op = (const struct open) + { + .path_addr = params[PATH].i32, + .flags = params[FLAGS].i32, + .mode = params[MODE].i32, + .errno_addr = params[ERRNO].i32, + .ret = ret + }; + + struct bin_proc *const p = user; + + *next = (const struct nw_next){.fn = check, .user = user}; + p->import = op; + return NW_AGAIN; +} diff --git a/src/bin/src/wnix/routines/CMakeLists.txt b/src/bin/src/wnix/routines/CMakeLists.txt new file mode 100644 index 0000000..784acf4 --- /dev/null +++ b/src/bin/src/wnix/routines/CMakeLists.txt @@ -0,0 +1,20 @@ +# wnix, a Unix-like operating system for WebAssembly applications. +# Copyright (C) 2025 Xavier Del Campo Romero +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +target_sources(bin PRIVATE + readstr.c + set_errno.c +) diff --git a/src/bin/src/wnix/routines/readstr.c b/src/bin/src/wnix/routines/readstr.c new file mode 100644 index 0000000..06f6fde --- /dev/null +++ b/src/bin/src/wnix/routines/readstr.c @@ -0,0 +1,129 @@ +/* + * wnix, a Unix-like operating system for WebAssembly applications. + * Copyright (C) 2025 Xavier Del Campo Romero + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <bin.h> +#include <bin/types.h> +#include <bin/wnix/routines.h> +#include <endian.h> +#include <nanowasm/nw.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +struct readstr +{ + char *s; + size_t i; + struct bin_readstr cfg; + struct nw_next next; +}; + +static void free_readstr(struct readstr *const rs) +{ + if (!rs) + return ; + + free(rs->s); + free(rs); +} + +static enum nw_state read_str(struct readstr *const rs) +{ + char b; + struct nw_sm_io io = {.buf = &b, .n = sizeof b}; + const struct bin_readstr *const cfg = &rs->cfg; + struct bin_proc *const p = cfg->p; + const enum nw_state n = nw_linear_load(&p->instance, &io, cfg->addr + rs->i); + + if (n) + return n; + + char *const s = realloc(rs->s, rs->i + 1); + + if (!s) + return NW_FATAL; + + s[rs->i++] = b; + rs->s = s; + return b ? NW_AGAIN : NW_OK; +} + +static enum nw_state check_read_str(void *const user, + struct nw_next *const next) +{ + struct readstr *const rs = user; + const struct bin_readstr *const cfg = &rs->cfg; + struct bin_proc *const p = cfg->p; + enum nw_state ret = NW_AGAIN; + enum {PATHMAX = 128}; + + if (rs->i >= PATHMAX) + { + if (rs->cfg.error(p, ENAMETOOLONG, next)) + ret = NW_FATAL; + } + else + { + switch (read_str(rs)) + { + case NW_FATAL: + if (rs->cfg.error(p, errno, next)) + ret = NW_FATAL; + + break; + + case NW_OK: + { + char *const s = strdup(rs->s); + + if (!s) + ret = NW_FATAL; + else + { + *cfg->str = s; + *next = rs->next; + } + } + break; + + case NW_AGAIN: + return NW_AGAIN; + } + } + + free_readstr(rs); + return ret; +} + +int bin_wnix_readstr(const struct bin_readstr *const cfg, + struct nw_next *const next) +{ + struct readstr *const rs = malloc(sizeof *rs); + + if (!rs) + return -1; + + *rs = (const struct readstr) + { + .cfg = *cfg, + .next = *next + }; + + *next = (const struct nw_next){.fn = check_read_str, .user = rs}; + return 0; +} diff --git a/src/bin/src/wnix/set_errno.c b/src/bin/src/wnix/routines/set_errno.c index cc04420..0db98af 100644 --- a/src/bin/src/wnix/set_errno.c +++ b/src/bin/src/wnix/routines/set_errno.c @@ -25,30 +25,38 @@ struct set_errno { - struct bin_proc *p; - struct nw_next next; - struct nw_sm_io io; struct endian_le32 error; - unsigned long addr; + struct nw_sm_io io; + struct bin_set_errno cfg; + struct nw_next next; }; -static enum nw_state store_errno(void *const user, - struct nw_next *const next) +static enum nw_state store_errno(void *const user, struct nw_next *const next) { struct set_errno *const e = user; - struct bin_proc *const p = e->p; - const enum nw_state n = nw_linear_store(&p->instance, &e->io, e->addr); + const struct bin_set_errno *const cfg = &e->cfg; + struct bin_proc *const p = cfg->p; + const enum nw_state n = nw_linear_store(&p->instance, &e->io, cfg->addr); + + switch (n) + { + case NW_FATAL: + free(e); + /* Fall through.*/ + case NW_AGAIN: + return n; - if (n) - return n; + case NW_OK: + *next = e->next; + free(e); + break; + } - *next = e->next; - free(e); return NW_AGAIN; } -int bin_wnix_set_errno(struct bin_proc *const p, const int error, - const unsigned long addr, struct nw_next *const next) +int bin_wnix_set_errno(const struct bin_set_errno *const cfg, + struct nw_next *const next) { struct set_errno *const e = malloc(sizeof *e); @@ -57,10 +65,9 @@ int bin_wnix_set_errno(struct bin_proc *const p, const int error, *e = (const struct set_errno) { - .error = endian_to_le32(error), + .error = endian_to_le32(cfg->error), + .cfg = *cfg, .next = *next, - .addr = addr, - .p = p, .io = { .buf = &e->error, diff --git a/src/bin/src/wnix/umask.c b/src/bin/src/wnix/umask.c new file mode 100644 index 0000000..f4fd701 --- /dev/null +++ b/src/bin/src/wnix/umask.c @@ -0,0 +1,35 @@ +/* + * wnix, a Unix-like operating system for WebAssembly applications. + * Copyright (C) 2025 Xavier Del Campo Romero + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <bin.h> +#include <bin/wnix.h> +#include <bin/proc.h> +#include <bin/types.h> +#include <nanowasm/nw.h> + +enum nw_state bin_wnix_umask(const union nw_value *const params, + union nw_value *const ret, void *const user, + struct nw_next *const next) +{ + struct bin_proc *const p = user; + const mode_t mask = params->i32, oldmask = p->umask; + + p->umask = mask; + ret->i32 = oldmask; + return NW_OK; +} diff --git a/src/bin/src/wnix/write.c b/src/bin/src/wnix/write.c new file mode 100644 index 0000000..3a4b8d5 --- /dev/null +++ b/src/bin/src/wnix/write.c @@ -0,0 +1,251 @@ +/* + * wnix, a Unix-like operating system for WebAssembly applications. + * Copyright (C) 2025 Xavier Del Campo Romero + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <bin.h> +#include <bin/wnix.h> +#include <bin/proc.h> +#include <bin/routines.h> +#include <bin/types.h> +#include <bin/wnix/routines.h> +#include <aio.h> +#include <fs/fs.h> +#include <loop.h> +#include <nanowasm/nw.h> +#include <nanowasm/linear.h> +#include <errno.h> +#include <stdlib.h> + +struct write +{ + long fd; + int error; + unsigned long addr, count, errno_addr; + char buf[64]; + size_t i; + union nw_value *ret; + struct aio *aio; + struct bin_proc_fd *pfd; + struct nw_sm_io io; +}; + +static enum nw_state read_chunk(void *, struct nw_next *); + +static void free_write(struct write *const w) +{ + if (!w) + return; + + aio_free(w->aio); + free(w); +} + +static enum nw_state fail(void *const user, struct nw_next *const next) +{ + struct bin_proc *const p = user; + struct write *const w = p->import; + + w->ret->i32 = -1; + free_write(w); + return NW_OK; +} + +static enum nw_state set_errno(struct bin_proc *const p, const int error, + struct nw_next *const next) +{ + struct write *const w = p->import; + + if (w->errno_addr) + { + const struct bin_set_errno cfg = + { + .addr = w->errno_addr, + .error = error, + .p = p + }; + + *next = (const struct nw_next) + { + .fn = fail, + .user = p + }; + + if (bin_wnix_set_errno(&cfg, next)) + { + free_write(w); + return NW_FATAL; + } + } + else + return fail(p, next); + + return NW_AGAIN; +} + +static void setup_buf(struct bin_proc *const p, struct nw_next *const next) +{ + struct write *const w = p->import; + const size_t rem = w->count - w->i, + n = rem > sizeof w->buf ? sizeof w->buf : rem; + + w->io = (const struct nw_sm_io) + { + .buf = w->buf, + .n = n + }; + + *next = (const struct nw_next){.fn = read_chunk, .user = p}; +} + +static enum nw_state check_write(void *const user, struct nw_next *const next) +{ + struct bin_proc *const p = user; + struct write *const w = p->import; + + if (w->error) + { + if (set_errno(p, w->error, next)) + { + free_write(w); + return NW_FATAL; + } + } + else if ((w->i += w->io.n) >= w->count) + { + w->ret->i32 = w->count; + free_write(w); + return NW_OK; + } + else + setup_buf(p, next); + + return NW_AGAIN; +} + +static int write_done(const enum state state, void *const args) +{ + struct bin_proc *const p = args; + struct write *const w = p->import; + + w->error = state ? errno : 0; + + if (bin_proc_unlock(p) || loop_rm_aio(w->aio)) + { + free_write(w); + return -1; + } + + aio_free(w->aio); + w->aio = NULL; + return 0; +} + +static enum nw_state do_write(void *const user, struct nw_next *const next) +{ + struct bin_proc *const p = user; + struct write *const w = p->import; + const struct aio_done d = + { + .f = write_done, + .args = user + }; + + const struct fs_write fw = + { + .fd = &w->pfd->fs_fd, + .buf = w->buf, + .n = w->io.n + }; + + struct aio *const aio = aio_write(&fw, &d); + + if (!aio || bin_proc_lock(p) || loop_add_aio(aio)) + { + aio_free(aio); + free_write(w); + return NW_FATAL; + } + + w->aio = aio; + *next = (const struct nw_next){.fn = check_write, .user = user}; + return NW_AGAIN; +} + +static enum nw_state read_chunk(void *const user, struct nw_next *const next) +{ + struct bin_proc *const p = user; + struct write *const w = p->import; + const unsigned long offset = w->addr + w->i; + const enum nw_state n = nw_linear_load(&p->instance, &w->io, offset); + + switch (n) + { + case NW_OK: + *next = (const struct nw_next){.fn = do_write, .user = user}; + break; + + case NW_FATAL: + free_write(w); + return n; + + case NW_AGAIN: + break; + } + + return NW_AGAIN; +} + +static enum nw_state check(void *const user, struct nw_next *const next) +{ + struct bin_proc *const p = user; + struct write *const w = p->import; + + if (!(w->pfd = bin_proc_fd_find(p, w->fd))) + return set_errno(p, EBADF, next); + else if (!w->addr || !w->errno_addr) + return set_errno(p, EINVAL, next); + else + setup_buf(p, next); + + return NW_AGAIN; +} + +enum nw_state bin_wnix_write(const union nw_value *const params, + union nw_value *const ret, void *const user, + struct nw_next *const next) +{ + enum {FD, ADDR, COUNT, ERRNO}; + struct write *const w = malloc(sizeof *w); + + if (!w) + return NW_FATAL; + + *w = (const struct write) + { + .fd = params[FD].i32, + .addr = params[ADDR].i32, + .count = params[COUNT].i32, + .errno_addr = params[ERRNO].i32, + .ret = ret + }; + + struct bin_proc *const p = user; + + *next = (const struct nw_next){.fn = check, .user = user}; + p->import = w; + return NW_AGAIN; +} |
