summaryrefslogtreecommitdiff
path: root/lib9p/include/lib9p/9p.h
blob: c3c6a8bbcbcb82d32f1bc744398afddf4e794c1e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
/* lib9p/9p.h - Base 9P protocol definitions for both clients and servers
 *
 * Copyright (C) 2024  Luke T. Shumaker <lukeshu@lukeshu.com>
 * SPDX-Licence-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 "config.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

/******************************************************************************/

#include <lib9p/9p.generated.h> /* *after* config.h */

#define LIB9P_NOTAG ((uint16_t)~0U)
#define LIB9P_NOFID ((uint32_t)~0U)

struct lib9p_ctx {
	/* negotiated */
	enum lib9p_version      version;
	uint32_t                max_msg_size;

	/* state */
	uint32_t                err_num;
	char                    err_msg[CONFIG_9P_MAX_ERR_SIZE];
};

static void lib9p_ctx_clear_error(struct lib9p_ctx *ctx) {
	assert(ctx);
	ctx->err_num = 0;
	ctx->err_msg[0] = '\0';
}

static bool lib9p_ctx_has_error(struct lib9p_ctx *ctx) {
	assert(ctx);
	return ctx->err_num || ctx->err_msg[0];
}

/** Write an static error into ctx, return -1.  */
int lib9p_error(struct lib9p_ctx *ctx, uint32_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, ...);

/**
 * Validate a message's structure; it's 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.
 *
 * 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
 * for that message type, or if an invalid string (invalid UTF-8,
 * contains a nul-byte) is encountered.
 *
 * @param net_bytes : the complete request, starting with the "size[4]"
 *
 * @return required size, or -1 on error
 *
 * @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);

/**
 * 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.
 *
 * @param ctx : negotiated protocol parameters, where to record errors
 * @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
 */
void lib9p_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
 * version-specific mask to bitfields.
 *
 * @param ctx : negotiated protocol parameters, where to record errors
 * @param typ : the message type
 * @param msg : the message to encode
 *
 * @return ret_bytes : the buffer to encode to, must be at be at least lib9p_ctx_max_msg_size(ctx) bytes
 * @return whether there was an error (false=success, true=error)
 *
 * @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);

#endif _LIB9P_9P_H_