diff options
Diffstat (limited to 'lib9p/9p.c')
-rw-r--r-- | lib9p/9p.c | 210 |
1 files changed, 166 insertions, 44 deletions
@@ -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; } |