summaryrefslogtreecommitdiff
path: root/lib9p/srv.c
diff options
context:
space:
mode:
authorLuke T. Shumaker <lukeshu@lukeshu.com>2024-09-26 19:36:54 -0600
committerLuke T. Shumaker <lukeshu@lukeshu.com>2024-09-26 19:36:54 -0600
commit71e1a86a033c380f85dd300d788af63bfef25bab (patch)
tree07aa53d5a933ba51535a78972edbfe0cd95a31c5 /lib9p/srv.c
parentf5da707e77ee954b12f3c961012e4f40fa4e1bd3 (diff)
wip reorg
Diffstat (limited to 'lib9p/srv.c')
-rw-r--r--lib9p/srv.c194
1 files changed, 194 insertions, 0 deletions
diff --git a/lib9p/srv.c b/lib9p/srv.c
new file mode 100644
index 0000000..59326c9
--- /dev/null
+++ b/lib9p/srv.c
@@ -0,0 +1,194 @@
+#include <assert.h>
+
+#include "coroutine.h"
+#include "netio.h"
+#include "9p/9p.h"
+
+#include "9p/internal.h"
+
+struct p9_srvconn {
+ /* immutable */
+ p9_srv *srv;
+ cid_t reader;
+ int fd;
+ /* mutable */
+ uint32_t max_msg_size;
+ enum p9_version version;
+ unsigned int refcount;
+};
+
+struct p9_srvreq {
+ p9_srvconn *conn;
+ uint8_t *msg;
+};
+
+COROUTINE p9_srv_read_cr(void *_srv) {
+ uint8_t buf[CONFIG_9P_MAX_MSG_SIZE];
+
+ p9_srv *srv = _srv;
+ assert(srv);
+ cr_begin();
+
+ for (;;) {
+ struct p9_srvconn conn = {
+ .srv = srv,
+ .reader = cr_getcid();
+
+ .max_msg_size = CONFIG_9P_MAX_MSG_SIZE;
+ .version = P9_VER_UNINITIALIZED;
+ .refcount = 1,
+ };
+ conn.fd = netio_accept(srv->sockfd);
+ if (conn.fd < 0) {
+ fprintf(stderr, "error: accept: %m", -conn.fd);
+ continue;
+ }
+
+ for (;;) {
+ /* Read the message size. */
+ size_t goal = 4, done = 0;
+ while (done < goal) {
+ ssize_t r = netio_read(conn.fd, &buf[done], sizeof(buf)-done);
+ if (r < 0) {
+ fprintf(stderr, "error: read: %m", -r);
+ goto close;
+ } else if (r == 0) {
+ if (done != 0)
+ fprintf(stderr, "error: read: unexpected EOF");
+ goto close;
+ }
+ done += r;
+ }
+ goal = decode_u32le(buf);
+ if (goal < 7) {
+ /* We can't respond with an Rerror becuase we wouldn't know what tag to use! */
+ fprintf(stderr, "error: T-message is impossibly small");
+ goto close;
+ }
+ if (goal > conn.max_msg_size) {
+ struct p9_ctx ctx = {
+ .version = conn.version,
+ .max_msg_size = conn.max_msg_size,
+ };
+ if (initialized)
+ p9_errorf(&ctx, LINUX_EMSGSIZE, "T-message larger than negotiated limit (%zu > %zu)", goal, conn.max_msg_size);
+ else
+ p9_errorf(&ctx, LINUX_EMSGSIZE, "T-message larger than server limit (%zu > %zu)", goal, conn.max_msg_size);
+ marshal_error(&ctx, buf);
+ netio_write(conn.fd, buf, decode_u32le(buf));
+ continue;
+ }
+ /* Read the rest of the message. */
+ while (done < goal) {
+ ssize_t r = netio_read(conn.fd, &buf[done], sizeof(buf)-done);
+ if (r < 0) {
+ fprintf(stderr, "error: read: %m", -r);
+ goto close;
+ } else if (r == 0) {
+ fprintf(stderr, "error: read: unexpected EOF");
+ goto close;
+ }
+ done += r;
+ }
+
+ /* Handle the message... */
+ if (conn.version == P9_VER_UNINITIALIZED) {
+ /* ...synchronously if we haven't negotiated the protocol yet, ... */
+ handle_message(&conn, buf);
+ } else {
+ /* ...asynchronously if we have. */
+ cr_chan_send(&srv->reqch, buf);
+ cr_pause_and_yield();
+ }
+ }
+ close:
+ netio_close(conn.fd, true, (--conn.refcount) == 0);
+ if (conn.refcount) {
+ cr_pause_and_yield();
+ assert(conn.refcount == 0);
+ netio_close(conn.fd, false, true);
+ }
+ }
+
+ cr_end();
+}
+
+COROUTINE p9_srv_write_cr(void *_srv) {
+ uint8_t net[CONFIG_9P_MAX_MSG_SIZE];
+
+ p9_srv *srv = _srv;
+ assert(srv);
+ cr_begin();
+
+ for (;;) {
+ struct p9_srvreq req;
+ cr_chan_recv(&srv->reqch, &req);
+ memcpy(net, req.msg, decode_u32le(req.msg));
+ req.conn->refcount++;
+ cr_unpause(req.conn->reader);
+
+ handle_message(&req.conn, net);
+
+ if ((--req.conn->refcount) == 0)
+ cr_unpause(req.conn->reader);
+ }
+
+ cr_end();
+}
+
+void handle_message(p9_srvconn *conn, uint8_t *net) {
+ uint8_t host[CONFIG_9P_MAX_MSG_SIZE];
+
+ struct p9_ctx ctx = {
+ .version = req.conn->version,
+ .max_msg_size = req.conn->max_msg_size,
+ };
+
+ size_t host_size = p9_unmarshal_size(&ctx, net);
+ if (host_size == (size_t)-1)
+ goto write;
+ if (host_size > sizeof(host)) {
+ p9_errorf(&ctx, LINUX_EMSGSIZE, "unmarshalled payload larger than server limit (%zu > %zu)", host_size, sizeof(host));
+ goto write;
+ }
+
+ uint16_t tag;
+ uint8_t typ = p9_unmarshal(&ctx, net, &tag, host);
+ if (typ == (uint8_t)-1)
+ goto write;
+ if (typ % 2 != 0) {
+ p9_errorf(&ctx, LINUX_EOPNOTSUPP, "expected a T-message but got an R-message");
+ goto write;
+ }
+
+ TODO;
+
+ write:
+ if (ctx.err_num || ctx.err_msg[0])
+ marshal_error(&ctx, net);
+ else
+ TODO;
+ netio_write(req.conn->fd, net, decode_u32le(net));
+}
+
+static inline uint16_t min_u16(uint16_t a, b) {
+ return (a < b) ? a : b;
+}
+
+/* We have special code for marshaling Rerror because we don't ever
+ * want to produce an error because the err_msg is too long for the
+ * `ctx->max_msg_size`! */
+void marshal_error(struct p9_ctx *ctx, uint16_t tag, uint8_t *net) {
+ struct p9_msg_Rerror host = {
+ .ename = {
+ .len = strnlen(ctx->err_msg, CONFIG_9P_MAX_ERR_SIZE),
+ .utf8 = ctx->err_msg,
+ },
+ };
+ if (host.ename.len + ctx->Rerror_overhead > ctx->max_msg_size)
+ host.ename.len = ctx->max_msg_size - overhead;
+ p9_marshal(ctx, tag, host, net);
+}
+
+ERANGE for reply too large
+EPROTONOSUPPORT for version errors