summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuke T. Shumaker <lukeshu@lukeshu.com>2025-04-19 11:04:27 -0600
committerLuke T. Shumaker <lukeshu@lukeshu.com>2025-04-19 12:53:42 -0600
commitd81b0e1036aedb127c4033a187b671deaaca54c4 (patch)
tree5c6f5a2e1bbbda0bc2a4d6f9c82b148ebf0d7569
parentb85e0bd570de1245afa2738057925320789601c5 (diff)
lib9p_srv: Rethink the dio interface
-rw-r--r--lib9p/srv.c84
-rw-r--r--lib9p/srv_include/lib9p/srv.h23
-rw-r--r--lib9p_util/static.c38
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 ***********************************************************************/