summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.editorconfig4
-rw-r--r--.gitignore4
-rw-r--r--3rd-party/.gitignore1
-rw-r--r--3rd-party/linux-errno.txt132
-rw-r--r--9p/.editorconfig3
-rw-r--r--9p/.gitignore5
-rw-r--r--9p/9P2000.L.txt56
-rw-r--r--9p/9P2000.e.txt11
-rw-r--r--9p/9P2000.txt82
-rw-r--r--9p/9P2000.u.txt20
-rwxr-xr-x9p/generate541
-rwxr-xr-x9p/linux-errno.h.gen33
-rw-r--r--9p/misc.txt24
-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--Makefile30
-rwxr-xr-xnet9p_defs.gen496
-rw-r--r--net9p_defs.txt176
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
diff --git a/.gitignore b/.gitignore
index 5c842a5..1891c8c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -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
diff --git a/net9p.c b/9p/srv.c
index f7c8195..f7c8195 100644
--- a/net9p.c
+++ b/9p/srv.c
diff --git a/net9p.h b/9p/srv.h
index f28a525..f28a525 100644
--- a/net9p.h
+++ b/9p/srv.h
diff --git a/Makefile b/Makefile
index 57583ae..fd80695 100644
--- a/Makefile
+++ b/Makefile
@@ -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
-