summaryrefslogtreecommitdiff
path: root/lib9p/9p.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib9p/9p.c')
-rw-r--r--lib9p/9p.c210
1 files changed, 166 insertions, 44 deletions
diff --git a/lib9p/9p.c b/lib9p/9p.c
index 9ea0c46..1caa01a 100644
--- a/lib9p/9p.c
+++ b/lib9p/9p.c
@@ -1,6 +1,6 @@
/* lib9p/9p.c - Base 9P protocol utilities for both clients and servers
*
- * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
@@ -9,15 +9,66 @@
#include <stdio.h> /* for vsnprintf() */
#include <string.h> /* for strncpy() */
+#define LOG_NAME 9P
+#include <libmisc/log.h> /* for const_byte_str() */
+
#include <lib9p/9p.h>
#include "internal.h"
+/* strings ********************************************************************/
+
+const char *lib9p_version_str(enum lib9p_version ver) {
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wtype-limits"
+ assert(0 <= ver && ver < LIB9P_VER_NUM);
+#pragma GCC diagnostic pop
+ return _lib9p_table_ver_name[ver];
+}
+
+const char *lib9p_msgtype_str(enum lib9p_version ver, enum lib9p_msg_type typ) {
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wtype-limits"
+ assert(0 <= ver && ver < LIB9P_VER_NUM);
+ assert(0 <= typ && typ <= 0xFF);
+#pragma GCC diagnostic pop
+ return _lib9p_table_msg_name[ver][typ] ?: const_byte_str(typ);
+}
+
+struct lib9p_s lib9p_str(char *s) {
+ if (!s)
+ return (struct lib9p_s){0};
+ return (struct lib9p_s){
+ .len = strlen(s),
+ .utf8 = s,
+ };
+}
+struct lib9p_s lib9p_strn(char *s, size_t maxlen) {
+ if (maxlen == 0 || !s)
+ return (struct lib9p_s){0};
+ return (struct lib9p_s){
+ .len = strnlen(s, maxlen),
+ .utf8 = s,
+ };
+}
+struct lib9p_s lib9p_str_slice(struct lib9p_s s, uint16_t beg, uint16_t end) {
+ assert(s.len == 0 || s.utf8);
+ assert(beg <= end && end <= s.len);
+ return (struct lib9p_s){
+ .len = end - beg,
+ .utf8 = &s.utf8[beg],
+ };
+}
+bool lib9p_str_eq(struct lib9p_s a, struct lib9p_s b) {
+ return a.len == b.len &&
+ (a.len == 0 || memcmp(a.utf8, b.utf8, a.len) == 0);
+}
+
/* ctx ************************************************************************/
void lib9p_ctx_clear_error(struct lib9p_ctx *ctx) {
assert(ctx);
-#ifdef CONFIG_9P_ENABLE_9P2000_u
+#if CONFIG_9P_ENABLE_9P2000_u
ctx->err_num = 0;
#endif
ctx->err_msg[0] = '\0';
@@ -34,7 +85,7 @@ int lib9p_error(struct lib9p_ctx *ctx, uint32_t linux_errno, char const *msg) {
strncpy(ctx->err_msg, msg, sizeof(ctx->err_msg));
ctx->err_msg[sizeof(ctx->err_msg)-1] = '\0';
-#ifdef CONFIG_9P_ENABLE_9P2000_u
+#if CONFIG_9P_ENABLE_9P2000_u
ctx->err_num = linux_errno;
#else
(void)(linux_errno);
@@ -55,7 +106,7 @@ int lib9p_errorf(struct lib9p_ctx *ctx, uint32_t linux_errno, char const *fmt, .
if ((size_t)(n+1) < sizeof(ctx->err_msg))
memset(&ctx->err_msg[n+1], 0, sizeof(ctx->err_msg)-(n+1));
-#ifdef CONFIG_9P_ENABLE_9P2000_u
+#if CONFIG_9P_ENABLE_9P2000_u
ctx->err_num = linux_errno;
#else
(void)(linux_errno);
@@ -64,33 +115,37 @@ int lib9p_errorf(struct lib9p_ctx *ctx, uint32_t linux_errno, char const *fmt, .
return -1;
}
-const char *lib9p_msg_type_str(struct lib9p_ctx *ctx, enum lib9p_msg_type typ) {
- assert(0 <= typ && typ <= 0xFF);
- return _lib9p_versions[ctx->version].msgs[typ].name;
-}
-
/* main message functions *****************************************************/
-ssize_t lib9p_validate(struct lib9p_ctx *ctx, uint8_t *net_bytes) {
+static
+ssize_t _lib9p_validate(uint8_t xxx_low_typ_bit,
+ const char *xxx_errmsg,
+ const struct _lib9p_recv_tentry xxx_table[LIB9P_VER_NUM][0x80],
+ struct lib9p_ctx *ctx, uint8_t *net_bytes) {
/* Inspect the first 5 bytes ourselves. */
struct _validate_ctx subctx = {
+ /* input */
.ctx = ctx,
- .net_size = decode_u32le(net_bytes),
+ .net_size = uint32le_decode(net_bytes),
.net_bytes = net_bytes,
+ /* output */
.net_offset = 0,
.host_extra = 0,
};
if (subctx.net_size < 5)
return lib9p_error(ctx, LINUX_EBADMSG, "message is impossibly short");
uint8_t typ = net_bytes[4];
- struct _table_msg table = _lib9p_versions[ctx->version].msgs[typ];
- if (!table.validate)
+ if (typ % 2 != xxx_low_typ_bit)
+ return lib9p_errorf(ctx, LINUX_EOPNOTSUPP, "%s: message_type=%s", xxx_errmsg,
+ lib9p_msgtype_str(ctx->version, typ));
+ struct _lib9p_recv_tentry tentry = xxx_table[ctx->version][typ/2];
+ if (!tentry.validate)
return lib9p_errorf(ctx, LINUX_EOPNOTSUPP, "unknown message type: %s (protocol_version=%s)",
- lib9p_msg_type_str(ctx, typ), lib9p_version_str(ctx->version));
+ lib9p_msgtype_str(ctx->version, typ), lib9p_version_str(ctx->version));
- /* Now use the message-type-specific table to process the whole thing. */
- if (table.validate(&subctx))
+ /* Now use the message-type-specific tentry to process the whole thing. */
+ if (tentry.validate(&subctx))
return -1;
assert(subctx.net_offset <= subctx.net_size);
if (subctx.net_offset < subctx.net_size)
@@ -99,51 +154,109 @@ ssize_t lib9p_validate(struct lib9p_ctx *ctx, uint8_t *net_bytes) {
/* Return. */
ssize_t ret;
- if (__builtin_add_overflow(table.basesize, subctx.host_extra, &ret))
+ if (__builtin_add_overflow(tentry.basesize, subctx.host_extra, &ret))
return lib9p_error(ctx, LINUX_EMSGSIZE, "unmarshalled payload overflows SSIZE_MAX");
return ret;
}
-void lib9p_unmarshal(struct lib9p_ctx *ctx, uint8_t *net_bytes,
- enum lib9p_msg_type *ret_typ, void *ret_body) {
+ssize_t lib9p_Tmsg_validate(struct lib9p_ctx *ctx, uint8_t *net_bytes) {
+ return _lib9p_validate(0, "expected a T-message but got an R-message", _lib9p_table_Tmsg_recv,
+ ctx, net_bytes);
+}
+
+ssize_t lib9p_Rmsg_validate(struct lib9p_ctx *ctx, uint8_t *net_bytes) {
+ return _lib9p_validate(1, "expected an R-message but got a T-message", _lib9p_table_Rmsg_recv,
+ ctx, net_bytes);
+}
+
+static
+void _lib9p_unmarshal(const struct _lib9p_recv_tentry xxx_table[LIB9P_VER_NUM][0x80],
+ struct lib9p_ctx *ctx, uint8_t *net_bytes,
+ enum lib9p_msg_type *ret_typ, void *ret_body) {
+ enum lib9p_msg_type typ = net_bytes[4];
+ *ret_typ = typ;
+ struct _lib9p_recv_tentry tentry = xxx_table[ctx->version][typ/2];
+
struct _unmarshal_ctx subctx = {
+ /* input */
.ctx = ctx,
.net_bytes = net_bytes,
+ /* output */
.net_offset = 0,
+ .extra = ret_body + tentry.basesize,
};
+ tentry.unmarshal(&subctx, ret_body);
+}
- *ret_typ = net_bytes[4];
- struct _table_msg table = _lib9p_versions[ctx->version].msgs[*ret_typ];
- subctx.extra = ret_body + table.basesize;
- table.unmarshal(&subctx, ret_body);
+void lib9p_Tmsg_unmarshal(struct lib9p_ctx *ctx, uint8_t *net_bytes,
+ enum lib9p_msg_type *ret_typ, void *ret_body) {
+ _lib9p_unmarshal(_lib9p_table_Tmsg_recv,
+ ctx, net_bytes, ret_typ, ret_body);
}
-bool lib9p_marshal(struct lib9p_ctx *ctx, enum lib9p_msg_type typ, void *body,
- uint8_t *ret_bytes) {
+void lib9p_Rmsg_unmarshal(struct lib9p_ctx *ctx, uint8_t *net_bytes,
+ enum lib9p_msg_type *ret_typ, void *ret_body) {
+ _lib9p_unmarshal(_lib9p_table_Rmsg_recv,
+ ctx, net_bytes, ret_typ, ret_body);
+}
+
+static
+bool _lib9p_marshal(const struct _lib9p_send_tentry xxx_table[LIB9P_VER_NUM][0x80],
+ struct lib9p_ctx *ctx, enum lib9p_msg_type typ, void *body,
+ size_t *ret_iov_cnt, struct iovec *ret_iov, uint8_t *ret_copied) {
struct _marshal_ctx subctx = {
- .ctx = ctx,
- .net_bytes = ret_bytes,
- .net_offset = 0,
+ /* input */
+ .ctx = ctx,
+
+ /* ouptut */
+ .net_iov_cnt = 1,
+ .net_iov = ret_iov,
+ .net_copied_size = 0,
+ .net_copied = ret_copied,
};
- struct _table_msg table = _lib9p_versions[ctx->version].msgs[typ];
- return table.marshal(&subctx, body);
+ struct _lib9p_send_tentry tentry = xxx_table[ctx->version][typ/2];
+ bool ret_erred = tentry.marshal(&subctx, body);
+ if (ret_iov[subctx.net_iov_cnt-1].iov_len == 0)
+ subctx.net_iov_cnt--;
+ *ret_iov_cnt = subctx.net_iov_cnt;
+ return ret_erred;
+}
+
+bool lib9p_Tmsg_marshal(struct lib9p_ctx *ctx, enum lib9p_msg_type typ, void *body,
+ struct lib9p_Tmsg_send_buf *ret) {
+ assert(typ % 2 == 0);
+ memset(ret, 0, sizeof(*ret));
+ return _lib9p_marshal(_lib9p_table_Tmsg_send,
+ ctx, typ, body,
+ &ret->iov_cnt, ret->iov, ret->copied);
+}
+
+bool lib9p_Rmsg_marshal(struct lib9p_ctx *ctx, enum lib9p_msg_type typ, void *body,
+ struct lib9p_Rmsg_send_buf *ret) {
+ assert(typ % 2 == 1);
+ memset(ret, 0, sizeof(*ret));
+ return _lib9p_marshal(_lib9p_table_Rmsg_send,
+ ctx, typ, body,
+ &ret->iov_cnt, ret->iov, ret->copied);
}
/* `struct lib9p_stat` helpers ************************************************/
-bool lib9p_validate_stat(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes,
+bool lib9p_stat_validate(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes,
uint32_t *ret_net_size, ssize_t *ret_host_size) {
struct _validate_ctx subctx = {
+ /* input */
.ctx = ctx,
.net_size = net_size,
.net_bytes = net_bytes,
+ /* output */
.net_offset = 0,
.host_extra = 0,
};
- if (_lib9p_validate_stat(&subctx))
+ if (_lib9p_stat_validate(&subctx))
return true;
if (ret_net_size)
*ret_net_size = subctx.net_offset;
@@ -153,29 +266,38 @@ bool lib9p_validate_stat(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_
return false;
}
-uint32_t lib9p_unmarshal_stat(struct lib9p_ctx *ctx, uint8_t *net_bytes,
+uint32_t lib9p_stat_unmarshal(struct lib9p_ctx *ctx, uint8_t *net_bytes,
struct lib9p_stat *ret_obj, void *ret_extra) {
struct _unmarshal_ctx subctx = {
- .ctx = ctx,
- .net_bytes = net_bytes,
- .net_offset = 0,
+ /* input */
+ .ctx = ctx,
+ .net_bytes = net_bytes,
- .extra = ret_extra,
+ /* output */
+ .net_offset = 0,
+ .extra = ret_extra,
};
- _lib9p_unmarshal_stat(&subctx, ret_obj);
+ _lib9p_stat_unmarshal(&subctx, ret_obj);
return subctx.net_offset;
}
-uint32_t lib9p_marshal_stat(struct lib9p_ctx *ctx, uint32_t max_net_size, struct lib9p_stat *obj,
+uint32_t lib9p_stat_marshal(struct lib9p_ctx *ctx, uint32_t max_net_size, struct lib9p_stat *obj,
uint8_t *ret_bytes) {
struct lib9p_ctx _ctx = *ctx;
_ctx.max_msg_size = max_net_size;
+
+ struct iovec iov = {0};
struct _marshal_ctx subctx = {
- .ctx = &_ctx,
- .net_bytes = ret_bytes,
- .net_offset = 0,
+ /* input */
+ .ctx = &_ctx,
+
+ /* output */
+ .net_iov_cnt = 1,
+ .net_iov = &iov,
+ .net_copied_size = 0,
+ .net_copied = ret_bytes,
};
- if (_lib9p_marshal_stat(&subctx, obj))
+ if (_lib9p_stat_marshal(&subctx, obj))
return 0;
- return subctx.net_offset;
+ return subctx.net_iov[0].iov_len;
}