diff options
author | Luke T. Shumaker <lukeshu@lukeshu.com> | 2025-04-02 16:41:26 -0600 |
---|---|---|
committer | Luke T. Shumaker <lukeshu@lukeshu.com> | 2025-04-02 18:42:19 -0600 |
commit | 24a331cb177327b3b4a30064150cd99c469a1d11 (patch) | |
tree | d2778fe1726d70355be69908f7a28f496230484a | |
parent | c077d93a390be2b00e95b3c91adf3c23bc2d0f27 (diff) |
lib9p: Split internal.h up, rename some CONFIG symbols
-rw-r--r-- | cmd/sbc_harness/config/config.h | 26 | ||||
-rw-r--r-- | lib9p/9p.c | 150 | ||||
-rw-r--r-- | lib9p/9p.generated.c | 4 | ||||
-rw-r--r-- | lib9p/CMakeLists.txt | 1 | ||||
-rw-r--r-- | lib9p/include/lib9p/9p.h | 5 | ||||
-rw-r--r-- | lib9p/internal.h | 99 | ||||
-rw-r--r-- | lib9p/map.h | 2 | ||||
-rw-r--r-- | lib9p/protogen/c.py | 4 | ||||
-rw-r--r-- | lib9p/srv.c | 30 | ||||
-rw-r--r-- | lib9p/tables.c | 165 | ||||
-rw-r--r-- | lib9p/tables.h | 44 | ||||
-rw-r--r-- | lib9p/tests/test_compile_config/config.h | 28 | ||||
-rw-r--r-- | lib9p/tests/test_server/config/config.h | 24 | ||||
-rw-r--r-- | lib9p/utf8.h | 34 | ||||
-rw-r--r-- | libmisc/include/libmisc/endian.h | 1 |
15 files changed, 320 insertions, 297 deletions
diff --git a/cmd/sbc_harness/config/config.h b/cmd/sbc_harness/config/config.h index fd089b1..da3edad 100644 --- a/cmd/sbc_harness/config/config.h +++ b/cmd/sbc_harness/config/config.h @@ -32,6 +32,8 @@ /* 9P *************************************************************************/ +#define CONFIG_9P_MAX_ERR_SIZE 128 /* 128 is what Plan 9 4e uses */ + /** * This max-msg-size is sized so that a Twrite message can return * 8KiB of data. @@ -49,22 +51,22 @@ * negotiated. In Plan 9 1e it was (8*1024)+128, and was bumped to * (8*1024)+160 in 2e and 3e. */ -#define CONFIG_9P_MAX_MSG_SIZE ((4*1024)+24) +#define CONFIG_9P_SRV_MAX_MSG_SIZE ((4*1024)+24) /** * Maximum host-data-structure size. A message may be larger in * unmarshaled-host-structures than marshaled-net-bytes due to (1) - * struct padding, (2) nul-terminator byes for strings. + * struct padding, (2) array pointers. */ -#define CONFIG_9P_MAX_HOSTMSG_SIZE CONFIG_9P_MAX_MSG_SIZE+16 -#define CONFIG_9P_MAX_ERR_SIZE 128 /* 128 is what Plan 9 4e uses */ -#define CONFIG_9P_SRV_MAX_FIDS 16 -#define CONFIG_9P_SRV_MAX_REQS 2 -#define CONFIG_9P_SRV_MAX_DEPTH 3 -#define CONFIG_9P_ENABLE_9P2000 1 /* bool */ -#define CONFIG_9P_ENABLE_9P2000_u 1 /* bool */ -#define CONFIG_9P_ENABLE_9P2000_e 0 /* bool */ -#define CONFIG_9P_ENABLE_9P2000_L 0 /* bool */ -#define CONFIG_9P_ENABLE_9P2000_p9p 0 /* bool */ +#define CONFIG_9P_SRV_MAX_HOSTMSG_SIZE CONFIG_9P_SRV_MAX_MSG_SIZE+16 +#define CONFIG_9P_SRV_MAX_FIDS 16 +#define CONFIG_9P_SRV_MAX_REQS 2 +#define CONFIG_9P_SRV_MAX_DEPTH 3 + +#define CONFIG_9P_ENABLE_9P2000 1 /* bool */ +#define CONFIG_9P_ENABLE_9P2000_u 1 /* bool */ +#define CONFIG_9P_ENABLE_9P2000_e 0 /* bool */ +#define CONFIG_9P_ENABLE_9P2000_L 0 /* bool */ +#define CONFIG_9P_ENABLE_9P2000_p9p 0 /* bool */ /* DHCP ***********************************************************************/ @@ -9,32 +9,10 @@ #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}; @@ -114,131 +92,3 @@ int lib9p_errorf(struct lib9p_ctx *ctx, lib9p_errno_t linux_errno, char const *f return -1; } - -/* main message functions *****************************************************/ - -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. */ - uint32_t net_size = uint32le_decode(net_bytes); - if (net_size < 5) - return lib9p_error(ctx, LINUX_EBADMSG, "message is impossibly short"); - uint8_t typ = net_bytes[4]; - 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_msgtype_str(ctx->version, typ), lib9p_version_str(ctx->version)); - - /* Now use the message-type-specific tentry to process the whole thing. */ - return tentry.validate(ctx, net_size, net_bytes); -} - -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]; - - tentry.unmarshal(ctx, net_bytes, 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); -} - -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_ret ret = { - .net_iov_cnt = 1, - .net_iov = ret_iov, - .net_copied_size = 0, - .net_copied = ret_copied, - }; - - struct _lib9p_send_tentry tentry = xxx_table[ctx->version][typ/2]; - bool ret_erred = tentry.marshal(ctx, body, &ret); - if (ret_iov[ret.net_iov_cnt-1].iov_len == 0) - ret.net_iov_cnt--; - *ret_iov_cnt = ret.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_stat_validate(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes, - uint32_t *ret_net_size, ssize_t *ret_host_size) { - ssize_t host_size = _lib9p_stat_validate(ctx, net_size, net_bytes, ret_net_size); - if (host_size < 0) - return true; - if (ret_host_size) - *ret_host_size = host_size; - return false; -} - -void lib9p_stat_unmarshal(struct lib9p_ctx *ctx, uint8_t *net_bytes, - struct lib9p_stat *ret) { - _lib9p_stat_unmarshal(ctx, net_bytes, ret); -} - -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_ret ret = { - .net_iov_cnt = 1, - .net_iov = &iov, - .net_copied_size = 0, - .net_copied = ret_bytes, - }; - if (_lib9p_stat_marshal(&_ctx, obj, &ret)) - return 0; - return ret.net_iov[0].iov_len; -} diff --git a/lib9p/9p.generated.c b/lib9p/9p.generated.c index 3031392..2549b87 100644 --- a/lib9p/9p.generated.c +++ b/lib9p/9p.generated.c @@ -6,10 +6,12 @@ #include <string.h> /* for memset() */ #include <libmisc/assert.h> +#include <libmisc/endian.h> #include <lib9p/9p.h> -#include "internal.h" +#include "tables.h" +#include "utf8.h" /* utilities ******************************************************************/ #if CONFIG_9P_ENABLE_9P2000 diff --git a/lib9p/CMakeLists.txt b/lib9p/CMakeLists.txt index 707308e..9240aa3 100644 --- a/lib9p/CMakeLists.txt +++ b/lib9p/CMakeLists.txt @@ -8,6 +8,7 @@ target_include_directories(lib9p SYSTEM INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/in target_sources(lib9p INTERFACE 9p.generated.c 9p.c + tables.c srv.c ) target_link_libraries(lib9p INTERFACE diff --git a/lib9p/include/lib9p/9p.h b/lib9p/include/lib9p/9p.h index ffac453..4cdb997 100644 --- a/lib9p/include/lib9p/9p.h +++ b/lib9p/include/lib9p/9p.h @@ -18,6 +18,7 @@ #ifndef CONFIG_9P_MAX_ERR_SIZE #error config.h must define CONFIG_9P_MAX_ERR_SIZE #endif +static_assert(CONFIG_9P_MAX_ERR_SIZE <= UINT16_MAX); /* constants ******************************************************************/ @@ -60,6 +61,10 @@ int lib9p_error(struct lib9p_ctx *ctx, lib9p_errno_t linux_errno, char const *ms /** Write a printf-style error into ctx, return -1. */ int lib9p_errorf(struct lib9p_ctx *ctx, lib9p_errno_t linux_errno, char const *fmt, ...) [[gnu::format(printf, 3, 4)]]; +/* misc utilities *************************************************************/ + +uint32_t lib9p_version_min_msg_size(enum lib9p_version); + /* main T-message functions ***************************************************/ /** diff --git a/lib9p/internal.h b/lib9p/internal.h deleted file mode 100644 index 51bf792..0000000 --- a/lib9p/internal.h +++ /dev/null @@ -1,99 +0,0 @@ -/* lib9p/internal.h - Internal machinery shared between parts of lib9p - * - * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com> - * SPDX-License-Identifier: AGPL-3.0-or-later - */ - -#ifndef _LIB9P_INTERNAL_H_ -#define _LIB9P_INTERNAL_H_ - -#include <stddef.h> /* for size_t */ -#include <limits.h> /* for SSIZE_MAX, not set by newlib */ -#ifndef SSIZE_MAX -#define SSIZE_MAX (SIZE_MAX >> 1) -#endif - -#include <libmisc/endian.h> -#include <libmisc/macro.h> - -#include <lib9p/9p.h> - -/* configuration **************************************************************/ - -#include "config.h" - -#ifndef CONFIG_9P_MAX_MSG_SIZE - #error config.h must define CONFIG_9P_MAX_MSG_SIZE -#endif -#ifndef CONFIG_9P_MAX_HOSTMSG_SIZE - #error config.h must define CONFIG_9P_MAX_HOSTMSG_SIZE -#endif -#ifndef CONFIG_9P_MAX_ERR_SIZE - #error config.h must define CONFIG_9P_MAX_ERR_SIZE -#endif - -static_assert(CONFIG_9P_MAX_ERR_SIZE <= UINT16_MAX); -static_assert(CONFIG_9P_MAX_MSG_SIZE <= CONFIG_9P_MAX_HOSTMSG_SIZE); -static_assert(CONFIG_9P_MAX_HOSTMSG_SIZE <= SSIZE_MAX); - -/* tables / exports ***********************************************************/ - -typedef ssize_t (*_validate_fn_t)(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes); -typedef void (*_unmarshal_fn_t)(struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out); - -struct _marshal_ret { - size_t net_iov_cnt; - struct iovec *net_iov; - size_t net_copied_size; - uint8_t *net_copied; -}; -typedef bool (*_marshal_fn_t)(struct lib9p_ctx *ctx, void *host_val, struct _marshal_ret *ret); - -struct _lib9p_recv_tentry { - _validate_fn_t validate; - _unmarshal_fn_t unmarshal; -}; - -struct _lib9p_send_tentry { - _marshal_fn_t marshal; -}; - -extern const char *const _lib9p_table_ver_name[LIB9P_VER_NUM]; -extern const char *const _lib9p_table_msg_name[LIB9P_VER_NUM][0x100]; -extern const uint32_t _lib9p_table_msg_min_size[LIB9P_VER_NUM]; -extern const struct _lib9p_recv_tentry _lib9p_table_Tmsg_recv[LIB9P_VER_NUM][0x80]; -extern const struct _lib9p_recv_tentry _lib9p_table_Rmsg_recv[LIB9P_VER_NUM][0x80]; -extern const struct _lib9p_send_tentry _lib9p_table_Tmsg_send[LIB9P_VER_NUM][0x80]; -extern const struct _lib9p_send_tentry _lib9p_table_Rmsg_send[LIB9P_VER_NUM][0x80]; - -ssize_t _lib9p_stat_validate(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes, uint32_t *ret_net_size); -void _lib9p_stat_unmarshal(struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out); -bool _lib9p_stat_marshal(struct lib9p_ctx *ctx, struct lib9p_stat *val, struct _marshal_ret *ret); - -/* unmarshal utilities ********************************************************/ - -static inline bool _is_valid_utf8(uint8_t *str, size_t len, bool forbid_nul) { - uint32_t ch; - uint8_t chlen; - assert(str); - for (size_t pos = 0; pos < len;) { - if ((str[pos] & 0b10000000) == 0b00000000) { ch = str[pos] & 0b01111111; chlen = 1; } - else if ((str[pos] & 0b11100000) == 0b11000000) { ch = str[pos] & 0b00011111; chlen = 2; } - else if ((str[pos] & 0b11110000) == 0b11100000) { ch = str[pos] & 0b00001111; chlen = 3; } - else if ((str[pos] & 0b11111000) == 0b11110000) { ch = str[pos] & 0b00000111; chlen = 4; } - else return false; - if ((ch == 0 && (chlen != 1 || forbid_nul)) || pos + chlen > len) return false; - for (uint8_t i = 1; i < chlen; i++) { - if ((str[pos+i] & 0b11000000) != 0b10000000) return false; - ch = (ch << 6) | (str[pos+i] & 0b00111111); - } - if (ch > 0x10FFFF) return false; - pos += chlen; - } - return true; -} - -#define is_valid_utf8(str, len) _is_valid_utf8(str, len, false) -#define is_valid_utf8_without_nul(str, len) _is_valid_utf8(str, len, true) - -#endif /* _LIB9P_INTERNAL_H_ */ diff --git a/lib9p/map.h b/lib9p/map.h index ab9564f..c5eab0f 100644 --- a/lib9p/map.h +++ b/lib9p/map.h @@ -4,8 +4,6 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ -#include "internal.h" - /** * `#define` `NAME`, `KEY_T`, `VAL_T`, and `CAP`; then `#include * "map.h". diff --git a/lib9p/protogen/c.py b/lib9p/protogen/c.py index 0c9b21f..c487c03 100644 --- a/lib9p/protogen/c.py +++ b/lib9p/protogen/c.py @@ -29,10 +29,12 @@ def gen_c(versions: set[str], typs: list[idl.UserType]) -> str: #include <string.h> /* for memset() */ #include <libmisc/assert.h> +#include <libmisc/endian.h> #include <lib9p/9p.h> -#include "internal.h" +#include "tables.h" +#include "utf8.h" """ # utilities ################################################################ diff --git a/lib9p/srv.c b/lib9p/srv.c index a29a4cb..2723207 100644 --- a/lib9p/srv.c +++ b/lib9p/srv.c @@ -6,12 +6,18 @@ #include <alloca.h> #include <inttypes.h> /* for PRI* */ +#include <stddef.h> /* for size_t */ +#include <limits.h> /* for SSIZE_MAX, not set by newlib */ +#ifndef SSIZE_MAX +#define SSIZE_MAX (SIZE_MAX >> 1) +#endif #include <libcr/coroutine.h> #include <libcr_ipc/chan.h> #include <libcr_ipc/mutex.h> #include <libcr_ipc/select.h> #include <libmisc/assert.h> +#include <libmisc/endian.h> #include <libhw/generic/net.h> #define LOG_NAME 9P_SRV @@ -20,7 +26,9 @@ #define IMPLEMENTATION_FOR_LIB9P_SRV_H YES #include <lib9p/srv.h> -#include "internal.h" +/* config *********************************************************************/ + +#include "config.h" #ifndef CONFIG_9P_SRV_MAX_FIDS #error config.h must define CONFIG_9P_SRV_MAX_FIDS @@ -32,6 +40,14 @@ /* 1=just the root dir, 2=just files in the root dir, 3=1 subdir, ... */ #error config.h must define CONFIG_9P_SRV_MAX_DEPTH #endif +#ifndef CONFIG_9P_SRV_MAX_MSG_SIZE + #error config.h must define CONFIG_9P_SRV_MAX_MSG_SIZE +#endif +#ifndef CONFIG_9P_SRV_MAX_HOSTMSG_SIZE + #error config.h must define CONFIG_9P_SRV_MAX_HOSTMSG_SIZE +#endif +static_assert(CONFIG_9P_SRV_MAX_MSG_SIZE <= CONFIG_9P_SRV_MAX_HOSTMSG_SIZE); +static_assert(CONFIG_9P_SRV_MAX_HOSTMSG_SIZE <= SSIZE_MAX); /* context ********************************************************************/ @@ -218,7 +234,7 @@ static void handle_message(struct _lib9p_srv_req *ctx); srv->readers++; - uint32_t initial_rerror_overhead = _lib9p_table_msg_min_size[LIB9P_VER_unknown]; + uint32_t initial_rerror_overhead = lib9p_version_min_msg_size(LIB9P_VER_unknown); for (;;) { struct _srv_conn conn = { @@ -238,7 +254,7 @@ static void handle_message(struct _lib9p_srv_req *ctx); struct _srv_sess sess = { .parent_conn = &conn, .version = LIB9P_VER_unknown, - .max_msg_size = CONFIG_9P_MAX_MSG_SIZE, + .max_msg_size = CONFIG_9P_SRV_MAX_MSG_SIZE, .rerror_overhead = initial_rerror_overhead, .initialized = false, }; @@ -397,7 +413,7 @@ static tmessage_handler tmessage_handlers[0x100] = { static void handle_message(struct _lib9p_srv_req *ctx) { uint8_t *host_req = NULL; - uint8_t host_resp[CONFIG_9P_MAX_HOSTMSG_SIZE]; + uint8_t host_resp[CONFIG_9P_SRV_MAX_HOSTMSG_SIZE]; /* Unmarshal it. */ ssize_t host_size = lib9p_Tmsg_validate(&ctx->ctx.basectx, ctx->net_bytes); @@ -573,7 +589,7 @@ static void handle_Tversion(struct _lib9p_srv_req *ctx, #endif } - uint32_t min_msg_size = _lib9p_table_msg_min_size[version]; + uint32_t min_msg_size = lib9p_version_min_msg_size(version); if (req->max_msg_size < min_msg_size) { lib9p_errorf(&ctx->ctx.basectx, LINUX_EDOM, "requested max_msg_size is less than minimum for %s (%"PRIu32" < %"PRIu32")", @@ -582,8 +598,8 @@ static void handle_Tversion(struct _lib9p_srv_req *ctx, } resp->version = lib9p_str((char *)lib9p_version_str(version)); /* cast to discard "const" qualifier */ - resp->max_msg_size = (CONFIG_9P_MAX_MSG_SIZE < req->max_msg_size) - ? CONFIG_9P_MAX_MSG_SIZE + resp->max_msg_size = (CONFIG_9P_SRV_MAX_MSG_SIZE < req->max_msg_size) + ? CONFIG_9P_SRV_MAX_MSG_SIZE : req->max_msg_size; /* Close the old session. */ diff --git a/lib9p/tables.c b/lib9p/tables.c new file mode 100644 index 0000000..34ac1af --- /dev/null +++ b/lib9p/tables.c @@ -0,0 +1,165 @@ +/* lib9p/tables.c - Access tables of version and message information + * + * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com> + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +#include <string.h> + +#include <libmisc/endian.h> +#include <libmisc/log.h> /* for const_byte_str() */ + +#include "tables.h" + +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]; +} + +uint32_t lib9p_version_min_msg_size(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_msg_min_size[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); +} + +/* main message functions *****************************************************/ + +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. */ + uint32_t net_size = uint32le_decode(net_bytes); + if (net_size < 5) + return lib9p_error(ctx, LINUX_EBADMSG, "message is impossibly short"); + uint8_t typ = net_bytes[4]; + 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_msgtype_str(ctx->version, typ), lib9p_version_str(ctx->version)); + + /* Now use the message-type-specific tentry to process the whole thing. */ + return tentry.validate(ctx, net_size, net_bytes); +} + +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]; + + tentry.unmarshal(ctx, net_bytes, 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); +} + +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_ret ret = { + .net_iov_cnt = 1, + .net_iov = ret_iov, + .net_copied_size = 0, + .net_copied = ret_copied, + }; + + struct _lib9p_send_tentry tentry = xxx_table[ctx->version][typ/2]; + bool ret_erred = tentry.marshal(ctx, body, &ret); + if (ret_iov[ret.net_iov_cnt-1].iov_len == 0) + ret.net_iov_cnt--; + *ret_iov_cnt = ret.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_stat_validate(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes, + uint32_t *ret_net_size, ssize_t *ret_host_size) { + ssize_t host_size = _lib9p_stat_validate(ctx, net_size, net_bytes, ret_net_size); + if (host_size < 0) + return true; + if (ret_host_size) + *ret_host_size = host_size; + return false; +} + +void lib9p_stat_unmarshal(struct lib9p_ctx *ctx, uint8_t *net_bytes, + struct lib9p_stat *ret) { + _lib9p_stat_unmarshal(ctx, net_bytes, ret); +} + +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_ret ret = { + .net_iov_cnt = 1, + .net_iov = &iov, + .net_copied_size = 0, + .net_copied = ret_bytes, + }; + if (_lib9p_stat_marshal(&_ctx, obj, &ret)) + return 0; + return ret.net_iov[0].iov_len; +} diff --git a/lib9p/tables.h b/lib9p/tables.h new file mode 100644 index 0000000..5591e4b --- /dev/null +++ b/lib9p/tables.h @@ -0,0 +1,44 @@ +/* lib9p/tables.h - Declare tables of version and message information + * + * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com> + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +#ifndef _LIB9P_TABLES_H_ +#define _LIB9P_TABLES_H_ + +#include <lib9p/9p.h> + +typedef ssize_t (*_validate_fn_t)(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes); +typedef void (*_unmarshal_fn_t)(struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out); + +struct _marshal_ret { + size_t net_iov_cnt; + struct iovec *net_iov; + size_t net_copied_size; + uint8_t *net_copied; +}; +typedef bool (*_marshal_fn_t)(struct lib9p_ctx *ctx, void *host_val, struct _marshal_ret *ret); + +struct _lib9p_recv_tentry { + _validate_fn_t validate; + _unmarshal_fn_t unmarshal; +}; + +struct _lib9p_send_tentry { + _marshal_fn_t marshal; +}; + +extern const char *const _lib9p_table_ver_name[LIB9P_VER_NUM]; +extern const char *const _lib9p_table_msg_name[LIB9P_VER_NUM][0x100]; +extern const uint32_t _lib9p_table_msg_min_size[LIB9P_VER_NUM]; +extern const struct _lib9p_recv_tentry _lib9p_table_Tmsg_recv[LIB9P_VER_NUM][0x80]; +extern const struct _lib9p_recv_tentry _lib9p_table_Rmsg_recv[LIB9P_VER_NUM][0x80]; +extern const struct _lib9p_send_tentry _lib9p_table_Tmsg_send[LIB9P_VER_NUM][0x80]; +extern const struct _lib9p_send_tentry _lib9p_table_Rmsg_send[LIB9P_VER_NUM][0x80]; + +ssize_t _lib9p_stat_validate(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes, uint32_t *ret_net_size); +void _lib9p_stat_unmarshal(struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out); +bool _lib9p_stat_marshal(struct lib9p_ctx *ctx, struct lib9p_stat *val, struct _marshal_ret *ret); + +#endif /* _LIB9P_TABLES_H_ */ diff --git a/lib9p/tests/test_compile_config/config.h b/lib9p/tests/test_compile_config/config.h index 38ab0c0..cc8eec1 100644 --- a/lib9p/tests/test_compile_config/config.h +++ b/lib9p/tests/test_compile_config/config.h @@ -9,20 +9,20 @@ /* 9P *************************************************************************/ -#define CONFIG_9P_MAX_MSG_SIZE ((4*1024)+24) -#define CONFIG_9P_MAX_HOSTMSG_SIZE CONFIG_9P_MAX_MSG_SIZE+16 -#define CONFIG_9P_MAX_ERR_SIZE 128 -#define CONFIG_9P_MAX_9P2000_e_WELEM 16 - -#define CONFIG_9P_SRV_MAX_FIDS 16 -#define CONFIG_9P_SRV_MAX_REQS 2 -#define CONFIG_9P_SRV_MAX_DEPTH 3 - -#define CONFIG_9P_ENABLE_9P2000 1 /* bool */ -#define CONFIG_9P_ENABLE_9P2000_u 1 /* bool */ -#define CONFIG_9P_ENABLE_9P2000_e 1 /* bool */ -#define CONFIG_9P_ENABLE_9P2000_L 1 /* bool */ -#define CONFIG_9P_ENABLE_9P2000_p9p 1 /* bool */ +#define CONFIG_9P_MAX_ERR_SIZE 128 +#define CONFIG_9P_MAX_9P2000_e_WELEM 16 + +#define CONFIG_9P_SRV_MAX_MSG_SIZE ((4*1024)+24) +#define CONFIG_9P_SRV_MAX_HOSTMSG_SIZE CONFIG_9P_SRV_MAX_MSG_SIZE+16 +#define CONFIG_9P_SRV_MAX_FIDS 16 +#define CONFIG_9P_SRV_MAX_REQS 2 +#define CONFIG_9P_SRV_MAX_DEPTH 3 + +#define CONFIG_9P_ENABLE_9P2000 1 /* bool */ +#define CONFIG_9P_ENABLE_9P2000_u 1 /* bool */ +#define CONFIG_9P_ENABLE_9P2000_e 1 /* bool */ +#define CONFIG_9P_ENABLE_9P2000_L 1 /* bool */ +#define CONFIG_9P_ENABLE_9P2000_p9p 1 /* bool */ /* COROUTINE ******************************************************************/ diff --git a/lib9p/tests/test_server/config/config.h b/lib9p/tests/test_server/config/config.h index afe6dc0..03143e1 100644 --- a/lib9p/tests/test_server/config/config.h +++ b/lib9p/tests/test_server/config/config.h @@ -12,6 +12,8 @@ /* 9P *************************************************************************/ +#define CONFIG_9P_MAX_ERR_SIZE 128 /* 128 is what Plan 9 4e uses */ + /** * This max-msg-size is sized so that a Twrite message can return * 8KiB of data. @@ -29,22 +31,22 @@ * negotiated. In Plan 9 1e it was (8*1024)+128, and was bumped to * (8*1024)+160 in 2e and 3e. */ -#define CONFIG_9P_MAX_MSG_SIZE ((4*1024)+24) +#define CONFIG_9P_SRV_MAX_MSG_SIZE ((4*1024)+24) /** * Maximum host-data-structure size. A message may be larger in * unmarshaled-host-structures than marshaled-net-bytes due to (1) * struct padding, (2) array pointers. */ -#define CONFIG_9P_MAX_HOSTMSG_SIZE CONFIG_9P_MAX_MSG_SIZE+16 -#define CONFIG_9P_MAX_ERR_SIZE 128 /* 128 is what Plan 9 4e uses */ -#define CONFIG_9P_SRV_MAX_FIDS 16 -#define CONFIG_9P_SRV_MAX_REQS 2 -#define CONFIG_9P_SRV_MAX_DEPTH 3 -#define CONFIG_9P_ENABLE_9P2000 1 /* bool */ -#define CONFIG_9P_ENABLE_9P2000_u 1 /* bool */ -#define CONFIG_9P_ENABLE_9P2000_e 0 /* bool */ -#define CONFIG_9P_ENABLE_9P2000_L 0 /* bool */ -#define CONFIG_9P_ENABLE_9P2000_p9p 0 /* bool */ +#define CONFIG_9P_SRV_MAX_HOSTMSG_SIZE CONFIG_9P_SRV_MAX_MSG_SIZE+16 +#define CONFIG_9P_SRV_MAX_FIDS 16 +#define CONFIG_9P_SRV_MAX_REQS 2 +#define CONFIG_9P_SRV_MAX_DEPTH 3 + +#define CONFIG_9P_ENABLE_9P2000 1 /* bool */ +#define CONFIG_9P_ENABLE_9P2000_u 1 /* bool */ +#define CONFIG_9P_ENABLE_9P2000_e 0 /* bool */ +#define CONFIG_9P_ENABLE_9P2000_L 0 /* bool */ +#define CONFIG_9P_ENABLE_9P2000_p9p 0 /* bool */ /* COROUTINE ******************************************************************/ diff --git a/lib9p/utf8.h b/lib9p/utf8.h new file mode 100644 index 0000000..5ffd674 --- /dev/null +++ b/lib9p/utf8.h @@ -0,0 +1,34 @@ +/* lib9p/utf8.h - Internal UTF-8 validation + * + * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com> + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +#ifndef _LIB9P_UTF8_H_ +#define _LIB9P_UTF8_H_ + +static inline bool _is_valid_utf8(uint8_t *str, size_t len, bool forbid_nul) { + uint32_t ch; + uint8_t chlen; + assert(str); + for (size_t pos = 0; pos < len;) { + if ((str[pos] & 0b10000000) == 0b00000000) { ch = str[pos] & 0b01111111; chlen = 1; } + else if ((str[pos] & 0b11100000) == 0b11000000) { ch = str[pos] & 0b00011111; chlen = 2; } + else if ((str[pos] & 0b11110000) == 0b11100000) { ch = str[pos] & 0b00001111; chlen = 3; } + else if ((str[pos] & 0b11111000) == 0b11110000) { ch = str[pos] & 0b00000111; chlen = 4; } + else return false; + if ((ch == 0 && (chlen != 1 || forbid_nul)) || pos + chlen > len) return false; + for (uint8_t i = 1; i < chlen; i++) { + if ((str[pos+i] & 0b11000000) != 0b10000000) return false; + ch = (ch << 6) | (str[pos+i] & 0b00111111); + } + if (ch > 0x10FFFF) return false; + pos += chlen; + } + return true; +} + +#define is_valid_utf8(str, len) _is_valid_utf8(str, len, false) +#define is_valid_utf8_without_nul(str, len) _is_valid_utf8(str, len, true) + +#endif /* _LIB9P_UTF8_H_ */ diff --git a/libmisc/include/libmisc/endian.h b/libmisc/include/libmisc/endian.h index 665a8c8..75240fe 100644 --- a/libmisc/include/libmisc/endian.h +++ b/libmisc/include/libmisc/endian.h @@ -7,6 +7,7 @@ #ifndef _LIBMISC_ENDIAN_H_ #define _LIBMISC_ENDIAN_H_ +#include <stddef.h> /* for size_t */ #include <stdint.h> /* for uint{n}_t */ #include <libmisc/assert.h> |