/*1* Copyright 2016 Jakub Klama <[email protected]>2* All rights reserved3*4* Redistribution and use in source and binary forms, with or without5* modification, are permitted providing that the following conditions6* are met:7* 1. Redistributions of source code must retain the above copyright8* notice, this list of conditions and the following disclaimer.9* 2. Redistributions in binary form must reproduce the above copyright10* notice, this list of conditions and the following disclaimer in the11* documentation and/or other materials provided with the distribution.12*13* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR14* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED15* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE16* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY17* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL18* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS19* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)20* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,21* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING22* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE23* POSSIBILITY OF SUCH DAMAGE.24*25*/262728#ifndef LIB9P_LIB9P_H29#define LIB9P_LIB9P_H3031#include <stdbool.h>32#include <stdio.h>33#include <sys/types.h>34#include <sys/queue.h>35#include <sys/uio.h>36#include <pthread.h>3738#if defined(__FreeBSD__)39#include <sys/sbuf.h>40#else41#include "sbuf/sbuf.h"42#endif4344#include "fcall.h"45#include "threadpool.h"46#include "hashtable.h"4748#define L9P_DEFAULT_MSIZE 819249#define L9P_MAX_IOV 12850#define L9P_NUMTHREADS 85152struct l9p_request;53struct l9p_backend;54struct l9p_fid;5556/*57* Functions to implement underlying transport for lib9p.58*59* The transport is responsible for:60*61* - allocating a response buffer (filling in the iovec and niov)62* (gets req, pointer to base of iov array of size L9P_MAX_IOV,63* pointer to niov, lt_aux)64*65* - sending a response, when a request has a reply ready66* (gets req, pointer to iov, niov, actual response length, lt_aux)67*68* - dropping the response buffer, when a request has been69* flushed or otherwise dropped without a response70* (gets req, pointer to iov, niov, lt_aux)71*72* The transport is of course also responsible for feeding in73* request-buffers, but that happens by the transport calling74* l9p_connection_recv().75*/76struct l9p_transport {77void *lt_aux;78int (*lt_get_response_buffer)(struct l9p_request *, struct iovec *,79size_t *, void *);80int (*lt_send_response)(struct l9p_request *, const struct iovec *,81size_t, size_t, void *);82void (*lt_drop_response)(struct l9p_request *, const struct iovec *,83size_t, void *);84};8586enum l9p_pack_mode {87L9P_PACK,88L9P_UNPACK89};9091enum l9p_integer_type {92L9P_BYTE = 1,93L9P_WORD = 2,94L9P_DWORD = 4,95L9P_QWORD = 896};9798enum l9p_version {99L9P_INVALID_VERSION = 0,100L9P_2000 = 1,101L9P_2000U = 2,102L9P_2000L = 3103};104105/*106* This structure is used for unpacking (decoding) incoming107* requests and packing (encoding) outgoing results. It has its108* own copy of the iov array, with its own counters for working109* through that array, but it borrows the actual DATA from the110* original iov array associated with the original request (see111* below).112*/113struct l9p_message {114enum l9p_pack_mode lm_mode;115struct iovec lm_iov[L9P_MAX_IOV];116size_t lm_niov;117size_t lm_cursor_iov;118size_t lm_cursor_offset;119size_t lm_size;120};121122/*123* Data structure for a request/response pair (Tfoo/Rfoo).124*125* Note that the response is not formatted out into raw data126* (overwriting the request raw data) until we are really127* responding, with the exception of read operations Tread128* and Treaddir, which overlay their result-data into the129* iov array in the process of reading.130*131* We have room for two incoming fids, in case we are132* using 9P2000.L protocol. Note that nothing that uses two133* fids also has an output fid (newfid), so we could have a134* union of lr_fid2 and lr_newfid, but keeping them separate135* is probably a bit less error-prone. (If we want to shave136* memory requirements there are more places to look.)137*138* (The fid, fid2, and newfid fields should be removed via139* reorganization, as they are only used for smuggling data140* between request.c and the backend and should just be141* parameters to backend ops.)142*/143struct l9p_request {144struct l9p_message lr_req_msg; /* for unpacking the request */145struct l9p_message lr_resp_msg; /* for packing the response */146union l9p_fcall lr_req; /* the request, decoded/unpacked */147union l9p_fcall lr_resp; /* the response, not yet packed */148149struct l9p_fid *lr_fid;150struct l9p_fid *lr_fid2;151struct l9p_fid *lr_newfid;152153struct l9p_connection *lr_conn; /* containing connection */154void *lr_aux; /* reserved for transport layer */155156struct iovec lr_data_iov[L9P_MAX_IOV]; /* iovecs for req + resp */157size_t lr_data_niov; /* actual size of data_iov */158159int lr_error; /* result from l9p_dispatch_request */160161/* proteced by threadpool mutex */162enum l9p_workstate lr_workstate; /* threadpool: work state */163enum l9p_flushstate lr_flushstate; /* flush state if flushee */164struct l9p_worker *lr_worker; /* threadpool: worker */165STAILQ_ENTRY(l9p_request) lr_worklink; /* reserved to threadpool */166167/* protected by tag hash table lock */168struct l9p_request_queue lr_flushq; /* q of flushers */169STAILQ_ENTRY(l9p_request) lr_flushlink; /* link w/in flush queue */170};171172/* N.B.: these dirents are variable length and for .L only */173struct l9p_dirent {174struct l9p_qid qid;175uint64_t offset;176uint8_t type;177char *name;178};179180/*181* The 9pfs protocol has the notion of a "session", which is182* traffic between any two "Tversion" requests. All fids183* (lc_files, below) are specific to one particular session.184*185* We need a data structure per connection (client/server186* pair). This data structure lasts longer than these 9pfs187* sessions, but contains the request/response pairs and fids.188* Logically, the per-session data should be separate, but189* most of the time that would just require an extra190* indirection. Instead, a new session simply clunks all191* fids, and otherwise keeps using this same connection.192*/193struct l9p_connection {194struct l9p_server *lc_server;195struct l9p_transport lc_lt;196struct l9p_threadpool lc_tp;197enum l9p_version lc_version;198uint32_t lc_msize;199uint32_t lc_max_io_size;200struct ht lc_files;201struct ht lc_requests;202LIST_ENTRY(l9p_connection) lc_link;203};204205struct l9p_server {206struct l9p_backend *ls_backend;207enum l9p_version ls_max_version;208LIST_HEAD(, l9p_connection) ls_conns;209};210211int l9p_pufcall(struct l9p_message *msg, union l9p_fcall *fcall,212enum l9p_version version);213ssize_t l9p_pustat(struct l9p_message *msg, struct l9p_stat *s,214enum l9p_version version);215uint16_t l9p_sizeof_stat(struct l9p_stat *stat, enum l9p_version version);216int l9p_pack_stat(struct l9p_message *msg, struct l9p_request *req,217struct l9p_stat *s);218ssize_t l9p_pudirent(struct l9p_message *msg, struct l9p_dirent *de);219220int l9p_server_init(struct l9p_server **serverp, struct l9p_backend *backend);221222int l9p_connection_init(struct l9p_server *server,223struct l9p_connection **connp);224void l9p_connection_free(struct l9p_connection *conn);225void l9p_connection_recv(struct l9p_connection *conn, const struct iovec *iov,226size_t niov, void *aux);227void l9p_connection_close(struct l9p_connection *conn);228struct l9p_fid *l9p_connection_alloc_fid(struct l9p_connection *conn,229uint32_t fid);230void l9p_connection_remove_fid(struct l9p_connection *conn,231struct l9p_fid *fid);232233int l9p_dispatch_request(struct l9p_request *req);234void l9p_respond(struct l9p_request *req, bool drop, bool rmtag);235236void l9p_init_msg(struct l9p_message *msg, struct l9p_request *req,237enum l9p_pack_mode mode);238void l9p_seek_iov(struct iovec *iov1, size_t niov1, struct iovec *iov2,239size_t *niov2, size_t seek);240size_t l9p_truncate_iov(struct iovec *iov, size_t niov, size_t length);241void l9p_describe_fcall(union l9p_fcall *fcall, enum l9p_version version,242struct sbuf *sb);243void l9p_freefcall(union l9p_fcall *fcall);244void l9p_freestat(struct l9p_stat *stat);245246gid_t *l9p_getgrlist(const char *, gid_t, int *);247248#endif /* LIB9P_LIB9P_H */249250251