diff options
author | Luke T. Shumaker <lukeshu@lukeshu.com> | 2025-04-19 11:04:27 -0600 |
---|---|---|
committer | Luke T. Shumaker <lukeshu@lukeshu.com> | 2025-04-19 12:53:42 -0600 |
commit | d81b0e1036aedb127c4033a187b671deaaca54c4 (patch) | |
tree | 5c6f5a2e1bbbda0bc2a4d6f9c82b148ebf0d7569 | |
parent | b85e0bd570de1245afa2738057925320789601c5 (diff) |
lib9p_srv: Rethink the dio interface
-rw-r--r-- | lib9p/srv.c | 84 | ||||
-rw-r--r-- | lib9p/srv_include/lib9p/srv.h | 23 | ||||
-rw-r--r-- | lib9p_util/static.c | 38 |
3 files changed, 91 insertions, 54 deletions
diff --git a/lib9p/srv.c b/lib9p/srv.c index b5455e0..96954aa 100644 --- a/lib9p/srv.c +++ b/lib9p/srv.c @@ -108,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; @@ -1163,32 +1164,77 @@ static void handle_Tread(struct srv_req *ctx, 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 { + /* 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); goto tread_return; } - /* Do it. */ + /* Read. */ resp.data = heap = malloc(req->count); /* TODO: cap req->count */ - 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; + 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_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_stat_assert(member_stat); + uint32_t nbytes = lib9p_stat_marshal(&ctx->basectx, req->count-resp.count, &member_stat, + (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; break; case SRV_FILETYPE_FILE: struct iovec iov; diff --git a/lib9p/srv_include/lib9p/srv.h b/lib9p/srv_include/lib9p/srv.h index 0ef674a..1c534e9 100644 --- a/lib9p/srv_include/lib9p/srv.h +++ b/lib9p/srv_include/lib9p/srv.h @@ -71,6 +71,11 @@ void lib9p_srv_acknowledge_flush(struct lib9p_srv_ctx *ctx); /* interface definitions ******************************************************/ +struct lib9p_srv_dirent { + struct lib9p_qid qid; + struct lib9p_s name; +}; + lo_interface lib9p_srv_fio; lo_interface lib9p_srv_dio; @@ -146,20 +151,16 @@ LO_INTERFACE(lib9p_srv_file); uint64_t byte_offset) 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) + /** \ + * Return the idx-th dirent. idx will always be either 0 or \ + * prev_idx+1. A dirrent with an empty name signals EOF. The string + * must remain valid until the next dread() call or iofree(). \ + */ \ + LO_FUNC(struct lib9p_srv_dirent , dread , struct lib9p_srv_ctx *, \ + size_t idx) LO_INTERFACE(lib9p_srv_dio); #define LIB9P_SRV_NOTDIR(TYP, NAM) \ diff --git a/lib9p_util/static.c b/lib9p_util/static.c index e1df3ca..d2413c6 100644 --- a/lib9p_util/static.c +++ b/lib9p_util/static.c @@ -110,33 +110,23 @@ static lo_interface lib9p_srv_dio util9p_static_dir_dopen(struct util9p_static_d static void util9p_static_dir_iofree(struct util9p_static_dir *self) { assert(self); } -static size_t util9p_static_dir_dread(struct util9p_static_dir *self, struct lib9p_srv_ctx *ctx, - uint8_t *buf, - uint32_t byte_count, - size_t _obj_offset) { +static struct lib9p_srv_dirent util9p_static_dir_dread(struct util9p_static_dir *self, struct lib9p_srv_ctx *ctx, size_t idx) { assert(self); assert(ctx); - uint32_t byte_offset = 0; - size_t obj_offset = _obj_offset; - while (!LO_IS_NULL(self->members[obj_offset])) { - lo_interface lib9p_srv_file file = self->members[obj_offset]; - struct lib9p_stat stat = LO_CALL(file, stat, ctx); - if (lib9p_ctx_has_error(&ctx->basectx)) - break; - lib9p_stat_assert(stat); - uint32_t nbytes = lib9p_stat_marshal(&ctx->basectx, byte_count-byte_offset, &stat, - &buf[byte_offset]); - if (!nbytes) { - if (obj_offset == _obj_offset) - lib9p_error(&ctx->basectx, - LIB9P_ERRNO_L_ERANGE, "stat object does not fit into negotiated max message size"); - break; - } - byte_offset += nbytes; - obj_offset++; - } - return obj_offset - _obj_offset; + lo_interface lib9p_srv_file file = self->members[idx]; + if (LO_IS_NULL(file)) + return (struct lib9p_srv_dirent){}; + + struct lib9p_stat stat = LO_CALL(file, stat, ctx); + if (lib9p_ctx_has_error(&ctx->basectx)) + return (struct lib9p_srv_dirent){}; + lib9p_stat_assert(stat); + + return (struct lib9p_srv_dirent){ + .qid = stat.qid, + .name = stat.name, + }; } /* file ***********************************************************************/ |