diff options
author | Luke T. Shumaker <lukeshu@lukeshu.com> | 2024-09-26 19:36:54 -0600 |
---|---|---|
committer | Luke T. Shumaker <lukeshu@lukeshu.com> | 2024-09-26 19:36:54 -0600 |
commit | 71e1a86a033c380f85dd300d788af63bfef25bab (patch) | |
tree | 07aa53d5a933ba51535a78972edbfe0cd95a31c5 /lib9p/srv.c | |
parent | f5da707e77ee954b12f3c961012e4f40fa4e1bd3 (diff) |
wip reorg
Diffstat (limited to 'lib9p/srv.c')
-rw-r--r-- | lib9p/srv.c | 194 |
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 |