diff options
Diffstat (limited to 'lib9p')
-rw-r--r-- | lib9p/include/lib9p/srv.h | 145 | ||||
-rw-r--r-- | lib9p/srv.c | 122 | ||||
-rw-r--r-- | lib9p/tests/test_server/CMakeLists.txt | 2 | ||||
-rw-r--r-- | lib9p/tests/test_server/main.c | 27 |
4 files changed, 207 insertions, 89 deletions
diff --git a/lib9p/include/lib9p/srv.h b/lib9p/include/lib9p/srv.h index 83aabc0..ff5ebdc 100644 --- a/lib9p/include/lib9p/srv.h +++ b/lib9p/include/lib9p/srv.h @@ -37,53 +37,98 @@ int lib9p_srv_acknowledge_flush(struct lib9p_srv_ctx *ctx); /* interface definitions ******************************************************/ -#define lib9p_srv_file_LO_IFACE \ - /* all - resource management */ \ - LO_FUNC(void , free ) /* must not error */ \ - LO_FUNC(struct lib9p_qid , qid ) /* must not error */ \ - LO_FUNC(uint32_t , chio , struct lib9p_srv_ctx *, \ - bool rd, bool wr, \ - bool trunc) \ - \ - /* all - syscalls */ \ - LO_FUNC(struct lib9p_stat , stat , struct lib9p_srv_ctx *) \ - LO_FUNC(void , wstat , struct lib9p_srv_ctx *, \ - struct lib9p_stat new) \ - LO_FUNC(void , remove , struct lib9p_srv_ctx *) \ - \ - /* directories - base */ \ - LO_FUNC(lo_interface lib9p_srv_file, dopen , struct lib9p_srv_ctx *, \ - struct lib9p_s childname) \ - LO_FUNC(lo_interface lib9p_srv_file, dcreate, struct lib9p_srv_ctx *, \ - struct lib9p_s childname, \ - lib9p_dm_t perm, \ - lib9p_o_t flags) \ - \ - /* directories - once opened */ \ - LO_FUNC(size_t /* <- obj cnt */ , dread , struct lib9p_srv_ctx *, \ - uint8_t *buf, \ - /* num bytes -> */ uint32_t byte_count, \ - /* starting at this object -> */ size_t obj_offset) \ - \ - /* non-directories - once opened */ \ - LO_FUNC(uint32_t , pread , struct lib9p_srv_ctx *, \ - void *buf, \ - uint32_t byte_count, \ - uint64_t byte_offset) \ - LO_FUNC(uint32_t , pwrite , struct lib9p_srv_ctx *, \ - void *buf, \ - uint32_t byte_count, \ +lo_interface lib9p_srv_fio; +lo_interface lib9p_srv_dio; + +/* FIXME: I don't like that the pointers returned by stat() and + * pread() have to remain live after they return. Perhaps a + * `respond()`-callback? But that just reads as gross in C. + * + * FIXME: It would be nice if pread() could return more than 1 iovec. + */ +#define lib9p_srv_file_LO_IFACE \ + /* resource management **********************************************/ \ + \ + /** \ + * free() is be called when all FIDs associated with the file are \ + * clunked. \ + * \ + * free() MUST NOT error. \ + */ \ + LO_FUNC(void , free ) \ + \ + /** \ + * qid() is called frequently and returns the current QID of the file. \ + * The .path field MUST never change, the .type field may change in \ + * response to wstat() calls (but the QT_DIR bit MUST NOT change), and \ + * the .vers field may change frequently in response to any number of \ + * things (wstat(), write(), or non-9P events). \ + * \ + * qid() MUST NOT error. \ + */ \ + LO_FUNC(struct lib9p_qid , qid ) \ + \ + /* non-"opened" generic I/O *****************************************/ \ + \ + LO_FUNC(struct lib9p_stat , stat , struct lib9p_srv_ctx *) \ + LO_FUNC(void , wstat , struct lib9p_srv_ctx *, \ + struct lib9p_stat new) \ + LO_FUNC(void , remove , struct lib9p_srv_ctx *) \ + \ + /* non-"opened" directory I/O ***************************************/ \ + \ + LO_FUNC(lo_interface lib9p_srv_file, dwalk , struct lib9p_srv_ctx *, \ + struct lib9p_s childname) \ + LO_FUNC(lo_interface lib9p_srv_file, dcreate, struct lib9p_srv_ctx *, \ + struct lib9p_s childname, \ + lib9p_dm_t perm, \ + lib9p_o_t flags) \ + \ + /* open() for I/O ***************************************************/ \ + \ + LO_FUNC(lo_interface lib9p_srv_fio , fopen , struct lib9p_srv_ctx *, \ + bool rd, bool wr, \ + bool trunc) \ + LO_FUNC(lo_interface lib9p_srv_dio , dopen , struct lib9p_srv_ctx *) +LO_INTERFACE(lib9p_srv_file); + +#define lib9p_srv_fio_LO_IFACE \ + LO_FUNC(struct lib9p_qid , qid ) \ + LO_FUNC(void , iofree ) \ + LO_FUNC(uint32_t , iounit ) \ + LO_FUNC(void , pread , struct lib9p_srv_ctx *, \ + uint32_t byte_count, \ + uint64_t byte_offset, \ + struct iovec *ret) \ + LO_FUNC(uint32_t , pwrite , struct lib9p_srv_ctx *, \ + void *buf, \ + uint32_t byte_count, \ uint64_t byte_offset) -LO_INTERFACE(lib9p_srv_file) +LO_INTERFACE(lib9p_srv_fio); + +/* FIXME: The dio interface just feels clunky. I'm not in a rush to + * change it because util9p_static_dir is already implemented and I + * don't anticipate the sbc-harness needing another dio + * implementation. But if I wanted lib9p to be used outside of + * sbc-harness, this is one of the first things that I'd want to + * change. + */ +#define lib9p_srv_dio_LO_IFACE \ + LO_FUNC(struct lib9p_qid , qid ) \ + LO_FUNC(void , iofree ) \ + LO_FUNC(size_t /* <- obj cnt */ , dread , struct lib9p_srv_ctx *, \ + uint8_t *buf, \ + /* num bytes -> */ uint32_t byte_count, \ + /* starting at this object -> */ size_t obj_offset) +LO_INTERFACE(lib9p_srv_dio); #define LIB9P_SRV_NOTDIR(TYP, NAM) \ - static lo_interface lib9p_srv_file NAM##_dopen (TYP *, struct lib9p_srv_ctx *, struct lib9p_s) { assert_notreached("not a directory"); } \ + static lo_interface lib9p_srv_file NAM##_dwalk (TYP *, struct lib9p_srv_ctx *, struct lib9p_s) { assert_notreached("not a directory"); } \ static lo_interface lib9p_srv_file NAM##_dcreate(TYP *, struct lib9p_srv_ctx *, struct lib9p_s, lib9p_dm_t, lib9p_o_t) { assert_notreached("not a directory"); } \ - static size_t NAM##_dread (TYP *, struct lib9p_srv_ctx *, uint8_t *, uint32_t, size_t) { assert_notreached("not a directory"); } + static lo_interface lib9p_srv_dio NAM##_dopen (TYP *, struct lib9p_srv_ctx *) { assert_notreached("not a directory"); } #define LIB9P_SRV_NOTFILE(TYP, NAM) \ - static uint32_t NAM##_pread (TYP *, struct lib9p_srv_ctx *, void *, uint32_t, uint64_t) { assert_notreached("not a file"); } \ - static uint32_t NAM##_pwrite(TYP *, struct lib9p_srv_ctx *, void *, uint32_t, uint64_t) { assert_notreached("not a file"); } + static lo_interface lib9p_srv_fio NAM##_fopen (TYP *, struct lib9p_srv_ctx *, bool, bool, bool) { assert_notreached("not a file"); } /* main server entrypoints ****************************************************/ @@ -103,16 +148,32 @@ struct lib9p_srv { }; /** + * In an infinite loop, accept a connection and read messages from it until + * close; dispatching requests to a pool of lib9p_srv_write_cr() coroutines + * with the same `srv`. + * * Will just close the connection if a T-message has a size[4] <7. * + * @param srv: The server configuration and state; has an associated pool of + * lib9p_srv_write_cr() coroutines. + * @param listener: The listener object to accept connections from. + * * @errno LINUX_EMSGSIZE T-message has size[4] bigger than max_msg_size * @errno LINUX_EDOM Tversion specified an impossibly small max_msg_size * @errno LINUX_EOPNOTSUPP T-message has an R-message type, or an unrecognized T-message type * @errno LINUX_EBADMSG T-message has wrong size[4] for its content, or has invalid UTF-8 * @errno LINUX_ERANGE R-message does not fit into max_msg_size */ - [[noreturn]] void lib9p_srv_read_cr(struct lib9p_srv *srv, lo_interface net_stream_listener listener); + +/** + * Service requests to the `struct lib9p_srv *srv` argument that have been + * read by lib9p_srv_read_cr(). + * + * @param struct lib9p_srv *srv: The server configuration and state; has an + * associated pool of lib9p_srv_read_cr() + * coroutines. + */ COROUTINE lib9p_srv_write_cr(void *_srv); #endif /* _LIB9P_SRV_H_ */ diff --git a/lib9p/srv.c b/lib9p/srv.c index 2475baf..50d0b78 100644 --- a/lib9p/srv.c +++ b/lib9p/srv.c @@ -60,8 +60,7 @@ struct srv_pathinfo { * from FIDs. */ unsigned int gc_refcount; /* References from fids with FIDFLAG_OPEN_R/FIDFLAG_OPEN_W. */ - unsigned int rd_refcount; - unsigned int wr_refcount; + unsigned int io_refcount; }; #define NAME pathmap @@ -77,11 +76,18 @@ struct srv_pathinfo { #define FIDFLAG_OPEN (FIDFLAG_OPEN_R|FIDFLAG_OPEN_W) struct _srv_fidinfo { - srv_path_t path; - uint8_t flags; - uint32_t iounit; - size_t dir_idx; - uint64_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 @@ -122,9 +128,9 @@ struct _srv_sess { /* mutable */ bool initialized; bool closing; - struct pathmap paths; - 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 { @@ -463,8 +469,7 @@ static inline struct srv_pathinfo *srv_util_pathsave(struct _lib9p_srv_req *ctx, .file = file, .parent_dir = parent_path, .gc_refcount = 0, - .rd_refcount = 0, - .wr_refcount = 0, + .io_refcount = 0, }); assert(pathinfo); if (parent_path != qid.path) { @@ -515,6 +520,12 @@ static inline struct _srv_fidinfo *srv_util_fidsave(struct _lib9p_srv_req *ctx, 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, @@ -748,7 +759,7 @@ static void handle_Twalk(struct _lib9p_srv_req *ctx, break; } - lo_interface lib9p_srv_file member_file = LO_CALL(pathinfo->file, dopen, &ctx->ctx, req->wname[resp->nwqid]); + 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; @@ -774,6 +785,13 @@ static void handle_Twalk(struct _lib9p_srv_req *ctx, pathinfo = new_pathinfo; } if (resp->nwqid == req->nwname) { + if (req->newfid == req->fid) { + 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 { @@ -816,7 +834,6 @@ static void handle_Topen(struct _lib9p_srv_req *ctx, /* Variables. */ lib9p_o_t reqmode = req->mode; uint8_t fidflags = fidinfo->flags; - uint32_t iounit = fidinfo->iounit; /* Check permissions. */ if (reqmode & LIB9P_O_RCLOSE) { @@ -831,13 +848,13 @@ static void handle_Topen(struct _lib9p_srv_req *ctx, LINUX_EACCES, "permission denied to remove-on-close"); return; } - fidflags = fidflags | FIDFLAG_RCLOSE; + fidflags |= FIDFLAG_RCLOSE; } struct lib9p_stat stat = LO_CALL(pathinfo->file, stat, &ctx->ctx); if (lib9p_ctx_has_error(&ctx->ctx.basectx)) return; lib9p_stat_assert(stat); - if ((stat.file_mode & LIB9P_DM_EXCL) && (pathinfo->rd_refcount || pathinfo->wr_refcount)) { + if ((stat.file_mode & LIB9P_DM_EXCL) && pathinfo->io_refcount) { lib9p_error(&ctx->ctx.basectx, LINUX_EEXIST, "exclusive file is already opened"); return; @@ -870,26 +887,36 @@ static void handle_Topen(struct _lib9p_srv_req *ctx, } /* Actually make the call. */ - if ((reqmode & LIB9P_O_TRUNC) || (rd && !pathinfo->rd_refcount) || (wr && !pathinfo->wr_refcount) ) { - iounit = LO_CALL(pathinfo->file, chio, &ctx->ctx, - fidflags & FIDFLAG_OPEN_R, - fidflags & FIDFLAG_OPEN_W, - reqmode & LIB9P_O_TRUNC); + 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 = fidflags | FIDFLAG_OPEN_R; - pathinfo->rd_refcount++; - } - if (wr) { - fidflags = fidflags | FIDFLAG_OPEN_W; - pathinfo->wr_refcount++; - } + 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; } @@ -923,7 +950,6 @@ static void handle_Tread(struct _lib9p_srv_req *ctx, /* Variables. */ struct srv_pathinfo *pathinfo = pathmap_load(&ctx->parent_sess->paths, fidinfo->path); assert(pathinfo); - resp->data = (char *)(&resp[1]); /* Do it. */ if (srv_util_pathisdir(pathinfo)) { @@ -931,16 +957,17 @@ static void handle_Tread(struct _lib9p_srv_req *ctx, 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 %"PRIu64"): %"PRIu64, - fidinfo->dir_off, req->offset); + fidinfo->dir.off, req->offset); return; } /* Do it. */ - size_t num = LO_CALL(pathinfo->file, dread, &ctx->ctx, (uint8_t *)resp->data, 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++) { @@ -950,10 +977,18 @@ static void handle_Tread(struct _lib9p_srv_req *ctx, } resp->count = len; /* Remember. */ - fidinfo->dir_idx = idx+num; - fidinfo->dir_off = req->offset + len; - } else - resp->count = LO_CALL(pathinfo->file, pread, &ctx->ctx, resp->data, 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, @@ -979,7 +1014,7 @@ static void handle_Twrite(struct _lib9p_srv_req *ctx, assert(pathinfo); /* Do it. */ - resp->count = LO_CALL(pathinfo->file, pwrite, &ctx->ctx, req->data, req->count, req->offset); + resp->count = LO_CALL(fidinfo->file.io, pwrite, &ctx->ctx, req->data, req->count, req->offset); } static void clunkremove(struct _lib9p_srv_req *ctx, lib9p_fid_t fid, bool remove) { @@ -1012,6 +1047,13 @@ static void clunkremove(struct _lib9p_srv_req *ctx, lib9p_fid_t fid, bool remove } 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); } diff --git a/lib9p/tests/test_server/CMakeLists.txt b/lib9p/tests/test_server/CMakeLists.txt index 74a759d..5313917 100644 --- a/lib9p/tests/test_server/CMakeLists.txt +++ b/lib9p/tests/test_server/CMakeLists.txt @@ -18,7 +18,7 @@ target_link_libraries(test_server_objs libmisc lib9p lib9p_util - libhw + libhw_cr ) # Analyze the stack ############################################################ diff --git a/lib9p/tests/test_server/main.c b/lib9p/tests/test_server/main.c index 074dbe7..c759029 100644 --- a/lib9p/tests/test_server/main.c +++ b/lib9p/tests/test_server/main.c @@ -48,7 +48,10 @@ struct api_file { uint64_t pathnum; }; LO_IMPLEMENTATION_H(lib9p_srv_file, struct api_file, api) +LO_IMPLEMENTATION_H(lib9p_srv_fio, struct api_file, api) + LO_IMPLEMENTATION_C(lib9p_srv_file, struct api_file, api, static) +LO_IMPLEMENTATION_C(lib9p_srv_fio, struct api_file, api, static) static void api_free(struct api_file *self) { assert(self); @@ -61,11 +64,6 @@ static struct lib9p_qid api_qid(struct api_file *self) { .path = self->pathnum, }; } -static uint32_t api_chio(struct api_file *self, struct lib9p_srv_ctx *ctx, bool, bool, bool) { - assert(self); - assert(ctx); - return 0; -} static struct lib9p_stat api_stat(struct api_file *self, struct lib9p_srv_ctx *ctx) { assert(self); @@ -101,6 +99,21 @@ static void api_remove(struct api_file *self, struct lib9p_srv_ctx *ctx) { LIB9P_SRV_NOTDIR(struct api_file, api) +static lo_interface lib9p_srv_fio api_fopen(struct api_file *self, struct lib9p_srv_ctx *ctx, bool, bool, bool) { + assert(self); + assert(ctx); + return lo_box_api_as_lib9p_srv_fio(self); +} + +static void api_iofree(struct api_file *self) { + assert(self); +} + +static uint32_t api_iounit(struct api_file *self) { + assert(self); + return 0; +} + static uint32_t api_pwrite(struct api_file *self, struct lib9p_srv_ctx *ctx, void *buf, uint32_t byte_count, uint64_t LM_UNUSED(offset)) { assert(self); assert(ctx); @@ -111,7 +124,9 @@ static uint32_t api_pwrite(struct api_file *self, struct lib9p_srv_ctx *ctx, voi LO_CALL(lo_box_hostnet_tcplist_as_net_stream_listener(&globals.listeners[i]), close); return byte_count; } -static uint32_t api_pread(struct api_file *, struct lib9p_srv_ctx *, void *, uint32_t, uint64_t) { +static void api_pread(struct api_file *LM_UNUSED(self), struct lib9p_srv_ctx *LM_UNUSED(ctx), + uint32_t LM_UNUSED(byte_count), uint64_t LM_UNUSED(byte_offset), + struct iovec *LM_UNUSED(ret)) { assert_notreached("not readable"); } |