#include #include /* for fprintf(), stderr */ #include /* for strerror() */ #include #include #include #include #include #include "internal.h" struct lib9p_srvconn { /* immutable */ struct lib9p_srv *srv; cid_t reader; int fd; /* mutable */ struct lib9p_ctx ctx; unsigned int refcount; }; struct lib9p_srvreq { struct lib9p_srvconn *conn; uint8_t *msg; }; static void marshal_error(struct lib9p_ctx *ctx, uint16_t tag, uint8_t *net) { struct lib9p_msg_Rerror host = { .ename = { .len = strnlen(ctx->err_msg, CONFIG_9P_MAX_ERR_SIZE), .utf8 = (uint8_t*)ctx->err_msg, }, .errno = ctx->err_num, }; lib9p_marshal(ctx, LIB9P_TYP_Rerror, tag, &host, net); } void handle_message(struct lib9p_srvconn *conn, uint8_t *net); COROUTINE lib9p_srv_read_cr(void *_srv) { uint8_t buf[CONFIG_9P_MAX_MSG_SIZE]; struct lib9p_srv *srv = _srv; assert(srv); cr_begin(); for (;;) { struct lib9p_srvconn conn = { .srv = srv, .reader = cr_getcid(), .ctx = { .version = LIB9P_VER_UNINITIALIZED, .max_msg_size = CONFIG_9P_MAX_MSG_SIZE, }, .refcount = 1, }; conn.fd = netio_accept(srv->sockfd); if (conn.fd < 0) { fprintf(stderr, "error: accept: %s", strerror(-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: %s", strerror(-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.ctx.max_msg_size) { lib9p_errorf(&conn.ctx, LINUX_EMSGSIZE, "T-message larger than %s limit (%zu > %zu)", conn.ctx.version ? "negotiated" : "server", goal, conn.ctx.max_msg_size); uint16_t tag = decode_u16le(&buf[5]); marshal_error(&conn.ctx, tag, 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: %s", strerror(-r)); goto close; } else if (r == 0) { fprintf(stderr, "error: read: unexpected EOF"); goto close; } done += r; } /* Handle the message... */ if (conn.ctx.version == LIB9P_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 lib9p_srv_write_cr(void *_srv) { uint8_t net[CONFIG_9P_MAX_MSG_SIZE]; lib9p_srv *srv = _srv; assert(srv); cr_begin(); for (;;) { struct lib9p_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(struct lib9p_srvconn *conn, uint8_t *net) { uint8_t host[CONFIG_9P_MAX_MSG_SIZE]; struct lib9p_ctx ctx = { .version = req.conn->version, .max_msg_size = req.conn->max_msg_size, }; size_t host_size = lib9p_unmarshal_size(&ctx, net); if (host_size == (size_t)-1) goto write; if (host_size > sizeof(host)) { lib9p_errorf(&ctx, LINUX_EMSGSIZE, "unmarshalled payload larger than server limit (%zu > %zu)", host_size, sizeof(host)); goto write; } uint16_t tag; uint8_t typ = lib9p_unmarshal(&ctx, net, &tag, host); if (typ == (uint8_t)-1) goto write; if (typ % 2 != 0) { lib9p_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)); } // EMSGSIZE for request too large // EPROTONOSUPPORT for version errors