summaryrefslogtreecommitdiff
path: root/lib9p/include/lib9p/9p.h
blob: bf304ac6d4fef322ccaae1ae9d43928fdce22751 (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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
/* lib9p/9p.h - Base 9P protocol definitions for both clients and servers
 *
 * Copyright (C) 2024  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 "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 */
#ifdef CONFIG_9P_ENABLE_9P2000_u
	uint32_t                err_num;
#endif
	char                    err_msg[CONFIG_9P_MAX_ERR_SIZE];
};

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

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

const char *lib9p_msg_type_str(struct lib9p_ctx *, enum lib9p_msg_type);

/** 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         )) );
	assert( (stat.file_size == 0) || !(stat.file_mode & LIB9P_DM_DIR) );
}

/** 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 ctx->max_msg_size 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);

/**
 * TODO
 *
 * @return ret_net_size: number of bytes consumed; <=net_size
 * @return ret_host_size: number of bytes that lib9p_unmarshal_stat 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);

/**
 * TODO
 *
 * @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
 */
uint32_t lib9p_unmarshal_stat(struct lib9p_ctx *ctx, uint8_t *net_bytes,
                              struct lib9p_stat *ret_obj, void *ret_extra);

/**
 * @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
 */
uint32_t lib9p_marshal_stat(struct lib9p_ctx *ctx, uint32_t max_net_size, struct lib9p_stat *obj,
                            uint8_t *ret_bytes);

#endif /* _LIB9P_9P_H_ */