summaryrefslogtreecommitdiff
path: root/lib9p/include/lib9p/9p.h
diff options
context:
space:
mode:
Diffstat (limited to 'lib9p/include/lib9p/9p.h')
-rw-r--r--lib9p/include/lib9p/9p.h153
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_ */