summaryrefslogtreecommitdiff
path: root/lib9p
diff options
context:
space:
mode:
Diffstat (limited to 'lib9p')
-rw-r--r--lib9p/srv.c341
-rw-r--r--lib9p/srv_include/lib9p/srv.h8
-rw-r--r--lib9p/tests/test_server/fs_flush.c9
-rw-r--r--lib9p/tests/test_server/fs_shutdown.c9
-rw-r--r--lib9p/tests/test_server/fs_whoami.c8
5 files changed, 221 insertions, 154 deletions
diff --git a/lib9p/srv.c b/lib9p/srv.c
index 9a00373..3e4de91 100644
--- a/lib9p/srv.c
+++ b/lib9p/srv.c
@@ -5,7 +5,6 @@
*/
#include <stddef.h> /* for size_t */
-#include <stdlib.h> /* for malloc() */
#include <string.h> /* for memcpy() */
#include <libcr/coroutine.h>
@@ -52,6 +51,7 @@ bool lib9p_srv_flush_requested(struct lib9p_srv_ctx *ctx) {
/* structs ********************************************************************/
+#ifndef NDEBUG
void lib9p_srv_stat_assert(const struct lib9p_srv_stat *stat) {
assert(stat);
assert( ((bool)(stat->mode & LIB9P_DM_DIR )) == ((bool)(stat->qid.type & LIB9P_QT_DIR )) );
@@ -61,6 +61,7 @@ void lib9p_srv_stat_assert(const struct lib9p_srv_stat *stat) {
assert( ((bool)(stat->mode & LIB9P_DM_TMP )) == ((bool)(stat->qid.type & LIB9P_QT_TMP )) );
assert( (stat->size == 0) || !(stat->mode & LIB9P_DM_DIR) );
}
+#endif
enum srv_filetype {
SRV_FILETYPE_FILE,
@@ -146,7 +147,6 @@ struct srv_sess {
enum lib9p_version version;
uint32_t max_msg_size;
/* mutable */
- bool initialized;
bool closing;
struct srv_pathmap paths; /* srv_path_t => `lib9p_srv_file` + metadata */
struct srv_fidmap fids; /* lib9p_fid_t => `lib9p_srv_{fio,dio}` + metadata */
@@ -166,7 +166,7 @@ static enum srv_filetype srv_qid_filetype(struct lib9p_qid qid) {
}
[[maybe_unused]]
-static bool srv_check_perm(struct srv_req *ctx, struct lib9p_srv_stat *stat, uint8_t action) {
+static bool srv_stat_check_perm(struct srv_req *ctx, struct lib9p_srv_stat *stat, uint8_t action) {
assert(ctx);
assert(stat);
assert(action);
@@ -178,14 +178,26 @@ static bool srv_check_perm(struct srv_req *ctx, struct lib9p_srv_stat *stat, uin
}
[[maybe_unused]]
+static ok_or_error srv_file_check_perm(struct srv_req *ctx, lo_interface lib9p_srv_file file, uint8_t action) {
+ assert(ctx);
+ assert(!LO_IS_NULL(file));
+ assert(action);
+
+ struct lib9p_srv_stat stat;
+ error err = LO_CALL(file, stat, ctx, &stat);
+ if (!ERROR_IS_NULL(err))
+ return ERROR_NEW_ERR(ok, err);
+ lib9p_srv_stat_assert(&stat);
+ return ERROR_NEW_VAL(ok, srv_stat_check_perm(ctx, &stat, action));
+}
+
+[[maybe_unused]]
static struct lib9p_srv_userid *srv_userid_new(struct lib9p_s name
#if CONFIG_9P_ENABLE_9P2000_u || CONFIG_9P_ENABLE_9P2000_L
, lib9p_nuid_t num
#endif
) {
- struct lib9p_srv_userid *ret = malloc(sizeof(struct lib9p_srv_userid) + name.len);
- if (!ret)
- return NULL;
+ struct lib9p_srv_userid *ret = (void *)heap_alloc(sizeof(struct lib9p_srv_userid) + name.len, char);
void *end_dat = (void *)&ret[1];
memcpy(end_dat, name.utf8, name.len);
#if CONFIG_9P_ENABLE_9P2000_u || CONFIG_9P_ENABLE_9P2000_L
@@ -367,13 +379,28 @@ static void srv_msglog(struct srv_req *req, enum lib9p_msg_type typ, void *hostm
#define srv_nonrespond_error log_errorln
-static void srv_write_Rmsg(struct srv_req *req, struct lib9p_Rmsg_send_buf *resp) {
- size_t_and_error r;
+static error srv_write_Rmsg(struct srv_req *req, enum lib9p_msg_type typ, void *host) {
+ assert(req);
+ assert(typ % 2 == 1);
+ assert(host);
+
+ struct lib9p_Rmsg_send_buf net;
+ error err = lib9p_Rmsg_marshal(&req->basectx,
+ typ, host,
+ &net);
+ if (!ERROR_IS_NULL(err))
+ return err;
+
+ srv_msglog(req, typ, host);
+
cr_mutex_lock(&req->parent_sess->parent_conn->writelock);
- r = io_writev(req->parent_sess->parent_conn->fd, resp->iov, resp->iov_cnt);
+ size_t_and_error r = io_writev(req->parent_sess->parent_conn->fd, net.iov, net.iov_cnt);
cr_mutex_unlock(&req->parent_sess->parent_conn->writelock);
- if (!ERROR_IS_NULL(r.err))
+ if (!ERROR_IS_NULL(r.err)) {
srv_nonrespond_error("write: (", r.size_t, ", ", (error, r.err), ")");
+ error_cleanup(&r.err);
+ }
+ return ERROR_NULL;
}
static void srv_respond_error(struct srv_req *req, error err) {
@@ -396,14 +423,9 @@ static void srv_respond_error(struct srv_req *req, error err) {
if (((uint32_t)host.errstr.len) + overhead > sess->max_msg_size)
host.errstr.len = sess->max_msg_size - overhead;
- struct lib9p_Rmsg_send_buf net;
-
- lib9p_Rmsg_marshal(&req->basectx,
- LIB9P_TYP_Rerror, &host,
- &net);
+ error marshal_err = srv_write_Rmsg(req, LIB9P_TYP_Rerror, &host);
+ assert(ERROR_IS_NULL(marshal_err));
- srv_msglog(req, LIB9P_TYP_Rerror, &host);
- srv_write_Rmsg(req, &net);
error_cleanup(&err);
}
@@ -453,110 +475,112 @@ void lib9p_srv_accept_and_read_loop(struct lib9p_srv *srv, lo_interface net_stre
}
}
+static void lib9p_srv_worker_Tversion(struct srv_req *ctx);
+
void lib9p_srv_read(struct lib9p_srv *srv, lo_interface net_stream_conn _conn) {
assert(srv);
assert(srv->rootdir);
assert(!LO_IS_NULL(_conn));
- struct srv_conn conn = {
+ struct srv_conn _conn_ = {
.parent_srv = srv,
.fd = _conn,
.reader = cr_getcid(),
};
- struct srv_sess sess = {
- .parent_conn = &conn,
+ struct srv_conn *conn = &_conn_;
+ struct srv_sess _sess_ = {
+ .parent_conn = conn,
.version = LIB9P_VER_uninitialized,
.max_msg_size = CONFIG_9P_SRV_MAX_MSG_SIZE,
- .initialized = false,
};
+ struct srv_sess *sess = &_sess_;
+ struct srv_req _req_;
+ struct srv_req *req = &_req_;
+
for (;;) {
/* Read the message. */
size_t done = 0;
uint8_t buf[7];
- if (srv_read_exactly(conn.fd, buf, 4, &done))
+ if (srv_read_exactly(conn->fd, buf, 4, &done))
break;
size_t goal = uint32le_decode(buf);
if (goal < 7) {
srv_nonrespond_error("T-message is impossibly small");
break;
}
- if (srv_read_exactly(conn.fd, buf, 7, &done))
+ if (srv_read_exactly(conn->fd, buf, 7, &done))
break;
- struct srv_req req = {
+ *req = (struct srv_req){
.basectx = {
- .version = sess.version,
- .max_msg_size = sess.max_msg_size,
+ .version = sess->version,
+ .max_msg_size = sess->max_msg_size,
},
- .parent_sess = &sess,
+ .parent_sess = sess,
.tag = uint16le_decode(&buf[5]),
.net_bytes = buf,
};
- if (goal > sess.max_msg_size) {
- srv_respond_error(&req, error_new(E_POSIX_EMSGSIZE,
- "T-message larger than ", sess.initialized ? "negotiated" : "server", " limit",
- " (", goal, " > ", sess.max_msg_size, ")"));
+ if (goal > sess->max_msg_size) {
+ srv_respond_error(req, error_new(E_POSIX_EMSGSIZE,
+ "T-message larger than ", sess->version == LIB9P_VER_uninitialized ? "server" : "negotiated", " limit",
+ " (", goal, " > ", sess->max_msg_size, ")"));
continue;
}
- req.net_bytes = malloc(goal);
- assert(req.net_bytes);
- memcpy(req.net_bytes, buf, done);
- if (srv_read_exactly(conn.fd, req.net_bytes, goal, &done)) {
- free(req.net_bytes);
+ req->net_bytes = heap_alloc(goal, uint8_t);
+ memcpy(req->net_bytes, buf, done);
+ if (srv_read_exactly(conn->fd, req->net_bytes, goal, &done)) {
+ free(req->net_bytes);
break;
}
/* Handle the message... */
- if (req.net_bytes[4] == LIB9P_TYP_Tversion)
+ if (req->net_bytes[4] == LIB9P_TYP_Tversion)
/* ...in this coroutine for Tversion, */
- lib9p_srv_worker(&req);
+ lib9p_srv_worker_Tversion(req);
else
/* ...but usually in another coroutine. */
- cr_rpc_send_req(&srv->reqch, &req);
+ cr_rpc_send_req(&srv->reqch, req);
}
- if (map_len(&sess.reqs) == 0) {
- error err = io_close(conn.fd);
- if (!ERROR_IS_NULL(err)) {
- srv_nonrespond_error("conn close: ", (error, err));
- error_cleanup(&err);
- }
- } else {
- error err = io_close_read(conn.fd);
- if (!ERROR_IS_NULL(err)) {
- srv_nonrespond_error("conn close: ", (error, err));
- error_cleanup(&err);
- }
- sess.closing = true;
+
+ /* Cleanup `conn` and `sess->reqs`. */
+ error err = io_close_read(conn->fd);
+ if (!ERROR_IS_NULL(err)) {
+ srv_nonrespond_error("conn close: ", (error, err));
+ error_cleanup(&err);
+ }
+ if (map_len(&sess->reqs)) {
+ sess->closing = true;
cr_pause_and_yield();
- assert(map_len(&sess.reqs) == 0);
- err = io_close_write(conn.fd);
- if (!ERROR_IS_NULL(err)) {
- srv_nonrespond_error("conn close: ", (error, err));
- error_cleanup(&err);
- }
}
+ assert(map_len(&sess->reqs) == 0);
+ err = io_close_write(conn->fd);
+ if (!ERROR_IS_NULL(err)) {
+ srv_nonrespond_error("conn close: ", (error, err));
+ error_cleanup(&err);
+ }
+ map_free(&sess->reqs);
- assert(map_len(&sess.reqs) == 0);
- map_free(&sess.reqs);
-
- struct srv_req pseudoreq = {
+ /* Cleanup `sess->fids`. */
+ *req = (struct srv_req){
.basectx = {
- .version = sess.version,
- .max_msg_size = sess.max_msg_size,
+ .version = sess->version,
+ .max_msg_size = sess->max_msg_size,
},
- .parent_sess = &sess,
+ .parent_sess = sess,
};
- MAP_FOREACH(&sess.fids, fid, fidinfo) {
- error err = srv_fid_del(&pseudoreq, fid, fidinfo, false);
+ MAP_FOREACH(&sess->fids, fid, fidinfo) {
+ err = srv_fid_del(req, fid, fidinfo, false);
if (!ERROR_IS_NULL(err)) {
srv_nonrespond_error("clunk: ", (error, err));
error_cleanup(&err);
}
}
- map_free(&sess.fids);
+ assert(map_len(&sess->fids) == 0);
+ map_free(&sess->fids);
- assert(map_len(&sess.paths) == 0);
- map_free(&sess.paths);
+ /* Cleanup `sess->paths`. */
+ assert(map_len(&sess->paths) == 0);
+ map_free(&sess->paths);
}
/* write coroutine ************************************************************/
@@ -621,7 +645,7 @@ _HANDLER_PROTO(swrite);
#endif
void lib9p_srv_worker(struct srv_req *ctx) {
- uint8_t *host_req = NULL;
+ [[gnu::cleanup(heap_cleanup)]] void *host_req = NULL;
/* Unmarshal it. *****************************************************/
size_t_or_error r = lib9p_Tmsg_validate(&ctx->basectx, ctx->net_bytes);
@@ -630,8 +654,7 @@ void lib9p_srv_worker(struct srv_req *ctx) {
goto release;
}
size_t host_size = r.size_t;
- host_req = calloc(1, host_size);
- assert(host_req);
+ host_req = heap_alloc(host_size, char);
enum lib9p_msg_type typ;
lib9p_Tmsg_unmarshal(&ctx->basectx, ctx->net_bytes,
&typ, host_req);
@@ -640,7 +663,6 @@ void lib9p_srv_worker(struct srv_req *ctx) {
/* Handle it. ********************************************************/
#define CASE(typ) case LIB9P_TYP_##typ: handle_##typ(ctx, (void *)host_req); break
LM_PARTIAL_SWITCH (typ) {
- CASE(Tversion);
#if _LIB9P_ENABLE_stat
CASE(Tauth);
CASE(Tattach);
@@ -680,8 +702,40 @@ void lib9p_srv_worker(struct srv_req *ctx) {
}
if (ctx->parent_sess->closing && !map_len(&ctx->parent_sess->reqs))
cr_unpause(ctx->parent_sess->parent_conn->reader);
- if (host_req)
- free(host_req);
+ free(ctx->net_bytes);
+}
+
+static void lib9p_srv_worker_Tversion(struct srv_req *ctx) {
+ [[gnu::cleanup(heap_cleanup)]] void *host_req = NULL;
+
+ /* Unmarshal it. *****************************************************/
+ size_t_or_error r = lib9p_Tmsg_validate(&ctx->basectx, ctx->net_bytes);
+ if (r.is_err) {
+ srv_respond_error(ctx, r.err);
+ goto release;
+ }
+ size_t host_size = r.size_t;
+ host_req = heap_alloc(host_size, char);
+ enum lib9p_msg_type typ;
+ lib9p_Tmsg_unmarshal(&ctx->basectx, ctx->net_bytes,
+ &typ, host_req);
+ srv_msglog(ctx, typ, host_req);
+
+ /* Handle it. ********************************************************/
+ handle_Tversion(ctx, (void *)host_req);
+ assert(ctx->responded);
+
+ /* Release resources. ************************************************/
+ release:
+ map_del(&ctx->parent_sess->reqs, ctx->tag);
+ size_t nwaiters;
+ while ((nwaiters = cr_chan_num_waiters(&ctx->flush_ch))) {
+ cr_chan_send(&ctx->flush_ch, (nwaiters == 1)
+ ? _LIB9P_SRV_FLUSH_RFLUSH
+ : _LIB9P_SRV_FLUSH_SILENT);
+ }
+ if (ctx->parent_sess->closing && !map_len(&ctx->parent_sess->reqs))
+ cr_unpause(ctx->parent_sess->parent_conn->reader);
free(ctx->net_bytes);
}
@@ -696,14 +750,9 @@ static void _srv_respond(struct srv_req *ctx, enum lib9p_msg_type resp_typ, void
}
} else {
assert(host_resp);
- struct lib9p_Rmsg_send_buf net_resp;
- err = lib9p_Rmsg_marshal(&ctx->basectx,
- resp_typ, host_resp,
- &net_resp);
+ err = srv_write_Rmsg(ctx, resp_typ, host_resp);
if (!ERROR_IS_NULL(err))
goto error;
- srv_msglog(ctx, resp_typ, host_resp);
- srv_write_Rmsg(ctx, &net_resp);
}
ctx->responded = true;
}
@@ -779,7 +828,7 @@ static void handle_Tversion(struct srv_req *ctx,
if (map_len(&ctx->parent_sess->reqs)) {
/* Flush all in-progress requests, and wait for them
* to finish. */
- struct cr_select_arg *args = stack_alloc(map_len(&ctx->parent_sess->reqs), struct cr_select_arg);
+ struct cr_select_arg *args = heap_alloc(map_len(&ctx->parent_sess->reqs), struct cr_select_arg);
while (map_len(&ctx->parent_sess->reqs)) {
size_t i = 0;
MAP_FOREACH(&ctx->parent_sess->reqs, tag, reqpp) {
@@ -789,6 +838,7 @@ static void handle_Tversion(struct srv_req *ctx,
assert(i == map_len(&ctx->parent_sess->reqs));
cr_select_v(i, args);
}
+ free(args);
}
/* Close all FIDs. */
MAP_FOREACH(&ctx->parent_sess->fids, fid, fidinfo) {
@@ -953,8 +1003,7 @@ static void handle_Twalk(struct srv_req *ctx,
assert(pathinfo);
pathinfo->gc_refcount++;
- struct lib9p_qid _resp_qid[16];
- resp.wqid = _resp_qid;
+ resp.wqid = heap_alloc(req->nwname, struct lib9p_qid);
for (resp.nwqid = 0; resp.nwqid < req->nwname; resp.nwqid++) {
if (pathinfo->type != SRV_FILETYPE_DIR) {
err = error_new(E_POSIX_ENOTDIR, "not a directory");
@@ -977,13 +1026,11 @@ static void handle_Twalk(struct srv_req *ctx,
}
if (new_pathinfo->type == SRV_FILETYPE_DIR) {
- lib9p_srv_stat_or_error stat = LO_CALL(new_pathinfo->file, stat, ctx);
- if (stat.is_err) {
- err = stat.err;
+ ok_or_error have_perm = srv_file_check_perm(ctx, new_pathinfo->file, 0b001);
+ if (have_perm.is_err) {
+ err = have_perm.err;
break;
- }
- lib9p_srv_stat_assert(&stat.lib9p_srv_stat);
- if (!srv_check_perm(ctx, &stat.lib9p_srv_stat, 0b001)) {
+ } else if (!have_perm.ok) {
err = error_new(E_POSIX_EACCES, "you do not have execute permission on that directory");
srv_path_decref(ctx, LO_CALL(new_pathinfo->file, qid).path);
break;
@@ -1011,6 +1058,8 @@ static void handle_Twalk(struct srv_req *ctx,
if (ctx->user)
ctx->user = srv_userid_decref(ctx->user);
srv_respond(ctx, walk, &resp, err);
+ if (resp.wqid)
+ free(resp.wqid);
}
static void handle_Topen(struct srv_req *ctx,
@@ -1047,29 +1096,26 @@ static void handle_Topen(struct srv_req *ctx,
if (reqmode & LIB9P_O_RCLOSE) {
struct srv_pathinfo *parent = map_load(&ctx->parent_sess->paths, pathinfo->parent_dir);
assert(parent);
- lib9p_srv_stat_or_error parent_stat = LO_CALL(parent->file, stat, ctx);
- if (parent_stat.is_err) {
- err = parent_stat.err;
+ ok_or_error have_perm = srv_file_check_perm(ctx, parent->file, 0b010);
+ if (have_perm.is_err) {
+ err = have_perm.err;
goto topen_return;
- }
- lib9p_srv_stat_assert(&parent_stat.lib9p_srv_stat);
- if (!srv_check_perm(ctx, &parent_stat.lib9p_srv_stat, 0b010)) {
+ } else if (!have_perm.ok) {
err = error_new(E_POSIX_EACCES, "permission denied to remove-on-close");
goto topen_return;
}
fidflags |= FIDFLAG_RCLOSE;
}
- lib9p_srv_stat_or_error stat = LO_CALL(pathinfo->file, stat, ctx);
- if (stat.is_err) {
- err = stat.err;
+ struct lib9p_srv_stat stat;
+ err = LO_CALL(pathinfo->file, stat, ctx, &stat);
+ if (!ERROR_IS_NULL(err))
goto topen_return;
- }
- lib9p_srv_stat_assert(&stat.lib9p_srv_stat);
- if ((stat.lib9p_srv_stat.mode & LIB9P_DM_EXCL) && pathinfo->io_refcount) {
+ lib9p_srv_stat_assert(&stat);
+ if ((stat.mode & LIB9P_DM_EXCL) && pathinfo->io_refcount) {
err = error_new(E_POSIX_EEXIST, "exclusive file is already opened");
goto topen_return;
}
- if (stat.lib9p_srv_stat.mode & LIB9P_DM_APPEND) {
+ if (stat.mode & LIB9P_DM_APPEND) {
fidflags |= FIDFLAG_APPEND;
reqmode = reqmode & ~LIB9P_O_TRUNC;
}
@@ -1093,7 +1139,7 @@ static void handle_Topen(struct srv_req *ctx,
rd = true;
break;
}
- if (!srv_check_perm(ctx, &stat.lib9p_srv_stat, perm_bits)) {
+ if (!srv_stat_check_perm(ctx, &stat, perm_bits)) {
err = error_new(E_POSIX_EACCES, "permission denied");
goto topen_return;
}
@@ -1160,9 +1206,9 @@ static void handle_Tcreate(struct srv_req *ctx,
srv_respond(ctx, create, &resp, err);
}
-static struct lib9p_stat srv_stat_to_net_stat(const struct lib9p_srv_stat *in) {
- assert(in);
- return (struct lib9p_stat){
+static void srv_stat_to_net_stat(struct lib9p_stat *out, const struct lib9p_srv_stat *in) {
+ lib9p_srv_stat_assert(in);
+ *out = (struct lib9p_stat){
.qid = in->qid,
.mode = in->mode,
.atime = in->atime_sec,
@@ -1271,6 +1317,30 @@ static void handle_read_file(struct srv_req *ctx, struct srv_fidinfo *fidinfo, u
}
#if _LIB9P_ENABLE_stat
+
+static error stat_and_convert(struct lib9p_stat *out, struct srv_req *ctx, lo_interface lib9p_srv_file file) {
+ struct lib9p_srv_stat hoststat;
+ error err = LO_CALL(file, stat, ctx, &hoststat);
+ if (!ERROR_IS_NULL(err))
+ return err;
+
+ srv_stat_to_net_stat(out, &hoststat);
+ return ERROR_NULL;
+}
+
+static uint32_t_or_error stat_and_encode(struct srv_req *ctx, lo_interface lib9p_srv_file file, void *out, size_t out_len) {
+ struct lib9p_srv_stat hoststat;
+ struct lib9p_stat netstat;
+ error err;
+
+ err = LO_CALL(file, stat, ctx, &hoststat);
+
+ if (!ERROR_IS_NULL(err))
+ return ERROR_NEW_ERR(uint32_t, err);
+ srv_stat_to_net_stat(&netstat, &hoststat);
+ return ERROR_NEW_VAL(uint32_t, lib9p_stat_marshal(&ctx->basectx, out_len, &netstat, (uint8_t *)out));
+}
+
static void handle_read_dir(struct srv_req *ctx, struct srv_fidinfo *fidinfo, uint64_t offset, uint32_t count) {
/* Seek. */
if (offset == 0) {
@@ -1283,10 +1353,10 @@ static void handle_read_dir(struct srv_req *ctx, struct srv_fidinfo *fidinfo, ui
}
/* Allocate. */
- [[gnu::cleanup(heap_cleanup)]] void *heap = NULL;
+ [[gnu::cleanup(heap_cleanup)]] char *heap = NULL;
struct lib9p_msg_Rread resp = {
.tag = ctx->tag,
- .data = heap = malloc(count),
+ .data = heap = heap_alloc(count, char),
.count = 0,
};
@@ -1342,28 +1412,20 @@ static void handle_read_dir(struct srv_req *ctx, struct srv_fidinfo *fidinfo, ui
}
/* 3. Call ->stat() to get `member_stat``. */
- struct lib9p_srv_stat member_stat;
- {
- lib9p_srv_stat_or_error r = LO_CALL(member_file, stat, ctx);
- if (free_member_file)
- LO_CALL(member_file, free);
- if (r.is_err) {
- if (resp.count) {
- /* Just do a short-read; discard the error. */
- error_cleanup(&r.err);
- break;
- }
- srv_respond(ctx, read, NULL, r.err);
- return;
+ /* 4. Encode `member_stat` into `resp.data`/`heap` and increment `resp.count`. */
+ uint32_t_or_error r = stat_and_encode(ctx, member_file, &heap[resp.count], count - resp.count);
+ if (free_member_file)
+ LO_CALL(member_file, free);
+ if (r.is_err) {
+ if (resp.count) {
+ /* Just do a short-read; discard the error. */
+ error_cleanup(&r.err);
+ break;
}
- member_stat = r.lib9p_srv_stat;
- lib9p_srv_stat_assert(&member_stat);
+ srv_respond(ctx, read, NULL, r.err);
+ return;
}
-
- /* 4. Encode `member_stat` into `resp.data` and increment `resp.count`. */
- struct lib9p_stat member_netstat = srv_stat_to_net_stat(&member_stat);
- uint32_t nbytes = lib9p_stat_marshal(&ctx->basectx, count-resp.count, &member_netstat,
- &((uint8_t *)resp.data)[resp.count]);
+ uint32_t nbytes = r.uint32_t;
if (!nbytes) {
if (resp.count) {
/* Just do a short-read; discard the error.
@@ -1451,13 +1513,12 @@ static void handle_Tremove(struct srv_req *ctx,
}
struct srv_pathinfo *parent = map_load(&ctx->parent_sess->paths, pathinfo->parent_dir);
assert(parent);
- lib9p_srv_stat_or_error r = LO_CALL(parent->file, stat, ctx);
- if (r.is_err) {
- err = r.err;
+ ok_or_error have_perm = srv_file_check_perm(ctx, parent->file, 0b010);
+ if (have_perm.is_err) {
+ err = have_perm.err;
+ remove = false;
goto tremove_main;
- }
- struct lib9p_srv_stat parent_stat = r.lib9p_srv_stat;
- if (!srv_check_perm(ctx, &parent_stat, 0b010)) {
+ } else if (!have_perm.ok) {
err = error_new(E_POSIX_EACCES, "you do not have write permission on the parent directory");
remove = false;
goto tremove_main;
@@ -1482,13 +1543,7 @@ static void handle_Tstat(struct srv_req *ctx,
assert(pathinfo);
ctx->user = srv_userid_incref(fidinfo->user);
- lib9p_srv_stat_or_error r = LO_CALL(pathinfo->file, stat, ctx);
- if (r.is_err) {
- err = r.err;
- goto tstat_return;
- }
- lib9p_srv_stat_assert(&r.lib9p_srv_stat);
- resp.stat = srv_stat_to_net_stat(&r.lib9p_srv_stat);
+ err = stat_and_convert(&resp.stat, ctx, pathinfo->file);
tstat_return:
if (ctx->user)
ctx->user = srv_userid_decref(ctx->user);
diff --git a/lib9p/srv_include/lib9p/srv.h b/lib9p/srv_include/lib9p/srv.h
index f9d240e..cea1d79 100644
--- a/lib9p/srv_include/lib9p/srv.h
+++ b/lib9p/srv_include/lib9p/srv.h
@@ -91,9 +91,12 @@ struct lib9p_srv_stat {
struct lib9p_s extension;
#endif
};
-DECLARE_ERROR_OR_(struct lib9p_srv_stat, lib9p_srv_stat);
+#ifdef NDEBUG
+#define lib9p_srv_stat_assert(stat) ((void)0)
+#else
void lib9p_srv_stat_assert(const struct lib9p_srv_stat *stat);
+#endif
/* interface definitions ******************************************************/
@@ -178,7 +181,8 @@ DECLARE_ERROR_OR_(lo_interface lib9p_srv_file, lib9p_srv_file);
/* non-"opened" generic I/O *****************************************/ \
\
/** Strings returned from stat() must remain valid until free(). */ \
- LO_FUNC(lib9p_srv_stat_or_error , stat , struct lib9p_srv_ctx *) \
+ LO_FUNC(error , stat , struct lib9p_srv_ctx *, \
+ struct lib9p_srv_stat * ret) \
LO_FUNC(error , wstat , struct lib9p_srv_ctx *, \
struct lib9p_srv_stat) \
LO_FUNC(error , remove , struct lib9p_srv_ctx *) \
diff --git a/lib9p/tests/test_server/fs_flush.c b/lib9p/tests/test_server/fs_flush.c
index e7f3ef1..c8152d4 100644
--- a/lib9p/tests/test_server/fs_flush.c
+++ b/lib9p/tests/test_server/fs_flush.c
@@ -30,10 +30,12 @@ struct lib9p_qid flush_file_qid(struct flush_file *self) {
};
}
-lib9p_srv_stat_or_error flush_file_stat(struct flush_file *self, struct lib9p_srv_ctx *ctx) {
+error flush_file_stat(struct flush_file *self, struct lib9p_srv_ctx *ctx, struct lib9p_srv_stat *out) {
assert(self);
assert(ctx);
- return ERROR_NEW_VAL(lib9p_srv_stat, ((struct lib9p_srv_stat){
+ assert(out);
+
+ *out = ((struct lib9p_srv_stat){
.qid = flush_file_qid(self),
.mode = 0444,
.atime_sec = UTIL9P_ATIME,
@@ -44,7 +46,8 @@ lib9p_srv_stat_or_error flush_file_stat(struct flush_file *self, struct lib9p_sr
.owner_gid = { .name = lib9p_str("root"), .num = 0 },
.last_modifier_uid = { .name = lib9p_str("root"), .num = 0 },
.extension = lib9p_str(NULL),
- }));
+ });
+ return ERROR_NULL;
}
error flush_file_wstat(struct flush_file *self, struct lib9p_srv_ctx *ctx, struct lib9p_srv_stat) {
assert(self);
diff --git a/lib9p/tests/test_server/fs_shutdown.c b/lib9p/tests/test_server/fs_shutdown.c
index d5f2b0d..53a243b 100644
--- a/lib9p/tests/test_server/fs_shutdown.c
+++ b/lib9p/tests/test_server/fs_shutdown.c
@@ -29,10 +29,12 @@ struct lib9p_qid shutdown_file_qid(struct shutdown_file *self) {
};
}
-lib9p_srv_stat_or_error shutdown_file_stat(struct shutdown_file *self, struct lib9p_srv_ctx *ctx) {
+error shutdown_file_stat(struct shutdown_file *self, struct lib9p_srv_ctx *ctx, struct lib9p_srv_stat *out) {
assert(self);
assert(ctx);
- return ERROR_NEW_VAL(lib9p_srv_stat, ((struct lib9p_srv_stat){
+ assert(out);
+
+ *out = ((struct lib9p_srv_stat){
.qid = shutdown_file_qid(self),
.mode = 0222 | LIB9P_DM_APPEND,
.atime_sec = UTIL9P_ATIME,
@@ -43,7 +45,8 @@ lib9p_srv_stat_or_error shutdown_file_stat(struct shutdown_file *self, struct li
.owner_gid = { .name=lib9p_str("root"), .num=0 },
.last_modifier_uid = { .name=lib9p_str("root"), .num=0 },
.extension = lib9p_str(NULL),
- }));
+ });
+ return ERROR_NULL;
}
error shutdown_file_wstat(struct shutdown_file *self, struct lib9p_srv_ctx *ctx, struct lib9p_srv_stat) {
assert(self);
diff --git a/lib9p/tests/test_server/fs_whoami.c b/lib9p/tests/test_server/fs_whoami.c
index 48bd433..a07fdba 100644
--- a/lib9p/tests/test_server/fs_whoami.c
+++ b/lib9p/tests/test_server/fs_whoami.c
@@ -52,11 +52,12 @@ struct lib9p_qid whoami_file_qid(struct whoami_file *self) {
};
}
-lib9p_srv_stat_or_error whoami_file_stat(struct whoami_file *self, struct lib9p_srv_ctx *ctx) {
+error whoami_file_stat(struct whoami_file *self, struct lib9p_srv_ctx *ctx, struct lib9p_srv_stat *out) {
assert(self);
assert(ctx);
+ assert(out);
- return ERROR_NEW_VAL(lib9p_srv_stat, ((struct lib9p_srv_stat){
+ *out = ((struct lib9p_srv_stat){
.qid = whoami_file_qid(self),
.mode = 0444,
.atime_sec = UTIL9P_ATIME,
@@ -67,7 +68,8 @@ lib9p_srv_stat_or_error whoami_file_stat(struct whoami_file *self, struct lib9p_
.owner_gid = { .name=lib9p_str("root"), .num=0 },
.last_modifier_uid = { .name=lib9p_str("root"), .num=0 },
.extension = lib9p_str(NULL),
- }));
+ });
+ return ERROR_NULL;
}
error whoami_file_wstat(struct whoami_file *self, struct lib9p_srv_ctx *ctx, struct lib9p_srv_stat) {
assert(self);