summaryrefslogtreecommitdiff
path: root/9p
diff options
context:
space:
mode:
authorLuke T. Shumaker <lukeshu@lukeshu.com>2024-09-24 09:51:37 -0600
committerLuke T. Shumaker <lukeshu@lukeshu.com>2024-09-24 09:51:37 -0600
commitd559c50a98e65ce889411b46ab108b392907e0f0 (patch)
treee38f83c11ae43f48aadead70f180be30df2bab78 /9p
parentfbd945a03f2d706bb4d62aab6a607c1694d6d77a (diff)
wip 9p
Diffstat (limited to '9p')
-rw-r--r--9p/.editorconfig2
-rw-r--r--9p/.gitignore5
-rw-r--r--9p/9P2000.txt2
-rw-r--r--9p/9p.h15
-rw-r--r--9p/defs.c89
-rwxr-xr-x9p/defs.gen (renamed from 9p/generate)249
-rw-r--r--9p/defs.h77
-rw-r--r--9p/internal.h114
-rwxr-xr-x9p/linux-errno.h.gen15
-rw-r--r--9p/srv.h19
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])"
diff --git a/9p/9p.h b/9p/9p.h
new file mode 100644
index 0000000..63fbd04
--- /dev/null
+++ b/9p/9p.h
@@ -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_ */")
diff --git a/9p/srv.h b/9p/srv.h
index f28a525..32a2e30 100644
--- a/9p/srv.h
+++ b/9p/srv.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_ */