1/*
2 * Copyright 2016 Jakub Klama <jceel@FreeBSD.org>
3 * All rights reserved
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted providing that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
18 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
22 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
23 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24 * POSSIBILITY OF SUCH DAMAGE.
25 *
26 */
27
28
29#ifndef LIB9P_LIB9P_H
30#define LIB9P_LIB9P_H
31
32#include <stdbool.h>
33#include <stdio.h>
34#include <sys/types.h>
35#include <sys/queue.h>
36#include <sys/uio.h>
37#include <pthread.h>
38
39#if defined(__FreeBSD__)
40#include <sys/sbuf.h>
41#else
42#include "sbuf/sbuf.h"
43#endif
44
45#include "fcall.h"
46#include "threadpool.h"
47#include "hashtable.h"
48
49#define L9P_DEFAULT_MSIZE   8192
50#define L9P_MAX_IOV         128
51#define	L9P_NUMTHREADS      8
52
53struct l9p_request;
54struct l9p_backend;
55struct l9p_fid;
56
57/*
58 * Functions to implement underlying transport for lib9p.
59 *
60 * The transport is responsible for:
61 *
62 *   - allocating a response buffer (filling in the iovec and niov)
63 *     (gets req, pointer to base of iov array of size L9P_MAX_IOV,
64 *      pointer to niov, lt_aux)
65 *
66 *   - sending a response, when a request has a reply ready
67 *     (gets req, pointer to iov, niov, actual response length, lt_aux)
68 *
69 *   - dropping the response buffer, when a request has been
70 *     flushed or otherwise dropped without a response
71 *     (gets req, pointer to iov, niov, lt_aux)
72 *
73 * The transport is of course also responsible for feeding in
74 * request-buffers, but that happens by the transport calling
75 * l9p_connection_recv().
76 */
77struct l9p_transport {
78	void *lt_aux;
79	int (*lt_get_response_buffer)(struct l9p_request *, struct iovec *,
80	    size_t *, void *);
81	int (*lt_send_response)(struct l9p_request *, const struct iovec *,
82	    size_t, size_t, void *);
83	void (*lt_drop_response)(struct l9p_request *, const struct iovec *,
84	    size_t, void *);
85};
86
87enum l9p_pack_mode {
88	L9P_PACK,
89	L9P_UNPACK
90};
91
92enum l9p_integer_type {
93	L9P_BYTE = 1,
94	L9P_WORD = 2,
95	L9P_DWORD = 4,
96	L9P_QWORD = 8
97};
98
99enum l9p_version {
100	L9P_INVALID_VERSION = 0,
101	L9P_2000 = 1,
102	L9P_2000U = 2,
103	L9P_2000L = 3
104};
105
106/*
107 * This structure is used for unpacking (decoding) incoming
108 * requests and packing (encoding) outgoing results.  It has its
109 * own copy of the iov array, with its own counters for working
110 * through that array, but it borrows the actual DATA from the
111 * original iov array associated with the original request (see
112 * below).
113 */
114struct l9p_message {
115	enum l9p_pack_mode lm_mode;
116	struct iovec lm_iov[L9P_MAX_IOV];
117	size_t lm_niov;
118	size_t lm_cursor_iov;
119	size_t lm_cursor_offset;
120	size_t lm_size;
121};
122
123/*
124 * Data structure for a request/response pair (Tfoo/Rfoo).
125 *
126 * Note that the response is not formatted out into raw data
127 * (overwriting the request raw data) until we are really
128 * responding, with the exception of read operations Tread
129 * and Treaddir, which overlay their result-data into the
130 * iov array in the process of reading.
131 *
132 * We have room for two incoming fids, in case we are
133 * using 9P2000.L protocol.  Note that nothing that uses two
134 * fids also has an output fid (newfid), so we could have a
135 * union of lr_fid2 and lr_newfid, but keeping them separate
136 * is probably a bit less error-prone.  (If we want to shave
137 * memory requirements there are more places to look.)
138 *
139 * (The fid, fid2, and newfid fields should be removed via
140 * reorganization, as they are only used for smuggling data
141 * between request.c and the backend and should just be
142 * parameters to backend ops.)
143 */
144struct l9p_request {
145	struct l9p_message lr_req_msg;	/* for unpacking the request */
146	struct l9p_message lr_resp_msg;	/* for packing the response */
147	union l9p_fcall lr_req;		/* the request, decoded/unpacked */
148	union l9p_fcall lr_resp;	/* the response, not yet packed */
149
150	struct l9p_fid *lr_fid;
151	struct l9p_fid *lr_fid2;
152	struct l9p_fid *lr_newfid;
153
154	struct l9p_connection *lr_conn;	/* containing connection */
155	void *lr_aux;			/* reserved for transport layer */
156
157	struct iovec lr_data_iov[L9P_MAX_IOV];	/* iovecs for req + resp */
158	size_t lr_data_niov;			/* actual size of data_iov */
159
160	int lr_error;			/* result from l9p_dispatch_request */
161
162	/* proteced by threadpool mutex */
163	enum l9p_workstate lr_workstate;	/* threadpool: work state */
164	enum l9p_flushstate lr_flushstate;	/* flush state if flushee */
165	struct l9p_worker *lr_worker;		/* threadpool: worker */
166	STAILQ_ENTRY(l9p_request) lr_worklink;	/* reserved to threadpool */
167
168	/* protected by tag hash table lock */
169	struct l9p_request_queue lr_flushq;	/* q of flushers */
170	STAILQ_ENTRY(l9p_request) lr_flushlink;	/* link w/in flush queue */
171};
172
173/* N.B.: these dirents are variable length and for .L only */
174struct l9p_dirent {
175	struct l9p_qid qid;
176	uint64_t offset;
177	uint8_t type;
178	char *name;
179};
180
181/*
182 * The 9pfs protocol has the notion of a "session", which is
183 * traffic between any two "Tversion" requests.  All fids
184 * (lc_files, below) are specific to one particular session.
185 *
186 * We need a data structure per connection (client/server
187 * pair). This data structure lasts longer than these 9pfs
188 * sessions, but contains the request/response pairs and fids.
189 * Logically, the per-session data should be separate, but
190 * most of the time that would just require an extra
191 * indirection.  Instead, a new session simply clunks all
192 * fids, and otherwise keeps using this same connection.
193 */
194struct l9p_connection {
195	struct l9p_server *lc_server;
196	struct l9p_transport lc_lt;
197	struct l9p_threadpool lc_tp;
198	enum l9p_version lc_version;
199	uint32_t lc_msize;
200	uint32_t lc_max_io_size;
201	struct ht lc_files;
202	struct ht lc_requests;
203	LIST_ENTRY(l9p_connection) lc_link;
204};
205
206struct l9p_server {
207	struct l9p_backend *ls_backend;
208	enum l9p_version ls_max_version;
209	LIST_HEAD(, l9p_connection) ls_conns;
210};
211
212int l9p_pufcall(struct l9p_message *msg, union l9p_fcall *fcall,
213    enum l9p_version version);
214ssize_t l9p_pustat(struct l9p_message *msg, struct l9p_stat *s,
215    enum l9p_version version);
216uint16_t l9p_sizeof_stat(struct l9p_stat *stat, enum l9p_version version);
217int l9p_pack_stat(struct l9p_message *msg, struct l9p_request *req,
218    struct l9p_stat *s);
219ssize_t l9p_pudirent(struct l9p_message *msg, struct l9p_dirent *de);
220
221int l9p_server_init(struct l9p_server **serverp, struct l9p_backend *backend);
222
223int l9p_connection_init(struct l9p_server *server,
224    struct l9p_connection **connp);
225void l9p_connection_free(struct l9p_connection *conn);
226void l9p_connection_recv(struct l9p_connection *conn, const struct iovec *iov,
227    size_t niov, void *aux);
228void l9p_connection_close(struct l9p_connection *conn);
229struct l9p_fid *l9p_connection_alloc_fid(struct l9p_connection *conn,
230    uint32_t fid);
231void l9p_connection_remove_fid(struct l9p_connection *conn,
232    struct l9p_fid *fid);
233
234int l9p_dispatch_request(struct l9p_request *req);
235void l9p_respond(struct l9p_request *req, bool drop, bool rmtag);
236
237void l9p_init_msg(struct l9p_message *msg, struct l9p_request *req,
238    enum l9p_pack_mode mode);
239void l9p_seek_iov(struct iovec *iov1, size_t niov1, struct iovec *iov2,
240    size_t *niov2, size_t seek);
241size_t l9p_truncate_iov(struct iovec *iov, size_t niov, size_t length);
242void l9p_describe_fcall(union l9p_fcall *fcall, enum l9p_version version,
243    struct sbuf *sb);
244void l9p_freefcall(union l9p_fcall *fcall);
245void l9p_freestat(struct l9p_stat *stat);
246
247gid_t *l9p_getgrlist(const char *, gid_t, int *);
248
249#endif  /* LIB9P_LIB9P_H */
250