From b683e779ef83b88f6ffc503a1d1a4f3ec25a5cfa Mon Sep 17 00:00:00 2001 From: "Luke T. Shumaker" Date: Fri, 4 Oct 2024 16:43:55 -0600 Subject: wip srv --- lib9p/9p.c | 5 ++ lib9p/9p.gen | 14 +++-- lib9p/9p.generated.c | 10 ++-- lib9p/CMakeLists.txt | 2 +- lib9p/include/lib9p/9p.generated.h | 4 +- lib9p/include/lib9p/9p.h | 1 + lib9p/include/lib9p/srv.h | 72 ++++++++++++++++++++++++-- lib9p/internal.h | 2 + lib9p/map.h | 102 +++++++++++++++++++++++++++++++++++++ lib9p/srv.c | 76 ++++++++++++++++++++++++++- 10 files changed, 270 insertions(+), 18 deletions(-) create mode 100644 lib9p/map.h (limited to 'lib9p') diff --git a/lib9p/9p.c b/lib9p/9p.c index af1c6ca..fffef31 100644 --- a/lib9p/9p.c +++ b/lib9p/9p.c @@ -23,6 +23,11 @@ uint32_t lib9p_ctx_max_msg_size(struct lib9p_ctx *ctx) { return ctx->max_msg_size; } +bool lib9p_ctx_has_error(struct lib9p_ctx *ctx) { + assert(ctx); + return ctx->err_num || ctx->err_msg[0]; +} + int lib9p_error(struct lib9p_ctx *ctx, uint32_t linux_errno, char const *msg) { strncpy(ctx->err_msg, msg, sizeof(ctx->err_msg)); ctx->err_msg[sizeof(ctx->err_msg)-1] = '\0'; diff --git a/lib9p/9p.gen b/lib9p/9p.gen index 6f2ad2c..cdec067 100755 --- a/lib9p/9p.gen +++ b/lib9p/9p.gen @@ -433,7 +433,10 @@ enum {idprefix}version {{ for member in struct.members: if struct.name == "stat" and member.name == "stat_size": # SPECIAL continue - ret += f"\t{c_typename(idprefix, member.typ).ljust(typewidth)} {'*' if member.cnt else ' '}{member.name};" + ctype = c_typename(idprefix, member.typ) + if (struct.name in ["d", "s"]) and member.cnt: # SPECIAL + ctype = "char" + ret += f"\t{ctype.ljust(typewidth)} {'*' if member.cnt else ' '}{member.name};" if (not all_the_same) and (comment := c_vercomment(member.ver)): ret += (" " * (namewidth - len(member.name))) + " " + comment ret += "\n" @@ -689,8 +692,8 @@ static ALWAYS_INLINE bool _validate_list(struct _validate_ctx *ctx, prev_size = member.static_size if typ.name == "stat": # SPECIAL assert typ.members[0].static_size - ret += f"\n{prefix1}((uint32_t)decode_u{typ.members[0].static_size*8}le(&ctx->net_bytes[size_offset]) != ctx->net_offset - size_offset)" - ret += f'\n{prefix2}\t? lib9p_error(ctx->ctx, LINUX_EBADMSG, "stat size does not match stat contents")"' + ret += f"\n{prefix1}((uint32_t)decode_u{typ.members[0].static_size*8}le(&ctx->net_bytes[size_offset]) != ctx->net_offset - size_offset" + ret += f'\n{prefix2}\t? lib9p_error(ctx->ctx, LINUX_EBADMSG, "stat size does not match stat contents")' ret += f"\n{prefix2}\t: false)" ret += ";\n" ret += "}\n" @@ -738,7 +741,7 @@ static ALWAYS_INLINE void unmarshal_8(struct _unmarshal_ctx *ctx, uint64_t *out) struct_versions = typ.members[0].ver for member in typ.members: if typ.name == "stat" and member.name == "stat_size": # SPECIAL - ret += f"\tsize->net_offset += {member.static_size};\n" + ret += f"\tctx->net_offset += {member.static_size};\n" continue ret += "\t" prefix = "\t" @@ -835,9 +838,10 @@ static ALWAYS_INLINE bool marshal_8(struct _marshal_ctx *ctx, uint64_t *val) { for member in typ.members: if typ.name == "stat" and member.name == "stat_size": # SPECIAL: assert member.static_size - ret += f"\n{prefix }((ctx->net_offset + {member.static_size} > ctx->ctx->max_msg_size)" + ret += f"\n{prefix }(ctx->net_offset + {member.static_size} > ctx->ctx->max_msg_size" ret += f"\n{prefix2}\t? _marshal_too_large(ctx)" ret += f"\n{prefix2}\t: ({{ ctx->net_offset += {member.static_size}; false; }}))" + prefix = prefix1 continue ret += f"\n{prefix}" if member.ver != struct_versions: diff --git a/lib9p/9p.generated.c b/lib9p/9p.generated.c index 4ec5ae5..fc243f5 100644 --- a/lib9p/9p.generated.c +++ b/lib9p/9p.generated.c @@ -400,8 +400,8 @@ static ALWAYS_INLINE bool validate_stat(struct _validate_ctx *ctx) { || ( (ctx->ctx->version==LIB9P_VER_9P2000_u) && validate_4(ctx) ) || ( (ctx->ctx->version==LIB9P_VER_9P2000_u) && validate_4(ctx) ) || ( (ctx->ctx->version==LIB9P_VER_9P2000_u) && validate_4(ctx) ) - || ((uint32_t)decode_u16le(&ctx->net_bytes[size_offset]) != ctx->net_offset - size_offset) - ? lib9p_error(ctx->ctx, LINUX_EBADMSG, "stat size does not match stat contents")" + || ((uint32_t)decode_u16le(&ctx->net_bytes[size_offset]) != ctx->net_offset - size_offset + ? lib9p_error(ctx->ctx, LINUX_EBADMSG, "stat size does not match stat contents") : false); } @@ -642,7 +642,7 @@ static ALWAYS_INLINE void unmarshal_qid(struct _unmarshal_ctx *ctx, struct lib9p static ALWAYS_INLINE void unmarshal_stat(struct _unmarshal_ctx *ctx, struct lib9p_stat *out) { memset(out, 0, sizeof(*out)); - size->net_offset += 2; + ctx->net_offset += 2; unmarshal_2(ctx, &out->kern_type); unmarshal_4(ctx, &out->kern_dev); unmarshal_qid(ctx, &out->file_qid); @@ -943,10 +943,10 @@ 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 size_offset = ctx->net_offset; - return ((ctx->net_offset + 2 > ctx->ctx->max_msg_size) + return (ctx->net_offset + 2 > ctx->ctx->max_msg_size ? _marshal_too_large(ctx) : ({ ctx->net_offset += 2; false; })) - return marshal_2(ctx, &val->kern_type) + || marshal_2(ctx, &val->kern_type) || marshal_4(ctx, &val->kern_dev) || marshal_qid(ctx, &val->file_qid) || marshal_dm(ctx, &val->file_mode) diff --git a/lib9p/CMakeLists.txt b/lib9p/CMakeLists.txt index 811e81a..3584d7a 100644 --- a/lib9p/CMakeLists.txt +++ b/lib9p/CMakeLists.txt @@ -6,7 +6,7 @@ add_library(lib9p INTERFACE) target_include_directories(lib9p SYSTEM INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include) target_sources(lib9p INTERFACE - types.c + 9p.generated.c 9p.c srv.c ) diff --git a/lib9p/include/lib9p/9p.generated.h b/lib9p/include/lib9p/9p.generated.h index f093aa1..135cf8a 100644 --- a/lib9p/include/lib9p/9p.generated.h +++ b/lib9p/include/lib9p/9p.generated.h @@ -81,12 +81,12 @@ typedef uint8_t lib9p_o_t; struct lib9p_d { uint32_t len; - uint8_t *dat; + char *dat; }; struct lib9p_s { uint16_t len; - uint8_t *utf8; + char *utf8; }; struct lib9p_qid { diff --git a/lib9p/include/lib9p/9p.h b/lib9p/include/lib9p/9p.h index 8e57a86..b96c938 100644 --- a/lib9p/include/lib9p/9p.h +++ b/lib9p/include/lib9p/9p.h @@ -19,6 +19,7 @@ struct lib9p_ctx; enum lib9p_version lib9p_ctx_version(struct lib9p_ctx *); uint32_t lib9p_ctx_max_msg_size(struct lib9p_ctx *); +bool lib9p_ctx_has_error(struct lib9p_ctx *); /** Write an static error into ctx, return -1. */ int lib9p_error(struct lib9p_ctx *ctx, uint32_t linux_errno, char const *msg); diff --git a/lib9p/include/lib9p/srv.h b/lib9p/include/lib9p/srv.h index 9220bd9..11894ea 100644 --- a/lib9p/include/lib9p/srv.h +++ b/lib9p/include/lib9p/srv.h @@ -4,11 +4,77 @@ #include #include -struct lib9p_req; +#include + +/* vtables you must implement *************************************************/ + +struct lib9p_srv_file_vtable { + /* all */ + struct lib9p_srv_io (*io )(struct lib9p_srv_reqctx *ctx, void *impldata, + lib9p_o_t flags); + struct lib9p_stat (*stat )(struct lib9p_srv_reqcggtx *ctx, void *impldata); + void (*wstat )(struct lib9p_srv_reqctx *ctx, void *impldata, + struct lib9p_stat new); + void (*remove )(struct lib9p_srv_reqctx *ctx, void *impldata); + void (*free )(struct lib9p_srv_reqctx *ctx, void *impldata); + + /* dir */ + struct lib9p_srv_file (*openchild )(struct lib9p_srv_reqctx *ctx, void *impldata, + char *childname); + struct lib9p_srv_file (*createchild)(struct lib9p_srv_reqctx *ctx, void *impldata, + char *childname, lib9p_dm_t perm, lib9p_o_t flags); +}; + +struct lib9p_srv_io_dir_vtable { + size_t (*readdir )(struct lib9p_srv_reqctx *ctx, void *impldata, + struct lib9p_stat *buf, size_t count, size_t offset); +}; + +struct lib9p_srv_io_file_vtable { + uint32_t (*pread )(struct lib9p_srv_reqctx *ctx, void *impldata, + void *buf, uint32_t count, uint64_t offset); + uint32_t (*pwrite )(struct lib9p_srv_reqctx *ctx, void *impldata, + void *buf, uint32_t count, uint64_t offset); +}; + +/* objects you'll deal with ***************************************************/ + +struct lib9p_srv_reqctx { + struct lib9p_ctx ctx; + uint32_t uid; + char *uname; +}; + +struct lib9p_srv_file { + struct lib9p_srv_file_vtable vtable; + + struct lib9p_srv_file *parent_dir; + void *impldata; +}; + +struct lib9p_srv_io { + union { + struct lib9p_srv_io_dir_vtable dir; + struct lib9p_srv_io_file_vtable file; + } vtable; + + struct lib9p_srv_file *file; + struct lib9p_qid qid; + uint32_t iounit; + + void *impldata; +}; + +/******************************************************************************/ struct lib9p_srv { - int sockfd; - cr_chan_t(struct lib9p_req *) reqch; + /* Things you provide */ + int sockfd; + void /*TODO*/ (*auth)(struct lib9p_srv_reqctx *ctx, char *treename); + struct lib9p_srv_file (*rootdir)(struct lib9p_srv_reqctx *ctx, char *treename); + + /* For internal use */ + cr_chan_t(struct lib9p_req *) reqch; }; /** diff --git a/lib9p/internal.h b/lib9p/internal.h index 40cfcee..cbec829 100644 --- a/lib9p/internal.h +++ b/lib9p/internal.h @@ -27,6 +27,8 @@ static_assert(CONFIG_9P_MAX_ERR_SIZE + CONFIG_9P_MAX_MSG_SIZE + 2*CONFIG_9P_MAX_ #define ALWAYS_INLINE inline __attribute__((always_inline)) #define FLATTEN __attribute__((flatten)) #define ARRAY_LEN(arr) (sizeof(arr)/sizeof((arr)[0])) +#define CAT2(a, b) a##b +#define CAT3(a, b, c) a##b##c /* types **********************************************************************/ diff --git a/lib9p/map.h b/lib9p/map.h new file mode 100644 index 0000000..a8d26b5 --- /dev/null +++ b/lib9p/map.h @@ -0,0 +1,102 @@ +/* lib9p/map.h - A really dumb map/dict data structur + * + * Copyright (C) 2024 Luke T. Shumaker + * SPDX-Licence-Identifier: AGPL-3.0-or-later + */ + +#include "internal.h" + +/** + * `#define` `NAME`, `KEY_T`, `VAL_T`, and `CAP`; then `#include + * "map.h". + */ + +#ifndef NAME +# error NAME must be defined +#endif +#ifndef KEY_T +# error KEY_T must be defined +#endif +#ifndef VAL_T +# error VAL_T must be defined +#endif +#ifndef CAP +# error CAP must be defined +#endif + +#ifndef MAP_KEY +#define MAP_KV(TNAME) CAT3(_,TNAME,_kv) +#define MAP_METHOD(TNAME, MNAME) CAT3(TNAME,_,MNAME) +#endif + +/* This implementation is just an array that we brute-force search + * over for a slot. I don't want to use the heap, which means + * statically-sized maps, and I'm probably going to choose a low + * static size, so this is fine. */ + +struct MAP_KV(NAME) { + bool set; + KEY_T key; + VAL_T val; +}; + +struct NAME { + size_t len; + struct MAP_KV(NAME) items[CAP]; +}; + +/** + * Load an item from the map; return a pointer to the in-map value, or + * NULL if the item is not in the map. + */ +static VAL_T *MAP_METHOD(NAME,load)(struct NAME *m, KEY_T k) { + if (!m->len) + return NULL; + for (size_t i = 0; i < ARRAY_LEN(m->items); i++) + if (m->items[i].set && m->items[i].key == k) + return &(m->items[i].val); + return NULL; +} + +/** + * Store an item into the map, perhaps replacing an existing value. + * Return a pointer to the in-map value, or NULL if the map is full. + */ +static VAL_T *MAP_METHOD(NAME,store)(struct NAME *m, KEY_T k, VAL_T v) { + VAL_T *old = MAP_METHOD(NAME,load)(m, k); + if (old) { + *old = v; + return old; + } + if (m->len == ARRAY_LEN(m->items)) + return NULL; + for (size_t i = 0; i < ARRAY_LEN(m->items); i++) + if (!m->items[i].set) { + m->items[i].set = true; + m->items[i].key = k; + m->items[i].val = v; + return &(m->items[i].val); + } + assert(false); +} + +/** + * Delete an item from the map. Returns true if an item was deleted, + * false if no such item was in the map. + */ +static bool MAP_METHOD(NAME,del)(struct NAME *m, KEY_T k) { + if (!m->len) + return NULL; + for (size_t i = 0; i < ARRAY_LEN(m->items); i++) + if (m->items[i].set && m->items[i].key == k) { + m->items[i].set = false; + m->len--; + return true; + } + return false; +} + +#undef NAME +#undef KEY_T +#undef VAL_T +#undef CAP diff --git a/lib9p/srv.c b/lib9p/srv.c index f25fe09..1510a4a 100644 --- a/lib9p/srv.c +++ b/lib9p/srv.c @@ -14,6 +14,12 @@ /* structs ********************************************************************/ +#define NAME fidmap +#define KEY_T uint32_t +#define VAL_T struct lib9p_srv_file +#define CAP CONFIG_9P_MAX_FIDS +#include "map.h" + /* The hierarchy of concepts is: * * server -> connection -> session -> request @@ -40,6 +46,7 @@ struct lib9p_sess { /* mutable */ bool initialized; unsigned int refcount; + struct fidmap fids; }; struct lib9p_req { @@ -48,7 +55,7 @@ struct lib9p_req { uint16_t tag; /* mutable */ uint8_t *net_bytes; /* CONFIG_9P_MAX_MSG_SIZE-sized */ - struct lib9p_ctx ctx; + struct lib9p_reqctx ctx; }; /* base utilities *************************************************************/ @@ -195,6 +202,8 @@ COROUTINE lib9p_srv_read_cr(void *_srv) { /* write coroutine ************************************************************/ static void handle_Tversion(struct lib9p_req *ctx, struct lib9p_msg_Tversion *req, struct lib9p_msg_Rversion *resp); +static void handle_Tauth(struct lib9p_req *ctx, struct lib9p_msg_Tauth *req, struct lib9p_msg_Rauth *resp); +static void handle_Tattach(struct lib9p_req *ctx, struct lib9p_msg_Tattach *req, struct lib9p_msg_Rattach *resp); COROUTINE lib9p_srv_write_cr(void *_srv) { uint8_t net[CONFIG_9P_MAX_MSG_SIZE]; @@ -287,7 +296,7 @@ COROUTINE lib9p_srv_write_cr(void *_srv) { } write: - if (req.ctx.err_num || req.ctx.err_msg[0]) + if (lib9p_ctx_has_error(&req.ctx)) respond_error(&req); else { if (lib9p_marshal(&req.ctx, typ+1, req.tag, host_resp, net)) @@ -335,3 +344,66 @@ static void handle_Tversion(struct lib9p_req *ctx, struct lib9p_msg_Tversion *re ? CONFIG_9P_MAX_MSG_SIZE : req->max_msg_size; } + +static void handle_Tauth(struct lib9p_req *ctx, struct lib9p_msg_Tauth *UNUSED(req), struct lib9p_msg_Rauth *UNUSED(resp)) { + if (!ctx->parent_sess->parent_conn->parent_srv->auth) { + lib9p_error(&ctx->ctx, LINUX_EOPNOTSUPP, "authentication not required"); + return; + } + ctx->ctx.uid = req->uid; + ctx->ctx.uname = req->uname.utf8; + lib9p_error(&ctx->ctx, LINUX_EOPNOTSUPP, "TODO: auth not implemented"); + return; +} + +static void handle_Tattach(struct lib9p_req *ctx, struct lib9p_msg_Tattach *req, struct lib9p_msg_Rattach *resp) { + ctx->ctx.uid = req->uid; + ctx->ctx.uname = req->uname.utf8; + if (ctx->parent_sess->parent_conn->parent_srv->auth) { + /* + struct lib9p_srv_filehandle *fh = fidmap_get(req->afid); + if (!fh) + lib9p_error(&ctx->ctx, LINUX_EACCES, "FID provided as auth-file is not a valid FID"); + else if (fh->type != FH_AUTH) + lib9p_error(&ctx->ctx, LINUX_EACCES, "FID provided as auth-file is not an auth-file"); + else if (strcmp(fh->data.auth.uname, req->uname.utf8) != 0) + lib9p_errorf(&ctx->ctx, LINUX_EACCES, "FID provided as auth-file is for user=\"%s\" and cannot be used for user=\"%s\"", + fh->data.auth.uname, req->uname.utf8); + else if (strcmp(fh->data.auth.aname, req->aname.utf8) != 0) + lib9p_errorf(&ctx->ctx, LINUX_EACCES, "FID provided as auth-file is for tree=\"%s\" and cannot be used for tree=\"%s\"", + fh->data.auth.aname, req->aname.utf8); + else if (!fh->data.auth.authenticated) + lib9p_error(&ctx->ctx, LINUX_EACCES, "FID provided as auth-file has not completed authentication"); + fh->refcount--; + if (lib9p_ctx_has_error(&ctx->ctx)) + return; + */ + lib9p_error(&ctx->ctx, LINUX_EOPNOTSUPP, "TODO: auth not implemented"); + return; + } else { + if (req->afid != LIB9P_NOFID) { + lib9p_error(&ctx->ctx, LINUX_EACCES, "FID provided as auth-file, but no auth-file is required"); + return; + } + } + + if (fidmap_load(&ctx->parent_sess->fids, req->fid)) { + lib9p_error(&ctx->ctx, LINUX_EBADF, "FID already in use"); + return; + } + + struct lib9p_srv_file rootdir = ctx->parent_sess->parent_conn->parent_srv->rootdir(&ctx->ctx, req->aname.utf8); + if (lib9p_ctx_has_error(&ctx->ctx.ctx)) + return; + + struct lib9p_srv_file *rootdir_ptr = fidmap_store(&ctx->parent_sess->fids, req->fid, rootdir); + if (!rootdir_ptr) { + lib9p_error(&ctx->ctx, LINUX_EMFILE, "too many open files"); + if (rootdir.free) + rootdir.free(ctx, rootdir.impldata); + return; + } + + resp->qid = rootdir.qid; + return; +} -- cgit v1.2.3-2-g168b