diff options
Diffstat (limited to 'lib9p/include/lib9p/9p.h')
-rw-r--r-- | lib9p/include/lib9p/9p.h | 153 |
1 files changed, 97 insertions, 56 deletions
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_ */ |