diff options
author | Luke T. Shumaker <lukeshu@lukeshu.com> | 2024-09-27 17:25:36 -0600 |
---|---|---|
committer | Luke T. Shumaker <lukeshu@lukeshu.com> | 2024-09-27 17:25:36 -0600 |
commit | e5e15c04bc58c34906e6d7cfcbad68d1a5617563 (patch) | |
tree | 580f5fb0fafc7e974c969fc8aae229205c836195 /lib9p | |
parent | 71e1a86a033c380f85dd300d788af63bfef25bab (diff) |
wip
Diffstat (limited to 'lib9p')
-rw-r--r-- | lib9p/.editorconfig | 2 | ||||
-rw-r--r-- | lib9p/.gitignore | 2 | ||||
-rw-r--r-- | lib9p/9P2000.e.txt | 4 | ||||
-rw-r--r-- | lib9p/9P2000.u.txt | 25 | ||||
-rw-r--r-- | lib9p/9p.c (renamed from lib9p/defs.c) | 39 | ||||
-rw-r--r-- | lib9p/9p.h | 15 | ||||
-rw-r--r-- | lib9p/CMakeLists.txt | 34 | ||||
-rwxr-xr-x | lib9p/defs.gen | 456 | ||||
-rw-r--r-- | lib9p/defs.h | 74 | ||||
-rw-r--r-- | lib9p/include/lib9p/9p.h | 72 | ||||
-rw-r--r-- | lib9p/include/lib9p/_types.h | 285 | ||||
-rwxr-xr-x | lib9p/include/lib9p/linux-errno.h.gen (renamed from lib9p/linux-errno.h.gen) | 0 | ||||
-rw-r--r-- | lib9p/include/lib9p/srv.h | 16 | ||||
-rw-r--r-- | lib9p/internal.h | 79 | ||||
-rw-r--r-- | lib9p/misc.txt | 24 | ||||
-rw-r--r-- | lib9p/srv.h | 16 | ||||
-rw-r--r-- | lib9p/types.c | 1331 | ||||
-rwxr-xr-x | lib9p/types.gen | 611 |
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)) |