diff options
author | Luke T. Shumaker <lukeshu@lukeshu.com> | 2025-04-19 12:53:51 -0600 |
---|---|---|
committer | Luke T. Shumaker <lukeshu@lukeshu.com> | 2025-04-19 12:53:51 -0600 |
commit | 59e1fc370371c8da876d6c2ce68794bb7f89e525 (patch) | |
tree | 8037e1493bfebe72cb6023f5eccb62cff0c63349 /lib9p/srv.c | |
parent | 0cf05b66f94bfe02ecca37a4cbafba25b27c32ae (diff) | |
parent | 7f34046f5b27617b70c1d55f479c2a1abb8b8d8a (diff) |
Merge branch 'lukeshu/9p-tidy'
Diffstat (limited to 'lib9p/srv.c')
-rw-r--r-- | lib9p/srv.c | 626 |
1 files changed, 381 insertions, 245 deletions
diff --git a/lib9p/srv.c b/lib9p/srv.c index 6ab2ab2..08ccfc3 100644 --- a/lib9p/srv.c +++ b/lib9p/srv.c @@ -92,11 +92,12 @@ struct srv_pathinfo { #define FIDFLAG_OPEN_R (1<<0) #define FIDFLAG_OPEN_W (1<<1) #define FIDFLAG_RCLOSE (1<<2) +#define FIDFLAG_APPEND (1<<3) #define FIDFLAG_OPEN (FIDFLAG_OPEN_R|FIDFLAG_OPEN_W) struct srv_fidinfo { srv_path_t path; - struct lib9p_srv_authinfo *authinfo; + struct lib9p_srv_userid *user; uint8_t flags; enum srv_filetype type; union { @@ -107,6 +108,7 @@ struct srv_fidinfo { lo_interface lib9p_srv_dio io; size_t idx; uint64_t off; + struct lib9p_srv_dirent buffered_dirent; } dir; struct { struct lib9p_s aname; @@ -163,44 +165,52 @@ static inline enum srv_filetype srv_qid_filetype(struct lib9p_qid qid) { return SRV_FILETYPE_FILE; } -static inline bool srv_check_perm(struct srv_req *ctx, struct lib9p_stat *stat, uint8_t action) { +static inline bool srv_check_perm(struct srv_req *ctx, struct lib9p_srv_stat *stat, uint8_t action) { assert(ctx); assert(stat); assert(action); /* TODO actually check user and group instead of just assuming "other". */ - uint8_t mode = (uint8_t)(stat->file_mode & 07); + uint8_t mode = (uint8_t)(stat->mode & 07); return mode & action; } -struct lib9p_srv_authinfo *srv_authinfo_new(struct lib9p_s uname, lib9p_nuid_t uid) { - struct lib9p_srv_authinfo *ret = malloc(sizeof(struct lib9p_srv_authinfo) + uname.len); +[[gnu::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; - ret->uid = uid; - ret->uname.len = uname.len; - ret->uname.utf8 = (void *)&ret[1]; - memcpy(ret->uname.utf8, uname.utf8, uname.len); +#if CONFIG_9P_ENABLE_9P2000_u || CONFIG_9P_ENABLE_9P2000_L + ret->num = num; +#endif + ret->name.len = name.len; + ret->name.utf8 = (void *)&ret[1]; + memcpy(ret->name.utf8, name.utf8, name.len); ret->refcount = 1; return ret; } +#if !(CONFIG_9P_ENABLE_9P2000_u || CONFIG_9P_ENABLE_9P2000_L) +#define srv_userid_new(name, num) srv_userid_new(name) +#endif -struct lib9p_srv_authinfo *srv_authinfo_decref(struct lib9p_srv_authinfo *authinfo) { - assert(authinfo); - assert(authinfo->refcount); - authinfo->refcount--; - if (!authinfo->refcount) { - free(authinfo); - return NULL; - } - return authinfo; +static struct lib9p_srv_userid *srv_userid_decref(struct lib9p_srv_userid *userid) { + assert(userid); + assert(userid->refcount); + userid->refcount--; + if (!userid->refcount) + free(userid); + return NULL; } -struct lib9p_srv_authinfo *srv_authinfo_incref(struct lib9p_srv_authinfo *authinfo) { - assert(authinfo); - authinfo->refcount++; - return authinfo; +static struct lib9p_srv_userid *srv_userid_incref(struct lib9p_srv_userid *userid) { + assert(userid); + userid->refcount++; + return userid; } /** @@ -262,32 +272,21 @@ static inline void srv_path_decref(struct srv_req *ctx, srv_path_t path) { } } -static inline void srv_fid_del(struct srv_req *ctx, lib9p_fid_t fid, bool remove) { - struct srv_fidinfo *fidinfo = map_load(&ctx->parent_sess->fids, fid); +static inline void srv_fid_del(struct srv_req *ctx, lib9p_fid_t fid, struct srv_fidinfo *fidinfo, bool remove) { + assert(ctx); + assert(!ctx->user); assert(fidinfo); + if (fidinfo->flags & FIDFLAG_RCLOSE) remove = true; + ctx->user = srv_userid_incref(fidinfo->user); + struct srv_pathinfo *pathinfo = map_load(&ctx->parent_sess->paths, fidinfo->path); assert(pathinfo); - if (remove) { - if (pathinfo->parent_dir == fidinfo->path) { - lib9p_errorf(&ctx->basectx, - LIB9P_ERRNO_L_EBUSY, "cannot remove root"); - goto clunk; - } - struct srv_pathinfo *parent = map_load(&ctx->parent_sess->paths, pathinfo->parent_dir); - assert(parent); - struct lib9p_stat parent_stat = LO_CALL(parent->file, stat, ctx); - if (!srv_check_perm(ctx, &parent_stat, 0b010)) { - lib9p_error(&ctx->basectx, - LIB9P_ERRNO_L_EACCES, "you do not have write permission on the parent directory"); - goto clunk; - } + if (remove) LO_CALL(pathinfo->file, remove, ctx); - } - clunk: if (fidinfo->flags & FIDFLAG_OPEN) { switch (fidinfo->type) { case SRV_FILETYPE_DIR: @@ -302,9 +301,11 @@ static inline void srv_fid_del(struct srv_req *ctx, lib9p_fid_t fid, bool remove } pathinfo->io_refcount--; } - fidinfo->authinfo = srv_authinfo_decref(fidinfo->authinfo); + fidinfo->user = srv_userid_decref(fidinfo->user); srv_path_decref(ctx, fidinfo->path); map_del(&ctx->parent_sess->fids, fid); + + ctx->user = srv_userid_decref(ctx->user); } /** @@ -329,7 +330,7 @@ static inline struct srv_fidinfo *srv_fid_store(struct srv_req *ctx, lib9p_fid_t assert(old_fidinfo->type == SRV_FILETYPE_DIR); assert(old_fidinfo->flags == 0); - old_fidinfo->authinfo = srv_authinfo_decref(old_fidinfo->authinfo); + old_fidinfo->user = srv_userid_decref(old_fidinfo->user); srv_path_decref(ctx, old_fidinfo->path); map_del(&ctx->parent_sess->fids, fid); } else { @@ -339,9 +340,9 @@ static inline struct srv_fidinfo *srv_fid_store(struct srv_req *ctx, lib9p_fid_t } } struct srv_fidinfo *fidinfo = map_store(&ctx->parent_sess->fids, fid, (struct srv_fidinfo){ - .path = qid.path, - .type = srv_qid_filetype(qid), - .authinfo = srv_authinfo_incref(ctx->authinfo), + .path = qid.path, + .type = srv_qid_filetype(qid), + .user = srv_userid_incref(ctx->user), }); assert(fidinfo); return fidinfo; @@ -375,7 +376,7 @@ static ssize_t srv_write_Rmsg(struct srv_req *req, struct lib9p_Rmsg_send_buf *r #define srv_nonrespond_errorf errorf static void srv_respond_error(struct srv_req *req) { -#if CONFIG_9P_ENABLE_9P2000_u +#if CONFIG_9P_ENABLE_9P2000_u || CONFIG_9P_ENABLE_9P2000_L assert(req->basectx.err_num); #endif assert(req->basectx.err_msg[0]); @@ -533,17 +534,19 @@ void lib9p_srv_read(struct lib9p_srv *srv, lo_interface net_stream_conn _conn) { assert(map_len(&sess.reqs) == 0); map_free(&sess.reqs); + struct srv_req pseudoreq = { + .basectx = { + .version = sess.version, + .max_msg_size = sess.max_msg_size, + }, + .parent_sess = &sess, + }; MAP_FOREACH(&sess.fids, fid, fidinfo) { - struct srv_req req = { - .basectx = { - .version = sess.version, - .max_msg_size = sess.max_msg_size, - }, - .parent_sess = &sess, - }; - srv_fid_del(&req, fid, false); - if (lib9p_ctx_has_error(&req.basectx)) - errorf("clunk: %.*s", CONFIG_9P_MAX_ERR_SIZE, req.basectx.err_msg); + srv_fid_del(&pseudoreq, fid, fidinfo, false); + if (lib9p_ctx_has_error(&pseudoreq.basectx)) { + srv_nonrespond_errorf("clunk: %.*s", CONFIG_9P_MAX_ERR_SIZE, pseudoreq.basectx.err_msg); + lib9p_ctx_clear_error(&pseudoreq.basectx); + } } map_free(&sess.fids); @@ -587,9 +590,9 @@ void lib9p_srv_worker_loop(struct lib9p_srv *srv) { #define _HANDLER_PROTO(typ) \ static void handle_T##typ(struct srv_req *, \ - struct lib9p_msg_T##typ *, \ - struct lib9p_msg_R##typ *) + struct lib9p_msg_T##typ *) _HANDLER_PROTO(version); +#if _LIB9P_ENABLE_stat _HANDLER_PROTO(auth); _HANDLER_PROTO(attach); _HANDLER_PROTO(flush); @@ -602,6 +605,7 @@ _HANDLER_PROTO(clunk); _HANDLER_PROTO(remove); _HANDLER_PROTO(stat); _HANDLER_PROTO(wstat); +#endif #if CONFIG_9P_ENABLE_9P2000_p9p _HANDLER_PROTO(openfd); #endif @@ -611,16 +615,17 @@ _HANDLER_PROTO(sread); _HANDLER_PROTO(swrite); #endif -typedef void (*tmessage_handler)(struct srv_req *, void *, void *); +typedef void (*tmessage_handler)(struct srv_req *, void *); void lib9p_srv_worker(struct srv_req *ctx) { uint8_t *host_req = NULL; - uint8_t host_resp[CONFIG_9P_SRV_MAX_HOSTMSG_SIZE]; /* Unmarshal it. *****************************************************/ ssize_t host_size = lib9p_Tmsg_validate(&ctx->basectx, ctx->net_bytes); - if (host_size < 0) - goto write; + if (host_size < 0) { + srv_respond_error(ctx); + goto release; + } host_req = calloc(1, host_size); assert(host_req); enum lib9p_msg_type typ; @@ -634,6 +639,7 @@ void lib9p_srv_worker(struct srv_req *ctx) { #pragma GCC diagnostic ignored "-Wswitch-enum" switch (typ) { case LIB9P_TYP_Tversion: handler = (tmessage_handler)handle_Tversion; break; +#if _LIB9P_ENABLE_stat case LIB9P_TYP_Tauth: handler = (tmessage_handler)handle_Tauth; break; case LIB9P_TYP_Tattach: handler = (tmessage_handler)handle_Tattach; break; case LIB9P_TYP_Tflush: handler = (tmessage_handler)handle_Tflush; break; @@ -646,8 +652,9 @@ void lib9p_srv_worker(struct srv_req *ctx) { case LIB9P_TYP_Tremove: handler = (tmessage_handler)handle_Tremove; break; case LIB9P_TYP_Tstat: handler = (tmessage_handler)handle_Tstat; break; case LIB9P_TYP_Twstat: handler = (tmessage_handler)handle_Twstat; break; +#endif #if CONFIG_9P_ENABLE_9P2000_p9p - case LIB9P_TYP_Topenfd: handler = (tmessage_handler)handle_Topenfd; break + case LIB9P_TYP_Topenfd: handler = (tmessage_handler)handle_Topenfd; break; #endif #if CONFIG_9P_ENABLE_9P2000_e case LIB9P_TYP_Tsession: handler = (tmessage_handler)handle_Tsession; break; @@ -658,24 +665,11 @@ void lib9p_srv_worker(struct srv_req *ctx) { assert_notreached("lib9p_Tmsg_validate() should have rejected unknown typ"); } #pragma GCC diagnostic pop - handler(ctx, (void *)host_req, (void *)host_resp); + handler(ctx, (void *)host_req); + assert(ctx->responded); - /* Write the response. ***********************************************/ - write: - if (lib9p_ctx_has_error(&ctx->basectx)) { - srv_respond_error(ctx); - } else if (ctx->flush_acknowledged) { - /* do nothing */ - } else { - struct lib9p_Rmsg_send_buf net_resp; - if (lib9p_Rmsg_marshal(&ctx->basectx, - typ+1, host_resp, - &net_resp)) - goto write; - srv_msglog(ctx, typ+1, &host_resp); - srv_write_Rmsg(ctx, &net_resp); - } /* Release resources. ************************************************/ + release: map_del(&ctx->parent_sess->reqs, ctx->tag); size_t nwaiters; while ((nwaiters = cr_chan_num_waiters(&ctx->flush_ch))) { @@ -690,19 +684,41 @@ void lib9p_srv_worker(struct srv_req *ctx) { free(ctx->net_bytes); } +static inline void _srv_respond(struct srv_req *ctx, enum lib9p_msg_type resp_typ, void *host_resp) { + assert(!ctx->responded); + if (lib9p_ctx_has_error(&ctx->basectx)) { + error: + srv_respond_error(ctx); + } else if (ctx->flush_acknowledged) { + /* do nothing */ + } else { + assert(host_resp); + struct lib9p_Rmsg_send_buf net_resp; + if (lib9p_Rmsg_marshal(&ctx->basectx, + resp_typ, host_resp, + &net_resp)) + goto error; + srv_msglog(ctx, resp_typ, host_resp); + srv_write_Rmsg(ctx, &net_resp); + } + ctx->responded = true; +} +#define srv_respond(CTX, TYP, HOST_RESP) do { \ + struct lib9p_msg_R##TYP *_host_resp = HOST_RESP; \ + _srv_respond(CTX, LIB9P_TYP_R##TYP, _host_resp); \ +} while (0) + /* handle_T* ******************************************************************/ -#define srv_handler_common(ctx, req, resp) do { \ - assert(ctx); \ - assert(req); \ - assert(resp); \ - resp->tag = req->tag; \ - } while (0) +#define srv_handler_common(ctx, typ, req) \ + assert(ctx); \ + assert(req); \ + struct lib9p_msg_T##typ *_typecheck_req [[gnu::unused]] = req; \ + struct lib9p_msg_R##typ resp = { .tag = ctx->tag } static void handle_Tversion(struct srv_req *ctx, - struct lib9p_msg_Tversion *req, - struct lib9p_msg_Rversion *resp) { - srv_handler_common(ctx, req, resp); + struct lib9p_msg_Tversion *req) { + srv_handler_common(ctx, version, req); enum lib9p_version version = LIB9P_VER_unknown; @@ -714,7 +730,9 @@ static void handle_Tversion(struct srv_req *ctx, '0' <= req->version.utf8[4] && req->version.utf8[4] <= '9' && '0' <= req->version.utf8[5] && req->version.utf8[5] <= '9' && (req->version.len == 6 || req->version.utf8[6] == '.')) { +#if CONFIG_9P_ENABLE_9P2000 version = LIB9P_VER_9P2000; +#endif #if CONFIG_9P_ENABLE_9P2000_p9p struct lib9p_srv *srv = ctx->parent_sess->parent_conn->parent_srv; if (srv->type_assert_unix && !LO_IS_NULL(srv->type_assert_unix(ctx->parent_sess->parent_conn->fd))) @@ -735,15 +753,15 @@ static void handle_Tversion(struct srv_req *ctx, lib9p_errorf(&ctx->basectx, LIB9P_ERRNO_L_EDOM, "requested max_msg_size is less than minimum for %s (%"PRIu32" < %"PRIu32")", lib9p_version_str(version), req->max_msg_size, min_msg_size); - return; + goto tversion_return; } - resp->version = lib9p_str((char *)lib9p_version_str(version)); /* cast to discard "const" qualifier */ + resp.version = lib9p_str((char *)lib9p_version_str(version)); /* cast to discard "const" qualifier */ #if CONFIG_9P_ENABLE_9P2000_p9p if (version == LIB9P_VER_9P2000_p9p) - resp->version = lib9p_str("9P2000"); + resp.version = lib9p_str("9P2000"); #endif - resp->max_msg_size = (CONFIG_9P_SRV_MAX_MSG_SIZE < req->max_msg_size) + resp.max_msg_size = (CONFIG_9P_SRV_MAX_MSG_SIZE < req->max_msg_size) ? CONFIG_9P_SRV_MAX_MSG_SIZE : req->max_msg_size; @@ -762,33 +780,36 @@ static void handle_Tversion(struct srv_req *ctx, cr_select_v(i, args); } } - if (map_len(&ctx->parent_sess->fids)) { - /* Close all FIDs. */ - MAP_FOREACH(&ctx->parent_sess->fids, fid, fidinfo) { - handle_Tclunk(ctx, - &(struct lib9p_msg_Tclunk){.fid = fid}, - &(struct lib9p_msg_Rclunk){}); + /* Close all FIDs. */ + MAP_FOREACH(&ctx->parent_sess->fids, fid, fidinfo) { + srv_fid_del(ctx, fid, fidinfo, false); + if (lib9p_ctx_has_error(&ctx->basectx)) { + srv_nonrespond_errorf("clunk: %.*s", CONFIG_9P_MAX_ERR_SIZE, ctx->basectx.err_msg); + lib9p_ctx_clear_error(&ctx->basectx); } } /* Replace the old session with the new session. */ ctx->parent_sess->version = version; - ctx->parent_sess->max_msg_size = resp->max_msg_size; + ctx->parent_sess->max_msg_size = resp.max_msg_size; + + tversion_return: + srv_respond(ctx, version, &resp); } +#if _LIB9P_ENABLE_stat static void handle_Tauth(struct srv_req *ctx, - struct lib9p_msg_Tauth *req, - struct lib9p_msg_Rauth *resp) { - srv_handler_common(ctx, req, resp); + struct lib9p_msg_Tauth *req) { + srv_handler_common(ctx, auth, req); struct lib9p_srv *srv = ctx->parent_sess->parent_conn->parent_srv; if (!srv->auth) { lib9p_error(&ctx->basectx, LIB9P_ERRNO_L_EOPNOTSUPP, "authentication not required"); - return; + goto tauth_return; } - ctx->authinfo = srv_authinfo_new(req->uname, req->n_uid); + ctx->user = srv_userid_new(req->uname, req->unum); srv->auth(ctx, req->aname); @@ -796,18 +817,20 @@ static void handle_Tauth(struct srv_req *ctx, LIB9P_ERRNO_L_EOPNOTSUPP, "TODO: auth not implemented"); if (lib9p_ctx_has_error(&ctx->basectx)) - ctx->authinfo = srv_authinfo_decref(ctx->authinfo); + ctx->user = srv_userid_decref(ctx->user); + + tauth_return: + srv_respond(ctx, auth, &resp); } static void handle_Tattach(struct srv_req *ctx, - struct lib9p_msg_Tattach *req, - struct lib9p_msg_Rattach *resp) { - srv_handler_common(ctx, req, resp); + struct lib9p_msg_Tattach *req) { + srv_handler_common(ctx, attach, req); if (req->fid == LIB9P_FID_NOFID) { lib9p_error(&ctx->basectx, LIB9P_ERRNO_L_EBADF, "cannot assign to NOFID"); - return; + goto tattach_return; } struct lib9p_srv *srv = ctx->parent_sess->parent_conn->parent_srv; @@ -819,16 +842,16 @@ static void handle_Tattach(struct srv_req *ctx, else if (afid->type != SRV_FILETYPE_AUTH) lib9p_error(&ctx->basectx, LIB9P_ERRNO_L_EACCES, "FID provided as auth-file is not an auth-file"); - else if (!lib9p_str_eq(afid->authinfo->uname, req->uname)) + else if (!lib9p_str_eq(afid->user->name, req->uname)) lib9p_errorf(&ctx->basectx, LIB9P_ERRNO_L_EACCES, "FID provided as auth-file is for user=\"%.*s\" and cannot be used for user=\"%.*s\"", - afid->authinfo->uname.len, afid->authinfo->uname.utf8, + afid->user->name.len, afid->user->name.utf8, req->uname.len, req->uname.utf8); -#if CONFIG_9P_ENABLE_9P2000_u - else if (afid->authinfo->uid != req->n_uid) +#if CONFIG_9P_ENABLE_9P2000_u || CONFIG_9P_ENABLE_9P2000_L + else if (afid->user->num != req->unum) lib9p_errorf(&ctx->basectx, LIB9P_ERRNO_L_EACCES, "FID provided as auth-file is for user=%"PRIu32" and cannot be used for user=%"PRIu32, - afid->authinfo->uid, req->n_uid); + afid->user->num, req->unum); #endif else if (!lib9p_str_eq(afid->auth.aname, req->aname)) lib9p_errorf(&ctx->basectx, @@ -839,24 +862,22 @@ static void handle_Tattach(struct srv_req *ctx, lib9p_error(&ctx->basectx, LIB9P_ERRNO_L_EACCES, "FID provided as auth-file has not completed authentication"); if (lib9p_ctx_has_error(&ctx->basectx)) - return; - ctx->authinfo = srv_authinfo_incref(afid->authinfo); + goto tattach_return; + ctx->user = srv_userid_incref(afid->user); } else { if (req->afid != LIB9P_FID_NOFID) { lib9p_error(&ctx->basectx, LIB9P_ERRNO_L_EACCES, "FID provided as auth-file, but no auth-file is required"); - return; + goto tattach_return; } - ctx->authinfo = srv_authinfo_new(req->uname, req->n_uid); + ctx->user = srv_userid_new(req->uname, req->unum); } /* 1. File object */ lo_interface lib9p_srv_file root_file = srv->rootdir(ctx, req->aname); assert(LO_IS_NULL(root_file) == lib9p_ctx_has_error(&ctx->basectx)); - if (lib9p_ctx_has_error(&ctx->basectx)) { - ctx->authinfo = srv_authinfo_decref(ctx->authinfo); - return; - } + if (lib9p_ctx_has_error(&ctx->basectx)) + goto tattach_return; struct lib9p_qid root_qid = LO_CALL(root_file, qid); assert(srv_qid_filetype(root_qid) == SRV_FILETYPE_DIR); @@ -867,19 +888,19 @@ static void handle_Tattach(struct srv_req *ctx, /* 3. fidinfo */ if (!srv_fid_store(ctx, req->fid, root_pathinfo, false)) { srv_path_decref(ctx, root_qid.path); - ctx->authinfo = srv_authinfo_decref(ctx->authinfo); - return; + goto tattach_return; } - ctx->authinfo = srv_authinfo_decref(ctx->authinfo); - resp->qid = root_qid; - return; + resp.qid = root_qid; + tattach_return: + if (ctx->user) + ctx->user = srv_userid_decref(ctx->user); + srv_respond(ctx, attach, &resp); } static void handle_Tflush(struct srv_req *ctx, - struct lib9p_msg_Tflush *req, - struct lib9p_msg_Rflush *resp) { - srv_handler_common(ctx, req, resp); + struct lib9p_msg_Tflush *req) { + srv_handler_common(ctx, flush, req); struct srv_req **oldreqp = map_load(&ctx->parent_sess->reqs, req->oldtag); if (oldreqp) { @@ -897,38 +918,39 @@ static void handle_Tflush(struct srv_req *ctx, break; } } + srv_respond(ctx, flush, &resp); } static void handle_Twalk(struct srv_req *ctx, - struct lib9p_msg_Twalk *req, - struct lib9p_msg_Rwalk *resp) { - srv_handler_common(ctx, req, resp); + struct lib9p_msg_Twalk *req) { + srv_handler_common(ctx, walk, req); if (req->newfid == LIB9P_FID_NOFID) { lib9p_error(&ctx->basectx, LIB9P_ERRNO_L_EBADF, "cannot assign to NOFID"); - return; + goto twalk_return; } struct srv_fidinfo *fidinfo = map_load(&ctx->parent_sess->fids, req->fid); if (!fidinfo) { lib9p_errorf(&ctx->basectx, LIB9P_ERRNO_L_EBADF, "bad file number %"PRIu32, req->fid); - return; + goto twalk_return; } if (fidinfo->flags & FIDFLAG_OPEN) { lib9p_error(&ctx->basectx, LIB9P_ERRNO_L_EALREADY, "cannot walk on FID open for I/O"); - return; + goto twalk_return; } - ctx->authinfo = srv_authinfo_incref(fidinfo->authinfo); + ctx->user = srv_userid_incref(fidinfo->user); struct srv_pathinfo *pathinfo = map_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++) { + struct lib9p_qid _resp_qid[16]; + resp.wqid = _resp_qid; + for (resp.nwqid = 0; resp.nwqid < req->nwname; resp.nwqid++) { if (pathinfo->type != SRV_FILETYPE_DIR) { lib9p_error(&ctx->basectx, LIB9P_ERRNO_L_ENOTDIR, "not a directory"); @@ -936,12 +958,12 @@ static void handle_Twalk(struct srv_req *ctx, } struct srv_pathinfo *new_pathinfo; - if (lib9p_str_eq(req->wname[resp->nwqid], lib9p_str(".."))) { + if (lib9p_str_eq(req->wname[resp.nwqid], lib9p_str(".."))) { new_pathinfo = map_load(&ctx->parent_sess->paths, pathinfo->parent_dir); assert(new_pathinfo); new_pathinfo->gc_refcount++; } else { - lo_interface lib9p_srv_file member_file = LO_CALL(pathinfo->file, dwalk, ctx, req->wname[resp->nwqid]); + lo_interface lib9p_srv_file member_file = LO_CALL(pathinfo->file, dwalk, ctx, req->wname[resp.nwqid]); assert(LO_IS_NULL(member_file) == lib9p_ctx_has_error(&ctx->basectx)); if (lib9p_ctx_has_error(&ctx->basectx)) break; @@ -950,10 +972,10 @@ static void handle_Twalk(struct srv_req *ctx, } if (new_pathinfo->type == SRV_FILETYPE_DIR) { - struct lib9p_stat stat = LO_CALL(new_pathinfo->file, stat, ctx); + struct lib9p_srv_stat stat = LO_CALL(new_pathinfo->file, stat, ctx); if (lib9p_ctx_has_error(&ctx->basectx)) break; - lib9p_stat_assert(stat); + lib9p_srv_stat_assert(stat); if (!srv_check_perm(ctx, &stat, 0b001)) { lib9p_error(&ctx->basectx, LIB9P_ERRNO_L_EACCES, "you do not have execute permission on that directory"); @@ -962,39 +984,41 @@ static void handle_Twalk(struct srv_req *ctx, } } - resp->wqid[resp->nwqid] = LO_CALL(new_pathinfo->file, qid); + resp.wqid[resp.nwqid] = LO_CALL(new_pathinfo->file, qid); srv_path_decref(ctx, LO_CALL(pathinfo->file, qid).path); pathinfo = new_pathinfo; } - if (resp->nwqid == req->nwname) { + if (resp.nwqid == req->nwname) { if (!srv_fid_store(ctx, req->newfid, pathinfo, req->newfid == req->fid)) srv_path_decref(ctx, LO_CALL(pathinfo->file, qid).path); } else { assert(lib9p_ctx_has_error(&ctx->basectx)); srv_path_decref(ctx, LO_CALL(pathinfo->file, qid).path); - if (resp->nwqid > 0) + if (resp.nwqid > 0) lib9p_ctx_clear_error(&ctx->basectx); } - ctx->authinfo = srv_authinfo_decref(ctx->authinfo); + twalk_return: + if (ctx->user) + ctx->user = srv_userid_decref(ctx->user); + srv_respond(ctx, walk, &resp); } static void handle_Topen(struct srv_req *ctx, - struct lib9p_msg_Topen *req, - struct lib9p_msg_Ropen *resp) { - srv_handler_common(ctx, req, resp); + struct lib9p_msg_Topen *req) { + srv_handler_common(ctx, open, req); /* Check that the FID is valid for this. */ struct srv_fidinfo *fidinfo = map_load(&ctx->parent_sess->fids, req->fid); if (!fidinfo) { lib9p_errorf(&ctx->basectx, LIB9P_ERRNO_L_EBADF, "bad file number %"PRIu32, req->fid); - return; + goto topen_return; } if (fidinfo->flags & FIDFLAG_OPEN) { lib9p_error(&ctx->basectx, LIB9P_ERRNO_L_EALREADY, "FID is already open"); - return; + goto topen_return; } if (fidinfo->type == SRV_FILETYPE_DIR) { if ( ((req->mode & LIB9P_O_MODE_MASK) != LIB9P_O_MODE_READ) || @@ -1002,10 +1026,10 @@ static void handle_Topen(struct srv_req *ctx, (req->mode & LIB9P_O_RCLOSE) ) { lib9p_error(&ctx->basectx, LIB9P_ERRNO_L_EISDIR, "directories cannot be written, executed, truncated, or removed-on-close"); - return; + goto topen_return; } } - ctx->authinfo = srv_authinfo_incref(fidinfo->authinfo); + ctx->user = srv_userid_incref(fidinfo->user); /* Variables. */ lib9p_o_t reqmode = req->mode; @@ -1017,10 +1041,10 @@ 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); - struct lib9p_stat parent_stat = LO_CALL(parent->file, stat, ctx); + struct lib9p_srv_stat parent_stat = LO_CALL(parent->file, stat, ctx); if (lib9p_ctx_has_error(&ctx->basectx)) goto topen_return; - lib9p_stat_assert(parent_stat); + lib9p_srv_stat_assert(parent_stat); if (!srv_check_perm(ctx, &parent_stat, 0b010)) { lib9p_error(&ctx->basectx, LIB9P_ERRNO_L_EACCES, "permission denied to remove-on-close"); @@ -1028,17 +1052,19 @@ static void handle_Topen(struct srv_req *ctx, } fidflags |= FIDFLAG_RCLOSE; } - struct lib9p_stat stat = LO_CALL(pathinfo->file, stat, ctx); + struct lib9p_srv_stat stat = LO_CALL(pathinfo->file, stat, ctx); if (lib9p_ctx_has_error(&ctx->basectx)) goto topen_return; - lib9p_stat_assert(stat); - if ((stat.file_mode & LIB9P_DM_EXCL) && pathinfo->io_refcount) { + lib9p_srv_stat_assert(stat); + if ((stat.mode & LIB9P_DM_EXCL) && pathinfo->io_refcount) { lib9p_error(&ctx->basectx, LIB9P_ERRNO_L_EEXIST, "exclusive file is already opened"); goto topen_return; } - if (stat.file_mode & LIB9P_DM_APPEND) + if (stat.mode & LIB9P_DM_APPEND) { + fidflags |= FIDFLAG_APPEND; reqmode = reqmode & ~LIB9P_O_TRUNC; + } uint8_t perm_bits = 0; bool rd = false, wr = false; switch (reqmode & LIB9P_O_MODE_MASK) { @@ -1104,25 +1130,48 @@ static void handle_Topen(struct srv_req *ctx, fidflags |= FIDFLAG_OPEN_W; pathinfo->io_refcount++; fidinfo->flags = fidflags; - resp->qid = qid; - resp->iounit = iounit; + resp.qid = qid; + resp.iounit = iounit; topen_return: - ctx->authinfo = srv_authinfo_decref(ctx->authinfo); + if (ctx->user) + ctx->user = srv_userid_decref(ctx->user); + srv_respond(ctx, open, &resp); } static void handle_Tcreate(struct srv_req *ctx, - struct lib9p_msg_Tcreate *req, - struct lib9p_msg_Rcreate *resp) { - srv_handler_common(ctx, req, resp); + struct lib9p_msg_Tcreate *req) { + srv_handler_common(ctx, create, req); lib9p_error(&ctx->basectx, LIB9P_ERRNO_L_EOPNOTSUPP, "create not (yet?) implemented"); + + srv_respond(ctx, create, &resp); +} + +static inline struct lib9p_stat srv_stat_to_net_stat(struct lib9p_srv_stat in) { + return (struct lib9p_stat){ + .qid = in.qid, + .mode = in.mode, + .atime = in.atime_sec, + .mtime = in.mtime_sec, + .length = in.size, + .name = in.name, + .owner_uname = in.owner_uid.name, + .owner_gname = in.owner_gid.name, + .last_modifier_uname = in.last_modifier_uid.name, +#if CONFIG_9P_ENABLE_9P2000_u + .owner_unum = in.owner_uid.num, + .owner_gnum = in.owner_gid.num, + .last_modifier_unum = in.last_modifier_uid.num, + .extension = in.extension, +#endif + }; } static void handle_Tread(struct srv_req *ctx, - struct lib9p_msg_Tread *req, - struct lib9p_msg_Rread *resp) { - srv_handler_common(ctx, req, resp); + struct lib9p_msg_Tread *req) { + srv_handler_common(ctx, read, req); + char *heap = NULL; /* TODO: serialize simultaneous reads to the same FID */ @@ -1131,67 +1180,120 @@ static void handle_Tread(struct srv_req *ctx, if (!fidinfo) { lib9p_errorf(&ctx->basectx, LIB9P_ERRNO_L_EBADF, "bad file number %"PRIu32, req->fid); - return; + goto tread_return; } if (!(fidinfo->flags & FIDFLAG_OPEN_R)) { lib9p_error(&ctx->basectx, LIB9P_ERRNO_L_EINVAL, "FID not open for reading"); - return; + goto tread_return; } /* Do it. */ - ctx->authinfo = srv_authinfo_incref(fidinfo->authinfo); + ctx->user = srv_userid_incref(fidinfo->user); switch (fidinfo->type) { case SRV_FILETYPE_DIR: - /* 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 _LIB9P_ENABLE_stat + /* Seek. */ + if (req->offset == 0) { + fidinfo->dir.idx = 0; + fidinfo->dir.off = 0; + fidinfo->dir.buffered_dirent = (struct lib9p_srv_dirent){}; + } else if (req->offset != fidinfo->dir.off) { lib9p_errorf(&ctx->basectx, LIB9P_ERRNO_L_EINVAL, "invalid offset (must be 0 or %"PRIu64"): %"PRIu64, fidinfo->dir.off, req->offset); - ctx->authinfo = srv_authinfo_decref(ctx->authinfo); - return; + goto tread_return; } - /* Do it. */ - resp->data = (char *)(&resp[1]); - size_t num = LO_CALL(fidinfo->dir.io, dread, 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_stat_validate(&ctx->basectx, req->count, &((uint8_t *)resp->data)[len], &i_len, NULL); - len += i_len; + /* Read. */ + resp.data = heap = malloc(req->count); /* TODO: cap req->count */ + resp.count = 0; + struct srv_pathinfo *dir_pathinfo = NULL; + for (;;) { + lo_interface lib9p_srv_file member_file = {}; + struct lib9p_srv_dirent member_dirent; + if (fidinfo->dir.buffered_dirent.name.len) { + member_dirent = fidinfo->dir.buffered_dirent; + } else { + member_dirent = LO_CALL(fidinfo->dir.io, dread, ctx, fidinfo->dir.idx); + if (lib9p_ctx_has_error(&ctx->basectx)) { + if (!resp.count) + goto tread_return; + lib9p_ctx_clear_error(&ctx->basectx); + break; + } + } + if (!member_dirent.name.len) + break; + struct lib9p_srv_stat member_stat; + struct srv_pathinfo *member_pathinfo = map_load(&ctx->parent_sess->paths, member_dirent.qid.path); + if (member_pathinfo) { + member_stat = LO_CALL(member_pathinfo->file, stat, ctx); + } else { + if (!dir_pathinfo) + dir_pathinfo = map_load(&ctx->parent_sess->paths, fidinfo->path); + assert(dir_pathinfo); + member_file = LO_CALL(dir_pathinfo->file, dwalk, ctx, member_dirent.name); + assert(LO_IS_NULL(member_file) == lib9p_ctx_has_error(&ctx->basectx)); + if (!lib9p_ctx_has_error(&ctx->basectx)) + member_stat = LO_CALL(member_file, stat, ctx); + } + if (lib9p_ctx_has_error(&ctx->basectx)) { + if (!LO_IS_NULL(member_file)) + LO_CALL(member_file, free); + if (!resp.count) + goto tread_return; + lib9p_ctx_clear_error(&ctx->basectx); + break; + } + lib9p_srv_stat_assert(member_stat); + struct lib9p_stat member_netstat = srv_stat_to_net_stat(member_stat); + uint32_t nbytes = lib9p_stat_marshal(&ctx->basectx, req->count-resp.count, &member_netstat, + (uint8_t *)&resp.data[resp.count]); + if (!LO_IS_NULL(member_file)) + LO_CALL(member_file, free); + if (!nbytes) { + if (!resp.count) { + lib9p_error(&ctx->basectx, + LIB9P_ERRNO_L_ERANGE, "stat object does not fit into negotiated max message size"); + goto tread_return; + } + fidinfo->dir.buffered_dirent = member_dirent; + break; + } + resp.count += nbytes; + fidinfo->dir.idx++; + fidinfo->dir.off += nbytes; + fidinfo->dir.buffered_dirent = (struct lib9p_srv_dirent){}; } - resp->count = len; - /* Remember. */ - fidinfo->dir.idx = idx+num; - fidinfo->dir.off = req->offset + len; +#else + assert_notreached("Tread for directory on protocol version without that"); +#endif break; case SRV_FILETYPE_FILE: struct iovec iov; LO_CALL(fidinfo->file.io, pread, ctx, req->count, req->offset, &iov); if (!lib9p_ctx_has_error(&ctx->basectx) && !ctx->flush_acknowledged) { - resp->count = iov.iov_len; - resp->data = iov.iov_base; - if (resp->count > req->count) - resp->count = req->count; + resp.count = iov.iov_len; + resp.data = iov.iov_base; + if (resp.count > req->count) + resp.count = req->count; } break; case SRV_FILETYPE_AUTH: assert_notreached("TODO: auth not yet implemented"); break; } - ctx->authinfo = srv_authinfo_decref(ctx->authinfo); + tread_return: + if (ctx->user) + ctx->user = srv_userid_decref(ctx->user); + srv_respond(ctx, read, &resp); + if (heap) + free(heap); } static void handle_Twrite(struct srv_req *ctx, - struct lib9p_msg_Twrite *req, - struct lib9p_msg_Rwrite *resp) { - srv_handler_common(ctx, req, resp); + struct lib9p_msg_Twrite *req) { + srv_handler_common(ctx, write, req); /* TODO: serialize simultaneous writes to the same FID */ @@ -1200,120 +1302,154 @@ static void handle_Twrite(struct srv_req *ctx, if (!fidinfo) { lib9p_errorf(&ctx->basectx, LIB9P_ERRNO_L_EBADF, "bad file number %"PRIu32, req->fid); - return; + goto twrite_return; } if (!(fidinfo->flags & FIDFLAG_OPEN_W)) { lib9p_error(&ctx->basectx, LIB9P_ERRNO_L_EINVAL, "FID not open for writing"); - return; + goto twrite_return; } + if (fidinfo->flags & FIDFLAG_APPEND) + req->offset = 0; /* Do it. */ - ctx->authinfo = srv_authinfo_incref(fidinfo->authinfo); - resp->count = LO_CALL(fidinfo->file.io, pwrite, ctx, req->data, req->count, req->offset); - ctx->authinfo = srv_authinfo_decref(ctx->authinfo); + ctx->user = srv_userid_incref(fidinfo->user); + resp.count = LO_CALL(fidinfo->file.io, pwrite, ctx, req->data, req->count, req->offset); + twrite_return: + if (ctx->user) + ctx->user = srv_userid_decref(ctx->user); + srv_respond(ctx, write, &resp); } static void handle_Tclunk(struct srv_req *ctx, - struct lib9p_msg_Tclunk *req, - struct lib9p_msg_Rclunk *resp) { - srv_handler_common(ctx, req, resp); + struct lib9p_msg_Tclunk *req) { + srv_handler_common(ctx, clunk, req); struct srv_fidinfo *fidinfo = map_load(&ctx->parent_sess->fids, req->fid); if (!fidinfo) { lib9p_errorf(&ctx->basectx, LIB9P_ERRNO_L_EBADF, "bad file number %"PRIu32, req->fid); - return; + goto tclunk_return; } - ctx->authinfo = srv_authinfo_incref(fidinfo->authinfo); - srv_fid_del(ctx, req->fid, false); - ctx->authinfo = srv_authinfo_decref(ctx->authinfo); + srv_fid_del(ctx, req->fid, fidinfo, false); + + tclunk_return: + srv_respond(ctx, clunk, &resp); } static void handle_Tremove(struct srv_req *ctx, - struct lib9p_msg_Tremove *req, - struct lib9p_msg_Rremove *resp) { - srv_handler_common(ctx, req, resp); + struct lib9p_msg_Tremove *req) { + srv_handler_common(ctx, remove, req); struct srv_fidinfo *fidinfo = map_load(&ctx->parent_sess->fids, req->fid); if (!fidinfo) { lib9p_errorf(&ctx->basectx, LIB9P_ERRNO_L_EBADF, "bad file number %"PRIu32, req->fid); - return; + goto tremove_return; } - ctx->authinfo = srv_authinfo_incref(fidinfo->authinfo); - srv_fid_del(ctx, req->fid, true); - ctx->authinfo = srv_authinfo_decref(ctx->authinfo); + bool remove = true; + struct srv_pathinfo *pathinfo = map_load(&ctx->parent_sess->paths, fidinfo->path); + assert(pathinfo); + if (pathinfo->parent_dir == fidinfo->path) { + lib9p_error(&ctx->basectx, + LIB9P_ERRNO_L_EBUSY, "cannot remove root"); + remove = false; + goto tremove_main; + } + struct srv_pathinfo *parent = map_load(&ctx->parent_sess->paths, pathinfo->parent_dir); + assert(parent); + struct lib9p_srv_stat parent_stat = LO_CALL(parent->file, stat, ctx); + if (!lib9p_ctx_has_error(&ctx->basectx) && !srv_check_perm(ctx, &parent_stat, 0b010)) { + lib9p_error(&ctx->basectx, + LIB9P_ERRNO_L_EACCES, "you do not have write permission on the parent directory"); + remove = false; + goto tremove_main; + } + + tremove_main: + srv_fid_del(ctx, req->fid, fidinfo, remove); + tremove_return: + srv_respond(ctx, remove, &resp); } static void handle_Tstat(struct srv_req *ctx, - struct lib9p_msg_Tstat *req, - struct lib9p_msg_Rstat *resp) { - srv_handler_common(ctx, req, resp); + struct lib9p_msg_Tstat *req) { + srv_handler_common(ctx, stat, req); struct srv_fidinfo *fidinfo = map_load(&ctx->parent_sess->fids, req->fid); if (!fidinfo) { lib9p_errorf(&ctx->basectx, LIB9P_ERRNO_L_EBADF, "bad file number %"PRIu32, req->fid); - return; + goto tstat_return; } struct srv_pathinfo *pathinfo = map_load(&ctx->parent_sess->paths, fidinfo->path); assert(pathinfo); - ctx->authinfo = srv_authinfo_incref(fidinfo->authinfo); - resp->stat = LO_CALL(pathinfo->file, stat, ctx); - if (!lib9p_ctx_has_error(&ctx->basectx)) - lib9p_stat_assert(resp->stat); - ctx->authinfo = srv_authinfo_decref(ctx->authinfo); + ctx->user = srv_userid_incref(fidinfo->user); + struct lib9p_srv_stat stat = LO_CALL(pathinfo->file, stat, ctx); + if (lib9p_ctx_has_error(&ctx->basectx)) + goto tstat_return; + lib9p_srv_stat_assert(stat); + resp.stat = srv_stat_to_net_stat(stat); + tstat_return: + if (ctx->user) + ctx->user = srv_userid_decref(ctx->user); + srv_respond(ctx, stat, &resp); } static void handle_Twstat(struct srv_req *ctx, - struct lib9p_msg_Twstat *req, - struct lib9p_msg_Rwstat *resp) { - srv_handler_common(ctx, req, resp); + struct lib9p_msg_Twstat *req) { + srv_handler_common(ctx, wstat, req); lib9p_error(&ctx->basectx, LIB9P_ERRNO_L_EOPNOTSUPP, "wstat not (yet?) implemented"); + + srv_respond(ctx, wstat, &resp); } +#endif #if CONFIG_9P_ENABLE_9P2000_p9p static void handle_Topenfd(struct srv_req *ctx, - struct lib9p_msg_Topenfd *req, - struct lib9p_msg_Ropenfd *resp) { - srv_handler_common(ctx, req, resp); + struct lib9p_msg_Topenfd *req) { + srv_handler_common(ctx, openfd, req); lib9p_error(&ctx->basectx, LIB9P_ERRNO_L_EOPNOTSUPP, "openfd not (yet?) implemented"); + + srv_respond(ctx, openfd, &resp); } #endif #if CONFIG_9P_ENABLE_9P2000_e static void handle_Tsession(struct srv_req *ctx, - struct lib9p_msg_Tsession *req, - struct lib9p_msg_Rsession *resp) { - srv_handler_common(ctx, req, resp); + struct lib9p_msg_Tsession *req) { + srv_handler_common(ctx, session, req); lib9p_error(&ctx->basectx, LIB9P_ERRNO_L_EOPNOTSUPP, "session not (yet?) implemented"); + + srv_respond(ctx, session, &resp); } static void handle_Tsread(struct srv_req *ctx, - struct lib9p_msg_Tsread *req, - struct lib9p_msg_Rsread *resp) { - srv_handler_common(ctx, req, resp); + struct lib9p_msg_Tsread *req) { + srv_handler_common(ctx, sread, req); lib9p_error(&ctx->basectx, LIB9P_ERRNO_L_EOPNOTSUPP, "sread not (yet?) implemented"); + + srv_respond(ctx, sread, &resp); } static void handle_Tswrite(struct srv_req *ctx, - struct lib9p_msg_Tswrite *req, - struct lib9p_msg_Rswrite *resp) { - srv_handler_common(ctx, req, resp); + struct lib9p_msg_Tswrite *req) { + srv_handler_common(ctx, swrite, req); lib9p_error(&ctx->basectx, LIB9P_ERRNO_L_EOPNOTSUPP, "swrite not (yet?) implemented"); + + srv_respond(ctx, swrite, &resp); } #endif |