aboutsummaryrefslogtreecommitdiff
path: root/contrib/lib9p/lib9p.h
blob: 79b741c9888796f3c2f6591e5c5236176eccb48a (plain) (blame)
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
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
/*
 * Copyright 2016 Jakub Klama <jceel@FreeBSD.org>
 * All rights reserved
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted providing that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 */


#ifndef LIB9P_LIB9P_H
#define LIB9P_LIB9P_H

#include <stdbool.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/queue.h>
#include <sys/uio.h>
#include <pthread.h>

#if defined(__FreeBSD__)
#include <sys/sbuf.h>
#else
#include "sbuf/sbuf.h"
#endif

#include "fcall.h"
#include "threadpool.h"
#include "hashtable.h"

#define L9P_DEFAULT_MSIZE   8192
#define L9P_MAX_IOV         128
#define	L9P_NUMTHREADS      8

struct l9p_request;
struct l9p_backend;
struct l9p_fid;

/*
 * Functions to implement underlying transport for lib9p.
 *
 * The transport is responsible for:
 *
 *   - allocating a response buffer (filling in the iovec and niov)
 *     (gets req, pointer to base of iov array of size L9P_MAX_IOV,
 *      pointer to niov, lt_aux)
 *
 *   - sending a response, when a request has a reply ready
 *     (gets req, pointer to iov, niov, actual response length, lt_aux)
 *
 *   - dropping the response buffer, when a request has been
 *     flushed or otherwise dropped without a response
 *     (gets req, pointer to iov, niov, lt_aux)
 *
 * The transport is of course also responsible for feeding in
 * request-buffers, but that happens by the transport calling
 * l9p_connection_recv().
 */
struct l9p_transport {
	void *lt_aux;
	int (*lt_get_response_buffer)(struct l9p_request *, struct iovec *,
	    size_t *, void *);
	int (*lt_send_response)(struct l9p_request *, const struct iovec *,
	    size_t, size_t, void *);
	void (*lt_drop_response)(struct l9p_request *, const struct iovec *,
	    size_t, void *);
};

enum l9p_pack_mode {
	L9P_PACK,
	L9P_UNPACK
};

enum l9p_integer_type {
	L9P_BYTE = 1,
	L9P_WORD = 2,
	L9P_DWORD = 4,
	L9P_QWORD = 8
};

enum l9p_version {
	L9P_INVALID_VERSION = 0,
	L9P_2000 = 1,
	L9P_2000U = 2,
	L9P_2000L = 3
};

/*
 * This structure is used for unpacking (decoding) incoming
 * requests and packing (encoding) outgoing results.  It has its
 * own copy of the iov array, with its own counters for working
 * through that array, but it borrows the actual DATA from the
 * original iov array associated with the original request (see
 * below).
 */
struct l9p_message {
	enum l9p_pack_mode lm_mode;
	struct iovec lm_iov[L9P_MAX_IOV];
	size_t lm_niov;
	size_t lm_cursor_iov;
	size_t lm_cursor_offset;
	size_t lm_size;
};

/*
 * Data structure for a request/response pair (Tfoo/Rfoo).
 *
 * Note that the response is not formatted out into raw data
 * (overwriting the request raw data) until we are really
 * responding, with the exception of read operations Tread
 * and Treaddir, which overlay their result-data into the
 * iov array in the process of reading.
 *
 * We have room for two incoming fids, in case we are
 * using 9P2000.L protocol.  Note that nothing that uses two
 * fids also has an output fid (newfid), so we could have a
 * union of lr_fid2 and lr_newfid, but keeping them separate
 * is probably a bit less error-prone.  (If we want to shave
 * memory requirements there are more places to look.)
 *
 * (The fid, fid2, and newfid fields should be removed via
 * reorganization, as they are only used for smuggling data
 * between request.c and the backend and should just be
 * parameters to backend ops.)
 */
struct l9p_request {
	struct l9p_message lr_req_msg;	/* for unpacking the request */
	struct l9p_message lr_resp_msg;	/* for packing the response */
	union l9p_fcall lr_req;		/* the request, decoded/unpacked */
	union l9p_fcall lr_resp;	/* the response, not yet packed */

	struct l9p_fid *lr_fid;
	struct l9p_fid *lr_fid2;
	struct l9p_fid *lr_newfid;

