diff options
author | Luke T. Shumaker <lukeshu@lukeshu.com> | 2024-09-24 09:51:37 -0600 |
---|---|---|
committer | Luke T. Shumaker <lukeshu@lukeshu.com> | 2024-09-24 09:51:37 -0600 |
commit | d559c50a98e65ce889411b46ab108b392907e0f0 (patch) | |
tree | e38f83c11ae43f48aadead70f180be30df2bab78 | |
parent | fbd945a03f2d706bb4d62aab6a607c1694d6d77a (diff) |
wip 9p
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | 3rd-party/linux-errno.txt | 264 | ||||
-rwxr-xr-x | 3rd-party/linux-errno.txt.gen | 14 | ||||
-rw-r--r-- | 9p/.editorconfig | 2 | ||||
-rw-r--r-- | 9p/.gitignore | 5 | ||||
-rw-r--r-- | 9p/9P2000.txt | 2 | ||||
-rw-r--r-- | 9p/9p.h | 15 | ||||
-rw-r--r-- | 9p/defs.c | 89 | ||||
-rwxr-xr-x | 9p/defs.gen (renamed from 9p/generate) | 249 | ||||
-rw-r--r-- | 9p/defs.h | 77 | ||||
-rw-r--r-- | 9p/internal.h | 114 | ||||
-rwxr-xr-x | 9p/linux-errno.h.gen | 15 | ||||
-rw-r--r-- | 9p/srv.h | 19 | ||||
-rw-r--r-- | Makefile | 12 | ||||
-rw-r--r-- | README.md | 49 | ||||
-rw-r--r-- | srv9p.c | 2 |
16 files changed, 608 insertions, 321 deletions
@@ -1,5 +1,6 @@ *.o *.log +.mypy_cache/ /build/ diff --git a/3rd-party/linux-errno.txt b/3rd-party/linux-errno.txt index 34b297d..b44da96 100644 --- a/3rd-party/linux-errno.txt +++ b/3rd-party/linux-errno.txt @@ -1,132 +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 +# 3rd-party/linux-errno.txt - Generated from linux.git v6.7. DO NOT EDIT! +1 EPERM Operation not permitted +2 ENOENT No such file or directory +3 ESRCH No such process +4 EINTR Interrupted system call +5 EIO I/O error +6 ENXIO No such device or address +7 E2BIG Argument list too long +8 ENOEXEC Exec format error +9 EBADF Bad file number +10 ECHILD No child processes +11 EAGAIN Try again +12 ENOMEM Out of memory +13 EACCES Permission denied +14 EFAULT Bad address +15 ENOTBLK Block device required +16 EBUSY Device or resource busy +17 EEXIST File exists +18 EXDEV Cross-device link +19 ENODEV No such device +20 ENOTDIR Not a directory +21 EISDIR Is a directory +22 EINVAL Invalid argument +23 ENFILE File table overflow +24 EMFILE Too many open files +25 ENOTTY Not a typewriter +26 ETXTBSY Text file busy +27 EFBIG File too large +28 ENOSPC No space left on device +29 ESPIPE Illegal seek +30 EROFS Read-only file system +31 EMLINK Too many links +32 EPIPE Broken pipe +33 EDOM Math argument out of domain of func +34 ERANGE Math result not representable +35 EDEADLK Resource deadlock would occur +36 ENAMETOOLONG File name too long +37 ENOLCK No record locks available +38 ENOSYS Invalid system call number +39 ENOTEMPTY Directory not empty +40 ELOOP Too many symbolic links encountered +42 ENOMSG No message of desired type +43 EIDRM Identifier removed +44 ECHRNG Channel number out of range +45 EL2NSYNC Level 2 not synchronized +46 EL3HLT Level 3 halted +47 EL3RST Level 3 reset +48 ELNRNG Link number out of range +49 EUNATCH Protocol driver not attached +50 ENOCSI No CSI structure available +51 EL2HLT Level 2 halted +52 EBADE Invalid exchange +53 EBADR Invalid request descriptor +54 EXFULL Exchange full +55 ENOANO No anode +56 EBADRQC Invalid request code +57 EBADSLT Invalid slot +59 EBFONT Bad font file format +60 ENOSTR Device not a stream +61 ENODATA No data available +62 ETIME Timer expired +63 ENOSR Out of streams resources +64 ENONET Machine is not on the network +65 ENOPKG Package not installed +66 EREMOTE Object is remote +67 ENOLINK Link has been severed +68 EADV Advertise error +69 ESRMNT Srmount error +70 ECOMM Communication error on send +71 EPROTO Protocol error +72 EMULTIHOP Multihop attempted +73 EDOTDOT RFS specific error +74 EBADMSG Not a data message +75 EOVERFLOW Value too large for defined data type +76 ENOTUNIQ Name not unique on network +77 EBADFD File descriptor in bad state +78 EREMCHG Remote address changed +79 ELIBACC Can not access a needed shared library +80 ELIBBAD Accessing a corrupted shared library +81 ELIBSCN .lib section in a.out corrupted +82 ELIBMAX Attempting to link in too many shared libraries +83 ELIBEXEC Cannot exec a shared library directly +84 EILSEQ Illegal byte sequence +85 ERESTART Interrupted system call should be restarted +86 ESTRPIPE Streams pipe error +87 EUSERS Too many users +88 ENOTSOCK Socket operation on non-socket +89 EDESTADDRREQ Destination address required +90 EMSGSIZE Message too long +91 EPROTOTYPE Protocol wrong type for socket +92 ENOPROTOOPT Protocol not available +93 EPROTONOSUPPORT Protocol not supported +94 ESOCKTNOSUPPORT Socket type not supported +95 EOPNOTSUPP Operation not supported on transport endpoint +96 EPFNOSUPPORT Protocol family not supported +97 EAFNOSUPPORT Address family not supported by protocol +98 EADDRINUSE Address already in use +99 EADDRNOTAVAIL Cannot assign requested address +100 ENETDOWN Network is down +101 ENETUNREACH Network is unreachable +102 ENETRESET Network dropped connection because of reset +103 ECONNABORTED Software caused connection abort +104 ECONNRESET Connection reset by peer +105 ENOBUFS No buffer space available +106 EISCONN Transport endpoint is already connected +107 ENOTCONN Transport endpoint is not connected +108 ESHUTDOWN Cannot send after transport endpoint shutdown +109 ETOOMANYREFS Too many references: cannot splice +110 ETIMEDOUT Connection timed out +111 ECONNREFUSED Connection refused +112 EHOSTDOWN Host is down +113 EHOSTUNREACH No route to host +114 EALREADY Operation already in progress +115 EINPROGRESS Operation now in progress +116 ESTALE Stale file handle +117 EUCLEAN Structure needs cleaning +118 ENOTNAM Not a XENIX named type file +119 ENAVAIL No XENIX semaphores available +120 EISNAM Is a named type file +121 EREMOTEIO Remote I/O error +122 EDQUOT Quota exceeded +123 ENOMEDIUM No medium found +124 EMEDIUMTYPE Wrong medium type +125 ECANCELED Operation Canceled +126 ENOKEY Required key not available +127 EKEYEXPIRED Key has expired +128 EKEYREVOKED Key has been revoked +129 EKEYREJECTED Key was rejected by service +130 EOWNERDEAD Owner died +131 ENOTRECOVERABLE State not recoverable +132 ERFKILL Operation not possible due to RF-kill +133 EHWPOISON Memory page has hardware error diff --git a/3rd-party/linux-errno.txt.gen b/3rd-party/linux-errno.txt.gen new file mode 100755 index 0000000..b03a67e --- /dev/null +++ b/3rd-party/linux-errno.txt.gen @@ -0,0 +1,14 @@ +#!/bin/sh +# 3rd-party/linux-errno.txt.gen - Generate a listing of Linux kernel errnos +# +# Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com> +# SPDX-Licence-Identifier: AGPL-3.0-or-later + +set -e +( + cd "$1" + echo "# ${0%.gen} - Generated from linux.git $(git describe). DO NOT EDIT!" + git ls-files include/uapi/ | grep errno | + xargs sed -nE 's,#\s*define\s+(E[A-Z0-9]+)\s+([0-9]+)\s+/\* (.*) \*/,\2 \1 \3,p' | + sort --numeric-sort +) >"${0%.gen}" diff --git a/9p/.editorconfig b/9p/.editorconfig index 3de59d7..3f04564 100644 --- a/9p/.editorconfig +++ b/9p/.editorconfig @@ -1,3 +1,3 @@ -[{generate,linux-errno.h.gen}] +[{defs.gen,linux-errno.h.gen}] indent_style = space indent_size = 4 diff --git a/9p/.gitignore b/9p/.gitignore index c8e8c58..667e81c 100644 --- a/9p/.gitignore +++ b/9p/.gitignore @@ -1,5 +1,2 @@ -/9P2000.c -/9P2000.h -/9P2000.*.c -/9P2000.*.h +/defs-* /linux-errno.h diff --git a/9p/9P2000.txt b/9p/9P2000.txt index 778673f..5f93cdf 100644 --- a/9p/9P2000.txt +++ b/9p/9P2000.txt @@ -25,7 +25,7 @@ version "9P2000" # data (u32le `n`, then `n` bytes of data) -d = "len[8] len*(dat[1])" +d = "len[4] len*(dat[1])" # string (u16le `n`, then `n` bytes of UTF-8) s = "len[2] len*(utf8[1])" @@ -0,0 +1,15 @@ +/* 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 new file mode 100644 index 0000000..886a0c1 --- /dev/null +++ b/9p/defs.c @@ -0,0 +1,89 @@ +/* 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/generate b/9p/defs.gen index 6456609..1a8dc69 100755 --- a/9p/generate +++ b/9p/defs.gen @@ -1,14 +1,15 @@ #!/usr/bin/env python -# 9p/generate - Generate C marshalers/unmarshalers for a .txt file +# 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/generate" +PROGRAM = "./9p/defs.gen" # Parse the *.txt ############################################################## @@ -176,9 +177,7 @@ def c_typename(idprefix: str, typ: Atom | Struct) -> str: def gen_h(txtname: str, idprefix: str, structs: list[Struct]) -> str: guard = ( - "_" - + txtname.replace(".txt", ".h").upper().replace("/", "_").replace(".", "_") - + "_" + f"_{txtname.replace('.txt', '.h').upper().replace('/', '_').replace('.', '_')}_" ) ret = f"""/* Generated by `{PROGRAM} {txtname}`. DO NOT EDIT! */ @@ -231,34 +230,14 @@ def gen_h(txtname: str, idprefix: str, structs: list[Struct]) -> str: 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" +/* tables *********************************************************************/ + +#define _P9_TYPECODE_FOR_CTYPE(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 += f", \\\n\t\t{c_typename(idprefix, msg)}: {idprefix.upper()}TYP_{msg.name}" + ret += "\n" ret += "\n" ret += f"#endif /* {guard} */\n" @@ -266,71 +245,16 @@ int {idprefix}unmarshal_msg(uint8_t *net_bytes, uint16_t *out_tag, void **out_bo 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 "{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); -} +#include "{header}" +#include "9p/internal.h" """ def used(arg: str) -> str: @@ -343,7 +267,6 @@ static inline void encode_u64le(uint64_t in, uint8_t *out) { 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++) @@ -352,6 +275,7 @@ static inline bool _checksize_list(size_t cnt, _checksize_fn_t fn, size_t host_s 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; } @@ -364,12 +288,14 @@ static inline bool checksize_4(uint32_t net_len, uint8_t *UNUSED(net_bytes), uin 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')}) {{\n" + 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 += "\treturn false;\n" + ret += "\n\treturn false;\n" ret += "}\n" continue prefix0 = "\treturn " @@ -397,132 +323,116 @@ static inline bool checksize_8(uint32_t net_len, uint8_t *UNUSED(net_bytes), uin /* 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) { +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 void unmarshal_2(uint8_t *net_bytes, uint32_t *mut_net_offset, void **UNUSED(mut_host_extra), uint16_t *out) { +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 void unmarshal_4(uint8_t *net_bytes, uint32_t *mut_net_offset, void **UNUSED(mut_host_extra), uint32_t *out) { +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 void unmarshal_8(uint8_t *net_bytes, uint32_t *mut_net_offset, void **UNUSED(mut_host_extra), uint64_t *out) { +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 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" + 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\tunmarshal_{member.typ.name}(net_bytes, mut_net_offset, mut_host_extra, &(out->{member.name}[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"\tunmarshal_{member.typ.name}(net_bytes, mut_net_offset, mut_host_extra, &(out->{member.name}));\n" + 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" - # 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; +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 void marshal_2(uint16_t val, uint8_t *out_net_bytes, uint32_t *mut_net_offset) { +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 void marshal_4(uint32_t val, uint8_t *out_net_bytes, uint32_t *mut_net_offset) { +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 void marshal_8(uint64_t val, uint8_t *out_net_bytes, uint32_t *mut_net_offset) { +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 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" + 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\tmarshal_{member.typ.name}(val.{member.name}[i], out_net_bytes, mut_net_offset);\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"\tmarshal_{member.typ.name}(val.{member.name}, out_net_bytes, mut_net_offset);\n" + 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" - # _marshal_msg_* ########################################################### + # tables ################################################################### ret += """ -/* _marshal_msg_* *************************************************************/ +/* tables *********************************************************************/ """ 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" + 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 @@ -534,8 +444,13 @@ if __name__ == "__main__": import sys for txtname in sys.argv[1:]: + txtdir, txtbase = os.path.split(txtname) version, structs = parse_file(txtname) - with open(txtname.replace(".txt", ".h"), "w") as fh: + with open( + os.path.join(txtdir, "defs-" + txtbase.replace(".txt", ".h")), "w" + ) as fh: fh.write(gen_h(txtname, "p9_", structs)) - with open(txtname.replace(".txt", ".c"), "w") as fh: + 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 new file mode 100644 index 0000000..20a6411 --- /dev/null +++ b/9p/defs.h @@ -0,0 +1,77 @@ +/* 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_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 version; + uint32_t max_msg_size; + + uint32_t err_num; + char err_msg[256]; /* I chose 256 arbitrarily. */ +}; + +/** 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 new file mode 100644 index 0000000..1bc0e92 --- /dev/null +++ b/9p/internal.h @@ -0,0 +1,114 @@ +/* 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 <stdint.h> +#include <stdbool.h> + +#include "9p/defs.h" + +/* C language *****************************************************************/ + +#define UNUSED(name) /* name __attribute__ ((unused)) */ + +/* 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_without_nul(uint8_t *str, size_t len) { + uint8_t mask; + uint8_t chlen; + for (size_t pos = 0; pos < len;) { + if ((str[pos] & 0b10000000) == 0b00000000) { mask = 0b01111111; chlen = 1; } + else if ((str[pos] & 0b11100000) == 0b11000000) { mask = 0b00011111; chlen = 2; } + else if ((str[pos] & 0b11110000) == 0b11100000) { mask = 0b00001111; chlen = 3; } + else if ((str[pos] & 0b11111000) == 0b11110000) { mask = 0b00000111; chlen = 4; } + else return false; + if (pos + chlen > len || (str[pos] & mask) == 0) return false; + switch (chlen) { + case 4: if ((str[pos+3] & 0b11000000) != 0b10000000) return false; /* fallthrough */ + case 3: if ((str[pos+2] & 0b11000000) != 0b10000000) return false; /* fallthrough */ + case 2: if ((str[pos+1] & 0b11000000) != 0b10000000) return false; /* fallthrough */ + } + pos += chlen; + } + return 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 index 749cd8e..b896384 100755 --- a/9p/linux-errno.h.gen +++ b/9p/linux-errno.h.gen @@ -3,26 +3,27 @@ def print_errnos(txtlists: list[str]) -> None: print( - f"/* Generated by `./9p/linux-errno.h.gen {' '.join(txtlists)}`. DO NOT EDIT! */" + f"/* 9p/linux-errno.h - Generated by `./9p/linux-errno.h.gen {' '.join(txtlists)}`. DO NOT EDIT! */" ) - errnos: dict[str, int] = {} + 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 = line.split(maxsplit=1) + _num, name, desc = line.split(maxsplit=2) num = int(_num) - name = name.strip() - errnos[name] = int(num) - namelen = max(len(name) for name in errnos.keys()) + 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)} {errnos[name]}") + print(f"#define LINUX_{name.ljust(namelen)} {str(errnos[name][0]).rjust(numlen)} /* {errnos[name][1]} */") print() print("#endif /* _9P_LINUX_ERRNO_H_ */") @@ -3,6 +3,25 @@ #include "coroutine.h" +#define 9P_DEFAULT_PORT 564 + +/** + * The default here is the same as in Plan 9 4e's lib9p. It's sized + * so that a Twrite message can return 8KiB of data; it uses the + * default (1024*8)+24 with the comment that "24" is "ample room for + * Twrite/Rread header (iounit)". In fact, the Twrite header is only + * 23 bytes ("size[4] Twrite[1] tag[2] fid[4] offset[8] count[4]") and + * the Rread header is even shorter at 11 bytes ("size[4] Rread[1] + * tag[2] count[4]"), so "24" appears to be the size of the Twrite + * header rounded up to a nice round number. + * + * In older versions of 9P ("9P1"), the max message size was defined + * as part of the protocol specification rather than negotiated. In + * Plan 9 1e it was (1024*8)+128, and was bumped to (1024*8)+160 in 2e + * and 3e. + */ +#define 9P_DEFAULT_MAX_MSG_SIZE ((1024*8)+24) + COROUTINE net9p_cr(void *); #endif /* _NET9P_H_ */ @@ -6,21 +6,17 @@ LDFLAGS += -static linux.git = $(HOME)/src/github.com/torvalds/linux -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 && \ - :; } >$@ +3rd-party/linux-errno.txt: %: %.gen + $< $(linux.git) 9p/linux-errno.h: %: %.gen 3rd-party/linux-errno.txt $^ >$@ -9p/%.c 9p/%.h: 9p/generate 9p/%.txt +9p/defs-%.c 9p/defs-%.h: 9p/defs.gen 9p/%.txt $^ srv9p: srv9p.o coroutine.o net9p.o 9p/9P2000.o -sources_py = 9p/generate +sources_py = 9p/defs.gen sources_py += 9p/linux-errno.h.gen lint: @@ -26,3 +26,52 @@ UART: - pin2: gpio1: RX (so connect it to your FTDI's TX) - pin3: gnd (so connect it to your FTDI's GND) - picocom --baud=115200 /dev/ttyUSB0 + +# Usage + +The harness uses DHCP to acquire an IPv4 address, then serves the 9P +protocol over TCP: + + - TCP port: 564 (9P does not have a standard TCP port number, but + this is the default port number used by most 9P-over-TCP clients, + including the Linux kernel's v9fs driver). + - Supported protocol versions: + - `9P2000` (base protocol): Yes + - `9P2000.u` (Unix extension): Yes, with Linux kernel + architecture-"generic" errnos. This will match the Linux kernel + errnos on most architectures (but notably not on Alpha, MIPS, + PA-RISC, PowerPC, or SPARC; I am unsure whether on these + platforms the kernel's v9fs filesystem driver will map the + "generic" errnos to the architecture-specific errnos for you). + - `9P2000.L` (Linux extension): No, it's an abomination and + unlikely to ever be supported + - `9P2000.e` (Erlang extension): No, but if you want it and ask + nicely I'd be happy to add it (I'm not sure why you'd want it + though). + - Authentication: None + +There are lots of 9P clients that you can use. 9P is a filesystem +protocol; and you can mount it directly with the the Linux kernel's +v9fs filesystem driver, with plan9port's `9pfuse`; or interact with it +without mounting it using the shell commands `9p` (from plan9port), +`wmiir`, `ixpc`; or interact with it without mounting it by using a +library for your programming language of choice. + +Some notes on choosing a client: + - On x86-32, the Linux kernel v9fs driver is known to drop entries + from directory listings; I advise using 9pfuse instead of you want + to mount it on 32-bit systems. + - I generally like mounting it as a real filesystem, but this means + that you only get errno errors, and the more-helpful error strings + are discarded. + - The sbc-harness only supports 8 concurrent connections to the 9P + server, so while shell commands are handy for poking around, for + real use where you're doing things in parallel you'll likely want + to mount it or use a library that can reuse existing connections. + +# Bugs/Limitations + + - Only supports 8 concurrent TCP connectsions to the 9P server (due + to limitations in the W5500 TCP-offload chip) + - Only supports IPv4, not IPv6 (due to limitations in the W5500 + TCP-offload chip) @@ -10,7 +10,7 @@ int main() { error(1, -sock, "netio_listen"); for (int i = 0; i < 8; i++) - if (!coroutine_add(net9p_cr, NULL)) + if (!coroutine_add(net9p_cr, &sock)) error(1, 0, "coroutine_add(net9p_cr, NULL)"); coroutine_main(); |