diff options
author | Luke T. Shumaker <lukeshu@lukeshu.com> | 2024-09-26 19:36:54 -0600 |
---|---|---|
committer | Luke T. Shumaker <lukeshu@lukeshu.com> | 2024-09-26 19:36:54 -0600 |
commit | 71e1a86a033c380f85dd300d788af63bfef25bab (patch) | |
tree | 07aa53d5a933ba51535a78972edbfe0cd95a31c5 /9p | |
parent | f5da707e77ee954b12f3c961012e4f40fa4e1bd3 (diff) |
wip reorg
Diffstat (limited to '9p')
-rw-r--r-- | 9p/.editorconfig | 3 | ||||
-rw-r--r-- | 9p/.gitignore | 2 | ||||
-rw-r--r-- | 9p/9P2000.L.txt | 56 | ||||
-rw-r--r-- | 9p/9P2000.e.txt | 16 | ||||
-rw-r--r-- | 9p/9P2000.txt | 82 | ||||
-rw-r--r-- | 9p/9P2000.u.txt | 20 | ||||
-rw-r--r-- | 9p/9p.h | 15 | ||||
-rw-r--r-- | 9p/defs.c | 89 | ||||
-rwxr-xr-x | 9p/defs.gen | 456 | ||||
-rw-r--r-- | 9p/defs.h | 74 | ||||
-rw-r--r-- | 9p/internal.h | 139 | ||||
-rwxr-xr-x | 9p/linux-errno.h.gen | 34 | ||||
-rw-r--r-- | 9p/misc.txt | 24 | ||||
-rw-r--r-- | 9p/srv.c | 194 | ||||
-rw-r--r-- | 9p/srv.h | 16 |
15 files changed, 0 insertions, 1220 deletions
diff --git a/9p/.editorconfig b/9p/.editorconfig deleted file mode 100644 index 3f04564..0000000 --- a/9p/.editorconfig +++ /dev/null @@ -1,3 +0,0 @@ -[{defs.gen,linux-errno.h.gen}] -indent_style = space -indent_size = 4 diff --git a/9p/.gitignore b/9p/.gitignore deleted file mode 100644 index 667e81c..0000000 --- a/9p/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/defs-* -/linux-errno.h diff --git a/9p/9P2000.L.txt b/9p/9P2000.L.txt deleted file mode 100644 index 71ab171..0000000 --- a/9p/9P2000.L.txt +++ /dev/null @@ -1,56 +0,0 @@ -# 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 deleted file mode 100644 index 22f056e..0000000 --- a/9p/9P2000.e.txt +++ /dev/null @@ -1,16 +0,0 @@ -# 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" - -150/Tsession = "key[8]" -151/Rsession = "" -152/Tsread = "fid[4] nwname[2] nwname*(wname[s])" -153/Rsread = "data[d]" -154/Tswrite = "Tswrite tag[2] fid[4] nwname[2] nwname*(wname[s]) data[d]" -155/Rswrite = "count[4]" diff --git a/9p/9P2000.txt b/9p/9P2000.txt deleted file mode 100644 index 5f93cdf..0000000 --- a/9p/9P2000.txt +++ /dev/null @@ -1,82 +0,0 @@ -# 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[4] 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 deleted file mode 100644 index 60d2b11..0000000 --- a/9p/9P2000.u.txt +++ /dev/null @@ -1,20 +0,0 @@ -# 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 += "n_uname[4]" - -Rerror += "errno[4]" diff --git a/9p/9p.h b/9p/9p.h deleted file mode 100644 index 63fbd04..0000000 --- a/9p/9p.h +++ /dev/null @@ -1,15 +0,0 @@ -/* 9p/9p.h - TODO - * - * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com> - * SPDX-Licence-Identifier: AGPL-3.0-or-later - */ - -#include "9p/linux-errno.h" -#include "9p/defs.h" -#include "9p/defs-9P2000.h" -/*#include "9p/defs-9P2000.u.h"*/ - -#define P9_TYPECODE_FOR_CTYPE(msg) _Generic((in_msg) \ - _P9_TYPECODE_FOR_CTYPE_9P2000(msg) \ - /* _P9_TYPECODE_FOR_CTYPE_9P2000u(msg) */ \ - ) diff --git a/9p/defs.c b/9p/defs.c deleted file mode 100644 index 886a0c1..0000000 --- a/9p/defs.c +++ /dev/null @@ -1,89 +0,0 @@ -/* 9p/defs.c - TODO - * - * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com> - * SPDX-Licence-Identifier: AGPL-3.0-or-later - */ - -#include <inttypes.h> /* for PRIu{n} */ -#include <stdarg.h> /* for va_* */ -#include <stdio.h> /* for vsnprintf() */ -#include <string.h> /* for strncpy() */ - -#include "9p/defs.h" -#include "9p/linux-errno.h" -#include "9p/internal.h" - -static struct version *versions[_P9_VER_CNT] = { - [P9_VER_9P2000] = &version_9P2000, - /* [P9_VER_9P2000u] = &version_9P2000u, */ -}; - -int p9_error(struct p9_ctx *ctx, uint32_t linux_errno, char const *msg) { - strncpy(ctx->err_msg, msg, sizeof(ctx->err_msg)); - ctx->err_msg[sizeof(ctx->err_msg)-1] = '\0'; - ctx->err_num = linux_errno; - return -1; -} - -int p9_errorf(struct p9_ctx *ctx, uint32_t linux_errno, char const *fmt, ...) { - int n; - va_list args; - - va_start(args, fmt); - n = vsnprintf(ctx->err_msg, sizeof(ctx->err_msg), fmt, args); - va_end(args); - if ((size_t)(n+1) < sizeof(ctx->err_msg)) - memset(&ctx->err_msg[n+1], 0, sizeof(ctx->err_msg)-(n+1)); - - ctx->err_num = linux_errno; - - return -1; -} - -size_t p9_unmarshal_size(struct p9_ctx *ctx, uint8_t *net_bytes) { - /* Header */ - uint32_t net_len = decode_u32le(net_bytes); - if (net_len < 7) - return p9_error(ctx, LINUX_EBADMSG, "message is too short"); - uint8_t typ = net_bytes[4]; - uint32_t net_offset = 7; - - /* Body */ - if (!versions[ctx->version]->msgs[typ].unmarshal_extrasize) - return p9_errorf(ctx, LINUX_EOPNOTSUPP, "unknown message type %"PRIu8, typ); - size_t host_size = versions[ctx->version]->msgs[typ].unmarshal_basesize; - if (versions[ctx->version]->msgs[typ].unmarshal_extrasize(net_len, net_bytes, &net_offset, &host_size)) - return p9_error(ctx, LINUX_EBADMSG, "message is too short for content"); - - return host_size; -} - -uint8_t p9_unmarshal(struct p9_ctx *ctx, uint8_t *net_bytes, uint16_t *out_tag, void *out_body) { - /* Header */ - uint8_t typ = net_bytes[4]; - *out_tag = decode_u16le(&net_bytes[5]); - uint32_t net_offset = 7; - - /* Body */ - void *host_extra = out_body + versions[ctx->version]->msgs[typ].unmarshal_basesize; - if (versions[ctx->version]->msgs[typ].unmarshal(net_bytes, &net_offset, &host_extra, out_body)) - return p9_error(ctx, LINUX_EBADMSG, "message contains invalid UTF-8"); - - return typ; -} - -uint32_t _p9_marshal(struct p9_ctx *ctx, uint8_t typ, uint16_t msgid, void *body, uint8_t *out_bytes) { - /* Header */ - out_bytes[4] = typ; - encode_u16le(msgid, &out_bytes[5]); - uint32_t net_offset = 7; - - /* Body */ - if (versions[ctx->version]->msgs[typ].marshal(ctx, body, out_bytes, &net_offset)) - return 0; - - /* Header, again */ - encode_u32le(net_offset, out_bytes); - - return net_offset; -} diff --git a/9p/defs.gen b/9p/defs.gen deleted file mode 100755 index 1a8dc69..0000000 --- a/9p/defs.gen +++ /dev/null @@ -1,456 +0,0 @@ -#!/usr/bin/env python -# 9p/defs.gen - Generate C marshalers/unmarshalers for a .txt file -# defining a 9P protocol variant. -# -# Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com> -# SPDX-Licence-Identifier: AGPL-3.0-or-later - -import enum -import os.path -import re - -PROGRAM = "./9p/defs.gen" - -# Parse the *.txt ############################################################## - - -class Atom(enum.Enum): - u8 = 1 - u16 = 2 - u32 = 4 - u64 = 8 - - @property - def name(self) -> str: - return str(self.value) - - @property - def static_size(self) -> int: - return self.value - - -# `msgid/structname = "member1 member2..."` -# `structname = "member1 member2..."` -# `structname += "member1 member2..."` -class Struct: - msgid: int | None = None - name: str - members: list["Member"] - - @property - def static_size(self) -> int | None: - size = 0 - for member in self.members: - msize = member.static_size - if msize is None: - return None - size += msize - return size - - -# `cnt*(name[typ])` -# the `cnt*(...)` wrapper is optional -class Member: - cnt: str | None = None - name: str - typ: Atom | Struct - - @property - def static_size(self) -> int | None: - if self.cnt: - return None - return self.typ.static_size - - -re_membername = "(?:[a-zA-Z_][a-zA-Z_0-9]*)" -re_memberspec = ( - f"(?:(?P<cnt>{re_membername})\\*\\()?(?P<name>{re_membername})\\[(?P<typ>.*)\\]\\)?" -) - - -def parse_members( - env: dict[str, Atom | Struct], existing: list[Member], specs: str -) -> list[Member]: - ret = existing - for spec in specs.split(): - m = re.fullmatch(re_memberspec, spec) - if not m: - raise SyntaxError(f"invalid member spec {repr(spec)}") - - member = Member() - - member.name = m.group("name") - if any(x.name == member.name for x in ret): - raise ValueError(f"duplicate member name {repr(member.name)}") - - if m.group("typ") not in env: - raise NameError(f"Unknown type {repr(m.group(2))}") - member.typ = env[m.group("typ")] - - if cnt := m.group("cnt"): - if len(ret) == 0 or ret[-1].name != cnt: - raise ValueError(f"list count must be previous item: {repr(cnt)}") - if not isinstance(ret[-1].typ, Atom): - raise ValueError(f"list count must be an integer type: {repr(cnt)}") - member.cnt = cnt - - ret += [member] - return ret - - -re_version = r'version\s+"(?P<version>[^"]+)"' -re_structspec = ( - r'(?:(?P<msgid>[0-9]+)/)?(?P<name>\S+)\s*(?P<op>\+?=)\s*"(?P<members>[^"]*)"' -) -re_structspec_cont = r'"(?P<members>[^"]*)"' - - -def parse_file(filename: str) -> tuple[str, list[Struct]]: - version: str | None = None - env: dict[str, Atom | Struct] = { - "1": Atom.u8, - "2": Atom.u16, - "4": Atom.u32, - "8": Atom.u64, - } - with open(filename, "r") as fh: - prev: Struct | None = None - for line in fh: - line = line.split("#", 1)[0].strip() - if not line: - continue - if m := re.fullmatch(re_version, line): - if version: - raise SyntaxError("must have exactly 1 version line") - version = m.group("version") - elif m := re.fullmatch(re_structspec, line): - if m.group("op") == "+=" and m.group("msgid"): - raise SyntaxError("cannot += to a message that is not yet defined") - match m.group("op"): - case "=": - struct = Struct() - if m.group("msgid"): - struct.msgid = int(m.group("msgid")) - struct.name = m.group("name") - struct.members = parse_members(env, [], m.group("members")) - env[struct.name] = struct - prev = struct - case "+=": - if m.group("name") not in env: - raise NameError(f"Unknown type {repr(m.group('name'))}") - _struct = env[m.group("name")] - if not isinstance(_struct, Struct): - raise NameError( - f"Type {repr(m.group('name'))} is not a struct" - ) - struct = _struct - struct.members = parse_members( - env, struct.members, m.group("members") - ) - prev = struct - elif m := re.fullmatch(re_structspec_cont, line): - if not prev: - raise SyntaxError("continuation line must come after a struct line") - prev.members = parse_members(env, prev.members, m.group("members")) - else: - raise SyntaxError(f"invalid line {repr(line)}") - if not version: - raise SyntaxError("must have exactly 1 version line") - structs = [x for x in env.values() if isinstance(x, Struct)] - return version, structs - - -# Generate C ################################################################### - - -def c_typename(idprefix: str, typ: Atom | Struct) -> str: - match typ: - case Atom(): - return f"uint{typ.value*8}_t" - case Struct(): - if typ.msgid is not None: - return f"struct {idprefix}msg_{typ.name}" - return f"struct {idprefix}{typ.name}" - case _: - raise ValueError(f"not a type: {typ.__class__.__name__}") - - -def gen_h(txtname: str, idprefix: str, structs: list[Struct]) -> str: - guard = ( - f"_{txtname.replace('.txt', '.h').upper().replace('/', '_').replace('.', '_')}_" - ) - ret = f"""/* Generated by `{PROGRAM} {txtname}`. DO NOT EDIT! */ - -#ifndef {guard} -#define {guard} - -#define {idprefix.upper()}MIN_MSGLEN 7 -""" - ret += """ -/* non-message structs ********************************************************/ - -""" - for struct in structs: - if struct.msgid is not None: - continue - ret += c_typename(idprefix, struct) + " {\n" - typewidth = max( - len(c_typename(idprefix, member.typ)) for member in struct.members - ) - for member in struct.members: - ret += f"\t{c_typename(idprefix, member.typ).ljust(typewidth)} {'*' if member.cnt else ' '}{member.name};\n" - ret += "};\n" - - ret += """ -/* message types **************************************************************/ - -""" - ret += f"enum {idprefix}msg_type {{ /* uint8_t */\n" - namewidth = max(len(msg.name) for msg in structs if msg.msgid is not None) - for msg in structs: - if msg.msgid is None: - continue - ret += f"\t{idprefix.upper()}TYP_{msg.name.ljust(namewidth)} = {msg.msgid},\n" - ret += "};\n" - - ret += """ -/* message structs ************************************************************/ - -""" - for msg in structs: - if msg.msgid is None: - continue - if not msg.members: - ret += c_typename(idprefix, msg) + " {};\n" - continue - ret += c_typename(idprefix, msg) + " {\n" - typewidth = max(len(c_typename(idprefix, member.typ)) for member in msg.members) - for member in msg.members: - ret += f"\t{c_typename(idprefix, member.typ).ljust(typewidth)} {'*' if member.cnt else ' '}{member.name};\n" - ret += "};\n" - - ret += f""" -/* tables *********************************************************************/ - -#define _P9_TYPECODE_FOR_CTYPE(msg) """ - for msg in structs: - if msg.msgid is None: - continue - ret += f", \\\n\t\t{c_typename(idprefix, msg)}: {idprefix.upper()}TYP_{msg.name}" - ret += "\n" - - ret += "\n" - ret += f"#endif /* {guard} */\n" - return ret - - -def gen_c(txtname: str, idprefix: str, structs: list[Struct]) -> str: - txtdir, txtbase = os.path.split(txtname) - header = os.path.join(txtdir, "defs-" + txtbase.replace(".txt", ".h")) - ret = f"""/* Generated by `{PROGRAM} {txtname}`. DO NOT EDIT! */ - -#include <stdbool.h> -#include <stdint.h> -#include <stdlib.h> /* for malloc() */ - -#include "{header}" -#include "9p/internal.h" -""" - - def used(arg: str) -> str: - return arg - - def unused(arg: str) -> str: - return f"UNUSED({arg})" - - # checksize_* ############################################################## - ret += """ -/* checksize_* ****************************************************************/ - -static inline bool _checksize_list(size_t cnt, _checksize_fn_t fn, size_t host_size, - uint32_t net_len, uint8_t *net_bytes, uint32_t *mut_net_offset, size_t *mut_host_extra) { - for (size_t i = 0; i < cnt; i++) - if (__builtin_add_overflow(*mut_host_extra, host_size, mut_host_extra) - || fn(net_len, net_bytes, mut_net_offset, mut_host_extra)) - return true; - return false; -} - -static inline bool checksize_1(uint32_t net_len, uint8_t *UNUSED(net_bytes), uint32_t *mut_net_offset, size_t *UNUSED(mut_host_extra)) { - return __builtin_add_overflow(*mut_net_offset, 1, mut_net_offset) || net_len < *mut_net_offset; -} -static inline bool checksize_2(uint32_t net_len, uint8_t *UNUSED(net_bytes), uint32_t *mut_net_offset, size_t *UNUSED(mut_host_extra)) { - return __builtin_add_overflow(*mut_net_offset, 2, mut_net_offset) || net_len < *mut_net_offset; -} -static inline bool checksize_4(uint32_t net_len, uint8_t *UNUSED(net_bytes), uint32_t *mut_net_offset, size_t *UNUSED(mut_host_extra)) { - return __builtin_add_overflow(*mut_net_offset, 4, mut_net_offset) || net_len < *mut_net_offset; -} -static inline bool checksize_8(uint32_t net_len, uint8_t *UNUSED(net_bytes), uint32_t *mut_net_offset, size_t *UNUSED(mut_host_extra)) { - return __builtin_add_overflow(*mut_net_offset, 8, mut_net_offset) || net_len < *mut_net_offset; -} - -""" - for struct in structs: - inline = ' inline' if struct.msgid is None else '' - argfn = used if struct.members else unused - ret += f"static{inline} bool checksize_{struct.name}(uint32_t {argfn('net_len')}, uint8_t *{argfn('net_bytes')}, uint32_t *{argfn('mut_net_offset')}, size_t *{argfn('mut_host_extra')}) {{" - if len(struct.members) == 0: - ret += "\n\treturn false;\n" - ret += "}\n" - continue - prefix0 = "\treturn " - prefix1 = "\t || " - prefix2 = "\t " - prefix = prefix0 - prev_size: int | None = None - for member in struct.members: - if member.cnt is not None: - assert prev_size - ret += f"\n{prefix }_checksize_list(decode_u{prev_size*8}le(&net_bytes[(*mut_net_offset)-{prev_size}]), checksize_{member.typ.name}, sizeof({c_typename(idprefix, member.typ)})," - ret += f"\n{prefix2} net_len, net_bytes, mut_net_offset, mut_host_extra)" - else: - ret += f"\n{prefix}checksize_{member.typ.name}(net_len, net_bytes, mut_net_offset, mut_host_extra)" - prefix = prefix1 - prev_size = member.static_size - if struct.name == "s": - ret += ( - f"\n{prefix}__builtin_add_overflow(*mut_host_extra, 1, mut_host_extra)" - ) - ret += ";\n}\n" - - # unmarshal_* ############################################################## - ret += """ -/* unmarshal_* ****************************************************************/ -/* checksize_XXX() should be called before unmarshal_XXX(). */ - -static inline bool unmarshal_1(uint8_t *net_bytes, uint32_t *mut_net_offset, void **UNUSED(mut_host_extra), uint8_t *out) { - *out = decode_u8le(&net_bytes[*mut_net_offset]); - *mut_net_offset += 1; - return false; -} -static inline bool unmarshal_2(uint8_t *net_bytes, uint32_t *mut_net_offset, void **UNUSED(mut_host_extra), uint16_t *out) { - *out = decode_u16le(&net_bytes[*mut_net_offset]); - *mut_net_offset += 2; - return false; -} -static inline bool unmarshal_4(uint8_t *net_bytes, uint32_t *mut_net_offset, void **UNUSED(mut_host_extra), uint32_t *out) { - *out = decode_u32le(&net_bytes[*mut_net_offset]); - *mut_net_offset += 4; - return false; -} -static inline bool unmarshal_8(uint8_t *net_bytes, uint32_t *mut_net_offset, void **UNUSED(mut_host_extra), uint64_t *out) { - *out = decode_u64le(&net_bytes[*mut_net_offset]); - *mut_net_offset += 8; - return false; -} - -""" - for struct in structs: - argfn = used if struct.members else unused - ret += f"static inline bool unmarshal_{struct.name}(uint8_t *{argfn('net_bytes')}, uint32_t *{argfn('mut_net_offset')}, void **{argfn('mut_host_extra')}, {c_typename(idprefix, struct)} *{argfn('out')}) {{\n" - for member in struct.members: - if member.cnt: - ret += f"\tout->{member.name} = *mut_host_extra;\n" - ret += f"\t*mut_host_extra += sizeof(*out->{member.name}) * out->{member.cnt};\n" - ret += f"\tfor (typeof(out->{member.cnt}) i = 0; i < out->{member.cnt}; i++)\n" - ret += f"\t\tif (unmarshal_{member.typ.name}(net_bytes, mut_net_offset, mut_host_extra, &(out->{member.name}[i])))\n" - ret += f"\t\t\treturn true;\n" - if struct.name == "s": - ret += f"\tif (!is_valid_utf8_without_nul(out->{member.name}, out->{member.cnt}))\n" - ret += f"\t\treturn true;\n" - ret += f"\tout->{member.name}[out->{member.cnt}] = '\\0';\n" - else: - ret += f"\tif (unmarshal_{member.typ.name}(net_bytes, mut_net_offset, mut_host_extra, &(out->{member.name})))\n" - ret += f"\t\treturn true;\n" - ret += "\treturn false;\n" - ret += "}\n" - - # marshal_* ################################################################ - ret += """ -/* marshal_* ******************************************************************/ - -static inline bool marshal_1(struct p9_ctx *ctx, uint8_t val, uint8_t *out_net_bytes, uint32_t *mut_net_offset) { - if (*mut_net_offset + 1 > ctx->max_msg_size) - return true; - out_net_bytes[*mut_net_offset] = val; - *mut_net_offset += 1; - return false; -} -static inline bool marshal_2(struct p9_ctx *ctx, uint16_t val, uint8_t *out_net_bytes, uint32_t *mut_net_offset) { - if (*mut_net_offset + 2 > ctx->max_msg_size) - return true; - encode_u16le(val, &out_net_bytes[*mut_net_offset]); - *mut_net_offset += 2; - return false; -} -static inline bool marshal_4(struct p9_ctx *ctx, uint32_t val, uint8_t *out_net_bytes, uint32_t *mut_net_offset) { - if (*mut_net_offset + 4 > ctx->max_msg_size) - return true; - encode_u32le(val, &out_net_bytes[*mut_net_offset]); - *mut_net_offset += 4; - return false; -} -static inline bool marshal_8(struct p9_ctx *ctx, uint64_t val, uint8_t *out_net_bytes, uint32_t *mut_net_offset) { - if (*mut_net_offset + 8 > ctx->max_msg_size) - return true; - encode_u64le(val, &out_net_bytes[*mut_net_offset]); - *mut_net_offset += 8; - return false; -} -""" - for struct in structs: - argfn = used if struct.members else unused - ret += f"static inline bool marshal_{struct.name}(struct p9_ctx *{argfn('ctx')}, {c_typename(idprefix, struct)} {argfn('val')}, uint8_t *{argfn('out_net_bytes')}, uint32_t *{argfn('mut_net_offset')}) {{\n" - for member in struct.members: - if member.cnt: - ret += f"\tfor (typeof(val.{member.cnt}) i = 0; i < val.{member.cnt}; i++)\n" - ret += f"\t\tif (marshal_{member.typ.name}(ctx, val.{member.name}[i], out_net_bytes, mut_net_offset))\n" - ret += f"\t\t\treturn true;\n" - else: - ret += f"\tif (marshal_{member.typ.name}(ctx, val.{member.name}, out_net_bytes, mut_net_offset))\n" - ret += f"\t\treturn true;\n" - ret += "\treturn false;\n" - ret += "}\n" - - # tables ################################################################### - ret += """ -/* tables *********************************************************************/ - -""" - for msg in structs: - if msg.msgid is None: - continue - ret += f"static bool _unmarshal_{msg.name}(uint8_t *net_bytes, uint32_t *mut_net_offset, void **mut_host_extra, void *out) {{ return unmarshal_{msg.name}(net_bytes, mut_net_offset, mut_host_extra, ({c_typename(idprefix, msg)} *)out); }}\n" - for msg in structs: - if msg.msgid is None: - continue - ret += f"static bool _marshal_{msg.name}(struct p9_ctx *ctx, void *val, uint8_t *out_net_bytes, uint32_t *mut_net_offset) {{ return marshal_{msg.name}(ctx, *(({c_typename(idprefix, msg)} *)val), out_net_bytes, mut_net_offset); }}\n" - ret += "struct version version_9P2000 = {\n" - ret += "\t.msgs = {\n" - for msg in structs: - if msg.msgid is None: - continue - ret += f"\t\t[{idprefix.upper()}TYP_{msg.name}] = {{ .unmarshal_basesize=sizeof({c_typename(idprefix, msg)}), .unmarshal_extrasize=checksize_{msg.name}, .unmarshal=_unmarshal_{msg.name}, .marshal=_marshal_{msg.name} }},\n" - ret += "\t},\n" - ret += "};\n" - - ############################################################################ - return ret - - -################################################################################ - -if __name__ == "__main__": - import sys - - for txtname in sys.argv[1:]: - txtdir, txtbase = os.path.split(txtname) - version, structs = parse_file(txtname) - with open( - os.path.join(txtdir, "defs-" + txtbase.replace(".txt", ".h")), "w" - ) as fh: - fh.write(gen_h(txtname, "p9_", structs)) - with open( - os.path.join(txtdir, "defs-" + txtbase.replace(".txt", ".c")), "w" - ) as fh: - fh.write(gen_c(txtname, "p9_", structs)) diff --git a/9p/defs.h b/9p/defs.h deleted file mode 100644 index 907cdde..0000000 --- a/9p/defs.h +++ /dev/null @@ -1,74 +0,0 @@ -/* 9p/defs.h - TODO - * - * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com> - * SPDX-Licence-Identifier: AGPL-3.0-or-later - */ - -#ifndef _9P_DEFS_H_ -#define _9P_DEFS_H_ - -#include <stdint.h> - -#define P9_NOTAG ((uint16_t)~0U) -#define P9_NOFID ((uint32_t)~0U) - -enum p9_version { - P9_VER_UNINITIALIZED, - /* P9_VER_9P1, */ - P9_VER_9P2000, - /*P9_VER_9P2000_u,*/ - /*P9_VER_9P2000_L,*/ - /*P9_VER_9P2000_e,*/ - _P9_VER_CNT, -}; - -struct p9_ctx; - -enum p9_version p9_ctx_version(p9_ctx *); - -/** Write an static error into ctx, return -1. */ -int p9_error(struct p9_ctx *ctx, uint32_t linux_errno, char const *msg); -/** Write a printf-style error into ctx, return -1. */ -int p9_errorf(struct p9_ctx *ctx, uint32_t linux_errno, char const *fmt, ...); - -/** - * Return how much space the message at net_bytes will take when - * unmarshaled. This number may be larger than net_bytes due to (1) - * struct padding, (2) nul-terminator byes for strings. - * - * Emits an error (return -1, set ctx->err_num and ctx->err_msg) if - * either the message type is unknown or if net_bytes is too short for - * that message type. - * - * @param net_bytes : the complete request, starting with the "size[4]" - * @return required size, or -1 on error - */ -size_t p9_unmarshal_size(struct p9_ctx *ctx, uint8_t *net_bytes); - -/** - * Unmarshal the 9P message `net_bytes` into the C struct `out_body`. - * - * Emits an error (return 0, set ctx->err_num and ctx->err_msg) if a - * string contains invalid UTF-8 or a nul-byte. - * - * @param net_bytes : the complete message, starting with the "size[4]" - * @param out_tag : the message-ID tag - * @param out_body : the message body, must be at least p9_unmarshal_size() bytes - * @return the message type, or -1 on error - */ -uint8_t p9_unmarshal(struct p9_ctx *ctx, uint8_t *net_bytes, uint16_t *out_tag, void *out_body); - -/** - * Marshal a `struct p9_msg_{type}` structure into a byte-array. - * - * @param struct p9_ctx *ctx : a request context - * @param uint16_t msgid : the message-ID tag - * @param struct p9_msg_{type} msg : the message to encode - * - * @param uint8_t *out_bytes : the buffer to encode to, must be at be at least ctx->max_msg_size bytes - * @return uint32_t : the encoded length, or -1 on error - */ -#define p9_marshal(ctx, msgid, msg, out_bytes) _p9_marshal(ctx, P9_TYPECODE_FOR_CTYPE(msg), msgid, &(msg), out_bytes) -uint32_t _p9_marshal(struct p9_ctx *ctx, uint8_t typ, uint16_t msgid, void *body, uint8_t *out_bytes); - -#endif /* _9P_DEFS_H_ */ diff --git a/9p/internal.h b/9p/internal.h deleted file mode 100644 index 61977d4..0000000 --- a/9p/internal.h +++ /dev/null @@ -1,139 +0,0 @@ -/* 9p/internal.h - TODO - * - * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com> - * SPDX-Licence-Identifier: AGPL-3.0-or-later - */ - -#ifndef _9P_INTERNAL_H_ -#define _9P_INTERNAL_H_ - -#include <assert.h> -#include <stdint.h> -#include <stdbool.h> - -#include "9p/defs.h" - -#define USE_CONFIG_9P -#include "config.h" -static_assert(CONFIG_9P_MAX_ERR_SIZE <= UINT16_MAX); - -/* C language *****************************************************************/ - -#define UNUSED(name) /* name __attribute__ ((unused)) */ - -/* types **********************************************************************/ - -struct p9_ctx { - enum p9_version version; - uint32_t max_msg_size; - uint32_t Rerror_overhead; - - uint32_t err_num; - char err_msg[CONFIG_9P_MAX_ERR_SIZE]; -}; - -static inline enum p9_version p9_ctx_version(p9_ctx *ctx) { - assert(ctx); - return ctx->version; -} - -/* vtables ********************************************************************/ - -typedef bool (*_checksize_fn_t)(uint32_t net_len, uint8_t *net_bytes, uint32_t *mut_net_offset, size_t *mut_host_extra); -typedef bool (*_unmarshal_fn_t)(uint8_t *net_bytes, uint32_t *mut_net_offset, void **mut_host_extra, void *out); -typedef bool (*_marshal_fn_t)(struct p9_ctx *ctx, void *val, uint8_t *out_net_bytes, uint32_t *mut_net_offset); - -struct msg_vtable { - size_t unmarshal_basesize; - _checksize_fn_t unmarshal_extrasize; - _unmarshal_fn_t unmarshal; - _marshal_fn_t marshal; -}; - -struct version { - struct msg_vtable msgs[0xFF]; -}; - -extern struct version version_9P2000; -/*extern struct version version_9P2000u; */ - -/* unmarshal utilities ********************************************************/ - -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 bool _is_valid_utf8(uint8_t *str, size_t len, bool forbid_nul) { - uint32_t ch; - uint8_t chlen; - assert(str); - for (size_t pos = 0; pos < len;) { - if ((str[pos] & 0b10000000) == 0b00000000) { ch = str[pos] & 0b01111111; chlen = 1; } - else if ((str[pos] & 0b11100000) == 0b11000000) { ch = str[pos] & 0b00011111; chlen = 2; } - else if ((str[pos] & 0b11110000) == 0b11100000) { ch = str[pos] & 0b00001111; chlen = 3; } - else if ((str[pos] & 0b11111000) == 0b11110000) { ch = str[pos] & 0b00000111; chlen = 4; } - else return false; - if ((ch == 0 && (chlen != 1 || forbid_nul)) || pos + chlen > len) return false; - for (uint8_t i = 1; i < chlen; i++) { - if ((str[pos+i] & 0b11000000) != 0b10000000) return false; - ch = (ch << 6) | (str[pos+i] & 0b00111111); - } - if (ch > 0x10FFFF) return false; - pos += chlen; - } - return true; -} - -#define is_valid_utf8(str, len) _is_valid_utf8(str, len, false) -#define is_valid_utf8_without_nul(str, len) _is_valid_utf8(str, len, true) - -/* marshal utilities **********************************************************/ - -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); -} - -#endif /* _9P_INTERNAL_H_ */ diff --git a/9p/linux-errno.h.gen b/9p/linux-errno.h.gen deleted file mode 100755 index b896384..0000000 --- a/9p/linux-errno.h.gen +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/env python - - -def print_errnos(txtlists: list[str]) -> None: - print( - f"/* 9p/linux-errno.h - Generated by `./9p/linux-errno.h.gen {' '.join(txtlists)}`. DO NOT EDIT! */" - ) - errnos: dict[str, tuple[int, str]] = {} - 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, desc = line.split(maxsplit=2) - num = int(_num) - desc = desc.strip() - errnos[name] = (num, desc) - print() - print("#ifndef _9P_LINUX_ERRNO_H_") - print("#define _9P_LINUX_ERRNO_H_") - print() - namelen = max(len(name) for name in errnos.keys()) - numlen = max(len(str(num)) for (num, desc) in errnos.values()) - for name in errnos: - print(f"#define LINUX_{name.ljust(namelen)} {str(errnos[name][0]).rjust(numlen)} /* {errnos[name][1]} */") - 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 deleted file mode 100644 index 93aa304..0000000 --- a/9p/misc.txt +++ /dev/null @@ -1,24 +0,0 @@ -# qid.types -QTDIR = 1<<7 -QTAPPEND = 1<<6 -QTEXCL = 1<<5 -QTMOUNT = 1<<4 # been around forever, but undocumented? -QTAUTH = 1<<3 -QTTMP = 1<<2 # added to Plan 9 2003-12 -QTSYMLINK = 1<<1 # .u -QTFILE = 1<<0 - -DMDIR = 1<<31 -DMAPPEND = 1<<30 -DMEXCL = 1<<29 -DMMOUNT = 1<<28 -DMAUTH = 1<<27 -DMTMP = 1<<26 -# = 1<<25 -# = 1<<24 -DMDEVICE = 1<<23 # .u -# = 1<<22 -DMNAMEDPIPE = 1<<21 # .u -DMSOCKET = 1<<20 # .u -DMSETUID = 1<<19 # .u -DMSETGID = 1<<18 # .u diff --git a/9p/srv.c b/9p/srv.c deleted file mode 100644 index 59326c9..0000000 --- a/9p/srv.c +++ /dev/null @@ -1,194 +0,0 @@ -#include <assert.h> - -#include "coroutine.h" -#include "netio.h" -#include "9p/9p.h" - -#include "9p/internal.h" - -struct p9_srvconn { - /* immutable */ - p9_srv *srv; - cid_t reader; - int fd; - /* mutable */ - uint32_t max_msg_size; - enum p9_version version; - unsigned int refcount; -}; - -struct p9_srvreq { - p9_srvconn *conn; - uint8_t *msg; -}; - -COROUTINE p9_srv_read_cr(void *_srv) { - uint8_t buf[CONFIG_9P_MAX_MSG_SIZE]; - - p9_srv *srv = _srv; - assert(srv); - cr_begin(); - - for (;;) { - struct p9_srvconn conn = { - .srv = srv, - .reader = cr_getcid(); - - .max_msg_size = CONFIG_9P_MAX_MSG_SIZE; - .version = P9_VER_UNINITIALIZED; - .refcount = 1, - }; - conn.fd = netio_accept(srv->sockfd); - if (conn.fd < 0) { - fprintf(stderr, "error: accept: %m", -conn.fd); - continue; - } - - for (;;) { - /* Read the message size. */ - size_t goal = 4, done = 0; - while (done < goal) { - ssize_t r = netio_read(conn.fd, &buf[done], sizeof(buf)-done); - if (r < 0) { - fprintf(stderr, "error: read: %m", -r); - goto close; - } else if (r == 0) { - if (done != 0) - fprintf(stderr, "error: read: unexpected EOF"); - goto close; - } - done += r; - } - goal = decode_u32le(buf); - if (goal < 7) { - /* We can't respond with an Rerror becuase we wouldn't know what tag to use! */ - fprintf(stderr, "error: T-message is impossibly small"); - goto close; - } - if (goal > conn.max_msg_size) { - struct p9_ctx ctx = { - .version = conn.version, - .max_msg_size = conn.max_msg_size, - }; - if (initialized) - p9_errorf(&ctx, LINUX_EMSGSIZE, "T-message larger than negotiated limit (%zu > %zu)", goal, conn.max_msg_size); - else - p9_errorf(&ctx, LINUX_EMSGSIZE, "T-message larger than server limit (%zu > %zu)", goal, conn.max_msg_size); - marshal_error(&ctx, buf); - netio_write(conn.fd, buf, decode_u32le(buf)); - continue; - } - /* Read the rest of the message. */ - while (done < goal) { - ssize_t r = netio_read(conn.fd, &buf[done], sizeof(buf)-done); - if (r < 0) { - fprintf(stderr, "error: read: %m", -r); - goto close; - } else if (r == 0) { - fprintf(stderr, "error: read: unexpected EOF"); - goto close; - } - done += r; - } - - /* Handle the message... */ - if (conn.version == P9_VER_UNINITIALIZED) { - /* ...synchronously if we haven't negotiated the protocol yet, ... */ - handle_message(&conn, buf); - } else { - /* ...asynchronously if we have. */ - cr_chan_send(&srv->reqch, buf); - cr_pause_and_yield(); - } - } - close: - netio_close(conn.fd, true, (--conn.refcount) == 0); - if (conn.refcount) { - cr_pause_and_yield(); - assert(conn.refcount == 0); - netio_close(conn.fd, false, true); - } - } - - cr_end(); -} - -COROUTINE p9_srv_write_cr(void *_srv) { - uint8_t net[CONFIG_9P_MAX_MSG_SIZE]; - - p9_srv *srv = _srv; - assert(srv); - cr_begin(); - - for (;;) { - struct p9_srvreq req; - cr_chan_recv(&srv->reqch, &req); - memcpy(net, req.msg, decode_u32le(req.msg)); - req.conn->refcount++; - cr_unpause(req.conn->reader); - - handle_message(&req.conn, net); - - if ((--req.conn->refcount) == 0) - cr_unpause(req.conn->reader); - } - - cr_end(); -} - -void handle_message(p9_srvconn *conn, uint8_t *net) { - uint8_t host[CONFIG_9P_MAX_MSG_SIZE]; - - struct p9_ctx ctx = { - .version = req.conn->version, - .max_msg_size = req.conn->max_msg_size, - }; - - size_t host_size = p9_unmarshal_size(&ctx, net); - if (host_size == (size_t)-1) - goto write; - if (host_size > sizeof(host)) { - p9_errorf(&ctx, LINUX_EMSGSIZE, "unmarshalled payload larger than server limit (%zu > %zu)", host_size, sizeof(host)); - goto write; - } - - uint16_t tag; - uint8_t typ = p9_unmarshal(&ctx, net, &tag, host); - if (typ == (uint8_t)-1) - goto write; - if (typ % 2 != 0) { - p9_errorf(&ctx, LINUX_EOPNOTSUPP, "expected a T-message but got an R-message"); - goto write; - } - - TODO; - - write: - if (ctx.err_num || ctx.err_msg[0]) - marshal_error(&ctx, net); - else - TODO; - netio_write(req.conn->fd, net, decode_u32le(net)); -} - -static inline uint16_t min_u16(uint16_t a, b) { - return (a < b) ? a : b; -} - -/* We have special code for marshaling Rerror because we don't ever - * want to produce an error because the err_msg is too long for the - * `ctx->max_msg_size`! */ -void marshal_error(struct p9_ctx *ctx, uint16_t tag, uint8_t *net) { - struct p9_msg_Rerror host = { - .ename = { - .len = strnlen(ctx->err_msg, CONFIG_9P_MAX_ERR_SIZE), - .utf8 = ctx->err_msg, - }, - }; - if (host.ename.len + ctx->Rerror_overhead > ctx->max_msg_size) - host.ename.len = ctx->max_msg_size - overhead; - p9_marshal(ctx, tag, host, net); -} - -ERANGE for reply too large -EPROTONOSUPPORT for version errors diff --git a/9p/srv.h b/9p/srv.h deleted file mode 100644 index e3623ed..0000000 --- a/9p/srv.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef _NET9P_H_ -#define _NET9P_H_ - -#include "coroutine.h" - -struct p9_srvreq; - -struct p9_srv { - int sockfd; - cr_chan_t(p9_srvreq *) reqch; -}; - -COROUTINE p9_srv_read_cr(void *_srv); -COROUTINE p9_srv_write_cr(void *_srv); - -#endif /* _NET9P_H_ */ |