summaryrefslogtreecommitdiff
path: root/lib9p
diff options
context:
space:
mode:
authorLuke T. Shumaker <lukeshu@lukeshu.com>2024-09-27 17:25:36 -0600
committerLuke T. Shumaker <lukeshu@lukeshu.com>2024-09-27 17:25:36 -0600
commite5e15c04bc58c34906e6d7cfcbad68d1a5617563 (patch)
tree580f5fb0fafc7e974c969fc8aae229205c836195 /lib9p
parent71e1a86a033c380f85dd300d788af63bfef25bab (diff)
wip
Diffstat (limited to 'lib9p')
-rw-r--r--lib9p/.editorconfig2
-rw-r--r--lib9p/.gitignore2
-rw-r--r--lib9p/9P2000.e.txt4
-rw-r--r--lib9p/9P2000.u.txt25
-rw-r--r--lib9p/9p.c (renamed from lib9p/defs.c)39
-rw-r--r--lib9p/9p.h15
-rw-r--r--lib9p/CMakeLists.txt34
-rwxr-xr-xlib9p/defs.gen456
-rw-r--r--lib9p/defs.h74
-rw-r--r--lib9p/include/lib9p/9p.h72
-rw-r--r--lib9p/include/lib9p/_types.h285
-rwxr-xr-xlib9p/include/lib9p/linux-errno.h.gen (renamed from lib9p/linux-errno.h.gen)0
-rw-r--r--lib9p/include/lib9p/srv.h16
-rw-r--r--lib9p/internal.h79
-rw-r--r--lib9p/misc.txt24
-rw-r--r--lib9p/srv.h16
-rw-r--r--lib9p/types.c1331
-rwxr-xr-xlib9p/types.gen611
18 files changed, 2452 insertions, 633 deletions
diff --git a/lib9p/.editorconfig b/lib9p/.editorconfig
index 3f04564..5c79099 100644
--- a/lib9p/.editorconfig
+++ b/lib9p/.editorconfig
@@ -1,3 +1,3 @@
-[{defs.gen,linux-errno.h.gen}]
+[{types.gen,linux-errno.h.gen}]
indent_style = space
indent_size = 4
diff --git a/lib9p/.gitignore b/lib9p/.gitignore
deleted file mode 100644
index 667e81c..0000000
--- a/lib9p/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-/defs-*
-/linux-errno.h
diff --git a/lib9p/9P2000.e.txt b/lib9p/9P2000.e.txt
index 22f056e..e60bce0 100644
--- a/lib9p/9P2000.e.txt
+++ b/lib9p/9P2000.e.txt
@@ -8,9 +8,11 @@
# https://github.com/cloudozer/ling/blob/master/doc/9p2000e.md
version "9P2000.e"
+from 9P2000.txt import *
+
150/Tsession = "key[8]"
151/Rsession = ""
152/Tsread = "fid[4] nwname[2] nwname*(wname[s])"
153/Rsread = "data[d]"
-154/Tswrite = "Tswrite tag[2] fid[4] nwname[2] nwname*(wname[s]) data[d]"
+154/Tswrite = "fid[4] nwname[2] nwname*(wname[s]) data[d]"
155/Rswrite = "count[4]"
diff --git a/lib9p/9P2000.u.txt b/lib9p/9P2000.u.txt
index 60d2b11..1fa71fb 100644
--- a/lib9p/9P2000.u.txt
+++ b/lib9p/9P2000.u.txt
@@ -18,3 +18,28 @@ stat += "file_extension[s]"
Tauth += "n_uname[4]"
Rerror += "errno[4]"
+
+# # qid.types
+# QTDIR = 1<<7
+# QTAPPEND = 1<<6
+# QTEXCL = 1<<5
+# QTMOUNT = 1<<4 # been around forever, but undocumented?
+# QTAUTH = 1<<3
+# QTTMP = 1<<2 # added to Plan 9 2003-12
+# QTSYMLINK = 1<<1 # .u
+# QTFILE = 1<<0
+#
+# DMDIR = 1<<31
+# DMAPPEND = 1<<30
+# DMEXCL = 1<<29
+# DMMOUNT = 1<<28
+# DMAUTH = 1<<27
+# DMTMP = 1<<26
+# # = 1<<25
+# # = 1<<24
+# DMDEVICE = 1<<23 # .u
+# # = 1<<22
+# DMNAMEDPIPE = 1<<21 # .u
+# DMSOCKET = 1<<20 # .u
+# DMSETUID = 1<<19 # .u
+# DMSETGID = 1<<18 # .u
diff --git a/lib9p/defs.c b/lib9p/9p.c
index 886a0c1..64b09a6 100644
--- a/lib9p/defs.c
+++ b/lib9p/9p.c
@@ -1,4 +1,4 @@
-/* 9p/defs.c - TODO
+/* lib9p/9p.c - Base 9P protocol utilities for both clients and servers
*
* Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com>
* SPDX-Licence-Identifier: AGPL-3.0-or-later
@@ -9,23 +9,28 @@
#include <stdio.h> /* for vsnprintf() */
#include <string.h> /* for strncpy() */
-#include "9p/defs.h"
-#include "9p/linux-errno.h"
-#include "9p/internal.h"
+#include <lib9p/9p.h>
-static struct version *versions[_P9_VER_CNT] = {
- [P9_VER_9P2000] = &version_9P2000,
- /* [P9_VER_9P2000u] = &version_9P2000u, */
-};
+#include "internal.h"
-int p9_error(struct p9_ctx *ctx, uint32_t linux_errno, char const *msg) {
+enum lib9p_version lib9p_ctx_version(lib9p_ctx *ctx) {
+ assert(ctx);
+ return ctx->version;
+}
+
+uint32_t lib9p_ctx_max_msg_size(lib9p_ctx *ctx) {
+ assert(ctx);
+ return ctx->max_msg_size;
+}
+
+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';
ctx->err_num = linux_errno;
return -1;
}
-int p9_errorf(struct p9_ctx *ctx, uint32_t linux_errno, char const *fmt, ...) {
+int lib9p_errorf(struct lib9p_ctx *ctx, uint32_t linux_errno, char const *fmt, ...) {
int n;
va_list args;
@@ -40,25 +45,25 @@ int p9_errorf(struct p9_ctx *ctx, uint32_t linux_errno, char const *fmt, ...) {
return -1;
}
-size_t p9_unmarshal_size(struct p9_ctx *ctx, uint8_t *net_bytes) {
+size_t lib9p_unmarshal_size(struct lib9p_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");
+ return lib9p_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);
+ return lib9p_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 lib9p_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) {
+uint8_t lib9p_unmarshal(struct lib9p_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]);
@@ -67,12 +72,12 @@ uint8_t p9_unmarshal(struct p9_ctx *ctx, uint8_t *net_bytes, uint16_t *out_tag,
/* 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 lib9p_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) {
+uint32_t lib9p_marshal(struct lib9p_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]);
diff --git a/lib9p/9p.h b/lib9p/9p.h
deleted file mode 100644
index 63fbd04..0000000
--- a/lib9p/9p.h
+++ /dev/null
@@ -1,15 +0,0 @@
-/* 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/lib9p/CMakeLists.txt b/lib9p/CMakeLists.txt
new file mode 100644
index 0000000..0faa109
--- /dev/null
+++ b/lib9p/CMakeLists.txt
@@ -0,0 +1,34 @@
+# libcr/CMakeLists.txt - TODO
+#
+# Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com>
+# SPDX-Licence-Identifier: AGPL-3.0-or-later
+
+add_library(lib9p INTERFACE)
+target_sources(lib9p INTERFACE
+ types.c
+ 9p.c
+ srv.c
+)
+target_include_directories(lib9p SYSTEM INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include)
+
+add_custom_command(
+ OUTPUT ${CMAKE_SOURCE_DIR}/3rd-party/linux-errno.txt
+ DEPENDS ${CMAKE_SOURCE_DIR}/3rd-party/linux-errno.txt.gen
+ COMMAND ${CMAKE_SOURCE_DIR}/3rd-party/linux-errno.txt.gen
+)
+add_custom_command(
+ OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/include/lib9p/linux-errno.h
+ DEPENDS include/lib9p/linux-errno.h.gen ${CMAKE_SOURCE_DIR}/3rd-party/linux-errno.txt
+ COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/include/lib9p/linux-errno.h.gen ${CMAKE_SOURCE_DIR}/3rd-party/linux-errno.txt >${CMAKE_CURRENT_SOURCE_DIR}/include/lib9p/linux-errno.h
+)
+add_custom_command(
+ OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/types.c ${CMAKE_CURRENT_SOURCE_DIR}/include/lib9p/_types.h
+ DEPENDS types.gen 9P2000.txt 9P2000.u.txt 9P2000.e.txt
+ COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/types.gen 9P2000.txt 9P2000.u.txt 9P2000.e.txt
+)
+add_dependencies(generate
+ ${CMAKE_SOURCE_DIR}/3rd-party/linux-errno.txt
+ ${CMAKE_CURRENT_SOURCE_DIR}/linux-errno.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/types.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/include/lib9p/_types.h
+)
diff --git a/lib9p/defs.gen b/lib9p/defs.gen
deleted file mode 100755
index 1a8dc69..0000000
--- a/lib9p/defs.gen
+++ /dev/null
@@ -1,456 +0,0 @@
-#!/usr/bin/env python
-# 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/defs.gen"
-
-# Parse the *.txt ##############################################################
-
-
-class Atom(enum.Enum):
- u8 = 1
- u16 = 2
- u32 = 4
- u64 = 8
-
- @property
- def name(self) -> str:
- return str(self.value)
-
- @property
- def static_size(self) -> int:
- return self.value
-
-
-# `msgid/structname = "member1 member2..."`
-# `structname = "member1 member2..."`
-# `structname += "member1 member2..."`
-class Struct:
- msgid: int | None = None
- name: str
- members: list["Member"]
-
- @property
- def static_size(self) -> int | None:
- size = 0
- for member in self.members:
- msize = member.static_size
- if msize is None:
- return None
- size += msize
- return size
-
-
-# `cnt*(name[typ])`
-# the `cnt*(...)` wrapper is optional
-class Member:
- cnt: str | None = None
- name: str
- typ: Atom | Struct
-
- @property
- def static_size(self) -> int | None:
- if self.cnt:
- return None
- return self.typ.static_size
-
-
-re_membername = "(?:[a-zA-Z_][a-zA-Z_0-9]*)"
-re_memberspec = (
- f"(?:(?P<cnt>{re_membername})\\*\\()?(?P<name>{re_membername})\\[(?P<typ>.*)\\]\\)?"
-)
-
-
-def parse_members(
- env: dict[str, Atom | Struct], existing: list[Member], specs: str
-) -> list[Member]:
- ret = existing
- for spec in specs.split():
- m = re.fullmatch(re_memberspec, spec)
- if not m:
- raise SyntaxError(f"invalid member spec {repr(spec)}")
-
- member = Member()
-
- member.name = m.group("name")
- if any(x.name == member.name for x in ret):
- raise ValueError(f"duplicate member name {repr(member.name)}")
-
- if m.group("typ") not in env:
- raise NameError(f"Unknown type {repr(m.group(2))}")
- member.typ = env[m.group("typ")]
-
- if cnt := m.group("cnt"):
- if len(ret) == 0 or ret[-1].name != cnt:
- raise ValueError(f"list count must be previous item: {repr(cnt)}")
- if not isinstance(ret[-1].typ, Atom):
- raise ValueError(f"list count must be an integer type: {repr(cnt)}")
- member.cnt = cnt
-
- ret += [member]
- return ret
-
-
-re_version = r'version\s+"(?P<version>[^"]+)"'
-re_structspec = (
- r'(?:(?P<msgid>[0-9]+)/)?(?P<name>\S+)\s*(?P<op>\+?=)\s*"(?P<members>[^"]*)"'
-)
-re_structspec_cont = r'"(?P<members>[^"]*)"'
-
-
-def parse_file(filename: str) -> tuple[str, list[Struct]]:
- version: str | None = None
- env: dict[str, Atom | Struct] = {
- "1": Atom.u8,
- "2": Atom.u16,
- "4": Atom.u32,
- "8": Atom.u64,
- }
- with open(filename, "r") as fh:
- prev: Struct | None = None
- for line in fh:
- line = line.split("#", 1)[0].strip()
- if not line:
- continue
- if m := re.fullmatch(re_version, line):
- if version:
- raise SyntaxError("must have exactly 1 version line")
- version = m.group("version")
- elif m := re.fullmatch(re_structspec, line):
- if m.group("op") == "+=" and m.group("msgid"):
- raise SyntaxError("cannot += to a message that is not yet defined")
- match m.group("op"):
- case "=":
- struct = Struct()
- if m.group("msgid"):
- struct.msgid = int(m.group("msgid"))
- struct.name = m.group("name")
- struct.members = parse_members(env, [], m.group("members"))
- env[struct.name] = struct
- prev = struct
- case "+=":
- if m.group("name") not in env:
- raise NameError(f"Unknown type {repr(m.group('name'))}")
- _struct = env[m.group("name")]
- if not isinstance(_struct, Struct):
- raise NameError(
- f"Type {repr(m.group('name'))} is not a struct"
- )
- struct = _struct
- struct.members = parse_members(
- env, struct.members, m.group("members")
- )
- prev = struct
- elif m := re.fullmatch(re_structspec_cont, line):
- if not prev:
- raise SyntaxError("continuation line must come after a struct line")
- prev.members = parse_members(env, prev.members, m.group("members"))
- else:
- raise SyntaxError(f"invalid line {repr(line)}")
- if not version:
- raise SyntaxError("must have exactly 1 version line")
- structs = [x for x in env.values() if isinstance(x, Struct)]
- return version, structs
-
-
-# Generate C ###################################################################
-
-
-def c_typename(idprefix: str, typ: Atom | Struct) -> str:
- match typ:
- case Atom():
- return f"uint{typ.value*8}_t"
- case Struct():
- if typ.msgid is not None:
- return f"struct {idprefix}msg_{typ.name}"
- return f"struct {idprefix}{typ.name}"
- case _:
- raise ValueError(f"not a type: {typ.__class__.__name__}")
-
-
-def gen_h(txtname: str, idprefix: str, structs: list[Struct]) -> str:
- guard = (
- f"_{txtname.replace('.txt', '.h').upper().replace('/', '_').replace('.', '_')}_"
- )
- ret = f"""/* Generated by `{PROGRAM} {txtname}`. DO NOT EDIT! */
-
-#ifndef {guard}
-#define {guard}
-
-#define {idprefix.upper()}MIN_MSGLEN 7
-"""
- ret += """
-/* non-message structs ********************************************************/
-
-"""
- for struct in structs:
- if struct.msgid is not None:
- continue
- ret += c_typename(idprefix, struct) + " {\n"
- typewidth = max(
- len(c_typename(idprefix, member.typ)) for member in struct.members
- )
- for member in struct.members:
- ret += f"\t{c_typename(idprefix, member.typ).ljust(typewidth)} {'*' if member.cnt else ' '}{member.name};\n"
- ret += "};\n"
-
- ret += """
-/* message types **************************************************************/
-
-"""
- ret += f"enum {idprefix}msg_type {{ /* uint8_t */\n"
- namewidth = max(len(msg.name) for msg in structs if msg.msgid is not None)
- for msg in structs:
- if msg.msgid is None:
- continue
- ret += f"\t{idprefix.upper()}TYP_{msg.name.ljust(namewidth)} = {msg.msgid},\n"
- ret += "};\n"
-
- ret += """
-/* message structs ************************************************************/
-
-"""
- for msg in structs:
- if msg.msgid is None:
- continue
- if not msg.members:
- ret += c_typename(idprefix, msg) + " {};\n"
- continue
- ret += c_typename(idprefix, msg) + " {\n"
- typewidth = max(len(c_typename(idprefix, member.typ)) for member in msg.members)
- for member in msg.members:
- ret += f"\t{c_typename(idprefix, member.typ).ljust(typewidth)} {'*' if member.cnt else ' '}{member.name};\n"
- ret += "};\n"
-
- ret += f"""
-/* tables *********************************************************************/
-
-#define _P9_TYPECODE_FOR_CTYPE(msg) """
- for msg in structs:
- if msg.msgid is None:
- continue
- ret += f", \\\n\t\t{c_typename(idprefix, msg)}: {idprefix.upper()}TYP_{msg.name}"
- ret += "\n"
-
- ret += "\n"
- ret += f"#endif /* {guard} */\n"
- return ret
-
-
-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 "{header}"
-#include "9p/internal.h"
-"""
-
- def used(arg: str) -> str:
- return arg
-
- def unused(arg: str) -> str:
- return f"UNUSED({arg})"
-
- # checksize_* ##############################################################
- ret += """
-/* checksize_* ****************************************************************/
-
-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++)
- if (__builtin_add_overflow(*mut_host_extra, host_size, mut_host_extra)
- || fn(net_len, net_bytes, mut_net_offset, mut_host_extra))
- 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;
-}
-static inline bool checksize_2(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, 2, mut_net_offset) || net_len < *mut_net_offset;
-}
-static inline bool checksize_4(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, 4, mut_net_offset) || net_len < *mut_net_offset;
-}
-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')}) {{"
- if len(struct.members) == 0:
- ret += "\n\treturn false;\n"
- ret += "}\n"
- continue
- prefix0 = "\treturn "
- prefix1 = "\t || "
- prefix2 = "\t "
- prefix = prefix0
- prev_size: int | None = None
- for member in struct.members:
- if member.cnt is not None:
- assert prev_size
- ret += f"\n{prefix }_checksize_list(decode_u{prev_size*8}le(&net_bytes[(*mut_net_offset)-{prev_size}]), checksize_{member.typ.name}, sizeof({c_typename(idprefix, member.typ)}),"
- ret += f"\n{prefix2} net_len, net_bytes, mut_net_offset, mut_host_extra)"
- else:
- ret += f"\n{prefix}checksize_{member.typ.name}(net_len, net_bytes, mut_net_offset, mut_host_extra)"
- prefix = prefix1
- prev_size = member.static_size
- if struct.name == "s":
- ret += (
- f"\n{prefix}__builtin_add_overflow(*mut_host_extra, 1, mut_host_extra)"
- )
- ret += ";\n}\n"
-
- # unmarshal_* ##############################################################
- ret += """
-/* unmarshal_* ****************************************************************/
-/* checksize_XXX() should be called before unmarshal_XXX(). */
-
-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 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 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 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 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\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"\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"
-
- # marshal_* ################################################################
- ret += """
-/* marshal_* ******************************************************************/
-
-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 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 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 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 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\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"\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"
-
- # tables ###################################################################
- ret += """
-/* tables *********************************************************************/
-
-"""
- for msg in structs:
- if msg.msgid is None:
- continue
- 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
-
-
-################################################################################
-
-if __name__ == "__main__":
- import sys
-
- for txtname in sys.argv[1:]:
- txtdir, txtbase = os.path.split(txtname)
- version, structs = parse_file(txtname)
- with open(
- os.path.join(txtdir, "defs-" + txtbase.replace(".txt", ".h")), "w"
- ) as fh:
- fh.write(gen_h(txtname, "p9_", structs))
- with open(
- os.path.join(txtdir, "defs-" + txtbase.replace(".txt", ".c")), "w"
- ) as fh:
- fh.write(gen_c(txtname, "p9_", structs))
diff --git a/lib9p/defs.h b/lib9p/defs.h
deleted file mode 100644
index 907cdde..0000000
--- a/lib9p/defs.h
+++ /dev/null
@@ -1,74 +0,0 @@
-/* 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_UNINITIALIZED,
- /* 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 p9_ctx_version(p9_ctx *);
-
-/** 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/lib9p/include/lib9p/9p.h b/lib9p/include/lib9p/9p.h
new file mode 100644
index 0000000..a55f08f
--- /dev/null
+++ b/lib9p/include/lib9p/9p.h
@@ -0,0 +1,72 @@
+/* lib9p/9p.h - Base 9P protocol definitions for both clients and servers
+ *
+ * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-Licence-Identifier: AGPL-3.0-or-later
+ */
+
+#ifndef _LIB9P_9P_H_
+#define _LIB9P_9P_H_
+
+#include <lib9p/linux-errno.h>
+#include <lib9p/_types.h>
+
+#define LIB9P_NOTAG ((uint16_t)~0U)
+#define LIB9P_NOFID ((uint32_t)~0U)
+
+struct lib9p_ctx;
+enum lib9p_version lib9p_ctx_version(lib9p_ctx *);
+uint32_t lib9p_ctx_max_msg_size(lib9p_ctx *);
+
+/** Write an static error into ctx, return -1. */
+int lib9p_error(struct lib9p_ctx *ctx, uint32_t linux_errno, char const *msg);
+/** Write a printf-style error into ctx, return -1. */
+int lib9p_errorf(struct lib9p_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, or if an invalid string (invalid UTF-8,
+ * contains a nul-byte) is encountered.
+ *
+ * @param net_bytes : the complete request, starting with the "size[4]"
+ *
+ * @return required size, or -1 on error
+ */
+ssize_t lib9p_unmarshal_size(struct lib9p_ctx *ctx, uint8_t *net_bytes);
+
+/**
+ * Unmarshal the 9P message `net_bytes` into the C struct `ret_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 ctx : negotiated protocol parameters, where to record errors
+ * @param net_bytes : the complete message, starting with the "size[4]"
+ *
+ * @return ret_typ : the mesage type
+ * @return ret_tag : the message-ID tag
+ * @return ret_body : the message body, must be at least lib9p_unmarshal_size() bytes
+ * @return the message type, or -1 on error
+ */
+bool lib9p_unmarshal(struct lib9p_ctx *ctx, uint8_t *net_bytes,
+ enum lib9p_msg_typ *ret_typ, uint16_t *ret_tag, void *ret_body);
+
+/**
+ * Marshal a `struct lib9p_msg_{typ}` structure into a byte-array.
+ *
+ * @param ctx : negotiated protocol parameters, where to record errors
+ * @param typ : the message type
+ * @param tag : the message-ID tag
+ * @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 whether there was an error (false=success, true=error)
+ */
+bool lib9p_marshal(struct lib9p_ctx *ctx, uint8_t typ, uint16_t tag, void *body,
+ uint8_t *ret_bytes);
+
+#endif _LIB9P_9P_H_
diff --git a/lib9p/include/lib9p/_types.h b/lib9p/include/lib9p/_types.h
new file mode 100644
index 0000000..348945d
--- /dev/null
+++ b/lib9p/include/lib9p/_types.h
@@ -0,0 +1,285 @@
+/* Generated by `./types.gen 9P2000.txt 9P2000.u.txt 9P2000.e.txt`. DO NOT EDIT! */
+
+#ifndef _LIB9P__TYPES_H_
+#define _LIB9P__TYPES_H_
+
+#include <stdint.h>
+
+/* versions *******************************************************************/
+
+enum lib9p_version {
+ LIB9P_VER_UNINITIALIZED = 0,
+ LIB9P_VER_9P2000, /* "9P2000" */
+ LIB9P_VER_9P2000_e, /* "9P2000.e" */
+ LIB9P_VER_9P2000_u, /* "9P2000.u" */
+ LIB9P_VER_NUM,
+};
+
+/* non-message structs ********************************************************/
+
+struct lib9p_d {
+ uint32_t len;
+ uint8_t *dat;
+};
+
+struct lib9p_s {
+ uint16_t len;
+ uint8_t *utf8;
+};
+
+struct lib9p_qid {
+ uint8_t type;
+ uint32_t vers;
+ uint64_t path;
+};
+
+struct lib9p_stat {
+ uint16_t stat_size;
+ uint16_t kern_type;
+ uint32_t kern_dev;
+ struct lib9p_qid file_qid;
+ uint32_t file_mode;
+ uint32_t file_atime;
+ uint32_t file_mtime;
+ uint64_t file_size;
+ struct lib9p_s file_name;
+ struct lib9p_s file_owner_uid;
+ struct lib9p_s file_owner_gid;
+ struct lib9p_s file_last_modified_uid;
+ struct lib9p_s file_extension; /* 9P2000.u */
+ uint32_t file_owner_n_uid; /* 9P2000.u */
+ uint32_t file_owner_n_gid; /* 9P2000.u */
+ uint32_t file_last_modified_n_uid; /* 9P2000.u */
+};
+
+/* messages *******************************************************************/
+
+enum lib9p_msg_type { /* uint8_t */
+ LIB9P_TYP_Tversion = 100,
+ LIB9P_TYP_Rversion = 101,
+ LIB9P_TYP_Tauth = 102,
+ LIB9P_TYP_Rauth = 103,
+ LIB9P_TYP_Tattach = 104,
+ LIB9P_TYP_Rattach = 105,
+ LIB9P_TYP_Rerror = 107,
+ LIB9P_TYP_Tflush = 108,
+ LIB9P_TYP_Rflush = 109,
+ LIB9P_TYP_Twalk = 110,
+ LIB9P_TYP_Rwalk = 111,
+ LIB9P_TYP_Topen = 112,
+ LIB9P_TYP_Ropen = 113,
+ LIB9P_TYP_Tcreate = 114,
+ LIB9P_TYP_Rcreate = 115,
+ LIB9P_TYP_Tread = 116,
+ LIB9P_TYP_Rread = 117,
+ LIB9P_TYP_Twrite = 118,
+ LIB9P_TYP_Rwrite = 119,
+ LIB9P_TYP_Tclunk = 120,
+ LIB9P_TYP_Rclunk = 121,
+ LIB9P_TYP_Tremove = 122,
+ LIB9P_TYP_Rremove = 123,
+ LIB9P_TYP_Tstat = 124,
+ LIB9P_TYP_Rstat = 125,
+ LIB9P_TYP_Twstat = 126,
+ LIB9P_TYP_Rwstat = 127,
+ LIB9P_TYP_Tsession = 150, /* 9P2000.e */
+ LIB9P_TYP_Rsession = 151, /* 9P2000.e */
+ LIB9P_TYP_Tsread = 152, /* 9P2000.e */
+ LIB9P_TYP_Rsread = 153, /* 9P2000.e */
+ LIB9P_TYP_Tswrite = 154, /* 9P2000.e */
+ LIB9P_TYP_Rswrite = 155, /* 9P2000.e */
+};
+
+#define LIB9P_TYPECODE_FOR_CTYPE(msg) _Generic((msg), \
+ struct lib9p_msg_Tversion: LIB9P_TYP_Tversion, \
+ struct lib9p_msg_Rversion: LIB9P_TYP_Rversion, \
+ struct lib9p_msg_Tauth: LIB9P_TYP_Tauth, \
+ struct lib9p_msg_Rauth: LIB9P_TYP_Rauth, \
+ struct lib9p_msg_Tattach: LIB9P_TYP_Tattach, \
+ struct lib9p_msg_Rattach: LIB9P_TYP_Rattach, \
+ struct lib9p_msg_Rerror: LIB9P_TYP_Rerror, \
+ struct lib9p_msg_Tflush: LIB9P_TYP_Tflush, \
+ struct lib9p_msg_Rflush: LIB9P_TYP_Rflush, \
+ struct lib9p_msg_Twalk: LIB9P_TYP_Twalk, \
+ struct lib9p_msg_Rwalk: LIB9P_TYP_Rwalk, \
+ struct lib9p_msg_Topen: LIB9P_TYP_Topen, \
+ struct lib9p_msg_Ropen: LIB9P_TYP_Ropen, \
+ struct lib9p_msg_Tcreate: LIB9P_TYP_Tcreate, \
+ struct lib9p_msg_Rcreate: LIB9P_TYP_Rcreate, \
+ struct lib9p_msg_Tread: LIB9P_TYP_Tread, \
+ struct lib9p_msg_Rread: LIB9P_TYP_Rread, \
+ struct lib9p_msg_Twrite: LIB9P_TYP_Twrite, \
+ struct lib9p_msg_Rwrite: LIB9P_TYP_Rwrite, \
+ struct lib9p_msg_Tclunk: LIB9P_TYP_Tclunk, \
+ struct lib9p_msg_Rclunk: LIB9P_TYP_Rclunk, \
+ struct lib9p_msg_Tremove: LIB9P_TYP_Tremove, \
+ struct lib9p_msg_Rremove: LIB9P_TYP_Rremove, \
+ struct lib9p_msg_Tstat: LIB9P_TYP_Tstat, \
+ struct lib9p_msg_Rstat: LIB9P_TYP_Rstat, \
+ struct lib9p_msg_Twstat: LIB9P_TYP_Twstat, \
+ struct lib9p_msg_Rwstat: LIB9P_TYP_Rwstat, \
+ struct lib9p_msg_Tsession: LIB9P_TYP_Tsession, \
+ struct lib9p_msg_Rsession: LIB9P_TYP_Rsession, \
+ struct lib9p_msg_Tsread: LIB9P_TYP_Tsread, \
+ struct lib9p_msg_Rsread: LIB9P_TYP_Rsread, \
+ struct lib9p_msg_Tswrite: LIB9P_TYP_Tswrite, \
+ struct lib9p_msg_Rswrite: LIB9P_TYP_Rswrite)
+
+struct lib9p_msg_Tversion {
+ uint32_t max_msg_size;
+ struct lib9p_s version;
+};
+
+struct lib9p_msg_Rversion {
+ uint32_t max_msg_size;
+ struct lib9p_s version;
+};
+
+struct lib9p_msg_Tauth {
+ uint32_t afid;
+ struct lib9p_s uname;
+ struct lib9p_s aname;
+ uint32_t n_uname; /* 9P2000.u */
+};
+
+struct lib9p_msg_Rauth {
+ struct lib9p_qid aqid;
+};
+
+struct lib9p_msg_Tattach {
+ uint32_t fid;
+ uint32_t afid;
+ struct lib9p_s uname;
+ struct lib9p_s aname;
+};
+
+struct lib9p_msg_Rattach {
+ struct lib9p_qid qid;
+};
+
+struct lib9p_msg_Rerror {
+ struct lib9p_s ename;
+ uint32_t errno; /* 9P2000.u */
+};
+
+struct lib9p_msg_Tflush {
+ uint16_t oldtag;
+};
+
+struct lib9p_msg_Rflush {};
+
+struct lib9p_msg_Twalk {
+ uint32_t fid;
+ uint32_t newfid;
+ uint16_t nwname;
+ struct lib9p_s *wname;
+};
+
+struct lib9p_msg_Rwalk {
+ uint16_t nwqid;
+ struct lib9p_qid *wqid;
+};
+
+struct lib9p_msg_Topen {
+ uint32_t fid;
+ uint8_t mode;
+};
+
+struct lib9p_msg_Ropen {
+ struct lib9p_qid qid;
+ uint32_t iounit;
+};
+
+struct lib9p_msg_Tcreate {
+ uint32_t fid;
+ struct lib9p_s name;
+ uint32_t perm;
+ uint8_t mode;
+};
+
+struct lib9p_msg_Rcreate {
+ struct lib9p_qid qid;
+ uint32_t iounit;
+};
+
+struct lib9p_msg_Tread {
+ uint32_t fid;
+ uint64_t offset;
+ uint32_t count;
+};
+
+struct lib9p_msg_Rread {
+ struct lib9p_d data;
+};
+
+struct lib9p_msg_Twrite {
+ uint32_t fid;
+ uint64_t offset;
+ struct lib9p_d data;
+};
+
+struct lib9p_msg_Rwrite {
+ uint32_t count;
+};
+
+struct lib9p_msg_Tclunk {
+ uint32_t fid;
+};
+
+struct lib9p_msg_Rclunk {};
+
+struct lib9p_msg_Tremove {
+ uint32_t fid;
+};
+
+struct lib9p_msg_Rremove {};
+
+struct lib9p_msg_Tstat {
+ uint32_t fid;
+};
+
+struct lib9p_msg_Rstat {
+ struct lib9p_stat stat;
+};
+
+struct lib9p_msg_Twstat {
+ uint32_t fid;
+ struct lib9p_stat stat;
+};
+
+struct lib9p_msg_Rwstat {};
+
+/* 9P2000.e */
+struct lib9p_msg_Tsession {
+ uint64_t key;
+};
+
+/* 9P2000.e */
+struct lib9p_msg_Rsession {};
+
+/* 9P2000.e */
+struct lib9p_msg_Tsread {
+ uint32_t fid;
+ uint16_t nwname;
+ struct lib9p_s *wname;
+};
+
+/* 9P2000.e */
+struct lib9p_msg_Rsread {
+ struct lib9p_d data;
+};
+
+/* 9P2000.e */
+struct lib9p_msg_Tswrite {
+ uint32_t fid;
+ uint16_t nwname;
+ struct lib9p_s *wname;
+ struct lib9p_d data;
+};
+
+/* 9P2000.e */
+struct lib9p_msg_Rswrite {
+ uint32_t count;
+};
+
+#endif /* _LIB9P__TYPES_H_ */
diff --git a/lib9p/linux-errno.h.gen b/lib9p/include/lib9p/linux-errno.h.gen
index b896384..b896384 100755
--- a/lib9p/linux-errno.h.gen
+++ b/lib9p/include/lib9p/linux-errno.h.gen
diff --git a/lib9p/include/lib9p/srv.h b/lib9p/include/lib9p/srv.h
new file mode 100644
index 0000000..3b8b21e
--- /dev/null
+++ b/lib9p/include/lib9p/srv.h
@@ -0,0 +1,16 @@
+#ifndef _LIB9P_SRV_H_
+#define _LIB9P_SRV_H_
+
+#include <libcr/coroutine.h>
+
+struct lib9p_srvreq;
+
+struct lib9p_srv {
+ int sockfd;
+ cr_chan_t(lib9p_srvreq *) reqch;
+};
+
+COROUTINE lib9p_srv_read_cr(void *_srv);
+COROUTINE lib9p_srv_write_cr(void *_srv);
+
+#endif /* _LIB9P_SRV_H_ */
diff --git a/lib9p/internal.h b/lib9p/internal.h
index 61977d4..ebdc3f3 100644
--- a/lib9p/internal.h
+++ b/lib9p/internal.h
@@ -1,21 +1,20 @@
-/* 9p/internal.h - TODO
+/* lib9p/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_
+#ifndef _LIB9P_INTERNAL_H_
+#define _LIB9P_INTERNAL_H_
#include <assert.h>
-#include <stdint.h>
#include <stdbool.h>
-#include "9p/defs.h"
+#include <lib9p/9p.h>
-#define USE_CONFIG_9P
+#define USE_CONFIG_LIB9P
#include "config.h"
-static_assert(CONFIG_9P_MAX_ERR_SIZE <= UINT16_MAX);
+static_assert(CONFIG_LIB9P_MAX_ERR_SIZE <= UINT16_MAX);
/* C language *****************************************************************/
@@ -23,39 +22,65 @@ static_assert(CONFIG_9P_MAX_ERR_SIZE <= UINT16_MAX);
/* types **********************************************************************/
-struct p9_ctx {
- enum p9_version version;
- uint32_t max_msg_size;
- uint32_t Rerror_overhead;
+/* NB: We declare this here instead of in the public <lib9p/9p.h>
+ * because we don't want to include "config.h" from public headers,
+ * and I want the MAX_ERR_SIZE to be configurable. */
+struct lib9p_ctx {
+ /* negotiated */
+ enum lib9p_version version;
+ uint32_t max_msg_size;
+ /* negotiated (server) */
+ uint32_t Rerror_overhead;
+
+ /* state */
+ uint32_t err_num;
+ char err_msg[CONFIG_LIB9P_MAX_ERR_SIZE];
+};
+
+/* vtables ********************************************************************/
- uint32_t err_num;
- char err_msg[CONFIG_9P_MAX_ERR_SIZE];
+struct _checksize_ctx {
+ struct lib9p_ctx *ctx;
+ uint32_t net_size;
+ uint8_t net_bytes;
+
+ uint32_t net_offset;
+ /* Increment `host_extra` to pre-allocate space that is
+ * "extra" beyond sizeof(). */
+ size_t host_extra;
};
-static inline enum p9_version p9_ctx_version(p9_ctx *ctx) {
- assert(ctx);
- return ctx->version;
-}
+struct _unmarshal_ctx {
+ struct lib9p_ctx *ctx;
-/* vtables ********************************************************************/
+ uint32_t net_offset;
+ /* `extra` points to the beginning of unallocated space. */
+ void *extra;
+};
+
+struct _marshal_ctx {
+ struct lib9p_ctx *ctx;
+
+ uint8_t *net_bytes;
+ uint32_t net_offset;
+};
-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);
+typedef bool (*_checksize_fn_t)(struct _checksize_ctx *ctx);
+typedef void (*_unmarshal_fn_t)(struct _unmarshal_ctx *ctx, void *out);
+typedef bool (*_marshal_fn_t)(struct _marshal_ctx *ctx, void *host_val);
-struct msg_vtable {
+struct _vtable_msg {
size_t unmarshal_basesize;
_checksize_fn_t unmarshal_extrasize;
_unmarshal_fn_t unmarshal;
_marshal_fn_t marshal;
};
-struct version {
- struct msg_vtable msgs[0xFF];
+struct _vtable_version {
+ struct _vtable_msg msgs[0xFF];
};
-extern struct version version_9P2000;
-/*extern struct version version_9P2000u; */
+extern struct _vtable_version _lib9p_vtables[LIB9P_VER_NUM];
/* unmarshal utilities ********************************************************/
@@ -136,4 +161,4 @@ static inline void encode_u64le(uint64_t in, uint8_t *out) {
out[7] = (uint8_t)((in >> 56) & 0xFF);
}
-#endif /* _9P_INTERNAL_H_ */
+#endif /* _LIB9P_INTERNAL_H_ */
diff --git a/lib9p/misc.txt b/lib9p/misc.txt
deleted file mode 100644
index 93aa304..0000000
--- a/lib9p/misc.txt
+++ /dev/null
@@ -1,24 +0,0 @@
-# qid.types
-QTDIR = 1<<7
-QTAPPEND = 1<<6
-QTEXCL = 1<<5
-QTMOUNT = 1<<4 # been around forever, but undocumented?
-QTAUTH = 1<<3
-QTTMP = 1<<2 # added to Plan 9 2003-12
-QTSYMLINK = 1<<1 # .u
-QTFILE = 1<<0
-
-DMDIR = 1<<31
-DMAPPEND = 1<<30
-DMEXCL = 1<<29
-DMMOUNT = 1<<28
-DMAUTH = 1<<27
-DMTMP = 1<<26
-# = 1<<25
-# = 1<<24
-DMDEVICE = 1<<23 # .u
-# = 1<<22
-DMNAMEDPIPE = 1<<21 # .u
-DMSOCKET = 1<<20 # .u
-DMSETUID = 1<<19 # .u
-DMSETGID = 1<<18 # .u
diff --git a/lib9p/srv.h b/lib9p/srv.h
deleted file mode 100644
index e3623ed..0000000
--- a/lib9p/srv.h
+++ /dev/null
@@ -1,16 +0,0 @@
-#ifndef _NET9P_H_
-#define _NET9P_H_
-
-#include "coroutine.h"
-
-struct p9_srvreq;
-
-struct p9_srv {
- int sockfd;
- cr_chan_t(p9_srvreq *) reqch;
-};
-
-COROUTINE p9_srv_read_cr(void *_srv);
-COROUTINE p9_srv_write_cr(void *_srv);
-
-#endif /* _NET9P_H_ */
diff --git a/lib9p/types.c b/lib9p/types.c
new file mode 100644
index 0000000..73ac28e
--- /dev/null
+++ b/lib9p/types.c
@@ -0,0 +1,1331 @@
+/* Generated by `./types.gen 9P2000.txt 9P2000.u.txt 9P2000.e.txt`. DO NOT EDIT! */
+
+#include <stdbool.h>
+
+#include <lib9p/9p.h>
+
+#include "internal.h"
+
+/* checksize_* (internals of unmarshal_size()) ********************************/
+
+static inline bool _checksize_net(struct _checksize_ctx *ctx, uint32_t n) {
+ if (__builtin_add_overflow(ctx->net_offset, n, &ctx->net_offset))
+ /* If needed-net-size overflowed uint32_t, then
+ * there's no way that actual-net-size will live up to
+ * that. */
+ return lib9p_error(ctx->ctx, LINUX_EBADMSG, "message is too short for content");
+ if (ctx->net_offset > ctx->net_size)
+ return lib9p_error(ctx->ctx, LINUX_EBADMSG, "message is too short for content");
+ return false;
+}
+
+static inline bool _checksize_host(struct _checksize_ctx *ctx, size_t n) {
+ if (__builtin_add_overflow(ctx->host_extra, n, &ctx->host_extra))
+ /* If needed-host-size overflowed size_t, then there's
+ * no way that actual-net-size will live up to
+ * that. */
+ return lib9p_error(ctx->ctx, LINUX_EBADMSG, "message is too short for content");
+ return false;
+}
+
+static inline bool _checksize_list(struct _checksize_ctx *ctx,
+ size_t cnt, _checksize_fn_t item_fn, size_t item_host_size) {
+ for (size_t i = 0; i < cnt; i++)
+ if (_checksize_host(ctx, item_host_size) || item_fn(ctx))
+ return true;
+ return false;
+}
+
+#define checksize_1(ctx) _checksize_net(ctx, 1)
+#define checksize_2(ctx) _checksize_net(ctx, 2)
+#define checksize_4(ctx) _checksize_net(ctx, 4)
+#define checksize_8(ctx) _checksize_net(ctx, 8)
+
+static inline bool checksize_d(struct _checksize_ctx *ctx) {
+ uint32_t base_offset = ctx->net_offset;
+ if (_checksize_4(ctx))
+ return true;
+ uint32_t len = decode_u32le(&ctx->net_bytes[base_offset]);
+ return checksize_net(ctx, len) || checksize_host(ctx, len);
+}
+
+static inline bool checksize_s(struct _checksize_ctx *ctx) {
+ uint32_t base_offset = ctx->net_offset;
+ if (_checksize_2(ctx))
+ return true;
+ uint16_t len = decode_u16le(&ctx->net_bytes[base_offset]);
+ if (checksize_net(ctx, len) || checksize_host(ctx, ((size_t)len)+1))
+ return true;
+ if (!is_valid_utf8_without_nul(&ctx->net_bytes[base_offset+2], len))
+ return lib9p_error(ctx->ctx, LINUX_EBADMSG, "message contains invalid UTF-8");
+ return false;
+}
+
+static inline bool checksize_qid(struct _checksize_ctx *ctx) {
+ return checksize_1(ctx)
+ || checksize_4(ctx)
+ || checksize_8(ctx);
+}
+
+static inline bool checksize_stat(struct _checksize_ctx *ctx) {
+ return checksize_2(ctx)
+ || checksize_2(ctx)
+ || checksize_4(ctx)
+ || checksize_qid(ctx)
+ || checksize_4(ctx)
+ || checksize_4(ctx)
+ || checksize_4(ctx)
+ || checksize_8(ctx)
+ || checksize_s(ctx)
+ || checksize_s(ctx)
+ || checksize_s(ctx)
+ || checksize_s(ctx)
+ || ( ( (ctx->ctx->version==LIB9P_VER_9P2000_u) ) && checksize_s(ctx) )
+ || ( ( (ctx->ctx->version==LIB9P_VER_9P2000_u) ) && checksize_4(ctx) )
+ || ( ( (ctx->ctx->version==LIB9P_VER_9P2000_u) ) && checksize_4(ctx) )
+ || ( ( (ctx->ctx->version==LIB9P_VER_9P2000_u) ) && checksize_4(ctx) );
+}
+
+static bool checksize_Tversion(struct _checksize_ctx *ctx) {
+ return checksize_4(ctx)
+ || checksize_s(ctx);
+}
+
+static bool checksize_Rversion(struct _checksize_ctx *ctx) {
+ return checksize_4(ctx)
+ || checksize_s(ctx);
+}
+
+static bool checksize_Tauth(struct _checksize_ctx *ctx) {
+ return checksize_4(ctx)
+ || checksize_s(ctx)
+ || checksize_s(ctx)
+ || ( ( (ctx->ctx->version==LIB9P_VER_9P2000_u) ) && checksize_4(ctx) );
+}
+
+static bool checksize_Rauth(struct _checksize_ctx *ctx) {
+ return checksize_qid(ctx);
+}
+
+static bool checksize_Tattach(struct _checksize_ctx *ctx) {
+ return checksize_4(ctx)
+ || checksize_4(ctx)
+ || checksize_s(ctx)
+ || checksize_s(ctx);
+}
+
+static bool checksize_Rattach(struct _checksize_ctx *ctx) {
+ return checksize_qid(ctx);
+}
+
+static bool checksize_Rerror(struct _checksize_ctx *ctx) {
+ return checksize_s(ctx)
+ || ( ( (ctx->ctx->version==LIB9P_VER_9P2000_u) ) && checksize_4(ctx) );
+}
+
+static bool checksize_Tflush(struct _checksize_ctx *ctx) {
+ return checksize_2(ctx);
+}
+
+static bool checksize_Rflush(struct _checksize_ctx *UNUSED(ctx)) {
+ return false;
+}
+
+static bool checksize_Twalk(struct _checksize_ctx *ctx) {
+ return checksize_4(ctx)
+ || checksize_4(ctx)
+ || checksize_2(ctx)
+ || _checksize_list(ctx, decode_u16le(&ctx->net_bytes[ctx->net_offset-2]), checksize_s, sizeof(struct lib9p_s));
+}
+
+static bool checksize_Rwalk(struct _checksize_ctx *ctx) {
+ return checksize_2(ctx)
+ || _checksize_list(ctx, decode_u16le(&ctx->net_bytes[ctx->net_offset-2]), checksize_qid, sizeof(struct lib9p_qid));
+}
+
+static bool checksize_Topen(struct _checksize_ctx *ctx) {
+ return checksize_4(ctx)
+ || checksize_1(ctx);
+}
+
+static bool checksize_Ropen(struct _checksize_ctx *ctx) {
+ return checksize_qid(ctx)
+ || checksize_4(ctx);
+}
+
+static bool checksize_Tcreate(struct _checksize_ctx *ctx) {
+ return checksize_4(ctx)
+ || checksize_s(ctx)
+ || checksize_4(ctx)
+ || checksize_1(ctx);
+}
+
+static bool checksize_Rcreate(struct _checksize_ctx *ctx) {
+ return checksize_qid(ctx)
+ || checksize_4(ctx);
+}
+
+static bool checksize_Tread(struct _checksize_ctx *ctx) {
+ return checksize_4(ctx)
+ || checksize_8(ctx)
+ || checksize_4(ctx);
+}
+
+static bool checksize_Rread(struct _checksize_ctx *ctx) {
+ return checksize_d(ctx);
+}
+
+static bool checksize_Twrite(struct _checksize_ctx *ctx) {
+ return checksize_4(ctx)
+ || checksize_8(ctx)
+ || checksize_d(ctx);
+}
+
+static bool checksize_Rwrite(struct _checksize_ctx *ctx) {
+ return checksize_4(ctx);
+}
+
+static bool checksize_Tclunk(struct _checksize_ctx *ctx) {
+ return checksize_4(ctx);
+}
+
+static bool checksize_Rclunk(struct _checksize_ctx *UNUSED(ctx)) {
+ return false;
+}
+
+static bool checksize_Tremove(struct _checksize_ctx *ctx) {
+ return checksize_4(ctx);
+}
+
+static bool checksize_Rremove(struct _checksize_ctx *UNUSED(ctx)) {
+ return false;
+}
+
+static bool checksize_Tstat(struct _checksize_ctx *ctx) {
+ return checksize_4(ctx);
+}
+
+static bool checksize_Rstat(struct _checksize_ctx *ctx) {
+ return checksize_stat(ctx);
+}
+
+static bool checksize_Twstat(struct _checksize_ctx *ctx) {
+ return checksize_4(ctx)
+ || checksize_stat(ctx);
+}
+
+static bool checksize_Rwstat(struct _checksize_ctx *UNUSED(ctx)) {
+ return false;
+}
+
+static bool checksize_Tsession(struct _checksize_ctx *ctx) {
+ return checksize_8(ctx);
+}
+
+static bool checksize_Rsession(struct _checksize_ctx *UNUSED(ctx)) {
+ return false;
+}
+
+static bool checksize_Tsread(struct _checksize_ctx *ctx) {
+ return checksize_4(ctx)
+ || checksize_2(ctx)
+ || _checksize_list(ctx, decode_u16le(&ctx->net_bytes[ctx->net_offset-2]), checksize_s, sizeof(struct lib9p_s));
+}
+
+static bool checksize_Rsread(struct _checksize_ctx *ctx) {
+ return checksize_d(ctx);
+}
+
+static bool checksize_Tswrite(struct _checksize_ctx *ctx) {
+ return checksize_4(ctx)
+ || checksize_2(ctx)
+ || _checksize_list(ctx, decode_u16le(&ctx->net_bytes[ctx->net_offset-2]), checksize_s, sizeof(struct lib9p_s))
+ || checksize_d(ctx);
+}
+
+static bool checksize_Rswrite(struct _checksize_ctx *ctx) {
+ return checksize_4(ctx);
+}
+
+/* unmarshal_* ****************************************************************/
+
+static inline vold unmarshal_1(struct _unmarshal_ctx *ctx, uint8_t *out) {
+ *out = decode_u8le(&ctx->net_bytes[ctx->net_offset]);
+ ctx->net_offset += 1;
+}
+
+static inline vold unmarshal_2(struct _unmarshal_ctx *ctx, uint16_t *out) {
+ *out = decode_u16le(&ctx->net_bytes[ctx->net_offset]);
+ ctx->net_offset += 2;
+}
+
+static inline vold unmarshal_4(struct _unmarshal_ctx *ctx, uint32_t *out) {
+ *out = decode_u32le(&ctx->net_bytes[ctx->net_offset]);
+ ctx->net_offset += 4;
+}
+
+static inline vold unmarshal_8(struct _unmarshal_ctx *ctx, uint64_t *out) {
+ *out = decode_u64le(&ctx->net_bytes[ctx->net_offset]);
+ ctx->net_offset += 8;
+}
+
+static inline void unmarshal_d(struct _unmarshal_ctx *ctx, struct lib9p_d *out) {
+ memset(out, 0, sizeof(*out));
+ unmarshal_4(ctx, &out->len);
+ out->dat = ctx->host_extra;
+ ctx->host_extra += sizeof(out->dat[0]) * out->len;
+ for (typeof(out->len) i = 0; i < out->len; i++)
+ unmarshal_1(ctx, &out->dat[i]);
+}
+
+static inline void unmarshal_s(struct _unmarshal_ctx *ctx, struct lib9p_s *out) {
+ memset(out, 0, sizeof(*out));
+ unmarshal_2(ctx, &out->len);
+ out->utf8 = ctx->host_extra;
+ ctx->host_extra += sizeof(out->utf8[0]) * out->len;
+ for (typeof(out->len) i = 0; i < out->len; i++)
+ unmarshal_1(ctx, &out->utf8[i]);
+}
+
+static inline void unmarshal_qid(struct _unmarshal_ctx *ctx, struct lib9p_qid *out) {
+ memset(out, 0, sizeof(*out));
+ unmarshal_1(ctx, &out->type);
+ unmarshal_4(ctx, &out->vers);
+ unmarshal_8(ctx, &out->path);
+}
+
+static inline void unmarshal_stat(struct _unmarshal_ctx *ctx, struct lib9p_stat *out) {
+ memset(out, 0, sizeof(*out));
+ unmarshal_2(ctx, &out->stat_size);
+ unmarshal_2(ctx, &out->kern_type);
+ unmarshal_4(ctx, &out->kern_dev);
+ unmarshal_qid(ctx, &out->file_qid);
+ unmarshal_4(ctx, &out->file_mode);
+ unmarshal_4(ctx, &out->file_atime);
+ unmarshal_4(ctx, &out->file_mtime);
+ unmarshal_8(ctx, &out->file_size);
+ unmarshal_s(ctx, &out->file_name);
+ unmarshal_s(ctx, &out->file_owner_uid);
+ unmarshal_s(ctx, &out->file_owner_gid);
+ unmarshal_s(ctx, &out->file_last_modified_uid);
+ if ( (ctx->ctx->version==LIB9P_VER_9P2000_u) ) unmarshal_s(ctx, &out->file_extension);
+ if ( (ctx->ctx->version==LIB9P_VER_9P2000_u) ) unmarshal_4(ctx, &out->file_owner_n_uid);
+ if ( (ctx->ctx->version==LIB9P_VER_9P2000_u) ) unmarshal_4(ctx, &out->file_owner_n_gid);
+ if ( (ctx->ctx->version==LIB9P_VER_9P2000_u) ) unmarshal_4(ctx, &out->file_last_modified_n_uid);
+}
+
+static void unmarshal_Tversion(struct _unmarshal_ctx *ctx, struct lib9p_msg_Tversion *out) {
+ memset(out, 0, sizeof(*out));
+ unmarshal_4(ctx, &out->max_msg_size);
+ unmarshal_s(ctx, &out->version);
+}
+
+static void unmarshal_Rversion(struct _unmarshal_ctx *ctx, struct lib9p_msg_Rversion *out) {
+ memset(out, 0, sizeof(*out));
+ unmarshal_4(ctx, &out->max_msg_size);
+ unmarshal_s(ctx, &out->version);
+}
+
+static void unmarshal_Tauth(struct _unmarshal_ctx *ctx, struct lib9p_msg_Tauth *out) {
+ memset(out, 0, sizeof(*out));
+ unmarshal_4(ctx, &out->afid);
+ unmarshal_s(ctx, &out->uname);
+ unmarshal_s(ctx, &out->aname);
+ if ( (ctx->ctx->version==LIB9P_VER_9P2000_u) ) unmarshal_4(ctx, &out->n_uname);
+}
+
+static void unmarshal_Rauth(struct _unmarshal_ctx *ctx, struct lib9p_msg_Rauth *out) {
+ memset(out, 0, sizeof(*out));
+ unmarshal_qid(ctx, &out->aqid);
+}
+
+static void unmarshal_Tattach(struct _unmarshal_ctx *ctx, struct lib9p_msg_Tattach *out) {
+ memset(out, 0, sizeof(*out));
+ unmarshal_4(ctx, &out->fid);
+ unmarshal_4(ctx, &out->afid);
+ unmarshal_s(ctx, &out->uname);
+ unmarshal_s(ctx, &out->aname);
+}
+
+static void unmarshal_Rattach(struct _unmarshal_ctx *ctx, struct lib9p_msg_Rattach *out) {
+ memset(out, 0, sizeof(*out));
+ unmarshal_qid(ctx, &out->qid);
+}
+
+static void unmarshal_Rerror(struct _unmarshal_ctx *ctx, struct lib9p_msg_Rerror *out) {
+ memset(out, 0, sizeof(*out));
+ unmarshal_s(ctx, &out->ename);
+ if ( (ctx->ctx->version==LIB9P_VER_9P2000_u) ) unmarshal_4(ctx, &out->errno);
+}
+
+static void unmarshal_Tflush(struct _unmarshal_ctx *ctx, struct lib9p_msg_Tflush *out) {
+ memset(out, 0, sizeof(*out));
+ unmarshal_2(ctx, &out->oldtag);
+}
+
+static void unmarshal_Rflush(struct _unmarshal_ctx *UNUSED(ctx), struct lib9p_msg_Rflush *UNUSED(out)) {
+ memset(out, 0, sizeof(*out));
+}
+
+static void unmarshal_Twalk(struct _unmarshal_ctx *ctx, struct lib9p_msg_Twalk *out) {
+ memset(out, 0, sizeof(*out));
+ unmarshal_4(ctx, &out->fid);
+ unmarshal_4(ctx, &out->newfid);
+ unmarshal_2(ctx, &out->nwname);
+ out->wname = ctx->host_extra;
+ ctx->host_extra += sizeof(out->wname[0]) * out->nwname;
+ for (typeof(out->nwname) i = 0; i < out->nwname; i++)
+ unmarshal_s(ctx, &out->wname[i]);
+}
+
+static void unmarshal_Rwalk(struct _unmarshal_ctx *ctx, struct lib9p_msg_Rwalk *out) {
+ memset(out, 0, sizeof(*out));
+ unmarshal_2(ctx, &out->nwqid);
+ out->wqid = ctx->host_extra;
+ ctx->host_extra += sizeof(out->wqid[0]) * out->nwqid;
+ for (typeof(out->nwqid) i = 0; i < out->nwqid; i++)
+ unmarshal_qid(ctx, &out->wqid[i]);
+}
+
+static void unmarshal_Topen(struct _unmarshal_ctx *ctx, struct lib9p_msg_Topen *out) {
+ memset(out, 0, sizeof(*out));
+ unmarshal_4(ctx, &out->fid);
+ unmarshal_1(ctx, &out->mode);
+}
+
+static void unmarshal_Ropen(struct _unmarshal_ctx *ctx, struct lib9p_msg_Ropen *out) {
+ memset(out, 0, sizeof(*out));
+ unmarshal_qid(ctx, &out->qid);
+ unmarshal_4(ctx, &out->iounit);
+}
+
+static void unmarshal_Tcreate(struct _unmarshal_ctx *ctx, struct lib9p_msg_Tcreate *out) {
+ memset(out, 0, sizeof(*out));
+ unmarshal_4(ctx, &out->fid);
+ unmarshal_s(ctx, &out->name);
+ unmarshal_4(ctx, &out->perm);
+ unmarshal_1(ctx, &out->mode);
+}
+
+static void unmarshal_Rcreate(struct _unmarshal_ctx *ctx, struct lib9p_msg_Rcreate *out) {
+ memset(out, 0, sizeof(*out));
+ unmarshal_qid(ctx, &out->qid);
+ unmarshal_4(ctx, &out->iounit);
+}
+
+static void unmarshal_Tread(struct _unmarshal_ctx *ctx, struct lib9p_msg_Tread *out) {
+ memset(out, 0, sizeof(*out));
+ unmarshal_4(ctx, &out->fid);
+ unmarshal_8(ctx, &out->offset);
+ unmarshal_4(ctx, &out->count);
+}
+
+static void unmarshal_Rread(struct _unmarshal_ctx *ctx, struct lib9p_msg_Rread *out) {
+ memset(out, 0, sizeof(*out));
+ unmarshal_d(ctx, &out->data);
+}
+
+static void unmarshal_Twrite(struct _unmarshal_ctx *ctx, struct lib9p_msg_Twrite *out) {
+ memset(out, 0, sizeof(*out));
+ unmarshal_4(ctx, &out->fid);
+ unmarshal_8(ctx, &out->offset);
+ unmarshal_d(ctx, &out->data);
+}
+
+static void unmarshal_Rwrite(struct _unmarshal_ctx *ctx, struct lib9p_msg_Rwrite *out) {
+ memset(out, 0, sizeof(*out));
+ unmarshal_4(ctx, &out->count);
+}
+
+static void unmarshal_Tclunk(struct _unmarshal_ctx *ctx, struct lib9p_msg_Tclunk *out) {
+ memset(out, 0, sizeof(*out));
+ unmarshal_4(ctx, &out->fid);
+}
+
+static void unmarshal_Rclunk(struct _unmarshal_ctx *UNUSED(ctx), struct lib9p_msg_Rclunk *UNUSED(out)) {
+ memset(out, 0, sizeof(*out));
+}
+
+static void unmarshal_Tremove(struct _unmarshal_ctx *ctx, struct lib9p_msg_Tremove *out) {
+ memset(out, 0, sizeof(*out));
+ unmarshal_4(ctx, &out->fid);
+}
+
+static void unmarshal_Rremove(struct _unmarshal_ctx *UNUSED(ctx), struct lib9p_msg_Rremove *UNUSED(out)) {
+ memset(out, 0, sizeof(*out));
+}
+
+static void unmarshal_Tstat(struct _unmarshal_ctx *ctx, struct lib9p_msg_Tstat *out) {
+ memset(out, 0, sizeof(*out));
+ unmarshal_4(ctx, &out->fid);
+}
+
+static void unmarshal_Rstat(struct _unmarshal_ctx *ctx, struct lib9p_msg_Rstat *out) {
+ memset(out, 0, sizeof(*out));
+ unmarshal_stat(ctx, &out->stat);
+}
+
+static void unmarshal_Twstat(struct _unmarshal_ctx *ctx, struct lib9p_msg_Twstat *out) {
+ memset(out, 0, sizeof(*out));
+ unmarshal_4(ctx, &out->fid);
+ unmarshal_stat(ctx, &out->stat);
+}
+
+static void unmarshal_Rwstat(struct _unmarshal_ctx *UNUSED(ctx), struct lib9p_msg_Rwstat *UNUSED(out)) {
+ memset(out, 0, sizeof(*out));
+}
+
+static void unmarshal_Tsession(struct _unmarshal_ctx *ctx, struct lib9p_msg_Tsession *out) {
+ memset(out, 0, sizeof(*out));
+ unmarshal_8(ctx, &out->key);
+}
+
+static void unmarshal_Rsession(struct _unmarshal_ctx *UNUSED(ctx), struct lib9p_msg_Rsession *UNUSED(out)) {
+ memset(out, 0, sizeof(*out));
+}
+
+static void unmarshal_Tsread(struct _unmarshal_ctx *ctx, struct lib9p_msg_Tsread *out) {
+ memset(out, 0, sizeof(*out));
+ unmarshal_4(ctx, &out->fid);
+ unmarshal_2(ctx, &out->nwname);
+ out->wname = ctx->host_extra;
+ ctx->host_extra += sizeof(out->wname[0]) * out->nwname;
+ for (typeof(out->nwname) i = 0; i < out->nwname; i++)
+ unmarshal_s(ctx, &out->wname[i]);
+}
+
+static void unmarshal_Rsread(struct _unmarshal_ctx *ctx, struct lib9p_msg_Rsread *out) {
+ memset(out, 0, sizeof(*out));
+ unmarshal_d(ctx, &out->data);
+}
+
+static void unmarshal_Tswrite(struct _unmarshal_ctx *ctx, struct lib9p_msg_Tswrite *out) {
+ memset(out, 0, sizeof(*out));
+ unmarshal_4(ctx, &out->fid);
+ unmarshal_2(ctx, &out->nwname);
+ out->wname = ctx->host_extra;
+ ctx->host_extra += sizeof(out->wname[0]) * out->nwname;
+ for (typeof(out->nwname) i = 0; i < out->nwname; i++)
+ unmarshal_s(ctx, &out->wname[i]);
+ unmarshal_d(ctx, &out->data);
+}
+
+static void unmarshal_Rswrite(struct _unmarshal_ctx *ctx, struct lib9p_msg_Rswrite *out) {
+ memset(out, 0, sizeof(*out));
+ unmarshal_4(ctx, &out->count);
+}
+
+/* marshal_* ******************************************************************/
+
+static inline bool _marshal_too_large(struct _marshal_ctx *ctx) {
+ lib9p_errorf(ctx->ctx, "%s too large to marshal into %s limit (limit=%"PRIu32")",
+ (ctx->net_bytes[4] % 2 == 0) ? "T-message" : "R-message",
+ ctx->ctx->version ? "negotiated" : ((ctx->net_bytes[4] % 2 == 0) ? "client" : "server"),
+ ctx->ctx->max_msg_size));
+ return true;
+}
+
+static inline bool marshal_1(struct _marshal_ctx *ctx, uint8_t *val) {
+ if (ctx->net_offset + 1 > ctx->max_msg_size)
+ return _marshal_too_large(ctx);
+ out_net_bytes[ctx->net_offset] = *val;
+ ctx->net_offset += 1;
+ return false;
+}
+
+static inline bool marshal_2(struct _marshal_ctx *ctx, uint16_t *val) {
+ if (ctx->net_offset + 2 > ctx->max_msg_size)
+ return _marshal_too_large(ctx);
+ encode_u16le(*val, &out_net_bytes[ctx->net_offset]);
+ ctx->net_offset += 2;
+ return false;
+}
+
+static inline bool marshal_4(struct _marshal_ctx *ctx, uint32_t *val) {
+ if (ctx->net_offset + 4 > ctx->max_msg_size)
+ return true;
+ encode_u32le(*val, &out_net_bytes[ctx->net_offset]);
+ ctx->net_offset += 4;
+ return false;
+}
+
+static inline bool marshal_8(struct _marshal_ctx *ctx, uint64_t *val) {
+ if (ctx->net_offset + 8 > ctx->max_msg_size)
+ return true;
+ encode_u64le(*val, &out_net_bytes[ctx->net_offset]);
+ ctx->net_offset += 8;
+ return false;
+}
+
+static inline bool marshal_d(struct _marshal_ctx *ctx, struct lib9p_d *val) {
+ return marshal_4(ctx, &val->len)
+ || ({
+ bool err = false;
+ for (typeof(val->len) i = 0; i < val->len && !err; i++)
+ err = marshal_1(ctx, &val->dat[i]));
+ err;
+ });
+}
+
+static inline bool marshal_s(struct _marshal_ctx *ctx, struct lib9p_s *val) {
+ return marshal_2(ctx, &val->len)
+ || ({
+ bool err = false;
+ for (typeof(val->len) i = 0; i < val->len && !err; i++)
+ err = marshal_1(ctx, &val->utf8[i]));
+ err;
+ });
+}
+
+static inline bool marshal_qid(struct _marshal_ctx *ctx, struct lib9p_qid *val) {
+ return marshal_1(ctx, &val->type)
+ || marshal_4(ctx, &val->vers)
+ || marshal_8(ctx, &val->path);
+}
+
+static inline bool marshal_stat(struct _marshal_ctx *ctx, struct lib9p_stat *val) {
+ return marshal_2(ctx, &val->stat_size)
+ || marshal_2(ctx, &val->kern_type)
+ || marshal_4(ctx, &val->kern_dev)
+ || marshal_qid(ctx, &val->file_qid)
+ || marshal_4(ctx, &val->file_mode)
+ || marshal_4(ctx, &val->file_atime)
+ || marshal_4(ctx, &val->file_mtime)
+ || marshal_8(ctx, &val->file_size)
+ || marshal_s(ctx, &val->file_name)
+ || marshal_s(ctx, &val->file_owner_uid)
+ || marshal_s(ctx, &val->file_owner_gid)
+ || marshal_s(ctx, &val->file_last_modified_uid)
+ || marshal_s(ctx, &val->file_extension)
+ || marshal_4(ctx, &val->file_owner_n_uid)
+ || marshal_4(ctx, &val->file_owner_n_gid)
+ || marshal_4(ctx, &val->file_last_modified_n_uid);
+}
+
+static bool marshal_Tversion(struct _marshal_ctx *ctx, struct lib9p_msg_Tversion *val) {
+ return marshal_4(ctx, &val->max_msg_size)
+ || marshal_s(ctx, &val->version);
+}
+
+static bool marshal_Rversion(struct _marshal_ctx *ctx, struct lib9p_msg_Rversion *val) {
+ return marshal_4(ctx, &val->max_msg_size)
+ || marshal_s(ctx, &val->version);
+}
+
+static bool marshal_Tauth(struct _marshal_ctx *ctx, struct lib9p_msg_Tauth *val) {
+ return marshal_4(ctx, &val->afid)
+ || marshal_s(ctx, &val->uname)
+ || marshal_s(ctx, &val->aname)
+ || marshal_4(ctx, &val->n_uname);
+}
+
+static bool marshal_Rauth(struct _marshal_ctx *ctx, struct lib9p_msg_Rauth *val) {
+ return marshal_qid(ctx, &val->aqid);
+}
+
+static bool marshal_Tattach(struct _marshal_ctx *ctx, struct lib9p_msg_Tattach *val) {
+ return marshal_4(ctx, &val->fid)
+ || marshal_4(ctx, &val->afid)
+ || marshal_s(ctx, &val->uname)
+ || marshal_s(ctx, &val->aname);
+}
+
+static bool marshal_Rattach(struct _marshal_ctx *ctx, struct lib9p_msg_Rattach *val) {
+ return marshal_qid(ctx, &val->qid);
+}
+
+static bool marshal_Rerror(struct _marshal_ctx *ctx, struct lib9p_msg_Rerror *val) {
+ return marshal_s(ctx, &val->ename)
+ || marshal_4(ctx, &val->errno);
+}
+
+static bool marshal_Tflush(struct _marshal_ctx *ctx, struct lib9p_msg_Tflush *val) {
+ return marshal_2(ctx, &val->oldtag);
+}
+
+static bool marshal_Rflush(struct _marshal_ctx *UNUSED(ctx), struct lib9p_msg_Rflush *UNUSED(val)) {
+ return false;
+}
+
+static bool marshal_Twalk(struct _marshal_ctx *ctx, struct lib9p_msg_Twalk *val) {
+ return marshal_4(ctx, &val->fid)
+ || marshal_4(ctx, &val->newfid)
+ || marshal_2(ctx, &val->nwname)
+ || ({
+ bool err = false;
+ for (typeof(val->nwname) i = 0; i < val->nwname && !err; i++)
+ err = marshal_s(ctx, &val->wname[i]));
+ err;
+ });
+}
+
+static bool marshal_Rwalk(struct _marshal_ctx *ctx, struct lib9p_msg_Rwalk *val) {
+ return marshal_2(ctx, &val->nwqid)
+ || ({
+ bool err = false;
+ for (typeof(val->nwqid) i = 0; i < val->nwqid && !err; i++)
+ err = marshal_qid(ctx, &val->wqid[i]));
+ err;
+ });
+}
+
+static bool marshal_Topen(struct _marshal_ctx *ctx, struct lib9p_msg_Topen *val) {
+ return marshal_4(ctx, &val->fid)
+ || marshal_1(ctx, &val->mode);
+}
+
+static bool marshal_Ropen(struct _marshal_ctx *ctx, struct lib9p_msg_Ropen *val) {
+ return marshal_qid(ctx, &val->qid)
+ || marshal_4(ctx, &val->iounit);
+}
+
+static bool marshal_Tcreate(struct _marshal_ctx *ctx, struct lib9p_msg_Tcreate *val) {
+ return marshal_4(ctx, &val->fid)
+ || marshal_s(ctx, &val->name)
+ || marshal_4(ctx, &val->perm)
+ || marshal_1(ctx, &val->mode);
+}
+
+static bool marshal_Rcreate(struct _marshal_ctx *ctx, struct lib9p_msg_Rcreate *val) {
+ return marshal_qid(ctx, &val->qid)
+ || marshal_4(ctx, &val->iounit);
+}
+
+static bool marshal_Tread(struct _marshal_ctx *ctx, struct lib9p_msg_Tread *val) {
+ return marshal_4(ctx, &val->fid)
+ || marshal_8(ctx, &val->offset)
+ || marshal_4(ctx, &val->count);
+}
+
+static bool marshal_Rread(struct _marshal_ctx *ctx, struct lib9p_msg_Rread *val) {
+ return marshal_d(ctx, &val->data);
+}
+
+static bool marshal_Twrite(struct _marshal_ctx *ctx, struct lib9p_msg_Twrite *val) {
+ return marshal_4(ctx, &val->fid)
+ || marshal_8(ctx, &val->offset)
+ || marshal_d(ctx, &val->data);
+}
+
+static bool marshal_Rwrite(struct _marshal_ctx *ctx, struct lib9p_msg_Rwrite *val) {
+ return marshal_4(ctx, &val->count);
+}
+
+static bool marshal_Tclunk(struct _marshal_ctx *ctx, struct lib9p_msg_Tclunk *val) {
+ return marshal_4(ctx, &val->fid);
+}
+
+static bool marshal_Rclunk(struct _marshal_ctx *UNUSED(ctx), struct lib9p_msg_Rclunk *UNUSED(val)) {
+ return false;
+}
+
+static bool marshal_Tremove(struct _marshal_ctx *ctx, struct lib9p_msg_Tremove *val) {
+ return marshal_4(ctx, &val->fid);
+}
+
+static bool marshal_Rremove(struct _marshal_ctx *UNUSED(ctx), struct lib9p_msg_Rremove *UNUSED(val)) {
+ return false;
+}
+
+static bool marshal_Tstat(struct _marshal_ctx *ctx, struct lib9p_msg_Tstat *val) {
+ return marshal_4(ctx, &val->fid);
+}
+
+static bool marshal_Rstat(struct _marshal_ctx *ctx, struct lib9p_msg_Rstat *val) {
+ return marshal_stat(ctx, &val->stat);
+}
+
+static bool marshal_Twstat(struct _marshal_ctx *ctx, struct lib9p_msg_Twstat *val) {
+ return marshal_4(ctx, &val->fid)
+ || marshal_stat(ctx, &val->stat);
+}
+
+static bool marshal_Rwstat(struct _marshal_ctx *UNUSED(ctx), struct lib9p_msg_Rwstat *UNUSED(val)) {
+ return false;
+}
+
+static bool marshal_Tsession(struct _marshal_ctx *ctx, struct lib9p_msg_Tsession *val) {
+ return marshal_8(ctx, &val->key);
+}
+
+static bool marshal_Rsession(struct _marshal_ctx *UNUSED(ctx), struct lib9p_msg_Rsession *UNUSED(val)) {
+ return false;
+}
+
+static bool marshal_Tsread(struct _marshal_ctx *ctx, struct lib9p_msg_Tsread *val) {
+ return marshal_4(ctx, &val->fid)
+ || marshal_2(ctx, &val->nwname)
+ || ({
+ bool err = false;
+ for (typeof(val->nwname) i = 0; i < val->nwname && !err; i++)
+ err = marshal_s(ctx, &val->wname[i]));
+ err;
+ });
+}
+
+static bool marshal_Rsread(struct _marshal_ctx *ctx, struct lib9p_msg_Rsread *val) {
+ return marshal_d(ctx, &val->data);
+}
+
+static bool marshal_Tswrite(struct _marshal_ctx *ctx, struct lib9p_msg_Tswrite *val) {
+ return marshal_4(ctx, &val->fid)
+ || marshal_2(ctx, &val->nwname)
+ || ({
+ bool err = false;
+ for (typeof(val->nwname) i = 0; i < val->nwname && !err; i++)
+ err = marshal_s(ctx, &val->wname[i]));
+ err;
+ })
+ || marshal_d(ctx, &val->data);
+}
+
+static bool marshal_Rswrite(struct _marshal_ctx *ctx, struct lib9p_msg_Rswrite *val) {
+ return marshal_4(ctx, &val->count);
+}
+
+/* vtables ********************************************************************/
+
+struct _vtable_version _lib9p_vtables[LIB9P_VER_NUM] = {
+ [LIB9P_VER_UNINITIALIZED] = {
+ [LIB9P_TYP_Tversion] = {
+ r.unmarshal_basesize = sizeof(struct lib9p_msg_Tversion),
+ r.unmarshal_extrasize = checksize_Tversion,
+ r.unmarshal = unmarshal_Tversion,
+ r.marshal = (_marshal_fn_t)marshal_Tversion,
+ },
+ [LIB9P_TYP_Rversion] = {
+ r.unmarshal_basesize = sizeof(struct lib9p_msg_Rversion),
+ r.unmarshal_extrasize = checksize_Rversion,
+ r.unmarshal = unmarshal_Rversion,
+ r.marshal = (_marshal_fn_t)marshal_Rversion,
+ },
+ },
+ [LIB9P_VER_9P2000] = {
+ [LIB9P_TYP_Tversion] = {
+ r.unmarshal_basesize = sizeof(struct lib9p_msg_Tversion),
+ r.unmarshal_extrasize = checksize_Tversion,
+ r.unmarshal = unmarshal_Tversion,
+ r.marshal = (_marshal_fn_t)marshal_Tversion,
+ },
+ [LIB9P_TYP_Rversion] = {
+ r.unmarshal_basesize = sizeof(struct lib9p_msg_Rversion),
+ r.unmarshal_extrasize = checksize_Rversion,
+ r.unmarshal = unmarshal_Rversion,
+ r.marshal = (_marshal_fn_t)marshal_Rversion,
+ },
+ [LIB9P_TYP_Tauth] = {
+ r.unmarshal_basesize = sizeof(struct lib9p_msg_Tauth),
+ r.unmarshal_extrasize = checksize_Tauth,
+ r.unmarshal = unmarshal_Tauth,
+ r.marshal = (_marshal_fn_t)marshal_Tauth,
+ },
+ [LIB9P_TYP_Rauth] = {
+ r.unmarshal_basesize = sizeof(struct lib9p_msg_Rauth),
+ r.unmarshal_extrasize = checksize_Rauth,
+ r.unmarshal = unmarshal_Rauth,
+ r.marshal = (_marshal_fn_t)marshal_Rauth,
+ },
+ [LIB9P_TYP_Tattach] = {
+ r.unmarshal_basesize = sizeof(struct lib9p_msg_Tattach),
+ r.unmarshal_extrasize = checksize_Tattach,
+ r.unmarshal = unmarshal_Tattach,
+ r.marshal = (_marshal_fn_t)marshal_Tattach,
+ },
+ [LIB9P_TYP_Rattach] = {
+ r.unmarshal_basesize = sizeof(struct lib9p_msg_Rattach),
+ r.unmarshal_extrasize = checksize_Rattach,
+ r.unmarshal = unmarshal_Rattach,
+ r.marshal = (_marshal_fn_t)marshal_Rattach,
+ },
+ [LIB9P_TYP_Rerror] = {
+ r.unmarshal_basesize = sizeof(struct lib9p_msg_Rerror),
+ r.unmarshal_extrasize = checksize_Rerror,
+ r.unmarshal = unmarshal_Rerror,
+ r.marshal = (_marshal_fn_t)marshal_Rerror,
+ },
+ [LIB9P_TYP_Tflush] = {
+ r.unmarshal_basesize = sizeof(struct lib9p_msg_Tflush),
+ r.unmarshal_extrasize = checksize_Tflush,
+ r.unmarshal = unmarshal_Tflush,
+ r.marshal = (_marshal_fn_t)marshal_Tflush,
+ },
+ [LIB9P_TYP_Rflush] = {
+ r.unmarshal_basesize = sizeof(struct lib9p_msg_Rflush),
+ r.unmarshal_extrasize = checksize_Rflush,
+ r.unmarshal = unmarshal_Rflush,
+ r.marshal = (_marshal_fn_t)marshal_Rflush,
+ },
+ [LIB9P_TYP_Twalk] = {
+ r.unmarshal_basesize = sizeof(struct lib9p_msg_Twalk),
+ r.unmarshal_extrasize = checksize_Twalk,
+ r.unmarshal = unmarshal_Twalk,
+ r.marshal = (_marshal_fn_t)marshal_Twalk,
+ },
+ [LIB9P_TYP_Rwalk] = {
+ r.unmarshal_basesize = sizeof(struct lib9p_msg_Rwalk),
+ r.unmarshal_extrasize = checksize_Rwalk,
+ r.unmarshal = unmarshal_Rwalk,
+ r.marshal = (_marshal_fn_t)marshal_Rwalk,
+ },
+ [LIB9P_TYP_Topen] = {
+ r.unmarshal_basesize = sizeof(struct lib9p_msg_Topen),
+ r.unmarshal_extrasize = checksize_Topen,
+ r.unmarshal = unmarshal_Topen,
+ r.marshal = (_marshal_fn_t)marshal_Topen,
+ },
+ [LIB9P_TYP_Ropen] = {
+ r.unmarshal_basesize = sizeof(struct lib9p_msg_Ropen),
+ r.unmarshal_extrasize = checksize_Ropen,
+ r.unmarshal = unmarshal_Ropen,
+ r.marshal = (_marshal_fn_t)marshal_Ropen,
+ },
+ [LIB9P_TYP_Tcreate] = {
+ r.unmarshal_basesize = sizeof(struct lib9p_msg_Tcreate),
+ r.unmarshal_extrasize = checksize_Tcreate,
+ r.unmarshal = unmarshal_Tcreate,
+ r.marshal = (_marshal_fn_t)marshal_Tcreate,
+ },
+ [LIB9P_TYP_Rcreate] = {
+ r.unmarshal_basesize = sizeof(struct lib9p_msg_Rcreate),
+ r.unmarshal_extrasize = checksize_Rcreate,
+ r.unmarshal = unmarshal_Rcreate,
+ r.marshal = (_marshal_fn_t)marshal_Rcreate,
+ },
+ [LIB9P_TYP_Tread] = {
+ r.unmarshal_basesize = sizeof(struct lib9p_msg_Tread),
+ r.unmarshal_extrasize = checksize_Tread,
+ r.unmarshal = unmarshal_Tread,
+ r.marshal = (_marshal_fn_t)marshal_Tread,
+ },
+ [LIB9P_TYP_Rread] = {
+ r.unmarshal_basesize = sizeof(struct lib9p_msg_Rread),
+ r.unmarshal_extrasize = checksize_Rread,
+ r.unmarshal = unmarshal_Rread,
+ r.marshal = (_marshal_fn_t)marshal_Rread,
+ },
+ [LIB9P_TYP_Twrite] = {
+ r.unmarshal_basesize = sizeof(struct lib9p_msg_Twrite),
+ r.unmarshal_extrasize = checksize_Twrite,
+ r.unmarshal = unmarshal_Twrite,
+ r.marshal = (_marshal_fn_t)marshal_Twrite,
+ },
+ [LIB9P_TYP_Rwrite] = {
+ r.unmarshal_basesize = sizeof(struct lib9p_msg_Rwrite),
+ r.unmarshal_extrasize = checksize_Rwrite,
+ r.unmarshal = unmarshal_Rwrite,
+ r.marshal = (_marshal_fn_t)marshal_Rwrite,
+ },
+ [LIB9P_TYP_Tclunk] = {
+ r.unmarshal_basesize = sizeof(struct lib9p_msg_Tclunk),
+ r.unmarshal_extrasize = checksize_Tclunk,
+ r.unmarshal = unmarshal_Tclunk,
+ r.marshal = (_marshal_fn_t)marshal_Tclunk,
+ },
+ [LIB9P_TYP_Rclunk] = {
+ r.unmarshal_basesize = sizeof(struct lib9p_msg_Rclunk),
+ r.unmarshal_extrasize = checksize_Rclunk,
+ r.unmarshal = unmarshal_Rclunk,
+ r.marshal = (_marshal_fn_t)marshal_Rclunk,
+ },
+ [LIB9P_TYP_Tremove] = {
+ r.unmarshal_basesize = sizeof(struct lib9p_msg_Tremove),
+ r.unmarshal_extrasize = checksize_Tremove,
+ r.unmarshal = unmarshal_Tremove,
+ r.marshal = (_marshal_fn_t)marshal_Tremove,
+ },
+ [LIB9P_TYP_Rremove] = {
+ r.unmarshal_basesize = sizeof(struct lib9p_msg_Rremove),
+ r.unmarshal_extrasize = checksize_Rremove,
+ r.unmarshal = unmarshal_Rremove,
+ r.marshal = (_marshal_fn_t)marshal_Rremove,
+ },
+ [LIB9P_TYP_Tstat] = {
+ r.unmarshal_basesize = sizeof(struct lib9p_msg_Tstat),
+ r.unmarshal_extrasize = checksize_Tstat,
+ r.unmarshal = unmarshal_Tstat,
+ r.marshal = (_marshal_fn_t)marshal_Tstat,
+ },
+ [LIB9P_TYP_Rstat] = {
+ r.unmarshal_basesize = sizeof(struct lib9p_msg_Rstat),
+ r.unmarshal_extrasize = checksize_Rstat,
+ r.unmarshal = unmarshal_Rstat,
+ r.marshal = (_marshal_fn_t)marshal_Rstat,
+ },
+ [LIB9P_TYP_Twstat] = {
+ r.unmarshal_basesize = sizeof(struct lib9p_msg_Twstat),
+ r.unmarshal_extrasize = checksize_Twstat,
+ r.unmarshal = unmarshal_Twstat,
+ r.marshal = (_marshal_fn_t)marshal_Twstat,
+ },
+ [LIB9P_TYP_Rwstat] = {
+ r.unmarshal_basesize = sizeof(struct lib9p_msg_Rwstat),
+ r.unmarshal_extrasize = checksize_Rwstat,
+ r.unmarshal = unmarshal_Rwstat,
+ r.marshal = (_marshal_fn_t)marshal_Rwstat,
+ },
+ },
+ [LIB9P_VER_9P2000_e] = {
+ [LIB9P_TYP_Tversion] = {
+ r.unmarshal_basesize = sizeof(struct lib9p_msg_Tversion),
+ r.unmarshal_extrasize = checksize_Tversion,
+ r.unmarshal = unmarshal_Tversion,
+ r.marshal = (_marshal_fn_t)marshal_Tversion,
+ },
+ [LIB9P_TYP_Rversion] = {
+ r.unmarshal_basesize = sizeof(struct lib9p_msg_Rversion),
+ r.unmarshal_extrasize = checksize_Rversion,
+ r.unmarshal = unmarshal_Rversion,
+ r.marshal = (_marshal_fn_t)marshal_Rversion,
+ },
+ [LIB9P_TYP_Tauth] = {
+ r.unmarshal_basesize = sizeof(struct lib9p_msg_Tauth),
+ r.unmarshal_extrasize = checksize_Tauth,
+ r.unmarshal = unmarshal_Tauth,
+ r.marshal = (_marshal_fn_t)marshal_Tauth,
+ },
+ [LIB9P_TYP_Rauth] = {
+ r.unmarshal_basesize = sizeof(struct lib9p_msg_Rauth),
+ r.unmarshal_extrasize = checksize_Rauth,
+ r.unmarshal = unmarshal_Rauth,
+ r.marshal = (_marshal_fn_t)marshal_Rauth,
+ },
+ [LIB9P_TYP_Tattach] = {
+ r.unmarshal_basesize = sizeof(struct lib9p_msg_Tattach),
+ r.unmarshal_extrasize = checksize_Tattach,
+ r.unmarshal = unmarshal_Tattach,
+ r.marshal = (_marshal_fn_t)marshal_Tattach,
+ },
+ [LIB9P_TYP_Rattach] = {
+ r.unmarshal_basesize = sizeof(struct lib9p_msg_Rattach),
+ r.unmarshal_extrasize = checksize_Rattach,
+ r.unmarshal = unmarshal_Rattach,
+ r.marshal = (_marshal_fn_t)marshal_Rattach,
+ },
+ [LIB9P_TYP_Rerror] = {
+ r.unmarshal_basesize = sizeof(struct lib9p_msg_Rerror),
+ r.unmarshal_extrasize = checksize_Rerror,
+ r.unmarshal = unmarshal_Rerror,
+ r.marshal = (_marshal_fn_t)marshal_Rerror,
+ },
+ [LIB9P_TYP_Tflush] = {
+ r.unmarshal_basesize = sizeof(struct lib9p_msg_Tflush),
+ r.unmarshal_extrasize = checksize_Tflush,
+ r.unmarshal = unmarshal_Tflush,
+ r.marshal = (_marshal_fn_t)marshal_Tflush,
+ },
+ [LIB9P_TYP_Rflush] = {
+ r.unmarshal_basesize = sizeof(struct lib9p_msg_Rflush),
+ r.unmarshal_extrasize = checksize_Rflush,
+ r.unmarshal = unmarshal_Rflush,
+ r.marshal = (_marshal_fn_t)marshal_Rflush,
+ },
+ [LIB9P_TYP_Twalk] = {
+ r.unmarshal_basesize = sizeof(struct lib9p_msg_Twalk),
+ r.unmarshal_extrasize = checksize_Twalk,
+ r.unmarshal = unmarshal_Twalk,
+ r.marshal = (_marshal_fn_t)marshal_Twalk,
+ },
+ [LIB9P_TYP_Rwalk] = {
+ r.unmarshal_basesize = sizeof(struct lib9p_msg_Rwalk),
+ r.unmarshal_extrasize = checksize_Rwalk,
+ r.unmarshal = unmarshal_Rwalk,
+ r.marshal = (_marshal_fn_t)marshal_Rwalk,
+ },
+ [LIB9P_TYP_Topen] = {
+ r.unmarshal_basesize = sizeof(struct lib9p_msg_Topen),
+ r.unmarshal_extrasize = checksize_Topen,
+ r.unmarshal = unmarshal_Topen,
+ r.marshal = (_marshal_fn_t)marshal_Topen,
+ },
+ [LIB9P_TYP_Ropen] = {
+ r.unmarshal_basesize = sizeof(struct lib9p_msg_Ropen),
+ r.unmarshal_extrasize = checksize_Ropen,
+ r.unmarshal = unmarshal_Ropen,
+ r.marshal = (_marshal_fn_t)marshal_Ropen,
+ },
+ [LIB9P_TYP_Tcreate] = {
+ r.unmarshal_basesize = sizeof(struct lib9p_msg_Tcreate),
+ r.unmarshal_extrasize = checksize_Tcreate,
+ r.unmarshal = unmarshal_Tcreate,
+ r.marshal = (_marshal_fn_t)marshal_Tcreate,
+ },
+ [LIB9P_TYP_Rcreate] = {
+ r.unmarshal_basesize = sizeof(struct lib9p_msg_Rcreate),
+ r.unmarshal_extrasize = checksize_Rcreate,
+ r.unmarshal = unmarshal_Rcreate,
+ r.marshal = (_marshal_fn_t)marshal_Rcreate,
+ },
+ [LIB9P_TYP_Tread] = {
+ r.unmarshal_basesize = sizeof(struct lib9p_msg_Tread),
+ r.unmarshal_extrasize = checksize_Tread,
+ r.unmarshal = unmarshal_Tread,
+ r.marshal = (_marshal_fn_t)marshal_Tread,
+ },
+ [LIB9P_TYP_Rread] = {
+ r.unmarshal_basesize = sizeof(struct lib9p_msg_Rread),
+ r.unmarshal_extrasize = checksize_Rread,
+ r.unmarshal = unmarshal_Rread,
+ r.marshal = (_marshal_fn_t)marshal_Rread,
+ },
+ [LIB9P_TYP_Twrite] = {
+ r.unmarshal_basesize = sizeof(struct lib9p_msg_Twrite),
+ r.unmarshal_extrasize = checksize_Twrite,
+ r.unmarshal = unmarshal_Twrite,
+ r.marshal = (_marshal_fn_t)marshal_Twrite,
+ },
+ [LIB9P_TYP_Rwrite] = {
+ r.unmarshal_basesize = sizeof(struct lib9p_msg_Rwrite),
+ r.unmarshal_extrasize = checksize_Rwrite,
+ r.unmarshal = unmarshal_Rwrite,
+ r.marshal = (_marshal_fn_t)marshal_Rwrite,
+ },
+ [LIB9P_TYP_Tclunk] = {
+ r.unmarshal_basesize = sizeof(struct lib9p_msg_Tclunk),
+ r.unmarshal_extrasize = checksize_Tclunk,
+ r.unmarshal = unmarshal_Tclunk,
+ r.marshal = (_marshal_fn_t)marshal_Tclunk,
+ },
+ [LIB9P_TYP_Rclunk] = {
+ r.unmarshal_basesize = sizeof(struct lib9p_msg_Rclunk),
+ r.unmarshal_extrasize = checksize_Rclunk,
+ r.unmarshal = unmarshal_Rclunk,
+ r.marshal = (_marshal_fn_t)marshal_Rclunk,
+ },
+ [LIB9P_TYP_Tremove] = {
+ r.unmarshal_basesize = sizeof(struct lib9p_msg_Tremove),
+ r.unmarshal_extrasize = checksize_Tremove,
+ r.unmarshal = unmarshal_Tremove,
+ r.marshal = (_marshal_fn_t)marshal_Tremove,
+ },
+ [LIB9P_TYP_Rremove] = {
+ r.unmarshal_basesize = sizeof(struct lib9p_msg_Rremove),
+ r.unmarshal_extrasize = checksize_Rremove,
+ r.unmarshal = unmarshal_Rremove,
+ r.marshal = (_marshal_fn_t)marshal_Rremove,
+ },
+ [LIB9P_TYP_Tstat] = {
+ r.unmarshal_basesize = sizeof(struct lib9p_msg_Tstat),
+ r.unmarshal_extrasize = checksize_Tstat,
+ r.unmarshal = unmarshal_Tstat,
+ r.marshal = (_marshal_fn_t)marshal_Tstat,
+ },
+ [LIB9P_TYP_Rstat] = {
+ r.unmarshal_basesize = sizeof(struct lib9p_msg_Rstat),
+ r.unmarshal_extrasize = checksize_Rstat,
+ r.unmarshal = unmarshal_Rstat,
+ r.marshal = (_marshal_fn_t)marshal_Rstat,
+ },
+ [LIB9P_TYP_Twstat] = {
+ r.unmarshal_basesize = sizeof(struct lib9p_msg_Twstat),
+ r.unmarshal_extrasize = checksize_Twstat,
+ r.unmarshal = unmarshal_Twstat,
+ r.marshal = (_marshal_fn_t)marshal_Twstat,
+ },
+ [LIB9P_TYP_Rwstat] = {
+ r.unmarshal_basesize = sizeof(struct lib9p_msg_Rwstat),
+ r.unmarshal_extrasize = checksize_Rwstat,
+ r.unmarshal = unmarshal_Rwstat,
+ r.marshal = (_marshal_fn_t)marshal_Rwstat,
+ },
+ [LIB9P_TYP_Tsession] = {
+ r.unmarshal_basesize = sizeof(struct lib9p_msg_Tsession),
+ r.unmarshal_extrasize = checksize_Tsession,
+ r.unmarshal = unmarshal_Tsession,
+ r.marshal = (_marshal_fn_t)marshal_Tsession,
+ },
+ [LIB9P_TYP_Rsession] = {
+ r.unmarshal_basesize = sizeof(struct lib9p_msg_Rsession),
+ r.unmarshal_extrasize = checksize_Rsession,
+ r.unmarshal = unmarshal_Rsession,
+ r.marshal = (_marshal_fn_t)marshal_Rsession,
+ },
+ [LIB9P_TYP_Tsread] = {
+ r.unmarshal_basesize = sizeof(struct lib9p_msg_Tsread),
+ r.unmarshal_extrasize = checksize_Tsread,
+ r.unmarshal = unmarshal_Tsread,
+ r.marshal = (_marshal_fn_t)marshal_Tsread,
+ },
+ [LIB9P_TYP_Rsread] = {
+ r.unmarshal_basesize = sizeof(struct lib9p_msg_Rsread),
+ r.unmarshal_extrasize = checksize_Rsread,
+ r.unmarshal = unmarshal_Rsread,
+ r.marshal = (_marshal_fn_t)marshal_Rsread,
+ },
+ [LIB9P_TYP_Tswrite] = {
+ r.unmarshal_basesize = sizeof(struct lib9p_msg_Tswrite),
+ r.unmarshal_extrasize = checksize_Tswrite,
+ r.unmarshal = unmarshal_Tswrite,
+ r.marshal = (_marshal_fn_t)marshal_Tswrite,
+ },
+ [LIB9P_TYP_Rswrite] = {
+ r.unmarshal_basesize = sizeof(struct lib9p_msg_Rswrite),
+ r.unmarshal_extrasize = checksize_Rswrite,
+ r.unmarshal = unmarshal_Rswrite,
+ r.marshal = (_marshal_fn_t)marshal_Rswrite,
+ },
+ },
+ [LIB9P_VER_9P2000_u] = {
+ [LIB9P_TYP_Tversion] = {
+ r.unmarshal_basesize = sizeof(struct lib9p_msg_Tversion),
+ r.unmarshal_extrasize = checksize_Tversion,
+ r.unmarshal = unmarshal_Tversion,
+ r.marshal = (_marshal_fn_t)marshal_Tversion,
+ },
+ [LIB9P_TYP_Rversion] = {
+ r.unmarshal_basesize = sizeof(struct lib9p_msg_Rversion),
+ r.unmarshal_extrasize = checksize_Rversion,
+ r.unmarshal = unmarshal_Rversion,
+ r.marshal = (_marshal_fn_t)marshal_Rversion,
+ },
+ [LIB9P_TYP_Tauth] = {
+ r.unmarshal_basesize = sizeof(struct lib9p_msg_Tauth),
+ r.unmarshal_extrasize = checksize_Tauth,
+ r.unmarshal = unmarshal_Tauth,
+ r.marshal = (_marshal_fn_t)marshal_Tauth,
+ },
+ [LIB9P_TYP_Rauth] = {
+ r.unmarshal_basesize = sizeof(struct lib9p_msg_Rauth),
+ r.unmarshal_extrasize = checksize_Rauth,
+ r.unmarshal = unmarshal_Rauth,
+ r.marshal = (_marshal_fn_t)marshal_Rauth,
+ },
+ [LIB9P_TYP_Tattach] = {
+ r.unmarshal_basesize = sizeof(struct lib9p_msg_Tattach),
+ r.unmarshal_extrasize = checksize_Tattach,
+ r.unmarshal = unmarshal_Tattach,
+ r.marshal = (_marshal_fn_t)marshal_Tattach,
+ },
+ [LIB9P_TYP_Rattach] = {
+ r.unmarshal_basesize = sizeof(struct lib9p_msg_Rattach),
+ r.unmarshal_extrasize = checksize_Rattach,
+ r.unmarshal = unmarshal_Rattach,
+ r.marshal = (_marshal_fn_t)marshal_Rattach,
+ },
+ [LIB9P_TYP_Rerror] = {
+ r.unmarshal_basesize = sizeof(struct lib9p_msg_Rerror),
+ r.unmarshal_extrasize = checksize_Rerror,
+ r.unmarshal = unmarshal_Rerror,
+ r.marshal = (_marshal_fn_t)marshal_Rerror,
+ },
+ [LIB9P_TYP_Tflush] = {
+ r.unmarshal_basesize = sizeof(struct lib9p_msg_Tflush),
+ r.unmarshal_extrasize = checksize_Tflush,
+ r.unmarshal = unmarshal_Tflush,
+ r.marshal = (_marshal_fn_t)marshal_Tflush,
+ },
+ [LIB9P_TYP_Rflush] = {
+ r.unmarshal_basesize = sizeof(struct lib9p_msg_Rflush),
+ r.unmarshal_extrasize = checksize_Rflush,
+ r.unmarshal = unmarshal_Rflush,
+ r.marshal = (_marshal_fn_t)marshal_Rflush,
+ },
+ [LIB9P_TYP_Twalk] = {
+ r.unmarshal_basesize = sizeof(struct lib9p_msg_Twalk),
+ r.unmarshal_extrasize = checksize_Twalk,
+ r.unmarshal = unmarshal_Twalk,
+ r.marshal = (_marshal_fn_t)marshal_Twalk,
+ },
+ [LIB9P_TYP_Rwalk] = {
+ r.unmarshal_basesize = sizeof(struct lib9p_msg_Rwalk),
+ r.unmarshal_extrasize = checksize_Rwalk,
+ r.unmarshal = unmarshal_Rwalk,
+ r.marshal = (_marshal_fn_t)marshal_Rwalk,
+ },
+ [LIB9P_TYP_Topen] = {
+ r.unmarshal_basesize = sizeof(struct lib9p_msg_Topen),
+ r.unmarshal_extrasize = checksize_Topen,
+ r.unmarshal = unmarshal_Topen,
+ r.marshal = (_marshal_fn_t)marshal_Topen,
+ },
+ [LIB9P_TYP_Ropen] = {
+ r.unmarshal_basesize = sizeof(struct lib9p_msg_Ropen),
+ r.unmarshal_extrasize = checksize_Ropen,
+ r.unmarshal = unmarshal_Ropen,
+ r.marshal = (_marshal_fn_t)marshal_Ropen,
+ },
+ [LIB9P_TYP_Tcreate] = {
+ r.unmarshal_basesize = sizeof(struct lib9p_msg_Tcreate),
+ r.unmarshal_extrasize = checksize_Tcreate,
+ r.unmarshal = unmarshal_Tcreate,
+ r.marshal = (_marshal_fn_t)marshal_Tcreate,
+ },
+ [LIB9P_TYP_Rcreate] = {
+ r.unmarshal_basesize = sizeof(struct lib9p_msg_Rcreate),
+ r.unmarshal_extrasize = checksize_Rcreate,
+ r.unmarshal = unmarshal_Rcreate,
+ r.marshal = (_marshal_fn_t)marshal_Rcreate,
+ },
+ [LIB9P_TYP_Tread] = {
+ r.unmarshal_basesize = sizeof(struct lib9p_msg_Tread),
+ r.unmarshal_extrasize = checksize_Tread,
+ r.unmarshal = unmarshal_Tread,
+ r.marshal = (_marshal_fn_t)marshal_Tread,
+ },
+ [LIB9P_TYP_Rread] = {
+ r.unmarshal_basesize = sizeof(struct lib9p_msg_Rread),
+ r.unmarshal_extrasize = checksize_Rread,
+ r.unmarshal = unmarshal_Rread,
+ r.marshal = (_marshal_fn_t)marshal_Rread,
+ },
+ [LIB9P_TYP_Twrite] = {
+ r.unmarshal_basesize = sizeof(struct lib9p_msg_Twrite),
+ r.unmarshal_extrasize = checksize_Twrite,
+ r.unmarshal = unmarshal_Twrite,
+ r.marshal = (_marshal_fn_t)marshal_Twrite,
+ },
+ [LIB9P_TYP_Rwrite] = {
+ r.unmarshal_basesize = sizeof(struct lib9p_msg_Rwrite),
+ r.unmarshal_extrasize = checksize_Rwrite,
+ r.unmarshal = unmarshal_Rwrite,
+ r.marshal = (_marshal_fn_t)marshal_Rwrite,
+ },
+ [LIB9P_TYP_Tclunk] = {
+ r.unmarshal_basesize = sizeof(struct lib9p_msg_Tclunk),
+ r.unmarshal_extrasize = checksize_Tclunk,
+ r.unmarshal = unmarshal_Tclunk,
+ r.marshal = (_marshal_fn_t)marshal_Tclunk,
+ },
+ [LIB9P_TYP_Rclunk] = {
+ r.unmarshal_basesize = sizeof(struct lib9p_msg_Rclunk),
+ r.unmarshal_extrasize = checksize_Rclunk,
+ r.unmarshal = unmarshal_Rclunk,
+ r.marshal = (_marshal_fn_t)marshal_Rclunk,
+ },
+ [LIB9P_TYP_Tremove] = {
+ r.unmarshal_basesize = sizeof(struct lib9p_msg_Tremove),
+ r.unmarshal_extrasize = checksize_Tremove,
+ r.unmarshal = unmarshal_Tremove,
+ r.marshal = (_marshal_fn_t)marshal_Tremove,
+ },
+ [LIB9P_TYP_Rremove] = {
+ r.unmarshal_basesize = sizeof(struct lib9p_msg_Rremove),
+ r.unmarshal_extrasize = checksize_Rremove,
+ r.unmarshal = unmarshal_Rremove,
+ r.marshal = (_marshal_fn_t)marshal_Rremove,
+ },
+ [LIB9P_TYP_Tstat] = {
+ r.unmarshal_basesize = sizeof(struct lib9p_msg_Tstat),
+ r.unmarshal_extrasize = checksize_Tstat,
+ r.unmarshal = unmarshal_Tstat,
+ r.marshal = (_marshal_fn_t)marshal_Tstat,
+ },
+ [LIB9P_TYP_Rstat] = {
+ r.unmarshal_basesize = sizeof(struct lib9p_msg_Rstat),
+ r.unmarshal_extrasize = checksize_Rstat,
+ r.unmarshal = unmarshal_Rstat,
+ r.marshal = (_marshal_fn_t)marshal_Rstat,
+ },
+ [LIB9P_TYP_Twstat] = {
+ r.unmarshal_basesize = sizeof(struct lib9p_msg_Twstat),
+ r.unmarshal_extrasize = checksize_Twstat,
+ r.unmarshal = unmarshal_Twstat,
+ r.marshal = (_marshal_fn_t)marshal_Twstat,
+ },
+ [LIB9P_TYP_Rwstat] = {
+ r.unmarshal_basesize = sizeof(struct lib9p_msg_Rwstat),
+ r.unmarshal_extrasize = checksize_Rwstat,
+ r.unmarshal = unmarshal_Rwstat,
+ r.marshal = (_marshal_fn_t)marshal_Rwstat,
+ },
+ },
+};
diff --git a/lib9p/types.gen b/lib9p/types.gen
new file mode 100755
index 0000000..141b591
--- /dev/null
+++ b/lib9p/types.gen
@@ -0,0 +1,611 @@
+#!/usr/bin/env python
+# lib9p/types.gen - Generate C marshalers/unmarshalers for .txt files
+# defining 9P protocol variants.
+#
+# Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com>
+# SPDX-Licence-Identifier: AGPL-3.0-or-later
+
+import enum
+import os.path
+import re
+from typing import Callable
+
+# Parse *.txt ##################################################################
+
+
+class Atom(enum.Enum):
+ u8 = 1
+ u16 = 2
+ u32 = 4
+ u64 = 8
+
+ @property
+ def name(self) -> str:
+ return str(self.value)
+
+ @property
+ def static_size(self) -> int:
+ return self.value
+
+
+# `msgid/structname = "member1 member2..."`
+# `structname = "member1 member2..."`
+# `structname += "member1 member2..."`
+class Struct:
+ msgid: int | None = None
+ msgver: set[str]
+ name: str
+ members: list["Member"]
+
+ def __init__(self) -> None:
+ self.msgver = set()
+
+ @property
+ def static_size(self) -> int | None:
+ size = 0
+ for member in self.members:
+ msize = member.static_size
+ if msize is None:
+ return None
+ size += msize
+ return size
+
+
+# `cnt*(name[typ])`
+# the `cnt*(...)` wrapper is optional
+class Member:
+ cnt: str | None = None
+ name: str
+ typ: Atom | Struct
+ ver: set[str]
+
+ @property
+ def static_size(self) -> int | None:
+ if self.cnt:
+ return None
+ return self.typ.static_size
+
+
+re_membername = "(?:[a-zA-Z_][a-zA-Z_0-9]*)"
+re_memberspec = (
+ f"(?:(?P<cnt>{re_membername})\\*\\()?(?P<name>{re_membername})\\[(?P<typ>.*)\\]\\)?"
+)
+
+
+def parse_members(
+ ver: str,
+ env: dict[str, Atom | Struct], existing: list[Member], specs: str
+) -> list[Member]:
+ ret = existing
+ for spec in specs.split():
+ m = re.fullmatch(re_memberspec, spec)
+ if not m:
+ raise SyntaxError(f"invalid member spec {repr(spec)}")
+
+ member = Member()
+ member.ver = {ver}
+
+ member.name = m.group("name")
+ if any(x.name == member.name for x in ret):
+ raise ValueError(f"duplicate member name {repr(member.name)}")
+
+ if m.group("typ") not in env:
+ raise NameError(f"Unknown type {repr(m.group(2))}")
+ member.typ = env[m.group("typ")]
+
+ if cnt := m.group("cnt"):
+ if len(ret) == 0 or ret[-1].name != cnt:
+ raise ValueError(f"list count must be previous item: {repr(cnt)}")
+ if not isinstance(ret[-1].typ, Atom):
+ raise ValueError(f"list count must be an integer type: {repr(cnt)}")
+ member.cnt = cnt
+
+ ret += [member]
+ return ret
+
+
+re_version = r'version\s+"(?P<version>[^"]+)"'
+re_import = r'from\s+(?P<file>\S+)\s+import\s+(?P<syms>\S+(?:\s*,\s*\S+)*)\s*'
+re_structspec = (
+ r'(?:(?P<msgid>[0-9]+)/)?(?P<name>\S+)\s*(?P<op>\+?=)\s*"(?P<members>[^"]*)"'
+)
+re_structspec_cont = r'"(?P<members>[^"]*)"'
+
+
+def parse_file(filename: str, get_include: Callable[[str], tuple[str, list[Struct]]]) -> tuple[str, list[Struct]]:
+ version: str | None = None
+ env: dict[str, Atom | Struct] = {
+ "1": Atom.u8,
+ "2": Atom.u16,
+ "4": Atom.u32,
+ "8": Atom.u64,
+ }
+ with open(filename, "r") as fh:
+ prev: Struct | None = None
+ for line in fh:
+ line = line.split("#", 1)[0].strip()
+ if not line:
+ continue
+ if m := re.fullmatch(re_version, line):
+ if version:
+ raise SyntaxError("must have exactly 1 version line")
+ version = m.group("version")
+ elif m := re.fullmatch(re_import, line):
+ if not version:
+ raise SyntaxError("must have exactly 1 version line")
+ other_version, other_structs = get_include(m.group("file"))
+ for symname in m.group("syms").split(sep=","):
+ symname = symname.strip()
+ for struct in other_structs:
+ if struct.name == symname or symname == '*':
+ if struct.msgid:
+ struct.msgver.add(version)
+ for member in struct.members:
+ if other_version in member.ver:
+ member.ver.add(version)
+ env[struct.name] = struct
+ elif m := re.fullmatch(re_structspec, line):
+ if not version:
+ raise SyntaxError("must have exactly 1 version line")
+ if m.group("op") == "+=" and m.group("msgid"):
+ raise SyntaxError("cannot += to a message that is not yet defined")
+ match m.group("op"):
+ case "=":
+ struct = Struct()
+ if m.group("msgid"):
+ struct.msgid = int(m.group("msgid"))
+ struct.msgver.add(version)
+ struct.name = m.group("name")
+ struct.members = parse_members(version, env, [], m.group("members"))
+ env[struct.name] = struct
+ prev = struct
+ case "+=":
+ if m.group("name") not in env:
+ raise NameError(f"Unknown type {repr(m.group('name'))}")
+ _struct = env[m.group("name")]
+ if not isinstance(_struct, Struct):
+ raise NameError(
+ f"Type {repr(m.group('name'))} is not a struct"
+ )
+ struct = _struct
+ struct.members = parse_members(version,
+ env, struct.members, m.group("members")
+ )
+ prev = struct
+ elif m := re.fullmatch(re_structspec_cont, line):
+ if not prev:
+ raise SyntaxError("continuation line must come after a struct line")
+ assert(version)
+ prev.members = parse_members(version, env, prev.members, m.group("members"))
+ else:
+ raise SyntaxError(f"invalid line {repr(line)}")
+ if not version:
+ raise SyntaxError("must have exactly 1 version line")
+ structs = [x for x in env.values() if isinstance(x, Struct)]
+ return version, structs
+
+# Generate C ###################################################################
+
+
+def c_typename(idprefix: str, typ: Atom | Struct) -> str:
+ match typ:
+ case Atom():
+ return f"uint{typ.value*8}_t"
+ case Struct():
+ if typ.msgid is not None:
+ return f"struct {idprefix}msg_{typ.name}"
+ return f"struct {idprefix}{typ.name}"
+ case _:
+ raise ValueError(f"not a type: {typ.__class__.__name__}")
+
+def c_vercomment(versions: set[str]) -> str|None:
+ if "9P2000" in versions:
+ return None
+ return "/* "+(", ".join(sorted(versions)))+" */"
+
+def c_ver(idprefix: str, ver: str) -> str:
+ return f"{idprefix.upper()}VER_{ver.replace('.', '_')}"
+
+def gen_h(idprefix: str, versions: set[str], structs: list[Struct]) -> str:
+ guard = "_LIB9P__TYPES_H_"
+ ret = f"""/* Generated by `{' '.join(sys.argv)}`. DO NOT EDIT! */
+
+#ifndef {guard}
+#define {guard}
+
+#include <stdint.h>
+"""
+
+ ret += f"""
+/* versions *******************************************************************/
+
+enum {idprefix}version {{
+ {idprefix.upper()}VER_UNINITIALIZED = 0,
+"""
+ verwidth = max(len(v) for v in versions)
+ for ver in sorted(versions):
+ ret += f"\t{c_ver(idprefix, ver)},"
+ ret += (" "*(verwidth-len(ver))) + ' /* "' + ver + '" */\n'
+ ret += f"\t{idprefix.upper()}VER_NUM,\n"
+ ret += "};\n"
+
+ ret += """
+/* non-message structs ********************************************************/
+"""
+ for struct in structs:
+ if struct.msgid is not None:
+ continue
+
+ all_the_same = len(struct.members) == 0 or all(m.ver == struct.members[0].ver for m in struct.members)
+ typewidth = max(len(c_typename(idprefix, m.typ)) for m in struct.members)
+ if not all_the_same:
+ namewidth = max(len(m.name) for m in struct.members)
+
+ ret += "\n"
+ ret += c_typename(idprefix, struct) + " {\n"
+ for member in struct.members:
+ ret += f"\t{c_typename(idprefix, member.typ).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"
+ ret += "};\n"
+
+ ret += """
+/* messages *******************************************************************/
+
+"""
+ ret += f"enum {idprefix}msg_type {{ /* uint8_t */\n"
+ namewidth = max(len(msg.name) for msg in structs if msg.msgid is not None)
+ for msg in structs:
+ if msg.msgid is None:
+ continue
+ ret += f"\t{idprefix.upper()}TYP_{msg.name.ljust(namewidth)} = {msg.msgid},"
+ if (comment := c_vercomment(msg.msgver)):
+ ret += " " + comment
+ ret += "\n"
+ ret += "};\n"
+
+ ret += "\n"
+ ret += f"#define {idprefix.upper()}TYPECODE_FOR_CTYPE(msg) _Generic((msg)"
+ for msg in structs:
+ if msg.msgid is None:
+ continue
+ ret += f", \\\n\t\t{c_typename(idprefix, msg)}: {idprefix.upper()}TYP_{msg.name}"
+ ret += ")\n"
+
+ for msg in structs:
+ if msg.msgid is None:
+ continue
+
+ ret += "\n"
+ if comment := c_vercomment(msg.msgver):
+ ret += comment + "\n"
+ ret += c_typename(idprefix, msg) + " {"
+ if not msg.members:
+ ret += "};\n"
+ continue
+ ret += "\n"
+
+ all_the_same = len(msg.members) == 0 or all(m.ver == msg.members[0].ver for m in msg.members)
+ typewidth = max(len(c_typename(idprefix, m.typ)) for m in msg.members)
+ if not all_the_same:
+ namewidth = max(len(m.name) for m in msg.members)
+
+ for member in msg.members:
+ ret += f"\t{c_typename(idprefix, member.typ).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"
+ ret += "};\n"
+
+
+ ret += "\n"
+ ret += f"#endif /* {guard} */\n"
+ return ret
+
+
+def gen_c(idprefix: str, versions: set[str], structs: list[Struct]) -> str:
+ ret = f"""/* Generated by `{' '.join(sys.argv)}`. DO NOT EDIT! */
+
+#include <stdbool.h>
+
+#include <lib9p/9p.h>
+
+#include "internal.h"
+"""
+
+ def used(arg: str) -> str:
+ return arg
+
+ def unused(arg: str) -> str:
+ return f"UNUSED({arg})"
+
+ # checksize_* ##############################################################
+ ret += """
+/* checksize_* (internals of unmarshal_size()) ********************************/
+
+static inline bool _checksize_net(struct _checksize_ctx *ctx, uint32_t n) {
+ if (__builtin_add_overflow(ctx->net_offset, n, &ctx->net_offset))
+ /* If needed-net-size overflowed uint32_t, then
+ * there's no way that actual-net-size will live up to
+ * that. */
+ return lib9p_error(ctx->ctx, LINUX_EBADMSG, "message is too short for content");
+ if (ctx->net_offset > ctx->net_size)
+ return lib9p_error(ctx->ctx, LINUX_EBADMSG, "message is too short for content");
+ return false;
+}
+
+static inline bool _checksize_host(struct _checksize_ctx *ctx, size_t n) {
+ if (__builtin_add_overflow(ctx->host_extra, n, &ctx->host_extra))
+ /* If needed-host-size overflowed size_t, then there's
+ * no way that actual-net-size will live up to
+ * that. */
+ return lib9p_error(ctx->ctx, LINUX_EBADMSG, "message is too short for content");
+ return false;
+}
+
+static inline bool _checksize_list(struct _checksize_ctx *ctx,
+ size_t cnt, _checksize_fn_t item_fn, size_t item_host_size) {
+ for (size_t i = 0; i < cnt; i++)
+ if (_checksize_host(ctx, item_host_size) || item_fn(ctx))
+ return true;
+ return false;
+}
+
+#define checksize_1(ctx) _checksize_net(ctx, 1)
+#define checksize_2(ctx) _checksize_net(ctx, 2)
+#define checksize_4(ctx) _checksize_net(ctx, 4)
+#define checksize_8(ctx) _checksize_net(ctx, 8)
+"""
+ for struct in structs:
+ inline = ' inline' if struct.msgid is None else ''
+ argfn = used if struct.members else unused
+ ret += "\n"
+ ret += f"static{inline} bool checksize_{struct.name}(struct _checksize_ctx *{argfn('ctx')}) {{"
+ if len(struct.members) == 0:
+ ret += "\n\treturn false;\n"
+ ret += "}\n"
+ continue
+
+ prefix0 = "\treturn "
+ prefix1 = "\t || "
+
+ match struct.name:
+ case "d":
+ # Optimize... maybe the compiler could figure out to do
+ # this, but let's make it obvious.
+ ret += "\n"
+ ret += "\tuint32_t base_offset = ctx->net_offset;\n"
+ ret += "\tif (_checksize_4(ctx))\n"
+ ret += "\t\treturn true;\n"
+ ret += "\tuint32_t len = decode_u32le(&ctx->net_bytes[base_offset]);\n"
+ ret += "\treturn checksize_net(ctx, len) || checksize_host(ctx, len);\n"
+ ret += "}\n"
+ case "s":
+ # Add an extra nul-byte on the host, and validate
+ # UTF-8 (also, similar optimization to "d").
+ ret += "\n"
+ ret += "\tuint32_t base_offset = ctx->net_offset;\n"
+ ret += "\tif (_checksize_2(ctx))\n"
+ ret += "\t\treturn true;\n"
+ ret += "\tuint16_t len = decode_u16le(&ctx->net_bytes[base_offset]);\n"
+ ret += "\tif (checksize_net(ctx, len) || checksize_host(ctx, ((size_t)len)+1))\n"
+ ret += "\t\treturn true;\n"
+ ret += "\tif (!is_valid_utf8_without_nul(&ctx->net_bytes[base_offset+2], len))\n"
+ ret += '\t\treturn lib9p_error(ctx->ctx, LINUX_EBADMSG, "message contains invalid UTF-8");\n'
+ ret += "\treturn false;\n"
+ ret += "}\n"
+ case _:
+ struct_versions = struct.members[0].ver
+
+ prefix = prefix0
+ prev_size: int | None = None
+ for member in struct.members:
+ ret += f"\n{prefix}"
+ if member.ver != struct_versions:
+ ret += "( ( " + (" || ".join(f"(ctx->ctx->version=={c_ver(idprefix, v)})" for v in sorted(member.ver))) + " ) && "
+ if member.cnt is not None:
+ assert prev_size
+ ret += f"_checksize_list(ctx, decode_u{prev_size*8}le(&ctx->net_bytes[ctx->net_offset-{prev_size}]), checksize_{member.typ.name}, sizeof({c_typename(idprefix, member.typ)}))"
+ else:
+ ret += f"checksize_{member.typ.name}(ctx)"
+ if member.ver != struct_versions:
+ ret += " )"
+ prefix = prefix1
+ prev_size = member.static_size
+ ret += ";\n}\n"
+
+ # unmarshal_* ##############################################################
+ ret += """
+/* unmarshal_* ****************************************************************/
+
+static inline vold unmarshal_1(struct _unmarshal_ctx *ctx, uint8_t *out) {
+ *out = decode_u8le(&ctx->net_bytes[ctx->net_offset]);
+ ctx->net_offset += 1;
+}
+
+static inline vold unmarshal_2(struct _unmarshal_ctx *ctx, uint16_t *out) {
+ *out = decode_u16le(&ctx->net_bytes[ctx->net_offset]);
+ ctx->net_offset += 2;
+}
+
+static inline vold unmarshal_4(struct _unmarshal_ctx *ctx, uint32_t *out) {
+ *out = decode_u32le(&ctx->net_bytes[ctx->net_offset]);
+ ctx->net_offset += 4;
+}
+
+static inline vold unmarshal_8(struct _unmarshal_ctx *ctx, uint64_t *out) {
+ *out = decode_u64le(&ctx->net_bytes[ctx->net_offset]);
+ ctx->net_offset += 8;
+}
+"""
+ for struct in structs:
+ inline = ' inline' if struct.msgid is None else ''
+ argfn = used if struct.members else unused
+ ret += "\n"
+ ret += f"static{inline} void unmarshal_{struct.name}(struct _unmarshal_ctx *{argfn('ctx')}, {c_typename(idprefix, struct)} *{argfn('out')}) {{\n"
+ ret += "\tmemset(out, 0, sizeof(*out));\n"
+
+ if struct.members:
+ struct_versions = struct.members[0].ver
+ for member in struct.members:
+ ret += "\t"
+ prefix = "\t"
+ if member.ver != struct_versions:
+ ret += "if ( " + (" || ".join(f"(ctx->ctx->version=={c_ver(idprefix, v)})" for v in sorted(member.ver))) + " ) "
+ prefix = "\t\t"
+ if member.cnt:
+ if member.ver != struct_versions:
+ ret += "{\n"
+ ret += f"{prefix}out->{member.name} = ctx->host_extra;\n"
+ ret += f"{prefix}ctx->host_extra += sizeof(out->{member.name}[0]) * out->{member.cnt};\n"
+ ret += f"{prefix}for (typeof(out->{member.cnt}) i = 0; i < out->{member.cnt}; i++)\n"
+ ret += f"{prefix}\tunmarshal_{member.typ.name}(ctx, &out->{member.name}[i]);\n"
+ if member.ver != struct_versions:
+ ret += "\t}\n"
+ else:
+ ret += f"unmarshal_{member.typ.name}(ctx, &out->{member.name});\n"
+ ret += "}\n"
+
+ # marshal_* ################################################################
+ ret += """
+/* marshal_* ******************************************************************/
+
+static inline bool _marshal_too_large(struct _marshal_ctx *ctx) {
+ lib9p_errorf(ctx->ctx, "%s too large to marshal into %s limit (limit=%"PRIu32")",
+ (ctx->net_bytes[4] % 2 == 0) ? "T-message" : "R-message",
+ ctx->ctx->version ? "negotiated" : ((ctx->net_bytes[4] % 2 == 0) ? "client" : "server"),
+ ctx->ctx->max_msg_size));
+ return true;
+}
+
+static inline bool marshal_1(struct _marshal_ctx *ctx, uint8_t *val) {
+ if (ctx->net_offset + 1 > ctx->max_msg_size)
+ return _marshal_too_large(ctx);
+ out_net_bytes[ctx->net_offset] = *val;
+ ctx->net_offset += 1;
+ return false;
+}
+
+static inline bool marshal_2(struct _marshal_ctx *ctx, uint16_t *val) {
+ if (ctx->net_offset + 2 > ctx->max_msg_size)
+ return _marshal_too_large(ctx);
+ encode_u16le(*val, &out_net_bytes[ctx->net_offset]);
+ ctx->net_offset += 2;
+ return false;
+}
+
+static inline bool marshal_4(struct _marshal_ctx *ctx, uint32_t *val) {
+ if (ctx->net_offset + 4 > ctx->max_msg_size)
+ return true;
+ encode_u32le(*val, &out_net_bytes[ctx->net_offset]);
+ ctx->net_offset += 4;
+ return false;
+}
+
+static inline bool marshal_8(struct _marshal_ctx *ctx, uint64_t *val) {
+ if (ctx->net_offset + 8 > ctx->max_msg_size)
+ return true;
+ encode_u64le(*val, &out_net_bytes[ctx->net_offset]);
+ ctx->net_offset += 8;
+ return false;
+}
+"""
+ for struct in structs:
+ inline = ' inline' if struct.msgid is None else ''
+ argfn = used if struct.members else unused
+ ret += "\n"
+ ret += f"static{inline} bool marshal_{struct.name}(struct _marshal_ctx *{argfn('ctx')}, {c_typename(idprefix, struct)} *{argfn('val')}) {{"
+ if len(struct.members) == 0:
+ ret += "\n\treturn false;\n"
+ ret += "}\n"
+ continue
+
+ prefix0 = "\treturn "
+ prefix1 = "\t || "
+ prefix2 = "\t "
+
+ prefix = prefix0
+ for member in struct.members:
+ if member.cnt:
+ ret += f"\n{prefix }({{"
+ ret += f"\n{prefix2}\tbool err = false;"
+ ret += f"\n{prefix2}\tfor (typeof(val->{member.cnt}) i = 0; i < val->{member.cnt} && !err; i++)"
+ ret += f"\n{prefix2}\t\terr = marshal_{member.typ.name}(ctx, &val->{member.name}[i]));"
+ ret += f"\n{prefix2}\terr;"
+ ret += f"\n{prefix2}}})"
+ else:
+ ret += f"\n{prefix}marshal_{member.typ.name}(ctx, &val->{member.name})"
+ prefix = prefix1
+ ret += ";\n}\n"
+
+ # vtables ##################################################################
+ def msg_entry(msg: Struct) -> str:
+ ret = ""
+ ret += f"\t\t[{idprefix.upper()}TYP_{msg.name}] = {{\n"
+ ret += f"\t\t\tr.unmarshal_basesize = sizeof({c_typename(idprefix, msg)}),\n"
+ ret += f"\t\t\tr.unmarshal_extrasize = checksize_{msg.name},\n"
+ ret += f"\t\t\tr.unmarshal = unmarshal_{msg.name},\n"
+ ret += f"\t\t\tr.marshal = (_marshal_fn_t)marshal_{msg.name},\n"
+ ret += "\t\t}"
+ return ret
+ ret += f"""
+/* vtables ********************************************************************/
+
+struct _vtable_version _{idprefix}vtables[LIB9P_VER_NUM] = {{
+ [{idprefix.upper()}VER_UNINITIALIZED] = {{
+{msg_entry(next(msg for msg in structs if msg.name == 'Tversion'))},
+{msg_entry(next(msg for msg in structs if msg.name == 'Rversion'))},
+ }},
+"""
+ for ver in sorted(versions):
+ ret += f"\t[{c_ver(idprefix, ver)}] = {{\n"
+ for msg in structs:
+ if ver not in msg.msgver:
+ continue
+ ret += msg_entry(msg) + ",\n"
+ ret += "\t},\n"
+ ret += "};\n"
+
+ ############################################################################
+ return ret
+
+
+################################################################################
+
+class Parser:
+ cache: dict[str, tuple[str, list[Struct]]] = {}
+
+ def parse_file(self, filename: str) -> tuple[str, list[Struct]]:
+ if filename not in self.cache:
+ self.cache[filename] = parse_file(filename, self.parse_file)
+ return self.cache[filename]
+
+ def all(self) -> tuple[set[str], list[Struct]]:
+ ret_versions: set[str] = set()
+ ret_structs: dict[str, Struct] = {}
+ for (version, structs) in self.cache.values():
+ if version in ret_versions:
+ raise ValueError(f"duplicate protocol version {repr(version)}")
+ ret_versions.add(version)
+ for struct in structs:
+ if struct.name in ret_structs:
+ if struct != ret_structs[struct.name]:
+ raise ValueError(f"duplicate struct name {repr(struct.name)}")
+ else:
+ ret_structs[struct.name] = struct
+ return ret_versions, list(ret_structs.values())
+
+if __name__ == "__main__":
+ import sys
+
+ if len(sys.argv) < 2:
+ raise ValueError("requires at least 1 .txt filename")
+ parser = Parser()
+ for txtname in sys.argv[1:]:
+ parser.parse_file(txtname)
+ versions, structs = parser.all()
+ with open("include/lib9p/_types.h", "w") as fh:
+ fh.write(gen_h("lib9p_", versions, structs))
+ with open("types.c", "w") as fh:
+ fh.write(gen_c("lib9p_", versions, structs))