	struct l9p_connection *lr_conn;	/* containing connection */
	void *lr_aux;			/* reserved for transport layer */

	struct iovec lr_data_iov[L9P_MAX_IOV];	/* iovecs for req + resp */
	size_t lr_data_niov;			/* actual size of data_iov */

	int lr_error;			/* result from l9p_dispatch_request */

	/* proteced by threadpool mutex */
	enum l9p_workstate lr_workstate;	/* threadpool: work state */
	enum l9p_flushstate lr_flushstate;	/* flush state if flushee */
	struct l9p_worker *lr_worker;		/* threadpool: worker */
	STAILQ_ENTRY(l9p_request) lr_worklink;	/* reserved to threadpool */

	/* protected by tag hash table lock */
	struct l9p_request_queue lr_flushq;	/* q of flushers */
	STAILQ_ENTRY(l9p_request) lr_flushlink;	/* link w/in flush queue */
};

/* N.B.: these dirents are variable length and for .L only */
struct l9p_dirent {
	struct l9p_qid qid;
	uint64_t offset;
	uint8_t type;
	char *name;
};

/*
 * The 9pfs protocol has the notion of a "session", which is
 * traffic between any two "Tversion" requests.  All fids
 * (lc_files, below) are specific to one particular session.
 *
 * We need a data structure per connection (client/server
 * pair). This data structure lasts longer than these 9pfs
 * sessions, but contains the request/response pairs and fids.
 * Logically, the per-session data should be separate, but
 * most of the time that would just require an extra
 * indirection.  Instead, a new session simply clunks all
 * fids, and otherwise keeps using this same connection.
 */
struct l9p_connection {
	struct l9p_server *lc_server;
	struct l9p_transport lc_lt;
	struct l9p_threadpool lc_tp;
	enum l9p_version lc_version;
	uint32_t lc_msize;
	uint32_t lc_max_io_size;
	struct ht lc_files;
	struct ht lc_requests;
	LIST_ENTRY(l9p_connection) lc_link;
};

struct l9p_server {
	struct l9p_backend *ls_backend;
	enum l9p_version ls_max_version;
	LIST_HEAD(, l9p_connection) ls_conns;
};

int l9p_pufcall(struct l9p_message *msg, union l9p_fcall *fcall,
    enum l9p_version version);
ssize_t l9p_pustat(struct l9p_message *msg, struct l9p_stat *s,
    enum l9p_version version);
uint16_t l9p_sizeof_stat(struct l9p_stat *stat, enum l9p_version version);
int l9p_pack_stat(struct l9p_message *msg, struct l9p_request *req,
    struct l9p_stat *s);
ssize_t l9p_pudirent(struct l9p_message *msg, struct l9p_dirent *de);

int l9p_server_init(struct l9p_server **serverp, struct l9p_backend *backend);

int l9p_connection_init(struct l9p_server *server,
    struct l9p_connection **connp);
void l9p_connection_free(struct l9p_connection *conn);
void l9p_connection_recv(struct l9p_connection *conn, const struct iovec *iov,
    size_t niov, void *aux);
void l9p_connection_close(struct l9p_connection *conn);
struct l9p_fid *l9p_connection_alloc_fid(struct l9p_connection *conn,
    uint32_t fid);
void l9p_connection_remove_fid(struct l9p_connection *conn,
    struct l9p_fid *fid);

int l9p_dispatch_request(struct l9p_request *req);
void l9p_respond(struct l9p_request *req, bool drop, bool rmtag);

void l9p_init_msg(struct l9p_message *msg, struct l9p_request *req,
    enum l9p_pack_mode mode);
void l9p_seek_iov(struct iovec *iov1, size_t niov1, struct iovec *iov2,
    size_t *niov2, size_t seek);
size_t l9p_truncate_iov(struct iovec *iov, size_t niov, size_t length);
void l9p_describe_fcall(union l9p_fcall *fcall, enum l9p_version version,
    struct sbuf *sb);
void l9p_freefcall(union l9p_fcall *fcall);
void l9p_freestat(struct l9p_stat *stat);

gid_t *l9p_getgrlist(const char *, gid_t, int *);

#endif  /* LIB9P_LIB9P_H */