diff options
-rw-r--r-- | .editorconfig | 4 | ||||
-rw-r--r-- | .gitignore | 4 | ||||
-rw-r--r-- | 3rd-party/.gitignore | 1 | ||||
-rw-r--r-- | 3rd-party/linux-errno.txt | 132 | ||||
-rw-r--r-- | 9p/.editorconfig | 3 | ||||
-rw-r--r-- | 9p/.gitignore | 5 | ||||
-rw-r--r-- | 9p/9P2000.L.txt | 56 | ||||
-rw-r--r-- | 9p/9P2000.e.txt | 11 | ||||
-rw-r--r-- | 9p/9P2000.txt | 82 | ||||
-rw-r--r-- | 9p/9P2000.u.txt | 20 | ||||
-rwxr-xr-x | 9p/generate | 541 | ||||
-rwxr-xr-x | 9p/linux-errno.h.gen | 33 | ||||
-rw-r--r-- | 9p/misc.txt | 24 | ||||
-rw-r--r-- | 9p/srv.c (renamed from net9p.c) | 0 | ||||
-rw-r--r-- | 9p/srv.h (renamed from net9p.h) | 0 | ||||
-rw-r--r-- | Makefile | 30 | ||||
-rwxr-xr-x | net9p_defs.gen | 496 | ||||
-rw-r--r-- | net9p_defs.txt | 176 |
18 files changed, 935 insertions, 683 deletions
diff --git a/.editorconfig b/.editorconfig index 574abd1..c04f338 100644 --- a/.editorconfig +++ b/.editorconfig @@ -5,7 +5,3 @@ end_of_line = lf insert_final_newline = true charset = utf-8 indent_style = tab - -[net9p_defs.gen] -indent_style = space -indent_size = 4 @@ -3,10 +3,6 @@ /build/ -/3rd-party/MS-LCID.txt /tusb_helpers.h -/net9p_defs.c -/net9p_defs.h - /srv9p diff --git a/3rd-party/.gitignore b/3rd-party/.gitignore new file mode 100644 index 0000000..4305236 --- /dev/null +++ b/3rd-party/.gitignore @@ -0,0 +1 @@ +/MS-LCID.txt diff --git a/3rd-party/linux-errno.txt b/3rd-party/linux-errno.txt new file mode 100644 index 0000000..34b297d --- /dev/null +++ b/3rd-party/linux-errno.txt @@ -0,0 +1,132 @@ +# Generated from linux.git v6.7. DO NOT EDIT! +1 EPERM +2 ENOENT +3 ESRCH +4 EINTR +5 EIO +6 ENXIO +7 E2BIG +8 ENOEXEC +9 EBADF +10 ECHILD +11 EAGAIN +12 ENOMEM +13 EACCES +14 EFAULT +15 ENOTBLK +16 EBUSY +17 EEXIST +18 EXDEV +19 ENODEV +20 ENOTDIR +21 EISDIR +22 EINVAL +23 ENFILE +24 EMFILE +25 ENOTTY +26 ETXTBSY +27 EFBIG +28 ENOSPC +29 ESPIPE +30 EROFS +31 EMLINK +32 EPIPE +33 EDOM +34 ERANGE +35 EDEADLK +36 ENAMETOOLONG +37 ENOLCK +38 ENOSYS +39 ENOTEMPTY +40 ELOOP +42 ENOMSG +43 EIDRM +44 ECHRNG +45 EL2NSYNC +46 EL3HLT +47 EL3RST +48 ELNRNG +49 EUNATCH +50 ENOCSI +51 EL2HLT +52 EBADE +53 EBADR +54 EXFULL +55 ENOANO +56 EBADRQC +57 EBADSLT +59 EBFONT +60 ENOSTR +61 ENODATA +62 ETIME +63 ENOSR +64 ENONET +65 ENOPKG +66 EREMOTE +67 ENOLINK +68 EADV +69 ESRMNT +70 ECOMM +71 EPROTO +72 EMULTIHOP +73 EDOTDOT +74 EBADMSG +75 EOVERFLOW +76 ENOTUNIQ +77 EBADFD +78 EREMCHG +79 ELIBACC +80 ELIBBAD +81 ELIBSCN +82 ELIBMAX +83 ELIBEXEC +84 EILSEQ +85 ERESTART +86 ESTRPIPE +87 EUSERS +88 ENOTSOCK +89 EDESTADDRREQ +90 EMSGSIZE +91 EPROTOTYPE +92 ENOPROTOOPT +93 EPROTONOSUPPORT +94 ESOCKTNOSUPPORT +95 EOPNOTSUPP +96 EPFNOSUPPORT +97 EAFNOSUPPORT +98 EADDRINUSE +99 EADDRNOTAVAIL +100 ENETDOWN +101 ENETUNREACH +102 ENETRESET +103 ECONNABORTED +104 ECONNRESET +105 ENOBUFS +106 EISCONN +107 ENOTCONN +108 ESHUTDOWN +109 ETOOMANYREFS +110 ETIMEDOUT +111 ECONNREFUSED +112 EHOSTDOWN +113 EHOSTUNREACH +114 EALREADY +115 EINPROGRESS +116 ESTALE +117 EUCLEAN +118 ENOTNAM +119 ENAVAIL +120 EISNAM +121 EREMOTEIO +122 EDQUOT +123 ENOMEDIUM +124 EMEDIUMTYPE +125 ECANCELED +126 ENOKEY +127 EKEYEXPIRED +128 EKEYREVOKED +129 EKEYREJECTED +130 EOWNERDEAD +131 ENOTRECOVERABLE +132 ERFKILL +133 EHWPOISON diff --git a/9p/.editorconfig b/9p/.editorconfig new file mode 100644 index 0000000..3de59d7 --- /dev/null +++ b/9p/.editorconfig @@ -0,0 +1,3 @@ +[{generate,linux-errno.h.gen}] +indent_style = space +indent_size = 4 diff --git a/9p/.gitignore b/9p/.gitignore new file mode 100644 index 0000000..c8e8c58 --- /dev/null +++ b/9p/.gitignore @@ -0,0 +1,5 @@ +/9P2000.c +/9P2000.h +/9P2000.*.c +/9P2000.*.h +/linux-errno.h diff --git a/9p/9P2000.L.txt b/9p/9P2000.L.txt new file mode 100644 index 0000000..71ab171 --- /dev/null +++ b/9p/9P2000.L.txt @@ -0,0 +1,56 @@ +# 9P2000.L.txt - Definitions of 9P2000.L messages +# +# Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com> +# SPDX-Licence-Identifier: AGPL-3.0-or-later + +# "9P2000.L" Linux extension +# https://github.com/chaos/diod/blob/master/protocol.md +version "9P2000.L" + +from 9P2000.txt import * +from 9P2000.u.txt import Tauth, Tattach + +#6/Tlerror = "illegal" # analogous to 106/Terror +7/Rlerror = "ecode[4]" # analogous to 107/Rerror +8/Tstatfs = "TODO" +9/Rstatfs = "TODO" +12/Tlopen = "TODO" # analogous to 112/Topen +13/Rlopen = "TODO" # analogous to 113/Ropen +14/Tlcreate = "TODO" # analogous to 114/Tcreate +15/Rlcreate = "TODO" # analogous to 115/Rcreate +16/Tsymlink = "TODO" +17/Rsymlink = "TODO" +18/Tmknod = "TODO" +19/Rmknod = "TODO" +20/Trename = "TODO" +21/Rrename = "TODO" +22/Treadlink = "TODO" +23/Rreadlink = "TODO" +24/Tgetattr = "TODO" +25/Rgetattr = "TODO" +26/Tsetattr = "TODO" +27/Rsetattr = "TODO" +#... +30/Txattrwalk = "TODO" +31/Rxattrwalk = "TODO" +32/Txattrcreate = "TODO" +33/Rxattrcreate = "TODO" +#... +40/Treaddir = "TODO" +41/Rreaddir = "TODO" +#... +50/Tfsync = "TODO" +51/Rfsync = "TODO" +52/Tlock = "TODO" +53/Rlock = "TODO" +54/Tgetlock = "TODO" +55/Rgetlock = "TODO" +# ... +70/Tlink = "TODO" +71/Rlink = "TODO" +72/Tmkdir = "TODO" +73/Tmkdir = "TODO" +74/Trenameat = "TODO" +75/Rrenameat = "TODO" +76/Tunlinkat = "TODO" +77/Runlinkat = "TODO" diff --git a/9p/9P2000.e.txt b/9p/9P2000.e.txt new file mode 100644 index 0000000..f926dc0 --- /dev/null +++ b/9p/9P2000.e.txt @@ -0,0 +1,11 @@ +# 9P2000e.e.txt - Definitions of 9P2000.e messages +# +# Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com> +# SPDX-Licence-Identifier: AGPL-3.0-or-later + +# "9P2000.e" Erlang extension +# https://erlangonxen.org/more/9p2000e +# https://github.com/cloudozer/ling/blob/master/doc/9p2000e.md +version "9P2000.e" + +# TODO diff --git a/9p/9P2000.txt b/9p/9P2000.txt new file mode 100644 index 0000000..778673f --- /dev/null +++ b/9p/9P2000.txt @@ -0,0 +1,82 @@ +# 9P2000.txt - Definitions of 9P2000 messages +# +# Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com> +# SPDX-Licence-Identifier: AGPL-3.0-or-later + +# The format of each message (excluding the "size[4] msg_type[1] +# tag[2]" header) is written here as a sequence of +# "member_name[member_type]" struct members. +# +# The primitive member types types are the following single-character +# mnemonics: +# +# - 1 = u8 +# - 2 = u16le +# - 4 = u32le +# - 8 = u16le + +# "9P2000" base protocol +# https://ericvh.github.io/9p-rfc/rfc9p2000.html +# https://github.com/ericvh/9p-rfc/blob/master/9p2000.xml +# +# But due to incompleteness of the draft RFC, the Plan 9 manual +# section-5 and the Plan 9 headers (particularly fcall.h) are a better +# references. +version "9P2000" + +# data (u32le `n`, then `n` bytes of data) +d = "len[8] len*(dat[1])" + +# string (u16le `n`, then `n` bytes of UTF-8) +s = "len[2] len*(utf8[1])" + +# qid (TODO) +qid = "type[1] vers[4] path[8]" + +# stat (TODO) +stat = "stat_size[2]" + "kern_type[2]" + "kern_dev[4]" + "file_qid[qid]" + "file_mode[4]" + "file_atime[4]" + "file_mtime[4]" + "file_size[8]" + "file_name[s]" + "file_owner_uid[s]" + "file_owner_gid[s]" + "file_last_modified_uid[s]" + +# In the 9P protocol, each message has a type, and message types come +# in pairs (except "Rerror"); "T" and "R"; "T" messages are +# client->server requests, and "R" messages are server->client +# responses (I do not know what the Plan 9 designers intended "T" and +# "R" to stand for). The type of a message is represented by a u8 ID. +100/Tversion = "max_msg_size[4] version[s]" +101/Rversion = "max_msg_size[4] version[s]" +102/Tauth = "afid[4] uname[s] aname[s]" +103/Rauth = "aqid[qid]" +104/Tattach = "fid[4] afid[4] uname[s] aname[s]" +105/Rattach = "qid[qid]" +#106/Terror = "illegal" +107/Rerror = "ename[s]" +108/Tflush = "oldtag[2]" +109/Rflush = "" +110/Twalk = "fid[4] newfid[4] nwname[2] nwname*(wname[s])" +111/Rwalk = "nwqid[2] nwqid*(wqid[qid])" +112/Topen = "fid[4] mode[1]" +113/Ropen = "qid[qid] iounit[4]" +114/Tcreate = "fid[4] name[s] perm[4] mode[1]" +115/Rcreate = "qid[qid] iounit[4]" +116/Tread = "fid[4] offset[8] count[4]" +117/Rread = "data[d]" # for directories data is the sequence "cnt*(entries[stat])" +118/Twrite = "fid[4] offset[8] data[d]" +119/Rwrite = "count[4]" +120/Tclunk = "fid[4]" +121/Rclunk = "" +122/Tremove = "fid[4]" +123/Rremove = "" +124/Tstat = "fid[4]" +125/Rstat = "stat[stat]" +126/Twstat = "fid[4] stat[stat]" +127/Rwstat = "" diff --git a/9p/9P2000.u.txt b/9p/9P2000.u.txt new file mode 100644 index 0000000..20aea48 --- /dev/null +++ b/9p/9P2000.u.txt @@ -0,0 +1,20 @@ +# 9P2000.u.txt - Definitions of 9P2000.u messages +# +# Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com> +# SPDX-Licence-Identifier: AGPL-3.0-or-later + +# "9P2000.u" Unix extension +# https://ericvh.github.io/9p-rfc/rfc9p2000.u.html +# https://github.com/ericvh/9p-rfc/blob/master/9p2000.u.xml +version "9P2000.u" + +from 9P2000.txt import * + +stat += "file_extension[s]" + "file_owner_n_uid[4]" + "file_owner_n_gid[4]" + "file_last_modified_n_uid[4]" + +Tauth += "nxs_uname[4]" + +Rerror += "errno[4]" diff --git a/9p/generate b/9p/generate new file mode 100755 index 0000000..6456609 --- /dev/null +++ b/9p/generate @@ -0,0 +1,541 @@ +#!/usr/bin/env python +# 9p/generate - Generate C marshalers/unmarshalers for a .txt file +# defining a 9P protocol variant. +# +# Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com> +# SPDX-Licence-Identifier: AGPL-3.0-or-later + +import enum +import re + +PROGRAM = "./9p/generate" + +# Parse the *.txt ############################################################## + + +class Atom(enum.Enum): + u8 = 1 + u16 = 2 + u32 = 4 + u64 = 8 + + @property + def name(self) -> str: + return str(self.value) + + @property + def static_size(self) -> int: + return self.value + + +# `msgid/structname = "member1 member2..."` +# `structname = "member1 member2..."` +# `structname += "member1 member2..."` +class Struct: + msgid: int | None = None + name: str + members: list["Member"] + + @property + def static_size(self) -> int | None: + size = 0 + for member in self.members: + msize = member.static_size + if msize is None: + return None + size += msize + return size + + +# `cnt*(name[typ])` +# the `cnt*(...)` wrapper is optional +class Member: + cnt: str | None = None + name: str + typ: Atom | Struct + + @property + def static_size(self) -> int | None: + if self.cnt: + return None + return self.typ.static_size + + +re_membername = "(?:[a-zA-Z_][a-zA-Z_0-9]*)" +re_memberspec = ( + f"(?:(?P<cnt>{re_membername})\\*\\()?(?P<name>{re_membername})\\[(?P<typ>.*)\\]\\)?" +) + + +def parse_members( + env: dict[str, Atom | Struct], existing: list[Member], specs: str +) -> list[Member]: + ret = existing + for spec in specs.split(): + m = re.fullmatch(re_memberspec, spec) + if not m: + raise SyntaxError(f"invalid member spec {repr(spec)}") + + member = Member() + + member.name = m.group("name") + if any(x.name == member.name for x in ret): + raise ValueError(f"duplicate member name {repr(member.name)}") + + if m.group("typ") not in env: + raise NameError(f"Unknown type {repr(m.group(2))}") + member.typ = env[m.group("typ")] + + if cnt := m.group("cnt"): + if len(ret) == 0 or ret[-1].name != cnt: + raise ValueError(f"list count must be previous item: {repr(cnt)}") + if not isinstance(ret[-1].typ, Atom): + raise ValueError(f"list count must be an integer type: {repr(cnt)}") + member.cnt = cnt + + ret += [member] + return ret + + +re_version = r'version\s+"(?P<version>[^"]+)"' +re_structspec = ( + r'(?:(?P<msgid>[0-9]+)/)?(?P<name>\S+)\s*(?P<op>\+?=)\s*"(?P<members>[^"]*)"' +) +re_structspec_cont = r'"(?P<members>[^"]*)"' + + +def parse_file(filename: str) -> tuple[str, list[Struct]]: + version: str | None = None + env: dict[str, Atom | Struct] = { + "1": Atom.u8, + "2": Atom.u16, + "4": Atom.u32, + "8": Atom.u64, + } + with open(filename, "r") as fh: + prev: Struct | None = None + for line in fh: + line = line.split("#", 1)[0].strip() + if not line: + continue + if m := re.fullmatch(re_version, line): + if version: + raise SyntaxError("must have exactly 1 version line") + version = m.group("version") + elif m := re.fullmatch(re_structspec, line): + if m.group("op") == "+=" and m.group("msgid"): + raise SyntaxError("cannot += to a message that is not yet defined") + match m.group("op"): + case "=": + struct = Struct() + if m.group("msgid"): + struct.msgid = int(m.group("msgid")) + struct.name = m.group("name") + struct.members = parse_members(env, [], m.group("members")) + env[struct.name] = struct + prev = struct + case "+=": + if m.group("name") not in env: + raise NameError(f"Unknown type {repr(m.group('name'))}") + _struct = env[m.group("name")] + if not isinstance(_struct, Struct): + raise NameError( + f"Type {repr(m.group('name'))} is not a struct" + ) + struct = _struct + struct.members = parse_members( + env, struct.members, m.group("members") + ) + prev = struct + elif m := re.fullmatch(re_structspec_cont, line): + if not prev: + raise SyntaxError("continuation line must come after a struct line") + prev.members = parse_members(env, prev.members, m.group("members")) + else: + raise SyntaxError(f"invalid line {repr(line)}") + if not version: + raise SyntaxError("must have exactly 1 version line") + structs = [x for x in env.values() if isinstance(x, Struct)] + return version, structs + + +# Generate C ################################################################### + + +def c_typename(idprefix: str, typ: Atom | Struct) -> str: + match typ: + case Atom(): + return f"uint{typ.value*8}_t" + case Struct(): + if typ.msgid is not None: + return f"struct {idprefix}msg_{typ.name}" + return f"struct {idprefix}{typ.name}" + case _: + raise ValueError(f"not a type: {typ.__class__.__name__}") + + +def gen_h(txtname: str, idprefix: str, structs: list[Struct]) -> str: + guard = ( + "_" + + txtname.replace(".txt", ".h").upper().replace("/", "_").replace(".", "_") + + "_" + ) + ret = f"""/* Generated by `{PROGRAM} {txtname}`. DO NOT EDIT! */ + +#ifndef {guard} +#define {guard} + +#define {idprefix.upper()}MIN_MSGLEN 7 +""" + ret += """ +/* non-message structs ********************************************************/ + +""" + for struct in structs: + if struct.msgid is not None: + continue + ret += c_typename(idprefix, struct) + " {\n" + typewidth = max( + len(c_typename(idprefix, member.typ)) for member in struct.members + ) + for member in struct.members: + ret += f"\t{c_typename(idprefix, member.typ).ljust(typewidth)} {'*' if member.cnt else ' '}{member.name};\n" + ret += "};\n" + + ret += """ +/* message types **************************************************************/ + +""" + ret += f"enum {idprefix}msg_type {{ /* uint8_t */\n" + namewidth = max(len(msg.name) for msg in structs if msg.msgid is not None) + for msg in structs: + if msg.msgid is None: + continue + ret += f"\t{idprefix.upper()}TYP_{msg.name.ljust(namewidth)} = {msg.msgid},\n" + ret += "};\n" + + ret += """ +/* message structs ************************************************************/ + +""" + for msg in structs: + if msg.msgid is None: + continue + if not msg.members: + ret += c_typename(idprefix, msg) + " {};\n" + continue + ret += c_typename(idprefix, msg) + " {\n" + typewidth = max(len(c_typename(idprefix, member.typ)) for member in msg.members) + for member in msg.members: + ret += f"\t{c_typename(idprefix, member.typ).ljust(typewidth)} {'*' if member.cnt else ' '}{member.name};\n" + ret += "};\n" + + ret += f""" +/* functions ******************************************************************/ + +/** + * @param net_bytes : the complete request, starting with the "size[4]" + * @param out_tag : the message-ID tag + * @param out_body : a pointer that get set to the parsed body, whose + * type is known by the return value, will need to + * be free()d + * @return -{idprefix.upper()}E{{error}} on error, {idprefix.upper()}TYP_{{type}} on success + */ +int {idprefix}unmarshal_msg(uint8_t *net_bytes, uint16_t *out_tag, void **out_body); + +/** + * @param uint16_t in_msgid : the message-ID tag + * @param struct {idprefix}msg_{{type}} in_msg : the message to encode + * @param uint8_t *out_buf : the buffer to encode to + * @return uint32_t : the encoded length + */ +#define {idprefix}marshal_msg(in_msgid, in_msg, out_buf) _Generic((in_msg)""" + for msg in structs: + if msg.msgid is None: + continue + ret += f", \\\n\t\t{c_typename(idprefix, msg)}: _{idprefix}marshal_{msg.name}(in_msgid, in_msg, out_buf)" + ret += "\\\n\t)(in_msg)\n" + for msg in structs: + if msg.msgid is None: + continue + ret += f"uint32_t _{idprefix}marshal_{msg.name}(uint16_t in_msgid, {c_typename(idprefix, msg)} in_msg, uint8_t *out_buf);\n" + + ret += "\n" + ret += f"#endif /* {guard} */\n" + return ret + + +def gen_c(txtname: str, idprefix: str, structs: list[Struct]) -> str: + ret = f"""/* Generated by `{PROGRAM} {txtname}`. DO NOT EDIT! */ + +#include <stdbool.h> +#include <stdint.h> +#include <stdlib.h> /* for malloc() */ + +#include "{txtname.replace('.txt', '.h')}" +""" + + # basic utilities ########################################################## + ret += """ +/* basic utilities ************************************************************/ + +#define UNUSED(name) /* name __attribute__ ((unused)) */ + +static inline uint8_t decode_u8le(uint8_t *in) { + return in[0]; +} +static inline uint16_t decode_u16le(uint8_t *in) { + return (((uint16_t)(in[0])) << 0) + | (((uint16_t)(in[1])) << 8) + ; +} +static inline uint32_t decode_u32le(uint8_t *in) { + return (((uint32_t)(in[0])) << 0) + | (((uint32_t)(in[1])) << 8) + | (((uint32_t)(in[2])) << 16) + | (((uint32_t)(in[3])) << 24) + ; +} +static inline uint64_t decode_u64le(uint8_t *in) { + return (((uint64_t)(in[0])) << 0) + | (((uint64_t)(in[1])) << 8) + | (((uint64_t)(in[2])) << 16) + | (((uint64_t)(in[3])) << 24) + | (((uint64_t)(in[4])) << 32) + | (((uint64_t)(in[5])) << 40) + | (((uint64_t)(in[6])) << 48) + | (((uint64_t)(in[7])) << 56) + ; +} + +static inline void encode_u8le(uint8_t in, uint8_t *out) { + out[0] = in; +} +static inline void encode_u16le(uint16_t in, uint8_t *out) { + out[0] = (uint8_t)((in >> 0) & 0xFF); + out[1] = (uint8_t)((in >> 8) & 0xFF); +} +static inline void encode_u32le(uint32_t in, uint8_t *out) { + out[0] = (uint8_t)((in >> 0) & 0xFF); + out[1] = (uint8_t)((in >> 8) & 0xFF); + out[2] = (uint8_t)((in >> 16) & 0xFF); + out[3] = (uint8_t)((in >> 24) & 0xFF); +} +static inline void encode_u64le(uint64_t in, uint8_t *out) { + out[0] = (uint8_t)((in >> 0) & 0xFF); + out[1] = (uint8_t)((in >> 8) & 0xFF); + out[2] = (uint8_t)((in >> 16) & 0xFF); + out[3] = (uint8_t)((in >> 24) & 0xFF); + out[4] = (uint8_t)((in >> 32) & 0xFF); + out[5] = (uint8_t)((in >> 40) & 0xFF); + out[6] = (uint8_t)((in >> 48) & 0xFF); + out[7] = (uint8_t)((in >> 56) & 0xFF); +} +""" + + def used(arg: str) -> str: + return arg + + def unused(arg: str) -> str: + return f"UNUSED({arg})" + + # checksize_* ############################################################## + ret += """ +/* checksize_* ****************************************************************/ + +typedef bool (*_checksize_fn_t)(uint32_t net_len, uint8_t *net_bytes, uint32_t *mut_net_offset, size_t *mut_host_extra); +static inline bool _checksize_list(size_t cnt, _checksize_fn_t fn, size_t host_size, + uint32_t net_len, uint8_t *net_bytes, uint32_t *mut_net_offset, size_t *mut_host_extra) { + for (size_t i = 0; i < cnt; i++) + if (__builtin_add_overflow(*mut_host_extra, host_size, mut_host_extra) + || fn(net_len, net_bytes, mut_net_offset, mut_host_extra)) + return true; + return false; +} +static inline bool checksize_1(uint32_t net_len, uint8_t *UNUSED(net_bytes), uint32_t *mut_net_offset, size_t *UNUSED(mut_host_extra)) { + return __builtin_add_overflow(*mut_net_offset, 1, mut_net_offset) || net_len < *mut_net_offset; +} +static inline bool checksize_2(uint32_t net_len, uint8_t *UNUSED(net_bytes), uint32_t *mut_net_offset, size_t *UNUSED(mut_host_extra)) { + return __builtin_add_overflow(*mut_net_offset, 2, mut_net_offset) || net_len < *mut_net_offset; +} +static inline bool checksize_4(uint32_t net_len, uint8_t *UNUSED(net_bytes), uint32_t *mut_net_offset, size_t *UNUSED(mut_host_extra)) { + return __builtin_add_overflow(*mut_net_offset, 4, mut_net_offset) || net_len < *mut_net_offset; +} +static inline bool checksize_8(uint32_t net_len, uint8_t *UNUSED(net_bytes), uint32_t *mut_net_offset, size_t *UNUSED(mut_host_extra)) { + return __builtin_add_overflow(*mut_net_offset, 8, mut_net_offset) || net_len < *mut_net_offset; +} +""" + for struct in structs: + argfn = used if struct.members else unused + ret += f"static inline bool checksize_{struct.name}(uint32_t {argfn('net_len')}, uint8_t *{argfn('net_bytes')}, uint32_t *{argfn('mut_net_offset')}, size_t *{argfn('mut_host_extra')}) {{\n" + if len(struct.members) == 0: + ret += "\treturn false;\n" + ret += "}\n" + continue + prefix0 = "\treturn " + prefix1 = "\t || " + prefix2 = "\t " + prefix = prefix0 + prev_size: int | None = None + for member in struct.members: + if member.cnt is not None: + assert prev_size + ret += f"\n{prefix }_checksize_list(decode_u{prev_size*8}le(&net_bytes[(*mut_net_offset)-{prev_size}]), checksize_{member.typ.name}, sizeof({c_typename(idprefix, member.typ)})," + ret += f"\n{prefix2} net_len, net_bytes, mut_net_offset, mut_host_extra)" + else: + ret += f"\n{prefix}checksize_{member.typ.name}(net_len, net_bytes, mut_net_offset, mut_host_extra)" + prefix = prefix1 + prev_size = member.static_size + if struct.name == "s": + ret += ( + f"\n{prefix}__builtin_add_overflow(*mut_host_extra, 1, mut_host_extra)" + ) + ret += ";\n}\n" + + # unmarshal_* ############################################################## + ret += """ +/* unmarshal_* ****************************************************************/ +/* checksize_XXX() should be called before unmarshal_XXX(). */ + +static inline void unmarshal_1(uint8_t *net_bytes, uint32_t *mut_net_offset, void **UNUSED(mut_host_extra), uint8_t *out) { + *out = decode_u8le(&net_bytes[*mut_net_offset]); + *mut_net_offset += 1; +} +static inline void unmarshal_2(uint8_t *net_bytes, uint32_t *mut_net_offset, void **UNUSED(mut_host_extra), uint16_t *out) { + *out = decode_u16le(&net_bytes[*mut_net_offset]); + *mut_net_offset += 2; +} +static inline void unmarshal_4(uint8_t *net_bytes, uint32_t *mut_net_offset, void **UNUSED(mut_host_extra), uint32_t *out) { + *out = decode_u32le(&net_bytes[*mut_net_offset]); + *mut_net_offset += 4; +} +static inline void unmarshal_8(uint8_t *net_bytes, uint32_t *mut_net_offset, void **UNUSED(mut_host_extra), uint64_t *out) { + *out = decode_u64le(&net_bytes[*mut_net_offset]); + *mut_net_offset += 8; +} +""" + for struct in structs: + argfn = used if struct.members else unused + ret += f"static inline void unmarshal_{struct.name}(uint8_t *{argfn('net_bytes')}, uint32_t *{argfn('mut_net_offset')}, void **{argfn('mut_host_extra')}, {c_typename(idprefix, struct)} *{argfn('out')}) {{" + if len(struct.members) == 0: + ret += "}\n" + continue + ret += "\n" + for member in struct.members: + if member.cnt: + ret += f"\tout->{member.name} = *mut_host_extra;\n" + ret += f"\t*mut_host_extra += sizeof(*out->{member.name}) * out->{member.cnt};\n" + ret += f"\tfor (typeof(out->{member.cnt}) i = 0; i < out->{member.cnt}; i++)\n" + ret += f"\t\tunmarshal_{member.typ.name}(net_bytes, mut_net_offset, mut_host_extra, &(out->{member.name}[i]));\n" + else: + ret += f"\tunmarshal_{member.typ.name}(net_bytes, mut_net_offset, mut_host_extra, &(out->{member.name}));\n" + ret += "}\n" + + # unmarshal_msg ############################################################ + ret += f""" +/* unmarshal_msg **************************************************************/ + +int {idprefix}unmarshal_msg(uint8_t *net_bytes, uint16_t *out_tag, void **out_body) {{ + uint32_t net_len = decode_u32le(net_bytes); + if (net_len < 7) + return -LINUX_EWRONGSIZE; + uint8_t typ = net_bytes[4]; + *out_tag = decode_u16le(&net_bytes[5]); + + uint32_t net_offset = 7; + size_t host_size; + void *host_extra; + switch (typ) {{ +""" + for msg in structs: + if msg.msgid is None: + continue + ret += f"\tcase {idprefix.upper()}TYP_{msg.name}:\n" + ret += f"\t\thost_size = sizeof({c_typename(idprefix, msg)});\n" + ret += f"\t\tif (checksize_{msg.name}(net_len, net_bytes, &net_offset, &host_size))\n" + ret += "\t\t\treturn -LINUX_EWRONGSIZE;\n" + ret += "\n" + ret += "\t\t*out_body = malloc(host_size);" + ret += "\n" + ret += "\t\tnet_offset = 7;\n" + ret += f"\t\thost_extra = *out_body + sizeof({c_typename(idprefix, msg)});\n" + ret += f"\t\tunmarshal_{msg.name}(net_bytes, &net_offset, &host_extra, *out_body);\n" + ret += "\n" + ret += "\t\tbreak;\n" + ret += """ + default: + return -LINUX_EOPNOTSUPP; + } + return typ; +} +""" + + # marshal_* ################################################################ + ret += """ +/* marshal_* ******************************************************************/ + +static inline void marshal_1(uint8_t val, uint8_t *out_net_bytes, uint32_t *mut_net_offset) { + out_net_bytes[*mut_net_offset] = val; + *mut_net_offset += 1; +} +static inline void marshal_2(uint16_t val, uint8_t *out_net_bytes, uint32_t *mut_net_offset) { + encode_u16le(val, &out_net_bytes[*mut_net_offset]); + *mut_net_offset += 2; +} +static inline void marshal_4(uint32_t val, uint8_t *out_net_bytes, uint32_t *mut_net_offset) { + encode_u32le(val, &out_net_bytes[*mut_net_offset]); + *mut_net_offset += 4; +} +static inline void marshal_8(uint64_t val, uint8_t *out_net_bytes, uint32_t *mut_net_offset) { + encode_u64le(val, &out_net_bytes[*mut_net_offset]); + *mut_net_offset += 8; +} +""" + for struct in structs: + argfn = used if struct.members else unused + ret += f"static inline void marshal_{struct.name}({c_typename(idprefix, struct)} {argfn('val')}, uint8_t *{argfn('out_net_bytes')}, uint32_t *{argfn('mut_net_offset')}) {{" + if len(struct.members) == 0: + ret += "}\n" + continue + ret += "\n" + for member in struct.members: + if member.cnt: + ret += f"\tfor (typeof(val.{member.cnt}) i = 0; i < val.{member.cnt}; i++)\n" + ret += f"\t\tmarshal_{member.typ.name}(val.{member.name}[i], out_net_bytes, mut_net_offset);\n" + else: + ret += f"\tmarshal_{member.typ.name}(val.{member.name}, out_net_bytes, mut_net_offset);\n" + ret += "}\n" + + # _marshal_msg_* ########################################################### + ret += """ +/* _marshal_msg_* *************************************************************/ + +""" + for msg in structs: + if msg.msgid is None: + continue + ret += f"uint32_t _{idprefix}marshal_{msg.name}(uint16_t in_msgid, {c_typename(idprefix, msg)} in_msg, uint8_t *out_buf) {{\n" + ret += "\tuint32_t offset = 4;\n" + ret += f"\tmarshal_1({idprefix.upper()}TYP_{msg.name}, out_buf, &offset);\n" + ret += "\tmarshal_2(in_msgid, out_buf, &offset);\n" + ret += f"\tmarshal_{msg.name}(in_msg, out_buf, &offset);\n" + ret += "\tencode_u32le(offset, out_buf);\n" + ret += "\treturn offset;\n" + ret += "}\n" + ret += "\n" + + ############################################################################ + return ret + + +################################################################################ + +if __name__ == "__main__": + import sys + + for txtname in sys.argv[1:]: + version, structs = parse_file(txtname) + with open(txtname.replace(".txt", ".h"), "w") as fh: + fh.write(gen_h(txtname, "p9_", structs)) + with open(txtname.replace(".txt", ".c"), "w") as fh: + fh.write(gen_c(txtname, "p9_", structs)) diff --git a/9p/linux-errno.h.gen b/9p/linux-errno.h.gen new file mode 100755 index 0000000..749cd8e --- /dev/null +++ b/9p/linux-errno.h.gen @@ -0,0 +1,33 @@ +#!/usr/bin/env python + + +def print_errnos(txtlists: list[str]) -> None: + print( + f"/* Generated by `./9p/linux-errno.h.gen {' '.join(txtlists)}`. DO NOT EDIT! */" + ) + errnos: dict[str, int] = {} + for txtlist in sys.argv[1:]: + with open(txtlist, "r") as fh: + for line in fh: + if line.startswith("#"): + print(f"/* {line[1:].strip()} */") + continue + _num, name = line.split(maxsplit=1) + num = int(_num) + name = name.strip() + errnos[name] = int(num) + namelen = max(len(name) for name in errnos.keys()) + print() + print("#ifndef _9P_LINUX_ERRNO_H_") + print("#define _9P_LINUX_ERRNO_H_") + print() + for name in errnos: + print(f"#define LINUX_{name.ljust(namelen)} {errnos[name]}") + print() + print("#endif /* _9P_LINUX_ERRNO_H_ */") + + +if __name__ == "__main__": + import sys + + print_errnos(sys.argv[1:]) diff --git a/9p/misc.txt b/9p/misc.txt new file mode 100644 index 0000000..93aa304 --- /dev/null +++ b/9p/misc.txt @@ -0,0 +1,24 @@ +# qid.types +QTDIR = 1<<7 +QTAPPEND = 1<<6 +QTEXCL = 1<<5 +QTMOUNT = 1<<4 # been around forever, but undocumented? +QTAUTH = 1<<3 +QTTMP = 1<<2 # added to Plan 9 2003-12 +QTSYMLINK = 1<<1 # .u +QTFILE = 1<<0 + +DMDIR = 1<<31 +DMAPPEND = 1<<30 +DMEXCL = 1<<29 +DMMOUNT = 1<<28 +DMAUTH = 1<<27 +DMTMP = 1<<26 +# = 1<<25 +# = 1<<24 +DMDEVICE = 1<<23 # .u +# = 1<<22 +DMNAMEDPIPE = 1<<21 # .u +DMSOCKET = 1<<20 # .u +DMSETUID = 1<<19 # .u +DMSETGID = 1<<18 # .u @@ -1,12 +1,36 @@ CFLAGS += -fno-split-stack CFLAGS += -Wall -Wextra -Werror CFLAGS += -g -O0 +CPPFLAGS += -I. LDFLAGS += -static -srv9p: srv9p.o coroutine.o net9p.o +linux.git = $(HOME)/src/github.com/torvalds/linux -net9p_defs.h net9p_defs.c: net9p_defs.gen net9p_defs.txt - ./$< +3rd-party/linux-errno.txt: + { \ + cd $(linux.git) && \ + echo "# Generated from linux.git $$(git describe). DO NOT EDIT!" && \ + git ls-files include/uapi/ | grep errno | xargs grep -E '#\s*define\s+E[A-Z0-9]+\s+[0-9]+' | awk '{print $$3, $$2}' | sort --numeric-sort && \ + :; } >$@ + +9p/linux-errno.h: %: %.gen 3rd-party/linux-errno.txt + $^ >$@ +9p/%.c 9p/%.h: 9p/generate 9p/%.txt + $^ + +srv9p: srv9p.o coroutine.o net9p.o 9p/9P2000.o + +sources_py = 9p/generate +sources_py += 9p/linux-errno.h.gen + +lint: + mypy --strict --scripts-are-modules $(sources_py) + black --check $(sources_py) + isort --check $(sources_py) +format: + black $(sources_py) + isort $(sources_py) +.PHONY: lint format .NOTINTERMEDIATE: .DELETE_ON_ERROR: diff --git a/net9p_defs.gen b/net9p_defs.gen deleted file mode 100755 index 53235df..0000000 --- a/net9p_defs.gen +++ /dev/null @@ -1,496 +0,0 @@ -#!/usr/bin/env python - -import enum -import re - - -# Parse net9p_defs.txt ######################################################### - -class Atom(enum.Enum): - u8 = 1 - u16 = 2 - u32 = 3 - u64 = 4 - - -class Struct: - name: str - members: list["Member"] - - -class List: - cnt: str - typ: Atom | Struct - - def __init__(self, /, *, cnt: str, typ: Atom | Struct) -> None: - self.cnt = cnt - self.typ = typ - - -class Member: - name: str - typ: Atom | Struct | List - - def __init__(self, /, *, name: str, typ: Atom | Struct | List) -> None: - self.name = name - self.typ = typ - - -def parse_members( - env: dict[str, Atom | Struct], existing: list[Member], specs: str -) -> list[Member]: - ret = existing - for spec in specs.split(): - m = re.fullmatch(r"(.+)\[([^*]+)(?:\*([^*]+))?\]", spec) - if not m: - raise SyntaxError(f"invalid member spec {repr(spec)}") - if m.group(2) not in env: - raise NameError(f"Unknown type {repr(m.group(2))}") - name = m.group(1) - typ = env[m.group(2)] - if any(x.name == name for x in ret): - raise ValueError(f"duplicate member name {repr(name)}") - if cnt := m.group(3): - 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)}") - ret += [Member(name=name, typ=List(cnt=cnt, typ=typ))] - else: - ret += [Member(name=name, typ=typ)] - return ret - - -class Message: - id: int - name: str - members: list[Member] - - -def parse_file(filename: str) -> tuple[list[Struct], list[Message]]: - msgs: list[Message] = [] - env: dict[str, Atom | Struct] = { - "1": Atom.u8, - "2": Atom.u16, - "4": Atom.u32, - "8": Atom.u64, - } - with open(filename, "r") as fh: - prev: Struct | Message | None = None - for line in fh: - line = line.split("#", 1)[0].strip() - if not line: - continue - if m := re.fullmatch(r'([0-9]+)\s*=\s*(\S+)\s*"([^"]*)"', line): - msg = Message() - msg.id = int(m.group(1)) - msg.name = m.group(2) - msg.members = parse_members(env, [], m.group(3)) - msgs += [msg] - prev = msg - elif m := re.fullmatch(r'(\S+)\s*=\s*"([^"]*)"', line): - struct = Struct() - struct.name = m.group(1) - struct.members = parse_members(env, [], m.group(2)) - env[struct.name] = struct - prev = struct - elif m := re.fullmatch(r'"([^"]*)"', line): - if not prev: - raise SyntaxError( - "a continuation line must come after a struct line" - ) - prev.members = parse_members(env, prev.members, line.strip('"')) - else: - raise SyntaxError(f"invalid line {repr(line)}") - structs = [x for x in env.values() if isinstance(x, Struct)] - return structs, msgs - -# Generate C ################################################################### - -def shortname(typ: Atom | Struct | Message) -> str: - match typ: - case Atom.u8: - return "1" - case Atom.u16: - return "2" - case Atom.u32: - return "4" - case Atom.u64: - return "8" - case Struct(): - return typ.name - case Message(): - return 'msg_'+typ.name - case _: - raise ValueError(f"not a type: {typ.__class__.__name__}") - -def c_typename(typ: Atom | Struct | List | Message) -> str: - match typ: - case Atom.u8: - return "uint8_t" - case Atom.u16: - return "uint16_t" - case Atom.u32: - return "uint32_t" - case Atom.u64: - return "uint64_t" - case Struct(): - return "struct v9fs_" + typ.name - case Message(): - return "struct v9fs_msg_" + typ.name - case List(): - return c_typename(typ.typ) + "*" - case _: - raise ValueError(f"not a type: {typ.__class__.__name__}") - - -def static_size(typ: Atom | Struct | List | Message) -> int | None: - match typ: - case Atom.u8: - return 1 - case Atom.u16: - return 2 - case Atom.u32: - return 4 - case Atom.u64: - return 8 - case Struct() | Message(): - size = 0 - for member in typ.members: - msize = static_size(member.typ) - if msize is None: - return None - size += msize - return size - case List(): - return None - case _: - raise ValueError(f"not a type: {typ.__class__.__name__}") - - -def gen_h(structs: list[Struct], msgs: list[Message]) -> str: - ret = "" - ret += "/* Generated by ./net9p_defs.gen. DO NOT EDIT! */\n" - ret += "\n" - ret += "#ifndef _NET9P_DEFS_H_\n" - ret += "#define _NET9P_DEFS_H_\n" - ret += "\n" - ret += "#define V9FS_EWRONGSIZE 7\n" - ret += "#define V9FS_EBADTYPE 4\n" - - ret += "\n" - for struct in structs: - ret += c_typename(struct) + " {\n" - typewidth = max(len(c_typename(member.typ)) for member in struct.members) - for member in struct.members: - ret += f"\t{c_typename(member.typ).ljust(typewidth)} {member.name};\n" - ret += "};\n" - - ret += "\n" - ret += "enum v9fs_msg_type {\n" - namewidth = max(len(msg.name) for msg in msgs) - for msg in msgs: - ret += f"\tV9FS_TYP_{msg.name.ljust(namewidth)} = {msg.id},\n" - ret += "};\n" - - ret += "\n" - for msg in msgs: - if not msg.members: - ret += c_typename(msg) + " {};\n" - else: - ret += c_typename(msg) + " {\n" - typewidth = max(len(c_typename(member.typ)) for member in msg.members) - for member in msg.members: - ret += f"\t{c_typename(member.typ).ljust(typewidth)} {member.name};\n" - ret += "};\n" - - ret += """ -/** - * @param net_bytes : the complete request, starting with the "size[4]" - * @param out_tag : the message-ID tag - * @param out_body : a pointer that get set to the parsed body, whose - * type is known by the return value, will need to - * be free()d - * @return -V9FS_E{error} on errors, V9FS_TYP_{type} on success - */ -int v9fs_unmarshal_msg(uint8_t *net_bytes, uint16_t *out_tag, void **out_body); -""" - - ret += """ -/** - * @param uint16_t in_msgid : the message-ID tag - * @param struct v9fs_msg_{type} in_msg : the message to encode - * @param uint8_t *out_buf : the buffer to encode to - * @return uint32_t : the encoded length - */ -#define v9fs_marshal_msg(in_msgid, in_msg, out_buf) _Generic((in_msg)""" - for msg in msgs: - ret += f", \\\n\t\t{c_typename(msg)}: _v9fs_marshal_{shortname(msg)}(in_msgid, in_msg, out_buf)" - ret += "\\\n\t)(in_msg)\n" - for msg in msgs: - ret += f"uint32_t _v9fs_marshal_{shortname(msg)}(uint16_t in_msgid, {c_typename(msg)} in_msg, uint8_t *out_buf);\n" - - ret += "\n" - ret += "#endif /* _NET9P_DEFS_H_ */\n" - return ret - - -def gen_c(structs: list[Struct], msgs: list[Message]) -> str: - ret = """ -/* Generated by ./net9p_defs.gen. DO NOT EDIT! */ - -#include <stdbool.h> -#include <stdint.h> -#include <stdlib.h> /* for malloc() */ - -#include "net9p_defs.h" -""" - - # basic utilities ########################################################## - ret += """ -/* basic utilities ************************************************************/ - -#define UNUSED __attribute__ ((unused)) - -static inline uint8_t decode_u8le(uint8_t *in) { - return in[0]; -} -static inline uint16_t decode_u16le(uint8_t *in) { - return (((uint16_t)(in[0])) << 0) - | (((uint16_t)(in[1])) << 8) - ; -} -static inline uint32_t decode_u32le(uint8_t *in) { - return (((uint32_t)(in[0])) << 0) - | (((uint32_t)(in[1])) << 8) - | (((uint32_t)(in[2])) << 16) - | (((uint32_t)(in[3])) << 24) - ; -} -static inline uint64_t decode_u64le(uint8_t *in) { - return (((uint64_t)(in[0])) << 0) - | (((uint64_t)(in[1])) << 8) - | (((uint64_t)(in[2])) << 16) - | (((uint64_t)(in[3])) << 24) - | (((uint64_t)(in[4])) << 32) - | (((uint64_t)(in[5])) << 40) - | (((uint64_t)(in[6])) << 48) - | (((uint64_t)(in[7])) << 56) - ; -} - -static inline void encode_u8le(uint8_t in, uint8_t *out) { - out[0] = in; -} -static inline void encode_u16le(uint16_t in, uint8_t *out) { - out[0] = (uint8_t)((in >> 0) & 0xFF); - out[1] = (uint8_t)((in >> 8) & 0xFF); -} -static inline void encode_u32le(uint32_t in, uint8_t *out) { - out[0] = (uint8_t)((in >> 0) & 0xFF); - out[1] = (uint8_t)((in >> 8) & 0xFF); - out[2] = (uint8_t)((in >> 16) & 0xFF); - out[3] = (uint8_t)((in >> 24) & 0xFF); -} -static inline void encode_u64le(uint64_t in, uint8_t *out) { - out[0] = (uint8_t)((in >> 0) & 0xFF); - out[1] = (uint8_t)((in >> 8) & 0xFF); - out[2] = (uint8_t)((in >> 16) & 0xFF); - out[3] = (uint8_t)((in >> 24) & 0xFF); - out[4] = (uint8_t)((in >> 32) & 0xFF); - out[5] = (uint8_t)((in >> 40) & 0xFF); - out[6] = (uint8_t)((in >> 48) & 0xFF); - out[7] = (uint8_t)((in >> 56) & 0xFF); -} -""" - - # checksize_* ############################################################## - ret += """ -/* checksize_* ****************************************************************/ - -typedef bool (*_checksize_fn_t)(uint32_t net_len, uint8_t *net_bytes, uint32_t *mut_net_offset, size_t *mut_host_extra); -static inline bool _checksize_list(size_t cnt, _checksize_fn_t fn, size_t host_size, - uint32_t net_len, uint8_t *net_bytes, uint32_t *mut_net_offset, size_t *mut_host_extra) { - for (size_t i = 0; i < cnt; i++) - if (__builtin_add_overflow(*mut_host_extra, host_size, mut_host_extra) - || fn(net_len, net_bytes, mut_net_offset, mut_host_extra)) - return true; - return false; -} -static inline bool checksize_1(uint32_t net_len, uint8_t *net_bytes UNUSED, uint32_t *mut_net_offset, size_t *mut_host_extra UNUSED) { - return __builtin_add_overflow(*mut_net_offset, 1, mut_net_offset) || net_len < *mut_net_offset; -} -static inline bool checksize_2(uint32_t net_len, uint8_t *net_bytes UNUSED, uint32_t *mut_net_offset, size_t *mut_host_extra UNUSED) { - return __builtin_add_overflow(*mut_net_offset, 2, mut_net_offset) || net_len < *mut_net_offset; -} -static inline bool checksize_4(uint32_t net_len, uint8_t *net_bytes UNUSED, uint32_t *mut_net_offset, size_t *mut_host_extra UNUSED) { - return __builtin_add_overflow(*mut_net_offset, 4, mut_net_offset) || net_len < *mut_net_offset; -} -static inline bool checksize_8(uint32_t net_len, uint8_t *net_bytes UNUSED, uint32_t *mut_net_offset, size_t *mut_host_extra UNUSED) { - return __builtin_add_overflow(*mut_net_offset, 8, mut_net_offset) || net_len < *mut_net_offset; -} -""" - for struct in structs + msgs: - argattr = ' UNUSED' if len(struct.members) == 0 else '' - ret += f"static inline bool checksize_{shortname(struct)}(uint32_t net_len{argattr}, uint8_t *net_bytes{argattr}, uint32_t *mut_net_offset{argattr}, size_t *mut_host_extra{argattr}) {{\n" - if len(struct.members) == 0: - ret += "\treturn false;\n" - ret += "}\n" - continue - prefix0 = "\treturn " - prefix1 = "\t || " - prefix2 = "\t " - prefix = prefix0 - prev_size: int|None = None - for member in struct.members: - if isinstance(member.typ, List): - ret += f"\n{prefix }_checksize_list(decode_u{prev_size*8}le(&net_bytes[(*mut_net_offset)-{prev_size}]), checksize_{shortname(member.typ.typ)}, sizeof({c_typename(member.typ.typ)})," - ret += f"\n{prefix2} net_len, net_bytes, mut_net_offset, mut_host_extra)" - else: - ret += f"\n{prefix}checksize_{shortname(member.typ)}(net_len, net_bytes, mut_net_offset, mut_host_extra)" - prefix = prefix1 - prev_size = static_size(member.typ) - if struct.name == 's': - ret += f"\n{prefix}__builtin_add_overflow(*mut_host_extra, 1, mut_host_extra)" - ret += ";\n}\n" - - # unmarshal_* ############################################################## - ret += """ -/* unmarshal_* ****************************************************************/ -/* checksize_XXX() should be called before unmarshal_XXX(). */ - -static inline void unmarshal_1(uint8_t *net_bytes, uint32_t *mut_net_offset, void **mut_host_extra UNUSED, uint8_t *out) { - *out = decode_u8le(&net_bytes[*mut_net_offset]); - *mut_net_offset += 1; -} -static inline void unmarshal_2(uint8_t *net_bytes, uint32_t *mut_net_offset, void **mut_host_extra UNUSED, uint16_t *out) { - *out = decode_u16le(&net_bytes[*mut_net_offset]); - *mut_net_offset += 2; -} -static inline void unmarshal_4(uint8_t *net_bytes, uint32_t *mut_net_offset, void **mut_host_extra UNUSED, uint32_t *out) { - *out = decode_u32le(&net_bytes[*mut_net_offset]); - *mut_net_offset += 4; -} -static inline void unmarshal_8(uint8_t *net_bytes, uint32_t *mut_net_offset, void **mut_host_extra UNUSED, uint64_t *out) { - *out = decode_u64le(&net_bytes[*mut_net_offset]); - *mut_net_offset += 8; -} -""" - for struct in structs + msgs: - argattr = ' UNUSED' if len(struct.members) == 0 else '' - ret += f"static inline void unmarshal_{shortname(struct)}(uint8_t *net_bytes{argattr}, uint32_t *mut_net_offset{argattr}, void **mut_host_extra{argattr}, {c_typename(struct)} *out{argattr}) {{" - if len(struct.members) == 0: - ret += "}\n" - continue - ret += "\n" - for member in struct.members: - if isinstance(member.typ, List): - ret += f"\tout->{member.name} = *mut_host_extra;\n" - ret += f"\t*mut_host_extra += sizeof(out->{member.name}) * out->{member.typ.cnt};\n" - ret += f"\tfor (typeof(out->{member.typ.cnt}) i = 0; i < out->{member.typ.cnt}; i++)\n" - ret += f"\t\tunmarshal_{shortname(member.typ.typ)}(net_bytes, mut_net_offset, mut_host_extra, &(out->{member.name}[i]));\n" - else: - ret += f"\tunmarshal_{shortname(member.typ)}(net_bytes, mut_net_offset, mut_host_extra, &(out->{member.name}));\n" - ret += "}\n" - - # v9fs_unmarshal_msg ####################################################### - ret += """ -/* v9fs_unmarshal_msg *********************************************************/ - -int v9fs_unmarshal_msg(uint8_t *net_bytes, uint16_t *out_tag, void **out_body) { - uint32_t net_len = decode_u32le(net_bytes); - if (net_len < 7) - return -V9FS_EWRONGSIZE; - uint8_t typ = net_bytes[4]; - *out_tag = decode_u16le(&net_bytes[5]); - - uint32_t net_offset = 7; - size_t host_size; - void *host_extra; - switch (typ) { -""" - for msg in msgs: - ret += f"\tcase V9FS_TYP_{msg.name}:\n" - ret += f"\t\thost_size = sizeof({c_typename(msg)});\n" - ret += f"\t\tif (checksize_{shortname(msg)}(net_len, net_bytes, &net_offset, &host_size))\n" - ret += "\t\t\treturn -V9FS_EWRONGSIZE;\n" - ret += "\n" - ret += "\t\t*out_body = malloc(host_size);" - ret += "\n" - ret += "\t\tnet_offset = 7;\n" - ret += f"\t\thost_extra = *out_body + sizeof({c_typename(msg)});\n" - ret += f"\t\tunmarshal_{shortname(msg)}(net_bytes, &net_offset, &host_extra, *out_body);\n" - ret += "\n" - ret += "\t\tbreak;\n" - ret += """ - default: - return -V9FS_EBADTYPE; - } - return typ; -} -""" - - # marshal_* ################################################################ - ret += """ -/* marshal_* ******************************************************************/ - -static inline void marshal_1(uint8_t val, uint8_t *out_net_bytes, uint32_t *mut_net_offset) { - out_net_bytes[*mut_net_offset] = val; - *mut_net_offset += 1; -} -static inline void marshal_2(uint16_t val, uint8_t *out_net_bytes, uint32_t *mut_net_offset) { - encode_u16le(val, &out_net_bytes[*mut_net_offset]); - *mut_net_offset += 2; -} -static inline void marshal_4(uint32_t val, uint8_t *out_net_bytes, uint32_t *mut_net_offset) { - encode_u32le(val, &out_net_bytes[*mut_net_offset]); - *mut_net_offset += 4; -} -static inline void marshal_8(uint64_t val, uint8_t *out_net_bytes, uint32_t *mut_net_offset) { - encode_u64le(val, &out_net_bytes[*mut_net_offset]); - *mut_net_offset += 8; -} -""" - for struct in structs + msgs: - argattr = ' UNUSED' if len(struct.members) == 0 else '' - ret += f"static inline void marshal_{shortname(struct)}({c_typename(struct)} val{argattr}, uint8_t *out_net_bytes{argattr}, uint32_t *mut_net_offset{argattr}) {{" - if len(struct.members) == 0: - ret += "}\n" - continue - ret += "\n" - for member in struct.members: - if isinstance(member.typ, List): - ret += f"\tfor (typeof(val.{member.typ.cnt}) i = 0; i < val.{member.typ.cnt}; i++)\n" - ret += f"\t\tmarshal_{shortname(member.typ.typ)}(val.{member.name}[i], out_net_bytes, mut_net_offset);\n" - else: - ret += f"\tmarshal_{shortname(member.typ)}(val.{member.name}, out_net_bytes, mut_net_offset);\n" - ret += "}\n" - - # _v9fs_marshal_msg_* ###################################################### - ret += """ -/* _v9fs_marshal_msg_* ********************************************************/ - -""" - for msg in msgs: - ret += f"uint32_t _v9fs_marshal_{shortname(msg)}(uint16_t in_msgid, {c_typename(msg)} in_msg, uint8_t *out_buf) {{\n" - ret += "\tuint32_t offset = 4;\n" - ret += f"\tmarshal_1(V9FS_TYP_{msg.name}, out_buf, &offset);\n" - ret += "\tmarshal_2(in_msgid, out_buf, &offset);\n" - ret += f"\tmarshal_{shortname(msg)}(in_msg, out_buf, &offset);\n" - ret += "\tencode_u32le(offset, out_buf);\n" - ret += "\treturn offset;\n" - ret += "}\n" - ret += "\n" - - ############################################################################ - return ret - - -################################################################################ - -if __name__ == "__main__": - structs, msgs = parse_file("net9p_defs.txt") - with open('net9p_defs.h', 'w') as fh: - fh.write(gen_h(structs, msgs)) - with open('net9p_defs.c', 'w') as fh: - fh.write(gen_c(structs, msgs)) diff --git a/net9p_defs.txt b/net9p_defs.txt deleted file mode 100644 index c3fcc9b..0000000 --- a/net9p_defs.txt +++ /dev/null @@ -1,176 +0,0 @@ -# net9p.txt - Definitions of 9P messages -# -# Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com> -# SPDX-Licence-Identifier: AGPL-3.0-or-later - -# In the 9P protocol, each message has a type, and message types come -# in pairs (except "Rerror"); "T" and "R"; "T" messages are -# client->server requests, and "R" messages are server->client -# responses (I do not know what the Plan 9 designers intended "T" and -# "R" to stand for). The type of a message is represented by a u8 ID. -# -# This file is a defines the the ID and format of each message type, -# and is used to generate implementation code. - -# The format of each message (excluding the "size[4] msg_type[1] -# tag[2]" header) is written here as a sequence of -# "member_name[member_type]" struct members. -# -# The primitive member types types are the following single-character -# mnemonics: -# -# - 1 = u8 -# - 2 = u16le -# - 4 = u32le -# - 8 = u16le -# -# A type expression may also be [type*count]" where "type" is the -# type, and then "count" is a previously-defined integer member. -# -# We also define a few reusable compound types: - -# data (u32le `n`, then `n` bytes of data) -d = "len[8] dat[1*len]" - -# string (u16le `n`, then `n` bytes of UTF-8) -s = "len[2] utf8[1*len]" - -# qid (TODO) -q = "type[1] vers[4] path[8]" - -# stat (TODO) -stat = "stat_size[2]" - "kern_type[2]" - "kern_dev[4]" - "file_qid[q]" - "file_mode[4]" - "file_atime[4]" - "file_mtime[4]" - "file_size[8]" - "file_name[s]" - "file_owner_uid[s]" - "file_owner_gid[s]" - "file_last_modified_uid[s]" - -stat.u = stat + "file_extension[s] file_owner_n_uid[4] file_owner_n_gid[4] file_last_modified_n_uid[4]" - -# "9P2000" base protocol -# https://ericvh.github.io/9p-rfc/rfc9p2000.html -# https://github.com/ericvh/9p-rfc/blob/master/9p2000.xml -# -# But due to incompleteness of the draft RFC, the Plan 9 manual -# section-5 and the Plan 9 headers (particularly fcall.h) are a better -# references. -100 = Tversion "max_msg_size[4] version[s]" -101 = Rversion "max_msg_size[4] version[s]" -102 = Tauth "afid[4] uname[s] aname[s]" .u+="nxs_uname[4]" -103 = Rauth "aqid[q]" -104 = Tattach "fid[4] afid[4] uname[s] aname[s]" .u+="n_uname[4]" -105 = Rattach "qid[q]" -#106 = Terror "illegal" -107 = Rerror "ename[s]" .u+="errno[4]" -108 = Tflush "oldtag[2]" -109 = Rflush "" -110 = Twalk "fid[4] newfid[4] nwname[2] wname[s*nwname]" -111 = Rwalk "nwqid[2] wqid[q*nwqid]" -112 = Topen "fid[4] mode[1]" -113 = Ropen "qid[q] iounit[4]" -114 = Tcreate "fid[4] name[s] perm[4] mode[1]" .u+="extension[s]" -115 = Rcreate "qid[q] iounit[4]" -116 = Tread "fid[4] offset[8] count[4]" # base="returns a sequence of "entries[stat*cnt]""; .L=can't read directories -117 = Rread "data[d]" -118 = Twrite "fid[4] offset[8] data[d]" -119 = Rwrite "count[4]" -120 = Tclunk "fid[4]" -121 = Rclunk "" -122 = Tremove "fid[4]" -123 = Rremove "" -124 = Tstat "fid[4]" -125 = Rstat "stat[stat]" -126 = Twstat "fid[4] stat[stat]" -127 = Rwstat "" - -# "9P2000.u" Unix extension -# https://ericvh.github.io/9p-rfc/rfc9p2000.u.html -# https://github.com/ericvh/9p-rfc/blob/master/9p2000.u.xml -# -# (no new message type numbers) - -# "9P2000.L" Linux extension -# https://github.com/chaos/diod/blob/master/protocol.md -#6 = Tlerror "illegal" # analogous to 106=Terror -7 = Rlerror "ecode[4]" # analogous to 107=Rerror -8 = Tstatfs "TODO" -9 = Rstatfs "TODO" -12 = Tlopen "TODO" # analogous to 112=Topen -13 = Rlopen "TODO" # analogous to 113=Ropen -14 = Tlcreate "TODO" # analogous to 114=Tcreate -15 = Rlcreate "TODO" # analogous to 115=Rcreate -16 = Tsymlink "TODO" -17 = Rsymlink "TODO" -18 = Tmknod "TODO" -19 = Rmknod "TODO" -20 = Trename "TODO" -21 = Rrename "TODO" -22 = Treadlink "TODO" -23 = Rreadlink "TODO" -24 = Tgetattr "TODO" -25 = Rgetattr "TODO" -26 = Tsetattr "TODO" -27 = Rsetattr "TODO" -#... -30 = Txattrwalk "TODO" -31 = Rxattrwalk "TODO" -32 = Txattrcreate "TODO" -33 = Rxattrcreate "TODO" -#... -40 = Treaddir "TODO" -41 = Rreaddir "TODO" -#... -50 = Tfsync "TODO" -51 = Rfsync "TODO" -52 = Tlock "TODO" -53 = Rlock "TODO" -54 = Tgetlock "TODO" -55 = Rgetlock "TODO" -# ... -70 = Tlink "TODO" -71 = Rlink "TODO" -72 = Tmkdir "TODO" -73 = Tmkdir "TODO" -74 = Trenameat "TODO" -75 = Rrenameat "TODO" -76 = Tunlinkat "TODO" -77 = Runlinkat "TODO" - -# "9P2000.e" Erlang extension -# https://erlangonxen.org/more/9p2000e -# https://github.com/cloudozer/ling/blob/master/doc/9p2000e.md -# -# TODO - -# qid.types -QTDIR = 1<<7 -QTAPPEND = 1<<6 -QTEXCL = 1<<5 -QTMOUNT = 1<<4 # been around forever, but undocumented? -QTAUTH = 1<<3 -QTTMP = 1<<2 # added to Plan 9 2003-12 -QTSYMLINK = 1<<1 # .u -QTFILE = 1<<0 - -DMDIR = 1<<31 -DMAPPEND = 1<<30 -DMEXCL = 1<<29 -DMMOUNT = 1<<28 -DMAUTH = 1<<27 -DMTMP = 1<<26 -#25 -#24 -DMDEVICE = 1<<23 # .u -#22 -DMNAMEDPIPE = 1<<21 # .u -DMSOCKET = 1<<20 # .u -DMSETUID = 1<<19 # .u -DMSETGID = 1<<18 # .u - |