diff --git a/lib9p/.gitignore b/lib9p/.gitignore new file mode 100644 index 0000000..3ccb06a --- /dev/null +++ b/lib9p/.gitignore @@ -0,0 +1,33 @@ +# Object files +*.o +*.ko +*.obj +*.elf + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# Debug files +*.dSYM/ +/build/ \ No newline at end of file diff --git a/lib9p/COPYRIGHT b/lib9p/COPYRIGHT new file mode 100644 index 0000000..b02f09a --- /dev/null +++ b/lib9p/COPYRIGHT @@ -0,0 +1,47 @@ +Copyright 2016 Jakub Klama +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. + +Some parts of the code are based on libixp (http://libs.suckless.org/libixp) +library code released under following license: + +© 2005-2006 Anselm R. Garbe +© 2006-2010 Kris Maglione + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/lib9p/GNUmakefile b/lib9p/GNUmakefile new file mode 100644 index 0000000..5764787 --- /dev/null +++ b/lib9p/GNUmakefile @@ -0,0 +1,48 @@ +CFLAGS := \ + -Weverything \ + -Wno-padded \ + -Wno-gnu-zero-variadic-macro-arguments \ + -Wno-format-nonliteral \ + -Werror \ + -g \ + -O0 + +LIB_SRCS := \ + pack.c \ + connection.c \ + request.c \ + log.c \ + hashtable.c \ + utils.c \ + sbuf/sbuf.c \ + transport/socket.c \ + backend/fs.c + +SERVER_SRCS := \ + example/server.c + +BUILD_DIR := build +LIB_OBJS := $(addprefix build/,$(LIB_SRCS:.c=.o)) +SERVER_OBJS := $(SERVER_SRCS:.c=.o) +LIB := lib9p.dylib +SERVER := server + +all: build $(LIB) $(SERVER) + +$(LIB): $(LIB_OBJS) + cc -dynamiclib $^ -o $@ + +$(SERVER): $(SERVER_OBJS) $(LIB) + cc $< -o build/$(SERVER) -L. -l9p + +clean: + rm -rf build + +build: + mkdir build + mkdir build/sbuf + mkdir build/transport + mkdir build/backend + +build/%.o: %.c + $(CC) $(CFLAGS) -c $< -o $@ diff --git a/lib9p/Makefile b/lib9p/Makefile new file mode 100644 index 0000000..751d3e1 --- /dev/null +++ b/lib9p/Makefile @@ -0,0 +1,16 @@ +LIB= 9p +SHLIB_MAJOR= 1 +SRCS= pack.c \ + connection.c \ + request.c log.c \ + hashtable.c \ + utils.c \ + transport/socket.c \ + backend/fs.c + +INCS= lib9p.h +CFLAGS= -g -O0 + +LIBADD= sbuf + +.include diff --git a/lib9p/backend/fs.c b/lib9p/backend/fs.c new file mode 100644 index 0000000..f135069 --- /dev/null +++ b/lib9p/backend/fs.c @@ -0,0 +1,759 @@ +/* + * Copyright 2016 Jakub Klama + * 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. + * + */ + +/* + * Based on libixp code: ©2007-2010 Kris Maglione + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 *, bool dotu); +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 *); +static void fs_flush(void *, struct l9p_request *); +static void fs_open(void *, struct l9p_request *); +static void fs_read(void *, struct l9p_request *); +static void fs_remove(void *, struct l9p_request *); +static void fs_stat(void *, struct l9p_request *); +static void fs_walk(void *, struct l9p_request *); +static void fs_write(void *, struct l9p_request *); +static void fs_wstat(void *, struct l9p_request *); +static void fs_freefid(void *softc, struct l9p_openfile *f); + +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 +{ + DIR *dir; + int fd; + char *name; + uid_t uid; + gid_t gid; +}; + +static struct openfile * +open_fid(const char *path) +{ + struct openfile *ret; + + ret = l9p_calloc(1, sizeof(*ret)); + ret->fd = -1; + ret->name = strdup(path); + return (ret); +} + +static void +dostat(struct l9p_stat *s, char *name, struct stat *buf, bool dotu) +{ + struct passwd *user; + struct group *group; + + memset(s, 0, sizeof(struct l9p_stat)); + + generate_qid(buf, &s->qid); + + s->type = 0; + s->dev = 0; + s->mode = buf->st_mode & 0777; + + if (S_ISDIR(buf->st_mode)) + s->mode |= L9P_DMDIR; + + if (S_ISLNK(buf->st_mode) && dotu) + s->mode |= L9P_DMSYMLINK; + + if (S_ISCHR(buf->st_mode) || S_ISBLK(buf->st_mode)) + s->mode |= L9P_DMDEVICE; + + if (S_ISSOCK(buf->st_mode)) + s->mode |= L9P_DMSOCKET; + + if (S_ISFIFO(buf->st_mode)) + s->mode |= L9P_DMNAMEDPIPE; + + s->atime = (uint32_t)buf->st_atime; + s->mtime = (uint32_t)buf->st_mtime; + s->length = (uint64_t)buf->st_size; + + /* XXX: not thread safe */ + s->name = strdup(basename(name)); + + if (!dotu) { + user = getpwuid(buf->st_uid); + group = getgrgid(buf->st_gid); + s->uid = user != NULL ? user->pw_name : NULL; + s->gid = group != NULL ? group->gr_name : NULL; + s->muid = user != NULL ? user->pw_name : NULL; + } else { + /* + * When using 9P2000.u, we don't need to bother about + * providing user and group names in textual form. + */ + s->n_uid = buf->st_uid; + s->n_gid = buf->st_gid; + s->n_muid = buf->st_uid; + + if (S_ISLNK(buf->st_mode)) { + char target[MAXPATHLEN]; + ssize_t ret = readlink(name, target, MAXPATHLEN); + + if (ret < 0) { + s->extension = NULL; + return; + } + + s->extension = strndup(target, (size_t)ret); + } + + if (S_ISBLK(buf->st_mode)) { + asprintf(&s->extension, "b %d %d", major(buf->st_rdev), + minor(buf->st_rdev)); + } + + if (S_ISCHR(buf->st_mode)) { + asprintf(&s->extension, "c %d %d", major(buf->st_rdev), + minor(buf->st_rdev)); + } + } +} + +static void +generate_qid(struct stat *buf, struct l9p_qid *qid) +{ + qid->path = buf->st_ino; + qid->version = 0; + + if (S_ISREG(buf->st_mode)) + qid->type |= L9P_QTFILE; + + if (S_ISDIR(buf->st_mode)) + qid->type |= L9P_QTDIR; + + if (S_ISLNK(buf->st_mode)) + qid->type |= L9P_QTSYMLINK; +} + +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); + } + + /* 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, (int)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_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 openfile *file; + + file = req->lr_fid->lo_aux; + assert(file != NULL); + + if (file->dir) { + closedir(file->dir); + file->dir = NULL; + } else if (file->fd != -1) { + close(file->fd); + file->fd = -1; + } + + l9p_respond(req, 0); +} + +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; + mode_t mode = req->lr_req.tcreate.perm & 0777; + + 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, mode); + else if (req->lr_req.tcreate.perm & L9P_DMSYMLINK) { + if (symlink(req->lr_req.tcreate.extension, newname) != 0) { + l9p_respond(req, errno); + return; + } + } else if (req->lr_req.tcreate.perm & L9P_DMNAMEDPIPE) { + if (mkfifo(newname, mode) != 0) { + l9p_respond(req, errno); + return; + } + } else if (req->lr_req.tcreate.perm & L9P_DMSOCKET) { + struct sockaddr_un sun; + int s = socket(AF_UNIX, SOCK_STREAM, 0); + + if (s < 0) { + l9p_respond(req, errno); + return; + } + + sun.sun_family = AF_UNIX; + sun.sun_len = sizeof(struct sockaddr_un); + strncpy(sun.sun_path, newname, sizeof(sun.sun_path)); + + if (bind(s, (struct sockaddr *)&sun, sun.sun_len) < 0) { + l9p_respond(req, errno); + return; + } + + if (close(s) != 0) { + l9p_respond(req, errno); + return; + } + } else if (req->lr_req.tcreate.perm & L9P_DMDEVICE) { + char type; + int major, minor; + + if (sscanf(req->lr_req.tcreate.extension, "%c %u %u", + &type, &major, &minor) < 2) { + l9p_respond(req, EINVAL); + return; + } + + switch (type) { + case 'b': + if (mknod(newname, S_IFBLK | mode, + makedev(major, minor)) != 0) + { + l9p_respond(req, errno); + return; + } + break; + case 'c': + if (mknod(newname, S_IFCHR | mode, + makedev(major, minor)) != 0) + { + l9p_respond(req, errno); + return; + } + break; + default: + l9p_respond(req, EINVAL); + return; + } + } else { + file->fd = open(newname, + O_CREAT | O_TRUNC | req->lr_req.tcreate.mode, + mode); + } + + if (lchown(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) +{ + + /* XXX: not used because this transport is synchronous */ + 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; + struct stat st; + + assert(file != NULL); + + if (stat(file->name, &st) != 0) { + l9p_respond(req, errno); + return; + } + + if (!check_access(&st, file->uid, req->lr_req.topen.mode)) { + l9p_respond(req, EPERM); + return; + } + + if (S_ISDIR(st.st_mode)) + file->dir = opendir(file->name); + else { + file->fd = open(file->name, req->lr_req.topen.mode); + if (file->fd < 0) { + l9p_respond(req, EPERM); + return; + } + } + + req->lr_resp.ropen.iounit = conn->lc_max_io_size; + l9p_respond(req, 0); +} + +static void +fs_read(void *softc __unused, struct l9p_request *req) +{ + struct openfile *file; + struct l9p_stat l9stat; + bool dotu = req->lr_conn->lc_version >= L9P_2000U; + ssize_t ret; + + file = req->lr_fid->lo_aux; + assert(file != NULL); + + if (file->dir != NULL) { + struct dirent *d; + struct stat st; + + for (;;) { + d = readdir(file->dir); + if (d) { + lstat(d->d_name, &st); + dostat(&l9stat, d->d_name, &st, dotu); + if (l9p_pack_stat(req, &l9stat) != 0) { + seekdir(file->dir, -1); + break; + } + + continue; + } + + break; + } + } else { + size_t niov = l9p_truncate_iov(req->lr_data_iov, + req->lr_data_niov, req->lr_req.io.count); + +#if defined(__FreeBSD__) + ret = preadv(file->fd, req->lr_data_iov, niov, + req->lr_req.io.offset); +#else + /* XXX: not thread safe, should really use aio_listio. */ + if (lseek(file->fd, (off_t)req->lr_req.io.offset, SEEK_SET) < 0) + { + l9p_respond(req, errno); + return; + } + + ret = (uint32_t)readv(file->fd, req->lr_data_iov, (int)niov); +#endif + + if (ret < 0) { + l9p_respond(req, errno); + return; + } + + req->lr_resp.io.count = (uint32_t)ret; + } + + l9p_respond(req, 0); +} + +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 (lstat(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 __unused, struct l9p_request *req) +{ + struct openfile *file; + struct stat st; + bool dotu = req->lr_conn->lc_version >= L9P_2000U; + + file = req->lr_fid->lo_aux; + assert(file); + + lstat(file->name, &st); + dostat(&req->lr_resp.rstat.stat, file->name, &st, dotu); + + l9p_respond(req, 0); +} + +static void +fs_walk(void *softc __unused, struct l9p_request *req) +{ + uint16_t i; + struct stat buf; + struct openfile *file = req->lr_fid->lo_aux; + struct openfile *newfile; + char name[MAXPATHLEN]; + + strcpy(name, file->name); + + for (i = 0; i < req->lr_req.twalk.nwname; i++) { + strcat(name, "/"); + strcat(name, req->lr_req.twalk.wname[i]); + if (lstat(name, &buf) < 0){ + l9p_respond(req, ENOENT); + return; + } + + generate_qid(&buf, &req->lr_resp.rwalk.wqid[i]); + } + + newfile = open_fid(name); + newfile->uid = file->uid; + req->lr_newfid->lo_aux = newfile; + req->lr_resp.rwalk.nwqid = i; + l9p_respond(req, 0); +} + +static void +fs_write(void *softc, struct l9p_request *req) +{ + struct fs_softc *sc = softc; + struct openfile *file; + ssize_t ret; + + 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); + +#if defined(__FreeBSD__) + ret = pwritev(file->fd, req->lr_data_iov, niov, + req->lr_req.io.offset); +#else + /* XXX: not thread safe, should really use aio_listio. */ + if (lseek(file->fd, (off_t)req->lr_req.io.offset, SEEK_SET) < 0) { + l9p_respond(req, errno); + return; + } + + ret = writev(file->fd, req->lr_data_iov, + (int)niov); +#endif + + if (ret < 0) { + l9p_respond(req, errno); + return; + } + + req->lr_resp.io.count = (uint32_t)ret; + 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); + + /* + * XXX: + * + * stat(9P) sez: + * + * Either all the changes in wstat request happen, or none of them + * does: if the request succeeds, all changes were made; if it fails, + * none were. + * + * Atomicity is clearly missing in current implementation. + */ + + if (sc->fs_readonly) { + l9p_respond(req, EROFS); + return; + } + + if (l9stat->atime != (uint32_t)~0) { + /* XXX: not implemented, ignore */ + } + + if (l9stat->mtime != (uint32_t)~0) { + /* XXX: not implemented, ignore */ + } + + if (l9stat->dev != (uint32_t)~0) { + l9p_respond(req, EPERM); + return; + } + + if (l9stat->length != (uint64_t)~0) { + if (file->dir != NULL) { + l9p_respond(req, EINVAL); + return; + } + + if (truncate(file->name, (off_t)l9stat->length) != 0) { + l9p_respond(req, errno); + return; + } + } + + if (l9stat->n_uid != (uid_t)~0) { + if (lchown(file->name, l9stat->n_uid, (gid_t)-1) != 0) { + l9p_respond(req, errno); + return; + } + } + + if (l9stat->n_gid != (uid_t)~0) { + if (lchown(file->name, (uid_t)-1, l9stat->n_gid) != 0) { + l9p_respond(req, errno); + return; + } + } + + if (l9stat->mode != (uint32_t)~0) { + if (chmod(file->name, l9stat->mode & 0777) != 0) { + l9p_respond(req, errno); + return; + } + } + + if (strlen(l9stat->name) > 0) { + /* XXX: not thread safe */ + char *dir = dirname(file->name); + char *newname; + + asprintf(&newname, "%s/%s", dir, l9stat->name); + rename(file->name, newname); + + free(newname); + } + + l9p_respond(req, 0); +} + +static void +fs_freefid(void *softc __unused, struct l9p_openfile *fid) +{ + struct openfile *f = fid->lo_aux; + + if (f == NULL) { + /* Nothing to do here */ + return; + } + + if (f->fd != -1) + close(f->fd); + + if (f->dir) + closedir(f->dir); + + free(f->name); + free(f); +} + +int +l9p_backend_fs_init(struct l9p_backend **backendp, const char *root) +{ + struct l9p_backend *backend; + struct fs_softc *sc; + + backend = l9p_malloc(sizeof(*backend)); + backend->attach = fs_attach; + backend->clunk = fs_clunk; + backend->create = fs_create; + backend->flush = fs_flush; + backend->open = fs_open; + backend->read = fs_read; + backend->remove = fs_remove; + backend->stat = fs_stat; + backend->walk = fs_walk; + backend->write = fs_write; + backend->wstat = fs_wstat; + backend->freefid = fs_freefid; + + sc = l9p_malloc(sizeof(*sc)); + sc->fs_rootpath = strdup(root); + sc->fs_readonly = false; + backend->softc = sc; + + setpassent(1); + + *backendp = backend; + return (0); +} diff --git a/lib9p/connection.c b/lib9p/connection.c new file mode 100644 index 0000000..2edbbba --- /dev/null +++ b/lib9p/connection.c @@ -0,0 +1,178 @@ +/* + * Copyright 2016 Jakub Klama + * 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. + * + */ + +#include +#include +#include +#include +#include +#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; + + server = l9p_calloc(1, sizeof (*server)); + server->ls_max_version = L9P_2000U; + server->ls_backend = backend; + LIST_INIT(&server->ls_conns); + + *serverp = server; + return (0); +} + +int +l9p_connection_init(struct l9p_server *server, struct l9p_connection **conn) +{ + struct l9p_connection *newconn; + + assert(server != NULL); + assert(conn != NULL); + + 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); +} + +void +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; +} + +void +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; +} + +void +l9p_connection_recv(struct l9p_connection *conn, const struct iovec *iov, + const size_t niov, void *aux) +{ + struct l9p_request *req; + + 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; + + if (l9p_pufcall(&req->lr_req_msg, &req->lr_req, conn->lc_version) != 0) { + L9P_LOG(L9P_WARNING, "cannot unpack received message"); + return; + } + + 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; + + 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); + } + + return (file); +} + +void +l9p_connection_remove_fid(struct l9p_connection *conn, struct l9p_openfile *fid) +{ + + conn->lc_server->ls_backend->freefid(conn->lc_server->ls_backend->softc, + fid); + + ht_remove(&conn->lc_files, fid->lo_fid); +} diff --git a/lib9p/example/server.c b/lib9p/example/server.c new file mode 100644 index 0000000..7241234 --- /dev/null +++ b/lib9p/example/server.c @@ -0,0 +1,52 @@ +/* + * Copyright 2016 Jakub Klama + * 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. + * + */ + +#include +#include +#include +#include "../lib9p.h" +#include "../transport/socket.h" +#include "../backend/fs.h" + +int +main(int argc, const char *argv[]) +{ + struct l9p_backend *fs_backend; + struct l9p_server *server; + + if (argc < 2) + errx(1, "Usage: server "); + + if (l9p_backend_fs_init(&fs_backend, argv[1]) != 0) + err(1, "cannot init backend"); + + if (l9p_server_init(&server, fs_backend) != 0) + err(1, "cannot create server"); + + server->ls_max_version = L9P_2000U; + l9p_start_server(server, "0.0.0.0", "564"); +} diff --git a/lib9p/fcall.h b/lib9p/fcall.h new file mode 100644 index 0000000..a67cb7a --- /dev/null +++ b/lib9p/fcall.h @@ -0,0 +1,236 @@ +/* + * Copyright 2016 Jakub Klama + * 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. + * + */ + +/* + * Based on libixp code: ©2007-2010 Kris Maglione + */ + +#ifndef LIB9P_FCALL_H +#define LIB9P_FCALL_H + +#include + +#define L9P_MAX_WELEM 256 + +enum l9p_ftype { + L9P_TVERSION = 100, + L9P_RVERSION, + L9P_TAUTH = 102, + L9P_RAUTH, + L9P_TATTACH = 104, + L9P_RATTACH, + L9P_TERROR = 106, /* illegal */ + L9P_RERROR, + L9P_TFLUSH = 108, + L9P_RFLUSH, + L9P_TWALK = 110, + L9P_RWALK, + L9P_TOPEN = 112, + L9P_ROPEN, + L9P_TCREATE = 114, + L9P_RCREATE, + L9P_TREAD = 116, + L9P_RREAD, + L9P_TWRITE = 118, + L9P_RWRITE, + L9P_TCLUNK = 120, + L9P_RCLUNK, + L9P_TREMOVE = 122, + L9P_RREMOVE, + L9P_TSTAT = 124, + L9P_RSTAT, + L9P_TWSTAT = 126, + L9P_RWSTAT, +}; + +enum l9p_qid_type { + L9P_QTDIR = 0x80, /* type bit for directories */ + L9P_QTAPPEND = 0x40, /* type bit for append only files */ + L9P_QTEXCL = 0x20, /* type bit for exclusive use files */ + L9P_QTMOUNT = 0x10, /* type bit for mounted channel */ + L9P_QTAUTH = 0x08, /* type bit for authentication file */ + L9P_QTTMP = 0x04, /* type bit for non-backed-up file */ + L9P_QTSYMLINK = 0x02, /* type bit for symbolic link */ + L9P_QTFILE = 0x00 /* type bits for plain file */ +}; + +#define L9P_DMDIR 0x80000000 +enum { + L9P_DMAPPEND = 0x40000000, + L9P_DMEXCL = 0x20000000, + L9P_DMMOUNT = 0x10000000, + L9P_DMAUTH = 0x08000000, + L9P_DMTMP = 0x04000000, + L9P_DMSYMLINK = 0x02000000, + /* 9P2000.u extensions */ + L9P_DMDEVICE = 0x00800000, + L9P_DMNAMEDPIPE = 0x00200000, + L9P_DMSOCKET = 0x00100000, + L9P_DMSETUID = 0x00080000, + L9P_DMSETGID = 0x00040000, +}; + +enum { + L9P_OREAD = 0, /* open for read */ + L9P_OWRITE = 1, /* write */ + L9P_ORDWR = 2, /* read and write */ + L9P_OEXEC = 3, /* execute, == read but check execute permission */ + L9P_OTRUNC = 16, /* or'ed in (except for exec), truncate file first */ + L9P_OCEXEC = 32, /* or'ed in, close on exec */ + L9P_ORCLOSE = 64, /* or'ed in, remove on close */ + L9P_ODIRECT = 128, /* or'ed in, direct access */ + L9P_ONONBLOCK = 256, /* or'ed in, non-blocking call */ + L9P_OEXCL = 0x1000, /* or'ed in, exclusive use (create only) */ + L9P_OLOCK = 0x2000, /* or'ed in, lock after opening */ + L9P_OAPPEND = 0x4000 /* or'ed in, append only */ +}; + +struct l9p_hdr { + uint8_t type; + uint16_t tag; + uint32_t fid; +}; + +struct l9p_qid { + enum l9p_qid_type type; + uint32_t version; + uint64_t path; +}; + +struct l9p_stat { + uint16_t type; + uint32_t dev; + struct l9p_qid qid; + uint32_t mode; + uint32_t atime; + uint32_t mtime; + uint64_t length; + char *name; + char *uid; + char *gid; + char *muid; + char *extension; + uid_t n_uid; + gid_t n_gid; + uid_t n_muid; +}; + +struct l9p_f_version { + struct l9p_hdr hdr; + uint32_t msize; + char *version; +}; + +struct l9p_f_tflush { + struct l9p_hdr hdr; + uint16_t oldtag; +}; + +struct l9p_f_error { + struct l9p_hdr hdr; + char *ename; + uint32_t errnum; +}; + +struct l9p_f_ropen { + struct l9p_hdr hdr; + struct l9p_qid qid; + uint32_t iounit; +}; + +struct l9p_f_rauth { + struct l9p_hdr hdr; + struct l9p_qid aqid; +}; + +struct l9p_f_attach { + struct l9p_hdr hdr; + uint32_t afid; + char *uname; + char *aname; + uid_t n_uname; +}; + +struct l9p_f_tcreate { + struct l9p_hdr hdr; + uint32_t perm; + char *name; + uint8_t mode; /* +Topen */ + char *extension; +}; + +struct l9p_f_twalk { + struct l9p_hdr hdr; + uint32_t newfid; + uint16_t nwname; + char *wname[L9P_MAX_WELEM]; +}; + +struct l9p_f_rwalk { + struct l9p_hdr hdr; + uint16_t nwqid; + struct l9p_qid wqid[L9P_MAX_WELEM]; +}; + +struct l9p_f_io { + struct l9p_hdr hdr; + uint64_t offset; /* Tread, Twrite */ + uint32_t count; /* Tread, Twrite, Rread */ + char *data; /* Twrite, Rread */ +}; + +struct l9p_f_rstat { + struct l9p_hdr hdr; + struct l9p_stat stat; +}; + +struct l9p_f_twstat { + struct l9p_hdr hdr; + struct l9p_stat stat; +}; + +union l9p_fcall { + struct l9p_hdr hdr; + struct l9p_f_version version; + struct l9p_f_tflush tflush; + struct l9p_f_ropen ropen; + struct l9p_f_ropen rcreate; + struct l9p_f_ropen rattach; + struct l9p_f_error error; + struct l9p_f_rauth rauth; + struct l9p_f_attach tattach; + struct l9p_f_attach tauth; + struct l9p_f_tcreate tcreate; + struct l9p_f_tcreate topen; + struct l9p_f_twalk twalk; + struct l9p_f_rwalk rwalk; + struct l9p_f_twstat twstat; + struct l9p_f_rstat rstat; + struct l9p_f_io io; +}; + +#endif /* LIB9P_FCALL_H */ diff --git a/lib9p/hashtable.c b/lib9p/hashtable.c new file mode 100644 index 0000000..0e2bd4e --- /dev/null +++ b/lib9p/hashtable.c @@ -0,0 +1,172 @@ +/* + * Copyright 2016 Jakub Klama + * 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include "lib9p_impl.h" +#include "hashtable.h" + +void +ht_init(struct ht *h, size_t size) +{ + size_t i; + + memset(h, 0, sizeof(struct ht)); + h->ht_nentries = size; + h->ht_entries = l9p_calloc(size, sizeof(struct ht_entry)); + + for (i = 0; i < size; i++) + TAILQ_INIT(&h->ht_entries[i].hte_items); +} + +void +ht_destroy(struct ht *h) +{ + struct ht_entry *he; + struct ht_item *hi; + size_t i; + + for (i = 0; i < h->ht_nentries; i++) { + he = &h->ht_entries[i]; + hi = TAILQ_FIRST(&he->hte_items); + + while ((hi = TAILQ_NEXT(hi, hti_link)) != NULL) + TAILQ_REMOVE(&he->hte_items, hi, hti_link); + } + + free(h->ht_entries); + free(h); +} + +void * +ht_find(struct ht *h, uint32_t hash) +{ + struct ht_entry *entry; + struct ht_item *item; + + entry = &h->ht_entries[hash % h->ht_nentries]; + + TAILQ_FOREACH(item, &entry->hte_items, hti_link) { + if (item->hti_hash == hash) + return (item->hti_data); + } + + return (NULL); +} + +int +ht_add(struct ht *h, uint32_t hash, void *value) +{ + struct ht_entry *entry; + struct ht_item *item; + + entry = &h->ht_entries[hash % h->ht_nentries]; + + TAILQ_FOREACH(item, &entry->hte_items, hti_link) { + if (item->hti_hash == hash) { + errno = EEXIST; + return (-1); + } + } + + item = l9p_calloc(1, sizeof(struct ht_item)); + item->hti_hash = hash; + item->hti_data = value; + TAILQ_INSERT_TAIL(&entry->hte_items, item, hti_link); + + return (0); +} + +int +ht_remove(struct ht *h, uint32_t hash) +{ + struct ht_entry *entry; + struct ht_item *item, *tmp; + size_t slot = hash % h->ht_nentries; + + entry = &h->ht_entries[slot]; + + TAILQ_FOREACH_SAFE(item, &entry->hte_items, hti_link, tmp) { + if (item->hti_hash == hash) { + TAILQ_REMOVE(&entry->hte_items, item, hti_link); + free(item->hti_data); + free(item); + return (0); + } + } + + errno = ENOENT; + return (-1); +} + +int +ht_remove_at_iter(struct ht_iter *iter) +{ + assert(iter != NULL); + + if (iter->htit_cursor == NULL) { + errno = EINVAL; + return (-1); + } + + TAILQ_REMOVE(&iter->htit_parent->ht_entries[iter->htit_slot].hte_items, + iter->htit_cursor, hti_link); + return (0); +} + +void +ht_iter(struct ht *h, struct ht_iter *iter) +{ + iter->htit_parent = h; + iter->htit_slot = 0; + iter->htit_cursor = TAILQ_FIRST(&h->ht_entries[0].hte_items); +} + +void * +ht_next(struct ht_iter *iter) +{ + struct ht_item *item; + + item = iter->htit_cursor; + +retry: + if ((iter->htit_cursor = TAILQ_NEXT(iter->htit_cursor, hti_link)) == NULL) + { + if (iter->htit_slot == iter->htit_parent->ht_nentries) + return (NULL); + + iter->htit_slot++; + goto retry; + + } + + return (item); +} diff --git a/lib9p/hashtable.h b/lib9p/hashtable.h new file mode 100644 index 0000000..0b28385 --- /dev/null +++ b/lib9p/hashtable.h @@ -0,0 +1,63 @@ +/* + * Copyright 2016 Jakub Klama + * 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_HASHTABLE_H +#define LIB9P_HASHTABLE_H + +#include + +struct ht { + struct ht_entry * ht_entries; + size_t ht_nentries; +}; + +struct ht_entry { + TAILQ_HEAD(, ht_item) hte_items; +}; + +struct ht_item { + uint32_t hti_hash; + void * hti_data; + TAILQ_ENTRY(ht_item) hti_link; +}; + +struct ht_iter { + struct ht * htit_parent; + struct ht_item * htit_cursor; + size_t htit_slot; +}; + +void ht_init(struct ht *h, size_t size); +void ht_destroy(struct ht *h); +void *ht_find(struct ht *h, uint32_t hash); +int ht_add(struct ht *h, uint32_t hash, void *value); +int ht_remove(struct ht *h, uint32_t hash); +int ht_remove_at_iter(struct ht_iter *iter); +void ht_iter(struct ht *h, struct ht_iter *iter); +void *ht_next(struct ht_iter *iter); + +#endif /* LIB9P_HASHTABLE_H */ diff --git a/lib9p/lib9p.h b/lib9p/lib9p.h new file mode 100644 index 0000000..4de3d26 --- /dev/null +++ b/lib9p/lib9p.h @@ -0,0 +1,186 @@ +/* + * Copyright 2016 Jakub Klama + * 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 +#include +#include +#include +#include + +#if defined(__FreeBSD__) +#include +#else +#include "sbuf/sbuf.h" +#endif + +#include "fcall.h" +#include "hashtable.h" + +#define L9P_DEFAULT_MSIZE 8192 +#define L9P_MAX_IOV 8 + +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 *, + const size_t, const 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_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_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 { + 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; + 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_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, + enum l9p_version version); +int 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_request *req, struct l9p_stat *s); + +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_on_send_response(struct l9p_connection *conn, + l9p_send_response_t *cb, void *aux); +void l9p_connection_on_get_response_buffer(struct l9p_connection *conn, + l9p_get_response_buffer_t *cb, void *aux); +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_openfile *l9p_connection_alloc_fid(struct l9p_connection *conn, + uint32_t fid); +void l9p_connection_remove_fid(struct l9p_connection *conn, + struct l9p_openfile *fid); + +void l9p_dispatch_request(struct l9p_request *req); +void l9p_respond(struct l9p_request *req, int errnum); + +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_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_stat(struct l9p_stat *st, struct sbuf *sb); +void l9p_freefcall(union l9p_fcall *fcall); +void l9p_freestat(struct l9p_stat *stat); + +int l9p_backend_fs_init(struct l9p_backend **backendp, const char *root); + +#endif /* LIB9P_LIB9P_H */ diff --git a/lib9p/lib9p_impl.h b/lib9p/lib9p_impl.h new file mode 100644 index 0000000..3653517 --- /dev/null +++ b/lib9p/lib9p_impl.h @@ -0,0 +1,78 @@ +/* + * Copyright 2016 Jakub Klama + * 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_IMPL_H +#define LIB9P_LIB9P_IMPL_H + +#include +#include + +#ifndef _KERNEL +static inline void * +l9p_malloc(size_t size) +{ + void *r = malloc(size); + + if (r == NULL) { + fprintf(stderr, "cannot allocate %zd bytes: out of memory\n", + size); + abort(); + } + + return (r); +} + +static inline void * +l9p_calloc(size_t n, size_t size) +{ + void *r = calloc(n, size); + + if (r == NULL) { + fprintf(stderr, "cannot allocate %zd bytes: out of memory\n", + n * size); + abort(); + } + + return (r); +} + +static inline void * +l9p_realloc(void *ptr, size_t newsize) +{ + void *r = realloc(ptr, newsize); + + if (r == NULL) { + fprintf(stderr, "cannot allocate %zd bytes: out of memory\n", + newsize); + abort(); + } + + return (r); +} +#endif /* _KERNEL */ + +#endif /* LIB9P_LIB9P_IMPL_H */ diff --git a/lib9p/log.c b/lib9p/log.c new file mode 100644 index 0000000..ff65b3f --- /dev/null +++ b/lib9p/log.c @@ -0,0 +1,49 @@ +/* + * Copyright 2016 Jakub Klama + * 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. + * + */ + +#include +#include +#include "log.h" + +static const char *l9p_log_level_names[] = { + "DEBUG", + "INFO", + "WARN", + "ERROR" +}; + +void +l9p_logf(enum l9p_log_level level, const char *func, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + fprintf(stderr, "[%s]\t %s: ", l9p_log_level_names[level], func); + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); + va_end(ap); +} diff --git a/lib9p/log.h b/lib9p/log.h new file mode 100644 index 0000000..7d3d8d3 --- /dev/null +++ b/lib9p/log.h @@ -0,0 +1,47 @@ +/* + * Copyright 2016 Jakub Klama + * 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_LOG_H +#define LIB9P_LOG_H + +enum l9p_log_level +{ + L9P_DEBUG, + L9P_INFO, + L9P_WARNING, + L9P_ERROR +}; + +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 */ diff --git a/lib9p/pack.c b/lib9p/pack.c new file mode 100644 index 0000000..13758c5 --- /dev/null +++ b/lib9p/pack.c @@ -0,0 +1,453 @@ +/* + * Copyright 2016 Jakub Klama + * 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. + * + */ + +/* + * Based on libixp code: ©2007-2010 Kris Maglione + */ + +#include +#include +#include +#include +#include +#include +#include "lib9p.h" +#include "lib9p_impl.h" + +#define N(ary) (sizeof(ary) / sizeof(*ary)) +#define STRING_SIZE(s) (L9P_WORD + (s != NULL ? (uint16_t)strlen(s) : 0)) +#define QID_SIZE (L9P_BYTE + L9P_DWORD + L9P_QWORD) + +static int l9p_iov_io(struct l9p_message *, void *, size_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_puqid(struct l9p_message *, struct l9p_qid *); +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; + + assert(msg != NULL); + + if (len == 0) + return (0); + + if (msg->lm_cursor_iov >= msg->lm_niov) + return (-1); + + assert(buffer != NULL); + + 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_PACK) { + memcpy((char *)msg->lm_iov[idx].iov_base + + msg->lm_cursor_offset, (char *)buffer + done, + towrite); + } + + if (msg->lm_mode == L9P_UNPACK) { + memcpy((char *)buffer + done, + (char *)msg->lm_iov[idx].iov_base + + msg->lm_cursor_offset, towrite); + } + + msg->lm_cursor_offset += towrite; + + if (space - towrite == 0) { + /* Advance to next iov */ + msg->lm_cursor_iov++; + msg->lm_cursor_offset = 0; + + if (msg->lm_cursor_iov > msg->lm_niov) + return (-1); + } + + done += towrite; + left -= towrite; + } + + msg->lm_size += done; + return ((int)done); +} + +static inline int +l9p_pu8(struct l9p_message *msg, uint8_t *val) +{ + + 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))); +} + +static inline int +l9p_pu32(struct l9p_message *msg, uint32_t *val) +{ + + 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))); +} + +static int +l9p_pustring(struct l9p_message *msg, char **s) +{ + uint16_t len; + + if (msg->lm_mode == L9P_PACK) + len = *s != NULL ? (uint16_t)strlen(*s) : 0; + + if (l9p_pu16(msg, &len) < 0) + return (-1); + + if (msg->lm_mode == L9P_UNPACK) + *s = l9p_calloc(1, len + 1); + + 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) +{ + size_t i; + int ret; + int r = 0; + + l9p_pu16(msg, num); + + for (i = 0; i < MIN(*num, max); i++) { + ret = l9p_pustring(msg, &strings[i]); + if (ret < 1) + return (-1); + + r += ret; + } + + return (r); +} + +static int +l9p_puqid(struct l9p_message *msg, struct l9p_qid *qid) +{ + int r = 0; + + r += l9p_pu8(msg, (uint8_t *) & qid->type); + r += l9p_pu32(msg, &qid->version); + r += l9p_pu64(msg, &qid->path); + + return (r); +} + +static int +l9p_puqids(struct l9p_message *msg, uint16_t *num, struct l9p_qid *qids) +{ + int i, ret, r = 0; + l9p_pu16(msg, num); + + 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; + + 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); + + 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); +} + +int +l9p_pufcall(struct l9p_message *msg, union l9p_fcall *fcall, + enum l9p_version version) +{ + uint32_t length = 0; + + 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; + + 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_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); + 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_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_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_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_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_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_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; + } + + if (msg->lm_mode == L9P_PACK) { + /* Rewind to the beginning */ + uint32_t len = (uint32_t)msg->lm_size; + msg->lm_cursor_offset = 0; + msg->lm_cursor_iov = 0; + + /* + * 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 (fcall->hdr.type == L9P_RREAD) + len += fcall->io.count; + + l9p_pu32(msg, &len); + } + + return (0); +} + +void +l9p_freefcall(union l9p_fcall *fcall) +{ + uint16_t i; + + switch (fcall->hdr.type) { + case L9P_TVERSION: + case L9P_RVERSION: + free(fcall->version.version); + return; + case L9P_TATTACH: + free(fcall->tattach.aname); + free(fcall->tattach.uname); + return; + case L9P_TWALK: + for (i = 0; i < fcall->twalk.nwname; i++) + free(fcall->twalk.wname[i]); + return; + case L9P_TCREATE: + case L9P_TOPEN: + free(fcall->tcreate.name); + free(fcall->tcreate.extension); + return; + case L9P_RSTAT: + l9p_freestat(&fcall->rstat.stat); + return; + case L9P_TWSTAT: + l9p_freestat(&fcall->twstat.stat); + return; + } +} + +void +l9p_freestat(struct l9p_stat *stat) +{ + free(stat->name); + free(stat->extension); + free(stat->uid); + free(stat->gid); + free(stat->muid); +} + +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); +} diff --git a/lib9p/request.c b/lib9p/request.c new file mode 100644 index 0000000..7ca2447 --- /dev/null +++ b/lib9p/request.c @@ -0,0 +1,439 @@ +/* + * Copyright 2016 Jakub Klama + * 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. + * + */ + +#include +#include +#include +#include +#include +#if defined(__FreeBSD__) +#include +#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])) + +static void l9p_dispatch_tversion(struct l9p_request *req); +static void l9p_dispatch_tattach(struct l9p_request *req); +static void l9p_dispatch_tclunk(struct l9p_request *req); +static void l9p_dispatch_tflush(struct l9p_request *req); +static void l9p_dispatch_tcreate(struct l9p_request *req); +static void l9p_dispatch_topen(struct l9p_request *req); +static void l9p_dispatch_tread(struct l9p_request *req); +static void l9p_dispatch_tremove(struct l9p_request *req); +static void l9p_dispatch_tstat(struct l9p_request *req); +static void l9p_dispatch_twalk(struct l9p_request *req); +static void l9p_dispatch_twrite(struct l9p_request *req); +static void l9p_dispatch_twstat(struct l9p_request *req); + +static const struct +{ + 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} +}; + +static const char *l9p_versions[] = { + "9P2000", + "9P2000.u", + "9P2000.L" +}; + +void +l9p_dispatch_request(struct l9p_request *req) +{ +#if defined(L9P_DEBUG) + struct sbuf *sb; +#endif + size_t i; + +#if defined(L9P_DEBUG) + sb = sbuf_new_auto(); + 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); +#endif + + 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; + } + } + + 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; + size_t iosize; +#if defined(L9P_DEBUG) + struc sbuf *sb; +#endif + + switch (req->lr_req.hdr.type) { + case L9P_TCLUNK: + case L9P_TREMOVE: + if (req->lr_fid != NULL) + l9p_connection_remove_fid(conn, req->lr_fid); + break; + + case L9P_TWALK: + if (errnum != 0 && req->lr_newfid != NULL && + req->lr_newfid != req->lr_fid) + l9p_connection_remove_fid(conn, req->lr_newfid); + + break; + } + + req->lr_resp.hdr.tag = req->lr_req.hdr.tag; + + 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 = (uint32_t)errnum; + } + +#if defined(L9P_DEBUG) + sb = sbuf_new_auto(); + l9p_describe_fcall(&req->lr_resp, L9P_2000, sb); + sbuf_done(sb); + + L9P_LOG(L9P_DEBUG, "%s", sbuf_data(sb)); + sbuf_delete(sb); +#endif + + if (l9p_pufcall(&req->lr_resp_msg, &req->lr_resp, conn->lc_version) != 0) { + L9P_LOG(L9P_ERROR, "cannot pack response"); + goto out; + } + + 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: + l9p_freefcall(&req->lr_req); + l9p_freefcall(&req->lr_resp); + 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); + + 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) { + l9p_freestat(st); + return (-1); + } + + if (l9p_pustat(msg, st, conn->lc_version) < 0) { + l9p_freestat(st); + return (-1); + } + + req->lr_resp.io.count += size; + l9p_freestat(st); + 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; + 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; + } + } + + 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]); + + 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; + + 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; + + 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); +} + +static void +l9p_dispatch_tflush(struct l9p_request *req) +{ + struct l9p_connection *conn = req->lr_conn; + + 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); +} + +static void +l9p_dispatch_tcreate(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 == NULL) { + l9p_respond(req, EBADF); + return; + } + + 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; + + 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, ENOSYS); + return; + } + + 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; + + 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); + + 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; + + 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, ENOSYS); + return; + } + + 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; + + 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, EBADF); + 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); +} + +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); +} diff --git a/lib9p/sbuf/sbuf.c b/lib9p/sbuf/sbuf.c new file mode 100644 index 0000000..0978d77 --- /dev/null +++ b/lib9p/sbuf/sbuf.c @@ -0,0 +1,106 @@ +/* + * Copyright 2016 Jakub Klama + * 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. + * + */ + +/* + * Minimal libsbuf reimplementation for Mac OS X. + */ + +#include +#include +#include +#include +#include +#include "sbuf.h" + +#define SBUF_INITIAL_SIZE 128 + +struct sbuf * +sbuf_new_auto() +{ + struct sbuf *s; + + s = malloc(sizeof(struct sbuf)); + s->s_buf = calloc(1, SBUF_INITIAL_SIZE + 1); + s->s_capacity = SBUF_INITIAL_SIZE; + s->s_size = 0; + + return (s); +} + +int +sbuf_printf(struct sbuf *s, const char *fmt, ...) +{ + int ret; + va_list ap; + + va_start(ap, fmt); + ret = sbuf_vprintf(s, fmt, ap); + va_end(ap); + + return (ret); +} + +int +sbuf_vprintf(struct sbuf *s, const char *fmt, va_list args) +{ + va_list copy; + int req; + + va_copy(copy, args); + req = vsnprintf(NULL, 0, fmt, copy); + va_end(copy); + + if (s->s_size + req >= s->s_capacity) { + s->s_capacity = s->s_size + req + 1; + s->s_buf = realloc(s->s_buf, (size_t)s->s_capacity); + } + + req = vsnprintf(s->s_buf + s->s_size, req + 1, fmt, args); + s->s_size += req; + + return (req); +} + +char * +sbuf_data(struct sbuf *s) +{ + return (s->s_buf); +} + +int +sbuf_done(struct sbuf *s) +{ + s->s_buf[s->s_size] = '\0'; + return (0); +} + +void +sbuf_delete(struct sbuf *s) +{ + free(s->s_buf); + free(s); +} diff --git a/lib9p/sbuf/sbuf.h b/lib9p/sbuf/sbuf.h new file mode 100644 index 0000000..fd6dd62 --- /dev/null +++ b/lib9p/sbuf/sbuf.h @@ -0,0 +1,53 @@ +/* + * Copyright 2016 Jakub Klama + * 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. + * + */ + +/* + * Minimal libsbuf reimplementation for Mac OS X. + */ + +#ifndef LIB9P_SBUF_H +#define LIB9P_SBUF_H + +#include + +struct sbuf +{ + char *s_buf; + int s_size; + int s_capacity; + int s_position; +}; + +struct sbuf *sbuf_new_auto(void); +int sbuf_printf(struct sbuf *s, const char *fmt, ...); +int sbuf_vprintf(struct sbuf *s, const char *fmt, va_list args); +int sbuf_done(struct sbuf *s); +void sbuf_delete(struct sbuf *s); +char *sbuf_data(struct sbuf *s); + +#endif /* LIB9P_SBUF_H */ + diff --git a/lib9p/transport/socket.c b/lib9p/transport/socket.c new file mode 100644 index 0000000..bf54240 --- /dev/null +++ b/lib9p/transport/socket.c @@ -0,0 +1,308 @@ +/* + * Copyright 2016 Jakub Klama + * 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../lib9p.h" +#include "../lib9p_impl.h" +#include "../log.h" +#include "socket.h" + +struct l9p_socket_softc +{ + struct l9p_connection *ls_conn; + struct sockaddr ls_sockaddr; + socklen_t ls_socklen; + pthread_t ls_thread; + int ls_fd; +}; + +static int l9p_socket_readmsg(struct l9p_socket_softc *, void **, size_t *); +static int l9p_socket_get_response_buffer(struct l9p_request *, + struct iovec *, size_t *, void *); +static int l9p_socket_send_response(struct l9p_request *, const struct iovec *, + const size_t, const size_t, void *); +static void *l9p_socket_thread(void *); +static ssize_t xread(int, void *, size_t); +static ssize_t xwrite(int, void *, size_t); + +int +l9p_start_server(struct l9p_server *server, const char *host, const char *port) +{ + struct addrinfo *res, *res0, hints; + struct kevent kev[2]; + struct kevent event[2]; + int err, kq, i, val, evs, nsockets = 0; + int sockets[2]; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + err = getaddrinfo(host, port, &hints, &res0); + + if (err) + return (-1); + + for (res = res0; res; res = res->ai_next) { + int s = socket(res->ai_family, res->ai_socktype, + res->ai_protocol); + + val = 1; + setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)); + + if (s < 0) + continue; + + if (bind(s, res->ai_addr, res->ai_addrlen) < 0) { + close(s); + continue; + } + + sockets[nsockets] = s; + EV_SET(&kev[nsockets++], s, EVFILT_READ, EV_ADD | EV_ENABLE, 0, + 0, 0); + listen(s, 10); + } + + kq = kqueue(); + + if (kevent(kq, kev, nsockets, NULL, 0, NULL) < 0) { + L9P_LOG(L9P_ERROR, "kevent(): %s", strerror(errno)); + return (-1); + } + + for (;;) { + evs = kevent(kq, NULL, 0, event, nsockets, NULL); + if (evs < 0) { + if (errno == EINTR) + continue; + + L9P_LOG(L9P_ERROR, "kevent(): %s", strerror(errno)); + return (-1); + } + + for (i = 0; i < evs; i++) { + struct sockaddr client_addr; + socklen_t client_addr_len; + int news = accept((int)event[i].ident, &client_addr, + &client_addr_len); + + if (news < 0) { + L9P_LOG(L9P_WARNING, "accept(): %s", + strerror(errno)); + continue; + } + + l9p_socket_accept(server, news, &client_addr, + client_addr_len); + } + } + +} + +void +l9p_socket_accept(struct l9p_server *server, int conn_fd, + struct sockaddr *client_addr, socklen_t client_addr_len) +{ + struct l9p_socket_softc *sc; + struct l9p_connection *conn; + char host[NI_MAXHOST + 1]; + char serv[NI_MAXSERV + 1]; + int err; + + err = getnameinfo(client_addr, client_addr_len, host, NI_MAXHOST, serv, + NI_MAXSERV, NI_NUMERICHOST | NI_NUMERICSERV); + + if (err != 0) { + L9P_LOG(L9P_WARNING, "cannot look up client name: %s", + gai_strerror(err)); + } else + L9P_LOG(L9P_INFO, "new connection from %s:%s", host, serv); + + if (l9p_connection_init(server, &conn) != 0) { + L9P_LOG(L9P_ERROR, "cannot create new connection"); + return; + } + + sc = l9p_calloc(1, sizeof(*sc)); + sc->ls_conn = conn; + sc->ls_fd = conn_fd; + + l9p_connection_on_send_response(conn, l9p_socket_send_response, sc); + l9p_connection_on_get_response_buffer(conn, + l9p_socket_get_response_buffer, sc); + + pthread_create(&sc->ls_thread, NULL, l9p_socket_thread, sc); +} + +static void * +l9p_socket_thread(void *arg) +{ + struct l9p_socket_softc *sc = (struct l9p_socket_softc *)arg; + struct iovec iov; + void *buf; + size_t length; + + for (;;) { + if (l9p_socket_readmsg(sc, &buf, &length) != 0) + break; + + iov.iov_base = buf; + iov.iov_len = length; + l9p_connection_recv(sc->ls_conn, &iov, 1, NULL); + } + + L9P_LOG(L9P_INFO, "connection closed"); + return (NULL); +} + +static int +l9p_socket_readmsg(struct l9p_socket_softc *sc, void **buf, size_t *size) +{ + uint32_t msize; + size_t toread; + void *buffer; + int fd = sc->ls_fd; + + assert(fd > 0); + + buffer = l9p_malloc(sizeof(uint32_t)); + + if (xread(fd, buffer, sizeof(uint32_t)) != sizeof(uint32_t)) { + L9P_LOG(L9P_ERROR, "short read: %s", strerror(errno)); + return (-1); + } + + msize = *(uint32_t *)buffer; + toread = msize - sizeof(uint32_t); + buffer = realloc(buffer, msize); + + if (xread(fd, (char *)buffer + sizeof(uint32_t), toread) != (ssize_t)toread) { + L9P_LOG(L9P_ERROR, "short read: %s", strerror(errno)); + return (-1); + } + + *size = msize; + *buf = buffer; + L9P_LOG(L9P_INFO, "%p: read complete message, buf=%p size=%d", + sc->ls_conn, buffer, msize); + + return (0); +} + +static int +l9p_socket_get_response_buffer(struct l9p_request *req, struct iovec *iov, + size_t *niovp, void *arg __unused) +{ + size_t size = req->lr_conn->lc_msize; + void *buf; + + buf = l9p_malloc(size); + iov[0].iov_base = buf; + iov[0].iov_len = size; + + *niovp = 1; + return (0); +} + +static int +l9p_socket_send_response(struct l9p_request *req __unused, + const struct iovec *iov, const size_t niov __unused, const size_t iolen, + void *arg) +{ + struct l9p_socket_softc *sc = (struct l9p_socket_softc *)arg; + + assert(sc->ls_fd > 0); + + L9P_LOG(L9P_DEBUG, "%p: sending reply, buf=%p, size=%d", arg, + iov[0].iov_base, iolen); + + if (xwrite(sc->ls_fd, iov[0].iov_base, iolen) != (int)iolen) { + L9P_LOG(L9P_ERROR, "short write: %s", strerror(errno)); + return (-1); + } + + free(iov[0].iov_base); + return (0); +} + +static ssize_t +xread(int fd, void *buf, size_t count) +{ + size_t done = 0; + ssize_t ret; + + while (done < count) { + ret = read(fd, (char *)buf + done, count - done); + if (ret < 0) { + if (errno == EINTR) + continue; + + return (-1); + } + + if (ret == 0) + return ((ssize_t)done); + + done += (size_t)ret; + } + + return ((ssize_t)done); +} + +static ssize_t +xwrite(int fd, void *buf, size_t count) +{ + size_t done = 0; + ssize_t ret; + + while (done < count) { + ret = write(fd, (char *)buf + done, count - done); + if (ret < 0) { + if (errno == EINTR) + continue; + + return (-1); + } + + if (ret == 0) + return ((ssize_t)done); + + done += (size_t)ret; + } + + return ((ssize_t)done); +} diff --git a/lib9p/transport/socket.h b/lib9p/transport/socket.h new file mode 100644 index 0000000..b022da1 --- /dev/null +++ b/lib9p/transport/socket.h @@ -0,0 +1,40 @@ +/* + * Copyright 2016 Jakub Klama + * 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_SOCKET_H +#define LIB9P_SOCKET_H + +#include +#include +#include "../lib9p.h" + +int l9p_start_server(struct l9p_server *server, const char *host, + const char *port); +void l9p_socket_accept(struct l9p_server *server, int conn_fd, + struct sockaddr *client_addr, socklen_t client_addr_len); + +#endif /* LIB9P_SOCKET_H */ diff --git a/lib9p/utils.c b/lib9p/utils.c new file mode 100644 index 0000000..347c5ae --- /dev/null +++ b/lib9p/utils.c @@ -0,0 +1,260 @@ +/* + * Copyright 2016 Jakub Klama + * 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. + * + */ + +#include +#include +#include +#include +#include +#if defined(__FreeBSD__) +#include +#else +#include "sbuf/sbuf.h" +#endif +#include "lib9p.h" +#include "fcall.h" + +static const char *ftype_names[] = { + "Tversion", + "Rversion", + "Tauth", + "Rauth", + "Tattach", + "Rattach", + "Terror", + "Rerror", + "Tflush", + "Rflush", + "Twalk", + "Rwalk", + "Topen", + "Ropen", + "Tcreate", + "Rcreate", + "Tread", + "Rread", + "Twrite", + "Rwrite", + "Tclunk", + "Rclunk", + "Tremove", + "Rremove", + "Tstat", + "Rstat", + "Twstat", + "Rwstat" +}; + +void +l9p_seek_iov(struct iovec *iov1, size_t niov1, struct iovec *iov2, + size_t *niov2, size_t seek) +{ + size_t remainder = 0; + size_t left = seek; + size_t i, j; + + for (i = 0; i < niov1; i++) { + size_t toseek = MIN(left, iov1[i].iov_len); + left -= toseek; + + if (toseek == iov1[i].iov_len) + continue; + + if (left == 0) { + remainder = toseek; + break; + } + } + + for (j = i; j < niov1; j++) { + iov2[j - i].iov_base = (char *)iov1[j].iov_base + remainder; + iov2[j - i].iov_len = iov1[j].iov_len - remainder; + remainder = 0; + } + + *niov2 = j - i; +} + +size_t +l9p_truncate_iov(struct iovec *iov, size_t niov, size_t length) +{ + size_t i, done = 0; + + for (i = 0; i < niov; i++) { + size_t toseek = MIN(length - done, iov[i].iov_len); + done += toseek; + + if (toseek < iov[i].iov_len) { + iov[i].iov_len = toseek; + return (i + 1); + } + } + + return (niov); +} + +void +l9p_describe_qid(struct l9p_qid *qid, struct sbuf *sb) +{ + + assert(qid != NULL); + assert(sb != NULL); + + sbuf_printf(sb, "<0x%02x,%u,0x%016" PRIx64 ">", qid->type, qid->version, + qid->path); +} + +void +l9p_describe_stat(struct l9p_stat *st, struct sbuf *sb) +{ + + assert(st != NULL); + assert(sb != NULL); + + sbuf_printf(sb, "type=0x%04x dev=%d name=\"%s\" uid=\"%s\"", + st->type, st->dev, st->name, st->uid); +} + +void +l9p_describe_fcall(union l9p_fcall *fcall, enum l9p_version version, + struct sbuf *sb) +{ + uint8_t type; + int i; + + assert(fcall != NULL); + assert(sb != NULL); + assert(version <= L9P_2000L && version >= L9P_2000); + + type = fcall->hdr.type; + + if (type < 100 || type > 127) { + sbuf_printf(sb, " tag=%d", type, + fcall->hdr.tag); + return; + } + + sbuf_printf(sb, "%s tag=%d", ftype_names[type - L9P_TVERSION], + fcall->hdr.tag); + + switch (type) { + case L9P_TVERSION: + case L9P_RVERSION: + sbuf_printf(sb, " version=\"%s\" msize=%d", fcall->version.version, + fcall->version.msize); + return; + case L9P_TAUTH: + sbuf_printf(sb, "afid=%d uname=\"%s\" aname=\"%s\"", fcall->hdr.fid, + fcall->tauth.uname, fcall->tauth.aname); + return; + case L9P_TATTACH: + sbuf_printf(sb, " fid=%d afid=%d uname=\"%s\" aname=\"%s\"", + fcall->hdr.fid, fcall->tattach.afid, fcall->tattach.uname, + fcall->tattach.aname); + if (version >= L9P_2000U) + sbuf_printf(sb, " n_uname=%d", fcall->tattach.n_uname); + return; + case L9P_RERROR: + sbuf_printf(sb, " ename=\"%s\" errnum=%d", fcall->error.ename, + fcall->error.errnum); + return; + case L9P_TFLUSH: + sbuf_printf(sb, " oldtag=%d", fcall->tflush.oldtag); + return; + case L9P_TWALK: + sbuf_printf(sb, " fid=%d newfid=%d wname=\"", + fcall->hdr.fid, fcall->twalk.newfid); + + for (i = 0; i < fcall->twalk.nwname; i++) { + sbuf_printf(sb, "%s", fcall->twalk.wname[i]); + if (i != fcall->twalk.nwname - 1) + sbuf_printf(sb, "/"); + } + sbuf_printf(sb, "\""); + return; + case L9P_RWALK: + sbuf_printf(sb, " wqid=["); + for (i = 0; i < fcall->rwalk.nwqid; i++) { + l9p_describe_qid(&fcall->rwalk.wqid[i], sb); + if (i != fcall->rwalk.nwqid - 1) + sbuf_printf(sb, ","); + } + sbuf_printf(sb, "]"); + return; + case L9P_TOPEN: + sbuf_printf(sb, " fid=%d mode=%d", fcall->hdr.fid, + fcall->tcreate.mode); + + return; + case L9P_ROPEN: + sbuf_printf(sb, " qid="); + l9p_describe_qid(&fcall->ropen.qid, sb); + sbuf_printf(sb, " iounit=%d", fcall->ropen.iounit); + return; + case L9P_TCREATE: + sbuf_printf(sb, " fid=%d name=\"%s\" perm=0x%08x mode=%d", + fcall->hdr.fid, fcall->tcreate.name, fcall->tcreate.perm, + fcall->tcreate.mode); + return; + case L9P_RCREATE: + return; + case L9P_TREAD: + sbuf_printf(sb, " fid=%d offset=%" PRIu64 " count=%u", fcall->hdr.fid, + fcall->io.offset, fcall->io.count); + return; + + case L9P_RREAD: + case L9P_RWRITE: + sbuf_printf(sb, " count=%d", fcall->io.count); + return; + case L9P_TWRITE: + sbuf_printf(sb, " fid=%d offset=%" PRIu64 " count=%u", fcall->hdr.fid, + fcall->io.offset, fcall->io.count); + return; + case L9P_TCLUNK: + sbuf_printf(sb, " fid=%d ", fcall->hdr.fid); + return; + case L9P_TREMOVE: + sbuf_printf(sb, " fid=%d", fcall->hdr.fid); + return; + case L9P_RREMOVE: + return; + case L9P_TSTAT: + sbuf_printf(sb, " fid=%d", fcall->hdr.fid); + return; + case L9P_RSTAT: + sbuf_printf(sb, " "); + l9p_describe_stat(&fcall->rstat.stat, sb); + return; + case L9P_TWSTAT: + sbuf_printf(sb, " fid=%d ", fcall->hdr.fid); + l9p_describe_stat(&fcall->twstat.stat, sb); + return; + case L9P_RWSTAT: + return; + } +}