summaryrefslogtreecommitdiff
path: root/lib9p/srv.c
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 /lib9p/srv.c
parentb85e0bd570de1245afa2738057925320789601c5 (diff)
lib9p_srv: Rethink the dio interface
Diffstat (limited to 'lib9p/srv.c')
-rw-r--r--lib9p/srv.c84
1 files changed, 65 insertions, 19 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;