diff options
author | Luke T. Shumaker <lukeshu@lukeshu.com> | 2024-10-10 21:48:16 -0600 |
---|---|---|
committer | Luke T. Shumaker <lukeshu@lukeshu.com> | 2024-10-10 21:48:16 -0600 |
commit | 2145808d29f495cf872d9b3ffe05420ff9fc5be6 (patch) | |
tree | 78ec87bcb6bdf9a19bd6f5a2cb6cb3f8333bfdbc | |
parent | db92bd35807305e6daec5ec40bd67cbc43fe88ee (diff) |
implement dread
-rw-r--r-- | cmd/srv9p/static.c | 37 | ||||
-rw-r--r-- | lib9p/9p.c | 47 | ||||
-rw-r--r-- | lib9p/9p.generated.c | 24 | ||||
-rwxr-xr-x | lib9p/idl.gen | 18 | ||||
-rw-r--r-- | lib9p/include/lib9p/9p.h | 29 | ||||
-rw-r--r-- | lib9p/include/lib9p/srv.h | 26 | ||||
-rw-r--r-- | lib9p/internal.h | 4 | ||||
-rw-r--r-- | lib9p/srv.c | 56 |
8 files changed, 205 insertions, 36 deletions
diff --git a/cmd/srv9p/static.c b/cmd/srv9p/static.c index 912ec03..a6da53f 100644 --- a/cmd/srv9p/static.c +++ b/cmd/srv9p/static.c @@ -99,6 +99,35 @@ static struct lib9p_srv_file *static_dir_dcreate(struct lib9p_srv_ctx *ctx, stru return NULL; } +static size_t static_dir_dread(struct lib9p_srv_ctx *ctx, struct lib9p_srv_file *_dir, + uint8_t *buf, + uint32_t byte_count, + size_t _obj_offset) { + assert(ctx); + struct static_dir *dir = (struct static_dir *)_dir; + assert(dir); + + uint32_t byte_offset = 0; + size_t obj_offset = _obj_offset; + while (dir->members[obj_offset]) { + struct lib9p_srv_file *filep = dir->members[obj_offset]; + struct lib9p_stat stat = filep->vtable->stat(ctx, filep); + if (lib9p_ctx_has_error(&ctx->basectx)) + break; + uint32_t nbytes = lib9p_marshal_stat(&ctx->basectx, byte_count-byte_offset, &stat, + &buf[byte_offset]); + if (!nbytes) { + if (obj_offset == _obj_offset) + lib9p_error(&ctx->basectx, + LINUX_ERANGE, "stat object does not fit into negotiated max message size"); + break; + } + byte_offset += nbytes; + obj_offset++; + } + return obj_offset - _obj_offset; +} + struct lib9p_srv_file_vtable static_dir_vtable = { .clone = static_dir_clone, .free = static_dir_free, @@ -110,10 +139,6 @@ struct lib9p_srv_file_vtable static_dir_vtable = { .dopen = static_dir_dopen, .dcreate = static_dir_dcreate, -}; -/* - struct lib9p_srv_io_dir_vtable static_dir_io_vtable = { - .readdir = TODO, - }; -*/ + .dread = static_dir_dread, +}; @@ -98,3 +98,50 @@ bool lib9p_marshal(struct lib9p_ctx *ctx, enum lib9p_msg_type typ, void *body, struct _vtable_msg vtable = _lib9p_vtables[ctx->version].msgs[typ]; return vtable.marshal(&subctx, body); } + +bool lib9p_validate_stat(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes, + uint32_t *ret_net_size, ssize_t *ret_host_size) { + struct _validate_ctx subctx = { + .ctx = ctx, + .net_size = net_size, + .net_bytes = net_bytes, + + .net_offset = 0, + .host_extra = 0, + }; + if (_lib9p_validate_stat(&subctx)) + return true; + if (ret_net_size) + *ret_net_size = subctx.net_offset; + if (ret_host_size) + if (__builtin_add_overflow(sizeof(struct lib9p_stat), subctx.host_extra, ret_host_size)) + return lib9p_error(ctx, LINUX_EMSGSIZE, "unmarshalled stat object overflows SSIZE_MAX"); + return false; +} + +uint32_t lib9p_unmarshal_stat(struct lib9p_ctx *ctx, uint8_t *net_bytes, + struct lib9p_stat *ret_obj, void *ret_extra) { + struct _unmarshal_ctx subctx = { + .ctx = ctx, + .net_bytes = net_bytes, + .net_offset = 0, + + .extra = ret_extra, + }; + _lib9p_unmarshal_stat(&subctx, ret_obj); + return subctx.net_offset; +} + +uint32_t lib9p_marshal_stat(struct lib9p_ctx *ctx, uint32_t max_net_size, struct lib9p_stat *obj, + uint8_t *ret_bytes) { + struct lib9p_ctx _ctx = *ctx; + _ctx.max_msg_size = max_net_size; + struct _marshal_ctx subctx = { + .ctx = &_ctx, + .net_bytes = ret_bytes, + .net_offset = 0, + }; + if (_lib9p_marshal_stat(&subctx, obj)) + return 0; + return subctx.net_offset; +} diff --git a/lib9p/9p.generated.c b/lib9p/9p.generated.c index 341acd0..660c925 100644 --- a/lib9p/9p.generated.c +++ b/lib9p/9p.generated.c @@ -1530,8 +1530,8 @@ static ALWAYS_INLINE bool marshal_qid(struct _marshal_ctx *ctx, struct lib9p_qid static ALWAYS_INLINE bool marshal_stat(struct _marshal_ctx *ctx, struct lib9p_stat *val) { uint32_t _stat_size_offset; - - uint32_t _kern_type_offset; return false + uint32_t _kern_type_offset; + return false || ({ _stat_size_offset = ctx->net_offset; ({ ctx->net_offset += 2; false; }); }) || ({ _kern_type_offset = ctx->net_offset; marshal_2(ctx, &val->kern_type); }) || marshal_4(ctx, &val->kern_dev) @@ -1908,8 +1908,8 @@ static FLATTEN bool marshal_Rstat(struct _marshal_ctx *ctx, struct lib9p_msg_Rst uint32_t _size_offset; uint32_t _typ_offset; uint32_t _nstat_offset; - - uint32_t _stat_offset; return false + uint32_t _stat_offset; + return false || ({ _size_offset = ctx->net_offset; ({ ctx->net_offset += 4; false; }); }) || ({ _typ_offset = ctx->net_offset; ({ ctx->net_offset += 1; false; }); }) || marshal_tag(ctx, &val->tag) @@ -1925,8 +1925,8 @@ static FLATTEN bool marshal_Twstat(struct _marshal_ctx *ctx, struct lib9p_msg_Tw uint32_t _size_offset; uint32_t _typ_offset; uint32_t _nstat_offset; - - uint32_t _stat_offset; return false + uint32_t _stat_offset; + return false || ({ _size_offset = ctx->net_offset; ({ ctx->net_offset += 4; false; }); }) || ({ _typ_offset = ctx->net_offset; ({ ctx->net_offset += 1; false; }); }) || marshal_tag(ctx, &val->tag) @@ -2041,7 +2041,7 @@ static FLATTEN bool marshal_Rswrite(struct _marshal_ctx *ctx, struct lib9p_msg_R ; } -/* vtables ********************************************************************/ +/* vtables / exports **********************************************************/ #define _MSG(typ) [LIB9P_TYP_##typ] = { \ .basesize = sizeof(struct lib9p_msg_##typ), \ @@ -2157,3 +2157,13 @@ struct _vtable_version _lib9p_vtables[LIB9P_VER_NUM] = { }}, #endif /* defined(CONFIG_9P_ENABLE_9P2000_u) */ }; + +FLATTEN bool _lib9p_validate_stat(struct _validate_ctx *ctx) { + return validate_stat(ctx); +} +FLATTEN void _lib9p_unmarshal_stat(struct _unmarshal_ctx *ctx, struct lib9p_stat *out) { + unmarshal_stat(ctx, out); +} +FLATTEN bool _lib9p_marshal_stat(struct _marshal_ctx *ctx, struct lib9p_stat *val) { + return marshal_stat(ctx, val); +} diff --git a/lib9p/idl.gen b/lib9p/idl.gen index 8e7639a..c0a2444 100755 --- a/lib9p/idl.gen +++ b/lib9p/idl.gen @@ -1045,7 +1045,7 @@ static ALWAYS_INLINE bool marshal_8(struct _marshal_ctx *ctx, uint64_t *val) { for tok in member.val.tokens: if isinstance(tok, ExprSym) and tok.name.startswith("&"): if tok.name[1:] not in mark_offset: - ret += f"\n\tuint32_t _{tok.name[1:]}_offset;" + ret += f"\tuint32_t _{tok.name[1:]}_offset;\n" mark_offset.add(tok.name[1:]) # Pass 2 - main pass @@ -1092,9 +1092,9 @@ static ALWAYS_INLINE bool marshal_8(struct _marshal_ctx *ctx, uint64_t *val) { ret += "\t ;\n" ret += "}\n" - # vtables ################################################################## + # vtables / exports ######################################################## ret += f""" -/* vtables ********************************************************************/ +/* vtables / exports **********************************************************/ #define _MSG(typ) [{idprefix.upper()}TYP_##typ] = {{ \\ .basesize = sizeof(struct {idprefix}msg_##typ), \\ @@ -1123,6 +1123,18 @@ struct _vtable_version _{idprefix}vtables[{c_ver_enum('NUM')}] = {{ ret += ifdef_pop(0) ret += "};\n" + ret += f""" +FLATTEN bool _{idprefix}validate_stat(struct _validate_ctx *ctx) {{ + return validate_stat(ctx); +}} +FLATTEN void _{idprefix}unmarshal_stat(struct _unmarshal_ctx *ctx, struct lib9p_stat *out) {{ + unmarshal_stat(ctx, out); +}} +FLATTEN bool _{idprefix}marshal_stat(struct _marshal_ctx *ctx, struct lib9p_stat *val) {{ + return marshal_stat(ctx, val); +}} +""" + ############################################################################ return ret diff --git a/lib9p/include/lib9p/9p.h b/lib9p/include/lib9p/9p.h index c3c6a8b..1e1667d 100644 --- a/lib9p/include/lib9p/9p.h +++ b/lib9p/include/lib9p/9p.h @@ -113,7 +113,7 @@ void lib9p_unmarshal(struct lib9p_ctx *ctx, uint8_t *net_bytes, * @param typ : the message type * @param msg : the message to encode * - * @return ret_bytes : the buffer to encode to, must be at be at least lib9p_ctx_max_msg_size(ctx) bytes + * @return ret_bytes : the buffer to encode to, must be at be at least ctx->max_msg_size bytes * @return whether there was an error (false=success, true=error) * * @errno LINUX_ERANGE: reply does not fit in ctx->max_msg_size @@ -121,4 +121,31 @@ void lib9p_unmarshal(struct lib9p_ctx *ctx, uint8_t *net_bytes, bool lib9p_marshal(struct lib9p_ctx *ctx, enum lib9p_msg_type typ, void *body, uint8_t *ret_bytes); +/** + * TODO + * + * @return ret_net_size: number of bytes consumed; <=net_size + * @return ret_host_size: number of bytes that lib9p_unmarshal_stat would take + * @return whether there was an error + */ +bool lib9p_validate_stat(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes, + uint32_t *ret_net_size, ssize_t *ret_host_size); + +/** + * TODO + * + * @return ret_obj : where to put the stat object itself + * @return ret_extra : where to put strings for the stat object + * @return consumed net_bytes + */ +uint32_t lib9p_unmarshal_stat(struct lib9p_ctx *ctx, uint8_t *net_bytes, + struct lib9p_stat *ret_obj, void *ret_extra); + +/** + * @return ret_bytes: the buffer to encode into + * @return the number of bytes written, or 0 if the stat object does not fit in max_net_size + */ +uint32_t lib9p_marshal_stat(struct lib9p_ctx *ctx, uint32_t max_net_size, struct lib9p_stat *obj, + uint8_t *ret_bytes); + #endif _LIB9P_9P_H_ diff --git a/lib9p/include/lib9p/srv.h b/lib9p/include/lib9p/srv.h index 5df6b1a..7aca76c 100644 --- a/lib9p/include/lib9p/srv.h +++ b/lib9p/include/lib9p/srv.h @@ -57,17 +57,20 @@ struct lib9p_srv_file_vtable { lib9p_dm_t perm, lib9p_o_t flags); /* directories - once opened */ - size_t (*readdir)(struct lib9p_srv_ctx *, struct lib9p_srv_io_dir *, - struct lib9p_stat *buf, size_t count, - size_t offset); + size_t /* <- obj cnt */(*dread )(struct lib9p_srv_ctx *, struct lib9p_srv_file *, + uint8_t *buf, + uint32_t byte_count, /* <- num bytes */ + size_t obj_offset); /* <- starting at this object */ /* non-directories - once opened */ - uint32_t (*pread )(struct lib9p_srv_ctx *, struct lib9p_srv_io_file *, - void *buf, uint32_t count, - uint64_t offset); - uint32_t (*pwrite )(struct lib9p_srv_ctx *, struct lib9p_srv_io_file *, - void *buf, uint32_t count, - uint64_t offset); + uint32_t (*pread )(struct lib9p_srv_ctx *, struct lib9p_srv_file *, + void *buf, + uint32_t byte_count, + uint64_t byte_offset); + uint32_t (*pwrite )(struct lib9p_srv_ctx *, struct lib9p_srv_file *, + void *buf, + uint32_t byte_count, + uint64_t byte_offset); }; /* objects you'll deal with ***************************************************/ @@ -83,8 +86,9 @@ struct lib9p_srv_file { * ref type 2: ->_parent_dir of another file */ struct lib9p_srv_file *_refcount; lib9p_o_t _io_flags; - struct lib9p_qid _io_qid; - uint32_t _io_unit; + bool _io_isdir; + size_t _io_dir_idx; + uint32_t _io_dir_off; /* This is where your implementation data goes. */ char data[0]; diff --git a/lib9p/internal.h b/lib9p/internal.h index 2b5f8f3..013024e 100644 --- a/lib9p/internal.h +++ b/lib9p/internal.h @@ -90,6 +90,10 @@ struct _vtable_version { extern struct _vtable_version _lib9p_vtables[LIB9P_VER_NUM]; +bool _lib9p_validate_stat(struct _validate_ctx *ctx); +void _lib9p_unmarshal_stat(struct _unmarshal_ctx *ctx, struct lib9p_stat *out); +bool _lib9p_marshal_stat(struct _marshal_ctx *ctx, struct lib9p_stat *val); + /* unmarshal utilities ********************************************************/ static ALWAYS_INLINE uint8_t decode_u8le(uint8_t *in) { diff --git a/lib9p/srv.c b/lib9p/srv.c index 8940afe..04cd119 100644 --- a/lib9p/srv.c +++ b/lib9p/srv.c @@ -729,12 +729,10 @@ static void handle_Topen(struct _lib9p_srv_req *ctx, if (lib9p_ctx_has_error(&ctx->ctx.basectx)) return; - filep->_io_flags = flags; - filep->_io_unit = iounit; - filep->_io_qid = stat.file_qid; - - resp->qid = filep->_io_qid; - resp->iounit = filep->_io_unit; + filep->_io_flags = filep->_io_flags | flags; + filep->_io_isdir = stat.file_mode & LIB9P_DM_DIR; + resp->qid = stat.file_qid; + resp->iounit = iounit; } static void handle_Tcreate(struct _lib9p_srv_req *ctx, @@ -751,8 +749,50 @@ static void handle_Tread(struct _lib9p_srv_req *ctx, struct lib9p_msg_Rread *resp) { handler_common(ctx, req, resp); - lib9p_error(&ctx->ctx.basectx, - LINUX_EOPNOTSUPP, "read not yet implemented"); + struct lib9p_srv_file **filepp = fidmap_load(&ctx->parent_sess->fids, req->fid); + if (!filepp) { + lib9p_errorf(&ctx->ctx.basectx, + LINUX_EBADF, "bad file number %"PRIu32, req->fid); + return; + } + struct lib9p_srv_file *filep = *filepp; + + switch (filep->_io_flags & LIB9P_O_MODE_MASK) { + case LIB9P_O_READ: + case LIB9P_O_RDWR: + /* ok */ + break; + default: + lib9p_error(&ctx->ctx.basectx, + LINUX_EINVAL, "FID not open for reading"); + return; + } + + resp->data.dat = (char *)(&resp[1]); + if (filep->_io_isdir) { + size_t idx; + if (req->offset == 0) + idx = 0; + else if (req->offset == filep->_io_dir_off) + idx = filep->_io_dir_idx; + else { + lib9p_errorf(&ctx->ctx.basectx, + LINUX_EINVAL, "invalid offset (must be 0 or %"PRIu32"): %"PRIu32, + filep->_io_dir_off, req->offset); + return; + } + size_t num = filep->vtable->dread(&ctx->ctx, filep, (uint8_t *)resp->data.dat, req->count, idx); + filep->_io_dir_idx = idx+num; + uint32_t len = 0; + for (size_t i = 0; i < num; i++) { + uint32_t i_len; + lib9p_validate_stat(&ctx->ctx.basectx, req->count, (uint8_t *)resp->data.dat, &i_len, NULL); + len += i_len; + } + filep->_io_dir_off = req->offset + len; + resp->data.len = len; + } else + resp->data.len = filep->vtable->pread(&ctx->ctx, filep, resp->data.dat, req->count, req->offset); } static void handle_Twrite(struct _lib9p_srv_req *ctx, |