aboutsummaryrefslogtreecommitdiff
path: root/src/bin
diff options
context:
space:
mode:
authorXavier Del Campo Romero <xavi92@disroot.org>2025-11-12 00:37:26 +0100
committerXavier Del Campo Romero <xavi92@disroot.org>2025-11-16 22:57:45 +0100
commit2ce58c995946f85666e793c4f06efff683e76ae4 (patch)
treefbf2658bb0b0f61dadcf4ca27f997eaded78aae5 /src/bin
parent5ce25ae3b5d8666d373f7d7e336546ce8508c213 (diff)
Diffstat (limited to 'src/bin')
-rw-r--r--src/bin/include/bin.h4
-rw-r--r--src/bin/private_include/bin/proc.h2
-rw-r--r--src/bin/private_include/bin/types.h16
-rw-r--r--src/bin/private_include/bin/wasi/errno.h105
-rw-r--r--src/bin/private_include/bin/wnix.h8
-rw-r--r--src/bin/private_include/bin/wnix/routines.h19
-rw-r--r--src/bin/src/exec.c9
-rw-r--r--src/bin/src/mod/cfg.c44
-rw-r--r--src/bin/src/proc/CMakeLists.txt2
-rw-r--r--src/bin/src/proc/fd_alloc.c54
-rw-r--r--src/bin/src/proc/fd_find.c38
-rw-r--r--src/bin/src/proc/free.c3
-rw-r--r--src/bin/src/proc/pc.c19
-rw-r--r--src/bin/src/update.c6
-rw-r--r--src/bin/src/wnix/CMakeLists.txt7
-rw-r--r--src/bin/src/wnix/argcopy.c14
-rw-r--r--src/bin/src/wnix/close.c32
-rw-r--r--src/bin/src/wnix/mkdir.c207
-rw-r--r--src/bin/src/wnix/mount.c75
-rw-r--r--src/bin/src/wnix/open.c222
-rw-r--r--src/bin/src/wnix/routines/CMakeLists.txt20
-rw-r--r--src/bin/src/wnix/routines/readstr.c129
-rw-r--r--src/bin/src/wnix/routines/set_errno.c (renamed from src/bin/src/wnix/set_errno.c)41
-rw-r--r--src/bin/src/wnix/umask.c35
-rw-r--r--src/bin/src/wnix/write.c251
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;
+}