diff options
author | Luke T. Shumaker <lukeshu@lukeshu.com> | 2025-06-27 08:58:41 -0600 |
---|---|---|
committer | Luke T. Shumaker <lukeshu@lukeshu.com> | 2025-06-27 08:58:41 -0600 |
commit | 5a16ac6589817d20749103f45c8d3b4f1eccf784 (patch) | |
tree | 59148ec2255d14c184c7920baaae9766dc2c5ade /lib9p | |
parent | 90f0c59f227b92a046f296865763dc77351782ed (diff) | |
parent | 112b4940c76e22606033acf437b00ad3edbd3c9c (diff) |
Diffstat (limited to 'lib9p')
-rw-r--r-- | lib9p/srv.c | 341 | ||||
-rw-r--r-- | lib9p/srv_include/lib9p/srv.h | 8 | ||||
-rw-r--r-- | lib9p/tests/test_server/fs_flush.c | 9 | ||||
-rw-r--r-- | lib9p/tests/test_server/fs_shutdown.c | 9 | ||||
-rw-r--r-- | lib9p/tests/test_server/fs_whoami.c | 8 |
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); |