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 /lib9p/srv.c | |
parent | b85e0bd570de1245afa2738057925320789601c5 (diff) |
lib9p_srv: Rethink the dio interface
Diffstat (limited to 'lib9p/srv.c')
-rw-r--r-- | lib9p/srv.c | 84 |
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; |