summaryrefslogtreecommitdiff
path: root/lib9p/srv.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib9p/srv.c')
-rw-r--r--lib9p/srv.c779
1 files changed, 488 insertions, 291 deletions
diff --git a/lib9p/srv.c b/lib9p/srv.c
index 9192794..60a1bb0 100644
--- a/lib9p/srv.c
+++ b/lib9p/srv.c
@@ -1,24 +1,54 @@
/* lib9p/srv.c - 9P server
*
- * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
-#include <assert.h>
#include <alloca.h>
#include <inttypes.h> /* for PRI* */
-#include <stdio.h> /* for fprintf(), stderr */
-#include <string.h> /* for strerror() */
+#include <limits.h> /* for SSIZE_MAX, not set by newlib */
+#include <stddef.h> /* for size_t */
+#include <stdlib.h> /* for malloc() */
+#include <string.h> /* for memcpy() */
+#ifndef SSIZE_MAX
+#define SSIZE_MAX (SIZE_MAX >> 1)
+#endif
#include <libcr/coroutine.h>
#include <libcr_ipc/chan.h>
#include <libcr_ipc/mutex.h>
-#include <libcr_ipc/select.h>
-#include <libmisc/vcall.h>
+#include <libmisc/assert.h>
+#include <libmisc/endian.h>
+#include <libhw/generic/net.h>
+
+#define LOG_NAME 9P_SRV
+#include <libmisc/log.h>
#define IMPLEMENTATION_FOR_LIB9P_SRV_H YES
#include <lib9p/srv.h>
-#include "internal.h"
+
+/* config *********************************************************************/
+
+#include "config.h"
+
+#ifndef CONFIG_9P_SRV_MAX_FIDS
+ #error config.h must define CONFIG_9P_SRV_MAX_FIDS
+#endif
+#ifndef CONFIG_9P_SRV_MAX_REQS
+ #error config.h must define CONFIG_9P_SRV_MAX_REQS
+#endif
+#ifndef CONFIG_9P_SRV_MAX_DEPTH
+ /* 1=just the root dir, 2=just files in the root dir, 3=1 subdir, ... */
+ #error config.h must define CONFIG_9P_SRV_MAX_DEPTH
+#endif
+#ifndef CONFIG_9P_SRV_MAX_MSG_SIZE
+ #error config.h must define CONFIG_9P_SRV_MAX_MSG_SIZE
+#endif
+#ifndef CONFIG_9P_SRV_MAX_HOSTMSG_SIZE
+ #error config.h must define CONFIG_9P_SRV_MAX_HOSTMSG_SIZE
+#endif
+static_assert(CONFIG_9P_SRV_MAX_MSG_SIZE <= CONFIG_9P_SRV_MAX_HOSTMSG_SIZE);
+static_assert(CONFIG_9P_SRV_MAX_HOSTMSG_SIZE <= SSIZE_MAX);
/* context ********************************************************************/
@@ -37,29 +67,56 @@ int lib9p_srv_acknowledge_flush(struct lib9p_srv_ctx *ctx) {
/* structs ********************************************************************/
+typedef typeof( ((struct lib9p_qid){}).path ) srv_path_t;
+
+struct srv_pathinfo {
+ lo_interface lib9p_srv_file file;
+ srv_path_t parent_dir;
+
+ /* References from other srv_pathinfos (via .parent_dir) or
+ * from FIDs. */
+ unsigned int gc_refcount;
+ /* References from fids with FIDFLAG_OPEN_R/FIDFLAG_OPEN_W. */
+ unsigned int io_refcount;
+};
+
+#define NAME pathmap
+#define KEY_T srv_path_t
+#define VAL_T struct srv_pathinfo
+/* ( naive ) + ( working space for walk() ) */
+#define CAP ( (CONFIG_9P_SRV_MAX_FIDS*CONFIG_9P_SRV_MAX_DEPTH) + (CONFIG_9P_SRV_MAX_REQS*2) )
+#include "map.h"
+
#define FIDFLAG_OPEN_R (1<<0)
#define FIDFLAG_OPEN_W (1<<1)
#define FIDFLAG_RCLOSE (1<<2)
-#define FIDFLAG_ISDIR (1<<3)
#define FIDFLAG_OPEN (FIDFLAG_OPEN_R|FIDFLAG_OPEN_W)
struct _srv_fidinfo {
- implements_lib9p_srv_file *file;
- uint8_t flags;
- size_t dir_idx;
- uint32_t dir_off;
+ srv_path_t path;
+ uint8_t flags;
+ union {
+ struct {
+ lo_interface lib9p_srv_fio io;
+ } file;
+ struct {
+ lo_interface lib9p_srv_dio io;
+ size_t idx;
+ uint64_t off;
+ } dir;
+ };
};
#define NAME fidmap
-#define KEY_T uint32_t
+#define KEY_T lib9p_fid_t
#define VAL_T struct _srv_fidinfo
-#define CAP CONFIG_9P_MAX_FIDS
+#define CAP CONFIG_9P_SRV_MAX_FIDS
#include "map.h"
#define NAME reqmap
-#define KEY_T uint32_t
+#define KEY_T lib9p_tag_t
#define VAL_T struct _lib9p_srv_req *
-#define CAP CONFIG_9P_MAX_REQS
+#define CAP CONFIG_9P_SRV_MAX_REQS
#include "map.h"
/* The hierarchy of concepts is:
@@ -73,7 +130,7 @@ struct _srv_fidinfo {
struct _srv_conn {
/* immutable */
struct lib9p_srv *parent_srv;
- implements_net_stream_conn *fd;
+ lo_interface net_stream_conn fd;
cid_t reader; /* the lib9p_srv_read_cr() coroutine */
/* mutable */
cr_mutex_t writelock;
@@ -88,53 +145,34 @@ struct _srv_sess {
/* mutable */
bool initialized;
bool closing;
- struct reqmap reqs;
- struct fidmap fids;
+ struct pathmap paths; /* srv_path_t => lib9p_srv_file + metadata */
+ struct fidmap fids; /* lib9p_fid_t => lib9p_srv_{fio,dio} + metadata */
+ struct reqmap reqs; /* lib9p_tag_t => *_lib9p_srv_req */
};
struct _lib9p_srv_req {
/* immutable */
struct _srv_sess *parent_sess;
uint16_t tag;
+ uint8_t *net_bytes;
/* mutable */
- uint8_t *net_bytes; /* CONFIG_9P_MAX_MSG_SIZE-sized */
struct lib9p_srv_ctx ctx;
};
/* base utilities *************************************************************/
-#define nonrespond_errorf(fmt, ...) \
- fprintf(stderr, "error: " fmt "\n" __VA_OPT__(,) __VA_ARGS__)
-
-static uint32_t rerror_overhead_for_version(enum lib9p_version version,
- uint8_t *scratch) {
- struct lib9p_ctx empty_ctx = {
- .version = version,
- .max_msg_size = CONFIG_9P_MAX_MSG_SIZE,
- };
- struct lib9p_msg_Rerror empty_error = { 0 };
- bool e;
+#define nonrespond_errorf errorf
- e = lib9p_marshal(&empty_ctx, LIB9P_TYP_Rerror,
- &empty_error, /* host_body */
- scratch); /* net_bytes */
- assert(!e);
-
- uint32_t min_msg_size = decode_u32le(scratch);
-
- /* Assert that min_msg_size + biggest_possible_MAX_ERR_SIZE
- * won't overflow uint32... because using
- * __builtin_add_overflow in respond_error() would be a bit
- * much. */
- assert(min_msg_size < (UINT32_MAX - UINT16_MAX));
- /* Assert that min_msg_size doesn't overflow MAX_MSG_SIZE. */
- assert(CONFIG_9P_MAX_MSG_SIZE >= min_msg_size);
-
- return min_msg_size;
+static ssize_t write_Rmsg(struct _lib9p_srv_req *req, struct lib9p_Rmsg_send_buf *resp) {
+ ssize_t r;
+ cr_mutex_lock(&req->parent_sess->parent_conn->writelock);
+ r = io_writev(req->parent_sess->parent_conn->fd, resp->iov, resp->iov_cnt);
+ cr_mutex_unlock(&req->parent_sess->parent_conn->writelock);
+ return r;
}
static void respond_error(struct _lib9p_srv_req *req) {
-#ifdef CONFIG_9P_ENABLE_9P2000_u
+#if CONFIG_9P_ENABLE_9P2000_u
assert(req->ctx.basectx.err_num);
#endif
assert(req->ctx.basectx.err_msg[0]);
@@ -142,12 +180,9 @@ static void respond_error(struct _lib9p_srv_req *req) {
ssize_t r;
struct lib9p_msg_Rerror host = {
.tag = req->tag,
- .ename = {
- .len = strnlen(req->ctx.basectx.err_msg,
- CONFIG_9P_MAX_ERR_SIZE),
- .utf8 = req->ctx.basectx.err_msg,
- },
-#ifdef CONFIG_9P_ENABLE_9P2000_u
+ .ename = lib9p_strn(req->ctx.basectx.err_msg,
+ CONFIG_9P_MAX_ERR_SIZE),
+#if CONFIG_9P_ENABLE_9P2000_u
.errno = req->ctx.basectx.err_num,
#endif
};
@@ -155,33 +190,36 @@ static void respond_error(struct _lib9p_srv_req *req) {
struct _srv_sess *sess = req->parent_sess;
/* Truncate the error-string if necessary to avoid needing to
- * return LINUX_ERANGE. The assert() in
- * rerror_overhead_for_version() has checked that this
- * addition doesn't overflow. */
+ * return LINUX_ERANGE. */
if (((uint32_t)host.ename.len) + sess->rerror_overhead > sess->max_msg_size)
host.ename.len = sess->max_msg_size - sess->rerror_overhead;
- lib9p_marshal(&req->ctx.basectx, LIB9P_TYP_Rerror,
- &host, req->net_bytes);
+ struct lib9p_Rmsg_send_buf net;
+
+ lib9p_Rmsg_marshal(&req->ctx.basectx,
+ LIB9P_TYP_Rerror, &host,
+ &net);
- cr_mutex_lock(&sess->parent_conn->writelock);
- r = VCALL(sess->parent_conn->fd, write,
- req->net_bytes, decode_u32le(req->net_bytes));
- cr_mutex_unlock(&sess->parent_conn->writelock);
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wformat"
+#pragma GCC diagnostic ignored "-Wformat-extra-args"
+ infof("< %v", lo_box_lib9p_msg_Rerror_as_fmt_formatter(&host));
+#pragma GCC diagnostic pop
+ r = write_Rmsg(req, &net);
if (r < 0)
- nonrespond_errorf("write: %s", strerror(-r));
+ nonrespond_errorf("write: %s", net_strerror(-r));
}
/* read coroutine *************************************************************/
-static bool read_at_least(implements_net_stream_conn *fd, uint8_t *buf, size_t goal, size_t *done) {
+static bool read_exactly(lo_interface net_stream_conn fd, uint8_t *buf, size_t goal, size_t *done) {
assert(buf);
assert(goal);
assert(done);
while (*done < goal) {
- ssize_t r = VCALL(fd, read, &buf[*done], CONFIG_9P_MAX_MSG_SIZE - *done);
+ ssize_t r = io_read(fd, &buf[*done], goal - *done);
if (r < 0) {
- nonrespond_errorf("read: %s", strerror(-r));
+ nonrespond_errorf("read: %s", net_strerror(-r));
return true;
} else if (r == 0) {
if (*done != 0)
@@ -195,30 +233,34 @@ static bool read_at_least(implements_net_stream_conn *fd, uint8_t *buf, size_t g
static void handle_message(struct _lib9p_srv_req *ctx);
-__attribute__ ((noreturn)) void lib9p_srv_read_cr(struct lib9p_srv *srv, implements_net_stream_listener *listener) {
- uint8_t buf[CONFIG_9P_MAX_MSG_SIZE];
-
+[[noreturn]] void lib9p_srv_read_cr(struct lib9p_srv *srv, lo_interface net_stream_listener listener) {
assert(srv);
assert(srv->rootdir);
- assert(listener);
+ assert(!LO_IS_NULL(listener));
+
+ srv->readers++;
- uint32_t initial_rerror_overhead = rerror_overhead_for_version(0, buf);
+ uint32_t initial_rerror_overhead = lib9p_version_min_msg_size(LIB9P_VER_unknown);
for (;;) {
struct _srv_conn conn = {
.parent_srv = srv,
- .fd = VCALL(listener, accept),
+ .fd = LO_CALL(listener, accept),
.reader = cr_getcid(),
};
- if (!conn.fd) {
+ if (LO_IS_NULL(conn.fd)) {
nonrespond_errorf("accept: error");
- continue;
+ srv->readers--;
+ if (srv->readers == 0)
+ while (srv->writers > 0)
+ _lib9p_srv_reqch_send_req(&srv->_reqch, NULL);
+ cr_exit();
}
struct _srv_sess sess = {
.parent_conn = &conn,
.version = LIB9P_VER_unknown,
- .max_msg_size = CONFIG_9P_MAX_MSG_SIZE,
+ .max_msg_size = CONFIG_9P_SRV_MAX_MSG_SIZE,
.rerror_overhead = initial_rerror_overhead,
.initialized = false,
};
@@ -226,18 +268,19 @@ __attribute__ ((noreturn)) void lib9p_srv_read_cr(struct lib9p_srv *srv, impleme
nextmsg:
/* Read the message. */
size_t done = 0;
- if (read_at_least(conn.fd, buf, 4, &done))
+ uint8_t buf[7];
+ if (read_exactly(conn.fd, buf, 4, &done))
goto close;
- size_t goal = decode_u32le(buf);
+ size_t goal = uint32le_decode(buf);
if (goal < 7) {
nonrespond_errorf("T-message is impossibly small");
goto close;
}
- if (read_at_least(conn.fd, buf, 7, &done))
+ if (read_exactly(conn.fd, buf, 7, &done))
goto close;
struct _lib9p_srv_req req = {
.parent_sess = &sess,
- .tag = decode_u16le(&buf[5]),
+ .tag = uint16le_decode(&buf[5]),
.net_bytes = buf,
.ctx = {
.basectx = {
@@ -255,11 +298,16 @@ __attribute__ ((noreturn)) void lib9p_srv_read_cr(struct lib9p_srv *srv, impleme
respond_error(&req);
goto nextmsg;
}
- if (read_at_least(conn.fd, buf, goal, &done))
+ req.net_bytes = malloc(goal);
+ assert(req.net_bytes);
+ memcpy(req.net_bytes, buf, done);
+ if (read_exactly(conn.fd, req.net_bytes, goal, &done)) {
+ free(req.net_bytes);
goto close;
+ }
/* Handle the message... */
- if (buf[4] == LIB9P_TYP_Tversion)
+ if (req.net_bytes[4] == LIB9P_TYP_Tversion)
/* ...in this coroutine for Tversion, */
handle_message(&req);
else
@@ -267,12 +315,14 @@ __attribute__ ((noreturn)) void lib9p_srv_read_cr(struct lib9p_srv *srv, impleme
_lib9p_srv_reqch_send_req(&srv->_reqch, &req);
}
close:
- VCALL(conn.fd, close, true, sess.reqs.len == 0);
- if (sess.reqs.len) {
+ if (sess.reqs.len == 0)
+ io_close(conn.fd);
+ else {
+ io_close_read(conn.fd);
sess.closing = true;
cr_pause_and_yield();
assert(sess.reqs.len == 0);
- VCALL(conn.fd, close, true, true);
+ io_close_write(conn.fd);
}
}
}
@@ -280,7 +330,6 @@ __attribute__ ((noreturn)) void lib9p_srv_read_cr(struct lib9p_srv *srv, impleme
/* write coroutine ************************************************************/
COROUTINE lib9p_srv_write_cr(void *_srv) {
- uint8_t net[CONFIG_9P_MAX_MSG_SIZE];
struct _lib9p_srv_req req;
_lib9p_srv_reqch_req_t rpc_handle;
@@ -289,14 +338,19 @@ COROUTINE lib9p_srv_write_cr(void *_srv) {
assert(srv->rootdir);
cr_begin();
+ srv->writers++;
+
for (;;) {
/* Receive the request from the reader coroutine. ************/
rpc_handle = _lib9p_srv_reqch_recv_req(&srv->_reqch);
- /* Deep-copy the request from the reader coroutine's
+ if (!rpc_handle.req) {
+ srv->writers--;
+ _lib9p_srv_reqch_send_resp(rpc_handle, 0);
+ cr_exit();
+ }
+ /* Copy the request from the reader coroutine's
* stack to our stack. */
req = *rpc_handle.req;
- memcpy(net, req.net_bytes, decode_u32le(req.net_bytes));
- req.net_bytes = net;
/* Record that we have it. */
reqmap_store(&req.parent_sess->reqs, req.tag, &req);
/* Notify the reader coroutine that we're done with
@@ -334,7 +388,7 @@ _HANDLER_PROTO(clunk);
_HANDLER_PROTO(remove);
_HANDLER_PROTO(stat);
_HANDLER_PROTO(wstat);
-#ifdef CONFIG_9P_ENABLE_9P2000_e
+#if CONFIG_9P_ENABLE_9P2000_e
_HANDLER_PROTO(session);
_HANDLER_PROTO(sread);
_HANDLER_PROTO(swrite);
@@ -356,7 +410,7 @@ static tmessage_handler tmessage_handlers[0x100] = {
[LIB9P_TYP_Tremove] = (tmessage_handler)handle_Tremove,
[LIB9P_TYP_Tstat] = (tmessage_handler)handle_Tstat,
[LIB9P_TYP_Twstat] = (tmessage_handler)handle_Twstat,
-#ifdef CONFIG_9P_ENABLE_9P2000_e
+#if CONFIG_9P_ENABLE_9P2000_e
[LIB9P_TYP_Tsession] = (tmessage_handler)handle_Tsession,
[LIB9P_TYP_Tsread] = (tmessage_handler)handle_Tsread,
[LIB9P_TYP_Tswrite] = (tmessage_handler)handle_Tswrite,
@@ -364,28 +418,23 @@ static tmessage_handler tmessage_handlers[0x100] = {
};
static void handle_message(struct _lib9p_srv_req *ctx) {
- uint8_t host_req[CONFIG_9P_MAX_HOSTMSG_SIZE];
- uint8_t host_resp[CONFIG_9P_MAX_HOSTMSG_SIZE];
+ uint8_t *host_req = NULL;
+ uint8_t host_resp[CONFIG_9P_SRV_MAX_HOSTMSG_SIZE];
/* Unmarshal it. */
- enum lib9p_msg_type typ = ctx->net_bytes[4];
- if (typ % 2 != 0) {
- lib9p_errorf(&ctx->ctx.basectx,
- LINUX_EOPNOTSUPP, "expected a T-message but got an R-message: message_type=%s",
- lib9p_msg_type_str(&ctx->ctx.basectx, typ));
- goto write;
- }
- ssize_t host_size = lib9p_validate(&ctx->ctx.basectx, ctx->net_bytes);
+ ssize_t host_size = lib9p_Tmsg_validate(&ctx->ctx.basectx, ctx->net_bytes);
if (host_size < 0)
goto write;
- if ((size_t)host_size > sizeof(host_req)) {
- lib9p_errorf(&ctx->ctx.basectx,
- LINUX_EMSGSIZE, "unmarshalled payload larger than server limit (%zu > %zu)",
- host_size, sizeof(host_req));
- goto write;
- }
- lib9p_unmarshal(&ctx->ctx.basectx, ctx->net_bytes,
+ host_req = calloc(1, host_size);
+ assert(host_req);
+ enum lib9p_msg_type typ;
+ lib9p_Tmsg_unmarshal(&ctx->ctx.basectx, ctx->net_bytes,
&typ, host_req);
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wformat"
+#pragma GCC diagnostic ignored "-Wformat-extra-args"
+ infof("> %v", lo_box_lib9p_msg_as_fmt_formatter(&ctx->ctx.basectx, typ, host_req));
+#pragma GCC diagnostic pop
/* Handle it. */
tmessage_handlers[typ](ctx, (void *)host_req, (void *)host_resp);
@@ -394,15 +443,21 @@ static void handle_message(struct _lib9p_srv_req *ctx) {
if (lib9p_ctx_has_error(&ctx->ctx.basectx))
respond_error(ctx);
else {
- if (lib9p_marshal(&ctx->ctx.basectx, typ+1, host_resp,
- ctx->net_bytes))
+ struct lib9p_Rmsg_send_buf net_resp;
+ if (lib9p_Rmsg_marshal(&ctx->ctx.basectx,
+ typ+1, host_resp,
+ &net_resp))
goto write;
-
- cr_mutex_lock(&ctx->parent_sess->parent_conn->writelock);
- VCALL(ctx->parent_sess->parent_conn->fd, write,
- ctx->net_bytes, decode_u32le(ctx->net_bytes));
- cr_mutex_unlock(&ctx->parent_sess->parent_conn->writelock);
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wformat"
+#pragma GCC diagnostic ignored "-Wformat-extra-args"
+ infof("< %v", lo_box_lib9p_msg_as_fmt_formatter(&ctx->ctx.basectx, typ+1, &host_resp));
+#pragma GCC diagnostic pop
+ write_Rmsg(ctx, &net_resp);
}
+ if (host_req)
+ free(host_req);
+ free(ctx->net_bytes);
}
#define util_handler_common(ctx, req, resp) do { \
@@ -412,7 +467,7 @@ static void handle_message(struct _lib9p_srv_req *ctx) {
resp->tag = req->tag; \
} while (0)
-static inline bool util_check_perm(struct lib9p_srv_ctx *ctx, struct lib9p_stat *stat, uint8_t action) {
+static inline bool srv_util_check_perm(struct _lib9p_srv_req *ctx, struct lib9p_stat *stat, uint8_t action) {
assert(ctx);
assert(stat);
assert(action);
@@ -423,17 +478,107 @@ static inline bool util_check_perm(struct lib9p_srv_ctx *ctx, struct lib9p_stat
return mode & action;
}
-static inline bool util_release(struct lib9p_srv_ctx *ctx, implements_lib9p_srv_file *file) {
- assert(file);
- file->_refcount--;
- if (file->_refcount == 0) {
- if (file->_parent_dir != file)
- util_release(ctx, file->_parent_dir);
- VCALL(file, free, ctx);
+/**
+ * Ensures that `file` is saved into the pathmap, and increments the
+ * gc_refcount by 1 (for presumptive insertion into the fidmap).
+ * parent_path's gc_refcount is also incremented as appropriate.
+ *
+ * Returns a pointer to the stored pathinfo.
+ */
+static inline struct srv_pathinfo *srv_util_pathsave(struct _lib9p_srv_req *ctx,
+ lo_interface lib9p_srv_file file,
+ srv_path_t parent_path) {
+ assert(ctx);
+ assert(!LO_IS_NULL(file));
+
+ struct lib9p_qid qid = LO_CALL(file, qid);
+ struct srv_pathinfo *pathinfo = pathmap_load(&ctx->parent_sess->paths, qid.path);
+ if (pathinfo)
+ assert(LO_EQ(pathinfo->file, file));
+ else {
+ pathinfo = pathmap_store(&ctx->parent_sess->paths, qid.path,
+ (struct srv_pathinfo){
+ .file = file,
+ .parent_dir = parent_path,
+ .gc_refcount = 0,
+ .io_refcount = 0,
+ });
+ assert(pathinfo);
+ if (parent_path != qid.path) {
+ struct srv_pathinfo *parent = pathmap_load(&ctx->parent_sess->paths, parent_path);
+ assert(parent);
+ parent->gc_refcount++;
+ }
}
- return lib9p_ctx_has_error(&ctx->basectx);
+ pathinfo->gc_refcount++;
+ return pathinfo;
}
+/**
+ * Decrement the path's gc_refcount, and trigger garbage collection as
+ * appropriate.
+ */
+static inline void srv_util_pathfree(struct _lib9p_srv_req *ctx, srv_path_t path) {
+ assert(ctx);
+
+ struct srv_pathinfo *pathinfo = pathmap_load(&ctx->parent_sess->paths, path);
+ assert(pathinfo);
+ pathinfo->gc_refcount--;
+ if (pathinfo->gc_refcount == 0) {
+ if (pathinfo->parent_dir != path)
+ srv_util_pathfree(ctx, pathinfo->parent_dir);
+ LO_CALL(pathinfo->file, free);
+ pathmap_del(&ctx->parent_sess->paths, path);
+ }
+}
+
+static inline bool srv_util_pathisdir(struct srv_pathinfo *pathinfo) {
+ assert(pathinfo);
+ return LO_CALL(pathinfo->file, qid).type & LIB9P_QT_DIR;
+}
+
+/**
+ * Store fid as pointing to pathinfo. Assumes that
+ * pathinfo->gc_refcount has already been incremented; does *not*
+ * decrement it on failure.
+ */
+static inline struct _srv_fidinfo *srv_util_fidsave(struct _lib9p_srv_req *ctx, lib9p_fid_t fid, struct srv_pathinfo *pathinfo, bool overwrite) {
+ assert(ctx);
+ assert(fid != LIB9P_FID_NOFID);
+ assert(pathinfo);
+
+ struct lib9p_qid qid = LO_CALL(pathinfo->file, qid);
+
+ struct _srv_fidinfo *fidinfo = fidmap_load(&ctx->parent_sess->fids, fid);
+ if (fidinfo) {
+ if (overwrite) {
+ struct srv_pathinfo *old_pathinfo = pathmap_load(&ctx->parent_sess->paths, fidinfo->path);
+ assert(old_pathinfo);
+ if (srv_util_pathisdir(old_pathinfo))
+ LO_CALL(fidinfo->dir.io, iofree);
+ else
+ LO_CALL(fidinfo->file.io, iofree);
+ srv_util_pathfree(ctx, fidinfo->path);
+ } else {
+ lib9p_error(&ctx->ctx.basectx,
+ LINUX_EBADF, "FID already in use");
+ return NULL;
+ }
+ } else {
+ fidinfo = fidmap_store(&ctx->parent_sess->fids, fid, (struct _srv_fidinfo){});
+ if (!fidinfo) {
+ lib9p_error(&ctx->ctx.basectx,
+ LINUX_EMFILE, "too many open files");
+ return NULL;
+ }
+ }
+ *fidinfo = (struct _srv_fidinfo){
+ .path = qid.path,
+ };
+ return fidinfo;
+}
+
+
static void handle_Tversion(struct _lib9p_srv_req *ctx,
struct lib9p_msg_Tversion *req,
struct lib9p_msg_Rversion *resp) {
@@ -448,33 +593,29 @@ static void handle_Tversion(struct _lib9p_srv_req *ctx,
'0' <= req->version.utf8[3] && req->version.utf8[3] <= '9' &&
'0' <= req->version.utf8[4] && req->version.utf8[4] <= '9' &&
'0' <= req->version.utf8[5] && req->version.utf8[5] <= '9' &&
- (req->version.utf8[6] == '\0' || req->version.utf8[6] == '.')) {
+ (req->version.len == 6 || req->version.utf8[6] == '.')) {
version = LIB9P_VER_9P2000;
-#ifdef CONFIG_9P_ENABLE_9P2000_u
- if (strcmp(&req->version.utf8[6], ".u") == 0)
+#if CONFIG_9P_ENABLE_9P2000_u
+ if (lib9p_str_eq(lib9p_str_sliceleft(req->version, 6), lib9p_str(".u")))
version = LIB9P_VER_9P2000_u;
#endif
-#ifdef CONFIG_9P_ENABLE_9P2000_e
- if (strcmp(&req->version.utf8[6], ".e") == 0)
+#if CONFIG_9P_ENABLE_9P2000_e
+ if (lib9p_str_eq(lib9p_str_sliceleft(req->version, 6), lib9p_str(".e")))
version = LIB9P_VER_9P2000_e;
#endif
}
- uint32_t min_msg_size = rerror_overhead_for_version(version, ctx->net_bytes);
+ uint32_t min_msg_size = lib9p_version_min_msg_size(version);
if (req->max_msg_size < min_msg_size) {
lib9p_errorf(&ctx->ctx.basectx,
LINUX_EDOM, "requested max_msg_size is less than minimum for %s (%"PRIu32" < %"PRIu32")",
- version, req->max_msg_size, min_msg_size);
+ lib9p_version_str(version), req->max_msg_size, min_msg_size);
return;
}
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wdiscarded-qualifiers"
- resp->version.utf8 = lib9p_version_str(version);
-#pragma GCC diagnostic pop
- resp->version.len = strlen(resp->version.utf8);
- resp->max_msg_size = (CONFIG_9P_MAX_MSG_SIZE < req->max_msg_size)
- ? CONFIG_9P_MAX_MSG_SIZE
+ resp->version = lib9p_str((char *)lib9p_version_str(version)); /* cast to discard "const" qualifier */
+ resp->max_msg_size = (CONFIG_9P_SRV_MAX_MSG_SIZE < req->max_msg_size)
+ ? CONFIG_9P_SRV_MAX_MSG_SIZE
: req->max_msg_size;
/* Close the old session. */
@@ -483,7 +624,7 @@ static void handle_Tversion(struct _lib9p_srv_req *ctx,
* to finish. */
struct cr_select_arg *list = alloca(sizeof(struct cr_select_arg) * ctx->parent_sess->reqs.len);
while (ctx->parent_sess->reqs.len) {
- uint16_t tag __attribute__((unused));
+ uint16_t tag [[gnu::unused]];
struct _lib9p_srv_req **reqpp;
size_t i = 0;
bool flushed;
@@ -496,7 +637,7 @@ static void handle_Tversion(struct _lib9p_srv_req *ctx,
if (ctx->parent_sess->fids.len) {
/* Close all FIDs. */
uint32_t fid;
- struct _srv_fidinfo *fidinfo __attribute__((unused));
+ struct _srv_fidinfo *fidinfo [[gnu::unused]];
MAP_FOREACH(&ctx->parent_sess->fids, fid, fidinfo) {
handle_Tclunk(ctx,
&(struct lib9p_msg_Tclunk){.fid = fid},
@@ -515,8 +656,8 @@ static void handle_Tauth(struct _lib9p_srv_req *ctx,
struct lib9p_msg_Rauth *resp) {
util_handler_common(ctx, req, resp);
- ctx->ctx.uid = req->n_uname;
- ctx->ctx.uname = req->uname.utf8;
+ ctx->ctx.uid = req->n_uid;
+ ctx->ctx.uname = req->uname;
struct lib9p_srv *srv = ctx->parent_sess->parent_conn->parent_srv;
if (!srv->auth) {
@@ -525,7 +666,7 @@ static void handle_Tauth(struct _lib9p_srv_req *ctx,
return;
}
- srv->auth(&ctx->ctx, req->aname.utf8);
+ srv->auth(&ctx->ctx, req->aname);
lib9p_error(&ctx->ctx.basectx,
LINUX_EOPNOTSUPP, "TODO: auth not implemented");
}
@@ -535,8 +676,8 @@ static void handle_Tattach(struct _lib9p_srv_req *ctx,
struct lib9p_msg_Rattach *resp) {
util_handler_common(ctx, req, resp);
- ctx->ctx.uid = req->n_uname;
- ctx->ctx.uname = req->uname.utf8;
+ ctx->ctx.uid = req->n_uid;
+ ctx->ctx.uname = req->uname;
struct lib9p_srv *srv = ctx->parent_sess->parent_conn->parent_srv;
if (srv->auth) {
@@ -548,14 +689,16 @@ static void handle_Tattach(struct _lib9p_srv_req *ctx,
else if (fh->type != FH_AUTH)
lib9p_error(&ctx->ctx.basectx,
LINUX_EACCES, "FID provided as auth-file is not an auth-file");
- else if (strcmp(fh->data.auth.uname, req->uname.utf8) != 0)
+ else if (!lib9p_str_eq(fh->data.auth.uname, req->uname))
lib9p_errorf(&ctx->ctx.basectx,
- LINUX_EACCES, "FID provided as auth-file is for user=\"%s\" and cannot be used for user=\"%s\"",
- fh->data.auth.uname, req->uname.utf8);
- else if (strcmp(fh->data.auth.aname, req->aname.utf8) != 0)
+ LINUX_EACCES, "FID provided as auth-file is for user=\"%.*s\" and cannot be used for user=\"%.*s\"",
+ fh->data.auth.uname.len, fh->data.auth.uname.utf8,
+ req->uname.len, req->uname.utf8);
+ else if (!lib9p_str_eq(fh->data.auth.aname, req->aname))
lib9p_errorf(&ctx->ctx.basectx,
- LINUX_EACCES, "FID provided as auth-file is for tree=\"%s\" and cannot be used for tree=\"%s\"",
- fh->data.auth.aname, req->aname.utf8);
+ LINUX_EACCES, "FID provided as auth-file is for tree=\"%.*s\" and cannot be used for tree=\"%.*s\"",
+ fh->data.auth.aname.len, fh->data.auth.aname.utf8,
+ req->aname.len, req->aname.utf8);
else if (!fh->data.auth.authenticated)
lib9p_error(&ctx->ctx.basectx,
LINUX_EACCES, "FID provided as auth-file has not completed authentication");
@@ -564,51 +707,41 @@ static void handle_Tattach(struct _lib9p_srv_req *ctx,
return;
*/
lib9p_error(&ctx->ctx.basectx,
- LINUX_EOPNOTSUPP, "TODO: auth not implemented");
+ LINUX_EOPNOTSUPP, "TODO: auth not (yet?) implemented");
return;
} else {
- if (req->afid != LIB9P_NOFID) {
+ if (req->afid != LIB9P_FID_NOFID) {
lib9p_error(&ctx->ctx.basectx,
LINUX_EACCES, "FID provided as auth-file, but no auth-file is required");
return;
}
}
- if (fidmap_load(&ctx->parent_sess->fids, req->fid)) {
- lib9p_error(&ctx->ctx.basectx,
- LINUX_EBADF, "FID already in use");
- return;
+ if (req->fid == LIB9P_FID_NOFID) {
+ lib9p_error(&ctx->ctx.basectx,
+ LINUX_EBADF, "cannot assign to NOFID");
+ return;
}
- implements_lib9p_srv_file *rootdir = srv->rootdir(&ctx->ctx, req->aname.utf8);
- assert((rootdir == NULL) == lib9p_ctx_has_error(&ctx->ctx.basectx));
+ /* 1. File object */
+ lo_interface lib9p_srv_file root_file = srv->rootdir(&ctx->ctx, req->aname);
+ assert(LO_IS_NULL(root_file) == lib9p_ctx_has_error(&ctx->ctx.basectx));
if (lib9p_ctx_has_error(&ctx->ctx.basectx))
return;
- rootdir->_parent_dir = rootdir;
- rootdir->_refcount++;
- if (!fidmap_store(&ctx->parent_sess->fids, req->fid, (struct _srv_fidinfo){
- .file = rootdir,
- .flags = FIDFLAG_ISDIR,
- })) {
- lib9p_error(&ctx->ctx.basectx,
- LINUX_EMFILE, "too many open files");
- util_release(&ctx->ctx, rootdir);
- return;
- }
+ struct lib9p_qid root_qid = LO_CALL(root_file, qid);
+ assert(root_qid.type & LIB9P_QT_DIR);
+
+ /* 2. pathinfo */
+ struct srv_pathinfo *root_pathinfo = srv_util_pathsave(ctx, root_file, root_qid.path);
- struct lib9p_stat stat = VCALL(rootdir, stat, &ctx->ctx);
- if (lib9p_ctx_has_error(&ctx->ctx.basectx)) {
- handle_Tclunk(ctx,
- &(struct lib9p_msg_Tclunk){.fid = req->fid},
- &(struct lib9p_msg_Rclunk){});
+ /* 3. fidinfo */
+ if (!srv_util_fidsave(ctx, req->fid, root_pathinfo, false)) {
+ srv_util_pathfree(ctx, root_qid.path);
return;
}
- lib9p_assert_stat(stat);
- assert(stat.file_mode & LIB9P_DM_DIR);
-
- resp->qid = stat.file_qid;
+ resp->qid = root_qid;
return;
}
@@ -627,87 +760,75 @@ static void handle_Twalk(struct _lib9p_srv_req *ctx,
struct lib9p_msg_Rwalk *resp) {
util_handler_common(ctx, req, resp);
+ if (req->newfid == LIB9P_FID_NOFID) {
+ lib9p_error(&ctx->ctx.basectx,
+ LINUX_EBADF, "cannot assign to NOFID");
+ return;
+ }
+
struct _srv_fidinfo *fidinfo = fidmap_load(&ctx->parent_sess->fids, req->fid);
if (!fidinfo) {
lib9p_errorf(&ctx->ctx.basectx,
LINUX_EBADF, "bad file number %"PRIu32, req->fid);
return;
}
- if (req->newfid != req->fid && fidmap_load(&ctx->parent_sess->fids, req->newfid)) {
- lib9p_error(&ctx->ctx.basectx,
- LINUX_EBADF, "FID already in use");
- return;
- }
- implements_lib9p_srv_file *dir = fidinfo->file;
- if (req->newfid != req->fid) {
- dir = VCALL(dir, clone, &ctx->ctx);
- assert((dir == NULL) == lib9p_ctx_has_error(&ctx->ctx.basectx));
- if (lib9p_ctx_has_error(&ctx->ctx.basectx))
- return;
- dir->_refcount++; /* ref-A: presumptive insertion into fidmap */
- }
- bool isdir = (fidinfo->flags & FIDFLAG_ISDIR);
+ struct srv_pathinfo *pathinfo = pathmap_load(&ctx->parent_sess->paths, fidinfo->path);
+ assert(pathinfo);
+ pathinfo->gc_refcount++;
resp->wqid = (struct lib9p_qid *)(&resp[1]);
for (resp->nwqid = 0; resp->nwqid < req->nwname; resp->nwqid++) {
- implements_lib9p_srv_file *member;
- if (strcmp(req->wname[resp->nwqid].utf8, "..") == 0) {
- member = dir->_parent_dir;
+ struct srv_pathinfo *new_pathinfo;
+ if (lib9p_str_eq(req->wname[resp->nwqid], lib9p_str(".."))) {
+ new_pathinfo = pathmap_load(&ctx->parent_sess->paths, pathinfo->parent_dir);
+ assert(new_pathinfo);
+ new_pathinfo->gc_refcount++;
} else {
- if (!isdir) {
+ if (!srv_util_pathisdir(pathinfo)) {
lib9p_error(&ctx->ctx.basectx,
LINUX_ENOTDIR, "not a directory");
break;
}
- member = VCALL(dir, dopen, &ctx->ctx, req->wname[resp->nwqid].utf8);
- assert((member == NULL) == lib9p_ctx_has_error(&ctx->ctx.basectx));
+ lo_interface lib9p_srv_file member_file = LO_CALL(pathinfo->file, dwalk, &ctx->ctx, req->wname[resp->nwqid]);
+ assert(LO_IS_NULL(member_file) == lib9p_ctx_has_error(&ctx->ctx.basectx));
if (lib9p_ctx_has_error(&ctx->ctx.basectx))
break;
- member->_parent_dir = dir;
- dir->_refcount++; /* member->_parent_dir */
+ new_pathinfo = srv_util_pathsave(ctx, member_file, LO_CALL(pathinfo->file, qid).path);
}
- member->_refcount++; /* presumptively take ref-A */
- struct lib9p_stat stat = VCALL(member, stat, &ctx->ctx);
- if (lib9p_ctx_has_error(&ctx->ctx.basectx)) {
- util_release(&ctx->ctx, member); /* presumption of taking ref-A failed */
- break;
- }
- lib9p_assert_stat(stat);
- isdir = stat.file_mode & LIB9P_DM_DIR;
- if (isdir && !util_check_perm(&ctx->ctx, &stat, 0b001)) {
- lib9p_error(&ctx->ctx.basectx,
- LINUX_EACCES, "you do not have execute permission on that directory");
- util_release(&ctx->ctx, member); /* presumption of taking ref-A failed */
- break;
+ if (srv_util_pathisdir(new_pathinfo)) {
+ struct lib9p_stat stat = LO_CALL(new_pathinfo->file, stat, &ctx->ctx);
+ if (lib9p_ctx_has_error(&ctx->ctx.basectx))
+ break;
+ lib9p_stat_assert(stat);
+ if (!srv_util_check_perm(ctx, &stat, 0b001)) {
+ lib9p_error(&ctx->ctx.basectx,
+ LINUX_EACCES, "you do not have execute permission on that directory");
+ srv_util_pathfree(ctx, LO_CALL(new_pathinfo->file, qid).path);
+ break;
+ }
}
- resp->wqid[resp->nwqid] = stat.file_qid;
+ resp->wqid[resp->nwqid] = LO_CALL(new_pathinfo->file, qid);
- /* presumption of taking ref-A succeeded */
- util_release(&ctx->ctx, dir);
- dir = member;
+ srv_util_pathfree(ctx, LO_CALL(pathinfo->file, qid).path);
+ pathinfo = new_pathinfo;
}
if (resp->nwqid == req->nwname) {
if (req->newfid == req->fid) {
- handle_Tclunk(ctx,
- &(struct lib9p_msg_Tclunk){.fid = req->fid},
- &(struct lib9p_msg_Rclunk){});
- }
- if (!fidmap_store(&ctx->parent_sess->fids, req->newfid, (struct _srv_fidinfo){
- .file = dir,
- .flags = isdir ? FIDFLAG_ISDIR : 0,
- })) {
- lib9p_error(&ctx->ctx.basectx,
- LINUX_EMFILE, "too many open files");
- util_release(&ctx->ctx, dir); /* presumption of insertion failed */
+ if (srv_util_pathisdir(pathinfo))
+ LO_CALL(fidinfo->dir.io, iofree);
+ else
+ LO_CALL(fidinfo->file.io, iofree);
+ fidinfo->flags = 0;
}
+ if (!srv_util_fidsave(ctx, req->newfid, pathinfo, req->newfid == req->fid))
+ srv_util_pathfree(ctx, LO_CALL(pathinfo->file, qid).path);
} else {
assert(lib9p_ctx_has_error(&ctx->ctx.basectx));
- if (req->newfid != req->fid)
- util_release(&ctx->ctx, dir); /* presumption of insertion failed */
+ srv_util_pathfree(ctx, LO_CALL(pathinfo->file, qid).path);
if (resp->nwqid > 0)
lib9p_ctx_clear_error(&ctx->ctx.basectx);
}
@@ -730,8 +851,10 @@ static void handle_Topen(struct _lib9p_srv_req *ctx,
LINUX_EALREADY, "FID is already open");
return;
}
- if (fidinfo->flags & FIDFLAG_ISDIR) {
- if ( ((req->mode & LIB9P_O_MODE_MASK) != LIB9P_O_READ) ||
+ struct srv_pathinfo *pathinfo = pathmap_load(&ctx->parent_sess->paths, fidinfo->path);
+ assert(pathinfo);
+ if (srv_util_pathisdir(pathinfo)) {
+ if ( ((req->mode & LIB9P_O_MODE_MASK) != LIB9P_O_MODE_READ) ||
(req->mode & LIB9P_O_TRUNC) ||
(req->mode & LIB9P_O_RCLOSE) ) {
lib9p_error(&ctx->ctx.basectx,
@@ -741,61 +864,92 @@ static void handle_Topen(struct _lib9p_srv_req *ctx,
}
/* Variables. */
- lib9p_o_t reqmode = req->mode;
- uint8_t fidflags = fidinfo->flags;
- implements_lib9p_srv_file *file = fidinfo->file;
+ lib9p_o_t reqmode = req->mode;
+ uint8_t fidflags = fidinfo->flags;
/* Check permissions. */
if (reqmode & LIB9P_O_RCLOSE) {
- struct lib9p_stat parent_stat = VCALL(file->_parent_dir, stat, &ctx->ctx);
+ struct srv_pathinfo *parent = pathmap_load(&ctx->parent_sess->paths, pathinfo->parent_dir);
+ assert(parent);
+ struct lib9p_stat parent_stat = LO_CALL(parent->file, stat, &ctx->ctx);
if (lib9p_ctx_has_error(&ctx->ctx.basectx))
return;
- lib9p_assert_stat(parent_stat);
- if (!util_check_perm(&ctx->ctx, &parent_stat, 0b010)) {
+ lib9p_stat_assert(parent_stat);
+ if (!srv_util_check_perm(ctx, &parent_stat, 0b010)) {
lib9p_error(&ctx->ctx.basectx,
LINUX_EACCES, "permission denied to remove-on-close");
return;
}
- fidflags = fidflags | FIDFLAG_RCLOSE;
+ fidflags |= FIDFLAG_RCLOSE;
}
- struct lib9p_stat stat = VCALL(file, stat, &ctx->ctx);
+ struct lib9p_stat stat = LO_CALL(pathinfo->file, stat, &ctx->ctx);
if (lib9p_ctx_has_error(&ctx->ctx.basectx))
return;
- lib9p_assert_stat(stat);
- if (stat.file_mode & LIB9P_QT_APPEND)
+ lib9p_stat_assert(stat);
+ if ((stat.file_mode & LIB9P_DM_EXCL) && pathinfo->io_refcount) {
+ lib9p_error(&ctx->ctx.basectx,
+ LINUX_EEXIST, "exclusive file is already opened");
+ return;
+ }
+ if (stat.file_mode & LIB9P_DM_APPEND)
reqmode = reqmode & ~LIB9P_O_TRUNC;
uint8_t perm_bits = 0;
+ bool rd = false, wr = false;
switch (reqmode & LIB9P_O_MODE_MASK) {
- case LIB9P_O_READ:
+ case LIB9P_O_MODE_READ:
perm_bits = 0b100;
- fidflags = fidflags | FIDFLAG_OPEN_R;
+ rd = true;
break;
- case LIB9P_O_WRITE:
+ case LIB9P_O_MODE_WRITE:
perm_bits = 0b010;
- fidflags = fidflags | FIDFLAG_OPEN_W;
+ wr = true;
break;
- case LIB9P_O_RDWR:
+ case LIB9P_O_MODE_RDWR:
perm_bits = 0b110;
- fidflags = fidflags | FIDFLAG_OPEN_R | FIDFLAG_OPEN_W;
+ rd = wr = true;
break;
- case LIB9P_O_EXEC:
+ case LIB9P_O_MODE_EXEC:
perm_bits = 0b001;
- fidflags = fidflags | FIDFLAG_OPEN_R;
+ rd = true;
break;
}
- if (!util_check_perm(&ctx->ctx, &stat, perm_bits)) {
+ if (!srv_util_check_perm(ctx, &stat, perm_bits)) {
lib9p_error(&ctx->ctx.basectx,
LINUX_EACCES, "permission denied");
+ return;
}
/* Actually make the call. */
- uint32_t iounit = VCALL(file, io, &ctx->ctx, reqmode);
- if (lib9p_ctx_has_error(&ctx->ctx.basectx))
- return;
+ uint32_t iounit;
+ struct lib9p_qid qid;
+ if (srv_util_pathisdir(pathinfo)) {
+ fidinfo->dir.io = LO_CALL(pathinfo->file, dopen, &ctx->ctx);
+ assert(LO_IS_NULL(fidinfo->dir.io) == lib9p_ctx_has_error(&ctx->ctx.basectx));
+ if (lib9p_ctx_has_error(&ctx->ctx.basectx))
+ return;
+ fidinfo->dir.idx = 0;
+ fidinfo->dir.off = 0;
+ qid = LO_CALL(fidinfo->dir.io, qid);
+ iounit = 0;
+ } else {
+ fidinfo->file.io = LO_CALL(pathinfo->file, fopen, &ctx->ctx,
+ rd, wr,
+ reqmode & LIB9P_O_TRUNC);
+ assert(LO_IS_NULL(fidinfo->file.io) == lib9p_ctx_has_error(&ctx->ctx.basectx));
+ if (lib9p_ctx_has_error(&ctx->ctx.basectx))
+ return;
+ qid = LO_CALL(fidinfo->file.io, qid);
+ iounit = LO_CALL(fidinfo->file.io, iounit);
+ }
/* Success. */
+ if (rd)
+ fidflags |= FIDFLAG_OPEN_R;
+ if (wr)
+ fidflags |= FIDFLAG_OPEN_W;
+ pathinfo->io_refcount++;
fidinfo->flags = fidflags;
- resp->qid = stat.file_qid;
+ resp->qid = qid;
resp->iounit = iounit;
}
@@ -813,6 +967,8 @@ static void handle_Tread(struct _lib9p_srv_req *ctx,
struct lib9p_msg_Rread *resp) {
util_handler_common(ctx, req, resp);
+ /* TODO: serialize simultaneous reads to the same FID */
+
/* Check that the FID is valid for this. */
struct _srv_fidinfo *fidinfo = fidmap_load(&ctx->parent_sess->fids, req->fid);
if (!fidinfo) {
@@ -827,38 +983,47 @@ static void handle_Tread(struct _lib9p_srv_req *ctx,
}
/* Variables. */
- implements_lib9p_srv_file *file = fidinfo->file;
- resp->data.dat = (char *)(&resp[1]);
+ struct srv_pathinfo *pathinfo = pathmap_load(&ctx->parent_sess->paths, fidinfo->path);
+ assert(pathinfo);
/* Do it. */
- if (fidinfo->flags & FIDFLAG_ISDIR) {
+ if (srv_util_pathisdir(pathinfo)) {
/* Translate byte-offset to object-index. */
size_t idx;
if (req->offset == 0)
idx = 0;
- else if (req->offset == fidinfo->dir_off)
- idx = fidinfo->dir_idx;
+ else if (req->offset == fidinfo->dir.off)
+ idx = fidinfo->dir.idx;
else {
lib9p_errorf(&ctx->ctx.basectx,
- LINUX_EINVAL, "invalid offset (must be 0 or %"PRIu32"): %"PRIu32,
- fidinfo->dir_off, req->offset);
+ LINUX_EINVAL, "invalid offset (must be 0 or %"PRIu64"): %"PRIu64,
+ fidinfo->dir.off, req->offset);
return;
}
/* Do it. */
- size_t num = VCALL(file, dread, &ctx->ctx, (uint8_t *)resp->data.dat, req->count, idx);
+ resp->data = (char *)(&resp[1]);
+ size_t num = LO_CALL(fidinfo->dir.io, dread, &ctx->ctx, (uint8_t *)resp->data, req->count, idx);
/* Translate object-count back to byte-count. */
uint32_t len = 0;
for (size_t i = 0; i < num; i++) {
uint32_t i_len;
- lib9p_validate_stat(&ctx->ctx.basectx, req->count, &((uint8_t *)resp->data.dat)[len], &i_len, NULL);
+ lib9p_stat_validate(&ctx->ctx.basectx, req->count, &((uint8_t *)resp->data)[len], &i_len, NULL);
len += i_len;
}
- resp->data.len = len;
+ resp->count = len;
/* Remember. */
- fidinfo->dir_idx = idx+num;
- fidinfo->dir_off = req->offset + len;
- } else
- resp->data.len = VCALL(file, pread, &ctx->ctx, resp->data.dat, req->count, req->offset);
+ fidinfo->dir.idx = idx+num;
+ fidinfo->dir.off = req->offset + len;
+ } else {
+ struct iovec iov;
+ LO_CALL(fidinfo->file.io, pread, &ctx->ctx, req->count, req->offset, &iov);
+ if (!lib9p_ctx_has_error(&ctx->ctx.basectx)) {
+ resp->count = iov.iov_len;
+ resp->data = iov.iov_base;
+ if (resp->count > req->count)
+ resp->count = req->count;
+ }
+ }
}
static void handle_Twrite(struct _lib9p_srv_req *ctx,
@@ -866,6 +1031,8 @@ static void handle_Twrite(struct _lib9p_srv_req *ctx,
struct lib9p_msg_Rwrite *resp) {
util_handler_common(ctx, req, resp);
+ /* TODO: serialize simultaneous writes to the same FID */
+
/* Check that the FID is valid for this. */
struct _srv_fidinfo *fidinfo = fidmap_load(&ctx->parent_sess->fids, req->fid);
if (!fidinfo) {
@@ -880,41 +1047,69 @@ static void handle_Twrite(struct _lib9p_srv_req *ctx,
}
/* Variables. */
- implements_lib9p_srv_file *file = fidinfo->file;
+ struct srv_pathinfo *pathinfo = pathmap_load(&ctx->parent_sess->paths, fidinfo->path);
+ assert(pathinfo);
/* Do it. */
- resp->count = VCALL(file, pwrite, &ctx->ctx, req->data.dat, req->data.len, req->offset);
+ resp->count = LO_CALL(fidinfo->file.io, pwrite, &ctx->ctx, req->data, req->count, req->offset);
}
-static void handle_Tclunk(struct _lib9p_srv_req *ctx,
- struct lib9p_msg_Tclunk *req,
- struct lib9p_msg_Rclunk *resp) {
- util_handler_common(ctx, req, resp);
-
- struct _srv_fidinfo *fidinfo = fidmap_load(&ctx->parent_sess->fids, req->fid);
+static void clunkremove(struct _lib9p_srv_req *ctx, lib9p_fid_t fid, bool remove) {
+ struct _srv_fidinfo *fidinfo = fidmap_load(&ctx->parent_sess->fids, fid);
if (!fidinfo) {
lib9p_errorf(&ctx->ctx.basectx,
- LINUX_EBADF, "bad file number %"PRIu32, req->fid);
+ LINUX_EBADF, "bad file number %"PRIu32, fid);
return;
}
- if (fidinfo->flags & FIDFLAG_RCLOSE) {
- handle_Tremove(ctx,
- &(struct lib9p_msg_Tremove){.fid = req->fid},
- &(struct lib9p_msg_Rremove){});
- return;
+ if (fidinfo->flags & FIDFLAG_RCLOSE)
+ remove = true;
+ struct srv_pathinfo *pathinfo = pathmap_load(&ctx->parent_sess->paths, fidinfo->path);
+ assert(pathinfo);
+
+ if (remove) {
+ if (pathinfo->parent_dir == fidinfo->path) {
+ lib9p_errorf(&ctx->ctx.basectx,
+ LINUX_EBUSY, "cannot remove root");
+ goto clunk;
+ }
+ struct srv_pathinfo *parent = pathmap_load(&ctx->parent_sess->paths, pathinfo->parent_dir);
+ assert(parent);
+ struct lib9p_stat parent_stat = LO_CALL(parent->file, stat, &ctx->ctx);
+ if (!srv_util_check_perm(ctx, &parent_stat, 0b010)) {
+ lib9p_error(&ctx->ctx.basectx,
+ LINUX_EACCES, "you do not have write permission on the parent directory");
+ goto clunk;
+ }
+ LO_CALL(pathinfo->file, remove, &ctx->ctx);
+ }
+
+ clunk:
+ if (fidinfo->flags & FIDFLAG_OPEN) {
+ if (srv_util_pathisdir(pathinfo))
+ LO_CALL(fidinfo->dir.io, iofree);
+ else
+ LO_CALL(fidinfo->file.io, iofree);
+ pathinfo->io_refcount--;
}
+ srv_util_pathfree(ctx, LO_CALL(pathinfo->file, qid).path);
+ fidmap_del(&ctx->parent_sess->fids, fid);
+}
- VCALL(fidinfo->file, free, &ctx->ctx);
- fidmap_del(&ctx->parent_sess->fids, req->fid);
+static void handle_Tclunk(struct _lib9p_srv_req *ctx,
+ struct lib9p_msg_Tclunk *req,
+ struct lib9p_msg_Rclunk *resp) {
+ util_handler_common(ctx, req, resp);
+
+ clunkremove(ctx, req->fid, false);
}
+
static void handle_Tremove(struct _lib9p_srv_req *ctx,
struct lib9p_msg_Tremove *req,
struct lib9p_msg_Rremove *resp) {
util_handler_common(ctx, req, resp);
- lib9p_error(&ctx->ctx.basectx,
- LINUX_EOPNOTSUPP, "remove not (yet?) implemented");
+ clunkremove(ctx, req->fid, true);
}
static void handle_Tstat(struct _lib9p_srv_req *ctx,
@@ -928,10 +1123,12 @@ static void handle_Tstat(struct _lib9p_srv_req *ctx,
LINUX_EBADF, "bad file number %"PRIu32, req->fid);
return;
}
+ struct srv_pathinfo *pathinfo = pathmap_load(&ctx->parent_sess->paths, fidinfo->path);
+ assert(pathinfo);
- resp->stat = VCALL(fidinfo->file, stat, &ctx->ctx);
+ resp->stat = LO_CALL(pathinfo->file, stat, &ctx->ctx);
if (!lib9p_ctx_has_error(&ctx->ctx.basectx))
- lib9p_assert_stat(resp->stat);
+ lib9p_stat_assert(resp->stat);
}
static void handle_Twstat(struct _lib9p_srv_req *ctx,
@@ -943,7 +1140,7 @@ static void handle_Twstat(struct _lib9p_srv_req *ctx,
LINUX_EOPNOTSUPP, "wstat not (yet?) implemented");
}
-#ifdef CONFIG_9P_ENABLE_9P2000_e
+#if CONFIG_9P_ENABLE_9P2000_e
static void handle_Tsession(struct _lib9p_srv_req *ctx,
struct lib9p_msg_Tsession *req,
struct lib9p_msg_Rsession *resp) {