A bunch of changes described below.

- Initial support for 9P2000.u protocol
- Initial write support.
- Switch from lists to hashtable for storing tags and fids
- Make logging optional (dependent on compile-time constant)
- Drain tags and fids on connection close.
This commit is contained in:
Jakub Klama 2016-01-31 01:56:11 +01:00
parent ac0155dd1b
commit 3baea0c48f
6 changed files with 927 additions and 665 deletions

View file

@ -38,12 +38,19 @@
#include <assert.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <sys/queue.h>
#include <dirent.h>
#include <pwd.h>
#include <grp.h>
#include <libgen.h>
#include "../lib9p.h"
#include "../lib9p_impl.h"
#include "../log.h"
static struct openfile *open_fid(const char *);
static void dostat(struct l9p_stat *, char *, struct stat *);
static void generate_qid(struct stat *, struct l9p_qid *);
static void fs_attach(void *, struct l9p_request *);
static void fs_clunk(void *, struct l9p_request *);
static void fs_create(void *, struct l9p_request *);
@ -60,6 +67,15 @@ struct fs_softc
{
const char *fs_rootpath;
bool fs_readonly;
TAILQ_HEAD(, fs_tree) fs_auxtrees;
};
struct fs_tree
{
const char *fst_name;
const char *fst_path;
bool fst_readonly;
TAILQ_ENTRY(fs_tree) fst_link;
};
struct openfile
@ -67,6 +83,8 @@ struct openfile
DIR *dir;
int fd;
char *name;
uid_t uid;
gid_t gid;
};
static struct openfile *
@ -74,7 +92,7 @@ open_fid(const char *path)
{
struct openfile *ret;
ret = calloc(1, sizeof(*ret));
ret = l9p_calloc(1, sizeof(*ret));
ret->fd = -1;
ret->name = strdup(path);
return (ret);
@ -83,48 +101,128 @@ open_fid(const char *path)
static void
dostat(struct l9p_stat *s, char *name, struct stat *buf)
{
char *user = getenv("USER");
struct passwd *user;
struct group *group;
user = getpwuid(buf->st_uid);
group = getgrgid(buf->st_gid);
generate_qid(buf, &s->qid);
s->type = 0;
s->dev = 0;
s->qid.type = buf->st_mode & S_IFMT >> 8;
s->qid.path = buf->st_ino;
s->qid.version = 0;
s->mode = buf->st_mode & 0777;
if (S_ISDIR(buf->st_mode)) {
if (S_ISDIR(buf->st_mode))
s->mode |= L9P_DMDIR;
s->qid.type |= L9P_QTDIR;
}
s->atime = buf->st_atime;
s->mtime = buf->st_mtime;
s->length = buf->st_size;
s->name = name;
s->uid = user;
s->gid = user;
s->muid = user;
s->uid = user != NULL ? user->pw_name : "";
s->gid = group != NULL ? group->gr_name : "";
s->muid = user != NULL ? user->pw_name : "";
s->n_uid = buf->st_uid;
s->n_gid = buf->st_gid;
s->n_muid = buf->st_uid;
}
static void
fs_attach(void *softc, struct l9p_request *req)
generate_qid(struct stat *buf, struct l9p_qid *qid)
{
struct fs_softc *sc = (struct fs_softc *)softc;
qid->path = buf->st_ino;
qid->version = 0;
if (S_ISDIR(buf->st_mode))
qid->type |= L9P_QTDIR;
}
req->lr_fid->lo_qid.type = L9P_QTDIR;
req->lr_fid->lo_qid.path = (uintptr_t)req->lr_fid;
req->lr_fid->lo_aux = open_fid(sc->fs_rootpath);
req->lr_resp.rattach.qid = req->lr_fid->lo_qid;
static bool
check_access(struct stat *st, uid_t uid, int amode)
{
struct passwd *pwd;
int groups[NGROUPS_MAX];
int ngroups = NGROUPS_MAX;
int i;
if (uid == 0)
return (true);
if (st->st_uid == uid) {
if (amode == L9P_OREAD && st->st_mode & S_IRUSR)
return (true);
if (amode == L9P_OWRITE && st->st_mode & S_IWUSR)
return (true);
if (amode == L9P_OEXEC && st->st_mode & S_IXUSR)
return (true);
}
l9p_respond(req, NULL);
/* Check for "other" access */
if (amode == L9P_OREAD && st->st_mode & S_IROTH)
return (true);
if (amode == L9P_OWRITE && st->st_mode & S_IWOTH)
return (true);
if (amode == L9P_OEXEC && st->st_mode & S_IXOTH)
return (true);
/* Check for group access */
pwd = getpwuid(uid);
getgrouplist(pwd->pw_name, pwd->pw_gid, groups, &ngroups);
for (i = 0; i < ngroups; i++) {
if (st->st_gid == (gid_t)groups[i]) {
if (amode == L9P_OREAD && st->st_mode & S_IRGRP)
return (true);
if (amode == L9P_OWRITE && st->st_mode & S_IWGRP)
return (true);
if (amode == L9P_OEXEC && st->st_mode & S_IXGRP)
return (true);
}
}
return (false);
}
static void
fs_clunk(void *softc, struct l9p_request *req)
fs_attach(void *softc, struct l9p_request *req)
{
struct fs_softc *sc = (struct fs_softc *)softc;
struct openfile *file;
uid_t uid;
assert(req->lr_fid != NULL);
file = open_fid(sc->fs_rootpath);
req->lr_fid->lo_qid.type = L9P_QTDIR;
req->lr_fid->lo_qid.path = (uintptr_t)req->lr_fid;
req->lr_fid->lo_aux = file;
req->lr_resp.rattach.qid = req->lr_fid->lo_qid;
uid = req->lr_req.tattach.n_uname;
if (req->lr_conn->lc_version >= L9P_2000U && uid != (uid_t)-1) {
struct passwd *pwd = getpwuid(uid);
if (pwd == NULL) {
l9p_respond(req, EPERM);
return;
}
file->uid = pwd->pw_uid;
file->gid = pwd->pw_gid;
}
l9p_respond(req, 0);
}
static void
fs_clunk(void *softc __unused, struct l9p_request *req)
{
struct l9p_connection *conn = req->lr_conn;
struct openfile *file;
struct stat st;
file = req->lr_fid->lo_aux;
assert(file != NULL);
@ -141,6 +239,54 @@ fs_clunk(void *softc, struct l9p_request *req)
static void
fs_create(void *softc, struct l9p_request *req)
{
struct fs_softc *sc = softc;
struct openfile *file = req->lr_fid->lo_aux;
struct stat st;
char *newname;
assert(file != NULL);
if (sc->fs_readonly) {
l9p_respond(req, EROFS);
return;
}
asprintf(&newname, "%s/%s", file->name, req->lr_req.tcreate.name);
if (stat(file->name, &st) != 0) {
l9p_respond(req, errno);
return;
}
if (!check_access(&st, file->uid, L9P_OWRITE)) {
l9p_respond(req, EPERM);
return;
}
if (req->lr_req.tcreate.perm & L9P_DMDIR) {
mkdir(newname, 0777);
} else {
file->fd = open(newname, O_CREAT | O_TRUNC | req->lr_req.tcreate.mode, req->lr_req.tcreate.perm);
}
if (chown(newname, file->uid, file->gid) != 0) {
l9p_respond(req, errno);
return;
}
l9p_respond(req, 0);
}
static void
fs_flush(void *softc __unused, struct l9p_request *req)
{
l9p_respond(req, 0);
}
static void
fs_open(void *softc __unused, struct l9p_request *req)
{
struct l9p_connection *conn = req->lr_conn;
struct openfile *file = req->lr_fid->lo_aux;
@ -153,37 +299,17 @@ fs_create(void *softc, struct l9p_request *req)
return;
}
if (req->lr_req.tcreate.mode & L9P_DMDIR) {
mkdir(file->name, 0777);
l9p_respond(req, 0);
} else {
if (!check_access(&st, file->uid, req->lr_req.topen.mode)) {
l9p_respond(req, EPERM);
return;
}
}
static void
fs_flush(void *softc, struct l9p_request *req)
{
l9p_respond(req, 0);
}
static void
fs_open(void *softc, struct l9p_request *req)
{
struct l9p_connection *conn = req->lr_conn;
struct openfile *file = req->lr_fid->lo_aux;
struct stat st;
assert(file != NULL);
stat(file->name, &st);
if (S_ISDIR(st.st_mode))
file->dir = opendir(file->name);
else {
file->fd = open(file->name, O_RDONLY);
file->fd = open(file->name, req->lr_req.topen.mode);
if (file->fd < 0) {
l9p_respond(req, EACCES);
l9p_respond(req, EPERM);
return;
}
}
@ -193,10 +319,9 @@ fs_open(void *softc, struct l9p_request *req)
}
static void
fs_read(void *softc, struct l9p_request *req)
fs_read(void *softc __unused, struct l9p_request *req)
{
struct openfile *file;
struct l9p_connection *conn = req->lr_conn;
struct l9p_stat l9stat;
file = req->lr_fid->lo_aux;
@ -233,18 +358,45 @@ fs_read(void *softc, struct l9p_request *req)
static void
fs_remove(void *softc, struct l9p_request *req)
{
struct fs_softc *sc = softc;
struct openfile *file;
struct stat st;
file = req->lr_fid->lo_aux;
assert(file);
if (sc->fs_readonly) {
l9p_respond(req, EROFS);
return;
}
if (stat(file->name, &st) != 0) {
l9p_respond(req, errno);
return;
}
if (!check_access(&st, file->uid, L9P_OWRITE)) {
l9p_respond(req, EPERM);
return;
}
if (unlink(file->name) != 0) {
l9p_respond(req, errno);
return;
}
l9p_respond(req, 0);
}
static void
fs_stat(void *softc, struct l9p_request *req)
fs_stat(void *softc __unused, struct l9p_request *req)
{
struct openfile *file;
struct stat st;
struct l9p_stat l9stat;
file = req->lr_fid->lo_aux;
assert(file);
stat(file->name, &st);
dostat(&req->lr_resp.rstat.stat, file->name, &st);
@ -252,12 +404,13 @@ fs_stat(void *softc, struct l9p_request *req)
}
static void
fs_walk(void *softc, struct l9p_request *req)
fs_walk(void *softc __unused, struct l9p_request *req)
{
int i;
struct stat buf;
struct openfile *file = (struct openfile *)req->lr_fid->lo_aux;
char *name = malloc(1024);
struct openfile *file = req->lr_fid->lo_aux;
struct openfile *newfile;
char name[MAXPATHLEN];
strcpy(name, file->name);
@ -267,29 +420,87 @@ fs_walk(void *softc, struct l9p_request *req)
strcat(name, req->lr_req.twalk.wname[i]);
if (stat(name, &buf) < 0){
l9p_respond(req, ENOENT);
free(name);
return;
}
req->lr_resp.rwalk.wqid[i].type = buf.st_mode & S_IFMT >> 8;
req->lr_resp.rwalk.wqid[i].path = buf.st_ino;
}
req->lr_newfid->lo_aux = open_fid(name);
newfile = open_fid(name);
newfile->uid = file->uid;
req->lr_newfid->lo_aux = newfile;
req->lr_resp.rwalk.nwqid = i;
free(name);
l9p_respond(req, 0);
}
static void
fs_write(void *softc, struct l9p_request *req)
{
struct fs_softc *sc = softc;
struct openfile *file;
file = req->lr_fid->lo_aux;
assert(file != NULL);
if (sc->fs_readonly) {
l9p_respond(req, EROFS);
return;
}
size_t niov = l9p_truncate_iov(req->lr_data_iov,
req->lr_data_niov, req->lr_req.io.count);
req->lr_resp.io.count = writev(file->fd, req->lr_data_iov, niov);
l9p_respond(req, 0);
}
static void
fs_wstat(void *softc, struct l9p_request *req)
{
struct fs_softc *sc = softc;
struct openfile *file;
struct l9p_stat *l9stat = &req->lr_req.twstat.stat;
file = req->lr_fid->lo_aux;
assert(file != NULL);
if (sc->fs_readonly) {
l9p_respond(req, EROFS);
return;
}
if (l9stat->atime != (uint32_t)~0) {
}
if (l9stat->dev != (uint32_t)~0) {
l9p_respond(req, EPERM);
return;
}
if (l9stat->length != (uint64_t)~0) {
}
if (l9stat->n_uid != (uid_t)~0) {
}
if (l9stat->n_gid != (uid_t)~0) {
}
if (strlen(l9stat->name) > 0) {
char *dir = dirname(file->name);
char *newname;
asprintf(&newname, "%s/%s", dir, l9stat->name);
rename(file->name, newname);
free(newname);
}
l9p_respond(req, 0);
}
int
@ -298,7 +509,7 @@ l9p_backend_fs_init(struct l9p_backend **backendp, const char *root)
struct l9p_backend *backend;
struct fs_softc *sc;
backend = malloc(sizeof(*backend));
backend = l9p_malloc(sizeof(*backend));
backend->attach = fs_attach;
backend->clunk = fs_clunk;
backend->create = fs_create;
@ -311,10 +522,13 @@ l9p_backend_fs_init(struct l9p_backend **backendp, const char *root)
backend->write = fs_write;
backend->wstat = fs_wstat;
sc = malloc(sizeof(*sc));
sc = l9p_malloc(sizeof(*sc));
sc->fs_rootpath = strdup(root);
sc->fs_readonly = false;
backend->softc = sc;
setpassent(1);
*backendp = backend;
return (0);
}

View file

@ -28,46 +28,52 @@
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <assert.h>
#include <sys/queue.h>
#include "lib9p.h"
#include "lib9p_impl.h"
#include "hashtable.h"
#include "log.h"
int
l9p_server_init(struct l9p_server **serverp, struct l9p_backend *backend)
{
struct l9p_server *server;
struct l9p_server *server;
server = calloc(1, sizeof(*server));
server->ls_max_version = L9P_2000;
server->ls_backend = backend;
LIST_INIT(&server->ls_conns);
server = l9p_calloc(1, sizeof (*server));
server->ls_max_version = L9P_2000;
server->ls_backend = backend;
LIST_INIT(&server->ls_conns);
*serverp = server;
return (0);
*serverp = server;
return (0);
}
int
l9p_connection_init(struct l9p_server *server, struct l9p_connection **conn)
{
struct l9p_connection *newconn;
struct l9p_connection *newconn;
newconn = calloc(1, sizeof(*newconn));
newconn->lc_server = server;
newconn->lc_msize = L9P_DEFAULT_MSIZE;
LIST_INIT(&newconn->lc_requests);
LIST_INIT(&newconn->lc_files);
LIST_INSERT_HEAD(&server->ls_conns, newconn, lc_link);
*conn = newconn;
assert(server != NULL);
assert(conn != NULL);
return (0);
newconn = l9p_calloc(1, sizeof (*newconn));
newconn->lc_server = server;
newconn->lc_msize = L9P_DEFAULT_MSIZE;
ht_init(&newconn->lc_files, 100);
ht_init(&newconn->lc_requests, 100);
LIST_INSERT_HEAD(&server->ls_conns, newconn, lc_link);
*conn = newconn;
return (0);
}
void
l9p_connection_free(struct l9p_connection *conn)
{
LIST_REMOVE(conn, lc_link);
free(conn);
LIST_REMOVE(conn, lc_link);
free(conn);
}
void
@ -75,8 +81,8 @@ l9p_connection_on_send_response(struct l9p_connection *conn,
l9p_send_response_t cb, void *aux)
{
conn->lc_send_response = cb;
conn->lc_send_response_aux = aux;
conn->lc_send_response = cb;
conn->lc_send_response_aux = aux;
}
void
@ -84,94 +90,79 @@ l9p_connection_on_get_response_buffer(struct l9p_connection *conn,
l9p_get_response_buffer_t cb, void *aux)
{
conn->lc_get_response_buffer = cb;
conn->lc_get_response_buffer_aux = aux;
conn->lc_get_response_buffer = cb;
conn->lc_get_response_buffer_aux = aux;
}
void
l9p_connection_recv(struct l9p_connection *conn, const struct iovec *iov,
const size_t niov, void *aux)
{
struct l9p_request *req;
struct l9p_request *req;
req = calloc(1, sizeof(struct l9p_request));
req->lr_aux = aux;
req->lr_conn = conn;
LIST_INSERT_HEAD(&conn->lc_requests, req, lr_link);
req = l9p_calloc(1, sizeof (struct l9p_request));
req->lr_aux = aux;
req->lr_conn = conn;
ht_add(&conn->lc_requests, req->lr_req.hdr.tag, req);
req->lr_req_msg.lm_mode = L9P_UNPACK;
req->lr_req_msg.lm_niov = niov;
memcpy(req->lr_req_msg.lm_iov, iov, sizeof(struct iovec) * niov);
req->lr_resp_msg.lm_mode = L9P_PACK;
req->lr_req_msg.lm_mode = L9P_UNPACK;
req->lr_req_msg.lm_niov = niov;
memcpy(req->lr_req_msg.lm_iov, iov, sizeof (struct iovec) * niov);
if (l9p_pufcall(&req->lr_req_msg, &req->lr_req, conn->lc_version) != 0) {
L9P_LOG(L9P_WARNING, "cannot unpack received message");
return;
}
req->lr_resp_msg.lm_mode = L9P_PACK;
if (conn->lc_get_response_buffer(req, req->lr_resp_msg.lm_iov,
&req->lr_resp_msg.lm_niov, conn->lc_get_response_buffer_aux) != 0) {
L9P_LOG(L9P_WARNING, "cannot obtain buffers for response");
return;
}
if (l9p_pufcall(&req->lr_req_msg, &req->lr_req, conn->lc_version) != 0) {
L9P_LOG(L9P_WARNING, "cannot unpack received message");
return;
}
l9p_dispatch_request(req);
if (conn->lc_get_response_buffer(req, req->lr_resp_msg.lm_iov,
&req->lr_resp_msg.lm_niov, conn->lc_get_response_buffer_aux) != 0) {
L9P_LOG(L9P_WARNING, "cannot obtain buffers for response");
return;
}
l9p_dispatch_request(req);
}
void
l9p_connection_close(struct l9p_connection *conn)
{
struct ht_iter iter;
struct l9p_openfile *fid;
struct l9p_request *req;
/* Drain pending requests (if any) */
ht_iter(&conn->lc_requests, &iter);
while ((req = ht_next(&iter)) != NULL) {
l9p_respond(req, EINTR);
ht_remove_at_iter(&iter);
}
/* Close opened files (if any) */
ht_iter(&conn->lc_files, &iter);
while ((fid = ht_next(&iter)) != NULL) {
conn->lc_server->ls_backend->freefid(
conn->lc_server->ls_backend->softc, fid);
ht_remove_at_iter(&iter);
}
ht_destroy(&conn->lc_requests);
ht_destroy(&conn->lc_files);
}
struct l9p_openfile *
l9p_connection_alloc_fid(struct l9p_connection *conn, uint32_t fid)
{
struct l9p_openfile *file;
struct l9p_openfile *file;
if (l9p_connection_find_fid(conn, fid) != NULL) {
errno = EEXIST;
return (NULL);
}
file = l9p_calloc(1, sizeof (struct l9p_openfile));
file->lo_fid = fid;
file->lo_conn = conn;
if (ht_add(&conn->lc_files, fid, file) != 0) {
free(file);
return (NULL);
}
file = calloc(1, sizeof(struct l9p_openfile));
file->lo_fid = fid;
file->lo_conn = conn;
LIST_INSERT_HEAD(&conn->lc_files, file, lo_link);
return (file);
}
struct l9p_openfile *
l9p_connection_find_fid(struct l9p_connection *conn, uint32_t fid)
{
struct l9p_openfile *i;
LIST_FOREACH(i, &conn->lc_files, lo_link) {
if (i->lo_fid == fid)
return (i);
}
return (NULL);
}
void
l9p_connection_remove_fid(struct l9p_connection *conn, struct l9p_openfile *fid)
{
LIST_REMOVE(fid, lo_link);
}
struct l9p_request *
l9p_connection_find_tag(struct l9p_connection *conn, uint32_t tag)
{
struct l9p_request *i;
LIST_FOREACH(i, &conn->lc_requests, lr_link) {
if (i->lr_tag == tag)
return (i);
}
return (NULL);
return (file);
}

180
lib9p.h
View file

@ -33,124 +33,115 @@
#include <sys/types.h>
#include <sys/queue.h>
#include <sys/uio.h>
#include <sys/sbuf.h>
#include <pthread.h>
#if defined(__FreeBSD__)
#include <sys/sbuf.h>
#else
#include "sbuf/sbuf.h"
#endif
#include "fcall.h"
#include "hashtable.h"
#define L9P_DEFAULT_MSIZE 8192
#define L9P_MAX_IOV 8
#define L9P_ENOFID "FID does not exist"
#define L9P_ENOFUNC "Function not implemented"
#define L9P_EINTR "Interrupted"
struct l9p_request;
typedef int (l9p_get_response_buffer_t)(struct l9p_request *,
typedef int (l9p_get_response_buffer_t) (struct l9p_request *,
struct iovec *, size_t *, void *);
typedef int (l9p_send_response_t)(struct l9p_request *, const struct iovec *,
typedef int (l9p_send_response_t) (struct l9p_request *, const struct iovec *,
const size_t, const size_t, void *);
enum l9p_pack_mode
{
L9P_PACK,
L9P_UNPACK
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_integer_type {
L9P_BYTE = 1,
L9P_WORD = 2,
L9P_DWORD = 4,
L9P_QWORD = 8
};
enum l9p_version
{
L9P_2000 = 0,
L9P_2000U = 1,
L9P_2000L = 2,
L9P_INVALID_VERSION = 3
enum l9p_version {
L9P_2000 = 0,
L9P_2000U = 1,
L9P_2000L = 2,
L9P_INVALID_VERSION = 3
};
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;
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;
};
struct l9p_request
{
uint32_t lr_tag;
struct l9p_message lr_req_msg;
struct l9p_message lr_resp_msg;
struct l9p_message lr_readdir_msg;
union l9p_fcall lr_req;
union l9p_fcall lr_resp;
struct l9p_openfile *lr_fid;
struct l9p_openfile *lr_newfid;
struct l9p_connection *lr_conn;
pthread_t lr_thread;
void *lr_aux;
struct iovec lr_data_iov[L9P_MAX_IOV];
size_t lr_data_niov;
LIST_ENTRY(l9p_request) lr_link;
struct l9p_request {
uint32_t lr_tag;
struct l9p_message lr_req_msg;
struct l9p_message lr_resp_msg;
struct l9p_message lr_readdir_msg;
union l9p_fcall lr_req;
union l9p_fcall lr_resp;
struct l9p_openfile *lr_fid;
struct l9p_openfile *lr_newfid;
struct l9p_connection *lr_conn;
pthread_t lr_thread;
void *lr_aux;
struct iovec lr_data_iov[L9P_MAX_IOV];
size_t lr_data_niov;
};
struct l9p_openfile
{
char *lo_uid;
void *lo_aux;
uint32_t lo_fid;
struct l9p_qid lo_qid;
struct l9p_connection *lo_conn;
LIST_ENTRY(l9p_openfile) lo_link;
struct l9p_openfile {
void *lo_aux;
uint32_t lo_fid;
struct l9p_qid lo_qid;
struct l9p_connection *lo_conn;
};
struct l9p_connection
{
struct l9p_server *lc_server;
enum l9p_version lc_version;
pthread_mutex_t lc_send_lock;
uint32_t lc_msize;
uint32_t lc_max_io_size;
l9p_send_response_t *lc_send_response;
l9p_get_response_buffer_t *lc_get_response_buffer;
void *lc_get_response_buffer_aux;
void *lc_send_response_aux;
void *lc_softc;
LIST_HEAD(, l9p_request) lc_requests;
LIST_HEAD(, l9p_openfile) lc_files;
LIST_ENTRY(l9p_connection) lc_link;
struct l9p_connection {
struct l9p_server *lc_server;
enum l9p_version lc_version;
pthread_mutex_t lc_send_lock;
uint32_t lc_msize;
uint32_t lc_max_io_size;
l9p_send_response_t *lc_send_response;
l9p_get_response_buffer_t *lc_get_response_buffer;
void *lc_get_response_buffer_aux;
void *lc_send_response_aux;
void *lc_softc;
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;
struct l9p_server {
struct l9p_backend *ls_backend;
enum l9p_version ls_max_version;
LIST_HEAD(, l9p_connection) ls_conns;
};
struct l9p_backend
{
void *softc;
void (*attach)(void *, struct l9p_request *);
void (*clunk)(void *, struct l9p_request *);
void (*create)(void *, struct l9p_request *);
void (*flush)(void *, struct l9p_request *);
void (*open)(void *, struct l9p_request *);
void (*read)(void *, struct l9p_request *);
void (*remove)(void *, struct l9p_request *);
void (*stat)(void *, struct l9p_request *);
void (*walk)(void *, struct l9p_request *);
void (*write)(void *, struct l9p_request *);
void (*wstat)(void *, struct l9p_request *);
void (*freefid)(void *, struct l9p_openfile *);
struct l9p_backend {
void *softc;
void (*attach)(void *, struct l9p_request *);
void (*clunk)(void *, struct l9p_request *);
void (*create)(void *, struct l9p_request *);
void (*flush)(void *, struct l9p_request *);
void (*open)(void *, struct l9p_request *);
void (*read)(void *, struct l9p_request *);
void (*remove)(void *, struct l9p_request *);
void (*stat)(void *, struct l9p_request *);
void (*walk)(void *, struct l9p_request *);
void (*write)(void *, struct l9p_request *);
void (*wstat)(void *, struct l9p_request *);
void (*freefid)(void *, struct l9p_openfile *);
};
int l9p_pufcall(struct l9p_message *msg, union l9p_fcall *fcall,
@ -174,12 +165,6 @@ void l9p_connection_recv(struct l9p_connection *conn, const struct iovec *iov,
void l9p_connection_close(struct l9p_connection *conn);
struct l9p_openfile *l9p_connection_alloc_fid(struct l9p_connection *conn,
uint32_t fid);
struct l9p_openfile *l9p_connection_find_fid(struct l9p_connection *conn,
uint32_t fid);
void l9p_connection_remove_fid(struct l9p_connection *conn,
struct l9p_openfile *fid);
struct l9p_request *l9p_connection_find_tag(struct l9p_connection *conn,
uint32_t tag);
void l9p_dispatch_request(struct l9p_request *req);
void l9p_respond(struct l9p_request *req, int errnum);
@ -188,6 +173,7 @@ void l9p_seek_iov(struct iovec *iov1, size_t niov1, struct iovec *iov2,
size_t *niov2, size_t seek);
int l9p_truncate_iov(struct iovec *iov, size_t niov, size_t length);
void l9p_describe_qid(struct l9p_qid *qid, struct sbuf *sb);
void l9p_describe_fcall(union l9p_fcall *fcall, enum l9p_version version, struct sbuf *sb);
void l9p_describe_fcall(union l9p_fcall *fcall, enum l9p_version version,
struct sbuf *sb);
#endif /* LIB9P_LIB9P_H */

6
log.h
View file

@ -38,6 +38,10 @@ enum l9p_log_level
void l9p_logf(enum l9p_log_level level, const char *func, const char *fmt, ...);
#if defined(L9P_DEBUG)
#define L9P_LOG(level, fmt, ...) l9p_logf(level, __func__, fmt, ##__VA_ARGS__)
#else
#define L9P_LOG(level, fmt, ...)
#endif
#endif /* LIB9P_LOG_H */

487
pack.c
View file

@ -25,9 +25,9 @@
*
*/
/*
* Based on libixp code: ©2007-2010 Kris Maglione <maglione.k at Gmail>
*/
/*
* Based on libixp code: ©2007-2010 Kris Maglione <maglione.k at Gmail>
*/
#include <stdlib.h>
#include <string.h>
@ -36,369 +36,372 @@
#include <sys/param.h>
#include <sys/uio.h>
#include "lib9p.h"
#include "lib9p_impl.h"
#define N(ary) (sizeof(ary) / sizeof(*ary))
#define STRING_SIZE(s) (L9P_WORD + (s != NULL ? strlen(s) : 0))
#define QID_SIZE (L9P_BYTE + L9P_DWORD + L9P_QWORD)
static int l9p_iov_io(struct l9p_message *, void *, size_t);
static int l9p_puint(struct l9p_message *, enum l9p_integer_type, uint32_t *);
static inline int l9p_pu8(struct l9p_message *, uint8_t *);
static inline int l9p_pu16(struct l9p_message *, uint16_t *);
static inline int l9p_pu32(struct l9p_message *, uint32_t *);
static inline int l9p_pu64(struct l9p_message *, uint64_t *);
static int l9p_pustring(struct l9p_message *, char **s);
static int l9p_pustrings(struct l9p_message *, uint16_t *,char *[], size_t);
static int l9p_pudata(struct l9p_message *, uint8_t **, size_t);
static int l9p_pustrings(struct l9p_message *, uint16_t *, char *[], size_t);
static int l9p_puqid(struct l9p_message *, struct l9p_qid *);
static int l9p_puqids(struct l9p_message *, uint16_t *, struct l9p_qid *q,
size_t);
static int l9p_puqids(struct l9p_message *, uint16_t *, struct l9p_qid *q);
static int
l9p_iov_io(struct l9p_message *msg, void *buffer, size_t len)
{
size_t done = 0;
size_t left = len;
size_t done = 0;
size_t left = len;
assert(msg != NULL);
assert(msg != NULL);
if (len == 0)
return (0);
if (len == 0)
return (0);
if (msg->lm_cursor_iov >= msg->lm_niov)
return (-1);
assert(buffer != NULL);
if (msg->lm_cursor_iov >= msg->lm_niov)
return (-1);
while (left > 0) {
size_t idx = msg->lm_cursor_iov;
size_t space = msg->lm_iov[idx].iov_len - msg->lm_cursor_offset;
size_t towrite = MIN(space, left);
assert(buffer != NULL);
if (msg->lm_mode == L9P_PACK)
memcpy(msg->lm_iov[idx].iov_base + msg->lm_cursor_offset,
buffer + done, towrite);
while (left > 0) {
size_t idx = msg->lm_cursor_iov;
size_t space = msg->lm_iov[idx].iov_len - msg->lm_cursor_offset;
size_t towrite = MIN(space, left);
if (msg->lm_mode == L9P_UNPACK)
memcpy(buffer + done, msg->lm_iov[idx].iov_base +
msg->lm_cursor_offset, towrite);
if (msg->lm_mode == L9P_PACK)
memcpy(msg->lm_iov[idx].iov_base + msg->lm_cursor_offset,
buffer + done, towrite);
msg->lm_cursor_offset += towrite;
if (msg->lm_mode == L9P_UNPACK)
memcpy(buffer + done, msg->lm_iov[idx].iov_base +
msg->lm_cursor_offset, towrite);
if (space - towrite == 0) {
/* Advance to next iov */
msg->lm_cursor_iov++;
msg->lm_cursor_offset = 0;
msg->lm_cursor_offset += towrite;
if (msg->lm_cursor_iov > msg->lm_niov)
return (-1);
}
if (space - towrite == 0) {
/* Advance to next iov */
msg->lm_cursor_iov++;
msg->lm_cursor_offset = 0;
done += towrite;
left -= towrite;
}
if (msg->lm_cursor_iov > msg->lm_niov)
return (-1);
}
msg->lm_size += done;
return (done);
done += towrite;
left -= towrite;
}
msg->lm_size += done;
return (done);
}
static inline int
l9p_pu8(struct l9p_message *msg, uint8_t *val)
{
return (l9p_iov_io(msg, val, sizeof(uint8_t)));
return (l9p_iov_io(msg, val, sizeof (uint8_t)));
}
static inline int
l9p_pu16(struct l9p_message *msg, uint16_t *val)
{
return (l9p_iov_io(msg, val, sizeof(uint16_t)));
return (l9p_iov_io(msg, val, sizeof (uint16_t)));
}
static inline int
l9p_pu32(struct l9p_message *msg, uint32_t *val)
{
return(l9p_iov_io(msg, val, sizeof(uint32_t)));
return (l9p_iov_io(msg, val, sizeof (uint32_t)));
}
static inline int
l9p_pu64(struct l9p_message *msg, uint64_t *val)
{
return(l9p_iov_io(msg, val, sizeof(uint64_t)));
return (l9p_iov_io(msg, val, sizeof (uint64_t)));
}
static int
l9p_pustring(struct l9p_message *msg, char **s)
{
uint16_t len;
uint16_t len;
if (msg->lm_mode == L9P_PACK)
len = *s != NULL ? strlen(*s) : 0;
if (l9p_pu16(msg, &len) < 0)
return (-1);
if (msg->lm_mode == L9P_PACK)
len = *s != NULL ? strlen(*s) : 0;
if (msg->lm_mode == L9P_UNPACK)
*s = calloc(1, len + 1);
if (l9p_pu16(msg, &len) < 0)
return (-1);
if (l9p_iov_io(msg, *s, len) < 0)
return (-1);
if (msg->lm_mode == L9P_UNPACK)
*s = l9p_calloc(1, len + 1);
return (len + 2);
if (l9p_iov_io(msg, *s, len) < 0)
return (-1);
return (len + 2);
}
static int
l9p_pustrings(struct l9p_message *msg, uint16_t *num, char *strings[],
size_t max)
{
char *s;
uint i, size;
uint16_t len;
int r = 0;
size_t i;
int ret;
int r = 0;
l9p_pu16(msg, num);
l9p_pu16(msg, num);
for (i = 0; i < MIN(*num, max); i++) {
if (l9p_pustring(msg, &strings[i]) < 0)
return (-1);
}
}
for (i = 0; i < MIN(*num, max); i++) {
ret = l9p_pustring(msg, &strings[i]);
if (ret < 1)
return (-1);
static int
l9p_pudata(struct l9p_message *msg, uint8_t **data, size_t len)
{
if (msg->lm_mode == L9P_UNPACK)
*data = malloc(len);
return (l9p_iov_io(msg, *data, len));
r += ret;
}
return (r);
}
static int
l9p_puqid(struct l9p_message *msg, struct l9p_qid *qid)
{
int r = 0;
int r = 0;
r += l9p_pu8(msg, (uint8_t *)&qid->type);
r += l9p_pu32(msg, &qid->version);
r += l9p_pu64(msg, &qid->path);
r += l9p_pu8(msg, (uint8_t *) & qid->type);
r += l9p_pu32(msg, &qid->version);
r += l9p_pu64(msg, &qid->path);
return (r);
return (r);
}
static int
l9p_puqids(struct l9p_message *msg, uint16_t *num, struct l9p_qid *qids,
size_t max)
l9p_puqids(struct l9p_message *msg, uint16_t *num, struct l9p_qid *qids)
{
int i;
l9p_pu16(msg, num);
int i, ret, r = 0;
l9p_pu16(msg, num);
for (i = 0; i < *num; i++) {
if (l9p_puqid(msg, &qids[i]) < 0)
return (-1);
}
for (i = 0; i < *num; i++) {
ret = l9p_puqid(msg, &qids[i]);
if (ret < 0)
return (-1);
r += ret;
}
return (r);
}
int
l9p_pustat(struct l9p_message *msg, struct l9p_stat *stat,
enum l9p_version version)
{
int r = 0;
uint16_t size;
int r = 0;
uint16_t size;
if (msg->lm_mode == L9P_PACK)
size = l9p_sizeof_stat(stat, version) - 2;
if (msg->lm_mode == L9P_PACK)
size = l9p_sizeof_stat(stat, version) - 2;
r += l9p_pu16(msg, &size);
r += l9p_pu16(msg, &stat->type);
r += l9p_pu32(msg, &stat->dev);
r += l9p_puqid(msg, &stat->qid);
r += l9p_pu32(msg, &stat->mode);
r += l9p_pu32(msg, &stat->atime);
r += l9p_pu32(msg, &stat->mtime);
r += l9p_pu64(msg, &stat->length);
r += l9p_pustring(msg, &stat->name);
r += l9p_pustring(msg, &stat->uid);
r += l9p_pustring(msg, &stat->gid);
r += l9p_pustring(msg, &stat->muid);
r += l9p_pu16(msg, &size);
r += l9p_pu16(msg, &stat->type);
r += l9p_pu32(msg, &stat->dev);
r += l9p_puqid(msg, &stat->qid);
r += l9p_pu32(msg, &stat->mode);
r += l9p_pu32(msg, &stat->atime);
r += l9p_pu32(msg, &stat->mtime);
r += l9p_pu64(msg, &stat->length);
r += l9p_pustring(msg, &stat->name);
r += l9p_pustring(msg, &stat->uid);
r += l9p_pustring(msg, &stat->gid);
r += l9p_pustring(msg, &stat->muid);
if (version == L9P_2000U) {
r += l9p_pustring(msg, &stat->extension);
r += l9p_pu32(msg, &stat->n_uid);
r += l9p_pu32(msg, &stat->n_gid);
r += l9p_pu32(msg, &stat->n_muid);
}
if (version == L9P_2000U) {
r += l9p_pustring(msg, &stat->extension);
r += l9p_pu32(msg, &stat->n_uid);
r += l9p_pu32(msg, &stat->n_gid);
r += l9p_pu32(msg, &stat->n_muid);
}
if (r < size + 2)
return (-1);
return (r);
return (r);
}
int
l9p_pufcall(struct l9p_message *msg, union l9p_fcall *fcall,
enum l9p_version version)
{
uint32_t length = 0;
uint32_t length = 0;
l9p_pu32(msg, &length);
l9p_pu8(msg, &fcall->hdr.type);
l9p_pu16(msg, &fcall->hdr.tag);
l9p_pu32(msg, &length);
l9p_pu8(msg, &fcall->hdr.type);
l9p_pu16(msg, &fcall->hdr.tag);
switch (fcall->hdr.type) {
case L9P_TVERSION:
case L9P_RVERSION:
l9p_pu32(msg, &fcall->version.msize);
l9p_pustring(msg, &fcall->version.version);
break;
switch (fcall->hdr.type) {
case L9P_TVERSION:
case L9P_RVERSION:
l9p_pu32(msg, &fcall->version.msize);
l9p_pustring(msg, &fcall->version.version);
break;
case L9P_TAUTH:
l9p_pu32(msg, &fcall->tauth.afid);
l9p_pustring(msg, &fcall->tauth.uname);
l9p_pustring(msg, &fcall->tauth.aname);
if (version == L9P_2000U)
l9p_pu32(msg, &fcall->tauth.n_uname);
break;
case L9P_TAUTH:
l9p_pu32(msg, &fcall->tauth.afid);
l9p_pustring(msg, &fcall->tauth.uname);
l9p_pustring(msg, &fcall->tauth.aname);
if (version == L9P_2000U)
l9p_pu32(msg, &fcall->tauth.n_uname);
break;
case L9P_RAUTH:
l9p_puqid(msg, &fcall->rauth.aqid);
break;
case L9P_RAUTH:
l9p_puqid(msg, &fcall->rauth.aqid);
break;
case L9P_RATTACH:
l9p_puqid(msg, &fcall->rattach.qid);
break;
case L9P_RATTACH:
l9p_puqid(msg, &fcall->rattach.qid);
break;
case L9P_TATTACH:
l9p_pu32(msg, &fcall->hdr.fid);
l9p_pu32(msg, &fcall->tattach.afid);
l9p_pustring(msg, &fcall->tattach.uname);
l9p_pustring(msg, &fcall->tattach.aname);
break;
case L9P_TATTACH:
l9p_pu32(msg, &fcall->hdr.fid);
l9p_pu32(msg, &fcall->tattach.afid);
l9p_pustring(msg, &fcall->tattach.uname);
l9p_pustring(msg, &fcall->tattach.aname);
if (version == L9P_2000U)
l9p_pu32(msg, &fcall->tattach.n_uname);
break;
case L9P_RERROR:
l9p_pustring(msg, &fcall->error.ename);
if (version == L9P_2000U)
l9p_pu32(msg, &fcall->error.errnum);
break;
case L9P_RERROR:
l9p_pustring(msg, &fcall->error.ename);
if (version == L9P_2000U)
l9p_pu32(msg, &fcall->error.errnum);
break;
case L9P_TFLUSH:
l9p_pu16(msg, &fcall->tflush.oldtag);
break;
case L9P_TFLUSH:
l9p_pu16(msg, &fcall->tflush.oldtag);
break;
case L9P_TWALK:
l9p_pu32(msg, &fcall->hdr.fid);
l9p_pu32(msg, &fcall->twalk.newfid);
l9p_pustrings(msg, &fcall->twalk.nwname, fcall->twalk.wname,
N(fcall->twalk.wname));
break;
case L9P_TWALK:
l9p_pu32(msg, &fcall->hdr.fid);
l9p_pu32(msg, &fcall->twalk.newfid);
l9p_pustrings(msg, &fcall->twalk.nwname,
fcall->twalk.wname, N(fcall->twalk.wname));
break;
case L9P_RWALK:
l9p_puqids(msg, &fcall->rwalk.nwqid, fcall->rwalk.wqid,
N(fcall->rwalk.wqid));
break;
case L9P_RWALK:
l9p_puqids(msg, &fcall->rwalk.nwqid, fcall->rwalk.wqid);
break;
case L9P_TOPEN:
l9p_pu32(msg, &fcall->hdr.fid);
l9p_pu8(msg, &fcall->topen.mode);
break;
case L9P_TOPEN:
l9p_pu32(msg, &fcall->hdr.fid);
l9p_pu8(msg, &fcall->topen.mode);
break;
case L9P_ROPEN:
case L9P_RCREATE:
l9p_puqid(msg, &fcall->ropen.qid);
l9p_pu32(msg, &fcall->ropen.iounit);
break;
case L9P_ROPEN:
case L9P_RCREATE:
l9p_puqid(msg, &fcall->ropen.qid);
l9p_pu32(msg, &fcall->ropen.iounit);
break;
case L9P_TCREATE:
l9p_pu32(msg, &fcall->hdr.fid);
l9p_pustring(msg, &fcall->tcreate.name);
l9p_pu32(msg, &fcall->tcreate.perm);
l9p_pu8(msg, &fcall->tcreate.mode);
if (version == L9P_2000U)
l9p_pustring(msg, &fcall->tcreate.extension);
break;
case L9P_TCREATE:
l9p_pu32(msg, &fcall->hdr.fid);
l9p_pustring(msg, &fcall->tcreate.name);
l9p_pu32(msg, &fcall->tcreate.perm);
l9p_pu8(msg, &fcall->tcreate.mode);
if (version == L9P_2000U)
l9p_pustring(msg, &fcall->tcreate.extension);
break;
case L9P_TREAD:
l9p_pu32(msg, &fcall->hdr.fid);
l9p_pu64(msg, &fcall->io.offset);
l9p_pu32(msg, &fcall->io.count);
break;
case L9P_TREAD:
l9p_pu32(msg, &fcall->hdr.fid);
l9p_pu64(msg, &fcall->io.offset);
l9p_pu32(msg, &fcall->io.count);
break;
case L9P_RREAD:
l9p_pu32(msg, &fcall->io.count);
break;
case L9P_RREAD:
l9p_pu32(msg, &fcall->io.count);
break;
case L9P_TWRITE:
l9p_pu32(msg, &fcall->hdr.fid);
l9p_pu64(msg, &fcall->io.offset);
l9p_pu32(msg, &fcall->io.count);
break;
case L9P_TWRITE:
l9p_pu32(msg, &fcall->hdr.fid);
l9p_pu64(msg, &fcall->io.offset);
l9p_pu32(msg, &fcall->io.count);
break;
case L9P_RWRITE:
l9p_pu32(msg, &fcall->io.count);
break;
case L9P_RWRITE:
l9p_pu32(msg, &fcall->io.count);
break;
case L9P_TCLUNK:
case L9P_TSTAT:
l9p_pu32(msg, &fcall->hdr.fid);
break;
case L9P_TCLUNK:
case L9P_TSTAT:
case L9P_TREMOVE:
l9p_pu32(msg, &fcall->hdr.fid);
break;
case L9P_RSTAT:
{
uint16_t size = l9p_sizeof_stat(&fcall->rstat.stat, version);
l9p_pu16(msg, &size);
l9p_pustat(msg, &fcall->rstat.stat, version);
}
break;
case L9P_TWSTAT:
{
uint16_t size;
l9p_pu32(msg, &fcall->hdr.fid);
l9p_pu16(msg, &size);
l9p_pustat(msg, &fcall->twstat.stat, version);
}
break;
}
case L9P_RSTAT:
{
uint16_t size = l9p_sizeof_stat(&fcall->rstat.stat,
version);
l9p_pu16(msg, &size);
l9p_pustat(msg, &fcall->rstat.stat, version);
}
break;
if (msg->lm_mode == L9P_PACK) {
/* Rewind to the beginning */
uint32_t len = msg->lm_size;
msg->lm_cursor_offset = 0;
msg->lm_cursor_iov = 0;
case L9P_TWSTAT:
{
uint16_t size;
l9p_pu32(msg, &fcall->hdr.fid);
l9p_pu16(msg, &size);
l9p_pustat(msg, &fcall->twstat.stat, version);
}
break;
}
/*
* Subtract 4 bytes from message size, becase we're
* overwriting size (rewinding message to the beginning)
* and writing again.
*/
msg->lm_size -= sizeof(uint32_t);
if (msg->lm_mode == L9P_PACK) {
/* Rewind to the beginning */
uint32_t len = msg->lm_size;
msg->lm_cursor_offset = 0;
msg->lm_cursor_iov = 0;
if (fcall->hdr.type == L9P_RREAD)
len += fcall->io.count;
/*
* Subtract 4 bytes from message size, becase we're
* overwriting size (rewinding message to the beginning)
* and writing again.
*/
msg->lm_size -= sizeof (uint32_t);
l9p_pu32(msg, &len);
}
if (fcall->hdr.type == L9P_RREAD)
len += fcall->io.count;
return (0);
l9p_pu32(msg, &len);
}
return (0);
}
uint16_t
l9p_sizeof_stat(struct l9p_stat *stat, enum l9p_version version)
{
uint16_t size = L9P_WORD /* size */
+ L9P_WORD /* type */
+ L9P_DWORD /* dev */
+ QID_SIZE /* qid */
+ 3 * L9P_DWORD /* mode, atime, mtime */
+ L9P_QWORD /* length */
+ STRING_SIZE(stat->name)
+ STRING_SIZE(stat->uid)
+ STRING_SIZE(stat->gid)
+ STRING_SIZE(stat->muid);
if (version == L9P_2000U) {
size += STRING_SIZE(stat->extension)
+ 3 * L9P_DWORD;
}
return (size);
uint16_t size = L9P_WORD /* size */
+ L9P_WORD /* type */
+ L9P_DWORD /* dev */
+ QID_SIZE /* qid */
+ 3 * L9P_DWORD /* mode, atime, mtime */
+ L9P_QWORD /* length */
+ STRING_SIZE(stat->name)
+ STRING_SIZE(stat->uid)
+ STRING_SIZE(stat->gid)
+ STRING_SIZE(stat->muid);
if (version == L9P_2000U) {
size += STRING_SIZE(stat->extension)
+ 3 * L9P_DWORD;
}
return (size);
}

422
request.c
View file

@ -27,11 +27,18 @@
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/param.h>
#include <sys/uio.h>
#if defined(__FreeBSD__)
#include <sys/sbuf.h>
#else
#include "sbuf/sbuf.h"
#endif
#include "lib9p.h"
#include "lib9p_impl.h"
#include "fcall.h"
#include "hashtable.h"
#include "log.h"
#define N(x) (sizeof(x) / sizeof(x[0]))
@ -51,308 +58,365 @@ static void l9p_dispatch_twstat(struct l9p_request *req);
static const struct
{
enum l9p_ftype type;
void (*handler)(struct l9p_request *);
enum l9p_ftype type;
void (*handler)(struct l9p_request *);
} l9p_handlers[] = {
{L9P_TVERSION, l9p_dispatch_tversion},
{L9P_TATTACH, l9p_dispatch_tattach},
{L9P_TCLUNK, l9p_dispatch_tclunk},
{L9P_TFLUSH, l9p_dispatch_tflush},
{L9P_TCREATE, l9p_dispatch_tcreate},
{L9P_TOPEN, l9p_dispatch_topen},
{L9P_TREAD, l9p_dispatch_tread},
{L9P_TWRITE, l9p_dispatch_twrite},
{L9P_TREMOVE, l9p_dispatch_tremove},
{L9P_TSTAT, l9p_dispatch_tstat},
{L9P_TWALK, l9p_dispatch_twalk},
{L9P_TWSTAT, l9p_dispatch_twstat}
{L9P_TVERSION, l9p_dispatch_tversion},
{L9P_TATTACH, l9p_dispatch_tattach},
{L9P_TCLUNK, l9p_dispatch_tclunk},
{L9P_TFLUSH, l9p_dispatch_tflush},
{L9P_TCREATE, l9p_dispatch_tcreate},
{L9P_TOPEN, l9p_dispatch_topen},
{L9P_TREAD, l9p_dispatch_tread},
{L9P_TWRITE, l9p_dispatch_twrite},
{L9P_TREMOVE, l9p_dispatch_tremove},
{L9P_TSTAT, l9p_dispatch_tstat},
{L9P_TWALK, l9p_dispatch_twalk},
{L9P_TWSTAT, l9p_dispatch_twstat}
};
static const char *l9p_versions[] = {
"9P2000",
"9P2000.u",
"9P2000.L"
"9P2000",
"9P2000.u",
"9P2000.L"
};
void
l9p_dispatch_request(struct l9p_request *req)
{
struct l9p_connection *conn = req->lr_conn;
struct sbuf *sb = sbuf_new_auto();
int i;
struct sbuf *sb = sbuf_new_auto();
size_t i;
l9p_describe_fcall(&req->lr_req, L9P_2000, sb);
sbuf_done(sb);
l9p_describe_fcall(&req->lr_req, req->lr_conn->lc_version, sb);
sbuf_done(sb);
L9P_LOG(L9P_DEBUG, "%s", sbuf_data(sb));
sbuf_delete(sb);
L9P_LOG(L9P_DEBUG, "%s", sbuf_data(sb));
sbuf_delete(sb);
req->lr_tag = req->lr_req.hdr.tag;
req->lr_tag = req->lr_req.hdr.tag;
for (i = 0; i < N(l9p_handlers); i++) {
if (req->lr_req.hdr.type == l9p_handlers[i].type) {
l9p_handlers[i].handler(req);
return;
}
}
for (i = 0; i < N(l9p_handlers); i++) {
if (req->lr_req.hdr.type == l9p_handlers[i].type) {
l9p_handlers[i].handler(req);
return;
}
}
L9P_LOG(L9P_WARNING, "unknown request of type %d", req->lr_req.hdr.type);
l9p_respond(req, L9P_ENOFUNC);
L9P_LOG(L9P_WARNING, "unknown request of type %d", req->lr_req.hdr.type);
l9p_respond(req, ENOSYS);
}
void
l9p_respond(struct l9p_request *req, int errnum)
{
struct l9p_connection *conn = req->lr_conn;
struct sbuf *sb = sbuf_new_auto();
size_t iosize;
struct l9p_connection *conn = req->lr_conn;
struct sbuf *sb = sbuf_new_auto();
size_t iosize;
switch (req->lr_req.hdr.type) {
case L9P_TCLUNK:
l9p_connection_remove_fid(conn, req->lr_fid);
break;
}
switch (req->lr_req.hdr.type) {
case L9P_TCLUNK:
case L9P_TREMOVE:
if (req->lr_fid != NULL)
ht_remove(&conn->lc_files, req->lr_fid->lo_fid);
break;
req->lr_resp.hdr.tag = req->lr_req.hdr.tag;
case L9P_TWALK:
if (errnum != 0 && req->lr_newfid != NULL &&
req->lr_newfid != req->lr_fid)
ht_remove(&conn->lc_files, req->lr_newfid->lo_fid);
if (errnum == 0)
req->lr_resp.hdr.type = req->lr_req.hdr.type + 1;
else {
req->lr_resp.hdr.type = L9P_RERROR;
req->lr_resp.error.ename = strerror(errnum);
req->lr_resp.error.errnum = errnum;
}
break;
}
l9p_describe_fcall(&req->lr_resp, L9P_2000, sb);
sbuf_done(sb);
req->lr_resp.hdr.tag = req->lr_req.hdr.tag;
L9P_LOG(L9P_DEBUG, "%s", sbuf_data(sb));
sbuf_delete(sb);
if (errnum == 0)
req->lr_resp.hdr.type = req->lr_req.hdr.type + 1;
else {
req->lr_resp.hdr.type = L9P_RERROR;
req->lr_resp.error.ename = strerror(errnum);
req->lr_resp.error.errnum = errnum;
}
if (l9p_pufcall(&req->lr_resp_msg, &req->lr_resp, conn->lc_version) != 0) {
L9P_LOG(L9P_ERROR, "cannot pack response");
goto out;
}
l9p_describe_fcall(&req->lr_resp, L9P_2000, sb);
sbuf_done(sb);
iosize = req->lr_resp_msg.lm_size;
L9P_LOG(L9P_DEBUG, "%s", sbuf_data(sb));
sbuf_delete(sb);
/* Include I/O size in calculation for Rread response */
if (req->lr_resp.hdr.type == L9P_RREAD)
iosize += req->lr_resp.io.count;
if (l9p_pufcall(&req->lr_resp_msg, &req->lr_resp, conn->lc_version) != 0) {
L9P_LOG(L9P_ERROR, "cannot pack response");
goto out;
}
conn->lc_send_response(req, req->lr_resp_msg.lm_iov,
req->lr_resp_msg.lm_niov, iosize, conn->lc_send_response_aux);
iosize = req->lr_resp_msg.lm_size;
/* Include I/O size in calculation for Rread response */
if (req->lr_resp.hdr.type == L9P_RREAD)
iosize += req->lr_resp.io.count;
conn->lc_send_response(req, req->lr_resp_msg.lm_iov,
req->lr_resp_msg.lm_niov, iosize, conn->lc_send_response_aux);
out:
LIST_REMOVE(req, lr_link);
free(req);
free(req);
}
int
l9p_pack_stat(struct l9p_request *req, struct l9p_stat *st)
{
struct l9p_connection *conn = req->lr_conn;
struct l9p_message *msg = &req->lr_readdir_msg;
uint16_t size = l9p_sizeof_stat(st, conn->lc_version);
struct l9p_connection *conn = req->lr_conn;
struct l9p_message *msg = &req->lr_readdir_msg;
uint16_t size = l9p_sizeof_stat(st, conn->lc_version);
if (msg->lm_size == 0) {
/* Initialize message */
msg->lm_mode = L9P_PACK;
msg->lm_niov = req->lr_data_niov;
memcpy(msg->lm_iov, req->lr_data_iov, sizeof(struct iovec) * req->lr_data_niov);
}
if (msg->lm_size == 0) {
/* Initialize message */
msg->lm_mode = L9P_PACK;
msg->lm_niov = req->lr_data_niov;
memcpy(msg->lm_iov, req->lr_data_iov,
sizeof (struct iovec) * req->lr_data_niov);
}
if (req->lr_resp.io.count + size > req->lr_req.io.count)
return (-1);
if (l9p_pustat(msg, st, conn->lc_version) < 0)
return (-1);
if (l9p_pustat(msg, st, conn->lc_version) < 0)
return (-1);
req->lr_resp.io.count += size;
return (0);
req->lr_resp.io.count += size;
return (0);
}
static void
l9p_dispatch_tversion(struct l9p_request *req)
{
struct l9p_connection *conn = req->lr_conn;
enum l9p_version remote_version = L9P_INVALID_VERSION;
int i;
struct l9p_connection *conn = req->lr_conn;
enum l9p_version remote_version = L9P_INVALID_VERSION;
size_t i;
for (i = 0; i < N(l9p_versions); i++) {
if (strcmp(req->lr_req.version.version, l9p_versions[i]) == 0) {
remote_version = (enum l9p_version)i;
break;
}
}
for (i = 0; i < N(l9p_versions); i++) {
if (strcmp(req->lr_req.version.version, l9p_versions[i]) == 0) {
remote_version = (enum l9p_version)i;
break;
}
}
if (remote_version == L9P_INVALID_VERSION) {
L9P_LOG(L9P_ERROR, "unsupported remote version: %s",
req->lr_req.version.version);
l9p_respond(req, L9P_ENOFUNC);
return;
}
if (remote_version == L9P_INVALID_VERSION) {
L9P_LOG(L9P_ERROR, "unsupported remote version: %s",
req->lr_req.version.version);
l9p_respond(req, ENOSYS);
return;
}
L9P_LOG(L9P_INFO, "remote version: %s", l9p_versions[remote_version]);
L9P_LOG(L9P_INFO, "local version: %s",
l9p_versions[conn->lc_server->ls_max_version]);
L9P_LOG(L9P_INFO, "remote version: %s", l9p_versions[remote_version]);
L9P_LOG(L9P_INFO, "local version: %s",
l9p_versions[conn->lc_server->ls_max_version]);
conn->lc_version = MIN(remote_version, conn->lc_server->ls_max_version);
conn->lc_msize = MIN(req->lr_req.version.msize, conn->lc_msize);
conn->lc_max_io_size = conn->lc_msize - 24;
req->lr_resp.version.version = strdup(l9p_versions[conn->lc_version]);
req->lr_resp.version.msize = conn->lc_msize;
l9p_respond(req, NULL);
conn->lc_version = MIN(remote_version, conn->lc_server->ls_max_version);
conn->lc_msize = MIN(req->lr_req.version.msize, conn->lc_msize);
conn->lc_max_io_size = conn->lc_msize - 24;
req->lr_resp.version.version = strdup(l9p_versions[conn->lc_version]);
req->lr_resp.version.msize = conn->lc_msize;
l9p_respond(req, 0);
}
static void
l9p_dispatch_tattach(struct l9p_request *req)
{
struct l9p_connection *conn = req->lr_conn;
struct l9p_connection *conn = req->lr_conn;
req->lr_fid = l9p_connection_alloc_fid(conn, req->lr_req.hdr.fid);
conn->lc_server->ls_backend->attach(conn->lc_server->ls_backend->softc, req);
req->lr_fid = l9p_connection_alloc_fid(conn, req->lr_req.hdr.fid);
if (req->lr_fid == NULL)
req->lr_fid = ht_find(&conn->lc_files, req->lr_req.hdr.fid);
conn->lc_server->ls_backend->attach(conn->lc_server->ls_backend->softc, req);
}
static void
l9p_dispatch_tclunk(struct l9p_request *req)
{
struct l9p_connection *conn = req->lr_conn;
struct l9p_connection *conn = req->lr_conn;
req->lr_fid = l9p_connection_find_fid(conn, req->lr_req.hdr.fid);
if (!req->lr_fid) {
l9p_respond(req, L9P_ENOFID);
return;
}
req->lr_fid = ht_find(&conn->lc_files, req->lr_req.hdr.fid);
if (req->lr_fid == NULL) {
l9p_respond(req, EBADF);
return;
}
conn->lc_server->ls_backend->clunk(conn->lc_server->ls_backend->softc, req);
conn->lc_server->ls_backend->clunk(conn->lc_server->ls_backend->softc, req);
}
static void
l9p_dispatch_tflush(struct l9p_request *req)
{
struct l9p_connection *conn = req->lr_conn;
struct l9p_connection *conn = req->lr_conn;
if (!conn->lc_server->ls_backend->flush) {
l9p_respond(req, L9P_ENOFUNC);
return;
}
if (!conn->lc_server->ls_backend->flush) {
l9p_respond(req, ENOSYS);
return;
}
conn->lc_server->ls_backend->flush(conn->lc_server->ls_backend->softc, req);
conn->lc_server->ls_backend->flush(conn->lc_server->ls_backend->softc, req);
}
static void
l9p_dispatch_tcreate(struct l9p_request *req)
{
struct l9p_connection *conn = req->lr_conn;
struct l9p_openfile *fid;
struct l9p_connection *conn = req->lr_conn;
if (l9p_connection_find_fid(conn, req->lr_req.tcreate.hdr.fid) != NULL) {
l9p_respond(req, L9P_ENOFID);
return;
}
req->lr_fid = ht_find(&conn->lc_files, req->lr_req.hdr.fid);
if (req->lr_fid == NULL) {
l9p_respond(req, EBADF);
return;
}
req->lr_fid = l9p_connection_alloc_fid(conn, req->lr_req.tattach.afid);
conn->lc_server->ls_backend->create(conn->lc_server->ls_backend->softc, req);
if (!conn->lc_server->ls_backend->create) {
l9p_respond(req, ENOSYS);
return;
}
conn->lc_server->ls_backend->create(conn->lc_server->ls_backend->softc, req);
}
static void
l9p_dispatch_topen(struct l9p_request *req)
{
struct l9p_connection *conn = req->lr_conn;
struct l9p_openfile *fid;
struct l9p_connection *conn = req->lr_conn;
req->lr_fid = l9p_connection_find_fid(conn, req->lr_req.topen.hdr.fid);
if (!req->lr_fid) {
l9p_respond(req, L9P_ENOFID);
return;
}
req->lr_fid = ht_find(&conn->lc_files, req->lr_req.hdr.fid);
if (!req->lr_fid) {
l9p_respond(req, EBADF);
return;
}
if (!conn->lc_server->ls_backend->open) {
l9p_respond(req, L9P_ENOFUNC);
return;
}
if (!conn->lc_server->ls_backend->open) {
l9p_respond(req, ENOSYS);
return;
}
conn->lc_server->ls_backend->open(conn->lc_server->ls_backend->softc, req);
conn->lc_server->ls_backend->open(conn->lc_server->ls_backend->softc, req);
}
static void
l9p_dispatch_tread(struct l9p_request *req)
{
struct l9p_connection *conn = req->lr_conn;
struct l9p_openfile *fid;
struct l9p_connection *conn = req->lr_conn;
req->lr_fid = l9p_connection_find_fid(conn, req->lr_req.hdr.fid);
if (!req->lr_fid) {
l9p_respond(req, L9P_ENOFID);
return;
}
req->lr_fid = ht_find(&conn->lc_files, req->lr_req.hdr.fid);
if (!req->lr_fid) {
l9p_respond(req, EBADF);
return;
}
l9p_seek_iov(req->lr_resp_msg.lm_iov, req->lr_resp_msg.lm_niov,
req->lr_data_iov, &req->lr_data_niov, 11);
l9p_seek_iov(req->lr_resp_msg.lm_iov, req->lr_resp_msg.lm_niov,
req->lr_data_iov, &req->lr_data_niov, 11);
conn->lc_server->ls_backend->read(conn->lc_server->ls_backend->softc, req);
conn->lc_server->ls_backend->read(conn->lc_server->ls_backend->softc, req);
}
static void
l9p_dispatch_tremove(struct l9p_request *req)
{
struct l9p_connection *conn = req->lr_conn;
req->lr_fid = ht_find(&conn->lc_files, req->lr_req.hdr.fid);
if (!req->lr_fid) {
l9p_respond(req, EBADF);
return;
}
if (!conn->lc_server->ls_backend->remove) {
l9p_respond(req, ENOSYS);
return;
}
conn->lc_server->ls_backend->remove(conn->lc_server->ls_backend->softc, req);
}
static void
l9p_dispatch_tstat(struct l9p_request *req)
{
struct l9p_connection *conn = req->lr_conn;
struct l9p_openfile *fid;
struct l9p_connection *conn = req->lr_conn;
req->lr_fid = l9p_connection_find_fid(conn, req->lr_req.twalk.hdr.fid);
if (!req->lr_fid) {
l9p_respond(req, L9P_ENOFID);
return;
}
req->lr_fid = ht_find(&conn->lc_files, req->lr_req.hdr.fid);
if (!req->lr_fid) {
l9p_respond(req, EBADF);
return;
}
if (!conn->lc_server->ls_backend->stat) {
l9p_respond(req, L9P_ENOFUNC);
return;
}
if (!conn->lc_server->ls_backend->stat) {
l9p_respond(req, ENOSYS);
return;
}
conn->lc_server->ls_backend->stat(conn->lc_server->ls_backend->softc, req);
conn->lc_server->ls_backend->stat(conn->lc_server->ls_backend->softc, req);
}
static void
l9p_dispatch_twalk(struct l9p_request *req)
{
struct l9p_connection *conn = req->lr_conn;
struct l9p_openfile *fid;
struct l9p_connection *conn = req->lr_conn;
req->lr_fid = l9p_connection_find_fid(conn, req->lr_req.twalk.hdr.fid);
if (!req->lr_fid) {
l9p_respond(req, L9P_ENOFID);
return;
}
req->lr_fid = ht_find(&conn->lc_files, req->lr_req.hdr.fid);
if (req->lr_fid == NULL) {
l9p_respond(req, EBADF);
return;
}
if (req->lr_req.twalk.hdr.fid != req->lr_req.twalk.newfid) {
req->lr_newfid = l9p_connection_alloc_fid(conn, req->lr_req.twalk.newfid);
if (req->lr_newfid == NULL) {
l9p_respond(req, L9P_ENOFID);
return;
}
}
if (req->lr_req.twalk.hdr.fid != req->lr_req.twalk.newfid) {
req->lr_newfid = l9p_connection_alloc_fid(conn,
req->lr_req.twalk.newfid);
if (req->lr_newfid == NULL) {
l9p_respond(req, EBADF);
return;
}
}
if (!conn->lc_server->ls_backend->walk) {
l9p_respond(req, L9P_ENOFUNC);
return;
}
if (!conn->lc_server->ls_backend->walk) {
l9p_respond(req, ENOSYS);
return;
}
conn->lc_server->ls_backend->walk(conn->lc_server->ls_backend->softc, req);
conn->lc_server->ls_backend->walk(conn->lc_server->ls_backend->softc, req);
}
static void
l9p_dispatch_twrite(struct l9p_request *req)
{
struct l9p_connection *conn = req->lr_conn;
req->lr_fid = ht_find(&conn->lc_files, req->lr_req.twalk.hdr.fid);
if (req->lr_fid == NULL) {
l9p_respond(req, EBADF);
return;
}
if (!conn->lc_server->ls_backend->write) {
l9p_respond(req, ENOSYS);
return;
}
l9p_seek_iov(req->lr_req_msg.lm_iov, req->lr_req_msg.lm_niov,
req->lr_data_iov, &req->lr_data_niov, 23);
conn->lc_server->ls_backend->write(conn->lc_server->ls_backend->softc, req);
}
static void
l9p_dispatch_twstat(struct l9p_request *req)
{
struct l9p_connection *conn = req->lr_conn;
req->lr_fid = ht_find(&conn->lc_files, req->lr_req.hdr.fid);
if (!req->lr_fid) {
l9p_respond(req, EBADF);
return;
}
if (!conn->lc_server->ls_backend->wstat) {
l9p_respond(req, ENOSYS);
return;
}
conn->lc_server->ls_backend->wstat(conn->lc_server->ls_backend->softc, req);
}