From f401359d87f3375efdd39801652cc18574240049 Mon Sep 17 00:00:00 2001 From: "Luke T. Shumaker" Date: Thu, 3 Oct 2024 12:50:36 -0600 Subject: lib9p: Rename the generated files --- lib9p/.editorconfig | 2 +- lib9p/9p.gen | 911 ++++++++++++++++++++++++++ lib9p/9p.generated.c | 1247 ++++++++++++++++++++++++++++++++++++ lib9p/include/lib9p/9p.generated.h | 312 +++++++++ lib9p/include/lib9p/9p.h | 2 +- lib9p/include/lib9p/_types.h | 313 --------- lib9p/types.c | 1247 ------------------------------------ lib9p/types.gen | 913 -------------------------- 8 files changed, 2472 insertions(+), 2475 deletions(-) create mode 100755 lib9p/9p.gen create mode 100644 lib9p/9p.generated.c create mode 100644 lib9p/include/lib9p/9p.generated.h delete mode 100644 lib9p/include/lib9p/_types.h delete mode 100644 lib9p/types.c delete mode 100755 lib9p/types.gen (limited to 'lib9p') diff --git a/lib9p/.editorconfig b/lib9p/.editorconfig index 5c79099..ee1a38f 100644 --- a/lib9p/.editorconfig +++ b/lib9p/.editorconfig @@ -1,3 +1,3 @@ -[{types.gen,linux-errno.h.gen}] +[{9p.gen,linux-errno.h.gen}] indent_style = space indent_size = 4 diff --git a/lib9p/9p.gen b/lib9p/9p.gen new file mode 100755 index 0000000..f974dd1 --- /dev/null +++ b/lib9p/9p.gen @@ -0,0 +1,911 @@ +#!/usr/bin/env python +# lib9p/9p.gen - Generate C marshalers/unmarshalers for .txt files +# defining 9P protocol variants. +# +# Copyright (C) 2024 Luke T. Shumaker +# SPDX-Licence-Identifier: AGPL-3.0-or-later + +import enum +import os.path +import re +from typing import Callable, Sequence + +# This strives to be "general-purpose" in that it just acts on the +# *.txt inputs; but (unfortunately?) there are a few special-cases in +# this script, marked with "SPECIAL". + +# 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 + + +class BitfieldVal: + name: str + val: str + ver: set[str] + + def __init__(self) -> None: + self.ver = set() + + +class Bitfield: + name: str + bits: list[str] + names: dict[str, BitfieldVal] + + @property + def static_size(self) -> int: + return int((len(self.bits) + 7) / 8) + + def bitname_is_valid(self, bitname: str, ver: str | None = None) -> bool: + assert bitname in self.bits + if not bitname: + return False + if bitname.startswith("_"): + return False + if ver and (ver not in self.names[bitname].ver): + return False + return True + + +# `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 | Bitfield | 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{re_membername})\\*\\()?(?P{re_membername})\\[(?P.*)\\]\\)?" +) + + +def parse_members( + ver: str, + env: dict[str, Atom | Bitfield | 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[^"]+)"' +re_import = r"from\s+(?P\S+)\s+import\s+(?P\S+(?:\s*,\s*\S+)*)\s*" +re_structspec = ( + r'(?:(?P[0-9]+)/)?(?P\S+)\s*(?P\+?=)\s*"(?P[^"]*)"' +) +re_structspec_cont = r'\s+"(?P[^"]*)"' +re_bitfieldspec = r"bitfield\s+(?P\S+)\s+(?P[0-9]+)" +re_bitfieldspec_bit = r"(?:\s+|(?P\S+)\s*\+=\s*)(?P[0-9]+)/(?P\S+)" +re_bitfieldspec_alias = ( + r"(?:\s+|(?P\S+)\s*\+=\s*)(?P\S+)\s*=\s*(?P.*)" +) + + +def parse_file( + filename: str, get_include: Callable[[str], tuple[str, list[Bitfield | Struct]]] +) -> tuple[str, list[Bitfield | Struct]]: + version: str | None = None + env: dict[str, Atom | Bitfield | Struct] = { + "1": Atom.u8, + "2": Atom.u16, + "4": Atom.u32, + "8": Atom.u64, + } + with open(filename, "r") as fh: + prev: Struct | Bitfield | None = None + for line in fh: + line = line.split("#", 1)[0].rstrip() + 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_typs = get_include(m.group("file")) + for symname in m.group("syms").split(sep=","): + symname = symname.strip() + for typ in other_typs: + if typ.name == symname or symname == "*": + match typ: + case Bitfield(): + for val in typ.names.values(): + if other_version in val.ver: + val.ver.add(version) + case Struct(): + if typ.msgid: + typ.msgver.add(version) + for member in typ.members: + if other_version in member.ver: + member.ver.add(version) + env[typ.name] = typ + 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(_struct.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 isinstance(prev, Struct): + raise SyntaxError( + "struct-continuation line must come after a struct line" + ) + assert version + prev.members = parse_members( + version, env, prev.members, m.group("members") + ) + elif m := re.fullmatch(re_bitfieldspec, line): + if not version: + raise SyntaxError("must have exactly 1 version line") + bf = Bitfield() + bf.name = m.group("name") + bf.bits = int(m.group("size")) * [""] + bf.names = {} + if len(bf.bits) not in [8, 16, 32, 64]: + raise ValueError(f"Bitfield {repr(bf.name)} has an unusual size") + env[bf.name] = bf + prev = bf + elif m := re.fullmatch(re_bitfieldspec_bit, line): + if m.group("bitfield"): + if m.group("bitfield") not in env: + raise NameError(f"Unknown bitfield {repr(m.group('bitfield'))}") + _bf = env[m.group("bitfield")] + if not isinstance(_bf, Bitfield): + raise NameError(f"Type {repr(_bf.name)} is not a bitfield") + bf = _bf + prev = bf + else: + if not isinstance(prev, Bitfield): + raise SyntaxError( + "bitfield-continuation line must come after a bitfield line" + ) + bf = prev + bit = int(m.group("bit")) + name = m.group("name") + if bit < 0 or bit >= len(bf.bits): + raise ValueError(f"{bf.name}: bit {bit} is out-of-bounds") + if bf.bits[bit]: + raise ValueError(f"{bf.name}: bit {bit} already assigned") + if name in bf.names: + raise ValueError(f"{bf.name}: name {name} already assigned") + + bf.bits[bit] = name + + assert version + val = BitfieldVal() + val.name = name + val.val = f"1<<{bit}" + val.ver.add(version) + bf.names[name] = val + elif m := re.fullmatch(re_bitfieldspec_alias, line): + if m.group("bitfield"): + if m.group("bitfield") not in env: + raise NameError(f"Unknown bitfield {repr(m.group('bitfield'))}") + _bf = env[m.group("bitfield")] + if not isinstance(_bf, Bitfield): + raise NameError(f"Type {repr(_bf.name)} is not a bitfield") + bf = _bf + prev = bf + else: + if not isinstance(prev, Bitfield): + raise SyntaxError( + "bitfield-continuation line must come after a bitfield line" + ) + bf = prev + name = m.group("name") + valstr = m.group("val") + if name in bf.names: + raise ValueError(f"{bf.name}: name {name} already assigned") + + assert version + val = BitfieldVal() + val.name = name + val.val = valstr + val.ver.add(version) + bf.names[name] = val + else: + raise SyntaxError(f"invalid line {repr(line)}") + if not version: + raise SyntaxError("must have exactly 1 version line") + + typs = [x for x in env.values() if not isinstance(x, Atom)] + return version, typs + + +# Generate C ################################################################### + + +def c_typename(idprefix: str, typ: Atom | Bitfield | Struct) -> str: + match typ: + case Atom(): + return f"uint{typ.value*8}_t" + case Bitfield(): + return f"{idprefix}{typ.name}_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_verenum(idprefix: str, ver: str) -> str: + return f"{idprefix.upper()}VER_{ver.replace('.', '_')}" + + +def c_vercomment(versions: set[str]) -> str | None: + if "9P2000" in versions: + return None + return "/* " + (", ".join(sorted(versions))) + " */" + + +def c_vercond(idprefix: str, versions: set[str]) -> str: + if len(versions) == 1: + return f"(ctx->ctx->version=={c_verenum(idprefix, next(v for v in versions))})" + return ( + "( " + (" || ".join(c_vercond(idprefix, {v}) for v in sorted(versions))) + " )" + ) + + +def just_structs_all(typs: list[Bitfield | Struct]) -> Sequence[Struct]: + return list(typ for typ in typs if isinstance(typ, Struct)) + + +def just_structs_nonmsg(typs: list[Bitfield | Struct]) -> Sequence[Struct]: + return list(typ for typ in typs if isinstance(typ, Struct) and typ.msgid is None) + + +def just_structs_msg(typs: list[Bitfield | Struct]) -> Sequence[Struct]: + return list( + typ for typ in typs if isinstance(typ, Struct) and typ.msgid is not None + ) + + +def just_bitfields(typs: list[Bitfield | Struct]) -> Sequence[Bitfield]: + return list(typ for typ in typs if isinstance(typ, Bitfield)) + + +def gen_h(idprefix: str, versions: set[str], typs: list[Bitfield | Struct]) -> str: + ret = f"""/* Generated by `{' '.join(sys.argv)}`. DO NOT EDIT! */ + +#ifndef _LIB9P_9P_H_ +# error Do not include directly; include instead +#endif + +#include /* for uint{{n}}_t types */ +""" + + ret += f""" +/* versions *******************************************************************/ + +enum {idprefix}version {{ +""" + fullversions = ["unknown = 0", *sorted(versions)] + verwidth = max(len(v) for v in fullversions) + for ver in fullversions: + ret += f"\t{c_verenum(idprefix, ver)}," + ret += (" " * (verwidth - len(ver))) + ' /* "' + ver.split()[0] + '" */\n' + ret += f"\t{c_verenum(idprefix, 'NUM')},\n" + ret += "};\n" + ret += "\n" + ret += f"const char *{idprefix}version_str(enum {idprefix}version);\n" + + ret += """ +/* non-message types **********************************************************/ +""" + for bf in just_bitfields(typs): + ret += "\n" + ret += f"typedef uint{bf.static_size*8}_t {c_typename(idprefix, bf)};\n" + names = [ + *reversed([bf.bits[n] or f"_UNUSED_{n}" for n in range(0, len(bf.bits))]), + *[k for k in bf.names if k not in bf.bits], + ] + namewidth = max(len(name) for name in names) + + for name in names: + if name.startswith("_"): + cname = f"_{idprefix.upper()}{bf.name.upper()}_{name[1:]}" + else: + cname = f"{idprefix.upper()}{bf.name.upper()}_{name}" + if name in bf.names: + val = bf.names[name].val + else: + assert name.startswith("_UNUSED_") + val = f"1<<{name[len('_UNUSED_'):]}" + ret += f"#define {cname}{' '*(namewidth-len(name))} (({c_typename(idprefix, bf)})({val}))" + if (name in bf.names) and (comment := c_vercomment(bf.names[name].ver)): + ret += " " + comment + ret += "\n" + + for struct in just_structs_nonmsg(typs): + 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 just_structs_msg(typs)) + for msg in just_structs_msg(typs): + 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"const char *{idprefix}msg_type_str(enum {idprefix}msg_type);\n" + + for msg in just_structs_msg(typs): + 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" + + return ret + + +def gen_c(idprefix: str, versions: set[str], typs: list[Bitfield | Struct]) -> str: + ret = f"""/* Generated by `{' '.join(sys.argv)}`. DO NOT EDIT! */ + +#include +#include +#include /* for size_t */ +#include /* for PRI* macros */ +#include /* for memset() */ + +#include + +#include "internal.h" +""" + + def used(arg: str) -> str: + return arg + + def unused(arg: str) -> str: + return f"UNUSED({arg})" + + # strings ################################################################## + ret += f""" +/* strings ********************************************************************/ + +static const char *version_strs[{c_verenum(idprefix, 'NUM')}] = {{ +""" + for ver in ["unknown", *sorted(versions)]: + ret += f'\t[{c_verenum(idprefix, ver)}] = "{ver}",\n' + ret += "};\n" + ret += f""" +const char *{idprefix}version_str(enum {idprefix}version ver) {{ + assert(0 <= ver && ver < {c_verenum(idprefix, 'NUM')}); + return version_strs[ver]; +}} + +static const char *msg_type_strs[0x100] = {{ +""" + id2name: dict[int, str] = {} + for msg in just_structs_msg(typs): + assert msg.msgid + id2name[msg.msgid] = msg.name + for n in range(0, 0x100): + ret += '\t[0x{:02X}] = "{}",\n'.format(n, id2name.get(n, "0x{:02X}".format(n))) + ret += "};\n" + ret += f""" +const char *{idprefix}msg_type_str(enum {idprefix}msg_type typ) {{ + assert(0 <= typ && typ <= 0xFF); + return msg_type_strs[typ]; +}} +""" + + # validate_* ############################################################### + ret += """ +/* validate_* *****************************************************************/ + +static ALWAYS_INLINE bool _validate_size_net(struct _validate_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 ALWAYS_INLINE bool _validate_size_host(struct _validate_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 ALWAYS_INLINE bool _validate_list(struct _validate_ctx *ctx, + size_t cnt, size_t max, + _validate_fn_t item_fn, size_t item_host_size) { + if (max && cnt > max) + return lib9p_errorf(ctx->ctx, LINUX_EBADMSG, "list size is too large (%zu > %zu)", + cnt, max); + for (size_t i = 0; i < cnt; i++) + if (_validate_size_host(ctx, item_host_size) || item_fn(ctx)) + return true; + return false; +} + +#define validate_1(ctx) _validate_size_net(ctx, 1) +#define validate_2(ctx) _validate_size_net(ctx, 2) +#define validate_4(ctx) _validate_size_net(ctx, 4) +#define validate_8(ctx) _validate_size_net(ctx, 8) +""" + for typ in typs: + inline = ( + " FLATTEN" + if (isinstance(typ, Struct) and typ.msgid is not None) + else " ALWAYS_INLINE" + ) + argfn = unused if (isinstance(typ, Struct) and not typ.members) else used + ret += "\n" + ret += f"static{inline} bool validate_{typ.name}(struct _validate_ctx *{argfn('ctx')}) {{" + + if typ.name == "d": # SPECIAL + # 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 (validate_4(ctx))\n" + ret += "\t\treturn true;\n" + ret += "\tuint32_t len = decode_u32le(&ctx->net_bytes[base_offset]);\n" + ret += "\treturn _validate_size_net(ctx, len) || _validate_size_host(ctx, len);\n" + ret += "}\n" + continue + if typ.name == "s": # SPECIAL + # 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 (validate_2(ctx))\n" + ret += "\t\treturn true;\n" + ret += "\tuint16_t len = decode_u16le(&ctx->net_bytes[base_offset]);\n" + ret += "\tif (_validate_size_net(ctx, len) || _validate_size_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" + continue + + match typ: + case Bitfield(): + ret += "\n" + all_the_same = all( + val.ver == [*typ.names.values()][0].ver + for val in typ.names.values() + ) + if ( + all_the_same + and (len(typ.bits) == typ.static_size * 8) + and all(typ.bitname_is_valid(bitname) for bitname in typ.bits) + ): + ret += f"\treturn validate_{typ.static_size}(ctx));\n" + else: + ret += f"\t if (validate_{typ.static_size}(ctx))\n" + ret += "\t\treturn true;\n" + if all_the_same: + ret += ( + f"\tstatic const {c_typename(idprefix, typ)} mask = 0b" + + "".join( + "1" if typ.bitname_is_valid(bitname) else "0" + for bitname in reversed(typ.bits) + ) + + ";\n" + ) + else: + ret += f"\tstatic const {c_typename(idprefix, typ)} masks[{c_verenum(idprefix, 'NUM')}] = {{\n" + verwidth = max(len(ver) for ver in versions) + for ver in sorted(versions): + ret += ( + f"\t\t[{c_verenum(idprefix, ver)}]{' '*(verwidth-len(ver))} = 0b" + + "".join( + "1" if typ.bitname_is_valid(bitname, ver) else "0" + for bitname in reversed(typ.bits) + ) + + ",\n" + ) + ret += "\t};\n" + ret += f"\t{c_typename(idprefix, typ)} mask = masks[ctx->ctx->version];\n" + ret += f"\t{c_typename(idprefix, typ)} val = decode_u{typ.static_size*8}le(&ctx->net_bytes[ctx->net_offset-{typ.static_size}]);\n" + ret += f"\tif (val & ~mask)\n" + ret += f'\t\treturn lib9p_errorf(ctx->ctx, LINUX_EBADMSG, "unknown bits in {typ.name} bitfield: %#0{typ.static_size}"PRIx{typ.static_size*8},\n' + ret += "\t\t val & ~mask);\n" + ret += "\treturn false;\n" + case Struct(): + if len(typ.members) == 0: + ret += "\n\treturn false;\n" + ret += "}\n" + continue + + prefix0 = "\treturn " + prefix1 = "\t || " + + struct_versions = typ.members[0].ver + + prefix = prefix0 + prev_size: int | None = None + for member in typ.members: + ret += f"\n{prefix}" + if member.ver != struct_versions: + ret += "( " + c_vercond(idprefix, member.ver) + " && " + if member.cnt is not None: + assert prev_size + maxelem = 0 + if ( + typ.name in ["Twalk", "Rwalk"] and member.name[:1] == "w" + ): # SPECIAL + maxelem = 16 + ret += f"_validate_list(ctx, decode_u{prev_size*8}le(&ctx->net_bytes[ctx->net_offset-{prev_size}]), {maxelem}, validate_{member.typ.name}, sizeof({c_typename(idprefix, member.typ)}))" + else: + ret += f"validate_{member.typ.name}(ctx)" + if member.ver != struct_versions: + ret += " )" + prefix = prefix1 + prev_size = member.static_size + ret += ";\n" + ret += "}\n" + + # unmarshal_* ############################################################## + ret += """ +/* unmarshal_* ****************************************************************/ + +static ALWAYS_INLINE void unmarshal_1(struct _unmarshal_ctx *ctx, uint8_t *out) { + *out = decode_u8le(&ctx->net_bytes[ctx->net_offset]); + ctx->net_offset += 1; +} + +static ALWAYS_INLINE void unmarshal_2(struct _unmarshal_ctx *ctx, uint16_t *out) { + *out = decode_u16le(&ctx->net_bytes[ctx->net_offset]); + ctx->net_offset += 2; +} + +static ALWAYS_INLINE void unmarshal_4(struct _unmarshal_ctx *ctx, uint32_t *out) { + *out = decode_u32le(&ctx->net_bytes[ctx->net_offset]); + ctx->net_offset += 4; +} + +static ALWAYS_INLINE void unmarshal_8(struct _unmarshal_ctx *ctx, uint64_t *out) { + *out = decode_u64le(&ctx->net_bytes[ctx->net_offset]); + ctx->net_offset += 8; +} +""" + for typ in typs: + inline = ( + " FLATTEN" + if (isinstance(typ, Struct) and typ.msgid is not None) + else " ALWAYS_INLINE" + ) + argfn = unused if (isinstance(typ, Struct) and not typ.members) else used + ret += "\n" + ret += f"static{inline} void unmarshal_{typ.name}(struct _unmarshal_ctx *{argfn('ctx')}, {c_typename(idprefix, typ)} *out) {{\n" + match typ: + case Bitfield(): + ret += f"\tunmarshal_{typ.static_size}(ctx, (uint{typ.static_size*8}_t *)out);\n" + case Struct(): + ret += "\tmemset(out, 0, sizeof(*out));\n" + + if typ.members: + struct_versions = typ.members[0].ver + for member in typ.members: + ret += "\t" + prefix = "\t" + if member.ver != struct_versions: + ret += "if ( " + c_vercond(idprefix, member.ver) + " ) " + prefix = "\t\t" + if member.cnt: + if member.ver != struct_versions: + ret += "{\n" + ret += f"{prefix}out->{member.name} = ctx->extra;\n" + ret += f"{prefix}ctx->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 ALWAYS_INLINE bool _marshal_too_large(struct _marshal_ctx *ctx) { + lib9p_errorf(ctx->ctx, LINUX_ERANGE, "%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 ALWAYS_INLINE bool marshal_1(struct _marshal_ctx *ctx, uint8_t *val) { + if (ctx->net_offset + 1 > ctx->ctx->max_msg_size) + return _marshal_too_large(ctx); + ctx->net_bytes[ctx->net_offset] = *val; + ctx->net_offset += 1; + return false; +} + +static ALWAYS_INLINE bool marshal_2(struct _marshal_ctx *ctx, uint16_t *val) { + if (ctx->net_offset + 2 > ctx->ctx->max_msg_size) + return _marshal_too_large(ctx); + encode_u16le(*val, &ctx->net_bytes[ctx->net_offset]); + ctx->net_offset += 2; + return false; +} + +static ALWAYS_INLINE bool marshal_4(struct _marshal_ctx *ctx, uint32_t *val) { + if (ctx->net_offset + 4 > ctx->ctx->max_msg_size) + return true; + encode_u32le(*val, &ctx->net_bytes[ctx->net_offset]); + ctx->net_offset += 4; + return false; +} + +static ALWAYS_INLINE bool marshal_8(struct _marshal_ctx *ctx, uint64_t *val) { + if (ctx->net_offset + 8 > ctx->ctx->max_msg_size) + return true; + encode_u64le(*val, &ctx->net_bytes[ctx->net_offset]); + ctx->net_offset += 8; + return false; +} +""" + for typ in typs: + inline = ( + " FLATTEN" + if (isinstance(typ, Struct) and typ.msgid is not None) + else " ALWAYS_INLINE" + ) + argfn = unused if (isinstance(typ, Struct) and not typ.members) else used + ret += "\n" + ret += f"static{inline} bool marshal_{typ.name}(struct _marshal_ctx *{argfn('ctx')}, {c_typename(idprefix, typ)} *{argfn('val')}) {{" + match typ: + case Bitfield(): + ret += "\n" + ret += f"\treturn marshal_{typ.static_size}(ctx, (uint{typ.static_size*8}_t *)val);\n" + case Struct(): + if len(typ.members) == 0: + ret += "\n\treturn false;\n" + ret += "}\n" + continue + + prefix0 = "\treturn " + prefix1 = "\t || " + prefix2 = "\t " + + struct_versions = typ.members[0].ver + prefix = prefix0 + for member in typ.members: + ret += f"\n{prefix}" + if member.ver != struct_versions: + ret += "( " + c_vercond(idprefix, member.ver) + " && " + if member.cnt: + ret += "({" + 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"marshal_{member.typ.name}(ctx, &val->{member.name})" + if member.ver != struct_versions: + ret += " )" + prefix = prefix1 + ret += ";\n" + ret += "}\n" + + # vtables ################################################################## + ret += f""" +/* vtables ********************************************************************/ + +#define _MSG(typ) [{idprefix.upper()}TYP_##typ] = {{ \\ + .basesize = sizeof(struct {idprefix}msg_##typ), \\ + .validate = validate_##typ, \\ + .unmarshal = (_unmarshal_fn_t)unmarshal_##typ, \\ + .marshal = (_marshal_fn_t)marshal_##typ, \\ + }} + +struct _vtable_version _{idprefix}vtables[{c_verenum(idprefix, 'NUM')}] = {{ +""" + + ret += f"\t[{c_verenum(idprefix, 'unknown')}] = {{ .msgs = {{\n" + for msg in just_structs_msg(typs): + if msg.name in ["Tversion", "Rversion", "Rerror"]: # SPECIAL + ret += f"\t\t_MSG({msg.name}),\n" + ret += "\t}},\n" + + for ver in sorted(versions): + ret += f"\t[{c_verenum(idprefix, ver)}] = {{ .msgs = {{\n" + for msg in just_structs_msg(typs): + if ver not in msg.msgver: + continue + ret += f"\t\t_MSG({msg.name}),\n" + ret += "\t}},\n" + ret += "};\n" + + ############################################################################ + return ret + + +################################################################################ + + +class Parser: + cache: dict[str, tuple[str, list[Bitfield | Struct]]] = {} + + def parse_file(self, filename: str) -> tuple[str, list[Bitfield | Struct]]: + filename = os.path.normpath(filename) + if filename not in self.cache: + + def get_include(other_filename: str) -> tuple[str, list[Bitfield | Struct]]: + return self.parse_file(os.path.join(filename, "..", other_filename)) + + self.cache[filename] = parse_file(filename, get_include) + return self.cache[filename] + + def all(self) -> tuple[set[str], list[Bitfield | Struct]]: + ret_versions: set[str] = set() + ret_typs: dict[str, Bitfield | Struct] = {} + for version, typs in self.cache.values(): + if version in ret_versions: + raise ValueError(f"duplicate protocol version {repr(version)}") + ret_versions.add(version) + for typ in typs: + if typ.name in ret_typs: + if typ != ret_typs[typ.name]: + raise ValueError(f"duplicate type name {repr(typ.name)}") + else: + ret_typs[typ.name] = typ + return ret_versions, list(ret_typs.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, typs = parser.all() + outdir = os.path.normpath(os.path.join(sys.argv[0], "..")) + with open(os.path.join(outdir, "include/lib9p/9p.generated.h"), "w") as fh: + fh.write(gen_h("lib9p_", versions, typs)) + with open(os.path.join(outdir, "9p.generated.c"), "w") as fh: + fh.write(gen_c("lib9p_", versions, typs)) diff --git a/lib9p/9p.generated.c b/lib9p/9p.generated.c new file mode 100644 index 0000000..387af79 --- /dev/null +++ b/lib9p/9p.generated.c @@ -0,0 +1,1247 @@ +/* Generated by `./lib9p/9p.gen lib9p/9P2000.txt lib9p/9P2000.u.txt lib9p/9P2000.e.txt`. DO NOT EDIT! */ + +#include +#include +#include /* for size_t */ +#include /* for PRI* macros */ +#include /* for memset() */ + +#include + +#include "internal.h" + +/* strings ********************************************************************/ + +static const char *version_strs[LIB9P_VER_NUM] = { + [LIB9P_VER_unknown] = "unknown", + [LIB9P_VER_9P2000] = "9P2000", + [LIB9P_VER_9P2000_e] = "9P2000.e", + [LIB9P_VER_9P2000_u] = "9P2000.u", +}; + +const char *lib9p_version_str(enum lib9p_version ver) { + assert(0 <= ver && ver < LIB9P_VER_NUM); + return version_strs[ver]; +} + +static const char *msg_type_strs[0x100] = { + [0x00] = "0x00", + [0x01] = "0x01", + [0x02] = "0x02", + [0x03] = "0x03", + [0x04] = "0x04", + [0x05] = "0x05", + [0x06] = "0x06", + [0x07] = "0x07", + [0x08] = "0x08", + [0x09] = "0x09", + [0x0A] = "0x0A", + [0x0B] = "0x0B", + [0x0C] = "0x0C", + [0x0D] = "0x0D", + [0x0E] = "0x0E", + [0x0F] = "0x0F", + [0x10] = "0x10", + [0x11] = "0x11", + [0x12] = "0x12", + [0x13] = "0x13", + [0x14] = "0x14", + [0x15] = "0x15", + [0x16] = "0x16", + [0x17] = "0x17", + [0x18] = "0x18", + [0x19] = "0x19", + [0x1A] = "0x1A", + [0x1B] = "0x1B", + [0x1C] = "0x1C", + [0x1D] = "0x1D", + [0x1E] = "0x1E", + [0x1F] = "0x1F", + [0x20] = "0x20", + [0x21] = "0x21", + [0x22] = "0x22", + [0x23] = "0x23", + [0x24] = "0x24", + [0x25] = "0x25", + [0x26] = "0x26", + [0x27] = "0x27", + [0x28] = "0x28", + [0x29] = "0x29", + [0x2A] = "0x2A", + [0x2B] = "0x2B", + [0x2C] = "0x2C", + [0x2D] = "0x2D", + [0x2E] = "0x2E", + [0x2F] = "0x2F", + [0x30] = "0x30", + [0x31] = "0x31", + [0x32] = "0x32", + [0x33] = "0x33", + [0x34] = "0x34", + [0x35] = "0x35", + [0x36] = "0x36", + [0x37] = "0x37", + [0x38] = "0x38", + [0x39] = "0x39", + [0x3A] = "0x3A", + [0x3B] = "0x3B", + [0x3C] = "0x3C", + [0x3D] = "0x3D", + [0x3E] = "0x3E", + [0x3F] = "0x3F", + [0x40] = "0x40", + [0x41] = "0x41", + [0x42] = "0x42", + [0x43] = "0x43", + [0x44] = "0x44", + [0x45] = "0x45", + [0x46] = "0x46", + [0x47] = "0x47", + [0x48] = "0x48", + [0x49] = "0x49", + [0x4A] = "0x4A", + [0x4B] = "0x4B", + [0x4C] = "0x4C", + [0x4D] = "0x4D", + [0x4E] = "0x4E", + [0x4F] = "0x4F", + [0x50] = "0x50", + [0x51] = "0x51", + [0x52] = "0x52", + [0x53] = "0x53", + [0x54] = "0x54", + [0x55] = "0x55", + [0x56] = "0x56", + [0x57] = "0x57", + [0x58] = "0x58", + [0x59] = "0x59", + [0x5A] = "0x5A", + [0x5B] = "0x5B", + [0x5C] = "0x5C", + [0x5D] = "0x5D", + [0x5E] = "0x5E", + [0x5F] = "0x5F", + [0x60] = "0x60", + [0x61] = "0x61", + [0x62] = "0x62", + [0x63] = "0x63", + [0x64] = "Tversion", + [0x65] = "Rversion", + [0x66] = "Tauth", + [0x67] = "Rauth", + [0x68] = "Tattach", + [0x69] = "Rattach", + [0x6A] = "0x6A", + [0x6B] = "Rerror", + [0x6C] = "Tflush", + [0x6D] = "Rflush", + [0x6E] = "Twalk", + [0x6F] = "Rwalk", + [0x70] = "Topen", + [0x71] = "Ropen", + [0x72] = "Tcreate", + [0x73] = "Rcreate", + [0x74] = "Tread", + [0x75] = "Rread", + [0x76] = "Twrite", + [0x77] = "Rwrite", + [0x78] = "Tclunk", + [0x79] = "Rclunk", + [0x7A] = "Tremove", + [0x7B] = "Rremove", + [0x7C] = "Tstat", + [0x7D] = "Rstat", + [0x7E] = "Twstat", + [0x7F] = "Rwstat", + [0x80] = "0x80", + [0x81] = "0x81", + [0x82] = "0x82", + [0x83] = "0x83", + [0x84] = "0x84", + [0x85] = "0x85", + [0x86] = "0x86", + [0x87] = "0x87", + [0x88] = "0x88", + [0x89] = "0x89", + [0x8A] = "0x8A", + [0x8B] = "0x8B", + [0x8C] = "0x8C", + [0x8D] = "0x8D", + [0x8E] = "0x8E", + [0x8F] = "0x8F", + [0x90] = "0x90", + [0x91] = "0x91", + [0x92] = "0x92", + [0x93] = "0x93", + [0x94] = "0x94", + [0x95] = "0x95", + [0x96] = "Tsession", + [0x97] = "Rsession", + [0x98] = "Tsread", + [0x99] = "Rsread", + [0x9A] = "Tswrite", + [0x9B] = "Rswrite", + [0x9C] = "0x9C", + [0x9D] = "0x9D", + [0x9E] = "0x9E", + [0x9F] = "0x9F", + [0xA0] = "0xA0", + [0xA1] = "0xA1", + [0xA2] = "0xA2", + [0xA3] = "0xA3", + [0xA4] = "0xA4", + [0xA5] = "0xA5", + [0xA6] = "0xA6", + [0xA7] = "0xA7", + [0xA8] = "0xA8", + [0xA9] = "0xA9", + [0xAA] = "0xAA", + [0xAB] = "0xAB", + [0xAC] = "0xAC", + [0xAD] = "0xAD", + [0xAE] = "0xAE", + [0xAF] = "0xAF", + [0xB0] = "0xB0", + [0xB1] = "0xB1", + [0xB2] = "0xB2", + [0xB3] = "0xB3", + [0xB4] = "0xB4", + [0xB5] = "0xB5", + [0xB6] = "0xB6", + [0xB7] = "0xB7", + [0xB8] = "0xB8", + [0xB9] = "0xB9", + [0xBA] = "0xBA", + [0xBB] = "0xBB", + [0xBC] = "0xBC", + [0xBD] = "0xBD", + [0xBE] = "0xBE", + [0xBF] = "0xBF", + [0xC0] = "0xC0", + [0xC1] = "0xC1", + [0xC2] = "0xC2", + [0xC3] = "0xC3", + [0xC4] = "0xC4", + [0xC5] = "0xC5", + [0xC6] = "0xC6", + [0xC7] = "0xC7", + [0xC8] = "0xC8", + [0xC9] = "0xC9", + [0xCA] = "0xCA", + [0xCB] = "0xCB", + [0xCC] = "0xCC", + [0xCD] = "0xCD", + [0xCE] = "0xCE", + [0xCF] = "0xCF", + [0xD0] = "0xD0", + [0xD1] = "0xD1", + [0xD2] = "0xD2", + [0xD3] = "0xD3", + [0xD4] = "0xD4", + [0xD5] = "0xD5", + [0xD6] = "0xD6", + [0xD7] = "0xD7", + [0xD8] = "0xD8", + [0xD9] = "0xD9", + [0xDA] = "0xDA", + [0xDB] = "0xDB", + [0xDC] = "0xDC", + [0xDD] = "0xDD", + [0xDE] = "0xDE", + [0xDF] = "0xDF", + [0xE0] = "0xE0", + [0xE1] = "0xE1", + [0xE2] = "0xE2", + [0xE3] = "0xE3", + [0xE4] = "0xE4", + [0xE5] = "0xE5", + [0xE6] = "0xE6", + [0xE7] = "0xE7", + [0xE8] = "0xE8", + [0xE9] = "0xE9", + [0xEA] = "0xEA", + [0xEB] = "0xEB", + [0xEC] = "0xEC", + [0xED] = "0xED", + [0xEE] = "0xEE", + [0xEF] = "0xEF", + [0xF0] = "0xF0", + [0xF1] = "0xF1", + [0xF2] = "0xF2", + [0xF3] = "0xF3", + [0xF4] = "0xF4", + [0xF5] = "0xF5", + [0xF6] = "0xF6", + [0xF7] = "0xF7", + [0xF8] = "0xF8", + [0xF9] = "0xF9", + [0xFA] = "0xFA", + [0xFB] = "0xFB", + [0xFC] = "0xFC", + [0xFD] = "0xFD", + [0xFE] = "0xFE", + [0xFF] = "0xFF", +}; + +const char *lib9p_msg_type_str(enum lib9p_msg_type typ) { + assert(0 <= typ && typ <= 0xFF); + return msg_type_strs[typ]; +} + +/* validate_* *****************************************************************/ + +static ALWAYS_INLINE bool _validate_size_net(struct _validate_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 ALWAYS_INLINE bool _validate_size_host(struct _validate_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 ALWAYS_INLINE bool _validate_list(struct _validate_ctx *ctx, + size_t cnt, size_t max, + _validate_fn_t item_fn, size_t item_host_size) { + if (max && cnt > max) + return lib9p_errorf(ctx->ctx, LINUX_EBADMSG, "list size is too large (%zu > %zu)", + cnt, max); + for (size_t i = 0; i < cnt; i++) + if (_validate_size_host(ctx, item_host_size) || item_fn(ctx)) + return true; + return false; +} + +#define validate_1(ctx) _validate_size_net(ctx, 1) +#define validate_2(ctx) _validate_size_net(ctx, 2) +#define validate_4(ctx) _validate_size_net(ctx, 4) +#define validate_8(ctx) _validate_size_net(ctx, 8) + +static ALWAYS_INLINE bool validate_d(struct _validate_ctx *ctx) { + uint32_t base_offset = ctx->net_offset; + if (validate_4(ctx)) + return true; + uint32_t len = decode_u32le(&ctx->net_bytes[base_offset]); + return _validate_size_net(ctx, len) || _validate_size_host(ctx, len); +} + +static ALWAYS_INLINE bool validate_s(struct _validate_ctx *ctx) { + uint32_t base_offset = ctx->net_offset; + if (validate_2(ctx)) + return true; + uint16_t len = decode_u16le(&ctx->net_bytes[base_offset]); + if (_validate_size_net(ctx, len) || _validate_size_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 ALWAYS_INLINE bool validate_dm(struct _validate_ctx *ctx) { + if (validate_4(ctx)) + return true; + static const lib9p_dm_t masks[LIB9P_VER_NUM] = { + [LIB9P_VER_9P2000] = 0b11101100000000000000000111111111, + [LIB9P_VER_9P2000_e] = 0b11101100000000000000000111111111, + [LIB9P_VER_9P2000_u] = 0b11101100101111000000000111111111, + }; + lib9p_dm_t mask = masks[ctx->ctx->version]; + lib9p_dm_t val = decode_u32le(&ctx->net_bytes[ctx->net_offset-4]); + if (val & ~mask) + return lib9p_errorf(ctx->ctx, LINUX_EBADMSG, "unknown bits in dm bitfield: %#04"PRIx32, + val & ~mask); + return false; +} + +static ALWAYS_INLINE bool validate_qt(struct _validate_ctx *ctx) { + if (validate_1(ctx)) + return true; + static const lib9p_qt_t masks[LIB9P_VER_NUM] = { + [LIB9P_VER_9P2000] = 0b11101100, + [LIB9P_VER_9P2000_e] = 0b11101100, + [LIB9P_VER_9P2000_u] = 0b11101110, + }; + lib9p_qt_t mask = masks[ctx->ctx->version]; + lib9p_qt_t val = decode_u8le(&ctx->net_bytes[ctx->net_offset-1]); + if (val & ~mask) + return lib9p_errorf(ctx->ctx, LINUX_EBADMSG, "unknown bits in qt bitfield: %#01"PRIx8, + val & ~mask); + return false; +} + +static ALWAYS_INLINE bool validate_qid(struct _validate_ctx *ctx) { + return validate_qt(ctx) + || validate_4(ctx) + || validate_8(ctx); +} + +static ALWAYS_INLINE bool validate_stat(struct _validate_ctx *ctx) { + return validate_2(ctx) + || validate_2(ctx) + || validate_4(ctx) + || validate_qid(ctx) + || validate_dm(ctx) + || validate_4(ctx) + || validate_4(ctx) + || validate_8(ctx) + || validate_s(ctx) + || validate_s(ctx) + || validate_s(ctx) + || validate_s(ctx) + || ( (ctx->ctx->version==LIB9P_VER_9P2000_u) && validate_s(ctx) ) + || ( (ctx->ctx->version==LIB9P_VER_9P2000_u) && validate_4(ctx) ) + || ( (ctx->ctx->version==LIB9P_VER_9P2000_u) && validate_4(ctx) ) + || ( (ctx->ctx->version==LIB9P_VER_9P2000_u) && validate_4(ctx) ); +} + +static ALWAYS_INLINE bool validate_o(struct _validate_ctx *ctx) { + if (validate_1(ctx)) + return true; + static const lib9p_o_t mask = 0b01010011; + lib9p_o_t val = decode_u8le(&ctx->net_bytes[ctx->net_offset-1]); + if (val & ~mask) + return lib9p_errorf(ctx->ctx, LINUX_EBADMSG, "unknown bits in o bitfield: %#01"PRIx8, + val & ~mask); + return false; +} + +static FLATTEN bool validate_Tversion(struct _validate_ctx *ctx) { + return validate_4(ctx) + || validate_s(ctx); +} + +static FLATTEN bool validate_Rversion(struct _validate_ctx *ctx) { + return validate_4(ctx) + || validate_s(ctx); +} + +static FLATTEN bool validate_Tauth(struct _validate_ctx *ctx) { + return validate_4(ctx) + || validate_s(ctx) + || validate_s(ctx) + || ( (ctx->ctx->version==LIB9P_VER_9P2000_u) && validate_4(ctx) ); +} + +static FLATTEN bool validate_Rauth(struct _validate_ctx *ctx) { + return validate_qid(ctx); +} + +static FLATTEN bool validate_Tattach(struct _validate_ctx *ctx) { + return validate_4(ctx) + || validate_4(ctx) + || validate_s(ctx) + || validate_s(ctx); +} + +static FLATTEN bool validate_Rattach(struct _validate_ctx *ctx) { + return validate_qid(ctx); +} + +static FLATTEN bool validate_Rerror(struct _validate_ctx *ctx) { + return validate_s(ctx) + || ( (ctx->ctx->version==LIB9P_VER_9P2000_u) && validate_4(ctx) ); +} + +static FLATTEN bool validate_Tflush(struct _validate_ctx *ctx) { + return validate_2(ctx); +} + +static FLATTEN bool validate_Rflush(struct _validate_ctx *UNUSED(ctx)) { + return false; +} + +static FLATTEN bool validate_Twalk(struct _validate_ctx *ctx) { + return validate_4(ctx) + || validate_4(ctx) + || validate_2(ctx) + || _validate_list(ctx, decode_u16le(&ctx->net_bytes[ctx->net_offset-2]), 16, validate_s, sizeof(struct lib9p_s)); +} + +static FLATTEN bool validate_Rwalk(struct _validate_ctx *ctx) { + return validate_2(ctx) + || _validate_list(ctx, decode_u16le(&ctx->net_bytes[ctx->net_offset-2]), 16, validate_qid, sizeof(struct lib9p_qid)); +} + +static FLATTEN bool validate_Topen(struct _validate_ctx *ctx) { + return validate_4(ctx) + || validate_o(ctx); +} + +static FLATTEN bool validate_Ropen(struct _validate_ctx *ctx) { + return validate_qid(ctx) + || validate_4(ctx); +} + +static FLATTEN bool validate_Tcreate(struct _validate_ctx *ctx) { + return validate_4(ctx) + || validate_s(ctx) + || validate_dm(ctx) + || validate_o(ctx); +} + +static FLATTEN bool validate_Rcreate(struct _validate_ctx *ctx) { + return validate_qid(ctx) + || validate_4(ctx); +} + +static FLATTEN bool validate_Tread(struct _validate_ctx *ctx) { + return validate_4(ctx) + || validate_8(ctx) + || validate_4(ctx); +} + +static FLATTEN bool validate_Rread(struct _validate_ctx *ctx) { + return validate_d(ctx); +} + +static FLATTEN bool validate_Twrite(struct _validate_ctx *ctx) { + return validate_4(ctx) + || validate_8(ctx) + || validate_d(ctx); +} + +static FLATTEN bool validate_Rwrite(struct _validate_ctx *ctx) { + return validate_4(ctx); +} + +static FLATTEN bool validate_Tclunk(struct _validate_ctx *ctx) { + return validate_4(ctx); +} + +static FLATTEN bool validate_Rclunk(struct _validate_ctx *UNUSED(ctx)) { + return false; +} + +static FLATTEN bool validate_Tremove(struct _validate_ctx *ctx) { + return validate_4(ctx); +} + +static FLATTEN bool validate_Rremove(struct _validate_ctx *UNUSED(ctx)) { + return false; +} + +static FLATTEN bool validate_Tstat(struct _validate_ctx *ctx) { + return validate_4(ctx); +} + +static FLATTEN bool validate_Rstat(struct _validate_ctx *ctx) { + return validate_stat(ctx); +} + +static FLATTEN bool validate_Twstat(struct _validate_ctx *ctx) { + return validate_4(ctx) + || validate_stat(ctx); +} + +static FLATTEN bool validate_Rwstat(struct _validate_ctx *UNUSED(ctx)) { + return false; +} + +static FLATTEN bool validate_Tsession(struct _validate_ctx *ctx) { + return validate_8(ctx); +} + +static FLATTEN bool validate_Rsession(struct _validate_ctx *UNUSED(ctx)) { + return false; +} + +static FLATTEN bool validate_Tsread(struct _validate_ctx *ctx) { + return validate_4(ctx) + || validate_2(ctx) + || _validate_list(ctx, decode_u16le(&ctx->net_bytes[ctx->net_offset-2]), 0, validate_s, sizeof(struct lib9p_s)); +} + +static FLATTEN bool validate_Rsread(struct _validate_ctx *ctx) { + return validate_d(ctx); +} + +static FLATTEN bool validate_Tswrite(struct _validate_ctx *ctx) { + return validate_4(ctx) + || validate_2(ctx) + || _validate_list(ctx, decode_u16le(&ctx->net_bytes[ctx->net_offset-2]), 0, validate_s, sizeof(struct lib9p_s)) + || validate_d(ctx); +} + +static FLATTEN bool validate_Rswrite(struct _validate_ctx *ctx) { + return validate_4(ctx); +} + +/* unmarshal_* ****************************************************************/ + +static ALWAYS_INLINE void unmarshal_1(struct _unmarshal_ctx *ctx, uint8_t *out) { + *out = decode_u8le(&ctx->net_bytes[ctx->net_offset]); + ctx->net_offset += 1; +} + +static ALWAYS_INLINE void unmarshal_2(struct _unmarshal_ctx *ctx, uint16_t *out) { + *out = decode_u16le(&ctx->net_bytes[ctx->net_offset]); + ctx->net_offset += 2; +} + +static ALWAYS_INLINE void unmarshal_4(struct _unmarshal_ctx *ctx, uint32_t *out) { + *out = decode_u32le(&ctx->net_bytes[ctx->net_offset]); + ctx->net_offset += 4; +} + +static ALWAYS_INLINE void unmarshal_8(struct _unmarshal_ctx *ctx, uint64_t *out) { + *out = decode_u64le(&ctx->net_bytes[ctx->net_offset]); + ctx->net_offset += 8; +} + +static ALWAYS_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->extra; + ctx->extra += sizeof(out->dat[0]) * out->len; + for (typeof(out->len) i = 0; i < out->len; i++) + unmarshal_1(ctx, &out->dat[i]); +} + +static ALWAYS_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->extra; + ctx->extra += sizeof(out->utf8[0]) * out->len; + for (typeof(out->len) i = 0; i < out->len; i++) + unmarshal_1(ctx, &out->utf8[i]); +} + +static ALWAYS_INLINE void unmarshal_dm(struct _unmarshal_ctx *ctx, lib9p_dm_t *out) { + unmarshal_4(ctx, (uint32_t *)out); +} + +static ALWAYS_INLINE void unmarshal_qt(struct _unmarshal_ctx *ctx, lib9p_qt_t *out) { + unmarshal_1(ctx, (uint8_t *)out); +} + +static ALWAYS_INLINE void unmarshal_qid(struct _unmarshal_ctx *ctx, struct lib9p_qid *out) { + memset(out, 0, sizeof(*out)); + unmarshal_qt(ctx, &out->type); + unmarshal_4(ctx, &out->vers); + unmarshal_8(ctx, &out->path); +} + +static ALWAYS_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_dm(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 ALWAYS_INLINE void unmarshal_o(struct _unmarshal_ctx *ctx, lib9p_o_t *out) { + unmarshal_1(ctx, (uint8_t *)out); +} + +static FLATTEN 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 FLATTEN 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 FLATTEN 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 FLATTEN void unmarshal_Rauth(struct _unmarshal_ctx *ctx, struct lib9p_msg_Rauth *out) { + memset(out, 0, sizeof(*out)); + unmarshal_qid(ctx, &out->aqid); +} + +static FLATTEN 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 FLATTEN void unmarshal_Rattach(struct _unmarshal_ctx *ctx, struct lib9p_msg_Rattach *out) { + memset(out, 0, sizeof(*out)); + unmarshal_qid(ctx, &out->qid); +} + +static FLATTEN 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 FLATTEN void unmarshal_Tflush(struct _unmarshal_ctx *ctx, struct lib9p_msg_Tflush *out) { + memset(out, 0, sizeof(*out)); + unmarshal_2(ctx, &out->oldtag); +} + +static FLATTEN void unmarshal_Rflush(struct _unmarshal_ctx *UNUSED(ctx), struct lib9p_msg_Rflush *out) { + memset(out, 0, sizeof(*out)); +} + +static FLATTEN 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->extra; + ctx->extra += sizeof(out->wname[0]) * out->nwname; + for (typeof(out->nwname) i = 0; i < out->nwname; i++) + unmarshal_s(ctx, &out->wname[i]); +} + +static FLATTEN 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->extra; + ctx->extra += sizeof(out->wqid[0]) * out->nwqid; + for (typeof(out->nwqid) i = 0; i < out->nwqid; i++) + unmarshal_qid(ctx, &out->wqid[i]); +} + +static FLATTEN void unmarshal_Topen(struct _unmarshal_ctx *ctx, struct lib9p_msg_Topen *out) { + memset(out, 0, sizeof(*out)); + unmarshal_4(ctx, &out->fid); + unmarshal_o(ctx, &out->mode); +} + +static FLATTEN 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 FLATTEN 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_dm(ctx, &out->perm); + unmarshal_o(ctx, &out->mode); +} + +static FLATTEN 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 FLATTEN 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 FLATTEN void unmarshal_Rread(struct _unmarshal_ctx *ctx, struct lib9p_msg_Rread *out) { + memset(out, 0, sizeof(*out)); + unmarshal_d(ctx, &out->data); +} + +static FLATTEN 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 FLATTEN void unmarshal_Rwrite(struct _unmarshal_ctx *ctx, struct lib9p_msg_Rwrite *out) { + memset(out, 0, sizeof(*out)); + unmarshal_4(ctx, &out->count); +} + +static FLATTEN void unmarshal_Tclunk(struct _unmarshal_ctx *ctx, struct lib9p_msg_Tclunk *out) { + memset(out, 0, sizeof(*out)); + unmarshal_4(ctx, &out->fid); +} + +static FLATTEN void unmarshal_Rclunk(struct _unmarshal_ctx *UNUSED(ctx), struct lib9p_msg_Rclunk *out) { + memset(out, 0, sizeof(*out)); +} + +static FLATTEN void unmarshal_Tremove(struct _unmarshal_ctx *ctx, struct lib9p_msg_Tremove *out) { + memset(out, 0, sizeof(*out)); + unmarshal_4(ctx, &out->fid); +} + +static FLATTEN void unmarshal_Rremove(struct _unmarshal_ctx *UNUSED(ctx), struct lib9p_msg_Rremove *out) { + memset(out, 0, sizeof(*out)); +} + +static FLATTEN void unmarshal_Tstat(struct _unmarshal_ctx *ctx, struct lib9p_msg_Tstat *out) { + memset(out, 0, sizeof(*out)); + unmarshal_4(ctx, &out->fid); +} + +static FLATTEN void unmarshal_Rstat(struct _unmarshal_ctx *ctx, struct lib9p_msg_Rstat *out) { + memset(out, 0, sizeof(*out)); + unmarshal_stat(ctx, &out->stat); +} + +static FLATTEN 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 FLATTEN void unmarshal_Rwstat(struct _unmarshal_ctx *UNUSED(ctx), struct lib9p_msg_Rwstat *out) { + memset(out, 0, sizeof(*out)); +} + +static FLATTEN void unmarshal_Tsession(struct _unmarshal_ctx *ctx, struct lib9p_msg_Tsession *out) { + memset(out, 0, sizeof(*out)); + unmarshal_8(ctx, &out->key); +} + +static FLATTEN void unmarshal_Rsession(struct _unmarshal_ctx *UNUSED(ctx), struct lib9p_msg_Rsession *out) { + memset(out, 0, sizeof(*out)); +} + +static FLATTEN 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->extra; + ctx->extra += sizeof(out->wname[0]) * out->nwname; + for (typeof(out->nwname) i = 0; i < out->nwname; i++) + unmarshal_s(ctx, &out->wname[i]); +} + +static FLATTEN void unmarshal_Rsread(struct _unmarshal_ctx *ctx, struct lib9p_msg_Rsread *out) { + memset(out, 0, sizeof(*out)); + unmarshal_d(ctx, &out->data); +} + +static FLATTEN 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->extra; + ctx->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 FLATTEN void unmarshal_Rswrite(struct _unmarshal_ctx *ctx, struct lib9p_msg_Rswrite *out) { + memset(out, 0, sizeof(*out)); + unmarshal_4(ctx, &out->count); +} + +/* marshal_* ******************************************************************/ + +static ALWAYS_INLINE bool _marshal_too_large(struct _marshal_ctx *ctx) { + lib9p_errorf(ctx->ctx, LINUX_ERANGE, "%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 ALWAYS_INLINE bool marshal_1(struct _marshal_ctx *ctx, uint8_t *val) { + if (ctx->net_offset + 1 > ctx->ctx->max_msg_size) + return _marshal_too_large(ctx); + ctx->net_bytes[ctx->net_offset] = *val; + ctx->net_offset += 1; + return false; +} + +static ALWAYS_INLINE bool marshal_2(struct _marshal_ctx *ctx, uint16_t *val) { + if (ctx->net_offset + 2 > ctx->ctx->max_msg_size) + return _marshal_too_large(ctx); + encode_u16le(*val, &ctx->net_bytes[ctx->net_offset]); + ctx->net_offset += 2; + return false; +} + +static ALWAYS_INLINE bool marshal_4(struct _marshal_ctx *ctx, uint32_t *val) { + if (ctx->net_offset + 4 > ctx->ctx->max_msg_size) + return true; + encode_u32le(*val, &ctx->net_bytes[ctx->net_offset]); + ctx->net_offset += 4; + return false; +} + +static ALWAYS_INLINE bool marshal_8(struct _marshal_ctx *ctx, uint64_t *val) { + if (ctx->net_offset + 8 > ctx->ctx->max_msg_size) + return true; + encode_u64le(*val, &ctx->net_bytes[ctx->net_offset]); + ctx->net_offset += 8; + return false; +} + +static ALWAYS_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 ALWAYS_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 ALWAYS_INLINE bool marshal_dm(struct _marshal_ctx *ctx, lib9p_dm_t *val) { + return marshal_4(ctx, (uint32_t *)val); +} + +static ALWAYS_INLINE bool marshal_qt(struct _marshal_ctx *ctx, lib9p_qt_t *val) { + return marshal_1(ctx, (uint8_t *)val); +} + +static ALWAYS_INLINE bool marshal_qid(struct _marshal_ctx *ctx, struct lib9p_qid *val) { + return marshal_qt(ctx, &val->type) + || marshal_4(ctx, &val->vers) + || marshal_8(ctx, &val->path); +} + +static ALWAYS_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_dm(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) + || ( (ctx->ctx->version==LIB9P_VER_9P2000_u) && marshal_s(ctx, &val->file_extension) ) + || ( (ctx->ctx->version==LIB9P_VER_9P2000_u) && marshal_4(ctx, &val->file_owner_n_uid) ) + || ( (ctx->ctx->version==LIB9P_VER_9P2000_u) && marshal_4(ctx, &val->file_owner_n_gid) ) + || ( (ctx->ctx->version==LIB9P_VER_9P2000_u) && marshal_4(ctx, &val->file_last_modified_n_uid) ); +} + +static ALWAYS_INLINE bool marshal_o(struct _marshal_ctx *ctx, lib9p_o_t *val) { + return marshal_1(ctx, (uint8_t *)val); +} + +static FLATTEN 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 FLATTEN 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 FLATTEN 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) + || ( (ctx->ctx->version==LIB9P_VER_9P2000_u) && marshal_4(ctx, &val->n_uname) ); +} + +static FLATTEN bool marshal_Rauth(struct _marshal_ctx *ctx, struct lib9p_msg_Rauth *val) { + return marshal_qid(ctx, &val->aqid); +} + +static FLATTEN 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 FLATTEN bool marshal_Rattach(struct _marshal_ctx *ctx, struct lib9p_msg_Rattach *val) { + return marshal_qid(ctx, &val->qid); +} + +static FLATTEN bool marshal_Rerror(struct _marshal_ctx *ctx, struct lib9p_msg_Rerror *val) { + return marshal_s(ctx, &val->ename) + || ( (ctx->ctx->version==LIB9P_VER_9P2000_u) && marshal_4(ctx, &val->errno) ); +} + +static FLATTEN bool marshal_Tflush(struct _marshal_ctx *ctx, struct lib9p_msg_Tflush *val) { + return marshal_2(ctx, &val->oldtag); +} + +static FLATTEN bool marshal_Rflush(struct _marshal_ctx *UNUSED(ctx), struct lib9p_msg_Rflush *UNUSED(val)) { + return false; +} + +static FLATTEN 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 FLATTEN 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 FLATTEN bool marshal_Topen(struct _marshal_ctx *ctx, struct lib9p_msg_Topen *val) { + return marshal_4(ctx, &val->fid) + || marshal_o(ctx, &val->mode); +} + +static FLATTEN bool marshal_Ropen(struct _marshal_ctx *ctx, struct lib9p_msg_Ropen *val) { + return marshal_qid(ctx, &val->qid) + || marshal_4(ctx, &val->iounit); +} + +static FLATTEN bool marshal_Tcreate(struct _marshal_ctx *ctx, struct lib9p_msg_Tcreate *val) { + return marshal_4(ctx, &val->fid) + || marshal_s(ctx, &val->name) + || marshal_dm(ctx, &val->perm) + || marshal_o(ctx, &val->mode); +} + +static FLATTEN bool marshal_Rcreate(struct _marshal_ctx *ctx, struct lib9p_msg_Rcreate *val) { + return marshal_qid(ctx, &val->qid) + || marshal_4(ctx, &val->iounit); +} + +static FLATTEN 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 FLATTEN bool marshal_Rread(struct _marshal_ctx *ctx, struct lib9p_msg_Rread *val) { + return marshal_d(ctx, &val->data); +} + +static FLATTEN 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 FLATTEN bool marshal_Rwrite(struct _marshal_ctx *ctx, struct lib9p_msg_Rwrite *val) { + return marshal_4(ctx, &val->count); +} + +static FLATTEN bool marshal_Tclunk(struct _marshal_ctx *ctx, struct lib9p_msg_Tclunk *val) { + return marshal_4(ctx, &val->fid); +} + +static FLATTEN bool marshal_Rclunk(struct _marshal_ctx *UNUSED(ctx), struct lib9p_msg_Rclunk *UNUSED(val)) { + return false; +} + +static FLATTEN bool marshal_Tremove(struct _marshal_ctx *ctx, struct lib9p_msg_Tremove *val) { + return marshal_4(ctx, &val->fid); +} + +static FLATTEN bool marshal_Rremove(struct _marshal_ctx *UNUSED(ctx), struct lib9p_msg_Rremove *UNUSED(val)) { + return false; +} + +static FLATTEN bool marshal_Tstat(struct _marshal_ctx *ctx, struct lib9p_msg_Tstat *val) { + return marshal_4(ctx, &val->fid); +} + +static FLATTEN bool marshal_Rstat(struct _marshal_ctx *ctx, struct lib9p_msg_Rstat *val) { + return marshal_stat(ctx, &val->stat); +} + +static FLATTEN bool marshal_Twstat(struct _marshal_ctx *ctx, struct lib9p_msg_Twstat *val) { + return marshal_4(ctx, &val->fid) + || marshal_stat(ctx, &val->stat); +} + +static FLATTEN bool marshal_Rwstat(struct _marshal_ctx *UNUSED(ctx), struct lib9p_msg_Rwstat *UNUSED(val)) { + return false; +} + +static FLATTEN bool marshal_Tsession(struct _marshal_ctx *ctx, struct lib9p_msg_Tsession *val) { + return marshal_8(ctx, &val->key); +} + +static FLATTEN bool marshal_Rsession(struct _marshal_ctx *UNUSED(ctx), struct lib9p_msg_Rsession *UNUSED(val)) { + return false; +} + +static FLATTEN 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 FLATTEN bool marshal_Rsread(struct _marshal_ctx *ctx, struct lib9p_msg_Rsread *val) { + return marshal_d(ctx, &val->data); +} + +static FLATTEN 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 FLATTEN bool marshal_Rswrite(struct _marshal_ctx *ctx, struct lib9p_msg_Rswrite *val) { + return marshal_4(ctx, &val->count); +} + +/* vtables ********************************************************************/ + +#define _MSG(typ) [LIB9P_TYP_##typ] = { \ + .basesize = sizeof(struct lib9p_msg_##typ), \ + .validate = validate_##typ, \ + .unmarshal = (_unmarshal_fn_t)unmarshal_##typ, \ + .marshal = (_marshal_fn_t)marshal_##typ, \ + } + +struct _vtable_version _lib9p_vtables[LIB9P_VER_NUM] = { + [LIB9P_VER_unknown] = { .msgs = { + _MSG(Tversion), + _MSG(Rversion), + _MSG(Rerror), + }}, + [LIB9P_VER_9P2000] = { .msgs = { + _MSG(Tversion), + _MSG(Rversion), + _MSG(Tauth), + _MSG(Rauth), + _MSG(Tattach), + _MSG(Rattach), + _MSG(Rerror), + _MSG(Tflush), + _MSG(Rflush), + _MSG(Twalk), + _MSG(Rwalk), + _MSG(Topen), + _MSG(Ropen), + _MSG(Tcreate), + _MSG(Rcreate), + _MSG(Tread), + _MSG(Rread), + _MSG(Twrite), + _MSG(Rwrite), + _MSG(Tclunk), + _MSG(Rclunk), + _MSG(Tremove), + _MSG(Rremove), + _MSG(Tstat), + _MSG(Rstat), + _MSG(Twstat), + _MSG(Rwstat), + }}, + [LIB9P_VER_9P2000_e] = { .msgs = { + _MSG(Tversion), + _MSG(Rversion), + _MSG(Tauth), + _MSG(Rauth), + _MSG(Tattach), + _MSG(Rattach), + _MSG(Rerror), + _MSG(Tflush), + _MSG(Rflush), + _MSG(Twalk), + _MSG(Rwalk), + _MSG(Topen), + _MSG(Ropen), + _MSG(Tcreate), + _MSG(Rcreate), + _MSG(Tread), + _MSG(Rread), + _MSG(Twrite), + _MSG(Rwrite), + _MSG(Tclunk), + _MSG(Rclunk), + _MSG(Tremove), + _MSG(Rremove), + _MSG(Tstat), + _MSG(Rstat), + _MSG(Twstat), + _MSG(Rwstat), + _MSG(Tsession), + _MSG(Rsession), + _MSG(Tsread), + _MSG(Rsread), + _MSG(Tswrite), + _MSG(Rswrite), + }}, + [LIB9P_VER_9P2000_u] = { .msgs = { + _MSG(Tversion), + _MSG(Rversion), + _MSG(Tauth), + _MSG(Rauth), + _MSG(Tattach), + _MSG(Rattach), + _MSG(Rerror), + _MSG(Tflush), + _MSG(Rflush), + _MSG(Twalk), + _MSG(Rwalk), + _MSG(Topen), + _MSG(Ropen), + _MSG(Tcreate), + _MSG(Rcreate), + _MSG(Tread), + _MSG(Rread), + _MSG(Twrite), + _MSG(Rwrite), + _MSG(Tclunk), + _MSG(Rclunk), + _MSG(Tremove), + _MSG(Rremove), + _MSG(Tstat), + _MSG(Rstat), + _MSG(Twstat), + _MSG(Rwstat), + }}, +}; diff --git a/lib9p/include/lib9p/9p.generated.h b/lib9p/include/lib9p/9p.generated.h new file mode 100644 index 0000000..c6aaee9 --- /dev/null +++ b/lib9p/include/lib9p/9p.generated.h @@ -0,0 +1,312 @@ +/* Generated by `./lib9p/9p.gen lib9p/9P2000.txt lib9p/9P2000.u.txt lib9p/9P2000.e.txt`. DO NOT EDIT! */ + +#ifndef _LIB9P_9P_H_ +# error Do not include directly; include instead +#endif + +#include /* for uint{n}_t types */ + +/* versions *******************************************************************/ + +enum lib9p_version { + LIB9P_VER_unknown = 0, /* "unknown" */ + LIB9P_VER_9P2000, /* "9P2000" */ + LIB9P_VER_9P2000_e, /* "9P2000.e" */ + LIB9P_VER_9P2000_u, /* "9P2000.u" */ + LIB9P_VER_NUM, +}; + +const char *lib9p_version_str(enum lib9p_version); + +/* non-message types **********************************************************/ + +typedef uint32_t lib9p_dm_t; +#define LIB9P_DM_DIR ((lib9p_dm_t)(1<<31)) +#define LIB9P_DM_APPEND ((lib9p_dm_t)(1<<30)) +#define LIB9P_DM_EXCL ((lib9p_dm_t)(1<<29)) +#define _LIB9P_DM_PLAN9_MOUNT ((lib9p_dm_t)(1<<28)) +#define LIB9P_DM_AUTH ((lib9p_dm_t)(1<<27)) +#define LIB9P_DM_TMP ((lib9p_dm_t)(1<<26)) +#define _LIB9P_DM_UNUSED_25 ((lib9p_dm_t)(1<<25)) +#define _LIB9P_DM_UNUSED_24 ((lib9p_dm_t)(1<<24)) +#define LIB9P_DM_DEVICE ((lib9p_dm_t)(1<<23)) /* 9P2000.u */ +#define _LIB9P_DM_UNUSED_22 ((lib9p_dm_t)(1<<22)) +#define LIB9P_DM_NAMEDPIPE ((lib9p_dm_t)(1<<21)) /* 9P2000.u */ +#define LIB9P_DM_SOCKET ((lib9p_dm_t)(1<<20)) /* 9P2000.u */ +#define LIB9P_DM_SETUID ((lib9p_dm_t)(1<<19)) /* 9P2000.u */ +#define LIB9P_DM_SETGID ((lib9p_dm_t)(1<<18)) /* 9P2000.u */ +#define _LIB9P_DM_UNUSED_17 ((lib9p_dm_t)(1<<17)) +#define _LIB9P_DM_UNUSED_16 ((lib9p_dm_t)(1<<16)) +#define _LIB9P_DM_UNUSED_15 ((lib9p_dm_t)(1<<15)) +#define _LIB9P_DM_UNUSED_14 ((lib9p_dm_t)(1<<14)) +#define _LIB9P_DM_UNUSED_13 ((lib9p_dm_t)(1<<13)) +#define _LIB9P_DM_UNUSED_12 ((lib9p_dm_t)(1<<12)) +#define _LIB9P_DM_UNUSED_11 ((lib9p_dm_t)(1<<11)) +#define _LIB9P_DM_UNUSED_10 ((lib9p_dm_t)(1<<10)) +#define _LIB9P_DM_UNUSED_9 ((lib9p_dm_t)(1<<9)) +#define LIB9P_DM_OWNER_R ((lib9p_dm_t)(1<<8)) +#define LIB9P_DM_OWNER_W ((lib9p_dm_t)(1<<7)) +#define LIB9P_DM_OWNER_X ((lib9p_dm_t)(1<<6)) +#define LIB9P_DM_GROUP_R ((lib9p_dm_t)(1<<5)) +#define LIB9P_DM_GROUP_W ((lib9p_dm_t)(1<<4)) +#define LIB9P_DM_GROUP_X ((lib9p_dm_t)(1<<3)) +#define LIB9P_DM_OTHER_R ((lib9p_dm_t)(1<<2)) +#define LIB9P_DM_OTHER_W ((lib9p_dm_t)(1<<1)) +#define LIB9P_DM_OTHER_X ((lib9p_dm_t)(1<<0)) + +typedef uint8_t lib9p_qt_t; +#define LIB9P_QT_DIR ((lib9p_qt_t)(1<<7)) +#define LIB9P_QT_APPEND ((lib9p_qt_t)(1<<6)) +#define LIB9P_QT_EXCL ((lib9p_qt_t)(1<<5)) +#define _LIB9P_QT_PLAN9_MOUNT ((lib9p_qt_t)(1<<4)) +#define LIB9P_QT_AUTH ((lib9p_qt_t)(1<<3)) +#define LIB9P_QT_TMP ((lib9p_qt_t)(1<<2)) +#define LIB9P_QT_SYMLINK ((lib9p_qt_t)(1<<1)) /* 9P2000.u */ +#define _LIB9P_QT_UNUSED_0 ((lib9p_qt_t)(1<<0)) +#define LIB9P_QT_FILE ((lib9p_qt_t)(0)) + +typedef uint8_t lib9p_o_t; +#define _LIB9P_O_UNUSED_7 ((lib9p_o_t)(1<<7)) +#define LIB9P_O_RCLOSE ((lib9p_o_t)(1<<6)) +#define _LIB9P_O_UNUSED_5 ((lib9p_o_t)(1<<5)) +#define LIB9P_O_TRUNC ((lib9p_o_t)(1<<4)) +#define _LIB9P_O_UNUSED_3 ((lib9p_o_t)(1<<3)) +#define _LIB9P_O_UNUSED_2 ((lib9p_o_t)(1<<2)) +#define LIB9P_O_rwx_1 ((lib9p_o_t)(1<<1)) +#define LIB9P_O_rwx_0 ((lib9p_o_t)(1<<0)) +#define LIB9P_O_READ ((lib9p_o_t)(0)) +#define LIB9P_O_WRITE ((lib9p_o_t)(1)) +#define LIB9P_O_RDWR ((lib9p_o_t)(2)) +#define LIB9P_O_EXEC ((lib9p_o_t)(3)) + +struct lib9p_d { + uint32_t len; + uint8_t *dat; +}; + +struct lib9p_s { + uint16_t len; + uint8_t *utf8; +}; + +struct lib9p_qid { + lib9p_qt_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; + lib9p_dm_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 */ +}; + +const char *lib9p_msg_type_str(enum lib9p_msg_type); + +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; + lib9p_o_t mode; +}; + +struct lib9p_msg_Ropen { + struct lib9p_qid qid; + uint32_t iounit; +}; + +struct lib9p_msg_Tcreate { + uint32_t fid; + struct lib9p_s name; + lib9p_dm_t perm; + lib9p_o_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; +}; diff --git a/lib9p/include/lib9p/9p.h b/lib9p/include/lib9p/9p.h index 2712998..8e57a86 100644 --- a/lib9p/include/lib9p/9p.h +++ b/lib9p/include/lib9p/9p.h @@ -10,8 +10,8 @@ #include #include /* for ssize_t */ +#include #include -#include #define LIB9P_NOTAG ((uint16_t)~0U) #define LIB9P_NOFID ((uint32_t)~0U) diff --git a/lib9p/include/lib9p/_types.h b/lib9p/include/lib9p/_types.h deleted file mode 100644 index 576d481..0000000 --- a/lib9p/include/lib9p/_types.h +++ /dev/null @@ -1,313 +0,0 @@ -/* Generated by `./lib9p/types.gen lib9p/9P2000.txt lib9p/9P2000.u.txt lib9p/9P2000.e.txt`. DO NOT EDIT! */ - -#ifndef _LIB9P__TYPES_H_ -#define _LIB9P__TYPES_H_ - -#include /* for uint{n}_t types */ - -/* versions *******************************************************************/ - -enum lib9p_version { - LIB9P_VER_unknown = 0, /* "unknown" */ - LIB9P_VER_9P2000, /* "9P2000" */ - LIB9P_VER_9P2000_e, /* "9P2000.e" */ - LIB9P_VER_9P2000_u, /* "9P2000.u" */ - LIB9P_VER_NUM, -}; - -const char *lib9p_version_str(enum lib9p_version); - -/* non-message types **********************************************************/ - -typedef uint32_t lib9p_dm_t; -#define LIB9P_DM_DIR ((lib9p_dm_t)(1<<31)) -#define LIB9P_DM_APPEND ((lib9p_dm_t)(1<<30)) -#define LIB9P_DM_EXCL ((lib9p_dm_t)(1<<29)) -#define _LIB9P_DM_PLAN9_MOUNT ((lib9p_dm_t)(1<<28)) -#define LIB9P_DM_AUTH ((lib9p_dm_t)(1<<27)) -#define LIB9P_DM_TMP ((lib9p_dm_t)(1<<26)) -#define _LIB9P_DM_UNUSED_25 ((lib9p_dm_t)(1<<25)) -#define _LIB9P_DM_UNUSED_24 ((lib9p_dm_t)(1<<24)) -#define LIB9P_DM_DEVICE ((lib9p_dm_t)(1<<23)) /* 9P2000.u */ -#define _LIB9P_DM_UNUSED_22 ((lib9p_dm_t)(1<<22)) -#define LIB9P_DM_NAMEDPIPE ((lib9p_dm_t)(1<<21)) /* 9P2000.u */ -#define LIB9P_DM_SOCKET ((lib9p_dm_t)(1<<20)) /* 9P2000.u */ -#define LIB9P_DM_SETUID ((lib9p_dm_t)(1<<19)) /* 9P2000.u */ -#define LIB9P_DM_SETGID ((lib9p_dm_t)(1<<18)) /* 9P2000.u */ -#define _LIB9P_DM_UNUSED_17 ((lib9p_dm_t)(1<<17)) -#define _LIB9P_DM_UNUSED_16 ((lib9p_dm_t)(1<<16)) -#define _LIB9P_DM_UNUSED_15 ((lib9p_dm_t)(1<<15)) -#define _LIB9P_DM_UNUSED_14 ((lib9p_dm_t)(1<<14)) -#define _LIB9P_DM_UNUSED_13 ((lib9p_dm_t)(1<<13)) -#define _LIB9P_DM_UNUSED_12 ((lib9p_dm_t)(1<<12)) -#define _LIB9P_DM_UNUSED_11 ((lib9p_dm_t)(1<<11)) -#define _LIB9P_DM_UNUSED_10 ((lib9p_dm_t)(1<<10)) -#define _LIB9P_DM_UNUSED_9 ((lib9p_dm_t)(1<<9)) -#define LIB9P_DM_OWNER_R ((lib9p_dm_t)(1<<8)) -#define LIB9P_DM_OWNER_W ((lib9p_dm_t)(1<<7)) -#define LIB9P_DM_OWNER_X ((lib9p_dm_t)(1<<6)) -#define LIB9P_DM_GROUP_R ((lib9p_dm_t)(1<<5)) -#define LIB9P_DM_GROUP_W ((lib9p_dm_t)(1<<4)) -#define LIB9P_DM_GROUP_X ((lib9p_dm_t)(1<<3)) -#define LIB9P_DM_OTHER_R ((lib9p_dm_t)(1<<2)) -#define LIB9P_DM_OTHER_W ((lib9p_dm_t)(1<<1)) -#define LIB9P_DM_OTHER_X ((lib9p_dm_t)(1<<0)) - -typedef uint8_t lib9p_qt_t; -#define LIB9P_QT_DIR ((lib9p_qt_t)(1<<7)) -#define LIB9P_QT_APPEND ((lib9p_qt_t)(1<<6)) -#define LIB9P_QT_EXCL ((lib9p_qt_t)(1<<5)) -#define _LIB9P_QT_PLAN9_MOUNT ((lib9p_qt_t)(1<<4)) -#define LIB9P_QT_AUTH ((lib9p_qt_t)(1<<3)) -#define LIB9P_QT_TMP ((lib9p_qt_t)(1<<2)) -#define LIB9P_QT_SYMLINK ((lib9p_qt_t)(1<<1)) /* 9P2000.u */ -#define _LIB9P_QT_UNUSED_0 ((lib9p_qt_t)(1<<0)) -#define LIB9P_QT_FILE ((lib9p_qt_t)(0)) - -typedef uint8_t lib9p_o_t; -#define _LIB9P_O_UNUSED_7 ((lib9p_o_t)(1<<7)) -#define LIB9P_O_RCLOSE ((lib9p_o_t)(1<<6)) -#define _LIB9P_O_UNUSED_5 ((lib9p_o_t)(1<<5)) -#define LIB9P_O_TRUNC ((lib9p_o_t)(1<<4)) -#define _LIB9P_O_UNUSED_3 ((lib9p_o_t)(1<<3)) -#define _LIB9P_O_UNUSED_2 ((lib9p_o_t)(1<<2)) -#define LIB9P_O_rwx_1 ((lib9p_o_t)(1<<1)) -#define LIB9P_O_rwx_0 ((lib9p_o_t)(1<<0)) -#define LIB9P_O_READ ((lib9p_o_t)(0)) -#define LIB9P_O_WRITE ((lib9p_o_t)(1)) -#define LIB9P_O_RDWR ((lib9p_o_t)(2)) -#define LIB9P_O_EXEC ((lib9p_o_t)(3)) - -struct lib9p_d { - uint32_t len; - uint8_t *dat; -}; - -struct lib9p_s { - uint16_t len; - uint8_t *utf8; -}; - -struct lib9p_qid { - lib9p_qt_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; - lib9p_dm_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 */ -}; - -const char *lib9p_msg_type_str(enum lib9p_msg_type); - -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; - lib9p_o_t mode; -}; - -struct lib9p_msg_Ropen { - struct lib9p_qid qid; - uint32_t iounit; -}; - -struct lib9p_msg_Tcreate { - uint32_t fid; - struct lib9p_s name; - lib9p_dm_t perm; - lib9p_o_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/types.c b/lib9p/types.c deleted file mode 100644 index 0343a48..0000000 --- a/lib9p/types.c +++ /dev/null @@ -1,1247 +0,0 @@ -/* Generated by `./lib9p/types.gen lib9p/9P2000.txt lib9p/9P2000.u.txt lib9p/9P2000.e.txt`. DO NOT EDIT! */ - -#include -#include -#include /* for size_t */ -#include /* for PRI* macros */ -#include /* for memset() */ - -#include - -#include "internal.h" - -/* strings ********************************************************************/ - -static const char *version_strs[LIB9P_VER_NUM] = { - [LIB9P_VER_unknown] = "unknown", - [LIB9P_VER_9P2000] = "9P2000", - [LIB9P_VER_9P2000_e] = "9P2000.e", - [LIB9P_VER_9P2000_u] = "9P2000.u", -}; - -const char *lib9p_version_str(enum lib9p_version ver) { - assert(0 <= ver && ver < LIB9P_VER_NUM); - return version_strs[ver]; -} - -static const char *msg_type_strs[0x100] = { - [0x00] = "0x00", - [0x01] = "0x01", - [0x02] = "0x02", - [0x03] = "0x03", - [0x04] = "0x04", - [0x05] = "0x05", - [0x06] = "0x06", - [0x07] = "0x07", - [0x08] = "0x08", - [0x09] = "0x09", - [0x0A] = "0x0A", - [0x0B] = "0x0B", - [0x0C] = "0x0C", - [0x0D] = "0x0D", - [0x0E] = "0x0E", - [0x0F] = "0x0F", - [0x10] = "0x10", - [0x11] = "0x11", - [0x12] = "0x12", - [0x13] = "0x13", - [0x14] = "0x14", - [0x15] = "0x15", - [0x16] = "0x16", - [0x17] = "0x17", - [0x18] = "0x18", - [0x19] = "0x19", - [0x1A] = "0x1A", - [0x1B] = "0x1B", - [0x1C] = "0x1C", - [0x1D] = "0x1D", - [0x1E] = "0x1E", - [0x1F] = "0x1F", - [0x20] = "0x20", - [0x21] = "0x21", - [0x22] = "0x22", - [0x23] = "0x23", - [0x24] = "0x24", - [0x25] = "0x25", - [0x26] = "0x26", - [0x27] = "0x27", - [0x28] = "0x28", - [0x29] = "0x29", - [0x2A] = "0x2A", - [0x2B] = "0x2B", - [0x2C] = "0x2C", - [0x2D] = "0x2D", - [0x2E] = "0x2E", - [0x2F] = "0x2F", - [0x30] = "0x30", - [0x31] = "0x31", - [0x32] = "0x32", - [0x33] = "0x33", - [0x34] = "0x34", - [0x35] = "0x35", - [0x36] = "0x36", - [0x37] = "0x37", - [0x38] = "0x38", - [0x39] = "0x39", - [0x3A] = "0x3A", - [0x3B] = "0x3B", - [0x3C] = "0x3C", - [0x3D] = "0x3D", - [0x3E] = "0x3E", - [0x3F] = "0x3F", - [0x40] = "0x40", - [0x41] = "0x41", - [0x42] = "0x42", - [0x43] = "0x43", - [0x44] = "0x44", - [0x45] = "0x45", - [0x46] = "0x46", - [0x47] = "0x47", - [0x48] = "0x48", - [0x49] = "0x49", - [0x4A] = "0x4A", - [0x4B] = "0x4B", - [0x4C] = "0x4C", - [0x4D] = "0x4D", - [0x4E] = "0x4E", - [0x4F] = "0x4F", - [0x50] = "0x50", - [0x51] = "0x51", - [0x52] = "0x52", - [0x53] = "0x53", - [0x54] = "0x54", - [0x55] = "0x55", - [0x56] = "0x56", - [0x57] = "0x57", - [0x58] = "0x58", - [0x59] = "0x59", - [0x5A] = "0x5A", - [0x5B] = "0x5B", - [0x5C] = "0x5C", - [0x5D] = "0x5D", - [0x5E] = "0x5E", - [0x5F] = "0x5F", - [0x60] = "0x60", - [0x61] = "0x61", - [0x62] = "0x62", - [0x63] = "0x63", - [0x64] = "Tversion", - [0x65] = "Rversion", - [0x66] = "Tauth", - [0x67] = "Rauth", - [0x68] = "Tattach", - [0x69] = "Rattach", - [0x6A] = "0x6A", - [0x6B] = "Rerror", - [0x6C] = "Tflush", - [0x6D] = "Rflush", - [0x6E] = "Twalk", - [0x6F] = "Rwalk", - [0x70] = "Topen", - [0x71] = "Ropen", - [0x72] = "Tcreate", - [0x73] = "Rcreate", - [0x74] = "Tread", - [0x75] = "Rread", - [0x76] = "Twrite", - [0x77] = "Rwrite", - [0x78] = "Tclunk", - [0x79] = "Rclunk", - [0x7A] = "Tremove", - [0x7B] = "Rremove", - [0x7C] = "Tstat", - [0x7D] = "Rstat", - [0x7E] = "Twstat", - [0x7F] = "Rwstat", - [0x80] = "0x80", - [0x81] = "0x81", - [0x82] = "0x82", - [0x83] = "0x83", - [0x84] = "0x84", - [0x85] = "0x85", - [0x86] = "0x86", - [0x87] = "0x87", - [0x88] = "0x88", - [0x89] = "0x89", - [0x8A] = "0x8A", - [0x8B] = "0x8B", - [0x8C] = "0x8C", - [0x8D] = "0x8D", - [0x8E] = "0x8E", - [0x8F] = "0x8F", - [0x90] = "0x90", - [0x91] = "0x91", - [0x92] = "0x92", - [0x93] = "0x93", - [0x94] = "0x94", - [0x95] = "0x95", - [0x96] = "Tsession", - [0x97] = "Rsession", - [0x98] = "Tsread", - [0x99] = "Rsread", - [0x9A] = "Tswrite", - [0x9B] = "Rswrite", - [0x9C] = "0x9C", - [0x9D] = "0x9D", - [0x9E] = "0x9E", - [0x9F] = "0x9F", - [0xA0] = "0xA0", - [0xA1] = "0xA1", - [0xA2] = "0xA2", - [0xA3] = "0xA3", - [0xA4] = "0xA4", - [0xA5] = "0xA5", - [0xA6] = "0xA6", - [0xA7] = "0xA7", - [0xA8] = "0xA8", - [0xA9] = "0xA9", - [0xAA] = "0xAA", - [0xAB] = "0xAB", - [0xAC] = "0xAC", - [0xAD] = "0xAD", - [0xAE] = "0xAE", - [0xAF] = "0xAF", - [0xB0] = "0xB0", - [0xB1] = "0xB1", - [0xB2] = "0xB2", - [0xB3] = "0xB3", - [0xB4] = "0xB4", - [0xB5] = "0xB5", - [0xB6] = "0xB6", - [0xB7] = "0xB7", - [0xB8] = "0xB8", - [0xB9] = "0xB9", - [0xBA] = "0xBA", - [0xBB] = "0xBB", - [0xBC] = "0xBC", - [0xBD] = "0xBD", - [0xBE] = "0xBE", - [0xBF] = "0xBF", - [0xC0] = "0xC0", - [0xC1] = "0xC1", - [0xC2] = "0xC2", - [0xC3] = "0xC3", - [0xC4] = "0xC4", - [0xC5] = "0xC5", - [0xC6] = "0xC6", - [0xC7] = "0xC7", - [0xC8] = "0xC8", - [0xC9] = "0xC9", - [0xCA] = "0xCA", - [0xCB] = "0xCB", - [0xCC] = "0xCC", - [0xCD] = "0xCD", - [0xCE] = "0xCE", - [0xCF] = "0xCF", - [0xD0] = "0xD0", - [0xD1] = "0xD1", - [0xD2] = "0xD2", - [0xD3] = "0xD3", - [0xD4] = "0xD4", - [0xD5] = "0xD5", - [0xD6] = "0xD6", - [0xD7] = "0xD7", - [0xD8] = "0xD8", - [0xD9] = "0xD9", - [0xDA] = "0xDA", - [0xDB] = "0xDB", - [0xDC] = "0xDC", - [0xDD] = "0xDD", - [0xDE] = "0xDE", - [0xDF] = "0xDF", - [0xE0] = "0xE0", - [0xE1] = "0xE1", - [0xE2] = "0xE2", - [0xE3] = "0xE3", - [0xE4] = "0xE4", - [0xE5] = "0xE5", - [0xE6] = "0xE6", - [0xE7] = "0xE7", - [0xE8] = "0xE8", - [0xE9] = "0xE9", - [0xEA] = "0xEA", - [0xEB] = "0xEB", - [0xEC] = "0xEC", - [0xED] = "0xED", - [0xEE] = "0xEE", - [0xEF] = "0xEF", - [0xF0] = "0xF0", - [0xF1] = "0xF1", - [0xF2] = "0xF2", - [0xF3] = "0xF3", - [0xF4] = "0xF4", - [0xF5] = "0xF5", - [0xF6] = "0xF6", - [0xF7] = "0xF7", - [0xF8] = "0xF8", - [0xF9] = "0xF9", - [0xFA] = "0xFA", - [0xFB] = "0xFB", - [0xFC] = "0xFC", - [0xFD] = "0xFD", - [0xFE] = "0xFE", - [0xFF] = "0xFF", -}; - -const char *lib9p_msg_type_str(enum lib9p_msg_type typ) { - assert(0 <= typ && typ <= 0xFF); - return msg_type_strs[typ]; -} - -/* validate_* *****************************************************************/ - -static ALWAYS_INLINE bool _validate_size_net(struct _validate_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 ALWAYS_INLINE bool _validate_size_host(struct _validate_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 ALWAYS_INLINE bool _validate_list(struct _validate_ctx *ctx, - size_t cnt, size_t max, - _validate_fn_t item_fn, size_t item_host_size) { - if (max && cnt > max) - return lib9p_errorf(ctx->ctx, LINUX_EBADMSG, "list size is too large (%zu > %zu)", - cnt, max); - for (size_t i = 0; i < cnt; i++) - if (_validate_size_host(ctx, item_host_size) || item_fn(ctx)) - return true; - return false; -} - -#define validate_1(ctx) _validate_size_net(ctx, 1) -#define validate_2(ctx) _validate_size_net(ctx, 2) -#define validate_4(ctx) _validate_size_net(ctx, 4) -#define validate_8(ctx) _validate_size_net(ctx, 8) - -static ALWAYS_INLINE bool validate_d(struct _validate_ctx *ctx) { - uint32_t base_offset = ctx->net_offset; - if (validate_4(ctx)) - return true; - uint32_t len = decode_u32le(&ctx->net_bytes[base_offset]); - return _validate_size_net(ctx, len) || _validate_size_host(ctx, len); -} - -static ALWAYS_INLINE bool validate_s(struct _validate_ctx *ctx) { - uint32_t base_offset = ctx->net_offset; - if (validate_2(ctx)) - return true; - uint16_t len = decode_u16le(&ctx->net_bytes[base_offset]); - if (_validate_size_net(ctx, len) || _validate_size_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 ALWAYS_INLINE bool validate_dm(struct _validate_ctx *ctx) { - if (validate_4(ctx)) - return true; - static const lib9p_dm_t masks[LIB9P_VER_NUM] = { - [LIB9P_VER_9P2000] = 0b11101100000000000000000111111111, - [LIB9P_VER_9P2000_e] = 0b11101100000000000000000111111111, - [LIB9P_VER_9P2000_u] = 0b11101100101111000000000111111111, - }; - lib9p_dm_t mask = masks[ctx->ctx->version]; - lib9p_dm_t val = decode_u32le(&ctx->net_bytes[ctx->net_offset-4]); - if (val & ~mask) - return lib9p_errorf(ctx->ctx, LINUX_EBADMSG, "unknown bits in dm bitfield: %#04"PRIx32, - val & ~mask); - return false; -} - -static ALWAYS_INLINE bool validate_qt(struct _validate_ctx *ctx) { - if (validate_1(ctx)) - return true; - static const lib9p_qt_t masks[LIB9P_VER_NUM] = { - [LIB9P_VER_9P2000] = 0b11101100, - [LIB9P_VER_9P2000_e] = 0b11101100, - [LIB9P_VER_9P2000_u] = 0b11101110, - }; - lib9p_qt_t mask = masks[ctx->ctx->version]; - lib9p_qt_t val = decode_u8le(&ctx->net_bytes[ctx->net_offset-1]); - if (val & ~mask) - return lib9p_errorf(ctx->ctx, LINUX_EBADMSG, "unknown bits in qt bitfield: %#01"PRIx8, - val & ~mask); - return false; -} - -static ALWAYS_INLINE bool validate_qid(struct _validate_ctx *ctx) { - return validate_qt(ctx) - || validate_4(ctx) - || validate_8(ctx); -} - -static ALWAYS_INLINE bool validate_stat(struct _validate_ctx *ctx) { - return validate_2(ctx) - || validate_2(ctx) - || validate_4(ctx) - || validate_qid(ctx) - || validate_dm(ctx) - || validate_4(ctx) - || validate_4(ctx) - || validate_8(ctx) - || validate_s(ctx) - || validate_s(ctx) - || validate_s(ctx) - || validate_s(ctx) - || ( (ctx->ctx->version==LIB9P_VER_9P2000_u) && validate_s(ctx) ) - || ( (ctx->ctx->version==LIB9P_VER_9P2000_u) && validate_4(ctx) ) - || ( (ctx->ctx->version==LIB9P_VER_9P2000_u) && validate_4(ctx) ) - || ( (ctx->ctx->version==LIB9P_VER_9P2000_u) && validate_4(ctx) ); -} - -static ALWAYS_INLINE bool validate_o(struct _validate_ctx *ctx) { - if (validate_1(ctx)) - return true; - static const lib9p_o_t mask = 0b01010011; - lib9p_o_t val = decode_u8le(&ctx->net_bytes[ctx->net_offset-1]); - if (val & ~mask) - return lib9p_errorf(ctx->ctx, LINUX_EBADMSG, "unknown bits in o bitfield: %#01"PRIx8, - val & ~mask); - return false; -} - -static FLATTEN bool validate_Tversion(struct _validate_ctx *ctx) { - return validate_4(ctx) - || validate_s(ctx); -} - -static FLATTEN bool validate_Rversion(struct _validate_ctx *ctx) { - return validate_4(ctx) - || validate_s(ctx); -} - -static FLATTEN bool validate_Tauth(struct _validate_ctx *ctx) { - return validate_4(ctx) - || validate_s(ctx) - || validate_s(ctx) - || ( (ctx->ctx->version==LIB9P_VER_9P2000_u) && validate_4(ctx) ); -} - -static FLATTEN bool validate_Rauth(struct _validate_ctx *ctx) { - return validate_qid(ctx); -} - -static FLATTEN bool validate_Tattach(struct _validate_ctx *ctx) { - return validate_4(ctx) - || validate_4(ctx) - || validate_s(ctx) - || validate_s(ctx); -} - -static FLATTEN bool validate_Rattach(struct _validate_ctx *ctx) { - return validate_qid(ctx); -} - -static FLATTEN bool validate_Rerror(struct _validate_ctx *ctx) { - return validate_s(ctx) - || ( (ctx->ctx->version==LIB9P_VER_9P2000_u) && validate_4(ctx) ); -} - -static FLATTEN bool validate_Tflush(struct _validate_ctx *ctx) { - return validate_2(ctx); -} - -static FLATTEN bool validate_Rflush(struct _validate_ctx *UNUSED(ctx)) { - return false; -} - -static FLATTEN bool validate_Twalk(struct _validate_ctx *ctx) { - return validate_4(ctx) - || validate_4(ctx) - || validate_2(ctx) - || _validate_list(ctx, decode_u16le(&ctx->net_bytes[ctx->net_offset-2]), 16, validate_s, sizeof(struct lib9p_s)); -} - -static FLATTEN bool validate_Rwalk(struct _validate_ctx *ctx) { - return validate_2(ctx) - || _validate_list(ctx, decode_u16le(&ctx->net_bytes[ctx->net_offset-2]), 16, validate_qid, sizeof(struct lib9p_qid)); -} - -static FLATTEN bool validate_Topen(struct _validate_ctx *ctx) { - return validate_4(ctx) - || validate_o(ctx); -} - -static FLATTEN bool validate_Ropen(struct _validate_ctx *ctx) { - return validate_qid(ctx) - || validate_4(ctx); -} - -static FLATTEN bool validate_Tcreate(struct _validate_ctx *ctx) { - return validate_4(ctx) - || validate_s(ctx) - || validate_dm(ctx) - || validate_o(ctx); -} - -static FLATTEN bool validate_Rcreate(struct _validate_ctx *ctx) { - return validate_qid(ctx) - || validate_4(ctx); -} - -static FLATTEN bool validate_Tread(struct _validate_ctx *ctx) { - return validate_4(ctx) - || validate_8(ctx) - || validate_4(ctx); -} - -static FLATTEN bool validate_Rread(struct _validate_ctx *ctx) { - return validate_d(ctx); -} - -static FLATTEN bool validate_Twrite(struct _validate_ctx *ctx) { - return validate_4(ctx) - || validate_8(ctx) - || validate_d(ctx); -} - -static FLATTEN bool validate_Rwrite(struct _validate_ctx *ctx) { - return validate_4(ctx); -} - -static FLATTEN bool validate_Tclunk(struct _validate_ctx *ctx) { - return validate_4(ctx); -} - -static FLATTEN bool validate_Rclunk(struct _validate_ctx *UNUSED(ctx)) { - return false; -} - -static FLATTEN bool validate_Tremove(struct _validate_ctx *ctx) { - return validate_4(ctx); -} - -static FLATTEN bool validate_Rremove(struct _validate_ctx *UNUSED(ctx)) { - return false; -} - -static FLATTEN bool validate_Tstat(struct _validate_ctx *ctx) { - return validate_4(ctx); -} - -static FLATTEN bool validate_Rstat(struct _validate_ctx *ctx) { - return validate_stat(ctx); -} - -static FLATTEN bool validate_Twstat(struct _validate_ctx *ctx) { - return validate_4(ctx) - || validate_stat(ctx); -} - -static FLATTEN bool validate_Rwstat(struct _validate_ctx *UNUSED(ctx)) { - return false; -} - -static FLATTEN bool validate_Tsession(struct _validate_ctx *ctx) { - return validate_8(ctx); -} - -static FLATTEN bool validate_Rsession(struct _validate_ctx *UNUSED(ctx)) { - return false; -} - -static FLATTEN bool validate_Tsread(struct _validate_ctx *ctx) { - return validate_4(ctx) - || validate_2(ctx) - || _validate_list(ctx, decode_u16le(&ctx->net_bytes[ctx->net_offset-2]), 0, validate_s, sizeof(struct lib9p_s)); -} - -static FLATTEN bool validate_Rsread(struct _validate_ctx *ctx) { - return validate_d(ctx); -} - -static FLATTEN bool validate_Tswrite(struct _validate_ctx *ctx) { - return validate_4(ctx) - || validate_2(ctx) - || _validate_list(ctx, decode_u16le(&ctx->net_bytes[ctx->net_offset-2]), 0, validate_s, sizeof(struct lib9p_s)) - || validate_d(ctx); -} - -static FLATTEN bool validate_Rswrite(struct _validate_ctx *ctx) { - return validate_4(ctx); -} - -/* unmarshal_* ****************************************************************/ - -static ALWAYS_INLINE void unmarshal_1(struct _unmarshal_ctx *ctx, uint8_t *out) { - *out = decode_u8le(&ctx->net_bytes[ctx->net_offset]); - ctx->net_offset += 1; -} - -static ALWAYS_INLINE void unmarshal_2(struct _unmarshal_ctx *ctx, uint16_t *out) { - *out = decode_u16le(&ctx->net_bytes[ctx->net_offset]); - ctx->net_offset += 2; -} - -static ALWAYS_INLINE void unmarshal_4(struct _unmarshal_ctx *ctx, uint32_t *out) { - *out = decode_u32le(&ctx->net_bytes[ctx->net_offset]); - ctx->net_offset += 4; -} - -static ALWAYS_INLINE void unmarshal_8(struct _unmarshal_ctx *ctx, uint64_t *out) { - *out = decode_u64le(&ctx->net_bytes[ctx->net_offset]); - ctx->net_offset += 8; -} - -static ALWAYS_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->extra; - ctx->extra += sizeof(out->dat[0]) * out->len; - for (typeof(out->len) i = 0; i < out->len; i++) - unmarshal_1(ctx, &out->dat[i]); -} - -static ALWAYS_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->extra; - ctx->extra += sizeof(out->utf8[0]) * out->len; - for (typeof(out->len) i = 0; i < out->len; i++) - unmarshal_1(ctx, &out->utf8[i]); -} - -static ALWAYS_INLINE void unmarshal_dm(struct _unmarshal_ctx *ctx, lib9p_dm_t *out) { - unmarshal_4(ctx, (uint32_t *)out); -} - -static ALWAYS_INLINE void unmarshal_qt(struct _unmarshal_ctx *ctx, lib9p_qt_t *out) { - unmarshal_1(ctx, (uint8_t *)out); -} - -static ALWAYS_INLINE void unmarshal_qid(struct _unmarshal_ctx *ctx, struct lib9p_qid *out) { - memset(out, 0, sizeof(*out)); - unmarshal_qt(ctx, &out->type); - unmarshal_4(ctx, &out->vers); - unmarshal_8(ctx, &out->path); -} - -static ALWAYS_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_dm(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 ALWAYS_INLINE void unmarshal_o(struct _unmarshal_ctx *ctx, lib9p_o_t *out) { - unmarshal_1(ctx, (uint8_t *)out); -} - -static FLATTEN 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 FLATTEN 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 FLATTEN 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 FLATTEN void unmarshal_Rauth(struct _unmarshal_ctx *ctx, struct lib9p_msg_Rauth *out) { - memset(out, 0, sizeof(*out)); - unmarshal_qid(ctx, &out->aqid); -} - -static FLATTEN 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 FLATTEN void unmarshal_Rattach(struct _unmarshal_ctx *ctx, struct lib9p_msg_Rattach *out) { - memset(out, 0, sizeof(*out)); - unmarshal_qid(ctx, &out->qid); -} - -static FLATTEN 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 FLATTEN void unmarshal_Tflush(struct _unmarshal_ctx *ctx, struct lib9p_msg_Tflush *out) { - memset(out, 0, sizeof(*out)); - unmarshal_2(ctx, &out->oldtag); -} - -static FLATTEN void unmarshal_Rflush(struct _unmarshal_ctx *UNUSED(ctx), struct lib9p_msg_Rflush *out) { - memset(out, 0, sizeof(*out)); -} - -static FLATTEN 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->extra; - ctx->extra += sizeof(out->wname[0]) * out->nwname; - for (typeof(out->nwname) i = 0; i < out->nwname; i++) - unmarshal_s(ctx, &out->wname[i]); -} - -static FLATTEN 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->extra; - ctx->extra += sizeof(out->wqid[0]) * out->nwqid; - for (typeof(out->nwqid) i = 0; i < out->nwqid; i++) - unmarshal_qid(ctx, &out->wqid[i]); -} - -static FLATTEN void unmarshal_Topen(struct _unmarshal_ctx *ctx, struct lib9p_msg_Topen *out) { - memset(out, 0, sizeof(*out)); - unmarshal_4(ctx, &out->fid); - unmarshal_o(ctx, &out->mode); -} - -static FLATTEN 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 FLATTEN 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_dm(ctx, &out->perm); - unmarshal_o(ctx, &out->mode); -} - -static FLATTEN 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 FLATTEN 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 FLATTEN void unmarshal_Rread(struct _unmarshal_ctx *ctx, struct lib9p_msg_Rread *out) { - memset(out, 0, sizeof(*out)); - unmarshal_d(ctx, &out->data); -} - -static FLATTEN 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 FLATTEN void unmarshal_Rwrite(struct _unmarshal_ctx *ctx, struct lib9p_msg_Rwrite *out) { - memset(out, 0, sizeof(*out)); - unmarshal_4(ctx, &out->count); -} - -static FLATTEN void unmarshal_Tclunk(struct _unmarshal_ctx *ctx, struct lib9p_msg_Tclunk *out) { - memset(out, 0, sizeof(*out)); - unmarshal_4(ctx, &out->fid); -} - -static FLATTEN void unmarshal_Rclunk(struct _unmarshal_ctx *UNUSED(ctx), struct lib9p_msg_Rclunk *out) { - memset(out, 0, sizeof(*out)); -} - -static FLATTEN void unmarshal_Tremove(struct _unmarshal_ctx *ctx, struct lib9p_msg_Tremove *out) { - memset(out, 0, sizeof(*out)); - unmarshal_4(ctx, &out->fid); -} - -static FLATTEN void unmarshal_Rremove(struct _unmarshal_ctx *UNUSED(ctx), struct lib9p_msg_Rremove *out) { - memset(out, 0, sizeof(*out)); -} - -static FLATTEN void unmarshal_Tstat(struct _unmarshal_ctx *ctx, struct lib9p_msg_Tstat *out) { - memset(out, 0, sizeof(*out)); - unmarshal_4(ctx, &out->fid); -} - -static FLATTEN void unmarshal_Rstat(struct _unmarshal_ctx *ctx, struct lib9p_msg_Rstat *out) { - memset(out, 0, sizeof(*out)); - unmarshal_stat(ctx, &out->stat); -} - -static FLATTEN 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 FLATTEN void unmarshal_Rwstat(struct _unmarshal_ctx *UNUSED(ctx), struct lib9p_msg_Rwstat *out) { - memset(out, 0, sizeof(*out)); -} - -static FLATTEN void unmarshal_Tsession(struct _unmarshal_ctx *ctx, struct lib9p_msg_Tsession *out) { - memset(out, 0, sizeof(*out)); - unmarshal_8(ctx, &out->key); -} - -static FLATTEN void unmarshal_Rsession(struct _unmarshal_ctx *UNUSED(ctx), struct lib9p_msg_Rsession *out) { - memset(out, 0, sizeof(*out)); -} - -static FLATTEN 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->extra; - ctx->extra += sizeof(out->wname[0]) * out->nwname; - for (typeof(out->nwname) i = 0; i < out->nwname; i++) - unmarshal_s(ctx, &out->wname[i]); -} - -static FLATTEN void unmarshal_Rsread(struct _unmarshal_ctx *ctx, struct lib9p_msg_Rsread *out) { - memset(out, 0, sizeof(*out)); - unmarshal_d(ctx, &out->data); -} - -static FLATTEN 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->extra; - ctx->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 FLATTEN void unmarshal_Rswrite(struct _unmarshal_ctx *ctx, struct lib9p_msg_Rswrite *out) { - memset(out, 0, sizeof(*out)); - unmarshal_4(ctx, &out->count); -} - -/* marshal_* ******************************************************************/ - -static ALWAYS_INLINE bool _marshal_too_large(struct _marshal_ctx *ctx) { - lib9p_errorf(ctx->ctx, LINUX_ERANGE, "%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 ALWAYS_INLINE bool marshal_1(struct _marshal_ctx *ctx, uint8_t *val) { - if (ctx->net_offset + 1 > ctx->ctx->max_msg_size) - return _marshal_too_large(ctx); - ctx->net_bytes[ctx->net_offset] = *val; - ctx->net_offset += 1; - return false; -} - -static ALWAYS_INLINE bool marshal_2(struct _marshal_ctx *ctx, uint16_t *val) { - if (ctx->net_offset + 2 > ctx->ctx->max_msg_size) - return _marshal_too_large(ctx); - encode_u16le(*val, &ctx->net_bytes[ctx->net_offset]); - ctx->net_offset += 2; - return false; -} - -static ALWAYS_INLINE bool marshal_4(struct _marshal_ctx *ctx, uint32_t *val) { - if (ctx->net_offset + 4 > ctx->ctx->max_msg_size) - return true; - encode_u32le(*val, &ctx->net_bytes[ctx->net_offset]); - ctx->net_offset += 4; - return false; -} - -static ALWAYS_INLINE bool marshal_8(struct _marshal_ctx *ctx, uint64_t *val) { - if (ctx->net_offset + 8 > ctx->ctx->max_msg_size) - return true; - encode_u64le(*val, &ctx->net_bytes[ctx->net_offset]); - ctx->net_offset += 8; - return false; -} - -static ALWAYS_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 ALWAYS_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 ALWAYS_INLINE bool marshal_dm(struct _marshal_ctx *ctx, lib9p_dm_t *val) { - return marshal_4(ctx, (uint32_t *)val); -} - -static ALWAYS_INLINE bool marshal_qt(struct _marshal_ctx *ctx, lib9p_qt_t *val) { - return marshal_1(ctx, (uint8_t *)val); -} - -static ALWAYS_INLINE bool marshal_qid(struct _marshal_ctx *ctx, struct lib9p_qid *val) { - return marshal_qt(ctx, &val->type) - || marshal_4(ctx, &val->vers) - || marshal_8(ctx, &val->path); -} - -static ALWAYS_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_dm(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) - || ( (ctx->ctx->version==LIB9P_VER_9P2000_u) && marshal_s(ctx, &val->file_extension) ) - || ( (ctx->ctx->version==LIB9P_VER_9P2000_u) && marshal_4(ctx, &val->file_owner_n_uid) ) - || ( (ctx->ctx->version==LIB9P_VER_9P2000_u) && marshal_4(ctx, &val->file_owner_n_gid) ) - || ( (ctx->ctx->version==LIB9P_VER_9P2000_u) && marshal_4(ctx, &val->file_last_modified_n_uid) ); -} - -static ALWAYS_INLINE bool marshal_o(struct _marshal_ctx *ctx, lib9p_o_t *val) { - return marshal_1(ctx, (uint8_t *)val); -} - -static FLATTEN 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 FLATTEN 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 FLATTEN 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) - || ( (ctx->ctx->version==LIB9P_VER_9P2000_u) && marshal_4(ctx, &val->n_uname) ); -} - -static FLATTEN bool marshal_Rauth(struct _marshal_ctx *ctx, struct lib9p_msg_Rauth *val) { - return marshal_qid(ctx, &val->aqid); -} - -static FLATTEN 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 FLATTEN bool marshal_Rattach(struct _marshal_ctx *ctx, struct lib9p_msg_Rattach *val) { - return marshal_qid(ctx, &val->qid); -} - -static FLATTEN bool marshal_Rerror(struct _marshal_ctx *ctx, struct lib9p_msg_Rerror *val) { - return marshal_s(ctx, &val->ename) - || ( (ctx->ctx->version==LIB9P_VER_9P2000_u) && marshal_4(ctx, &val->errno) ); -} - -static FLATTEN bool marshal_Tflush(struct _marshal_ctx *ctx, struct lib9p_msg_Tflush *val) { - return marshal_2(ctx, &val->oldtag); -} - -static FLATTEN bool marshal_Rflush(struct _marshal_ctx *UNUSED(ctx), struct lib9p_msg_Rflush *UNUSED(val)) { - return false; -} - -static FLATTEN 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 FLATTEN 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 FLATTEN bool marshal_Topen(struct _marshal_ctx *ctx, struct lib9p_msg_Topen *val) { - return marshal_4(ctx, &val->fid) - || marshal_o(ctx, &val->mode); -} - -static FLATTEN bool marshal_Ropen(struct _marshal_ctx *ctx, struct lib9p_msg_Ropen *val) { - return marshal_qid(ctx, &val->qid) - || marshal_4(ctx, &val->iounit); -} - -static FLATTEN bool marshal_Tcreate(struct _marshal_ctx *ctx, struct lib9p_msg_Tcreate *val) { - return marshal_4(ctx, &val->fid) - || marshal_s(ctx, &val->name) - || marshal_dm(ctx, &val->perm) - || marshal_o(ctx, &val->mode); -} - -static FLATTEN bool marshal_Rcreate(struct _marshal_ctx *ctx, struct lib9p_msg_Rcreate *val) { - return marshal_qid(ctx, &val->qid) - || marshal_4(ctx, &val->iounit); -} - -static FLATTEN 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 FLATTEN bool marshal_Rread(struct _marshal_ctx *ctx, struct lib9p_msg_Rread *val) { - return marshal_d(ctx, &val->data); -} - -static FLATTEN 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 FLATTEN bool marshal_Rwrite(struct _marshal_ctx *ctx, struct lib9p_msg_Rwrite *val) { - return marshal_4(ctx, &val->count); -} - -static FLATTEN bool marshal_Tclunk(struct _marshal_ctx *ctx, struct lib9p_msg_Tclunk *val) { - return marshal_4(ctx, &val->fid); -} - -static FLATTEN bool marshal_Rclunk(struct _marshal_ctx *UNUSED(ctx), struct lib9p_msg_Rclunk *UNUSED(val)) { - return false; -} - -static FLATTEN bool marshal_Tremove(struct _marshal_ctx *ctx, struct lib9p_msg_Tremove *val) { - return marshal_4(ctx, &val->fid); -} - -static FLATTEN bool marshal_Rremove(struct _marshal_ctx *UNUSED(ctx), struct lib9p_msg_Rremove *UNUSED(val)) { - return false; -} - -static FLATTEN bool marshal_Tstat(struct _marshal_ctx *ctx, struct lib9p_msg_Tstat *val) { - return marshal_4(ctx, &val->fid); -} - -static FLATTEN bool marshal_Rstat(struct _marshal_ctx *ctx, struct lib9p_msg_Rstat *val) { - return marshal_stat(ctx, &val->stat); -} - -static FLATTEN bool marshal_Twstat(struct _marshal_ctx *ctx, struct lib9p_msg_Twstat *val) { - return marshal_4(ctx, &val->fid) - || marshal_stat(ctx, &val->stat); -} - -static FLATTEN bool marshal_Rwstat(struct _marshal_ctx *UNUSED(ctx), struct lib9p_msg_Rwstat *UNUSED(val)) { - return false; -} - -static FLATTEN bool marshal_Tsession(struct _marshal_ctx *ctx, struct lib9p_msg_Tsession *val) { - return marshal_8(ctx, &val->key); -} - -static FLATTEN bool marshal_Rsession(struct _marshal_ctx *UNUSED(ctx), struct lib9p_msg_Rsession *UNUSED(val)) { - return false; -} - -static FLATTEN 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 FLATTEN bool marshal_Rsread(struct _marshal_ctx *ctx, struct lib9p_msg_Rsread *val) { - return marshal_d(ctx, &val->data); -} - -static FLATTEN 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 FLATTEN bool marshal_Rswrite(struct _marshal_ctx *ctx, struct lib9p_msg_Rswrite *val) { - return marshal_4(ctx, &val->count); -} - -/* vtables ********************************************************************/ - -#define _MSG(typ) [LIB9P_TYP_##typ] = { \ - .basesize = sizeof(struct lib9p_msg_##typ), \ - .validate = validate_##typ, \ - .unmarshal = (_unmarshal_fn_t)unmarshal_##typ, \ - .marshal = (_marshal_fn_t)marshal_##typ, \ - } - -struct _vtable_version _lib9p_vtables[LIB9P_VER_NUM] = { - [LIB9P_VER_unknown] = { .msgs = { - _MSG(Tversion), - _MSG(Rversion), - _MSG(Rerror), - }}, - [LIB9P_VER_9P2000] = { .msgs = { - _MSG(Tversion), - _MSG(Rversion), - _MSG(Tauth), - _MSG(Rauth), - _MSG(Tattach), - _MSG(Rattach), - _MSG(Rerror), - _MSG(Tflush), - _MSG(Rflush), - _MSG(Twalk), - _MSG(Rwalk), - _MSG(Topen), - _MSG(Ropen), - _MSG(Tcreate), - _MSG(Rcreate), - _MSG(Tread), - _MSG(Rread), - _MSG(Twrite), - _MSG(Rwrite), - _MSG(Tclunk), - _MSG(Rclunk), - _MSG(Tremove), - _MSG(Rremove), - _MSG(Tstat), - _MSG(Rstat), - _MSG(Twstat), - _MSG(Rwstat), - }}, - [LIB9P_VER_9P2000_e] = { .msgs = { - _MSG(Tversion), - _MSG(Rversion), - _MSG(Tauth), - _MSG(Rauth), - _MSG(Tattach), - _MSG(Rattach), - _MSG(Rerror), - _MSG(Tflush), - _MSG(Rflush), - _MSG(Twalk), - _MSG(Rwalk), - _MSG(Topen), - _MSG(Ropen), - _MSG(Tcreate), - _MSG(Rcreate), - _MSG(Tread), - _MSG(Rread), - _MSG(Twrite), - _MSG(Rwrite), - _MSG(Tclunk), - _MSG(Rclunk), - _MSG(Tremove), - _MSG(Rremove), - _MSG(Tstat), - _MSG(Rstat), - _MSG(Twstat), - _MSG(Rwstat), - _MSG(Tsession), - _MSG(Rsession), - _MSG(Tsread), - _MSG(Rsread), - _MSG(Tswrite), - _MSG(Rswrite), - }}, - [LIB9P_VER_9P2000_u] = { .msgs = { - _MSG(Tversion), - _MSG(Rversion), - _MSG(Tauth), - _MSG(Rauth), - _MSG(Tattach), - _MSG(Rattach), - _MSG(Rerror), - _MSG(Tflush), - _MSG(Rflush), - _MSG(Twalk), - _MSG(Rwalk), - _MSG(Topen), - _MSG(Ropen), - _MSG(Tcreate), - _MSG(Rcreate), - _MSG(Tread), - _MSG(Rread), - _MSG(Twrite), - _MSG(Rwrite), - _MSG(Tclunk), - _MSG(Rclunk), - _MSG(Tremove), - _MSG(Rremove), - _MSG(Tstat), - _MSG(Rstat), - _MSG(Twstat), - _MSG(Rwstat), - }}, -}; diff --git a/lib9p/types.gen b/lib9p/types.gen deleted file mode 100755 index 8b1b878..0000000 --- a/lib9p/types.gen +++ /dev/null @@ -1,913 +0,0 @@ -#!/usr/bin/env python -# lib9p/types.gen - Generate C marshalers/unmarshalers for .txt files -# defining 9P protocol variants. -# -# Copyright (C) 2024 Luke T. Shumaker -# SPDX-Licence-Identifier: AGPL-3.0-or-later - -import enum -import os.path -import re -from typing import Callable, Sequence - -# This strives to be "general-purpose" in that it just acts on the -# *.txt inputs; but (unfortunately?) there are a few special-cases in -# this script, marked with "SPECIAL". - -# 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 - - -class BitfieldVal: - name: str - val: str - ver: set[str] - - def __init__(self) -> None: - self.ver = set() - - -class Bitfield: - name: str - bits: list[str] - names: dict[str, BitfieldVal] - - @property - def static_size(self) -> int: - return int((len(self.bits) + 7) / 8) - - def bitname_is_valid(self, bitname: str, ver: str | None = None) -> bool: - assert bitname in self.bits - if not bitname: - return False - if bitname.startswith("_"): - return False - if ver and (ver not in self.names[bitname].ver): - return False - return True - - -# `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 | Bitfield | 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{re_membername})\\*\\()?(?P{re_membername})\\[(?P.*)\\]\\)?" -) - - -def parse_members( - ver: str, - env: dict[str, Atom | Bitfield | 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[^"]+)"' -re_import = r"from\s+(?P\S+)\s+import\s+(?P\S+(?:\s*,\s*\S+)*)\s*" -re_structspec = ( - r'(?:(?P[0-9]+)/)?(?P\S+)\s*(?P\+?=)\s*"(?P[^"]*)"' -) -re_structspec_cont = r'\s+"(?P[^"]*)"' -re_bitfieldspec = r"bitfield\s+(?P\S+)\s+(?P[0-9]+)" -re_bitfieldspec_bit = r"(?:\s+|(?P\S+)\s*\+=\s*)(?P[0-9]+)/(?P\S+)" -re_bitfieldspec_alias = ( - r"(?:\s+|(?P\S+)\s*\+=\s*)(?P\S+)\s*=\s*(?P.*)" -) - - -def parse_file( - filename: str, get_include: Callable[[str], tuple[str, list[Bitfield | Struct]]] -) -> tuple[str, list[Bitfield | Struct]]: - version: str | None = None - env: dict[str, Atom | Bitfield | Struct] = { - "1": Atom.u8, - "2": Atom.u16, - "4": Atom.u32, - "8": Atom.u64, - } - with open(filename, "r") as fh: - prev: Struct | Bitfield | None = None - for line in fh: - line = line.split("#", 1)[0].rstrip() - 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_typs = get_include(m.group("file")) - for symname in m.group("syms").split(sep=","): - symname = symname.strip() - for typ in other_typs: - if typ.name == symname or symname == "*": - match typ: - case Bitfield(): - for val in typ.names.values(): - if other_version in val.ver: - val.ver.add(version) - case Struct(): - if typ.msgid: - typ.msgver.add(version) - for member in typ.members: - if other_version in member.ver: - member.ver.add(version) - env[typ.name] = typ - 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(_struct.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 isinstance(prev, Struct): - raise SyntaxError( - "struct-continuation line must come after a struct line" - ) - assert version - prev.members = parse_members( - version, env, prev.members, m.group("members") - ) - elif m := re.fullmatch(re_bitfieldspec, line): - if not version: - raise SyntaxError("must have exactly 1 version line") - bf = Bitfield() - bf.name = m.group("name") - bf.bits = int(m.group("size")) * [""] - bf.names = {} - if len(bf.bits) not in [8, 16, 32, 64]: - raise ValueError(f"Bitfield {repr(bf.name)} has an unusual size") - env[bf.name] = bf - prev = bf - elif m := re.fullmatch(re_bitfieldspec_bit, line): - if m.group("bitfield"): - if m.group("bitfield") not in env: - raise NameError(f"Unknown bitfield {repr(m.group('bitfield'))}") - _bf = env[m.group("bitfield")] - if not isinstance(_bf, Bitfield): - raise NameError(f"Type {repr(_bf.name)} is not a bitfield") - bf = _bf - prev = bf - else: - if not isinstance(prev, Bitfield): - raise SyntaxError( - "bitfield-continuation line must come after a bitfield line" - ) - bf = prev - bit = int(m.group("bit")) - name = m.group("name") - if bit < 0 or bit >= len(bf.bits): - raise ValueError(f"{bf.name}: bit {bit} is out-of-bounds") - if bf.bits[bit]: - raise ValueError(f"{bf.name}: bit {bit} already assigned") - if name in bf.names: - raise ValueError(f"{bf.name}: name {name} already assigned") - - bf.bits[bit] = name - - assert version - val = BitfieldVal() - val.name = name - val.val = f"1<<{bit}" - val.ver.add(version) - bf.names[name] = val - elif m := re.fullmatch(re_bitfieldspec_alias, line): - if m.group("bitfield"): - if m.group("bitfield") not in env: - raise NameError(f"Unknown bitfield {repr(m.group('bitfield'))}") - _bf = env[m.group("bitfield")] - if not isinstance(_bf, Bitfield): - raise NameError(f"Type {repr(_bf.name)} is not a bitfield") - bf = _bf - prev = bf - else: - if not isinstance(prev, Bitfield): - raise SyntaxError( - "bitfield-continuation line must come after a bitfield line" - ) - bf = prev - name = m.group("name") - valstr = m.group("val") - if name in bf.names: - raise ValueError(f"{bf.name}: name {name} already assigned") - - assert version - val = BitfieldVal() - val.name = name - val.val = valstr - val.ver.add(version) - bf.names[name] = val - else: - raise SyntaxError(f"invalid line {repr(line)}") - if not version: - raise SyntaxError("must have exactly 1 version line") - - typs = [x for x in env.values() if not isinstance(x, Atom)] - return version, typs - - -# Generate C ################################################################### - - -def c_typename(idprefix: str, typ: Atom | Bitfield | Struct) -> str: - match typ: - case Atom(): - return f"uint{typ.value*8}_t" - case Bitfield(): - return f"{idprefix}{typ.name}_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_verenum(idprefix: str, ver: str) -> str: - return f"{idprefix.upper()}VER_{ver.replace('.', '_')}" - - -def c_vercomment(versions: set[str]) -> str | None: - if "9P2000" in versions: - return None - return "/* " + (", ".join(sorted(versions))) + " */" - - -def c_vercond(idprefix: str, versions: set[str]) -> str: - if len(versions) == 1: - return f"(ctx->ctx->version=={c_verenum(idprefix, next(v for v in versions))})" - return ( - "( " + (" || ".join(c_vercond(idprefix, {v}) for v in sorted(versions))) + " )" - ) - - -def just_structs_all(typs: list[Bitfield | Struct]) -> Sequence[Struct]: - return list(typ for typ in typs if isinstance(typ, Struct)) - - -def just_structs_nonmsg(typs: list[Bitfield | Struct]) -> Sequence[Struct]: - return list(typ for typ in typs if isinstance(typ, Struct) and typ.msgid is None) - - -def just_structs_msg(typs: list[Bitfield | Struct]) -> Sequence[Struct]: - return list( - typ for typ in typs if isinstance(typ, Struct) and typ.msgid is not None - ) - - -def just_bitfields(typs: list[Bitfield | Struct]) -> Sequence[Bitfield]: - return list(typ for typ in typs if isinstance(typ, Bitfield)) - - -def gen_h(idprefix: str, versions: set[str], typs: list[Bitfield | Struct]) -> str: - guard = "_LIB9P__TYPES_H_" - ret = f"""/* Generated by `{' '.join(sys.argv)}`. DO NOT EDIT! */ - -#ifndef {guard} -#define {guard} - -#include /* for uint{{n}}_t types */ -""" - - ret += f""" -/* versions *******************************************************************/ - -enum {idprefix}version {{ -""" - fullversions = ["unknown = 0", *sorted(versions)] - verwidth = max(len(v) for v in fullversions) - for ver in fullversions: - ret += f"\t{c_verenum(idprefix, ver)}," - ret += (" " * (verwidth - len(ver))) + ' /* "' + ver.split()[0] + '" */\n' - ret += f"\t{c_verenum(idprefix, 'NUM')},\n" - ret += "};\n" - ret += "\n" - ret += f"const char *{idprefix}version_str(enum {idprefix}version);\n" - - ret += """ -/* non-message types **********************************************************/ -""" - for bf in just_bitfields(typs): - ret += "\n" - ret += f"typedef uint{bf.static_size*8}_t {c_typename(idprefix, bf)};\n" - names = [ - *reversed([bf.bits[n] or f"_UNUSED_{n}" for n in range(0, len(bf.bits))]), - *[k for k in bf.names if k not in bf.bits], - ] - namewidth = max(len(name) for name in names) - - for name in names: - if name.startswith("_"): - cname = f"_{idprefix.upper()}{bf.name.upper()}_{name[1:]}" - else: - cname = f"{idprefix.upper()}{bf.name.upper()}_{name}" - if name in bf.names: - val = bf.names[name].val - else: - assert name.startswith("_UNUSED_") - val = f"1<<{name[len('_UNUSED_'):]}" - ret += f"#define {cname}{' '*(namewidth-len(name))} (({c_typename(idprefix, bf)})({val}))" - if (name in bf.names) and (comment := c_vercomment(bf.names[name].ver)): - ret += " " + comment - ret += "\n" - - for struct in just_structs_nonmsg(typs): - 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 just_structs_msg(typs)) - for msg in just_structs_msg(typs): - 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"const char *{idprefix}msg_type_str(enum {idprefix}msg_type);\n" - - for msg in just_structs_msg(typs): - 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], typs: list[Bitfield | Struct]) -> str: - ret = f"""/* Generated by `{' '.join(sys.argv)}`. DO NOT EDIT! */ - -#include -#include -#include /* for size_t */ -#include /* for PRI* macros */ -#include /* for memset() */ - -#include - -#include "internal.h" -""" - - def used(arg: str) -> str: - return arg - - def unused(arg: str) -> str: - return f"UNUSED({arg})" - - # strings ################################################################## - ret += f""" -/* strings ********************************************************************/ - -static const char *version_strs[{c_verenum(idprefix, 'NUM')}] = {{ -""" - for ver in ["unknown", *sorted(versions)]: - ret += f'\t[{c_verenum(idprefix, ver)}] = "{ver}",\n' - ret += "};\n" - ret += f""" -const char *{idprefix}version_str(enum {idprefix}version ver) {{ - assert(0 <= ver && ver < {c_verenum(idprefix, 'NUM')}); - return version_strs[ver]; -}} - -static const char *msg_type_strs[0x100] = {{ -""" - id2name: dict[int, str] = {} - for msg in just_structs_msg(typs): - assert msg.msgid - id2name[msg.msgid] = msg.name - for n in range(0, 0x100): - ret += '\t[0x{:02X}] = "{}",\n'.format(n, id2name.get(n, "0x{:02X}".format(n))) - ret += "};\n" - ret += f""" -const char *{idprefix}msg_type_str(enum {idprefix}msg_type typ) {{ - assert(0 <= typ && typ <= 0xFF); - return msg_type_strs[typ]; -}} -""" - - # validate_* ############################################################### - ret += """ -/* validate_* *****************************************************************/ - -static ALWAYS_INLINE bool _validate_size_net(struct _validate_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 ALWAYS_INLINE bool _validate_size_host(struct _validate_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 ALWAYS_INLINE bool _validate_list(struct _validate_ctx *ctx, - size_t cnt, size_t max, - _validate_fn_t item_fn, size_t item_host_size) { - if (max && cnt > max) - return lib9p_errorf(ctx->ctx, LINUX_EBADMSG, "list size is too large (%zu > %zu)", - cnt, max); - for (size_t i = 0; i < cnt; i++) - if (_validate_size_host(ctx, item_host_size) || item_fn(ctx)) - return true; - return false; -} - -#define validate_1(ctx) _validate_size_net(ctx, 1) -#define validate_2(ctx) _validate_size_net(ctx, 2) -#define validate_4(ctx) _validate_size_net(ctx, 4) -#define validate_8(ctx) _validate_size_net(ctx, 8) -""" - for typ in typs: - inline = ( - " FLATTEN" - if (isinstance(typ, Struct) and typ.msgid is not None) - else " ALWAYS_INLINE" - ) - argfn = unused if (isinstance(typ, Struct) and not typ.members) else used - ret += "\n" - ret += f"static{inline} bool validate_{typ.name}(struct _validate_ctx *{argfn('ctx')}) {{" - - if typ.name == "d": # SPECIAL - # 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 (validate_4(ctx))\n" - ret += "\t\treturn true;\n" - ret += "\tuint32_t len = decode_u32le(&ctx->net_bytes[base_offset]);\n" - ret += "\treturn _validate_size_net(ctx, len) || _validate_size_host(ctx, len);\n" - ret += "}\n" - continue - if typ.name == "s": # SPECIAL - # 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 (validate_2(ctx))\n" - ret += "\t\treturn true;\n" - ret += "\tuint16_t len = decode_u16le(&ctx->net_bytes[base_offset]);\n" - ret += "\tif (_validate_size_net(ctx, len) || _validate_size_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" - continue - - match typ: - case Bitfield(): - ret += "\n" - all_the_same = all( - val.ver == [*typ.names.values()][0].ver - for val in typ.names.values() - ) - if ( - all_the_same - and (len(typ.bits) == typ.static_size * 8) - and all(typ.bitname_is_valid(bitname) for bitname in typ.bits) - ): - ret += f"\treturn validate_{typ.static_size}(ctx));\n" - else: - ret += f"\t if (validate_{typ.static_size}(ctx))\n" - ret += "\t\treturn true;\n" - if all_the_same: - ret += ( - f"\tstatic const {c_typename(idprefix, typ)} mask = 0b" - + "".join( - "1" if typ.bitname_is_valid(bitname) else "0" - for bitname in reversed(typ.bits) - ) - + ";\n" - ) - else: - ret += f"\tstatic const {c_typename(idprefix, typ)} masks[{c_verenum(idprefix, 'NUM')}] = {{\n" - verwidth = max(len(ver) for ver in versions) - for ver in sorted(versions): - ret += ( - f"\t\t[{c_verenum(idprefix, ver)}]{' '*(verwidth-len(ver))} = 0b" - + "".join( - "1" if typ.bitname_is_valid(bitname, ver) else "0" - for bitname in reversed(typ.bits) - ) - + ",\n" - ) - ret += "\t};\n" - ret += f"\t{c_typename(idprefix, typ)} mask = masks[ctx->ctx->version];\n" - ret += f"\t{c_typename(idprefix, typ)} val = decode_u{typ.static_size*8}le(&ctx->net_bytes[ctx->net_offset-{typ.static_size}]);\n" - ret += f"\tif (val & ~mask)\n" - ret += f'\t\treturn lib9p_errorf(ctx->ctx, LINUX_EBADMSG, "unknown bits in {typ.name} bitfield: %#0{typ.static_size}"PRIx{typ.static_size*8},\n' - ret += "\t\t val & ~mask);\n" - ret += "\treturn false;\n" - case Struct(): - if len(typ.members) == 0: - ret += "\n\treturn false;\n" - ret += "}\n" - continue - - prefix0 = "\treturn " - prefix1 = "\t || " - - struct_versions = typ.members[0].ver - - prefix = prefix0 - prev_size: int | None = None - for member in typ.members: - ret += f"\n{prefix}" - if member.ver != struct_versions: - ret += "( " + c_vercond(idprefix, member.ver) + " && " - if member.cnt is not None: - assert prev_size - maxelem = 0 - if ( - typ.name in ["Twalk", "Rwalk"] and member.name[:1] == "w" - ): # SPECIAL - maxelem = 16 - ret += f"_validate_list(ctx, decode_u{prev_size*8}le(&ctx->net_bytes[ctx->net_offset-{prev_size}]), {maxelem}, validate_{member.typ.name}, sizeof({c_typename(idprefix, member.typ)}))" - else: - ret += f"validate_{member.typ.name}(ctx)" - if member.ver != struct_versions: - ret += " )" - prefix = prefix1 - prev_size = member.static_size - ret += ";\n" - ret += "}\n" - - # unmarshal_* ############################################################## - ret += """ -/* unmarshal_* ****************************************************************/ - -static ALWAYS_INLINE void unmarshal_1(struct _unmarshal_ctx *ctx, uint8_t *out) { - *out = decode_u8le(&ctx->net_bytes[ctx->net_offset]); - ctx->net_offset += 1; -} - -static ALWAYS_INLINE void unmarshal_2(struct _unmarshal_ctx *ctx, uint16_t *out) { - *out = decode_u16le(&ctx->net_bytes[ctx->net_offset]); - ctx->net_offset += 2; -} - -static ALWAYS_INLINE void unmarshal_4(struct _unmarshal_ctx *ctx, uint32_t *out) { - *out = decode_u32le(&ctx->net_bytes[ctx->net_offset]); - ctx->net_offset += 4; -} - -static ALWAYS_INLINE void unmarshal_8(struct _unmarshal_ctx *ctx, uint64_t *out) { - *out = decode_u64le(&ctx->net_bytes[ctx->net_offset]); - ctx->net_offset += 8; -} -""" - for typ in typs: - inline = ( - " FLATTEN" - if (isinstance(typ, Struct) and typ.msgid is not None) - else " ALWAYS_INLINE" - ) - argfn = unused if (isinstance(typ, Struct) and not typ.members) else used - ret += "\n" - ret += f"static{inline} void unmarshal_{typ.name}(struct _unmarshal_ctx *{argfn('ctx')}, {c_typename(idprefix, typ)} *out) {{\n" - match typ: - case Bitfield(): - ret += f"\tunmarshal_{typ.static_size}(ctx, (uint{typ.static_size*8}_t *)out);\n" - case Struct(): - ret += "\tmemset(out, 0, sizeof(*out));\n" - - if typ.members: - struct_versions = typ.members[0].ver - for member in typ.members: - ret += "\t" - prefix = "\t" - if member.ver != struct_versions: - ret += "if ( " + c_vercond(idprefix, member.ver) + " ) " - prefix = "\t\t" - if member.cnt: - if member.ver != struct_versions: - ret += "{\n" - ret += f"{prefix}out->{member.name} = ctx->extra;\n" - ret += f"{prefix}ctx->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 ALWAYS_INLINE bool _marshal_too_large(struct _marshal_ctx *ctx) { - lib9p_errorf(ctx->ctx, LINUX_ERANGE, "%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 ALWAYS_INLINE bool marshal_1(struct _marshal_ctx *ctx, uint8_t *val) { - if (ctx->net_offset + 1 > ctx->ctx->max_msg_size) - return _marshal_too_large(ctx); - ctx->net_bytes[ctx->net_offset] = *val; - ctx->net_offset += 1; - return false; -} - -static ALWAYS_INLINE bool marshal_2(struct _marshal_ctx *ctx, uint16_t *val) { - if (ctx->net_offset + 2 > ctx->ctx->max_msg_size) - return _marshal_too_large(ctx); - encode_u16le(*val, &ctx->net_bytes[ctx->net_offset]); - ctx->net_offset += 2; - return false; -} - -static ALWAYS_INLINE bool marshal_4(struct _marshal_ctx *ctx, uint32_t *val) { - if (ctx->net_offset + 4 > ctx->ctx->max_msg_size) - return true; - encode_u32le(*val, &ctx->net_bytes[ctx->net_offset]); - ctx->net_offset += 4; - return false; -} - -static ALWAYS_INLINE bool marshal_8(struct _marshal_ctx *ctx, uint64_t *val) { - if (ctx->net_offset + 8 > ctx->ctx->max_msg_size) - return true; - encode_u64le(*val, &ctx->net_bytes[ctx->net_offset]); - ctx->net_offset += 8; - return false; -} -""" - for typ in typs: - inline = ( - " FLATTEN" - if (isinstance(typ, Struct) and typ.msgid is not None) - else " ALWAYS_INLINE" - ) - argfn = unused if (isinstance(typ, Struct) and not typ.members) else used - ret += "\n" - ret += f"static{inline} bool marshal_{typ.name}(struct _marshal_ctx *{argfn('ctx')}, {c_typename(idprefix, typ)} *{argfn('val')}) {{" - match typ: - case Bitfield(): - ret += "\n" - ret += f"\treturn marshal_{typ.static_size}(ctx, (uint{typ.static_size*8}_t *)val);\n" - case Struct(): - if len(typ.members) == 0: - ret += "\n\treturn false;\n" - ret += "}\n" - continue - - prefix0 = "\treturn " - prefix1 = "\t || " - prefix2 = "\t " - - struct_versions = typ.members[0].ver - prefix = prefix0 - for member in typ.members: - ret += f"\n{prefix}" - if member.ver != struct_versions: - ret += "( " + c_vercond(idprefix, member.ver) + " && " - if member.cnt: - ret += "({" - 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"marshal_{member.typ.name}(ctx, &val->{member.name})" - if member.ver != struct_versions: - ret += " )" - prefix = prefix1 - ret += ";\n" - ret += "}\n" - - # vtables ################################################################## - ret += f""" -/* vtables ********************************************************************/ - -#define _MSG(typ) [{idprefix.upper()}TYP_##typ] = {{ \\ - .basesize = sizeof(struct {idprefix}msg_##typ), \\ - .validate = validate_##typ, \\ - .unmarshal = (_unmarshal_fn_t)unmarshal_##typ, \\ - .marshal = (_marshal_fn_t)marshal_##typ, \\ - }} - -struct _vtable_version _{idprefix}vtables[{c_verenum(idprefix, 'NUM')}] = {{ -""" - - ret += f"\t[{c_verenum(idprefix, 'unknown')}] = {{ .msgs = {{\n" - for msg in just_structs_msg(typs): - if msg.name in ["Tversion", "Rversion", "Rerror"]: # SPECIAL - ret += f"\t\t_MSG({msg.name}),\n" - ret += "\t}},\n" - - for ver in sorted(versions): - ret += f"\t[{c_verenum(idprefix, ver)}] = {{ .msgs = {{\n" - for msg in just_structs_msg(typs): - if ver not in msg.msgver: - continue - ret += f"\t\t_MSG({msg.name}),\n" - ret += "\t}},\n" - ret += "};\n" - - ############################################################################ - return ret - - -################################################################################ - - -class Parser: - cache: dict[str, tuple[str, list[Bitfield | Struct]]] = {} - - def parse_file(self, filename: str) -> tuple[str, list[Bitfield | Struct]]: - filename = os.path.normpath(filename) - if filename not in self.cache: - - def get_include(other_filename: str) -> tuple[str, list[Bitfield | Struct]]: - return self.parse_file(os.path.join(filename, "..", other_filename)) - - self.cache[filename] = parse_file(filename, get_include) - return self.cache[filename] - - def all(self) -> tuple[set[str], list[Bitfield | Struct]]: - ret_versions: set[str] = set() - ret_typs: dict[str, Bitfield | Struct] = {} - for version, typs in self.cache.values(): - if version in ret_versions: - raise ValueError(f"duplicate protocol version {repr(version)}") - ret_versions.add(version) - for typ in typs: - if typ.name in ret_typs: - if typ != ret_typs[typ.name]: - raise ValueError(f"duplicate type name {repr(typ.name)}") - else: - ret_typs[typ.name] = typ - return ret_versions, list(ret_typs.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, typs = parser.all() - outdir = os.path.normpath(os.path.join(sys.argv[0], "..")) - with open(os.path.join(outdir, "include/lib9p/_types.h"), "w") as fh: - fh.write(gen_h("lib9p_", versions, typs)) - with open(os.path.join(outdir, "types.c"), "w") as fh: - fh.write(gen_c("lib9p_", versions, typs)) -- cgit v1.2.3-2-g168b