diff options
author | Luke T. Shumaker <lukeshu@lukeshu.com> | 2024-09-24 09:51:37 -0600 |
---|---|---|
committer | Luke T. Shumaker <lukeshu@lukeshu.com> | 2024-09-24 09:51:37 -0600 |
commit | d559c50a98e65ce889411b46ab108b392907e0f0 (patch) | |
tree | e38f83c11ae43f48aadead70f180be30df2bab78 /9p | |
parent | fbd945a03f2d706bb4d62aab6a607c1694d6d77a (diff) |
wip 9p
Diffstat (limited to '9p')
-rw-r--r-- | 9p/.editorconfig | 2 | ||||
-rw-r--r-- | 9p/.gitignore | 5 | ||||
-rw-r--r-- | 9p/9P2000.txt | 2 | ||||
-rw-r--r-- | 9p/9p.h | 15 | ||||
-rw-r--r-- | 9p/defs.c | 89 | ||||
-rwxr-xr-x | 9p/defs.gen (renamed from 9p/generate) | 249 | ||||
-rw-r--r-- | 9p/defs.h | 77 | ||||
-rw-r--r-- | 9p/internal.h | 114 | ||||
-rwxr-xr-x | 9p/linux-errno.h.gen | 15 | ||||
-rw-r--r-- | 9p/srv.h | 19 |
10 files changed, 407 insertions, 180 deletions
diff --git a/9p/.editorconfig b/9p/.editorconfig index 3de59d7..3f04564 100644 --- a/9p/.editorconfig +++ b/9p/.editorconfig @@ -1,3 +1,3 @@ -[{generate,linux-errno.h.gen}] +[{defs.gen,linux-errno.h.gen}] indent_style = space indent_size = 4 diff --git a/9p/.gitignore b/9p/.gitignore index c8e8c58..667e81c 100644 --- a/9p/.gitignore +++ b/9p/.gitignore @@ -1,5 +1,2 @@ -/9P2000.c -/9P2000.h -/9P2000.*.c -/9P2000.*.h +/defs-* /linux-errno.h diff --git a/9p/9P2000.txt b/9p/9P2000.txt index 778673f..5f93cdf 100644 --- a/9p/9P2000.txt +++ b/9p/9P2000.txt @@ -25,7 +25,7 @@ version "9P2000" # data (u32le `n`, then `n` bytes of data) -d = "len[8] len*(dat[1])" +d = "len[4] len*(dat[1])" # string (u16le `n`, then `n` bytes of UTF-8) s = "len[2] len*(utf8[1])" @@ -0,0 +1,15 @@ +/* 9p/9p.h - TODO + * + * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com> + * SPDX-Licence-Identifier: AGPL-3.0-or-later + */ + +#include "9p/linux-errno.h" +#include "9p/defs.h" +#include "9p/defs-9P2000.h" +/*#include "9p/defs-9P2000.u.h"*/ + +#define P9_TYPECODE_FOR_CTYPE(msg) _Generic((in_msg) \ + _P9_TYPECODE_FOR_CTYPE_9P2000(msg) \ + /* _P9_TYPECODE_FOR_CTYPE_9P2000u(msg) */ \ + ) diff --git a/9p/defs.c b/9p/defs.c new file mode 100644 index 0000000..886a0c1 --- /dev/null +++ b/9p/defs.c @@ -0,0 +1,89 @@ +/* 9p/defs.c - TODO + * + * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com> + * SPDX-Licence-Identifier: AGPL-3.0-or-later + */ + +#include <inttypes.h> /* for PRIu{n} */ +#include <stdarg.h> /* for va_* */ +#include <stdio.h> /* for vsnprintf() */ +#include <string.h> /* for strncpy() */ + +#include "9p/defs.h" +#include "9p/linux-errno.h" +#include "9p/internal.h" + +static struct version *versions[_P9_VER_CNT] = { + [P9_VER_9P2000] = &version_9P2000, + /* [P9_VER_9P2000u] = &version_9P2000u, */ +}; + +int p9_error(struct p9_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'; + ctx->err_num = linux_errno; + return -1; +} + +int p9_errorf(struct p9_ctx *ctx, uint32_t linux_errno, char const *fmt, ...) { + int n; + va_list args; + + va_start(args, fmt); + n = vsnprintf(ctx->err_msg, sizeof(ctx->err_msg), fmt, args); + va_end(args); + if ((size_t)(n+1) < sizeof(ctx->err_msg)) + memset(&ctx->err_msg[n+1], 0, sizeof(ctx->err_msg)-(n+1)); + + ctx->err_num = linux_errno; + + return -1; +} + +size_t p9_unmarshal_size(struct p9_ctx *ctx, uint8_t *net_bytes) { + /* Header */ + uint32_t net_len = decode_u32le(net_bytes); + if (net_len < 7) + return p9_error(ctx, LINUX_EBADMSG, "message is too short"); + uint8_t typ = net_bytes[4]; + uint32_t net_offset = 7; + + /* Body */ + if (!versions[ctx->version]->msgs[typ].unmarshal_extrasize) + return p9_errorf(ctx, LINUX_EOPNOTSUPP, "unknown message type %"PRIu8, typ); + size_t host_size = versions[ctx->version]->msgs[typ].unmarshal_basesize; + if (versions[ctx->version]->msgs[typ].unmarshal_extrasize(net_len, net_bytes, &net_offset, &host_size)) + return p9_error(ctx, LINUX_EBADMSG, "message is too short for content"); + + return host_size; +} + +uint8_t p9_unmarshal(struct p9_ctx *ctx, uint8_t *net_bytes, uint16_t *out_tag, void *out_body) { + /* Header */ + uint8_t typ = net_bytes[4]; + *out_tag = decode_u16le(&net_bytes[5]); + uint32_t net_offset = 7; + + /* Body */ + void *host_extra = out_body + versions[ctx->version]->msgs[typ].unmarshal_basesize; + if (versions[ctx->version]->msgs[typ].unmarshal(net_bytes, &net_offset, &host_extra, out_body)) + return p9_error(ctx, LINUX_EBADMSG, "message contains invalid UTF-8"); + + return typ; +} + +uint32_t _p9_marshal(struct p9_ctx *ctx, uint8_t typ, uint16_t msgid, void *body, uint8_t *out_bytes) { + /* Header */ + out_bytes[4] = typ; + encode_u16le(msgid, &out_bytes[5]); + uint32_t net_offset = 7; + + /* Body */ + if (versions[ctx->version]->msgs[typ].marshal(ctx, body, out_bytes, &net_offset)) + return 0; + + /* Header, again */ + encode_u32le(net_offset, out_bytes); + + return net_offset; +} diff --git a/9p/generate b/9p/defs.gen index 6456609..1a8dc69 100755 --- a/9p/generate +++ b/9p/defs.gen @@ -1,14 +1,15 @@ #!/usr/bin/env python -# 9p/generate - Generate C marshalers/unmarshalers for a .txt file +# 9p/defs.gen - Generate C marshalers/unmarshalers for a .txt file # defining a 9P protocol variant. # # Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com> # SPDX-Licence-Identifier: AGPL-3.0-or-later import enum +import os.path import re -PROGRAM = "./9p/generate" +PROGRAM = "./9p/defs.gen" # Parse the *.txt ############################################################## @@ -176,9 +177,7 @@ def c_typename(idprefix: str, typ: Atom | Struct) -> str: def gen_h(txtname: str, idprefix: str, structs: list[Struct]) -> str: guard = ( - "_" - + txtname.replace(".txt", ".h").upper().replace("/", "_").replace(".", "_") - + "_" + f"_{txtname.replace('.txt', '.h').upper().replace('/', '_').replace('.', '_')}_" ) ret = f"""/* Generated by `{PROGRAM} {txtname}`. DO NOT EDIT! */ @@ -231,34 +230,14 @@ def gen_h(txtname: str, idprefix: str, structs: list[Struct]) -> str: ret += "};\n" ret += f""" -/* functions ******************************************************************/ - -/** - * @param net_bytes : the complete request, starting with the "size[4]" - * @param out_tag : the message-ID tag - * @param out_body : a pointer that get set to the parsed body, whose - * type is known by the return value, will need to - * be free()d - * @return -{idprefix.upper()}E{{error}} on error, {idprefix.upper()}TYP_{{type}} on success - */ -int {idprefix}unmarshal_msg(uint8_t *net_bytes, uint16_t *out_tag, void **out_body); - -/** - * @param uint16_t in_msgid : the message-ID tag - * @param struct {idprefix}msg_{{type}} in_msg : the message to encode - * @param uint8_t *out_buf : the buffer to encode to - * @return uint32_t : the encoded length - */ -#define {idprefix}marshal_msg(in_msgid, in_msg, out_buf) _Generic((in_msg)""" - for msg in structs: - if msg.msgid is None: - continue - ret += f", \\\n\t\t{c_typename(idprefix, msg)}: _{idprefix}marshal_{msg.name}(in_msgid, in_msg, out_buf)" - ret += "\\\n\t)(in_msg)\n" +/* tables *********************************************************************/ + +#define _P9_TYPECODE_FOR_CTYPE(msg) """ for msg in structs: if msg.msgid is None: continue - ret += f"uint32_t _{idprefix}marshal_{msg.name}(uint16_t in_msgid, {c_typename(idprefix, msg)} in_msg, uint8_t *out_buf);\n" + ret += f", \\\n\t\t{c_typename(idprefix, msg)}: {idprefix.upper()}TYP_{msg.name}" + ret += "\n" ret += "\n" ret += f"#endif /* {guard} */\n" @@ -266,71 +245,16 @@ int {idprefix}unmarshal_msg(uint8_t *net_bytes, uint16_t *out_tag, void **out_bo def gen_c(txtname: str, idprefix: str, structs: list[Struct]) -> str: + txtdir, txtbase = os.path.split(txtname) + header = os.path.join(txtdir, "defs-" + txtbase.replace(".txt", ".h")) ret = f"""/* Generated by `{PROGRAM} {txtname}`. DO NOT EDIT! */ #include <stdbool.h> #include <stdint.h> #include <stdlib.h> /* for malloc() */ -#include "{txtname.replace('.txt', '.h')}" -""" - - # basic utilities ########################################################## - ret += """ -/* basic utilities ************************************************************/ - -#define UNUSED(name) /* name __attribute__ ((unused)) */ - -static inline uint8_t decode_u8le(uint8_t *in) { - return in[0]; -} -static inline uint16_t decode_u16le(uint8_t *in) { - return (((uint16_t)(in[0])) << 0) - | (((uint16_t)(in[1])) << 8) - ; -} -static inline uint32_t decode_u32le(uint8_t *in) { - return (((uint32_t)(in[0])) << 0) - | (((uint32_t)(in[1])) << 8) - | (((uint32_t)(in[2])) << 16) - | (((uint32_t)(in[3])) << 24) - ; -} -static inline uint64_t decode_u64le(uint8_t *in) { - return (((uint64_t)(in[0])) << 0) - | (((uint64_t)(in[1])) << 8) - | (((uint64_t)(in[2])) << 16) - | (((uint64_t)(in[3])) << 24) - | (((uint64_t)(in[4])) << 32) - | (((uint64_t)(in[5])) << 40) - | (((uint64_t)(in[6])) << 48) - | (((uint64_t)(in[7])) << 56) - ; -} - -static inline void encode_u8le(uint8_t in, uint8_t *out) { - out[0] = in; -} -static inline void encode_u16le(uint16_t in, uint8_t *out) { - out[0] = (uint8_t)((in >> 0) & 0xFF); - out[1] = (uint8_t)((in >> 8) & 0xFF); -} -static inline void encode_u32le(uint32_t in, uint8_t *out) { - out[0] = (uint8_t)((in >> 0) & 0xFF); - out[1] = (uint8_t)((in >> 8) & 0xFF); - out[2] = (uint8_t)((in >> 16) & 0xFF); - out[3] = (uint8_t)((in >> 24) & 0xFF); -} -static inline void encode_u64le(uint64_t in, uint8_t *out) { - out[0] = (uint8_t)((in >> 0) & 0xFF); - out[1] = (uint8_t)((in >> 8) & 0xFF); - out[2] = (uint8_t)((in >> 16) & 0xFF); - out[3] = (uint8_t)((in >> 24) & 0xFF); - out[4] = (uint8_t)((in >> 32) & 0xFF); - out[5] = (uint8_t)((in >> 40) & 0xFF); - out[6] = (uint8_t)((in >> 48) & 0xFF); - out[7] = (uint8_t)((in >> 56) & 0xFF); -} +#include "{header}" +#include "9p/internal.h" """ def used(arg: str) -> str: @@ -343,7 +267,6 @@ static inline void encode_u64le(uint64_t in, uint8_t *out) { ret += """ /* checksize_* ****************************************************************/ -typedef bool (*_checksize_fn_t)(uint32_t net_len, uint8_t *net_bytes, uint32_t *mut_net_offset, size_t *mut_host_extra); static inline bool _checksize_list(size_t cnt, _checksize_fn_t fn, size_t host_size, uint32_t net_len, uint8_t *net_bytes, uint32_t *mut_net_offset, size_t *mut_host_extra) { for (size_t i = 0; i < cnt; i++) @@ -352,6 +275,7 @@ static inline bool _checksize_list(size_t cnt, _checksize_fn_t fn, size_t host_s return true; return false; } + static inline bool checksize_1(uint32_t net_len, uint8_t *UNUSED(net_bytes), uint32_t *mut_net_offset, size_t *UNUSED(mut_host_extra)) { return __builtin_add_overflow(*mut_net_offset, 1, mut_net_offset) || net_len < *mut_net_offset; } @@ -364,12 +288,14 @@ static inline bool checksize_4(uint32_t net_len, uint8_t *UNUSED(net_bytes), uin static inline bool checksize_8(uint32_t net_len, uint8_t *UNUSED(net_bytes), uint32_t *mut_net_offset, size_t *UNUSED(mut_host_extra)) { return __builtin_add_overflow(*mut_net_offset, 8, mut_net_offset) || net_len < *mut_net_offset; } + """ for struct in structs: + inline = ' inline' if struct.msgid is None else '' argfn = used if struct.members else unused - ret += f"static inline bool checksize_{struct.name}(uint32_t {argfn('net_len')}, uint8_t *{argfn('net_bytes')}, uint32_t *{argfn('mut_net_offset')}, size_t *{argfn('mut_host_extra')}) {{\n" + ret += f"static{inline} bool checksize_{struct.name}(uint32_t {argfn('net_len')}, uint8_t *{argfn('net_bytes')}, uint32_t *{argfn('mut_net_offset')}, size_t *{argfn('mut_host_extra')}) {{" if len(struct.members) == 0: - ret += "\treturn false;\n" + ret += "\n\treturn false;\n" ret += "}\n" continue prefix0 = "\treturn " @@ -397,132 +323,116 @@ static inline bool checksize_8(uint32_t net_len, uint8_t *UNUSED(net_bytes), uin /* unmarshal_* ****************************************************************/ /* checksize_XXX() should be called before unmarshal_XXX(). */ -static inline void unmarshal_1(uint8_t *net_bytes, uint32_t *mut_net_offset, void **UNUSED(mut_host_extra), uint8_t *out) { +static inline bool unmarshal_1(uint8_t *net_bytes, uint32_t *mut_net_offset, void **UNUSED(mut_host_extra), uint8_t *out) { *out = decode_u8le(&net_bytes[*mut_net_offset]); *mut_net_offset += 1; + return false; } -static inline void unmarshal_2(uint8_t *net_bytes, uint32_t *mut_net_offset, void **UNUSED(mut_host_extra), uint16_t *out) { +static inline bool unmarshal_2(uint8_t *net_bytes, uint32_t *mut_net_offset, void **UNUSED(mut_host_extra), uint16_t *out) { *out = decode_u16le(&net_bytes[*mut_net_offset]); *mut_net_offset += 2; + return false; } -static inline void unmarshal_4(uint8_t *net_bytes, uint32_t *mut_net_offset, void **UNUSED(mut_host_extra), uint32_t *out) { +static inline bool unmarshal_4(uint8_t *net_bytes, uint32_t *mut_net_offset, void **UNUSED(mut_host_extra), uint32_t *out) { *out = decode_u32le(&net_bytes[*mut_net_offset]); *mut_net_offset += 4; + return false; } -static inline void unmarshal_8(uint8_t *net_bytes, uint32_t *mut_net_offset, void **UNUSED(mut_host_extra), uint64_t *out) { +static inline bool unmarshal_8(uint8_t *net_bytes, uint32_t *mut_net_offset, void **UNUSED(mut_host_extra), uint64_t *out) { *out = decode_u64le(&net_bytes[*mut_net_offset]); *mut_net_offset += 8; + return false; } + """ for struct in structs: argfn = used if struct.members else unused - ret += f"static inline void unmarshal_{struct.name}(uint8_t *{argfn('net_bytes')}, uint32_t *{argfn('mut_net_offset')}, void **{argfn('mut_host_extra')}, {c_typename(idprefix, struct)} *{argfn('out')}) {{" - if len(struct.members) == 0: - ret += "}\n" - continue - ret += "\n" + ret += f"static inline bool unmarshal_{struct.name}(uint8_t *{argfn('net_bytes')}, uint32_t *{argfn('mut_net_offset')}, void **{argfn('mut_host_extra')}, {c_typename(idprefix, struct)} *{argfn('out')}) {{\n" for member in struct.members: if member.cnt: ret += f"\tout->{member.name} = *mut_host_extra;\n" ret += f"\t*mut_host_extra += sizeof(*out->{member.name}) * out->{member.cnt};\n" ret += f"\tfor (typeof(out->{member.cnt}) i = 0; i < out->{member.cnt}; i++)\n" - ret += f"\t\tunmarshal_{member.typ.name}(net_bytes, mut_net_offset, mut_host_extra, &(out->{member.name}[i]));\n" + ret += f"\t\tif (unmarshal_{member.typ.name}(net_bytes, mut_net_offset, mut_host_extra, &(out->{member.name}[i])))\n" + ret += f"\t\t\treturn true;\n" + if struct.name == "s": + ret += f"\tif (!is_valid_utf8_without_nul(out->{member.name}, out->{member.cnt}))\n" + ret += f"\t\treturn true;\n" + ret += f"\tout->{member.name}[out->{member.cnt}] = '\\0';\n" else: - ret += f"\tunmarshal_{member.typ.name}(net_bytes, mut_net_offset, mut_host_extra, &(out->{member.name}));\n" + ret += f"\tif (unmarshal_{member.typ.name}(net_bytes, mut_net_offset, mut_host_extra, &(out->{member.name})))\n" + ret += f"\t\treturn true;\n" + ret += "\treturn false;\n" ret += "}\n" - # unmarshal_msg ############################################################ - ret += f""" -/* unmarshal_msg **************************************************************/ - -int {idprefix}unmarshal_msg(uint8_t *net_bytes, uint16_t *out_tag, void **out_body) {{ - uint32_t net_len = decode_u32le(net_bytes); - if (net_len < 7) - return -LINUX_EWRONGSIZE; - uint8_t typ = net_bytes[4]; - *out_tag = decode_u16le(&net_bytes[5]); - - uint32_t net_offset = 7; - size_t host_size; - void *host_extra; - switch (typ) {{ -""" - for msg in structs: - if msg.msgid is None: - continue - ret += f"\tcase {idprefix.upper()}TYP_{msg.name}:\n" - ret += f"\t\thost_size = sizeof({c_typename(idprefix, msg)});\n" - ret += f"\t\tif (checksize_{msg.name}(net_len, net_bytes, &net_offset, &host_size))\n" - ret += "\t\t\treturn -LINUX_EWRONGSIZE;\n" - ret += "\n" - ret += "\t\t*out_body = malloc(host_size);" - ret += "\n" - ret += "\t\tnet_offset = 7;\n" - ret += f"\t\thost_extra = *out_body + sizeof({c_typename(idprefix, msg)});\n" - ret += f"\t\tunmarshal_{msg.name}(net_bytes, &net_offset, &host_extra, *out_body);\n" - ret += "\n" - ret += "\t\tbreak;\n" - ret += """ - default: - return -LINUX_EOPNOTSUPP; - } - return typ; -} -""" - # marshal_* ################################################################ ret += """ /* marshal_* ******************************************************************/ -static inline void marshal_1(uint8_t val, uint8_t *out_net_bytes, uint32_t *mut_net_offset) { - out_net_bytes[*mut_net_offset] = val; +static inline bool marshal_1(struct p9_ctx *ctx, uint8_t val, uint8_t *out_net_bytes, uint32_t *mut_net_offset) { + if (*mut_net_offset + 1 > ctx->max_msg_size) + return true; + out_net_bytes[*mut_net_offset] = val; *mut_net_offset += 1; + return false; } -static inline void marshal_2(uint16_t val, uint8_t *out_net_bytes, uint32_t *mut_net_offset) { +static inline bool marshal_2(struct p9_ctx *ctx, uint16_t val, uint8_t *out_net_bytes, uint32_t *mut_net_offset) { + if (*mut_net_offset + 2 > ctx->max_msg_size) + return true; encode_u16le(val, &out_net_bytes[*mut_net_offset]); *mut_net_offset += 2; + return false; } -static inline void marshal_4(uint32_t val, uint8_t *out_net_bytes, uint32_t *mut_net_offset) { +static inline bool marshal_4(struct p9_ctx *ctx, uint32_t val, uint8_t *out_net_bytes, uint32_t *mut_net_offset) { + if (*mut_net_offset + 4 > ctx->max_msg_size) + return true; encode_u32le(val, &out_net_bytes[*mut_net_offset]); *mut_net_offset += 4; + return false; } -static inline void marshal_8(uint64_t val, uint8_t *out_net_bytes, uint32_t *mut_net_offset) { +static inline bool marshal_8(struct p9_ctx *ctx, uint64_t val, uint8_t *out_net_bytes, uint32_t *mut_net_offset) { + if (*mut_net_offset + 8 > ctx->max_msg_size) + return true; encode_u64le(val, &out_net_bytes[*mut_net_offset]); *mut_net_offset += 8; + return false; } """ for struct in structs: argfn = used if struct.members else unused - ret += f"static inline void marshal_{struct.name}({c_typename(idprefix, struct)} {argfn('val')}, uint8_t *{argfn('out_net_bytes')}, uint32_t *{argfn('mut_net_offset')}) {{" - if len(struct.members) == 0: - ret += "}\n" - continue - ret += "\n" + ret += f"static inline bool marshal_{struct.name}(struct p9_ctx *{argfn('ctx')}, {c_typename(idprefix, struct)} {argfn('val')}, uint8_t *{argfn('out_net_bytes')}, uint32_t *{argfn('mut_net_offset')}) {{\n" for member in struct.members: if member.cnt: ret += f"\tfor (typeof(val.{member.cnt}) i = 0; i < val.{member.cnt}; i++)\n" - ret += f"\t\tmarshal_{member.typ.name}(val.{member.name}[i], out_net_bytes, mut_net_offset);\n" + ret += f"\t\tif (marshal_{member.typ.name}(ctx, val.{member.name}[i], out_net_bytes, mut_net_offset))\n" + ret += f"\t\t\treturn true;\n" else: - ret += f"\tmarshal_{member.typ.name}(val.{member.name}, out_net_bytes, mut_net_offset);\n" + ret += f"\tif (marshal_{member.typ.name}(ctx, val.{member.name}, out_net_bytes, mut_net_offset))\n" + ret += f"\t\treturn true;\n" + ret += "\treturn false;\n" ret += "}\n" - # _marshal_msg_* ########################################################### + # tables ################################################################### ret += """ -/* _marshal_msg_* *************************************************************/ +/* tables *********************************************************************/ """ for msg in structs: if msg.msgid is None: continue - ret += f"uint32_t _{idprefix}marshal_{msg.name}(uint16_t in_msgid, {c_typename(idprefix, msg)} in_msg, uint8_t *out_buf) {{\n" - ret += "\tuint32_t offset = 4;\n" - ret += f"\tmarshal_1({idprefix.upper()}TYP_{msg.name}, out_buf, &offset);\n" - ret += "\tmarshal_2(in_msgid, out_buf, &offset);\n" - ret += f"\tmarshal_{msg.name}(in_msg, out_buf, &offset);\n" - ret += "\tencode_u32le(offset, out_buf);\n" - ret += "\treturn offset;\n" - ret += "}\n" - ret += "\n" + ret += f"static bool _unmarshal_{msg.name}(uint8_t *net_bytes, uint32_t *mut_net_offset, void **mut_host_extra, void *out) {{ return unmarshal_{msg.name}(net_bytes, mut_net_offset, mut_host_extra, ({c_typename(idprefix, msg)} *)out); }}\n" + for msg in structs: + if msg.msgid is None: + continue + ret += f"static bool _marshal_{msg.name}(struct p9_ctx *ctx, void *val, uint8_t *out_net_bytes, uint32_t *mut_net_offset) {{ return marshal_{msg.name}(ctx, *(({c_typename(idprefix, msg)} *)val), out_net_bytes, mut_net_offset); }}\n" + ret += "struct version version_9P2000 = {\n" + ret += "\t.msgs = {\n" + for msg in structs: + if msg.msgid is None: + continue + ret += f"\t\t[{idprefix.upper()}TYP_{msg.name}] = {{ .unmarshal_basesize=sizeof({c_typename(idprefix, msg)}), .unmarshal_extrasize=checksize_{msg.name}, .unmarshal=_unmarshal_{msg.name}, .marshal=_marshal_{msg.name} }},\n" + ret += "\t},\n" + ret += "};\n" ############################################################################ return ret @@ -534,8 +444,13 @@ if __name__ == "__main__": import sys for txtname in sys.argv[1:]: + txtdir, txtbase = os.path.split(txtname) version, structs = parse_file(txtname) - with open(txtname.replace(".txt", ".h"), "w") as fh: + with open( + os.path.join(txtdir, "defs-" + txtbase.replace(".txt", ".h")), "w" + ) as fh: fh.write(gen_h(txtname, "p9_", structs)) - with open(txtname.replace(".txt", ".c"), "w") as fh: + with open( + os.path.join(txtdir, "defs-" + txtbase.replace(".txt", ".c")), "w" + ) as fh: fh.write(gen_c(txtname, "p9_", structs)) diff --git a/9p/defs.h b/9p/defs.h new file mode 100644 index 0000000..20a6411 --- /dev/null +++ b/9p/defs.h @@ -0,0 +1,77 @@ +/* 9p/defs.h - TODO + * + * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com> + * SPDX-Licence-Identifier: AGPL-3.0-or-later + */ + +#ifndef _9P_DEFS_H_ +#define _9P_DEFS_H_ + +#include <stdint.h> + +#define P9_NOTAG ((uint16_t)~0U) +#define P9_NOFID ((uint32_t)~0U) + +enum p9_version { + /* P9_VER_9P1, */ + P9_VER_9P2000, + /*P9_VER_9P2000_u,*/ + /*P9_VER_9P2000_L,*/ + /*P9_VER_9P2000_e,*/ + _P9_VER_CNT, +}; + +struct p9_ctx { + enum p9_version version; + uint32_t max_msg_size; + + uint32_t err_num; + char err_msg[256]; /* I chose 256 arbitrarily. */ +}; + +/** Write an static error into ctx, return -1. */ +int p9_error(struct p9_ctx *ctx, uint32_t linux_errno, char const *msg); +/** Write a printf-style error into ctx, return -1. */ +int p9_errorf(struct p9_ctx *ctx, uint32_t linux_errno, char const *fmt, ...); + +/** + * Return how much space the message at net_bytes will take when + * unmarshaled. This number may be larger than net_bytes due to (1) + * struct padding, (2) nul-terminator byes for strings. + * + * Emits an error (return -1, set ctx->err_num and ctx->err_msg) if + * either the message type is unknown or if net_bytes is too short for + * that message type. + * + * @param net_bytes : the complete request, starting with the "size[4]" + * @return required size, or -1 on error + */ +size_t p9_unmarshal_size(struct p9_ctx *ctx, uint8_t *net_bytes); + +/** + * Unmarshal the 9P message `net_bytes` into the C struct `out_body`. + * + * Emits an error (return 0, set ctx->err_num and ctx->err_msg) if a + * string contains invalid UTF-8 or a nul-byte. + * + * @param net_bytes : the complete message, starting with the "size[4]" + * @param out_tag : the message-ID tag + * @param out_body : the message body, must be at least p9_unmarshal_size() bytes + * @return the message type, or -1 on error + */ +uint8_t p9_unmarshal(struct p9_ctx *ctx, uint8_t *net_bytes, uint16_t *out_tag, void *out_body); + +/** + * Marshal a `struct p9_msg_{type}` structure into a byte-array. + * + * @param struct p9_ctx *ctx : a request context + * @param uint16_t msgid : the message-ID tag + * @param struct p9_msg_{type} msg : the message to encode + * + * @param uint8_t *out_bytes : the buffer to encode to, must be at be at least ctx->max_msg_size bytes + * @return uint32_t : the encoded length, or -1 on error + */ +#define p9_marshal(ctx, msgid, msg, out_bytes) _p9_marshal(ctx, P9_TYPECODE_FOR_CTYPE(msg), msgid, &(msg), out_bytes) +uint32_t _p9_marshal(struct p9_ctx *ctx, uint8_t typ, uint16_t msgid, void *body, uint8_t *out_bytes); + +#endif /* _9P_DEFS_H_ */ diff --git a/9p/internal.h b/9p/internal.h new file mode 100644 index 0000000..1bc0e92 --- /dev/null +++ b/9p/internal.h @@ -0,0 +1,114 @@ +/* 9p/internal.h - TODO + * + * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com> + * SPDX-Licence-Identifier: AGPL-3.0-or-later + */ + +#ifndef _9P_INTERNAL_H_ +#define _9P_INTERNAL_H_ + +#include <stdint.h> +#include <stdbool.h> + +#include "9p/defs.h" + +/* C language *****************************************************************/ + +#define UNUSED(name) /* name __attribute__ ((unused)) */ + +/* vtables ********************************************************************/ + +typedef bool (*_checksize_fn_t)(uint32_t net_len, uint8_t *net_bytes, uint32_t *mut_net_offset, size_t *mut_host_extra); +typedef bool (*_unmarshal_fn_t)(uint8_t *net_bytes, uint32_t *mut_net_offset, void **mut_host_extra, void *out); +typedef bool (*_marshal_fn_t)(struct p9_ctx *ctx, void *val, uint8_t *out_net_bytes, uint32_t *mut_net_offset); + +struct msg_vtable { + size_t unmarshal_basesize; + _checksize_fn_t unmarshal_extrasize; + _unmarshal_fn_t unmarshal; + _marshal_fn_t marshal; +}; + +struct version { + struct msg_vtable msgs[0xFF]; +}; + +extern struct version version_9P2000; +/*extern struct version version_9P2000u; */ + +/* unmarshal utilities ********************************************************/ + +static inline uint8_t decode_u8le(uint8_t *in) { + return in[0]; +} +static inline uint16_t decode_u16le(uint8_t *in) { + return (((uint16_t)(in[0])) << 0) + | (((uint16_t)(in[1])) << 8) + ; +} +static inline uint32_t decode_u32le(uint8_t *in) { + return (((uint32_t)(in[0])) << 0) + | (((uint32_t)(in[1])) << 8) + | (((uint32_t)(in[2])) << 16) + | (((uint32_t)(in[3])) << 24) + ; +} +static inline uint64_t decode_u64le(uint8_t *in) { + return (((uint64_t)(in[0])) << 0) + | (((uint64_t)(in[1])) << 8) + | (((uint64_t)(in[2])) << 16) + | (((uint64_t)(in[3])) << 24) + | (((uint64_t)(in[4])) << 32) + | (((uint64_t)(in[5])) << 40) + | (((uint64_t)(in[6])) << 48) + | (((uint64_t)(in[7])) << 56) + ; +} + +static inline bool is_valid_utf8_without_nul(uint8_t *str, size_t len) { + uint8_t mask; + uint8_t chlen; + for (size_t pos = 0; pos < len;) { + if ((str[pos] & 0b10000000) == 0b00000000) { mask = 0b01111111; chlen = 1; } + else if ((str[pos] & 0b11100000) == 0b11000000) { mask = 0b00011111; chlen = 2; } + else if ((str[pos] & 0b11110000) == 0b11100000) { mask = 0b00001111; chlen = 3; } + else if ((str[pos] & 0b11111000) == 0b11110000) { mask = 0b00000111; chlen = 4; } + else return false; + if (pos + chlen > len || (str[pos] & mask) == 0) return false; + switch (chlen) { + case 4: if ((str[pos+3] & 0b11000000) != 0b10000000) return false; /* fallthrough */ + case 3: if ((str[pos+2] & 0b11000000) != 0b10000000) return false; /* fallthrough */ + case 2: if ((str[pos+1] & 0b11000000) != 0b10000000) return false; /* fallthrough */ + } + pos += chlen; + } + return true; +} + +/* marshal utilities **********************************************************/ + +static inline void encode_u8le(uint8_t in, uint8_t *out) { + out[0] = in; +} +static inline void encode_u16le(uint16_t in, uint8_t *out) { + out[0] = (uint8_t)((in >> 0) & 0xFF); + out[1] = (uint8_t)((in >> 8) & 0xFF); +} +static inline void encode_u32le(uint32_t in, uint8_t *out) { + out[0] = (uint8_t)((in >> 0) & 0xFF); + out[1] = (uint8_t)((in >> 8) & 0xFF); + out[2] = (uint8_t)((in >> 16) & 0xFF); + out[3] = (uint8_t)((in >> 24) & 0xFF); +} +static inline void encode_u64le(uint64_t in, uint8_t *out) { + out[0] = (uint8_t)((in >> 0) & 0xFF); + out[1] = (uint8_t)((in >> 8) & 0xFF); + out[2] = (uint8_t)((in >> 16) & 0xFF); + out[3] = (uint8_t)((in >> 24) & 0xFF); + out[4] = (uint8_t)((in >> 32) & 0xFF); + out[5] = (uint8_t)((in >> 40) & 0xFF); + out[6] = (uint8_t)((in >> 48) & 0xFF); + out[7] = (uint8_t)((in >> 56) & 0xFF); +} + +#endif /* _9P_INTERNAL_H_ */ diff --git a/9p/linux-errno.h.gen b/9p/linux-errno.h.gen index 749cd8e..b896384 100755 --- a/9p/linux-errno.h.gen +++ b/9p/linux-errno.h.gen @@ -3,26 +3,27 @@ def print_errnos(txtlists: list[str]) -> None: print( - f"/* Generated by `./9p/linux-errno.h.gen {' '.join(txtlists)}`. DO NOT EDIT! */" + f"/* 9p/linux-errno.h - Generated by `./9p/linux-errno.h.gen {' '.join(txtlists)}`. DO NOT EDIT! */" ) - errnos: dict[str, int] = {} + errnos: dict[str, tuple[int, str]] = {} for txtlist in sys.argv[1:]: with open(txtlist, "r") as fh: for line in fh: if line.startswith("#"): print(f"/* {line[1:].strip()} */") continue - _num, name = line.split(maxsplit=1) + _num, name, desc = line.split(maxsplit=2) num = int(_num) - name = name.strip() - errnos[name] = int(num) - namelen = max(len(name) for name in errnos.keys()) + desc = desc.strip() + errnos[name] = (num, desc) print() print("#ifndef _9P_LINUX_ERRNO_H_") print("#define _9P_LINUX_ERRNO_H_") print() + namelen = max(len(name) for name in errnos.keys()) + numlen = max(len(str(num)) for (num, desc) in errnos.values()) for name in errnos: - print(f"#define LINUX_{name.ljust(namelen)} {errnos[name]}") + print(f"#define LINUX_{name.ljust(namelen)} {str(errnos[name][0]).rjust(numlen)} /* {errnos[name][1]} */") print() print("#endif /* _9P_LINUX_ERRNO_H_ */") @@ -3,6 +3,25 @@ #include "coroutine.h" +#define 9P_DEFAULT_PORT 564 + +/** + * The default here is the same as in Plan 9 4e's lib9p. It's sized + * so that a Twrite message can return 8KiB of data; it uses the + * default (1024*8)+24 with the comment that "24" is "ample room for + * Twrite/Rread header (iounit)". In fact, the Twrite header is only + * 23 bytes ("size[4] Twrite[1] tag[2] fid[4] offset[8] count[4]") and + * the Rread header is even shorter at 11 bytes ("size[4] Rread[1] + * tag[2] count[4]"), so "24" appears to be the size of the Twrite + * header rounded up to a nice round number. + * + * In older versions of 9P ("9P1"), the max message size was defined + * as part of the protocol specification rather than negotiated. In + * Plan 9 1e it was (1024*8)+128, and was bumped to (1024*8)+160 in 2e + * and 3e. + */ +#define 9P_DEFAULT_MAX_MSG_SIZE ((1024*8)+24) + COROUTINE net9p_cr(void *); #endif /* _NET9P_H_ */ |