diff options
Diffstat (limited to 'lib9p')
57 files changed, 15298 insertions, 5081 deletions
@@ -1,23 +1,53 @@ /* 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 */ #include <inttypes.h> /* for PRIu{n} */ #include <stdarg.h> /* for va_* */ -#include <stdio.h> /* for vsnprintf() */ #include <string.h> /* for strncpy() */ +#include <libfmt/fmt.h> /* for fmt_vsnprintf() */ + #include <lib9p/9p.h> -#include "internal.h" +/* strings ********************************************************************/ + +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'; @@ -28,13 +58,13 @@ bool lib9p_ctx_has_error(struct lib9p_ctx *ctx) { return ctx->err_msg[0]; } -int lib9p_error(struct lib9p_ctx *ctx, uint32_t linux_errno, char const *msg) { +int lib9p_error(struct lib9p_ctx *ctx, lib9p_errno_t linux_errno, char const *msg) { if (lib9p_ctx_has_error(ctx)) return -1; 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); @@ -43,19 +73,19 @@ int lib9p_error(struct lib9p_ctx *ctx, uint32_t linux_errno, char const *msg) { return -1; } -int lib9p_errorf(struct lib9p_ctx *ctx, uint32_t linux_errno, char const *fmt, ...) { +int lib9p_errorf(struct lib9p_ctx *ctx, lib9p_errno_t linux_errno, char const *fmt, ...) { int n; va_list args; if (lib9p_ctx_has_error(ctx)) return -1; va_start(args, fmt); - n = vsnprintf(ctx->err_msg, sizeof(ctx->err_msg), fmt, args); + n = fmt_vsnprintf(ctx->err_msg, sizeof(ctx->err_msg), fmt, args); va_end(args); 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); @@ -63,119 +93,3 @@ 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) { - /* Inspect the first 5 bytes ourselves. */ - struct _validate_ctx subctx = { - .ctx = ctx, - .net_size = decode_u32le(net_bytes), - .net_bytes = net_bytes, - - .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) - return lib9p_errorf(ctx, LINUX_EOPNOTSUPP, "unknown message type: %s (protocol_version=%s)", - lib9p_msg_type_str(ctx, typ), lib9p_version_str(ctx->version)); - - /* Now use the message-type-specific table to process the whole thing. */ - if (table.validate(&subctx)) - return -1; - assert(subctx.net_offset <= subctx.net_size); - if (subctx.net_offset < subctx.net_size) - return lib9p_errorf(ctx, LINUX_EBADMSG, "message has %"PRIu32" extra bytes", - subctx.net_size - subctx.net_offset); - - /* Return. */ - ssize_t ret; - if (__builtin_add_overflow(table.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) { - struct _unmarshal_ctx subctx = { - .ctx = ctx, - .net_bytes = net_bytes, - - .net_offset = 0, - }; - - *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); -} - -bool lib9p_marshal(struct lib9p_ctx *ctx, enum lib9p_msg_type typ, void *body, - uint8_t *ret_bytes) { - struct _marshal_ctx subctx = { - .ctx = ctx, - .net_bytes = ret_bytes, - .net_offset = 0, - }; - - struct _table_msg table = _lib9p_versions[ctx->version].msgs[typ]; - return table.marshal(&subctx, body); -} - -/* `struct lib9p_stat` helpers ************************************************/ - -bool lib9p_validate_stat(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 = { - .ctx = ctx, - .net_size = net_size, - .net_bytes = net_bytes, - - .net_offset = 0, - .host_extra = 0, - }; - if (_lib9p_validate_stat(&subctx)) - return true; - if (ret_net_size) - *ret_net_size = subctx.net_offset; - if (ret_host_size) - if (__builtin_add_overflow(sizeof(struct lib9p_stat), subctx.host_extra, ret_host_size)) - return lib9p_error(ctx, LINUX_EMSGSIZE, "unmarshalled stat object overflows SSIZE_MAX"); - return false; -} - -uint32_t lib9p_unmarshal_stat(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, - - .extra = ret_extra, - }; - _lib9p_unmarshal_stat(&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, - uint8_t *ret_bytes) { - struct lib9p_ctx _ctx = *ctx; - _ctx.max_msg_size = max_net_size; - struct _marshal_ctx subctx = { - .ctx = &_ctx, - .net_bytes = ret_bytes, - .net_offset = 0, - }; - if (_lib9p_marshal_stat(&subctx, obj)) - return 0; - return subctx.net_offset; -} diff --git a/lib9p/9p.generated.c b/lib9p/9p.generated.c index 6136433..284a0a6 100644 --- a/lib9p/9p.generated.c +++ b/lib9p/9p.generated.c @@ -1,2164 +1,7435 @@ -/* Generated by `./lib9p/idl.gen lib9p/idl/2002-9P2000.9p lib9p/idl/2005-9P2000.u.9p lib9p/idl/2012-9P2000.e.9p`. DO NOT EDIT! */ +/* Generated by `lib9p/proto.gen lib9p/idl/2002-9P2000.9p lib9p/idl/2003-9P2000.p9p.9p lib9p/idl/2005-9P2000.u.9p lib9p/idl/2010-9P2000.L.9p lib9p/idl/2012-9P2000.e.9p`. DO NOT EDIT! */ -#include <assert.h> #include <stdbool.h> #include <stddef.h> /* for size_t */ #include <inttypes.h> /* for PRI* macros */ #include <string.h> /* for memset() */ +#include <libmisc/assert.h> +#include <libmisc/endian.h> + #include <lib9p/9p.h> -#include "internal.h" - -/* strings ********************************************************************/ - -static const char *version_strs[LIB9P_VER_NUM] = { - [LIB9P_VER_unknown] = "unknown", -#if defined(CONFIG_9P_ENABLE_9P2000) - [LIB9P_VER_9P2000] = "9P2000", -#endif /* defined(CONFIG_9P_ENABLE_9P2000) */ -#if defined(CONFIG_9P_ENABLE_9P2000_e) - [LIB9P_VER_9P2000_e] = "9P2000.e", -#endif /* defined(CONFIG_9P_ENABLE_9P2000_e) */ -#if defined(CONFIG_9P_ENABLE_9P2000_u) - [LIB9P_VER_9P2000_u] = "9P2000.u", -#endif /* defined(CONFIG_9P_ENABLE_9P2000_u) */ +#include "tables.h" +#include "utf8.h" + +/* libobj vtables *************************************************************/ +#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u +LO_IMPLEMENTATION_C(fmt_formatter, lib9p_tag_t, lib9p_tag, static); +LO_IMPLEMENTATION_C(fmt_formatter, lib9p_fid_t, lib9p_fid, static); +LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_s, lib9p_s, static); +#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */ +#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u +LO_IMPLEMENTATION_C(fmt_formatter, lib9p_dm_t, lib9p_dm, static); +#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */ +#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u +LO_IMPLEMENTATION_C(fmt_formatter, lib9p_qt_t, lib9p_qt, static); +LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_qid, lib9p_qid, static); +#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */ +#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u +LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_stat, lib9p_stat, static); +LO_IMPLEMENTATION_C(fmt_formatter, lib9p_o_t, lib9p_o, static); +#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */ +#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u +LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Tversion, lib9p_msg_Tversion, static); +LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Rversion, lib9p_msg_Rversion, static); +LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Tauth, lib9p_msg_Tauth, static); +LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Rauth, lib9p_msg_Rauth, static); +LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Tattach, lib9p_msg_Tattach, static); +LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Rattach, lib9p_msg_Rattach, static); +LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Rerror, lib9p_msg_Rerror, static); +LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Tflush, lib9p_msg_Tflush, static); +LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Rflush, lib9p_msg_Rflush, static); +LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Twalk, lib9p_msg_Twalk, static); +LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Rwalk, lib9p_msg_Rwalk, static); +#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */ +#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u +LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Topen, lib9p_msg_Topen, static); +LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Ropen, lib9p_msg_Ropen, static); +LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Tcreate, lib9p_msg_Tcreate, static); +LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Rcreate, lib9p_msg_Rcreate, static); +#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */ +#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u +LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Tread, lib9p_msg_Tread, static); +LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Rread, lib9p_msg_Rread, static); +LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Twrite, lib9p_msg_Twrite, static); +LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Rwrite, lib9p_msg_Rwrite, static); +LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Tclunk, lib9p_msg_Tclunk, static); +LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Rclunk, lib9p_msg_Rclunk, static); +LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Tremove, lib9p_msg_Tremove, static); +LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Rremove, lib9p_msg_Rremove, static); +#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */ +#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u +LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Tstat, lib9p_msg_Tstat, static); +LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Rstat, lib9p_msg_Rstat, static); +LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Twstat, lib9p_msg_Twstat, static); +LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Rwstat, lib9p_msg_Rwstat, static); +#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */ +#if CONFIG_9P_ENABLE_9P2000_p9p +LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Topenfd, lib9p_msg_Topenfd, static); +LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Ropenfd, lib9p_msg_Ropenfd, static); +#endif /* CONFIG_9P_ENABLE_9P2000_p9p */ +#if CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_u +LO_IMPLEMENTATION_C(fmt_formatter, lib9p_nuid_t, lib9p_nuid, static); +LO_IMPLEMENTATION_C(fmt_formatter, lib9p_errno_t, lib9p_errno, static); +#endif /* CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_u */ +#if CONFIG_9P_ENABLE_9P2000_L +LO_IMPLEMENTATION_C(fmt_formatter, lib9p_super_magic_t, lib9p_super_magic, static); +LO_IMPLEMENTATION_C(fmt_formatter, lib9p_lo_t, lib9p_lo, static); +LO_IMPLEMENTATION_C(fmt_formatter, lib9p_dt_t, lib9p_dt, static); +LO_IMPLEMENTATION_C(fmt_formatter, lib9p_mode_t, lib9p_mode, static); +LO_IMPLEMENTATION_C(fmt_formatter, lib9p_b4_t, lib9p_b4, static); +LO_IMPLEMENTATION_C(fmt_formatter, lib9p_getattr_t, lib9p_getattr, static); +LO_IMPLEMENTATION_C(fmt_formatter, lib9p_setattr_t, lib9p_setattr, static); +LO_IMPLEMENTATION_C(fmt_formatter, lib9p_lock_type_t, lib9p_lock_type, static); +LO_IMPLEMENTATION_C(fmt_formatter, lib9p_lock_flags_t, lib9p_lock_flags, static); +LO_IMPLEMENTATION_C(fmt_formatter, lib9p_lock_status_t, lib9p_lock_status, static); +LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Rlerror, lib9p_msg_Rlerror, static); +LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Tstatfs, lib9p_msg_Tstatfs, static); +LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Rstatfs, lib9p_msg_Rstatfs, static); +LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Tlopen, lib9p_msg_Tlopen, static); +LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Rlopen, lib9p_msg_Rlopen, static); +LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Tlcreate, lib9p_msg_Tlcreate, static); +LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Rlcreate, lib9p_msg_Rlcreate, static); +LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Tsymlink, lib9p_msg_Tsymlink, static); +LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Rsymlink, lib9p_msg_Rsymlink, static); +LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Tmknod, lib9p_msg_Tmknod, static); +LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Rmknod, lib9p_msg_Rmknod, static); +LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Trename, lib9p_msg_Trename, static); +LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Rrename, lib9p_msg_Rrename, static); +LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Treadlink, lib9p_msg_Treadlink, static); +LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Rreadlink, lib9p_msg_Rreadlink, static); +LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Tgetattr, lib9p_msg_Tgetattr, static); +LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Rgetattr, lib9p_msg_Rgetattr, static); +LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Tsetattr, lib9p_msg_Tsetattr, static); +LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Rsetattr, lib9p_msg_Rsetattr, static); +LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Txattrwalk, lib9p_msg_Txattrwalk, static); +LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Rxattrwalk, lib9p_msg_Rxattrwalk, static); +LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Txattrcreate, lib9p_msg_Txattrcreate, static); +LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Rxattrcreate, lib9p_msg_Rxattrcreate, static); +LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Treaddir, lib9p_msg_Treaddir, static); +LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Rreaddir, lib9p_msg_Rreaddir, static); +LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Tfsync, lib9p_msg_Tfsync, static); +LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Rfsync, lib9p_msg_Rfsync, static); +LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Tlock, lib9p_msg_Tlock, static); +LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Rlock, lib9p_msg_Rlock, static); +LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Tgetlock, lib9p_msg_Tgetlock, static); +LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Rgetlock, lib9p_msg_Rgetlock, static); +LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Tlink, lib9p_msg_Tlink, static); +LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Rlink, lib9p_msg_Rlink, static); +LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Tmkdir, lib9p_msg_Tmkdir, static); +LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Rmkdir, lib9p_msg_Rmkdir, static); +LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Trenameat, lib9p_msg_Trenameat, static); +LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Rrenameat, lib9p_msg_Rrenameat, static); +LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Tunlinkat, lib9p_msg_Tunlinkat, static); +LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Runlinkat, lib9p_msg_Runlinkat, static); +#endif /* CONFIG_9P_ENABLE_9P2000_L */ +#if CONFIG_9P_ENABLE_9P2000_e +LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Tsession, lib9p_msg_Tsession, static); +LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Rsession, lib9p_msg_Rsession, static); +LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Tsread, lib9p_msg_Tsread, static); +LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Rsread, lib9p_msg_Rsread, static); +LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Tswrite, lib9p_msg_Tswrite, static); +LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Rswrite, lib9p_msg_Rswrite, static); +#endif /* CONFIG_9P_ENABLE_9P2000_e */ + +/* utilities ******************************************************************/ +#if CONFIG_9P_ENABLE_9P2000 + #define _is_ver_9P2000(v) (v == LIB9P_VER_9P2000) +#else + #define _is_ver_9P2000(v) false +#endif +#if CONFIG_9P_ENABLE_9P2000_L + #define _is_ver_9P2000_L(v) (v == LIB9P_VER_9P2000_L) +#else + #define _is_ver_9P2000_L(v) false +#endif +#if CONFIG_9P_ENABLE_9P2000_e + #define _is_ver_9P2000_e(v) (v == LIB9P_VER_9P2000_e) +#else + #define _is_ver_9P2000_e(v) false +#endif +#if CONFIG_9P_ENABLE_9P2000_p9p + #define _is_ver_9P2000_p9p(v) (v == LIB9P_VER_9P2000_p9p) +#else + #define _is_ver_9P2000_p9p(v) false +#endif +#if CONFIG_9P_ENABLE_9P2000_u + #define _is_ver_9P2000_u(v) (v == LIB9P_VER_9P2000_u) +#else + #define _is_ver_9P2000_u(v) false +#endif + +/** + * is_ver(ctx, ver) is essentially `(ctx->version == LIB9P_VER_##ver)`, but + * compiles correctly (to `false`) even if `LIB9P_VER_##ver` isn't defined + * (because `!CONFIG_9P_ENABLE_##ver`). This is useful when `||`ing + * several version checks together. + */ +#define is_ver(ctx, ver) _is_ver_##ver((ctx)->version) + +/* bitmasks *******************************************************************/ + +#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u +static const lib9p_dm_t dm_masks[LIB9P_VER_NUM] = { +#if CONFIG_9P_ENABLE_9P2000 + [LIB9P_VER_9P2000] = 0b11111100000000000000000111111111, +#endif /* CONFIG_9P_ENABLE_9P2000 */ +#if CONFIG_9P_ENABLE_9P2000_L + [LIB9P_VER_9P2000_L] = 0b00000000000000000000000000000000, +#endif /* CONFIG_9P_ENABLE_9P2000_L */ +#if CONFIG_9P_ENABLE_9P2000_e + [LIB9P_VER_9P2000_e] = 0b11111100000000000000000111111111, +#endif /* CONFIG_9P_ENABLE_9P2000_e */ +#if CONFIG_9P_ENABLE_9P2000_p9p + [LIB9P_VER_9P2000_p9p] = 0b11111100000000000000000111111111, +#endif /* CONFIG_9P_ENABLE_9P2000_p9p */ +#if CONFIG_9P_ENABLE_9P2000_u + [LIB9P_VER_9P2000_u] = 0b11111100101111000000000111111111, +#endif /* CONFIG_9P_ENABLE_9P2000_u */ }; -const char *lib9p_version_str(enum lib9p_version ver) { - assert(0 <= ver && ver < LIB9P_VER_NUM); - return version_strs[ver]; -} +#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */ +#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u +static const lib9p_qt_t qt_masks[LIB9P_VER_NUM] = { +#if CONFIG_9P_ENABLE_9P2000 + [LIB9P_VER_9P2000] = 0b11111100, +#endif /* CONFIG_9P_ENABLE_9P2000 */ +#if CONFIG_9P_ENABLE_9P2000_L + [LIB9P_VER_9P2000_L] = 0b11111100, +#endif /* CONFIG_9P_ENABLE_9P2000_L */ +#if CONFIG_9P_ENABLE_9P2000_e + [LIB9P_VER_9P2000_e] = 0b11111100, +#endif /* CONFIG_9P_ENABLE_9P2000_e */ +#if CONFIG_9P_ENABLE_9P2000_p9p + [LIB9P_VER_9P2000_p9p] = 0b11111100, +#endif /* CONFIG_9P_ENABLE_9P2000_p9p */ +#if CONFIG_9P_ENABLE_9P2000_u + [LIB9P_VER_9P2000_u] = 0b11111110, +#endif /* CONFIG_9P_ENABLE_9P2000_u */ +}; + +#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */ +#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u +static const lib9p_o_t o_masks[LIB9P_VER_NUM] = { +#if CONFIG_9P_ENABLE_9P2000 + [LIB9P_VER_9P2000] = 0b01010011, +#endif /* CONFIG_9P_ENABLE_9P2000 */ +#if CONFIG_9P_ENABLE_9P2000_L + [LIB9P_VER_9P2000_L] = 0b00000000, +#endif /* CONFIG_9P_ENABLE_9P2000_L */ +#if CONFIG_9P_ENABLE_9P2000_e + [LIB9P_VER_9P2000_e] = 0b01010011, +#endif /* CONFIG_9P_ENABLE_9P2000_e */ +#if CONFIG_9P_ENABLE_9P2000_p9p + [LIB9P_VER_9P2000_p9p] = 0b01010011, +#endif /* CONFIG_9P_ENABLE_9P2000_p9p */ +#if CONFIG_9P_ENABLE_9P2000_u + [LIB9P_VER_9P2000_u] = 0b01010011, +#endif /* CONFIG_9P_ENABLE_9P2000_u */ +}; + +#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */ +#if CONFIG_9P_ENABLE_9P2000_L +static const lib9p_lo_t lo_masks[LIB9P_VER_NUM] = { +#if CONFIG_9P_ENABLE_9P2000 + [LIB9P_VER_9P2000] = 0b00000000000000000000000000000000, +#endif /* CONFIG_9P_ENABLE_9P2000 */ + [LIB9P_VER_9P2000_L] = 0b00000000000111111111111111000011, +#if CONFIG_9P_ENABLE_9P2000_e + [LIB9P_VER_9P2000_e] = 0b00000000000000000000000000000000, +#endif /* CONFIG_9P_ENABLE_9P2000_e */ +#if CONFIG_9P_ENABLE_9P2000_p9p + [LIB9P_VER_9P2000_p9p] = 0b00000000000000000000000000000000, +#endif /* CONFIG_9P_ENABLE_9P2000_p9p */ +#if CONFIG_9P_ENABLE_9P2000_u + [LIB9P_VER_9P2000_u] = 0b00000000000000000000000000000000, +#endif /* CONFIG_9P_ENABLE_9P2000_u */ +}; + +static const lib9p_mode_t mode_masks[LIB9P_VER_NUM] = { +#if CONFIG_9P_ENABLE_9P2000 + [LIB9P_VER_9P2000] = 0b00000000000000000000000000000000, +#endif /* CONFIG_9P_ENABLE_9P2000 */ + [LIB9P_VER_9P2000_L] = 0b00000000000000001111111111111111, +#if CONFIG_9P_ENABLE_9P2000_e + [LIB9P_VER_9P2000_e] = 0b00000000000000000000000000000000, +#endif /* CONFIG_9P_ENABLE_9P2000_e */ +#if CONFIG_9P_ENABLE_9P2000_p9p + [LIB9P_VER_9P2000_p9p] = 0b00000000000000000000000000000000, +#endif /* CONFIG_9P_ENABLE_9P2000_p9p */ +#if CONFIG_9P_ENABLE_9P2000_u + [LIB9P_VER_9P2000_u] = 0b00000000000000000000000000000000, +#endif /* CONFIG_9P_ENABLE_9P2000_u */ +}; + +static const lib9p_getattr_t getattr_masks[LIB9P_VER_NUM] = { +#if CONFIG_9P_ENABLE_9P2000 + [LIB9P_VER_9P2000] = 0b0000000000000000000000000000000000000000000000000000000000000000, +#endif /* CONFIG_9P_ENABLE_9P2000 */ + [LIB9P_VER_9P2000_L] = 0b0000000000000000000000000000000000000000000000000011111111111111, +#if CONFIG_9P_ENABLE_9P2000_e + [LIB9P_VER_9P2000_e] = 0b0000000000000000000000000000000000000000000000000000000000000000, +#endif /* CONFIG_9P_ENABLE_9P2000_e */ +#if CONFIG_9P_ENABLE_9P2000_p9p + [LIB9P_VER_9P2000_p9p] = 0b0000000000000000000000000000000000000000000000000000000000000000, +#endif /* CONFIG_9P_ENABLE_9P2000_p9p */ +#if CONFIG_9P_ENABLE_9P2000_u + [LIB9P_VER_9P2000_u] = 0b0000000000000000000000000000000000000000000000000000000000000000, +#endif /* CONFIG_9P_ENABLE_9P2000_u */ +}; + +static const lib9p_setattr_t setattr_masks[LIB9P_VER_NUM] = { +#if CONFIG_9P_ENABLE_9P2000 + [LIB9P_VER_9P2000] = 0b00000000000000000000000000000000, +#endif /* CONFIG_9P_ENABLE_9P2000 */ + [LIB9P_VER_9P2000_L] = 0b00000000000000000000000111111111, +#if CONFIG_9P_ENABLE_9P2000_e + [LIB9P_VER_9P2000_e] = 0b00000000000000000000000000000000, +#endif /* CONFIG_9P_ENABLE_9P2000_e */ +#if CONFIG_9P_ENABLE_9P2000_p9p + [LIB9P_VER_9P2000_p9p] = 0b00000000000000000000000000000000, +#endif /* CONFIG_9P_ENABLE_9P2000_p9p */ +#if CONFIG_9P_ENABLE_9P2000_u + [LIB9P_VER_9P2000_u] = 0b00000000000000000000000000000000, +#endif /* CONFIG_9P_ENABLE_9P2000_u */ +}; + +static const lib9p_lock_flags_t lock_flags_masks[LIB9P_VER_NUM] = { +#if CONFIG_9P_ENABLE_9P2000 + [LIB9P_VER_9P2000] = 0b00000000000000000000000000000000, +#endif /* CONFIG_9P_ENABLE_9P2000 */ + [LIB9P_VER_9P2000_L] = 0b00000000000000000000000000000011, +#if CONFIG_9P_ENABLE_9P2000_e + [LIB9P_VER_9P2000_e] = 0b00000000000000000000000000000000, +#endif /* CONFIG_9P_ENABLE_9P2000_e */ +#if CONFIG_9P_ENABLE_9P2000_p9p + [LIB9P_VER_9P2000_p9p] = 0b00000000000000000000000000000000, +#endif /* CONFIG_9P_ENABLE_9P2000_p9p */ +#if CONFIG_9P_ENABLE_9P2000_u + [LIB9P_VER_9P2000_u] = 0b00000000000000000000000000000000, +#endif /* CONFIG_9P_ENABLE_9P2000_u */ +}; +#endif /* CONFIG_9P_ENABLE_9P2000_L */ /* validate_* *****************************************************************/ -static ALWAYS_INLINE bool _validate_size_net(struct _validate_ctx *ctx, uint32_t n) { - if (__builtin_add_overflow(ctx->net_offset, n, &ctx->net_offset)) - /* If needed-net-size overflowed uint32_t, then - * there's no way that actual-net-size will live up to - * that. */ - return lib9p_error(ctx->ctx, LINUX_EBADMSG, "message is too short for content"); - if (ctx->net_offset > ctx->net_size) - return lib9p_error(ctx->ctx, LINUX_EBADMSG, "message is too short for content"); +#define VALIDATE_NET_BYTES(n) \ + if (__builtin_add_overflow(net_offset, n, &net_offset)) \ + /* If needed-net-size overflowed uint32_t, then \ + * there's no way that actual-net-size will live up to \ + * that. */ \ + return lib9p_error(ctx, LINUX_EBADMSG, "message is too short for content"); \ + if (net_offset > net_size) \ + return lib9p_errorf(ctx, LINUX_EBADMSG, "message is too short for content (%"PRIu32" > %"PRIu32") @ %d", net_offset, net_size, __LINE__); +#define VALIDATE_NET_UTF8(n) \ + { \ + size_t len = n; \ + VALIDATE_NET_BYTES(len); \ + if (!is_valid_utf8_without_nul(&net_bytes[net_offset-len], len)) \ + return lib9p_error(ctx, LINUX_EBADMSG, "message contains invalid UTF-8"); \ + } +#define RESERVE_HOST_BYTES(n) \ + if (__builtin_add_overflow(host_size, n, &host_size)) \ + /* If needed-host-size overflowed ssize_t, then there's \ + * no way that actual-net-size will live up to \ + * that. */ \ + return lib9p_error(ctx, LINUX_EBADMSG, "message is too short for content"); +#define GET_U8LE(off) (net_bytes[off]) +#define GET_U16LE(off) uint16le_decode(&net_bytes[off]) +#define GET_U32LE(off) uint32le_decode(&net_bytes[off]) +#define GET_U64LE(off) uint64le_decode(&net_bytes[off]) +#define LAST_U8LE() GET_U8LE(net_offset-1) +#define LAST_U16LE() GET_U16LE(net_offset-2) +#define LAST_U32LE() GET_U32LE(net_offset-4) +#define LAST_U64LE() GET_U64LE(net_offset-8) + +#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u +static ssize_t validate_stat(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes, uint32_t *ret_net_size) { + uint32_t net_offset = 0; + ssize_t host_size = sizeof(struct lib9p_stat); + uint32_t offsetof_stat_size = net_offset + 0; + uint32_t offsetof_kern_type = net_offset + 2; + uint32_t offsetof_file_qid_type = net_offset + 8; + VALIDATE_NET_BYTES(21); + if (GET_U8LE(offsetof_file_qid_type) & ~qt_masks[ctx->version]) + return lib9p_errorf(ctx, LINUX_EBADMSG, "unknown bits in qt bitfield: %#02"PRIx8, + GET_U8LE(offsetof_file_qid_type) & ~qt_masks[ctx->version]); + uint32_t offsetof_file_mode = net_offset + 0; + VALIDATE_NET_BYTES(22); + VALIDATE_NET_UTF8(LAST_U16LE()); + VALIDATE_NET_BYTES(2); + VALIDATE_NET_UTF8(LAST_U16LE()); + VALIDATE_NET_BYTES(2); + VALIDATE_NET_UTF8(LAST_U16LE()); + VALIDATE_NET_BYTES(2); + VALIDATE_NET_UTF8(LAST_U16LE()); +#if CONFIG_9P_ENABLE_9P2000_u + if (is_ver(ctx, 9P2000_u)) { + VALIDATE_NET_BYTES(2); + VALIDATE_NET_UTF8(LAST_U16LE()); + VALIDATE_NET_BYTES(12); + } +#endif /* CONFIG_9P_ENABLE_9P2000_u */ + uint32_t offsetof_end = net_offset + 0; + if ((uint32_t)GET_U32LE(offsetof_stat_size) != (uint32_t)(offsetof_end - offsetof_kern_type)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "stat->stat_size value is wrong: actual: %"PRIu32" != correct:%"PRIu32, + (uint32_t)GET_U32LE(offsetof_stat_size), (uint32_t)(offsetof_end - offsetof_kern_type)); + if (GET_U32LE(offsetof_file_mode) & ~dm_masks[ctx->version]) + return lib9p_errorf(ctx, LINUX_EBADMSG, "unknown bits in dm bitfield: %#08"PRIx32, + GET_U32LE(offsetof_file_mode) & ~dm_masks[ctx->version]); + if (ret_net_size) + *ret_net_size = net_offset; + return (ssize_t)host_size; +} + +#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */ +#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u +static ssize_t validate_Tversion(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { + uint32_t net_offset = 0; + ssize_t host_size = sizeof(struct lib9p_msg_Tversion); + uint32_t offsetof_size = net_offset + 0; + uint32_t offsetof_typ = net_offset + 4; + VALIDATE_NET_BYTES(13); + VALIDATE_NET_UTF8(LAST_U16LE()); + uint32_t offsetof_end = net_offset + 0; + if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Tversion->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32, + (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size)); + if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(100)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Tversion->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, + (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(100)); + return (ssize_t)host_size; +} + +static ssize_t validate_Rversion(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { + uint32_t net_offset = 0; + ssize_t host_size = sizeof(struct lib9p_msg_Rversion); + uint32_t offsetof_size = net_offset + 0; + uint32_t offsetof_typ = net_offset + 4; + VALIDATE_NET_BYTES(13); + VALIDATE_NET_UTF8(LAST_U16LE()); + uint32_t offsetof_end = net_offset + 0; + if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Rversion->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32, + (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size)); + if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(101)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Rversion->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, + (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(101)); + return (ssize_t)host_size; +} + +static ssize_t validate_Tauth(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { + uint32_t net_offset = 0; + ssize_t host_size = sizeof(struct lib9p_msg_Tauth); + uint32_t offsetof_size = net_offset + 0; + uint32_t offsetof_typ = net_offset + 4; + VALIDATE_NET_BYTES(13); + VALIDATE_NET_UTF8(LAST_U16LE()); + VALIDATE_NET_BYTES(2); + VALIDATE_NET_UTF8(LAST_U16LE()); +#if CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_u + if (( is_ver(ctx, 9P2000_L) || is_ver(ctx, 9P2000_u) )) { + VALIDATE_NET_BYTES(4); + } +#endif /* CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_u */ + uint32_t offsetof_end = net_offset + 0; + if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Tauth->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32, + (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size)); + if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(102)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Tauth->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, + (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(102)); + return (ssize_t)host_size; +} + +static ssize_t validate_Rauth(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { + uint32_t net_offset = 0; + ssize_t host_size = sizeof(struct lib9p_msg_Rauth); + uint32_t offsetof_size = net_offset + 0; + uint32_t offsetof_typ = net_offset + 4; + uint32_t offsetof_aqid_type = net_offset + 7; + VALIDATE_NET_BYTES(20); + if (GET_U8LE(offsetof_aqid_type) & ~qt_masks[ctx->version]) + return lib9p_errorf(ctx, LINUX_EBADMSG, "unknown bits in qt bitfield: %#02"PRIx8, + GET_U8LE(offsetof_aqid_type) & ~qt_masks[ctx->version]); + uint32_t offsetof_end = net_offset + 0; + if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Rauth->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32, + (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size)); + if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(103)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Rauth->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, + (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(103)); + return (ssize_t)host_size; +} + +static ssize_t validate_Tattach(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { + uint32_t net_offset = 0; + ssize_t host_size = sizeof(struct lib9p_msg_Tattach); + uint32_t offsetof_size = net_offset + 0; + uint32_t offsetof_typ = net_offset + 4; + VALIDATE_NET_BYTES(17); + VALIDATE_NET_UTF8(LAST_U16LE()); + VALIDATE_NET_BYTES(2); + VALIDATE_NET_UTF8(LAST_U16LE()); +#if CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_u + if (( is_ver(ctx, 9P2000_L) || is_ver(ctx, 9P2000_u) )) { + VALIDATE_NET_BYTES(4); + } +#endif /* CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_u */ + uint32_t offsetof_end = net_offset + 0; + if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Tattach->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32, + (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size)); + if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(104)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Tattach->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, + (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(104)); + return (ssize_t)host_size; +} + +static ssize_t validate_Rattach(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { + uint32_t net_offset = 0; + ssize_t host_size = sizeof(struct lib9p_msg_Rattach); + uint32_t offsetof_size = net_offset + 0; + uint32_t offsetof_typ = net_offset + 4; + uint32_t offsetof_qid_type = net_offset + 7; + VALIDATE_NET_BYTES(20); + if (GET_U8LE(offsetof_qid_type) & ~qt_masks[ctx->version]) + return lib9p_errorf(ctx, LINUX_EBADMSG, "unknown bits in qt bitfield: %#02"PRIx8, + GET_U8LE(offsetof_qid_type) & ~qt_masks[ctx->version]); + uint32_t offsetof_end = net_offset + 0; + if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Rattach->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32, + (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size)); + if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(105)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Rattach->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, + (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(105)); + return (ssize_t)host_size; +} + +static ssize_t validate_Rerror(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { + uint32_t net_offset = 0; + ssize_t host_size = sizeof(struct lib9p_msg_Rerror); + uint32_t offsetof_size = net_offset + 0; + uint32_t offsetof_typ = net_offset + 4; + VALIDATE_NET_BYTES(9); + VALIDATE_NET_UTF8(LAST_U16LE()); +#if CONFIG_9P_ENABLE_9P2000_u + if (is_ver(ctx, 9P2000_u)) { + VALIDATE_NET_BYTES(4); + } +#endif /* CONFIG_9P_ENABLE_9P2000_u */ + uint32_t offsetof_end = net_offset + 0; + if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Rerror->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32, + (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size)); + if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(107)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Rerror->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, + (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(107)); + return (ssize_t)host_size; +} + +static ssize_t validate_Tflush(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { + uint32_t net_offset = 0; + ssize_t host_size = sizeof(struct lib9p_msg_Tflush); + uint32_t offsetof_size = net_offset + 0; + uint32_t offsetof_typ = net_offset + 4; + uint32_t offsetof_end = net_offset + 9; + VALIDATE_NET_BYTES(9); + if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Tflush->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32, + (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size)); + if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(108)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Tflush->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, + (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(108)); + return (ssize_t)host_size; +} + +static ssize_t validate_Rflush(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { + uint32_t net_offset = 0; + ssize_t host_size = sizeof(struct lib9p_msg_Rflush); + uint32_t offsetof_size = net_offset + 0; + uint32_t offsetof_typ = net_offset + 4; + uint32_t offsetof_end = net_offset + 7; + VALIDATE_NET_BYTES(7); + if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Rflush->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32, + (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size)); + if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(109)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Rflush->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, + (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(109)); + return (ssize_t)host_size; +} + +static ssize_t validate_Twalk(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { + uint32_t net_offset = 0; + ssize_t host_size = sizeof(struct lib9p_msg_Twalk); + uint32_t offsetof_size = net_offset + 0; + uint32_t offsetof_typ = net_offset + 4; + uint32_t offsetof_nwname = net_offset + 15; + VALIDATE_NET_BYTES(17); + for (uint16_t i = 0, cnt = LAST_U16LE(); i < cnt; i++) { + RESERVE_HOST_BYTES(sizeof(struct lib9p_s)); + VALIDATE_NET_BYTES(2); + VALIDATE_NET_UTF8(LAST_U16LE()); + } + uint32_t offsetof_end = net_offset + 0; + if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Twalk->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32, + (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size)); + if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(110)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Twalk->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, + (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(110)); + if ((uint16_t)GET_U16LE(offsetof_nwname) > (uint16_t)(16)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Twalk->nwname value is too large: %"PRIu16" > %"PRIu16, + (uint16_t)GET_U16LE(offsetof_nwname), (uint16_t)(16)); + return (ssize_t)host_size; +} + +static ssize_t validate_Rwalk(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { + uint32_t net_offset = 0; + ssize_t host_size = sizeof(struct lib9p_msg_Rwalk); + uint32_t offsetof_size = net_offset + 0; + uint32_t offsetof_typ = net_offset + 4; + uint32_t offsetof_nwqid = net_offset + 7; + VALIDATE_NET_BYTES(9); + for (uint16_t i = 0, cnt = LAST_U16LE(); i < cnt; i++) { + RESERVE_HOST_BYTES(sizeof(struct lib9p_qid)); + uint32_t offsetof_wqid_type = net_offset + 0; + VALIDATE_NET_BYTES(13); + if (GET_U8LE(offsetof_wqid_type) & ~qt_masks[ctx->version]) + return lib9p_errorf(ctx, LINUX_EBADMSG, "unknown bits in qt bitfield: %#02"PRIx8, + GET_U8LE(offsetof_wqid_type) & ~qt_masks[ctx->version]); + } + uint32_t offsetof_end = net_offset + 0; + if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Rwalk->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32, + (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size)); + if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(111)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Rwalk->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, + (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(111)); + if ((uint16_t)GET_U16LE(offsetof_nwqid) > (uint16_t)(16)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Rwalk->nwqid value is too large: %"PRIu16" > %"PRIu16, + (uint16_t)GET_U16LE(offsetof_nwqid), (uint16_t)(16)); + return (ssize_t)host_size; +} + +#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */ +#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u +static ssize_t validate_Topen(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { + uint32_t net_offset = 0; + ssize_t host_size = sizeof(struct lib9p_msg_Topen); + uint32_t offsetof_size = net_offset + 0; + uint32_t offsetof_typ = net_offset + 4; + uint32_t offsetof_mode = net_offset + 11; + uint32_t offsetof_end = net_offset + 12; + VALIDATE_NET_BYTES(12); + if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Topen->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32, + (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size)); + if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(112)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Topen->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, + (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(112)); + if (GET_U8LE(offsetof_mode) & ~o_masks[ctx->version]) + return lib9p_errorf(ctx, LINUX_EBADMSG, "unknown bits in o bitfield: %#02"PRIx8, + GET_U8LE(offsetof_mode) & ~o_masks[ctx->version]); + return (ssize_t)host_size; +} + +static ssize_t validate_Ropen(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { + uint32_t net_offset = 0; + ssize_t host_size = sizeof(struct lib9p_msg_Ropen); + uint32_t offsetof_size = net_offset + 0; + uint32_t offsetof_typ = net_offset + 4; + uint32_t offsetof_qid_type = net_offset + 7; + VALIDATE_NET_BYTES(20); + if (GET_U8LE(offsetof_qid_type) & ~qt_masks[ctx->version]) + return lib9p_errorf(ctx, LINUX_EBADMSG, "unknown bits in qt bitfield: %#02"PRIx8, + GET_U8LE(offsetof_qid_type) & ~qt_masks[ctx->version]); + uint32_t offsetof_end = net_offset + 4; + VALIDATE_NET_BYTES(4); + if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Ropen->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32, + (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size)); + if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(113)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Ropen->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, + (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(113)); + return (ssize_t)host_size; +} + +static ssize_t validate_Tcreate(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { + uint32_t net_offset = 0; + ssize_t host_size = sizeof(struct lib9p_msg_Tcreate); + uint32_t offsetof_size = net_offset + 0; + uint32_t offsetof_typ = net_offset + 4; + VALIDATE_NET_BYTES(13); + VALIDATE_NET_UTF8(LAST_U16LE()); + uint32_t offsetof_perm = net_offset + 0; + uint32_t offsetof_mode = net_offset + 4; + uint32_t offsetof_end = net_offset + 5; + VALIDATE_NET_BYTES(5); + if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Tcreate->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32, + (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size)); + if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(114)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Tcreate->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, + (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(114)); + if (GET_U32LE(offsetof_perm) & ~dm_masks[ctx->version]) + return lib9p_errorf(ctx, LINUX_EBADMSG, "unknown bits in dm bitfield: %#08"PRIx32, + GET_U32LE(offsetof_perm) & ~dm_masks[ctx->version]); + if (GET_U8LE(offsetof_mode) & ~o_masks[ctx->version]) + return lib9p_errorf(ctx, LINUX_EBADMSG, "unknown bits in o bitfield: %#02"PRIx8, + GET_U8LE(offsetof_mode) & ~o_masks[ctx->version]); + return (ssize_t)host_size; +} + +static ssize_t validate_Rcreate(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { + uint32_t net_offset = 0; + ssize_t host_size = sizeof(struct lib9p_msg_Rcreate); + uint32_t offsetof_size = net_offset + 0; + uint32_t offsetof_typ = net_offset + 4; + uint32_t offsetof_qid_type = net_offset + 7; + VALIDATE_NET_BYTES(20); + if (GET_U8LE(offsetof_qid_type) & ~qt_masks[ctx->version]) + return lib9p_errorf(ctx, LINUX_EBADMSG, "unknown bits in qt bitfield: %#02"PRIx8, + GET_U8LE(offsetof_qid_type) & ~qt_masks[ctx->version]); + uint32_t offsetof_end = net_offset + 4; + VALIDATE_NET_BYTES(4); + if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Rcreate->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32, + (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size)); + if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(115)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Rcreate->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, + (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(115)); + return (ssize_t)host_size; +} + +#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */ +#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u +static ssize_t validate_Tread(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { + uint32_t net_offset = 0; + ssize_t host_size = sizeof(struct lib9p_msg_Tread); + uint32_t offsetof_size = net_offset + 0; + uint32_t offsetof_typ = net_offset + 4; + uint32_t offsetof_offset = net_offset + 11; + uint32_t offsetof_count = net_offset + 19; + uint32_t offsetof_end = net_offset + 23; + VALIDATE_NET_BYTES(23); + if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Tread->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32, + (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size)); + if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(116)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Tread->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, + (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(116)); + if ((uint64_t)GET_U64LE(offsetof_offset) > (uint64_t)(INT64_MAX)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Tread->offset value is too large: %"PRIu64" > %"PRIu64, + (uint64_t)GET_U64LE(offsetof_offset), (uint64_t)(INT64_MAX)); + if ((uint32_t)GET_U32LE(offsetof_count) > (uint32_t)(INT32_MAX)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Tread->count value is too large: %"PRIu32" > %"PRIu32, + (uint32_t)GET_U32LE(offsetof_count), (uint32_t)(INT32_MAX)); + return (ssize_t)host_size; +} + +static ssize_t validate_Rread(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { + uint32_t net_offset = 0; + ssize_t host_size = sizeof(struct lib9p_msg_Rread); + uint32_t offsetof_size = net_offset + 0; + uint32_t offsetof_typ = net_offset + 4; + uint32_t offsetof_count = net_offset + 7; + VALIDATE_NET_BYTES(11); + VALIDATE_NET_BYTES(LAST_U32LE()); + uint32_t offsetof_end = net_offset + 0; + if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Rread->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32, + (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size)); + if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(117)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Rread->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, + (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(117)); + if ((uint32_t)GET_U32LE(offsetof_count) > (uint32_t)(INT32_MAX)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Rread->count value is too large: %"PRIu32" > %"PRIu32, + (uint32_t)GET_U32LE(offsetof_count), (uint32_t)(INT32_MAX)); + return (ssize_t)host_size; +} + +static ssize_t validate_Twrite(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { + uint32_t net_offset = 0; + ssize_t host_size = sizeof(struct lib9p_msg_Twrite); + uint32_t offsetof_size = net_offset + 0; + uint32_t offsetof_typ = net_offset + 4; + uint32_t offsetof_offset = net_offset + 11; + uint32_t offsetof_count = net_offset + 19; + VALIDATE_NET_BYTES(23); + VALIDATE_NET_BYTES(LAST_U32LE()); + uint32_t offsetof_end = net_offset + 0; + if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Twrite->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32, + (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size)); + if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(118)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Twrite->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, + (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(118)); + if ((uint64_t)GET_U64LE(offsetof_offset) > (uint64_t)(INT64_MAX)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Twrite->offset value is too large: %"PRIu64" > %"PRIu64, + (uint64_t)GET_U64LE(offsetof_offset), (uint64_t)(INT64_MAX)); + if ((uint32_t)GET_U32LE(offsetof_count) > (uint32_t)(INT32_MAX)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Twrite->count value is too large: %"PRIu32" > %"PRIu32, + (uint32_t)GET_U32LE(offsetof_count), (uint32_t)(INT32_MAX)); + return (ssize_t)host_size; +} + +static ssize_t validate_Rwrite(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { + uint32_t net_offset = 0; + ssize_t host_size = sizeof(struct lib9p_msg_Rwrite); + uint32_t offsetof_size = net_offset + 0; + uint32_t offsetof_typ = net_offset + 4; + uint32_t offsetof_count = net_offset + 7; + uint32_t offsetof_end = net_offset + 11; + VALIDATE_NET_BYTES(11); + if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Rwrite->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32, + (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size)); + if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(119)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Rwrite->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, + (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(119)); + if ((uint32_t)GET_U32LE(offsetof_count) > (uint32_t)(INT32_MAX)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Rwrite->count value is too large: %"PRIu32" > %"PRIu32, + (uint32_t)GET_U32LE(offsetof_count), (uint32_t)(INT32_MAX)); + return (ssize_t)host_size; +} + +static ssize_t validate_Tclunk(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { + uint32_t net_offset = 0; + ssize_t host_size = sizeof(struct lib9p_msg_Tclunk); + uint32_t offsetof_size = net_offset + 0; + uint32_t offsetof_typ = net_offset + 4; + uint32_t offsetof_end = net_offset + 11; + VALIDATE_NET_BYTES(11); + if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Tclunk->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32, + (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size)); + if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(120)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Tclunk->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, + (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(120)); + return (ssize_t)host_size; +} + +static ssize_t validate_Rclunk(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { + uint32_t net_offset = 0; + ssize_t host_size = sizeof(struct lib9p_msg_Rclunk); + uint32_t offsetof_size = net_offset + 0; + uint32_t offsetof_typ = net_offset + 4; + uint32_t offsetof_end = net_offset + 7; + VALIDATE_NET_BYTES(7); + if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Rclunk->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32, + (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size)); + if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(121)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Rclunk->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, + (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(121)); + return (ssize_t)host_size; +} + +static ssize_t validate_Tremove(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { + uint32_t net_offset = 0; + ssize_t host_size = sizeof(struct lib9p_msg_Tremove); + uint32_t offsetof_size = net_offset + 0; + uint32_t offsetof_typ = net_offset + 4; + uint32_t offsetof_end = net_offset + 11; + VALIDATE_NET_BYTES(11); + if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Tremove->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32, + (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size)); + if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(122)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Tremove->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, + (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(122)); + return (ssize_t)host_size; +} + +static ssize_t validate_Rremove(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { + uint32_t net_offset = 0; + ssize_t host_size = sizeof(struct lib9p_msg_Rremove); + uint32_t offsetof_size = net_offset + 0; + uint32_t offsetof_typ = net_offset + 4; + uint32_t offsetof_end = net_offset + 7; + VALIDATE_NET_BYTES(7); + if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Rremove->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32, + (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size)); + if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(123)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Rremove->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, + (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(123)); + return (ssize_t)host_size; +} + +#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */ +#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u +static ssize_t validate_Tstat(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { + uint32_t net_offset = 0; + ssize_t host_size = sizeof(struct lib9p_msg_Tstat); + uint32_t offsetof_size = net_offset + 0; + uint32_t offsetof_typ = net_offset + 4; + uint32_t offsetof_end = net_offset + 11; + VALIDATE_NET_BYTES(11); + if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Tstat->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32, + (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size)); + if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(124)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Tstat->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, + (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(124)); + return (ssize_t)host_size; +} + +static ssize_t validate_Rstat(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { + uint32_t net_offset = 0; + ssize_t host_size = sizeof(struct lib9p_msg_Rstat); + uint32_t offsetof_size = net_offset + 0; + uint32_t offsetof_typ = net_offset + 4; + uint32_t offsetof_nstat = net_offset + 7; + uint32_t offsetof_stat = net_offset + 9; + uint32_t offsetof_stat_stat_size = net_offset + 9; + uint32_t offsetof_stat_kern_type = net_offset + 11; + uint32_t offsetof_stat_file_qid_type = net_offset + 17; + VALIDATE_NET_BYTES(30); + if (GET_U8LE(offsetof_stat_file_qid_type) & ~qt_masks[ctx->version]) + return lib9p_errorf(ctx, LINUX_EBADMSG, "unknown bits in qt bitfield: %#02"PRIx8, + GET_U8LE(offsetof_stat_file_qid_type) & ~qt_masks[ctx->version]); + uint32_t offsetof_stat_file_mode = net_offset + 0; + VALIDATE_NET_BYTES(22); + VALIDATE_NET_UTF8(LAST_U16LE()); + VALIDATE_NET_BYTES(2); + VALIDATE_NET_UTF8(LAST_U16LE()); + VALIDATE_NET_BYTES(2); + VALIDATE_NET_UTF8(LAST_U16LE()); + VALIDATE_NET_BYTES(2); + VALIDATE_NET_UTF8(LAST_U16LE()); +#if CONFIG_9P_ENABLE_9P2000_u + if (is_ver(ctx, 9P2000_u)) { + VALIDATE_NET_BYTES(2); + VALIDATE_NET_UTF8(LAST_U16LE()); + VALIDATE_NET_BYTES(12); + } +#endif /* CONFIG_9P_ENABLE_9P2000_u */ + uint32_t offsetof_stat_end = net_offset + 0; + if ((uint32_t)GET_U32LE(offsetof_stat_stat_size) != (uint32_t)(offsetof_stat_end - offsetof_stat_kern_type)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Rstat->stat.stat_size value is wrong: actual: %"PRIu32" != correct:%"PRIu32, + (uint32_t)GET_U32LE(offsetof_stat_stat_size), (uint32_t)(offsetof_stat_end - offsetof_stat_kern_type)); + if (GET_U32LE(offsetof_stat_file_mode) & ~dm_masks[ctx->version]) + return lib9p_errorf(ctx, LINUX_EBADMSG, "unknown bits in dm bitfield: %#08"PRIx32, + GET_U32LE(offsetof_stat_file_mode) & ~dm_masks[ctx->version]); + uint32_t offsetof_end = net_offset + 0; + if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Rstat->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32, + (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size)); + if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(125)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Rstat->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, + (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(125)); + if ((uint32_t)GET_U32LE(offsetof_nstat) != (uint32_t)(offsetof_end - offsetof_stat)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Rstat->nstat value is wrong: actual: %"PRIu32" != correct:%"PRIu32, + (uint32_t)GET_U32LE(offsetof_nstat), (uint32_t)(offsetof_end - offsetof_stat)); + return (ssize_t)host_size; +} + +static ssize_t validate_Twstat(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { + uint32_t net_offset = 0; + ssize_t host_size = sizeof(struct lib9p_msg_Twstat); + uint32_t offsetof_size = net_offset + 0; + uint32_t offsetof_typ = net_offset + 4; + uint32_t offsetof_nstat = net_offset + 11; + uint32_t offsetof_stat = net_offset + 13; + uint32_t offsetof_stat_stat_size = net_offset + 13; + uint32_t offsetof_stat_kern_type = net_offset + 15; + uint32_t offsetof_stat_file_qid_type = net_offset + 21; + VALIDATE_NET_BYTES(34); + if (GET_U8LE(offsetof_stat_file_qid_type) & ~qt_masks[ctx->version]) + return lib9p_errorf(ctx, LINUX_EBADMSG, "unknown bits in qt bitfield: %#02"PRIx8, + GET_U8LE(offsetof_stat_file_qid_type) & ~qt_masks[ctx->version]); + uint32_t offsetof_stat_file_mode = net_offset + 0; + VALIDATE_NET_BYTES(22); + VALIDATE_NET_UTF8(LAST_U16LE()); + VALIDATE_NET_BYTES(2); + VALIDATE_NET_UTF8(LAST_U16LE()); + VALIDATE_NET_BYTES(2); + VALIDATE_NET_UTF8(LAST_U16LE()); + VALIDATE_NET_BYTES(2); + VALIDATE_NET_UTF8(LAST_U16LE()); +#if CONFIG_9P_ENABLE_9P2000_u + if (is_ver(ctx, 9P2000_u)) { + VALIDATE_NET_BYTES(2); + VALIDATE_NET_UTF8(LAST_U16LE()); + VALIDATE_NET_BYTES(12); + } +#endif /* CONFIG_9P_ENABLE_9P2000_u */ + uint32_t offsetof_stat_end = net_offset + 0; + if ((uint32_t)GET_U32LE(offsetof_stat_stat_size) != (uint32_t)(offsetof_stat_end - offsetof_stat_kern_type)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Twstat->stat.stat_size value is wrong: actual: %"PRIu32" != correct:%"PRIu32, + (uint32_t)GET_U32LE(offsetof_stat_stat_size), (uint32_t)(offsetof_stat_end - offsetof_stat_kern_type)); + if (GET_U32LE(offsetof_stat_file_mode) & ~dm_masks[ctx->version]) + return lib9p_errorf(ctx, LINUX_EBADMSG, "unknown bits in dm bitfield: %#08"PRIx32, + GET_U32LE(offsetof_stat_file_mode) & ~dm_masks[ctx->version]); + uint32_t offsetof_end = net_offset + 0; + if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Twstat->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32, + (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size)); + if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(126)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Twstat->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, + (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(126)); + if ((uint32_t)GET_U32LE(offsetof_nstat) != (uint32_t)(offsetof_end - offsetof_stat)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Twstat->nstat value is wrong: actual: %"PRIu32" != correct:%"PRIu32, + (uint32_t)GET_U32LE(offsetof_nstat), (uint32_t)(offsetof_end - offsetof_stat)); + return (ssize_t)host_size; +} + +static ssize_t validate_Rwstat(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { + uint32_t net_offset = 0; + ssize_t host_size = sizeof(struct lib9p_msg_Rwstat); + uint32_t offsetof_size = net_offset + 0; + uint32_t offsetof_typ = net_offset + 4; + uint32_t offsetof_end = net_offset + 7; + VALIDATE_NET_BYTES(7); + if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Rwstat->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32, + (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size)); + if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(127)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Rwstat->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, + (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(127)); + return (ssize_t)host_size; +} + +#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */ +#if CONFIG_9P_ENABLE_9P2000_p9p +static ssize_t validate_Topenfd(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { + uint32_t net_offset = 0; + ssize_t host_size = sizeof(struct lib9p_msg_Topenfd); + uint32_t offsetof_size = net_offset + 0; + uint32_t offsetof_typ = net_offset + 4; + uint32_t offsetof_mode = net_offset + 11; + uint32_t offsetof_end = net_offset + 12; + VALIDATE_NET_BYTES(12); + if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Topenfd->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32, + (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size)); + if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(98)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Topenfd->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, + (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(98)); + if (GET_U8LE(offsetof_mode) & ~o_masks[ctx->version]) + return lib9p_errorf(ctx, LINUX_EBADMSG, "unknown bits in o bitfield: %#02"PRIx8, + GET_U8LE(offsetof_mode) & ~o_masks[ctx->version]); + return (ssize_t)host_size; +} + +static ssize_t validate_Ropenfd(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { + uint32_t net_offset = 0; + ssize_t host_size = sizeof(struct lib9p_msg_Ropenfd); + uint32_t offsetof_size = net_offset + 0; + uint32_t offsetof_typ = net_offset + 4; + uint32_t offsetof_qid_type = net_offset + 7; + VALIDATE_NET_BYTES(20); + if (GET_U8LE(offsetof_qid_type) & ~qt_masks[ctx->version]) + return lib9p_errorf(ctx, LINUX_EBADMSG, "unknown bits in qt bitfield: %#02"PRIx8, + GET_U8LE(offsetof_qid_type) & ~qt_masks[ctx->version]); + uint32_t offsetof_end = net_offset + 8; + VALIDATE_NET_BYTES(8); + if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Ropenfd->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32, + (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size)); + if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(99)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Ropenfd->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, + (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(99)); + return (ssize_t)host_size; +} + +#endif /* CONFIG_9P_ENABLE_9P2000_p9p */ +#if CONFIG_9P_ENABLE_9P2000_L +static ssize_t validate_Rlerror(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { + uint32_t net_offset = 0; + ssize_t host_size = sizeof(struct lib9p_msg_Rlerror); + uint32_t offsetof_size = net_offset + 0; + uint32_t offsetof_typ = net_offset + 4; + uint32_t offsetof_end = net_offset + 11; + VALIDATE_NET_BYTES(11); + if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Rlerror->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32, + (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size)); + if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(7)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Rlerror->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, + (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(7)); + return (ssize_t)host_size; +} + +static ssize_t validate_Tstatfs(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { + uint32_t net_offset = 0; + ssize_t host_size = sizeof(struct lib9p_msg_Tstatfs); + uint32_t offsetof_size = net_offset + 0; + uint32_t offsetof_typ = net_offset + 4; + uint32_t offsetof_end = net_offset + 11; + VALIDATE_NET_BYTES(11); + if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Tstatfs->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32, + (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size)); + if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(8)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Tstatfs->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, + (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(8)); + return (ssize_t)host_size; +} + +static ssize_t validate_Rstatfs(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { + uint32_t net_offset = 0; + ssize_t host_size = sizeof(struct lib9p_msg_Rstatfs); + uint32_t offsetof_size = net_offset + 0; + uint32_t offsetof_typ = net_offset + 4; + uint32_t offsetof_end = net_offset + 67; + VALIDATE_NET_BYTES(67); + if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Rstatfs->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32, + (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size)); + if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(9)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Rstatfs->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, + (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(9)); + return (ssize_t)host_size; +} + +static ssize_t validate_Tlopen(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { + uint32_t net_offset = 0; + ssize_t host_size = sizeof(struct lib9p_msg_Tlopen); + uint32_t offsetof_size = net_offset + 0; + uint32_t offsetof_typ = net_offset + 4; + uint32_t offsetof_flags = net_offset + 11; + uint32_t offsetof_end = net_offset + 15; + VALIDATE_NET_BYTES(15); + if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Tlopen->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32, + (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size)); + if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(12)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Tlopen->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, + (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(12)); + if (GET_U32LE(offsetof_flags) & ~lo_masks[ctx->version]) + return lib9p_errorf(ctx, LINUX_EBADMSG, "unknown bits in lo bitfield: %#08"PRIx32, + GET_U32LE(offsetof_flags) & ~lo_masks[ctx->version]); + return (ssize_t)host_size; +} + +static ssize_t validate_Rlopen(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { + uint32_t net_offset = 0; + ssize_t host_size = sizeof(struct lib9p_msg_Rlopen); + uint32_t offsetof_size = net_offset + 0; + uint32_t offsetof_typ = net_offset + 4; + uint32_t offsetof_qid_type = net_offset + 7; + VALIDATE_NET_BYTES(20); + if (GET_U8LE(offsetof_qid_type) & ~qt_masks[ctx->version]) + return lib9p_errorf(ctx, LINUX_EBADMSG, "unknown bits in qt bitfield: %#02"PRIx8, + GET_U8LE(offsetof_qid_type) & ~qt_masks[ctx->version]); + uint32_t offsetof_end = net_offset + 4; + VALIDATE_NET_BYTES(4); + if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Rlopen->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32, + (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size)); + if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(13)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Rlopen->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, + (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(13)); + return (ssize_t)host_size; +} + +static ssize_t validate_Tlcreate(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { + uint32_t net_offset = 0; + ssize_t host_size = sizeof(struct lib9p_msg_Tlcreate); + uint32_t offsetof_size = net_offset + 0; + uint32_t offsetof_typ = net_offset + 4; + VALIDATE_NET_BYTES(13); + VALIDATE_NET_UTF8(LAST_U16LE()); + uint32_t offsetof_flags = net_offset + 0; + uint32_t offsetof_mode = net_offset + 4; + uint32_t offsetof_end = net_offset + 12; + VALIDATE_NET_BYTES(12); + if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Tlcreate->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32, + (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size)); + if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(14)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Tlcreate->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, + (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(14)); + if (GET_U32LE(offsetof_flags) & ~lo_masks[ctx->version]) + return lib9p_errorf(ctx, LINUX_EBADMSG, "unknown bits in lo bitfield: %#08"PRIx32, + GET_U32LE(offsetof_flags) & ~lo_masks[ctx->version]); + if (GET_U32LE(offsetof_mode) & ~mode_masks[ctx->version]) + return lib9p_errorf(ctx, LINUX_EBADMSG, "unknown bits in mode bitfield: %#08"PRIx32, + GET_U32LE(offsetof_mode) & ~mode_masks[ctx->version]); + return (ssize_t)host_size; +} + +static ssize_t validate_Rlcreate(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { + uint32_t net_offset = 0; + ssize_t host_size = sizeof(struct lib9p_msg_Rlcreate); + uint32_t offsetof_size = net_offset + 0; + uint32_t offsetof_typ = net_offset + 4; + uint32_t offsetof_qid_type = net_offset + 7; + VALIDATE_NET_BYTES(20); + if (GET_U8LE(offsetof_qid_type) & ~qt_masks[ctx->version]) + return lib9p_errorf(ctx, LINUX_EBADMSG, "unknown bits in qt bitfield: %#02"PRIx8, + GET_U8LE(offsetof_qid_type) & ~qt_masks[ctx->version]); + uint32_t offsetof_end = net_offset + 4; + VALIDATE_NET_BYTES(4); + if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Rlcreate->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32, + (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size)); + if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(15)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Rlcreate->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, + (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(15)); + return (ssize_t)host_size; +} + +static ssize_t validate_Tsymlink(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { + uint32_t net_offset = 0; + ssize_t host_size = sizeof(struct lib9p_msg_Tsymlink); + uint32_t offsetof_size = net_offset + 0; + uint32_t offsetof_typ = net_offset + 4; + VALIDATE_NET_BYTES(13); + VALIDATE_NET_UTF8(LAST_U16LE()); + VALIDATE_NET_BYTES(2); + VALIDATE_NET_UTF8(LAST_U16LE()); + uint32_t offsetof_end = net_offset + 4; + VALIDATE_NET_BYTES(4); + if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Tsymlink->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32, + (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size)); + if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(16)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Tsymlink->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, + (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(16)); + return (ssize_t)host_size; +} + +static ssize_t validate_Rsymlink(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { + uint32_t net_offset = 0; + ssize_t host_size = sizeof(struct lib9p_msg_Rsymlink); + uint32_t offsetof_size = net_offset + 0; + uint32_t offsetof_typ = net_offset + 4; + uint32_t offsetof_qid_type = net_offset + 7; + VALIDATE_NET_BYTES(20); + if (GET_U8LE(offsetof_qid_type) & ~qt_masks[ctx->version]) + return lib9p_errorf(ctx, LINUX_EBADMSG, "unknown bits in qt bitfield: %#02"PRIx8, + GET_U8LE(offsetof_qid_type) & ~qt_masks[ctx->version]); + uint32_t offsetof_end = net_offset + 0; + if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Rsymlink->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32, + (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size)); + if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(17)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Rsymlink->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, + (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(17)); + return (ssize_t)host_size; +} + +static ssize_t validate_Tmknod(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { + uint32_t net_offset = 0; + ssize_t host_size = sizeof(struct lib9p_msg_Tmknod); + uint32_t offsetof_size = net_offset + 0; + uint32_t offsetof_typ = net_offset + 4; + VALIDATE_NET_BYTES(13); + VALIDATE_NET_UTF8(LAST_U16LE()); + uint32_t offsetof_mode = net_offset + 0; + uint32_t offsetof_end = net_offset + 16; + VALIDATE_NET_BYTES(16); + if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Tmknod->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32, + (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size)); + if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(18)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Tmknod->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, + (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(18)); + if (GET_U32LE(offsetof_mode) & ~mode_masks[ctx->version]) + return lib9p_errorf(ctx, LINUX_EBADMSG, "unknown bits in mode bitfield: %#08"PRIx32, + GET_U32LE(offsetof_mode) & ~mode_masks[ctx->version]); + return (ssize_t)host_size; +} + +static ssize_t validate_Rmknod(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { + uint32_t net_offset = 0; + ssize_t host_size = sizeof(struct lib9p_msg_Rmknod); + uint32_t offsetof_size = net_offset + 0; + uint32_t offsetof_typ = net_offset + 4; + uint32_t offsetof_qid_type = net_offset + 7; + VALIDATE_NET_BYTES(20); + if (GET_U8LE(offsetof_qid_type) & ~qt_masks[ctx->version]) + return lib9p_errorf(ctx, LINUX_EBADMSG, "unknown bits in qt bitfield: %#02"PRIx8, + GET_U8LE(offsetof_qid_type) & ~qt_masks[ctx->version]); + uint32_t offsetof_end = net_offset + 0; + if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Rmknod->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32, + (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size)); + if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(19)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Rmknod->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, + (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(19)); + return (ssize_t)host_size; +} + +static ssize_t validate_Trename(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { + uint32_t net_offset = 0; + ssize_t host_size = sizeof(struct lib9p_msg_Trename); + uint32_t offsetof_size = net_offset + 0; + uint32_t offsetof_typ = net_offset + 4; + VALIDATE_NET_BYTES(17); + VALIDATE_NET_UTF8(LAST_U16LE()); + uint32_t offsetof_end = net_offset + 0; + if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Trename->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32, + (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size)); + if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(20)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Trename->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, + (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(20)); + return (ssize_t)host_size; +} + +static ssize_t validate_Rrename(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { + uint32_t net_offset = 0; + ssize_t host_size = sizeof(struct lib9p_msg_Rrename); + uint32_t offsetof_size = net_offset + 0; + uint32_t offsetof_typ = net_offset + 4; + uint32_t offsetof_end = net_offset + 7; + VALIDATE_NET_BYTES(7); + if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Rrename->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32, + (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size)); + if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(21)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Rrename->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, + (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(21)); + return (ssize_t)host_size; +} + +static ssize_t validate_Treadlink(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { + uint32_t net_offset = 0; + ssize_t host_size = sizeof(struct lib9p_msg_Treadlink); + uint32_t offsetof_size = net_offset + 0; + uint32_t offsetof_typ = net_offset + 4; + uint32_t offsetof_end = net_offset + 11; + VALIDATE_NET_BYTES(11); + if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Treadlink->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32, + (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size)); + if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(22)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Treadlink->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, + (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(22)); + return (ssize_t)host_size; +} + +static ssize_t validate_Rreadlink(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { + uint32_t net_offset = 0; + ssize_t host_size = sizeof(struct lib9p_msg_Rreadlink); + uint32_t offsetof_size = net_offset + 0; + uint32_t offsetof_typ = net_offset + 4; + VALIDATE_NET_BYTES(9); + VALIDATE_NET_UTF8(LAST_U16LE()); + uint32_t offsetof_end = net_offset + 0; + if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Rreadlink->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32, + (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size)); + if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(23)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Rreadlink->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, + (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(23)); + return (ssize_t)host_size; +} + +static ssize_t validate_Tgetattr(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { + uint32_t net_offset = 0; + ssize_t host_size = sizeof(struct lib9p_msg_Tgetattr); + uint32_t offsetof_size = net_offset + 0; + uint32_t offsetof_typ = net_offset + 4; + uint32_t offsetof_request_mask = net_offset + 11; + uint32_t offsetof_end = net_offset + 19; + VALIDATE_NET_BYTES(19); + if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Tgetattr->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32, + (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size)); + if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(24)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Tgetattr->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, + (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(24)); + if (GET_U64LE(offsetof_request_mask) & ~getattr_masks[ctx->version]) + return lib9p_errorf(ctx, LINUX_EBADMSG, "unknown bits in getattr bitfield: %#016"PRIx64, + GET_U64LE(offsetof_request_mask) & ~getattr_masks[ctx->version]); + return (ssize_t)host_size; +} + +static ssize_t validate_Rgetattr(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { + uint32_t net_offset = 0; + ssize_t host_size = sizeof(struct lib9p_msg_Rgetattr); + uint32_t offsetof_size = net_offset + 0; + uint32_t offsetof_typ = net_offset + 4; + uint32_t offsetof_valid = net_offset + 7; + uint32_t offsetof_qid_type = net_offset + 15; + VALIDATE_NET_BYTES(28); + if (GET_U8LE(offsetof_qid_type) & ~qt_masks[ctx->version]) + return lib9p_errorf(ctx, LINUX_EBADMSG, "unknown bits in qt bitfield: %#02"PRIx8, + GET_U8LE(offsetof_qid_type) & ~qt_masks[ctx->version]); + uint32_t offsetof_mode = net_offset + 0; + uint32_t offsetof_end = net_offset + 132; + VALIDATE_NET_BYTES(132); + if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Rgetattr->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32, + (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size)); + if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(25)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Rgetattr->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, + (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(25)); + if (GET_U64LE(offsetof_valid) & ~getattr_masks[ctx->version]) + return lib9p_errorf(ctx, LINUX_EBADMSG, "unknown bits in getattr bitfield: %#016"PRIx64, + GET_U64LE(offsetof_valid) & ~getattr_masks[ctx->version]); + if (GET_U32LE(offsetof_mode) & ~mode_masks[ctx->version]) + return lib9p_errorf(ctx, LINUX_EBADMSG, "unknown bits in mode bitfield: %#08"PRIx32, + GET_U32LE(offsetof_mode) & ~mode_masks[ctx->version]); + return (ssize_t)host_size; +} + +static ssize_t validate_Tsetattr(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { + uint32_t net_offset = 0; + ssize_t host_size = sizeof(struct lib9p_msg_Tsetattr); + uint32_t offsetof_size = net_offset + 0; + uint32_t offsetof_typ = net_offset + 4; + uint32_t offsetof_valid = net_offset + 11; + uint32_t offsetof_mode = net_offset + 15; + uint32_t offsetof_end = net_offset + 67; + VALIDATE_NET_BYTES(67); + if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Tsetattr->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32, + (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size)); + if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(26)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Tsetattr->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, + (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(26)); + if (GET_U32LE(offsetof_valid) & ~setattr_masks[ctx->version]) + return lib9p_errorf(ctx, LINUX_EBADMSG, "unknown bits in setattr bitfield: %#08"PRIx32, + GET_U32LE(offsetof_valid) & ~setattr_masks[ctx->version]); + if (GET_U32LE(offsetof_mode) & ~mode_masks[ctx->version]) + return lib9p_errorf(ctx, LINUX_EBADMSG, "unknown bits in mode bitfield: %#08"PRIx32, + GET_U32LE(offsetof_mode) & ~mode_masks[ctx->version]); + return (ssize_t)host_size; +} + +static ssize_t validate_Rsetattr(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { + uint32_t net_offset = 0; + ssize_t host_size = sizeof(struct lib9p_msg_Rsetattr); + uint32_t offsetof_size = net_offset + 0; + uint32_t offsetof_typ = net_offset + 4; + uint32_t offsetof_end = net_offset + 7; + VALIDATE_NET_BYTES(7); + if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Rsetattr->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32, + (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size)); + if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(27)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Rsetattr->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, + (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(27)); + return (ssize_t)host_size; +} + +static ssize_t validate_Txattrwalk(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { + uint32_t net_offset = 0; + ssize_t host_size = sizeof(struct lib9p_msg_Txattrwalk); + uint32_t offsetof_size = net_offset + 0; + uint32_t offsetof_typ = net_offset + 4; + VALIDATE_NET_BYTES(17); + VALIDATE_NET_UTF8(LAST_U16LE()); + uint32_t offsetof_end = net_offset + 0; + if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Txattrwalk->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32, + (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size)); + if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(30)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Txattrwalk->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, + (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(30)); + return (ssize_t)host_size; +} + +static ssize_t validate_Rxattrwalk(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { + uint32_t net_offset = 0; + ssize_t host_size = sizeof(struct lib9p_msg_Rxattrwalk); + uint32_t offsetof_size = net_offset + 0; + uint32_t offsetof_typ = net_offset + 4; + uint32_t offsetof_end = net_offset + 15; + VALIDATE_NET_BYTES(15); + if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Rxattrwalk->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32, + (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size)); + if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(31)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Rxattrwalk->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, + (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(31)); + return (ssize_t)host_size; +} + +static ssize_t validate_Txattrcreate(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { + uint32_t net_offset = 0; + ssize_t host_size = sizeof(struct lib9p_msg_Txattrcreate); + uint32_t offsetof_size = net_offset + 0; + uint32_t offsetof_typ = net_offset + 4; + VALIDATE_NET_BYTES(13); + VALIDATE_NET_UTF8(LAST_U16LE()); + uint32_t offsetof_end = net_offset + 12; + VALIDATE_NET_BYTES(12); + if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Txattrcreate->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32, + (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size)); + if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(32)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Txattrcreate->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, + (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(32)); + return (ssize_t)host_size; +} + +static ssize_t validate_Rxattrcreate(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { + uint32_t net_offset = 0; + ssize_t host_size = sizeof(struct lib9p_msg_Rxattrcreate); + uint32_t offsetof_size = net_offset + 0; + uint32_t offsetof_typ = net_offset + 4; + uint32_t offsetof_end = net_offset + 7; + VALIDATE_NET_BYTES(7); + if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Rxattrcreate->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32, + (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size)); + if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(33)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Rxattrcreate->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, + (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(33)); + return (ssize_t)host_size; +} + +static ssize_t validate_Treaddir(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { + uint32_t net_offset = 0; + ssize_t host_size = sizeof(struct lib9p_msg_Treaddir); + uint32_t offsetof_size = net_offset + 0; + uint32_t offsetof_typ = net_offset + 4; + uint32_t offsetof_end = net_offset + 23; + VALIDATE_NET_BYTES(23); + if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Treaddir->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32, + (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size)); + if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(40)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Treaddir->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, + (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(40)); + return (ssize_t)host_size; +} + +static ssize_t validate_Rreaddir(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { + uint32_t net_offset = 0; + ssize_t host_size = sizeof(struct lib9p_msg_Rreaddir); + uint32_t offsetof_size = net_offset + 0; + uint32_t offsetof_typ = net_offset + 4; + VALIDATE_NET_BYTES(11); + VALIDATE_NET_BYTES(LAST_U32LE()); + uint32_t offsetof_end = net_offset + 0; + if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Rreaddir->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32, + (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size)); + if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(41)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Rreaddir->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, + (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(41)); + return (ssize_t)host_size; +} + +static ssize_t validate_Tfsync(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { + uint32_t net_offset = 0; + ssize_t host_size = sizeof(struct lib9p_msg_Tfsync); + uint32_t offsetof_size = net_offset + 0; + uint32_t offsetof_typ = net_offset + 4; + uint32_t offsetof_end = net_offset + 15; + VALIDATE_NET_BYTES(15); + if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Tfsync->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32, + (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size)); + if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(50)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Tfsync->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, + (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(50)); + return (ssize_t)host_size; +} + +static ssize_t validate_Rfsync(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { + uint32_t net_offset = 0; + ssize_t host_size = sizeof(struct lib9p_msg_Rfsync); + uint32_t offsetof_size = net_offset + 0; + uint32_t offsetof_typ = net_offset + 4; + uint32_t offsetof_end = net_offset + 7; + VALIDATE_NET_BYTES(7); + if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Rfsync->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32, + (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size)); + if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(51)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Rfsync->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, + (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(51)); + return (ssize_t)host_size; +} + +static ssize_t validate_Tlock(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { + uint32_t net_offset = 0; + ssize_t host_size = sizeof(struct lib9p_msg_Tlock); + uint32_t offsetof_size = net_offset + 0; + uint32_t offsetof_typ = net_offset + 4; + uint32_t offsetof_flags = net_offset + 12; + VALIDATE_NET_BYTES(38); + VALIDATE_NET_UTF8(LAST_U16LE()); + uint32_t offsetof_end = net_offset + 0; + if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Tlock->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32, + (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size)); + if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(52)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Tlock->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, + (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(52)); + if (GET_U32LE(offsetof_flags) & ~lock_flags_masks[ctx->version]) + return lib9p_errorf(ctx, LINUX_EBADMSG, "unknown bits in lock_flags bitfield: %#08"PRIx32, + GET_U32LE(offsetof_flags) & ~lock_flags_masks[ctx->version]); + return (ssize_t)host_size; +} + +static ssize_t validate_Rlock(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { + uint32_t net_offset = 0; + ssize_t host_size = sizeof(struct lib9p_msg_Rlock); + uint32_t offsetof_size = net_offset + 0; + uint32_t offsetof_typ = net_offset + 4; + uint32_t offsetof_end = net_offset + 8; + VALIDATE_NET_BYTES(8); + if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Rlock->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32, + (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size)); + if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(53)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Rlock->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, + (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(53)); + return (ssize_t)host_size; +} + +static ssize_t validate_Tgetlock(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { + uint32_t net_offset = 0; + ssize_t host_size = sizeof(struct lib9p_msg_Tgetlock); + uint32_t offsetof_size = net_offset + 0; + uint32_t offsetof_typ = net_offset + 4; + VALIDATE_NET_BYTES(34); + VALIDATE_NET_UTF8(LAST_U16LE()); + uint32_t offsetof_end = net_offset + 0; + if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Tgetlock->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32, + (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size)); + if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(54)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Tgetlock->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, + (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(54)); + return (ssize_t)host_size; +} + +static ssize_t validate_Rgetlock(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { + uint32_t net_offset = 0; + ssize_t host_size = sizeof(struct lib9p_msg_Rgetlock); + uint32_t offsetof_size = net_offset + 0; + uint32_t offsetof_typ = net_offset + 4; + VALIDATE_NET_BYTES(30); + VALIDATE_NET_UTF8(LAST_U16LE()); + uint32_t offsetof_end = net_offset + 0; + if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Rgetlock->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32, + (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size)); + if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(55)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Rgetlock->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, + (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(55)); + return (ssize_t)host_size; +} + +static ssize_t validate_Tlink(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { + uint32_t net_offset = 0; + ssize_t host_size = sizeof(struct lib9p_msg_Tlink); + uint32_t offsetof_size = net_offset + 0; + uint32_t offsetof_typ = net_offset + 4; + VALIDATE_NET_BYTES(17); + VALIDATE_NET_UTF8(LAST_U16LE()); + uint32_t offsetof_end = net_offset + 0; + if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Tlink->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32, + (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size)); + if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(70)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Tlink->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, + (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(70)); + return (ssize_t)host_size; +} + +static ssize_t validate_Rlink(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { + uint32_t net_offset = 0; + ssize_t host_size = sizeof(struct lib9p_msg_Rlink); + uint32_t offsetof_size = net_offset + 0; + uint32_t offsetof_typ = net_offset + 4; + uint32_t offsetof_end = net_offset + 7; + VALIDATE_NET_BYTES(7); + if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Rlink->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32, + (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size)); + if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(71)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Rlink->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, + (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(71)); + return (ssize_t)host_size; +} + +static ssize_t validate_Tmkdir(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { + uint32_t net_offset = 0; + ssize_t host_size = sizeof(struct lib9p_msg_Tmkdir); + uint32_t offsetof_size = net_offset + 0; + uint32_t offsetof_typ = net_offset + 4; + VALIDATE_NET_BYTES(13); + VALIDATE_NET_UTF8(LAST_U16LE()); + uint32_t offsetof_mode = net_offset + 0; + uint32_t offsetof_end = net_offset + 8; + VALIDATE_NET_BYTES(8); + if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Tmkdir->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32, + (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size)); + if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(72)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Tmkdir->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, + (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(72)); + if (GET_U32LE(offsetof_mode) & ~mode_masks[ctx->version]) + return lib9p_errorf(ctx, LINUX_EBADMSG, "unknown bits in mode bitfield: %#08"PRIx32, + GET_U32LE(offsetof_mode) & ~mode_masks[ctx->version]); + return (ssize_t)host_size; +} + +static ssize_t validate_Rmkdir(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { + uint32_t net_offset = 0; + ssize_t host_size = sizeof(struct lib9p_msg_Rmkdir); + uint32_t offsetof_size = net_offset + 0; + uint32_t offsetof_typ = net_offset + 4; + uint32_t offsetof_qid_type = net_offset + 7; + VALIDATE_NET_BYTES(20); + if (GET_U8LE(offsetof_qid_type) & ~qt_masks[ctx->version]) + return lib9p_errorf(ctx, LINUX_EBADMSG, "unknown bits in qt bitfield: %#02"PRIx8, + GET_U8LE(offsetof_qid_type) & ~qt_masks[ctx->version]); + uint32_t offsetof_end = net_offset + 0; + if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Rmkdir->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32, + (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size)); + if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(73)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Rmkdir->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, + (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(73)); + return (ssize_t)host_size; +} + +static ssize_t validate_Trenameat(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { + uint32_t net_offset = 0; + ssize_t host_size = sizeof(struct lib9p_msg_Trenameat); + uint32_t offsetof_size = net_offset + 0; + uint32_t offsetof_typ = net_offset + 4; + VALIDATE_NET_BYTES(13); + VALIDATE_NET_UTF8(LAST_U16LE()); + VALIDATE_NET_BYTES(6); + VALIDATE_NET_UTF8(LAST_U16LE()); + uint32_t offsetof_end = net_offset + 0; + if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Trenameat->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32, + (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size)); + if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(74)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Trenameat->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, + (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(74)); + return (ssize_t)host_size; +} + +static ssize_t validate_Rrenameat(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { + uint32_t net_offset = 0; + ssize_t host_size = sizeof(struct lib9p_msg_Rrenameat); + uint32_t offsetof_size = net_offset + 0; + uint32_t offsetof_typ = net_offset + 4; + uint32_t offsetof_end = net_offset + 7; + VALIDATE_NET_BYTES(7); + if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Rrenameat->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32, + (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size)); + if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(75)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Rrenameat->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, + (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(75)); + return (ssize_t)host_size; +} + +static ssize_t validate_Tunlinkat(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { + uint32_t net_offset = 0; + ssize_t host_size = sizeof(struct lib9p_msg_Tunlinkat); + uint32_t offsetof_size = net_offset + 0; + uint32_t offsetof_typ = net_offset + 4; + VALIDATE_NET_BYTES(13); + VALIDATE_NET_UTF8(LAST_U16LE()); + uint32_t offsetof_end = net_offset + 4; + VALIDATE_NET_BYTES(4); + if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Tunlinkat->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32, + (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size)); + if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(76)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Tunlinkat->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, + (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(76)); + return (ssize_t)host_size; +} + +static ssize_t validate_Runlinkat(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { + uint32_t net_offset = 0; + ssize_t host_size = sizeof(struct lib9p_msg_Runlinkat); + uint32_t offsetof_size = net_offset + 0; + uint32_t offsetof_typ = net_offset + 4; + uint32_t offsetof_end = net_offset + 7; + VALIDATE_NET_BYTES(7); + if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Runlinkat->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32, + (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size)); + if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(77)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Runlinkat->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, + (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(77)); + return (ssize_t)host_size; +} + +#endif /* CONFIG_9P_ENABLE_9P2000_L */ +#if CONFIG_9P_ENABLE_9P2000_e +static ssize_t validate_Tsession(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { + uint32_t net_offset = 0; + ssize_t host_size = sizeof(struct lib9p_msg_Tsession); + uint32_t offsetof_size = net_offset + 0; + uint32_t offsetof_typ = net_offset + 4; + uint32_t offsetof_end = net_offset + 15; + VALIDATE_NET_BYTES(15); + if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Tsession->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32, + (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size)); + if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(150)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Tsession->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, + (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(150)); + return (ssize_t)host_size; +} + +static ssize_t validate_Rsession(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { + uint32_t net_offset = 0; + ssize_t host_size = sizeof(struct lib9p_msg_Rsession); + uint32_t offsetof_size = net_offset + 0; + uint32_t offsetof_typ = net_offset + 4; + uint32_t offsetof_end = net_offset + 7; + VALIDATE_NET_BYTES(7); + if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Rsession->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32, + (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size)); + if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(151)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Rsession->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, + (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(151)); + return (ssize_t)host_size; +} + +static ssize_t validate_Tsread(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { + uint32_t net_offset = 0; + ssize_t host_size = sizeof(struct lib9p_msg_Tsread); + uint32_t offsetof_size = net_offset + 0; + uint32_t offsetof_typ = net_offset + 4; + VALIDATE_NET_BYTES(13); + for (uint16_t i = 0, cnt = LAST_U16LE(); i < cnt; i++) { + RESERVE_HOST_BYTES(sizeof(struct lib9p_s)); + VALIDATE_NET_BYTES(2); + VALIDATE_NET_UTF8(LAST_U16LE()); + } + uint32_t offsetof_end = net_offset + 0; + if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Tsread->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32, + (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size)); + if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(152)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Tsread->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, + (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(152)); + return (ssize_t)host_size; +} + +static ssize_t validate_Rsread(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { + uint32_t net_offset = 0; + ssize_t host_size = sizeof(struct lib9p_msg_Rsread); + uint32_t offsetof_size = net_offset + 0; + uint32_t offsetof_typ = net_offset + 4; + VALIDATE_NET_BYTES(11); + VALIDATE_NET_BYTES(LAST_U32LE()); + uint32_t offsetof_end = net_offset + 0; + if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Rsread->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32, + (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size)); + if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(153)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Rsread->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, + (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(153)); + return (ssize_t)host_size; +} + +static ssize_t validate_Tswrite(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { + uint32_t net_offset = 0; + ssize_t host_size = sizeof(struct lib9p_msg_Tswrite); + uint32_t offsetof_size = net_offset + 0; + uint32_t offsetof_typ = net_offset + 4; + VALIDATE_NET_BYTES(13); + for (uint16_t i = 0, cnt = LAST_U16LE(); i < cnt; i++) { + RESERVE_HOST_BYTES(sizeof(struct lib9p_s)); + VALIDATE_NET_BYTES(2); + VALIDATE_NET_UTF8(LAST_U16LE()); + } + VALIDATE_NET_BYTES(4); + VALIDATE_NET_BYTES(LAST_U32LE()); + uint32_t offsetof_end = net_offset + 0; + if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Tswrite->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32, + (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size)); + if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(154)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Tswrite->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, + (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(154)); + return (ssize_t)host_size; +} + +static ssize_t validate_Rswrite(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { + uint32_t net_offset = 0; + ssize_t host_size = sizeof(struct lib9p_msg_Rswrite); + uint32_t offsetof_size = net_offset + 0; + uint32_t offsetof_typ = net_offset + 4; + uint32_t offsetof_end = net_offset + 11; + VALIDATE_NET_BYTES(11); + if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Rswrite->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32, + (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size)); + if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(155)) + return lib9p_errorf(ctx, LINUX_EBADMSG, "Rswrite->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, + (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(155)); + return (ssize_t)host_size; +} +#endif /* CONFIG_9P_ENABLE_9P2000_e */ + +/* unmarshal_* ****************************************************************/ + +#define UNMARSHAL_BYTES(ctx, data_lvalue, len) \ + data_lvalue = (char *)&net_bytes[net_offset]; \ + net_offset += len; +#define UNMARSHAL_U8LE(ctx, val_lvalue) \ + val_lvalue = net_bytes[net_offset]; \ + net_offset += 1; +#define UNMARSHAL_U16LE(ctx, val_lvalue) \ + val_lvalue = uint16le_decode(&net_bytes[net_offset]); \ + net_offset += 2; +#define UNMARSHAL_U32LE(ctx, val_lvalue) \ + val_lvalue = uint32le_decode(&net_bytes[net_offset]); \ + net_offset += 4; +#define UNMARSHAL_U64LE(ctx, val_lvalue) \ + val_lvalue = uint64le_decode(&net_bytes[net_offset]); \ + net_offset += 8; + +#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u +static void unmarshal_stat([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { + struct lib9p_stat *out = out_buf; + [[gnu::unused]] void *extra = &out[1]; + uint32_t net_offset = 0; + net_offset += 2; + UNMARSHAL_U16LE(ctx, out->kern_type); + UNMARSHAL_U32LE(ctx, out->kern_dev); + UNMARSHAL_U8LE(ctx, out->file_qid.type); + UNMARSHAL_U32LE(ctx, out->file_qid.vers); + UNMARSHAL_U64LE(ctx, out->file_qid.path); + UNMARSHAL_U32LE(ctx, out->file_mode); + UNMARSHAL_U32LE(ctx, out->file_atime); + UNMARSHAL_U32LE(ctx, out->file_mtime); + UNMARSHAL_U64LE(ctx, out->file_size); + UNMARSHAL_U16LE(ctx, out->file_name.len); + UNMARSHAL_BYTES(ctx, out->file_name.utf8, out->file_name.len); + UNMARSHAL_U16LE(ctx, out->file_owner_uid.len); + UNMARSHAL_BYTES(ctx, out->file_owner_uid.utf8, out->file_owner_uid.len); + UNMARSHAL_U16LE(ctx, out->file_owner_gid.len); + UNMARSHAL_BYTES(ctx, out->file_owner_gid.utf8, out->file_owner_gid.len); + UNMARSHAL_U16LE(ctx, out->file_last_modified_uid.len); + UNMARSHAL_BYTES(ctx, out->file_last_modified_uid.utf8, out->file_last_modified_uid.len); +#if CONFIG_9P_ENABLE_9P2000_u + if (is_ver(ctx, 9P2000_u)) { + UNMARSHAL_U16LE(ctx, out->file_extension.len); + UNMARSHAL_BYTES(ctx, out->file_extension.utf8, out->file_extension.len); + UNMARSHAL_U32LE(ctx, out->file_owner_n_uid); + UNMARSHAL_U32LE(ctx, out->file_owner_n_gid); + UNMARSHAL_U32LE(ctx, out->file_last_modified_n_uid); + } +#endif /* CONFIG_9P_ENABLE_9P2000_u */ +} + +#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */ +#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u +static void unmarshal_Tversion([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { + struct lib9p_msg_Tversion *out = out_buf; + [[gnu::unused]] void *extra = &out[1]; + uint32_t net_offset = 0; + net_offset += 4; + net_offset += 1; + UNMARSHAL_U16LE(ctx, out->tag); + UNMARSHAL_U32LE(ctx, out->max_msg_size); + UNMARSHAL_U16LE(ctx, out->version.len); + UNMARSHAL_BYTES(ctx, out->version.utf8, out->version.len); +} + +static void unmarshal_Rversion([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { + struct lib9p_msg_Rversion *out = out_buf; + [[gnu::unused]] void *extra = &out[1]; + uint32_t net_offset = 0; + net_offset += 4; + net_offset += 1; + UNMARSHAL_U16LE(ctx, out->tag); + UNMARSHAL_U32LE(ctx, out->max_msg_size); + UNMARSHAL_U16LE(ctx, out->version.len); + UNMARSHAL_BYTES(ctx, out->version.utf8, out->version.len); +} + +static void unmarshal_Tauth([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { + struct lib9p_msg_Tauth *out = out_buf; + [[gnu::unused]] void *extra = &out[1]; + uint32_t net_offset = 0; + net_offset += 4; + net_offset += 1; + UNMARSHAL_U16LE(ctx, out->tag); + UNMARSHAL_U32LE(ctx, out->afid); + UNMARSHAL_U16LE(ctx, out->uname.len); + UNMARSHAL_BYTES(ctx, out->uname.utf8, out->uname.len); + UNMARSHAL_U16LE(ctx, out->aname.len); + UNMARSHAL_BYTES(ctx, out->aname.utf8, out->aname.len); +#if CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_u + if (( is_ver(ctx, 9P2000_L) || is_ver(ctx, 9P2000_u) )) { + UNMARSHAL_U32LE(ctx, out->n_uid); + } +#endif /* CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_u */ +} + +static void unmarshal_Rauth([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { + struct lib9p_msg_Rauth *out = out_buf; + [[gnu::unused]] void *extra = &out[1]; + uint32_t net_offset = 0; + net_offset += 4; + net_offset += 1; + UNMARSHAL_U16LE(ctx, out->tag); + UNMARSHAL_U8LE(ctx, out->aqid.type); + UNMARSHAL_U32LE(ctx, out->aqid.vers); + UNMARSHAL_U64LE(ctx, out->aqid.path); +} + +static void unmarshal_Tattach([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { + struct lib9p_msg_Tattach *out = out_buf; + [[gnu::unused]] void *extra = &out[1]; + uint32_t net_offset = 0; + net_offset += 4; + net_offset += 1; + UNMARSHAL_U16LE(ctx, out->tag); + UNMARSHAL_U32LE(ctx, out->fid); + UNMARSHAL_U32LE(ctx, out->afid); + UNMARSHAL_U16LE(ctx, out->uname.len); + UNMARSHAL_BYTES(ctx, out->uname.utf8, out->uname.len); + UNMARSHAL_U16LE(ctx, out->aname.len); + UNMARSHAL_BYTES(ctx, out->aname.utf8, out->aname.len); +#if CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_u + if (( is_ver(ctx, 9P2000_L) || is_ver(ctx, 9P2000_u) )) { + UNMARSHAL_U32LE(ctx, out->n_uid); + } +#endif /* CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_u */ +} + +static void unmarshal_Rattach([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { + struct lib9p_msg_Rattach *out = out_buf; + [[gnu::unused]] void *extra = &out[1]; + uint32_t net_offset = 0; + net_offset += 4; + net_offset += 1; + UNMARSHAL_U16LE(ctx, out->tag); + UNMARSHAL_U8LE(ctx, out->qid.type); + UNMARSHAL_U32LE(ctx, out->qid.vers); + UNMARSHAL_U64LE(ctx, out->qid.path); +} + +static void unmarshal_Rerror([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { + struct lib9p_msg_Rerror *out = out_buf; + [[gnu::unused]] void *extra = &out[1]; + uint32_t net_offset = 0; + net_offset += 4; + net_offset += 1; + UNMARSHAL_U16LE(ctx, out->tag); + UNMARSHAL_U16LE(ctx, out->errstr.len); + UNMARSHAL_BYTES(ctx, out->errstr.utf8, out->errstr.len); +#if CONFIG_9P_ENABLE_9P2000_u + if (is_ver(ctx, 9P2000_u)) { + UNMARSHAL_U32LE(ctx, out->errnum); + } +#endif /* CONFIG_9P_ENABLE_9P2000_u */ +} + +static void unmarshal_Tflush([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { + struct lib9p_msg_Tflush *out = out_buf; + [[gnu::unused]] void *extra = &out[1]; + uint32_t net_offset = 0; + net_offset += 4; + net_offset += 1; + UNMARSHAL_U16LE(ctx, out->tag); + UNMARSHAL_U16LE(ctx, out->oldtag); +} + +static void unmarshal_Rflush([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { + struct lib9p_msg_Rflush *out = out_buf; + [[gnu::unused]] void *extra = &out[1]; + uint32_t net_offset = 0; + net_offset += 4; + net_offset += 1; + UNMARSHAL_U16LE(ctx, out->tag); +} + +static void unmarshal_Twalk([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { + struct lib9p_msg_Twalk *out = out_buf; + [[gnu::unused]] void *extra = &out[1]; + uint32_t net_offset = 0; + net_offset += 4; + net_offset += 1; + UNMARSHAL_U16LE(ctx, out->tag); + UNMARSHAL_U32LE(ctx, out->fid); + UNMARSHAL_U32LE(ctx, out->newfid); + UNMARSHAL_U16LE(ctx, out->nwname); + out->wname = extra; + extra += sizeof(out->wname[0]) * out->nwname; + for (uint16_t i = 0; i < out->nwname; i++) { + UNMARSHAL_U16LE(ctx, out->wname[i].len); + UNMARSHAL_BYTES(ctx, out->wname[i].utf8, out->wname[i].len); + } +} + +static void unmarshal_Rwalk([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { + struct lib9p_msg_Rwalk *out = out_buf; + [[gnu::unused]] void *extra = &out[1]; + uint32_t net_offset = 0; + net_offset += 4; + net_offset += 1; + UNMARSHAL_U16LE(ctx, out->tag); + UNMARSHAL_U16LE(ctx, out->nwqid); + out->wqid = extra; + extra += sizeof(out->wqid[0]) * out->nwqid; + for (uint16_t i = 0; i < out->nwqid; i++) { + UNMARSHAL_U8LE(ctx, out->wqid[i].type); + UNMARSHAL_U32LE(ctx, out->wqid[i].vers); + UNMARSHAL_U64LE(ctx, out->wqid[i].path); + } +} + +#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */ +#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u +static void unmarshal_Topen([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { + struct lib9p_msg_Topen *out = out_buf; + [[gnu::unused]] void *extra = &out[1]; + uint32_t net_offset = 0; + net_offset += 4; + net_offset += 1; + UNMARSHAL_U16LE(ctx, out->tag); + UNMARSHAL_U32LE(ctx, out->fid); + UNMARSHAL_U8LE(ctx, out->mode); +} + +static void unmarshal_Ropen([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { + struct lib9p_msg_Ropen *out = out_buf; + [[gnu::unused]] void *extra = &out[1]; + uint32_t net_offset = 0; + net_offset += 4; + net_offset += 1; + UNMARSHAL_U16LE(ctx, out->tag); + UNMARSHAL_U8LE(ctx, out->qid.type); + UNMARSHAL_U32LE(ctx, out->qid.vers); + UNMARSHAL_U64LE(ctx, out->qid.path); + UNMARSHAL_U32LE(ctx, out->iounit); +} + +static void unmarshal_Tcreate([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { + struct lib9p_msg_Tcreate *out = out_buf; + [[gnu::unused]] void *extra = &out[1]; + uint32_t net_offset = 0; + net_offset += 4; + net_offset += 1; + UNMARSHAL_U16LE(ctx, out->tag); + UNMARSHAL_U32LE(ctx, out->fid); + UNMARSHAL_U16LE(ctx, out->name.len); + UNMARSHAL_BYTES(ctx, out->name.utf8, out->name.len); + UNMARSHAL_U32LE(ctx, out->perm); + UNMARSHAL_U8LE(ctx, out->mode); +} + +static void unmarshal_Rcreate([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { + struct lib9p_msg_Rcreate *out = out_buf; + [[gnu::unused]] void *extra = &out[1]; + uint32_t net_offset = 0; + net_offset += 4; + net_offset += 1; + UNMARSHAL_U16LE(ctx, out->tag); + UNMARSHAL_U8LE(ctx, out->qid.type); + UNMARSHAL_U32LE(ctx, out->qid.vers); + UNMARSHAL_U64LE(ctx, out->qid.path); + UNMARSHAL_U32LE(ctx, out->iounit); +} + +#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */ +#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u +static void unmarshal_Tread([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { + struct lib9p_msg_Tread *out = out_buf; + [[gnu::unused]] void *extra = &out[1]; + uint32_t net_offset = 0; + net_offset += 4; + net_offset += 1; + UNMARSHAL_U16LE(ctx, out->tag); + UNMARSHAL_U32LE(ctx, out->fid); + UNMARSHAL_U64LE(ctx, out->offset); + UNMARSHAL_U32LE(ctx, out->count); +} + +static void unmarshal_Rread([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { + struct lib9p_msg_Rread *out = out_buf; + [[gnu::unused]] void *extra = &out[1]; + uint32_t net_offset = 0; + net_offset += 4; + net_offset += 1; + UNMARSHAL_U16LE(ctx, out->tag); + UNMARSHAL_U32LE(ctx, out->count); + UNMARSHAL_BYTES(ctx, out->data, out->count); +} + +static void unmarshal_Twrite([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { + struct lib9p_msg_Twrite *out = out_buf; + [[gnu::unused]] void *extra = &out[1]; + uint32_t net_offset = 0; + net_offset += 4; + net_offset += 1; + UNMARSHAL_U16LE(ctx, out->tag); + UNMARSHAL_U32LE(ctx, out->fid); + UNMARSHAL_U64LE(ctx, out->offset); + UNMARSHAL_U32LE(ctx, out->count); + UNMARSHAL_BYTES(ctx, out->data, out->count); +} + +static void unmarshal_Rwrite([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { + struct lib9p_msg_Rwrite *out = out_buf; + [[gnu::unused]] void *extra = &out[1]; + uint32_t net_offset = 0; + net_offset += 4; + net_offset += 1; + UNMARSHAL_U16LE(ctx, out->tag); + UNMARSHAL_U32LE(ctx, out->count); +} + +static void unmarshal_Tclunk([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { + struct lib9p_msg_Tclunk *out = out_buf; + [[gnu::unused]] void *extra = &out[1]; + uint32_t net_offset = 0; + net_offset += 4; + net_offset += 1; + UNMARSHAL_U16LE(ctx, out->tag); + UNMARSHAL_U32LE(ctx, out->fid); +} + +static void unmarshal_Rclunk([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { + struct lib9p_msg_Rclunk *out = out_buf; + [[gnu::unused]] void *extra = &out[1]; + uint32_t net_offset = 0; + net_offset += 4; + net_offset += 1; + UNMARSHAL_U16LE(ctx, out->tag); +} + +static void unmarshal_Tremove([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { + struct lib9p_msg_Tremove *out = out_buf; + [[gnu::unused]] void *extra = &out[1]; + uint32_t net_offset = 0; + net_offset += 4; + net_offset += 1; + UNMARSHAL_U16LE(ctx, out->tag); + UNMARSHAL_U32LE(ctx, out->fid); +} + +static void unmarshal_Rremove([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { + struct lib9p_msg_Rremove *out = out_buf; + [[gnu::unused]] void *extra = &out[1]; + uint32_t net_offset = 0; + net_offset += 4; + net_offset += 1; + UNMARSHAL_U16LE(ctx, out->tag); +} + +#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */ +#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u +static void unmarshal_Tstat([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { + struct lib9p_msg_Tstat *out = out_buf; + [[gnu::unused]] void *extra = &out[1]; + uint32_t net_offset = 0; + net_offset += 4; + net_offset += 1; + UNMARSHAL_U16LE(ctx, out->tag); + UNMARSHAL_U32LE(ctx, out->fid); +} + +static void unmarshal_Rstat([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { + struct lib9p_msg_Rstat *out = out_buf; + [[gnu::unused]] void *extra = &out[1]; + uint32_t net_offset = 0; + net_offset += 4; + net_offset += 1; + UNMARSHAL_U16LE(ctx, out->tag); + net_offset += 2; + net_offset += 2; + UNMARSHAL_U16LE(ctx, out->stat.kern_type); + UNMARSHAL_U32LE(ctx, out->stat.kern_dev); + UNMARSHAL_U8LE(ctx, out->stat.file_qid.type); + UNMARSHAL_U32LE(ctx, out->stat.file_qid.vers); + UNMARSHAL_U64LE(ctx, out->stat.file_qid.path); + UNMARSHAL_U32LE(ctx, out->stat.file_mode); + UNMARSHAL_U32LE(ctx, out->stat.file_atime); + UNMARSHAL_U32LE(ctx, out->stat.file_mtime); + UNMARSHAL_U64LE(ctx, out->stat.file_size); + UNMARSHAL_U16LE(ctx, out->stat.file_name.len); + UNMARSHAL_BYTES(ctx, out->stat.file_name.utf8, out->stat.file_name.len); + UNMARSHAL_U16LE(ctx, out->stat.file_owner_uid.len); + UNMARSHAL_BYTES(ctx, out->stat.file_owner_uid.utf8, out->stat.file_owner_uid.len); + UNMARSHAL_U16LE(ctx, out->stat.file_owner_gid.len); + UNMARSHAL_BYTES(ctx, out->stat.file_owner_gid.utf8, out->stat.file_owner_gid.len); + UNMARSHAL_U16LE(ctx, out->stat.file_last_modified_uid.len); + UNMARSHAL_BYTES(ctx, out->stat.file_last_modified_uid.utf8, out->stat.file_last_modified_uid.len); +#if CONFIG_9P_ENABLE_9P2000_u + if (is_ver(ctx, 9P2000_u)) { + UNMARSHAL_U16LE(ctx, out->stat.file_extension.len); + UNMARSHAL_BYTES(ctx, out->stat.file_extension.utf8, out->stat.file_extension.len); + UNMARSHAL_U32LE(ctx, out->stat.file_owner_n_uid); + UNMARSHAL_U32LE(ctx, out->stat.file_owner_n_gid); + UNMARSHAL_U32LE(ctx, out->stat.file_last_modified_n_uid); + } +#endif /* CONFIG_9P_ENABLE_9P2000_u */ +} + +static void unmarshal_Twstat([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { + struct lib9p_msg_Twstat *out = out_buf; + [[gnu::unused]] void *extra = &out[1]; + uint32_t net_offset = 0; + net_offset += 4; + net_offset += 1; + UNMARSHAL_U16LE(ctx, out->tag); + UNMARSHAL_U32LE(ctx, out->fid); + net_offset += 2; + net_offset += 2; + UNMARSHAL_U16LE(ctx, out->stat.kern_type); + UNMARSHAL_U32LE(ctx, out->stat.kern_dev); + UNMARSHAL_U8LE(ctx, out->stat.file_qid.type); + UNMARSHAL_U32LE(ctx, out->stat.file_qid.vers); + UNMARSHAL_U64LE(ctx, out->stat.file_qid.path); + UNMARSHAL_U32LE(ctx, out->stat.file_mode); + UNMARSHAL_U32LE(ctx, out->stat.file_atime); + UNMARSHAL_U32LE(ctx, out->stat.file_mtime); + UNMARSHAL_U64LE(ctx, out->stat.file_size); + UNMARSHAL_U16LE(ctx, out->stat.file_name.len); + UNMARSHAL_BYTES(ctx, out->stat.file_name.utf8, out->stat.file_name.len); + UNMARSHAL_U16LE(ctx, out->stat.file_owner_uid.len); + UNMARSHAL_BYTES(ctx, out->stat.file_owner_uid.utf8, out->stat.file_owner_uid.len); + UNMARSHAL_U16LE(ctx, out->stat.file_owner_gid.len); + UNMARSHAL_BYTES(ctx, out->stat.file_owner_gid.utf8, out->stat.file_owner_gid.len); + UNMARSHAL_U16LE(ctx, out->stat.file_last_modified_uid.len); + UNMARSHAL_BYTES(ctx, out->stat.file_last_modified_uid.utf8, out->stat.file_last_modified_uid.len); +#if CONFIG_9P_ENABLE_9P2000_u + if (is_ver(ctx, 9P2000_u)) { + UNMARSHAL_U16LE(ctx, out->stat.file_extension.len); + UNMARSHAL_BYTES(ctx, out->stat.file_extension.utf8, out->stat.file_extension.len); + UNMARSHAL_U32LE(ctx, out->stat.file_owner_n_uid); + UNMARSHAL_U32LE(ctx, out->stat.file_owner_n_gid); + UNMARSHAL_U32LE(ctx, out->stat.file_last_modified_n_uid); + } +#endif /* CONFIG_9P_ENABLE_9P2000_u */ +} + +static void unmarshal_Rwstat([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { + struct lib9p_msg_Rwstat *out = out_buf; + [[gnu::unused]] void *extra = &out[1]; + uint32_t net_offset = 0; + net_offset += 4; + net_offset += 1; + UNMARSHAL_U16LE(ctx, out->tag); +} + +#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */ +#if CONFIG_9P_ENABLE_9P2000_p9p +static void unmarshal_Topenfd([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { + struct lib9p_msg_Topenfd *out = out_buf; + [[gnu::unused]] void *extra = &out[1]; + uint32_t net_offset = 0; + net_offset += 4; + net_offset += 1; + UNMARSHAL_U16LE(ctx, out->tag); + UNMARSHAL_U32LE(ctx, out->fid); + UNMARSHAL_U8LE(ctx, out->mode); +} + +static void unmarshal_Ropenfd([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { + struct lib9p_msg_Ropenfd *out = out_buf; + [[gnu::unused]] void *extra = &out[1]; + uint32_t net_offset = 0; + net_offset += 4; + net_offset += 1; + UNMARSHAL_U16LE(ctx, out->tag); + UNMARSHAL_U8LE(ctx, out->qid.type); + UNMARSHAL_U32LE(ctx, out->qid.vers); + UNMARSHAL_U64LE(ctx, out->qid.path); + UNMARSHAL_U32LE(ctx, out->iounit); + UNMARSHAL_U32LE(ctx, out->unixfd); +} + +#endif /* CONFIG_9P_ENABLE_9P2000_p9p */ +#if CONFIG_9P_ENABLE_9P2000_L +static void unmarshal_Rlerror([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { + struct lib9p_msg_Rlerror *out = out_buf; + [[gnu::unused]] void *extra = &out[1]; + uint32_t net_offset = 0; + net_offset += 4; + net_offset += 1; + UNMARSHAL_U16LE(ctx, out->tag); + UNMARSHAL_U32LE(ctx, out->errnum); +} + +static void unmarshal_Tstatfs([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { + struct lib9p_msg_Tstatfs *out = out_buf; + [[gnu::unused]] void *extra = &out[1]; + uint32_t net_offset = 0; + net_offset += 4; + net_offset += 1; + UNMARSHAL_U16LE(ctx, out->tag); + UNMARSHAL_U32LE(ctx, out->fid); +} + +static void unmarshal_Rstatfs([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { + struct lib9p_msg_Rstatfs *out = out_buf; + [[gnu::unused]] void *extra = &out[1]; + uint32_t net_offset = 0; + net_offset += 4; + net_offset += 1; + UNMARSHAL_U16LE(ctx, out->tag); + UNMARSHAL_U32LE(ctx, out->type); + UNMARSHAL_U32LE(ctx, out->bsize); + UNMARSHAL_U64LE(ctx, out->blocks); + UNMARSHAL_U64LE(ctx, out->bfree); + UNMARSHAL_U64LE(ctx, out->bavail); + UNMARSHAL_U64LE(ctx, out->files); + UNMARSHAL_U64LE(ctx, out->ffree); + UNMARSHAL_U64LE(ctx, out->fsid); + UNMARSHAL_U32LE(ctx, out->namelen); +} + +static void unmarshal_Tlopen([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { + struct lib9p_msg_Tlopen *out = out_buf; + [[gnu::unused]] void *extra = &out[1]; + uint32_t net_offset = 0; + net_offset += 4; + net_offset += 1; + UNMARSHAL_U16LE(ctx, out->tag); + UNMARSHAL_U32LE(ctx, out->fid); + UNMARSHAL_U32LE(ctx, out->flags); +} + +static void unmarshal_Rlopen([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { + struct lib9p_msg_Rlopen *out = out_buf; + [[gnu::unused]] void *extra = &out[1]; + uint32_t net_offset = 0; + net_offset += 4; + net_offset += 1; + UNMARSHAL_U16LE(ctx, out->tag); + UNMARSHAL_U8LE(ctx, out->qid.type); + UNMARSHAL_U32LE(ctx, out->qid.vers); + UNMARSHAL_U64LE(ctx, out->qid.path); + UNMARSHAL_U32LE(ctx, out->iounit); +} + +static void unmarshal_Tlcreate([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { + struct lib9p_msg_Tlcreate *out = out_buf; + [[gnu::unused]] void *extra = &out[1]; + uint32_t net_offset = 0; + net_offset += 4; + net_offset += 1; + UNMARSHAL_U16LE(ctx, out->tag); + UNMARSHAL_U32LE(ctx, out->fid); + UNMARSHAL_U16LE(ctx, out->name.len); + UNMARSHAL_BYTES(ctx, out->name.utf8, out->name.len); + UNMARSHAL_U32LE(ctx, out->flags); + UNMARSHAL_U32LE(ctx, out->mode); + UNMARSHAL_U32LE(ctx, out->gid); +} + +static void unmarshal_Rlcreate([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { + struct lib9p_msg_Rlcreate *out = out_buf; + [[gnu::unused]] void *extra = &out[1]; + uint32_t net_offset = 0; + net_offset += 4; + net_offset += 1; + UNMARSHAL_U16LE(ctx, out->tag); + UNMARSHAL_U8LE(ctx, out->qid.type); + UNMARSHAL_U32LE(ctx, out->qid.vers); + UNMARSHAL_U64LE(ctx, out->qid.path); + UNMARSHAL_U32LE(ctx, out->iounit); +} + +static void unmarshal_Tsymlink([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { + struct lib9p_msg_Tsymlink *out = out_buf; + [[gnu::unused]] void *extra = &out[1]; + uint32_t net_offset = 0; + net_offset += 4; + net_offset += 1; + UNMARSHAL_U16LE(ctx, out->tag); + UNMARSHAL_U32LE(ctx, out->fid); + UNMARSHAL_U16LE(ctx, out->name.len); + UNMARSHAL_BYTES(ctx, out->name.utf8, out->name.len); + UNMARSHAL_U16LE(ctx, out->symtgt.len); + UNMARSHAL_BYTES(ctx, out->symtgt.utf8, out->symtgt.len); + UNMARSHAL_U32LE(ctx, out->gid); +} + +static void unmarshal_Rsymlink([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { + struct lib9p_msg_Rsymlink *out = out_buf; + [[gnu::unused]] void *extra = &out[1]; + uint32_t net_offset = 0; + net_offset += 4; + net_offset += 1; + UNMARSHAL_U16LE(ctx, out->tag); + UNMARSHAL_U8LE(ctx, out->qid.type); + UNMARSHAL_U32LE(ctx, out->qid.vers); + UNMARSHAL_U64LE(ctx, out->qid.path); +} + +static void unmarshal_Tmknod([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { + struct lib9p_msg_Tmknod *out = out_buf; + [[gnu::unused]] void *extra = &out[1]; + uint32_t net_offset = 0; + net_offset += 4; + net_offset += 1; + UNMARSHAL_U16LE(ctx, out->tag); + UNMARSHAL_U32LE(ctx, out->dfid); + UNMARSHAL_U16LE(ctx, out->name.len); + UNMARSHAL_BYTES(ctx, out->name.utf8, out->name.len); + UNMARSHAL_U32LE(ctx, out->mode); + UNMARSHAL_U32LE(ctx, out->major); + UNMARSHAL_U32LE(ctx, out->minor); + UNMARSHAL_U32LE(ctx, out->gid); +} + +static void unmarshal_Rmknod([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { + struct lib9p_msg_Rmknod *out = out_buf; + [[gnu::unused]] void *extra = &out[1]; + uint32_t net_offset = 0; + net_offset += 4; + net_offset += 1; + UNMARSHAL_U16LE(ctx, out->tag); + UNMARSHAL_U8LE(ctx, out->qid.type); + UNMARSHAL_U32LE(ctx, out->qid.vers); + UNMARSHAL_U64LE(ctx, out->qid.path); +} + +static void unmarshal_Trename([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { + struct lib9p_msg_Trename *out = out_buf; + [[gnu::unused]] void *extra = &out[1]; + uint32_t net_offset = 0; + net_offset += 4; + net_offset += 1; + UNMARSHAL_U16LE(ctx, out->tag); + UNMARSHAL_U32LE(ctx, out->fid); + UNMARSHAL_U32LE(ctx, out->dfid); + UNMARSHAL_U16LE(ctx, out->name.len); + UNMARSHAL_BYTES(ctx, out->name.utf8, out->name.len); +} + +static void unmarshal_Rrename([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { + struct lib9p_msg_Rrename *out = out_buf; + [[gnu::unused]] void *extra = &out[1]; + uint32_t net_offset = 0; + net_offset += 4; + net_offset += 1; + UNMARSHAL_U16LE(ctx, out->tag); +} + +static void unmarshal_Treadlink([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { + struct lib9p_msg_Treadlink *out = out_buf; + [[gnu::unused]] void *extra = &out[1]; + uint32_t net_offset = 0; + net_offset += 4; + net_offset += 1; + UNMARSHAL_U16LE(ctx, out->tag); + UNMARSHAL_U32LE(ctx, out->fid); +} + +static void unmarshal_Rreadlink([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { + struct lib9p_msg_Rreadlink *out = out_buf; + [[gnu::unused]] void *extra = &out[1]; + uint32_t net_offset = 0; + net_offset += 4; + net_offset += 1; + UNMARSHAL_U16LE(ctx, out->tag); + UNMARSHAL_U16LE(ctx, out->target.len); + UNMARSHAL_BYTES(ctx, out->target.utf8, out->target.len); +} + +static void unmarshal_Tgetattr([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { + struct lib9p_msg_Tgetattr *out = out_buf; + [[gnu::unused]] void *extra = &out[1]; + uint32_t net_offset = 0; + net_offset += 4; + net_offset += 1; + UNMARSHAL_U16LE(ctx, out->tag); + UNMARSHAL_U32LE(ctx, out->fid); + UNMARSHAL_U64LE(ctx, out->request_mask); +} + +static void unmarshal_Rgetattr([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { + struct lib9p_msg_Rgetattr *out = out_buf; + [[gnu::unused]] void *extra = &out[1]; + uint32_t net_offset = 0; + net_offset += 4; + net_offset += 1; + UNMARSHAL_U16LE(ctx, out->tag); + UNMARSHAL_U64LE(ctx, out->valid); + UNMARSHAL_U8LE(ctx, out->qid.type); + UNMARSHAL_U32LE(ctx, out->qid.vers); + UNMARSHAL_U64LE(ctx, out->qid.path); + UNMARSHAL_U32LE(ctx, out->mode); + UNMARSHAL_U32LE(ctx, out->uid); + UNMARSHAL_U32LE(ctx, out->gid); + UNMARSHAL_U64LE(ctx, out->nlink); + UNMARSHAL_U64LE(ctx, out->rdev); + UNMARSHAL_U64LE(ctx, out->filesize); + UNMARSHAL_U64LE(ctx, out->blksize); + UNMARSHAL_U64LE(ctx, out->blocks); + UNMARSHAL_U64LE(ctx, out->atime_sec); + UNMARSHAL_U64LE(ctx, out->atime_nsec); + UNMARSHAL_U64LE(ctx, out->mtime_sec); + UNMARSHAL_U64LE(ctx, out->mtime_nsec); + UNMARSHAL_U64LE(ctx, out->ctime_sec); + UNMARSHAL_U64LE(ctx, out->ctime_nsec); + UNMARSHAL_U64LE(ctx, out->btime_sec); + UNMARSHAL_U64LE(ctx, out->btime_nsec); + UNMARSHAL_U64LE(ctx, out->gen); + UNMARSHAL_U64LE(ctx, out->data_version); +} + +static void unmarshal_Tsetattr([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { + struct lib9p_msg_Tsetattr *out = out_buf; + [[gnu::unused]] void *extra = &out[1]; + uint32_t net_offset = 0; + net_offset += 4; + net_offset += 1; + UNMARSHAL_U16LE(ctx, out->tag); + UNMARSHAL_U32LE(ctx, out->fid); + UNMARSHAL_U32LE(ctx, out->valid); + UNMARSHAL_U32LE(ctx, out->mode); + UNMARSHAL_U32LE(ctx, out->uid); + UNMARSHAL_U32LE(ctx, out->gid); + UNMARSHAL_U64LE(ctx, out->filesize); + UNMARSHAL_U64LE(ctx, out->atime_sec); + UNMARSHAL_U64LE(ctx, out->atime_nsec); + UNMARSHAL_U64LE(ctx, out->mtime_sec); + UNMARSHAL_U64LE(ctx, out->mtime_nsec); +} + +static void unmarshal_Rsetattr([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { + struct lib9p_msg_Rsetattr *out = out_buf; + [[gnu::unused]] void *extra = &out[1]; + uint32_t net_offset = 0; + net_offset += 4; + net_offset += 1; + UNMARSHAL_U16LE(ctx, out->tag); +} + +static void unmarshal_Txattrwalk([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { + struct lib9p_msg_Txattrwalk *out = out_buf; + [[gnu::unused]] void *extra = &out[1]; + uint32_t net_offset = 0; + net_offset += 4; + net_offset += 1; + UNMARSHAL_U16LE(ctx, out->tag); + UNMARSHAL_U32LE(ctx, out->fid); + UNMARSHAL_U32LE(ctx, out->newfid); + UNMARSHAL_U16LE(ctx, out->name.len); + UNMARSHAL_BYTES(ctx, out->name.utf8, out->name.len); +} + +static void unmarshal_Rxattrwalk([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { + struct lib9p_msg_Rxattrwalk *out = out_buf; + [[gnu::unused]] void *extra = &out[1]; + uint32_t net_offset = 0; + net_offset += 4; + net_offset += 1; + UNMARSHAL_U16LE(ctx, out->tag); + UNMARSHAL_U64LE(ctx, out->attr_size); +} + +static void unmarshal_Txattrcreate([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { + struct lib9p_msg_Txattrcreate *out = out_buf; + [[gnu::unused]] void *extra = &out[1]; + uint32_t net_offset = 0; + net_offset += 4; + net_offset += 1; + UNMARSHAL_U16LE(ctx, out->tag); + UNMARSHAL_U32LE(ctx, out->fid); + UNMARSHAL_U16LE(ctx, out->name.len); + UNMARSHAL_BYTES(ctx, out->name.utf8, out->name.len); + UNMARSHAL_U64LE(ctx, out->attr_size); + UNMARSHAL_U32LE(ctx, out->flags); +} + +static void unmarshal_Rxattrcreate([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { + struct lib9p_msg_Rxattrcreate *out = out_buf; + [[gnu::unused]] void *extra = &out[1]; + uint32_t net_offset = 0; + net_offset += 4; + net_offset += 1; + UNMARSHAL_U16LE(ctx, out->tag); +} + +static void unmarshal_Treaddir([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { + struct lib9p_msg_Treaddir *out = out_buf; + [[gnu::unused]] void *extra = &out[1]; + uint32_t net_offset = 0; + net_offset += 4; + net_offset += 1; + UNMARSHAL_U16LE(ctx, out->tag); + UNMARSHAL_U32LE(ctx, out->fid); + UNMARSHAL_U64LE(ctx, out->offset); + UNMARSHAL_U32LE(ctx, out->count); +} + +static void unmarshal_Rreaddir([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { + struct lib9p_msg_Rreaddir *out = out_buf; + [[gnu::unused]] void *extra = &out[1]; + uint32_t net_offset = 0; + net_offset += 4; + net_offset += 1; + UNMARSHAL_U16LE(ctx, out->tag); + UNMARSHAL_U32LE(ctx, out->count); + UNMARSHAL_BYTES(ctx, out->data, out->count); +} + +static void unmarshal_Tfsync([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { + struct lib9p_msg_Tfsync *out = out_buf; + [[gnu::unused]] void *extra = &out[1]; + uint32_t net_offset = 0; + net_offset += 4; + net_offset += 1; + UNMARSHAL_U16LE(ctx, out->tag); + UNMARSHAL_U32LE(ctx, out->fid); + UNMARSHAL_U32LE(ctx, out->datasync); +} + +static void unmarshal_Rfsync([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { + struct lib9p_msg_Rfsync *out = out_buf; + [[gnu::unused]] void *extra = &out[1]; + uint32_t net_offset = 0; + net_offset += 4; + net_offset += 1; + UNMARSHAL_U16LE(ctx, out->tag); +} + +static void unmarshal_Tlock([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { + struct lib9p_msg_Tlock *out = out_buf; + [[gnu::unused]] void *extra = &out[1]; + uint32_t net_offset = 0; + net_offset += 4; + net_offset += 1; + UNMARSHAL_U16LE(ctx, out->tag); + UNMARSHAL_U32LE(ctx, out->fid); + UNMARSHAL_U8LE(ctx, out->type); + UNMARSHAL_U32LE(ctx, out->flags); + UNMARSHAL_U64LE(ctx, out->start); + UNMARSHAL_U64LE(ctx, out->length); + UNMARSHAL_U32LE(ctx, out->proc_id); + UNMARSHAL_U16LE(ctx, out->client_id.len); + UNMARSHAL_BYTES(ctx, out->client_id.utf8, out->client_id.len); +} + +static void unmarshal_Rlock([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { + struct lib9p_msg_Rlock *out = out_buf; + [[gnu::unused]] void *extra = &out[1]; + uint32_t net_offset = 0; + net_offset += 4; + net_offset += 1; + UNMARSHAL_U16LE(ctx, out->tag); + UNMARSHAL_U8LE(ctx, out->status); +} + +static void unmarshal_Tgetlock([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { + struct lib9p_msg_Tgetlock *out = out_buf; + [[gnu::unused]] void *extra = &out[1]; + uint32_t net_offset = 0; + net_offset += 4; + net_offset += 1; + UNMARSHAL_U16LE(ctx, out->tag); + UNMARSHAL_U32LE(ctx, out->fid); + UNMARSHAL_U8LE(ctx, out->type); + UNMARSHAL_U64LE(ctx, out->start); + UNMARSHAL_U64LE(ctx, out->length); + UNMARSHAL_U32LE(ctx, out->proc_id); + UNMARSHAL_U16LE(ctx, out->client_id.len); + UNMARSHAL_BYTES(ctx, out->client_id.utf8, out->client_id.len); +} + +static void unmarshal_Rgetlock([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { + struct lib9p_msg_Rgetlock *out = out_buf; + [[gnu::unused]] void *extra = &out[1]; + uint32_t net_offset = 0; + net_offset += 4; + net_offset += 1; + UNMARSHAL_U16LE(ctx, out->tag); + UNMARSHAL_U8LE(ctx, out->type); + UNMARSHAL_U64LE(ctx, out->start); + UNMARSHAL_U64LE(ctx, out->length); + UNMARSHAL_U32LE(ctx, out->proc_id); + UNMARSHAL_U16LE(ctx, out->client_id.len); + UNMARSHAL_BYTES(ctx, out->client_id.utf8, out->client_id.len); +} + +static void unmarshal_Tlink([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { + struct lib9p_msg_Tlink *out = out_buf; + [[gnu::unused]] void *extra = &out[1]; + uint32_t net_offset = 0; + net_offset += 4; + net_offset += 1; + UNMARSHAL_U16LE(ctx, out->tag); + UNMARSHAL_U32LE(ctx, out->dfid); + UNMARSHAL_U32LE(ctx, out->fid); + UNMARSHAL_U16LE(ctx, out->name.len); + UNMARSHAL_BYTES(ctx, out->name.utf8, out->name.len); +} + +static void unmarshal_Rlink([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { + struct lib9p_msg_Rlink *out = out_buf; + [[gnu::unused]] void *extra = &out[1]; + uint32_t net_offset = 0; + net_offset += 4; + net_offset += 1; + UNMARSHAL_U16LE(ctx, out->tag); +} + +static void unmarshal_Tmkdir([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { + struct lib9p_msg_Tmkdir *out = out_buf; + [[gnu::unused]] void *extra = &out[1]; + uint32_t net_offset = 0; + net_offset += 4; + net_offset += 1; + UNMARSHAL_U16LE(ctx, out->tag); + UNMARSHAL_U32LE(ctx, out->dfid); + UNMARSHAL_U16LE(ctx, out->name.len); + UNMARSHAL_BYTES(ctx, out->name.utf8, out->name.len); + UNMARSHAL_U32LE(ctx, out->mode); + UNMARSHAL_U32LE(ctx, out->gid); +} + +static void unmarshal_Rmkdir([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { + struct lib9p_msg_Rmkdir *out = out_buf; + [[gnu::unused]] void *extra = &out[1]; + uint32_t net_offset = 0; + net_offset += 4; + net_offset += 1; + UNMARSHAL_U16LE(ctx, out->tag); + UNMARSHAL_U8LE(ctx, out->qid.type); + UNMARSHAL_U32LE(ctx, out->qid.vers); + UNMARSHAL_U64LE(ctx, out->qid.path); +} + +static void unmarshal_Trenameat([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { + struct lib9p_msg_Trenameat *out = out_buf; + [[gnu::unused]] void *extra = &out[1]; + uint32_t net_offset = 0; + net_offset += 4; + net_offset += 1; + UNMARSHAL_U16LE(ctx, out->tag); + UNMARSHAL_U32LE(ctx, out->olddirfid); + UNMARSHAL_U16LE(ctx, out->oldname.len); + UNMARSHAL_BYTES(ctx, out->oldname.utf8, out->oldname.len); + UNMARSHAL_U32LE(ctx, out->newdirfid); + UNMARSHAL_U16LE(ctx, out->newname.len); + UNMARSHAL_BYTES(ctx, out->newname.utf8, out->newname.len); +} + +static void unmarshal_Rrenameat([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { + struct lib9p_msg_Rrenameat *out = out_buf; + [[gnu::unused]] void *extra = &out[1]; + uint32_t net_offset = 0; + net_offset += 4; + net_offset += 1; + UNMARSHAL_U16LE(ctx, out->tag); +} + +static void unmarshal_Tunlinkat([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { + struct lib9p_msg_Tunlinkat *out = out_buf; + [[gnu::unused]] void *extra = &out[1]; + uint32_t net_offset = 0; + net_offset += 4; + net_offset += 1; + UNMARSHAL_U16LE(ctx, out->tag); + UNMARSHAL_U32LE(ctx, out->dirfd); + UNMARSHAL_U16LE(ctx, out->name.len); + UNMARSHAL_BYTES(ctx, out->name.utf8, out->name.len); + UNMARSHAL_U32LE(ctx, out->flags); +} + +static void unmarshal_Runlinkat([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { + struct lib9p_msg_Runlinkat *out = out_buf; + [[gnu::unused]] void *extra = &out[1]; + uint32_t net_offset = 0; + net_offset += 4; + net_offset += 1; + UNMARSHAL_U16LE(ctx, out->tag); +} + +#endif /* CONFIG_9P_ENABLE_9P2000_L */ +#if CONFIG_9P_ENABLE_9P2000_e +static void unmarshal_Tsession([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { + struct lib9p_msg_Tsession *out = out_buf; + [[gnu::unused]] void *extra = &out[1]; + uint32_t net_offset = 0; + net_offset += 4; + net_offset += 1; + UNMARSHAL_U16LE(ctx, out->tag); + UNMARSHAL_U64LE(ctx, out->key); +} + +static void unmarshal_Rsession([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { + struct lib9p_msg_Rsession *out = out_buf; + [[gnu::unused]] void *extra = &out[1]; + uint32_t net_offset = 0; + net_offset += 4; + net_offset += 1; + UNMARSHAL_U16LE(ctx, out->tag); +} + +static void unmarshal_Tsread([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { + struct lib9p_msg_Tsread *out = out_buf; + [[gnu::unused]] void *extra = &out[1]; + uint32_t net_offset = 0; + net_offset += 4; + net_offset += 1; + UNMARSHAL_U16LE(ctx, out->tag); + UNMARSHAL_U32LE(ctx, out->fid); + UNMARSHAL_U16LE(ctx, out->nwname); + out->wname = extra; + extra += sizeof(out->wname[0]) * out->nwname; + for (uint16_t i = 0; i < out->nwname; i++) { + UNMARSHAL_U16LE(ctx, out->wname[i].len); + UNMARSHAL_BYTES(ctx, out->wname[i].utf8, out->wname[i].len); + } +} + +static void unmarshal_Rsread([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { + struct lib9p_msg_Rsread *out = out_buf; + [[gnu::unused]] void *extra = &out[1]; + uint32_t net_offset = 0; + net_offset += 4; + net_offset += 1; + UNMARSHAL_U16LE(ctx, out->tag); + UNMARSHAL_U32LE(ctx, out->count); + UNMARSHAL_BYTES(ctx, out->data, out->count); +} + +static void unmarshal_Tswrite([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { + struct lib9p_msg_Tswrite *out = out_buf; + [[gnu::unused]] void *extra = &out[1]; + uint32_t net_offset = 0; + net_offset += 4; + net_offset += 1; + UNMARSHAL_U16LE(ctx, out->tag); + UNMARSHAL_U32LE(ctx, out->fid); + UNMARSHAL_U16LE(ctx, out->nwname); + out->wname = extra; + extra += sizeof(out->wname[0]) * out->nwname; + for (uint16_t i = 0; i < out->nwname; i++) { + UNMARSHAL_U16LE(ctx, out->wname[i].len); + UNMARSHAL_BYTES(ctx, out->wname[i].utf8, out->wname[i].len); + } + UNMARSHAL_U32LE(ctx, out->count); + UNMARSHAL_BYTES(ctx, out->data, out->count); +} + +static void unmarshal_Rswrite([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { + struct lib9p_msg_Rswrite *out = out_buf; + [[gnu::unused]] void *extra = &out[1]; + uint32_t net_offset = 0; + net_offset += 4; + net_offset += 1; + UNMARSHAL_U16LE(ctx, out->tag); + UNMARSHAL_U32LE(ctx, out->count); +} +#endif /* CONFIG_9P_ENABLE_9P2000_e */ + +/* marshal_* ******************************************************************/ + +#define MARSHAL_BYTES_ZEROCOPY(ctx, data, len) \ + if (ret->net_iov[ret->net_iov_cnt-1].iov_len) \ + ret->net_iov_cnt++; \ + ret->net_iov[ret->net_iov_cnt-1].iov_base = data; \ + ret->net_iov[ret->net_iov_cnt-1].iov_len = len; \ + ret->net_iov_cnt++; +#define MARSHAL_BYTES(ctx, data, len) \ + if (!ret->net_iov[ret->net_iov_cnt-1].iov_base) \ + ret->net_iov[ret->net_iov_cnt-1].iov_base = &ret->net_copied[ret->net_copied_size]; \ + memcpy(&ret->net_copied[ret->net_copied_size], data, len); \ + ret->net_copied_size += len; \ + ret->net_iov[ret->net_iov_cnt-1].iov_len += len; +#define MARSHAL_U8LE(ctx, val) \ + if (!ret->net_iov[ret->net_iov_cnt-1].iov_base) \ + ret->net_iov[ret->net_iov_cnt-1].iov_base = &ret->net_copied[ret->net_copied_size]; \ + ret->net_copied[ret->net_copied_size] = val; \ + ret->net_copied_size += 1; \ + ret->net_iov[ret->net_iov_cnt-1].iov_len += 1; +#define MARSHAL_U16LE(ctx, val) \ + if (!ret->net_iov[ret->net_iov_cnt-1].iov_base) \ + ret->net_iov[ret->net_iov_cnt-1].iov_base = &ret->net_copied[ret->net_copied_size]; \ + uint16le_encode(&ret->net_copied[ret->net_copied_size], val); \ + ret->net_copied_size += 2; \ + ret->net_iov[ret->net_iov_cnt-1].iov_len += 2; +#define MARSHAL_U32LE(ctx, val) \ + if (!ret->net_iov[ret->net_iov_cnt-1].iov_base) \ + ret->net_iov[ret->net_iov_cnt-1].iov_base = &ret->net_copied[ret->net_copied_size]; \ + uint32le_encode(&ret->net_copied[ret->net_copied_size], val); \ + ret->net_copied_size += 4; \ + ret->net_iov[ret->net_iov_cnt-1].iov_len += 4; +#define MARSHAL_U64LE(ctx, val) \ + if (!ret->net_iov[ret->net_iov_cnt-1].iov_base) \ + ret->net_iov[ret->net_iov_cnt-1].iov_base = &ret->net_copied[ret->net_copied_size]; \ + uint64le_encode(&ret->net_copied[ret->net_copied_size], val); \ + ret->net_copied_size += 8; \ + ret->net_iov[ret->net_iov_cnt-1].iov_len += 8; + +#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u +static bool marshal_stat(struct lib9p_ctx *ctx, struct lib9p_stat *val, struct _marshal_ret *ret) { + uint32_t needed_size = 49 + val->file_name.len + val->file_owner_uid.len + val->file_owner_gid.len + val->file_last_modified_uid.len; +#if CONFIG_9P_ENABLE_9P2000_u + if is_ver(ctx, 9P2000_u) { + needed_size += 14 + val->file_extension.len; + } +#endif /* CONFIG_9P_ENABLE_9P2000_u */ + if (needed_size > ctx->max_msg_size) { + return true; + } + uint32_t offsetof_end = needed_size; + uint32_t offsetof_kern_type = 2; + MARSHAL_U16LE(ctx, offsetof_end - offsetof_kern_type); + MARSHAL_U16LE(ctx, val->kern_type); + MARSHAL_U32LE(ctx, val->kern_dev); + MARSHAL_U8LE(ctx, val->file_qid.type & qt_masks[ctx->version]); + MARSHAL_U32LE(ctx, val->file_qid.vers); + MARSHAL_U64LE(ctx, val->file_qid.path); + MARSHAL_U32LE(ctx, val->file_mode & dm_masks[ctx->version]); + MARSHAL_U32LE(ctx, val->file_atime); + MARSHAL_U32LE(ctx, val->file_mtime); + MARSHAL_U64LE(ctx, val->file_size); + MARSHAL_U16LE(ctx, val->file_name.len); + MARSHAL_BYTES(ctx, val->file_name.utf8, val->file_name.len); + MARSHAL_U16LE(ctx, val->file_owner_uid.len); + MARSHAL_BYTES(ctx, val->file_owner_uid.utf8, val->file_owner_uid.len); + MARSHAL_U16LE(ctx, val->file_owner_gid.len); + MARSHAL_BYTES(ctx, val->file_owner_gid.utf8, val->file_owner_gid.len); + MARSHAL_U16LE(ctx, val->file_last_modified_uid.len); + MARSHAL_BYTES(ctx, val->file_last_modified_uid.utf8, val->file_last_modified_uid.len); +#if CONFIG_9P_ENABLE_9P2000_u + if (is_ver(ctx, 9P2000_u)) { + MARSHAL_U16LE(ctx, val->file_extension.len); + MARSHAL_BYTES(ctx, val->file_extension.utf8, val->file_extension.len); + MARSHAL_U32LE(ctx, val->file_owner_n_uid); + MARSHAL_U32LE(ctx, val->file_owner_n_gid); + MARSHAL_U32LE(ctx, val->file_last_modified_n_uid); + } +#endif /* CONFIG_9P_ENABLE_9P2000_u */ return false; } -static ALWAYS_INLINE bool _validate_size_host(struct _validate_ctx *ctx, size_t n) { - if (__builtin_add_overflow(ctx->host_extra, n, &ctx->host_extra)) - /* If needed-host-size overflowed size_t, then there's - * no way that actual-net-size will live up to - * that. */ - return lib9p_error(ctx->ctx, LINUX_EBADMSG, "message is too short for content"); +#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */ +#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u +static bool marshal_Tversion(struct lib9p_ctx *ctx, struct lib9p_msg_Tversion *val, struct _marshal_ret *ret) { + uint32_t needed_size = 13 + val->version.len; + if (needed_size > ctx->max_msg_size) { + lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", + "Tversion", + ctx->version ? "negotiated" : "client", + ctx->max_msg_size); + return true; + } + uint32_t offsetof_end = needed_size; + uint32_t offsetof_size = 0; + MARSHAL_U32LE(ctx, offsetof_end - offsetof_size); + MARSHAL_U8LE(ctx, 100); + MARSHAL_U16LE(ctx, val->tag); + MARSHAL_U32LE(ctx, val->max_msg_size); + MARSHAL_U16LE(ctx, val->version.len); + MARSHAL_BYTES_ZEROCOPY(ctx, val->version.utf8, val->version.len); return false; } -static ALWAYS_INLINE bool _validate_list(struct _validate_ctx *ctx, - size_t cnt, - _validate_fn_t item_fn, size_t item_host_size) { - for (size_t i = 0; i < cnt; i++) - if (_validate_size_host(ctx, item_host_size) || item_fn(ctx)) - return true; +static bool marshal_Rversion(struct lib9p_ctx *ctx, struct lib9p_msg_Rversion *val, struct _marshal_ret *ret) { + uint32_t needed_size = 13 + val->version.len; + if (needed_size > ctx->max_msg_size) { + lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", + "Rversion", + ctx->version ? "negotiated" : "server", + ctx->max_msg_size); + return true; + } + uint32_t offsetof_end = needed_size; + uint32_t offsetof_size = 0; + MARSHAL_U32LE(ctx, offsetof_end - offsetof_size); + MARSHAL_U8LE(ctx, 101); + MARSHAL_U16LE(ctx, val->tag); + MARSHAL_U32LE(ctx, val->max_msg_size); + MARSHAL_U16LE(ctx, val->version.len); + MARSHAL_BYTES_ZEROCOPY(ctx, val->version.utf8, val->version.len); return false; } -#define validate_1(ctx) _validate_size_net(ctx, 1) -#define validate_2(ctx) _validate_size_net(ctx, 2) -#define validate_4(ctx) _validate_size_net(ctx, 4) -#define validate_8(ctx) _validate_size_net(ctx, 8) +static bool marshal_Tauth(struct lib9p_ctx *ctx, struct lib9p_msg_Tauth *val, struct _marshal_ret *ret) { + uint32_t needed_size = 15 + val->uname.len + val->aname.len; +#if CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_u + if ( is_ver(ctx, 9P2000_L) || is_ver(ctx, 9P2000_u) ) { + needed_size += 4; + } +#endif /* CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_u */ + if (needed_size > ctx->max_msg_size) { + lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", + "Tauth", + ctx->version ? "negotiated" : "client", + ctx->max_msg_size); + return true; + } + uint32_t offsetof_end = needed_size; + uint32_t offsetof_size = 0; + MARSHAL_U32LE(ctx, offsetof_end - offsetof_size); + MARSHAL_U8LE(ctx, 102); + MARSHAL_U16LE(ctx, val->tag); + MARSHAL_U32LE(ctx, val->afid); + MARSHAL_U16LE(ctx, val->uname.len); + MARSHAL_BYTES_ZEROCOPY(ctx, val->uname.utf8, val->uname.len); + MARSHAL_U16LE(ctx, val->aname.len); + MARSHAL_BYTES_ZEROCOPY(ctx, val->aname.utf8, val->aname.len); +#if CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_u + if (( is_ver(ctx, 9P2000_L) || is_ver(ctx, 9P2000_u) )) { + MARSHAL_U32LE(ctx, val->n_uid); + } +#endif /* CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_u */ + return false; +} -#if defined(CONFIG_9P_ENABLE_9P2000) || defined(CONFIG_9P_ENABLE_9P2000_e) || defined(CONFIG_9P_ENABLE_9P2000_u) -static ALWAYS_INLINE bool validate_tag(struct _validate_ctx *ctx) { - return validate_2(ctx); +static bool marshal_Rauth(struct lib9p_ctx *ctx, struct lib9p_msg_Rauth *val, struct _marshal_ret *ret) { + uint32_t needed_size = 20; + if (needed_size > ctx->max_msg_size) { + lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", + "Rauth", + ctx->version ? "negotiated" : "server", + ctx->max_msg_size); + return true; + } + uint32_t offsetof_end = needed_size; + uint32_t offsetof_size = 0; + MARSHAL_U32LE(ctx, offsetof_end - offsetof_size); + MARSHAL_U8LE(ctx, 103); + MARSHAL_U16LE(ctx, val->tag); + MARSHAL_U8LE(ctx, val->aqid.type & qt_masks[ctx->version]); + MARSHAL_U32LE(ctx, val->aqid.vers); + MARSHAL_U64LE(ctx, val->aqid.path); + return false; } -static ALWAYS_INLINE bool validate_fid(struct _validate_ctx *ctx) { - return validate_4(ctx); +static bool marshal_Tattach(struct lib9p_ctx *ctx, struct lib9p_msg_Tattach *val, struct _marshal_ret *ret) { + uint32_t needed_size = 19 + val->uname.len + val->aname.len; +#if CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_u + if ( is_ver(ctx, 9P2000_L) || is_ver(ctx, 9P2000_u) ) { + needed_size += 4; + } +#endif /* CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_u */ + if (needed_size > ctx->max_msg_size) { + lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", + "Tattach", + ctx->version ? "negotiated" : "client", + ctx->max_msg_size); + return true; + } + uint32_t offsetof_end = needed_size; + uint32_t offsetof_size = 0; + MARSHAL_U32LE(ctx, offsetof_end - offsetof_size); + MARSHAL_U8LE(ctx, 104); + MARSHAL_U16LE(ctx, val->tag); + MARSHAL_U32LE(ctx, val->fid); + MARSHAL_U32LE(ctx, val->afid); + MARSHAL_U16LE(ctx, val->uname.len); + MARSHAL_BYTES_ZEROCOPY(ctx, val->uname.utf8, val->uname.len); + MARSHAL_U16LE(ctx, val->aname.len); + MARSHAL_BYTES_ZEROCOPY(ctx, val->aname.utf8, val->aname.len); +#if CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_u + if (( is_ver(ctx, 9P2000_L) || is_ver(ctx, 9P2000_u) )) { + MARSHAL_U32LE(ctx, val->n_uid); + } +#endif /* CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_u */ + return false; } -static ALWAYS_INLINE bool validate_d(struct _validate_ctx *ctx) { - uint32_t base_offset = ctx->net_offset; - if (validate_4(ctx)) +static bool marshal_Rattach(struct lib9p_ctx *ctx, struct lib9p_msg_Rattach *val, struct _marshal_ret *ret) { + uint32_t needed_size = 20; + if (needed_size > ctx->max_msg_size) { + lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", + "Rattach", + ctx->version ? "negotiated" : "server", + ctx->max_msg_size); return true; - uint32_t len = decode_u32le(&ctx->net_bytes[base_offset]); - return _validate_size_net(ctx, len) || _validate_size_host(ctx, len); + } + uint32_t offsetof_end = needed_size; + uint32_t offsetof_size = 0; + MARSHAL_U32LE(ctx, offsetof_end - offsetof_size); + MARSHAL_U8LE(ctx, 105); + MARSHAL_U16LE(ctx, val->tag); + MARSHAL_U8LE(ctx, val->qid.type & qt_masks[ctx->version]); + MARSHAL_U32LE(ctx, val->qid.vers); + MARSHAL_U64LE(ctx, val->qid.path); + return false; } -static ALWAYS_INLINE bool validate_s(struct _validate_ctx *ctx) { - uint32_t base_offset = ctx->net_offset; - if (validate_2(ctx)) +static bool marshal_Rerror(struct lib9p_ctx *ctx, struct lib9p_msg_Rerror *val, struct _marshal_ret *ret) { + uint32_t needed_size = 9 + val->errstr.len; +#if CONFIG_9P_ENABLE_9P2000_u + if is_ver(ctx, 9P2000_u) { + needed_size += 4; + } +#endif /* CONFIG_9P_ENABLE_9P2000_u */ + if (needed_size > ctx->max_msg_size) { + lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", + "Rerror", + ctx->version ? "negotiated" : "server", + ctx->max_msg_size); return true; - uint16_t len = decode_u16le(&ctx->net_bytes[base_offset]); - if (_validate_size_net(ctx, len) || _validate_size_host(ctx, ((size_t)len)+1)) + } + uint32_t offsetof_end = needed_size; + uint32_t offsetof_size = 0; + MARSHAL_U32LE(ctx, offsetof_end - offsetof_size); + MARSHAL_U8LE(ctx, 107); + MARSHAL_U16LE(ctx, val->tag); + MARSHAL_U16LE(ctx, val->errstr.len); + MARSHAL_BYTES_ZEROCOPY(ctx, val->errstr.utf8, val->errstr.len); +#if CONFIG_9P_ENABLE_9P2000_u + if (is_ver(ctx, 9P2000_u)) { + MARSHAL_U32LE(ctx, val->errnum); + } +#endif /* CONFIG_9P_ENABLE_9P2000_u */ + return false; +} + +static bool marshal_Tflush(struct lib9p_ctx *ctx, struct lib9p_msg_Tflush *val, struct _marshal_ret *ret) { + uint32_t needed_size = 9; + if (needed_size > ctx->max_msg_size) { + lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", + "Tflush", + ctx->version ? "negotiated" : "client", + ctx->max_msg_size); return true; - if (!is_valid_utf8_without_nul(&ctx->net_bytes[base_offset+2], len)) - return lib9p_error(ctx->ctx, LINUX_EBADMSG, "message contains invalid UTF-8"); + } + uint32_t offsetof_end = needed_size; + uint32_t offsetof_size = 0; + MARSHAL_U32LE(ctx, offsetof_end - offsetof_size); + MARSHAL_U8LE(ctx, 108); + MARSHAL_U16LE(ctx, val->tag); + MARSHAL_U16LE(ctx, val->oldtag); return false; } -static const lib9p_dm_t dm_masks[LIB9P_VER_NUM] = { -#if defined(CONFIG_9P_ENABLE_9P2000) - [LIB9P_VER_9P2000] = 0b11101100000000000000000111111111, -#endif /* defined(CONFIG_9P_ENABLE_9P2000) */ -#if defined(CONFIG_9P_ENABLE_9P2000_e) - [LIB9P_VER_9P2000_e] = 0b11101100000000000000000111111111, -#endif /* defined(CONFIG_9P_ENABLE_9P2000_e) */ -#if defined(CONFIG_9P_ENABLE_9P2000_u) - [LIB9P_VER_9P2000_u] = 0b11101100101111000000000111111111, -#endif /* defined(CONFIG_9P_ENABLE_9P2000_u) */ -}; -static ALWAYS_INLINE bool validate_dm(struct _validate_ctx *ctx) { - if (validate_4(ctx)) +static bool marshal_Rflush(struct lib9p_ctx *ctx, struct lib9p_msg_Rflush *val, struct _marshal_ret *ret) { + uint32_t needed_size = 7; + if (needed_size > ctx->max_msg_size) { + lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", + "Rflush", + ctx->version ? "negotiated" : "server", + ctx->max_msg_size); return true; - lib9p_dm_t mask = dm_masks[ctx->ctx->version]; - lib9p_dm_t val = decode_u32le(&ctx->net_bytes[ctx->net_offset-4]); - if (val & ~mask) - return lib9p_errorf(ctx->ctx, LINUX_EBADMSG, "unknown bits in dm bitfield: %#04"PRIx32, val & ~mask); + } + uint32_t offsetof_end = needed_size; + uint32_t offsetof_size = 0; + MARSHAL_U32LE(ctx, offsetof_end - offsetof_size); + MARSHAL_U8LE(ctx, 109); + MARSHAL_U16LE(ctx, val->tag); return false; } -static const lib9p_qt_t qt_masks[LIB9P_VER_NUM] = { -#if defined(CONFIG_9P_ENABLE_9P2000) - [LIB9P_VER_9P2000] = 0b11101100, -#endif /* defined(CONFIG_9P_ENABLE_9P2000) */ -#if defined(CONFIG_9P_ENABLE_9P2000_e) - [LIB9P_VER_9P2000_e] = 0b11101100, -#endif /* defined(CONFIG_9P_ENABLE_9P2000_e) */ -#if defined(CONFIG_9P_ENABLE_9P2000_u) - [LIB9P_VER_9P2000_u] = 0b11101110, -#endif /* defined(CONFIG_9P_ENABLE_9P2000_u) */ -}; -static ALWAYS_INLINE bool validate_qt(struct _validate_ctx *ctx) { - if (validate_1(ctx)) - return true; - lib9p_qt_t mask = qt_masks[ctx->ctx->version]; - lib9p_qt_t val = decode_u8le(&ctx->net_bytes[ctx->net_offset-1]); - if (val & ~mask) - return lib9p_errorf(ctx->ctx, LINUX_EBADMSG, "unknown bits in qt bitfield: %#01"PRIx8, val & ~mask); - return false; -} - -static ALWAYS_INLINE bool validate_qid(struct _validate_ctx *ctx) { - return false - || validate_qt(ctx) - || validate_4(ctx) - || validate_8(ctx) - ; -} - -static ALWAYS_INLINE bool validate_stat(struct _validate_ctx *ctx) { - uint16_t stat_size; - uint32_t _kern_type_offset; - return false - || (validate_2(ctx) || ({ stat_size = decode_u16le(&ctx->net_bytes[ctx->net_offset-2]); false; })) - || ({ _kern_type_offset = ctx->net_offset; validate_2(ctx); }) - || validate_4(ctx) - || validate_qid(ctx) - || validate_dm(ctx) - || validate_4(ctx) - || validate_4(ctx) - || validate_8(ctx) - || validate_s(ctx) - || validate_s(ctx) - || validate_s(ctx) - || validate_s(ctx) -#if defined(CONFIG_9P_ENABLE_9P2000_u) - || ( (ctx->ctx->version==LIB9P_VER_9P2000_u) && validate_s(ctx) ) - || ( (ctx->ctx->version==LIB9P_VER_9P2000_u) && validate_4(ctx) ) - || ( (ctx->ctx->version==LIB9P_VER_9P2000_u) && validate_4(ctx) ) - || ( (ctx->ctx->version==LIB9P_VER_9P2000_u) && validate_4(ctx) ) -#endif /* defined(CONFIG_9P_ENABLE_9P2000_u) */ - || ({ uint32_t exp = ctx->net_offset - _kern_type_offset; (((uint32_t)stat_size) != exp) && - lib9p_errorf(ctx->ctx, LINUX_EBADMSG, "stat_size value is wrong (actual:%"PRIu32" != correct:%"PRIu32")", (uint32_t)stat_size, exp); }) - ; +static bool marshal_Twalk(struct lib9p_ctx *ctx, struct lib9p_msg_Twalk *val, struct _marshal_ret *ret) { + uint32_t needed_size = 17; + for (uint16_t i = 0; i < val->nwname; i++) { + needed_size += 2 + val->wname[i].len; + } + if (needed_size > ctx->max_msg_size) { + lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", + "Twalk", + ctx->version ? "negotiated" : "client", + ctx->max_msg_size); + return true; + } + uint32_t offsetof_end = needed_size; + uint32_t offsetof_size = 0; + MARSHAL_U32LE(ctx, offsetof_end - offsetof_size); + MARSHAL_U8LE(ctx, 110); + MARSHAL_U16LE(ctx, val->tag); + MARSHAL_U32LE(ctx, val->fid); + MARSHAL_U32LE(ctx, val->newfid); + MARSHAL_U16LE(ctx, val->nwname); + for (uint16_t i = 0; i < val->nwname; i++) { + MARSHAL_U16LE(ctx, val->wname[i].len); + MARSHAL_BYTES_ZEROCOPY(ctx, val->wname[i].utf8, val->wname[i].len); + } + return false; } -static const lib9p_o_t o_masks[LIB9P_VER_NUM] = { -#if defined(CONFIG_9P_ENABLE_9P2000) - [LIB9P_VER_9P2000] = 0b01010011, -#endif /* defined(CONFIG_9P_ENABLE_9P2000) */ -#if defined(CONFIG_9P_ENABLE_9P2000_e) - [LIB9P_VER_9P2000_e] = 0b01010011, -#endif /* defined(CONFIG_9P_ENABLE_9P2000_e) */ -#if defined(CONFIG_9P_ENABLE_9P2000_u) - [LIB9P_VER_9P2000_u] = 0b01010011, -#endif /* defined(CONFIG_9P_ENABLE_9P2000_u) */ -}; -static ALWAYS_INLINE bool validate_o(struct _validate_ctx *ctx) { - if (validate_1(ctx)) - return true; - lib9p_o_t mask = o_masks[ctx->ctx->version]; - lib9p_o_t val = decode_u8le(&ctx->net_bytes[ctx->net_offset-1]); - if (val & ~mask) - return lib9p_errorf(ctx->ctx, LINUX_EBADMSG, "unknown bits in o bitfield: %#01"PRIx8, val & ~mask); - return false; -} - -static FLATTEN bool validate_Tversion(struct _validate_ctx *ctx) { - uint32_t size; - uint8_t typ; - uint32_t _size_offset; - return false - || (({ _size_offset = ctx->net_offset; validate_4(ctx); }) || ({ size = decode_u32le(&ctx->net_bytes[ctx->net_offset-4]); false; })) - || (validate_1(ctx) || ({ typ = decode_u8le(&ctx->net_bytes[ctx->net_offset-1]); false; })) - || validate_tag(ctx) - || validate_4(ctx) - || validate_s(ctx) - || ({ uint32_t exp = ctx->net_offset - _size_offset; (((uint32_t)size) != exp) && - lib9p_errorf(ctx->ctx, LINUX_EBADMSG, "size value is wrong (actual:%"PRIu32" != correct:%"PRIu32")", (uint32_t)size, exp); }) - || ({ uint32_t exp = 100; (((uint32_t)typ) != exp) && - lib9p_errorf(ctx->ctx, LINUX_EBADMSG, "typ value is wrong (actual:%"PRIu32" != correct:%"PRIu32")", (uint32_t)typ, exp); }) - ; -} - -static FLATTEN bool validate_Rversion(struct _validate_ctx *ctx) { - uint32_t size; - uint8_t typ; - uint32_t _size_offset; - return false - || (({ _size_offset = ctx->net_offset; validate_4(ctx); }) || ({ size = decode_u32le(&ctx->net_bytes[ctx->net_offset-4]); false; })) - || (validate_1(ctx) || ({ typ = decode_u8le(&ctx->net_bytes[ctx->net_offset-1]); false; })) - || validate_tag(ctx) - || validate_4(ctx) - || validate_s(ctx) - || ({ uint32_t exp = ctx->net_offset - _size_offset; (((uint32_t)size) != exp) && - lib9p_errorf(ctx->ctx, LINUX_EBADMSG, "size value is wrong (actual:%"PRIu32" != correct:%"PRIu32")", (uint32_t)size, exp); }) - || ({ uint32_t exp = 101; (((uint32_t)typ) != exp) && - lib9p_errorf(ctx->ctx, LINUX_EBADMSG, "typ value is wrong (actual:%"PRIu32" != correct:%"PRIu32")", (uint32_t)typ, exp); }) - ; -} - -static FLATTEN bool validate_Tauth(struct _validate_ctx *ctx) { - uint32_t size; - uint8_t typ; - uint32_t _size_offset; - return false - || (({ _size_offset = ctx->net_offset; validate_4(ctx); }) || ({ size = decode_u32le(&ctx->net_bytes[ctx->net_offset-4]); false; })) - || (validate_1(ctx) || ({ typ = decode_u8le(&ctx->net_bytes[ctx->net_offset-1]); false; })) - || validate_tag(ctx) - || validate_fid(ctx) - || validate_s(ctx) - || validate_s(ctx) -#if defined(CONFIG_9P_ENABLE_9P2000_u) - || ( (ctx->ctx->version==LIB9P_VER_9P2000_u) && validate_4(ctx) ) -#endif /* defined(CONFIG_9P_ENABLE_9P2000_u) */ - || ({ uint32_t exp = ctx->net_offset - _size_offset; (((uint32_t)size) != exp) && - lib9p_errorf(ctx->ctx, LINUX_EBADMSG, "size value is wrong (actual:%"PRIu32" != correct:%"PRIu32")", (uint32_t)size, exp); }) - || ({ uint32_t exp = 102; (((uint32_t)typ) != exp) && - lib9p_errorf(ctx->ctx, LINUX_EBADMSG, "typ value is wrong (actual:%"PRIu32" != correct:%"PRIu32")", (uint32_t)typ, exp); }) - ; -} - -static FLATTEN bool validate_Rauth(struct _validate_ctx *ctx) { - uint32_t size; - uint8_t typ; - uint32_t _size_offset; - return false - || (({ _size_offset = ctx->net_offset; validate_4(ctx); }) || ({ size = decode_u32le(&ctx->net_bytes[ctx->net_offset-4]); false; })) - || (validate_1(ctx) || ({ typ = decode_u8le(&ctx->net_bytes[ctx->net_offset-1]); false; })) - || validate_tag(ctx) - || validate_qid(ctx) - || ({ uint32_t exp = ctx->net_offset - _size_offset; (((uint32_t)size) != exp) && - lib9p_errorf(ctx->ctx, LINUX_EBADMSG, "size value is wrong (actual:%"PRIu32" != correct:%"PRIu32")", (uint32_t)size, exp); }) - || ({ uint32_t exp = 103; (((uint32_t)typ) != exp) && - lib9p_errorf(ctx->ctx, LINUX_EBADMSG, "typ value is wrong (actual:%"PRIu32" != correct:%"PRIu32")", (uint32_t)typ, exp); }) - ; -} - -static FLATTEN bool validate_Tattach(struct _validate_ctx *ctx) { - uint32_t size; - uint8_t typ; - uint32_t _size_offset; - return false - || (({ _size_offset = ctx->net_offset; validate_4(ctx); }) || ({ size = decode_u32le(&ctx->net_bytes[ctx->net_offset-4]); false; })) - || (validate_1(ctx) || ({ typ = decode_u8le(&ctx->net_bytes[ctx->net_offset-1]); false; })) - || validate_tag(ctx) - || validate_fid(ctx) - || validate_fid(ctx) - || validate_s(ctx) - || validate_s(ctx) -#if defined(CONFIG_9P_ENABLE_9P2000_u) - || ( (ctx->ctx->version==LIB9P_VER_9P2000_u) && validate_4(ctx) ) -#endif /* defined(CONFIG_9P_ENABLE_9P2000_u) */ - || ({ uint32_t exp = ctx->net_offset - _size_offset; (((uint32_t)size) != exp) && - lib9p_errorf(ctx->ctx, LINUX_EBADMSG, "size value is wrong (actual:%"PRIu32" != correct:%"PRIu32")", (uint32_t)size, exp); }) - || ({ uint32_t exp = 104; (((uint32_t)typ) != exp) && - lib9p_errorf(ctx->ctx, LINUX_EBADMSG, "typ value is wrong (actual:%"PRIu32" != correct:%"PRIu32")", (uint32_t)typ, exp); }) - ; -} - -static FLATTEN bool validate_Rattach(struct _validate_ctx *ctx) { - uint32_t size; - uint8_t typ; - uint32_t _size_offset; - return false - || (({ _size_offset = ctx->net_offset; validate_4(ctx); }) || ({ size = decode_u32le(&ctx->net_bytes[ctx->net_offset-4]); false; })) - || (validate_1(ctx) || ({ typ = decode_u8le(&ctx->net_bytes[ctx->net_offset-1]); false; })) - || validate_tag(ctx) - || validate_qid(ctx) - || ({ uint32_t exp = ctx->net_offset - _size_offset; (((uint32_t)size) != exp) && - lib9p_errorf(ctx->ctx, LINUX_EBADMSG, "size value is wrong (actual:%"PRIu32" != correct:%"PRIu32")", (uint32_t)size, exp); }) - || ({ uint32_t exp = 105; (((uint32_t)typ) != exp) && - lib9p_errorf(ctx->ctx, LINUX_EBADMSG, "typ value is wrong (actual:%"PRIu32" != correct:%"PRIu32")", (uint32_t)typ, exp); }) - ; -} - -static FLATTEN bool validate_Rerror(struct _validate_ctx *ctx) { - uint32_t size; - uint8_t typ; - uint32_t _size_offset; - return false - || (({ _size_offset = ctx->net_offset; validate_4(ctx); }) || ({ size = decode_u32le(&ctx->net_bytes[ctx->net_offset-4]); false; })) - || (validate_1(ctx) || ({ typ = decode_u8le(&ctx->net_bytes[ctx->net_offset-1]); false; })) - || validate_tag(ctx) - || validate_s(ctx) -#if defined(CONFIG_9P_ENABLE_9P2000_u) - || ( (ctx->ctx->version==LIB9P_VER_9P2000_u) && validate_4(ctx) ) -#endif /* defined(CONFIG_9P_ENABLE_9P2000_u) */ - || ({ uint32_t exp = ctx->net_offset - _size_offset; (((uint32_t)size) != exp) && - lib9p_errorf(ctx->ctx, LINUX_EBADMSG, "size value is wrong (actual:%"PRIu32" != correct:%"PRIu32")", (uint32_t)size, exp); }) - || ({ uint32_t exp = 107; (((uint32_t)typ) != exp) && - lib9p_errorf(ctx->ctx, LINUX_EBADMSG, "typ value is wrong (actual:%"PRIu32" != correct:%"PRIu32")", (uint32_t)typ, exp); }) - ; -} - -static FLATTEN bool validate_Tflush(struct _validate_ctx *ctx) { - uint32_t size; - uint8_t typ; - uint32_t _size_offset; - return false - || (({ _size_offset = ctx->net_offset; validate_4(ctx); }) || ({ size = decode_u32le(&ctx->net_bytes[ctx->net_offset-4]); false; })) - || (validate_1(ctx) || ({ typ = decode_u8le(&ctx->net_bytes[ctx->net_offset-1]); false; })) - || validate_tag(ctx) - || validate_2(ctx) - || ({ uint32_t exp = ctx->net_offset - _size_offset; (((uint32_t)size) != exp) && - lib9p_errorf(ctx->ctx, LINUX_EBADMSG, "size value is wrong (actual:%"PRIu32" != correct:%"PRIu32")", (uint32_t)size, exp); }) - || ({ uint32_t exp = 108; (((uint32_t)typ) != exp) && - lib9p_errorf(ctx->ctx, LINUX_EBADMSG, "typ value is wrong (actual:%"PRIu32" != correct:%"PRIu32")", (uint32_t)typ, exp); }) - ; -} - -static FLATTEN bool validate_Rflush(struct _validate_ctx *ctx) { - uint32_t size; - uint8_t typ; - uint32_t _size_offset; - return false - || (({ _size_offset = ctx->net_offset; validate_4(ctx); }) || ({ size = decode_u32le(&ctx->net_bytes[ctx->net_offset-4]); false; })) - || (validate_1(ctx) || ({ typ = decode_u8le(&ctx->net_bytes[ctx->net_offset-1]); false; })) - || validate_tag(ctx) - || ({ uint32_t exp = ctx->net_offset - _size_offset; (((uint32_t)size) != exp) && - lib9p_errorf(ctx->ctx, LINUX_EBADMSG, "size value is wrong (actual:%"PRIu32" != correct:%"PRIu32")", (uint32_t)size, exp); }) - || ({ uint32_t exp = 109; (((uint32_t)typ) != exp) && - lib9p_errorf(ctx->ctx, LINUX_EBADMSG, "typ value is wrong (actual:%"PRIu32" != correct:%"PRIu32")", (uint32_t)typ, exp); }) - ; -} - -static FLATTEN bool validate_Twalk(struct _validate_ctx *ctx) { - uint32_t size; - uint8_t typ; - uint16_t nwname; - uint32_t _size_offset; - return false - || (({ _size_offset = ctx->net_offset; validate_4(ctx); }) || ({ size = decode_u32le(&ctx->net_bytes[ctx->net_offset-4]); false; })) - || (validate_1(ctx) || ({ typ = decode_u8le(&ctx->net_bytes[ctx->net_offset-1]); false; })) - || validate_tag(ctx) - || validate_fid(ctx) - || validate_fid(ctx) - || (validate_2(ctx) || ({ nwname = decode_u16le(&ctx->net_bytes[ctx->net_offset-2]); false; })) - || _validate_list(ctx, decode_u16le(&ctx->net_bytes[ctx->net_offset-2]), validate_s, sizeof(struct lib9p_s)) - || ({ uint32_t exp = ctx->net_offset - _size_offset; (((uint32_t)size) != exp) && - lib9p_errorf(ctx->ctx, LINUX_EBADMSG, "size value is wrong (actual:%"PRIu32" != correct:%"PRIu32")", (uint32_t)size, exp); }) - || ({ uint32_t exp = 110; (((uint32_t)typ) != exp) && - lib9p_errorf(ctx->ctx, LINUX_EBADMSG, "typ value is wrong (actual:%"PRIu32" != correct:%"PRIu32")", (uint32_t)typ, exp); }) - || ({ uint32_t max = 16; (((uint32_t)nwname) > max) && - lib9p_errorf(ctx->ctx, LINUX_EBADMSG, "nwname value is too large (%"PRIu32" > %"PRIu32")", nwname, max); }) - ; -} - -static FLATTEN bool validate_Rwalk(struct _validate_ctx *ctx) { - uint32_t size; - uint8_t typ; - uint16_t nwqid; - uint32_t _size_offset; - return false - || (({ _size_offset = ctx->net_offset; validate_4(ctx); }) || ({ size = decode_u32le(&ctx->net_bytes[ctx->net_offset-4]); false; })) - || (validate_1(ctx) || ({ typ = decode_u8le(&ctx->net_bytes[ctx->net_offset-1]); false; })) - || validate_tag(ctx) - || (validate_2(ctx) || ({ nwqid = decode_u16le(&ctx->net_bytes[ctx->net_offset-2]); false; })) - || _validate_list(ctx, decode_u16le(&ctx->net_bytes[ctx->net_offset-2]), validate_qid, sizeof(struct lib9p_qid)) - || ({ uint32_t exp = ctx->net_offset - _size_offset; (((uint32_t)size) != exp) && - lib9p_errorf(ctx->ctx, LINUX_EBADMSG, "size value is wrong (actual:%"PRIu32" != correct:%"PRIu32")", (uint32_t)size, exp); }) - || ({ uint32_t exp = 111; (((uint32_t)typ) != exp) && - lib9p_errorf(ctx->ctx, LINUX_EBADMSG, "typ value is wrong (actual:%"PRIu32" != correct:%"PRIu32")", (uint32_t)typ, exp); }) - || ({ uint32_t max = 16; (((uint32_t)nwqid) > max) && - lib9p_errorf(ctx->ctx, LINUX_EBADMSG, "nwqid value is too large (%"PRIu32" > %"PRIu32")", nwqid, max); }) - ; -} - -static FLATTEN bool validate_Topen(struct _validate_ctx *ctx) { - uint32_t size; - uint8_t typ; - uint32_t _size_offset; - return false - || (({ _size_offset = ctx->net_offset; validate_4(ctx); }) || ({ size = decode_u32le(&ctx->net_bytes[ctx->net_offset-4]); false; })) - || (validate_1(ctx) || ({ typ = decode_u8le(&ctx->net_bytes[ctx->net_offset-1]); false; })) - || validate_tag(ctx) - || validate_fid(ctx) - || validate_o(ctx) - || ({ uint32_t exp = ctx->net_offset - _size_offset; (((uint32_t)size) != exp) && - lib9p_errorf(ctx->ctx, LINUX_EBADMSG, "size value is wrong (actual:%"PRIu32" != correct:%"PRIu32")", (uint32_t)size, exp); }) - || ({ uint32_t exp = 112; (((uint32_t)typ) != exp) && - lib9p_errorf(ctx->ctx, LINUX_EBADMSG, "typ value is wrong (actual:%"PRIu32" != correct:%"PRIu32")", (uint32_t)typ, exp); }) - ; -} - -static FLATTEN bool validate_Ropen(struct _validate_ctx *ctx) { - uint32_t size; - uint8_t typ; - uint32_t _size_offset; - return false - || (({ _size_offset = ctx->net_offset; validate_4(ctx); }) || ({ size = decode_u32le(&ctx->net_bytes[ctx->net_offset-4]); false; })) - || (validate_1(ctx) || ({ typ = decode_u8le(&ctx->net_bytes[ctx->net_offset-1]); false; })) - || validate_tag(ctx) - || validate_qid(ctx) - || validate_4(ctx) - || ({ uint32_t exp = ctx->net_offset - _size_offset; (((uint32_t)size) != exp) && - lib9p_errorf(ctx->ctx, LINUX_EBADMSG, "size value is wrong (actual:%"PRIu32" != correct:%"PRIu32")", (uint32_t)size, exp); }) - || ({ uint32_t exp = 113; (((uint32_t)typ) != exp) && - lib9p_errorf(ctx->ctx, LINUX_EBADMSG, "typ value is wrong (actual:%"PRIu32" != correct:%"PRIu32")", (uint32_t)typ, exp); }) - ; -} - -static FLATTEN bool validate_Tcreate(struct _validate_ctx *ctx) { - uint32_t size; - uint8_t typ; - uint32_t _size_offset; - return false - || (({ _size_offset = ctx->net_offset; validate_4(ctx); }) || ({ size = decode_u32le(&ctx->net_bytes[ctx->net_offset-4]); false; })) - || (validate_1(ctx) || ({ typ = decode_u8le(&ctx->net_bytes[ctx->net_offset-1]); false; })) - || validate_tag(ctx) - || validate_fid(ctx) - || validate_s(ctx) - || validate_dm(ctx) - || validate_o(ctx) - || ({ uint32_t exp = ctx->net_offset - _size_offset; (((uint32_t)size) != exp) && - lib9p_errorf(ctx->ctx, LINUX_EBADMSG, "size value is wrong (actual:%"PRIu32" != correct:%"PRIu32")", (uint32_t)size, exp); }) - || ({ uint32_t exp = 114; (((uint32_t)typ) != exp) && - lib9p_errorf(ctx->ctx, LINUX_EBADMSG, "typ value is wrong (actual:%"PRIu32" != correct:%"PRIu32")", (uint32_t)typ, exp); }) - ; -} - -static FLATTEN bool validate_Rcreate(struct _validate_ctx *ctx) { - uint32_t size; - uint8_t typ; - uint32_t _size_offset; - return false - || (({ _size_offset = ctx->net_offset; validate_4(ctx); }) || ({ size = decode_u32le(&ctx->net_bytes[ctx->net_offset-4]); false; })) - || (validate_1(ctx) || ({ typ = decode_u8le(&ctx->net_bytes[ctx->net_offset-1]); false; })) - || validate_tag(ctx) - || validate_qid(ctx) - || validate_4(ctx) - || ({ uint32_t exp = ctx->net_offset - _size_offset; (((uint32_t)size) != exp) && - lib9p_errorf(ctx->ctx, LINUX_EBADMSG, "size value is wrong (actual:%"PRIu32" != correct:%"PRIu32")", (uint32_t)size, exp); }) - || ({ uint32_t exp = 115; (((uint32_t)typ) != exp) && - lib9p_errorf(ctx->ctx, LINUX_EBADMSG, "typ value is wrong (actual:%"PRIu32" != correct:%"PRIu32")", (uint32_t)typ, exp); }) - ; -} - -static FLATTEN bool validate_Tread(struct _validate_ctx *ctx) { - uint32_t size; - uint8_t typ; - uint32_t _size_offset; - return false - || (({ _size_offset = ctx->net_offset; validate_4(ctx); }) || ({ size = decode_u32le(&ctx->net_bytes[ctx->net_offset-4]); false; })) - || (validate_1(ctx) || ({ typ = decode_u8le(&ctx->net_bytes[ctx->net_offset-1]); false; })) - || validate_tag(ctx) - || validate_fid(ctx) - || validate_8(ctx) - || validate_4(ctx) - || ({ uint32_t exp = ctx->net_offset - _size_offset; (((uint32_t)size) != exp) && - lib9p_errorf(ctx->ctx, LINUX_EBADMSG, "size value is wrong (actual:%"PRIu32" != correct:%"PRIu32")", (uint32_t)size, exp); }) - || ({ uint32_t exp = 116; (((uint32_t)typ) != exp) && - lib9p_errorf(ctx->ctx, LINUX_EBADMSG, "typ value is wrong (actual:%"PRIu32" != correct:%"PRIu32")", (uint32_t)typ, exp); }) - ; -} - -static FLATTEN bool validate_Rread(struct _validate_ctx *ctx) { - uint32_t size; - uint8_t typ; - uint32_t _size_offset; - return false - || (({ _size_offset = ctx->net_offset; validate_4(ctx); }) || ({ size = decode_u32le(&ctx->net_bytes[ctx->net_offset-4]); false; })) - || (validate_1(ctx) || ({ typ = decode_u8le(&ctx->net_bytes[ctx->net_offset-1]); false; })) - || validate_tag(ctx) - || validate_d(ctx) - || ({ uint32_t exp = ctx->net_offset - _size_offset; (((uint32_t)size) != exp) && - lib9p_errorf(ctx->ctx, LINUX_EBADMSG, "size value is wrong (actual:%"PRIu32" != correct:%"PRIu32")", (uint32_t)size, exp); }) - || ({ uint32_t exp = 117; (((uint32_t)typ) != exp) && - lib9p_errorf(ctx->ctx, LINUX_EBADMSG, "typ value is wrong (actual:%"PRIu32" != correct:%"PRIu32")", (uint32_t)typ, exp); }) - ; -} - -static FLATTEN bool validate_Twrite(struct _validate_ctx *ctx) { - uint32_t size; - uint8_t typ; - uint32_t _size_offset; - return false - || (({ _size_offset = ctx->net_offset; validate_4(ctx); }) || ({ size = decode_u32le(&ctx->net_bytes[ctx->net_offset-4]); false; })) - || (validate_1(ctx) || ({ typ = decode_u8le(&ctx->net_bytes[ctx->net_offset-1]); false; })) - || validate_tag(ctx) - || validate_fid(ctx) - || validate_8(ctx) - || validate_d(ctx) - || ({ uint32_t exp = ctx->net_offset - _size_offset; (((uint32_t)size) != exp) && - lib9p_errorf(ctx->ctx, LINUX_EBADMSG, "size value is wrong (actual:%"PRIu32" != correct:%"PRIu32")", (uint32_t)size, exp); }) - || ({ uint32_t exp = 118; (((uint32_t)typ) != exp) && - lib9p_errorf(ctx->ctx, LINUX_EBADMSG, "typ value is wrong (actual:%"PRIu32" != correct:%"PRIu32")", (uint32_t)typ, exp); }) - ; -} - -static FLATTEN bool validate_Rwrite(struct _validate_ctx *ctx) { - uint32_t size; - uint8_t typ; - uint32_t _size_offset; - return false - || (({ _size_offset = ctx->net_offset; validate_4(ctx); }) || ({ size = decode_u32le(&ctx->net_bytes[ctx->net_offset-4]); false; })) - || (validate_1(ctx) || ({ typ = decode_u8le(&ctx->net_bytes[ctx->net_offset-1]); false; })) - || validate_tag(ctx) - || validate_4(ctx) - || ({ uint32_t exp = ctx->net_offset - _size_offset; (((uint32_t)size) != exp) && - lib9p_errorf(ctx->ctx, LINUX_EBADMSG, "size value is wrong (actual:%"PRIu32" != correct:%"PRIu32")", (uint32_t)size, exp); }) - || ({ uint32_t exp = 119; (((uint32_t)typ) != exp) && - lib9p_errorf(ctx->ctx, LINUX_EBADMSG, "typ value is wrong (actual:%"PRIu32" != correct:%"PRIu32")", (uint32_t)typ, exp); }) - ; -} - -static FLATTEN bool validate_Tclunk(struct _validate_ctx *ctx) { - uint32_t size; - uint8_t typ; - uint32_t _size_offset; - return false - || (({ _size_offset = ctx->net_offset; validate_4(ctx); }) || ({ size = decode_u32le(&ctx->net_bytes[ctx->net_offset-4]); false; })) - || (validate_1(ctx) || ({ typ = decode_u8le(&ctx->net_bytes[ctx->net_offset-1]); false; })) - || validate_tag(ctx) - || validate_fid(ctx) - || ({ uint32_t exp = ctx->net_offset - _size_offset; (((uint32_t)size) != exp) && - lib9p_errorf(ctx->ctx, LINUX_EBADMSG, "size value is wrong (actual:%"PRIu32" != correct:%"PRIu32")", (uint32_t)size, exp); }) - || ({ uint32_t exp = 120; (((uint32_t)typ) != exp) && - lib9p_errorf(ctx->ctx, LINUX_EBADMSG, "typ value is wrong (actual:%"PRIu32" != correct:%"PRIu32")", (uint32_t)typ, exp); }) - ; -} - -static FLATTEN bool validate_Rclunk(struct _validate_ctx *ctx) { - uint32_t size; - uint8_t typ; - uint32_t _size_offset; - return false - || (({ _size_offset = ctx->net_offset; validate_4(ctx); }) || ({ size = decode_u32le(&ctx->net_bytes[ctx->net_offset-4]); false; })) - || (validate_1(ctx) || ({ typ = decode_u8le(&ctx->net_bytes[ctx->net_offset-1]); false; })) - || validate_tag(ctx) - || ({ uint32_t exp = ctx->net_offset - _size_offset; (((uint32_t)size) != exp) && - lib9p_errorf(ctx->ctx, LINUX_EBADMSG, "size value is wrong (actual:%"PRIu32" != correct:%"PRIu32")", (uint32_t)size, exp); }) - || ({ uint32_t exp = 121; (((uint32_t)typ) != exp) && - lib9p_errorf(ctx->ctx, LINUX_EBADMSG, "typ value is wrong (actual:%"PRIu32" != correct:%"PRIu32")", (uint32_t)typ, exp); }) - ; -} - -static FLATTEN bool validate_Tremove(struct _validate_ctx *ctx) { - uint32_t size; - uint8_t typ; - uint32_t _size_offset; - return false - || (({ _size_offset = ctx->net_offset; validate_4(ctx); }) || ({ size = decode_u32le(&ctx->net_bytes[ctx->net_offset-4]); false; })) - || (validate_1(ctx) || ({ typ = decode_u8le(&ctx->net_bytes[ctx->net_offset-1]); false; })) - || validate_tag(ctx) - || validate_fid(ctx) - || ({ uint32_t exp = ctx->net_offset - _size_offset; (((uint32_t)size) != exp) && - lib9p_errorf(ctx->ctx, LINUX_EBADMSG, "size value is wrong (actual:%"PRIu32" != correct:%"PRIu32")", (uint32_t)size, exp); }) - || ({ uint32_t exp = 122; (((uint32_t)typ) != exp) && - lib9p_errorf(ctx->ctx, LINUX_EBADMSG, "typ value is wrong (actual:%"PRIu32" != correct:%"PRIu32")", (uint32_t)typ, exp); }) - ; -} - -static FLATTEN bool validate_Rremove(struct _validate_ctx *ctx) { - uint32_t size; - uint8_t typ; - uint32_t _size_offset; - return false - || (({ _size_offset = ctx->net_offset; validate_4(ctx); }) || ({ size = decode_u32le(&ctx->net_bytes[ctx->net_offset-4]); false; })) - || (validate_1(ctx) || ({ typ = decode_u8le(&ctx->net_bytes[ctx->net_offset-1]); false; })) - || validate_tag(ctx) - || ({ uint32_t exp = ctx->net_offset - _size_offset; (((uint32_t)size) != exp) && - lib9p_errorf(ctx->ctx, LINUX_EBADMSG, "size value is wrong (actual:%"PRIu32" != correct:%"PRIu32")", (uint32_t)size, exp); }) - || ({ uint32_t exp = 123; (((uint32_t)typ) != exp) && - lib9p_errorf(ctx->ctx, LINUX_EBADMSG, "typ value is wrong (actual:%"PRIu32" != correct:%"PRIu32")", (uint32_t)typ, exp); }) - ; -} - -static FLATTEN bool validate_Tstat(struct _validate_ctx *ctx) { - uint32_t size; - uint8_t typ; - uint32_t _size_offset; - return false - || (({ _size_offset = ctx->net_offset; validate_4(ctx); }) || ({ size = decode_u32le(&ctx->net_bytes[ctx->net_offset-4]); false; })) - || (validate_1(ctx) || ({ typ = decode_u8le(&ctx->net_bytes[ctx->net_offset-1]); false; })) - || validate_tag(ctx) - || validate_fid(ctx) - || ({ uint32_t exp = ctx->net_offset - _size_offset; (((uint32_t)size) != exp) && - lib9p_errorf(ctx->ctx, LINUX_EBADMSG, "size value is wrong (actual:%"PRIu32" != correct:%"PRIu32")", (uint32_t)size, exp); }) - || ({ uint32_t exp = 124; (((uint32_t)typ) != exp) && - lib9p_errorf(ctx->ctx, LINUX_EBADMSG, "typ value is wrong (actual:%"PRIu32" != correct:%"PRIu32")", (uint32_t)typ, exp); }) - ; -} - -static FLATTEN bool validate_Rstat(struct _validate_ctx *ctx) { - uint32_t size; - uint8_t typ; - uint16_t nstat; - uint32_t _size_offset; - uint32_t _stat_offset; - return false - || (({ _size_offset = ctx->net_offset; validate_4(ctx); }) || ({ size = decode_u32le(&ctx->net_bytes[ctx->net_offset-4]); false; })) - || (validate_1(ctx) || ({ typ = decode_u8le(&ctx->net_bytes[ctx->net_offset-1]); false; })) - || validate_tag(ctx) - || (validate_2(ctx) || ({ nstat = decode_u16le(&ctx->net_bytes[ctx->net_offset-2]); false; })) - || ({ _stat_offset = ctx->net_offset; validate_stat(ctx); }) - || ({ uint32_t exp = ctx->net_offset - _size_offset; (((uint32_t)size) != exp) && - lib9p_errorf(ctx->ctx, LINUX_EBADMSG, "size value is wrong (actual:%"PRIu32" != correct:%"PRIu32")", (uint32_t)size, exp); }) - || ({ uint32_t exp = 125; (((uint32_t)typ) != exp) && - lib9p_errorf(ctx->ctx, LINUX_EBADMSG, "typ value is wrong (actual:%"PRIu32" != correct:%"PRIu32")", (uint32_t)typ, exp); }) - || ({ uint32_t exp = ctx->net_offset - _stat_offset; (((uint32_t)nstat) != exp) && - lib9p_errorf(ctx->ctx, LINUX_EBADMSG, "nstat value is wrong (actual:%"PRIu32" != correct:%"PRIu32")", (uint32_t)nstat, exp); }) - ; -} - -static FLATTEN bool validate_Twstat(struct _validate_ctx *ctx) { - uint32_t size; - uint8_t typ; - uint16_t nstat; - uint32_t _size_offset; - uint32_t _stat_offset; - return false - || (({ _size_offset = ctx->net_offset; validate_4(ctx); }) || ({ size = decode_u32le(&ctx->net_bytes[ctx->net_offset-4]); false; })) - || (validate_1(ctx) || ({ typ = decode_u8le(&ctx->net_bytes[ctx->net_offset-1]); false; })) - || validate_tag(ctx) - || validate_fid(ctx) - || (validate_2(ctx) || ({ nstat = decode_u16le(&ctx->net_bytes[ctx->net_offset-2]); false; })) - || ({ _stat_offset = ctx->net_offset; validate_stat(ctx); }) - || ({ uint32_t exp = ctx->net_offset - _size_offset; (((uint32_t)size) != exp) && - lib9p_errorf(ctx->ctx, LINUX_EBADMSG, "size value is wrong (actual:%"PRIu32" != correct:%"PRIu32")", (uint32_t)size, exp); }) - || ({ uint32_t exp = 126; (((uint32_t)typ) != exp) && - lib9p_errorf(ctx->ctx, LINUX_EBADMSG, "typ value is wrong (actual:%"PRIu32" != correct:%"PRIu32")", (uint32_t)typ, exp); }) - || ({ uint32_t exp = ctx->net_offset - _stat_offset; (((uint32_t)nstat) != exp) && - lib9p_errorf(ctx->ctx, LINUX_EBADMSG, "nstat value is wrong (actual:%"PRIu32" != correct:%"PRIu32")", (uint32_t)nstat, exp); }) - ; -} - -static FLATTEN bool validate_Rwstat(struct _validate_ctx *ctx) { - uint32_t size; - uint8_t typ; - uint32_t _size_offset; - return false - || (({ _size_offset = ctx->net_offset; validate_4(ctx); }) || ({ size = decode_u32le(&ctx->net_bytes[ctx->net_offset-4]); false; })) - || (validate_1(ctx) || ({ typ = decode_u8le(&ctx->net_bytes[ctx->net_offset-1]); false; })) - || validate_tag(ctx) - || ({ uint32_t exp = ctx->net_offset - _size_offset; (((uint32_t)size) != exp) && - lib9p_errorf(ctx->ctx, LINUX_EBADMSG, "size value is wrong (actual:%"PRIu32" != correct:%"PRIu32")", (uint32_t)size, exp); }) - || ({ uint32_t exp = 127; (((uint32_t)typ) != exp) && - lib9p_errorf(ctx->ctx, LINUX_EBADMSG, "typ value is wrong (actual:%"PRIu32" != correct:%"PRIu32")", (uint32_t)typ, exp); }) - ; -} - -#endif /* defined(CONFIG_9P_ENABLE_9P2000) || defined(CONFIG_9P_ENABLE_9P2000_e) || defined(CONFIG_9P_ENABLE_9P2000_u) */ -#if defined(CONFIG_9P_ENABLE_9P2000_e) -static FLATTEN bool validate_Tsession(struct _validate_ctx *ctx) { - uint32_t size; - uint8_t typ; - uint32_t _size_offset; - return false - || (({ _size_offset = ctx->net_offset; validate_4(ctx); }) || ({ size = decode_u32le(&ctx->net_bytes[ctx->net_offset-4]); false; })) - || (validate_1(ctx) || ({ typ = decode_u8le(&ctx->net_bytes[ctx->net_offset-1]); false; })) - || validate_tag(ctx) - || validate_8(ctx) - || ({ uint32_t exp = ctx->net_offset - _size_offset; (((uint32_t)size) != exp) && - lib9p_errorf(ctx->ctx, LINUX_EBADMSG, "size value is wrong (actual:%"PRIu32" != correct:%"PRIu32")", (uint32_t)size, exp); }) - || ({ uint32_t exp = 150; (((uint32_t)typ) != exp) && - lib9p_errorf(ctx->ctx, LINUX_EBADMSG, "typ value is wrong (actual:%"PRIu32" != correct:%"PRIu32")", (uint32_t)typ, exp); }) - ; -} - -static FLATTEN bool validate_Rsession(struct _validate_ctx *ctx) { - uint32_t size; - uint8_t typ; - uint32_t _size_offset; - return false - || (({ _size_offset = ctx->net_offset; validate_4(ctx); }) || ({ size = decode_u32le(&ctx->net_bytes[ctx->net_offset-4]); false; })) - || (validate_1(ctx) || ({ typ = decode_u8le(&ctx->net_bytes[ctx->net_offset-1]); false; })) - || validate_tag(ctx) - || ({ uint32_t exp = ctx->net_offset - _size_offset; (((uint32_t)size) != exp) && - lib9p_errorf(ctx->ctx, LINUX_EBADMSG, "size value is wrong (actual:%"PRIu32" != correct:%"PRIu32")", (uint32_t)size, exp); }) - || ({ uint32_t exp = 151; (((uint32_t)typ) != exp) && - lib9p_errorf(ctx->ctx, LINUX_EBADMSG, "typ value is wrong (actual:%"PRIu32" != correct:%"PRIu32")", (uint32_t)typ, exp); }) - ; -} - -static FLATTEN bool validate_Tsread(struct _validate_ctx *ctx) { - uint32_t size; - uint8_t typ; - uint32_t _size_offset; - return false - || (({ _size_offset = ctx->net_offset; validate_4(ctx); }) || ({ size = decode_u32le(&ctx->net_bytes[ctx->net_offset-4]); false; })) - || (validate_1(ctx) || ({ typ = decode_u8le(&ctx->net_bytes[ctx->net_offset-1]); false; })) - || validate_tag(ctx) - || validate_4(ctx) - || validate_2(ctx) - || _validate_list(ctx, decode_u16le(&ctx->net_bytes[ctx->net_offset-2]), validate_s, sizeof(struct lib9p_s)) - || ({ uint32_t exp = ctx->net_offset - _size_offset; (((uint32_t)size) != exp) && - lib9p_errorf(ctx->ctx, LINUX_EBADMSG, "size value is wrong (actual:%"PRIu32" != correct:%"PRIu32")", (uint32_t)size, exp); }) - || ({ uint32_t exp = 152; (((uint32_t)typ) != exp) && - lib9p_errorf(ctx->ctx, LINUX_EBADMSG, "typ value is wrong (actual:%"PRIu32" != correct:%"PRIu32")", (uint32_t)typ, exp); }) - ; -} - -static FLATTEN bool validate_Rsread(struct _validate_ctx *ctx) { - uint32_t size; - uint8_t typ; - uint32_t _size_offset; - return false - || (({ _size_offset = ctx->net_offset; validate_4(ctx); }) || ({ size = decode_u32le(&ctx->net_bytes[ctx->net_offset-4]); false; })) - || (validate_1(ctx) || ({ typ = decode_u8le(&ctx->net_bytes[ctx->net_offset-1]); false; })) - || validate_tag(ctx) - || validate_d(ctx) - || ({ uint32_t exp = ctx->net_offset - _size_offset; (((uint32_t)size) != exp) && - lib9p_errorf(ctx->ctx, LINUX_EBADMSG, "size value is wrong (actual:%"PRIu32" != correct:%"PRIu32")", (uint32_t)size, exp); }) - || ({ uint32_t exp = 153; (((uint32_t)typ) != exp) && - lib9p_errorf(ctx->ctx, LINUX_EBADMSG, "typ value is wrong (actual:%"PRIu32" != correct:%"PRIu32")", (uint32_t)typ, exp); }) - ; -} - -static FLATTEN bool validate_Tswrite(struct _validate_ctx *ctx) { - uint32_t size; - uint8_t typ; - uint32_t _size_offset; - return false - || (({ _size_offset = ctx->net_offset; validate_4(ctx); }) || ({ size = decode_u32le(&ctx->net_bytes[ctx->net_offset-4]); false; })) - || (validate_1(ctx) || ({ typ = decode_u8le(&ctx->net_bytes[ctx->net_offset-1]); false; })) - || validate_tag(ctx) - || validate_4(ctx) - || validate_2(ctx) - || _validate_list(ctx, decode_u16le(&ctx->net_bytes[ctx->net_offset-2]), validate_s, sizeof(struct lib9p_s)) - || validate_d(ctx) - || ({ uint32_t exp = ctx->net_offset - _size_offset; (((uint32_t)size) != exp) && - lib9p_errorf(ctx->ctx, LINUX_EBADMSG, "size value is wrong (actual:%"PRIu32" != correct:%"PRIu32")", (uint32_t)size, exp); }) - || ({ uint32_t exp = 154; (((uint32_t)typ) != exp) && - lib9p_errorf(ctx->ctx, LINUX_EBADMSG, "typ value is wrong (actual:%"PRIu32" != correct:%"PRIu32")", (uint32_t)typ, exp); }) - ; -} - -static FLATTEN bool validate_Rswrite(struct _validate_ctx *ctx) { - uint32_t size; - uint8_t typ; - uint32_t _size_offset; - return false - || (({ _size_offset = ctx->net_offset; validate_4(ctx); }) || ({ size = decode_u32le(&ctx->net_bytes[ctx->net_offset-4]); false; })) - || (validate_1(ctx) || ({ typ = decode_u8le(&ctx->net_bytes[ctx->net_offset-1]); false; })) - || validate_tag(ctx) - || validate_4(ctx) - || ({ uint32_t exp = ctx->net_offset - _size_offset; (((uint32_t)size) != exp) && - lib9p_errorf(ctx->ctx, LINUX_EBADMSG, "size value is wrong (actual:%"PRIu32" != correct:%"PRIu32")", (uint32_t)size, exp); }) - || ({ uint32_t exp = 155; (((uint32_t)typ) != exp) && - lib9p_errorf(ctx->ctx, LINUX_EBADMSG, "typ value is wrong (actual:%"PRIu32" != correct:%"PRIu32")", (uint32_t)typ, exp); }) - ; -} -#endif /* defined(CONFIG_9P_ENABLE_9P2000_e) */ +static bool marshal_Rwalk(struct lib9p_ctx *ctx, struct lib9p_msg_Rwalk *val, struct _marshal_ret *ret) { + uint32_t needed_size = 9 + (val->nwqid)*13; + if (needed_size > ctx->max_msg_size) { + lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", + "Rwalk", + ctx->version ? "negotiated" : "server", + ctx->max_msg_size); + return true; + } + uint32_t offsetof_end = needed_size; + uint32_t offsetof_size = 0; + MARSHAL_U32LE(ctx, offsetof_end - offsetof_size); + MARSHAL_U8LE(ctx, 111); + MARSHAL_U16LE(ctx, val->tag); + MARSHAL_U16LE(ctx, val->nwqid); + for (uint16_t i = 0; i < val->nwqid; i++) { + MARSHAL_U8LE(ctx, val->wqid[i].type & qt_masks[ctx->version]); + MARSHAL_U32LE(ctx, val->wqid[i].vers); + MARSHAL_U64LE(ctx, val->wqid[i].path); + } + return false; +} -/* unmarshal_* ****************************************************************/ +#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */ +#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u +static bool marshal_Topen(struct lib9p_ctx *ctx, struct lib9p_msg_Topen *val, struct _marshal_ret *ret) { + uint32_t needed_size = 12; + if (needed_size > ctx->max_msg_size) { + lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", + "Topen", + ctx->version ? "negotiated" : "client", + ctx->max_msg_size); + return true; + } + uint32_t offsetof_end = needed_size; + uint32_t offsetof_size = 0; + MARSHAL_U32LE(ctx, offsetof_end - offsetof_size); + MARSHAL_U8LE(ctx, 112); + MARSHAL_U16LE(ctx, val->tag); + MARSHAL_U32LE(ctx, val->fid); + MARSHAL_U8LE(ctx, val->mode & o_masks[ctx->version]); + return false; +} -static ALWAYS_INLINE void unmarshal_1(struct _unmarshal_ctx *ctx, uint8_t *out) { - *out = decode_u8le(&ctx->net_bytes[ctx->net_offset]); - ctx->net_offset += 1; +static bool marshal_Ropen(struct lib9p_ctx *ctx, struct lib9p_msg_Ropen *val, struct _marshal_ret *ret) { + uint32_t needed_size = 24; + if (needed_size > ctx->max_msg_size) { + lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", + "Ropen", + ctx->version ? "negotiated" : "server", + ctx->max_msg_size); + return true; + } + uint32_t offsetof_end = needed_size; + uint32_t offsetof_size = 0; + MARSHAL_U32LE(ctx, offsetof_end - offsetof_size); + MARSHAL_U8LE(ctx, 113); + MARSHAL_U16LE(ctx, val->tag); + MARSHAL_U8LE(ctx, val->qid.type & qt_masks[ctx->version]); + MARSHAL_U32LE(ctx, val->qid.vers); + MARSHAL_U64LE(ctx, val->qid.path); + MARSHAL_U32LE(ctx, val->iounit); + return false; } -static ALWAYS_INLINE void unmarshal_2(struct _unmarshal_ctx *ctx, uint16_t *out) { - *out = decode_u16le(&ctx->net_bytes[ctx->net_offset]); - ctx->net_offset += 2; +static bool marshal_Tcreate(struct lib9p_ctx *ctx, struct lib9p_msg_Tcreate *val, struct _marshal_ret *ret) { + uint32_t needed_size = 18 + val->name.len; + if (needed_size > ctx->max_msg_size) { + lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", + "Tcreate", + ctx->version ? "negotiated" : "client", + ctx->max_msg_size); + return true; + } + uint32_t offsetof_end = needed_size; + uint32_t offsetof_size = 0; + MARSHAL_U32LE(ctx, offsetof_end - offsetof_size); + MARSHAL_U8LE(ctx, 114); + MARSHAL_U16LE(ctx, val->tag); + MARSHAL_U32LE(ctx, val->fid); + MARSHAL_U16LE(ctx, val->name.len); + MARSHAL_BYTES_ZEROCOPY(ctx, val->name.utf8, val->name.len); + MARSHAL_U32LE(ctx, val->perm & dm_masks[ctx->version]); + MARSHAL_U8LE(ctx, val->mode & o_masks[ctx->version]); + return false; } -static ALWAYS_INLINE void unmarshal_4(struct _unmarshal_ctx *ctx, uint32_t *out) { - *out = decode_u32le(&ctx->net_bytes[ctx->net_offset]); - ctx->net_offset += 4; +static bool marshal_Rcreate(struct lib9p_ctx *ctx, struct lib9p_msg_Rcreate *val, struct _marshal_ret *ret) { + uint32_t needed_size = 24; + if (needed_size > ctx->max_msg_size) { + lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", + "Rcreate", + ctx->version ? "negotiated" : "server", + ctx->max_msg_size); + return true; + } + uint32_t offsetof_end = needed_size; + uint32_t offsetof_size = 0; + MARSHAL_U32LE(ctx, offsetof_end - offsetof_size); + MARSHAL_U8LE(ctx, 115); + MARSHAL_U16LE(ctx, val->tag); + MARSHAL_U8LE(ctx, val->qid.type & qt_masks[ctx->version]); + MARSHAL_U32LE(ctx, val->qid.vers); + MARSHAL_U64LE(ctx, val->qid.path); + MARSHAL_U32LE(ctx, val->iounit); + return false; } -static ALWAYS_INLINE void unmarshal_8(struct _unmarshal_ctx *ctx, uint64_t *out) { - *out = decode_u64le(&ctx->net_bytes[ctx->net_offset]); - ctx->net_offset += 8; +#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */ +#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u +static bool marshal_Tread(struct lib9p_ctx *ctx, struct lib9p_msg_Tread *val, struct _marshal_ret *ret) { + uint32_t needed_size = 23; + if (needed_size > ctx->max_msg_size) { + lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", + "Tread", + ctx->version ? "negotiated" : "client", + ctx->max_msg_size); + return true; + } + uint32_t offsetof_end = needed_size; + uint32_t offsetof_size = 0; + MARSHAL_U32LE(ctx, offsetof_end - offsetof_size); + MARSHAL_U8LE(ctx, 116); + MARSHAL_U16LE(ctx, val->tag); + MARSHAL_U32LE(ctx, val->fid); + MARSHAL_U64LE(ctx, val->offset); + MARSHAL_U32LE(ctx, val->count); + return false; } -#if defined(CONFIG_9P_ENABLE_9P2000) || defined(CONFIG_9P_ENABLE_9P2000_e) || defined(CONFIG_9P_ENABLE_9P2000_u) -static ALWAYS_INLINE void unmarshal_tag(struct _unmarshal_ctx *ctx, lib9p_tag_t *out) { - unmarshal_2(ctx, (uint16_t *)out); +static bool marshal_Rread(struct lib9p_ctx *ctx, struct lib9p_msg_Rread *val, struct _marshal_ret *ret) { + uint32_t needed_size = 11 + val->count; + if (needed_size > ctx->max_msg_size) { + lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", + "Rread", + ctx->version ? "negotiated" : "server", + ctx->max_msg_size); + return true; + } + uint32_t offsetof_end = needed_size; + uint32_t offsetof_size = 0; + MARSHAL_U32LE(ctx, offsetof_end - offsetof_size); + MARSHAL_U8LE(ctx, 117); + MARSHAL_U16LE(ctx, val->tag); + MARSHAL_U32LE(ctx, val->count); + MARSHAL_BYTES_ZEROCOPY(ctx, val->data, val->count); + return false; } -static ALWAYS_INLINE void unmarshal_fid(struct _unmarshal_ctx *ctx, lib9p_fid_t *out) { - unmarshal_4(ctx, (uint32_t *)out); +static bool marshal_Twrite(struct lib9p_ctx *ctx, struct lib9p_msg_Twrite *val, struct _marshal_ret *ret) { + uint32_t needed_size = 23 + val->count; + if (needed_size > ctx->max_msg_size) { + lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", + "Twrite", + ctx->version ? "negotiated" : "client", + ctx->max_msg_size); + return true; + } + uint32_t offsetof_end = needed_size; + uint32_t offsetof_size = 0; + MARSHAL_U32LE(ctx, offsetof_end - offsetof_size); + MARSHAL_U8LE(ctx, 118); + MARSHAL_U16LE(ctx, val->tag); + MARSHAL_U32LE(ctx, val->fid); + MARSHAL_U64LE(ctx, val->offset); + MARSHAL_U32LE(ctx, val->count); + MARSHAL_BYTES_ZEROCOPY(ctx, val->data, val->count); + return false; } -static ALWAYS_INLINE void unmarshal_d(struct _unmarshal_ctx *ctx, struct lib9p_d *out) { - memset(out, 0, sizeof(*out)); - unmarshal_4(ctx, &out->len); - out->dat = ctx->extra; - ctx->extra += sizeof(out->dat[0]) * out->len; - for (typeof(out->len) i = 0; i < out->len; i++) - unmarshal_1(ctx, (uint8_t *)&out->dat[i]); +static bool marshal_Rwrite(struct lib9p_ctx *ctx, struct lib9p_msg_Rwrite *val, struct _marshal_ret *ret) { + uint32_t needed_size = 11; + if (needed_size > ctx->max_msg_size) { + lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", + "Rwrite", + ctx->version ? "negotiated" : "server", + ctx->max_msg_size); + return true; + } + uint32_t offsetof_end = needed_size; + uint32_t offsetof_size = 0; + MARSHAL_U32LE(ctx, offsetof_end - offsetof_size); + MARSHAL_U8LE(ctx, 119); + MARSHAL_U16LE(ctx, val->tag); + MARSHAL_U32LE(ctx, val->count); + return false; } -static ALWAYS_INLINE void unmarshal_s(struct _unmarshal_ctx *ctx, struct lib9p_s *out) { - memset(out, 0, sizeof(*out)); - unmarshal_2(ctx, &out->len); - out->utf8 = ctx->extra; - ctx->extra += sizeof(out->utf8[0]) * out->len; - for (typeof(out->len) i = 0; i < out->len; i++) - unmarshal_1(ctx, (uint8_t *)&out->utf8[i]); - ctx->extra++; - out->utf8[out->len] = '\0'; +static bool marshal_Tclunk(struct lib9p_ctx *ctx, struct lib9p_msg_Tclunk *val, struct _marshal_ret *ret) { + uint32_t needed_size = 11; + if (needed_size > ctx->max_msg_size) { + lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", + "Tclunk", + ctx->version ? "negotiated" : "client", + ctx->max_msg_size); + return true; + } + uint32_t offsetof_end = needed_size; + uint32_t offsetof_size = 0; + MARSHAL_U32LE(ctx, offsetof_end - offsetof_size); + MARSHAL_U8LE(ctx, 120); + MARSHAL_U16LE(ctx, val->tag); + MARSHAL_U32LE(ctx, val->fid); + return false; } -static ALWAYS_INLINE void unmarshal_dm(struct _unmarshal_ctx *ctx, lib9p_dm_t *out) { - unmarshal_4(ctx, (uint32_t *)out); +static bool marshal_Rclunk(struct lib9p_ctx *ctx, struct lib9p_msg_Rclunk *val, struct _marshal_ret *ret) { + uint32_t needed_size = 7; + if (needed_size > ctx->max_msg_size) { + lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", + "Rclunk", + ctx->version ? "negotiated" : "server", + ctx->max_msg_size); + return true; + } + uint32_t offsetof_end = needed_size; + uint32_t offsetof_size = 0; + MARSHAL_U32LE(ctx, offsetof_end - offsetof_size); + MARSHAL_U8LE(ctx, 121); + MARSHAL_U16LE(ctx, val->tag); + return false; } -static ALWAYS_INLINE void unmarshal_qt(struct _unmarshal_ctx *ctx, lib9p_qt_t *out) { - unmarshal_1(ctx, (uint8_t *)out); +static bool marshal_Tremove(struct lib9p_ctx *ctx, struct lib9p_msg_Tremove *val, struct _marshal_ret *ret) { + uint32_t needed_size = 11; + if (needed_size > ctx->max_msg_size) { + lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", + "Tremove", + ctx->version ? "negotiated" : "client", + ctx->max_msg_size); + return true; + } + uint32_t offsetof_end = needed_size; + uint32_t offsetof_size = 0; + MARSHAL_U32LE(ctx, offsetof_end - offsetof_size); + MARSHAL_U8LE(ctx, 122); + MARSHAL_U16LE(ctx, val->tag); + MARSHAL_U32LE(ctx, val->fid); + return false; } -static ALWAYS_INLINE void unmarshal_qid(struct _unmarshal_ctx *ctx, struct lib9p_qid *out) { - memset(out, 0, sizeof(*out)); - unmarshal_qt(ctx, &out->type); - unmarshal_4(ctx, &out->vers); - unmarshal_8(ctx, &out->path); +static bool marshal_Rremove(struct lib9p_ctx *ctx, struct lib9p_msg_Rremove *val, struct _marshal_ret *ret) { + uint32_t needed_size = 7; + if (needed_size > ctx->max_msg_size) { + lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", + "Rremove", + ctx->version ? "negotiated" : "server", + ctx->max_msg_size); + return true; + } + uint32_t offsetof_end = needed_size; + uint32_t offsetof_size = 0; + MARSHAL_U32LE(ctx, offsetof_end - offsetof_size); + MARSHAL_U8LE(ctx, 123); + MARSHAL_U16LE(ctx, val->tag); + return false; } -static ALWAYS_INLINE void unmarshal_stat(struct _unmarshal_ctx *ctx, struct lib9p_stat *out) { - memset(out, 0, sizeof(*out)); - ctx->net_offset += 2; - unmarshal_2(ctx, &out->kern_type); - unmarshal_4(ctx, &out->kern_dev); - unmarshal_qid(ctx, &out->file_qid); - unmarshal_dm(ctx, &out->file_mode); - unmarshal_4(ctx, &out->file_atime); - unmarshal_4(ctx, &out->file_mtime); - unmarshal_8(ctx, &out->file_size); - unmarshal_s(ctx, &out->file_name); - unmarshal_s(ctx, &out->file_owner_uid); - unmarshal_s(ctx, &out->file_owner_gid); - unmarshal_s(ctx, &out->file_last_modified_uid); -#if defined(CONFIG_9P_ENABLE_9P2000_u) - if ( (ctx->ctx->version==LIB9P_VER_9P2000_u) ) unmarshal_s(ctx, &out->file_extension); - if ( (ctx->ctx->version==LIB9P_VER_9P2000_u) ) unmarshal_4(ctx, &out->file_owner_n_uid); - if ( (ctx->ctx->version==LIB9P_VER_9P2000_u) ) unmarshal_4(ctx, &out->file_owner_n_gid); - if ( (ctx->ctx->version==LIB9P_VER_9P2000_u) ) unmarshal_4(ctx, &out->file_last_modified_n_uid); -#endif /* defined(CONFIG_9P_ENABLE_9P2000_u) */ -} - -static ALWAYS_INLINE void unmarshal_o(struct _unmarshal_ctx *ctx, lib9p_o_t *out) { - unmarshal_1(ctx, (uint8_t *)out); -} - -static FLATTEN void unmarshal_Tversion(struct _unmarshal_ctx *ctx, struct lib9p_msg_Tversion *out) { - memset(out, 0, sizeof(*out)); - ctx->net_offset += 4; - ctx->net_offset += 1; - unmarshal_tag(ctx, &out->tag); - unmarshal_4(ctx, &out->max_msg_size); - unmarshal_s(ctx, &out->version); -} - -static FLATTEN void unmarshal_Rversion(struct _unmarshal_ctx *ctx, struct lib9p_msg_Rversion *out) { - memset(out, 0, sizeof(*out)); - ctx->net_offset += 4; - ctx->net_offset += 1; - unmarshal_tag(ctx, &out->tag); - unmarshal_4(ctx, &out->max_msg_size); - unmarshal_s(ctx, &out->version); -} - -static FLATTEN void unmarshal_Tauth(struct _unmarshal_ctx *ctx, struct lib9p_msg_Tauth *out) { - memset(out, 0, sizeof(*out)); - ctx->net_offset += 4; - ctx->net_offset += 1; - unmarshal_tag(ctx, &out->tag); - unmarshal_fid(ctx, &out->afid); - unmarshal_s(ctx, &out->uname); - unmarshal_s(ctx, &out->aname); -#if defined(CONFIG_9P_ENABLE_9P2000_u) - if ( (ctx->ctx->version==LIB9P_VER_9P2000_u) ) unmarshal_4(ctx, &out->n_uname); -#endif /* defined(CONFIG_9P_ENABLE_9P2000_u) */ -} - -static FLATTEN void unmarshal_Rauth(struct _unmarshal_ctx *ctx, struct lib9p_msg_Rauth *out) { - memset(out, 0, sizeof(*out)); - ctx->net_offset += 4; - ctx->net_offset += 1; - unmarshal_tag(ctx, &out->tag); - unmarshal_qid(ctx, &out->aqid); -} - -static FLATTEN void unmarshal_Tattach(struct _unmarshal_ctx *ctx, struct lib9p_msg_Tattach *out) { - memset(out, 0, sizeof(*out)); - ctx->net_offset += 4; - ctx->net_offset += 1; - unmarshal_tag(ctx, &out->tag); - unmarshal_fid(ctx, &out->fid); - unmarshal_fid(ctx, &out->afid); - unmarshal_s(ctx, &out->uname); - unmarshal_s(ctx, &out->aname); -#if defined(CONFIG_9P_ENABLE_9P2000_u) - if ( (ctx->ctx->version==LIB9P_VER_9P2000_u) ) unmarshal_4(ctx, &out->n_uname); -#endif /* defined(CONFIG_9P_ENABLE_9P2000_u) */ -} - -static FLATTEN void unmarshal_Rattach(struct _unmarshal_ctx *ctx, struct lib9p_msg_Rattach *out) { - memset(out, 0, sizeof(*out)); - ctx->net_offset += 4; - ctx->net_offset += 1; - unmarshal_tag(ctx, &out->tag); - unmarshal_qid(ctx, &out->qid); -} - -static FLATTEN void unmarshal_Rerror(struct _unmarshal_ctx *ctx, struct lib9p_msg_Rerror *out) { - memset(out, 0, sizeof(*out)); - ctx->net_offset += 4; - ctx->net_offset += 1; - unmarshal_tag(ctx, &out->tag); - unmarshal_s(ctx, &out->ename); -#if defined(CONFIG_9P_ENABLE_9P2000_u) - if ( (ctx->ctx->version==LIB9P_VER_9P2000_u) ) unmarshal_4(ctx, &out->errno); -#endif /* defined(CONFIG_9P_ENABLE_9P2000_u) */ -} - -static FLATTEN void unmarshal_Tflush(struct _unmarshal_ctx *ctx, struct lib9p_msg_Tflush *out) { - memset(out, 0, sizeof(*out)); - ctx->net_offset += 4; - ctx->net_offset += 1; - unmarshal_tag(ctx, &out->tag); - unmarshal_2(ctx, &out->oldtag); -} - -static FLATTEN void unmarshal_Rflush(struct _unmarshal_ctx *ctx, struct lib9p_msg_Rflush *out) { - memset(out, 0, sizeof(*out)); - ctx->net_offset += 4; - ctx->net_offset += 1; - unmarshal_tag(ctx, &out->tag); -} - -static FLATTEN void unmarshal_Twalk(struct _unmarshal_ctx *ctx, struct lib9p_msg_Twalk *out) { - memset(out, 0, sizeof(*out)); - ctx->net_offset += 4; - ctx->net_offset += 1; - unmarshal_tag(ctx, &out->tag); - unmarshal_fid(ctx, &out->fid); - unmarshal_fid(ctx, &out->newfid); - unmarshal_2(ctx, &out->nwname); - out->wname = ctx->extra; - ctx->extra += sizeof(out->wname[0]) * out->nwname; - for (typeof(out->nwname) i = 0; i < out->nwname; i++) - unmarshal_s(ctx, &out->wname[i]); -} - -static FLATTEN void unmarshal_Rwalk(struct _unmarshal_ctx *ctx, struct lib9p_msg_Rwalk *out) { - memset(out, 0, sizeof(*out)); - ctx->net_offset += 4; - ctx->net_offset += 1; - unmarshal_tag(ctx, &out->tag); - unmarshal_2(ctx, &out->nwqid); - out->wqid = ctx->extra; - ctx->extra += sizeof(out->wqid[0]) * out->nwqid; - for (typeof(out->nwqid) i = 0; i < out->nwqid; i++) - unmarshal_qid(ctx, &out->wqid[i]); -} - -static FLATTEN void unmarshal_Topen(struct _unmarshal_ctx *ctx, struct lib9p_msg_Topen *out) { - memset(out, 0, sizeof(*out)); - ctx->net_offset += 4; - ctx->net_offset += 1; - unmarshal_tag(ctx, &out->tag); - unmarshal_fid(ctx, &out->fid); - unmarshal_o(ctx, &out->mode); -} - -static FLATTEN void unmarshal_Ropen(struct _unmarshal_ctx *ctx, struct lib9p_msg_Ropen *out) { - memset(out, 0, sizeof(*out)); - ctx->net_offset += 4; - ctx->net_offset += 1; - unmarshal_tag(ctx, &out->tag); - unmarshal_qid(ctx, &out->qid); - unmarshal_4(ctx, &out->iounit); -} - -static FLATTEN void unmarshal_Tcreate(struct _unmarshal_ctx *ctx, struct lib9p_msg_Tcreate *out) { - memset(out, 0, sizeof(*out)); - ctx->net_offset += 4; - ctx->net_offset += 1; - unmarshal_tag(ctx, &out->tag); - unmarshal_fid(ctx, &out->fid); - unmarshal_s(ctx, &out->name); - unmarshal_dm(ctx, &out->perm); - unmarshal_o(ctx, &out->mode); -} - -static FLATTEN void unmarshal_Rcreate(struct _unmarshal_ctx *ctx, struct lib9p_msg_Rcreate *out) { - memset(out, 0, sizeof(*out)); - ctx->net_offset += 4; - ctx->net_offset += 1; - unmarshal_tag(ctx, &out->tag); - unmarshal_qid(ctx, &out->qid); - unmarshal_4(ctx, &out->iounit); -} - -static FLATTEN void unmarshal_Tread(struct _unmarshal_ctx *ctx, struct lib9p_msg_Tread *out) { - memset(out, 0, sizeof(*out)); - ctx->net_offset += 4; - ctx->net_offset += 1; - unmarshal_tag(ctx, &out->tag); - unmarshal_fid(ctx, &out->fid); - unmarshal_8(ctx, &out->offset); - unmarshal_4(ctx, &out->count); -} - -static FLATTEN void unmarshal_Rread(struct _unmarshal_ctx *ctx, struct lib9p_msg_Rread *out) { - memset(out, 0, sizeof(*out)); - ctx->net_offset += 4; - ctx->net_offset += 1; - unmarshal_tag(ctx, &out->tag); - unmarshal_d(ctx, &out->data); -} - -static FLATTEN void unmarshal_Twrite(struct _unmarshal_ctx *ctx, struct lib9p_msg_Twrite *out) { - memset(out, 0, sizeof(*out)); - ctx->net_offset += 4; - ctx->net_offset += 1; - unmarshal_tag(ctx, &out->tag); - unmarshal_fid(ctx, &out->fid); - unmarshal_8(ctx, &out->offset); - unmarshal_d(ctx, &out->data); -} - -static FLATTEN void unmarshal_Rwrite(struct _unmarshal_ctx *ctx, struct lib9p_msg_Rwrite *out) { - memset(out, 0, sizeof(*out)); - ctx->net_offset += 4; - ctx->net_offset += 1; - unmarshal_tag(ctx, &out->tag); - unmarshal_4(ctx, &out->count); -} - -static FLATTEN void unmarshal_Tclunk(struct _unmarshal_ctx *ctx, struct lib9p_msg_Tclunk *out) { - memset(out, 0, sizeof(*out)); - ctx->net_offset += 4; - ctx->net_offset += 1; - unmarshal_tag(ctx, &out->tag); - unmarshal_fid(ctx, &out->fid); -} - -static FLATTEN void unmarshal_Rclunk(struct _unmarshal_ctx *ctx, struct lib9p_msg_Rclunk *out) { - memset(out, 0, sizeof(*out)); - ctx->net_offset += 4; - ctx->net_offset += 1; - unmarshal_tag(ctx, &out->tag); -} - -static FLATTEN void unmarshal_Tremove(struct _unmarshal_ctx *ctx, struct lib9p_msg_Tremove *out) { - memset(out, 0, sizeof(*out)); - ctx->net_offset += 4; - ctx->net_offset += 1; - unmarshal_tag(ctx, &out->tag); - unmarshal_fid(ctx, &out->fid); -} - -static FLATTEN void unmarshal_Rremove(struct _unmarshal_ctx *ctx, struct lib9p_msg_Rremove *out) { - memset(out, 0, sizeof(*out)); - ctx->net_offset += 4; - ctx->net_offset += 1; - unmarshal_tag(ctx, &out->tag); -} - -static FLATTEN void unmarshal_Tstat(struct _unmarshal_ctx *ctx, struct lib9p_msg_Tstat *out) { - memset(out, 0, sizeof(*out)); - ctx->net_offset += 4; - ctx->net_offset += 1; - unmarshal_tag(ctx, &out->tag); - unmarshal_fid(ctx, &out->fid); -} - -static FLATTEN void unmarshal_Rstat(struct _unmarshal_ctx *ctx, struct lib9p_msg_Rstat *out) { - memset(out, 0, sizeof(*out)); - ctx->net_offset += 4; - ctx->net_offset += 1; - unmarshal_tag(ctx, &out->tag); - ctx->net_offset += 2; - unmarshal_stat(ctx, &out->stat); -} - -static FLATTEN void unmarshal_Twstat(struct _unmarshal_ctx *ctx, struct lib9p_msg_Twstat *out) { - memset(out, 0, sizeof(*out)); - ctx->net_offset += 4; - ctx->net_offset += 1; - unmarshal_tag(ctx, &out->tag); - unmarshal_fid(ctx, &out->fid); - ctx->net_offset += 2; - unmarshal_stat(ctx, &out->stat); -} - -static FLATTEN void unmarshal_Rwstat(struct _unmarshal_ctx *ctx, struct lib9p_msg_Rwstat *out) { - memset(out, 0, sizeof(*out)); - ctx->net_offset += 4; - ctx->net_offset += 1; - unmarshal_tag(ctx, &out->tag); -} - -#endif /* defined(CONFIG_9P_ENABLE_9P2000) || defined(CONFIG_9P_ENABLE_9P2000_e) || defined(CONFIG_9P_ENABLE_9P2000_u) */ -#if defined(CONFIG_9P_ENABLE_9P2000_e) -static FLATTEN void unmarshal_Tsession(struct _unmarshal_ctx *ctx, struct lib9p_msg_Tsession *out) { - memset(out, 0, sizeof(*out)); - ctx->net_offset += 4; - ctx->net_offset += 1; - unmarshal_tag(ctx, &out->tag); - unmarshal_8(ctx, &out->key); -} - -static FLATTEN void unmarshal_Rsession(struct _unmarshal_ctx *ctx, struct lib9p_msg_Rsession *out) { - memset(out, 0, sizeof(*out)); - ctx->net_offset += 4; - ctx->net_offset += 1; - unmarshal_tag(ctx, &out->tag); -} - -static FLATTEN void unmarshal_Tsread(struct _unmarshal_ctx *ctx, struct lib9p_msg_Tsread *out) { - memset(out, 0, sizeof(*out)); - ctx->net_offset += 4; - ctx->net_offset += 1; - unmarshal_tag(ctx, &out->tag); - unmarshal_4(ctx, &out->fid); - unmarshal_2(ctx, &out->nwname); - out->wname = ctx->extra; - ctx->extra += sizeof(out->wname[0]) * out->nwname; - for (typeof(out->nwname) i = 0; i < out->nwname; i++) - unmarshal_s(ctx, &out->wname[i]); -} - -static FLATTEN void unmarshal_Rsread(struct _unmarshal_ctx *ctx, struct lib9p_msg_Rsread *out) { - memset(out, 0, sizeof(*out)); - ctx->net_offset += 4; - ctx->net_offset += 1; - unmarshal_tag(ctx, &out->tag); - unmarshal_d(ctx, &out->data); -} - -static FLATTEN void unmarshal_Tswrite(struct _unmarshal_ctx *ctx, struct lib9p_msg_Tswrite *out) { - memset(out, 0, sizeof(*out)); - ctx->net_offset += 4; - ctx->net_offset += 1; - unmarshal_tag(ctx, &out->tag); - unmarshal_4(ctx, &out->fid); - unmarshal_2(ctx, &out->nwname); - out->wname = ctx->extra; - ctx->extra += sizeof(out->wname[0]) * out->nwname; - for (typeof(out->nwname) i = 0; i < out->nwname; i++) - unmarshal_s(ctx, &out->wname[i]); - unmarshal_d(ctx, &out->data); -} - -static FLATTEN void unmarshal_Rswrite(struct _unmarshal_ctx *ctx, struct lib9p_msg_Rswrite *out) { - memset(out, 0, sizeof(*out)); - ctx->net_offset += 4; - ctx->net_offset += 1; - unmarshal_tag(ctx, &out->tag); - unmarshal_4(ctx, &out->count); -} -#endif /* defined(CONFIG_9P_ENABLE_9P2000_e) */ +#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */ +#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u +static bool marshal_Tstat(struct lib9p_ctx *ctx, struct lib9p_msg_Tstat *val, struct _marshal_ret *ret) { + uint32_t needed_size = 11; + if (needed_size > ctx->max_msg_size) { + lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", + "Tstat", + ctx->version ? "negotiated" : "client", + ctx->max_msg_size); + return true; + } + uint32_t offsetof_end = needed_size; + uint32_t offsetof_size = 0; + MARSHAL_U32LE(ctx, offsetof_end - offsetof_size); + MARSHAL_U8LE(ctx, 124); + MARSHAL_U16LE(ctx, val->tag); + MARSHAL_U32LE(ctx, val->fid); + return false; +} -/* marshal_* ******************************************************************/ +static bool marshal_Rstat(struct lib9p_ctx *ctx, struct lib9p_msg_Rstat *val, struct _marshal_ret *ret) { + uint32_t needed_size = 58 + val->stat.file_name.len + val->stat.file_owner_uid.len + val->stat.file_owner_gid.len + val->stat.file_last_modified_uid.len; +#if CONFIG_9P_ENABLE_9P2000_u + if is_ver(ctx, 9P2000_u) { + needed_size += 14 + val->stat.file_extension.len; + } +#endif /* CONFIG_9P_ENABLE_9P2000_u */ + if (needed_size > ctx->max_msg_size) { + lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", + "Rstat", + ctx->version ? "negotiated" : "server", + ctx->max_msg_size); + return true; + } + uint32_t offsetof_end = needed_size; + uint32_t offsetof_size = 0; + uint32_t offsetof_stat = 9; + MARSHAL_U32LE(ctx, offsetof_end - offsetof_size); + MARSHAL_U8LE(ctx, 125); + MARSHAL_U16LE(ctx, val->tag); + MARSHAL_U16LE(ctx, offsetof_end - offsetof_stat); + uint32_t offsetof_stat_end = 49 + val->stat.file_name.len + val->stat.file_owner_uid.len + val->stat.file_owner_gid.len + val->stat.file_last_modified_uid.len; +#if CONFIG_9P_ENABLE_9P2000_u + if is_ver(ctx, 9P2000_u) { + offsetof_stat_end += 14 + val->stat.file_extension.len; + } +#endif /* CONFIG_9P_ENABLE_9P2000_u */ + uint32_t offsetof_stat_kern_type = 2; + MARSHAL_U16LE(ctx, offsetof_stat_end - offsetof_stat_kern_type); + MARSHAL_U16LE(ctx, val->stat.kern_type); + MARSHAL_U32LE(ctx, val->stat.kern_dev); + MARSHAL_U8LE(ctx, val->stat.file_qid.type & qt_masks[ctx->version]); + MARSHAL_U32LE(ctx, val->stat.file_qid.vers); + MARSHAL_U64LE(ctx, val->stat.file_qid.path); + MARSHAL_U32LE(ctx, val->stat.file_mode & dm_masks[ctx->version]); + MARSHAL_U32LE(ctx, val->stat.file_atime); + MARSHAL_U32LE(ctx, val->stat.file_mtime); + MARSHAL_U64LE(ctx, val->stat.file_size); + MARSHAL_U16LE(ctx, val->stat.file_name.len); + MARSHAL_BYTES_ZEROCOPY(ctx, val->stat.file_name.utf8, val->stat.file_name.len); + MARSHAL_U16LE(ctx, val->stat.file_owner_uid.len); + MARSHAL_BYTES_ZEROCOPY(ctx, val->stat.file_owner_uid.utf8, val->stat.file_owner_uid.len); + MARSHAL_U16LE(ctx, val->stat.file_owner_gid.len); + MARSHAL_BYTES_ZEROCOPY(ctx, val->stat.file_owner_gid.utf8, val->stat.file_owner_gid.len); + MARSHAL_U16LE(ctx, val->stat.file_last_modified_uid.len); + MARSHAL_BYTES_ZEROCOPY(ctx, val->stat.file_last_modified_uid.utf8, val->stat.file_last_modified_uid.len); +#if CONFIG_9P_ENABLE_9P2000_u + if (is_ver(ctx, 9P2000_u)) { + MARSHAL_U16LE(ctx, val->stat.file_extension.len); + MARSHAL_BYTES_ZEROCOPY(ctx, val->stat.file_extension.utf8, val->stat.file_extension.len); + MARSHAL_U32LE(ctx, val->stat.file_owner_n_uid); + MARSHAL_U32LE(ctx, val->stat.file_owner_n_gid); + MARSHAL_U32LE(ctx, val->stat.file_last_modified_n_uid); + } +#endif /* CONFIG_9P_ENABLE_9P2000_u */ + return false; +} + +static bool marshal_Twstat(struct lib9p_ctx *ctx, struct lib9p_msg_Twstat *val, struct _marshal_ret *ret) { + uint32_t needed_size = 62 + val->stat.file_name.len + val->stat.file_owner_uid.len + val->stat.file_owner_gid.len + val->stat.file_last_modified_uid.len; +#if CONFIG_9P_ENABLE_9P2000_u + if is_ver(ctx, 9P2000_u) { + needed_size += 14 + val->stat.file_extension.len; + } +#endif /* CONFIG_9P_ENABLE_9P2000_u */ + if (needed_size > ctx->max_msg_size) { + lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", + "Twstat", + ctx->version ? "negotiated" : "client", + ctx->max_msg_size); + return true; + } + uint32_t offsetof_end = needed_size; + uint32_t offsetof_size = 0; + uint32_t offsetof_stat = 13; + MARSHAL_U32LE(ctx, offsetof_end - offsetof_size); + MARSHAL_U8LE(ctx, 126); + MARSHAL_U16LE(ctx, val->tag); + MARSHAL_U32LE(ctx, val->fid); + MARSHAL_U16LE(ctx, offsetof_end - offsetof_stat); + uint32_t offsetof_stat_end = 49 + val->stat.file_name.len + val->stat.file_owner_uid.len + val->stat.file_owner_gid.len + val->stat.file_last_modified_uid.len; +#if CONFIG_9P_ENABLE_9P2000_u + if is_ver(ctx, 9P2000_u) { + offsetof_stat_end += 14 + val->stat.file_extension.len; + } +#endif /* CONFIG_9P_ENABLE_9P2000_u */ + uint32_t offsetof_stat_kern_type = 2; + MARSHAL_U16LE(ctx, offsetof_stat_end - offsetof_stat_kern_type); + MARSHAL_U16LE(ctx, val->stat.kern_type); + MARSHAL_U32LE(ctx, val->stat.kern_dev); + MARSHAL_U8LE(ctx, val->stat.file_qid.type & qt_masks[ctx->version]); + MARSHAL_U32LE(ctx, val->stat.file_qid.vers); + MARSHAL_U64LE(ctx, val->stat.file_qid.path); + MARSHAL_U32LE(ctx, val->stat.file_mode & dm_masks[ctx->version]); + MARSHAL_U32LE(ctx, val->stat.file_atime); + MARSHAL_U32LE(ctx, val->stat.file_mtime); + MARSHAL_U64LE(ctx, val->stat.file_size); + MARSHAL_U16LE(ctx, val->stat.file_name.len); + MARSHAL_BYTES_ZEROCOPY(ctx, val->stat.file_name.utf8, val->stat.file_name.len); + MARSHAL_U16LE(ctx, val->stat.file_owner_uid.len); + MARSHAL_BYTES_ZEROCOPY(ctx, val->stat.file_owner_uid.utf8, val->stat.file_owner_uid.len); + MARSHAL_U16LE(ctx, val->stat.file_owner_gid.len); + MARSHAL_BYTES_ZEROCOPY(ctx, val->stat.file_owner_gid.utf8, val->stat.file_owner_gid.len); + MARSHAL_U16LE(ctx, val->stat.file_last_modified_uid.len); + MARSHAL_BYTES_ZEROCOPY(ctx, val->stat.file_last_modified_uid.utf8, val->stat.file_last_modified_uid.len); +#if CONFIG_9P_ENABLE_9P2000_u + if (is_ver(ctx, 9P2000_u)) { + MARSHAL_U16LE(ctx, val->stat.file_extension.len); + MARSHAL_BYTES_ZEROCOPY(ctx, val->stat.file_extension.utf8, val->stat.file_extension.len); + MARSHAL_U32LE(ctx, val->stat.file_owner_n_uid); + MARSHAL_U32LE(ctx, val->stat.file_owner_n_gid); + MARSHAL_U32LE(ctx, val->stat.file_last_modified_n_uid); + } +#endif /* CONFIG_9P_ENABLE_9P2000_u */ + return false; +} + +static bool marshal_Rwstat(struct lib9p_ctx *ctx, struct lib9p_msg_Rwstat *val, struct _marshal_ret *ret) { + uint32_t needed_size = 7; + if (needed_size > ctx->max_msg_size) { + lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", + "Rwstat", + ctx->version ? "negotiated" : "server", + ctx->max_msg_size); + return true; + } + uint32_t offsetof_end = needed_size; + uint32_t offsetof_size = 0; + MARSHAL_U32LE(ctx, offsetof_end - offsetof_size); + MARSHAL_U8LE(ctx, 127); + MARSHAL_U16LE(ctx, val->tag); + return false; +} + +#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */ +#if CONFIG_9P_ENABLE_9P2000_p9p +static bool marshal_Topenfd(struct lib9p_ctx *ctx, struct lib9p_msg_Topenfd *val, struct _marshal_ret *ret) { + uint32_t needed_size = 12; + if (needed_size > ctx->max_msg_size) { + lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", + "Topenfd", + ctx->version ? "negotiated" : "client", + ctx->max_msg_size); + return true; + } + uint32_t offsetof_end = needed_size; + uint32_t offsetof_size = 0; + MARSHAL_U32LE(ctx, offsetof_end - offsetof_size); + MARSHAL_U8LE(ctx, 98); + MARSHAL_U16LE(ctx, val->tag); + MARSHAL_U32LE(ctx, val->fid); + MARSHAL_U8LE(ctx, val->mode & o_masks[ctx->version]); + return false; +} + +static bool marshal_Ropenfd(struct lib9p_ctx *ctx, struct lib9p_msg_Ropenfd *val, struct _marshal_ret *ret) { + uint32_t needed_size = 28; + if (needed_size > ctx->max_msg_size) { + lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", + "Ropenfd", + ctx->version ? "negotiated" : "server", + ctx->max_msg_size); + return true; + } + uint32_t offsetof_end = needed_size; + uint32_t offsetof_size = 0; + MARSHAL_U32LE(ctx, offsetof_end - offsetof_size); + MARSHAL_U8LE(ctx, 99); + MARSHAL_U16LE(ctx, val->tag); + MARSHAL_U8LE(ctx, val->qid.type & qt_masks[ctx->version]); + MARSHAL_U32LE(ctx, val->qid.vers); + MARSHAL_U64LE(ctx, val->qid.path); + MARSHAL_U32LE(ctx, val->iounit); + MARSHAL_U32LE(ctx, val->unixfd); + return false; +} + +#endif /* CONFIG_9P_ENABLE_9P2000_p9p */ +#if CONFIG_9P_ENABLE_9P2000_L +static bool marshal_Rlerror(struct lib9p_ctx *ctx, struct lib9p_msg_Rlerror *val, struct _marshal_ret *ret) { + uint32_t needed_size = 11; + if (needed_size > ctx->max_msg_size) { + lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", + "Rlerror", + ctx->version ? "negotiated" : "server", + ctx->max_msg_size); + return true; + } + uint32_t offsetof_end = needed_size; + uint32_t offsetof_size = 0; + MARSHAL_U32LE(ctx, offsetof_end - offsetof_size); + MARSHAL_U8LE(ctx, 7); + MARSHAL_U16LE(ctx, val->tag); + MARSHAL_U32LE(ctx, val->errnum); + return false; +} + +static bool marshal_Tstatfs(struct lib9p_ctx *ctx, struct lib9p_msg_Tstatfs *val, struct _marshal_ret *ret) { + uint32_t needed_size = 11; + if (needed_size > ctx->max_msg_size) { + lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", + "Tstatfs", + ctx->version ? "negotiated" : "client", + ctx->max_msg_size); + return true; + } + uint32_t offsetof_end = needed_size; + uint32_t offsetof_size = 0; + MARSHAL_U32LE(ctx, offsetof_end - offsetof_size); + MARSHAL_U8LE(ctx, 8); + MARSHAL_U16LE(ctx, val->tag); + MARSHAL_U32LE(ctx, val->fid); + return false; +} + +static bool marshal_Rstatfs(struct lib9p_ctx *ctx, struct lib9p_msg_Rstatfs *val, struct _marshal_ret *ret) { + uint32_t needed_size = 67; + if (needed_size > ctx->max_msg_size) { + lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", + "Rstatfs", + ctx->version ? "negotiated" : "server", + ctx->max_msg_size); + return true; + } + uint32_t offsetof_end = needed_size; + uint32_t offsetof_size = 0; + MARSHAL_U32LE(ctx, offsetof_end - offsetof_size); + MARSHAL_U8LE(ctx, 9); + MARSHAL_U16LE(ctx, val->tag); + MARSHAL_U32LE(ctx, val->type); + MARSHAL_U32LE(ctx, val->bsize); + MARSHAL_U64LE(ctx, val->blocks); + MARSHAL_U64LE(ctx, val->bfree); + MARSHAL_U64LE(ctx, val->bavail); + MARSHAL_U64LE(ctx, val->files); + MARSHAL_U64LE(ctx, val->ffree); + MARSHAL_U64LE(ctx, val->fsid); + MARSHAL_U32LE(ctx, val->namelen); + return false; +} + +static bool marshal_Tlopen(struct lib9p_ctx *ctx, struct lib9p_msg_Tlopen *val, struct _marshal_ret *ret) { + uint32_t needed_size = 15; + if (needed_size > ctx->max_msg_size) { + lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", + "Tlopen", + ctx->version ? "negotiated" : "client", + ctx->max_msg_size); + return true; + } + uint32_t offsetof_end = needed_size; + uint32_t offsetof_size = 0; + MARSHAL_U32LE(ctx, offsetof_end - offsetof_size); + MARSHAL_U8LE(ctx, 12); + MARSHAL_U16LE(ctx, val->tag); + MARSHAL_U32LE(ctx, val->fid); + MARSHAL_U32LE(ctx, val->flags & lo_masks[ctx->version]); + return false; +} + +static bool marshal_Rlopen(struct lib9p_ctx *ctx, struct lib9p_msg_Rlopen *val, struct _marshal_ret *ret) { + uint32_t needed_size = 24; + if (needed_size > ctx->max_msg_size) { + lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", + "Rlopen", + ctx->version ? "negotiated" : "server", + ctx->max_msg_size); + return true; + } + uint32_t offsetof_end = needed_size; + uint32_t offsetof_size = 0; + MARSHAL_U32LE(ctx, offsetof_end - offsetof_size); + MARSHAL_U8LE(ctx, 13); + MARSHAL_U16LE(ctx, val->tag); + MARSHAL_U8LE(ctx, val->qid.type & qt_masks[ctx->version]); + MARSHAL_U32LE(ctx, val->qid.vers); + MARSHAL_U64LE(ctx, val->qid.path); + MARSHAL_U32LE(ctx, val->iounit); + return false; +} + +static bool marshal_Tlcreate(struct lib9p_ctx *ctx, struct lib9p_msg_Tlcreate *val, struct _marshal_ret *ret) { + uint32_t needed_size = 25 + val->name.len; + if (needed_size > ctx->max_msg_size) { + lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", + "Tlcreate", + ctx->version ? "negotiated" : "client", + ctx->max_msg_size); + return true; + } + uint32_t offsetof_end = needed_size; + uint32_t offsetof_size = 0; + MARSHAL_U32LE(ctx, offsetof_end - offsetof_size); + MARSHAL_U8LE(ctx, 14); + MARSHAL_U16LE(ctx, val->tag); + MARSHAL_U32LE(ctx, val->fid); + MARSHAL_U16LE(ctx, val->name.len); + MARSHAL_BYTES_ZEROCOPY(ctx, val->name.utf8, val->name.len); + MARSHAL_U32LE(ctx, val->flags & lo_masks[ctx->version]); + MARSHAL_U32LE(ctx, val->mode & mode_masks[ctx->version]); + MARSHAL_U32LE(ctx, val->gid); + return false; +} + +static bool marshal_Rlcreate(struct lib9p_ctx *ctx, struct lib9p_msg_Rlcreate *val, struct _marshal_ret *ret) { + uint32_t needed_size = 24; + if (needed_size > ctx->max_msg_size) { + lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", + "Rlcreate", + ctx->version ? "negotiated" : "server", + ctx->max_msg_size); + return true; + } + uint32_t offsetof_end = needed_size; + uint32_t offsetof_size = 0; + MARSHAL_U32LE(ctx, offsetof_end - offsetof_size); + MARSHAL_U8LE(ctx, 15); + MARSHAL_U16LE(ctx, val->tag); + MARSHAL_U8LE(ctx, val->qid.type & qt_masks[ctx->version]); + MARSHAL_U32LE(ctx, val->qid.vers); + MARSHAL_U64LE(ctx, val->qid.path); + MARSHAL_U32LE(ctx, val->iounit); + return false; +} + +static bool marshal_Tsymlink(struct lib9p_ctx *ctx, struct lib9p_msg_Tsymlink *val, struct _marshal_ret *ret) { + uint32_t needed_size = 19 + val->name.len + val->symtgt.len; + if (needed_size > ctx->max_msg_size) { + lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", + "Tsymlink", + ctx->version ? "negotiated" : "client", + ctx->max_msg_size); + return true; + } + uint32_t offsetof_end = needed_size; + uint32_t offsetof_size = 0; + MARSHAL_U32LE(ctx, offsetof_end - offsetof_size); + MARSHAL_U8LE(ctx, 16); + MARSHAL_U16LE(ctx, val->tag); + MARSHAL_U32LE(ctx, val->fid); + MARSHAL_U16LE(ctx, val->name.len); + MARSHAL_BYTES_ZEROCOPY(ctx, val->name.utf8, val->name.len); + MARSHAL_U16LE(ctx, val->symtgt.len); + MARSHAL_BYTES_ZEROCOPY(ctx, val->symtgt.utf8, val->symtgt.len); + MARSHAL_U32LE(ctx, val->gid); + return false; +} + +static bool marshal_Rsymlink(struct lib9p_ctx *ctx, struct lib9p_msg_Rsymlink *val, struct _marshal_ret *ret) { + uint32_t needed_size = 20; + if (needed_size > ctx->max_msg_size) { + lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", + "Rsymlink", + ctx->version ? "negotiated" : "server", + ctx->max_msg_size); + return true; + } + uint32_t offsetof_end = needed_size; + uint32_t offsetof_size = 0; + MARSHAL_U32LE(ctx, offsetof_end - offsetof_size); + MARSHAL_U8LE(ctx, 17); + MARSHAL_U16LE(ctx, val->tag); + MARSHAL_U8LE(ctx, val->qid.type & qt_masks[ctx->version]); + MARSHAL_U32LE(ctx, val->qid.vers); + MARSHAL_U64LE(ctx, val->qid.path); + return false; +} + +static bool marshal_Tmknod(struct lib9p_ctx *ctx, struct lib9p_msg_Tmknod *val, struct _marshal_ret *ret) { + uint32_t needed_size = 29 + val->name.len; + if (needed_size > ctx->max_msg_size) { + lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", + "Tmknod", + ctx->version ? "negotiated" : "client", + ctx->max_msg_size); + return true; + } + uint32_t offsetof_end = needed_size; + uint32_t offsetof_size = 0; + MARSHAL_U32LE(ctx, offsetof_end - offsetof_size); + MARSHAL_U8LE(ctx, 18); + MARSHAL_U16LE(ctx, val->tag); + MARSHAL_U32LE(ctx, val->dfid); + MARSHAL_U16LE(ctx, val->name.len); + MARSHAL_BYTES_ZEROCOPY(ctx, val->name.utf8, val->name.len); + MARSHAL_U32LE(ctx, val->mode & mode_masks[ctx->version]); + MARSHAL_U32LE(ctx, val->major); + MARSHAL_U32LE(ctx, val->minor); + MARSHAL_U32LE(ctx, val->gid); + return false; +} -static ALWAYS_INLINE bool _marshal_too_large(struct _marshal_ctx *ctx) { - lib9p_errorf(ctx->ctx, LINUX_ERANGE, "%s too large to marshal into %s limit (limit=%"PRIu32")", - (ctx->net_bytes[4] % 2 == 0) ? "T-message" : "R-message", - ctx->ctx->version ? "negotiated" : ((ctx->net_bytes[4] % 2 == 0) ? "client" : "server"), - ctx->ctx->max_msg_size); - return true; +static bool marshal_Rmknod(struct lib9p_ctx *ctx, struct lib9p_msg_Rmknod *val, struct _marshal_ret *ret) { + uint32_t needed_size = 20; + if (needed_size > ctx->max_msg_size) { + lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", + "Rmknod", + ctx->version ? "negotiated" : "server", + ctx->max_msg_size); + return true; + } + uint32_t offsetof_end = needed_size; + uint32_t offsetof_size = 0; + MARSHAL_U32LE(ctx, offsetof_end - offsetof_size); + MARSHAL_U8LE(ctx, 19); + MARSHAL_U16LE(ctx, val->tag); + MARSHAL_U8LE(ctx, val->qid.type & qt_masks[ctx->version]); + MARSHAL_U32LE(ctx, val->qid.vers); + MARSHAL_U64LE(ctx, val->qid.path); + return false; } -static ALWAYS_INLINE bool marshal_1(struct _marshal_ctx *ctx, uint8_t *val) { - if (ctx->net_offset + 1 > ctx->ctx->max_msg_size) - return _marshal_too_large(ctx); - ctx->net_bytes[ctx->net_offset] = *val; - ctx->net_offset += 1; +static bool marshal_Trename(struct lib9p_ctx *ctx, struct lib9p_msg_Trename *val, struct _marshal_ret *ret) { + uint32_t needed_size = 17 + val->name.len; + if (needed_size > ctx->max_msg_size) { + lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", + "Trename", + ctx->version ? "negotiated" : "client", + ctx->max_msg_size); + return true; + } + uint32_t offsetof_end = needed_size; + uint32_t offsetof_size = 0; + MARSHAL_U32LE(ctx, offsetof_end - offsetof_size); + MARSHAL_U8LE(ctx, 20); + MARSHAL_U16LE(ctx, val->tag); + MARSHAL_U32LE(ctx, val->fid); + MARSHAL_U32LE(ctx, val->dfid); + MARSHAL_U16LE(ctx, val->name.len); + MARSHAL_BYTES_ZEROCOPY(ctx, val->name.utf8, val->name.len); return false; } -static ALWAYS_INLINE bool marshal_2(struct _marshal_ctx *ctx, uint16_t *val) { - if (ctx->net_offset + 2 > ctx->ctx->max_msg_size) - return _marshal_too_large(ctx); - encode_u16le(*val, &ctx->net_bytes[ctx->net_offset]); - ctx->net_offset += 2; +static bool marshal_Rrename(struct lib9p_ctx *ctx, struct lib9p_msg_Rrename *val, struct _marshal_ret *ret) { + uint32_t needed_size = 7; + if (needed_size > ctx->max_msg_size) { + lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", + "Rrename", + ctx->version ? "negotiated" : "server", + ctx->max_msg_size); + return true; + } + uint32_t offsetof_end = needed_size; + uint32_t offsetof_size = 0; + MARSHAL_U32LE(ctx, offsetof_end - offsetof_size); + MARSHAL_U8LE(ctx, 21); + MARSHAL_U16LE(ctx, val->tag); return false; } -static ALWAYS_INLINE bool marshal_4(struct _marshal_ctx *ctx, uint32_t *val) { - if (ctx->net_offset + 4 > ctx->ctx->max_msg_size) +static bool marshal_Treadlink(struct lib9p_ctx *ctx, struct lib9p_msg_Treadlink *val, struct _marshal_ret *ret) { + uint32_t needed_size = 11; + if (needed_size > ctx->max_msg_size) { + lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", + "Treadlink", + ctx->version ? "negotiated" : "client", + ctx->max_msg_size); return true; - encode_u32le(*val, &ctx->net_bytes[ctx->net_offset]); - ctx->net_offset += 4; + } + uint32_t offsetof_end = needed_size; + uint32_t offsetof_size = 0; + MARSHAL_U32LE(ctx, offsetof_end - offsetof_size); + MARSHAL_U8LE(ctx, 22); + MARSHAL_U16LE(ctx, val->tag); + MARSHAL_U32LE(ctx, val->fid); return false; } -static ALWAYS_INLINE bool marshal_8(struct _marshal_ctx *ctx, uint64_t *val) { - if (ctx->net_offset + 8 > ctx->ctx->max_msg_size) +static bool marshal_Rreadlink(struct lib9p_ctx *ctx, struct lib9p_msg_Rreadlink *val, struct _marshal_ret *ret) { + uint32_t needed_size = 9 + val->target.len; + if (needed_size > ctx->max_msg_size) { + lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", + "Rreadlink", + ctx->version ? "negotiated" : "server", + ctx->max_msg_size); return true; - encode_u64le(*val, &ctx->net_bytes[ctx->net_offset]); - ctx->net_offset += 8; + } + uint32_t offsetof_end = needed_size; + uint32_t offsetof_size = 0; + MARSHAL_U32LE(ctx, offsetof_end - offsetof_size); + MARSHAL_U8LE(ctx, 23); + MARSHAL_U16LE(ctx, val->tag); + MARSHAL_U16LE(ctx, val->target.len); + MARSHAL_BYTES_ZEROCOPY(ctx, val->target.utf8, val->target.len); return false; } -#if defined(CONFIG_9P_ENABLE_9P2000) || defined(CONFIG_9P_ENABLE_9P2000_e) || defined(CONFIG_9P_ENABLE_9P2000_u) -static ALWAYS_INLINE bool marshal_tag(struct _marshal_ctx *ctx, lib9p_tag_t *val) { - return marshal_2(ctx, (uint16_t *)val); +static bool marshal_Tgetattr(struct lib9p_ctx *ctx, struct lib9p_msg_Tgetattr *val, struct _marshal_ret *ret) { + uint32_t needed_size = 19; + if (needed_size > ctx->max_msg_size) { + lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", + "Tgetattr", + ctx->version ? "negotiated" : "client", + ctx->max_msg_size); + return true; + } + uint32_t offsetof_end = needed_size; + uint32_t offsetof_size = 0; + MARSHAL_U32LE(ctx, offsetof_end - offsetof_size); + MARSHAL_U8LE(ctx, 24); + MARSHAL_U16LE(ctx, val->tag); + MARSHAL_U32LE(ctx, val->fid); + MARSHAL_U64LE(ctx, val->request_mask & getattr_masks[ctx->version]); + return false; } -static ALWAYS_INLINE bool marshal_fid(struct _marshal_ctx *ctx, lib9p_fid_t *val) { - return marshal_4(ctx, (uint32_t *)val); +static bool marshal_Rgetattr(struct lib9p_ctx *ctx, struct lib9p_msg_Rgetattr *val, struct _marshal_ret *ret) { + uint32_t needed_size = 160; + if (needed_size > ctx->max_msg_size) { + lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", + "Rgetattr", + ctx->version ? "negotiated" : "server", + ctx->max_msg_size); + return true; + } + uint32_t offsetof_end = needed_size; + uint32_t offsetof_size = 0; + MARSHAL_U32LE(ctx, offsetof_end - offsetof_size); + MARSHAL_U8LE(ctx, 25); + MARSHAL_U16LE(ctx, val->tag); + MARSHAL_U64LE(ctx, val->valid & getattr_masks[ctx->version]); + MARSHAL_U8LE(ctx, val->qid.type & qt_masks[ctx->version]); + MARSHAL_U32LE(ctx, val->qid.vers); + MARSHAL_U64LE(ctx, val->qid.path); + MARSHAL_U32LE(ctx, val->mode & mode_masks[ctx->version]); + MARSHAL_U32LE(ctx, val->uid); + MARSHAL_U32LE(ctx, val->gid); + MARSHAL_U64LE(ctx, val->nlink); + MARSHAL_U64LE(ctx, val->rdev); + MARSHAL_U64LE(ctx, val->filesize); + MARSHAL_U64LE(ctx, val->blksize); + MARSHAL_U64LE(ctx, val->blocks); + MARSHAL_U64LE(ctx, val->atime_sec); + MARSHAL_U64LE(ctx, val->atime_nsec); + MARSHAL_U64LE(ctx, val->mtime_sec); + MARSHAL_U64LE(ctx, val->mtime_nsec); + MARSHAL_U64LE(ctx, val->ctime_sec); + MARSHAL_U64LE(ctx, val->ctime_nsec); + MARSHAL_U64LE(ctx, val->btime_sec); + MARSHAL_U64LE(ctx, val->btime_nsec); + MARSHAL_U64LE(ctx, val->gen); + MARSHAL_U64LE(ctx, val->data_version); + return false; } -static ALWAYS_INLINE bool marshal_d(struct _marshal_ctx *ctx, struct lib9p_d *val) { - return false - || marshal_4(ctx, &val->len) - || ({ bool err = false; - for (typeof(val->len) i = 0; i < val->len && !err; i++) - err = marshal_1(ctx, (uint8_t *)&val->dat[i]); - err; }) - ; +static bool marshal_Tsetattr(struct lib9p_ctx *ctx, struct lib9p_msg_Tsetattr *val, struct _marshal_ret *ret) { + uint32_t needed_size = 67; + if (needed_size > ctx->max_msg_size) { + lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", + "Tsetattr", + ctx->version ? "negotiated" : "client", + ctx->max_msg_size); + return true; + } + uint32_t offsetof_end = needed_size; + uint32_t offsetof_size = 0; + MARSHAL_U32LE(ctx, offsetof_end - offsetof_size); + MARSHAL_U8LE(ctx, 26); + MARSHAL_U16LE(ctx, val->tag); + MARSHAL_U32LE(ctx, val->fid); + MARSHAL_U32LE(ctx, val->valid & setattr_masks[ctx->version]); + MARSHAL_U32LE(ctx, val->mode & mode_masks[ctx->version]); + MARSHAL_U32LE(ctx, val->uid); + MARSHAL_U32LE(ctx, val->gid); + MARSHAL_U64LE(ctx, val->filesize); + MARSHAL_U64LE(ctx, val->atime_sec); + MARSHAL_U64LE(ctx, val->atime_nsec); + MARSHAL_U64LE(ctx, val->mtime_sec); + MARSHAL_U64LE(ctx, val->mtime_nsec); + return false; +} + +static bool marshal_Rsetattr(struct lib9p_ctx *ctx, struct lib9p_msg_Rsetattr *val, struct _marshal_ret *ret) { + uint32_t needed_size = 7; + if (needed_size > ctx->max_msg_size) { + lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", + "Rsetattr", + ctx->version ? "negotiated" : "server", + ctx->max_msg_size); + return true; + } + uint32_t offsetof_end = needed_size; + uint32_t offsetof_size = 0; + MARSHAL_U32LE(ctx, offsetof_end - offsetof_size); + MARSHAL_U8LE(ctx, 27); + MARSHAL_U16LE(ctx, val->tag); + return false; +} + +static bool marshal_Txattrwalk(struct lib9p_ctx *ctx, struct lib9p_msg_Txattrwalk *val, struct _marshal_ret *ret) { + uint32_t needed_size = 17 + val->name.len; + if (needed_size > ctx->max_msg_size) { + lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", + "Txattrwalk", + ctx->version ? "negotiated" : "client", + ctx->max_msg_size); + return true; + } + uint32_t offsetof_end = needed_size; + uint32_t offsetof_size = 0; + MARSHAL_U32LE(ctx, offsetof_end - offsetof_size); + MARSHAL_U8LE(ctx, 30); + MARSHAL_U16LE(ctx, val->tag); + MARSHAL_U32LE(ctx, val->fid); + MARSHAL_U32LE(ctx, val->newfid); + MARSHAL_U16LE(ctx, val->name.len); + MARSHAL_BYTES_ZEROCOPY(ctx, val->name.utf8, val->name.len); + return false; +} + +static bool marshal_Rxattrwalk(struct lib9p_ctx *ctx, struct lib9p_msg_Rxattrwalk *val, struct _marshal_ret *ret) { + uint32_t needed_size = 15; + if (needed_size > ctx->max_msg_size) { + lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", + "Rxattrwalk", + ctx->version ? "negotiated" : "server", + ctx->max_msg_size); + return true; + } + uint32_t offsetof_end = needed_size; + uint32_t offsetof_size = 0; + MARSHAL_U32LE(ctx, offsetof_end - offsetof_size); + MARSHAL_U8LE(ctx, 31); + MARSHAL_U16LE(ctx, val->tag); + MARSHAL_U64LE(ctx, val->attr_size); + return false; +} + +static bool marshal_Txattrcreate(struct lib9p_ctx *ctx, struct lib9p_msg_Txattrcreate *val, struct _marshal_ret *ret) { + uint32_t needed_size = 25 + val->name.len; + if (needed_size > ctx->max_msg_size) { + lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", + "Txattrcreate", + ctx->version ? "negotiated" : "client", + ctx->max_msg_size); + return true; + } + uint32_t offsetof_end = needed_size; + uint32_t offsetof_size = 0; + MARSHAL_U32LE(ctx, offsetof_end - offsetof_size); + MARSHAL_U8LE(ctx, 32); + MARSHAL_U16LE(ctx, val->tag); + MARSHAL_U32LE(ctx, val->fid); + MARSHAL_U16LE(ctx, val->name.len); + MARSHAL_BYTES_ZEROCOPY(ctx, val->name.utf8, val->name.len); + MARSHAL_U64LE(ctx, val->attr_size); + MARSHAL_U32LE(ctx, val->flags); + return false; +} + +static bool marshal_Rxattrcreate(struct lib9p_ctx *ctx, struct lib9p_msg_Rxattrcreate *val, struct _marshal_ret *ret) { + uint32_t needed_size = 7; + if (needed_size > ctx->max_msg_size) { + lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", + "Rxattrcreate", + ctx->version ? "negotiated" : "server", + ctx->max_msg_size); + return true; + } + uint32_t offsetof_end = needed_size; + uint32_t offsetof_size = 0; + MARSHAL_U32LE(ctx, offsetof_end - offsetof_size); + MARSHAL_U8LE(ctx, 33); + MARSHAL_U16LE(ctx, val->tag); + return false; +} + +static bool marshal_Treaddir(struct lib9p_ctx *ctx, struct lib9p_msg_Treaddir *val, struct _marshal_ret *ret) { + uint32_t needed_size = 23; + if (needed_size > ctx->max_msg_size) { + lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", + "Treaddir", + ctx->version ? "negotiated" : "client", + ctx->max_msg_size); + return true; + } + uint32_t offsetof_end = needed_size; + uint32_t offsetof_size = 0; + MARSHAL_U32LE(ctx, offsetof_end - offsetof_size); + MARSHAL_U8LE(ctx, 40); + MARSHAL_U16LE(ctx, val->tag); + MARSHAL_U32LE(ctx, val->fid); + MARSHAL_U64LE(ctx, val->offset); + MARSHAL_U32LE(ctx, val->count); + return false; +} + +static bool marshal_Rreaddir(struct lib9p_ctx *ctx, struct lib9p_msg_Rreaddir *val, struct _marshal_ret *ret) { + uint64_t needed_size = 11 + val->count; + if (needed_size > (uint64_t)(ctx->max_msg_size)) { + lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", + "Rreaddir", + ctx->version ? "negotiated" : "server", + ctx->max_msg_size); + return true; + } + uint32_t offsetof_end = (uint32_t)needed_size; + uint32_t offsetof_size = 0; + MARSHAL_U32LE(ctx, offsetof_end - offsetof_size); + MARSHAL_U8LE(ctx, 41); + MARSHAL_U16LE(ctx, val->tag); + MARSHAL_U32LE(ctx, val->count); + MARSHAL_BYTES_ZEROCOPY(ctx, val->data, val->count); + return false; +} + +static bool marshal_Tfsync(struct lib9p_ctx *ctx, struct lib9p_msg_Tfsync *val, struct _marshal_ret *ret) { + uint32_t needed_size = 15; + if (needed_size > ctx->max_msg_size) { + lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", + "Tfsync", + ctx->version ? "negotiated" : "client", + ctx->max_msg_size); + return true; + } + uint32_t offsetof_end = needed_size; + uint32_t offsetof_size = 0; + MARSHAL_U32LE(ctx, offsetof_end - offsetof_size); + MARSHAL_U8LE(ctx, 50); + MARSHAL_U16LE(ctx, val->tag); + MARSHAL_U32LE(ctx, val->fid); + MARSHAL_U32LE(ctx, val->datasync); + return false; +} + +static bool marshal_Rfsync(struct lib9p_ctx *ctx, struct lib9p_msg_Rfsync *val, struct _marshal_ret *ret) { + uint32_t needed_size = 7; + if (needed_size > ctx->max_msg_size) { + lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", + "Rfsync", + ctx->version ? "negotiated" : "server", + ctx->max_msg_size); + return true; + } + uint32_t offsetof_end = needed_size; + uint32_t offsetof_size = 0; + MARSHAL_U32LE(ctx, offsetof_end - offsetof_size); + MARSHAL_U8LE(ctx, 51); + MARSHAL_U16LE(ctx, val->tag); + return false; +} + +static bool marshal_Tlock(struct lib9p_ctx *ctx, struct lib9p_msg_Tlock *val, struct _marshal_ret *ret) { + uint32_t needed_size = 38 + val->client_id.len; + if (needed_size > ctx->max_msg_size) { + lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", + "Tlock", + ctx->version ? "negotiated" : "client", + ctx->max_msg_size); + return true; + } + uint32_t offsetof_end = needed_size; + uint32_t offsetof_size = 0; + MARSHAL_U32LE(ctx, offsetof_end - offsetof_size); + MARSHAL_U8LE(ctx, 52); + MARSHAL_U16LE(ctx, val->tag); + MARSHAL_U32LE(ctx, val->fid); + MARSHAL_U8LE(ctx, val->type); + MARSHAL_U32LE(ctx, val->flags & lock_flags_masks[ctx->version]); + MARSHAL_U64LE(ctx, val->start); + MARSHAL_U64LE(ctx, val->length); + MARSHAL_U32LE(ctx, val->proc_id); + MARSHAL_U16LE(ctx, val->client_id.len); + MARSHAL_BYTES_ZEROCOPY(ctx, val->client_id.utf8, val->client_id.len); + return false; +} + +static bool marshal_Rlock(struct lib9p_ctx *ctx, struct lib9p_msg_Rlock *val, struct _marshal_ret *ret) { + uint32_t needed_size = 8; + if (needed_size > ctx->max_msg_size) { + lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", + "Rlock", + ctx->version ? "negotiated" : "server", + ctx->max_msg_size); + return true; + } + uint32_t offsetof_end = needed_size; + uint32_t offsetof_size = 0; + MARSHAL_U32LE(ctx, offsetof_end - offsetof_size); + MARSHAL_U8LE(ctx, 53); + MARSHAL_U16LE(ctx, val->tag); + MARSHAL_U8LE(ctx, val->status); + return false; +} + +static bool marshal_Tgetlock(struct lib9p_ctx *ctx, struct lib9p_msg_Tgetlock *val, struct _marshal_ret *ret) { + uint32_t needed_size = 34 + val->client_id.len; + if (needed_size > ctx->max_msg_size) { + lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", + "Tgetlock", + ctx->version ? "negotiated" : "client", + ctx->max_msg_size); + return true; + } + uint32_t offsetof_end = needed_size; + uint32_t offsetof_size = 0; + MARSHAL_U32LE(ctx, offsetof_end - offsetof_size); + MARSHAL_U8LE(ctx, 54); + MARSHAL_U16LE(ctx, val->tag); + MARSHAL_U32LE(ctx, val->fid); + MARSHAL_U8LE(ctx, val->type); + MARSHAL_U64LE(ctx, val->start); + MARSHAL_U64LE(ctx, val->length); + MARSHAL_U32LE(ctx, val->proc_id); + MARSHAL_U16LE(ctx, val->client_id.len); + MARSHAL_BYTES_ZEROCOPY(ctx, val->client_id.utf8, val->client_id.len); + return false; +} + +static bool marshal_Rgetlock(struct lib9p_ctx *ctx, struct lib9p_msg_Rgetlock *val, struct _marshal_ret *ret) { + uint32_t needed_size = 30 + val->client_id.len; + if (needed_size > ctx->max_msg_size) { + lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", + "Rgetlock", + ctx->version ? "negotiated" : "server", + ctx->max_msg_size); + return true; + } + uint32_t offsetof_end = needed_size; + uint32_t offsetof_size = 0; + MARSHAL_U32LE(ctx, offsetof_end - offsetof_size); + MARSHAL_U8LE(ctx, 55); + MARSHAL_U16LE(ctx, val->tag); + MARSHAL_U8LE(ctx, val->type); + MARSHAL_U64LE(ctx, val->start); + MARSHAL_U64LE(ctx, val->length); + MARSHAL_U32LE(ctx, val->proc_id); + MARSHAL_U16LE(ctx, val->client_id.len); + MARSHAL_BYTES_ZEROCOPY(ctx, val->client_id.utf8, val->client_id.len); + return false; +} + +static bool marshal_Tlink(struct lib9p_ctx *ctx, struct lib9p_msg_Tlink *val, struct _marshal_ret *ret) { + uint32_t needed_size = 17 + val->name.len; + if (needed_size > ctx->max_msg_size) { + lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", + "Tlink", + ctx->version ? "negotiated" : "client", + ctx->max_msg_size); + return true; + } + uint32_t offsetof_end = needed_size; + uint32_t offsetof_size = 0; + MARSHAL_U32LE(ctx, offsetof_end - offsetof_size); + MARSHAL_U8LE(ctx, 70); + MARSHAL_U16LE(ctx, val->tag); + MARSHAL_U32LE(ctx, val->dfid); + MARSHAL_U32LE(ctx, val->fid); + MARSHAL_U16LE(ctx, val->name.len); + MARSHAL_BYTES_ZEROCOPY(ctx, val->name.utf8, val->name.len); + return false; +} + +static bool marshal_Rlink(struct lib9p_ctx *ctx, struct lib9p_msg_Rlink *val, struct _marshal_ret *ret) { + uint32_t needed_size = 7; + if (needed_size > ctx->max_msg_size) { + lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", + "Rlink", + ctx->version ? "negotiated" : "server", + ctx->max_msg_size); + return true; + } + uint32_t offsetof_end = needed_size; + uint32_t offsetof_size = 0; + MARSHAL_U32LE(ctx, offsetof_end - offsetof_size); + MARSHAL_U8LE(ctx, 71); + MARSHAL_U16LE(ctx, val->tag); + return false; +} + +static bool marshal_Tmkdir(struct lib9p_ctx *ctx, struct lib9p_msg_Tmkdir *val, struct _marshal_ret *ret) { + uint32_t needed_size = 21 + val->name.len; + if (needed_size > ctx->max_msg_size) { + lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", + "Tmkdir", + ctx->version ? "negotiated" : "client", + ctx->max_msg_size); + return true; + } + uint32_t offsetof_end = needed_size; + uint32_t offsetof_size = 0; + MARSHAL_U32LE(ctx, offsetof_end - offsetof_size); + MARSHAL_U8LE(ctx, 72); + MARSHAL_U16LE(ctx, val->tag); + MARSHAL_U32LE(ctx, val->dfid); + MARSHAL_U16LE(ctx, val->name.len); + MARSHAL_BYTES_ZEROCOPY(ctx, val->name.utf8, val->name.len); + MARSHAL_U32LE(ctx, val->mode & mode_masks[ctx->version]); + MARSHAL_U32LE(ctx, val->gid); + return false; +} + +static bool marshal_Rmkdir(struct lib9p_ctx *ctx, struct lib9p_msg_Rmkdir *val, struct _marshal_ret *ret) { + uint32_t needed_size = 20; + if (needed_size > ctx->max_msg_size) { + lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", + "Rmkdir", + ctx->version ? "negotiated" : "server", + ctx->max_msg_size); + return true; + } + uint32_t offsetof_end = needed_size; + uint32_t offsetof_size = 0; + MARSHAL_U32LE(ctx, offsetof_end - offsetof_size); + MARSHAL_U8LE(ctx, 73); + MARSHAL_U16LE(ctx, val->tag); + MARSHAL_U8LE(ctx, val->qid.type & qt_masks[ctx->version]); + MARSHAL_U32LE(ctx, val->qid.vers); + MARSHAL_U64LE(ctx, val->qid.path); + return false; +} + +static bool marshal_Trenameat(struct lib9p_ctx *ctx, struct lib9p_msg_Trenameat *val, struct _marshal_ret *ret) { + uint32_t needed_size = 19 + val->oldname.len + val->newname.len; + if (needed_size > ctx->max_msg_size) { + lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", + "Trenameat", + ctx->version ? "negotiated" : "client", + ctx->max_msg_size); + return true; + } + uint32_t offsetof_end = needed_size; + uint32_t offsetof_size = 0; + MARSHAL_U32LE(ctx, offsetof_end - offsetof_size); + MARSHAL_U8LE(ctx, 74); + MARSHAL_U16LE(ctx, val->tag); + MARSHAL_U32LE(ctx, val->olddirfid); + MARSHAL_U16LE(ctx, val->oldname.len); + MARSHAL_BYTES_ZEROCOPY(ctx, val->oldname.utf8, val->oldname.len); + MARSHAL_U32LE(ctx, val->newdirfid); + MARSHAL_U16LE(ctx, val->newname.len); + MARSHAL_BYTES_ZEROCOPY(ctx, val->newname.utf8, val->newname.len); + return false; +} + +static bool marshal_Rrenameat(struct lib9p_ctx *ctx, struct lib9p_msg_Rrenameat *val, struct _marshal_ret *ret) { + uint32_t needed_size = 7; + if (needed_size > ctx->max_msg_size) { + lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", + "Rrenameat", + ctx->version ? "negotiated" : "server", + ctx->max_msg_size); + return true; + } + uint32_t offsetof_end = needed_size; + uint32_t offsetof_size = 0; + MARSHAL_U32LE(ctx, offsetof_end - offsetof_size); + MARSHAL_U8LE(ctx, 75); + MARSHAL_U16LE(ctx, val->tag); + return false; +} + +static bool marshal_Tunlinkat(struct lib9p_ctx *ctx, struct lib9p_msg_Tunlinkat *val, struct _marshal_ret *ret) { + uint32_t needed_size = 17 + val->name.len; + if (needed_size > ctx->max_msg_size) { + lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", + "Tunlinkat", + ctx->version ? "negotiated" : "client", + ctx->max_msg_size); + return true; + } + uint32_t offsetof_end = needed_size; + uint32_t offsetof_size = 0; + MARSHAL_U32LE(ctx, offsetof_end - offsetof_size); + MARSHAL_U8LE(ctx, 76); + MARSHAL_U16LE(ctx, val->tag); + MARSHAL_U32LE(ctx, val->dirfd); + MARSHAL_U16LE(ctx, val->name.len); + MARSHAL_BYTES_ZEROCOPY(ctx, val->name.utf8, val->name.len); + MARSHAL_U32LE(ctx, val->flags); + return false; +} + +static bool marshal_Runlinkat(struct lib9p_ctx *ctx, struct lib9p_msg_Runlinkat *val, struct _marshal_ret *ret) { + uint32_t needed_size = 7; + if (needed_size > ctx->max_msg_size) { + lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", + "Runlinkat", + ctx->version ? "negotiated" : "server", + ctx->max_msg_size); + return true; + } + uint32_t offsetof_end = needed_size; + uint32_t offsetof_size = 0; + MARSHAL_U32LE(ctx, offsetof_end - offsetof_size); + MARSHAL_U8LE(ctx, 77); + MARSHAL_U16LE(ctx, val->tag); + return false; +} + +#endif /* CONFIG_9P_ENABLE_9P2000_L */ +#if CONFIG_9P_ENABLE_9P2000_e +static bool marshal_Tsession(struct lib9p_ctx *ctx, struct lib9p_msg_Tsession *val, struct _marshal_ret *ret) { + uint32_t needed_size = 15; + if (needed_size > ctx->max_msg_size) { + lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", + "Tsession", + ctx->version ? "negotiated" : "client", + ctx->max_msg_size); + return true; + } + uint32_t offsetof_end = needed_size; + uint32_t offsetof_size = 0; + MARSHAL_U32LE(ctx, offsetof_end - offsetof_size); + MARSHAL_U8LE(ctx, 150); + MARSHAL_U16LE(ctx, val->tag); + MARSHAL_U64LE(ctx, val->key); + return false; +} + +static bool marshal_Rsession(struct lib9p_ctx *ctx, struct lib9p_msg_Rsession *val, struct _marshal_ret *ret) { + uint32_t needed_size = 7; + if (needed_size > ctx->max_msg_size) { + lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", + "Rsession", + ctx->version ? "negotiated" : "server", + ctx->max_msg_size); + return true; + } + uint32_t offsetof_end = needed_size; + uint32_t offsetof_size = 0; + MARSHAL_U32LE(ctx, offsetof_end - offsetof_size); + MARSHAL_U8LE(ctx, 151); + MARSHAL_U16LE(ctx, val->tag); + return false; } - -static ALWAYS_INLINE bool marshal_s(struct _marshal_ctx *ctx, struct lib9p_s *val) { - return false - || marshal_2(ctx, &val->len) - || ({ bool err = false; - for (typeof(val->len) i = 0; i < val->len && !err; i++) - err = marshal_1(ctx, (uint8_t *)&val->utf8[i]); - err; }) - ; -} - -static ALWAYS_INLINE bool marshal_dm(struct _marshal_ctx *ctx, lib9p_dm_t *val) { - lib9p_dm_t masked_val = *val & dm_masks[ctx->ctx->version]; - return marshal_4(ctx, (uint32_t *)&masked_val); -} - -static ALWAYS_INLINE bool marshal_qt(struct _marshal_ctx *ctx, lib9p_qt_t *val) { - lib9p_qt_t masked_val = *val & qt_masks[ctx->ctx->version]; - return marshal_1(ctx, (uint8_t *)&masked_val); -} - -static ALWAYS_INLINE bool marshal_qid(struct _marshal_ctx *ctx, struct lib9p_qid *val) { - return false - || marshal_qt(ctx, &val->type) - || marshal_4(ctx, &val->vers) - || marshal_8(ctx, &val->path) - ; -} - -static ALWAYS_INLINE bool marshal_stat(struct _marshal_ctx *ctx, struct lib9p_stat *val) { - uint32_t _stat_size_offset; - uint32_t _kern_type_offset; - return false - || ({ _stat_size_offset = ctx->net_offset; ({ ctx->net_offset += 2; false; }); }) - || ({ _kern_type_offset = ctx->net_offset; marshal_2(ctx, &val->kern_type); }) - || marshal_4(ctx, &val->kern_dev) - || marshal_qid(ctx, &val->file_qid) - || marshal_dm(ctx, &val->file_mode) - || marshal_4(ctx, &val->file_atime) - || marshal_4(ctx, &val->file_mtime) - || marshal_8(ctx, &val->file_size) - || marshal_s(ctx, &val->file_name) - || marshal_s(ctx, &val->file_owner_uid) - || marshal_s(ctx, &val->file_owner_gid) - || marshal_s(ctx, &val->file_last_modified_uid) -#if defined(CONFIG_9P_ENABLE_9P2000_u) - || ( (ctx->ctx->version==LIB9P_VER_9P2000_u) && marshal_s(ctx, &val->file_extension) ) - || ( (ctx->ctx->version==LIB9P_VER_9P2000_u) && marshal_4(ctx, &val->file_owner_n_uid) ) - || ( (ctx->ctx->version==LIB9P_VER_9P2000_u) && marshal_4(ctx, &val->file_owner_n_gid) ) - || ( (ctx->ctx->version==LIB9P_VER_9P2000_u) && marshal_4(ctx, &val->file_last_modified_n_uid) ) -#endif /* defined(CONFIG_9P_ENABLE_9P2000_u) */ - || ({ encode_u16le(ctx->net_offset - _kern_type_offset, &ctx->net_bytes[_stat_size_offset]); false; }) - ; -} - -static ALWAYS_INLINE bool marshal_o(struct _marshal_ctx *ctx, lib9p_o_t *val) { - lib9p_o_t masked_val = *val & o_masks[ctx->ctx->version]; - return marshal_1(ctx, (uint8_t *)&masked_val); -} - -static FLATTEN bool marshal_Tversion(struct _marshal_ctx *ctx, struct lib9p_msg_Tversion *val) { - uint32_t _size_offset; - uint32_t _typ_offset; - return false - || ({ _size_offset = ctx->net_offset; ({ ctx->net_offset += 4; false; }); }) - || ({ _typ_offset = ctx->net_offset; ({ ctx->net_offset += 1; false; }); }) - || marshal_tag(ctx, &val->tag) - || marshal_4(ctx, &val->max_msg_size) - || marshal_s(ctx, &val->version) - || ({ encode_u32le(ctx->net_offset - _size_offset, &ctx->net_bytes[_size_offset]); false; }) - || ({ encode_u8le(100, &ctx->net_bytes[_typ_offset]); false; }) - ; -} - -static FLATTEN bool marshal_Rversion(struct _marshal_ctx *ctx, struct lib9p_msg_Rversion *val) { - uint32_t _size_offset; - uint32_t _typ_offset; - return false - || ({ _size_offset = ctx->net_offset; ({ ctx->net_offset += 4; false; }); }) - || ({ _typ_offset = ctx->net_offset; ({ ctx->net_offset += 1; false; }); }) - || marshal_tag(ctx, &val->tag) - || marshal_4(ctx, &val->max_msg_size) - || marshal_s(ctx, &val->version) - || ({ encode_u32le(ctx->net_offset - _size_offset, &ctx->net_bytes[_size_offset]); false; }) - || ({ encode_u8le(101, &ctx->net_bytes[_typ_offset]); false; }) - ; -} - -static FLATTEN bool marshal_Tauth(struct _marshal_ctx *ctx, struct lib9p_msg_Tauth *val) { - uint32_t _size_offset; - uint32_t _typ_offset; - return false - || ({ _size_offset = ctx->net_offset; ({ ctx->net_offset += 4; false; }); }) - || ({ _typ_offset = ctx->net_offset; ({ ctx->net_offset += 1; false; }); }) - || marshal_tag(ctx, &val->tag) - || marshal_fid(ctx, &val->afid) - || marshal_s(ctx, &val->uname) - || marshal_s(ctx, &val->aname) -#if defined(CONFIG_9P_ENABLE_9P2000_u) - || ( (ctx->ctx->version==LIB9P_VER_9P2000_u) && marshal_4(ctx, &val->n_uname) ) -#endif /* defined(CONFIG_9P_ENABLE_9P2000_u) */ - || ({ encode_u32le(ctx->net_offset - _size_offset, &ctx->net_bytes[_size_offset]); false; }) - || ({ encode_u8le(102, &ctx->net_bytes[_typ_offset]); false; }) - ; -} - -static FLATTEN bool marshal_Rauth(struct _marshal_ctx *ctx, struct lib9p_msg_Rauth *val) { - uint32_t _size_offset; - uint32_t _typ_offset; - return false - || ({ _size_offset = ctx->net_offset; ({ ctx->net_offset += 4; false; }); }) - || ({ _typ_offset = ctx->net_offset; ({ ctx->net_offset += 1; false; }); }) - || marshal_tag(ctx, &val->tag) - || marshal_qid(ctx, &val->aqid) - || ({ encode_u32le(ctx->net_offset - _size_offset, &ctx->net_bytes[_size_offset]); false; }) - || ({ encode_u8le(103, &ctx->net_bytes[_typ_offset]); false; }) - ; -} - -static FLATTEN bool marshal_Tattach(struct _marshal_ctx *ctx, struct lib9p_msg_Tattach *val) { - uint32_t _size_offset; - uint32_t _typ_offset; - return false - || ({ _size_offset = ctx->net_offset; ({ ctx->net_offset += 4; false; }); }) - || ({ _typ_offset = ctx->net_offset; ({ ctx->net_offset += 1; false; }); }) - || marshal_tag(ctx, &val->tag) - || marshal_fid(ctx, &val->fid) - || marshal_fid(ctx, &val->afid) - || marshal_s(ctx, &val->uname) - || marshal_s(ctx, &val->aname) -#if defined(CONFIG_9P_ENABLE_9P2000_u) - || ( (ctx->ctx->version==LIB9P_VER_9P2000_u) && marshal_4(ctx, &val->n_uname) ) -#endif /* defined(CONFIG_9P_ENABLE_9P2000_u) */ - || ({ encode_u32le(ctx->net_offset - _size_offset, &ctx->net_bytes[_size_offset]); false; }) - || ({ encode_u8le(104, &ctx->net_bytes[_typ_offset]); false; }) - ; -} - -static FLATTEN bool marshal_Rattach(struct _marshal_ctx *ctx, struct lib9p_msg_Rattach *val) { - uint32_t _size_offset; - uint32_t _typ_offset; - return false - || ({ _size_offset = ctx->net_offset; ({ ctx->net_offset += 4; false; }); }) - || ({ _typ_offset = ctx->net_offset; ({ ctx->net_offset += 1; false; }); }) - || marshal_tag(ctx, &val->tag) - || marshal_qid(ctx, &val->qid) - || ({ encode_u32le(ctx->net_offset - _size_offset, &ctx->net_bytes[_size_offset]); false; }) - || ({ encode_u8le(105, &ctx->net_bytes[_typ_offset]); false; }) - ; -} - -static FLATTEN bool marshal_Rerror(struct _marshal_ctx *ctx, struct lib9p_msg_Rerror *val) { - uint32_t _size_offset; - uint32_t _typ_offset; - return false - || ({ _size_offset = ctx->net_offset; ({ ctx->net_offset += 4; false; }); }) - || ({ _typ_offset = ctx->net_offset; ({ ctx->net_offset += 1; false; }); }) - || marshal_tag(ctx, &val->tag) - || marshal_s(ctx, &val->ename) -#if defined(CONFIG_9P_ENABLE_9P2000_u) - || ( (ctx->ctx->version==LIB9P_VER_9P2000_u) && marshal_4(ctx, &val->errno) ) -#endif /* defined(CONFIG_9P_ENABLE_9P2000_u) */ - || ({ encode_u32le(ctx->net_offset - _size_offset, &ctx->net_bytes[_size_offset]); false; }) - || ({ encode_u8le(107, &ctx->net_bytes[_typ_offset]); false; }) - ; -} - -static FLATTEN bool marshal_Tflush(struct _marshal_ctx *ctx, struct lib9p_msg_Tflush *val) { - uint32_t _size_offset; - uint32_t _typ_offset; - return false - || ({ _size_offset = ctx->net_offset; ({ ctx->net_offset += 4; false; }); }) - || ({ _typ_offset = ctx->net_offset; ({ ctx->net_offset += 1; false; }); }) - || marshal_tag(ctx, &val->tag) - || marshal_2(ctx, &val->oldtag) - || ({ encode_u32le(ctx->net_offset - _size_offset, &ctx->net_bytes[_size_offset]); false; }) - || ({ encode_u8le(108, &ctx->net_bytes[_typ_offset]); false; }) - ; -} - -static FLATTEN bool marshal_Rflush(struct _marshal_ctx *ctx, struct lib9p_msg_Rflush *val) { - uint32_t _size_offset; - uint32_t _typ_offset; - return false - || ({ _size_offset = ctx->net_offset; ({ ctx->net_offset += 4; false; }); }) - || ({ _typ_offset = ctx->net_offset; ({ ctx->net_offset += 1; false; }); }) - || marshal_tag(ctx, &val->tag) - || ({ encode_u32le(ctx->net_offset - _size_offset, &ctx->net_bytes[_size_offset]); false; }) - || ({ encode_u8le(109, &ctx->net_bytes[_typ_offset]); false; }) - ; -} - -static FLATTEN bool marshal_Twalk(struct _marshal_ctx *ctx, struct lib9p_msg_Twalk *val) { - uint32_t _size_offset; - uint32_t _typ_offset; - return false - || ({ _size_offset = ctx->net_offset; ({ ctx->net_offset += 4; false; }); }) - || ({ _typ_offset = ctx->net_offset; ({ ctx->net_offset += 1; false; }); }) - || marshal_tag(ctx, &val->tag) - || marshal_fid(ctx, &val->fid) - || marshal_fid(ctx, &val->newfid) - || marshal_2(ctx, &val->nwname) - || ({ bool err = false; - for (typeof(val->nwname) i = 0; i < val->nwname && !err; i++) - err = marshal_s(ctx, &val->wname[i]); - err; }) - || ({ encode_u32le(ctx->net_offset - _size_offset, &ctx->net_bytes[_size_offset]); false; }) - || ({ encode_u8le(110, &ctx->net_bytes[_typ_offset]); false; }) - ; -} - -static FLATTEN bool marshal_Rwalk(struct _marshal_ctx *ctx, struct lib9p_msg_Rwalk *val) { - uint32_t _size_offset; - uint32_t _typ_offset; - return false - || ({ _size_offset = ctx->net_offset; ({ ctx->net_offset += 4; false; }); }) - || ({ _typ_offset = ctx->net_offset; ({ ctx->net_offset += 1; false; }); }) - || marshal_tag(ctx, &val->tag) - || marshal_2(ctx, &val->nwqid) - || ({ bool err = false; - for (typeof(val->nwqid) i = 0; i < val->nwqid && !err; i++) - err = marshal_qid(ctx, &val->wqid[i]); - err; }) - || ({ encode_u32le(ctx->net_offset - _size_offset, &ctx->net_bytes[_size_offset]); false; }) - || ({ encode_u8le(111, &ctx->net_bytes[_typ_offset]); false; }) - ; -} - -static FLATTEN bool marshal_Topen(struct _marshal_ctx *ctx, struct lib9p_msg_Topen *val) { - uint32_t _size_offset; - uint32_t _typ_offset; - return false - || ({ _size_offset = ctx->net_offset; ({ ctx->net_offset += 4; false; }); }) - || ({ _typ_offset = ctx->net_offset; ({ ctx->net_offset += 1; false; }); }) - || marshal_tag(ctx, &val->tag) - || marshal_fid(ctx, &val->fid) - || marshal_o(ctx, &val->mode) - || ({ encode_u32le(ctx->net_offset - _size_offset, &ctx->net_bytes[_size_offset]); false; }) - || ({ encode_u8le(112, &ctx->net_bytes[_typ_offset]); false; }) - ; -} - -static FLATTEN bool marshal_Ropen(struct _marshal_ctx *ctx, struct lib9p_msg_Ropen *val) { - uint32_t _size_offset; - uint32_t _typ_offset; - return false - || ({ _size_offset = ctx->net_offset; ({ ctx->net_offset += 4; false; }); }) - || ({ _typ_offset = ctx->net_offset; ({ ctx->net_offset += 1; false; }); }) - || marshal_tag(ctx, &val->tag) - || marshal_qid(ctx, &val->qid) - || marshal_4(ctx, &val->iounit) - || ({ encode_u32le(ctx->net_offset - _size_offset, &ctx->net_bytes[_size_offset]); false; }) - || ({ encode_u8le(113, &ctx->net_bytes[_typ_offset]); false; }) - ; -} - -static FLATTEN bool marshal_Tcreate(struct _marshal_ctx *ctx, struct lib9p_msg_Tcreate *val) { - uint32_t _size_offset; - uint32_t _typ_offset; - return false - || ({ _size_offset = ctx->net_offset; ({ ctx->net_offset += 4; false; }); }) - || ({ _typ_offset = ctx->net_offset; ({ ctx->net_offset += 1; false; }); }) - || marshal_tag(ctx, &val->tag) - || marshal_fid(ctx, &val->fid) - || marshal_s(ctx, &val->name) - || marshal_dm(ctx, &val->perm) - || marshal_o(ctx, &val->mode) - || ({ encode_u32le(ctx->net_offset - _size_offset, &ctx->net_bytes[_size_offset]); false; }) - || ({ encode_u8le(114, &ctx->net_bytes[_typ_offset]); false; }) - ; -} - -static FLATTEN bool marshal_Rcreate(struct _marshal_ctx *ctx, struct lib9p_msg_Rcreate *val) { - uint32_t _size_offset; - uint32_t _typ_offset; - return false - || ({ _size_offset = ctx->net_offset; ({ ctx->net_offset += 4; false; }); }) - || ({ _typ_offset = ctx->net_offset; ({ ctx->net_offset += 1; false; }); }) - || marshal_tag(ctx, &val->tag) - || marshal_qid(ctx, &val->qid) - || marshal_4(ctx, &val->iounit) - || ({ encode_u32le(ctx->net_offset - _size_offset, &ctx->net_bytes[_size_offset]); false; }) - || ({ encode_u8le(115, &ctx->net_bytes[_typ_offset]); false; }) - ; -} - -static FLATTEN bool marshal_Tread(struct _marshal_ctx *ctx, struct lib9p_msg_Tread *val) { - uint32_t _size_offset; - uint32_t _typ_offset; - return false - || ({ _size_offset = ctx->net_offset; ({ ctx->net_offset += 4; false; }); }) - || ({ _typ_offset = ctx->net_offset; ({ ctx->net_offset += 1; false; }); }) - || marshal_tag(ctx, &val->tag) - || marshal_fid(ctx, &val->fid) - || marshal_8(ctx, &val->offset) - || marshal_4(ctx, &val->count) - || ({ encode_u32le(ctx->net_offset - _size_offset, &ctx->net_bytes[_size_offset]); false; }) - || ({ encode_u8le(116, &ctx->net_bytes[_typ_offset]); false; }) - ; -} - -static FLATTEN bool marshal_Rread(struct _marshal_ctx *ctx, struct lib9p_msg_Rread *val) { - uint32_t _size_offset; - uint32_t _typ_offset; - return false - || ({ _size_offset = ctx->net_offset; ({ ctx->net_offset += 4; false; }); }) - || ({ _typ_offset = ctx->net_offset; ({ ctx->net_offset += 1; false; }); }) - || marshal_tag(ctx, &val->tag) - || marshal_d(ctx, &val->data) - || ({ encode_u32le(ctx->net_offset - _size_offset, &ctx->net_bytes[_size_offset]); false; }) - || ({ encode_u8le(117, &ctx->net_bytes[_typ_offset]); false; }) - ; -} - -static FLATTEN bool marshal_Twrite(struct _marshal_ctx *ctx, struct lib9p_msg_Twrite *val) { - uint32_t _size_offset; - uint32_t _typ_offset; - return false - || ({ _size_offset = ctx->net_offset; ({ ctx->net_offset += 4; false; }); }) - || ({ _typ_offset = ctx->net_offset; ({ ctx->net_offset += 1; false; }); }) - || marshal_tag(ctx, &val->tag) - || marshal_fid(ctx, &val->fid) - || marshal_8(ctx, &val->offset) - || marshal_d(ctx, &val->data) - || ({ encode_u32le(ctx->net_offset - _size_offset, &ctx->net_bytes[_size_offset]); false; }) - || ({ encode_u8le(118, &ctx->net_bytes[_typ_offset]); false; }) - ; -} - -static FLATTEN bool marshal_Rwrite(struct _marshal_ctx *ctx, struct lib9p_msg_Rwrite *val) { - uint32_t _size_offset; - uint32_t _typ_offset; - return false - || ({ _size_offset = ctx->net_offset; ({ ctx->net_offset += 4; false; }); }) - || ({ _typ_offset = ctx->net_offset; ({ ctx->net_offset += 1; false; }); }) - || marshal_tag(ctx, &val->tag) - || marshal_4(ctx, &val->count) - || ({ encode_u32le(ctx->net_offset - _size_offset, &ctx->net_bytes[_size_offset]); false; }) - || ({ encode_u8le(119, &ctx->net_bytes[_typ_offset]); false; }) - ; -} - -static FLATTEN bool marshal_Tclunk(struct _marshal_ctx *ctx, struct lib9p_msg_Tclunk *val) { - uint32_t _size_offset; - uint32_t _typ_offset; - return false - || ({ _size_offset = ctx->net_offset; ({ ctx->net_offset += 4; false; }); }) - || ({ _typ_offset = ctx->net_offset; ({ ctx->net_offset += 1; false; }); }) - || marshal_tag(ctx, &val->tag) - || marshal_fid(ctx, &val->fid) - || ({ encode_u32le(ctx->net_offset - _size_offset, &ctx->net_bytes[_size_offset]); false; }) - || ({ encode_u8le(120, &ctx->net_bytes[_typ_offset]); false; }) - ; -} - -static FLATTEN bool marshal_Rclunk(struct _marshal_ctx *ctx, struct lib9p_msg_Rclunk *val) { - uint32_t _size_offset; - uint32_t _typ_offset; - return false - || ({ _size_offset = ctx->net_offset; ({ ctx->net_offset += 4; false; }); }) - || ({ _typ_offset = ctx->net_offset; ({ ctx->net_offset += 1; false; }); }) - || marshal_tag(ctx, &val->tag) - || ({ encode_u32le(ctx->net_offset - _size_offset, &ctx->net_bytes[_size_offset]); false; }) - || ({ encode_u8le(121, &ctx->net_bytes[_typ_offset]); false; }) - ; -} - -static FLATTEN bool marshal_Tremove(struct _marshal_ctx *ctx, struct lib9p_msg_Tremove *val) { - uint32_t _size_offset; - uint32_t _typ_offset; - return false - || ({ _size_offset = ctx->net_offset; ({ ctx->net_offset += 4; false; }); }) - || ({ _typ_offset = ctx->net_offset; ({ ctx->net_offset += 1; false; }); }) - || marshal_tag(ctx, &val->tag) - || marshal_fid(ctx, &val->fid) - || ({ encode_u32le(ctx->net_offset - _size_offset, &ctx->net_bytes[_size_offset]); false; }) - || ({ encode_u8le(122, &ctx->net_bytes[_typ_offset]); false; }) - ; -} - -static FLATTEN bool marshal_Rremove(struct _marshal_ctx *ctx, struct lib9p_msg_Rremove *val) { - uint32_t _size_offset; - uint32_t _typ_offset; - return false - || ({ _size_offset = ctx->net_offset; ({ ctx->net_offset += 4; false; }); }) - || ({ _typ_offset = ctx->net_offset; ({ ctx->net_offset += 1; false; }); }) - || marshal_tag(ctx, &val->tag) - || ({ encode_u32le(ctx->net_offset - _size_offset, &ctx->net_bytes[_size_offset]); false; }) - || ({ encode_u8le(123, &ctx->net_bytes[_typ_offset]); false; }) - ; -} - -static FLATTEN bool marshal_Tstat(struct _marshal_ctx *ctx, struct lib9p_msg_Tstat *val) { - uint32_t _size_offset; - uint32_t _typ_offset; - return false - || ({ _size_offset = ctx->net_offset; ({ ctx->net_offset += 4; false; }); }) - || ({ _typ_offset = ctx->net_offset; ({ ctx->net_offset += 1; false; }); }) - || marshal_tag(ctx, &val->tag) - || marshal_fid(ctx, &val->fid) - || ({ encode_u32le(ctx->net_offset - _size_offset, &ctx->net_bytes[_size_offset]); false; }) - || ({ encode_u8le(124, &ctx->net_bytes[_typ_offset]); false; }) - ; -} - -static FLATTEN bool marshal_Rstat(struct _marshal_ctx *ctx, struct lib9p_msg_Rstat *val) { - uint32_t _size_offset; - uint32_t _typ_offset; - uint32_t _nstat_offset; - uint32_t _stat_offset; - return false - || ({ _size_offset = ctx->net_offset; ({ ctx->net_offset += 4; false; }); }) - || ({ _typ_offset = ctx->net_offset; ({ ctx->net_offset += 1; false; }); }) - || marshal_tag(ctx, &val->tag) - || ({ _nstat_offset = ctx->net_offset; ({ ctx->net_offset += 2; false; }); }) - || ({ _stat_offset = ctx->net_offset; marshal_stat(ctx, &val->stat); }) - || ({ encode_u32le(ctx->net_offset - _size_offset, &ctx->net_bytes[_size_offset]); false; }) - || ({ encode_u8le(125, &ctx->net_bytes[_typ_offset]); false; }) - || ({ encode_u16le(ctx->net_offset - _stat_offset, &ctx->net_bytes[_nstat_offset]); false; }) - ; -} - -static FLATTEN bool marshal_Twstat(struct _marshal_ctx *ctx, struct lib9p_msg_Twstat *val) { - uint32_t _size_offset; - uint32_t _typ_offset; - uint32_t _nstat_offset; - uint32_t _stat_offset; - return false - || ({ _size_offset = ctx->net_offset; ({ ctx->net_offset += 4; false; }); }) - || ({ _typ_offset = ctx->net_offset; ({ ctx->net_offset += 1; false; }); }) - || marshal_tag(ctx, &val->tag) - || marshal_fid(ctx, &val->fid) - || ({ _nstat_offset = ctx->net_offset; ({ ctx->net_offset += 2; false; }); }) - || ({ _stat_offset = ctx->net_offset; marshal_stat(ctx, &val->stat); }) - || ({ encode_u32le(ctx->net_offset - _size_offset, &ctx->net_bytes[_size_offset]); false; }) - || ({ encode_u8le(126, &ctx->net_bytes[_typ_offset]); false; }) - || ({ encode_u16le(ctx->net_offset - _stat_offset, &ctx->net_bytes[_nstat_offset]); false; }) - ; -} - -static FLATTEN bool marshal_Rwstat(struct _marshal_ctx *ctx, struct lib9p_msg_Rwstat *val) { - uint32_t _size_offset; - uint32_t _typ_offset; - return false - || ({ _size_offset = ctx->net_offset; ({ ctx->net_offset += 4; false; }); }) - || ({ _typ_offset = ctx->net_offset; ({ ctx->net_offset += 1; false; }); }) - || marshal_tag(ctx, &val->tag) - || ({ encode_u32le(ctx->net_offset - _size_offset, &ctx->net_bytes[_size_offset]); false; }) - || ({ encode_u8le(127, &ctx->net_bytes[_typ_offset]); false; }) - ; -} - -#endif /* defined(CONFIG_9P_ENABLE_9P2000) || defined(CONFIG_9P_ENABLE_9P2000_e) || defined(CONFIG_9P_ENABLE_9P2000_u) */ -#if defined(CONFIG_9P_ENABLE_9P2000_e) -static FLATTEN bool marshal_Tsession(struct _marshal_ctx *ctx, struct lib9p_msg_Tsession *val) { - uint32_t _size_offset; - uint32_t _typ_offset; - return false - || ({ _size_offset = ctx->net_offset; ({ ctx->net_offset += 4; false; }); }) - || ({ _typ_offset = ctx->net_offset; ({ ctx->net_offset += 1; false; }); }) - || marshal_tag(ctx, &val->tag) - || marshal_8(ctx, &val->key) - || ({ encode_u32le(ctx->net_offset - _size_offset, &ctx->net_bytes[_size_offset]); false; }) - || ({ encode_u8le(150, &ctx->net_bytes[_typ_offset]); false; }) - ; -} - -static FLATTEN bool marshal_Rsession(struct _marshal_ctx *ctx, struct lib9p_msg_Rsession *val) { - uint32_t _size_offset; - uint32_t _typ_offset; - return false - || ({ _size_offset = ctx->net_offset; ({ ctx->net_offset += 4; false; }); }) - || ({ _typ_offset = ctx->net_offset; ({ ctx->net_offset += 1; false; }); }) - || marshal_tag(ctx, &val->tag) - || ({ encode_u32le(ctx->net_offset - _size_offset, &ctx->net_bytes[_size_offset]); false; }) - || ({ encode_u8le(151, &ctx->net_bytes[_typ_offset]); false; }) - ; -} - -static FLATTEN bool marshal_Tsread(struct _marshal_ctx *ctx, struct lib9p_msg_Tsread *val) { - uint32_t _size_offset; - uint32_t _typ_offset; - return false - || ({ _size_offset = ctx->net_offset; ({ ctx->net_offset += 4; false; }); }) - || ({ _typ_offset = ctx->net_offset; ({ ctx->net_offset += 1; false; }); }) - || marshal_tag(ctx, &val->tag) - || marshal_4(ctx, &val->fid) - || marshal_2(ctx, &val->nwname) - || ({ bool err = false; - for (typeof(val->nwname) i = 0; i < val->nwname && !err; i++) - err = marshal_s(ctx, &val->wname[i]); - err; }) - || ({ encode_u32le(ctx->net_offset - _size_offset, &ctx->net_bytes[_size_offset]); false; }) - || ({ encode_u8le(152, &ctx->net_bytes[_typ_offset]); false; }) - ; -} - -static FLATTEN bool marshal_Rsread(struct _marshal_ctx *ctx, struct lib9p_msg_Rsread *val) { - uint32_t _size_offset; - uint32_t _typ_offset; - return false - || ({ _size_offset = ctx->net_offset; ({ ctx->net_offset += 4; false; }); }) - || ({ _typ_offset = ctx->net_offset; ({ ctx->net_offset += 1; false; }); }) - || marshal_tag(ctx, &val->tag) - || marshal_d(ctx, &val->data) - || ({ encode_u32le(ctx->net_offset - _size_offset, &ctx->net_bytes[_size_offset]); false; }) - || ({ encode_u8le(153, &ctx->net_bytes[_typ_offset]); false; }) - ; -} - -static FLATTEN bool marshal_Tswrite(struct _marshal_ctx *ctx, struct lib9p_msg_Tswrite *val) { - uint32_t _size_offset; - uint32_t _typ_offset; - return false - || ({ _size_offset = ctx->net_offset; ({ ctx->net_offset += 4; false; }); }) - || ({ _typ_offset = ctx->net_offset; ({ ctx->net_offset += 1; false; }); }) - || marshal_tag(ctx, &val->tag) - || marshal_4(ctx, &val->fid) - || marshal_2(ctx, &val->nwname) - || ({ bool err = false; - for (typeof(val->nwname) i = 0; i < val->nwname && !err; i++) - err = marshal_s(ctx, &val->wname[i]); - err; }) - || marshal_d(ctx, &val->data) - || ({ encode_u32le(ctx->net_offset - _size_offset, &ctx->net_bytes[_size_offset]); false; }) - || ({ encode_u8le(154, &ctx->net_bytes[_typ_offset]); false; }) - ; -} - -static FLATTEN bool marshal_Rswrite(struct _marshal_ctx *ctx, struct lib9p_msg_Rswrite *val) { - uint32_t _size_offset; - uint32_t _typ_offset; - return false - || ({ _size_offset = ctx->net_offset; ({ ctx->net_offset += 4; false; }); }) - || ({ _typ_offset = ctx->net_offset; ({ ctx->net_offset += 1; false; }); }) - || marshal_tag(ctx, &val->tag) - || marshal_4(ctx, &val->count) - || ({ encode_u32le(ctx->net_offset - _size_offset, &ctx->net_bytes[_size_offset]); false; }) - || ({ encode_u8le(155, &ctx->net_bytes[_typ_offset]); false; }) - ; -} -#endif /* defined(CONFIG_9P_ENABLE_9P2000_e) */ - -/* tables / exports ***********************************************************/ - -#define _MSG(typ) [LIB9P_TYP_##typ] = { \ - .name = #typ, \ - .basesize = sizeof(struct lib9p_msg_##typ), \ - .validate = validate_##typ, \ - .unmarshal = (_unmarshal_fn_t)unmarshal_##typ, \ - .marshal = (_marshal_fn_t)marshal_##typ, \ - } -#define _NONMSG(num) [num] = { \ - .name = #num, \ - } - -struct _table_version _lib9p_versions[LIB9P_VER_NUM] = { - [LIB9P_VER_unknown] = { .msgs = { - _NONMSG(0x00), - _NONMSG(0x01), - _NONMSG(0x02), - _NONMSG(0x03), - _NONMSG(0x04), - _NONMSG(0x05), - _NONMSG(0x06), - _NONMSG(0x07), - _NONMSG(0x08), - _NONMSG(0x09), - _NONMSG(0x0A), - _NONMSG(0x0B), - _NONMSG(0x0C), - _NONMSG(0x0D), - _NONMSG(0x0E), - _NONMSG(0x0F), - _NONMSG(0x10), - _NONMSG(0x11), - _NONMSG(0x12), - _NONMSG(0x13), - _NONMSG(0x14), - _NONMSG(0x15), - _NONMSG(0x16), - _NONMSG(0x17), - _NONMSG(0x18), - _NONMSG(0x19), - _NONMSG(0x1A), - _NONMSG(0x1B), - _NONMSG(0x1C), - _NONMSG(0x1D), - _NONMSG(0x1E), - _NONMSG(0x1F), - _NONMSG(0x20), - _NONMSG(0x21), - _NONMSG(0x22), - _NONMSG(0x23), - _NONMSG(0x24), - _NONMSG(0x25), - _NONMSG(0x26), - _NONMSG(0x27), - _NONMSG(0x28), - _NONMSG(0x29), - _NONMSG(0x2A), - _NONMSG(0x2B), - _NONMSG(0x2C), - _NONMSG(0x2D), - _NONMSG(0x2E), - _NONMSG(0x2F), - _NONMSG(0x30), - _NONMSG(0x31), - _NONMSG(0x32), - _NONMSG(0x33), - _NONMSG(0x34), - _NONMSG(0x35), - _NONMSG(0x36), - _NONMSG(0x37), - _NONMSG(0x38), - _NONMSG(0x39), - _NONMSG(0x3A), - _NONMSG(0x3B), - _NONMSG(0x3C), - _NONMSG(0x3D), - _NONMSG(0x3E), - _NONMSG(0x3F), - _NONMSG(0x40), - _NONMSG(0x41), - _NONMSG(0x42), - _NONMSG(0x43), - _NONMSG(0x44), - _NONMSG(0x45), - _NONMSG(0x46), - _NONMSG(0x47), - _NONMSG(0x48), - _NONMSG(0x49), - _NONMSG(0x4A), - _NONMSG(0x4B), - _NONMSG(0x4C), - _NONMSG(0x4D), - _NONMSG(0x4E), - _NONMSG(0x4F), - _NONMSG(0x50), - _NONMSG(0x51), - _NONMSG(0x52), - _NONMSG(0x53), - _NONMSG(0x54), - _NONMSG(0x55), - _NONMSG(0x56), - _NONMSG(0x57), - _NONMSG(0x58), - _NONMSG(0x59), - _NONMSG(0x5A), - _NONMSG(0x5B), - _NONMSG(0x5C), - _NONMSG(0x5D), - _NONMSG(0x5E), - _NONMSG(0x5F), - _NONMSG(0x60), - _NONMSG(0x61), - _NONMSG(0x62), - _NONMSG(0x63), + +static bool marshal_Tsread(struct lib9p_ctx *ctx, struct lib9p_msg_Tsread *val, struct _marshal_ret *ret) { + uint64_t needed_size = 13; + for (uint16_t i = 0; i < val->nwname; i++) { + needed_size += 2 + val->wname[i].len; + } + if (needed_size > (uint64_t)(ctx->max_msg_size)) { + lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", + "Tsread", + ctx->version ? "negotiated" : "client", + ctx->max_msg_size); + return true; + } + uint32_t offsetof_end = (uint32_t)needed_size; + uint32_t offsetof_size = 0; + MARSHAL_U32LE(ctx, offsetof_end - offsetof_size); + MARSHAL_U8LE(ctx, 152); + MARSHAL_U16LE(ctx, val->tag); + MARSHAL_U32LE(ctx, val->fid); + MARSHAL_U16LE(ctx, val->nwname); + for (uint16_t i = 0; i < val->nwname; i++) { + MARSHAL_U16LE(ctx, val->wname[i].len); + MARSHAL_BYTES_ZEROCOPY(ctx, val->wname[i].utf8, val->wname[i].len); + } + return false; +} + +static bool marshal_Rsread(struct lib9p_ctx *ctx, struct lib9p_msg_Rsread *val, struct _marshal_ret *ret) { + uint64_t needed_size = 11 + val->count; + if (needed_size > (uint64_t)(ctx->max_msg_size)) { + lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", + "Rsread", + ctx->version ? "negotiated" : "server", + ctx->max_msg_size); + return true; + } + uint32_t offsetof_end = (uint32_t)needed_size; + uint32_t offsetof_size = 0; + MARSHAL_U32LE(ctx, offsetof_end - offsetof_size); + MARSHAL_U8LE(ctx, 153); + MARSHAL_U16LE(ctx, val->tag); + MARSHAL_U32LE(ctx, val->count); + MARSHAL_BYTES_ZEROCOPY(ctx, val->data, val->count); + return false; +} + +static bool marshal_Tswrite(struct lib9p_ctx *ctx, struct lib9p_msg_Tswrite *val, struct _marshal_ret *ret) { + uint64_t needed_size = 17 + val->count; + for (uint16_t i = 0; i < val->nwname; i++) { + needed_size += 2 + val->wname[i].len; + } + if (needed_size > (uint64_t)(ctx->max_msg_size)) { + lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", + "Tswrite", + ctx->version ? "negotiated" : "client", + ctx->max_msg_size); + return true; + } + uint32_t offsetof_end = (uint32_t)needed_size; + uint32_t offsetof_size = 0; + MARSHAL_U32LE(ctx, offsetof_end - offsetof_size); + MARSHAL_U8LE(ctx, 154); + MARSHAL_U16LE(ctx, val->tag); + MARSHAL_U32LE(ctx, val->fid); + MARSHAL_U16LE(ctx, val->nwname); + for (uint16_t i = 0; i < val->nwname; i++) { + MARSHAL_U16LE(ctx, val->wname[i].len); + MARSHAL_BYTES_ZEROCOPY(ctx, val->wname[i].utf8, val->wname[i].len); + } + MARSHAL_U32LE(ctx, val->count); + MARSHAL_BYTES_ZEROCOPY(ctx, val->data, val->count); + return false; +} + +static bool marshal_Rswrite(struct lib9p_ctx *ctx, struct lib9p_msg_Rswrite *val, struct _marshal_ret *ret) { + uint32_t needed_size = 11; + if (needed_size > ctx->max_msg_size) { + lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", + "Rswrite", + ctx->version ? "negotiated" : "server", + ctx->max_msg_size); + return true; + } + uint32_t offsetof_end = needed_size; + uint32_t offsetof_size = 0; + MARSHAL_U32LE(ctx, offsetof_end - offsetof_size); + MARSHAL_U8LE(ctx, 155); + MARSHAL_U16LE(ctx, val->tag); + MARSHAL_U32LE(ctx, val->count); + return false; +} +#endif /* CONFIG_9P_ENABLE_9P2000_e */ + +/* *_format *******************************************************************/ + +#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u +static void lib9p_tag_format(lib9p_tag_t *self, struct fmt_state *state) { + switch (*self) { + case LIB9P_TAG_NOTAG: + fmt_state_puts(state, "NOTAG"); + break; + default: + fmt_state_printf(state, "%"PRIu16, *self); + } +} + +static void lib9p_fid_format(lib9p_fid_t *self, struct fmt_state *state) { + switch (*self) { + case LIB9P_FID_NOFID: + fmt_state_puts(state, "NOFID"); + break; + default: + fmt_state_printf(state, "%"PRIu32, *self); + } +} + +static void lib9p_s_format(struct lib9p_s *self, struct fmt_state *state) { +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat" +#pragma GCC diagnostic ignored "-Wformat-extra-args" + fmt_state_printf(state, "%.*q", self->len, self->utf8); +#pragma GCC diagnostic pop +} + +#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */ +#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u +static void lib9p_dm_format(lib9p_dm_t *self, struct fmt_state *state) { + bool empty = true; + fmt_state_putchar(state, '('); + if ((*self & ~((lib9p_dm_t)0777)) & (UINT32_C(1)<<31)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "DIR"); + empty = false; + } + if ((*self & ~((lib9p_dm_t)0777)) & (UINT32_C(1)<<30)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "APPEND"); + empty = false; + } + if ((*self & ~((lib9p_dm_t)0777)) & (UINT32_C(1)<<29)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "EXCL"); + empty = false; + } + if ((*self & ~((lib9p_dm_t)0777)) & (UINT32_C(1)<<28)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "_PLAN9_MOUNT"); + empty = false; + } + if ((*self & ~((lib9p_dm_t)0777)) & (UINT32_C(1)<<27)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "AUTH"); + empty = false; + } + if ((*self & ~((lib9p_dm_t)0777)) & (UINT32_C(1)<<26)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "TMP"); + empty = false; + } + if ((*self & ~((lib9p_dm_t)0777)) & (UINT32_C(1)<<25)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<25"); + empty = false; + } + if ((*self & ~((lib9p_dm_t)0777)) & (UINT32_C(1)<<24)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<24"); + empty = false; + } + if ((*self & ~((lib9p_dm_t)0777)) & (UINT32_C(1)<<23)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "DEVICE"); + empty = false; + } + if ((*self & ~((lib9p_dm_t)0777)) & (UINT32_C(1)<<22)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<22"); + empty = false; + } + if ((*self & ~((lib9p_dm_t)0777)) & (UINT32_C(1)<<21)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "PIPE"); + empty = false; + } + if ((*self & ~((lib9p_dm_t)0777)) & (UINT32_C(1)<<20)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "SOCKET"); + empty = false; + } + if ((*self & ~((lib9p_dm_t)0777)) & (UINT32_C(1)<<19)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "SETUID"); + empty = false; + } + if ((*self & ~((lib9p_dm_t)0777)) & (UINT32_C(1)<<18)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "SETGID"); + empty = false; + } + if ((*self & ~((lib9p_dm_t)0777)) & (UINT32_C(1)<<17)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<17"); + empty = false; + } + if ((*self & ~((lib9p_dm_t)0777)) & (UINT32_C(1)<<16)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<16"); + empty = false; + } + if ((*self & ~((lib9p_dm_t)0777)) & (UINT32_C(1)<<15)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<15"); + empty = false; + } + if ((*self & ~((lib9p_dm_t)0777)) & (UINT32_C(1)<<14)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<14"); + empty = false; + } + if ((*self & ~((lib9p_dm_t)0777)) & (UINT32_C(1)<<13)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<13"); + empty = false; + } + if ((*self & ~((lib9p_dm_t)0777)) & (UINT32_C(1)<<12)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<12"); + empty = false; + } + if ((*self & ~((lib9p_dm_t)0777)) & (UINT32_C(1)<<11)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<11"); + empty = false; + } + if ((*self & ~((lib9p_dm_t)0777)) & (UINT32_C(1)<<10)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<10"); + empty = false; + } + if ((*self & ~((lib9p_dm_t)0777)) & (UINT32_C(1)<<9)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<9"); + empty = false; + } + if ((*self & ~((lib9p_dm_t)0777)) & (UINT32_C(1)<<8)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "OWNER_R"); + empty = false; + } + if ((*self & ~((lib9p_dm_t)0777)) & (UINT32_C(1)<<7)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "OWNER_W"); + empty = false; + } + if ((*self & ~((lib9p_dm_t)0777)) & (UINT32_C(1)<<6)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "OWNER_X"); + empty = false; + } + if ((*self & ~((lib9p_dm_t)0777)) & (UINT32_C(1)<<5)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "GROUP_R"); + empty = false; + } + if ((*self & ~((lib9p_dm_t)0777)) & (UINT32_C(1)<<4)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "GROUP_W"); + empty = false; + } + if ((*self & ~((lib9p_dm_t)0777)) & (UINT32_C(1)<<3)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "GROUP_X"); + empty = false; + } + if ((*self & ~((lib9p_dm_t)0777)) & (UINT32_C(1)<<2)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "OTHER_R"); + empty = false; + } + if ((*self & ~((lib9p_dm_t)0777)) & (UINT32_C(1)<<1)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "OTHER_W"); + empty = false; + } + if ((*self & ~((lib9p_dm_t)0777)) & (UINT32_C(1)<<0)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "OTHER_X"); + empty = false; + } + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_printf(state, "%#04"PRIo32, *self & 0777); + fmt_state_putchar(state, ')'); +} + +#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */ +#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u +static void lib9p_qt_format(lib9p_qt_t *self, struct fmt_state *state) { + bool empty = true; + fmt_state_putchar(state, '('); + if (*self & (UINT8_C(1)<<7)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "DIR"); + empty = false; + } + if (*self & (UINT8_C(1)<<6)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "APPEND"); + empty = false; + } + if (*self & (UINT8_C(1)<<5)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "EXCL"); + empty = false; + } + if (*self & (UINT8_C(1)<<4)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "_PLAN9_MOUNT"); + empty = false; + } + if (*self & (UINT8_C(1)<<3)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "AUTH"); + empty = false; + } + if (*self & (UINT8_C(1)<<2)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "TMP"); + empty = false; + } + if (*self & (UINT8_C(1)<<1)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "SYMLINK"); + empty = false; + } + if (*self & (UINT8_C(1)<<0)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<0"); + empty = false; + } + if (empty) + fmt_state_putchar(state, '0'); + fmt_state_putchar(state, ')'); +} + +static void lib9p_qid_format(struct lib9p_qid *self, struct fmt_state *state) { + fmt_state_putchar(state, '{'); + fmt_state_puts(state, " type="); + lib9p_qt_format(&self->type, state); + fmt_state_puts(state, " vers="); + fmt_state_printf(state, "%"PRIu32, self->vers); + fmt_state_puts(state, " path="); + fmt_state_printf(state, "%"PRIu64, self->path); + fmt_state_puts(state, " }"); +} + +#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */ +#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u +static void lib9p_stat_format(struct lib9p_stat *self, struct fmt_state *state) { + fmt_state_putchar(state, '{'); + fmt_state_puts(state, " kern_type="); + fmt_state_printf(state, "%"PRIu16, self->kern_type); + fmt_state_puts(state, " kern_dev="); + fmt_state_printf(state, "%"PRIu32, self->kern_dev); + fmt_state_puts(state, " file_qid="); + lib9p_qid_format(&self->file_qid, state); + fmt_state_puts(state, " file_mode="); + lib9p_dm_format(&self->file_mode, state); + fmt_state_puts(state, " file_atime="); + fmt_state_printf(state, "%"PRIu32, self->file_atime); + fmt_state_puts(state, " file_mtime="); + fmt_state_printf(state, "%"PRIu32, self->file_mtime); + fmt_state_puts(state, " file_size="); + fmt_state_printf(state, "%"PRIu64, self->file_size); + fmt_state_puts(state, " file_name="); + lib9p_s_format(&self->file_name, state); + fmt_state_puts(state, " file_owner_uid="); + lib9p_s_format(&self->file_owner_uid, state); + fmt_state_puts(state, " file_owner_gid="); + lib9p_s_format(&self->file_owner_gid, state); + fmt_state_puts(state, " file_last_modified_uid="); + lib9p_s_format(&self->file_last_modified_uid, state); +#if CONFIG_9P_ENABLE_9P2000_u + fmt_state_puts(state, " file_extension="); + lib9p_s_format(&self->file_extension, state); + fmt_state_puts(state, " file_owner_n_uid="); + lib9p_nuid_format(&self->file_owner_n_uid, state); + fmt_state_puts(state, " file_owner_n_gid="); + lib9p_nuid_format(&self->file_owner_n_gid, state); + fmt_state_puts(state, " file_last_modified_n_uid="); + lib9p_nuid_format(&self->file_last_modified_n_uid, state); +#endif /* CONFIG_9P_ENABLE_9P2000_u */ + fmt_state_puts(state, " }"); +} + +static void lib9p_o_format(lib9p_o_t *self, struct fmt_state *state) { + bool empty = true; + fmt_state_putchar(state, '('); + if (*self & (UINT8_C(1)<<7)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<7"); + empty = false; + } + if (*self & (UINT8_C(1)<<6)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "RCLOSE"); + empty = false; + } + if (*self & (UINT8_C(1)<<5)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "CEXEC"); + empty = false; + } + if (*self & (UINT8_C(1)<<4)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "TRUNC"); + empty = false; + } + if (*self & (UINT8_C(1)<<3)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<3"); + empty = false; + } + if (*self & (UINT8_C(1)<<2)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<2"); + empty = false; + } + switch (*self & LIB9P_O_MODE_MASK) { + case LIB9P_O_MODE_READ: + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "MODE_READ"); + empty = false; + break; + case LIB9P_O_MODE_WRITE: + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "MODE_WRITE"); + empty = false; + break; + case LIB9P_O_MODE_RDWR: + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "MODE_RDWR"); + empty = false; + break; + case LIB9P_O_MODE_EXEC: + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "MODE_EXEC"); + empty = false; + break; + default: + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_printf(state, "%"PRIu8, *self & LIB9P_O_MODE_MASK); + empty = false; + } + if (empty) + fmt_state_putchar(state, '0'); + fmt_state_putchar(state, ')'); +} + +#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */ +#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u +static void lib9p_msg_Tversion_format(struct lib9p_msg_Tversion *self, struct fmt_state *state) { + fmt_state_puts(state, "Tversion {"); + fmt_state_puts(state, " tag="); + lib9p_tag_format(&self->tag, state); + fmt_state_puts(state, " max_msg_size="); + fmt_state_printf(state, "%"PRIu32, self->max_msg_size); + fmt_state_puts(state, " version="); + lib9p_s_format(&self->version, state); + fmt_state_puts(state, " }"); +} + +static void lib9p_msg_Rversion_format(struct lib9p_msg_Rversion *self, struct fmt_state *state) { + fmt_state_puts(state, "Rversion {"); + fmt_state_puts(state, " tag="); + lib9p_tag_format(&self->tag, state); + fmt_state_puts(state, " max_msg_size="); + fmt_state_printf(state, "%"PRIu32, self->max_msg_size); + fmt_state_puts(state, " version="); + lib9p_s_format(&self->version, state); + fmt_state_puts(state, " }"); +} + +static void lib9p_msg_Tauth_format(struct lib9p_msg_Tauth *self, struct fmt_state *state) { + fmt_state_puts(state, "Tauth {"); + fmt_state_puts(state, " tag="); + lib9p_tag_format(&self->tag, state); + fmt_state_puts(state, " afid="); + lib9p_fid_format(&self->afid, state); + fmt_state_puts(state, " uname="); + lib9p_s_format(&self->uname, state); + fmt_state_puts(state, " aname="); + lib9p_s_format(&self->aname, state); +#if CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_u + fmt_state_puts(state, " n_uid="); + lib9p_nuid_format(&self->n_uid, state); +#endif /* CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_u */ + fmt_state_puts(state, " }"); +} + +static void lib9p_msg_Rauth_format(struct lib9p_msg_Rauth *self, struct fmt_state *state) { + fmt_state_puts(state, "Rauth {"); + fmt_state_puts(state, " tag="); + lib9p_tag_format(&self->tag, state); + fmt_state_puts(state, " aqid="); + lib9p_qid_format(&self->aqid, state); + fmt_state_puts(state, " }"); +} + +static void lib9p_msg_Tattach_format(struct lib9p_msg_Tattach *self, struct fmt_state *state) { + fmt_state_puts(state, "Tattach {"); + fmt_state_puts(state, " tag="); + lib9p_tag_format(&self->tag, state); + fmt_state_puts(state, " fid="); + lib9p_fid_format(&self->fid, state); + fmt_state_puts(state, " afid="); + lib9p_fid_format(&self->afid, state); + fmt_state_puts(state, " uname="); + lib9p_s_format(&self->uname, state); + fmt_state_puts(state, " aname="); + lib9p_s_format(&self->aname, state); +#if CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_u + fmt_state_puts(state, " n_uid="); + lib9p_nuid_format(&self->n_uid, state); +#endif /* CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_u */ + fmt_state_puts(state, " }"); +} + +static void lib9p_msg_Rattach_format(struct lib9p_msg_Rattach *self, struct fmt_state *state) { + fmt_state_puts(state, "Rattach {"); + fmt_state_puts(state, " tag="); + lib9p_tag_format(&self->tag, state); + fmt_state_puts(state, " qid="); + lib9p_qid_format(&self->qid, state); + fmt_state_puts(state, " }"); +} + +static void lib9p_msg_Rerror_format(struct lib9p_msg_Rerror *self, struct fmt_state *state) { + fmt_state_puts(state, "Rerror {"); + fmt_state_puts(state, " tag="); + lib9p_tag_format(&self->tag, state); + fmt_state_puts(state, " errstr="); + lib9p_s_format(&self->errstr, state); +#if CONFIG_9P_ENABLE_9P2000_u + fmt_state_puts(state, " errnum="); + lib9p_errno_format(&self->errnum, state); +#endif /* CONFIG_9P_ENABLE_9P2000_u */ + fmt_state_puts(state, " }"); +} + +static void lib9p_msg_Tflush_format(struct lib9p_msg_Tflush *self, struct fmt_state *state) { + fmt_state_puts(state, "Tflush {"); + fmt_state_puts(state, " tag="); + lib9p_tag_format(&self->tag, state); + fmt_state_puts(state, " oldtag="); + fmt_state_printf(state, "%"PRIu16, self->oldtag); + fmt_state_puts(state, " }"); +} + +static void lib9p_msg_Rflush_format(struct lib9p_msg_Rflush *self, struct fmt_state *state) { + fmt_state_puts(state, "Rflush {"); + fmt_state_puts(state, " tag="); + lib9p_tag_format(&self->tag, state); + fmt_state_puts(state, " }"); +} + +static void lib9p_msg_Twalk_format(struct lib9p_msg_Twalk *self, struct fmt_state *state) { + fmt_state_puts(state, "Twalk {"); + fmt_state_puts(state, " tag="); + lib9p_tag_format(&self->tag, state); + fmt_state_puts(state, " fid="); + lib9p_fid_format(&self->fid, state); + fmt_state_puts(state, " newfid="); + lib9p_fid_format(&self->newfid, state); + fmt_state_puts(state, " nwname="); + fmt_state_printf(state, "%"PRIu16, self->nwname); + fmt_state_puts(state, " wname=["); + for (uint16_t i = 0; i < self->nwname; i++) { + if (i) + fmt_state_puts(state, ", "); + lib9p_s_format(&self->wname[i], state); + } + fmt_state_puts(state, " ]"); + fmt_state_puts(state, " }"); +} + +static void lib9p_msg_Rwalk_format(struct lib9p_msg_Rwalk *self, struct fmt_state *state) { + fmt_state_puts(state, "Rwalk {"); + fmt_state_puts(state, " tag="); + lib9p_tag_format(&self->tag, state); + fmt_state_puts(state, " nwqid="); + fmt_state_printf(state, "%"PRIu16, self->nwqid); + fmt_state_puts(state, " wqid=["); + for (uint16_t i = 0; i < self->nwqid; i++) { + if (i) + fmt_state_puts(state, ", "); + lib9p_qid_format(&self->wqid[i], state); + } + fmt_state_puts(state, " ]"); + fmt_state_puts(state, " }"); +} + +#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */ +#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u +static void lib9p_msg_Topen_format(struct lib9p_msg_Topen *self, struct fmt_state *state) { + fmt_state_puts(state, "Topen {"); + fmt_state_puts(state, " tag="); + lib9p_tag_format(&self->tag, state); + fmt_state_puts(state, " fid="); + lib9p_fid_format(&self->fid, state); + fmt_state_puts(state, " mode="); + lib9p_o_format(&self->mode, state); + fmt_state_puts(state, " }"); +} + +static void lib9p_msg_Ropen_format(struct lib9p_msg_Ropen *self, struct fmt_state *state) { + fmt_state_puts(state, "Ropen {"); + fmt_state_puts(state, " tag="); + lib9p_tag_format(&self->tag, state); + fmt_state_puts(state, " qid="); + lib9p_qid_format(&self->qid, state); + fmt_state_puts(state, " iounit="); + fmt_state_printf(state, "%"PRIu32, self->iounit); + fmt_state_puts(state, " }"); +} + +static void lib9p_msg_Tcreate_format(struct lib9p_msg_Tcreate *self, struct fmt_state *state) { + fmt_state_puts(state, "Tcreate {"); + fmt_state_puts(state, " tag="); + lib9p_tag_format(&self->tag, state); + fmt_state_puts(state, " fid="); + lib9p_fid_format(&self->fid, state); + fmt_state_puts(state, " name="); + lib9p_s_format(&self->name, state); + fmt_state_puts(state, " perm="); + lib9p_dm_format(&self->perm, state); + fmt_state_puts(state, " mode="); + lib9p_o_format(&self->mode, state); + fmt_state_puts(state, " }"); +} + +static void lib9p_msg_Rcreate_format(struct lib9p_msg_Rcreate *self, struct fmt_state *state) { + fmt_state_puts(state, "Rcreate {"); + fmt_state_puts(state, " tag="); + lib9p_tag_format(&self->tag, state); + fmt_state_puts(state, " qid="); + lib9p_qid_format(&self->qid, state); + fmt_state_puts(state, " iounit="); + fmt_state_printf(state, "%"PRIu32, self->iounit); + fmt_state_puts(state, " }"); +} + +#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */ +#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u +static void lib9p_msg_Tread_format(struct lib9p_msg_Tread *self, struct fmt_state *state) { + fmt_state_puts(state, "Tread {"); + fmt_state_puts(state, " tag="); + lib9p_tag_format(&self->tag, state); + fmt_state_puts(state, " fid="); + lib9p_fid_format(&self->fid, state); + fmt_state_puts(state, " offset="); + fmt_state_printf(state, "%"PRIu64, self->offset); + fmt_state_puts(state, " count="); + fmt_state_printf(state, "%"PRIu32, self->count); + fmt_state_puts(state, " }"); +} + +static void lib9p_msg_Rread_format(struct lib9p_msg_Rread *self, struct fmt_state *state) { + fmt_state_puts(state, "Rread {"); + fmt_state_puts(state, " tag="); + lib9p_tag_format(&self->tag, state); + fmt_state_puts(state, " count="); + fmt_state_printf(state, "%"PRIu32, self->count); + if (is_valid_utf8_without_nul((uint8_t *)self->data, (size_t)self->count)) { +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat" +#pragma GCC diagnostic ignored "-Wformat-extra-args" + fmt_state_printf(state, " data=%.*q%s", + (int)(self->count < 50 ? self->count : 50), + (char *)self->data, + self->count < 50 ? "" : "..."); +#pragma GCC diagnostic pop + } else { + fmt_state_puts(state, " data=<bytedata>"); + } + fmt_state_puts(state, " }"); +} + +static void lib9p_msg_Twrite_format(struct lib9p_msg_Twrite *self, struct fmt_state *state) { + fmt_state_puts(state, "Twrite {"); + fmt_state_puts(state, " tag="); + lib9p_tag_format(&self->tag, state); + fmt_state_puts(state, " fid="); + lib9p_fid_format(&self->fid, state); + fmt_state_puts(state, " offset="); + fmt_state_printf(state, "%"PRIu64, self->offset); + fmt_state_puts(state, " count="); + fmt_state_printf(state, "%"PRIu32, self->count); + if (is_valid_utf8_without_nul((uint8_t *)self->data, (size_t)self->count)) { +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat" +#pragma GCC diagnostic ignored "-Wformat-extra-args" + fmt_state_printf(state, " data=%.*q%s", + (int)(self->count < 50 ? self->count : 50), + (char *)self->data, + self->count < 50 ? "" : "..."); +#pragma GCC diagnostic pop + } else { + fmt_state_puts(state, " data=<bytedata>"); + } + fmt_state_puts(state, " }"); +} + +static void lib9p_msg_Rwrite_format(struct lib9p_msg_Rwrite *self, struct fmt_state *state) { + fmt_state_puts(state, "Rwrite {"); + fmt_state_puts(state, " tag="); + lib9p_tag_format(&self->tag, state); + fmt_state_puts(state, " count="); + fmt_state_printf(state, "%"PRIu32, self->count); + fmt_state_puts(state, " }"); +} + +static void lib9p_msg_Tclunk_format(struct lib9p_msg_Tclunk *self, struct fmt_state *state) { + fmt_state_puts(state, "Tclunk {"); + fmt_state_puts(state, " tag="); + lib9p_tag_format(&self->tag, state); + fmt_state_puts(state, " fid="); + lib9p_fid_format(&self->fid, state); + fmt_state_puts(state, " }"); +} + +static void lib9p_msg_Rclunk_format(struct lib9p_msg_Rclunk *self, struct fmt_state *state) { + fmt_state_puts(state, "Rclunk {"); + fmt_state_puts(state, " tag="); + lib9p_tag_format(&self->tag, state); + fmt_state_puts(state, " }"); +} + +static void lib9p_msg_Tremove_format(struct lib9p_msg_Tremove *self, struct fmt_state *state) { + fmt_state_puts(state, "Tremove {"); + fmt_state_puts(state, " tag="); + lib9p_tag_format(&self->tag, state); + fmt_state_puts(state, " fid="); + lib9p_fid_format(&self->fid, state); + fmt_state_puts(state, " }"); +} + +static void lib9p_msg_Rremove_format(struct lib9p_msg_Rremove *self, struct fmt_state *state) { + fmt_state_puts(state, "Rremove {"); + fmt_state_puts(state, " tag="); + lib9p_tag_format(&self->tag, state); + fmt_state_puts(state, " }"); +} + +#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */ +#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u +static void lib9p_msg_Tstat_format(struct lib9p_msg_Tstat *self, struct fmt_state *state) { + fmt_state_puts(state, "Tstat {"); + fmt_state_puts(state, " tag="); + lib9p_tag_format(&self->tag, state); + fmt_state_puts(state, " fid="); + lib9p_fid_format(&self->fid, state); + fmt_state_puts(state, " }"); +} + +static void lib9p_msg_Rstat_format(struct lib9p_msg_Rstat *self, struct fmt_state *state) { + fmt_state_puts(state, "Rstat {"); + fmt_state_puts(state, " tag="); + lib9p_tag_format(&self->tag, state); + fmt_state_puts(state, " stat="); + lib9p_stat_format(&self->stat, state); + fmt_state_puts(state, " }"); +} + +static void lib9p_msg_Twstat_format(struct lib9p_msg_Twstat *self, struct fmt_state *state) { + fmt_state_puts(state, "Twstat {"); + fmt_state_puts(state, " tag="); + lib9p_tag_format(&self->tag, state); + fmt_state_puts(state, " fid="); + lib9p_fid_format(&self->fid, state); + fmt_state_puts(state, " stat="); + lib9p_stat_format(&self->stat, state); + fmt_state_puts(state, " }"); +} + +static void lib9p_msg_Rwstat_format(struct lib9p_msg_Rwstat *self, struct fmt_state *state) { + fmt_state_puts(state, "Rwstat {"); + fmt_state_puts(state, " tag="); + lib9p_tag_format(&self->tag, state); + fmt_state_puts(state, " }"); +} + +#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */ +#if CONFIG_9P_ENABLE_9P2000_p9p +static void lib9p_msg_Topenfd_format(struct lib9p_msg_Topenfd *self, struct fmt_state *state) { + fmt_state_puts(state, "Topenfd {"); + fmt_state_puts(state, " tag="); + lib9p_tag_format(&self->tag, state); + fmt_state_puts(state, " fid="); + lib9p_fid_format(&self->fid, state); + fmt_state_puts(state, " mode="); + lib9p_o_format(&self->mode, state); + fmt_state_puts(state, " }"); +} + +static void lib9p_msg_Ropenfd_format(struct lib9p_msg_Ropenfd *self, struct fmt_state *state) { + fmt_state_puts(state, "Ropenfd {"); + fmt_state_puts(state, " tag="); + lib9p_tag_format(&self->tag, state); + fmt_state_puts(state, " qid="); + lib9p_qid_format(&self->qid, state); + fmt_state_puts(state, " iounit="); + fmt_state_printf(state, "%"PRIu32, self->iounit); + fmt_state_puts(state, " unixfd="); + fmt_state_printf(state, "%"PRIu32, self->unixfd); + fmt_state_puts(state, " }"); +} + +#endif /* CONFIG_9P_ENABLE_9P2000_p9p */ +#if CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_u +static void lib9p_nuid_format(lib9p_nuid_t *self, struct fmt_state *state) { + switch (*self) { + case LIB9P_NUID_NONUID: + fmt_state_puts(state, "NONUID"); + break; + default: + fmt_state_printf(state, "%"PRIu32, *self); + } +} + +static void lib9p_errno_format(lib9p_errno_t *self, struct fmt_state *state) { + switch (*self) { + case LIB9P_ERRNO_NOERROR: + fmt_state_puts(state, "NOERROR"); + break; + default: + fmt_state_printf(state, "%"PRIu32, *self); + } +} + +#endif /* CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_u */ +#if CONFIG_9P_ENABLE_9P2000_L +static void lib9p_super_magic_format(lib9p_super_magic_t *self, struct fmt_state *state) { + switch (*self) { + case LIB9P_SUPER_MAGIC_V9FS_MAGIC: + fmt_state_puts(state, "V9FS_MAGIC"); + break; + default: + fmt_state_printf(state, "%"PRIu32, *self); + } +} + +static void lib9p_lo_format(lib9p_lo_t *self, struct fmt_state *state) { + bool empty = true; + fmt_state_putchar(state, '('); + if (*self & (UINT32_C(1)<<31)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<31"); + empty = false; + } + if (*self & (UINT32_C(1)<<30)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<30"); + empty = false; + } + if (*self & (UINT32_C(1)<<29)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<29"); + empty = false; + } + if (*self & (UINT32_C(1)<<28)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<28"); + empty = false; + } + if (*self & (UINT32_C(1)<<27)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<27"); + empty = false; + } + if (*self & (UINT32_C(1)<<26)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<26"); + empty = false; + } + if (*self & (UINT32_C(1)<<25)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<25"); + empty = false; + } + if (*self & (UINT32_C(1)<<24)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<24"); + empty = false; + } + if (*self & (UINT32_C(1)<<23)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<23"); + empty = false; + } + if (*self & (UINT32_C(1)<<22)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<22"); + empty = false; + } + if (*self & (UINT32_C(1)<<21)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<21"); + empty = false; + } + if (*self & (UINT32_C(1)<<20)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "SYNC"); + empty = false; + } + if (*self & (UINT32_C(1)<<19)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "CLOEXEC"); + empty = false; + } + if (*self & (UINT32_C(1)<<18)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "NOATIME"); + empty = false; + } + if (*self & (UINT32_C(1)<<17)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "NOFOLLOW"); + empty = false; + } + if (*self & (UINT32_C(1)<<16)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "DIRECTORY"); + empty = false; + } + if (*self & (UINT32_C(1)<<15)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "LARGEFILE"); + empty = false; + } + if (*self & (UINT32_C(1)<<14)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "DIRECT"); + empty = false; + } + if (*self & (UINT32_C(1)<<13)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "BSD_FASYNC"); + empty = false; + } + if (*self & (UINT32_C(1)<<12)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "DSYNC"); + empty = false; + } + if (*self & (UINT32_C(1)<<11)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "NONBLOCK"); + empty = false; + } + if (*self & (UINT32_C(1)<<10)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "APPEND"); + empty = false; + } + if (*self & (UINT32_C(1)<<9)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "TRUNC"); + empty = false; + } + if (*self & (UINT32_C(1)<<8)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "NOCTTY"); + empty = false; + } + if (*self & (UINT32_C(1)<<7)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "EXCL"); + empty = false; + } + if (*self & (UINT32_C(1)<<6)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "CREATE"); + empty = false; + } + if (*self & (UINT32_C(1)<<5)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<5"); + empty = false; + } + if (*self & (UINT32_C(1)<<4)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<4"); + empty = false; + } + if (*self & (UINT32_C(1)<<3)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<3"); + empty = false; + } + if (*self & (UINT32_C(1)<<2)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<2"); + empty = false; + } + switch (*self & LIB9P_LO_MODE_MASK) { + case LIB9P_LO_MODE_RDONLY: + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "MODE_RDONLY"); + empty = false; + break; + case LIB9P_LO_MODE_WRONLY: + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "MODE_WRONLY"); + empty = false; + break; + case LIB9P_LO_MODE_RDWR: + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "MODE_RDWR"); + empty = false; + break; + case LIB9P_LO_MODE_NOACCESS: + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "MODE_NOACCESS"); + empty = false; + break; + default: + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_printf(state, "%"PRIu32, *self & LIB9P_LO_MODE_MASK); + empty = false; + } + if (empty) + fmt_state_putchar(state, '0'); + fmt_state_putchar(state, ')'); +} + +static void lib9p_dt_format(lib9p_dt_t *self, struct fmt_state *state) { + switch (*self) { + case LIB9P_DT_UNKNOWN: + fmt_state_puts(state, "UNKNOWN"); + break; + case LIB9P_DT_PIPE: + fmt_state_puts(state, "PIPE"); + break; + case LIB9P_DT_CHAR_DEV: + fmt_state_puts(state, "CHAR_DEV"); + break; + case LIB9P_DT_DIRECTORY: + fmt_state_puts(state, "DIRECTORY"); + break; + case LIB9P_DT_BLOCK_DEV: + fmt_state_puts(state, "BLOCK_DEV"); + break; + case LIB9P_DT_REGULAR: + fmt_state_puts(state, "REGULAR"); + break; + case LIB9P_DT_SYMLINK: + fmt_state_puts(state, "SYMLINK"); + break; + case LIB9P_DT_SOCKET: + fmt_state_puts(state, "SOCKET"); + break; + case _LIB9P_DT_WHITEOUT: + fmt_state_puts(state, "_WHITEOUT"); + break; + default: + fmt_state_printf(state, "%"PRIu8, *self); + } +} + +static void lib9p_mode_format(lib9p_mode_t *self, struct fmt_state *state) { + bool empty = true; + fmt_state_putchar(state, '('); + if (*self & (UINT32_C(1)<<31)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<31"); + empty = false; + } + if (*self & (UINT32_C(1)<<30)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<30"); + empty = false; + } + if (*self & (UINT32_C(1)<<29)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<29"); + empty = false; + } + if (*self & (UINT32_C(1)<<28)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<28"); + empty = false; + } + if (*self & (UINT32_C(1)<<27)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<27"); + empty = false; + } + if (*self & (UINT32_C(1)<<26)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<26"); + empty = false; + } + if (*self & (UINT32_C(1)<<25)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<25"); + empty = false; + } + if (*self & (UINT32_C(1)<<24)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<24"); + empty = false; + } + if (*self & (UINT32_C(1)<<23)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<23"); + empty = false; + } + if (*self & (UINT32_C(1)<<22)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<22"); + empty = false; + } + if (*self & (UINT32_C(1)<<21)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<21"); + empty = false; + } + if (*self & (UINT32_C(1)<<20)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<20"); + empty = false; + } + if (*self & (UINT32_C(1)<<19)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<19"); + empty = false; + } + if (*self & (UINT32_C(1)<<18)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<18"); + empty = false; + } + if (*self & (UINT32_C(1)<<17)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<17"); + empty = false; + } + if (*self & (UINT32_C(1)<<16)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<16"); + empty = false; + } + switch (*self & LIB9P_MODE_FMT_MASK) { + case LIB9P_MODE_FMT_PIPE: + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "FMT_PIPE"); + empty = false; + break; + case LIB9P_MODE_FMT_CHAR_DEV: + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "FMT_CHAR_DEV"); + empty = false; + break; + case LIB9P_MODE_FMT_DIRECTORY: + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "FMT_DIRECTORY"); + empty = false; + break; + case LIB9P_MODE_FMT_BLOCK_DEV: + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "FMT_BLOCK_DEV"); + empty = false; + break; + case LIB9P_MODE_FMT_REGULAR: + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "FMT_REGULAR"); + empty = false; + break; + case LIB9P_MODE_FMT_SYMLINK: + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "FMT_SYMLINK"); + empty = false; + break; + case LIB9P_MODE_FMT_SOCKET: + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "FMT_SOCKET"); + empty = false; + break; + default: + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_printf(state, "%"PRIu32, *self & LIB9P_MODE_FMT_MASK); + empty = false; + } + if (*self & (UINT32_C(1)<<11)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "PERM_SETGROUP"); + empty = false; + } + if (*self & (UINT32_C(1)<<10)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "PERM_SETUSER"); + empty = false; + } + if (*self & (UINT32_C(1)<<9)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "PERM_STICKY"); + empty = false; + } + if (*self & (UINT32_C(1)<<8)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "PERM_OWNER_R"); + empty = false; + } + if (*self & (UINT32_C(1)<<7)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "PERM_OWNER_W"); + empty = false; + } + if (*self & (UINT32_C(1)<<6)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "PERM_OWNER_X"); + empty = false; + } + if (*self & (UINT32_C(1)<<5)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "PERM_GROUP_R"); + empty = false; + } + if (*self & (UINT32_C(1)<<4)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "PERM_GROUP_W"); + empty = false; + } + if (*self & (UINT32_C(1)<<3)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "PERM_GROUP_X"); + empty = false; + } + if (*self & (UINT32_C(1)<<2)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "PERM_OTHER_R"); + empty = false; + } + if (*self & (UINT32_C(1)<<1)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "PERM_OTHER_W"); + empty = false; + } + if (*self & (UINT32_C(1)<<0)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "PERM_OTHER_X"); + empty = false; + } + if (empty) + fmt_state_putchar(state, '0'); + fmt_state_putchar(state, ')'); +} + +static void lib9p_b4_format(lib9p_b4_t *self, struct fmt_state *state) { + switch (*self) { + case LIB9P_B4_FALSE: + fmt_state_puts(state, "FALSE"); + break; + case LIB9P_B4_TRUE: + fmt_state_puts(state, "TRUE"); + break; + default: + fmt_state_printf(state, "%"PRIu32, *self); + } +} + +static void lib9p_getattr_format(lib9p_getattr_t *self, struct fmt_state *state) { + bool empty = true; + fmt_state_putchar(state, '('); + if (*self & (UINT64_C(1)<<63)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<63"); + empty = false; + } + if (*self & (UINT64_C(1)<<62)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<62"); + empty = false; + } + if (*self & (UINT64_C(1)<<61)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<61"); + empty = false; + } + if (*self & (UINT64_C(1)<<60)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<60"); + empty = false; + } + if (*self & (UINT64_C(1)<<59)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<59"); + empty = false; + } + if (*self & (UINT64_C(1)<<58)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<58"); + empty = false; + } + if (*self & (UINT64_C(1)<<57)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<57"); + empty = false; + } + if (*self & (UINT64_C(1)<<56)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<56"); + empty = false; + } + if (*self & (UINT64_C(1)<<55)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<55"); + empty = false; + } + if (*self & (UINT64_C(1)<<54)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<54"); + empty = false; + } + if (*self & (UINT64_C(1)<<53)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<53"); + empty = false; + } + if (*self & (UINT64_C(1)<<52)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<52"); + empty = false; + } + if (*self & (UINT64_C(1)<<51)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<51"); + empty = false; + } + if (*self & (UINT64_C(1)<<50)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<50"); + empty = false; + } + if (*self & (UINT64_C(1)<<49)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<49"); + empty = false; + } + if (*self & (UINT64_C(1)<<48)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<48"); + empty = false; + } + if (*self & (UINT64_C(1)<<47)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<47"); + empty = false; + } + if (*self & (UINT64_C(1)<<46)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<46"); + empty = false; + } + if (*self & (UINT64_C(1)<<45)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<45"); + empty = false; + } + if (*self & (UINT64_C(1)<<44)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<44"); + empty = false; + } + if (*self & (UINT64_C(1)<<43)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<43"); + empty = false; + } + if (*self & (UINT64_C(1)<<42)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<42"); + empty = false; + } + if (*self & (UINT64_C(1)<<41)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<41"); + empty = false; + } + if (*self & (UINT64_C(1)<<40)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<40"); + empty = false; + } + if (*self & (UINT64_C(1)<<39)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<39"); + empty = false; + } + if (*self & (UINT64_C(1)<<38)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<38"); + empty = false; + } + if (*self & (UINT64_C(1)<<37)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<37"); + empty = false; + } + if (*self & (UINT64_C(1)<<36)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<36"); + empty = false; + } + if (*self & (UINT64_C(1)<<35)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<35"); + empty = false; + } + if (*self & (UINT64_C(1)<<34)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<34"); + empty = false; + } + if (*self & (UINT64_C(1)<<33)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<33"); + empty = false; + } + if (*self & (UINT64_C(1)<<32)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<32"); + empty = false; + } + if (*self & (UINT64_C(1)<<31)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<31"); + empty = false; + } + if (*self & (UINT64_C(1)<<30)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<30"); + empty = false; + } + if (*self & (UINT64_C(1)<<29)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<29"); + empty = false; + } + if (*self & (UINT64_C(1)<<28)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<28"); + empty = false; + } + if (*self & (UINT64_C(1)<<27)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<27"); + empty = false; + } + if (*self & (UINT64_C(1)<<26)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<26"); + empty = false; + } + if (*self & (UINT64_C(1)<<25)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<25"); + empty = false; + } + if (*self & (UINT64_C(1)<<24)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<24"); + empty = false; + } + if (*self & (UINT64_C(1)<<23)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<23"); + empty = false; + } + if (*self & (UINT64_C(1)<<22)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<22"); + empty = false; + } + if (*self & (UINT64_C(1)<<21)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<21"); + empty = false; + } + if (*self & (UINT64_C(1)<<20)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<20"); + empty = false; + } + if (*self & (UINT64_C(1)<<19)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<19"); + empty = false; + } + if (*self & (UINT64_C(1)<<18)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<18"); + empty = false; + } + if (*self & (UINT64_C(1)<<17)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<17"); + empty = false; + } + if (*self & (UINT64_C(1)<<16)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<16"); + empty = false; + } + if (*self & (UINT64_C(1)<<15)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<15"); + empty = false; + } + if (*self & (UINT64_C(1)<<14)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<14"); + empty = false; + } + if (*self & (UINT64_C(1)<<13)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "DATA_VERSION"); + empty = false; + } + if (*self & (UINT64_C(1)<<12)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "GEN"); + empty = false; + } + if (*self & (UINT64_C(1)<<11)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "BTIME"); + empty = false; + } + if (*self & (UINT64_C(1)<<10)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "BLOCKS"); + empty = false; + } + if (*self & (UINT64_C(1)<<9)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "SIZE"); + empty = false; + } + if (*self & (UINT64_C(1)<<8)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "INO"); + empty = false; + } + if (*self & (UINT64_C(1)<<7)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "CTIME"); + empty = false; + } + if (*self & (UINT64_C(1)<<6)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "MTIME"); + empty = false; + } + if (*self & (UINT64_C(1)<<5)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "ATIME"); + empty = false; + } + if (*self & (UINT64_C(1)<<4)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "RDEV"); + empty = false; + } + if (*self & (UINT64_C(1)<<3)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "GID"); + empty = false; + } + if (*self & (UINT64_C(1)<<2)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "UID"); + empty = false; + } + if (*self & (UINT64_C(1)<<1)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "NLINK"); + empty = false; + } + if (*self & (UINT64_C(1)<<0)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "MODE"); + empty = false; + } + if (empty) + fmt_state_putchar(state, '0'); + fmt_state_putchar(state, ')'); +} + +static void lib9p_setattr_format(lib9p_setattr_t *self, struct fmt_state *state) { + bool empty = true; + fmt_state_putchar(state, '('); + if (*self & (UINT32_C(1)<<31)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<31"); + empty = false; + } + if (*self & (UINT32_C(1)<<30)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<30"); + empty = false; + } + if (*self & (UINT32_C(1)<<29)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<29"); + empty = false; + } + if (*self & (UINT32_C(1)<<28)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<28"); + empty = false; + } + if (*self & (UINT32_C(1)<<27)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<27"); + empty = false; + } + if (*self & (UINT32_C(1)<<26)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<26"); + empty = false; + } + if (*self & (UINT32_C(1)<<25)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<25"); + empty = false; + } + if (*self & (UINT32_C(1)<<24)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<24"); + empty = false; + } + if (*self & (UINT32_C(1)<<23)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<23"); + empty = false; + } + if (*self & (UINT32_C(1)<<22)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<22"); + empty = false; + } + if (*self & (UINT32_C(1)<<21)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<21"); + empty = false; + } + if (*self & (UINT32_C(1)<<20)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<20"); + empty = false; + } + if (*self & (UINT32_C(1)<<19)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<19"); + empty = false; + } + if (*self & (UINT32_C(1)<<18)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<18"); + empty = false; + } + if (*self & (UINT32_C(1)<<17)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<17"); + empty = false; + } + if (*self & (UINT32_C(1)<<16)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<16"); + empty = false; + } + if (*self & (UINT32_C(1)<<15)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<15"); + empty = false; + } + if (*self & (UINT32_C(1)<<14)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<14"); + empty = false; + } + if (*self & (UINT32_C(1)<<13)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<13"); + empty = false; + } + if (*self & (UINT32_C(1)<<12)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<12"); + empty = false; + } + if (*self & (UINT32_C(1)<<11)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<11"); + empty = false; + } + if (*self & (UINT32_C(1)<<10)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<10"); + empty = false; + } + if (*self & (UINT32_C(1)<<9)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<9"); + empty = false; + } + if (*self & (UINT32_C(1)<<8)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "MTIME_SET"); + empty = false; + } + if (*self & (UINT32_C(1)<<7)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "ATIME_SET"); + empty = false; + } + if (*self & (UINT32_C(1)<<6)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "CTIME"); + empty = false; + } + if (*self & (UINT32_C(1)<<5)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "MTIME"); + empty = false; + } + if (*self & (UINT32_C(1)<<4)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "ATIME"); + empty = false; + } + if (*self & (UINT32_C(1)<<3)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "SIZE"); + empty = false; + } + if (*self & (UINT32_C(1)<<2)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "GID"); + empty = false; + } + if (*self & (UINT32_C(1)<<1)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "UID"); + empty = false; + } + if (*self & (UINT32_C(1)<<0)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "MODE"); + empty = false; + } + if (empty) + fmt_state_putchar(state, '0'); + fmt_state_putchar(state, ')'); +} + +static void lib9p_lock_type_format(lib9p_lock_type_t *self, struct fmt_state *state) { + switch (*self) { + case LIB9P_LOCK_TYPE_RDLCK: + fmt_state_puts(state, "RDLCK"); + break; + case LIB9P_LOCK_TYPE_WRLCK: + fmt_state_puts(state, "WRLCK"); + break; + case LIB9P_LOCK_TYPE_UNLCK: + fmt_state_puts(state, "UNLCK"); + break; + default: + fmt_state_printf(state, "%"PRIu8, *self); + } +} + +static void lib9p_lock_flags_format(lib9p_lock_flags_t *self, struct fmt_state *state) { + bool empty = true; + fmt_state_putchar(state, '('); + if (*self & (UINT32_C(1)<<31)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<31"); + empty = false; + } + if (*self & (UINT32_C(1)<<30)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<30"); + empty = false; + } + if (*self & (UINT32_C(1)<<29)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<29"); + empty = false; + } + if (*self & (UINT32_C(1)<<28)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<28"); + empty = false; + } + if (*self & (UINT32_C(1)<<27)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<27"); + empty = false; + } + if (*self & (UINT32_C(1)<<26)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<26"); + empty = false; + } + if (*self & (UINT32_C(1)<<25)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<25"); + empty = false; + } + if (*self & (UINT32_C(1)<<24)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<24"); + empty = false; + } + if (*self & (UINT32_C(1)<<23)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<23"); + empty = false; + } + if (*self & (UINT32_C(1)<<22)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<22"); + empty = false; + } + if (*self & (UINT32_C(1)<<21)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<21"); + empty = false; + } + if (*self & (UINT32_C(1)<<20)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<20"); + empty = false; + } + if (*self & (UINT32_C(1)<<19)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<19"); + empty = false; + } + if (*self & (UINT32_C(1)<<18)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<18"); + empty = false; + } + if (*self & (UINT32_C(1)<<17)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<17"); + empty = false; + } + if (*self & (UINT32_C(1)<<16)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<16"); + empty = false; + } + if (*self & (UINT32_C(1)<<15)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<15"); + empty = false; + } + if (*self & (UINT32_C(1)<<14)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<14"); + empty = false; + } + if (*self & (UINT32_C(1)<<13)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<13"); + empty = false; + } + if (*self & (UINT32_C(1)<<12)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<12"); + empty = false; + } + if (*self & (UINT32_C(1)<<11)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<11"); + empty = false; + } + if (*self & (UINT32_C(1)<<10)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<10"); + empty = false; + } + if (*self & (UINT32_C(1)<<9)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<9"); + empty = false; + } + if (*self & (UINT32_C(1)<<8)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<8"); + empty = false; + } + if (*self & (UINT32_C(1)<<7)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<7"); + empty = false; + } + if (*self & (UINT32_C(1)<<6)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<6"); + empty = false; + } + if (*self & (UINT32_C(1)<<5)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<5"); + empty = false; + } + if (*self & (UINT32_C(1)<<4)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<4"); + empty = false; + } + if (*self & (UINT32_C(1)<<3)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<3"); + empty = false; + } + if (*self & (UINT32_C(1)<<2)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "1<<2"); + empty = false; + } + if (*self & (UINT32_C(1)<<1)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "RECLAIM"); + empty = false; + } + if (*self & (UINT32_C(1)<<0)) { + if (!empty) + fmt_state_putchar(state, '|'); + fmt_state_puts(state, "BLOCK"); + empty = false; + } + if (empty) + fmt_state_putchar(state, '0'); + fmt_state_putchar(state, ')'); +} + +static void lib9p_lock_status_format(lib9p_lock_status_t *self, struct fmt_state *state) { + switch (*self) { + case LIB9P_LOCK_STATUS_SUCCESS: + fmt_state_puts(state, "SUCCESS"); + break; + case LIB9P_LOCK_STATUS_BLOCKED: + fmt_state_puts(state, "BLOCKED"); + break; + case LIB9P_LOCK_STATUS_ERROR: + fmt_state_puts(state, "ERROR"); + break; + case LIB9P_LOCK_STATUS_GRACE: + fmt_state_puts(state, "GRACE"); + break; + default: + fmt_state_printf(state, "%"PRIu8, *self); + } +} + +static void lib9p_msg_Rlerror_format(struct lib9p_msg_Rlerror *self, struct fmt_state *state) { + fmt_state_puts(state, "Rlerror {"); + fmt_state_puts(state, " tag="); + lib9p_tag_format(&self->tag, state); + fmt_state_puts(state, " errnum="); + lib9p_errno_format(&self->errnum, state); + fmt_state_puts(state, " }"); +} + +static void lib9p_msg_Tstatfs_format(struct lib9p_msg_Tstatfs *self, struct fmt_state *state) { + fmt_state_puts(state, "Tstatfs {"); + fmt_state_puts(state, " tag="); + lib9p_tag_format(&self->tag, state); + fmt_state_puts(state, " fid="); + lib9p_fid_format(&self->fid, state); + fmt_state_puts(state, " }"); +} + +static void lib9p_msg_Rstatfs_format(struct lib9p_msg_Rstatfs *self, struct fmt_state *state) { + fmt_state_puts(state, "Rstatfs {"); + fmt_state_puts(state, " tag="); + lib9p_tag_format(&self->tag, state); + fmt_state_puts(state, " type="); + lib9p_super_magic_format(&self->type, state); + fmt_state_puts(state, " bsize="); + fmt_state_printf(state, "%"PRIu32, self->bsize); + fmt_state_puts(state, " blocks="); + fmt_state_printf(state, "%"PRIu64, self->blocks); + fmt_state_puts(state, " bfree="); + fmt_state_printf(state, "%"PRIu64, self->bfree); + fmt_state_puts(state, " bavail="); + fmt_state_printf(state, "%"PRIu64, self->bavail); + fmt_state_puts(state, " files="); + fmt_state_printf(state, "%"PRIu64, self->files); + fmt_state_puts(state, " ffree="); + fmt_state_printf(state, "%"PRIu64, self->ffree); + fmt_state_puts(state, " fsid="); + fmt_state_printf(state, "%"PRIu64, self->fsid); + fmt_state_puts(state, " namelen="); + fmt_state_printf(state, "%"PRIu32, self->namelen); + fmt_state_puts(state, " }"); +} + +static void lib9p_msg_Tlopen_format(struct lib9p_msg_Tlopen *self, struct fmt_state *state) { + fmt_state_puts(state, "Tlopen {"); + fmt_state_puts(state, " tag="); + lib9p_tag_format(&self->tag, state); + fmt_state_puts(state, " fid="); + lib9p_fid_format(&self->fid, state); + fmt_state_puts(state, " flags="); + lib9p_lo_format(&self->flags, state); + fmt_state_puts(state, " }"); +} + +static void lib9p_msg_Rlopen_format(struct lib9p_msg_Rlopen *self, struct fmt_state *state) { + fmt_state_puts(state, "Rlopen {"); + fmt_state_puts(state, " tag="); + lib9p_tag_format(&self->tag, state); + fmt_state_puts(state, " qid="); + lib9p_qid_format(&self->qid, state); + fmt_state_puts(state, " iounit="); + fmt_state_printf(state, "%"PRIu32, self->iounit); + fmt_state_puts(state, " }"); +} + +static void lib9p_msg_Tlcreate_format(struct lib9p_msg_Tlcreate *self, struct fmt_state *state) { + fmt_state_puts(state, "Tlcreate {"); + fmt_state_puts(state, " tag="); + lib9p_tag_format(&self->tag, state); + fmt_state_puts(state, " fid="); + lib9p_fid_format(&self->fid, state); + fmt_state_puts(state, " name="); + lib9p_s_format(&self->name, state); + fmt_state_puts(state, " flags="); + lib9p_lo_format(&self->flags, state); + fmt_state_puts(state, " mode="); + lib9p_mode_format(&self->mode, state); + fmt_state_puts(state, " gid="); + lib9p_nuid_format(&self->gid, state); + fmt_state_puts(state, " }"); +} + +static void lib9p_msg_Rlcreate_format(struct lib9p_msg_Rlcreate *self, struct fmt_state *state) { + fmt_state_puts(state, "Rlcreate {"); + fmt_state_puts(state, " tag="); + lib9p_tag_format(&self->tag, state); + fmt_state_puts(state, " qid="); + lib9p_qid_format(&self->qid, state); + fmt_state_puts(state, " iounit="); + fmt_state_printf(state, "%"PRIu32, self->iounit); + fmt_state_puts(state, " }"); +} + +static void lib9p_msg_Tsymlink_format(struct lib9p_msg_Tsymlink *self, struct fmt_state *state) { + fmt_state_puts(state, "Tsymlink {"); + fmt_state_puts(state, " tag="); + lib9p_tag_format(&self->tag, state); + fmt_state_puts(state, " fid="); + lib9p_fid_format(&self->fid, state); + fmt_state_puts(state, " name="); + lib9p_s_format(&self->name, state); + fmt_state_puts(state, " symtgt="); + lib9p_s_format(&self->symtgt, state); + fmt_state_puts(state, " gid="); + lib9p_nuid_format(&self->gid, state); + fmt_state_puts(state, " }"); +} + +static void lib9p_msg_Rsymlink_format(struct lib9p_msg_Rsymlink *self, struct fmt_state *state) { + fmt_state_puts(state, "Rsymlink {"); + fmt_state_puts(state, " tag="); + lib9p_tag_format(&self->tag, state); + fmt_state_puts(state, " qid="); + lib9p_qid_format(&self->qid, state); + fmt_state_puts(state, " }"); +} + +static void lib9p_msg_Tmknod_format(struct lib9p_msg_Tmknod *self, struct fmt_state *state) { + fmt_state_puts(state, "Tmknod {"); + fmt_state_puts(state, " tag="); + lib9p_tag_format(&self->tag, state); + fmt_state_puts(state, " dfid="); + lib9p_fid_format(&self->dfid, state); + fmt_state_puts(state, " name="); + lib9p_s_format(&self->name, state); + fmt_state_puts(state, " mode="); + lib9p_mode_format(&self->mode, state); + fmt_state_puts(state, " major="); + fmt_state_printf(state, "%"PRIu32, self->major); + fmt_state_puts(state, " minor="); + fmt_state_printf(state, "%"PRIu32, self->minor); + fmt_state_puts(state, " gid="); + lib9p_nuid_format(&self->gid, state); + fmt_state_puts(state, " }"); +} + +static void lib9p_msg_Rmknod_format(struct lib9p_msg_Rmknod *self, struct fmt_state *state) { + fmt_state_puts(state, "Rmknod {"); + fmt_state_puts(state, " tag="); + lib9p_tag_format(&self->tag, state); + fmt_state_puts(state, " qid="); + lib9p_qid_format(&self->qid, state); + fmt_state_puts(state, " }"); +} + +static void lib9p_msg_Trename_format(struct lib9p_msg_Trename *self, struct fmt_state *state) { + fmt_state_puts(state, "Trename {"); + fmt_state_puts(state, " tag="); + lib9p_tag_format(&self->tag, state); + fmt_state_puts(state, " fid="); + lib9p_fid_format(&self->fid, state); + fmt_state_puts(state, " dfid="); + lib9p_fid_format(&self->dfid, state); + fmt_state_puts(state, " name="); + lib9p_s_format(&self->name, state); + fmt_state_puts(state, " }"); +} + +static void lib9p_msg_Rrename_format(struct lib9p_msg_Rrename *self, struct fmt_state *state) { + fmt_state_puts(state, "Rrename {"); + fmt_state_puts(state, " tag="); + lib9p_tag_format(&self->tag, state); + fmt_state_puts(state, " }"); +} + +static void lib9p_msg_Treadlink_format(struct lib9p_msg_Treadlink *self, struct fmt_state *state) { + fmt_state_puts(state, "Treadlink {"); + fmt_state_puts(state, " tag="); + lib9p_tag_format(&self->tag, state); + fmt_state_puts(state, " fid="); + lib9p_fid_format(&self->fid, state); + fmt_state_puts(state, " }"); +} + +static void lib9p_msg_Rreadlink_format(struct lib9p_msg_Rreadlink *self, struct fmt_state *state) { + fmt_state_puts(state, "Rreadlink {"); + fmt_state_puts(state, " tag="); + lib9p_tag_format(&self->tag, state); + fmt_state_puts(state, " target="); + lib9p_s_format(&self->target, state); + fmt_state_puts(state, " }"); +} + +static void lib9p_msg_Tgetattr_format(struct lib9p_msg_Tgetattr *self, struct fmt_state *state) { + fmt_state_puts(state, "Tgetattr {"); + fmt_state_puts(state, " tag="); + lib9p_tag_format(&self->tag, state); + fmt_state_puts(state, " fid="); + lib9p_fid_format(&self->fid, state); + fmt_state_puts(state, " request_mask="); + lib9p_getattr_format(&self->request_mask, state); + fmt_state_puts(state, " }"); +} + +static void lib9p_msg_Rgetattr_format(struct lib9p_msg_Rgetattr *self, struct fmt_state *state) { + fmt_state_puts(state, "Rgetattr {"); + fmt_state_puts(state, " tag="); + lib9p_tag_format(&self->tag, state); + fmt_state_puts(state, " valid="); + lib9p_getattr_format(&self->valid, state); + fmt_state_puts(state, " qid="); + lib9p_qid_format(&self->qid, state); + fmt_state_puts(state, " mode="); + lib9p_mode_format(&self->mode, state); + fmt_state_puts(state, " uid="); + lib9p_nuid_format(&self->uid, state); + fmt_state_puts(state, " gid="); + lib9p_nuid_format(&self->gid, state); + fmt_state_puts(state, " nlink="); + fmt_state_printf(state, "%"PRIu64, self->nlink); + fmt_state_puts(state, " rdev="); + fmt_state_printf(state, "%"PRIu64, self->rdev); + fmt_state_puts(state, " filesize="); + fmt_state_printf(state, "%"PRIu64, self->filesize); + fmt_state_puts(state, " blksize="); + fmt_state_printf(state, "%"PRIu64, self->blksize); + fmt_state_puts(state, " blocks="); + fmt_state_printf(state, "%"PRIu64, self->blocks); + fmt_state_puts(state, " atime_sec="); + fmt_state_printf(state, "%"PRIu64, self->atime_sec); + fmt_state_puts(state, " atime_nsec="); + fmt_state_printf(state, "%"PRIu64, self->atime_nsec); + fmt_state_puts(state, " mtime_sec="); + fmt_state_printf(state, "%"PRIu64, self->mtime_sec); + fmt_state_puts(state, " mtime_nsec="); + fmt_state_printf(state, "%"PRIu64, self->mtime_nsec); + fmt_state_puts(state, " ctime_sec="); + fmt_state_printf(state, "%"PRIu64, self->ctime_sec); + fmt_state_puts(state, " ctime_nsec="); + fmt_state_printf(state, "%"PRIu64, self->ctime_nsec); + fmt_state_puts(state, " btime_sec="); + fmt_state_printf(state, "%"PRIu64, self->btime_sec); + fmt_state_puts(state, " btime_nsec="); + fmt_state_printf(state, "%"PRIu64, self->btime_nsec); + fmt_state_puts(state, " gen="); + fmt_state_printf(state, "%"PRIu64, self->gen); + fmt_state_puts(state, " data_version="); + fmt_state_printf(state, "%"PRIu64, self->data_version); + fmt_state_puts(state, " }"); +} + +static void lib9p_msg_Tsetattr_format(struct lib9p_msg_Tsetattr *self, struct fmt_state *state) { + fmt_state_puts(state, "Tsetattr {"); + fmt_state_puts(state, " tag="); + lib9p_tag_format(&self->tag, state); + fmt_state_puts(state, " fid="); + lib9p_fid_format(&self->fid, state); + fmt_state_puts(state, " valid="); + lib9p_setattr_format(&self->valid, state); + fmt_state_puts(state, " mode="); + lib9p_mode_format(&self->mode, state); + fmt_state_puts(state, " uid="); + lib9p_nuid_format(&self->uid, state); + fmt_state_puts(state, " gid="); + lib9p_nuid_format(&self->gid, state); + fmt_state_puts(state, " filesize="); + fmt_state_printf(state, "%"PRIu64, self->filesize); + fmt_state_puts(state, " atime_sec="); + fmt_state_printf(state, "%"PRIu64, self->atime_sec); + fmt_state_puts(state, " atime_nsec="); + fmt_state_printf(state, "%"PRIu64, self->atime_nsec); + fmt_state_puts(state, " mtime_sec="); + fmt_state_printf(state, "%"PRIu64, self->mtime_sec); + fmt_state_puts(state, " mtime_nsec="); + fmt_state_printf(state, "%"PRIu64, self->mtime_nsec); + fmt_state_puts(state, " }"); +} + +static void lib9p_msg_Rsetattr_format(struct lib9p_msg_Rsetattr *self, struct fmt_state *state) { + fmt_state_puts(state, "Rsetattr {"); + fmt_state_puts(state, " tag="); + lib9p_tag_format(&self->tag, state); + fmt_state_puts(state, " }"); +} + +static void lib9p_msg_Txattrwalk_format(struct lib9p_msg_Txattrwalk *self, struct fmt_state *state) { + fmt_state_puts(state, "Txattrwalk {"); + fmt_state_puts(state, " tag="); + lib9p_tag_format(&self->tag, state); + fmt_state_puts(state, " fid="); + lib9p_fid_format(&self->fid, state); + fmt_state_puts(state, " newfid="); + lib9p_fid_format(&self->newfid, state); + fmt_state_puts(state, " name="); + lib9p_s_format(&self->name, state); + fmt_state_puts(state, " }"); +} + +static void lib9p_msg_Rxattrwalk_format(struct lib9p_msg_Rxattrwalk *self, struct fmt_state *state) { + fmt_state_puts(state, "Rxattrwalk {"); + fmt_state_puts(state, " tag="); + lib9p_tag_format(&self->tag, state); + fmt_state_puts(state, " attr_size="); + fmt_state_printf(state, "%"PRIu64, self->attr_size); + fmt_state_puts(state, " }"); +} + +static void lib9p_msg_Txattrcreate_format(struct lib9p_msg_Txattrcreate *self, struct fmt_state *state) { + fmt_state_puts(state, "Txattrcreate {"); + fmt_state_puts(state, " tag="); + lib9p_tag_format(&self->tag, state); + fmt_state_puts(state, " fid="); + lib9p_fid_format(&self->fid, state); + fmt_state_puts(state, " name="); + lib9p_s_format(&self->name, state); + fmt_state_puts(state, " attr_size="); + fmt_state_printf(state, "%"PRIu64, self->attr_size); + fmt_state_puts(state, " flags="); + fmt_state_printf(state, "%"PRIu32, self->flags); + fmt_state_puts(state, " }"); +} + +static void lib9p_msg_Rxattrcreate_format(struct lib9p_msg_Rxattrcreate *self, struct fmt_state *state) { + fmt_state_puts(state, "Rxattrcreate {"); + fmt_state_puts(state, " tag="); + lib9p_tag_format(&self->tag, state); + fmt_state_puts(state, " }"); +} + +static void lib9p_msg_Treaddir_format(struct lib9p_msg_Treaddir *self, struct fmt_state *state) { + fmt_state_puts(state, "Treaddir {"); + fmt_state_puts(state, " tag="); + lib9p_tag_format(&self->tag, state); + fmt_state_puts(state, " fid="); + lib9p_fid_format(&self->fid, state); + fmt_state_puts(state, " offset="); + fmt_state_printf(state, "%"PRIu64, self->offset); + fmt_state_puts(state, " count="); + fmt_state_printf(state, "%"PRIu32, self->count); + fmt_state_puts(state, " }"); +} + +static void lib9p_msg_Rreaddir_format(struct lib9p_msg_Rreaddir *self, struct fmt_state *state) { + fmt_state_puts(state, "Rreaddir {"); + fmt_state_puts(state, " tag="); + lib9p_tag_format(&self->tag, state); + fmt_state_puts(state, " count="); + fmt_state_printf(state, "%"PRIu32, self->count); + if (is_valid_utf8_without_nul((uint8_t *)self->data, (size_t)self->count)) { +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat" +#pragma GCC diagnostic ignored "-Wformat-extra-args" + fmt_state_printf(state, " data=%.*q%s", + (int)(self->count < 50 ? self->count : 50), + (char *)self->data, + self->count < 50 ? "" : "..."); +#pragma GCC diagnostic pop + } else { + fmt_state_puts(state, " data=<bytedata>"); + } + fmt_state_puts(state, " }"); +} + +static void lib9p_msg_Tfsync_format(struct lib9p_msg_Tfsync *self, struct fmt_state *state) { + fmt_state_puts(state, "Tfsync {"); + fmt_state_puts(state, " tag="); + lib9p_tag_format(&self->tag, state); + fmt_state_puts(state, " fid="); + lib9p_fid_format(&self->fid, state); + fmt_state_puts(state, " datasync="); + lib9p_b4_format(&self->datasync, state); + fmt_state_puts(state, " }"); +} + +static void lib9p_msg_Rfsync_format(struct lib9p_msg_Rfsync *self, struct fmt_state *state) { + fmt_state_puts(state, "Rfsync {"); + fmt_state_puts(state, " tag="); + lib9p_tag_format(&self->tag, state); + fmt_state_puts(state, " }"); +} + +static void lib9p_msg_Tlock_format(struct lib9p_msg_Tlock *self, struct fmt_state *state) { + fmt_state_puts(state, "Tlock {"); + fmt_state_puts(state, " tag="); + lib9p_tag_format(&self->tag, state); + fmt_state_puts(state, " fid="); + lib9p_fid_format(&self->fid, state); + fmt_state_puts(state, " type="); + lib9p_lock_type_format(&self->type, state); + fmt_state_puts(state, " flags="); + lib9p_lock_flags_format(&self->flags, state); + fmt_state_puts(state, " start="); + fmt_state_printf(state, "%"PRIu64, self->start); + fmt_state_puts(state, " length="); + fmt_state_printf(state, "%"PRIu64, self->length); + fmt_state_puts(state, " proc_id="); + fmt_state_printf(state, "%"PRIu32, self->proc_id); + fmt_state_puts(state, " client_id="); + lib9p_s_format(&self->client_id, state); + fmt_state_puts(state, " }"); +} + +static void lib9p_msg_Rlock_format(struct lib9p_msg_Rlock *self, struct fmt_state *state) { + fmt_state_puts(state, "Rlock {"); + fmt_state_puts(state, " tag="); + lib9p_tag_format(&self->tag, state); + fmt_state_puts(state, " status="); + lib9p_lock_status_format(&self->status, state); + fmt_state_puts(state, " }"); +} + +static void lib9p_msg_Tgetlock_format(struct lib9p_msg_Tgetlock *self, struct fmt_state *state) { + fmt_state_puts(state, "Tgetlock {"); + fmt_state_puts(state, " tag="); + lib9p_tag_format(&self->tag, state); + fmt_state_puts(state, " fid="); + lib9p_fid_format(&self->fid, state); + fmt_state_puts(state, " type="); + lib9p_lock_type_format(&self->type, state); + fmt_state_puts(state, " start="); + fmt_state_printf(state, "%"PRIu64, self->start); + fmt_state_puts(state, " length="); + fmt_state_printf(state, "%"PRIu64, self->length); + fmt_state_puts(state, " proc_id="); + fmt_state_printf(state, "%"PRIu32, self->proc_id); + fmt_state_puts(state, " client_id="); + lib9p_s_format(&self->client_id, state); + fmt_state_puts(state, " }"); +} + +static void lib9p_msg_Rgetlock_format(struct lib9p_msg_Rgetlock *self, struct fmt_state *state) { + fmt_state_puts(state, "Rgetlock {"); + fmt_state_puts(state, " tag="); + lib9p_tag_format(&self->tag, state); + fmt_state_puts(state, " type="); + lib9p_lock_type_format(&self->type, state); + fmt_state_puts(state, " start="); + fmt_state_printf(state, "%"PRIu64, self->start); + fmt_state_puts(state, " length="); + fmt_state_printf(state, "%"PRIu64, self->length); + fmt_state_puts(state, " proc_id="); + fmt_state_printf(state, "%"PRIu32, self->proc_id); + fmt_state_puts(state, " client_id="); + lib9p_s_format(&self->client_id, state); + fmt_state_puts(state, " }"); +} + +static void lib9p_msg_Tlink_format(struct lib9p_msg_Tlink *self, struct fmt_state *state) { + fmt_state_puts(state, "Tlink {"); + fmt_state_puts(state, " tag="); + lib9p_tag_format(&self->tag, state); + fmt_state_puts(state, " dfid="); + lib9p_fid_format(&self->dfid, state); + fmt_state_puts(state, " fid="); + lib9p_fid_format(&self->fid, state); + fmt_state_puts(state, " name="); + lib9p_s_format(&self->name, state); + fmt_state_puts(state, " }"); +} + +static void lib9p_msg_Rlink_format(struct lib9p_msg_Rlink *self, struct fmt_state *state) { + fmt_state_puts(state, "Rlink {"); + fmt_state_puts(state, " tag="); + lib9p_tag_format(&self->tag, state); + fmt_state_puts(state, " }"); +} + +static void lib9p_msg_Tmkdir_format(struct lib9p_msg_Tmkdir *self, struct fmt_state *state) { + fmt_state_puts(state, "Tmkdir {"); + fmt_state_puts(state, " tag="); + lib9p_tag_format(&self->tag, state); + fmt_state_puts(state, " dfid="); + lib9p_fid_format(&self->dfid, state); + fmt_state_puts(state, " name="); + lib9p_s_format(&self->name, state); + fmt_state_puts(state, " mode="); + lib9p_mode_format(&self->mode, state); + fmt_state_puts(state, " gid="); + lib9p_nuid_format(&self->gid, state); + fmt_state_puts(state, " }"); +} + +static void lib9p_msg_Rmkdir_format(struct lib9p_msg_Rmkdir *self, struct fmt_state *state) { + fmt_state_puts(state, "Rmkdir {"); + fmt_state_puts(state, " tag="); + lib9p_tag_format(&self->tag, state); + fmt_state_puts(state, " qid="); + lib9p_qid_format(&self->qid, state); + fmt_state_puts(state, " }"); +} + +static void lib9p_msg_Trenameat_format(struct lib9p_msg_Trenameat *self, struct fmt_state *state) { + fmt_state_puts(state, "Trenameat {"); + fmt_state_puts(state, " tag="); + lib9p_tag_format(&self->tag, state); + fmt_state_puts(state, " olddirfid="); + lib9p_fid_format(&self->olddirfid, state); + fmt_state_puts(state, " oldname="); + lib9p_s_format(&self->oldname, state); + fmt_state_puts(state, " newdirfid="); + lib9p_fid_format(&self->newdirfid, state); + fmt_state_puts(state, " newname="); + lib9p_s_format(&self->newname, state); + fmt_state_puts(state, " }"); +} + +static void lib9p_msg_Rrenameat_format(struct lib9p_msg_Rrenameat *self, struct fmt_state *state) { + fmt_state_puts(state, "Rrenameat {"); + fmt_state_puts(state, " tag="); + lib9p_tag_format(&self->tag, state); + fmt_state_puts(state, " }"); +} + +static void lib9p_msg_Tunlinkat_format(struct lib9p_msg_Tunlinkat *self, struct fmt_state *state) { + fmt_state_puts(state, "Tunlinkat {"); + fmt_state_puts(state, " tag="); + lib9p_tag_format(&self->tag, state); + fmt_state_puts(state, " dirfd="); + lib9p_fid_format(&self->dirfd, state); + fmt_state_puts(state, " name="); + lib9p_s_format(&self->name, state); + fmt_state_puts(state, " flags="); + fmt_state_printf(state, "%"PRIu32, self->flags); + fmt_state_puts(state, " }"); +} + +static void lib9p_msg_Runlinkat_format(struct lib9p_msg_Runlinkat *self, struct fmt_state *state) { + fmt_state_puts(state, "Runlinkat {"); + fmt_state_puts(state, " tag="); + lib9p_tag_format(&self->tag, state); + fmt_state_puts(state, " }"); +} + +#endif /* CONFIG_9P_ENABLE_9P2000_L */ +#if CONFIG_9P_ENABLE_9P2000_e +static void lib9p_msg_Tsession_format(struct lib9p_msg_Tsession *self, struct fmt_state *state) { + fmt_state_puts(state, "Tsession {"); + fmt_state_puts(state, " tag="); + lib9p_tag_format(&self->tag, state); + fmt_state_puts(state, " key="); + fmt_state_printf(state, "%"PRIu64, self->key); + fmt_state_puts(state, " }"); +} + +static void lib9p_msg_Rsession_format(struct lib9p_msg_Rsession *self, struct fmt_state *state) { + fmt_state_puts(state, "Rsession {"); + fmt_state_puts(state, " tag="); + lib9p_tag_format(&self->tag, state); + fmt_state_puts(state, " }"); +} + +static void lib9p_msg_Tsread_format(struct lib9p_msg_Tsread *self, struct fmt_state *state) { + fmt_state_puts(state, "Tsread {"); + fmt_state_puts(state, " tag="); + lib9p_tag_format(&self->tag, state); + fmt_state_puts(state, " fid="); + fmt_state_printf(state, "%"PRIu32, self->fid); + fmt_state_puts(state, " nwname="); + fmt_state_printf(state, "%"PRIu16, self->nwname); + fmt_state_puts(state, " wname=["); + for (uint16_t i = 0; i < self->nwname; i++) { + if (i) + fmt_state_puts(state, ", "); + lib9p_s_format(&self->wname[i], state); + } + fmt_state_puts(state, " ]"); + fmt_state_puts(state, " }"); +} + +static void lib9p_msg_Rsread_format(struct lib9p_msg_Rsread *self, struct fmt_state *state) { + fmt_state_puts(state, "Rsread {"); + fmt_state_puts(state, " tag="); + lib9p_tag_format(&self->tag, state); + fmt_state_puts(state, " count="); + fmt_state_printf(state, "%"PRIu32, self->count); + if (is_valid_utf8_without_nul((uint8_t *)self->data, (size_t)self->count)) { +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat" +#pragma GCC diagnostic ignored "-Wformat-extra-args" + fmt_state_printf(state, " data=%.*q%s", + (int)(self->count < 50 ? self->count : 50), + (char *)self->data, + self->count < 50 ? "" : "..."); +#pragma GCC diagnostic pop + } else { + fmt_state_puts(state, " data=<bytedata>"); + } + fmt_state_puts(state, " }"); +} + +static void lib9p_msg_Tswrite_format(struct lib9p_msg_Tswrite *self, struct fmt_state *state) { + fmt_state_puts(state, "Tswrite {"); + fmt_state_puts(state, " tag="); + lib9p_tag_format(&self->tag, state); + fmt_state_puts(state, " fid="); + fmt_state_printf(state, "%"PRIu32, self->fid); + fmt_state_puts(state, " nwname="); + fmt_state_printf(state, "%"PRIu16, self->nwname); + fmt_state_puts(state, " wname=["); + for (uint16_t i = 0; i < self->nwname; i++) { + if (i) + fmt_state_puts(state, ", "); + lib9p_s_format(&self->wname[i], state); + } + fmt_state_puts(state, " ]"); + fmt_state_puts(state, " count="); + fmt_state_printf(state, "%"PRIu32, self->count); + if (is_valid_utf8_without_nul((uint8_t *)self->data, (size_t)self->count)) { +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat" +#pragma GCC diagnostic ignored "-Wformat-extra-args" + fmt_state_printf(state, " data=%.*q%s", + (int)(self->count < 50 ? self->count : 50), + (char *)self->data, + self->count < 50 ? "" : "..."); +#pragma GCC diagnostic pop + } else { + fmt_state_puts(state, " data=<bytedata>"); + } + fmt_state_puts(state, " }"); +} + +static void lib9p_msg_Rswrite_format(struct lib9p_msg_Rswrite *self, struct fmt_state *state) { + fmt_state_puts(state, "Rswrite {"); + fmt_state_puts(state, " tag="); + lib9p_tag_format(&self->tag, state); + fmt_state_puts(state, " count="); + fmt_state_printf(state, "%"PRIu32, self->count); + fmt_state_puts(state, " }"); +} +#endif /* CONFIG_9P_ENABLE_9P2000_e */ + +/* tables.h *******************************************************************/ + +const struct _lib9p_ver_tentry _lib9p_table_ver[LIB9P_VER_NUM] = { + [LIB9P_VER_unknown] = {.name="unknown", .min_msg_size=9}, +#if CONFIG_9P_ENABLE_9P2000 + [LIB9P_VER_9P2000] = {.name="9P2000", .min_msg_size=9}, +#endif /* CONFIG_9P_ENABLE_9P2000 */ +#if CONFIG_9P_ENABLE_9P2000_L + [LIB9P_VER_9P2000_L] = {.name="9P2000.L", .min_msg_size=9}, +#endif /* CONFIG_9P_ENABLE_9P2000_L */ +#if CONFIG_9P_ENABLE_9P2000_e + [LIB9P_VER_9P2000_e] = {.name="9P2000.e", .min_msg_size=9}, +#endif /* CONFIG_9P_ENABLE_9P2000_e */ +#if CONFIG_9P_ENABLE_9P2000_p9p + [LIB9P_VER_9P2000_p9p] = {.name="9P2000.p9p", .min_msg_size=9}, +#endif /* CONFIG_9P_ENABLE_9P2000_p9p */ +#if CONFIG_9P_ENABLE_9P2000_u + [LIB9P_VER_9P2000_u] = {.name="9P2000.u", .min_msg_size=13}, +#endif /* CONFIG_9P_ENABLE_9P2000_u */ +}; + +#define _MSG(typ) [LIB9P_TYP_##typ] = { \ + .name = #typ, \ + .box_as_fmt_formatter = (_box_as_fmt_formatter_fn_t)lo_box_lib9p_msg_##typ##_as_fmt_formatter, \ + } +const struct _lib9p_msg_tentry _lib9p_table_msg[LIB9P_VER_NUM][0x100] = { + [LIB9P_VER_unknown] = { _MSG(Tversion), _MSG(Rversion), - _NONMSG(0x66), - _NONMSG(0x67), - _NONMSG(0x68), - _NONMSG(0x69), - _NONMSG(0x6A), _MSG(Rerror), - _NONMSG(0x6C), - _NONMSG(0x6D), - _NONMSG(0x6E), - _NONMSG(0x6F), - _NONMSG(0x70), - _NONMSG(0x71), - _NONMSG(0x72), - _NONMSG(0x73), - _NONMSG(0x74), - _NONMSG(0x75), - _NONMSG(0x76), - _NONMSG(0x77), - _NONMSG(0x78), - _NONMSG(0x79), - _NONMSG(0x7A), - _NONMSG(0x7B), - _NONMSG(0x7C), - _NONMSG(0x7D), - _NONMSG(0x7E), - _NONMSG(0x7F), - _NONMSG(0x80), - _NONMSG(0x81), - _NONMSG(0x82), - _NONMSG(0x83), - _NONMSG(0x84), - _NONMSG(0x85), - _NONMSG(0x86), - _NONMSG(0x87), - _NONMSG(0x88), - _NONMSG(0x89), - _NONMSG(0x8A), - _NONMSG(0x8B), - _NONMSG(0x8C), - _NONMSG(0x8D), - _NONMSG(0x8E), - _NONMSG(0x8F), - _NONMSG(0x90), - _NONMSG(0x91), - _NONMSG(0x92), - _NONMSG(0x93), - _NONMSG(0x94), - _NONMSG(0x95), - _NONMSG(0x96), - _NONMSG(0x97), - _NONMSG(0x98), - _NONMSG(0x99), - _NONMSG(0x9A), - _NONMSG(0x9B), - _NONMSG(0x9C), - _NONMSG(0x9D), - _NONMSG(0x9E), - _NONMSG(0x9F), - _NONMSG(0xA0), - _NONMSG(0xA1), - _NONMSG(0xA2), - _NONMSG(0xA3), - _NONMSG(0xA4), - _NONMSG(0xA5), - _NONMSG(0xA6), - _NONMSG(0xA7), - _NONMSG(0xA8), - _NONMSG(0xA9), - _NONMSG(0xAA), - _NONMSG(0xAB), - _NONMSG(0xAC), - _NONMSG(0xAD), - _NONMSG(0xAE), - _NONMSG(0xAF), - _NONMSG(0xB0), - _NONMSG(0xB1), - _NONMSG(0xB2), - _NONMSG(0xB3), - _NONMSG(0xB4), - _NONMSG(0xB5), - _NONMSG(0xB6), - _NONMSG(0xB7), - _NONMSG(0xB8), - _NONMSG(0xB9), - _NONMSG(0xBA), - _NONMSG(0xBB), - _NONMSG(0xBC), - _NONMSG(0xBD), - _NONMSG(0xBE), - _NONMSG(0xBF), - _NONMSG(0xC0), - _NONMSG(0xC1), - _NONMSG(0xC2), - _NONMSG(0xC3), - _NONMSG(0xC4), - _NONMSG(0xC5), - _NONMSG(0xC6), - _NONMSG(0xC7), - _NONMSG(0xC8), - _NONMSG(0xC9), - _NONMSG(0xCA), - _NONMSG(0xCB), - _NONMSG(0xCC), - _NONMSG(0xCD), - _NONMSG(0xCE), - _NONMSG(0xCF), - _NONMSG(0xD0), - _NONMSG(0xD1), - _NONMSG(0xD2), - _NONMSG(0xD3), - _NONMSG(0xD4), - _NONMSG(0xD5), - _NONMSG(0xD6), - _NONMSG(0xD7), - _NONMSG(0xD8), - _NONMSG(0xD9), - _NONMSG(0xDA), - _NONMSG(0xDB), - _NONMSG(0xDC), - _NONMSG(0xDD), - _NONMSG(0xDE), - _NONMSG(0xDF), - _NONMSG(0xE0), - _NONMSG(0xE1), - _NONMSG(0xE2), - _NONMSG(0xE3), - _NONMSG(0xE4), - _NONMSG(0xE5), - _NONMSG(0xE6), - _NONMSG(0xE7), - _NONMSG(0xE8), - _NONMSG(0xE9), - _NONMSG(0xEA), - _NONMSG(0xEB), - _NONMSG(0xEC), - _NONMSG(0xED), - _NONMSG(0xEE), - _NONMSG(0xEF), - _NONMSG(0xF0), - _NONMSG(0xF1), - _NONMSG(0xF2), - _NONMSG(0xF3), - _NONMSG(0xF4), - _NONMSG(0xF5), - _NONMSG(0xF6), - _NONMSG(0xF7), - _NONMSG(0xF8), - _NONMSG(0xF9), - _NONMSG(0xFA), - _NONMSG(0xFB), - _NONMSG(0xFC), - _NONMSG(0xFD), - _NONMSG(0xFE), - _NONMSG(0xFF), - }}, -#if defined(CONFIG_9P_ENABLE_9P2000) - [LIB9P_VER_9P2000] = { .msgs = { - _NONMSG(0x00), - _NONMSG(0x01), - _NONMSG(0x02), - _NONMSG(0x03), - _NONMSG(0x04), - _NONMSG(0x05), - _NONMSG(0x06), - _NONMSG(0x07), - _NONMSG(0x08), - _NONMSG(0x09), - _NONMSG(0x0A), - _NONMSG(0x0B), - _NONMSG(0x0C), - _NONMSG(0x0D), - _NONMSG(0x0E), - _NONMSG(0x0F), - _NONMSG(0x10), - _NONMSG(0x11), - _NONMSG(0x12), - _NONMSG(0x13), - _NONMSG(0x14), - _NONMSG(0x15), - _NONMSG(0x16), - _NONMSG(0x17), - _NONMSG(0x18), - _NONMSG(0x19), - _NONMSG(0x1A), - _NONMSG(0x1B), - _NONMSG(0x1C), - _NONMSG(0x1D), - _NONMSG(0x1E), - _NONMSG(0x1F), - _NONMSG(0x20), - _NONMSG(0x21), - _NONMSG(0x22), - _NONMSG(0x23), - _NONMSG(0x24), - _NONMSG(0x25), - _NONMSG(0x26), - _NONMSG(0x27), - _NONMSG(0x28), - _NONMSG(0x29), - _NONMSG(0x2A), - _NONMSG(0x2B), - _NONMSG(0x2C), - _NONMSG(0x2D), - _NONMSG(0x2E), - _NONMSG(0x2F), - _NONMSG(0x30), - _NONMSG(0x31), - _NONMSG(0x32), - _NONMSG(0x33), - _NONMSG(0x34), - _NONMSG(0x35), - _NONMSG(0x36), - _NONMSG(0x37), - _NONMSG(0x38), - _NONMSG(0x39), - _NONMSG(0x3A), - _NONMSG(0x3B), - _NONMSG(0x3C), - _NONMSG(0x3D), - _NONMSG(0x3E), - _NONMSG(0x3F), - _NONMSG(0x40), - _NONMSG(0x41), - _NONMSG(0x42), - _NONMSG(0x43), - _NONMSG(0x44), - _NONMSG(0x45), - _NONMSG(0x46), - _NONMSG(0x47), - _NONMSG(0x48), - _NONMSG(0x49), - _NONMSG(0x4A), - _NONMSG(0x4B), - _NONMSG(0x4C), - _NONMSG(0x4D), - _NONMSG(0x4E), - _NONMSG(0x4F), - _NONMSG(0x50), - _NONMSG(0x51), - _NONMSG(0x52), - _NONMSG(0x53), - _NONMSG(0x54), - _NONMSG(0x55), - _NONMSG(0x56), - _NONMSG(0x57), - _NONMSG(0x58), - _NONMSG(0x59), - _NONMSG(0x5A), - _NONMSG(0x5B), - _NONMSG(0x5C), - _NONMSG(0x5D), - _NONMSG(0x5E), - _NONMSG(0x5F), - _NONMSG(0x60), - _NONMSG(0x61), - _NONMSG(0x62), - _NONMSG(0x63), + }, +#if CONFIG_9P_ENABLE_9P2000 + [LIB9P_VER_9P2000] = { _MSG(Tversion), _MSG(Rversion), _MSG(Tauth), _MSG(Rauth), _MSG(Tattach), _MSG(Rattach), - _NONMSG(0x6A), _MSG(Rerror), _MSG(Tflush), _MSG(Rflush), @@ -2180,245 +7451,78 @@ struct _table_version _lib9p_versions[LIB9P_VER_NUM] = { _MSG(Rstat), _MSG(Twstat), _MSG(Rwstat), - _NONMSG(0x80), - _NONMSG(0x81), - _NONMSG(0x82), - _NONMSG(0x83), - _NONMSG(0x84), - _NONMSG(0x85), - _NONMSG(0x86), - _NONMSG(0x87), - _NONMSG(0x88), - _NONMSG(0x89), - _NONMSG(0x8A), - _NONMSG(0x8B), - _NONMSG(0x8C), - _NONMSG(0x8D), - _NONMSG(0x8E), - _NONMSG(0x8F), - _NONMSG(0x90), - _NONMSG(0x91), - _NONMSG(0x92), - _NONMSG(0x93), - _NONMSG(0x94), - _NONMSG(0x95), - _NONMSG(0x96), - _NONMSG(0x97), - _NONMSG(0x98), - _NONMSG(0x99), - _NONMSG(0x9A), - _NONMSG(0x9B), - _NONMSG(0x9C), - _NONMSG(0x9D), - _NONMSG(0x9E), - _NONMSG(0x9F), - _NONMSG(0xA0), - _NONMSG(0xA1), - _NONMSG(0xA2), - _NONMSG(0xA3), - _NONMSG(0xA4), - _NONMSG(0xA5), - _NONMSG(0xA6), - _NONMSG(0xA7), - _NONMSG(0xA8), - _NONMSG(0xA9), - _NONMSG(0xAA), - _NONMSG(0xAB), - _NONMSG(0xAC), - _NONMSG(0xAD), - _NONMSG(0xAE), - _NONMSG(0xAF), - _NONMSG(0xB0), - _NONMSG(0xB1), - _NONMSG(0xB2), - _NONMSG(0xB3), - _NONMSG(0xB4), - _NONMSG(0xB5), - _NONMSG(0xB6), - _NONMSG(0xB7), - _NONMSG(0xB8), - _NONMSG(0xB9), - _NONMSG(0xBA), - _NONMSG(0xBB), - _NONMSG(0xBC), - _NONMSG(0xBD), - _NONMSG(0xBE), - _NONMSG(0xBF), - _NONMSG(0xC0), - _NONMSG(0xC1), - _NONMSG(0xC2), - _NONMSG(0xC3), - _NONMSG(0xC4), - _NONMSG(0xC5), - _NONMSG(0xC6), - _NONMSG(0xC7), - _NONMSG(0xC8), - _NONMSG(0xC9), - _NONMSG(0xCA), - _NONMSG(0xCB), - _NONMSG(0xCC), - _NONMSG(0xCD), - _NONMSG(0xCE), - _NONMSG(0xCF), - _NONMSG(0xD0), - _NONMSG(0xD1), - _NONMSG(0xD2), - _NONMSG(0xD3), - _NONMSG(0xD4), - _NONMSG(0xD5), - _NONMSG(0xD6), - _NONMSG(0xD7), - _NONMSG(0xD8), - _NONMSG(0xD9), - _NONMSG(0xDA), - _NONMSG(0xDB), - _NONMSG(0xDC), - _NONMSG(0xDD), - _NONMSG(0xDE), - _NONMSG(0xDF), - _NONMSG(0xE0), - _NONMSG(0xE1), - _NONMSG(0xE2), - _NONMSG(0xE3), - _NONMSG(0xE4), - _NONMSG(0xE5), - _NONMSG(0xE6), - _NONMSG(0xE7), - _NONMSG(0xE8), - _NONMSG(0xE9), - _NONMSG(0xEA), - _NONMSG(0xEB), - _NONMSG(0xEC), - _NONMSG(0xED), - _NONMSG(0xEE), - _NONMSG(0xEF), - _NONMSG(0xF0), - _NONMSG(0xF1), - _NONMSG(0xF2), - _NONMSG(0xF3), - _NONMSG(0xF4), - _NONMSG(0xF5), - _NONMSG(0xF6), - _NONMSG(0xF7), - _NONMSG(0xF8), - _NONMSG(0xF9), - _NONMSG(0xFA), - _NONMSG(0xFB), - _NONMSG(0xFC), - _NONMSG(0xFD), - _NONMSG(0xFE), - _NONMSG(0xFF), - }}, -#endif /* defined(CONFIG_9P_ENABLE_9P2000) */ -#if defined(CONFIG_9P_ENABLE_9P2000_e) - [LIB9P_VER_9P2000_e] = { .msgs = { - _NONMSG(0x00), - _NONMSG(0x01), - _NONMSG(0x02), - _NONMSG(0x03), - _NONMSG(0x04), - _NONMSG(0x05), - _NONMSG(0x06), - _NONMSG(0x07), - _NONMSG(0x08), - _NONMSG(0x09), - _NONMSG(0x0A), - _NONMSG(0x0B), - _NONMSG(0x0C), - _NONMSG(0x0D), - _NONMSG(0x0E), - _NONMSG(0x0F), - _NONMSG(0x10), - _NONMSG(0x11), - _NONMSG(0x12), - _NONMSG(0x13), - _NONMSG(0x14), - _NONMSG(0x15), - _NONMSG(0x16), - _NONMSG(0x17), - _NONMSG(0x18), - _NONMSG(0x19), - _NONMSG(0x1A), - _NONMSG(0x1B), - _NONMSG(0x1C), - _NONMSG(0x1D), - _NONMSG(0x1E), - _NONMSG(0x1F), - _NONMSG(0x20), - _NONMSG(0x21), - _NONMSG(0x22), - _NONMSG(0x23), - _NONMSG(0x24), - _NONMSG(0x25), - _NONMSG(0x26), - _NONMSG(0x27), - _NONMSG(0x28), - _NONMSG(0x29), - _NONMSG(0x2A), - _NONMSG(0x2B), - _NONMSG(0x2C), - _NONMSG(0x2D), - _NONMSG(0x2E), - _NONMSG(0x2F), - _NONMSG(0x30), - _NONMSG(0x31), - _NONMSG(0x32), - _NONMSG(0x33), - _NONMSG(0x34), - _NONMSG(0x35), - _NONMSG(0x36), - _NONMSG(0x37), - _NONMSG(0x38), - _NONMSG(0x39), - _NONMSG(0x3A), - _NONMSG(0x3B), - _NONMSG(0x3C), - _NONMSG(0x3D), - _NONMSG(0x3E), - _NONMSG(0x3F), - _NONMSG(0x40), - _NONMSG(0x41), - _NONMSG(0x42), - _NONMSG(0x43), - _NONMSG(0x44), - _NONMSG(0x45), - _NONMSG(0x46), - _NONMSG(0x47), - _NONMSG(0x48), - _NONMSG(0x49), - _NONMSG(0x4A), - _NONMSG(0x4B), - _NONMSG(0x4C), - _NONMSG(0x4D), - _NONMSG(0x4E), - _NONMSG(0x4F), - _NONMSG(0x50), - _NONMSG(0x51), - _NONMSG(0x52), - _NONMSG(0x53), - _NONMSG(0x54), - _NONMSG(0x55), - _NONMSG(0x56), - _NONMSG(0x57), - _NONMSG(0x58), - _NONMSG(0x59), - _NONMSG(0x5A), - _NONMSG(0x5B), - _NONMSG(0x5C), - _NONMSG(0x5D), - _NONMSG(0x5E), - _NONMSG(0x5F), - _NONMSG(0x60), - _NONMSG(0x61), - _NONMSG(0x62), - _NONMSG(0x63), + }, +#endif /* CONFIG_9P_ENABLE_9P2000 */ +#if CONFIG_9P_ENABLE_9P2000_L + [LIB9P_VER_9P2000_L] = { + _MSG(Rlerror), + _MSG(Tstatfs), + _MSG(Rstatfs), + _MSG(Tlopen), + _MSG(Rlopen), + _MSG(Tlcreate), + _MSG(Rlcreate), + _MSG(Tsymlink), + _MSG(Rsymlink), + _MSG(Tmknod), + _MSG(Rmknod), + _MSG(Trename), + _MSG(Rrename), + _MSG(Treadlink), + _MSG(Rreadlink), + _MSG(Tgetattr), + _MSG(Rgetattr), + _MSG(Tsetattr), + _MSG(Rsetattr), + _MSG(Txattrwalk), + _MSG(Rxattrwalk), + _MSG(Txattrcreate), + _MSG(Rxattrcreate), + _MSG(Treaddir), + _MSG(Rreaddir), + _MSG(Tfsync), + _MSG(Rfsync), + _MSG(Tlock), + _MSG(Rlock), + _MSG(Tgetlock), + _MSG(Rgetlock), + _MSG(Tlink), + _MSG(Rlink), + _MSG(Tmkdir), + _MSG(Rmkdir), + _MSG(Trenameat), + _MSG(Rrenameat), + _MSG(Tunlinkat), + _MSG(Runlinkat), + _MSG(Tversion), + _MSG(Rversion), + _MSG(Tauth), + _MSG(Rauth), + _MSG(Tattach), + _MSG(Rattach), + _MSG(Rerror), + _MSG(Tflush), + _MSG(Rflush), + _MSG(Twalk), + _MSG(Rwalk), + _MSG(Tread), + _MSG(Rread), + _MSG(Twrite), + _MSG(Rwrite), + _MSG(Tclunk), + _MSG(Rclunk), + _MSG(Tremove), + _MSG(Rremove), + }, +#endif /* CONFIG_9P_ENABLE_9P2000_L */ +#if CONFIG_9P_ENABLE_9P2000_e + [LIB9P_VER_9P2000_e] = { _MSG(Tversion), _MSG(Rversion), _MSG(Tauth), _MSG(Rauth), _MSG(Tattach), _MSG(Rattach), - _NONMSG(0x6A), _MSG(Rerror), _MSG(Tflush), _MSG(Rflush), @@ -2440,245 +7544,55 @@ struct _table_version _lib9p_versions[LIB9P_VER_NUM] = { _MSG(Rstat), _MSG(Twstat), _MSG(Rwstat), - _NONMSG(0x80), - _NONMSG(0x81), - _NONMSG(0x82), - _NONMSG(0x83), - _NONMSG(0x84), - _NONMSG(0x85), - _NONMSG(0x86), - _NONMSG(0x87), - _NONMSG(0x88), - _NONMSG(0x89), - _NONMSG(0x8A), - _NONMSG(0x8B), - _NONMSG(0x8C), - _NONMSG(0x8D), - _NONMSG(0x8E), - _NONMSG(0x8F), - _NONMSG(0x90), - _NONMSG(0x91), - _NONMSG(0x92), - _NONMSG(0x93), - _NONMSG(0x94), - _NONMSG(0x95), _MSG(Tsession), _MSG(Rsession), _MSG(Tsread), _MSG(Rsread), _MSG(Tswrite), _MSG(Rswrite), - _NONMSG(0x9C), - _NONMSG(0x9D), - _NONMSG(0x9E), - _NONMSG(0x9F), - _NONMSG(0xA0), - _NONMSG(0xA1), - _NONMSG(0xA2), - _NONMSG(0xA3), - _NONMSG(0xA4), - _NONMSG(0xA5), - _NONMSG(0xA6), - _NONMSG(0xA7), - _NONMSG(0xA8), - _NONMSG(0xA9), - _NONMSG(0xAA), - _NONMSG(0xAB), - _NONMSG(0xAC), - _NONMSG(0xAD), - _NONMSG(0xAE), - _NONMSG(0xAF), - _NONMSG(0xB0), - _NONMSG(0xB1), - _NONMSG(0xB2), - _NONMSG(0xB3), - _NONMSG(0xB4), - _NONMSG(0xB5), - _NONMSG(0xB6), - _NONMSG(0xB7), - _NONMSG(0xB8), - _NONMSG(0xB9), - _NONMSG(0xBA), - _NONMSG(0xBB), - _NONMSG(0xBC), - _NONMSG(0xBD), - _NONMSG(0xBE), - _NONMSG(0xBF), - _NONMSG(0xC0), - _NONMSG(0xC1), - _NONMSG(0xC2), - _NONMSG(0xC3), - _NONMSG(0xC4), - _NONMSG(0xC5), - _NONMSG(0xC6), - _NONMSG(0xC7), - _NONMSG(0xC8), - _NONMSG(0xC9), - _NONMSG(0xCA), - _NONMSG(0xCB), - _NONMSG(0xCC), - _NONMSG(0xCD), - _NONMSG(0xCE), - _NONMSG(0xCF), - _NONMSG(0xD0), - _NONMSG(0xD1), - _NONMSG(0xD2), - _NONMSG(0xD3), - _NONMSG(0xD4), - _NONMSG(0xD5), - _NONMSG(0xD6), - _NONMSG(0xD7), - _NONMSG(0xD8), - _NONMSG(0xD9), - _NONMSG(0xDA), - _NONMSG(0xDB), - _NONMSG(0xDC), - _NONMSG(0xDD), - _NONMSG(0xDE), - _NONMSG(0xDF), - _NONMSG(0xE0), - _NONMSG(0xE1), - _NONMSG(0xE2), - _NONMSG(0xE3), - _NONMSG(0xE4), - _NONMSG(0xE5), - _NONMSG(0xE6), - _NONMSG(0xE7), - _NONMSG(0xE8), - _NONMSG(0xE9), - _NONMSG(0xEA), - _NONMSG(0xEB), - _NONMSG(0xEC), - _NONMSG(0xED), - _NONMSG(0xEE), - _NONMSG(0xEF), - _NONMSG(0xF0), - _NONMSG(0xF1), - _NONMSG(0xF2), - _NONMSG(0xF3), - _NONMSG(0xF4), - _NONMSG(0xF5), - _NONMSG(0xF6), - _NONMSG(0xF7), - _NONMSG(0xF8), - _NONMSG(0xF9), - _NONMSG(0xFA), - _NONMSG(0xFB), - _NONMSG(0xFC), - _NONMSG(0xFD), - _NONMSG(0xFE), - _NONMSG(0xFF), - }}, -#endif /* defined(CONFIG_9P_ENABLE_9P2000_e) */ -#if defined(CONFIG_9P_ENABLE_9P2000_u) - [LIB9P_VER_9P2000_u] = { .msgs = { - _NONMSG(0x00), - _NONMSG(0x01), - _NONMSG(0x02), - _NONMSG(0x03), - _NONMSG(0x04), - _NONMSG(0x05), - _NONMSG(0x06), - _NONMSG(0x07), - _NONMSG(0x08), - _NONMSG(0x09), - _NONMSG(0x0A), - _NONMSG(0x0B), - _NONMSG(0x0C), - _NONMSG(0x0D), - _NONMSG(0x0E), - _NONMSG(0x0F), - _NONMSG(0x10), - _NONMSG(0x11), - _NONMSG(0x12), - _NONMSG(0x13), - _NONMSG(0x14), - _NONMSG(0x15), - _NONMSG(0x16), - _NONMSG(0x17), - _NONMSG(0x18), - _NONMSG(0x19), - _NONMSG(0x1A), - _NONMSG(0x1B), - _NONMSG(0x1C), - _NONMSG(0x1D), - _NONMSG(0x1E), - _NONMSG(0x1F), - _NONMSG(0x20), - _NONMSG(0x21), - _NONMSG(0x22), - _NONMSG(0x23), - _NONMSG(0x24), - _NONMSG(0x25), - _NONMSG(0x26), - _NONMSG(0x27), - _NONMSG(0x28), - _NONMSG(0x29), - _NONMSG(0x2A), - _NONMSG(0x2B), - _NONMSG(0x2C), - _NONMSG(0x2D), - _NONMSG(0x2E), - _NONMSG(0x2F), - _NONMSG(0x30), - _NONMSG(0x31), - _NONMSG(0x32), - _NONMSG(0x33), - _NONMSG(0x34), - _NONMSG(0x35), - _NONMSG(0x36), - _NONMSG(0x37), - _NONMSG(0x38), - _NONMSG(0x39), - _NONMSG(0x3A), - _NONMSG(0x3B), - _NONMSG(0x3C), - _NONMSG(0x3D), - _NONMSG(0x3E), - _NONMSG(0x3F), - _NONMSG(0x40), - _NONMSG(0x41), - _NONMSG(0x42), - _NONMSG(0x43), - _NONMSG(0x44), - _NONMSG(0x45), - _NONMSG(0x46), - _NONMSG(0x47), - _NONMSG(0x48), - _NONMSG(0x49), - _NONMSG(0x4A), - _NONMSG(0x4B), - _NONMSG(0x4C), - _NONMSG(0x4D), - _NONMSG(0x4E), - _NONMSG(0x4F), - _NONMSG(0x50), - _NONMSG(0x51), - _NONMSG(0x52), - _NONMSG(0x53), - _NONMSG(0x54), - _NONMSG(0x55), - _NONMSG(0x56), - _NONMSG(0x57), - _NONMSG(0x58), - _NONMSG(0x59), - _NONMSG(0x5A), - _NONMSG(0x5B), - _NONMSG(0x5C), - _NONMSG(0x5D), - _NONMSG(0x5E), - _NONMSG(0x5F), - _NONMSG(0x60), - _NONMSG(0x61), - _NONMSG(0x62), - _NONMSG(0x63), + }, +#endif /* CONFIG_9P_ENABLE_9P2000_e */ +#if CONFIG_9P_ENABLE_9P2000_p9p + [LIB9P_VER_9P2000_p9p] = { + _MSG(Topenfd), + _MSG(Ropenfd), + _MSG(Tversion), + _MSG(Rversion), + _MSG(Tauth), + _MSG(Rauth), + _MSG(Tattach), + _MSG(Rattach), + _MSG(Rerror), + _MSG(Tflush), + _MSG(Rflush), + _MSG(Twalk), + _MSG(Rwalk), + _MSG(Topen), + _MSG(Ropen), + _MSG(Tcreate), + _MSG(Rcreate), + _MSG(Tread), + _MSG(Rread), + _MSG(Twrite), + _MSG(Rwrite), + _MSG(Tclunk), + _MSG(Rclunk), + _MSG(Tremove), + _MSG(Rremove), + _MSG(Tstat), + _MSG(Rstat), + _MSG(Twstat), + _MSG(Rwstat), + }, +#endif /* CONFIG_9P_ENABLE_9P2000_p9p */ +#if CONFIG_9P_ENABLE_9P2000_u + [LIB9P_VER_9P2000_u] = { _MSG(Tversion), _MSG(Rversion), _MSG(Tauth), _MSG(Rauth), _MSG(Tattach), _MSG(Rattach), - _NONMSG(0x6A), _MSG(Rerror), _MSG(Tflush), _MSG(Rflush), @@ -2700,144 +7614,478 @@ struct _table_version _lib9p_versions[LIB9P_VER_NUM] = { _MSG(Rstat), _MSG(Twstat), _MSG(Rwstat), - _NONMSG(0x80), - _NONMSG(0x81), - _NONMSG(0x82), - _NONMSG(0x83), - _NONMSG(0x84), - _NONMSG(0x85), - _NONMSG(0x86), - _NONMSG(0x87), - _NONMSG(0x88), - _NONMSG(0x89), - _NONMSG(0x8A), - _NONMSG(0x8B), - _NONMSG(0x8C), - _NONMSG(0x8D), - _NONMSG(0x8E), - _NONMSG(0x8F), - _NONMSG(0x90), - _NONMSG(0x91), - _NONMSG(0x92), - _NONMSG(0x93), - _NONMSG(0x94), - _NONMSG(0x95), - _NONMSG(0x96), - _NONMSG(0x97), - _NONMSG(0x98), - _NONMSG(0x99), - _NONMSG(0x9A), - _NONMSG(0x9B), - _NONMSG(0x9C), - _NONMSG(0x9D), - _NONMSG(0x9E), - _NONMSG(0x9F), - _NONMSG(0xA0), - _NONMSG(0xA1), - _NONMSG(0xA2), - _NONMSG(0xA3), - _NONMSG(0xA4), - _NONMSG(0xA5), - _NONMSG(0xA6), - _NONMSG(0xA7), - _NONMSG(0xA8), - _NONMSG(0xA9), - _NONMSG(0xAA), - _NONMSG(0xAB), - _NONMSG(0xAC), - _NONMSG(0xAD), - _NONMSG(0xAE), - _NONMSG(0xAF), - _NONMSG(0xB0), - _NONMSG(0xB1), - _NONMSG(0xB2), - _NONMSG(0xB3), - _NONMSG(0xB4), - _NONMSG(0xB5), - _NONMSG(0xB6), - _NONMSG(0xB7), - _NONMSG(0xB8), - _NONMSG(0xB9), - _NONMSG(0xBA), - _NONMSG(0xBB), - _NONMSG(0xBC), - _NONMSG(0xBD), - _NONMSG(0xBE), - _NONMSG(0xBF), - _NONMSG(0xC0), - _NONMSG(0xC1), - _NONMSG(0xC2), - _NONMSG(0xC3), - _NONMSG(0xC4), - _NONMSG(0xC5), - _NONMSG(0xC6), - _NONMSG(0xC7), - _NONMSG(0xC8), - _NONMSG(0xC9), - _NONMSG(0xCA), - _NONMSG(0xCB), - _NONMSG(0xCC), - _NONMSG(0xCD), - _NONMSG(0xCE), - _NONMSG(0xCF), - _NONMSG(0xD0), - _NONMSG(0xD1), - _NONMSG(0xD2), - _NONMSG(0xD3), - _NONMSG(0xD4), - _NONMSG(0xD5), - _NONMSG(0xD6), - _NONMSG(0xD7), - _NONMSG(0xD8), - _NONMSG(0xD9), - _NONMSG(0xDA), - _NONMSG(0xDB), - _NONMSG(0xDC), - _NONMSG(0xDD), - _NONMSG(0xDE), - _NONMSG(0xDF), - _NONMSG(0xE0), - _NONMSG(0xE1), - _NONMSG(0xE2), - _NONMSG(0xE3), - _NONMSG(0xE4), - _NONMSG(0xE5), - _NONMSG(0xE6), - _NONMSG(0xE7), - _NONMSG(0xE8), - _NONMSG(0xE9), - _NONMSG(0xEA), - _NONMSG(0xEB), - _NONMSG(0xEC), - _NONMSG(0xED), - _NONMSG(0xEE), - _NONMSG(0xEF), - _NONMSG(0xF0), - _NONMSG(0xF1), - _NONMSG(0xF2), - _NONMSG(0xF3), - _NONMSG(0xF4), - _NONMSG(0xF5), - _NONMSG(0xF6), - _NONMSG(0xF7), - _NONMSG(0xF8), - _NONMSG(0xF9), - _NONMSG(0xFA), - _NONMSG(0xFB), - _NONMSG(0xFC), - _NONMSG(0xFD), - _NONMSG(0xFE), - _NONMSG(0xFF), - }}, -#endif /* defined(CONFIG_9P_ENABLE_9P2000_u) */ + }, +#endif /* CONFIG_9P_ENABLE_9P2000_u */ +}; + +#define _MSG_RECV(typ) [LIB9P_TYP_##typ/2] = { \ + .validate = validate_##typ, \ + .unmarshal = (_unmarshal_fn_t)unmarshal_##typ, \ + } +#define _MSG_SEND(typ) [LIB9P_TYP_##typ/2] = { \ + .marshal = (_marshal_fn_t)marshal_##typ, \ + } + +const struct _lib9p_recv_tentry _lib9p_table_Tmsg_recv[LIB9P_VER_NUM][0x80] = { + [LIB9P_VER_unknown] = { + _MSG_RECV(Tversion), + }, +#if CONFIG_9P_ENABLE_9P2000 + [LIB9P_VER_9P2000] = { + _MSG_RECV(Tversion), + _MSG_RECV(Tauth), + _MSG_RECV(Tattach), + _MSG_RECV(Tflush), + _MSG_RECV(Twalk), + _MSG_RECV(Topen), + _MSG_RECV(Tcreate), + _MSG_RECV(Tread), + _MSG_RECV(Twrite), + _MSG_RECV(Tclunk), + _MSG_RECV(Tremove), + _MSG_RECV(Tstat), + _MSG_RECV(Twstat), + }, +#endif /* CONFIG_9P_ENABLE_9P2000 */ +#if CONFIG_9P_ENABLE_9P2000_L + [LIB9P_VER_9P2000_L] = { + _MSG_RECV(Tstatfs), + _MSG_RECV(Tlopen), + _MSG_RECV(Tlcreate), + _MSG_RECV(Tsymlink), + _MSG_RECV(Tmknod), + _MSG_RECV(Trename), + _MSG_RECV(Treadlink), + _MSG_RECV(Tgetattr), + _MSG_RECV(Tsetattr), + _MSG_RECV(Txattrwalk), + _MSG_RECV(Txattrcreate), + _MSG_RECV(Treaddir), + _MSG_RECV(Tfsync), + _MSG_RECV(Tlock), + _MSG_RECV(Tgetlock), + _MSG_RECV(Tlink), + _MSG_RECV(Tmkdir), + _MSG_RECV(Trenameat), + _MSG_RECV(Tunlinkat), + _MSG_RECV(Tversion), + _MSG_RECV(Tauth), + _MSG_RECV(Tattach), + _MSG_RECV(Tflush), + _MSG_RECV(Twalk), + _MSG_RECV(Tread), + _MSG_RECV(Twrite), + _MSG_RECV(Tclunk), + _MSG_RECV(Tremove), + }, +#endif /* CONFIG_9P_ENABLE_9P2000_L */ +#if CONFIG_9P_ENABLE_9P2000_e + [LIB9P_VER_9P2000_e] = { + _MSG_RECV(Tversion), + _MSG_RECV(Tauth), + _MSG_RECV(Tattach), + _MSG_RECV(Tflush), + _MSG_RECV(Twalk), + _MSG_RECV(Topen), + _MSG_RECV(Tcreate), + _MSG_RECV(Tread), + _MSG_RECV(Twrite), + _MSG_RECV(Tclunk), + _MSG_RECV(Tremove), + _MSG_RECV(Tstat), + _MSG_RECV(Twstat), + _MSG_RECV(Tsession), + _MSG_RECV(Tsread), + _MSG_RECV(Tswrite), + }, +#endif /* CONFIG_9P_ENABLE_9P2000_e */ +#if CONFIG_9P_ENABLE_9P2000_p9p + [LIB9P_VER_9P2000_p9p] = { + _MSG_RECV(Topenfd), + _MSG_RECV(Tversion), + _MSG_RECV(Tauth), + _MSG_RECV(Tattach), + _MSG_RECV(Tflush), + _MSG_RECV(Twalk), + _MSG_RECV(Topen), + _MSG_RECV(Tcreate), + _MSG_RECV(Tread), + _MSG_RECV(Twrite), + _MSG_RECV(Tclunk), + _MSG_RECV(Tremove), + _MSG_RECV(Tstat), + _MSG_RECV(Twstat), + }, +#endif /* CONFIG_9P_ENABLE_9P2000_p9p */ +#if CONFIG_9P_ENABLE_9P2000_u + [LIB9P_VER_9P2000_u] = { + _MSG_RECV(Tversion), + _MSG_RECV(Tauth), + _MSG_RECV(Tattach), + _MSG_RECV(Tflush), + _MSG_RECV(Twalk), + _MSG_RECV(Topen), + _MSG_RECV(Tcreate), + _MSG_RECV(Tread), + _MSG_RECV(Twrite), + _MSG_RECV(Tclunk), + _MSG_RECV(Tremove), + _MSG_RECV(Tstat), + _MSG_RECV(Twstat), + }, +#endif /* CONFIG_9P_ENABLE_9P2000_u */ +}; + +const struct _lib9p_recv_tentry _lib9p_table_Rmsg_recv[LIB9P_VER_NUM][0x80] = { + [LIB9P_VER_unknown] = { + _MSG_RECV(Rversion), + _MSG_RECV(Rerror), + }, +#if CONFIG_9P_ENABLE_9P2000 + [LIB9P_VER_9P2000] = { + _MSG_RECV(Rversion), + _MSG_RECV(Rauth), + _MSG_RECV(Rattach), + _MSG_RECV(Rerror), + _MSG_RECV(Rflush), + _MSG_RECV(Rwalk), + _MSG_RECV(Ropen), + _MSG_RECV(Rcreate), + _MSG_RECV(Rread), + _MSG_RECV(Rwrite), + _MSG_RECV(Rclunk), + _MSG_RECV(Rremove), + _MSG_RECV(Rstat), + _MSG_RECV(Rwstat), + }, +#endif /* CONFIG_9P_ENABLE_9P2000 */ +#if CONFIG_9P_ENABLE_9P2000_L + [LIB9P_VER_9P2000_L] = { + _MSG_RECV(Rlerror), + _MSG_RECV(Rstatfs), + _MSG_RECV(Rlopen), + _MSG_RECV(Rlcreate), + _MSG_RECV(Rsymlink), + _MSG_RECV(Rmknod), + _MSG_RECV(Rrename), + _MSG_RECV(Rreadlink), + _MSG_RECV(Rgetattr), + _MSG_RECV(Rsetattr), + _MSG_RECV(Rxattrwalk), + _MSG_RECV(Rxattrcreate), + _MSG_RECV(Rreaddir), + _MSG_RECV(Rfsync), + _MSG_RECV(Rlock), + _MSG_RECV(Rgetlock), + _MSG_RECV(Rlink), + _MSG_RECV(Rmkdir), + _MSG_RECV(Rrenameat), + _MSG_RECV(Runlinkat), + _MSG_RECV(Rversion), + _MSG_RECV(Rauth), + _MSG_RECV(Rattach), + _MSG_RECV(Rerror), + _MSG_RECV(Rflush), + _MSG_RECV(Rwalk), + _MSG_RECV(Rread), + _MSG_RECV(Rwrite), + _MSG_RECV(Rclunk), + _MSG_RECV(Rremove), + }, +#endif /* CONFIG_9P_ENABLE_9P2000_L */ +#if CONFIG_9P_ENABLE_9P2000_e + [LIB9P_VER_9P2000_e] = { + _MSG_RECV(Rversion), + _MSG_RECV(Rauth), + _MSG_RECV(Rattach), + _MSG_RECV(Rerror), + _MSG_RECV(Rflush), + _MSG_RECV(Rwalk), + _MSG_RECV(Ropen), + _MSG_RECV(Rcreate), + _MSG_RECV(Rread), + _MSG_RECV(Rwrite), + _MSG_RECV(Rclunk), + _MSG_RECV(Rremove), + _MSG_RECV(Rstat), + _MSG_RECV(Rwstat), + _MSG_RECV(Rsession), + _MSG_RECV(Rsread), + _MSG_RECV(Rswrite), + }, +#endif /* CONFIG_9P_ENABLE_9P2000_e */ +#if CONFIG_9P_ENABLE_9P2000_p9p + [LIB9P_VER_9P2000_p9p] = { + _MSG_RECV(Ropenfd), + _MSG_RECV(Rversion), + _MSG_RECV(Rauth), + _MSG_RECV(Rattach), + _MSG_RECV(Rerror), + _MSG_RECV(Rflush), + _MSG_RECV(Rwalk), + _MSG_RECV(Ropen), + _MSG_RECV(Rcreate), + _MSG_RECV(Rread), + _MSG_RECV(Rwrite), + _MSG_RECV(Rclunk), + _MSG_RECV(Rremove), + _MSG_RECV(Rstat), + _MSG_RECV(Rwstat), + }, +#endif /* CONFIG_9P_ENABLE_9P2000_p9p */ +#if CONFIG_9P_ENABLE_9P2000_u + [LIB9P_VER_9P2000_u] = { + _MSG_RECV(Rversion), + _MSG_RECV(Rauth), + _MSG_RECV(Rattach), + _MSG_RECV(Rerror), + _MSG_RECV(Rflush), + _MSG_RECV(Rwalk), + _MSG_RECV(Ropen), + _MSG_RECV(Rcreate), + _MSG_RECV(Rread), + _MSG_RECV(Rwrite), + _MSG_RECV(Rclunk), + _MSG_RECV(Rremove), + _MSG_RECV(Rstat), + _MSG_RECV(Rwstat), + }, +#endif /* CONFIG_9P_ENABLE_9P2000_u */ +}; + +const struct _lib9p_send_tentry _lib9p_table_Tmsg_send[LIB9P_VER_NUM][0x80] = { + [LIB9P_VER_unknown] = { + _MSG_SEND(Tversion), + }, +#if CONFIG_9P_ENABLE_9P2000 + [LIB9P_VER_9P2000] = { + _MSG_SEND(Tversion), + _MSG_SEND(Tauth), + _MSG_SEND(Tattach), + _MSG_SEND(Tflush), + _MSG_SEND(Twalk), + _MSG_SEND(Topen), + _MSG_SEND(Tcreate), + _MSG_SEND(Tread), + _MSG_SEND(Twrite), + _MSG_SEND(Tclunk), + _MSG_SEND(Tremove), + _MSG_SEND(Tstat), + _MSG_SEND(Twstat), + }, +#endif /* CONFIG_9P_ENABLE_9P2000 */ +#if CONFIG_9P_ENABLE_9P2000_L + [LIB9P_VER_9P2000_L] = { + _MSG_SEND(Tstatfs), + _MSG_SEND(Tlopen), + _MSG_SEND(Tlcreate), + _MSG_SEND(Tsymlink), + _MSG_SEND(Tmknod), + _MSG_SEND(Trename), + _MSG_SEND(Treadlink), + _MSG_SEND(Tgetattr), + _MSG_SEND(Tsetattr), + _MSG_SEND(Txattrwalk), + _MSG_SEND(Txattrcreate), + _MSG_SEND(Treaddir), + _MSG_SEND(Tfsync), + _MSG_SEND(Tlock), + _MSG_SEND(Tgetlock), + _MSG_SEND(Tlink), + _MSG_SEND(Tmkdir), + _MSG_SEND(Trenameat), + _MSG_SEND(Tunlinkat), + _MSG_SEND(Tversion), + _MSG_SEND(Tauth), + _MSG_SEND(Tattach), + _MSG_SEND(Tflush), + _MSG_SEND(Twalk), + _MSG_SEND(Tread), + _MSG_SEND(Twrite), + _MSG_SEND(Tclunk), + _MSG_SEND(Tremove), + }, +#endif /* CONFIG_9P_ENABLE_9P2000_L */ +#if CONFIG_9P_ENABLE_9P2000_e + [LIB9P_VER_9P2000_e] = { + _MSG_SEND(Tversion), + _MSG_SEND(Tauth), + _MSG_SEND(Tattach), + _MSG_SEND(Tflush), + _MSG_SEND(Twalk), + _MSG_SEND(Topen), + _MSG_SEND(Tcreate), + _MSG_SEND(Tread), + _MSG_SEND(Twrite), + _MSG_SEND(Tclunk), + _MSG_SEND(Tremove), + _MSG_SEND(Tstat), + _MSG_SEND(Twstat), + _MSG_SEND(Tsession), + _MSG_SEND(Tsread), + _MSG_SEND(Tswrite), + }, +#endif /* CONFIG_9P_ENABLE_9P2000_e */ +#if CONFIG_9P_ENABLE_9P2000_p9p + [LIB9P_VER_9P2000_p9p] = { + _MSG_SEND(Topenfd), + _MSG_SEND(Tversion), + _MSG_SEND(Tauth), + _MSG_SEND(Tattach), + _MSG_SEND(Tflush), + _MSG_SEND(Twalk), + _MSG_SEND(Topen), + _MSG_SEND(Tcreate), + _MSG_SEND(Tread), + _MSG_SEND(Twrite), + _MSG_SEND(Tclunk), + _MSG_SEND(Tremove), + _MSG_SEND(Tstat), + _MSG_SEND(Twstat), + }, +#endif /* CONFIG_9P_ENABLE_9P2000_p9p */ +#if CONFIG_9P_ENABLE_9P2000_u + [LIB9P_VER_9P2000_u] = { + _MSG_SEND(Tversion), + _MSG_SEND(Tauth), + _MSG_SEND(Tattach), + _MSG_SEND(Tflush), + _MSG_SEND(Twalk), + _MSG_SEND(Topen), + _MSG_SEND(Tcreate), + _MSG_SEND(Tread), + _MSG_SEND(Twrite), + _MSG_SEND(Tclunk), + _MSG_SEND(Tremove), + _MSG_SEND(Tstat), + _MSG_SEND(Twstat), + }, +#endif /* CONFIG_9P_ENABLE_9P2000_u */ +}; + +const struct _lib9p_send_tentry _lib9p_table_Rmsg_send[LIB9P_VER_NUM][0x80] = { + [LIB9P_VER_unknown] = { + _MSG_SEND(Rversion), + _MSG_SEND(Rerror), + }, +#if CONFIG_9P_ENABLE_9P2000 + [LIB9P_VER_9P2000] = { + _MSG_SEND(Rversion), + _MSG_SEND(Rauth), + _MSG_SEND(Rattach), + _MSG_SEND(Rerror), + _MSG_SEND(Rflush), + _MSG_SEND(Rwalk), + _MSG_SEND(Ropen), + _MSG_SEND(Rcreate), + _MSG_SEND(Rread), + _MSG_SEND(Rwrite), + _MSG_SEND(Rclunk), + _MSG_SEND(Rremove), + _MSG_SEND(Rstat), + _MSG_SEND(Rwstat), + }, +#endif /* CONFIG_9P_ENABLE_9P2000 */ +#if CONFIG_9P_ENABLE_9P2000_L + [LIB9P_VER_9P2000_L] = { + _MSG_SEND(Rlerror), + _MSG_SEND(Rstatfs), + _MSG_SEND(Rlopen), + _MSG_SEND(Rlcreate), + _MSG_SEND(Rsymlink), + _MSG_SEND(Rmknod), + _MSG_SEND(Rrename), + _MSG_SEND(Rreadlink), + _MSG_SEND(Rgetattr), + _MSG_SEND(Rsetattr), + _MSG_SEND(Rxattrwalk), + _MSG_SEND(Rxattrcreate), + _MSG_SEND(Rreaddir), + _MSG_SEND(Rfsync), + _MSG_SEND(Rlock), + _MSG_SEND(Rgetlock), + _MSG_SEND(Rlink), + _MSG_SEND(Rmkdir), + _MSG_SEND(Rrenameat), + _MSG_SEND(Runlinkat), + _MSG_SEND(Rversion), + _MSG_SEND(Rauth), + _MSG_SEND(Rattach), + _MSG_SEND(Rerror), + _MSG_SEND(Rflush), + _MSG_SEND(Rwalk), + _MSG_SEND(Rread), + _MSG_SEND(Rwrite), + _MSG_SEND(Rclunk), + _MSG_SEND(Rremove), + }, +#endif /* CONFIG_9P_ENABLE_9P2000_L */ +#if CONFIG_9P_ENABLE_9P2000_e + [LIB9P_VER_9P2000_e] = { + _MSG_SEND(Rversion), + _MSG_SEND(Rauth), + _MSG_SEND(Rattach), + _MSG_SEND(Rerror), + _MSG_SEND(Rflush), + _MSG_SEND(Rwalk), + _MSG_SEND(Ropen), + _MSG_SEND(Rcreate), + _MSG_SEND(Rread), + _MSG_SEND(Rwrite), + _MSG_SEND(Rclunk), + _MSG_SEND(Rremove), + _MSG_SEND(Rstat), + _MSG_SEND(Rwstat), + _MSG_SEND(Rsession), + _MSG_SEND(Rsread), + _MSG_SEND(Rswrite), + }, +#endif /* CONFIG_9P_ENABLE_9P2000_e */ +#if CONFIG_9P_ENABLE_9P2000_p9p + [LIB9P_VER_9P2000_p9p] = { + _MSG_SEND(Ropenfd), + _MSG_SEND(Rversion), + _MSG_SEND(Rauth), + _MSG_SEND(Rattach), + _MSG_SEND(Rerror), + _MSG_SEND(Rflush), + _MSG_SEND(Rwalk), + _MSG_SEND(Ropen), + _MSG_SEND(Rcreate), + _MSG_SEND(Rread), + _MSG_SEND(Rwrite), + _MSG_SEND(Rclunk), + _MSG_SEND(Rremove), + _MSG_SEND(Rstat), + _MSG_SEND(Rwstat), + }, +#endif /* CONFIG_9P_ENABLE_9P2000_p9p */ +#if CONFIG_9P_ENABLE_9P2000_u + [LIB9P_VER_9P2000_u] = { + _MSG_SEND(Rversion), + _MSG_SEND(Rauth), + _MSG_SEND(Rattach), + _MSG_SEND(Rerror), + _MSG_SEND(Rflush), + _MSG_SEND(Rwalk), + _MSG_SEND(Ropen), + _MSG_SEND(Rcreate), + _MSG_SEND(Rread), + _MSG_SEND(Rwrite), + _MSG_SEND(Rclunk), + _MSG_SEND(Rremove), + _MSG_SEND(Rstat), + _MSG_SEND(Rwstat), + }, +#endif /* CONFIG_9P_ENABLE_9P2000_u */ }; -FLATTEN bool _lib9p_validate_stat(struct _validate_ctx *ctx) { - return validate_stat(ctx); +LM_FLATTEN ssize_t _lib9p_stat_validate(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes, uint32_t *ret_net_size) { + return validate_stat(ctx, net_size, net_bytes, ret_net_size); } -FLATTEN void _lib9p_unmarshal_stat(struct _unmarshal_ctx *ctx, struct lib9p_stat *out) { - unmarshal_stat(ctx, out); +LM_FLATTEN void _lib9p_stat_unmarshal(struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out) { + unmarshal_stat(ctx, net_bytes, out); } -FLATTEN bool _lib9p_marshal_stat(struct _marshal_ctx *ctx, struct lib9p_stat *val) { - return marshal_stat(ctx, val); +LM_FLATTEN bool _lib9p_stat_marshal(struct lib9p_ctx *ctx, struct lib9p_stat *val, struct _marshal_ret *ret) { + return marshal_stat(ctx, val, ret); } diff --git a/lib9p/CMakeLists.txt b/lib9p/CMakeLists.txt index 488cff9..949b6d6 100644 --- a/lib9p/CMakeLists.txt +++ b/lib9p/CMakeLists.txt @@ -1,17 +1,47 @@ -# lib9p/CMakeLists.txt - TODO +# lib9p/CMakeLists.txt - A 9P protocol and server library # -# 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 add_library(lib9p INTERFACE) -target_include_directories(lib9p SYSTEM INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include) +target_include_directories(lib9p PUBLIC INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include) target_sources(lib9p INTERFACE 9p.generated.c 9p.c + tables.c srv.c ) target_link_libraries(lib9p INTERFACE libcr_ipc + libfmt + libhw_generic libmisc - libhw ) + +if (ENABLE_TESTS) + add_subdirectory(tests/test_server) + + function(add_lib9p_executable arg_testname) + add_executable("${arg_testname}" "tests/${arg_testname}.c") + target_link_libraries("${arg_testname}" lib9p) + target_include_directories("${arg_testname}" PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/tests + ${CMAKE_CURRENT_SOURCE_DIR}/tests/client_config + ) + endfunction() + function(add_lib9p_test arg_testscript) + get_filename_component(tmp_basename "${arg_testscript}" "NAME") + add_test( + NAME "lib9p/${tmp_basename}" + COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/tests/runtest" "${arg_testscript}" "${CMAKE_CURRENT_SOURCE_DIR}/tests/${tmp_basename}.explog" + ) + endfunction() + + add_lib9p_test("${CMAKE_CURRENT_SOURCE_DIR}/tests/testclient-p9p") + + add_lib9p_executable("testclient-sess") + add_lib9p_test("./testclient-sess") + + add_lib_test(lib9p test_compile) + target_include_directories(test_compile PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_compile_config) +endif() diff --git a/lib9p/idl.gen b/lib9p/idl.gen deleted file mode 100755 index ec42cfd..0000000 --- a/lib9p/idl.gen +++ /dev/null @@ -1,1184 +0,0 @@ -#!/usr/bin/env python -# lib9p/idl.gen - Generate C marshalers/unmarshalers for .9p files -# defining 9P protocol variants. -# -# Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com> -# SPDX-License-Identifier: AGPL-3.0-or-later - -import enum -import os.path -import re -from abc import ABC, abstractmethod -from typing import Callable, Final, Literal, TypeAlias, TypeVar, cast - -# This strives to be "general-purpose" in that it just acts on the -# *.9p inputs; but (unfortunately?) there are a few special-cases in -# this script, marked with "SPECIAL". - -# Types ######################################################################## - - -class Primitive(enum.Enum): - u8 = 1 - u16 = 2 - u32 = 4 - u64 = 8 - - @property - def in_versions(self) -> set[str]: - return set() - - @property - def name(self) -> str: - return str(self.value) - - @property - def static_size(self) -> int: - return self.value - - -class Number: - name: str - in_versions: set[str] - - prim: Primitive - - def __init__(self) -> None: - self.in_versions = set() - - @property - def static_size(self) -> int: - return self.prim.static_size - - -class BitfieldVal: - name: str - in_versions: set[str] - - val: str - - def __init__(self) -> None: - self.in_versions = set() - - -class Bitfield: - name: str - in_versions: set[str] - - prim: Primitive - - bits: list[str] # bitnames - names: dict[str, BitfieldVal] # bits *and* aliases - - def __init__(self) -> None: - self.in_versions = set() - self.names = {} - - @property - def static_size(self) -> int: - return self.prim.static_size - - def bit_is_valid(self, bit: str | int, ver: str | None = None) -> bool: - """Return whether the given bit is valid in the given protocol - version. - - """ - bitname = self.bits[bit] if isinstance(bit, int) else bit - assert bitname in self.bits - if not bitname: - return False - if bitname.startswith("_"): - return False - if ver and (ver not in self.names[bitname].in_versions): - return False - return True - - -class ExprLit: - val: int - - def __init__(self, val: int) -> None: - self.val = val - - -class ExprSym: - name: str - - def __init__(self, name: str) -> None: - self.name = name - - -class ExprOp: - op: Literal["-", "+"] - - def __init__(self, op: Literal["-", "+"]) -> None: - self.op = op - - -class Expr: - tokens: list[ExprLit | ExprSym | ExprOp] - - def __init__(self) -> None: - self.tokens = [] - - def __bool__(self) -> bool: - return len(self.tokens) > 0 - - -class StructMember: - # from left-to-right when parsing - cnt: str | None = None - name: str - typ: "Type" - max: Expr - val: Expr - - in_versions: set[str] - - @property - def static_size(self) -> int | None: - if self.cnt: - return None - return self.typ.static_size - - -class Struct: - name: str - in_versions: set[str] - - members: list[StructMember] - - def __init__(self) -> None: - self.in_versions = set() - - @property - def static_size(self) -> int | None: - size = 0 - for member in self.members: - msize = member.static_size - if msize is None: - return None - size += msize - return size - - -class Message(Struct): - @property - def msgid(self) -> int: - assert len(self.members) >= 3 - assert self.members[1].name == "typ" - assert self.members[1].static_size == 1 - assert self.members[1].val - assert len(self.members[1].val.tokens) == 1 - assert isinstance(self.members[1].val.tokens[0], ExprLit) - return self.members[1].val.tokens[0].val - - -Type: TypeAlias = Primitive | Number | Bitfield | Struct | Message -# type Type = Primitive | Number | Bitfield | Struct | Message # Change to this once we have Python 3.13 -T = TypeVar("T", Number, Bitfield, Struct, Message) - -# Parse *.9p ################################################################### - -re_priname = "(?:1|2|4|8)" # primitive names -re_symname = "(?:[a-zA-Z_][a-zA-Z_0-9]*)" # "symbol" names; most *.9p-defined names -re_impname = r"(?:\*|" + re_symname + ")" # names we can import -re_msgname = r"(?:[TR][a-zA-Z_0-9]*)" # names a message can be - -re_memtype = f"(?:{re_symname}|{re_priname})" # typenames that a struct member can be - -re_expr = f"(?:(?:-|\\+|[0-9]+|&?{re_symname})+)" - -re_bitspec_bit = f"(?P<bit>[0-9]+)\\s*=\\s*(?P<name>{re_symname})" -re_bitspec_alias = f"(?P<name>{re_symname})\\s*=\\s*(?P<val>\\S+)" - -re_memberspec = f"(?:(?P<cnt>{re_symname})\\*\\()?(?P<name>{re_symname})\\[(?P<typ>{re_memtype})(?:,max=(?P<max>{re_expr})|,val=(?P<val>{re_expr}))*\\]\\)?" - - -def parse_bitspec(ver: str, bf: Bitfield, spec: str) -> None: - spec = spec.strip() - - bit: int | None - val: BitfieldVal - if m := re.fullmatch(re_bitspec_bit, spec): - bit = int(m.group("bit")) - name = m.group("name") - - val = BitfieldVal() - val.name = name - val.val = f"1<<{bit}" - val.in_versions.add(ver) - - if bit < 0 or bit >= len(bf.bits): - raise ValueError(f"{bf.name}: bit {bit} is out-of-bounds") - if bf.bits[bit]: - raise ValueError(f"{bf.name}: bit {bit} already assigned") - bf.bits[bit] = val.name - elif m := re.fullmatch(re_bitspec_alias, spec): - name = m.group("name") - valstr = m.group("val") - - val = BitfieldVal() - val.name = name - val.val = valstr - val.in_versions.add(ver) - else: - raise SyntaxError(f"invalid bitfield spec {repr(spec)}") - - if val.name in bf.names: - raise ValueError(f"{bf.name}: name {val.name} already assigned") - bf.names[val.name] = val - - -def parse_expr(expr: str) -> Expr: - assert re.fullmatch(re_expr, expr) - ret = Expr() - for tok in re.split("([-+])", expr): - if tok == "-" or tok == "+": - # I, for the life of me, do not understand why I need this - # cast() to keep mypy happy. - ret.tokens += [ExprOp(cast(Literal["-", "+"], tok))] - elif re.fullmatch("[0-9]+", tok): - ret.tokens += [ExprLit(int(tok))] - else: - ret.tokens += [ExprSym(tok)] - return ret - - -def parse_members(ver: str, env: dict[str, Type], struct: Struct, specs: str) -> None: - for spec in specs.split(): - m = re.fullmatch(re_memberspec, spec) - if not m: - raise SyntaxError(f"invalid member spec {repr(spec)}") - - member = StructMember() - member.in_versions = {ver} - - member.name = m.group("name") - if any(x.name == member.name for x in struct.members): - raise ValueError(f"duplicate member name {repr(member.name)}") - - if m.group("typ") not in env: - raise NameError(f"Unknown type {repr(m.group(2))}") - member.typ = env[m.group("typ")] - - if cnt := m.group("cnt"): - if len(struct.members) == 0 or struct.members[-1].name != cnt: - raise ValueError(f"list count must be previous item: {repr(cnt)}") - if not isinstance(struct.members[-1].typ, Primitive): - raise ValueError(f"list count must be an integer type: {repr(cnt)}") - member.cnt = cnt - - if maxstr := m.group("max"): - if (not isinstance(member.typ, Primitive)) or member.cnt: - raise ValueError("',max=' may only be specified on a non-repeated atom") - member.max = parse_expr(maxstr) - else: - member.max = Expr() - - if valstr := m.group("val"): - if (not isinstance(member.typ, Primitive)) or member.cnt: - raise ValueError("',val=' may only be specified on a non-repeated atom") - member.val = parse_expr(valstr) - else: - member.val = Expr() - - struct.members += [member] - - -def re_string(grpname: str) -> str: - return f'"(?P<{grpname}>[^"]*)"' - - -re_line_version = f"version\\s+{re_string('version')}" -re_line_import = f"from\\s+(?P<file>\\S+)\\s+import\\s+(?P<syms>{re_impname}(?:\\s*,\\s*{re_impname})*)" -re_line_num = f"num\\s+(?P<name>{re_symname})\\s*=\\s*(?P<prim>{re_priname})" -re_line_bitfield = f"bitfield\\s+(?P<name>{re_symname})\\s*=\\s*(?P<prim>{re_priname})" -re_line_bitfield_ = ( - f"bitfield\\s+(?P<name>{re_symname})\\s*\\+=\\s*{re_string('member')}" -) -re_line_struct = ( - f"struct\\s+(?P<name>{re_symname})\\s*(?P<op>\\+?=)\\s*{re_string('members')}" -) -re_line_msg = ( - f"msg\\s+(?P<name>{re_msgname})\\s*(?P<op>\\+?=)\\s*{re_string('members')}" -) -re_line_cont = f"\\s+{re_string('specs')}" # could be bitfield/struct/msg - - -def parse_file( - filename: str, get_include: Callable[[str], tuple[str, list[Type]]] -) -> tuple[str, list[Type]]: - version: str | None = None - env: dict[str, Type] = { - "1": Primitive.u8, - "2": Primitive.u16, - "4": Primitive.u32, - "8": Primitive.u64, - } - - def get_type(name: str, tc: type[T]) -> T: - nonlocal env - if name not in env: - raise NameError(f"Unknown type {repr(name)}") - ret = env[name] - if (not isinstance(ret, tc)) or (ret.__class__.__name__ != tc.__name__): - raise NameError(f"Type {repr(ret.name)} is not a {tc.__name__}") - return ret - - with open(filename, "r") as fh: - prev: Type | None = None - for line in fh: - line = line.split("#", 1)[0].rstrip() - if not line: - continue - if m := re.fullmatch(re_line_version, line): - if version: - raise SyntaxError("must have exactly 1 version line") - version = m.group("version") - continue - if not version: - raise SyntaxError("must have exactly 1 version line") - - if m := re.fullmatch(re_line_import, line): - other_version, other_typs = get_include(m.group("file")) - for symname in m.group("syms").split(sep=","): - symname = symname.strip() - for typ in other_typs: - if typ.name == symname or symname == "*": - match typ: - case Primitive(): - pass - case Number(): - typ.in_versions.add(version) - case Bitfield(): - typ.in_versions.add(version) - for val in typ.names.values(): - if other_version in val.in_versions: - val.in_versions.add(version) - case Struct(): # and Message() - typ.in_versions.add(version) - for member in typ.members: - if other_version in member.in_versions: - member.in_versions.add(version) - env[typ.name] = typ - elif m := re.fullmatch(re_line_num, line): - num = Number() - num.name = m.group("name") - num.in_versions.add(version) - - prim = env[m.group("prim")] - assert isinstance(prim, Primitive) - num.prim = prim - - env[num.name] = num - prev = num - elif m := re.fullmatch(re_line_bitfield, line): - bf = Bitfield() - bf.name = m.group("name") - bf.in_versions.add(version) - - prim = env[m.group("prim")] - assert isinstance(prim, Primitive) - bf.prim = prim - - bf.bits = (prim.static_size * 8) * [""] - - env[bf.name] = bf - prev = bf - elif m := re.fullmatch(re_line_bitfield_, line): - bf = get_type(m.group("name"), Bitfield) - parse_bitspec(version, bf, m.group("member")) - - prev = bf - elif m := re.fullmatch(re_line_struct, line): - match m.group("op"): - case "=": - struct = Struct() - struct.name = m.group("name") - struct.in_versions.add(version) - struct.members = [] - parse_members(version, env, struct, m.group("members")) - - env[struct.name] = struct - prev = struct - case "+=": - struct = get_type(m.group("name"), Struct) - parse_members(version, env, struct, m.group("members")) - - prev = struct - elif m := re.fullmatch(re_line_msg, line): - match m.group("op"): - case "=": - msg = Message() - msg.name = m.group("name") - msg.in_versions.add(version) - msg.members = [] - parse_members(version, env, msg, m.group("members")) - - env[msg.name] = msg - prev = msg - case "+=": - msg = get_type(m.group("name"), Message) - parse_members(version, env, msg, m.group("members")) - - prev = msg - elif m := re.fullmatch(re_line_cont, line): - match prev: - case Bitfield(): - parse_bitspec(version, prev, m.group("specs")) - case Struct(): # and Message() - parse_members(version, env, prev, m.group("specs")) - case _: - raise SyntaxError( - "continuation line must come after a bitfield, struct, or msg line" - ) - else: - raise SyntaxError(f"invalid line {repr(line)}") - if not version: - raise SyntaxError("must have exactly 1 version line") - - typs: list[Type] = [x for x in env.values() if not isinstance(x, Primitive)] - - for typ in [typ for typ in typs if isinstance(typ, Struct)]: - valid_syms = ["end", *["&" + m.name for m in typ.members]] - for member in typ.members: - for tok in [*member.max.tokens, *member.val.tokens]: - if isinstance(tok, ExprSym) and tok.name not in valid_syms: - raise ValueError( - f"{typ.name}.{member.name}: invalid sym: {tok.name}" - ) - - return version, typs - - -# Generate C ################################################################### - -idprefix = "lib9p_" - - -def c_ver_enum(ver: str) -> str: - return f"{idprefix.upper()}VER_{ver.replace('.', '_')}" - - -def c_ver_ifdef(versions: set[str]) -> str: - return " || ".join( - f"defined(CONFIG_9P_ENABLE_{v.replace('.', '_')})" for v in sorted(versions) - ) - - -def c_ver_cond(versions: set[str]) -> str: - if len(versions) == 1: - return f"(ctx->ctx->version=={c_ver_enum(next(v for v in versions))})" - return "( " + (" || ".join(c_ver_cond({v}) for v in sorted(versions))) + " )" - - -def c_typename(typ: Type) -> str: - match typ: - case Primitive(): - return f"uint{typ.value*8}_t" - case Number(): - return f"{idprefix}{typ.name}_t" - case Bitfield(): - return f"{idprefix}{typ.name}_t" - case Message(): - return f"struct {idprefix}msg_{typ.name}" - case Struct(): - return f"struct {idprefix}{typ.name}" - case _: - raise ValueError(f"not a type: {typ.__class__.__name__}") - - -_ifdef_stack: list[str | None] = [] - - -def ifdef_push(n: int, _newval: str) -> str: - # Grow the stack as needed - global _ifdef_stack - while len(_ifdef_stack) < n: - _ifdef_stack += [None] - - # Set some variables - parentval: str | None = None - for x in _ifdef_stack[:-1]: - if x is not None: - parentval = x - oldval = _ifdef_stack[-1] - newval: str | None = _newval - if newval == parentval: - newval = None - - # Put newval on the stack. - _ifdef_stack[-1] = newval - - # Build output. - ret = "" - if newval != oldval: - if oldval is not None: - ret += f"#endif /* {oldval} */\n" - if newval is not None: - ret += f"#if {newval}\n" - return ret - - -def ifdef_pop(n: int) -> str: - global _ifdef_stack - ret = "" - while len(_ifdef_stack) > n: - if _ifdef_stack[-1] is not None: - ret += f"#endif /* {_ifdef_stack[-1]} */\n" - _ifdef_stack = _ifdef_stack[:-1] - return ret - - -def gen_h(versions: set[str], typs: list[Type]) -> str: - global _ifdef_stack - _ifdef_stack = [] - - ret = f"""/* Generated by `{' '.join(sys.argv)}`. DO NOT EDIT! */ - -#ifndef _LIB9P_9P_H_ - #error Do not include <lib9p/9p.generated.h> directly; include <lib9p/9p.h> instead -#endif - -#include <stdint.h> /* for uint{{n}}_t types */ -""" - - ret += f""" -/* versions *******************************************************************/ - -enum {idprefix}version {{ -""" - fullversions = ["unknown = 0", *sorted(versions)] - verwidth = max(len(v) for v in fullversions) - for ver in fullversions: - if ver in versions: - ret += ifdef_push(1, c_ver_ifdef({ver})) - ret += f"\t{c_ver_enum(ver)}," - ret += (" " * (verwidth - len(ver))) + ' /* "' + ver.split()[0] + '" */\n' - ret += ifdef_pop(0) - ret += f"\t{c_ver_enum('NUM')},\n" - ret += "};\n" - ret += "\n" - ret += f"const char *{idprefix}version_str(enum {idprefix}version);\n" - - ret += """ -/* non-message types **********************************************************/ -""" - for typ in [typ for typ in typs if not isinstance(typ, Message)]: - ret += "\n" - ret += ifdef_push(1, c_ver_ifdef(typ.in_versions)) - match typ: - case Number(): - ret += f"typedef {c_typename(typ.prim)} {c_typename(typ)};\n" - case Bitfield(): - ret += f"typedef {c_typename(typ.prim)} {c_typename(typ)};\n" - names = [ - *reversed( - [typ.bits[n] or f" {n}" for n in range(0, len(typ.bits))] - ), - "", - *[k for k in typ.names if k not in typ.bits], - ] - namewidth = max(len(name) for name in names) - - ret += "\n" - for name in names: - if name == "": - ret += "\n" - elif name.startswith(" "): - ret += ifdef_push(2, c_ver_ifdef(typ.in_versions)) - sp = " " * ( - len("# define ") - + len(idprefix) - + len(typ.name) - + 1 - + namewidth - + 2 - - len("/* unused") - ) - ret += f"/* unused{sp}(({c_typename(typ)})(1<<{name[1:]})) */\n" - else: - ret += ifdef_push(2, c_ver_ifdef(typ.names[name].in_versions)) - if name.startswith("_"): - c_name = f"_{idprefix.upper()}{typ.name.upper()}_{name[1:]}" - else: - c_name = f"{idprefix.upper()}{typ.name.upper()}_{name}" - sp1 = " " if _ifdef_stack[-1] else "" - sp2 = " " if _ifdef_stack[-1] else " " - sp3 = " " * (2 + namewidth - len(name)) - ret += f"#{sp1}define{sp2}{c_name}{sp3}(({c_typename(typ)})({typ.names[name].val}))\n" - ret += ifdef_pop(1) - case Struct(): - typewidth = max(len(c_typename(m.typ)) for m in typ.members) - - ret += c_typename(typ) + " {\n" - for member in typ.members: - if member.val: - continue - ret += ifdef_push(2, c_ver_ifdef(member.in_versions)) - c_type = c_typename(member.typ) - if (typ.name in ["d", "s"]) and member.cnt: # SPECIAL - c_type = "char" - ret += f"\t{c_type.ljust(typewidth)} {'*' if member.cnt else ' '}{member.name};\n" - ret += ifdef_pop(1) - ret += "};\n" - ret += ifdef_pop(0) - - ret += """ -/* messages *******************************************************************/ - -""" - ret += f"enum {idprefix}msg_type {{ /* uint8_t */\n" - namewidth = max(len(msg.name) for msg in typs if isinstance(msg, Message)) - for msg in [msg for msg in typs if isinstance(msg, Message)]: - ret += ifdef_push(1, c_ver_ifdef(msg.in_versions)) - ret += f"\t{idprefix.upper()}TYP_{msg.name.ljust(namewidth)} = {msg.msgid},\n" - ret += ifdef_pop(0) - ret += "};\n" - - for msg in [msg for msg in typs if isinstance(msg, Message)]: - ret += "\n" - ret += ifdef_push(1, c_ver_ifdef(msg.in_versions)) - ret += c_typename(msg) + " {" - if not msg.members: - ret += "};\n" - continue - ret += "\n" - - typewidth = max(len(c_typename(m.typ)) for m in msg.members) - - for member in msg.members: - if member.val: - continue - ret += ifdef_push(2, c_ver_ifdef(member.in_versions)) - ret += f"\t{c_typename(member.typ).ljust(typewidth)} {'*' if member.cnt else ' '}{member.name};\n" - ret += ifdef_pop(1) - ret += "};\n" - ret += ifdef_pop(0) - - return ret - - -def c_expr(expr: Expr) -> str: - ret: list[str] = [] - for tok in expr.tokens: - match tok: - case ExprOp(): - ret += [tok.op] - case ExprLit(): - ret += [str(tok.val)] - case ExprSym(name="end"): - ret += ["ctx->net_offset"] - case ExprSym(): - ret += [f"_{tok.name[1:]}_offset"] - return " ".join(ret) - - -def gen_c(versions: set[str], typs: list[Type]) -> str: - global _ifdef_stack - _ifdef_stack = [] - - ret = f"""/* Generated by `{' '.join(sys.argv)}`. DO NOT EDIT! */ - -#include <assert.h> -#include <stdbool.h> -#include <stddef.h> /* for size_t */ -#include <inttypes.h> /* for PRI* macros */ -#include <string.h> /* for memset() */ - -#include <lib9p/9p.h> - -#include "internal.h" -""" - - def used(arg: str) -> str: - return arg - - def unused(arg: str) -> str: - return f"UNUSED({arg})" - - # strings ################################################################## - ret += f""" -/* strings ********************************************************************/ - -static const char *version_strs[{c_ver_enum('NUM')}] = {{ -""" - for ver in ["unknown", *sorted(versions)]: - if ver in versions: - ret += ifdef_push(1, c_ver_ifdef({ver})) - ret += f'\t[{c_ver_enum(ver)}] = "{ver}",\n' - ret += ifdef_pop(0) - ret += "};\n" - ret += f""" -const char *{idprefix}version_str(enum {idprefix}version ver) {{ - assert(0 <= ver && ver < {c_ver_enum('NUM')}); - return version_strs[ver]; -}} -""" - - # validate_* ############################################################### - ret += """ -/* validate_* *****************************************************************/ - -static ALWAYS_INLINE bool _validate_size_net(struct _validate_ctx *ctx, uint32_t n) { - if (__builtin_add_overflow(ctx->net_offset, n, &ctx->net_offset)) - /* If needed-net-size overflowed uint32_t, then - * there's no way that actual-net-size will live up to - * that. */ - return lib9p_error(ctx->ctx, LINUX_EBADMSG, "message is too short for content"); - if (ctx->net_offset > ctx->net_size) - return lib9p_error(ctx->ctx, LINUX_EBADMSG, "message is too short for content"); - return false; -} - -static ALWAYS_INLINE bool _validate_size_host(struct _validate_ctx *ctx, size_t n) { - if (__builtin_add_overflow(ctx->host_extra, n, &ctx->host_extra)) - /* If needed-host-size overflowed size_t, then there's - * no way that actual-net-size will live up to - * that. */ - return lib9p_error(ctx->ctx, LINUX_EBADMSG, "message is too short for content"); - return false; -} - -static ALWAYS_INLINE bool _validate_list(struct _validate_ctx *ctx, - size_t cnt, - _validate_fn_t item_fn, size_t item_host_size) { - for (size_t i = 0; i < cnt; i++) - if (_validate_size_host(ctx, item_host_size) || item_fn(ctx)) - return true; - return false; -} - -#define validate_1(ctx) _validate_size_net(ctx, 1) -#define validate_2(ctx) _validate_size_net(ctx, 2) -#define validate_4(ctx) _validate_size_net(ctx, 4) -#define validate_8(ctx) _validate_size_net(ctx, 8) -""" - for typ in typs: - inline = "FLATTEN" if isinstance(typ, Message) else "ALWAYS_INLINE" - argfn = unused if (isinstance(typ, Struct) and not typ.members) else used - ret += "\n" - ret += ifdef_push(1, c_ver_ifdef(typ.in_versions)) - - if isinstance(typ, Bitfield): - ret += f"static const {c_typename(typ)} {typ.name}_masks[{c_ver_enum('NUM')}] = {{\n" - verwidth = max(len(ver) for ver in versions) - for ver in sorted(versions): - ret += ifdef_push(2, c_ver_ifdef({ver})) - ret += ( - f"\t[{c_ver_enum(ver)}]{' '*(verwidth-len(ver))} = 0b" - + "".join( - "1" if typ.bit_is_valid(bitname, ver) else "0" - for bitname in reversed(typ.bits) - ) - + ",\n" - ) - ret += ifdef_pop(1) - ret += "};\n" - - ret += f"static {inline} bool validate_{typ.name}(struct _validate_ctx *{argfn('ctx')}) {{\n" - - if typ.name == "d": # SPECIAL - # Optimize... maybe the compiler could figure out to do - # this, but let's make it obvious. - ret += "\tuint32_t base_offset = ctx->net_offset;\n" - ret += "\tif (validate_4(ctx))\n" - ret += "\t\treturn true;\n" - ret += "\tuint32_t len = decode_u32le(&ctx->net_bytes[base_offset]);\n" - ret += "\treturn _validate_size_net(ctx, len) || _validate_size_host(ctx, len);\n" - ret += "}\n" - continue - if typ.name == "s": # SPECIAL - # Add an extra nul-byte on the host, and validate UTF-8 - # (also, similar optimization to "d"). - ret += "\tuint32_t base_offset = ctx->net_offset;\n" - ret += "\tif (validate_2(ctx))\n" - ret += "\t\treturn true;\n" - ret += "\tuint16_t len = decode_u16le(&ctx->net_bytes[base_offset]);\n" - ret += "\tif (_validate_size_net(ctx, len) || _validate_size_host(ctx, ((size_t)len)+1))\n" - ret += "\t\treturn true;\n" - ret += "\tif (!is_valid_utf8_without_nul(&ctx->net_bytes[base_offset+2], len))\n" - ret += '\t\treturn lib9p_error(ctx->ctx, LINUX_EBADMSG, "message contains invalid UTF-8");\n' - ret += "\treturn false;\n" - ret += "}\n" - continue - - match typ: - case Number(): - ret += f"\treturn validate_{typ.prim.name}(ctx);\n" - case Bitfield(): - ret += f"\t if (validate_{typ.static_size}(ctx))\n" - ret += "\t\treturn true;\n" - ret += ( - f"\t{c_typename(typ)} mask = {typ.name}_masks[ctx->ctx->version];\n" - ) - ret += f"\t{c_typename(typ)} val = decode_u{typ.static_size*8}le(&ctx->net_bytes[ctx->net_offset-{typ.static_size}]);\n" - ret += f"\tif (val & ~mask)\n" - ret += f'\t\treturn lib9p_errorf(ctx->ctx, LINUX_EBADMSG, "unknown bits in {typ.name} bitfield: %#0{typ.static_size}"PRIx{typ.static_size*8}, val & ~mask);\n' - ret += "\treturn false;\n" - case Struct(): # and Message() - if len(typ.members) == 0: - ret += "\treturn false;\n" - ret += "}\n" - continue - - # Pass 1 - declare value variables - for member in typ.members: - if member.max or member.val: - ret += ifdef_push(2, c_ver_ifdef(member.in_versions)) - ret += f"\t{c_typename(member.typ)} {member.name};\n" - ret += ifdef_pop(1) - - # Pass 2 - declare offset variables - mark_offset: set[str] = set() - for member in typ.members: - for tok in [*member.max.tokens, *member.val.tokens]: - if isinstance(tok, ExprSym) and tok.name.startswith("&"): - if tok.name[1:] not in mark_offset: - ret += f"\tuint32_t _{tok.name[1:]}_offset;\n" - mark_offset.add(tok.name[1:]) - - # Pass 3 - main pass - ret += "\treturn false\n" - prev_size: int | None = None - for member in typ.members: - ret += ifdef_push(2, c_ver_ifdef(member.in_versions)) - ret += f"\t || " - if member.in_versions != typ.in_versions: - ret += "( " + c_ver_cond(member.in_versions) + " && " - if member.cnt is not None: - assert prev_size - ret += f"_validate_list(ctx, decode_u{prev_size*8}le(&ctx->net_bytes[ctx->net_offset-{prev_size}]), validate_{member.typ.name}, sizeof({c_typename(member.typ)}))" - else: - if member.max or member.val: - ret += "(" - if member.name in mark_offset: - ret += f"({{ _{member.name}_offset = ctx->net_offset; " - ret += f"validate_{member.typ.name}(ctx)" - if member.name in mark_offset: - ret += "; })" - if member.max or member.val: - bytes = member.static_size - assert bytes - bits = bytes * 8 - ret += f" || ({{ {member.name} = decode_u{bits}le(&ctx->net_bytes[ctx->net_offset-{bytes}]); false; }}))" - if member.in_versions != typ.in_versions: - ret += " )" - ret += "\n" - prev_size = member.static_size - - # Pass 4 - validate ,max= and ,val= constraints - for member in typ.members: - if member.max: - ret += ifdef_push(2, c_ver_ifdef(member.in_versions)) - ret += f"\t || ({{ uint32_t max = {c_expr(member.max)}; (((uint32_t){member.name}) > max) &&\n" - ret += f'\t lib9p_errorf(ctx->ctx, LINUX_EBADMSG, "{member.name} value is too large (%"PRIu32" > %"PRIu32")", {member.name}, max); }})\n' - if member.val: - ret += ifdef_push(2, c_ver_ifdef(member.in_versions)) - ret += f"\t || ({{ uint32_t exp = {c_expr(member.val)}; (((uint32_t){member.name}) != exp) &&\n" - ret += f'\t lib9p_errorf(ctx->ctx, LINUX_EBADMSG, "{member.name} value is wrong (actual:%"PRIu32" != correct:%"PRIu32")", (uint32_t){member.name}, exp); }})\n' - - ret += ifdef_pop(1) - ret += "\t ;\n" - ret += "}\n" - ret += ifdef_pop(0) - - # unmarshal_* ############################################################## - ret += """ -/* unmarshal_* ****************************************************************/ - -static ALWAYS_INLINE void unmarshal_1(struct _unmarshal_ctx *ctx, uint8_t *out) { - *out = decode_u8le(&ctx->net_bytes[ctx->net_offset]); - ctx->net_offset += 1; -} - -static ALWAYS_INLINE void unmarshal_2(struct _unmarshal_ctx *ctx, uint16_t *out) { - *out = decode_u16le(&ctx->net_bytes[ctx->net_offset]); - ctx->net_offset += 2; -} - -static ALWAYS_INLINE void unmarshal_4(struct _unmarshal_ctx *ctx, uint32_t *out) { - *out = decode_u32le(&ctx->net_bytes[ctx->net_offset]); - ctx->net_offset += 4; -} - -static ALWAYS_INLINE void unmarshal_8(struct _unmarshal_ctx *ctx, uint64_t *out) { - *out = decode_u64le(&ctx->net_bytes[ctx->net_offset]); - ctx->net_offset += 8; -} -""" - for typ in typs: - inline = "FLATTEN" if isinstance(typ, Message) else "ALWAYS_INLINE" - argfn = unused if (isinstance(typ, Struct) and not typ.members) else used - ret += "\n" - ret += ifdef_push(1, c_ver_ifdef(typ.in_versions)) - ret += f"static {inline} void unmarshal_{typ.name}(struct _unmarshal_ctx *{argfn('ctx')}, {c_typename(typ)} *out) {{\n" - match typ: - case Number(): - ret += f"\tunmarshal_{typ.prim.name}(ctx, ({c_typename(typ.prim)} *)out);\n" - case Bitfield(): - ret += f"\tunmarshal_{typ.prim.name}(ctx, ({c_typename(typ.prim)} *)out);\n" - case Struct(): - ret += "\tmemset(out, 0, sizeof(*out));\n" - - for member in typ.members: - ret += ifdef_push(2, c_ver_ifdef(member.in_versions)) - if member.val: - ret += f"\tctx->net_offset += {member.static_size};\n" - continue - ret += "\t" - - prefix = "\t" - if member.in_versions != typ.in_versions: - ret += "if ( " + c_ver_cond(member.in_versions) + " ) " - prefix = "\t\t" - if member.cnt: - if member.in_versions != typ.in_versions: - ret += "{\n" - ret += prefix - ret += f"out->{member.name} = ctx->extra;\n" - ret += f"{prefix}ctx->extra += sizeof(out->{member.name}[0]) * out->{member.cnt};\n" - ret += f"{prefix}for (typeof(out->{member.cnt}) i = 0; i < out->{member.cnt}; i++)\n" - if typ.name in ["d", "s"]: # SPECIAL - ret += f"{prefix}\tunmarshal_{member.typ.name}(ctx, (uint8_t *)&out->{member.name}[i]);\n" - else: - ret += f"{prefix}\tunmarshal_{member.typ.name}(ctx, &out->{member.name}[i]);\n" - if member.in_versions != typ.in_versions: - ret += "\t}\n" - else: - ret += ( - f"unmarshal_{member.typ.name}(ctx, &out->{member.name});\n" - ) - if typ.name == "s": # SPECIAL - ret += "\tctx->extra++;\n" - ret += "\tout->utf8[out->len] = '\\0';\n" - ret += ifdef_pop(1) - ret += "}\n" - ret += ifdef_pop(0) - - # marshal_* ################################################################ - ret += """ -/* marshal_* ******************************************************************/ - -static ALWAYS_INLINE bool _marshal_too_large(struct _marshal_ctx *ctx) { - lib9p_errorf(ctx->ctx, LINUX_ERANGE, "%s too large to marshal into %s limit (limit=%"PRIu32")", - (ctx->net_bytes[4] % 2 == 0) ? "T-message" : "R-message", - ctx->ctx->version ? "negotiated" : ((ctx->net_bytes[4] % 2 == 0) ? "client" : "server"), - ctx->ctx->max_msg_size); - return true; -} - -static ALWAYS_INLINE bool marshal_1(struct _marshal_ctx *ctx, uint8_t *val) { - if (ctx->net_offset + 1 > ctx->ctx->max_msg_size) - return _marshal_too_large(ctx); - ctx->net_bytes[ctx->net_offset] = *val; - ctx->net_offset += 1; - return false; -} - -static ALWAYS_INLINE bool marshal_2(struct _marshal_ctx *ctx, uint16_t *val) { - if (ctx->net_offset + 2 > ctx->ctx->max_msg_size) - return _marshal_too_large(ctx); - encode_u16le(*val, &ctx->net_bytes[ctx->net_offset]); - ctx->net_offset += 2; - return false; -} - -static ALWAYS_INLINE bool marshal_4(struct _marshal_ctx *ctx, uint32_t *val) { - if (ctx->net_offset + 4 > ctx->ctx->max_msg_size) - return true; - encode_u32le(*val, &ctx->net_bytes[ctx->net_offset]); - ctx->net_offset += 4; - return false; -} - -static ALWAYS_INLINE bool marshal_8(struct _marshal_ctx *ctx, uint64_t *val) { - if (ctx->net_offset + 8 > ctx->ctx->max_msg_size) - return true; - encode_u64le(*val, &ctx->net_bytes[ctx->net_offset]); - ctx->net_offset += 8; - return false; -} -""" - for typ in typs: - inline = "FLATTEN" if isinstance(typ, Message) else "ALWAYS_INLINE" - argfn = unused if (isinstance(typ, Struct) and not typ.members) else used - ret += "\n" - ret += ifdef_push(1, c_ver_ifdef(typ.in_versions)) - ret += f"static {inline} bool marshal_{typ.name}(struct _marshal_ctx *{argfn('ctx')}, {c_typename(typ)} *{argfn('val')}) {{\n" - match typ: - case Number(): - ret += f"\treturn marshal_{typ.prim.name}(ctx, ({c_typename(typ.prim)} *)val);\n" - case Bitfield(): - ret += f"\t{c_typename(typ)} masked_val = *val & {typ.name}_masks[ctx->ctx->version];\n" - ret += f"\treturn marshal_{typ.prim.name}(ctx, ({c_typename(typ.prim)} *)&masked_val);\n" - case Struct(): - if len(typ.members) == 0: - ret += "\treturn false;\n" - ret += "}\n" - continue - - # Pass 1 - declare offset variables - mark_offset = set() - for member in typ.members: - if member.val: - if member.name not in mark_offset: - ret += f"\tuint32_t _{member.name}_offset;\n" - mark_offset.add(member.name) - for tok in member.val.tokens: - if isinstance(tok, ExprSym) and tok.name.startswith("&"): - if tok.name[1:] not in mark_offset: - ret += f"\tuint32_t _{tok.name[1:]}_offset;\n" - mark_offset.add(tok.name[1:]) - - # Pass 2 - main pass - ret += "\treturn false\n" - for member in typ.members: - ret += ifdef_push(2, c_ver_ifdef(member.in_versions)) - ret += "\t || " - if member.in_versions != typ.in_versions: - ret += "( " + c_ver_cond(member.in_versions) + " && " - if member.name in mark_offset: - ret += f"({{ _{member.name}_offset = ctx->net_offset; " - if member.cnt: - ret += "({ bool err = false;\n" - ret += f"\t for (typeof(val->{member.cnt}) i = 0; i < val->{member.cnt} && !err; i++)\n" - ret += "\t \terr = " - if typ.name in ["d", "s"]: # SPECIAL - # Special-case is that we cast from `char` to `uint8_t`. - ret += f"marshal_{member.typ.name}(ctx, (uint8_t *)&val->{member.name}[i]);\n" - else: - ret += f"marshal_{member.typ.name}(ctx, &val->{member.name}[i]);\n" - ret += f"\t err; }})" - elif member.val: - # Just increment net_offset, don't actually marsha anything (yet). - assert member.static_size - ret += ( - f"({{ ctx->net_offset += {member.static_size}; false; }})" - ) - else: - ret += f"marshal_{member.typ.name}(ctx, &val->{member.name})" - if member.name in mark_offset: - ret += "; })" - if member.in_versions != typ.in_versions: - ret += " )" - ret += "\n" - - # Pass 3 - marshal ,val= members - for member in typ.members: - if member.val: - assert member.static_size - ret += ifdef_push(2, c_ver_ifdef(member.in_versions)) - ret += f"\t || ({{ encode_u{member.static_size*8}le({c_expr(member.val)}, &ctx->net_bytes[_{member.name}_offset]); false; }})\n" - - ret += ifdef_pop(1) - ret += "\t ;\n" - ret += "}\n" - ret += ifdef_pop(0) - - # tables / exports ######################################################### - ret += f""" -/* tables / exports ***********************************************************/ - -#define _MSG(typ) [{idprefix.upper()}TYP_##typ] = {{ \\ - .name = #typ, \\ - .basesize = sizeof(struct {idprefix}msg_##typ), \\ - .validate = validate_##typ, \\ - .unmarshal = (_unmarshal_fn_t)unmarshal_##typ, \\ - .marshal = (_marshal_fn_t)marshal_##typ, \\ - }} -#define _NONMSG(num) [num] = {{ \\ - .name = #num, \\ - }} - -struct _table_version _{idprefix}versions[{c_ver_enum('NUM')}] = {{ -""" - id2typ: dict[int, Message] = {} - for msg in [msg for msg in typs if isinstance(msg, Message)]: - id2typ[msg.msgid] = msg - - for ver in ["unknown", *sorted(versions)]: - if ver != "unknown": - ret += ifdef_push(1, c_ver_ifdef({ver})) - ret += f"\t[{c_ver_enum(ver)}] = {{ .msgs = {{\n" - - for n in range(0, 0x100): - xmsg: Message | None = id2typ.get(n, None) - if xmsg: - if ver == "unknown": # SPECIAL - if xmsg.name not in ["Tversion", "Rversion", "Rerror"]: - xmsg = None - else: - if ver not in xmsg.in_versions: - xmsg = None - if xmsg: - ret += f"\t\t_MSG({xmsg.name}),\n" - else: - ret += "\t\t_NONMSG(0x{:02X}),\n".format(n) - ret += "\t}},\n" - ret += ifdef_pop(0) - ret += "};\n" - - ret += f""" -FLATTEN bool _{idprefix}validate_stat(struct _validate_ctx *ctx) {{ - return validate_stat(ctx); -}} -FLATTEN void _{idprefix}unmarshal_stat(struct _unmarshal_ctx *ctx, struct lib9p_stat *out) {{ - unmarshal_stat(ctx, out); -}} -FLATTEN bool _{idprefix}marshal_stat(struct _marshal_ctx *ctx, struct lib9p_stat *val) {{ - return marshal_stat(ctx, val); -}} -""" - - ############################################################################ - return ret - - -################################################################################ - - -class Parser: - cache: dict[str, tuple[str, list[Type]]] = {} - - def parse_file(self, filename: str) -> tuple[str, list[Type]]: - filename = os.path.normpath(filename) - if filename not in self.cache: - - def get_include(other_filename: str) -> tuple[str, list[Type]]: - return self.parse_file(os.path.join(filename, "..", other_filename)) - - self.cache[filename] = parse_file(filename, get_include) - return self.cache[filename] - - def all(self) -> tuple[set[str], list[Type]]: - ret_versions: set[str] = set() - ret_typs: dict[str, Type] = {} - for version, typs in self.cache.values(): - if version in ret_versions: - raise ValueError(f"duplicate protocol version {repr(version)}") - ret_versions.add(version) - for typ in typs: - if typ.name in ret_typs: - if typ != ret_typs[typ.name]: - raise ValueError(f"duplicate type name {repr(typ.name)}") - else: - ret_typs[typ.name] = typ - return ret_versions, list(ret_typs.values()) - - -if __name__ == "__main__": - import sys - - if len(sys.argv) < 2: - raise ValueError("requires at least 1 .9p filename") - parser = Parser() - for txtname in sys.argv[1:]: - parser.parse_file(txtname) - versions, typs = parser.all() - outdir = os.path.normpath(os.path.join(sys.argv[0], "..")) - with open(os.path.join(outdir, "include/lib9p/9p.generated.h"), "w") as fh: - fh.write(gen_h(versions, typs)) - with open(os.path.join(outdir, "9p.generated.c"), "w") as fh: - fh.write(gen_c(versions, typs)) diff --git a/lib9p/idl/0000-README.md b/lib9p/idl/0000-README.md index cec27e2..84cf865 100644 --- a/lib9p/idl/0000-README.md +++ b/lib9p/idl/0000-README.md @@ -1,7 +1,7 @@ <!-- - 0000-README.md - Overview of 9P protocol definitions + lib9p/idl/0000-README.md - Overview of 9P protocol definitions - 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 --> @@ -17,35 +17,82 @@ client->server requests, and R-messages are server->client responses type of a message is represented by a u8 ID; T-messages are even and R-messages are odd. -Messages are made up of the primitives; unsigned little-endian -integers, identified with the following single-character mnemonics: +9P messages are exchanged over a reliable bidirectional in-order octet +stream. Messages are made up of the primitives; unsigned +little-endian integers, identified with the following single-character +mnemonics: - 1 = u8 - 2 = u16le - 4 = u32le - 8 = u16le -Out of these primitives, we can make other numeric types, +Out of these primitives, we can make more complex types: + +## User-defined types + +### Numeric types num NUMNAME = PRIMITIVE_TYPE + "NAME=VAL"... + +Besides just being an alias for a primitive type, a numeric type may +define 0 or more named constants of that type, each wrapped in +"quotes". + +### Bitfields + + bitfield BFNAME = PRIMITIVE_TYPE + "bit NBIT=NAME"... + "bit NBIT=reserved(NAME)"... + "bit NBIT=num(NUMNAME)"... + "alias NAME=VAL"... + "mask NAME=VAL"... + "num(NUMNAME) NAME=VAL"... -bitfields, +The same NBIT may not be defined multiple times. The same NAME may +not be defined multiple times. - bitfield BFNAME = PRIMITIVE_TYPE "NBIT=NAME... ALIAS=VAL..." + - A `reserved(...)` bit indicates that the bit is named but is not + allowed to be used. + - `num(...)` bits embed a numeric/enumerated field within a set of + bits. Once several bits have been allocated to a numeric field + with `bit NBIT=num(NUMNAME)`, constant values for that field may be + declared with `num(NUMNAME) NAME=VAL`. For each numeric field, a + `mask NUMNAME=BITMASK` is automatically declared. + - A `mask` defines a bitmask that selects several bits. + - An `alias` defines a convenience alias for a bit or set of bits. -structures, +### Structures - struct STRUCTNAME = "FILENAME[FIELDTYPE]..." + struct STRUCTNAME = "FIELDNAME[FIELDTYPE]..." -and messages (which are a special-case of structures). +Or a special-case for structs that are messages; `msg` has the same +syntax as `struct`, but has restrictions on the STRUCTNAME and the +first 3 fields must all be declared in the same way: msg Tname = "size[4,val=end-&size] typ[1,val=TYP] tag[tag] REST..." Struct fields that have numeric types (either primitives or `num` types) can add to their type `,val=` and/or `,max=` to specify what the exact value must be and/or what the maximum (inclusive) value is. +A field that is repeated a variable number of times be wrapped in +parenthesis and prefixed with the fieldname containing that count: +`OTHERFIELDNAME*(FIELDNAME[FIELDTYPE])`. `,val=` and `,max` take a string of `+`/`-` tokens and values; a value -can either be a decimal numeric constant (eg: `107`), the `&fieldname` -to refer to the offset of a field name in that struct, or the special -value `end` to refer to the offset of the end of the struct. +can be + - a decimal numeric constant (eg: `107`), + - `&fieldname` to refer to the offset of a field name in that struct, + - the special value `end` to refer to the offset of the end of the + struct, + - the special value `u{8,16,32,64}_max` to refer to the constant + value `(1<<{n})-1`, or + - the special value `s{8,16,32,64}_max` to refer to the constant value + `(1<<({n}-1))-1`. + +## Parser + +A parser for this syntax is given in `__init__.py`. However, +`__init__.py` places the somewhat arbitrary undocumented restrictions +on fields referenced as the count of a repeated field. diff --git a/lib9p/idl/0000-TODO.md b/lib9p/idl/0000-TODO.md new file mode 100644 index 0000000..e52902f --- /dev/null +++ b/lib9p/idl/0000-TODO.md @@ -0,0 +1,11 @@ +<!-- + lib9p/idl/0000-TODO.md - Changes I intend to make to idl/__init__.py + and proto.gen + + Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com> + SPDX-License-Identifier: AGPL-3.0-or-later + --> + +- Decide how to handle duplicate type names from different versions +- Decide how to handle duplicate `enum lib9p_msg_type` names and + values diff --git a/lib9p/idl/1992-9P0.9p.wip b/lib9p/idl/1992-9P0.9p.wip index 4278fa3..c9432c9 100644 --- a/lib9p/idl/1992-9P0.9p.wip +++ b/lib9p/idl/1992-9P0.9p.wip @@ -1,9 +1,22 @@ -# 1992-9P0.9p - Definitions of 9P0 (Plan 9 1st ed) messages +# lib9p/idl/1992-9P0.9p - Definitions of 9P0 (Plan 9 1st ed) messages # -# 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 -# https://man.cat-v.org/plan_9_1st_ed/5/ +# The original 9P protocol ("9P0"), from Plan 9 1st edition. +# +# Documentation: +# - https://github.com/plan9foundation/plan9/tree/1e-1993-01-03/sys/man/5 +# - https://github.com/plan9foundation/plan9/tree/1e-1993-01-03/sys/man/6/auth +# - https://man.cat-v.org/plan_9_1st_ed/5/ +# - https://man.cat-v.org/plan_9_1st_ed/6/auth +# +# Implementation references: +# - https://github.com/plan9foundation/plan9/blob/1e-1993-01-03/sys/include/fcall.h (MAXFDATA) +# - https://github.com/plan9foundation/plan9/blob/1e-1993-01-03/sys/include/libc.h (`ch` bits) +# - https://github.com/plan9foundation/plan9/blob/1e-1993-01-03/sys/src/fs/port/fcall.c (`stat`) +# - https://github.com/plan9foundation/plan9/blob/1e-1993-01-03/sys/src/fs/port/fs.c (`offset:max`) +# - https://github.com/plan9foundation/plan9/blob/1e-1993-01-03/sys/src/fs/port/portdata.h (MAXDAT) version "9P0" # tag - identify a request/response pair @@ -15,39 +28,114 @@ num fid = 2 # uni"Q"ue "ID"entification struct qid = "path[4] version[4]" -# a nul-padded string -struct name = 28*(txt[1]) - -msg Tnop = "typ[1,val=TODO] tag[tag,val=0xFFFF]" -msg Rnop = "typ[1,val=TODO] tag[tag,val=0xFFFF]" -msg Tsession = "typ[1,val=TODO] tag[tag,val=0xFFFF]" -msg Rsession = "typ[1,val=TODO] tag[tag,val=0xFFFF]" -msg Rerror = "typ[1,val=TODO] tag[tag] ename[64]" -msg Tflush = "typ[1,val=TODO] tag[tag] oldtag[tag]" -msg Rflush = "typ[1,val=TODO] tag[tag]" -msg Tauth = "typ[1,val=TODO] tag[tag] fid[fid] uid[28] chal[36]" -msg Rauth = "typ[1,val=TODO] tag[tag] fid[fid] chal[30]" -msg Tattach = "typ[1,val=TODO] tag[tag] fid[fid] uid[28] aname[28] auth[28]" -msg Rattach = "typ[1,val=TODO] tag[tag] fid[fid] qid[8]" -msg Tclone = "typ[1,val=TODO] tag[tag] fid[fid] newfid[fid]" -msg Rclone = "typ[1,val=TODO] tag[tag] fid[fid]" -msg Tclwalk = "typ[1,val=TODO] tag[tag] fid[fid] newfid[fid] name[28]" -msg Rclwalk = "typ[1,val=TODO] tag[tag] fid[fid] qid[8]" -msg Twalk = "typ[1,val=TODO] tag[tag] fid[fid] name[28]" -msg Rwalk = "typ[1,val=TODO] tag[tag] fid[fid] qid[8]" -msg Topen = "typ[1,val=TODO] tag[tag] fid[fid] mode[1]" -msg Ropen = "typ[1,val=TODO] tag[tag] fid[fid] qid[8]" -msg Tcreate = "typ[1,val=TODO] tag[tag] fid[fid] name[28] perm[4] mode[1]" -msg Rcreate = "typ[1,val=TODO] tag[tag] fid[fid] qid[8]" -msg Tread = "typ[1,val=TODO] tag[tag] fid[fid] offset[8] count[2,max=8192]" -msg Rread = "typ[1,val=TODO] tag[tag] fid[fid] count[2,max=8192] pad[1] count*(data[1])" -msg Twrite = "typ[1,val=TODO] tag[tag] fid[fid] offset[8] count[2,max=8192] pad[1] count*(data[1])" -msg Rwrite = "typ[1,val=TODO] tag[tag] fid[fid] count[2,max=8192]" -msg Tclunk = "typ[1,val=TODO] tag[tag] fid[fid]" -msg Rclunk = "typ[1,val=TODO] tag[tag] fid[fid]" -msg Tremove = "typ[1,val=TODO] tag[tag] fid[fid]" -msg Rremove = "typ[1,val=TODO] tag[tag] fid[fid]" -msg Tstat = "typ[1,val=TODO] tag[tag] fid[fid]" -msg Rstat = "typ[1,val=TODO] tag[tag] fid[fid] stat[116]" -msg Twstat = "typ[1,val=TODO] tag[tag] fid[fid] stat[116]" -msg Rwstat = "typ[1,val=TODO] tag[tag] fid[fid]" +# a nul-terminated+padded string +struct name = "28*(txt[1])" + +# a nul-terminated+padded string +struct errstr = "64*(txt[1])" + +# "O"pen flags (flags to pass to Topen and Tcreate) +# Unused bits are *ignored*. +bitfield o = 1 + "bit 0=num(MODE)" # low bit of the 2-bit READ/WRITE/RDWR/EXEC enum + "bit 1=num(MODE)" # high bit of the 2-bit READ/WRITE/RDWR/EXEC enum + #"bit 2=unused" + #"bit 3=unused" + "bit 4=TRUNC" + "bit 5=reserved(CEXEC)" # close-on-exec + "bit 6=RCLOSE" # remove-on-close + #"bit 7=unused" + + "num(MODE) READ = 0" # make available for this FID: Tread() + "num(MODE) WRITE = 1" # make available for this FID: Twrite() + "num(MODE) RDWR = 2" # make available for this FID: Tread() and Twrite() + "num(MODE) EXEC = 3" # make available for this FID: Tread() + + "mask FLAG = 0b11111100" + +# "CH"annel flags - file permissions and attributes (a "channel" is +# what a file handle is called inside of the Plan 9 kernel). +bitfield ch = 4 + "bit 31=DIR" + "bit 30=APPEND" + "bit 29=EXCL" + #... + "bit 8=OWNER_R" + "bit 7=OWNER_W" + "bit 6=OWNER_X" + "bit 5=GROUP_R" + "bit 4=GROUP_W" + "bit 3=GROUP_X" + "bit 2=OTHER_R" + "bit 1=OTHER_W" + "bit 0=OTHER_X" + + "mask PERM=0777" # {OWNER,GROUP,OTHER}_{R,W,X} + +struct stat = "file_name[name]" + "file_owner[name]" + "file_group[name]" + "file_qid[qid]" + "file_mode[ch]" + "file_atime[4]" + "file_mtime[4]" + "file_size[8]" + "kern_type[2]" + "kern_dev[2]" + +# Authentication uses symetric-key encryption, using a per-client +# secret-key. The encryption scheme is beyond the scope of this +# document. +struct auth_ticket = "15*(dat[1])" +struct encrypted_auth_challenge = "36*(ciphertext[1])" +struct cleartext_auth_challenge = "magic[1,val=1] 7*(client_challenge[1]) server[name]" +struct encrypted_auth_response = "30*(ciphertext[1])" +struct cleartext_auth_response = "magic[1,val=4] 7*(client_challenge[1]) ticket[auth_ticket]" + +# A 9P0 session goes: +# +# [nop()] +# session() +# [auth_tok=auth()] +# attach([auth_tok]) +# ... + +msg Tmux = "typ[1,val=48] mux[2]" # Undocumented, but implemented by mux(3) / libmux.a +#msg Rmux = "typ[1,val=49] illegal" +msg Tnop = "typ[1,val=50] tag[tag,val=0xFFFF]" +msg Rnop = "typ[1,val=51] tag[tag,val=0xFFFF]" +msg Tsession = "typ[1,val=52] tag[tag,val=0xFFFF]" +msg Rsession = "typ[1,val=53] tag[tag,val=0xFFFF]" +#msg Terror = "typ[1,val=54] illegal" +msg Rerror = "typ[1,val=55] tag[tag] errstr[errstr]" +msg Tflush = "typ[1,val=56] tag[tag] oldtag[tag]" +msg Rflush = "typ[1,val=57] tag[tag]" +msg Tattach = "typ[1,val=58] tag[tag] fid[fid] uid[name] aname[name] auth[auth_ticket] 13*(pad[1])" # Pad to allow auth_tickets up to 28 bytes. +msg Rattach = "typ[1,val=59] tag[tag] fid[fid] qid[qid]" +msg Tclone = "typ[1,val=60] tag[tag] fid[fid] newfid[fid]" +msg Rclone = "typ[1,val=61] tag[tag] fid[fid]" +msg Twalk = "typ[1,val=62] tag[tag] fid[fid] name[name]" +msg Rwalk = "typ[1,val=63] tag[tag] fid[fid] qid[qid]" +msg Topen = "typ[1,val=64] tag[tag] fid[fid] mode[o]" +msg Ropen = "typ[1,val=65] tag[tag] fid[fid] qid[qid]" +msg Tcreate = "typ[1,val=66] tag[tag] fid[fid] name[name] perm[ch] mode[o]" +msg Rcreate = "typ[1,val=67] tag[tag] fid[fid] qid[qid]" +# For `count:max`, see 1e/2e/3e `sys/include/fcall.h:MAXFDATA` or 1e/2e `sys/src/fs/port/portdata.h:MAXDAT`. +# For read `offset:max`, see 1e/2e/3e `sys/src/fs/port/fs.c:f_read()` or 3e `sys/src/lib9p/srv.c:srv():case Tread`. +# For write `offset:max`, see 1e/2e/3e `sys/src/fs/port/fs.c:f_write()`. +msg Tread = "typ[1,val=68] tag[tag] fid[fid] offset[8,max=s64_max] count[2,max=8192]" +msg Rread = "typ[1,val=69] tag[tag] fid[fid] count[2,max=8192] pad[1] count*(data[1])" +msg Twrite = "typ[1,val=70] tag[tag] fid[fid] offset[8,max=s64_max] count[2,max=8192] pad[1] count*(data[1])" +msg Rwrite = "typ[1,val=71] tag[tag] fid[fid] count[2,max=8192]" +msg Tclunk = "typ[1,val=72] tag[tag] fid[fid]" +msg Rclunk = "typ[1,val=73] tag[tag] fid[fid]" +msg Tremove = "typ[1,val=74] tag[tag] fid[fid]" +msg Rremove = "typ[1,val=75] tag[tag] fid[fid]" +msg Tstat = "typ[1,val=76] tag[tag] fid[fid]" +msg Rstat = "typ[1,val=77] tag[tag] fid[fid] stat[stat]" +msg Twstat = "typ[1,val=78] tag[tag] fid[fid] stat[stat]" +msg Rwstat = "typ[1,val=79] tag[tag] fid[fid]" +msg Tclwalk = "typ[1,val=80] tag[tag] fid[fid] newfid[fid] name[name]" +msg Rclwalk = "typ[1,val=81] tag[tag] fid[fid] qid[qid]" +msg Tauth = "typ[1,val=82] tag[tag] fid[fid] uid[name] chal[encrypted_auth_challenge]" # chal is an encrypted +msg Rauth = "typ[1,val=83] tag[tag] fid[fid] chal[encrypted_auth_response]" diff --git a/lib9p/idl/1995-9P1.9p.wip b/lib9p/idl/1995-9P1.9p.wip index 55814d4..660e24a 100644 --- a/lib9p/idl/1995-9P1.9p.wip +++ b/lib9p/idl/1995-9P1.9p.wip @@ -1,52 +1,107 @@ -# 1995-9P1.9p - Definitions of 9P1 (Plan 9 2nd ed and 3rd ed) messages +# lib9p/idl/1995-9P1.9p - Definitions of 9P1 (Plan 9 2nd ed and 3rd ed) messages # -# 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 -# https://man.cat-v.org/plan_9_2nd_ed/5/ -# https://man.cat-v.org/plan_9_3rd_ed/5/ +# Plan 9 2nd and 3rd edition used a version of 9P lightly revised from +# the 1st edition version, re-thinking authentication. +# +# 2nd edition documentation: +# - https://github.com/plan9foundation/plan9/tree/2e-1995-04-05/sys/man/5 +# - https://github.com/plan9foundation/plan9/tree/2e-1995-04-05/sys/man/6/auth +# - https://man.cat-v.org/plan_9_2nd_ed/5/ +# - https://man.cat-v.org/plan_9_2nd_ed/6/auth +# +# 2nd edition implementation references: +# - https://github.com/plan9foundation/plan9/blob/2e-1995-04-05/sys/include/fcall.h (MAXFDATA) +# - https://github.com/plan9foundation/plan9/blob/2e-1995-04-05/sys/include/libc.h (`ch` bits) +# - https://github.com/plan9foundation/plan9/blob/2e-1995-04-05/sys/include/auth.h (auth matic) +# - https://github.com/plan9foundation/plan9/blob/2e-1995-04-05/sys/src/fs/port/fcall.c (`stat`) +# - https://github.com/plan9foundation/plan9/blob/2e-1995-04-05/sys/src/fs/port/fs.c (`offset:max`) +# - https://github.com/plan9foundation/plan9/blob/2e-1995-04-05/sys/src/fs/port/portdata.h (`MAXDAT`) +# - https://github.com/plan9foundation/plan9/blob/2e-1995-04-05/sys/src/libauth/convM2T.c (`auth_ticket`) +# +# 3rd edition documentation: +# - https://github.com/plan9foundation/plan9/tree/3e-2001-03-28/sys/man/5 +# - https://github.com/plan9foundation/plan9/tree/3e-2001-03-28/sys/man/6/auth +# - https://man.cat-v.org/plan_9_3rd_ed/5/ +# - https://man.cat-v.org/plan_9_3rd_ed/6/auth +# +# 3rd edition implementation references: +# - https://github.com/plan9foundation/plan9/blob/3e-2001-03-28/sys/include/fcall.h (MAXFDATA) +# - https://github.com/plan9foundation/plan9/blob/3e-2001-03-28/sys/include/libc.h (`ch` bits) +# - https://github.com/plan9foundation/plan9/blob/3e-2001-03-28/sys/include/auth.h (auth magic) +# - https://github.com/plan9foundation/plan9/blob/3e-2001-03-28/sys/src/fs/port/fcall.c (`stat`) +# - https://github.com/plan9foundation/plan9/blob/3e-2001-03-28/sys/src/fs/port/fs.c (`offset:max`) +# - https://github.com/plan9foundation/plan9/blob/3e-2001-03-28/sys/src/lib9p/srv.c (read `offset:max`) +# - https://github.com/plan9foundation/plan9/blob/3e-2001-03-28/sys/src/libauth/convM2T.c (`auth_ticket`) version "9P1" -# tag - identify a request/response pair -num tag = 2 +from ./1992-9P0.9p import tag, fid, qid, name, errstr, o, ch, stat -# file identifier - like a UNIX file-descriptor -num fid = 2 +# CHMOUNT is undocumented (and is explicitly excluded from the 9P2000 +# draft RFC). As I understand it, CHMOUNT indicates that the file is +# mounted by the kernel as a 9P transport; that the kernel has a lock +# on doing I/O on it, so userspace can't do I/O on it. +bitfield ch += "bit 28=_PLAN9_MOUNT" -# uni"Q"ue "ID"entification -struct qid = "path[4] version[4]" +# Authentication uses DES encryption. The client obtains a ticket and +# a nonce-key from a separate auth-server; how it does this is beyond +# the scope of this document. +struct random = "8*(dat[1])" +struct domain_name = "48*(txt[1])" +struct des_key = "7*(dat[1])" +struct encrypted_ticket = "72*(ciphertext[1])" # encrypted by auth-server with server-key +struct cleartext_ticket = "magic[1,val=64] server_chal[random] client_uid[name] server_uid[name] nonce_key[des_key]" +struct encrypted_authenticator_challenge = "13*(ciphertext[1])" # encrypted by client with nonce-key obtained from auth-server +struct cleartext_authenticator_challenge = "magic[1,val=66] server_chal[random] replay_count[4]" +struct encrypted_authenticator_response = "13*(ciphertext[1])" # encrypted by server with nonce-key obtained from ticket +struct cleartext_authenticator_response = "magic[1,val=67] client_chal[random] replay_count[4]" -# a nul-padded string -struct name = 28*(txt[1]) +# A 9P0 session goes: +# +# [nop()] +# auth_tok=[session()] +# attach(auth_tok) +# ... -msg Tnop = "typ[1,val=TODO] tag[tag,val=0xFFFF]" -msg Rnop = "typ[1,val=TODO] tag[tag,val=0xFFFF]" -msg Tsession = "typ[1,val=TODO] tag[tag,val=0xFFFF] chal[8]" -msg Rsession = "typ[1,val=TODO] tag[tag,val=0xFFFF] chal[8] authid[28] authdom[48]" -msg Rerror = "typ[1,val=TODO] tag[tag] ename[64]" -msg Tflush = "typ[1,val=TODO] tag[tag] oldtag[tag]" -msg Rflush = "typ[1,val=TODO] tag[tag]" -msg Tattach = "typ[1,val=TODO] tag[tag] fid[fid] uid[28] aname[28] ticket[72] auth[13]" -msg Rattach = "typ[1,val=TODO] tag[tag] fid[fid] qid[8] rauth[13]" -msg Tclone = "typ[1,val=TODO] tag[tag] fid[fid] newfid[fid]" -msg Rclone = "typ[1,val=TODO] tag[tag] fid[fid]" -msg Tclwalk = "typ[1,val=TODO] tag[tag] fid[fid] newfid[fid] name[28]" -msg Rclwalk = "typ[1,val=TODO] tag[tag] fid[fid] qid[8]" -msg Twalk = "typ[1,val=TODO] tag[tag] fid[fid] name[28]" -msg Rwalk = "typ[1,val=TODO] tag[tag] fid[fid] qid[8]" -msg Topen = "typ[1,val=TODO] tag[tag] fid[fid] mode[1]" -msg Ropen = "typ[1,val=TODO] tag[tag] fid[fid] qid[8]" -msg Tcreate = "typ[1,val=TODO] tag[tag] fid[fid] name[28] perm[4] mode[1]" -msg Rcreate = "typ[1,val=TODO] tag[tag] fid[fid] qid[8]" -msg Tread = "typ[1,val=TODO] tag[tag] fid[fid] offset[8] count[2,max=8192]" -msg Rread = "typ[1,val=TODO] tag[tag] fid[fid] count[2,max=8192] pad[1] count*(data[1])" -msg Twrite = "typ[1,val=TODO] tag[tag] fid[fid] offset[8] count[2,max=8192] pad[1] count*(data[1])" -msg Rwrite = "typ[1,val=TODO] tag[tag] fid[fid] count[2,max=8192]" -msg Tclunk = "typ[1,val=TODO] tag[tag] fid[fid]" -msg Rclunk = "typ[1,val=TODO] tag[tag] fid[fid]" -msg Tremove = "typ[1,val=TODO] tag[tag] fid[fid]" -msg Rremove = "typ[1,val=TODO] tag[tag] fid[fid]" -msg Tstat = "typ[1,val=TODO] tag[tag] fid[fid]" -msg Rstat = "typ[1,val=TODO] tag[tag] fid[fid] stat[116]" -msg Twstat = "typ[1,val=TODO] tag[tag] fid[fid] stat[116]" -msg Rwstat = "typ[1,val=TODO] tag[tag] fid[fid]" +#from ./1992-9P0.9p import Tmux # typ=48 ; removed +#from ./1992-9P0.9p import Rmux # typ=49 ; removed +from ./1992-9P0.9p import Tnop # typ=50 +from ./1992-9P0.9p import Rnop # typ=51 +#from ./1992-9P0.9p import Tsession # typ=52 ; revised, now has typ=84 +#from ./1992-9P0.9p import Rsession # typ=53 ; revised, now has typ=85 +#from ./1992-9P0.9p import Terror # typ=54 ; never existed +from ./1992-9P0.9p import Rerror # typ=55 +from ./1992-9P0.9p import Tflush # typ=56 +from ./1992-9P0.9p import Rflush # typ=57 +#from ./1992-9P0.9p import Tattach # typ=58 ; revised, now has typ=86 +#from ./1992-9P0.9p import Rattach # typ=59 ; revised, now has typ=87 +from ./1992-9P0.9p import Tclone # typ=60 +from ./1992-9P0.9p import Rclone # typ=61 +from ./1992-9P0.9p import Twalk # typ=62 +from ./1992-9P0.9p import Rwalk # typ=63 +from ./1992-9P0.9p import Topen # typ=64 +from ./1992-9P0.9p import Ropen # typ=65 +from ./1992-9P0.9p import Tcreate # typ=66 +from ./1992-9P0.9p import Rcreate # typ=67 +from ./1992-9P0.9p import Tread # typ=68 +from ./1992-9P0.9p import Rread # typ=69 +from ./1992-9P0.9p import Twrite # typ=70 +from ./1992-9P0.9p import Rwrite # typ=71 +from ./1992-9P0.9p import Tclunk # typ=72 +from ./1992-9P0.9p import Rclunk # typ=73 +from ./1992-9P0.9p import Tremove # typ=74 +from ./1992-9P0.9p import Rremove # typ=75 +from ./1992-9P0.9p import Tstat # typ=76 +from ./1992-9P0.9p import Rstat # typ=77 +from ./1992-9P0.9p import Twstat # typ=78 +from ./1992-9P0.9p import Rwstat # typ=79 +from ./1992-9P0.9p import Tclwalk # typ=80 +from ./1992-9P0.9p import Rclwalk # typ=81 +#from ./1992-9P0.9p import Tauth # typ=82 ; merged into Tsession +#from ./1992-9P0.9p import Rauth # typ=83 ; merged into Rsession +msg Tsession = "typ[1,val=84] tag[tag,val=0xFFFF] chal[random]" +msg Rsession = "typ[1,val=85] tag[tag,val=0xFFFF] chal[random] server_name[name] server_domain[domain_name]" +msg Tattach = "typ[1,val=86] tag[tag] fid[fid] uid[name] aname[name] ticket[encrypted_ticket] auth[encrypted_authenticator_challenge]" +msg Rattach = "typ[1,val=87] tag[tag] fid[fid] qid[qid] rauth[encrypted_authenticator_response]" diff --git a/lib9p/idl/1996-Styx.9p.wip b/lib9p/idl/1996-Styx.9p.wip index 2feb24f..143be83 100644 --- a/lib9p/idl/1996-Styx.9p.wip +++ b/lib9p/idl/1996-Styx.9p.wip @@ -1,15 +1,59 @@ -# 1996-Styx.9p - Definitions of Styx messages +# lib9p/idl/1996-Styx.9p - Definitions of Styx messages # -# 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 # Styx was a variant of the 9P protocol used by the Inferno operating -# system. Message framing looks like 9P1 (1995), but semantics look -# more like 9P2000 (2002). I am not sure whether there are Styx -# protocol differences between Inferno 1e, 2e, or 3e (4e adopted -# 9P2000). -# -# - 1996 beta -# - 1997 1.0 -# - 1999 2nd ed -# - 2001 3rd ed +# system (before it switched to 9P2000 in 4th edition). It looks +# exactly like 9P1 but with different message-type numbers and without +# `clwalk` or `session`, and no authentication in `attach`. +# +# There do not appear to be Styx protocol differences between Inferno +# 1e, 2e, or 3e. +# +# - 1996 beta https://github.com/inferno-os/inferno-1e0/blob/main/ see `man/html/proto*.htm`, `include/styx.h`, and `Linux/386/include/lib9.h` +# - 1997 1e https://github.com/inferno-os/inferno-1e1/blob/master/ see `man/html/mpgs{113..124}.htm`, `include/styx.h`, `os/port/lib.h`, and `os/fs/fs.c` +# - 1999 2e https://github.com/inferno-os/inferno-2e/blob/master/ see `include/styx.h`, `os/port/lib.h`, and `os/kfs/fs.c` (no public manpages) +# - 2001 3e https://github.com/inferno-os/inferno-3e/blob/master/ see `include/man/5/`, `include/styx.h`, `os/port/lib.h`, and `os/kfs/fs.c` +version "Styx" + +from ./1992-9P1.9p import tag, fid, qid, name, errstr, o, ch, stat + +# A Styx session goes: +# +# [nop()] +# attach() +# ... + +msg Tnop = "typ[1,val=0] tag[tag,val=0xFFFF]" +msg Rnop = "typ[1,val=1] tag[tag,val=0xFFFF]" +#msg Terror = "typ[1,val=2] illegal" +msg Rerror = "typ[1,val=3] tag[tag] errstr[errstr]" +msg Tflush = "typ[1,val=4] tag[tag] oldtag[tag]" +msg Rflush = "typ[1,val=5] tag[tag]" +msg Tclone = "typ[1,val=6] tag[tag] fid[fid] newfid[fid]" +msg Rclone = "typ[1,val=7] tag[tag] fid[fid]" +msg Twalk = "typ[1,val=8] tag[tag] fid[fid] name[name]" +msg Rwalk = "typ[1,val=9] tag[tag] fid[fid] qid[qid]" +msg Topen = "typ[1,val=10] tag[tag] fid[fid] mode[o]" +msg Ropen = "typ[1,val=11] tag[tag] fid[fid] qid[qid]" +msg Tcreate = "typ[1,val=12] tag[tag] fid[fid] name[name] perm[ch] mode[o]" +msg Rcreate = "typ[1,val=13] tag[tag] fid[fid] qid[qid]" +# For `offset:max`, see `fs.c` `f_read()` and `f_write()`. +# For `count:max`, see `styx.h:MAXFDATA`. +msg Tread = "typ[1,val=14] tag[tag] fid[fid] offset[8,max=s64_max] count[2,max=8192]" +msg Rread = "typ[1,val=15] tag[tag] fid[fid] count[2,max=8192] pad[1] count*(data[1])" +msg Twrite = "typ[1,val=16] tag[tag] fid[fid] offset[8,max=s64_max] count[2,max=8192] pad[1] count*(data[1])" +msg Rwrite = "typ[1,val=17] tag[tag] fid[fid] count[2,max=8192]" +msg Tclunk = "typ[1,val=18] tag[tag] fid[fid]" +msg Rclunk = "typ[1,val=19] tag[tag] fid[fid]" +msg Tremove = "typ[1,val=20] tag[tag] fid[fid]" +msg Rremove = "typ[1,val=21] tag[tag] fid[fid]" +msg Tstat = "typ[1,val=22] tag[tag] fid[fid]" +msg Rstat = "typ[1,val=23] tag[tag] fid[fid] stat[stat]" +msg Twstat = "typ[1,val=24] tag[tag] fid[fid] stat[stat]" +msg Rwstat = "typ[1,val=25] tag[tag] fid[fid]" +#msg Tsession = "typ[1,val=26]" # The 1e kernel used Tsession in structs internally, but never transmitted it. +#msg Rsession = "typ[1,val=27]" # Implied by Tsession. +msg Tattach = "typ[1,val=28] tag[tag] fid[fid] uid[name] aname[name]" +msg Rattach = "typ[1,val=29] tag[tag] fid[fid] qid[qid]" diff --git a/lib9p/idl/2002-9P2000.9p b/lib9p/idl/2002-9P2000.9p index 03f00c2..13393c6 100644 --- a/lib9p/idl/2002-9P2000.9p +++ b/lib9p/idl/2002-9P2000.9p @@ -1,6 +1,6 @@ -# 2002-9P2000.9p - Definitions of 9P2000 messages +# lib9p/idl/2002-9P2000.9p - Definitions of 9P2000 messages # -# 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 # "9P2000" base protocol @@ -9,74 +9,77 @@ # # But due to incompleteness of the draft RFC, the Plan 9 manual # section-5 and the Plan 9 headers (particularly fcall.h) are often -# better references. +# better references. The s{32,64}_max limitations are not documented +# in the draft RFC or the manual pages, but have been enforced by +# lib9p/srv.c in every release of Plan 9 4e. # # https://github.com/plan9foundation/plan9/tree/main/sys/man/5 # https://man.cat-v.org/plan_9/5/ # https://github.com/plan9foundation/plan9/blob/main/sys/include/fcall.h +# https://github.com/plan9foundation/plan9/blob/main/sys/include/libc.h +# https://github.com/plan9foundation/plan9/blob/main/sys/src/lib9p/srv.c version "9P2000" # tag - identify a request/response pair num tag = 2 + "NOTAG = u16_max" # file identifier - like a UNIX file-descriptor num fid = 4 - -# data - u32le `n`, then `n` bytes of data -struct d = "len[4] len*(dat[1])" + "NOFID = u32_max" # string - u16le `n`, then `n` bytes of UTF-8, without any nul-bytes struct s = "len[2] len*(utf8[1])" -# "d"? mode - file permissions and attributes +# "D"ir-entry "M"ode - file permissions and attributes bitfield dm = 4 - "31=DIR" - "30=APPEND" - "29=EXCL" - # DMMOUNT has been around in Plan 9 forever, but is - # undocumented, and is explicitly excluded from the 9P2000 - # draft RFC. As I understand it, DMMOUNT indicates that the - # file is mounted by the kernel as a 9P transport; that the - # kernel has a lock on doing I/O on it, so userspace can't do - # I/O on it. - "28=_PLAN9_MOUNT" - "27=AUTH" - "26=TMP" + "bit 31=DIR" + "bit 30=APPEND" + "bit 29=EXCL" + # DMMOUNT has been around in Plan 9 since 2e (CHMOUNT in <4e), + # but is undocumented, and is explicitly excluded from the + # 9P2000 draft RFC. As I understand it, DMMOUNT indicates + # that the file is mounted by the kernel as a 9P transport; + # that the kernel has a lock on doing I/O on it, so userspace + # can't do I/O on it. + "bit 28=_PLAN9_MOUNT" + "bit 27=AUTH" + "bit 26=TMP" #... - "8=OWNER_R" - "7=OWNER_W" - "6=OWNER_X" - "5=GROUP_R" - "4=GROUP_W" - "3=GROUP_X" - "2=OTHER_R" - "1=OTHER_W" - "0=OTHER_X" + "bit 8=OWNER_R" + "bit 7=OWNER_W" + "bit 6=OWNER_X" + "bit 5=GROUP_R" + "bit 4=GROUP_W" + "bit 3=GROUP_X" + "bit 2=OTHER_R" + "bit 1=OTHER_W" + "bit 0=OTHER_X" - "PERM_MASK=0777" # {OWNER,GROUP,OTHER}_{R,W,X} + "mask PERM=0777" # {OWNER,GROUP,OTHER}_{R,W,X} # QID Type - see `struct qid` below bitfield qt = 1 - "7=DIR" - "6=APPEND" - "5=EXCL" - "4=_PLAN9_MOUNT" # see "MOUNT" in "dm" above - "3=AUTH" + "bit 7=DIR" + "bit 6=APPEND" + "bit 5=EXCL" + "bit 4=_PLAN9_MOUNT" # See "_PLAN9_MOUNT" in "dm" above. + "bit 3=AUTH" # Fun historical fact: QTTMP was a relatively late addition to # Plan 9, in 2003-12. - "2=TMP" - #"1=unused" + "bit 2=TMP" + #"bit 1=unused" # "The name QTFILE, defined to be zero, identifies the value # of the type for a plain file." - "FILE=0" + "alias FILE=0" # uni"Q"ue "ID"entification - "two files on the same server hierarchy # are the same if and only if their qids are the same" # # - "path" is a unique uint64_t that does most of the work in the # above statement about files being the same if their QIDs are the -# same; " If a file is deleted and recreated with the same name in +# same; "If a file is deleted and recreated with the same name in # the same directory, the old and new path components of the qids # should be different" # @@ -102,23 +105,30 @@ struct stat = "stat_size[2,val=end-&kern_type]" "file_last_modified_uid[s]" # "O"pen flags (flags to pass to Topen and Tcreate) +# Unused bits *must* be 0. bitfield o = 1 - "0=mode_0" # low bit of the 2-bit READ/WRITE/RDWR/EXEC enum - "1=mode_1" # high bit of the 2-bit READ/WRITE/RDWR/EXEC enum" - #"2=unused" - #"3=unused" - "4=TRUNC" - #"5=unused" - "6=RCLOSE" # remove-on-close - #"7=unused" + "bit 0=num(MODE)" # low bit of the 2-bit READ/WRITE/RDWR/EXEC enum + "bit 1=num(MODE)" # high bit of the 2-bit READ/WRITE/RDWR/EXEC enum + #"bit 2=unused" + #"bit 3=unused" + "bit 4=TRUNC" + "bit 5=reserved(CEXEC)" # close-on-exec + "bit 6=RCLOSE" # remove-on-close + #"bit 7=unused" + + "num(MODE) READ = 0" # make available for this FID: Tread() + "num(MODE) WRITE = 1" # make available for this FID: Twrite() + "num(MODE) RDWR = 2" # make available for this FID: Tread() and Twrite() + "num(MODE) EXEC = 3" # make available for this FID: Tread() - "READ = 0" # make available for this FID: Tread() - "WRITE = 1" # make available for this FID: Twrite() - "RDWR = 2" # make available for this FID: Tread() and Twrite() - "EXEC = 3" # make available for this FID: Tread() + "mask FLAG = 0b11111100" - "MODE_MASK = 0b00000011" - "FLAG_MASK = 0b11111100" +# A 9P2000 session goes: +# +# version() +# [auth_fid=auth] +# attach([auth_fid]) +# ... msg Tversion = "size[4,val=end-&size] typ[1,val=100] tag[tag] max_msg_size[4] version[s]" msg Rversion = "size[4,val=end-&size] typ[1,val=101] tag[tag] max_msg_size[4] version[s]" @@ -127,7 +137,7 @@ msg Rauth = "size[4,val=end-&size] typ[1,val=103] tag[tag] aqid[qid]" msg Tattach = "size[4,val=end-&size] typ[1,val=104] tag[tag] fid[fid] afid[fid] uname[s] aname[s]" msg Rattach = "size[4,val=end-&size] typ[1,val=105] tag[tag] qid[qid]" #msg Terror = "size[4,val=end-&size] typ[1,val=106] tag[tag] illegal" -msg Rerror = "size[4,val=end-&size] typ[1,val=107] tag[tag] ename[s]" +msg Rerror = "size[4,val=end-&size] typ[1,val=107] tag[tag] errstr[s]" msg Tflush = "size[4,val=end-&size] typ[1,val=108] tag[tag] oldtag[2]" msg Rflush = "size[4,val=end-&size] typ[1,val=109] tag[tag]" msg Twalk = "size[4,val=end-&size] typ[1,val=110] tag[tag] fid[fid] newfid[fid] nwname[2,max=16] nwname*(wname[s])" @@ -136,10 +146,10 @@ msg Topen = "size[4,val=end-&size] typ[1,val=112] tag[tag] fid[fid] mode[o]" msg Ropen = "size[4,val=end-&size] typ[1,val=113] tag[tag] qid[qid] iounit[4]" msg Tcreate = "size[4,val=end-&size] typ[1,val=114] tag[tag] fid[fid] name[s] perm[dm] mode[o]" msg Rcreate = "size[4,val=end-&size] typ[1,val=115] tag[tag] qid[qid] iounit[4]" -msg Tread = "size[4,val=end-&size] typ[1,val=116] tag[tag] fid[fid] offset[8] count[4]" -msg Rread = "size[4,val=end-&size] typ[1,val=117] tag[tag] data[d]" # for directories, `data` is the sequence "cnt*(entries[stat])" -msg Twrite = "size[4,val=end-&size] typ[1,val=118] tag[tag] fid[fid] offset[8] data[d]" -msg Rwrite = "size[4,val=end-&size] typ[1,val=119] tag[tag] count[4]" +msg Tread = "size[4,val=end-&size] typ[1,val=116] tag[tag] fid[fid] offset[8,max=s64_max] count[4,max=s32_max]" # See 4e `sys/src/lib9p/srv.c:sread()` for `offset:max` and `count:max`. +msg Rread = "size[4,val=end-&size] typ[1,val=117] tag[tag] count[4,max=s32_max] count*(data[1])" # `max` is inherited from Tread, for directories `data` is the sequence "cnt*(entries[stat])". +msg Twrite = "size[4,val=end-&size] typ[1,val=118] tag[tag] fid[fid] offset[8,max=s64_max] count[4,max=s32_max] count*(data[1])" # See 4e `sys/src/lib9p/srv.c:swrite()` for `offset:max` and `count:max`. +msg Rwrite = "size[4,val=end-&size] typ[1,val=119] tag[tag] count[4,max=s32_max]" # `max` is inherited from Twrite. msg Tclunk = "size[4,val=end-&size] typ[1,val=120] tag[tag] fid[fid]" msg Rclunk = "size[4,val=end-&size] typ[1,val=121] tag[tag]" msg Tremove = "size[4,val=end-&size] typ[1,val=122] tag[tag] fid[fid]" diff --git a/lib9p/idl/2003-9P2000.p9p.9p b/lib9p/idl/2003-9P2000.p9p.9p new file mode 100644 index 0000000..3f6a524 --- /dev/null +++ b/lib9p/idl/2003-9P2000.p9p.9p @@ -0,0 +1,49 @@ +# lib9p/idl/2003-9P2000.p9p.9p - Definitions of plan9port extension messages +# +# Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com> +# SPDX-License-Identifier: AGPL-3.0-or-later + +# Plan 9 from User Space (a.k.a. plan9port)'s lib9pclient:fsopenfd(3) +# and 9pserve(4) proxy server (which takes the place of the Plan 9 +# kernel) add a special-purpose `openfd` command to 9P2000. +# +# https://9fans.github.io/plan9port/man/man9/openfd.htlm +# https://github.com/9fans/plan9port/blob/master/man/man9/openfd.9p +# https://github.com/9fans/plan9port/commit/32f69c36e0eec1227934bbd34854bfebd88686f2 +# https://github.com/9fans/plan9port/pull/692 + +# BUG: There is no version-string for this extension; plan9port still +# calls it vanilla "9P2000". +version "9P2000.p9p" + +from ./2002-9P2000.9p import * + +# On Plan 9 the usual 9P client is the kernel, not normal userspace +# programs. The kernel multiplexes multiple userspace processes onto +# a single 9P connection; the userspace programs make 9P calls by +# making syscalls. plan9port emulates this by having mock syscalls +# that make 9P client calls over an AF_UNIX socket to a local +# `9pserve` daemon that does the multiplexing (you add an "fs" prefix; +# e.g. you replace syscall:`open()` with lib9pclient:`fsopen()`). +# +# "Unfortunately", programs in plan9port must deal both with 9P files +# and native "Unix" files; and need to turn an 9P FID into a native +# file descriptor. To do this, the `9pserve` program and lib9pclient +# add an extension call to 9P2000: Topenfd/Ropenfd/fsopenfd(). +# +# An AF_UNIX socket has the ability to send a file descriptor over it +# via an out-of-band "socket control message" ("CMSG" or "SCM"). +# +# Topenfd asks 9pserve to create a socketpair() file descriptor that +# 9pserve will pump to/from a FID, and then send that pipe file +# descriptor over a control-message to the client program. +# +# When replying, the server sends not just an in-band Ropenfd message, +# but also an out-of-band control-message with a socketpair() file +# descriptor. A successful call results in the FID being clunked. +msg Topenfd = "size[4,val=end-&size] typ[1,val=98] tag[tag] fid[fid] mode[o]" +msg Ropenfd = "size[4,val=end-&size] typ[1,val=99] tag[tag] qid[qid] iounit[4] unixfd[4]" +# BUG: The "unixfd" field nominally indicates the the file descriptor +# of the pipe, but really 9pserve doesn't know which FD it will end up +# on the client process, and lib9pclient ignores the value here and +# overwrites it with the file descriptor indicated from the CMSG diff --git a/lib9p/idl/2005-9P2000.u.9p b/lib9p/idl/2005-9P2000.u.9p index 8b59efa..1d630f9 100644 --- a/lib9p/idl/2005-9P2000.u.9p +++ b/lib9p/idl/2005-9P2000.u.9p @@ -1,6 +1,6 @@ -# 2005-9P2000.u.9p - Definitions of 9P2000.u messages +# lib9p/idl/2005-9P2000.u.9p - Definitions of 9P2000.u messages # -# 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 # "9P2000.u" Unix extension @@ -10,20 +10,27 @@ version "9P2000.u" from ./2002-9P2000.9p import * +# numeric user ID +num nuid = 4 + "NONUID = u32_max" + +num errno = 4 + "NOERROR = 0" + struct stat += "file_extension[s]" - "file_owner_n_uid[4]" - "file_owner_n_gid[4]" - "file_last_modified_n_uid[4]" + "file_owner_n_uid[nuid]" + "file_owner_n_gid[nuid]" + "file_last_modified_n_uid[nuid]" -msg Tauth += "n_uname[4]" -msg Tattach += "n_uname[4]" +msg Tauth += "n_uid[nuid]" +msg Tattach += "n_uid[nuid]" -msg Rerror += "errno[4]" +msg Rerror += "errnum[errno]" -bitfield dm += "23=DEVICE" - "21=NAMEDPIPE" - "20=SOCKET" - "19=SETUID" - "18=SETGID" +bitfield dm += "bit 23=DEVICE" + "bit 21=PIPE" + "bit 20=SOCKET" + "bit 19=SETUID" + "bit 18=SETGID" -bitfield qt += "1=SYMLINK" +bitfield qt += "bit 1=SYMLINK" diff --git a/lib9p/idl/2010-9P2000.L.9p b/lib9p/idl/2010-9P2000.L.9p new file mode 100644 index 0000000..652660c --- /dev/null +++ b/lib9p/idl/2010-9P2000.L.9p @@ -0,0 +1,230 @@ +# lib9p/idl/2010-9P2000.L.9p - Definitions of 9P2000.L messages +# +# Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com> +# SPDX-License-Identifier: AGPL-3.0-or-later + +# "9P2000.L" Linux extension +# https://github.com/chaos/diod/blob/master/protocol.md +# https://github.com/chaos/diod/blob/master/src/libnpfs/protocol.h +version "9P2000.L" + +from ./2002-9P2000.9p import tag, fid, s, qt, qid +from ./2002-9P2000.9p import Rerror +from ./2002-9P2000.9p import Tversion, Rversion, Tflush, Rflush, Twalk, Rwalk, Tread, Rread, Twrite, Rwrite, Tclunk, Rclunk, Tremove, Rremove +from ./2005-9P2000.u.9p import nuid, errno, Tauth, Rauth, Tattach, Rattach + +#num errno += # TODO + +num super_magic = 4 + # See <linux/magic.h> (linux.git include/uapi/linux/magic.h). + # + # To quote `util-linux.git:include/statfs_magic.h`: + # "Unfortunately, Linux kernel header file <linux/magic.h> is + # incomplete mess and kernel returns by statfs f_type many numbers + # that are nowhere specified (in API)." + # + # util-linux <statfs_magic.h> is also incomplete. As is the + # statfs(2) man-page. + # + # I'm working on a patchset to the kernel to get <linux/magic.h> + # to be complete, but in the mean-time I'm just not going to + # bother with putting a list here. + # + # TODO + "V9FS_MAGIC=0x01021997" + +# "L"inux "O"pen flags (flags to pass to Tlopen and Tlcreate) +# +# The values are not specified in in protocol.md, but are specified in +# protocol.h (and are different than the Linux kernel's values, which +# vary by architecture). +bitfield lo = 4 + "bit 0=num(MODE)" # low bit of the 2-bit RDONLY/WRONLY/RDWR/NOACCESS enum + "bit 1=num(MODE)" # high bit of the 2-bit RDONLY/WRONLY/RDWR/NOACCESS enum + #"bit 2=unused" + #"bit 3=unused" + #"bit 4=unused" + #"bit 5=unused" + "bit 6=CREATE" + "bit 7=EXCL" + "bit 8=NOCTTY" + "bit 9=TRUNC" + "bit 10=APPEND" + "bit 11=NONBLOCK" + "bit 12=DSYNC" + "bit 13=BSD_FASYNC" + "bit 14=DIRECT" + "bit 15=LARGEFILE" + "bit 16=DIRECTORY" + "bit 17=NOFOLLOW" + "bit 18=NOATIME" + "bit 19=CLOEXEC" + "bit 20=SYNC" + + "num(MODE) RDONLY = 0" + "num(MODE) WRONLY = 1" + "num(MODE) RDWR = 2" + "num(MODE) NOACCESS = 3" + + "mask FLAG = 0b111111111111111000000" + +# "D"irentry "T"ype +# +# These match the Linux kernel's values. +num dt = 1 + "UNKNOWN = 0" + "PIPE = 1" + "CHAR_DEV = 2" + "DIRECTORY = 4" + "BLOCK_DEV = 6" # proof it's not a bitfield + "REGULAR = 8" + "SYMLINK = 10" # proof it's not a bitfield + "SOCKET = 12" # proof it's not a bitfield + "_WHITEOUT = 14" # proof it's not a bitfield + +# Mode +# +# These match the Linux kernel's values. Why is this 32-bits wide +# instead of just 16? Who knows? +bitfield mode = 4 + #... + "bit 15=num(FMT)" # bit of the 4-bit FMT_ enum + "bit 14=num(FMT)" # bit of the 4-bit FMT_ enum + "bit 13=num(FMT)" # bit of the 4-bit FMT_ enum + "bit 12=num(FMT)" # bit of the 4-bit FMT_ enum + #... + "bit 11=PERM_SETGROUP" + "bit 10=PERM_SETUSER" + "bit 9=PERM_STICKY" + "bit 8=PERM_OWNER_R" + "bit 7=PERM_OWNER_W" + "bit 6=PERM_OWNER_X" + "bit 5=PERM_GROUP_R" + "bit 4=PERM_GROUP_W" + "bit 3=PERM_GROUP_X" + "bit 2=PERM_OTHER_R" + "bit 1=PERM_OTHER_W" + "bit 0=PERM_OTHER_X" + + "num(FMT) PIPE = dt.PIPE<<12" + "num(FMT) CHAR_DEV = dt.CHAR_DEV<<12" + "num(FMT) DIRECTORY = dt.DIRECTORY<<12" + "num(FMT) BLOCK_DEV = dt.BLOCK_DEV<<12" + "num(FMT) REGULAR = dt.REGULAR<<12" + "num(FMT) SYMLINK = dt.SYMLINK<<12" + "num(FMT) SOCKET = dt.SOCKET<<12" + + "mask PERM = 07777" # PERM_* + +# A boolean value that is for some reason 4 bytes wide. +num b4 = 4 + "FALSE=0" + "TRUE=1" + # all other values are true also + +bitfield getattr = 8 + "bit 0=MODE" + "bit 1=NLINK" + "bit 2=UID" + "bit 3=GID" + "bit 4=RDEV" + "bit 5=ATIME" + "bit 6=MTIME" + "bit 7=CTIME" + "bit 8=INO" + "bit 9=SIZE" + "bit 10=BLOCKS" + + "bit 11=BTIME" + "bit 12=GEN" + "bit 13=DATA_VERSION" + + "alias BASIC=0x000007ff" # Mask for fields up to BLOCKS + "alias ALL =0x00003fff" # Mask for All fields above + +bitfield setattr = 4 + "bit 0=MODE" + "bit 1=UID" + "bit 2=GID" + "bit 3=SIZE" + "bit 4=ATIME" + "bit 5=MTIME" + "bit 6=CTIME" + "bit 7=ATIME_SET" + "bit 8=MTIME_SET" + +num lock_type = 1 + "RDLCK=0" + "WRLCK=1" + "UNLCK=2" + +bitfield lock_flags = 4 + "bit 0=BLOCK" + "bit 1=RECLAIM" + +num lock_status = 1 + "SUCCESS=0" + "BLOCKED=1" + "ERROR=2" + "GRACE=3" + +#msg Tlerror = "size[4,val=end-&size] typ[1,val=6] tag[tag] illegal" # analogous to 106/Terror +msg Rlerror = "size[4,val=end-&size] typ[1,val=7] tag[tag] errnum[errno]" # analogous to 107/Rerror +msg Tstatfs = "size[4,val=end-&size] typ[1,val=8] tag[tag] fid[fid]" +msg Rstatfs = "size[4,val=end-&size] typ[1,val=9] tag[tag]" # Description | statfs | statvfs + "type[super_magic]" # Type of filesystem | f_type | - + "bsize[4]" # Block size in bytes | f_bsize | f_bsize + # - # Fragment size in bytes | f_frsize (since Linux 2.6) | f_frsize + "blocks[8]" # Size of FS in f_frsize units | f_blocks | f_blocks + "bfree[8]" # Number of free blocks | f_bfree | f_bfree + "bavail[8]" # Number of free blocks for unprivileged users | f_bavail | b_avail + "files[8]" # Number of inodes | f_files | f_files + "ffree[8]" # Number of free inodes | f_ffree | f_ffree + # - # Number of free inodes for unprivileged users | - | f_favail + "fsid[8]" # Filesystem instance ID | f_fsid | f_fsid + # - # Mount flags | f_flags (since Linux 2.6.36) | f_flag + "namelen[4]" # Maximum filename length | f_namemax | f_namemax +msg Tlopen = "size[4,val=end-&size] typ[1,val=12] tag[tag] fid[fid] flags[lo]" # analogous to 112/Topen +msg Rlopen = "size[4,val=end-&size] typ[1,val=13] tag[tag] qid[qid] iounit[4]" # analogous to 113/Ropen +msg Tlcreate = "size[4,val=end-&size] typ[1,val=14] tag[tag] fid[fid] name[s] flags[lo] mode[mode] gid[nuid]" # analogous to 114/Tcreate +msg Rlcreate = "size[4,val=end-&size] typ[1,val=15] tag[tag] qid[qid] iounit[4]" # analogous to 115/Rcreate +msg Tsymlink = "size[4,val=end-&size] typ[1,val=16] tag[tag] fid[fid] name[s] symtgt[s] gid[nuid]" +msg Rsymlink = "size[4,val=end-&size] typ[1,val=17] tag[tag] qid[qid]" +msg Tmknod = "size[4,val=end-&size] typ[1,val=18] tag[tag] dfid[fid] name[s] mode[mode] major[4] minor[4] gid[nuid]" +msg Rmknod = "size[4,val=end-&size] typ[1,val=19] tag[tag] qid[qid]" +msg Trename = "size[4,val=end-&size] typ[1,val=20] tag[tag] fid[fid] dfid[fid] name[s]" +msg Rrename = "size[4,val=end-&size] typ[1,val=21] tag[tag]" +msg Treadlink = "size[4,val=end-&size] typ[1,val=22] tag[tag] fid[fid]" +msg Rreadlink = "size[4,val=end-&size] typ[1,val=23] tag[tag] target[s]" +msg Tgetattr = "size[4,val=end-&size] typ[1,val=24] tag[tag] fid[fid] request_mask[getattr]" +msg Rgetattr = "size[4,val=end-&size] typ[1,val=25] tag[tag] valid[getattr] qid[qid] mode[mode] uid[nuid] gid[nuid] nlink[8]" + "rdev[8] filesize[8] blksize[8] blocks[8]" + "atime_sec[8] atime_nsec[8] mtime_sec[8] mtime_nsec[8]" + "ctime_sec[8] ctime_nsec[8] btime_sec[8] btime_nsec[8]" + "gen[8] data_version[8]" +msg Tsetattr = "size[4,val=end-&size] typ[1,val=26] tag[tag] fid[fid] valid[setattr] mode[mode] uid[nuid] gid[nuid] filesize[8] atime_sec[8] atime_nsec[8] mtime_sec[8] mtime_nsec[8]" +msg Rsetattr = "size[4,val=end-&size] typ[1,val=27] tag[tag]" +#... +msg Txattrwalk = "size[4,val=end-&size] typ[1,val=30] tag[tag] fid[fid] newfid[fid] name[s]" +msg Rxattrwalk = "size[4,val=end-&size] typ[1,val=31] tag[tag] attr_size[8]" +msg Txattrcreate = "size[4,val=end-&size] typ[1,val=32] tag[tag] fid[fid] name[s] attr_size[8] flags[4]" +msg Rxattrcreate = "size[4,val=end-&size] typ[1,val=33] tag[tag]" +#... +msg Treaddir = "size[4,val=end-&size] typ[1,val=40] tag[tag] fid[fid] offset[8] count[4]" +msg Rreaddir = "size[4,val=end-&size] typ[1,val=41] tag[tag] count[4] count*(data[1])" # data is "qid[qid] offset[8] type[dt] name[s]" +#... +msg Tfsync = "size[4,val=end-&size] typ[1,val=50] tag[tag] fid[fid] datasync[b4]" +msg Rfsync = "size[4,val=end-&size] typ[1,val=51] tag[tag]" +msg Tlock = "size[4,val=end-&size] typ[1,val=52] tag[tag] fid[fid] type[lock_type] flags[lock_flags] start[8] length[8] proc_id[4] client_id[s]" +msg Rlock = "size[4,val=end-&size] typ[1,val=53] tag[tag] status[lock_status]" +msg Tgetlock = "size[4,val=end-&size] typ[1,val=54] tag[tag] fid[fid] type[lock_type] start[8] length[8] proc_id[4] client_id[s]" +msg Rgetlock = "size[4,val=end-&size] typ[1,val=55] tag[tag] type[lock_type] start[8] length[8] proc_id[4] client_id[s]" +# ... +msg Tlink = "size[4,val=end-&size] typ[1,val=70] tag[tag] dfid[fid] fid[fid] name[s]" +msg Rlink = "size[4,val=end-&size] typ[1,val=71] tag[tag]" +msg Tmkdir = "size[4,val=end-&size] typ[1,val=72] tag[tag] dfid[fid] name[s] mode[mode] gid[nuid]" +msg Rmkdir = "size[4,val=end-&size] typ[1,val=73] tag[tag] qid[qid]" +msg Trenameat = "size[4,val=end-&size] typ[1,val=74] tag[tag] olddirfid[fid] oldname[s] newdirfid[fid] newname[s]" +msg Rrenameat = "size[4,val=end-&size] typ[1,val=75] tag[tag]" +msg Tunlinkat = "size[4,val=end-&size] typ[1,val=76] tag[tag] dirfd[fid] name[s] flags[4]" +msg Runlinkat = "size[4,val=end-&size] typ[1,val=77] tag[tag]" diff --git a/lib9p/idl/2010-9P2000.L.9p.wip b/lib9p/idl/2010-9P2000.L.9p.wip deleted file mode 100644 index 5261f7e..0000000 --- a/lib9p/idl/2010-9P2000.L.9p.wip +++ /dev/null @@ -1,56 +0,0 @@ -# 2010-9P2000.L.9p - Definitions of 9P2000.L messages -# -# Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com> -# SPDX-License-Identifier: AGPL-3.0-or-later - -# "9P2000.L" Linux extension -# https://github.com/chaos/diod/blob/master/protocol.md -version "9P2000.L" - -from ./2002-9P2000.9p import * -from ./2005-9P2000.u.9p import Tauth, Tattach - -#msg Tlerror = "size[4,val=end-&size] typ[1,val=6] tag[tag] illegal" # analogous to 106/Terror -msg Rlerror = "size[4,val=end-&size] typ[1,val=7] tag[tag] ecode[4]" # analogous to 107/Rerror -msg Tstatfs = "size[4,val=end-&size] typ[1,val=8] tag[tag] TODO" -msg Rstatfs = "size[4,val=end-&size] typ[1,val=9] tag[tag] TODO" -msg Tlopen = "size[4,val=end-&size] typ[1,val=12] tag[tag] TODO" # analogous to 112/Topen -msg Rlopen = "size[4,val=end-&size] typ[1,val=13] tag[tag] TODO" # analogous to 113/Ropen -msg Tlcreate = "size[4,val=end-&size] typ[1,val=14] tag[tag] TODO" # analogous to 114/Tcreate -msg Rlcreate = "size[4,val=end-&size] typ[1,val=15] tag[tag] TODO" # analogous to 115/Rcreate -msg Tsymlink = "size[4,val=end-&size] typ[1,val=16] tag[tag] TODO" -msg Rsymlink = "size[4,val=end-&size] typ[1,val=17] tag[tag] TODO" -msg Tmknod = "size[4,val=end-&size] typ[1,val=18] tag[tag] TODO" -msg Rmknod = "size[4,val=end-&size] typ[1,val=19] tag[tag] TODO" -msg Trename = "size[4,val=end-&size] typ[1,val=20] tag[tag] TODO" -msg Rrename = "size[4,val=end-&size] typ[1,val=21] tag[tag] TODO" -msg Treadlink = "size[4,val=end-&size] typ[1,val=22] tag[tag] TODO" -msg Rreadlink = "size[4,val=end-&size] typ[1,val=23] tag[tag] TODO" -msg Tgetattr = "size[4,val=end-&size] typ[1,val=24] tag[tag] TODO" -msg Rgetattr = "size[4,val=end-&size] typ[1,val=25] tag[tag] TODO" -msg Tsetattr = "size[4,val=end-&size] typ[1,val=26] tag[tag] TODO" -msg Rsetattr = "size[4,val=end-&size] typ[1,val=27] tag[tag] TODO" -#... -msg Txattrwalk = "size[4,val=end-&size] typ[1,val=30] tag[tag] TODO" -msg Rxattrwalk = "size[4,val=end-&size] typ[1,val=31] tag[tag] TODO" -msg Txattrcreate = "size[4,val=end-&size] typ[1,val=32] tag[tag] TODO" -msg Rxattrcreate = "size[4,val=end-&size] typ[1,val=33] tag[tag] TODO" -#... -msg Treaddir = "size[4,val=end-&size] typ[1,val=40] tag[tag] TODO" -msg Rreaddir = "size[4,val=end-&size] typ[1,val=41] tag[tag] TODO" -#... -msg Tfsync = "size[4,val=end-&size] typ[1,val=50] tag[tag] TODO" -msg Rfsync = "size[4,val=end-&size] typ[1,val=51] tag[tag] TODO" -msg Tlock = "size[4,val=end-&size] typ[1,val=52] tag[tag] TODO" -msg Rlock = "size[4,val=end-&size] typ[1,val=53] tag[tag] TODO" -msg Tgetlock = "size[4,val=end-&size] typ[1,val=54] tag[tag] TODO" -msg Rgetlock = "size[4,val=end-&size] typ[1,val=55] tag[tag] TODO" -# ... -msg Tlink = "size[4,val=end-&size] typ[1,val=70] tag[tag] TODO" -msg Rlink = "size[4,val=end-&size] typ[1,val=71] tag[tag] TODO" -msg Tmkdir = "size[4,val=end-&size] typ[1,val=72] tag[tag] TODO" -msg Tmkdir = "size[4,val=end-&size] typ[1,val=73] tag[tag] TODO" -msg Trenameat = "size[4,val=end-&size] typ[1,val=74] tag[tag] TODO" -msg Rrenameat = "size[4,val=end-&size] typ[1,val=75] tag[tag] TODO" -msg Tunlinkat = "size[4,val=end-&size] typ[1,val=76] tag[tag] TODO" -msg Runlinkat = "size[4,val=end-&size] typ[1,val=77] tag[tag] TODO" diff --git a/lib9p/idl/2012-9P2000.e.9p b/lib9p/idl/2012-9P2000.e.9p index 27db50f..dde9d96 100644 --- a/lib9p/idl/2012-9P2000.e.9p +++ b/lib9p/idl/2012-9P2000.e.9p @@ -1,6 +1,6 @@ -# 2012-9P2000.e.9p - Definitions of 9P2000.e messages +# lib9p/idl/2012-9P2000.e.9p - Definitions of 9P2000.e messages # -# 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 # "9P2000.e" Erlang extension @@ -13,6 +13,6 @@ from ./2002-9P2000.9p import * msg Tsession = "size[4,val=end-&size] typ[1,val=150] tag[tag] key[8]" msg Rsession = "size[4,val=end-&size] typ[1,val=151] tag[tag]" msg Tsread = "size[4,val=end-&size] typ[1,val=152] tag[tag] fid[4] nwname[2] nwname*(wname[s])" -msg Rsread = "size[4,val=end-&size] typ[1,val=153] tag[tag] data[d]" -msg Tswrite = "size[4,val=end-&size] typ[1,val=154] tag[tag] fid[4] nwname[2] nwname*(wname[s]) data[d]" +msg Rsread = "size[4,val=end-&size] typ[1,val=153] tag[tag] count[4] count*(data[1])" +msg Tswrite = "size[4,val=end-&size] typ[1,val=154] tag[tag] fid[4] nwname[2] nwname*(wname[s]) count[4] count*(data[1])" msg Rswrite = "size[4,val=end-&size] typ[1,val=155] tag[tag] count[4]" diff --git a/lib9p/idl/__init__.py b/lib9p/idl/__init__.py new file mode 100644 index 0000000..2d09217 --- /dev/null +++ b/lib9p/idl/__init__.py @@ -0,0 +1,878 @@ +# lib9p/idl/__init__.py - A parser for .9p specification files. +# +# Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com> +# SPDX-License-Identifier: AGPL-3.0-or-later + +import enum +import os.path +import re +import typing + +# pylint: disable=unused-variable +__all__ = [ + # entrypoint + "Parser", + # types + "Type", + "Primitive", + *["Expr", "ExprTok", "ExprOp", "ExprLit", "ExprSym", "ExprOff", "ExprNum"], + "Number", + *["Bitfield", "Bit", "BitCat", "BitNum", "BitAlias"], + *["Struct", "StructMember"], + "Message", +] + +# The syntax that this parses is described in `./0000-README.md`. + +# Utilities #################################################################### + + +def get_type(env: dict[str, "Type"], name: str, tc: type["T"]) -> "T": + if name not in env: + raise NameError(f"Unknown type {name!r}") + ret = env[name] + if (not isinstance(ret, tc)) or (ret.__class__.__name__ != tc.__name__): + raise NameError(f"Type {ret.typname!r} is not a {tc.__name__}") + return ret + + +# Types ######################################################################## + + +class Primitive(enum.Enum): + u8 = 1 + u16 = 2 + u32 = 4 + u64 = 8 + + @property + def in_versions(self) -> set[str]: + return set() + + @property + def typname(self) -> str: + return str(self.value) + + @property + def static_size(self) -> int: + return self.value + + def min_size(self, version: str) -> int: + return self.value + + def max_size(self, version: str) -> int: + return self.value + + +class ExprOp: + op: typing.Literal["-", "+", "<<"] + + def __init__(self, op: typing.Literal["-", "+", "<<"]) -> None: + self.op = op + + +class ExprLit: + val: int + + def __init__(self, val: int) -> None: + self.val = val + + +class ExprSym: + symname: str + + def __init__(self, name: str) -> None: + self.symname = name + + +class ExprOff: + membname: str + + def __init__(self, name: str) -> None: + self.membname = name + + +class ExprNum: + numname: str + valname: str + + def __init__(self, numname: str, valname: str) -> None: + self.numname = numname + self.valname = valname + + +type ExprTok = ExprOp | ExprLit | ExprSym | ExprOff | ExprNum + + +class Expr: + tokens: typing.Sequence[ExprTok] + const: int | None + + def __init__( + self, env: dict[str, "Type"], tokens: typing.Sequence[ExprTok] = () + ) -> None: + self.tokens = tokens + self.const = self._const(env, tokens) + + def _const( + self, env: dict[str, "Type"], toks: typing.Sequence[ExprTok] + ) -> int | None: + if not toks: + return None + + def read_val() -> int | None: + nonlocal toks + assert toks + neg = False + match toks[0]: + case ExprOp(op="-"): + neg = True + toks = toks[1:] + assert not isinstance(toks[0], ExprOp) + val: int + match toks[0]: + case ExprLit(): + val = toks[0].val + case ExprSym(): + if m := re.fullmatch(r"^u(8|16|32|64)_max$", toks[0].symname): + n = int(m.group(1)) + val = (1 << n) - 1 + elif m := re.fullmatch(r"^s(8|16|32|64)_max$", toks[0].symname): + n = int(m.group(1)) + val = (1 << (n - 1)) - 1 + else: + return None + case ExprOff(): + return None + case ExprNum(): + num = get_type(env, toks[0].numname, Number) + if toks[0].valname not in num.vals: + raise NameError( + f"Type {toks[0].numname!r} does not have a value {toks[0].valname!r}" + ) + _val = num.vals[toks[0].valname].const + if _val is None: + return None + val = _val + toks = toks[1:] + return -val if neg else val + + ret = read_val() + if ret is None: + return None + while toks: + assert isinstance(toks[0], ExprOp) + op = toks[0].op + toks = toks[1:] + operand = read_val() + if operand is None: + return None + match op: + case "+": + ret = ret + operand + case "-": + ret = ret - operand + case "<<": + ret = ret << operand + return ret + + def __bool__(self) -> bool: + return len(self.tokens) > 0 + + +class Number: + typname: str + in_versions: set[str] + + prim: Primitive + + vals: dict[str, Expr] + + def __init__(self) -> None: + self.in_versions = set() + self.vals = {} + + @property + def static_size(self) -> int: + return self.prim.static_size + + def min_size(self, version: str) -> int: + return self.static_size + + def max_size(self, version: str) -> int: + return self.static_size + + +class BitAlias: + bitname: str + in_versions: set[str] + val: Expr + + def __init__(self, name: str, val: Expr) -> None: + if val.const is None: + raise ValueError(f"{name!r} value is not constant") + self.bitname = name + self.in_versions = set() + self.val = val + + +class BitNum: + numname: str + mask: int + vals: dict[str, BitAlias] + + def __init__(self, name: str) -> None: + self.numname = name + self.mask = 0 + self.vals = {} + + +type BitCat = typing.Literal["UNUSED", "USED", "RESERVED"] | BitNum + + +class Bit: + bitname: str + in_versions: set[str] + num: int + cat: BitCat + + def __init__(self, num: int) -> None: + self.bitname = "" + self.in_versions = set() + self.num = num + self.cat = "UNUSED" + + +class Bitfield: + typname: str + in_versions: set[str] + prim: Primitive + + bits: list[Bit] + nums: dict[str, BitNum] + masks: dict[str, BitAlias] + aliases: dict[str, BitAlias] + + names: set[str] + + def __init__(self, name: str, prim: Primitive) -> None: + self.typname = name + self.in_versions = set() + self.prim = prim + + self.bits = [Bit(i) for i in range(prim.static_size * 8)] + self.nums = {} + self.masks = {} + self.aliases = {} + + self.names = set() + + @property + def static_size(self) -> int: + return self.prim.static_size + + def min_size(self, version: str) -> int: + return self.static_size + + def max_size(self, version: str) -> int: + return self.static_size + + +class StructMember: + # from left-to-right when parsing + cnt: "StructMember| int | None" = None + membname: str + typ: "Type" + max: Expr + val: Expr + + in_versions: set[str] + + @property + def min_cnt(self) -> int: + assert self.cnt + if isinstance(self.cnt, int): + return self.cnt + if not isinstance(self.cnt.typ, Primitive): + raise ValueError( + f"list count must be an integer type: {self.cnt.membname!r}" + ) + if self.cnt.val: # TODO: allow this? + raise ValueError(f"list count may not have ,val=: {self.cnt.membname!r}") + return 0 + + @property + def max_cnt(self) -> int: + assert self.cnt + if isinstance(self.cnt, int): + return self.cnt + if not isinstance(self.cnt.typ, Primitive): + raise ValueError( + f"list count must be an integer type: {self.cnt.membname!r}" + ) + if self.cnt.val: # TODO: allow this? + raise ValueError(f"list count may not have ,val=: {self.cnt.membname!r}") + if self.cnt.max: + # TODO: be more flexible? + val = self.cnt.max.const + if val is None: + raise ValueError( + f"list count ,max= must be a constant value: {self.cnt.membname!r}" + ) + return val + return (1 << (self.cnt.typ.value * 8)) - 1 + + @property + def static_size(self) -> int | None: + if self.cnt: + return None + return self.typ.static_size + + def min_size(self, version: str) -> int: + cnt = self.min_cnt if self.cnt else 1 + return cnt * self.typ.min_size(version) + + def max_size(self, version: str) -> int: + cnt = self.max_cnt if self.cnt else 1 + return cnt * self.typ.max_size(version) + + +class Struct: + typname: str + in_versions: set[str] + + members: list[StructMember] + + def __init__(self) -> None: + self.in_versions = set() + + @property + def static_size(self) -> int | None: + size = 0 + for member in self.members: + if member.in_versions < self.in_versions: + return None + msize = member.static_size + if msize is None: + return None + size += msize + return size + + def min_size(self, version: str) -> int: + return sum( + member.min_size(version) + for member in self.members + if (version in member.in_versions) + ) + + def max_size(self, version: str) -> int: + return sum( + member.max_size(version) + for member in self.members + if (version in member.in_versions) + ) + + +class Message(Struct): + @property + def msgid(self) -> int: + assert len(self.members) >= 3 + assert self.members[1].membname == "typ" + assert self.members[1].static_size == 1 + assert self.members[1].val + assert len(self.members[1].val.tokens) == 1 + assert isinstance(self.members[1].val.tokens[0], ExprLit) + return self.members[1].val.tokens[0].val + + +type Type = Primitive | Number | Bitfield | Struct | Message +type UserType = Number | Bitfield | Struct | Message +T = typing.TypeVar("T", Number, Bitfield, Struct, Message) + +# Parse ######################################################################## + +# common elements ###################### + +re_priname = "(?:1|2|4|8)" # primitive names +re_symname = "(?:[a-zA-Z_][a-zA-Z_0-9]*)" # "symbol" names; most *.9p-defined names +re_symname_u = "(?:[A-Z_][A-Z_0-9]*)" # upper-case "symbol" names; bit names +re_symname_l = "(?:[a-z_][a-z_0-9]*)" # lower-case "symbol" names; bit names +re_impname = r"(?:\*|" + re_symname + ")" # names we can import +re_msgname = r"(?:[TR][a-zA-Z_0-9]*)" # names a message can be + +re_memtype = f"(?:{re_symname}|{re_priname})" # typenames that a struct member can be + +valid_syms = [ + "end", + "u8_max", + "u16_max", + "u32_max", + "u64_max", + "s8_max", + "s16_max", + "s32_max", + "s64_max", +] + +_re_expr_op = r"(?:-|\+|<<)" + +_res_expr_val = { + "lit_2": r"0b[01]+", + "lit_8": r"0[0-7]+", + "lit_10": r"0(?![0-9bxX])|[1-9][0-9]*", + "lit_16": r"0[xX][0-9a-fA-F]+", + "sym": "|".join(valid_syms), # pre-defined symbols + "off": f"&{re_symname}", # offset of a field this struct + "num": f"{re_symname}\\.{re_symname}", # `num` values +} + +re_expr_tok = ( + "(?:" + + "|".join( + [ + f"(?P<op>{_re_expr_op})", + *[f"(?P<{k}>{v})" for k, v in _res_expr_val.items()], + ] + ) + + ")" +) + +_re_expr_val = "(?:" + "|".join(_res_expr_val.values()) + ")" + +re_expr = f"(?:\\s*(?:-\\s*)?{_re_expr_val}\\s*(?:{_re_expr_op}\\s*(?:-\\s*)?{_re_expr_val}\\s*)*)" + + +def parse_expr(env: dict[str, Type], expr: str) -> Expr: + assert re.fullmatch(re_expr, expr) + tokens: list[ExprTok] = [] + for m in re.finditer(re_expr_tok, expr): + if tok := m.group("op"): + tokens.append(ExprOp(typing.cast(typing.Literal["-", "+", "<<"], tok))) + elif tok := m.group("lit_2"): + tokens.append(ExprLit(int(tok[2:], 2))) + elif tok := m.group("lit_8"): + tokens.append(ExprLit(int(tok[1:], 8))) + elif tok := m.group("lit_10"): + tokens.append(ExprLit(int(tok, 10))) + elif tok := m.group("lit_16"): + tokens.append(ExprLit(int(tok[2:], 16))) + elif tok := m.group("sym"): + tokens.append(ExprSym(tok)) + elif tok := m.group("off"): + tokens.append(ExprOff(tok[1:])) + elif tok := m.group("num"): + [numname, valname] = tok.split(".", 1) + tokens.append(ExprNum(numname, valname)) + else: + assert False + return Expr(env, tokens) + + +# numspec ############################## + +re_numspec = f"(?P<name>{re_symname})\\s*=\\s*(?P<val>{re_expr})" + + +def parse_numspec(env: dict[str, Type], ver: str, n: Number, spec: str) -> None: + spec = spec.strip() + + if m := re.fullmatch(re_numspec, spec): + name = m.group("name") + if name in n.vals: + raise ValueError(f"{n.typname}: name {name!r} already assigned") + val = parse_expr(env, m.group("val")) + if val is None: + raise ValueError( + f"{n.typname}: {name!r} value is not constant: {m.group('val')!r}" + ) + n.vals[name] = val + else: + raise SyntaxError(f"invalid num spec {spec!r}") + + +# bitspec ############################## + +re_bitspec_bit = ( + "bit\\s+(?P<bitnum>[0-9]+)\\s*=\\s*(?:" + + "|".join( + [ + f"(?P<name_used>{re_symname_u})", + f"reserved\\((?P<name_reserved>{re_symname_u})\\)", + f"num\\((?P<name_num>{re_symname_u})\\)", + ] + ) + + ")" +) +re_bitspec_mask = f"mask\\s+(?P<name>{re_symname_u})\\s*=\\s*(?P<val>{re_expr})" +re_bitspec_alias = f"alias\\s+(?P<name>{re_symname_u})\\s*=\\s*(?P<val>{re_expr})" +re_bitspec_num = f"num\\((?P<num>{re_symname_u})\\)\\s+(?P<name>{re_symname_u})\\s*=\\s*(?P<val>{re_expr})" + + +def parse_bitspec(env: dict[str, Type], ver: str, bf: Bitfield, spec: str) -> None: + spec = spec.strip() + + def check_name(name: str, is_num: bool = False) -> None: + if name == "MASK": + raise ValueError(f"{bf.typname}: bit name may not be {'MASK'!r}: {name!r}") + if name.endswith("_MASK"): + raise ValueError( + f"{bf.typname}: bit name may not end with {'_MASK'!r}: {name!r}" + ) + if name in bf.names and not (is_num and name in bf.nums): + raise ValueError(f"{bf.typname}: bit name already assigned: {name!r}") + + if m := re.fullmatch(re_bitspec_bit, spec): + bitnum = int(m.group("bitnum")) + if bitnum < 0 or bitnum >= len(bf.bits): + raise ValueError(f"{bf.typname}: bit num {bitnum} out-of-bounds") + bit = bf.bits[bitnum] + if bit.cat != "UNUSED": + raise ValueError(f"{bf.typname}: bit num {bitnum} already assigned") + if name := m.group("name_used"): + bit.bitname = name + bit.cat = "USED" + bit.in_versions.add(ver) + elif name := m.group("name_reserved"): + bit.bitname = name + bit.cat = "RESERVED" + bit.in_versions.add(ver) + elif name := m.group("name_num"): + bit.bitname = name + if name not in bf.nums: + bf.nums[name] = BitNum(name) + bf.nums[name].mask |= 1 << bit.num + bit.cat = bf.nums[name] + bit.in_versions.add(ver) + if bit.bitname: + check_name(name, isinstance(bit.cat, BitNum)) + bf.names.add(bit.bitname) + elif m := re.fullmatch(re_bitspec_mask, spec): + mask = BitAlias(m.group("name"), parse_expr(env, m.group("val"))) + mask.in_versions.add(ver) + check_name(mask.bitname) + bf.masks[mask.bitname] = mask + bf.names.add(mask.bitname) + elif m := re.fullmatch(re_bitspec_alias, spec): + alias = BitAlias(m.group("name"), parse_expr(env, m.group("val"))) + alias.in_versions.add(ver) + check_name(alias.bitname) + bf.aliases[alias.bitname] = alias + bf.names.add(alias.bitname) + elif m := re.fullmatch(re_bitspec_num, spec): + numname = m.group("num") + alias = BitAlias(m.group("name"), parse_expr(env, m.group("val"))) + alias.in_versions.add(ver) + check_name(alias.bitname) + if numname not in bf.nums: + raise NameError( + f"{bf.typname}: nested num not allocated any bits: {numname!r}" + ) + assert alias.val.const is not None + if alias.val.const & ~bf.nums[numname].mask: + raise ValueError( + f"{bf.typname}: {alias.bitname!r} does not fit within bitmask: val={alias.val.const:b} mask={bf.nums[numname].mask}" + ) + bf.nums[numname].vals[alias.bitname] = alias + bf.names.add(alias.bitname) + else: + raise SyntaxError(f"invalid bitfield spec {spec!r}") + + +# struct members ####################### + + +re_memberspec = f"(?:(?P<cnt>{re_symname}|[1-9][0-9]*)\\*\\()?(?P<name>{re_symname})\\[(?P<typ>{re_memtype})(?:,max=(?P<max>{re_expr})|,val=(?P<val>{re_expr}))*\\]\\)?" + + +def parse_members(ver: str, env: dict[str, Type], struct: Struct, specs: str) -> None: + for spec in specs.split(): + m = re.fullmatch(re_memberspec, spec) + if not m: + raise SyntaxError(f"invalid member spec {spec!r}") + + member = StructMember() + member.in_versions = {ver} + + member.membname = m.group("name") + if any(x.membname == member.membname for x in struct.members): + raise ValueError(f"duplicate member name {member.membname!r}") + + if m.group("typ") not in env: + raise NameError(f"Unknown type {m.group('typ')!r}") + member.typ = env[m.group("typ")] + + if cnt := m.group("cnt"): + if cnt.isnumeric(): + member.cnt = int(cnt) + else: + if len(struct.members) == 0 or struct.members[-1].membname != cnt: + raise ValueError(f"list count must be previous item: {cnt!r}") + member.cnt = struct.members[-1] + _ = member.max_cnt # force validation + + if maxstr := m.group("max"): + if ( + not isinstance(member.typ, Primitive) + and not isinstance(member.typ, Number) + ) or member.cnt: + raise ValueError( + "',max=' may only be specified on a non-repeated numeric type" + ) + member.max = parse_expr(env, maxstr) + else: + member.max = Expr(env) + + if valstr := m.group("val"): + if ( + not isinstance(member.typ, Primitive) + and not isinstance(member.typ, Number) + ) or member.cnt: + raise ValueError( + "',val=' may only be specified on a non-repeated numeric type" + ) + member.val = parse_expr(env, valstr) + else: + member.val = Expr(env) + + struct.members += [member] + + +# main parser ########################## + + +def re_string(grpname: str) -> str: + return f'"(?P<{grpname}>[^"]*)"' + + +re_line_version = f"version\\s+{re_string('version')}" +re_line_import = f"from\\s+(?P<file>\\S+)\\s+import\\s+(?P<syms>{re_impname}(?:\\s*,\\s*{re_impname})*)" +re_line_num = f"num\\s+(?P<name>{re_symname})\\s*=\\s*(?P<prim>{re_priname})" +re_line_bitfield = f"bitfield\\s+(?P<name>{re_symname})\\s*=\\s*(?P<prim>{re_priname})" +re_line_bitfield_ = ( + f"bitfield\\s+(?P<name>{re_symname})\\s*\\+=\\s*{re_string('member')}" +) +re_line_struct = ( + f"struct\\s+(?P<name>{re_symname})\\s*(?P<op>\\+?=)\\s*{re_string('members')}" +) +re_line_msg = ( + f"msg\\s+(?P<name>{re_msgname})\\s*(?P<op>\\+?=)\\s*{re_string('members')}" +) +re_line_cont = f"\\s+{re_string('specs')}" # could be bitfield/struct/msg + + +def parse_file( + filename: str, get_include: typing.Callable[[str], tuple[str, list[UserType]]] +) -> tuple[str, list[UserType]]: + version: str | None = None + env: dict[str, Type] = { + "1": Primitive.u8, + "2": Primitive.u16, + "4": Primitive.u32, + "8": Primitive.u64, + } + + with open(filename, "r", encoding="utf-8") as fh: + prev: Type | None = None + for lineno, line in enumerate(fh): + try: + line = line.split("#", 1)[0].rstrip() + if not line: + continue + if m := re.fullmatch(re_line_version, line): + if version: + raise SyntaxError("must have exactly 1 version line") + version = m.group("version") + continue + if not version: + raise SyntaxError("must have exactly 1 version line") + + if m := re.fullmatch(re_line_import, line): + other_version, other_typs = get_include(m.group("file")) + for symname in m.group("syms").split(sep=","): + symname = symname.strip() + found = False + for typ in other_typs: + if symname in (typ.typname, "*"): + found = True + match typ: + case Primitive(): + pass + case Number(): + typ.in_versions.add(version) + case Bitfield(): + typ.in_versions.add(version) + for bf_bit in typ.bits: + if other_version in bf_bit.in_versions: + bf_bit.in_versions.add(version) + for bf_num in typ.nums.values(): + for bf_val in bf_num.vals.values(): + if other_version in bf_val.in_versions: + bf_val.in_versions.add(version) + for bf_mask in typ.masks.values(): + if other_version in bf_mask.in_versions: + bf_mask.in_versions.add(version) + for bf_alias in typ.aliases.values(): + if other_version in bf_alias.in_versions: + bf_alias.in_versions.add(version) + case Struct(): # and Message() + typ.in_versions.add(version) + for member in typ.members: + if other_version in member.in_versions: + member.in_versions.add(version) + if typ.typname in env and env[typ.typname] != typ: + raise ValueError( + f"duplicate type name {typ.typname!r}" + ) + env[typ.typname] = typ + if symname != "*" and not found: + raise ValueError( + f"import: {m.group('file')}: no symbol {symname!r}" + ) + elif m := re.fullmatch(re_line_num, line): + num = Number() + num.typname = m.group("name") + num.in_versions.add(version) + + prim = env[m.group("prim")] + assert isinstance(prim, Primitive) + num.prim = prim + + if num.typname in env: + raise ValueError(f"duplicate type name {num.typname!r}") + env[num.typname] = num + prev = num + elif m := re.fullmatch(re_line_bitfield, line): + prim = env[m.group("prim")] + assert isinstance(prim, Primitive) + + bf = Bitfield(m.group("name"), prim) + bf.in_versions.add(version) + + if bf.typname in env: + raise ValueError(f"duplicate type name {bf.typname!r}") + env[bf.typname] = bf + prev = bf + elif m := re.fullmatch(re_line_bitfield_, line): + bf = get_type(env, m.group("name"), Bitfield) + parse_bitspec(env, version, bf, m.group("member")) + + prev = bf + elif m := re.fullmatch(re_line_struct, line): + match m.group("op"): + case "=": + struct = Struct() + struct.typname = m.group("name") + struct.in_versions.add(version) + struct.members = [] + parse_members(version, env, struct, m.group("members")) + + if struct.typname in env: + raise ValueError( + f"duplicate type name {struct.typname!r}" + ) + env[struct.typname] = struct + prev = struct + case "+=": + struct = get_type(env, m.group("name"), Struct) + parse_members(version, env, struct, m.group("members")) + + prev = struct + elif m := re.fullmatch(re_line_msg, line): + match m.group("op"): + case "=": + msg = Message() + msg.typname = m.group("name") + msg.in_versions.add(version) + msg.members = [] + parse_members(version, env, msg, m.group("members")) + + if msg.typname in env: + raise ValueError(f"duplicate type name {msg.typname!r}") + env[msg.typname] = msg + prev = msg + case "+=": + msg = get_type(env, m.group("name"), Message) + parse_members(version, env, msg, m.group("members")) + + prev = msg + elif m := re.fullmatch(re_line_cont, line): + match prev: + case Bitfield(): + parse_bitspec(env, version, prev, m.group("specs")) + case Number(): + parse_numspec(env, version, prev, m.group("specs")) + case Struct(): # and Message() + parse_members(version, env, prev, m.group("specs")) + case _: + raise SyntaxError( + "continuation line must come after a bitfield, struct, or msg line" + ) + else: + raise SyntaxError("invalid line") + except (SyntaxError, NameError, ValueError) as e: + e2 = SyntaxError(str(e)) + e2.filename = filename + e2.lineno = lineno + 1 + e2.text = line + raise e2 from e + if not version: + raise SyntaxError("must have exactly 1 version line") + + typs: list[UserType] = [x for x in env.values() if not isinstance(x, Primitive)] + + for typ in [typ for typ in typs if isinstance(typ, Struct)]: + for member in typ.members: + if ( + not isinstance(member.typ, Primitive) + and member.typ.in_versions < member.in_versions + ): + raise ValueError( + f"{typ.typname}.{member.membname}: type {member.typ.typname} does not exist in {member.in_versions.difference(member.typ.in_versions)}" + ) + for tok in [*member.max.tokens, *member.val.tokens]: + if isinstance(tok, ExprOff) and not any( + m.membname == tok.membname for m in typ.members + ): + raise NameError( + f"{typ.typname}.{member.membname}: invalid offset: &{tok.membname}" + ) + + return version, typs + + +# Filesystem ################################################################### + + +class Parser: + cache: dict[str, tuple[str, list[UserType]]] = {} + + def parse_file(self, filename: str) -> tuple[str, list[UserType]]: + filename = os.path.normpath(filename) + if filename not in self.cache: + + def get_include(other_filename: str) -> tuple[str, list[UserType]]: + return self.parse_file(os.path.join(filename, "..", other_filename)) + + self.cache[filename] = parse_file(filename, get_include) + return self.cache[filename] + + def all(self) -> tuple[set[str], list[UserType]]: + ret_versions: set[str] = set() + ret_typs: dict[str, UserType] = {} + for version, typs in self.cache.values(): + if version in ret_versions: + raise ValueError(f"duplicate protocol version {version!r}") + ret_versions.add(version) + for typ in typs: + if typ.typname in ret_typs: + if typ != ret_typs[typ.typname]: + raise ValueError(f"duplicate type name {typ.typname!r}") + else: + ret_typs[typ.typname] = typ + msgids: set[int] = set() + for typ in ret_typs.values(): + if isinstance(typ, Message): + if typ.msgid in msgids: + raise ValueError(f"duplicate msgid {typ.msgid!r}") + msgids.add(typ.msgid) + return ret_versions, list(ret_typs.values()) diff --git a/lib9p/include/lib9p/9p.generated.h b/lib9p/include/lib9p/9p.generated.h index d5e94d1..a53f117 100644 --- a/lib9p/include/lib9p/9p.generated.h +++ b/lib9p/include/lib9p/9p.generated.h @@ -1,4 +1,4 @@ -/* Generated by `./lib9p/idl.gen lib9p/idl/2002-9P2000.9p lib9p/idl/2005-9P2000.u.9p lib9p/idl/2012-9P2000.e.9p`. DO NOT EDIT! */ +/* Generated by `lib9p/proto.gen lib9p/idl/2002-9P2000.9p lib9p/idl/2003-9P2000.p9p.9p lib9p/idl/2005-9P2000.u.9p lib9p/idl/2010-9P2000.L.9p lib9p/idl/2012-9P2000.e.9p`. DO NOT EDIT! */ #ifndef _LIB9P_9P_H_ #error Do not include <lib9p/9p.generated.h> directly; include <lib9p/9p.h> instead @@ -6,271 +6,947 @@ #include <stdint.h> /* for uint{n}_t types */ -/* versions *******************************************************************/ +#include <libfmt/fmt.h> /* for fmt_formatter */ +#include <libhw/generic/io.h> /* for struct iovec */ + +/* config *********************************************************************/ + +#include "config.h" + +#ifndef CONFIG_9P_ENABLE_9P2000 + #error config.h must define CONFIG_9P_ENABLE_9P2000 +#endif + +#ifndef CONFIG_9P_ENABLE_9P2000_L + #error config.h must define CONFIG_9P_ENABLE_9P2000_L +#endif + +#ifndef CONFIG_9P_ENABLE_9P2000_e + #error config.h must define CONFIG_9P_ENABLE_9P2000_e +#else + #if CONFIG_9P_ENABLE_9P2000_e + #ifndef CONFIG_9P_MAX_9P2000_e_WELEM + #error if CONFIG_9P_ENABLE_9P2000_e then config.h must define CONFIG_9P_MAX_9P2000_e_WELEM + #endif + static_assert(CONFIG_9P_MAX_9P2000_e_WELEM > 0); + #endif +#endif + +#ifndef CONFIG_9P_ENABLE_9P2000_p9p + #error config.h must define CONFIG_9P_ENABLE_9P2000_p9p +#endif + +#ifndef CONFIG_9P_ENABLE_9P2000_u + #error config.h must define CONFIG_9P_ENABLE_9P2000_u +#endif + +/* enum version ***************************************************************/ enum lib9p_version { LIB9P_VER_unknown = 0, /* "unknown" */ -#if defined(CONFIG_9P_ENABLE_9P2000) +#if CONFIG_9P_ENABLE_9P2000 LIB9P_VER_9P2000, /* "9P2000" */ -#endif /* defined(CONFIG_9P_ENABLE_9P2000) */ -#if defined(CONFIG_9P_ENABLE_9P2000_e) +#endif /* CONFIG_9P_ENABLE_9P2000 */ +#if CONFIG_9P_ENABLE_9P2000_L + LIB9P_VER_9P2000_L, /* "9P2000.L" */ +#endif /* CONFIG_9P_ENABLE_9P2000_L */ +#if CONFIG_9P_ENABLE_9P2000_e LIB9P_VER_9P2000_e, /* "9P2000.e" */ -#endif /* defined(CONFIG_9P_ENABLE_9P2000_e) */ -#if defined(CONFIG_9P_ENABLE_9P2000_u) +#endif /* CONFIG_9P_ENABLE_9P2000_e */ +#if CONFIG_9P_ENABLE_9P2000_p9p + LIB9P_VER_9P2000_p9p, /* "9P2000.p9p" */ +#endif /* CONFIG_9P_ENABLE_9P2000_p9p */ +#if CONFIG_9P_ENABLE_9P2000_u LIB9P_VER_9P2000_u, /* "9P2000.u" */ -#endif /* defined(CONFIG_9P_ENABLE_9P2000_u) */ +#endif /* CONFIG_9P_ENABLE_9P2000_u */ LIB9P_VER_NUM, }; +LO_IMPLEMENTATION_H(fmt_formatter, enum lib9p_version, lib9p_version); -const char *lib9p_version_str(enum lib9p_version); +/* enum msg_type **************************************************************/ -/* non-message types **********************************************************/ +enum lib9p_msg_type { /* uint8_t */ +#if CONFIG_9P_ENABLE_9P2000_L + LIB9P_TYP_Rlerror = 7, + LIB9P_TYP_Tstatfs = 8, + LIB9P_TYP_Rstatfs = 9, + LIB9P_TYP_Tlopen = 12, + LIB9P_TYP_Rlopen = 13, + LIB9P_TYP_Tlcreate = 14, + LIB9P_TYP_Rlcreate = 15, + LIB9P_TYP_Tsymlink = 16, + LIB9P_TYP_Rsymlink = 17, + LIB9P_TYP_Tmknod = 18, + LIB9P_TYP_Rmknod = 19, + LIB9P_TYP_Trename = 20, + LIB9P_TYP_Rrename = 21, + LIB9P_TYP_Treadlink = 22, + LIB9P_TYP_Rreadlink = 23, + LIB9P_TYP_Tgetattr = 24, + LIB9P_TYP_Rgetattr = 25, + LIB9P_TYP_Tsetattr = 26, + LIB9P_TYP_Rsetattr = 27, + LIB9P_TYP_Txattrwalk = 30, + LIB9P_TYP_Rxattrwalk = 31, + LIB9P_TYP_Txattrcreate = 32, + LIB9P_TYP_Rxattrcreate = 33, + LIB9P_TYP_Treaddir = 40, + LIB9P_TYP_Rreaddir = 41, + LIB9P_TYP_Tfsync = 50, + LIB9P_TYP_Rfsync = 51, + LIB9P_TYP_Tlock = 52, + LIB9P_TYP_Rlock = 53, + LIB9P_TYP_Tgetlock = 54, + LIB9P_TYP_Rgetlock = 55, + LIB9P_TYP_Tlink = 70, + LIB9P_TYP_Rlink = 71, + LIB9P_TYP_Tmkdir = 72, + LIB9P_TYP_Rmkdir = 73, + LIB9P_TYP_Trenameat = 74, + LIB9P_TYP_Rrenameat = 75, + LIB9P_TYP_Tunlinkat = 76, + LIB9P_TYP_Runlinkat = 77, +#endif /* CONFIG_9P_ENABLE_9P2000_L */ +#if CONFIG_9P_ENABLE_9P2000_p9p + LIB9P_TYP_Topenfd = 98, + LIB9P_TYP_Ropenfd = 99, +#endif /* CONFIG_9P_ENABLE_9P2000_p9p */ +#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u + LIB9P_TYP_Tversion = 100, + LIB9P_TYP_Rversion = 101, + LIB9P_TYP_Tauth = 102, + LIB9P_TYP_Rauth = 103, + LIB9P_TYP_Tattach = 104, + LIB9P_TYP_Rattach = 105, + LIB9P_TYP_Rerror = 107, + LIB9P_TYP_Tflush = 108, + LIB9P_TYP_Rflush = 109, + LIB9P_TYP_Twalk = 110, + LIB9P_TYP_Rwalk = 111, +#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */ +#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u + LIB9P_TYP_Topen = 112, + LIB9P_TYP_Ropen = 113, + LIB9P_TYP_Tcreate = 114, + LIB9P_TYP_Rcreate = 115, +#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */ +#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u + LIB9P_TYP_Tread = 116, + LIB9P_TYP_Rread = 117, + LIB9P_TYP_Twrite = 118, + LIB9P_TYP_Rwrite = 119, + LIB9P_TYP_Tclunk = 120, + LIB9P_TYP_Rclunk = 121, + LIB9P_TYP_Tremove = 122, + LIB9P_TYP_Rremove = 123, +#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */ +#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u + LIB9P_TYP_Tstat = 124, + LIB9P_TYP_Rstat = 125, + LIB9P_TYP_Twstat = 126, + LIB9P_TYP_Rwstat = 127, +#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */ +#if CONFIG_9P_ENABLE_9P2000_e + LIB9P_TYP_Tsession = 150, + LIB9P_TYP_Rsession = 151, + LIB9P_TYP_Tsread = 152, + LIB9P_TYP_Rsread = 153, + LIB9P_TYP_Tswrite = 154, + LIB9P_TYP_Rswrite = 155, +#endif /* CONFIG_9P_ENABLE_9P2000_e */ +}; +LO_IMPLEMENTATION_H(fmt_formatter, enum lib9p_msg_type, lib9p_msg_type); + +/* payload types **************************************************************/ -#if defined(CONFIG_9P_ENABLE_9P2000) || defined(CONFIG_9P_ENABLE_9P2000_e) || defined(CONFIG_9P_ENABLE_9P2000_u) +#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u +/* size = 2 ; max_iov = 1 ; max_copy = 2 */ typedef uint16_t lib9p_tag_t; +LO_IMPLEMENTATION_H(fmt_formatter, lib9p_tag_t, lib9p_tag); +#define LIB9P_TAG_NOTAG ((lib9p_tag_t)(UINT16_MAX)) +/* size = 4 ; max_iov = 1 ; max_copy = 4 */ typedef uint32_t lib9p_fid_t; +LO_IMPLEMENTATION_H(fmt_formatter, lib9p_fid_t, lib9p_fid); +#define LIB9P_FID_NOFID ((lib9p_fid_t)(UINT32_MAX)) -struct lib9p_d { - uint32_t len; - char *dat; -}; - +/* min_size = 2 ; exp_size = 29 ; max_size = 65,537 ; max_iov = 2 ; max_copy = 2 */ struct lib9p_s { - uint16_t len; - char *utf8; + uint16_t len; + [[gnu::nonstring]] char *utf8; }; +LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_s, lib9p_s); +#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */ +#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u +/* size = 4 ; max_iov = 1 ; max_copy = 4 */ typedef uint32_t lib9p_dm_t; +LO_IMPLEMENTATION_H(fmt_formatter, lib9p_dm_t, lib9p_dm); +/* bits */ +#define LIB9P_DM_DIR ((lib9p_dm_t)(UINT32_C(1)<<31)) +#define LIB9P_DM_APPEND ((lib9p_dm_t)(UINT32_C(1)<<30)) +#define LIB9P_DM_EXCL ((lib9p_dm_t)(UINT32_C(1)<<29)) +#define _LIB9P_DM_PLAN9_MOUNT ((lib9p_dm_t)(UINT32_C(1)<<28)) +#define LIB9P_DM_AUTH ((lib9p_dm_t)(UINT32_C(1)<<27)) +#define LIB9P_DM_TMP ((lib9p_dm_t)(UINT32_C(1)<<26)) +#define _LIB9P_DM_UNUSED_25 ((lib9p_dm_t)(UINT32_C(1)<<25)) +#define _LIB9P_DM_UNUSED_24 ((lib9p_dm_t)(UINT32_C(1)<<24)) +#if CONFIG_9P_ENABLE_9P2000_u +# define LIB9P_DM_DEVICE ((lib9p_dm_t)(UINT32_C(1)<<23)) +#endif /* CONFIG_9P_ENABLE_9P2000_u */ +#define _LIB9P_DM_UNUSED_22 ((lib9p_dm_t)(UINT32_C(1)<<22)) +#if CONFIG_9P_ENABLE_9P2000_u +# define LIB9P_DM_PIPE ((lib9p_dm_t)(UINT32_C(1)<<21)) +# define LIB9P_DM_SOCKET ((lib9p_dm_t)(UINT32_C(1)<<20)) +# define LIB9P_DM_SETUID ((lib9p_dm_t)(UINT32_C(1)<<19)) +# define LIB9P_DM_SETGID ((lib9p_dm_t)(UINT32_C(1)<<18)) +#endif /* CONFIG_9P_ENABLE_9P2000_u */ +#define _LIB9P_DM_UNUSED_17 ((lib9p_dm_t)(UINT32_C(1)<<17)) +#define _LIB9P_DM_UNUSED_16 ((lib9p_dm_t)(UINT32_C(1)<<16)) +#define _LIB9P_DM_UNUSED_15 ((lib9p_dm_t)(UINT32_C(1)<<15)) +#define _LIB9P_DM_UNUSED_14 ((lib9p_dm_t)(UINT32_C(1)<<14)) +#define _LIB9P_DM_UNUSED_13 ((lib9p_dm_t)(UINT32_C(1)<<13)) +#define _LIB9P_DM_UNUSED_12 ((lib9p_dm_t)(UINT32_C(1)<<12)) +#define _LIB9P_DM_UNUSED_11 ((lib9p_dm_t)(UINT32_C(1)<<11)) +#define _LIB9P_DM_UNUSED_10 ((lib9p_dm_t)(UINT32_C(1)<<10)) +#define _LIB9P_DM_UNUSED_9 ((lib9p_dm_t)(UINT32_C(1)<<9)) +#define LIB9P_DM_OWNER_R ((lib9p_dm_t)(UINT32_C(1)<<8)) +#define LIB9P_DM_OWNER_W ((lib9p_dm_t)(UINT32_C(1)<<7)) +#define LIB9P_DM_OWNER_X ((lib9p_dm_t)(UINT32_C(1)<<6)) +#define LIB9P_DM_GROUP_R ((lib9p_dm_t)(UINT32_C(1)<<5)) +#define LIB9P_DM_GROUP_W ((lib9p_dm_t)(UINT32_C(1)<<4)) +#define LIB9P_DM_GROUP_X ((lib9p_dm_t)(UINT32_C(1)<<3)) +#define LIB9P_DM_OTHER_R ((lib9p_dm_t)(UINT32_C(1)<<2)) +#define LIB9P_DM_OTHER_W ((lib9p_dm_t)(UINT32_C(1)<<1)) +#define LIB9P_DM_OTHER_X ((lib9p_dm_t)(UINT32_C(1)<<0)) +/* masks */ +#define LIB9P_DM_PERM_MASK ((lib9p_dm_t)(0b000000000000000000000111111111)) -#define LIB9P_DM_DIR ((lib9p_dm_t)(1<<31)) -#define LIB9P_DM_APPEND ((lib9p_dm_t)(1<<30)) -#define LIB9P_DM_EXCL ((lib9p_dm_t)(1<<29)) -#define _LIB9P_DM_PLAN9_MOUNT ((lib9p_dm_t)(1<<28)) -#define LIB9P_DM_AUTH ((lib9p_dm_t)(1<<27)) -#define LIB9P_DM_TMP ((lib9p_dm_t)(1<<26)) -/* unused ((lib9p_dm_t)(1<<25)) */ -/* unused ((lib9p_dm_t)(1<<24)) */ -#if defined(CONFIG_9P_ENABLE_9P2000_u) -# define LIB9P_DM_DEVICE ((lib9p_dm_t)(1<<23)) -#endif /* defined(CONFIG_9P_ENABLE_9P2000_u) */ -/* unused ((lib9p_dm_t)(1<<22)) */ -#if defined(CONFIG_9P_ENABLE_9P2000_u) -# define LIB9P_DM_NAMEDPIPE ((lib9p_dm_t)(1<<21)) -# define LIB9P_DM_SOCKET ((lib9p_dm_t)(1<<20)) -# define LIB9P_DM_SETUID ((lib9p_dm_t)(1<<19)) -# define LIB9P_DM_SETGID ((lib9p_dm_t)(1<<18)) -#endif /* defined(CONFIG_9P_ENABLE_9P2000_u) */ -/* unused ((lib9p_dm_t)(1<<17)) */ -/* unused ((lib9p_dm_t)(1<<16)) */ -/* unused ((lib9p_dm_t)(1<<15)) */ -/* unused ((lib9p_dm_t)(1<<14)) */ -/* unused ((lib9p_dm_t)(1<<13)) */ -/* unused ((lib9p_dm_t)(1<<12)) */ -/* unused ((lib9p_dm_t)(1<<11)) */ -/* unused ((lib9p_dm_t)(1<<10)) */ -/* unused ((lib9p_dm_t)(1<<9)) */ -#define LIB9P_DM_OWNER_R ((lib9p_dm_t)(1<<8)) -#define LIB9P_DM_OWNER_W ((lib9p_dm_t)(1<<7)) -#define LIB9P_DM_OWNER_X ((lib9p_dm_t)(1<<6)) -#define LIB9P_DM_GROUP_R ((lib9p_dm_t)(1<<5)) -#define LIB9P_DM_GROUP_W ((lib9p_dm_t)(1<<4)) -#define LIB9P_DM_GROUP_X ((lib9p_dm_t)(1<<3)) -#define LIB9P_DM_OTHER_R ((lib9p_dm_t)(1<<2)) -#define LIB9P_DM_OTHER_W ((lib9p_dm_t)(1<<1)) -#define LIB9P_DM_OTHER_X ((lib9p_dm_t)(1<<0)) - -#define LIB9P_DM_PERM_MASK ((lib9p_dm_t)(0777)) - +#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */ +#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u +/* size = 1 ; max_iov = 1 ; max_copy = 1 */ typedef uint8_t lib9p_qt_t; +LO_IMPLEMENTATION_H(fmt_formatter, lib9p_qt_t, lib9p_qt); +/* bits */ +#define LIB9P_QT_DIR ((lib9p_qt_t)(UINT8_C(1)<<7)) +#define LIB9P_QT_APPEND ((lib9p_qt_t)(UINT8_C(1)<<6)) +#define LIB9P_QT_EXCL ((lib9p_qt_t)(UINT8_C(1)<<5)) +#define _LIB9P_QT_PLAN9_MOUNT ((lib9p_qt_t)(UINT8_C(1)<<4)) +#define LIB9P_QT_AUTH ((lib9p_qt_t)(UINT8_C(1)<<3)) +#define LIB9P_QT_TMP ((lib9p_qt_t)(UINT8_C(1)<<2)) +#if CONFIG_9P_ENABLE_9P2000_u +# define LIB9P_QT_SYMLINK ((lib9p_qt_t)(UINT8_C(1)<<1)) +#endif /* CONFIG_9P_ENABLE_9P2000_u */ +#define _LIB9P_QT_UNUSED_0 ((lib9p_qt_t)(UINT8_C(1)<<0)) +/* aliases */ +#define LIB9P_QT_FILE ((lib9p_qt_t)(0)) -#define LIB9P_QT_DIR ((lib9p_qt_t)(1<<7)) -#define LIB9P_QT_APPEND ((lib9p_qt_t)(1<<6)) -#define LIB9P_QT_EXCL ((lib9p_qt_t)(1<<5)) -#define _LIB9P_QT_PLAN9_MOUNT ((lib9p_qt_t)(1<<4)) -#define LIB9P_QT_AUTH ((lib9p_qt_t)(1<<3)) -#define LIB9P_QT_TMP ((lib9p_qt_t)(1<<2)) -#if defined(CONFIG_9P_ENABLE_9P2000_u) -# define LIB9P_QT_SYMLINK ((lib9p_qt_t)(1<<1)) -#endif /* defined(CONFIG_9P_ENABLE_9P2000_u) */ -/* unused ((lib9p_qt_t)(1<<0)) */ +#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */ +#if CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_u +/* size = 4 ; max_iov = 1 ; max_copy = 4 */ +typedef uint32_t lib9p_nuid_t; +LO_IMPLEMENTATION_H(fmt_formatter, lib9p_nuid_t, lib9p_nuid); +#define LIB9P_NUID_NONUID ((lib9p_nuid_t)(UINT32_MAX)) -#define LIB9P_QT_FILE ((lib9p_qt_t)(0)) +#endif /* CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_u */ +#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u +/* size = 1 ; max_iov = 1 ; max_copy = 1 */ +typedef uint8_t lib9p_o_t; +LO_IMPLEMENTATION_H(fmt_formatter, lib9p_o_t, lib9p_o); +/* bits */ +#define _LIB9P_O_UNUSED_7 ((lib9p_o_t)(UINT8_C(1)<<7)) +#define LIB9P_O_RCLOSE ((lib9p_o_t)(UINT8_C(1)<<6)) +#define _LIB9P_O_RESERVED_CEXEC ((lib9p_o_t)(UINT8_C(1)<<5)) +#define LIB9P_O_TRUNC ((lib9p_o_t)(UINT8_C(1)<<4)) +#define _LIB9P_O_UNUSED_3 ((lib9p_o_t)(UINT8_C(1)<<3)) +#define _LIB9P_O_UNUSED_2 ((lib9p_o_t)(UINT8_C(1)<<2)) +/* number LIB9P_O_MODE_* ((lib9p_o_t)(UINT8_C(1)<<1)) */ +/* number LIB9P_O_MODE_* ((lib9p_o_t)(UINT8_C(1)<<0)) */ +/* masks */ +#define LIB9P_O_FLAG_MASK ((lib9p_o_t)(0b11111100)) +/* number: MODE */ +#define LIB9P_O_MODE_READ ((lib9p_o_t)(0)) +#define LIB9P_O_MODE_WRITE ((lib9p_o_t)(1)) +#define LIB9P_O_MODE_RDWR ((lib9p_o_t)(2)) +#define LIB9P_O_MODE_EXEC ((lib9p_o_t)(3)) +#define LIB9P_O_MODE_MASK ((lib9p_o_t)(0b000011)) -struct lib9p_qid { - lib9p_qt_t type; - uint32_t vers; - uint64_t path; +#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */ +#if CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_u +/* size = 4 ; max_iov = 1 ; max_copy = 4 */ +typedef uint32_t lib9p_errno_t; +LO_IMPLEMENTATION_H(fmt_formatter, lib9p_errno_t, lib9p_errno); +#define LIB9P_ERRNO_NOERROR ((lib9p_errno_t)(0)) + +#endif /* CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_u */ +#if CONFIG_9P_ENABLE_9P2000_L +/* size = 4 ; max_iov = 1 ; max_copy = 4 */ +typedef uint32_t lib9p_super_magic_t; +LO_IMPLEMENTATION_H(fmt_formatter, lib9p_super_magic_t, lib9p_super_magic); +#define LIB9P_SUPER_MAGIC_V9FS_MAGIC ((lib9p_super_magic_t)(16914839)) + +/* size = 4 ; max_iov = 1 ; max_copy = 4 */ +typedef uint32_t lib9p_lo_t; +LO_IMPLEMENTATION_H(fmt_formatter, lib9p_lo_t, lib9p_lo); +/* bits */ +#define _LIB9P_LO_UNUSED_31 ((lib9p_lo_t)(UINT32_C(1)<<31)) +#define _LIB9P_LO_UNUSED_30 ((lib9p_lo_t)(UINT32_C(1)<<30)) +#define _LIB9P_LO_UNUSED_29 ((lib9p_lo_t)(UINT32_C(1)<<29)) +#define _LIB9P_LO_UNUSED_28 ((lib9p_lo_t)(UINT32_C(1)<<28)) +#define _LIB9P_LO_UNUSED_27 ((lib9p_lo_t)(UINT32_C(1)<<27)) +#define _LIB9P_LO_UNUSED_26 ((lib9p_lo_t)(UINT32_C(1)<<26)) +#define _LIB9P_LO_UNUSED_25 ((lib9p_lo_t)(UINT32_C(1)<<25)) +#define _LIB9P_LO_UNUSED_24 ((lib9p_lo_t)(UINT32_C(1)<<24)) +#define _LIB9P_LO_UNUSED_23 ((lib9p_lo_t)(UINT32_C(1)<<23)) +#define _LIB9P_LO_UNUSED_22 ((lib9p_lo_t)(UINT32_C(1)<<22)) +#define _LIB9P_LO_UNUSED_21 ((lib9p_lo_t)(UINT32_C(1)<<21)) +#define LIB9P_LO_SYNC ((lib9p_lo_t)(UINT32_C(1)<<20)) +#define LIB9P_LO_CLOEXEC ((lib9p_lo_t)(UINT32_C(1)<<19)) +#define LIB9P_LO_NOATIME ((lib9p_lo_t)(UINT32_C(1)<<18)) +#define LIB9P_LO_NOFOLLOW ((lib9p_lo_t)(UINT32_C(1)<<17)) +#define LIB9P_LO_DIRECTORY ((lib9p_lo_t)(UINT32_C(1)<<16)) +#define LIB9P_LO_LARGEFILE ((lib9p_lo_t)(UINT32_C(1)<<15)) +#define LIB9P_LO_DIRECT ((lib9p_lo_t)(UINT32_C(1)<<14)) +#define LIB9P_LO_BSD_FASYNC ((lib9p_lo_t)(UINT32_C(1)<<13)) +#define LIB9P_LO_DSYNC ((lib9p_lo_t)(UINT32_C(1)<<12)) +#define LIB9P_LO_NONBLOCK ((lib9p_lo_t)(UINT32_C(1)<<11)) +#define LIB9P_LO_APPEND ((lib9p_lo_t)(UINT32_C(1)<<10)) +#define LIB9P_LO_TRUNC ((lib9p_lo_t)(UINT32_C(1)<<9)) +#define LIB9P_LO_NOCTTY ((lib9p_lo_t)(UINT32_C(1)<<8)) +#define LIB9P_LO_EXCL ((lib9p_lo_t)(UINT32_C(1)<<7)) +#define LIB9P_LO_CREATE ((lib9p_lo_t)(UINT32_C(1)<<6)) +#define _LIB9P_LO_UNUSED_5 ((lib9p_lo_t)(UINT32_C(1)<<5)) +#define _LIB9P_LO_UNUSED_4 ((lib9p_lo_t)(UINT32_C(1)<<4)) +#define _LIB9P_LO_UNUSED_3 ((lib9p_lo_t)(UINT32_C(1)<<3)) +#define _LIB9P_LO_UNUSED_2 ((lib9p_lo_t)(UINT32_C(1)<<2)) +/* number LIB9P_LO_MODE_* ((lib9p_lo_t)(UINT32_C(1)<<1)) */ +/* number LIB9P_LO_MODE_* ((lib9p_lo_t)(UINT32_C(1)<<0)) */ +/* masks */ +#define LIB9P_LO_FLAG_MASK ((lib9p_lo_t)(0b000000000111111111111111000000)) +/* number: MODE */ +#define LIB9P_LO_MODE_RDONLY ((lib9p_lo_t)(0)) +#define LIB9P_LO_MODE_WRONLY ((lib9p_lo_t)(1)) +#define LIB9P_LO_MODE_RDWR ((lib9p_lo_t)(2)) +#define LIB9P_LO_MODE_NOACCESS ((lib9p_lo_t)(3)) +#define LIB9P_LO_MODE_MASK ((lib9p_lo_t)(0b000000000000000000000000000011)) + +/* size = 1 ; max_iov = 1 ; max_copy = 1 */ +typedef uint8_t lib9p_dt_t; +LO_IMPLEMENTATION_H(fmt_formatter, lib9p_dt_t, lib9p_dt); +#define LIB9P_DT_UNKNOWN ((lib9p_dt_t)(0)) +#define LIB9P_DT_PIPE ((lib9p_dt_t)(1)) +#define LIB9P_DT_CHAR_DEV ((lib9p_dt_t)(2)) +#define LIB9P_DT_DIRECTORY ((lib9p_dt_t)(4)) +#define LIB9P_DT_BLOCK_DEV ((lib9p_dt_t)(6)) +#define LIB9P_DT_REGULAR ((lib9p_dt_t)(8)) +#define LIB9P_DT_SYMLINK ((lib9p_dt_t)(10)) +#define LIB9P_DT_SOCKET ((lib9p_dt_t)(12)) +#define _LIB9P_DT_WHITEOUT ((lib9p_dt_t)(14)) + +/* size = 4 ; max_iov = 1 ; max_copy = 4 */ +typedef uint32_t lib9p_mode_t; +LO_IMPLEMENTATION_H(fmt_formatter, lib9p_mode_t, lib9p_mode); +/* bits */ +#define _LIB9P_MODE_UNUSED_31 ((lib9p_mode_t)(UINT32_C(1)<<31)) +#define _LIB9P_MODE_UNUSED_30 ((lib9p_mode_t)(UINT32_C(1)<<30)) +#define _LIB9P_MODE_UNUSED_29 ((lib9p_mode_t)(UINT32_C(1)<<29)) +#define _LIB9P_MODE_UNUSED_28 ((lib9p_mode_t)(UINT32_C(1)<<28)) +#define _LIB9P_MODE_UNUSED_27 ((lib9p_mode_t)(UINT32_C(1)<<27)) +#define _LIB9P_MODE_UNUSED_26 ((lib9p_mode_t)(UINT32_C(1)<<26)) +#define _LIB9P_MODE_UNUSED_25 ((lib9p_mode_t)(UINT32_C(1)<<25)) +#define _LIB9P_MODE_UNUSED_24 ((lib9p_mode_t)(UINT32_C(1)<<24)) +#define _LIB9P_MODE_UNUSED_23 ((lib9p_mode_t)(UINT32_C(1)<<23)) +#define _LIB9P_MODE_UNUSED_22 ((lib9p_mode_t)(UINT32_C(1)<<22)) +#define _LIB9P_MODE_UNUSED_21 ((lib9p_mode_t)(UINT32_C(1)<<21)) +#define _LIB9P_MODE_UNUSED_20 ((lib9p_mode_t)(UINT32_C(1)<<20)) +#define _LIB9P_MODE_UNUSED_19 ((lib9p_mode_t)(UINT32_C(1)<<19)) +#define _LIB9P_MODE_UNUSED_18 ((lib9p_mode_t)(UINT32_C(1)<<18)) +#define _LIB9P_MODE_UNUSED_17 ((lib9p_mode_t)(UINT32_C(1)<<17)) +#define _LIB9P_MODE_UNUSED_16 ((lib9p_mode_t)(UINT32_C(1)<<16)) +/* number LIB9P_MODE_FMT_* ((lib9p_mode_t)(UINT32_C(1)<<15)) */ +/* number LIB9P_MODE_FMT_* ((lib9p_mode_t)(UINT32_C(1)<<14)) */ +/* number LIB9P_MODE_FMT_* ((lib9p_mode_t)(UINT32_C(1)<<13)) */ +/* number LIB9P_MODE_FMT_* ((lib9p_mode_t)(UINT32_C(1)<<12)) */ +#define LIB9P_MODE_PERM_SETGROUP ((lib9p_mode_t)(UINT32_C(1)<<11)) +#define LIB9P_MODE_PERM_SETUSER ((lib9p_mode_t)(UINT32_C(1)<<10)) +#define LIB9P_MODE_PERM_STICKY ((lib9p_mode_t)(UINT32_C(1)<<9)) +#define LIB9P_MODE_PERM_OWNER_R ((lib9p_mode_t)(UINT32_C(1)<<8)) +#define LIB9P_MODE_PERM_OWNER_W ((lib9p_mode_t)(UINT32_C(1)<<7)) +#define LIB9P_MODE_PERM_OWNER_X ((lib9p_mode_t)(UINT32_C(1)<<6)) +#define LIB9P_MODE_PERM_GROUP_R ((lib9p_mode_t)(UINT32_C(1)<<5)) +#define LIB9P_MODE_PERM_GROUP_W ((lib9p_mode_t)(UINT32_C(1)<<4)) +#define LIB9P_MODE_PERM_GROUP_X ((lib9p_mode_t)(UINT32_C(1)<<3)) +#define LIB9P_MODE_PERM_OTHER_R ((lib9p_mode_t)(UINT32_C(1)<<2)) +#define LIB9P_MODE_PERM_OTHER_W ((lib9p_mode_t)(UINT32_C(1)<<1)) +#define LIB9P_MODE_PERM_OTHER_X ((lib9p_mode_t)(UINT32_C(1)<<0)) +/* masks */ +#define LIB9P_MODE_PERM_MASK ((lib9p_mode_t)(0b000000000000000000111111111111)) +/* number: FMT */ +#define LIB9P_MODE_FMT_PIPE ((lib9p_mode_t)(LIB9P_DT_PIPE << 12)) +#define LIB9P_MODE_FMT_CHAR_DEV ((lib9p_mode_t)(LIB9P_DT_CHAR_DEV << 12)) +#define LIB9P_MODE_FMT_DIRECTORY ((lib9p_mode_t)(LIB9P_DT_DIRECTORY << 12)) +#define LIB9P_MODE_FMT_BLOCK_DEV ((lib9p_mode_t)(LIB9P_DT_BLOCK_DEV << 12)) +#define LIB9P_MODE_FMT_REGULAR ((lib9p_mode_t)(LIB9P_DT_REGULAR << 12)) +#define LIB9P_MODE_FMT_SYMLINK ((lib9p_mode_t)(LIB9P_DT_SYMLINK << 12)) +#define LIB9P_MODE_FMT_SOCKET ((lib9p_mode_t)(LIB9P_DT_SOCKET << 12)) +#define LIB9P_MODE_FMT_MASK ((lib9p_mode_t)(0b000000000000001111000000000000)) + +/* size = 4 ; max_iov = 1 ; max_copy = 4 */ +typedef uint32_t lib9p_b4_t; +LO_IMPLEMENTATION_H(fmt_formatter, lib9p_b4_t, lib9p_b4); +#define LIB9P_B4_FALSE ((lib9p_b4_t)(0)) +#define LIB9P_B4_TRUE ((lib9p_b4_t)(1)) + +/* size = 8 ; max_iov = 1 ; max_copy = 8 */ +typedef uint64_t lib9p_getattr_t; +LO_IMPLEMENTATION_H(fmt_formatter, lib9p_getattr_t, lib9p_getattr); +/* bits */ +#define _LIB9P_GETATTR_UNUSED_63 ((lib9p_getattr_t)(UINT64_C(1)<<63)) +#define _LIB9P_GETATTR_UNUSED_62 ((lib9p_getattr_t)(UINT64_C(1)<<62)) +#define _LIB9P_GETATTR_UNUSED_61 ((lib9p_getattr_t)(UINT64_C(1)<<61)) +#define _LIB9P_GETATTR_UNUSED_60 ((lib9p_getattr_t)(UINT64_C(1)<<60)) +#define _LIB9P_GETATTR_UNUSED_59 ((lib9p_getattr_t)(UINT64_C(1)<<59)) +#define _LIB9P_GETATTR_UNUSED_58 ((lib9p_getattr_t)(UINT64_C(1)<<58)) +#define _LIB9P_GETATTR_UNUSED_57 ((lib9p_getattr_t)(UINT64_C(1)<<57)) +#define _LIB9P_GETATTR_UNUSED_56 ((lib9p_getattr_t)(UINT64_C(1)<<56)) +#define _LIB9P_GETATTR_UNUSED_55 ((lib9p_getattr_t)(UINT64_C(1)<<55)) +#define _LIB9P_GETATTR_UNUSED_54 ((lib9p_getattr_t)(UINT64_C(1)<<54)) +#define _LIB9P_GETATTR_UNUSED_53 ((lib9p_getattr_t)(UINT64_C(1)<<53)) +#define _LIB9P_GETATTR_UNUSED_52 ((lib9p_getattr_t)(UINT64_C(1)<<52)) +#define _LIB9P_GETATTR_UNUSED_51 ((lib9p_getattr_t)(UINT64_C(1)<<51)) +#define _LIB9P_GETATTR_UNUSED_50 ((lib9p_getattr_t)(UINT64_C(1)<<50)) +#define _LIB9P_GETATTR_UNUSED_49 ((lib9p_getattr_t)(UINT64_C(1)<<49)) +#define _LIB9P_GETATTR_UNUSED_48 ((lib9p_getattr_t)(UINT64_C(1)<<48)) +#define _LIB9P_GETATTR_UNUSED_47 ((lib9p_getattr_t)(UINT64_C(1)<<47)) +#define _LIB9P_GETATTR_UNUSED_46 ((lib9p_getattr_t)(UINT64_C(1)<<46)) +#define _LIB9P_GETATTR_UNUSED_45 ((lib9p_getattr_t)(UINT64_C(1)<<45)) +#define _LIB9P_GETATTR_UNUSED_44 ((lib9p_getattr_t)(UINT64_C(1)<<44)) +#define _LIB9P_GETATTR_UNUSED_43 ((lib9p_getattr_t)(UINT64_C(1)<<43)) +#define _LIB9P_GETATTR_UNUSED_42 ((lib9p_getattr_t)(UINT64_C(1)<<42)) +#define _LIB9P_GETATTR_UNUSED_41 ((lib9p_getattr_t)(UINT64_C(1)<<41)) +#define _LIB9P_GETATTR_UNUSED_40 ((lib9p_getattr_t)(UINT64_C(1)<<40)) +#define _LIB9P_GETATTR_UNUSED_39 ((lib9p_getattr_t)(UINT64_C(1)<<39)) +#define _LIB9P_GETATTR_UNUSED_38 ((lib9p_getattr_t)(UINT64_C(1)<<38)) +#define _LIB9P_GETATTR_UNUSED_37 ((lib9p_getattr_t)(UINT64_C(1)<<37)) +#define _LIB9P_GETATTR_UNUSED_36 ((lib9p_getattr_t)(UINT64_C(1)<<36)) +#define _LIB9P_GETATTR_UNUSED_35 ((lib9p_getattr_t)(UINT64_C(1)<<35)) +#define _LIB9P_GETATTR_UNUSED_34 ((lib9p_getattr_t)(UINT64_C(1)<<34)) +#define _LIB9P_GETATTR_UNUSED_33 ((lib9p_getattr_t)(UINT64_C(1)<<33)) +#define _LIB9P_GETATTR_UNUSED_32 ((lib9p_getattr_t)(UINT64_C(1)<<32)) +#define _LIB9P_GETATTR_UNUSED_31 ((lib9p_getattr_t)(UINT64_C(1)<<31)) +#define _LIB9P_GETATTR_UNUSED_30 ((lib9p_getattr_t)(UINT64_C(1)<<30)) +#define _LIB9P_GETATTR_UNUSED_29 ((lib9p_getattr_t)(UINT64_C(1)<<29)) +#define _LIB9P_GETATTR_UNUSED_28 ((lib9p_getattr_t)(UINT64_C(1)<<28)) +#define _LIB9P_GETATTR_UNUSED_27 ((lib9p_getattr_t)(UINT64_C(1)<<27)) +#define _LIB9P_GETATTR_UNUSED_26 ((lib9p_getattr_t)(UINT64_C(1)<<26)) +#define _LIB9P_GETATTR_UNUSED_25 ((lib9p_getattr_t)(UINT64_C(1)<<25)) +#define _LIB9P_GETATTR_UNUSED_24 ((lib9p_getattr_t)(UINT64_C(1)<<24)) +#define _LIB9P_GETATTR_UNUSED_23 ((lib9p_getattr_t)(UINT64_C(1)<<23)) +#define _LIB9P_GETATTR_UNUSED_22 ((lib9p_getattr_t)(UINT64_C(1)<<22)) +#define _LIB9P_GETATTR_UNUSED_21 ((lib9p_getattr_t)(UINT64_C(1)<<21)) +#define _LIB9P_GETATTR_UNUSED_20 ((lib9p_getattr_t)(UINT64_C(1)<<20)) +#define _LIB9P_GETATTR_UNUSED_19 ((lib9p_getattr_t)(UINT64_C(1)<<19)) +#define _LIB9P_GETATTR_UNUSED_18 ((lib9p_getattr_t)(UINT64_C(1)<<18)) +#define _LIB9P_GETATTR_UNUSED_17 ((lib9p_getattr_t)(UINT64_C(1)<<17)) +#define _LIB9P_GETATTR_UNUSED_16 ((lib9p_getattr_t)(UINT64_C(1)<<16)) +#define _LIB9P_GETATTR_UNUSED_15 ((lib9p_getattr_t)(UINT64_C(1)<<15)) +#define _LIB9P_GETATTR_UNUSED_14 ((lib9p_getattr_t)(UINT64_C(1)<<14)) +#define LIB9P_GETATTR_DATA_VERSION ((lib9p_getattr_t)(UINT64_C(1)<<13)) +#define LIB9P_GETATTR_GEN ((lib9p_getattr_t)(UINT64_C(1)<<12)) +#define LIB9P_GETATTR_BTIME ((lib9p_getattr_t)(UINT64_C(1)<<11)) +#define LIB9P_GETATTR_BLOCKS ((lib9p_getattr_t)(UINT64_C(1)<<10)) +#define LIB9P_GETATTR_SIZE ((lib9p_getattr_t)(UINT64_C(1)<<9)) +#define LIB9P_GETATTR_INO ((lib9p_getattr_t)(UINT64_C(1)<<8)) +#define LIB9P_GETATTR_CTIME ((lib9p_getattr_t)(UINT64_C(1)<<7)) +#define LIB9P_GETATTR_MTIME ((lib9p_getattr_t)(UINT64_C(1)<<6)) +#define LIB9P_GETATTR_ATIME ((lib9p_getattr_t)(UINT64_C(1)<<5)) +#define LIB9P_GETATTR_RDEV ((lib9p_getattr_t)(UINT64_C(1)<<4)) +#define LIB9P_GETATTR_GID ((lib9p_getattr_t)(UINT64_C(1)<<3)) +#define LIB9P_GETATTR_UID ((lib9p_getattr_t)(UINT64_C(1)<<2)) +#define LIB9P_GETATTR_NLINK ((lib9p_getattr_t)(UINT64_C(1)<<1)) +#define LIB9P_GETATTR_MODE ((lib9p_getattr_t)(UINT64_C(1)<<0)) +/* aliases */ +#define LIB9P_GETATTR_BASIC ((lib9p_getattr_t)(2047)) +#define LIB9P_GETATTR_ALL ((lib9p_getattr_t)(16383)) + +/* size = 4 ; max_iov = 1 ; max_copy = 4 */ +typedef uint32_t lib9p_setattr_t; +LO_IMPLEMENTATION_H(fmt_formatter, lib9p_setattr_t, lib9p_setattr); +/* bits */ +#define _LIB9P_SETATTR_UNUSED_31 ((lib9p_setattr_t)(UINT32_C(1)<<31)) +#define _LIB9P_SETATTR_UNUSED_30 ((lib9p_setattr_t)(UINT32_C(1)<<30)) +#define _LIB9P_SETATTR_UNUSED_29 ((lib9p_setattr_t)(UINT32_C(1)<<29)) +#define _LIB9P_SETATTR_UNUSED_28 ((lib9p_setattr_t)(UINT32_C(1)<<28)) +#define _LIB9P_SETATTR_UNUSED_27 ((lib9p_setattr_t)(UINT32_C(1)<<27)) +#define _LIB9P_SETATTR_UNUSED_26 ((lib9p_setattr_t)(UINT32_C(1)<<26)) +#define _LIB9P_SETATTR_UNUSED_25 ((lib9p_setattr_t)(UINT32_C(1)<<25)) +#define _LIB9P_SETATTR_UNUSED_24 ((lib9p_setattr_t)(UINT32_C(1)<<24)) +#define _LIB9P_SETATTR_UNUSED_23 ((lib9p_setattr_t)(UINT32_C(1)<<23)) +#define _LIB9P_SETATTR_UNUSED_22 ((lib9p_setattr_t)(UINT32_C(1)<<22)) +#define _LIB9P_SETATTR_UNUSED_21 ((lib9p_setattr_t)(UINT32_C(1)<<21)) +#define _LIB9P_SETATTR_UNUSED_20 ((lib9p_setattr_t)(UINT32_C(1)<<20)) +#define _LIB9P_SETATTR_UNUSED_19 ((lib9p_setattr_t)(UINT32_C(1)<<19)) +#define _LIB9P_SETATTR_UNUSED_18 ((lib9p_setattr_t)(UINT32_C(1)<<18)) +#define _LIB9P_SETATTR_UNUSED_17 ((lib9p_setattr_t)(UINT32_C(1)<<17)) +#define _LIB9P_SETATTR_UNUSED_16 ((lib9p_setattr_t)(UINT32_C(1)<<16)) +#define _LIB9P_SETATTR_UNUSED_15 ((lib9p_setattr_t)(UINT32_C(1)<<15)) +#define _LIB9P_SETATTR_UNUSED_14 ((lib9p_setattr_t)(UINT32_C(1)<<14)) +#define _LIB9P_SETATTR_UNUSED_13 ((lib9p_setattr_t)(UINT32_C(1)<<13)) +#define _LIB9P_SETATTR_UNUSED_12 ((lib9p_setattr_t)(UINT32_C(1)<<12)) +#define _LIB9P_SETATTR_UNUSED_11 ((lib9p_setattr_t)(UINT32_C(1)<<11)) +#define _LIB9P_SETATTR_UNUSED_10 ((lib9p_setattr_t)(UINT32_C(1)<<10)) +#define _LIB9P_SETATTR_UNUSED_9 ((lib9p_setattr_t)(UINT32_C(1)<<9)) +#define LIB9P_SETATTR_MTIME_SET ((lib9p_setattr_t)(UINT32_C(1)<<8)) +#define LIB9P_SETATTR_ATIME_SET ((lib9p_setattr_t)(UINT32_C(1)<<7)) +#define LIB9P_SETATTR_CTIME ((lib9p_setattr_t)(UINT32_C(1)<<6)) +#define LIB9P_SETATTR_MTIME ((lib9p_setattr_t)(UINT32_C(1)<<5)) +#define LIB9P_SETATTR_ATIME ((lib9p_setattr_t)(UINT32_C(1)<<4)) +#define LIB9P_SETATTR_SIZE ((lib9p_setattr_t)(UINT32_C(1)<<3)) +#define LIB9P_SETATTR_GID ((lib9p_setattr_t)(UINT32_C(1)<<2)) +#define LIB9P_SETATTR_UID ((lib9p_setattr_t)(UINT32_C(1)<<1)) +#define LIB9P_SETATTR_MODE ((lib9p_setattr_t)(UINT32_C(1)<<0)) + +/* size = 1 ; max_iov = 1 ; max_copy = 1 */ +typedef uint8_t lib9p_lock_type_t; +LO_IMPLEMENTATION_H(fmt_formatter, lib9p_lock_type_t, lib9p_lock_type); +#define LIB9P_LOCK_TYPE_RDLCK ((lib9p_lock_type_t)(0)) +#define LIB9P_LOCK_TYPE_WRLCK ((lib9p_lock_type_t)(1)) +#define LIB9P_LOCK_TYPE_UNLCK ((lib9p_lock_type_t)(2)) + +/* size = 4 ; max_iov = 1 ; max_copy = 4 */ +typedef uint32_t lib9p_lock_flags_t; +LO_IMPLEMENTATION_H(fmt_formatter, lib9p_lock_flags_t, lib9p_lock_flags); +/* bits */ +#define _LIB9P_LOCK_FLAGS_UNUSED_31 ((lib9p_lock_flags_t)(UINT32_C(1)<<31)) +#define _LIB9P_LOCK_FLAGS_UNUSED_30 ((lib9p_lock_flags_t)(UINT32_C(1)<<30)) +#define _LIB9P_LOCK_FLAGS_UNUSED_29 ((lib9p_lock_flags_t)(UINT32_C(1)<<29)) +#define _LIB9P_LOCK_FLAGS_UNUSED_28 ((lib9p_lock_flags_t)(UINT32_C(1)<<28)) +#define _LIB9P_LOCK_FLAGS_UNUSED_27 ((lib9p_lock_flags_t)(UINT32_C(1)<<27)) +#define _LIB9P_LOCK_FLAGS_UNUSED_26 ((lib9p_lock_flags_t)(UINT32_C(1)<<26)) +#define _LIB9P_LOCK_FLAGS_UNUSED_25 ((lib9p_lock_flags_t)(UINT32_C(1)<<25)) +#define _LIB9P_LOCK_FLAGS_UNUSED_24 ((lib9p_lock_flags_t)(UINT32_C(1)<<24)) +#define _LIB9P_LOCK_FLAGS_UNUSED_23 ((lib9p_lock_flags_t)(UINT32_C(1)<<23)) +#define _LIB9P_LOCK_FLAGS_UNUSED_22 ((lib9p_lock_flags_t)(UINT32_C(1)<<22)) +#define _LIB9P_LOCK_FLAGS_UNUSED_21 ((lib9p_lock_flags_t)(UINT32_C(1)<<21)) +#define _LIB9P_LOCK_FLAGS_UNUSED_20 ((lib9p_lock_flags_t)(UINT32_C(1)<<20)) +#define _LIB9P_LOCK_FLAGS_UNUSED_19 ((lib9p_lock_flags_t)(UINT32_C(1)<<19)) +#define _LIB9P_LOCK_FLAGS_UNUSED_18 ((lib9p_lock_flags_t)(UINT32_C(1)<<18)) +#define _LIB9P_LOCK_FLAGS_UNUSED_17 ((lib9p_lock_flags_t)(UINT32_C(1)<<17)) +#define _LIB9P_LOCK_FLAGS_UNUSED_16 ((lib9p_lock_flags_t)(UINT32_C(1)<<16)) +#define _LIB9P_LOCK_FLAGS_UNUSED_15 ((lib9p_lock_flags_t)(UINT32_C(1)<<15)) +#define _LIB9P_LOCK_FLAGS_UNUSED_14 ((lib9p_lock_flags_t)(UINT32_C(1)<<14)) +#define _LIB9P_LOCK_FLAGS_UNUSED_13 ((lib9p_lock_flags_t)(UINT32_C(1)<<13)) +#define _LIB9P_LOCK_FLAGS_UNUSED_12 ((lib9p_lock_flags_t)(UINT32_C(1)<<12)) +#define _LIB9P_LOCK_FLAGS_UNUSED_11 ((lib9p_lock_flags_t)(UINT32_C(1)<<11)) +#define _LIB9P_LOCK_FLAGS_UNUSED_10 ((lib9p_lock_flags_t)(UINT32_C(1)<<10)) +#define _LIB9P_LOCK_FLAGS_UNUSED_9 ((lib9p_lock_flags_t)(UINT32_C(1)<<9)) +#define _LIB9P_LOCK_FLAGS_UNUSED_8 ((lib9p_lock_flags_t)(UINT32_C(1)<<8)) +#define _LIB9P_LOCK_FLAGS_UNUSED_7 ((lib9p_lock_flags_t)(UINT32_C(1)<<7)) +#define _LIB9P_LOCK_FLAGS_UNUSED_6 ((lib9p_lock_flags_t)(UINT32_C(1)<<6)) +#define _LIB9P_LOCK_FLAGS_UNUSED_5 ((lib9p_lock_flags_t)(UINT32_C(1)<<5)) +#define _LIB9P_LOCK_FLAGS_UNUSED_4 ((lib9p_lock_flags_t)(UINT32_C(1)<<4)) +#define _LIB9P_LOCK_FLAGS_UNUSED_3 ((lib9p_lock_flags_t)(UINT32_C(1)<<3)) +#define _LIB9P_LOCK_FLAGS_UNUSED_2 ((lib9p_lock_flags_t)(UINT32_C(1)<<2)) +#define LIB9P_LOCK_FLAGS_RECLAIM ((lib9p_lock_flags_t)(UINT32_C(1)<<1)) +#define LIB9P_LOCK_FLAGS_BLOCK ((lib9p_lock_flags_t)(UINT32_C(1)<<0)) + +/* size = 1 ; max_iov = 1 ; max_copy = 1 */ +typedef uint8_t lib9p_lock_status_t; +LO_IMPLEMENTATION_H(fmt_formatter, lib9p_lock_status_t, lib9p_lock_status); +#define LIB9P_LOCK_STATUS_SUCCESS ((lib9p_lock_status_t)(0)) +#define LIB9P_LOCK_STATUS_BLOCKED ((lib9p_lock_status_t)(1)) +#define LIB9P_LOCK_STATUS_ERROR ((lib9p_lock_status_t)(2)) +#define LIB9P_LOCK_STATUS_GRACE ((lib9p_lock_status_t)(3)) + +#endif /* CONFIG_9P_ENABLE_9P2000_L */ +#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u +/* size = 9 ; max_iov = 1 ; max_copy = 9 */ +struct lib9p_msg_Tflush { + lib9p_tag_t tag; + uint16_t oldtag; }; +LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Tflush, lib9p_msg_Tflush); -struct lib9p_stat { - uint16_t kern_type; - uint32_t kern_dev; - struct lib9p_qid file_qid; - lib9p_dm_t file_mode; - uint32_t file_atime; - uint32_t file_mtime; - uint64_t file_size; - struct lib9p_s file_name; - struct lib9p_s file_owner_uid; - struct lib9p_s file_owner_gid; - struct lib9p_s file_last_modified_uid; -#if defined(CONFIG_9P_ENABLE_9P2000_u) - struct lib9p_s file_extension; - uint32_t file_owner_n_uid; - uint32_t file_owner_n_gid; - uint32_t file_last_modified_n_uid; -#endif /* defined(CONFIG_9P_ENABLE_9P2000_u) */ +/* size = 7 ; max_iov = 1 ; max_copy = 7 */ +struct lib9p_msg_Rflush { + lib9p_tag_t tag; }; +LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Rflush, lib9p_msg_Rflush); -typedef uint8_t lib9p_o_t; +/* min_size = 11 ; exp_size = 8,203 ; max_size = 2,147,483,658 ; max_iov = 2 ; max_copy = 11 */ +struct lib9p_msg_Rread { + lib9p_tag_t tag; + uint32_t count; + [[gnu::nonstring]] char *data; +}; +LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Rread, lib9p_msg_Rread); -/* unused ((lib9p_o_t)(1<<7)) */ -#define LIB9P_O_RCLOSE ((lib9p_o_t)(1<<6)) -/* unused ((lib9p_o_t)(1<<5)) */ -#define LIB9P_O_TRUNC ((lib9p_o_t)(1<<4)) -/* unused ((lib9p_o_t)(1<<3)) */ -/* unused ((lib9p_o_t)(1<<2)) */ -#define LIB9P_O_mode_1 ((lib9p_o_t)(1<<1)) -#define LIB9P_O_mode_0 ((lib9p_o_t)(1<<0)) - -#define LIB9P_O_READ ((lib9p_o_t)(0)) -#define LIB9P_O_WRITE ((lib9p_o_t)(1)) -#define LIB9P_O_RDWR ((lib9p_o_t)(2)) -#define LIB9P_O_EXEC ((lib9p_o_t)(3)) -#define LIB9P_O_MODE_MASK ((lib9p_o_t)(0b00000011)) -#define LIB9P_O_FLAG_MASK ((lib9p_o_t)(0b11111100)) -#endif /* defined(CONFIG_9P_ENABLE_9P2000) || defined(CONFIG_9P_ENABLE_9P2000_e) || defined(CONFIG_9P_ENABLE_9P2000_u) */ - -/* messages *******************************************************************/ +/* size = 11 ; max_iov = 1 ; max_copy = 11 */ +struct lib9p_msg_Rwrite { + lib9p_tag_t tag; + uint32_t count; +}; +LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Rwrite, lib9p_msg_Rwrite); -enum lib9p_msg_type { /* uint8_t */ -#if defined(CONFIG_9P_ENABLE_9P2000) || defined(CONFIG_9P_ENABLE_9P2000_e) || defined(CONFIG_9P_ENABLE_9P2000_u) - LIB9P_TYP_Tversion = 100, - LIB9P_TYP_Rversion = 101, - LIB9P_TYP_Tauth = 102, - LIB9P_TYP_Rauth = 103, - LIB9P_TYP_Tattach = 104, - LIB9P_TYP_Rattach = 105, - LIB9P_TYP_Rerror = 107, - LIB9P_TYP_Tflush = 108, - LIB9P_TYP_Rflush = 109, - LIB9P_TYP_Twalk = 110, - LIB9P_TYP_Rwalk = 111, - LIB9P_TYP_Topen = 112, - LIB9P_TYP_Ropen = 113, - LIB9P_TYP_Tcreate = 114, - LIB9P_TYP_Rcreate = 115, - LIB9P_TYP_Tread = 116, - LIB9P_TYP_Rread = 117, - LIB9P_TYP_Twrite = 118, - LIB9P_TYP_Rwrite = 119, - LIB9P_TYP_Tclunk = 120, - LIB9P_TYP_Rclunk = 121, - LIB9P_TYP_Tremove = 122, - LIB9P_TYP_Rremove = 123, - LIB9P_TYP_Tstat = 124, - LIB9P_TYP_Rstat = 125, - LIB9P_TYP_Twstat = 126, - LIB9P_TYP_Rwstat = 127, -#endif /* defined(CONFIG_9P_ENABLE_9P2000) || defined(CONFIG_9P_ENABLE_9P2000_e) || defined(CONFIG_9P_ENABLE_9P2000_u) */ -#if defined(CONFIG_9P_ENABLE_9P2000_e) - LIB9P_TYP_Tsession = 150, - LIB9P_TYP_Rsession = 151, - LIB9P_TYP_Tsread = 152, - LIB9P_TYP_Rsread = 153, - LIB9P_TYP_Tswrite = 154, - LIB9P_TYP_Rswrite = 155, -#endif /* defined(CONFIG_9P_ENABLE_9P2000_e) */ -}; - -#if defined(CONFIG_9P_ENABLE_9P2000) || defined(CONFIG_9P_ENABLE_9P2000_e) || defined(CONFIG_9P_ENABLE_9P2000_u) +/* size = 7 ; max_iov = 1 ; max_copy = 7 */ +struct lib9p_msg_Rclunk { + lib9p_tag_t tag; +}; +LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Rclunk, lib9p_msg_Rclunk); + +/* size = 7 ; max_iov = 1 ; max_copy = 7 */ +struct lib9p_msg_Rremove { + lib9p_tag_t tag; +}; +LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Rremove, lib9p_msg_Rremove); + +#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */ +#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u +/* size = 7 ; max_iov = 1 ; max_copy = 7 */ +struct lib9p_msg_Rwstat { + lib9p_tag_t tag; +}; +LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Rwstat, lib9p_msg_Rwstat); + +#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */ +#if CONFIG_9P_ENABLE_9P2000_L +/* size = 7 ; max_iov = 1 ; max_copy = 7 */ +struct lib9p_msg_Rrename { + lib9p_tag_t tag; +}; +LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Rrename, lib9p_msg_Rrename); + +/* size = 7 ; max_iov = 1 ; max_copy = 7 */ +struct lib9p_msg_Rsetattr { + lib9p_tag_t tag; +}; +LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Rsetattr, lib9p_msg_Rsetattr); + +/* size = 15 ; max_iov = 1 ; max_copy = 15 */ +struct lib9p_msg_Rxattrwalk { + lib9p_tag_t tag; + uint64_t attr_size; +}; +LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Rxattrwalk, lib9p_msg_Rxattrwalk); + +/* size = 7 ; max_iov = 1 ; max_copy = 7 */ +struct lib9p_msg_Rxattrcreate { + lib9p_tag_t tag; +}; +LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Rxattrcreate, lib9p_msg_Rxattrcreate); + +/* min_size = 11 ; exp_size = 8,203 ; max_size = 4,294,967,306 (warning: >UINT32_MAX) ; max_iov = 2 ; max_copy = 11 */ +struct lib9p_msg_Rreaddir { + lib9p_tag_t tag; + uint32_t count; + [[gnu::nonstring]] char *data; +}; +LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Rreaddir, lib9p_msg_Rreaddir); + +/* size = 7 ; max_iov = 1 ; max_copy = 7 */ +struct lib9p_msg_Rfsync { + lib9p_tag_t tag; +}; +LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Rfsync, lib9p_msg_Rfsync); + +/* size = 7 ; max_iov = 1 ; max_copy = 7 */ +struct lib9p_msg_Rlink { + lib9p_tag_t tag; +}; +LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Rlink, lib9p_msg_Rlink); + +/* size = 7 ; max_iov = 1 ; max_copy = 7 */ +struct lib9p_msg_Rrenameat { + lib9p_tag_t tag; +}; +LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Rrenameat, lib9p_msg_Rrenameat); + +/* size = 7 ; max_iov = 1 ; max_copy = 7 */ +struct lib9p_msg_Runlinkat { + lib9p_tag_t tag; +}; +LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Runlinkat, lib9p_msg_Runlinkat); + +#endif /* CONFIG_9P_ENABLE_9P2000_L */ +#if CONFIG_9P_ENABLE_9P2000_e +/* size = 15 ; max_iov = 1 ; max_copy = 15 */ +struct lib9p_msg_Tsession { + lib9p_tag_t tag; + uint64_t key; +}; +LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Tsession, lib9p_msg_Tsession); + +/* size = 7 ; max_iov = 1 ; max_copy = 7 */ +struct lib9p_msg_Rsession { + lib9p_tag_t tag; +}; +LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Rsession, lib9p_msg_Rsession); + +/* min_size = 11 ; exp_size = 8,203 ; max_size = 4,294,967,306 (warning: >UINT32_MAX) ; max_iov = 2 ; max_copy = 11 */ +struct lib9p_msg_Rsread { + lib9p_tag_t tag; + uint32_t count; + [[gnu::nonstring]] char *data; +}; +LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Rsread, lib9p_msg_Rsread); + +/* size = 11 ; max_iov = 1 ; max_copy = 11 */ +struct lib9p_msg_Rswrite { + lib9p_tag_t tag; + uint32_t count; +}; +LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Rswrite, lib9p_msg_Rswrite); + +#endif /* CONFIG_9P_ENABLE_9P2000_e */ +#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u +/* size = 23 ; max_iov = 1 ; max_copy = 23 */ +struct lib9p_msg_Tread { + lib9p_tag_t tag; + lib9p_fid_t fid; + uint64_t offset; + uint32_t count; +}; +LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Tread, lib9p_msg_Tread); + +/* min_size = 23 ; exp_size = 8,215 ; max_size = 2,147,483,670 ; max_iov = 2 ; max_copy = 23 */ +struct lib9p_msg_Twrite { + lib9p_tag_t tag; + lib9p_fid_t fid; + uint64_t offset; + uint32_t count; + [[gnu::nonstring]] char *data; +}; +LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Twrite, lib9p_msg_Twrite); + +/* size = 11 ; max_iov = 1 ; max_copy = 11 */ +struct lib9p_msg_Tclunk { + lib9p_tag_t tag; + lib9p_fid_t fid; +}; +LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Tclunk, lib9p_msg_Tclunk); + +/* size = 11 ; max_iov = 1 ; max_copy = 11 */ +struct lib9p_msg_Tremove { + lib9p_tag_t tag; + lib9p_fid_t fid; +}; +LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Tremove, lib9p_msg_Tremove); + +#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */ +#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u +/* size = 11 ; max_iov = 1 ; max_copy = 11 */ +struct lib9p_msg_Tstat { + lib9p_tag_t tag; + lib9p_fid_t fid; +}; +LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Tstat, lib9p_msg_Tstat); + +#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */ +#if CONFIG_9P_ENABLE_9P2000_L +/* size = 11 ; max_iov = 1 ; max_copy = 11 */ +struct lib9p_msg_Tstatfs { + lib9p_tag_t tag; + lib9p_fid_t fid; +}; +LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Tstatfs, lib9p_msg_Tstatfs); + +/* size = 11 ; max_iov = 1 ; max_copy = 11 */ +struct lib9p_msg_Treadlink { + lib9p_tag_t tag; + lib9p_fid_t fid; +}; +LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Treadlink, lib9p_msg_Treadlink); + +/* size = 23 ; max_iov = 1 ; max_copy = 23 */ +struct lib9p_msg_Treaddir { + lib9p_tag_t tag; + lib9p_fid_t fid; + uint64_t offset; + uint32_t count; +}; +LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Treaddir, lib9p_msg_Treaddir); + +#endif /* CONFIG_9P_ENABLE_9P2000_L */ +#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u +/* min_size = 13 ; exp_size = 40 ; max_size = 65,548 ; max_iov = 2 ; max_copy = 13 */ struct lib9p_msg_Tversion { lib9p_tag_t tag; uint32_t max_msg_size; struct lib9p_s version; }; +LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Tversion, lib9p_msg_Tversion); +/* min_size = 13 ; exp_size = 40 ; max_size = 65,548 ; max_iov = 2 ; max_copy = 13 */ struct lib9p_msg_Rversion { lib9p_tag_t tag; uint32_t max_msg_size; struct lib9p_s version; }; +LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Rversion, lib9p_msg_Rversion); -struct lib9p_msg_Tauth { +/* min_size = 17 ; exp_size = 481 ; max_size = 1,048,609 ; max_iov = 32 ; max_copy = 49 */ +struct lib9p_msg_Twalk { lib9p_tag_t tag; - lib9p_fid_t afid; - struct lib9p_s uname; - struct lib9p_s aname; -#if defined(CONFIG_9P_ENABLE_9P2000_u) - uint32_t n_uname; -#endif /* defined(CONFIG_9P_ENABLE_9P2000_u) */ + lib9p_fid_t fid; + lib9p_fid_t newfid; + uint16_t nwname; + struct lib9p_s *wname; }; +LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Twalk, lib9p_msg_Twalk); -struct lib9p_msg_Rauth { - lib9p_tag_t tag; - struct lib9p_qid aqid; +#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */ +#if CONFIG_9P_ENABLE_9P2000_L +/* min_size = 17 ; exp_size = 44 ; max_size = 65,552 ; max_iov = 2 ; max_copy = 17 */ +struct lib9p_msg_Trename { + lib9p_tag_t tag; + lib9p_fid_t fid; + lib9p_fid_t dfid; + struct lib9p_s name; }; +LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Trename, lib9p_msg_Trename); -struct lib9p_msg_Tattach { +/* min_size = 9 ; exp_size = 36 ; max_size = 65,544 ; max_iov = 2 ; max_copy = 9 */ +struct lib9p_msg_Rreadlink { + lib9p_tag_t tag; + struct lib9p_s target; +}; +LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Rreadlink, lib9p_msg_Rreadlink); + +/* min_size = 17 ; exp_size = 44 ; max_size = 65,552 ; max_iov = 2 ; max_copy = 17 */ +struct lib9p_msg_Txattrwalk { lib9p_tag_t tag; lib9p_fid_t fid; - lib9p_fid_t afid; - struct lib9p_s uname; - struct lib9p_s aname; -#if defined(CONFIG_9P_ENABLE_9P2000_u) - uint32_t n_uname; -#endif /* defined(CONFIG_9P_ENABLE_9P2000_u) */ + lib9p_fid_t newfid; + struct lib9p_s name; }; +LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Txattrwalk, lib9p_msg_Txattrwalk); -struct lib9p_msg_Rattach { - lib9p_tag_t tag; - struct lib9p_qid qid; +/* min_size = 25 ; exp_size = 52 ; max_size = 65,560 ; max_iov = 3 ; max_copy = 25 */ +struct lib9p_msg_Txattrcreate { + lib9p_tag_t tag; + lib9p_fid_t fid; + struct lib9p_s name; + uint64_t attr_size; + uint32_t flags; }; +LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Txattrcreate, lib9p_msg_Txattrcreate); -struct lib9p_msg_Rerror { +/* min_size = 17 ; exp_size = 44 ; max_size = 65,552 ; max_iov = 2 ; max_copy = 17 */ +struct lib9p_msg_Tlink { lib9p_tag_t tag; - struct lib9p_s ename; -#if defined(CONFIG_9P_ENABLE_9P2000_u) - uint32_t errno; -#endif /* defined(CONFIG_9P_ENABLE_9P2000_u) */ + lib9p_fid_t dfid; + lib9p_fid_t fid; + struct lib9p_s name; }; +LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Tlink, lib9p_msg_Tlink); -struct lib9p_msg_Tflush { - lib9p_tag_t tag; - uint16_t oldtag; +/* min_size = 19 ; exp_size = 73 ; max_size = 131,089 ; max_iov = 4 ; max_copy = 19 */ +struct lib9p_msg_Trenameat { + lib9p_tag_t tag; + lib9p_fid_t olddirfid; + struct lib9p_s oldname; + lib9p_fid_t newdirfid; + struct lib9p_s newname; }; +LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Trenameat, lib9p_msg_Trenameat); -struct lib9p_msg_Rflush { - lib9p_tag_t tag; +/* min_size = 17 ; exp_size = 44 ; max_size = 65,552 ; max_iov = 3 ; max_copy = 17 */ +struct lib9p_msg_Tunlinkat { + lib9p_tag_t tag; + lib9p_fid_t dirfd; + struct lib9p_s name; + uint32_t flags; }; +LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Tunlinkat, lib9p_msg_Tunlinkat); -struct lib9p_msg_Twalk { +#endif /* CONFIG_9P_ENABLE_9P2000_L */ +#if CONFIG_9P_ENABLE_9P2000_e +/* min_size = 13 ; exp_size = 477 ; max_size = 4,294,967,308 (warning: >UINT32_MAX) ; max_iov = 0 + (CONFIG_9P_MAX_9P2000_e_WELEM * 2) ; max_copy = 13 + (CONFIG_9P_MAX_9P2000_e_WELEM * 2) */ +struct lib9p_msg_Tsread { lib9p_tag_t tag; - lib9p_fid_t fid; - lib9p_fid_t newfid; + uint32_t fid; uint16_t nwname; struct lib9p_s *wname; }; +LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Tsread, lib9p_msg_Tsread); -struct lib9p_msg_Rwalk { - lib9p_tag_t tag; - uint16_t nwqid; - struct lib9p_qid *wqid; +/* min_size = 17 ; exp_size = 8,673 ; max_size = 8,589,934,607 (warning: >UINT32_MAX) ; max_iov = 2 + (CONFIG_9P_MAX_9P2000_e_WELEM * 2) ; max_copy = 17 + (CONFIG_9P_MAX_9P2000_e_WELEM * 2) */ +struct lib9p_msg_Tswrite { + lib9p_tag_t tag; + uint32_t fid; + uint16_t nwname; + struct lib9p_s *wname; + uint32_t count; + [[gnu::nonstring]] char *data; }; +LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Tswrite, lib9p_msg_Tswrite); +#endif /* CONFIG_9P_ENABLE_9P2000_e */ +#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u +/* size = 13 ; max_iov = 1 ; max_copy = 13 */ +struct lib9p_qid { + lib9p_qt_t type; + uint32_t vers; + uint64_t path; +}; +LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_qid, lib9p_qid); + +/* LIB9P_VER_9P2000 : min_size = 15 ; exp_size = 69 ; max_size = 131,085 ; max_iov = 4 ; max_copy = 15 */ +/* LIB9P_VER_9P2000_L : min_size = 19 ; exp_size = 73 ; max_size = 131,089 ; max_iov = 5 ; max_copy = 19 */ +/* LIB9P_VER_9P2000_e : min_size = 15 ; exp_size = 69 ; max_size = 131,085 ; max_iov = 4 ; max_copy = 15 */ +/* LIB9P_VER_9P2000_p9p: min_size = 15 ; exp_size = 69 ; max_size = 131,085 ; max_iov = 4 ; max_copy = 15 */ +/* LIB9P_VER_9P2000_u : min_size = 19 ; exp_size = 73 ; max_size = 131,089 ; max_iov = 5 ; max_copy = 19 */ +struct lib9p_msg_Tauth { + lib9p_tag_t tag; + lib9p_fid_t afid; + struct lib9p_s uname; + struct lib9p_s aname; +#if CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_u + lib9p_nuid_t n_uid; +#endif /* CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_u */ +}; +LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Tauth, lib9p_msg_Tauth); + +/* LIB9P_VER_9P2000 : min_size = 19 ; exp_size = 73 ; max_size = 131,089 ; max_iov = 4 ; max_copy = 19 */ +/* LIB9P_VER_9P2000_L : min_size = 23 ; exp_size = 77 ; max_size = 131,093 ; max_iov = 5 ; max_copy = 23 */ +/* LIB9P_VER_9P2000_e : min_size = 19 ; exp_size = 73 ; max_size = 131,089 ; max_iov = 4 ; max_copy = 19 */ +/* LIB9P_VER_9P2000_p9p: min_size = 19 ; exp_size = 73 ; max_size = 131,089 ; max_iov = 4 ; max_copy = 19 */ +/* LIB9P_VER_9P2000_u : min_size = 23 ; exp_size = 77 ; max_size = 131,093 ; max_iov = 5 ; max_copy = 23 */ +struct lib9p_msg_Tattach { + lib9p_tag_t tag; + lib9p_fid_t fid; + lib9p_fid_t afid; + struct lib9p_s uname; + struct lib9p_s aname; +#if CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_u + lib9p_nuid_t n_uid; +#endif /* CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_u */ +}; +LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Tattach, lib9p_msg_Tattach); + +#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */ +#if CONFIG_9P_ENABLE_9P2000_L +/* min_size = 19 ; exp_size = 73 ; max_size = 131,089 ; max_iov = 5 ; max_copy = 19 */ +struct lib9p_msg_Tsymlink { + lib9p_tag_t tag; + lib9p_fid_t fid; + struct lib9p_s name; + struct lib9p_s symtgt; + lib9p_nuid_t gid; +}; +LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Tsymlink, lib9p_msg_Tsymlink); + +#endif /* CONFIG_9P_ENABLE_9P2000_L */ +#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u +/* size = 12 ; max_iov = 1 ; max_copy = 12 */ struct lib9p_msg_Topen { lib9p_tag_t tag; lib9p_fid_t fid; lib9p_o_t mode; }; +LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Topen, lib9p_msg_Topen); -struct lib9p_msg_Ropen { - lib9p_tag_t tag; - struct lib9p_qid qid; - uint32_t iounit; -}; - +/* min_size = 18 ; exp_size = 45 ; max_size = 65,553 ; max_iov = 3 ; max_copy = 18 */ struct lib9p_msg_Tcreate { lib9p_tag_t tag; lib9p_fid_t fid; @@ -278,108 +954,395 @@ struct lib9p_msg_Tcreate { lib9p_dm_t perm; lib9p_o_t mode; }; +LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Tcreate, lib9p_msg_Tcreate); -struct lib9p_msg_Rcreate { - lib9p_tag_t tag; - struct lib9p_qid qid; - uint32_t iounit; +#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */ +#if CONFIG_9P_ENABLE_9P2000_p9p +/* size = 12 ; max_iov = 1 ; max_copy = 12 */ +struct lib9p_msg_Topenfd { + lib9p_tag_t tag; + lib9p_fid_t fid; + lib9p_o_t mode; }; +LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Topenfd, lib9p_msg_Topenfd); -struct lib9p_msg_Tread { +#endif /* CONFIG_9P_ENABLE_9P2000_p9p */ +#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u +/* LIB9P_VER_9P2000 : min_size = 9 ; exp_size = 36 ; max_size = 65,544 ; max_iov = 2 ; max_copy = 9 */ +/* LIB9P_VER_9P2000_L : min_size = 9 ; exp_size = 36 ; max_size = 65,544 ; max_iov = 2 ; max_copy = 9 */ +/* LIB9P_VER_9P2000_e : min_size = 9 ; exp_size = 36 ; max_size = 65,544 ; max_iov = 2 ; max_copy = 9 */ +/* LIB9P_VER_9P2000_p9p: min_size = 9 ; exp_size = 36 ; max_size = 65,544 ; max_iov = 2 ; max_copy = 9 */ +/* LIB9P_VER_9P2000_u : min_size = 13 ; exp_size = 40 ; max_size = 65,548 ; max_iov = 3 ; max_copy = 13 */ +struct lib9p_msg_Rerror { + lib9p_tag_t tag; + struct lib9p_s errstr; +#if CONFIG_9P_ENABLE_9P2000_u + lib9p_errno_t errnum; +#endif /* CONFIG_9P_ENABLE_9P2000_u */ +}; +LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Rerror, lib9p_msg_Rerror); + +#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */ +#if CONFIG_9P_ENABLE_9P2000_L +/* size = 11 ; max_iov = 1 ; max_copy = 11 */ +struct lib9p_msg_Rlerror { + lib9p_tag_t tag; + lib9p_errno_t errnum; +}; +LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Rlerror, lib9p_msg_Rlerror); + +/* size = 67 ; max_iov = 1 ; max_copy = 67 */ +struct lib9p_msg_Rstatfs { + lib9p_tag_t tag; + lib9p_super_magic_t type; + uint32_t bsize; + uint64_t blocks; + uint64_t bfree; + uint64_t bavail; + uint64_t files; + uint64_t ffree; + uint64_t fsid; + uint32_t namelen; +}; +LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Rstatfs, lib9p_msg_Rstatfs); + +/* size = 15 ; max_iov = 1 ; max_copy = 15 */ +struct lib9p_msg_Tlopen { lib9p_tag_t tag; lib9p_fid_t fid; - uint64_t offset; - uint32_t count; + lib9p_lo_t flags; }; +LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Tlopen, lib9p_msg_Tlopen); -struct lib9p_msg_Rread { +/* min_size = 25 ; exp_size = 52 ; max_size = 65,560 ; max_iov = 3 ; max_copy = 25 */ +struct lib9p_msg_Tlcreate { lib9p_tag_t tag; - struct lib9p_d data; + lib9p_fid_t fid; + struct lib9p_s name; + lib9p_lo_t flags; + lib9p_mode_t mode; + lib9p_nuid_t gid; }; +LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Tlcreate, lib9p_msg_Tlcreate); -struct lib9p_msg_Twrite { +/* min_size = 29 ; exp_size = 56 ; max_size = 65,564 ; max_iov = 3 ; max_copy = 29 */ +struct lib9p_msg_Tmknod { lib9p_tag_t tag; - lib9p_fid_t fid; - uint64_t offset; - struct lib9p_d data; + lib9p_fid_t dfid; + struct lib9p_s name; + lib9p_mode_t mode; + uint32_t major; + uint32_t minor; + lib9p_nuid_t gid; }; +LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Tmknod, lib9p_msg_Tmknod); -struct lib9p_msg_Rwrite { - lib9p_tag_t tag; - uint32_t count; +/* min_size = 21 ; exp_size = 48 ; max_size = 65,556 ; max_iov = 3 ; max_copy = 21 */ +struct lib9p_msg_Tmkdir { + lib9p_tag_t tag; + lib9p_fid_t dfid; + struct lib9p_s name; + lib9p_mode_t mode; + lib9p_nuid_t gid; }; +LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Tmkdir, lib9p_msg_Tmkdir); -struct lib9p_msg_Tclunk { +/* size = 15 ; max_iov = 1 ; max_copy = 15 */ +struct lib9p_msg_Tfsync { lib9p_tag_t tag; lib9p_fid_t fid; + lib9p_b4_t datasync; }; +LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Tfsync, lib9p_msg_Tfsync); -struct lib9p_msg_Rclunk { - lib9p_tag_t tag; +/* size = 19 ; max_iov = 1 ; max_copy = 19 */ +struct lib9p_msg_Tgetattr { + lib9p_tag_t tag; + lib9p_fid_t fid; + lib9p_getattr_t request_mask; }; +LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Tgetattr, lib9p_msg_Tgetattr); -struct lib9p_msg_Tremove { - lib9p_tag_t tag; - lib9p_fid_t fid; +/* size = 67 ; max_iov = 1 ; max_copy = 67 */ +struct lib9p_msg_Tsetattr { + lib9p_tag_t tag; + lib9p_fid_t fid; + lib9p_setattr_t valid; + lib9p_mode_t mode; + lib9p_nuid_t uid; + lib9p_nuid_t gid; + uint64_t filesize; + uint64_t atime_sec; + uint64_t atime_nsec; + uint64_t mtime_sec; + uint64_t mtime_nsec; }; +LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Tsetattr, lib9p_msg_Tsetattr); -struct lib9p_msg_Rremove { - lib9p_tag_t tag; +/* min_size = 34 ; exp_size = 61 ; max_size = 65,569 ; max_iov = 2 ; max_copy = 34 */ +struct lib9p_msg_Tgetlock { + lib9p_tag_t tag; + lib9p_fid_t fid; + lib9p_lock_type_t type; + uint64_t start; + uint64_t length; + uint32_t proc_id; + struct lib9p_s client_id; }; +LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Tgetlock, lib9p_msg_Tgetlock); -struct lib9p_msg_Tstat { - lib9p_tag_t tag; - lib9p_fid_t fid; +/* min_size = 30 ; exp_size = 57 ; max_size = 65,565 ; max_iov = 2 ; max_copy = 30 */ +struct lib9p_msg_Rgetlock { + lib9p_tag_t tag; + lib9p_lock_type_t type; + uint64_t start; + uint64_t length; + uint32_t proc_id; + struct lib9p_s client_id; +}; +LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Rgetlock, lib9p_msg_Rgetlock); + +/* min_size = 38 ; exp_size = 65 ; max_size = 65,573 ; max_iov = 2 ; max_copy = 38 */ +struct lib9p_msg_Tlock { + lib9p_tag_t tag; + lib9p_fid_t fid; + lib9p_lock_type_t type; + lib9p_lock_flags_t flags; + uint64_t start; + uint64_t length; + uint32_t proc_id; + struct lib9p_s client_id; +}; +LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Tlock, lib9p_msg_Tlock); + +/* size = 8 ; max_iov = 1 ; max_copy = 8 */ +struct lib9p_msg_Rlock { + lib9p_tag_t tag; + lib9p_lock_status_t status; +}; +LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Rlock, lib9p_msg_Rlock); + +#endif /* CONFIG_9P_ENABLE_9P2000_L */ +#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u +/* LIB9P_VER_9P2000 : min_size = 49 ; exp_size = 157 ; max_size = 262,189 ; max_iov = 8 ; max_copy = 49 */ +/* LIB9P_VER_9P2000_e : min_size = 49 ; exp_size = 157 ; max_size = 262,189 ; max_iov = 8 ; max_copy = 49 */ +/* LIB9P_VER_9P2000_p9p: min_size = 49 ; exp_size = 157 ; max_size = 262,189 ; max_iov = 8 ; max_copy = 49 */ +/* LIB9P_VER_9P2000_u : min_size = 63 ; exp_size = 198 ; max_size = 327,738 ; max_iov = 11 ; max_copy = 63 */ +struct lib9p_stat { + uint16_t kern_type; + uint32_t kern_dev; + struct lib9p_qid file_qid; + lib9p_dm_t file_mode; + uint32_t file_atime; + uint32_t file_mtime; + uint64_t file_size; + struct lib9p_s file_name; + struct lib9p_s file_owner_uid; + struct lib9p_s file_owner_gid; + struct lib9p_s file_last_modified_uid; +#if CONFIG_9P_ENABLE_9P2000_u + struct lib9p_s file_extension; + lib9p_nuid_t file_owner_n_uid; + lib9p_nuid_t file_owner_n_gid; + lib9p_nuid_t file_last_modified_n_uid; +#endif /* CONFIG_9P_ENABLE_9P2000_u */ +}; +LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_stat, lib9p_stat); + +#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */ +#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u +/* size = 20 ; max_iov = 1 ; max_copy = 20 */ +struct lib9p_msg_Rauth { + lib9p_tag_t tag; + struct lib9p_qid aqid; +}; +LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Rauth, lib9p_msg_Rauth); + +/* size = 20 ; max_iov = 1 ; max_copy = 20 */ +struct lib9p_msg_Rattach { + lib9p_tag_t tag; + struct lib9p_qid qid; +}; +LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Rattach, lib9p_msg_Rattach); + +/* min_size = 9 ; exp_size = 217 ; max_size = 217 ; max_iov = 1 ; max_copy = 217 */ +struct lib9p_msg_Rwalk { + lib9p_tag_t tag; + uint16_t nwqid; + struct lib9p_qid *wqid; +}; +LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Rwalk, lib9p_msg_Rwalk); + +#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */ +#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u +/* size = 24 ; max_iov = 1 ; max_copy = 24 */ +struct lib9p_msg_Ropen { + lib9p_tag_t tag; + struct lib9p_qid qid; + uint32_t iounit; +}; +LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Ropen, lib9p_msg_Ropen); + +/* size = 24 ; max_iov = 1 ; max_copy = 24 */ +struct lib9p_msg_Rcreate { + lib9p_tag_t tag; + struct lib9p_qid qid; + uint32_t iounit; +}; +LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Rcreate, lib9p_msg_Rcreate); + +#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */ +#if CONFIG_9P_ENABLE_9P2000_p9p +/* size = 28 ; max_iov = 1 ; max_copy = 28 */ +struct lib9p_msg_Ropenfd { + lib9p_tag_t tag; + struct lib9p_qid qid; + uint32_t iounit; + uint32_t unixfd; +}; +LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Ropenfd, lib9p_msg_Ropenfd); + +#endif /* CONFIG_9P_ENABLE_9P2000_p9p */ +#if CONFIG_9P_ENABLE_9P2000_L +/* size = 24 ; max_iov = 1 ; max_copy = 24 */ +struct lib9p_msg_Rlopen { + lib9p_tag_t tag; + struct lib9p_qid qid; + uint32_t iounit; +}; +LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Rlopen, lib9p_msg_Rlopen); + +/* size = 24 ; max_iov = 1 ; max_copy = 24 */ +struct lib9p_msg_Rlcreate { + lib9p_tag_t tag; + struct lib9p_qid qid; + uint32_t iounit; +}; +LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Rlcreate, lib9p_msg_Rlcreate); + +/* size = 20 ; max_iov = 1 ; max_copy = 20 */ +struct lib9p_msg_Rsymlink { + lib9p_tag_t tag; + struct lib9p_qid qid; +}; +LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Rsymlink, lib9p_msg_Rsymlink); + +/* size = 20 ; max_iov = 1 ; max_copy = 20 */ +struct lib9p_msg_Rmknod { + lib9p_tag_t tag; + struct lib9p_qid qid; +}; +LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Rmknod, lib9p_msg_Rmknod); + +/* size = 160 ; max_iov = 1 ; max_copy = 160 */ +struct lib9p_msg_Rgetattr { + lib9p_tag_t tag; + lib9p_getattr_t valid; + struct lib9p_qid qid; + lib9p_mode_t mode; + lib9p_nuid_t uid; + lib9p_nuid_t gid; + uint64_t nlink; + uint64_t rdev; + uint64_t filesize; + uint64_t blksize; + uint64_t blocks; + uint64_t atime_sec; + uint64_t atime_nsec; + uint64_t mtime_sec; + uint64_t mtime_nsec; + uint64_t ctime_sec; + uint64_t ctime_nsec; + uint64_t btime_sec; + uint64_t btime_nsec; + uint64_t gen; + uint64_t data_version; }; +LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Rgetattr, lib9p_msg_Rgetattr); +/* size = 20 ; max_iov = 1 ; max_copy = 20 */ +struct lib9p_msg_Rmkdir { + lib9p_tag_t tag; + struct lib9p_qid qid; +}; +LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Rmkdir, lib9p_msg_Rmkdir); + +#endif /* CONFIG_9P_ENABLE_9P2000_L */ +#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u +/* LIB9P_VER_9P2000 : min_size = 58 ; exp_size = 166 ; max_size = 262,198 ; max_iov = 8 ; max_copy = 58 */ +/* LIB9P_VER_9P2000_e : min_size = 58 ; exp_size = 166 ; max_size = 262,198 ; max_iov = 8 ; max_copy = 58 */ +/* LIB9P_VER_9P2000_p9p: min_size = 58 ; exp_size = 166 ; max_size = 262,198 ; max_iov = 8 ; max_copy = 58 */ +/* LIB9P_VER_9P2000_u : min_size = 72 ; exp_size = 207 ; max_size = 327,747 ; max_iov = 11 ; max_copy = 72 */ struct lib9p_msg_Rstat { lib9p_tag_t tag; struct lib9p_stat stat; }; +LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Rstat, lib9p_msg_Rstat); +/* LIB9P_VER_9P2000 : min_size = 62 ; exp_size = 170 ; max_size = 262,202 ; max_iov = 8 ; max_copy = 62 */ +/* LIB9P_VER_9P2000_e : min_size = 62 ; exp_size = 170 ; max_size = 262,202 ; max_iov = 8 ; max_copy = 62 */ +/* LIB9P_VER_9P2000_p9p: min_size = 62 ; exp_size = 170 ; max_size = 262,202 ; max_iov = 8 ; max_copy = 62 */ +/* LIB9P_VER_9P2000_u : min_size = 76 ; exp_size = 211 ; max_size = 327,751 ; max_iov = 11 ; max_copy = 76 */ struct lib9p_msg_Twstat { lib9p_tag_t tag; lib9p_fid_t fid; struct lib9p_stat stat; }; +LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Twstat, lib9p_msg_Twstat); +#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */ -struct lib9p_msg_Rwstat { - lib9p_tag_t tag; -}; +/* containers *****************************************************************/ -#endif /* defined(CONFIG_9P_ENABLE_9P2000) || defined(CONFIG_9P_ENABLE_9P2000_e) || defined(CONFIG_9P_ENABLE_9P2000_u) */ -#if defined(CONFIG_9P_ENABLE_9P2000_e) -struct lib9p_msg_Tsession { - lib9p_tag_t tag; - uint64_t key; -}; +#define _LIB9P_MAX(a, b) ((a) > (b)) ? (a) : (b) -struct lib9p_msg_Rsession { - lib9p_tag_t tag; -}; +#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u + #if CONFIG_9P_ENABLE_9P2000_e + #define LIB9P_TMSG_MAX_IOV _LIB9P_MAX(32, 2 + (CONFIG_9P_MAX_9P2000_e_WELEM * 2)) + #else + #define LIB9P_TMSG_MAX_IOV 32 + #endif +#endif -struct lib9p_msg_Tsread { - lib9p_tag_t tag; - uint32_t fid; - uint16_t nwname; - struct lib9p_s *wname; -}; +#if CONFIG_9P_ENABLE_9P2000_u + #if CONFIG_9P_ENABLE_9P2000_e + #define LIB9P_TMSG_MAX_COPY _LIB9P_MAX(76, 17 + (CONFIG_9P_MAX_9P2000_e_WELEM * 2)) + #else + #define LIB9P_TMSG_MAX_COPY 76 + #endif +#elif CONFIG_9P_ENABLE_9P2000_L + #if CONFIG_9P_ENABLE_9P2000_e + #define LIB9P_TMSG_MAX_COPY _LIB9P_MAX(67, 17 + (CONFIG_9P_MAX_9P2000_e_WELEM * 2)) + #else + #define LIB9P_TMSG_MAX_COPY 67 + #endif +#elif CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p + #if CONFIG_9P_ENABLE_9P2000_e + #define LIB9P_TMSG_MAX_COPY _LIB9P_MAX(62, 17 + (CONFIG_9P_MAX_9P2000_e_WELEM * 2)) + #else + #define LIB9P_TMSG_MAX_COPY 62 + #endif +#endif -struct lib9p_msg_Rsread { - lib9p_tag_t tag; - struct lib9p_d data; -}; +#if CONFIG_9P_ENABLE_9P2000_u + #define LIB9P_RMSG_MAX_IOV 11 +#elif CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p + #define LIB9P_RMSG_MAX_IOV 8 +#elif CONFIG_9P_ENABLE_9P2000_L + #define LIB9P_RMSG_MAX_IOV 2 +#endif -struct lib9p_msg_Tswrite { - lib9p_tag_t tag; - uint32_t fid; - uint16_t nwname; - struct lib9p_s *wname; - struct lib9p_d data; +#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u + #define LIB9P_RMSG_MAX_COPY 217 +#endif + +struct lib9p_Tmsg_send_buf { + size_t iov_cnt; + struct iovec iov[LIB9P_TMSG_MAX_IOV]; + uint8_t copied[LIB9P_TMSG_MAX_COPY]; }; -struct lib9p_msg_Rswrite { - lib9p_tag_t tag; - uint32_t count; +struct lib9p_Rmsg_send_buf { + size_t iov_cnt; + struct iovec iov[LIB9P_RMSG_MAX_IOV]; + uint8_t copied[LIB9P_RMSG_MAX_COPY]; }; -#endif /* defined(CONFIG_9P_ENABLE_9P2000_e) */ diff --git a/lib9p/include/lib9p/9p.h b/lib9p/include/lib9p/9p.h index 171ad3b..42381cf 100644 --- a/lib9p/include/lib9p/9p.h +++ b/lib9p/include/lib9p/9p.h @@ -1,35 +1,42 @@ /* lib9p/9p.h - Base 9P protocol definitions 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 */ #ifndef _LIB9P_9P_H_ #define _LIB9P_9P_H_ -#include <assert.h> #include <stdbool.h> #include <sys/types.h> /* for ssize_t */ -#include <lib9p/linux-errno.h> - -/* configuration **************************************************************/ +#include <libmisc/assert.h> -#include "config.h" +#include <lib9p/linux-errno.h> +#include <lib9p/9p.generated.h> #ifndef CONFIG_9P_MAX_ERR_SIZE #error config.h must define CONFIG_9P_MAX_ERR_SIZE #endif -#ifndef CONFIG_9P_ENABLE_9P2000 - #define CONFIG_9P_ENABLE_9P2000 -#endif +static_assert(CONFIG_9P_MAX_ERR_SIZE <= UINT16_MAX); + +/* constants ******************************************************************/ + +enum { + LIB9P_DEFAULT_PORT_9FS = 564, + LIB9P_DEFAULT_PORT_STYX = 6666, +}; -/* protocol definitions *******************************************************/ +/* strings ********************************************************************/ -#include <lib9p/9p.generated.h> /* *after* config.h */ +const char *lib9p_version_str(enum lib9p_version); +const char *lib9p_msgtype_str(enum lib9p_version, enum lib9p_msg_type); -#define LIB9P_NOTAG ((uint16_t)~0U) -#define LIB9P_NOFID ((uint32_t)~0U) +struct lib9p_s lib9p_str(char *s); +struct lib9p_s lib9p_strn(char *s, size_t maxlen); +struct lib9p_s lib9p_str_slice(struct lib9p_s s, uint16_t beg, uint16_t end); +#define lib9p_str_sliceleft(s, beg) lib9p_str_slice(s, beg, (s).len) +bool lib9p_str_eq(struct lib9p_s a, struct lib9p_s b); /* ctx ************************************************************************/ @@ -40,9 +47,9 @@ struct lib9p_ctx { /* state */ #ifdef CONFIG_9P_ENABLE_9P2000_u - uint32_t err_num; + lib9p_errno_t err_num; #endif - char err_msg[CONFIG_9P_MAX_ERR_SIZE]; + [[gnu::nonstring]] char err_msg[CONFIG_9P_MAX_ERR_SIZE]; }; void lib9p_ctx_clear_error(struct lib9p_ctx *ctx); @@ -50,21 +57,25 @@ void lib9p_ctx_clear_error(struct lib9p_ctx *ctx); bool lib9p_ctx_has_error(struct lib9p_ctx *ctx); /** Write an static error into ctx, return -1. */ -int lib9p_error(struct lib9p_ctx *ctx, uint32_t linux_errno, char const *msg); +int lib9p_error(struct lib9p_ctx *ctx, lib9p_errno_t linux_errno, char const *msg); /** Write a printf-style error into ctx, return -1. */ -int lib9p_errorf(struct lib9p_ctx *ctx, uint32_t linux_errno, char const *fmt, ...); +int lib9p_errorf(struct lib9p_ctx *ctx, lib9p_errno_t linux_errno, char const *fmt, ...) [[gnu::format(printf, 3, 4)]]; -const char *lib9p_msg_type_str(struct lib9p_ctx *, enum lib9p_msg_type); +/* misc utilities *************************************************************/ -/* main message functions *****************************************************/ +uint32_t lib9p_version_min_msg_size(enum lib9p_version); + +lo_interface fmt_formatter lo_box_lib9p_msg_as_fmt_formatter(struct lib9p_ctx *ctx, enum lib9p_msg_type typ, void *body); + +/* main T-message functions ***************************************************/ /** - * Validate a message's structure; it's size, string encodings, enums, + * Validate a message's structure; its size, string encodings, enums, * and bitfields. * * Return how much space the message will take when unmarshaled. This * number may be larger than net_bytes due to (1) struct padding, (2) - * nul-terminator bytes for strings. + * array pointers. * * Emits an error (return -1, set ctx->err_num and ctx->err_msg) if * either the message type is unknown, or if net_bytes is too short @@ -75,39 +86,37 @@ const char *lib9p_msg_type_str(struct lib9p_ctx *, enum lib9p_msg_type); * * @return required size, or -1 on error * + * @errno LINUX_EOPNOTSUPP: message is an R-message * @errno LINUX_EOPNOTSUPP: message has unknown type * @errno LINUX_EBADMSG: message is wrong size for content * @errno LINUX_EBADMSG: message contains invalid UTF-8 * @errno LINUX_EBADMSG: message contains a bitfield with unknown bits * @errno LINUX_EMSGSIZE: would-be return value overflows SSIZE_MAX */ -ssize_t lib9p_validate(struct lib9p_ctx *ctx, uint8_t *net_bytes); +ssize_t lib9p_Tmsg_validate(struct lib9p_ctx *ctx, uint8_t *net_bytes); /** * Unmarshal the 9P message `net_bytes` into the C struct `ret_body`. * - * Emits an error (return 0, set ctx->err_num and ctx->err_msg) if a - * string contains invalid UTF-8 or a nul-byte. - * - * lib9p_unmarshal does no validation; you must run lib9p_validate() - * first. + * lib9p_Tmsg_unmarshal does no validation; you must run + * lib9p_Tmsg_validate() first. * - * @param ctx : negotiated protocol parameters, where to record errors + * @param ctx : negotiated protocol parameters * @param net_bytes : the complete message, starting with the "size[4]" * * @return ret_typ : the mesage type - * @return ret_body : the message body, must be at least lib9p_validate() bytes + * @return ret_body : the message body, must be at least lib9p_Tmsg_validate() bytes */ -void lib9p_unmarshal(struct lib9p_ctx *ctx, uint8_t *net_bytes, - enum lib9p_msg_type *ret_typ, void *ret_body); +void lib9p_Tmsg_unmarshal(struct lib9p_ctx *ctx, uint8_t *net_bytes, + enum lib9p_msg_type *ret_typ, void *ret_body); /** * Marshal a `struct lib9p_msg_{typ}` structure into a byte-array. * - * lib9p_marshal does no validation; it trusts that the programmer - * won't give it garbage input. However, just as it doesn't marshal - * struct fields that aren't in ctx->version, it won't marshal - * bitfield bits that aren't in ctx->version; it applies a + * lib9p_Tmsg_marshal does no validation; it trusts that the + * programmer won't give it garbage input. However, just as it + * doesn't marshal struct fields that aren't in ctx->version, it won't + * marshal bitfield bits that aren't in ctx->version; it applies a * version-specific mask to bitfields. * * @param ctx : negotiated protocol parameters, where to record errors @@ -119,47 +128,79 @@ void lib9p_unmarshal(struct lib9p_ctx *ctx, uint8_t *net_bytes, * * @errno LINUX_ERANGE: reply does not fit in ctx->max_msg_size */ -bool lib9p_marshal(struct lib9p_ctx *ctx, enum lib9p_msg_type typ, void *body, - uint8_t *ret_bytes); +bool lib9p_Tmsg_marshal(struct lib9p_ctx *ctx, enum lib9p_msg_type typ, void *body, + struct lib9p_Tmsg_send_buf *ret); + +/* main R-message functions ***************************************************/ + +/** Same as above, but for R-messages instead of T-messages. */ + +ssize_t lib9p_Rmsg_validate(struct lib9p_ctx *ctx, uint8_t *net_bytes); +void lib9p_Rmsg_unmarshal(struct lib9p_ctx *ctx, uint8_t *net_bytes, + enum lib9p_msg_type *ret_typ, void *ret_body); +bool lib9p_Rmsg_marshal(struct lib9p_ctx *ctx, enum lib9p_msg_type typ, void *body, + struct lib9p_Rmsg_send_buf *ret); + /* `struct lib9p_stat` helpers ************************************************/ /** Assert that a `struct lib9p_stat` object looks valid. */ -static inline void lib9p_assert_stat(struct lib9p_stat stat) { - assert( ((bool)(stat.file_mode & LIB9P_DM_DIR )) == ((bool)(stat.file_qid.type & LIB9P_QT_DIR )) ); - assert( ((bool)(stat.file_mode & LIB9P_DM_APPEND )) == ((bool)(stat.file_qid.type & LIB9P_QT_APPEND )) ); - assert( ((bool)(stat.file_mode & LIB9P_DM_EXCL )) == ((bool)(stat.file_qid.type & LIB9P_QT_EXCL )) ); - assert( ((bool)(stat.file_mode & _LIB9P_DM_PLAN9_MOUNT)) == ((bool)(stat.file_qid.type & _LIB9P_QT_PLAN9_MOUNT)) ); - assert( ((bool)(stat.file_mode & LIB9P_DM_AUTH )) == ((bool)(stat.file_qid.type & LIB9P_QT_AUTH )) ); - assert( ((bool)(stat.file_mode & LIB9P_DM_TMP )) == ((bool)(stat.file_qid.type & LIB9P_QT_TMP )) ); +static inline void lib9p_stat_assert(struct lib9p_stat stat) { + assert( ((bool)(stat.file_mode & LIB9P_DM_DIR )) == ((bool)(stat.file_qid.type & LIB9P_QT_DIR )) ); + assert( ((bool)(stat.file_mode & LIB9P_DM_APPEND)) == ((bool)(stat.file_qid.type & LIB9P_QT_APPEND)) ); + assert( ((bool)(stat.file_mode & LIB9P_DM_EXCL )) == ((bool)(stat.file_qid.type & LIB9P_QT_EXCL )) ); + assert( ((bool)(stat.file_mode & LIB9P_DM_AUTH )) == ((bool)(stat.file_qid.type & LIB9P_QT_AUTH )) ); + assert( ((bool)(stat.file_mode & LIB9P_DM_TMP )) == ((bool)(stat.file_qid.type & LIB9P_QT_TMP )) ); assert( (stat.file_size == 0) || !(stat.file_mode & LIB9P_DM_DIR) ); } /** - * TODO + * Validate a message's `stat` structure. + * + * @param ctx : negotiated protocol parameters, where to record errors + * @param net_bytes : network-encoded stat structure + * @param net_size : the number of net_bytes that may be read * - * @return ret_net_size: number of bytes consumed; <=net_size - * @return ret_host_size: number of bytes that lib9p_unmarshal_stat would take + * @return ret_net_size : number of bytes consumed; <=net_size + * @return ret_host_size : number of bytes that lib9p_stat_unmarshal would take * @return whether there was an error */ -bool lib9p_validate_stat(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes, - uint32_t *ret_net_size, ssize_t *ret_host_size); +bool lib9p_stat_validate(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes, + uint32_t *ret_net_size, size_t *ret_host_size); /** - * TODO + * Unmarshal the 9P `net_bytes` into the C struct `ret_obj`. + * + * lib9p_stat_unmarshal does no validation; you must run + * lib9p_stat_validate() first. + * + * @param ctx : negotiated protocol parameters + * @param net_bytes : network-encoded stat structure * - * @return ret_obj : where to put the stat object itself - * @return ret_extra : where to put strings for the stat object - * @return consumed net_bytes + * @return ret : the stat object, must be at least lib9p_stat_validate()->ret_net_size bytes */ -uint32_t lib9p_unmarshal_stat(struct lib9p_ctx *ctx, uint8_t *net_bytes, - struct lib9p_stat *ret_obj, void *ret_extra); +void lib9p_stat_unmarshal(struct lib9p_ctx *ctx, uint8_t *net_bytes, + struct lib9p_stat *ret); /** + * Marhsal a `struct lib9p_stat` structure into a byte-array. + * + * lib9p_Tmsg_marshal does no validation; it trusts that the + * programmer won't give it garbage input. However, just as it + * doesn't marshal struct fields that aren't in ctx->version, it won't + * marshal bitfield bits that aren't in ctx->version; it applies a + * version-specific mask to bitfields. + * + * @param ctx : negotiated protocol parameters, where to record errors + * @param max_net_size : the maximum network-encoded size to allow + * @param obj : the message to encode + * * @return ret_bytes: the buffer to encode into * @return the number of bytes written, or 0 if the stat object does not fit in max_net_size + * + * @errno LINUX_ERANGE: reply does not fit in max_net_size */ -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); #endif /* _LIB9P_9P_H_ */ diff --git a/lib9p/include/lib9p/linux-errno.h b/lib9p/include/lib9p/linux-errno.h index e7c74f5..e864fb6 100644 --- a/lib9p/include/lib9p/linux-errno.h +++ b/lib9p/include/lib9p/linux-errno.h @@ -1,5 +1,5 @@ /* lib9p/linux-errno.h - Generated by `lib9p/include/lib9p/linux-errno.h.gen 3rd-party/linux-errno.txt`. DO NOT EDIT! */ -/* 3rd-party/linux-errno.txt - Generated from build-aux/linux-errno.txt.gen and linux.git v6.7. DO NOT EDIT! */ +/* 3rd-party/linux-errno.txt - Generated from lib9p/linux-errno.txt.gen and linux.git v6.14. DO NOT EDIT! */ #ifndef _LIB9P_LINUX_ERRNO_H_ #define _LIB9P_LINUX_ERRNO_H_ diff --git a/lib9p/include/lib9p/linux-errno.h.gen b/lib9p/include/lib9p/linux-errno.h.gen index 8f4e0c8..2c736a2 100755 --- a/lib9p/include/lib9p/linux-errno.h.gen +++ b/lib9p/include/lib9p/linux-errno.h.gen @@ -1,7 +1,7 @@ #!/usr/bin/env python # lib9p/linux-errno.h.gen - Generate a C header from a list of errno numbers # -# 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 import sys @@ -13,7 +13,7 @@ def print_errnos() -> None: ) errnos: dict[str, tuple[int, str]] = {} for txtlist in sys.argv[1:]: - with open(txtlist, "r") as fh: + with open(txtlist, "r", encoding="utf-8") as fh: for line in fh: if line.startswith("#"): print(f"/* {line[1:].strip()} */") @@ -26,12 +26,10 @@ def print_errnos() -> None: print("#ifndef _LIB9P_LINUX_ERRNO_H_") print("#define _LIB9P_LINUX_ERRNO_H_") print() - namelen = max(len(name) for name in errnos.keys()) + namelen = max(len(name) for name in errnos) numlen = max(len(str(num)) for (num, desc) in errnos.values()) - for name in errnos: - print( - f"#define LINUX_{name.ljust(namelen)} {str(errnos[name][0]).rjust(numlen)} /* {errnos[name][1]} */" - ) + for name, [num, msg] in errnos.items(): + print(f"#define LINUX_{name:<{namelen}} {num:>{numlen}} /* {msg} */") print() print("#endif /* _LIB9P_LINUX_ERRNO_H_ */") diff --git a/lib9p/include/lib9p/srv.h b/lib9p/include/lib9p/srv.h index ab7ec43..85fc6bd 100644 --- a/lib9p/include/lib9p/srv.h +++ b/lib9p/include/lib9p/srv.h @@ -1,6 +1,6 @@ /* lib9p/srv.h - 9P server * - * 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 */ @@ -11,110 +11,170 @@ #include <libcr_ipc/rpc.h> #include <libcr_ipc/chan.h> #include <libhw/generic/net.h> +#include <libmisc/assert.h> #include <libmisc/private.h> +#include <libobj/obj.h> #include <lib9p/9p.h> /* context ********************************************************************/ -CR_CHAN_DECLARE(_lib9p_srv_flushch, bool) +CR_CHAN_DECLARE(_lib9p_srv_flushch, bool); struct lib9p_srv_ctx { struct lib9p_ctx basectx; uint32_t uid; - char *uname; + struct lib9p_s uname; - BEGIN_PRIVATE(LIB9P_SRV_H) + BEGIN_PRIVATE(LIB9P_SRV_H); _lib9p_srv_flushch_t _flushch; - END_PRIVATE(LIB9P_SRV_H) + END_PRIVATE(LIB9P_SRV_H); }; bool lib9p_srv_flush_requested(struct lib9p_srv_ctx *ctx); -int lib9p_srv_acknowledge_flush(struct lib9p_srv_ctx *ctx); +void lib9p_srv_acknowledge_flush(struct lib9p_srv_ctx *ctx); /* interface definitions ******************************************************/ -struct lib9p_srv_file_vtable; - -struct __lib9p_srv_file; -typedef struct __lib9p_srv_file { - struct lib9p_srv_file_vtable *vtable; - - BEGIN_PRIVATE(LIB9P_SRV_H) - /* Managed by srv.c, but should be cloned by ->vtable->clone(). */ - struct __lib9p_srv_file *_parent_dir; /* clone this - - /* Managed by srv.c, but should be initialized to 0 by ->vtable->clone(). */ - /* ref type 1: an entry in fidmap - * ref type 2: ->_parent_dir of another file */ - unsigned int _refcount; - END_PRIVATE(LIB9P_SRV_H) -} implements_lib9p_srv_file; - -struct lib9p_srv_file_vtable { - /* all - resource management */ - implements_lib9p_srv_file *(*clone )(implements_lib9p_srv_file *, struct lib9p_srv_ctx *); - void (*free )(implements_lib9p_srv_file *, struct lib9p_srv_ctx *); - - /* all - syscalls */ - uint32_t (*io )(implements_lib9p_srv_file *, struct lib9p_srv_ctx *, - lib9p_o_t flags); - struct lib9p_stat (*stat )(implements_lib9p_srv_file *, struct lib9p_srv_ctx *); - void (*wstat )(implements_lib9p_srv_file *, struct lib9p_srv_ctx *, - struct lib9p_stat new); - void (*remove )(implements_lib9p_srv_file *, struct lib9p_srv_ctx *); - - /* directories - base */ - implements_lib9p_srv_file *(*dopen )(implements_lib9p_srv_file *, struct lib9p_srv_ctx *, - char *childname); - implements_lib9p_srv_file *(*dcreate)(implements_lib9p_srv_file *, struct lib9p_srv_ctx *, - char *childname, - lib9p_dm_t perm, lib9p_o_t flags); - - /* directories - once opened */ - size_t /* <- obj cnt */ (*dread )(implements_lib9p_srv_file *, struct lib9p_srv_ctx *, - uint8_t *buf, - uint32_t byte_count, /* <- num bytes */ - size_t obj_offset); /* <- starting at this object */ - - /* non-directories - once opened */ - uint32_t (*pread )(implements_lib9p_srv_file *, struct lib9p_srv_ctx *, - void *buf, - uint32_t byte_count, - uint64_t byte_offset); - uint32_t (*pwrite )(implements_lib9p_srv_file *, struct lib9p_srv_ctx *, - void *buf, - uint32_t byte_count, - uint64_t byte_offset); -}; +lo_interface lib9p_srv_fio; +lo_interface lib9p_srv_dio; + +/* FIXME: I don't like that the pointers returned by stat() and + * pread() have to remain live after they return. Perhaps a + * `respond()`-callback? But that just reads as gross in C. + * + * FIXME: It would be nice if pread() could return more than 1 iovec. + */ +#define lib9p_srv_file_LO_IFACE \ + /* resource management **********************************************/ \ + \ + /** \ + * free() is be called when all FIDs associated with the file are \ + * clunked. \ + * \ + * free() MUST NOT error. \ + */ \ + LO_FUNC(void , free ) \ + \ + /** \ + * qid() is called frequently and returns the current QID of the file. \ + * The .path field MUST never change, the .type field may change in \ + * response to wstat() calls (but the QT_DIR bit MUST NOT change), and \ + * the .vers field may change frequently in response to any number of \ + * things (wstat(), write(), or non-9P events). \ + * \ + * qid() MUST NOT error. \ + */ \ + LO_FUNC(struct lib9p_qid , qid ) \ + \ + /* non-"opened" generic I/O *****************************************/ \ + \ + LO_FUNC(struct lib9p_stat , stat , struct lib9p_srv_ctx *) \ + LO_FUNC(void , wstat , struct lib9p_srv_ctx *, \ + struct lib9p_stat new) \ + LO_FUNC(void , remove , struct lib9p_srv_ctx *) \ + \ + /* non-"opened" directory I/O ***************************************/ \ + \ + LO_FUNC(lo_interface lib9p_srv_file, dwalk , struct lib9p_srv_ctx *, \ + struct lib9p_s childname) \ + LO_FUNC(lo_interface lib9p_srv_file, dcreate, struct lib9p_srv_ctx *, \ + struct lib9p_s childname, \ + lib9p_dm_t perm, \ + lib9p_o_t flags) \ + \ + /* open() for I/O ***************************************************/ \ + \ + LO_FUNC(lo_interface lib9p_srv_fio , fopen , struct lib9p_srv_ctx *, \ + bool rd, bool wr, \ + bool trunc) \ + LO_FUNC(lo_interface lib9p_srv_dio , dopen , struct lib9p_srv_ctx *) +LO_INTERFACE(lib9p_srv_file); + +#define lib9p_srv_fio_LO_IFACE \ + LO_FUNC(struct lib9p_qid , qid ) \ + LO_FUNC(void , iofree ) \ + LO_FUNC(uint32_t , iounit ) \ + LO_FUNC(void , pread , struct lib9p_srv_ctx *, \ + uint32_t byte_count, \ + uint64_t byte_offset, \ + struct iovec *ret) \ + LO_FUNC(uint32_t , pwrite , struct lib9p_srv_ctx *, \ + void *buf, \ + uint32_t byte_count, \ + uint64_t byte_offset) +LO_INTERFACE(lib9p_srv_fio); + +/* FIXME: The dio interface just feels clunky. I'm not in a rush to + * change it because util9p_static_dir is already implemented and I + * don't anticipate the sbc-harness needing another dio + * implementation. But if I wanted lib9p to be used outside of + * sbc-harness, this is one of the first things that I'd want to + * change. + */ +#define lib9p_srv_dio_LO_IFACE \ + LO_FUNC(struct lib9p_qid , qid ) \ + LO_FUNC(void , iofree ) \ + LO_FUNC(size_t /* <- obj cnt */ , dread , struct lib9p_srv_ctx *, \ + uint8_t *buf, \ + /* num bytes -> */ uint32_t byte_count, \ + /* starting at this object -> */ size_t obj_offset) +LO_INTERFACE(lib9p_srv_dio); + +#define LIB9P_SRV_NOTDIR(TYP, NAM) \ + static lo_interface lib9p_srv_file NAM##_dwalk (TYP *, struct lib9p_srv_ctx *, struct lib9p_s) { assert_notreached("not a directory"); } \ + static lo_interface lib9p_srv_file NAM##_dcreate(TYP *, struct lib9p_srv_ctx *, struct lib9p_s, lib9p_dm_t, lib9p_o_t) { assert_notreached("not a directory"); } \ + static lo_interface lib9p_srv_dio NAM##_dopen (TYP *, struct lib9p_srv_ctx *) { assert_notreached("not a directory"); } + +#define LIB9P_SRV_NOTFILE(TYP, NAM) \ + static lo_interface lib9p_srv_fio NAM##_fopen (TYP *, struct lib9p_srv_ctx *, bool, bool, bool) { assert_notreached("not a file"); } /* main server entrypoints ****************************************************/ -CR_RPC_DECLARE(_lib9p_srv_reqch, struct _lib9p_srv_req *, bool) +CR_RPC_DECLARE(_lib9p_srv_reqch, struct _lib9p_srv_req *, bool); struct lib9p_srv { /* Things you provide */ - void /*TODO*/ (*auth )(struct lib9p_srv_ctx *, char *treename); /* optional */ - implements_lib9p_srv_file *(*rootdir)(struct lib9p_srv_ctx *, char *treename); + void /*TODO*/ (*auth )(struct lib9p_srv_ctx *, struct lib9p_s treename); /* optional */ + lo_interface lib9p_srv_file (*rootdir)(struct lib9p_srv_ctx *, struct lib9p_s treename); + void (*msglog )(struct lib9p_srv_ctx *, enum lib9p_msg_type, void *hostmsg); /* optional */ /* For internal use */ - BEGIN_PRIVATE(LIB9P_SRV_H) + BEGIN_PRIVATE(LIB9P_SRV_H); + unsigned int readers; + unsigned int writers; _lib9p_srv_reqch_t _reqch; - END_PRIVATE(LIB9P_SRV_H) + END_PRIVATE(LIB9P_SRV_H); }; /** + * In an infinite loop, accept a connection and read messages from it until + * close; dispatching requests to a pool of lib9p_srv_write_cr() coroutines + * with the same `srv`. + * * Will just close the connection if a T-message has a size[4] <7. * + * @param srv: The server configuration and state; has an associated pool of + * lib9p_srv_write_cr() coroutines. + * @param listener: The listener object to accept connections from. + * * @errno LINUX_EMSGSIZE T-message has size[4] bigger than max_msg_size * @errno LINUX_EDOM Tversion specified an impossibly small max_msg_size * @errno LINUX_EOPNOTSUPP T-message has an R-message type, or an unrecognized T-message type * @errno LINUX_EBADMSG T-message has wrong size[4] for its content, or has invalid UTF-8 * @errno LINUX_ERANGE R-message does not fit into max_msg_size */ +[[noreturn]] void lib9p_srv_read_cr(struct lib9p_srv *srv, lo_interface net_stream_listener listener); -__attribute__ ((noreturn)) void lib9p_srv_read_cr(struct lib9p_srv *srv, implements_net_stream_listener *listener); +/** + * Service requests to the `struct lib9p_srv *srv` argument that have been + * read by lib9p_srv_read_cr(). + * + * @param struct lib9p_srv *srv: The server configuration and state; has an + * associated pool of lib9p_srv_read_cr() + * coroutines. + */ COROUTINE lib9p_srv_write_cr(void *_srv); #endif /* _LIB9P_SRV_H_ */ diff --git a/lib9p/internal.h b/lib9p/internal.h deleted file mode 100644 index 004660a..0000000 --- a/lib9p/internal.h +++ /dev/null @@ -1,179 +0,0 @@ -/* lib9p/internal.h - TODO - * - * Copyright (C) 2024 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 */ - -#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_FIDS - #error config.h must define CONFIG_9P_MAX_FIDS -#endif -#ifndef CONFIG_9P_MAX_REQS - #error config.h must define CONFIG_9P_MAX_REQS -#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); - -/* C language *****************************************************************/ - -#define UNUSED(name) /* name __attribute__((unused)) */ -#define ALWAYS_INLINE inline __attribute__((always_inline)) -#define FLATTEN __attribute__((flatten)) -#define ARRAY_LEN(arr) (sizeof(arr)/sizeof((arr)[0])) -#define CAT2(a, b) a##b -#define CAT3(a, b, c) a##b##c - -/* specialized contexts *******************************************************/ - -struct _validate_ctx { - struct lib9p_ctx *ctx; - uint32_t net_size; - uint8_t *net_bytes; - - uint32_t net_offset; - /* Increment `host_extra` to pre-allocate space that is - * "extra" beyond sizeof(). */ - size_t host_extra; -}; -typedef bool (*_validate_fn_t)(struct _validate_ctx *ctx); - -struct _unmarshal_ctx { - struct lib9p_ctx *ctx; - uint8_t *net_bytes; - - uint32_t net_offset; - /* `extra` points to the beginning of unallocated space. */ - void *extra; -}; -typedef void (*_unmarshal_fn_t)(struct _unmarshal_ctx *ctx, void *out); - -struct _marshal_ctx { - struct lib9p_ctx *ctx; - - uint8_t *net_bytes; - uint32_t net_offset; -}; -typedef bool (*_marshal_fn_t)(struct _marshal_ctx *ctx, void *host_val); - -/* tables *********************************************************************/ - -struct _table_msg { - char *name; - size_t basesize; - _validate_fn_t validate; - _unmarshal_fn_t unmarshal; - _marshal_fn_t marshal; -}; - -struct _table_version { - struct _table_msg msgs[0x100]; -}; - -extern struct _table_version _lib9p_versions[LIB9P_VER_NUM]; - -bool _lib9p_validate_stat(struct _validate_ctx *ctx); -void _lib9p_unmarshal_stat(struct _unmarshal_ctx *ctx, struct lib9p_stat *out); -bool _lib9p_marshal_stat(struct _marshal_ctx *ctx, struct lib9p_stat *val); - -/* unmarshal utilities ********************************************************/ - -static ALWAYS_INLINE uint8_t decode_u8le(uint8_t *in) { - return in[0]; -} -static ALWAYS_INLINE uint16_t decode_u16le(uint8_t *in) { - return (((uint16_t)(in[0])) << 0) - | (((uint16_t)(in[1])) << 8) - ; -} -static ALWAYS_INLINE uint32_t decode_u32le(uint8_t *in) { - return (((uint32_t)(in[0])) << 0) - | (((uint32_t)(in[1])) << 8) - | (((uint32_t)(in[2])) << 16) - | (((uint32_t)(in[3])) << 24) - ; -} -static ALWAYS_INLINE uint64_t decode_u64le(uint8_t *in) { - return (((uint64_t)(in[0])) << 0) - | (((uint64_t)(in[1])) << 8) - | (((uint64_t)(in[2])) << 16) - | (((uint64_t)(in[3])) << 24) - | (((uint64_t)(in[4])) << 32) - | (((uint64_t)(in[5])) << 40) - | (((uint64_t)(in[6])) << 48) - | (((uint64_t)(in[7])) << 56) - ; -} - -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) - -/* marshal utilities **********************************************************/ - -static ALWAYS_INLINE void encode_u8le(uint8_t in, uint8_t *out) { - out[0] = in; -} -static ALWAYS_INLINE void encode_u16le(uint16_t in, uint8_t *out) { - out[0] = (uint8_t)((in >> 0) & 0xFF); - out[1] = (uint8_t)((in >> 8) & 0xFF); -} -static ALWAYS_INLINE void encode_u32le(uint32_t in, uint8_t *out) { - out[0] = (uint8_t)((in >> 0) & 0xFF); - out[1] = (uint8_t)((in >> 8) & 0xFF); - out[2] = (uint8_t)((in >> 16) & 0xFF); - out[3] = (uint8_t)((in >> 24) & 0xFF); -} -static ALWAYS_INLINE void encode_u64le(uint64_t in, uint8_t *out) { - out[0] = (uint8_t)((in >> 0) & 0xFF); - out[1] = (uint8_t)((in >> 8) & 0xFF); - out[2] = (uint8_t)((in >> 16) & 0xFF); - out[3] = (uint8_t)((in >> 24) & 0xFF); - out[4] = (uint8_t)((in >> 32) & 0xFF); - out[5] = (uint8_t)((in >> 40) & 0xFF); - out[6] = (uint8_t)((in >> 48) & 0xFF); - out[7] = (uint8_t)((in >> 56) & 0xFF); -} - -#endif /* _LIB9P_INTERNAL_H_ */ diff --git a/lib9p/linux-errno.txt.gen b/lib9p/linux-errno.txt.gen new file mode 100755 index 0000000..687e58b --- /dev/null +++ b/lib9p/linux-errno.txt.gen @@ -0,0 +1,17 @@ +#!/usr/bin/env bash +# lib9p/linux-errno.txt.gen - Generate a listing of Linux kernel errnos +# +# Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com> +# SPDX-License-Identifier: AGPL-3.0-or-later + +set -e +linux_git=${1:?} +outfile=${2:?} + +( + cd "${linux_git}" + echo "# ${outfile} - Generated from $0 and linux.git $(git describe). DO NOT EDIT!" + git ls-files include/uapi/ | grep errno | + xargs sed -nE 's,#\s*define\s+(E[A-Z0-9]+)\s+([0-9]+)\s+/\* (.*) \*/,\2 \1 \3,p' | + sort --numeric-sort +) >"${outfile}" diff --git a/lib9p/map.h b/lib9p/map.h index c816bde..c5eab0f 100644 --- a/lib9p/map.h +++ b/lib9p/map.h @@ -1,11 +1,9 @@ -/* lib9p/map.h - A really dumb map/dict data structur +/* lib9p/map.h - A really dumb map/dict data structure * - * 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 */ -#include "internal.h" - /** * `#define` `NAME`, `KEY_T`, `VAL_T`, and `CAP`; then `#include * "map.h". @@ -25,10 +23,10 @@ #endif #ifndef MAP_KEY -#define MAP_KV(TNAME) CAT3(_,TNAME,_kv) -#define MAP_METHOD(TNAME, MNAME) CAT3(TNAME,_,MNAME) +#define MAP_KV(TNAME) LM_CAT3(_,TNAME,_kv) +#define MAP_METHOD(TNAME, MNAME) LM_CAT3(TNAME,_,MNAME) #define MAP_FOREACH(m, k, v) \ - for (size_t i = 0; i < ARRAY_LEN((m)->items); i++) \ + for (size_t i = 0; i < LM_ARRAY_LEN((m)->items); i++) \ if ( ({ k = (m)->items[i].key; v = &(m)->items[i].val; (m)->items[i].set; }) ) #endif @@ -58,7 +56,7 @@ struct NAME { static VAL_T *MAP_METHOD(NAME,load)(struct NAME *m, KEY_T k) { if (!m->len) return NULL; - for (size_t i = 0; i < ARRAY_LEN(m->items); i++) + for (size_t i = 0; i < LM_ARRAY_LEN(m->items); i++) if (m->items[i].set && m->items[i].key == k) return &(m->items[i].val); return NULL; @@ -74,9 +72,9 @@ static VAL_T *MAP_METHOD(NAME,store)(struct NAME *m, KEY_T k, VAL_T v) { *old = v; return old; } - if (m->len == ARRAY_LEN(m->items)) + if (m->len == LM_ARRAY_LEN(m->items)) return NULL; - for (size_t i = 0; i < ARRAY_LEN(m->items); i++) + for (size_t i = 0; i < LM_ARRAY_LEN(m->items); i++) if (!m->items[i].set) { m->len++; m->items[i].set = true; @@ -84,7 +82,7 @@ static VAL_T *MAP_METHOD(NAME,store)(struct NAME *m, KEY_T k, VAL_T v) { m->items[i].val = v; return &(m->items[i].val); } - __builtin_unreachable(); + assert_notreached("should have returned from inside for() loop"); } /** @@ -94,7 +92,7 @@ static VAL_T *MAP_METHOD(NAME,store)(struct NAME *m, KEY_T k, VAL_T v) { static bool MAP_METHOD(NAME,del)(struct NAME *m, KEY_T k) { if (!m->len) return NULL; - for (size_t i = 0; i < ARRAY_LEN(m->items); i++) + for (size_t i = 0; i < LM_ARRAY_LEN(m->items); i++) if (m->items[i].set && m->items[i].key == k) { m->items[i].set = false; m->len--; diff --git a/lib9p/proto.gen b/lib9p/proto.gen new file mode 100755 index 0000000..60f1347 --- /dev/null +++ b/lib9p/proto.gen @@ -0,0 +1,15 @@ +#!/usr/bin/env python +# lib9p/proto.gen - Generate C marshalers/unmarshalers for .9p files +# defining 9P protocol variants. +# +# Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com> +# SPDX-License-Identifier: AGPL-3.0-or-later + +import os.path +import sys + +sys.path.insert(0, os.path.normpath(os.path.join(__file__, ".."))) +import protogen # pylint: disable=wrong-import-position + +if __name__ == "__main__": + protogen.main() diff --git a/lib9p/protogen/__init__.py b/lib9p/protogen/__init__.py new file mode 100644 index 0000000..c2c6173 --- /dev/null +++ b/lib9p/protogen/__init__.py @@ -0,0 +1,57 @@ +# lib9p/protogen/__init__.py - Generate C marshalers/unmarshalers for +# .9p files defining 9P protocol variants +# +# Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com> +# SPDX-License-Identifier: AGPL-3.0-or-later + +import os.path +import sys +import typing + +import idl + +from . import c, h + +# pylint: disable=unused-variable +__all__ = ["main"] + + +def main() -> None: + if typing.TYPE_CHECKING: + + class ANSIColors: + MAGENTA = "\x1b[35m" + RED = "\x1b[31m" + RESET = "\x1b[0m" + + else: + from _colorize import ANSIColors # Present in Python 3.13+ + + if len(sys.argv) < 2: + raise ValueError("requires at least 1 .9p filename") + parser = idl.Parser() + for txtname in sys.argv[1:]: + try: + parser.parse_file(txtname) + except SyntaxError as e: + print( + f"{ANSIColors.RED}{e.filename}{ANSIColors.RESET}:{ANSIColors.MAGENTA}{e.lineno}{ANSIColors.RESET}: {e.msg}", + file=sys.stderr, + ) + assert e.text + print(f"\t{e.text}", file=sys.stderr) + text_suffix = e.text.lstrip() + text_prefix = e.text[: -len(text_suffix)] + print( + f"\t{text_prefix}{ANSIColors.RED}{'~'*len(text_suffix)}{ANSIColors.RESET}", + file=sys.stderr, + ) + sys.exit(2) + versions, typs = parser.all() + outdir = os.path.normpath(os.path.join(sys.argv[0], "..")) + with open( + os.path.join(outdir, "include/lib9p/9p.generated.h"), "w", encoding="utf-8" + ) as fh: + fh.write(h.gen_h(versions, typs)) + with open(os.path.join(outdir, "9p.generated.c"), "w", encoding="utf-8") as fh: + fh.write(c.gen_c(versions, typs)) diff --git a/lib9p/protogen/c.py b/lib9p/protogen/c.py new file mode 100644 index 0000000..a6824ce --- /dev/null +++ b/lib9p/protogen/c.py @@ -0,0 +1,201 @@ +# lib9p/protogen/c.py - Generate 9p.generated.c +# +# Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com> +# SPDX-License-Identifier: AGPL-3.0-or-later + +import sys + +import idl + +from . import c9util, c_format, c_marshal, c_unmarshal, c_validate, cutil + +# This strives to be "general-purpose" in that it just acts on the +# *.9p inputs; but (unfortunately?) there are a few special-cases in +# this script, marked with "SPECIAL". + + +# pylint: disable=unused-variable +__all__ = ["gen_c"] + + +def gen_c(versions: set[str], typs: list[idl.UserType]) -> str: + cutil.ifdef_init() + + ret = f"""/* Generated by `{' '.join(sys.argv)}`. DO NOT EDIT! */ + +#include <stdbool.h> +#include <stddef.h> /* for size_t */ +#include <inttypes.h> /* for PRI* macros */ +#include <string.h> /* for memset() */ + +#include <libmisc/assert.h> +#include <libmisc/endian.h> + +#include <lib9p/9p.h> + +#include "tables.h" +#include "utf8.h" +""" + # libobj vtables ########################################################### + ret += """ +/* libobj vtables *************************************************************/ +""" + for typ in typs: + ret += cutil.ifdef_push(1, c9util.ver_ifdef(typ.in_versions)) + ret += f"LO_IMPLEMENTATION_C(fmt_formatter, {c9util.typename(typ)}, {c9util.basename(typ)}, static);\n" + ret += cutil.ifdef_pop(0) + + # utilities ################################################################ + ret += """ +/* utilities ******************************************************************/ +""" + + id2typ: dict[int, idl.Message] = {} + for msg in [msg for msg in typs if isinstance(msg, idl.Message)]: + id2typ[msg.msgid] = msg + + for v in sorted(versions): + ret += f"#if CONFIG_9P_ENABLE_{v.replace('.', '_')}\n" + ret += ( + f"\t#define _is_ver_{v.replace('.', '_')}(v) (v == {c9util.ver_enum(v)})\n" + ) + ret += "#else\n" + ret += f"\t#define _is_ver_{v.replace('.', '_')}(v) false\n" + ret += "#endif\n" + ret += "\n" + ret += "/**\n" + ret += f" * is_ver(ctx, ver) is essentially `(ctx->version == {c9util.Ident('VER_')}##ver)`, but\n" + ret += f" * compiles correctly (to `false`) even if `{c9util.Ident('VER_')}##ver` isn't defined\n" + ret += " * (because `!CONFIG_9P_ENABLE_##ver`). This is useful when `||`ing\n" + ret += " * several version checks together.\n" + ret += " */\n" + ret += "#define is_ver(ctx, ver) _is_ver_##ver((ctx)->version)\n" + + # bitmasks ################################################################# + ret += """ +/* bitmasks *******************************************************************/ +""" + for typ in typs: + if not isinstance(typ, idl.Bitfield): + continue + ret += "\n" + ret += cutil.ifdef_push(1, c9util.ver_ifdef(typ.in_versions)) + ret += f"static const {c9util.typename(typ)} {typ.typname}_masks[{c9util.ver_enum('NUM')}] = {{\n" + verwidth = max(len(ver) for ver in versions) + for ver in sorted(versions): + ret += cutil.ifdef_push(2, c9util.ver_ifdef({ver})) + ret += ( + f"\t[{c9util.ver_enum(ver)}]{' '*(verwidth-len(ver))} = 0b" + + "".join( + ( + "1" + if (bit.cat == "USED" or isinstance(bit.cat, idl.BitNum)) + and ver in bit.in_versions + else "0" + ) + for bit in reversed(typ.bits) + ) + + ",\n" + ) + ret += cutil.ifdef_pop(1) + ret += "};\n" + ret += cutil.ifdef_pop(0) + + # validate_* ############################################################### + ret += c_validate.gen_c_validate(versions, typs) + + # unmarshal_* ############################################################## + ret += c_unmarshal.gen_c_unmarshal(versions, typs) + + # marshal_* ################################################################ + ret += c_marshal.gen_c_marshal(versions, typs) + + # *_format ################################################################# + ret += c_format.gen_c_format(versions, typs) + + # tables.h ################################################################# + ret += """ +/* tables.h *******************************************************************/ +""" + + ret += "\n" + ret += f"const struct {c9util.ident('_ver_tentry')} {c9util.ident('_table_ver')}[{c9util.ver_enum('NUM')}] = {{\n" + rerror = next(typ for typ in typs if typ.typname == "Rerror") + for ver in ["unknown", *sorted(versions)]: + if ver == "unknown": + min_msg_size = rerror.min_size("9P2000") # SPECIAL (initialization) + else: + ret += cutil.ifdef_push(1, c9util.ver_ifdef({ver})) + min_msg_size = rerror.min_size(ver) + ret += f'\t[{c9util.ver_enum(ver)}] = {{.name="{ver}", .min_msg_size={min_msg_size}}},\n' + ret += cutil.ifdef_pop(0) + ret += "};\n" + + def msg_table( + cstruct: str, cname: str, each: str, _range: tuple[int, int, int] + ) -> str: + ret = f"const struct {c9util.ident(cstruct)} {c9util.ident(cname)}[{c9util.ver_enum('NUM')}][{hex(len(range(*_range)))}] = {{\n" + for ver in ["unknown", *sorted(versions)]: + if ver != "unknown": + ret += cutil.ifdef_push(1, c9util.ver_ifdef({ver})) + ret += f"\t[{c9util.ver_enum(ver)}] = {{\n" + for n in range(*_range): + xmsg: idl.Message | None = id2typ.get(n, None) + if xmsg: + if ver == "unknown": # SPECIAL (initialization) + if xmsg.typname not in ["Tversion", "Rversion", "Rerror"]: + xmsg = None + else: + if ver not in xmsg.in_versions: + xmsg = None + if xmsg: + ret += f"\t\t{each}({xmsg.typname}),\n" + ret += "\t},\n" + ret += cutil.ifdef_pop(0) + ret += "};\n" + return ret + + ret += "\n" + ret += cutil.macro( + f"#define _MSG(typ) [{c9util.Ident('TYP_')}##typ] = {{\n" + f"\t\t.name = #typ,\n" + f"\t\t.box_as_fmt_formatter = (_box_as_fmt_formatter_fn_t)lo_box_{c9util.ident('msg_')}##typ##_as_fmt_formatter,\n" + f"\t}}\n" + ) + ret += msg_table("_msg_tentry", "_table_msg", "_MSG", (0, 0x100, 1)) + + ret += "\n" + ret += cutil.macro( + f"#define _MSG_RECV(typ) [{c9util.Ident('TYP_')}##typ/2] = {{\n" + f"\t\t.validate = validate_##typ,\n" + f"\t\t.unmarshal = (_unmarshal_fn_t)unmarshal_##typ,\n" + f"\t}}\n" + ) + ret += cutil.macro( + f"#define _MSG_SEND(typ) [{c9util.Ident('TYP_')}##typ/2] = {{\n" + f"\t\t.marshal = (_marshal_fn_t)marshal_##typ,\n" + f"\t}}\n" + ) + ret += "\n" + ret += msg_table("_recv_tentry", "_table_Tmsg_recv", "_MSG_RECV", (0, 0x100, 2)) + ret += "\n" + ret += msg_table("_recv_tentry", "_table_Rmsg_recv", "_MSG_RECV", (1, 0x100, 2)) + ret += "\n" + ret += msg_table("_send_tentry", "_table_Tmsg_send", "_MSG_SEND", (0, 0x100, 2)) + ret += "\n" + ret += msg_table("_send_tentry", "_table_Rmsg_send", "_MSG_SEND", (1, 0x100, 2)) + + ret += f""" +LM_FLATTEN ssize_t {c9util.ident('_stat_validate')}(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes, uint32_t *ret_net_size) {{ +\treturn validate_stat(ctx, net_size, net_bytes, ret_net_size); +}} +LM_FLATTEN void {c9util.ident('_stat_unmarshal')}(struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out) {{ +\tunmarshal_stat(ctx, net_bytes, out); +}} +LM_FLATTEN bool {c9util.ident('_stat_marshal')}(struct lib9p_ctx *ctx, struct {c9util.ident('stat')} *val, struct _marshal_ret *ret) {{ +\treturn marshal_stat(ctx, val, ret); +}} +""" + + ############################################################################ + return ret diff --git a/lib9p/protogen/c9util.py b/lib9p/protogen/c9util.py new file mode 100644 index 0000000..cf91951 --- /dev/null +++ b/lib9p/protogen/c9util.py @@ -0,0 +1,134 @@ +# lib9p/protogen/c9util.py - Utilities for generating lib9p-specific C +# +# Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com> +# SPDX-License-Identifier: AGPL-3.0-or-later + +import re +import typing + +import idl + +# This strives to be "general-purpose" in that it just acts on the +# *.9p inputs; but (unfortunately?) there are a few special-cases in +# this script, marked with "SPECIAL". + +# pylint: disable=unused-variable +__all__ = [ + "add_prefix", + "ident", + "Ident", + "IDENT", + "ver_enum", + "ver_ifdef", + "ver_cond", + "typename", + "idl_expr", +] + +# idents ####################################################################### + + +def add_prefix(p: str, s: str) -> str: + if s.startswith("_"): + return "_" + p + s[1:] + return p + s + + +def _ident(p: str, s: str) -> str: + return add_prefix(p, s.replace(".", "_")) + + +def ident(s: str) -> str: + return _ident("lib9p_", s) + + +def Ident(s: str) -> str: + return _ident("lib9p_".upper(), s) + + +def IDENT(s: str) -> str: + return _ident("lib9p_", s).upper() + + +# versions ##################################################################### + + +def ver_enum(ver: str) -> str: + return Ident("VER_" + ver) + + +def ver_ifdef(versions: typing.Collection[str]) -> str: + return " || ".join( + f"CONFIG_9P_ENABLE_{v.replace('.', '_')}" for v in sorted(versions) + ) + + +def ver_cond(versions: typing.Collection[str]) -> str: + if len(versions) == 1: + v = next(v for v in versions) + return f"is_ver(ctx, {v.replace('.', '_')})" + return "( " + (" || ".join(ver_cond({v}) for v in sorted(versions))) + " )" + + +# misc ######################################################################### + + +def basename(typ: idl.UserType) -> str: + match typ: + case idl.Number(): + return ident(typ.typname) + case idl.Bitfield(): + return ident(typ.typname) + case idl.Message(): + return ident(f"msg_{typ.typname}") + case idl.Struct(): + return ident(typ.typname) + case _: + raise ValueError(f"not a defined type: {typ.__class__.__name__}") + + +def typename(typ: idl.Type, parent: idl.StructMember | None = None) -> str: + match typ: + case idl.Primitive(): + if typ.value == 1 and parent and parent.cnt: # SPECIAL (string) + return "[[gnu::nonstring]] char" + return f"uint{typ.value*8}_t" + case idl.Number(): + return f"{basename(typ)}_t" + case idl.Bitfield(): + return f"{basename(typ)}_t" + case idl.Message(): + return f"struct {basename(typ)}" + case idl.Struct(): + return f"struct {basename(typ)}" + case _: + raise ValueError(f"not a type: {typ.__class__.__name__}") + + +def idl_expr( + expr: idl.Expr, lookup_sym: typing.Callable[[str], str], bitwidth: int = 0 +) -> str: + ret: list[str] = [] + for tok in expr.tokens: + match tok: + case idl.ExprOp(): + ret.append(tok.op) + case idl.ExprLit(): + if bitwidth: + ret.append(f"{tok.val:#0{bitwidth}b}") + else: + ret.append(str(tok.val)) + case idl.ExprSym(): + if m := re.fullmatch(r"^u(8|16|32|64)_max$", tok.symname): + ret.append(f"UINT{m.group(1)}_MAX") + elif m := re.fullmatch(r"^s(8|16|32|64)_max$", tok.symname): + ret.append(f"INT{m.group(1)}_MAX") + else: + ret.append(lookup_sym(tok.symname)) + case idl.ExprOff(): + ret.append(lookup_sym("&" + tok.membname)) + case idl.ExprNum(): + ret.append(Ident(add_prefix(f"{tok.numname}_".upper(), tok.valname))) + case _: + assert False + return " ".join(ret) diff --git a/lib9p/protogen/c_format.py b/lib9p/protogen/c_format.py new file mode 100644 index 0000000..4a809d1 --- /dev/null +++ b/lib9p/protogen/c_format.py @@ -0,0 +1,161 @@ +# lib9p/protogen/c_format.py - Generate C pretty-print functions +# +# Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com> +# SPDX-License-Identifier: AGPL-3.0-or-later + + +import idl + +from . import c9util, cutil + +# This strives to be "general-purpose" in that it just acts on the +# *.9p inputs; but (unfortunately?) there are a few special-cases in +# this script, marked with "SPECIAL". + +# pylint: disable=unused-variable +__all__ = ["gen_c_format"] + + +def bf_numname(typ: idl.Bitfield, num: idl.BitNum, base: str) -> str: + prefix = f"{typ.typname}_{num.numname}_".upper() + return c9util.Ident(c9util.add_prefix(prefix, base)) + + +def ext_printf(line: str) -> str: + assert line.startswith("\t") + assert line.endswith("\n") + # It sucks that %v trips -Wformat and -Wformat-extra-args + # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47781 + ret = "#pragma GCC diagnostic push\n" + ret += '#pragma GCC diagnostic ignored "-Wformat"\n' + ret += '#pragma GCC diagnostic ignored "-Wformat-extra-args"\n' + ret += line + ret += "#pragma GCC diagnostic pop\n" + return ret + + +def gen_c_format(versions: set[str], typs: list[idl.UserType]) -> str: + ret = """ +/* *_format *******************************************************************/ +""" + for typ in typs: + ret += "\n" + ret += cutil.ifdef_push(1, c9util.ver_ifdef(typ.in_versions)) + ret += f"static void {c9util.basename(typ)}_format({c9util.typename(typ)} *self, struct fmt_state *state) {{\n" + match typ: + case idl.Number(): + if typ.vals: + ret += "\tswitch (*self) {\n" + for name in typ.vals: + ret += f"\tcase {c9util.Ident(c9util.add_prefix(f'{typ.typname}_'.upper(), name))}:\n" + ret += f'\t\tfmt_state_puts(state, "{name}");\n' + ret += "\t\tbreak;\n" + ret += "\tdefault:\n" + ret += f'\t\tfmt_state_printf(state, "%"PRIu{typ.static_size*8}, *self);\n' + ret += "\t}\n" + else: + ret += f'\t\tfmt_state_printf(state, "%"PRIu{typ.static_size*8}, *self);\n' + case idl.Bitfield(): + val = "*self" + if typ.typname == "dm": # SPECIAL (pretty file permissions) + val = f"(*self & ~(({c9util.typename(typ)})0777))" + ret += "\tbool empty = true;\n" + ret += "\tfmt_state_putchar(state, '(');\n" + nums: set[str] = set() + + for bit in reversed(typ.bits): + match bit.cat: + case "UNUSED" | "USED" | "RESERVED": + if bit.cat == "UNUSED": + bitname = f"1<<{bit.num}" + else: + bitname = bit.bitname + ret += f"\tif ({val} & (UINT{typ.static_size*8}_C(1)<<{bit.num})) {{\n" + ret += "\t\tif (!empty)\n" + ret += "\t\t\tfmt_state_putchar(state, '|');\n" + ret += f'\t\tfmt_state_puts(state, "{bitname}");\n' + ret += "\t\tempty = false;\n" + ret += "\t}\n" + case idl.BitNum(): + if bit.cat.numname in nums: + continue + ret += f"\tswitch ({val} & {bf_numname(typ, bit.cat, 'MASK')}) {{\n" + for name in bit.cat.vals: + ret += f"\tcase {bf_numname(typ, bit.cat, name)}:\n" + bitname = c9util.add_prefix( + f"{bit.cat.numname}_".upper(), name + ) + ret += "\t\tif (!empty)\n" + ret += "\t\t\tfmt_state_putchar(state, '|');\n" + ret += f'\t\tfmt_state_puts(state, "{bitname}");\n' + ret += "\t\tempty = false;\n" + ret += "\t\tbreak;\n" + ret += "\tdefault:\n" + ret += "\t\tif (!empty)\n" + ret += "\t\t\tfmt_state_putchar(state, '|');\n" + ret += f'\t\tfmt_state_printf(state, "%"PRIu{typ.static_size*8}, {val} & {bf_numname(typ, bit.cat, 'MASK')});\n' + ret += "\t\tempty = false;\n" + ret += "\t}\n" + nums.add(bit.cat.numname) + if typ.typname == "dm": # SPECIAL (pretty file permissions) + ret += "\tif (!empty)\n" + ret += "\t\tfmt_state_putchar(state, '|');\n" + ret += f'\tfmt_state_printf(state, "%#04"PRIo{typ.static_size*8}, *self & 0777);\n' + else: + ret += "\tif (empty)\n" + ret += "\t\tfmt_state_putchar(state, '0');\n" + ret += "\tfmt_state_putchar(state, ')');\n" + case idl.Struct(typname="s"): # SPECIAL(string) + ret += ext_printf( + '\tfmt_state_printf(state, "%.*q", self->len, self->utf8);\n' + ) + case idl.Struct(): # and idl.Message(): + if isinstance(typ, idl.Message): + ret += f'\tfmt_state_puts(state, "{typ.typname} {{");\n' + else: + ret += "\tfmt_state_putchar(state, '{');\n" + for member in typ.members: + if member.val: + continue + ret += cutil.ifdef_push(2, c9util.ver_ifdef(member.in_versions)) + if member.cnt: + if isinstance(member.cnt, int): + cnt_str = str(member.cnt) + cnt_typ = "size_t" + else: + cnt_str = f"self->{member.cnt.membname}" + cnt_typ = c9util.typename(member.cnt.typ) + if member.typ.static_size == 1: # SPECIAL (data) + ret += f"\tif (is_valid_utf8_without_nul((uint8_t *)self->{member.membname}, (size_t){cnt_str})) {{\n" + ret += ext_printf( + f'\t\tfmt_state_printf(state, " {member.membname}=%.*q%s",\n' + f"\t\t\t(int)({cnt_str} < 50 ? {cnt_str} : 50),\n" + f"\t\t\t(char *)self->{member.membname},\n" + f'\t\t\t{cnt_str} < 50 ? "" : "...");\n' + ) + ret += "\t} else {\n" + ret += f'\t\tfmt_state_puts(state, " {member.membname}=<bytedata>");\n' + ret += "\t}\n" + continue + ret += f'\tfmt_state_puts(state, " {member.membname}=[");\n' + ret += f"\tfor ({cnt_typ} i = 0; i < {cnt_str}; i++) {{\n" + ret += "\t\tif (i)\n" + ret += '\t\t\tfmt_state_puts(state, ", ");\n' + if isinstance(member.typ, idl.Primitive): + ret += f'\t\tfmt_state_printf(state, "%"PRIu{member.typ.static_size*8}, self->{member.membname}[i]);\n' + else: + ret += f"\t\t{c9util.basename(member.typ)}_format(&self->{member.membname}[i], state);\n" + ret += "\t}\n" + ret += '\tfmt_state_puts(state, " ]");\n' + else: + ret += f'\tfmt_state_puts(state, " {member.membname}=");\n' + if isinstance(member.typ, idl.Primitive): + ret += f'\tfmt_state_printf(state, "%"PRIu{member.typ.static_size*8}, self->{member.membname});\n' + else: + ret += f"\t{c9util.basename(member.typ)}_format(&self->{member.membname}, state);\n" + ret += cutil.ifdef_pop(1) + ret += '\tfmt_state_puts(state, " }");\n' + ret += "}\n" + ret += cutil.ifdef_pop(0) + + return ret diff --git a/lib9p/protogen/c_marshal.py b/lib9p/protogen/c_marshal.py new file mode 100644 index 0000000..4dab864 --- /dev/null +++ b/lib9p/protogen/c_marshal.py @@ -0,0 +1,403 @@ +# lib9p/protogen/c_marshal.py - Generate C marshal functions +# +# Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com> +# SPDX-License-Identifier: AGPL-3.0-or-later + +import typing + +import idl + +from . import c9util, cutil, idlutil + +# This strives to be "general-purpose" in that it just acts on the +# *.9p inputs; but (unfortunately?) there are a few special-cases in +# this script, marked with "SPECIAL". + + +# pylint: disable=unused-variable +__all__ = ["gen_c_marshal"] + +# get_offset_expr() ############################################################ + + +class OffsetExpr: + static: int + cond: dict[frozenset[str], "OffsetExpr"] + rep: list[tuple[idlutil.Path | int, "OffsetExpr"]] + + def __init__(self) -> None: + self.static = 0 + self.rep = [] + self.cond = {} + + def add(self, other: "OffsetExpr") -> None: + self.static += other.static + self.rep += other.rep + for k, v in other.cond.items(): + if k in self.cond: + self.cond[k].add(v) + else: + self.cond[k] = v + + def gen_c( + self, + dsttyp: str, + dstvar: str, + root: str, + indent_depth: int, + loop_depth: int, + ) -> str: + oneline: list[str] = [] + multiline = "" + if self.static: + oneline.append(str(self.static)) + for cnt, sub in self.rep: + if isinstance(cnt, int): + cnt_str = str(cnt) + cnt_typ = "size_t" + else: + cnt_str = cnt.c_str(root) + cnt_typ = c9util.typename(cnt.elems[-1].typ) + if not sub.cond and not sub.rep: + if sub.static == 1: + oneline.append(cnt_str) + else: + oneline.append(f"({cnt_str})*{sub.static}") + continue + loopvar = chr(ord("i") + loop_depth) + multiline += f"{'\t'*indent_depth}for ({cnt_typ} {loopvar} = 0; {loopvar} < {cnt_str}; {loopvar}++) {{\n" + multiline += sub.gen_c("", dstvar, root, indent_depth + 1, loop_depth + 1) + multiline += f"{'\t'*indent_depth}}}\n" + for vers, sub in self.cond.items(): + multiline += cutil.ifdef_push(indent_depth + 1, c9util.ver_ifdef(vers)) + multiline += f"{'\t'*indent_depth}if {c9util.ver_cond(vers)} {{\n" + multiline += sub.gen_c("", dstvar, root, indent_depth + 1, loop_depth) + multiline += f"{'\t'*indent_depth}}}\n" + multiline += cutil.ifdef_pop(indent_depth) + ret = "" + if dsttyp: + if not oneline: + oneline.append("0") + ret += f"{'\t'*indent_depth}{dsttyp} {dstvar} = {' + '.join(oneline)};\n" + elif oneline: + ret += f"{'\t'*indent_depth}{dstvar} += {' + '.join(oneline)};\n" + ret += multiline + return ret + + +type OffsetExprRecursion = typing.Callable[[idlutil.Path], idlutil.WalkCmd] + + +def get_offset_expr(typ: idl.UserType, recurse: OffsetExprRecursion) -> OffsetExpr: + if not isinstance(typ, idl.Struct): + assert typ.static_size + ret = OffsetExpr() + ret.static = typ.static_size + return ret + + class ExprStackItem(typing.NamedTuple): + path: idlutil.Path + expr: OffsetExpr + pop: typing.Callable[[], None] + + expr_stack: list[ExprStackItem] + + def pop_root() -> None: + assert False + + def pop_cond() -> None: + nonlocal expr_stack + key = frozenset(expr_stack[-1].path.elems[-1].in_versions) + if key in expr_stack[-2].expr.cond: + expr_stack[-2].expr.cond[key].add(expr_stack[-1].expr) + else: + expr_stack[-2].expr.cond[key] = expr_stack[-1].expr + expr_stack = expr_stack[:-1] + + def pop_rep() -> None: + nonlocal expr_stack + member_path = expr_stack[-1].path + member = member_path.elems[-1] + assert member.cnt + cnt: idlutil.Path | int + if isinstance(member.cnt, int): + cnt = member.cnt + else: + cnt = member_path.parent().add(member.cnt) + expr_stack[-2].expr.rep.append((cnt, expr_stack[-1].expr)) + expr_stack = expr_stack[:-1] + + def handle( + path: idlutil.Path, + ) -> tuple[idlutil.WalkCmd, typing.Callable[[], None] | None]: + nonlocal recurse + + ret = recurse(path) + if ret != idlutil.WalkCmd.KEEP_GOING: + return ret, None + + nonlocal expr_stack + expr_stack_len = len(expr_stack) + + def pop() -> None: + nonlocal expr_stack + nonlocal expr_stack_len + while len(expr_stack) > expr_stack_len: + expr_stack[-1].pop() + + if path.elems: + child = path.elems[-1] + parent = path.elems[-2].typ if len(path.elems) > 1 else path.root + if child.in_versions < parent.in_versions: + expr_stack.append( + ExprStackItem(path=path, expr=OffsetExpr(), pop=pop_cond) + ) + if child.cnt: + expr_stack.append( + ExprStackItem(path=path, expr=OffsetExpr(), pop=pop_rep) + ) + if not isinstance(child.typ, idl.Struct): + assert child.typ.static_size + expr_stack[-1].expr.static += child.typ.static_size + return ret, pop + + expr_stack = [ + ExprStackItem(path=idlutil.Path(typ), expr=OffsetExpr(), pop=pop_root) + ] + idlutil.walk(typ, handle) + return expr_stack[0].expr + + +def go_to_end(path: idlutil.Path) -> idlutil.WalkCmd: + return idlutil.WalkCmd.KEEP_GOING + + +def go_to_tok(name: str) -> typing.Callable[[idlutil.Path], idlutil.WalkCmd]: + def ret(path: idlutil.Path) -> idlutil.WalkCmd: + if len(path.elems) == 1 and path.elems[0].membname == name: + return idlutil.WalkCmd.ABORT + return idlutil.WalkCmd.KEEP_GOING + + return ret + + +# Generate .c ################################################################## + + +def gen_c_marshal(versions: set[str], typs: list[idl.UserType]) -> str: + ret = """ +/* marshal_* ******************************************************************/ + +""" + ret += cutil.macro( + "#define MARSHAL_BYTES_ZEROCOPY(ctx, data, len)\n" + "\tif (ret->net_iov[ret->net_iov_cnt-1].iov_len)\n" + "\t\tret->net_iov_cnt++;\n" + "\tret->net_iov[ret->net_iov_cnt-1].iov_base = data;\n" + "\tret->net_iov[ret->net_iov_cnt-1].iov_len = len;\n" + "\tret->net_iov_cnt++;\n" + ) + ret += cutil.macro( + "#define MARSHAL_BYTES(ctx, data, len)\n" + "\tif (!ret->net_iov[ret->net_iov_cnt-1].iov_base)\n" + "\t\tret->net_iov[ret->net_iov_cnt-1].iov_base = &ret->net_copied[ret->net_copied_size];\n" + "\tmemcpy(&ret->net_copied[ret->net_copied_size], data, len);\n" + "\tret->net_copied_size += len;\n" + "\tret->net_iov[ret->net_iov_cnt-1].iov_len += len;\n" + ) + ret += cutil.macro( + "#define MARSHAL_U8LE(ctx, val)\n" + "\tif (!ret->net_iov[ret->net_iov_cnt-1].iov_base)\n" + "\t\tret->net_iov[ret->net_iov_cnt-1].iov_base = &ret->net_copied[ret->net_copied_size];\n" + "\tret->net_copied[ret->net_copied_size] = val;\n" + "\tret->net_copied_size += 1;\n" + "\tret->net_iov[ret->net_iov_cnt-1].iov_len += 1;\n" + ) + ret += cutil.macro( + "#define MARSHAL_U16LE(ctx, val)\n" + "\tif (!ret->net_iov[ret->net_iov_cnt-1].iov_base)\n" + "\t\tret->net_iov[ret->net_iov_cnt-1].iov_base = &ret->net_copied[ret->net_copied_size];\n" + "\tuint16le_encode(&ret->net_copied[ret->net_copied_size], val);\n" + "\tret->net_copied_size += 2;\n" + "\tret->net_iov[ret->net_iov_cnt-1].iov_len += 2;\n" + ) + ret += cutil.macro( + "#define MARSHAL_U32LE(ctx, val)\n" + "\tif (!ret->net_iov[ret->net_iov_cnt-1].iov_base)\n" + "\t\tret->net_iov[ret->net_iov_cnt-1].iov_base = &ret->net_copied[ret->net_copied_size];\n" + "\tuint32le_encode(&ret->net_copied[ret->net_copied_size], val);\n" + "\tret->net_copied_size += 4;\n" + "\tret->net_iov[ret->net_iov_cnt-1].iov_len += 4;\n" + ) + ret += cutil.macro( + "#define MARSHAL_U64LE(ctx, val)\n" + "\tif (!ret->net_iov[ret->net_iov_cnt-1].iov_base)\n" + "\t\tret->net_iov[ret->net_iov_cnt-1].iov_base = &ret->net_copied[ret->net_copied_size];\n" + "\tuint64le_encode(&ret->net_copied[ret->net_copied_size], val);\n" + "\tret->net_copied_size += 8;\n" + "\tret->net_iov[ret->net_iov_cnt-1].iov_len += 8;\n" + ) + + class IndentLevel(typing.NamedTuple): + ifdef: bool # whether this is both `{` and `#if`, or just `{` + + indent_stack: list[IndentLevel] + + def ifdef_lvl() -> int: + return sum(1 if lvl.ifdef else 0 for lvl in indent_stack) + + def indent_lvl() -> int: + return len(indent_stack) + + max_size: int + + def handle( + path: idlutil.Path, + ) -> tuple[idlutil.WalkCmd, typing.Callable[[], None]]: + nonlocal ret + nonlocal indent_stack + nonlocal max_size + indent_stack_len = len(indent_stack) + + def pop() -> None: + nonlocal ret + nonlocal indent_stack + nonlocal indent_stack_len + while len(indent_stack) > indent_stack_len: + if len(indent_stack) == indent_stack_len + 1 and indent_stack[-1].ifdef: + break + ret += f"{'\t'*(indent_lvl()-1)}}}\n" + if indent_stack.pop().ifdef: + ret += cutil.ifdef_pop(ifdef_lvl()) + + loopdepth = sum(1 for elem in path.elems if elem.cnt) + struct = path.elems[-1].typ if path.elems else path.root + if isinstance(struct, idl.Struct): + offsets: list[str] = [] + for member in struct.members: + if not member.val: + continue + for tok in member.val.tokens: + match tok: + case idl.ExprSym(symname="end"): + if tok.symname not in offsets: + offsets.append(tok.symname) + case idl.ExprOff(): + if f"&{tok.membname}" not in offsets: + offsets.append(f"&{tok.membname}") + for name in offsets: + name_prefix = f"offsetof{''.join('_'+m.membname for m in path.elems)}_" + if name == "end": + if not path.elems: + if max_size > cutil.UINT32_MAX: + ret += f"{'\t'*indent_lvl()}uint32_t {name_prefix}end = (uint32_t)needed_size;\n" + else: + ret += f"{'\t'*indent_lvl()}uint32_t {name_prefix}end = needed_size;\n" + continue + recurse: OffsetExprRecursion = go_to_end + else: + assert name.startswith("&") + name = name[1:] + recurse = go_to_tok(name) + expr = get_offset_expr(struct, recurse) + expr_prefix = path.c_str("val->", loopdepth) + if not expr_prefix.endswith(">"): + expr_prefix += "." + ret += expr.gen_c( + "uint32_t", + name_prefix + name, + expr_prefix, + indent_lvl(), + loopdepth, + ) + if not path.elems: + return idlutil.WalkCmd.KEEP_GOING, pop + + child = path.elems[-1] + parent = path.elems[-2].typ if len(path.elems) > 1 else path.root + if child.in_versions < parent.in_versions: + if line := cutil.ifdef_push( + ifdef_lvl() + 1, c9util.ver_ifdef(child.in_versions) + ): + ret += line + ret += ( + f"{'\t'*indent_lvl()}if ({c9util.ver_cond(child.in_versions)}) {{\n" + ) + indent_stack.append(IndentLevel(ifdef=True)) + if child.cnt: + if isinstance(child.cnt, int): + cnt_str = str(child.cnt) + cnt_typ = "size_t" + else: + cnt_str = path.parent().add(child.cnt).c_str("val->") + cnt_typ = c9util.typename(child.cnt.typ) + if child.typ.static_size == 1: # SPECIAL (zerocopy) + if path.root.typname == "stat": # SPECIAL (stat) + ret += f"{'\t'*indent_lvl()}MARSHAL_BYTES(ctx, {path.c_str('val->')[:-3]}, {cnt_str});\n" + else: + ret += f"{'\t'*indent_lvl()}MARSHAL_BYTES_ZEROCOPY(ctx, {path.c_str('val->')[:-3]}, {cnt_str});\n" + return idlutil.WalkCmd.KEEP_GOING, pop + loopvar = chr(ord("i") + loopdepth - 1) + ret += f"{'\t'*indent_lvl()}for ({cnt_typ} {loopvar} = 0; {loopvar} < {cnt_str}; {loopvar}++) {{\n" + indent_stack.append(IndentLevel(ifdef=False)) + if not isinstance(child.typ, idl.Struct): + if child.val: + + def lookup_sym(sym: str) -> str: + nonlocal path + if sym.startswith("&"): + sym = sym[1:] + return f"offsetof{''.join('_'+m.membname for m in path.elems[:-1])}_{sym}" + + val = c9util.idl_expr(child.val, lookup_sym) + else: + val = path.c_str("val->") + if isinstance(child.typ, idl.Bitfield): + val += f" & {child.typ.typname}_masks[ctx->version]" + ret += f"{'\t'*indent_lvl()}MARSHAL_U{child.typ.static_size*8}LE(ctx, {val});\n" + return idlutil.WalkCmd.KEEP_GOING, pop + + for typ in typs: + if not ( + isinstance(typ, idl.Message) or typ.typname == "stat" + ): # SPECIAL (include stat) + continue + assert isinstance(typ, idl.Struct) + ret += "\n" + ret += cutil.ifdef_push(1, c9util.ver_ifdef(typ.in_versions)) + ret += f"static bool marshal_{typ.typname}(struct lib9p_ctx *ctx, {c9util.typename(typ)} *val, struct _marshal_ret *ret) {{\n" + + # Pass 1 - check size + max_size = max(typ.max_size(v) for v in typ.in_versions) + + if max_size > cutil.UINT32_MAX: # SPECIAL (9P2000.e) + ret += get_offset_expr(typ, go_to_end).gen_c( + "uint64_t", "needed_size", "val->", 1, 0 + ) + ret += "\tif (needed_size > (uint64_t)(ctx->max_msg_size)) {\n" + else: + ret += get_offset_expr(typ, go_to_end).gen_c( + "uint32_t", "needed_size", "val->", 1, 0 + ) + ret += "\tif (needed_size > ctx->max_msg_size) {\n" + if isinstance(typ, idl.Message): # SPECIAL (disable for stat) + ret += '\t\tlib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")",\n' + ret += f'\t\t\t"{typ.typname}",\n' + ret += f'\t\t\tctx->version ? "negotiated" : "{'client' if typ.msgid % 2 == 0 else 'server'}",\n' + ret += "\t\t\tctx->max_msg_size);\n" + ret += "\t\treturn true;\n" + ret += "\t}\n" + + # Pass 2 - write data + indent_stack = [IndentLevel(ifdef=True)] + idlutil.walk(typ, handle) + while len(indent_stack) > 1: + ret += f"{'\t'*(indent_lvl()-1)}}}\n" + if indent_stack.pop().ifdef: + ret += cutil.ifdef_pop(ifdef_lvl()) + + # Return + ret += "\treturn false;\n" + ret += "}\n" + ret += cutil.ifdef_pop(0) + return ret diff --git a/lib9p/protogen/c_unmarshal.py b/lib9p/protogen/c_unmarshal.py new file mode 100644 index 0000000..34635f9 --- /dev/null +++ b/lib9p/protogen/c_unmarshal.py @@ -0,0 +1,138 @@ +# lib9p/protogen/c_unmarshal.py - Generate C unmarshal functions +# +# Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com> +# SPDX-License-Identifier: AGPL-3.0-or-later + +import typing + +import idl + +from . import c9util, cutil, idlutil + +# This strives to be "general-purpose" in that it just acts on the +# *.9p inputs; but (unfortunately?) there are a few special-cases in +# this script, marked with "SPECIAL". + + +# pylint: disable=unused-variable +__all__ = ["gen_c_unmarshal"] + + +def gen_c_unmarshal(versions: set[str], typs: list[idl.UserType]) -> str: + ret = """ +/* unmarshal_* ****************************************************************/ + +""" + ret += cutil.macro( + "#define UNMARSHAL_BYTES(ctx, data_lvalue, len)\n" + "\tdata_lvalue = (char *)&net_bytes[net_offset];\n" + "\tnet_offset += len;\n" + ) + ret += cutil.macro( + "#define UNMARSHAL_U8LE(ctx, val_lvalue)\n" + "\tval_lvalue = net_bytes[net_offset];\n" + "\tnet_offset += 1;\n" + ) + ret += cutil.macro( + "#define UNMARSHAL_U16LE(ctx, val_lvalue)\n" + "\tval_lvalue = uint16le_decode(&net_bytes[net_offset]);\n" + "\tnet_offset += 2;\n" + ) + ret += cutil.macro( + "#define UNMARSHAL_U32LE(ctx, val_lvalue)\n" + "\tval_lvalue = uint32le_decode(&net_bytes[net_offset]);\n" + "\tnet_offset += 4;\n" + ) + ret += cutil.macro( + "#define UNMARSHAL_U64LE(ctx, val_lvalue)\n" + "\tval_lvalue = uint64le_decode(&net_bytes[net_offset]);\n" + "\tnet_offset += 8;\n" + ) + + class IndentLevel(typing.NamedTuple): + ifdef: bool # whether this is both `{` and `#if`, or just `{` + + indent_stack: list[IndentLevel] + + def ifdef_lvl() -> int: + return sum(1 if lvl.ifdef else 0 for lvl in indent_stack) + + def indent_lvl() -> int: + return len(indent_stack) + + def handle( + path: idlutil.Path, + ) -> tuple[idlutil.WalkCmd, typing.Callable[[], None]]: + nonlocal ret + nonlocal indent_stack + indent_stack_len = len(indent_stack) + + def pop() -> None: + nonlocal ret + nonlocal indent_stack + nonlocal indent_stack_len + while len(indent_stack) > indent_stack_len: + if len(indent_stack) == indent_stack_len + 1 and indent_stack[-1].ifdef: + break + ret += f"{'\t'*(indent_lvl()-1)}}}\n" + if indent_stack.pop().ifdef: + ret += cutil.ifdef_pop(ifdef_lvl()) + + if not path.elems: + return idlutil.WalkCmd.KEEP_GOING, pop + + child = path.elems[-1] + parent = path.elems[-2].typ if len(path.elems) > 1 else path.root + if child.in_versions < parent.in_versions: + if line := cutil.ifdef_push( + ifdef_lvl() + 1, c9util.ver_ifdef(child.in_versions) + ): + ret += line + ret += ( + f"{'\t'*indent_lvl()}if ({c9util.ver_cond(child.in_versions)}) {{\n" + ) + indent_stack.append(IndentLevel(ifdef=True)) + if child.cnt: + if isinstance(child.cnt, int): + cnt_str = str(child.cnt) + cnt_typ = "size_t" + else: + cnt_str = path.parent().add(child.cnt).c_str("out->") + cnt_typ = c9util.typename(child.cnt.typ) + if child.typ.static_size == 1: # SPECIAL (zerocopy) + ret += f"{'\t'*indent_lvl()}UNMARSHAL_BYTES(ctx, {path.c_str('out->')[:-3]}, {cnt_str});\n" + return idlutil.WalkCmd.KEEP_GOING, pop + ret += f"{'\t'*indent_lvl()}{path.c_str('out->')[:-3]} = extra;\n" + ret += f"{'\t'*indent_lvl()}extra += sizeof({path.c_str('out->')[:-3]}[0]) * {cnt_str};\n" + loopdepth = sum(1 for elem in path.elems if elem.cnt) + loopvar = chr(ord("i") + loopdepth - 1) + ret += f"{'\t'*indent_lvl()}for ({cnt_typ} {loopvar} = 0; {loopvar} < {cnt_str}; {loopvar}++) {{\n" + indent_stack.append(IndentLevel(ifdef=False)) + if not isinstance(child.typ, idl.Struct): + if child.val: + ret += f"{'\t'*indent_lvl()}net_offset += {child.typ.static_size};\n" + else: + ret += f"{'\t'*indent_lvl()}UNMARSHAL_U{child.typ.static_size*8}LE(ctx, {path.c_str('out->')});\n" + return idlutil.WalkCmd.KEEP_GOING, pop + + for typ in typs: + if not ( + isinstance(typ, idl.Message) or typ.typname == "stat" + ): # SPECIAL (include stat) + continue + assert isinstance(typ, idl.Struct) + ret += "\n" + ret += cutil.ifdef_push(1, c9util.ver_ifdef(typ.in_versions)) + ret += f"static void unmarshal_{typ.typname}([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {{\n" + ret += f"\t{c9util.typename(typ)} *out = out_buf;\n" + ret += "\t[[gnu::unused]] void *extra = &out[1];\n" + ret += "\tuint32_t net_offset = 0;\n" + + indent_stack = [IndentLevel(ifdef=True)] + idlutil.walk(typ, handle) + while len(indent_stack) > 0: + ret += f"{'\t'*(indent_lvl()-1)}}}\n" + if indent_stack.pop().ifdef and indent_stack: + ret += cutil.ifdef_pop(ifdef_lvl()) + ret += cutil.ifdef_pop(0) + return ret diff --git a/lib9p/protogen/c_validate.py b/lib9p/protogen/c_validate.py new file mode 100644 index 0000000..535a750 --- /dev/null +++ b/lib9p/protogen/c_validate.py @@ -0,0 +1,299 @@ +# lib9p/protogen/c_validate.py - Generate C validation functions +# +# Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com> +# SPDX-License-Identifier: AGPL-3.0-or-later + +import typing + +import idl + +from . import c9util, cutil, idlutil + +# This strives to be "general-purpose" in that it just acts on the +# *.9p inputs; but (unfortunately?) there are a few special-cases in +# this script, marked with "SPECIAL". + + +# pylint: disable=unused-variable +__all__ = ["gen_c_validate"] + + +def should_save_offset(parent: idl.Struct, child: idl.StructMember) -> bool: + if child.val or child.max or isinstance(child.typ, idl.Bitfield): + return True + for sibling in parent.members: + if sibling.val: + for tok in sibling.val.tokens: + if isinstance(tok, idl.ExprOff) and tok.membname == child.membname: + return True + if sibling.max: + for tok in sibling.max.tokens: + if isinstance(tok, idl.ExprOff) and tok.membname == child.membname: + return True + return False + + +def should_save_end_offset(struct: idl.Struct) -> bool: + for memb in struct.members: + if memb.val: + for tok in memb.val.tokens: + if isinstance(tok, idl.ExprSym) and tok.symname == "end": + return True + if memb.max: + for tok in memb.max.tokens: + if isinstance(tok, idl.ExprSym) and tok.symname == "end": + return True + return False + + +def gen_c_validate(versions: set[str], typs: list[idl.UserType]) -> str: + ret = """ +/* validate_* *****************************************************************/ + +""" + ret += cutil.macro( + "#define VALIDATE_NET_BYTES(n)\n" + "\tif (__builtin_add_overflow(net_offset, n, &net_offset))\n" + "\t\t/* If needed-net-size overflowed uint32_t, then\n" + "\t\t * there's no way that actual-net-size will live up to\n" + "\t\t * that. */\n" + '\t\treturn lib9p_error(ctx, LINUX_EBADMSG, "message is too short for content");\n' + "\tif (net_offset > net_size)\n" + '\t\treturn lib9p_errorf(ctx, LINUX_EBADMSG, "message is too short for content (%"PRIu32" > %"PRIu32") @ %d", net_offset, net_size, __LINE__);\n' + ) + ret += cutil.macro( + "#define VALIDATE_NET_UTF8(n)\n" + "\t{\n" + "\t\tsize_t len = n;\n" + "\t\tVALIDATE_NET_BYTES(len);\n" + "\t\tif (!is_valid_utf8_without_nul(&net_bytes[net_offset-len], len))\n" + '\t\t\treturn lib9p_error(ctx, LINUX_EBADMSG, "message contains invalid UTF-8");\n' + "\t}\n" + ) + ret += cutil.macro( + "#define RESERVE_HOST_BYTES(n)\n" + "\tif (__builtin_add_overflow(host_size, n, &host_size))\n" + "\t\t/* If needed-host-size overflowed ssize_t, then there's\n" + "\t\t * no way that actual-net-size will live up to\n" + "\t\t * that. */\n" + '\t\treturn lib9p_error(ctx, LINUX_EBADMSG, "message is too short for content");\n' + ) + + ret += "#define GET_U8LE(off) (net_bytes[off])\n" + ret += "#define GET_U16LE(off) uint16le_decode(&net_bytes[off])\n" + ret += "#define GET_U32LE(off) uint32le_decode(&net_bytes[off])\n" + ret += "#define GET_U64LE(off) uint64le_decode(&net_bytes[off])\n" + + ret += "#define LAST_U8LE() GET_U8LE(net_offset-1)\n" + ret += "#define LAST_U16LE() GET_U16LE(net_offset-2)\n" + ret += "#define LAST_U32LE() GET_U32LE(net_offset-4)\n" + ret += "#define LAST_U64LE() GET_U64LE(net_offset-8)\n" + + class IndentLevel(typing.NamedTuple): + ifdef: bool # whether this is both `{` and `#if`, or just `{` + + indent_stack: list[IndentLevel] + + def ifdef_lvl() -> int: + return sum(1 if lvl.ifdef else 0 for lvl in indent_stack) + + def indent_lvl() -> int: + return len(indent_stack) + + incr_buf: int + + def incr_flush() -> None: + nonlocal ret + nonlocal incr_buf + if incr_buf: + ret += f"{'\t'*indent_lvl()}VALIDATE_NET_BYTES({incr_buf});\n" + incr_buf = 0 + + def gen_validate_size(path: idlutil.Path) -> None: + nonlocal ret + nonlocal incr_buf + nonlocal indent_stack + + assert path.elems + child = path.elems[-1] + parent = path.elems[-2].typ if len(path.elems) > 1 else path.root + assert isinstance(parent, idl.Struct) + + if child.in_versions < parent.in_versions: + if line := cutil.ifdef_push( + ifdef_lvl() + 1, c9util.ver_ifdef(child.in_versions) + ): + incr_flush() + ret += line + ret += ( + f"{'\t'*indent_lvl()}if ({c9util.ver_cond(child.in_versions)}) {{\n" + ) + indent_stack.append(IndentLevel(ifdef=True)) + if should_save_offset(parent, child): + ret += f"{'\t'*indent_lvl()}uint32_t offsetof{''.join('_'+m.membname for m in path.elems)} = net_offset + {incr_buf};\n" + if child.cnt: + if isinstance(child.cnt, int): + cnt_str = str(child.cnt) + cnt_typ = "size_t" + else: + assert child.cnt.typ.static_size + incr_flush() + cnt_str = f"LAST_U{child.cnt.typ.static_size*8}LE()" + cnt_typ = c9util.typename(child.cnt.typ) + if child.membname == "utf8": # SPECIAL (string) + assert child.typ.static_size == 1 + # Yes, this is content-validation and "belongs" in + # gen_validate_content(), not here. But it's just + # easier this way. + incr_flush() + ret += f"{'\t'*indent_lvl()}VALIDATE_NET_UTF8({cnt_str});\n" + return + if child.typ.static_size == 1: # SPECIAL (zerocopy) + if isinstance(child.cnt, int): + incr_buf += child.cnt + return + incr_flush() + ret += f"{'\t'*indent_lvl()}VALIDATE_NET_BYTES({cnt_str});\n" + return + loopdepth = sum(1 for elem in path.elems if elem.cnt) + loopvar = chr(ord("i") + loopdepth - 1) + incr_flush() + ret += f"{'\t'*indent_lvl()}for ({cnt_typ} {loopvar} = 0, cnt = {cnt_str}; {loopvar} < cnt; {loopvar}++) {{\n" + indent_stack.append(IndentLevel(ifdef=False)) + ret += f"{'\t'*indent_lvl()}RESERVE_HOST_BYTES(sizeof({c9util.typename(child.typ)}));\n" + if not isinstance(child.typ, idl.Struct): + incr_buf += child.typ.static_size + + def gen_validate_content(path: idlutil.Path) -> None: + nonlocal ret + nonlocal incr_buf + nonlocal indent_stack + + assert path.elems + child = path.elems[-1] + parent = path.elems[-2].typ if len(path.elems) > 1 else path.root + assert isinstance(parent, idl.Struct) + + def lookup_sym(sym: str) -> str: + if sym.startswith("&"): + sym = sym[1:] + return f"offsetof{''.join('_'+m.membname for m in path.elems[:-1])}_{sym}" + + if child.val: + incr_flush() + assert child.typ.static_size + nbits = child.typ.static_size * 8 + nbits = child.typ.static_size * 8 + if nbits < 32 and any( + isinstance(tok, idl.ExprSym) + and (tok.symname == "end" or tok.symname.startswith("&")) + for tok in child.val.tokens + ): + nbits = 32 + act = f"(uint{nbits}_t)GET_U{nbits}LE({lookup_sym(f'&{child.membname}')})" + exp = f"(uint{nbits}_t)({c9util.idl_expr(child.val, lookup_sym)})" + ret += f"{'\t'*indent_lvl()}if ({act} != {exp})\n" + ret += f'{"\t"*(indent_lvl()+1)}return lib9p_errorf(ctx, LINUX_EBADMSG, "{path} value is wrong: actual: %"PRIu{nbits}" != correct:%"PRIu{nbits},\n' + ret += f"{'\t'*(indent_lvl()+2)}{act}, {exp});\n" + if child.max: + incr_flush() + assert child.typ.static_size + nbits = child.typ.static_size * 8 + if nbits < 32 and any( + isinstance(tok, idl.ExprSym) + and (tok.symname == "end" or tok.symname.startswith("&")) + for tok in child.max.tokens + ): + nbits = 32 + act = f"(uint{nbits}_t)GET_U{nbits}LE({lookup_sym(f'&{child.membname}')})" + exp = f"(uint{nbits}_t)({c9util.idl_expr(child.max, lookup_sym)})" + ret += f"{'\t'*indent_lvl()}if ({act} > {exp})\n" + ret += f'{"\t"*(indent_lvl()+1)}return lib9p_errorf(ctx, LINUX_EBADMSG, "{path} value is too large: %"PRIu{nbits}" > %"PRIu{nbits},\n' + ret += f"{'\t'*(indent_lvl()+2)}{act}, {exp});\n" + if isinstance(child.typ, idl.Bitfield): + incr_flush() + nbytes = child.typ.static_size + nbits = nbytes * 8 + act = f"GET_U{nbits}LE({lookup_sym(f'&{child.membname}')})" + ret += f"{'\t'*indent_lvl()}if ({act} & ~{child.typ.typname}_masks[ctx->version])\n" + ret += f'{"\t"*(indent_lvl()+1)}return lib9p_errorf(ctx, LINUX_EBADMSG, "unknown bits in {child.typ.typname} bitfield: %#0{nbytes*2}"PRIx{nbits},\n' + ret += f"{'\t'*(indent_lvl()+2)}{act} & ~{child.typ.typname}_masks[ctx->version]);\n" + + def handle( + path: idlutil.Path, + ) -> tuple[idlutil.WalkCmd, typing.Callable[[], None]]: + nonlocal ret + nonlocal incr_buf + nonlocal indent_stack + indent_stack_len = len(indent_stack) + pop_struct = path.elems[-1].typ if path.elems else path.root + pop_path = path + pop_indent_stack_len: int + + def pop() -> None: + nonlocal ret + nonlocal indent_stack + nonlocal indent_stack_len + nonlocal pop_struct + nonlocal pop_path + nonlocal pop_indent_stack_len + if isinstance(pop_struct, idl.Struct): + while len(indent_stack) > pop_indent_stack_len: + incr_flush() + ret += f"{'\t'*(indent_lvl()-1)}}}\n" + if indent_stack.pop().ifdef: + ret += cutil.ifdef_pop(ifdef_lvl()) + parent = pop_struct + path = pop_path + if should_save_end_offset(parent): + ret += f"{'\t'*indent_lvl()}uint32_t offsetof{''.join('_'+m.membname for m in path.elems)}_end = net_offset + {incr_buf};\n" + for child in parent.members: + gen_validate_content(pop_path.add(child)) + while len(indent_stack) > indent_stack_len: + if len(indent_stack) == indent_stack_len + 1 and indent_stack[-1].ifdef: + break + incr_flush() + ret += f"{'\t'*(indent_lvl()-1)}}}\n" + if indent_stack.pop().ifdef: + ret += cutil.ifdef_pop(ifdef_lvl()) + + if path.elems: + gen_validate_size(path) + + pop_indent_stack_len = len(indent_stack) + + return idlutil.WalkCmd.KEEP_GOING, pop + + for typ in typs: + if not ( + isinstance(typ, idl.Message) or typ.typname == "stat" + ): # SPECIAL (include stat) + continue + assert isinstance(typ, idl.Struct) + ret += "\n" + ret += cutil.ifdef_push(1, c9util.ver_ifdef(typ.in_versions)) + if typ.typname == "stat": # SPECIAL (stat) + ret += f"static ssize_t validate_{typ.typname}(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes, uint32_t *ret_net_size) {{\n" + else: + ret += f"static ssize_t validate_{typ.typname}(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {{\n" + + ret += "\tuint32_t net_offset = 0;\n" + ret += f"\tssize_t host_size = sizeof({c9util.typename(typ)});\n" + + incr_buf = 0 + indent_stack = [IndentLevel(ifdef=True)] + idlutil.walk(typ, handle) + while len(indent_stack) > 1: + incr_flush() + ret += f"{'\t'*(indent_lvl()-1)}}}\n" + if indent_stack.pop().ifdef: + ret += cutil.ifdef_pop(ifdef_lvl()) + + incr_flush() + if typ.typname == "stat": # SPECIAL (stat) + ret += "\tif (ret_net_size)\n" + ret += "\t\t*ret_net_size = net_offset;\n" + ret += "\treturn (ssize_t)host_size;\n" + ret += "}\n" + ret += cutil.ifdef_pop(0) + return ret diff --git a/lib9p/protogen/cutil.py b/lib9p/protogen/cutil.py new file mode 100644 index 0000000..8df6db9 --- /dev/null +++ b/lib9p/protogen/cutil.py @@ -0,0 +1,84 @@ +# lib9p/protogen/cutil.py - Utilities for generating C code +# +# Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com> +# SPDX-License-Identifier: AGPL-3.0-or-later + +# pylint: disable=unused-variable +__all__ = [ + "UINT32_MAX", + "UINT64_MAX", + "macro", + "ifdef_init", + "ifdef_push", + "ifdef_pop", + "ifdef_leaf_is_noop", +] + +UINT32_MAX = (1 << 32) - 1 +UINT64_MAX = (1 << 64) - 1 + + +def tab_ljust(s: str, width: int) -> str: + cur = len(s.expandtabs(tabsize=8)) + if cur >= width: + return s + return s + " " * (width - cur) + + +def macro(full: str) -> str: + full = full.rstrip() + assert "\n" in full + lines = [l.rstrip() for l in full.split("\n")] + width = max(len(l.expandtabs(tabsize=8)) for l in lines[:-1]) + lines = [tab_ljust(l, width) for l in lines] + return " \\\n".join(lines).rstrip() + "\n" + + +_ifdef_stack: list[str | None] = [] + + +def ifdef_init() -> None: + global _ifdef_stack + _ifdef_stack = [] + + +def ifdef_push(n: int, _newval: str) -> str: + # Grow the stack as needed + while len(_ifdef_stack) < n: + _ifdef_stack.append(None) + + # Set some variables + parentval: str | None = None + for x in _ifdef_stack[:-1]: + if x is not None: + parentval = x + oldval = _ifdef_stack[-1] + newval: str | None = _newval + if newval == parentval: + newval = None + + # Put newval on the stack. + _ifdef_stack[-1] = newval + + # Build output. + ret = "" + if newval != oldval: + if oldval is not None: + ret += f"#endif /* {oldval} */\n" + if newval is not None: + ret += f"#if {newval}\n" + return ret + + +def ifdef_pop(n: int) -> str: + global _ifdef_stack + ret = "" + while len(_ifdef_stack) > n: + if _ifdef_stack[-1] is not None: + ret += f"#endif /* {_ifdef_stack[-1]} */\n" + _ifdef_stack = _ifdef_stack[:-1] + return ret + + +def ifdef_leaf_is_noop() -> bool: + return not _ifdef_stack[-1] diff --git a/lib9p/protogen/h.py b/lib9p/protogen/h.py new file mode 100644 index 0000000..8f7fba2 --- /dev/null +++ b/lib9p/protogen/h.py @@ -0,0 +1,535 @@ +# lib9p/protogen/h.py - Generate 9p.generated.h +# +# Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com> +# SPDX-License-Identifier: AGPL-3.0-or-later + +import sys +import typing + +import idl + +from . import c9util, cutil, idlutil + +# This strives to be "general-purpose" in that it just acts on the +# *.9p inputs; but (unfortunately?) there are a few special-cases in +# this script, marked with "SPECIAL". + +# pylint: disable=unused-variable +__all__ = ["gen_h"] + +# get_buffer_size() ############################################################ + + +class BufferSize(typing.NamedTuple): + min_size: int # really just here to sanity-check against typ.min_size(version) + exp_size: int # "expected" or max-reasonable size + max_size: int # really just here to sanity-check against typ.max_size(version) + max_copy: int + max_copy_extra: str + max_iov: int + max_iov_extra: str + + +class TmpBufferSize: + min_size: int + exp_size: int + max_size: int + max_copy: int + max_copy_extra: str + max_iov: int + max_iov_extra: str + + tmp_starts_with_copy: bool + tmp_ends_with_copy: bool + + def __init__(self) -> None: + self.min_size = 0 + self.exp_size = 0 + self.max_size = 0 + self.max_copy = 0 + self.max_copy_extra = "" + self.max_iov = 0 + self.max_iov_extra = "" + self.tmp_starts_with_copy = False + self.tmp_ends_with_copy = False + + +def _get_buffer_size(typ: idl.Type, version: str) -> TmpBufferSize: + assert isinstance(typ, idl.Primitive) or (version in typ.in_versions) + + ret = TmpBufferSize() + + if not isinstance(typ, idl.Struct): + assert typ.static_size + ret.min_size = typ.static_size + ret.exp_size = typ.static_size + ret.max_size = typ.static_size + ret.max_copy = typ.static_size + ret.max_iov = 1 + ret.tmp_starts_with_copy = True + ret.tmp_ends_with_copy = True + return ret + + def handle(path: idlutil.Path) -> tuple[idlutil.WalkCmd, None]: + nonlocal ret + if path.elems: + child = path.elems[-1] + if version not in child.in_versions: + return idlutil.WalkCmd.DONT_RECURSE, None + if child.cnt: + if child.typ.static_size == 1: # SPECIAL (zerocopy) + ret.max_iov += 1 + # HEURISTIC: 27 for strings (max-strlen from 9P1), 8KiB for other data + ret.exp_size += 27 if child.membname == "utf8" else 8192 + ret.max_size += child.max_cnt + ret.tmp_ends_with_copy = False + return idlutil.WalkCmd.DONT_RECURSE, None + sub = _get_buffer_size(child.typ, version) + ret.exp_size += sub.exp_size * 16 # HEURISTIC: MAXWELEM + ret.max_size += sub.max_size * child.max_cnt + if child.membname == "wname" and path.root.typname in ( + "Tsread", + "Tswrite", + ): # SPECIAL (9P2000.e) + assert ret.tmp_ends_with_copy + assert sub.tmp_starts_with_copy + assert not sub.tmp_ends_with_copy + ret.max_copy_extra = ( + f" + (CONFIG_9P_MAX_9P2000_e_WELEM * {sub.max_copy})" + ) + ret.max_iov_extra = ( + f" + (CONFIG_9P_MAX_9P2000_e_WELEM * {sub.max_iov})" + ) + ret.max_iov -= 1 + else: + ret.max_copy += sub.max_copy * child.max_cnt + if sub.max_iov == 1 and sub.tmp_starts_with_copy: # is purely copy + ret.max_iov += 1 + else: # contains zero-copy segments + ret.max_iov += sub.max_iov * child.max_cnt + if ret.tmp_ends_with_copy and sub.tmp_starts_with_copy: + # we can merge this one + ret.max_iov -= 1 + if ( + sub.tmp_ends_with_copy + and sub.tmp_starts_with_copy + and sub.max_iov > 1 + ): + # we can merge these + ret.max_iov -= child.max_cnt - 1 + ret.tmp_ends_with_copy = sub.tmp_ends_with_copy + return idlutil.WalkCmd.DONT_RECURSE, None + if not isinstance(child.typ, idl.Struct): + assert child.typ.static_size + if not ret.tmp_ends_with_copy: + if ret.max_size == 0: + ret.tmp_starts_with_copy = True + ret.max_iov += 1 + ret.tmp_ends_with_copy = True + ret.min_size += child.typ.static_size + ret.exp_size += child.typ.static_size + ret.max_size += child.typ.static_size + ret.max_copy += child.typ.static_size + return idlutil.WalkCmd.KEEP_GOING, None + + idlutil.walk(typ, handle) + assert ret.min_size == typ.min_size(version) + assert ret.max_size == typ.max_size(version) + return ret + + +def get_buffer_size(typ: idl.Type, version: str) -> BufferSize: + tmp = _get_buffer_size(typ, version) + return BufferSize( + min_size=tmp.min_size, + exp_size=tmp.exp_size, + max_size=tmp.max_size, + max_copy=tmp.max_copy, + max_copy_extra=tmp.max_copy_extra, + max_iov=tmp.max_iov, + max_iov_extra=tmp.max_iov_extra, + ) + + +# Generate .h ################################################################## + + +def gen_h(versions: set[str], typs: list[idl.UserType]) -> str: + cutil.ifdef_init() + + ret = f"""/* Generated by `{' '.join(sys.argv)}`. DO NOT EDIT! */ + +#ifndef _LIB9P_9P_H_ +\t#error Do not include <lib9p/9p.generated.h> directly; include <lib9p/9p.h> instead +#endif + +#include <stdint.h> /* for uint{{n}}_t types */ + +#include <libfmt/fmt.h> /* for fmt_formatter */ +#include <libhw/generic/io.h> /* for struct iovec */ +""" + + id2typ: dict[int, idl.Message] = {} + for msg in [msg for msg in typs if isinstance(msg, idl.Message)]: + id2typ[msg.msgid] = msg + + ret += """ +/* config *********************************************************************/ + +#include "config.h" +""" + for ver in sorted(versions): + ret += "\n" + ret += f"#ifndef {c9util.ver_ifdef({ver})}\n" + ret += f"\t#error config.h must define {c9util.ver_ifdef({ver})}\n" + if ver == "9P2000.e": # SPECIAL (9P2000.e) + ret += "#else\n" + ret += f"\t#if {c9util.ver_ifdef({ver})}\n" + ret += "\t\t#ifndef CONFIG_9P_MAX_9P2000_e_WELEM\n" + ret += f"\t\t\t#error if {c9util.ver_ifdef({ver})} then config.h must define CONFIG_9P_MAX_9P2000_e_WELEM\n" + ret += "\t\t#endif\n" + ret += "\t\tstatic_assert(CONFIG_9P_MAX_9P2000_e_WELEM > 0);\n" + ret += "\t#endif\n" + ret += "#endif\n" + + ret += f""" +/* enum version ***************************************************************/ + +enum {c9util.ident('version')} {{ +""" + fullversions = ["unknown = 0", *sorted(versions)] + verwidth = max(len(v) for v in fullversions) + for ver in fullversions: + if ver in versions: + ret += cutil.ifdef_push(1, c9util.ver_ifdef({ver})) + ret += f"\t{c9util.ver_enum(ver)}," + ret += (" " * (verwidth - len(ver))) + ' /* "' + ver.split()[0] + '" */\n' + ret += cutil.ifdef_pop(0) + ret += f"\t{c9util.ver_enum('NUM')},\n" + ret += "};\n" + ret += f"LO_IMPLEMENTATION_H(fmt_formatter, enum {c9util.ident('version')}, {c9util.ident('version')});\n" + + ret += """ +/* enum msg_type **************************************************************/ + +""" + ret += f"enum {c9util.ident('msg_type')} {{ /* uint8_t */\n" + namewidth = max(len(msg.typname) for msg in typs if isinstance(msg, idl.Message)) + for n in range(0x100): + if n not in id2typ: + continue + msg = id2typ[n] + ret += cutil.ifdef_push(1, c9util.ver_ifdef(msg.in_versions)) + ret += f"\t{c9util.Ident(f'TYP_{msg.typname:<{namewidth}}')} = {msg.msgid},\n" + ret += cutil.ifdef_pop(0) + ret += "};\n" + ret += f"LO_IMPLEMENTATION_H(fmt_formatter, enum {c9util.ident('msg_type')}, {c9util.ident('msg_type')});\n" + + ret += """ +/* payload types **************************************************************/ +""" + + def per_version_comment( + typ: idl.UserType, fn: typing.Callable[[idl.UserType, str], str] + ) -> str: + lines: dict[str, str] = {} + for version in sorted(typ.in_versions): + lines[version] = fn(typ, version) + if len(set(lines.values())) == 1: + for _, line in lines.items(): + return f"/* {line} */\n" + assert False + else: + ret = "" + v_width = max(len(c9util.ver_enum(v)) for v in typ.in_versions) + for version, line in lines.items(): + ret += f"/* {c9util.ver_enum(version):<{v_width}}: {line} */\n" + return ret + + for typ in idlutil.topo_sorted(typs): + ret += "\n" + ret += cutil.ifdef_push(1, c9util.ver_ifdef(typ.in_versions)) + + def sum_size(typ: idl.UserType, version: str) -> str: + sz = get_buffer_size(typ, version) + assert ( + sz.min_size <= sz.exp_size + and sz.exp_size <= sz.max_size + and sz.max_size < cutil.UINT64_MAX + ) + ret = "" + if sz.min_size == sz.max_size: + ret += f"size = {sz.min_size:,}" + else: + ret += f"min_size = {sz.min_size:,} ; exp_size = {sz.exp_size:,} ; max_size = {sz.max_size:,}" + if sz.max_size > cutil.UINT32_MAX: + ret += " (warning: >UINT32_MAX)" + ret += f" ; max_iov = {sz.max_iov:,}{sz.max_iov_extra} ; max_copy = {sz.max_copy:,}{sz.max_copy_extra}" + return ret + + ret += per_version_comment(typ, sum_size) + + match typ: + case idl.Number(): + ret += gen_number(typ) + case idl.Bitfield(): + ret += gen_bitfield(typ) + case idl.Struct(): # and idl.Message(): + ret += gen_struct(typ) + ret += cutil.ifdef_pop(0) + + ret += """ +/* containers *****************************************************************/ +""" + ret += "\n" + ret += f"#define {c9util.IDENT('_MAX')}(a, b) ((a) > (b)) ? (a) : (b)\n" + + tmsg_max_iov: dict[str, int] = {} + tmsg_max_copy: dict[str, int] = {} + rmsg_max_iov: dict[str, int] = {} + rmsg_max_copy: dict[str, int] = {} + for typ in typs: + if not isinstance(typ, idl.Message): + continue + if typ.typname in ("Tsread", "Tswrite"): # SPECIAL (9P2000.e) + continue + max_iov = tmsg_max_iov if typ.msgid % 2 == 0 else rmsg_max_iov + max_copy = tmsg_max_copy if typ.msgid % 2 == 0 else rmsg_max_copy + for version in typ.in_versions: + if version not in max_iov: + max_iov[version] = 0 + max_copy[version] = 0 + sz = get_buffer_size(typ, version) + if sz.max_iov > max_iov[version]: + max_iov[version] = sz.max_iov + if sz.max_copy > max_copy[version]: + max_copy[version] = sz.max_copy + + for name, table in [ + ("tmsg_max_iov", tmsg_max_iov), + ("tmsg_max_copy", tmsg_max_copy), + ("rmsg_max_iov", rmsg_max_iov), + ("rmsg_max_copy", rmsg_max_copy), + ]: + inv: dict[int, set[str]] = {} + for version, maxval in table.items(): + if maxval not in inv: + inv[maxval] = set() + inv[maxval].add(version) + + ret += "\n" + directive = "if" + seen_e = False # SPECIAL (9P2000.e) + for maxval in sorted(inv, reverse=True): + ret += f"#{directive} {c9util.ver_ifdef(inv[maxval])}\n" + indent = 1 + if name.startswith("tmsg") and not seen_e: # SPECIAL (9P2000.e) + typ = next(typ for typ in typs if typ.typname == "Tswrite") + sz = get_buffer_size(typ, "9P2000.e") + match name: + case "tmsg_max_iov": + maxexpr = f"{sz.max_iov}{sz.max_iov_extra}" + case "tmsg_max_copy": + maxexpr = f"{sz.max_copy}{sz.max_copy_extra}" + case _: + assert False + ret += f"\t#if {c9util.ver_ifdef({"9P2000.e"})}\n" + ret += f"\t\t#define {c9util.IDENT(name)} {c9util.IDENT('_MAX')}({maxval}, {maxexpr})\n" + ret += "\t#else\n" + indent += 1 + ret += f"{'\t'*indent}#define {c9util.IDENT(name)} {maxval}\n" + if name.startswith("tmsg") and not seen_e: # SPECIAL (9P2000.e) + ret += "\t#endif\n" + if "9P2000.e" in inv[maxval]: + seen_e = True + directive = "elif" + ret += "#endif\n" + + ret += "\n" + ret += f"struct {c9util.ident('Tmsg_send_buf')} {{\n" + ret += "\tsize_t iov_cnt;\n" + ret += f"\tstruct iovec iov[{c9util.IDENT('TMSG_MAX_IOV')}];\n" + ret += f"\tuint8_t copied[{c9util.IDENT('TMSG_MAX_COPY')}];\n" + ret += "};\n" + + ret += "\n" + ret += f"struct {c9util.ident('Rmsg_send_buf')} {{\n" + ret += "\tsize_t iov_cnt;\n" + ret += f"\tstruct iovec iov[{c9util.IDENT('RMSG_MAX_IOV')}];\n" + ret += f"\tuint8_t copied[{c9util.IDENT('RMSG_MAX_COPY')}];\n" + ret += "};\n" + + return ret + + +def gen_number(typ: idl.Number) -> str: + ret = f"typedef {c9util.typename(typ.prim)} {c9util.typename(typ)};\n" + ret += f"LO_IMPLEMENTATION_H(fmt_formatter, {c9util.typename(typ)}, {c9util.basename(typ)});\n" + + def lookup_sym(sym: str) -> str: + assert False + + def cname(base: str) -> str: + prefix = f"{typ.typname}_".upper() + return c9util.Ident(c9util.add_prefix(prefix, base)) + + namewidth = max(len(cname(name)) for name in typ.vals) + for name, val in typ.vals.items(): + c_name = cname(name) + c_val = c9util.idl_expr(val, lookup_sym) + ret += f"#define {c_name:<{namewidth}} (({c9util.typename(typ)})({c_val}))\n" + return ret + + +def gen_bitfield(typ: idl.Bitfield) -> str: + ret = f"typedef {c9util.typename(typ.prim)} {c9util.typename(typ)};\n" + ret += f"LO_IMPLEMENTATION_H(fmt_formatter, {c9util.typename(typ)}, {c9util.basename(typ)});\n" + + def lookup_sym(sym: str) -> str: + assert False + + # There are 4 parts here: bits, aliases, masks, and numbers. + + # 1. bits + + def bitname(bit: idl.Bit) -> str: + prefix = f"{typ.typname}_".upper() + base = bit.bitname + match bit: + case idl.Bit(cat="RESERVED"): + base = "_RESERVED_" + base + case idl.Bit(cat=idl.BitNum()): + base += "_*" + case idl.Bit(cat="UNUSED"): + base = f"_UNUSED_{bit.num}" + return c9util.Ident(c9util.add_prefix(prefix, base)) + + namewidth = max(len(bitname(bit)) for bit in typ.bits) + + ret += "/* bits */\n" + for bit in reversed(typ.bits): + vers = bit.in_versions + if bit.cat == "UNUSED": + vers = typ.in_versions + ret += cutil.ifdef_push(2, c9util.ver_ifdef(vers)) + + # It is important all of the `beg` strings have + # the same length. + end = "" + match bit.cat: + case "USED" | "RESERVED" | "UNUSED": + if cutil.ifdef_leaf_is_noop(): + beg = "#define " + else: + beg = "# define" + case idl.BitNum(): + beg = "/* number" + end = " */" + + c_name = bitname(bit) + c_val = f"UINT{typ.static_size*8}_C(1)<<{bit.num}" + ret += ( + f"{beg} {c_name:<{namewidth}} (({c9util.typename(typ)})({c_val})){end}\n" + ) + ret += cutil.ifdef_pop(1) + + # 2. aliases + if typ.aliases: + + def aliasname(alias: idl.BitAlias) -> str: + prefix = f"{typ.typname}_".upper() + base = alias.bitname + return c9util.Ident(c9util.add_prefix(prefix, base)) + + ret += "/* aliases */\n" + for alias in typ.aliases.values(): + ret += cutil.ifdef_push(2, c9util.ver_ifdef(alias.in_versions)) + + end = "" + if cutil.ifdef_leaf_is_noop(): + beg = "#define " + else: + beg = "# define" + + c_name = aliasname(alias) + c_val = c9util.idl_expr(alias.val, lookup_sym) + ret += f"{beg} {c_name:<{namewidth}} (({c9util.typename(typ)})({c_val})){end}\n" + + ret += cutil.ifdef_pop(1) + + # 3. masks + if typ.masks: + + def maskname(mask: idl.BitAlias) -> str: + prefix = f"{typ.typname}_".upper() + base = mask.bitname + return c9util.Ident(c9util.add_prefix(prefix, base) + "_MASK") + + ret += "/* masks */\n" + for mask in typ.masks.values(): + ret += cutil.ifdef_push(2, c9util.ver_ifdef(mask.in_versions)) + + end = "" + if cutil.ifdef_leaf_is_noop(): + beg = "#define " + else: + beg = "# define" + + c_name = maskname(mask) + c_val = c9util.idl_expr(mask.val, lookup_sym, bitwidth=typ.static_size * 8) + ret += f"{beg} {c_name:<{namewidth}} (({c9util.typename(typ)})({c_val})){end}\n" + + ret += cutil.ifdef_pop(1) + + # 4. numbers + def numname(num: idl.BitNum, base: str) -> str: + prefix = f"{typ.typname}_{num.numname}_".upper() + return c9util.Ident(c9util.add_prefix(prefix, base)) + + for num in typ.nums.values(): + namewidth = max( + len(numname(num, base)) + for base in [ + *[alias.bitname for alias in num.vals.values()], + "MASK", + ] + ) + ret += f"/* number: {num.numname} */\n" + for alias in num.vals.values(): + ret += cutil.ifdef_push(2, c9util.ver_ifdef(alias.in_versions)) + + end = "" + if cutil.ifdef_leaf_is_noop(): + beg = "#define " + else: + beg = "# define" + + c_name = numname(num, alias.bitname) + c_val = c9util.idl_expr(alias.val, lookup_sym) + ret += f"{beg} {c_name:<{namewidth}} (({c9util.typename(typ)})({c_val})){end}\n" + ret += cutil.ifdef_pop(1) + c_name = numname(num, "MASK") + c_val = f"{num.mask:#0{typ.static_size*8}b}" + ret += ( + f"{beg} {c_name:<{namewidth}} (({c9util.typename(typ)})({c_val})){end}\n" + ) + + return ret + + +def gen_struct(typ: idl.Struct) -> str: # and idl.Message + ret = c9util.typename(typ) + " {" + if typ.members: + ret += "\n" + + typewidth = max(len(c9util.typename(m.typ, m)) for m in typ.members) + + for member in typ.members: + if member.val: + continue + ret += cutil.ifdef_push(2, c9util.ver_ifdef(member.in_versions)) + ret += f"\t{c9util.typename(member.typ, member):<{typewidth}} {'*' if member.cnt else ' '}{member.membname};\n" + ret += cutil.ifdef_pop(1) + ret += "};\n" + ret += f"LO_IMPLEMENTATION_H(fmt_formatter, {c9util.typename(typ)}, {c9util.basename(typ)});\n" + return ret diff --git a/lib9p/protogen/idlutil.py b/lib9p/protogen/idlutil.py new file mode 100644 index 0000000..dc4d012 --- /dev/null +++ b/lib9p/protogen/idlutil.py @@ -0,0 +1,112 @@ +# lib9p/protogen/idlutil.py - Utilities for working with the 9P idl package +# +# Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com> +# SPDX-License-Identifier: AGPL-3.0-or-later + +import enum +import graphlib +import typing + +import idl + +# pylint: disable=unused-variable +__all__ = [ + "topo_sorted", + "Path", + "WalkCmd", + "WalkHandler", + "walk", +] + +# topo_sorted() ################################################################ + + +def topo_sorted(typs: list[idl.UserType]) -> typing.Iterable[idl.UserType]: + ts: graphlib.TopologicalSorter[idl.UserType] = graphlib.TopologicalSorter() + for typ in typs: + match typ: + case idl.Number(): + ts.add(typ) + case idl.Bitfield(): + ts.add(typ) + case idl.Struct(): # and idl.Message(): + deps = [ + member.typ + for member in typ.members + if not isinstance(member.typ, idl.Primitive) + ] + ts.add(typ, *deps) + return ts.static_order() + + +# walk() ####################################################################### + + +class Path: + root: idl.Type + elems: list[idl.StructMember] + + def __init__( + self, root: idl.Type, elems: list[idl.StructMember] | None = None + ) -> None: + self.root = root + self.elems = elems if elems is not None else [] + + def add(self, elem: idl.StructMember) -> "Path": + return Path(self.root, self.elems + [elem]) + + def parent(self) -> "Path": + return Path(self.root, self.elems[:-1]) + + def c_str(self, base: str, loopdepth: int = 0) -> str: + ret = base + for i, elem in enumerate(self.elems): + if i > 0: + ret += "." + ret += elem.membname + if elem.cnt: + ret += f"[{chr(ord('i')+loopdepth)}]" + loopdepth += 1 + return ret + + def __str__(self) -> str: + return self.c_str(self.root.typname + "->") + + +class WalkCmd(enum.Enum): + KEEP_GOING = 1 + DONT_RECURSE = 2 + ABORT = 3 + + +type WalkHandler = typing.Callable[ + [Path], tuple[WalkCmd, typing.Callable[[], None] | None] +] + + +def _walk(path: Path, handle: WalkHandler) -> WalkCmd: + typ = path.elems[-1].typ if path.elems else path.root + + ret, atexit = handle(path) + + if isinstance(typ, idl.Struct): + match ret: + case WalkCmd.KEEP_GOING: + for member in typ.members: + if _walk(path.add(member), handle) == WalkCmd.ABORT: + ret = WalkCmd.ABORT + break + case WalkCmd.DONT_RECURSE: + ret = WalkCmd.KEEP_GOING + case WalkCmd.ABORT: + ret = WalkCmd.ABORT + case _: + assert False, f"invalid cmd: {ret}" + + if atexit: + atexit() + return ret + + +def walk(typ: idl.Type, handle: WalkHandler) -> None: + _walk(Path(typ), handle) diff --git a/lib9p/srv.c b/lib9p/srv.c index 9192794..ea8b932 100644 --- a/lib9p/srv.c +++ b/lib9p/srv.c @@ -1,24 +1,54 @@ /* lib9p/srv.c - 9P server * - * 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 */ -#include <assert.h> #include <alloca.h> #include <inttypes.h> /* for PRI* */ -#include <stdio.h> /* for fprintf(), stderr */ -#include <string.h> /* for strerror() */ +#include <limits.h> /* for SSIZE_MAX, not set by newlib */ +#include <stddef.h> /* for size_t */ +#include <stdlib.h> /* for malloc() */ +#include <string.h> /* for memcpy() */ +#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/vcall.h> +#include <libmisc/assert.h> +#include <libmisc/endian.h> +#include <libhw/generic/net.h> + +#define LOG_NAME 9P_SRV +#include <libmisc/log.h> #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 +#endif +#ifndef CONFIG_9P_SRV_MAX_REQS + #error config.h must define CONFIG_9P_SRV_MAX_REQS +#endif +#ifndef CONFIG_9P_SRV_MAX_DEPTH + /* 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 ********************************************************************/ @@ -27,39 +57,65 @@ bool lib9p_srv_flush_requested(struct lib9p_srv_ctx *ctx) { return _lib9p_srv_flushch_can_send(&ctx->_flushch); } -int lib9p_srv_acknowledge_flush(struct lib9p_srv_ctx *ctx) { +void lib9p_srv_acknowledge_flush(struct lib9p_srv_ctx *ctx) { assert(ctx); assert(_lib9p_srv_flushch_can_send(&ctx->_flushch)); lib9p_error(&ctx->basectx, LINUX_ECANCELED, "request canceled by flush"); _lib9p_srv_flushch_send(&ctx->_flushch, true); - return -1; } /* structs ********************************************************************/ +typedef typeof( ((struct lib9p_qid){}).path ) srv_path_t; + +struct srv_pathinfo { + lo_interface lib9p_srv_file file; + srv_path_t parent_dir; + + /* References from other srv_pathinfos (via .parent_dir) or + * from FIDs. */ + unsigned int gc_refcount; + /* References from fids with FIDFLAG_OPEN_R/FIDFLAG_OPEN_W. */ + unsigned int io_refcount; +}; + +#define NAME pathmap +#define KEY_T srv_path_t +#define VAL_T struct srv_pathinfo +/* ( naive ) + ( working space for walk() ) */ +#define CAP ( (CONFIG_9P_SRV_MAX_FIDS*CONFIG_9P_SRV_MAX_DEPTH) + (CONFIG_9P_SRV_MAX_REQS*2) ) +#include "map.h" + #define FIDFLAG_OPEN_R (1<<0) #define FIDFLAG_OPEN_W (1<<1) #define FIDFLAG_RCLOSE (1<<2) -#define FIDFLAG_ISDIR (1<<3) #define FIDFLAG_OPEN (FIDFLAG_OPEN_R|FIDFLAG_OPEN_W) struct _srv_fidinfo { - implements_lib9p_srv_file *file; - uint8_t flags; - size_t dir_idx; - uint32_t dir_off; + srv_path_t path; + uint8_t flags; + union { + struct { + lo_interface lib9p_srv_fio io; + } file; + struct { + lo_interface lib9p_srv_dio io; + size_t idx; + uint64_t off; + } dir; + }; }; #define NAME fidmap -#define KEY_T uint32_t +#define KEY_T lib9p_fid_t #define VAL_T struct _srv_fidinfo -#define CAP CONFIG_9P_MAX_FIDS +#define CAP CONFIG_9P_SRV_MAX_FIDS #include "map.h" #define NAME reqmap -#define KEY_T uint32_t +#define KEY_T lib9p_tag_t #define VAL_T struct _lib9p_srv_req * -#define CAP CONFIG_9P_MAX_REQS +#define CAP CONFIG_9P_SRV_MAX_REQS #include "map.h" /* The hierarchy of concepts is: @@ -73,7 +129,7 @@ struct _srv_fidinfo { struct _srv_conn { /* immutable */ struct lib9p_srv *parent_srv; - implements_net_stream_conn *fd; + lo_interface net_stream_conn fd; cid_t reader; /* the lib9p_srv_read_cr() coroutine */ /* mutable */ cr_mutex_t writelock; @@ -88,53 +144,49 @@ struct _srv_sess { /* mutable */ bool initialized; bool closing; - struct reqmap reqs; - struct fidmap fids; + struct pathmap paths; /* srv_path_t => lib9p_srv_file + metadata */ + struct fidmap fids; /* lib9p_fid_t => lib9p_srv_{fio,dio} + metadata */ + struct reqmap reqs; /* lib9p_tag_t => *_lib9p_srv_req */ }; struct _lib9p_srv_req { /* immutable */ struct _srv_sess *parent_sess; uint16_t tag; + uint8_t *net_bytes; /* mutable */ - uint8_t *net_bytes; /* CONFIG_9P_MAX_MSG_SIZE-sized */ struct lib9p_srv_ctx ctx; }; /* base utilities *************************************************************/ -#define nonrespond_errorf(fmt, ...) \ - fprintf(stderr, "error: " fmt "\n" __VA_OPT__(,) __VA_ARGS__) - -static uint32_t rerror_overhead_for_version(enum lib9p_version version, - uint8_t *scratch) { - struct lib9p_ctx empty_ctx = { - .version = version, - .max_msg_size = CONFIG_9P_MAX_MSG_SIZE, - }; - struct lib9p_msg_Rerror empty_error = { 0 }; - bool e; - - e = lib9p_marshal(&empty_ctx, LIB9P_TYP_Rerror, - &empty_error, /* host_body */ - scratch); /* net_bytes */ - assert(!e); - - uint32_t min_msg_size = decode_u32le(scratch); +static void msglog(struct _lib9p_srv_req *req, enum lib9p_msg_type typ, void *hostmsg) { + struct lib9p_srv *srv = req->parent_sess->parent_conn->parent_srv; + if (srv->msglog) { + srv->msglog(&req->ctx, typ, hostmsg); + return; + } + /* It sucks that %v trips -Wformat and -Wformat-extra-args + * https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47781 */ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat" +#pragma GCC diagnostic ignored "-Wformat-extra-args" + infof("%c %v", typ % 2 ? '<' : '>', lo_box_lib9p_msg_as_fmt_formatter(&req->ctx.basectx, typ, hostmsg)); +#pragma GCC diagnostic pop +} - /* Assert that min_msg_size + biggest_possible_MAX_ERR_SIZE - * won't overflow uint32... because using - * __builtin_add_overflow in respond_error() would be a bit - * much. */ - assert(min_msg_size < (UINT32_MAX - UINT16_MAX)); - /* Assert that min_msg_size doesn't overflow MAX_MSG_SIZE. */ - assert(CONFIG_9P_MAX_MSG_SIZE >= min_msg_size); +#define nonrespond_errorf errorf - return min_msg_size; +static ssize_t write_Rmsg(struct _lib9p_srv_req *req, struct lib9p_Rmsg_send_buf *resp) { + ssize_t r; + cr_mutex_lock(&req->parent_sess->parent_conn->writelock); + r = io_writev(req->parent_sess->parent_conn->fd, resp->iov, resp->iov_cnt); + cr_mutex_unlock(&req->parent_sess->parent_conn->writelock); + return r; } static void respond_error(struct _lib9p_srv_req *req) { -#ifdef CONFIG_9P_ENABLE_9P2000_u +#if CONFIG_9P_ENABLE_9P2000_u assert(req->ctx.basectx.err_num); #endif assert(req->ctx.basectx.err_msg[0]); @@ -142,46 +194,42 @@ static void respond_error(struct _lib9p_srv_req *req) { ssize_t r; struct lib9p_msg_Rerror host = { .tag = req->tag, - .ename = { - .len = strnlen(req->ctx.basectx.err_msg, - CONFIG_9P_MAX_ERR_SIZE), - .utf8 = req->ctx.basectx.err_msg, - }, -#ifdef CONFIG_9P_ENABLE_9P2000_u - .errno = req->ctx.basectx.err_num, + .errstr = lib9p_strn(req->ctx.basectx.err_msg, + CONFIG_9P_MAX_ERR_SIZE), +#if CONFIG_9P_ENABLE_9P2000_u + .errnum = req->ctx.basectx.err_num, #endif }; struct _srv_sess *sess = req->parent_sess; /* Truncate the error-string if necessary to avoid needing to - * return LINUX_ERANGE. The assert() in - * rerror_overhead_for_version() has checked that this - * addition doesn't overflow. */ - if (((uint32_t)host.ename.len) + sess->rerror_overhead > sess->max_msg_size) - host.ename.len = sess->max_msg_size - sess->rerror_overhead; - - lib9p_marshal(&req->ctx.basectx, LIB9P_TYP_Rerror, - &host, req->net_bytes); - - cr_mutex_lock(&sess->parent_conn->writelock); - r = VCALL(sess->parent_conn->fd, write, - req->net_bytes, decode_u32le(req->net_bytes)); - cr_mutex_unlock(&sess->parent_conn->writelock); + * return LINUX_ERANGE. */ + if (((uint32_t)host.errstr.len) + sess->rerror_overhead > sess->max_msg_size) + host.errstr.len = sess->max_msg_size - sess->rerror_overhead; + + struct lib9p_Rmsg_send_buf net; + + lib9p_Rmsg_marshal(&req->ctx.basectx, + LIB9P_TYP_Rerror, &host, + &net); + + msglog(req, LIB9P_TYP_Rerror, &host); + r = write_Rmsg(req, &net); if (r < 0) - nonrespond_errorf("write: %s", strerror(-r)); + nonrespond_errorf("write: %s", net_strerror(-r)); } /* read coroutine *************************************************************/ -static bool read_at_least(implements_net_stream_conn *fd, uint8_t *buf, size_t goal, size_t *done) { +static bool read_exactly(lo_interface net_stream_conn fd, uint8_t *buf, size_t goal, size_t *done) { assert(buf); assert(goal); assert(done); while (*done < goal) { - ssize_t r = VCALL(fd, read, &buf[*done], CONFIG_9P_MAX_MSG_SIZE - *done); + ssize_t r = io_read(fd, &buf[*done], goal - *done); if (r < 0) { - nonrespond_errorf("read: %s", strerror(-r)); + nonrespond_errorf("read: %s", net_strerror(-r)); return true; } else if (r == 0) { if (*done != 0) @@ -195,30 +243,34 @@ static bool read_at_least(implements_net_stream_conn *fd, uint8_t *buf, size_t g static void handle_message(struct _lib9p_srv_req *ctx); -__attribute__ ((noreturn)) void lib9p_srv_read_cr(struct lib9p_srv *srv, implements_net_stream_listener *listener) { - uint8_t buf[CONFIG_9P_MAX_MSG_SIZE]; - +[[noreturn]] void lib9p_srv_read_cr(struct lib9p_srv *srv, lo_interface net_stream_listener listener) { assert(srv); assert(srv->rootdir); - assert(listener); + assert(!LO_IS_NULL(listener)); - uint32_t initial_rerror_overhead = rerror_overhead_for_version(0, buf); + srv->readers++; + + uint32_t initial_rerror_overhead = lib9p_version_min_msg_size(LIB9P_VER_unknown); for (;;) { struct _srv_conn conn = { .parent_srv = srv, - .fd = VCALL(listener, accept), + .fd = LO_CALL(listener, accept), .reader = cr_getcid(), }; - if (!conn.fd) { + if (LO_IS_NULL(conn.fd)) { nonrespond_errorf("accept: error"); - continue; + srv->readers--; + if (srv->readers == 0) + while (srv->writers > 0) + _lib9p_srv_reqch_send_req(&srv->_reqch, NULL); + cr_exit(); } 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, }; @@ -226,18 +278,19 @@ __attribute__ ((noreturn)) void lib9p_srv_read_cr(struct lib9p_srv *srv, impleme nextmsg: /* Read the message. */ size_t done = 0; - if (read_at_least(conn.fd, buf, 4, &done)) + uint8_t buf[7]; + if (read_exactly(conn.fd, buf, 4, &done)) goto close; - size_t goal = decode_u32le(buf); + size_t goal = uint32le_decode(buf); if (goal < 7) { nonrespond_errorf("T-message is impossibly small"); goto close; } - if (read_at_least(conn.fd, buf, 7, &done)) + if (read_exactly(conn.fd, buf, 7, &done)) goto close; struct _lib9p_srv_req req = { .parent_sess = &sess, - .tag = decode_u16le(&buf[5]), + .tag = uint16le_decode(&buf[5]), .net_bytes = buf, .ctx = { .basectx = { @@ -255,11 +308,16 @@ __attribute__ ((noreturn)) void lib9p_srv_read_cr(struct lib9p_srv *srv, impleme respond_error(&req); goto nextmsg; } - if (read_at_least(conn.fd, buf, goal, &done)) + req.net_bytes = malloc(goal); + assert(req.net_bytes); + memcpy(req.net_bytes, buf, done); + if (read_exactly(conn.fd, req.net_bytes, goal, &done)) { + free(req.net_bytes); goto close; + } /* Handle the message... */ - if (buf[4] == LIB9P_TYP_Tversion) + if (req.net_bytes[4] == LIB9P_TYP_Tversion) /* ...in this coroutine for Tversion, */ handle_message(&req); else @@ -267,12 +325,14 @@ __attribute__ ((noreturn)) void lib9p_srv_read_cr(struct lib9p_srv *srv, impleme _lib9p_srv_reqch_send_req(&srv->_reqch, &req); } close: - VCALL(conn.fd, close, true, sess.reqs.len == 0); - if (sess.reqs.len) { + if (sess.reqs.len == 0) + io_close(conn.fd); + else { + io_close_read(conn.fd); sess.closing = true; cr_pause_and_yield(); assert(sess.reqs.len == 0); - VCALL(conn.fd, close, true, true); + io_close_write(conn.fd); } } } @@ -280,7 +340,6 @@ __attribute__ ((noreturn)) void lib9p_srv_read_cr(struct lib9p_srv *srv, impleme /* write coroutine ************************************************************/ COROUTINE lib9p_srv_write_cr(void *_srv) { - uint8_t net[CONFIG_9P_MAX_MSG_SIZE]; struct _lib9p_srv_req req; _lib9p_srv_reqch_req_t rpc_handle; @@ -289,14 +348,19 @@ COROUTINE lib9p_srv_write_cr(void *_srv) { assert(srv->rootdir); cr_begin(); + srv->writers++; + for (;;) { /* Receive the request from the reader coroutine. ************/ rpc_handle = _lib9p_srv_reqch_recv_req(&srv->_reqch); - /* Deep-copy the request from the reader coroutine's + if (!rpc_handle.req) { + srv->writers--; + _lib9p_srv_reqch_send_resp(rpc_handle, 0); + cr_exit(); + } + /* Copy the request from the reader coroutine's * stack to our stack. */ req = *rpc_handle.req; - memcpy(net, req.net_bytes, decode_u32le(req.net_bytes)); - req.net_bytes = net; /* Record that we have it. */ reqmap_store(&req.parent_sess->reqs, req.tag, &req); /* Notify the reader coroutine that we're done with @@ -334,7 +398,7 @@ _HANDLER_PROTO(clunk); _HANDLER_PROTO(remove); _HANDLER_PROTO(stat); _HANDLER_PROTO(wstat); -#ifdef CONFIG_9P_ENABLE_9P2000_e +#if CONFIG_9P_ENABLE_9P2000_e _HANDLER_PROTO(session); _HANDLER_PROTO(sread); _HANDLER_PROTO(swrite); @@ -356,7 +420,7 @@ static tmessage_handler tmessage_handlers[0x100] = { [LIB9P_TYP_Tremove] = (tmessage_handler)handle_Tremove, [LIB9P_TYP_Tstat] = (tmessage_handler)handle_Tstat, [LIB9P_TYP_Twstat] = (tmessage_handler)handle_Twstat, -#ifdef CONFIG_9P_ENABLE_9P2000_e +#if CONFIG_9P_ENABLE_9P2000_e [LIB9P_TYP_Tsession] = (tmessage_handler)handle_Tsession, [LIB9P_TYP_Tsread] = (tmessage_handler)handle_Tsread, [LIB9P_TYP_Tswrite] = (tmessage_handler)handle_Tswrite, @@ -364,28 +428,19 @@ static tmessage_handler tmessage_handlers[0x100] = { }; static void handle_message(struct _lib9p_srv_req *ctx) { - uint8_t host_req[CONFIG_9P_MAX_HOSTMSG_SIZE]; - uint8_t host_resp[CONFIG_9P_MAX_HOSTMSG_SIZE]; + uint8_t *host_req = NULL; + uint8_t host_resp[CONFIG_9P_SRV_MAX_HOSTMSG_SIZE]; /* Unmarshal it. */ - enum lib9p_msg_type typ = ctx->net_bytes[4]; - if (typ % 2 != 0) { - lib9p_errorf(&ctx->ctx.basectx, - LINUX_EOPNOTSUPP, "expected a T-message but got an R-message: message_type=%s", - lib9p_msg_type_str(&ctx->ctx.basectx, typ)); - goto write; - } - ssize_t host_size = lib9p_validate(&ctx->ctx.basectx, ctx->net_bytes); + ssize_t host_size = lib9p_Tmsg_validate(&ctx->ctx.basectx, ctx->net_bytes); if (host_size < 0) goto write; - if ((size_t)host_size > sizeof(host_req)) { - lib9p_errorf(&ctx->ctx.basectx, - LINUX_EMSGSIZE, "unmarshalled payload larger than server limit (%zu > %zu)", - host_size, sizeof(host_req)); - goto write; - } - lib9p_unmarshal(&ctx->ctx.basectx, ctx->net_bytes, + host_req = calloc(1, host_size); + assert(host_req); + enum lib9p_msg_type typ; + lib9p_Tmsg_unmarshal(&ctx->ctx.basectx, ctx->net_bytes, &typ, host_req); + msglog(ctx, typ, host_req); /* Handle it. */ tmessage_handlers[typ](ctx, (void *)host_req, (void *)host_resp); @@ -394,15 +449,17 @@ static void handle_message(struct _lib9p_srv_req *ctx) { if (lib9p_ctx_has_error(&ctx->ctx.basectx)) respond_error(ctx); else { - if (lib9p_marshal(&ctx->ctx.basectx, typ+1, host_resp, - ctx->net_bytes)) + struct lib9p_Rmsg_send_buf net_resp; + if (lib9p_Rmsg_marshal(&ctx->ctx.basectx, + typ+1, host_resp, + &net_resp)) goto write; - - cr_mutex_lock(&ctx->parent_sess->parent_conn->writelock); - VCALL(ctx->parent_sess->parent_conn->fd, write, - ctx->net_bytes, decode_u32le(ctx->net_bytes)); - cr_mutex_unlock(&ctx->parent_sess->parent_conn->writelock); + msglog(ctx, typ+1, &host_resp); + write_Rmsg(ctx, &net_resp); } + if (host_req) + free(host_req); + free(ctx->net_bytes); } #define util_handler_common(ctx, req, resp) do { \ @@ -412,7 +469,7 @@ static void handle_message(struct _lib9p_srv_req *ctx) { resp->tag = req->tag; \ } while (0) -static inline bool util_check_perm(struct lib9p_srv_ctx *ctx, struct lib9p_stat *stat, uint8_t action) { +static inline bool srv_util_check_perm(struct _lib9p_srv_req *ctx, struct lib9p_stat *stat, uint8_t action) { assert(ctx); assert(stat); assert(action); @@ -423,17 +480,110 @@ static inline bool util_check_perm(struct lib9p_srv_ctx *ctx, struct lib9p_stat return mode & action; } -static inline bool util_release(struct lib9p_srv_ctx *ctx, implements_lib9p_srv_file *file) { - assert(file); - file->_refcount--; - if (file->_refcount == 0) { - if (file->_parent_dir != file) - util_release(ctx, file->_parent_dir); - VCALL(file, free, ctx); +/** + * Ensures that `file` is saved into the pathmap, and increments the + * gc_refcount by 1 (for presumptive insertion into the fidmap). + * parent_path's gc_refcount is also incremented as appropriate. + * + * Returns a pointer to the stored pathinfo. + */ +static inline struct srv_pathinfo *srv_util_pathsave(struct _lib9p_srv_req *ctx, + lo_interface lib9p_srv_file file, + srv_path_t parent_path) { + assert(ctx); + assert(!LO_IS_NULL(file)); + + struct lib9p_qid qid = LO_CALL(file, qid); + struct srv_pathinfo *pathinfo = pathmap_load(&ctx->parent_sess->paths, qid.path); + if (pathinfo) + assert(LO_EQ(pathinfo->file, file)); + else { + pathinfo = pathmap_store(&ctx->parent_sess->paths, qid.path, + (struct srv_pathinfo){ + .file = file, + .parent_dir = parent_path, + .gc_refcount = 0, + .io_refcount = 0, + }); + assert(pathinfo); + if (parent_path != qid.path) { + struct srv_pathinfo *parent = pathmap_load(&ctx->parent_sess->paths, parent_path); + assert(parent); + parent->gc_refcount++; + } } - return lib9p_ctx_has_error(&ctx->basectx); + pathinfo->gc_refcount++; + return pathinfo; } +/** + * Decrement the path's gc_refcount, and trigger garbage collection as + * appropriate. + */ +static inline void srv_util_pathfree(struct _lib9p_srv_req *ctx, srv_path_t path) { + assert(ctx); + + struct srv_pathinfo *pathinfo = pathmap_load(&ctx->parent_sess->paths, path); + assert(pathinfo); + pathinfo->gc_refcount--; + if (pathinfo->gc_refcount == 0) { + if (pathinfo->parent_dir != path) + srv_util_pathfree(ctx, pathinfo->parent_dir); + LO_CALL(pathinfo->file, free); + pathmap_del(&ctx->parent_sess->paths, path); + } +} + +static inline bool srv_util_pathisdir(struct srv_pathinfo *pathinfo) { + assert(pathinfo); + return LO_CALL(pathinfo->file, qid).type & LIB9P_QT_DIR; +} + +/** + * Store fid as pointing to pathinfo. Assumes that + * pathinfo->gc_refcount has already been incremented; does *not* + * decrement it on failure. + */ +static inline struct _srv_fidinfo *srv_util_fidsave(struct _lib9p_srv_req *ctx, lib9p_fid_t fid, struct srv_pathinfo *pathinfo, bool overwrite) { + assert(ctx); + assert(fid != LIB9P_FID_NOFID); + assert(pathinfo); + + struct lib9p_qid qid = LO_CALL(pathinfo->file, qid); + + struct _srv_fidinfo *fidinfo = fidmap_load(&ctx->parent_sess->fids, fid); + if (fidinfo) { + if (overwrite) { + struct srv_pathinfo *old_pathinfo = pathmap_load(&ctx->parent_sess->paths, fidinfo->path); + assert(old_pathinfo); + if (srv_util_pathisdir(old_pathinfo)) { + if (!LO_IS_NULL(fidinfo->dir.io)) + LO_CALL(fidinfo->dir.io, iofree); + } else { + if (!LO_IS_NULL(fidinfo->file.io)) + LO_CALL(fidinfo->file.io, iofree); + } + srv_util_pathfree(ctx, fidinfo->path); + } else { + lib9p_error(&ctx->ctx.basectx, + LINUX_EBADF, "FID already in use"); + return NULL; + } + } else { + fidinfo = fidmap_store(&ctx->parent_sess->fids, fid, (struct _srv_fidinfo){}); + if (!fidinfo) { + lib9p_error(&ctx->ctx.basectx, + LINUX_EMFILE, "too many open files"); + return NULL; + } + } + *fidinfo = (struct _srv_fidinfo){ + .path = qid.path, + }; + return fidinfo; +} + + static void handle_Tversion(struct _lib9p_srv_req *ctx, struct lib9p_msg_Tversion *req, struct lib9p_msg_Rversion *resp) { @@ -448,33 +598,29 @@ static void handle_Tversion(struct _lib9p_srv_req *ctx, '0' <= req->version.utf8[3] && req->version.utf8[3] <= '9' && '0' <= req->version.utf8[4] && req->version.utf8[4] <= '9' && '0' <= req->version.utf8[5] && req->version.utf8[5] <= '9' && - (req->version.utf8[6] == '\0' || req->version.utf8[6] == '.')) { + (req->version.len == 6 || req->version.utf8[6] == '.')) { version = LIB9P_VER_9P2000; -#ifdef CONFIG_9P_ENABLE_9P2000_u - if (strcmp(&req->version.utf8[6], ".u") == 0) +#if CONFIG_9P_ENABLE_9P2000_u + if (lib9p_str_eq(lib9p_str_sliceleft(req->version, 6), lib9p_str(".u"))) version = LIB9P_VER_9P2000_u; #endif -#ifdef CONFIG_9P_ENABLE_9P2000_e - if (strcmp(&req->version.utf8[6], ".e") == 0) +#if CONFIG_9P_ENABLE_9P2000_e + if (lib9p_str_eq(lib9p_str_sliceleft(req->version, 6), lib9p_str(".e"))) version = LIB9P_VER_9P2000_e; #endif } - uint32_t min_msg_size = rerror_overhead_for_version(version, ctx->net_bytes); + 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")", - version, req->max_msg_size, min_msg_size); + lib9p_version_str(version), req->max_msg_size, min_msg_size); return; } -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wdiscarded-qualifiers" - resp->version.utf8 = lib9p_version_str(version); -#pragma GCC diagnostic pop - resp->version.len = strlen(resp->version.utf8); - resp->max_msg_size = (CONFIG_9P_MAX_MSG_SIZE < req->max_msg_size) - ? CONFIG_9P_MAX_MSG_SIZE + resp->version = lib9p_str((char *)lib9p_version_str(version)); /* cast to discard "const" qualifier */ + 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. */ @@ -483,7 +629,7 @@ static void handle_Tversion(struct _lib9p_srv_req *ctx, * to finish. */ struct cr_select_arg *list = alloca(sizeof(struct cr_select_arg) * ctx->parent_sess->reqs.len); while (ctx->parent_sess->reqs.len) { - uint16_t tag __attribute__((unused)); + uint16_t tag [[gnu::unused]]; struct _lib9p_srv_req **reqpp; size_t i = 0; bool flushed; @@ -496,7 +642,7 @@ static void handle_Tversion(struct _lib9p_srv_req *ctx, if (ctx->parent_sess->fids.len) { /* Close all FIDs. */ uint32_t fid; - struct _srv_fidinfo *fidinfo __attribute__((unused)); + struct _srv_fidinfo *fidinfo [[gnu::unused]]; MAP_FOREACH(&ctx->parent_sess->fids, fid, fidinfo) { handle_Tclunk(ctx, &(struct lib9p_msg_Tclunk){.fid = fid}, @@ -515,8 +661,8 @@ static void handle_Tauth(struct _lib9p_srv_req *ctx, struct lib9p_msg_Rauth *resp) { util_handler_common(ctx, req, resp); - ctx->ctx.uid = req->n_uname; - ctx->ctx.uname = req->uname.utf8; + ctx->ctx.uid = req->n_uid; + ctx->ctx.uname = req->uname; struct lib9p_srv *srv = ctx->parent_sess->parent_conn->parent_srv; if (!srv->auth) { @@ -525,7 +671,7 @@ static void handle_Tauth(struct _lib9p_srv_req *ctx, return; } - srv->auth(&ctx->ctx, req->aname.utf8); + srv->auth(&ctx->ctx, req->aname); lib9p_error(&ctx->ctx.basectx, LINUX_EOPNOTSUPP, "TODO: auth not implemented"); } @@ -535,8 +681,8 @@ static void handle_Tattach(struct _lib9p_srv_req *ctx, struct lib9p_msg_Rattach *resp) { util_handler_common(ctx, req, resp); - ctx->ctx.uid = req->n_uname; - ctx->ctx.uname = req->uname.utf8; + ctx->ctx.uid = req->n_uid; + ctx->ctx.uname = req->uname; struct lib9p_srv *srv = ctx->parent_sess->parent_conn->parent_srv; if (srv->auth) { @@ -548,14 +694,16 @@ static void handle_Tattach(struct _lib9p_srv_req *ctx, else if (fh->type != FH_AUTH) lib9p_error(&ctx->ctx.basectx, LINUX_EACCES, "FID provided as auth-file is not an auth-file"); - else if (strcmp(fh->data.auth.uname, req->uname.utf8) != 0) + else if (!lib9p_str_eq(fh->data.auth.uname, req->uname)) lib9p_errorf(&ctx->ctx.basectx, - LINUX_EACCES, "FID provided as auth-file is for user=\"%s\" and cannot be used for user=\"%s\"", - fh->data.auth.uname, req->uname.utf8); - else if (strcmp(fh->data.auth.aname, req->aname.utf8) != 0) + LINUX_EACCES, "FID provided as auth-file is for user=\"%.*s\" and cannot be used for user=\"%.*s\"", + fh->data.auth.uname.len, fh->data.auth.uname.utf8, + req->uname.len, req->uname.utf8); + else if (!lib9p_str_eq(fh->data.auth.aname, req->aname)) lib9p_errorf(&ctx->ctx.basectx, - LINUX_EACCES, "FID provided as auth-file is for tree=\"%s\" and cannot be used for tree=\"%s\"", - fh->data.auth.aname, req->aname.utf8); + LINUX_EACCES, "FID provided as auth-file is for tree=\"%.*s\" and cannot be used for tree=\"%.*s\"", + fh->data.auth.aname.len, fh->data.auth.aname.utf8, + req->aname.len, req->aname.utf8); else if (!fh->data.auth.authenticated) lib9p_error(&ctx->ctx.basectx, LINUX_EACCES, "FID provided as auth-file has not completed authentication"); @@ -564,51 +712,41 @@ static void handle_Tattach(struct _lib9p_srv_req *ctx, return; */ lib9p_error(&ctx->ctx.basectx, - LINUX_EOPNOTSUPP, "TODO: auth not implemented"); + LINUX_EOPNOTSUPP, "TODO: auth not (yet?) implemented"); return; } else { - if (req->afid != LIB9P_NOFID) { + if (req->afid != LIB9P_FID_NOFID) { lib9p_error(&ctx->ctx.basectx, LINUX_EACCES, "FID provided as auth-file, but no auth-file is required"); return; } } - if (fidmap_load(&ctx->parent_sess->fids, req->fid)) { - lib9p_error(&ctx->ctx.basectx, - LINUX_EBADF, "FID already in use"); - return; + if (req->fid == LIB9P_FID_NOFID) { + lib9p_error(&ctx->ctx.basectx, + LINUX_EBADF, "cannot assign to NOFID"); + return; } - implements_lib9p_srv_file *rootdir = srv->rootdir(&ctx->ctx, req->aname.utf8); - assert((rootdir == NULL) == lib9p_ctx_has_error(&ctx->ctx.basectx)); + /* 1. File object */ + lo_interface lib9p_srv_file root_file = srv->rootdir(&ctx->ctx, req->aname); + assert(LO_IS_NULL(root_file) == lib9p_ctx_has_error(&ctx->ctx.basectx)); if (lib9p_ctx_has_error(&ctx->ctx.basectx)) return; - rootdir->_parent_dir = rootdir; - rootdir->_refcount++; - if (!fidmap_store(&ctx->parent_sess->fids, req->fid, (struct _srv_fidinfo){ - .file = rootdir, - .flags = FIDFLAG_ISDIR, - })) { - lib9p_error(&ctx->ctx.basectx, - LINUX_EMFILE, "too many open files"); - util_release(&ctx->ctx, rootdir); - return; - } + struct lib9p_qid root_qid = LO_CALL(root_file, qid); + assert(root_qid.type & LIB9P_QT_DIR); + + /* 2. pathinfo */ + struct srv_pathinfo *root_pathinfo = srv_util_pathsave(ctx, root_file, root_qid.path); - struct lib9p_stat stat = VCALL(rootdir, stat, &ctx->ctx); - if (lib9p_ctx_has_error(&ctx->ctx.basectx)) { - handle_Tclunk(ctx, - &(struct lib9p_msg_Tclunk){.fid = req->fid}, - &(struct lib9p_msg_Rclunk){}); + /* 3. fidinfo */ + if (!srv_util_fidsave(ctx, req->fid, root_pathinfo, false)) { + srv_util_pathfree(ctx, root_qid.path); return; } - lib9p_assert_stat(stat); - assert(stat.file_mode & LIB9P_DM_DIR); - - resp->qid = stat.file_qid; + resp->qid = root_qid; return; } @@ -627,87 +765,68 @@ static void handle_Twalk(struct _lib9p_srv_req *ctx, struct lib9p_msg_Rwalk *resp) { util_handler_common(ctx, req, resp); + if (req->newfid == LIB9P_FID_NOFID) { + lib9p_error(&ctx->ctx.basectx, + LINUX_EBADF, "cannot assign to NOFID"); + return; + } + struct _srv_fidinfo *fidinfo = fidmap_load(&ctx->parent_sess->fids, req->fid); if (!fidinfo) { lib9p_errorf(&ctx->ctx.basectx, LINUX_EBADF, "bad file number %"PRIu32, req->fid); return; } - if (req->newfid != req->fid && fidmap_load(&ctx->parent_sess->fids, req->newfid)) { - lib9p_error(&ctx->ctx.basectx, - LINUX_EBADF, "FID already in use"); - return; - } - implements_lib9p_srv_file *dir = fidinfo->file; - if (req->newfid != req->fid) { - dir = VCALL(dir, clone, &ctx->ctx); - assert((dir == NULL) == lib9p_ctx_has_error(&ctx->ctx.basectx)); - if (lib9p_ctx_has_error(&ctx->ctx.basectx)) - return; - dir->_refcount++; /* ref-A: presumptive insertion into fidmap */ - } - bool isdir = (fidinfo->flags & FIDFLAG_ISDIR); + struct srv_pathinfo *pathinfo = pathmap_load(&ctx->parent_sess->paths, fidinfo->path); + assert(pathinfo); + pathinfo->gc_refcount++; resp->wqid = (struct lib9p_qid *)(&resp[1]); for (resp->nwqid = 0; resp->nwqid < req->nwname; resp->nwqid++) { - implements_lib9p_srv_file *member; - if (strcmp(req->wname[resp->nwqid].utf8, "..") == 0) { - member = dir->_parent_dir; + struct srv_pathinfo *new_pathinfo; + if (lib9p_str_eq(req->wname[resp->nwqid], lib9p_str(".."))) { + new_pathinfo = pathmap_load(&ctx->parent_sess->paths, pathinfo->parent_dir); + assert(new_pathinfo); + new_pathinfo->gc_refcount++; } else { - if (!isdir) { + if (!srv_util_pathisdir(pathinfo)) { lib9p_error(&ctx->ctx.basectx, LINUX_ENOTDIR, "not a directory"); break; } - member = VCALL(dir, dopen, &ctx->ctx, req->wname[resp->nwqid].utf8); - assert((member == NULL) == lib9p_ctx_has_error(&ctx->ctx.basectx)); + lo_interface lib9p_srv_file member_file = LO_CALL(pathinfo->file, dwalk, &ctx->ctx, req->wname[resp->nwqid]); + assert(LO_IS_NULL(member_file) == lib9p_ctx_has_error(&ctx->ctx.basectx)); if (lib9p_ctx_has_error(&ctx->ctx.basectx)) break; - member->_parent_dir = dir; - dir->_refcount++; /* member->_parent_dir */ + new_pathinfo = srv_util_pathsave(ctx, member_file, LO_CALL(pathinfo->file, qid).path); } - member->_refcount++; /* presumptively take ref-A */ - struct lib9p_stat stat = VCALL(member, stat, &ctx->ctx); - if (lib9p_ctx_has_error(&ctx->ctx.basectx)) { - util_release(&ctx->ctx, member); /* presumption of taking ref-A failed */ - break; - } - lib9p_assert_stat(stat); - isdir = stat.file_mode & LIB9P_DM_DIR; - if (isdir && !util_check_perm(&ctx->ctx, &stat, 0b001)) { - lib9p_error(&ctx->ctx.basectx, - LINUX_EACCES, "you do not have execute permission on that directory"); - util_release(&ctx->ctx, member); /* presumption of taking ref-A failed */ - break; + if (srv_util_pathisdir(new_pathinfo)) { + struct lib9p_stat stat = LO_CALL(new_pathinfo->file, stat, &ctx->ctx); + if (lib9p_ctx_has_error(&ctx->ctx.basectx)) + break; + lib9p_stat_assert(stat); + if (!srv_util_check_perm(ctx, &stat, 0b001)) { + lib9p_error(&ctx->ctx.basectx, + LINUX_EACCES, "you do not have execute permission on that directory"); + srv_util_pathfree(ctx, LO_CALL(new_pathinfo->file, qid).path); + break; + } } - resp->wqid[resp->nwqid] = stat.file_qid; + resp->wqid[resp->nwqid] = LO_CALL(new_pathinfo->file, qid); - /* presumption of taking ref-A succeeded */ - util_release(&ctx->ctx, dir); - dir = member; + srv_util_pathfree(ctx, LO_CALL(pathinfo->file, qid).path); + pathinfo = new_pathinfo; } if (resp->nwqid == req->nwname) { - if (req->newfid == req->fid) { - handle_Tclunk(ctx, - &(struct lib9p_msg_Tclunk){.fid = req->fid}, - &(struct lib9p_msg_Rclunk){}); - } - if (!fidmap_store(&ctx->parent_sess->fids, req->newfid, (struct _srv_fidinfo){ - .file = dir, - .flags = isdir ? FIDFLAG_ISDIR : 0, - })) { - lib9p_error(&ctx->ctx.basectx, - LINUX_EMFILE, "too many open files"); - util_release(&ctx->ctx, dir); /* presumption of insertion failed */ - } + if (!srv_util_fidsave(ctx, req->newfid, pathinfo, req->newfid == req->fid)) + srv_util_pathfree(ctx, LO_CALL(pathinfo->file, qid).path); } else { assert(lib9p_ctx_has_error(&ctx->ctx.basectx)); - if (req->newfid != req->fid) - util_release(&ctx->ctx, dir); /* presumption of insertion failed */ + srv_util_pathfree(ctx, LO_CALL(pathinfo->file, qid).path); if (resp->nwqid > 0) lib9p_ctx_clear_error(&ctx->ctx.basectx); } @@ -730,8 +849,10 @@ static void handle_Topen(struct _lib9p_srv_req *ctx, LINUX_EALREADY, "FID is already open"); return; } - if (fidinfo->flags & FIDFLAG_ISDIR) { - if ( ((req->mode & LIB9P_O_MODE_MASK) != LIB9P_O_READ) || + struct srv_pathinfo *pathinfo = pathmap_load(&ctx->parent_sess->paths, fidinfo->path); + assert(pathinfo); + if (srv_util_pathisdir(pathinfo)) { + if ( ((req->mode & LIB9P_O_MODE_MASK) != LIB9P_O_MODE_READ) || (req->mode & LIB9P_O_TRUNC) || (req->mode & LIB9P_O_RCLOSE) ) { lib9p_error(&ctx->ctx.basectx, @@ -741,61 +862,92 @@ static void handle_Topen(struct _lib9p_srv_req *ctx, } /* Variables. */ - lib9p_o_t reqmode = req->mode; - uint8_t fidflags = fidinfo->flags; - implements_lib9p_srv_file *file = fidinfo->file; + lib9p_o_t reqmode = req->mode; + uint8_t fidflags = fidinfo->flags; /* Check permissions. */ if (reqmode & LIB9P_O_RCLOSE) { - struct lib9p_stat parent_stat = VCALL(file->_parent_dir, stat, &ctx->ctx); + struct srv_pathinfo *parent = pathmap_load(&ctx->parent_sess->paths, pathinfo->parent_dir); + assert(parent); + struct lib9p_stat parent_stat = LO_CALL(parent->file, stat, &ctx->ctx); if (lib9p_ctx_has_error(&ctx->ctx.basectx)) return; - lib9p_assert_stat(parent_stat); - if (!util_check_perm(&ctx->ctx, &parent_stat, 0b010)) { + lib9p_stat_assert(parent_stat); + if (!srv_util_check_perm(ctx, &parent_stat, 0b010)) { lib9p_error(&ctx->ctx.basectx, LINUX_EACCES, "permission denied to remove-on-close"); return; } - fidflags = fidflags | FIDFLAG_RCLOSE; + fidflags |= FIDFLAG_RCLOSE; } - struct lib9p_stat stat = VCALL(file, stat, &ctx->ctx); + struct lib9p_stat stat = LO_CALL(pathinfo->file, stat, &ctx->ctx); if (lib9p_ctx_has_error(&ctx->ctx.basectx)) return; - lib9p_assert_stat(stat); - if (stat.file_mode & LIB9P_QT_APPEND) + lib9p_stat_assert(stat); + if ((stat.file_mode & LIB9P_DM_EXCL) && pathinfo->io_refcount) { + lib9p_error(&ctx->ctx.basectx, + LINUX_EEXIST, "exclusive file is already opened"); + return; + } + if (stat.file_mode & LIB9P_DM_APPEND) reqmode = reqmode & ~LIB9P_O_TRUNC; uint8_t perm_bits = 0; + bool rd = false, wr = false; switch (reqmode & LIB9P_O_MODE_MASK) { - case LIB9P_O_READ: + case LIB9P_O_MODE_READ: perm_bits = 0b100; - fidflags = fidflags | FIDFLAG_OPEN_R; + rd = true; break; - case LIB9P_O_WRITE: + case LIB9P_O_MODE_WRITE: perm_bits = 0b010; - fidflags = fidflags | FIDFLAG_OPEN_W; + wr = true; break; - case LIB9P_O_RDWR: + case LIB9P_O_MODE_RDWR: perm_bits = 0b110; - fidflags = fidflags | FIDFLAG_OPEN_R | FIDFLAG_OPEN_W; + rd = wr = true; break; - case LIB9P_O_EXEC: + case LIB9P_O_MODE_EXEC: perm_bits = 0b001; - fidflags = fidflags | FIDFLAG_OPEN_R; + rd = true; break; } - if (!util_check_perm(&ctx->ctx, &stat, perm_bits)) { + if (!srv_util_check_perm(ctx, &stat, perm_bits)) { lib9p_error(&ctx->ctx.basectx, LINUX_EACCES, "permission denied"); + return; } /* Actually make the call. */ - uint32_t iounit = VCALL(file, io, &ctx->ctx, reqmode); - if (lib9p_ctx_has_error(&ctx->ctx.basectx)) - return; + uint32_t iounit; + struct lib9p_qid qid; + if (srv_util_pathisdir(pathinfo)) { + fidinfo->dir.io = LO_CALL(pathinfo->file, dopen, &ctx->ctx); + assert(LO_IS_NULL(fidinfo->dir.io) == lib9p_ctx_has_error(&ctx->ctx.basectx)); + if (lib9p_ctx_has_error(&ctx->ctx.basectx)) + return; + fidinfo->dir.idx = 0; + fidinfo->dir.off = 0; + qid = LO_CALL(fidinfo->dir.io, qid); + iounit = 0; + } else { + fidinfo->file.io = LO_CALL(pathinfo->file, fopen, &ctx->ctx, + rd, wr, + reqmode & LIB9P_O_TRUNC); + assert(LO_IS_NULL(fidinfo->file.io) == lib9p_ctx_has_error(&ctx->ctx.basectx)); + if (lib9p_ctx_has_error(&ctx->ctx.basectx)) + return; + qid = LO_CALL(fidinfo->file.io, qid); + iounit = LO_CALL(fidinfo->file.io, iounit); + } /* Success. */ + if (rd) + fidflags |= FIDFLAG_OPEN_R; + if (wr) + fidflags |= FIDFLAG_OPEN_W; + pathinfo->io_refcount++; fidinfo->flags = fidflags; - resp->qid = stat.file_qid; + resp->qid = qid; resp->iounit = iounit; } @@ -813,6 +965,8 @@ static void handle_Tread(struct _lib9p_srv_req *ctx, struct lib9p_msg_Rread *resp) { util_handler_common(ctx, req, resp); + /* TODO: serialize simultaneous reads to the same FID */ + /* Check that the FID is valid for this. */ struct _srv_fidinfo *fidinfo = fidmap_load(&ctx->parent_sess->fids, req->fid); if (!fidinfo) { @@ -827,38 +981,47 @@ static void handle_Tread(struct _lib9p_srv_req *ctx, } /* Variables. */ - implements_lib9p_srv_file *file = fidinfo->file; - resp->data.dat = (char *)(&resp[1]); + struct srv_pathinfo *pathinfo = pathmap_load(&ctx->parent_sess->paths, fidinfo->path); + assert(pathinfo); /* Do it. */ - if (fidinfo->flags & FIDFLAG_ISDIR) { + if (srv_util_pathisdir(pathinfo)) { /* Translate byte-offset to object-index. */ size_t idx; if (req->offset == 0) idx = 0; - else if (req->offset == fidinfo->dir_off) - idx = fidinfo->dir_idx; + else if (req->offset == fidinfo->dir.off) + idx = fidinfo->dir.idx; else { lib9p_errorf(&ctx->ctx.basectx, - LINUX_EINVAL, "invalid offset (must be 0 or %"PRIu32"): %"PRIu32, - fidinfo->dir_off, req->offset); + LINUX_EINVAL, "invalid offset (must be 0 or %"PRIu64"): %"PRIu64, + fidinfo->dir.off, req->offset); return; } /* Do it. */ - size_t num = VCALL(file, dread, &ctx->ctx, (uint8_t *)resp->data.dat, req->count, idx); + resp->data = (char *)(&resp[1]); + size_t num = LO_CALL(fidinfo->dir.io, dread, &ctx->ctx, (uint8_t *)resp->data, req->count, idx); /* Translate object-count back to byte-count. */ uint32_t len = 0; for (size_t i = 0; i < num; i++) { uint32_t i_len; - lib9p_validate_stat(&ctx->ctx.basectx, req->count, &((uint8_t *)resp->data.dat)[len], &i_len, NULL); + lib9p_stat_validate(&ctx->ctx.basectx, req->count, &((uint8_t *)resp->data)[len], &i_len, NULL); len += i_len; } - resp->data.len = len; + resp->count = len; /* Remember. */ - fidinfo->dir_idx = idx+num; - fidinfo->dir_off = req->offset + len; - } else - resp->data.len = VCALL(file, pread, &ctx->ctx, resp->data.dat, req->count, req->offset); + fidinfo->dir.idx = idx+num; + fidinfo->dir.off = req->offset + len; + } else { + struct iovec iov; + LO_CALL(fidinfo->file.io, pread, &ctx->ctx, req->count, req->offset, &iov); + if (!lib9p_ctx_has_error(&ctx->ctx.basectx)) { + resp->count = iov.iov_len; + resp->data = iov.iov_base; + if (resp->count > req->count) + resp->count = req->count; + } + } } static void handle_Twrite(struct _lib9p_srv_req *ctx, @@ -866,6 +1029,8 @@ static void handle_Twrite(struct _lib9p_srv_req *ctx, struct lib9p_msg_Rwrite *resp) { util_handler_common(ctx, req, resp); + /* TODO: serialize simultaneous writes to the same FID */ + /* Check that the FID is valid for this. */ struct _srv_fidinfo *fidinfo = fidmap_load(&ctx->parent_sess->fids, req->fid); if (!fidinfo) { @@ -880,41 +1045,69 @@ static void handle_Twrite(struct _lib9p_srv_req *ctx, } /* Variables. */ - implements_lib9p_srv_file *file = fidinfo->file; + struct srv_pathinfo *pathinfo = pathmap_load(&ctx->parent_sess->paths, fidinfo->path); + assert(pathinfo); /* Do it. */ - resp->count = VCALL(file, pwrite, &ctx->ctx, req->data.dat, req->data.len, req->offset); + resp->count = LO_CALL(fidinfo->file.io, pwrite, &ctx->ctx, req->data, req->count, req->offset); } -static void handle_Tclunk(struct _lib9p_srv_req *ctx, - struct lib9p_msg_Tclunk *req, - struct lib9p_msg_Rclunk *resp) { - util_handler_common(ctx, req, resp); - - struct _srv_fidinfo *fidinfo = fidmap_load(&ctx->parent_sess->fids, req->fid); +static void clunkremove(struct _lib9p_srv_req *ctx, lib9p_fid_t fid, bool remove) { + struct _srv_fidinfo *fidinfo = fidmap_load(&ctx->parent_sess->fids, fid); if (!fidinfo) { lib9p_errorf(&ctx->ctx.basectx, - LINUX_EBADF, "bad file number %"PRIu32, req->fid); + LINUX_EBADF, "bad file number %"PRIu32, fid); return; } - if (fidinfo->flags & FIDFLAG_RCLOSE) { - handle_Tremove(ctx, - &(struct lib9p_msg_Tremove){.fid = req->fid}, - &(struct lib9p_msg_Rremove){}); - return; + if (fidinfo->flags & FIDFLAG_RCLOSE) + remove = true; + struct srv_pathinfo *pathinfo = pathmap_load(&ctx->parent_sess->paths, fidinfo->path); + assert(pathinfo); + + if (remove) { + if (pathinfo->parent_dir == fidinfo->path) { + lib9p_errorf(&ctx->ctx.basectx, + LINUX_EBUSY, "cannot remove root"); + goto clunk; + } + struct srv_pathinfo *parent = pathmap_load(&ctx->parent_sess->paths, pathinfo->parent_dir); + assert(parent); + struct lib9p_stat parent_stat = LO_CALL(parent->file, stat, &ctx->ctx); + if (!srv_util_check_perm(ctx, &parent_stat, 0b010)) { + lib9p_error(&ctx->ctx.basectx, + LINUX_EACCES, "you do not have write permission on the parent directory"); + goto clunk; + } + LO_CALL(pathinfo->file, remove, &ctx->ctx); + } + + clunk: + if (fidinfo->flags & FIDFLAG_OPEN) { + if (srv_util_pathisdir(pathinfo)) + LO_CALL(fidinfo->dir.io, iofree); + else + LO_CALL(fidinfo->file.io, iofree); + pathinfo->io_refcount--; } + srv_util_pathfree(ctx, LO_CALL(pathinfo->file, qid).path); + fidmap_del(&ctx->parent_sess->fids, fid); +} - VCALL(fidinfo->file, free, &ctx->ctx); - fidmap_del(&ctx->parent_sess->fids, req->fid); +static void handle_Tclunk(struct _lib9p_srv_req *ctx, + struct lib9p_msg_Tclunk *req, + struct lib9p_msg_Rclunk *resp) { + util_handler_common(ctx, req, resp); + + clunkremove(ctx, req->fid, false); } + static void handle_Tremove(struct _lib9p_srv_req *ctx, struct lib9p_msg_Tremove *req, struct lib9p_msg_Rremove *resp) { util_handler_common(ctx, req, resp); - lib9p_error(&ctx->ctx.basectx, - LINUX_EOPNOTSUPP, "remove not (yet?) implemented"); + clunkremove(ctx, req->fid, true); } static void handle_Tstat(struct _lib9p_srv_req *ctx, @@ -928,10 +1121,12 @@ static void handle_Tstat(struct _lib9p_srv_req *ctx, LINUX_EBADF, "bad file number %"PRIu32, req->fid); return; } + struct srv_pathinfo *pathinfo = pathmap_load(&ctx->parent_sess->paths, fidinfo->path); + assert(pathinfo); - resp->stat = VCALL(fidinfo->file, stat, &ctx->ctx); + resp->stat = LO_CALL(pathinfo->file, stat, &ctx->ctx); if (!lib9p_ctx_has_error(&ctx->ctx.basectx)) - lib9p_assert_stat(resp->stat); + lib9p_stat_assert(resp->stat); } static void handle_Twstat(struct _lib9p_srv_req *ctx, @@ -943,7 +1138,7 @@ static void handle_Twstat(struct _lib9p_srv_req *ctx, LINUX_EOPNOTSUPP, "wstat not (yet?) implemented"); } -#ifdef CONFIG_9P_ENABLE_9P2000_e +#if CONFIG_9P_ENABLE_9P2000_e static void handle_Tsession(struct _lib9p_srv_req *ctx, struct lib9p_msg_Tsession *req, struct lib9p_msg_Rsession *resp) { diff --git a/lib9p/tables.c b/lib9p/tables.c new file mode 100644 index 0000000..86e3298 --- /dev/null +++ b/lib9p/tables.c @@ -0,0 +1,188 @@ +/* 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" + +/* bounds checks **************************************************************/ + +static inline void assert_ver(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 +} + +static inline void assert_typ(enum lib9p_msg_type typ) { +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wtype-limits" + assert(0 <= typ && typ < 0xFF); +#pragma GCC diagnostic pop +} + +/* simple lookups *************************************************************/ + +const char *lib9p_version_str(enum lib9p_version ver) { + assert_ver(ver); + return _lib9p_table_ver[ver].name; +} + +uint32_t lib9p_version_min_msg_size(enum lib9p_version ver) { + assert_ver(ver); + return _lib9p_table_ver[ver].min_msg_size; +} + +const char *lib9p_msgtype_str(enum lib9p_version ver, enum lib9p_msg_type typ) { + assert_ver(ver); + assert_typ(typ); + return _lib9p_table_msg[ver][typ].name ?: const_byte_str(typ); +} + +lo_interface fmt_formatter lo_box_lib9p_msg_as_fmt_formatter(struct lib9p_ctx *ctx, enum lib9p_msg_type typ, void *body) { + assert(ctx); + assert_ver(ctx->version); + assert_typ(typ); + assert(_lib9p_table_msg[ctx->version][typ].box_as_fmt_formatter); + return _lib9p_table_msg[ctx->version][typ].box_as_fmt_formatter(body); +} + +/* 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) { + assert_ver(ctx->version); + /* 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) { + assert_ver(ctx->version); + enum lib9p_msg_type typ = net_bytes[4]; + *ret_typ = typ; + struct _lib9p_recv_tentry tentry = xxx_table[ctx->version][typ/2]; + assert(tentry.unmarshal); + + 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) { + assert_ver(ctx->version); + assert_typ(typ); + 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]; + assert(tentry.marshal); + + 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, size_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 = (size_t)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..edb402a --- /dev/null +++ b/lib9p/tables.h @@ -0,0 +1,59 @@ +/* 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> + +/* version ********************************************************************/ + +struct _lib9p_ver_tentry { + const char *name; + uint32_t min_msg_size; +}; + +extern const struct _lib9p_ver_tentry _lib9p_table_ver[LIB9P_VER_NUM]; + +/* message ********************************************************************/ + +typedef lo_interface fmt_formatter (*_box_as_fmt_formatter_fn_t)(void *host_val); +struct _lib9p_msg_tentry { + const char *name; + _box_as_fmt_formatter_fn_t box_as_fmt_formatter; +}; + +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 _lib9p_recv_tentry { + _validate_fn_t validate; + _unmarshal_fn_t unmarshal; +}; + +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_send_tentry { + _marshal_fn_t marshal; +}; + +extern const struct _lib9p_msg_tentry _lib9p_table_msg[LIB9P_VER_NUM][0x100]; +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]; + +/* stat ***********************************************************************/ + +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/client_config/config.h b/lib9p/tests/client_config/config.h new file mode 100644 index 0000000..65ee9de --- /dev/null +++ b/lib9p/tests/client_config/config.h @@ -0,0 +1,40 @@ +/* config.h - Compile-time configuration for lib9p test clients + * + * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com> + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +#ifndef _CONFIG_H_ +#define _CONFIG_H_ + +/* 9P *************************************************************************/ + +#define CONFIG_9P_MAX_ERR_SIZE 128 +#define CONFIG_9P_MAX_9P2000_e_WELEM 16 + +#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 */ + +/* 9P server (unused) *********************************************************/ + +#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 + +/* COROUTINE (unused) *********************************************************/ + +#define CONFIG_COROUTINE_STACK_SIZE_DEFAULT (32*1024) +#define CONFIG_COROUTINE_NAME_LEN 16 +#define CONFIG_COROUTINE_MEASURE_STACK 1 /* bool */ +#define CONFIG_COROUTINE_PROTECT_STACK 1 /* bool */ +#define CONFIG_COROUTINE_DEBUG 0 /* bool */ +#define CONFIG_COROUTINE_VALGRIND 1 /* bool */ +#define CONFIG_COROUTINE_GDB 1 /* bool */ +#define CONFIG_COROUTINE_NUM 2 + +#endif /* _CONFIG_H_ */ diff --git a/lib9p/tests/runtest b/lib9p/tests/runtest new file mode 100755 index 0000000..6883391 --- /dev/null +++ b/lib9p/tests/runtest @@ -0,0 +1,50 @@ +#!/usr/bin/env bash +# lib9p/tests/runtest - Test harness for the 9P `test_server` +# +# Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com> +# SPDX-License-Identifier: AGPL-3.0-or-later + +set -euE -o pipefail + +build_aux=$(realpath --canonicalize-missing -- "${BASH_SOURCE[0]}/../../../build-aux") + +if [[ $# != 2 ]]; then + echo >&2 "Usage: $0 CLIENTSCRIPT EXPLOG" + exit 2 +fi +clientscript="$1" +explog="$2" + +cleanup=() +cleanup() { + { set +x; } &>/dev/null + local i + for ((i = ${#cleanup[@]} - 1; i >= 0; i--)); do + eval "set -x; ${cleanup[$i]}" + { set +x; } &>/dev/null + done +} +trap cleanup EXIT + +logfile=$(mktemp -t lib9p-log.XXXXXXXXXX) +cleanup+=("rm -f -- ${logfile@Q}") + +port=$(python -c 'import socket; s=socket.socket(); s.bind(("", 0)); print(s.getsockname()[1]); s.close()') + +set -x + +"${build_aux}/valgrind" ./tests/test_server/test_server "$port" "$logfile" & +server_pid=$! +cleanup+=("kill $server_pid || true; wait $server_pid || true") +while [[ -d /proc/$server_pid ]] && ! (readlink /proc/$server_pid/fd/* 2>/dev/null | grep -q ^socket:); do sleep 0.1; done + +if [[ "$(head -c2 -- "$clientscript")" == '#!' ]]; then + "$clientscript" "$port" +else + "${build_aux}/valgrind" "$clientscript" "$port" +fi + +wait "$server_pid" +cleanup=("${cleanup[@]::1}") + +diff -u -- <(grep -e '^[<>]' -- "$explog") "$logfile" diff --git a/lib9p/tests/test_compile.c b/lib9p/tests/test_compile.c new file mode 100644 index 0000000..8f2445d --- /dev/null +++ b/lib9p/tests/test_compile.c @@ -0,0 +1,299 @@ +/* lib9p/tests/test_compile.c - Generated by lib9p/tests/test_compile.c.gen. DO NOT EDIT! */ + +#include <lib9p/9p.h> +int main(void) { + [[gnu::unused]] uint64_t x; + x = LIB9P_TAG_NOTAG; + x = LIB9P_FID_NOFID; + x = LIB9P_DM_DIR; + x = LIB9P_DM_APPEND; + x = LIB9P_DM_EXCL; + x = _LIB9P_DM_PLAN9_MOUNT; + x = LIB9P_DM_AUTH; + x = LIB9P_DM_TMP; + x = _LIB9P_DM_UNUSED_25; + x = _LIB9P_DM_UNUSED_24; + x = LIB9P_DM_DEVICE; + x = _LIB9P_DM_UNUSED_22; + x = LIB9P_DM_PIPE; + x = LIB9P_DM_SOCKET; + x = LIB9P_DM_SETUID; + x = LIB9P_DM_SETGID; + x = _LIB9P_DM_UNUSED_17; + x = _LIB9P_DM_UNUSED_16; + x = _LIB9P_DM_UNUSED_15; + x = _LIB9P_DM_UNUSED_14; + x = _LIB9P_DM_UNUSED_13; + x = _LIB9P_DM_UNUSED_12; + x = _LIB9P_DM_UNUSED_11; + x = _LIB9P_DM_UNUSED_10; + x = _LIB9P_DM_UNUSED_9; + x = LIB9P_DM_OWNER_R; + x = LIB9P_DM_OWNER_W; + x = LIB9P_DM_OWNER_X; + x = LIB9P_DM_GROUP_R; + x = LIB9P_DM_GROUP_W; + x = LIB9P_DM_GROUP_X; + x = LIB9P_DM_OTHER_R; + x = LIB9P_DM_OTHER_W; + x = LIB9P_DM_OTHER_X; + x = LIB9P_DM_PERM_MASK; + x = LIB9P_QT_DIR; + x = LIB9P_QT_APPEND; + x = LIB9P_QT_EXCL; + x = _LIB9P_QT_PLAN9_MOUNT; + x = LIB9P_QT_AUTH; + x = LIB9P_QT_TMP; + x = LIB9P_QT_SYMLINK; + x = _LIB9P_QT_UNUSED_0; + x = LIB9P_QT_FILE; + x = LIB9P_NUID_NONUID; + x = _LIB9P_O_UNUSED_7; + x = LIB9P_O_RCLOSE; + x = _LIB9P_O_RESERVED_CEXEC; + x = LIB9P_O_TRUNC; + x = _LIB9P_O_UNUSED_3; + x = _LIB9P_O_UNUSED_2; + x = LIB9P_O_FLAG_MASK; + x = LIB9P_O_MODE_READ; + x = LIB9P_O_MODE_WRITE; + x = LIB9P_O_MODE_RDWR; + x = LIB9P_O_MODE_EXEC; + x = LIB9P_O_MODE_MASK; + x = LIB9P_ERRNO_NOERROR; + x = LIB9P_SUPER_MAGIC_V9FS_MAGIC; + x = _LIB9P_LO_UNUSED_31; + x = _LIB9P_LO_UNUSED_30; + x = _LIB9P_LO_UNUSED_29; + x = _LIB9P_LO_UNUSED_28; + x = _LIB9P_LO_UNUSED_27; + x = _LIB9P_LO_UNUSED_26; + x = _LIB9P_LO_UNUSED_25; + x = _LIB9P_LO_UNUSED_24; + x = _LIB9P_LO_UNUSED_23; + x = _LIB9P_LO_UNUSED_22; + x = _LIB9P_LO_UNUSED_21; + x = LIB9P_LO_SYNC; + x = LIB9P_LO_CLOEXEC; + x = LIB9P_LO_NOATIME; + x = LIB9P_LO_NOFOLLOW; + x = LIB9P_LO_DIRECTORY; + x = LIB9P_LO_LARGEFILE; + x = LIB9P_LO_DIRECT; + x = LIB9P_LO_BSD_FASYNC; + x = LIB9P_LO_DSYNC; + x = LIB9P_LO_NONBLOCK; + x = LIB9P_LO_APPEND; + x = LIB9P_LO_TRUNC; + x = LIB9P_LO_NOCTTY; + x = LIB9P_LO_EXCL; + x = LIB9P_LO_CREATE; + x = _LIB9P_LO_UNUSED_5; + x = _LIB9P_LO_UNUSED_4; + x = _LIB9P_LO_UNUSED_3; + x = _LIB9P_LO_UNUSED_2; + x = LIB9P_LO_FLAG_MASK; + x = LIB9P_LO_MODE_RDONLY; + x = LIB9P_LO_MODE_WRONLY; + x = LIB9P_LO_MODE_RDWR; + x = LIB9P_LO_MODE_NOACCESS; + x = LIB9P_LO_MODE_MASK; + x = LIB9P_DT_UNKNOWN; + x = LIB9P_DT_PIPE; + x = LIB9P_DT_CHAR_DEV; + x = LIB9P_DT_DIRECTORY; + x = LIB9P_DT_BLOCK_DEV; + x = LIB9P_DT_REGULAR; + x = LIB9P_DT_SYMLINK; + x = LIB9P_DT_SOCKET; + x = _LIB9P_DT_WHITEOUT; + x = _LIB9P_MODE_UNUSED_31; + x = _LIB9P_MODE_UNUSED_30; + x = _LIB9P_MODE_UNUSED_29; + x = _LIB9P_MODE_UNUSED_28; + x = _LIB9P_MODE_UNUSED_27; + x = _LIB9P_MODE_UNUSED_26; + x = _LIB9P_MODE_UNUSED_25; + x = _LIB9P_MODE_UNUSED_24; + x = _LIB9P_MODE_UNUSED_23; + x = _LIB9P_MODE_UNUSED_22; + x = _LIB9P_MODE_UNUSED_21; + x = _LIB9P_MODE_UNUSED_20; + x = _LIB9P_MODE_UNUSED_19; + x = _LIB9P_MODE_UNUSED_18; + x = _LIB9P_MODE_UNUSED_17; + x = _LIB9P_MODE_UNUSED_16; + x = LIB9P_MODE_PERM_SETGROUP; + x = LIB9P_MODE_PERM_SETUSER; + x = LIB9P_MODE_PERM_STICKY; + x = LIB9P_MODE_PERM_OWNER_R; + x = LIB9P_MODE_PERM_OWNER_W; + x = LIB9P_MODE_PERM_OWNER_X; + x = LIB9P_MODE_PERM_GROUP_R; + x = LIB9P_MODE_PERM_GROUP_W; + x = LIB9P_MODE_PERM_GROUP_X; + x = LIB9P_MODE_PERM_OTHER_R; + x = LIB9P_MODE_PERM_OTHER_W; + x = LIB9P_MODE_PERM_OTHER_X; + x = LIB9P_MODE_PERM_MASK; + x = LIB9P_MODE_FMT_PIPE; + x = LIB9P_MODE_FMT_CHAR_DEV; + x = LIB9P_MODE_FMT_DIRECTORY; + x = LIB9P_MODE_FMT_BLOCK_DEV; + x = LIB9P_MODE_FMT_REGULAR; + x = LIB9P_MODE_FMT_SYMLINK; + x = LIB9P_MODE_FMT_SOCKET; + x = LIB9P_MODE_FMT_MASK; + x = LIB9P_B4_FALSE; + x = LIB9P_B4_TRUE; + x = _LIB9P_GETATTR_UNUSED_63; + x = _LIB9P_GETATTR_UNUSED_62; + x = _LIB9P_GETATTR_UNUSED_61; + x = _LIB9P_GETATTR_UNUSED_60; + x = _LIB9P_GETATTR_UNUSED_59; + x = _LIB9P_GETATTR_UNUSED_58; + x = _LIB9P_GETATTR_UNUSED_57; + x = _LIB9P_GETATTR_UNUSED_56; + x = _LIB9P_GETATTR_UNUSED_55; + x = _LIB9P_GETATTR_UNUSED_54; + x = _LIB9P_GETATTR_UNUSED_53; + x = _LIB9P_GETATTR_UNUSED_52; + x = _LIB9P_GETATTR_UNUSED_51; + x = _LIB9P_GETATTR_UNUSED_50; + x = _LIB9P_GETATTR_UNUSED_49; + x = _LIB9P_GETATTR_UNUSED_48; + x = _LIB9P_GETATTR_UNUSED_47; + x = _LIB9P_GETATTR_UNUSED_46; + x = _LIB9P_GETATTR_UNUSED_45; + x = _LIB9P_GETATTR_UNUSED_44; + x = _LIB9P_GETATTR_UNUSED_43; + x = _LIB9P_GETATTR_UNUSED_42; + x = _LIB9P_GETATTR_UNUSED_41; + x = _LIB9P_GETATTR_UNUSED_40; + x = _LIB9P_GETATTR_UNUSED_39; + x = _LIB9P_GETATTR_UNUSED_38; + x = _LIB9P_GETATTR_UNUSED_37; + x = _LIB9P_GETATTR_UNUSED_36; + x = _LIB9P_GETATTR_UNUSED_35; + x = _LIB9P_GETATTR_UNUSED_34; + x = _LIB9P_GETATTR_UNUSED_33; + x = _LIB9P_GETATTR_UNUSED_32; + x = _LIB9P_GETATTR_UNUSED_31; + x = _LIB9P_GETATTR_UNUSED_30; + x = _LIB9P_GETATTR_UNUSED_29; + x = _LIB9P_GETATTR_UNUSED_28; + x = _LIB9P_GETATTR_UNUSED_27; + x = _LIB9P_GETATTR_UNUSED_26; + x = _LIB9P_GETATTR_UNUSED_25; + x = _LIB9P_GETATTR_UNUSED_24; + x = _LIB9P_GETATTR_UNUSED_23; + x = _LIB9P_GETATTR_UNUSED_22; + x = _LIB9P_GETATTR_UNUSED_21; + x = _LIB9P_GETATTR_UNUSED_20; + x = _LIB9P_GETATTR_UNUSED_19; + x = _LIB9P_GETATTR_UNUSED_18; + x = _LIB9P_GETATTR_UNUSED_17; + x = _LIB9P_GETATTR_UNUSED_16; + x = _LIB9P_GETATTR_UNUSED_15; + x = _LIB9P_GETATTR_UNUSED_14; + x = LIB9P_GETATTR_DATA_VERSION; + x = LIB9P_GETATTR_GEN; + x = LIB9P_GETATTR_BTIME; + x = LIB9P_GETATTR_BLOCKS; + x = LIB9P_GETATTR_SIZE; + x = LIB9P_GETATTR_INO; + x = LIB9P_GETATTR_CTIME; + x = LIB9P_GETATTR_MTIME; + x = LIB9P_GETATTR_ATIME; + x = LIB9P_GETATTR_RDEV; + x = LIB9P_GETATTR_GID; + x = LIB9P_GETATTR_UID; + x = LIB9P_GETATTR_NLINK; + x = LIB9P_GETATTR_MODE; + x = LIB9P_GETATTR_BASIC; + x = LIB9P_GETATTR_ALL; + x = _LIB9P_SETATTR_UNUSED_31; + x = _LIB9P_SETATTR_UNUSED_30; + x = _LIB9P_SETATTR_UNUSED_29; + x = _LIB9P_SETATTR_UNUSED_28; + x = _LIB9P_SETATTR_UNUSED_27; + x = _LIB9P_SETATTR_UNUSED_26; + x = _LIB9P_SETATTR_UNUSED_25; + x = _LIB9P_SETATTR_UNUSED_24; + x = _LIB9P_SETATTR_UNUSED_23; + x = _LIB9P_SETATTR_UNUSED_22; + x = _LIB9P_SETATTR_UNUSED_21; + x = _LIB9P_SETATTR_UNUSED_20; + x = _LIB9P_SETATTR_UNUSED_19; + x = _LIB9P_SETATTR_UNUSED_18; + x = _LIB9P_SETATTR_UNUSED_17; + x = _LIB9P_SETATTR_UNUSED_16; + x = _LIB9P_SETATTR_UNUSED_15; + x = _LIB9P_SETATTR_UNUSED_14; + x = _LIB9P_SETATTR_UNUSED_13; + x = _LIB9P_SETATTR_UNUSED_12; + x = _LIB9P_SETATTR_UNUSED_11; + x = _LIB9P_SETATTR_UNUSED_10; + x = _LIB9P_SETATTR_UNUSED_9; + x = LIB9P_SETATTR_MTIME_SET; + x = LIB9P_SETATTR_ATIME_SET; + x = LIB9P_SETATTR_CTIME; + x = LIB9P_SETATTR_MTIME; + x = LIB9P_SETATTR_ATIME; + x = LIB9P_SETATTR_SIZE; + x = LIB9P_SETATTR_GID; + x = LIB9P_SETATTR_UID; + x = LIB9P_SETATTR_MODE; + x = LIB9P_LOCK_TYPE_RDLCK; + x = LIB9P_LOCK_TYPE_WRLCK; + x = LIB9P_LOCK_TYPE_UNLCK; + x = _LIB9P_LOCK_FLAGS_UNUSED_31; + x = _LIB9P_LOCK_FLAGS_UNUSED_30; + x = _LIB9P_LOCK_FLAGS_UNUSED_29; + x = _LIB9P_LOCK_FLAGS_UNUSED_28; + x = _LIB9P_LOCK_FLAGS_UNUSED_27; + x = _LIB9P_LOCK_FLAGS_UNUSED_26; + x = _LIB9P_LOCK_FLAGS_UNUSED_25; + x = _LIB9P_LOCK_FLAGS_UNUSED_24; + x = _LIB9P_LOCK_FLAGS_UNUSED_23; + x = _LIB9P_LOCK_FLAGS_UNUSED_22; + x = _LIB9P_LOCK_FLAGS_UNUSED_21; + x = _LIB9P_LOCK_FLAGS_UNUSED_20; + x = _LIB9P_LOCK_FLAGS_UNUSED_19; + x = _LIB9P_LOCK_FLAGS_UNUSED_18; + x = _LIB9P_LOCK_FLAGS_UNUSED_17; + x = _LIB9P_LOCK_FLAGS_UNUSED_16; + x = _LIB9P_LOCK_FLAGS_UNUSED_15; + x = _LIB9P_LOCK_FLAGS_UNUSED_14; + x = _LIB9P_LOCK_FLAGS_UNUSED_13; + x = _LIB9P_LOCK_FLAGS_UNUSED_12; + x = _LIB9P_LOCK_FLAGS_UNUSED_11; + x = _LIB9P_LOCK_FLAGS_UNUSED_10; + x = _LIB9P_LOCK_FLAGS_UNUSED_9; + x = _LIB9P_LOCK_FLAGS_UNUSED_8; + x = _LIB9P_LOCK_FLAGS_UNUSED_7; + x = _LIB9P_LOCK_FLAGS_UNUSED_6; + x = _LIB9P_LOCK_FLAGS_UNUSED_5; + x = _LIB9P_LOCK_FLAGS_UNUSED_4; + x = _LIB9P_LOCK_FLAGS_UNUSED_3; + x = _LIB9P_LOCK_FLAGS_UNUSED_2; + x = LIB9P_LOCK_FLAGS_RECLAIM; + x = LIB9P_LOCK_FLAGS_BLOCK; + x = LIB9P_LOCK_STATUS_SUCCESS; + x = LIB9P_LOCK_STATUS_BLOCKED; + x = LIB9P_LOCK_STATUS_ERROR; + x = LIB9P_LOCK_STATUS_GRACE; + x = LIB9P_TMSG_MAX_IOV; + x = LIB9P_TMSG_MAX_IOV; + x = LIB9P_TMSG_MAX_COPY; + x = LIB9P_TMSG_MAX_COPY; + x = LIB9P_TMSG_MAX_COPY; + x = LIB9P_TMSG_MAX_COPY; + x = LIB9P_TMSG_MAX_COPY; + x = LIB9P_TMSG_MAX_COPY; + x = LIB9P_RMSG_MAX_IOV; + x = LIB9P_RMSG_MAX_IOV; + x = LIB9P_RMSG_MAX_IOV; + x = LIB9P_RMSG_MAX_COPY; + return 0; +} diff --git a/lib9p/tests/test_compile.c.gen b/lib9p/tests/test_compile.c.gen new file mode 100755 index 0000000..47046b3 --- /dev/null +++ b/lib9p/tests/test_compile.c.gen @@ -0,0 +1,19 @@ +#!/bin/sh +# lib9p/tests/test_compile.c.gen - Generate code to make sure all generated macros work +# +# Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com> +# SPDX-License-Identifier: AGPL-3.0-or-later + +generated_h=$1 +outfile=$2 + +{ + echo "/* ${outfile} - Generated by $0. DO NOT EDIT! */" + echo + echo "#include <lib9p/9p.h>" + echo 'int main(void) {' + echo ' [[gnu::unused]] uint64_t x;' + sed -nE 's/^\s*#\s*define\s*(\S[^ (]*)\s.*/ x = \1;/p' <"$generated_h" + echo ' return 0;' + echo '}' +} >"$outfile" diff --git a/lib9p/tests/test_compile_config/config.h b/lib9p/tests/test_compile_config/config.h new file mode 100644 index 0000000..cc8eec1 --- /dev/null +++ b/lib9p/tests/test_compile_config/config.h @@ -0,0 +1,38 @@ +/* config.h - Compile-time configuration for lib9p/test/test_compile + * + * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com> + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +#ifndef _CONFIG_H_ +#define _CONFIG_H_ + +/* 9P *************************************************************************/ + +#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 ******************************************************************/ + +#define CONFIG_COROUTINE_STACK_SIZE_DEFAULT (32*1024) +#define CONFIG_COROUTINE_NAME_LEN 16 +#define CONFIG_COROUTINE_MEASURE_STACK 1 /* bool */ +#define CONFIG_COROUTINE_PROTECT_STACK 1 /* bool */ +#define CONFIG_COROUTINE_DEBUG 0 /* bool */ +#define CONFIG_COROUTINE_VALGRIND 1 /* bool */ +#define CONFIG_COROUTINE_GDB 1 /* bool */ +#define CONFIG_COROUTINE_NUM 2 + +#endif /* _CONFIG_H_ */ diff --git a/lib9p/tests/test_server/CMakeLists.txt b/lib9p/tests/test_server/CMakeLists.txt new file mode 100644 index 0000000..eb16165 --- /dev/null +++ b/lib9p/tests/test_server/CMakeLists.txt @@ -0,0 +1,45 @@ +# lib9p/tests/test_server/CMakeLists.txt - Build script for test_server executable +# +# Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com> +# SPDX-License-Identifier: AGPL-3.0-or-later + +if (PICO_PLATFORM STREQUAL "host") + +# Compile ###################################################################### + +add_library(test_server_objs OBJECT + main.c + fs_shutdown.c + fs_slowread.c +) +target_include_directories(test_server_objs PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/config) +target_include_directories(test_server_objs PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) +target_link_libraries(test_server_objs + libcr + libcr_ipc + libmisc + lib9p + lib9p_util + libhw_cr +) + +# Analyze the stack ############################################################ + +add_stack_analysis(test_server_stack.c test_server_objs) + +# Link ######################################################################### + +add_executable(test_server) +target_sources(test_server PRIVATE + test_server_stack.c + "$<TARGET_OBJECTS:test_server_objs>" +) + +# Embed ######################################################################## + +target_embed_sources(test_server_objs test_server static.h + static/README.md + static/Documentation/x.txt +) + +endif() diff --git a/lib9p/tests/test_server/config/config.h b/lib9p/tests/test_server/config/config.h new file mode 100644 index 0000000..03143e1 --- /dev/null +++ b/lib9p/tests/test_server/config/config.h @@ -0,0 +1,66 @@ +/* config.h - Compile-time configuration for lib9p/test/test_server + * + * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com> + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +#ifndef _CONFIG_H_ +#define _CONFIG_H_ + +#define _CONFIG_9P_NUM_SOCKS 8 +#define CONFIG_SRV9P_NUM_CONNS _CONFIG_9P_NUM_SOCKS + +/* 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. + * + * This is the same as the default in Plan 9 4e's lib9p; it has the + * comment that "24" is "ample room for Twrite/Rread header + * (iounit)". In fact, the Twrite header is only 23 bytes + * ("size[4] Twrite[1] tag[2] fid[4] offset[8] count[4]") and the + * Rread header is even shorter at 11 bytes ("size[4] Rread[1] + * tag[2] count[4]"), so "24" appears to be the size of the Twrite + * header rounded up to a nice round number. + * + * In older versions of 9P ("9P1"), the max message size was + * defined as part of the protocol specification rather than + * negotiated. In Plan 9 1e it was (8*1024)+128, and was bumped to + * (8*1024)+160 in 2e and 3e. + */ +#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_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 ******************************************************************/ + +#define CONFIG_COROUTINE_STACK_SIZE_DEFAULT (32*1024) +#define CONFIG_COROUTINE_NAME_LEN 16 +#define CONFIG_COROUTINE_MEASURE_STACK 1 /* bool */ +#define CONFIG_COROUTINE_PROTECT_STACK 1 /* bool */ +#define CONFIG_COROUTINE_DEBUG 0 /* bool */ +#define CONFIG_COROUTINE_VALGRIND 1 /* bool */ +#define CONFIG_COROUTINE_GDB 1 /* bool */ +#define CONFIG_COROUTINE_NUM ( \ + 1 /* usb_common */ + \ + 1 /* usb_keyboard */ + \ + CONFIG_SRV9P_NUM_CONNS /* accept+read */ + \ + (CONFIG_9P_SRV_MAX_REQS*CONFIG_SRV9P_NUM_CONNS) /* work+write */ ) + +#endif /* _CONFIG_H_ */ diff --git a/lib9p/tests/test_server/fs_shutdown.c b/lib9p/tests/test_server/fs_shutdown.c new file mode 100644 index 0000000..3f88985 --- /dev/null +++ b/lib9p/tests/test_server/fs_shutdown.c @@ -0,0 +1,93 @@ +/* lib9p/tests/test_server/fs_shutdown.c - /shutdown API endpoint + * + * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com> + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +#include "fs_shutdown.h" + +LO_IMPLEMENTATION_C(lib9p_srv_file, struct shutdown_file, shutdown_file, static); + +LO_IMPLEMENTATION_H(lib9p_srv_fio, struct shutdown_file, shutdown_file); +LO_IMPLEMENTATION_C(lib9p_srv_fio, struct shutdown_file, shutdown_file, static); + +/* srv_file *******************************************************************/ + +static void shutdown_file_free(struct shutdown_file *self) { + assert(self); +} +static struct lib9p_qid shutdown_file_qid(struct shutdown_file *self) { + assert(self); + return (struct lib9p_qid){ + .type = LIB9P_QT_FILE, + .vers = 1, + .path = self->pathnum, + }; +} + +static struct lib9p_stat shutdown_file_stat(struct shutdown_file *self, struct lib9p_srv_ctx *ctx) { + assert(self); + assert(ctx); + return (struct lib9p_stat){ + .kern_type = 0, + .kern_dev = 0, + .file_qid = shutdown_file_qid(self), + .file_mode = 0222, + .file_atime = UTIL9P_ATIME, + .file_mtime = UTIL9P_MTIME, + .file_size = 0, + .file_name = lib9p_str(self->name), + .file_owner_uid = lib9p_str("root"), + .file_owner_gid = lib9p_str("root"), + .file_last_modified_uid = lib9p_str("root"), + .file_extension = lib9p_str(NULL), + .file_owner_n_uid = 0, + .file_owner_n_gid = 0, + .file_last_modified_n_uid = 0, + }; +} +static void shutdown_file_wstat(struct shutdown_file *self, struct lib9p_srv_ctx *ctx, struct lib9p_stat) { + assert(self); + assert(ctx); + lib9p_error(&ctx->basectx, LINUX_EROFS, "cannot wstat API file"); +} +static void shutdown_file_remove(struct shutdown_file *self, struct lib9p_srv_ctx *ctx) { + assert(self); + assert(ctx); + lib9p_error(&ctx->basectx, LINUX_EROFS, "cannot remove API file"); +} + +LIB9P_SRV_NOTDIR(struct shutdown_file, shutdown_file) + +static lo_interface lib9p_srv_fio shutdown_file_fopen(struct shutdown_file *self, struct lib9p_srv_ctx *ctx, bool, bool, bool) { + assert(self); + assert(ctx); + return lo_box_shutdown_file_as_lib9p_srv_fio(self); +} + +/* srv_fio ********************************************************************/ + +static void shutdown_file_iofree(struct shutdown_file *self) { + assert(self); +} + +static uint32_t shutdown_file_iounit(struct shutdown_file *self) { + assert(self); + return 0; +} + +static uint32_t shutdown_file_pwrite(struct shutdown_file *self, struct lib9p_srv_ctx *ctx, void *buf, uint32_t byte_count, uint64_t LM_UNUSED(offset)) { + assert(self); + assert(ctx); + assert(buf); + if (byte_count == 0) + return 0; + for (size_t i = 0; i < self->nlisteners; i++) + LO_CALL(lo_box_hostnet_tcplist_as_net_stream_listener(&self->listeners[i]), close); + return byte_count; +} +static void shutdown_file_pread(struct shutdown_file *LM_UNUSED(self), struct lib9p_srv_ctx *LM_UNUSED(ctx), + uint32_t LM_UNUSED(byte_count), uint64_t LM_UNUSED(byte_offset), + struct iovec *LM_UNUSED(ret)) { + assert_notreached("not readable"); +} diff --git a/lib9p/tests/test_server/fs_shutdown.h b/lib9p/tests/test_server/fs_shutdown.h new file mode 100644 index 0000000..65956db --- /dev/null +++ b/lib9p/tests/test_server/fs_shutdown.h @@ -0,0 +1,23 @@ +/* lib9p/tests/test_server/fs_shutdown.h - /shutdown API endpoint + * + * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com> + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +#ifndef _LIB9P_TESTS_TEST_SERVER_FS_SHUTDOWN_H_ +#define _LIB9P_TESTS_TEST_SERVER_FS_SHUTDOWN_H_ + +#include <util9p/static.h> +#include <libhw/host_net.h> + +struct shutdown_file { + char *name; + uint64_t pathnum; + + struct hostnet_tcp_listener *listeners; + size_t nlisteners; +}; +LO_IMPLEMENTATION_H(lib9p_srv_file, struct shutdown_file, shutdown_file); +#define lo_box_shutdown_file_as_lib9p_srv_file(obj) util9p_box(shutdown_file, obj) + +#endif /* _LIB9P_TESTS_TEST_SERVER_FS_SHUTDOWN_H_ */ diff --git a/lib9p/tests/test_server/fs_slowread.c b/lib9p/tests/test_server/fs_slowread.c new file mode 100644 index 0000000..520edd2 --- /dev/null +++ b/lib9p/tests/test_server/fs_slowread.c @@ -0,0 +1,101 @@ +/* lib9p/tests/test_server/fs_slowread.c - slowread API endpoint + * + * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com> + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +#include "fs_slowread.h" + +LO_IMPLEMENTATION_C(lib9p_srv_file, struct slowread_file, slowread_file, static); + +LO_IMPLEMENTATION_H(lib9p_srv_fio, struct slowread_file, slowread_file); +LO_IMPLEMENTATION_C(lib9p_srv_fio, struct slowread_file, slowread_file, static); + +/* srv_file *******************************************************************/ + +static void slowread_file_free(struct slowread_file *self) { + assert(self); +} +static struct lib9p_qid slowread_file_qid(struct slowread_file *self) { + assert(self); + return (struct lib9p_qid){ + .type = LIB9P_QT_FILE, + .vers = 1, + .path = self->pathnum, + }; +} + +static struct lib9p_stat slowread_file_stat(struct slowread_file *self, struct lib9p_srv_ctx *ctx) { + assert(self); + assert(ctx); + return (struct lib9p_stat){ + .kern_type = 0, + .kern_dev = 0, + .file_qid = slowread_file_qid(self), + .file_mode = 0444, + .file_atime = UTIL9P_ATIME, + .file_mtime = UTIL9P_MTIME, + .file_size = 6, + .file_name = lib9p_str(self->name), + .file_owner_uid = lib9p_str("root"), + .file_owner_gid = lib9p_str("root"), + .file_last_modified_uid = lib9p_str("root"), + .file_extension = lib9p_str(NULL), + .file_owner_n_uid = 0, + .file_owner_n_gid = 0, + .file_last_modified_n_uid = 0, + }; +} +static void slowread_file_wstat(struct slowread_file *self, struct lib9p_srv_ctx *ctx, struct lib9p_stat) { + assert(self); + assert(ctx); + lib9p_error(&ctx->basectx, LINUX_EROFS, "cannot wstat API file"); +} +static void slowread_file_remove(struct slowread_file *self, struct lib9p_srv_ctx *ctx) { + assert(self); + assert(ctx); + lib9p_error(&ctx->basectx, LINUX_EROFS, "cannot remove API file"); +} + +LIB9P_SRV_NOTDIR(struct slowread_file, slowread_file) + +static lo_interface lib9p_srv_fio slowread_file_fopen(struct slowread_file *self, struct lib9p_srv_ctx *ctx, bool, bool, bool) { + assert(self); + assert(ctx); + return lo_box_slowread_file_as_lib9p_srv_fio(self); +} + +/* srv_fio ********************************************************************/ + +static void slowread_file_iofree(struct slowread_file *self) { + assert(self); +} + +static uint32_t slowread_file_iounit(struct slowread_file *self) { + assert(self); + return 0; +} + +static uint32_t slowread_file_pwrite(struct slowread_file *LM_UNUSED(self), + struct lib9p_srv_ctx *LM_UNUSED(ctx), + void *LM_UNUSED(buf), uint32_t LM_UNUSED(byte_count), + uint64_t LM_UNUSED(offset)) { + assert_notreached("not writable"); +} +static void slowread_file_pread(struct slowread_file *self, struct lib9p_srv_ctx *ctx, + uint32_t byte_count, uint64_t LM_UNUSED(byte_offset), + struct iovec *ret) { + assert(self); + assert(ctx); + assert(ret); + + while (!lib9p_srv_flush_requested(ctx)) + cr_yield(); + if (self->flushable) + lib9p_srv_acknowledge_flush(ctx); + else + *ret = (struct iovec){ + .iov_base = "Sloth\n", + .iov_len = 6 < byte_count ? 6 : byte_count, + }; +} diff --git a/lib9p/tests/test_server/fs_slowread.h b/lib9p/tests/test_server/fs_slowread.h new file mode 100644 index 0000000..ef4b65f --- /dev/null +++ b/lib9p/tests/test_server/fs_slowread.h @@ -0,0 +1,22 @@ +/* lib9p/tests/test_server/fs_slowread.h - slowread API endpoint + * + * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com> + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +#ifndef _LIB9P_TESTS_TEST_SERVER_FS_SLOWREAD_H_ +#define _LIB9P_TESTS_TEST_SERVER_FS_SLOWREAD_H_ + +#include <util9p/static.h> +#include <libhw/host_net.h> + +struct slowread_file { + char *name; + uint64_t pathnum; + + bool flushable; +}; +LO_IMPLEMENTATION_H(lib9p_srv_file, struct slowread_file, slowread_file); +#define lo_box_slowread_file_as_lib9p_srv_file(obj) util9p_box(slowread_file, obj) + +#endif /* _LIB9P_TESTS_TEST_SERVER_FS_SLOWREAD_H_ */ diff --git a/lib9p/tests/test_server/main.c b/lib9p/tests/test_server/main.c new file mode 100644 index 0000000..2743629 --- /dev/null +++ b/lib9p/tests/test_server/main.c @@ -0,0 +1,154 @@ +/* lib9p/tests/test_server/main.c - Main entry point for test 9P server + * + * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com> + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +#include <error.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> /* for atoi() */ + +#include <lib9p/srv.h> +#include <libcr/coroutine.h> +#include <libhw/generic/net.h> +#include <libhw/generic/alarmclock.h> +#include <libhw/host_alarmclock.h> +#include <libhw/host_net.h> +#include <libmisc/macro.h> +#include <util9p/static.h> + +#include "static.h" +#include "fs_shutdown.h" +#include "fs_slowread.h" + +/* configuration **************************************************************/ + +#include "config.h" + +#ifndef CONFIG_SRV9P_NUM_CONNS + #error config.h must define CONFIG_SRV9P_NUM_CONNS +#endif + +/* globals ********************************************************************/ + +static lo_interface lib9p_srv_file get_root(struct lib9p_srv_ctx *, struct lib9p_s); + +const char *hexdig = "0123456789abcdef"; + +struct { + uint16_t port; + struct hostnet_tcp_listener listeners[CONFIG_SRV9P_NUM_CONNS]; + struct lib9p_srv srv; + FILE *logstream; +} globals = { + .srv = (struct lib9p_srv){ + .rootdir = get_root, + }, +}; + +/* file tree ******************************************************************/ + +#define STATIC_FILE(N, STRNAME, SYMNAME) \ + UTIL9P_STATIC_FILE(N, STRNAME, \ + .data_start = _binary_static_##SYMNAME##_start, \ + .data_end = _binary_static_##SYMNAME##_end) +#define STATIC_DIR(N, STRNAME, ...) \ + UTIL9P_STATIC_DIR(N, STRNAME, __VA_ARGS__) + +#define API_FILE(N, STRNAME, SYMNAME, ...) \ + lo_box_##SYMNAME##_file_as_lib9p_srv_file(&((struct SYMNAME##_file){ \ + .name = STRNAME, \ + .pathnum = N \ + __VA_OPT__(,) __VA_ARGS__ \ + })) + +struct lib9p_srv_file root = + STATIC_DIR(1, "", + STATIC_DIR(2, "Documentation", + STATIC_FILE(3, "x", Documentation_x_txt), + ), + STATIC_FILE(4, "README.md", README_md), + API_FILE(5, "shutdown", shutdown, + .listeners = globals.listeners, + .nlisteners = LM_ARRAY_LEN(globals.listeners)), + API_FILE(6, "slowread", slowread, + .flushable = false), + API_FILE(7, "slowread-flushable", slowread, + .flushable = true), + ); + +static lo_interface lib9p_srv_file get_root(struct lib9p_srv_ctx *LM_UNUSED(ctx), struct lib9p_s LM_UNUSED(treename)) { + return root; +} + +/* main ***********************************************************************/ + +static COROUTINE read_cr(void *_i) { + int i = *((int *)_i); + cr_begin(); + + hostnet_tcp_listener_init(&globals.listeners[i], globals.port); + + lib9p_srv_read_cr(&globals.srv, lo_box_hostnet_tcplist_as_net_stream_listener(&globals.listeners[i])); + + cr_end(); +} + +static COROUTINE init_cr(void *) { + cr_begin(); + + sleep_for_ms(1); + + for (int i = 0; i < CONFIG_SRV9P_NUM_CONNS; i++) { + char name[] = {'r', 'e', 'a', 'd', '-', hexdig[i], '\0'}; + if (!coroutine_add(name, read_cr, &i)) + error(1, 0, "coroutine_add(read_cr, &i)"); + } + for (int i = 0; i < 2*CONFIG_SRV9P_NUM_CONNS; i++) { + char name[] = {'w', 'r', 'i', 't', 'e', '-', hexdig[i], '\0'}; + if (!coroutine_add(name, lib9p_srv_write_cr, &globals.srv)) + error(1, 0, "coroutine_add(lib9p_srv_write_cr, &globals.srv)"); + } + + cr_exit(); +} + +static void log_fct(char character, void *_stream) { + FILE *stream = _stream; + putc(character, stream); + putchar(character); +} + +static void log_msg(struct lib9p_srv_ctx *ctx, enum lib9p_msg_type typ, void *hostmsg) { + /* It sucks that %v trips -Wformat and -Wformat-extra-args + * https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47781 */ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat" +#pragma GCC diagnostic ignored "-Wformat-extra-args" + fmt_fctprintf(log_fct, globals.logstream, + "%c %v\n", typ % 2 ? '<' : '>', + lo_box_lib9p_msg_as_fmt_formatter(&ctx->basectx, typ, hostmsg)); +#pragma GCC diagnostic pop + fflush(globals.logstream); +} + +int main(int argc, char *argv[]) { + if (argc != 3) + error(2, 0, "usage: %s PORT_NUMBER LOGFILE", argv[0]); + + globals.port = atoi(argv[1]); + globals.logstream = fopen(argv[2], "w"); + if (!globals.logstream) + error(2, errno, "fopen"); + globals.srv.msglog = log_msg; + + struct hostclock clock_monotonic = { + .clock_id = CLOCK_MONOTONIC, + }; + bootclock = lo_box_hostclock_as_alarmclock(&clock_monotonic); + coroutine_add("init", init_cr, NULL); + coroutine_main(); + fclose(globals.logstream); + return 0; +} diff --git a/lib9p/tests/test_server/static/Documentation/x.txt b/lib9p/tests/test_server/static/Documentation/x.txt new file mode 100644 index 0000000..e85ee4e --- /dev/null +++ b/lib9p/tests/test_server/static/Documentation/x.txt @@ -0,0 +1,7 @@ +<!-- + Documentation/x.txt - test static file + + Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com> + SPDX-License-Identifier: AGPL-3.0-or-later +--> +foo diff --git a/lib9p/tests/test_server/static/README.md b/lib9p/tests/test_server/static/README.md new file mode 100644 index 0000000..c2d88ed --- /dev/null +++ b/lib9p/tests/test_server/static/README.md @@ -0,0 +1,7 @@ +<!-- + README.md - test static file + + Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com> + SPDX-License-Identifier: AGPL-3.0-or-later +--> +Hello, world! diff --git a/lib9p/tests/testclient-p9p b/lib9p/tests/testclient-p9p new file mode 100755 index 0000000..81a7e50 --- /dev/null +++ b/lib9p/tests/testclient-p9p @@ -0,0 +1,61 @@ +#!/usr/bin/env bash +# lib9p/tests/testclient-p9p - Test the 9P `test_server` against Plan 9 Port's `9p` utility +# +# Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com> +# SPDX-License-Identifier: AGPL-3.0-or-later + +set -euE -o pipefail + +if [[ $# != 1 ]]; then + echo >&2 "Usage: $0 SERVER_PORT" + echo >&2 "Usage: ./runtest $0 EXPLOG" + exit 2 +fi + +expect_lines() ( + { set +x; } &>/dev/null + printf >&2 '+ diff -u expected.txt actual.txt\n' + diff -u <(printf '%s\n' "$@") <(printf '%s\n' "$out") +) + +set -x +client=(9p -a "localhost:${1}") + +out=$("${client[@]}" ls -l '') +expect_lines \ + 'd-r-xr-xr-x M 0 root root 0 Oct 7 2024 Documentation' \ + '--r--r--r-- M 0 root root 166 Oct 7 2024 README.md' \ + '---w--w--w- M 0 root root 0 Oct 7 2024 shutdown' \ + '--r--r--r-- M 0 root root 6 Oct 7 2024 slowread' \ + '--r--r--r-- M 0 root root 6 Oct 7 2024 slowread-flushable' + +out=$("${client[@]}" ls -l 'Documentation/') +expect_lines \ + '--r--r--r-- M 0 root root 166 Oct 7 2024 x' + +out=$("${client[@]}" read 'README.md') +expect_lines \ + '<!--' \ + ' README.md - test static file' \ + '' \ + ' Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>' \ + ' SPDX-License-Identifier: AGPL-3.0-or-later' \ + '-->' \ + 'Hello, world!' + +out=$("${client[@]}" read 'Documentation/x') +expect_lines \ + '<!--' \ + ' Documentation/x.txt - test static file' \ + '' \ + ' Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>' \ + ' SPDX-License-Identifier: AGPL-3.0-or-later' \ + '-->' \ + 'foo' + +out=$("${client[@]}" stat 'Documentation/x') +expect_lines \ + "'x' 'root' 'root' 'root' q (0000000000000003 1 ) m 0444 at 1728337905 mt 1728337904 l 166 t 0 d 0" + +out=$("${client[@]}" write 'shutdown' <<<1) +expect_lines '' diff --git a/lib9p/tests/testclient-p9p.explog b/lib9p/tests/testclient-p9p.explog new file mode 100644 index 0000000..3bfb0b0 --- /dev/null +++ b/lib9p/tests/testclient-p9p.explog @@ -0,0 +1,106 @@ +# lib9p/tests/testclient-p9p.explog - Expected 9P logfile of testclient-p9p +# +# Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com> +# SPDX-License-Identifier: AGPL-3.0-or-later +> Tversion { tag=NOTAG max_msg_size=8192 version="9P2000" } +< Rversion { tag=NOTAG max_msg_size=4120 version="9P2000" } +> Tauth { tag=0 afid=0 uname="lukeshu" aname="" n_uid=0 } +< Rerror { tag=0 errstr="authentication not required" errnum=95 } +> Tattach { tag=0 fid=0 afid=NOFID uname="lukeshu" aname="" n_uid=0 } +< Rattach { tag=0 qid={ type=(DIR) vers=1 path=1 } } +> Twalk { tag=0 fid=0 newfid=1 nwname=0 wname=[ ] } +< Rwalk { tag=0 nwqid=0 wqid=[ ] } +> Tstat { tag=0 fid=1 } +< Rstat { tag=0 stat={ kern_type=0 kern_dev=0 file_qid={ type=(DIR) vers=1 path=1 } file_mode=(DIR|0555) file_atime=1728337905 file_mtime=1728337904 file_size=0 file_name="" file_owner_uid="root" file_owner_gid="root" file_last_modified_uid="root" file_extension="" file_owner_n_uid=0 file_owner_n_gid=0 file_last_modified_n_uid=0 } } +> Tclunk { tag=0 fid=1 } +< Rclunk { tag=0 } +> Twalk { tag=0 fid=0 newfid=1 nwname=0 wname=[ ] } +< Rwalk { tag=0 nwqid=0 wqid=[ ] } +> Topen { tag=0 fid=1 mode=(MODE_READ) } +< Ropen { tag=0 qid={ type=(DIR) vers=1 path=1 } iounit=0 } +> Tread { tag=0 fid=1 offset=0 count=4096 } +< Rread { tag=0 count=361 data=<bytedata> } +> Tread { tag=0 fid=1 offset=361 count=4096 } +< Rread { tag=0 count=0 data="" } +> Tclunk { tag=0 fid=1 } +< Rclunk { tag=0 } +> Tversion { tag=NOTAG max_msg_size=8192 version="9P2000" } +< Rversion { tag=NOTAG max_msg_size=4120 version="9P2000" } +> Tauth { tag=0 afid=0 uname="lukeshu" aname="" n_uid=0 } +< Rerror { tag=0 errstr="authentication not required" errnum=95 } +> Tattach { tag=0 fid=0 afid=NOFID uname="lukeshu" aname="" n_uid=0 } +< Rattach { tag=0 qid={ type=(DIR) vers=1 path=1 } } +> Twalk { tag=0 fid=0 newfid=1 nwname=1 wname=["Documentation" ] } +< Rwalk { tag=0 nwqid=1 wqid=[{ type=(DIR) vers=1 path=2 } ] } +> Tstat { tag=0 fid=1 } +< Rstat { tag=0 stat={ kern_type=0 kern_dev=0 file_qid={ type=(DIR) vers=1 path=2 } file_mode=(DIR|0555) file_atime=1728337905 file_mtime=1728337904 file_size=0 file_name="Documentation" file_owner_uid="root" file_owner_gid="root" file_last_modified_uid="root" file_extension="" file_owner_n_uid=0 file_owner_n_gid=0 file_last_modified_n_uid=0 } } +> Tclunk { tag=0 fid=1 } +< Rclunk { tag=0 } +> Twalk { tag=0 fid=0 newfid=1 nwname=1 wname=["Documentation" ] } +< Rwalk { tag=0 nwqid=1 wqid=[{ type=(DIR) vers=1 path=2 } ] } +> Topen { tag=0 fid=1 mode=(MODE_READ) } +< Ropen { tag=0 qid={ type=(DIR) vers=1 path=2 } iounit=0 } +> Tread { tag=0 fid=1 offset=0 count=4096 } +< Rread { tag=0 count=62 data=<bytedata> } +> Tread { tag=0 fid=1 offset=62 count=4096 } +< Rread { tag=0 count=0 data="" } +> Tclunk { tag=0 fid=1 } +< Rclunk { tag=0 } +> Tversion { tag=NOTAG max_msg_size=8192 version="9P2000" } +< Rversion { tag=NOTAG max_msg_size=4120 version="9P2000" } +> Tauth { tag=0 afid=0 uname="lukeshu" aname="" n_uid=0 } +< Rerror { tag=0 errstr="authentication not required" errnum=95 } +> Tattach { tag=0 fid=0 afid=NOFID uname="lukeshu" aname="" n_uid=0 } +< Rattach { tag=0 qid={ type=(DIR) vers=1 path=1 } } +> Twalk { tag=0 fid=0 newfid=1 nwname=1 wname=["README.md" ] } +< Rwalk { tag=0 nwqid=1 wqid=[{ type=(0) vers=1 path=4 } ] } +> Topen { tag=0 fid=1 mode=(MODE_READ) } +< Ropen { tag=0 qid={ type=(0) vers=1 path=4 } iounit=0 } +> Tread { tag=0 fid=1 offset=0 count=4096 } +< Rread { tag=0 count=166 data="<!--\n README.md - test static file\n\n Copyright ("... } +> Tread { tag=0 fid=1 offset=166 count=4096 } +< Rread { tag=0 count=0 data="" } +> Tclunk { tag=0 fid=1 } +< Rclunk { tag=0 } +> Tversion { tag=NOTAG max_msg_size=8192 version="9P2000" } +< Rversion { tag=NOTAG max_msg_size=4120 version="9P2000" } +> Tauth { tag=0 afid=0 uname="lukeshu" aname="" n_uid=0 } +< Rerror { tag=0 errstr="authentication not required" errnum=95 } +> Tattach { tag=0 fid=0 afid=NOFID uname="lukeshu" aname="" n_uid=0 } +< Rattach { tag=0 qid={ type=(DIR) vers=1 path=1 } } +> Twalk { tag=0 fid=0 newfid=1 nwname=2 wname=["Documentation", "x" ] } +< Rwalk { tag=0 nwqid=2 wqid=[{ type=(DIR) vers=1 path=2 }, { type=(0) vers=1 path=3 } ] } +> Topen { tag=0 fid=1 mode=(MODE_READ) } +< Ropen { tag=0 qid={ type=(0) vers=1 path=3 } iounit=0 } +> Tread { tag=0 fid=1 offset=0 count=4096 } +< Rread { tag=0 count=166 data="<!--\n Documentation/x.txt - test static file\n\n C"... } +> Tread { tag=0 fid=1 offset=166 count=4096 } +< Rread { tag=0 count=0 data="" } +> Tclunk { tag=0 fid=1 } +< Rclunk { tag=0 } +> Tversion { tag=NOTAG max_msg_size=8192 version="9P2000" } +< Rversion { tag=NOTAG max_msg_size=4120 version="9P2000" } +> Tauth { tag=0 afid=0 uname="lukeshu" aname="" n_uid=0 } +< Rerror { tag=0 errstr="authentication not required" errnum=95 } +> Tattach { tag=0 fid=0 afid=NOFID uname="lukeshu" aname="" n_uid=0 } +< Rattach { tag=0 qid={ type=(DIR) vers=1 path=1 } } +> Twalk { tag=0 fid=0 newfid=1 nwname=2 wname=["Documentation", "x" ] } +< Rwalk { tag=0 nwqid=2 wqid=[{ type=(DIR) vers=1 path=2 }, { type=(0) vers=1 path=3 } ] } +> Tstat { tag=0 fid=1 } +< Rstat { tag=0 stat={ kern_type=0 kern_dev=0 file_qid={ type=(0) vers=1 path=3 } file_mode=(0444) file_atime=1728337905 file_mtime=1728337904 file_size=166 file_name="x" file_owner_uid="root" file_owner_gid="root" file_last_modified_uid="root" file_extension="" file_owner_n_uid=0 file_owner_n_gid=0 file_last_modified_n_uid=0 } } +> Tclunk { tag=0 fid=1 } +< Rclunk { tag=0 } +> Tversion { tag=NOTAG max_msg_size=8192 version="9P2000" } +< Rversion { tag=NOTAG max_msg_size=4120 version="9P2000" } +> Tauth { tag=0 afid=0 uname="lukeshu" aname="" n_uid=0 } +< Rerror { tag=0 errstr="authentication not required" errnum=95 } +> Tattach { tag=0 fid=0 afid=NOFID uname="lukeshu" aname="" n_uid=0 } +< Rattach { tag=0 qid={ type=(DIR) vers=1 path=1 } } +> Twalk { tag=0 fid=0 newfid=1 nwname=1 wname=["shutdown" ] } +< Rwalk { tag=0 nwqid=1 wqid=[{ type=(0) vers=1 path=5 } ] } +> Topen { tag=0 fid=1 mode=(TRUNC|MODE_WRITE) } +< Ropen { tag=0 qid={ type=(0) vers=1 path=5 } iounit=0 } +> Twrite { tag=0 fid=1 offset=0 count=2 data="1\n" } +< Rwrite { tag=0 count=2 } +> Tclunk { tag=0 fid=1 } +< Rclunk { tag=0 } diff --git a/lib9p/tests/testclient-sess.c b/lib9p/tests/testclient-sess.c new file mode 100644 index 0000000..423dc2c --- /dev/null +++ b/lib9p/tests/testclient-sess.c @@ -0,0 +1,144 @@ +/* lib9p/tests/testclient-sess.c - Test the 9P `test_server`'s sessions + * + * Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com> + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +#include <arpa/inet.h> /* for htons(), inet_addr() */ +#include <errno.h> +#include <error.h> +#include <netinet/in.h> /* for struct sockaddr{,_in} */ +#include <stdlib.h> /* for atoi() */ +#include <sys/socket.h> /* for socket(), connect() */ +#include <sys/uio.h> /* for writev() */ +#include <unistd.h> /* for read() */ + +#include <libmisc/assert.h> +#include <libmisc/endian.h> +#include <lib9p/9p.h> + +#define MAX_MSG_SIZE (8*1024) + +static void _send9p(int fd, struct lib9p_ctx *ctx, enum lib9p_msg_type typ, void *body) { + struct lib9p_Tmsg_send_buf buf; + bool err = lib9p_Tmsg_marshal(ctx, typ, body, &buf); + assert(!err); + size_t exp = 0; + for (size_t i = 0; i < buf.iov_cnt; i++) + exp += buf.iov[i].iov_len; + ssize_t act = writev(fd, buf.iov, buf.iov_cnt); + if (act < 0) + error(1, errno, "writev"); + assert((size_t)act == exp); +} + +#define send9p(typ, ...) _send9p(fd, &ctx, LIB9P_TYP_##typ, &((struct lib9p_msg_##typ){ __VA_ARGS__ })) + +static void _recv9p(int fd) { + uint8_t buf[MAX_MSG_SIZE]; + size_t goal = 4; + size_t done = 0; + while (done < goal) { + ssize_t n = read(fd, &buf[done], goal-done); + if (n < 0) + error(1, errno, "read"); + done += n; + } + goal = uint32le_decode(buf); + assert(goal <= MAX_MSG_SIZE); + while (done < goal) { + ssize_t n = read(fd, &buf[done], goal-done); + if (n < 0) + error(1, errno, "read"); + done += n; + } +} + +#define recv9p() _recv9p(fd) + +int main(int argc, char *argv[]) { + if (argc != 2) + error(2, 0, "Usage: %s SERVER_PORT", argv[0]); + uint16_t server_port = atoi(argv[1]); + + union { + struct sockaddr gen; + struct sockaddr_in in; + } server_addr = {}; + server_addr.in.sin_family = AF_INET; + server_addr.in.sin_addr.s_addr = inet_addr("127.0.0.1"); + server_addr.in.sin_port = htons(server_port); + + int fd = socket(AF_INET, SOCK_STREAM, 0); + if (fd < 0) + error(1, errno, "socket"); + if (connect(fd, &server_addr.gen, sizeof(server_addr)) < 0) + error(1, errno, "connect"); + + struct lib9p_ctx ctx = { + .max_msg_size = 16*1024, + }; + + struct lib9p_s wname[1]; + + /* numeric downgrade, unknown ext *************************************/ + send9p(Tversion, .tag=0, .max_msg_size=57, .version=lib9p_str("9P2025.x")); + recv9p(); /* Rversion */ + ctx.version = LIB9P_VER_9P2000; + + /* numeric downgrade, known ext ***************************************/ + send9p(Tversion, .tag=0, .max_msg_size=57, .version=lib9p_str("9P2025.u")); + recv9p(); /* Rversion */ + ctx.version = LIB9P_VER_9P2000_u; + + /* ext version ********************************************************/ + send9p(Tversion, .tag=0, .max_msg_size=57, .version=lib9p_str("9P2000.u")); + recv9p(); /* Rversion */ + ctx.version = LIB9P_VER_9P2000_u; + + /* main session *******************************************************/ + send9p(Tversion, .tag=0, .max_msg_size=(8*1024), .version=lib9p_str("9P2000")); + recv9p(); /* Rversion */ + ctx.version = LIB9P_VER_9P2000; + send9p(Tattach, .tag=0, .fid=0, .afid=LIB9P_FID_NOFID, .uname=lib9p_str("nobody"), .aname=lib9p_str("")); + recv9p(); /* Rattach */ + + /* flush, but original response comes back first */ + wname[0] = lib9p_str("slowread"); send9p(Twalk, .tag=0, .fid=0, .newfid=1, .nwname=1, .wname=wname); + recv9p(); /* Rwalk */ + send9p(Topen, .tag=0, .fid=1, .mode=LIB9P_O_MODE_READ); + recv9p(); /* Ropen */ + send9p(Tread, .tag=1, .fid=1, .offset=0, .count=6); + send9p(Tflush, .tag=2, .oldtag=1); + recv9p(); /* Rread */ + recv9p(); /* Rflush */ + + /* flush, original request is aborted with error */ + wname[0] = lib9p_str("slowread-flushable"); send9p(Twalk, .tag=1, .fid=0, .newfid=2, .nwname=1, .wname=wname); + recv9p(); /* Rwalk */ + send9p(Topen, .tag=0, .fid=2, .mode=LIB9P_O_MODE_READ); + recv9p(); /* Ropen */ + send9p(Tread, .tag=1, .fid=2, .offset=0, .count=6); + send9p(Tflush, .tag=2, .oldtag=1); + recv9p(); /* Rerror */ + recv9p(); /* Rflush */ + + /* flush, unknown tag */ + send9p(Tflush, .tag=0, .oldtag=99); + recv9p(); /* Rflush */ + + /* shutdown ***********************************************************/ + send9p(Tversion, .tag=0, .max_msg_size=(8*1024), .version=lib9p_str("9P2000")); + recv9p(); /* Rversion */ + ctx.version = LIB9P_VER_9P2000; + send9p(Tattach, .tag=0, .fid=0, .afid=LIB9P_FID_NOFID, .uname=lib9p_str("nobody"), .aname=lib9p_str("")); + recv9p(); /* Rattach */ + /* check the newfid==fid case */ + wname[0] = lib9p_str("shutdown"); send9p(Twalk, .tag=0, .fid=0, .newfid=0, .nwname=1, .wname=wname); + recv9p(); /* Rwalk */ + send9p(Topen, .tag=0, .fid=0, .mode=LIB9P_O_MODE_WRITE); + recv9p(); /* Ropen */ + send9p(Twrite, .tag=0, .fid=0, .offset=0, .count=2, .data="1\n"); + recv9p(); /* Rwrite */ + return 0; +} diff --git a/lib9p/tests/testclient-sess.explog b/lib9p/tests/testclient-sess.explog new file mode 100644 index 0000000..b1f3085 --- /dev/null +++ b/lib9p/tests/testclient-sess.explog @@ -0,0 +1,58 @@ +# lib9p/tests/testclient-sess.explog - Expected 9P logfile of testclient-sess.c +# +# Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com> +# SPDX-License-Identifier: AGPL-3.0-or-later + +# numeric downgrade, unknown ext ############################################### +> Tversion { tag=0 max_msg_size=57 version="9P2025.x" } +< Rversion { tag=0 max_msg_size=57 version="9P2000" } + +# numeric downgrade, known ext ################################################# +> Tversion { tag=0 max_msg_size=57 version="9P2025.u" } +< Rversion { tag=0 max_msg_size=57 version="9P2000.u" } + +# ext version ################################################################## +> Tversion { tag=0 max_msg_size=57 version="9P2000.u" } +< Rversion { tag=0 max_msg_size=57 version="9P2000.u" } + +# main session ################################################################# +> Tversion { tag=0 max_msg_size=8192 version="9P2000" } +< Rversion { tag=0 max_msg_size=4120 version="9P2000" } +> Tattach { tag=0 fid=0 afid=NOFID uname="nobody" aname="" n_uid=0 } +< Rattach { tag=0 qid={ type=(DIR) vers=1 path=1 } } + +# flush, but original response comes back first +> Twalk { tag=0 fid=0 newfid=1 nwname=1 wname=["slowread" ] } +< Rwalk { tag=0 nwqid=1 wqid=[{ type=(0) vers=1 path=6 } ] } +> Topen { tag=0 fid=1 mode=(MODE_READ) } +< Ropen { tag=0 qid={ type=(0) vers=1 path=6 } iounit=0 } +> Tread { tag=1 fid=1 offset=0 count=6 } +> Tflush { tag=2 oldtag=1 } +< Rread { tag=1 count=6 data="Sloth\n" } +< Rflush { tag=2 } + +# flush, succeeds +> Twalk { tag=1 fid=0 newfid=2 nwname=1 wname=["slowread-flushable" ] } +< Rwalk { tag=1 nwqid=1 wqid=[{ type=(0) vers=1 path=7 } ] } +> Topen { tag=0 fid=2 mode=(MODE_READ) } +< Ropen { tag=0 qid={ type=(0) vers=1 path=7 } iounit=0 } +> Tread { tag=1 fid=2 offset=0 count=6 } +> Tflush { tag=2 oldtag=1 } +< Rflush { tag=2 } +< Rerror { tag=1 errstr="request canceled by flush" errnum=125 } + +# flush, unknown tag +> Tflush { tag=0 oldtag=99 } +< Rflush { tag=0 } + +# shutdown ##################################################################### +> Tversion { tag=0 max_msg_size=8192 version="9P2000" } +< Rversion { tag=0 max_msg_size=4120 version="9P2000" } +> Tattach { tag=0 fid=0 afid=NOFID uname="nobody" aname="" n_uid=0 } +< Rattach { tag=0 qid={ type=(DIR) vers=1 path=1 } } +> Twalk { tag=0 fid=0 newfid=0 nwname=1 wname=["shutdown" ] } +< Rwalk { tag=0 nwqid=1 wqid=[{ type=(0) vers=1 path=5 } ] } +> Topen { tag=0 fid=0 mode=(MODE_WRITE) } +< Ropen { tag=0 qid={ type=(0) vers=1 path=5 } iounit=0 } +> Twrite { tag=0 fid=0 offset=0 count=2 data="1\n" } +< Rwrite { tag=0 count=2 } 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_ */ |