summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.editorconfig54
-rw-r--r--.gitignore4
-rw-r--r--.gitmodules3
-rw-r--r--3rd-party/COPYING.wiznet-dhcp.txt60
-rw-r--r--3rd-party/linux-errno.txt2
m---------3rd-party/pico-fmt0
m---------3rd-party/pico-sdk0
-rw-r--r--CMakeLists.txt50
-rw-r--r--GNUmakefile149
-rw-r--r--HACKING.md11
-rw-r--r--PLAN.md23
-rw-r--r--README.md18
-rwxr-xr-xbuild-aux/embed-sources.h.gen4
-rwxr-xr-xbuild-aux/gcov-prune33
-rwxr-xr-xbuild-aux/get-dscname36
-rwxr-xr-xbuild-aux/lint-bin28
-rwxr-xr-xbuild-aux/lint-generic55
-rwxr-xr-xbuild-aux/lint-h27
-rwxr-xr-xbuild-aux/lint-src160
-rwxr-xr-xbuild-aux/lint-unknown24
-rw-r--r--build-aux/measurestack/analyze.py288
-rw-r--r--build-aux/measurestack/app_main.py17
-rw-r--r--build-aux/measurestack/app_output.py46
-rw-r--r--build-aux/measurestack/app_plugins.py521
-rw-r--r--build-aux/measurestack/test_analyze.py55
-rw-r--r--build-aux/measurestack/testutil.py134
-rw-r--r--build-aux/measurestack/util.py18
-rwxr-xr-xbuild-aux/tent-graph180
-rwxr-xr-xbuild-aux/valgrind16
-rw-r--r--cmd/sbc_harness/CMakeLists.txt79
l---------cmd/sbc_harness/static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS/printf.mit.txt1
-rw-r--r--cmd/sbc_harness/static/Documentation/harness_flash_bin.txt24
-rw-r--r--cmd/sbc_harness/tusb_log.c15
-rw-r--r--cmd/sbc_harness/usb_keyboard.h20
-rw-r--r--flashimg/cpu_hdmi/CMakeLists.txt20
-rw-r--r--flashimg/cpu_hdmi/main.c9
-rw-r--r--flashimg/cpu_main/CMakeLists.txt105
-rw-r--r--flashimg/cpu_main/config/config.h (renamed from cmd/sbc_harness/config/config.h)58
-rw-r--r--flashimg/cpu_main/config/tusb_config.h (renamed from cmd/sbc_harness/config/tusb_config.h)49
-rw-r--r--flashimg/cpu_main/fs_harness_flash_bin.c332
-rw-r--r--flashimg/cpu_main/fs_harness_flash_bin.h36
-rw-r--r--flashimg/cpu_main/fs_harness_uptime_txt.c167
-rw-r--r--flashimg/cpu_main/fs_harness_uptime_txt.h18
-rw-r--r--flashimg/cpu_main/ihex.c231
-rw-r--r--flashimg/cpu_main/ihex.h49
-rw-r--r--flashimg/cpu_main/main.c (renamed from cmd/sbc_harness/main.c)139
-rw-r--r--flashimg/cpu_main/static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS.md (renamed from cmd/sbc_harness/static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS.md)12
l---------flashimg/cpu_main/static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS/agpl-3.0.txt (renamed from cmd/sbc_harness/static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS/agpl-3.0.txt)0
l---------flashimg/cpu_main/static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS/dhcp.bsd3-mit.txt1
l---------flashimg/cpu_main/static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS/newlib.txt (renamed from cmd/sbc_harness/static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS/newlib.txt)0
l---------flashimg/cpu_main/static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS/pico-sdk.bsd3.txt (renamed from cmd/sbc_harness/static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS/pico-sdk.bsd3.txt)0
l---------flashimg/cpu_main/static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS/tinyusb.mit.txt (renamed from cmd/sbc_harness/static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS/tinyusb.mit.txt)0
-rw-r--r--flashimg/cpu_main/static/Documentation/harness_flash.bin.txt43
-rw-r--r--flashimg/cpu_main/static/Documentation/harness_rom.bin.txt (renamed from cmd/sbc_harness/static/Documentation/harness_rom_bin.txt)0
-rw-r--r--flashimg/cpu_main/static/Documentation/harness_uptime.txt.txt27
-rw-r--r--flashimg/cpu_main/tests/test_ihex.c128
-rw-r--r--flashimg/cpu_main/tusb_log.c22
-rw-r--r--flashimg/cpu_main/usb_keyboard.c (renamed from cmd/sbc_harness/usb_keyboard.c)39
-rw-r--r--flashimg/cpu_main/usb_keyboard.h20
-rw-r--r--gdb-helpers/libcr.py142
-rw-r--r--gdb-helpers/rp2040.py4
-rw-r--r--lib9p/9p.c95
-rw-r--r--lib9p/9p.generated.c8037
-rw-r--r--lib9p/CMakeLists.txt89
-rw-r--r--lib9p/core.c207
-rwxr-xr-xlib9p/core.gen (renamed from lib9p/proto.gen)10
-rw-r--r--lib9p/core_gen/__init__.py (renamed from lib9p/protogen/__init__.py)8
-rw-r--r--lib9p/core_gen/c.py (renamed from lib9p/protogen/c.py)71
-rw-r--r--lib9p/core_gen/c9util.py (renamed from lib9p/protogen/c9util.py)6
-rw-r--r--lib9p/core_gen/c_fmt_print.py (renamed from lib9p/protogen/c_format.py)88
-rw-r--r--lib9p/core_gen/c_marshal.py (renamed from lib9p/protogen/c_marshal.py)58
-rw-r--r--lib9p/core_gen/c_unmarshal.py (renamed from lib9p/protogen/c_unmarshal.py)6
-rw-r--r--lib9p/core_gen/c_validate.py (renamed from lib9p/protogen/c_validate.py)40
-rw-r--r--lib9p/core_gen/cutil.py (renamed from lib9p/protogen/cutil.py)2
-rw-r--r--lib9p/core_gen/h.py (renamed from lib9p/protogen/h.py)36
-rw-r--r--lib9p/core_gen/idlutil.py (renamed from lib9p/protogen/idlutil.py)42
-rw-r--r--lib9p/core_generated.c8115
-rw-r--r--lib9p/core_include/lib9p/_core_generated.h (renamed from lib9p/include/lib9p/9p.generated.h)1050
-rw-r--r--lib9p/core_include/lib9p/core.h (renamed from lib9p/include/lib9p/9p.h)114
-rw-r--r--lib9p/core_tables.h (renamed from lib9p/tables.h)29
-rw-r--r--lib9p/idl/0000-uninitialized.9p13
-rw-r--r--lib9p/idl/1992-9P0.9p.wip4
-rw-r--r--lib9p/idl/1996-Styx.9p.wip2
-rw-r--r--lib9p/idl/2002-9P2000.9p34
-rw-r--r--lib9p/idl/2003-9P2000.p9p.9p2
-rw-r--r--lib9p/idl/2005-9P2000.u.9p14
-rw-r--r--lib9p/idl/2010-9P2000.L.9p165
-rwxr-xr-xlib9p/idl/2010-9P2000.L.9p.gen282
-rw-r--r--lib9p/idl/__init__.py20
-rw-r--r--lib9p/include/lib9p/linux-errno.h139
-rwxr-xr-xlib9p/include/lib9p/linux-errno.h.gen38
-rw-r--r--lib9p/include/lib9p/srv.h179
-rwxr-xr-xlib9p/linux-errno.txt.gen (renamed from build-aux/linux-errno.txt.gen)4
-rw-r--r--lib9p/map.h114
-rw-r--r--lib9p/srv.c1893
-rw-r--r--lib9p/srv_errno.h14
-rw-r--r--lib9p/srv_generated.c91
-rwxr-xr-xlib9p/srv_generated.c.gen30
-rw-r--r--lib9p/srv_include/lib9p/srv.h313
-rw-r--r--lib9p/tables.c186
-rw-r--r--lib9p/tests/client_config/config.h18
-rwxr-xr-xlib9p/tests/runtest81
-rw-r--r--lib9p/tests/test_compile.c1546
-rwxr-xr-xlib9p/tests/test_compile.c.gen10
-rw-r--r--lib9p/tests/test_compile_config/config.h22
-rw-r--r--lib9p/tests/test_server/CMakeLists.txt8
-rw-r--r--lib9p/tests/test_server/config/config.h36
-rw-r--r--lib9p/tests/test_server/fs_flush.c128
-rw-r--r--lib9p/tests/test_server/fs_flush.h26
-rw-r--r--lib9p/tests/test_server/fs_shutdown.c108
-rw-r--r--lib9p/tests/test_server/fs_shutdown.h22
-rw-r--r--lib9p/tests/test_server/fs_whoami.c147
-rw-r--r--lib9p/tests/test_server/fs_whoami.h19
-rw-r--r--lib9p/tests/test_server/main.c219
-rw-r--r--lib9p/tests/testclient-hangup.c100
-rw-r--r--lib9p/tests/testclient-hangup.explog14
-rwxr-xr-xlib9p/tests/testclient-p9p65
-rw-r--r--lib9p/tests/testclient-p9p.explog106
-rw-r--r--lib9p/tests/testclient-sess.c246
-rw-r--r--lib9p/tests/testclient-sess.explog156
-rw-r--r--lib9p/utf8.h34
-rw-r--r--lib9p_util/CMakeLists.txt4
-rw-r--r--lib9p_util/include/util9p/static.h32
-rw-r--r--lib9p_util/static.c240
-rw-r--r--libcr/CMakeLists.txt7
-rw-r--r--libcr/coroutine.c107
-rw-r--r--libcr/include/libcr/coroutine.h44
-rw-r--r--libcr/tests/test_matrix.c6
-rw-r--r--libcr/tests/test_matrix/config.h2
-rw-r--r--libcr_ipc/CMakeLists.txt21
-rw-r--r--libcr_ipc/chan.c146
-rw-r--r--libcr_ipc/include/libcr_ipc/_linkedlist.h99
-rw-r--r--libcr_ipc/include/libcr_ipc/chan.h242
-rw-r--r--libcr_ipc/include/libcr_ipc/mutex.h48
-rw-r--r--libcr_ipc/include/libcr_ipc/owned_mutex.h79
-rw-r--r--libcr_ipc/include/libcr_ipc/rpc.h270
-rw-r--r--libcr_ipc/include/libcr_ipc/rwmutex.h76
-rw-r--r--libcr_ipc/include/libcr_ipc/select.h88
-rw-r--r--libcr_ipc/include/libcr_ipc/sema.h69
-rw-r--r--libcr_ipc/mutex.c44
-rw-r--r--libcr_ipc/rpc.c84
-rw-r--r--libcr_ipc/rwmutex.c98
-rw-r--r--libcr_ipc/select.c102
-rw-r--r--libcr_ipc/sema.c62
-rw-r--r--libcr_ipc/tests/config.h6
-rw-r--r--libcr_ipc/tests/test_chan.c108
-rw-r--r--libcr_ipc/tests/test_chan_compile.c13
-rw-r--r--libcr_ipc/tests/test_mutex.c37
-rw-r--r--libcr_ipc/tests/test_mutex_compile.c11
-rw-r--r--libcr_ipc/tests/test_rpc.c28
-rw-r--r--libcr_ipc/tests/test_rpc_compile.c13
-rw-r--r--libcr_ipc/tests/test_rwmutex.c88
-rw-r--r--libcr_ipc/tests/test_rwmutex_compile.c11
-rw-r--r--libcr_ipc/tests/test_sema.c22
-rw-r--r--libcr_ipc/tests/test_sema_compile.c11
-rw-r--r--libdhcp/CMakeLists.txt9
-rw-r--r--libdhcp/dhcp_client.c127
-rw-r--r--libdhcp/dhcp_common.c104
-rw-r--r--libdhcp/dhcp_common.h96
-rw-r--r--libdhcp/include/libdhcp/client.h60
-rw-r--r--libdhcp/tests/config.h25
l---------libdhcp/tests/test.h (renamed from libobj/tests/test.h)0
-rw-r--r--libdhcp/tests/test_client.c120
-rw-r--r--libfmt/CMakeLists.txt17
-rw-r--r--libfmt/include/libfmt/fmt.h23
-rw-r--r--libfmt/libmisc.c59
-rw-r--r--libfmt/libobj.c17
-rw-r--r--libfmt/quote.c159
-rw-r--r--libhw_cr/CMakeLists.txt5
-rw-r--r--libhw_cr/host_alarmclock.c43
-rw-r--r--libhw_cr/host_include/libhw/host_alarmclock.h11
-rw-r--r--libhw_cr/host_include/libhw/host_net.h25
-rw-r--r--libhw_cr/host_net.c340
-rw-r--r--libhw_cr/host_util.c130
-rw-r--r--libhw_cr/host_util.h43
-rw-r--r--libhw_cr/rp2040_dma.c24
-rw-r--r--libhw_cr/rp2040_dma.h14
-rw-r--r--libhw_cr/rp2040_gpioirq.c4
-rw-r--r--libhw_cr/rp2040_hwspi.c76
-rw-r--r--libhw_cr/rp2040_hwtimer.c29
-rw-r--r--libhw_cr/rp2040_include/libhw/rp2040_hwspi.h10
-rw-r--r--libhw_cr/rp2040_include/libhw/w5500.h25
-rw-r--r--libhw_cr/w5500.c268
-rw-r--r--libhw_cr/w5500_ll.c115
-rw-r--r--libhw_cr/w5500_ll.h201
-rw-r--r--libhw_generic/CMakeLists.txt9
-rw-r--r--libhw_generic/alarmclock.c18
-rw-r--r--libhw_generic/include/libhw/generic/alarmclock.h35
-rw-r--r--libhw_generic/include/libhw/generic/io.h209
-rw-r--r--libhw_generic/include/libhw/generic/net.h80
-rw-r--r--libhw_generic/include/libhw/generic/spi.h2
-rw-r--r--libhw_generic/io.c45
-rw-r--r--libhw_generic/net.c20
-rw-r--r--libhw_generic/tests/test_io.c8
-rw-r--r--libmisc/CMakeLists.txt24
-rw-r--r--libmisc/assert.c16
-rw-r--r--libmisc/endian.c136
-rw-r--r--libmisc/error.c35
-rw-r--r--libmisc/error_generated.c186
-rwxr-xr-xlibmisc/error_generated.c.gen35
-rw-r--r--libmisc/fmt.c266
-rw-r--r--libmisc/hash.c24
-rw-r--r--libmisc/include/libmisc/_intercept.h17
-rw-r--r--libmisc/include/libmisc/alloc.h41
-rw-r--r--libmisc/include/libmisc/assert.h24
-rw-r--r--libmisc/include/libmisc/endian.h219
-rw-r--r--libmisc/include/libmisc/error.h171
-rw-r--r--libmisc/include/libmisc/fmt.h163
-rw-r--r--libmisc/include/libmisc/hash.h24
-rw-r--r--libmisc/include/libmisc/linkedlist.h108
-rw-r--r--libmisc/include/libmisc/log.h21
-rw-r--r--libmisc/include/libmisc/macro.h147
-rw-r--r--libmisc/include/libmisc/map.h145
-rw-r--r--libmisc/include/libmisc/obj.h184
-rw-r--r--libmisc/include/libmisc/private.h4
-rw-r--r--libmisc/include/libmisc/rand.h34
-rw-r--r--libmisc/include/libmisc/utf8.h25
-rw-r--r--libmisc/intercept.c20
-rw-r--r--libmisc/linkedlist.c64
-rw-r--r--libmisc/log.c19
-rw-r--r--libmisc/map.c230
-rw-r--r--libmisc/rand.c38
-rw-r--r--libmisc/tests/test_assert.c76
-rw-r--r--libmisc/tests/test_endian.c2
-rw-r--r--libmisc/tests/test_fmt.c243
-rw-r--r--libmisc/tests/test_log.c82
-rw-r--r--libmisc/tests/test_macro.c138
-rw-r--r--libmisc/tests/test_map.c60
-rw-r--r--libmisc/tests/test_obj.c (renamed from libobj/tests/test_obj.c)16
-rw-r--r--libmisc/tests/test_obj_autobox.c77
-rwxr-xr-xlibmisc/tests/test_obj_autobox.c.gen17
-rw-r--r--libmisc/tests/test_obj_nest.c (renamed from libobj/tests/test_nest.c)47
-rw-r--r--libmisc/tests/test_private.c10
-rw-r--r--libmisc/tests/test_rand.c7
-rw-r--r--libmisc/utf8.c44
-rwxr-xr-xlibmisc/wrap-cc186
-rw-r--r--libobj/CMakeLists.txt14
-rw-r--r--libobj/include/libobj/obj.h154
-rw-r--r--libusb/CMakeLists.txt4
-rw-r--r--libusb/include/libusb/tusb_helpers.h2
-rwxr-xr-xlibusb/include/libusb/tusb_helpers.h.gen21
-rw-r--r--libusb/usb_common.c23
-rw-r--r--notes.md168
243 files changed, 22429 insertions, 14862 deletions
diff --git a/.editorconfig b/.editorconfig
index f907b33..ced4c65 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -15,6 +15,8 @@ indent_style = tab
insert_final_newline = true
trim_trailing_whitespace = true
+# By well-known name ###########################################################
+
[*.{c,h}]
_mode = c
@@ -27,20 +29,6 @@ _mode = cmake
[*.md]
_mode = markdown
-[*.9p{,.wip}]
-_mode = 9p
-
-[{lib9p/tests/test_server/static.h.gen,build-aux/embed-sources.h.gen,build-aux/lint-{generic,unknown},lib9p/tests/test_compile.c.gen}]
-_mode = sh
-
-[{build-aux/lint-h,build-aux/lint-bin,build-aux/get-dscname,build-aux/linux-errno.txt.gen,libusb/include/libusb/tusb_helpers.h.gen,lib9p/tests/runtest}]
-_mode = bash
-
-[{lib9p/proto.gen,lib9p/include/lib9p/linux-errno.h.gen,build-aux/stack.c.gen}]
-_mode = python3
-indent_style = space
-indent_size = 4
-
[*.py]
_mode = python3
indent_style = space
@@ -49,11 +37,43 @@ indent_size = 4
[requirements.txt]
_mode = pip
-[**/Documentation/**.txt]
-_mode = man-cat
-
[{.editorconfig,.gitmodules,.pylintrc}]
_mode = ini
[.gitignore]
_mode = gitignore
+
+# By specific filename (non-lib9p) #############################################
+
+[{build-aux/embed-sources.h.gen,build-aux/valgrind,libmisc/tests/test_obj_autobox.c.gen}]
+_mode = sh
+
+[{build-aux/lint-{src,bin},build-aux/gcov-prune,libmisc/error_generated.c.gen,libusb/include/libusb/tusb_helpers.h.gen}]
+_mode = bash
+
+[{build-aux/stack.c.gen,build-aux/tent-graph,libmisc/wrap-cc}]
+_mode = python3
+indent_style = space
+indent_size = 4
+
+[**/Documentation/**.txt]
+_mode = man-cat
+
+# By specific filename (lib9p) #################################################
+
+[lib9p/idl/*.9p{,.wip}]
+_mode = 9p-idl
+
+[lib9p/tests/*.explog]
+_mode = 9p-log
+
+[lib9p/tests/test_server/static.h.gen]
+_mode = sh
+
+[{lib9p/linux-errno.txt.gen,lib9p/srv_generated.c.gen,lib9p/tests/test_compile.c.gen,lib9p/tests/runtest,lib9p/tests/testclient-p9p}]
+_mode = bash
+
+[{lib9p/core.gen,lib9p/idl/2010-9P2000.L.9p.gen}]
+_mode = python3
+indent_style = space
+indent_size = 4
diff --git a/.gitignore b/.gitignore
index e1f5883..9d27e6a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,11 +1,13 @@
# .gitignore - Which files to ignore
#
-# Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com>
+# Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
# SPDX-License-Identifier: AGPL-3.0-or-later
*.o
*.log
*.tmp
+*.gcov.json.gz
+
.mypy_cache/
__pycache__/
.gdb_history
diff --git a/.gitmodules b/.gitmodules
index 17dec29..8a4c874 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -6,6 +6,3 @@
[submodule "3rd-party/pico-sdk"]
path = 3rd-party/pico-sdk
url = https://github.com/LukeShu/pico-sdk
-[submodule "3rd-party/pico-fmt"]
- path = 3rd-party/pico-fmt
- url = https://github.com/LukeShu/pico-fmt
diff --git a/3rd-party/COPYING.wiznet-dhcp.txt b/3rd-party/COPYING.wiznet-dhcp.txt
new file mode 100644
index 0000000..363bc9c
--- /dev/null
+++ b/3rd-party/COPYING.wiznet-dhcp.txt
@@ -0,0 +1,60 @@
+-----------------------------------------------------------------------------
+https://github.com/Wiznet/ioLibrary_Driver/blob/b981401e7f3d07015619adf44c13998e13e777f9/Internet/DHCP/dhcp.c
+https://github.com/Wiznet/ioLibrary_Driver/blob/b981401e7f3d07015619adf44c13998e13e777f9/Internet/DHCP/dhcp.h
+
+Copyright (c) 2013, WIZnet Co., LTD.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+ * Neither the name of the <ORGANIZATION> nor the names of its
+contributors may be used to endorse or promote products derived
+from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+THE POSSIBILITY OF SUCH DAMAGE.
+
+SPDX-License-Identifier: BSD-3-Clause
+
+-----------------------------------------------------------------------------
+https://github.com/Wiznet/ioLibrary_Driver/blob/b981401e7f3d07015619adf44c13998e13e777f9/license.txt
+
+Copyright (c) 2014 WIZnet Co.,Ltd.
+Copyright (c) WIZnet ioLibrary Project.
+All rights reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+SPDX-License-Identifier: MIT
diff --git a/3rd-party/linux-errno.txt b/3rd-party/linux-errno.txt
index 839efc2..5b450f0 100644
--- a/3rd-party/linux-errno.txt
+++ b/3rd-party/linux-errno.txt
@@ -1,4 +1,4 @@
-# 3rd-party/linux-errno.txt - Generated from build-aux/linux-errno.txt.gen and linux.git v6.7. DO NOT EDIT!
+# 3rd-party/linux-errno.txt - Generated from lib9p/linux-errno.txt.gen and linux.git v6.14. DO NOT EDIT!
1 EPERM Operation not permitted
2 ENOENT No such file or directory
3 ESRCH No such process
diff --git a/3rd-party/pico-fmt b/3rd-party/pico-fmt
deleted file mode 160000
-Subproject beaecdcba7fdf0d584d245a9d0ad6be6bdc94a1
diff --git a/3rd-party/pico-sdk b/3rd-party/pico-sdk
-Subproject 1c00d64a4e0fdf948494c9aaf4d257b5739796a
+Subproject bbad146d79b42081d85b76e44f136ff8d42f1a1
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 22756c1..2d40b05 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -14,8 +14,6 @@ include("${PICO_SDK_PATH}/external/pico_sdk_import.cmake")
project(sbc_harness)
-add_subdirectory(3rd-party/pico-fmt/pico_fmt)
-add_subdirectory(3rd-party/pico-fmt/pico_printf)
pico_sdk_init()
if ((PICO_PLATFORM STREQUAL "host") AND (NOT PICO_NO_GC_SECTIONS))
@@ -24,13 +22,18 @@ if ((PICO_PLATFORM STREQUAL "host") AND (NOT PICO_NO_GC_SECTIONS))
add_link_options("LINKER:--gc-sections")
endif()
+# Use modern C...
+set(CMAKE_C_STANDARD 23)
+# ... but with some misfeatures disabled.
+add_compile_options(-Werror=vla)
+
+# Have the compiler help detect mistakes.
add_compile_options(-Wall -Wextra -Wswitch-enum -Werror)
string(TOUPPER "${CMAKE_BUILD_TYPE}" _upper_cmake_build_type)
string(REPLACE " " ";" _build_type_flags "${CMAKE_C_FLAGS_${_upper_cmake_build_type}}")
if ("-DNDEBUG" IN_LIST _build_type_flags)
add_compile_options(-Wno-unused-variable -Wno-unused-parameter -Wno-unused-but-set-variable)
- target_compile_definitions(pico_printf INTERFACE PICO_PRINTF_ALWAYS_INCLUDED=1)
endif()
function(_suppress_tinyusb_warnings)
@@ -41,6 +44,11 @@ function(_suppress_tinyusb_warnings)
COMPILE_OPTIONS "-Wno-switch-enum")
endfunction()
+if (PICO_PLATFORM STREQUAL "host")
+ add_compile_options(--coverage)
+ add_link_options(--coverage)
+endif()
+
function(target_embed_sources arg_compile_target arg_link_target arg_hdrname)
set(embed_objs)
foreach(embed_src IN LISTS ARGN)
@@ -112,14 +120,41 @@ function(add_lib_test arg_libname arg_testname)
target_include_directories("${arg_testname}" PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/tests)
add_test(
NAME "${arg_libname}/${arg_testname}"
- COMMAND valgrind --error-exitcode=2 "./${arg_testname}"
+ COMMAND "${CMAKE_SOURCE_DIR}/build-aux/valgrind" "./${arg_testname}"
)
endif()
endfunction()
+function(_apply_matrix_helper _m_depth _m_assignments)
+ list(LENGTH _m_arg_matrix _m_dimensions)
+ math(EXPR _m_dimensions ${_m_dimensions}/2)
+ if("${_m_depth}" EQUAL "${_m_dimensions}")
+ cmake_language(CALL "${_m_arg_action}" "${_m_n}" "${_m_assignments}")
+ math(EXPR _m_n "${_m_n}+1")
+ set(_m_n "${_m_n}" PARENT_SCOPE)
+ else()
+ math(EXPR _m_ik "${_m_depth}*2")
+ list(GET _m_arg_matrix "${_m_ik}" _m_tmp_key)
+
+ math(EXPR _m_iv "${_m_ik}+1")
+ list(GET _m_arg_matrix "${_m_iv}" _m_tmp_vals)
+ string(REGEX REPLACE "^\\[(.*)\\]$" "\\1" _m_tmp_vals "${_m_tmp_vals}")
+
+ foreach(_m_tmp_val IN LISTS _m_tmp_vals)
+ math(EXPR _m_tmp_depth "${_m_depth}+1")
+ set(_m_tmp_assignments "${_m_assignments}")
+ list(APPEND _m_tmp_assignments "${_m_tmp_key}=${_m_tmp_val}")
+ _apply_matrix_helper("${_m_tmp_depth}" "${_m_tmp_assignments}")
+ set(_m_n "${_m_n}" PARENT_SCOPE)
+ endforeach()
+ endif()
+endfunction()
+function(apply_matrix _m_arg_action _m_arg_matrix)
+ set(_m_n 0)
+ _apply_matrix_helper(0 "")
+endfunction()
+
add_subdirectory(libmisc)
-add_subdirectory(libobj)
-add_subdirectory(libfmt)
add_subdirectory(libcr)
add_subdirectory(libcr_ipc)
add_subdirectory(libhw_generic)
@@ -129,4 +164,5 @@ add_subdirectory(libusb)
add_subdirectory(lib9p)
add_subdirectory(lib9p_util)
-add_subdirectory(cmd/sbc_harness)
+add_subdirectory(flashimg/cpu_main)
+add_subdirectory(flashimg/cpu_hdmi)
diff --git a/GNUmakefile b/GNUmakefile
index f6ff6c3..272306a 100644
--- a/GNUmakefile
+++ b/GNUmakefile
@@ -33,20 +33,32 @@ generate/files += 3rd-party/COPYING.newlib.txt
3rd-party/COPYING.newlib.txt: /usr/share/licenses/arm-none-eabi-newlib/COPYING.NEWLIB
cp $< $@
+generate/files += libmisc/tests/test_obj_autobox.c
+libmisc/tests/test_obj_autobox.c: %: %.gen libmisc/tests/test_obj_nest.c
+ $^ $@
+
generate/files += 3rd-party/linux-errno.txt
-3rd-party/linux-errno.txt: build-aux/linux-errno.txt.gen
+3rd-party/linux-errno.txt: lib9p/linux-errno.txt.gen
$< $(linux.git) $@
-generate/files += lib9p/include/lib9p/linux-errno.h
-lib9p/include/lib9p/linux-errno.h: %: %.gen 3rd-party/linux-errno.txt
+generate/files += libmisc/error_generated.c
+libmisc/error_generated.c: %: %.gen libmisc/include/libmisc/error.h
+ $^ $@
+
+generate/files += lib9p/idl/2010-9P2000.L.9p
+lib9p/idl/2010-9P2000.L.9p: %: %.gen 3rd-party/linux-errno.txt
$^ >$@
-generate/files += lib9p/9p.generated.c lib9p/include/lib9p/9p.generated.h
-lib9p/9p.generated.c lib9p/include/lib9p/9p.generated.h &: lib9p/proto.gen lib9p/idl/__init__.py lib9p/protogen lib9p/protogen/*.py lib9p/idl lib9p/idl/*.9p
- $< $(filter %.9p,$^)
+generate/files += lib9p/core_generated.c lib9p/core_include/lib9p/_core_generated.h
+lib9p/core_generated.c lib9p/core_include/lib9p/_core_generated.h &: lib9p/core.gen lib9p/idl/__init__.py lib9p/core_gen lib9p/core_gen/*.py lib9p/idl lib9p/idl/2010-9P2000.L.9p lib9p/idl/*.9p
+ $< $(sort $(filter %.9p,$^))
+
+generate/files += lib9p/srv_generated.c
+lib9p/srv_generated.c: %: %.gen libmisc/include/libmisc/error.h
+ $^ $@
generate/files += lib9p/tests/test_compile.c
-lib9p/tests/test_compile.c: %: %.gen lib9p/include/lib9p/9p.generated.h
+lib9p/tests/test_compile.c: %: %.gen lib9p/core_include/lib9p/_core_generated.h
$^ $@
generate/files += libusb/include/libusb/tusb_helpers.h 3rd-party/MS-LCID.pdf 3rd-party/MS-LCID.txt
@@ -55,18 +67,30 @@ libusb/include/libusb/tusb_helpers.h 3rd-party/MS-LCID.pdf 3rd-party/MS-LCID.txt
generate/files += build-aux/sources.mk
ifeq ($(INNER),)
+nonsource/files = $(generate/files)
+nonsource/files += 3rd-party/COPYING.wiznet-dhcp.txt
+# 100644 blob/regular file
+# 100755 blob/executable file
+# 120000 blob/symlink
+# 160000 commit (submodule)
+# 040000 tree (directory)
build-aux/sources.mk: $(if $(wildcard .git),FORCE)
- git ls-files | grep -vFx $(foreach f,$(generate/files),-e $f) \
- | sed 's,^,$(CURDIR)/,' | xargs editorconfig \
- | sed -nE -e 's,\[$(CURDIR)/(.*)\],\1,p' -e 's/^_mode=//p' \
- | sed -E '{N;s/(.*)\n(.*)/sources_\2 += \1/;}' \
- | sort \
+ git ls-files --format='%(objectmode) %(path)' \
+ | sed -n 's/^100... //p' \
+ | grep -vFx $(foreach f,$(nonsource/files),-e $f) \
+ | sed 's,^,$(CURDIR)/,' | xargs editorconfig \
+ | sed -nE -e 's,\[$(CURDIR)/(.*)\],\1,p' -e 's/^_mode=//p' \
+ | sed -E '{N;s/(.*)\n(.*)/sources_\2 += \1/;}' \
+ | sort \
>$@.tmp
if ! cmp -s $@.tmp $@; then mv $@.tmp $@; fi
@echo '################################################################################'
endif
+generate:
+ifeq ($(INNER),)
generate: $(generate/files)
+endif
.PHONY: generate
generate-clean:
@@ -75,29 +99,50 @@ generate-clean:
# `build` and `check` ##########################################################
-platforms := rp2040 host # $(shell sed -nE 's/if *\(PICO_PLATFORM STREQUAL "(.*)"\)/\1/p' cmd/*/CMakeLists.txt)
+# define the matrix
+
+platforms_build = rp2040
+platforms_check = host
+platforms = $(platforms_build) $(platforms_check)
build_types = Debug Release RelWithDebInfo MinSizeRel
-export CTEST_PARALLEL_LEVEL = 0
-export CTEST_OUTPUT_ON_FAILURE = 1
+# span the matrix
-build: $(foreach t,$(build_types),$(foreach p,$(platforms),build/$p-$t/build))
+build: $(foreach t,$(build_types),$(foreach p,$(platforms_build),build/$p-$t/build))
.PHONY: build
+build-check: $(foreach t,$(build_types),$(foreach p,$(platforms_check),build/$p-$t/build))
+.PHONY: build-check
+
+check: generate
+ $(MAKE) -k INNER=t $(foreach t,$(build_types),$(foreach p,$(platforms_check),build/$p-$t/check))
+.PHONY: check
+
+# define the cells
+
+# build/{matrix}/Makefile
$(foreach t,$(build_types),$(foreach p,$(platforms),build/$p-$t/Makefile)): build/%/Makefile:
mkdir -p $(@D) && cd $(@D) && cmake -DPICO_PLATFORM=$(firstword $(subst -, ,$*)) -DCMAKE_BUILD_TYPE=$(lastword $(subst -, ,$*)) ../..
+# build/{matrix}/build
$(foreach t,$(build_types),$(foreach p,$(platforms),build/$p-$t/build)): build/%/build: build/%/Makefile generate
$(MAKE) -C $(<D)
.PHONY: $(foreach t,$(build_types),$(foreach p,$(platforms),build/$p-$t/build))
-check: build
- $(MAKE) -j1 -k INNER=t $(foreach t,$(build_types),$(foreach p,$(platforms),build/$p-$t/check))
-.PHONY: check
-
-$(foreach t,$(build_types),$(foreach p,$(platforms),build/$p-$t/check)): build/%/check: build/%/Makefile
- $(MAKE) -C $(<D) test
-.PHONY: $(foreach t,$(build_types),$(foreach p,$(platforms),build/$p-$t/check))
+# build/{matrix}/check
+#
+# `gcc` writes .gcno
+# Running the program writes .gcda (updates existing files, concurrent-safe)
+# GCC `gcov` post-processes .gcno+.gcda to .gcov
+# `gcovr` is a Python script that calls `gcov` and merges and post-processes the .gcov files to other formats
+gcovr_flags = --txt=$(@D)/coverage.txt
+gcovr_flags += --html=$(@D)/coverage.html --html-details --html-single-page=js-enabled
+gcovr_flags += --sort uncovered-number --sort-reverse
+$(foreach t,$(build_types),$(foreach p,$(platforms_check),build/$p-$t/check)): build/%/check: build/%/build
+ ./build-aux/gcov-prune $(@D)
+ +cd $(@D) && ctest --output-on-failure $(if $(filter --jobserver-auth=%,$(MAKEFLAGS)),--parallel)
+ gcovr $(gcovr_flags) -- $(@D)
+.PHONY: $(foreach t,$(build_types),$(foreach p,$(platforms_check),build/$p-$t/check))
# `lint` and `format` ##########################################################
@@ -109,44 +154,30 @@ build-aux/venv: build-aux/requirements.txt
$@/bin/pip install -r $<
touch --no-create $@
-# `lint` ###########
-lint:
- $(MAKE) -k INNER=t $(patsubst sources_%,lint/%,$(filter sources_%,$(.VARIABLES)))
-# Only lint binaries if the build scripts pass lint.
- $(MAKE) INNER=t lint/bin
+flashimgs = $(patsubst flashimg/%/CMakeLists.txt,%,$(filter flashimg/%/CMakeLists.txt,$(sources_cmake)))
+
+# `lint` ###############################
+lint: lint/src .WAIT lint/bin
+lint/src:
+ $(MAKE) -k INNER=t $(patsubst sources_%,lint/%,$(filter-out sources_all,$(filter sources_%,$(.VARIABLES))))
lint/bin: build build-aux/lint-bin
- ./build-aux/lint-bin $(foreach t,$(build_types),build/rp2040-$t/cmd/sbc_harness/sbc_harness.elf)
-lint/sh lint/bash: lint/%:
- shellcheck $(sources_$*)
-lint/python3: lint/%: build-aux/venv
- ./build-aux/venv/bin/mypy --strict --scripts-are-modules $(sources_$*)
- ./build-aux/venv/bin/black --check $(sources_$*)
- ./build-aux/venv/bin/isort --check $(sources_$*)
- ./build-aux/venv/bin/pylint $(sources_$*)
- ! grep -nh 'SPECIAL$$' -- lib9p/proto.gen lib9p/protogen/*.py
- ./build-aux/venv/bin/pytest $(foreach f,$(sources_python3),$(if $(filter test_%.py,$(notdir $f)),$f))
-lint/c: lint/%: build-aux/lint-h build-aux/get-dscname
- ./build-aux/lint-h $(filter %.h,$(sources_$*))
-lint/make lint/cmake lint/gitignore lint/ini lint/9p lint/markdown lint/pip lint/man-cat: lint/%:
- @:
-lint/unknown: lint/%: build-aux/lint-unknown
- ./build-aux/lint-unknown $(sources_$*)
-lint/all: lint/%: build-aux/lint-generic build-aux/get-dscname
- ./build-aux/lint-generic '%s\n' $(sources_$*)
+ ./build-aux/lint-bin $(foreach i,$(flashimgs),$(foreach t,$(build_types),$(foreach p,$(platforms_build),build/$p-$t/flashimg/$i/$i.elf)))
+$(patsubst sources_%,lint/%,$(filter-out sources_all,$(filter sources_%,$(.VARIABLES)))): build-aux/lint-src
+ ./build-aux/lint-src $(@F) $(sources_$(@F))
+lint/python3: build-aux/venv
.PHONY: lint lint/%
-# `format` #########
-format:
- $(MAKE) -k INNER=t $(patsubst sources_%,format/%,$(filter-out sources_all,$(filter sources_%,$(.VARIABLES))))
-format/python3: format/%: ./build-aux/venv
- ./build-aux/venv/bin/black $(sources_$*)
- ./build-aux/venv/bin/isort $(sources_$*)
-format/sh format/bash: format/%
- @:
+# `format` #############################
+# generic ##########
+format: $(patsubst sources_%,format/%,$(filter-out sources_all sources_unknown,$(filter sources_%,$(.VARIABLES))))
+.PHONY: format format/%
+# specific #########
format/c: format/%:
@: TODO: Adopt a C code-formatter
-format/make format/cmake format/gitignore format/ini format/9p format/markdown format/pip format/man-cat: format/%:
- @:
-format/unknown: format/%:
- @:
-.PHONY: format format/%
+format/sh format/bash: format/%:
+ shfmt --write --case-indent --simplify $(sources_$(@F))
+format/python3: ./build-aux/venv
+ ./build-aux/venv/bin/black $(sources_$(@F))
+ ./build-aux/venv/bin/isort $(sources_$(@F))
+format/make format/cmake format/gitignore format/ini format/9p-idl format/9p-log format/markdown format/pip format/man-cat:
+ @: TODO: Write/adopt formatters for these file types
diff --git a/HACKING.md b/HACKING.md
index 9e19c26..04835f1 100644
--- a/HACKING.md
+++ b/HACKING.md
@@ -7,11 +7,10 @@
# Source Layout
-Our own "flavor" of C: GNU C plus `-fplan9-extensions`, making use of:
+Our own "flavor" of C: GNU C, plus making use of:
- - `libobj/`: For Go-like object-oriented programming
- - `libmisc/`: Low-level C programming utilities; sort of an augmented
- "libc"
+ - `libmisc/`: Low-level C programming utilities (including Go-like
+ object-oriented programming); sort of an augmented "libc"
Our own tiny cooperative-multitasking OS:
@@ -28,7 +27,7 @@ Libraries for generic parts of what the harness is doing:
- `libdhcp/`: A DHCP client
- `libusb/`: Wrapper and utilities for TinyUSB
- - `cmd/sbc_harness/`: The main firmware image
+ - `flashimg/cpu_main/`: The main firmware image
- `3rd-party/`: Sources from third parties; some definitions of
things (USB language codes, Linux kernel errnos), and the Pico-SDK
(for the RP2040 CPU, and its included TinyUSB). Does not include
@@ -154,7 +153,7 @@ Example use:
> - `openocd -f interface/cmsis-dap.cfg -c 'set USE_CORE 0' -f target/rp2040.cfg -c "adapter speed 5000"`
> (Explanation of `USE_CORE`: https://github.com/raspberrypi/debugprobe/issues/45)
> In another terminal:
-> - `arm-none-eabi-gdb ./build/rp2040-Debug/cmd/sbc_harness/sbc_harness.elf`
+> - `arm-none-eabi-gdb ./build/rp2040-Debug/flashimg/cpu_main/cpu_main.elf`
> ```
> target extended-remote localhost:3333
> monitor reset init
diff --git a/PLAN.md b/PLAN.md
deleted file mode 100644
index 2bae3fd..0000000
--- a/PLAN.md
+++ /dev/null
@@ -1,23 +0,0 @@
-<!--
- PLAN.md - Misc planning notes
-
- Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com>
- SPDX-License-Identifier: AGPL-3.0-or-later
--->
-
-- with hardware I have:
- 1. [X] type "hello world" as a USB keyboard
- 2. [ ] get networking up (ping)
- 3. [ ] forward port 21 as a USB keyboard
- 4. [ ] forward a 9p file as a USB keyboard
- 5. [ ] connect UART as a 9p socket
-- waiting on hardware:
- - sdcard slot:
- 1. [ ] whatever the "hello world" of SD is
- 2. [ ] as a device on 9p
- - HDMI socket
- 1. [ ] PicoDVI hello-world
- 2. [ ] "PiciDVI" to network
- 3. [ ] reverse the flow of PicoDVI
-
-https://hackaday.com/2022/08/26/bit-banged-ethernet-on-the-raspberry-pi-pico/
diff --git a/README.md b/README.md
index de909d3..8b7cb10 100644
--- a/README.md
+++ b/README.md
@@ -21,7 +21,7 @@ At the time of this writing, on Parabola GNU/Linux-libre that means:
- picotool 2.1.1-1
Then, simply run `make`. This will create
-`build/rp2040-*/cmd/sbc_harness/sbc_harness.{elf,bin,hex,uf2}` files:
+`build/rp2040-*/flashimg/cpu_main/cpu_main.{elf,bin,hex,uf2}` files:
- The `.elf` is the firmware image plus debugger symbols and
relocation data.
@@ -48,17 +48,19 @@ There are several ways of putting this firmware file onto the harness:
harness will appear to a host PC as a USB storage device. Simply
mount the device and copy the `.uf2` file to the device. It will
automatically reboot into the new firmware image.
- 2. debug port: Using OpenOCD (see `HACKING.md`), run the OpenOCD command
- `program /path/to/sbc_harness.elf reset`.
+ 2. debug port: Using OpenOCD (see `HACKING.md`), run the OpenOCD
+ command `program /path/to/cpu_main.elf reset` (TODO: I don't
+ really understand what OpenOCD is doing that it wants the `.elf`
+ instead of the `.bin`) .
If OpenOCD is not already running:
```
- openocd -f interface/cmsis-dap.cfg -f target/rp2040.cfg -c "program $PWD/build/rp2040-Debug/cmd/sbc_harness/sbc_harness.elf reset exit"`
+ openocd -f interface/cmsis-dap.cfg -f target/rp2040.cfg -c 'adapter speed 5000' -c "program $PWD/build/rp2040-Debug/flashimg/cpu_main/cpu_main.elf reset exit"
```
If OpenOCD is already running:
```
- socat STDIO TCP:localhost:4444 <<<"program $PWD/build/rp2040-Debug/cmd/sbc_harness/sbc_harness.elf reset"
+ socat STDIO TCP:localhost:4444 <<<"program $PWD/build/rp2040-Debug/flashimg/cpu_main/cpu_main.elf reset"
```
3. Use `flashprog` or `flashrom` and a SOIC-8 clip to directly
program the flash chip. I'm not sure why you would do this
@@ -74,9 +76,9 @@ protocol over TCP:
- `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, as of Linux v6.7, 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
+ errnos on most architectures (but, as of Linux v6.14, not on
+ Alpha, MIPS, PA-RISC, PowerPC, or SPARC; unfortunately on these
+ platforms the kernel's v9fs filesystem driver won't 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
diff --git a/build-aux/embed-sources.h.gen b/build-aux/embed-sources.h.gen
index 0329496..ee9eb42 100755
--- a/build-aux/embed-sources.h.gen
+++ b/build-aux/embed-sources.h.gen
@@ -6,5 +6,5 @@
nm --format=posix "$@" |
sed -n -E \
- -e 's/(.*_(end|start)) [DR] .*/extern char \1[];/p' \
- -e 's/(.*_size) A .*/extern size_t \1;/p'
+ -e 's/(.*_(end|start)) [DR] .*/extern char \1[];/p' \
+ -e 's/(.*_size) A .*/extern size_t \1;/p'
diff --git a/build-aux/gcov-prune b/build-aux/gcov-prune
new file mode 100755
index 0000000..dc190a9
--- /dev/null
+++ b/build-aux/gcov-prune
@@ -0,0 +1,33 @@
+#!/usr/bin/env bash
+# build-aux/gcov-prune - Prune old GCC coverage files
+#
+# Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+# SPDX-License-Identifier: AGPL-3.0-or-later
+
+set -e
+
+[[ $# == 1 ]]
+
+sourcedir="$(realpath -- .)"
+builddir="$(realpath -- "$1")"
+
+# `gcc` writes .gcno
+# Running the program writes .gcda (updates existing files, concurrent-safe)
+# GCC `gcov` post-processes .gcno+.gcda to .gcov
+# `gcovr` is a Python script that calls `gcov` and merges and post-processes the .gcov files to other formats
+
+# Prune orphaned .gcno files.
+find "$builddir" -name '*.gcno' -printf '%P\0' | while read -d '' -r gcno_file; do
+ rel_base="${gcno_file%/CMakeFiles/*}"
+ src_file="$gcno_file"
+ src_file="${src_file#*/CMakeFiles/*.dir/}"
+ src_file="${src_file%.gcno}"
+ src_file="${src_file//__/..}"
+ src_file="$rel_base/$src_file"
+ if [[ ! -e "$sourcedir/$src_file" || "$sourcedir/$src_file" -nt "$builddir/$gcno_file" ]]; then
+ rm -fv -- "$builddir/$gcno_file"
+ fi
+done
+
+# Prune all .gcda files.
+find "$builddir" -name '*.gcda' -delete
diff --git a/build-aux/get-dscname b/build-aux/get-dscname
deleted file mode 100755
index 34a1b08..0000000
--- a/build-aux/get-dscname
+++ /dev/null
@@ -1,36 +0,0 @@
-#!/usr/bin/env bash
-# build-aux/get-dscname - Get a file's self-described filename
-#
-# Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
-# SPDX-License-Identifier: AGPL-3.0-or-later
-
-if [ $# -ne 1 ]; then
- echo "$0: expected exactly 1 argument"
- exit 2
-fi
-
-if [[ $1 == */Documentation/* ]] && [[ "$(sed 1q -- "$1")" == 'NAME' ]]; then
- sed -n '
- 2{
- s,[/.],_,g;
- s,^\s*_,Documentation/,;
- s,$,.txt,;
-
- p;
- q;
- }
- ' -- "$1"
-else
- sed -n '
- 1,3{
- /^\#!/d;
- /^<!--$/d;
- /-\*- .* -\*-/d;
- s,[/*\# ]*,,;
- s/ - .*//;
-
- p;
- q;
- }
- ' -- "$1"
-fi
diff --git a/build-aux/lint-bin b/build-aux/lint-bin
index 78ed19f..3b9eb4b 100755
--- a/build-aux/lint-bin
+++ b/build-aux/lint-bin
@@ -18,7 +18,7 @@ shopt -s extglob
# Textual info:
# - ${elf%.elf}.dis : `objdump --section-headers ${elf}; objdump --disassemble ${elf}; picotool coprodis --quiet ${elf}`
# - ${elf}.map : `ld --print-map` info
-# - ${elf%.elf}_stack.c : `stack.c.gen`
+# - stack.c : `stack.c.gen`
RED=$(tput setaf 1)
RESET=$(tput sgr0)
@@ -62,7 +62,7 @@ lint_globals() {
cd "$rel_base"
total=0
while read -r symbol addr size source; do
- if (( addr == 0 )); then
+ if ((addr == 0)); then
continue
fi
case "$source" in
@@ -97,14 +97,15 @@ lint_stack() {
while read -r line; do
func=${line#$'\t'}
if [[ $line == $'\t'* ]]; then
- err "$in_elffile" "function in binary but not _stack.c: ${func}"
+ err "$in_elffile" "function in binary but not stack.c: ${func}"
else
- err "$in_elffile" "function in _stack.c but not binary: ${func}"
+ err "$in_elffile" "function in stack.c but not binary: ${func}"
fi
done < <(
comm -3 \
- <(sed -En 's/^included: (.*:)?//p' "${in_elffile%.elf}_stack.c" | sort -u) \
- <(readelf_funcs "$in_elffile" | sed -E -e 's/\.part\.[0-9]*$//' -e 's/^__(.*)_veneer$/\1/' | sort -u))
+ <(sed -En 's/^included: (.*:)?//p' "${in_elffile%/*}/stack.c" | sort -u) \
+ <(readelf_funcs "$in_elffile" | sed -E -e 's/\.part\.[0-9]*$//' -e 's/^__(.*)_veneer$/\1/' | sort -u)
+ )
}
lint_func_blocklist() {
@@ -113,13 +114,14 @@ lint_func_blocklist() {
local blocklist=(
gpio_default_irq_handler
+ {,__wrap,weak_raw_,stdio_,_}{,v}{,sn}printf
)
while read -r func; do
err "$in_elffile" "Contains blocklisted function: ${func}"
done < <(readelf --syms --wide -- "$in_elffile" |
- awk '$4 == "FUNC" { print $8 }' |
- grep -Fx "${blocklist[@]/#/-e}")
+ awk '$4 == "FUNC" { print $8 }' |
+ grep -Fx "${blocklist[@]/#/-e}")
}
main() {
@@ -130,8 +132,14 @@ main() {
{
echo 'Global variables:'
lint_globals "${elf}.map" | sed 's/^/ /'
- } > "${elf%.elf}.lint.globals"
- (lint_stack "$elf") &> "${elf%.elf}.lint.stack"
+ echo
+ heap=$(grep -B1 'HeapLimit =' -- "${elf}.map" |
+ sed -E -e 's/^\s*(\.heap\s*)?0x/0x/' -e 's/\s.*//' |
+ sed -E -e '1{N;s/(.*)\n(.*)/\2-\1/;}' -e 's/.*/print(&)/' |
+ python)
+ printf "Left for heap: 0x%04x (%'d)\n" "$heap" "$heap"
+ } >"${elf%.elf}.lint.globals"
+ (lint_stack "$elf") &>"${elf%.elf}.lint.stack"
lint_func_blocklist "$elf"
done
diff --git a/build-aux/lint-generic b/build-aux/lint-generic
deleted file mode 100755
index 58a65d2..0000000
--- a/build-aux/lint-generic
+++ /dev/null
@@ -1,55 +0,0 @@
-#!/bin/sh
-# build-aux/lint-generic - Non-language-specific lint checks
-#
-# Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
-# SPDX-License-Identifier: AGPL-3.0-or-later
-
-RED=$(tput setaf 1)
-RESET=$(tput sgr0)
-
-err() {
- printf "${RED}%s${RESET}: %s\n" "$1" "$2" >&2
- r=1
-}
-
-r=0
-for filename in "$@"; do
- if ! { [ -f "$filename" ] && ! [ -h "$filename" ]; }; then
- # Ignore non-files
- continue
- fi
-
- # File header ##########################################################
-
- shebang="$(sed -n '1{/^#!/{/^#!\/hint\//q; p;};}' "$filename")"
- if [ -x "$filename" ] && [ -z "$shebang" ]; then
- err "$filename" 'is executable but does not have a shebang'
- elif [ -n "$shebang" ] && ! [ -x "$filename" ]; then
- err "$filename" 'has a shebang but is executable'
- fi
-
- if ! grep -E -q 'Copyright \(C\) 202[4-9]((-|, )202[5-9])* Luke T. Shumaker' "$filename"; then
- err "$filename" 'is missing a copyright statement'
- fi
- if ! grep -q '\sSPDX-License-Identifier[:] ' "$filename"; then
- err "$filename" 'is missing an SPDX-License-Identifier'
- fi
-
- dscname_act=$(./build-aux/get-dscname "$filename")
- dscname_exp=$(echo "$filename" | sed \
- -e 's,.*/config/,,' \
- -e 's,.*/config\.h$,config.h,' \
- -e 's,.*include/,,' \
- -e 's,.*static/,,' \
- -e 's/\.wip$//')
- if [ "$dscname_act" != "$dscname_exp" ] && [ "cmd/$dscname_act" != "$dscname_exp" ]; then
- err "$filename" "self-identifies as $dscname_act (expected $dscname_exp)"
- fi
-
- # File body ############################################################
-
- if grep -n --color=auto "$(printf '\\S\t')" "$filename"; then
- err "$filename" 'uses tabs for alignment'
- fi
-done
-exit $r
diff --git a/build-aux/lint-h b/build-aux/lint-h
deleted file mode 100755
index 26ac13d..0000000
--- a/build-aux/lint-h
+++ /dev/null
@@ -1,27 +0,0 @@
-#!/usr/bin/env bash
-# build-aux/lint-h - Lint checks for C header files
-#
-# Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
-# SPDX-License-Identifier: AGPL-3.0-or-later
-
-RED=$(tput setaf 1)
-RESET=$(tput sgr0)
-
-err() {
- printf "${RED}%s${RESET}: %s\n" "$1" "$2" >&2
- r=1
-}
-
-r=0
-for filename in "$@"; do
- dscname=$(./build-aux/get-dscname "$filename")
- guard=${dscname//'/'/'_'}
- guard=${guard//'.'/'_'}
- guard="_${guard^^}_"
- if ! { grep -Fxq "#ifndef ${guard}" "$filename" &&
- grep -Fxq "#define ${guard}" "$filename" &&
- grep -Fxq "#endif /* ${guard} */" "$filename"; }; then
- err "$filename" "does not have ${guard} guard"
- fi
-done
-exit $r
diff --git a/build-aux/lint-src b/build-aux/lint-src
new file mode 100755
index 0000000..d536631
--- /dev/null
+++ b/build-aux/lint-src
@@ -0,0 +1,160 @@
+#!/usr/bin/env bash
+# build-aux/lint-src - Lint checks for source files
+#
+# Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+# SPDX-License-Identifier: AGPL-3.0-or-later
+
+RED=$(tput setaf 1)
+RESET=$(tput sgr0)
+
+err() {
+ printf "${RED}%s${RESET}: %s\n" "$1" "$2" >&2
+ r=1
+}
+
+# `get-dscname FILENAME` reads FILENAME and prints the name that the
+# comment at the top of the file self-identifies the file as.
+get-dscname() {
+ if [[ $1 == */Documentation/* && "$(sed 1q -- "$1")" == 'NAME' ]]; then
+ sed -n '
+ 2{
+ s,/,_,g;
+ s,^\s*_,Documentation/,;
+ s,$,.txt,;
+
+ p;
+ q;
+ }
+ ' -- "$1"
+ else
+ sed -n '
+ 1,3{
+ /^\#!/d;
+ /^<!--$/d;
+ /-\*- .* -\*-/d;
+ s,[/*\# ]*,,;
+ s/ - .*//;
+
+ p;
+ q;
+ }
+ ' -- "$1"
+ fi
+}
+
+{
+ filetype=$1
+ filenames=("${@:2}")
+
+ r=0
+ for filename in "${filenames[@]}"; do
+ # File header ##########################################################
+
+ shebang="$(sed -n '1{/^#!/p;}' "$filename")"
+ if [[ -x $filename && (-z $shebang || $shebang == '#!/hint/'*) ]]; then
+ err "$filename" 'is executable but does not have a shebang'
+ elif [[ (-n $shebang && $shebang != '#!/hint/'*) && ! -x $filename ]]; then
+ err "$filename" 'has a shebang but is not executable'
+ fi
+ case "$shebang" in
+ '') : ;;
+ '#!/bin/sh') : ;;
+ '#!/usr/bin/env bash') : ;;
+ '#!/usr/bin/env python3') : ;;
+ *) err "$filename" 'has an unrecognized shebang' ;;
+ esac
+ if [[ -n $shebang && $shebang != */"$filetype" && $shebang != *' '"$filetype" ]]; then
+ err "$filename" "wrong shebang for $filetype"
+ fi
+
+ if ! grep -E -q 'Copyright \(C\) 202[4-9]((-|, )202[5-9])* Luke T. Shumaker' "$filename"; then
+ err "$filename" 'is missing a copyright statement'
+ fi
+ if test -e .git && ! git diff --quiet milestone/2025-01-01 HEAD -- "$filename"; then
+ if ! grep -E -q 'Copyright \(C\) .*2025 Luke T. Shumaker' "$filename"; then
+ err "$filename" 'has an outdated copyright statement'
+ fi
+ fi
+ if ! grep -q '\sSPDX-License-Identifier[:] ' "$filename"; then
+ err "$filename" 'is missing an SPDX-License-Identifier'
+ fi
+
+ dscname_act=$(get-dscname "$filename")
+ dscname_exp=$(echo "$filename" | sed \
+ -e 's,.*include/,,' \
+ -e 's,.*static/,,' \
+ -e 's/\.wip$//')
+ if [[ $dscname_act != "$dscname_exp" ]]; then
+ err "$filename" "self-identifies as $dscname_act (expected $dscname_exp)"
+ fi
+
+ # File body ############################################################
+
+ if grep -n --color=auto $'\\S\t' "$filename"; then
+ err "$filename" 'uses tabs for alignment'
+ fi
+ done
+ case "$filetype" in
+ unknown)
+ for filename in "${filenames[@]}"; do
+ err "$filename" 'cannot lint unknown file type'
+ done
+ ;;
+ c)
+ for filename in "${filenames[@]}"; do
+ if [[ $filename == *.h ]]; then
+ dscname=$(get-dscname "$filename")
+ guard=$dscname
+ guard=${guard#*/config/}
+ if [[ $guard == */config.h ]]; then
+ guard=config.h
+ fi
+ guard=${guard//'/'/'_'}
+ guard=${guard//'.'/'_'}
+ guard="_${guard^^}_"
+ if ! { grep -Fxq "#ifndef ${guard}" "$filename" &&
+ grep -Fxq "#define ${guard}" "$filename" &&
+ grep -Fxq "#endif /* ${guard} */" "$filename"; }; then
+ err "$filename" "does not have ${guard} guard"
+ fi
+ if [[ $filename != libmisc/include/libmisc/obj.h ]] &&
+ grep -Fn --color=auto -e LO_IMPLEMENTATION_C -e LO_IMPLEMENTATION_STATIC "$filename"; then
+ err "$filename" "contains LO_IMPLEMENTATION_C and/or LO_IMPLEMENTATION_STATIC"
+ fi
+ fi
+ if [[ $filename == *.c ]]; then
+ if [[ $filename != libmisc/tests/test_obj.c ]] &&
+ grep -Fn --color=auto L_IMPLEMENTATION_H "$filename"; then
+ err "$filename" "contains LO_IMPLEMENTATION_H"
+ fi
+ fi
+ done
+ ;;
+ sh | bash)
+ shellcheck "${filenames[@]}" || exit $?
+ shfmt --diff --case-indent --simplify "${filenames[@]}" || exit $?
+ ;;
+ python3)
+ ./build-aux/venv/bin/mypy --strict --scripts-are-modules "${filenames[@]}" || exit $?
+ ./build-aux/venv/bin/black --check "${filenames[@]}" || exit $?
+ ./build-aux/venv/bin/isort --check "${filenames[@]}" || exit $?
+ ./build-aux/venv/bin/pylint "${filenames[@]}" || exit $?
+ if grep -nh 'SPECIAL$$' -- lib9p/core.gen lib9p/core_gen/*.py; then exit 1; fi
+ testfiles=()
+ for filename in "${filenames[@]}"; do
+ if [[ ${filename##*/} == test_*.py ]]; then
+ testfiles+=("$filename")
+ fi
+ done
+ ./build-aux/venv/bin/pytest "${testfiles[@]}" || exit $?
+ ;;
+ make | cmake | gitignore | ini | 9p-idl | 9p-log | markdown | pip | man-cat)
+ # TODO: Write/adopt linters for these file types
+ :
+ ;;
+ *)
+ err "$0" "unknown filetype: ${filetype}"
+ ;;
+ esac
+ exit $r
+}
diff --git a/build-aux/lint-unknown b/build-aux/lint-unknown
deleted file mode 100755
index dda9541..0000000
--- a/build-aux/lint-unknown
+++ /dev/null
@@ -1,24 +0,0 @@
-#!/bin/sh
-# build-aux/lint-unknown - Lint checks for unknown files
-#
-# Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
-# SPDX-License-Identifier: AGPL-3.0-or-later
-
-RED=$(tput setaf 1)
-RESET=$(tput sgr0)
-
-err() {
- printf "${RED}%s${RESET}: %s\n" "$1" "$2" >&2
- r=1
-}
-
-r=0
-for filename in "$@"; do
- if ! { [ -f "$filename" ] && ! [ -h "$filename" ]; }; then
- # Ignore non-files
- continue
- fi
-
- err "$filename" 'cannot lint unknown file type'
-done
-exit $r
diff --git a/build-aux/measurestack/analyze.py b/build-aux/measurestack/analyze.py
index a93874f..f151642 100644
--- a/build-aux/measurestack/analyze.py
+++ b/build-aux/measurestack/analyze.py
@@ -3,24 +3,103 @@
# Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
# SPDX-License-Identifier: AGPL-3.0-or-later
+import random
import re
import sys
import typing
from . import vcg
+# Whether to print "//dbg-cache:" on cache writes
+dbg_cache = False
+# Whether to print the graph in a /* comment */ before processing it
+dbg_dumpgraph = False
+# Whether to print "//dbg-nstatic:" lines that trace nstatic() execution
+dbg_nstatic = False
+# Whether to disable nstatic() caching (but does NOT disable any cache-related debug logging)
+dbg_nocache = False
+# Whether to sort things for consistently-ordered execution, or shuffle things to detect bugs
+dbg_sort: typing.Literal["unsorted", "sorted", "shuffled"] = "unsorted"
+
# pylint: disable=unused-variable
__all__ = [
"BaseName",
"QName",
"UsageKind",
"Node",
+ "maybe_sorted",
"AnalyzeResultVal",
"AnalyzeResultGroup",
"AnalyzeResult",
"analyze",
]
+
+def dumps(x: typing.Any, depth: int = 0, compact: bool = False) -> str:
+ match x:
+ case int() | str() | None:
+ return repr(x)
+ case dict():
+ if len(x) == 0:
+ return "{}"
+ ret = "{"
+ if not compact:
+ ret += "\n"
+ for k, v in x.items():
+ if not compact:
+ ret += "\t" * (depth + 1)
+ ret += dumps(k, depth + 1, True)
+ ret += ":"
+ if not compact:
+ ret += " "
+ ret += dumps(v, depth + 1, compact)
+ ret += ","
+ if not compact:
+ ret += "\n"
+ if not compact:
+ ret += "\t" * depth
+ ret += "}"
+ return ret
+ case list():
+ if len(x) == 0:
+ return "[]"
+ ret = "["
+ if not compact:
+ ret += "\n"
+ for v in x:
+ if not compact:
+ ret += "\t" * (depth + 1)
+ ret += dumps(v, depth + 1, compact)
+ ret += ","
+ if not compact:
+ ret += "\n"
+ if not compact:
+ ret += "\t" * depth
+ ret += "]"
+ return ret
+ case set():
+ if len(x) == 0:
+ return "set()"
+ ret = "{"
+ if not compact:
+ ret += "\n"
+ for v in x:
+ if not compact:
+ ret += "\t" * (depth + 1)
+ ret += dumps(v, depth + 1, compact)
+ ret += ","
+ if not compact:
+ ret += "\n"
+ if not compact:
+ ret += "\t" * depth
+ ret += "}"
+ return ret
+ case _:
+ if hasattr(x, "__dict__"):
+ return f"{x.__class__.__name__}(*{dumps(x.__dict__, depth, compact)})"
+ return f"TODO({x.__class__.__name__})"
+
+
# types ########################################################################
@@ -152,32 +231,44 @@ class AnalyzeResult(typing.NamedTuple):
class SkipModel(typing.NamedTuple):
"""Running the skipmodel calls `.fn(chain, ...)` with the chain
- consisting of the last `.nchain` items (if .nchain is an int), or
- the chain starting with the *last* occurance of `.nchain` (if
- .nchain is a collection). If the chain is not that long or does
- not contain a member of the collection, then .fn is not called and
- the call is *not* skipped.
+ consisting of the last few items of the input chain.
+
+ If `.nchain` is an int:
+
+ - the chain is the last `.nchain` items or the input chain. If
+ the input chain is not that long, then `.fn` is not called and
+ the call is *not* skipped.
+ If `.nchain` is a collection:
+
+ - the chain starts with the *last* occurance of `.nchain` in the
+ input chain. If the input chain does not contain a member of
+ the collection, then .fn is called with an empty chain.
"""
nchain: int | typing.Collection[BaseName]
- fn: typing.Callable[[typing.Sequence[QName], QName], bool]
-
- def __call__(self, chain: typing.Sequence[QName], call: QName) -> tuple[bool, int]:
- if isinstance(self.nchain, int):
- if len(chain) >= self.nchain:
- _chain = chain[-self.nchain :]
- return self.fn(_chain, call), len(_chain)
- else:
- for i in reversed(range(len(chain))):
- if chain[i].base() in self.nchain:
- _chain = chain[i - 1 :]
- return self.fn(_chain, call), len(_chain)
- return False, 0
+ fn: typing.Callable[[typing.Sequence[QName], Node, QName], bool]
+
+ def __call__(
+ self, chain: typing.Sequence[QName], node: Node, call: QName
+ ) -> tuple[bool, int]:
+ match self.nchain:
+ case int():
+ if len(chain) >= self.nchain:
+ _chain = chain[-self.nchain :]
+ return self.fn(_chain, node, call), len(_chain) + 1
+ return False, 0
+ case _:
+ for i in reversed(range(len(chain))):
+ if chain[i].base() in self.nchain:
+ _chain = chain[i:]
+ return self.fn(_chain, node, call), len(_chain) + 1
+ return self.fn([], node, call), 1
class Application(typing.Protocol):
def extra_nodes(self) -> typing.Collection[Node]: ...
+ def mutate_node(self, node: Node) -> None: ...
def indirect_callees(
self, elem: vcg.VCGElem
) -> tuple[typing.Collection[QName], bool]: ...
@@ -186,7 +277,7 @@ class Application(typing.Protocol):
# code #########################################################################
-re_node_label = re.compile(
+re_node_normal_label = re.compile(
r"(?P<funcname>[^\n]+)\n"
+ r"(?P<location>[^\n]+:[0-9]+:[0-9]+)\n"
+ r"(?P<nstatic>[0-9]+) bytes \((?P<usage_kind>static|dynamic|dynamic,bounded)\)\n"
@@ -194,6 +285,10 @@ re_node_label = re.compile(
+ r"(?:\n.*)*",
flags=re.MULTILINE,
)
+re_node_alias_label = re.compile(
+ r"(?P<funcname>[^\n]+)\n" + r"(?P<location>[^\n]+:[0-9]+:[0-9]+)",
+ flags=re.MULTILINE,
+)
class _Graph:
@@ -235,6 +330,39 @@ class _Graph:
return self._resolve_cache[funcname]
+if typing.TYPE_CHECKING:
+ from _typeshed import SupportsRichComparisonT as _T_sortable
+
+_T = typing.TypeVar("_T")
+
+
+@typing.overload
+def maybe_sorted(
+ unsorted: typing.Iterable["_T_sortable"], /, *, key: None = None
+) -> typing.Iterable["_T_sortable"]: ...
+@typing.overload
+def maybe_sorted(
+ unsorted: typing.Iterable[_T], /, *, key: typing.Callable[[_T], "_T_sortable"]
+) -> typing.Iterable[_T]: ...
+
+
+def maybe_sorted(
+ unsorted: typing.Iterable[_T],
+ /,
+ *,
+ key: typing.Callable[[_T], "_T_sortable"] | None = None,
+) -> typing.Iterable[_T]:
+ match dbg_sort:
+ case "unsorted":
+ return unsorted
+ case "sorted":
+ return sorted(unsorted, key=key) # type: ignore
+ case "shuffled":
+ ret = [*unsorted]
+ random.shuffle(ret)
+ return ret
+
+
def _make_graph(
ci_fnames: typing.Collection[str],
app: Application,
@@ -253,20 +381,44 @@ def _make_graph(
case "title":
node.funcname = QName(v)
case "label":
- if elem.attrs.get("shape", "") != "ellipse":
- m = re_node_label.fullmatch(v)
- if not m:
- raise ValueError(f"unexpected label value {v!r}")
- node.location = m.group("location")
- node.usage_kind = typing.cast(
- UsageKind, m.group("usage_kind")
- )
- node.nstatic = int(m.group("nstatic"))
- node.ndynamic = int(m.group("ndynamic"))
+ shape: str | None = elem.attrs.get("shape", None)
+ match shape:
+ case "ellipse": # external
+ pass
+ case "triangle": # alias (since GCC 15)
+ m = re_node_alias_label.fullmatch(v)
+ if not m:
+ raise ValueError(
+ f"unexpected label value {v!r}"
+ )
+ node.location = m.group("location")
+ node.usage_kind = "static"
+ node.nstatic = 0
+ node.ndynamic = 0
+ case None: # normal
+ m = re_node_normal_label.fullmatch(v)
+ if not m:
+ raise ValueError(
+ f"unexpected label value {v!r}"
+ )
+ node.location = m.group("location")
+ node.usage_kind = typing.cast(
+ UsageKind, m.group("usage_kind")
+ )
+ node.nstatic = int(m.group("nstatic"))
+ node.ndynamic = int(m.group("ndynamic"))
+ case _:
+ raise ValueError(
+ f"unexpected shape value {shape!r}"
+ )
case "shape":
- if v != "ellipse":
- raise ValueError(f"unexpected shape value {v!r}")
- skip = True
+ match v:
+ case "ellipse": # external
+ skip = True
+ case "triangle": # alias (since GCC 15)
+ pass
+ case _:
+ raise ValueError(f"unexpected shape value {v!r}")
case _:
raise ValueError(f"unknown edge key {k!r}")
if not skip:
@@ -297,7 +449,10 @@ def _make_graph(
raise ValueError(f"unknown caller: {caller}")
if callee == QName("__indirect_call"):
callees, missing_ok = app.indirect_callees(elem)
- for callee in callees:
+ assert (
+ len(callees) > 0
+ ), f"app returning 0 callees for {elem.attrs.get('label')} indicates the code would crash"
+ for callee in maybe_sorted(callees):
if callee not in graph[caller].calls:
graph[caller].calls[callee] = missing_ok
else:
@@ -305,16 +460,22 @@ def _make_graph(
case _:
raise ValueError(f"unknown elem type {elem.typ!r}")
- for ci_fname in ci_fnames:
+ for ci_fname in maybe_sorted(ci_fnames):
with open(ci_fname, "r", encoding="utf-8") as fh:
for elem in vcg.parse_vcg(fh):
handle_elem(elem)
- for node in app.extra_nodes():
+ def sort_key(node: Node) -> QName:
+ return node.funcname
+
+ for node in maybe_sorted(app.extra_nodes(), key=sort_key):
if node.funcname in graph:
raise ValueError(f"duplicate node {node.funcname}")
graph[node.funcname] = node
+ for node in graph.values():
+ app.mutate_node(node)
+
ret = _Graph()
ret.graph = graph
ret.qualified = {}
@@ -332,33 +493,33 @@ def analyze(
cfg_max_call_depth: int,
) -> AnalyzeResult:
graphdata = _make_graph(ci_fnames, app)
+ if dbg_dumpgraph:
+ print(f"/* {dumps(graphdata)} */")
missing: set[QName] = set()
dynamic: set[QName] = set()
included_funcs: set[QName] = set()
- dbg = False
-
track_inclusion: bool = True
skipmodels = app.skipmodels()
for name, model in skipmodels.items():
- if isinstance(model.nchain, int):
- assert model.nchain > 1
- else:
+ if not isinstance(model.nchain, int):
assert len(model.nchain) > 0
_nstatic_cache: dict[QName, int] = {}
def _nstatic(chain: list[QName], funcname: QName) -> tuple[int, int]:
- nonlocal dbg
nonlocal track_inclusion
assert funcname in graphdata.graph
+ def putdbg(msg: str) -> None:
+ print(f"//dbg-nstatic: {'- '*len(chain)}{msg}")
+
node = graphdata.graph[funcname]
- if dbg:
- print(f"//dbg: {'- '*len(chain)}{funcname}\t{node.nstatic}")
+ if dbg_nstatic:
+ putdbg(f"{funcname}\t{node.nstatic}")
if node.usage_kind == "dynamic" or node.ndynamic > 0:
dynamic.add(funcname)
if track_inclusion:
@@ -378,37 +539,52 @@ def analyze(
call_qname = graphdata.resolve_funcname(call_orig_qname)
if not call_qname:
if skipmodel:
- skip, _ = skipmodel(chain, call_orig_qname)
+ skip, _ = skipmodel(chain[:-1], node, call_orig_qname)
if skip:
- if dbg:
- print(
- f"//dbg: {'- '*len(chain)}{call_orig_qname}\tskip missing"
- )
+ if dbg_nstatic:
+ putdbg(f"{call_orig_qname}\tskip missing")
continue
if not call_missing_ok:
missing.add(call_orig_qname)
- if dbg:
- print(f"//dbg: {'- '*len(chain)}{call_orig_qname}\tmissing")
+ if dbg_nstatic:
+ putdbg(f"{call_orig_qname}\tmissing")
continue
# 2. Skip
if skipmodel:
- skip, skip_nchain = skipmodel(chain, call_qname)
+ skip, skip_nchain = skipmodel(chain[:-1], node, call_qname)
max_call_nchain = max(max_call_nchain, skip_nchain)
if skip:
- if dbg:
- print(f"//dbg: {'- '*len(chain)}{call_qname}\tskip")
+ if dbg_nstatic:
+ putdbg(f"{call_qname}\tskip")
continue
# 3. Call
- if skip_nchain == 0 and call_qname in _nstatic_cache:
- max_call_nstatic = max(max_call_nstatic, _nstatic_cache[call_qname])
+ if (
+ (not dbg_nocache)
+ and skip_nchain == 0
+ and call_qname in _nstatic_cache
+ ):
+ call_nstatic = _nstatic_cache[call_qname]
+ if dbg_nstatic:
+ putdbg(f"{call_qname}\ttotal={call_nstatic} (cache-read)")
+ max_call_nstatic = max(max_call_nstatic, call_nstatic)
else:
call_nstatic, call_nchain = _nstatic(chain, call_qname)
max_call_nstatic = max(max_call_nstatic, call_nstatic)
max_call_nchain = max(max_call_nchain, call_nchain)
if skip_nchain == 0 and call_nchain == 0:
- _nstatic_cache[call_qname] = call_nstatic
+ if dbg_nstatic:
+ putdbg(f"{call_qname}\ttotal={call_nstatic} (cache-write)")
+ if call_qname not in _nstatic_cache:
+ if dbg_cache:
+ print(f"//dbg-cache: {call_qname} = {call_nstatic}")
+ _nstatic_cache[call_qname] = call_nstatic
+ else:
+ assert dbg_nocache
+ assert _nstatic_cache[call_qname] == call_nstatic
+ elif dbg_nstatic:
+ putdbg(f"{call_qname}\ttotal={call_nstatic} (do-not-cache)")
chain.pop()
return node.nstatic + max_call_nstatic, max(0, max_call_nchain - 1)
diff --git a/build-aux/measurestack/app_main.py b/build-aux/measurestack/app_main.py
index 7573146..884aeee 100644
--- a/build-aux/measurestack/app_main.py
+++ b/build-aux/measurestack/app_main.py
@@ -27,8 +27,8 @@ def main(
# sbc-harness ####################################################
- libobj_plugin = app_plugins.LibObjPlugin(arg_c_fnames)
- lib9p_plugin = app_plugins.Lib9PPlugin(arg_base_dir, arg_c_fnames, libobj_plugin)
+ libmisc_plugin = app_plugins.LibMiscPlugin(arg_c_fnames)
+ lib9p_plugin = app_plugins.Lib9PPlugin(arg_base_dir, arg_c_fnames)
def sbc_is_thread(name: QName) -> int:
if str(name).endswith("_cr") and name.base() != BaseName("lib9p_srv_read_cr"):
@@ -47,13 +47,11 @@ def main(
plugins += [
app_plugins.CmdPlugin(),
- libobj_plugin,
- app_plugins.PicoFmtPlugin(arg_pico_platform),
- app_plugins.LibHWPlugin(arg_pico_platform, libobj_plugin),
+ libmisc_plugin,
+ app_plugins.LibHWPlugin(arg_pico_platform, libmisc_plugin),
app_plugins.LibCRPlugin(),
app_plugins.LibCRIPCPlugin(),
lib9p_plugin,
- app_plugins.LibMiscPlugin(),
]
# pico-sdk #######################################################
@@ -69,6 +67,7 @@ def main(
plugins += [
app_plugins.PicoSDKPlugin(
get_init_array=get_init_array,
+ PICO_PANIC_FUNCTION="assert_panic",
),
app_plugins.TinyUSBDevicePlugin(arg_c_fnames),
app_plugins.NewlibPlugin(),
@@ -89,12 +88,10 @@ def main(
def misc_filter(name: QName) -> tuple[int, bool]:
if name in [
QName("__assert_msg_fail"),
- QName("__lm_printf"),
- QName("__lm_light_printf"),
- QName("fmt_vfctprintf"),
- QName("fmt_vsnprintf"),
]:
return 1, False
+ if str(name.base()).endswith("_putb"):
+ return 1, False
return 0, False
extra_includes: list[BaseName] = []
diff --git a/build-aux/measurestack/app_output.py b/build-aux/measurestack/app_output.py
index 5336b85..5cf7d17 100644
--- a/build-aux/measurestack/app_output.py
+++ b/build-aux/measurestack/app_output.py
@@ -1,4 +1,4 @@
-# build-aux/measurestack/app_output.py - Generate `*_stack.c` files
+# build-aux/measurestack/app_output.py - Generate `stack.c` files
#
# Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
# SPDX-License-Identifier: AGPL-3.0-or-later
@@ -51,14 +51,14 @@ def print_group(
print(sep1)
-def next_power_of_2(x: int) -> int:
- return 1 << (x.bit_length())
+def lm_round_up(n: int, d: int) -> int:
+ return ((n + d - 1) // d) * d
def print_c(
result: analyze.AnalyzeResult, location_xform: typing.Callable[[QName], str]
) -> None:
- print("#include <stddef.h> /* for size_t */")
+ print('#include "config.h" /* for COROUTINE_STACK_* extern declarations */')
print()
print("/*")
print_group(result, location_xform, "Threads")
@@ -75,6 +75,9 @@ def print_c(
base: int
size: int
+ print("[[gnu::aligned]] void _bogus_aligned_fn(void) {};")
+ print("#define STACK_ALIGNED [[gnu::aligned(__alignof__(_bogus_aligned_fn))]]")
+
rows: list[CrRow] = []
mainrow: CrRow | None = None
for funcname, val in result.groups["Threads"].rows.items():
@@ -84,20 +87,20 @@ def print_c(
if name in ["main", "_entry_point"]:
mainrow = CrRow(name=name, cnt=1, base=base, size=size)
else:
- size = next_power_of_2(size + stack_guard_size) - stack_guard_size
+ size = lm_round_up(size + stack_guard_size, 512)
rows.append(CrRow(name=name, cnt=val.cnt, base=base, size=size))
- namelen = max(len(r.name) for r in rows)
+ namelen = max(len(f"{r.name}{r.cnt}" if r.cnt > 1 else r.name) for r in rows)
baselen = max(len(str(r.base)) for r in rows)
sizesum = sum(r.cnt * (r.size + stack_guard_size) for r in rows)
sizelen = len(str(max(sizesum, mainrow.size if mainrow else 0)))
def print_row(comment: bool, name: str, size: int, eqn: str | None = None) -> None:
- prefix = "const size_t CONFIG_COROUTINE_STACK_SIZE_"
+ prefix = "STACK_ALIGNED char COROUTINE_STACK_"
if comment:
print(f"/* {name}".ljust(len(prefix) + namelen), end="")
else:
print(f"{prefix}{name:<{namelen}}", end="")
- print(f" = {size:>{sizelen}};", end="")
+ print(f"[{size:>{sizelen}}];", end="")
if comment:
print(" */", end="")
elif eqn:
@@ -107,13 +110,15 @@ def print_c(
print()
for row in sorted(rows):
- print_row(
- False,
- row.name,
- row.size,
- f"LM_NEXT_POWER_OF_2({row.base:>{baselen}}+{intrstack}+{stack_guard_size})-{stack_guard_size}",
+ comment = (
+ f"LM_ROUND_UP({row.base:>{baselen}}+{intrstack}+{stack_guard_size}, 512)"
)
- print_row(True, "TOTAL (inc. stack guard)", sizesum)
+ if row.cnt > 1:
+ for i in range(row.cnt):
+ print_row(False, f"{row.name}{i}", row.size, comment)
+ else:
+ print_row(False, row.name, row.size, comment)
+ print_row(True, "TOTAL", sizesum)
if mainrow:
print_row(
True,
@@ -122,6 +127,19 @@ def print_c(
f" {mainrow.base:>{baselen}}+{intrstack}",
)
print()
+ for row in sorted(rows):
+ name = row.name
+ if row.cnt > 1:
+ name += "0"
+ print(f"char *const COROUTINE_STACK_{row.name}[{row.cnt}] = {{")
+ for i in range(row.cnt):
+ print(f"\tCOROUTINE_STACK_{row.name}{i},")
+ print("};")
+ print(
+ f"const size_t COROUTINE_STACK_{row.name}_len = sizeof(COROUTINE_STACK_{name});"
+ )
+
+ print()
print("/*")
print_group(result, location_xform, "Misc")
diff --git a/build-aux/measurestack/app_plugins.py b/build-aux/measurestack/app_plugins.py
index 36e661b..a921407 100644
--- a/build-aux/measurestack/app_plugins.py
+++ b/build-aux/measurestack/app_plugins.py
@@ -4,6 +4,7 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
import re
+import subprocess
import typing
from . import analyze, util
@@ -13,13 +14,11 @@ from .util import synthetic_node
# pylint: disable=unused-variable
__all__ = [
"CmdPlugin",
- "LibObjPlugin",
"LibHWPlugin",
"LibCRPlugin",
"LibCRIPCPlugin",
"Lib9PPlugin",
"LibMiscPlugin",
- "PicoFmtPlugin",
"PicoSDKPlugin",
"TinyUSBDevicePlugin",
"NewlibPlugin",
@@ -40,32 +39,54 @@ class CmdPlugin:
def extra_nodes(self) -> typing.Collection[Node]:
return []
+ def mutate_node(self, node: Node) -> None:
+ pass
+
def indirect_callees(
self, loc: str, line: str
) -> tuple[typing.Collection[QName], bool] | None:
if "/3rd-party/" in loc:
return None
if "srv->auth" in line:
- return [], False
+ return [QName("__indirect_call_with_null_check:srv->auth")], False
if "srv->rootdir" in line:
return [QName("get_root")], False
+ if "/ihex.c" in loc:
+ if "self->handle_data" in line:
+ return [QName("flash_handle_ihex_data")], False
+ if "self->handle_eof" in line:
+ return [QName("flash_handle_ihex_eof")], False
+ if "self->handle_set_exec_start_lin" in line:
+ return [
+ QName(
+ "__indirect_call_with_null_check:self->handle_set_exec_start_lin"
+ )
+ ], False
+ if "self->handle_set_exec_start_seg" in line:
+ return [
+ QName(
+ "__indirect_call_with_null_check:self->handle_set_exec_start_seg"
+ )
+ ], False
return None
def skipmodels(self) -> dict[BaseName, analyze.SkipModel]:
return {}
-re_comment = re.compile(r"/\*.*?\*/")
-re_ws = re.compile(r"\s+")
-re_lo_iface = re.compile(r"^\s*#\s*define\s+(?P<name>\S+)_LO_IFACE")
-re_lo_func = re.compile(r"LO_FUNC *\([^,]*, *(?P<name>[^,) ]+) *[,)]")
-re_lo_implementation = re.compile(
- r"^LO_IMPLEMENTATION_[HC]\s*\(\s*(?P<iface>[^, ]+)\s*,\s*(?P<impl_typ>[^,]+)\s*,\s*(?P<impl_name>[^, ]+)\s*[,)].*"
-)
-re_call_objcall = re.compile(r"LO_CALL\((?P<obj>[^,]+), (?P<meth>[^,)]+)[,)].*")
-
+class LibMiscPlugin:
+ re_comment = re.compile(r"/\*.*?\*/")
+ re_ws = re.compile(r"\s+")
+ re_lo_iface = re.compile(r"^\s*#\s*define\s+(?P<name>\S+)_LO_IFACE")
+ re_lo_func = re.compile(r"LO_FUNC *\([^,]*, *(?P<name>[^,) ]+) *[,)]")
+ re_lo_implementation = re.compile(
+ r"^LO_IMPLEMENTATION_(?P<vis>H|C|STATIC)\s*\("
+ r"\s*(?P<iface>[^, ]+)\s*,"
+ r"\s*(?P<impl_typ>[^,]+)\s*,"
+ r"\s*(?P<impl_name>[^, ]+)\s*\)"
+ )
+ re_lo_call = re.compile(r".*\bLO_CALL\((?P<obj>[^,]+), (?P<meth>[^,)]+)[,)].*")
-class LibObjPlugin:
objcalls: dict[str, set[QName]] # method_name => {method_impls}
def __init__(self, arg_c_fnames: typing.Collection[str]) -> None:
@@ -73,16 +94,16 @@ class LibObjPlugin:
for fname in arg_c_fnames:
with open(fname, "r", encoding="utf-8") as fh:
while line := fh.readline():
- if m := re_lo_iface.match(line):
+ if m := self.re_lo_iface.match(line):
iface_name = m.group("name")
if iface_name not in ifaces:
ifaces[iface_name] = set()
while line.endswith("\\\n"):
line += fh.readline()
line = line.replace("\\\n", " ")
- line = re_comment.sub(" ", line)
- line = re_ws.sub(" ", line)
- for m2 in re_lo_func.finditer(line):
+ line = self.re_comment.sub(" ", line)
+ line = self.re_ws.sub(" ", line)
+ for m2 in self.re_lo_func.finditer(line):
ifaces[iface_name].add(m2.group("name"))
implementations: dict[str, set[str]] = {} # iface_name => {impl_names}
@@ -92,7 +113,7 @@ class LibObjPlugin:
with open(fname, "r", encoding="utf-8") as fh:
for line in fh:
line = line.strip()
- if m := re_lo_implementation.match(line):
+ if m := self.re_lo_implementation.match(line):
implementations[m.group("iface")].add(m.group("impl_name"))
objcalls: dict[str, set[QName]] = {} # method_name => {method_impls}
@@ -116,15 +137,25 @@ class LibObjPlugin:
def extra_nodes(self) -> typing.Collection[Node]:
return []
+ def mutate_node(self, node: Node) -> None:
+ pass
+
def indirect_callees(
self, loc: str, line: str
) -> tuple[typing.Collection[QName], bool] | None:
-
if "/3rd-party/" in loc:
return None
- if m := re_call_objcall.fullmatch(line):
- if m.group("meth") in self.objcalls:
- return self.objcalls[m.group("meth")], False
+ if m := self.re_lo_call.fullmatch(line):
+ meth = m.group("meth")
+ if meth in self.objcalls:
+ callees: typing.Collection[QName] = self.objcalls[meth]
+ if len(callees) == 0:
+ raise ValueError(f"{loc}: no implementors of {meth}")
+ if meth == "writev" and "lib9p/srv.c" in loc: # KLUDGE
+ callees = [
+ c for c in callees if c.base() != BaseName("rread_writev")
+ ]
+ return callees, False
return [
QName(f"__indirect_call:{m.group('obj')}.vtable->{m.group('meth')}")
], False
@@ -136,11 +167,11 @@ class LibObjPlugin:
class LibHWPlugin:
pico_platform: str
- libobj: LibObjPlugin
+ libmisc: LibMiscPlugin
- def __init__(self, arg_pico_platform: str, libobj: LibObjPlugin) -> None:
+ def __init__(self, arg_pico_platform: str, libmisc: LibMiscPlugin) -> None:
self.pico_platform = arg_pico_platform
- self.libobj = libobj
+ self.libmisc = libmisc
def is_intrhandler(self, name: QName) -> bool:
return name.base() in [
@@ -160,6 +191,9 @@ class LibHWPlugin:
def extra_nodes(self) -> typing.Collection[Node]:
return []
+ def mutate_node(self, node: Node) -> None:
+ pass
+
def indirect_callees(
self, loc: str, line: str
) -> tuple[typing.Collection[QName], bool] | None:
@@ -174,11 +208,14 @@ class LibHWPlugin:
"io_readwritev",
]:
if f"{fn}(" in line:
- return self.libobj.indirect_callees(loc, f"LO_CALL(x, {fn[3:]})")
- if "io_read(" in line:
- return self.libobj.indirect_callees(loc, "LO_CALL(x, readv)")
- if "io_writev(" in line:
- return self.libobj.indirect_callees(loc, "LO_CALL(x, writev)")
+ return self.libmisc.indirect_callees(loc, f"LO_CALL(x, {fn[3:]})")
+ for fn in [
+ "io_read",
+ "io_write",
+ ]:
+ if f"{fn}(" in line:
+ # Like above, but add a "v" to the end.
+ return self.libmisc.indirect_callees(loc, f"LO_CALL(x, {fn[3:]}v)")
if "trigger->cb(trigger->cb_arg)" in line:
ret = [
QName("alarmclock_sleep_intrhandler"),
@@ -218,6 +255,9 @@ class LibCRPlugin:
def extra_nodes(self) -> typing.Collection[Node]:
return []
+ def mutate_node(self, node: Node) -> None:
+ pass
+
def indirect_callees(
self, loc: str, line: str
) -> tuple[typing.Collection[QName], bool] | None:
@@ -240,6 +280,9 @@ class LibCRIPCPlugin:
def extra_nodes(self) -> typing.Collection[Node]:
return []
+ def mutate_node(self, node: Node) -> None:
+ pass
+
def indirect_callees(
self, loc: str, line: str
) -> tuple[typing.Collection[QName], bool] | None:
@@ -247,8 +290,8 @@ class LibCRIPCPlugin:
return None
if "/chan.c:" in loc and "front->dequeue(" in line:
return [
- QName("_cr_chan_dequeue"),
- QName("_cr_select_dequeue"),
+ QName("cr_chan_dequeue"),
+ QName("cr_select_dequeue"),
], False
return None
@@ -256,38 +299,18 @@ class LibCRIPCPlugin:
return {}
-re_tmessage_handler = re.compile(
- r"^\s*\[LIB9P_TYP_T[^]]+\]\s*=\s*\(tmessage_handler\)\s*(?P<handler>\S+),\s*$"
-)
-re_lib9p_msg_entry = re.compile(r"^\s*_MSG_(?:[A-Z]+)\((?P<typ>\S+)\),$")
-re_lib9p_caller = re.compile(
- r"^lib9p_(?P<grp>[TR])msg_(?P<meth>validate|unmarshal|marshal)$"
-)
-re_lib9p_callee = re.compile(
- r"^(?P<meth>validate|unmarshal|marshal)_(?P<msg>(?P<grp>[TR]).*)$"
-)
-
-
class Lib9PPlugin:
- tmessage_handlers: set[QName] | None
+ re_lib9p_msg_entry = re.compile(r"^\s*_MSG\((?P<typ>\S+)\),$")
+
lib9p_msgs: set[str]
- _CONFIG_9P_NUM_SOCKS: int | None
- CONFIG_9P_SRV_MAX_REQS: int | None
- CONFIG_9P_SRV_MAX_DEPTH: int | None
- formatters: typing.Collection[BaseName]
+ _CONFIG_9P_MAX_CONNS: int | None
+ _CONFIG_9P_MAX_REQS: int | None
def __init__(
self,
arg_base_dir: str,
arg_c_fnames: typing.Collection[str],
- libobj_plugin: LibObjPlugin,
) -> None:
- self.formatters = {
- x.base()
- for x in libobj_plugin.objcalls["format"]
- if str(x.base()).startswith("lib9p_")
- }
-
# Find filenames #######################################################
def _is_config_h(fname: str) -> bool:
@@ -305,55 +328,45 @@ class Lib9PPlugin:
)
lib9p_generated_c_fname = util.get_zero_or_one(
- lambda fname: fname.endswith("lib9p/9p.generated.c"), arg_c_fnames
+ lambda fname: fname.endswith("lib9p/core_generated.c"), arg_c_fnames
)
# Read config ##########################################################
def config_h_get(varname: str) -> int | None:
if config_h_fname:
- with open(config_h_fname, "r", encoding="utf-8") as fh:
- for line in fh:
- line = line.rstrip()
- if line.startswith("#define"):
- parts = line.split()
- if parts[1] == varname:
- return int(parts[2])
+ line = subprocess.run(
+ ["cpp"],
+ input=f'#include "{config_h_fname}"\n{varname}\n',
+ check=True,
+ capture_output=True,
+ encoding="utf-8",
+ ).stdout.split("\n")[-2]
+ return int(eval(line)) # pylint: disable=eval-used
return None
- self._CONFIG_9P_NUM_SOCKS = config_h_get("_CONFIG_9P_NUM_SOCKS")
- self.CONFIG_9P_SRV_MAX_REQS = config_h_get("CONFIG_9P_SRV_MAX_REQS")
- self.CONFIG_9P_SRV_MAX_DEPTH = config_h_get("CONFIG_9P_SRV_MAX_DEPTH")
+ self._CONFIG_9P_MAX_CONNS = config_h_get("_CONFIG_9P_MAX_CONNS")
+ self._CONFIG_9P_MAX_REQS = config_h_get("_CONFIG_9P_MAX_REQS")
# Read sources #########################################################
- tmessage_handlers: set[QName] | None = None
- if lib9p_srv_c_fname:
- tmessage_handlers = set()
- with open(lib9p_srv_c_fname, "r", encoding="utf-8") as fh:
- for line in fh:
- line = line.rstrip()
- if m := re_tmessage_handler.fullmatch(line):
- tmessage_handlers.add(QName(m.group("handler")))
- self.tmessage_handlers = tmessage_handlers
-
lib9p_msgs: set[str] = set()
if lib9p_generated_c_fname:
with open(lib9p_generated_c_fname, "r", encoding="utf-8") as fh:
for line in fh:
line = line.rstrip()
- if m := re_lib9p_msg_entry.fullmatch(line):
+ if m := self.re_lib9p_msg_entry.fullmatch(line):
typ = m.group("typ")
lib9p_msgs.add(typ)
self.lib9p_msgs = lib9p_msgs
def thread_count(self, name: QName) -> int:
- assert self._CONFIG_9P_NUM_SOCKS
- assert self.CONFIG_9P_SRV_MAX_REQS
+ assert self._CONFIG_9P_MAX_CONNS
+ assert self._CONFIG_9P_MAX_REQS
if "read" in str(name.base()):
- return self._CONFIG_9P_NUM_SOCKS
+ return self._CONFIG_9P_MAX_CONNS
if "write" in str(name.base()):
- return self._CONFIG_9P_NUM_SOCKS * self.CONFIG_9P_SRV_MAX_REQS
+ return self._CONFIG_9P_MAX_REQS
return 1
def is_intrhandler(self, name: QName) -> bool:
@@ -368,211 +381,53 @@ class Lib9PPlugin:
def extra_nodes(self) -> typing.Collection[Node]:
return []
- def indirect_callees(
- self, loc: str, line: str
- ) -> tuple[typing.Collection[QName], bool] | None:
- if "/3rd-party/" in loc:
- return None
- if (
- self.tmessage_handlers
- and "/srv.c:" in loc
- and "tmessage_handlers[typ](" in line
- ):
- # Functions for disabled protocol extensions will be missing.
- return self.tmessage_handlers, True
- if self.lib9p_msgs and "/9p.c:" in loc:
- for meth in ["validate", "unmarshal", "marshal"]:
- if line.startswith(f"tentry.{meth}("):
- # Functions for disabled protocol extensions will be missing.
- return [QName(f"{meth}_{msg}") for msg in self.lib9p_msgs], True
- return None
-
- def skipmodels(self) -> dict[BaseName, analyze.SkipModel]:
- ret: dict[BaseName, analyze.SkipModel] = {
- BaseName("_lib9p_validate"): analyze.SkipModel(
- 2,
- self._skipmodel__lib9p_validate_unmarshal_marshal,
- ),
- BaseName("_lib9p_unmarshal"): analyze.SkipModel(
- 2,
- self._skipmodel__lib9p_validate_unmarshal_marshal,
- ),
- BaseName("_lib9p_marshal"): analyze.SkipModel(
- 2,
- self._skipmodel__lib9p_validate_unmarshal_marshal,
- ),
- BaseName("_vfctprintf"): analyze.SkipModel(
- self.formatters, self._skipmodel__vfctprintf
- ),
- }
- if isinstance(self.CONFIG_9P_SRV_MAX_DEPTH, int):
- ret[BaseName("srv_util_pathfree")] = analyze.SkipModel(
- self.CONFIG_9P_SRV_MAX_DEPTH,
- self._skipmodel_srv_util_pathfree,
- )
- return ret
-
- def _skipmodel__lib9p_validate_unmarshal_marshal(
- self, chain: typing.Sequence[QName], call: QName
- ) -> bool:
- m_caller = re_lib9p_caller.fullmatch(str(chain[-2].base()))
- assert m_caller
-
- m_callee = re_lib9p_callee.fullmatch(str(call.base()))
- if not m_callee:
- return False
- return m_caller.group("grp") != m_callee.group("grp")
-
- def _skipmodel_srv_util_pathfree(
- self, chain: typing.Sequence[QName], call: QName
- ) -> bool:
- assert isinstance(self.CONFIG_9P_SRV_MAX_DEPTH, int)
- if call.base() == BaseName("srv_util_pathfree"):
- return len(chain) >= self.CONFIG_9P_SRV_MAX_DEPTH and all(
- c.base() == BaseName("srv_util_pathfree")
- for c in chain[-self.CONFIG_9P_SRV_MAX_DEPTH :]
- )
- return False
-
- def _skipmodel__vfctprintf(
- self, chain: typing.Sequence[QName], call: QName
- ) -> bool:
- if call.base() == BaseName("libfmt_conv_formatter"):
- return any(c.base() in self.formatters for c in chain)
- return False
-
-
-class LibMiscPlugin:
- def is_intrhandler(self, name: QName) -> bool:
- return False
-
- def init_array(self) -> typing.Collection[QName]:
- return []
-
- def extra_includes(self) -> typing.Collection[BaseName]:
- return []
-
- def extra_nodes(self) -> typing.Collection[Node]:
- return []
-
- def indirect_callees(
- self, loc: str, line: str
- ) -> tuple[typing.Collection[QName], bool] | None:
- return None
-
- def skipmodels(self) -> dict[BaseName, analyze.SkipModel]:
- return {
- BaseName("__assert_msg_fail"): analyze.SkipModel(
- {BaseName("__assert_msg_fail")}, self._skipmodel___assert_msg_fail
- ),
- }
-
- def _skipmodel___assert_msg_fail(
- self, chain: typing.Sequence[QName], call: QName
- ) -> bool:
- if call.base() in [BaseName("__lm_printf"), BaseName("__lm_light_printf")]:
- return any(
- c.base() == BaseName("__assert_msg_fail") for c in reversed(chain[:-1])
- )
- return False
-
-
-class PicoFmtPlugin:
- known_fct: dict[BaseName, BaseName]
-
- def __init__(self, arg_pico_platform: str) -> None:
- self.known_fct = {
- # pico_fmt
- BaseName("fmt_vsnprintf"): BaseName("_out_buffer"),
- }
- match arg_pico_platform:
- case "rp2040":
- self.known_fct.update(
- {
- # pico_stdio
- BaseName("__wrap_vprintf"): BaseName("stdio_buffered_printer"),
- BaseName("stdio_vprintf"): BaseName("stdio_buffered_printer"),
- # libfmt
- BaseName("__lm_light_printf"): BaseName("libfmt_light_fct"),
- }
- )
- case "host":
- self.known_fct.update(
- {
- # libfmt
- BaseName("__lm_printf"): BaseName("libfmt_libc_fct"),
- BaseName("__lm_light_printf"): BaseName("libfmt_libc_fct"),
- }
- )
-
- def is_intrhandler(self, name: QName) -> bool:
- return False
-
- def init_array(self) -> typing.Collection[QName]:
- return []
-
- def extra_includes(self) -> typing.Collection[BaseName]:
- return []
+ def mutate_node(self, node: Node) -> None:
+ pass
- def extra_nodes(self) -> typing.Collection[Node]:
- return []
+ re_table_call = re.compile(
+ r"\s*_lib9p_(?P<meth>validate|unmarshal|marshal)\(.*(?P<grp>[RT])msg.*\);\s*"
+ )
+ re_print_call = re.compile(r".*lib9p_table_msg.*\.print\(.*")
def indirect_callees(
self, loc: str, line: str
) -> tuple[typing.Collection[QName], bool] | None:
- if "/3rd-party/pico-fmt/" not in loc:
+ if "/3rd-party/" in loc:
return None
- if "/printf.c:" in loc:
- m = util.re_call_other.fullmatch(line)
- call: str | None = m.group("func") if m else None
- if "->fct" in line:
- return [x.as_qname() for x in self.known_fct.values()], False
- if "specifier_table" in line:
+ if self.lib9p_msgs and "lib9p/core.c:" in loc:
+ if m := self.re_table_call.fullmatch(line):
+ meth = m.group("meth")
+ grp = m.group("grp")
+ # Functions for disabled protocol extensions will be missing.
return [
- # pico-fmt
- QName("conv_sint"),
- QName("conv_uint"),
- # QName("conv_double"),
- QName("conv_char"),
- QName("conv_str"),
- QName("conv_ptr"),
- QName("conv_pct"),
- # libfmt
- QName("libfmt_conv_formatter"),
- QName("libfmt_conv_quote"),
- ], False
+ QName(f"{meth}_{msg}")
+ for msg in self.lib9p_msgs
+ if msg.startswith(grp)
+ ], True
+ if self.re_print_call.fullmatch(line):
+ # Functions for disabled protocol extensions will be missing.
+ return [QName(f"fmt_print_{msg}") for msg in self.lib9p_msgs], True
+ if "lib9p/srv.c:" in loc:
+ if "srv->msglog(" in line:
+ # Actual ROMs shouldn't set this, and so will be missing on rp2040 builds.
+ return [QName("log_msg")], True
return None
def skipmodels(self) -> dict[BaseName, analyze.SkipModel]:
- ret: dict[BaseName, analyze.SkipModel] = {
- BaseName("fmt_state_putchar"): analyze.SkipModel(
- self.known_fct.keys(), self._skipmodel_fmt_state_putchar
- ),
- }
- return ret
-
- def _skipmodel_fmt_state_putchar(
- self, chain: typing.Sequence[QName], call: QName
- ) -> bool:
- if call.base() in self.known_fct.values():
- fct: BaseName | None = None
- for pcall in reversed(chain):
- if pcall.base() in self.known_fct:
- fct = self.known_fct[pcall.base()]
- return call.base() != fct
- return True
- return False
+ return {}
class PicoSDKPlugin:
get_init_array: typing.Callable[[], typing.Collection[QName]]
app_init_array: typing.Collection[QName] | None
app_preinit_array: typing.Collection[QName]
+ _PICO_PANIC_FUNCTION: str | None
def __init__(
self,
*,
get_init_array: typing.Callable[[], typing.Collection[QName]],
+ PICO_PANIC_FUNCTION: str | None,
) -> None:
# grep for '__attribute__((constructor))' / '[[gnu::constructor]]'.
self.get_init_array = get_init_array
@@ -606,6 +461,8 @@ class PicoSDKPlugin:
QName("runtime_init_install_ram_vector_table"),
]
+ self._PICO_PANIC_FUNCTION = PICO_PANIC_FUNCTION
+
def is_intrhandler(self, name: QName) -> bool:
return name.base() in [
BaseName("isr_invalid"),
@@ -642,6 +499,8 @@ class PicoSDKPlugin:
return [QName("rom_func_lookup(ROM_FUNC_FLASH_RANGE_ERASE)")], False
case "flash_flush_cache_func":
return [QName("rom_func_lookup(ROM_FUNC_FLASH_FLUSH_CACHE)")], False
+ case "flash_range_program_func":
+ return [QName("rom_func_lookup(ROM_FUNC_FLASH_RANGE_PROGRAM)")], False
case "rom_table_lookup":
return [QName("rom_hword_as_ptr(BOOTROM_TABLE_LOOKUP_OFFSET)")], False
if "/flash.c:" in loc and "boot2_copyout" in line:
@@ -662,7 +521,7 @@ class PicoSDKPlugin:
case "in_chars":
return [QName("stdio_uart_in_chars")], False
if "/newlib_interface.c:" in loc:
- if line == "*p)();":
+ if line == "(*p)();":
if self.app_init_array is None:
self.app_init_array = self.get_init_array()
return self.app_init_array, False
@@ -676,11 +535,6 @@ class PicoSDKPlugin:
def extra_nodes(self) -> typing.Collection[Node]:
ret = []
- # src/rp2_common/hardware_divider/include/hardware/divider_helper.S
- save_div_state_and_lr = 5 * 4
- # src/rp2_common/pico_divider/divider_hardware.S
- save_div_state_and_lr_64 = 5 * 4
-
# src/src/rp2_common/pico_crt0/crt0.S
for n in range(32):
ret += [synthetic_node(f"isr_irq{n}", 0, {"__unhandled_user_irq"})]
@@ -696,10 +550,16 @@ class PicoSDKPlugin:
synthetic_node("_reset_handler", 0, {"runtime_init", "main", "exit"}),
]
+ # src/rp2_common/pico_int64_ops/pico_int64_ops_aeabi.S
ret += [
- # src/rp2_common/pico_int64_ops/pico_int64_ops_aeabi.S
synthetic_node("__wrap___aeabi_lmul", 4),
- # src/rp2_common/pico_divider/divider_hardware.S
+ ]
+
+ # src/rp2_common/hardware_divider/include/hardware/divider_helper.S
+ save_div_state_and_lr = 5 * 4
+ # src/rp2_common/pico_divider/divider_hardware.S
+ save_div_state_and_lr_64 = 5 * 4
+ ret += [
# s32 aliases
synthetic_node("div_s32s32", 0, {"divmod_s32s32"}),
synthetic_node("__wrap___aeabi_idiv", 0, {"divmod_s32s32"}),
@@ -754,7 +614,10 @@ class PicoSDKPlugin:
# *_rem
synthetic_node("divod_s64s64_rem", 2 * 4, {"divmod_s64s64"}),
synthetic_node("divod_u64u64_rem", 2 * 4, {"divmod_u64u64"}),
- # src/rp2_common/pico_mem_ops/mem_ops_aeabi.S
+ ]
+
+ # src/rp2_common/pico_mem_ops/mem_ops_aeabi.S
+ ret += [
synthetic_node("__aeabi_mem_init", 0, {"rom_funcs_lookup"}),
synthetic_node(
"__wrap___aeabi_memset", 0, {"rom_func_lookup(ROM_FUNC_MEMSET)"}
@@ -770,7 +633,10 @@ class PicoSDKPlugin:
synthetic_node("__wrap_memset", 0, {"rom_func_lookup(ROM_FUNC_MEMSET)"}),
synthetic_node("__wrap___aeabi_memcpy", 0, {"__wrap_memcpy"}),
synthetic_node("__wrap_memcpy", 0, {"rom_func_lookup(ROM_FUNC_MEMCPY)"}),
- # src/rp2_common/pico_bit_ops/bit_ops_aeabi.S
+ ]
+
+ # src/rp2_common/pico_bit_ops/bit_ops_aeabi.S
+ ret += [
synthetic_node("__aeabi_bits_init", 0, {"rom_funcs_lookup"}),
synthetic_node("__wrap___clz", 0, {"__wrap___clzsi2"}),
synthetic_node("__wrap___clzl", 0, {"__wrap___clzsi2"}),
@@ -790,29 +656,46 @@ class PicoSDKPlugin:
synthetic_node("reverse32", 0, {"rom_func_lookup(ROM_FUNC_REVERSE32)"}),
synthetic_node("__revll", 0, {"reverse64"}),
synthetic_node("reverse64", 3 * 4, {"rom_func_lookup(ROM_FUNC_REVERSE32)"}),
- # src/rp2040/boot_stage2/boot2_${name,,}.S for name=W25Q080,
- # controlled by `#define PICO_BOOT_STAGE2_{name} 1` in
- # src/boards/include/boards/pico.h
+ ]
+
+ # src/rp2040/boot_stage2/boot2_${name,,}.S for name=W25Q080,
+ # controlled by `#define PICO_BOOT_STAGE2_{name} 1` in
+ # src/boards/include/boards/pico.h
+ ret += [
# synthetic_node("_stage2_boot", 0), # TODO
- # https://github.com/raspberrypi/pico-bootrom-rp2040
+ ]
+
+ # https://github.com/raspberrypi/pico-bootrom-rp2040
+ ret += [
# synthetic_node("rom_func_lookup(ROM_FUNC_CONNECT_INTERNAL_FLASH)", 0), # TODO
# synthetic_node("rom_func_lookup(ROM_FUNC_FLASH_EXIT_XIP)", 0), # TODO
# synthetic_node("rom_func_lookup(ROM_FUNC_FLASH_FLUSH_CACHE)", 0), # TODO
# synthetic_node("rom_hword_as_ptr(BOOTROM_TABLE_LOOKUP_OFFSET)", 0), # TODO
]
- return ret
+ return ret
-re_tud_class = re.compile(
- r"^\s*#\s*define\s+(?P<k>CFG_TUD_(?:\S{3}|AUDIO|VIDEO|MIDI|VENDOR|USBTMC|DFU_RUNTIME|ECM_RNDIS))\s+(?P<v>\S+).*"
-)
-re_tud_entry = re.compile(r"^\s+\.(?P<meth>\S+)\s*=\s*(?P<impl>[a-zA-Z0-9_]+)(?:,.*)?")
-re_tud_if1 = re.compile(r"^\s*#\s*if (\S+)\s*")
-re_tud_if2 = re.compile(r"^\s*#\s*if (\S+)\s*\|\|\s*(\S+)\s*")
-re_tud_endif = re.compile(r"^\s*#\s*endif\s*")
+ def mutate_node(self, node: Node) -> None:
+ if self._PICO_PANIC_FUNCTION and node.funcname.base() == BaseName("panic"):
+ # inline assembly from src/rp2_common/pico_platform_panic/panic.c
+ assert node.nstatic == 0
+ assert node.ndynamic == 0
+ assert len(node.calls) == 0
+ node.nstatic += 4
+ node.calls[QName(self._PICO_PANIC_FUNCTION)] = False
class TinyUSBDevicePlugin:
+ re_tud_class = re.compile(
+ r"^\s*#\s*define\s+(?P<k>CFG_TUD_(?:\S{3}|AUDIO|VIDEO|MIDI|VENDOR|USBTMC|DFU_RUNTIME|ECM_RNDIS))\s+(?P<v>\S+).*"
+ )
+ re_tud_entry = re.compile(
+ r"^\s+\.(?P<meth>\S+)\s*=\s*(?P<impl>[a-zA-Z0-9_]+)(?:,.*)?"
+ )
+ re_tud_if1 = re.compile(r"^\s*#\s*if (\S+)\s*")
+ re_tud_if2 = re.compile(r"^\s*#\s*if (\S+)\s*\|\|\s*(\S+)\s*")
+ re_tud_endif = re.compile(r"^\s*#\s*endif\s*")
+
tud_drivers: dict[str, set[QName]] # method_name => {method_impls}
def __init__(self, arg_c_fnames: typing.Collection[str]) -> None:
@@ -834,7 +717,7 @@ class TinyUSBDevicePlugin:
in_table = False
for line in fh:
line = line.rstrip()
- if m := re_tud_class.fullmatch(line):
+ if m := self.re_tud_class.fullmatch(line):
k = m.group("k")
v = m.group("v")
tusb_config[k] = bool(int(v))
@@ -846,13 +729,13 @@ class TinyUSBDevicePlugin:
for line in fh:
line = line.rstrip()
if in_table:
- if m := re_tud_if1.fullmatch(line):
+ if m := self.re_tud_if1.fullmatch(line):
enabled = tusb_config[m.group(1)]
- elif m := re_tud_if2.fullmatch(line):
+ elif m := self.re_tud_if2.fullmatch(line):
enabled = tusb_config[m.group(1)] or tusb_config[m.group(2)]
- elif re_tud_endif.fullmatch(line):
+ elif self.re_tud_endif.fullmatch(line):
enabled = True
- if m := re_tud_entry.fullmatch(line):
+ if m := self.re_tud_entry.fullmatch(line):
meth = m.group("meth")
impl = m.group("impl")
if meth == "name" or not enabled:
@@ -879,6 +762,9 @@ class TinyUSBDevicePlugin:
def extra_nodes(self) -> typing.Collection[Node]:
return []
+ def mutate_node(self, node: Node) -> None:
+ pass
+
def indirect_callees(
self, loc: str, line: str
) -> tuple[typing.Collection[QName], bool] | None:
@@ -895,10 +781,18 @@ class TinyUSBDevicePlugin:
ret.update(self.tud_drivers["control_xfer_cb"])
return ret, False
if call.startswith("driver->"):
- return self.tud_drivers[call[len("driver->") :]], False
+ meth = call[len("driver->") :]
+ callees = self.tud_drivers[meth]
+ if len(callees) == 0:
+ if meth == "sof":
+ return [QName(f"__indirect_call_with_null_check:{call}")], False
+ raise ValueError(f"{loc}: no implementors of {meth}")
+ return callees, False
if call == "event.func_call.func":
# callback from usb_defer_func()
- return [], False
+ return [
+ QName("__indirect_call_with_null_check:event.func_call.func")
+ ], False
return None
@@ -920,11 +814,14 @@ class NewlibPlugin:
]
def extra_nodes(self) -> typing.Collection[Node]:
+ ret = []
+
# This is accurate to
# /usr/arm-none-eabi/lib/thumb/v6-m/nofp/libg.a as of
# Parabola's arm-none-eabi-newlib 4.5.0.20241231-1.
- return [
- # malloc
+
+ # malloc
+ ret += [
synthetic_node("free", 8, {"_free_r"}),
synthetic_node("malloc", 8, {"_malloc_r"}),
synthetic_node("realloc", 8, {"_realloc_r"}),
@@ -934,20 +831,27 @@ class NewlibPlugin:
# synthetic_node("_malloc_r", 0), # TODO
# synthetic_node("_realloc_r", 0), # TODO
# synthetic_node("_memalign_r", 0), # TODO
- # execution
+ ]
+
+ # execution
+ ret += [
synthetic_node("raise", 16, {"_getpid_r"}),
synthetic_node("abort", 8, {"raise", "_exit"}),
synthetic_node("longjmp", 0),
synthetic_node("setjmp", 0),
- # <strings.h>
+ ]
+
+ # <strings.h>
+ ret += [
synthetic_node("memcmp", 12),
- synthetic_node("memcpy", 28),
- synthetic_node("memset", 20),
synthetic_node("strcmp", 16),
synthetic_node("strlen", 8),
synthetic_node("strncpy", 16),
synthetic_node("strnlen", 8),
- # other
+ ]
+
+ # other
+ ret += [
synthetic_node("__errno", 0),
synthetic_node("_getpid_r", 8, {"_getpid"}),
synthetic_node("random", 8),
@@ -964,6 +868,11 @@ class NewlibPlugin:
synthetic_node("__libc_fini_array", 16, {"_fini"}),
]
+ return ret
+
+ def mutate_node(self, node: Node) -> None:
+ pass
+
def indirect_callees(
self, loc: str, line: str
) -> tuple[typing.Collection[QName], bool] | None:
@@ -978,10 +887,7 @@ class LibGCCPlugin:
return False
def init_array(self) -> typing.Collection[QName]:
- return [
- QName("libfmt_install_formatter"),
- QName("libfmt_install_quote"),
- ]
+ return []
def extra_includes(self) -> typing.Collection[BaseName]:
return []
@@ -997,6 +903,9 @@ class LibGCCPlugin:
synthetic_node("_fini", 24),
]
+ def mutate_node(self, node: Node) -> None:
+ pass
+
def indirect_callees(
self, loc: str, line: str
) -> tuple[typing.Collection[QName], bool] | None:
diff --git a/build-aux/measurestack/test_analyze.py b/build-aux/measurestack/test_analyze.py
index ff1732d..df205e8 100644
--- a/build-aux/measurestack/test_analyze.py
+++ b/build-aux/measurestack/test_analyze.py
@@ -5,17 +5,20 @@
# pylint: disable=unused-variable
+import re
+import typing
+
import pytest
-from .analyze import BaseName, QName
+from . import analyze, testutil, util
def test_name_base() -> None:
- assert QName("foo.c:bar.1").base() == BaseName("bar")
+ assert analyze.QName("foo.c:bar.1").base() == analyze.BaseName("bar")
def test_name_pretty() -> None:
- name = QName("foo.c:bar.1")
+ name = analyze.QName("foo.c:bar.1")
assert f"{name}" == "QName('foo.c:bar.1')"
assert f"{name.base()}" == "BaseName('bar')"
assert f"{[name]}" == "[QName('foo.c:bar.1')]"
@@ -23,7 +26,7 @@ def test_name_pretty() -> None:
def test_name_eq() -> None:
- name = QName("foo.c:bar.1")
+ name = analyze.QName("foo.c:bar.1")
with pytest.raises(AssertionError) as e:
if name == "foo":
pass
@@ -32,3 +35,47 @@ def test_name_eq() -> None:
if name.base() == "foo":
pass
assert "comparing BaseName with str" in str(e)
+
+
+def test_max_call_depth() -> None:
+ graph: typing.Sequence[tuple[str, typing.Collection[str]]] = [
+ ("a", {"b"}), # 1
+ ("b", {"c"}), # 2
+ ("c", {"d"}), # 3
+ ("d", {"e"}), # 4
+ ("e", {}), # 5
+ ]
+
+ testcases: dict[int, bool] = {
+ 1: True,
+ 2: True,
+ 3: True,
+ 4: True,
+ 5: False,
+ 6: False,
+ 7: False,
+ }
+
+ def test_filter(name: analyze.QName) -> tuple[int, bool]:
+ if str(name.base()) in ["a"]:
+ return 1, True
+ return 0, False
+
+ def doit(depth: int, graph_plugin: util.Plugin) -> None:
+ analyze.analyze(
+ ci_fnames=[],
+ app_func_filters={"Main": test_filter},
+ app=util.PluginApplication(testutil.nop_location_xform, [graph_plugin]),
+ cfg_max_call_depth=depth,
+ )
+
+ pat = re.compile("^max call depth exceeded: ")
+
+ for depth, should_fail in testcases.items():
+ graph_plugin = testutil.GraphProviderPlugin(depth, graph)
+
+ if should_fail:
+ with pytest.raises(ValueError, match=pat):
+ doit(depth, graph_plugin)
+ else:
+ doit(depth, graph_plugin)
diff --git a/build-aux/measurestack/testutil.py b/build-aux/measurestack/testutil.py
new file mode 100644
index 0000000..3c32134
--- /dev/null
+++ b/build-aux/measurestack/testutil.py
@@ -0,0 +1,134 @@
+# build-aux/measurestack/testutil.py - Utilities for writing tests
+#
+# Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+# SPDX-License-Identifier: AGPL-3.0-or-later
+
+import typing
+
+from . import analyze, util
+
+# pylint: disable=unused-variable
+__all__ = [
+ "aprime_gen",
+ "aprime_decompose",
+ "NopPlugin",
+ "GraphProviderPlugin",
+ "nop_location_xform",
+]
+
+
+def aprime_gen(l: int, n: int) -> typing.Sequence[int]:
+ """Return an `l`-length sequence of nonnegative
+ integers such that any `n`-length-or-shorter combination of
+ members with repeats allowed can be uniquely identified by its
+ sum.
+
+ (If that were "product" instead of "sum", the obvious solution
+ would be the first `l` primes.)
+
+ """
+ seq = [1]
+ while len(seq) < l:
+ x = seq[-1] * n + 1
+ seq.append(x)
+ return seq
+
+
+def aprime_decompose(
+ aprimes: typing.Sequence[int], tot: int
+) -> tuple[typing.Collection[int], typing.Collection[int]]:
+ ret_idx = []
+ ret_val = []
+ while tot:
+ idx = max(i for i in range(len(aprimes)) if aprimes[i] <= tot)
+ val = aprimes[idx]
+ ret_idx.append(idx)
+ ret_val.append(val)
+ tot -= val
+ return ret_idx, ret_val
+
+
+class NopPlugin:
+ def is_intrhandler(self, name: analyze.QName) -> bool:
+ return False
+
+ def init_array(self) -> typing.Collection[analyze.QName]:
+ return []
+
+ def extra_includes(self) -> typing.Collection[analyze.BaseName]:
+ return []
+
+ def indirect_callees(
+ self, loc: str, line: str
+ ) -> tuple[typing.Collection[analyze.QName], bool] | None:
+ return None
+
+ def skipmodels(self) -> dict[analyze.BaseName, analyze.SkipModel]:
+ return {}
+
+ def extra_nodes(self) -> typing.Collection[analyze.Node]:
+ return []
+
+ def mutate_node(self, node: analyze.Node) -> None:
+ pass
+
+
+class GraphProviderPlugin(NopPlugin):
+ _nodes: typing.Sequence[analyze.Node]
+
+ def __init__(
+ self,
+ max_call_depth: int,
+ graph: typing.Sequence[tuple[str, typing.Collection[str]]],
+ ) -> None:
+ seq = aprime_gen(len(graph), max_call_depth)
+ nodes: list[analyze.Node] = []
+ for i, (name, calls) in enumerate(graph):
+ nodes.append(util.synthetic_node(name, seq[i], calls))
+ assert (
+ len(graph)
+ == len(nodes)
+ == len(set(n.nstatic for n in nodes))
+ == len(set(str(n.funcname.base()) for n in nodes))
+ )
+ self._nodes = nodes
+
+ def extra_nodes(self) -> typing.Collection[analyze.Node]:
+ return self._nodes
+
+ def decode_nstatic(self, tot: int) -> typing.Collection[str]:
+ idxs, _ = aprime_decompose([n.nstatic for n in self._nodes], tot)
+ return [str(self._nodes[i].funcname.base()) for i in idxs]
+
+ def encode_nstatic(self, calls: typing.Collection[str]) -> int:
+ tot = 0
+ d: dict[str, int] = {}
+ for node in self._nodes:
+ d[str(node.funcname.base())] = node.nstatic
+ print(d)
+ for call in calls:
+ tot += d[call]
+ return tot
+
+ def sorted_calls(self, calls: typing.Collection[str]) -> typing.Sequence[str]:
+ d: dict[str, int] = {}
+ for node in self._nodes:
+ d[str(node.funcname.base())] = node.nstatic
+
+ def k(call: str) -> int:
+ return d[call]
+
+ return sorted(calls, key=k)
+
+ def assert_nstatic(self, act_tot: int, exp_calls: typing.Collection[str]) -> None:
+ exp_tot = self.encode_nstatic(exp_calls)
+ if act_tot != exp_tot:
+ act_str = f"{act_tot}: {self.sorted_calls(self.decode_nstatic(act_tot))}"
+ exp_str = f"{exp_tot}: {self.sorted_calls(exp_calls)}"
+ assert (
+ False
+ ), f"act:{act_tot} != exp:{exp_tot}\n\t-exp = {exp_str}\n\t+act = {act_str}"
+
+
+def nop_location_xform(loc: str) -> str:
+ return loc
diff --git a/build-aux/measurestack/util.py b/build-aux/measurestack/util.py
index 47b2617..c94ce07 100644
--- a/build-aux/measurestack/util.py
+++ b/build-aux/measurestack/util.py
@@ -7,7 +7,7 @@ import re
import typing
from . import analyze, vcg
-from .analyze import BaseName, Node, QName
+from .analyze import BaseName, Node, QName, maybe_sorted
# pylint: disable=unused-variable
__all__ = [
@@ -32,7 +32,7 @@ def synthetic_node(
n.nstatic = nstatic
n.ndynamic = 0
- n.calls = dict((QName(c), False) for c in calls)
+ n.calls = dict((QName(c), False) for c in maybe_sorted(calls))
return n
@@ -46,9 +46,9 @@ def read_source(location: str) -> str:
raise ValueError(f"unexpected label value {location!r}")
filename = m.group("filename")
row = int(m.group("row")) - 1
- col = int(m.group("col")) - 1
+ # col = int(m.group("col")) - 1
with open(filename, "r", encoding="utf-8") as fh:
- return fh.readlines()[row][col:].rstrip()
+ return fh.readlines()[row].strip()
def get_zero_or_one(
@@ -61,7 +61,7 @@ def get_zero_or_one(
return None
-re_call_other = re.compile(r"(?P<func>[^(]+)\(.*")
+re_call_other = re.compile(r".*?\b(?P<func>(?!if\b)[->.a-zA-Z0-9_]+)\(.*")
class Plugin(typing.Protocol):
@@ -79,6 +79,7 @@ class Plugin(typing.Protocol):
def extra_includes(self) -> typing.Collection[BaseName]: ...
def extra_nodes(self) -> typing.Collection[Node]: ...
+ def mutate_node(self, node: Node) -> None: ...
def indirect_callees(
self, loc: str, line: str
) -> tuple[typing.Collection[QName], bool] | None: ...
@@ -101,6 +102,10 @@ class PluginApplication:
ret.extend(plugin.extra_nodes())
return ret
+ def mutate_node(self, node: Node) -> None:
+ for plugin in self._plugins:
+ plugin.mutate_node(node)
+
def indirect_callees(
self, elem: vcg.VCGElem
) -> tuple[typing.Collection[QName], bool]:
@@ -110,6 +115,9 @@ class PluginApplication:
for plugin in self._plugins:
ret = plugin.indirect_callees(loc, line)
if ret is not None:
+ assert (
+ len(ret[0]) > 0
+ ), f"{plugin.__class__.__name__} returning 0 calles for {loc} indicates the code would crash"
return ret
placeholder = "__indirect_call"
diff --git a/build-aux/tent-graph b/build-aux/tent-graph
new file mode 100755
index 0000000..25c58c5
--- /dev/null
+++ b/build-aux/tent-graph
@@ -0,0 +1,180 @@
+#!/usr/bin/env python3
+# build-aux/tent-graph - Take dbg_noncache=True dbg_nstatic=True stack.c on stdin, and produce a tent graph SVG on stdout
+#
+# Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+# SPDX-License-Identifier: AGPL-3.0-or-later
+
+import ast
+import re
+import sys
+
+
+class Block:
+ title: str
+ parent: "Block|None"
+ children: list["Block"]
+ nbytes: int
+
+ def __init__(self, *, title: str, nbytes: int, parent: "Block|None") -> None:
+ self.title = title
+ self.parent = parent
+ self.children = []
+ self.nbytes = nbytes
+
+ @property
+ def rows(self) -> int:
+ if not self.children:
+ return 1
+ return sum(c.rows for c in self.children)
+
+ @property
+ def sum_nbytes(self) -> int:
+ if not self.children:
+ return self.nbytes
+ return self.nbytes + max(c.sum_nbytes for c in self.children)
+
+ def prune(self) -> None:
+ tgt = self.sum_nbytes - self.nbytes
+ self.children = [c for c in self.children if c.sum_nbytes == tgt]
+
+
+re_line = re.compile(
+ r"^//dbg-nstatic:(?P<indent>(?: -)*) QName\((?P<func>.*)\)\t(?P<size>[0-9]+)$"
+)
+
+
+def parse() -> list[Block]:
+ roots: list[Block] = []
+
+ stack: list[Block] = []
+ for line in sys.stdin:
+ m = re_line.fullmatch(line.strip())
+ if not m:
+ continue
+
+ depth = len(m.group("indent")) // 2
+ func = ast.literal_eval(m.group("func"))
+ size = int(m.group("size"), 10)
+
+ stack = stack[:depth]
+
+ block = Block(
+ title=func,
+ nbytes=size,
+ parent=stack[-1] if stack else None,
+ )
+ if block.parent:
+ block.parent.children.append(block)
+ else:
+ roots.append(block)
+ stack.append(block)
+
+ return roots
+
+
+def render(roots: list[Block]) -> None:
+ total_nbytes = max(r.sum_nbytes for r in roots)
+ total_rows = sum(r.rows for r in roots)
+
+ img_w = 1920
+ img_h = 948
+
+ details_h = 16
+ text_yoff = 12
+ text_xoff = 3
+
+ main_h = img_h - details_h
+ nbyte_h = main_h / total_nbytes
+ row_w = img_w / total_rows
+
+ print(
+ f"""<?xml version="1.0" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" width="{img_w}" height="{img_h}" onload="init(evt)" viewBox="0 0 {img_w} {img_h}" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+<style type="text/css">
+ .func_g:hover {{ stroke:black; stroke-width:0.5; }}
+ .func_g rect {{ rx: 2px; ry: 2px; }}
+ rect#background {{ fill: #EEEEEE; }}
+ text {{ font-size: 12px; font-family: Verdana; fill: rgb(0,0,0); }}
+</style>
+<script type="text/ecmascript">
+<![CDATA[
+ var details;
+ function init(evt) {{ details = document.getElementById("details").firstChild; }}
+ function s(info) {{ details.nodeValue = "Function: " + info; }}
+ function c() {{ details.nodeValue = ' '; }}
+]]>
+</script>
+<rect id="background" x="0" y="0" width="{img_w}" height="{img_h}" />
+<text text-anchor="" x="{text_xoff}" y="{img_h-details_h+text_yoff}" id="details"> </text>"""
+ )
+
+ min_nbytes = roots[0].nbytes
+ max_nbytes = 0
+
+ def visit(b: Block) -> None:
+ nonlocal min_nbytes
+ nonlocal max_nbytes
+ min_nbytes = min(min_nbytes, b.nbytes)
+ max_nbytes = max(max_nbytes, b.nbytes)
+ for c in b.children:
+ visit(c)
+
+ for r in roots:
+ visit(r)
+
+ def print_block(block: Block, nbyte: int, row: int) -> None:
+ nonlocal min_nbytes
+ nonlocal max_nbytes
+
+ if block.nbytes:
+ hue = 100 - int(
+ ((block.nbytes - min_nbytes) / (max_nbytes - min_nbytes)) * 100
+ )
+
+ x = row * row_w
+ y = nbyte * nbyte_h
+ w = max(1, block.rows * row_w - 1)
+ h = block.nbytes * nbyte_h
+ title = f"{block.title} = {block.nbytes} / {block.sum_nbytes} bytes"
+
+ nonlocal main_h
+ print(f'<g class="func_g" onmouseover="s(\'{title}\')" onmouseout="c()">')
+ print(f"\t<title>{title}</title>")
+ print(
+ f'\t<rect x="{x}" y="{main_h-y-h}" width="{w}" height="{h}" fill="hsl({hue} 60% 60%)" />'
+ )
+
+ short_title = title.rsplit(":", 1)[-1]
+ if h > details_h and w > len(short_title) * 10:
+ print(
+ f'\t<text x="{x+text_xoff}" y="{main_h-y-h+text_yoff}">{short_title}</text>'
+ )
+ print("</g>")
+
+ def sort_key(c: Block) -> int:
+ return c.sum_nbytes
+
+ for c in sorted(block.children, key=sort_key, reverse=True):
+ print_block(c, nbyte + block.nbytes, row)
+ row += c.rows
+
+ row = 0
+ for r in roots:
+ print_block(r, 0, row)
+ row += r.rows
+
+ print("</svg>")
+
+
+def main() -> None:
+ roots = parse()
+
+ # tgt = max(r.sum_nbytes for r in roots)
+ # roots = [r for r in roots if r.sum_nbytes == tgt]
+
+ render(roots)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/build-aux/valgrind b/build-aux/valgrind
new file mode 100755
index 0000000..0700e4d
--- /dev/null
+++ b/build-aux/valgrind
@@ -0,0 +1,16 @@
+#!/bin/sh
+# build-aux/valgrind - Wrapper around valgrind to keep flags consistent
+#
+# Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+# SPDX-License-Identifier: AGPL-3.0-or-later
+
+exec \
+ valgrind \
+ --fair-sched=yes \
+ --error-exitcode=2 \
+ --leak-check=full \
+ --show-leak-kinds=all \
+ --errors-for-leak-kinds=all \
+ --show-error-list=all \
+ -- \
+ "$@"
diff --git a/cmd/sbc_harness/CMakeLists.txt b/cmd/sbc_harness/CMakeLists.txt
deleted file mode 100644
index 64bf356..0000000
--- a/cmd/sbc_harness/CMakeLists.txt
+++ /dev/null
@@ -1,79 +0,0 @@
-# cmd/sbc_harness/CMakeLists.txt - Build script for main sbc_harness.uf2 firmware file
-#
-# Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
-# SPDX-License-Identifier: AGPL-3.0-or-later
-
-if (PICO_PLATFORM STREQUAL "rp2040")
-
-# Compile ######################################################################
-
-add_library(sbc_harness_objs OBJECT
- main.c
- usb_keyboard.c
- tusb_log.c
-)
-target_include_directories(sbc_harness_objs PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/config)
-target_include_directories(sbc_harness_objs PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
-target_include_directories(sbc_harness_objs PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
-target_link_libraries(sbc_harness_objs
- pico_runtime
- pico_stdio_uart
-
- hardware_flash
-
- libmisc
- libfmt
- libusb
- libdhcp
- libhw_cr
- lib9p
- lib9p_util
-)
-pico_minimize_runtime(sbc_harness_objs
- INCLUDE PRINTF PRINTF_MINIMAL PRINTF_LONG_LONG PRINTF_PTRDIFF_T
-)
-target_compile_definitions(sbc_harness_objs PRIVATE
- #PICO_USE_FASTEST_SUPPORTED_CLOCK=1
-
- # Calculated by `./3rd-party/pico-sdk/src/rp2_common/hardware_clocks/scripts/vcocalc.py --cmake-only 170`
- PLL_SYS_REFDIV=2
- PLL_SYS_VCO_FREQ_HZ=1530000000
- PLL_SYS_POSTDIV1=3
- PLL_SYS_POSTDIV2=3
- SYS_CLK_HZ=170000000
-)
-
-suppress_tinyusb_warnings()
-
-# Analyze the stack ############################################################
-
-add_stack_analysis(sbc_harness_stack.c sbc_harness_objs)
-
-# Link #########################################################################
-
-add_executable(sbc_harness)
-target_sources(sbc_harness PRIVATE
- sbc_harness_stack.c
- "$<TARGET_OBJECTS:sbc_harness_objs>"
-)
-target_link_libraries(sbc_harness
- pico_standard_link
-)
-target_link_options(sbc_harness PRIVATE "$<TARGET_PROPERTY:sbc_harness_objs,LINK_OPTIONS>")
-pico_add_extra_outputs(sbc_harness) # create .map/.bin/.hex/.uf2 files in addition to .elf
-pico_set_program_url(sbc_harness "https://git.lukeshu.com/sbc-harness")
-
-# Embed ########################################################################
-
-target_embed_sources(sbc_harness_objs sbc_harness static.h
- static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS.md
- static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS/agpl-3.0.txt
- static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS/pico-sdk.bsd3.txt
- static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS/printf.mit.txt
- static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS/tinyusb.mit.txt
- static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS/newlib.txt
- static/Documentation/harness_rom_bin.txt
- static/Documentation/harness_flash_bin.txt
-)
-
-endif()
diff --git a/cmd/sbc_harness/static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS/printf.mit.txt b/cmd/sbc_harness/static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS/printf.mit.txt
deleted file mode 120000
index 5a6e342..0000000
--- a/cmd/sbc_harness/static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS/printf.mit.txt
+++ /dev/null
@@ -1 +0,0 @@
-../../../../../3rd-party/pico-sdk/src/rp2_common/pico_printf/LICENSE \ No newline at end of file
diff --git a/cmd/sbc_harness/static/Documentation/harness_flash_bin.txt b/cmd/sbc_harness/static/Documentation/harness_flash_bin.txt
deleted file mode 100644
index 982d7e0..0000000
--- a/cmd/sbc_harness/static/Documentation/harness_flash_bin.txt
+++ /dev/null
@@ -1,24 +0,0 @@
-NAME
- /harness/flash.bin
-
-DESCRIPTION
- Access to the flash storage chip (where the harness firmware
- is stored).
-
- Any number of readers may read the flash contents.
-
-BUGS
- - The size of the chip is configured at compile-time. If the
- firmware is loaded onto hardware with a larger flash chip
- than it was compiled for, then the upper part of the chip
- will not be accessible with this file. If the firmware is
- loaded onto hardware with a smaller flash chip than it was
- compiled for, then accessing the missing upper part of the
- chip will crash.
-
- - This file is not writable; it aught to be possible to update
- the harness firmware by writing to this file.
-
-AUTHOR
- Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com>
- SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/cmd/sbc_harness/tusb_log.c b/cmd/sbc_harness/tusb_log.c
deleted file mode 100644
index 4c6b7df..0000000
--- a/cmd/sbc_harness/tusb_log.c
+++ /dev/null
@@ -1,15 +0,0 @@
-/* sbc_harness/tusb_log.c - Logger for tusb_config.h
- *
- * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com>
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
-
-#define LOG_NAME TINY_USB
-#include <libmisc/log.h>
-
-void _libmisc_tu_mess_failed(const char *expr,
- const char *file, unsigned int line, const char *func) {
- errorf("%s:%u:%s(): assertion \"%s\" failed",
- file, line, func,
- expr);
-}
diff --git a/cmd/sbc_harness/usb_keyboard.h b/cmd/sbc_harness/usb_keyboard.h
deleted file mode 100644
index 210014d..0000000
--- a/cmd/sbc_harness/usb_keyboard.h
+++ /dev/null
@@ -1,20 +0,0 @@
-/* sbc_harness/usb_keyboard.h - Implementation of a USB keyboard device
- *
- * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com>
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
-
-#ifndef _SBC_HARNESS_USB_KEYBOARD_H_
-#define _SBC_HARNESS_USB_KEYBOARD_H_
-
-#include <stdint.h> /* for uint32_t */
-
-#include <libcr/coroutine.h> /* for COROUTINE */
-#include <libcr_ipc/rpc.h> /* for CR_RPC_DECLARE */
-
-CR_RPC_DECLARE(usb_keyboard_rpc, uint32_t, int)
-
-void usb_keyboard_init(void);
-COROUTINE usb_keyboard_cr(void *arg);
-
-#endif /* _SBC_HARNESS_USB_KEYBOARD_H_ */
diff --git a/flashimg/cpu_hdmi/CMakeLists.txt b/flashimg/cpu_hdmi/CMakeLists.txt
new file mode 100644
index 0000000..9fc1c08
--- /dev/null
+++ b/flashimg/cpu_hdmi/CMakeLists.txt
@@ -0,0 +1,20 @@
+# flashimg/cpu_hdmi/CMakeLists.txt - Build script for auxiliary cpu_hdmi.uf2 firmware file
+#
+# Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+# SPDX-License-Identifier: AGPL-3.0-or-later
+
+if (PICO_PLATFORM STREQUAL "rp2040")
+
+add_executable(cpu_hdmi
+ main.c
+)
+target_link_libraries(cpu_hdmi
+ pico_runtime
+ pico_stdio
+)
+pico_minimize_runtime(cpu_hdmi)
+
+pico_add_extra_outputs(cpu_hdmi) # create .map/.bin/.hex/.uf2 files in addition to .elf
+pico_set_program_url(cpu_hdmi "https://git.lukeshu.com/sbc-harness")
+
+endif()
diff --git a/flashimg/cpu_hdmi/main.c b/flashimg/cpu_hdmi/main.c
new file mode 100644
index 0000000..cdba958
--- /dev/null
+++ b/flashimg/cpu_hdmi/main.c
@@ -0,0 +1,9 @@
+/* flashimg/cpu_hdmi/main.c - Entry point for the HDMI-decoder co-processor
+ *
+ * Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+int main() {
+ return 0;
+}
diff --git a/flashimg/cpu_main/CMakeLists.txt b/flashimg/cpu_main/CMakeLists.txt
new file mode 100644
index 0000000..ba47e88
--- /dev/null
+++ b/flashimg/cpu_main/CMakeLists.txt
@@ -0,0 +1,105 @@
+# flashimg/cpu_main/CMakeLists.txt - Build script for main cpu_main.uf2 firmware file
+#
+# Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+# SPDX-License-Identifier: AGPL-3.0-or-later
+
+if (PICO_PLATFORM STREQUAL "rp2040")
+
+# Compile ######################################################################
+
+add_library(cpu_main_objs OBJECT
+ main.c
+ usb_keyboard.c
+ tusb_log.c
+
+ fs_harness_flash_bin.c
+ fs_harness_uptime_txt.c
+
+ ihex.c
+)
+target_include_directories(cpu_main_objs PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/config)
+target_include_directories(cpu_main_objs PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
+target_include_directories(cpu_main_objs PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
+target_link_libraries(cpu_main_objs
+ pico_runtime
+
+ hardware_flash
+ hardware_uart
+ hardware_watchdog
+
+ libmisc
+ libusb
+ libdhcp
+ libhw_cr
+ lib9p_srv
+ lib9p_util
+)
+pico_minimize_runtime(cpu_main_objs
+ INCLUDE PANIC
+)
+target_compile_definitions(cpu_main_objs PRIVATE
+ PICO_PANIC_FUNCTION=assert_panic
+
+ #PICO_USE_FASTEST_SUPPORTED_CLOCK=1
+
+ # Calculated by `./3rd-party/pico-sdk/src/rp2_common/hardware_clocks/scripts/vcocalc.py --cmake-only 170`
+ PLL_SYS_REFDIV=2
+ PLL_SYS_VCO_FREQ_HZ=1530000000
+ PLL_SYS_POSTDIV1=3
+ PLL_SYS_POSTDIV2=3
+ SYS_CLK_HZ=170000000
+)
+
+suppress_tinyusb_warnings()
+set_source_files_properties(
+ ${PICO_SDK_PATH}/src/rp2_common/pico_clib_interface/newlib_interface.c
+ PROPERTIES
+ COMPILE_OPTIONS "-Wno-unused-parameter")
+
+# Analyze the stack ############################################################
+
+add_stack_analysis(stack.c cpu_main_objs)
+
+# Link #########################################################################
+
+add_executable(cpu_main)
+target_include_directories(cpu_main PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/config)
+target_sources(cpu_main PRIVATE
+ stack.c
+ "$<TARGET_OBJECTS:cpu_main_objs>"
+)
+target_link_libraries(cpu_main
+ pico_standard_link
+)
+target_link_options(cpu_main PRIVATE "$<TARGET_PROPERTY:cpu_main_objs,LINK_OPTIONS>")
+pico_add_extra_outputs(cpu_main) # create .map/.bin/.hex/.uf2 files in addition to .elf
+pico_set_program_url(cpu_main "https://git.lukeshu.com/sbc-harness")
+
+# Embed ########################################################################
+
+target_embed_sources(cpu_main_objs cpu_main static.h
+ static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS.md
+ static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS/agpl-3.0.txt
+ static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS/dhcp.bsd3-mit.txt
+ static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS/newlib.txt
+ static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS/pico-sdk.bsd3.txt
+ static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS/tinyusb.mit.txt
+ static/Documentation/harness_rom.bin.txt
+ static/Documentation/harness_flash.bin.txt
+ static/Documentation/harness_uptime.txt.txt
+)
+
+endif()
+
+# Tests ########################################################################
+if ((PICO_PLATFORM STREQUAL "host") AND (ENABLE_TESTS))
+ add_executable(test_ihex "tests/test_ihex.c" "ihex.c")
+ target_include_directories(test_ihex PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/tests)
+ target_link_libraries(test_ihex
+ libhw_generic
+ )
+ add_test(
+ NAME "flashimg/cpu_main/test_ihex"
+ COMMAND "${CMAKE_SOURCE_DIR}/build-aux/valgrind" "./test_ihex"
+ )
+endif()
diff --git a/cmd/sbc_harness/config/config.h b/flashimg/cpu_main/config/config.h
index da3edad..700b77b 100644
--- a/cmd/sbc_harness/config/config.h
+++ b/flashimg/cpu_main/config/config.h
@@ -1,4 +1,4 @@
-/* config.h - Compile-time configuration for sbc_harness
+/* flashimg/cpu_main/config/config.h - Compile-time configuration for the cpu_main firmware
*
* Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
@@ -9,6 +9,11 @@
#include <stddef.h> /* for size_t */
+#define CONFIG_FLASH_DEBUG 1
+
+#define _CONFIG_9P_MAX_CONNS 3 /* FIXME: bump this back up to 8 */
+#define _CONFIG_9P_MAX_REQS (2*_CONFIG_9P_MAX_CONNS)
+
/* RP2040 *********************************************************************/
#define CONFIG_RP2040_SPI_DEBUG 1 /* bool */
@@ -32,7 +37,15 @@
/* 9P *************************************************************************/
-#define CONFIG_9P_MAX_ERR_SIZE 128 /* 128 is what Plan 9 4e uses */
+#define CONFIG_9P_ENABLE_9P2000 1 /* bool */
+#define CONFIG_9P_ENABLE_9P2000_u 1 /* bool */
+#define CONFIG_9P_ENABLE_9P2000_e 0 /* bool */
+#define CONFIG_9P_ENABLE_9P2000_L 0 /* bool */
+#define CONFIG_9P_ENABLE_9P2000_p9p 0 /* bool */
+
+/* 9P_SRV *********************************************************************/
+
+#define CONFIG_9P_SRV_DEBUG 1 /* bool */
/**
* This max-msg-size is sized so that a Twrite message can return
@@ -52,21 +65,13 @@
* (8*1024)+160 in 2e and 3e.
*/
#define CONFIG_9P_SRV_MAX_MSG_SIZE ((4*1024)+24)
+#define CONFIG_9P_SRV_MAX_ERR_SIZE 128 /* 128 is what Plan 9 4e uses */
/**
* Maximum host-data-structure size. A message may be larger in
* unmarshaled-host-structures than marshaled-net-bytes due to (1)
* struct padding, (2) array pointers.
*/
#define CONFIG_9P_SRV_MAX_HOSTMSG_SIZE CONFIG_9P_SRV_MAX_MSG_SIZE+16
-#define CONFIG_9P_SRV_MAX_FIDS 16
-#define CONFIG_9P_SRV_MAX_REQS 2
-#define CONFIG_9P_SRV_MAX_DEPTH 3
-
-#define CONFIG_9P_ENABLE_9P2000 1 /* bool */
-#define CONFIG_9P_ENABLE_9P2000_u 1 /* bool */
-#define CONFIG_9P_ENABLE_9P2000_e 0 /* bool */
-#define CONFIG_9P_ENABLE_9P2000_L 0 /* bool */
-#define CONFIG_9P_ENABLE_9P2000_p9p 0 /* bool */
/* DHCP ***********************************************************************/
@@ -88,13 +93,15 @@
/* COROUTINE ******************************************************************/
-extern const size_t CONFIG_COROUTINE_STACK_SIZE_dhcp_cr;
-extern const size_t CONFIG_COROUTINE_STACK_SIZE_init_cr;
-extern const size_t CONFIG_COROUTINE_STACK_SIZE_lib9p_srv_write_cr;
-extern const size_t CONFIG_COROUTINE_STACK_SIZE_read9p_cr;
-extern const size_t CONFIG_COROUTINE_STACK_SIZE_usb_common_cr;
-extern const size_t CONFIG_COROUTINE_STACK_SIZE_usb_keyboard_cr;
-extern const size_t CONFIG_COROUTINE_STACK_SIZE_w5500_irq_cr;
+#define CONFIG_COROUTINE_STACK_PREALLOCATE /* defined-or-not */
+extern char COROUTINE_STACK_init_cr []; extern const size_t COROUTINE_STACK_init_cr_len;
+extern char COROUTINE_STACK_dhcp_cr []; extern const size_t COROUTINE_STACK_dhcp_cr_len;
+extern char *const COROUTINE_STACK_read9p_cr []; extern const size_t COROUTINE_STACK_read9p_cr_len;
+extern char *const COROUTINE_STACK_write9p_cr []; extern const size_t COROUTINE_STACK_write9p_cr_len;
+extern char COROUTINE_STACK_usb_common_cr []; extern const size_t COROUTINE_STACK_usb_common_cr_len;
+extern char COROUTINE_STACK_usb_keyboard_cr[]; extern const size_t COROUTINE_STACK_usb_keyboard_cr_len;
+extern char COROUTINE_STACK_w5500_irq_cr []; extern const size_t COROUTINE_STACK_w5500_irq_cr_len;
+
#define CONFIG_COROUTINE_NAME_LEN 16
#define CONFIG_COROUTINE_MEASURE_STACK 1 /* bool */
#define CONFIG_COROUTINE_PROTECT_STACK 1 /* bool */
@@ -102,12 +109,13 @@ extern const size_t CONFIG_COROUTINE_STACK_SIZE_w5500_irq_cr;
#define CONFIG_COROUTINE_VALGRIND 0 /* bool */
#define CONFIG_COROUTINE_GDB 1 /* bool */
-#define _CONFIG_9P_NUM_SOCKS 3 /* FIXME: bump this back up to 8 */
-#define CONFIG_COROUTINE_NUM ( \
- 1 /* usb_common */ + \
- 1 /* usb_keyboard */ + \
- 1 /* W5500 irq handler */ + \
- _CONFIG_9P_NUM_SOCKS /* 9P accept()+read() */ + \
- (CONFIG_9P_SRV_MAX_REQS*_CONFIG_9P_NUM_SOCKS) /* 9P work+write() */ )
+#define CONFIG_COROUTINE_NUM ( \
+ 1 /* init_cr */ + \
+ 1 /* usb_common_cr */ + \
+ 1 /* usb_keyboard_cr */ + \
+ 1 /* dhcp_cr */ + \
+ _CONFIG_9P_MAX_CONNS /* read9p_cr */ + \
+ _CONFIG_9P_MAX_REQS /* write9p_cr */ + \
+ 1 /* W5500_irq_cr */ )
#endif /* _CONFIG_H_ */
diff --git a/cmd/sbc_harness/config/tusb_config.h b/flashimg/cpu_main/config/tusb_config.h
index 0a6d3e4..18be9b5 100644
--- a/cmd/sbc_harness/config/tusb_config.h
+++ b/flashimg/cpu_main/config/tusb_config.h
@@ -1,6 +1,6 @@
-/* tusb_config.h - Compile-time configuration for the TinyUSB library
+/* flashimg/cpu_main/config/tusb_config.h - Compile-time configuration for the TinyUSB library
*
- * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
*
* SPDX-License-Identifier: MIT
@@ -31,40 +31,31 @@
#ifndef _TUSB_CONFIG_H_
#define _TUSB_CONFIG_H_
+#include <stdint.h> /* for uint{n}_t */
+
#ifdef __cplusplus
extern "C" {
#endif
//--------------------------------------------------------------------
-// Override the default definition of TU_ASSERT() to use our logging
+// Override the default TU_MESS_FAILED() and tu_print_*() to use our logging
//--------------------------------------------------------------------
-// "magically" select between the 1-arg and 2-args variants, inject a
-// stringified version of `_cond`.
-//
-// Note: Use GNU-C `, ##__VA_ARGS__`, not standard C `__VA_OPT__(,)
-// __VA_ARGS__`; because __VA_OPT__ doesn't handle present-but-empty
-// arguments the way that we need.
-#define TU_ASSERT(_cond, ...) \
- _GET_3RD_ARG(_cond, ##__VA_ARGS__, \
- _LIBMISC_TU_ASSERT_2ARGS, _LIBMISC_TU_ASSERT_1ARGS, _dummy) \
- (_cond, #_cond, ##__VA_ARGS__)
-
-#define _LIBMISC_TU_ASSERT_1ARGS(_cond, _cond_str) \
- _LIBMISC_TU_ASSERT_2ARGS(_cond, _cond_str, false)
-
-#define _LIBMISC_TU_ASSERT_2ARGS(_cond, _cond_str, _ret) \
- do { \
- if ( !(_cond) ) { \
- _libmisc_tu_mess_failed(_cond_str, \
- __FILE__, __LINE__, __func__); \
- TU_BREAKPOINT(); \
- return _ret; \
- } \
- } while(0)
-
-void _libmisc_tu_mess_failed(const char *expr,
- const char *file, unsigned int line, const char *func);
+#define TU_MESS_FAILED(_cond_str) _libmisc_tu_mess_failed(_cond_str, __FILE__, __LINE__, __func__)
+#define tu_print_str _libmisc_tu_print_str
+#define tu_print_byte _libmisc_tu_print_byte
+#define tu_print_base10 _libmisc_tu_print_base10
+#define tu_print_base16 _libmisc_tu_print_base16
+#define tu_print_base16_u8 _libmisc_tu_print_base16_u8
+#define tu_print_base16_u16 _libmisc_tu_print_base16_u16
+
+void _libmisc_tu_mess_failed(const char *expr, const char *file, unsigned int line, const char *func);
+void _libmisc_tu_print_str(const char *);
+void _libmisc_tu_print_byte(uint8_t);
+void _libmisc_tu_print_base10(unsigned long);
+void _libmisc_tu_print_base16(unsigned long);
+void _libmisc_tu_print_base16_u8(uint8_t);
+void _libmisc_tu_print_base16_u16(uint16_t);
//--------------------------------------------------------------------
// Configuration: common
diff --git a/flashimg/cpu_main/fs_harness_flash_bin.c b/flashimg/cpu_main/fs_harness_flash_bin.c
new file mode 100644
index 0000000..7b41c86
--- /dev/null
+++ b/flashimg/cpu_main/fs_harness_flash_bin.c
@@ -0,0 +1,332 @@
+/* flashimg/cpu_main/fs_harness_flash_bin.c - 9P access to flash storage
+ *
+ * Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#include <string.h>
+
+#include <hardware/flash.h>
+#include <hardware/watchdog.h>
+
+#define LOG_NAME FLASH
+#include <libmisc/log.h>
+
+#include <util9p/static.h>
+
+#define IMPLEMENTATION_FOR_FS_HARNESS_FLASH_BIN YES
+#include "fs_harness_flash_bin.h"
+
+LO_IMPLEMENTATION_C(lib9p_srv_file, struct flash_file, flash_file);
+LO_IMPLEMENTATION_STATIC(lib9p_srv_fio, struct flash_file, flash_file);
+
+#define DATA_START ((const char *)(XIP_NOALLOC_BASE))
+#define DATA_SIZE PICO_FLASH_SIZE_BYTES
+#define DATA_HSIZE (DATA_SIZE/2)
+static_assert(DATA_SIZE % FLASH_SECTOR_SIZE == 0);
+static_assert(DATA_HSIZE % FLASH_SECTOR_SIZE == 0);
+
+/* There are some memcpy()s (and memcmp()s?) in here that maybe should
+ * be replaced with SSI DMA. */
+
+/* ab_flash_* (mid-level utilities for our A/B write scheme) ******************/
+
+/**
+ * Copy the upper half of flash to the lower half of flash, then reboot.
+ *
+ * @param buf : a scratch buffer that is at least FLASH_SECTOR_SIZE
+ */
+[[noreturn]] static void __no_inline_not_in_flash_func(ab_flash_finalize)(uint8_t *buf) {
+ assert(buf);
+
+ log_infoln("copying upper flash to lower flash...");
+
+ cr_save_and_disable_interrupts();
+
+ for (size_t off = 0; off < DATA_HSIZE; off += FLASH_SECTOR_SIZE) {
+ memcpy(buf, DATA_START+DATA_HSIZE+off, FLASH_SECTOR_SIZE);
+ if (memcmp(DATA_START+off, buf, FLASH_SECTOR_SIZE) == 0)
+ continue;
+ flash_range_erase(off, FLASH_SECTOR_SIZE);
+ flash_range_program(off, buf, FLASH_SECTOR_SIZE);
+ }
+
+ log_infoln("rebooting...");
+
+ watchdog_reboot(0, 0, 300);
+
+ for (;;)
+ asm volatile ("nop");
+}
+
+/**
+ * Set the upper half of flash to all "1" bits.
+ *
+ * @param buf : a scratch buffer that is at least FLASH_SECTOR_SIZE
+ */
+static void ab_flash_initialize(void *buf) {
+ assert(buf);
+
+ memset(buf, 0xFF, FLASH_SECTOR_SIZE);
+
+ log_infoln("erasing upper flash...");
+ for (size_t off = DATA_HSIZE; off < DATA_SIZE; off += FLASH_SECTOR_SIZE) {
+ if (memcmp(buf, DATA_START+off, FLASH_SECTOR_SIZE) == 0)
+ continue;
+ bool saved = cr_save_and_disable_interrupts();
+ flash_range_erase(off, FLASH_SECTOR_SIZE);
+ cr_restore_interrupts(saved);
+ }
+ log_debugln("... erased");
+}
+
+/**
+ * Write `dat` to flash sector `pos`+(DATA_SIZE/2) (i.e. `pos` is a
+ * sector in the lower half, but this function writes to the upper
+ * half).
+ *
+ * @param pos : start-position of the sector to write to
+ * @param dat : the FLASH_SECTOR_SIZE bytes to write
+ */
+static void ab_flash_write_sector(size_t pos, uint8_t *dat) {
+ assert(pos < DATA_HSIZE);
+ assert(pos % FLASH_SECTOR_SIZE == 0);
+ assert(dat);
+
+ pos += DATA_HSIZE;
+
+ log_infoln("write flash sector @ ", (base16_u32_, pos), "...");
+ if (memcmp(dat, DATA_START+pos, FLASH_SECTOR_SIZE) != 0) {
+ bool saved = cr_save_and_disable_interrupts();
+ flash_range_erase(pos, FLASH_SECTOR_SIZE);
+ flash_range_program(pos, dat, FLASH_SECTOR_SIZE);
+ cr_restore_interrupts(saved);
+ }
+ log_debugln("... written");
+}
+
+/* io_preader_to, io_pwriter, io_closer ***************************************/
+
+LO_IMPLEMENTATION_STATIC(io_preader_to, struct flashio, flashio);
+LO_IMPLEMENTATION_STATIC(io_pwriter, struct flashio, flashio);
+LO_IMPLEMENTATION_STATIC(io_closer, struct flashio, flashio);
+
+/** read from anywhere on the chip */
+static size_t_and_error flashio_pread_to(struct flashio *self, lo_interface io_writer dst, uoff_t _src_offset, size_t count) {
+ assert(self);
+
+ if (_src_offset > DATA_SIZE)
+ return ERROR_AND(size_t, 0, error_new(E_POSIX_EINVAL, "offset is past the chip size"));
+ size_t src_offset = (size_t) _src_offset;
+
+ if (src_offset == DATA_SIZE)
+ return ERROR_AND(size_t, 0, error_new(E_EOF));
+
+ /* Assume that somewhere down the line the pointer we pass to
+ * io_write() will be passed to DMA. We don't want the DMA
+ * engine to hit (slow) XIP (for instance, this can cause
+ * reads/writes to the SSP to get out of sync with eachother).
+ * So, copy the data to a buffer in (fast) RAM first. It's
+ * lame that the DMA engine can only have a DREQ on one side
+ * of the channel.
+ */
+ size_t sector_base = LM_ROUND_DOWN(src_offset, FLASH_SECTOR_SIZE);
+ if (src_offset + count > sector_base + FLASH_SECTOR_SIZE)
+ count = (sector_base + FLASH_SECTOR_SIZE) - src_offset;
+ assert(src_offset + count <= DATA_SIZE);
+
+ if (!self->rbuf.ok || self->rbuf.pos != sector_base) {
+ self->rbuf.ok = true;
+ self->rbuf.pos = sector_base;
+ memcpy(self->rbuf.dat, DATA_START+sector_base, FLASH_SECTOR_SIZE);
+ }
+
+ return io_write(dst, &self->rbuf.dat[src_offset-sector_base], count);
+}
+
+/** takes offsets in the lower half, writes to the upper half */
+static size_t_and_error flashio_pwritev(struct flashio *self, const struct wr_iovec *iov, int iovcnt, uoff_t _offset) {
+ assert(self);
+ assert(iov);
+ assert(iovcnt > 0);
+
+ size_t total_count = 0;
+ for (int i = 0; i < iovcnt; i++)
+ total_count += iov[i].iov_len;
+ assert(total_count > 0);
+
+ if (_offset >= DATA_HSIZE)
+ return ERROR_AND(size_t, 0, error_new(E_POSIX_ENOSPC, "cannot write past half the chip size"));
+ size_t offset = (size_t) _offset;
+
+ size_t total_done = 0;
+ for (int i = 0; i < iovcnt; i++) {
+ size_t iov_done = 0;
+ while (iov_done < iov[i].iov_len) {
+ if (offset >= DATA_HSIZE)
+ return ERROR_AND(size_t, total_done, error_new(E_POSIX_ENOSPC, "cannot write past half the chip size"));
+ size_t sector_base = LM_ROUND_DOWN(offset, FLASH_SECTOR_SIZE);
+ size_t len = iov[i].iov_len - iov_done;
+ if (offset + len > sector_base + FLASH_SECTOR_SIZE)
+ len = (sector_base + FLASH_SECTOR_SIZE) - offset;
+ assert(offset + len <= DATA_HSIZE);
+
+ if (self->wbuf.ok && self->wbuf.pos != sector_base)
+ ab_flash_write_sector(self->wbuf.pos, self->wbuf.dat);
+ if (!self->wbuf.ok || self->wbuf.pos != sector_base) {
+ self->wbuf.ok = true;
+ self->wbuf.pos = sector_base;
+ if (len != FLASH_SECTOR_SIZE)
+ /* Don't bother with a read if we're just going to overwrite it. */
+ memcpy(self->wbuf.dat, DATA_START+DATA_HSIZE+sector_base, FLASH_SECTOR_SIZE);
+ }
+ memcpy(&self->wbuf.dat[offset-sector_base], iov[i].iov_write_from+iov_done, len);
+ total_done += len;
+ iov_done += len;
+ offset += len;
+ }
+ }
+ return ERROR_AND(size_t, total_done, ERROR_NULL);
+}
+
+static error flashio_close(struct flashio *self) {
+ assert(self);
+
+ if (self->finalize) {
+ if (self->wbuf.ok)
+ ab_flash_write_sector(self->wbuf.pos, self->wbuf.dat);
+ ab_flash_finalize(self->wbuf.dat);
+ }
+
+ return ERROR_NULL;
+}
+
+/* srv_file *******************************************************************/
+
+void flash_file_free(struct flash_file *self) {
+ assert(self);
+}
+struct lib9p_qid flash_file_qid(struct flash_file *self) {
+ assert(self);
+
+ return (struct lib9p_qid){
+ .type = LIB9P_QT_FILE|LIB9P_QT_EXCL|LIB9P_QT_APPEND,
+ .vers = 1,
+ .path = self->pathnum,
+ };
+}
+
+error flash_file_stat(struct flash_file *self, struct lib9p_srv_ctx *ctx, struct lib9p_srv_stat *out) {
+ assert(self);
+ assert(ctx);
+ assert(out);
+
+ *out = ((struct lib9p_srv_stat){
+ .qid = flash_file_qid(self),
+ .mode = LIB9P_DM_EXCL|LIB9P_DM_APPEND|0666,
+ .atime_sec = UTIL9P_ATIME,
+ .mtime_sec = UTIL9P_MTIME,
+ .size = DATA_SIZE,
+ .name = lib9p_str(self->name),
+ .owner_uid = { .name = lib9p_str("root"), .num = 0 },
+ .owner_gid = { .name = lib9p_str("root"), .num = 0 },
+ .last_modifier_uid = { .name = lib9p_str("root"), .num = 0 },
+ .extension = lib9p_str(NULL),
+ });
+ return ERROR_NULL;
+}
+error flash_file_wstat(struct flash_file *self, struct lib9p_srv_ctx *ctx, struct lib9p_srv_stat) {
+ assert(self);
+ assert(ctx);
+
+ return error_new(E_POSIX_EROFS, "read-only part of filesystem");
+}
+error flash_file_remove(struct flash_file *self, struct lib9p_srv_ctx *ctx) {
+ assert(self);
+ assert(ctx);
+
+ return error_new(E_POSIX_EROFS, "read-only part of filesystem");
+}
+
+LIB9P_SRV_NOTDIR(, struct flash_file, flash_file);
+
+static error flash_handle_ihex_data(void *, uint32_t off, uint8_t count, uint8_t *dat);
+static error flash_handle_ihex_eof(void *);
+
+lib9p_srv_fio_or_error flash_file_fopen(struct flash_file *self, struct lib9p_srv_ctx *ctx,
+ bool LM_UNUSED(rd), bool wr, bool LM_UNUSED(trunc)) {
+ assert(self);
+ assert(ctx);
+
+ memset(&self->io, 0, sizeof(self->io));
+ if (wr) {
+ ab_flash_initialize(self->io.wbuf.dat);
+ }
+
+ memset(&self->ihex, 0, sizeof(self->ihex));
+ self->ihex.handle_arg = self;
+ self->ihex.handle_data = flash_handle_ihex_data;
+ self->ihex.handle_eof = flash_handle_ihex_eof;
+
+ return ERROR_NEW_VAL(lib9p_srv_fio, LO_BOX(lib9p_srv_fio, self));
+}
+
+/* srv_fio ********************************************************************/
+
+static struct lib9p_qid flash_file_ioqid(struct flash_file *self) {
+ return flash_file_qid(self);
+}
+static uint32_t flash_file_iounit(struct flash_file *self) {
+ assert(self);
+ return FLASH_SECTOR_SIZE;
+}
+
+static void flash_file_iofree(struct flash_file *self) {
+ assert(self);
+
+ error err = flashio_close(&self->io);
+ assert(ERROR_IS_NULL(err));
+
+ err = ihex_decoder_close(&self->ihex);
+ assert(ERROR_IS_NULL(err));
+}
+
+static error flash_file_pread(struct flash_file *self, struct lib9p_srv_ctx *ctx,
+ lo_interface io_writer dst, uint64_t byte_offset, uint32_t byte_count) {
+ assert(self);
+ assert(ctx);
+
+ return flashio_pread_to(&self->io, dst, byte_offset, byte_count).err;
+}
+
+static error flash_handle_ihex_data(void *_self, uint32_t off, uint8_t count, uint8_t *dat) {
+ struct flash_file *self = _self;
+
+ if (off < XIP_BASE || off >= XIP_BASE + DATA_HSIZE)
+ return error_new(E_POSIX_ENOSPC, "cannot write outside of the first half of the chip: ",
+ (base16_u32_, off), " is outside of [", (base16_u32_, XIP_BASE), ",", (base16_u32_, XIP_BASE + DATA_HSIZE), ")");
+
+ return flashio_pwritev(&self->io,
+ &((struct wr_iovec){.iov_write_from = dat, .iov_len = count}), 1,
+ off - XIP_BASE).err;
+}
+static error flash_handle_ihex_eof(void *_self) {
+ struct flash_file *self = _self;
+ self->io.finalize = true;
+ return ERROR_NULL;
+}
+
+/* TODO: Also support uf2, not just ihex. */
+static uint32_t_or_error flash_file_pwrite(struct flash_file *self, struct lib9p_srv_ctx *ctx,
+ const void *buf,
+ uint32_t byte_count,
+ uint64_t LM_UNUSED(byte_offset)) {
+ assert(self);
+ assert(ctx);
+
+ size_t_and_error r = ihex_decoder_writev(&self->ihex,
+ &((struct wr_iovec){.iov_write_from = buf, .iov_len = byte_count}), 1);
+ if (r.size_t == 0 && !ERROR_IS_NULL(r.err))
+ return ERROR_NEW_ERR(uint32_t, r.err);
+ return ERROR_NEW_VAL(uint32_t, (uint32_t) r.size_t);
+}
diff --git a/flashimg/cpu_main/fs_harness_flash_bin.h b/flashimg/cpu_main/fs_harness_flash_bin.h
new file mode 100644
index 0000000..68cc953
--- /dev/null
+++ b/flashimg/cpu_main/fs_harness_flash_bin.h
@@ -0,0 +1,36 @@
+/* flashimg/cpu_main/fs_harness_flash_bin.h - 9P access to flash storage
+ *
+ * Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#ifndef _FLASHIMG_CPU_MAIN_FS_HARNESS_FLASH_BIN_H_
+#define _FLASHIMG_CPU_MAIN_FS_HARNESS_FLASH_BIN_H_
+
+#include <hardware/flash.h> /* for FLASH_SECTOR_SIZE */
+
+#include <lib9p/srv.h>
+
+#include "ihex.h"
+
+struct flashio {
+ bool finalize;
+ struct {
+ bool ok;
+ size_t pos;
+ uint8_t dat[FLASH_SECTOR_SIZE];
+ } wbuf, rbuf;
+};
+
+struct flash_file {
+ char *name;
+ uint64_t pathnum;
+
+ BEGIN_PRIVATE(FS_HARNESS_FLASH_BIN);
+ struct flashio io;
+ struct ihex_decoder ihex;
+ END_PRIVATE(FS_HARNESS_FLASH_BIN);
+};
+LO_IMPLEMENTATION_H(lib9p_srv_file, struct flash_file, flash_file);
+
+#endif /* _FLASHIMG_CPU_MAIN_FS_HARNESS_FLASH_BIN_H_ */
diff --git a/flashimg/cpu_main/fs_harness_uptime_txt.c b/flashimg/cpu_main/fs_harness_uptime_txt.c
new file mode 100644
index 0000000..0a2797f
--- /dev/null
+++ b/flashimg/cpu_main/fs_harness_uptime_txt.c
@@ -0,0 +1,167 @@
+/* flashimg/cpu_main/fs_harness_uptime_txt.c - 9P access to harness uptime
+ *
+ * Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#include <libhw/generic/alarmclock.h>
+#include <libmisc/alloc.h> /* for heap_alloc(), free() */
+#include <libmisc/fmt.h> /* for fmt_snprint() */
+#include <util9p/static.h>
+
+#include "fs_harness_uptime_txt.h"
+
+LO_IMPLEMENTATION_C(lib9p_srv_file, struct uptime_file, uptime_file);
+
+struct uptime_fio {
+ struct uptime_file *parent;
+ size_t buf_len;
+ /* The maximum length (UINT64_MAX) string is 52 bytes, not
+ * including a nul-terminator:
+ *
+ * "18446744073709551615ns\n" # 22+1
+ * "584y343d 23h34m33.709551615s\n" # 28+1
+ */
+ char buf[52];
+};
+LO_IMPLEMENTATION_STATIC(lib9p_srv_fio, struct uptime_fio, uptime_fio);
+
+/* srv_file *******************************************************************/
+
+void uptime_file_free(struct uptime_file *self) {
+ assert(self);
+}
+struct lib9p_qid uptime_file_qid(struct uptime_file *self) {
+ assert(self);
+
+ return (struct lib9p_qid){
+ .type = LIB9P_QT_FILE,
+ .vers = 1,
+ .path = self->pathnum,
+ };
+}
+
+error uptime_file_stat(struct uptime_file *self, struct lib9p_srv_ctx *ctx, struct lib9p_srv_stat *out) {
+ assert(self);
+ assert(ctx);
+ assert(out);
+
+ uint64_t now = LO_CALL(bootclock, get_time_ns);
+ uint64_t size = 0;
+ while (now) {
+ size++;
+ now /= 10;
+ }
+ if (!size)
+ size++;
+ size += 3;
+
+ *out = ((struct lib9p_srv_stat){
+ .qid = uptime_file_qid(self),
+ .mode = 0444,
+ .atime_sec = UTIL9P_ATIME,
+ .mtime_sec = UTIL9P_MTIME,
+ .size = size,
+ .name = lib9p_str(self->name),
+ .owner_uid = { .name = lib9p_str("root"), .num = 0 },
+ .owner_gid = { .name = lib9p_str("root"), .num = 0 },
+ .last_modifier_uid = { .name = lib9p_str("root"), .num = 0 },
+ .extension = lib9p_str(NULL),
+ });
+ return ERROR_NULL;
+}
+error uptime_file_wstat(struct uptime_file *self, struct lib9p_srv_ctx *ctx, struct lib9p_srv_stat) {
+ assert(self);
+ assert(ctx);
+
+ return error_new(E_POSIX_EROFS, "read-only part of filesystem");
+}
+error uptime_file_remove(struct uptime_file *self, struct lib9p_srv_ctx *ctx) {
+ assert(self);
+ assert(ctx);
+
+ return error_new(E_POSIX_EROFS, "read-only part of filesystem");
+}
+
+LIB9P_SRV_NOTDIR(, struct uptime_file, uptime_file);
+
+lib9p_srv_fio_or_error uptime_file_fopen(struct uptime_file *self, struct lib9p_srv_ctx *ctx,
+ bool LM_UNUSED(rd), bool LM_UNUSED(wr), bool LM_UNUSED(trunc)) {
+ assert(self);
+ assert(ctx);
+
+ struct uptime_fio *ret = heap_alloc(1, struct uptime_fio);
+ ret->parent = self;
+ ret->buf_len = 0;
+
+ return ERROR_NEW_VAL(lib9p_srv_fio, LO_BOX(lib9p_srv_fio, ret));
+}
+
+/* srv_fio ********************************************************************/
+
+static uint32_t uptime_fio_iounit(struct uptime_fio *self) {
+ assert(self);
+ return sizeof(self->buf);
+}
+
+static void uptime_fio_iofree(struct uptime_fio *self) {
+ assert(self);
+ free(self);
+}
+
+static struct lib9p_qid uptime_fio_ioqid(struct uptime_fio *self) {
+ assert(self);
+ assert(self->parent);
+ return uptime_file_qid(self->parent);
+}
+
+#define NS_PER_M (NS_PER_S*60)
+#define NS_PER_H (NS_PER_S*60*60)
+#define NS_PER_D (NS_PER_S*60*60*24)
+#define NS_PER_Y (NS_PER_S*60*60*24*365)
+
+static error uptime_fio_pread(struct uptime_fio *self, struct lib9p_srv_ctx *ctx,
+ lo_interface io_writer dst, uint64_t byte_offset, uint32_t byte_count) {
+ assert(self);
+ assert(ctx);
+
+ if (byte_offset == 0 || self->buf_len == 0) {
+ uint64_t now = LO_CALL(bootclock, get_time_ns);
+ self->buf_len = fmt_snprint(self->buf, sizeof(self->buf), now, "ns\n");
+
+ uint64_t ns = now;
+ uint64_t y = ns/NS_PER_Y; ns -= y*NS_PER_Y;
+ uint64_t d = ns/NS_PER_D; ns -= d*NS_PER_D;
+ uint64_t h = ns/NS_PER_H; ns -= h*NS_PER_H;
+ uint64_t m = ns/NS_PER_M; ns -= m*NS_PER_M;
+ uint64_t s = ns/NS_PER_S; ns -= s*NS_PER_S;
+ if (y)
+ self->buf_len += fmt_snprint(&self->buf[self->buf_len], sizeof(self->buf)-self->buf_len, y, "y");
+ if (y || d)
+ self->buf_len += fmt_snprint(&self->buf[self->buf_len], sizeof(self->buf)-self->buf_len, d, "d ");
+ if (y || d || h)
+ self->buf_len += fmt_snprint(&self->buf[self->buf_len], sizeof(self->buf)-self->buf_len, h, "h");
+ if (y || d || h || m)
+ self->buf_len += fmt_snprint(&self->buf[self->buf_len], sizeof(self->buf)-self->buf_len, m, "m");
+ self->buf_len += fmt_snprint(&self->buf[self->buf_len], sizeof(self->buf)-self->buf_len, s, ".", (rjust, 9, '0', ns), "s\n");
+ }
+
+ if (byte_offset > (uint64_t)self->buf_len)
+ return error_new(E_POSIX_EINVAL, "offset is past end-of-file length");
+
+ size_t beg_off = (size_t)byte_offset;
+ size_t end_off = beg_off + (size_t)byte_count;
+ if (end_off > self->buf_len)
+ end_off = self->buf_len;
+ return io_write(dst, &self->buf[beg_off], end_off-beg_off).err;
+}
+
+static uint32_t_or_error uptime_fio_pwrite(struct uptime_fio *self, struct lib9p_srv_ctx *ctx,
+ const void *LM_UNUSED(buf),
+ uint32_t LM_UNUSED(byte_count),
+ uint64_t LM_UNUSED(byte_offset)) {
+ assert(self);
+ assert(ctx);
+
+ return ERROR_NEW_ERR(uint32_t, error_new(E_POSIX_EROFS, "read-only part of filesystem"));
+}
diff --git a/flashimg/cpu_main/fs_harness_uptime_txt.h b/flashimg/cpu_main/fs_harness_uptime_txt.h
new file mode 100644
index 0000000..fcf8dbc
--- /dev/null
+++ b/flashimg/cpu_main/fs_harness_uptime_txt.h
@@ -0,0 +1,18 @@
+/* flashimg/cpu_main/fs_harness_uptime_txt.h - 9P access to harness uptime
+ *
+ * Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#ifndef _FLASHIMG_CPU_MAIN_FS_HARNESS_UPTIME_TXT_H_
+#define _FLASHIMG_CPU_MAIN_FS_HARNESS_UPTIME_TXT_H_
+
+#include <lib9p/srv.h>
+
+struct uptime_file {
+ char *name;
+ uint64_t pathnum;
+};
+LO_IMPLEMENTATION_H(lib9p_srv_file, struct uptime_file, uptime_file);
+
+#endif /* _FLASHIMG_CPU_MAIN_FS_HARNESS_UPTIME_TXT_H_ */
diff --git a/flashimg/cpu_main/ihex.c b/flashimg/cpu_main/ihex.c
new file mode 100644
index 0000000..588a541
--- /dev/null
+++ b/flashimg/cpu_main/ihex.c
@@ -0,0 +1,231 @@
+/* flashimg/cpu_main/ihex.c - Intel Hex decoder
+ *
+ * Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+/* https://archive.org/details/IntelHEXStandard */
+
+#include <string.h> /* for memchr() */
+
+#include <libmisc/assert.h>
+#include <libmisc/endian.h>
+
+#define IMPLEMENTATION_FOR_IHEX_H YES
+#include "ihex.h"
+
+LO_IMPLEMENTATION_C(io_writer, struct ihex_decoder, ihex_decoder);
+LO_IMPLEMENTATION_C(io_closer, struct ihex_decoder, ihex_decoder);
+
+enum ihex_record_type {
+ /* [U]SBA: [Upper] Segment Base Address : SBA = USBA<< 4 */
+ /* [U]LBA: [Upper] Linear Base Address : LBA = ULBA<<16 */
+ /* _EXT records define where DATA records are written to */
+ /* _START records define where execution should start */
+ IHEX_REC_DATA = 0x00, /* .dat is .len bytes of data, which go at either (USBA<<4)+(.off%64KiB) or ((ULBA<<16)+.off)%4GiB */
+ IHEX_REC_EOF = 0x01, /* .len=0, .off=0 */
+ IHEX_REC_ADDR_SEG_EXT = 0x02, /* .len=2, .off=0, .dat is u16be USBA */
+ IHEX_REC_ADDR_SEG_START = 0x03, /* .len=4, .off=0, .dat is u16be CS register then u16be IP register */
+ IHEX_REC_ADDR_LIN_EXT = 0x04, /* .len=2, .off=0, .dat is u16be ULBA */
+ IHEX_REC_ADDR_LIN_START = 0x05, /* .len=4, .off=0, .dat is u32be EIP register */
+};
+
+struct ihex_record {
+ uint8_t len;
+ uint16_t off;
+ enum ihex_record_type typ;
+ uint8_t *dat;
+};
+
+static error ihex_handle_record(struct ihex_decoder *self, struct ihex_record *rec) {
+ switch (rec->typ) {
+ case IHEX_REC_ADDR_SEG_EXT:
+ self->addr_mode = _IHEX_MODE_SEG;
+ self->addr_base = ((uint32_t)uint16be_decode(rec->dat)) << 4;
+ return ERROR_NULL;
+ case IHEX_REC_ADDR_LIN_EXT:
+ self->addr_mode = _IHEX_MODE_LIN;
+ self->addr_base = ((uint32_t)uint16be_decode(rec->dat)) << 16;
+ return ERROR_NULL;;
+ case IHEX_REC_DATA:
+ switch (self->addr_mode) {
+ case _IHEX_MODE_NONE:
+ return error_new(E_POSIX_EINVAL, "ihex: data record before base-address record");
+ case _IHEX_MODE_SEG:
+ if (!self->handle_data)
+ return ERROR_NULL;
+ if (rec->len <= UINT16_MAX - rec->off) {
+ /* 1 write */
+ return self->handle_data(self->handle_arg, self->addr_base + rec->off, rec->len, rec->dat);
+ } else {
+ /* wraps around; split into 2 writes */
+ uint8_t first_len = (uint8_t) (UINT16_MAX - rec->off);
+ if (first_len) {
+ error err = self->handle_data(self->handle_arg, self->addr_base + rec->off, first_len, rec->dat);
+ if (!ERROR_IS_NULL(err))
+ return err;
+ }
+ return self->handle_data(self->handle_arg, self->addr_base, rec->len - first_len, &rec->dat[first_len]);
+ }
+ case _IHEX_MODE_LIN:
+ if (!self->handle_data)
+ return ERROR_NULL;
+ uint32_t off = self->addr_base + rec->off;
+ if (rec->len <= UINT32_MAX - off) {
+ /* 1 write */
+ return self->handle_data(self->handle_arg, off, rec->len, rec->dat);
+ } else {
+ /* wraps around; split into 2 writes */
+ uint8_t first_len = (uint8_t) (UINT32_MAX - off);
+ if (first_len) {
+ error err = self->handle_data(self->handle_arg, off, first_len, rec->dat);
+ if (!ERROR_IS_NULL(err))
+ return err;
+ }
+ return self->handle_data(self->handle_arg, 0, rec->len - first_len, &rec->dat[first_len]);
+ }
+ default:
+ assert_notreached("bad addr_mode");
+ }
+ case IHEX_REC_EOF:
+ self->seen_eof = true;
+ if (!self->handle_eof)
+ return ERROR_NULL;
+ return self->handle_eof(self->handle_arg);
+ case IHEX_REC_ADDR_SEG_START:
+ if (!self->handle_set_exec_start_seg)
+ return ERROR_NULL;
+ uint16_t cs = uint16be_decode(&rec->dat[0]);
+ uint16_t ip = uint16be_decode(&rec->dat[2]);
+ return self->handle_set_exec_start_seg(self->handle_arg, cs, ip);
+ case IHEX_REC_ADDR_LIN_START:
+ if (!self->handle_set_exec_start_lin)
+ return ERROR_NULL;
+ uint32_t eip = uint32be_decode(rec->dat);
+ return self->handle_set_exec_start_lin(self->handle_arg, eip);
+ default:
+ assert_notreached("bad record type");
+ }
+}
+
+/**
+ * Hex-decode the byte 0xAB, and push it onto self->buf. If this
+ * completes the record in self->buf, then handle that record.
+ *
+ * @return the number of ASCII bytes consumed (0, 1, or 2) before
+ * encountering an error.
+ */
+static size_t_and_error ihex_decode_byte(struct ihex_decoder *self, char a, char b) {
+ uint8_t byte;
+ if ('0' <= a && a <= '9')
+ byte = (a - '0') << 4;
+ else if ('A' <= a && a <= 'F')
+ byte = (a - 'A' + 10) << 4;
+ else
+ return ERROR_AND(size_t, 0, error_new(E_POSIX_EILSEQ, "ihex: invalid hexadecimal: ", (qbyte, a)));
+ if ('0' <= b && b <= '9')
+ byte |= b - '0';
+ else if ('A' <= b && b <= 'F')
+ byte |= b - 'A' + 10;
+ else
+ return ERROR_AND(size_t, 1, error_new(E_POSIX_EILSEQ, "ihex: invalid hexadecimal: ", (qbyte, b)));
+ self->buf[self->buf_len++] = byte;
+ if (self->buf_len == self->buf[0]+5) {
+ uint8_t sum = 0;
+ for (size_t i = 0; i < (size_t)self->buf[0]+5; i++)
+ sum += self->buf[i];
+ if (sum != 0) {
+ self->sticky_err = error_new(E_POSIX_EPROTO, "ihex: checksum mismatch");
+ return ERROR_AND(size_t, 2, error_dup(self->sticky_err));
+ }
+ struct ihex_record rec = {
+ .len = self->buf[0],
+ .off = uint16be_decode(&self->buf[1]),
+ .typ = self->buf[3],
+ .dat = &self->buf[4],
+ };
+ error err = ihex_handle_record(self, &rec);
+ if (!ERROR_IS_NULL(err)) {
+ self->sticky_err = err;
+ return ERROR_AND(size_t, 2, error_dup(err));
+ }
+ self->in_record = false;
+ self->buf_len = 0;
+ }
+ return ERROR_AND(size_t, 2, ERROR_NULL);
+}
+
+static size_t_and_error ihex_decoder_write(struct ihex_decoder *self, const char *dat, size_t len_in) {
+ assert(self);
+ if (!len_in)
+ return ERROR_AND(size_t, 0, ERROR_NULL);
+ assert(dat);
+
+ if (!ERROR_IS_NULL(self->sticky_err))
+ return ERROR_AND(size_t, 0, error_dup(self->sticky_err));
+
+ size_t len_consumed = 0;
+
+ if (self->buf_char) {
+ assert(self->in_record);
+ size_t_and_error r = ihex_decode_byte(self, self->buf_char, dat[0]);
+ if (r.size_t)
+ len_consumed += r.size_t - 1;
+ self->buf_char = 0;
+ if (!ERROR_IS_NULL(r.err))
+ return ERROR_AND(size_t, len_consumed, r.err);
+ }
+
+ while (len_consumed < len_in) {
+ if (!self->in_record) {
+ const char *marker = memchr(&dat[len_consumed], ':', len_in-len_consumed);
+ if (!marker) {
+ len_consumed = len_in;
+ continue;
+ }
+ len_consumed += marker - &dat[len_consumed];
+
+ assert(dat[len_consumed] == ':');
+ if (self->seen_eof)
+ return ERROR_AND(size_t, len_consumed, error_new(E_POSIX_EPROTO, "ihex: record after EOF record"));
+ len_consumed++;
+ self->in_record = true;
+ }
+ while (len_in - len_consumed >= 2 && self->in_record) {
+ size_t_and_error r = ihex_decode_byte(self, dat[len_consumed], dat[len_consumed+1]);
+ len_consumed += r.size_t;
+ if (!ERROR_IS_NULL(r.err))
+ return ERROR_AND(size_t, len_consumed, r.err);
+ }
+ if (len_in - len_consumed && self->in_record) {
+ assert(len_in - len_consumed == 1);
+ if (!(('0' <= dat[len_consumed] && dat[len_consumed] <= '9') ||
+ ('A' <= dat[len_consumed] && dat[len_consumed] <= 'F')))
+ return ERROR_AND(size_t, len_consumed, error_new(E_POSIX_EILSEQ, "ihex: invalid hexadecimal: ", (qbyte, dat[len_consumed])));
+ self->buf_char = dat[len_consumed++];
+ }
+ }
+
+ assert(len_consumed == len_in);
+ return ERROR_AND(size_t, len_in, ERROR_NULL);
+}
+
+size_t_and_error ihex_decoder_writev(struct ihex_decoder *self, const struct wr_iovec *iov, int iovcnt) {
+ assert(self);
+ assert(iov);
+ assert(iovcnt);
+
+ size_t total = 0;
+ for (int i = 0; i < iovcnt; i++) {
+ size_t_and_error r = ihex_decoder_write(self, iov[i].iov_write_from, iov[i].iov_len);
+ total += r.size_t;
+ if (!ERROR_IS_NULL(r.err))
+ return ERROR_AND(size_t, total, r.err);
+ }
+ return ERROR_AND(size_t, total, ERROR_NULL);
+}
+
+error ihex_decoder_close(struct ihex_decoder *self) {
+ error_cleanup(&self->sticky_err);
+ return ERROR_NULL;
+}
diff --git a/flashimg/cpu_main/ihex.h b/flashimg/cpu_main/ihex.h
new file mode 100644
index 0000000..dafd65e
--- /dev/null
+++ b/flashimg/cpu_main/ihex.h
@@ -0,0 +1,49 @@
+/* flashimg/cpu_main/ihex.h - Intel Hex decoder
+ *
+ * Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+/* https://archive.org/details/IntelHEXStandard */
+
+#ifndef _FLASHIMG_CPU_MAIN_IHEX_H_
+#define _FLASHIMG_CPU_MAIN_IHEX_H_
+
+#include <stdbool.h> /* for bool */
+#include <stddef.h> /* for size_t */
+#include <stdint.h> /* for uint{n}_t */
+
+#include <libhw/generic/io.h>
+#include <libmisc/private.h>
+
+struct ihex_decoder {
+ void *handle_arg;
+ error (*handle_data)(void *arg, uint32_t off, uint8_t count, uint8_t *dat);
+ error (*handle_set_exec_start_seg)(void *arg, uint16_t cs, uint16_t ip);
+ error (*handle_set_exec_start_lin)(void *arg, uint32_t eip);
+ error (*handle_eof)(void *arg);
+
+ BEGIN_PRIVATE(IHEX_H);
+
+ bool seen_eof;
+ error sticky_err;
+
+ /* ihex_decoder_write: deal with ASCII soup */
+ bool in_record;
+ char buf_char; /* the first nibble of a byte (still in ASCII) */
+
+ /* ihex_decode_byte: build records from decoded bytes */
+ /* The currently-being-decoded record, after hex decoding. */
+ uint16_t buf_len;
+ uint8_t buf[0xFF+5];
+
+ /* ihex_handle_record: handle record semantics */
+ enum { _IHEX_MODE_NONE, _IHEX_MODE_SEG, _IHEX_MODE_LIN } addr_mode;
+ uint32_t addr_base;
+
+ END_PRIVATE(IHEX_H);
+};
+LO_IMPLEMENTATION_H(io_writer, struct ihex_decoder, ihex_decoder);
+LO_IMPLEMENTATION_H(io_closer, struct ihex_decoder, ihex_decoder);
+
+#endif /* _FLASHIMG_CPU_MAIN_IHEX_H_ */
diff --git a/cmd/sbc_harness/main.c b/flashimg/cpu_main/main.c
index 2b2b3f8..9dd2588 100644
--- a/cmd/sbc_harness/main.c
+++ b/flashimg/cpu_main/main.c
@@ -1,4 +1,4 @@
-/* sbc_harness/main.c - Main entry point and event loop for sbc-harness
+/* flashimg/cpu_main/main.c - Main entry point and event loop for sbc-harness
*
* Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
@@ -8,8 +8,9 @@
#include <string.h> /* libc: for strlen() */
/* pico-sdk */
-#include <pico/stdio_uart.h> /* pico-sdk:pico_stdio_uart: for stdio_uart_init() */
#include <hardware/flash.h> /* pico-sdk:hardware_flash: for flash_get_unique_id() */
+#include <hardware/uart.h>
+#include <hardware/gpio.h>
/* our OS */
#include <libcr/coroutine.h>
@@ -19,9 +20,9 @@
#include <libhw/w5500.h>
/* our application libraries */
+#include <lib9p/srv.h>
#include <libdhcp/client.h>
#include <libusb/usb_common.h>
-#include <lib9p/srv.h>
#include <util9p/static.h>
/* our utility libraries */
@@ -30,13 +31,22 @@
#include <libmisc/log.h>
/* local headers */
-#include "usb_keyboard.h"
+#include "fs_harness_flash_bin.h"
+#include "fs_harness_uptime_txt.h"
#include "static.h"
+#include "usb_keyboard.h"
/* configuration **************************************************************/
#include "config.h"
+#ifndef _CONFIG_9P_MAX_CONNS
+ #error config.h must define _CONFIG_9P_MAX_CONNS
+#endif
+#ifndef _CONFIG_9P_MAX_REQS
+ #error config.h must define _CONFIG_9P_MAX_REQS
+#endif
+
/* file tree ******************************************************************/
enum { PATH_BASE = __COUNTER__ };
@@ -45,7 +55,14 @@ enum { PATH_BASE = __COUNTER__ };
#define STATIC_FILE(STRNAME, ...) UTIL9P_STATIC_FILE(PATH_COUNTER, STRNAME, __VA_ARGS__)
#define STATIC_DIR(STRNAME, ...) UTIL9P_STATIC_DIR(PATH_COUNTER, STRNAME, __VA_ARGS__)
-struct lib9p_srv_file root =
+#define API_FILE(STRNAME, SYMNAME, ...) \
+ lo_box_##SYMNAME##_file_as_lib9p_srv_file(&((struct SYMNAME##_file){ \
+ .name = STRNAME, \
+ .pathnum = PATH_COUNTER \
+ __VA_OPT__(,) __VA_ARGS__ \
+ }))
+
+static struct lib9p_srv_file root =
STATIC_DIR("",
STATIC_DIR("Documentation",
STATIC_FILE("YOUR_RIGHTS_AND_OBLIGATIONS.md",
@@ -55,18 +72,18 @@ struct lib9p_srv_file root =
STATIC_FILE("agpl-3.0.txt",
.data_start = _binary_static_Documentation_YOUR_RIGHTS_AND_OBLIGATIONS_agpl_3_0_txt_start,
.data_end = _binary_static_Documentation_YOUR_RIGHTS_AND_OBLIGATIONS_agpl_3_0_txt_end),
+ STATIC_FILE("dhcp.bsd3-mit.txt",
+ .data_start = _binary_static_Documentation_YOUR_RIGHTS_AND_OBLIGATIONS_dhcp_bsd3_mit_txt_start,
+ .data_end = _binary_static_Documentation_YOUR_RIGHTS_AND_OBLIGATIONS_dhcp_bsd3_mit_txt_end),
+ STATIC_FILE("newlib.txt",
+ .data_start = _binary_static_Documentation_YOUR_RIGHTS_AND_OBLIGATIONS_newlib_txt_start,
+ .data_end = _binary_static_Documentation_YOUR_RIGHTS_AND_OBLIGATIONS_newlib_txt_end),
STATIC_FILE("pico-sdk.bsd3.txt",
.data_start = _binary_static_Documentation_YOUR_RIGHTS_AND_OBLIGATIONS_pico_sdk_bsd3_txt_start,
.data_end = _binary_static_Documentation_YOUR_RIGHTS_AND_OBLIGATIONS_pico_sdk_bsd3_txt_end),
- STATIC_FILE("printf.mit.txt",
- .data_start = _binary_static_Documentation_YOUR_RIGHTS_AND_OBLIGATIONS_printf_mit_txt_start,
- .data_end = _binary_static_Documentation_YOUR_RIGHTS_AND_OBLIGATIONS_printf_mit_txt_end),
STATIC_FILE("tinyusb.mit.txt",
.data_start = _binary_static_Documentation_YOUR_RIGHTS_AND_OBLIGATIONS_tinyusb_mit_txt_start,
.data_end = _binary_static_Documentation_YOUR_RIGHTS_AND_OBLIGATIONS_tinyusb_mit_txt_end),
- STATIC_FILE("newlib.txt",
- .data_start = _binary_static_Documentation_YOUR_RIGHTS_AND_OBLIGATIONS_newlib_txt_start,
- .data_end = _binary_static_Documentation_YOUR_RIGHTS_AND_OBLIGATIONS_newlib_txt_end),
),
STATIC_FILE("harness_rom_bin.txt",
.data_start = _binary_static_Documentation_harness_rom_bin_txt_start,
@@ -74,15 +91,18 @@ struct lib9p_srv_file root =
STATIC_FILE("harness_flash_bin.txt",
.data_start = _binary_static_Documentation_harness_flash_bin_txt_start,
.data_end = _binary_static_Documentation_harness_flash_bin_txt_end),
+ STATIC_FILE("harness_uptime_txt.txt",
+ .data_start = _binary_static_Documentation_harness_uptime_txt_txt_start,
+ .data_end = _binary_static_Documentation_harness_uptime_txt_txt_end),
),
STATIC_DIR("harness",
STATIC_FILE("rom.bin",
.data_start = (void*)0x00000000,
.data_size = 16*1024),
- // TODO: Make flash.bin writable.
- STATIC_FILE("flash.bin",
- .data_start = (void*)0x10000000,
- .data_size = PICO_FLASH_SIZE_BYTES),
+ API_FILE("flash.bin",
+ flash),
+ API_FILE("uptime.txt",
+ uptime),
// TODO: system.log
// TODO: proc.txt
// TODO: cpuinfo.txt
@@ -95,8 +115,8 @@ struct lib9p_srv_file root =
),
);
-static lo_interface lib9p_srv_file get_root(struct lib9p_srv_ctx *LM_UNUSED(ctx), struct lib9p_s LM_UNUSED(treename)) {
- return root;
+static lib9p_srv_file_or_error get_root(struct lib9p_srv_ctx *LM_UNUSED(ctx), struct lib9p_s LM_UNUSED(treename)) {
+ return ERROR_NEW_VAL(lib9p_srv_file, root);
}
/* Code ***********************************************************************/
@@ -110,7 +130,7 @@ static COROUTINE hello_world_cr(void *_chan) {
for (size_t i = 0;; i = (i+1) % strlen(msg)) {
int result = usb_keyboard_rpc_send_req(chan, (uint32_t)msg[i]);
if (result < 1) {
- errorf("error sending rune U+%d", (uint32_t)msg[i]);
+ log_errorln("error sending rune U+", msg[i]);
break;
}
}
@@ -130,7 +150,7 @@ struct {
static COROUTINE dhcp_cr(void *) {
cr_begin();
- dhcp_client_main(lo_box_w5500_if_as_net_iface(&globals.dev_w5500), "harness");
+ dhcp_client_main(LO_BOX(net_iface, &globals.dev_w5500), "harness");
cr_end();
}
@@ -138,16 +158,24 @@ static COROUTINE dhcp_cr(void *) {
static COROUTINE read9p_cr(void *) {
cr_begin();
- lo_interface net_iface iface = lo_box_w5500_if_as_net_iface(&globals.dev_w5500);
+ lo_interface net_iface iface = LO_BOX(net_iface, &globals.dev_w5500);
lo_interface net_stream_listener listener = LO_CALL(iface, tcp_listen, LIB9P_DEFAULT_PORT_9FS);
- lib9p_srv_read_cr(&globals.srv, listener);
+ lib9p_srv_accept_and_read_loop(&globals.srv, listener);
cr_end();
}
-const char *const hexdig = "0123456789ABCDEF";
-static_assert(CONFIG_9P_SRV_MAX_REQS*_CONFIG_9P_NUM_SOCKS <= 16);
+static COROUTINE write9p_cr(void *) {
+ cr_begin();
+
+ lib9p_srv_worker_loop(&globals.srv);
+
+ cr_end();
+}
+
+static const char *const hexdig = "0123456789ABCDEF";
+static_assert(_CONFIG_9P_MAX_REQS <= 16);
COROUTINE init_cr(void *) {
cr_begin();
@@ -177,7 +205,7 @@ COROUTINE init_cr(void *) {
17, /* PIN_CS */
0, 1, 2, 3); /* DMA channels */
w5500_init(&globals.dev_w5500, "W5500",
- lo_box_rp2040_hwspi_as_spi(&globals.dev_spi),
+ LO_BOX(spi, &globals.dev_spi),
21, /* PIN_INTR */
20, /* PIN_RESET */
((struct net_eth_addr){{
@@ -194,32 +222,71 @@ COROUTINE init_cr(void *) {
usb_keyboard_init();
usb_common_lateinit();
- globals.keyboard_chan = (usb_keyboard_rpc_t){0};
+ globals.keyboard_chan = (usb_keyboard_rpc_t){};
globals.srv.rootdir = get_root;
/* set up coroutines **************************************************/
- coroutine_add("usb_common", usb_common_cr, NULL);
- coroutine_add("usb_keyboard", usb_keyboard_cr, &globals.keyboard_chan);
- //coroutine_add("hello_world", hello_world_cr, &globals.keyboard_chan);
- coroutine_add("dhcp", dhcp_cr, NULL);
- for (int i = 0; i < _CONFIG_9P_NUM_SOCKS; i++) {
+ cid_t cid;
+ cid = coroutine_add("usb_common", usb_common_cr, NULL);
+ assert(cid);
+ cid = coroutine_add("usb_keyboard", usb_keyboard_cr, &globals.keyboard_chan);
+ assert(cid);
+ //cid = coroutine_add("hello_world", hello_world_cr, &globals.keyboard_chan);
+ assert(cid);
+ cid = coroutine_add("dhcp", dhcp_cr, NULL);
+ assert(cid);
+ for (int i = 0; i < _CONFIG_9P_MAX_CONNS; i++) {
char name[] = {'r', 'e', 'a', 'd', '-', hexdig[i], '\0'};
- coroutine_add(name, read9p_cr, NULL);
+ cid = coroutine_add_with_stack(COROUTINE_STACK_read9p_cr[i], COROUTINE_STACK_read9p_cr_len,
+ name, read9p_cr, NULL);
+ assert(cid);
}
- for (int i = 0; i < CONFIG_9P_SRV_MAX_REQS*_CONFIG_9P_NUM_SOCKS; i++) {
+ for (int i = 0; i < _CONFIG_9P_MAX_REQS; i++) {
char name[] = {'w', 'r', 'i', 't', 'e', '-', hexdig[i], '\0'};
- coroutine_add(name, lib9p_srv_write_cr, &globals.srv);
+ cid = coroutine_add_with_stack(COROUTINE_STACK_write9p_cr[i], COROUTINE_STACK_write9p_cr_len,
+ name, write9p_cr, NULL);
+ assert(cid);
}
cr_exit();
}
+void __lm_putchar(unsigned char c) {
+ if (c == '\n') {
+ while (!uart_is_writable(uart0))
+ tight_loop_contents();
+ uart_get_hw(uart0)->dr = '\r';
+ }
+ while (!uart_is_writable(uart0))
+ tight_loop_contents();
+ uart_get_hw(uart0)->dr = c;
+}
+
+void __lm_abort(void) {
+ exit(1);
+}
+
+[[noreturn]] void assert_panic(const char *fmt, ...) {
+ /* Most of the panic()s in pico-sdk are simple strings, very
+ * few use printf-style formatters. And those that do are
+ * still readable if we ignore the formatting. */
+ assert_notreached(fmt);
+}
+
int main() {
bootclock = rp2040_hwtimer(0);
- stdio_uart_init();
+
+ uart_init(uart0, 115200);
+ gpio_set_function(0, GPIO_FUNC_UART); /* tx */
+ gpio_set_function(1, GPIO_FUNC_UART); /* rx */
+ bi_decl(bi_2pins_with_names(0, "UART TX",
+ 1, "UART RX"));
+
/* char *hdr = "=" * (80-strlen("info : MAIN: ")); */
- infof("===================================================================");
- coroutine_add("init", init_cr, NULL);
+ log_infoln("===================================================================");
+ cid_t cid = coroutine_add("init", init_cr, NULL);
+ assert(cid);
coroutine_main();
+ assert_notreached("all coroutines exited");
}
diff --git a/cmd/sbc_harness/static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS.md b/flashimg/cpu_main/static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS.md
index 1e89d72..b3fc12e 100644
--- a/cmd/sbc_harness/static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS.md
+++ b/flashimg/cpu_main/static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS.md
@@ -8,22 +8,22 @@
The firmware running on the SBC-Harness is Free Software -- if you
have access to this file, then you have the freedom to use, study,
-share, and improve; as long as you follow a few conditions that
-basically amount to "paying it forward".
+share, and improve the firmware; as long as you follow a few
+conditions that basically amount to "paying it forward".
The precise terms of your rights and obligations are given in the
files in the `YOUR_RIGHTS_AND_OBLIGATIONS/` directory. You must obey
-each of the files in that directory when distributing copies or
-modified copies of the firmware:
+each of the files in that directory when distributing
+verbatim-or-modified copies of the firmware:
- `agpl-3.0.txt`: The firmware is as-a-whole licensed to you under
the terms of the GNU Affero General Public License (GNU AGPL) as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version. This is the main
- document that protects your rights and defines obligations.
+ document that protects your rights and defines your obligations.
- other files: The firmware makes use of other code that is under
various other licenses. When taken with the AGPL, they amount to
requiring that you pass along the copyright and license text in
- those files when you distribute copies or modified copies of the
+ those files when you distribute verbatim-or-modified copies of the
firmware.
diff --git a/cmd/sbc_harness/static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS/agpl-3.0.txt b/flashimg/cpu_main/static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS/agpl-3.0.txt
index 8a9b6f3..8a9b6f3 120000
--- a/cmd/sbc_harness/static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS/agpl-3.0.txt
+++ b/flashimg/cpu_main/static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS/agpl-3.0.txt
diff --git a/flashimg/cpu_main/static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS/dhcp.bsd3-mit.txt b/flashimg/cpu_main/static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS/dhcp.bsd3-mit.txt
new file mode 120000
index 0000000..0277bc8
--- /dev/null
+++ b/flashimg/cpu_main/static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS/dhcp.bsd3-mit.txt
@@ -0,0 +1 @@
+../../../../../3rd-party/COPYING.wiznet-dhcp.txt \ No newline at end of file
diff --git a/cmd/sbc_harness/static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS/newlib.txt b/flashimg/cpu_main/static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS/newlib.txt
index 5c5939c..5c5939c 120000
--- a/cmd/sbc_harness/static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS/newlib.txt
+++ b/flashimg/cpu_main/static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS/newlib.txt
diff --git a/cmd/sbc_harness/static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS/pico-sdk.bsd3.txt b/flashimg/cpu_main/static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS/pico-sdk.bsd3.txt
index 52c4374..52c4374 120000
--- a/cmd/sbc_harness/static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS/pico-sdk.bsd3.txt
+++ b/flashimg/cpu_main/static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS/pico-sdk.bsd3.txt
diff --git a/cmd/sbc_harness/static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS/tinyusb.mit.txt b/flashimg/cpu_main/static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS/tinyusb.mit.txt
index 22a67cf..22a67cf 120000
--- a/cmd/sbc_harness/static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS/tinyusb.mit.txt
+++ b/flashimg/cpu_main/static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS/tinyusb.mit.txt
diff --git a/flashimg/cpu_main/static/Documentation/harness_flash.bin.txt b/flashimg/cpu_main/static/Documentation/harness_flash.bin.txt
new file mode 100644
index 0000000..1b58d6d
--- /dev/null
+++ b/flashimg/cpu_main/static/Documentation/harness_flash.bin.txt
@@ -0,0 +1,43 @@
+NAME
+ /harness/flash.bin
+
+DESCRIPTION
+ Access the flash storage chip (where the harness firmware is
+ stored).
+
+ Reading from the file reads the raw flash contents.
+
+ Writing to the file does not accept raw data; instead the data
+ must be encapsulated in the [Intel Hex] format, with the Hex
+ file writing to the region 0x1000_0000-0x1010_0000. While less
+ convenient than verbatim data, the Hex format provides in-band
+ checksums and EOF-markers that help prevent rendering the
+ harness unbootable with corrupted or truncated writes. Any
+ holes in the Intel Hex file are filled with "1" bits. Once a
+ complete Intel Hex file has been written without error and the
+ file is closed, the harness reboots into the new firmware.
+
+BUGS
+ - The size of the chip is configured at compile-time. If the
+ firmware is loaded onto hardware with a larger flash chip
+ than it was compiled for, then the upper part of the chip
+ will not be accessible with this file. If the firmware is
+ loaded onto hardware with a smaller flash chip than it was
+ compiled for, then accessing the missing upper part of the
+ chip will crash.
+
+ - When writing to the flash using this file, only half of the
+ chip capacity is usable (the size of the region specified
+ above is half the chip size); the top half and bottom half
+ are mirrors of each-other. This is to avoid the firmware
+ crashing as its program text is overwritten; the firmware is
+ executing out of the bottom half, and writing to the top
+ half; once the file is closed, a minimal in-RAM function
+ copies the top half to the bottom half and reboots.
+
+SEE ALSO:
+ [Intel Hex]: https://archive.org/details/IntelHEXStandard
+
+AUTHOR
+ Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/cmd/sbc_harness/static/Documentation/harness_rom_bin.txt b/flashimg/cpu_main/static/Documentation/harness_rom.bin.txt
index 63fd0a3..63fd0a3 100644
--- a/cmd/sbc_harness/static/Documentation/harness_rom_bin.txt
+++ b/flashimg/cpu_main/static/Documentation/harness_rom.bin.txt
diff --git a/flashimg/cpu_main/static/Documentation/harness_uptime.txt.txt b/flashimg/cpu_main/static/Documentation/harness_uptime.txt.txt
new file mode 100644
index 0000000..09e9243
--- /dev/null
+++ b/flashimg/cpu_main/static/Documentation/harness_uptime.txt.txt
@@ -0,0 +1,27 @@
+NAME
+ /harness/uptime.txt
+
+DESCRIPTION
+ Reading this file gives a text string of the format
+
+ {ns}ns
+ [[[[{y}y]{d}d ]{h}h]{m}m]{s.09}s
+
+ That is: the first line is simply the harness's uptime in an
+ integer number of nanoseconds; and the second line is this
+ same number in a more human-readable form; divided into
+ seconds, minutes, hours, days, and years.
+
+BUGS
+ - Using nanoseconds gives the illusion of more precision than
+ there actually is; the harness' clock only has microsecond
+ resolution; the last 3 digits of the returned nanosecond
+ count will always be 0.
+
+ - In the human-readable form, the days are always exactly
+ 60*60*24 seconds (leap seconds are ignored), and the years
+ are always exactly 365 days (leap years are ignored).
+
+AUTHOR
+ Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/flashimg/cpu_main/tests/test_ihex.c b/flashimg/cpu_main/tests/test_ihex.c
new file mode 100644
index 0000000..f173388
--- /dev/null
+++ b/flashimg/cpu_main/tests/test_ihex.c
@@ -0,0 +1,128 @@
+/* flashimg/cpu_main/tests/test_ihex.c - Tests for ihex.c
+ *
+ * Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#include <stdio.h> /* for putchar() */
+#include <string.h>
+
+#include <libmisc/fmt.h>
+
+#include "ihex.h"
+
+struct stdout { size_t len; };
+LO_IMPLEMENTATION_STATIC(fmt_dest, struct stdout, stdout);
+static void stdout_putb(struct stdout *self, uint8_t b) {
+ putchar(b);
+ self->len++;
+}
+static size_t stdout_tell(struct stdout *self) {
+ return self->len;
+}
+
+static lo_interface fmt_dest fmt_stdout = lo_box_stdout_as_fmt_dest(&((struct stdout){}));
+
+#define test_assert(expr) do { \
+ if (!(expr)) \
+ fmt_print( \
+ fmt_stdout, \
+ "test failure: ", __FILE__, ":", __LINE__, ":", __func__, \
+ ": " #expr "\n"); \
+ } while (0)
+
+static char *input =
+ /* ,-byte count
+ * | ,-address
+ * | | ,-record type
+ * | | | ,- checksum
+ *[][--][][..............................][]\r\n */
+ ":020000041000EA\r\n" /* base_addr = linear(0x1000) = 0x1000<<16 */
+ ":1000000000B5324B212058609868022188439860DF\r\n" /* memcpy(chip[base_addr+0x0000], "\x00\xB5\x32\x4B\x21\x20\x58\x60\x98\x68\x02\x21\x88\x43\x98\x60", 16) */
+ ":10001000D860186158612E4B002199600221596106\r\n" /* memcpy(chip[base_addr+0x0010], "\xD8\x60\x18\x61\x58\x61\x2E\x4B\x00\x21\x99\x60\x02\x21\x59\x61", 16) */
+ ":10BE9C000000000000000000000000000000000096\r\n" /* memcpy(chip[base_addr+0xBE9C], "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 16) */
+ ":04BEAC00CC4A00205C\r\n" /* memcpy(chip[base_addr+0xBEAC], "\x00\xCC\x4A\x00\x20", 5) */
+ ":04000005100001E9FD\r\n" /* start_exec_at = linear(0x100001E9) */
+ ":00000001FF\r\n"; /* EOF */
+
+static int cnt = 0;
+
+static error handle_data(void *, uint32_t off, uint8_t count, uint8_t *dat) {
+ switch (cnt) {
+ case 0:
+ test_assert(off == UINT32_C(0x10000000));
+ test_assert(count == 16);
+ test_assert(memcmp(dat, "\x00\xB5\x32\x4B\x21\x20\x58\x60\x98\x68\x02\x21\x88\x43\x98\x60", 16) == 0);
+ break;
+ case 1:
+ test_assert(off == UINT32_C(0x10000010));
+ test_assert(count == 16);
+ test_assert(memcmp(dat, "\xD8\x60\x18\x61\x58\x61\x2E\x4B\x00\x21\x99\x60\x02\x21\x59\x61", 16) == 0);
+ break;
+ case 2:
+ test_assert(off == UINT32_C(0x1000BE9C));
+ test_assert(count == 16);
+ test_assert(memcmp(dat, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 16) == 0);
+ break;
+ case 4:
+ test_assert(off == UINT32_C(0x1000BE9C));
+ test_assert(count == 5);
+ test_assert(memcmp(dat, "\x00\xCC\x4A\x00\x20", 5) == 0);
+ break;
+ default:
+ test_assert(false);
+ }
+ cnt++;
+ return ERROR_NULL;
+}
+static error handle_set_exec_start_seg(void *, uint16_t LM_UNUSED(cs), uint16_t LM_UNUSED(ip)) {
+ switch (cnt) {
+ default:
+ test_assert(false);
+ }
+ cnt++;
+ return ERROR_NULL;
+}
+static error handle_set_exec_start_lin(void *, uint32_t eip) {
+ switch (cnt) {
+ case 5:
+ test_assert(eip == UINT32_C(0x100001E9));
+ break;
+ default:
+ test_assert(false);
+ }
+ cnt++;
+ return ERROR_NULL;
+}
+static error handle_eof(void *) {
+ switch (cnt) {
+ case 6:
+ break;
+ default:
+ test_assert(false);
+ }
+ cnt++;
+ return ERROR_NULL;
+}
+
+int main() {
+ struct ihex_decoder dec = {
+ .handle_data = handle_data,
+ .handle_set_exec_start_seg = handle_set_exec_start_seg,
+ .handle_set_exec_start_lin = handle_set_exec_start_lin,
+ .handle_eof = handle_eof,
+ };
+
+ size_t_and_error ret = ihex_decoder_writev(&dec, &((struct wr_iovec){
+ .iov_write_from = input,
+ .iov_len = strlen(input),
+ }), 1);
+ fmt_print(fmt_stdout,
+ "ret = (", ret.size_t, ", ", (error, ret.err), ")\n",
+ "cnt = ", cnt, "\n");
+ test_assert(ret.size_t == strlen(input));
+ test_assert(ERROR_IS_NULL(ret.err));
+ test_assert(cnt == 6);
+
+ return 0;
+}
diff --git a/flashimg/cpu_main/tusb_log.c b/flashimg/cpu_main/tusb_log.c
new file mode 100644
index 0000000..aacec87
--- /dev/null
+++ b/flashimg/cpu_main/tusb_log.c
@@ -0,0 +1,22 @@
+/* flashimg/cpu_main/tusb_log.c - Logger for tusb_config.h
+ *
+ * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#define LOG_NAME TINY_USB
+#include <libmisc/log.h>
+
+#include "tusb_config.h"
+
+void _libmisc_tu_mess_failed(const char *expr,
+ const char *file, unsigned int line, const char *func) {
+ log_errorln(file, ":", line, ":", func, "(): assertion ", (qstr, expr), " failed");
+}
+
+void _libmisc_tu_print_str(const char *x) { fmt_print_str(_log_dest, x); }
+void _libmisc_tu_print_byte(uint8_t x) { fmt_print_byte(_log_dest, x); }
+void _libmisc_tu_print_base10(unsigned long x) { fmt_print_base10(_log_dest, x); }
+void _libmisc_tu_print_base16(unsigned long x) { fmt_print(_log_dest, "0x", (base16, x)); }
+void _libmisc_tu_print_base16_u8(uint8_t x) { fmt_print_base16_u8_(_log_dest, x); }
+void _libmisc_tu_print_base16_u16(uint16_t x) { fmt_print_base16_u16_(_log_dest, x); }
diff --git a/cmd/sbc_harness/usb_keyboard.c b/flashimg/cpu_main/usb_keyboard.c
index f3cb42d..dd9e26b 100644
--- a/cmd/sbc_harness/usb_keyboard.c
+++ b/flashimg/cpu_main/usb_keyboard.c
@@ -1,4 +1,4 @@
-/* sbc_harness/usb_keyboard.c - Implementation of a USB keyboard device
+/* flashimg/cpu_main/usb_keyboard.c - Implementation of a USB keyboard device
*
* Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
@@ -6,9 +6,9 @@
#include <tusb.h>
+#include <libmisc/macro.h>
#include <libusb/tusb_helpers.h> /* for TUD_ENDPOINT_IN */
#include <libusb/usb_common.h>
-#include <libmisc/macro.h>
#include "usb_keyboard.h"
@@ -20,7 +20,7 @@ static uint8_t const hid_report_descriptor_keyboard[] = { TUD_HID_REPORT_DESC_KE
static uint8_t kbd_ifc = 0;
-void usb_keyboard_init() {
+void usb_keyboard_init(void) {
if (kbd_ifc)
return;
@@ -49,13 +49,13 @@ COROUTINE usb_keyboard_cr(void *_chan) {
uint8_t report_id = 0;
uint8_t modifier = 0;
- uint8_t keycodes[6] = {0};
+ uint8_t keycodes[6] = {};
for (;;) {
while (!tud_hid_n_ready(kbd_ifc))
cr_yield();
- if (usb_keyboard_rpc_can_recv_req(chan)) {
- usb_keyboard_rpc_req_t req = usb_keyboard_rpc_recv_req(chan);
+ if (cr_rpc_can_recv_req(chan)) {
+ usb_keyboard_rpc_req_t req = cr_rpc_recv_req(chan);
uint32_t rune = req.req;
modifier = ascii2keycode[rune][0] ? KEYBOARD_MODIFIER_LEFTSHIFT : 0;
@@ -69,7 +69,7 @@ COROUTINE usb_keyboard_cr(void *_chan) {
keycodes[0] = 0;
tud_hid_n_keyboard_report(kbd_ifc, report_id, modifier, keycodes);
- usb_keyboard_rpc_send_resp(req, 1);
+ cr_rpc_send_resp(req, 1);
} else {
modifier = 0;
keycodes[0] = 0;
@@ -85,23 +85,20 @@ COROUTINE usb_keyboard_cr(void *_chan) {
* §6.2.2 "Report Descriptor") for the given index.
*/
uint8_t const *tud_hid_descriptor_report_cb(uint8_t index) {
- static uint8_t const *reports[] = {
- hid_report_descriptor_keyboard,
- };
- if (index >= TU_ARRAY_SIZE(reports))
- return NULL;
- return reports[index];
+ static uint8_t const *reports[] = {
+ hid_report_descriptor_keyboard,
+ };
+ if (index >= TU_ARRAY_SIZE(reports))
+ return NULL;
+ return reports[index];
}
-uint16_t tud_hid_get_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t* buffer, uint16_t reqlen)
-{
+uint16_t tud_hid_get_report_cb(uint8_t LM_UNUSED(instance),
+ uint8_t LM_UNUSED(report_id),
+ hid_report_type_t LM_UNUSED(report_type),
+ uint8_t* LM_UNUSED(buffer),
+ uint16_t LM_UNUSED(reqlen)) {
// TODO not implemented
- (void) instance;
- (void) report_id;
- (void) report_type;
- (void) buffer;
- (void) reqlen;
-
return 0;
}
diff --git a/flashimg/cpu_main/usb_keyboard.h b/flashimg/cpu_main/usb_keyboard.h
new file mode 100644
index 0000000..52b9b67
--- /dev/null
+++ b/flashimg/cpu_main/usb_keyboard.h
@@ -0,0 +1,20 @@
+/* flashimg/cpu_main/usb_keyboard.h - Implementation of a USB keyboard device
+ *
+ * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#ifndef _FLASHIMG_CPU_MAIN_USB_KEYBOARD_H_
+#define _FLASHIMG_CPU_MAIN_USB_KEYBOARD_H_
+
+#include <stdint.h> /* for uint32_t */
+
+#include <libcr/coroutine.h> /* for COROUTINE */
+#include <libcr_ipc/rpc.h> /* for CR_RPC_DECLARE */
+
+CR_RPC_DECLARE(usb_keyboard_rpc, uint32_t, int);
+
+void usb_keyboard_init(void);
+COROUTINE usb_keyboard_cr(void *arg);
+
+#endif /* _FLASHIMG_CPU_MAIN_USB_KEYBOARD_H_ */
diff --git a/gdb-helpers/libcr.py b/gdb-helpers/libcr.py
index fcfd86e..6f95a81 100644
--- a/gdb-helpers/libcr.py
+++ b/gdb-helpers/libcr.py
@@ -89,6 +89,7 @@ def gdb_longjmp(buf: gdb_JmpBuf) -> None:
class CrGlobals:
+ main: "CrMain"
coroutines: list["CrCoroutine"]
_breakpoint: "CrBreakpoint"
_known_threads: set[gdb.InferiorThread]
@@ -98,6 +99,7 @@ class CrGlobals:
gdb.parse_and_eval("sizeof(coroutine_table)/sizeof(coroutine_table[0])")
)
+ self.main = CrMain(self)
self.coroutines = [CrCoroutine(self, i + 1) for i in range(num)]
self._breakpoint = CrBreakpoint()
@@ -126,35 +128,37 @@ class CrGlobals:
# Ignore thread creation events.
self._known_threads = cur_threads
return
- if self.coroutine_running:
- if not self.coroutine_running.is_selected():
- if gdb_bug_32428:
- print("Must return to running coroutine before continuing.")
- print("Hit ^C twice then run:")
- print(f" cr select {self.coroutine_running.id}")
- while True:
- time.sleep(1)
- assert self.coroutine_running.cont_env
- gdb_longjmp(self.coroutine_running.cont_env)
+ if not self.coroutine_running.is_selected():
+ if gdb_bug_32428:
+ print("Must return to running coroutine before continuing.")
+ print("Hit ^C twice then run:")
+ print(f" cr select {self.coroutine_running.cid}")
+ while True:
+ time.sleep(1)
+ assert self.coroutine_running.cont_env
+ gdb_longjmp(self.coroutine_running.cont_env)
+ self.main.cont_env = None
for cr in self.coroutines:
cr.cont_env = None
def is_valid_cid(self, cid: int) -> bool:
- return 0 < cid <= len(self.coroutines)
+ return (0 < cid <= len(self.coroutines)) and (
+ self.coroutines[cid - 1].state != self.CR_NONE
+ )
@property
- def coroutine_running(self) -> "CrCoroutine | None":
+ def coroutine_running(self) -> "CrMain | CrCoroutine":
cid = int(gdb.parse_and_eval("coroutine_running"))
if not self.is_valid_cid(cid):
- return None
+ return self.main
return self.coroutines[cid - 1]
@property
- def coroutine_selected(self) -> "CrCoroutine | None":
+ def coroutine_selected(self) -> "CrMain | CrCoroutine":
for cr in self.coroutines:
if cr.is_selected():
return cr
- return None
+ return self.main
@property
def CR_NONE(self) -> gdb.Value:
@@ -164,6 +168,35 @@ class CrGlobals:
def CR_RUNNING(self) -> gdb.Value:
return gdb.parse_and_eval("CR_RUNNING")
+ def select(self, cr: "CrMain | CrCoroutine", level: int = -1) -> None:
+ self.coroutine_selected.cont_env = gdb_setjmp()
+
+ if cr.cont_env:
+ gdb_longjmp(cr.cont_env)
+ else:
+ env: gdb_JmpBuf
+ if cr == self.coroutine_running:
+ assert False # cr.cont_env should have been set
+ match cr:
+ case CrMain():
+ env = self.readjmp("&coroutine_main_env")
+ case CrCoroutine():
+ if cr.state == self.CR_RUNNING:
+ env = self.readjmp("&coroutine_add_env")
+ else:
+ env = self.readjmp(f"&coroutine_table[{cr.cid-1}].env")
+ gdb_longjmp(env)
+ cr_select_top_frame()
+
+ @contextlib.contextmanager
+ def with_selected(self, cr: "CrMain | CrCoroutine") -> typing.Iterator[None]:
+ saved_env = gdb_setjmp()
+ self.select(cr)
+ try:
+ yield
+ finally:
+ gdb_longjmp(saved_env)
+
class CrBreakpointUnwinder(gdb.unwinder.Unwinder):
"""Used to temporarily disable unwinding so that
@@ -245,6 +278,22 @@ def cr_select_top_frame() -> None:
break
+class CrMain:
+ cr_globals: CrGlobals
+ cont_env: gdb_JmpBuf | None
+
+ def __init__(self, cr_globals: CrGlobals) -> None:
+ self.cr_globals = cr_globals
+ self.cont_env = None
+
+ @property
+ def cid(self) -> int:
+ return 0
+
+ def is_selected(self) -> bool:
+ return not any(cr.is_selected() for cr in self.cr_globals.coroutines)
+
+
class CrCoroutine:
cr_globals: CrGlobals
cid: int
@@ -256,10 +305,6 @@ class CrCoroutine:
self.cont_env = None
@property
- def id(self) -> int:
- return self.cid
-
- @property
def state(self) -> gdb.Value:
return gdb.parse_and_eval(f"coroutine_table[{self.cid-1}].state")
@@ -272,36 +317,10 @@ class CrCoroutine:
def is_selected(self) -> bool:
sp = int(gdb.parse_and_eval("$sp"))
- lo = int(gdb.parse_and_eval(f"coroutine_table[{self.id-1}].stack"))
- hi = lo + int(gdb.parse_and_eval(f"coroutine_table[{self.id-1}].stack_size"))
+ lo = int(gdb.parse_and_eval(f"coroutine_table[{self.cid-1}].stack"))
+ hi = lo + int(gdb.parse_and_eval(f"coroutine_table[{self.cid-1}].stack_size"))
return lo <= sp < hi
- def select(self, level: int = -1) -> None:
- if self.cr_globals.coroutine_selected:
- self.cr_globals.coroutine_selected.cont_env = gdb_setjmp()
-
- if self.cont_env:
- gdb_longjmp(self.cont_env)
- else:
- env: gdb_JmpBuf
- if self == self.cr_globals.coroutine_running:
- assert False # self.cont_env should have been set
- elif self.state == self.cr_globals.CR_RUNNING:
- env = self.cr_globals.readjmp("&coroutine_add_env")
- else:
- env = self.cr_globals.readjmp(f"&coroutine_table[{self.id-1}].env")
- gdb_longjmp(env)
- cr_select_top_frame()
-
- @contextlib.contextmanager
- def with_selected(self) -> typing.Iterator[None]:
- saved_env = gdb_setjmp()
- self.select()
- try:
- yield
- finally:
- gdb_longjmp(saved_env)
-
# User-facing commands #########################################################
@@ -342,8 +361,11 @@ class CrListCommand(gdb.Command):
rows: list[tuple[str, str, str, str, str]] = [
("", "Id", "Name", "State", "Frame")
]
- for cr in self.cr_globals.coroutines:
- if cr.state == self.cr_globals.CR_NONE:
+ for cid in range(len(self.cr_globals.coroutines) + 1):
+ cr: CrMain | CrCoroutine = (
+ self.cr_globals.coroutines[cid - 1] if cid else self.cr_globals.main
+ )
+ if isinstance(cr, CrCoroutine) and cr.state == self.cr_globals.CR_NONE:
continue
rows += [
(
@@ -353,9 +375,9 @@ class CrListCommand(gdb.Command):
"G" if cr.is_selected() else " ",
]
),
- str(cr.id),
- repr(cr.name),
- str(cr.state),
+ str(cr.cid),
+ repr(cr.name) if isinstance(cr, CrCoroutine) else "-",
+ str(cr.state) if isinstance(cr, CrCoroutine) else "-",
self._pretty_frame(cr, from_tty),
)
]
@@ -382,9 +404,9 @@ class CrListCommand(gdb.Command):
l = l[:maxline]
print(l)
- def _pretty_frame(self, cr: CrCoroutine, from_tty: bool) -> str:
+ def _pretty_frame(self, cr: CrMain | CrCoroutine, from_tty: bool) -> str:
try:
- with cr.with_selected():
+ with self.cr_globals.with_selected(cr):
saved_level = gdb.selected_frame().level()
cr_select_top_frame()
full = gdb.execute("frame", from_tty=from_tty, to_string=True)
@@ -411,16 +433,15 @@ class CrSelectCommand(gdb.Command):
if len(argv) != 1:
raise gdb.GdbError("Usage: cr select COROUTINE")
cr = self._find(argv[0])
- cr.select()
+ self.cr_globals.select(cr)
gdb.execute("frame")
- def _find(self, name: str) -> CrCoroutine:
+ def _find(self, name: str) -> CrMain | CrCoroutine:
if name.isnumeric():
cid = int(name)
- if (
- self.cr_globals.is_valid_cid(cid)
- and self.cr_globals.coroutines[cid - 1].state != self.cr_globals.CR_NONE
- ):
+ if cid == 0:
+ return self.cr_globals.main
+ if self.cr_globals.is_valid_cid(cid):
return self.cr_globals.coroutines[cid - 1]
crs: list[CrCoroutine] = []
for cr in self.cr_globals.coroutines:
@@ -445,6 +466,7 @@ def cr_initialize() -> None:
if _cr_globals:
old = _cr_globals
new = CrGlobals()
+ new.main.cont_env = old.main.cont_env
for i in range(min(len(old.coroutines), len(new.coroutines))):
new.coroutines[i].cont_env = old.coroutines[i].cont_env
old.delete()
diff --git a/gdb-helpers/rp2040.py b/gdb-helpers/rp2040.py
index 087974d..f019299 100644
--- a/gdb-helpers/rp2040.py
+++ b/gdb-helpers/rp2040.py
@@ -152,7 +152,7 @@ ICSR : {fmt32(icsr) } Control and State
╓endiannes (0=little, 1=big)
vect_key┐ ║ ╓sys_reset_req
┌───────┴──────┐║ ║╓vect_clr_active
-AIRCR : {fmt32(read_mmreg(base+0xed0c)) } Application Interrupt and Reset Control
+AIRCR : {fmt32(read_mmreg(base+0xed0c)) } Application {{Interrupt+Reset}} Control
╓sleep_deep
s_ev_on_pend╖ ║╓sleep_on_exit
@@ -172,7 +172,7 @@ PRIMASK : {fmt32(read_reg('primask')) } Priority Mask
app exec intr
┌┴─┐ ┌───────┴──────┐┌───┴───┐
-xPSR : {fmt32(psr) } {{Application,Execution,Interrupt}} Program Status
+xPSR : {fmt32(psr) } {{App,Exec,Intr}} Program Status
║║║║ ║ ║└───┬───┘
[N]egative╜║║║ ║ ║ └{psr&0x1FF} ({exception_names[psr&0x1FF]})
[Z]ero╜║║ ╙[T]humb ╙require [a]lignment
diff --git a/lib9p/9p.c b/lib9p/9p.c
deleted file mode 100644
index e7b20b5..0000000
--- a/lib9p/9p.c
+++ /dev/null
@@ -1,95 +0,0 @@
-/* lib9p/9p.c - Base 9P protocol utilities for both clients and servers
- *
- * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
-
-#include <inttypes.h> /* for PRIu{n} */
-#include <stdarg.h> /* for va_* */
-#include <string.h> /* for strncpy() */
-
-#include <libfmt/fmt.h> /* for fmt_vsnprintf() */
-
-#include <lib9p/9p.h>
-
-/* strings ********************************************************************/
-
-struct lib9p_s lib9p_str(char *s) {
- if (!s)
- return (struct lib9p_s){0};
- return (struct lib9p_s){
- .len = strlen(s),
- .utf8 = s,
- };
-}
-struct lib9p_s lib9p_strn(char *s, size_t maxlen) {
- if (maxlen == 0 || !s)
- return (struct lib9p_s){0};
- return (struct lib9p_s){
- .len = strnlen(s, maxlen),
- .utf8 = s,
- };
-}
-struct lib9p_s lib9p_str_slice(struct lib9p_s s, uint16_t beg, uint16_t end) {
- assert(s.len == 0 || s.utf8);
- assert(beg <= end && end <= s.len);
- return (struct lib9p_s){
- .len = end - beg,
- .utf8 = &s.utf8[beg],
- };
-}
-bool lib9p_str_eq(struct lib9p_s a, struct lib9p_s b) {
- return a.len == b.len &&
- (a.len == 0 || memcmp(a.utf8, b.utf8, a.len) == 0);
-}
-
-/* ctx ************************************************************************/
-
-void lib9p_ctx_clear_error(struct lib9p_ctx *ctx) {
- assert(ctx);
-#if CONFIG_9P_ENABLE_9P2000_u
- ctx->err_num = 0;
-#endif
- ctx->err_msg[0] = '\0';
-}
-
-bool lib9p_ctx_has_error(struct lib9p_ctx *ctx) {
- assert(ctx);
- return ctx->err_msg[0];
-}
-
-int lib9p_error(struct lib9p_ctx *ctx, lib9p_errno_t linux_errno, char const *msg) {
- if (lib9p_ctx_has_error(ctx))
- return -1;
- strncpy(ctx->err_msg, msg, sizeof(ctx->err_msg));
- ctx->err_msg[sizeof(ctx->err_msg)-1] = '\0';
-
-#if CONFIG_9P_ENABLE_9P2000_u
- ctx->err_num = linux_errno;
-#else
- (void)(linux_errno);
-#endif
-
- return -1;
-}
-
-int lib9p_errorf(struct lib9p_ctx *ctx, lib9p_errno_t linux_errno, char const *fmt, ...) {
- int n;
- va_list args;
-
- if (lib9p_ctx_has_error(ctx))
- return -1;
- va_start(args, fmt);
- n = fmt_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));
-
-#if CONFIG_9P_ENABLE_9P2000_u
- ctx->err_num = linux_errno;
-#else
- (void)(linux_errno);
-#endif
-
- return -1;
-}
diff --git a/lib9p/9p.generated.c b/lib9p/9p.generated.c
deleted file mode 100644
index b58a485..0000000
--- a/lib9p/9p.generated.c
+++ /dev/null
@@ -1,8037 +0,0 @@
-/* Generated by `lib9p/proto.gen lib9p/idl/2002-9P2000.9p lib9p/idl/2003-9P2000.p9p.9p lib9p/idl/2005-9P2000.u.9p lib9p/idl/2010-9P2000.L.9p lib9p/idl/2012-9P2000.e.9p`. DO NOT EDIT! */
-
-#include <stdbool.h>
-#include <stddef.h> /* for size_t */
-#include <inttypes.h> /* for PRI* macros */
-#include <string.h> /* for memset() */
-
-#include <libmisc/assert.h>
-#include <libmisc/endian.h>
-
-#include <lib9p/9p.h>
-
-#include "tables.h"
-#include "utf8.h"
-
-/* libobj vtables *************************************************************/
-#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u
-LO_IMPLEMENTATION_C(fmt_formatter, lib9p_tag_t, lib9p_tag, static);
-LO_IMPLEMENTATION_C(fmt_formatter, lib9p_fid_t, lib9p_fid, static);
-LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_s, lib9p_s, static);
-#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */
-#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u
-LO_IMPLEMENTATION_C(fmt_formatter, lib9p_dm_t, lib9p_dm, static);
-#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */
-#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u
-LO_IMPLEMENTATION_C(fmt_formatter, lib9p_qt_t, lib9p_qt, static);
-LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_qid, lib9p_qid, static);
-#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */
-#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u
-LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_stat, lib9p_stat, static);
-LO_IMPLEMENTATION_C(fmt_formatter, lib9p_o_t, lib9p_o, static);
-#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */
-#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u
-LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Tversion, lib9p_msg_Tversion, static);
-LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Rversion, lib9p_msg_Rversion, static);
-LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Tauth, lib9p_msg_Tauth, static);
-LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Rauth, lib9p_msg_Rauth, static);
-LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Tattach, lib9p_msg_Tattach, static);
-LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Rattach, lib9p_msg_Rattach, static);
-LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Rerror, lib9p_msg_Rerror, static);
-LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Tflush, lib9p_msg_Tflush, static);
-LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Rflush, lib9p_msg_Rflush, static);
-LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Twalk, lib9p_msg_Twalk, static);
-LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Rwalk, lib9p_msg_Rwalk, static);
-#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */
-#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u
-LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Topen, lib9p_msg_Topen, static);
-LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Ropen, lib9p_msg_Ropen, static);
-LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Tcreate, lib9p_msg_Tcreate, static);
-LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Rcreate, lib9p_msg_Rcreate, static);
-#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */
-#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u
-LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Tread, lib9p_msg_Tread, static);
-LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Rread, lib9p_msg_Rread, static);
-LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Twrite, lib9p_msg_Twrite, static);
-LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Rwrite, lib9p_msg_Rwrite, static);
-LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Tclunk, lib9p_msg_Tclunk, static);
-LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Rclunk, lib9p_msg_Rclunk, static);
-LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Tremove, lib9p_msg_Tremove, static);
-LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Rremove, lib9p_msg_Rremove, static);
-#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */
-#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u
-LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Tstat, lib9p_msg_Tstat, static);
-LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Rstat, lib9p_msg_Rstat, static);
-LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Twstat, lib9p_msg_Twstat, static);
-LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Rwstat, lib9p_msg_Rwstat, static);
-#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */
-#if CONFIG_9P_ENABLE_9P2000_p9p
-LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Topenfd, lib9p_msg_Topenfd, static);
-LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Ropenfd, lib9p_msg_Ropenfd, static);
-#endif /* CONFIG_9P_ENABLE_9P2000_p9p */
-#if CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_u
-LO_IMPLEMENTATION_C(fmt_formatter, lib9p_nuid_t, lib9p_nuid, static);
-LO_IMPLEMENTATION_C(fmt_formatter, lib9p_errno_t, lib9p_errno, static);
-#endif /* CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_u */
-#if CONFIG_9P_ENABLE_9P2000_L
-LO_IMPLEMENTATION_C(fmt_formatter, lib9p_super_magic_t, lib9p_super_magic, static);
-LO_IMPLEMENTATION_C(fmt_formatter, lib9p_lo_t, lib9p_lo, static);
-LO_IMPLEMENTATION_C(fmt_formatter, lib9p_dt_t, lib9p_dt, static);
-LO_IMPLEMENTATION_C(fmt_formatter, lib9p_mode_t, lib9p_mode, static);
-LO_IMPLEMENTATION_C(fmt_formatter, lib9p_b4_t, lib9p_b4, static);
-LO_IMPLEMENTATION_C(fmt_formatter, lib9p_getattr_t, lib9p_getattr, static);
-LO_IMPLEMENTATION_C(fmt_formatter, lib9p_setattr_t, lib9p_setattr, static);
-LO_IMPLEMENTATION_C(fmt_formatter, lib9p_lock_type_t, lib9p_lock_type, static);
-LO_IMPLEMENTATION_C(fmt_formatter, lib9p_lock_flags_t, lib9p_lock_flags, static);
-LO_IMPLEMENTATION_C(fmt_formatter, lib9p_lock_status_t, lib9p_lock_status, static);
-LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Rlerror, lib9p_msg_Rlerror, static);
-LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Tstatfs, lib9p_msg_Tstatfs, static);
-LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Rstatfs, lib9p_msg_Rstatfs, static);
-LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Tlopen, lib9p_msg_Tlopen, static);
-LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Rlopen, lib9p_msg_Rlopen, static);
-LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Tlcreate, lib9p_msg_Tlcreate, static);
-LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Rlcreate, lib9p_msg_Rlcreate, static);
-LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Tsymlink, lib9p_msg_Tsymlink, static);
-LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Rsymlink, lib9p_msg_Rsymlink, static);
-LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Tmknod, lib9p_msg_Tmknod, static);
-LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Rmknod, lib9p_msg_Rmknod, static);
-LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Trename, lib9p_msg_Trename, static);
-LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Rrename, lib9p_msg_Rrename, static);
-LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Treadlink, lib9p_msg_Treadlink, static);
-LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Rreadlink, lib9p_msg_Rreadlink, static);
-LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Tgetattr, lib9p_msg_Tgetattr, static);
-LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Rgetattr, lib9p_msg_Rgetattr, static);
-LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Tsetattr, lib9p_msg_Tsetattr, static);
-LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Rsetattr, lib9p_msg_Rsetattr, static);
-LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Txattrwalk, lib9p_msg_Txattrwalk, static);
-LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Rxattrwalk, lib9p_msg_Rxattrwalk, static);
-LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Txattrcreate, lib9p_msg_Txattrcreate, static);
-LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Rxattrcreate, lib9p_msg_Rxattrcreate, static);
-LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Treaddir, lib9p_msg_Treaddir, static);
-LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Rreaddir, lib9p_msg_Rreaddir, static);
-LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Tfsync, lib9p_msg_Tfsync, static);
-LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Rfsync, lib9p_msg_Rfsync, static);
-LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Tlock, lib9p_msg_Tlock, static);
-LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Rlock, lib9p_msg_Rlock, static);
-LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Tgetlock, lib9p_msg_Tgetlock, static);
-LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Rgetlock, lib9p_msg_Rgetlock, static);
-LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Tlink, lib9p_msg_Tlink, static);
-LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Rlink, lib9p_msg_Rlink, static);
-LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Tmkdir, lib9p_msg_Tmkdir, static);
-LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Rmkdir, lib9p_msg_Rmkdir, static);
-LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Trenameat, lib9p_msg_Trenameat, static);
-LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Rrenameat, lib9p_msg_Rrenameat, static);
-LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Tunlinkat, lib9p_msg_Tunlinkat, static);
-LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Runlinkat, lib9p_msg_Runlinkat, static);
-#endif /* CONFIG_9P_ENABLE_9P2000_L */
-#if CONFIG_9P_ENABLE_9P2000_e
-LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Tsession, lib9p_msg_Tsession, static);
-LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Rsession, lib9p_msg_Rsession, static);
-LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Tsread, lib9p_msg_Tsread, static);
-LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Rsread, lib9p_msg_Rsread, static);
-LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Tswrite, lib9p_msg_Tswrite, static);
-LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Rswrite, lib9p_msg_Rswrite, static);
-#endif /* CONFIG_9P_ENABLE_9P2000_e */
-
-/* utilities ******************************************************************/
-#if CONFIG_9P_ENABLE_9P2000
- #define _is_ver_9P2000(v) (v == LIB9P_VER_9P2000)
-#else
- #define _is_ver_9P2000(v) false
-#endif
-#if CONFIG_9P_ENABLE_9P2000_L
- #define _is_ver_9P2000_L(v) (v == LIB9P_VER_9P2000_L)
-#else
- #define _is_ver_9P2000_L(v) false
-#endif
-#if CONFIG_9P_ENABLE_9P2000_e
- #define _is_ver_9P2000_e(v) (v == LIB9P_VER_9P2000_e)
-#else
- #define _is_ver_9P2000_e(v) false
-#endif
-#if CONFIG_9P_ENABLE_9P2000_p9p
- #define _is_ver_9P2000_p9p(v) (v == LIB9P_VER_9P2000_p9p)
-#else
- #define _is_ver_9P2000_p9p(v) false
-#endif
-#if CONFIG_9P_ENABLE_9P2000_u
- #define _is_ver_9P2000_u(v) (v == LIB9P_VER_9P2000_u)
-#else
- #define _is_ver_9P2000_u(v) false
-#endif
-
-/**
- * is_ver(ctx, ver) is essentially `(ctx->version == LIB9P_VER_##ver)`, but
- * compiles correctly (to `false`) even if `LIB9P_VER_##ver` isn't defined
- * (because `!CONFIG_9P_ENABLE_##ver`). This is useful when `||`ing
- * several version checks together.
- */
-#define is_ver(ctx, ver) _is_ver_##ver((ctx)->version)
-
-/* bitmasks *******************************************************************/
-
-#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u
-static const lib9p_dm_t dm_masks[LIB9P_VER_NUM] = {
-#if CONFIG_9P_ENABLE_9P2000
- [LIB9P_VER_9P2000] = 0b11111100000000000000000111111111,
-#endif /* CONFIG_9P_ENABLE_9P2000 */
-#if CONFIG_9P_ENABLE_9P2000_L
- [LIB9P_VER_9P2000_L] = 0b00000000000000000000000000000000,
-#endif /* CONFIG_9P_ENABLE_9P2000_L */
-#if CONFIG_9P_ENABLE_9P2000_e
- [LIB9P_VER_9P2000_e] = 0b11111100000000000000000111111111,
-#endif /* CONFIG_9P_ENABLE_9P2000_e */
-#if CONFIG_9P_ENABLE_9P2000_p9p
- [LIB9P_VER_9P2000_p9p] = 0b11111100000000000000000111111111,
-#endif /* CONFIG_9P_ENABLE_9P2000_p9p */
-#if CONFIG_9P_ENABLE_9P2000_u
- [LIB9P_VER_9P2000_u] = 0b11111100101111000000000111111111,
-#endif /* CONFIG_9P_ENABLE_9P2000_u */
-};
-
-#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */
-#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u
-static const lib9p_qt_t qt_masks[LIB9P_VER_NUM] = {
-#if CONFIG_9P_ENABLE_9P2000
- [LIB9P_VER_9P2000] = 0b11111100,
-#endif /* CONFIG_9P_ENABLE_9P2000 */
-#if CONFIG_9P_ENABLE_9P2000_L
- [LIB9P_VER_9P2000_L] = 0b11111100,
-#endif /* CONFIG_9P_ENABLE_9P2000_L */
-#if CONFIG_9P_ENABLE_9P2000_e
- [LIB9P_VER_9P2000_e] = 0b11111100,
-#endif /* CONFIG_9P_ENABLE_9P2000_e */
-#if CONFIG_9P_ENABLE_9P2000_p9p
- [LIB9P_VER_9P2000_p9p] = 0b11111100,
-#endif /* CONFIG_9P_ENABLE_9P2000_p9p */
-#if CONFIG_9P_ENABLE_9P2000_u
- [LIB9P_VER_9P2000_u] = 0b11111110,
-#endif /* CONFIG_9P_ENABLE_9P2000_u */
-};
-
-#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */
-#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u
-static const lib9p_o_t o_masks[LIB9P_VER_NUM] = {
-#if CONFIG_9P_ENABLE_9P2000
- [LIB9P_VER_9P2000] = 0b01010011,
-#endif /* CONFIG_9P_ENABLE_9P2000 */
-#if CONFIG_9P_ENABLE_9P2000_L
- [LIB9P_VER_9P2000_L] = 0b00000000,
-#endif /* CONFIG_9P_ENABLE_9P2000_L */
-#if CONFIG_9P_ENABLE_9P2000_e
- [LIB9P_VER_9P2000_e] = 0b01010011,
-#endif /* CONFIG_9P_ENABLE_9P2000_e */
-#if CONFIG_9P_ENABLE_9P2000_p9p
- [LIB9P_VER_9P2000_p9p] = 0b01010011,
-#endif /* CONFIG_9P_ENABLE_9P2000_p9p */
-#if CONFIG_9P_ENABLE_9P2000_u
- [LIB9P_VER_9P2000_u] = 0b01010011,
-#endif /* CONFIG_9P_ENABLE_9P2000_u */
-};
-
-#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */
-#if CONFIG_9P_ENABLE_9P2000_L
-static const lib9p_lo_t lo_masks[LIB9P_VER_NUM] = {
-#if CONFIG_9P_ENABLE_9P2000
- [LIB9P_VER_9P2000] = 0b00000000000000000000000000000000,
-#endif /* CONFIG_9P_ENABLE_9P2000 */
- [LIB9P_VER_9P2000_L] = 0b00000000000111111111111111000011,
-#if CONFIG_9P_ENABLE_9P2000_e
- [LIB9P_VER_9P2000_e] = 0b00000000000000000000000000000000,
-#endif /* CONFIG_9P_ENABLE_9P2000_e */
-#if CONFIG_9P_ENABLE_9P2000_p9p
- [LIB9P_VER_9P2000_p9p] = 0b00000000000000000000000000000000,
-#endif /* CONFIG_9P_ENABLE_9P2000_p9p */
-#if CONFIG_9P_ENABLE_9P2000_u
- [LIB9P_VER_9P2000_u] = 0b00000000000000000000000000000000,
-#endif /* CONFIG_9P_ENABLE_9P2000_u */
-};
-
-static const lib9p_mode_t mode_masks[LIB9P_VER_NUM] = {
-#if CONFIG_9P_ENABLE_9P2000
- [LIB9P_VER_9P2000] = 0b00000000000000000000000000000000,
-#endif /* CONFIG_9P_ENABLE_9P2000 */
- [LIB9P_VER_9P2000_L] = 0b00000000000000001111111111111111,
-#if CONFIG_9P_ENABLE_9P2000_e
- [LIB9P_VER_9P2000_e] = 0b00000000000000000000000000000000,
-#endif /* CONFIG_9P_ENABLE_9P2000_e */
-#if CONFIG_9P_ENABLE_9P2000_p9p
- [LIB9P_VER_9P2000_p9p] = 0b00000000000000000000000000000000,
-#endif /* CONFIG_9P_ENABLE_9P2000_p9p */
-#if CONFIG_9P_ENABLE_9P2000_u
- [LIB9P_VER_9P2000_u] = 0b00000000000000000000000000000000,
-#endif /* CONFIG_9P_ENABLE_9P2000_u */
-};
-
-static const lib9p_getattr_t getattr_masks[LIB9P_VER_NUM] = {
-#if CONFIG_9P_ENABLE_9P2000
- [LIB9P_VER_9P2000] = 0b0000000000000000000000000000000000000000000000000000000000000000,
-#endif /* CONFIG_9P_ENABLE_9P2000 */
- [LIB9P_VER_9P2000_L] = 0b0000000000000000000000000000000000000000000000000011111111111111,
-#if CONFIG_9P_ENABLE_9P2000_e
- [LIB9P_VER_9P2000_e] = 0b0000000000000000000000000000000000000000000000000000000000000000,
-#endif /* CONFIG_9P_ENABLE_9P2000_e */
-#if CONFIG_9P_ENABLE_9P2000_p9p
- [LIB9P_VER_9P2000_p9p] = 0b0000000000000000000000000000000000000000000000000000000000000000,
-#endif /* CONFIG_9P_ENABLE_9P2000_p9p */
-#if CONFIG_9P_ENABLE_9P2000_u
- [LIB9P_VER_9P2000_u] = 0b0000000000000000000000000000000000000000000000000000000000000000,
-#endif /* CONFIG_9P_ENABLE_9P2000_u */
-};
-
-static const lib9p_setattr_t setattr_masks[LIB9P_VER_NUM] = {
-#if CONFIG_9P_ENABLE_9P2000
- [LIB9P_VER_9P2000] = 0b00000000000000000000000000000000,
-#endif /* CONFIG_9P_ENABLE_9P2000 */
- [LIB9P_VER_9P2000_L] = 0b00000000000000000000000111111111,
-#if CONFIG_9P_ENABLE_9P2000_e
- [LIB9P_VER_9P2000_e] = 0b00000000000000000000000000000000,
-#endif /* CONFIG_9P_ENABLE_9P2000_e */
-#if CONFIG_9P_ENABLE_9P2000_p9p
- [LIB9P_VER_9P2000_p9p] = 0b00000000000000000000000000000000,
-#endif /* CONFIG_9P_ENABLE_9P2000_p9p */
-#if CONFIG_9P_ENABLE_9P2000_u
- [LIB9P_VER_9P2000_u] = 0b00000000000000000000000000000000,
-#endif /* CONFIG_9P_ENABLE_9P2000_u */
-};
-
-static const lib9p_lock_flags_t lock_flags_masks[LIB9P_VER_NUM] = {
-#if CONFIG_9P_ENABLE_9P2000
- [LIB9P_VER_9P2000] = 0b00000000000000000000000000000000,
-#endif /* CONFIG_9P_ENABLE_9P2000 */
- [LIB9P_VER_9P2000_L] = 0b00000000000000000000000000000011,
-#if CONFIG_9P_ENABLE_9P2000_e
- [LIB9P_VER_9P2000_e] = 0b00000000000000000000000000000000,
-#endif /* CONFIG_9P_ENABLE_9P2000_e */
-#if CONFIG_9P_ENABLE_9P2000_p9p
- [LIB9P_VER_9P2000_p9p] = 0b00000000000000000000000000000000,
-#endif /* CONFIG_9P_ENABLE_9P2000_p9p */
-#if CONFIG_9P_ENABLE_9P2000_u
- [LIB9P_VER_9P2000_u] = 0b00000000000000000000000000000000,
-#endif /* CONFIG_9P_ENABLE_9P2000_u */
-};
-#endif /* CONFIG_9P_ENABLE_9P2000_L */
-
-/* validate_* *****************************************************************/
-
-#define VALIDATE_NET_BYTES(n) \
- if (__builtin_add_overflow(net_offset, n, &net_offset)) \
- /* If needed-net-size overflowed uint32_t, then \
- * there's no way that actual-net-size will live up to \
- * that. */ \
- return lib9p_error(ctx, LINUX_EBADMSG, "message is too short for content"); \
- if (net_offset > net_size) \
- return lib9p_errorf(ctx, LINUX_EBADMSG, "message is too short for content (%"PRIu32" > %"PRIu32") @ %d", net_offset, net_size, __LINE__);
-#define VALIDATE_NET_UTF8(n) \
- { \
- size_t len = n; \
- VALIDATE_NET_BYTES(len); \
- if (!is_valid_utf8_without_nul(&net_bytes[net_offset-len], len)) \
- return lib9p_error(ctx, LINUX_EBADMSG, "message contains invalid UTF-8"); \
- }
-#define RESERVE_HOST_BYTES(n) \
- if (__builtin_add_overflow(host_size, n, &host_size)) \
- /* If needed-host-size overflowed ssize_t, then there's \
- * no way that actual-net-size will live up to \
- * that. */ \
- return lib9p_error(ctx, LINUX_EBADMSG, "message is too short for content");
-#define GET_U8LE(off) (net_bytes[off])
-#define GET_U16LE(off) uint16le_decode(&net_bytes[off])
-#define GET_U32LE(off) uint32le_decode(&net_bytes[off])
-#define GET_U64LE(off) uint64le_decode(&net_bytes[off])
-#define LAST_U8LE() GET_U8LE(net_offset-1)
-#define LAST_U16LE() GET_U16LE(net_offset-2)
-#define LAST_U32LE() GET_U32LE(net_offset-4)
-#define LAST_U64LE() GET_U64LE(net_offset-8)
-
-#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u
-static ssize_t validate_stat(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes, uint32_t *ret_net_size) {
- uint32_t net_offset = 0;
- ssize_t host_size = sizeof(struct lib9p_stat);
- uint32_t offsetof_stat_size = net_offset + 0;
- uint32_t offsetof_kern_type = net_offset + 2;
- uint32_t offsetof_file_qid_type = net_offset + 8;
- VALIDATE_NET_BYTES(21);
- if (GET_U8LE(offsetof_file_qid_type) & ~qt_masks[ctx->version])
- return lib9p_errorf(ctx, LINUX_EBADMSG, "unknown bits in qt bitfield: %#02"PRIx8,
- GET_U8LE(offsetof_file_qid_type) & ~qt_masks[ctx->version]);
- uint32_t offsetof_file_mode = net_offset + 0;
- VALIDATE_NET_BYTES(22);
- VALIDATE_NET_UTF8(LAST_U16LE());
- VALIDATE_NET_BYTES(2);
- VALIDATE_NET_UTF8(LAST_U16LE());
- VALIDATE_NET_BYTES(2);
- VALIDATE_NET_UTF8(LAST_U16LE());
- VALIDATE_NET_BYTES(2);
- VALIDATE_NET_UTF8(LAST_U16LE());
-#if CONFIG_9P_ENABLE_9P2000_u
- if (is_ver(ctx, 9P2000_u)) {
- VALIDATE_NET_BYTES(2);
- VALIDATE_NET_UTF8(LAST_U16LE());
- VALIDATE_NET_BYTES(12);
- }
-#endif /* CONFIG_9P_ENABLE_9P2000_u */
- uint32_t offsetof_end = net_offset + 0;
- if ((uint32_t)GET_U32LE(offsetof_stat_size) != (uint32_t)(offsetof_end - offsetof_kern_type))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "stat->stat_size value is wrong: actual: %"PRIu32" != correct:%"PRIu32,
- (uint32_t)GET_U32LE(offsetof_stat_size), (uint32_t)(offsetof_end - offsetof_kern_type));
- if (GET_U32LE(offsetof_file_mode) & ~dm_masks[ctx->version])
- return lib9p_errorf(ctx, LINUX_EBADMSG, "unknown bits in dm bitfield: %#08"PRIx32,
- GET_U32LE(offsetof_file_mode) & ~dm_masks[ctx->version]);
- if (ret_net_size)
- *ret_net_size = net_offset;
- return (ssize_t)host_size;
-}
-
-#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */
-#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u
-static ssize_t validate_Tversion(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
- uint32_t net_offset = 0;
- ssize_t host_size = sizeof(struct lib9p_msg_Tversion);
- uint32_t offsetof_size = net_offset + 0;
- uint32_t offsetof_typ = net_offset + 4;
- VALIDATE_NET_BYTES(13);
- VALIDATE_NET_UTF8(LAST_U16LE());
- uint32_t offsetof_end = net_offset + 0;
- if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Tversion->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32,
- (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size));
- if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(100))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Tversion->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8,
- (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(100));
- return (ssize_t)host_size;
-}
-
-static ssize_t validate_Rversion(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
- uint32_t net_offset = 0;
- ssize_t host_size = sizeof(struct lib9p_msg_Rversion);
- uint32_t offsetof_size = net_offset + 0;
- uint32_t offsetof_typ = net_offset + 4;
- VALIDATE_NET_BYTES(13);
- VALIDATE_NET_UTF8(LAST_U16LE());
- uint32_t offsetof_end = net_offset + 0;
- if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Rversion->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32,
- (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size));
- if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(101))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Rversion->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8,
- (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(101));
- return (ssize_t)host_size;
-}
-
-static ssize_t validate_Tauth(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
- uint32_t net_offset = 0;
- ssize_t host_size = sizeof(struct lib9p_msg_Tauth);
- uint32_t offsetof_size = net_offset + 0;
- uint32_t offsetof_typ = net_offset + 4;
- VALIDATE_NET_BYTES(13);
- VALIDATE_NET_UTF8(LAST_U16LE());
- VALIDATE_NET_BYTES(2);
- VALIDATE_NET_UTF8(LAST_U16LE());
-#if CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_u
- if (( is_ver(ctx, 9P2000_L) || is_ver(ctx, 9P2000_u) )) {
- VALIDATE_NET_BYTES(4);
- }
-#endif /* CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_u */
- uint32_t offsetof_end = net_offset + 0;
- if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Tauth->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32,
- (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size));
- if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(102))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Tauth->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8,
- (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(102));
- return (ssize_t)host_size;
-}
-
-static ssize_t validate_Rauth(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
- uint32_t net_offset = 0;
- ssize_t host_size = sizeof(struct lib9p_msg_Rauth);
- uint32_t offsetof_size = net_offset + 0;
- uint32_t offsetof_typ = net_offset + 4;
- uint32_t offsetof_aqid_type = net_offset + 7;
- VALIDATE_NET_BYTES(20);
- if (GET_U8LE(offsetof_aqid_type) & ~qt_masks[ctx->version])
- return lib9p_errorf(ctx, LINUX_EBADMSG, "unknown bits in qt bitfield: %#02"PRIx8,
- GET_U8LE(offsetof_aqid_type) & ~qt_masks[ctx->version]);
- uint32_t offsetof_end = net_offset + 0;
- if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Rauth->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32,
- (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size));
- if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(103))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Rauth->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8,
- (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(103));
- return (ssize_t)host_size;
-}
-
-static ssize_t validate_Tattach(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
- uint32_t net_offset = 0;
- ssize_t host_size = sizeof(struct lib9p_msg_Tattach);
- uint32_t offsetof_size = net_offset + 0;
- uint32_t offsetof_typ = net_offset + 4;
- VALIDATE_NET_BYTES(17);
- VALIDATE_NET_UTF8(LAST_U16LE());
- VALIDATE_NET_BYTES(2);
- VALIDATE_NET_UTF8(LAST_U16LE());
-#if CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_u
- if (( is_ver(ctx, 9P2000_L) || is_ver(ctx, 9P2000_u) )) {
- VALIDATE_NET_BYTES(4);
- }
-#endif /* CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_u */
- uint32_t offsetof_end = net_offset + 0;
- if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Tattach->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32,
- (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size));
- if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(104))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Tattach->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8,
- (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(104));
- return (ssize_t)host_size;
-}
-
-static ssize_t validate_Rattach(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
- uint32_t net_offset = 0;
- ssize_t host_size = sizeof(struct lib9p_msg_Rattach);
- uint32_t offsetof_size = net_offset + 0;
- uint32_t offsetof_typ = net_offset + 4;
- uint32_t offsetof_qid_type = net_offset + 7;
- VALIDATE_NET_BYTES(20);
- if (GET_U8LE(offsetof_qid_type) & ~qt_masks[ctx->version])
- return lib9p_errorf(ctx, LINUX_EBADMSG, "unknown bits in qt bitfield: %#02"PRIx8,
- GET_U8LE(offsetof_qid_type) & ~qt_masks[ctx->version]);
- uint32_t offsetof_end = net_offset + 0;
- if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Rattach->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32,
- (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size));
- if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(105))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Rattach->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8,
- (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(105));
- return (ssize_t)host_size;
-}
-
-static ssize_t validate_Rerror(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
- uint32_t net_offset = 0;
- ssize_t host_size = sizeof(struct lib9p_msg_Rerror);
- uint32_t offsetof_size = net_offset + 0;
- uint32_t offsetof_typ = net_offset + 4;
- VALIDATE_NET_BYTES(9);
- VALIDATE_NET_UTF8(LAST_U16LE());
-#if CONFIG_9P_ENABLE_9P2000_u
- if (is_ver(ctx, 9P2000_u)) {
- VALIDATE_NET_BYTES(4);
- }
-#endif /* CONFIG_9P_ENABLE_9P2000_u */
- uint32_t offsetof_end = net_offset + 0;
- if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Rerror->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32,
- (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size));
- if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(107))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Rerror->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8,
- (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(107));
- return (ssize_t)host_size;
-}
-
-static ssize_t validate_Tflush(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
- uint32_t net_offset = 0;
- ssize_t host_size = sizeof(struct lib9p_msg_Tflush);
- uint32_t offsetof_size = net_offset + 0;
- uint32_t offsetof_typ = net_offset + 4;
- uint32_t offsetof_end = net_offset + 9;
- VALIDATE_NET_BYTES(9);
- if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Tflush->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32,
- (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size));
- if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(108))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Tflush->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8,
- (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(108));
- return (ssize_t)host_size;
-}
-
-static ssize_t validate_Rflush(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
- uint32_t net_offset = 0;
- ssize_t host_size = sizeof(struct lib9p_msg_Rflush);
- uint32_t offsetof_size = net_offset + 0;
- uint32_t offsetof_typ = net_offset + 4;
- uint32_t offsetof_end = net_offset + 7;
- VALIDATE_NET_BYTES(7);
- if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Rflush->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32,
- (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size));
- if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(109))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Rflush->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8,
- (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(109));
- return (ssize_t)host_size;
-}
-
-static ssize_t validate_Twalk(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
- uint32_t net_offset = 0;
- ssize_t host_size = sizeof(struct lib9p_msg_Twalk);
- uint32_t offsetof_size = net_offset + 0;
- uint32_t offsetof_typ = net_offset + 4;
- uint32_t offsetof_nwname = net_offset + 15;
- VALIDATE_NET_BYTES(17);
- for (uint16_t i = 0, cnt = LAST_U16LE(); i < cnt; i++) {
- RESERVE_HOST_BYTES(sizeof(struct lib9p_s));
- VALIDATE_NET_BYTES(2);
- VALIDATE_NET_UTF8(LAST_U16LE());
- }
- uint32_t offsetof_end = net_offset + 0;
- if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Twalk->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32,
- (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size));
- if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(110))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Twalk->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8,
- (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(110));
- if ((uint16_t)GET_U16LE(offsetof_nwname) > (uint16_t)(16))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Twalk->nwname value is too large: %"PRIu16" > %"PRIu16,
- (uint16_t)GET_U16LE(offsetof_nwname), (uint16_t)(16));
- return (ssize_t)host_size;
-}
-
-static ssize_t validate_Rwalk(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
- uint32_t net_offset = 0;
- ssize_t host_size = sizeof(struct lib9p_msg_Rwalk);
- uint32_t offsetof_size = net_offset + 0;
- uint32_t offsetof_typ = net_offset + 4;
- uint32_t offsetof_nwqid = net_offset + 7;
- VALIDATE_NET_BYTES(9);
- for (uint16_t i = 0, cnt = LAST_U16LE(); i < cnt; i++) {
- RESERVE_HOST_BYTES(sizeof(struct lib9p_qid));
- uint32_t offsetof_wqid_type = net_offset + 0;
- VALIDATE_NET_BYTES(13);
- if (GET_U8LE(offsetof_wqid_type) & ~qt_masks[ctx->version])
- return lib9p_errorf(ctx, LINUX_EBADMSG, "unknown bits in qt bitfield: %#02"PRIx8,
- GET_U8LE(offsetof_wqid_type) & ~qt_masks[ctx->version]);
- }
- uint32_t offsetof_end = net_offset + 0;
- if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Rwalk->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32,
- (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size));
- if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(111))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Rwalk->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8,
- (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(111));
- if ((uint16_t)GET_U16LE(offsetof_nwqid) > (uint16_t)(16))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Rwalk->nwqid value is too large: %"PRIu16" > %"PRIu16,
- (uint16_t)GET_U16LE(offsetof_nwqid), (uint16_t)(16));
- return (ssize_t)host_size;
-}
-
-#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */
-#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u
-static ssize_t validate_Topen(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
- uint32_t net_offset = 0;
- ssize_t host_size = sizeof(struct lib9p_msg_Topen);
- uint32_t offsetof_size = net_offset + 0;
- uint32_t offsetof_typ = net_offset + 4;
- uint32_t offsetof_mode = net_offset + 11;
- uint32_t offsetof_end = net_offset + 12;
- VALIDATE_NET_BYTES(12);
- if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Topen->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32,
- (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size));
- if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(112))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Topen->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8,
- (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(112));
- if (GET_U8LE(offsetof_mode) & ~o_masks[ctx->version])
- return lib9p_errorf(ctx, LINUX_EBADMSG, "unknown bits in o bitfield: %#02"PRIx8,
- GET_U8LE(offsetof_mode) & ~o_masks[ctx->version]);
- return (ssize_t)host_size;
-}
-
-static ssize_t validate_Ropen(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
- uint32_t net_offset = 0;
- ssize_t host_size = sizeof(struct lib9p_msg_Ropen);
- uint32_t offsetof_size = net_offset + 0;
- uint32_t offsetof_typ = net_offset + 4;
- uint32_t offsetof_qid_type = net_offset + 7;
- VALIDATE_NET_BYTES(20);
- if (GET_U8LE(offsetof_qid_type) & ~qt_masks[ctx->version])
- return lib9p_errorf(ctx, LINUX_EBADMSG, "unknown bits in qt bitfield: %#02"PRIx8,
- GET_U8LE(offsetof_qid_type) & ~qt_masks[ctx->version]);
- uint32_t offsetof_end = net_offset + 4;
- VALIDATE_NET_BYTES(4);
- if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Ropen->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32,
- (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size));
- if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(113))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Ropen->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8,
- (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(113));
- return (ssize_t)host_size;
-}
-
-static ssize_t validate_Tcreate(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
- uint32_t net_offset = 0;
- ssize_t host_size = sizeof(struct lib9p_msg_Tcreate);
- uint32_t offsetof_size = net_offset + 0;
- uint32_t offsetof_typ = net_offset + 4;
- VALIDATE_NET_BYTES(13);
- VALIDATE_NET_UTF8(LAST_U16LE());
- uint32_t offsetof_perm = net_offset + 0;
- uint32_t offsetof_mode = net_offset + 4;
- uint32_t offsetof_end = net_offset + 5;
- VALIDATE_NET_BYTES(5);
- if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Tcreate->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32,
- (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size));
- if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(114))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Tcreate->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8,
- (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(114));
- if (GET_U32LE(offsetof_perm) & ~dm_masks[ctx->version])
- return lib9p_errorf(ctx, LINUX_EBADMSG, "unknown bits in dm bitfield: %#08"PRIx32,
- GET_U32LE(offsetof_perm) & ~dm_masks[ctx->version]);
- if (GET_U8LE(offsetof_mode) & ~o_masks[ctx->version])
- return lib9p_errorf(ctx, LINUX_EBADMSG, "unknown bits in o bitfield: %#02"PRIx8,
- GET_U8LE(offsetof_mode) & ~o_masks[ctx->version]);
- return (ssize_t)host_size;
-}
-
-static ssize_t validate_Rcreate(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
- uint32_t net_offset = 0;
- ssize_t host_size = sizeof(struct lib9p_msg_Rcreate);
- uint32_t offsetof_size = net_offset + 0;
- uint32_t offsetof_typ = net_offset + 4;
- uint32_t offsetof_qid_type = net_offset + 7;
- VALIDATE_NET_BYTES(20);
- if (GET_U8LE(offsetof_qid_type) & ~qt_masks[ctx->version])
- return lib9p_errorf(ctx, LINUX_EBADMSG, "unknown bits in qt bitfield: %#02"PRIx8,
- GET_U8LE(offsetof_qid_type) & ~qt_masks[ctx->version]);
- uint32_t offsetof_end = net_offset + 4;
- VALIDATE_NET_BYTES(4);
- if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Rcreate->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32,
- (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size));
- if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(115))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Rcreate->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8,
- (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(115));
- return (ssize_t)host_size;
-}
-
-#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */
-#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u
-static ssize_t validate_Tread(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
- uint32_t net_offset = 0;
- ssize_t host_size = sizeof(struct lib9p_msg_Tread);
- uint32_t offsetof_size = net_offset + 0;
- uint32_t offsetof_typ = net_offset + 4;
- uint32_t offsetof_offset = net_offset + 11;
- uint32_t offsetof_count = net_offset + 19;
- uint32_t offsetof_end = net_offset + 23;
- VALIDATE_NET_BYTES(23);
- if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Tread->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32,
- (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size));
- if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(116))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Tread->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8,
- (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(116));
- if ((uint64_t)GET_U64LE(offsetof_offset) > (uint64_t)(INT64_MAX))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Tread->offset value is too large: %"PRIu64" > %"PRIu64,
- (uint64_t)GET_U64LE(offsetof_offset), (uint64_t)(INT64_MAX));
- if ((uint32_t)GET_U32LE(offsetof_count) > (uint32_t)(INT32_MAX))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Tread->count value is too large: %"PRIu32" > %"PRIu32,
- (uint32_t)GET_U32LE(offsetof_count), (uint32_t)(INT32_MAX));
- return (ssize_t)host_size;
-}
-
-static ssize_t validate_Rread(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
- uint32_t net_offset = 0;
- ssize_t host_size = sizeof(struct lib9p_msg_Rread);
- uint32_t offsetof_size = net_offset + 0;
- uint32_t offsetof_typ = net_offset + 4;
- uint32_t offsetof_count = net_offset + 7;
- VALIDATE_NET_BYTES(11);
- VALIDATE_NET_BYTES(LAST_U32LE());
- uint32_t offsetof_end = net_offset + 0;
- if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Rread->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32,
- (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size));
- if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(117))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Rread->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8,
- (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(117));
- if ((uint32_t)GET_U32LE(offsetof_count) > (uint32_t)(INT32_MAX))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Rread->count value is too large: %"PRIu32" > %"PRIu32,
- (uint32_t)GET_U32LE(offsetof_count), (uint32_t)(INT32_MAX));
- return (ssize_t)host_size;
-}
-
-static ssize_t validate_Twrite(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
- uint32_t net_offset = 0;
- ssize_t host_size = sizeof(struct lib9p_msg_Twrite);
- uint32_t offsetof_size = net_offset + 0;
- uint32_t offsetof_typ = net_offset + 4;
- uint32_t offsetof_offset = net_offset + 11;
- uint32_t offsetof_count = net_offset + 19;
- VALIDATE_NET_BYTES(23);
- VALIDATE_NET_BYTES(LAST_U32LE());
- uint32_t offsetof_end = net_offset + 0;
- if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Twrite->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32,
- (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size));
- if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(118))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Twrite->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8,
- (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(118));
- if ((uint64_t)GET_U64LE(offsetof_offset) > (uint64_t)(INT64_MAX))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Twrite->offset value is too large: %"PRIu64" > %"PRIu64,
- (uint64_t)GET_U64LE(offsetof_offset), (uint64_t)(INT64_MAX));
- if ((uint32_t)GET_U32LE(offsetof_count) > (uint32_t)(INT32_MAX))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Twrite->count value is too large: %"PRIu32" > %"PRIu32,
- (uint32_t)GET_U32LE(offsetof_count), (uint32_t)(INT32_MAX));
- return (ssize_t)host_size;
-}
-
-static ssize_t validate_Rwrite(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
- uint32_t net_offset = 0;
- ssize_t host_size = sizeof(struct lib9p_msg_Rwrite);
- uint32_t offsetof_size = net_offset + 0;
- uint32_t offsetof_typ = net_offset + 4;
- uint32_t offsetof_count = net_offset + 7;
- uint32_t offsetof_end = net_offset + 11;
- VALIDATE_NET_BYTES(11);
- if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Rwrite->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32,
- (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size));
- if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(119))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Rwrite->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8,
- (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(119));
- if ((uint32_t)GET_U32LE(offsetof_count) > (uint32_t)(INT32_MAX))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Rwrite->count value is too large: %"PRIu32" > %"PRIu32,
- (uint32_t)GET_U32LE(offsetof_count), (uint32_t)(INT32_MAX));
- return (ssize_t)host_size;
-}
-
-static ssize_t validate_Tclunk(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
- uint32_t net_offset = 0;
- ssize_t host_size = sizeof(struct lib9p_msg_Tclunk);
- uint32_t offsetof_size = net_offset + 0;
- uint32_t offsetof_typ = net_offset + 4;
- uint32_t offsetof_end = net_offset + 11;
- VALIDATE_NET_BYTES(11);
- if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Tclunk->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32,
- (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size));
- if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(120))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Tclunk->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8,
- (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(120));
- return (ssize_t)host_size;
-}
-
-static ssize_t validate_Rclunk(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
- uint32_t net_offset = 0;
- ssize_t host_size = sizeof(struct lib9p_msg_Rclunk);
- uint32_t offsetof_size = net_offset + 0;
- uint32_t offsetof_typ = net_offset + 4;
- uint32_t offsetof_end = net_offset + 7;
- VALIDATE_NET_BYTES(7);
- if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Rclunk->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32,
- (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size));
- if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(121))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Rclunk->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8,
- (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(121));
- return (ssize_t)host_size;
-}
-
-static ssize_t validate_Tremove(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
- uint32_t net_offset = 0;
- ssize_t host_size = sizeof(struct lib9p_msg_Tremove);
- uint32_t offsetof_size = net_offset + 0;
- uint32_t offsetof_typ = net_offset + 4;
- uint32_t offsetof_end = net_offset + 11;
- VALIDATE_NET_BYTES(11);
- if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Tremove->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32,
- (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size));
- if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(122))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Tremove->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8,
- (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(122));
- return (ssize_t)host_size;
-}
-
-static ssize_t validate_Rremove(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
- uint32_t net_offset = 0;
- ssize_t host_size = sizeof(struct lib9p_msg_Rremove);
- uint32_t offsetof_size = net_offset + 0;
- uint32_t offsetof_typ = net_offset + 4;
- uint32_t offsetof_end = net_offset + 7;
- VALIDATE_NET_BYTES(7);
- if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Rremove->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32,
- (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size));
- if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(123))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Rremove->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8,
- (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(123));
- return (ssize_t)host_size;
-}
-
-#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */
-#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u
-static ssize_t validate_Tstat(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
- uint32_t net_offset = 0;
- ssize_t host_size = sizeof(struct lib9p_msg_Tstat);
- uint32_t offsetof_size = net_offset + 0;
- uint32_t offsetof_typ = net_offset + 4;
- uint32_t offsetof_end = net_offset + 11;
- VALIDATE_NET_BYTES(11);
- if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Tstat->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32,
- (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size));
- if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(124))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Tstat->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8,
- (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(124));
- return (ssize_t)host_size;
-}
-
-static ssize_t validate_Rstat(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
- uint32_t net_offset = 0;
- ssize_t host_size = sizeof(struct lib9p_msg_Rstat);
- uint32_t offsetof_size = net_offset + 0;
- uint32_t offsetof_typ = net_offset + 4;
- uint32_t offsetof_nstat = net_offset + 7;
- uint32_t offsetof_stat = net_offset + 9;
- uint32_t offsetof_stat_stat_size = net_offset + 9;
- uint32_t offsetof_stat_kern_type = net_offset + 11;
- uint32_t offsetof_stat_file_qid_type = net_offset + 17;
- VALIDATE_NET_BYTES(30);
- if (GET_U8LE(offsetof_stat_file_qid_type) & ~qt_masks[ctx->version])
- return lib9p_errorf(ctx, LINUX_EBADMSG, "unknown bits in qt bitfield: %#02"PRIx8,
- GET_U8LE(offsetof_stat_file_qid_type) & ~qt_masks[ctx->version]);
- uint32_t offsetof_stat_file_mode = net_offset + 0;
- VALIDATE_NET_BYTES(22);
- VALIDATE_NET_UTF8(LAST_U16LE());
- VALIDATE_NET_BYTES(2);
- VALIDATE_NET_UTF8(LAST_U16LE());
- VALIDATE_NET_BYTES(2);
- VALIDATE_NET_UTF8(LAST_U16LE());
- VALIDATE_NET_BYTES(2);
- VALIDATE_NET_UTF8(LAST_U16LE());
-#if CONFIG_9P_ENABLE_9P2000_u
- if (is_ver(ctx, 9P2000_u)) {
- VALIDATE_NET_BYTES(2);
- VALIDATE_NET_UTF8(LAST_U16LE());
- VALIDATE_NET_BYTES(12);
- }
-#endif /* CONFIG_9P_ENABLE_9P2000_u */
- uint32_t offsetof_stat_end = net_offset + 0;
- if ((uint32_t)GET_U32LE(offsetof_stat_stat_size) != (uint32_t)(offsetof_stat_end - offsetof_stat_kern_type))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Rstat->stat.stat_size value is wrong: actual: %"PRIu32" != correct:%"PRIu32,
- (uint32_t)GET_U32LE(offsetof_stat_stat_size), (uint32_t)(offsetof_stat_end - offsetof_stat_kern_type));
- if (GET_U32LE(offsetof_stat_file_mode) & ~dm_masks[ctx->version])
- return lib9p_errorf(ctx, LINUX_EBADMSG, "unknown bits in dm bitfield: %#08"PRIx32,
- GET_U32LE(offsetof_stat_file_mode) & ~dm_masks[ctx->version]);
- uint32_t offsetof_end = net_offset + 0;
- if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Rstat->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32,
- (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size));
- if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(125))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Rstat->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8,
- (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(125));
- if ((uint32_t)GET_U32LE(offsetof_nstat) != (uint32_t)(offsetof_end - offsetof_stat))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Rstat->nstat value is wrong: actual: %"PRIu32" != correct:%"PRIu32,
- (uint32_t)GET_U32LE(offsetof_nstat), (uint32_t)(offsetof_end - offsetof_stat));
- return (ssize_t)host_size;
-}
-
-static ssize_t validate_Twstat(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
- uint32_t net_offset = 0;
- ssize_t host_size = sizeof(struct lib9p_msg_Twstat);
- uint32_t offsetof_size = net_offset + 0;
- uint32_t offsetof_typ = net_offset + 4;
- uint32_t offsetof_nstat = net_offset + 11;
- uint32_t offsetof_stat = net_offset + 13;
- uint32_t offsetof_stat_stat_size = net_offset + 13;
- uint32_t offsetof_stat_kern_type = net_offset + 15;
- uint32_t offsetof_stat_file_qid_type = net_offset + 21;
- VALIDATE_NET_BYTES(34);
- if (GET_U8LE(offsetof_stat_file_qid_type) & ~qt_masks[ctx->version])
- return lib9p_errorf(ctx, LINUX_EBADMSG, "unknown bits in qt bitfield: %#02"PRIx8,
- GET_U8LE(offsetof_stat_file_qid_type) & ~qt_masks[ctx->version]);
- uint32_t offsetof_stat_file_mode = net_offset + 0;
- VALIDATE_NET_BYTES(22);
- VALIDATE_NET_UTF8(LAST_U16LE());
- VALIDATE_NET_BYTES(2);
- VALIDATE_NET_UTF8(LAST_U16LE());
- VALIDATE_NET_BYTES(2);
- VALIDATE_NET_UTF8(LAST_U16LE());
- VALIDATE_NET_BYTES(2);
- VALIDATE_NET_UTF8(LAST_U16LE());
-#if CONFIG_9P_ENABLE_9P2000_u
- if (is_ver(ctx, 9P2000_u)) {
- VALIDATE_NET_BYTES(2);
- VALIDATE_NET_UTF8(LAST_U16LE());
- VALIDATE_NET_BYTES(12);
- }
-#endif /* CONFIG_9P_ENABLE_9P2000_u */
- uint32_t offsetof_stat_end = net_offset + 0;
- if ((uint32_t)GET_U32LE(offsetof_stat_stat_size) != (uint32_t)(offsetof_stat_end - offsetof_stat_kern_type))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Twstat->stat.stat_size value is wrong: actual: %"PRIu32" != correct:%"PRIu32,
- (uint32_t)GET_U32LE(offsetof_stat_stat_size), (uint32_t)(offsetof_stat_end - offsetof_stat_kern_type));
- if (GET_U32LE(offsetof_stat_file_mode) & ~dm_masks[ctx->version])
- return lib9p_errorf(ctx, LINUX_EBADMSG, "unknown bits in dm bitfield: %#08"PRIx32,
- GET_U32LE(offsetof_stat_file_mode) & ~dm_masks[ctx->version]);
- uint32_t offsetof_end = net_offset + 0;
- if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Twstat->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32,
- (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size));
- if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(126))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Twstat->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8,
- (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(126));
- if ((uint32_t)GET_U32LE(offsetof_nstat) != (uint32_t)(offsetof_end - offsetof_stat))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Twstat->nstat value is wrong: actual: %"PRIu32" != correct:%"PRIu32,
- (uint32_t)GET_U32LE(offsetof_nstat), (uint32_t)(offsetof_end - offsetof_stat));
- return (ssize_t)host_size;
-}
-
-static ssize_t validate_Rwstat(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
- uint32_t net_offset = 0;
- ssize_t host_size = sizeof(struct lib9p_msg_Rwstat);
- uint32_t offsetof_size = net_offset + 0;
- uint32_t offsetof_typ = net_offset + 4;
- uint32_t offsetof_end = net_offset + 7;
- VALIDATE_NET_BYTES(7);
- if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Rwstat->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32,
- (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size));
- if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(127))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Rwstat->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8,
- (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(127));
- return (ssize_t)host_size;
-}
-
-#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */
-#if CONFIG_9P_ENABLE_9P2000_p9p
-static ssize_t validate_Topenfd(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
- uint32_t net_offset = 0;
- ssize_t host_size = sizeof(struct lib9p_msg_Topenfd);
- uint32_t offsetof_size = net_offset + 0;
- uint32_t offsetof_typ = net_offset + 4;
- uint32_t offsetof_mode = net_offset + 11;
- uint32_t offsetof_end = net_offset + 12;
- VALIDATE_NET_BYTES(12);
- if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Topenfd->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32,
- (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size));
- if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(98))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Topenfd->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8,
- (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(98));
- if (GET_U8LE(offsetof_mode) & ~o_masks[ctx->version])
- return lib9p_errorf(ctx, LINUX_EBADMSG, "unknown bits in o bitfield: %#02"PRIx8,
- GET_U8LE(offsetof_mode) & ~o_masks[ctx->version]);
- return (ssize_t)host_size;
-}
-
-static ssize_t validate_Ropenfd(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
- uint32_t net_offset = 0;
- ssize_t host_size = sizeof(struct lib9p_msg_Ropenfd);
- uint32_t offsetof_size = net_offset + 0;
- uint32_t offsetof_typ = net_offset + 4;
- uint32_t offsetof_qid_type = net_offset + 7;
- VALIDATE_NET_BYTES(20);
- if (GET_U8LE(offsetof_qid_type) & ~qt_masks[ctx->version])
- return lib9p_errorf(ctx, LINUX_EBADMSG, "unknown bits in qt bitfield: %#02"PRIx8,
- GET_U8LE(offsetof_qid_type) & ~qt_masks[ctx->version]);
- uint32_t offsetof_end = net_offset + 8;
- VALIDATE_NET_BYTES(8);
- if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Ropenfd->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32,
- (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size));
- if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(99))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Ropenfd->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8,
- (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(99));
- return (ssize_t)host_size;
-}
-
-#endif /* CONFIG_9P_ENABLE_9P2000_p9p */
-#if CONFIG_9P_ENABLE_9P2000_L
-static ssize_t validate_Rlerror(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
- uint32_t net_offset = 0;
- ssize_t host_size = sizeof(struct lib9p_msg_Rlerror);
- uint32_t offsetof_size = net_offset + 0;
- uint32_t offsetof_typ = net_offset + 4;
- uint32_t offsetof_end = net_offset + 11;
- VALIDATE_NET_BYTES(11);
- if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Rlerror->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32,
- (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size));
- if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(7))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Rlerror->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8,
- (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(7));
- return (ssize_t)host_size;
-}
-
-static ssize_t validate_Tstatfs(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
- uint32_t net_offset = 0;
- ssize_t host_size = sizeof(struct lib9p_msg_Tstatfs);
- uint32_t offsetof_size = net_offset + 0;
- uint32_t offsetof_typ = net_offset + 4;
- uint32_t offsetof_end = net_offset + 11;
- VALIDATE_NET_BYTES(11);
- if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Tstatfs->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32,
- (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size));
- if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(8))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Tstatfs->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8,
- (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(8));
- return (ssize_t)host_size;
-}
-
-static ssize_t validate_Rstatfs(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
- uint32_t net_offset = 0;
- ssize_t host_size = sizeof(struct lib9p_msg_Rstatfs);
- uint32_t offsetof_size = net_offset + 0;
- uint32_t offsetof_typ = net_offset + 4;
- uint32_t offsetof_end = net_offset + 67;
- VALIDATE_NET_BYTES(67);
- if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Rstatfs->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32,
- (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size));
- if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(9))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Rstatfs->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8,
- (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(9));
- return (ssize_t)host_size;
-}
-
-static ssize_t validate_Tlopen(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
- uint32_t net_offset = 0;
- ssize_t host_size = sizeof(struct lib9p_msg_Tlopen);
- uint32_t offsetof_size = net_offset + 0;
- uint32_t offsetof_typ = net_offset + 4;
- uint32_t offsetof_flags = net_offset + 11;
- uint32_t offsetof_end = net_offset + 15;
- VALIDATE_NET_BYTES(15);
- if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Tlopen->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32,
- (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size));
- if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(12))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Tlopen->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8,
- (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(12));
- if (GET_U32LE(offsetof_flags) & ~lo_masks[ctx->version])
- return lib9p_errorf(ctx, LINUX_EBADMSG, "unknown bits in lo bitfield: %#08"PRIx32,
- GET_U32LE(offsetof_flags) & ~lo_masks[ctx->version]);
- return (ssize_t)host_size;
-}
-
-static ssize_t validate_Rlopen(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
- uint32_t net_offset = 0;
- ssize_t host_size = sizeof(struct lib9p_msg_Rlopen);
- uint32_t offsetof_size = net_offset + 0;
- uint32_t offsetof_typ = net_offset + 4;
- uint32_t offsetof_qid_type = net_offset + 7;
- VALIDATE_NET_BYTES(20);
- if (GET_U8LE(offsetof_qid_type) & ~qt_masks[ctx->version])
- return lib9p_errorf(ctx, LINUX_EBADMSG, "unknown bits in qt bitfield: %#02"PRIx8,
- GET_U8LE(offsetof_qid_type) & ~qt_masks[ctx->version]);
- uint32_t offsetof_end = net_offset + 4;
- VALIDATE_NET_BYTES(4);
- if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Rlopen->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32,
- (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size));
- if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(13))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Rlopen->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8,
- (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(13));
- return (ssize_t)host_size;
-}
-
-static ssize_t validate_Tlcreate(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
- uint32_t net_offset = 0;
- ssize_t host_size = sizeof(struct lib9p_msg_Tlcreate);
- uint32_t offsetof_size = net_offset + 0;
- uint32_t offsetof_typ = net_offset + 4;
- VALIDATE_NET_BYTES(13);
- VALIDATE_NET_UTF8(LAST_U16LE());
- uint32_t offsetof_flags = net_offset + 0;
- uint32_t offsetof_mode = net_offset + 4;
- uint32_t offsetof_end = net_offset + 12;
- VALIDATE_NET_BYTES(12);
- if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Tlcreate->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32,
- (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size));
- if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(14))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Tlcreate->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8,
- (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(14));
- if (GET_U32LE(offsetof_flags) & ~lo_masks[ctx->version])
- return lib9p_errorf(ctx, LINUX_EBADMSG, "unknown bits in lo bitfield: %#08"PRIx32,
- GET_U32LE(offsetof_flags) & ~lo_masks[ctx->version]);
- if (GET_U32LE(offsetof_mode) & ~mode_masks[ctx->version])
- return lib9p_errorf(ctx, LINUX_EBADMSG, "unknown bits in mode bitfield: %#08"PRIx32,
- GET_U32LE(offsetof_mode) & ~mode_masks[ctx->version]);
- return (ssize_t)host_size;
-}
-
-static ssize_t validate_Rlcreate(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
- uint32_t net_offset = 0;
- ssize_t host_size = sizeof(struct lib9p_msg_Rlcreate);
- uint32_t offsetof_size = net_offset + 0;
- uint32_t offsetof_typ = net_offset + 4;
- uint32_t offsetof_qid_type = net_offset + 7;
- VALIDATE_NET_BYTES(20);
- if (GET_U8LE(offsetof_qid_type) & ~qt_masks[ctx->version])
- return lib9p_errorf(ctx, LINUX_EBADMSG, "unknown bits in qt bitfield: %#02"PRIx8,
- GET_U8LE(offsetof_qid_type) & ~qt_masks[ctx->version]);
- uint32_t offsetof_end = net_offset + 4;
- VALIDATE_NET_BYTES(4);
- if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Rlcreate->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32,
- (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size));
- if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(15))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Rlcreate->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8,
- (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(15));
- return (ssize_t)host_size;
-}
-
-static ssize_t validate_Tsymlink(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
- uint32_t net_offset = 0;
- ssize_t host_size = sizeof(struct lib9p_msg_Tsymlink);
- uint32_t offsetof_size = net_offset + 0;
- uint32_t offsetof_typ = net_offset + 4;
- VALIDATE_NET_BYTES(13);
- VALIDATE_NET_UTF8(LAST_U16LE());
- VALIDATE_NET_BYTES(2);
- VALIDATE_NET_UTF8(LAST_U16LE());
- uint32_t offsetof_end = net_offset + 4;
- VALIDATE_NET_BYTES(4);
- if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Tsymlink->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32,
- (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size));
- if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(16))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Tsymlink->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8,
- (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(16));
- return (ssize_t)host_size;
-}
-
-static ssize_t validate_Rsymlink(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
- uint32_t net_offset = 0;
- ssize_t host_size = sizeof(struct lib9p_msg_Rsymlink);
- uint32_t offsetof_size = net_offset + 0;
- uint32_t offsetof_typ = net_offset + 4;
- uint32_t offsetof_qid_type = net_offset + 7;
- VALIDATE_NET_BYTES(20);
- if (GET_U8LE(offsetof_qid_type) & ~qt_masks[ctx->version])
- return lib9p_errorf(ctx, LINUX_EBADMSG, "unknown bits in qt bitfield: %#02"PRIx8,
- GET_U8LE(offsetof_qid_type) & ~qt_masks[ctx->version]);
- uint32_t offsetof_end = net_offset + 0;
- if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Rsymlink->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32,
- (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size));
- if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(17))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Rsymlink->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8,
- (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(17));
- return (ssize_t)host_size;
-}
-
-static ssize_t validate_Tmknod(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
- uint32_t net_offset = 0;
- ssize_t host_size = sizeof(struct lib9p_msg_Tmknod);
- uint32_t offsetof_size = net_offset + 0;
- uint32_t offsetof_typ = net_offset + 4;
- VALIDATE_NET_BYTES(13);
- VALIDATE_NET_UTF8(LAST_U16LE());
- uint32_t offsetof_mode = net_offset + 0;
- uint32_t offsetof_end = net_offset + 16;
- VALIDATE_NET_BYTES(16);
- if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Tmknod->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32,
- (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size));
- if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(18))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Tmknod->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8,
- (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(18));
- if (GET_U32LE(offsetof_mode) & ~mode_masks[ctx->version])
- return lib9p_errorf(ctx, LINUX_EBADMSG, "unknown bits in mode bitfield: %#08"PRIx32,
- GET_U32LE(offsetof_mode) & ~mode_masks[ctx->version]);
- return (ssize_t)host_size;
-}
-
-static ssize_t validate_Rmknod(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
- uint32_t net_offset = 0;
- ssize_t host_size = sizeof(struct lib9p_msg_Rmknod);
- uint32_t offsetof_size = net_offset + 0;
- uint32_t offsetof_typ = net_offset + 4;
- uint32_t offsetof_qid_type = net_offset + 7;
- VALIDATE_NET_BYTES(20);
- if (GET_U8LE(offsetof_qid_type) & ~qt_masks[ctx->version])
- return lib9p_errorf(ctx, LINUX_EBADMSG, "unknown bits in qt bitfield: %#02"PRIx8,
- GET_U8LE(offsetof_qid_type) & ~qt_masks[ctx->version]);
- uint32_t offsetof_end = net_offset + 0;
- if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Rmknod->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32,
- (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size));
- if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(19))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Rmknod->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8,
- (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(19));
- return (ssize_t)host_size;
-}
-
-static ssize_t validate_Trename(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
- uint32_t net_offset = 0;
- ssize_t host_size = sizeof(struct lib9p_msg_Trename);
- uint32_t offsetof_size = net_offset + 0;
- uint32_t offsetof_typ = net_offset + 4;
- VALIDATE_NET_BYTES(17);
- VALIDATE_NET_UTF8(LAST_U16LE());
- uint32_t offsetof_end = net_offset + 0;
- if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Trename->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32,
- (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size));
- if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(20))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Trename->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8,
- (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(20));
- return (ssize_t)host_size;
-}
-
-static ssize_t validate_Rrename(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
- uint32_t net_offset = 0;
- ssize_t host_size = sizeof(struct lib9p_msg_Rrename);
- uint32_t offsetof_size = net_offset + 0;
- uint32_t offsetof_typ = net_offset + 4;
- uint32_t offsetof_end = net_offset + 7;
- VALIDATE_NET_BYTES(7);
- if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Rrename->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32,
- (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size));
- if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(21))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Rrename->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8,
- (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(21));
- return (ssize_t)host_size;
-}
-
-static ssize_t validate_Treadlink(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
- uint32_t net_offset = 0;
- ssize_t host_size = sizeof(struct lib9p_msg_Treadlink);
- uint32_t offsetof_size = net_offset + 0;
- uint32_t offsetof_typ = net_offset + 4;
- uint32_t offsetof_end = net_offset + 11;
- VALIDATE_NET_BYTES(11);
- if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Treadlink->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32,
- (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size));
- if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(22))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Treadlink->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8,
- (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(22));
- return (ssize_t)host_size;
-}
-
-static ssize_t validate_Rreadlink(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
- uint32_t net_offset = 0;
- ssize_t host_size = sizeof(struct lib9p_msg_Rreadlink);
- uint32_t offsetof_size = net_offset + 0;
- uint32_t offsetof_typ = net_offset + 4;
- VALIDATE_NET_BYTES(9);
- VALIDATE_NET_UTF8(LAST_U16LE());
- uint32_t offsetof_end = net_offset + 0;
- if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Rreadlink->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32,
- (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size));
- if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(23))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Rreadlink->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8,
- (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(23));
- return (ssize_t)host_size;
-}
-
-static ssize_t validate_Tgetattr(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
- uint32_t net_offset = 0;
- ssize_t host_size = sizeof(struct lib9p_msg_Tgetattr);
- uint32_t offsetof_size = net_offset + 0;
- uint32_t offsetof_typ = net_offset + 4;
- uint32_t offsetof_request_mask = net_offset + 11;
- uint32_t offsetof_end = net_offset + 19;
- VALIDATE_NET_BYTES(19);
- if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Tgetattr->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32,
- (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size));
- if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(24))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Tgetattr->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8,
- (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(24));
- if (GET_U64LE(offsetof_request_mask) & ~getattr_masks[ctx->version])
- return lib9p_errorf(ctx, LINUX_EBADMSG, "unknown bits in getattr bitfield: %#016"PRIx64,
- GET_U64LE(offsetof_request_mask) & ~getattr_masks[ctx->version]);
- return (ssize_t)host_size;
-}
-
-static ssize_t validate_Rgetattr(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
- uint32_t net_offset = 0;
- ssize_t host_size = sizeof(struct lib9p_msg_Rgetattr);
- uint32_t offsetof_size = net_offset + 0;
- uint32_t offsetof_typ = net_offset + 4;
- uint32_t offsetof_valid = net_offset + 7;
- uint32_t offsetof_qid_type = net_offset + 15;
- VALIDATE_NET_BYTES(28);
- if (GET_U8LE(offsetof_qid_type) & ~qt_masks[ctx->version])
- return lib9p_errorf(ctx, LINUX_EBADMSG, "unknown bits in qt bitfield: %#02"PRIx8,
- GET_U8LE(offsetof_qid_type) & ~qt_masks[ctx->version]);
- uint32_t offsetof_mode = net_offset + 0;
- uint32_t offsetof_end = net_offset + 132;
- VALIDATE_NET_BYTES(132);
- if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Rgetattr->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32,
- (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size));
- if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(25))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Rgetattr->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8,
- (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(25));
- if (GET_U64LE(offsetof_valid) & ~getattr_masks[ctx->version])
- return lib9p_errorf(ctx, LINUX_EBADMSG, "unknown bits in getattr bitfield: %#016"PRIx64,
- GET_U64LE(offsetof_valid) & ~getattr_masks[ctx->version]);
- if (GET_U32LE(offsetof_mode) & ~mode_masks[ctx->version])
- return lib9p_errorf(ctx, LINUX_EBADMSG, "unknown bits in mode bitfield: %#08"PRIx32,
- GET_U32LE(offsetof_mode) & ~mode_masks[ctx->version]);
- return (ssize_t)host_size;
-}
-
-static ssize_t validate_Tsetattr(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
- uint32_t net_offset = 0;
- ssize_t host_size = sizeof(struct lib9p_msg_Tsetattr);
- uint32_t offsetof_size = net_offset + 0;
- uint32_t offsetof_typ = net_offset + 4;
- uint32_t offsetof_valid = net_offset + 11;
- uint32_t offsetof_mode = net_offset + 15;
- uint32_t offsetof_end = net_offset + 67;
- VALIDATE_NET_BYTES(67);
- if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Tsetattr->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32,
- (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size));
- if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(26))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Tsetattr->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8,
- (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(26));
- if (GET_U32LE(offsetof_valid) & ~setattr_masks[ctx->version])
- return lib9p_errorf(ctx, LINUX_EBADMSG, "unknown bits in setattr bitfield: %#08"PRIx32,
- GET_U32LE(offsetof_valid) & ~setattr_masks[ctx->version]);
- if (GET_U32LE(offsetof_mode) & ~mode_masks[ctx->version])
- return lib9p_errorf(ctx, LINUX_EBADMSG, "unknown bits in mode bitfield: %#08"PRIx32,
- GET_U32LE(offsetof_mode) & ~mode_masks[ctx->version]);
- return (ssize_t)host_size;
-}
-
-static ssize_t validate_Rsetattr(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
- uint32_t net_offset = 0;
- ssize_t host_size = sizeof(struct lib9p_msg_Rsetattr);
- uint32_t offsetof_size = net_offset + 0;
- uint32_t offsetof_typ = net_offset + 4;
- uint32_t offsetof_end = net_offset + 7;
- VALIDATE_NET_BYTES(7);
- if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Rsetattr->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32,
- (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size));
- if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(27))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Rsetattr->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8,
- (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(27));
- return (ssize_t)host_size;
-}
-
-static ssize_t validate_Txattrwalk(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
- uint32_t net_offset = 0;
- ssize_t host_size = sizeof(struct lib9p_msg_Txattrwalk);
- uint32_t offsetof_size = net_offset + 0;
- uint32_t offsetof_typ = net_offset + 4;
- VALIDATE_NET_BYTES(17);
- VALIDATE_NET_UTF8(LAST_U16LE());
- uint32_t offsetof_end = net_offset + 0;
- if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Txattrwalk->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32,
- (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size));
- if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(30))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Txattrwalk->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8,
- (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(30));
- return (ssize_t)host_size;
-}
-
-static ssize_t validate_Rxattrwalk(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
- uint32_t net_offset = 0;
- ssize_t host_size = sizeof(struct lib9p_msg_Rxattrwalk);
- uint32_t offsetof_size = net_offset + 0;
- uint32_t offsetof_typ = net_offset + 4;
- uint32_t offsetof_end = net_offset + 15;
- VALIDATE_NET_BYTES(15);
- if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Rxattrwalk->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32,
- (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size));
- if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(31))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Rxattrwalk->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8,
- (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(31));
- return (ssize_t)host_size;
-}
-
-static ssize_t validate_Txattrcreate(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
- uint32_t net_offset = 0;
- ssize_t host_size = sizeof(struct lib9p_msg_Txattrcreate);
- uint32_t offsetof_size = net_offset + 0;
- uint32_t offsetof_typ = net_offset + 4;
- VALIDATE_NET_BYTES(13);
- VALIDATE_NET_UTF8(LAST_U16LE());
- uint32_t offsetof_end = net_offset + 12;
- VALIDATE_NET_BYTES(12);
- if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Txattrcreate->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32,
- (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size));
- if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(32))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Txattrcreate->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8,
- (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(32));
- return (ssize_t)host_size;
-}
-
-static ssize_t validate_Rxattrcreate(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
- uint32_t net_offset = 0;
- ssize_t host_size = sizeof(struct lib9p_msg_Rxattrcreate);
- uint32_t offsetof_size = net_offset + 0;
- uint32_t offsetof_typ = net_offset + 4;
- uint32_t offsetof_end = net_offset + 7;
- VALIDATE_NET_BYTES(7);
- if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Rxattrcreate->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32,
- (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size));
- if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(33))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Rxattrcreate->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8,
- (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(33));
- return (ssize_t)host_size;
-}
-
-static ssize_t validate_Treaddir(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
- uint32_t net_offset = 0;
- ssize_t host_size = sizeof(struct lib9p_msg_Treaddir);
- uint32_t offsetof_size = net_offset + 0;
- uint32_t offsetof_typ = net_offset + 4;
- uint32_t offsetof_end = net_offset + 23;
- VALIDATE_NET_BYTES(23);
- if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Treaddir->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32,
- (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size));
- if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(40))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Treaddir->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8,
- (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(40));
- return (ssize_t)host_size;
-}
-
-static ssize_t validate_Rreaddir(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
- uint32_t net_offset = 0;
- ssize_t host_size = sizeof(struct lib9p_msg_Rreaddir);
- uint32_t offsetof_size = net_offset + 0;
- uint32_t offsetof_typ = net_offset + 4;
- VALIDATE_NET_BYTES(11);
- VALIDATE_NET_BYTES(LAST_U32LE());
- uint32_t offsetof_end = net_offset + 0;
- if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Rreaddir->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32,
- (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size));
- if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(41))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Rreaddir->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8,
- (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(41));
- return (ssize_t)host_size;
-}
-
-static ssize_t validate_Tfsync(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
- uint32_t net_offset = 0;
- ssize_t host_size = sizeof(struct lib9p_msg_Tfsync);
- uint32_t offsetof_size = net_offset + 0;
- uint32_t offsetof_typ = net_offset + 4;
- uint32_t offsetof_end = net_offset + 15;
- VALIDATE_NET_BYTES(15);
- if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Tfsync->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32,
- (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size));
- if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(50))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Tfsync->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8,
- (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(50));
- return (ssize_t)host_size;
-}
-
-static ssize_t validate_Rfsync(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
- uint32_t net_offset = 0;
- ssize_t host_size = sizeof(struct lib9p_msg_Rfsync);
- uint32_t offsetof_size = net_offset + 0;
- uint32_t offsetof_typ = net_offset + 4;
- uint32_t offsetof_end = net_offset + 7;
- VALIDATE_NET_BYTES(7);
- if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Rfsync->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32,
- (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size));
- if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(51))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Rfsync->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8,
- (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(51));
- return (ssize_t)host_size;
-}
-
-static ssize_t validate_Tlock(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
- uint32_t net_offset = 0;
- ssize_t host_size = sizeof(struct lib9p_msg_Tlock);
- uint32_t offsetof_size = net_offset + 0;
- uint32_t offsetof_typ = net_offset + 4;
- uint32_t offsetof_flags = net_offset + 12;
- VALIDATE_NET_BYTES(38);
- VALIDATE_NET_UTF8(LAST_U16LE());
- uint32_t offsetof_end = net_offset + 0;
- if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Tlock->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32,
- (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size));
- if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(52))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Tlock->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8,
- (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(52));
- if (GET_U32LE(offsetof_flags) & ~lock_flags_masks[ctx->version])
- return lib9p_errorf(ctx, LINUX_EBADMSG, "unknown bits in lock_flags bitfield: %#08"PRIx32,
- GET_U32LE(offsetof_flags) & ~lock_flags_masks[ctx->version]);
- return (ssize_t)host_size;
-}
-
-static ssize_t validate_Rlock(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
- uint32_t net_offset = 0;
- ssize_t host_size = sizeof(struct lib9p_msg_Rlock);
- uint32_t offsetof_size = net_offset + 0;
- uint32_t offsetof_typ = net_offset + 4;
- uint32_t offsetof_end = net_offset + 8;
- VALIDATE_NET_BYTES(8);
- if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Rlock->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32,
- (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size));
- if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(53))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Rlock->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8,
- (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(53));
- return (ssize_t)host_size;
-}
-
-static ssize_t validate_Tgetlock(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
- uint32_t net_offset = 0;
- ssize_t host_size = sizeof(struct lib9p_msg_Tgetlock);
- uint32_t offsetof_size = net_offset + 0;
- uint32_t offsetof_typ = net_offset + 4;
- VALIDATE_NET_BYTES(34);
- VALIDATE_NET_UTF8(LAST_U16LE());
- uint32_t offsetof_end = net_offset + 0;
- if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Tgetlock->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32,
- (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size));
- if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(54))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Tgetlock->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8,
- (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(54));
- return (ssize_t)host_size;
-}
-
-static ssize_t validate_Rgetlock(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
- uint32_t net_offset = 0;
- ssize_t host_size = sizeof(struct lib9p_msg_Rgetlock);
- uint32_t offsetof_size = net_offset + 0;
- uint32_t offsetof_typ = net_offset + 4;
- VALIDATE_NET_BYTES(30);
- VALIDATE_NET_UTF8(LAST_U16LE());
- uint32_t offsetof_end = net_offset + 0;
- if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Rgetlock->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32,
- (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size));
- if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(55))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Rgetlock->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8,
- (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(55));
- return (ssize_t)host_size;
-}
-
-static ssize_t validate_Tlink(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
- uint32_t net_offset = 0;
- ssize_t host_size = sizeof(struct lib9p_msg_Tlink);
- uint32_t offsetof_size = net_offset + 0;
- uint32_t offsetof_typ = net_offset + 4;
- VALIDATE_NET_BYTES(17);
- VALIDATE_NET_UTF8(LAST_U16LE());
- uint32_t offsetof_end = net_offset + 0;
- if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Tlink->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32,
- (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size));
- if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(70))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Tlink->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8,
- (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(70));
- return (ssize_t)host_size;
-}
-
-static ssize_t validate_Rlink(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
- uint32_t net_offset = 0;
- ssize_t host_size = sizeof(struct lib9p_msg_Rlink);
- uint32_t offsetof_size = net_offset + 0;
- uint32_t offsetof_typ = net_offset + 4;
- uint32_t offsetof_end = net_offset + 7;
- VALIDATE_NET_BYTES(7);
- if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Rlink->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32,
- (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size));
- if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(71))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Rlink->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8,
- (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(71));
- return (ssize_t)host_size;
-}
-
-static ssize_t validate_Tmkdir(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
- uint32_t net_offset = 0;
- ssize_t host_size = sizeof(struct lib9p_msg_Tmkdir);
- uint32_t offsetof_size = net_offset + 0;
- uint32_t offsetof_typ = net_offset + 4;
- VALIDATE_NET_BYTES(13);
- VALIDATE_NET_UTF8(LAST_U16LE());
- uint32_t offsetof_mode = net_offset + 0;
- uint32_t offsetof_end = net_offset + 8;
- VALIDATE_NET_BYTES(8);
- if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Tmkdir->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32,
- (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size));
- if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(72))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Tmkdir->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8,
- (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(72));
- if (GET_U32LE(offsetof_mode) & ~mode_masks[ctx->version])
- return lib9p_errorf(ctx, LINUX_EBADMSG, "unknown bits in mode bitfield: %#08"PRIx32,
- GET_U32LE(offsetof_mode) & ~mode_masks[ctx->version]);
- return (ssize_t)host_size;
-}
-
-static ssize_t validate_Rmkdir(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
- uint32_t net_offset = 0;
- ssize_t host_size = sizeof(struct lib9p_msg_Rmkdir);
- uint32_t offsetof_size = net_offset + 0;
- uint32_t offsetof_typ = net_offset + 4;
- uint32_t offsetof_qid_type = net_offset + 7;
- VALIDATE_NET_BYTES(20);
- if (GET_U8LE(offsetof_qid_type) & ~qt_masks[ctx->version])
- return lib9p_errorf(ctx, LINUX_EBADMSG, "unknown bits in qt bitfield: %#02"PRIx8,
- GET_U8LE(offsetof_qid_type) & ~qt_masks[ctx->version]);
- uint32_t offsetof_end = net_offset + 0;
- if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Rmkdir->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32,
- (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size));
- if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(73))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Rmkdir->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8,
- (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(73));
- return (ssize_t)host_size;
-}
-
-static ssize_t validate_Trenameat(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
- uint32_t net_offset = 0;
- ssize_t host_size = sizeof(struct lib9p_msg_Trenameat);
- uint32_t offsetof_size = net_offset + 0;
- uint32_t offsetof_typ = net_offset + 4;
- VALIDATE_NET_BYTES(13);
- VALIDATE_NET_UTF8(LAST_U16LE());
- VALIDATE_NET_BYTES(6);
- VALIDATE_NET_UTF8(LAST_U16LE());
- uint32_t offsetof_end = net_offset + 0;
- if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Trenameat->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32,
- (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size));
- if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(74))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Trenameat->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8,
- (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(74));
- return (ssize_t)host_size;
-}
-
-static ssize_t validate_Rrenameat(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
- uint32_t net_offset = 0;
- ssize_t host_size = sizeof(struct lib9p_msg_Rrenameat);
- uint32_t offsetof_size = net_offset + 0;
- uint32_t offsetof_typ = net_offset + 4;
- uint32_t offsetof_end = net_offset + 7;
- VALIDATE_NET_BYTES(7);
- if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Rrenameat->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32,
- (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size));
- if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(75))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Rrenameat->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8,
- (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(75));
- return (ssize_t)host_size;
-}
-
-static ssize_t validate_Tunlinkat(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
- uint32_t net_offset = 0;
- ssize_t host_size = sizeof(struct lib9p_msg_Tunlinkat);
- uint32_t offsetof_size = net_offset + 0;
- uint32_t offsetof_typ = net_offset + 4;
- VALIDATE_NET_BYTES(13);
- VALIDATE_NET_UTF8(LAST_U16LE());
- uint32_t offsetof_end = net_offset + 4;
- VALIDATE_NET_BYTES(4);
- if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Tunlinkat->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32,
- (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size));
- if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(76))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Tunlinkat->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8,
- (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(76));
- return (ssize_t)host_size;
-}
-
-static ssize_t validate_Runlinkat(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
- uint32_t net_offset = 0;
- ssize_t host_size = sizeof(struct lib9p_msg_Runlinkat);
- uint32_t offsetof_size = net_offset + 0;
- uint32_t offsetof_typ = net_offset + 4;
- uint32_t offsetof_end = net_offset + 7;
- VALIDATE_NET_BYTES(7);
- if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Runlinkat->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32,
- (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size));
- if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(77))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Runlinkat->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8,
- (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(77));
- return (ssize_t)host_size;
-}
-
-#endif /* CONFIG_9P_ENABLE_9P2000_L */
-#if CONFIG_9P_ENABLE_9P2000_e
-static ssize_t validate_Tsession(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
- uint32_t net_offset = 0;
- ssize_t host_size = sizeof(struct lib9p_msg_Tsession);
- uint32_t offsetof_size = net_offset + 0;
- uint32_t offsetof_typ = net_offset + 4;
- uint32_t offsetof_end = net_offset + 15;
- VALIDATE_NET_BYTES(15);
- if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Tsession->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32,
- (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size));
- if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(150))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Tsession->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8,
- (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(150));
- return (ssize_t)host_size;
-}
-
-static ssize_t validate_Rsession(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
- uint32_t net_offset = 0;
- ssize_t host_size = sizeof(struct lib9p_msg_Rsession);
- uint32_t offsetof_size = net_offset + 0;
- uint32_t offsetof_typ = net_offset + 4;
- uint32_t offsetof_end = net_offset + 7;
- VALIDATE_NET_BYTES(7);
- if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Rsession->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32,
- (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size));
- if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(151))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Rsession->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8,
- (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(151));
- return (ssize_t)host_size;
-}
-
-static ssize_t validate_Tsread(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
- uint32_t net_offset = 0;
- ssize_t host_size = sizeof(struct lib9p_msg_Tsread);
- uint32_t offsetof_size = net_offset + 0;
- uint32_t offsetof_typ = net_offset + 4;
- VALIDATE_NET_BYTES(13);
- for (uint16_t i = 0, cnt = LAST_U16LE(); i < cnt; i++) {
- RESERVE_HOST_BYTES(sizeof(struct lib9p_s));
- VALIDATE_NET_BYTES(2);
- VALIDATE_NET_UTF8(LAST_U16LE());
- }
- uint32_t offsetof_end = net_offset + 0;
- if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Tsread->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32,
- (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size));
- if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(152))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Tsread->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8,
- (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(152));
- return (ssize_t)host_size;
-}
-
-static ssize_t validate_Rsread(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
- uint32_t net_offset = 0;
- ssize_t host_size = sizeof(struct lib9p_msg_Rsread);
- uint32_t offsetof_size = net_offset + 0;
- uint32_t offsetof_typ = net_offset + 4;
- VALIDATE_NET_BYTES(11);
- VALIDATE_NET_BYTES(LAST_U32LE());
- uint32_t offsetof_end = net_offset + 0;
- if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Rsread->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32,
- (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size));
- if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(153))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Rsread->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8,
- (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(153));
- return (ssize_t)host_size;
-}
-
-static ssize_t validate_Tswrite(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
- uint32_t net_offset = 0;
- ssize_t host_size = sizeof(struct lib9p_msg_Tswrite);
- uint32_t offsetof_size = net_offset + 0;
- uint32_t offsetof_typ = net_offset + 4;
- VALIDATE_NET_BYTES(13);
- for (uint16_t i = 0, cnt = LAST_U16LE(); i < cnt; i++) {
- RESERVE_HOST_BYTES(sizeof(struct lib9p_s));
- VALIDATE_NET_BYTES(2);
- VALIDATE_NET_UTF8(LAST_U16LE());
- }
- VALIDATE_NET_BYTES(4);
- VALIDATE_NET_BYTES(LAST_U32LE());
- uint32_t offsetof_end = net_offset + 0;
- if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Tswrite->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32,
- (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size));
- if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(154))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Tswrite->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8,
- (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(154));
- return (ssize_t)host_size;
-}
-
-static ssize_t validate_Rswrite(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
- uint32_t net_offset = 0;
- ssize_t host_size = sizeof(struct lib9p_msg_Rswrite);
- uint32_t offsetof_size = net_offset + 0;
- uint32_t offsetof_typ = net_offset + 4;
- uint32_t offsetof_end = net_offset + 11;
- VALIDATE_NET_BYTES(11);
- if ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Rswrite->size value is wrong: actual: %"PRIu32" != correct:%"PRIu32,
- (uint32_t)GET_U32LE(offsetof_size), (uint32_t)(offsetof_end - offsetof_size));
- if ((uint8_t)GET_U8LE(offsetof_typ) != (uint8_t)(155))
- return lib9p_errorf(ctx, LINUX_EBADMSG, "Rswrite->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8,
- (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(155));
- return (ssize_t)host_size;
-}
-#endif /* CONFIG_9P_ENABLE_9P2000_e */
-
-/* unmarshal_* ****************************************************************/
-
-#define UNMARSHAL_BYTES(ctx, data_lvalue, len) \
- data_lvalue = (char *)&net_bytes[net_offset]; \
- net_offset += len;
-#define UNMARSHAL_U8LE(ctx, val_lvalue) \
- val_lvalue = net_bytes[net_offset]; \
- net_offset += 1;
-#define UNMARSHAL_U16LE(ctx, val_lvalue) \
- val_lvalue = uint16le_decode(&net_bytes[net_offset]); \
- net_offset += 2;
-#define UNMARSHAL_U32LE(ctx, val_lvalue) \
- val_lvalue = uint32le_decode(&net_bytes[net_offset]); \
- net_offset += 4;
-#define UNMARSHAL_U64LE(ctx, val_lvalue) \
- val_lvalue = uint64le_decode(&net_bytes[net_offset]); \
- net_offset += 8;
-
-#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u
-static void unmarshal_stat([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
- struct lib9p_stat *out = out_buf;
- [[gnu::unused]] void *extra = &out[1];
- uint32_t net_offset = 0;
- net_offset += 2;
- UNMARSHAL_U16LE(ctx, out->kern_type);
- UNMARSHAL_U32LE(ctx, out->kern_dev);
- UNMARSHAL_U8LE(ctx, out->file_qid.type);
- UNMARSHAL_U32LE(ctx, out->file_qid.vers);
- UNMARSHAL_U64LE(ctx, out->file_qid.path);
- UNMARSHAL_U32LE(ctx, out->file_mode);
- UNMARSHAL_U32LE(ctx, out->file_atime);
- UNMARSHAL_U32LE(ctx, out->file_mtime);
- UNMARSHAL_U64LE(ctx, out->file_size);
- UNMARSHAL_U16LE(ctx, out->file_name.len);
- UNMARSHAL_BYTES(ctx, out->file_name.utf8, out->file_name.len);
- UNMARSHAL_U16LE(ctx, out->file_owner_uid.len);
- UNMARSHAL_BYTES(ctx, out->file_owner_uid.utf8, out->file_owner_uid.len);
- UNMARSHAL_U16LE(ctx, out->file_owner_gid.len);
- UNMARSHAL_BYTES(ctx, out->file_owner_gid.utf8, out->file_owner_gid.len);
- UNMARSHAL_U16LE(ctx, out->file_last_modified_uid.len);
- UNMARSHAL_BYTES(ctx, out->file_last_modified_uid.utf8, out->file_last_modified_uid.len);
-#if CONFIG_9P_ENABLE_9P2000_u
- if (is_ver(ctx, 9P2000_u)) {
- UNMARSHAL_U16LE(ctx, out->file_extension.len);
- UNMARSHAL_BYTES(ctx, out->file_extension.utf8, out->file_extension.len);
- UNMARSHAL_U32LE(ctx, out->file_owner_n_uid);
- UNMARSHAL_U32LE(ctx, out->file_owner_n_gid);
- UNMARSHAL_U32LE(ctx, out->file_last_modified_n_uid);
- }
-#endif /* CONFIG_9P_ENABLE_9P2000_u */
-}
-
-#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */
-#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u
-static void unmarshal_Tversion([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
- struct lib9p_msg_Tversion *out = out_buf;
- [[gnu::unused]] void *extra = &out[1];
- uint32_t net_offset = 0;
- net_offset += 4;
- net_offset += 1;
- UNMARSHAL_U16LE(ctx, out->tag);
- UNMARSHAL_U32LE(ctx, out->max_msg_size);
- UNMARSHAL_U16LE(ctx, out->version.len);
- UNMARSHAL_BYTES(ctx, out->version.utf8, out->version.len);
-}
-
-static void unmarshal_Rversion([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
- struct lib9p_msg_Rversion *out = out_buf;
- [[gnu::unused]] void *extra = &out[1];
- uint32_t net_offset = 0;
- net_offset += 4;
- net_offset += 1;
- UNMARSHAL_U16LE(ctx, out->tag);
- UNMARSHAL_U32LE(ctx, out->max_msg_size);
- UNMARSHAL_U16LE(ctx, out->version.len);
- UNMARSHAL_BYTES(ctx, out->version.utf8, out->version.len);
-}
-
-static void unmarshal_Tauth([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
- struct lib9p_msg_Tauth *out = out_buf;
- [[gnu::unused]] void *extra = &out[1];
- uint32_t net_offset = 0;
- net_offset += 4;
- net_offset += 1;
- UNMARSHAL_U16LE(ctx, out->tag);
- UNMARSHAL_U32LE(ctx, out->afid);
- UNMARSHAL_U16LE(ctx, out->uname.len);
- UNMARSHAL_BYTES(ctx, out->uname.utf8, out->uname.len);
- UNMARSHAL_U16LE(ctx, out->aname.len);
- UNMARSHAL_BYTES(ctx, out->aname.utf8, out->aname.len);
-#if CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_u
- if (( is_ver(ctx, 9P2000_L) || is_ver(ctx, 9P2000_u) )) {
- UNMARSHAL_U32LE(ctx, out->n_uid);
- }
-#endif /* CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_u */
-}
-
-static void unmarshal_Rauth([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
- struct lib9p_msg_Rauth *out = out_buf;
- [[gnu::unused]] void *extra = &out[1];
- uint32_t net_offset = 0;
- net_offset += 4;
- net_offset += 1;
- UNMARSHAL_U16LE(ctx, out->tag);
- UNMARSHAL_U8LE(ctx, out->aqid.type);
- UNMARSHAL_U32LE(ctx, out->aqid.vers);
- UNMARSHAL_U64LE(ctx, out->aqid.path);
-}
-
-static void unmarshal_Tattach([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
- struct lib9p_msg_Tattach *out = out_buf;
- [[gnu::unused]] void *extra = &out[1];
- uint32_t net_offset = 0;
- net_offset += 4;
- net_offset += 1;
- UNMARSHAL_U16LE(ctx, out->tag);
- UNMARSHAL_U32LE(ctx, out->fid);
- UNMARSHAL_U32LE(ctx, out->afid);
- UNMARSHAL_U16LE(ctx, out->uname.len);
- UNMARSHAL_BYTES(ctx, out->uname.utf8, out->uname.len);
- UNMARSHAL_U16LE(ctx, out->aname.len);
- UNMARSHAL_BYTES(ctx, out->aname.utf8, out->aname.len);
-#if CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_u
- if (( is_ver(ctx, 9P2000_L) || is_ver(ctx, 9P2000_u) )) {
- UNMARSHAL_U32LE(ctx, out->n_uid);
- }
-#endif /* CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_u */
-}
-
-static void unmarshal_Rattach([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
- struct lib9p_msg_Rattach *out = out_buf;
- [[gnu::unused]] void *extra = &out[1];
- uint32_t net_offset = 0;
- net_offset += 4;
- net_offset += 1;
- UNMARSHAL_U16LE(ctx, out->tag);
- UNMARSHAL_U8LE(ctx, out->qid.type);
- UNMARSHAL_U32LE(ctx, out->qid.vers);
- UNMARSHAL_U64LE(ctx, out->qid.path);
-}
-
-static void unmarshal_Rerror([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
- struct lib9p_msg_Rerror *out = out_buf;
- [[gnu::unused]] void *extra = &out[1];
- uint32_t net_offset = 0;
- net_offset += 4;
- net_offset += 1;
- UNMARSHAL_U16LE(ctx, out->tag);
- UNMARSHAL_U16LE(ctx, out->ename.len);
- UNMARSHAL_BYTES(ctx, out->ename.utf8, out->ename.len);
-#if CONFIG_9P_ENABLE_9P2000_u
- if (is_ver(ctx, 9P2000_u)) {
- UNMARSHAL_U32LE(ctx, out->errno);
- }
-#endif /* CONFIG_9P_ENABLE_9P2000_u */
-}
-
-static void unmarshal_Tflush([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
- struct lib9p_msg_Tflush *out = out_buf;
- [[gnu::unused]] void *extra = &out[1];
- uint32_t net_offset = 0;
- net_offset += 4;
- net_offset += 1;
- UNMARSHAL_U16LE(ctx, out->tag);
- UNMARSHAL_U16LE(ctx, out->oldtag);
-}
-
-static void unmarshal_Rflush([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
- struct lib9p_msg_Rflush *out = out_buf;
- [[gnu::unused]] void *extra = &out[1];
- uint32_t net_offset = 0;
- net_offset += 4;
- net_offset += 1;
- UNMARSHAL_U16LE(ctx, out->tag);
-}
-
-static void unmarshal_Twalk([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
- struct lib9p_msg_Twalk *out = out_buf;
- [[gnu::unused]] void *extra = &out[1];
- uint32_t net_offset = 0;
- net_offset += 4;
- net_offset += 1;
- UNMARSHAL_U16LE(ctx, out->tag);
- UNMARSHAL_U32LE(ctx, out->fid);
- UNMARSHAL_U32LE(ctx, out->newfid);
- UNMARSHAL_U16LE(ctx, out->nwname);
- out->wname = extra;
- extra += sizeof(out->wname[0]) * out->nwname;
- for (uint16_t i = 0; i < out->nwname; i++) {
- UNMARSHAL_U16LE(ctx, out->wname[i].len);
- UNMARSHAL_BYTES(ctx, out->wname[i].utf8, out->wname[i].len);
- }
-}
-
-static void unmarshal_Rwalk([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
- struct lib9p_msg_Rwalk *out = out_buf;
- [[gnu::unused]] void *extra = &out[1];
- uint32_t net_offset = 0;
- net_offset += 4;
- net_offset += 1;
- UNMARSHAL_U16LE(ctx, out->tag);
- UNMARSHAL_U16LE(ctx, out->nwqid);
- out->wqid = extra;
- extra += sizeof(out->wqid[0]) * out->nwqid;
- for (uint16_t i = 0; i < out->nwqid; i++) {
- UNMARSHAL_U8LE(ctx, out->wqid[i].type);
- UNMARSHAL_U32LE(ctx, out->wqid[i].vers);
- UNMARSHAL_U64LE(ctx, out->wqid[i].path);
- }
-}
-
-#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */
-#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u
-static void unmarshal_Topen([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
- struct lib9p_msg_Topen *out = out_buf;
- [[gnu::unused]] void *extra = &out[1];
- uint32_t net_offset = 0;
- net_offset += 4;
- net_offset += 1;
- UNMARSHAL_U16LE(ctx, out->tag);
- UNMARSHAL_U32LE(ctx, out->fid);
- UNMARSHAL_U8LE(ctx, out->mode);
-}
-
-static void unmarshal_Ropen([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
- struct lib9p_msg_Ropen *out = out_buf;
- [[gnu::unused]] void *extra = &out[1];
- uint32_t net_offset = 0;
- net_offset += 4;
- net_offset += 1;
- UNMARSHAL_U16LE(ctx, out->tag);
- UNMARSHAL_U8LE(ctx, out->qid.type);
- UNMARSHAL_U32LE(ctx, out->qid.vers);
- UNMARSHAL_U64LE(ctx, out->qid.path);
- UNMARSHAL_U32LE(ctx, out->iounit);
-}
-
-static void unmarshal_Tcreate([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
- struct lib9p_msg_Tcreate *out = out_buf;
- [[gnu::unused]] void *extra = &out[1];
- uint32_t net_offset = 0;
- net_offset += 4;
- net_offset += 1;
- UNMARSHAL_U16LE(ctx, out->tag);
- UNMARSHAL_U32LE(ctx, out->fid);
- UNMARSHAL_U16LE(ctx, out->name.len);
- UNMARSHAL_BYTES(ctx, out->name.utf8, out->name.len);
- UNMARSHAL_U32LE(ctx, out->perm);
- UNMARSHAL_U8LE(ctx, out->mode);
-}
-
-static void unmarshal_Rcreate([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
- struct lib9p_msg_Rcreate *out = out_buf;
- [[gnu::unused]] void *extra = &out[1];
- uint32_t net_offset = 0;
- net_offset += 4;
- net_offset += 1;
- UNMARSHAL_U16LE(ctx, out->tag);
- UNMARSHAL_U8LE(ctx, out->qid.type);
- UNMARSHAL_U32LE(ctx, out->qid.vers);
- UNMARSHAL_U64LE(ctx, out->qid.path);
- UNMARSHAL_U32LE(ctx, out->iounit);
-}
-
-#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */
-#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u
-static void unmarshal_Tread([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
- struct lib9p_msg_Tread *out = out_buf;
- [[gnu::unused]] void *extra = &out[1];
- uint32_t net_offset = 0;
- net_offset += 4;
- net_offset += 1;
- UNMARSHAL_U16LE(ctx, out->tag);
- UNMARSHAL_U32LE(ctx, out->fid);
- UNMARSHAL_U64LE(ctx, out->offset);
- UNMARSHAL_U32LE(ctx, out->count);
-}
-
-static void unmarshal_Rread([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
- struct lib9p_msg_Rread *out = out_buf;
- [[gnu::unused]] void *extra = &out[1];
- uint32_t net_offset = 0;
- net_offset += 4;
- net_offset += 1;
- UNMARSHAL_U16LE(ctx, out->tag);
- UNMARSHAL_U32LE(ctx, out->count);
- UNMARSHAL_BYTES(ctx, out->data, out->count);
-}
-
-static void unmarshal_Twrite([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
- struct lib9p_msg_Twrite *out = out_buf;
- [[gnu::unused]] void *extra = &out[1];
- uint32_t net_offset = 0;
- net_offset += 4;
- net_offset += 1;
- UNMARSHAL_U16LE(ctx, out->tag);
- UNMARSHAL_U32LE(ctx, out->fid);
- UNMARSHAL_U64LE(ctx, out->offset);
- UNMARSHAL_U32LE(ctx, out->count);
- UNMARSHAL_BYTES(ctx, out->data, out->count);
-}
-
-static void unmarshal_Rwrite([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
- struct lib9p_msg_Rwrite *out = out_buf;
- [[gnu::unused]] void *extra = &out[1];
- uint32_t net_offset = 0;
- net_offset += 4;
- net_offset += 1;
- UNMARSHAL_U16LE(ctx, out->tag);
- UNMARSHAL_U32LE(ctx, out->count);
-}
-
-static void unmarshal_Tclunk([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
- struct lib9p_msg_Tclunk *out = out_buf;
- [[gnu::unused]] void *extra = &out[1];
- uint32_t net_offset = 0;
- net_offset += 4;
- net_offset += 1;
- UNMARSHAL_U16LE(ctx, out->tag);
- UNMARSHAL_U32LE(ctx, out->fid);
-}
-
-static void unmarshal_Rclunk([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
- struct lib9p_msg_Rclunk *out = out_buf;
- [[gnu::unused]] void *extra = &out[1];
- uint32_t net_offset = 0;
- net_offset += 4;
- net_offset += 1;
- UNMARSHAL_U16LE(ctx, out->tag);
-}
-
-static void unmarshal_Tremove([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
- struct lib9p_msg_Tremove *out = out_buf;
- [[gnu::unused]] void *extra = &out[1];
- uint32_t net_offset = 0;
- net_offset += 4;
- net_offset += 1;
- UNMARSHAL_U16LE(ctx, out->tag);
- UNMARSHAL_U32LE(ctx, out->fid);
-}
-
-static void unmarshal_Rremove([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
- struct lib9p_msg_Rremove *out = out_buf;
- [[gnu::unused]] void *extra = &out[1];
- uint32_t net_offset = 0;
- net_offset += 4;
- net_offset += 1;
- UNMARSHAL_U16LE(ctx, out->tag);
-}
-
-#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */
-#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u
-static void unmarshal_Tstat([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
- struct lib9p_msg_Tstat *out = out_buf;
- [[gnu::unused]] void *extra = &out[1];
- uint32_t net_offset = 0;
- net_offset += 4;
- net_offset += 1;
- UNMARSHAL_U16LE(ctx, out->tag);
- UNMARSHAL_U32LE(ctx, out->fid);
-}
-
-static void unmarshal_Rstat([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
- struct lib9p_msg_Rstat *out = out_buf;
- [[gnu::unused]] void *extra = &out[1];
- uint32_t net_offset = 0;
- net_offset += 4;
- net_offset += 1;
- UNMARSHAL_U16LE(ctx, out->tag);
- net_offset += 2;
- net_offset += 2;
- UNMARSHAL_U16LE(ctx, out->stat.kern_type);
- UNMARSHAL_U32LE(ctx, out->stat.kern_dev);
- UNMARSHAL_U8LE(ctx, out->stat.file_qid.type);
- UNMARSHAL_U32LE(ctx, out->stat.file_qid.vers);
- UNMARSHAL_U64LE(ctx, out->stat.file_qid.path);
- UNMARSHAL_U32LE(ctx, out->stat.file_mode);
- UNMARSHAL_U32LE(ctx, out->stat.file_atime);
- UNMARSHAL_U32LE(ctx, out->stat.file_mtime);
- UNMARSHAL_U64LE(ctx, out->stat.file_size);
- UNMARSHAL_U16LE(ctx, out->stat.file_name.len);
- UNMARSHAL_BYTES(ctx, out->stat.file_name.utf8, out->stat.file_name.len);
- UNMARSHAL_U16LE(ctx, out->stat.file_owner_uid.len);
- UNMARSHAL_BYTES(ctx, out->stat.file_owner_uid.utf8, out->stat.file_owner_uid.len);
- UNMARSHAL_U16LE(ctx, out->stat.file_owner_gid.len);
- UNMARSHAL_BYTES(ctx, out->stat.file_owner_gid.utf8, out->stat.file_owner_gid.len);
- UNMARSHAL_U16LE(ctx, out->stat.file_last_modified_uid.len);
- UNMARSHAL_BYTES(ctx, out->stat.file_last_modified_uid.utf8, out->stat.file_last_modified_uid.len);
-#if CONFIG_9P_ENABLE_9P2000_u
- if (is_ver(ctx, 9P2000_u)) {
- UNMARSHAL_U16LE(ctx, out->stat.file_extension.len);
- UNMARSHAL_BYTES(ctx, out->stat.file_extension.utf8, out->stat.file_extension.len);
- UNMARSHAL_U32LE(ctx, out->stat.file_owner_n_uid);
- UNMARSHAL_U32LE(ctx, out->stat.file_owner_n_gid);
- UNMARSHAL_U32LE(ctx, out->stat.file_last_modified_n_uid);
- }
-#endif /* CONFIG_9P_ENABLE_9P2000_u */
-}
-
-static void unmarshal_Twstat([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
- struct lib9p_msg_Twstat *out = out_buf;
- [[gnu::unused]] void *extra = &out[1];
- uint32_t net_offset = 0;
- net_offset += 4;
- net_offset += 1;
- UNMARSHAL_U16LE(ctx, out->tag);
- UNMARSHAL_U32LE(ctx, out->fid);
- net_offset += 2;
- net_offset += 2;
- UNMARSHAL_U16LE(ctx, out->stat.kern_type);
- UNMARSHAL_U32LE(ctx, out->stat.kern_dev);
- UNMARSHAL_U8LE(ctx, out->stat.file_qid.type);
- UNMARSHAL_U32LE(ctx, out->stat.file_qid.vers);
- UNMARSHAL_U64LE(ctx, out->stat.file_qid.path);
- UNMARSHAL_U32LE(ctx, out->stat.file_mode);
- UNMARSHAL_U32LE(ctx, out->stat.file_atime);
- UNMARSHAL_U32LE(ctx, out->stat.file_mtime);
- UNMARSHAL_U64LE(ctx, out->stat.file_size);
- UNMARSHAL_U16LE(ctx, out->stat.file_name.len);
- UNMARSHAL_BYTES(ctx, out->stat.file_name.utf8, out->stat.file_name.len);
- UNMARSHAL_U16LE(ctx, out->stat.file_owner_uid.len);
- UNMARSHAL_BYTES(ctx, out->stat.file_owner_uid.utf8, out->stat.file_owner_uid.len);
- UNMARSHAL_U16LE(ctx, out->stat.file_owner_gid.len);
- UNMARSHAL_BYTES(ctx, out->stat.file_owner_gid.utf8, out->stat.file_owner_gid.len);
- UNMARSHAL_U16LE(ctx, out->stat.file_last_modified_uid.len);
- UNMARSHAL_BYTES(ctx, out->stat.file_last_modified_uid.utf8, out->stat.file_last_modified_uid.len);
-#if CONFIG_9P_ENABLE_9P2000_u
- if (is_ver(ctx, 9P2000_u)) {
- UNMARSHAL_U16LE(ctx, out->stat.file_extension.len);
- UNMARSHAL_BYTES(ctx, out->stat.file_extension.utf8, out->stat.file_extension.len);
- UNMARSHAL_U32LE(ctx, out->stat.file_owner_n_uid);
- UNMARSHAL_U32LE(ctx, out->stat.file_owner_n_gid);
- UNMARSHAL_U32LE(ctx, out->stat.file_last_modified_n_uid);
- }
-#endif /* CONFIG_9P_ENABLE_9P2000_u */
-}
-
-static void unmarshal_Rwstat([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
- struct lib9p_msg_Rwstat *out = out_buf;
- [[gnu::unused]] void *extra = &out[1];
- uint32_t net_offset = 0;
- net_offset += 4;
- net_offset += 1;
- UNMARSHAL_U16LE(ctx, out->tag);
-}
-
-#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */
-#if CONFIG_9P_ENABLE_9P2000_p9p
-static void unmarshal_Topenfd([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
- struct lib9p_msg_Topenfd *out = out_buf;
- [[gnu::unused]] void *extra = &out[1];
- uint32_t net_offset = 0;
- net_offset += 4;
- net_offset += 1;
- UNMARSHAL_U16LE(ctx, out->tag);
- UNMARSHAL_U32LE(ctx, out->fid);
- UNMARSHAL_U8LE(ctx, out->mode);
-}
-
-static void unmarshal_Ropenfd([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
- struct lib9p_msg_Ropenfd *out = out_buf;
- [[gnu::unused]] void *extra = &out[1];
- uint32_t net_offset = 0;
- net_offset += 4;
- net_offset += 1;
- UNMARSHAL_U16LE(ctx, out->tag);
- UNMARSHAL_U8LE(ctx, out->qid.type);
- UNMARSHAL_U32LE(ctx, out->qid.vers);
- UNMARSHAL_U64LE(ctx, out->qid.path);
- UNMARSHAL_U32LE(ctx, out->iounit);
- UNMARSHAL_U32LE(ctx, out->unixfd);
-}
-
-#endif /* CONFIG_9P_ENABLE_9P2000_p9p */
-#if CONFIG_9P_ENABLE_9P2000_L
-static void unmarshal_Rlerror([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
- struct lib9p_msg_Rlerror *out = out_buf;
- [[gnu::unused]] void *extra = &out[1];
- uint32_t net_offset = 0;
- net_offset += 4;
- net_offset += 1;
- UNMARSHAL_U16LE(ctx, out->tag);
- UNMARSHAL_U32LE(ctx, out->ecode);
-}
-
-static void unmarshal_Tstatfs([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
- struct lib9p_msg_Tstatfs *out = out_buf;
- [[gnu::unused]] void *extra = &out[1];
- uint32_t net_offset = 0;
- net_offset += 4;
- net_offset += 1;
- UNMARSHAL_U16LE(ctx, out->tag);
- UNMARSHAL_U32LE(ctx, out->fid);
-}
-
-static void unmarshal_Rstatfs([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
- struct lib9p_msg_Rstatfs *out = out_buf;
- [[gnu::unused]] void *extra = &out[1];
- uint32_t net_offset = 0;
- net_offset += 4;
- net_offset += 1;
- UNMARSHAL_U16LE(ctx, out->tag);
- UNMARSHAL_U32LE(ctx, out->type);
- UNMARSHAL_U32LE(ctx, out->bsize);
- UNMARSHAL_U64LE(ctx, out->blocks);
- UNMARSHAL_U64LE(ctx, out->bfree);
- UNMARSHAL_U64LE(ctx, out->bavail);
- UNMARSHAL_U64LE(ctx, out->files);
- UNMARSHAL_U64LE(ctx, out->ffree);
- UNMARSHAL_U64LE(ctx, out->fsid);
- UNMARSHAL_U32LE(ctx, out->namelen);
-}
-
-static void unmarshal_Tlopen([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
- struct lib9p_msg_Tlopen *out = out_buf;
- [[gnu::unused]] void *extra = &out[1];
- uint32_t net_offset = 0;
- net_offset += 4;
- net_offset += 1;
- UNMARSHAL_U16LE(ctx, out->tag);
- UNMARSHAL_U32LE(ctx, out->fid);
- UNMARSHAL_U32LE(ctx, out->flags);
-}
-
-static void unmarshal_Rlopen([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
- struct lib9p_msg_Rlopen *out = out_buf;
- [[gnu::unused]] void *extra = &out[1];
- uint32_t net_offset = 0;
- net_offset += 4;
- net_offset += 1;
- UNMARSHAL_U16LE(ctx, out->tag);
- UNMARSHAL_U8LE(ctx, out->qid.type);
- UNMARSHAL_U32LE(ctx, out->qid.vers);
- UNMARSHAL_U64LE(ctx, out->qid.path);
- UNMARSHAL_U32LE(ctx, out->iounit);
-}
-
-static void unmarshal_Tlcreate([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
- struct lib9p_msg_Tlcreate *out = out_buf;
- [[gnu::unused]] void *extra = &out[1];
- uint32_t net_offset = 0;
- net_offset += 4;
- net_offset += 1;
- UNMARSHAL_U16LE(ctx, out->tag);
- UNMARSHAL_U32LE(ctx, out->fid);
- UNMARSHAL_U16LE(ctx, out->name.len);
- UNMARSHAL_BYTES(ctx, out->name.utf8, out->name.len);
- UNMARSHAL_U32LE(ctx, out->flags);
- UNMARSHAL_U32LE(ctx, out->mode);
- UNMARSHAL_U32LE(ctx, out->gid);
-}
-
-static void unmarshal_Rlcreate([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
- struct lib9p_msg_Rlcreate *out = out_buf;
- [[gnu::unused]] void *extra = &out[1];
- uint32_t net_offset = 0;
- net_offset += 4;
- net_offset += 1;
- UNMARSHAL_U16LE(ctx, out->tag);
- UNMARSHAL_U8LE(ctx, out->qid.type);
- UNMARSHAL_U32LE(ctx, out->qid.vers);
- UNMARSHAL_U64LE(ctx, out->qid.path);
- UNMARSHAL_U32LE(ctx, out->iounit);
-}
-
-static void unmarshal_Tsymlink([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
- struct lib9p_msg_Tsymlink *out = out_buf;
- [[gnu::unused]] void *extra = &out[1];
- uint32_t net_offset = 0;
- net_offset += 4;
- net_offset += 1;
- UNMARSHAL_U16LE(ctx, out->tag);
- UNMARSHAL_U32LE(ctx, out->fid);
- UNMARSHAL_U16LE(ctx, out->name.len);
- UNMARSHAL_BYTES(ctx, out->name.utf8, out->name.len);
- UNMARSHAL_U16LE(ctx, out->symtgt.len);
- UNMARSHAL_BYTES(ctx, out->symtgt.utf8, out->symtgt.len);
- UNMARSHAL_U32LE(ctx, out->gid);
-}
-
-static void unmarshal_Rsymlink([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
- struct lib9p_msg_Rsymlink *out = out_buf;
- [[gnu::unused]] void *extra = &out[1];
- uint32_t net_offset = 0;
- net_offset += 4;
- net_offset += 1;
- UNMARSHAL_U16LE(ctx, out->tag);
- UNMARSHAL_U8LE(ctx, out->qid.type);
- UNMARSHAL_U32LE(ctx, out->qid.vers);
- UNMARSHAL_U64LE(ctx, out->qid.path);
-}
-
-static void unmarshal_Tmknod([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
- struct lib9p_msg_Tmknod *out = out_buf;
- [[gnu::unused]] void *extra = &out[1];
- uint32_t net_offset = 0;
- net_offset += 4;
- net_offset += 1;
- UNMARSHAL_U16LE(ctx, out->tag);
- UNMARSHAL_U32LE(ctx, out->dfid);
- UNMARSHAL_U16LE(ctx, out->name.len);
- UNMARSHAL_BYTES(ctx, out->name.utf8, out->name.len);
- UNMARSHAL_U32LE(ctx, out->mode);
- UNMARSHAL_U32LE(ctx, out->major);
- UNMARSHAL_U32LE(ctx, out->minor);
- UNMARSHAL_U32LE(ctx, out->gid);
-}
-
-static void unmarshal_Rmknod([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
- struct lib9p_msg_Rmknod *out = out_buf;
- [[gnu::unused]] void *extra = &out[1];
- uint32_t net_offset = 0;
- net_offset += 4;
- net_offset += 1;
- UNMARSHAL_U16LE(ctx, out->tag);
- UNMARSHAL_U8LE(ctx, out->qid.type);
- UNMARSHAL_U32LE(ctx, out->qid.vers);
- UNMARSHAL_U64LE(ctx, out->qid.path);
-}
-
-static void unmarshal_Trename([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
- struct lib9p_msg_Trename *out = out_buf;
- [[gnu::unused]] void *extra = &out[1];
- uint32_t net_offset = 0;
- net_offset += 4;
- net_offset += 1;
- UNMARSHAL_U16LE(ctx, out->tag);
- UNMARSHAL_U32LE(ctx, out->fid);
- UNMARSHAL_U32LE(ctx, out->dfid);
- UNMARSHAL_U16LE(ctx, out->name.len);
- UNMARSHAL_BYTES(ctx, out->name.utf8, out->name.len);
-}
-
-static void unmarshal_Rrename([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
- struct lib9p_msg_Rrename *out = out_buf;
- [[gnu::unused]] void *extra = &out[1];
- uint32_t net_offset = 0;
- net_offset += 4;
- net_offset += 1;
- UNMARSHAL_U16LE(ctx, out->tag);
-}
-
-static void unmarshal_Treadlink([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
- struct lib9p_msg_Treadlink *out = out_buf;
- [[gnu::unused]] void *extra = &out[1];
- uint32_t net_offset = 0;
- net_offset += 4;
- net_offset += 1;
- UNMARSHAL_U16LE(ctx, out->tag);
- UNMARSHAL_U32LE(ctx, out->fid);
-}
-
-static void unmarshal_Rreadlink([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
- struct lib9p_msg_Rreadlink *out = out_buf;
- [[gnu::unused]] void *extra = &out[1];
- uint32_t net_offset = 0;
- net_offset += 4;
- net_offset += 1;
- UNMARSHAL_U16LE(ctx, out->tag);
- UNMARSHAL_U16LE(ctx, out->target.len);
- UNMARSHAL_BYTES(ctx, out->target.utf8, out->target.len);
-}
-
-static void unmarshal_Tgetattr([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
- struct lib9p_msg_Tgetattr *out = out_buf;
- [[gnu::unused]] void *extra = &out[1];
- uint32_t net_offset = 0;
- net_offset += 4;
- net_offset += 1;
- UNMARSHAL_U16LE(ctx, out->tag);
- UNMARSHAL_U32LE(ctx, out->fid);
- UNMARSHAL_U64LE(ctx, out->request_mask);
-}
-
-static void unmarshal_Rgetattr([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
- struct lib9p_msg_Rgetattr *out = out_buf;
- [[gnu::unused]] void *extra = &out[1];
- uint32_t net_offset = 0;
- net_offset += 4;
- net_offset += 1;
- UNMARSHAL_U16LE(ctx, out->tag);
- UNMARSHAL_U64LE(ctx, out->valid);
- UNMARSHAL_U8LE(ctx, out->qid.type);
- UNMARSHAL_U32LE(ctx, out->qid.vers);
- UNMARSHAL_U64LE(ctx, out->qid.path);
- UNMARSHAL_U32LE(ctx, out->mode);
- UNMARSHAL_U32LE(ctx, out->uid);
- UNMARSHAL_U32LE(ctx, out->gid);
- UNMARSHAL_U64LE(ctx, out->nlink);
- UNMARSHAL_U64LE(ctx, out->rdev);
- UNMARSHAL_U64LE(ctx, out->filesize);
- UNMARSHAL_U64LE(ctx, out->blksize);
- UNMARSHAL_U64LE(ctx, out->blocks);
- UNMARSHAL_U64LE(ctx, out->atime_sec);
- UNMARSHAL_U64LE(ctx, out->atime_nsec);
- UNMARSHAL_U64LE(ctx, out->mtime_sec);
- UNMARSHAL_U64LE(ctx, out->mtime_nsec);
- UNMARSHAL_U64LE(ctx, out->ctime_sec);
- UNMARSHAL_U64LE(ctx, out->ctime_nsec);
- UNMARSHAL_U64LE(ctx, out->btime_sec);
- UNMARSHAL_U64LE(ctx, out->btime_nsec);
- UNMARSHAL_U64LE(ctx, out->gen);
- UNMARSHAL_U64LE(ctx, out->data_version);
-}
-
-static void unmarshal_Tsetattr([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
- struct lib9p_msg_Tsetattr *out = out_buf;
- [[gnu::unused]] void *extra = &out[1];
- uint32_t net_offset = 0;
- net_offset += 4;
- net_offset += 1;
- UNMARSHAL_U16LE(ctx, out->tag);
- UNMARSHAL_U32LE(ctx, out->fid);
- UNMARSHAL_U32LE(ctx, out->valid);
- UNMARSHAL_U32LE(ctx, out->mode);
- UNMARSHAL_U32LE(ctx, out->uid);
- UNMARSHAL_U32LE(ctx, out->gid);
- UNMARSHAL_U64LE(ctx, out->filesize);
- UNMARSHAL_U64LE(ctx, out->atime_sec);
- UNMARSHAL_U64LE(ctx, out->atime_nsec);
- UNMARSHAL_U64LE(ctx, out->mtime_sec);
- UNMARSHAL_U64LE(ctx, out->mtime_nsec);
-}
-
-static void unmarshal_Rsetattr([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
- struct lib9p_msg_Rsetattr *out = out_buf;
- [[gnu::unused]] void *extra = &out[1];
- uint32_t net_offset = 0;
- net_offset += 4;
- net_offset += 1;
- UNMARSHAL_U16LE(ctx, out->tag);
-}
-
-static void unmarshal_Txattrwalk([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
- struct lib9p_msg_Txattrwalk *out = out_buf;
- [[gnu::unused]] void *extra = &out[1];
- uint32_t net_offset = 0;
- net_offset += 4;
- net_offset += 1;
- UNMARSHAL_U16LE(ctx, out->tag);
- UNMARSHAL_U32LE(ctx, out->fid);
- UNMARSHAL_U32LE(ctx, out->newfid);
- UNMARSHAL_U16LE(ctx, out->name.len);
- UNMARSHAL_BYTES(ctx, out->name.utf8, out->name.len);
-}
-
-static void unmarshal_Rxattrwalk([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
- struct lib9p_msg_Rxattrwalk *out = out_buf;
- [[gnu::unused]] void *extra = &out[1];
- uint32_t net_offset = 0;
- net_offset += 4;
- net_offset += 1;
- UNMARSHAL_U16LE(ctx, out->tag);
- UNMARSHAL_U64LE(ctx, out->attr_size);
-}
-
-static void unmarshal_Txattrcreate([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
- struct lib9p_msg_Txattrcreate *out = out_buf;
- [[gnu::unused]] void *extra = &out[1];
- uint32_t net_offset = 0;
- net_offset += 4;
- net_offset += 1;
- UNMARSHAL_U16LE(ctx, out->tag);
- UNMARSHAL_U32LE(ctx, out->fid);
- UNMARSHAL_U16LE(ctx, out->name.len);
- UNMARSHAL_BYTES(ctx, out->name.utf8, out->name.len);
- UNMARSHAL_U64LE(ctx, out->attr_size);
- UNMARSHAL_U32LE(ctx, out->flags);
-}
-
-static void unmarshal_Rxattrcreate([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
- struct lib9p_msg_Rxattrcreate *out = out_buf;
- [[gnu::unused]] void *extra = &out[1];
- uint32_t net_offset = 0;
- net_offset += 4;
- net_offset += 1;
- UNMARSHAL_U16LE(ctx, out->tag);
-}
-
-static void unmarshal_Treaddir([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
- struct lib9p_msg_Treaddir *out = out_buf;
- [[gnu::unused]] void *extra = &out[1];
- uint32_t net_offset = 0;
- net_offset += 4;
- net_offset += 1;
- UNMARSHAL_U16LE(ctx, out->tag);
- UNMARSHAL_U32LE(ctx, out->fid);
- UNMARSHAL_U64LE(ctx, out->offset);
- UNMARSHAL_U32LE(ctx, out->count);
-}
-
-static void unmarshal_Rreaddir([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
- struct lib9p_msg_Rreaddir *out = out_buf;
- [[gnu::unused]] void *extra = &out[1];
- uint32_t net_offset = 0;
- net_offset += 4;
- net_offset += 1;
- UNMARSHAL_U16LE(ctx, out->tag);
- UNMARSHAL_U32LE(ctx, out->count);
- UNMARSHAL_BYTES(ctx, out->data, out->count);
-}
-
-static void unmarshal_Tfsync([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
- struct lib9p_msg_Tfsync *out = out_buf;
- [[gnu::unused]] void *extra = &out[1];
- uint32_t net_offset = 0;
- net_offset += 4;
- net_offset += 1;
- UNMARSHAL_U16LE(ctx, out->tag);
- UNMARSHAL_U32LE(ctx, out->fid);
- UNMARSHAL_U32LE(ctx, out->datasync);
-}
-
-static void unmarshal_Rfsync([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
- struct lib9p_msg_Rfsync *out = out_buf;
- [[gnu::unused]] void *extra = &out[1];
- uint32_t net_offset = 0;
- net_offset += 4;
- net_offset += 1;
- UNMARSHAL_U16LE(ctx, out->tag);
-}
-
-static void unmarshal_Tlock([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
- struct lib9p_msg_Tlock *out = out_buf;
- [[gnu::unused]] void *extra = &out[1];
- uint32_t net_offset = 0;
- net_offset += 4;
- net_offset += 1;
- UNMARSHAL_U16LE(ctx, out->tag);
- UNMARSHAL_U32LE(ctx, out->fid);
- UNMARSHAL_U8LE(ctx, out->type);
- UNMARSHAL_U32LE(ctx, out->flags);
- UNMARSHAL_U64LE(ctx, out->start);
- UNMARSHAL_U64LE(ctx, out->length);
- UNMARSHAL_U32LE(ctx, out->proc_id);
- UNMARSHAL_U16LE(ctx, out->client_id.len);
- UNMARSHAL_BYTES(ctx, out->client_id.utf8, out->client_id.len);
-}
-
-static void unmarshal_Rlock([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
- struct lib9p_msg_Rlock *out = out_buf;
- [[gnu::unused]] void *extra = &out[1];
- uint32_t net_offset = 0;
- net_offset += 4;
- net_offset += 1;
- UNMARSHAL_U16LE(ctx, out->tag);
- UNMARSHAL_U8LE(ctx, out->status);
-}
-
-static void unmarshal_Tgetlock([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
- struct lib9p_msg_Tgetlock *out = out_buf;
- [[gnu::unused]] void *extra = &out[1];
- uint32_t net_offset = 0;
- net_offset += 4;
- net_offset += 1;
- UNMARSHAL_U16LE(ctx, out->tag);
- UNMARSHAL_U32LE(ctx, out->fid);
- UNMARSHAL_U8LE(ctx, out->type);
- UNMARSHAL_U64LE(ctx, out->start);
- UNMARSHAL_U64LE(ctx, out->length);
- UNMARSHAL_U32LE(ctx, out->proc_id);
- UNMARSHAL_U16LE(ctx, out->client_id.len);
- UNMARSHAL_BYTES(ctx, out->client_id.utf8, out->client_id.len);
-}
-
-static void unmarshal_Rgetlock([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
- struct lib9p_msg_Rgetlock *out = out_buf;
- [[gnu::unused]] void *extra = &out[1];
- uint32_t net_offset = 0;
- net_offset += 4;
- net_offset += 1;
- UNMARSHAL_U16LE(ctx, out->tag);
- UNMARSHAL_U8LE(ctx, out->type);
- UNMARSHAL_U64LE(ctx, out->start);
- UNMARSHAL_U64LE(ctx, out->length);
- UNMARSHAL_U32LE(ctx, out->proc_id);
- UNMARSHAL_U16LE(ctx, out->client_id.len);
- UNMARSHAL_BYTES(ctx, out->client_id.utf8, out->client_id.len);
-}
-
-static void unmarshal_Tlink([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
- struct lib9p_msg_Tlink *out = out_buf;
- [[gnu::unused]] void *extra = &out[1];
- uint32_t net_offset = 0;
- net_offset += 4;
- net_offset += 1;
- UNMARSHAL_U16LE(ctx, out->tag);
- UNMARSHAL_U32LE(ctx, out->dfid);
- UNMARSHAL_U32LE(ctx, out->fid);
- UNMARSHAL_U16LE(ctx, out->name.len);
- UNMARSHAL_BYTES(ctx, out->name.utf8, out->name.len);
-}
-
-static void unmarshal_Rlink([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
- struct lib9p_msg_Rlink *out = out_buf;
- [[gnu::unused]] void *extra = &out[1];
- uint32_t net_offset = 0;
- net_offset += 4;
- net_offset += 1;
- UNMARSHAL_U16LE(ctx, out->tag);
-}
-
-static void unmarshal_Tmkdir([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
- struct lib9p_msg_Tmkdir *out = out_buf;
- [[gnu::unused]] void *extra = &out[1];
- uint32_t net_offset = 0;
- net_offset += 4;
- net_offset += 1;
- UNMARSHAL_U16LE(ctx, out->tag);
- UNMARSHAL_U32LE(ctx, out->dfid);
- UNMARSHAL_U16LE(ctx, out->name.len);
- UNMARSHAL_BYTES(ctx, out->name.utf8, out->name.len);
- UNMARSHAL_U32LE(ctx, out->mode);
- UNMARSHAL_U32LE(ctx, out->gid);
-}
-
-static void unmarshal_Rmkdir([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
- struct lib9p_msg_Rmkdir *out = out_buf;
- [[gnu::unused]] void *extra = &out[1];
- uint32_t net_offset = 0;
- net_offset += 4;
- net_offset += 1;
- UNMARSHAL_U16LE(ctx, out->tag);
- UNMARSHAL_U8LE(ctx, out->qid.type);
- UNMARSHAL_U32LE(ctx, out->qid.vers);
- UNMARSHAL_U64LE(ctx, out->qid.path);
-}
-
-static void unmarshal_Trenameat([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
- struct lib9p_msg_Trenameat *out = out_buf;
- [[gnu::unused]] void *extra = &out[1];
- uint32_t net_offset = 0;
- net_offset += 4;
- net_offset += 1;
- UNMARSHAL_U16LE(ctx, out->tag);
- UNMARSHAL_U32LE(ctx, out->olddirfid);
- UNMARSHAL_U16LE(ctx, out->oldname.len);
- UNMARSHAL_BYTES(ctx, out->oldname.utf8, out->oldname.len);
- UNMARSHAL_U32LE(ctx, out->newdirfid);
- UNMARSHAL_U16LE(ctx, out->newname.len);
- UNMARSHAL_BYTES(ctx, out->newname.utf8, out->newname.len);
-}
-
-static void unmarshal_Rrenameat([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
- struct lib9p_msg_Rrenameat *out = out_buf;
- [[gnu::unused]] void *extra = &out[1];
- uint32_t net_offset = 0;
- net_offset += 4;
- net_offset += 1;
- UNMARSHAL_U16LE(ctx, out->tag);
-}
-
-static void unmarshal_Tunlinkat([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
- struct lib9p_msg_Tunlinkat *out = out_buf;
- [[gnu::unused]] void *extra = &out[1];
- uint32_t net_offset = 0;
- net_offset += 4;
- net_offset += 1;
- UNMARSHAL_U16LE(ctx, out->tag);
- UNMARSHAL_U32LE(ctx, out->dirfd);
- UNMARSHAL_U16LE(ctx, out->name.len);
- UNMARSHAL_BYTES(ctx, out->name.utf8, out->name.len);
- UNMARSHAL_U32LE(ctx, out->flags);
-}
-
-static void unmarshal_Runlinkat([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
- struct lib9p_msg_Runlinkat *out = out_buf;
- [[gnu::unused]] void *extra = &out[1];
- uint32_t net_offset = 0;
- net_offset += 4;
- net_offset += 1;
- UNMARSHAL_U16LE(ctx, out->tag);
-}
-
-#endif /* CONFIG_9P_ENABLE_9P2000_L */
-#if CONFIG_9P_ENABLE_9P2000_e
-static void unmarshal_Tsession([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
- struct lib9p_msg_Tsession *out = out_buf;
- [[gnu::unused]] void *extra = &out[1];
- uint32_t net_offset = 0;
- net_offset += 4;
- net_offset += 1;
- UNMARSHAL_U16LE(ctx, out->tag);
- UNMARSHAL_U64LE(ctx, out->key);
-}
-
-static void unmarshal_Rsession([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
- struct lib9p_msg_Rsession *out = out_buf;
- [[gnu::unused]] void *extra = &out[1];
- uint32_t net_offset = 0;
- net_offset += 4;
- net_offset += 1;
- UNMARSHAL_U16LE(ctx, out->tag);
-}
-
-static void unmarshal_Tsread([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
- struct lib9p_msg_Tsread *out = out_buf;
- [[gnu::unused]] void *extra = &out[1];
- uint32_t net_offset = 0;
- net_offset += 4;
- net_offset += 1;
- UNMARSHAL_U16LE(ctx, out->tag);
- UNMARSHAL_U32LE(ctx, out->fid);
- UNMARSHAL_U16LE(ctx, out->nwname);
- out->wname = extra;
- extra += sizeof(out->wname[0]) * out->nwname;
- for (uint16_t i = 0; i < out->nwname; i++) {
- UNMARSHAL_U16LE(ctx, out->wname[i].len);
- UNMARSHAL_BYTES(ctx, out->wname[i].utf8, out->wname[i].len);
- }
-}
-
-static void unmarshal_Rsread([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
- struct lib9p_msg_Rsread *out = out_buf;
- [[gnu::unused]] void *extra = &out[1];
- uint32_t net_offset = 0;
- net_offset += 4;
- net_offset += 1;
- UNMARSHAL_U16LE(ctx, out->tag);
- UNMARSHAL_U32LE(ctx, out->count);
- UNMARSHAL_BYTES(ctx, out->data, out->count);
-}
-
-static void unmarshal_Tswrite([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
- struct lib9p_msg_Tswrite *out = out_buf;
- [[gnu::unused]] void *extra = &out[1];
- uint32_t net_offset = 0;
- net_offset += 4;
- net_offset += 1;
- UNMARSHAL_U16LE(ctx, out->tag);
- UNMARSHAL_U32LE(ctx, out->fid);
- UNMARSHAL_U16LE(ctx, out->nwname);
- out->wname = extra;
- extra += sizeof(out->wname[0]) * out->nwname;
- for (uint16_t i = 0; i < out->nwname; i++) {
- UNMARSHAL_U16LE(ctx, out->wname[i].len);
- UNMARSHAL_BYTES(ctx, out->wname[i].utf8, out->wname[i].len);
- }
- UNMARSHAL_U32LE(ctx, out->count);
- UNMARSHAL_BYTES(ctx, out->data, out->count);
-}
-
-static void unmarshal_Rswrite([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
- struct lib9p_msg_Rswrite *out = out_buf;
- [[gnu::unused]] void *extra = &out[1];
- uint32_t net_offset = 0;
- net_offset += 4;
- net_offset += 1;
- UNMARSHAL_U16LE(ctx, out->tag);
- UNMARSHAL_U32LE(ctx, out->count);
-}
-#endif /* CONFIG_9P_ENABLE_9P2000_e */
-
-/* marshal_* ******************************************************************/
-
-#define MARSHAL_BYTES_ZEROCOPY(ctx, data, len) \
- if (ret->net_iov[ret->net_iov_cnt-1].iov_len) \
- ret->net_iov_cnt++; \
- ret->net_iov[ret->net_iov_cnt-1].iov_base = data; \
- ret->net_iov[ret->net_iov_cnt-1].iov_len = len; \
- ret->net_iov_cnt++;
-#define MARSHAL_BYTES(ctx, data, len) \
- if (!ret->net_iov[ret->net_iov_cnt-1].iov_base) \
- ret->net_iov[ret->net_iov_cnt-1].iov_base = &ret->net_copied[ret->net_copied_size]; \
- memcpy(&ret->net_copied[ret->net_copied_size], data, len); \
- ret->net_copied_size += len; \
- ret->net_iov[ret->net_iov_cnt-1].iov_len += len;
-#define MARSHAL_U8LE(ctx, val) \
- if (!ret->net_iov[ret->net_iov_cnt-1].iov_base) \
- ret->net_iov[ret->net_iov_cnt-1].iov_base = &ret->net_copied[ret->net_copied_size]; \
- ret->net_copied[ret->net_copied_size] = val; \
- ret->net_copied_size += 1; \
- ret->net_iov[ret->net_iov_cnt-1].iov_len += 1;
-#define MARSHAL_U16LE(ctx, val) \
- if (!ret->net_iov[ret->net_iov_cnt-1].iov_base) \
- ret->net_iov[ret->net_iov_cnt-1].iov_base = &ret->net_copied[ret->net_copied_size]; \
- uint16le_encode(&ret->net_copied[ret->net_copied_size], val); \
- ret->net_copied_size += 2; \
- ret->net_iov[ret->net_iov_cnt-1].iov_len += 2;
-#define MARSHAL_U32LE(ctx, val) \
- if (!ret->net_iov[ret->net_iov_cnt-1].iov_base) \
- ret->net_iov[ret->net_iov_cnt-1].iov_base = &ret->net_copied[ret->net_copied_size]; \
- uint32le_encode(&ret->net_copied[ret->net_copied_size], val); \
- ret->net_copied_size += 4; \
- ret->net_iov[ret->net_iov_cnt-1].iov_len += 4;
-#define MARSHAL_U64LE(ctx, val) \
- if (!ret->net_iov[ret->net_iov_cnt-1].iov_base) \
- ret->net_iov[ret->net_iov_cnt-1].iov_base = &ret->net_copied[ret->net_copied_size]; \
- uint64le_encode(&ret->net_copied[ret->net_copied_size], val); \
- ret->net_copied_size += 8; \
- ret->net_iov[ret->net_iov_cnt-1].iov_len += 8;
-
-#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u
-static bool marshal_stat(struct lib9p_ctx *ctx, struct lib9p_stat *val, struct _marshal_ret *ret) {
- uint32_t needed_size = 49 + val->file_name.len + val->file_owner_uid.len + val->file_owner_gid.len + val->file_last_modified_uid.len;
-#if CONFIG_9P_ENABLE_9P2000_u
- if is_ver(ctx, 9P2000_u) {
- needed_size += 14 + val->file_extension.len;
- }
-#endif /* CONFIG_9P_ENABLE_9P2000_u */
- if (needed_size > ctx->max_msg_size) {
- return true;
- }
- uint32_t offsetof_end = needed_size;
- uint32_t offsetof_kern_type = 2;
- MARSHAL_U16LE(ctx, offsetof_end - offsetof_kern_type);
- MARSHAL_U16LE(ctx, val->kern_type);
- MARSHAL_U32LE(ctx, val->kern_dev);
- MARSHAL_U8LE(ctx, val->file_qid.type & qt_masks[ctx->version]);
- MARSHAL_U32LE(ctx, val->file_qid.vers);
- MARSHAL_U64LE(ctx, val->file_qid.path);
- MARSHAL_U32LE(ctx, val->file_mode & dm_masks[ctx->version]);
- MARSHAL_U32LE(ctx, val->file_atime);
- MARSHAL_U32LE(ctx, val->file_mtime);
- MARSHAL_U64LE(ctx, val->file_size);
- MARSHAL_U16LE(ctx, val->file_name.len);
- MARSHAL_BYTES(ctx, val->file_name.utf8, val->file_name.len);
- MARSHAL_U16LE(ctx, val->file_owner_uid.len);
- MARSHAL_BYTES(ctx, val->file_owner_uid.utf8, val->file_owner_uid.len);
- MARSHAL_U16LE(ctx, val->file_owner_gid.len);
- MARSHAL_BYTES(ctx, val->file_owner_gid.utf8, val->file_owner_gid.len);
- MARSHAL_U16LE(ctx, val->file_last_modified_uid.len);
- MARSHAL_BYTES(ctx, val->file_last_modified_uid.utf8, val->file_last_modified_uid.len);
-#if CONFIG_9P_ENABLE_9P2000_u
- if (is_ver(ctx, 9P2000_u)) {
- MARSHAL_U16LE(ctx, val->file_extension.len);
- MARSHAL_BYTES(ctx, val->file_extension.utf8, val->file_extension.len);
- MARSHAL_U32LE(ctx, val->file_owner_n_uid);
- MARSHAL_U32LE(ctx, val->file_owner_n_gid);
- MARSHAL_U32LE(ctx, val->file_last_modified_n_uid);
- }
-#endif /* CONFIG_9P_ENABLE_9P2000_u */
- return false;
-}
-
-#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */
-#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u
-static bool marshal_Tversion(struct lib9p_ctx *ctx, struct lib9p_msg_Tversion *val, struct _marshal_ret *ret) {
- uint32_t needed_size = 13 + val->version.len;
- if (needed_size > ctx->max_msg_size) {
- lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")",
- "Tversion",
- ctx->version ? "negotiated" : "client",
- ctx->max_msg_size);
- return true;
- }
- uint32_t offsetof_end = needed_size;
- uint32_t offsetof_size = 0;
- MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
- MARSHAL_U8LE(ctx, 100);
- MARSHAL_U16LE(ctx, val->tag);
- MARSHAL_U32LE(ctx, val->max_msg_size);
- MARSHAL_U16LE(ctx, val->version.len);
- MARSHAL_BYTES_ZEROCOPY(ctx, val->version.utf8, val->version.len);
- return false;
-}
-
-static bool marshal_Rversion(struct lib9p_ctx *ctx, struct lib9p_msg_Rversion *val, struct _marshal_ret *ret) {
- uint32_t needed_size = 13 + val->version.len;
- if (needed_size > ctx->max_msg_size) {
- lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")",
- "Rversion",
- ctx->version ? "negotiated" : "server",
- ctx->max_msg_size);
- return true;
- }
- uint32_t offsetof_end = needed_size;
- uint32_t offsetof_size = 0;
- MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
- MARSHAL_U8LE(ctx, 101);
- MARSHAL_U16LE(ctx, val->tag);
- MARSHAL_U32LE(ctx, val->max_msg_size);
- MARSHAL_U16LE(ctx, val->version.len);
- MARSHAL_BYTES_ZEROCOPY(ctx, val->version.utf8, val->version.len);
- return false;
-}
-
-static bool marshal_Tauth(struct lib9p_ctx *ctx, struct lib9p_msg_Tauth *val, struct _marshal_ret *ret) {
- uint32_t needed_size = 15 + val->uname.len + val->aname.len;
-#if CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_u
- if ( is_ver(ctx, 9P2000_L) || is_ver(ctx, 9P2000_u) ) {
- needed_size += 4;
- }
-#endif /* CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_u */
- if (needed_size > ctx->max_msg_size) {
- lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")",
- "Tauth",
- ctx->version ? "negotiated" : "client",
- ctx->max_msg_size);
- return true;
- }
- uint32_t offsetof_end = needed_size;
- uint32_t offsetof_size = 0;
- MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
- MARSHAL_U8LE(ctx, 102);
- MARSHAL_U16LE(ctx, val->tag);
- MARSHAL_U32LE(ctx, val->afid);
- MARSHAL_U16LE(ctx, val->uname.len);
- MARSHAL_BYTES_ZEROCOPY(ctx, val->uname.utf8, val->uname.len);
- MARSHAL_U16LE(ctx, val->aname.len);
- MARSHAL_BYTES_ZEROCOPY(ctx, val->aname.utf8, val->aname.len);
-#if CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_u
- if (( is_ver(ctx, 9P2000_L) || is_ver(ctx, 9P2000_u) )) {
- MARSHAL_U32LE(ctx, val->n_uid);
- }
-#endif /* CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_u */
- return false;
-}
-
-static bool marshal_Rauth(struct lib9p_ctx *ctx, struct lib9p_msg_Rauth *val, struct _marshal_ret *ret) {
- uint32_t needed_size = 20;
- if (needed_size > ctx->max_msg_size) {
- lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")",
- "Rauth",
- ctx->version ? "negotiated" : "server",
- ctx->max_msg_size);
- return true;
- }
- uint32_t offsetof_end = needed_size;
- uint32_t offsetof_size = 0;
- MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
- MARSHAL_U8LE(ctx, 103);
- MARSHAL_U16LE(ctx, val->tag);
- MARSHAL_U8LE(ctx, val->aqid.type & qt_masks[ctx->version]);
- MARSHAL_U32LE(ctx, val->aqid.vers);
- MARSHAL_U64LE(ctx, val->aqid.path);
- return false;
-}
-
-static bool marshal_Tattach(struct lib9p_ctx *ctx, struct lib9p_msg_Tattach *val, struct _marshal_ret *ret) {
- uint32_t needed_size = 19 + val->uname.len + val->aname.len;
-#if CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_u
- if ( is_ver(ctx, 9P2000_L) || is_ver(ctx, 9P2000_u) ) {
- needed_size += 4;
- }
-#endif /* CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_u */
- if (needed_size > ctx->max_msg_size) {
- lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")",
- "Tattach",
- ctx->version ? "negotiated" : "client",
- ctx->max_msg_size);
- return true;
- }
- uint32_t offsetof_end = needed_size;
- uint32_t offsetof_size = 0;
- MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
- MARSHAL_U8LE(ctx, 104);
- MARSHAL_U16LE(ctx, val->tag);
- MARSHAL_U32LE(ctx, val->fid);
- MARSHAL_U32LE(ctx, val->afid);
- MARSHAL_U16LE(ctx, val->uname.len);
- MARSHAL_BYTES_ZEROCOPY(ctx, val->uname.utf8, val->uname.len);
- MARSHAL_U16LE(ctx, val->aname.len);
- MARSHAL_BYTES_ZEROCOPY(ctx, val->aname.utf8, val->aname.len);
-#if CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_u
- if (( is_ver(ctx, 9P2000_L) || is_ver(ctx, 9P2000_u) )) {
- MARSHAL_U32LE(ctx, val->n_uid);
- }
-#endif /* CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_u */
- return false;
-}
-
-static bool marshal_Rattach(struct lib9p_ctx *ctx, struct lib9p_msg_Rattach *val, struct _marshal_ret *ret) {
- uint32_t needed_size = 20;
- if (needed_size > ctx->max_msg_size) {
- lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")",
- "Rattach",
- ctx->version ? "negotiated" : "server",
- ctx->max_msg_size);
- return true;
- }
- uint32_t offsetof_end = needed_size;
- uint32_t offsetof_size = 0;
- MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
- MARSHAL_U8LE(ctx, 105);
- MARSHAL_U16LE(ctx, val->tag);
- MARSHAL_U8LE(ctx, val->qid.type & qt_masks[ctx->version]);
- MARSHAL_U32LE(ctx, val->qid.vers);
- MARSHAL_U64LE(ctx, val->qid.path);
- return false;
-}
-
-static bool marshal_Rerror(struct lib9p_ctx *ctx, struct lib9p_msg_Rerror *val, struct _marshal_ret *ret) {
- uint32_t needed_size = 9 + val->ename.len;
-#if CONFIG_9P_ENABLE_9P2000_u
- if is_ver(ctx, 9P2000_u) {
- needed_size += 4;
- }
-#endif /* CONFIG_9P_ENABLE_9P2000_u */
- if (needed_size > ctx->max_msg_size) {
- lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")",
- "Rerror",
- ctx->version ? "negotiated" : "server",
- ctx->max_msg_size);
- return true;
- }
- uint32_t offsetof_end = needed_size;
- uint32_t offsetof_size = 0;
- MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
- MARSHAL_U8LE(ctx, 107);
- MARSHAL_U16LE(ctx, val->tag);
- MARSHAL_U16LE(ctx, val->ename.len);
- MARSHAL_BYTES_ZEROCOPY(ctx, val->ename.utf8, val->ename.len);
-#if CONFIG_9P_ENABLE_9P2000_u
- if (is_ver(ctx, 9P2000_u)) {
- MARSHAL_U32LE(ctx, val->errno);
- }
-#endif /* CONFIG_9P_ENABLE_9P2000_u */
- return false;
-}
-
-static bool marshal_Tflush(struct lib9p_ctx *ctx, struct lib9p_msg_Tflush *val, struct _marshal_ret *ret) {
- uint32_t needed_size = 9;
- if (needed_size > ctx->max_msg_size) {
- lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")",
- "Tflush",
- ctx->version ? "negotiated" : "client",
- ctx->max_msg_size);
- return true;
- }
- uint32_t offsetof_end = needed_size;
- uint32_t offsetof_size = 0;
- MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
- MARSHAL_U8LE(ctx, 108);
- MARSHAL_U16LE(ctx, val->tag);
- MARSHAL_U16LE(ctx, val->oldtag);
- return false;
-}
-
-static bool marshal_Rflush(struct lib9p_ctx *ctx, struct lib9p_msg_Rflush *val, struct _marshal_ret *ret) {
- uint32_t needed_size = 7;
- if (needed_size > ctx->max_msg_size) {
- lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")",
- "Rflush",
- ctx->version ? "negotiated" : "server",
- ctx->max_msg_size);
- return true;
- }
- uint32_t offsetof_end = needed_size;
- uint32_t offsetof_size = 0;
- MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
- MARSHAL_U8LE(ctx, 109);
- MARSHAL_U16LE(ctx, val->tag);
- return false;
-}
-
-static bool marshal_Twalk(struct lib9p_ctx *ctx, struct lib9p_msg_Twalk *val, struct _marshal_ret *ret) {
- uint32_t needed_size = 17;
- for (uint16_t i = 0; i < val->nwname; i++) {
- needed_size += 2 + val->wname[i].len;
- }
- if (needed_size > ctx->max_msg_size) {
- lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")",
- "Twalk",
- ctx->version ? "negotiated" : "client",
- ctx->max_msg_size);
- return true;
- }
- uint32_t offsetof_end = needed_size;
- uint32_t offsetof_size = 0;
- MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
- MARSHAL_U8LE(ctx, 110);
- MARSHAL_U16LE(ctx, val->tag);
- MARSHAL_U32LE(ctx, val->fid);
- MARSHAL_U32LE(ctx, val->newfid);
- MARSHAL_U16LE(ctx, val->nwname);
- for (uint16_t i = 0; i < val->nwname; i++) {
- MARSHAL_U16LE(ctx, val->wname[i].len);
- MARSHAL_BYTES_ZEROCOPY(ctx, val->wname[i].utf8, val->wname[i].len);
- }
- return false;
-}
-
-static bool marshal_Rwalk(struct lib9p_ctx *ctx, struct lib9p_msg_Rwalk *val, struct _marshal_ret *ret) {
- uint32_t needed_size = 9 + (val->nwqid)*13;
- if (needed_size > ctx->max_msg_size) {
- lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")",
- "Rwalk",
- ctx->version ? "negotiated" : "server",
- ctx->max_msg_size);
- return true;
- }
- uint32_t offsetof_end = needed_size;
- uint32_t offsetof_size = 0;
- MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
- MARSHAL_U8LE(ctx, 111);
- MARSHAL_U16LE(ctx, val->tag);
- MARSHAL_U16LE(ctx, val->nwqid);
- for (uint16_t i = 0; i < val->nwqid; i++) {
- MARSHAL_U8LE(ctx, val->wqid[i].type & qt_masks[ctx->version]);
- MARSHAL_U32LE(ctx, val->wqid[i].vers);
- MARSHAL_U64LE(ctx, val->wqid[i].path);
- }
- return false;
-}
-
-#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */
-#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u
-static bool marshal_Topen(struct lib9p_ctx *ctx, struct lib9p_msg_Topen *val, struct _marshal_ret *ret) {
- uint32_t needed_size = 12;
- if (needed_size > ctx->max_msg_size) {
- lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")",
- "Topen",
- ctx->version ? "negotiated" : "client",
- ctx->max_msg_size);
- return true;
- }
- uint32_t offsetof_end = needed_size;
- uint32_t offsetof_size = 0;
- MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
- MARSHAL_U8LE(ctx, 112);
- MARSHAL_U16LE(ctx, val->tag);
- MARSHAL_U32LE(ctx, val->fid);
- MARSHAL_U8LE(ctx, val->mode & o_masks[ctx->version]);
- return false;
-}
-
-static bool marshal_Ropen(struct lib9p_ctx *ctx, struct lib9p_msg_Ropen *val, struct _marshal_ret *ret) {
- uint32_t needed_size = 24;
- if (needed_size > ctx->max_msg_size) {
- lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")",
- "Ropen",
- ctx->version ? "negotiated" : "server",
- ctx->max_msg_size);
- return true;
- }
- uint32_t offsetof_end = needed_size;
- uint32_t offsetof_size = 0;
- MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
- MARSHAL_U8LE(ctx, 113);
- MARSHAL_U16LE(ctx, val->tag);
- MARSHAL_U8LE(ctx, val->qid.type & qt_masks[ctx->version]);
- MARSHAL_U32LE(ctx, val->qid.vers);
- MARSHAL_U64LE(ctx, val->qid.path);
- MARSHAL_U32LE(ctx, val->iounit);
- return false;
-}
-
-static bool marshal_Tcreate(struct lib9p_ctx *ctx, struct lib9p_msg_Tcreate *val, struct _marshal_ret *ret) {
- uint32_t needed_size = 18 + val->name.len;
- if (needed_size > ctx->max_msg_size) {
- lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")",
- "Tcreate",
- ctx->version ? "negotiated" : "client",
- ctx->max_msg_size);
- return true;
- }
- uint32_t offsetof_end = needed_size;
- uint32_t offsetof_size = 0;
- MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
- MARSHAL_U8LE(ctx, 114);
- MARSHAL_U16LE(ctx, val->tag);
- MARSHAL_U32LE(ctx, val->fid);
- MARSHAL_U16LE(ctx, val->name.len);
- MARSHAL_BYTES_ZEROCOPY(ctx, val->name.utf8, val->name.len);
- MARSHAL_U32LE(ctx, val->perm & dm_masks[ctx->version]);
- MARSHAL_U8LE(ctx, val->mode & o_masks[ctx->version]);
- return false;
-}
-
-static bool marshal_Rcreate(struct lib9p_ctx *ctx, struct lib9p_msg_Rcreate *val, struct _marshal_ret *ret) {
- uint32_t needed_size = 24;
- if (needed_size > ctx->max_msg_size) {
- lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")",
- "Rcreate",
- ctx->version ? "negotiated" : "server",
- ctx->max_msg_size);
- return true;
- }
- uint32_t offsetof_end = needed_size;
- uint32_t offsetof_size = 0;
- MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
- MARSHAL_U8LE(ctx, 115);
- MARSHAL_U16LE(ctx, val->tag);
- MARSHAL_U8LE(ctx, val->qid.type & qt_masks[ctx->version]);
- MARSHAL_U32LE(ctx, val->qid.vers);
- MARSHAL_U64LE(ctx, val->qid.path);
- MARSHAL_U32LE(ctx, val->iounit);
- return false;
-}
-
-#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */
-#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u
-static bool marshal_Tread(struct lib9p_ctx *ctx, struct lib9p_msg_Tread *val, struct _marshal_ret *ret) {
- uint32_t needed_size = 23;
- if (needed_size > ctx->max_msg_size) {
- lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")",
- "Tread",
- ctx->version ? "negotiated" : "client",
- ctx->max_msg_size);
- return true;
- }
- uint32_t offsetof_end = needed_size;
- uint32_t offsetof_size = 0;
- MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
- MARSHAL_U8LE(ctx, 116);
- MARSHAL_U16LE(ctx, val->tag);
- MARSHAL_U32LE(ctx, val->fid);
- MARSHAL_U64LE(ctx, val->offset);
- MARSHAL_U32LE(ctx, val->count);
- return false;
-}
-
-static bool marshal_Rread(struct lib9p_ctx *ctx, struct lib9p_msg_Rread *val, struct _marshal_ret *ret) {
- uint32_t needed_size = 11 + val->count;
- if (needed_size > ctx->max_msg_size) {
- lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")",
- "Rread",
- ctx->version ? "negotiated" : "server",
- ctx->max_msg_size);
- return true;
- }
- uint32_t offsetof_end = needed_size;
- uint32_t offsetof_size = 0;
- MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
- MARSHAL_U8LE(ctx, 117);
- MARSHAL_U16LE(ctx, val->tag);
- MARSHAL_U32LE(ctx, val->count);
- MARSHAL_BYTES_ZEROCOPY(ctx, val->data, val->count);
- return false;
-}
-
-static bool marshal_Twrite(struct lib9p_ctx *ctx, struct lib9p_msg_Twrite *val, struct _marshal_ret *ret) {
- uint32_t needed_size = 23 + val->count;
- if (needed_size > ctx->max_msg_size) {
- lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")",
- "Twrite",
- ctx->version ? "negotiated" : "client",
- ctx->max_msg_size);
- return true;
- }
- uint32_t offsetof_end = needed_size;
- uint32_t offsetof_size = 0;
- MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
- MARSHAL_U8LE(ctx, 118);
- MARSHAL_U16LE(ctx, val->tag);
- MARSHAL_U32LE(ctx, val->fid);
- MARSHAL_U64LE(ctx, val->offset);
- MARSHAL_U32LE(ctx, val->count);
- MARSHAL_BYTES_ZEROCOPY(ctx, val->data, val->count);
- return false;
-}
-
-static bool marshal_Rwrite(struct lib9p_ctx *ctx, struct lib9p_msg_Rwrite *val, struct _marshal_ret *ret) {
- uint32_t needed_size = 11;
- if (needed_size > ctx->max_msg_size) {
- lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")",
- "Rwrite",
- ctx->version ? "negotiated" : "server",
- ctx->max_msg_size);
- return true;
- }
- uint32_t offsetof_end = needed_size;
- uint32_t offsetof_size = 0;
- MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
- MARSHAL_U8LE(ctx, 119);
- MARSHAL_U16LE(ctx, val->tag);
- MARSHAL_U32LE(ctx, val->count);
- return false;
-}
-
-static bool marshal_Tclunk(struct lib9p_ctx *ctx, struct lib9p_msg_Tclunk *val, struct _marshal_ret *ret) {
- uint32_t needed_size = 11;
- if (needed_size > ctx->max_msg_size) {
- lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")",
- "Tclunk",
- ctx->version ? "negotiated" : "client",
- ctx->max_msg_size);
- return true;
- }
- uint32_t offsetof_end = needed_size;
- uint32_t offsetof_size = 0;
- MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
- MARSHAL_U8LE(ctx, 120);
- MARSHAL_U16LE(ctx, val->tag);
- MARSHAL_U32LE(ctx, val->fid);
- return false;
-}
-
-static bool marshal_Rclunk(struct lib9p_ctx *ctx, struct lib9p_msg_Rclunk *val, struct _marshal_ret *ret) {
- uint32_t needed_size = 7;
- if (needed_size > ctx->max_msg_size) {
- lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")",
- "Rclunk",
- ctx->version ? "negotiated" : "server",
- ctx->max_msg_size);
- return true;
- }
- uint32_t offsetof_end = needed_size;
- uint32_t offsetof_size = 0;
- MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
- MARSHAL_U8LE(ctx, 121);
- MARSHAL_U16LE(ctx, val->tag);
- return false;
-}
-
-static bool marshal_Tremove(struct lib9p_ctx *ctx, struct lib9p_msg_Tremove *val, struct _marshal_ret *ret) {
- uint32_t needed_size = 11;
- if (needed_size > ctx->max_msg_size) {
- lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")",
- "Tremove",
- ctx->version ? "negotiated" : "client",
- ctx->max_msg_size);
- return true;
- }
- uint32_t offsetof_end = needed_size;
- uint32_t offsetof_size = 0;
- MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
- MARSHAL_U8LE(ctx, 122);
- MARSHAL_U16LE(ctx, val->tag);
- MARSHAL_U32LE(ctx, val->fid);
- return false;
-}
-
-static bool marshal_Rremove(struct lib9p_ctx *ctx, struct lib9p_msg_Rremove *val, struct _marshal_ret *ret) {
- uint32_t needed_size = 7;
- if (needed_size > ctx->max_msg_size) {
- lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")",
- "Rremove",
- ctx->version ? "negotiated" : "server",
- ctx->max_msg_size);
- return true;
- }
- uint32_t offsetof_end = needed_size;
- uint32_t offsetof_size = 0;
- MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
- MARSHAL_U8LE(ctx, 123);
- MARSHAL_U16LE(ctx, val->tag);
- return false;
-}
-
-#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */
-#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u
-static bool marshal_Tstat(struct lib9p_ctx *ctx, struct lib9p_msg_Tstat *val, struct _marshal_ret *ret) {
- uint32_t needed_size = 11;
- if (needed_size > ctx->max_msg_size) {
- lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")",
- "Tstat",
- ctx->version ? "negotiated" : "client",
- ctx->max_msg_size);
- return true;
- }
- uint32_t offsetof_end = needed_size;
- uint32_t offsetof_size = 0;
- MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
- MARSHAL_U8LE(ctx, 124);
- MARSHAL_U16LE(ctx, val->tag);
- MARSHAL_U32LE(ctx, val->fid);
- return false;
-}
-
-static bool marshal_Rstat(struct lib9p_ctx *ctx, struct lib9p_msg_Rstat *val, struct _marshal_ret *ret) {
- uint32_t needed_size = 58 + val->stat.file_name.len + val->stat.file_owner_uid.len + val->stat.file_owner_gid.len + val->stat.file_last_modified_uid.len;
-#if CONFIG_9P_ENABLE_9P2000_u
- if is_ver(ctx, 9P2000_u) {
- needed_size += 14 + val->stat.file_extension.len;
- }
-#endif /* CONFIG_9P_ENABLE_9P2000_u */
- if (needed_size > ctx->max_msg_size) {
- lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")",
- "Rstat",
- ctx->version ? "negotiated" : "server",
- ctx->max_msg_size);
- return true;
- }
- uint32_t offsetof_end = needed_size;
- uint32_t offsetof_size = 0;
- uint32_t offsetof_stat = 9;
- MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
- MARSHAL_U8LE(ctx, 125);
- MARSHAL_U16LE(ctx, val->tag);
- MARSHAL_U16LE(ctx, offsetof_end - offsetof_stat);
- uint32_t offsetof_stat_end = 49 + val->stat.file_name.len + val->stat.file_owner_uid.len + val->stat.file_owner_gid.len + val->stat.file_last_modified_uid.len;
-#if CONFIG_9P_ENABLE_9P2000_u
- if is_ver(ctx, 9P2000_u) {
- offsetof_stat_end += 14 + val->stat.file_extension.len;
- }
-#endif /* CONFIG_9P_ENABLE_9P2000_u */
- uint32_t offsetof_stat_kern_type = 2;
- MARSHAL_U16LE(ctx, offsetof_stat_end - offsetof_stat_kern_type);
- MARSHAL_U16LE(ctx, val->stat.kern_type);
- MARSHAL_U32LE(ctx, val->stat.kern_dev);
- MARSHAL_U8LE(ctx, val->stat.file_qid.type & qt_masks[ctx->version]);
- MARSHAL_U32LE(ctx, val->stat.file_qid.vers);
- MARSHAL_U64LE(ctx, val->stat.file_qid.path);
- MARSHAL_U32LE(ctx, val->stat.file_mode & dm_masks[ctx->version]);
- MARSHAL_U32LE(ctx, val->stat.file_atime);
- MARSHAL_U32LE(ctx, val->stat.file_mtime);
- MARSHAL_U64LE(ctx, val->stat.file_size);
- MARSHAL_U16LE(ctx, val->stat.file_name.len);
- MARSHAL_BYTES_ZEROCOPY(ctx, val->stat.file_name.utf8, val->stat.file_name.len);
- MARSHAL_U16LE(ctx, val->stat.file_owner_uid.len);
- MARSHAL_BYTES_ZEROCOPY(ctx, val->stat.file_owner_uid.utf8, val->stat.file_owner_uid.len);
- MARSHAL_U16LE(ctx, val->stat.file_owner_gid.len);
- MARSHAL_BYTES_ZEROCOPY(ctx, val->stat.file_owner_gid.utf8, val->stat.file_owner_gid.len);
- MARSHAL_U16LE(ctx, val->stat.file_last_modified_uid.len);
- MARSHAL_BYTES_ZEROCOPY(ctx, val->stat.file_last_modified_uid.utf8, val->stat.file_last_modified_uid.len);
-#if CONFIG_9P_ENABLE_9P2000_u
- if (is_ver(ctx, 9P2000_u)) {
- MARSHAL_U16LE(ctx, val->stat.file_extension.len);
- MARSHAL_BYTES_ZEROCOPY(ctx, val->stat.file_extension.utf8, val->stat.file_extension.len);
- MARSHAL_U32LE(ctx, val->stat.file_owner_n_uid);
- MARSHAL_U32LE(ctx, val->stat.file_owner_n_gid);
- MARSHAL_U32LE(ctx, val->stat.file_last_modified_n_uid);
- }
-#endif /* CONFIG_9P_ENABLE_9P2000_u */
- return false;
-}
-
-static bool marshal_Twstat(struct lib9p_ctx *ctx, struct lib9p_msg_Twstat *val, struct _marshal_ret *ret) {
- uint32_t needed_size = 62 + val->stat.file_name.len + val->stat.file_owner_uid.len + val->stat.file_owner_gid.len + val->stat.file_last_modified_uid.len;
-#if CONFIG_9P_ENABLE_9P2000_u
- if is_ver(ctx, 9P2000_u) {
- needed_size += 14 + val->stat.file_extension.len;
- }
-#endif /* CONFIG_9P_ENABLE_9P2000_u */
- if (needed_size > ctx->max_msg_size) {
- lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")",
- "Twstat",
- ctx->version ? "negotiated" : "client",
- ctx->max_msg_size);
- return true;
- }
- uint32_t offsetof_end = needed_size;
- uint32_t offsetof_size = 0;
- uint32_t offsetof_stat = 13;
- MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
- MARSHAL_U8LE(ctx, 126);
- MARSHAL_U16LE(ctx, val->tag);
- MARSHAL_U32LE(ctx, val->fid);
- MARSHAL_U16LE(ctx, offsetof_end - offsetof_stat);
- uint32_t offsetof_stat_end = 49 + val->stat.file_name.len + val->stat.file_owner_uid.len + val->stat.file_owner_gid.len + val->stat.file_last_modified_uid.len;
-#if CONFIG_9P_ENABLE_9P2000_u
- if is_ver(ctx, 9P2000_u) {
- offsetof_stat_end += 14 + val->stat.file_extension.len;
- }
-#endif /* CONFIG_9P_ENABLE_9P2000_u */
- uint32_t offsetof_stat_kern_type = 2;
- MARSHAL_U16LE(ctx, offsetof_stat_end - offsetof_stat_kern_type);
- MARSHAL_U16LE(ctx, val->stat.kern_type);
- MARSHAL_U32LE(ctx, val->stat.kern_dev);
- MARSHAL_U8LE(ctx, val->stat.file_qid.type & qt_masks[ctx->version]);
- MARSHAL_U32LE(ctx, val->stat.file_qid.vers);
- MARSHAL_U64LE(ctx, val->stat.file_qid.path);
- MARSHAL_U32LE(ctx, val->stat.file_mode & dm_masks[ctx->version]);
- MARSHAL_U32LE(ctx, val->stat.file_atime);
- MARSHAL_U32LE(ctx, val->stat.file_mtime);
- MARSHAL_U64LE(ctx, val->stat.file_size);
- MARSHAL_U16LE(ctx, val->stat.file_name.len);
- MARSHAL_BYTES_ZEROCOPY(ctx, val->stat.file_name.utf8, val->stat.file_name.len);
- MARSHAL_U16LE(ctx, val->stat.file_owner_uid.len);
- MARSHAL_BYTES_ZEROCOPY(ctx, val->stat.file_owner_uid.utf8, val->stat.file_owner_uid.len);
- MARSHAL_U16LE(ctx, val->stat.file_owner_gid.len);
- MARSHAL_BYTES_ZEROCOPY(ctx, val->stat.file_owner_gid.utf8, val->stat.file_owner_gid.len);
- MARSHAL_U16LE(ctx, val->stat.file_last_modified_uid.len);
- MARSHAL_BYTES_ZEROCOPY(ctx, val->stat.file_last_modified_uid.utf8, val->stat.file_last_modified_uid.len);
-#if CONFIG_9P_ENABLE_9P2000_u
- if (is_ver(ctx, 9P2000_u)) {
- MARSHAL_U16LE(ctx, val->stat.file_extension.len);
- MARSHAL_BYTES_ZEROCOPY(ctx, val->stat.file_extension.utf8, val->stat.file_extension.len);
- MARSHAL_U32LE(ctx, val->stat.file_owner_n_uid);
- MARSHAL_U32LE(ctx, val->stat.file_owner_n_gid);
- MARSHAL_U32LE(ctx, val->stat.file_last_modified_n_uid);
- }
-#endif /* CONFIG_9P_ENABLE_9P2000_u */
- return false;
-}
-
-static bool marshal_Rwstat(struct lib9p_ctx *ctx, struct lib9p_msg_Rwstat *val, struct _marshal_ret *ret) {
- uint32_t needed_size = 7;
- if (needed_size > ctx->max_msg_size) {
- lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")",
- "Rwstat",
- ctx->version ? "negotiated" : "server",
- ctx->max_msg_size);
- return true;
- }
- uint32_t offsetof_end = needed_size;
- uint32_t offsetof_size = 0;
- MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
- MARSHAL_U8LE(ctx, 127);
- MARSHAL_U16LE(ctx, val->tag);
- return false;
-}
-
-#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */
-#if CONFIG_9P_ENABLE_9P2000_p9p
-static bool marshal_Topenfd(struct lib9p_ctx *ctx, struct lib9p_msg_Topenfd *val, struct _marshal_ret *ret) {
- uint32_t needed_size = 12;
- if (needed_size > ctx->max_msg_size) {
- lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")",
- "Topenfd",
- ctx->version ? "negotiated" : "client",
- ctx->max_msg_size);
- return true;
- }
- uint32_t offsetof_end = needed_size;
- uint32_t offsetof_size = 0;
- MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
- MARSHAL_U8LE(ctx, 98);
- MARSHAL_U16LE(ctx, val->tag);
- MARSHAL_U32LE(ctx, val->fid);
- MARSHAL_U8LE(ctx, val->mode & o_masks[ctx->version]);
- return false;
-}
-
-static bool marshal_Ropenfd(struct lib9p_ctx *ctx, struct lib9p_msg_Ropenfd *val, struct _marshal_ret *ret) {
- uint32_t needed_size = 28;
- if (needed_size > ctx->max_msg_size) {
- lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")",
- "Ropenfd",
- ctx->version ? "negotiated" : "server",
- ctx->max_msg_size);
- return true;
- }
- uint32_t offsetof_end = needed_size;
- uint32_t offsetof_size = 0;
- MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
- MARSHAL_U8LE(ctx, 99);
- MARSHAL_U16LE(ctx, val->tag);
- MARSHAL_U8LE(ctx, val->qid.type & qt_masks[ctx->version]);
- MARSHAL_U32LE(ctx, val->qid.vers);
- MARSHAL_U64LE(ctx, val->qid.path);
- MARSHAL_U32LE(ctx, val->iounit);
- MARSHAL_U32LE(ctx, val->unixfd);
- return false;
-}
-
-#endif /* CONFIG_9P_ENABLE_9P2000_p9p */
-#if CONFIG_9P_ENABLE_9P2000_L
-static bool marshal_Rlerror(struct lib9p_ctx *ctx, struct lib9p_msg_Rlerror *val, struct _marshal_ret *ret) {
- uint32_t needed_size = 11;
- if (needed_size > ctx->max_msg_size) {
- lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")",
- "Rlerror",
- ctx->version ? "negotiated" : "server",
- ctx->max_msg_size);
- return true;
- }
- uint32_t offsetof_end = needed_size;
- uint32_t offsetof_size = 0;
- MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
- MARSHAL_U8LE(ctx, 7);
- MARSHAL_U16LE(ctx, val->tag);
- MARSHAL_U32LE(ctx, val->ecode);
- return false;
-}
-
-static bool marshal_Tstatfs(struct lib9p_ctx *ctx, struct lib9p_msg_Tstatfs *val, struct _marshal_ret *ret) {
- uint32_t needed_size = 11;
- if (needed_size > ctx->max_msg_size) {
- lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")",
- "Tstatfs",
- ctx->version ? "negotiated" : "client",
- ctx->max_msg_size);
- return true;
- }
- uint32_t offsetof_end = needed_size;
- uint32_t offsetof_size = 0;
- MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
- MARSHAL_U8LE(ctx, 8);
- MARSHAL_U16LE(ctx, val->tag);
- MARSHAL_U32LE(ctx, val->fid);
- return false;
-}
-
-static bool marshal_Rstatfs(struct lib9p_ctx *ctx, struct lib9p_msg_Rstatfs *val, struct _marshal_ret *ret) {
- uint32_t needed_size = 67;
- if (needed_size > ctx->max_msg_size) {
- lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")",
- "Rstatfs",
- ctx->version ? "negotiated" : "server",
- ctx->max_msg_size);
- return true;
- }
- uint32_t offsetof_end = needed_size;
- uint32_t offsetof_size = 0;
- MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
- MARSHAL_U8LE(ctx, 9);
- MARSHAL_U16LE(ctx, val->tag);
- MARSHAL_U32LE(ctx, val->type);
- MARSHAL_U32LE(ctx, val->bsize);
- MARSHAL_U64LE(ctx, val->blocks);
- MARSHAL_U64LE(ctx, val->bfree);
- MARSHAL_U64LE(ctx, val->bavail);
- MARSHAL_U64LE(ctx, val->files);
- MARSHAL_U64LE(ctx, val->ffree);
- MARSHAL_U64LE(ctx, val->fsid);
- MARSHAL_U32LE(ctx, val->namelen);
- return false;
-}
-
-static bool marshal_Tlopen(struct lib9p_ctx *ctx, struct lib9p_msg_Tlopen *val, struct _marshal_ret *ret) {
- uint32_t needed_size = 15;
- if (needed_size > ctx->max_msg_size) {
- lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")",
- "Tlopen",
- ctx->version ? "negotiated" : "client",
- ctx->max_msg_size);
- return true;
- }
- uint32_t offsetof_end = needed_size;
- uint32_t offsetof_size = 0;
- MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
- MARSHAL_U8LE(ctx, 12);
- MARSHAL_U16LE(ctx, val->tag);
- MARSHAL_U32LE(ctx, val->fid);
- MARSHAL_U32LE(ctx, val->flags & lo_masks[ctx->version]);
- return false;
-}
-
-static bool marshal_Rlopen(struct lib9p_ctx *ctx, struct lib9p_msg_Rlopen *val, struct _marshal_ret *ret) {
- uint32_t needed_size = 24;
- if (needed_size > ctx->max_msg_size) {
- lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")",
- "Rlopen",
- ctx->version ? "negotiated" : "server",
- ctx->max_msg_size);
- return true;
- }
- uint32_t offsetof_end = needed_size;
- uint32_t offsetof_size = 0;
- MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
- MARSHAL_U8LE(ctx, 13);
- MARSHAL_U16LE(ctx, val->tag);
- MARSHAL_U8LE(ctx, val->qid.type & qt_masks[ctx->version]);
- MARSHAL_U32LE(ctx, val->qid.vers);
- MARSHAL_U64LE(ctx, val->qid.path);
- MARSHAL_U32LE(ctx, val->iounit);
- return false;
-}
-
-static bool marshal_Tlcreate(struct lib9p_ctx *ctx, struct lib9p_msg_Tlcreate *val, struct _marshal_ret *ret) {
- uint32_t needed_size = 25 + val->name.len;
- if (needed_size > ctx->max_msg_size) {
- lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")",
- "Tlcreate",
- ctx->version ? "negotiated" : "client",
- ctx->max_msg_size);
- return true;
- }
- uint32_t offsetof_end = needed_size;
- uint32_t offsetof_size = 0;
- MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
- MARSHAL_U8LE(ctx, 14);
- MARSHAL_U16LE(ctx, val->tag);
- MARSHAL_U32LE(ctx, val->fid);
- MARSHAL_U16LE(ctx, val->name.len);
- MARSHAL_BYTES_ZEROCOPY(ctx, val->name.utf8, val->name.len);
- MARSHAL_U32LE(ctx, val->flags & lo_masks[ctx->version]);
- MARSHAL_U32LE(ctx, val->mode & mode_masks[ctx->version]);
- MARSHAL_U32LE(ctx, val->gid);
- return false;
-}
-
-static bool marshal_Rlcreate(struct lib9p_ctx *ctx, struct lib9p_msg_Rlcreate *val, struct _marshal_ret *ret) {
- uint32_t needed_size = 24;
- if (needed_size > ctx->max_msg_size) {
- lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")",
- "Rlcreate",
- ctx->version ? "negotiated" : "server",
- ctx->max_msg_size);
- return true;
- }
- uint32_t offsetof_end = needed_size;
- uint32_t offsetof_size = 0;
- MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
- MARSHAL_U8LE(ctx, 15);
- MARSHAL_U16LE(ctx, val->tag);
- MARSHAL_U8LE(ctx, val->qid.type & qt_masks[ctx->version]);
- MARSHAL_U32LE(ctx, val->qid.vers);
- MARSHAL_U64LE(ctx, val->qid.path);
- MARSHAL_U32LE(ctx, val->iounit);
- return false;
-}
-
-static bool marshal_Tsymlink(struct lib9p_ctx *ctx, struct lib9p_msg_Tsymlink *val, struct _marshal_ret *ret) {
- uint32_t needed_size = 19 + val->name.len + val->symtgt.len;
- if (needed_size > ctx->max_msg_size) {
- lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")",
- "Tsymlink",
- ctx->version ? "negotiated" : "client",
- ctx->max_msg_size);
- return true;
- }
- uint32_t offsetof_end = needed_size;
- uint32_t offsetof_size = 0;
- MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
- MARSHAL_U8LE(ctx, 16);
- MARSHAL_U16LE(ctx, val->tag);
- MARSHAL_U32LE(ctx, val->fid);
- MARSHAL_U16LE(ctx, val->name.len);
- MARSHAL_BYTES_ZEROCOPY(ctx, val->name.utf8, val->name.len);
- MARSHAL_U16LE(ctx, val->symtgt.len);
- MARSHAL_BYTES_ZEROCOPY(ctx, val->symtgt.utf8, val->symtgt.len);
- MARSHAL_U32LE(ctx, val->gid);
- return false;
-}
-
-static bool marshal_Rsymlink(struct lib9p_ctx *ctx, struct lib9p_msg_Rsymlink *val, struct _marshal_ret *ret) {
- uint32_t needed_size = 20;
- if (needed_size > ctx->max_msg_size) {
- lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")",
- "Rsymlink",
- ctx->version ? "negotiated" : "server",
- ctx->max_msg_size);
- return true;
- }
- uint32_t offsetof_end = needed_size;
- uint32_t offsetof_size = 0;
- MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
- MARSHAL_U8LE(ctx, 17);
- MARSHAL_U16LE(ctx, val->tag);
- MARSHAL_U8LE(ctx, val->qid.type & qt_masks[ctx->version]);
- MARSHAL_U32LE(ctx, val->qid.vers);
- MARSHAL_U64LE(ctx, val->qid.path);
- return false;
-}
-
-static bool marshal_Tmknod(struct lib9p_ctx *ctx, struct lib9p_msg_Tmknod *val, struct _marshal_ret *ret) {
- uint32_t needed_size = 29 + val->name.len;
- if (needed_size > ctx->max_msg_size) {
- lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")",
- "Tmknod",
- ctx->version ? "negotiated" : "client",
- ctx->max_msg_size);
- return true;
- }
- uint32_t offsetof_end = needed_size;
- uint32_t offsetof_size = 0;
- MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
- MARSHAL_U8LE(ctx, 18);
- MARSHAL_U16LE(ctx, val->tag);
- MARSHAL_U32LE(ctx, val->dfid);
- MARSHAL_U16LE(ctx, val->name.len);
- MARSHAL_BYTES_ZEROCOPY(ctx, val->name.utf8, val->name.len);
- MARSHAL_U32LE(ctx, val->mode & mode_masks[ctx->version]);
- MARSHAL_U32LE(ctx, val->major);
- MARSHAL_U32LE(ctx, val->minor);
- MARSHAL_U32LE(ctx, val->gid);
- return false;
-}
-
-static bool marshal_Rmknod(struct lib9p_ctx *ctx, struct lib9p_msg_Rmknod *val, struct _marshal_ret *ret) {
- uint32_t needed_size = 20;
- if (needed_size > ctx->max_msg_size) {
- lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")",
- "Rmknod",
- ctx->version ? "negotiated" : "server",
- ctx->max_msg_size);
- return true;
- }
- uint32_t offsetof_end = needed_size;
- uint32_t offsetof_size = 0;
- MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
- MARSHAL_U8LE(ctx, 19);
- MARSHAL_U16LE(ctx, val->tag);
- MARSHAL_U8LE(ctx, val->qid.type & qt_masks[ctx->version]);
- MARSHAL_U32LE(ctx, val->qid.vers);
- MARSHAL_U64LE(ctx, val->qid.path);
- return false;
-}
-
-static bool marshal_Trename(struct lib9p_ctx *ctx, struct lib9p_msg_Trename *val, struct _marshal_ret *ret) {
- uint32_t needed_size = 17 + val->name.len;
- if (needed_size > ctx->max_msg_size) {
- lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")",
- "Trename",
- ctx->version ? "negotiated" : "client",
- ctx->max_msg_size);
- return true;
- }
- uint32_t offsetof_end = needed_size;
- uint32_t offsetof_size = 0;
- MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
- MARSHAL_U8LE(ctx, 20);
- MARSHAL_U16LE(ctx, val->tag);
- MARSHAL_U32LE(ctx, val->fid);
- MARSHAL_U32LE(ctx, val->dfid);
- MARSHAL_U16LE(ctx, val->name.len);
- MARSHAL_BYTES_ZEROCOPY(ctx, val->name.utf8, val->name.len);
- return false;
-}
-
-static bool marshal_Rrename(struct lib9p_ctx *ctx, struct lib9p_msg_Rrename *val, struct _marshal_ret *ret) {
- uint32_t needed_size = 7;
- if (needed_size > ctx->max_msg_size) {
- lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")",
- "Rrename",
- ctx->version ? "negotiated" : "server",
- ctx->max_msg_size);
- return true;
- }
- uint32_t offsetof_end = needed_size;
- uint32_t offsetof_size = 0;
- MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
- MARSHAL_U8LE(ctx, 21);
- MARSHAL_U16LE(ctx, val->tag);
- return false;
-}
-
-static bool marshal_Treadlink(struct lib9p_ctx *ctx, struct lib9p_msg_Treadlink *val, struct _marshal_ret *ret) {
- uint32_t needed_size = 11;
- if (needed_size > ctx->max_msg_size) {
- lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")",
- "Treadlink",
- ctx->version ? "negotiated" : "client",
- ctx->max_msg_size);
- return true;
- }
- uint32_t offsetof_end = needed_size;
- uint32_t offsetof_size = 0;
- MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
- MARSHAL_U8LE(ctx, 22);
- MARSHAL_U16LE(ctx, val->tag);
- MARSHAL_U32LE(ctx, val->fid);
- return false;
-}
-
-static bool marshal_Rreadlink(struct lib9p_ctx *ctx, struct lib9p_msg_Rreadlink *val, struct _marshal_ret *ret) {
- uint32_t needed_size = 9 + val->target.len;
- if (needed_size > ctx->max_msg_size) {
- lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")",
- "Rreadlink",
- ctx->version ? "negotiated" : "server",
- ctx->max_msg_size);
- return true;
- }
- uint32_t offsetof_end = needed_size;
- uint32_t offsetof_size = 0;
- MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
- MARSHAL_U8LE(ctx, 23);
- MARSHAL_U16LE(ctx, val->tag);
- MARSHAL_U16LE(ctx, val->target.len);
- MARSHAL_BYTES_ZEROCOPY(ctx, val->target.utf8, val->target.len);
- return false;
-}
-
-static bool marshal_Tgetattr(struct lib9p_ctx *ctx, struct lib9p_msg_Tgetattr *val, struct _marshal_ret *ret) {
- uint32_t needed_size = 19;
- if (needed_size > ctx->max_msg_size) {
- lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")",
- "Tgetattr",
- ctx->version ? "negotiated" : "client",
- ctx->max_msg_size);
- return true;
- }
- uint32_t offsetof_end = needed_size;
- uint32_t offsetof_size = 0;
- MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
- MARSHAL_U8LE(ctx, 24);
- MARSHAL_U16LE(ctx, val->tag);
- MARSHAL_U32LE(ctx, val->fid);
- MARSHAL_U64LE(ctx, val->request_mask & getattr_masks[ctx->version]);
- return false;
-}
-
-static bool marshal_Rgetattr(struct lib9p_ctx *ctx, struct lib9p_msg_Rgetattr *val, struct _marshal_ret *ret) {
- uint32_t needed_size = 160;
- if (needed_size > ctx->max_msg_size) {
- lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")",
- "Rgetattr",
- ctx->version ? "negotiated" : "server",
- ctx->max_msg_size);
- return true;
- }
- uint32_t offsetof_end = needed_size;
- uint32_t offsetof_size = 0;
- MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
- MARSHAL_U8LE(ctx, 25);
- MARSHAL_U16LE(ctx, val->tag);
- MARSHAL_U64LE(ctx, val->valid & getattr_masks[ctx->version]);
- MARSHAL_U8LE(ctx, val->qid.type & qt_masks[ctx->version]);
- MARSHAL_U32LE(ctx, val->qid.vers);
- MARSHAL_U64LE(ctx, val->qid.path);
- MARSHAL_U32LE(ctx, val->mode & mode_masks[ctx->version]);
- MARSHAL_U32LE(ctx, val->uid);
- MARSHAL_U32LE(ctx, val->gid);
- MARSHAL_U64LE(ctx, val->nlink);
- MARSHAL_U64LE(ctx, val->rdev);
- MARSHAL_U64LE(ctx, val->filesize);
- MARSHAL_U64LE(ctx, val->blksize);
- MARSHAL_U64LE(ctx, val->blocks);
- MARSHAL_U64LE(ctx, val->atime_sec);
- MARSHAL_U64LE(ctx, val->atime_nsec);
- MARSHAL_U64LE(ctx, val->mtime_sec);
- MARSHAL_U64LE(ctx, val->mtime_nsec);
- MARSHAL_U64LE(ctx, val->ctime_sec);
- MARSHAL_U64LE(ctx, val->ctime_nsec);
- MARSHAL_U64LE(ctx, val->btime_sec);
- MARSHAL_U64LE(ctx, val->btime_nsec);
- MARSHAL_U64LE(ctx, val->gen);
- MARSHAL_U64LE(ctx, val->data_version);
- return false;
-}
-
-static bool marshal_Tsetattr(struct lib9p_ctx *ctx, struct lib9p_msg_Tsetattr *val, struct _marshal_ret *ret) {
- uint32_t needed_size = 67;
- if (needed_size > ctx->max_msg_size) {
- lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")",
- "Tsetattr",
- ctx->version ? "negotiated" : "client",
- ctx->max_msg_size);
- return true;
- }
- uint32_t offsetof_end = needed_size;
- uint32_t offsetof_size = 0;
- MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
- MARSHAL_U8LE(ctx, 26);
- MARSHAL_U16LE(ctx, val->tag);
- MARSHAL_U32LE(ctx, val->fid);
- MARSHAL_U32LE(ctx, val->valid & setattr_masks[ctx->version]);
- MARSHAL_U32LE(ctx, val->mode & mode_masks[ctx->version]);
- MARSHAL_U32LE(ctx, val->uid);
- MARSHAL_U32LE(ctx, val->gid);
- MARSHAL_U64LE(ctx, val->filesize);
- MARSHAL_U64LE(ctx, val->atime_sec);
- MARSHAL_U64LE(ctx, val->atime_nsec);
- MARSHAL_U64LE(ctx, val->mtime_sec);
- MARSHAL_U64LE(ctx, val->mtime_nsec);
- return false;
-}
-
-static bool marshal_Rsetattr(struct lib9p_ctx *ctx, struct lib9p_msg_Rsetattr *val, struct _marshal_ret *ret) {
- uint32_t needed_size = 7;
- if (needed_size > ctx->max_msg_size) {
- lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")",
- "Rsetattr",
- ctx->version ? "negotiated" : "server",
- ctx->max_msg_size);
- return true;
- }
- uint32_t offsetof_end = needed_size;
- uint32_t offsetof_size = 0;
- MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
- MARSHAL_U8LE(ctx, 27);
- MARSHAL_U16LE(ctx, val->tag);
- return false;
-}
-
-static bool marshal_Txattrwalk(struct lib9p_ctx *ctx, struct lib9p_msg_Txattrwalk *val, struct _marshal_ret *ret) {
- uint32_t needed_size = 17 + val->name.len;
- if (needed_size > ctx->max_msg_size) {
- lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")",
- "Txattrwalk",
- ctx->version ? "negotiated" : "client",
- ctx->max_msg_size);
- return true;
- }
- uint32_t offsetof_end = needed_size;
- uint32_t offsetof_size = 0;
- MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
- MARSHAL_U8LE(ctx, 30);
- MARSHAL_U16LE(ctx, val->tag);
- MARSHAL_U32LE(ctx, val->fid);
- MARSHAL_U32LE(ctx, val->newfid);
- MARSHAL_U16LE(ctx, val->name.len);
- MARSHAL_BYTES_ZEROCOPY(ctx, val->name.utf8, val->name.len);
- return false;
-}
-
-static bool marshal_Rxattrwalk(struct lib9p_ctx *ctx, struct lib9p_msg_Rxattrwalk *val, struct _marshal_ret *ret) {
- uint32_t needed_size = 15;
- if (needed_size > ctx->max_msg_size) {
- lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")",
- "Rxattrwalk",
- ctx->version ? "negotiated" : "server",
- ctx->max_msg_size);
- return true;
- }
- uint32_t offsetof_end = needed_size;
- uint32_t offsetof_size = 0;
- MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
- MARSHAL_U8LE(ctx, 31);
- MARSHAL_U16LE(ctx, val->tag);
- MARSHAL_U64LE(ctx, val->attr_size);
- return false;
-}
-
-static bool marshal_Txattrcreate(struct lib9p_ctx *ctx, struct lib9p_msg_Txattrcreate *val, struct _marshal_ret *ret) {
- uint32_t needed_size = 25 + val->name.len;
- if (needed_size > ctx->max_msg_size) {
- lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")",
- "Txattrcreate",
- ctx->version ? "negotiated" : "client",
- ctx->max_msg_size);
- return true;
- }
- uint32_t offsetof_end = needed_size;
- uint32_t offsetof_size = 0;
- MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
- MARSHAL_U8LE(ctx, 32);
- MARSHAL_U16LE(ctx, val->tag);
- MARSHAL_U32LE(ctx, val->fid);
- MARSHAL_U16LE(ctx, val->name.len);
- MARSHAL_BYTES_ZEROCOPY(ctx, val->name.utf8, val->name.len);
- MARSHAL_U64LE(ctx, val->attr_size);
- MARSHAL_U32LE(ctx, val->flags);
- return false;
-}
-
-static bool marshal_Rxattrcreate(struct lib9p_ctx *ctx, struct lib9p_msg_Rxattrcreate *val, struct _marshal_ret *ret) {
- uint32_t needed_size = 7;
- if (needed_size > ctx->max_msg_size) {
- lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")",
- "Rxattrcreate",
- ctx->version ? "negotiated" : "server",
- ctx->max_msg_size);
- return true;
- }
- uint32_t offsetof_end = needed_size;
- uint32_t offsetof_size = 0;
- MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
- MARSHAL_U8LE(ctx, 33);
- MARSHAL_U16LE(ctx, val->tag);
- return false;
-}
-
-static bool marshal_Treaddir(struct lib9p_ctx *ctx, struct lib9p_msg_Treaddir *val, struct _marshal_ret *ret) {
- uint32_t needed_size = 23;
- if (needed_size > ctx->max_msg_size) {
- lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")",
- "Treaddir",
- ctx->version ? "negotiated" : "client",
- ctx->max_msg_size);
- return true;
- }
- uint32_t offsetof_end = needed_size;
- uint32_t offsetof_size = 0;
- MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
- MARSHAL_U8LE(ctx, 40);
- MARSHAL_U16LE(ctx, val->tag);
- MARSHAL_U32LE(ctx, val->fid);
- MARSHAL_U64LE(ctx, val->offset);
- MARSHAL_U32LE(ctx, val->count);
- return false;
-}
-
-static bool marshal_Rreaddir(struct lib9p_ctx *ctx, struct lib9p_msg_Rreaddir *val, struct _marshal_ret *ret) {
- uint64_t needed_size = 11 + val->count;
- if (needed_size > (uint64_t)(ctx->max_msg_size)) {
- lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")",
- "Rreaddir",
- ctx->version ? "negotiated" : "server",
- ctx->max_msg_size);
- return true;
- }
- uint32_t offsetof_end = (uint32_t)needed_size;
- uint32_t offsetof_size = 0;
- MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
- MARSHAL_U8LE(ctx, 41);
- MARSHAL_U16LE(ctx, val->tag);
- MARSHAL_U32LE(ctx, val->count);
- MARSHAL_BYTES_ZEROCOPY(ctx, val->data, val->count);
- return false;
-}
-
-static bool marshal_Tfsync(struct lib9p_ctx *ctx, struct lib9p_msg_Tfsync *val, struct _marshal_ret *ret) {
- uint32_t needed_size = 15;
- if (needed_size > ctx->max_msg_size) {
- lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")",
- "Tfsync",
- ctx->version ? "negotiated" : "client",
- ctx->max_msg_size);
- return true;
- }
- uint32_t offsetof_end = needed_size;
- uint32_t offsetof_size = 0;
- MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
- MARSHAL_U8LE(ctx, 50);
- MARSHAL_U16LE(ctx, val->tag);
- MARSHAL_U32LE(ctx, val->fid);
- MARSHAL_U32LE(ctx, val->datasync);
- return false;
-}
-
-static bool marshal_Rfsync(struct lib9p_ctx *ctx, struct lib9p_msg_Rfsync *val, struct _marshal_ret *ret) {
- uint32_t needed_size = 7;
- if (needed_size > ctx->max_msg_size) {
- lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")",
- "Rfsync",
- ctx->version ? "negotiated" : "server",
- ctx->max_msg_size);
- return true;
- }
- uint32_t offsetof_end = needed_size;
- uint32_t offsetof_size = 0;
- MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
- MARSHAL_U8LE(ctx, 51);
- MARSHAL_U16LE(ctx, val->tag);
- return false;
-}
-
-static bool marshal_Tlock(struct lib9p_ctx *ctx, struct lib9p_msg_Tlock *val, struct _marshal_ret *ret) {
- uint32_t needed_size = 38 + val->client_id.len;
- if (needed_size > ctx->max_msg_size) {
- lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")",
- "Tlock",
- ctx->version ? "negotiated" : "client",
- ctx->max_msg_size);
- return true;
- }
- uint32_t offsetof_end = needed_size;
- uint32_t offsetof_size = 0;
- MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
- MARSHAL_U8LE(ctx, 52);
- MARSHAL_U16LE(ctx, val->tag);
- MARSHAL_U32LE(ctx, val->fid);
- MARSHAL_U8LE(ctx, val->type);
- MARSHAL_U32LE(ctx, val->flags & lock_flags_masks[ctx->version]);
- MARSHAL_U64LE(ctx, val->start);
- MARSHAL_U64LE(ctx, val->length);
- MARSHAL_U32LE(ctx, val->proc_id);
- MARSHAL_U16LE(ctx, val->client_id.len);
- MARSHAL_BYTES_ZEROCOPY(ctx, val->client_id.utf8, val->client_id.len);
- return false;
-}
-
-static bool marshal_Rlock(struct lib9p_ctx *ctx, struct lib9p_msg_Rlock *val, struct _marshal_ret *ret) {
- uint32_t needed_size = 8;
- if (needed_size > ctx->max_msg_size) {
- lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")",
- "Rlock",
- ctx->version ? "negotiated" : "server",
- ctx->max_msg_size);
- return true;
- }
- uint32_t offsetof_end = needed_size;
- uint32_t offsetof_size = 0;
- MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
- MARSHAL_U8LE(ctx, 53);
- MARSHAL_U16LE(ctx, val->tag);
- MARSHAL_U8LE(ctx, val->status);
- return false;
-}
-
-static bool marshal_Tgetlock(struct lib9p_ctx *ctx, struct lib9p_msg_Tgetlock *val, struct _marshal_ret *ret) {
- uint32_t needed_size = 34 + val->client_id.len;
- if (needed_size > ctx->max_msg_size) {
- lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")",
- "Tgetlock",
- ctx->version ? "negotiated" : "client",
- ctx->max_msg_size);
- return true;
- }
- uint32_t offsetof_end = needed_size;
- uint32_t offsetof_size = 0;
- MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
- MARSHAL_U8LE(ctx, 54);
- MARSHAL_U16LE(ctx, val->tag);
- MARSHAL_U32LE(ctx, val->fid);
- MARSHAL_U8LE(ctx, val->type);
- MARSHAL_U64LE(ctx, val->start);
- MARSHAL_U64LE(ctx, val->length);
- MARSHAL_U32LE(ctx, val->proc_id);
- MARSHAL_U16LE(ctx, val->client_id.len);
- MARSHAL_BYTES_ZEROCOPY(ctx, val->client_id.utf8, val->client_id.len);
- return false;
-}
-
-static bool marshal_Rgetlock(struct lib9p_ctx *ctx, struct lib9p_msg_Rgetlock *val, struct _marshal_ret *ret) {
- uint32_t needed_size = 30 + val->client_id.len;
- if (needed_size > ctx->max_msg_size) {
- lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")",
- "Rgetlock",
- ctx->version ? "negotiated" : "server",
- ctx->max_msg_size);
- return true;
- }
- uint32_t offsetof_end = needed_size;
- uint32_t offsetof_size = 0;
- MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
- MARSHAL_U8LE(ctx, 55);
- MARSHAL_U16LE(ctx, val->tag);
- MARSHAL_U8LE(ctx, val->type);
- MARSHAL_U64LE(ctx, val->start);
- MARSHAL_U64LE(ctx, val->length);
- MARSHAL_U32LE(ctx, val->proc_id);
- MARSHAL_U16LE(ctx, val->client_id.len);
- MARSHAL_BYTES_ZEROCOPY(ctx, val->client_id.utf8, val->client_id.len);
- return false;
-}
-
-static bool marshal_Tlink(struct lib9p_ctx *ctx, struct lib9p_msg_Tlink *val, struct _marshal_ret *ret) {
- uint32_t needed_size = 17 + val->name.len;
- if (needed_size > ctx->max_msg_size) {
- lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")",
- "Tlink",
- ctx->version ? "negotiated" : "client",
- ctx->max_msg_size);
- return true;
- }
- uint32_t offsetof_end = needed_size;
- uint32_t offsetof_size = 0;
- MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
- MARSHAL_U8LE(ctx, 70);
- MARSHAL_U16LE(ctx, val->tag);
- MARSHAL_U32LE(ctx, val->dfid);
- MARSHAL_U32LE(ctx, val->fid);
- MARSHAL_U16LE(ctx, val->name.len);
- MARSHAL_BYTES_ZEROCOPY(ctx, val->name.utf8, val->name.len);
- return false;
-}
-
-static bool marshal_Rlink(struct lib9p_ctx *ctx, struct lib9p_msg_Rlink *val, struct _marshal_ret *ret) {
- uint32_t needed_size = 7;
- if (needed_size > ctx->max_msg_size) {
- lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")",
- "Rlink",
- ctx->version ? "negotiated" : "server",
- ctx->max_msg_size);
- return true;
- }
- uint32_t offsetof_end = needed_size;
- uint32_t offsetof_size = 0;
- MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
- MARSHAL_U8LE(ctx, 71);
- MARSHAL_U16LE(ctx, val->tag);
- return false;
-}
-
-static bool marshal_Tmkdir(struct lib9p_ctx *ctx, struct lib9p_msg_Tmkdir *val, struct _marshal_ret *ret) {
- uint32_t needed_size = 21 + val->name.len;
- if (needed_size > ctx->max_msg_size) {
- lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")",
- "Tmkdir",
- ctx->version ? "negotiated" : "client",
- ctx->max_msg_size);
- return true;
- }
- uint32_t offsetof_end = needed_size;
- uint32_t offsetof_size = 0;
- MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
- MARSHAL_U8LE(ctx, 72);
- MARSHAL_U16LE(ctx, val->tag);
- MARSHAL_U32LE(ctx, val->dfid);
- MARSHAL_U16LE(ctx, val->name.len);
- MARSHAL_BYTES_ZEROCOPY(ctx, val->name.utf8, val->name.len);
- MARSHAL_U32LE(ctx, val->mode & mode_masks[ctx->version]);
- MARSHAL_U32LE(ctx, val->gid);
- return false;
-}
-
-static bool marshal_Rmkdir(struct lib9p_ctx *ctx, struct lib9p_msg_Rmkdir *val, struct _marshal_ret *ret) {
- uint32_t needed_size = 20;
- if (needed_size > ctx->max_msg_size) {
- lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")",
- "Rmkdir",
- ctx->version ? "negotiated" : "server",
- ctx->max_msg_size);
- return true;
- }
- uint32_t offsetof_end = needed_size;
- uint32_t offsetof_size = 0;
- MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
- MARSHAL_U8LE(ctx, 73);
- MARSHAL_U16LE(ctx, val->tag);
- MARSHAL_U8LE(ctx, val->qid.type & qt_masks[ctx->version]);
- MARSHAL_U32LE(ctx, val->qid.vers);
- MARSHAL_U64LE(ctx, val->qid.path);
- return false;
-}
-
-static bool marshal_Trenameat(struct lib9p_ctx *ctx, struct lib9p_msg_Trenameat *val, struct _marshal_ret *ret) {
- uint32_t needed_size = 19 + val->oldname.len + val->newname.len;
- if (needed_size > ctx->max_msg_size) {
- lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")",
- "Trenameat",
- ctx->version ? "negotiated" : "client",
- ctx->max_msg_size);
- return true;
- }
- uint32_t offsetof_end = needed_size;
- uint32_t offsetof_size = 0;
- MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
- MARSHAL_U8LE(ctx, 74);
- MARSHAL_U16LE(ctx, val->tag);
- MARSHAL_U32LE(ctx, val->olddirfid);
- MARSHAL_U16LE(ctx, val->oldname.len);
- MARSHAL_BYTES_ZEROCOPY(ctx, val->oldname.utf8, val->oldname.len);
- MARSHAL_U32LE(ctx, val->newdirfid);
- MARSHAL_U16LE(ctx, val->newname.len);
- MARSHAL_BYTES_ZEROCOPY(ctx, val->newname.utf8, val->newname.len);
- return false;
-}
-
-static bool marshal_Rrenameat(struct lib9p_ctx *ctx, struct lib9p_msg_Rrenameat *val, struct _marshal_ret *ret) {
- uint32_t needed_size = 7;
- if (needed_size > ctx->max_msg_size) {
- lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")",
- "Rrenameat",
- ctx->version ? "negotiated" : "server",
- ctx->max_msg_size);
- return true;
- }
- uint32_t offsetof_end = needed_size;
- uint32_t offsetof_size = 0;
- MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
- MARSHAL_U8LE(ctx, 75);
- MARSHAL_U16LE(ctx, val->tag);
- return false;
-}
-
-static bool marshal_Tunlinkat(struct lib9p_ctx *ctx, struct lib9p_msg_Tunlinkat *val, struct _marshal_ret *ret) {
- uint32_t needed_size = 17 + val->name.len;
- if (needed_size > ctx->max_msg_size) {
- lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")",
- "Tunlinkat",
- ctx->version ? "negotiated" : "client",
- ctx->max_msg_size);
- return true;
- }
- uint32_t offsetof_end = needed_size;
- uint32_t offsetof_size = 0;
- MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
- MARSHAL_U8LE(ctx, 76);
- MARSHAL_U16LE(ctx, val->tag);
- MARSHAL_U32LE(ctx, val->dirfd);
- MARSHAL_U16LE(ctx, val->name.len);
- MARSHAL_BYTES_ZEROCOPY(ctx, val->name.utf8, val->name.len);
- MARSHAL_U32LE(ctx, val->flags);
- return false;
-}
-
-static bool marshal_Runlinkat(struct lib9p_ctx *ctx, struct lib9p_msg_Runlinkat *val, struct _marshal_ret *ret) {
- uint32_t needed_size = 7;
- if (needed_size > ctx->max_msg_size) {
- lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")",
- "Runlinkat",
- ctx->version ? "negotiated" : "server",
- ctx->max_msg_size);
- return true;
- }
- uint32_t offsetof_end = needed_size;
- uint32_t offsetof_size = 0;
- MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
- MARSHAL_U8LE(ctx, 77);
- MARSHAL_U16LE(ctx, val->tag);
- return false;
-}
-
-#endif /* CONFIG_9P_ENABLE_9P2000_L */
-#if CONFIG_9P_ENABLE_9P2000_e
-static bool marshal_Tsession(struct lib9p_ctx *ctx, struct lib9p_msg_Tsession *val, struct _marshal_ret *ret) {
- uint32_t needed_size = 15;
- if (needed_size > ctx->max_msg_size) {
- lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")",
- "Tsession",
- ctx->version ? "negotiated" : "client",
- ctx->max_msg_size);
- return true;
- }
- uint32_t offsetof_end = needed_size;
- uint32_t offsetof_size = 0;
- MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
- MARSHAL_U8LE(ctx, 150);
- MARSHAL_U16LE(ctx, val->tag);
- MARSHAL_U64LE(ctx, val->key);
- return false;
-}
-
-static bool marshal_Rsession(struct lib9p_ctx *ctx, struct lib9p_msg_Rsession *val, struct _marshal_ret *ret) {
- uint32_t needed_size = 7;
- if (needed_size > ctx->max_msg_size) {
- lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")",
- "Rsession",
- ctx->version ? "negotiated" : "server",
- ctx->max_msg_size);
- return true;
- }
- uint32_t offsetof_end = needed_size;
- uint32_t offsetof_size = 0;
- MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
- MARSHAL_U8LE(ctx, 151);
- MARSHAL_U16LE(ctx, val->tag);
- return false;
-}
-
-static bool marshal_Tsread(struct lib9p_ctx *ctx, struct lib9p_msg_Tsread *val, struct _marshal_ret *ret) {
- uint64_t needed_size = 13;
- for (uint16_t i = 0; i < val->nwname; i++) {
- needed_size += 2 + val->wname[i].len;
- }
- if (needed_size > (uint64_t)(ctx->max_msg_size)) {
- lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")",
- "Tsread",
- ctx->version ? "negotiated" : "client",
- ctx->max_msg_size);
- return true;
- }
- uint32_t offsetof_end = (uint32_t)needed_size;
- uint32_t offsetof_size = 0;
- MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
- MARSHAL_U8LE(ctx, 152);
- MARSHAL_U16LE(ctx, val->tag);
- MARSHAL_U32LE(ctx, val->fid);
- MARSHAL_U16LE(ctx, val->nwname);
- for (uint16_t i = 0; i < val->nwname; i++) {
- MARSHAL_U16LE(ctx, val->wname[i].len);
- MARSHAL_BYTES_ZEROCOPY(ctx, val->wname[i].utf8, val->wname[i].len);
- }
- return false;
-}
-
-static bool marshal_Rsread(struct lib9p_ctx *ctx, struct lib9p_msg_Rsread *val, struct _marshal_ret *ret) {
- uint64_t needed_size = 11 + val->count;
- if (needed_size > (uint64_t)(ctx->max_msg_size)) {
- lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")",
- "Rsread",
- ctx->version ? "negotiated" : "server",
- ctx->max_msg_size);
- return true;
- }
- uint32_t offsetof_end = (uint32_t)needed_size;
- uint32_t offsetof_size = 0;
- MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
- MARSHAL_U8LE(ctx, 153);
- MARSHAL_U16LE(ctx, val->tag);
- MARSHAL_U32LE(ctx, val->count);
- MARSHAL_BYTES_ZEROCOPY(ctx, val->data, val->count);
- return false;
-}
-
-static bool marshal_Tswrite(struct lib9p_ctx *ctx, struct lib9p_msg_Tswrite *val, struct _marshal_ret *ret) {
- uint64_t needed_size = 17 + val->count;
- for (uint16_t i = 0; i < val->nwname; i++) {
- needed_size += 2 + val->wname[i].len;
- }
- if (needed_size > (uint64_t)(ctx->max_msg_size)) {
- lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")",
- "Tswrite",
- ctx->version ? "negotiated" : "client",
- ctx->max_msg_size);
- return true;
- }
- uint32_t offsetof_end = (uint32_t)needed_size;
- uint32_t offsetof_size = 0;
- MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
- MARSHAL_U8LE(ctx, 154);
- MARSHAL_U16LE(ctx, val->tag);
- MARSHAL_U32LE(ctx, val->fid);
- MARSHAL_U16LE(ctx, val->nwname);
- for (uint16_t i = 0; i < val->nwname; i++) {
- MARSHAL_U16LE(ctx, val->wname[i].len);
- MARSHAL_BYTES_ZEROCOPY(ctx, val->wname[i].utf8, val->wname[i].len);
- }
- MARSHAL_U32LE(ctx, val->count);
- MARSHAL_BYTES_ZEROCOPY(ctx, val->data, val->count);
- return false;
-}
-
-static bool marshal_Rswrite(struct lib9p_ctx *ctx, struct lib9p_msg_Rswrite *val, struct _marshal_ret *ret) {
- uint32_t needed_size = 11;
- if (needed_size > ctx->max_msg_size) {
- lib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")",
- "Rswrite",
- ctx->version ? "negotiated" : "server",
- ctx->max_msg_size);
- return true;
- }
- uint32_t offsetof_end = needed_size;
- uint32_t offsetof_size = 0;
- MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
- MARSHAL_U8LE(ctx, 155);
- MARSHAL_U16LE(ctx, val->tag);
- MARSHAL_U32LE(ctx, val->count);
- return false;
-}
-#endif /* CONFIG_9P_ENABLE_9P2000_e */
-
-/* *_format *******************************************************************/
-
-#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u
-static void lib9p_tag_format(lib9p_tag_t *self, struct fmt_state *state) {
- switch (*self) {
- case LIB9P_TAG_NOTAG:
- fmt_state_puts(state, "NOTAG");
- break;
- default:
- fmt_state_printf(state, "%"PRIu16, *self);
- }
-}
-
-static void lib9p_fid_format(lib9p_fid_t *self, struct fmt_state *state) {
- switch (*self) {
- case LIB9P_FID_NOFID:
- fmt_state_puts(state, "NOFID");
- break;
- default:
- fmt_state_printf(state, "%"PRIu32, *self);
- }
-}
-
-static void lib9p_s_format(struct lib9p_s *self, struct fmt_state *state) {
- /* https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47781 */
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wformat"
-#pragma GCC diagnostic ignored "-Wformat-extra-args"
- fmt_state_printf(state, "%.*q", self->len, self->utf8);
-#pragma GCC diagnostic pop
-}
-
-#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */
-#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u
-static void lib9p_dm_format(lib9p_dm_t *self, struct fmt_state *state) {
- bool empty = true;
- fmt_state_putchar(state, '(');
- if ((*self & ~((lib9p_dm_t)0777)) & (UINT32_C(1)<<31)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "DIR");
- empty = false;
- }
- if ((*self & ~((lib9p_dm_t)0777)) & (UINT32_C(1)<<30)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "APPEND");
- empty = false;
- }
- if ((*self & ~((lib9p_dm_t)0777)) & (UINT32_C(1)<<29)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "EXCL");
- empty = false;
- }
- if ((*self & ~((lib9p_dm_t)0777)) & (UINT32_C(1)<<28)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "_PLAN9_MOUNT");
- empty = false;
- }
- if ((*self & ~((lib9p_dm_t)0777)) & (UINT32_C(1)<<27)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "AUTH");
- empty = false;
- }
- if ((*self & ~((lib9p_dm_t)0777)) & (UINT32_C(1)<<26)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "TMP");
- empty = false;
- }
- if ((*self & ~((lib9p_dm_t)0777)) & (UINT32_C(1)<<25)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<25");
- empty = false;
- }
- if ((*self & ~((lib9p_dm_t)0777)) & (UINT32_C(1)<<24)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<24");
- empty = false;
- }
- if ((*self & ~((lib9p_dm_t)0777)) & (UINT32_C(1)<<23)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "DEVICE");
- empty = false;
- }
- if ((*self & ~((lib9p_dm_t)0777)) & (UINT32_C(1)<<22)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<22");
- empty = false;
- }
- if ((*self & ~((lib9p_dm_t)0777)) & (UINT32_C(1)<<21)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "PIPE");
- empty = false;
- }
- if ((*self & ~((lib9p_dm_t)0777)) & (UINT32_C(1)<<20)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "SOCKET");
- empty = false;
- }
- if ((*self & ~((lib9p_dm_t)0777)) & (UINT32_C(1)<<19)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "SETUID");
- empty = false;
- }
- if ((*self & ~((lib9p_dm_t)0777)) & (UINT32_C(1)<<18)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "SETGID");
- empty = false;
- }
- if ((*self & ~((lib9p_dm_t)0777)) & (UINT32_C(1)<<17)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<17");
- empty = false;
- }
- if ((*self & ~((lib9p_dm_t)0777)) & (UINT32_C(1)<<16)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<16");
- empty = false;
- }
- if ((*self & ~((lib9p_dm_t)0777)) & (UINT32_C(1)<<15)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<15");
- empty = false;
- }
- if ((*self & ~((lib9p_dm_t)0777)) & (UINT32_C(1)<<14)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<14");
- empty = false;
- }
- if ((*self & ~((lib9p_dm_t)0777)) & (UINT32_C(1)<<13)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<13");
- empty = false;
- }
- if ((*self & ~((lib9p_dm_t)0777)) & (UINT32_C(1)<<12)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<12");
- empty = false;
- }
- if ((*self & ~((lib9p_dm_t)0777)) & (UINT32_C(1)<<11)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<11");
- empty = false;
- }
- if ((*self & ~((lib9p_dm_t)0777)) & (UINT32_C(1)<<10)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<10");
- empty = false;
- }
- if ((*self & ~((lib9p_dm_t)0777)) & (UINT32_C(1)<<9)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<9");
- empty = false;
- }
- if ((*self & ~((lib9p_dm_t)0777)) & (UINT32_C(1)<<8)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "OWNER_R");
- empty = false;
- }
- if ((*self & ~((lib9p_dm_t)0777)) & (UINT32_C(1)<<7)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "OWNER_W");
- empty = false;
- }
- if ((*self & ~((lib9p_dm_t)0777)) & (UINT32_C(1)<<6)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "OWNER_X");
- empty = false;
- }
- if ((*self & ~((lib9p_dm_t)0777)) & (UINT32_C(1)<<5)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "GROUP_R");
- empty = false;
- }
- if ((*self & ~((lib9p_dm_t)0777)) & (UINT32_C(1)<<4)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "GROUP_W");
- empty = false;
- }
- if ((*self & ~((lib9p_dm_t)0777)) & (UINT32_C(1)<<3)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "GROUP_X");
- empty = false;
- }
- if ((*self & ~((lib9p_dm_t)0777)) & (UINT32_C(1)<<2)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "OTHER_R");
- empty = false;
- }
- if ((*self & ~((lib9p_dm_t)0777)) & (UINT32_C(1)<<1)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "OTHER_W");
- empty = false;
- }
- if ((*self & ~((lib9p_dm_t)0777)) & (UINT32_C(1)<<0)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "OTHER_X");
- empty = false;
- }
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_printf(state, "%#04"PRIo32, *self & 0777);
- fmt_state_putchar(state, ')');
-}
-
-#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */
-#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u
-static void lib9p_qt_format(lib9p_qt_t *self, struct fmt_state *state) {
- bool empty = true;
- fmt_state_putchar(state, '(');
- if (*self & (UINT8_C(1)<<7)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "DIR");
- empty = false;
- }
- if (*self & (UINT8_C(1)<<6)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "APPEND");
- empty = false;
- }
- if (*self & (UINT8_C(1)<<5)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "EXCL");
- empty = false;
- }
- if (*self & (UINT8_C(1)<<4)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "_PLAN9_MOUNT");
- empty = false;
- }
- if (*self & (UINT8_C(1)<<3)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "AUTH");
- empty = false;
- }
- if (*self & (UINT8_C(1)<<2)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "TMP");
- empty = false;
- }
- if (*self & (UINT8_C(1)<<1)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "SYMLINK");
- empty = false;
- }
- if (*self & (UINT8_C(1)<<0)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<0");
- empty = false;
- }
- if (empty)
- fmt_state_putchar(state, '0');
- fmt_state_putchar(state, ')');
-}
-
-static void lib9p_qid_format(struct lib9p_qid *self, struct fmt_state *state) {
- fmt_state_putchar(state, '{');
- fmt_state_puts(state, " type=");
- lib9p_qt_format(&self->type, state);
- fmt_state_puts(state, " vers=");
- fmt_state_printf(state, "%"PRIu32, self->vers);
- fmt_state_puts(state, " path=");
- fmt_state_printf(state, "%"PRIu64, self->path);
- fmt_state_puts(state, " }");
-}
-
-#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */
-#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u
-static void lib9p_stat_format(struct lib9p_stat *self, struct fmt_state *state) {
- fmt_state_putchar(state, '{');
- fmt_state_puts(state, " kern_type=");
- fmt_state_printf(state, "%"PRIu16, self->kern_type);
- fmt_state_puts(state, " kern_dev=");
- fmt_state_printf(state, "%"PRIu32, self->kern_dev);
- fmt_state_puts(state, " file_qid=");
- lib9p_qid_format(&self->file_qid, state);
- fmt_state_puts(state, " file_mode=");
- lib9p_dm_format(&self->file_mode, state);
- fmt_state_puts(state, " file_atime=");
- fmt_state_printf(state, "%"PRIu32, self->file_atime);
- fmt_state_puts(state, " file_mtime=");
- fmt_state_printf(state, "%"PRIu32, self->file_mtime);
- fmt_state_puts(state, " file_size=");
- fmt_state_printf(state, "%"PRIu64, self->file_size);
- fmt_state_puts(state, " file_name=");
- lib9p_s_format(&self->file_name, state);
- fmt_state_puts(state, " file_owner_uid=");
- lib9p_s_format(&self->file_owner_uid, state);
- fmt_state_puts(state, " file_owner_gid=");
- lib9p_s_format(&self->file_owner_gid, state);
- fmt_state_puts(state, " file_last_modified_uid=");
- lib9p_s_format(&self->file_last_modified_uid, state);
-#if CONFIG_9P_ENABLE_9P2000_u
- fmt_state_puts(state, " file_extension=");
- lib9p_s_format(&self->file_extension, state);
- fmt_state_puts(state, " file_owner_n_uid=");
- lib9p_nuid_format(&self->file_owner_n_uid, state);
- fmt_state_puts(state, " file_owner_n_gid=");
- lib9p_nuid_format(&self->file_owner_n_gid, state);
- fmt_state_puts(state, " file_last_modified_n_uid=");
- lib9p_nuid_format(&self->file_last_modified_n_uid, state);
-#endif /* CONFIG_9P_ENABLE_9P2000_u */
- fmt_state_puts(state, " }");
-}
-
-static void lib9p_o_format(lib9p_o_t *self, struct fmt_state *state) {
- bool empty = true;
- fmt_state_putchar(state, '(');
- if (*self & (UINT8_C(1)<<7)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<7");
- empty = false;
- }
- if (*self & (UINT8_C(1)<<6)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "RCLOSE");
- empty = false;
- }
- if (*self & (UINT8_C(1)<<5)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "CEXEC");
- empty = false;
- }
- if (*self & (UINT8_C(1)<<4)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "TRUNC");
- empty = false;
- }
- if (*self & (UINT8_C(1)<<3)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<3");
- empty = false;
- }
- if (*self & (UINT8_C(1)<<2)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<2");
- empty = false;
- }
- switch (*self & LIB9P_O_MODE_MASK) {
- case LIB9P_O_MODE_READ:
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "MODE_READ");
- empty = false;
- break;
- case LIB9P_O_MODE_WRITE:
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "MODE_WRITE");
- empty = false;
- break;
- case LIB9P_O_MODE_RDWR:
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "MODE_RDWR");
- empty = false;
- break;
- case LIB9P_O_MODE_EXEC:
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "MODE_EXEC");
- empty = false;
- break;
- default:
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_printf(state, "%"PRIu8, *self & LIB9P_O_MODE_MASK);
- empty = false;
- }
- if (empty)
- fmt_state_putchar(state, '0');
- fmt_state_putchar(state, ')');
-}
-
-#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */
-#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u
-static void lib9p_msg_Tversion_format(struct lib9p_msg_Tversion *self, struct fmt_state *state) {
- fmt_state_puts(state, "Tversion {");
- fmt_state_puts(state, " tag=");
- lib9p_tag_format(&self->tag, state);
- fmt_state_puts(state, " max_msg_size=");
- fmt_state_printf(state, "%"PRIu32, self->max_msg_size);
- fmt_state_puts(state, " version=");
- lib9p_s_format(&self->version, state);
- fmt_state_puts(state, " }");
-}
-
-static void lib9p_msg_Rversion_format(struct lib9p_msg_Rversion *self, struct fmt_state *state) {
- fmt_state_puts(state, "Rversion {");
- fmt_state_puts(state, " tag=");
- lib9p_tag_format(&self->tag, state);
- fmt_state_puts(state, " max_msg_size=");
- fmt_state_printf(state, "%"PRIu32, self->max_msg_size);
- fmt_state_puts(state, " version=");
- lib9p_s_format(&self->version, state);
- fmt_state_puts(state, " }");
-}
-
-static void lib9p_msg_Tauth_format(struct lib9p_msg_Tauth *self, struct fmt_state *state) {
- fmt_state_puts(state, "Tauth {");
- fmt_state_puts(state, " tag=");
- lib9p_tag_format(&self->tag, state);
- fmt_state_puts(state, " afid=");
- lib9p_fid_format(&self->afid, state);
- fmt_state_puts(state, " uname=");
- lib9p_s_format(&self->uname, state);
- fmt_state_puts(state, " aname=");
- lib9p_s_format(&self->aname, state);
-#if CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_u
- fmt_state_puts(state, " n_uid=");
- lib9p_nuid_format(&self->n_uid, state);
-#endif /* CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_u */
- fmt_state_puts(state, " }");
-}
-
-static void lib9p_msg_Rauth_format(struct lib9p_msg_Rauth *self, struct fmt_state *state) {
- fmt_state_puts(state, "Rauth {");
- fmt_state_puts(state, " tag=");
- lib9p_tag_format(&self->tag, state);
- fmt_state_puts(state, " aqid=");
- lib9p_qid_format(&self->aqid, state);
- fmt_state_puts(state, " }");
-}
-
-static void lib9p_msg_Tattach_format(struct lib9p_msg_Tattach *self, struct fmt_state *state) {
- fmt_state_puts(state, "Tattach {");
- fmt_state_puts(state, " tag=");
- lib9p_tag_format(&self->tag, state);
- fmt_state_puts(state, " fid=");
- lib9p_fid_format(&self->fid, state);
- fmt_state_puts(state, " afid=");
- lib9p_fid_format(&self->afid, state);
- fmt_state_puts(state, " uname=");
- lib9p_s_format(&self->uname, state);
- fmt_state_puts(state, " aname=");
- lib9p_s_format(&self->aname, state);
-#if CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_u
- fmt_state_puts(state, " n_uid=");
- lib9p_nuid_format(&self->n_uid, state);
-#endif /* CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_u */
- fmt_state_puts(state, " }");
-}
-
-static void lib9p_msg_Rattach_format(struct lib9p_msg_Rattach *self, struct fmt_state *state) {
- fmt_state_puts(state, "Rattach {");
- fmt_state_puts(state, " tag=");
- lib9p_tag_format(&self->tag, state);
- fmt_state_puts(state, " qid=");
- lib9p_qid_format(&self->qid, state);
- fmt_state_puts(state, " }");
-}
-
-static void lib9p_msg_Rerror_format(struct lib9p_msg_Rerror *self, struct fmt_state *state) {
- fmt_state_puts(state, "Rerror {");
- fmt_state_puts(state, " tag=");
- lib9p_tag_format(&self->tag, state);
- fmt_state_puts(state, " ename=");
- lib9p_s_format(&self->ename, state);
-#if CONFIG_9P_ENABLE_9P2000_u
- fmt_state_puts(state, " errno=");
- lib9p_errno_format(&self->errno, state);
-#endif /* CONFIG_9P_ENABLE_9P2000_u */
- fmt_state_puts(state, " }");
-}
-
-static void lib9p_msg_Tflush_format(struct lib9p_msg_Tflush *self, struct fmt_state *state) {
- fmt_state_puts(state, "Tflush {");
- fmt_state_puts(state, " tag=");
- lib9p_tag_format(&self->tag, state);
- fmt_state_puts(state, " oldtag=");
- fmt_state_printf(state, "%"PRIu16, self->oldtag);
- fmt_state_puts(state, " }");
-}
-
-static void lib9p_msg_Rflush_format(struct lib9p_msg_Rflush *self, struct fmt_state *state) {
- fmt_state_puts(state, "Rflush {");
- fmt_state_puts(state, " tag=");
- lib9p_tag_format(&self->tag, state);
- fmt_state_puts(state, " }");
-}
-
-static void lib9p_msg_Twalk_format(struct lib9p_msg_Twalk *self, struct fmt_state *state) {
- fmt_state_puts(state, "Twalk {");
- fmt_state_puts(state, " tag=");
- lib9p_tag_format(&self->tag, state);
- fmt_state_puts(state, " fid=");
- lib9p_fid_format(&self->fid, state);
- fmt_state_puts(state, " newfid=");
- lib9p_fid_format(&self->newfid, state);
- fmt_state_puts(state, " nwname=");
- fmt_state_printf(state, "%"PRIu16, self->nwname);
- fmt_state_puts(state, " wname=[");
- for (uint16_t i = 0; i < self->nwname; i++) {
- if (i)
- fmt_state_puts(state, ", ");
- lib9p_s_format(&self->wname[i], state);
- }
- fmt_state_puts(state, " ]");
- fmt_state_puts(state, " }");
-}
-
-static void lib9p_msg_Rwalk_format(struct lib9p_msg_Rwalk *self, struct fmt_state *state) {
- fmt_state_puts(state, "Rwalk {");
- fmt_state_puts(state, " tag=");
- lib9p_tag_format(&self->tag, state);
- fmt_state_puts(state, " nwqid=");
- fmt_state_printf(state, "%"PRIu16, self->nwqid);
- fmt_state_puts(state, " wqid=[");
- for (uint16_t i = 0; i < self->nwqid; i++) {
- if (i)
- fmt_state_puts(state, ", ");
- lib9p_qid_format(&self->wqid[i], state);
- }
- fmt_state_puts(state, " ]");
- fmt_state_puts(state, " }");
-}
-
-#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */
-#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u
-static void lib9p_msg_Topen_format(struct lib9p_msg_Topen *self, struct fmt_state *state) {
- fmt_state_puts(state, "Topen {");
- fmt_state_puts(state, " tag=");
- lib9p_tag_format(&self->tag, state);
- fmt_state_puts(state, " fid=");
- lib9p_fid_format(&self->fid, state);
- fmt_state_puts(state, " mode=");
- lib9p_o_format(&self->mode, state);
- fmt_state_puts(state, " }");
-}
-
-static void lib9p_msg_Ropen_format(struct lib9p_msg_Ropen *self, struct fmt_state *state) {
- fmt_state_puts(state, "Ropen {");
- fmt_state_puts(state, " tag=");
- lib9p_tag_format(&self->tag, state);
- fmt_state_puts(state, " qid=");
- lib9p_qid_format(&self->qid, state);
- fmt_state_puts(state, " iounit=");
- fmt_state_printf(state, "%"PRIu32, self->iounit);
- fmt_state_puts(state, " }");
-}
-
-static void lib9p_msg_Tcreate_format(struct lib9p_msg_Tcreate *self, struct fmt_state *state) {
- fmt_state_puts(state, "Tcreate {");
- fmt_state_puts(state, " tag=");
- lib9p_tag_format(&self->tag, state);
- fmt_state_puts(state, " fid=");
- lib9p_fid_format(&self->fid, state);
- fmt_state_puts(state, " name=");
- lib9p_s_format(&self->name, state);
- fmt_state_puts(state, " perm=");
- lib9p_dm_format(&self->perm, state);
- fmt_state_puts(state, " mode=");
- lib9p_o_format(&self->mode, state);
- fmt_state_puts(state, " }");
-}
-
-static void lib9p_msg_Rcreate_format(struct lib9p_msg_Rcreate *self, struct fmt_state *state) {
- fmt_state_puts(state, "Rcreate {");
- fmt_state_puts(state, " tag=");
- lib9p_tag_format(&self->tag, state);
- fmt_state_puts(state, " qid=");
- lib9p_qid_format(&self->qid, state);
- fmt_state_puts(state, " iounit=");
- fmt_state_printf(state, "%"PRIu32, self->iounit);
- fmt_state_puts(state, " }");
-}
-
-#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */
-#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u
-static void lib9p_msg_Tread_format(struct lib9p_msg_Tread *self, struct fmt_state *state) {
- fmt_state_puts(state, "Tread {");
- fmt_state_puts(state, " tag=");
- lib9p_tag_format(&self->tag, state);
- fmt_state_puts(state, " fid=");
- lib9p_fid_format(&self->fid, state);
- fmt_state_puts(state, " offset=");
- fmt_state_printf(state, "%"PRIu64, self->offset);
- fmt_state_puts(state, " count=");
- fmt_state_printf(state, "%"PRIu32, self->count);
- fmt_state_puts(state, " }");
-}
-
-static void lib9p_msg_Rread_format(struct lib9p_msg_Rread *self, struct fmt_state *state) {
- fmt_state_puts(state, "Rread {");
- fmt_state_puts(state, " tag=");
- lib9p_tag_format(&self->tag, state);
- fmt_state_puts(state, " count=");
- fmt_state_printf(state, "%"PRIu32, self->count);
- fmt_state_puts(state, " data=<bytedata>");
- fmt_state_puts(state, " }");
-}
-
-static void lib9p_msg_Twrite_format(struct lib9p_msg_Twrite *self, struct fmt_state *state) {
- fmt_state_puts(state, "Twrite {");
- fmt_state_puts(state, " tag=");
- lib9p_tag_format(&self->tag, state);
- fmt_state_puts(state, " fid=");
- lib9p_fid_format(&self->fid, state);
- fmt_state_puts(state, " offset=");
- fmt_state_printf(state, "%"PRIu64, self->offset);
- fmt_state_puts(state, " count=");
- fmt_state_printf(state, "%"PRIu32, self->count);
- fmt_state_puts(state, " data=<bytedata>");
- fmt_state_puts(state, " }");
-}
-
-static void lib9p_msg_Rwrite_format(struct lib9p_msg_Rwrite *self, struct fmt_state *state) {
- fmt_state_puts(state, "Rwrite {");
- fmt_state_puts(state, " tag=");
- lib9p_tag_format(&self->tag, state);
- fmt_state_puts(state, " count=");
- fmt_state_printf(state, "%"PRIu32, self->count);
- fmt_state_puts(state, " }");
-}
-
-static void lib9p_msg_Tclunk_format(struct lib9p_msg_Tclunk *self, struct fmt_state *state) {
- fmt_state_puts(state, "Tclunk {");
- fmt_state_puts(state, " tag=");
- lib9p_tag_format(&self->tag, state);
- fmt_state_puts(state, " fid=");
- lib9p_fid_format(&self->fid, state);
- fmt_state_puts(state, " }");
-}
-
-static void lib9p_msg_Rclunk_format(struct lib9p_msg_Rclunk *self, struct fmt_state *state) {
- fmt_state_puts(state, "Rclunk {");
- fmt_state_puts(state, " tag=");
- lib9p_tag_format(&self->tag, state);
- fmt_state_puts(state, " }");
-}
-
-static void lib9p_msg_Tremove_format(struct lib9p_msg_Tremove *self, struct fmt_state *state) {
- fmt_state_puts(state, "Tremove {");
- fmt_state_puts(state, " tag=");
- lib9p_tag_format(&self->tag, state);
- fmt_state_puts(state, " fid=");
- lib9p_fid_format(&self->fid, state);
- fmt_state_puts(state, " }");
-}
-
-static void lib9p_msg_Rremove_format(struct lib9p_msg_Rremove *self, struct fmt_state *state) {
- fmt_state_puts(state, "Rremove {");
- fmt_state_puts(state, " tag=");
- lib9p_tag_format(&self->tag, state);
- fmt_state_puts(state, " }");
-}
-
-#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */
-#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u
-static void lib9p_msg_Tstat_format(struct lib9p_msg_Tstat *self, struct fmt_state *state) {
- fmt_state_puts(state, "Tstat {");
- fmt_state_puts(state, " tag=");
- lib9p_tag_format(&self->tag, state);
- fmt_state_puts(state, " fid=");
- lib9p_fid_format(&self->fid, state);
- fmt_state_puts(state, " }");
-}
-
-static void lib9p_msg_Rstat_format(struct lib9p_msg_Rstat *self, struct fmt_state *state) {
- fmt_state_puts(state, "Rstat {");
- fmt_state_puts(state, " tag=");
- lib9p_tag_format(&self->tag, state);
- fmt_state_puts(state, " stat=");
- lib9p_stat_format(&self->stat, state);
- fmt_state_puts(state, " }");
-}
-
-static void lib9p_msg_Twstat_format(struct lib9p_msg_Twstat *self, struct fmt_state *state) {
- fmt_state_puts(state, "Twstat {");
- fmt_state_puts(state, " tag=");
- lib9p_tag_format(&self->tag, state);
- fmt_state_puts(state, " fid=");
- lib9p_fid_format(&self->fid, state);
- fmt_state_puts(state, " stat=");
- lib9p_stat_format(&self->stat, state);
- fmt_state_puts(state, " }");
-}
-
-static void lib9p_msg_Rwstat_format(struct lib9p_msg_Rwstat *self, struct fmt_state *state) {
- fmt_state_puts(state, "Rwstat {");
- fmt_state_puts(state, " tag=");
- lib9p_tag_format(&self->tag, state);
- fmt_state_puts(state, " }");
-}
-
-#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */
-#if CONFIG_9P_ENABLE_9P2000_p9p
-static void lib9p_msg_Topenfd_format(struct lib9p_msg_Topenfd *self, struct fmt_state *state) {
- fmt_state_puts(state, "Topenfd {");
- fmt_state_puts(state, " tag=");
- lib9p_tag_format(&self->tag, state);
- fmt_state_puts(state, " fid=");
- lib9p_fid_format(&self->fid, state);
- fmt_state_puts(state, " mode=");
- lib9p_o_format(&self->mode, state);
- fmt_state_puts(state, " }");
-}
-
-static void lib9p_msg_Ropenfd_format(struct lib9p_msg_Ropenfd *self, struct fmt_state *state) {
- fmt_state_puts(state, "Ropenfd {");
- fmt_state_puts(state, " tag=");
- lib9p_tag_format(&self->tag, state);
- fmt_state_puts(state, " qid=");
- lib9p_qid_format(&self->qid, state);
- fmt_state_puts(state, " iounit=");
- fmt_state_printf(state, "%"PRIu32, self->iounit);
- fmt_state_puts(state, " unixfd=");
- fmt_state_printf(state, "%"PRIu32, self->unixfd);
- fmt_state_puts(state, " }");
-}
-
-#endif /* CONFIG_9P_ENABLE_9P2000_p9p */
-#if CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_u
-static void lib9p_nuid_format(lib9p_nuid_t *self, struct fmt_state *state) {
- switch (*self) {
- case LIB9P_NUID_NONUID:
- fmt_state_puts(state, "NONUID");
- break;
- default:
- fmt_state_printf(state, "%"PRIu32, *self);
- }
-}
-
-static void lib9p_errno_format(lib9p_errno_t *self, struct fmt_state *state) {
- switch (*self) {
- case LIB9P_ERRNO_NOERROR:
- fmt_state_puts(state, "NOERROR");
- break;
- default:
- fmt_state_printf(state, "%"PRIu32, *self);
- }
-}
-
-#endif /* CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_u */
-#if CONFIG_9P_ENABLE_9P2000_L
-static void lib9p_super_magic_format(lib9p_super_magic_t *self, struct fmt_state *state) {
- switch (*self) {
- case LIB9P_SUPER_MAGIC_V9FS_MAGIC:
- fmt_state_puts(state, "V9FS_MAGIC");
- break;
- default:
- fmt_state_printf(state, "%"PRIu32, *self);
- }
-}
-
-static void lib9p_lo_format(lib9p_lo_t *self, struct fmt_state *state) {
- bool empty = true;
- fmt_state_putchar(state, '(');
- if (*self & (UINT32_C(1)<<31)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<31");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<30)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<30");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<29)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<29");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<28)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<28");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<27)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<27");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<26)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<26");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<25)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<25");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<24)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<24");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<23)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<23");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<22)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<22");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<21)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<21");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<20)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "SYNC");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<19)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "CLOEXEC");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<18)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "NOATIME");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<17)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "NOFOLLOW");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<16)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "DIRECTORY");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<15)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "LARGEFILE");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<14)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "DIRECT");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<13)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "BSD_FASYNC");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<12)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "DSYNC");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<11)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "NONBLOCK");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<10)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "APPEND");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<9)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "TRUNC");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<8)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "NOCTTY");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<7)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "EXCL");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<6)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "CREATE");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<5)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<5");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<4)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<4");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<3)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<3");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<2)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<2");
- empty = false;
- }
- switch (*self & LIB9P_LO_MODE_MASK) {
- case LIB9P_LO_MODE_RDONLY:
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "MODE_RDONLY");
- empty = false;
- break;
- case LIB9P_LO_MODE_WRONLY:
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "MODE_WRONLY");
- empty = false;
- break;
- case LIB9P_LO_MODE_RDWR:
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "MODE_RDWR");
- empty = false;
- break;
- case LIB9P_LO_MODE_NOACCESS:
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "MODE_NOACCESS");
- empty = false;
- break;
- default:
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_printf(state, "%"PRIu32, *self & LIB9P_LO_MODE_MASK);
- empty = false;
- }
- if (empty)
- fmt_state_putchar(state, '0');
- fmt_state_putchar(state, ')');
-}
-
-static void lib9p_dt_format(lib9p_dt_t *self, struct fmt_state *state) {
- switch (*self) {
- case LIB9P_DT_UNKNOWN:
- fmt_state_puts(state, "UNKNOWN");
- break;
- case LIB9P_DT_PIPE:
- fmt_state_puts(state, "PIPE");
- break;
- case LIB9P_DT_CHAR_DEV:
- fmt_state_puts(state, "CHAR_DEV");
- break;
- case LIB9P_DT_DIRECTORY:
- fmt_state_puts(state, "DIRECTORY");
- break;
- case LIB9P_DT_BLOCK_DEV:
- fmt_state_puts(state, "BLOCK_DEV");
- break;
- case LIB9P_DT_REGULAR:
- fmt_state_puts(state, "REGULAR");
- break;
- case LIB9P_DT_SYMLINK:
- fmt_state_puts(state, "SYMLINK");
- break;
- case LIB9P_DT_SOCKET:
- fmt_state_puts(state, "SOCKET");
- break;
- case _LIB9P_DT_WHITEOUT:
- fmt_state_puts(state, "_WHITEOUT");
- break;
- default:
- fmt_state_printf(state, "%"PRIu8, *self);
- }
-}
-
-static void lib9p_mode_format(lib9p_mode_t *self, struct fmt_state *state) {
- bool empty = true;
- fmt_state_putchar(state, '(');
- if (*self & (UINT32_C(1)<<31)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<31");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<30)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<30");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<29)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<29");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<28)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<28");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<27)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<27");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<26)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<26");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<25)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<25");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<24)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<24");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<23)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<23");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<22)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<22");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<21)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<21");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<20)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<20");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<19)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<19");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<18)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<18");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<17)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<17");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<16)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<16");
- empty = false;
- }
- switch (*self & LIB9P_MODE_FMT_MASK) {
- case LIB9P_MODE_FMT_PIPE:
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "FMT_PIPE");
- empty = false;
- break;
- case LIB9P_MODE_FMT_CHAR_DEV:
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "FMT_CHAR_DEV");
- empty = false;
- break;
- case LIB9P_MODE_FMT_DIRECTORY:
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "FMT_DIRECTORY");
- empty = false;
- break;
- case LIB9P_MODE_FMT_BLOCK_DEV:
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "FMT_BLOCK_DEV");
- empty = false;
- break;
- case LIB9P_MODE_FMT_REGULAR:
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "FMT_REGULAR");
- empty = false;
- break;
- case LIB9P_MODE_FMT_SYMLINK:
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "FMT_SYMLINK");
- empty = false;
- break;
- case LIB9P_MODE_FMT_SOCKET:
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "FMT_SOCKET");
- empty = false;
- break;
- default:
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_printf(state, "%"PRIu32, *self & LIB9P_MODE_FMT_MASK);
- empty = false;
- }
- if (*self & (UINT32_C(1)<<11)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "PERM_SETGROUP");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<10)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "PERM_SETUSER");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<9)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "PERM_STICKY");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<8)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "PERM_OWNER_R");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<7)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "PERM_OWNER_W");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<6)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "PERM_OWNER_X");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<5)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "PERM_GROUP_R");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<4)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "PERM_GROUP_W");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<3)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "PERM_GROUP_X");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<2)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "PERM_OTHER_R");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<1)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "PERM_OTHER_W");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<0)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "PERM_OTHER_X");
- empty = false;
- }
- if (empty)
- fmt_state_putchar(state, '0');
- fmt_state_putchar(state, ')');
-}
-
-static void lib9p_b4_format(lib9p_b4_t *self, struct fmt_state *state) {
- switch (*self) {
- case LIB9P_B4_FALSE:
- fmt_state_puts(state, "FALSE");
- break;
- case LIB9P_B4_TRUE:
- fmt_state_puts(state, "TRUE");
- break;
- default:
- fmt_state_printf(state, "%"PRIu32, *self);
- }
-}
-
-static void lib9p_getattr_format(lib9p_getattr_t *self, struct fmt_state *state) {
- bool empty = true;
- fmt_state_putchar(state, '(');
- if (*self & (UINT64_C(1)<<63)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<63");
- empty = false;
- }
- if (*self & (UINT64_C(1)<<62)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<62");
- empty = false;
- }
- if (*self & (UINT64_C(1)<<61)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<61");
- empty = false;
- }
- if (*self & (UINT64_C(1)<<60)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<60");
- empty = false;
- }
- if (*self & (UINT64_C(1)<<59)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<59");
- empty = false;
- }
- if (*self & (UINT64_C(1)<<58)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<58");
- empty = false;
- }
- if (*self & (UINT64_C(1)<<57)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<57");
- empty = false;
- }
- if (*self & (UINT64_C(1)<<56)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<56");
- empty = false;
- }
- if (*self & (UINT64_C(1)<<55)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<55");
- empty = false;
- }
- if (*self & (UINT64_C(1)<<54)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<54");
- empty = false;
- }
- if (*self & (UINT64_C(1)<<53)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<53");
- empty = false;
- }
- if (*self & (UINT64_C(1)<<52)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<52");
- empty = false;
- }
- if (*self & (UINT64_C(1)<<51)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<51");
- empty = false;
- }
- if (*self & (UINT64_C(1)<<50)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<50");
- empty = false;
- }
- if (*self & (UINT64_C(1)<<49)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<49");
- empty = false;
- }
- if (*self & (UINT64_C(1)<<48)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<48");
- empty = false;
- }
- if (*self & (UINT64_C(1)<<47)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<47");
- empty = false;
- }
- if (*self & (UINT64_C(1)<<46)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<46");
- empty = false;
- }
- if (*self & (UINT64_C(1)<<45)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<45");
- empty = false;
- }
- if (*self & (UINT64_C(1)<<44)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<44");
- empty = false;
- }
- if (*self & (UINT64_C(1)<<43)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<43");
- empty = false;
- }
- if (*self & (UINT64_C(1)<<42)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<42");
- empty = false;
- }
- if (*self & (UINT64_C(1)<<41)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<41");
- empty = false;
- }
- if (*self & (UINT64_C(1)<<40)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<40");
- empty = false;
- }
- if (*self & (UINT64_C(1)<<39)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<39");
- empty = false;
- }
- if (*self & (UINT64_C(1)<<38)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<38");
- empty = false;
- }
- if (*self & (UINT64_C(1)<<37)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<37");
- empty = false;
- }
- if (*self & (UINT64_C(1)<<36)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<36");
- empty = false;
- }
- if (*self & (UINT64_C(1)<<35)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<35");
- empty = false;
- }
- if (*self & (UINT64_C(1)<<34)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<34");
- empty = false;
- }
- if (*self & (UINT64_C(1)<<33)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<33");
- empty = false;
- }
- if (*self & (UINT64_C(1)<<32)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<32");
- empty = false;
- }
- if (*self & (UINT64_C(1)<<31)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<31");
- empty = false;
- }
- if (*self & (UINT64_C(1)<<30)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<30");
- empty = false;
- }
- if (*self & (UINT64_C(1)<<29)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<29");
- empty = false;
- }
- if (*self & (UINT64_C(1)<<28)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<28");
- empty = false;
- }
- if (*self & (UINT64_C(1)<<27)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<27");
- empty = false;
- }
- if (*self & (UINT64_C(1)<<26)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<26");
- empty = false;
- }
- if (*self & (UINT64_C(1)<<25)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<25");
- empty = false;
- }
- if (*self & (UINT64_C(1)<<24)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<24");
- empty = false;
- }
- if (*self & (UINT64_C(1)<<23)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<23");
- empty = false;
- }
- if (*self & (UINT64_C(1)<<22)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<22");
- empty = false;
- }
- if (*self & (UINT64_C(1)<<21)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<21");
- empty = false;
- }
- if (*self & (UINT64_C(1)<<20)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<20");
- empty = false;
- }
- if (*self & (UINT64_C(1)<<19)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<19");
- empty = false;
- }
- if (*self & (UINT64_C(1)<<18)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<18");
- empty = false;
- }
- if (*self & (UINT64_C(1)<<17)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<17");
- empty = false;
- }
- if (*self & (UINT64_C(1)<<16)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<16");
- empty = false;
- }
- if (*self & (UINT64_C(1)<<15)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<15");
- empty = false;
- }
- if (*self & (UINT64_C(1)<<14)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<14");
- empty = false;
- }
- if (*self & (UINT64_C(1)<<13)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "DATA_VERSION");
- empty = false;
- }
- if (*self & (UINT64_C(1)<<12)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "GEN");
- empty = false;
- }
- if (*self & (UINT64_C(1)<<11)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "BTIME");
- empty = false;
- }
- if (*self & (UINT64_C(1)<<10)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "BLOCKS");
- empty = false;
- }
- if (*self & (UINT64_C(1)<<9)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "SIZE");
- empty = false;
- }
- if (*self & (UINT64_C(1)<<8)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "INO");
- empty = false;
- }
- if (*self & (UINT64_C(1)<<7)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "CTIME");
- empty = false;
- }
- if (*self & (UINT64_C(1)<<6)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "MTIME");
- empty = false;
- }
- if (*self & (UINT64_C(1)<<5)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "ATIME");
- empty = false;
- }
- if (*self & (UINT64_C(1)<<4)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "RDEV");
- empty = false;
- }
- if (*self & (UINT64_C(1)<<3)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "GID");
- empty = false;
- }
- if (*self & (UINT64_C(1)<<2)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "UID");
- empty = false;
- }
- if (*self & (UINT64_C(1)<<1)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "NLINK");
- empty = false;
- }
- if (*self & (UINT64_C(1)<<0)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "MODE");
- empty = false;
- }
- if (empty)
- fmt_state_putchar(state, '0');
- fmt_state_putchar(state, ')');
-}
-
-static void lib9p_setattr_format(lib9p_setattr_t *self, struct fmt_state *state) {
- bool empty = true;
- fmt_state_putchar(state, '(');
- if (*self & (UINT32_C(1)<<31)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<31");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<30)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<30");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<29)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<29");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<28)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<28");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<27)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<27");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<26)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<26");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<25)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<25");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<24)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<24");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<23)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<23");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<22)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<22");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<21)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<21");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<20)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<20");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<19)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<19");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<18)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<18");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<17)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<17");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<16)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<16");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<15)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<15");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<14)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<14");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<13)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<13");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<12)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<12");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<11)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<11");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<10)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<10");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<9)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<9");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<8)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "MTIME_SET");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<7)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "ATIME_SET");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<6)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "CTIME");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<5)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "MTIME");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<4)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "ATIME");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<3)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "SIZE");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<2)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "GID");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<1)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "UID");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<0)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "MODE");
- empty = false;
- }
- if (empty)
- fmt_state_putchar(state, '0');
- fmt_state_putchar(state, ')');
-}
-
-static void lib9p_lock_type_format(lib9p_lock_type_t *self, struct fmt_state *state) {
- switch (*self) {
- case LIB9P_LOCK_TYPE_RDLCK:
- fmt_state_puts(state, "RDLCK");
- break;
- case LIB9P_LOCK_TYPE_WRLCK:
- fmt_state_puts(state, "WRLCK");
- break;
- case LIB9P_LOCK_TYPE_UNLCK:
- fmt_state_puts(state, "UNLCK");
- break;
- default:
- fmt_state_printf(state, "%"PRIu8, *self);
- }
-}
-
-static void lib9p_lock_flags_format(lib9p_lock_flags_t *self, struct fmt_state *state) {
- bool empty = true;
- fmt_state_putchar(state, '(');
- if (*self & (UINT32_C(1)<<31)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<31");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<30)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<30");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<29)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<29");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<28)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<28");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<27)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<27");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<26)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<26");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<25)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<25");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<24)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<24");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<23)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<23");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<22)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<22");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<21)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<21");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<20)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<20");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<19)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<19");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<18)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<18");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<17)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<17");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<16)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<16");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<15)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<15");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<14)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<14");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<13)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<13");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<12)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<12");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<11)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<11");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<10)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<10");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<9)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<9");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<8)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<8");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<7)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<7");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<6)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<6");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<5)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<5");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<4)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<4");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<3)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<3");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<2)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "1<<2");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<1)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "RECLAIM");
- empty = false;
- }
- if (*self & (UINT32_C(1)<<0)) {
- if (!empty)
- fmt_state_putchar(state, '|');
- fmt_state_puts(state, "BLOCK");
- empty = false;
- }
- if (empty)
- fmt_state_putchar(state, '0');
- fmt_state_putchar(state, ')');
-}
-
-static void lib9p_lock_status_format(lib9p_lock_status_t *self, struct fmt_state *state) {
- switch (*self) {
- case LIB9P_LOCK_STATUS_SUCCESS:
- fmt_state_puts(state, "SUCCESS");
- break;
- case LIB9P_LOCK_STATUS_BLOCKED:
- fmt_state_puts(state, "BLOCKED");
- break;
- case LIB9P_LOCK_STATUS_ERROR:
- fmt_state_puts(state, "ERROR");
- break;
- case LIB9P_LOCK_STATUS_GRACE:
- fmt_state_puts(state, "GRACE");
- break;
- default:
- fmt_state_printf(state, "%"PRIu8, *self);
- }
-}
-
-static void lib9p_msg_Rlerror_format(struct lib9p_msg_Rlerror *self, struct fmt_state *state) {
- fmt_state_puts(state, "Rlerror {");
- fmt_state_puts(state, " tag=");
- lib9p_tag_format(&self->tag, state);
- fmt_state_puts(state, " ecode=");
- lib9p_errno_format(&self->ecode, state);
- fmt_state_puts(state, " }");
-}
-
-static void lib9p_msg_Tstatfs_format(struct lib9p_msg_Tstatfs *self, struct fmt_state *state) {
- fmt_state_puts(state, "Tstatfs {");
- fmt_state_puts(state, " tag=");
- lib9p_tag_format(&self->tag, state);
- fmt_state_puts(state, " fid=");
- lib9p_fid_format(&self->fid, state);
- fmt_state_puts(state, " }");
-}
-
-static void lib9p_msg_Rstatfs_format(struct lib9p_msg_Rstatfs *self, struct fmt_state *state) {
- fmt_state_puts(state, "Rstatfs {");
- fmt_state_puts(state, " tag=");
- lib9p_tag_format(&self->tag, state);
- fmt_state_puts(state, " type=");
- lib9p_super_magic_format(&self->type, state);
- fmt_state_puts(state, " bsize=");
- fmt_state_printf(state, "%"PRIu32, self->bsize);
- fmt_state_puts(state, " blocks=");
- fmt_state_printf(state, "%"PRIu64, self->blocks);
- fmt_state_puts(state, " bfree=");
- fmt_state_printf(state, "%"PRIu64, self->bfree);
- fmt_state_puts(state, " bavail=");
- fmt_state_printf(state, "%"PRIu64, self->bavail);
- fmt_state_puts(state, " files=");
- fmt_state_printf(state, "%"PRIu64, self->files);
- fmt_state_puts(state, " ffree=");
- fmt_state_printf(state, "%"PRIu64, self->ffree);
- fmt_state_puts(state, " fsid=");
- fmt_state_printf(state, "%"PRIu64, self->fsid);
- fmt_state_puts(state, " namelen=");
- fmt_state_printf(state, "%"PRIu32, self->namelen);
- fmt_state_puts(state, " }");
-}
-
-static void lib9p_msg_Tlopen_format(struct lib9p_msg_Tlopen *self, struct fmt_state *state) {
- fmt_state_puts(state, "Tlopen {");
- fmt_state_puts(state, " tag=");
- lib9p_tag_format(&self->tag, state);
- fmt_state_puts(state, " fid=");
- lib9p_fid_format(&self->fid, state);
- fmt_state_puts(state, " flags=");
- lib9p_lo_format(&self->flags, state);
- fmt_state_puts(state, " }");
-}
-
-static void lib9p_msg_Rlopen_format(struct lib9p_msg_Rlopen *self, struct fmt_state *state) {
- fmt_state_puts(state, "Rlopen {");
- fmt_state_puts(state, " tag=");
- lib9p_tag_format(&self->tag, state);
- fmt_state_puts(state, " qid=");
- lib9p_qid_format(&self->qid, state);
- fmt_state_puts(state, " iounit=");
- fmt_state_printf(state, "%"PRIu32, self->iounit);
- fmt_state_puts(state, " }");
-}
-
-static void lib9p_msg_Tlcreate_format(struct lib9p_msg_Tlcreate *self, struct fmt_state *state) {
- fmt_state_puts(state, "Tlcreate {");
- fmt_state_puts(state, " tag=");
- lib9p_tag_format(&self->tag, state);
- fmt_state_puts(state, " fid=");
- lib9p_fid_format(&self->fid, state);
- fmt_state_puts(state, " name=");
- lib9p_s_format(&self->name, state);
- fmt_state_puts(state, " flags=");
- lib9p_lo_format(&self->flags, state);
- fmt_state_puts(state, " mode=");
- lib9p_mode_format(&self->mode, state);
- fmt_state_puts(state, " gid=");
- lib9p_nuid_format(&self->gid, state);
- fmt_state_puts(state, " }");
-}
-
-static void lib9p_msg_Rlcreate_format(struct lib9p_msg_Rlcreate *self, struct fmt_state *state) {
- fmt_state_puts(state, "Rlcreate {");
- fmt_state_puts(state, " tag=");
- lib9p_tag_format(&self->tag, state);
- fmt_state_puts(state, " qid=");
- lib9p_qid_format(&self->qid, state);
- fmt_state_puts(state, " iounit=");
- fmt_state_printf(state, "%"PRIu32, self->iounit);
- fmt_state_puts(state, " }");
-}
-
-static void lib9p_msg_Tsymlink_format(struct lib9p_msg_Tsymlink *self, struct fmt_state *state) {
- fmt_state_puts(state, "Tsymlink {");
- fmt_state_puts(state, " tag=");
- lib9p_tag_format(&self->tag, state);
- fmt_state_puts(state, " fid=");
- lib9p_fid_format(&self->fid, state);
- fmt_state_puts(state, " name=");
- lib9p_s_format(&self->name, state);
- fmt_state_puts(state, " symtgt=");
- lib9p_s_format(&self->symtgt, state);
- fmt_state_puts(state, " gid=");
- lib9p_nuid_format(&self->gid, state);
- fmt_state_puts(state, " }");
-}
-
-static void lib9p_msg_Rsymlink_format(struct lib9p_msg_Rsymlink *self, struct fmt_state *state) {
- fmt_state_puts(state, "Rsymlink {");
- fmt_state_puts(state, " tag=");
- lib9p_tag_format(&self->tag, state);
- fmt_state_puts(state, " qid=");
- lib9p_qid_format(&self->qid, state);
- fmt_state_puts(state, " }");
-}
-
-static void lib9p_msg_Tmknod_format(struct lib9p_msg_Tmknod *self, struct fmt_state *state) {
- fmt_state_puts(state, "Tmknod {");
- fmt_state_puts(state, " tag=");
- lib9p_tag_format(&self->tag, state);
- fmt_state_puts(state, " dfid=");
- lib9p_fid_format(&self->dfid, state);
- fmt_state_puts(state, " name=");
- lib9p_s_format(&self->name, state);
- fmt_state_puts(state, " mode=");
- lib9p_mode_format(&self->mode, state);
- fmt_state_puts(state, " major=");
- fmt_state_printf(state, "%"PRIu32, self->major);
- fmt_state_puts(state, " minor=");
- fmt_state_printf(state, "%"PRIu32, self->minor);
- fmt_state_puts(state, " gid=");
- lib9p_nuid_format(&self->gid, state);
- fmt_state_puts(state, " }");
-}
-
-static void lib9p_msg_Rmknod_format(struct lib9p_msg_Rmknod *self, struct fmt_state *state) {
- fmt_state_puts(state, "Rmknod {");
- fmt_state_puts(state, " tag=");
- lib9p_tag_format(&self->tag, state);
- fmt_state_puts(state, " qid=");
- lib9p_qid_format(&self->qid, state);
- fmt_state_puts(state, " }");
-}
-
-static void lib9p_msg_Trename_format(struct lib9p_msg_Trename *self, struct fmt_state *state) {
- fmt_state_puts(state, "Trename {");
- fmt_state_puts(state, " tag=");
- lib9p_tag_format(&self->tag, state);
- fmt_state_puts(state, " fid=");
- lib9p_fid_format(&self->fid, state);
- fmt_state_puts(state, " dfid=");
- lib9p_fid_format(&self->dfid, state);
- fmt_state_puts(state, " name=");
- lib9p_s_format(&self->name, state);
- fmt_state_puts(state, " }");
-}
-
-static void lib9p_msg_Rrename_format(struct lib9p_msg_Rrename *self, struct fmt_state *state) {
- fmt_state_puts(state, "Rrename {");
- fmt_state_puts(state, " tag=");
- lib9p_tag_format(&self->tag, state);
- fmt_state_puts(state, " }");
-}
-
-static void lib9p_msg_Treadlink_format(struct lib9p_msg_Treadlink *self, struct fmt_state *state) {
- fmt_state_puts(state, "Treadlink {");
- fmt_state_puts(state, " tag=");
- lib9p_tag_format(&self->tag, state);
- fmt_state_puts(state, " fid=");
- lib9p_fid_format(&self->fid, state);
- fmt_state_puts(state, " }");
-}
-
-static void lib9p_msg_Rreadlink_format(struct lib9p_msg_Rreadlink *self, struct fmt_state *state) {
- fmt_state_puts(state, "Rreadlink {");
- fmt_state_puts(state, " tag=");
- lib9p_tag_format(&self->tag, state);
- fmt_state_puts(state, " target=");
- lib9p_s_format(&self->target, state);
- fmt_state_puts(state, " }");
-}
-
-static void lib9p_msg_Tgetattr_format(struct lib9p_msg_Tgetattr *self, struct fmt_state *state) {
- fmt_state_puts(state, "Tgetattr {");
- fmt_state_puts(state, " tag=");
- lib9p_tag_format(&self->tag, state);
- fmt_state_puts(state, " fid=");
- lib9p_fid_format(&self->fid, state);
- fmt_state_puts(state, " request_mask=");
- lib9p_getattr_format(&self->request_mask, state);
- fmt_state_puts(state, " }");
-}
-
-static void lib9p_msg_Rgetattr_format(struct lib9p_msg_Rgetattr *self, struct fmt_state *state) {
- fmt_state_puts(state, "Rgetattr {");
- fmt_state_puts(state, " tag=");
- lib9p_tag_format(&self->tag, state);
- fmt_state_puts(state, " valid=");
- lib9p_getattr_format(&self->valid, state);
- fmt_state_puts(state, " qid=");
- lib9p_qid_format(&self->qid, state);
- fmt_state_puts(state, " mode=");
- lib9p_mode_format(&self->mode, state);
- fmt_state_puts(state, " uid=");
- lib9p_nuid_format(&self->uid, state);
- fmt_state_puts(state, " gid=");
- lib9p_nuid_format(&self->gid, state);
- fmt_state_puts(state, " nlink=");
- fmt_state_printf(state, "%"PRIu64, self->nlink);
- fmt_state_puts(state, " rdev=");
- fmt_state_printf(state, "%"PRIu64, self->rdev);
- fmt_state_puts(state, " filesize=");
- fmt_state_printf(state, "%"PRIu64, self->filesize);
- fmt_state_puts(state, " blksize=");
- fmt_state_printf(state, "%"PRIu64, self->blksize);
- fmt_state_puts(state, " blocks=");
- fmt_state_printf(state, "%"PRIu64, self->blocks);
- fmt_state_puts(state, " atime_sec=");
- fmt_state_printf(state, "%"PRIu64, self->atime_sec);
- fmt_state_puts(state, " atime_nsec=");
- fmt_state_printf(state, "%"PRIu64, self->atime_nsec);
- fmt_state_puts(state, " mtime_sec=");
- fmt_state_printf(state, "%"PRIu64, self->mtime_sec);
- fmt_state_puts(state, " mtime_nsec=");
- fmt_state_printf(state, "%"PRIu64, self->mtime_nsec);
- fmt_state_puts(state, " ctime_sec=");
- fmt_state_printf(state, "%"PRIu64, self->ctime_sec);
- fmt_state_puts(state, " ctime_nsec=");
- fmt_state_printf(state, "%"PRIu64, self->ctime_nsec);
- fmt_state_puts(state, " btime_sec=");
- fmt_state_printf(state, "%"PRIu64, self->btime_sec);
- fmt_state_puts(state, " btime_nsec=");
- fmt_state_printf(state, "%"PRIu64, self->btime_nsec);
- fmt_state_puts(state, " gen=");
- fmt_state_printf(state, "%"PRIu64, self->gen);
- fmt_state_puts(state, " data_version=");
- fmt_state_printf(state, "%"PRIu64, self->data_version);
- fmt_state_puts(state, " }");
-}
-
-static void lib9p_msg_Tsetattr_format(struct lib9p_msg_Tsetattr *self, struct fmt_state *state) {
- fmt_state_puts(state, "Tsetattr {");
- fmt_state_puts(state, " tag=");
- lib9p_tag_format(&self->tag, state);
- fmt_state_puts(state, " fid=");
- lib9p_fid_format(&self->fid, state);
- fmt_state_puts(state, " valid=");
- lib9p_setattr_format(&self->valid, state);
- fmt_state_puts(state, " mode=");
- lib9p_mode_format(&self->mode, state);
- fmt_state_puts(state, " uid=");
- lib9p_nuid_format(&self->uid, state);
- fmt_state_puts(state, " gid=");
- lib9p_nuid_format(&self->gid, state);
- fmt_state_puts(state, " filesize=");
- fmt_state_printf(state, "%"PRIu64, self->filesize);
- fmt_state_puts(state, " atime_sec=");
- fmt_state_printf(state, "%"PRIu64, self->atime_sec);
- fmt_state_puts(state, " atime_nsec=");
- fmt_state_printf(state, "%"PRIu64, self->atime_nsec);
- fmt_state_puts(state, " mtime_sec=");
- fmt_state_printf(state, "%"PRIu64, self->mtime_sec);
- fmt_state_puts(state, " mtime_nsec=");
- fmt_state_printf(state, "%"PRIu64, self->mtime_nsec);
- fmt_state_puts(state, " }");
-}
-
-static void lib9p_msg_Rsetattr_format(struct lib9p_msg_Rsetattr *self, struct fmt_state *state) {
- fmt_state_puts(state, "Rsetattr {");
- fmt_state_puts(state, " tag=");
- lib9p_tag_format(&self->tag, state);
- fmt_state_puts(state, " }");
-}
-
-static void lib9p_msg_Txattrwalk_format(struct lib9p_msg_Txattrwalk *self, struct fmt_state *state) {
- fmt_state_puts(state, "Txattrwalk {");
- fmt_state_puts(state, " tag=");
- lib9p_tag_format(&self->tag, state);
- fmt_state_puts(state, " fid=");
- lib9p_fid_format(&self->fid, state);
- fmt_state_puts(state, " newfid=");
- lib9p_fid_format(&self->newfid, state);
- fmt_state_puts(state, " name=");
- lib9p_s_format(&self->name, state);
- fmt_state_puts(state, " }");
-}
-
-static void lib9p_msg_Rxattrwalk_format(struct lib9p_msg_Rxattrwalk *self, struct fmt_state *state) {
- fmt_state_puts(state, "Rxattrwalk {");
- fmt_state_puts(state, " tag=");
- lib9p_tag_format(&self->tag, state);
- fmt_state_puts(state, " attr_size=");
- fmt_state_printf(state, "%"PRIu64, self->attr_size);
- fmt_state_puts(state, " }");
-}
-
-static void lib9p_msg_Txattrcreate_format(struct lib9p_msg_Txattrcreate *self, struct fmt_state *state) {
- fmt_state_puts(state, "Txattrcreate {");
- fmt_state_puts(state, " tag=");
- lib9p_tag_format(&self->tag, state);
- fmt_state_puts(state, " fid=");
- lib9p_fid_format(&self->fid, state);
- fmt_state_puts(state, " name=");
- lib9p_s_format(&self->name, state);
- fmt_state_puts(state, " attr_size=");
- fmt_state_printf(state, "%"PRIu64, self->attr_size);
- fmt_state_puts(state, " flags=");
- fmt_state_printf(state, "%"PRIu32, self->flags);
- fmt_state_puts(state, " }");
-}
-
-static void lib9p_msg_Rxattrcreate_format(struct lib9p_msg_Rxattrcreate *self, struct fmt_state *state) {
- fmt_state_puts(state, "Rxattrcreate {");
- fmt_state_puts(state, " tag=");
- lib9p_tag_format(&self->tag, state);
- fmt_state_puts(state, " }");
-}
-
-static void lib9p_msg_Treaddir_format(struct lib9p_msg_Treaddir *self, struct fmt_state *state) {
- fmt_state_puts(state, "Treaddir {");
- fmt_state_puts(state, " tag=");
- lib9p_tag_format(&self->tag, state);
- fmt_state_puts(state, " fid=");
- lib9p_fid_format(&self->fid, state);
- fmt_state_puts(state, " offset=");
- fmt_state_printf(state, "%"PRIu64, self->offset);
- fmt_state_puts(state, " count=");
- fmt_state_printf(state, "%"PRIu32, self->count);
- fmt_state_puts(state, " }");
-}
-
-static void lib9p_msg_Rreaddir_format(struct lib9p_msg_Rreaddir *self, struct fmt_state *state) {
- fmt_state_puts(state, "Rreaddir {");
- fmt_state_puts(state, " tag=");
- lib9p_tag_format(&self->tag, state);
- fmt_state_puts(state, " count=");
- fmt_state_printf(state, "%"PRIu32, self->count);
- fmt_state_puts(state, " data=<bytedata>");
- fmt_state_puts(state, " }");
-}
-
-static void lib9p_msg_Tfsync_format(struct lib9p_msg_Tfsync *self, struct fmt_state *state) {
- fmt_state_puts(state, "Tfsync {");
- fmt_state_puts(state, " tag=");
- lib9p_tag_format(&self->tag, state);
- fmt_state_puts(state, " fid=");
- lib9p_fid_format(&self->fid, state);
- fmt_state_puts(state, " datasync=");
- lib9p_b4_format(&self->datasync, state);
- fmt_state_puts(state, " }");
-}
-
-static void lib9p_msg_Rfsync_format(struct lib9p_msg_Rfsync *self, struct fmt_state *state) {
- fmt_state_puts(state, "Rfsync {");
- fmt_state_puts(state, " tag=");
- lib9p_tag_format(&self->tag, state);
- fmt_state_puts(state, " }");
-}
-
-static void lib9p_msg_Tlock_format(struct lib9p_msg_Tlock *self, struct fmt_state *state) {
- fmt_state_puts(state, "Tlock {");
- fmt_state_puts(state, " tag=");
- lib9p_tag_format(&self->tag, state);
- fmt_state_puts(state, " fid=");
- lib9p_fid_format(&self->fid, state);
- fmt_state_puts(state, " type=");
- lib9p_lock_type_format(&self->type, state);
- fmt_state_puts(state, " flags=");
- lib9p_lock_flags_format(&self->flags, state);
- fmt_state_puts(state, " start=");
- fmt_state_printf(state, "%"PRIu64, self->start);
- fmt_state_puts(state, " length=");
- fmt_state_printf(state, "%"PRIu64, self->length);
- fmt_state_puts(state, " proc_id=");
- fmt_state_printf(state, "%"PRIu32, self->proc_id);
- fmt_state_puts(state, " client_id=");
- lib9p_s_format(&self->client_id, state);
- fmt_state_puts(state, " }");
-}
-
-static void lib9p_msg_Rlock_format(struct lib9p_msg_Rlock *self, struct fmt_state *state) {
- fmt_state_puts(state, "Rlock {");
- fmt_state_puts(state, " tag=");
- lib9p_tag_format(&self->tag, state);
- fmt_state_puts(state, " status=");
- lib9p_lock_status_format(&self->status, state);
- fmt_state_puts(state, " }");
-}
-
-static void lib9p_msg_Tgetlock_format(struct lib9p_msg_Tgetlock *self, struct fmt_state *state) {
- fmt_state_puts(state, "Tgetlock {");
- fmt_state_puts(state, " tag=");
- lib9p_tag_format(&self->tag, state);
- fmt_state_puts(state, " fid=");
- lib9p_fid_format(&self->fid, state);
- fmt_state_puts(state, " type=");
- lib9p_lock_type_format(&self->type, state);
- fmt_state_puts(state, " start=");
- fmt_state_printf(state, "%"PRIu64, self->start);
- fmt_state_puts(state, " length=");
- fmt_state_printf(state, "%"PRIu64, self->length);
- fmt_state_puts(state, " proc_id=");
- fmt_state_printf(state, "%"PRIu32, self->proc_id);
- fmt_state_puts(state, " client_id=");
- lib9p_s_format(&self->client_id, state);
- fmt_state_puts(state, " }");
-}
-
-static void lib9p_msg_Rgetlock_format(struct lib9p_msg_Rgetlock *self, struct fmt_state *state) {
- fmt_state_puts(state, "Rgetlock {");
- fmt_state_puts(state, " tag=");
- lib9p_tag_format(&self->tag, state);
- fmt_state_puts(state, " type=");
- lib9p_lock_type_format(&self->type, state);
- fmt_state_puts(state, " start=");
- fmt_state_printf(state, "%"PRIu64, self->start);
- fmt_state_puts(state, " length=");
- fmt_state_printf(state, "%"PRIu64, self->length);
- fmt_state_puts(state, " proc_id=");
- fmt_state_printf(state, "%"PRIu32, self->proc_id);
- fmt_state_puts(state, " client_id=");
- lib9p_s_format(&self->client_id, state);
- fmt_state_puts(state, " }");
-}
-
-static void lib9p_msg_Tlink_format(struct lib9p_msg_Tlink *self, struct fmt_state *state) {
- fmt_state_puts(state, "Tlink {");
- fmt_state_puts(state, " tag=");
- lib9p_tag_format(&self->tag, state);
- fmt_state_puts(state, " dfid=");
- lib9p_fid_format(&self->dfid, state);
- fmt_state_puts(state, " fid=");
- lib9p_fid_format(&self->fid, state);
- fmt_state_puts(state, " name=");
- lib9p_s_format(&self->name, state);
- fmt_state_puts(state, " }");
-}
-
-static void lib9p_msg_Rlink_format(struct lib9p_msg_Rlink *self, struct fmt_state *state) {
- fmt_state_puts(state, "Rlink {");
- fmt_state_puts(state, " tag=");
- lib9p_tag_format(&self->tag, state);
- fmt_state_puts(state, " }");
-}
-
-static void lib9p_msg_Tmkdir_format(struct lib9p_msg_Tmkdir *self, struct fmt_state *state) {
- fmt_state_puts(state, "Tmkdir {");
- fmt_state_puts(state, " tag=");
- lib9p_tag_format(&self->tag, state);
- fmt_state_puts(state, " dfid=");
- lib9p_fid_format(&self->dfid, state);
- fmt_state_puts(state, " name=");
- lib9p_s_format(&self->name, state);
- fmt_state_puts(state, " mode=");
- lib9p_mode_format(&self->mode, state);
- fmt_state_puts(state, " gid=");
- lib9p_nuid_format(&self->gid, state);
- fmt_state_puts(state, " }");
-}
-
-static void lib9p_msg_Rmkdir_format(struct lib9p_msg_Rmkdir *self, struct fmt_state *state) {
- fmt_state_puts(state, "Rmkdir {");
- fmt_state_puts(state, " tag=");
- lib9p_tag_format(&self->tag, state);
- fmt_state_puts(state, " qid=");
- lib9p_qid_format(&self->qid, state);
- fmt_state_puts(state, " }");
-}
-
-static void lib9p_msg_Trenameat_format(struct lib9p_msg_Trenameat *self, struct fmt_state *state) {
- fmt_state_puts(state, "Trenameat {");
- fmt_state_puts(state, " tag=");
- lib9p_tag_format(&self->tag, state);
- fmt_state_puts(state, " olddirfid=");
- lib9p_fid_format(&self->olddirfid, state);
- fmt_state_puts(state, " oldname=");
- lib9p_s_format(&self->oldname, state);
- fmt_state_puts(state, " newdirfid=");
- lib9p_fid_format(&self->newdirfid, state);
- fmt_state_puts(state, " newname=");
- lib9p_s_format(&self->newname, state);
- fmt_state_puts(state, " }");
-}
-
-static void lib9p_msg_Rrenameat_format(struct lib9p_msg_Rrenameat *self, struct fmt_state *state) {
- fmt_state_puts(state, "Rrenameat {");
- fmt_state_puts(state, " tag=");
- lib9p_tag_format(&self->tag, state);
- fmt_state_puts(state, " }");
-}
-
-static void lib9p_msg_Tunlinkat_format(struct lib9p_msg_Tunlinkat *self, struct fmt_state *state) {
- fmt_state_puts(state, "Tunlinkat {");
- fmt_state_puts(state, " tag=");
- lib9p_tag_format(&self->tag, state);
- fmt_state_puts(state, " dirfd=");
- lib9p_fid_format(&self->dirfd, state);
- fmt_state_puts(state, " name=");
- lib9p_s_format(&self->name, state);
- fmt_state_puts(state, " flags=");
- fmt_state_printf(state, "%"PRIu32, self->flags);
- fmt_state_puts(state, " }");
-}
-
-static void lib9p_msg_Runlinkat_format(struct lib9p_msg_Runlinkat *self, struct fmt_state *state) {
- fmt_state_puts(state, "Runlinkat {");
- fmt_state_puts(state, " tag=");
- lib9p_tag_format(&self->tag, state);
- fmt_state_puts(state, " }");
-}
-
-#endif /* CONFIG_9P_ENABLE_9P2000_L */
-#if CONFIG_9P_ENABLE_9P2000_e
-static void lib9p_msg_Tsession_format(struct lib9p_msg_Tsession *self, struct fmt_state *state) {
- fmt_state_puts(state, "Tsession {");
- fmt_state_puts(state, " tag=");
- lib9p_tag_format(&self->tag, state);
- fmt_state_puts(state, " key=");
- fmt_state_printf(state, "%"PRIu64, self->key);
- fmt_state_puts(state, " }");
-}
-
-static void lib9p_msg_Rsession_format(struct lib9p_msg_Rsession *self, struct fmt_state *state) {
- fmt_state_puts(state, "Rsession {");
- fmt_state_puts(state, " tag=");
- lib9p_tag_format(&self->tag, state);
- fmt_state_puts(state, " }");
-}
-
-static void lib9p_msg_Tsread_format(struct lib9p_msg_Tsread *self, struct fmt_state *state) {
- fmt_state_puts(state, "Tsread {");
- fmt_state_puts(state, " tag=");
- lib9p_tag_format(&self->tag, state);
- fmt_state_puts(state, " fid=");
- fmt_state_printf(state, "%"PRIu32, self->fid);
- fmt_state_puts(state, " nwname=");
- fmt_state_printf(state, "%"PRIu16, self->nwname);
- fmt_state_puts(state, " wname=[");
- for (uint16_t i = 0; i < self->nwname; i++) {
- if (i)
- fmt_state_puts(state, ", ");
- lib9p_s_format(&self->wname[i], state);
- }
- fmt_state_puts(state, " ]");
- fmt_state_puts(state, " }");
-}
-
-static void lib9p_msg_Rsread_format(struct lib9p_msg_Rsread *self, struct fmt_state *state) {
- fmt_state_puts(state, "Rsread {");
- fmt_state_puts(state, " tag=");
- lib9p_tag_format(&self->tag, state);
- fmt_state_puts(state, " count=");
- fmt_state_printf(state, "%"PRIu32, self->count);
- fmt_state_puts(state, " data=<bytedata>");
- fmt_state_puts(state, " }");
-}
-
-static void lib9p_msg_Tswrite_format(struct lib9p_msg_Tswrite *self, struct fmt_state *state) {
- fmt_state_puts(state, "Tswrite {");
- fmt_state_puts(state, " tag=");
- lib9p_tag_format(&self->tag, state);
- fmt_state_puts(state, " fid=");
- fmt_state_printf(state, "%"PRIu32, self->fid);
- fmt_state_puts(state, " nwname=");
- fmt_state_printf(state, "%"PRIu16, self->nwname);
- fmt_state_puts(state, " wname=[");
- for (uint16_t i = 0; i < self->nwname; i++) {
- if (i)
- fmt_state_puts(state, ", ");
- lib9p_s_format(&self->wname[i], state);
- }
- fmt_state_puts(state, " ]");
- fmt_state_puts(state, " count=");
- fmt_state_printf(state, "%"PRIu32, self->count);
- fmt_state_puts(state, " data=<bytedata>");
- fmt_state_puts(state, " }");
-}
-
-static void lib9p_msg_Rswrite_format(struct lib9p_msg_Rswrite *self, struct fmt_state *state) {
- fmt_state_puts(state, "Rswrite {");
- fmt_state_puts(state, " tag=");
- lib9p_tag_format(&self->tag, state);
- fmt_state_puts(state, " count=");
- fmt_state_printf(state, "%"PRIu32, self->count);
- fmt_state_puts(state, " }");
-}
-#endif /* CONFIG_9P_ENABLE_9P2000_e */
-
-/* tables.h *******************************************************************/
-
-const struct _lib9p_ver_tentry _lib9p_table_ver[LIB9P_VER_NUM] = {
- [LIB9P_VER_unknown] = {.name="unknown", .min_msg_size=9},
-#if CONFIG_9P_ENABLE_9P2000
- [LIB9P_VER_9P2000] = {.name="9P2000", .min_msg_size=9},
-#endif /* CONFIG_9P_ENABLE_9P2000 */
-#if CONFIG_9P_ENABLE_9P2000_L
- [LIB9P_VER_9P2000_L] = {.name="9P2000.L", .min_msg_size=9},
-#endif /* CONFIG_9P_ENABLE_9P2000_L */
-#if CONFIG_9P_ENABLE_9P2000_e
- [LIB9P_VER_9P2000_e] = {.name="9P2000.e", .min_msg_size=9},
-#endif /* CONFIG_9P_ENABLE_9P2000_e */
-#if CONFIG_9P_ENABLE_9P2000_p9p
- [LIB9P_VER_9P2000_p9p] = {.name="9P2000.p9p", .min_msg_size=9},
-#endif /* CONFIG_9P_ENABLE_9P2000_p9p */
-#if CONFIG_9P_ENABLE_9P2000_u
- [LIB9P_VER_9P2000_u] = {.name="9P2000.u", .min_msg_size=13},
-#endif /* CONFIG_9P_ENABLE_9P2000_u */
-};
-
-#define _MSG(typ) [LIB9P_TYP_##typ] = { \
- .name = #typ, \
- .box_as_fmt_formatter = (_box_as_fmt_formatter_fn_t)lo_box_lib9p_msg_##typ##_as_fmt_formatter, \
- }
-const struct _lib9p_msg_tentry _lib9p_table_msg[LIB9P_VER_NUM][0x100] = {
- [LIB9P_VER_unknown] = {
- _MSG(Tversion),
- _MSG(Rversion),
- _MSG(Rerror),
- },
-#if CONFIG_9P_ENABLE_9P2000
- [LIB9P_VER_9P2000] = {
- _MSG(Tversion),
- _MSG(Rversion),
- _MSG(Tauth),
- _MSG(Rauth),
- _MSG(Tattach),
- _MSG(Rattach),
- _MSG(Rerror),
- _MSG(Tflush),
- _MSG(Rflush),
- _MSG(Twalk),
- _MSG(Rwalk),
- _MSG(Topen),
- _MSG(Ropen),
- _MSG(Tcreate),
- _MSG(Rcreate),
- _MSG(Tread),
- _MSG(Rread),
- _MSG(Twrite),
- _MSG(Rwrite),
- _MSG(Tclunk),
- _MSG(Rclunk),
- _MSG(Tremove),
- _MSG(Rremove),
- _MSG(Tstat),
- _MSG(Rstat),
- _MSG(Twstat),
- _MSG(Rwstat),
- },
-#endif /* CONFIG_9P_ENABLE_9P2000 */
-#if CONFIG_9P_ENABLE_9P2000_L
- [LIB9P_VER_9P2000_L] = {
- _MSG(Rlerror),
- _MSG(Tstatfs),
- _MSG(Rstatfs),
- _MSG(Tlopen),
- _MSG(Rlopen),
- _MSG(Tlcreate),
- _MSG(Rlcreate),
- _MSG(Tsymlink),
- _MSG(Rsymlink),
- _MSG(Tmknod),
- _MSG(Rmknod),
- _MSG(Trename),
- _MSG(Rrename),
- _MSG(Treadlink),
- _MSG(Rreadlink),
- _MSG(Tgetattr),
- _MSG(Rgetattr),
- _MSG(Tsetattr),
- _MSG(Rsetattr),
- _MSG(Txattrwalk),
- _MSG(Rxattrwalk),
- _MSG(Txattrcreate),
- _MSG(Rxattrcreate),
- _MSG(Treaddir),
- _MSG(Rreaddir),
- _MSG(Tfsync),
- _MSG(Rfsync),
- _MSG(Tlock),
- _MSG(Rlock),
- _MSG(Tgetlock),
- _MSG(Rgetlock),
- _MSG(Tlink),
- _MSG(Rlink),
- _MSG(Tmkdir),
- _MSG(Rmkdir),
- _MSG(Trenameat),
- _MSG(Rrenameat),
- _MSG(Tunlinkat),
- _MSG(Runlinkat),
- _MSG(Tversion),
- _MSG(Rversion),
- _MSG(Tauth),
- _MSG(Rauth),
- _MSG(Tattach),
- _MSG(Rattach),
- _MSG(Rerror),
- _MSG(Tflush),
- _MSG(Rflush),
- _MSG(Twalk),
- _MSG(Rwalk),
- _MSG(Tread),
- _MSG(Rread),
- _MSG(Twrite),
- _MSG(Rwrite),
- _MSG(Tclunk),
- _MSG(Rclunk),
- _MSG(Tremove),
- _MSG(Rremove),
- },
-#endif /* CONFIG_9P_ENABLE_9P2000_L */
-#if CONFIG_9P_ENABLE_9P2000_e
- [LIB9P_VER_9P2000_e] = {
- _MSG(Tversion),
- _MSG(Rversion),
- _MSG(Tauth),
- _MSG(Rauth),
- _MSG(Tattach),
- _MSG(Rattach),
- _MSG(Rerror),
- _MSG(Tflush),
- _MSG(Rflush),
- _MSG(Twalk),
- _MSG(Rwalk),
- _MSG(Topen),
- _MSG(Ropen),
- _MSG(Tcreate),
- _MSG(Rcreate),
- _MSG(Tread),
- _MSG(Rread),
- _MSG(Twrite),
- _MSG(Rwrite),
- _MSG(Tclunk),
- _MSG(Rclunk),
- _MSG(Tremove),
- _MSG(Rremove),
- _MSG(Tstat),
- _MSG(Rstat),
- _MSG(Twstat),
- _MSG(Rwstat),
- _MSG(Tsession),
- _MSG(Rsession),
- _MSG(Tsread),
- _MSG(Rsread),
- _MSG(Tswrite),
- _MSG(Rswrite),
- },
-#endif /* CONFIG_9P_ENABLE_9P2000_e */
-#if CONFIG_9P_ENABLE_9P2000_p9p
- [LIB9P_VER_9P2000_p9p] = {
- _MSG(Topenfd),
- _MSG(Ropenfd),
- _MSG(Tversion),
- _MSG(Rversion),
- _MSG(Tauth),
- _MSG(Rauth),
- _MSG(Tattach),
- _MSG(Rattach),
- _MSG(Rerror),
- _MSG(Tflush),
- _MSG(Rflush),
- _MSG(Twalk),
- _MSG(Rwalk),
- _MSG(Topen),
- _MSG(Ropen),
- _MSG(Tcreate),
- _MSG(Rcreate),
- _MSG(Tread),
- _MSG(Rread),
- _MSG(Twrite),
- _MSG(Rwrite),
- _MSG(Tclunk),
- _MSG(Rclunk),
- _MSG(Tremove),
- _MSG(Rremove),
- _MSG(Tstat),
- _MSG(Rstat),
- _MSG(Twstat),
- _MSG(Rwstat),
- },
-#endif /* CONFIG_9P_ENABLE_9P2000_p9p */
-#if CONFIG_9P_ENABLE_9P2000_u
- [LIB9P_VER_9P2000_u] = {
- _MSG(Tversion),
- _MSG(Rversion),
- _MSG(Tauth),
- _MSG(Rauth),
- _MSG(Tattach),
- _MSG(Rattach),
- _MSG(Rerror),
- _MSG(Tflush),
- _MSG(Rflush),
- _MSG(Twalk),
- _MSG(Rwalk),
- _MSG(Topen),
- _MSG(Ropen),
- _MSG(Tcreate),
- _MSG(Rcreate),
- _MSG(Tread),
- _MSG(Rread),
- _MSG(Twrite),
- _MSG(Rwrite),
- _MSG(Tclunk),
- _MSG(Rclunk),
- _MSG(Tremove),
- _MSG(Rremove),
- _MSG(Tstat),
- _MSG(Rstat),
- _MSG(Twstat),
- _MSG(Rwstat),
- },
-#endif /* CONFIG_9P_ENABLE_9P2000_u */
-};
-
-#define _MSG_RECV(typ) [LIB9P_TYP_##typ/2] = { \
- .validate = validate_##typ, \
- .unmarshal = (_unmarshal_fn_t)unmarshal_##typ, \
- }
-#define _MSG_SEND(typ) [LIB9P_TYP_##typ/2] = { \
- .marshal = (_marshal_fn_t)marshal_##typ, \
- }
-
-const struct _lib9p_recv_tentry _lib9p_table_Tmsg_recv[LIB9P_VER_NUM][0x80] = {
- [LIB9P_VER_unknown] = {
- _MSG_RECV(Tversion),
- },
-#if CONFIG_9P_ENABLE_9P2000
- [LIB9P_VER_9P2000] = {
- _MSG_RECV(Tversion),
- _MSG_RECV(Tauth),
- _MSG_RECV(Tattach),
- _MSG_RECV(Tflush),
- _MSG_RECV(Twalk),
- _MSG_RECV(Topen),
- _MSG_RECV(Tcreate),
- _MSG_RECV(Tread),
- _MSG_RECV(Twrite),
- _MSG_RECV(Tclunk),
- _MSG_RECV(Tremove),
- _MSG_RECV(Tstat),
- _MSG_RECV(Twstat),
- },
-#endif /* CONFIG_9P_ENABLE_9P2000 */
-#if CONFIG_9P_ENABLE_9P2000_L
- [LIB9P_VER_9P2000_L] = {
- _MSG_RECV(Tstatfs),
- _MSG_RECV(Tlopen),
- _MSG_RECV(Tlcreate),
- _MSG_RECV(Tsymlink),
- _MSG_RECV(Tmknod),
- _MSG_RECV(Trename),
- _MSG_RECV(Treadlink),
- _MSG_RECV(Tgetattr),
- _MSG_RECV(Tsetattr),
- _MSG_RECV(Txattrwalk),
- _MSG_RECV(Txattrcreate),
- _MSG_RECV(Treaddir),
- _MSG_RECV(Tfsync),
- _MSG_RECV(Tlock),
- _MSG_RECV(Tgetlock),
- _MSG_RECV(Tlink),
- _MSG_RECV(Tmkdir),
- _MSG_RECV(Trenameat),
- _MSG_RECV(Tunlinkat),
- _MSG_RECV(Tversion),
- _MSG_RECV(Tauth),
- _MSG_RECV(Tattach),
- _MSG_RECV(Tflush),
- _MSG_RECV(Twalk),
- _MSG_RECV(Tread),
- _MSG_RECV(Twrite),
- _MSG_RECV(Tclunk),
- _MSG_RECV(Tremove),
- },
-#endif /* CONFIG_9P_ENABLE_9P2000_L */
-#if CONFIG_9P_ENABLE_9P2000_e
- [LIB9P_VER_9P2000_e] = {
- _MSG_RECV(Tversion),
- _MSG_RECV(Tauth),
- _MSG_RECV(Tattach),
- _MSG_RECV(Tflush),
- _MSG_RECV(Twalk),
- _MSG_RECV(Topen),
- _MSG_RECV(Tcreate),
- _MSG_RECV(Tread),
- _MSG_RECV(Twrite),
- _MSG_RECV(Tclunk),
- _MSG_RECV(Tremove),
- _MSG_RECV(Tstat),
- _MSG_RECV(Twstat),
- _MSG_RECV(Tsession),
- _MSG_RECV(Tsread),
- _MSG_RECV(Tswrite),
- },
-#endif /* CONFIG_9P_ENABLE_9P2000_e */
-#if CONFIG_9P_ENABLE_9P2000_p9p
- [LIB9P_VER_9P2000_p9p] = {
- _MSG_RECV(Topenfd),
- _MSG_RECV(Tversion),
- _MSG_RECV(Tauth),
- _MSG_RECV(Tattach),
- _MSG_RECV(Tflush),
- _MSG_RECV(Twalk),
- _MSG_RECV(Topen),
- _MSG_RECV(Tcreate),
- _MSG_RECV(Tread),
- _MSG_RECV(Twrite),
- _MSG_RECV(Tclunk),
- _MSG_RECV(Tremove),
- _MSG_RECV(Tstat),
- _MSG_RECV(Twstat),
- },
-#endif /* CONFIG_9P_ENABLE_9P2000_p9p */
-#if CONFIG_9P_ENABLE_9P2000_u
- [LIB9P_VER_9P2000_u] = {
- _MSG_RECV(Tversion),
- _MSG_RECV(Tauth),
- _MSG_RECV(Tattach),
- _MSG_RECV(Tflush),
- _MSG_RECV(Twalk),
- _MSG_RECV(Topen),
- _MSG_RECV(Tcreate),
- _MSG_RECV(Tread),
- _MSG_RECV(Twrite),
- _MSG_RECV(Tclunk),
- _MSG_RECV(Tremove),
- _MSG_RECV(Tstat),
- _MSG_RECV(Twstat),
- },
-#endif /* CONFIG_9P_ENABLE_9P2000_u */
-};
-
-const struct _lib9p_recv_tentry _lib9p_table_Rmsg_recv[LIB9P_VER_NUM][0x80] = {
- [LIB9P_VER_unknown] = {
- _MSG_RECV(Rversion),
- _MSG_RECV(Rerror),
- },
-#if CONFIG_9P_ENABLE_9P2000
- [LIB9P_VER_9P2000] = {
- _MSG_RECV(Rversion),
- _MSG_RECV(Rauth),
- _MSG_RECV(Rattach),
- _MSG_RECV(Rerror),
- _MSG_RECV(Rflush),
- _MSG_RECV(Rwalk),
- _MSG_RECV(Ropen),
- _MSG_RECV(Rcreate),
- _MSG_RECV(Rread),
- _MSG_RECV(Rwrite),
- _MSG_RECV(Rclunk),
- _MSG_RECV(Rremove),
- _MSG_RECV(Rstat),
- _MSG_RECV(Rwstat),
- },
-#endif /* CONFIG_9P_ENABLE_9P2000 */
-#if CONFIG_9P_ENABLE_9P2000_L
- [LIB9P_VER_9P2000_L] = {
- _MSG_RECV(Rlerror),
- _MSG_RECV(Rstatfs),
- _MSG_RECV(Rlopen),
- _MSG_RECV(Rlcreate),
- _MSG_RECV(Rsymlink),
- _MSG_RECV(Rmknod),
- _MSG_RECV(Rrename),
- _MSG_RECV(Rreadlink),
- _MSG_RECV(Rgetattr),
- _MSG_RECV(Rsetattr),
- _MSG_RECV(Rxattrwalk),
- _MSG_RECV(Rxattrcreate),
- _MSG_RECV(Rreaddir),
- _MSG_RECV(Rfsync),
- _MSG_RECV(Rlock),
- _MSG_RECV(Rgetlock),
- _MSG_RECV(Rlink),
- _MSG_RECV(Rmkdir),
- _MSG_RECV(Rrenameat),
- _MSG_RECV(Runlinkat),
- _MSG_RECV(Rversion),
- _MSG_RECV(Rauth),
- _MSG_RECV(Rattach),
- _MSG_RECV(Rerror),
- _MSG_RECV(Rflush),
- _MSG_RECV(Rwalk),
- _MSG_RECV(Rread),
- _MSG_RECV(Rwrite),
- _MSG_RECV(Rclunk),
- _MSG_RECV(Rremove),
- },
-#endif /* CONFIG_9P_ENABLE_9P2000_L */
-#if CONFIG_9P_ENABLE_9P2000_e
- [LIB9P_VER_9P2000_e] = {
- _MSG_RECV(Rversion),
- _MSG_RECV(Rauth),
- _MSG_RECV(Rattach),
- _MSG_RECV(Rerror),
- _MSG_RECV(Rflush),
- _MSG_RECV(Rwalk),
- _MSG_RECV(Ropen),
- _MSG_RECV(Rcreate),
- _MSG_RECV(Rread),
- _MSG_RECV(Rwrite),
- _MSG_RECV(Rclunk),
- _MSG_RECV(Rremove),
- _MSG_RECV(Rstat),
- _MSG_RECV(Rwstat),
- _MSG_RECV(Rsession),
- _MSG_RECV(Rsread),
- _MSG_RECV(Rswrite),
- },
-#endif /* CONFIG_9P_ENABLE_9P2000_e */
-#if CONFIG_9P_ENABLE_9P2000_p9p
- [LIB9P_VER_9P2000_p9p] = {
- _MSG_RECV(Ropenfd),
- _MSG_RECV(Rversion),
- _MSG_RECV(Rauth),
- _MSG_RECV(Rattach),
- _MSG_RECV(Rerror),
- _MSG_RECV(Rflush),
- _MSG_RECV(Rwalk),
- _MSG_RECV(Ropen),
- _MSG_RECV(Rcreate),
- _MSG_RECV(Rread),
- _MSG_RECV(Rwrite),
- _MSG_RECV(Rclunk),
- _MSG_RECV(Rremove),
- _MSG_RECV(Rstat),
- _MSG_RECV(Rwstat),
- },
-#endif /* CONFIG_9P_ENABLE_9P2000_p9p */
-#if CONFIG_9P_ENABLE_9P2000_u
- [LIB9P_VER_9P2000_u] = {
- _MSG_RECV(Rversion),
- _MSG_RECV(Rauth),
- _MSG_RECV(Rattach),
- _MSG_RECV(Rerror),
- _MSG_RECV(Rflush),
- _MSG_RECV(Rwalk),
- _MSG_RECV(Ropen),
- _MSG_RECV(Rcreate),
- _MSG_RECV(Rread),
- _MSG_RECV(Rwrite),
- _MSG_RECV(Rclunk),
- _MSG_RECV(Rremove),
- _MSG_RECV(Rstat),
- _MSG_RECV(Rwstat),
- },
-#endif /* CONFIG_9P_ENABLE_9P2000_u */
-};
-
-const struct _lib9p_send_tentry _lib9p_table_Tmsg_send[LIB9P_VER_NUM][0x80] = {
- [LIB9P_VER_unknown] = {
- _MSG_SEND(Tversion),
- },
-#if CONFIG_9P_ENABLE_9P2000
- [LIB9P_VER_9P2000] = {
- _MSG_SEND(Tversion),
- _MSG_SEND(Tauth),
- _MSG_SEND(Tattach),
- _MSG_SEND(Tflush),
- _MSG_SEND(Twalk),
- _MSG_SEND(Topen),
- _MSG_SEND(Tcreate),
- _MSG_SEND(Tread),
- _MSG_SEND(Twrite),
- _MSG_SEND(Tclunk),
- _MSG_SEND(Tremove),
- _MSG_SEND(Tstat),
- _MSG_SEND(Twstat),
- },
-#endif /* CONFIG_9P_ENABLE_9P2000 */
-#if CONFIG_9P_ENABLE_9P2000_L
- [LIB9P_VER_9P2000_L] = {
- _MSG_SEND(Tstatfs),
- _MSG_SEND(Tlopen),
- _MSG_SEND(Tlcreate),
- _MSG_SEND(Tsymlink),
- _MSG_SEND(Tmknod),
- _MSG_SEND(Trename),
- _MSG_SEND(Treadlink),
- _MSG_SEND(Tgetattr),
- _MSG_SEND(Tsetattr),
- _MSG_SEND(Txattrwalk),
- _MSG_SEND(Txattrcreate),
- _MSG_SEND(Treaddir),
- _MSG_SEND(Tfsync),
- _MSG_SEND(Tlock),
- _MSG_SEND(Tgetlock),
- _MSG_SEND(Tlink),
- _MSG_SEND(Tmkdir),
- _MSG_SEND(Trenameat),
- _MSG_SEND(Tunlinkat),
- _MSG_SEND(Tversion),
- _MSG_SEND(Tauth),
- _MSG_SEND(Tattach),
- _MSG_SEND(Tflush),
- _MSG_SEND(Twalk),
- _MSG_SEND(Tread),
- _MSG_SEND(Twrite),
- _MSG_SEND(Tclunk),
- _MSG_SEND(Tremove),
- },
-#endif /* CONFIG_9P_ENABLE_9P2000_L */
-#if CONFIG_9P_ENABLE_9P2000_e
- [LIB9P_VER_9P2000_e] = {
- _MSG_SEND(Tversion),
- _MSG_SEND(Tauth),
- _MSG_SEND(Tattach),
- _MSG_SEND(Tflush),
- _MSG_SEND(Twalk),
- _MSG_SEND(Topen),
- _MSG_SEND(Tcreate),
- _MSG_SEND(Tread),
- _MSG_SEND(Twrite),
- _MSG_SEND(Tclunk),
- _MSG_SEND(Tremove),
- _MSG_SEND(Tstat),
- _MSG_SEND(Twstat),
- _MSG_SEND(Tsession),
- _MSG_SEND(Tsread),
- _MSG_SEND(Tswrite),
- },
-#endif /* CONFIG_9P_ENABLE_9P2000_e */
-#if CONFIG_9P_ENABLE_9P2000_p9p
- [LIB9P_VER_9P2000_p9p] = {
- _MSG_SEND(Topenfd),
- _MSG_SEND(Tversion),
- _MSG_SEND(Tauth),
- _MSG_SEND(Tattach),
- _MSG_SEND(Tflush),
- _MSG_SEND(Twalk),
- _MSG_SEND(Topen),
- _MSG_SEND(Tcreate),
- _MSG_SEND(Tread),
- _MSG_SEND(Twrite),
- _MSG_SEND(Tclunk),
- _MSG_SEND(Tremove),
- _MSG_SEND(Tstat),
- _MSG_SEND(Twstat),
- },
-#endif /* CONFIG_9P_ENABLE_9P2000_p9p */
-#if CONFIG_9P_ENABLE_9P2000_u
- [LIB9P_VER_9P2000_u] = {
- _MSG_SEND(Tversion),
- _MSG_SEND(Tauth),
- _MSG_SEND(Tattach),
- _MSG_SEND(Tflush),
- _MSG_SEND(Twalk),
- _MSG_SEND(Topen),
- _MSG_SEND(Tcreate),
- _MSG_SEND(Tread),
- _MSG_SEND(Twrite),
- _MSG_SEND(Tclunk),
- _MSG_SEND(Tremove),
- _MSG_SEND(Tstat),
- _MSG_SEND(Twstat),
- },
-#endif /* CONFIG_9P_ENABLE_9P2000_u */
-};
-
-const struct _lib9p_send_tentry _lib9p_table_Rmsg_send[LIB9P_VER_NUM][0x80] = {
- [LIB9P_VER_unknown] = {
- _MSG_SEND(Rversion),
- _MSG_SEND(Rerror),
- },
-#if CONFIG_9P_ENABLE_9P2000
- [LIB9P_VER_9P2000] = {
- _MSG_SEND(Rversion),
- _MSG_SEND(Rauth),
- _MSG_SEND(Rattach),
- _MSG_SEND(Rerror),
- _MSG_SEND(Rflush),
- _MSG_SEND(Rwalk),
- _MSG_SEND(Ropen),
- _MSG_SEND(Rcreate),
- _MSG_SEND(Rread),
- _MSG_SEND(Rwrite),
- _MSG_SEND(Rclunk),
- _MSG_SEND(Rremove),
- _MSG_SEND(Rstat),
- _MSG_SEND(Rwstat),
- },
-#endif /* CONFIG_9P_ENABLE_9P2000 */
-#if CONFIG_9P_ENABLE_9P2000_L
- [LIB9P_VER_9P2000_L] = {
- _MSG_SEND(Rlerror),
- _MSG_SEND(Rstatfs),
- _MSG_SEND(Rlopen),
- _MSG_SEND(Rlcreate),
- _MSG_SEND(Rsymlink),
- _MSG_SEND(Rmknod),
- _MSG_SEND(Rrename),
- _MSG_SEND(Rreadlink),
- _MSG_SEND(Rgetattr),
- _MSG_SEND(Rsetattr),
- _MSG_SEND(Rxattrwalk),
- _MSG_SEND(Rxattrcreate),
- _MSG_SEND(Rreaddir),
- _MSG_SEND(Rfsync),
- _MSG_SEND(Rlock),
- _MSG_SEND(Rgetlock),
- _MSG_SEND(Rlink),
- _MSG_SEND(Rmkdir),
- _MSG_SEND(Rrenameat),
- _MSG_SEND(Runlinkat),
- _MSG_SEND(Rversion),
- _MSG_SEND(Rauth),
- _MSG_SEND(Rattach),
- _MSG_SEND(Rerror),
- _MSG_SEND(Rflush),
- _MSG_SEND(Rwalk),
- _MSG_SEND(Rread),
- _MSG_SEND(Rwrite),
- _MSG_SEND(Rclunk),
- _MSG_SEND(Rremove),
- },
-#endif /* CONFIG_9P_ENABLE_9P2000_L */
-#if CONFIG_9P_ENABLE_9P2000_e
- [LIB9P_VER_9P2000_e] = {
- _MSG_SEND(Rversion),
- _MSG_SEND(Rauth),
- _MSG_SEND(Rattach),
- _MSG_SEND(Rerror),
- _MSG_SEND(Rflush),
- _MSG_SEND(Rwalk),
- _MSG_SEND(Ropen),
- _MSG_SEND(Rcreate),
- _MSG_SEND(Rread),
- _MSG_SEND(Rwrite),
- _MSG_SEND(Rclunk),
- _MSG_SEND(Rremove),
- _MSG_SEND(Rstat),
- _MSG_SEND(Rwstat),
- _MSG_SEND(Rsession),
- _MSG_SEND(Rsread),
- _MSG_SEND(Rswrite),
- },
-#endif /* CONFIG_9P_ENABLE_9P2000_e */
-#if CONFIG_9P_ENABLE_9P2000_p9p
- [LIB9P_VER_9P2000_p9p] = {
- _MSG_SEND(Ropenfd),
- _MSG_SEND(Rversion),
- _MSG_SEND(Rauth),
- _MSG_SEND(Rattach),
- _MSG_SEND(Rerror),
- _MSG_SEND(Rflush),
- _MSG_SEND(Rwalk),
- _MSG_SEND(Ropen),
- _MSG_SEND(Rcreate),
- _MSG_SEND(Rread),
- _MSG_SEND(Rwrite),
- _MSG_SEND(Rclunk),
- _MSG_SEND(Rremove),
- _MSG_SEND(Rstat),
- _MSG_SEND(Rwstat),
- },
-#endif /* CONFIG_9P_ENABLE_9P2000_p9p */
-#if CONFIG_9P_ENABLE_9P2000_u
- [LIB9P_VER_9P2000_u] = {
- _MSG_SEND(Rversion),
- _MSG_SEND(Rauth),
- _MSG_SEND(Rattach),
- _MSG_SEND(Rerror),
- _MSG_SEND(Rflush),
- _MSG_SEND(Rwalk),
- _MSG_SEND(Ropen),
- _MSG_SEND(Rcreate),
- _MSG_SEND(Rread),
- _MSG_SEND(Rwrite),
- _MSG_SEND(Rclunk),
- _MSG_SEND(Rremove),
- _MSG_SEND(Rstat),
- _MSG_SEND(Rwstat),
- },
-#endif /* CONFIG_9P_ENABLE_9P2000_u */
-};
-
-LM_FLATTEN ssize_t _lib9p_stat_validate(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes, uint32_t *ret_net_size) {
- return validate_stat(ctx, net_size, net_bytes, ret_net_size);
-}
-LM_FLATTEN void _lib9p_stat_unmarshal(struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out) {
- unmarshal_stat(ctx, net_bytes, out);
-}
-LM_FLATTEN bool _lib9p_stat_marshal(struct lib9p_ctx *ctx, struct lib9p_stat *val, struct _marshal_ret *ret) {
- return marshal_stat(ctx, val, ret);
-}
diff --git a/lib9p/CMakeLists.txt b/lib9p/CMakeLists.txt
index 11a58ba..9ef8465 100644
--- a/lib9p/CMakeLists.txt
+++ b/lib9p/CMakeLists.txt
@@ -3,27 +3,86 @@
# Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
# SPDX-License-Identifier: AGPL-3.0-or-later
-add_library(lib9p INTERFACE)
-target_include_directories(lib9p SYSTEM INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include)
-target_sources(lib9p INTERFACE
- 9p.generated.c
- 9p.c
- tables.c
- srv.c
+add_library(lib9p_core_headers INTERFACE)
+target_include_directories(lib9p_core_headers PUBLIC INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/core_include)
+target_link_libraries(lib9p_core_headers INTERFACE
+ libhw_generic_headers
+ libmisc_headers
)
-target_link_libraries(lib9p INTERFACE
- libcr_ipc
- libfmt
+
+add_library(lib9p_core INTERFACE)
+target_link_libraries(lib9p_core INTERFACE lib9p_core_headers)
+target_sources(lib9p_core INTERFACE
+ core.c
+ core_generated.c
+)
+target_link_libraries(lib9p_core INTERFACE
libhw_generic
libmisc
)
+add_library(lib9p_srv_headers INTERFACE)
+target_include_directories(lib9p_srv_headers PUBLIC INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/srv_include)
+target_link_libraries(lib9p_srv_headers INTERFACE
+ lib9p_core_headers
+ libcr_ipc_headers
+)
+
+add_library(lib9p_srv INTERFACE)
+target_link_libraries(lib9p_srv INTERFACE lib9p_srv_headers)
+target_sources(lib9p_srv INTERFACE
+ srv.c
+ srv_generated.c
+)
+target_link_libraries(lib9p_srv INTERFACE
+ lib9p_core
+ libcr_ipc
+)
+
if (ENABLE_TESTS)
add_subdirectory(tests/test_server)
- add_test(
- NAME "lib9p/runtest"
- COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/tests/runtest"
+
+ function(add_lib9p_executable arg_testname)
+ add_executable("${arg_testname}" "tests/${arg_testname}.c")
+ target_link_libraries("${arg_testname}" lib9p_core)
+ target_include_directories("${arg_testname}" PRIVATE
+ ${CMAKE_CURRENT_SOURCE_DIR}/tests
+ ${CMAKE_CURRENT_SOURCE_DIR}/tests/client_config
+ )
+ endfunction()
+ function(add_lib9p_test arg_testscript)
+ get_filename_component(tmp_basename "${arg_testscript}" "NAME")
+ add_test(
+ NAME "lib9p/${tmp_basename}"
+ COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/tests/runtest" "${arg_testscript}" "${CMAKE_CURRENT_SOURCE_DIR}/tests/${tmp_basename}.explog"
+ )
+ endfunction()
+
+ add_lib9p_test("${CMAKE_CURRENT_SOURCE_DIR}/tests/testclient-p9p")
+
+ add_lib9p_executable("testclient-sess")
+ add_lib9p_test("./testclient-sess")
+
+ add_lib9p_executable("testclient-hangup")
+ add_lib9p_test("./testclient-hangup")
+
+ set(cfg_matrix
+ "CONFIG_9P_ENABLE_9P2000;[0;1]"
+ "CONFIG_9P_ENABLE_9P2000_u;[0;1]"
+ "CONFIG_9P_ENABLE_9P2000_e;[0;1]"
+ "CONFIG_9P_ENABLE_9P2000_L;[0;1]"
+ "CONFIG_9P_ENABLE_9P2000_p9p;[0;1]"
)
- add_lib_test(lib9p test_compile)
- target_include_directories(test_compile PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_compile_config)
+ function(add_compile_test n defs)
+ add_library("test_compile${n}" STATIC "tests/test_compile.c"
+ core.c
+ core_generated.c
+ srv.c
+ srv_generated.c
+ )
+ target_link_libraries("test_compile${n}" PUBLIC lib9p_srv_headers)
+ target_include_directories("test_compile${n}" PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_compile_config)
+ target_compile_definitions("test_compile${n}" PUBLIC "${defs}")
+ endfunction()
+ apply_matrix(add_compile_test "${cfg_matrix}")
endif()
diff --git a/lib9p/core.c b/lib9p/core.c
new file mode 100644
index 0000000..ae2d3ca
--- /dev/null
+++ b/lib9p/core.c
@@ -0,0 +1,207 @@
+/* lib9p/core.c - Base 9P protocol utilities for both clients and servers
+ *
+ * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#include <string.h> /* for strlen(), strnlen(), strncpy(), memcmp(), memset() */
+
+#include <libmisc/assert.h> /* for assert() */
+#include <libmisc/endian.h> /* for uint32le_decode() */
+#include <libmisc/log.h> /* for const_byte_str() */
+
+#include <lib9p/core.h>
+
+#include "core_tables.h"
+
+/* strings ********************************************************************/
+
+struct lib9p_s lib9p_str(const char *s) {
+ if (!s)
+ return (struct lib9p_s){};
+ return (struct lib9p_s){
+ .len = strlen(s),
+ .utf8 = s,
+ };
+}
+struct lib9p_s lib9p_strn(const char *s, size_t maxlen) {
+ if (maxlen == 0 || !s)
+ return (struct lib9p_s){};
+ return (struct lib9p_s){
+ .len = strnlen(s, maxlen),
+ .utf8 = s,
+ };
+}
+struct lib9p_s lib9p_str_slice(struct lib9p_s s, uint16_t beg, uint16_t end) {
+ assert(s.len == 0 || s.utf8);
+ assert(beg <= end && end <= s.len);
+ return (struct lib9p_s){
+ .len = end - beg,
+ .utf8 = &s.utf8[beg],
+ };
+}
+bool lib9p_str_eq(struct lib9p_s a, struct lib9p_s b) {
+ return a.len == b.len &&
+ (a.len == 0 || memcmp(a.utf8, b.utf8, a.len) == 0);
+}
+
+/* bounds checks **************************************************************/
+
+static inline void assert_ver(enum lib9p_version ver) {
+ assert(0 <= ver && ver < LIB9P_VER_NUM);
+}
+
+static inline void assert_typ(enum lib9p_msg_type typ) {
+ assert(0 <= typ && typ < 0xFF);
+}
+
+/* simple lookups *************************************************************/
+
+const char *lib9p_version_str(enum lib9p_version ver) {
+ assert_ver(ver);
+ return _lib9p_table_ver[ver].name;
+}
+
+uint32_t lib9p_version_min_Rerror_size(enum lib9p_version ver) {
+ assert_ver(ver);
+ return _lib9p_table_ver[ver].min_Rerror_size;
+}
+
+uint32_t lib9p_version_min_Rread_size(enum lib9p_version ver) {
+ assert_ver(ver);
+ return _lib9p_table_ver[ver].min_Rread_size;
+}
+
+const char *lib9p_msgtype_str(enum lib9p_version ver, enum lib9p_msg_type typ) {
+ assert_ver(ver);
+ assert_typ(typ);
+ return _lib9p_table_msg[ver][typ].name ?: const_byte_str(typ);
+}
+
+/* main message functions *****************************************************/
+
+void fmt_print_lib9p_msg(lo_interface fmt_dest w, struct lib9p_ctx *ctx, enum lib9p_msg_type typ, void *body) {
+ assert(ctx);
+ assert_ver(ctx->version);
+ assert_typ(typ);
+ assert(_lib9p_table_msg[ctx->version][typ].print);
+ _lib9p_table_msg[ctx->version][typ].print(w, ctx, body);
+}
+
+#define _lib9p_validate(LOW_TYP_BIT, ERRMSG, TABLE) do { \
+ assert_ver(ctx->version); \
+ /* Inspect the first 5 bytes ourselves. */ \
+ uint32_t net_size = uint32le_decode(net_bytes); \
+ if (net_size < 5) \
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "message is impossibly short")); \
+ uint8_t typ = net_bytes[4]; \
+ if (typ % 2 != LOW_TYP_BIT) \
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EOPNOTSUPP, ERRMSG ": message_type=", \
+ lib9p_msgtype_str(ctx->version, typ))); \
+ struct _lib9p_recv_tentry tentry = TABLE[ctx->version][typ/2]; \
+ if (!tentry.validate) \
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EOPNOTSUPP, "unknown message type: ", \
+ lib9p_msgtype_str(ctx->version, typ), \
+ " (protocol_version=", lib9p_version_str(ctx->version), ")")); \
+ \
+ /* Now use the message-type-specific tentry to process the whole thing. */ \
+ return tentry.validate(ctx, net_size, net_bytes); \
+} while (0)
+
+size_t_or_error lib9p_Tmsg_validate(struct lib9p_ctx *ctx, uint8_t *net_bytes) {
+ _lib9p_validate(0, "expected a T-message but got an R-message", _lib9p_table_Tmsg_recv);
+}
+
+size_t_or_error lib9p_Rmsg_validate(struct lib9p_ctx *ctx, uint8_t *net_bytes) {
+ _lib9p_validate(1, "expected an R-message but got a T-message", _lib9p_table_Rmsg_recv);
+}
+
+#define _lib9p_unmarshal(TABLE) do { \
+ assert_ver(ctx->version); \
+ enum lib9p_msg_type typ = net_bytes[4]; \
+ *ret_typ = typ; \
+ struct _lib9p_recv_tentry tentry = TABLE[ctx->version][typ/2]; \
+ assert(tentry.unmarshal); \
+ \
+ tentry.unmarshal(ctx, net_bytes, ret_body); \
+} while (0)
+
+void lib9p_Tmsg_unmarshal(struct lib9p_ctx *ctx, uint8_t *net_bytes,
+ enum lib9p_msg_type *ret_typ, void *ret_body) {
+ _lib9p_unmarshal(_lib9p_table_Tmsg_recv);
+}
+
+void lib9p_Rmsg_unmarshal(struct lib9p_ctx *ctx, uint8_t *net_bytes,
+ enum lib9p_msg_type *ret_typ, void *ret_body) {
+ _lib9p_unmarshal(_lib9p_table_Rmsg_recv);
+}
+
+#define _lib9p_marshal(LOW_TYP_BIT, TABLE) do { \
+ assert_ver(ctx->version); \
+ assert(typ % 2 == LOW_TYP_BIT); \
+ assert_typ(typ); \
+ \
+ memset(ret, 0, sizeof(*ret)); \
+ \
+ struct _marshal_ret _ret = { \
+ .net_iov_cnt = 1, \
+ .net_iov = ret->iov, \
+ .net_copied_size = 0, \
+ .net_copied = ret->copied, \
+ }; \
+ struct _lib9p_send_tentry tentry = TABLE[ctx->version][typ/2]; \
+ assert(tentry.marshal); \
+ \
+ error ret_err = tentry.marshal(ctx, body, &_ret); \
+ if (_ret.net_iov[_ret.net_iov_cnt-1].iov_len == 0) \
+ _ret.net_iov_cnt--; \
+ \
+ ret->iov_cnt = _ret.net_iov_cnt; \
+ return ret_err; \
+} while (0)
+
+error lib9p_Tmsg_marshal(struct lib9p_ctx *ctx, enum lib9p_msg_type typ, void *body,
+ struct lib9p_Tmsg_send_buf *ret) {
+ _lib9p_marshal(0, _lib9p_table_Tmsg_send);
+}
+
+error lib9p_Rmsg_marshal(struct lib9p_ctx *ctx, enum lib9p_msg_type typ, void *body,
+ struct lib9p_Rmsg_send_buf *ret) {
+ _lib9p_marshal(1, _lib9p_table_Rmsg_send);
+}
+
+/* `struct lib9p_stat` helpers ************************************************/
+
+#if _LIB9P_ENABLE_stat
+error lib9p_stat_validate(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes,
+ uint32_t *ret_net_size, size_t *ret_host_size) {
+ size_t_or_error host_size = _lib9p_stat_validate(ctx, net_size, net_bytes, ret_net_size);
+ if (host_size.is_err)
+ return host_size.err;
+ if (ret_host_size)
+ *ret_host_size = host_size.size_t;
+ return ERROR_NULL;
+}
+
+void lib9p_stat_unmarshal(struct lib9p_ctx *ctx, uint8_t *net_bytes,
+ struct lib9p_stat *ret) {
+ _lib9p_stat_unmarshal(ctx, net_bytes, ret);
+}
+
+uint32_t lib9p_stat_marshal(struct lib9p_ctx *ctx, uint32_t max_net_size, struct lib9p_stat *obj,
+ uint8_t *ret_bytes) {
+ struct lib9p_ctx _ctx = *ctx;
+ _ctx.max_msg_size = max_net_size;
+
+ struct wr_iovec iov = {};
+ struct _marshal_ret ret = {
+ .net_iov_cnt = 1,
+ .net_iov = &iov,
+ .net_copied_size = 0,
+ .net_copied = ret_bytes,
+ };
+ if (_lib9p_stat_marshal(&_ctx, obj, &ret))
+ return 0;
+ return ret.net_iov[0].iov_len;
+}
+#endif
diff --git a/lib9p/proto.gen b/lib9p/core.gen
index 60f1347..24f66de 100755
--- a/lib9p/proto.gen
+++ b/lib9p/core.gen
@@ -1,6 +1,6 @@
-#!/usr/bin/env python
-# lib9p/proto.gen - Generate C marshalers/unmarshalers for .9p files
-# defining 9P protocol variants.
+#!/usr/bin/env python3
+# lib9p/core.gen - Generate C marshalers/unmarshalers for .9p files
+# defining 9P protocol variants.
#
# Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
# SPDX-License-Identifier: AGPL-3.0-or-later
@@ -9,7 +9,7 @@ import os.path
import sys
sys.path.insert(0, os.path.normpath(os.path.join(__file__, "..")))
-import protogen # pylint: disable=wrong-import-position
+import core_gen # pylint: disable=wrong-import-position
if __name__ == "__main__":
- protogen.main()
+ core_gen.main()
diff --git a/lib9p/protogen/__init__.py b/lib9p/core_gen/__init__.py
index c2c6173..b0da237 100644
--- a/lib9p/protogen/__init__.py
+++ b/lib9p/core_gen/__init__.py
@@ -1,4 +1,4 @@
-# lib9p/protogen/__init__.py - Generate C marshalers/unmarshalers for
+# lib9p/core_gen/__init__.py - Generate C marshalers/unmarshalers for
# .9p files defining 9P protocol variants
#
# Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
@@ -50,8 +50,10 @@ def main() -> None:
versions, typs = parser.all()
outdir = os.path.normpath(os.path.join(sys.argv[0], ".."))
with open(
- os.path.join(outdir, "include/lib9p/9p.generated.h"), "w", encoding="utf-8"
+ os.path.join(outdir, "core_include/lib9p/_core_generated.h"),
+ "w",
+ encoding="utf-8",
) as fh:
fh.write(h.gen_h(versions, typs))
- with open(os.path.join(outdir, "9p.generated.c"), "w", encoding="utf-8") as fh:
+ with open(os.path.join(outdir, "core_generated.c"), "w", encoding="utf-8") as fh:
fh.write(c.gen_c(versions, typs))
diff --git a/lib9p/protogen/c.py b/lib9p/core_gen/c.py
index a6824ce..ab34387 100644
--- a/lib9p/protogen/c.py
+++ b/lib9p/core_gen/c.py
@@ -1,4 +1,4 @@
-# lib9p/protogen/c.py - Generate 9p.generated.c
+# lib9p/core_gen/c.py - Generate core_generated.c
#
# Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
# SPDX-License-Identifier: AGPL-3.0-or-later
@@ -7,7 +7,7 @@ import sys
import idl
-from . import c9util, c_format, c_marshal, c_unmarshal, c_validate, cutil
+from . import c9util, c_fmt_print, c_marshal, c_unmarshal, c_validate, cutil
# This strives to be "general-purpose" in that it just acts on the
# *.9p inputs; but (unfortunately?) there are a few special-cases in
@@ -23,28 +23,17 @@ def gen_c(versions: set[str], typs: list[idl.UserType]) -> str:
ret = f"""/* Generated by `{' '.join(sys.argv)}`. DO NOT EDIT! */
-#include <stdbool.h>
-#include <stddef.h> /* for size_t */
-#include <inttypes.h> /* for PRI* macros */
-#include <string.h> /* for memset() */
+#include <stddef.h> /* for size_t */
+#include <string.h> /* for memset() */
#include <libmisc/assert.h>
#include <libmisc/endian.h>
+#include <libmisc/utf8.h>
-#include <lib9p/9p.h>
+#include <lib9p/core.h>
-#include "tables.h"
-#include "utf8.h"
+#include "core_tables.h"
"""
- # libobj vtables ###########################################################
- ret += """
-/* libobj vtables *************************************************************/
-"""
- for typ in typs:
- ret += cutil.ifdef_push(1, c9util.ver_ifdef(typ.in_versions))
- ret += f"LO_IMPLEMENTATION_C(fmt_formatter, {c9util.typename(typ)}, {c9util.basename(typ)}, static);\n"
- ret += cutil.ifdef_pop(0)
-
# utilities ################################################################
ret += """
/* utilities ******************************************************************/
@@ -79,7 +68,9 @@ def gen_c(versions: set[str], typs: list[idl.UserType]) -> str:
if not isinstance(typ, idl.Bitfield):
continue
ret += "\n"
- ret += cutil.ifdef_push(1, c9util.ver_ifdef(typ.in_versions))
+ ret += cutil.ifdef_push( # SPECIAL (initialization)
+ 1, c9util.ver_ifdef(typ.in_versions - {"uninitialized"})
+ )
ret += f"static const {c9util.typename(typ)} {typ.typname}_masks[{c9util.ver_enum('NUM')}] = {{\n"
verwidth = max(len(ver) for ver in versions)
for ver in sorted(versions):
@@ -110,8 +101,8 @@ def gen_c(versions: set[str], typs: list[idl.UserType]) -> str:
# marshal_* ################################################################
ret += c_marshal.gen_c_marshal(versions, typs)
- # *_format #################################################################
- ret += c_format.gen_c_format(versions, typs)
+ # fmt_print_* ##############################################################
+ ret += c_fmt_print.gen_c_fmt_print(versions, typs)
# tables.h #################################################################
ret += """
@@ -121,13 +112,15 @@ def gen_c(versions: set[str], typs: list[idl.UserType]) -> str:
ret += "\n"
ret += f"const struct {c9util.ident('_ver_tentry')} {c9util.ident('_table_ver')}[{c9util.ver_enum('NUM')}] = {{\n"
rerror = next(typ for typ in typs if typ.typname == "Rerror")
- for ver in ["unknown", *sorted(versions)]:
- if ver == "unknown":
- min_msg_size = rerror.min_size("9P2000") # SPECIAL (initialization)
- else:
- ret += cutil.ifdef_push(1, c9util.ver_ifdef({ver}))
- min_msg_size = rerror.min_size(ver)
- ret += f'\t[{c9util.ver_enum(ver)}] = {{.name="{ver}", .min_msg_size={min_msg_size}}},\n'
+ rlerror = next(typ for typ in typs if typ.typname == "Rlerror")
+ rread = next(typ for typ in typs if typ.typname == "Rread")
+ for ver in sorted(versions):
+ ret += cutil.ifdef_push(1, c9util.ver_ifdef({ver}))
+ min_rerror_size = rerror.min_size(ver)
+ if ver == "9P2000.L": # SPECIAL (9P2000.L)
+ min_rerror_size = rlerror.min_size(ver)
+ min_rread_size = rread.min_size(ver)
+ ret += f'\t[{c9util.ver_enum(ver)}] = {{.name="{ver}", .min_Rerror_size={min_rerror_size}, .min_Rread_size={min_rread_size}}},\n'
ret += cutil.ifdef_pop(0)
ret += "};\n"
@@ -135,19 +128,14 @@ def gen_c(versions: set[str], typs: list[idl.UserType]) -> str:
cstruct: str, cname: str, each: str, _range: tuple[int, int, int]
) -> str:
ret = f"const struct {c9util.ident(cstruct)} {c9util.ident(cname)}[{c9util.ver_enum('NUM')}][{hex(len(range(*_range)))}] = {{\n"
- for ver in ["unknown", *sorted(versions)]:
- if ver != "unknown":
- ret += cutil.ifdef_push(1, c9util.ver_ifdef({ver}))
+ for ver in sorted(versions):
+ ret += cutil.ifdef_push(1, c9util.ver_ifdef({ver}))
ret += f"\t[{c9util.ver_enum(ver)}] = {{\n"
for n in range(*_range):
xmsg: idl.Message | None = id2typ.get(n, None)
if xmsg:
- if ver == "unknown": # SPECIAL (initialization)
- if xmsg.typname not in ["Tversion", "Rversion", "Rerror"]:
- xmsg = None
- else:
- if ver not in xmsg.in_versions:
- xmsg = None
+ if ver not in xmsg.in_versions:
+ xmsg = None
if xmsg:
ret += f"\t\t{each}({xmsg.typname}),\n"
ret += "\t},\n"
@@ -158,8 +146,8 @@ def gen_c(versions: set[str], typs: list[idl.UserType]) -> str:
ret += "\n"
ret += cutil.macro(
f"#define _MSG(typ) [{c9util.Ident('TYP_')}##typ] = {{\n"
- f"\t\t.name = #typ,\n"
- f"\t\t.box_as_fmt_formatter = (_box_as_fmt_formatter_fn_t)lo_box_{c9util.ident('msg_')}##typ##_as_fmt_formatter,\n"
+ f"\t\t.name = #typ,\n"
+ f"\t\t.print = (_print_fn_t)fmt_print_##typ,\n"
f"\t}}\n"
)
ret += msg_table("_msg_tentry", "_table_msg", "_MSG", (0, 0x100, 1))
@@ -186,7 +174,8 @@ def gen_c(versions: set[str], typs: list[idl.UserType]) -> str:
ret += msg_table("_send_tentry", "_table_Rmsg_send", "_MSG_SEND", (1, 0x100, 2))
ret += f"""
-LM_FLATTEN ssize_t {c9util.ident('_stat_validate')}(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes, uint32_t *ret_net_size) {{
+{cutil.ifdef_push(1, c9util.ver_ifdef(next(typ for typ in typs if typ.typname == 'stat').in_versions)).rstrip()}
+LM_FLATTEN size_t_or_error {c9util.ident('_stat_validate')}(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes, uint32_t *ret_net_size) {{
\treturn validate_stat(ctx, net_size, net_bytes, ret_net_size);
}}
LM_FLATTEN void {c9util.ident('_stat_unmarshal')}(struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out) {{
@@ -195,7 +184,7 @@ LM_FLATTEN void {c9util.ident('_stat_unmarshal')}(struct lib9p_ctx *ctx, uint8_t
LM_FLATTEN bool {c9util.ident('_stat_marshal')}(struct lib9p_ctx *ctx, struct {c9util.ident('stat')} *val, struct _marshal_ret *ret) {{
\treturn marshal_stat(ctx, val, ret);
}}
-"""
+{cutil.ifdef_pop(0)}"""
############################################################################
return ret
diff --git a/lib9p/protogen/c9util.py b/lib9p/core_gen/c9util.py
index cf91951..7e1e41d 100644
--- a/lib9p/protogen/c9util.py
+++ b/lib9p/core_gen/c9util.py
@@ -1,4 +1,4 @@
-# lib9p/protogen/c9util.py - Utilities for generating lib9p-specific C
+# lib9p/core_gen/c9util.py - Utilities for generating lib9p-specific C
#
# Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
# SPDX-License-Identifier: AGPL-3.0-or-later
@@ -91,7 +91,9 @@ def typename(typ: idl.Type, parent: idl.StructMember | None = None) -> str:
match typ:
case idl.Primitive():
if typ.value == 1 and parent and parent.cnt: # SPECIAL (string)
- return "[[gnu::nonstring]] char"
+ if parent.membname == "utf8":
+ return "[[gnu::nonstring]] const char"
+ return "const void"
return f"uint{typ.value*8}_t"
case idl.Number():
return f"{basename(typ)}_t"
diff --git a/lib9p/protogen/c_format.py b/lib9p/core_gen/c_fmt_print.py
index a1bcbf3..7a0a9d3 100644
--- a/lib9p/protogen/c_format.py
+++ b/lib9p/core_gen/c_fmt_print.py
@@ -1,4 +1,4 @@
-# lib9p/protogen/c_format.py - Generate C pretty-print functions
+# lib9p/core_gen/c_fmt_print.py - Generate C pretty-print functions
#
# Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
# SPDX-License-Identifier: AGPL-3.0-or-later
@@ -6,14 +6,14 @@
import idl
-from . import c9util, cutil
+from . import c9util, cutil, idlutil
# This strives to be "general-purpose" in that it just acts on the
# *.9p inputs; but (unfortunately?) there are a few special-cases in
# this script, marked with "SPECIAL".
# pylint: disable=unused-variable
-__all__ = ["gen_c_format"]
+__all__ = ["gen_c_fmt_print"]
def bf_numname(typ: idl.Bitfield, num: idl.BitNum, base: str) -> str:
@@ -21,33 +21,34 @@ def bf_numname(typ: idl.Bitfield, num: idl.BitNum, base: str) -> str:
return c9util.Ident(c9util.add_prefix(prefix, base))
-def gen_c_format(versions: set[str], typs: list[idl.UserType]) -> str:
+def gen_c_fmt_print(versions: set[str], typs: list[idl.UserType]) -> str:
ret = """
-/* *_format *******************************************************************/
+/* fmt_print_* ****************************************************************/
"""
- for typ in typs:
+ for typ in idlutil.topo_sorted(typs):
ret += "\n"
ret += cutil.ifdef_push(1, c9util.ver_ifdef(typ.in_versions))
- ret += f"static void {c9util.basename(typ)}_format({c9util.typename(typ)} *self, struct fmt_state *state) {{\n"
+ storage = "" if typ.typname == "stat" else "static " # SPECIAL (stat is public)
+ ret += f"[[maybe_unused]] {storage}void fmt_print_{typ.typname}(lo_interface fmt_dest w, [[maybe_unused]] struct lib9p_ctx *ctx, {c9util.typename(typ)} *self) {{\n"
match typ:
case idl.Number():
if typ.vals:
ret += "\tswitch (*self) {\n"
for name in typ.vals:
ret += f"\tcase {c9util.Ident(c9util.add_prefix(f'{typ.typname}_'.upper(), name))}:\n"
- ret += f'\t\tfmt_state_puts(state, "{name}");\n'
+ ret += f'\t\tfmt_print_str(w, "{name}");\n'
ret += "\t\tbreak;\n"
ret += "\tdefault:\n"
- ret += f'\t\tfmt_state_printf(state, "%"PRIu{typ.static_size*8}, *self);\n'
+ ret += "\t\tfmt_print_base10(w, *self);\n"
ret += "\t}\n"
else:
- ret += f'\t\tfmt_state_printf(state, "%"PRIu{typ.static_size*8}, *self);\n'
+ ret += "\t\tfmt_print_base10(w, *self);\n"
case idl.Bitfield():
val = "*self"
if typ.typname == "dm": # SPECIAL (pretty file permissions)
val = f"(*self & ~(({c9util.typename(typ)})0777))"
ret += "\tbool empty = true;\n"
- ret += "\tfmt_state_putchar(state, '(');\n"
+ ret += "\tfmt_print_byte(w, '(');\n"
nums: set[str] = set()
for bit in reversed(typ.bits):
@@ -59,8 +60,8 @@ def gen_c_format(versions: set[str], typs: list[idl.UserType]) -> str:
bitname = bit.bitname
ret += f"\tif ({val} & (UINT{typ.static_size*8}_C(1)<<{bit.num})) {{\n"
ret += "\t\tif (!empty)\n"
- ret += "\t\t\tfmt_state_putchar(state, '|');\n"
- ret += f'\t\tfmt_state_puts(state, "{bitname}");\n'
+ ret += "\t\t\tfmt_print_byte(w, '|');\n"
+ ret += f'\t\tfmt_print_str(w, "{bitname}");\n'
ret += "\t\tempty = false;\n"
ret += "\t}\n"
case idl.BitNum():
@@ -73,69 +74,72 @@ def gen_c_format(versions: set[str], typs: list[idl.UserType]) -> str:
f"{bit.cat.numname}_".upper(), name
)
ret += "\t\tif (!empty)\n"
- ret += "\t\t\tfmt_state_putchar(state, '|');\n"
- ret += f'\t\tfmt_state_puts(state, "{bitname}");\n'
+ ret += "\t\t\tfmt_print_byte(w, '|');\n"
+ ret += f'\t\tfmt_print_str(w, "{bitname}");\n'
ret += "\t\tempty = false;\n"
ret += "\t\tbreak;\n"
ret += "\tdefault:\n"
ret += "\t\tif (!empty)\n"
- ret += "\t\t\tfmt_state_putchar(state, '|');\n"
- ret += f'\t\tfmt_state_printf(state, "%"PRIu{typ.static_size*8}, {val} & {bf_numname(typ, bit.cat, 'MASK')});\n'
+ ret += "\t\t\tfmt_print_byte(w, '|');\n"
+ ret += f"\t\tfmt_print_base10(w, {val} & {bf_numname(typ, bit.cat, 'MASK')});\n"
ret += "\t\tempty = false;\n"
ret += "\t}\n"
nums.add(bit.cat.numname)
if typ.typname == "dm": # SPECIAL (pretty file permissions)
ret += "\tif (!empty)\n"
- ret += "\t\tfmt_state_putchar(state, '|');\n"
- ret += f'\tfmt_state_printf(state, "%#04"PRIo{typ.static_size*8}, *self & 0777);\n'
+ ret += "\t\tfmt_print_byte(w, '|');\n"
+ ret += "\tfmt_print(w, (rjust, 4, '0', (base8, *self & 0777)));\n"
else:
ret += "\tif (empty)\n"
- ret += "\t\tfmt_state_putchar(state, '0');\n"
- ret += "\tfmt_state_putchar(state, ')');\n"
- case idl.Struct(typname="s"): # SPECIAL(string)
- ret += "\t/* https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47781 */\n"
- ret += "#pragma GCC diagnostic push\n"
- ret += '#pragma GCC diagnostic ignored "-Wformat"\n'
- ret += '#pragma GCC diagnostic ignored "-Wformat-extra-args"\n'
- ret += '\tfmt_state_printf(state, "%.*q", self->len, self->utf8);\n'
- ret += "#pragma GCC diagnostic pop\n"
+ ret += "\t\tfmt_print_byte(w, '0');\n"
+ ret += "\tfmt_print_byte(w, ')');\n"
+ case idl.Struct(typname="s"): # SPECIAL (string)
+ ret += "\tfmt_print_qmem(w, self->utf8, self->len);\n"
case idl.Struct(): # and idl.Message():
if isinstance(typ, idl.Message):
- ret += f'\tfmt_state_puts(state, "{typ.typname} {{");\n'
+ ret += f'\tfmt_print_str(w, "{typ.typname} {{");\n'
else:
- ret += "\tfmt_state_putchar(state, '{');\n"
+ ret += "\tfmt_print_byte(w, '{');\n"
for member in typ.members:
if member.val:
continue
ret += cutil.ifdef_push(2, c9util.ver_ifdef(member.in_versions))
if member.cnt:
- if member.typ.static_size == 1: # SPECIAL (data)
- ret += f'\tfmt_state_puts(state, " {member.membname}=<bytedata>");\n'
- continue
if isinstance(member.cnt, int):
cnt_str = str(member.cnt)
cnt_typ = "size_t"
else:
cnt_str = f"self->{member.cnt.membname}"
cnt_typ = c9util.typename(member.cnt.typ)
- ret += f'\tfmt_state_puts(state, " {member.membname}=[");\n'
+ if member.typ.static_size == 1: # SPECIAL (data)
+ ret += f"\tif (utf8_is_valid_without_nul((uint8_t *)self->{member.membname}, (size_t){cnt_str})) {{\n"
+ ret += f'\t\tfmt_print_str(w, " {member.membname}=");\n'
+ ret += f"\t\tfmt_print_qmem(w, self->{member.membname}, {cnt_str} < 50 ? {cnt_str} : 50);\n"
+ ret += f"\t\tif ({cnt_str} > 50)\n"
+ ret += '\t\t\tfmt_print_str(w, "...");\n'
+ ret += "\t} else {\n"
+ ret += f'\t\tfmt_print_str(w, " {member.membname}=<bytedata>");\n'
+ ret += "\t}\n"
+ continue
+ ret += f'\tfmt_print_str(w, " {member.membname}=[");\n'
ret += f"\tfor ({cnt_typ} i = 0; i < {cnt_str}; i++) {{\n"
ret += "\t\tif (i)\n"
- ret += '\t\t\tfmt_state_puts(state, ", ");\n'
+ ret += "\t\t\tfmt_print_byte(w, ',');\n"
+ ret += "\t\tfmt_print_byte(w, ' ');\n"
if isinstance(member.typ, idl.Primitive):
- ret += f'\t\tfmt_state_printf(state, "%"PRIu{member.typ.static_size*8}, self->{member.membname}[i]);\n'
+ ret += f"\t\tfmt_print_base10(w, self->{member.membname}[i]);\n"
else:
- ret += f"\t\t{c9util.basename(member.typ)}_format(&self->{member.membname}[i], state);\n"
+ ret += f"\t\tfmt_print_{member.typ.typname}(w, ctx, &self->{member.membname}[i]);\n"
ret += "\t}\n"
- ret += '\tfmt_state_puts(state, " ]");\n'
+ ret += '\tfmt_print_str(w, " ]");\n'
else:
- ret += f'\tfmt_state_puts(state, " {member.membname}=");\n'
+ ret += f'\tfmt_print_str(w, " {member.membname}=");\n'
if isinstance(member.typ, idl.Primitive):
- ret += f'\tfmt_state_printf(state, "%"PRIu{member.typ.static_size*8}, self->{member.membname});\n'
+ ret += f"\tfmt_print_base10(w, self->{member.membname});\n"
else:
- ret += f"\t{c9util.basename(member.typ)}_format(&self->{member.membname}, state);\n"
+ ret += f"\tfmt_print_{member.typ.typname}(w, ctx, &self->{member.membname});\n"
ret += cutil.ifdef_pop(1)
- ret += '\tfmt_state_puts(state, " }");\n'
+ ret += '\tfmt_print_str(w, " }");\n'
ret += "}\n"
ret += cutil.ifdef_pop(0)
diff --git a/lib9p/protogen/c_marshal.py b/lib9p/core_gen/c_marshal.py
index 4dab864..2747ce8 100644
--- a/lib9p/protogen/c_marshal.py
+++ b/lib9p/core_gen/c_marshal.py
@@ -1,4 +1,4 @@
-# lib9p/protogen/c_marshal.py - Generate C marshal functions
+# lib9p/core_gen/c_marshal.py - Generate C marshal functions
#
# Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
# SPDX-License-Identifier: AGPL-3.0-or-later
@@ -193,46 +193,46 @@ def gen_c_marshal(versions: set[str], typs: list[idl.UserType]) -> str:
"#define MARSHAL_BYTES_ZEROCOPY(ctx, data, len)\n"
"\tif (ret->net_iov[ret->net_iov_cnt-1].iov_len)\n"
"\t\tret->net_iov_cnt++;\n"
- "\tret->net_iov[ret->net_iov_cnt-1].iov_base = data;\n"
+ "\tret->net_iov[ret->net_iov_cnt-1].iov_write_from = data;\n"
"\tret->net_iov[ret->net_iov_cnt-1].iov_len = len;\n"
"\tret->net_iov_cnt++;\n"
)
ret += cutil.macro(
"#define MARSHAL_BYTES(ctx, data, len)\n"
- "\tif (!ret->net_iov[ret->net_iov_cnt-1].iov_base)\n"
- "\t\tret->net_iov[ret->net_iov_cnt-1].iov_base = &ret->net_copied[ret->net_copied_size];\n"
+ "\tif (!ret->net_iov[ret->net_iov_cnt-1].iov_write_from)\n"
+ "\t\tret->net_iov[ret->net_iov_cnt-1].iov_write_from = &ret->net_copied[ret->net_copied_size];\n"
"\tmemcpy(&ret->net_copied[ret->net_copied_size], data, len);\n"
"\tret->net_copied_size += len;\n"
"\tret->net_iov[ret->net_iov_cnt-1].iov_len += len;\n"
)
ret += cutil.macro(
"#define MARSHAL_U8LE(ctx, val)\n"
- "\tif (!ret->net_iov[ret->net_iov_cnt-1].iov_base)\n"
- "\t\tret->net_iov[ret->net_iov_cnt-1].iov_base = &ret->net_copied[ret->net_copied_size];\n"
+ "\tif (!ret->net_iov[ret->net_iov_cnt-1].iov_write_from)\n"
+ "\t\tret->net_iov[ret->net_iov_cnt-1].iov_write_from = &ret->net_copied[ret->net_copied_size];\n"
"\tret->net_copied[ret->net_copied_size] = val;\n"
"\tret->net_copied_size += 1;\n"
"\tret->net_iov[ret->net_iov_cnt-1].iov_len += 1;\n"
)
ret += cutil.macro(
"#define MARSHAL_U16LE(ctx, val)\n"
- "\tif (!ret->net_iov[ret->net_iov_cnt-1].iov_base)\n"
- "\t\tret->net_iov[ret->net_iov_cnt-1].iov_base = &ret->net_copied[ret->net_copied_size];\n"
+ "\tif (!ret->net_iov[ret->net_iov_cnt-1].iov_write_from)\n"
+ "\t\tret->net_iov[ret->net_iov_cnt-1].iov_write_from = &ret->net_copied[ret->net_copied_size];\n"
"\tuint16le_encode(&ret->net_copied[ret->net_copied_size], val);\n"
"\tret->net_copied_size += 2;\n"
"\tret->net_iov[ret->net_iov_cnt-1].iov_len += 2;\n"
)
ret += cutil.macro(
"#define MARSHAL_U32LE(ctx, val)\n"
- "\tif (!ret->net_iov[ret->net_iov_cnt-1].iov_base)\n"
- "\t\tret->net_iov[ret->net_iov_cnt-1].iov_base = &ret->net_copied[ret->net_copied_size];\n"
+ "\tif (!ret->net_iov[ret->net_iov_cnt-1].iov_write_from)\n"
+ "\t\tret->net_iov[ret->net_iov_cnt-1].iov_write_from = &ret->net_copied[ret->net_copied_size];\n"
"\tuint32le_encode(&ret->net_copied[ret->net_copied_size], val);\n"
"\tret->net_copied_size += 4;\n"
"\tret->net_iov[ret->net_iov_cnt-1].iov_len += 4;\n"
)
ret += cutil.macro(
"#define MARSHAL_U64LE(ctx, val)\n"
- "\tif (!ret->net_iov[ret->net_iov_cnt-1].iov_base)\n"
- "\t\tret->net_iov[ret->net_iov_cnt-1].iov_base = &ret->net_copied[ret->net_copied_size];\n"
+ "\tif (!ret->net_iov[ret->net_iov_cnt-1].iov_write_from)\n"
+ "\t\tret->net_iov[ret->net_iov_cnt-1].iov_write_from = &ret->net_copied[ret->net_copied_size];\n"
"\tuint64le_encode(&ret->net_copied[ret->net_copied_size], val);\n"
"\tret->net_copied_size += 8;\n"
"\tret->net_iov[ret->net_iov_cnt-1].iov_len += 8;\n"
@@ -365,27 +365,30 @@ def gen_c_marshal(versions: set[str], typs: list[idl.UserType]) -> str:
assert isinstance(typ, idl.Struct)
ret += "\n"
ret += cutil.ifdef_push(1, c9util.ver_ifdef(typ.in_versions))
- ret += f"static bool marshal_{typ.typname}(struct lib9p_ctx *ctx, {c9util.typename(typ)} *val, struct _marshal_ret *ret) {{\n"
+ if not isinstance(typ, idl.Message): # SPECIAL (bool for stat)
+ ret += f"static bool marshal_{typ.typname}(struct lib9p_ctx *ctx, {c9util.typename(typ)} *val, struct _marshal_ret *ret) {{\n"
+ else:
+ ret += f"static error marshal_{typ.typname}(struct lib9p_ctx *ctx, {c9util.typename(typ)} *val, struct _marshal_ret *ret) {{\n"
# Pass 1 - check size
max_size = max(typ.max_size(v) for v in typ.in_versions)
+ szbits = 32
if max_size > cutil.UINT32_MAX: # SPECIAL (9P2000.e)
- ret += get_offset_expr(typ, go_to_end).gen_c(
- "uint64_t", "needed_size", "val->", 1, 0
- )
+ szbits = 64
+ ret += get_offset_expr(typ, go_to_end).gen_c(
+ f"uint{szbits}_t", "needed_size", "val->", 1, 0
+ )
+ if szbits > 32: # SPECIAL (9P2000.e)
ret += "\tif (needed_size > (uint64_t)(ctx->max_msg_size)) {\n"
else:
- ret += get_offset_expr(typ, go_to_end).gen_c(
- "uint32_t", "needed_size", "val->", 1, 0
- )
ret += "\tif (needed_size > ctx->max_msg_size) {\n"
- if isinstance(typ, idl.Message): # SPECIAL (disable for stat)
- ret += '\t\tlib9p_errorf(ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")",\n'
- ret += f'\t\t\t"{typ.typname}",\n'
- ret += f'\t\t\tctx->version ? "negotiated" : "{'client' if typ.msgid % 2 == 0 else 'server'}",\n'
- ret += "\t\t\tctx->max_msg_size);\n"
- ret += "\t\treturn true;\n"
+ if not isinstance(typ, idl.Message): # SPECIAL (bool for stat)
+ ret += "\t\treturn true;\n"
+ else:
+ ret += f'\t\treturn error_new(E_POSIX_ERANGE, "{typ.typname} message too large to marshal into ",\n'
+ ret += f'\t\t\tctx->version ? "negotiated" : "{'client' if typ.msgid % 2 == 0 else 'server'}", " limit",\n'
+ ret += '\t\t\t" (", needed_size, " > ", ctx->max_msg_size, ")");\n'
ret += "\t}\n"
# Pass 2 - write data
@@ -397,7 +400,10 @@ def gen_c_marshal(versions: set[str], typs: list[idl.UserType]) -> str:
ret += cutil.ifdef_pop(ifdef_lvl())
# Return
- ret += "\treturn false;\n"
+ if not isinstance(typ, idl.Message): # SPECIAL (bool for stat)
+ ret += "\treturn false;\n"
+ else:
+ ret += "\treturn ERROR_NULL;\n"
ret += "}\n"
ret += cutil.ifdef_pop(0)
return ret
diff --git a/lib9p/protogen/c_unmarshal.py b/lib9p/core_gen/c_unmarshal.py
index 34635f9..206a85b 100644
--- a/lib9p/protogen/c_unmarshal.py
+++ b/lib9p/core_gen/c_unmarshal.py
@@ -1,4 +1,4 @@
-# lib9p/protogen/c_unmarshal.py - Generate C unmarshal functions
+# lib9p/core_gen/c_unmarshal.py - Generate C unmarshal functions
#
# Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
# SPDX-License-Identifier: AGPL-3.0-or-later
@@ -123,9 +123,9 @@ def gen_c_unmarshal(versions: set[str], typs: list[idl.UserType]) -> str:
assert isinstance(typ, idl.Struct)
ret += "\n"
ret += cutil.ifdef_push(1, c9util.ver_ifdef(typ.in_versions))
- ret += f"static void unmarshal_{typ.typname}([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {{\n"
+ ret += f"static void unmarshal_{typ.typname}([[maybe_unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {{\n"
ret += f"\t{c9util.typename(typ)} *out = out_buf;\n"
- ret += "\t[[gnu::unused]] void *extra = &out[1];\n"
+ ret += "\t[[maybe_unused]] void *extra = &out[1];\n"
ret += "\tuint32_t net_offset = 0;\n"
indent_stack = [IndentLevel(ifdef=True)]
diff --git a/lib9p/protogen/c_validate.py b/lib9p/core_gen/c_validate.py
index 535a750..1bfe329 100644
--- a/lib9p/protogen/c_validate.py
+++ b/lib9p/core_gen/c_validate.py
@@ -1,4 +1,4 @@
-# lib9p/protogen/c_validate.py - Generate C validation functions
+# lib9p/core_gen/c_validate.py - Generate C validation functions
#
# Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
# SPDX-License-Identifier: AGPL-3.0-or-later
@@ -57,26 +57,26 @@ def gen_c_validate(versions: set[str], typs: list[idl.UserType]) -> str:
"\t\t/* If needed-net-size overflowed uint32_t, then\n"
"\t\t * there's no way that actual-net-size will live up to\n"
"\t\t * that. */\n"
- '\t\treturn lib9p_error(ctx, LINUX_EBADMSG, "message is too short for content");\n'
+ '\t\treturn ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "message is too short for content"));\n'
"\tif (net_offset > net_size)\n"
- '\t\treturn lib9p_errorf(ctx, LINUX_EBADMSG, "message is too short for content (%"PRIu32" > %"PRIu32") @ %d", net_offset, net_size, __LINE__);\n'
+ '\t\treturn ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "message is too short for content (", net_offset, " > ", net_size, ")"));\n'
)
ret += cutil.macro(
"#define VALIDATE_NET_UTF8(n)\n"
"\t{\n"
"\t\tsize_t len = n;\n"
"\t\tVALIDATE_NET_BYTES(len);\n"
- "\t\tif (!is_valid_utf8_without_nul(&net_bytes[net_offset-len], len))\n"
- '\t\t\treturn lib9p_error(ctx, LINUX_EBADMSG, "message contains invalid UTF-8");\n'
+ "\t\tif (!utf8_is_valid_without_nul(&net_bytes[net_offset-len], len))\n"
+ '\t\t\treturn ERROR_NEW_ERR(size_t, error_new(E_POSIX_EILSEQ, "message contains invalid UTF-8"));\n'
"\t}\n"
)
ret += cutil.macro(
"#define RESERVE_HOST_BYTES(n)\n"
"\tif (__builtin_add_overflow(host_size, n, &host_size))\n"
- "\t\t/* If needed-host-size overflowed ssize_t, then there's\n"
+ "\t\t/* If needed-host-size overflowed size_t, then there's\n"
"\t\t * no way that actual-net-size will live up to\n"
"\t\t * that. */\n"
- '\t\treturn lib9p_error(ctx, LINUX_EBADMSG, "message is too short for content");\n'
+ '\t\treturn ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "message is too short for content"));\n'
)
ret += "#define GET_U8LE(off) (net_bytes[off])\n"
@@ -190,11 +190,10 @@ def gen_c_validate(versions: set[str], typs: list[idl.UserType]) -> str:
for tok in child.val.tokens
):
nbits = 32
- act = f"(uint{nbits}_t)GET_U{nbits}LE({lookup_sym(f'&{child.membname}')})"
- exp = f"(uint{nbits}_t)({c9util.idl_expr(child.val, lookup_sym)})"
+ act = f"GET_U{nbits}LE({lookup_sym(f'&{child.membname}')})"
+ exp = f"{c9util.idl_expr(child.val, lookup_sym)}"
ret += f"{'\t'*indent_lvl()}if ({act} != {exp})\n"
- ret += f'{"\t"*(indent_lvl()+1)}return lib9p_errorf(ctx, LINUX_EBADMSG, "{path} value is wrong: actual: %"PRIu{nbits}" != correct:%"PRIu{nbits},\n'
- ret += f"{'\t'*(indent_lvl()+2)}{act}, {exp});\n"
+ ret += f'{"\t"*(indent_lvl()+1)}return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "{path} value is wrong: actual:", (base10, {act}), " != correct:", (base10, {exp})));\n'
if child.max:
incr_flush()
assert child.typ.static_size
@@ -205,19 +204,18 @@ def gen_c_validate(versions: set[str], typs: list[idl.UserType]) -> str:
for tok in child.max.tokens
):
nbits = 32
- act = f"(uint{nbits}_t)GET_U{nbits}LE({lookup_sym(f'&{child.membname}')})"
- exp = f"(uint{nbits}_t)({c9util.idl_expr(child.max, lookup_sym)})"
+ act = f"GET_U{nbits}LE({lookup_sym(f'&{child.membname}')})"
+ exp = f"{c9util.idl_expr(child.max, lookup_sym)}"
ret += f"{'\t'*indent_lvl()}if ({act} > {exp})\n"
- ret += f'{"\t"*(indent_lvl()+1)}return lib9p_errorf(ctx, LINUX_EBADMSG, "{path} value is too large: %"PRIu{nbits}" > %"PRIu{nbits},\n'
- ret += f"{'\t'*(indent_lvl()+2)}{act}, {exp});\n"
+ ret += f'{"\t"*(indent_lvl()+1)}return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "{path} value is too large: ", (base10, {act}), " > ", (base10, {exp})));\n'
if isinstance(child.typ, idl.Bitfield):
incr_flush()
nbytes = child.typ.static_size
nbits = nbytes * 8
act = f"GET_U{nbits}LE({lookup_sym(f'&{child.membname}')})"
ret += f"{'\t'*indent_lvl()}if ({act} & ~{child.typ.typname}_masks[ctx->version])\n"
- ret += f'{"\t"*(indent_lvl()+1)}return lib9p_errorf(ctx, LINUX_EBADMSG, "unknown bits in {child.typ.typname} bitfield: %#0{nbytes*2}"PRIx{nbits},\n'
- ret += f"{'\t'*(indent_lvl()+2)}{act} & ~{child.typ.typname}_masks[ctx->version]);\n"
+ ret += f'{"\t"*(indent_lvl()+1)}return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "unknown bits in {child.typ.typname} bitfield: ",\n'
+ ret += f"{'\t'*(indent_lvl()+2)}(base16_u{nbits}_, {act} & ~{child.typ.typname}_masks[ctx->version])));\n"
def handle(
path: idlutil.Path,
@@ -273,12 +271,12 @@ def gen_c_validate(versions: set[str], typs: list[idl.UserType]) -> str:
ret += "\n"
ret += cutil.ifdef_push(1, c9util.ver_ifdef(typ.in_versions))
if typ.typname == "stat": # SPECIAL (stat)
- ret += f"static ssize_t validate_{typ.typname}(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes, uint32_t *ret_net_size) {{\n"
+ ret += f"static size_t_or_error validate_{typ.typname}(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes, uint32_t *ret_net_size) {{\n"
else:
- ret += f"static ssize_t validate_{typ.typname}(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {{\n"
+ ret += f"static size_t_or_error validate_{typ.typname}([[maybe_unused]] struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {{\n"
ret += "\tuint32_t net_offset = 0;\n"
- ret += f"\tssize_t host_size = sizeof({c9util.typename(typ)});\n"
+ ret += f"\tsize_t host_size = sizeof({c9util.typename(typ)});\n"
incr_buf = 0
indent_stack = [IndentLevel(ifdef=True)]
@@ -293,7 +291,7 @@ def gen_c_validate(versions: set[str], typs: list[idl.UserType]) -> str:
if typ.typname == "stat": # SPECIAL (stat)
ret += "\tif (ret_net_size)\n"
ret += "\t\t*ret_net_size = net_offset;\n"
- ret += "\treturn (ssize_t)host_size;\n"
+ ret += "\treturn ERROR_NEW_VAL(size_t, host_size);\n"
ret += "}\n"
ret += cutil.ifdef_pop(0)
return ret
diff --git a/lib9p/protogen/cutil.py b/lib9p/core_gen/cutil.py
index 8df6db9..9183cc4 100644
--- a/lib9p/protogen/cutil.py
+++ b/lib9p/core_gen/cutil.py
@@ -1,4 +1,4 @@
-# lib9p/protogen/cutil.py - Utilities for generating C code
+# lib9p/core_gen/cutil.py - Utilities for generating C code
#
# Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
# SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib9p/protogen/h.py b/lib9p/core_gen/h.py
index 3b33419..eba1492 100644
--- a/lib9p/protogen/h.py
+++ b/lib9p/core_gen/h.py
@@ -1,4 +1,4 @@
-# lib9p/protogen/h.py - Generate 9p.generated.h
+# lib9p/core_gen/h.py - Generate _core_generated.h
#
# Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
# SPDX-License-Identifier: AGPL-3.0-or-later
@@ -159,24 +159,16 @@ def gen_h(versions: set[str], typs: list[idl.UserType]) -> str:
ret = f"""/* Generated by `{' '.join(sys.argv)}`. DO NOT EDIT! */
-#ifndef _LIB9P_9P_H_
-\t#error Do not include <lib9p/9p.generated.h> directly; include <lib9p/9p.h> instead
+#ifndef _LIB9P_CORE_H_
+\t#error Do not include <lib9p/_core_generated.h> directly; include <lib9p/core.h> instead
#endif
-
-#include <stdint.h> /* for uint{{n}}_t types */
-
-#include <libfmt/fmt.h> /* for fmt_formatter */
-#include <libhw/generic/net.h> /* for struct iovec */
"""
-
id2typ: dict[int, idl.Message] = {}
for msg in [msg for msg in typs if isinstance(msg, idl.Message)]:
id2typ[msg.msgid] = msg
ret += """
/* config *********************************************************************/
-
-#include "config.h"
"""
for ver in sorted(versions):
ret += "\n"
@@ -192,14 +184,21 @@ def gen_h(versions: set[str], typs: list[idl.UserType]) -> str:
ret += "\t#endif\n"
ret += "#endif\n"
+ # SPECIAL (convenience)
+ ret += "\n"
+ ret += f"#define _LIB9P_ENABLE_stat {c9util.ver_ifdef(next(typ for typ in typs if typ.typname == 'stat').in_versions)}\n"
+
ret += f"""
/* enum version ***************************************************************/
enum {c9util.ident('version')} {{
"""
- fullversions = ["unknown = 0", *sorted(versions)]
- verwidth = max(len(v) for v in fullversions)
- for ver in fullversions:
+ xversions = [ # SPECIAL (initialization)
+ "uninitialized = 0",
+ *sorted(v for v in versions if v != "uninitialized"),
+ ]
+ verwidth = max(len(v) for v in xversions)
+ for ver in xversions:
if ver in versions:
ret += cutil.ifdef_push(1, c9util.ver_ifdef({ver}))
ret += f"\t{c9util.ver_enum(ver)},"
@@ -207,7 +206,6 @@ enum {c9util.ident('version')} {{
ret += cutil.ifdef_pop(0)
ret += f"\t{c9util.ver_enum('NUM')},\n"
ret += "};\n"
- ret += f"LO_IMPLEMENTATION_H(fmt_formatter, enum {c9util.ident('version')}, {c9util.ident('version')});\n"
ret += """
/* enum msg_type **************************************************************/
@@ -223,7 +221,6 @@ enum {c9util.ident('version')} {{
ret += f"\t{c9util.Ident(f'TYP_{msg.typname:<{namewidth}}')} = {msg.msgid},\n"
ret += cutil.ifdef_pop(0)
ret += "};\n"
- ret += f"LO_IMPLEMENTATION_H(fmt_formatter, enum {c9util.ident('msg_type')}, {c9util.ident('msg_type')});\n"
ret += """
/* payload types **************************************************************/
@@ -348,14 +345,14 @@ enum {c9util.ident('version')} {{
ret += "\n"
ret += f"struct {c9util.ident('Tmsg_send_buf')} {{\n"
ret += "\tsize_t iov_cnt;\n"
- ret += f"\tstruct iovec iov[{c9util.IDENT('TMSG_MAX_IOV')}];\n"
+ ret += f"\tstruct wr_iovec iov[{c9util.IDENT('TMSG_MAX_IOV')}];\n"
ret += f"\tuint8_t copied[{c9util.IDENT('TMSG_MAX_COPY')}];\n"
ret += "};\n"
ret += "\n"
ret += f"struct {c9util.ident('Rmsg_send_buf')} {{\n"
ret += "\tsize_t iov_cnt;\n"
- ret += f"\tstruct iovec iov[{c9util.IDENT('RMSG_MAX_IOV')}];\n"
+ ret += f"\tstruct wr_iovec iov[{c9util.IDENT('RMSG_MAX_IOV')}];\n"
ret += f"\tuint8_t copied[{c9util.IDENT('RMSG_MAX_COPY')}];\n"
ret += "};\n"
@@ -364,7 +361,6 @@ enum {c9util.ident('version')} {{
def gen_number(typ: idl.Number) -> str:
ret = f"typedef {c9util.typename(typ.prim)} {c9util.typename(typ)};\n"
- ret += f"LO_IMPLEMENTATION_H(fmt_formatter, {c9util.typename(typ)}, {c9util.basename(typ)});\n"
def lookup_sym(sym: str) -> str:
assert False
@@ -383,7 +379,6 @@ def gen_number(typ: idl.Number) -> str:
def gen_bitfield(typ: idl.Bitfield) -> str:
ret = f"typedef {c9util.typename(typ.prim)} {c9util.typename(typ)};\n"
- ret += f"LO_IMPLEMENTATION_H(fmt_formatter, {c9util.typename(typ)}, {c9util.basename(typ)});\n"
def lookup_sym(sym: str) -> str:
assert False
@@ -531,5 +526,4 @@ def gen_struct(typ: idl.Struct) -> str: # and idl.Message
ret += f"\t{c9util.typename(member.typ, member):<{typewidth}} {'*' if member.cnt else ' '}{member.membname};\n"
ret += cutil.ifdef_pop(1)
ret += "};\n"
- ret += f"LO_IMPLEMENTATION_H(fmt_formatter, {c9util.typename(typ)}, {c9util.basename(typ)});\n"
return ret
diff --git a/lib9p/protogen/idlutil.py b/lib9p/core_gen/idlutil.py
index dc4d012..e92839a 100644
--- a/lib9p/protogen/idlutil.py
+++ b/lib9p/core_gen/idlutil.py
@@ -1,10 +1,9 @@
-# lib9p/protogen/idlutil.py - Utilities for working with the 9P idl package
+# lib9p/core_gen/idlutil.py - Utilities for working with the 9P idl package
#
# Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
# SPDX-License-Identifier: AGPL-3.0-or-later
import enum
-import graphlib
import typing
import idl
@@ -22,21 +21,40 @@ __all__ = [
def topo_sorted(typs: list[idl.UserType]) -> typing.Iterable[idl.UserType]:
- ts: graphlib.TopologicalSorter[idl.UserType] = graphlib.TopologicalSorter()
+ ret: list[idl.UserType] = []
+ struct_ord: dict[str, int] = {}
+
+ def get_struct_ord(typ: idl.Struct) -> int:
+ nonlocal struct_ord
+ if typ.typname not in struct_ord:
+ deps = [
+ get_struct_ord(member.typ)
+ for member in typ.members
+ if isinstance(member.typ, idl.Struct)
+ ]
+ if len(deps) == 0:
+ struct_ord[typ.typname] = 0
+ else:
+ struct_ord[typ.typname] = 1 + max(deps)
+ return struct_ord[typ.typname]
+
for typ in typs:
match typ:
case idl.Number():
- ts.add(typ)
+ ret.append(typ)
case idl.Bitfield():
- ts.add(typ)
+ ret.append(typ)
case idl.Struct(): # and idl.Message():
- deps = [
- member.typ
- for member in typ.members
- if not isinstance(member.typ, idl.Primitive)
- ]
- ts.add(typ, *deps)
- return ts.static_order()
+ _ = get_struct_ord(typ)
+ for _ord in sorted(set(struct_ord.values())):
+ for typ in typs:
+ if not isinstance(typ, idl.Struct):
+ continue
+ if struct_ord[typ.typname] != _ord:
+ continue
+ ret.append(typ)
+ assert len(ret) == len(typs)
+ return ret
# walk() #######################################################################
diff --git a/lib9p/core_generated.c b/lib9p/core_generated.c
new file mode 100644
index 0000000..5acc551
--- /dev/null
+++ b/lib9p/core_generated.c
@@ -0,0 +1,8115 @@
+/* Generated by `lib9p/core.gen lib9p/idl/0000-uninitialized.9p lib9p/idl/2002-9P2000.9p lib9p/idl/2003-9P2000.p9p.9p lib9p/idl/2005-9P2000.u.9p lib9p/idl/2010-9P2000.L.9p lib9p/idl/2012-9P2000.e.9p`. DO NOT EDIT! */
+
+#include <stddef.h> /* for size_t */
+#include <string.h> /* for memset() */
+
+#include <libmisc/assert.h>
+#include <libmisc/endian.h>
+#include <libmisc/utf8.h>
+
+#include <lib9p/core.h>
+
+#include "core_tables.h"
+
+/* utilities ******************************************************************/
+#if CONFIG_9P_ENABLE_9P2000
+ #define _is_ver_9P2000(v) (v == LIB9P_VER_9P2000)
+#else
+ #define _is_ver_9P2000(v) false
+#endif
+#if CONFIG_9P_ENABLE_9P2000_L
+ #define _is_ver_9P2000_L(v) (v == LIB9P_VER_9P2000_L)
+#else
+ #define _is_ver_9P2000_L(v) false
+#endif
+#if CONFIG_9P_ENABLE_9P2000_e
+ #define _is_ver_9P2000_e(v) (v == LIB9P_VER_9P2000_e)
+#else
+ #define _is_ver_9P2000_e(v) false
+#endif
+#if CONFIG_9P_ENABLE_9P2000_p9p
+ #define _is_ver_9P2000_p9p(v) (v == LIB9P_VER_9P2000_p9p)
+#else
+ #define _is_ver_9P2000_p9p(v) false
+#endif
+#if CONFIG_9P_ENABLE_9P2000_u
+ #define _is_ver_9P2000_u(v) (v == LIB9P_VER_9P2000_u)
+#else
+ #define _is_ver_9P2000_u(v) false
+#endif
+#if CONFIG_9P_ENABLE_uninitialized
+ #define _is_ver_uninitialized(v) (v == LIB9P_VER_uninitialized)
+#else
+ #define _is_ver_uninitialized(v) false
+#endif
+
+/**
+ * is_ver(ctx, ver) is essentially `(ctx->version == LIB9P_VER_##ver)`, but
+ * compiles correctly (to `false`) even if `LIB9P_VER_##ver` isn't defined
+ * (because `!CONFIG_9P_ENABLE_##ver`). This is useful when `||`ing
+ * several version checks together.
+ */
+#define is_ver(ctx, ver) _is_ver_##ver((ctx)->version)
+
+/* bitmasks *******************************************************************/
+
+#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u
+static const lib9p_dm_t dm_masks[LIB9P_VER_NUM] = {
+#if CONFIG_9P_ENABLE_9P2000
+ [LIB9P_VER_9P2000] = 0b11111100000000000000000111111111,
+#endif /* CONFIG_9P_ENABLE_9P2000 */
+#if CONFIG_9P_ENABLE_9P2000_L
+ [LIB9P_VER_9P2000_L] = 0b00000000000000000000000000000000,
+#endif /* CONFIG_9P_ENABLE_9P2000_L */
+#if CONFIG_9P_ENABLE_9P2000_e
+ [LIB9P_VER_9P2000_e] = 0b11111100000000000000000111111111,
+#endif /* CONFIG_9P_ENABLE_9P2000_e */
+#if CONFIG_9P_ENABLE_9P2000_p9p
+ [LIB9P_VER_9P2000_p9p] = 0b11111100000000000000000111111111,
+#endif /* CONFIG_9P_ENABLE_9P2000_p9p */
+#if CONFIG_9P_ENABLE_9P2000_u
+ [LIB9P_VER_9P2000_u] = 0b11111100101111000000000111111111,
+#endif /* CONFIG_9P_ENABLE_9P2000_u */
+#if CONFIG_9P_ENABLE_uninitialized
+ [LIB9P_VER_uninitialized] = 0b11111100000000000000000111111111,
+#endif /* CONFIG_9P_ENABLE_uninitialized */
+};
+
+#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */
+#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u
+static const lib9p_qt_t qt_masks[LIB9P_VER_NUM] = {
+#if CONFIG_9P_ENABLE_9P2000
+ [LIB9P_VER_9P2000] = 0b11111100,
+#endif /* CONFIG_9P_ENABLE_9P2000 */
+#if CONFIG_9P_ENABLE_9P2000_L
+ [LIB9P_VER_9P2000_L] = 0b11111100,
+#endif /* CONFIG_9P_ENABLE_9P2000_L */
+#if CONFIG_9P_ENABLE_9P2000_e
+ [LIB9P_VER_9P2000_e] = 0b11111100,
+#endif /* CONFIG_9P_ENABLE_9P2000_e */
+#if CONFIG_9P_ENABLE_9P2000_p9p
+ [LIB9P_VER_9P2000_p9p] = 0b11111100,
+#endif /* CONFIG_9P_ENABLE_9P2000_p9p */
+#if CONFIG_9P_ENABLE_9P2000_u
+ [LIB9P_VER_9P2000_u] = 0b11111110,
+#endif /* CONFIG_9P_ENABLE_9P2000_u */
+#if CONFIG_9P_ENABLE_uninitialized
+ [LIB9P_VER_uninitialized] = 0b11111100,
+#endif /* CONFIG_9P_ENABLE_uninitialized */
+};
+
+#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */
+#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u
+static const lib9p_o_t o_masks[LIB9P_VER_NUM] = {
+#if CONFIG_9P_ENABLE_9P2000
+ [LIB9P_VER_9P2000] = 0b01010011,
+#endif /* CONFIG_9P_ENABLE_9P2000 */
+#if CONFIG_9P_ENABLE_9P2000_L
+ [LIB9P_VER_9P2000_L] = 0b00000000,
+#endif /* CONFIG_9P_ENABLE_9P2000_L */
+#if CONFIG_9P_ENABLE_9P2000_e
+ [LIB9P_VER_9P2000_e] = 0b01010011,
+#endif /* CONFIG_9P_ENABLE_9P2000_e */
+#if CONFIG_9P_ENABLE_9P2000_p9p
+ [LIB9P_VER_9P2000_p9p] = 0b01010011,
+#endif /* CONFIG_9P_ENABLE_9P2000_p9p */
+#if CONFIG_9P_ENABLE_9P2000_u
+ [LIB9P_VER_9P2000_u] = 0b01010011,
+#endif /* CONFIG_9P_ENABLE_9P2000_u */
+#if CONFIG_9P_ENABLE_uninitialized
+ [LIB9P_VER_uninitialized] = 0b00000000,
+#endif /* CONFIG_9P_ENABLE_uninitialized */
+};
+
+#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */
+#if CONFIG_9P_ENABLE_9P2000_L
+static const lib9p_lo_t lo_masks[LIB9P_VER_NUM] = {
+#if CONFIG_9P_ENABLE_9P2000
+ [LIB9P_VER_9P2000] = 0b00000000000000000000000000000000,
+#endif /* CONFIG_9P_ENABLE_9P2000 */
+ [LIB9P_VER_9P2000_L] = 0b00000000000111111111111111000011,
+#if CONFIG_9P_ENABLE_9P2000_e
+ [LIB9P_VER_9P2000_e] = 0b00000000000000000000000000000000,
+#endif /* CONFIG_9P_ENABLE_9P2000_e */
+#if CONFIG_9P_ENABLE_9P2000_p9p
+ [LIB9P_VER_9P2000_p9p] = 0b00000000000000000000000000000000,
+#endif /* CONFIG_9P_ENABLE_9P2000_p9p */
+#if CONFIG_9P_ENABLE_9P2000_u
+ [LIB9P_VER_9P2000_u] = 0b00000000000000000000000000000000,
+#endif /* CONFIG_9P_ENABLE_9P2000_u */
+#if CONFIG_9P_ENABLE_uninitialized
+ [LIB9P_VER_uninitialized] = 0b00000000000000000000000000000000,
+#endif /* CONFIG_9P_ENABLE_uninitialized */
+};
+
+static const lib9p_mode_t mode_masks[LIB9P_VER_NUM] = {
+#if CONFIG_9P_ENABLE_9P2000
+ [LIB9P_VER_9P2000] = 0b00000000000000000000000000000000,
+#endif /* CONFIG_9P_ENABLE_9P2000 */
+ [LIB9P_VER_9P2000_L] = 0b00000000000000001111111111111111,
+#if CONFIG_9P_ENABLE_9P2000_e
+ [LIB9P_VER_9P2000_e] = 0b00000000000000000000000000000000,
+#endif /* CONFIG_9P_ENABLE_9P2000_e */
+#if CONFIG_9P_ENABLE_9P2000_p9p
+ [LIB9P_VER_9P2000_p9p] = 0b00000000000000000000000000000000,
+#endif /* CONFIG_9P_ENABLE_9P2000_p9p */
+#if CONFIG_9P_ENABLE_9P2000_u
+ [LIB9P_VER_9P2000_u] = 0b00000000000000000000000000000000,
+#endif /* CONFIG_9P_ENABLE_9P2000_u */
+#if CONFIG_9P_ENABLE_uninitialized
+ [LIB9P_VER_uninitialized] = 0b00000000000000000000000000000000,
+#endif /* CONFIG_9P_ENABLE_uninitialized */
+};
+
+static const lib9p_getattr_t getattr_masks[LIB9P_VER_NUM] = {
+#if CONFIG_9P_ENABLE_9P2000
+ [LIB9P_VER_9P2000] = 0b0000000000000000000000000000000000000000000000000000000000000000,
+#endif /* CONFIG_9P_ENABLE_9P2000 */
+ [LIB9P_VER_9P2000_L] = 0b0000000000000000000000000000000000000000000000000011111111111111,
+#if CONFIG_9P_ENABLE_9P2000_e
+ [LIB9P_VER_9P2000_e] = 0b0000000000000000000000000000000000000000000000000000000000000000,
+#endif /* CONFIG_9P_ENABLE_9P2000_e */
+#if CONFIG_9P_ENABLE_9P2000_p9p
+ [LIB9P_VER_9P2000_p9p] = 0b0000000000000000000000000000000000000000000000000000000000000000,
+#endif /* CONFIG_9P_ENABLE_9P2000_p9p */
+#if CONFIG_9P_ENABLE_9P2000_u
+ [LIB9P_VER_9P2000_u] = 0b0000000000000000000000000000000000000000000000000000000000000000,
+#endif /* CONFIG_9P_ENABLE_9P2000_u */
+#if CONFIG_9P_ENABLE_uninitialized
+ [LIB9P_VER_uninitialized] = 0b0000000000000000000000000000000000000000000000000000000000000000,
+#endif /* CONFIG_9P_ENABLE_uninitialized */
+};
+
+static const lib9p_setattr_t setattr_masks[LIB9P_VER_NUM] = {
+#if CONFIG_9P_ENABLE_9P2000
+ [LIB9P_VER_9P2000] = 0b00000000000000000000000000000000,
+#endif /* CONFIG_9P_ENABLE_9P2000 */
+ [LIB9P_VER_9P2000_L] = 0b00000000000000000000000111111111,
+#if CONFIG_9P_ENABLE_9P2000_e
+ [LIB9P_VER_9P2000_e] = 0b00000000000000000000000000000000,
+#endif /* CONFIG_9P_ENABLE_9P2000_e */
+#if CONFIG_9P_ENABLE_9P2000_p9p
+ [LIB9P_VER_9P2000_p9p] = 0b00000000000000000000000000000000,
+#endif /* CONFIG_9P_ENABLE_9P2000_p9p */
+#if CONFIG_9P_ENABLE_9P2000_u
+ [LIB9P_VER_9P2000_u] = 0b00000000000000000000000000000000,
+#endif /* CONFIG_9P_ENABLE_9P2000_u */
+#if CONFIG_9P_ENABLE_uninitialized
+ [LIB9P_VER_uninitialized] = 0b00000000000000000000000000000000,
+#endif /* CONFIG_9P_ENABLE_uninitialized */
+};
+
+static const lib9p_lock_flags_t lock_flags_masks[LIB9P_VER_NUM] = {
+#if CONFIG_9P_ENABLE_9P2000
+ [LIB9P_VER_9P2000] = 0b00000000000000000000000000000000,
+#endif /* CONFIG_9P_ENABLE_9P2000 */
+ [LIB9P_VER_9P2000_L] = 0b00000000000000000000000000000011,
+#if CONFIG_9P_ENABLE_9P2000_e
+ [LIB9P_VER_9P2000_e] = 0b00000000000000000000000000000000,
+#endif /* CONFIG_9P_ENABLE_9P2000_e */
+#if CONFIG_9P_ENABLE_9P2000_p9p
+ [LIB9P_VER_9P2000_p9p] = 0b00000000000000000000000000000000,
+#endif /* CONFIG_9P_ENABLE_9P2000_p9p */
+#if CONFIG_9P_ENABLE_9P2000_u
+ [LIB9P_VER_9P2000_u] = 0b00000000000000000000000000000000,
+#endif /* CONFIG_9P_ENABLE_9P2000_u */
+#if CONFIG_9P_ENABLE_uninitialized
+ [LIB9P_VER_uninitialized] = 0b00000000000000000000000000000000,
+#endif /* CONFIG_9P_ENABLE_uninitialized */
+};
+#endif /* CONFIG_9P_ENABLE_9P2000_L */
+
+/* validate_* *****************************************************************/
+
+#define VALIDATE_NET_BYTES(n) \
+ if (__builtin_add_overflow(net_offset, n, &net_offset)) \
+ /* If needed-net-size overflowed uint32_t, then \
+ * there's no way that actual-net-size will live up to \
+ * that. */ \
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "message is too short for content")); \
+ if (net_offset > net_size) \
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "message is too short for content (", net_offset, " > ", net_size, ")"));
+#define VALIDATE_NET_UTF8(n) \
+ { \
+ size_t len = n; \
+ VALIDATE_NET_BYTES(len); \
+ if (!utf8_is_valid_without_nul(&net_bytes[net_offset-len], len)) \
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EILSEQ, "message contains invalid UTF-8")); \
+ }
+#define RESERVE_HOST_BYTES(n) \
+ if (__builtin_add_overflow(host_size, n, &host_size)) \
+ /* If needed-host-size overflowed size_t, then there's \
+ * no way that actual-net-size will live up to \
+ * that. */ \
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "message is too short for content"));
+#define GET_U8LE(off) (net_bytes[off])
+#define GET_U16LE(off) uint16le_decode(&net_bytes[off])
+#define GET_U32LE(off) uint32le_decode(&net_bytes[off])
+#define GET_U64LE(off) uint64le_decode(&net_bytes[off])
+#define LAST_U8LE() GET_U8LE(net_offset-1)
+#define LAST_U16LE() GET_U16LE(net_offset-2)
+#define LAST_U32LE() GET_U32LE(net_offset-4)
+#define LAST_U64LE() GET_U64LE(net_offset-8)
+
+#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u
+static size_t_or_error validate_stat(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes, uint32_t *ret_net_size) {
+ uint32_t net_offset = 0;
+ size_t host_size = sizeof(struct lib9p_stat);
+ uint32_t offsetof__stat_size = net_offset + 0;
+ uint32_t offsetof_fstype = net_offset + 2;
+ uint32_t offsetof_qid_type = net_offset + 8;
+ VALIDATE_NET_BYTES(21);
+ if (GET_U8LE(offsetof_qid_type) & ~qt_masks[ctx->version])
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "unknown bits in qt bitfield: ",
+ (base16_u8_, GET_U8LE(offsetof_qid_type) & ~qt_masks[ctx->version])));
+ uint32_t offsetof_mode = net_offset + 0;
+ VALIDATE_NET_BYTES(22);
+ VALIDATE_NET_UTF8(LAST_U16LE());
+ VALIDATE_NET_BYTES(2);
+ VALIDATE_NET_UTF8(LAST_U16LE());
+ VALIDATE_NET_BYTES(2);
+ VALIDATE_NET_UTF8(LAST_U16LE());
+ VALIDATE_NET_BYTES(2);
+ VALIDATE_NET_UTF8(LAST_U16LE());
+#if CONFIG_9P_ENABLE_9P2000_u
+ if (is_ver(ctx, 9P2000_u)) {
+ VALIDATE_NET_BYTES(2);
+ VALIDATE_NET_UTF8(LAST_U16LE());
+ VALIDATE_NET_BYTES(12);
+ }
+#endif /* CONFIG_9P_ENABLE_9P2000_u */
+ uint32_t offsetof_end = net_offset + 0;
+ if (GET_U32LE(offsetof__stat_size) != offsetof_end - offsetof_fstype)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "stat->_stat_size value is wrong: actual:", (base10, GET_U32LE(offsetof__stat_size)), " != correct:", (base10, offsetof_end - offsetof_fstype)));
+ if (GET_U32LE(offsetof_mode) & ~dm_masks[ctx->version])
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "unknown bits in dm bitfield: ",
+ (base16_u32_, GET_U32LE(offsetof_mode) & ~dm_masks[ctx->version])));
+ if (ret_net_size)
+ *ret_net_size = net_offset;
+ return ERROR_NEW_VAL(size_t, host_size);
+}
+
+#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */
+#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u || CONFIG_9P_ENABLE_uninitialized
+static size_t_or_error validate_Tversion([[maybe_unused]] struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
+ uint32_t net_offset = 0;
+ size_t host_size = sizeof(struct lib9p_msg_Tversion);
+ uint32_t offsetof_size = net_offset + 0;
+ uint32_t offsetof_typ = net_offset + 4;
+ VALIDATE_NET_BYTES(13);
+ VALIDATE_NET_UTF8(LAST_U16LE());
+ uint32_t offsetof_end = net_offset + 0;
+ if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Tversion->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, offsetof_end - offsetof_size)));
+ if (GET_U8LE(offsetof_typ) != 100)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Tversion->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 100)));
+ return ERROR_NEW_VAL(size_t, host_size);
+}
+
+static size_t_or_error validate_Rversion([[maybe_unused]] struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
+ uint32_t net_offset = 0;
+ size_t host_size = sizeof(struct lib9p_msg_Rversion);
+ uint32_t offsetof_size = net_offset + 0;
+ uint32_t offsetof_typ = net_offset + 4;
+ VALIDATE_NET_BYTES(13);
+ VALIDATE_NET_UTF8(LAST_U16LE());
+ uint32_t offsetof_end = net_offset + 0;
+ if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Rversion->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, offsetof_end - offsetof_size)));
+ if (GET_U8LE(offsetof_typ) != 101)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Rversion->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 101)));
+ return ERROR_NEW_VAL(size_t, host_size);
+}
+
+#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u || CONFIG_9P_ENABLE_uninitialized */
+#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u
+static size_t_or_error validate_Tauth([[maybe_unused]] struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
+ uint32_t net_offset = 0;
+ size_t host_size = sizeof(struct lib9p_msg_Tauth);
+ uint32_t offsetof_size = net_offset + 0;
+ uint32_t offsetof_typ = net_offset + 4;
+ VALIDATE_NET_BYTES(13);
+ VALIDATE_NET_UTF8(LAST_U16LE());
+ VALIDATE_NET_BYTES(2);
+ VALIDATE_NET_UTF8(LAST_U16LE());
+#if CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_u
+ if (( is_ver(ctx, 9P2000_L) || is_ver(ctx, 9P2000_u) )) {
+ VALIDATE_NET_BYTES(4);
+ }
+#endif /* CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_u */
+ uint32_t offsetof_end = net_offset + 0;
+ if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Tauth->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, offsetof_end - offsetof_size)));
+ if (GET_U8LE(offsetof_typ) != 102)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Tauth->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 102)));
+ return ERROR_NEW_VAL(size_t, host_size);
+}
+
+static size_t_or_error validate_Rauth([[maybe_unused]] struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
+ uint32_t net_offset = 0;
+ size_t host_size = sizeof(struct lib9p_msg_Rauth);
+ uint32_t offsetof_size = net_offset + 0;
+ uint32_t offsetof_typ = net_offset + 4;
+ uint32_t offsetof_aqid_type = net_offset + 7;
+ VALIDATE_NET_BYTES(20);
+ if (GET_U8LE(offsetof_aqid_type) & ~qt_masks[ctx->version])
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "unknown bits in qt bitfield: ",
+ (base16_u8_, GET_U8LE(offsetof_aqid_type) & ~qt_masks[ctx->version])));
+ uint32_t offsetof_end = net_offset + 0;
+ if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Rauth->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, offsetof_end - offsetof_size)));
+ if (GET_U8LE(offsetof_typ) != 103)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Rauth->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 103)));
+ return ERROR_NEW_VAL(size_t, host_size);
+}
+
+static size_t_or_error validate_Tattach([[maybe_unused]] struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
+ uint32_t net_offset = 0;
+ size_t host_size = sizeof(struct lib9p_msg_Tattach);
+ uint32_t offsetof_size = net_offset + 0;
+ uint32_t offsetof_typ = net_offset + 4;
+ VALIDATE_NET_BYTES(17);
+ VALIDATE_NET_UTF8(LAST_U16LE());
+ VALIDATE_NET_BYTES(2);
+ VALIDATE_NET_UTF8(LAST_U16LE());
+#if CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_u
+ if (( is_ver(ctx, 9P2000_L) || is_ver(ctx, 9P2000_u) )) {
+ VALIDATE_NET_BYTES(4);
+ }
+#endif /* CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_u */
+ uint32_t offsetof_end = net_offset + 0;
+ if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Tattach->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, offsetof_end - offsetof_size)));
+ if (GET_U8LE(offsetof_typ) != 104)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Tattach->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 104)));
+ return ERROR_NEW_VAL(size_t, host_size);
+}
+
+static size_t_or_error validate_Rattach([[maybe_unused]] struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
+ uint32_t net_offset = 0;
+ size_t host_size = sizeof(struct lib9p_msg_Rattach);
+ uint32_t offsetof_size = net_offset + 0;
+ uint32_t offsetof_typ = net_offset + 4;
+ uint32_t offsetof_qid_type = net_offset + 7;
+ VALIDATE_NET_BYTES(20);
+ if (GET_U8LE(offsetof_qid_type) & ~qt_masks[ctx->version])
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "unknown bits in qt bitfield: ",
+ (base16_u8_, GET_U8LE(offsetof_qid_type) & ~qt_masks[ctx->version])));
+ uint32_t offsetof_end = net_offset + 0;
+ if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Rattach->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, offsetof_end - offsetof_size)));
+ if (GET_U8LE(offsetof_typ) != 105)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Rattach->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 105)));
+ return ERROR_NEW_VAL(size_t, host_size);
+}
+
+#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */
+#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u || CONFIG_9P_ENABLE_uninitialized
+static size_t_or_error validate_Rerror([[maybe_unused]] struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
+ uint32_t net_offset = 0;
+ size_t host_size = sizeof(struct lib9p_msg_Rerror);
+ uint32_t offsetof_size = net_offset + 0;
+ uint32_t offsetof_typ = net_offset + 4;
+ VALIDATE_NET_BYTES(9);
+ VALIDATE_NET_UTF8(LAST_U16LE());
+#if CONFIG_9P_ENABLE_9P2000_u
+ if (is_ver(ctx, 9P2000_u)) {
+ VALIDATE_NET_BYTES(4);
+ }
+#endif /* CONFIG_9P_ENABLE_9P2000_u */
+ uint32_t offsetof_end = net_offset + 0;
+ if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Rerror->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, offsetof_end - offsetof_size)));
+ if (GET_U8LE(offsetof_typ) != 107)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Rerror->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 107)));
+ return ERROR_NEW_VAL(size_t, host_size);
+}
+
+#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u || CONFIG_9P_ENABLE_uninitialized */
+#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u
+static size_t_or_error validate_Tflush([[maybe_unused]] struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
+ uint32_t net_offset = 0;
+ size_t host_size = sizeof(struct lib9p_msg_Tflush);
+ uint32_t offsetof_size = net_offset + 0;
+ uint32_t offsetof_typ = net_offset + 4;
+ uint32_t offsetof_end = net_offset + 9;
+ VALIDATE_NET_BYTES(9);
+ if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Tflush->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, offsetof_end - offsetof_size)));
+ if (GET_U8LE(offsetof_typ) != 108)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Tflush->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 108)));
+ return ERROR_NEW_VAL(size_t, host_size);
+}
+
+static size_t_or_error validate_Rflush([[maybe_unused]] struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
+ uint32_t net_offset = 0;
+ size_t host_size = sizeof(struct lib9p_msg_Rflush);
+ uint32_t offsetof_size = net_offset + 0;
+ uint32_t offsetof_typ = net_offset + 4;
+ uint32_t offsetof_end = net_offset + 7;
+ VALIDATE_NET_BYTES(7);
+ if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Rflush->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, offsetof_end - offsetof_size)));
+ if (GET_U8LE(offsetof_typ) != 109)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Rflush->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 109)));
+ return ERROR_NEW_VAL(size_t, host_size);
+}
+
+static size_t_or_error validate_Twalk([[maybe_unused]] struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
+ uint32_t net_offset = 0;
+ size_t host_size = sizeof(struct lib9p_msg_Twalk);
+ uint32_t offsetof_size = net_offset + 0;
+ uint32_t offsetof_typ = net_offset + 4;
+ uint32_t offsetof_nwname = net_offset + 15;
+ VALIDATE_NET_BYTES(17);
+ for (uint16_t i = 0, cnt = LAST_U16LE(); i < cnt; i++) {
+ RESERVE_HOST_BYTES(sizeof(struct lib9p_s));
+ VALIDATE_NET_BYTES(2);
+ VALIDATE_NET_UTF8(LAST_U16LE());
+ }
+ uint32_t offsetof_end = net_offset + 0;
+ if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Twalk->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, offsetof_end - offsetof_size)));
+ if (GET_U8LE(offsetof_typ) != 110)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Twalk->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 110)));
+ if (GET_U16LE(offsetof_nwname) > 16)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Twalk->nwname value is too large: ", (base10, GET_U16LE(offsetof_nwname)), " > ", (base10, 16)));
+ return ERROR_NEW_VAL(size_t, host_size);
+}
+
+static size_t_or_error validate_Rwalk([[maybe_unused]] struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
+ uint32_t net_offset = 0;
+ size_t host_size = sizeof(struct lib9p_msg_Rwalk);
+ uint32_t offsetof_size = net_offset + 0;
+ uint32_t offsetof_typ = net_offset + 4;
+ uint32_t offsetof_nwqid = net_offset + 7;
+ VALIDATE_NET_BYTES(9);
+ for (uint16_t i = 0, cnt = LAST_U16LE(); i < cnt; i++) {
+ RESERVE_HOST_BYTES(sizeof(struct lib9p_qid));
+ uint32_t offsetof_wqid_type = net_offset + 0;
+ VALIDATE_NET_BYTES(13);
+ if (GET_U8LE(offsetof_wqid_type) & ~qt_masks[ctx->version])
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "unknown bits in qt bitfield: ",
+ (base16_u8_, GET_U8LE(offsetof_wqid_type) & ~qt_masks[ctx->version])));
+ }
+ uint32_t offsetof_end = net_offset + 0;
+ if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Rwalk->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, offsetof_end - offsetof_size)));
+ if (GET_U8LE(offsetof_typ) != 111)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Rwalk->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 111)));
+ if (GET_U16LE(offsetof_nwqid) > 16)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Rwalk->nwqid value is too large: ", (base10, GET_U16LE(offsetof_nwqid)), " > ", (base10, 16)));
+ return ERROR_NEW_VAL(size_t, host_size);
+}
+
+#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */
+#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u
+static size_t_or_error validate_Topen([[maybe_unused]] struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
+ uint32_t net_offset = 0;
+ size_t host_size = sizeof(struct lib9p_msg_Topen);
+ uint32_t offsetof_size = net_offset + 0;
+ uint32_t offsetof_typ = net_offset + 4;
+ uint32_t offsetof_mode = net_offset + 11;
+ uint32_t offsetof_end = net_offset + 12;
+ VALIDATE_NET_BYTES(12);
+ if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Topen->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, offsetof_end - offsetof_size)));
+ if (GET_U8LE(offsetof_typ) != 112)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Topen->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 112)));
+ if (GET_U8LE(offsetof_mode) & ~o_masks[ctx->version])
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "unknown bits in o bitfield: ",
+ (base16_u8_, GET_U8LE(offsetof_mode) & ~o_masks[ctx->version])));
+ return ERROR_NEW_VAL(size_t, host_size);
+}
+
+static size_t_or_error validate_Ropen([[maybe_unused]] struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
+ uint32_t net_offset = 0;
+ size_t host_size = sizeof(struct lib9p_msg_Ropen);
+ uint32_t offsetof_size = net_offset + 0;
+ uint32_t offsetof_typ = net_offset + 4;
+ uint32_t offsetof_qid_type = net_offset + 7;
+ VALIDATE_NET_BYTES(20);
+ if (GET_U8LE(offsetof_qid_type) & ~qt_masks[ctx->version])
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "unknown bits in qt bitfield: ",
+ (base16_u8_, GET_U8LE(offsetof_qid_type) & ~qt_masks[ctx->version])));
+ uint32_t offsetof_end = net_offset + 4;
+ VALIDATE_NET_BYTES(4);
+ if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Ropen->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, offsetof_end - offsetof_size)));
+ if (GET_U8LE(offsetof_typ) != 113)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Ropen->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 113)));
+ return ERROR_NEW_VAL(size_t, host_size);
+}
+
+static size_t_or_error validate_Tcreate([[maybe_unused]] struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
+ uint32_t net_offset = 0;
+ size_t host_size = sizeof(struct lib9p_msg_Tcreate);
+ uint32_t offsetof_size = net_offset + 0;
+ uint32_t offsetof_typ = net_offset + 4;
+ VALIDATE_NET_BYTES(13);
+ VALIDATE_NET_UTF8(LAST_U16LE());
+ uint32_t offsetof_perm = net_offset + 0;
+ uint32_t offsetof_mode = net_offset + 4;
+ uint32_t offsetof_end = net_offset + 5;
+ VALIDATE_NET_BYTES(5);
+ if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Tcreate->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, offsetof_end - offsetof_size)));
+ if (GET_U8LE(offsetof_typ) != 114)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Tcreate->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 114)));
+ if (GET_U32LE(offsetof_perm) & ~dm_masks[ctx->version])
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "unknown bits in dm bitfield: ",
+ (base16_u32_, GET_U32LE(offsetof_perm) & ~dm_masks[ctx->version])));
+ if (GET_U8LE(offsetof_mode) & ~o_masks[ctx->version])
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "unknown bits in o bitfield: ",
+ (base16_u8_, GET_U8LE(offsetof_mode) & ~o_masks[ctx->version])));
+ return ERROR_NEW_VAL(size_t, host_size);
+}
+
+static size_t_or_error validate_Rcreate([[maybe_unused]] struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
+ uint32_t net_offset = 0;
+ size_t host_size = sizeof(struct lib9p_msg_Rcreate);
+ uint32_t offsetof_size = net_offset + 0;
+ uint32_t offsetof_typ = net_offset + 4;
+ uint32_t offsetof_qid_type = net_offset + 7;
+ VALIDATE_NET_BYTES(20);
+ if (GET_U8LE(offsetof_qid_type) & ~qt_masks[ctx->version])
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "unknown bits in qt bitfield: ",
+ (base16_u8_, GET_U8LE(offsetof_qid_type) & ~qt_masks[ctx->version])));
+ uint32_t offsetof_end = net_offset + 4;
+ VALIDATE_NET_BYTES(4);
+ if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Rcreate->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, offsetof_end - offsetof_size)));
+ if (GET_U8LE(offsetof_typ) != 115)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Rcreate->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 115)));
+ return ERROR_NEW_VAL(size_t, host_size);
+}
+
+#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */
+#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u
+static size_t_or_error validate_Tread([[maybe_unused]] struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
+ uint32_t net_offset = 0;
+ size_t host_size = sizeof(struct lib9p_msg_Tread);
+ uint32_t offsetof_size = net_offset + 0;
+ uint32_t offsetof_typ = net_offset + 4;
+ uint32_t offsetof_offset = net_offset + 11;
+ uint32_t offsetof_count = net_offset + 19;
+ uint32_t offsetof_end = net_offset + 23;
+ VALIDATE_NET_BYTES(23);
+ if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Tread->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, offsetof_end - offsetof_size)));
+ if (GET_U8LE(offsetof_typ) != 116)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Tread->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 116)));
+ if (GET_U64LE(offsetof_offset) > INT64_MAX)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Tread->offset value is too large: ", (base10, GET_U64LE(offsetof_offset)), " > ", (base10, INT64_MAX)));
+ if (GET_U32LE(offsetof_count) > INT32_MAX)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Tread->count value is too large: ", (base10, GET_U32LE(offsetof_count)), " > ", (base10, INT32_MAX)));
+ return ERROR_NEW_VAL(size_t, host_size);
+}
+
+static size_t_or_error validate_Rread([[maybe_unused]] struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
+ uint32_t net_offset = 0;
+ size_t host_size = sizeof(struct lib9p_msg_Rread);
+ uint32_t offsetof_size = net_offset + 0;
+ uint32_t offsetof_typ = net_offset + 4;
+ uint32_t offsetof_count = net_offset + 7;
+ VALIDATE_NET_BYTES(11);
+ VALIDATE_NET_BYTES(LAST_U32LE());
+ uint32_t offsetof_end = net_offset + 0;
+ if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Rread->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, offsetof_end - offsetof_size)));
+ if (GET_U8LE(offsetof_typ) != 117)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Rread->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 117)));
+ if (GET_U32LE(offsetof_count) > INT32_MAX)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Rread->count value is too large: ", (base10, GET_U32LE(offsetof_count)), " > ", (base10, INT32_MAX)));
+ return ERROR_NEW_VAL(size_t, host_size);
+}
+
+static size_t_or_error validate_Twrite([[maybe_unused]] struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
+ uint32_t net_offset = 0;
+ size_t host_size = sizeof(struct lib9p_msg_Twrite);
+ uint32_t offsetof_size = net_offset + 0;
+ uint32_t offsetof_typ = net_offset + 4;
+ uint32_t offsetof_offset = net_offset + 11;
+ uint32_t offsetof_count = net_offset + 19;
+ VALIDATE_NET_BYTES(23);
+ VALIDATE_NET_BYTES(LAST_U32LE());
+ uint32_t offsetof_end = net_offset + 0;
+ if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Twrite->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, offsetof_end - offsetof_size)));
+ if (GET_U8LE(offsetof_typ) != 118)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Twrite->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 118)));
+ if (GET_U64LE(offsetof_offset) > INT64_MAX)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Twrite->offset value is too large: ", (base10, GET_U64LE(offsetof_offset)), " > ", (base10, INT64_MAX)));
+ if (GET_U32LE(offsetof_count) > INT32_MAX)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Twrite->count value is too large: ", (base10, GET_U32LE(offsetof_count)), " > ", (base10, INT32_MAX)));
+ return ERROR_NEW_VAL(size_t, host_size);
+}
+
+static size_t_or_error validate_Rwrite([[maybe_unused]] struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
+ uint32_t net_offset = 0;
+ size_t host_size = sizeof(struct lib9p_msg_Rwrite);
+ uint32_t offsetof_size = net_offset + 0;
+ uint32_t offsetof_typ = net_offset + 4;
+ uint32_t offsetof_count = net_offset + 7;
+ uint32_t offsetof_end = net_offset + 11;
+ VALIDATE_NET_BYTES(11);
+ if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Rwrite->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, offsetof_end - offsetof_size)));
+ if (GET_U8LE(offsetof_typ) != 119)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Rwrite->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 119)));
+ if (GET_U32LE(offsetof_count) > INT32_MAX)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Rwrite->count value is too large: ", (base10, GET_U32LE(offsetof_count)), " > ", (base10, INT32_MAX)));
+ return ERROR_NEW_VAL(size_t, host_size);
+}
+
+static size_t_or_error validate_Tclunk([[maybe_unused]] struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
+ uint32_t net_offset = 0;
+ size_t host_size = sizeof(struct lib9p_msg_Tclunk);
+ uint32_t offsetof_size = net_offset + 0;
+ uint32_t offsetof_typ = net_offset + 4;
+ uint32_t offsetof_end = net_offset + 11;
+ VALIDATE_NET_BYTES(11);
+ if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Tclunk->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, offsetof_end - offsetof_size)));
+ if (GET_U8LE(offsetof_typ) != 120)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Tclunk->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 120)));
+ return ERROR_NEW_VAL(size_t, host_size);
+}
+
+static size_t_or_error validate_Rclunk([[maybe_unused]] struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
+ uint32_t net_offset = 0;
+ size_t host_size = sizeof(struct lib9p_msg_Rclunk);
+ uint32_t offsetof_size = net_offset + 0;
+ uint32_t offsetof_typ = net_offset + 4;
+ uint32_t offsetof_end = net_offset + 7;
+ VALIDATE_NET_BYTES(7);
+ if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Rclunk->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, offsetof_end - offsetof_size)));
+ if (GET_U8LE(offsetof_typ) != 121)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Rclunk->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 121)));
+ return ERROR_NEW_VAL(size_t, host_size);
+}
+
+static size_t_or_error validate_Tremove([[maybe_unused]] struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
+ uint32_t net_offset = 0;
+ size_t host_size = sizeof(struct lib9p_msg_Tremove);
+ uint32_t offsetof_size = net_offset + 0;
+ uint32_t offsetof_typ = net_offset + 4;
+ uint32_t offsetof_end = net_offset + 11;
+ VALIDATE_NET_BYTES(11);
+ if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Tremove->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, offsetof_end - offsetof_size)));
+ if (GET_U8LE(offsetof_typ) != 122)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Tremove->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 122)));
+ return ERROR_NEW_VAL(size_t, host_size);
+}
+
+static size_t_or_error validate_Rremove([[maybe_unused]] struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
+ uint32_t net_offset = 0;
+ size_t host_size = sizeof(struct lib9p_msg_Rremove);
+ uint32_t offsetof_size = net_offset + 0;
+ uint32_t offsetof_typ = net_offset + 4;
+ uint32_t offsetof_end = net_offset + 7;
+ VALIDATE_NET_BYTES(7);
+ if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Rremove->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, offsetof_end - offsetof_size)));
+ if (GET_U8LE(offsetof_typ) != 123)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Rremove->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 123)));
+ return ERROR_NEW_VAL(size_t, host_size);
+}
+
+#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */
+#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u
+static size_t_or_error validate_Tstat([[maybe_unused]] struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
+ uint32_t net_offset = 0;
+ size_t host_size = sizeof(struct lib9p_msg_Tstat);
+ uint32_t offsetof_size = net_offset + 0;
+ uint32_t offsetof_typ = net_offset + 4;
+ uint32_t offsetof_end = net_offset + 11;
+ VALIDATE_NET_BYTES(11);
+ if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Tstat->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, offsetof_end - offsetof_size)));
+ if (GET_U8LE(offsetof_typ) != 124)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Tstat->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 124)));
+ return ERROR_NEW_VAL(size_t, host_size);
+}
+
+static size_t_or_error validate_Rstat([[maybe_unused]] struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
+ uint32_t net_offset = 0;
+ size_t host_size = sizeof(struct lib9p_msg_Rstat);
+ uint32_t offsetof_size = net_offset + 0;
+ uint32_t offsetof_typ = net_offset + 4;
+ uint32_t offsetof_nstat = net_offset + 7;
+ uint32_t offsetof_stat = net_offset + 9;
+ uint32_t offsetof_stat__stat_size = net_offset + 9;
+ uint32_t offsetof_stat_fstype = net_offset + 11;
+ uint32_t offsetof_stat_qid_type = net_offset + 17;
+ VALIDATE_NET_BYTES(30);
+ if (GET_U8LE(offsetof_stat_qid_type) & ~qt_masks[ctx->version])
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "unknown bits in qt bitfield: ",
+ (base16_u8_, GET_U8LE(offsetof_stat_qid_type) & ~qt_masks[ctx->version])));
+ uint32_t offsetof_stat_mode = net_offset + 0;
+ VALIDATE_NET_BYTES(22);
+ VALIDATE_NET_UTF8(LAST_U16LE());
+ VALIDATE_NET_BYTES(2);
+ VALIDATE_NET_UTF8(LAST_U16LE());
+ VALIDATE_NET_BYTES(2);
+ VALIDATE_NET_UTF8(LAST_U16LE());
+ VALIDATE_NET_BYTES(2);
+ VALIDATE_NET_UTF8(LAST_U16LE());
+#if CONFIG_9P_ENABLE_9P2000_u
+ if (is_ver(ctx, 9P2000_u)) {
+ VALIDATE_NET_BYTES(2);
+ VALIDATE_NET_UTF8(LAST_U16LE());
+ VALIDATE_NET_BYTES(12);
+ }
+#endif /* CONFIG_9P_ENABLE_9P2000_u */
+ uint32_t offsetof_stat_end = net_offset + 0;
+ if (GET_U32LE(offsetof_stat__stat_size) != offsetof_stat_end - offsetof_stat_fstype)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Rstat->stat._stat_size value is wrong: actual:", (base10, GET_U32LE(offsetof_stat__stat_size)), " != correct:", (base10, offsetof_stat_end - offsetof_stat_fstype)));
+ if (GET_U32LE(offsetof_stat_mode) & ~dm_masks[ctx->version])
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "unknown bits in dm bitfield: ",
+ (base16_u32_, GET_U32LE(offsetof_stat_mode) & ~dm_masks[ctx->version])));
+ uint32_t offsetof_end = net_offset + 0;
+ if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Rstat->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, offsetof_end - offsetof_size)));
+ if (GET_U8LE(offsetof_typ) != 125)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Rstat->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 125)));
+ if (GET_U32LE(offsetof_nstat) != offsetof_end - offsetof_stat)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Rstat->nstat value is wrong: actual:", (base10, GET_U32LE(offsetof_nstat)), " != correct:", (base10, offsetof_end - offsetof_stat)));
+ return ERROR_NEW_VAL(size_t, host_size);
+}
+
+static size_t_or_error validate_Twstat([[maybe_unused]] struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
+ uint32_t net_offset = 0;
+ size_t host_size = sizeof(struct lib9p_msg_Twstat);
+ uint32_t offsetof_size = net_offset + 0;
+ uint32_t offsetof_typ = net_offset + 4;
+ uint32_t offsetof_nstat = net_offset + 11;
+ uint32_t offsetof_stat = net_offset + 13;
+ uint32_t offsetof_stat__stat_size = net_offset + 13;
+ uint32_t offsetof_stat_fstype = net_offset + 15;
+ uint32_t offsetof_stat_qid_type = net_offset + 21;
+ VALIDATE_NET_BYTES(34);
+ if (GET_U8LE(offsetof_stat_qid_type) & ~qt_masks[ctx->version])
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "unknown bits in qt bitfield: ",
+ (base16_u8_, GET_U8LE(offsetof_stat_qid_type) & ~qt_masks[ctx->version])));
+ uint32_t offsetof_stat_mode = net_offset + 0;
+ VALIDATE_NET_BYTES(22);
+ VALIDATE_NET_UTF8(LAST_U16LE());
+ VALIDATE_NET_BYTES(2);
+ VALIDATE_NET_UTF8(LAST_U16LE());
+ VALIDATE_NET_BYTES(2);
+ VALIDATE_NET_UTF8(LAST_U16LE());
+ VALIDATE_NET_BYTES(2);
+ VALIDATE_NET_UTF8(LAST_U16LE());
+#if CONFIG_9P_ENABLE_9P2000_u
+ if (is_ver(ctx, 9P2000_u)) {
+ VALIDATE_NET_BYTES(2);
+ VALIDATE_NET_UTF8(LAST_U16LE());
+ VALIDATE_NET_BYTES(12);
+ }
+#endif /* CONFIG_9P_ENABLE_9P2000_u */
+ uint32_t offsetof_stat_end = net_offset + 0;
+ if (GET_U32LE(offsetof_stat__stat_size) != offsetof_stat_end - offsetof_stat_fstype)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Twstat->stat._stat_size value is wrong: actual:", (base10, GET_U32LE(offsetof_stat__stat_size)), " != correct:", (base10, offsetof_stat_end - offsetof_stat_fstype)));
+ if (GET_U32LE(offsetof_stat_mode) & ~dm_masks[ctx->version])
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "unknown bits in dm bitfield: ",
+ (base16_u32_, GET_U32LE(offsetof_stat_mode) & ~dm_masks[ctx->version])));
+ uint32_t offsetof_end = net_offset + 0;
+ if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Twstat->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, offsetof_end - offsetof_size)));
+ if (GET_U8LE(offsetof_typ) != 126)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Twstat->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 126)));
+ if (GET_U32LE(offsetof_nstat) != offsetof_end - offsetof_stat)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Twstat->nstat value is wrong: actual:", (base10, GET_U32LE(offsetof_nstat)), " != correct:", (base10, offsetof_end - offsetof_stat)));
+ return ERROR_NEW_VAL(size_t, host_size);
+}
+
+static size_t_or_error validate_Rwstat([[maybe_unused]] struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
+ uint32_t net_offset = 0;
+ size_t host_size = sizeof(struct lib9p_msg_Rwstat);
+ uint32_t offsetof_size = net_offset + 0;
+ uint32_t offsetof_typ = net_offset + 4;
+ uint32_t offsetof_end = net_offset + 7;
+ VALIDATE_NET_BYTES(7);
+ if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Rwstat->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, offsetof_end - offsetof_size)));
+ if (GET_U8LE(offsetof_typ) != 127)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Rwstat->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 127)));
+ return ERROR_NEW_VAL(size_t, host_size);
+}
+
+#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */
+#if CONFIG_9P_ENABLE_9P2000_p9p
+static size_t_or_error validate_Topenfd([[maybe_unused]] struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
+ uint32_t net_offset = 0;
+ size_t host_size = sizeof(struct lib9p_msg_Topenfd);
+ uint32_t offsetof_size = net_offset + 0;
+ uint32_t offsetof_typ = net_offset + 4;
+ uint32_t offsetof_mode = net_offset + 11;
+ uint32_t offsetof_end = net_offset + 12;
+ VALIDATE_NET_BYTES(12);
+ if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Topenfd->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, offsetof_end - offsetof_size)));
+ if (GET_U8LE(offsetof_typ) != 98)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Topenfd->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 98)));
+ if (GET_U8LE(offsetof_mode) & ~o_masks[ctx->version])
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "unknown bits in o bitfield: ",
+ (base16_u8_, GET_U8LE(offsetof_mode) & ~o_masks[ctx->version])));
+ return ERROR_NEW_VAL(size_t, host_size);
+}
+
+static size_t_or_error validate_Ropenfd([[maybe_unused]] struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
+ uint32_t net_offset = 0;
+ size_t host_size = sizeof(struct lib9p_msg_Ropenfd);
+ uint32_t offsetof_size = net_offset + 0;
+ uint32_t offsetof_typ = net_offset + 4;
+ uint32_t offsetof_qid_type = net_offset + 7;
+ VALIDATE_NET_BYTES(20);
+ if (GET_U8LE(offsetof_qid_type) & ~qt_masks[ctx->version])
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "unknown bits in qt bitfield: ",
+ (base16_u8_, GET_U8LE(offsetof_qid_type) & ~qt_masks[ctx->version])));
+ uint32_t offsetof_end = net_offset + 8;
+ VALIDATE_NET_BYTES(8);
+ if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Ropenfd->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, offsetof_end - offsetof_size)));
+ if (GET_U8LE(offsetof_typ) != 99)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Ropenfd->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 99)));
+ return ERROR_NEW_VAL(size_t, host_size);
+}
+
+#endif /* CONFIG_9P_ENABLE_9P2000_p9p */
+#if CONFIG_9P_ENABLE_9P2000_L
+static size_t_or_error validate_Rlerror([[maybe_unused]] struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
+ uint32_t net_offset = 0;
+ size_t host_size = sizeof(struct lib9p_msg_Rlerror);
+ uint32_t offsetof_size = net_offset + 0;
+ uint32_t offsetof_typ = net_offset + 4;
+ uint32_t offsetof_end = net_offset + 11;
+ VALIDATE_NET_BYTES(11);
+ if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Rlerror->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, offsetof_end - offsetof_size)));
+ if (GET_U8LE(offsetof_typ) != 7)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Rlerror->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 7)));
+ return ERROR_NEW_VAL(size_t, host_size);
+}
+
+static size_t_or_error validate_Tstatfs([[maybe_unused]] struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
+ uint32_t net_offset = 0;
+ size_t host_size = sizeof(struct lib9p_msg_Tstatfs);
+ uint32_t offsetof_size = net_offset + 0;
+ uint32_t offsetof_typ = net_offset + 4;
+ uint32_t offsetof_end = net_offset + 11;
+ VALIDATE_NET_BYTES(11);
+ if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Tstatfs->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, offsetof_end - offsetof_size)));
+ if (GET_U8LE(offsetof_typ) != 8)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Tstatfs->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 8)));
+ return ERROR_NEW_VAL(size_t, host_size);
+}
+
+static size_t_or_error validate_Rstatfs([[maybe_unused]] struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
+ uint32_t net_offset = 0;
+ size_t host_size = sizeof(struct lib9p_msg_Rstatfs);
+ uint32_t offsetof_size = net_offset + 0;
+ uint32_t offsetof_typ = net_offset + 4;
+ uint32_t offsetof_end = net_offset + 67;
+ VALIDATE_NET_BYTES(67);
+ if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Rstatfs->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, offsetof_end - offsetof_size)));
+ if (GET_U8LE(offsetof_typ) != 9)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Rstatfs->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 9)));
+ return ERROR_NEW_VAL(size_t, host_size);
+}
+
+static size_t_or_error validate_Tlopen([[maybe_unused]] struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
+ uint32_t net_offset = 0;
+ size_t host_size = sizeof(struct lib9p_msg_Tlopen);
+ uint32_t offsetof_size = net_offset + 0;
+ uint32_t offsetof_typ = net_offset + 4;
+ uint32_t offsetof_flags = net_offset + 11;
+ uint32_t offsetof_end = net_offset + 15;
+ VALIDATE_NET_BYTES(15);
+ if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Tlopen->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, offsetof_end - offsetof_size)));
+ if (GET_U8LE(offsetof_typ) != 12)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Tlopen->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 12)));
+ if (GET_U32LE(offsetof_flags) & ~lo_masks[ctx->version])
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "unknown bits in lo bitfield: ",
+ (base16_u32_, GET_U32LE(offsetof_flags) & ~lo_masks[ctx->version])));
+ return ERROR_NEW_VAL(size_t, host_size);
+}
+
+static size_t_or_error validate_Rlopen([[maybe_unused]] struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
+ uint32_t net_offset = 0;
+ size_t host_size = sizeof(struct lib9p_msg_Rlopen);
+ uint32_t offsetof_size = net_offset + 0;
+ uint32_t offsetof_typ = net_offset + 4;
+ uint32_t offsetof_qid_type = net_offset + 7;
+ VALIDATE_NET_BYTES(20);
+ if (GET_U8LE(offsetof_qid_type) & ~qt_masks[ctx->version])
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "unknown bits in qt bitfield: ",
+ (base16_u8_, GET_U8LE(offsetof_qid_type) & ~qt_masks[ctx->version])));
+ uint32_t offsetof_end = net_offset + 4;
+ VALIDATE_NET_BYTES(4);
+ if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Rlopen->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, offsetof_end - offsetof_size)));
+ if (GET_U8LE(offsetof_typ) != 13)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Rlopen->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 13)));
+ return ERROR_NEW_VAL(size_t, host_size);
+}
+
+static size_t_or_error validate_Tlcreate([[maybe_unused]] struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
+ uint32_t net_offset = 0;
+ size_t host_size = sizeof(struct lib9p_msg_Tlcreate);
+ uint32_t offsetof_size = net_offset + 0;
+ uint32_t offsetof_typ = net_offset + 4;
+ VALIDATE_NET_BYTES(13);
+ VALIDATE_NET_UTF8(LAST_U16LE());
+ uint32_t offsetof_flags = net_offset + 0;
+ uint32_t offsetof_mode = net_offset + 4;
+ uint32_t offsetof_end = net_offset + 12;
+ VALIDATE_NET_BYTES(12);
+ if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Tlcreate->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, offsetof_end - offsetof_size)));
+ if (GET_U8LE(offsetof_typ) != 14)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Tlcreate->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 14)));
+ if (GET_U32LE(offsetof_flags) & ~lo_masks[ctx->version])
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "unknown bits in lo bitfield: ",
+ (base16_u32_, GET_U32LE(offsetof_flags) & ~lo_masks[ctx->version])));
+ if (GET_U32LE(offsetof_mode) & ~mode_masks[ctx->version])
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "unknown bits in mode bitfield: ",
+ (base16_u32_, GET_U32LE(offsetof_mode) & ~mode_masks[ctx->version])));
+ return ERROR_NEW_VAL(size_t, host_size);
+}
+
+static size_t_or_error validate_Rlcreate([[maybe_unused]] struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
+ uint32_t net_offset = 0;
+ size_t host_size = sizeof(struct lib9p_msg_Rlcreate);
+ uint32_t offsetof_size = net_offset + 0;
+ uint32_t offsetof_typ = net_offset + 4;
+ uint32_t offsetof_qid_type = net_offset + 7;
+ VALIDATE_NET_BYTES(20);
+ if (GET_U8LE(offsetof_qid_type) & ~qt_masks[ctx->version])
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "unknown bits in qt bitfield: ",
+ (base16_u8_, GET_U8LE(offsetof_qid_type) & ~qt_masks[ctx->version])));
+ uint32_t offsetof_end = net_offset + 4;
+ VALIDATE_NET_BYTES(4);
+ if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Rlcreate->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, offsetof_end - offsetof_size)));
+ if (GET_U8LE(offsetof_typ) != 15)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Rlcreate->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 15)));
+ return ERROR_NEW_VAL(size_t, host_size);
+}
+
+static size_t_or_error validate_Tsymlink([[maybe_unused]] struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
+ uint32_t net_offset = 0;
+ size_t host_size = sizeof(struct lib9p_msg_Tsymlink);
+ uint32_t offsetof_size = net_offset + 0;
+ uint32_t offsetof_typ = net_offset + 4;
+ VALIDATE_NET_BYTES(13);
+ VALIDATE_NET_UTF8(LAST_U16LE());
+ VALIDATE_NET_BYTES(2);
+ VALIDATE_NET_UTF8(LAST_U16LE());
+ uint32_t offsetof_end = net_offset + 4;
+ VALIDATE_NET_BYTES(4);
+ if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Tsymlink->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, offsetof_end - offsetof_size)));
+ if (GET_U8LE(offsetof_typ) != 16)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Tsymlink->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 16)));
+ return ERROR_NEW_VAL(size_t, host_size);
+}
+
+static size_t_or_error validate_Rsymlink([[maybe_unused]] struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
+ uint32_t net_offset = 0;
+ size_t host_size = sizeof(struct lib9p_msg_Rsymlink);
+ uint32_t offsetof_size = net_offset + 0;
+ uint32_t offsetof_typ = net_offset + 4;
+ uint32_t offsetof_qid_type = net_offset + 7;
+ VALIDATE_NET_BYTES(20);
+ if (GET_U8LE(offsetof_qid_type) & ~qt_masks[ctx->version])
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "unknown bits in qt bitfield: ",
+ (base16_u8_, GET_U8LE(offsetof_qid_type) & ~qt_masks[ctx->version])));
+ uint32_t offsetof_end = net_offset + 0;
+ if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Rsymlink->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, offsetof_end - offsetof_size)));
+ if (GET_U8LE(offsetof_typ) != 17)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Rsymlink->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 17)));
+ return ERROR_NEW_VAL(size_t, host_size);
+}
+
+static size_t_or_error validate_Tmknod([[maybe_unused]] struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
+ uint32_t net_offset = 0;
+ size_t host_size = sizeof(struct lib9p_msg_Tmknod);
+ uint32_t offsetof_size = net_offset + 0;
+ uint32_t offsetof_typ = net_offset + 4;
+ VALIDATE_NET_BYTES(13);
+ VALIDATE_NET_UTF8(LAST_U16LE());
+ uint32_t offsetof_mode = net_offset + 0;
+ uint32_t offsetof_end = net_offset + 16;
+ VALIDATE_NET_BYTES(16);
+ if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Tmknod->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, offsetof_end - offsetof_size)));
+ if (GET_U8LE(offsetof_typ) != 18)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Tmknod->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 18)));
+ if (GET_U32LE(offsetof_mode) & ~mode_masks[ctx->version])
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "unknown bits in mode bitfield: ",
+ (base16_u32_, GET_U32LE(offsetof_mode) & ~mode_masks[ctx->version])));
+ return ERROR_NEW_VAL(size_t, host_size);
+}
+
+static size_t_or_error validate_Rmknod([[maybe_unused]] struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
+ uint32_t net_offset = 0;
+ size_t host_size = sizeof(struct lib9p_msg_Rmknod);
+ uint32_t offsetof_size = net_offset + 0;
+ uint32_t offsetof_typ = net_offset + 4;
+ uint32_t offsetof_qid_type = net_offset + 7;
+ VALIDATE_NET_BYTES(20);
+ if (GET_U8LE(offsetof_qid_type) & ~qt_masks[ctx->version])
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "unknown bits in qt bitfield: ",
+ (base16_u8_, GET_U8LE(offsetof_qid_type) & ~qt_masks[ctx->version])));
+ uint32_t offsetof_end = net_offset + 0;
+ if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Rmknod->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, offsetof_end - offsetof_size)));
+ if (GET_U8LE(offsetof_typ) != 19)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Rmknod->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 19)));
+ return ERROR_NEW_VAL(size_t, host_size);
+}
+
+static size_t_or_error validate_Trename([[maybe_unused]] struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
+ uint32_t net_offset = 0;
+ size_t host_size = sizeof(struct lib9p_msg_Trename);
+ uint32_t offsetof_size = net_offset + 0;
+ uint32_t offsetof_typ = net_offset + 4;
+ VALIDATE_NET_BYTES(17);
+ VALIDATE_NET_UTF8(LAST_U16LE());
+ uint32_t offsetof_end = net_offset + 0;
+ if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Trename->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, offsetof_end - offsetof_size)));
+ if (GET_U8LE(offsetof_typ) != 20)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Trename->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 20)));
+ return ERROR_NEW_VAL(size_t, host_size);
+}
+
+static size_t_or_error validate_Rrename([[maybe_unused]] struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
+ uint32_t net_offset = 0;
+ size_t host_size = sizeof(struct lib9p_msg_Rrename);
+ uint32_t offsetof_size = net_offset + 0;
+ uint32_t offsetof_typ = net_offset + 4;
+ uint32_t offsetof_end = net_offset + 7;
+ VALIDATE_NET_BYTES(7);
+ if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Rrename->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, offsetof_end - offsetof_size)));
+ if (GET_U8LE(offsetof_typ) != 21)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Rrename->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 21)));
+ return ERROR_NEW_VAL(size_t, host_size);
+}
+
+static size_t_or_error validate_Treadlink([[maybe_unused]] struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
+ uint32_t net_offset = 0;
+ size_t host_size = sizeof(struct lib9p_msg_Treadlink);
+ uint32_t offsetof_size = net_offset + 0;
+ uint32_t offsetof_typ = net_offset + 4;
+ uint32_t offsetof_end = net_offset + 11;
+ VALIDATE_NET_BYTES(11);
+ if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Treadlink->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, offsetof_end - offsetof_size)));
+ if (GET_U8LE(offsetof_typ) != 22)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Treadlink->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 22)));
+ return ERROR_NEW_VAL(size_t, host_size);
+}
+
+static size_t_or_error validate_Rreadlink([[maybe_unused]] struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
+ uint32_t net_offset = 0;
+ size_t host_size = sizeof(struct lib9p_msg_Rreadlink);
+ uint32_t offsetof_size = net_offset + 0;
+ uint32_t offsetof_typ = net_offset + 4;
+ VALIDATE_NET_BYTES(9);
+ VALIDATE_NET_UTF8(LAST_U16LE());
+ uint32_t offsetof_end = net_offset + 0;
+ if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Rreadlink->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, offsetof_end - offsetof_size)));
+ if (GET_U8LE(offsetof_typ) != 23)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Rreadlink->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 23)));
+ return ERROR_NEW_VAL(size_t, host_size);
+}
+
+static size_t_or_error validate_Tgetattr([[maybe_unused]] struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
+ uint32_t net_offset = 0;
+ size_t host_size = sizeof(struct lib9p_msg_Tgetattr);
+ uint32_t offsetof_size = net_offset + 0;
+ uint32_t offsetof_typ = net_offset + 4;
+ uint32_t offsetof_request_mask = net_offset + 11;
+ uint32_t offsetof_end = net_offset + 19;
+ VALIDATE_NET_BYTES(19);
+ if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Tgetattr->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, offsetof_end - offsetof_size)));
+ if (GET_U8LE(offsetof_typ) != 24)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Tgetattr->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 24)));
+ if (GET_U64LE(offsetof_request_mask) & ~getattr_masks[ctx->version])
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "unknown bits in getattr bitfield: ",
+ (base16_u64_, GET_U64LE(offsetof_request_mask) & ~getattr_masks[ctx->version])));
+ return ERROR_NEW_VAL(size_t, host_size);
+}
+
+static size_t_or_error validate_Rgetattr([[maybe_unused]] struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
+ uint32_t net_offset = 0;
+ size_t host_size = sizeof(struct lib9p_msg_Rgetattr);
+ uint32_t offsetof_size = net_offset + 0;
+ uint32_t offsetof_typ = net_offset + 4;
+ uint32_t offsetof_valid = net_offset + 7;
+ uint32_t offsetof_qid_type = net_offset + 15;
+ VALIDATE_NET_BYTES(28);
+ if (GET_U8LE(offsetof_qid_type) & ~qt_masks[ctx->version])
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "unknown bits in qt bitfield: ",
+ (base16_u8_, GET_U8LE(offsetof_qid_type) & ~qt_masks[ctx->version])));
+ uint32_t offsetof_mode = net_offset + 0;
+ uint32_t offsetof_end = net_offset + 132;
+ VALIDATE_NET_BYTES(132);
+ if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Rgetattr->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, offsetof_end - offsetof_size)));
+ if (GET_U8LE(offsetof_typ) != 25)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Rgetattr->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 25)));
+ if (GET_U64LE(offsetof_valid) & ~getattr_masks[ctx->version])
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "unknown bits in getattr bitfield: ",
+ (base16_u64_, GET_U64LE(offsetof_valid) & ~getattr_masks[ctx->version])));
+ if (GET_U32LE(offsetof_mode) & ~mode_masks[ctx->version])
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "unknown bits in mode bitfield: ",
+ (base16_u32_, GET_U32LE(offsetof_mode) & ~mode_masks[ctx->version])));
+ return ERROR_NEW_VAL(size_t, host_size);
+}
+
+static size_t_or_error validate_Tsetattr([[maybe_unused]] struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
+ uint32_t net_offset = 0;
+ size_t host_size = sizeof(struct lib9p_msg_Tsetattr);
+ uint32_t offsetof_size = net_offset + 0;
+ uint32_t offsetof_typ = net_offset + 4;
+ uint32_t offsetof_valid = net_offset + 11;
+ uint32_t offsetof_mode = net_offset + 15;
+ uint32_t offsetof_end = net_offset + 67;
+ VALIDATE_NET_BYTES(67);
+ if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Tsetattr->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, offsetof_end - offsetof_size)));
+ if (GET_U8LE(offsetof_typ) != 26)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Tsetattr->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 26)));
+ if (GET_U32LE(offsetof_valid) & ~setattr_masks[ctx->version])
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "unknown bits in setattr bitfield: ",
+ (base16_u32_, GET_U32LE(offsetof_valid) & ~setattr_masks[ctx->version])));
+ if (GET_U32LE(offsetof_mode) & ~mode_masks[ctx->version])
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "unknown bits in mode bitfield: ",
+ (base16_u32_, GET_U32LE(offsetof_mode) & ~mode_masks[ctx->version])));
+ return ERROR_NEW_VAL(size_t, host_size);
+}
+
+static size_t_or_error validate_Rsetattr([[maybe_unused]] struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
+ uint32_t net_offset = 0;
+ size_t host_size = sizeof(struct lib9p_msg_Rsetattr);
+ uint32_t offsetof_size = net_offset + 0;
+ uint32_t offsetof_typ = net_offset + 4;
+ uint32_t offsetof_end = net_offset + 7;
+ VALIDATE_NET_BYTES(7);
+ if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Rsetattr->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, offsetof_end - offsetof_size)));
+ if (GET_U8LE(offsetof_typ) != 27)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Rsetattr->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 27)));
+ return ERROR_NEW_VAL(size_t, host_size);
+}
+
+static size_t_or_error validate_Txattrwalk([[maybe_unused]] struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
+ uint32_t net_offset = 0;
+ size_t host_size = sizeof(struct lib9p_msg_Txattrwalk);
+ uint32_t offsetof_size = net_offset + 0;
+ uint32_t offsetof_typ = net_offset + 4;
+ VALIDATE_NET_BYTES(17);
+ VALIDATE_NET_UTF8(LAST_U16LE());
+ uint32_t offsetof_end = net_offset + 0;
+ if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Txattrwalk->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, offsetof_end - offsetof_size)));
+ if (GET_U8LE(offsetof_typ) != 30)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Txattrwalk->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 30)));
+ return ERROR_NEW_VAL(size_t, host_size);
+}
+
+static size_t_or_error validate_Rxattrwalk([[maybe_unused]] struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
+ uint32_t net_offset = 0;
+ size_t host_size = sizeof(struct lib9p_msg_Rxattrwalk);
+ uint32_t offsetof_size = net_offset + 0;
+ uint32_t offsetof_typ = net_offset + 4;
+ uint32_t offsetof_end = net_offset + 15;
+ VALIDATE_NET_BYTES(15);
+ if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Rxattrwalk->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, offsetof_end - offsetof_size)));
+ if (GET_U8LE(offsetof_typ) != 31)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Rxattrwalk->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 31)));
+ return ERROR_NEW_VAL(size_t, host_size);
+}
+
+static size_t_or_error validate_Txattrcreate([[maybe_unused]] struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
+ uint32_t net_offset = 0;
+ size_t host_size = sizeof(struct lib9p_msg_Txattrcreate);
+ uint32_t offsetof_size = net_offset + 0;
+ uint32_t offsetof_typ = net_offset + 4;
+ VALIDATE_NET_BYTES(13);
+ VALIDATE_NET_UTF8(LAST_U16LE());
+ uint32_t offsetof_end = net_offset + 12;
+ VALIDATE_NET_BYTES(12);
+ if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Txattrcreate->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, offsetof_end - offsetof_size)));
+ if (GET_U8LE(offsetof_typ) != 32)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Txattrcreate->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 32)));
+ return ERROR_NEW_VAL(size_t, host_size);
+}
+
+static size_t_or_error validate_Rxattrcreate([[maybe_unused]] struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
+ uint32_t net_offset = 0;
+ size_t host_size = sizeof(struct lib9p_msg_Rxattrcreate);
+ uint32_t offsetof_size = net_offset + 0;
+ uint32_t offsetof_typ = net_offset + 4;
+ uint32_t offsetof_end = net_offset + 7;
+ VALIDATE_NET_BYTES(7);
+ if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Rxattrcreate->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, offsetof_end - offsetof_size)));
+ if (GET_U8LE(offsetof_typ) != 33)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Rxattrcreate->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 33)));
+ return ERROR_NEW_VAL(size_t, host_size);
+}
+
+static size_t_or_error validate_Treaddir([[maybe_unused]] struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
+ uint32_t net_offset = 0;
+ size_t host_size = sizeof(struct lib9p_msg_Treaddir);
+ uint32_t offsetof_size = net_offset + 0;
+ uint32_t offsetof_typ = net_offset + 4;
+ uint32_t offsetof_end = net_offset + 23;
+ VALIDATE_NET_BYTES(23);
+ if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Treaddir->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, offsetof_end - offsetof_size)));
+ if (GET_U8LE(offsetof_typ) != 40)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Treaddir->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 40)));
+ return ERROR_NEW_VAL(size_t, host_size);
+}
+
+static size_t_or_error validate_Rreaddir([[maybe_unused]] struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
+ uint32_t net_offset = 0;
+ size_t host_size = sizeof(struct lib9p_msg_Rreaddir);
+ uint32_t offsetof_size = net_offset + 0;
+ uint32_t offsetof_typ = net_offset + 4;
+ VALIDATE_NET_BYTES(11);
+ VALIDATE_NET_BYTES(LAST_U32LE());
+ uint32_t offsetof_end = net_offset + 0;
+ if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Rreaddir->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, offsetof_end - offsetof_size)));
+ if (GET_U8LE(offsetof_typ) != 41)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Rreaddir->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 41)));
+ return ERROR_NEW_VAL(size_t, host_size);
+}
+
+static size_t_or_error validate_Tfsync([[maybe_unused]] struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
+ uint32_t net_offset = 0;
+ size_t host_size = sizeof(struct lib9p_msg_Tfsync);
+ uint32_t offsetof_size = net_offset + 0;
+ uint32_t offsetof_typ = net_offset + 4;
+ uint32_t offsetof_end = net_offset + 15;
+ VALIDATE_NET_BYTES(15);
+ if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Tfsync->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, offsetof_end - offsetof_size)));
+ if (GET_U8LE(offsetof_typ) != 50)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Tfsync->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 50)));
+ return ERROR_NEW_VAL(size_t, host_size);
+}
+
+static size_t_or_error validate_Rfsync([[maybe_unused]] struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
+ uint32_t net_offset = 0;
+ size_t host_size = sizeof(struct lib9p_msg_Rfsync);
+ uint32_t offsetof_size = net_offset + 0;
+ uint32_t offsetof_typ = net_offset + 4;
+ uint32_t offsetof_end = net_offset + 7;
+ VALIDATE_NET_BYTES(7);
+ if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Rfsync->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, offsetof_end - offsetof_size)));
+ if (GET_U8LE(offsetof_typ) != 51)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Rfsync->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 51)));
+ return ERROR_NEW_VAL(size_t, host_size);
+}
+
+static size_t_or_error validate_Tlock([[maybe_unused]] struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
+ uint32_t net_offset = 0;
+ size_t host_size = sizeof(struct lib9p_msg_Tlock);
+ uint32_t offsetof_size = net_offset + 0;
+ uint32_t offsetof_typ = net_offset + 4;
+ uint32_t offsetof_flags = net_offset + 12;
+ VALIDATE_NET_BYTES(38);
+ VALIDATE_NET_UTF8(LAST_U16LE());
+ uint32_t offsetof_end = net_offset + 0;
+ if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Tlock->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, offsetof_end - offsetof_size)));
+ if (GET_U8LE(offsetof_typ) != 52)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Tlock->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 52)));
+ if (GET_U32LE(offsetof_flags) & ~lock_flags_masks[ctx->version])
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "unknown bits in lock_flags bitfield: ",
+ (base16_u32_, GET_U32LE(offsetof_flags) & ~lock_flags_masks[ctx->version])));
+ return ERROR_NEW_VAL(size_t, host_size);
+}
+
+static size_t_or_error validate_Rlock([[maybe_unused]] struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
+ uint32_t net_offset = 0;
+ size_t host_size = sizeof(struct lib9p_msg_Rlock);
+ uint32_t offsetof_size = net_offset + 0;
+ uint32_t offsetof_typ = net_offset + 4;
+ uint32_t offsetof_end = net_offset + 8;
+ VALIDATE_NET_BYTES(8);
+ if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Rlock->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, offsetof_end - offsetof_size)));
+ if (GET_U8LE(offsetof_typ) != 53)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Rlock->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 53)));
+ return ERROR_NEW_VAL(size_t, host_size);
+}
+
+static size_t_or_error validate_Tgetlock([[maybe_unused]] struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
+ uint32_t net_offset = 0;
+ size_t host_size = sizeof(struct lib9p_msg_Tgetlock);
+ uint32_t offsetof_size = net_offset + 0;
+ uint32_t offsetof_typ = net_offset + 4;
+ VALIDATE_NET_BYTES(34);
+ VALIDATE_NET_UTF8(LAST_U16LE());
+ uint32_t offsetof_end = net_offset + 0;
+ if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Tgetlock->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, offsetof_end - offsetof_size)));
+ if (GET_U8LE(offsetof_typ) != 54)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Tgetlock->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 54)));
+ return ERROR_NEW_VAL(size_t, host_size);
+}
+
+static size_t_or_error validate_Rgetlock([[maybe_unused]] struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
+ uint32_t net_offset = 0;
+ size_t host_size = sizeof(struct lib9p_msg_Rgetlock);
+ uint32_t offsetof_size = net_offset + 0;
+ uint32_t offsetof_typ = net_offset + 4;
+ VALIDATE_NET_BYTES(30);
+ VALIDATE_NET_UTF8(LAST_U16LE());
+ uint32_t offsetof_end = net_offset + 0;
+ if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Rgetlock->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, offsetof_end - offsetof_size)));
+ if (GET_U8LE(offsetof_typ) != 55)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Rgetlock->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 55)));
+ return ERROR_NEW_VAL(size_t, host_size);
+}
+
+static size_t_or_error validate_Tlink([[maybe_unused]] struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
+ uint32_t net_offset = 0;
+ size_t host_size = sizeof(struct lib9p_msg_Tlink);
+ uint32_t offsetof_size = net_offset + 0;
+ uint32_t offsetof_typ = net_offset + 4;
+ VALIDATE_NET_BYTES(17);
+ VALIDATE_NET_UTF8(LAST_U16LE());
+ uint32_t offsetof_end = net_offset + 0;
+ if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Tlink->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, offsetof_end - offsetof_size)));
+ if (GET_U8LE(offsetof_typ) != 70)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Tlink->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 70)));
+ return ERROR_NEW_VAL(size_t, host_size);
+}
+
+static size_t_or_error validate_Rlink([[maybe_unused]] struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
+ uint32_t net_offset = 0;
+ size_t host_size = sizeof(struct lib9p_msg_Rlink);
+ uint32_t offsetof_size = net_offset + 0;
+ uint32_t offsetof_typ = net_offset + 4;
+ uint32_t offsetof_end = net_offset + 7;
+ VALIDATE_NET_BYTES(7);
+ if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Rlink->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, offsetof_end - offsetof_size)));
+ if (GET_U8LE(offsetof_typ) != 71)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Rlink->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 71)));
+ return ERROR_NEW_VAL(size_t, host_size);
+}
+
+static size_t_or_error validate_Tmkdir([[maybe_unused]] struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
+ uint32_t net_offset = 0;
+ size_t host_size = sizeof(struct lib9p_msg_Tmkdir);
+ uint32_t offsetof_size = net_offset + 0;
+ uint32_t offsetof_typ = net_offset + 4;
+ VALIDATE_NET_BYTES(13);
+ VALIDATE_NET_UTF8(LAST_U16LE());
+ uint32_t offsetof_mode = net_offset + 0;
+ uint32_t offsetof_end = net_offset + 8;
+ VALIDATE_NET_BYTES(8);
+ if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Tmkdir->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, offsetof_end - offsetof_size)));
+ if (GET_U8LE(offsetof_typ) != 72)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Tmkdir->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 72)));
+ if (GET_U32LE(offsetof_mode) & ~mode_masks[ctx->version])
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "unknown bits in mode bitfield: ",
+ (base16_u32_, GET_U32LE(offsetof_mode) & ~mode_masks[ctx->version])));
+ return ERROR_NEW_VAL(size_t, host_size);
+}
+
+static size_t_or_error validate_Rmkdir([[maybe_unused]] struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
+ uint32_t net_offset = 0;
+ size_t host_size = sizeof(struct lib9p_msg_Rmkdir);
+ uint32_t offsetof_size = net_offset + 0;
+ uint32_t offsetof_typ = net_offset + 4;
+ uint32_t offsetof_qid_type = net_offset + 7;
+ VALIDATE_NET_BYTES(20);
+ if (GET_U8LE(offsetof_qid_type) & ~qt_masks[ctx->version])
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "unknown bits in qt bitfield: ",
+ (base16_u8_, GET_U8LE(offsetof_qid_type) & ~qt_masks[ctx->version])));
+ uint32_t offsetof_end = net_offset + 0;
+ if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Rmkdir->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, offsetof_end - offsetof_size)));
+ if (GET_U8LE(offsetof_typ) != 73)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Rmkdir->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 73)));
+ return ERROR_NEW_VAL(size_t, host_size);
+}
+
+static size_t_or_error validate_Trenameat([[maybe_unused]] struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
+ uint32_t net_offset = 0;
+ size_t host_size = sizeof(struct lib9p_msg_Trenameat);
+ uint32_t offsetof_size = net_offset + 0;
+ uint32_t offsetof_typ = net_offset + 4;
+ VALIDATE_NET_BYTES(13);
+ VALIDATE_NET_UTF8(LAST_U16LE());
+ VALIDATE_NET_BYTES(6);
+ VALIDATE_NET_UTF8(LAST_U16LE());
+ uint32_t offsetof_end = net_offset + 0;
+ if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Trenameat->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, offsetof_end - offsetof_size)));
+ if (GET_U8LE(offsetof_typ) != 74)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Trenameat->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 74)));
+ return ERROR_NEW_VAL(size_t, host_size);
+}
+
+static size_t_or_error validate_Rrenameat([[maybe_unused]] struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
+ uint32_t net_offset = 0;
+ size_t host_size = sizeof(struct lib9p_msg_Rrenameat);
+ uint32_t offsetof_size = net_offset + 0;
+ uint32_t offsetof_typ = net_offset + 4;
+ uint32_t offsetof_end = net_offset + 7;
+ VALIDATE_NET_BYTES(7);
+ if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Rrenameat->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, offsetof_end - offsetof_size)));
+ if (GET_U8LE(offsetof_typ) != 75)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Rrenameat->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 75)));
+ return ERROR_NEW_VAL(size_t, host_size);
+}
+
+static size_t_or_error validate_Tunlinkat([[maybe_unused]] struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
+ uint32_t net_offset = 0;
+ size_t host_size = sizeof(struct lib9p_msg_Tunlinkat);
+ uint32_t offsetof_size = net_offset + 0;
+ uint32_t offsetof_typ = net_offset + 4;
+ VALIDATE_NET_BYTES(13);
+ VALIDATE_NET_UTF8(LAST_U16LE());
+ uint32_t offsetof_end = net_offset + 4;
+ VALIDATE_NET_BYTES(4);
+ if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Tunlinkat->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, offsetof_end - offsetof_size)));
+ if (GET_U8LE(offsetof_typ) != 76)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Tunlinkat->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 76)));
+ return ERROR_NEW_VAL(size_t, host_size);
+}
+
+static size_t_or_error validate_Runlinkat([[maybe_unused]] struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
+ uint32_t net_offset = 0;
+ size_t host_size = sizeof(struct lib9p_msg_Runlinkat);
+ uint32_t offsetof_size = net_offset + 0;
+ uint32_t offsetof_typ = net_offset + 4;
+ uint32_t offsetof_end = net_offset + 7;
+ VALIDATE_NET_BYTES(7);
+ if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Runlinkat->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, offsetof_end - offsetof_size)));
+ if (GET_U8LE(offsetof_typ) != 77)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Runlinkat->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 77)));
+ return ERROR_NEW_VAL(size_t, host_size);
+}
+
+#endif /* CONFIG_9P_ENABLE_9P2000_L */
+#if CONFIG_9P_ENABLE_9P2000_e
+static size_t_or_error validate_Tsession([[maybe_unused]] struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
+ uint32_t net_offset = 0;
+ size_t host_size = sizeof(struct lib9p_msg_Tsession);
+ uint32_t offsetof_size = net_offset + 0;
+ uint32_t offsetof_typ = net_offset + 4;
+ uint32_t offsetof_end = net_offset + 15;
+ VALIDATE_NET_BYTES(15);
+ if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Tsession->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, offsetof_end - offsetof_size)));
+ if (GET_U8LE(offsetof_typ) != 150)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Tsession->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 150)));
+ return ERROR_NEW_VAL(size_t, host_size);
+}
+
+static size_t_or_error validate_Rsession([[maybe_unused]] struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
+ uint32_t net_offset = 0;
+ size_t host_size = sizeof(struct lib9p_msg_Rsession);
+ uint32_t offsetof_size = net_offset + 0;
+ uint32_t offsetof_typ = net_offset + 4;
+ uint32_t offsetof_end = net_offset + 7;
+ VALIDATE_NET_BYTES(7);
+ if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Rsession->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, offsetof_end - offsetof_size)));
+ if (GET_U8LE(offsetof_typ) != 151)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Rsession->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 151)));
+ return ERROR_NEW_VAL(size_t, host_size);
+}
+
+static size_t_or_error validate_Tsread([[maybe_unused]] struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
+ uint32_t net_offset = 0;
+ size_t host_size = sizeof(struct lib9p_msg_Tsread);
+ uint32_t offsetof_size = net_offset + 0;
+ uint32_t offsetof_typ = net_offset + 4;
+ VALIDATE_NET_BYTES(13);
+ for (uint16_t i = 0, cnt = LAST_U16LE(); i < cnt; i++) {
+ RESERVE_HOST_BYTES(sizeof(struct lib9p_s));
+ VALIDATE_NET_BYTES(2);
+ VALIDATE_NET_UTF8(LAST_U16LE());
+ }
+ uint32_t offsetof_end = net_offset + 0;
+ if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Tsread->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, offsetof_end - offsetof_size)));
+ if (GET_U8LE(offsetof_typ) != 152)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Tsread->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 152)));
+ return ERROR_NEW_VAL(size_t, host_size);
+}
+
+static size_t_or_error validate_Rsread([[maybe_unused]] struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
+ uint32_t net_offset = 0;
+ size_t host_size = sizeof(struct lib9p_msg_Rsread);
+ uint32_t offsetof_size = net_offset + 0;
+ uint32_t offsetof_typ = net_offset + 4;
+ VALIDATE_NET_BYTES(11);
+ VALIDATE_NET_BYTES(LAST_U32LE());
+ uint32_t offsetof_end = net_offset + 0;
+ if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Rsread->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, offsetof_end - offsetof_size)));
+ if (GET_U8LE(offsetof_typ) != 153)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Rsread->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 153)));
+ return ERROR_NEW_VAL(size_t, host_size);
+}
+
+static size_t_or_error validate_Tswrite([[maybe_unused]] struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
+ uint32_t net_offset = 0;
+ size_t host_size = sizeof(struct lib9p_msg_Tswrite);
+ uint32_t offsetof_size = net_offset + 0;
+ uint32_t offsetof_typ = net_offset + 4;
+ VALIDATE_NET_BYTES(13);
+ for (uint16_t i = 0, cnt = LAST_U16LE(); i < cnt; i++) {
+ RESERVE_HOST_BYTES(sizeof(struct lib9p_s));
+ VALIDATE_NET_BYTES(2);
+ VALIDATE_NET_UTF8(LAST_U16LE());
+ }
+ VALIDATE_NET_BYTES(4);
+ VALIDATE_NET_BYTES(LAST_U32LE());
+ uint32_t offsetof_end = net_offset + 0;
+ if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Tswrite->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, offsetof_end - offsetof_size)));
+ if (GET_U8LE(offsetof_typ) != 154)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Tswrite->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 154)));
+ return ERROR_NEW_VAL(size_t, host_size);
+}
+
+static size_t_or_error validate_Rswrite([[maybe_unused]] struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) {
+ uint32_t net_offset = 0;
+ size_t host_size = sizeof(struct lib9p_msg_Rswrite);
+ uint32_t offsetof_size = net_offset + 0;
+ uint32_t offsetof_typ = net_offset + 4;
+ uint32_t offsetof_end = net_offset + 11;
+ VALIDATE_NET_BYTES(11);
+ if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Rswrite->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, offsetof_end - offsetof_size)));
+ if (GET_U8LE(offsetof_typ) != 155)
+ return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Rswrite->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 155)));
+ return ERROR_NEW_VAL(size_t, host_size);
+}
+#endif /* CONFIG_9P_ENABLE_9P2000_e */
+
+/* unmarshal_* ****************************************************************/
+
+#define UNMARSHAL_BYTES(ctx, data_lvalue, len) \
+ data_lvalue = (char *)&net_bytes[net_offset]; \
+ net_offset += len;
+#define UNMARSHAL_U8LE(ctx, val_lvalue) \
+ val_lvalue = net_bytes[net_offset]; \
+ net_offset += 1;
+#define UNMARSHAL_U16LE(ctx, val_lvalue) \
+ val_lvalue = uint16le_decode(&net_bytes[net_offset]); \
+ net_offset += 2;
+#define UNMARSHAL_U32LE(ctx, val_lvalue) \
+ val_lvalue = uint32le_decode(&net_bytes[net_offset]); \
+ net_offset += 4;
+#define UNMARSHAL_U64LE(ctx, val_lvalue) \
+ val_lvalue = uint64le_decode(&net_bytes[net_offset]); \
+ net_offset += 8;
+
+#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u
+static void unmarshal_stat([[maybe_unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
+ struct lib9p_stat *out = out_buf;
+ [[maybe_unused]] void *extra = &out[1];
+ uint32_t net_offset = 0;
+ net_offset += 2;
+ UNMARSHAL_U16LE(ctx, out->fstype);
+ UNMARSHAL_U32LE(ctx, out->fsdev);
+ UNMARSHAL_U8LE(ctx, out->qid.type);
+ UNMARSHAL_U32LE(ctx, out->qid.vers);
+ UNMARSHAL_U64LE(ctx, out->qid.path);
+ UNMARSHAL_U32LE(ctx, out->mode);
+ UNMARSHAL_U32LE(ctx, out->atime);
+ UNMARSHAL_U32LE(ctx, out->mtime);
+ UNMARSHAL_U64LE(ctx, out->length);
+ UNMARSHAL_U16LE(ctx, out->name.len);
+ UNMARSHAL_BYTES(ctx, out->name.utf8, out->name.len);
+ UNMARSHAL_U16LE(ctx, out->owner_uname.len);
+ UNMARSHAL_BYTES(ctx, out->owner_uname.utf8, out->owner_uname.len);
+ UNMARSHAL_U16LE(ctx, out->owner_gname.len);
+ UNMARSHAL_BYTES(ctx, out->owner_gname.utf8, out->owner_gname.len);
+ UNMARSHAL_U16LE(ctx, out->last_modifier_uname.len);
+ UNMARSHAL_BYTES(ctx, out->last_modifier_uname.utf8, out->last_modifier_uname.len);
+#if CONFIG_9P_ENABLE_9P2000_u
+ if (is_ver(ctx, 9P2000_u)) {
+ UNMARSHAL_U16LE(ctx, out->extension.len);
+ UNMARSHAL_BYTES(ctx, out->extension.utf8, out->extension.len);
+ UNMARSHAL_U32LE(ctx, out->owner_unum);
+ UNMARSHAL_U32LE(ctx, out->owner_gnum);
+ UNMARSHAL_U32LE(ctx, out->last_modifier_unum);
+ }
+#endif /* CONFIG_9P_ENABLE_9P2000_u */
+}
+
+#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */
+#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u || CONFIG_9P_ENABLE_uninitialized
+static void unmarshal_Tversion([[maybe_unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
+ struct lib9p_msg_Tversion *out = out_buf;
+ [[maybe_unused]] void *extra = &out[1];
+ uint32_t net_offset = 0;
+ net_offset += 4;
+ net_offset += 1;
+ UNMARSHAL_U16LE(ctx, out->tag);
+ UNMARSHAL_U32LE(ctx, out->max_msg_size);
+ UNMARSHAL_U16LE(ctx, out->version.len);
+ UNMARSHAL_BYTES(ctx, out->version.utf8, out->version.len);
+}
+
+static void unmarshal_Rversion([[maybe_unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
+ struct lib9p_msg_Rversion *out = out_buf;
+ [[maybe_unused]] void *extra = &out[1];
+ uint32_t net_offset = 0;
+ net_offset += 4;
+ net_offset += 1;
+ UNMARSHAL_U16LE(ctx, out->tag);
+ UNMARSHAL_U32LE(ctx, out->max_msg_size);
+ UNMARSHAL_U16LE(ctx, out->version.len);
+ UNMARSHAL_BYTES(ctx, out->version.utf8, out->version.len);
+}
+
+#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u || CONFIG_9P_ENABLE_uninitialized */
+#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u
+static void unmarshal_Tauth([[maybe_unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
+ struct lib9p_msg_Tauth *out = out_buf;
+ [[maybe_unused]] void *extra = &out[1];
+ uint32_t net_offset = 0;
+ net_offset += 4;
+ net_offset += 1;
+ UNMARSHAL_U16LE(ctx, out->tag);
+ UNMARSHAL_U32LE(ctx, out->afid);
+ UNMARSHAL_U16LE(ctx, out->uname.len);
+ UNMARSHAL_BYTES(ctx, out->uname.utf8, out->uname.len);
+ UNMARSHAL_U16LE(ctx, out->aname.len);
+ UNMARSHAL_BYTES(ctx, out->aname.utf8, out->aname.len);
+#if CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_u
+ if (( is_ver(ctx, 9P2000_L) || is_ver(ctx, 9P2000_u) )) {
+ UNMARSHAL_U32LE(ctx, out->unum);
+ }
+#endif /* CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_u */
+}
+
+static void unmarshal_Rauth([[maybe_unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
+ struct lib9p_msg_Rauth *out = out_buf;
+ [[maybe_unused]] void *extra = &out[1];
+ uint32_t net_offset = 0;
+ net_offset += 4;
+ net_offset += 1;
+ UNMARSHAL_U16LE(ctx, out->tag);
+ UNMARSHAL_U8LE(ctx, out->aqid.type);
+ UNMARSHAL_U32LE(ctx, out->aqid.vers);
+ UNMARSHAL_U64LE(ctx, out->aqid.path);
+}
+
+static void unmarshal_Tattach([[maybe_unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
+ struct lib9p_msg_Tattach *out = out_buf;
+ [[maybe_unused]] void *extra = &out[1];
+ uint32_t net_offset = 0;
+ net_offset += 4;
+ net_offset += 1;
+ UNMARSHAL_U16LE(ctx, out->tag);
+ UNMARSHAL_U32LE(ctx, out->fid);
+ UNMARSHAL_U32LE(ctx, out->afid);
+ UNMARSHAL_U16LE(ctx, out->uname.len);
+ UNMARSHAL_BYTES(ctx, out->uname.utf8, out->uname.len);
+ UNMARSHAL_U16LE(ctx, out->aname.len);
+ UNMARSHAL_BYTES(ctx, out->aname.utf8, out->aname.len);
+#if CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_u
+ if (( is_ver(ctx, 9P2000_L) || is_ver(ctx, 9P2000_u) )) {
+ UNMARSHAL_U32LE(ctx, out->unum);
+ }
+#endif /* CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_u */
+}
+
+static void unmarshal_Rattach([[maybe_unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
+ struct lib9p_msg_Rattach *out = out_buf;
+ [[maybe_unused]] void *extra = &out[1];
+ uint32_t net_offset = 0;
+ net_offset += 4;
+ net_offset += 1;
+ UNMARSHAL_U16LE(ctx, out->tag);
+ UNMARSHAL_U8LE(ctx, out->qid.type);
+ UNMARSHAL_U32LE(ctx, out->qid.vers);
+ UNMARSHAL_U64LE(ctx, out->qid.path);
+}
+
+#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */
+#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u || CONFIG_9P_ENABLE_uninitialized
+static void unmarshal_Rerror([[maybe_unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
+ struct lib9p_msg_Rerror *out = out_buf;
+ [[maybe_unused]] void *extra = &out[1];
+ uint32_t net_offset = 0;
+ net_offset += 4;
+ net_offset += 1;
+ UNMARSHAL_U16LE(ctx, out->tag);
+ UNMARSHAL_U16LE(ctx, out->errstr.len);
+ UNMARSHAL_BYTES(ctx, out->errstr.utf8, out->errstr.len);
+#if CONFIG_9P_ENABLE_9P2000_u
+ if (is_ver(ctx, 9P2000_u)) {
+ UNMARSHAL_U32LE(ctx, out->errnum);
+ }
+#endif /* CONFIG_9P_ENABLE_9P2000_u */
+}
+
+#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u || CONFIG_9P_ENABLE_uninitialized */
+#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u
+static void unmarshal_Tflush([[maybe_unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
+ struct lib9p_msg_Tflush *out = out_buf;
+ [[maybe_unused]] void *extra = &out[1];
+ uint32_t net_offset = 0;
+ net_offset += 4;
+ net_offset += 1;
+ UNMARSHAL_U16LE(ctx, out->tag);
+ UNMARSHAL_U16LE(ctx, out->oldtag);
+}
+
+static void unmarshal_Rflush([[maybe_unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
+ struct lib9p_msg_Rflush *out = out_buf;
+ [[maybe_unused]] void *extra = &out[1];
+ uint32_t net_offset = 0;
+ net_offset += 4;
+ net_offset += 1;
+ UNMARSHAL_U16LE(ctx, out->tag);
+}
+
+static void unmarshal_Twalk([[maybe_unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
+ struct lib9p_msg_Twalk *out = out_buf;
+ [[maybe_unused]] void *extra = &out[1];
+ uint32_t net_offset = 0;
+ net_offset += 4;
+ net_offset += 1;
+ UNMARSHAL_U16LE(ctx, out->tag);
+ UNMARSHAL_U32LE(ctx, out->fid);
+ UNMARSHAL_U32LE(ctx, out->newfid);
+ UNMARSHAL_U16LE(ctx, out->nwname);
+ out->wname = extra;
+ extra += sizeof(out->wname[0]) * out->nwname;
+ for (uint16_t i = 0; i < out->nwname; i++) {
+ UNMARSHAL_U16LE(ctx, out->wname[i].len);
+ UNMARSHAL_BYTES(ctx, out->wname[i].utf8, out->wname[i].len);
+ }
+}
+
+static void unmarshal_Rwalk([[maybe_unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
+ struct lib9p_msg_Rwalk *out = out_buf;
+ [[maybe_unused]] void *extra = &out[1];
+ uint32_t net_offset = 0;
+ net_offset += 4;
+ net_offset += 1;
+ UNMARSHAL_U16LE(ctx, out->tag);
+ UNMARSHAL_U16LE(ctx, out->nwqid);
+ out->wqid = extra;
+ extra += sizeof(out->wqid[0]) * out->nwqid;
+ for (uint16_t i = 0; i < out->nwqid; i++) {
+ UNMARSHAL_U8LE(ctx, out->wqid[i].type);
+ UNMARSHAL_U32LE(ctx, out->wqid[i].vers);
+ UNMARSHAL_U64LE(ctx, out->wqid[i].path);
+ }
+}
+
+#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */
+#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u
+static void unmarshal_Topen([[maybe_unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
+ struct lib9p_msg_Topen *out = out_buf;
+ [[maybe_unused]] void *extra = &out[1];
+ uint32_t net_offset = 0;
+ net_offset += 4;
+ net_offset += 1;
+ UNMARSHAL_U16LE(ctx, out->tag);
+ UNMARSHAL_U32LE(ctx, out->fid);
+ UNMARSHAL_U8LE(ctx, out->mode);
+}
+
+static void unmarshal_Ropen([[maybe_unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
+ struct lib9p_msg_Ropen *out = out_buf;
+ [[maybe_unused]] void *extra = &out[1];
+ uint32_t net_offset = 0;
+ net_offset += 4;
+ net_offset += 1;
+ UNMARSHAL_U16LE(ctx, out->tag);
+ UNMARSHAL_U8LE(ctx, out->qid.type);
+ UNMARSHAL_U32LE(ctx, out->qid.vers);
+ UNMARSHAL_U64LE(ctx, out->qid.path);
+ UNMARSHAL_U32LE(ctx, out->iounit);
+}
+
+static void unmarshal_Tcreate([[maybe_unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
+ struct lib9p_msg_Tcreate *out = out_buf;
+ [[maybe_unused]] void *extra = &out[1];
+ uint32_t net_offset = 0;
+ net_offset += 4;
+ net_offset += 1;
+ UNMARSHAL_U16LE(ctx, out->tag);
+ UNMARSHAL_U32LE(ctx, out->fid);
+ UNMARSHAL_U16LE(ctx, out->name.len);
+ UNMARSHAL_BYTES(ctx, out->name.utf8, out->name.len);
+ UNMARSHAL_U32LE(ctx, out->perm);
+ UNMARSHAL_U8LE(ctx, out->mode);
+}
+
+static void unmarshal_Rcreate([[maybe_unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
+ struct lib9p_msg_Rcreate *out = out_buf;
+ [[maybe_unused]] void *extra = &out[1];
+ uint32_t net_offset = 0;
+ net_offset += 4;
+ net_offset += 1;
+ UNMARSHAL_U16LE(ctx, out->tag);
+ UNMARSHAL_U8LE(ctx, out->qid.type);
+ UNMARSHAL_U32LE(ctx, out->qid.vers);
+ UNMARSHAL_U64LE(ctx, out->qid.path);
+ UNMARSHAL_U32LE(ctx, out->iounit);
+}
+
+#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */
+#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u
+static void unmarshal_Tread([[maybe_unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
+ struct lib9p_msg_Tread *out = out_buf;
+ [[maybe_unused]] void *extra = &out[1];
+ uint32_t net_offset = 0;
+ net_offset += 4;
+ net_offset += 1;
+ UNMARSHAL_U16LE(ctx, out->tag);
+ UNMARSHAL_U32LE(ctx, out->fid);
+ UNMARSHAL_U64LE(ctx, out->offset);
+ UNMARSHAL_U32LE(ctx, out->count);
+}
+
+static void unmarshal_Rread([[maybe_unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
+ struct lib9p_msg_Rread *out = out_buf;
+ [[maybe_unused]] void *extra = &out[1];
+ uint32_t net_offset = 0;
+ net_offset += 4;
+ net_offset += 1;
+ UNMARSHAL_U16LE(ctx, out->tag);
+ UNMARSHAL_U32LE(ctx, out->count);
+ UNMARSHAL_BYTES(ctx, out->data, out->count);
+}
+
+static void unmarshal_Twrite([[maybe_unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
+ struct lib9p_msg_Twrite *out = out_buf;
+ [[maybe_unused]] void *extra = &out[1];
+ uint32_t net_offset = 0;
+ net_offset += 4;
+ net_offset += 1;
+ UNMARSHAL_U16LE(ctx, out->tag);
+ UNMARSHAL_U32LE(ctx, out->fid);
+ UNMARSHAL_U64LE(ctx, out->offset);
+ UNMARSHAL_U32LE(ctx, out->count);
+ UNMARSHAL_BYTES(ctx, out->data, out->count);
+}
+
+static void unmarshal_Rwrite([[maybe_unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
+ struct lib9p_msg_Rwrite *out = out_buf;
+ [[maybe_unused]] void *extra = &out[1];
+ uint32_t net_offset = 0;
+ net_offset += 4;
+ net_offset += 1;
+ UNMARSHAL_U16LE(ctx, out->tag);
+ UNMARSHAL_U32LE(ctx, out->count);
+}
+
+static void unmarshal_Tclunk([[maybe_unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
+ struct lib9p_msg_Tclunk *out = out_buf;
+ [[maybe_unused]] void *extra = &out[1];
+ uint32_t net_offset = 0;
+ net_offset += 4;
+ net_offset += 1;
+ UNMARSHAL_U16LE(ctx, out->tag);
+ UNMARSHAL_U32LE(ctx, out->fid);
+}
+
+static void unmarshal_Rclunk([[maybe_unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
+ struct lib9p_msg_Rclunk *out = out_buf;
+ [[maybe_unused]] void *extra = &out[1];
+ uint32_t net_offset = 0;
+ net_offset += 4;
+ net_offset += 1;
+ UNMARSHAL_U16LE(ctx, out->tag);
+}
+
+static void unmarshal_Tremove([[maybe_unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
+ struct lib9p_msg_Tremove *out = out_buf;
+ [[maybe_unused]] void *extra = &out[1];
+ uint32_t net_offset = 0;
+ net_offset += 4;
+ net_offset += 1;
+ UNMARSHAL_U16LE(ctx, out->tag);
+ UNMARSHAL_U32LE(ctx, out->fid);
+}
+
+static void unmarshal_Rremove([[maybe_unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
+ struct lib9p_msg_Rremove *out = out_buf;
+ [[maybe_unused]] void *extra = &out[1];
+ uint32_t net_offset = 0;
+ net_offset += 4;
+ net_offset += 1;
+ UNMARSHAL_U16LE(ctx, out->tag);
+}
+
+#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */
+#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u
+static void unmarshal_Tstat([[maybe_unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
+ struct lib9p_msg_Tstat *out = out_buf;
+ [[maybe_unused]] void *extra = &out[1];
+ uint32_t net_offset = 0;
+ net_offset += 4;
+ net_offset += 1;
+ UNMARSHAL_U16LE(ctx, out->tag);
+ UNMARSHAL_U32LE(ctx, out->fid);
+}
+
+static void unmarshal_Rstat([[maybe_unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
+ struct lib9p_msg_Rstat *out = out_buf;
+ [[maybe_unused]] void *extra = &out[1];
+ uint32_t net_offset = 0;
+ net_offset += 4;
+ net_offset += 1;
+ UNMARSHAL_U16LE(ctx, out->tag);
+ net_offset += 2;
+ net_offset += 2;
+ UNMARSHAL_U16LE(ctx, out->stat.fstype);
+ UNMARSHAL_U32LE(ctx, out->stat.fsdev);
+ UNMARSHAL_U8LE(ctx, out->stat.qid.type);
+ UNMARSHAL_U32LE(ctx, out->stat.qid.vers);
+ UNMARSHAL_U64LE(ctx, out->stat.qid.path);
+ UNMARSHAL_U32LE(ctx, out->stat.mode);
+ UNMARSHAL_U32LE(ctx, out->stat.atime);
+ UNMARSHAL_U32LE(ctx, out->stat.mtime);
+ UNMARSHAL_U64LE(ctx, out->stat.length);
+ UNMARSHAL_U16LE(ctx, out->stat.name.len);
+ UNMARSHAL_BYTES(ctx, out->stat.name.utf8, out->stat.name.len);
+ UNMARSHAL_U16LE(ctx, out->stat.owner_uname.len);
+ UNMARSHAL_BYTES(ctx, out->stat.owner_uname.utf8, out->stat.owner_uname.len);
+ UNMARSHAL_U16LE(ctx, out->stat.owner_gname.len);
+ UNMARSHAL_BYTES(ctx, out->stat.owner_gname.utf8, out->stat.owner_gname.len);
+ UNMARSHAL_U16LE(ctx, out->stat.last_modifier_uname.len);
+ UNMARSHAL_BYTES(ctx, out->stat.last_modifier_uname.utf8, out->stat.last_modifier_uname.len);
+#if CONFIG_9P_ENABLE_9P2000_u
+ if (is_ver(ctx, 9P2000_u)) {
+ UNMARSHAL_U16LE(ctx, out->stat.extension.len);
+ UNMARSHAL_BYTES(ctx, out->stat.extension.utf8, out->stat.extension.len);
+ UNMARSHAL_U32LE(ctx, out->stat.owner_unum);
+ UNMARSHAL_U32LE(ctx, out->stat.owner_gnum);
+ UNMARSHAL_U32LE(ctx, out->stat.last_modifier_unum);
+ }
+#endif /* CONFIG_9P_ENABLE_9P2000_u */
+}
+
+static void unmarshal_Twstat([[maybe_unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
+ struct lib9p_msg_Twstat *out = out_buf;
+ [[maybe_unused]] void *extra = &out[1];
+ uint32_t net_offset = 0;
+ net_offset += 4;
+ net_offset += 1;
+ UNMARSHAL_U16LE(ctx, out->tag);
+ UNMARSHAL_U32LE(ctx, out->fid);
+ net_offset += 2;
+ net_offset += 2;
+ UNMARSHAL_U16LE(ctx, out->stat.fstype);
+ UNMARSHAL_U32LE(ctx, out->stat.fsdev);
+ UNMARSHAL_U8LE(ctx, out->stat.qid.type);
+ UNMARSHAL_U32LE(ctx, out->stat.qid.vers);
+ UNMARSHAL_U64LE(ctx, out->stat.qid.path);
+ UNMARSHAL_U32LE(ctx, out->stat.mode);
+ UNMARSHAL_U32LE(ctx, out->stat.atime);
+ UNMARSHAL_U32LE(ctx, out->stat.mtime);
+ UNMARSHAL_U64LE(ctx, out->stat.length);
+ UNMARSHAL_U16LE(ctx, out->stat.name.len);
+ UNMARSHAL_BYTES(ctx, out->stat.name.utf8, out->stat.name.len);
+ UNMARSHAL_U16LE(ctx, out->stat.owner_uname.len);
+ UNMARSHAL_BYTES(ctx, out->stat.owner_uname.utf8, out->stat.owner_uname.len);
+ UNMARSHAL_U16LE(ctx, out->stat.owner_gname.len);
+ UNMARSHAL_BYTES(ctx, out->stat.owner_gname.utf8, out->stat.owner_gname.len);
+ UNMARSHAL_U16LE(ctx, out->stat.last_modifier_uname.len);
+ UNMARSHAL_BYTES(ctx, out->stat.last_modifier_uname.utf8, out->stat.last_modifier_uname.len);
+#if CONFIG_9P_ENABLE_9P2000_u
+ if (is_ver(ctx, 9P2000_u)) {
+ UNMARSHAL_U16LE(ctx, out->stat.extension.len);
+ UNMARSHAL_BYTES(ctx, out->stat.extension.utf8, out->stat.extension.len);
+ UNMARSHAL_U32LE(ctx, out->stat.owner_unum);
+ UNMARSHAL_U32LE(ctx, out->stat.owner_gnum);
+ UNMARSHAL_U32LE(ctx, out->stat.last_modifier_unum);
+ }
+#endif /* CONFIG_9P_ENABLE_9P2000_u */
+}
+
+static void unmarshal_Rwstat([[maybe_unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
+ struct lib9p_msg_Rwstat *out = out_buf;
+ [[maybe_unused]] void *extra = &out[1];
+ uint32_t net_offset = 0;
+ net_offset += 4;
+ net_offset += 1;
+ UNMARSHAL_U16LE(ctx, out->tag);
+}
+
+#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */
+#if CONFIG_9P_ENABLE_9P2000_p9p
+static void unmarshal_Topenfd([[maybe_unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
+ struct lib9p_msg_Topenfd *out = out_buf;
+ [[maybe_unused]] void *extra = &out[1];
+ uint32_t net_offset = 0;
+ net_offset += 4;
+ net_offset += 1;
+ UNMARSHAL_U16LE(ctx, out->tag);
+ UNMARSHAL_U32LE(ctx, out->fid);
+ UNMARSHAL_U8LE(ctx, out->mode);
+}
+
+static void unmarshal_Ropenfd([[maybe_unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
+ struct lib9p_msg_Ropenfd *out = out_buf;
+ [[maybe_unused]] void *extra = &out[1];
+ uint32_t net_offset = 0;
+ net_offset += 4;
+ net_offset += 1;
+ UNMARSHAL_U16LE(ctx, out->tag);
+ UNMARSHAL_U8LE(ctx, out->qid.type);
+ UNMARSHAL_U32LE(ctx, out->qid.vers);
+ UNMARSHAL_U64LE(ctx, out->qid.path);
+ UNMARSHAL_U32LE(ctx, out->iounit);
+ UNMARSHAL_U32LE(ctx, out->unixfd);
+}
+
+#endif /* CONFIG_9P_ENABLE_9P2000_p9p */
+#if CONFIG_9P_ENABLE_9P2000_L
+static void unmarshal_Rlerror([[maybe_unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
+ struct lib9p_msg_Rlerror *out = out_buf;
+ [[maybe_unused]] void *extra = &out[1];
+ uint32_t net_offset = 0;
+ net_offset += 4;
+ net_offset += 1;
+ UNMARSHAL_U16LE(ctx, out->tag);
+ UNMARSHAL_U32LE(ctx, out->errnum);
+}
+
+static void unmarshal_Tstatfs([[maybe_unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
+ struct lib9p_msg_Tstatfs *out = out_buf;
+ [[maybe_unused]] void *extra = &out[1];
+ uint32_t net_offset = 0;
+ net_offset += 4;
+ net_offset += 1;
+ UNMARSHAL_U16LE(ctx, out->tag);
+ UNMARSHAL_U32LE(ctx, out->fid);
+}
+
+static void unmarshal_Rstatfs([[maybe_unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
+ struct lib9p_msg_Rstatfs *out = out_buf;
+ [[maybe_unused]] void *extra = &out[1];
+ uint32_t net_offset = 0;
+ net_offset += 4;
+ net_offset += 1;
+ UNMARSHAL_U16LE(ctx, out->tag);
+ UNMARSHAL_U32LE(ctx, out->type);
+ UNMARSHAL_U32LE(ctx, out->bsize);
+ UNMARSHAL_U64LE(ctx, out->blocks);
+ UNMARSHAL_U64LE(ctx, out->bfree);
+ UNMARSHAL_U64LE(ctx, out->bavail);
+ UNMARSHAL_U64LE(ctx, out->files);
+ UNMARSHAL_U64LE(ctx, out->ffree);
+ UNMARSHAL_U64LE(ctx, out->fsid);
+ UNMARSHAL_U32LE(ctx, out->namelen);
+}
+
+static void unmarshal_Tlopen([[maybe_unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
+ struct lib9p_msg_Tlopen *out = out_buf;
+ [[maybe_unused]] void *extra = &out[1];
+ uint32_t net_offset = 0;
+ net_offset += 4;
+ net_offset += 1;
+ UNMARSHAL_U16LE(ctx, out->tag);
+ UNMARSHAL_U32LE(ctx, out->fid);
+ UNMARSHAL_U32LE(ctx, out->flags);
+}
+
+static void unmarshal_Rlopen([[maybe_unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
+ struct lib9p_msg_Rlopen *out = out_buf;
+ [[maybe_unused]] void *extra = &out[1];
+ uint32_t net_offset = 0;
+ net_offset += 4;
+ net_offset += 1;
+ UNMARSHAL_U16LE(ctx, out->tag);
+ UNMARSHAL_U8LE(ctx, out->qid.type);
+ UNMARSHAL_U32LE(ctx, out->qid.vers);
+ UNMARSHAL_U64LE(ctx, out->qid.path);
+ UNMARSHAL_U32LE(ctx, out->iounit);
+}
+
+static void unmarshal_Tlcreate([[maybe_unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
+ struct lib9p_msg_Tlcreate *out = out_buf;
+ [[maybe_unused]] void *extra = &out[1];
+ uint32_t net_offset = 0;
+ net_offset += 4;
+ net_offset += 1;
+ UNMARSHAL_U16LE(ctx, out->tag);
+ UNMARSHAL_U32LE(ctx, out->fid);
+ UNMARSHAL_U16LE(ctx, out->name.len);
+ UNMARSHAL_BYTES(ctx, out->name.utf8, out->name.len);
+ UNMARSHAL_U32LE(ctx, out->flags);
+ UNMARSHAL_U32LE(ctx, out->mode);
+ UNMARSHAL_U32LE(ctx, out->gid);
+}
+
+static void unmarshal_Rlcreate([[maybe_unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
+ struct lib9p_msg_Rlcreate *out = out_buf;
+ [[maybe_unused]] void *extra = &out[1];
+ uint32_t net_offset = 0;
+ net_offset += 4;
+ net_offset += 1;
+ UNMARSHAL_U16LE(ctx, out->tag);
+ UNMARSHAL_U8LE(ctx, out->qid.type);
+ UNMARSHAL_U32LE(ctx, out->qid.vers);
+ UNMARSHAL_U64LE(ctx, out->qid.path);
+ UNMARSHAL_U32LE(ctx, out->iounit);
+}
+
+static void unmarshal_Tsymlink([[maybe_unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
+ struct lib9p_msg_Tsymlink *out = out_buf;
+ [[maybe_unused]] void *extra = &out[1];
+ uint32_t net_offset = 0;
+ net_offset += 4;
+ net_offset += 1;
+ UNMARSHAL_U16LE(ctx, out->tag);
+ UNMARSHAL_U32LE(ctx, out->fid);
+ UNMARSHAL_U16LE(ctx, out->name.len);
+ UNMARSHAL_BYTES(ctx, out->name.utf8, out->name.len);
+ UNMARSHAL_U16LE(ctx, out->symtgt.len);
+ UNMARSHAL_BYTES(ctx, out->symtgt.utf8, out->symtgt.len);
+ UNMARSHAL_U32LE(ctx, out->gid);
+}
+
+static void unmarshal_Rsymlink([[maybe_unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
+ struct lib9p_msg_Rsymlink *out = out_buf;
+ [[maybe_unused]] void *extra = &out[1];
+ uint32_t net_offset = 0;
+ net_offset += 4;
+ net_offset += 1;
+ UNMARSHAL_U16LE(ctx, out->tag);
+ UNMARSHAL_U8LE(ctx, out->qid.type);
+ UNMARSHAL_U32LE(ctx, out->qid.vers);
+ UNMARSHAL_U64LE(ctx, out->qid.path);
+}
+
+static void unmarshal_Tmknod([[maybe_unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
+ struct lib9p_msg_Tmknod *out = out_buf;
+ [[maybe_unused]] void *extra = &out[1];
+ uint32_t net_offset = 0;
+ net_offset += 4;
+ net_offset += 1;
+ UNMARSHAL_U16LE(ctx, out->tag);
+ UNMARSHAL_U32LE(ctx, out->dfid);
+ UNMARSHAL_U16LE(ctx, out->name.len);
+ UNMARSHAL_BYTES(ctx, out->name.utf8, out->name.len);
+ UNMARSHAL_U32LE(ctx, out->mode);
+ UNMARSHAL_U32LE(ctx, out->major);
+ UNMARSHAL_U32LE(ctx, out->minor);
+ UNMARSHAL_U32LE(ctx, out->gid);
+}
+
+static void unmarshal_Rmknod([[maybe_unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
+ struct lib9p_msg_Rmknod *out = out_buf;
+ [[maybe_unused]] void *extra = &out[1];
+ uint32_t net_offset = 0;
+ net_offset += 4;
+ net_offset += 1;
+ UNMARSHAL_U16LE(ctx, out->tag);
+ UNMARSHAL_U8LE(ctx, out->qid.type);
+ UNMARSHAL_U32LE(ctx, out->qid.vers);
+ UNMARSHAL_U64LE(ctx, out->qid.path);
+}
+
+static void unmarshal_Trename([[maybe_unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
+ struct lib9p_msg_Trename *out = out_buf;
+ [[maybe_unused]] void *extra = &out[1];
+ uint32_t net_offset = 0;
+ net_offset += 4;
+ net_offset += 1;
+ UNMARSHAL_U16LE(ctx, out->tag);
+ UNMARSHAL_U32LE(ctx, out->fid);
+ UNMARSHAL_U32LE(ctx, out->dfid);
+ UNMARSHAL_U16LE(ctx, out->name.len);
+ UNMARSHAL_BYTES(ctx, out->name.utf8, out->name.len);
+}
+
+static void unmarshal_Rrename([[maybe_unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
+ struct lib9p_msg_Rrename *out = out_buf;
+ [[maybe_unused]] void *extra = &out[1];
+ uint32_t net_offset = 0;
+ net_offset += 4;
+ net_offset += 1;
+ UNMARSHAL_U16LE(ctx, out->tag);
+}
+
+static void unmarshal_Treadlink([[maybe_unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
+ struct lib9p_msg_Treadlink *out = out_buf;
+ [[maybe_unused]] void *extra = &out[1];
+ uint32_t net_offset = 0;
+ net_offset += 4;
+ net_offset += 1;
+ UNMARSHAL_U16LE(ctx, out->tag);
+ UNMARSHAL_U32LE(ctx, out->fid);
+}
+
+static void unmarshal_Rreadlink([[maybe_unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
+ struct lib9p_msg_Rreadlink *out = out_buf;
+ [[maybe_unused]] void *extra = &out[1];
+ uint32_t net_offset = 0;
+ net_offset += 4;
+ net_offset += 1;
+ UNMARSHAL_U16LE(ctx, out->tag);
+ UNMARSHAL_U16LE(ctx, out->target.len);
+ UNMARSHAL_BYTES(ctx, out->target.utf8, out->target.len);
+}
+
+static void unmarshal_Tgetattr([[maybe_unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
+ struct lib9p_msg_Tgetattr *out = out_buf;
+ [[maybe_unused]] void *extra = &out[1];
+ uint32_t net_offset = 0;
+ net_offset += 4;
+ net_offset += 1;
+ UNMARSHAL_U16LE(ctx, out->tag);
+ UNMARSHAL_U32LE(ctx, out->fid);
+ UNMARSHAL_U64LE(ctx, out->request_mask);
+}
+
+static void unmarshal_Rgetattr([[maybe_unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
+ struct lib9p_msg_Rgetattr *out = out_buf;
+ [[maybe_unused]] void *extra = &out[1];
+ uint32_t net_offset = 0;
+ net_offset += 4;
+ net_offset += 1;
+ UNMARSHAL_U16LE(ctx, out->tag);
+ UNMARSHAL_U64LE(ctx, out->valid);
+ UNMARSHAL_U8LE(ctx, out->qid.type);
+ UNMARSHAL_U32LE(ctx, out->qid.vers);
+ UNMARSHAL_U64LE(ctx, out->qid.path);
+ UNMARSHAL_U32LE(ctx, out->mode);
+ UNMARSHAL_U32LE(ctx, out->uid);
+ UNMARSHAL_U32LE(ctx, out->gid);
+ UNMARSHAL_U64LE(ctx, out->nlink);
+ UNMARSHAL_U64LE(ctx, out->rdev);
+ UNMARSHAL_U64LE(ctx, out->filesize);
+ UNMARSHAL_U64LE(ctx, out->blksize);
+ UNMARSHAL_U64LE(ctx, out->blocks);
+ UNMARSHAL_U64LE(ctx, out->atime_sec);
+ UNMARSHAL_U64LE(ctx, out->atime_nsec);
+ UNMARSHAL_U64LE(ctx, out->mtime_sec);
+ UNMARSHAL_U64LE(ctx, out->mtime_nsec);
+ UNMARSHAL_U64LE(ctx, out->ctime_sec);
+ UNMARSHAL_U64LE(ctx, out->ctime_nsec);
+ UNMARSHAL_U64LE(ctx, out->btime_sec);
+ UNMARSHAL_U64LE(ctx, out->btime_nsec);
+ UNMARSHAL_U64LE(ctx, out->gen);
+ UNMARSHAL_U64LE(ctx, out->data_version);
+}
+
+static void unmarshal_Tsetattr([[maybe_unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
+ struct lib9p_msg_Tsetattr *out = out_buf;
+ [[maybe_unused]] void *extra = &out[1];
+ uint32_t net_offset = 0;
+ net_offset += 4;
+ net_offset += 1;
+ UNMARSHAL_U16LE(ctx, out->tag);
+ UNMARSHAL_U32LE(ctx, out->fid);
+ UNMARSHAL_U32LE(ctx, out->valid);
+ UNMARSHAL_U32LE(ctx, out->mode);
+ UNMARSHAL_U32LE(ctx, out->uid);
+ UNMARSHAL_U32LE(ctx, out->gid);
+ UNMARSHAL_U64LE(ctx, out->filesize);
+ UNMARSHAL_U64LE(ctx, out->atime_sec);
+ UNMARSHAL_U64LE(ctx, out->atime_nsec);
+ UNMARSHAL_U64LE(ctx, out->mtime_sec);
+ UNMARSHAL_U64LE(ctx, out->mtime_nsec);
+}
+
+static void unmarshal_Rsetattr([[maybe_unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
+ struct lib9p_msg_Rsetattr *out = out_buf;
+ [[maybe_unused]] void *extra = &out[1];
+ uint32_t net_offset = 0;
+ net_offset += 4;
+ net_offset += 1;
+ UNMARSHAL_U16LE(ctx, out->tag);
+}
+
+static void unmarshal_Txattrwalk([[maybe_unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
+ struct lib9p_msg_Txattrwalk *out = out_buf;
+ [[maybe_unused]] void *extra = &out[1];
+ uint32_t net_offset = 0;
+ net_offset += 4;
+ net_offset += 1;
+ UNMARSHAL_U16LE(ctx, out->tag);
+ UNMARSHAL_U32LE(ctx, out->fid);
+ UNMARSHAL_U32LE(ctx, out->newfid);
+ UNMARSHAL_U16LE(ctx, out->name.len);
+ UNMARSHAL_BYTES(ctx, out->name.utf8, out->name.len);
+}
+
+static void unmarshal_Rxattrwalk([[maybe_unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
+ struct lib9p_msg_Rxattrwalk *out = out_buf;
+ [[maybe_unused]] void *extra = &out[1];
+ uint32_t net_offset = 0;
+ net_offset += 4;
+ net_offset += 1;
+ UNMARSHAL_U16LE(ctx, out->tag);
+ UNMARSHAL_U64LE(ctx, out->attr_size);
+}
+
+static void unmarshal_Txattrcreate([[maybe_unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
+ struct lib9p_msg_Txattrcreate *out = out_buf;
+ [[maybe_unused]] void *extra = &out[1];
+ uint32_t net_offset = 0;
+ net_offset += 4;
+ net_offset += 1;
+ UNMARSHAL_U16LE(ctx, out->tag);
+ UNMARSHAL_U32LE(ctx, out->fid);
+ UNMARSHAL_U16LE(ctx, out->name.len);
+ UNMARSHAL_BYTES(ctx, out->name.utf8, out->name.len);
+ UNMARSHAL_U64LE(ctx, out->attr_size);
+ UNMARSHAL_U32LE(ctx, out->flags);
+}
+
+static void unmarshal_Rxattrcreate([[maybe_unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
+ struct lib9p_msg_Rxattrcreate *out = out_buf;
+ [[maybe_unused]] void *extra = &out[1];
+ uint32_t net_offset = 0;
+ net_offset += 4;
+ net_offset += 1;
+ UNMARSHAL_U16LE(ctx, out->tag);
+}
+
+static void unmarshal_Treaddir([[maybe_unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
+ struct lib9p_msg_Treaddir *out = out_buf;
+ [[maybe_unused]] void *extra = &out[1];
+ uint32_t net_offset = 0;
+ net_offset += 4;
+ net_offset += 1;
+ UNMARSHAL_U16LE(ctx, out->tag);
+ UNMARSHAL_U32LE(ctx, out->fid);
+ UNMARSHAL_U64LE(ctx, out->offset);
+ UNMARSHAL_U32LE(ctx, out->count);
+}
+
+static void unmarshal_Rreaddir([[maybe_unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
+ struct lib9p_msg_Rreaddir *out = out_buf;
+ [[maybe_unused]] void *extra = &out[1];
+ uint32_t net_offset = 0;
+ net_offset += 4;
+ net_offset += 1;
+ UNMARSHAL_U16LE(ctx, out->tag);
+ UNMARSHAL_U32LE(ctx, out->count);
+ UNMARSHAL_BYTES(ctx, out->data, out->count);
+}
+
+static void unmarshal_Tfsync([[maybe_unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
+ struct lib9p_msg_Tfsync *out = out_buf;
+ [[maybe_unused]] void *extra = &out[1];
+ uint32_t net_offset = 0;
+ net_offset += 4;
+ net_offset += 1;
+ UNMARSHAL_U16LE(ctx, out->tag);
+ UNMARSHAL_U32LE(ctx, out->fid);
+ UNMARSHAL_U32LE(ctx, out->datasync);
+}
+
+static void unmarshal_Rfsync([[maybe_unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
+ struct lib9p_msg_Rfsync *out = out_buf;
+ [[maybe_unused]] void *extra = &out[1];
+ uint32_t net_offset = 0;
+ net_offset += 4;
+ net_offset += 1;
+ UNMARSHAL_U16LE(ctx, out->tag);
+}
+
+static void unmarshal_Tlock([[maybe_unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
+ struct lib9p_msg_Tlock *out = out_buf;
+ [[maybe_unused]] void *extra = &out[1];
+ uint32_t net_offset = 0;
+ net_offset += 4;
+ net_offset += 1;
+ UNMARSHAL_U16LE(ctx, out->tag);
+ UNMARSHAL_U32LE(ctx, out->fid);
+ UNMARSHAL_U8LE(ctx, out->type);
+ UNMARSHAL_U32LE(ctx, out->flags);
+ UNMARSHAL_U64LE(ctx, out->start);
+ UNMARSHAL_U64LE(ctx, out->length);
+ UNMARSHAL_U32LE(ctx, out->proc_id);
+ UNMARSHAL_U16LE(ctx, out->client_id.len);
+ UNMARSHAL_BYTES(ctx, out->client_id.utf8, out->client_id.len);
+}
+
+static void unmarshal_Rlock([[maybe_unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
+ struct lib9p_msg_Rlock *out = out_buf;
+ [[maybe_unused]] void *extra = &out[1];
+ uint32_t net_offset = 0;
+ net_offset += 4;
+ net_offset += 1;
+ UNMARSHAL_U16LE(ctx, out->tag);
+ UNMARSHAL_U8LE(ctx, out->status);
+}
+
+static void unmarshal_Tgetlock([[maybe_unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
+ struct lib9p_msg_Tgetlock *out = out_buf;
+ [[maybe_unused]] void *extra = &out[1];
+ uint32_t net_offset = 0;
+ net_offset += 4;
+ net_offset += 1;
+ UNMARSHAL_U16LE(ctx, out->tag);
+ UNMARSHAL_U32LE(ctx, out->fid);
+ UNMARSHAL_U8LE(ctx, out->type);
+ UNMARSHAL_U64LE(ctx, out->start);
+ UNMARSHAL_U64LE(ctx, out->length);
+ UNMARSHAL_U32LE(ctx, out->proc_id);
+ UNMARSHAL_U16LE(ctx, out->client_id.len);
+ UNMARSHAL_BYTES(ctx, out->client_id.utf8, out->client_id.len);
+}
+
+static void unmarshal_Rgetlock([[maybe_unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
+ struct lib9p_msg_Rgetlock *out = out_buf;
+ [[maybe_unused]] void *extra = &out[1];
+ uint32_t net_offset = 0;
+ net_offset += 4;
+ net_offset += 1;
+ UNMARSHAL_U16LE(ctx, out->tag);
+ UNMARSHAL_U8LE(ctx, out->type);
+ UNMARSHAL_U64LE(ctx, out->start);
+ UNMARSHAL_U64LE(ctx, out->length);
+ UNMARSHAL_U32LE(ctx, out->proc_id);
+ UNMARSHAL_U16LE(ctx, out->client_id.len);
+ UNMARSHAL_BYTES(ctx, out->client_id.utf8, out->client_id.len);
+}
+
+static void unmarshal_Tlink([[maybe_unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
+ struct lib9p_msg_Tlink *out = out_buf;
+ [[maybe_unused]] void *extra = &out[1];
+ uint32_t net_offset = 0;
+ net_offset += 4;
+ net_offset += 1;
+ UNMARSHAL_U16LE(ctx, out->tag);
+ UNMARSHAL_U32LE(ctx, out->dfid);
+ UNMARSHAL_U32LE(ctx, out->fid);
+ UNMARSHAL_U16LE(ctx, out->name.len);
+ UNMARSHAL_BYTES(ctx, out->name.utf8, out->name.len);
+}
+
+static void unmarshal_Rlink([[maybe_unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
+ struct lib9p_msg_Rlink *out = out_buf;
+ [[maybe_unused]] void *extra = &out[1];
+ uint32_t net_offset = 0;
+ net_offset += 4;
+ net_offset += 1;
+ UNMARSHAL_U16LE(ctx, out->tag);
+}
+
+static void unmarshal_Tmkdir([[maybe_unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
+ struct lib9p_msg_Tmkdir *out = out_buf;
+ [[maybe_unused]] void *extra = &out[1];
+ uint32_t net_offset = 0;
+ net_offset += 4;
+ net_offset += 1;
+ UNMARSHAL_U16LE(ctx, out->tag);
+ UNMARSHAL_U32LE(ctx, out->dfid);
+ UNMARSHAL_U16LE(ctx, out->name.len);
+ UNMARSHAL_BYTES(ctx, out->name.utf8, out->name.len);
+ UNMARSHAL_U32LE(ctx, out->mode);
+ UNMARSHAL_U32LE(ctx, out->gid);
+}
+
+static void unmarshal_Rmkdir([[maybe_unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
+ struct lib9p_msg_Rmkdir *out = out_buf;
+ [[maybe_unused]] void *extra = &out[1];
+ uint32_t net_offset = 0;
+ net_offset += 4;
+ net_offset += 1;
+ UNMARSHAL_U16LE(ctx, out->tag);
+ UNMARSHAL_U8LE(ctx, out->qid.type);
+ UNMARSHAL_U32LE(ctx, out->qid.vers);
+ UNMARSHAL_U64LE(ctx, out->qid.path);
+}
+
+static void unmarshal_Trenameat([[maybe_unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
+ struct lib9p_msg_Trenameat *out = out_buf;
+ [[maybe_unused]] void *extra = &out[1];
+ uint32_t net_offset = 0;
+ net_offset += 4;
+ net_offset += 1;
+ UNMARSHAL_U16LE(ctx, out->tag);
+ UNMARSHAL_U32LE(ctx, out->olddirfid);
+ UNMARSHAL_U16LE(ctx, out->oldname.len);
+ UNMARSHAL_BYTES(ctx, out->oldname.utf8, out->oldname.len);
+ UNMARSHAL_U32LE(ctx, out->newdirfid);
+ UNMARSHAL_U16LE(ctx, out->newname.len);
+ UNMARSHAL_BYTES(ctx, out->newname.utf8, out->newname.len);
+}
+
+static void unmarshal_Rrenameat([[maybe_unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
+ struct lib9p_msg_Rrenameat *out = out_buf;
+ [[maybe_unused]] void *extra = &out[1];
+ uint32_t net_offset = 0;
+ net_offset += 4;
+ net_offset += 1;
+ UNMARSHAL_U16LE(ctx, out->tag);
+}
+
+static void unmarshal_Tunlinkat([[maybe_unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
+ struct lib9p_msg_Tunlinkat *out = out_buf;
+ [[maybe_unused]] void *extra = &out[1];
+ uint32_t net_offset = 0;
+ net_offset += 4;
+ net_offset += 1;
+ UNMARSHAL_U16LE(ctx, out->tag);
+ UNMARSHAL_U32LE(ctx, out->dirfd);
+ UNMARSHAL_U16LE(ctx, out->name.len);
+ UNMARSHAL_BYTES(ctx, out->name.utf8, out->name.len);
+ UNMARSHAL_U32LE(ctx, out->flags);
+}
+
+static void unmarshal_Runlinkat([[maybe_unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
+ struct lib9p_msg_Runlinkat *out = out_buf;
+ [[maybe_unused]] void *extra = &out[1];
+ uint32_t net_offset = 0;
+ net_offset += 4;
+ net_offset += 1;
+ UNMARSHAL_U16LE(ctx, out->tag);
+}
+
+#endif /* CONFIG_9P_ENABLE_9P2000_L */
+#if CONFIG_9P_ENABLE_9P2000_e
+static void unmarshal_Tsession([[maybe_unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
+ struct lib9p_msg_Tsession *out = out_buf;
+ [[maybe_unused]] void *extra = &out[1];
+ uint32_t net_offset = 0;
+ net_offset += 4;
+ net_offset += 1;
+ UNMARSHAL_U16LE(ctx, out->tag);
+ UNMARSHAL_U64LE(ctx, out->key);
+}
+
+static void unmarshal_Rsession([[maybe_unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
+ struct lib9p_msg_Rsession *out = out_buf;
+ [[maybe_unused]] void *extra = &out[1];
+ uint32_t net_offset = 0;
+ net_offset += 4;
+ net_offset += 1;
+ UNMARSHAL_U16LE(ctx, out->tag);
+}
+
+static void unmarshal_Tsread([[maybe_unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
+ struct lib9p_msg_Tsread *out = out_buf;
+ [[maybe_unused]] void *extra = &out[1];
+ uint32_t net_offset = 0;
+ net_offset += 4;
+ net_offset += 1;
+ UNMARSHAL_U16LE(ctx, out->tag);
+ UNMARSHAL_U32LE(ctx, out->fid);
+ UNMARSHAL_U16LE(ctx, out->nwname);
+ out->wname = extra;
+ extra += sizeof(out->wname[0]) * out->nwname;
+ for (uint16_t i = 0; i < out->nwname; i++) {
+ UNMARSHAL_U16LE(ctx, out->wname[i].len);
+ UNMARSHAL_BYTES(ctx, out->wname[i].utf8, out->wname[i].len);
+ }
+}
+
+static void unmarshal_Rsread([[maybe_unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
+ struct lib9p_msg_Rsread *out = out_buf;
+ [[maybe_unused]] void *extra = &out[1];
+ uint32_t net_offset = 0;
+ net_offset += 4;
+ net_offset += 1;
+ UNMARSHAL_U16LE(ctx, out->tag);
+ UNMARSHAL_U32LE(ctx, out->count);
+ UNMARSHAL_BYTES(ctx, out->data, out->count);
+}
+
+static void unmarshal_Tswrite([[maybe_unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
+ struct lib9p_msg_Tswrite *out = out_buf;
+ [[maybe_unused]] void *extra = &out[1];
+ uint32_t net_offset = 0;
+ net_offset += 4;
+ net_offset += 1;
+ UNMARSHAL_U16LE(ctx, out->tag);
+ UNMARSHAL_U32LE(ctx, out->fid);
+ UNMARSHAL_U16LE(ctx, out->nwname);
+ out->wname = extra;
+ extra += sizeof(out->wname[0]) * out->nwname;
+ for (uint16_t i = 0; i < out->nwname; i++) {
+ UNMARSHAL_U16LE(ctx, out->wname[i].len);
+ UNMARSHAL_BYTES(ctx, out->wname[i].utf8, out->wname[i].len);
+ }
+ UNMARSHAL_U32LE(ctx, out->count);
+ UNMARSHAL_BYTES(ctx, out->data, out->count);
+}
+
+static void unmarshal_Rswrite([[maybe_unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {
+ struct lib9p_msg_Rswrite *out = out_buf;
+ [[maybe_unused]] void *extra = &out[1];
+ uint32_t net_offset = 0;
+ net_offset += 4;
+ net_offset += 1;
+ UNMARSHAL_U16LE(ctx, out->tag);
+ UNMARSHAL_U32LE(ctx, out->count);
+}
+#endif /* CONFIG_9P_ENABLE_9P2000_e */
+
+/* marshal_* ******************************************************************/
+
+#define MARSHAL_BYTES_ZEROCOPY(ctx, data, len) \
+ if (ret->net_iov[ret->net_iov_cnt-1].iov_len) \
+ ret->net_iov_cnt++; \
+ ret->net_iov[ret->net_iov_cnt-1].iov_write_from = data; \
+ ret->net_iov[ret->net_iov_cnt-1].iov_len = len; \
+ ret->net_iov_cnt++;
+#define MARSHAL_BYTES(ctx, data, len) \
+ if (!ret->net_iov[ret->net_iov_cnt-1].iov_write_from) \
+ ret->net_iov[ret->net_iov_cnt-1].iov_write_from = &ret->net_copied[ret->net_copied_size]; \
+ memcpy(&ret->net_copied[ret->net_copied_size], data, len); \
+ ret->net_copied_size += len; \
+ ret->net_iov[ret->net_iov_cnt-1].iov_len += len;
+#define MARSHAL_U8LE(ctx, val) \
+ if (!ret->net_iov[ret->net_iov_cnt-1].iov_write_from) \
+ ret->net_iov[ret->net_iov_cnt-1].iov_write_from = &ret->net_copied[ret->net_copied_size]; \
+ ret->net_copied[ret->net_copied_size] = val; \
+ ret->net_copied_size += 1; \
+ ret->net_iov[ret->net_iov_cnt-1].iov_len += 1;
+#define MARSHAL_U16LE(ctx, val) \
+ if (!ret->net_iov[ret->net_iov_cnt-1].iov_write_from) \
+ ret->net_iov[ret->net_iov_cnt-1].iov_write_from = &ret->net_copied[ret->net_copied_size]; \
+ uint16le_encode(&ret->net_copied[ret->net_copied_size], val); \
+ ret->net_copied_size += 2; \
+ ret->net_iov[ret->net_iov_cnt-1].iov_len += 2;
+#define MARSHAL_U32LE(ctx, val) \
+ if (!ret->net_iov[ret->net_iov_cnt-1].iov_write_from) \
+ ret->net_iov[ret->net_iov_cnt-1].iov_write_from = &ret->net_copied[ret->net_copied_size]; \
+ uint32le_encode(&ret->net_copied[ret->net_copied_size], val); \
+ ret->net_copied_size += 4; \
+ ret->net_iov[ret->net_iov_cnt-1].iov_len += 4;
+#define MARSHAL_U64LE(ctx, val) \
+ if (!ret->net_iov[ret->net_iov_cnt-1].iov_write_from) \
+ ret->net_iov[ret->net_iov_cnt-1].iov_write_from = &ret->net_copied[ret->net_copied_size]; \
+ uint64le_encode(&ret->net_copied[ret->net_copied_size], val); \
+ ret->net_copied_size += 8; \
+ ret->net_iov[ret->net_iov_cnt-1].iov_len += 8;
+
+#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u
+static bool marshal_stat(struct lib9p_ctx *ctx, struct lib9p_stat *val, struct _marshal_ret *ret) {
+ uint32_t needed_size = 49 + val->name.len + val->owner_uname.len + val->owner_gname.len + val->last_modifier_uname.len;
+#if CONFIG_9P_ENABLE_9P2000_u
+ if is_ver(ctx, 9P2000_u) {
+ needed_size += 14 + val->extension.len;
+ }
+#endif /* CONFIG_9P_ENABLE_9P2000_u */
+ if (needed_size > ctx->max_msg_size) {
+ return true;
+ }
+ uint32_t offsetof_end = needed_size;
+ uint32_t offsetof_fstype = 2;
+ MARSHAL_U16LE(ctx, offsetof_end - offsetof_fstype);
+ MARSHAL_U16LE(ctx, val->fstype);
+ MARSHAL_U32LE(ctx, val->fsdev);
+ MARSHAL_U8LE(ctx, val->qid.type & qt_masks[ctx->version]);
+ MARSHAL_U32LE(ctx, val->qid.vers);
+ MARSHAL_U64LE(ctx, val->qid.path);
+ MARSHAL_U32LE(ctx, val->mode & dm_masks[ctx->version]);
+ MARSHAL_U32LE(ctx, val->atime);
+ MARSHAL_U32LE(ctx, val->mtime);
+ MARSHAL_U64LE(ctx, val->length);
+ MARSHAL_U16LE(ctx, val->name.len);
+ MARSHAL_BYTES(ctx, val->name.utf8, val->name.len);
+ MARSHAL_U16LE(ctx, val->owner_uname.len);
+ MARSHAL_BYTES(ctx, val->owner_uname.utf8, val->owner_uname.len);
+ MARSHAL_U16LE(ctx, val->owner_gname.len);
+ MARSHAL_BYTES(ctx, val->owner_gname.utf8, val->owner_gname.len);
+ MARSHAL_U16LE(ctx, val->last_modifier_uname.len);
+ MARSHAL_BYTES(ctx, val->last_modifier_uname.utf8, val->last_modifier_uname.len);
+#if CONFIG_9P_ENABLE_9P2000_u
+ if (is_ver(ctx, 9P2000_u)) {
+ MARSHAL_U16LE(ctx, val->extension.len);
+ MARSHAL_BYTES(ctx, val->extension.utf8, val->extension.len);
+ MARSHAL_U32LE(ctx, val->owner_unum);
+ MARSHAL_U32LE(ctx, val->owner_gnum);
+ MARSHAL_U32LE(ctx, val->last_modifier_unum);
+ }
+#endif /* CONFIG_9P_ENABLE_9P2000_u */
+ return false;
+}
+
+#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */
+#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u || CONFIG_9P_ENABLE_uninitialized
+static error marshal_Tversion(struct lib9p_ctx *ctx, struct lib9p_msg_Tversion *val, struct _marshal_ret *ret) {
+ uint32_t needed_size = 13 + val->version.len;
+ if (needed_size > ctx->max_msg_size) {
+ return error_new(E_POSIX_ERANGE, "Tversion message too large to marshal into ",
+ ctx->version ? "negotiated" : "client", " limit",
+ " (", needed_size, " > ", ctx->max_msg_size, ")");
+ }
+ uint32_t offsetof_end = needed_size;
+ uint32_t offsetof_size = 0;
+ MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
+ MARSHAL_U8LE(ctx, 100);
+ MARSHAL_U16LE(ctx, val->tag);
+ MARSHAL_U32LE(ctx, val->max_msg_size);
+ MARSHAL_U16LE(ctx, val->version.len);
+ MARSHAL_BYTES_ZEROCOPY(ctx, val->version.utf8, val->version.len);
+ return ERROR_NULL;
+}
+
+static error marshal_Rversion(struct lib9p_ctx *ctx, struct lib9p_msg_Rversion *val, struct _marshal_ret *ret) {
+ uint32_t needed_size = 13 + val->version.len;
+ if (needed_size > ctx->max_msg_size) {
+ return error_new(E_POSIX_ERANGE, "Rversion message too large to marshal into ",
+ ctx->version ? "negotiated" : "server", " limit",
+ " (", needed_size, " > ", ctx->max_msg_size, ")");
+ }
+ uint32_t offsetof_end = needed_size;
+ uint32_t offsetof_size = 0;
+ MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
+ MARSHAL_U8LE(ctx, 101);
+ MARSHAL_U16LE(ctx, val->tag);
+ MARSHAL_U32LE(ctx, val->max_msg_size);
+ MARSHAL_U16LE(ctx, val->version.len);
+ MARSHAL_BYTES_ZEROCOPY(ctx, val->version.utf8, val->version.len);
+ return ERROR_NULL;
+}
+
+#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u || CONFIG_9P_ENABLE_uninitialized */
+#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u
+static error marshal_Tauth(struct lib9p_ctx *ctx, struct lib9p_msg_Tauth *val, struct _marshal_ret *ret) {
+ uint32_t needed_size = 15 + val->uname.len + val->aname.len;
+#if CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_u
+ if ( is_ver(ctx, 9P2000_L) || is_ver(ctx, 9P2000_u) ) {
+ needed_size += 4;
+ }
+#endif /* CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_u */
+ if (needed_size > ctx->max_msg_size) {
+ return error_new(E_POSIX_ERANGE, "Tauth message too large to marshal into ",
+ ctx->version ? "negotiated" : "client", " limit",
+ " (", needed_size, " > ", ctx->max_msg_size, ")");
+ }
+ uint32_t offsetof_end = needed_size;
+ uint32_t offsetof_size = 0;
+ MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
+ MARSHAL_U8LE(ctx, 102);
+ MARSHAL_U16LE(ctx, val->tag);
+ MARSHAL_U32LE(ctx, val->afid);
+ MARSHAL_U16LE(ctx, val->uname.len);
+ MARSHAL_BYTES_ZEROCOPY(ctx, val->uname.utf8, val->uname.len);
+ MARSHAL_U16LE(ctx, val->aname.len);
+ MARSHAL_BYTES_ZEROCOPY(ctx, val->aname.utf8, val->aname.len);
+#if CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_u
+ if (( is_ver(ctx, 9P2000_L) || is_ver(ctx, 9P2000_u) )) {
+ MARSHAL_U32LE(ctx, val->unum);
+ }
+#endif /* CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_u */
+ return ERROR_NULL;
+}
+
+static error marshal_Rauth(struct lib9p_ctx *ctx, struct lib9p_msg_Rauth *val, struct _marshal_ret *ret) {
+ uint32_t needed_size = 20;
+ if (needed_size > ctx->max_msg_size) {
+ return error_new(E_POSIX_ERANGE, "Rauth message too large to marshal into ",
+ ctx->version ? "negotiated" : "server", " limit",
+ " (", needed_size, " > ", ctx->max_msg_size, ")");
+ }
+ uint32_t offsetof_end = needed_size;
+ uint32_t offsetof_size = 0;
+ MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
+ MARSHAL_U8LE(ctx, 103);
+ MARSHAL_U16LE(ctx, val->tag);
+ MARSHAL_U8LE(ctx, val->aqid.type & qt_masks[ctx->version]);
+ MARSHAL_U32LE(ctx, val->aqid.vers);
+ MARSHAL_U64LE(ctx, val->aqid.path);
+ return ERROR_NULL;
+}
+
+static error marshal_Tattach(struct lib9p_ctx *ctx, struct lib9p_msg_Tattach *val, struct _marshal_ret *ret) {
+ uint32_t needed_size = 19 + val->uname.len + val->aname.len;
+#if CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_u
+ if ( is_ver(ctx, 9P2000_L) || is_ver(ctx, 9P2000_u) ) {
+ needed_size += 4;
+ }
+#endif /* CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_u */
+ if (needed_size > ctx->max_msg_size) {
+ return error_new(E_POSIX_ERANGE, "Tattach message too large to marshal into ",
+ ctx->version ? "negotiated" : "client", " limit",
+ " (", needed_size, " > ", ctx->max_msg_size, ")");
+ }
+ uint32_t offsetof_end = needed_size;
+ uint32_t offsetof_size = 0;
+ MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
+ MARSHAL_U8LE(ctx, 104);
+ MARSHAL_U16LE(ctx, val->tag);
+ MARSHAL_U32LE(ctx, val->fid);
+ MARSHAL_U32LE(ctx, val->afid);
+ MARSHAL_U16LE(ctx, val->uname.len);
+ MARSHAL_BYTES_ZEROCOPY(ctx, val->uname.utf8, val->uname.len);
+ MARSHAL_U16LE(ctx, val->aname.len);
+ MARSHAL_BYTES_ZEROCOPY(ctx, val->aname.utf8, val->aname.len);
+#if CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_u
+ if (( is_ver(ctx, 9P2000_L) || is_ver(ctx, 9P2000_u) )) {
+ MARSHAL_U32LE(ctx, val->unum);
+ }
+#endif /* CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_u */
+ return ERROR_NULL;
+}
+
+static error marshal_Rattach(struct lib9p_ctx *ctx, struct lib9p_msg_Rattach *val, struct _marshal_ret *ret) {
+ uint32_t needed_size = 20;
+ if (needed_size > ctx->max_msg_size) {
+ return error_new(E_POSIX_ERANGE, "Rattach message too large to marshal into ",
+ ctx->version ? "negotiated" : "server", " limit",
+ " (", needed_size, " > ", ctx->max_msg_size, ")");
+ }
+ uint32_t offsetof_end = needed_size;
+ uint32_t offsetof_size = 0;
+ MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
+ MARSHAL_U8LE(ctx, 105);
+ MARSHAL_U16LE(ctx, val->tag);
+ MARSHAL_U8LE(ctx, val->qid.type & qt_masks[ctx->version]);
+ MARSHAL_U32LE(ctx, val->qid.vers);
+ MARSHAL_U64LE(ctx, val->qid.path);
+ return ERROR_NULL;
+}
+
+#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */
+#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u || CONFIG_9P_ENABLE_uninitialized
+static error marshal_Rerror(struct lib9p_ctx *ctx, struct lib9p_msg_Rerror *val, struct _marshal_ret *ret) {
+ uint32_t needed_size = 9 + val->errstr.len;
+#if CONFIG_9P_ENABLE_9P2000_u
+ if is_ver(ctx, 9P2000_u) {
+ needed_size += 4;
+ }
+#endif /* CONFIG_9P_ENABLE_9P2000_u */
+ if (needed_size > ctx->max_msg_size) {
+ return error_new(E_POSIX_ERANGE, "Rerror message too large to marshal into ",
+ ctx->version ? "negotiated" : "server", " limit",
+ " (", needed_size, " > ", ctx->max_msg_size, ")");
+ }
+ uint32_t offsetof_end = needed_size;
+ uint32_t offsetof_size = 0;
+ MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
+ MARSHAL_U8LE(ctx, 107);
+ MARSHAL_U16LE(ctx, val->tag);
+ MARSHAL_U16LE(ctx, val->errstr.len);
+ MARSHAL_BYTES_ZEROCOPY(ctx, val->errstr.utf8, val->errstr.len);
+#if CONFIG_9P_ENABLE_9P2000_u
+ if (is_ver(ctx, 9P2000_u)) {
+ MARSHAL_U32LE(ctx, val->errnum);
+ }
+#endif /* CONFIG_9P_ENABLE_9P2000_u */
+ return ERROR_NULL;
+}
+
+#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u || CONFIG_9P_ENABLE_uninitialized */
+#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u
+static error marshal_Tflush(struct lib9p_ctx *ctx, struct lib9p_msg_Tflush *val, struct _marshal_ret *ret) {
+ uint32_t needed_size = 9;
+ if (needed_size > ctx->max_msg_size) {
+ return error_new(E_POSIX_ERANGE, "Tflush message too large to marshal into ",
+ ctx->version ? "negotiated" : "client", " limit",
+ " (", needed_size, " > ", ctx->max_msg_size, ")");
+ }
+ uint32_t offsetof_end = needed_size;
+ uint32_t offsetof_size = 0;
+ MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
+ MARSHAL_U8LE(ctx, 108);
+ MARSHAL_U16LE(ctx, val->tag);
+ MARSHAL_U16LE(ctx, val->oldtag);
+ return ERROR_NULL;
+}
+
+static error marshal_Rflush(struct lib9p_ctx *ctx, struct lib9p_msg_Rflush *val, struct _marshal_ret *ret) {
+ uint32_t needed_size = 7;
+ if (needed_size > ctx->max_msg_size) {
+ return error_new(E_POSIX_ERANGE, "Rflush message too large to marshal into ",
+ ctx->version ? "negotiated" : "server", " limit",
+ " (", needed_size, " > ", ctx->max_msg_size, ")");
+ }
+ uint32_t offsetof_end = needed_size;
+ uint32_t offsetof_size = 0;
+ MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
+ MARSHAL_U8LE(ctx, 109);
+ MARSHAL_U16LE(ctx, val->tag);
+ return ERROR_NULL;
+}
+
+static error marshal_Twalk(struct lib9p_ctx *ctx, struct lib9p_msg_Twalk *val, struct _marshal_ret *ret) {
+ uint32_t needed_size = 17;
+ for (uint16_t i = 0; i < val->nwname; i++) {
+ needed_size += 2 + val->wname[i].len;
+ }
+ if (needed_size > ctx->max_msg_size) {
+ return error_new(E_POSIX_ERANGE, "Twalk message too large to marshal into ",
+ ctx->version ? "negotiated" : "client", " limit",
+ " (", needed_size, " > ", ctx->max_msg_size, ")");
+ }
+ uint32_t offsetof_end = needed_size;
+ uint32_t offsetof_size = 0;
+ MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
+ MARSHAL_U8LE(ctx, 110);
+ MARSHAL_U16LE(ctx, val->tag);
+ MARSHAL_U32LE(ctx, val->fid);
+ MARSHAL_U32LE(ctx, val->newfid);
+ MARSHAL_U16LE(ctx, val->nwname);
+ for (uint16_t i = 0; i < val->nwname; i++) {
+ MARSHAL_U16LE(ctx, val->wname[i].len);
+ MARSHAL_BYTES_ZEROCOPY(ctx, val->wname[i].utf8, val->wname[i].len);
+ }
+ return ERROR_NULL;
+}
+
+static error marshal_Rwalk(struct lib9p_ctx *ctx, struct lib9p_msg_Rwalk *val, struct _marshal_ret *ret) {
+ uint32_t needed_size = 9 + (val->nwqid)*13;
+ if (needed_size > ctx->max_msg_size) {
+ return error_new(E_POSIX_ERANGE, "Rwalk message too large to marshal into ",
+ ctx->version ? "negotiated" : "server", " limit",
+ " (", needed_size, " > ", ctx->max_msg_size, ")");
+ }
+ uint32_t offsetof_end = needed_size;
+ uint32_t offsetof_size = 0;
+ MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
+ MARSHAL_U8LE(ctx, 111);
+ MARSHAL_U16LE(ctx, val->tag);
+ MARSHAL_U16LE(ctx, val->nwqid);
+ for (uint16_t i = 0; i < val->nwqid; i++) {
+ MARSHAL_U8LE(ctx, val->wqid[i].type & qt_masks[ctx->version]);
+ MARSHAL_U32LE(ctx, val->wqid[i].vers);
+ MARSHAL_U64LE(ctx, val->wqid[i].path);
+ }
+ return ERROR_NULL;
+}
+
+#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */
+#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u
+static error marshal_Topen(struct lib9p_ctx *ctx, struct lib9p_msg_Topen *val, struct _marshal_ret *ret) {
+ uint32_t needed_size = 12;
+ if (needed_size > ctx->max_msg_size) {
+ return error_new(E_POSIX_ERANGE, "Topen message too large to marshal into ",
+ ctx->version ? "negotiated" : "client", " limit",
+ " (", needed_size, " > ", ctx->max_msg_size, ")");
+ }
+ uint32_t offsetof_end = needed_size;
+ uint32_t offsetof_size = 0;
+ MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
+ MARSHAL_U8LE(ctx, 112);
+ MARSHAL_U16LE(ctx, val->tag);
+ MARSHAL_U32LE(ctx, val->fid);
+ MARSHAL_U8LE(ctx, val->mode & o_masks[ctx->version]);
+ return ERROR_NULL;
+}
+
+static error marshal_Ropen(struct lib9p_ctx *ctx, struct lib9p_msg_Ropen *val, struct _marshal_ret *ret) {
+ uint32_t needed_size = 24;
+ if (needed_size > ctx->max_msg_size) {
+ return error_new(E_POSIX_ERANGE, "Ropen message too large to marshal into ",
+ ctx->version ? "negotiated" : "server", " limit",
+ " (", needed_size, " > ", ctx->max_msg_size, ")");
+ }
+ uint32_t offsetof_end = needed_size;
+ uint32_t offsetof_size = 0;
+ MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
+ MARSHAL_U8LE(ctx, 113);
+ MARSHAL_U16LE(ctx, val->tag);
+ MARSHAL_U8LE(ctx, val->qid.type & qt_masks[ctx->version]);
+ MARSHAL_U32LE(ctx, val->qid.vers);
+ MARSHAL_U64LE(ctx, val->qid.path);
+ MARSHAL_U32LE(ctx, val->iounit);
+ return ERROR_NULL;
+}
+
+static error marshal_Tcreate(struct lib9p_ctx *ctx, struct lib9p_msg_Tcreate *val, struct _marshal_ret *ret) {
+ uint32_t needed_size = 18 + val->name.len;
+ if (needed_size > ctx->max_msg_size) {
+ return error_new(E_POSIX_ERANGE, "Tcreate message too large to marshal into ",
+ ctx->version ? "negotiated" : "client", " limit",
+ " (", needed_size, " > ", ctx->max_msg_size, ")");
+ }
+ uint32_t offsetof_end = needed_size;
+ uint32_t offsetof_size = 0;
+ MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
+ MARSHAL_U8LE(ctx, 114);
+ MARSHAL_U16LE(ctx, val->tag);
+ MARSHAL_U32LE(ctx, val->fid);
+ MARSHAL_U16LE(ctx, val->name.len);
+ MARSHAL_BYTES_ZEROCOPY(ctx, val->name.utf8, val->name.len);
+ MARSHAL_U32LE(ctx, val->perm & dm_masks[ctx->version]);
+ MARSHAL_U8LE(ctx, val->mode & o_masks[ctx->version]);
+ return ERROR_NULL;
+}
+
+static error marshal_Rcreate(struct lib9p_ctx *ctx, struct lib9p_msg_Rcreate *val, struct _marshal_ret *ret) {
+ uint32_t needed_size = 24;
+ if (needed_size > ctx->max_msg_size) {
+ return error_new(E_POSIX_ERANGE, "Rcreate message too large to marshal into ",
+ ctx->version ? "negotiated" : "server", " limit",
+ " (", needed_size, " > ", ctx->max_msg_size, ")");
+ }
+ uint32_t offsetof_end = needed_size;
+ uint32_t offsetof_size = 0;
+ MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
+ MARSHAL_U8LE(ctx, 115);
+ MARSHAL_U16LE(ctx, val->tag);
+ MARSHAL_U8LE(ctx, val->qid.type & qt_masks[ctx->version]);
+ MARSHAL_U32LE(ctx, val->qid.vers);
+ MARSHAL_U64LE(ctx, val->qid.path);
+ MARSHAL_U32LE(ctx, val->iounit);
+ return ERROR_NULL;
+}
+
+#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */
+#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u
+static error marshal_Tread(struct lib9p_ctx *ctx, struct lib9p_msg_Tread *val, struct _marshal_ret *ret) {
+ uint32_t needed_size = 23;
+ if (needed_size > ctx->max_msg_size) {
+ return error_new(E_POSIX_ERANGE, "Tread message too large to marshal into ",
+ ctx->version ? "negotiated" : "client", " limit",
+ " (", needed_size, " > ", ctx->max_msg_size, ")");
+ }
+ uint32_t offsetof_end = needed_size;
+ uint32_t offsetof_size = 0;
+ MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
+ MARSHAL_U8LE(ctx, 116);
+ MARSHAL_U16LE(ctx, val->tag);
+ MARSHAL_U32LE(ctx, val->fid);
+ MARSHAL_U64LE(ctx, val->offset);
+ MARSHAL_U32LE(ctx, val->count);
+ return ERROR_NULL;
+}
+
+static error marshal_Rread(struct lib9p_ctx *ctx, struct lib9p_msg_Rread *val, struct _marshal_ret *ret) {
+ uint32_t needed_size = 11 + val->count;
+ if (needed_size > ctx->max_msg_size) {
+ return error_new(E_POSIX_ERANGE, "Rread message too large to marshal into ",
+ ctx->version ? "negotiated" : "server", " limit",
+ " (", needed_size, " > ", ctx->max_msg_size, ")");
+ }
+ uint32_t offsetof_end = needed_size;
+ uint32_t offsetof_size = 0;
+ MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
+ MARSHAL_U8LE(ctx, 117);
+ MARSHAL_U16LE(ctx, val->tag);
+ MARSHAL_U32LE(ctx, val->count);
+ MARSHAL_BYTES_ZEROCOPY(ctx, val->data, val->count);
+ return ERROR_NULL;
+}
+
+static error marshal_Twrite(struct lib9p_ctx *ctx, struct lib9p_msg_Twrite *val, struct _marshal_ret *ret) {
+ uint32_t needed_size = 23 + val->count;
+ if (needed_size > ctx->max_msg_size) {
+ return error_new(E_POSIX_ERANGE, "Twrite message too large to marshal into ",
+ ctx->version ? "negotiated" : "client", " limit",
+ " (", needed_size, " > ", ctx->max_msg_size, ")");
+ }
+ uint32_t offsetof_end = needed_size;
+ uint32_t offsetof_size = 0;
+ MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
+ MARSHAL_U8LE(ctx, 118);
+ MARSHAL_U16LE(ctx, val->tag);
+ MARSHAL_U32LE(ctx, val->fid);
+ MARSHAL_U64LE(ctx, val->offset);
+ MARSHAL_U32LE(ctx, val->count);
+ MARSHAL_BYTES_ZEROCOPY(ctx, val->data, val->count);
+ return ERROR_NULL;
+}
+
+static error marshal_Rwrite(struct lib9p_ctx *ctx, struct lib9p_msg_Rwrite *val, struct _marshal_ret *ret) {
+ uint32_t needed_size = 11;
+ if (needed_size > ctx->max_msg_size) {
+ return error_new(E_POSIX_ERANGE, "Rwrite message too large to marshal into ",
+ ctx->version ? "negotiated" : "server", " limit",
+ " (", needed_size, " > ", ctx->max_msg_size, ")");
+ }
+ uint32_t offsetof_end = needed_size;
+ uint32_t offsetof_size = 0;
+ MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
+ MARSHAL_U8LE(ctx, 119);
+ MARSHAL_U16LE(ctx, val->tag);
+ MARSHAL_U32LE(ctx, val->count);
+ return ERROR_NULL;
+}
+
+static error marshal_Tclunk(struct lib9p_ctx *ctx, struct lib9p_msg_Tclunk *val, struct _marshal_ret *ret) {
+ uint32_t needed_size = 11;
+ if (needed_size > ctx->max_msg_size) {
+ return error_new(E_POSIX_ERANGE, "Tclunk message too large to marshal into ",
+ ctx->version ? "negotiated" : "client", " limit",
+ " (", needed_size, " > ", ctx->max_msg_size, ")");
+ }
+ uint32_t offsetof_end = needed_size;
+ uint32_t offsetof_size = 0;
+ MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
+ MARSHAL_U8LE(ctx, 120);
+ MARSHAL_U16LE(ctx, val->tag);
+ MARSHAL_U32LE(ctx, val->fid);
+ return ERROR_NULL;
+}
+
+static error marshal_Rclunk(struct lib9p_ctx *ctx, struct lib9p_msg_Rclunk *val, struct _marshal_ret *ret) {
+ uint32_t needed_size = 7;
+ if (needed_size > ctx->max_msg_size) {
+ return error_new(E_POSIX_ERANGE, "Rclunk message too large to marshal into ",
+ ctx->version ? "negotiated" : "server", " limit",
+ " (", needed_size, " > ", ctx->max_msg_size, ")");
+ }
+ uint32_t offsetof_end = needed_size;
+ uint32_t offsetof_size = 0;
+ MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
+ MARSHAL_U8LE(ctx, 121);
+ MARSHAL_U16LE(ctx, val->tag);
+ return ERROR_NULL;
+}
+
+static error marshal_Tremove(struct lib9p_ctx *ctx, struct lib9p_msg_Tremove *val, struct _marshal_ret *ret) {
+ uint32_t needed_size = 11;
+ if (needed_size > ctx->max_msg_size) {
+ return error_new(E_POSIX_ERANGE, "Tremove message too large to marshal into ",
+ ctx->version ? "negotiated" : "client", " limit",
+ " (", needed_size, " > ", ctx->max_msg_size, ")");
+ }
+ uint32_t offsetof_end = needed_size;
+ uint32_t offsetof_size = 0;
+ MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
+ MARSHAL_U8LE(ctx, 122);
+ MARSHAL_U16LE(ctx, val->tag);
+ MARSHAL_U32LE(ctx, val->fid);
+ return ERROR_NULL;
+}
+
+static error marshal_Rremove(struct lib9p_ctx *ctx, struct lib9p_msg_Rremove *val, struct _marshal_ret *ret) {
+ uint32_t needed_size = 7;
+ if (needed_size > ctx->max_msg_size) {
+ return error_new(E_POSIX_ERANGE, "Rremove message too large to marshal into ",
+ ctx->version ? "negotiated" : "server", " limit",
+ " (", needed_size, " > ", ctx->max_msg_size, ")");
+ }
+ uint32_t offsetof_end = needed_size;
+ uint32_t offsetof_size = 0;
+ MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
+ MARSHAL_U8LE(ctx, 123);
+ MARSHAL_U16LE(ctx, val->tag);
+ return ERROR_NULL;
+}
+
+#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */
+#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u
+static error marshal_Tstat(struct lib9p_ctx *ctx, struct lib9p_msg_Tstat *val, struct _marshal_ret *ret) {
+ uint32_t needed_size = 11;
+ if (needed_size > ctx->max_msg_size) {
+ return error_new(E_POSIX_ERANGE, "Tstat message too large to marshal into ",
+ ctx->version ? "negotiated" : "client", " limit",
+ " (", needed_size, " > ", ctx->max_msg_size, ")");
+ }
+ uint32_t offsetof_end = needed_size;
+ uint32_t offsetof_size = 0;
+ MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
+ MARSHAL_U8LE(ctx, 124);
+ MARSHAL_U16LE(ctx, val->tag);
+ MARSHAL_U32LE(ctx, val->fid);
+ return ERROR_NULL;
+}
+
+static error marshal_Rstat(struct lib9p_ctx *ctx, struct lib9p_msg_Rstat *val, struct _marshal_ret *ret) {
+ uint32_t needed_size = 58 + val->stat.name.len + val->stat.owner_uname.len + val->stat.owner_gname.len + val->stat.last_modifier_uname.len;
+#if CONFIG_9P_ENABLE_9P2000_u
+ if is_ver(ctx, 9P2000_u) {
+ needed_size += 14 + val->stat.extension.len;
+ }
+#endif /* CONFIG_9P_ENABLE_9P2000_u */
+ if (needed_size > ctx->max_msg_size) {
+ return error_new(E_POSIX_ERANGE, "Rstat message too large to marshal into ",
+ ctx->version ? "negotiated" : "server", " limit",
+ " (", needed_size, " > ", ctx->max_msg_size, ")");
+ }
+ uint32_t offsetof_end = needed_size;
+ uint32_t offsetof_size = 0;
+ uint32_t offsetof_stat = 9;
+ MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
+ MARSHAL_U8LE(ctx, 125);
+ MARSHAL_U16LE(ctx, val->tag);
+ MARSHAL_U16LE(ctx, offsetof_end - offsetof_stat);
+ uint32_t offsetof_stat_end = 49 + val->stat.name.len + val->stat.owner_uname.len + val->stat.owner_gname.len + val->stat.last_modifier_uname.len;
+#if CONFIG_9P_ENABLE_9P2000_u
+ if is_ver(ctx, 9P2000_u) {
+ offsetof_stat_end += 14 + val->stat.extension.len;
+ }
+#endif /* CONFIG_9P_ENABLE_9P2000_u */
+ uint32_t offsetof_stat_fstype = 2;
+ MARSHAL_U16LE(ctx, offsetof_stat_end - offsetof_stat_fstype);
+ MARSHAL_U16LE(ctx, val->stat.fstype);
+ MARSHAL_U32LE(ctx, val->stat.fsdev);
+ MARSHAL_U8LE(ctx, val->stat.qid.type & qt_masks[ctx->version]);
+ MARSHAL_U32LE(ctx, val->stat.qid.vers);
+ MARSHAL_U64LE(ctx, val->stat.qid.path);
+ MARSHAL_U32LE(ctx, val->stat.mode & dm_masks[ctx->version]);
+ MARSHAL_U32LE(ctx, val->stat.atime);
+ MARSHAL_U32LE(ctx, val->stat.mtime);
+ MARSHAL_U64LE(ctx, val->stat.length);
+ MARSHAL_U16LE(ctx, val->stat.name.len);
+ MARSHAL_BYTES_ZEROCOPY(ctx, val->stat.name.utf8, val->stat.name.len);
+ MARSHAL_U16LE(ctx, val->stat.owner_uname.len);
+ MARSHAL_BYTES_ZEROCOPY(ctx, val->stat.owner_uname.utf8, val->stat.owner_uname.len);
+ MARSHAL_U16LE(ctx, val->stat.owner_gname.len);
+ MARSHAL_BYTES_ZEROCOPY(ctx, val->stat.owner_gname.utf8, val->stat.owner_gname.len);
+ MARSHAL_U16LE(ctx, val->stat.last_modifier_uname.len);
+ MARSHAL_BYTES_ZEROCOPY(ctx, val->stat.last_modifier_uname.utf8, val->stat.last_modifier_uname.len);
+#if CONFIG_9P_ENABLE_9P2000_u
+ if (is_ver(ctx, 9P2000_u)) {
+ MARSHAL_U16LE(ctx, val->stat.extension.len);
+ MARSHAL_BYTES_ZEROCOPY(ctx, val->stat.extension.utf8, val->stat.extension.len);
+ MARSHAL_U32LE(ctx, val->stat.owner_unum);
+ MARSHAL_U32LE(ctx, val->stat.owner_gnum);
+ MARSHAL_U32LE(ctx, val->stat.last_modifier_unum);
+ }
+#endif /* CONFIG_9P_ENABLE_9P2000_u */
+ return ERROR_NULL;
+}
+
+static error marshal_Twstat(struct lib9p_ctx *ctx, struct lib9p_msg_Twstat *val, struct _marshal_ret *ret) {
+ uint32_t needed_size = 62 + val->stat.name.len + val->stat.owner_uname.len + val->stat.owner_gname.len + val->stat.last_modifier_uname.len;
+#if CONFIG_9P_ENABLE_9P2000_u
+ if is_ver(ctx, 9P2000_u) {
+ needed_size += 14 + val->stat.extension.len;
+ }
+#endif /* CONFIG_9P_ENABLE_9P2000_u */
+ if (needed_size > ctx->max_msg_size) {
+ return error_new(E_POSIX_ERANGE, "Twstat message too large to marshal into ",
+ ctx->version ? "negotiated" : "client", " limit",
+ " (", needed_size, " > ", ctx->max_msg_size, ")");
+ }
+ uint32_t offsetof_end = needed_size;
+ uint32_t offsetof_size = 0;
+ uint32_t offsetof_stat = 13;
+ MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
+ MARSHAL_U8LE(ctx, 126);
+ MARSHAL_U16LE(ctx, val->tag);
+ MARSHAL_U32LE(ctx, val->fid);
+ MARSHAL_U16LE(ctx, offsetof_end - offsetof_stat);
+ uint32_t offsetof_stat_end = 49 + val->stat.name.len + val->stat.owner_uname.len + val->stat.owner_gname.len + val->stat.last_modifier_uname.len;
+#if CONFIG_9P_ENABLE_9P2000_u
+ if is_ver(ctx, 9P2000_u) {
+ offsetof_stat_end += 14 + val->stat.extension.len;
+ }
+#endif /* CONFIG_9P_ENABLE_9P2000_u */
+ uint32_t offsetof_stat_fstype = 2;
+ MARSHAL_U16LE(ctx, offsetof_stat_end - offsetof_stat_fstype);
+ MARSHAL_U16LE(ctx, val->stat.fstype);
+ MARSHAL_U32LE(ctx, val->stat.fsdev);
+ MARSHAL_U8LE(ctx, val->stat.qid.type & qt_masks[ctx->version]);
+ MARSHAL_U32LE(ctx, val->stat.qid.vers);
+ MARSHAL_U64LE(ctx, val->stat.qid.path);
+ MARSHAL_U32LE(ctx, val->stat.mode & dm_masks[ctx->version]);
+ MARSHAL_U32LE(ctx, val->stat.atime);
+ MARSHAL_U32LE(ctx, val->stat.mtime);
+ MARSHAL_U64LE(ctx, val->stat.length);
+ MARSHAL_U16LE(ctx, val->stat.name.len);
+ MARSHAL_BYTES_ZEROCOPY(ctx, val->stat.name.utf8, val->stat.name.len);
+ MARSHAL_U16LE(ctx, val->stat.owner_uname.len);
+ MARSHAL_BYTES_ZEROCOPY(ctx, val->stat.owner_uname.utf8, val->stat.owner_uname.len);
+ MARSHAL_U16LE(ctx, val->stat.owner_gname.len);
+ MARSHAL_BYTES_ZEROCOPY(ctx, val->stat.owner_gname.utf8, val->stat.owner_gname.len);
+ MARSHAL_U16LE(ctx, val->stat.last_modifier_uname.len);
+ MARSHAL_BYTES_ZEROCOPY(ctx, val->stat.last_modifier_uname.utf8, val->stat.last_modifier_uname.len);
+#if CONFIG_9P_ENABLE_9P2000_u
+ if (is_ver(ctx, 9P2000_u)) {
+ MARSHAL_U16LE(ctx, val->stat.extension.len);
+ MARSHAL_BYTES_ZEROCOPY(ctx, val->stat.extension.utf8, val->stat.extension.len);
+ MARSHAL_U32LE(ctx, val->stat.owner_unum);
+ MARSHAL_U32LE(ctx, val->stat.owner_gnum);
+ MARSHAL_U32LE(ctx, val->stat.last_modifier_unum);
+ }
+#endif /* CONFIG_9P_ENABLE_9P2000_u */
+ return ERROR_NULL;
+}
+
+static error marshal_Rwstat(struct lib9p_ctx *ctx, struct lib9p_msg_Rwstat *val, struct _marshal_ret *ret) {
+ uint32_t needed_size = 7;
+ if (needed_size > ctx->max_msg_size) {
+ return error_new(E_POSIX_ERANGE, "Rwstat message too large to marshal into ",
+ ctx->version ? "negotiated" : "server", " limit",
+ " (", needed_size, " > ", ctx->max_msg_size, ")");
+ }
+ uint32_t offsetof_end = needed_size;
+ uint32_t offsetof_size = 0;
+ MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
+ MARSHAL_U8LE(ctx, 127);
+ MARSHAL_U16LE(ctx, val->tag);
+ return ERROR_NULL;
+}
+
+#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */
+#if CONFIG_9P_ENABLE_9P2000_p9p
+static error marshal_Topenfd(struct lib9p_ctx *ctx, struct lib9p_msg_Topenfd *val, struct _marshal_ret *ret) {
+ uint32_t needed_size = 12;
+ if (needed_size > ctx->max_msg_size) {
+ return error_new(E_POSIX_ERANGE, "Topenfd message too large to marshal into ",
+ ctx->version ? "negotiated" : "client", " limit",
+ " (", needed_size, " > ", ctx->max_msg_size, ")");
+ }
+ uint32_t offsetof_end = needed_size;
+ uint32_t offsetof_size = 0;
+ MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
+ MARSHAL_U8LE(ctx, 98);
+ MARSHAL_U16LE(ctx, val->tag);
+ MARSHAL_U32LE(ctx, val->fid);
+ MARSHAL_U8LE(ctx, val->mode & o_masks[ctx->version]);
+ return ERROR_NULL;
+}
+
+static error marshal_Ropenfd(struct lib9p_ctx *ctx, struct lib9p_msg_Ropenfd *val, struct _marshal_ret *ret) {
+ uint32_t needed_size = 28;
+ if (needed_size > ctx->max_msg_size) {
+ return error_new(E_POSIX_ERANGE, "Ropenfd message too large to marshal into ",
+ ctx->version ? "negotiated" : "server", " limit",
+ " (", needed_size, " > ", ctx->max_msg_size, ")");
+ }
+ uint32_t offsetof_end = needed_size;
+ uint32_t offsetof_size = 0;
+ MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
+ MARSHAL_U8LE(ctx, 99);
+ MARSHAL_U16LE(ctx, val->tag);
+ MARSHAL_U8LE(ctx, val->qid.type & qt_masks[ctx->version]);
+ MARSHAL_U32LE(ctx, val->qid.vers);
+ MARSHAL_U64LE(ctx, val->qid.path);
+ MARSHAL_U32LE(ctx, val->iounit);
+ MARSHAL_U32LE(ctx, val->unixfd);
+ return ERROR_NULL;
+}
+
+#endif /* CONFIG_9P_ENABLE_9P2000_p9p */
+#if CONFIG_9P_ENABLE_9P2000_L
+static error marshal_Rlerror(struct lib9p_ctx *ctx, struct lib9p_msg_Rlerror *val, struct _marshal_ret *ret) {
+ uint32_t needed_size = 11;
+ if (needed_size > ctx->max_msg_size) {
+ return error_new(E_POSIX_ERANGE, "Rlerror message too large to marshal into ",
+ ctx->version ? "negotiated" : "server", " limit",
+ " (", needed_size, " > ", ctx->max_msg_size, ")");
+ }
+ uint32_t offsetof_end = needed_size;
+ uint32_t offsetof_size = 0;
+ MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
+ MARSHAL_U8LE(ctx, 7);
+ MARSHAL_U16LE(ctx, val->tag);
+ MARSHAL_U32LE(ctx, val->errnum);
+ return ERROR_NULL;
+}
+
+static error marshal_Tstatfs(struct lib9p_ctx *ctx, struct lib9p_msg_Tstatfs *val, struct _marshal_ret *ret) {
+ uint32_t needed_size = 11;
+ if (needed_size > ctx->max_msg_size) {
+ return error_new(E_POSIX_ERANGE, "Tstatfs message too large to marshal into ",
+ ctx->version ? "negotiated" : "client", " limit",
+ " (", needed_size, " > ", ctx->max_msg_size, ")");
+ }
+ uint32_t offsetof_end = needed_size;
+ uint32_t offsetof_size = 0;
+ MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
+ MARSHAL_U8LE(ctx, 8);
+ MARSHAL_U16LE(ctx, val->tag);
+ MARSHAL_U32LE(ctx, val->fid);
+ return ERROR_NULL;
+}
+
+static error marshal_Rstatfs(struct lib9p_ctx *ctx, struct lib9p_msg_Rstatfs *val, struct _marshal_ret *ret) {
+ uint32_t needed_size = 67;
+ if (needed_size > ctx->max_msg_size) {
+ return error_new(E_POSIX_ERANGE, "Rstatfs message too large to marshal into ",
+ ctx->version ? "negotiated" : "server", " limit",
+ " (", needed_size, " > ", ctx->max_msg_size, ")");
+ }
+ uint32_t offsetof_end = needed_size;
+ uint32_t offsetof_size = 0;
+ MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
+ MARSHAL_U8LE(ctx, 9);
+ MARSHAL_U16LE(ctx, val->tag);
+ MARSHAL_U32LE(ctx, val->type);
+ MARSHAL_U32LE(ctx, val->bsize);
+ MARSHAL_U64LE(ctx, val->blocks);
+ MARSHAL_U64LE(ctx, val->bfree);
+ MARSHAL_U64LE(ctx, val->bavail);
+ MARSHAL_U64LE(ctx, val->files);
+ MARSHAL_U64LE(ctx, val->ffree);
+ MARSHAL_U64LE(ctx, val->fsid);
+ MARSHAL_U32LE(ctx, val->namelen);
+ return ERROR_NULL;
+}
+
+static error marshal_Tlopen(struct lib9p_ctx *ctx, struct lib9p_msg_Tlopen *val, struct _marshal_ret *ret) {
+ uint32_t needed_size = 15;
+ if (needed_size > ctx->max_msg_size) {
+ return error_new(E_POSIX_ERANGE, "Tlopen message too large to marshal into ",
+ ctx->version ? "negotiated" : "client", " limit",
+ " (", needed_size, " > ", ctx->max_msg_size, ")");
+ }
+ uint32_t offsetof_end = needed_size;
+ uint32_t offsetof_size = 0;
+ MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
+ MARSHAL_U8LE(ctx, 12);
+ MARSHAL_U16LE(ctx, val->tag);
+ MARSHAL_U32LE(ctx, val->fid);
+ MARSHAL_U32LE(ctx, val->flags & lo_masks[ctx->version]);
+ return ERROR_NULL;
+}
+
+static error marshal_Rlopen(struct lib9p_ctx *ctx, struct lib9p_msg_Rlopen *val, struct _marshal_ret *ret) {
+ uint32_t needed_size = 24;
+ if (needed_size > ctx->max_msg_size) {
+ return error_new(E_POSIX_ERANGE, "Rlopen message too large to marshal into ",
+ ctx->version ? "negotiated" : "server", " limit",
+ " (", needed_size, " > ", ctx->max_msg_size, ")");
+ }
+ uint32_t offsetof_end = needed_size;
+ uint32_t offsetof_size = 0;
+ MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
+ MARSHAL_U8LE(ctx, 13);
+ MARSHAL_U16LE(ctx, val->tag);
+ MARSHAL_U8LE(ctx, val->qid.type & qt_masks[ctx->version]);
+ MARSHAL_U32LE(ctx, val->qid.vers);
+ MARSHAL_U64LE(ctx, val->qid.path);
+ MARSHAL_U32LE(ctx, val->iounit);
+ return ERROR_NULL;
+}
+
+static error marshal_Tlcreate(struct lib9p_ctx *ctx, struct lib9p_msg_Tlcreate *val, struct _marshal_ret *ret) {
+ uint32_t needed_size = 25 + val->name.len;
+ if (needed_size > ctx->max_msg_size) {
+ return error_new(E_POSIX_ERANGE, "Tlcreate message too large to marshal into ",
+ ctx->version ? "negotiated" : "client", " limit",
+ " (", needed_size, " > ", ctx->max_msg_size, ")");
+ }
+ uint32_t offsetof_end = needed_size;
+ uint32_t offsetof_size = 0;
+ MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
+ MARSHAL_U8LE(ctx, 14);
+ MARSHAL_U16LE(ctx, val->tag);
+ MARSHAL_U32LE(ctx, val->fid);
+ MARSHAL_U16LE(ctx, val->name.len);
+ MARSHAL_BYTES_ZEROCOPY(ctx, val->name.utf8, val->name.len);
+ MARSHAL_U32LE(ctx, val->flags & lo_masks[ctx->version]);
+ MARSHAL_U32LE(ctx, val->mode & mode_masks[ctx->version]);
+ MARSHAL_U32LE(ctx, val->gid);
+ return ERROR_NULL;
+}
+
+static error marshal_Rlcreate(struct lib9p_ctx *ctx, struct lib9p_msg_Rlcreate *val, struct _marshal_ret *ret) {
+ uint32_t needed_size = 24;
+ if (needed_size > ctx->max_msg_size) {
+ return error_new(E_POSIX_ERANGE, "Rlcreate message too large to marshal into ",
+ ctx->version ? "negotiated" : "server", " limit",
+ " (", needed_size, " > ", ctx->max_msg_size, ")");
+ }
+ uint32_t offsetof_end = needed_size;
+ uint32_t offsetof_size = 0;
+ MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
+ MARSHAL_U8LE(ctx, 15);
+ MARSHAL_U16LE(ctx, val->tag);
+ MARSHAL_U8LE(ctx, val->qid.type & qt_masks[ctx->version]);
+ MARSHAL_U32LE(ctx, val->qid.vers);
+ MARSHAL_U64LE(ctx, val->qid.path);
+ MARSHAL_U32LE(ctx, val->iounit);
+ return ERROR_NULL;
+}
+
+static error marshal_Tsymlink(struct lib9p_ctx *ctx, struct lib9p_msg_Tsymlink *val, struct _marshal_ret *ret) {
+ uint32_t needed_size = 19 + val->name.len + val->symtgt.len;
+ if (needed_size > ctx->max_msg_size) {
+ return error_new(E_POSIX_ERANGE, "Tsymlink message too large to marshal into ",
+ ctx->version ? "negotiated" : "client", " limit",
+ " (", needed_size, " > ", ctx->max_msg_size, ")");
+ }
+ uint32_t offsetof_end = needed_size;
+ uint32_t offsetof_size = 0;
+ MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
+ MARSHAL_U8LE(ctx, 16);
+ MARSHAL_U16LE(ctx, val->tag);
+ MARSHAL_U32LE(ctx, val->fid);
+ MARSHAL_U16LE(ctx, val->name.len);
+ MARSHAL_BYTES_ZEROCOPY(ctx, val->name.utf8, val->name.len);
+ MARSHAL_U16LE(ctx, val->symtgt.len);
+ MARSHAL_BYTES_ZEROCOPY(ctx, val->symtgt.utf8, val->symtgt.len);
+ MARSHAL_U32LE(ctx, val->gid);
+ return ERROR_NULL;
+}
+
+static error marshal_Rsymlink(struct lib9p_ctx *ctx, struct lib9p_msg_Rsymlink *val, struct _marshal_ret *ret) {
+ uint32_t needed_size = 20;
+ if (needed_size > ctx->max_msg_size) {
+ return error_new(E_POSIX_ERANGE, "Rsymlink message too large to marshal into ",
+ ctx->version ? "negotiated" : "server", " limit",
+ " (", needed_size, " > ", ctx->max_msg_size, ")");
+ }
+ uint32_t offsetof_end = needed_size;
+ uint32_t offsetof_size = 0;
+ MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
+ MARSHAL_U8LE(ctx, 17);
+ MARSHAL_U16LE(ctx, val->tag);
+ MARSHAL_U8LE(ctx, val->qid.type & qt_masks[ctx->version]);
+ MARSHAL_U32LE(ctx, val->qid.vers);
+ MARSHAL_U64LE(ctx, val->qid.path);
+ return ERROR_NULL;
+}
+
+static error marshal_Tmknod(struct lib9p_ctx *ctx, struct lib9p_msg_Tmknod *val, struct _marshal_ret *ret) {
+ uint32_t needed_size = 29 + val->name.len;
+ if (needed_size > ctx->max_msg_size) {
+ return error_new(E_POSIX_ERANGE, "Tmknod message too large to marshal into ",
+ ctx->version ? "negotiated" : "client", " limit",
+ " (", needed_size, " > ", ctx->max_msg_size, ")");
+ }
+ uint32_t offsetof_end = needed_size;
+ uint32_t offsetof_size = 0;
+ MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
+ MARSHAL_U8LE(ctx, 18);
+ MARSHAL_U16LE(ctx, val->tag);
+ MARSHAL_U32LE(ctx, val->dfid);
+ MARSHAL_U16LE(ctx, val->name.len);
+ MARSHAL_BYTES_ZEROCOPY(ctx, val->name.utf8, val->name.len);
+ MARSHAL_U32LE(ctx, val->mode & mode_masks[ctx->version]);
+ MARSHAL_U32LE(ctx, val->major);
+ MARSHAL_U32LE(ctx, val->minor);
+ MARSHAL_U32LE(ctx, val->gid);
+ return ERROR_NULL;
+}
+
+static error marshal_Rmknod(struct lib9p_ctx *ctx, struct lib9p_msg_Rmknod *val, struct _marshal_ret *ret) {
+ uint32_t needed_size = 20;
+ if (needed_size > ctx->max_msg_size) {
+ return error_new(E_POSIX_ERANGE, "Rmknod message too large to marshal into ",
+ ctx->version ? "negotiated" : "server", " limit",
+ " (", needed_size, " > ", ctx->max_msg_size, ")");
+ }
+ uint32_t offsetof_end = needed_size;
+ uint32_t offsetof_size = 0;
+ MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
+ MARSHAL_U8LE(ctx, 19);
+ MARSHAL_U16LE(ctx, val->tag);
+ MARSHAL_U8LE(ctx, val->qid.type & qt_masks[ctx->version]);
+ MARSHAL_U32LE(ctx, val->qid.vers);
+ MARSHAL_U64LE(ctx, val->qid.path);
+ return ERROR_NULL;
+}
+
+static error marshal_Trename(struct lib9p_ctx *ctx, struct lib9p_msg_Trename *val, struct _marshal_ret *ret) {
+ uint32_t needed_size = 17 + val->name.len;
+ if (needed_size > ctx->max_msg_size) {
+ return error_new(E_POSIX_ERANGE, "Trename message too large to marshal into ",
+ ctx->version ? "negotiated" : "client", " limit",
+ " (", needed_size, " > ", ctx->max_msg_size, ")");
+ }
+ uint32_t offsetof_end = needed_size;
+ uint32_t offsetof_size = 0;
+ MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
+ MARSHAL_U8LE(ctx, 20);
+ MARSHAL_U16LE(ctx, val->tag);
+ MARSHAL_U32LE(ctx, val->fid);
+ MARSHAL_U32LE(ctx, val->dfid);
+ MARSHAL_U16LE(ctx, val->name.len);
+ MARSHAL_BYTES_ZEROCOPY(ctx, val->name.utf8, val->name.len);
+ return ERROR_NULL;
+}
+
+static error marshal_Rrename(struct lib9p_ctx *ctx, struct lib9p_msg_Rrename *val, struct _marshal_ret *ret) {
+ uint32_t needed_size = 7;
+ if (needed_size > ctx->max_msg_size) {
+ return error_new(E_POSIX_ERANGE, "Rrename message too large to marshal into ",
+ ctx->version ? "negotiated" : "server", " limit",
+ " (", needed_size, " > ", ctx->max_msg_size, ")");
+ }
+ uint32_t offsetof_end = needed_size;
+ uint32_t offsetof_size = 0;
+ MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
+ MARSHAL_U8LE(ctx, 21);
+ MARSHAL_U16LE(ctx, val->tag);
+ return ERROR_NULL;
+}
+
+static error marshal_Treadlink(struct lib9p_ctx *ctx, struct lib9p_msg_Treadlink *val, struct _marshal_ret *ret) {
+ uint32_t needed_size = 11;
+ if (needed_size > ctx->max_msg_size) {
+ return error_new(E_POSIX_ERANGE, "Treadlink message too large to marshal into ",
+ ctx->version ? "negotiated" : "client", " limit",
+ " (", needed_size, " > ", ctx->max_msg_size, ")");
+ }
+ uint32_t offsetof_end = needed_size;
+ uint32_t offsetof_size = 0;
+ MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
+ MARSHAL_U8LE(ctx, 22);
+ MARSHAL_U16LE(ctx, val->tag);
+ MARSHAL_U32LE(ctx, val->fid);
+ return ERROR_NULL;
+}
+
+static error marshal_Rreadlink(struct lib9p_ctx *ctx, struct lib9p_msg_Rreadlink *val, struct _marshal_ret *ret) {
+ uint32_t needed_size = 9 + val->target.len;
+ if (needed_size > ctx->max_msg_size) {
+ return error_new(E_POSIX_ERANGE, "Rreadlink message too large to marshal into ",
+ ctx->version ? "negotiated" : "server", " limit",
+ " (", needed_size, " > ", ctx->max_msg_size, ")");
+ }
+ uint32_t offsetof_end = needed_size;
+ uint32_t offsetof_size = 0;
+ MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
+ MARSHAL_U8LE(ctx, 23);
+ MARSHAL_U16LE(ctx, val->tag);
+ MARSHAL_U16LE(ctx, val->target.len);
+ MARSHAL_BYTES_ZEROCOPY(ctx, val->target.utf8, val->target.len);
+ return ERROR_NULL;
+}
+
+static error marshal_Tgetattr(struct lib9p_ctx *ctx, struct lib9p_msg_Tgetattr *val, struct _marshal_ret *ret) {
+ uint32_t needed_size = 19;
+ if (needed_size > ctx->max_msg_size) {
+ return error_new(E_POSIX_ERANGE, "Tgetattr message too large to marshal into ",
+ ctx->version ? "negotiated" : "client", " limit",
+ " (", needed_size, " > ", ctx->max_msg_size, ")");
+ }
+ uint32_t offsetof_end = needed_size;
+ uint32_t offsetof_size = 0;
+ MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
+ MARSHAL_U8LE(ctx, 24);
+ MARSHAL_U16LE(ctx, val->tag);
+ MARSHAL_U32LE(ctx, val->fid);
+ MARSHAL_U64LE(ctx, val->request_mask & getattr_masks[ctx->version]);
+ return ERROR_NULL;
+}
+
+static error marshal_Rgetattr(struct lib9p_ctx *ctx, struct lib9p_msg_Rgetattr *val, struct _marshal_ret *ret) {
+ uint32_t needed_size = 160;
+ if (needed_size > ctx->max_msg_size) {
+ return error_new(E_POSIX_ERANGE, "Rgetattr message too large to marshal into ",
+ ctx->version ? "negotiated" : "server", " limit",
+ " (", needed_size, " > ", ctx->max_msg_size, ")");
+ }
+ uint32_t offsetof_end = needed_size;
+ uint32_t offsetof_size = 0;
+ MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
+ MARSHAL_U8LE(ctx, 25);
+ MARSHAL_U16LE(ctx, val->tag);
+ MARSHAL_U64LE(ctx, val->valid & getattr_masks[ctx->version]);
+ MARSHAL_U8LE(ctx, val->qid.type & qt_masks[ctx->version]);
+ MARSHAL_U32LE(ctx, val->qid.vers);
+ MARSHAL_U64LE(ctx, val->qid.path);
+ MARSHAL_U32LE(ctx, val->mode & mode_masks[ctx->version]);
+ MARSHAL_U32LE(ctx, val->uid);
+ MARSHAL_U32LE(ctx, val->gid);
+ MARSHAL_U64LE(ctx, val->nlink);
+ MARSHAL_U64LE(ctx, val->rdev);
+ MARSHAL_U64LE(ctx, val->filesize);
+ MARSHAL_U64LE(ctx, val->blksize);
+ MARSHAL_U64LE(ctx, val->blocks);
+ MARSHAL_U64LE(ctx, val->atime_sec);
+ MARSHAL_U64LE(ctx, val->atime_nsec);
+ MARSHAL_U64LE(ctx, val->mtime_sec);
+ MARSHAL_U64LE(ctx, val->mtime_nsec);
+ MARSHAL_U64LE(ctx, val->ctime_sec);
+ MARSHAL_U64LE(ctx, val->ctime_nsec);
+ MARSHAL_U64LE(ctx, val->btime_sec);
+ MARSHAL_U64LE(ctx, val->btime_nsec);
+ MARSHAL_U64LE(ctx, val->gen);
+ MARSHAL_U64LE(ctx, val->data_version);
+ return ERROR_NULL;
+}
+
+static error marshal_Tsetattr(struct lib9p_ctx *ctx, struct lib9p_msg_Tsetattr *val, struct _marshal_ret *ret) {
+ uint32_t needed_size = 67;
+ if (needed_size > ctx->max_msg_size) {
+ return error_new(E_POSIX_ERANGE, "Tsetattr message too large to marshal into ",
+ ctx->version ? "negotiated" : "client", " limit",
+ " (", needed_size, " > ", ctx->max_msg_size, ")");
+ }
+ uint32_t offsetof_end = needed_size;
+ uint32_t offsetof_size = 0;
+ MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
+ MARSHAL_U8LE(ctx, 26);
+ MARSHAL_U16LE(ctx, val->tag);
+ MARSHAL_U32LE(ctx, val->fid);
+ MARSHAL_U32LE(ctx, val->valid & setattr_masks[ctx->version]);
+ MARSHAL_U32LE(ctx, val->mode & mode_masks[ctx->version]);
+ MARSHAL_U32LE(ctx, val->uid);
+ MARSHAL_U32LE(ctx, val->gid);
+ MARSHAL_U64LE(ctx, val->filesize);
+ MARSHAL_U64LE(ctx, val->atime_sec);
+ MARSHAL_U64LE(ctx, val->atime_nsec);
+ MARSHAL_U64LE(ctx, val->mtime_sec);
+ MARSHAL_U64LE(ctx, val->mtime_nsec);
+ return ERROR_NULL;
+}
+
+static error marshal_Rsetattr(struct lib9p_ctx *ctx, struct lib9p_msg_Rsetattr *val, struct _marshal_ret *ret) {
+ uint32_t needed_size = 7;
+ if (needed_size > ctx->max_msg_size) {
+ return error_new(E_POSIX_ERANGE, "Rsetattr message too large to marshal into ",
+ ctx->version ? "negotiated" : "server", " limit",
+ " (", needed_size, " > ", ctx->max_msg_size, ")");
+ }
+ uint32_t offsetof_end = needed_size;
+ uint32_t offsetof_size = 0;
+ MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
+ MARSHAL_U8LE(ctx, 27);
+ MARSHAL_U16LE(ctx, val->tag);
+ return ERROR_NULL;
+}
+
+static error marshal_Txattrwalk(struct lib9p_ctx *ctx, struct lib9p_msg_Txattrwalk *val, struct _marshal_ret *ret) {
+ uint32_t needed_size = 17 + val->name.len;
+ if (needed_size > ctx->max_msg_size) {
+ return error_new(E_POSIX_ERANGE, "Txattrwalk message too large to marshal into ",
+ ctx->version ? "negotiated" : "client", " limit",
+ " (", needed_size, " > ", ctx->max_msg_size, ")");
+ }
+ uint32_t offsetof_end = needed_size;
+ uint32_t offsetof_size = 0;
+ MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
+ MARSHAL_U8LE(ctx, 30);
+ MARSHAL_U16LE(ctx, val->tag);
+ MARSHAL_U32LE(ctx, val->fid);
+ MARSHAL_U32LE(ctx, val->newfid);
+ MARSHAL_U16LE(ctx, val->name.len);
+ MARSHAL_BYTES_ZEROCOPY(ctx, val->name.utf8, val->name.len);
+ return ERROR_NULL;
+}
+
+static error marshal_Rxattrwalk(struct lib9p_ctx *ctx, struct lib9p_msg_Rxattrwalk *val, struct _marshal_ret *ret) {
+ uint32_t needed_size = 15;
+ if (needed_size > ctx->max_msg_size) {
+ return error_new(E_POSIX_ERANGE, "Rxattrwalk message too large to marshal into ",
+ ctx->version ? "negotiated" : "server", " limit",
+ " (", needed_size, " > ", ctx->max_msg_size, ")");
+ }
+ uint32_t offsetof_end = needed_size;
+ uint32_t offsetof_size = 0;
+ MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
+ MARSHAL_U8LE(ctx, 31);
+ MARSHAL_U16LE(ctx, val->tag);
+ MARSHAL_U64LE(ctx, val->attr_size);
+ return ERROR_NULL;
+}
+
+static error marshal_Txattrcreate(struct lib9p_ctx *ctx, struct lib9p_msg_Txattrcreate *val, struct _marshal_ret *ret) {
+ uint32_t needed_size = 25 + val->name.len;
+ if (needed_size > ctx->max_msg_size) {
+ return error_new(E_POSIX_ERANGE, "Txattrcreate message too large to marshal into ",
+ ctx->version ? "negotiated" : "client", " limit",
+ " (", needed_size, " > ", ctx->max_msg_size, ")");
+ }
+ uint32_t offsetof_end = needed_size;
+ uint32_t offsetof_size = 0;
+ MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
+ MARSHAL_U8LE(ctx, 32);
+ MARSHAL_U16LE(ctx, val->tag);
+ MARSHAL_U32LE(ctx, val->fid);
+ MARSHAL_U16LE(ctx, val->name.len);
+ MARSHAL_BYTES_ZEROCOPY(ctx, val->name.utf8, val->name.len);
+ MARSHAL_U64LE(ctx, val->attr_size);
+ MARSHAL_U32LE(ctx, val->flags);
+ return ERROR_NULL;
+}
+
+static error marshal_Rxattrcreate(struct lib9p_ctx *ctx, struct lib9p_msg_Rxattrcreate *val, struct _marshal_ret *ret) {
+ uint32_t needed_size = 7;
+ if (needed_size > ctx->max_msg_size) {
+ return error_new(E_POSIX_ERANGE, "Rxattrcreate message too large to marshal into ",
+ ctx->version ? "negotiated" : "server", " limit",
+ " (", needed_size, " > ", ctx->max_msg_size, ")");
+ }
+ uint32_t offsetof_end = needed_size;
+ uint32_t offsetof_size = 0;
+ MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
+ MARSHAL_U8LE(ctx, 33);
+ MARSHAL_U16LE(ctx, val->tag);
+ return ERROR_NULL;
+}
+
+static error marshal_Treaddir(struct lib9p_ctx *ctx, struct lib9p_msg_Treaddir *val, struct _marshal_ret *ret) {
+ uint32_t needed_size = 23;
+ if (needed_size > ctx->max_msg_size) {
+ return error_new(E_POSIX_ERANGE, "Treaddir message too large to marshal into ",
+ ctx->version ? "negotiated" : "client", " limit",
+ " (", needed_size, " > ", ctx->max_msg_size, ")");
+ }
+ uint32_t offsetof_end = needed_size;
+ uint32_t offsetof_size = 0;
+ MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
+ MARSHAL_U8LE(ctx, 40);
+ MARSHAL_U16LE(ctx, val->tag);
+ MARSHAL_U32LE(ctx, val->fid);
+ MARSHAL_U64LE(ctx, val->offset);
+ MARSHAL_U32LE(ctx, val->count);
+ return ERROR_NULL;
+}
+
+static error marshal_Rreaddir(struct lib9p_ctx *ctx, struct lib9p_msg_Rreaddir *val, struct _marshal_ret *ret) {
+ uint64_t needed_size = 11 + val->count;
+ if (needed_size > (uint64_t)(ctx->max_msg_size)) {
+ return error_new(E_POSIX_ERANGE, "Rreaddir message too large to marshal into ",
+ ctx->version ? "negotiated" : "server", " limit",
+ " (", needed_size, " > ", ctx->max_msg_size, ")");
+ }
+ uint32_t offsetof_end = (uint32_t)needed_size;
+ uint32_t offsetof_size = 0;
+ MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
+ MARSHAL_U8LE(ctx, 41);
+ MARSHAL_U16LE(ctx, val->tag);
+ MARSHAL_U32LE(ctx, val->count);
+ MARSHAL_BYTES_ZEROCOPY(ctx, val->data, val->count);
+ return ERROR_NULL;
+}
+
+static error marshal_Tfsync(struct lib9p_ctx *ctx, struct lib9p_msg_Tfsync *val, struct _marshal_ret *ret) {
+ uint32_t needed_size = 15;
+ if (needed_size > ctx->max_msg_size) {
+ return error_new(E_POSIX_ERANGE, "Tfsync message too large to marshal into ",
+ ctx->version ? "negotiated" : "client", " limit",
+ " (", needed_size, " > ", ctx->max_msg_size, ")");
+ }
+ uint32_t offsetof_end = needed_size;
+ uint32_t offsetof_size = 0;
+ MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
+ MARSHAL_U8LE(ctx, 50);
+ MARSHAL_U16LE(ctx, val->tag);
+ MARSHAL_U32LE(ctx, val->fid);
+ MARSHAL_U32LE(ctx, val->datasync);
+ return ERROR_NULL;
+}
+
+static error marshal_Rfsync(struct lib9p_ctx *ctx, struct lib9p_msg_Rfsync *val, struct _marshal_ret *ret) {
+ uint32_t needed_size = 7;
+ if (needed_size > ctx->max_msg_size) {
+ return error_new(E_POSIX_ERANGE, "Rfsync message too large to marshal into ",
+ ctx->version ? "negotiated" : "server", " limit",
+ " (", needed_size, " > ", ctx->max_msg_size, ")");
+ }
+ uint32_t offsetof_end = needed_size;
+ uint32_t offsetof_size = 0;
+ MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
+ MARSHAL_U8LE(ctx, 51);
+ MARSHAL_U16LE(ctx, val->tag);
+ return ERROR_NULL;
+}
+
+static error marshal_Tlock(struct lib9p_ctx *ctx, struct lib9p_msg_Tlock *val, struct _marshal_ret *ret) {
+ uint32_t needed_size = 38 + val->client_id.len;
+ if (needed_size > ctx->max_msg_size) {
+ return error_new(E_POSIX_ERANGE, "Tlock message too large to marshal into ",
+ ctx->version ? "negotiated" : "client", " limit",
+ " (", needed_size, " > ", ctx->max_msg_size, ")");
+ }
+ uint32_t offsetof_end = needed_size;
+ uint32_t offsetof_size = 0;
+ MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
+ MARSHAL_U8LE(ctx, 52);
+ MARSHAL_U16LE(ctx, val->tag);
+ MARSHAL_U32LE(ctx, val->fid);
+ MARSHAL_U8LE(ctx, val->type);
+ MARSHAL_U32LE(ctx, val->flags & lock_flags_masks[ctx->version]);
+ MARSHAL_U64LE(ctx, val->start);
+ MARSHAL_U64LE(ctx, val->length);
+ MARSHAL_U32LE(ctx, val->proc_id);
+ MARSHAL_U16LE(ctx, val->client_id.len);
+ MARSHAL_BYTES_ZEROCOPY(ctx, val->client_id.utf8, val->client_id.len);
+ return ERROR_NULL;
+}
+
+static error marshal_Rlock(struct lib9p_ctx *ctx, struct lib9p_msg_Rlock *val, struct _marshal_ret *ret) {
+ uint32_t needed_size = 8;
+ if (needed_size > ctx->max_msg_size) {
+ return error_new(E_POSIX_ERANGE, "Rlock message too large to marshal into ",
+ ctx->version ? "negotiated" : "server", " limit",
+ " (", needed_size, " > ", ctx->max_msg_size, ")");
+ }
+ uint32_t offsetof_end = needed_size;
+ uint32_t offsetof_size = 0;
+ MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
+ MARSHAL_U8LE(ctx, 53);
+ MARSHAL_U16LE(ctx, val->tag);
+ MARSHAL_U8LE(ctx, val->status);
+ return ERROR_NULL;
+}
+
+static error marshal_Tgetlock(struct lib9p_ctx *ctx, struct lib9p_msg_Tgetlock *val, struct _marshal_ret *ret) {
+ uint32_t needed_size = 34 + val->client_id.len;
+ if (needed_size > ctx->max_msg_size) {
+ return error_new(E_POSIX_ERANGE, "Tgetlock message too large to marshal into ",
+ ctx->version ? "negotiated" : "client", " limit",
+ " (", needed_size, " > ", ctx->max_msg_size, ")");
+ }
+ uint32_t offsetof_end = needed_size;
+ uint32_t offsetof_size = 0;
+ MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
+ MARSHAL_U8LE(ctx, 54);
+ MARSHAL_U16LE(ctx, val->tag);
+ MARSHAL_U32LE(ctx, val->fid);
+ MARSHAL_U8LE(ctx, val->type);
+ MARSHAL_U64LE(ctx, val->start);
+ MARSHAL_U64LE(ctx, val->length);
+ MARSHAL_U32LE(ctx, val->proc_id);
+ MARSHAL_U16LE(ctx, val->client_id.len);
+ MARSHAL_BYTES_ZEROCOPY(ctx, val->client_id.utf8, val->client_id.len);
+ return ERROR_NULL;
+}
+
+static error marshal_Rgetlock(struct lib9p_ctx *ctx, struct lib9p_msg_Rgetlock *val, struct _marshal_ret *ret) {
+ uint32_t needed_size = 30 + val->client_id.len;
+ if (needed_size > ctx->max_msg_size) {
+ return error_new(E_POSIX_ERANGE, "Rgetlock message too large to marshal into ",
+ ctx->version ? "negotiated" : "server", " limit",
+ " (", needed_size, " > ", ctx->max_msg_size, ")");
+ }
+ uint32_t offsetof_end = needed_size;
+ uint32_t offsetof_size = 0;
+ MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
+ MARSHAL_U8LE(ctx, 55);
+ MARSHAL_U16LE(ctx, val->tag);
+ MARSHAL_U8LE(ctx, val->type);
+ MARSHAL_U64LE(ctx, val->start);
+ MARSHAL_U64LE(ctx, val->length);
+ MARSHAL_U32LE(ctx, val->proc_id);
+ MARSHAL_U16LE(ctx, val->client_id.len);
+ MARSHAL_BYTES_ZEROCOPY(ctx, val->client_id.utf8, val->client_id.len);
+ return ERROR_NULL;
+}
+
+static error marshal_Tlink(struct lib9p_ctx *ctx, struct lib9p_msg_Tlink *val, struct _marshal_ret *ret) {
+ uint32_t needed_size = 17 + val->name.len;
+ if (needed_size > ctx->max_msg_size) {
+ return error_new(E_POSIX_ERANGE, "Tlink message too large to marshal into ",
+ ctx->version ? "negotiated" : "client", " limit",
+ " (", needed_size, " > ", ctx->max_msg_size, ")");
+ }
+ uint32_t offsetof_end = needed_size;
+ uint32_t offsetof_size = 0;
+ MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
+ MARSHAL_U8LE(ctx, 70);
+ MARSHAL_U16LE(ctx, val->tag);
+ MARSHAL_U32LE(ctx, val->dfid);
+ MARSHAL_U32LE(ctx, val->fid);
+ MARSHAL_U16LE(ctx, val->name.len);
+ MARSHAL_BYTES_ZEROCOPY(ctx, val->name.utf8, val->name.len);
+ return ERROR_NULL;
+}
+
+static error marshal_Rlink(struct lib9p_ctx *ctx, struct lib9p_msg_Rlink *val, struct _marshal_ret *ret) {
+ uint32_t needed_size = 7;
+ if (needed_size > ctx->max_msg_size) {
+ return error_new(E_POSIX_ERANGE, "Rlink message too large to marshal into ",
+ ctx->version ? "negotiated" : "server", " limit",
+ " (", needed_size, " > ", ctx->max_msg_size, ")");
+ }
+ uint32_t offsetof_end = needed_size;
+ uint32_t offsetof_size = 0;
+ MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
+ MARSHAL_U8LE(ctx, 71);
+ MARSHAL_U16LE(ctx, val->tag);
+ return ERROR_NULL;
+}
+
+static error marshal_Tmkdir(struct lib9p_ctx *ctx, struct lib9p_msg_Tmkdir *val, struct _marshal_ret *ret) {
+ uint32_t needed_size = 21 + val->name.len;
+ if (needed_size > ctx->max_msg_size) {
+ return error_new(E_POSIX_ERANGE, "Tmkdir message too large to marshal into ",
+ ctx->version ? "negotiated" : "client", " limit",
+ " (", needed_size, " > ", ctx->max_msg_size, ")");
+ }
+ uint32_t offsetof_end = needed_size;
+ uint32_t offsetof_size = 0;
+ MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
+ MARSHAL_U8LE(ctx, 72);
+ MARSHAL_U16LE(ctx, val->tag);
+ MARSHAL_U32LE(ctx, val->dfid);
+ MARSHAL_U16LE(ctx, val->name.len);
+ MARSHAL_BYTES_ZEROCOPY(ctx, val->name.utf8, val->name.len);
+ MARSHAL_U32LE(ctx, val->mode & mode_masks[ctx->version]);
+ MARSHAL_U32LE(ctx, val->gid);
+ return ERROR_NULL;
+}
+
+static error marshal_Rmkdir(struct lib9p_ctx *ctx, struct lib9p_msg_Rmkdir *val, struct _marshal_ret *ret) {
+ uint32_t needed_size = 20;
+ if (needed_size > ctx->max_msg_size) {
+ return error_new(E_POSIX_ERANGE, "Rmkdir message too large to marshal into ",
+ ctx->version ? "negotiated" : "server", " limit",
+ " (", needed_size, " > ", ctx->max_msg_size, ")");
+ }
+ uint32_t offsetof_end = needed_size;
+ uint32_t offsetof_size = 0;
+ MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
+ MARSHAL_U8LE(ctx, 73);
+ MARSHAL_U16LE(ctx, val->tag);
+ MARSHAL_U8LE(ctx, val->qid.type & qt_masks[ctx->version]);
+ MARSHAL_U32LE(ctx, val->qid.vers);
+ MARSHAL_U64LE(ctx, val->qid.path);
+ return ERROR_NULL;
+}
+
+static error marshal_Trenameat(struct lib9p_ctx *ctx, struct lib9p_msg_Trenameat *val, struct _marshal_ret *ret) {
+ uint32_t needed_size = 19 + val->oldname.len + val->newname.len;
+ if (needed_size > ctx->max_msg_size) {
+ return error_new(E_POSIX_ERANGE, "Trenameat message too large to marshal into ",
+ ctx->version ? "negotiated" : "client", " limit",
+ " (", needed_size, " > ", ctx->max_msg_size, ")");
+ }
+ uint32_t offsetof_end = needed_size;
+ uint32_t offsetof_size = 0;
+ MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
+ MARSHAL_U8LE(ctx, 74);
+ MARSHAL_U16LE(ctx, val->tag);
+ MARSHAL_U32LE(ctx, val->olddirfid);
+ MARSHAL_U16LE(ctx, val->oldname.len);
+ MARSHAL_BYTES_ZEROCOPY(ctx, val->oldname.utf8, val->oldname.len);
+ MARSHAL_U32LE(ctx, val->newdirfid);
+ MARSHAL_U16LE(ctx, val->newname.len);
+ MARSHAL_BYTES_ZEROCOPY(ctx, val->newname.utf8, val->newname.len);
+ return ERROR_NULL;
+}
+
+static error marshal_Rrenameat(struct lib9p_ctx *ctx, struct lib9p_msg_Rrenameat *val, struct _marshal_ret *ret) {
+ uint32_t needed_size = 7;
+ if (needed_size > ctx->max_msg_size) {
+ return error_new(E_POSIX_ERANGE, "Rrenameat message too large to marshal into ",
+ ctx->version ? "negotiated" : "server", " limit",
+ " (", needed_size, " > ", ctx->max_msg_size, ")");
+ }
+ uint32_t offsetof_end = needed_size;
+ uint32_t offsetof_size = 0;
+ MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
+ MARSHAL_U8LE(ctx, 75);
+ MARSHAL_U16LE(ctx, val->tag);
+ return ERROR_NULL;
+}
+
+static error marshal_Tunlinkat(struct lib9p_ctx *ctx, struct lib9p_msg_Tunlinkat *val, struct _marshal_ret *ret) {
+ uint32_t needed_size = 17 + val->name.len;
+ if (needed_size > ctx->max_msg_size) {
+ return error_new(E_POSIX_ERANGE, "Tunlinkat message too large to marshal into ",
+ ctx->version ? "negotiated" : "client", " limit",
+ " (", needed_size, " > ", ctx->max_msg_size, ")");
+ }
+ uint32_t offsetof_end = needed_size;
+ uint32_t offsetof_size = 0;
+ MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
+ MARSHAL_U8LE(ctx, 76);
+ MARSHAL_U16LE(ctx, val->tag);
+ MARSHAL_U32LE(ctx, val->dirfd);
+ MARSHAL_U16LE(ctx, val->name.len);
+ MARSHAL_BYTES_ZEROCOPY(ctx, val->name.utf8, val->name.len);
+ MARSHAL_U32LE(ctx, val->flags);
+ return ERROR_NULL;
+}
+
+static error marshal_Runlinkat(struct lib9p_ctx *ctx, struct lib9p_msg_Runlinkat *val, struct _marshal_ret *ret) {
+ uint32_t needed_size = 7;
+ if (needed_size > ctx->max_msg_size) {
+ return error_new(E_POSIX_ERANGE, "Runlinkat message too large to marshal into ",
+ ctx->version ? "negotiated" : "server", " limit",
+ " (", needed_size, " > ", ctx->max_msg_size, ")");
+ }
+ uint32_t offsetof_end = needed_size;
+ uint32_t offsetof_size = 0;
+ MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
+ MARSHAL_U8LE(ctx, 77);
+ MARSHAL_U16LE(ctx, val->tag);
+ return ERROR_NULL;
+}
+
+#endif /* CONFIG_9P_ENABLE_9P2000_L */
+#if CONFIG_9P_ENABLE_9P2000_e
+static error marshal_Tsession(struct lib9p_ctx *ctx, struct lib9p_msg_Tsession *val, struct _marshal_ret *ret) {
+ uint32_t needed_size = 15;
+ if (needed_size > ctx->max_msg_size) {
+ return error_new(E_POSIX_ERANGE, "Tsession message too large to marshal into ",
+ ctx->version ? "negotiated" : "client", " limit",
+ " (", needed_size, " > ", ctx->max_msg_size, ")");
+ }
+ uint32_t offsetof_end = needed_size;
+ uint32_t offsetof_size = 0;
+ MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
+ MARSHAL_U8LE(ctx, 150);
+ MARSHAL_U16LE(ctx, val->tag);
+ MARSHAL_U64LE(ctx, val->key);
+ return ERROR_NULL;
+}
+
+static error marshal_Rsession(struct lib9p_ctx *ctx, struct lib9p_msg_Rsession *val, struct _marshal_ret *ret) {
+ uint32_t needed_size = 7;
+ if (needed_size > ctx->max_msg_size) {
+ return error_new(E_POSIX_ERANGE, "Rsession message too large to marshal into ",
+ ctx->version ? "negotiated" : "server", " limit",
+ " (", needed_size, " > ", ctx->max_msg_size, ")");
+ }
+ uint32_t offsetof_end = needed_size;
+ uint32_t offsetof_size = 0;
+ MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
+ MARSHAL_U8LE(ctx, 151);
+ MARSHAL_U16LE(ctx, val->tag);
+ return ERROR_NULL;
+}
+
+static error marshal_Tsread(struct lib9p_ctx *ctx, struct lib9p_msg_Tsread *val, struct _marshal_ret *ret) {
+ uint64_t needed_size = 13;
+ for (uint16_t i = 0; i < val->nwname; i++) {
+ needed_size += 2 + val->wname[i].len;
+ }
+ if (needed_size > (uint64_t)(ctx->max_msg_size)) {
+ return error_new(E_POSIX_ERANGE, "Tsread message too large to marshal into ",
+ ctx->version ? "negotiated" : "client", " limit",
+ " (", needed_size, " > ", ctx->max_msg_size, ")");
+ }
+ uint32_t offsetof_end = (uint32_t)needed_size;
+ uint32_t offsetof_size = 0;
+ MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
+ MARSHAL_U8LE(ctx, 152);
+ MARSHAL_U16LE(ctx, val->tag);
+ MARSHAL_U32LE(ctx, val->fid);
+ MARSHAL_U16LE(ctx, val->nwname);
+ for (uint16_t i = 0; i < val->nwname; i++) {
+ MARSHAL_U16LE(ctx, val->wname[i].len);
+ MARSHAL_BYTES_ZEROCOPY(ctx, val->wname[i].utf8, val->wname[i].len);
+ }
+ return ERROR_NULL;
+}
+
+static error marshal_Rsread(struct lib9p_ctx *ctx, struct lib9p_msg_Rsread *val, struct _marshal_ret *ret) {
+ uint64_t needed_size = 11 + val->count;
+ if (needed_size > (uint64_t)(ctx->max_msg_size)) {
+ return error_new(E_POSIX_ERANGE, "Rsread message too large to marshal into ",
+ ctx->version ? "negotiated" : "server", " limit",
+ " (", needed_size, " > ", ctx->max_msg_size, ")");
+ }
+ uint32_t offsetof_end = (uint32_t)needed_size;
+ uint32_t offsetof_size = 0;
+ MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
+ MARSHAL_U8LE(ctx, 153);
+ MARSHAL_U16LE(ctx, val->tag);
+ MARSHAL_U32LE(ctx, val->count);
+ MARSHAL_BYTES_ZEROCOPY(ctx, val->data, val->count);
+ return ERROR_NULL;
+}
+
+static error marshal_Tswrite(struct lib9p_ctx *ctx, struct lib9p_msg_Tswrite *val, struct _marshal_ret *ret) {
+ uint64_t needed_size = 17 + val->count;
+ for (uint16_t i = 0; i < val->nwname; i++) {
+ needed_size += 2 + val->wname[i].len;
+ }
+ if (needed_size > (uint64_t)(ctx->max_msg_size)) {
+ return error_new(E_POSIX_ERANGE, "Tswrite message too large to marshal into ",
+ ctx->version ? "negotiated" : "client", " limit",
+ " (", needed_size, " > ", ctx->max_msg_size, ")");
+ }
+ uint32_t offsetof_end = (uint32_t)needed_size;
+ uint32_t offsetof_size = 0;
+ MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
+ MARSHAL_U8LE(ctx, 154);
+ MARSHAL_U16LE(ctx, val->tag);
+ MARSHAL_U32LE(ctx, val->fid);
+ MARSHAL_U16LE(ctx, val->nwname);
+ for (uint16_t i = 0; i < val->nwname; i++) {
+ MARSHAL_U16LE(ctx, val->wname[i].len);
+ MARSHAL_BYTES_ZEROCOPY(ctx, val->wname[i].utf8, val->wname[i].len);
+ }
+ MARSHAL_U32LE(ctx, val->count);
+ MARSHAL_BYTES_ZEROCOPY(ctx, val->data, val->count);
+ return ERROR_NULL;
+}
+
+static error marshal_Rswrite(struct lib9p_ctx *ctx, struct lib9p_msg_Rswrite *val, struct _marshal_ret *ret) {
+ uint32_t needed_size = 11;
+ if (needed_size > ctx->max_msg_size) {
+ return error_new(E_POSIX_ERANGE, "Rswrite message too large to marshal into ",
+ ctx->version ? "negotiated" : "server", " limit",
+ " (", needed_size, " > ", ctx->max_msg_size, ")");
+ }
+ uint32_t offsetof_end = needed_size;
+ uint32_t offsetof_size = 0;
+ MARSHAL_U32LE(ctx, offsetof_end - offsetof_size);
+ MARSHAL_U8LE(ctx, 155);
+ MARSHAL_U16LE(ctx, val->tag);
+ MARSHAL_U32LE(ctx, val->count);
+ return ERROR_NULL;
+}
+#endif /* CONFIG_9P_ENABLE_9P2000_e */
+
+/* fmt_print_* ****************************************************************/
+
+#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u || CONFIG_9P_ENABLE_uninitialized
+[[maybe_unused]] static void fmt_print_tag(lo_interface fmt_dest w, [[maybe_unused]] struct lib9p_ctx *ctx, lib9p_tag_t *self) {
+ switch (*self) {
+ case LIB9P_TAG_NOTAG:
+ fmt_print_str(w, "NOTAG");
+ break;
+ default:
+ fmt_print_base10(w, *self);
+ }
+}
+
+[[maybe_unused]] static void fmt_print_fid(lo_interface fmt_dest w, [[maybe_unused]] struct lib9p_ctx *ctx, lib9p_fid_t *self) {
+ switch (*self) {
+ case LIB9P_FID_NOFID:
+ fmt_print_str(w, "NOFID");
+ break;
+ default:
+ fmt_print_base10(w, *self);
+ }
+}
+
+#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u || CONFIG_9P_ENABLE_uninitialized */
+#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u || CONFIG_9P_ENABLE_uninitialized
+[[maybe_unused]] static void fmt_print_dm(lo_interface fmt_dest w, [[maybe_unused]] struct lib9p_ctx *ctx, lib9p_dm_t *self) {
+ bool empty = true;
+ fmt_print_byte(w, '(');
+ if ((*self & ~((lib9p_dm_t)0777)) & (UINT32_C(1)<<31)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "DIR");
+ empty = false;
+ }
+ if ((*self & ~((lib9p_dm_t)0777)) & (UINT32_C(1)<<30)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "APPEND");
+ empty = false;
+ }
+ if ((*self & ~((lib9p_dm_t)0777)) & (UINT32_C(1)<<29)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "EXCL");
+ empty = false;
+ }
+ if ((*self & ~((lib9p_dm_t)0777)) & (UINT32_C(1)<<28)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "_PLAN9_MOUNT");
+ empty = false;
+ }
+ if ((*self & ~((lib9p_dm_t)0777)) & (UINT32_C(1)<<27)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "AUTH");
+ empty = false;
+ }
+ if ((*self & ~((lib9p_dm_t)0777)) & (UINT32_C(1)<<26)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "TMP");
+ empty = false;
+ }
+ if ((*self & ~((lib9p_dm_t)0777)) & (UINT32_C(1)<<25)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<25");
+ empty = false;
+ }
+ if ((*self & ~((lib9p_dm_t)0777)) & (UINT32_C(1)<<24)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<24");
+ empty = false;
+ }
+ if ((*self & ~((lib9p_dm_t)0777)) & (UINT32_C(1)<<23)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "DEVICE");
+ empty = false;
+ }
+ if ((*self & ~((lib9p_dm_t)0777)) & (UINT32_C(1)<<22)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<22");
+ empty = false;
+ }
+ if ((*self & ~((lib9p_dm_t)0777)) & (UINT32_C(1)<<21)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "PIPE");
+ empty = false;
+ }
+ if ((*self & ~((lib9p_dm_t)0777)) & (UINT32_C(1)<<20)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "SOCKET");
+ empty = false;
+ }
+ if ((*self & ~((lib9p_dm_t)0777)) & (UINT32_C(1)<<19)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "SETUID");
+ empty = false;
+ }
+ if ((*self & ~((lib9p_dm_t)0777)) & (UINT32_C(1)<<18)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "SETGID");
+ empty = false;
+ }
+ if ((*self & ~((lib9p_dm_t)0777)) & (UINT32_C(1)<<17)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<17");
+ empty = false;
+ }
+ if ((*self & ~((lib9p_dm_t)0777)) & (UINT32_C(1)<<16)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<16");
+ empty = false;
+ }
+ if ((*self & ~((lib9p_dm_t)0777)) & (UINT32_C(1)<<15)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<15");
+ empty = false;
+ }
+ if ((*self & ~((lib9p_dm_t)0777)) & (UINT32_C(1)<<14)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<14");
+ empty = false;
+ }
+ if ((*self & ~((lib9p_dm_t)0777)) & (UINT32_C(1)<<13)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<13");
+ empty = false;
+ }
+ if ((*self & ~((lib9p_dm_t)0777)) & (UINT32_C(1)<<12)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<12");
+ empty = false;
+ }
+ if ((*self & ~((lib9p_dm_t)0777)) & (UINT32_C(1)<<11)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<11");
+ empty = false;
+ }
+ if ((*self & ~((lib9p_dm_t)0777)) & (UINT32_C(1)<<10)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<10");
+ empty = false;
+ }
+ if ((*self & ~((lib9p_dm_t)0777)) & (UINT32_C(1)<<9)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<9");
+ empty = false;
+ }
+ if ((*self & ~((lib9p_dm_t)0777)) & (UINT32_C(1)<<8)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "OWNER_R");
+ empty = false;
+ }
+ if ((*self & ~((lib9p_dm_t)0777)) & (UINT32_C(1)<<7)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "OWNER_W");
+ empty = false;
+ }
+ if ((*self & ~((lib9p_dm_t)0777)) & (UINT32_C(1)<<6)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "OWNER_X");
+ empty = false;
+ }
+ if ((*self & ~((lib9p_dm_t)0777)) & (UINT32_C(1)<<5)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "GROUP_R");
+ empty = false;
+ }
+ if ((*self & ~((lib9p_dm_t)0777)) & (UINT32_C(1)<<4)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "GROUP_W");
+ empty = false;
+ }
+ if ((*self & ~((lib9p_dm_t)0777)) & (UINT32_C(1)<<3)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "GROUP_X");
+ empty = false;
+ }
+ if ((*self & ~((lib9p_dm_t)0777)) & (UINT32_C(1)<<2)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "OTHER_R");
+ empty = false;
+ }
+ if ((*self & ~((lib9p_dm_t)0777)) & (UINT32_C(1)<<1)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "OTHER_W");
+ empty = false;
+ }
+ if ((*self & ~((lib9p_dm_t)0777)) & (UINT32_C(1)<<0)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "OTHER_X");
+ empty = false;
+ }
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print(w, (rjust, 4, '0', (base8, *self & 0777)));
+ fmt_print_byte(w, ')');
+}
+
+#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u || CONFIG_9P_ENABLE_uninitialized */
+#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u || CONFIG_9P_ENABLE_uninitialized
+[[maybe_unused]] static void fmt_print_qt(lo_interface fmt_dest w, [[maybe_unused]] struct lib9p_ctx *ctx, lib9p_qt_t *self) {
+ bool empty = true;
+ fmt_print_byte(w, '(');
+ if (*self & (UINT8_C(1)<<7)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "DIR");
+ empty = false;
+ }
+ if (*self & (UINT8_C(1)<<6)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "APPEND");
+ empty = false;
+ }
+ if (*self & (UINT8_C(1)<<5)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "EXCL");
+ empty = false;
+ }
+ if (*self & (UINT8_C(1)<<4)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "_PLAN9_MOUNT");
+ empty = false;
+ }
+ if (*self & (UINT8_C(1)<<3)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "AUTH");
+ empty = false;
+ }
+ if (*self & (UINT8_C(1)<<2)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "TMP");
+ empty = false;
+ }
+ if (*self & (UINT8_C(1)<<1)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "SYMLINK");
+ empty = false;
+ }
+ if (*self & (UINT8_C(1)<<0)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<0");
+ empty = false;
+ }
+ if (empty)
+ fmt_print_byte(w, '0');
+ fmt_print_byte(w, ')');
+}
+
+#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u || CONFIG_9P_ENABLE_uninitialized */
+#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u
+[[maybe_unused]] static void fmt_print_o(lo_interface fmt_dest w, [[maybe_unused]] struct lib9p_ctx *ctx, lib9p_o_t *self) {
+ bool empty = true;
+ fmt_print_byte(w, '(');
+ if (*self & (UINT8_C(1)<<7)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<7");
+ empty = false;
+ }
+ if (*self & (UINT8_C(1)<<6)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "RCLOSE");
+ empty = false;
+ }
+ if (*self & (UINT8_C(1)<<5)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "CEXEC");
+ empty = false;
+ }
+ if (*self & (UINT8_C(1)<<4)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "TRUNC");
+ empty = false;
+ }
+ if (*self & (UINT8_C(1)<<3)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<3");
+ empty = false;
+ }
+ if (*self & (UINT8_C(1)<<2)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<2");
+ empty = false;
+ }
+ switch (*self & LIB9P_O_MODE_MASK) {
+ case LIB9P_O_MODE_READ:
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "MODE_READ");
+ empty = false;
+ break;
+ case LIB9P_O_MODE_WRITE:
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "MODE_WRITE");
+ empty = false;
+ break;
+ case LIB9P_O_MODE_RDWR:
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "MODE_RDWR");
+ empty = false;
+ break;
+ case LIB9P_O_MODE_EXEC:
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "MODE_EXEC");
+ empty = false;
+ break;
+ default:
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_base10(w, *self & LIB9P_O_MODE_MASK);
+ empty = false;
+ }
+ if (empty)
+ fmt_print_byte(w, '0');
+ fmt_print_byte(w, ')');
+}
+
+#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */
+#if CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_u
+[[maybe_unused]] static void fmt_print_nuid(lo_interface fmt_dest w, [[maybe_unused]] struct lib9p_ctx *ctx, lib9p_nuid_t *self) {
+ switch (*self) {
+ case LIB9P_NUID_NONUID:
+ fmt_print_str(w, "NONUID");
+ break;
+ default:
+ fmt_print_base10(w, *self);
+ }
+}
+
+[[maybe_unused]] static void fmt_print_errno(lo_interface fmt_dest w, [[maybe_unused]] struct lib9p_ctx *ctx, lib9p_errno_t *self) {
+ switch (*self) {
+ case LIB9P_ERRNO_NOERROR:
+ fmt_print_str(w, "NOERROR");
+ break;
+ case LIB9P_ERRNO_L_EPERM:
+ fmt_print_str(w, "L_EPERM");
+ break;
+ case LIB9P_ERRNO_L_ENOENT:
+ fmt_print_str(w, "L_ENOENT");
+ break;
+ case LIB9P_ERRNO_L_ESRCH:
+ fmt_print_str(w, "L_ESRCH");
+ break;
+ case LIB9P_ERRNO_L_EINTR:
+ fmt_print_str(w, "L_EINTR");
+ break;
+ case LIB9P_ERRNO_L_EIO:
+ fmt_print_str(w, "L_EIO");
+ break;
+ case LIB9P_ERRNO_L_ENXIO:
+ fmt_print_str(w, "L_ENXIO");
+ break;
+ case LIB9P_ERRNO_L_E2BIG:
+ fmt_print_str(w, "L_E2BIG");
+ break;
+ case LIB9P_ERRNO_L_ENOEXEC:
+ fmt_print_str(w, "L_ENOEXEC");
+ break;
+ case LIB9P_ERRNO_L_EBADF:
+ fmt_print_str(w, "L_EBADF");
+ break;
+ case LIB9P_ERRNO_L_ECHILD:
+ fmt_print_str(w, "L_ECHILD");
+ break;
+ case LIB9P_ERRNO_L_EAGAIN:
+ fmt_print_str(w, "L_EAGAIN");
+ break;
+ case LIB9P_ERRNO_L_ENOMEM:
+ fmt_print_str(w, "L_ENOMEM");
+ break;
+ case LIB9P_ERRNO_L_EACCES:
+ fmt_print_str(w, "L_EACCES");
+ break;
+ case LIB9P_ERRNO_L_EFAULT:
+ fmt_print_str(w, "L_EFAULT");
+ break;
+ case LIB9P_ERRNO_L_ENOTBLK:
+ fmt_print_str(w, "L_ENOTBLK");
+ break;
+ case LIB9P_ERRNO_L_EBUSY:
+ fmt_print_str(w, "L_EBUSY");
+ break;
+ case LIB9P_ERRNO_L_EEXIST:
+ fmt_print_str(w, "L_EEXIST");
+ break;
+ case LIB9P_ERRNO_L_EXDEV:
+ fmt_print_str(w, "L_EXDEV");
+ break;
+ case LIB9P_ERRNO_L_ENODEV:
+ fmt_print_str(w, "L_ENODEV");
+ break;
+ case LIB9P_ERRNO_L_ENOTDIR:
+ fmt_print_str(w, "L_ENOTDIR");
+ break;
+ case LIB9P_ERRNO_L_EISDIR:
+ fmt_print_str(w, "L_EISDIR");
+ break;
+ case LIB9P_ERRNO_L_EINVAL:
+ fmt_print_str(w, "L_EINVAL");
+ break;
+ case LIB9P_ERRNO_L_ENFILE:
+ fmt_print_str(w, "L_ENFILE");
+ break;
+ case LIB9P_ERRNO_L_EMFILE:
+ fmt_print_str(w, "L_EMFILE");
+ break;
+ case LIB9P_ERRNO_L_ENOTTY:
+ fmt_print_str(w, "L_ENOTTY");
+ break;
+ case LIB9P_ERRNO_L_ETXTBSY:
+ fmt_print_str(w, "L_ETXTBSY");
+ break;
+ case LIB9P_ERRNO_L_EFBIG:
+ fmt_print_str(w, "L_EFBIG");
+ break;
+ case LIB9P_ERRNO_L_ENOSPC:
+ fmt_print_str(w, "L_ENOSPC");
+ break;
+ case LIB9P_ERRNO_L_ESPIPE:
+ fmt_print_str(w, "L_ESPIPE");
+ break;
+ case LIB9P_ERRNO_L_EROFS:
+ fmt_print_str(w, "L_EROFS");
+ break;
+ case LIB9P_ERRNO_L_EMLINK:
+ fmt_print_str(w, "L_EMLINK");
+ break;
+ case LIB9P_ERRNO_L_EPIPE:
+ fmt_print_str(w, "L_EPIPE");
+ break;
+ case LIB9P_ERRNO_L_EDOM:
+ fmt_print_str(w, "L_EDOM");
+ break;
+ case LIB9P_ERRNO_L_ERANGE:
+ fmt_print_str(w, "L_ERANGE");
+ break;
+ case LIB9P_ERRNO_L_EDEADLK:
+ fmt_print_str(w, "L_EDEADLK");
+ break;
+ case LIB9P_ERRNO_L_ENAMETOOLONG:
+ fmt_print_str(w, "L_ENAMETOOLONG");
+ break;
+ case LIB9P_ERRNO_L_ENOLCK:
+ fmt_print_str(w, "L_ENOLCK");
+ break;
+ case LIB9P_ERRNO_L_ENOSYS:
+ fmt_print_str(w, "L_ENOSYS");
+ break;
+ case LIB9P_ERRNO_L_ENOTEMPTY:
+ fmt_print_str(w, "L_ENOTEMPTY");
+ break;
+ case LIB9P_ERRNO_L_ELOOP:
+ fmt_print_str(w, "L_ELOOP");
+ break;
+ case LIB9P_ERRNO_L_ENOMSG:
+ fmt_print_str(w, "L_ENOMSG");
+ break;
+ case LIB9P_ERRNO_L_EIDRM:
+ fmt_print_str(w, "L_EIDRM");
+ break;
+ case LIB9P_ERRNO_L_ECHRNG:
+ fmt_print_str(w, "L_ECHRNG");
+ break;
+ case LIB9P_ERRNO_L_EL2NSYNC:
+ fmt_print_str(w, "L_EL2NSYNC");
+ break;
+ case LIB9P_ERRNO_L_EL3HLT:
+ fmt_print_str(w, "L_EL3HLT");
+ break;
+ case LIB9P_ERRNO_L_EL3RST:
+ fmt_print_str(w, "L_EL3RST");
+ break;
+ case LIB9P_ERRNO_L_ELNRNG:
+ fmt_print_str(w, "L_ELNRNG");
+ break;
+ case LIB9P_ERRNO_L_EUNATCH:
+ fmt_print_str(w, "L_EUNATCH");
+ break;
+ case LIB9P_ERRNO_L_ENOCSI:
+ fmt_print_str(w, "L_ENOCSI");
+ break;
+ case LIB9P_ERRNO_L_EL2HLT:
+ fmt_print_str(w, "L_EL2HLT");
+ break;
+ case LIB9P_ERRNO_L_EBADE:
+ fmt_print_str(w, "L_EBADE");
+ break;
+ case LIB9P_ERRNO_L_EBADR:
+ fmt_print_str(w, "L_EBADR");
+ break;
+ case LIB9P_ERRNO_L_EXFULL:
+ fmt_print_str(w, "L_EXFULL");
+ break;
+ case LIB9P_ERRNO_L_ENOANO:
+ fmt_print_str(w, "L_ENOANO");
+ break;
+ case LIB9P_ERRNO_L_EBADRQC:
+ fmt_print_str(w, "L_EBADRQC");
+ break;
+ case LIB9P_ERRNO_L_EBADSLT:
+ fmt_print_str(w, "L_EBADSLT");
+ break;
+ case LIB9P_ERRNO_L_EBFONT:
+ fmt_print_str(w, "L_EBFONT");
+ break;
+ case LIB9P_ERRNO_L_ENOSTR:
+ fmt_print_str(w, "L_ENOSTR");
+ break;
+ case LIB9P_ERRNO_L_ENODATA:
+ fmt_print_str(w, "L_ENODATA");
+ break;
+ case LIB9P_ERRNO_L_ETIME:
+ fmt_print_str(w, "L_ETIME");
+ break;
+ case LIB9P_ERRNO_L_ENOSR:
+ fmt_print_str(w, "L_ENOSR");
+ break;
+ case LIB9P_ERRNO_L_ENONET:
+ fmt_print_str(w, "L_ENONET");
+ break;
+ case LIB9P_ERRNO_L_ENOPKG:
+ fmt_print_str(w, "L_ENOPKG");
+ break;
+ case LIB9P_ERRNO_L_EREMOTE:
+ fmt_print_str(w, "L_EREMOTE");
+ break;
+ case LIB9P_ERRNO_L_ENOLINK:
+ fmt_print_str(w, "L_ENOLINK");
+ break;
+ case LIB9P_ERRNO_L_EADV:
+ fmt_print_str(w, "L_EADV");
+ break;
+ case LIB9P_ERRNO_L_ESRMNT:
+ fmt_print_str(w, "L_ESRMNT");
+ break;
+ case LIB9P_ERRNO_L_ECOMM:
+ fmt_print_str(w, "L_ECOMM");
+ break;
+ case LIB9P_ERRNO_L_EPROTO:
+ fmt_print_str(w, "L_EPROTO");
+ break;
+ case LIB9P_ERRNO_L_EMULTIHOP:
+ fmt_print_str(w, "L_EMULTIHOP");
+ break;
+ case LIB9P_ERRNO_L_EDOTDOT:
+ fmt_print_str(w, "L_EDOTDOT");
+ break;
+ case LIB9P_ERRNO_L_EBADMSG:
+ fmt_print_str(w, "L_EBADMSG");
+ break;
+ case LIB9P_ERRNO_L_EOVERFLOW:
+ fmt_print_str(w, "L_EOVERFLOW");
+ break;
+ case LIB9P_ERRNO_L_ENOTUNIQ:
+ fmt_print_str(w, "L_ENOTUNIQ");
+ break;
+ case LIB9P_ERRNO_L_EBADFD:
+ fmt_print_str(w, "L_EBADFD");
+ break;
+ case LIB9P_ERRNO_L_EREMCHG:
+ fmt_print_str(w, "L_EREMCHG");
+ break;
+ case LIB9P_ERRNO_L_ELIBACC:
+ fmt_print_str(w, "L_ELIBACC");
+ break;
+ case LIB9P_ERRNO_L_ELIBBAD:
+ fmt_print_str(w, "L_ELIBBAD");
+ break;
+ case LIB9P_ERRNO_L_ELIBSCN:
+ fmt_print_str(w, "L_ELIBSCN");
+ break;
+ case LIB9P_ERRNO_L_ELIBMAX:
+ fmt_print_str(w, "L_ELIBMAX");
+ break;
+ case LIB9P_ERRNO_L_ELIBEXEC:
+ fmt_print_str(w, "L_ELIBEXEC");
+ break;
+ case LIB9P_ERRNO_L_EILSEQ:
+ fmt_print_str(w, "L_EILSEQ");
+ break;
+ case LIB9P_ERRNO_L_ERESTART:
+ fmt_print_str(w, "L_ERESTART");
+ break;
+ case LIB9P_ERRNO_L_ESTRPIPE:
+ fmt_print_str(w, "L_ESTRPIPE");
+ break;
+ case LIB9P_ERRNO_L_EUSERS:
+ fmt_print_str(w, "L_EUSERS");
+ break;
+ case LIB9P_ERRNO_L_ENOTSOCK:
+ fmt_print_str(w, "L_ENOTSOCK");
+ break;
+ case LIB9P_ERRNO_L_EDESTADDRREQ:
+ fmt_print_str(w, "L_EDESTADDRREQ");
+ break;
+ case LIB9P_ERRNO_L_EMSGSIZE:
+ fmt_print_str(w, "L_EMSGSIZE");
+ break;
+ case LIB9P_ERRNO_L_EPROTOTYPE:
+ fmt_print_str(w, "L_EPROTOTYPE");
+ break;
+ case LIB9P_ERRNO_L_ENOPROTOOPT:
+ fmt_print_str(w, "L_ENOPROTOOPT");
+ break;
+ case LIB9P_ERRNO_L_EPROTONOSUPPORT:
+ fmt_print_str(w, "L_EPROTONOSUPPORT");
+ break;
+ case LIB9P_ERRNO_L_ESOCKTNOSUPPORT:
+ fmt_print_str(w, "L_ESOCKTNOSUPPORT");
+ break;
+ case LIB9P_ERRNO_L_EOPNOTSUPP:
+ fmt_print_str(w, "L_EOPNOTSUPP");
+ break;
+ case LIB9P_ERRNO_L_EPFNOSUPPORT:
+ fmt_print_str(w, "L_EPFNOSUPPORT");
+ break;
+ case LIB9P_ERRNO_L_EAFNOSUPPORT:
+ fmt_print_str(w, "L_EAFNOSUPPORT");
+ break;
+ case LIB9P_ERRNO_L_EADDRINUSE:
+ fmt_print_str(w, "L_EADDRINUSE");
+ break;
+ case LIB9P_ERRNO_L_EADDRNOTAVAIL:
+ fmt_print_str(w, "L_EADDRNOTAVAIL");
+ break;
+ case LIB9P_ERRNO_L_ENETDOWN:
+ fmt_print_str(w, "L_ENETDOWN");
+ break;
+ case LIB9P_ERRNO_L_ENETUNREACH:
+ fmt_print_str(w, "L_ENETUNREACH");
+ break;
+ case LIB9P_ERRNO_L_ENETRESET:
+ fmt_print_str(w, "L_ENETRESET");
+ break;
+ case LIB9P_ERRNO_L_ECONNABORTED:
+ fmt_print_str(w, "L_ECONNABORTED");
+ break;
+ case LIB9P_ERRNO_L_ECONNRESET:
+ fmt_print_str(w, "L_ECONNRESET");
+ break;
+ case LIB9P_ERRNO_L_ENOBUFS:
+ fmt_print_str(w, "L_ENOBUFS");
+ break;
+ case LIB9P_ERRNO_L_EISCONN:
+ fmt_print_str(w, "L_EISCONN");
+ break;
+ case LIB9P_ERRNO_L_ENOTCONN:
+ fmt_print_str(w, "L_ENOTCONN");
+ break;
+ case LIB9P_ERRNO_L_ESHUTDOWN:
+ fmt_print_str(w, "L_ESHUTDOWN");
+ break;
+ case LIB9P_ERRNO_L_ETOOMANYREFS:
+ fmt_print_str(w, "L_ETOOMANYREFS");
+ break;
+ case LIB9P_ERRNO_L_ETIMEDOUT:
+ fmt_print_str(w, "L_ETIMEDOUT");
+ break;
+ case LIB9P_ERRNO_L_ECONNREFUSED:
+ fmt_print_str(w, "L_ECONNREFUSED");
+ break;
+ case LIB9P_ERRNO_L_EHOSTDOWN:
+ fmt_print_str(w, "L_EHOSTDOWN");
+ break;
+ case LIB9P_ERRNO_L_EHOSTUNREACH:
+ fmt_print_str(w, "L_EHOSTUNREACH");
+ break;
+ case LIB9P_ERRNO_L_EALREADY:
+ fmt_print_str(w, "L_EALREADY");
+ break;
+ case LIB9P_ERRNO_L_EINPROGRESS:
+ fmt_print_str(w, "L_EINPROGRESS");
+ break;
+ case LIB9P_ERRNO_L_ESTALE:
+ fmt_print_str(w, "L_ESTALE");
+ break;
+ case LIB9P_ERRNO_L_EUCLEAN:
+ fmt_print_str(w, "L_EUCLEAN");
+ break;
+ case LIB9P_ERRNO_L_ENOTNAM:
+ fmt_print_str(w, "L_ENOTNAM");
+ break;
+ case LIB9P_ERRNO_L_ENAVAIL:
+ fmt_print_str(w, "L_ENAVAIL");
+ break;
+ case LIB9P_ERRNO_L_EISNAM:
+ fmt_print_str(w, "L_EISNAM");
+ break;
+ case LIB9P_ERRNO_L_EREMOTEIO:
+ fmt_print_str(w, "L_EREMOTEIO");
+ break;
+ case LIB9P_ERRNO_L_EDQUOT:
+ fmt_print_str(w, "L_EDQUOT");
+ break;
+ case LIB9P_ERRNO_L_ENOMEDIUM:
+ fmt_print_str(w, "L_ENOMEDIUM");
+ break;
+ case LIB9P_ERRNO_L_EMEDIUMTYPE:
+ fmt_print_str(w, "L_EMEDIUMTYPE");
+ break;
+ case LIB9P_ERRNO_L_ECANCELED:
+ fmt_print_str(w, "L_ECANCELED");
+ break;
+ case LIB9P_ERRNO_L_ENOKEY:
+ fmt_print_str(w, "L_ENOKEY");
+ break;
+ case LIB9P_ERRNO_L_EKEYEXPIRED:
+ fmt_print_str(w, "L_EKEYEXPIRED");
+ break;
+ case LIB9P_ERRNO_L_EKEYREVOKED:
+ fmt_print_str(w, "L_EKEYREVOKED");
+ break;
+ case LIB9P_ERRNO_L_EKEYREJECTED:
+ fmt_print_str(w, "L_EKEYREJECTED");
+ break;
+ case LIB9P_ERRNO_L_EOWNERDEAD:
+ fmt_print_str(w, "L_EOWNERDEAD");
+ break;
+ case LIB9P_ERRNO_L_ENOTRECOVERABLE:
+ fmt_print_str(w, "L_ENOTRECOVERABLE");
+ break;
+ case LIB9P_ERRNO_L_ERFKILL:
+ fmt_print_str(w, "L_ERFKILL");
+ break;
+ case LIB9P_ERRNO_L_EHWPOISON:
+ fmt_print_str(w, "L_EHWPOISON");
+ break;
+ default:
+ fmt_print_base10(w, *self);
+ }
+}
+
+#endif /* CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_u */
+#if CONFIG_9P_ENABLE_9P2000_L
+[[maybe_unused]] static void fmt_print_super_magic(lo_interface fmt_dest w, [[maybe_unused]] struct lib9p_ctx *ctx, lib9p_super_magic_t *self) {
+ switch (*self) {
+ case LIB9P_SUPER_MAGIC_V9FS_MAGIC:
+ fmt_print_str(w, "V9FS_MAGIC");
+ break;
+ default:
+ fmt_print_base10(w, *self);
+ }
+}
+
+[[maybe_unused]] static void fmt_print_lo(lo_interface fmt_dest w, [[maybe_unused]] struct lib9p_ctx *ctx, lib9p_lo_t *self) {
+ bool empty = true;
+ fmt_print_byte(w, '(');
+ if (*self & (UINT32_C(1)<<31)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<31");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<30)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<30");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<29)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<29");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<28)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<28");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<27)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<27");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<26)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<26");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<25)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<25");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<24)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<24");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<23)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<23");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<22)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<22");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<21)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<21");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<20)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "SYNC");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<19)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "CLOEXEC");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<18)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "NOATIME");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<17)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "NOFOLLOW");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<16)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "DIRECTORY");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<15)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "LARGEFILE");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<14)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "DIRECT");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<13)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "BSD_FASYNC");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<12)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "DSYNC");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<11)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "NONBLOCK");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<10)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "APPEND");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<9)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "TRUNC");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<8)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "NOCTTY");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<7)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "EXCL");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<6)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "CREATE");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<5)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<5");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<4)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<4");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<3)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<3");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<2)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<2");
+ empty = false;
+ }
+ switch (*self & LIB9P_LO_MODE_MASK) {
+ case LIB9P_LO_MODE_RDONLY:
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "MODE_RDONLY");
+ empty = false;
+ break;
+ case LIB9P_LO_MODE_WRONLY:
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "MODE_WRONLY");
+ empty = false;
+ break;
+ case LIB9P_LO_MODE_RDWR:
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "MODE_RDWR");
+ empty = false;
+ break;
+ case LIB9P_LO_MODE_NOACCESS:
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "MODE_NOACCESS");
+ empty = false;
+ break;
+ default:
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_base10(w, *self & LIB9P_LO_MODE_MASK);
+ empty = false;
+ }
+ if (empty)
+ fmt_print_byte(w, '0');
+ fmt_print_byte(w, ')');
+}
+
+[[maybe_unused]] static void fmt_print_dt(lo_interface fmt_dest w, [[maybe_unused]] struct lib9p_ctx *ctx, lib9p_dt_t *self) {
+ switch (*self) {
+ case LIB9P_DT_UNKNOWN:
+ fmt_print_str(w, "UNKNOWN");
+ break;
+ case LIB9P_DT_PIPE:
+ fmt_print_str(w, "PIPE");
+ break;
+ case LIB9P_DT_CHAR_DEV:
+ fmt_print_str(w, "CHAR_DEV");
+ break;
+ case LIB9P_DT_DIRECTORY:
+ fmt_print_str(w, "DIRECTORY");
+ break;
+ case LIB9P_DT_BLOCK_DEV:
+ fmt_print_str(w, "BLOCK_DEV");
+ break;
+ case LIB9P_DT_REGULAR:
+ fmt_print_str(w, "REGULAR");
+ break;
+ case LIB9P_DT_SYMLINK:
+ fmt_print_str(w, "SYMLINK");
+ break;
+ case LIB9P_DT_SOCKET:
+ fmt_print_str(w, "SOCKET");
+ break;
+ case _LIB9P_DT_WHITEOUT:
+ fmt_print_str(w, "_WHITEOUT");
+ break;
+ default:
+ fmt_print_base10(w, *self);
+ }
+}
+
+[[maybe_unused]] static void fmt_print_mode(lo_interface fmt_dest w, [[maybe_unused]] struct lib9p_ctx *ctx, lib9p_mode_t *self) {
+ bool empty = true;
+ fmt_print_byte(w, '(');
+ if (*self & (UINT32_C(1)<<31)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<31");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<30)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<30");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<29)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<29");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<28)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<28");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<27)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<27");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<26)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<26");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<25)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<25");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<24)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<24");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<23)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<23");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<22)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<22");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<21)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<21");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<20)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<20");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<19)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<19");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<18)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<18");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<17)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<17");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<16)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<16");
+ empty = false;
+ }
+ switch (*self & LIB9P_MODE_FMT_MASK) {
+ case LIB9P_MODE_FMT_PIPE:
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "FMT_PIPE");
+ empty = false;
+ break;
+ case LIB9P_MODE_FMT_CHAR_DEV:
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "FMT_CHAR_DEV");
+ empty = false;
+ break;
+ case LIB9P_MODE_FMT_DIRECTORY:
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "FMT_DIRECTORY");
+ empty = false;
+ break;
+ case LIB9P_MODE_FMT_BLOCK_DEV:
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "FMT_BLOCK_DEV");
+ empty = false;
+ break;
+ case LIB9P_MODE_FMT_REGULAR:
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "FMT_REGULAR");
+ empty = false;
+ break;
+ case LIB9P_MODE_FMT_SYMLINK:
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "FMT_SYMLINK");
+ empty = false;
+ break;
+ case LIB9P_MODE_FMT_SOCKET:
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "FMT_SOCKET");
+ empty = false;
+ break;
+ default:
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_base10(w, *self & LIB9P_MODE_FMT_MASK);
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<11)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "PERM_SETGROUP");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<10)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "PERM_SETUSER");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<9)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "PERM_STICKY");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<8)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "PERM_OWNER_R");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<7)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "PERM_OWNER_W");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<6)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "PERM_OWNER_X");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<5)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "PERM_GROUP_R");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<4)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "PERM_GROUP_W");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<3)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "PERM_GROUP_X");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<2)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "PERM_OTHER_R");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<1)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "PERM_OTHER_W");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<0)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "PERM_OTHER_X");
+ empty = false;
+ }
+ if (empty)
+ fmt_print_byte(w, '0');
+ fmt_print_byte(w, ')');
+}
+
+[[maybe_unused]] static void fmt_print_b4(lo_interface fmt_dest w, [[maybe_unused]] struct lib9p_ctx *ctx, lib9p_b4_t *self) {
+ switch (*self) {
+ case LIB9P_B4_FALSE:
+ fmt_print_str(w, "FALSE");
+ break;
+ case LIB9P_B4_TRUE:
+ fmt_print_str(w, "TRUE");
+ break;
+ default:
+ fmt_print_base10(w, *self);
+ }
+}
+
+[[maybe_unused]] static void fmt_print_getattr(lo_interface fmt_dest w, [[maybe_unused]] struct lib9p_ctx *ctx, lib9p_getattr_t *self) {
+ bool empty = true;
+ fmt_print_byte(w, '(');
+ if (*self & (UINT64_C(1)<<63)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<63");
+ empty = false;
+ }
+ if (*self & (UINT64_C(1)<<62)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<62");
+ empty = false;
+ }
+ if (*self & (UINT64_C(1)<<61)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<61");
+ empty = false;
+ }
+ if (*self & (UINT64_C(1)<<60)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<60");
+ empty = false;
+ }
+ if (*self & (UINT64_C(1)<<59)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<59");
+ empty = false;
+ }
+ if (*self & (UINT64_C(1)<<58)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<58");
+ empty = false;
+ }
+ if (*self & (UINT64_C(1)<<57)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<57");
+ empty = false;
+ }
+ if (*self & (UINT64_C(1)<<56)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<56");
+ empty = false;
+ }
+ if (*self & (UINT64_C(1)<<55)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<55");
+ empty = false;
+ }
+ if (*self & (UINT64_C(1)<<54)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<54");
+ empty = false;
+ }
+ if (*self & (UINT64_C(1)<<53)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<53");
+ empty = false;
+ }
+ if (*self & (UINT64_C(1)<<52)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<52");
+ empty = false;
+ }
+ if (*self & (UINT64_C(1)<<51)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<51");
+ empty = false;
+ }
+ if (*self & (UINT64_C(1)<<50)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<50");
+ empty = false;
+ }
+ if (*self & (UINT64_C(1)<<49)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<49");
+ empty = false;
+ }
+ if (*self & (UINT64_C(1)<<48)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<48");
+ empty = false;
+ }
+ if (*self & (UINT64_C(1)<<47)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<47");
+ empty = false;
+ }
+ if (*self & (UINT64_C(1)<<46)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<46");
+ empty = false;
+ }
+ if (*self & (UINT64_C(1)<<45)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<45");
+ empty = false;
+ }
+ if (*self & (UINT64_C(1)<<44)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<44");
+ empty = false;
+ }
+ if (*self & (UINT64_C(1)<<43)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<43");
+ empty = false;
+ }
+ if (*self & (UINT64_C(1)<<42)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<42");
+ empty = false;
+ }
+ if (*self & (UINT64_C(1)<<41)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<41");
+ empty = false;
+ }
+ if (*self & (UINT64_C(1)<<40)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<40");
+ empty = false;
+ }
+ if (*self & (UINT64_C(1)<<39)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<39");
+ empty = false;
+ }
+ if (*self & (UINT64_C(1)<<38)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<38");
+ empty = false;
+ }
+ if (*self & (UINT64_C(1)<<37)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<37");
+ empty = false;
+ }
+ if (*self & (UINT64_C(1)<<36)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<36");
+ empty = false;
+ }
+ if (*self & (UINT64_C(1)<<35)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<35");
+ empty = false;
+ }
+ if (*self & (UINT64_C(1)<<34)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<34");
+ empty = false;
+ }
+ if (*self & (UINT64_C(1)<<33)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<33");
+ empty = false;
+ }
+ if (*self & (UINT64_C(1)<<32)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<32");
+ empty = false;
+ }
+ if (*self & (UINT64_C(1)<<31)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<31");
+ empty = false;
+ }
+ if (*self & (UINT64_C(1)<<30)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<30");
+ empty = false;
+ }
+ if (*self & (UINT64_C(1)<<29)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<29");
+ empty = false;
+ }
+ if (*self & (UINT64_C(1)<<28)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<28");
+ empty = false;
+ }
+ if (*self & (UINT64_C(1)<<27)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<27");
+ empty = false;
+ }
+ if (*self & (UINT64_C(1)<<26)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<26");
+ empty = false;
+ }
+ if (*self & (UINT64_C(1)<<25)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<25");
+ empty = false;
+ }
+ if (*self & (UINT64_C(1)<<24)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<24");
+ empty = false;
+ }
+ if (*self & (UINT64_C(1)<<23)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<23");
+ empty = false;
+ }
+ if (*self & (UINT64_C(1)<<22)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<22");
+ empty = false;
+ }
+ if (*self & (UINT64_C(1)<<21)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<21");
+ empty = false;
+ }
+ if (*self & (UINT64_C(1)<<20)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<20");
+ empty = false;
+ }
+ if (*self & (UINT64_C(1)<<19)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<19");
+ empty = false;
+ }
+ if (*self & (UINT64_C(1)<<18)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<18");
+ empty = false;
+ }
+ if (*self & (UINT64_C(1)<<17)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<17");
+ empty = false;
+ }
+ if (*self & (UINT64_C(1)<<16)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<16");
+ empty = false;
+ }
+ if (*self & (UINT64_C(1)<<15)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<15");
+ empty = false;
+ }
+ if (*self & (UINT64_C(1)<<14)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<14");
+ empty = false;
+ }
+ if (*self & (UINT64_C(1)<<13)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "DATA_VERSION");
+ empty = false;
+ }
+ if (*self & (UINT64_C(1)<<12)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "GEN");
+ empty = false;
+ }
+ if (*self & (UINT64_C(1)<<11)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "BTIME");
+ empty = false;
+ }
+ if (*self & (UINT64_C(1)<<10)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "BLOCKS");
+ empty = false;
+ }
+ if (*self & (UINT64_C(1)<<9)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "SIZE");
+ empty = false;
+ }
+ if (*self & (UINT64_C(1)<<8)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "INO");
+ empty = false;
+ }
+ if (*self & (UINT64_C(1)<<7)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "CTIME");
+ empty = false;
+ }
+ if (*self & (UINT64_C(1)<<6)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "MTIME");
+ empty = false;
+ }
+ if (*self & (UINT64_C(1)<<5)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "ATIME");
+ empty = false;
+ }
+ if (*self & (UINT64_C(1)<<4)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "RDEV");
+ empty = false;
+ }
+ if (*self & (UINT64_C(1)<<3)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "GID");
+ empty = false;
+ }
+ if (*self & (UINT64_C(1)<<2)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "UID");
+ empty = false;
+ }
+ if (*self & (UINT64_C(1)<<1)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "NLINK");
+ empty = false;
+ }
+ if (*self & (UINT64_C(1)<<0)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "MODE");
+ empty = false;
+ }
+ if (empty)
+ fmt_print_byte(w, '0');
+ fmt_print_byte(w, ')');
+}
+
+[[maybe_unused]] static void fmt_print_setattr(lo_interface fmt_dest w, [[maybe_unused]] struct lib9p_ctx *ctx, lib9p_setattr_t *self) {
+ bool empty = true;
+ fmt_print_byte(w, '(');
+ if (*self & (UINT32_C(1)<<31)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<31");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<30)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<30");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<29)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<29");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<28)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<28");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<27)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<27");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<26)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<26");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<25)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<25");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<24)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<24");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<23)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<23");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<22)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<22");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<21)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<21");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<20)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<20");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<19)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<19");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<18)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<18");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<17)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<17");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<16)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<16");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<15)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<15");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<14)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<14");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<13)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<13");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<12)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<12");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<11)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<11");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<10)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<10");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<9)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<9");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<8)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "MTIME_SET");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<7)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "ATIME_SET");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<6)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "CTIME");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<5)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "MTIME");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<4)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "ATIME");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<3)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "SIZE");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<2)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "GID");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<1)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "UID");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<0)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "MODE");
+ empty = false;
+ }
+ if (empty)
+ fmt_print_byte(w, '0');
+ fmt_print_byte(w, ')');
+}
+
+[[maybe_unused]] static void fmt_print_lock_type(lo_interface fmt_dest w, [[maybe_unused]] struct lib9p_ctx *ctx, lib9p_lock_type_t *self) {
+ switch (*self) {
+ case LIB9P_LOCK_TYPE_RDLCK:
+ fmt_print_str(w, "RDLCK");
+ break;
+ case LIB9P_LOCK_TYPE_WRLCK:
+ fmt_print_str(w, "WRLCK");
+ break;
+ case LIB9P_LOCK_TYPE_UNLCK:
+ fmt_print_str(w, "UNLCK");
+ break;
+ default:
+ fmt_print_base10(w, *self);
+ }
+}
+
+[[maybe_unused]] static void fmt_print_lock_flags(lo_interface fmt_dest w, [[maybe_unused]] struct lib9p_ctx *ctx, lib9p_lock_flags_t *self) {
+ bool empty = true;
+ fmt_print_byte(w, '(');
+ if (*self & (UINT32_C(1)<<31)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<31");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<30)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<30");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<29)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<29");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<28)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<28");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<27)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<27");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<26)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<26");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<25)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<25");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<24)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<24");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<23)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<23");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<22)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<22");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<21)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<21");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<20)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<20");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<19)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<19");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<18)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<18");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<17)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<17");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<16)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<16");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<15)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<15");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<14)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<14");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<13)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<13");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<12)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<12");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<11)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<11");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<10)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<10");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<9)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<9");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<8)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<8");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<7)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<7");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<6)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<6");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<5)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<5");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<4)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<4");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<3)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<3");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<2)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "1<<2");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<1)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "RECLAIM");
+ empty = false;
+ }
+ if (*self & (UINT32_C(1)<<0)) {
+ if (!empty)
+ fmt_print_byte(w, '|');
+ fmt_print_str(w, "BLOCK");
+ empty = false;
+ }
+ if (empty)
+ fmt_print_byte(w, '0');
+ fmt_print_byte(w, ')');
+}
+
+[[maybe_unused]] static void fmt_print_lock_status(lo_interface fmt_dest w, [[maybe_unused]] struct lib9p_ctx *ctx, lib9p_lock_status_t *self) {
+ switch (*self) {
+ case LIB9P_LOCK_STATUS_SUCCESS:
+ fmt_print_str(w, "SUCCESS");
+ break;
+ case LIB9P_LOCK_STATUS_BLOCKED:
+ fmt_print_str(w, "BLOCKED");
+ break;
+ case LIB9P_LOCK_STATUS_ERROR:
+ fmt_print_str(w, "ERROR");
+ break;
+ case LIB9P_LOCK_STATUS_GRACE:
+ fmt_print_str(w, "GRACE");
+ break;
+ default:
+ fmt_print_base10(w, *self);
+ }
+}
+
+#endif /* CONFIG_9P_ENABLE_9P2000_L */
+#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u || CONFIG_9P_ENABLE_uninitialized
+[[maybe_unused]] static void fmt_print_s(lo_interface fmt_dest w, [[maybe_unused]] struct lib9p_ctx *ctx, struct lib9p_s *self) {
+ fmt_print_qmem(w, self->utf8, self->len);
+}
+
+[[maybe_unused]] static void fmt_print_qid(lo_interface fmt_dest w, [[maybe_unused]] struct lib9p_ctx *ctx, struct lib9p_qid *self) {
+ fmt_print_byte(w, '{');
+ fmt_print_str(w, " type=");
+ fmt_print_qt(w, ctx, &self->type);
+ fmt_print_str(w, " vers=");
+ fmt_print_base10(w, self->vers);
+ fmt_print_str(w, " path=");
+ fmt_print_base10(w, self->path);
+ fmt_print_str(w, " }");
+}
+
+#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u || CONFIG_9P_ENABLE_uninitialized */
+#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u
+[[maybe_unused]] static void fmt_print_Tflush(lo_interface fmt_dest w, [[maybe_unused]] struct lib9p_ctx *ctx, struct lib9p_msg_Tflush *self) {
+ fmt_print_str(w, "Tflush {");
+ fmt_print_str(w, " tag=");
+ fmt_print_tag(w, ctx, &self->tag);
+ fmt_print_str(w, " oldtag=");
+ fmt_print_base10(w, self->oldtag);
+ fmt_print_str(w, " }");
+}
+
+[[maybe_unused]] static void fmt_print_Rflush(lo_interface fmt_dest w, [[maybe_unused]] struct lib9p_ctx *ctx, struct lib9p_msg_Rflush *self) {
+ fmt_print_str(w, "Rflush {");
+ fmt_print_str(w, " tag=");
+ fmt_print_tag(w, ctx, &self->tag);
+ fmt_print_str(w, " }");
+}
+
+#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */
+#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u
+[[maybe_unused]] static void fmt_print_Topen(lo_interface fmt_dest w, [[maybe_unused]] struct lib9p_ctx *ctx, struct lib9p_msg_Topen *self) {
+ fmt_print_str(w, "Topen {");
+ fmt_print_str(w, " tag=");
+ fmt_print_tag(w, ctx, &self->tag);
+ fmt_print_str(w, " fid=");
+ fmt_print_fid(w, ctx, &self->fid);
+ fmt_print_str(w, " mode=");
+ fmt_print_o(w, ctx, &self->mode);
+ fmt_print_str(w, " }");
+}
+
+#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */
+#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u
+[[maybe_unused]] static void fmt_print_Tread(lo_interface fmt_dest w, [[maybe_unused]] struct lib9p_ctx *ctx, struct lib9p_msg_Tread *self) {
+ fmt_print_str(w, "Tread {");
+ fmt_print_str(w, " tag=");
+ fmt_print_tag(w, ctx, &self->tag);
+ fmt_print_str(w, " fid=");
+ fmt_print_fid(w, ctx, &self->fid);
+ fmt_print_str(w, " offset=");
+ fmt_print_base10(w, self->offset);
+ fmt_print_str(w, " count=");
+ fmt_print_base10(w, self->count);
+ fmt_print_str(w, " }");
+}
+
+[[maybe_unused]] static void fmt_print_Rread(lo_interface fmt_dest w, [[maybe_unused]] struct lib9p_ctx *ctx, struct lib9p_msg_Rread *self) {
+ fmt_print_str(w, "Rread {");
+ fmt_print_str(w, " tag=");
+ fmt_print_tag(w, ctx, &self->tag);
+ fmt_print_str(w, " count=");
+ fmt_print_base10(w, self->count);
+ if (utf8_is_valid_without_nul((uint8_t *)self->data, (size_t)self->count)) {
+ fmt_print_str(w, " data=");
+ fmt_print_qmem(w, self->data, self->count < 50 ? self->count : 50);
+ if (self->count > 50)
+ fmt_print_str(w, "...");
+ } else {
+ fmt_print_str(w, " data=<bytedata>");
+ }
+ fmt_print_str(w, " }");
+}
+
+[[maybe_unused]] static void fmt_print_Twrite(lo_interface fmt_dest w, [[maybe_unused]] struct lib9p_ctx *ctx, struct lib9p_msg_Twrite *self) {
+ fmt_print_str(w, "Twrite {");
+ fmt_print_str(w, " tag=");
+ fmt_print_tag(w, ctx, &self->tag);
+ fmt_print_str(w, " fid=");
+ fmt_print_fid(w, ctx, &self->fid);
+ fmt_print_str(w, " offset=");
+ fmt_print_base10(w, self->offset);
+ fmt_print_str(w, " count=");
+ fmt_print_base10(w, self->count);
+ if (utf8_is_valid_without_nul((uint8_t *)self->data, (size_t)self->count)) {
+ fmt_print_str(w, " data=");
+ fmt_print_qmem(w, self->data, self->count < 50 ? self->count : 50);
+ if (self->count > 50)
+ fmt_print_str(w, "...");
+ } else {
+ fmt_print_str(w, " data=<bytedata>");
+ }
+ fmt_print_str(w, " }");
+}
+
+[[maybe_unused]] static void fmt_print_Rwrite(lo_interface fmt_dest w, [[maybe_unused]] struct lib9p_ctx *ctx, struct lib9p_msg_Rwrite *self) {
+ fmt_print_str(w, "Rwrite {");
+ fmt_print_str(w, " tag=");
+ fmt_print_tag(w, ctx, &self->tag);
+ fmt_print_str(w, " count=");
+ fmt_print_base10(w, self->count);
+ fmt_print_str(w, " }");
+}
+
+[[maybe_unused]] static void fmt_print_Tclunk(lo_interface fmt_dest w, [[maybe_unused]] struct lib9p_ctx *ctx, struct lib9p_msg_Tclunk *self) {
+ fmt_print_str(w, "Tclunk {");
+ fmt_print_str(w, " tag=");
+ fmt_print_tag(w, ctx, &self->tag);
+ fmt_print_str(w, " fid=");
+ fmt_print_fid(w, ctx, &self->fid);
+ fmt_print_str(w, " }");
+}
+
+[[maybe_unused]] static void fmt_print_Rclunk(lo_interface fmt_dest w, [[maybe_unused]] struct lib9p_ctx *ctx, struct lib9p_msg_Rclunk *self) {
+ fmt_print_str(w, "Rclunk {");
+ fmt_print_str(w, " tag=");
+ fmt_print_tag(w, ctx, &self->tag);
+ fmt_print_str(w, " }");
+}
+
+[[maybe_unused]] static void fmt_print_Tremove(lo_interface fmt_dest w, [[maybe_unused]] struct lib9p_ctx *ctx, struct lib9p_msg_Tremove *self) {
+ fmt_print_str(w, "Tremove {");
+ fmt_print_str(w, " tag=");
+ fmt_print_tag(w, ctx, &self->tag);
+ fmt_print_str(w, " fid=");
+ fmt_print_fid(w, ctx, &self->fid);
+ fmt_print_str(w, " }");
+}
+
+[[maybe_unused]] static void fmt_print_Rremove(lo_interface fmt_dest w, [[maybe_unused]] struct lib9p_ctx *ctx, struct lib9p_msg_Rremove *self) {
+ fmt_print_str(w, "Rremove {");
+ fmt_print_str(w, " tag=");
+ fmt_print_tag(w, ctx, &self->tag);
+ fmt_print_str(w, " }");
+}
+
+#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */
+#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u
+[[maybe_unused]] static void fmt_print_Tstat(lo_interface fmt_dest w, [[maybe_unused]] struct lib9p_ctx *ctx, struct lib9p_msg_Tstat *self) {
+ fmt_print_str(w, "Tstat {");
+ fmt_print_str(w, " tag=");
+ fmt_print_tag(w, ctx, &self->tag);
+ fmt_print_str(w, " fid=");
+ fmt_print_fid(w, ctx, &self->fid);
+ fmt_print_str(w, " }");
+}
+
+[[maybe_unused]] static void fmt_print_Rwstat(lo_interface fmt_dest w, [[maybe_unused]] struct lib9p_ctx *ctx, struct lib9p_msg_Rwstat *self) {
+ fmt_print_str(w, "Rwstat {");
+ fmt_print_str(w, " tag=");
+ fmt_print_tag(w, ctx, &self->tag);
+ fmt_print_str(w, " }");
+}
+
+#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */
+#if CONFIG_9P_ENABLE_9P2000_p9p
+[[maybe_unused]] static void fmt_print_Topenfd(lo_interface fmt_dest w, [[maybe_unused]] struct lib9p_ctx *ctx, struct lib9p_msg_Topenfd *self) {
+ fmt_print_str(w, "Topenfd {");
+ fmt_print_str(w, " tag=");
+ fmt_print_tag(w, ctx, &self->tag);
+ fmt_print_str(w, " fid=");
+ fmt_print_fid(w, ctx, &self->fid);
+ fmt_print_str(w, " mode=");
+ fmt_print_o(w, ctx, &self->mode);
+ fmt_print_str(w, " }");
+}
+
+#endif /* CONFIG_9P_ENABLE_9P2000_p9p */
+#if CONFIG_9P_ENABLE_9P2000_L
+[[maybe_unused]] static void fmt_print_Rlerror(lo_interface fmt_dest w, [[maybe_unused]] struct lib9p_ctx *ctx, struct lib9p_msg_Rlerror *self) {
+ fmt_print_str(w, "Rlerror {");
+ fmt_print_str(w, " tag=");
+ fmt_print_tag(w, ctx, &self->tag);
+ fmt_print_str(w, " errnum=");
+ fmt_print_errno(w, ctx, &self->errnum);
+ fmt_print_str(w, " }");
+}
+
+[[maybe_unused]] static void fmt_print_Tstatfs(lo_interface fmt_dest w, [[maybe_unused]] struct lib9p_ctx *ctx, struct lib9p_msg_Tstatfs *self) {
+ fmt_print_str(w, "Tstatfs {");
+ fmt_print_str(w, " tag=");
+ fmt_print_tag(w, ctx, &self->tag);
+ fmt_print_str(w, " fid=");
+ fmt_print_fid(w, ctx, &self->fid);
+ fmt_print_str(w, " }");
+}
+
+[[maybe_unused]] static void fmt_print_Rstatfs(lo_interface fmt_dest w, [[maybe_unused]] struct lib9p_ctx *ctx, struct lib9p_msg_Rstatfs *self) {
+ fmt_print_str(w, "Rstatfs {");
+ fmt_print_str(w, " tag=");
+ fmt_print_tag(w, ctx, &self->tag);
+ fmt_print_str(w, " type=");
+ fmt_print_super_magic(w, ctx, &self->type);
+ fmt_print_str(w, " bsize=");
+ fmt_print_base10(w, self->bsize);
+ fmt_print_str(w, " blocks=");
+ fmt_print_base10(w, self->blocks);
+ fmt_print_str(w, " bfree=");
+ fmt_print_base10(w, self->bfree);
+ fmt_print_str(w, " bavail=");
+ fmt_print_base10(w, self->bavail);
+ fmt_print_str(w, " files=");
+ fmt_print_base10(w, self->files);
+ fmt_print_str(w, " ffree=");
+ fmt_print_base10(w, self->ffree);
+ fmt_print_str(w, " fsid=");
+ fmt_print_base10(w, self->fsid);
+ fmt_print_str(w, " namelen=");
+ fmt_print_base10(w, self->namelen);
+ fmt_print_str(w, " }");
+}
+
+[[maybe_unused]] static void fmt_print_Tlopen(lo_interface fmt_dest w, [[maybe_unused]] struct lib9p_ctx *ctx, struct lib9p_msg_Tlopen *self) {
+ fmt_print_str(w, "Tlopen {");
+ fmt_print_str(w, " tag=");
+ fmt_print_tag(w, ctx, &self->tag);
+ fmt_print_str(w, " fid=");
+ fmt_print_fid(w, ctx, &self->fid);
+ fmt_print_str(w, " flags=");
+ fmt_print_lo(w, ctx, &self->flags);
+ fmt_print_str(w, " }");
+}
+
+[[maybe_unused]] static void fmt_print_Rrename(lo_interface fmt_dest w, [[maybe_unused]] struct lib9p_ctx *ctx, struct lib9p_msg_Rrename *self) {
+ fmt_print_str(w, "Rrename {");
+ fmt_print_str(w, " tag=");
+ fmt_print_tag(w, ctx, &self->tag);
+ fmt_print_str(w, " }");
+}
+
+[[maybe_unused]] static void fmt_print_Treadlink(lo_interface fmt_dest w, [[maybe_unused]] struct lib9p_ctx *ctx, struct lib9p_msg_Treadlink *self) {
+ fmt_print_str(w, "Treadlink {");
+ fmt_print_str(w, " tag=");
+ fmt_print_tag(w, ctx, &self->tag);
+ fmt_print_str(w, " fid=");
+ fmt_print_fid(w, ctx, &self->fid);
+ fmt_print_str(w, " }");
+}
+
+[[maybe_unused]] static void fmt_print_Tgetattr(lo_interface fmt_dest w, [[maybe_unused]] struct lib9p_ctx *ctx, struct lib9p_msg_Tgetattr *self) {
+ fmt_print_str(w, "Tgetattr {");
+ fmt_print_str(w, " tag=");
+ fmt_print_tag(w, ctx, &self->tag);
+ fmt_print_str(w, " fid=");
+ fmt_print_fid(w, ctx, &self->fid);
+ fmt_print_str(w, " request_mask=");
+ fmt_print_getattr(w, ctx, &self->request_mask);
+ fmt_print_str(w, " }");
+}
+
+[[maybe_unused]] static void fmt_print_Tsetattr(lo_interface fmt_dest w, [[maybe_unused]] struct lib9p_ctx *ctx, struct lib9p_msg_Tsetattr *self) {
+ fmt_print_str(w, "Tsetattr {");
+ fmt_print_str(w, " tag=");
+ fmt_print_tag(w, ctx, &self->tag);
+ fmt_print_str(w, " fid=");
+ fmt_print_fid(w, ctx, &self->fid);
+ fmt_print_str(w, " valid=");
+ fmt_print_setattr(w, ctx, &self->valid);
+ fmt_print_str(w, " mode=");
+ fmt_print_mode(w, ctx, &self->mode);
+ fmt_print_str(w, " uid=");
+ fmt_print_nuid(w, ctx, &self->uid);
+ fmt_print_str(w, " gid=");
+ fmt_print_nuid(w, ctx, &self->gid);
+ fmt_print_str(w, " filesize=");
+ fmt_print_base10(w, self->filesize);
+ fmt_print_str(w, " atime_sec=");
+ fmt_print_base10(w, self->atime_sec);
+ fmt_print_str(w, " atime_nsec=");
+ fmt_print_base10(w, self->atime_nsec);
+ fmt_print_str(w, " mtime_sec=");
+ fmt_print_base10(w, self->mtime_sec);
+ fmt_print_str(w, " mtime_nsec=");
+ fmt_print_base10(w, self->mtime_nsec);
+ fmt_print_str(w, " }");
+}
+
+[[maybe_unused]] static void fmt_print_Rsetattr(lo_interface fmt_dest w, [[maybe_unused]] struct lib9p_ctx *ctx, struct lib9p_msg_Rsetattr *self) {
+ fmt_print_str(w, "Rsetattr {");
+ fmt_print_str(w, " tag=");
+ fmt_print_tag(w, ctx, &self->tag);
+ fmt_print_str(w, " }");
+}
+
+[[maybe_unused]] static void fmt_print_Rxattrwalk(lo_interface fmt_dest w, [[maybe_unused]] struct lib9p_ctx *ctx, struct lib9p_msg_Rxattrwalk *self) {
+ fmt_print_str(w, "Rxattrwalk {");
+ fmt_print_str(w, " tag=");
+ fmt_print_tag(w, ctx, &self->tag);
+ fmt_print_str(w, " attr_size=");
+ fmt_print_base10(w, self->attr_size);
+ fmt_print_str(w, " }");
+}
+
+[[maybe_unused]] static void fmt_print_Rxattrcreate(lo_interface fmt_dest w, [[maybe_unused]] struct lib9p_ctx *ctx, struct lib9p_msg_Rxattrcreate *self) {
+ fmt_print_str(w, "Rxattrcreate {");
+ fmt_print_str(w, " tag=");
+ fmt_print_tag(w, ctx, &self->tag);
+ fmt_print_str(w, " }");
+}
+
+[[maybe_unused]] static void fmt_print_Treaddir(lo_interface fmt_dest w, [[maybe_unused]] struct lib9p_ctx *ctx, struct lib9p_msg_Treaddir *self) {
+ fmt_print_str(w, "Treaddir {");
+ fmt_print_str(w, " tag=");
+ fmt_print_tag(w, ctx, &self->tag);
+ fmt_print_str(w, " fid=");
+ fmt_print_fid(w, ctx, &self->fid);
+ fmt_print_str(w, " offset=");
+ fmt_print_base10(w, self->offset);
+ fmt_print_str(w, " count=");
+ fmt_print_base10(w, self->count);
+ fmt_print_str(w, " }");
+}
+
+[[maybe_unused]] static void fmt_print_Rreaddir(lo_interface fmt_dest w, [[maybe_unused]] struct lib9p_ctx *ctx, struct lib9p_msg_Rreaddir *self) {
+ fmt_print_str(w, "Rreaddir {");
+ fmt_print_str(w, " tag=");
+ fmt_print_tag(w, ctx, &self->tag);
+ fmt_print_str(w, " count=");
+ fmt_print_base10(w, self->count);
+ if (utf8_is_valid_without_nul((uint8_t *)self->data, (size_t)self->count)) {
+ fmt_print_str(w, " data=");
+ fmt_print_qmem(w, self->data, self->count < 50 ? self->count : 50);
+ if (self->count > 50)
+ fmt_print_str(w, "...");
+ } else {
+ fmt_print_str(w, " data=<bytedata>");
+ }
+ fmt_print_str(w, " }");
+}
+
+[[maybe_unused]] static void fmt_print_Tfsync(lo_interface fmt_dest w, [[maybe_unused]] struct lib9p_ctx *ctx, struct lib9p_msg_Tfsync *self) {
+ fmt_print_str(w, "Tfsync {");
+ fmt_print_str(w, " tag=");
+ fmt_print_tag(w, ctx, &self->tag);
+ fmt_print_str(w, " fid=");
+ fmt_print_fid(w, ctx, &self->fid);
+ fmt_print_str(w, " datasync=");
+ fmt_print_b4(w, ctx, &self->datasync);
+ fmt_print_str(w, " }");
+}
+
+[[maybe_unused]] static void fmt_print_Rfsync(lo_interface fmt_dest w, [[maybe_unused]] struct lib9p_ctx *ctx, struct lib9p_msg_Rfsync *self) {
+ fmt_print_str(w, "Rfsync {");
+ fmt_print_str(w, " tag=");
+ fmt_print_tag(w, ctx, &self->tag);
+ fmt_print_str(w, " }");
+}
+
+[[maybe_unused]] static void fmt_print_Rlock(lo_interface fmt_dest w, [[maybe_unused]] struct lib9p_ctx *ctx, struct lib9p_msg_Rlock *self) {
+ fmt_print_str(w, "Rlock {");
+ fmt_print_str(w, " tag=");
+ fmt_print_tag(w, ctx, &self->tag);
+ fmt_print_str(w, " status=");
+ fmt_print_lock_status(w, ctx, &self->status);
+ fmt_print_str(w, " }");
+}
+
+[[maybe_unused]] static void fmt_print_Rlink(lo_interface fmt_dest w, [[maybe_unused]] struct lib9p_ctx *ctx, struct lib9p_msg_Rlink *self) {
+ fmt_print_str(w, "Rlink {");
+ fmt_print_str(w, " tag=");
+ fmt_print_tag(w, ctx, &self->tag);
+ fmt_print_str(w, " }");
+}
+
+[[maybe_unused]] static void fmt_print_Rrenameat(lo_interface fmt_dest w, [[maybe_unused]] struct lib9p_ctx *ctx, struct lib9p_msg_Rrenameat *self) {
+ fmt_print_str(w, "Rrenameat {");
+ fmt_print_str(w, " tag=");
+ fmt_print_tag(w, ctx, &self->tag);
+ fmt_print_str(w, " }");
+}
+
+[[maybe_unused]] static void fmt_print_Runlinkat(lo_interface fmt_dest w, [[maybe_unused]] struct lib9p_ctx *ctx, struct lib9p_msg_Runlinkat *self) {
+ fmt_print_str(w, "Runlinkat {");
+ fmt_print_str(w, " tag=");
+ fmt_print_tag(w, ctx, &self->tag);
+ fmt_print_str(w, " }");
+}
+
+#endif /* CONFIG_9P_ENABLE_9P2000_L */
+#if CONFIG_9P_ENABLE_9P2000_e
+[[maybe_unused]] static void fmt_print_Tsession(lo_interface fmt_dest w, [[maybe_unused]] struct lib9p_ctx *ctx, struct lib9p_msg_Tsession *self) {
+ fmt_print_str(w, "Tsession {");
+ fmt_print_str(w, " tag=");
+ fmt_print_tag(w, ctx, &self->tag);
+ fmt_print_str(w, " key=");
+ fmt_print_base10(w, self->key);
+ fmt_print_str(w, " }");
+}
+
+[[maybe_unused]] static void fmt_print_Rsession(lo_interface fmt_dest w, [[maybe_unused]] struct lib9p_ctx *ctx, struct lib9p_msg_Rsession *self) {
+ fmt_print_str(w, "Rsession {");
+ fmt_print_str(w, " tag=");
+ fmt_print_tag(w, ctx, &self->tag);
+ fmt_print_str(w, " }");
+}
+
+[[maybe_unused]] static void fmt_print_Rsread(lo_interface fmt_dest w, [[maybe_unused]] struct lib9p_ctx *ctx, struct lib9p_msg_Rsread *self) {
+ fmt_print_str(w, "Rsread {");
+ fmt_print_str(w, " tag=");
+ fmt_print_tag(w, ctx, &self->tag);
+ fmt_print_str(w, " count=");
+ fmt_print_base10(w, self->count);
+ if (utf8_is_valid_without_nul((uint8_t *)self->data, (size_t)self->count)) {
+ fmt_print_str(w, " data=");
+ fmt_print_qmem(w, self->data, self->count < 50 ? self->count : 50);
+ if (self->count > 50)
+ fmt_print_str(w, "...");
+ } else {
+ fmt_print_str(w, " data=<bytedata>");
+ }
+ fmt_print_str(w, " }");
+}
+
+[[maybe_unused]] static void fmt_print_Rswrite(lo_interface fmt_dest w, [[maybe_unused]] struct lib9p_ctx *ctx, struct lib9p_msg_Rswrite *self) {
+ fmt_print_str(w, "Rswrite {");
+ fmt_print_str(w, " tag=");
+ fmt_print_tag(w, ctx, &self->tag);
+ fmt_print_str(w, " count=");
+ fmt_print_base10(w, self->count);
+ fmt_print_str(w, " }");
+}
+
+#endif /* CONFIG_9P_ENABLE_9P2000_e */
+#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u
+[[maybe_unused]] void fmt_print_stat(lo_interface fmt_dest w, [[maybe_unused]] struct lib9p_ctx *ctx, struct lib9p_stat *self) {
+ fmt_print_byte(w, '{');
+ fmt_print_str(w, " fstype=");
+ fmt_print_base10(w, self->fstype);
+ fmt_print_str(w, " fsdev=");
+ fmt_print_base10(w, self->fsdev);
+ fmt_print_str(w, " qid=");
+ fmt_print_qid(w, ctx, &self->qid);
+ fmt_print_str(w, " mode=");
+ fmt_print_dm(w, ctx, &self->mode);
+ fmt_print_str(w, " atime=");
+ fmt_print_base10(w, self->atime);
+ fmt_print_str(w, " mtime=");
+ fmt_print_base10(w, self->mtime);
+ fmt_print_str(w, " length=");
+ fmt_print_base10(w, self->length);
+ fmt_print_str(w, " name=");
+ fmt_print_s(w, ctx, &self->name);
+ fmt_print_str(w, " owner_uname=");
+ fmt_print_s(w, ctx, &self->owner_uname);
+ fmt_print_str(w, " owner_gname=");
+ fmt_print_s(w, ctx, &self->owner_gname);
+ fmt_print_str(w, " last_modifier_uname=");
+ fmt_print_s(w, ctx, &self->last_modifier_uname);
+#if CONFIG_9P_ENABLE_9P2000_u
+ fmt_print_str(w, " extension=");
+ fmt_print_s(w, ctx, &self->extension);
+ fmt_print_str(w, " owner_unum=");
+ fmt_print_nuid(w, ctx, &self->owner_unum);
+ fmt_print_str(w, " owner_gnum=");
+ fmt_print_nuid(w, ctx, &self->owner_gnum);
+ fmt_print_str(w, " last_modifier_unum=");
+ fmt_print_nuid(w, ctx, &self->last_modifier_unum);
+#endif /* CONFIG_9P_ENABLE_9P2000_u */
+ fmt_print_str(w, " }");
+}
+
+#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */
+#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u || CONFIG_9P_ENABLE_uninitialized
+[[maybe_unused]] static void fmt_print_Tversion(lo_interface fmt_dest w, [[maybe_unused]] struct lib9p_ctx *ctx, struct lib9p_msg_Tversion *self) {
+ fmt_print_str(w, "Tversion {");
+ fmt_print_str(w, " tag=");
+ fmt_print_tag(w, ctx, &self->tag);
+ fmt_print_str(w, " max_msg_size=");
+ fmt_print_base10(w, self->max_msg_size);
+ fmt_print_str(w, " version=");
+ fmt_print_s(w, ctx, &self->version);
+ fmt_print_str(w, " }");
+}
+
+[[maybe_unused]] static void fmt_print_Rversion(lo_interface fmt_dest w, [[maybe_unused]] struct lib9p_ctx *ctx, struct lib9p_msg_Rversion *self) {
+ fmt_print_str(w, "Rversion {");
+ fmt_print_str(w, " tag=");
+ fmt_print_tag(w, ctx, &self->tag);
+ fmt_print_str(w, " max_msg_size=");
+ fmt_print_base10(w, self->max_msg_size);
+ fmt_print_str(w, " version=");
+ fmt_print_s(w, ctx, &self->version);
+ fmt_print_str(w, " }");
+}
+
+#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u || CONFIG_9P_ENABLE_uninitialized */
+#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u
+[[maybe_unused]] static void fmt_print_Tauth(lo_interface fmt_dest w, [[maybe_unused]] struct lib9p_ctx *ctx, struct lib9p_msg_Tauth *self) {
+ fmt_print_str(w, "Tauth {");
+ fmt_print_str(w, " tag=");
+ fmt_print_tag(w, ctx, &self->tag);
+ fmt_print_str(w, " afid=");
+ fmt_print_fid(w, ctx, &self->afid);
+ fmt_print_str(w, " uname=");
+ fmt_print_s(w, ctx, &self->uname);
+ fmt_print_str(w, " aname=");
+ fmt_print_s(w, ctx, &self->aname);
+#if CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_u
+ fmt_print_str(w, " unum=");
+ fmt_print_nuid(w, ctx, &self->unum);
+#endif /* CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_u */
+ fmt_print_str(w, " }");
+}
+
+[[maybe_unused]] static void fmt_print_Rauth(lo_interface fmt_dest w, [[maybe_unused]] struct lib9p_ctx *ctx, struct lib9p_msg_Rauth *self) {
+ fmt_print_str(w, "Rauth {");
+ fmt_print_str(w, " tag=");
+ fmt_print_tag(w, ctx, &self->tag);
+ fmt_print_str(w, " aqid=");
+ fmt_print_qid(w, ctx, &self->aqid);
+ fmt_print_str(w, " }");
+}
+
+[[maybe_unused]] static void fmt_print_Tattach(lo_interface fmt_dest w, [[maybe_unused]] struct lib9p_ctx *ctx, struct lib9p_msg_Tattach *self) {
+ fmt_print_str(w, "Tattach {");
+ fmt_print_str(w, " tag=");
+ fmt_print_tag(w, ctx, &self->tag);
+ fmt_print_str(w, " fid=");
+ fmt_print_fid(w, ctx, &self->fid);
+ fmt_print_str(w, " afid=");
+ fmt_print_fid(w, ctx, &self->afid);
+ fmt_print_str(w, " uname=");
+ fmt_print_s(w, ctx, &self->uname);
+ fmt_print_str(w, " aname=");
+ fmt_print_s(w, ctx, &self->aname);
+#if CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_u
+ fmt_print_str(w, " unum=");
+ fmt_print_nuid(w, ctx, &self->unum);
+#endif /* CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_u */
+ fmt_print_str(w, " }");
+}
+
+[[maybe_unused]] static void fmt_print_Rattach(lo_interface fmt_dest w, [[maybe_unused]] struct lib9p_ctx *ctx, struct lib9p_msg_Rattach *self) {
+ fmt_print_str(w, "Rattach {");
+ fmt_print_str(w, " tag=");
+ fmt_print_tag(w, ctx, &self->tag);
+ fmt_print_str(w, " qid=");
+ fmt_print_qid(w, ctx, &self->qid);
+ fmt_print_str(w, " }");
+}
+
+#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */
+#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u || CONFIG_9P_ENABLE_uninitialized
+[[maybe_unused]] static void fmt_print_Rerror(lo_interface fmt_dest w, [[maybe_unused]] struct lib9p_ctx *ctx, struct lib9p_msg_Rerror *self) {
+ fmt_print_str(w, "Rerror {");
+ fmt_print_str(w, " tag=");
+ fmt_print_tag(w, ctx, &self->tag);
+ fmt_print_str(w, " errstr=");
+ fmt_print_s(w, ctx, &self->errstr);
+#if CONFIG_9P_ENABLE_9P2000_u
+ fmt_print_str(w, " errnum=");
+ fmt_print_errno(w, ctx, &self->errnum);
+#endif /* CONFIG_9P_ENABLE_9P2000_u */
+ fmt_print_str(w, " }");
+}
+
+#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u || CONFIG_9P_ENABLE_uninitialized */
+#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u
+[[maybe_unused]] static void fmt_print_Twalk(lo_interface fmt_dest w, [[maybe_unused]] struct lib9p_ctx *ctx, struct lib9p_msg_Twalk *self) {
+ fmt_print_str(w, "Twalk {");
+ fmt_print_str(w, " tag=");
+ fmt_print_tag(w, ctx, &self->tag);
+ fmt_print_str(w, " fid=");
+ fmt_print_fid(w, ctx, &self->fid);
+ fmt_print_str(w, " newfid=");
+ fmt_print_fid(w, ctx, &self->newfid);
+ fmt_print_str(w, " nwname=");
+ fmt_print_base10(w, self->nwname);
+ fmt_print_str(w, " wname=[");
+ for (uint16_t i = 0; i < self->nwname; i++) {
+ if (i)
+ fmt_print_byte(w, ',');
+ fmt_print_byte(w, ' ');
+ fmt_print_s(w, ctx, &self->wname[i]);
+ }
+ fmt_print_str(w, " ]");
+ fmt_print_str(w, " }");
+}
+
+[[maybe_unused]] static void fmt_print_Rwalk(lo_interface fmt_dest w, [[maybe_unused]] struct lib9p_ctx *ctx, struct lib9p_msg_Rwalk *self) {
+ fmt_print_str(w, "Rwalk {");
+ fmt_print_str(w, " tag=");
+ fmt_print_tag(w, ctx, &self->tag);
+ fmt_print_str(w, " nwqid=");
+ fmt_print_base10(w, self->nwqid);
+ fmt_print_str(w, " wqid=[");
+ for (uint16_t i = 0; i < self->nwqid; i++) {
+ if (i)
+ fmt_print_byte(w, ',');
+ fmt_print_byte(w, ' ');
+ fmt_print_qid(w, ctx, &self->wqid[i]);
+ }
+ fmt_print_str(w, " ]");
+ fmt_print_str(w, " }");
+}
+
+#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */
+#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u
+[[maybe_unused]] static void fmt_print_Ropen(lo_interface fmt_dest w, [[maybe_unused]] struct lib9p_ctx *ctx, struct lib9p_msg_Ropen *self) {
+ fmt_print_str(w, "Ropen {");
+ fmt_print_str(w, " tag=");
+ fmt_print_tag(w, ctx, &self->tag);
+ fmt_print_str(w, " qid=");
+ fmt_print_qid(w, ctx, &self->qid);
+ fmt_print_str(w, " iounit=");
+ fmt_print_base10(w, self->iounit);
+ fmt_print_str(w, " }");
+}
+
+[[maybe_unused]] static void fmt_print_Tcreate(lo_interface fmt_dest w, [[maybe_unused]] struct lib9p_ctx *ctx, struct lib9p_msg_Tcreate *self) {
+ fmt_print_str(w, "Tcreate {");
+ fmt_print_str(w, " tag=");
+ fmt_print_tag(w, ctx, &self->tag);
+ fmt_print_str(w, " fid=");
+ fmt_print_fid(w, ctx, &self->fid);
+ fmt_print_str(w, " name=");
+ fmt_print_s(w, ctx, &self->name);
+ fmt_print_str(w, " perm=");
+ fmt_print_dm(w, ctx, &self->perm);
+ fmt_print_str(w, " mode=");
+ fmt_print_o(w, ctx, &self->mode);
+ fmt_print_str(w, " }");
+}
+
+[[maybe_unused]] static void fmt_print_Rcreate(lo_interface fmt_dest w, [[maybe_unused]] struct lib9p_ctx *ctx, struct lib9p_msg_Rcreate *self) {
+ fmt_print_str(w, "Rcreate {");
+ fmt_print_str(w, " tag=");
+ fmt_print_tag(w, ctx, &self->tag);
+ fmt_print_str(w, " qid=");
+ fmt_print_qid(w, ctx, &self->qid);
+ fmt_print_str(w, " iounit=");
+ fmt_print_base10(w, self->iounit);
+ fmt_print_str(w, " }");
+}
+
+#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */
+#if CONFIG_9P_ENABLE_9P2000_p9p
+[[maybe_unused]] static void fmt_print_Ropenfd(lo_interface fmt_dest w, [[maybe_unused]] struct lib9p_ctx *ctx, struct lib9p_msg_Ropenfd *self) {
+ fmt_print_str(w, "Ropenfd {");
+ fmt_print_str(w, " tag=");
+ fmt_print_tag(w, ctx, &self->tag);
+ fmt_print_str(w, " qid=");
+ fmt_print_qid(w, ctx, &self->qid);
+ fmt_print_str(w, " iounit=");
+ fmt_print_base10(w, self->iounit);
+ fmt_print_str(w, " unixfd=");
+ fmt_print_base10(w, self->unixfd);
+ fmt_print_str(w, " }");
+}
+
+#endif /* CONFIG_9P_ENABLE_9P2000_p9p */
+#if CONFIG_9P_ENABLE_9P2000_L
+[[maybe_unused]] static void fmt_print_Rlopen(lo_interface fmt_dest w, [[maybe_unused]] struct lib9p_ctx *ctx, struct lib9p_msg_Rlopen *self) {
+ fmt_print_str(w, "Rlopen {");
+ fmt_print_str(w, " tag=");
+ fmt_print_tag(w, ctx, &self->tag);
+ fmt_print_str(w, " qid=");
+ fmt_print_qid(w, ctx, &self->qid);
+ fmt_print_str(w, " iounit=");
+ fmt_print_base10(w, self->iounit);
+ fmt_print_str(w, " }");
+}
+
+[[maybe_unused]] static void fmt_print_Tlcreate(lo_interface fmt_dest w, [[maybe_unused]] struct lib9p_ctx *ctx, struct lib9p_msg_Tlcreate *self) {
+ fmt_print_str(w, "Tlcreate {");
+ fmt_print_str(w, " tag=");
+ fmt_print_tag(w, ctx, &self->tag);
+ fmt_print_str(w, " fid=");
+ fmt_print_fid(w, ctx, &self->fid);
+ fmt_print_str(w, " name=");
+ fmt_print_s(w, ctx, &self->name);
+ fmt_print_str(w, " flags=");
+ fmt_print_lo(w, ctx, &self->flags);
+ fmt_print_str(w, " mode=");
+ fmt_print_mode(w, ctx, &self->mode);
+ fmt_print_str(w, " gid=");
+ fmt_print_nuid(w, ctx, &self->gid);
+ fmt_print_str(w, " }");
+}
+
+[[maybe_unused]] static void fmt_print_Rlcreate(lo_interface fmt_dest w, [[maybe_unused]] struct lib9p_ctx *ctx, struct lib9p_msg_Rlcreate *self) {
+ fmt_print_str(w, "Rlcreate {");
+ fmt_print_str(w, " tag=");
+ fmt_print_tag(w, ctx, &self->tag);
+ fmt_print_str(w, " qid=");
+ fmt_print_qid(w, ctx, &self->qid);
+ fmt_print_str(w, " iounit=");
+ fmt_print_base10(w, self->iounit);
+ fmt_print_str(w, " }");
+}
+
+[[maybe_unused]] static void fmt_print_Tsymlink(lo_interface fmt_dest w, [[maybe_unused]] struct lib9p_ctx *ctx, struct lib9p_msg_Tsymlink *self) {
+ fmt_print_str(w, "Tsymlink {");
+ fmt_print_str(w, " tag=");
+ fmt_print_tag(w, ctx, &self->tag);
+ fmt_print_str(w, " fid=");
+ fmt_print_fid(w, ctx, &self->fid);
+ fmt_print_str(w, " name=");
+ fmt_print_s(w, ctx, &self->name);
+ fmt_print_str(w, " symtgt=");
+ fmt_print_s(w, ctx, &self->symtgt);
+ fmt_print_str(w, " gid=");
+ fmt_print_nuid(w, ctx, &self->gid);
+ fmt_print_str(w, " }");
+}
+
+[[maybe_unused]] static void fmt_print_Rsymlink(lo_interface fmt_dest w, [[maybe_unused]] struct lib9p_ctx *ctx, struct lib9p_msg_Rsymlink *self) {
+ fmt_print_str(w, "Rsymlink {");
+ fmt_print_str(w, " tag=");
+ fmt_print_tag(w, ctx, &self->tag);
+ fmt_print_str(w, " qid=");
+ fmt_print_qid(w, ctx, &self->qid);
+ fmt_print_str(w, " }");
+}
+
+[[maybe_unused]] static void fmt_print_Tmknod(lo_interface fmt_dest w, [[maybe_unused]] struct lib9p_ctx *ctx, struct lib9p_msg_Tmknod *self) {
+ fmt_print_str(w, "Tmknod {");
+ fmt_print_str(w, " tag=");
+ fmt_print_tag(w, ctx, &self->tag);
+ fmt_print_str(w, " dfid=");
+ fmt_print_fid(w, ctx, &self->dfid);
+ fmt_print_str(w, " name=");
+ fmt_print_s(w, ctx, &self->name);
+ fmt_print_str(w, " mode=");
+ fmt_print_mode(w, ctx, &self->mode);
+ fmt_print_str(w, " major=");
+ fmt_print_base10(w, self->major);
+ fmt_print_str(w, " minor=");
+ fmt_print_base10(w, self->minor);
+ fmt_print_str(w, " gid=");
+ fmt_print_nuid(w, ctx, &self->gid);
+ fmt_print_str(w, " }");
+}
+
+[[maybe_unused]] static void fmt_print_Rmknod(lo_interface fmt_dest w, [[maybe_unused]] struct lib9p_ctx *ctx, struct lib9p_msg_Rmknod *self) {
+ fmt_print_str(w, "Rmknod {");
+ fmt_print_str(w, " tag=");
+ fmt_print_tag(w, ctx, &self->tag);
+ fmt_print_str(w, " qid=");
+ fmt_print_qid(w, ctx, &self->qid);
+ fmt_print_str(w, " }");
+}
+
+[[maybe_unused]] static void fmt_print_Trename(lo_interface fmt_dest w, [[maybe_unused]] struct lib9p_ctx *ctx, struct lib9p_msg_Trename *self) {
+ fmt_print_str(w, "Trename {");
+ fmt_print_str(w, " tag=");
+ fmt_print_tag(w, ctx, &self->tag);
+ fmt_print_str(w, " fid=");
+ fmt_print_fid(w, ctx, &self->fid);
+ fmt_print_str(w, " dfid=");
+ fmt_print_fid(w, ctx, &self->dfid);
+ fmt_print_str(w, " name=");
+ fmt_print_s(w, ctx, &self->name);
+ fmt_print_str(w, " }");
+}
+
+[[maybe_unused]] static void fmt_print_Rreadlink(lo_interface fmt_dest w, [[maybe_unused]] struct lib9p_ctx *ctx, struct lib9p_msg_Rreadlink *self) {
+ fmt_print_str(w, "Rreadlink {");
+ fmt_print_str(w, " tag=");
+ fmt_print_tag(w, ctx, &self->tag);
+ fmt_print_str(w, " target=");
+ fmt_print_s(w, ctx, &self->target);
+ fmt_print_str(w, " }");
+}
+
+[[maybe_unused]] static void fmt_print_Rgetattr(lo_interface fmt_dest w, [[maybe_unused]] struct lib9p_ctx *ctx, struct lib9p_msg_Rgetattr *self) {
+ fmt_print_str(w, "Rgetattr {");
+ fmt_print_str(w, " tag=");
+ fmt_print_tag(w, ctx, &self->tag);
+ fmt_print_str(w, " valid=");
+ fmt_print_getattr(w, ctx, &self->valid);
+ fmt_print_str(w, " qid=");
+ fmt_print_qid(w, ctx, &self->qid);
+ fmt_print_str(w, " mode=");
+ fmt_print_mode(w, ctx, &self->mode);
+ fmt_print_str(w, " uid=");
+ fmt_print_nuid(w, ctx, &self->uid);
+ fmt_print_str(w, " gid=");
+ fmt_print_nuid(w, ctx, &self->gid);
+ fmt_print_str(w, " nlink=");
+ fmt_print_base10(w, self->nlink);
+ fmt_print_str(w, " rdev=");
+ fmt_print_base10(w, self->rdev);
+ fmt_print_str(w, " filesize=");
+ fmt_print_base10(w, self->filesize);
+ fmt_print_str(w, " blksize=");
+ fmt_print_base10(w, self->blksize);
+ fmt_print_str(w, " blocks=");
+ fmt_print_base10(w, self->blocks);
+ fmt_print_str(w, " atime_sec=");
+ fmt_print_base10(w, self->atime_sec);
+ fmt_print_str(w, " atime_nsec=");
+ fmt_print_base10(w, self->atime_nsec);
+ fmt_print_str(w, " mtime_sec=");
+ fmt_print_base10(w, self->mtime_sec);
+ fmt_print_str(w, " mtime_nsec=");
+ fmt_print_base10(w, self->mtime_nsec);
+ fmt_print_str(w, " ctime_sec=");
+ fmt_print_base10(w, self->ctime_sec);
+ fmt_print_str(w, " ctime_nsec=");
+ fmt_print_base10(w, self->ctime_nsec);
+ fmt_print_str(w, " btime_sec=");
+ fmt_print_base10(w, self->btime_sec);
+ fmt_print_str(w, " btime_nsec=");
+ fmt_print_base10(w, self->btime_nsec);
+ fmt_print_str(w, " gen=");
+ fmt_print_base10(w, self->gen);
+ fmt_print_str(w, " data_version=");
+ fmt_print_base10(w, self->data_version);
+ fmt_print_str(w, " }");
+}
+
+[[maybe_unused]] static void fmt_print_Txattrwalk(lo_interface fmt_dest w, [[maybe_unused]] struct lib9p_ctx *ctx, struct lib9p_msg_Txattrwalk *self) {
+ fmt_print_str(w, "Txattrwalk {");
+ fmt_print_str(w, " tag=");
+ fmt_print_tag(w, ctx, &self->tag);
+ fmt_print_str(w, " fid=");
+ fmt_print_fid(w, ctx, &self->fid);
+ fmt_print_str(w, " newfid=");
+ fmt_print_fid(w, ctx, &self->newfid);
+ fmt_print_str(w, " name=");
+ fmt_print_s(w, ctx, &self->name);
+ fmt_print_str(w, " }");
+}
+
+[[maybe_unused]] static void fmt_print_Txattrcreate(lo_interface fmt_dest w, [[maybe_unused]] struct lib9p_ctx *ctx, struct lib9p_msg_Txattrcreate *self) {
+ fmt_print_str(w, "Txattrcreate {");
+ fmt_print_str(w, " tag=");
+ fmt_print_tag(w, ctx, &self->tag);
+ fmt_print_str(w, " fid=");
+ fmt_print_fid(w, ctx, &self->fid);
+ fmt_print_str(w, " name=");
+ fmt_print_s(w, ctx, &self->name);
+ fmt_print_str(w, " attr_size=");
+ fmt_print_base10(w, self->attr_size);
+ fmt_print_str(w, " flags=");
+ fmt_print_base10(w, self->flags);
+ fmt_print_str(w, " }");
+}
+
+[[maybe_unused]] static void fmt_print_Tlock(lo_interface fmt_dest w, [[maybe_unused]] struct lib9p_ctx *ctx, struct lib9p_msg_Tlock *self) {
+ fmt_print_str(w, "Tlock {");
+ fmt_print_str(w, " tag=");
+ fmt_print_tag(w, ctx, &self->tag);
+ fmt_print_str(w, " fid=");
+ fmt_print_fid(w, ctx, &self->fid);
+ fmt_print_str(w, " type=");
+ fmt_print_lock_type(w, ctx, &self->type);
+ fmt_print_str(w, " flags=");
+ fmt_print_lock_flags(w, ctx, &self->flags);
+ fmt_print_str(w, " start=");
+ fmt_print_base10(w, self->start);
+ fmt_print_str(w, " length=");
+ fmt_print_base10(w, self->length);
+ fmt_print_str(w, " proc_id=");
+ fmt_print_base10(w, self->proc_id);
+ fmt_print_str(w, " client_id=");
+ fmt_print_s(w, ctx, &self->client_id);
+ fmt_print_str(w, " }");
+}
+
+[[maybe_unused]] static void fmt_print_Tgetlock(lo_interface fmt_dest w, [[maybe_unused]] struct lib9p_ctx *ctx, struct lib9p_msg_Tgetlock *self) {
+ fmt_print_str(w, "Tgetlock {");
+ fmt_print_str(w, " tag=");
+ fmt_print_tag(w, ctx, &self->tag);
+ fmt_print_str(w, " fid=");
+ fmt_print_fid(w, ctx, &self->fid);
+ fmt_print_str(w, " type=");
+ fmt_print_lock_type(w, ctx, &self->type);
+ fmt_print_str(w, " start=");
+ fmt_print_base10(w, self->start);
+ fmt_print_str(w, " length=");
+ fmt_print_base10(w, self->length);
+ fmt_print_str(w, " proc_id=");
+ fmt_print_base10(w, self->proc_id);
+ fmt_print_str(w, " client_id=");
+ fmt_print_s(w, ctx, &self->client_id);
+ fmt_print_str(w, " }");
+}
+
+[[maybe_unused]] static void fmt_print_Rgetlock(lo_interface fmt_dest w, [[maybe_unused]] struct lib9p_ctx *ctx, struct lib9p_msg_Rgetlock *self) {
+ fmt_print_str(w, "Rgetlock {");
+ fmt_print_str(w, " tag=");
+ fmt_print_tag(w, ctx, &self->tag);
+ fmt_print_str(w, " type=");
+ fmt_print_lock_type(w, ctx, &self->type);
+ fmt_print_str(w, " start=");
+ fmt_print_base10(w, self->start);
+ fmt_print_str(w, " length=");
+ fmt_print_base10(w, self->length);
+ fmt_print_str(w, " proc_id=");
+ fmt_print_base10(w, self->proc_id);
+ fmt_print_str(w, " client_id=");
+ fmt_print_s(w, ctx, &self->client_id);
+ fmt_print_str(w, " }");
+}
+
+[[maybe_unused]] static void fmt_print_Tlink(lo_interface fmt_dest w, [[maybe_unused]] struct lib9p_ctx *ctx, struct lib9p_msg_Tlink *self) {
+ fmt_print_str(w, "Tlink {");
+ fmt_print_str(w, " tag=");
+ fmt_print_tag(w, ctx, &self->tag);
+ fmt_print_str(w, " dfid=");
+ fmt_print_fid(w, ctx, &self->dfid);
+ fmt_print_str(w, " fid=");
+ fmt_print_fid(w, ctx, &self->fid);
+ fmt_print_str(w, " name=");
+ fmt_print_s(w, ctx, &self->name);
+ fmt_print_str(w, " }");
+}
+
+[[maybe_unused]] static void fmt_print_Tmkdir(lo_interface fmt_dest w, [[maybe_unused]] struct lib9p_ctx *ctx, struct lib9p_msg_Tmkdir *self) {
+ fmt_print_str(w, "Tmkdir {");
+ fmt_print_str(w, " tag=");
+ fmt_print_tag(w, ctx, &self->tag);
+ fmt_print_str(w, " dfid=");
+ fmt_print_fid(w, ctx, &self->dfid);
+ fmt_print_str(w, " name=");
+ fmt_print_s(w, ctx, &self->name);
+ fmt_print_str(w, " mode=");
+ fmt_print_mode(w, ctx, &self->mode);
+ fmt_print_str(w, " gid=");
+ fmt_print_nuid(w, ctx, &self->gid);
+ fmt_print_str(w, " }");
+}
+
+[[maybe_unused]] static void fmt_print_Rmkdir(lo_interface fmt_dest w, [[maybe_unused]] struct lib9p_ctx *ctx, struct lib9p_msg_Rmkdir *self) {
+ fmt_print_str(w, "Rmkdir {");
+ fmt_print_str(w, " tag=");
+ fmt_print_tag(w, ctx, &self->tag);
+ fmt_print_str(w, " qid=");
+ fmt_print_qid(w, ctx, &self->qid);
+ fmt_print_str(w, " }");
+}
+
+[[maybe_unused]] static void fmt_print_Trenameat(lo_interface fmt_dest w, [[maybe_unused]] struct lib9p_ctx *ctx, struct lib9p_msg_Trenameat *self) {
+ fmt_print_str(w, "Trenameat {");
+ fmt_print_str(w, " tag=");
+ fmt_print_tag(w, ctx, &self->tag);
+ fmt_print_str(w, " olddirfid=");
+ fmt_print_fid(w, ctx, &self->olddirfid);
+ fmt_print_str(w, " oldname=");
+ fmt_print_s(w, ctx, &self->oldname);
+ fmt_print_str(w, " newdirfid=");
+ fmt_print_fid(w, ctx, &self->newdirfid);
+ fmt_print_str(w, " newname=");
+ fmt_print_s(w, ctx, &self->newname);
+ fmt_print_str(w, " }");
+}
+
+[[maybe_unused]] static void fmt_print_Tunlinkat(lo_interface fmt_dest w, [[maybe_unused]] struct lib9p_ctx *ctx, struct lib9p_msg_Tunlinkat *self) {
+ fmt_print_str(w, "Tunlinkat {");
+ fmt_print_str(w, " tag=");
+ fmt_print_tag(w, ctx, &self->tag);
+ fmt_print_str(w, " dirfd=");
+ fmt_print_fid(w, ctx, &self->dirfd);
+ fmt_print_str(w, " name=");
+ fmt_print_s(w, ctx, &self->name);
+ fmt_print_str(w, " flags=");
+ fmt_print_base10(w, self->flags);
+ fmt_print_str(w, " }");
+}
+
+#endif /* CONFIG_9P_ENABLE_9P2000_L */
+#if CONFIG_9P_ENABLE_9P2000_e
+[[maybe_unused]] static void fmt_print_Tsread(lo_interface fmt_dest w, [[maybe_unused]] struct lib9p_ctx *ctx, struct lib9p_msg_Tsread *self) {
+ fmt_print_str(w, "Tsread {");
+ fmt_print_str(w, " tag=");
+ fmt_print_tag(w, ctx, &self->tag);
+ fmt_print_str(w, " fid=");
+ fmt_print_base10(w, self->fid);
+ fmt_print_str(w, " nwname=");
+ fmt_print_base10(w, self->nwname);
+ fmt_print_str(w, " wname=[");
+ for (uint16_t i = 0; i < self->nwname; i++) {
+ if (i)
+ fmt_print_byte(w, ',');
+ fmt_print_byte(w, ' ');
+ fmt_print_s(w, ctx, &self->wname[i]);
+ }
+ fmt_print_str(w, " ]");
+ fmt_print_str(w, " }");
+}
+
+[[maybe_unused]] static void fmt_print_Tswrite(lo_interface fmt_dest w, [[maybe_unused]] struct lib9p_ctx *ctx, struct lib9p_msg_Tswrite *self) {
+ fmt_print_str(w, "Tswrite {");
+ fmt_print_str(w, " tag=");
+ fmt_print_tag(w, ctx, &self->tag);
+ fmt_print_str(w, " fid=");
+ fmt_print_base10(w, self->fid);
+ fmt_print_str(w, " nwname=");
+ fmt_print_base10(w, self->nwname);
+ fmt_print_str(w, " wname=[");
+ for (uint16_t i = 0; i < self->nwname; i++) {
+ if (i)
+ fmt_print_byte(w, ',');
+ fmt_print_byte(w, ' ');
+ fmt_print_s(w, ctx, &self->wname[i]);
+ }
+ fmt_print_str(w, " ]");
+ fmt_print_str(w, " count=");
+ fmt_print_base10(w, self->count);
+ if (utf8_is_valid_without_nul((uint8_t *)self->data, (size_t)self->count)) {
+ fmt_print_str(w, " data=");
+ fmt_print_qmem(w, self->data, self->count < 50 ? self->count : 50);
+ if (self->count > 50)
+ fmt_print_str(w, "...");
+ } else {
+ fmt_print_str(w, " data=<bytedata>");
+ }
+ fmt_print_str(w, " }");
+}
+
+#endif /* CONFIG_9P_ENABLE_9P2000_e */
+#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u
+[[maybe_unused]] static void fmt_print_Rstat(lo_interface fmt_dest w, [[maybe_unused]] struct lib9p_ctx *ctx, struct lib9p_msg_Rstat *self) {
+ fmt_print_str(w, "Rstat {");
+ fmt_print_str(w, " tag=");
+ fmt_print_tag(w, ctx, &self->tag);
+ fmt_print_str(w, " stat=");
+ fmt_print_stat(w, ctx, &self->stat);
+ fmt_print_str(w, " }");
+}
+
+[[maybe_unused]] static void fmt_print_Twstat(lo_interface fmt_dest w, [[maybe_unused]] struct lib9p_ctx *ctx, struct lib9p_msg_Twstat *self) {
+ fmt_print_str(w, "Twstat {");
+ fmt_print_str(w, " tag=");
+ fmt_print_tag(w, ctx, &self->tag);
+ fmt_print_str(w, " fid=");
+ fmt_print_fid(w, ctx, &self->fid);
+ fmt_print_str(w, " stat=");
+ fmt_print_stat(w, ctx, &self->stat);
+ fmt_print_str(w, " }");
+}
+#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */
+
+/* tables.h *******************************************************************/
+
+const struct _lib9p_ver_tentry _lib9p_table_ver[LIB9P_VER_NUM] = {
+#if CONFIG_9P_ENABLE_9P2000
+ [LIB9P_VER_9P2000] = {.name="9P2000", .min_Rerror_size=9, .min_Rread_size=11},
+#endif /* CONFIG_9P_ENABLE_9P2000 */
+#if CONFIG_9P_ENABLE_9P2000_L
+ [LIB9P_VER_9P2000_L] = {.name="9P2000.L", .min_Rerror_size=11, .min_Rread_size=11},
+#endif /* CONFIG_9P_ENABLE_9P2000_L */
+#if CONFIG_9P_ENABLE_9P2000_e
+ [LIB9P_VER_9P2000_e] = {.name="9P2000.e", .min_Rerror_size=9, .min_Rread_size=11},
+#endif /* CONFIG_9P_ENABLE_9P2000_e */
+#if CONFIG_9P_ENABLE_9P2000_p9p
+ [LIB9P_VER_9P2000_p9p] = {.name="9P2000.p9p", .min_Rerror_size=9, .min_Rread_size=11},
+#endif /* CONFIG_9P_ENABLE_9P2000_p9p */
+#if CONFIG_9P_ENABLE_9P2000_u
+ [LIB9P_VER_9P2000_u] = {.name="9P2000.u", .min_Rerror_size=13, .min_Rread_size=11},
+#endif /* CONFIG_9P_ENABLE_9P2000_u */
+#if CONFIG_9P_ENABLE_uninitialized
+ [LIB9P_VER_uninitialized] = {.name="uninitialized", .min_Rerror_size=9, .min_Rread_size=0},
+#endif /* CONFIG_9P_ENABLE_uninitialized */
+};
+
+#define _MSG(typ) [LIB9P_TYP_##typ] = { \
+ .name = #typ, \
+ .print = (_print_fn_t)fmt_print_##typ, \
+ }
+const struct _lib9p_msg_tentry _lib9p_table_msg[LIB9P_VER_NUM][0x100] = {
+#if CONFIG_9P_ENABLE_9P2000
+ [LIB9P_VER_9P2000] = {
+ _MSG(Tversion),
+ _MSG(Rversion),
+ _MSG(Tauth),
+ _MSG(Rauth),
+ _MSG(Tattach),
+ _MSG(Rattach),
+ _MSG(Rerror),
+ _MSG(Tflush),
+ _MSG(Rflush),
+ _MSG(Twalk),
+ _MSG(Rwalk),
+ _MSG(Topen),
+ _MSG(Ropen),
+ _MSG(Tcreate),
+ _MSG(Rcreate),
+ _MSG(Tread),
+ _MSG(Rread),
+ _MSG(Twrite),
+ _MSG(Rwrite),
+ _MSG(Tclunk),
+ _MSG(Rclunk),
+ _MSG(Tremove),
+ _MSG(Rremove),
+ _MSG(Tstat),
+ _MSG(Rstat),
+ _MSG(Twstat),
+ _MSG(Rwstat),
+ },
+#endif /* CONFIG_9P_ENABLE_9P2000 */
+#if CONFIG_9P_ENABLE_9P2000_L
+ [LIB9P_VER_9P2000_L] = {
+ _MSG(Rlerror),
+ _MSG(Tstatfs),
+ _MSG(Rstatfs),
+ _MSG(Tlopen),
+ _MSG(Rlopen),
+ _MSG(Tlcreate),
+ _MSG(Rlcreate),
+ _MSG(Tsymlink),
+ _MSG(Rsymlink),
+ _MSG(Tmknod),
+ _MSG(Rmknod),
+ _MSG(Trename),
+ _MSG(Rrename),
+ _MSG(Treadlink),
+ _MSG(Rreadlink),
+ _MSG(Tgetattr),
+ _MSG(Rgetattr),
+ _MSG(Tsetattr),
+ _MSG(Rsetattr),
+ _MSG(Txattrwalk),
+ _MSG(Rxattrwalk),
+ _MSG(Txattrcreate),
+ _MSG(Rxattrcreate),
+ _MSG(Treaddir),
+ _MSG(Rreaddir),
+ _MSG(Tfsync),
+ _MSG(Rfsync),
+ _MSG(Tlock),
+ _MSG(Rlock),
+ _MSG(Tgetlock),
+ _MSG(Rgetlock),
+ _MSG(Tlink),
+ _MSG(Rlink),
+ _MSG(Tmkdir),
+ _MSG(Rmkdir),
+ _MSG(Trenameat),
+ _MSG(Rrenameat),
+ _MSG(Tunlinkat),
+ _MSG(Runlinkat),
+ _MSG(Tversion),
+ _MSG(Rversion),
+ _MSG(Tauth),
+ _MSG(Rauth),
+ _MSG(Tattach),
+ _MSG(Rattach),
+ _MSG(Tflush),
+ _MSG(Rflush),
+ _MSG(Twalk),
+ _MSG(Rwalk),
+ _MSG(Tread),
+ _MSG(Rread),
+ _MSG(Twrite),
+ _MSG(Rwrite),
+ _MSG(Tclunk),
+ _MSG(Rclunk),
+ _MSG(Tremove),
+ _MSG(Rremove),
+ },
+#endif /* CONFIG_9P_ENABLE_9P2000_L */
+#if CONFIG_9P_ENABLE_9P2000_e
+ [LIB9P_VER_9P2000_e] = {
+ _MSG(Tversion),
+ _MSG(Rversion),
+ _MSG(Tauth),
+ _MSG(Rauth),
+ _MSG(Tattach),
+ _MSG(Rattach),
+ _MSG(Rerror),
+ _MSG(Tflush),
+ _MSG(Rflush),
+ _MSG(Twalk),
+ _MSG(Rwalk),
+ _MSG(Topen),
+ _MSG(Ropen),
+ _MSG(Tcreate),
+ _MSG(Rcreate),
+ _MSG(Tread),
+ _MSG(Rread),
+ _MSG(Twrite),
+ _MSG(Rwrite),
+ _MSG(Tclunk),
+ _MSG(Rclunk),
+ _MSG(Tremove),
+ _MSG(Rremove),
+ _MSG(Tstat),
+ _MSG(Rstat),
+ _MSG(Twstat),
+ _MSG(Rwstat),
+ _MSG(Tsession),
+ _MSG(Rsession),
+ _MSG(Tsread),
+ _MSG(Rsread),
+ _MSG(Tswrite),
+ _MSG(Rswrite),
+ },
+#endif /* CONFIG_9P_ENABLE_9P2000_e */
+#if CONFIG_9P_ENABLE_9P2000_p9p
+ [LIB9P_VER_9P2000_p9p] = {
+ _MSG(Topenfd),
+ _MSG(Ropenfd),
+ _MSG(Tversion),
+ _MSG(Rversion),
+ _MSG(Tauth),
+ _MSG(Rauth),
+ _MSG(Tattach),
+ _MSG(Rattach),
+ _MSG(Rerror),
+ _MSG(Tflush),
+ _MSG(Rflush),
+ _MSG(Twalk),
+ _MSG(Rwalk),
+ _MSG(Topen),
+ _MSG(Ropen),
+ _MSG(Tcreate),
+ _MSG(Rcreate),
+ _MSG(Tread),
+ _MSG(Rread),
+ _MSG(Twrite),
+ _MSG(Rwrite),
+ _MSG(Tclunk),
+ _MSG(Rclunk),
+ _MSG(Tremove),
+ _MSG(Rremove),
+ _MSG(Tstat),
+ _MSG(Rstat),
+ _MSG(Twstat),
+ _MSG(Rwstat),
+ },
+#endif /* CONFIG_9P_ENABLE_9P2000_p9p */
+#if CONFIG_9P_ENABLE_9P2000_u
+ [LIB9P_VER_9P2000_u] = {
+ _MSG(Tversion),
+ _MSG(Rversion),
+ _MSG(Tauth),
+ _MSG(Rauth),
+ _MSG(Tattach),
+ _MSG(Rattach),
+ _MSG(Rerror),
+ _MSG(Tflush),
+ _MSG(Rflush),
+ _MSG(Twalk),
+ _MSG(Rwalk),
+ _MSG(Topen),
+ _MSG(Ropen),
+ _MSG(Tcreate),
+ _MSG(Rcreate),
+ _MSG(Tread),
+ _MSG(Rread),
+ _MSG(Twrite),
+ _MSG(Rwrite),
+ _MSG(Tclunk),
+ _MSG(Rclunk),
+ _MSG(Tremove),
+ _MSG(Rremove),
+ _MSG(Tstat),
+ _MSG(Rstat),
+ _MSG(Twstat),
+ _MSG(Rwstat),
+ },
+#endif /* CONFIG_9P_ENABLE_9P2000_u */
+#if CONFIG_9P_ENABLE_uninitialized
+ [LIB9P_VER_uninitialized] = {
+ _MSG(Tversion),
+ _MSG(Rversion),
+ _MSG(Rerror),
+ },
+#endif /* CONFIG_9P_ENABLE_uninitialized */
+};
+
+#define _MSG_RECV(typ) [LIB9P_TYP_##typ/2] = { \
+ .validate = validate_##typ, \
+ .unmarshal = (_unmarshal_fn_t)unmarshal_##typ, \
+ }
+#define _MSG_SEND(typ) [LIB9P_TYP_##typ/2] = { \
+ .marshal = (_marshal_fn_t)marshal_##typ, \
+ }
+
+const struct _lib9p_recv_tentry _lib9p_table_Tmsg_recv[LIB9P_VER_NUM][0x80] = {
+#if CONFIG_9P_ENABLE_9P2000
+ [LIB9P_VER_9P2000] = {
+ _MSG_RECV(Tversion),
+ _MSG_RECV(Tauth),
+ _MSG_RECV(Tattach),
+ _MSG_RECV(Tflush),
+ _MSG_RECV(Twalk),
+ _MSG_RECV(Topen),
+ _MSG_RECV(Tcreate),
+ _MSG_RECV(Tread),
+ _MSG_RECV(Twrite),
+ _MSG_RECV(Tclunk),
+ _MSG_RECV(Tremove),
+ _MSG_RECV(Tstat),
+ _MSG_RECV(Twstat),
+ },
+#endif /* CONFIG_9P_ENABLE_9P2000 */
+#if CONFIG_9P_ENABLE_9P2000_L
+ [LIB9P_VER_9P2000_L] = {
+ _MSG_RECV(Tstatfs),
+ _MSG_RECV(Tlopen),
+ _MSG_RECV(Tlcreate),
+ _MSG_RECV(Tsymlink),
+ _MSG_RECV(Tmknod),
+ _MSG_RECV(Trename),
+ _MSG_RECV(Treadlink),
+ _MSG_RECV(Tgetattr),
+ _MSG_RECV(Tsetattr),
+ _MSG_RECV(Txattrwalk),
+ _MSG_RECV(Txattrcreate),
+ _MSG_RECV(Treaddir),
+ _MSG_RECV(Tfsync),
+ _MSG_RECV(Tlock),
+ _MSG_RECV(Tgetlock),
+ _MSG_RECV(Tlink),
+ _MSG_RECV(Tmkdir),
+ _MSG_RECV(Trenameat),
+ _MSG_RECV(Tunlinkat),
+ _MSG_RECV(Tversion),
+ _MSG_RECV(Tauth),
+ _MSG_RECV(Tattach),
+ _MSG_RECV(Tflush),
+ _MSG_RECV(Twalk),
+ _MSG_RECV(Tread),
+ _MSG_RECV(Twrite),
+ _MSG_RECV(Tclunk),
+ _MSG_RECV(Tremove),
+ },
+#endif /* CONFIG_9P_ENABLE_9P2000_L */
+#if CONFIG_9P_ENABLE_9P2000_e
+ [LIB9P_VER_9P2000_e] = {
+ _MSG_RECV(Tversion),
+ _MSG_RECV(Tauth),
+ _MSG_RECV(Tattach),
+ _MSG_RECV(Tflush),
+ _MSG_RECV(Twalk),
+ _MSG_RECV(Topen),
+ _MSG_RECV(Tcreate),
+ _MSG_RECV(Tread),
+ _MSG_RECV(Twrite),
+ _MSG_RECV(Tclunk),
+ _MSG_RECV(Tremove),
+ _MSG_RECV(Tstat),
+ _MSG_RECV(Twstat),
+ _MSG_RECV(Tsession),
+ _MSG_RECV(Tsread),
+ _MSG_RECV(Tswrite),
+ },
+#endif /* CONFIG_9P_ENABLE_9P2000_e */
+#if CONFIG_9P_ENABLE_9P2000_p9p
+ [LIB9P_VER_9P2000_p9p] = {
+ _MSG_RECV(Topenfd),
+ _MSG_RECV(Tversion),
+ _MSG_RECV(Tauth),
+ _MSG_RECV(Tattach),
+ _MSG_RECV(Tflush),
+ _MSG_RECV(Twalk),
+ _MSG_RECV(Topen),
+ _MSG_RECV(Tcreate),
+ _MSG_RECV(Tread),
+ _MSG_RECV(Twrite),
+ _MSG_RECV(Tclunk),
+ _MSG_RECV(Tremove),
+ _MSG_RECV(Tstat),
+ _MSG_RECV(Twstat),
+ },
+#endif /* CONFIG_9P_ENABLE_9P2000_p9p */
+#if CONFIG_9P_ENABLE_9P2000_u
+ [LIB9P_VER_9P2000_u] = {
+ _MSG_RECV(Tversion),
+ _MSG_RECV(Tauth),
+ _MSG_RECV(Tattach),
+ _MSG_RECV(Tflush),
+ _MSG_RECV(Twalk),
+ _MSG_RECV(Topen),
+ _MSG_RECV(Tcreate),
+ _MSG_RECV(Tread),
+ _MSG_RECV(Twrite),
+ _MSG_RECV(Tclunk),
+ _MSG_RECV(Tremove),
+ _MSG_RECV(Tstat),
+ _MSG_RECV(Twstat),
+ },
+#endif /* CONFIG_9P_ENABLE_9P2000_u */
+#if CONFIG_9P_ENABLE_uninitialized
+ [LIB9P_VER_uninitialized] = {
+ _MSG_RECV(Tversion),
+ },
+#endif /* CONFIG_9P_ENABLE_uninitialized */
+};
+
+const struct _lib9p_recv_tentry _lib9p_table_Rmsg_recv[LIB9P_VER_NUM][0x80] = {
+#if CONFIG_9P_ENABLE_9P2000
+ [LIB9P_VER_9P2000] = {
+ _MSG_RECV(Rversion),
+ _MSG_RECV(Rauth),
+ _MSG_RECV(Rattach),
+ _MSG_RECV(Rerror),
+ _MSG_RECV(Rflush),
+ _MSG_RECV(Rwalk),
+ _MSG_RECV(Ropen),
+ _MSG_RECV(Rcreate),
+ _MSG_RECV(Rread),
+ _MSG_RECV(Rwrite),
+ _MSG_RECV(Rclunk),
+ _MSG_RECV(Rremove),
+ _MSG_RECV(Rstat),
+ _MSG_RECV(Rwstat),
+ },
+#endif /* CONFIG_9P_ENABLE_9P2000 */
+#if CONFIG_9P_ENABLE_9P2000_L
+ [LIB9P_VER_9P2000_L] = {
+ _MSG_RECV(Rlerror),
+ _MSG_RECV(Rstatfs),
+ _MSG_RECV(Rlopen),
+ _MSG_RECV(Rlcreate),
+ _MSG_RECV(Rsymlink),
+ _MSG_RECV(Rmknod),
+ _MSG_RECV(Rrename),
+ _MSG_RECV(Rreadlink),
+ _MSG_RECV(Rgetattr),
+ _MSG_RECV(Rsetattr),
+ _MSG_RECV(Rxattrwalk),
+ _MSG_RECV(Rxattrcreate),
+ _MSG_RECV(Rreaddir),
+ _MSG_RECV(Rfsync),
+ _MSG_RECV(Rlock),
+ _MSG_RECV(Rgetlock),
+ _MSG_RECV(Rlink),
+ _MSG_RECV(Rmkdir),
+ _MSG_RECV(Rrenameat),
+ _MSG_RECV(Runlinkat),
+ _MSG_RECV(Rversion),
+ _MSG_RECV(Rauth),
+ _MSG_RECV(Rattach),
+ _MSG_RECV(Rflush),
+ _MSG_RECV(Rwalk),
+ _MSG_RECV(Rread),
+ _MSG_RECV(Rwrite),
+ _MSG_RECV(Rclunk),
+ _MSG_RECV(Rremove),
+ },
+#endif /* CONFIG_9P_ENABLE_9P2000_L */
+#if CONFIG_9P_ENABLE_9P2000_e
+ [LIB9P_VER_9P2000_e] = {
+ _MSG_RECV(Rversion),
+ _MSG_RECV(Rauth),
+ _MSG_RECV(Rattach),
+ _MSG_RECV(Rerror),
+ _MSG_RECV(Rflush),
+ _MSG_RECV(Rwalk),
+ _MSG_RECV(Ropen),
+ _MSG_RECV(Rcreate),
+ _MSG_RECV(Rread),
+ _MSG_RECV(Rwrite),
+ _MSG_RECV(Rclunk),
+ _MSG_RECV(Rremove),
+ _MSG_RECV(Rstat),
+ _MSG_RECV(Rwstat),
+ _MSG_RECV(Rsession),
+ _MSG_RECV(Rsread),
+ _MSG_RECV(Rswrite),
+ },
+#endif /* CONFIG_9P_ENABLE_9P2000_e */
+#if CONFIG_9P_ENABLE_9P2000_p9p
+ [LIB9P_VER_9P2000_p9p] = {
+ _MSG_RECV(Ropenfd),
+ _MSG_RECV(Rversion),
+ _MSG_RECV(Rauth),
+ _MSG_RECV(Rattach),
+ _MSG_RECV(Rerror),
+ _MSG_RECV(Rflush),
+ _MSG_RECV(Rwalk),
+ _MSG_RECV(Ropen),
+ _MSG_RECV(Rcreate),
+ _MSG_RECV(Rread),
+ _MSG_RECV(Rwrite),
+ _MSG_RECV(Rclunk),
+ _MSG_RECV(Rremove),
+ _MSG_RECV(Rstat),
+ _MSG_RECV(Rwstat),
+ },
+#endif /* CONFIG_9P_ENABLE_9P2000_p9p */
+#if CONFIG_9P_ENABLE_9P2000_u
+ [LIB9P_VER_9P2000_u] = {
+ _MSG_RECV(Rversion),
+ _MSG_RECV(Rauth),
+ _MSG_RECV(Rattach),
+ _MSG_RECV(Rerror),
+ _MSG_RECV(Rflush),
+ _MSG_RECV(Rwalk),
+ _MSG_RECV(Ropen),
+ _MSG_RECV(Rcreate),
+ _MSG_RECV(Rread),
+ _MSG_RECV(Rwrite),
+ _MSG_RECV(Rclunk),
+ _MSG_RECV(Rremove),
+ _MSG_RECV(Rstat),
+ _MSG_RECV(Rwstat),
+ },
+#endif /* CONFIG_9P_ENABLE_9P2000_u */
+#if CONFIG_9P_ENABLE_uninitialized
+ [LIB9P_VER_uninitialized] = {
+ _MSG_RECV(Rversion),
+ _MSG_RECV(Rerror),
+ },
+#endif /* CONFIG_9P_ENABLE_uninitialized */
+};
+
+const struct _lib9p_send_tentry _lib9p_table_Tmsg_send[LIB9P_VER_NUM][0x80] = {
+#if CONFIG_9P_ENABLE_9P2000
+ [LIB9P_VER_9P2000] = {
+ _MSG_SEND(Tversion),
+ _MSG_SEND(Tauth),
+ _MSG_SEND(Tattach),
+ _MSG_SEND(Tflush),
+ _MSG_SEND(Twalk),
+ _MSG_SEND(Topen),
+ _MSG_SEND(Tcreate),
+ _MSG_SEND(Tread),
+ _MSG_SEND(Twrite),
+ _MSG_SEND(Tclunk),
+ _MSG_SEND(Tremove),
+ _MSG_SEND(Tstat),
+ _MSG_SEND(Twstat),
+ },
+#endif /* CONFIG_9P_ENABLE_9P2000 */
+#if CONFIG_9P_ENABLE_9P2000_L
+ [LIB9P_VER_9P2000_L] = {
+ _MSG_SEND(Tstatfs),
+ _MSG_SEND(Tlopen),
+ _MSG_SEND(Tlcreate),
+ _MSG_SEND(Tsymlink),
+ _MSG_SEND(Tmknod),
+ _MSG_SEND(Trename),
+ _MSG_SEND(Treadlink),
+ _MSG_SEND(Tgetattr),
+ _MSG_SEND(Tsetattr),
+ _MSG_SEND(Txattrwalk),
+ _MSG_SEND(Txattrcreate),
+ _MSG_SEND(Treaddir),
+ _MSG_SEND(Tfsync),
+ _MSG_SEND(Tlock),
+ _MSG_SEND(Tgetlock),
+ _MSG_SEND(Tlink),
+ _MSG_SEND(Tmkdir),
+ _MSG_SEND(Trenameat),
+ _MSG_SEND(Tunlinkat),
+ _MSG_SEND(Tversion),
+ _MSG_SEND(Tauth),
+ _MSG_SEND(Tattach),
+ _MSG_SEND(Tflush),
+ _MSG_SEND(Twalk),
+ _MSG_SEND(Tread),
+ _MSG_SEND(Twrite),
+ _MSG_SEND(Tclunk),
+ _MSG_SEND(Tremove),
+ },
+#endif /* CONFIG_9P_ENABLE_9P2000_L */
+#if CONFIG_9P_ENABLE_9P2000_e
+ [LIB9P_VER_9P2000_e] = {
+ _MSG_SEND(Tversion),
+ _MSG_SEND(Tauth),
+ _MSG_SEND(Tattach),
+ _MSG_SEND(Tflush),
+ _MSG_SEND(Twalk),
+ _MSG_SEND(Topen),
+ _MSG_SEND(Tcreate),
+ _MSG_SEND(Tread),
+ _MSG_SEND(Twrite),
+ _MSG_SEND(Tclunk),
+ _MSG_SEND(Tremove),
+ _MSG_SEND(Tstat),
+ _MSG_SEND(Twstat),
+ _MSG_SEND(Tsession),
+ _MSG_SEND(Tsread),
+ _MSG_SEND(Tswrite),
+ },
+#endif /* CONFIG_9P_ENABLE_9P2000_e */
+#if CONFIG_9P_ENABLE_9P2000_p9p
+ [LIB9P_VER_9P2000_p9p] = {
+ _MSG_SEND(Topenfd),
+ _MSG_SEND(Tversion),
+ _MSG_SEND(Tauth),
+ _MSG_SEND(Tattach),
+ _MSG_SEND(Tflush),
+ _MSG_SEND(Twalk),
+ _MSG_SEND(Topen),
+ _MSG_SEND(Tcreate),
+ _MSG_SEND(Tread),
+ _MSG_SEND(Twrite),
+ _MSG_SEND(Tclunk),
+ _MSG_SEND(Tremove),
+ _MSG_SEND(Tstat),
+ _MSG_SEND(Twstat),
+ },
+#endif /* CONFIG_9P_ENABLE_9P2000_p9p */
+#if CONFIG_9P_ENABLE_9P2000_u
+ [LIB9P_VER_9P2000_u] = {
+ _MSG_SEND(Tversion),
+ _MSG_SEND(Tauth),
+ _MSG_SEND(Tattach),
+ _MSG_SEND(Tflush),
+ _MSG_SEND(Twalk),
+ _MSG_SEND(Topen),
+ _MSG_SEND(Tcreate),
+ _MSG_SEND(Tread),
+ _MSG_SEND(Twrite),
+ _MSG_SEND(Tclunk),
+ _MSG_SEND(Tremove),
+ _MSG_SEND(Tstat),
+ _MSG_SEND(Twstat),
+ },
+#endif /* CONFIG_9P_ENABLE_9P2000_u */
+#if CONFIG_9P_ENABLE_uninitialized
+ [LIB9P_VER_uninitialized] = {
+ _MSG_SEND(Tversion),
+ },
+#endif /* CONFIG_9P_ENABLE_uninitialized */
+};
+
+const struct _lib9p_send_tentry _lib9p_table_Rmsg_send[LIB9P_VER_NUM][0x80] = {
+#if CONFIG_9P_ENABLE_9P2000
+ [LIB9P_VER_9P2000] = {
+ _MSG_SEND(Rversion),
+ _MSG_SEND(Rauth),
+ _MSG_SEND(Rattach),
+ _MSG_SEND(Rerror),
+ _MSG_SEND(Rflush),
+ _MSG_SEND(Rwalk),
+ _MSG_SEND(Ropen),
+ _MSG_SEND(Rcreate),
+ _MSG_SEND(Rread),
+ _MSG_SEND(Rwrite),
+ _MSG_SEND(Rclunk),
+ _MSG_SEND(Rremove),
+ _MSG_SEND(Rstat),
+ _MSG_SEND(Rwstat),
+ },
+#endif /* CONFIG_9P_ENABLE_9P2000 */
+#if CONFIG_9P_ENABLE_9P2000_L
+ [LIB9P_VER_9P2000_L] = {
+ _MSG_SEND(Rlerror),
+ _MSG_SEND(Rstatfs),
+ _MSG_SEND(Rlopen),
+ _MSG_SEND(Rlcreate),
+ _MSG_SEND(Rsymlink),
+ _MSG_SEND(Rmknod),
+ _MSG_SEND(Rrename),
+ _MSG_SEND(Rreadlink),
+ _MSG_SEND(Rgetattr),
+ _MSG_SEND(Rsetattr),
+ _MSG_SEND(Rxattrwalk),
+ _MSG_SEND(Rxattrcreate),
+ _MSG_SEND(Rreaddir),
+ _MSG_SEND(Rfsync),
+ _MSG_SEND(Rlock),
+ _MSG_SEND(Rgetlock),
+ _MSG_SEND(Rlink),
+ _MSG_SEND(Rmkdir),
+ _MSG_SEND(Rrenameat),
+ _MSG_SEND(Runlinkat),
+ _MSG_SEND(Rversion),
+ _MSG_SEND(Rauth),
+ _MSG_SEND(Rattach),
+ _MSG_SEND(Rflush),
+ _MSG_SEND(Rwalk),
+ _MSG_SEND(Rread),
+ _MSG_SEND(Rwrite),
+ _MSG_SEND(Rclunk),
+ _MSG_SEND(Rremove),
+ },
+#endif /* CONFIG_9P_ENABLE_9P2000_L */
+#if CONFIG_9P_ENABLE_9P2000_e
+ [LIB9P_VER_9P2000_e] = {
+ _MSG_SEND(Rversion),
+ _MSG_SEND(Rauth),
+ _MSG_SEND(Rattach),
+ _MSG_SEND(Rerror),
+ _MSG_SEND(Rflush),
+ _MSG_SEND(Rwalk),
+ _MSG_SEND(Ropen),
+ _MSG_SEND(Rcreate),
+ _MSG_SEND(Rread),
+ _MSG_SEND(Rwrite),
+ _MSG_SEND(Rclunk),
+ _MSG_SEND(Rremove),
+ _MSG_SEND(Rstat),
+ _MSG_SEND(Rwstat),
+ _MSG_SEND(Rsession),
+ _MSG_SEND(Rsread),
+ _MSG_SEND(Rswrite),
+ },
+#endif /* CONFIG_9P_ENABLE_9P2000_e */
+#if CONFIG_9P_ENABLE_9P2000_p9p
+ [LIB9P_VER_9P2000_p9p] = {
+ _MSG_SEND(Ropenfd),
+ _MSG_SEND(Rversion),
+ _MSG_SEND(Rauth),
+ _MSG_SEND(Rattach),
+ _MSG_SEND(Rerror),
+ _MSG_SEND(Rflush),
+ _MSG_SEND(Rwalk),
+ _MSG_SEND(Ropen),
+ _MSG_SEND(Rcreate),
+ _MSG_SEND(Rread),
+ _MSG_SEND(Rwrite),
+ _MSG_SEND(Rclunk),
+ _MSG_SEND(Rremove),
+ _MSG_SEND(Rstat),
+ _MSG_SEND(Rwstat),
+ },
+#endif /* CONFIG_9P_ENABLE_9P2000_p9p */
+#if CONFIG_9P_ENABLE_9P2000_u
+ [LIB9P_VER_9P2000_u] = {
+ _MSG_SEND(Rversion),
+ _MSG_SEND(Rauth),
+ _MSG_SEND(Rattach),
+ _MSG_SEND(Rerror),
+ _MSG_SEND(Rflush),
+ _MSG_SEND(Rwalk),
+ _MSG_SEND(Ropen),
+ _MSG_SEND(Rcreate),
+ _MSG_SEND(Rread),
+ _MSG_SEND(Rwrite),
+ _MSG_SEND(Rclunk),
+ _MSG_SEND(Rremove),
+ _MSG_SEND(Rstat),
+ _MSG_SEND(Rwstat),
+ },
+#endif /* CONFIG_9P_ENABLE_9P2000_u */
+#if CONFIG_9P_ENABLE_uninitialized
+ [LIB9P_VER_uninitialized] = {
+ _MSG_SEND(Rversion),
+ _MSG_SEND(Rerror),
+ },
+#endif /* CONFIG_9P_ENABLE_uninitialized */
+};
+
+#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u
+LM_FLATTEN size_t_or_error _lib9p_stat_validate(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes, uint32_t *ret_net_size) {
+ return validate_stat(ctx, net_size, net_bytes, ret_net_size);
+}
+LM_FLATTEN void _lib9p_stat_unmarshal(struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out) {
+ unmarshal_stat(ctx, net_bytes, out);
+}
+LM_FLATTEN bool _lib9p_stat_marshal(struct lib9p_ctx *ctx, struct lib9p_stat *val, struct _marshal_ret *ret) {
+ return marshal_stat(ctx, val, ret);
+}
+#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */
diff --git a/lib9p/include/lib9p/9p.generated.h b/lib9p/core_include/lib9p/_core_generated.h
index 7e901a3..3f87181 100644
--- a/lib9p/include/lib9p/9p.generated.h
+++ b/lib9p/core_include/lib9p/_core_generated.h
@@ -1,18 +1,11 @@
-/* Generated by `lib9p/proto.gen lib9p/idl/2002-9P2000.9p lib9p/idl/2003-9P2000.p9p.9p lib9p/idl/2005-9P2000.u.9p lib9p/idl/2010-9P2000.L.9p lib9p/idl/2012-9P2000.e.9p`. DO NOT EDIT! */
+/* Generated by `lib9p/core.gen lib9p/idl/0000-uninitialized.9p lib9p/idl/2002-9P2000.9p lib9p/idl/2003-9P2000.p9p.9p lib9p/idl/2005-9P2000.u.9p lib9p/idl/2010-9P2000.L.9p lib9p/idl/2012-9P2000.e.9p`. DO NOT EDIT! */
-#ifndef _LIB9P_9P_H_
- #error Do not include <lib9p/9p.generated.h> directly; include <lib9p/9p.h> instead
+#ifndef _LIB9P_CORE_H_
+ #error Do not include <lib9p/_core_generated.h> directly; include <lib9p/core.h> instead
#endif
-#include <stdint.h> /* for uint{n}_t types */
-
-#include <libfmt/fmt.h> /* for fmt_formatter */
-#include <libhw/generic/net.h> /* for struct iovec */
-
/* config *********************************************************************/
-#include "config.h"
-
#ifndef CONFIG_9P_ENABLE_9P2000
#error config.h must define CONFIG_9P_ENABLE_9P2000
#endif
@@ -40,28 +33,33 @@
#error config.h must define CONFIG_9P_ENABLE_9P2000_u
#endif
+#ifndef CONFIG_9P_ENABLE_uninitialized
+ #error config.h must define CONFIG_9P_ENABLE_uninitialized
+#endif
+
+#define _LIB9P_ENABLE_stat CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u
+
/* enum version ***************************************************************/
enum lib9p_version {
- LIB9P_VER_unknown = 0, /* "unknown" */
+ LIB9P_VER_uninitialized = 0, /* "uninitialized" */
#if CONFIG_9P_ENABLE_9P2000
- LIB9P_VER_9P2000, /* "9P2000" */
+ LIB9P_VER_9P2000, /* "9P2000" */
#endif /* CONFIG_9P_ENABLE_9P2000 */
#if CONFIG_9P_ENABLE_9P2000_L
- LIB9P_VER_9P2000_L, /* "9P2000.L" */
+ LIB9P_VER_9P2000_L, /* "9P2000.L" */
#endif /* CONFIG_9P_ENABLE_9P2000_L */
#if CONFIG_9P_ENABLE_9P2000_e
- LIB9P_VER_9P2000_e, /* "9P2000.e" */
+ LIB9P_VER_9P2000_e, /* "9P2000.e" */
#endif /* CONFIG_9P_ENABLE_9P2000_e */
#if CONFIG_9P_ENABLE_9P2000_p9p
- LIB9P_VER_9P2000_p9p, /* "9P2000.p9p" */
+ LIB9P_VER_9P2000_p9p, /* "9P2000.p9p" */
#endif /* CONFIG_9P_ENABLE_9P2000_p9p */
#if CONFIG_9P_ENABLE_9P2000_u
- LIB9P_VER_9P2000_u, /* "9P2000.u" */
+ LIB9P_VER_9P2000_u, /* "9P2000.u" */
#endif /* CONFIG_9P_ENABLE_9P2000_u */
LIB9P_VER_NUM,
};
-LO_IMPLEMENTATION_H(fmt_formatter, enum lib9p_version, lib9p_version);
/* enum msg_type **************************************************************/
@@ -111,14 +109,20 @@ enum lib9p_msg_type { /* uint8_t */
LIB9P_TYP_Topenfd = 98,
LIB9P_TYP_Ropenfd = 99,
#endif /* CONFIG_9P_ENABLE_9P2000_p9p */
-#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u
+#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u || CONFIG_9P_ENABLE_uninitialized
LIB9P_TYP_Tversion = 100,
LIB9P_TYP_Rversion = 101,
+#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u || CONFIG_9P_ENABLE_uninitialized */
+#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u
LIB9P_TYP_Tauth = 102,
LIB9P_TYP_Rauth = 103,
LIB9P_TYP_Tattach = 104,
LIB9P_TYP_Rattach = 105,
+#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */
+#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u || CONFIG_9P_ENABLE_uninitialized
LIB9P_TYP_Rerror = 107,
+#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u || CONFIG_9P_ENABLE_uninitialized */
+#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u
LIB9P_TYP_Tflush = 108,
LIB9P_TYP_Rflush = 109,
LIB9P_TYP_Twalk = 110,
@@ -155,33 +159,22 @@ enum lib9p_msg_type { /* uint8_t */
LIB9P_TYP_Rswrite = 155,
#endif /* CONFIG_9P_ENABLE_9P2000_e */
};
-LO_IMPLEMENTATION_H(fmt_formatter, enum lib9p_msg_type, lib9p_msg_type);
/* payload types **************************************************************/
-#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u
+#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u || CONFIG_9P_ENABLE_uninitialized
/* size = 2 ; max_iov = 1 ; max_copy = 2 */
typedef uint16_t lib9p_tag_t;
-LO_IMPLEMENTATION_H(fmt_formatter, lib9p_tag_t, lib9p_tag);
#define LIB9P_TAG_NOTAG ((lib9p_tag_t)(UINT16_MAX))
/* size = 4 ; max_iov = 1 ; max_copy = 4 */
typedef uint32_t lib9p_fid_t;
-LO_IMPLEMENTATION_H(fmt_formatter, lib9p_fid_t, lib9p_fid);
#define LIB9P_FID_NOFID ((lib9p_fid_t)(UINT32_MAX))
-/* min_size = 2 ; exp_size = 29 ; max_size = 65,537 ; max_iov = 2 ; max_copy = 2 */
-struct lib9p_s {
- uint16_t len;
- [[gnu::nonstring]] char *utf8;
-};
-LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_s, lib9p_s);
-
-#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */
-#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u
+#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u || CONFIG_9P_ENABLE_uninitialized */
+#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u || CONFIG_9P_ENABLE_uninitialized
/* size = 4 ; max_iov = 1 ; max_copy = 4 */
typedef uint32_t lib9p_dm_t;
-LO_IMPLEMENTATION_H(fmt_formatter, lib9p_dm_t, lib9p_dm);
/* bits */
#define LIB9P_DM_DIR ((lib9p_dm_t)(UINT32_C(1)<<31))
#define LIB9P_DM_APPEND ((lib9p_dm_t)(UINT32_C(1)<<30))
@@ -222,11 +215,10 @@ LO_IMPLEMENTATION_H(fmt_formatter, lib9p_dm_t, lib9p_dm);
/* masks */
#define LIB9P_DM_PERM_MASK ((lib9p_dm_t)(0b000000000000000000000111111111))
-#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */
-#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u
+#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u || CONFIG_9P_ENABLE_uninitialized */
+#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u || CONFIG_9P_ENABLE_uninitialized
/* size = 1 ; max_iov = 1 ; max_copy = 1 */
typedef uint8_t lib9p_qt_t;
-LO_IMPLEMENTATION_H(fmt_formatter, lib9p_qt_t, lib9p_qt);
/* bits */
#define LIB9P_QT_DIR ((lib9p_qt_t)(UINT8_C(1)<<7))
#define LIB9P_QT_APPEND ((lib9p_qt_t)(UINT8_C(1)<<6))
@@ -241,18 +233,10 @@ LO_IMPLEMENTATION_H(fmt_formatter, lib9p_qt_t, lib9p_qt);
/* aliases */
#define LIB9P_QT_FILE ((lib9p_qt_t)(0))
-#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */
-#if CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_u
-/* size = 4 ; max_iov = 1 ; max_copy = 4 */
-typedef uint32_t lib9p_nuid_t;
-LO_IMPLEMENTATION_H(fmt_formatter, lib9p_nuid_t, lib9p_nuid);
-#define LIB9P_NUID_NONUID ((lib9p_nuid_t)(UINT32_MAX))
-
-#endif /* CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_u */
+#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u || CONFIG_9P_ENABLE_uninitialized */
#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u
/* size = 1 ; max_iov = 1 ; max_copy = 1 */
typedef uint8_t lib9p_o_t;
-LO_IMPLEMENTATION_H(fmt_formatter, lib9p_o_t, lib9p_o);
/* bits */
#define _LIB9P_O_UNUSED_7 ((lib9p_o_t)(UINT8_C(1)<<7))
#define LIB9P_O_RCLOSE ((lib9p_o_t)(UINT8_C(1)<<6))
@@ -274,20 +258,152 @@ LO_IMPLEMENTATION_H(fmt_formatter, lib9p_o_t, lib9p_o);
#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */
#if CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_u
/* size = 4 ; max_iov = 1 ; max_copy = 4 */
+typedef uint32_t lib9p_nuid_t;
+#define LIB9P_NUID_NONUID ((lib9p_nuid_t)(UINT32_MAX))
+
+/* size = 4 ; max_iov = 1 ; max_copy = 4 */
typedef uint32_t lib9p_errno_t;
-LO_IMPLEMENTATION_H(fmt_formatter, lib9p_errno_t, lib9p_errno);
-#define LIB9P_ERRNO_NOERROR ((lib9p_errno_t)(0))
+#define LIB9P_ERRNO_NOERROR ((lib9p_errno_t)(0))
+#define LIB9P_ERRNO_L_EPERM ((lib9p_errno_t)(1))
+#define LIB9P_ERRNO_L_ENOENT ((lib9p_errno_t)(2))
+#define LIB9P_ERRNO_L_ESRCH ((lib9p_errno_t)(3))
+#define LIB9P_ERRNO_L_EINTR ((lib9p_errno_t)(4))
+#define LIB9P_ERRNO_L_EIO ((lib9p_errno_t)(5))
+#define LIB9P_ERRNO_L_ENXIO ((lib9p_errno_t)(6))
+#define LIB9P_ERRNO_L_E2BIG ((lib9p_errno_t)(7))
+#define LIB9P_ERRNO_L_ENOEXEC ((lib9p_errno_t)(8))
+#define LIB9P_ERRNO_L_EBADF ((lib9p_errno_t)(9))
+#define LIB9P_ERRNO_L_ECHILD ((lib9p_errno_t)(10))
+#define LIB9P_ERRNO_L_EAGAIN ((lib9p_errno_t)(11))
+#define LIB9P_ERRNO_L_ENOMEM ((lib9p_errno_t)(12))
+#define LIB9P_ERRNO_L_EACCES ((lib9p_errno_t)(13))
+#define LIB9P_ERRNO_L_EFAULT ((lib9p_errno_t)(14))
+#define LIB9P_ERRNO_L_ENOTBLK ((lib9p_errno_t)(15))
+#define LIB9P_ERRNO_L_EBUSY ((lib9p_errno_t)(16))
+#define LIB9P_ERRNO_L_EEXIST ((lib9p_errno_t)(17))
+#define LIB9P_ERRNO_L_EXDEV ((lib9p_errno_t)(18))
+#define LIB9P_ERRNO_L_ENODEV ((lib9p_errno_t)(19))
+#define LIB9P_ERRNO_L_ENOTDIR ((lib9p_errno_t)(20))
+#define LIB9P_ERRNO_L_EISDIR ((lib9p_errno_t)(21))
+#define LIB9P_ERRNO_L_EINVAL ((lib9p_errno_t)(22))
+#define LIB9P_ERRNO_L_ENFILE ((lib9p_errno_t)(23))
+#define LIB9P_ERRNO_L_EMFILE ((lib9p_errno_t)(24))
+#define LIB9P_ERRNO_L_ENOTTY ((lib9p_errno_t)(25))
+#define LIB9P_ERRNO_L_ETXTBSY ((lib9p_errno_t)(26))
+#define LIB9P_ERRNO_L_EFBIG ((lib9p_errno_t)(27))
+#define LIB9P_ERRNO_L_ENOSPC ((lib9p_errno_t)(28))
+#define LIB9P_ERRNO_L_ESPIPE ((lib9p_errno_t)(29))
+#define LIB9P_ERRNO_L_EROFS ((lib9p_errno_t)(30))
+#define LIB9P_ERRNO_L_EMLINK ((lib9p_errno_t)(31))
+#define LIB9P_ERRNO_L_EPIPE ((lib9p_errno_t)(32))
+#define LIB9P_ERRNO_L_EDOM ((lib9p_errno_t)(33))
+#define LIB9P_ERRNO_L_ERANGE ((lib9p_errno_t)(34))
+#define LIB9P_ERRNO_L_EDEADLK ((lib9p_errno_t)(35))
+#define LIB9P_ERRNO_L_ENAMETOOLONG ((lib9p_errno_t)(36))
+#define LIB9P_ERRNO_L_ENOLCK ((lib9p_errno_t)(37))
+#define LIB9P_ERRNO_L_ENOSYS ((lib9p_errno_t)(38))
+#define LIB9P_ERRNO_L_ENOTEMPTY ((lib9p_errno_t)(39))
+#define LIB9P_ERRNO_L_ELOOP ((lib9p_errno_t)(40))
+#define LIB9P_ERRNO_L_ENOMSG ((lib9p_errno_t)(42))
+#define LIB9P_ERRNO_L_EIDRM ((lib9p_errno_t)(43))
+#define LIB9P_ERRNO_L_ECHRNG ((lib9p_errno_t)(44))
+#define LIB9P_ERRNO_L_EL2NSYNC ((lib9p_errno_t)(45))
+#define LIB9P_ERRNO_L_EL3HLT ((lib9p_errno_t)(46))
+#define LIB9P_ERRNO_L_EL3RST ((lib9p_errno_t)(47))
+#define LIB9P_ERRNO_L_ELNRNG ((lib9p_errno_t)(48))
+#define LIB9P_ERRNO_L_EUNATCH ((lib9p_errno_t)(49))
+#define LIB9P_ERRNO_L_ENOCSI ((lib9p_errno_t)(50))
+#define LIB9P_ERRNO_L_EL2HLT ((lib9p_errno_t)(51))
+#define LIB9P_ERRNO_L_EBADE ((lib9p_errno_t)(52))
+#define LIB9P_ERRNO_L_EBADR ((lib9p_errno_t)(53))
+#define LIB9P_ERRNO_L_EXFULL ((lib9p_errno_t)(54))
+#define LIB9P_ERRNO_L_ENOANO ((lib9p_errno_t)(55))
+#define LIB9P_ERRNO_L_EBADRQC ((lib9p_errno_t)(56))
+#define LIB9P_ERRNO_L_EBADSLT ((lib9p_errno_t)(57))
+#define LIB9P_ERRNO_L_EBFONT ((lib9p_errno_t)(59))
+#define LIB9P_ERRNO_L_ENOSTR ((lib9p_errno_t)(60))
+#define LIB9P_ERRNO_L_ENODATA ((lib9p_errno_t)(61))
+#define LIB9P_ERRNO_L_ETIME ((lib9p_errno_t)(62))
+#define LIB9P_ERRNO_L_ENOSR ((lib9p_errno_t)(63))
+#define LIB9P_ERRNO_L_ENONET ((lib9p_errno_t)(64))
+#define LIB9P_ERRNO_L_ENOPKG ((lib9p_errno_t)(65))
+#define LIB9P_ERRNO_L_EREMOTE ((lib9p_errno_t)(66))
+#define LIB9P_ERRNO_L_ENOLINK ((lib9p_errno_t)(67))
+#define LIB9P_ERRNO_L_EADV ((lib9p_errno_t)(68))
+#define LIB9P_ERRNO_L_ESRMNT ((lib9p_errno_t)(69))
+#define LIB9P_ERRNO_L_ECOMM ((lib9p_errno_t)(70))
+#define LIB9P_ERRNO_L_EPROTO ((lib9p_errno_t)(71))
+#define LIB9P_ERRNO_L_EMULTIHOP ((lib9p_errno_t)(72))
+#define LIB9P_ERRNO_L_EDOTDOT ((lib9p_errno_t)(73))
+#define LIB9P_ERRNO_L_EBADMSG ((lib9p_errno_t)(74))
+#define LIB9P_ERRNO_L_EOVERFLOW ((lib9p_errno_t)(75))
+#define LIB9P_ERRNO_L_ENOTUNIQ ((lib9p_errno_t)(76))
+#define LIB9P_ERRNO_L_EBADFD ((lib9p_errno_t)(77))
+#define LIB9P_ERRNO_L_EREMCHG ((lib9p_errno_t)(78))
+#define LIB9P_ERRNO_L_ELIBACC ((lib9p_errno_t)(79))
+#define LIB9P_ERRNO_L_ELIBBAD ((lib9p_errno_t)(80))
+#define LIB9P_ERRNO_L_ELIBSCN ((lib9p_errno_t)(81))
+#define LIB9P_ERRNO_L_ELIBMAX ((lib9p_errno_t)(82))
+#define LIB9P_ERRNO_L_ELIBEXEC ((lib9p_errno_t)(83))
+#define LIB9P_ERRNO_L_EILSEQ ((lib9p_errno_t)(84))
+#define LIB9P_ERRNO_L_ERESTART ((lib9p_errno_t)(85))
+#define LIB9P_ERRNO_L_ESTRPIPE ((lib9p_errno_t)(86))
+#define LIB9P_ERRNO_L_EUSERS ((lib9p_errno_t)(87))
+#define LIB9P_ERRNO_L_ENOTSOCK ((lib9p_errno_t)(88))
+#define LIB9P_ERRNO_L_EDESTADDRREQ ((lib9p_errno_t)(89))
+#define LIB9P_ERRNO_L_EMSGSIZE ((lib9p_errno_t)(90))
+#define LIB9P_ERRNO_L_EPROTOTYPE ((lib9p_errno_t)(91))
+#define LIB9P_ERRNO_L_ENOPROTOOPT ((lib9p_errno_t)(92))
+#define LIB9P_ERRNO_L_EPROTONOSUPPORT ((lib9p_errno_t)(93))
+#define LIB9P_ERRNO_L_ESOCKTNOSUPPORT ((lib9p_errno_t)(94))
+#define LIB9P_ERRNO_L_EOPNOTSUPP ((lib9p_errno_t)(95))
+#define LIB9P_ERRNO_L_EPFNOSUPPORT ((lib9p_errno_t)(96))
+#define LIB9P_ERRNO_L_EAFNOSUPPORT ((lib9p_errno_t)(97))
+#define LIB9P_ERRNO_L_EADDRINUSE ((lib9p_errno_t)(98))
+#define LIB9P_ERRNO_L_EADDRNOTAVAIL ((lib9p_errno_t)(99))
+#define LIB9P_ERRNO_L_ENETDOWN ((lib9p_errno_t)(100))
+#define LIB9P_ERRNO_L_ENETUNREACH ((lib9p_errno_t)(101))
+#define LIB9P_ERRNO_L_ENETRESET ((lib9p_errno_t)(102))
+#define LIB9P_ERRNO_L_ECONNABORTED ((lib9p_errno_t)(103))
+#define LIB9P_ERRNO_L_ECONNRESET ((lib9p_errno_t)(104))
+#define LIB9P_ERRNO_L_ENOBUFS ((lib9p_errno_t)(105))
+#define LIB9P_ERRNO_L_EISCONN ((lib9p_errno_t)(106))
+#define LIB9P_ERRNO_L_ENOTCONN ((lib9p_errno_t)(107))
+#define LIB9P_ERRNO_L_ESHUTDOWN ((lib9p_errno_t)(108))
+#define LIB9P_ERRNO_L_ETOOMANYREFS ((lib9p_errno_t)(109))
+#define LIB9P_ERRNO_L_ETIMEDOUT ((lib9p_errno_t)(110))
+#define LIB9P_ERRNO_L_ECONNREFUSED ((lib9p_errno_t)(111))
+#define LIB9P_ERRNO_L_EHOSTDOWN ((lib9p_errno_t)(112))
+#define LIB9P_ERRNO_L_EHOSTUNREACH ((lib9p_errno_t)(113))
+#define LIB9P_ERRNO_L_EALREADY ((lib9p_errno_t)(114))
+#define LIB9P_ERRNO_L_EINPROGRESS ((lib9p_errno_t)(115))
+#define LIB9P_ERRNO_L_ESTALE ((lib9p_errno_t)(116))
+#define LIB9P_ERRNO_L_EUCLEAN ((lib9p_errno_t)(117))
+#define LIB9P_ERRNO_L_ENOTNAM ((lib9p_errno_t)(118))
+#define LIB9P_ERRNO_L_ENAVAIL ((lib9p_errno_t)(119))
+#define LIB9P_ERRNO_L_EISNAM ((lib9p_errno_t)(120))
+#define LIB9P_ERRNO_L_EREMOTEIO ((lib9p_errno_t)(121))
+#define LIB9P_ERRNO_L_EDQUOT ((lib9p_errno_t)(122))
+#define LIB9P_ERRNO_L_ENOMEDIUM ((lib9p_errno_t)(123))
+#define LIB9P_ERRNO_L_EMEDIUMTYPE ((lib9p_errno_t)(124))
+#define LIB9P_ERRNO_L_ECANCELED ((lib9p_errno_t)(125))
+#define LIB9P_ERRNO_L_ENOKEY ((lib9p_errno_t)(126))
+#define LIB9P_ERRNO_L_EKEYEXPIRED ((lib9p_errno_t)(127))
+#define LIB9P_ERRNO_L_EKEYREVOKED ((lib9p_errno_t)(128))
+#define LIB9P_ERRNO_L_EKEYREJECTED ((lib9p_errno_t)(129))
+#define LIB9P_ERRNO_L_EOWNERDEAD ((lib9p_errno_t)(130))
+#define LIB9P_ERRNO_L_ENOTRECOVERABLE ((lib9p_errno_t)(131))
+#define LIB9P_ERRNO_L_ERFKILL ((lib9p_errno_t)(132))
+#define LIB9P_ERRNO_L_EHWPOISON ((lib9p_errno_t)(133))
#endif /* CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_u */
#if CONFIG_9P_ENABLE_9P2000_L
/* size = 4 ; max_iov = 1 ; max_copy = 4 */
typedef uint32_t lib9p_super_magic_t;
-LO_IMPLEMENTATION_H(fmt_formatter, lib9p_super_magic_t, lib9p_super_magic);
#define LIB9P_SUPER_MAGIC_V9FS_MAGIC ((lib9p_super_magic_t)(16914839))
/* size = 4 ; max_iov = 1 ; max_copy = 4 */
typedef uint32_t lib9p_lo_t;
-LO_IMPLEMENTATION_H(fmt_formatter, lib9p_lo_t, lib9p_lo);
/* bits */
#define _LIB9P_LO_UNUSED_31 ((lib9p_lo_t)(UINT32_C(1)<<31))
#define _LIB9P_LO_UNUSED_30 ((lib9p_lo_t)(UINT32_C(1)<<30))
@@ -332,7 +448,6 @@ LO_IMPLEMENTATION_H(fmt_formatter, lib9p_lo_t, lib9p_lo);
/* size = 1 ; max_iov = 1 ; max_copy = 1 */
typedef uint8_t lib9p_dt_t;
-LO_IMPLEMENTATION_H(fmt_formatter, lib9p_dt_t, lib9p_dt);
#define LIB9P_DT_UNKNOWN ((lib9p_dt_t)(0))
#define LIB9P_DT_PIPE ((lib9p_dt_t)(1))
#define LIB9P_DT_CHAR_DEV ((lib9p_dt_t)(2))
@@ -345,7 +460,6 @@ LO_IMPLEMENTATION_H(fmt_formatter, lib9p_dt_t, lib9p_dt);
/* size = 4 ; max_iov = 1 ; max_copy = 4 */
typedef uint32_t lib9p_mode_t;
-LO_IMPLEMENTATION_H(fmt_formatter, lib9p_mode_t, lib9p_mode);
/* bits */
#define _LIB9P_MODE_UNUSED_31 ((lib9p_mode_t)(UINT32_C(1)<<31))
#define _LIB9P_MODE_UNUSED_30 ((lib9p_mode_t)(UINT32_C(1)<<30))
@@ -393,13 +507,11 @@ LO_IMPLEMENTATION_H(fmt_formatter, lib9p_mode_t, lib9p_mode);
/* size = 4 ; max_iov = 1 ; max_copy = 4 */
typedef uint32_t lib9p_b4_t;
-LO_IMPLEMENTATION_H(fmt_formatter, lib9p_b4_t, lib9p_b4);
#define LIB9P_B4_FALSE ((lib9p_b4_t)(0))
#define LIB9P_B4_TRUE ((lib9p_b4_t)(1))
/* size = 8 ; max_iov = 1 ; max_copy = 8 */
typedef uint64_t lib9p_getattr_t;
-LO_IMPLEMENTATION_H(fmt_formatter, lib9p_getattr_t, lib9p_getattr);
/* bits */
#define _LIB9P_GETATTR_UNUSED_63 ((lib9p_getattr_t)(UINT64_C(1)<<63))
#define _LIB9P_GETATTR_UNUSED_62 ((lib9p_getattr_t)(UINT64_C(1)<<62))
@@ -471,7 +583,6 @@ LO_IMPLEMENTATION_H(fmt_formatter, lib9p_getattr_t, lib9p_getattr);
/* size = 4 ; max_iov = 1 ; max_copy = 4 */
typedef uint32_t lib9p_setattr_t;
-LO_IMPLEMENTATION_H(fmt_formatter, lib9p_setattr_t, lib9p_setattr);
/* bits */
#define _LIB9P_SETATTR_UNUSED_31 ((lib9p_setattr_t)(UINT32_C(1)<<31))
#define _LIB9P_SETATTR_UNUSED_30 ((lib9p_setattr_t)(UINT32_C(1)<<30))
@@ -508,14 +619,12 @@ LO_IMPLEMENTATION_H(fmt_formatter, lib9p_setattr_t, lib9p_setattr);
/* size = 1 ; max_iov = 1 ; max_copy = 1 */
typedef uint8_t lib9p_lock_type_t;
-LO_IMPLEMENTATION_H(fmt_formatter, lib9p_lock_type_t, lib9p_lock_type);
#define LIB9P_LOCK_TYPE_RDLCK ((lib9p_lock_type_t)(0))
#define LIB9P_LOCK_TYPE_WRLCK ((lib9p_lock_type_t)(1))
#define LIB9P_LOCK_TYPE_UNLCK ((lib9p_lock_type_t)(2))
/* size = 4 ; max_iov = 1 ; max_copy = 4 */
typedef uint32_t lib9p_lock_flags_t;
-LO_IMPLEMENTATION_H(fmt_formatter, lib9p_lock_flags_t, lib9p_lock_flags);
/* bits */
#define _LIB9P_LOCK_FLAGS_UNUSED_31 ((lib9p_lock_flags_t)(UINT32_C(1)<<31))
#define _LIB9P_LOCK_FLAGS_UNUSED_30 ((lib9p_lock_flags_t)(UINT32_C(1)<<30))
@@ -552,120 +661,255 @@ LO_IMPLEMENTATION_H(fmt_formatter, lib9p_lock_flags_t, lib9p_lock_flags);
/* size = 1 ; max_iov = 1 ; max_copy = 1 */
typedef uint8_t lib9p_lock_status_t;
-LO_IMPLEMENTATION_H(fmt_formatter, lib9p_lock_status_t, lib9p_lock_status);
#define LIB9P_LOCK_STATUS_SUCCESS ((lib9p_lock_status_t)(0))
#define LIB9P_LOCK_STATUS_BLOCKED ((lib9p_lock_status_t)(1))
#define LIB9P_LOCK_STATUS_ERROR ((lib9p_lock_status_t)(2))
#define LIB9P_LOCK_STATUS_GRACE ((lib9p_lock_status_t)(3))
#endif /* CONFIG_9P_ENABLE_9P2000_L */
+#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u || CONFIG_9P_ENABLE_uninitialized
+/* min_size = 2 ; exp_size = 29 ; max_size = 65,537 ; max_iov = 2 ; max_copy = 2 */
+struct lib9p_s {
+ uint16_t len;
+ [[gnu::nonstring]] const char *utf8;
+};
+
+/* size = 13 ; max_iov = 1 ; max_copy = 13 */
+struct lib9p_qid {
+ lib9p_qt_t type;
+ uint32_t vers;
+ uint64_t path;
+};
+
+#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u || CONFIG_9P_ENABLE_uninitialized */
#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u
/* size = 9 ; max_iov = 1 ; max_copy = 9 */
struct lib9p_msg_Tflush {
lib9p_tag_t tag;
uint16_t oldtag;
};
-LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Tflush, lib9p_msg_Tflush);
/* size = 7 ; max_iov = 1 ; max_copy = 7 */
struct lib9p_msg_Rflush {
lib9p_tag_t tag;
};
-LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Rflush, lib9p_msg_Rflush);
+
+#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */
+#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u
+/* size = 12 ; max_iov = 1 ; max_copy = 12 */
+struct lib9p_msg_Topen {
+ lib9p_tag_t tag;
+ lib9p_fid_t fid;
+ lib9p_o_t mode;
+};
+
+#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */
+#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u
+/* size = 23 ; max_iov = 1 ; max_copy = 23 */
+struct lib9p_msg_Tread {
+ lib9p_tag_t tag;
+ lib9p_fid_t fid;
+ uint64_t offset;
+ uint32_t count;
+};
/* min_size = 11 ; exp_size = 8,203 ; max_size = 2,147,483,658 ; max_iov = 2 ; max_copy = 11 */
struct lib9p_msg_Rread {
- lib9p_tag_t tag;
- uint32_t count;
- [[gnu::nonstring]] char *data;
+ lib9p_tag_t tag;
+ uint32_t count;
+ const void *data;
+};
+
+/* min_size = 23 ; exp_size = 8,215 ; max_size = 2,147,483,670 ; max_iov = 2 ; max_copy = 23 */
+struct lib9p_msg_Twrite {
+ lib9p_tag_t tag;
+ lib9p_fid_t fid;
+ uint64_t offset;
+ uint32_t count;
+ const void *data;
};
-LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Rread, lib9p_msg_Rread);
/* size = 11 ; max_iov = 1 ; max_copy = 11 */
struct lib9p_msg_Rwrite {
lib9p_tag_t tag;
uint32_t count;
};
-LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Rwrite, lib9p_msg_Rwrite);
+
+/* size = 11 ; max_iov = 1 ; max_copy = 11 */
+struct lib9p_msg_Tclunk {
+ lib9p_tag_t tag;
+ lib9p_fid_t fid;
+};
/* size = 7 ; max_iov = 1 ; max_copy = 7 */
struct lib9p_msg_Rclunk {
lib9p_tag_t tag;
};
-LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Rclunk, lib9p_msg_Rclunk);
+
+/* size = 11 ; max_iov = 1 ; max_copy = 11 */
+struct lib9p_msg_Tremove {
+ lib9p_tag_t tag;
+ lib9p_fid_t fid;
+};
/* size = 7 ; max_iov = 1 ; max_copy = 7 */
struct lib9p_msg_Rremove {
lib9p_tag_t tag;
};
-LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Rremove, lib9p_msg_Rremove);
#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */
#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u
+/* size = 11 ; max_iov = 1 ; max_copy = 11 */
+struct lib9p_msg_Tstat {
+ lib9p_tag_t tag;
+ lib9p_fid_t fid;
+};
+
/* size = 7 ; max_iov = 1 ; max_copy = 7 */
struct lib9p_msg_Rwstat {
lib9p_tag_t tag;
};
-LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Rwstat, lib9p_msg_Rwstat);
#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */
+#if CONFIG_9P_ENABLE_9P2000_p9p
+/* size = 12 ; max_iov = 1 ; max_copy = 12 */
+struct lib9p_msg_Topenfd {
+ lib9p_tag_t tag;
+ lib9p_fid_t fid;
+ lib9p_o_t mode;
+};
+
+#endif /* CONFIG_9P_ENABLE_9P2000_p9p */
#if CONFIG_9P_ENABLE_9P2000_L
+/* size = 11 ; max_iov = 1 ; max_copy = 11 */
+struct lib9p_msg_Rlerror {
+ lib9p_tag_t tag;
+ lib9p_errno_t errnum;
+};
+
+/* size = 11 ; max_iov = 1 ; max_copy = 11 */
+struct lib9p_msg_Tstatfs {
+ lib9p_tag_t tag;
+ lib9p_fid_t fid;
+};
+
+/* size = 67 ; max_iov = 1 ; max_copy = 67 */
+struct lib9p_msg_Rstatfs {
+ lib9p_tag_t tag;
+ lib9p_super_magic_t type;
+ uint32_t bsize;
+ uint64_t blocks;
+ uint64_t bfree;
+ uint64_t bavail;
+ uint64_t files;
+ uint64_t ffree;
+ uint64_t fsid;
+ uint32_t namelen;
+};
+
+/* size = 15 ; max_iov = 1 ; max_copy = 15 */
+struct lib9p_msg_Tlopen {
+ lib9p_tag_t tag;
+ lib9p_fid_t fid;
+ lib9p_lo_t flags;
+};
+
/* size = 7 ; max_iov = 1 ; max_copy = 7 */
struct lib9p_msg_Rrename {
lib9p_tag_t tag;
};
-LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Rrename, lib9p_msg_Rrename);
+
+/* size = 11 ; max_iov = 1 ; max_copy = 11 */
+struct lib9p_msg_Treadlink {
+ lib9p_tag_t tag;
+ lib9p_fid_t fid;
+};
+
+/* size = 19 ; max_iov = 1 ; max_copy = 19 */
+struct lib9p_msg_Tgetattr {
+ lib9p_tag_t tag;
+ lib9p_fid_t fid;
+ lib9p_getattr_t request_mask;
+};
+
+/* size = 67 ; max_iov = 1 ; max_copy = 67 */
+struct lib9p_msg_Tsetattr {
+ lib9p_tag_t tag;
+ lib9p_fid_t fid;
+ lib9p_setattr_t valid;
+ lib9p_mode_t mode;
+ lib9p_nuid_t uid;
+ lib9p_nuid_t gid;
+ uint64_t filesize;
+ uint64_t atime_sec;
+ uint64_t atime_nsec;
+ uint64_t mtime_sec;
+ uint64_t mtime_nsec;
+};
/* size = 7 ; max_iov = 1 ; max_copy = 7 */
struct lib9p_msg_Rsetattr {
lib9p_tag_t tag;
};
-LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Rsetattr, lib9p_msg_Rsetattr);
/* size = 15 ; max_iov = 1 ; max_copy = 15 */
struct lib9p_msg_Rxattrwalk {
lib9p_tag_t tag;
uint64_t attr_size;
};
-LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Rxattrwalk, lib9p_msg_Rxattrwalk);
/* size = 7 ; max_iov = 1 ; max_copy = 7 */
struct lib9p_msg_Rxattrcreate {
lib9p_tag_t tag;
};
-LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Rxattrcreate, lib9p_msg_Rxattrcreate);
+
+/* size = 23 ; max_iov = 1 ; max_copy = 23 */
+struct lib9p_msg_Treaddir {
+ lib9p_tag_t tag;
+ lib9p_fid_t fid;
+ uint64_t offset;
+ uint32_t count;
+};
/* min_size = 11 ; exp_size = 8,203 ; max_size = 4,294,967,306 (warning: >UINT32_MAX) ; max_iov = 2 ; max_copy = 11 */
struct lib9p_msg_Rreaddir {
- lib9p_tag_t tag;
- uint32_t count;
- [[gnu::nonstring]] char *data;
+ lib9p_tag_t tag;
+ uint32_t count;
+ const void *data;
+};
+
+/* size = 15 ; max_iov = 1 ; max_copy = 15 */
+struct lib9p_msg_Tfsync {
+ lib9p_tag_t tag;
+ lib9p_fid_t fid;
+ lib9p_b4_t datasync;
};
-LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Rreaddir, lib9p_msg_Rreaddir);
/* size = 7 ; max_iov = 1 ; max_copy = 7 */
struct lib9p_msg_Rfsync {
lib9p_tag_t tag;
};
-LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Rfsync, lib9p_msg_Rfsync);
+
+/* size = 8 ; max_iov = 1 ; max_copy = 8 */
+struct lib9p_msg_Rlock {
+ lib9p_tag_t tag;
+ lib9p_lock_status_t status;
+};
/* size = 7 ; max_iov = 1 ; max_copy = 7 */
struct lib9p_msg_Rlink {
lib9p_tag_t tag;
};
-LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Rlink, lib9p_msg_Rlink);
/* size = 7 ; max_iov = 1 ; max_copy = 7 */
struct lib9p_msg_Rrenameat {
lib9p_tag_t tag;
};
-LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Rrenameat, lib9p_msg_Rrenameat);
/* size = 7 ; max_iov = 1 ; max_copy = 7 */
struct lib9p_msg_Runlinkat {
lib9p_tag_t tag;
};
-LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Runlinkat, lib9p_msg_Runlinkat);
#endif /* CONFIG_9P_ENABLE_9P2000_L */
#if CONFIG_9P_ENABLE_9P2000_e
@@ -674,107 +918,59 @@ struct lib9p_msg_Tsession {
lib9p_tag_t tag;
uint64_t key;
};
-LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Tsession, lib9p_msg_Tsession);
/* size = 7 ; max_iov = 1 ; max_copy = 7 */
struct lib9p_msg_Rsession {
lib9p_tag_t tag;
};
-LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Rsession, lib9p_msg_Rsession);
/* min_size = 11 ; exp_size = 8,203 ; max_size = 4,294,967,306 (warning: >UINT32_MAX) ; max_iov = 2 ; max_copy = 11 */
struct lib9p_msg_Rsread {
- lib9p_tag_t tag;
- uint32_t count;
- [[gnu::nonstring]] char *data;
+ lib9p_tag_t tag;
+ uint32_t count;
+ const void *data;
};
-LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Rsread, lib9p_msg_Rsread);
/* size = 11 ; max_iov = 1 ; max_copy = 11 */
struct lib9p_msg_Rswrite {
lib9p_tag_t tag;
uint32_t count;
};
-LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Rswrite, lib9p_msg_Rswrite);
#endif /* CONFIG_9P_ENABLE_9P2000_e */
-#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u
-/* size = 23 ; max_iov = 1 ; max_copy = 23 */
-struct lib9p_msg_Tread {
- lib9p_tag_t tag;
- lib9p_fid_t fid;
- uint64_t offset;
- uint32_t count;
-};
-LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Tread, lib9p_msg_Tread);
-
-/* min_size = 23 ; exp_size = 8,215 ; max_size = 2,147,483,670 ; max_iov = 2 ; max_copy = 23 */
-struct lib9p_msg_Twrite {
- lib9p_tag_t tag;
- lib9p_fid_t fid;
- uint64_t offset;
- uint32_t count;
- [[gnu::nonstring]] char *data;
-};
-LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Twrite, lib9p_msg_Twrite);
-
-/* size = 11 ; max_iov = 1 ; max_copy = 11 */
-struct lib9p_msg_Tclunk {
- lib9p_tag_t tag;
- lib9p_fid_t fid;
-};
-LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Tclunk, lib9p_msg_Tclunk);
-
-/* size = 11 ; max_iov = 1 ; max_copy = 11 */
-struct lib9p_msg_Tremove {
- lib9p_tag_t tag;
- lib9p_fid_t fid;
-};
-LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Tremove, lib9p_msg_Tremove);
-
-#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */
#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u
-/* size = 11 ; max_iov = 1 ; max_copy = 11 */
-struct lib9p_msg_Tstat {
- lib9p_tag_t tag;
- lib9p_fid_t fid;
+/* LIB9P_VER_9P2000 : min_size = 49 ; exp_size = 157 ; max_size = 262,189 ; max_iov = 8 ; max_copy = 49 */
+/* LIB9P_VER_9P2000_e : min_size = 49 ; exp_size = 157 ; max_size = 262,189 ; max_iov = 8 ; max_copy = 49 */
+/* LIB9P_VER_9P2000_p9p: min_size = 49 ; exp_size = 157 ; max_size = 262,189 ; max_iov = 8 ; max_copy = 49 */
+/* LIB9P_VER_9P2000_u : min_size = 63 ; exp_size = 198 ; max_size = 327,738 ; max_iov = 11 ; max_copy = 63 */
+struct lib9p_stat {
+ uint16_t fstype;
+ uint32_t fsdev;
+ struct lib9p_qid qid;
+ lib9p_dm_t mode;
+ uint32_t atime;
+ uint32_t mtime;
+ uint64_t length;
+ struct lib9p_s name;
+ struct lib9p_s owner_uname;
+ struct lib9p_s owner_gname;
+ struct lib9p_s last_modifier_uname;
+#if CONFIG_9P_ENABLE_9P2000_u
+ struct lib9p_s extension;
+ lib9p_nuid_t owner_unum;
+ lib9p_nuid_t owner_gnum;
+ lib9p_nuid_t last_modifier_unum;
+#endif /* CONFIG_9P_ENABLE_9P2000_u */
};
-LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Tstat, lib9p_msg_Tstat);
#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */
-#if CONFIG_9P_ENABLE_9P2000_L
-/* size = 11 ; max_iov = 1 ; max_copy = 11 */
-struct lib9p_msg_Tstatfs {
- lib9p_tag_t tag;
- lib9p_fid_t fid;
-};
-LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Tstatfs, lib9p_msg_Tstatfs);
-
-/* size = 11 ; max_iov = 1 ; max_copy = 11 */
-struct lib9p_msg_Treadlink {
- lib9p_tag_t tag;
- lib9p_fid_t fid;
-};
-LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Treadlink, lib9p_msg_Treadlink);
-
-/* size = 23 ; max_iov = 1 ; max_copy = 23 */
-struct lib9p_msg_Treaddir {
- lib9p_tag_t tag;
- lib9p_fid_t fid;
- uint64_t offset;
- uint32_t count;
-};
-LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Treaddir, lib9p_msg_Treaddir);
-
-#endif /* CONFIG_9P_ENABLE_9P2000_L */
-#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u
+#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u || CONFIG_9P_ENABLE_uninitialized
/* min_size = 13 ; exp_size = 40 ; max_size = 65,548 ; max_iov = 2 ; max_copy = 13 */
struct lib9p_msg_Tversion {
lib9p_tag_t tag;
uint32_t max_msg_size;
struct lib9p_s version;
};
-LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Tversion, lib9p_msg_Tversion);
/* min_size = 13 ; exp_size = 40 ; max_size = 65,548 ; max_iov = 2 ; max_copy = 13 */
struct lib9p_msg_Rversion {
@@ -782,115 +978,9 @@ struct lib9p_msg_Rversion {
uint32_t max_msg_size;
struct lib9p_s version;
};
-LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Rversion, lib9p_msg_Rversion);
-
-/* min_size = 17 ; exp_size = 481 ; max_size = 1,048,609 ; max_iov = 32 ; max_copy = 49 */
-struct lib9p_msg_Twalk {
- lib9p_tag_t tag;
- lib9p_fid_t fid;
- lib9p_fid_t newfid;
- uint16_t nwname;
- struct lib9p_s *wname;
-};
-LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Twalk, lib9p_msg_Twalk);
-
-#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */
-#if CONFIG_9P_ENABLE_9P2000_L
-/* min_size = 17 ; exp_size = 44 ; max_size = 65,552 ; max_iov = 2 ; max_copy = 17 */
-struct lib9p_msg_Trename {
- lib9p_tag_t tag;
- lib9p_fid_t fid;
- lib9p_fid_t dfid;
- struct lib9p_s name;
-};
-LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Trename, lib9p_msg_Trename);
-
-/* min_size = 9 ; exp_size = 36 ; max_size = 65,544 ; max_iov = 2 ; max_copy = 9 */
-struct lib9p_msg_Rreadlink {
- lib9p_tag_t tag;
- struct lib9p_s target;
-};
-LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Rreadlink, lib9p_msg_Rreadlink);
-
-/* min_size = 17 ; exp_size = 44 ; max_size = 65,552 ; max_iov = 2 ; max_copy = 17 */
-struct lib9p_msg_Txattrwalk {
- lib9p_tag_t tag;
- lib9p_fid_t fid;
- lib9p_fid_t newfid;
- struct lib9p_s name;
-};
-LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Txattrwalk, lib9p_msg_Txattrwalk);
-
-/* min_size = 25 ; exp_size = 52 ; max_size = 65,560 ; max_iov = 3 ; max_copy = 25 */
-struct lib9p_msg_Txattrcreate {
- lib9p_tag_t tag;
- lib9p_fid_t fid;
- struct lib9p_s name;
- uint64_t attr_size;
- uint32_t flags;
-};
-LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Txattrcreate, lib9p_msg_Txattrcreate);
-
-/* min_size = 17 ; exp_size = 44 ; max_size = 65,552 ; max_iov = 2 ; max_copy = 17 */
-struct lib9p_msg_Tlink {
- lib9p_tag_t tag;
- lib9p_fid_t dfid;
- lib9p_fid_t fid;
- struct lib9p_s name;
-};
-LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Tlink, lib9p_msg_Tlink);
-
-/* min_size = 19 ; exp_size = 73 ; max_size = 131,089 ; max_iov = 4 ; max_copy = 19 */
-struct lib9p_msg_Trenameat {
- lib9p_tag_t tag;
- lib9p_fid_t olddirfid;
- struct lib9p_s oldname;
- lib9p_fid_t newdirfid;
- struct lib9p_s newname;
-};
-LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Trenameat, lib9p_msg_Trenameat);
-/* min_size = 17 ; exp_size = 44 ; max_size = 65,552 ; max_iov = 3 ; max_copy = 17 */
-struct lib9p_msg_Tunlinkat {
- lib9p_tag_t tag;
- lib9p_fid_t dirfd;
- struct lib9p_s name;
- uint32_t flags;
-};
-LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Tunlinkat, lib9p_msg_Tunlinkat);
-
-#endif /* CONFIG_9P_ENABLE_9P2000_L */
-#if CONFIG_9P_ENABLE_9P2000_e
-/* min_size = 13 ; exp_size = 477 ; max_size = 4,294,967,308 (warning: >UINT32_MAX) ; max_iov = 0 + (CONFIG_9P_MAX_9P2000_e_WELEM * 2) ; max_copy = 13 + (CONFIG_9P_MAX_9P2000_e_WELEM * 2) */
-struct lib9p_msg_Tsread {
- lib9p_tag_t tag;
- uint32_t fid;
- uint16_t nwname;
- struct lib9p_s *wname;
-};
-LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Tsread, lib9p_msg_Tsread);
-
-/* min_size = 17 ; exp_size = 8,673 ; max_size = 8,589,934,607 (warning: >UINT32_MAX) ; max_iov = 2 + (CONFIG_9P_MAX_9P2000_e_WELEM * 2) ; max_copy = 17 + (CONFIG_9P_MAX_9P2000_e_WELEM * 2) */
-struct lib9p_msg_Tswrite {
- lib9p_tag_t tag;
- uint32_t fid;
- uint16_t nwname;
- struct lib9p_s *wname;
- uint32_t count;
- [[gnu::nonstring]] char *data;
-};
-LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Tswrite, lib9p_msg_Tswrite);
-
-#endif /* CONFIG_9P_ENABLE_9P2000_e */
+#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u || CONFIG_9P_ENABLE_uninitialized */
#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u
-/* size = 13 ; max_iov = 1 ; max_copy = 13 */
-struct lib9p_qid {
- lib9p_qt_t type;
- uint32_t vers;
- uint64_t path;
-};
-LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_qid, lib9p_qid);
-
/* LIB9P_VER_9P2000 : min_size = 15 ; exp_size = 69 ; max_size = 131,085 ; max_iov = 4 ; max_copy = 15 */
/* LIB9P_VER_9P2000_L : min_size = 19 ; exp_size = 73 ; max_size = 131,089 ; max_iov = 5 ; max_copy = 19 */
/* LIB9P_VER_9P2000_e : min_size = 15 ; exp_size = 69 ; max_size = 131,085 ; max_iov = 4 ; max_copy = 15 */
@@ -902,10 +992,15 @@ struct lib9p_msg_Tauth {
struct lib9p_s uname;
struct lib9p_s aname;
#if CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_u
- lib9p_nuid_t n_uid;
+ lib9p_nuid_t unum;
#endif /* CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_u */
};
-LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Tauth, lib9p_msg_Tauth);
+
+/* size = 20 ; max_iov = 1 ; max_copy = 20 */
+struct lib9p_msg_Rauth {
+ lib9p_tag_t tag;
+ struct lib9p_qid aqid;
+};
/* LIB9P_VER_9P2000 : min_size = 19 ; exp_size = 73 ; max_size = 131,089 ; max_iov = 4 ; max_copy = 19 */
/* LIB9P_VER_9P2000_L : min_size = 23 ; exp_size = 77 ; max_size = 131,093 ; max_iov = 5 ; max_copy = 23 */
@@ -919,251 +1014,41 @@ struct lib9p_msg_Tattach {
struct lib9p_s uname;
struct lib9p_s aname;
#if CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_u
- lib9p_nuid_t n_uid;
+ lib9p_nuid_t unum;
#endif /* CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_u */
};
-LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Tattach, lib9p_msg_Tattach);
-
-#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */
-#if CONFIG_9P_ENABLE_9P2000_L
-/* min_size = 19 ; exp_size = 73 ; max_size = 131,089 ; max_iov = 5 ; max_copy = 19 */
-struct lib9p_msg_Tsymlink {
- lib9p_tag_t tag;
- lib9p_fid_t fid;
- struct lib9p_s name;
- struct lib9p_s symtgt;
- lib9p_nuid_t gid;
-};
-LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Tsymlink, lib9p_msg_Tsymlink);
-
-#endif /* CONFIG_9P_ENABLE_9P2000_L */
-#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u
-/* size = 12 ; max_iov = 1 ; max_copy = 12 */
-struct lib9p_msg_Topen {
- lib9p_tag_t tag;
- lib9p_fid_t fid;
- lib9p_o_t mode;
-};
-LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Topen, lib9p_msg_Topen);
-
-/* min_size = 18 ; exp_size = 45 ; max_size = 65,553 ; max_iov = 3 ; max_copy = 18 */
-struct lib9p_msg_Tcreate {
- lib9p_tag_t tag;
- lib9p_fid_t fid;
- struct lib9p_s name;
- lib9p_dm_t perm;
- lib9p_o_t mode;
-};
-LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Tcreate, lib9p_msg_Tcreate);
-#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */
-#if CONFIG_9P_ENABLE_9P2000_p9p
-/* size = 12 ; max_iov = 1 ; max_copy = 12 */
-struct lib9p_msg_Topenfd {
- lib9p_tag_t tag;
- lib9p_fid_t fid;
- lib9p_o_t mode;
+/* size = 20 ; max_iov = 1 ; max_copy = 20 */
+struct lib9p_msg_Rattach {
+ lib9p_tag_t tag;
+ struct lib9p_qid qid;
};
-LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Topenfd, lib9p_msg_Topenfd);
-#endif /* CONFIG_9P_ENABLE_9P2000_p9p */
-#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u
-/* LIB9P_VER_9P2000 : min_size = 9 ; exp_size = 36 ; max_size = 65,544 ; max_iov = 2 ; max_copy = 9 */
-/* LIB9P_VER_9P2000_L : min_size = 9 ; exp_size = 36 ; max_size = 65,544 ; max_iov = 2 ; max_copy = 9 */
-/* LIB9P_VER_9P2000_e : min_size = 9 ; exp_size = 36 ; max_size = 65,544 ; max_iov = 2 ; max_copy = 9 */
-/* LIB9P_VER_9P2000_p9p: min_size = 9 ; exp_size = 36 ; max_size = 65,544 ; max_iov = 2 ; max_copy = 9 */
-/* LIB9P_VER_9P2000_u : min_size = 13 ; exp_size = 40 ; max_size = 65,548 ; max_iov = 3 ; max_copy = 13 */
+#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */
+#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u || CONFIG_9P_ENABLE_uninitialized
+/* LIB9P_VER_9P2000 : min_size = 9 ; exp_size = 36 ; max_size = 65,544 ; max_iov = 2 ; max_copy = 9 */
+/* LIB9P_VER_9P2000_e : min_size = 9 ; exp_size = 36 ; max_size = 65,544 ; max_iov = 2 ; max_copy = 9 */
+/* LIB9P_VER_9P2000_p9p : min_size = 9 ; exp_size = 36 ; max_size = 65,544 ; max_iov = 2 ; max_copy = 9 */
+/* LIB9P_VER_9P2000_u : min_size = 13 ; exp_size = 40 ; max_size = 65,548 ; max_iov = 3 ; max_copy = 13 */
+/* LIB9P_VER_uninitialized: min_size = 9 ; exp_size = 36 ; max_size = 65,544 ; max_iov = 2 ; max_copy = 9 */
struct lib9p_msg_Rerror {
lib9p_tag_t tag;
- struct lib9p_s ename;
+ struct lib9p_s errstr;
#if CONFIG_9P_ENABLE_9P2000_u
- lib9p_errno_t errno;
+ lib9p_errno_t errnum;
#endif /* CONFIG_9P_ENABLE_9P2000_u */
};
-LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Rerror, lib9p_msg_Rerror);
-#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */
-#if CONFIG_9P_ENABLE_9P2000_L
-/* size = 11 ; max_iov = 1 ; max_copy = 11 */
-struct lib9p_msg_Rlerror {
- lib9p_tag_t tag;
- lib9p_errno_t ecode;
-};
-LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Rlerror, lib9p_msg_Rlerror);
-
-/* size = 67 ; max_iov = 1 ; max_copy = 67 */
-struct lib9p_msg_Rstatfs {
- lib9p_tag_t tag;
- lib9p_super_magic_t type;
- uint32_t bsize;
- uint64_t blocks;
- uint64_t bfree;
- uint64_t bavail;
- uint64_t files;
- uint64_t ffree;
- uint64_t fsid;
- uint32_t namelen;
-};
-LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Rstatfs, lib9p_msg_Rstatfs);
-
-/* size = 15 ; max_iov = 1 ; max_copy = 15 */
-struct lib9p_msg_Tlopen {
- lib9p_tag_t tag;
- lib9p_fid_t fid;
- lib9p_lo_t flags;
-};
-LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Tlopen, lib9p_msg_Tlopen);
-
-/* min_size = 25 ; exp_size = 52 ; max_size = 65,560 ; max_iov = 3 ; max_copy = 25 */
-struct lib9p_msg_Tlcreate {
+#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u || CONFIG_9P_ENABLE_uninitialized */
+#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u
+/* min_size = 17 ; exp_size = 481 ; max_size = 1,048,609 ; max_iov = 32 ; max_copy = 49 */
+struct lib9p_msg_Twalk {
lib9p_tag_t tag;
lib9p_fid_t fid;
- struct lib9p_s name;
- lib9p_lo_t flags;
- lib9p_mode_t mode;
- lib9p_nuid_t gid;
-};
-LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Tlcreate, lib9p_msg_Tlcreate);
-
-/* min_size = 29 ; exp_size = 56 ; max_size = 65,564 ; max_iov = 3 ; max_copy = 29 */
-struct lib9p_msg_Tmknod {
- lib9p_tag_t tag;
- lib9p_fid_t dfid;
- struct lib9p_s name;
- lib9p_mode_t mode;
- uint32_t major;
- uint32_t minor;
- lib9p_nuid_t gid;
-};
-LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Tmknod, lib9p_msg_Tmknod);
-
-/* min_size = 21 ; exp_size = 48 ; max_size = 65,556 ; max_iov = 3 ; max_copy = 21 */
-struct lib9p_msg_Tmkdir {
- lib9p_tag_t tag;
- lib9p_fid_t dfid;
- struct lib9p_s name;
- lib9p_mode_t mode;
- lib9p_nuid_t gid;
-};
-LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Tmkdir, lib9p_msg_Tmkdir);
-
-/* size = 15 ; max_iov = 1 ; max_copy = 15 */
-struct lib9p_msg_Tfsync {
- lib9p_tag_t tag;
- lib9p_fid_t fid;
- lib9p_b4_t datasync;
-};
-LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Tfsync, lib9p_msg_Tfsync);
-
-/* size = 19 ; max_iov = 1 ; max_copy = 19 */
-struct lib9p_msg_Tgetattr {
- lib9p_tag_t tag;
- lib9p_fid_t fid;
- lib9p_getattr_t request_mask;
-};
-LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Tgetattr, lib9p_msg_Tgetattr);
-
-/* size = 67 ; max_iov = 1 ; max_copy = 67 */
-struct lib9p_msg_Tsetattr {
- lib9p_tag_t tag;
- lib9p_fid_t fid;
- lib9p_setattr_t valid;
- lib9p_mode_t mode;
- lib9p_nuid_t uid;
- lib9p_nuid_t gid;
- uint64_t filesize;
- uint64_t atime_sec;
- uint64_t atime_nsec;
- uint64_t mtime_sec;
- uint64_t mtime_nsec;
-};
-LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Tsetattr, lib9p_msg_Tsetattr);
-
-/* min_size = 34 ; exp_size = 61 ; max_size = 65,569 ; max_iov = 2 ; max_copy = 34 */
-struct lib9p_msg_Tgetlock {
- lib9p_tag_t tag;
- lib9p_fid_t fid;
- lib9p_lock_type_t type;
- uint64_t start;
- uint64_t length;
- uint32_t proc_id;
- struct lib9p_s client_id;
-};
-LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Tgetlock, lib9p_msg_Tgetlock);
-
-/* min_size = 30 ; exp_size = 57 ; max_size = 65,565 ; max_iov = 2 ; max_copy = 30 */
-struct lib9p_msg_Rgetlock {
- lib9p_tag_t tag;
- lib9p_lock_type_t type;
- uint64_t start;
- uint64_t length;
- uint32_t proc_id;
- struct lib9p_s client_id;
-};
-LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Rgetlock, lib9p_msg_Rgetlock);
-
-/* min_size = 38 ; exp_size = 65 ; max_size = 65,573 ; max_iov = 2 ; max_copy = 38 */
-struct lib9p_msg_Tlock {
- lib9p_tag_t tag;
- lib9p_fid_t fid;
- lib9p_lock_type_t type;
- lib9p_lock_flags_t flags;
- uint64_t start;
- uint64_t length;
- uint32_t proc_id;
- struct lib9p_s client_id;
-};
-LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Tlock, lib9p_msg_Tlock);
-
-/* size = 8 ; max_iov = 1 ; max_copy = 8 */
-struct lib9p_msg_Rlock {
- lib9p_tag_t tag;
- lib9p_lock_status_t status;
-};
-LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Rlock, lib9p_msg_Rlock);
-
-#endif /* CONFIG_9P_ENABLE_9P2000_L */
-#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u
-/* LIB9P_VER_9P2000 : min_size = 49 ; exp_size = 157 ; max_size = 262,189 ; max_iov = 8 ; max_copy = 49 */
-/* LIB9P_VER_9P2000_e : min_size = 49 ; exp_size = 157 ; max_size = 262,189 ; max_iov = 8 ; max_copy = 49 */
-/* LIB9P_VER_9P2000_p9p: min_size = 49 ; exp_size = 157 ; max_size = 262,189 ; max_iov = 8 ; max_copy = 49 */
-/* LIB9P_VER_9P2000_u : min_size = 63 ; exp_size = 198 ; max_size = 327,738 ; max_iov = 11 ; max_copy = 63 */
-struct lib9p_stat {
- uint16_t kern_type;
- uint32_t kern_dev;
- struct lib9p_qid file_qid;
- lib9p_dm_t file_mode;
- uint32_t file_atime;
- uint32_t file_mtime;
- uint64_t file_size;
- struct lib9p_s file_name;
- struct lib9p_s file_owner_uid;
- struct lib9p_s file_owner_gid;
- struct lib9p_s file_last_modified_uid;
-#if CONFIG_9P_ENABLE_9P2000_u
- struct lib9p_s file_extension;
- lib9p_nuid_t file_owner_n_uid;
- lib9p_nuid_t file_owner_n_gid;
- lib9p_nuid_t file_last_modified_n_uid;
-#endif /* CONFIG_9P_ENABLE_9P2000_u */
-};
-LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_stat, lib9p_stat);
-
-#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */
-#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u
-/* size = 20 ; max_iov = 1 ; max_copy = 20 */
-struct lib9p_msg_Rauth {
- lib9p_tag_t tag;
- struct lib9p_qid aqid;
-};
-LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Rauth, lib9p_msg_Rauth);
-
-/* size = 20 ; max_iov = 1 ; max_copy = 20 */
-struct lib9p_msg_Rattach {
- lib9p_tag_t tag;
- struct lib9p_qid qid;
+ lib9p_fid_t newfid;
+ uint16_t nwname;
+ struct lib9p_s *wname;
};
-LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Rattach, lib9p_msg_Rattach);
/* min_size = 9 ; exp_size = 217 ; max_size = 217 ; max_iov = 1 ; max_copy = 217 */
struct lib9p_msg_Rwalk {
@@ -1171,7 +1056,6 @@ struct lib9p_msg_Rwalk {
uint16_t nwqid;
struct lib9p_qid *wqid;
};
-LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Rwalk, lib9p_msg_Rwalk);
#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */
#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u
@@ -1181,7 +1065,15 @@ struct lib9p_msg_Ropen {
struct lib9p_qid qid;
uint32_t iounit;
};
-LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Ropen, lib9p_msg_Ropen);
+
+/* min_size = 18 ; exp_size = 45 ; max_size = 65,553 ; max_iov = 3 ; max_copy = 18 */
+struct lib9p_msg_Tcreate {
+ lib9p_tag_t tag;
+ lib9p_fid_t fid;
+ struct lib9p_s name;
+ lib9p_dm_t perm;
+ lib9p_o_t mode;
+};
/* size = 24 ; max_iov = 1 ; max_copy = 24 */
struct lib9p_msg_Rcreate {
@@ -1189,7 +1081,6 @@ struct lib9p_msg_Rcreate {
struct lib9p_qid qid;
uint32_t iounit;
};
-LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Rcreate, lib9p_msg_Rcreate);
#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */
#if CONFIG_9P_ENABLE_9P2000_p9p
@@ -1200,7 +1091,6 @@ struct lib9p_msg_Ropenfd {
uint32_t iounit;
uint32_t unixfd;
};
-LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Ropenfd, lib9p_msg_Ropenfd);
#endif /* CONFIG_9P_ENABLE_9P2000_p9p */
#if CONFIG_9P_ENABLE_9P2000_L
@@ -1210,7 +1100,16 @@ struct lib9p_msg_Rlopen {
struct lib9p_qid qid;
uint32_t iounit;
};
-LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Rlopen, lib9p_msg_Rlopen);
+
+/* min_size = 25 ; exp_size = 52 ; max_size = 65,560 ; max_iov = 3 ; max_copy = 25 */
+struct lib9p_msg_Tlcreate {
+ lib9p_tag_t tag;
+ lib9p_fid_t fid;
+ struct lib9p_s name;
+ lib9p_lo_t flags;
+ lib9p_mode_t mode;
+ lib9p_nuid_t gid;
+};
/* size = 24 ; max_iov = 1 ; max_copy = 24 */
struct lib9p_msg_Rlcreate {
@@ -1218,21 +1117,52 @@ struct lib9p_msg_Rlcreate {
struct lib9p_qid qid;
uint32_t iounit;
};
-LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Rlcreate, lib9p_msg_Rlcreate);
+
+/* min_size = 19 ; exp_size = 73 ; max_size = 131,089 ; max_iov = 5 ; max_copy = 19 */
+struct lib9p_msg_Tsymlink {
+ lib9p_tag_t tag;
+ lib9p_fid_t fid;
+ struct lib9p_s name;
+ struct lib9p_s symtgt;
+ lib9p_nuid_t gid;
+};
/* size = 20 ; max_iov = 1 ; max_copy = 20 */
struct lib9p_msg_Rsymlink {
lib9p_tag_t tag;
struct lib9p_qid qid;
};
-LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Rsymlink, lib9p_msg_Rsymlink);
+
+/* min_size = 29 ; exp_size = 56 ; max_size = 65,564 ; max_iov = 3 ; max_copy = 29 */
+struct lib9p_msg_Tmknod {
+ lib9p_tag_t tag;
+ lib9p_fid_t dfid;
+ struct lib9p_s name;
+ lib9p_mode_t mode;
+ uint32_t major;
+ uint32_t minor;
+ lib9p_nuid_t gid;
+};
/* size = 20 ; max_iov = 1 ; max_copy = 20 */
struct lib9p_msg_Rmknod {
lib9p_tag_t tag;
struct lib9p_qid qid;
};
-LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Rmknod, lib9p_msg_Rmknod);
+
+/* min_size = 17 ; exp_size = 44 ; max_size = 65,552 ; max_iov = 2 ; max_copy = 17 */
+struct lib9p_msg_Trename {
+ lib9p_tag_t tag;
+ lib9p_fid_t fid;
+ lib9p_fid_t dfid;
+ struct lib9p_s name;
+};
+
+/* min_size = 9 ; exp_size = 36 ; max_size = 65,544 ; max_iov = 2 ; max_copy = 9 */
+struct lib9p_msg_Rreadlink {
+ lib9p_tag_t tag;
+ struct lib9p_s target;
+};
/* size = 160 ; max_iov = 1 ; max_copy = 160 */
struct lib9p_msg_Rgetattr {
@@ -1258,16 +1188,118 @@ struct lib9p_msg_Rgetattr {
uint64_t gen;
uint64_t data_version;
};
-LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Rgetattr, lib9p_msg_Rgetattr);
+
+/* min_size = 17 ; exp_size = 44 ; max_size = 65,552 ; max_iov = 2 ; max_copy = 17 */
+struct lib9p_msg_Txattrwalk {
+ lib9p_tag_t tag;
+ lib9p_fid_t fid;
+ lib9p_fid_t newfid;
+ struct lib9p_s name;
+};
+
+/* min_size = 25 ; exp_size = 52 ; max_size = 65,560 ; max_iov = 3 ; max_copy = 25 */
+struct lib9p_msg_Txattrcreate {
+ lib9p_tag_t tag;
+ lib9p_fid_t fid;
+ struct lib9p_s name;
+ uint64_t attr_size;
+ uint32_t flags;
+};
+
+/* min_size = 38 ; exp_size = 65 ; max_size = 65,573 ; max_iov = 2 ; max_copy = 38 */
+struct lib9p_msg_Tlock {
+ lib9p_tag_t tag;
+ lib9p_fid_t fid;
+ lib9p_lock_type_t type;
+ lib9p_lock_flags_t flags;
+ uint64_t start;
+ uint64_t length;
+ uint32_t proc_id;
+ struct lib9p_s client_id;
+};
+
+/* min_size = 34 ; exp_size = 61 ; max_size = 65,569 ; max_iov = 2 ; max_copy = 34 */
+struct lib9p_msg_Tgetlock {
+ lib9p_tag_t tag;
+ lib9p_fid_t fid;
+ lib9p_lock_type_t type;
+ uint64_t start;
+ uint64_t length;
+ uint32_t proc_id;
+ struct lib9p_s client_id;
+};
+
+/* min_size = 30 ; exp_size = 57 ; max_size = 65,565 ; max_iov = 2 ; max_copy = 30 */
+struct lib9p_msg_Rgetlock {
+ lib9p_tag_t tag;
+ lib9p_lock_type_t type;
+ uint64_t start;
+ uint64_t length;
+ uint32_t proc_id;
+ struct lib9p_s client_id;
+};
+
+/* min_size = 17 ; exp_size = 44 ; max_size = 65,552 ; max_iov = 2 ; max_copy = 17 */
+struct lib9p_msg_Tlink {
+ lib9p_tag_t tag;
+ lib9p_fid_t dfid;
+ lib9p_fid_t fid;
+ struct lib9p_s name;
+};
+
+/* min_size = 21 ; exp_size = 48 ; max_size = 65,556 ; max_iov = 3 ; max_copy = 21 */
+struct lib9p_msg_Tmkdir {
+ lib9p_tag_t tag;
+ lib9p_fid_t dfid;
+ struct lib9p_s name;
+ lib9p_mode_t mode;
+ lib9p_nuid_t gid;
+};
/* size = 20 ; max_iov = 1 ; max_copy = 20 */
struct lib9p_msg_Rmkdir {
lib9p_tag_t tag;
struct lib9p_qid qid;
};
-LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Rmkdir, lib9p_msg_Rmkdir);
+
+/* min_size = 19 ; exp_size = 73 ; max_size = 131,089 ; max_iov = 4 ; max_copy = 19 */
+struct lib9p_msg_Trenameat {
+ lib9p_tag_t tag;
+ lib9p_fid_t olddirfid;
+ struct lib9p_s oldname;
+ lib9p_fid_t newdirfid;
+ struct lib9p_s newname;
+};
+
+/* min_size = 17 ; exp_size = 44 ; max_size = 65,552 ; max_iov = 3 ; max_copy = 17 */
+struct lib9p_msg_Tunlinkat {
+ lib9p_tag_t tag;
+ lib9p_fid_t dirfd;
+ struct lib9p_s name;
+ uint32_t flags;
+};
#endif /* CONFIG_9P_ENABLE_9P2000_L */
+#if CONFIG_9P_ENABLE_9P2000_e
+/* min_size = 13 ; exp_size = 477 ; max_size = 4,294,967,308 (warning: >UINT32_MAX) ; max_iov = 0 + (CONFIG_9P_MAX_9P2000_e_WELEM * 2) ; max_copy = 13 + (CONFIG_9P_MAX_9P2000_e_WELEM * 2) */
+struct lib9p_msg_Tsread {
+ lib9p_tag_t tag;
+ uint32_t fid;
+ uint16_t nwname;
+ struct lib9p_s *wname;
+};
+
+/* min_size = 17 ; exp_size = 8,673 ; max_size = 8,589,934,607 (warning: >UINT32_MAX) ; max_iov = 2 + (CONFIG_9P_MAX_9P2000_e_WELEM * 2) ; max_copy = 17 + (CONFIG_9P_MAX_9P2000_e_WELEM * 2) */
+struct lib9p_msg_Tswrite {
+ lib9p_tag_t tag;
+ uint32_t fid;
+ uint16_t nwname;
+ struct lib9p_s *wname;
+ uint32_t count;
+ const void *data;
+};
+
+#endif /* CONFIG_9P_ENABLE_9P2000_e */
#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u
/* LIB9P_VER_9P2000 : min_size = 58 ; exp_size = 166 ; max_size = 262,198 ; max_iov = 8 ; max_copy = 58 */
/* LIB9P_VER_9P2000_e : min_size = 58 ; exp_size = 166 ; max_size = 262,198 ; max_iov = 8 ; max_copy = 58 */
@@ -1277,7 +1309,6 @@ struct lib9p_msg_Rstat {
lib9p_tag_t tag;
struct lib9p_stat stat;
};
-LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Rstat, lib9p_msg_Rstat);
/* LIB9P_VER_9P2000 : min_size = 62 ; exp_size = 170 ; max_size = 262,202 ; max_iov = 8 ; max_copy = 62 */
/* LIB9P_VER_9P2000_e : min_size = 62 ; exp_size = 170 ; max_size = 262,202 ; max_iov = 8 ; max_copy = 62 */
@@ -1288,7 +1319,6 @@ struct lib9p_msg_Twstat {
lib9p_fid_t fid;
struct lib9p_stat stat;
};
-LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Twstat, lib9p_msg_Twstat);
#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */
/* containers *****************************************************************/
@@ -1301,6 +1331,8 @@ LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Twstat, lib9p_msg_Twstat);
#else
#define LIB9P_TMSG_MAX_IOV 32
#endif
+#elif CONFIG_9P_ENABLE_uninitialized
+ #define LIB9P_TMSG_MAX_IOV 2
#endif
#if CONFIG_9P_ENABLE_9P2000_u
@@ -1321,28 +1353,32 @@ LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Twstat, lib9p_msg_Twstat);
#else
#define LIB9P_TMSG_MAX_COPY 62
#endif
+#elif CONFIG_9P_ENABLE_uninitialized
+ #define LIB9P_TMSG_MAX_COPY 13
#endif
#if CONFIG_9P_ENABLE_9P2000_u
#define LIB9P_RMSG_MAX_IOV 11
#elif CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p
#define LIB9P_RMSG_MAX_IOV 8
-#elif CONFIG_9P_ENABLE_9P2000_L
+#elif CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_uninitialized
#define LIB9P_RMSG_MAX_IOV 2
#endif
#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u
#define LIB9P_RMSG_MAX_COPY 217
+#elif CONFIG_9P_ENABLE_uninitialized
+ #define LIB9P_RMSG_MAX_COPY 13
#endif
struct lib9p_Tmsg_send_buf {
size_t iov_cnt;
- struct iovec iov[LIB9P_TMSG_MAX_IOV];
+ struct wr_iovec iov[LIB9P_TMSG_MAX_IOV];
uint8_t copied[LIB9P_TMSG_MAX_COPY];
};
struct lib9p_Rmsg_send_buf {
size_t iov_cnt;
- struct iovec iov[LIB9P_RMSG_MAX_IOV];
+ struct wr_iovec iov[LIB9P_RMSG_MAX_IOV];
uint8_t copied[LIB9P_RMSG_MAX_COPY];
};
diff --git a/lib9p/include/lib9p/9p.h b/lib9p/core_include/lib9p/core.h
index 5919260..84ecd0f 100644
--- a/lib9p/include/lib9p/9p.h
+++ b/lib9p/core_include/lib9p/core.h
@@ -1,24 +1,23 @@
-/* lib9p/9p.h - Base 9P protocol definitions for both clients and servers
+/* lib9p/core.h - Base 9P protocol definitions for both clients and servers
*
* Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
-#ifndef _LIB9P_9P_H_
-#define _LIB9P_9P_H_
+#ifndef _LIB9P_CORE_H_
+#define _LIB9P_CORE_H_
-#include <stdbool.h>
-#include <sys/types.h> /* for ssize_t */
+#include <stdint.h> /* for uint{n}_t */
+#include <string.h> /* for memset() */
+#include <libhw/generic/io.h> /* for struct wr_iovec */
#include <libmisc/assert.h>
+#include <libmisc/fmt.h>
-#include <lib9p/linux-errno.h>
-#include <lib9p/9p.generated.h>
+#define CONFIG_9P_ENABLE_uninitialized 1
+#include "config.h"
-#ifndef CONFIG_9P_MAX_ERR_SIZE
- #error config.h must define CONFIG_9P_MAX_ERR_SIZE
-#endif
-static_assert(CONFIG_9P_MAX_ERR_SIZE <= UINT16_MAX);
+#include <lib9p/_core_generated.h> /* _after_ other includes, including config.h */
/* constants ******************************************************************/
@@ -32,8 +31,8 @@ enum {
const char *lib9p_version_str(enum lib9p_version);
const char *lib9p_msgtype_str(enum lib9p_version, enum lib9p_msg_type);
-struct lib9p_s lib9p_str(char *s);
-struct lib9p_s lib9p_strn(char *s, size_t maxlen);
+struct lib9p_s lib9p_str(const char *s);
+struct lib9p_s lib9p_strn(const char *s, size_t maxlen);
struct lib9p_s lib9p_str_slice(struct lib9p_s s, uint16_t beg, uint16_t end);
#define lib9p_str_sliceleft(s, beg) lib9p_str_slice(s, beg, (s).len)
bool lib9p_str_eq(struct lib9p_s a, struct lib9p_s b);
@@ -44,28 +43,14 @@ struct lib9p_ctx {
/* negotiated */
enum lib9p_version version;
uint32_t max_msg_size;
-
- /* state */
-#ifdef CONFIG_9P_ENABLE_9P2000_u
- lib9p_errno_t err_num;
-#endif
- [[gnu::nonstring]] char err_msg[CONFIG_9P_MAX_ERR_SIZE];
};
-void lib9p_ctx_clear_error(struct lib9p_ctx *ctx);
-
-bool lib9p_ctx_has_error(struct lib9p_ctx *ctx);
-
-/** Write an static error into ctx, return -1. */
-int lib9p_error(struct lib9p_ctx *ctx, lib9p_errno_t linux_errno, char const *msg);
-/** Write a printf-style error into ctx, return -1. */
-int lib9p_errorf(struct lib9p_ctx *ctx, lib9p_errno_t linux_errno, char const *fmt, ...) [[gnu::format(printf, 3, 4)]];
-
/* misc utilities *************************************************************/
-uint32_t lib9p_version_min_msg_size(enum lib9p_version);
+uint32_t lib9p_version_min_Rerror_size(enum lib9p_version);
+uint32_t lib9p_version_min_Rread_size(enum lib9p_version);
-lo_interface fmt_formatter lo_box_lib9p_msg_as_fmt_formatter(struct lib9p_ctx *ctx, enum lib9p_msg_type typ, void *body);
+void fmt_print_lib9p_msg(lo_interface fmt_dest w, struct lib9p_ctx *ctx, enum lib9p_msg_type typ, void *body);
/* main T-message functions ***************************************************/
@@ -77,23 +62,19 @@ lo_interface fmt_formatter lo_box_lib9p_msg_as_fmt_formatter(struct lib9p_ctx *c
* number may be larger than net_bytes due to (1) struct padding, (2)
* array pointers.
*
- * 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, or if an invalid string (invalid UTF-8,
- * contains a nul-byte) is encountered.
- *
+ * @param ctx : negotiated protocol parameters
* @param net_bytes : the complete request, starting with the "size[4]"
*
- * @return required size, or -1 on error
+ * @return required size, or an error
*
- * @errno LINUX_EOPNOTSUPP: message is an R-message
- * @errno LINUX_EOPNOTSUPP: message has unknown type
- * @errno LINUX_EBADMSG: message is wrong size for content
- * @errno LINUX_EBADMSG: message contains invalid UTF-8
- * @errno LINUX_EBADMSG: message contains a bitfield with unknown bits
- * @errno LINUX_EMSGSIZE: would-be return value overflows SSIZE_MAX
+ * @errno E_POSIX_EOPNOTSUPP: message is an R-message
+ * @errno E_POSIX_EOPNOTSUPP: message has unknown type
+ * @errno E_POSIX_EBADMSG: message is wrong size for content
+ * @errno E_POSIX_EILSEQ: message contains invalid UTF-8, or the UTF-8 contains a nul-byte
+ * @errno E_POSIX_EBADMSG: message contains a bitfield with unknown bits
+ * @errno E_POSIX_EMSGSIZE: would-be return value overflows SSIZE_MAX
*/
-ssize_t lib9p_Tmsg_validate(struct lib9p_ctx *ctx, uint8_t *net_bytes);
+size_t_or_error lib9p_Tmsg_validate(struct lib9p_ctx *ctx, uint8_t *net_bytes);
/**
* Unmarshal the 9P message `net_bytes` into the C struct `ret_body`.
@@ -119,54 +100,48 @@ void lib9p_Tmsg_unmarshal(struct lib9p_ctx *ctx, uint8_t *net_bytes,
* marshal bitfield bits that aren't in ctx->version; it applies a
* version-specific mask to bitfields.
*
- * @param ctx : negotiated protocol parameters, where to record errors
+ * @param ctx : negotiated protocol parameters
* @param typ : the message type
- * @param msg : the message to encode
+ * @param msg : the message to encode (`struct lib9p_msg_XXXX` according to `typ`)
*
- * @return ret_bytes : the buffer to encode to, must be at be at least ctx->max_msg_size bytes
- * @return whether there was an error (false=success, true=error)
+ * @return ret : the buffer to encode to
+ * @return error
*
- * @errno LINUX_ERANGE: reply does not fit in ctx->max_msg_size
+ * @errno E_POSIX_ERANGE: reply does not fit in ctx->max_msg_size
*/
-bool lib9p_Tmsg_marshal(struct lib9p_ctx *ctx, enum lib9p_msg_type typ, void *body,
- struct lib9p_Tmsg_send_buf *ret);
+error lib9p_Tmsg_marshal(struct lib9p_ctx *ctx, enum lib9p_msg_type typ, void *body,
+ struct lib9p_Tmsg_send_buf *ret);
/* main R-message functions ***************************************************/
/** Same as above, but for R-messages instead of T-messages. */
-ssize_t lib9p_Rmsg_validate(struct lib9p_ctx *ctx, uint8_t *net_bytes);
+size_t_or_error lib9p_Rmsg_validate(struct lib9p_ctx *ctx, uint8_t *net_bytes);
void lib9p_Rmsg_unmarshal(struct lib9p_ctx *ctx, uint8_t *net_bytes,
enum lib9p_msg_type *ret_typ, void *ret_body);
-bool lib9p_Rmsg_marshal(struct lib9p_ctx *ctx, enum lib9p_msg_type typ, void *body,
- struct lib9p_Rmsg_send_buf *ret);
+error lib9p_Rmsg_marshal(struct lib9p_ctx *ctx, enum lib9p_msg_type typ, void *body,
+ struct lib9p_Rmsg_send_buf *ret);
/* `struct lib9p_stat` helpers ************************************************/
-/** Assert that a `struct lib9p_stat` object looks valid. */
-static inline void lib9p_stat_assert(struct lib9p_stat stat) {
- assert( ((bool)(stat.file_mode & LIB9P_DM_DIR )) == ((bool)(stat.file_qid.type & LIB9P_QT_DIR )) );
- assert( ((bool)(stat.file_mode & LIB9P_DM_APPEND)) == ((bool)(stat.file_qid.type & LIB9P_QT_APPEND)) );
- assert( ((bool)(stat.file_mode & LIB9P_DM_EXCL )) == ((bool)(stat.file_qid.type & LIB9P_QT_EXCL )) );
- assert( ((bool)(stat.file_mode & LIB9P_DM_AUTH )) == ((bool)(stat.file_qid.type & LIB9P_QT_AUTH )) );
- assert( ((bool)(stat.file_mode & LIB9P_DM_TMP )) == ((bool)(stat.file_qid.type & LIB9P_QT_TMP )) );
- assert( (stat.file_size == 0) || !(stat.file_mode & LIB9P_DM_DIR) );
-}
+#if _LIB9P_ENABLE_stat
+
+void fmt_print_lib9p_stat(lo_interface fmt_dest w, struct lib9p_ctx *ctx, struct lib9p_stat *stat);
/**
* Validate a message's `stat` structure.
*
- * @param ctx : negotiated protocol parameters, where to record errors
+ * @param ctx : negotiated protocol parameters
* @param net_bytes : network-encoded stat structure
* @param net_size : the number of net_bytes that may be read
*
* @return ret_net_size : number of bytes consumed; <=net_size
* @return ret_host_size : number of bytes that lib9p_stat_unmarshal would take
- * @return whether there was an error
+ * @return error
*/
-bool lib9p_stat_validate(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes,
- uint32_t *ret_net_size, ssize_t *ret_host_size);
+error lib9p_stat_validate(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes,
+ uint32_t *ret_net_size, size_t *ret_host_size);
/**
* Unmarshal the 9P `net_bytes` into the C struct `ret_obj`.
@@ -191,16 +166,15 @@ void lib9p_stat_unmarshal(struct lib9p_ctx *ctx, uint8_t *net_bytes,
* marshal bitfield bits that aren't in ctx->version; it applies a
* version-specific mask to bitfields.
*
- * @param ctx : negotiated protocol parameters, where to record errors
+ * @param ctx : negotiated protocol parameters
* @param max_net_size : the maximum network-encoded size to allow
* @param obj : the message to encode
*
* @return ret_bytes: the buffer to encode into
* @return the number of bytes written, or 0 if the stat object does not fit in max_net_size
- *
- * @errno LINUX_ERANGE: reply does not fit in max_net_size
*/
uint32_t lib9p_stat_marshal(struct lib9p_ctx *ctx, uint32_t max_net_size, struct lib9p_stat *obj,
uint8_t *ret_bytes);
+#endif
-#endif /* _LIB9P_9P_H_ */
+#endif /* _LIB9P_CORE_H_ */
diff --git a/lib9p/tables.h b/lib9p/core_tables.h
index edb402a..94cdbe1 100644
--- a/lib9p/tables.h
+++ b/lib9p/core_tables.h
@@ -1,32 +1,33 @@
-/* lib9p/tables.h - Declare tables of version and message information
+/* lib9p/core_tables.h - Declare tables of version and message information
*
* Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
-#ifndef _LIB9P_TABLES_H_
-#define _LIB9P_TABLES_H_
+#ifndef _LIB9P_CORE_TABLES_H_
+#define _LIB9P_CORE_TABLES_H_
-#include <lib9p/9p.h>
+#include <lib9p/core.h>
/* version ********************************************************************/
struct _lib9p_ver_tentry {
const char *name;
- uint32_t min_msg_size;
+ uint32_t min_Rerror_size;
+ uint32_t min_Rread_size;
};
extern const struct _lib9p_ver_tentry _lib9p_table_ver[LIB9P_VER_NUM];
/* message ********************************************************************/
-typedef lo_interface fmt_formatter (*_box_as_fmt_formatter_fn_t)(void *host_val);
+typedef void (*_print_fn_t)(lo_interface fmt_dest, struct lib9p_ctx *, void *);
struct _lib9p_msg_tentry {
- const char *name;
- _box_as_fmt_formatter_fn_t box_as_fmt_formatter;
+ const char *name;
+ _print_fn_t print;
};
-typedef ssize_t (*_validate_fn_t)(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes);
+typedef size_t_or_error (*_validate_fn_t)(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes);
typedef void (*_unmarshal_fn_t)(struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out);
struct _lib9p_recv_tentry {
_validate_fn_t validate;
@@ -35,11 +36,11 @@ struct _lib9p_recv_tentry {
struct _marshal_ret {
size_t net_iov_cnt;
- struct iovec *net_iov;
+ struct wr_iovec *net_iov;
size_t net_copied_size;
uint8_t *net_copied;
};
-typedef bool (*_marshal_fn_t)(struct lib9p_ctx *ctx, void *host_val, struct _marshal_ret *ret);
+typedef error (*_marshal_fn_t)(struct lib9p_ctx *ctx, void *host_val, struct _marshal_ret *ret);
struct _lib9p_send_tentry {
_marshal_fn_t marshal;
};
@@ -52,8 +53,10 @@ extern const struct _lib9p_send_tentry _lib9p_table_Rmsg_send[LIB9P_VER_NUM][0x8
/* stat ***********************************************************************/
-ssize_t _lib9p_stat_validate(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes, uint32_t *ret_net_size);
+#if _LIB9P_ENABLE_stat
+size_t_or_error _lib9p_stat_validate(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes, uint32_t *ret_net_size);
void _lib9p_stat_unmarshal(struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out);
bool _lib9p_stat_marshal(struct lib9p_ctx *ctx, struct lib9p_stat *val, struct _marshal_ret *ret);
+#endif
-#endif /* _LIB9P_TABLES_H_ */
+#endif /* _LIB9P_CORE_TABLES_H_ */
diff --git a/lib9p/idl/0000-uninitialized.9p b/lib9p/idl/0000-uninitialized.9p
new file mode 100644
index 0000000..193cb19
--- /dev/null
+++ b/lib9p/idl/0000-uninitialized.9p
@@ -0,0 +1,13 @@
+# lib9p/idl/0000-uninitialized.9p - Defs for bootstrapping
+#
+# Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+# SPDX-License-Identifier: AGPL-3.0-or-later
+
+version "uninitialized"
+
+# For the actual protocol.
+from ./2002-9P2000.9p import s, tag
+from ./2002-9P2000.9p import Tversion, Rversion, Rerror
+
+# For srv.h
+from ./2002-9P2000.9p import dm, qt, qid, fid
diff --git a/lib9p/idl/1992-9P0.9p.wip b/lib9p/idl/1992-9P0.9p.wip
index a434ba2..de902dd 100644
--- a/lib9p/idl/1992-9P0.9p.wip
+++ b/lib9p/idl/1992-9P0.9p.wip
@@ -107,7 +107,7 @@ msg Rnop = "typ[1,val=51] tag[tag,val=0xFFFF]"
msg Tsession = "typ[1,val=52] tag[tag,val=0xFFFF]"
msg Rsession = "typ[1,val=53] tag[tag,val=0xFFFF]"
#msg Terror = "typ[1,val=54] illegal"
-msg Rerror = "typ[1,val=55] tag[tag] ename[errstr]"
+msg Rerror = "typ[1,val=55] tag[tag] errstr[errstr]"
msg Tflush = "typ[1,val=56] tag[tag] oldtag[tag]"
msg Rflush = "typ[1,val=57] tag[tag]"
msg Tattach = "typ[1,val=58] tag[tag] fid[fid] uid[name] aname[name] auth[auth_ticket] 13*(pad[1])" # Pad to allow auth_tickets up to 28 bytes.
@@ -137,5 +137,5 @@ msg Twstat = "typ[1,val=78] tag[tag] fid[fid] stat[stat]"
msg Rwstat = "typ[1,val=79] tag[tag] fid[fid]"
msg Tclwalk = "typ[1,val=80] tag[tag] fid[fid] newfid[fid] name[name]"
msg Rclwalk = "typ[1,val=81] tag[tag] fid[fid] qid[qid]"
-msg Tauth = "typ[1,val=82] tag[tag] fid[fid] uid[name] chal[encrypted_auth_challenge]" # chal is an encrypted
+msg Tauth = "typ[1,val=82] tag[tag] fid[fid] uid[name] chal[encrypted_auth_challenge]"
msg Rauth = "typ[1,val=83] tag[tag] fid[fid] chal[encrypted_auth_response]"
diff --git a/lib9p/idl/1996-Styx.9p.wip b/lib9p/idl/1996-Styx.9p.wip
index 3cb3774..143be83 100644
--- a/lib9p/idl/1996-Styx.9p.wip
+++ b/lib9p/idl/1996-Styx.9p.wip
@@ -28,7 +28,7 @@ from ./1992-9P1.9p import tag, fid, qid, name, errstr, o, ch, stat
msg Tnop = "typ[1,val=0] tag[tag,val=0xFFFF]"
msg Rnop = "typ[1,val=1] tag[tag,val=0xFFFF]"
#msg Terror = "typ[1,val=2] illegal"
-msg Rerror = "typ[1,val=3] tag[tag] ename[errstr]"
+msg Rerror = "typ[1,val=3] tag[tag] errstr[errstr]"
msg Tflush = "typ[1,val=4] tag[tag] oldtag[tag]"
msg Rflush = "typ[1,val=5] tag[tag]"
msg Tclone = "typ[1,val=6] tag[tag] fid[fid] newfid[fid]"
diff --git a/lib9p/idl/2002-9P2000.9p b/lib9p/idl/2002-9P2000.9p
index 2b51612..712ffea 100644
--- a/lib9p/idl/2002-9P2000.9p
+++ b/lib9p/idl/2002-9P2000.9p
@@ -91,18 +91,26 @@ bitfield qt = 1
struct qid = "type[qt] vers[4] path[8]"
# stat - file status
-struct stat = "stat_size[2,val=end-&kern_type]"
- "kern_type[2]"
- "kern_dev[4]"
- "file_qid[qid]"
- "file_mode[dm]"
- "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]"
+struct stat = "_stat_size[2,val=end-&fstype]"
+ # fstype and fsdev are documented simply as "for kernel
+ # use". The are ignored by Plan 9's in-kernel 9P
+ # client; mntdirfix() overwrites them with fstype='M'
+ # and fsdev=nonce. Processes may observe values other
+ # than fstype='M', as 'M' is used for actual 9P servers,
+ # while other values are used for in-kernel device
+ # servers (such as '|' for pipes, or 'I' for the IP
+ # stack).
+ "fstype[2]" # filesystem type
+ "fsdev[4]" # filesystem instance/subtype
+ "qid[qid]"
+ "mode[dm]"
+ "atime[4]"
+ "mtime[4]"
+ "length[8]" # 0 for directories
+ "name[s]"
+ "owner_uname[s]"
+ "owner_gname[s]"
+ "last_modifier_uname[s]"
# "O"pen flags (flags to pass to Topen and Tcreate)
# Unused bits *must* be 0.
@@ -137,7 +145,7 @@ msg Rauth = "size[4,val=end-&size] typ[1,val=103] tag[tag] aqid[qid]"
msg Tattach = "size[4,val=end-&size] typ[1,val=104] tag[tag] fid[fid] afid[fid] uname[s] aname[s]"
msg Rattach = "size[4,val=end-&size] typ[1,val=105] tag[tag] qid[qid]"
#msg Terror = "size[4,val=end-&size] typ[1,val=106] tag[tag] illegal"
-msg Rerror = "size[4,val=end-&size] typ[1,val=107] tag[tag] ename[s]"
+msg Rerror = "size[4,val=end-&size] typ[1,val=107] tag[tag] errstr[s]"
msg Tflush = "size[4,val=end-&size] typ[1,val=108] tag[tag] oldtag[2]"
msg Rflush = "size[4,val=end-&size] typ[1,val=109] tag[tag]"
msg Twalk = "size[4,val=end-&size] typ[1,val=110] tag[tag] fid[fid] newfid[fid] nwname[2,max=16] nwname*(wname[s])"
diff --git a/lib9p/idl/2003-9P2000.p9p.9p b/lib9p/idl/2003-9P2000.p9p.9p
index 3f6a524..1d1c307 100644
--- a/lib9p/idl/2003-9P2000.p9p.9p
+++ b/lib9p/idl/2003-9P2000.p9p.9p
@@ -27,7 +27,7 @@ from ./2002-9P2000.9p import *
# e.g. you replace syscall:`open()` with lib9pclient:`fsopen()`).
#
# "Unfortunately", programs in plan9port must deal both with 9P files
-# and native "Unix" files; and need to turn an 9P FID into a native
+# and native "Unix" files; and need to turn a 9P FID into a native
# file descriptor. To do this, the `9pserve` program and lib9pclient
# add an extension call to 9P2000: Topenfd/Ropenfd/fsopenfd().
#
diff --git a/lib9p/idl/2005-9P2000.u.9p b/lib9p/idl/2005-9P2000.u.9p
index 6c2f2dc..446385c 100644
--- a/lib9p/idl/2005-9P2000.u.9p
+++ b/lib9p/idl/2005-9P2000.u.9p
@@ -17,15 +17,15 @@ num nuid = 4
num errno = 4
"NOERROR = 0"
-struct stat += "file_extension[s]"
- "file_owner_n_uid[nuid]"
- "file_owner_n_gid[nuid]"
- "file_last_modified_n_uid[nuid]"
+struct stat += "extension[s]"
+ "owner_unum[nuid]"
+ "owner_gnum[nuid]"
+ "last_modifier_unum[nuid]"
-msg Tauth += "n_uid[nuid]"
-msg Tattach += "n_uid[nuid]"
+msg Tauth += "unum[nuid]"
+msg Tattach += "unum[nuid]"
-msg Rerror += "errno[errno]"
+msg Rerror += "errnum[errno]"
bitfield dm += "bit 23=DEVICE"
"bit 21=PIPE"
diff --git a/lib9p/idl/2010-9P2000.L.9p b/lib9p/idl/2010-9P2000.L.9p
index d81a15b..5eb7d5c 100644
--- a/lib9p/idl/2010-9P2000.L.9p
+++ b/lib9p/idl/2010-9P2000.L.9p
@@ -1,3 +1,6 @@
+# lib9p/idl/2010-9P2000.L.9p - Generated by `lib9p/idl/2010-9P2000.L.9p.gen 3rd-party/linux-errno.txt`. DO NOT EDIT!
+# 3rd-party/linux-errno.txt - Generated from lib9p/linux-errno.txt.gen and linux.git v6.14. DO NOT EDIT!
+
# lib9p/idl/2010-9P2000.L.9p - Definitions of 9P2000.L messages
#
# Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
@@ -8,12 +11,149 @@
# https://github.com/chaos/diod/blob/master/src/libnpfs/protocol.h
version "9P2000.L"
+# low-level types ##############################################################
+
from ./2002-9P2000.9p import tag, fid, s, qt, qid
-from ./2002-9P2000.9p import Rerror
-from ./2002-9P2000.9p import Tversion, Rversion, Tflush, Rflush, Twalk, Rwalk, Tread, Rread, Twrite, Rwrite, Tclunk, Rclunk, Tremove, Rremove
-from ./2005-9P2000.u.9p import nuid, errno, Tauth, Rauth, Tattach, Rattach
+from ./2005-9P2000.u.9p import nuid, errno
-#num errno += # TODO
+# BUG: The definitions of errno are not defined in the 9P2000.L
+# protocol spec, and Linux kernel errno values vary by architecture.
+# Most architectures share a "generic" list, but a handful (as of
+# Linux v6.14, Alpha, MIPS, PA-RISC, PowerPC, and SPARC) have their
+# own numbers. This IDL file lists the generic numbers.
+#
+# https://github.com/chaos/diod/issues/35
+num errno += "L_EPERM = 1" # Operation not permitted
+ "L_ENOENT = 2" # No such file or directory
+ "L_ESRCH = 3" # No such process
+ "L_EINTR = 4" # Interrupted system call
+ "L_EIO = 5" # I/O error
+ "L_ENXIO = 6" # No such device or address
+ "L_E2BIG = 7" # Argument list too long
+ "L_ENOEXEC = 8" # Exec format error
+ "L_EBADF = 9" # Bad file number
+ "L_ECHILD = 10" # No child processes
+ "L_EAGAIN = 11" # Try again
+ "L_ENOMEM = 12" # Out of memory
+ "L_EACCES = 13" # Permission denied
+ "L_EFAULT = 14" # Bad address
+ "L_ENOTBLK = 15" # Block device required
+ "L_EBUSY = 16" # Device or resource busy
+ "L_EEXIST = 17" # File exists
+ "L_EXDEV = 18" # Cross-device link
+ "L_ENODEV = 19" # No such device
+ "L_ENOTDIR = 20" # Not a directory
+ "L_EISDIR = 21" # Is a directory
+ "L_EINVAL = 22" # Invalid argument
+ "L_ENFILE = 23" # File table overflow
+ "L_EMFILE = 24" # Too many open files
+ "L_ENOTTY = 25" # Not a typewriter
+ "L_ETXTBSY = 26" # Text file busy
+ "L_EFBIG = 27" # File too large
+ "L_ENOSPC = 28" # No space left on device
+ "L_ESPIPE = 29" # Illegal seek
+ "L_EROFS = 30" # Read-only file system
+ "L_EMLINK = 31" # Too many links
+ "L_EPIPE = 32" # Broken pipe
+ "L_EDOM = 33" # Math argument out of domain of func
+ "L_ERANGE = 34" # Math result not representable
+ "L_EDEADLK = 35" # Resource deadlock would occur
+ "L_ENAMETOOLONG = 36" # File name too long
+ "L_ENOLCK = 37" # No record locks available
+ "L_ENOSYS = 38" # Invalid system call number
+ "L_ENOTEMPTY = 39" # Directory not empty
+ "L_ELOOP = 40" # Too many symbolic links encountered
+ "L_ENOMSG = 42" # No message of desired type
+ "L_EIDRM = 43" # Identifier removed
+ "L_ECHRNG = 44" # Channel number out of range
+ "L_EL2NSYNC = 45" # Level 2 not synchronized
+ "L_EL3HLT = 46" # Level 3 halted
+ "L_EL3RST = 47" # Level 3 reset
+ "L_ELNRNG = 48" # Link number out of range
+ "L_EUNATCH = 49" # Protocol driver not attached
+ "L_ENOCSI = 50" # No CSI structure available
+ "L_EL2HLT = 51" # Level 2 halted
+ "L_EBADE = 52" # Invalid exchange
+ "L_EBADR = 53" # Invalid request descriptor
+ "L_EXFULL = 54" # Exchange full
+ "L_ENOANO = 55" # No anode
+ "L_EBADRQC = 56" # Invalid request code
+ "L_EBADSLT = 57" # Invalid slot
+ "L_EBFONT = 59" # Bad font file format
+ "L_ENOSTR = 60" # Device not a stream
+ "L_ENODATA = 61" # No data available
+ "L_ETIME = 62" # Timer expired
+ "L_ENOSR = 63" # Out of streams resources
+ "L_ENONET = 64" # Machine is not on the network
+ "L_ENOPKG = 65" # Package not installed
+ "L_EREMOTE = 66" # Object is remote
+ "L_ENOLINK = 67" # Link has been severed
+ "L_EADV = 68" # Advertise error
+ "L_ESRMNT = 69" # Srmount error
+ "L_ECOMM = 70" # Communication error on send
+ "L_EPROTO = 71" # Protocol error
+ "L_EMULTIHOP = 72" # Multihop attempted
+ "L_EDOTDOT = 73" # RFS specific error
+ "L_EBADMSG = 74" # Not a data message
+ "L_EOVERFLOW = 75" # Value too large for defined data type
+ "L_ENOTUNIQ = 76" # Name not unique on network
+ "L_EBADFD = 77" # File descriptor in bad state
+ "L_EREMCHG = 78" # Remote address changed
+ "L_ELIBACC = 79" # Can not access a needed shared library
+ "L_ELIBBAD = 80" # Accessing a corrupted shared library
+ "L_ELIBSCN = 81" # .lib section in a.out corrupted
+ "L_ELIBMAX = 82" # Attempting to link in too many shared libraries
+ "L_ELIBEXEC = 83" # Cannot exec a shared library directly
+ "L_EILSEQ = 84" # Illegal byte sequence
+ "L_ERESTART = 85" # Interrupted system call should be restarted
+ "L_ESTRPIPE = 86" # Streams pipe error
+ "L_EUSERS = 87" # Too many users
+ "L_ENOTSOCK = 88" # Socket operation on non-socket
+ "L_EDESTADDRREQ = 89" # Destination address required
+ "L_EMSGSIZE = 90" # Message too long
+ "L_EPROTOTYPE = 91" # Protocol wrong type for socket
+ "L_ENOPROTOOPT = 92" # Protocol not available
+ "L_EPROTONOSUPPORT = 93" # Protocol not supported
+ "L_ESOCKTNOSUPPORT = 94" # Socket type not supported
+ "L_EOPNOTSUPP = 95" # Operation not supported on transport endpoint
+ "L_EPFNOSUPPORT = 96" # Protocol family not supported
+ "L_EAFNOSUPPORT = 97" # Address family not supported by protocol
+ "L_EADDRINUSE = 98" # Address already in use
+ "L_EADDRNOTAVAIL = 99" # Cannot assign requested address
+ "L_ENETDOWN = 100" # Network is down
+ "L_ENETUNREACH = 101" # Network is unreachable
+ "L_ENETRESET = 102" # Network dropped connection because of reset
+ "L_ECONNABORTED = 103" # Software caused connection abort
+ "L_ECONNRESET = 104" # Connection reset by peer
+ "L_ENOBUFS = 105" # No buffer space available
+ "L_EISCONN = 106" # Transport endpoint is already connected
+ "L_ENOTCONN = 107" # Transport endpoint is not connected
+ "L_ESHUTDOWN = 108" # Cannot send after transport endpoint shutdown
+ "L_ETOOMANYREFS = 109" # Too many references: cannot splice
+ "L_ETIMEDOUT = 110" # Connection timed out
+ "L_ECONNREFUSED = 111" # Connection refused
+ "L_EHOSTDOWN = 112" # Host is down
+ "L_EHOSTUNREACH = 113" # No route to host
+ "L_EALREADY = 114" # Operation already in progress
+ "L_EINPROGRESS = 115" # Operation now in progress
+ "L_ESTALE = 116" # Stale file handle
+ "L_EUCLEAN = 117" # Structure needs cleaning
+ "L_ENOTNAM = 118" # Not a XENIX named type file
+ "L_ENAVAIL = 119" # No XENIX semaphores available
+ "L_EISNAM = 120" # Is a named type file
+ "L_EREMOTEIO = 121" # Remote I/O error
+ "L_EDQUOT = 122" # Quota exceeded
+ "L_ENOMEDIUM = 123" # No medium found
+ "L_EMEDIUMTYPE = 124" # Wrong medium type
+ "L_ECANCELED = 125" # Operation Canceled
+ "L_ENOKEY = 126" # Required key not available
+ "L_EKEYEXPIRED = 127" # Key has expired
+ "L_EKEYREVOKED = 128" # Key has been revoked
+ "L_EKEYREJECTED = 129" # Key was rejected by service
+ "L_EOWNERDEAD = 130" # Owner died
+ "L_ENOTRECOVERABLE = 131" # State not recoverable
+ "L_ERFKILL = 132" # Operation not possible due to RF-kill
+ "L_EHWPOISON = 133" # Memory page has hardware error
num super_magic = 4
# See <linux/magic.h> (linux.git include/uapi/linux/magic.h).
@@ -168,8 +308,23 @@ num lock_status = 1
"ERROR=2"
"GRACE=3"
+# 9P2000 Operations (.L subset) ################################################
+
+from ./2002-9P2000.9p import Tversion, Rversion
+from ./2002-9P2000.9p import Tflush, Rflush
+from ./2002-9P2000.9p import Twalk, Rwalk
+from ./2002-9P2000.9p import Tread, Rread, Twrite, Rwrite
+from ./2002-9P2000.9p import Tclunk, Rclunk
+from ./2002-9P2000.9p import Tremove, Rremove
+
+# 9P2000.u Operations (.L subset) ##############################################
+
+from ./2005-9P2000.u.9p import Tattach, Rattach, Tauth, Rauth
+
+# 9P2000.L Operations ##########################################################
+
#msg Tlerror = "size[4,val=end-&size] typ[1,val=6] tag[tag] illegal" # analogous to 106/Terror
-msg Rlerror = "size[4,val=end-&size] typ[1,val=7] tag[tag] ecode[errno]" # analogous to 107/Rerror
+msg Rlerror = "size[4,val=end-&size] typ[1,val=7] tag[tag] errnum[errno]" # analogous to 107/Rerror
msg Tstatfs = "size[4,val=end-&size] typ[1,val=8] tag[tag] fid[fid]"
msg Rstatfs = "size[4,val=end-&size] typ[1,val=9] tag[tag]" # Description | statfs | statvfs
"type[super_magic]" # Type of filesystem | f_type | -
diff --git a/lib9p/idl/2010-9P2000.L.9p.gen b/lib9p/idl/2010-9P2000.L.9p.gen
new file mode 100755
index 0000000..f0bdb6b
--- /dev/null
+++ b/lib9p/idl/2010-9P2000.L.9p.gen
@@ -0,0 +1,282 @@
+#!/usr/bin/env python3
+# lib9p/idl/2010-9P2000.L.9p.gen - Generate definitions of 9P2000.L messages
+import sys
+
+print(
+ f"# lib9p/idl/2010-9P2000.L.9p - Generated by `{' '.join(sys.argv)}`. DO NOT EDIT!"
+)
+errnos: dict[str, tuple[int, str]] = {}
+with open(sys.argv[1], "r", encoding="utf-8") as fh:
+ for line in fh:
+ if line.startswith("#"):
+ print(line)
+ continue
+ _num, _name, _desc = line.split(maxsplit=2)
+ errnos[_name] = (int(_num), _desc.strip())
+print(
+ """
+# lib9p/idl/2010-9P2000.L.9p - Definitions of 9P2000.L messages
+#
+# Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+# SPDX-License-Identifier: AGPL-3.0-or-later
+
+# "9P2000.L" Linux extension
+# https://github.com/chaos/diod/blob/master/protocol.md
+# https://github.com/chaos/diod/blob/master/src/libnpfs/protocol.h
+version "9P2000.L"
+
+# low-level types ##############################################################
+
+from ./2002-9P2000.9p import tag, fid, s, qt, qid
+from ./2005-9P2000.u.9p import nuid, errno
+
+# BUG: The definitions of errno are not defined in the 9P2000.L
+# protocol spec, and Linux kernel errno values vary by architecture.
+# Most architectures share a "generic" list, but a handful (as of
+# Linux v6.14, Alpha, MIPS, PA-RISC, PowerPC, and SPARC) have their
+# own numbers. This IDL file lists the generic numbers.
+#
+# https://github.com/chaos/diod/issues/35
+""".strip()
+)
+
+prefix = "num errno += "
+namelen = max(len(name) for name in errnos)
+numlen = max(len(str(num)) for (num, desc) in errnos.values())
+for name, (num, desc) in errnos.items():
+ print(f'{prefix}"L_{name:<{namelen}} = {num:>{numlen}}" # {desc}')
+ prefix = " " * len(prefix)
+print()
+
+print(
+ """
+num super_magic = 4
+ # See <linux/magic.h> (linux.git include/uapi/linux/magic.h).
+ #
+ # To quote `util-linux.git:include/statfs_magic.h`:
+ # "Unfortunately, Linux kernel header file <linux/magic.h> is
+ # incomplete mess and kernel returns by statfs f_type many numbers
+ # that are nowhere specified (in API)."
+ #
+ # util-linux <statfs_magic.h> is also incomplete. As is the
+ # statfs(2) man-page.
+ #
+ # I'm working on a patchset to the kernel to get <linux/magic.h>
+ # to be complete, but in the mean-time I'm just not going to
+ # bother with putting a list here.
+ #
+ # TODO
+ "V9FS_MAGIC=0x01021997"
+
+# "L"inux "O"pen flags (flags to pass to Tlopen and Tlcreate)
+#
+# The values are not specified in in protocol.md, but are specified in
+# protocol.h (and are different than the Linux kernel's values, which
+# vary by architecture).
+bitfield lo = 4
+ "bit 0=num(MODE)" # low bit of the 2-bit RDONLY/WRONLY/RDWR/NOACCESS enum
+ "bit 1=num(MODE)" # high bit of the 2-bit RDONLY/WRONLY/RDWR/NOACCESS enum
+ #"bit 2=unused"
+ #"bit 3=unused"
+ #"bit 4=unused"
+ #"bit 5=unused"
+ "bit 6=CREATE"
+ "bit 7=EXCL"
+ "bit 8=NOCTTY"
+ "bit 9=TRUNC"
+ "bit 10=APPEND"
+ "bit 11=NONBLOCK"
+ "bit 12=DSYNC"
+ "bit 13=BSD_FASYNC"
+ "bit 14=DIRECT"
+ "bit 15=LARGEFILE"
+ "bit 16=DIRECTORY"
+ "bit 17=NOFOLLOW"
+ "bit 18=NOATIME"
+ "bit 19=CLOEXEC"
+ "bit 20=SYNC"
+
+ "num(MODE) RDONLY = 0"
+ "num(MODE) WRONLY = 1"
+ "num(MODE) RDWR = 2"
+ "num(MODE) NOACCESS = 3"
+
+ "mask FLAG = 0b111111111111111000000"
+
+# "D"irentry "T"ype
+#
+# These match the Linux kernel's values.
+num dt = 1
+ "UNKNOWN = 0"
+ "PIPE = 1"
+ "CHAR_DEV = 2"
+ "DIRECTORY = 4"
+ "BLOCK_DEV = 6" # proof it's not a bitfield
+ "REGULAR = 8"
+ "SYMLINK = 10" # proof it's not a bitfield
+ "SOCKET = 12" # proof it's not a bitfield
+ "_WHITEOUT = 14" # proof it's not a bitfield
+
+# Mode
+#
+# These match the Linux kernel's values. Why is this 32-bits wide
+# instead of just 16? Who knows?
+bitfield mode = 4
+ #...
+ "bit 15=num(FMT)" # bit of the 4-bit FMT_ enum
+ "bit 14=num(FMT)" # bit of the 4-bit FMT_ enum
+ "bit 13=num(FMT)" # bit of the 4-bit FMT_ enum
+ "bit 12=num(FMT)" # bit of the 4-bit FMT_ enum
+ #...
+ "bit 11=PERM_SETGROUP"
+ "bit 10=PERM_SETUSER"
+ "bit 9=PERM_STICKY"
+ "bit 8=PERM_OWNER_R"
+ "bit 7=PERM_OWNER_W"
+ "bit 6=PERM_OWNER_X"
+ "bit 5=PERM_GROUP_R"
+ "bit 4=PERM_GROUP_W"
+ "bit 3=PERM_GROUP_X"
+ "bit 2=PERM_OTHER_R"
+ "bit 1=PERM_OTHER_W"
+ "bit 0=PERM_OTHER_X"
+
+ "num(FMT) PIPE = dt.PIPE<<12"
+ "num(FMT) CHAR_DEV = dt.CHAR_DEV<<12"
+ "num(FMT) DIRECTORY = dt.DIRECTORY<<12"
+ "num(FMT) BLOCK_DEV = dt.BLOCK_DEV<<12"
+ "num(FMT) REGULAR = dt.REGULAR<<12"
+ "num(FMT) SYMLINK = dt.SYMLINK<<12"
+ "num(FMT) SOCKET = dt.SOCKET<<12"
+
+ "mask PERM = 07777" # PERM_*
+
+# A boolean value that is for some reason 4 bytes wide.
+num b4 = 4
+ "FALSE=0"
+ "TRUE=1"
+ # all other values are true also
+
+bitfield getattr = 8
+ "bit 0=MODE"
+ "bit 1=NLINK"
+ "bit 2=UID"
+ "bit 3=GID"
+ "bit 4=RDEV"
+ "bit 5=ATIME"
+ "bit 6=MTIME"
+ "bit 7=CTIME"
+ "bit 8=INO"
+ "bit 9=SIZE"
+ "bit 10=BLOCKS"
+
+ "bit 11=BTIME"
+ "bit 12=GEN"
+ "bit 13=DATA_VERSION"
+
+ "alias BASIC=0x000007ff" # Mask for fields up to BLOCKS
+ "alias ALL =0x00003fff" # Mask for All fields above
+
+bitfield setattr = 4
+ "bit 0=MODE"
+ "bit 1=UID"
+ "bit 2=GID"
+ "bit 3=SIZE"
+ "bit 4=ATIME"
+ "bit 5=MTIME"
+ "bit 6=CTIME"
+ "bit 7=ATIME_SET"
+ "bit 8=MTIME_SET"
+
+num lock_type = 1
+ "RDLCK=0"
+ "WRLCK=1"
+ "UNLCK=2"
+
+bitfield lock_flags = 4
+ "bit 0=BLOCK"
+ "bit 1=RECLAIM"
+
+num lock_status = 1
+ "SUCCESS=0"
+ "BLOCKED=1"
+ "ERROR=2"
+ "GRACE=3"
+
+# 9P2000 Operations (.L subset) ################################################
+
+from ./2002-9P2000.9p import Tversion, Rversion
+from ./2002-9P2000.9p import Tflush, Rflush
+from ./2002-9P2000.9p import Twalk, Rwalk
+from ./2002-9P2000.9p import Tread, Rread, Twrite, Rwrite
+from ./2002-9P2000.9p import Tclunk, Rclunk
+from ./2002-9P2000.9p import Tremove, Rremove
+
+# 9P2000.u Operations (.L subset) ##############################################
+
+from ./2005-9P2000.u.9p import Tattach, Rattach, Tauth, Rauth
+
+# 9P2000.L Operations ##########################################################
+
+#msg Tlerror = "size[4,val=end-&size] typ[1,val=6] tag[tag] illegal" # analogous to 106/Terror
+msg Rlerror = "size[4,val=end-&size] typ[1,val=7] tag[tag] errnum[errno]" # analogous to 107/Rerror
+msg Tstatfs = "size[4,val=end-&size] typ[1,val=8] tag[tag] fid[fid]"
+msg Rstatfs = "size[4,val=end-&size] typ[1,val=9] tag[tag]" # Description | statfs | statvfs
+ "type[super_magic]" # Type of filesystem | f_type | -
+ "bsize[4]" # Block size in bytes | f_bsize | f_bsize
+ # - # Fragment size in bytes | f_frsize (since Linux 2.6) | f_frsize
+ "blocks[8]" # Size of FS in f_frsize units | f_blocks | f_blocks
+ "bfree[8]" # Number of free blocks | f_bfree | f_bfree
+ "bavail[8]" # Number of free blocks for unprivileged users | f_bavail | b_avail
+ "files[8]" # Number of inodes | f_files | f_files
+ "ffree[8]" # Number of free inodes | f_ffree | f_ffree
+ # - # Number of free inodes for unprivileged users | - | f_favail
+ "fsid[8]" # Filesystem instance ID | f_fsid | f_fsid
+ # - # Mount flags | f_flags (since Linux 2.6.36) | f_flag
+ "namelen[4]" # Maximum filename length | f_namemax | f_namemax
+msg Tlopen = "size[4,val=end-&size] typ[1,val=12] tag[tag] fid[fid] flags[lo]" # analogous to 112/Topen
+msg Rlopen = "size[4,val=end-&size] typ[1,val=13] tag[tag] qid[qid] iounit[4]" # analogous to 113/Ropen
+msg Tlcreate = "size[4,val=end-&size] typ[1,val=14] tag[tag] fid[fid] name[s] flags[lo] mode[mode] gid[nuid]" # analogous to 114/Tcreate
+msg Rlcreate = "size[4,val=end-&size] typ[1,val=15] tag[tag] qid[qid] iounit[4]" # analogous to 115/Rcreate
+msg Tsymlink = "size[4,val=end-&size] typ[1,val=16] tag[tag] fid[fid] name[s] symtgt[s] gid[nuid]"
+msg Rsymlink = "size[4,val=end-&size] typ[1,val=17] tag[tag] qid[qid]"
+msg Tmknod = "size[4,val=end-&size] typ[1,val=18] tag[tag] dfid[fid] name[s] mode[mode] major[4] minor[4] gid[nuid]"
+msg Rmknod = "size[4,val=end-&size] typ[1,val=19] tag[tag] qid[qid]"
+msg Trename = "size[4,val=end-&size] typ[1,val=20] tag[tag] fid[fid] dfid[fid] name[s]"
+msg Rrename = "size[4,val=end-&size] typ[1,val=21] tag[tag]"
+msg Treadlink = "size[4,val=end-&size] typ[1,val=22] tag[tag] fid[fid]"
+msg Rreadlink = "size[4,val=end-&size] typ[1,val=23] tag[tag] target[s]"
+msg Tgetattr = "size[4,val=end-&size] typ[1,val=24] tag[tag] fid[fid] request_mask[getattr]"
+msg Rgetattr = "size[4,val=end-&size] typ[1,val=25] tag[tag] valid[getattr] qid[qid] mode[mode] uid[nuid] gid[nuid] nlink[8]"
+ "rdev[8] filesize[8] blksize[8] blocks[8]"
+ "atime_sec[8] atime_nsec[8] mtime_sec[8] mtime_nsec[8]"
+ "ctime_sec[8] ctime_nsec[8] btime_sec[8] btime_nsec[8]"
+ "gen[8] data_version[8]"
+msg Tsetattr = "size[4,val=end-&size] typ[1,val=26] tag[tag] fid[fid] valid[setattr] mode[mode] uid[nuid] gid[nuid] filesize[8] atime_sec[8] atime_nsec[8] mtime_sec[8] mtime_nsec[8]"
+msg Rsetattr = "size[4,val=end-&size] typ[1,val=27] tag[tag]"
+#...
+msg Txattrwalk = "size[4,val=end-&size] typ[1,val=30] tag[tag] fid[fid] newfid[fid] name[s]"
+msg Rxattrwalk = "size[4,val=end-&size] typ[1,val=31] tag[tag] attr_size[8]"
+msg Txattrcreate = "size[4,val=end-&size] typ[1,val=32] tag[tag] fid[fid] name[s] attr_size[8] flags[4]"
+msg Rxattrcreate = "size[4,val=end-&size] typ[1,val=33] tag[tag]"
+#...
+msg Treaddir = "size[4,val=end-&size] typ[1,val=40] tag[tag] fid[fid] offset[8] count[4]"
+msg Rreaddir = "size[4,val=end-&size] typ[1,val=41] tag[tag] count[4] count*(data[1])" # data is "qid[qid] offset[8] type[dt] name[s]"
+#...
+msg Tfsync = "size[4,val=end-&size] typ[1,val=50] tag[tag] fid[fid] datasync[b4]"
+msg Rfsync = "size[4,val=end-&size] typ[1,val=51] tag[tag]"
+msg Tlock = "size[4,val=end-&size] typ[1,val=52] tag[tag] fid[fid] type[lock_type] flags[lock_flags] start[8] length[8] proc_id[4] client_id[s]"
+msg Rlock = "size[4,val=end-&size] typ[1,val=53] tag[tag] status[lock_status]"
+msg Tgetlock = "size[4,val=end-&size] typ[1,val=54] tag[tag] fid[fid] type[lock_type] start[8] length[8] proc_id[4] client_id[s]"
+msg Rgetlock = "size[4,val=end-&size] typ[1,val=55] tag[tag] type[lock_type] start[8] length[8] proc_id[4] client_id[s]"
+# ...
+msg Tlink = "size[4,val=end-&size] typ[1,val=70] tag[tag] dfid[fid] fid[fid] name[s]"
+msg Rlink = "size[4,val=end-&size] typ[1,val=71] tag[tag]"
+msg Tmkdir = "size[4,val=end-&size] typ[1,val=72] tag[tag] dfid[fid] name[s] mode[mode] gid[nuid]"
+msg Rmkdir = "size[4,val=end-&size] typ[1,val=73] tag[tag] qid[qid]"
+msg Trenameat = "size[4,val=end-&size] typ[1,val=74] tag[tag] olddirfid[fid] oldname[s] newdirfid[fid] newname[s]"
+msg Rrenameat = "size[4,val=end-&size] typ[1,val=75] tag[tag]"
+msg Tunlinkat = "size[4,val=end-&size] typ[1,val=76] tag[tag] dirfd[fid] name[s] flags[4]"
+msg Runlinkat = "size[4,val=end-&size] typ[1,val=77] tag[tag]"
+""".strip()
+)
diff --git a/lib9p/idl/__init__.py b/lib9p/idl/__init__.py
index 2d09217..3133cc4 100644
--- a/lib9p/idl/__init__.py
+++ b/lib9p/idl/__init__.py
@@ -647,17 +647,16 @@ def re_string(grpname: str) -> str:
re_line_version = f"version\\s+{re_string('version')}"
re_line_import = f"from\\s+(?P<file>\\S+)\\s+import\\s+(?P<syms>{re_impname}(?:\\s*,\\s*{re_impname})*)"
re_line_num = f"num\\s+(?P<name>{re_symname})\\s*=\\s*(?P<prim>{re_priname})"
+re_line_num_ = f"num\\s+(?P<name>{re_symname})\\s*\\+=\\s*{re_string('spec')}"
re_line_bitfield = f"bitfield\\s+(?P<name>{re_symname})\\s*=\\s*(?P<prim>{re_priname})"
-re_line_bitfield_ = (
- f"bitfield\\s+(?P<name>{re_symname})\\s*\\+=\\s*{re_string('member')}"
-)
+re_line_bitfield_ = f"bitfield\\s+(?P<name>{re_symname})\\s*\\+=\\s*{re_string('spec')}"
re_line_struct = (
f"struct\\s+(?P<name>{re_symname})\\s*(?P<op>\\+?=)\\s*{re_string('members')}"
)
re_line_msg = (
f"msg\\s+(?P<name>{re_msgname})\\s*(?P<op>\\+?=)\\s*{re_string('members')}"
)
-re_line_cont = f"\\s+{re_string('specs')}" # could be bitfield/struct/msg
+re_line_cont = f"\\s+{re_string('spec')}" # could be bitfield/struct/msg
def parse_file(
@@ -741,6 +740,11 @@ def parse_file(
raise ValueError(f"duplicate type name {num.typname!r}")
env[num.typname] = num
prev = num
+ elif m := re.fullmatch(re_line_num_, line):
+ num = get_type(env, m.group("name"), Number)
+ parse_numspec(env, version, num, m.group("spec"))
+
+ prev = num
elif m := re.fullmatch(re_line_bitfield, line):
prim = env[m.group("prim")]
assert isinstance(prim, Primitive)
@@ -754,7 +758,7 @@ def parse_file(
prev = bf
elif m := re.fullmatch(re_line_bitfield_, line):
bf = get_type(env, m.group("name"), Bitfield)
- parse_bitspec(env, version, bf, m.group("member"))
+ parse_bitspec(env, version, bf, m.group("spec"))
prev = bf
elif m := re.fullmatch(re_line_struct, line):
@@ -798,11 +802,11 @@ def parse_file(
elif m := re.fullmatch(re_line_cont, line):
match prev:
case Bitfield():
- parse_bitspec(env, version, prev, m.group("specs"))
+ parse_bitspec(env, version, prev, m.group("spec"))
case Number():
- parse_numspec(env, version, prev, m.group("specs"))
+ parse_numspec(env, version, prev, m.group("spec"))
case Struct(): # and Message()
- parse_members(version, env, prev, m.group("specs"))
+ parse_members(version, env, prev, m.group("spec"))
case _:
raise SyntaxError(
"continuation line must come after a bitfield, struct, or msg line"
diff --git a/lib9p/include/lib9p/linux-errno.h b/lib9p/include/lib9p/linux-errno.h
deleted file mode 100644
index e7c74f5..0000000
--- a/lib9p/include/lib9p/linux-errno.h
+++ /dev/null
@@ -1,139 +0,0 @@
-/* lib9p/linux-errno.h - Generated by `lib9p/include/lib9p/linux-errno.h.gen 3rd-party/linux-errno.txt`. DO NOT EDIT! */
-/* 3rd-party/linux-errno.txt - Generated from build-aux/linux-errno.txt.gen and linux.git v6.7. DO NOT EDIT! */
-
-#ifndef _LIB9P_LINUX_ERRNO_H_
-#define _LIB9P_LINUX_ERRNO_H_
-
-#define LINUX_EPERM 1 /* Operation not permitted */
-#define LINUX_ENOENT 2 /* No such file or directory */
-#define LINUX_ESRCH 3 /* No such process */
-#define LINUX_EINTR 4 /* Interrupted system call */
-#define LINUX_EIO 5 /* I/O error */
-#define LINUX_ENXIO 6 /* No such device or address */
-#define LINUX_E2BIG 7 /* Argument list too long */
-#define LINUX_ENOEXEC 8 /* Exec format error */
-#define LINUX_EBADF 9 /* Bad file number */
-#define LINUX_ECHILD 10 /* No child processes */
-#define LINUX_EAGAIN 11 /* Try again */
-#define LINUX_ENOMEM 12 /* Out of memory */
-#define LINUX_EACCES 13 /* Permission denied */
-#define LINUX_EFAULT 14 /* Bad address */
-#define LINUX_ENOTBLK 15 /* Block device required */
-#define LINUX_EBUSY 16 /* Device or resource busy */
-#define LINUX_EEXIST 17 /* File exists */
-#define LINUX_EXDEV 18 /* Cross-device link */
-#define LINUX_ENODEV 19 /* No such device */
-#define LINUX_ENOTDIR 20 /* Not a directory */
-#define LINUX_EISDIR 21 /* Is a directory */
-#define LINUX_EINVAL 22 /* Invalid argument */
-#define LINUX_ENFILE 23 /* File table overflow */
-#define LINUX_EMFILE 24 /* Too many open files */
-#define LINUX_ENOTTY 25 /* Not a typewriter */
-#define LINUX_ETXTBSY 26 /* Text file busy */
-#define LINUX_EFBIG 27 /* File too large */
-#define LINUX_ENOSPC 28 /* No space left on device */
-#define LINUX_ESPIPE 29 /* Illegal seek */
-#define LINUX_EROFS 30 /* Read-only file system */
-#define LINUX_EMLINK 31 /* Too many links */
-#define LINUX_EPIPE 32 /* Broken pipe */
-#define LINUX_EDOM 33 /* Math argument out of domain of func */
-#define LINUX_ERANGE 34 /* Math result not representable */
-#define LINUX_EDEADLK 35 /* Resource deadlock would occur */
-#define LINUX_ENAMETOOLONG 36 /* File name too long */
-#define LINUX_ENOLCK 37 /* No record locks available */
-#define LINUX_ENOSYS 38 /* Invalid system call number */
-#define LINUX_ENOTEMPTY 39 /* Directory not empty */
-#define LINUX_ELOOP 40 /* Too many symbolic links encountered */
-#define LINUX_ENOMSG 42 /* No message of desired type */
-#define LINUX_EIDRM 43 /* Identifier removed */
-#define LINUX_ECHRNG 44 /* Channel number out of range */
-#define LINUX_EL2NSYNC 45 /* Level 2 not synchronized */
-#define LINUX_EL3HLT 46 /* Level 3 halted */
-#define LINUX_EL3RST 47 /* Level 3 reset */
-#define LINUX_ELNRNG 48 /* Link number out of range */
-#define LINUX_EUNATCH 49 /* Protocol driver not attached */
-#define LINUX_ENOCSI 50 /* No CSI structure available */
-#define LINUX_EL2HLT 51 /* Level 2 halted */
-#define LINUX_EBADE 52 /* Invalid exchange */
-#define LINUX_EBADR 53 /* Invalid request descriptor */
-#define LINUX_EXFULL 54 /* Exchange full */
-#define LINUX_ENOANO 55 /* No anode */
-#define LINUX_EBADRQC 56 /* Invalid request code */
-#define LINUX_EBADSLT 57 /* Invalid slot */
-#define LINUX_EBFONT 59 /* Bad font file format */
-#define LINUX_ENOSTR 60 /* Device not a stream */
-#define LINUX_ENODATA 61 /* No data available */
-#define LINUX_ETIME 62 /* Timer expired */
-#define LINUX_ENOSR 63 /* Out of streams resources */
-#define LINUX_ENONET 64 /* Machine is not on the network */
-#define LINUX_ENOPKG 65 /* Package not installed */
-#define LINUX_EREMOTE 66 /* Object is remote */
-#define LINUX_ENOLINK 67 /* Link has been severed */
-#define LINUX_EADV 68 /* Advertise error */
-#define LINUX_ESRMNT 69 /* Srmount error */
-#define LINUX_ECOMM 70 /* Communication error on send */
-#define LINUX_EPROTO 71 /* Protocol error */
-#define LINUX_EMULTIHOP 72 /* Multihop attempted */
-#define LINUX_EDOTDOT 73 /* RFS specific error */
-#define LINUX_EBADMSG 74 /* Not a data message */
-#define LINUX_EOVERFLOW 75 /* Value too large for defined data type */
-#define LINUX_ENOTUNIQ 76 /* Name not unique on network */
-#define LINUX_EBADFD 77 /* File descriptor in bad state */
-#define LINUX_EREMCHG 78 /* Remote address changed */
-#define LINUX_ELIBACC 79 /* Can not access a needed shared library */
-#define LINUX_ELIBBAD 80 /* Accessing a corrupted shared library */
-#define LINUX_ELIBSCN 81 /* .lib section in a.out corrupted */
-#define LINUX_ELIBMAX 82 /* Attempting to link in too many shared libraries */
-#define LINUX_ELIBEXEC 83 /* Cannot exec a shared library directly */
-#define LINUX_EILSEQ 84 /* Illegal byte sequence */
-#define LINUX_ERESTART 85 /* Interrupted system call should be restarted */
-#define LINUX_ESTRPIPE 86 /* Streams pipe error */
-#define LINUX_EUSERS 87 /* Too many users */
-#define LINUX_ENOTSOCK 88 /* Socket operation on non-socket */
-#define LINUX_EDESTADDRREQ 89 /* Destination address required */
-#define LINUX_EMSGSIZE 90 /* Message too long */
-#define LINUX_EPROTOTYPE 91 /* Protocol wrong type for socket */
-#define LINUX_ENOPROTOOPT 92 /* Protocol not available */
-#define LINUX_EPROTONOSUPPORT 93 /* Protocol not supported */
-#define LINUX_ESOCKTNOSUPPORT 94 /* Socket type not supported */
-#define LINUX_EOPNOTSUPP 95 /* Operation not supported on transport endpoint */
-#define LINUX_EPFNOSUPPORT 96 /* Protocol family not supported */
-#define LINUX_EAFNOSUPPORT 97 /* Address family not supported by protocol */
-#define LINUX_EADDRINUSE 98 /* Address already in use */
-#define LINUX_EADDRNOTAVAIL 99 /* Cannot assign requested address */
-#define LINUX_ENETDOWN 100 /* Network is down */
-#define LINUX_ENETUNREACH 101 /* Network is unreachable */
-#define LINUX_ENETRESET 102 /* Network dropped connection because of reset */
-#define LINUX_ECONNABORTED 103 /* Software caused connection abort */
-#define LINUX_ECONNRESET 104 /* Connection reset by peer */
-#define LINUX_ENOBUFS 105 /* No buffer space available */
-#define LINUX_EISCONN 106 /* Transport endpoint is already connected */
-#define LINUX_ENOTCONN 107 /* Transport endpoint is not connected */
-#define LINUX_ESHUTDOWN 108 /* Cannot send after transport endpoint shutdown */
-#define LINUX_ETOOMANYREFS 109 /* Too many references: cannot splice */
-#define LINUX_ETIMEDOUT 110 /* Connection timed out */
-#define LINUX_ECONNREFUSED 111 /* Connection refused */
-#define LINUX_EHOSTDOWN 112 /* Host is down */
-#define LINUX_EHOSTUNREACH 113 /* No route to host */
-#define LINUX_EALREADY 114 /* Operation already in progress */
-#define LINUX_EINPROGRESS 115 /* Operation now in progress */
-#define LINUX_ESTALE 116 /* Stale file handle */
-#define LINUX_EUCLEAN 117 /* Structure needs cleaning */
-#define LINUX_ENOTNAM 118 /* Not a XENIX named type file */
-#define LINUX_ENAVAIL 119 /* No XENIX semaphores available */
-#define LINUX_EISNAM 120 /* Is a named type file */
-#define LINUX_EREMOTEIO 121 /* Remote I/O error */
-#define LINUX_EDQUOT 122 /* Quota exceeded */
-#define LINUX_ENOMEDIUM 123 /* No medium found */
-#define LINUX_EMEDIUMTYPE 124 /* Wrong medium type */
-#define LINUX_ECANCELED 125 /* Operation Canceled */
-#define LINUX_ENOKEY 126 /* Required key not available */
-#define LINUX_EKEYEXPIRED 127 /* Key has expired */
-#define LINUX_EKEYREVOKED 128 /* Key has been revoked */
-#define LINUX_EKEYREJECTED 129 /* Key was rejected by service */
-#define LINUX_EOWNERDEAD 130 /* Owner died */
-#define LINUX_ENOTRECOVERABLE 131 /* State not recoverable */
-#define LINUX_ERFKILL 132 /* Operation not possible due to RF-kill */
-#define LINUX_EHWPOISON 133 /* Memory page has hardware error */
-
-#endif /* _LIB9P_LINUX_ERRNO_H_ */
diff --git a/lib9p/include/lib9p/linux-errno.h.gen b/lib9p/include/lib9p/linux-errno.h.gen
deleted file mode 100755
index 2c736a2..0000000
--- a/lib9p/include/lib9p/linux-errno.h.gen
+++ /dev/null
@@ -1,38 +0,0 @@
-#!/usr/bin/env python
-# lib9p/linux-errno.h.gen - Generate a C header from a list of errno numbers
-#
-# Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
-# SPDX-License-Identifier: AGPL-3.0-or-later
-
-import sys
-
-
-def print_errnos() -> None:
- print(
- f"/* lib9p/linux-errno.h - Generated by `{' '.join(sys.argv)}`. DO NOT EDIT! */"
- )
- errnos: dict[str, tuple[int, str]] = {}
- for txtlist in sys.argv[1:]:
- with open(txtlist, "r", encoding="utf-8") 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 _LIB9P_LINUX_ERRNO_H_")
- print("#define _LIB9P_LINUX_ERRNO_H_")
- print()
- namelen = max(len(name) for name in errnos)
- numlen = max(len(str(num)) for (num, desc) in errnos.values())
- for name, [num, msg] in errnos.items():
- print(f"#define LINUX_{name:<{namelen}} {num:>{numlen}} /* {msg} */")
- print()
- print("#endif /* _LIB9P_LINUX_ERRNO_H_ */")
-
-
-if __name__ == "__main__":
- print_errnos()
diff --git a/lib9p/include/lib9p/srv.h b/lib9p/include/lib9p/srv.h
deleted file mode 100644
index ff5ebdc..0000000
--- a/lib9p/include/lib9p/srv.h
+++ /dev/null
@@ -1,179 +0,0 @@
-/* lib9p/srv.h - 9P server
- *
- * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
-
-#ifndef _LIB9P_SRV_H_
-#define _LIB9P_SRV_H_
-
-#include <libcr/coroutine.h>
-#include <libcr_ipc/rpc.h>
-#include <libcr_ipc/chan.h>
-#include <libhw/generic/net.h>
-#include <libmisc/assert.h>
-#include <libmisc/private.h>
-#include <libobj/obj.h>
-
-#include <lib9p/9p.h>
-
-/* context ********************************************************************/
-
-CR_CHAN_DECLARE(_lib9p_srv_flushch, bool)
-
-struct lib9p_srv_ctx {
- struct lib9p_ctx basectx;
- uint32_t uid;
- struct lib9p_s uname;
-
- BEGIN_PRIVATE(LIB9P_SRV_H)
- _lib9p_srv_flushch_t _flushch;
- END_PRIVATE(LIB9P_SRV_H)
-};
-
-bool lib9p_srv_flush_requested(struct lib9p_srv_ctx *ctx);
-
-int lib9p_srv_acknowledge_flush(struct lib9p_srv_ctx *ctx);
-
-/* interface definitions ******************************************************/
-
-lo_interface lib9p_srv_fio;
-lo_interface lib9p_srv_dio;
-
-/* FIXME: I don't like that the pointers returned by stat() and
- * pread() have to remain live after they return. Perhaps a
- * `respond()`-callback? But that just reads as gross in C.
- *
- * FIXME: It would be nice if pread() could return more than 1 iovec.
- */
-#define lib9p_srv_file_LO_IFACE \
- /* resource management **********************************************/ \
- \
- /** \
- * free() is be called when all FIDs associated with the file are \
- * clunked. \
- * \
- * free() MUST NOT error. \
- */ \
- LO_FUNC(void , free ) \
- \
- /** \
- * qid() is called frequently and returns the current QID of the file. \
- * The .path field MUST never change, the .type field may change in \
- * response to wstat() calls (but the QT_DIR bit MUST NOT change), and \
- * the .vers field may change frequently in response to any number of \
- * things (wstat(), write(), or non-9P events). \
- * \
- * qid() MUST NOT error. \
- */ \
- LO_FUNC(struct lib9p_qid , qid ) \
- \
- /* non-"opened" generic I/O *****************************************/ \
- \
- LO_FUNC(struct lib9p_stat , stat , struct lib9p_srv_ctx *) \
- LO_FUNC(void , wstat , struct lib9p_srv_ctx *, \
- struct lib9p_stat new) \
- LO_FUNC(void , remove , struct lib9p_srv_ctx *) \
- \
- /* non-"opened" directory I/O ***************************************/ \
- \
- LO_FUNC(lo_interface lib9p_srv_file, dwalk , struct lib9p_srv_ctx *, \
- struct lib9p_s childname) \
- LO_FUNC(lo_interface lib9p_srv_file, dcreate, struct lib9p_srv_ctx *, \
- struct lib9p_s childname, \
- lib9p_dm_t perm, \
- lib9p_o_t flags) \
- \
- /* open() for I/O ***************************************************/ \
- \
- LO_FUNC(lo_interface lib9p_srv_fio , fopen , struct lib9p_srv_ctx *, \
- bool rd, bool wr, \
- bool trunc) \
- LO_FUNC(lo_interface lib9p_srv_dio , dopen , struct lib9p_srv_ctx *)
-LO_INTERFACE(lib9p_srv_file);
-
-#define lib9p_srv_fio_LO_IFACE \
- LO_FUNC(struct lib9p_qid , qid ) \
- LO_FUNC(void , iofree ) \
- LO_FUNC(uint32_t , iounit ) \
- LO_FUNC(void , pread , struct lib9p_srv_ctx *, \
- uint32_t byte_count, \
- uint64_t byte_offset, \
- struct iovec *ret) \
- LO_FUNC(uint32_t , pwrite , struct lib9p_srv_ctx *, \
- void *buf, \
- uint32_t byte_count, \
- uint64_t byte_offset)
-LO_INTERFACE(lib9p_srv_fio);
-
-/* FIXME: The dio interface just feels clunky. I'm not in a rush to
- * change it because util9p_static_dir is already implemented and I
- * don't anticipate the sbc-harness needing another dio
- * implementation. But if I wanted lib9p to be used outside of
- * sbc-harness, this is one of the first things that I'd want to
- * change.
- */
-#define lib9p_srv_dio_LO_IFACE \
- LO_FUNC(struct lib9p_qid , qid ) \
- LO_FUNC(void , iofree ) \
- LO_FUNC(size_t /* <- obj cnt */ , dread , struct lib9p_srv_ctx *, \
- uint8_t *buf, \
- /* num bytes -> */ uint32_t byte_count, \
- /* starting at this object -> */ size_t obj_offset)
-LO_INTERFACE(lib9p_srv_dio);
-
-#define LIB9P_SRV_NOTDIR(TYP, NAM) \
- static lo_interface lib9p_srv_file NAM##_dwalk (TYP *, struct lib9p_srv_ctx *, struct lib9p_s) { assert_notreached("not a directory"); } \
- static lo_interface lib9p_srv_file NAM##_dcreate(TYP *, struct lib9p_srv_ctx *, struct lib9p_s, lib9p_dm_t, lib9p_o_t) { assert_notreached("not a directory"); } \
- static lo_interface lib9p_srv_dio NAM##_dopen (TYP *, struct lib9p_srv_ctx *) { assert_notreached("not a directory"); }
-
-#define LIB9P_SRV_NOTFILE(TYP, NAM) \
- static lo_interface lib9p_srv_fio NAM##_fopen (TYP *, struct lib9p_srv_ctx *, bool, bool, bool) { assert_notreached("not a file"); }
-
-/* main server entrypoints ****************************************************/
-
-CR_RPC_DECLARE(_lib9p_srv_reqch, struct _lib9p_srv_req *, bool)
-
-struct lib9p_srv {
- /* Things you provide */
- void /*TODO*/ (*auth )(struct lib9p_srv_ctx *, struct lib9p_s treename); /* optional */
- lo_interface lib9p_srv_file (*rootdir)(struct lib9p_srv_ctx *, struct lib9p_s treename);
-
- /* For internal use */
- BEGIN_PRIVATE(LIB9P_SRV_H)
- unsigned int readers;
- unsigned int writers;
- _lib9p_srv_reqch_t _reqch;
- END_PRIVATE(LIB9P_SRV_H)
-};
-
-/**
- * In an infinite loop, accept a connection and read messages from it until
- * close; dispatching requests to a pool of lib9p_srv_write_cr() coroutines
- * with the same `srv`.
- *
- * Will just close the connection if a T-message has a size[4] <7.
- *
- * @param srv: The server configuration and state; has an associated pool of
- * lib9p_srv_write_cr() coroutines.
- * @param listener: The listener object to accept connections from.
- *
- * @errno LINUX_EMSGSIZE T-message has size[4] bigger than max_msg_size
- * @errno LINUX_EDOM Tversion specified an impossibly small max_msg_size
- * @errno LINUX_EOPNOTSUPP T-message has an R-message type, or an unrecognized T-message type
- * @errno LINUX_EBADMSG T-message has wrong size[4] for its content, or has invalid UTF-8
- * @errno LINUX_ERANGE R-message does not fit into max_msg_size
- */
-[[noreturn]] void lib9p_srv_read_cr(struct lib9p_srv *srv, lo_interface net_stream_listener listener);
-
-/**
- * Service requests to the `struct lib9p_srv *srv` argument that have been
- * read by lib9p_srv_read_cr().
- *
- * @param struct lib9p_srv *srv: The server configuration and state; has an
- * associated pool of lib9p_srv_read_cr()
- * coroutines.
- */
-COROUTINE lib9p_srv_write_cr(void *_srv);
-
-#endif /* _LIB9P_SRV_H_ */
diff --git a/build-aux/linux-errno.txt.gen b/lib9p/linux-errno.txt.gen
index f94178f..687e58b 100755
--- a/build-aux/linux-errno.txt.gen
+++ b/lib9p/linux-errno.txt.gen
@@ -1,7 +1,7 @@
#!/usr/bin/env bash
-# build-aux/linux-errno.txt.gen - Generate a listing of Linux kernel errnos
+# lib9p/linux-errno.txt.gen - Generate a listing of Linux kernel errnos
#
-# Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com>
+# Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
# SPDX-License-Identifier: AGPL-3.0-or-later
set -e
diff --git a/lib9p/map.h b/lib9p/map.h
deleted file mode 100644
index c5eab0f..0000000
--- a/lib9p/map.h
+++ /dev/null
@@ -1,114 +0,0 @@
-/* lib9p/map.h - A really dumb map/dict data structure
- *
- * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
-
-/**
- * `#define` `NAME`, `KEY_T`, `VAL_T`, and `CAP`; then `#include
- * "map.h".
- */
-
-#ifndef NAME
- #error NAME must be defined
-#endif
-#ifndef KEY_T
- #error KEY_T must be defined
-#endif
-#ifndef VAL_T
- #error VAL_T must be defined
-#endif
-#ifndef CAP
- #error CAP must be defined
-#endif
-
-#ifndef MAP_KEY
-#define MAP_KV(TNAME) LM_CAT3(_,TNAME,_kv)
-#define MAP_METHOD(TNAME, MNAME) LM_CAT3(TNAME,_,MNAME)
-#define MAP_FOREACH(m, k, v) \
- for (size_t i = 0; i < LM_ARRAY_LEN((m)->items); i++) \
- if ( ({ k = (m)->items[i].key; v = &(m)->items[i].val; (m)->items[i].set; }) )
-#endif
-
-/* This implementation is just an array that we brute-force search
- * over for a slot. I don't want to use the heap, which means
- * statically-sized maps, and I'm probably going to choose a low
- * static size, so this is fine. */
-
-struct MAP_KV(NAME) {
- bool set;
- KEY_T key;
- VAL_T val;
-};
-
-struct NAME {
- size_t len;
- struct MAP_KV(NAME) items[CAP];
-};
-
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wunused-function"
-
-/**
- * Load an item from the map; return a pointer to the in-map value, or
- * NULL if the item is not in the map.
- */
-static VAL_T *MAP_METHOD(NAME,load)(struct NAME *m, KEY_T k) {
- if (!m->len)
- return NULL;
- for (size_t i = 0; i < LM_ARRAY_LEN(m->items); i++)
- if (m->items[i].set && m->items[i].key == k)
- return &(m->items[i].val);
- return NULL;
-}
-
-/**
- * Store an item into the map, perhaps replacing an existing value.
- * Return a pointer to the in-map value, or NULL if the map is full.
- */
-static VAL_T *MAP_METHOD(NAME,store)(struct NAME *m, KEY_T k, VAL_T v) {
- VAL_T *old = MAP_METHOD(NAME,load)(m, k);
- if (old) {
- *old = v;
- return old;
- }
- if (m->len == LM_ARRAY_LEN(m->items))
- return NULL;
- for (size_t i = 0; i < LM_ARRAY_LEN(m->items); i++)
- if (!m->items[i].set) {
- m->len++;
- m->items[i].set = true;
- m->items[i].key = k;
- m->items[i].val = v;
- return &(m->items[i].val);
- }
- assert_notreached("should have returned from inside for() loop");
-}
-
-/**
- * Delete an item from the map. Returns true if an item was deleted,
- * false if no such item was in the map.
- */
-static bool MAP_METHOD(NAME,del)(struct NAME *m, KEY_T k) {
- if (!m->len)
- return NULL;
- for (size_t i = 0; i < LM_ARRAY_LEN(m->items); i++)
- if (m->items[i].set && m->items[i].key == k) {
- m->items[i].set = false;
- m->len--;
- return true;
- }
- return false;
-}
-
-#pragma GCC diagnostic pop
-
-#undef NAME
-#undef KEY_T
-#undef VAL_T
-#undef CAP
-
-/* Keep the linter happy. */
-#ifndef _LIB9P_MAP_H_
-#define _LIB9P_MAP_H_
-#endif /* _LIB9P_MAP_H_ */
diff --git a/lib9p/srv.c b/lib9p/srv.c
index bfeb06f..3e4de91 100644
--- a/lib9p/srv.c
+++ b/lib9p/srv.c
@@ -4,21 +4,16 @@
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
-#include <alloca.h>
-#include <inttypes.h> /* for PRI* */
#include <stddef.h> /* for size_t */
-#include <limits.h> /* for SSIZE_MAX, not set by newlib */
-#ifndef SSIZE_MAX
-#define SSIZE_MAX (SIZE_MAX >> 1)
-#endif
+#include <string.h> /* for memcpy() */
#include <libcr/coroutine.h>
-#include <libcr_ipc/chan.h>
#include <libcr_ipc/mutex.h>
-#include <libcr_ipc/select.h>
+#include <libhw/generic/net.h>
+#include <libmisc/alloc.h>
#include <libmisc/assert.h>
#include <libmisc/endian.h>
-#include <libhw/generic/net.h>
+#include <libmisc/map.h>
#define LOG_NAME 9P_SRV
#include <libmisc/log.h>
@@ -26,20 +21,12 @@
#define IMPLEMENTATION_FOR_LIB9P_SRV_H YES
#include <lib9p/srv.h>
+#include "srv_errno.h"
+
/* config *********************************************************************/
#include "config.h"
-#ifndef CONFIG_9P_SRV_MAX_FIDS
- #error config.h must define CONFIG_9P_SRV_MAX_FIDS
-#endif
-#ifndef CONFIG_9P_SRV_MAX_REQS
- #error config.h must define CONFIG_9P_SRV_MAX_REQS
-#endif
-#ifndef CONFIG_9P_SRV_MAX_DEPTH
- /* 1=just the root dir, 2=just files in the root dir, 3=1 subdir, ... */
- #error config.h must define CONFIG_9P_SRV_MAX_DEPTH
-#endif
#ifndef CONFIG_9P_SRV_MAX_MSG_SIZE
#error config.h must define CONFIG_9P_SRV_MAX_MSG_SIZE
#endif
@@ -47,29 +34,50 @@
#error config.h must define CONFIG_9P_SRV_MAX_HOSTMSG_SIZE
#endif
static_assert(CONFIG_9P_SRV_MAX_MSG_SIZE <= CONFIG_9P_SRV_MAX_HOSTMSG_SIZE);
-static_assert(CONFIG_9P_SRV_MAX_HOSTMSG_SIZE <= SSIZE_MAX);
+static_assert(CONFIG_9P_SRV_MAX_HOSTMSG_SIZE <= SIZE_MAX);
/* context ********************************************************************/
bool lib9p_srv_flush_requested(struct lib9p_srv_ctx *ctx) {
assert(ctx);
- return _lib9p_srv_flushch_can_send(&ctx->_flushch);
+ return cr_chan_can_send(&ctx->flush_ch);
}
-int lib9p_srv_acknowledge_flush(struct lib9p_srv_ctx *ctx) {
- assert(ctx);
- assert(_lib9p_srv_flushch_can_send(&ctx->_flushch));
- lib9p_error(&ctx->basectx, LINUX_ECANCELED, "request canceled by flush");
- _lib9p_srv_flushch_send(&ctx->_flushch, true);
- return -1;
-}
+#define req_debug(...) \
+ log_debugln( \
+ "cid=", cr_getcid(), ": ", \
+ lib9p_msgtype_str(ctx->basectx.version, ctx->net_bytes[4]), "(tag=", ctx->tag, "): ", \
+ __VA_ARGS__)
/* structs ********************************************************************/
+#ifndef NDEBUG
+void lib9p_srv_stat_assert(const struct lib9p_srv_stat *stat) {
+ assert(stat);
+ assert( ((bool)(stat->mode & LIB9P_DM_DIR )) == ((bool)(stat->qid.type & LIB9P_QT_DIR )) );
+ assert( ((bool)(stat->mode & LIB9P_DM_APPEND)) == ((bool)(stat->qid.type & LIB9P_QT_APPEND)) );
+ assert( ((bool)(stat->mode & LIB9P_DM_EXCL )) == ((bool)(stat->qid.type & LIB9P_QT_EXCL )) );
+ assert( ((bool)(stat->mode & LIB9P_DM_AUTH )) == ((bool)(stat->qid.type & LIB9P_QT_AUTH )) );
+ assert( ((bool)(stat->mode & LIB9P_DM_TMP )) == ((bool)(stat->qid.type & LIB9P_QT_TMP )) );
+ assert( (stat->size == 0) || !(stat->mode & LIB9P_DM_DIR) );
+}
+#endif
+
+enum srv_filetype {
+ SRV_FILETYPE_FILE,
+ SRV_FILETYPE_DIR,
+ SRV_FILETYPE_AUTH,
+};
+
+/* path *****************************************/
+
typedef typeof( ((struct lib9p_qid){}).path ) srv_path_t;
struct srv_pathinfo {
lo_interface lib9p_srv_file file;
+ enum srv_filetype type;
+ /* .parent_dir is used for (1) Twalk(".."), and (2) for checking
+ * permissions on the parent directory for remove(). */
srv_path_t parent_dir;
/* References from other srv_pathinfos (via .parent_dir) or
@@ -79,21 +87,19 @@ struct srv_pathinfo {
unsigned int io_refcount;
};
-#define NAME pathmap
-#define KEY_T srv_path_t
-#define VAL_T struct srv_pathinfo
-/* ( naive ) + ( working space for walk() ) */
-#define CAP ( (CONFIG_9P_SRV_MAX_FIDS*CONFIG_9P_SRV_MAX_DEPTH) + (CONFIG_9P_SRV_MAX_REQS*2) )
-#include "map.h"
+/* fid ******************************************/
#define FIDFLAG_OPEN_R (1<<0)
#define FIDFLAG_OPEN_W (1<<1)
#define FIDFLAG_RCLOSE (1<<2)
+#define FIDFLAG_APPEND (1<<3)
#define FIDFLAG_OPEN (FIDFLAG_OPEN_R|FIDFLAG_OPEN_W)
-struct _srv_fidinfo {
+struct srv_fidinfo {
srv_path_t path;
+ struct lib9p_srv_userid *user;
uint8_t flags;
+ enum srv_filetype type;
union {
struct {
lo_interface lib9p_srv_fio io;
@@ -102,275 +108,520 @@ struct _srv_fidinfo {
lo_interface lib9p_srv_dio io;
size_t idx;
uint64_t off;
+ struct lib9p_srv_dirent buffered_dirent;
} dir;
+ struct {
+ struct lib9p_s aname;
+ bool completed;
+ } auth;
};
};
+DECLARE_ERROR_OR_(struct srv_fidinfo *, srv_fidinfop);
-#define NAME fidmap
-#define KEY_T lib9p_fid_t
-#define VAL_T struct _srv_fidinfo
-#define CAP CONFIG_9P_SRV_MAX_FIDS
-#include "map.h"
-
-#define NAME reqmap
-#define KEY_T lib9p_tag_t
-#define VAL_T struct _lib9p_srv_req *
-#define CAP CONFIG_9P_SRV_MAX_REQS
-#include "map.h"
-
-/* The hierarchy of concepts is:
+/* contexts **************************************
+ *
+ * The hierarchy of contexts is:
*
* server -> connection -> session -> request
*
*/
-/* struct _srv_srv {} is defined in <lib9p/srv.h> */
+/* struct lib9p_srv {} is defined in <lib9p/srv.h> */
-struct _srv_conn {
+struct srv_conn {
/* immutable */
struct lib9p_srv *parent_srv;
lo_interface net_stream_conn fd;
- cid_t reader; /* the lib9p_srv_read_cr() coroutine */
+ cid_t reader; /* the lib9p_srv_read() coroutine */
/* mutable */
cr_mutex_t writelock;
};
-struct _srv_sess {
+#define srv_sess _lib9p_srv_sess
+MAP_DECLARE(srv_pathmap, srv_path_t, struct srv_pathinfo);
+MAP_DECLARE(srv_fidmap, lib9p_fid_t, struct srv_fidinfo);
+MAP_DECLARE(srv_reqmap, lib9p_tag_t, struct lib9p_srv_ctx *);
+struct srv_sess {
/* immutable */
- struct _srv_conn *parent_conn;
+ struct srv_conn *parent_conn;
enum lib9p_version version;
uint32_t max_msg_size;
- uint32_t rerror_overhead;
/* mutable */
- bool initialized;
bool closing;
- struct pathmap paths; /* srv_path_t => lib9p_srv_file + metadata */
- struct fidmap fids; /* lib9p_fid_t => lib9p_srv_{fio,dio} + metadata */
- struct reqmap reqs; /* lib9p_tag_t => *_lib9p_srv_req */
+ struct srv_pathmap paths; /* srv_path_t => `lib9p_srv_file` + metadata */
+ struct srv_fidmap fids; /* lib9p_fid_t => `lib9p_srv_{fio,dio}` + metadata */
+ struct srv_reqmap reqs; /* lib9p_tag_t => `struct srv_req *` */
};
-struct _lib9p_srv_req {
- /* immutable */
- struct _srv_sess *parent_sess;
- uint16_t tag;
- uint8_t *net_bytes;
- /* mutable */
- struct lib9p_srv_ctx ctx;
-};
+#define srv_req lib9p_srv_ctx /* struct lib9p_srv_ctx {} is defined in <lib9p/srv.h> */
+
+/* utilities for the above types **********************************************/
+
+static enum srv_filetype srv_qid_filetype(struct lib9p_qid qid) {
+ if (qid.type & LIB9P_QT_AUTH)
+ return SRV_FILETYPE_AUTH;
+ if (qid.type & LIB9P_QT_DIR)
+ return SRV_FILETYPE_DIR;
+ return SRV_FILETYPE_FILE;
+}
+
+[[maybe_unused]]
+static bool srv_stat_check_perm(struct srv_req *ctx, struct lib9p_srv_stat *stat, uint8_t action) {
+ assert(ctx);
+ assert(stat);
+ assert(action);
+
+ /* TODO actually check user and group instead of just assuming "other". */
+ uint8_t mode = (uint8_t)(stat->mode & 07);
+
+ return mode & action;
+}
+
+[[maybe_unused]]
+static ok_or_error srv_file_check_perm(struct srv_req *ctx, lo_interface lib9p_srv_file file, uint8_t action) {
+ assert(ctx);
+ assert(!LO_IS_NULL(file));
+ assert(action);
+
+ struct lib9p_srv_stat stat;
+ error err = LO_CALL(file, stat, ctx, &stat);
+ if (!ERROR_IS_NULL(err))
+ return ERROR_NEW_ERR(ok, err);
+ lib9p_srv_stat_assert(&stat);
+ return ERROR_NEW_VAL(ok, srv_stat_check_perm(ctx, &stat, action));
+}
+
+[[maybe_unused]]
+static struct lib9p_srv_userid *srv_userid_new(struct lib9p_s name
+#if CONFIG_9P_ENABLE_9P2000_u || CONFIG_9P_ENABLE_9P2000_L
+ , lib9p_nuid_t num
+#endif
+ ) {
+ struct lib9p_srv_userid *ret = (void *)heap_alloc(sizeof(struct lib9p_srv_userid) + name.len, char);
+ void *end_dat = (void *)&ret[1];
+ memcpy(end_dat, name.utf8, name.len);
+#if CONFIG_9P_ENABLE_9P2000_u || CONFIG_9P_ENABLE_9P2000_L
+ ret->num = num;
+#endif
+ ret->name.len = name.len;
+ ret->name.utf8 = end_dat;
+ ret->refcount = 1;
+ return ret;
+}
+#if !(CONFIG_9P_ENABLE_9P2000_u || CONFIG_9P_ENABLE_9P2000_L)
+#define srv_userid_new(name, num) srv_userid_new(name)
+#endif
+
+static struct lib9p_srv_userid *srv_userid_decref(struct lib9p_srv_userid *userid) {
+ assert(userid);
+ assert(userid->refcount);
+ userid->refcount--;
+ if (!userid->refcount)
+ free(userid);
+ return NULL;
+}
+
+static struct lib9p_srv_userid *srv_userid_incref(struct lib9p_srv_userid *userid) {
+ assert(userid);
+ userid->refcount++;
+ return userid;
+}
+
+/**
+ * Ensures that `file` is saved into the pathmap, and increments the
+ * gc_refcount by 1 (for presumptive insertion into the fidmap).
+ * parent_path's gc_refcount is also incremented as appropriate.
+ *
+ * Returns a pointer to the stored pathinfo.
+ */
+[[maybe_unused]]
+static struct srv_pathinfo *srv_path_save(struct srv_req *ctx,
+ lo_interface lib9p_srv_file file,
+ srv_path_t parent_path) {
+ assert(ctx);
+ assert(!LO_IS_NULL(file));
+
+ struct lib9p_qid qid = LO_CALL(file, qid);
+ struct srv_pathinfo *pathinfo = map_load(&ctx->parent_sess->paths, qid.path);
+ if (pathinfo)
+ assert(LO_EQ(pathinfo->file, file));
+ else {
+ pathinfo = map_store(&ctx->parent_sess->paths, qid.path,
+ (struct srv_pathinfo){
+ .file = file,
+ .type = srv_qid_filetype(qid),
+ .parent_dir = parent_path,
+ .gc_refcount = 0,
+ .io_refcount = 0,
+ });
+ assert(pathinfo);
+ if (parent_path != qid.path) {
+ struct srv_pathinfo *parent = map_load(&ctx->parent_sess->paths, parent_path);
+ assert(parent);
+ parent->gc_refcount++;
+ }
+ }
+ pathinfo->gc_refcount++;
+ return pathinfo;
+}
+
+/**
+ * Decrement the path's gc_refcount, and trigger garbage collection as
+ * appropriate.
+ */
+static void srv_path_decref(struct srv_req *ctx, srv_path_t path) {
+ assert(ctx);
+
+ for (;;) {
+ struct srv_pathinfo *pathinfo = map_load(&ctx->parent_sess->paths, path);
+ assert(pathinfo);
+ pathinfo->gc_refcount--;
+ if (pathinfo->gc_refcount)
+ break;
+ srv_path_t parent_path = pathinfo->parent_dir;
+ LO_CALL(pathinfo->file, free);
+ map_del(&ctx->parent_sess->paths, path);
+ if (parent_path == path)
+ break;
+ path = parent_path;
+ }
+}
+
+static error srv_fid_del(struct srv_req *ctx, lib9p_fid_t fid, struct srv_fidinfo *fidinfo, bool remove) {
+ assert(ctx);
+ assert(!ctx->user);
+ assert(fidinfo);
+
+ error err = {};
+
+ if (fidinfo->flags & FIDFLAG_RCLOSE)
+ remove = true;
+ ctx->user = srv_userid_incref(fidinfo->user);
+
+ struct srv_pathinfo *pathinfo = map_load(&ctx->parent_sess->paths, fidinfo->path);
+ assert(pathinfo);
+
+ if (remove)
+ err = LO_CALL(pathinfo->file, remove, ctx);
+
+ if (fidinfo->flags & FIDFLAG_OPEN) {
+ switch (fidinfo->type) {
+ case SRV_FILETYPE_DIR:
+ LO_CALL(fidinfo->dir.io, iofree);
+ break;
+ case SRV_FILETYPE_FILE:
+ LO_CALL(fidinfo->file.io, iofree);
+ break;
+ case SRV_FILETYPE_AUTH:
+ assert_notreached("TODO: auth not yet implemented");
+ break;
+ }
+ pathinfo->io_refcount--;
+ }
+ fidinfo->user = srv_userid_decref(fidinfo->user);
+ srv_path_decref(ctx, fidinfo->path);
+ map_del(&ctx->parent_sess->fids, fid);
+
+ ctx->user = srv_userid_decref(ctx->user);
+
+ return err;
+}
+
+/**
+ * Store fid as pointing to pathinfo. Assumes that
+ * pathinfo->gc_refcount has already been incremented; does *not*
+ * decrement it on failure.
+ */
+[[maybe_unused]]
+static srv_fidinfop_or_error srv_fid_store(struct srv_req *ctx, lib9p_fid_t fid, struct srv_pathinfo *pathinfo, bool overwrite) {
+ assert(ctx);
+ assert(fid != LIB9P_FID_NOFID);
+ assert(pathinfo);
+
+ struct lib9p_qid qid = LO_CALL(pathinfo->file, qid);
+
+ struct srv_fidinfo *old_fidinfo = map_load(&ctx->parent_sess->fids, fid);
+ if (old_fidinfo) {
+ if (overwrite) {
+ /* This should only happen from Twalk; because
+ * directories cannot be RCLOSE and Twalk cannot walk on
+ * FIDs open for I/O, we can skip most of
+ * srv_fid_del(). */
+ assert(old_fidinfo->type == SRV_FILETYPE_DIR);
+ assert(old_fidinfo->flags == 0);
+
+ old_fidinfo->user = srv_userid_decref(old_fidinfo->user);
+ srv_path_decref(ctx, old_fidinfo->path);
+ map_del(&ctx->parent_sess->fids, fid);
+ } else {
+ return ERROR_NEW_ERR(srv_fidinfop, error_new(E_POSIX_EBADF, "FID already in use"));
+ }
+ }
+ struct srv_fidinfo *fidinfo = map_store(&ctx->parent_sess->fids, fid, (struct srv_fidinfo){
+ .path = qid.path,
+ .type = srv_qid_filetype(qid),
+ .user = srv_userid_incref(ctx->user),
+ });
+ assert(fidinfo);
+ return ERROR_NEW_VAL(srv_fidinfop, fidinfo);
+}
/* base utilities *************************************************************/
-#define nonrespond_errorf errorf
+static void srv_msglog(struct srv_req *req, enum lib9p_msg_type typ, void *hostmsg) {
+ struct lib9p_srv *srv = req->parent_sess->parent_conn->parent_srv;
+ if (srv->msglog) {
+ srv->msglog(req, typ, hostmsg);
+ return;
+ }
+ log_infoln(typ % 2 ? "< " : "> ", (lib9p_msg, &req->basectx, typ, hostmsg));
+}
+
+#define srv_nonrespond_error log_errorln
+
+static error srv_write_Rmsg(struct srv_req *req, enum lib9p_msg_type typ, void *host) {
+ assert(req);
+ assert(typ % 2 == 1);
+ assert(host);
+
+ struct lib9p_Rmsg_send_buf net;
+ error err = lib9p_Rmsg_marshal(&req->basectx,
+ typ, host,
+ &net);
+ if (!ERROR_IS_NULL(err))
+ return err;
+
+ srv_msglog(req, typ, host);
-static ssize_t write_Rmsg(struct _lib9p_srv_req *req, struct lib9p_Rmsg_send_buf *resp) {
- ssize_t r;
cr_mutex_lock(&req->parent_sess->parent_conn->writelock);
- r = io_writev(req->parent_sess->parent_conn->fd, resp->iov, resp->iov_cnt);
+ size_t_and_error r = io_writev(req->parent_sess->parent_conn->fd, net.iov, net.iov_cnt);
cr_mutex_unlock(&req->parent_sess->parent_conn->writelock);
- return r;
+ if (!ERROR_IS_NULL(r.err)) {
+ srv_nonrespond_error("write: (", r.size_t, ", ", (error, r.err), ")");
+ error_cleanup(&r.err);
+ }
+ return ERROR_NULL;
}
-static void respond_error(struct _lib9p_srv_req *req) {
-#if CONFIG_9P_ENABLE_9P2000_u
- assert(req->ctx.basectx.err_num);
-#endif
- assert(req->ctx.basectx.err_msg[0]);
+static void srv_respond_error(struct srv_req *req, error err) {
+ assert(!ERROR_IS_NULL(err));
- ssize_t r;
struct lib9p_msg_Rerror host = {
.tag = req->tag,
- .ename = lib9p_strn(req->ctx.basectx.err_msg,
- CONFIG_9P_MAX_ERR_SIZE),
+ .errstr = lib9p_str(error_msg(err)),
#if CONFIG_9P_ENABLE_9P2000_u
- .errno = req->ctx.basectx.err_num,
+ .errnum = libmisc_to_linuxgeneric_errno(err.num),
#endif
};
- struct _srv_sess *sess = req->parent_sess;
+ struct srv_sess *sess = req->parent_sess;
- /* Truncate the error-string if necessary to avoid needing to
- * return LINUX_ERANGE. */
- if (((uint32_t)host.ename.len) + sess->rerror_overhead > sess->max_msg_size)
- host.ename.len = sess->max_msg_size - sess->rerror_overhead;
+ uint32_t overhead = lib9p_version_min_Rerror_size(sess->version);
- struct lib9p_Rmsg_send_buf net;
+ /* Truncate the error-string if necessary to avoid needing to
+ * return E_POSIX_ERANGE. */
+ if (((uint32_t)host.errstr.len) + overhead > sess->max_msg_size)
+ host.errstr.len = sess->max_msg_size - overhead;
- lib9p_Rmsg_marshal(&req->ctx.basectx,
- LIB9P_TYP_Rerror, &host,
- &net);
+ error marshal_err = srv_write_Rmsg(req, LIB9P_TYP_Rerror, &host);
+ assert(ERROR_IS_NULL(marshal_err));
- infof("< %v", lo_box_lib9p_msg_Rerror_as_fmt_formatter(&host));
- r = write_Rmsg(req, &net);
- if (r < 0)
- nonrespond_errorf("write: %s", net_strerror(-r));
+ error_cleanup(&err);
}
/* read coroutine *************************************************************/
-static bool read_exactly(lo_interface net_stream_conn fd, uint8_t *buf, size_t goal, size_t *done) {
+/** Return whether to `break`. */
+static bool srv_read_exactly(lo_interface net_stream_conn fd, uint8_t *buf, size_t goal, size_t *done) {
assert(buf);
assert(goal);
assert(done);
while (*done < goal) {
- ssize_t r = io_read(fd, &buf[*done], goal - *done);
- if (r < 0) {
- nonrespond_errorf("read: %s", net_strerror(-r));
- return true;
- } else if (r == 0) {
- if (*done != 0)
- nonrespond_errorf("read: unexpected EOF");
+ size_t_or_error r = io_read(fd, &buf[*done], goal - *done);
+ if (r.is_err) {
+ if (r.err.num == E_EOF) {
+ if (*done != 0)
+ srv_nonrespond_error("read: unexpected EOF");
+ } else {
+ srv_nonrespond_error("read: ", (error, r.err));
+ }
+ error_cleanup(&r.err);
return true;
}
- *done += r;
+ *done += r.size_t;
}
return false;
}
-static void handle_message(struct _lib9p_srv_req *ctx);
-
-[[noreturn]] void lib9p_srv_read_cr(struct lib9p_srv *srv, lo_interface net_stream_listener listener) {
+void lib9p_srv_accept_and_read_loop(struct lib9p_srv *srv, lo_interface net_stream_listener listener) {
assert(srv);
assert(srv->rootdir);
assert(!LO_IS_NULL(listener));
srv->readers++;
- uint32_t initial_rerror_overhead = lib9p_version_min_msg_size(LIB9P_VER_unknown);
-
for (;;) {
- struct _srv_conn conn = {
- .parent_srv = srv,
- .fd = LO_CALL(listener, accept),
- .reader = cr_getcid(),
- };
- if (LO_IS_NULL(conn.fd)) {
- nonrespond_errorf("accept: error");
+ net_stream_conn_or_error r = LO_CALL(listener, accept);
+ if (r.is_err) {
+ srv_nonrespond_error("accept: ", (error, r.err));
+ error_cleanup(&r.err);
srv->readers--;
if (srv->readers == 0)
while (srv->writers > 0)
- _lib9p_srv_reqch_send_req(&srv->_reqch, NULL);
- cr_exit();
+ cr_rpc_send_req(&srv->reqch, NULL);
+ return;
}
+ lib9p_srv_read(srv, r.net_stream_conn);
+ }
+}
- struct _srv_sess sess = {
- .parent_conn = &conn,
- .version = LIB9P_VER_unknown,
- .max_msg_size = CONFIG_9P_SRV_MAX_MSG_SIZE,
- .rerror_overhead = initial_rerror_overhead,
- .initialized = false,
- };
- for (;;) {
- nextmsg:
- /* Read the message. */
- size_t done = 0;
- uint8_t buf[7];
- if (read_exactly(conn.fd, buf, 4, &done))
- goto close;
- size_t goal = uint32le_decode(buf);
- if (goal < 7) {
- nonrespond_errorf("T-message is impossibly small");
- goto close;
- }
- if (read_exactly(conn.fd, buf, 7, &done))
- goto close;
- struct _lib9p_srv_req req = {
- .parent_sess = &sess,
- .tag = uint16le_decode(&buf[5]),
- .net_bytes = buf,
- .ctx = {
- .basectx = {
- .version = sess.version,
- .max_msg_size = sess.max_msg_size,
- },
- },
- };
- if (goal > sess.max_msg_size) {
- lib9p_errorf(&req.ctx.basectx,
- LINUX_EMSGSIZE, "T-message larger than %s limit (%zu > %"PRIu32")",
- sess.initialized ? "negotiated" : "server",
- goal,
- sess.max_msg_size);
- respond_error(&req);
- goto nextmsg;
- }
- req.net_bytes = malloc(goal);
- assert(req.net_bytes);
- memcpy(req.net_bytes, buf, done);
- if (read_exactly(conn.fd, req.net_bytes, goal, &done)) {
- free(req.net_bytes);
- goto close;
- }
+static void lib9p_srv_worker_Tversion(struct srv_req *ctx);
- /* Handle the message... */
- if (req.net_bytes[4] == LIB9P_TYP_Tversion)
- /* ...in this coroutine for Tversion, */
- handle_message(&req);
- else
- /* ...but usually in another coroutine. */
- _lib9p_srv_reqch_send_req(&srv->_reqch, &req);
+void lib9p_srv_read(struct lib9p_srv *srv, lo_interface net_stream_conn _conn) {
+ assert(srv);
+ assert(srv->rootdir);
+ assert(!LO_IS_NULL(_conn));
+
+ struct srv_conn _conn_ = {
+ .parent_srv = srv,
+ .fd = _conn,
+ .reader = cr_getcid(),
+ };
+ struct srv_conn *conn = &_conn_;
+ struct srv_sess _sess_ = {
+ .parent_conn = conn,
+ .version = LIB9P_VER_uninitialized,
+ .max_msg_size = CONFIG_9P_SRV_MAX_MSG_SIZE,
+ };
+ struct srv_sess *sess = &_sess_;
+ struct srv_req _req_;
+ struct srv_req *req = &_req_;
+
+ for (;;) {
+ /* Read the message. */
+ size_t done = 0;
+ uint8_t buf[7];
+ if (srv_read_exactly(conn->fd, buf, 4, &done))
+ break;
+ size_t goal = uint32le_decode(buf);
+ if (goal < 7) {
+ srv_nonrespond_error("T-message is impossibly small");
+ break;
+ }
+ if (srv_read_exactly(conn->fd, buf, 7, &done))
+ break;
+ *req = (struct srv_req){
+ .basectx = {
+ .version = sess->version,
+ .max_msg_size = sess->max_msg_size,
+ },
+
+ .parent_sess = sess,
+ .tag = uint16le_decode(&buf[5]),
+ .net_bytes = buf,
+ };
+ if (goal > sess->max_msg_size) {
+ srv_respond_error(req, error_new(E_POSIX_EMSGSIZE,
+ "T-message larger than ", sess->version == LIB9P_VER_uninitialized ? "server" : "negotiated", " limit",
+ " (", goal, " > ", sess->max_msg_size, ")"));
+ continue;
}
- close:
- if (sess.reqs.len == 0)
- io_close(conn.fd);
- else {
- io_close_read(conn.fd);
- sess.closing = true;
- cr_pause_and_yield();
- assert(sess.reqs.len == 0);
- io_close_write(conn.fd);
+ req->net_bytes = heap_alloc(goal, uint8_t);
+ memcpy(req->net_bytes, buf, done);
+ if (srv_read_exactly(conn->fd, req->net_bytes, goal, &done)) {
+ free(req->net_bytes);
+ break;
+ }
+
+ /* Handle the message... */
+ if (req->net_bytes[4] == LIB9P_TYP_Tversion)
+ /* ...in this coroutine for Tversion, */
+ lib9p_srv_worker_Tversion(req);
+ else
+ /* ...but usually in another coroutine. */
+ cr_rpc_send_req(&srv->reqch, req);
+ }
+
+ /* Cleanup `conn` and `sess->reqs`. */
+ error err = io_close_read(conn->fd);
+ if (!ERROR_IS_NULL(err)) {
+ srv_nonrespond_error("conn close: ", (error, err));
+ error_cleanup(&err);
+ }
+ if (map_len(&sess->reqs)) {
+ sess->closing = true;
+ cr_pause_and_yield();
+ }
+ assert(map_len(&sess->reqs) == 0);
+ err = io_close_write(conn->fd);
+ if (!ERROR_IS_NULL(err)) {
+ srv_nonrespond_error("conn close: ", (error, err));
+ error_cleanup(&err);
+ }
+ map_free(&sess->reqs);
+
+ /* Cleanup `sess->fids`. */
+ *req = (struct srv_req){
+ .basectx = {
+ .version = sess->version,
+ .max_msg_size = sess->max_msg_size,
+ },
+ .parent_sess = sess,
+ };
+ MAP_FOREACH(&sess->fids, fid, fidinfo) {
+ err = srv_fid_del(req, fid, fidinfo, false);
+ if (!ERROR_IS_NULL(err)) {
+ srv_nonrespond_error("clunk: ", (error, err));
+ error_cleanup(&err);
}
}
+ assert(map_len(&sess->fids) == 0);
+ map_free(&sess->fids);
+
+ /* Cleanup `sess->paths`. */
+ assert(map_len(&sess->paths) == 0);
+ map_free(&sess->paths);
}
/* write coroutine ************************************************************/
-COROUTINE lib9p_srv_write_cr(void *_srv) {
- struct _lib9p_srv_req req;
+void lib9p_srv_worker_loop(struct lib9p_srv *srv) {
+ struct srv_req req;
_lib9p_srv_reqch_req_t rpc_handle;
- struct lib9p_srv *srv = _srv;
assert(srv);
assert(srv->rootdir);
- cr_begin();
srv->writers++;
for (;;) {
/* Receive the request from the reader coroutine. ************/
- rpc_handle = _lib9p_srv_reqch_recv_req(&srv->_reqch);
+ rpc_handle = cr_rpc_recv_req(&srv->reqch);
if (!rpc_handle.req) {
srv->writers--;
- _lib9p_srv_reqch_send_resp(rpc_handle, 0);
- cr_exit();
+ cr_rpc_send_resp(rpc_handle, 0);
+ return;
}
/* Copy the request from the reader coroutine's
* stack to our stack. */
req = *rpc_handle.req;
/* Record that we have it. */
- reqmap_store(&req.parent_sess->reqs, req.tag, &req);
+ struct srv_req **reqpp = map_store(&req.parent_sess->reqs, req.tag, &req);
+ assert(reqpp && *reqpp == &req);
/* Notify the reader coroutine that we're done with
* its data. */
- _lib9p_srv_reqch_send_resp(rpc_handle, 0);
+ cr_rpc_send_resp(rpc_handle, 0);
/* Process the request. **************************************/
- handle_message(&req);
-
- /* Release resources. ****************************************/
- while (_lib9p_srv_flushch_can_send(&req.ctx._flushch))
- _lib9p_srv_flushch_send(&req.ctx._flushch, false);
- reqmap_del(&req.parent_sess->reqs, req.tag);
- if (req.parent_sess->closing && !req.parent_sess->reqs.len)
- cr_unpause(req.parent_sess->parent_conn->reader);
+ lib9p_srv_worker(&req);
}
-
- cr_end();
}
#define _HANDLER_PROTO(typ) \
- static void handle_T##typ(struct _lib9p_srv_req *, \
- struct lib9p_msg_T##typ *, \
- struct lib9p_msg_R##typ *)
+ static void handle_T##typ(struct srv_req *, \
+ struct lib9p_msg_T##typ *)
_HANDLER_PROTO(version);
+#if _LIB9P_ENABLE_stat
_HANDLER_PROTO(auth);
_HANDLER_PROTO(attach);
_HANDLER_PROTO(flush);
@@ -383,195 +634,149 @@ _HANDLER_PROTO(clunk);
_HANDLER_PROTO(remove);
_HANDLER_PROTO(stat);
_HANDLER_PROTO(wstat);
+#endif
+#if CONFIG_9P_ENABLE_9P2000_p9p
+_HANDLER_PROTO(openfd);
+#endif
#if CONFIG_9P_ENABLE_9P2000_e
_HANDLER_PROTO(session);
_HANDLER_PROTO(sread);
_HANDLER_PROTO(swrite);
#endif
-typedef void (*tmessage_handler)(struct _lib9p_srv_req *, void *, void *);
-
-static tmessage_handler tmessage_handlers[0x100] = {
- [LIB9P_TYP_Tversion] = (tmessage_handler)handle_Tversion,
- [LIB9P_TYP_Tauth] = (tmessage_handler)handle_Tauth,
- [LIB9P_TYP_Tattach] = (tmessage_handler)handle_Tattach,
- [LIB9P_TYP_Tflush] = (tmessage_handler)handle_Tflush,
- [LIB9P_TYP_Twalk] = (tmessage_handler)handle_Twalk,
- [LIB9P_TYP_Topen] = (tmessage_handler)handle_Topen,
- [LIB9P_TYP_Tcreate] = (tmessage_handler)handle_Tcreate,
- [LIB9P_TYP_Tread] = (tmessage_handler)handle_Tread,
- [LIB9P_TYP_Twrite] = (tmessage_handler)handle_Twrite,
- [LIB9P_TYP_Tclunk] = (tmessage_handler)handle_Tclunk,
- [LIB9P_TYP_Tremove] = (tmessage_handler)handle_Tremove,
- [LIB9P_TYP_Tstat] = (tmessage_handler)handle_Tstat,
- [LIB9P_TYP_Twstat] = (tmessage_handler)handle_Twstat,
-#if CONFIG_9P_ENABLE_9P2000_e
- [LIB9P_TYP_Tsession] = (tmessage_handler)handle_Tsession,
- [LIB9P_TYP_Tsread] = (tmessage_handler)handle_Tsread,
- [LIB9P_TYP_Tswrite] = (tmessage_handler)handle_Tswrite,
-#endif
-};
-
-static void handle_message(struct _lib9p_srv_req *ctx) {
- uint8_t *host_req = NULL;
- uint8_t host_resp[CONFIG_9P_SRV_MAX_HOSTMSG_SIZE];
+void lib9p_srv_worker(struct srv_req *ctx) {
+ [[gnu::cleanup(heap_cleanup)]] void *host_req = NULL;
- /* Unmarshal it. */
- ssize_t host_size = lib9p_Tmsg_validate(&ctx->ctx.basectx, ctx->net_bytes);
- if (host_size < 0)
- goto write;
- host_req = calloc(1, host_size);
- assert(host_req);
+ /* Unmarshal it. *****************************************************/
+ size_t_or_error r = lib9p_Tmsg_validate(&ctx->basectx, ctx->net_bytes);
+ if (r.is_err) {
+ srv_respond_error(ctx, r.err);
+ goto release;
+ }
+ size_t host_size = r.size_t;
+ host_req = heap_alloc(host_size, char);
enum lib9p_msg_type typ;
- lib9p_Tmsg_unmarshal(&ctx->ctx.basectx, ctx->net_bytes,
- &typ, host_req);
- infof("> %v", lo_box_lib9p_msg_as_fmt_formatter(&ctx->ctx.basectx, typ, host_req));
-
- /* Handle it. */
- tmessage_handlers[typ](ctx, (void *)host_req, (void *)host_resp);
-
- write:
- if (lib9p_ctx_has_error(&ctx->ctx.basectx))
- respond_error(ctx);
- else {
- struct lib9p_Rmsg_send_buf net_resp;
- if (lib9p_Rmsg_marshal(&ctx->ctx.basectx,
- typ+1, host_resp,
- &net_resp))
- goto write;
- infof("< %v", lo_box_lib9p_msg_as_fmt_formatter(&ctx->ctx.basectx, typ+1, &host_resp));
- write_Rmsg(ctx, &net_resp);
+ lib9p_Tmsg_unmarshal(&ctx->basectx, ctx->net_bytes,
+ &typ, host_req);
+ srv_msglog(ctx, typ, host_req);
+
+ /* Handle it. ********************************************************/
+#define CASE(typ) case LIB9P_TYP_##typ: handle_##typ(ctx, (void *)host_req); break
+ LM_PARTIAL_SWITCH (typ) {
+#if _LIB9P_ENABLE_stat
+ CASE(Tauth);
+ CASE(Tattach);
+ CASE(Tflush);
+ CASE(Twalk);
+ CASE(Topen);
+ CASE(Tcreate);
+ CASE(Tread);
+ CASE(Twrite);
+ CASE(Tclunk);
+ CASE(Tremove);
+ CASE(Tstat);
+ CASE(Twstat);
+#endif
+#if CONFIG_9P_ENABLE_9P2000_p9p
+ CASE(Topenfd);
+#endif
+#if CONFIG_9P_ENABLE_9P2000_e
+ CASE(Tsession);
+ CASE(Tsread);
+ CASE(Tswrite);
+#endif
+#undef CASE
+ default:
+ assert_notreached("lib9p_Tmsg_validate() should have rejected unknown typ");
}
- if (host_req)
- free(host_req);
+ assert(ctx->responded);
+
+ /* Release resources. ************************************************/
+ release:
+ map_del(&ctx->parent_sess->reqs, ctx->tag);
+ size_t nwaiters;
+ while ((nwaiters = cr_chan_num_waiters(&ctx->flush_ch))) {
+ cr_chan_send(&ctx->flush_ch, (nwaiters == 1)
+ ? _LIB9P_SRV_FLUSH_RFLUSH
+ : _LIB9P_SRV_FLUSH_SILENT);
+ }
+ if (ctx->parent_sess->closing && !map_len(&ctx->parent_sess->reqs))
+ cr_unpause(ctx->parent_sess->parent_conn->reader);
free(ctx->net_bytes);
}
-#define util_handler_common(ctx, req, resp) do { \
- assert(ctx); \
- assert(req); \
- assert(resp); \
- resp->tag = req->tag; \
- } while (0)
-
-static inline bool srv_util_check_perm(struct _lib9p_srv_req *ctx, struct lib9p_stat *stat, uint8_t action) {
- assert(ctx);
- assert(stat);
- assert(action);
+static void lib9p_srv_worker_Tversion(struct srv_req *ctx) {
+ [[gnu::cleanup(heap_cleanup)]] void *host_req = NULL;
- /* TODO actually check user and group instead of just assuming "other". */
- uint8_t mode = (uint8_t)(stat->file_mode & 07);
-
- return mode & action;
-}
-
-/**
- * Ensures that `file` is saved into the pathmap, and increments the
- * gc_refcount by 1 (for presumptive insertion into the fidmap).
- * parent_path's gc_refcount is also incremented as appropriate.
- *
- * Returns a pointer to the stored pathinfo.
- */
-static inline struct srv_pathinfo *srv_util_pathsave(struct _lib9p_srv_req *ctx,
- lo_interface lib9p_srv_file file,
- srv_path_t parent_path) {
- assert(ctx);
- assert(!LO_IS_NULL(file));
-
- struct lib9p_qid qid = LO_CALL(file, qid);
- struct srv_pathinfo *pathinfo = pathmap_load(&ctx->parent_sess->paths, qid.path);
- if (pathinfo)
- assert(LO_EQ(pathinfo->file, file));
- else {
- pathinfo = pathmap_store(&ctx->parent_sess->paths, qid.path,
- (struct srv_pathinfo){
- .file = file,
- .parent_dir = parent_path,
- .gc_refcount = 0,
- .io_refcount = 0,
- });
- assert(pathinfo);
- if (parent_path != qid.path) {
- struct srv_pathinfo *parent = pathmap_load(&ctx->parent_sess->paths, parent_path);
- assert(parent);
- parent->gc_refcount++;
- }
+ /* Unmarshal it. *****************************************************/
+ size_t_or_error r = lib9p_Tmsg_validate(&ctx->basectx, ctx->net_bytes);
+ if (r.is_err) {
+ srv_respond_error(ctx, r.err);
+ goto release;
}
- pathinfo->gc_refcount++;
- return pathinfo;
-}
-
-/**
- * Decrement the path's gc_refcount, and trigger garbage collection as
- * appropriate.
- */
-static inline void srv_util_pathfree(struct _lib9p_srv_req *ctx, srv_path_t path) {
- assert(ctx);
-
- struct srv_pathinfo *pathinfo = pathmap_load(&ctx->parent_sess->paths, path);
- assert(pathinfo);
- pathinfo->gc_refcount--;
- if (pathinfo->gc_refcount == 0) {
- if (pathinfo->parent_dir != path)
- srv_util_pathfree(ctx, pathinfo->parent_dir);
- LO_CALL(pathinfo->file, free);
- pathmap_del(&ctx->parent_sess->paths, path);
+ size_t host_size = r.size_t;
+ host_req = heap_alloc(host_size, char);
+ enum lib9p_msg_type typ;
+ lib9p_Tmsg_unmarshal(&ctx->basectx, ctx->net_bytes,
+ &typ, host_req);
+ srv_msglog(ctx, typ, host_req);
+
+ /* Handle it. ********************************************************/
+ handle_Tversion(ctx, (void *)host_req);
+ assert(ctx->responded);
+
+ /* Release resources. ************************************************/
+ release:
+ map_del(&ctx->parent_sess->reqs, ctx->tag);
+ size_t nwaiters;
+ while ((nwaiters = cr_chan_num_waiters(&ctx->flush_ch))) {
+ cr_chan_send(&ctx->flush_ch, (nwaiters == 1)
+ ? _LIB9P_SRV_FLUSH_RFLUSH
+ : _LIB9P_SRV_FLUSH_SILENT);
}
+ if (ctx->parent_sess->closing && !map_len(&ctx->parent_sess->reqs))
+ cr_unpause(ctx->parent_sess->parent_conn->reader);
+ free(ctx->net_bytes);
}
-static inline bool srv_util_pathisdir(struct srv_pathinfo *pathinfo) {
- assert(pathinfo);
- return LO_CALL(pathinfo->file, qid).type & LIB9P_QT_DIR;
-}
-
-/**
- * Store fid as pointing to pathinfo. Assumes that
- * pathinfo->gc_refcount has already been incremented; does *not*
- * decrement it on failure.
- */
-static inline struct _srv_fidinfo *srv_util_fidsave(struct _lib9p_srv_req *ctx, lib9p_fid_t fid, struct srv_pathinfo *pathinfo, bool overwrite) {
- assert(ctx);
- assert(fid != LIB9P_FID_NOFID);
- assert(pathinfo);
-
- struct lib9p_qid qid = LO_CALL(pathinfo->file, qid);
-
- struct _srv_fidinfo *fidinfo = fidmap_load(&ctx->parent_sess->fids, fid);
- if (fidinfo) {
- if (overwrite) {
- struct srv_pathinfo *old_pathinfo = pathmap_load(&ctx->parent_sess->paths, fidinfo->path);
- assert(old_pathinfo);
- if (srv_util_pathisdir(old_pathinfo))
- LO_CALL(fidinfo->dir.io, iofree);
- else
- LO_CALL(fidinfo->file.io, iofree);
- srv_util_pathfree(ctx, fidinfo->path);
+static void _srv_respond(struct srv_req *ctx, enum lib9p_msg_type resp_typ, void *host_resp, error err) {
+ assert(!ctx->responded);
+ if (!ERROR_IS_NULL(err)) {
+ if (err.num == E_POSIX_ECANCELED && lib9p_srv_flush_requested(ctx)) {
+ error_cleanup(&err);
} else {
- lib9p_error(&ctx->ctx.basectx,
- LINUX_EBADF, "FID already in use");
- return NULL;
+ error:
+ srv_respond_error(ctx, err);
}
} else {
- fidinfo = fidmap_store(&ctx->parent_sess->fids, fid, (struct _srv_fidinfo){});
- if (!fidinfo) {
- lib9p_error(&ctx->ctx.basectx,
- LINUX_EMFILE, "too many open files");
- return NULL;
- }
+ assert(host_resp);
+ err = srv_write_Rmsg(ctx, resp_typ, host_resp);
+ if (!ERROR_IS_NULL(err))
+ goto error;
}
- *fidinfo = (struct _srv_fidinfo){
- .path = qid.path,
- };
- return fidinfo;
+ ctx->responded = true;
}
+#define srv_respond(CTX, TYP, HOST_RESP, ERR) do { \
+ struct lib9p_msg_R##TYP *_host_resp = HOST_RESP; \
+ _srv_respond(CTX, LIB9P_TYP_R##TYP, _host_resp, ERR); \
+} while (0)
+
+/* handle_T* ******************************************************************/
+#define srv_handler_common_no_err(ctx, typ, req) \
+ assert(ctx); \
+ assert(req); \
+ struct lib9p_msg_T##typ *_typecheck_req [[maybe_unused]] = req; \
+ struct lib9p_msg_R##typ resp = { .tag = ctx->tag }
+#define srv_handler_common(ctx, typ, req) \
+ srv_handler_common_no_err(ctx, typ, req); \
+ error err = {}
-static void handle_Tversion(struct _lib9p_srv_req *ctx,
- struct lib9p_msg_Tversion *req,
- struct lib9p_msg_Rversion *resp) {
- util_handler_common(ctx, req, resp);
+static void handle_Tversion(struct srv_req *ctx,
+ struct lib9p_msg_Tversion *req) {
+ srv_handler_common(ctx, version, req);
- enum lib9p_version version = LIB9P_VER_unknown;
+ enum lib9p_version version = LIB9P_VER_uninitialized;
if (req->version.len >= 6 &&
req->version.utf8[0] == '9' &&
@@ -581,7 +786,14 @@ static void handle_Tversion(struct _lib9p_srv_req *ctx,
'0' <= req->version.utf8[4] && req->version.utf8[4] <= '9' &&
'0' <= req->version.utf8[5] && req->version.utf8[5] <= '9' &&
(req->version.len == 6 || req->version.utf8[6] == '.')) {
+#if CONFIG_9P_ENABLE_9P2000
version = LIB9P_VER_9P2000;
+#endif
+#if CONFIG_9P_ENABLE_9P2000_p9p
+ struct lib9p_srv *srv = ctx->parent_sess->parent_conn->parent_srv;
+ if (srv->type_assert_unix && !LO_IS_NULL(srv->type_assert_unix(ctx->parent_sess->parent_conn->fd)))
+ version = LIB9P_VER_9P2000_p9p;
+#endif
#if CONFIG_9P_ENABLE_9P2000_u
if (lib9p_str_eq(lib9p_str_sliceleft(req->version, 6), lib9p_str(".u")))
version = LIB9P_VER_9P2000_u;
@@ -592,294 +804,321 @@ static void handle_Tversion(struct _lib9p_srv_req *ctx,
#endif
}
- uint32_t min_msg_size = lib9p_version_min_msg_size(version);
+ /* XXX: There are good arguments that min_msg_size should be
+ * something larger than max(rerror.min_size(),
+ * rread.min_size()+1). */
+ uint32_t min_msg_size = _LIB9P_MAX(lib9p_version_min_Rerror_size(ctx->basectx.version),
+ lib9p_version_min_Rread_size(ctx->basectx.version)+1);
if (req->max_msg_size < min_msg_size) {
- lib9p_errorf(&ctx->ctx.basectx,
- LINUX_EDOM, "requested max_msg_size is less than minimum for %s (%"PRIu32" < %"PRIu32")",
- lib9p_version_str(version), req->max_msg_size, min_msg_size);
- return;
+ err = error_new(E_POSIX_EDOM, "requested max_msg_size is less than minimum for ", lib9p_version_str(version),
+ " (", req->max_msg_size, " < ", min_msg_size, ")");
+ goto tversion_return;
}
- resp->version = lib9p_str((char *)lib9p_version_str(version)); /* cast to discard "const" qualifier */
- resp->max_msg_size = (CONFIG_9P_SRV_MAX_MSG_SIZE < req->max_msg_size)
+ resp.version = lib9p_str(lib9p_version_str(version));
+#if CONFIG_9P_ENABLE_9P2000_p9p
+ if (version == LIB9P_VER_9P2000_p9p)
+ resp.version = lib9p_str("9P2000");
+#endif
+ resp.max_msg_size = (CONFIG_9P_SRV_MAX_MSG_SIZE < req->max_msg_size)
? CONFIG_9P_SRV_MAX_MSG_SIZE
: req->max_msg_size;
/* Close the old session. */
- if (ctx->parent_sess->reqs.len) {
+ if (map_len(&ctx->parent_sess->reqs)) {
/* Flush all in-progress requests, and wait for them
* to finish. */
- struct cr_select_arg *list = alloca(sizeof(struct cr_select_arg) * ctx->parent_sess->reqs.len);
- while (ctx->parent_sess->reqs.len) {
- uint16_t tag [[gnu::unused]];
- struct _lib9p_srv_req **reqpp;
+ struct cr_select_arg *args = heap_alloc(map_len(&ctx->parent_sess->reqs), struct cr_select_arg);
+ while (map_len(&ctx->parent_sess->reqs)) {
size_t i = 0;
- bool flushed;
MAP_FOREACH(&ctx->parent_sess->reqs, tag, reqpp) {
- list[i] = CR_SELECT_RECV(&((*reqpp)->ctx._flushch), &flushed);
+ enum _lib9p_srv_flush_result flushed;
+ args[i++] = CR_SELECT_RECV(&((*reqpp)->flush_ch), &flushed);
}
- cr_select_v(i, list);
+ assert(i == map_len(&ctx->parent_sess->reqs));
+ cr_select_v(i, args);
}
+ free(args);
}
- if (ctx->parent_sess->fids.len) {
- /* Close all FIDs. */
- uint32_t fid;
- struct _srv_fidinfo *fidinfo [[gnu::unused]];
- MAP_FOREACH(&ctx->parent_sess->fids, fid, fidinfo) {
- handle_Tclunk(ctx,
- &(struct lib9p_msg_Tclunk){.fid = fid},
- &(struct lib9p_msg_Rclunk){});
+ /* Close all FIDs. */
+ MAP_FOREACH(&ctx->parent_sess->fids, fid, fidinfo) {
+ error fiderr = srv_fid_del(ctx, fid, fidinfo, false);
+ if (!ERROR_IS_NULL(fiderr)) {
+ srv_nonrespond_error("clunk: ", (error, fiderr));
+ error_cleanup(&fiderr);
}
}
/* Replace the old session with the new session. */
ctx->parent_sess->version = version;
- ctx->parent_sess->max_msg_size = resp->max_msg_size;
- ctx->parent_sess->rerror_overhead = min_msg_size;
+ ctx->parent_sess->max_msg_size = resp.max_msg_size;
+
+ tversion_return:
+ srv_respond(ctx, version, &resp, err);
}
-static void handle_Tauth(struct _lib9p_srv_req *ctx,
- struct lib9p_msg_Tauth *req,
- struct lib9p_msg_Rauth *resp) {
- util_handler_common(ctx, req, resp);
+#if _LIB9P_ENABLE_stat
+static void handle_Tauth(struct srv_req *ctx,
+ struct lib9p_msg_Tauth *req) {
+ srv_handler_common(ctx, auth, req);
- ctx->ctx.uid = req->n_uid;
- ctx->ctx.uname = req->uname;
struct lib9p_srv *srv = ctx->parent_sess->parent_conn->parent_srv;
-
if (!srv->auth) {
- lib9p_error(&ctx->ctx.basectx,
- LINUX_EOPNOTSUPP, "authentication not required");
- return;
+ err = error_new(E_POSIX_EOPNOTSUPP, "authentication not required");
+ goto tauth_return;
}
- srv->auth(&ctx->ctx, req->aname);
- lib9p_error(&ctx->ctx.basectx,
- LINUX_EOPNOTSUPP, "TODO: auth not implemented");
+ ctx->user = srv_userid_new(req->uname, req->unum);
+
+ err = srv->auth(ctx, req->aname);
+ if (!ERROR_IS_NULL(err))
+ goto tauth_return;
+
+ err = error_new(E_POSIX_EOPNOTSUPP, "TODO: auth not implemented");
+
+ tauth_return:
+ if (!ERROR_IS_NULL(err) && ctx->user)
+ ctx->user = srv_userid_decref(ctx->user);
+ srv_respond(ctx, auth, &resp, err);
}
-static void handle_Tattach(struct _lib9p_srv_req *ctx,
- struct lib9p_msg_Tattach *req,
- struct lib9p_msg_Rattach *resp) {
- util_handler_common(ctx, req, resp);
+static void handle_Tattach(struct srv_req *ctx,
+ struct lib9p_msg_Tattach *req) {
+ srv_handler_common(ctx, attach, req);
- ctx->ctx.uid = req->n_uid;
- ctx->ctx.uname = req->uname;
- struct lib9p_srv *srv = ctx->parent_sess->parent_conn->parent_srv;
+ if (req->fid == LIB9P_FID_NOFID) {
+ err = error_new(E_POSIX_EBADF, "cannot assign to NOFID");
+ goto tattach_return;
+ }
+ struct lib9p_srv *srv = ctx->parent_sess->parent_conn->parent_srv;
if (srv->auth) {
- /*
- struct lib9p_srv_filehandle *fh = fidmap_get(req->afid);
- if (!fh)
- lib9p_error(&ctx->ctx.basectx,
- LINUX_EACCES, "FID provided as auth-file is not a valid FID");
- else if (fh->type != FH_AUTH)
- lib9p_error(&ctx->ctx.basectx,
- LINUX_EACCES, "FID provided as auth-file is not an auth-file");
- else if (!lib9p_str_eq(fh->data.auth.uname, req->uname))
- lib9p_errorf(&ctx->ctx.basectx,
- LINUX_EACCES, "FID provided as auth-file is for user=\"%.*s\" and cannot be used for user=\"%.*s\"",
- fh->data.auth.uname.len, fh->data.auth.uname.utf8,
- req->uname.len, req->uname.utf8);
- else if (!lib9p_str_eq(fh->data.auth.aname, req->aname))
- lib9p_errorf(&ctx->ctx.basectx,
- LINUX_EACCES, "FID provided as auth-file is for tree=\"%.*s\" and cannot be used for tree=\"%.*s\"",
- fh->data.auth.aname.len, fh->data.auth.aname.utf8,
- req->aname.len, req->aname.utf8);
- else if (!fh->data.auth.authenticated)
- lib9p_error(&ctx->ctx.basectx,
- LINUX_EACCES, "FID provided as auth-file has not completed authentication");
- fh->refcount--;
- if (lib9p_ctx_has_error(&ctx->ctx))
- return;
- */
- lib9p_error(&ctx->ctx.basectx,
- LINUX_EOPNOTSUPP, "TODO: auth not (yet?) implemented");
- return;
+ struct srv_fidinfo *afid = map_load(&ctx->parent_sess->fids, req->afid);
+ if (!afid)
+ err = error_new(E_POSIX_EACCES, "FID provided as auth-file is not a valid FID");
+ else if (afid->type != SRV_FILETYPE_AUTH)
+ err = error_new(E_POSIX_EACCES, "FID provided as auth-file is not an auth-file");
+ else if (!lib9p_str_eq(afid->user->name, req->uname))
+ err = error_new(E_POSIX_EACCES,
+ "FID provided as auth-file is for user=", (qmem, afid->user->name.utf8, afid->user->name.len),
+ " and cannot be used for user=", (qmem, req->uname.utf8, req->uname.len));
+#if CONFIG_9P_ENABLE_9P2000_u || CONFIG_9P_ENABLE_9P2000_L
+ else if (afid->user->num != req->unum)
+ err = error_new(E_POSIX_EACCES,
+ "FID provided as auth-file is for user=", afid->user->num,
+ " and cannot be used for user=", req->unum);
+#endif
+ else if (!lib9p_str_eq(afid->auth.aname, req->aname))
+ err = error_new(E_POSIX_EACCES,
+ "FID provided as auth-file is for tree=", (qmem, afid->auth.aname.utf8, afid->auth.aname.len),
+ " and cannot be used for tree=", (qmem, req->aname.utf8, req->aname.len));
+ else if (!afid->auth.completed)
+ err = error_new(E_POSIX_EACCES, "FID provided as auth-file has not completed authentication");
+ if (!ERROR_IS_NULL(err))
+ goto tattach_return;
+ ctx->user = srv_userid_incref(afid->user);
} else {
if (req->afid != LIB9P_FID_NOFID) {
- lib9p_error(&ctx->ctx.basectx,
- LINUX_EACCES, "FID provided as auth-file, but no auth-file is required");
- return;
+ err = error_new(E_POSIX_EACCES, "FID provided as auth-file, but no auth-file is required");
+ goto tattach_return;
}
- }
-
- if (req->fid == LIB9P_FID_NOFID) {
- lib9p_error(&ctx->ctx.basectx,
- LINUX_EBADF, "cannot assign to NOFID");
- return;
+ ctx->user = srv_userid_new(req->uname, req->unum);
}
/* 1. File object */
- lo_interface lib9p_srv_file root_file = srv->rootdir(&ctx->ctx, req->aname);
- assert(LO_IS_NULL(root_file) == lib9p_ctx_has_error(&ctx->ctx.basectx));
- if (lib9p_ctx_has_error(&ctx->ctx.basectx))
- return;
+ lib9p_srv_file_or_error root_file_r = srv->rootdir(ctx, req->aname);
+ if (root_file_r.is_err) {
+ err = root_file_r.err;
+ goto tattach_return;
+ }
+ lo_interface lib9p_srv_file root_file = root_file_r.lib9p_srv_file;
struct lib9p_qid root_qid = LO_CALL(root_file, qid);
- assert(root_qid.type & LIB9P_QT_DIR);
+ assert(srv_qid_filetype(root_qid) == SRV_FILETYPE_DIR);
/* 2. pathinfo */
- struct srv_pathinfo *root_pathinfo = srv_util_pathsave(ctx, root_file, root_qid.path);
+ struct srv_pathinfo *root_pathinfo = srv_path_save(ctx, root_file, root_qid.path);
/* 3. fidinfo */
- if (!srv_util_fidsave(ctx, req->fid, root_pathinfo, false)) {
- srv_util_pathfree(ctx, root_qid.path);
- return;
+ srv_fidinfop_or_error fidinfo = srv_fid_store(ctx, req->fid, root_pathinfo, false);
+ if (fidinfo.is_err) {
+ err = fidinfo.err;
+ srv_path_decref(ctx, root_qid.path);
+ goto tattach_return;
}
- resp->qid = root_qid;
- return;
+ resp.qid = root_qid;
+ tattach_return:
+ if (ctx->user)
+ ctx->user = srv_userid_decref(ctx->user);
+ srv_respond(ctx, attach, &resp, err);
}
-static void handle_Tflush(struct _lib9p_srv_req *ctx,
- struct lib9p_msg_Tflush *req,
- struct lib9p_msg_Rflush *resp) {
- util_handler_common(ctx, req, resp);
-
- struct _lib9p_srv_req **oldreqp = reqmap_load(&ctx->parent_sess->reqs, req->oldtag);
- if (oldreqp)
- _lib9p_srv_flushch_recv(&((*oldreqp)->ctx._flushch));
+static void handle_Tflush(struct srv_req *ctx,
+ struct lib9p_msg_Tflush *req) {
+ srv_handler_common(ctx, flush, req);
+
+ struct srv_req **oldreqp = map_load(&ctx->parent_sess->reqs, req->oldtag);
+ if (oldreqp) {
+ struct srv_req *oldreq = *oldreqp;
+ enum _lib9p_srv_flush_result res = _LIB9P_SRV_FLUSH_RFLUSH;
+ switch (cr_select_l(CR_SELECT_RECV(&oldreq->flush_ch, &res),
+ CR_SELECT_SEND(&ctx->flush_ch, &res))) {
+ case 0: /* original request returned */
+ req_debug("original request (tag=", req->oldtag, ") returned");
+ if (res == _LIB9P_SRV_FLUSH_SILENT) {
+ ctx->responded = true;
+ return;
+ }
+ break;
+ case 1: /* flush itself got flushed */
+ ctx->responded = true;
+ return;
+ }
+ }
+ srv_respond(ctx, flush, &resp, err);
}
-static void handle_Twalk(struct _lib9p_srv_req *ctx,
- struct lib9p_msg_Twalk *req,
- struct lib9p_msg_Rwalk *resp) {
- util_handler_common(ctx, req, resp);
+static void handle_Twalk(struct srv_req *ctx,
+ struct lib9p_msg_Twalk *req) {
+ srv_handler_common(ctx, walk, req);
if (req->newfid == LIB9P_FID_NOFID) {
- lib9p_error(&ctx->ctx.basectx,
- LINUX_EBADF, "cannot assign to NOFID");
- return;
+ err = error_new(E_POSIX_EBADF, "cannot assign to NOFID");
+ goto twalk_return;
}
- struct _srv_fidinfo *fidinfo = fidmap_load(&ctx->parent_sess->fids, req->fid);
+ struct srv_fidinfo *fidinfo = map_load(&ctx->parent_sess->fids, req->fid);
if (!fidinfo) {
- lib9p_errorf(&ctx->ctx.basectx,
- LINUX_EBADF, "bad file number %"PRIu32, req->fid);
- return;
+ err = error_new(E_POSIX_EBADF, "bad file number ", req->fid);
+ goto twalk_return;
}
+ if (fidinfo->flags & FIDFLAG_OPEN) {
+ err = error_new(E_POSIX_EALREADY, "cannot walk on FID open for I/O");
+ goto twalk_return;
+ }
+ ctx->user = srv_userid_incref(fidinfo->user);
- struct srv_pathinfo *pathinfo = pathmap_load(&ctx->parent_sess->paths, fidinfo->path);
+ struct srv_pathinfo *pathinfo = map_load(&ctx->parent_sess->paths, fidinfo->path);
assert(pathinfo);
pathinfo->gc_refcount++;
- resp->wqid = (struct lib9p_qid *)(&resp[1]);
- for (resp->nwqid = 0; resp->nwqid < req->nwname; resp->nwqid++) {
+ resp.wqid = heap_alloc(req->nwname, struct lib9p_qid);
+ for (resp.nwqid = 0; resp.nwqid < req->nwname; resp.nwqid++) {
+ if (pathinfo->type != SRV_FILETYPE_DIR) {
+ err = error_new(E_POSIX_ENOTDIR, "not a directory");
+ break;
+ }
+
struct srv_pathinfo *new_pathinfo;
- if (lib9p_str_eq(req->wname[resp->nwqid], lib9p_str(".."))) {
- new_pathinfo = pathmap_load(&ctx->parent_sess->paths, pathinfo->parent_dir);
+ if (lib9p_str_eq(req->wname[resp.nwqid], lib9p_str(".."))) {
+ new_pathinfo = map_load(&ctx->parent_sess->paths, pathinfo->parent_dir);
assert(new_pathinfo);
new_pathinfo->gc_refcount++;
} else {
- if (!srv_util_pathisdir(pathinfo)) {
- lib9p_error(&ctx->ctx.basectx,
- LINUX_ENOTDIR, "not a directory");
+ lib9p_srv_file_or_error member_file = LO_CALL(pathinfo->file, dwalk, ctx, req->wname[resp.nwqid]);
+ if (member_file.is_err) {
+ err = member_file.err;
break;
}
-
- lo_interface lib9p_srv_file member_file = LO_CALL(pathinfo->file, dwalk, &ctx->ctx, req->wname[resp->nwqid]);
- assert(LO_IS_NULL(member_file) == lib9p_ctx_has_error(&ctx->ctx.basectx));
- if (lib9p_ctx_has_error(&ctx->ctx.basectx))
- break;
- new_pathinfo = srv_util_pathsave(ctx, member_file, LO_CALL(pathinfo->file, qid).path);
+ new_pathinfo = srv_path_save(ctx, member_file.lib9p_srv_file, LO_CALL(pathinfo->file, qid).path);
+ assert(new_pathinfo);
}
- if (srv_util_pathisdir(new_pathinfo)) {
- struct lib9p_stat stat = LO_CALL(new_pathinfo->file, stat, &ctx->ctx);
- if (lib9p_ctx_has_error(&ctx->ctx.basectx))
+ if (new_pathinfo->type == SRV_FILETYPE_DIR) {
+ ok_or_error have_perm = srv_file_check_perm(ctx, new_pathinfo->file, 0b001);
+ if (have_perm.is_err) {
+ err = have_perm.err;
break;
- lib9p_stat_assert(stat);
- if (!srv_util_check_perm(ctx, &stat, 0b001)) {
- lib9p_error(&ctx->ctx.basectx,
- LINUX_EACCES, "you do not have execute permission on that directory");
- srv_util_pathfree(ctx, LO_CALL(new_pathinfo->file, qid).path);
+ } else if (!have_perm.ok) {
+ err = error_new(E_POSIX_EACCES, "you do not have execute permission on that directory");
+ srv_path_decref(ctx, LO_CALL(new_pathinfo->file, qid).path);
break;
}
}
- resp->wqid[resp->nwqid] = LO_CALL(new_pathinfo->file, qid);
+ resp.wqid[resp.nwqid] = LO_CALL(new_pathinfo->file, qid);
- srv_util_pathfree(ctx, LO_CALL(pathinfo->file, qid).path);
+ srv_path_decref(ctx, LO_CALL(pathinfo->file, qid).path);
pathinfo = new_pathinfo;
}
- if (resp->nwqid == req->nwname) {
- if (req->newfid == req->fid) {
- if (srv_util_pathisdir(pathinfo))
- LO_CALL(fidinfo->dir.io, iofree);
- else
- LO_CALL(fidinfo->file.io, iofree);
- fidinfo->flags = 0;
+ if (resp.nwqid == req->nwname) {
+ srv_fidinfop_or_error fidinfo = srv_fid_store(ctx, req->newfid, pathinfo, req->newfid == req->fid);
+ if (fidinfo.is_err) {
+ err = fidinfo.err;
+ srv_path_decref(ctx, LO_CALL(pathinfo->file, qid).path);
}
- if (!srv_util_fidsave(ctx, req->newfid, pathinfo, req->newfid == req->fid))
- srv_util_pathfree(ctx, LO_CALL(pathinfo->file, qid).path);
} else {
- assert(lib9p_ctx_has_error(&ctx->ctx.basectx));
- srv_util_pathfree(ctx, LO_CALL(pathinfo->file, qid).path);
- if (resp->nwqid > 0)
- lib9p_ctx_clear_error(&ctx->ctx.basectx);
+ assert(!ERROR_IS_NULL(err));
+ srv_path_decref(ctx, LO_CALL(pathinfo->file, qid).path);
+ if (resp.nwqid > 0)
+ error_cleanup(&err);
}
+ twalk_return:
+ if (ctx->user)
+ ctx->user = srv_userid_decref(ctx->user);
+ srv_respond(ctx, walk, &resp, err);
+ if (resp.wqid)
+ free(resp.wqid);
}
-static void handle_Topen(struct _lib9p_srv_req *ctx,
- struct lib9p_msg_Topen *req,
- struct lib9p_msg_Ropen *resp) {
- util_handler_common(ctx, req, resp);
+static void handle_Topen(struct srv_req *ctx,
+ struct lib9p_msg_Topen *req) {
+ srv_handler_common(ctx, open, req);
/* Check that the FID is valid for this. */
- struct _srv_fidinfo *fidinfo = fidmap_load(&ctx->parent_sess->fids, req->fid);
+ struct srv_fidinfo *fidinfo = map_load(&ctx->parent_sess->fids, req->fid);
if (!fidinfo) {
- lib9p_errorf(&ctx->ctx.basectx,
- LINUX_EBADF, "bad file number %"PRIu32, req->fid);
- return;
+ err = error_new(E_POSIX_EBADF, "bad file number ", req->fid);
+ goto topen_return;
}
if (fidinfo->flags & FIDFLAG_OPEN) {
- lib9p_error(&ctx->ctx.basectx,
- LINUX_EALREADY, "FID is already open");
- return;
+ err = error_new(E_POSIX_EALREADY, "FID is already open");
+ goto topen_return;
}
- struct srv_pathinfo *pathinfo = pathmap_load(&ctx->parent_sess->paths, fidinfo->path);
- assert(pathinfo);
- if (srv_util_pathisdir(pathinfo)) {
+ if (fidinfo->type == SRV_FILETYPE_DIR) {
if ( ((req->mode & LIB9P_O_MODE_MASK) != LIB9P_O_MODE_READ) ||
(req->mode & LIB9P_O_TRUNC) ||
(req->mode & LIB9P_O_RCLOSE) ) {
- lib9p_error(&ctx->ctx.basectx,
- LINUX_EISDIR, "directories cannot be written, executed, truncated, or removed-on-close");
- return;
+ err = error_new(E_POSIX_EISDIR, "directories cannot be written, executed, truncated, or removed-on-close");
+ goto topen_return;
}
}
+ ctx->user = srv_userid_incref(fidinfo->user);
/* Variables. */
- lib9p_o_t reqmode = req->mode;
- uint8_t fidflags = fidinfo->flags;
+ lib9p_o_t reqmode = req->mode;
+ uint8_t fidflags = fidinfo->flags;
+ struct srv_pathinfo *pathinfo = map_load(&ctx->parent_sess->paths, fidinfo->path);
+ assert(pathinfo);
/* Check permissions. */
if (reqmode & LIB9P_O_RCLOSE) {
- struct srv_pathinfo *parent = pathmap_load(&ctx->parent_sess->paths, pathinfo->parent_dir);
+ struct srv_pathinfo *parent = map_load(&ctx->parent_sess->paths, pathinfo->parent_dir);
assert(parent);
- struct lib9p_stat parent_stat = LO_CALL(parent->file, stat, &ctx->ctx);
- if (lib9p_ctx_has_error(&ctx->ctx.basectx))
- return;
- lib9p_stat_assert(parent_stat);
- if (!srv_util_check_perm(ctx, &parent_stat, 0b010)) {
- lib9p_error(&ctx->ctx.basectx,
- LINUX_EACCES, "permission denied to remove-on-close");
- return;
+ ok_or_error have_perm = srv_file_check_perm(ctx, parent->file, 0b010);
+ if (have_perm.is_err) {
+ err = have_perm.err;
+ goto topen_return;
+ } else if (!have_perm.ok) {
+ err = error_new(E_POSIX_EACCES, "permission denied to remove-on-close");
+ goto topen_return;
}
fidflags |= FIDFLAG_RCLOSE;
}
- struct lib9p_stat stat = LO_CALL(pathinfo->file, stat, &ctx->ctx);
- if (lib9p_ctx_has_error(&ctx->ctx.basectx))
- return;
- lib9p_stat_assert(stat);
- if ((stat.file_mode & LIB9P_DM_EXCL) && pathinfo->io_refcount) {
- lib9p_error(&ctx->ctx.basectx,
- LINUX_EEXIST, "exclusive file is already opened");
- return;
+ struct lib9p_srv_stat stat;
+ err = LO_CALL(pathinfo->file, stat, ctx, &stat);
+ if (!ERROR_IS_NULL(err))
+ goto topen_return;
+ lib9p_srv_stat_assert(&stat);
+ if ((stat.mode & LIB9P_DM_EXCL) && pathinfo->io_refcount) {
+ err = error_new(E_POSIX_EEXIST, "exclusive file is already opened");
+ goto topen_return;
}
- if (stat.file_mode & LIB9P_DM_APPEND)
+ if (stat.mode & LIB9P_DM_APPEND) {
+ fidflags |= FIDFLAG_APPEND;
reqmode = reqmode & ~LIB9P_O_TRUNC;
+ }
uint8_t perm_bits = 0;
bool rd = false, wr = false;
switch (reqmode & LIB9P_O_MODE_MASK) {
@@ -900,32 +1139,47 @@ static void handle_Topen(struct _lib9p_srv_req *ctx,
rd = true;
break;
}
- if (!srv_util_check_perm(ctx, &stat, perm_bits)) {
- lib9p_error(&ctx->ctx.basectx,
- LINUX_EACCES, "permission denied");
+ if (!srv_stat_check_perm(ctx, &stat, perm_bits)) {
+ err = error_new(E_POSIX_EACCES, "permission denied");
+ goto topen_return;
}
/* Actually make the call. */
uint32_t iounit;
struct lib9p_qid qid;
- if (srv_util_pathisdir(pathinfo)) {
- fidinfo->dir.io = LO_CALL(pathinfo->file, dopen, &ctx->ctx);
- assert(LO_IS_NULL(fidinfo->dir.io) == lib9p_ctx_has_error(&ctx->ctx.basectx));
- if (lib9p_ctx_has_error(&ctx->ctx.basectx))
- return;
+ switch (pathinfo->type) {
+ case SRV_FILETYPE_DIR:
+ lib9p_srv_dio_or_error dio_r =
+ LO_CALL(pathinfo->file, dopen, ctx);
+ if (dio_r.is_err) {
+ err = dio_r.err;
+ goto topen_return;
+ }
+ fidinfo->dir.io = dio_r.lib9p_srv_dio;
fidinfo->dir.idx = 0;
fidinfo->dir.off = 0;
- qid = LO_CALL(fidinfo->dir.io, qid);
+ qid = LO_CALL(fidinfo->dir.io, ioqid);
iounit = 0;
- } else {
- fidinfo->file.io = LO_CALL(pathinfo->file, fopen, &ctx->ctx,
- rd, wr,
- reqmode & LIB9P_O_TRUNC);
- assert(LO_IS_NULL(fidinfo->file.io) == lib9p_ctx_has_error(&ctx->ctx.basectx));
- if (lib9p_ctx_has_error(&ctx->ctx.basectx))
- return;
- qid = LO_CALL(fidinfo->file.io, qid);
+ break;
+ case SRV_FILETYPE_FILE:
+ lib9p_srv_fio_or_error fio_r =
+ LO_CALL(pathinfo->file, fopen, ctx,
+ rd, wr,
+ reqmode & LIB9P_O_TRUNC);
+ if (fio_r.is_err) {
+ err = fio_r.err;
+ goto topen_return;
+ }
+ fidinfo->file.io = fio_r.lib9p_srv_fio;
+ qid = LO_CALL(fidinfo->file.io, ioqid);
iounit = LO_CALL(fidinfo->file.io, iounit);
+ break;
+ case SRV_FILETYPE_AUTH:
+ assert_notreached("TODO: auth not yet implemented");
+ break;
+ default:
+ assert_notreached("invalid srv_filetype");
+ break;
}
/* Success. */
@@ -935,218 +1189,413 @@ static void handle_Topen(struct _lib9p_srv_req *ctx,
fidflags |= FIDFLAG_OPEN_W;
pathinfo->io_refcount++;
fidinfo->flags = fidflags;
- resp->qid = qid;
- resp->iounit = iounit;
+ resp.qid = qid;
+ resp.iounit = iounit;
+ topen_return:
+ if (ctx->user)
+ ctx->user = srv_userid_decref(ctx->user);
+ srv_respond(ctx, open, &resp, err);
}
-static void handle_Tcreate(struct _lib9p_srv_req *ctx,
- struct lib9p_msg_Tcreate *req,
- struct lib9p_msg_Rcreate *resp) {
- util_handler_common(ctx, req, resp);
+static void handle_Tcreate(struct srv_req *ctx,
+ struct lib9p_msg_Tcreate *req) {
+ srv_handler_common(ctx, create, req);
- lib9p_error(&ctx->ctx.basectx,
- LINUX_EOPNOTSUPP, "create not (yet?) implemented");
+ err = error_new(E_POSIX_EOPNOTSUPP, "create not (yet?) implemented");
+
+ srv_respond(ctx, create, &resp, err);
}
-static void handle_Tread(struct _lib9p_srv_req *ctx,
- struct lib9p_msg_Tread *req,
- struct lib9p_msg_Rread *resp) {
- util_handler_common(ctx, req, resp);
+static void srv_stat_to_net_stat(struct lib9p_stat *out, const struct lib9p_srv_stat *in) {
+ lib9p_srv_stat_assert(in);
+ *out = (struct lib9p_stat){
+ .qid = in->qid,
+ .mode = in->mode,
+ .atime = in->atime_sec,
+ .mtime = in->mtime_sec,
+ .length = in->size,
+ .name = in->name,
+ .owner_uname = in->owner_uid.name,
+ .owner_gname = in->owner_gid.name,
+ .last_modifier_uname = in->last_modifier_uid.name,
+#if CONFIG_9P_ENABLE_9P2000_u
+ .owner_unum = in->owner_uid.num,
+ .owner_gnum = in->owner_gid.num,
+ .last_modifier_unum = in->last_modifier_uid.num,
+ .extension = in->extension,
+#endif
+ };
+}
+
+static void handle_read_file(struct srv_req *ctx, struct srv_fidinfo *fidinfo, uint64_t offset, uint32_t count);
+static void handle_read_dir(struct srv_req *ctx, struct srv_fidinfo *fidinfo, uint64_t offset, uint32_t count);
+
+static void handle_Tread(struct srv_req *ctx,
+ struct lib9p_msg_Tread *req) {
+ assert(ctx);
+ assert(req);
+
+ /* TODO: serialize simultaneous reads to the same FID */
+
+ /* req->count <= CONFIG_9P_SRV_MAX_MSG_SIZE <= CONFIG_9P_SRV_MAX_HOSTMSG_SIZE <= SIZE_MAX */
+ assert(req->count <= SIZE_MAX);
+ /* req->offset is u64, uoff is u64 */
+ static_assert(req->offset <= UOFF_MAX);
+
+ if (req->count > ctx->basectx.max_msg_size - lib9p_version_min_Rread_size(ctx->basectx.version))
+ req->count = ctx->basectx.max_msg_size - lib9p_version_min_Rread_size(ctx->basectx.version);
/* Check that the FID is valid for this. */
- struct _srv_fidinfo *fidinfo = fidmap_load(&ctx->parent_sess->fids, req->fid);
+ struct srv_fidinfo *fidinfo = map_load(&ctx->parent_sess->fids, req->fid);
if (!fidinfo) {
- lib9p_errorf(&ctx->ctx.basectx,
- LINUX_EBADF, "bad file number %"PRIu32, req->fid);
+ srv_respond(ctx, read, NULL, error_new(E_POSIX_EBADF, "bad file number ", req->fid));
return;
}
if (!(fidinfo->flags & FIDFLAG_OPEN_R)) {
- lib9p_error(&ctx->ctx.basectx,
- LINUX_EINVAL, "FID not open for reading");
+ srv_respond(ctx, read, NULL, error_new(E_POSIX_EINVAL, "FID not open for reading"));
return;
}
- /* Variables. */
- struct srv_pathinfo *pathinfo = pathmap_load(&ctx->parent_sess->paths, fidinfo->path);
- assert(pathinfo);
-
/* Do it. */
- if (srv_util_pathisdir(pathinfo)) {
- /* Translate byte-offset to object-index. */
- size_t idx;
- if (req->offset == 0)
- idx = 0;
- else if (req->offset == fidinfo->dir.off)
- idx = fidinfo->dir.idx;
- else {
- lib9p_errorf(&ctx->ctx.basectx,
- LINUX_EINVAL, "invalid offset (must be 0 or %"PRIu64"): %"PRIu64,
- fidinfo->dir.off, req->offset);
- return;
+ ctx->user = srv_userid_incref(fidinfo->user);
+ switch (fidinfo->type) {
+ case SRV_FILETYPE_DIR:
+#if _LIB9P_ENABLE_stat
+ handle_read_dir(ctx, fidinfo, req->offset, req->count);
+#else
+ assert_notreached("Tread for directory on protocol version without that");
+#endif
+ break;
+ case SRV_FILETYPE_FILE:
+ handle_read_file(ctx, fidinfo, req->offset, req->count);
+ break;
+ case SRV_FILETYPE_AUTH:
+ assert_notreached("TODO: auth not yet implemented");
+ break;
+ }
+ ctx->user = srv_userid_decref(ctx->user);
+}
+
+struct rread_writer {
+ struct srv_req *ctx;
+ size_t count;
+ bool written;
+
+};
+LO_IMPLEMENTATION_STATIC(io_writer, struct rread_writer, rread);
+
+static size_t_and_error rread_writev(struct rread_writer *self, const struct wr_iovec *iov, int iovcnt) {
+ assert(self);
+ assert(!self->written);
+ assert(iovcnt == 1);
+ assert(iov);
+ assert(iov->iov_len <= self->count);
+
+ struct lib9p_msg_Rread resp = {
+ .tag = self->ctx->tag,
+ .count = iov->iov_len,
+ .data = iov->iov_write_from,
+ };
+
+ srv_respond(self->ctx, read, &resp, ERROR_NULL);
+
+ self->written = true;
+ return ERROR_AND(size_t, iov->iov_len, ERROR_NULL);
+}
+
+static void handle_read_file(struct srv_req *ctx, struct srv_fidinfo *fidinfo, uint64_t offset, uint32_t count) {
+ struct rread_writer _writer = {
+ .ctx = ctx,
+ .count = (size_t) count,
+ .written = false,
+ };
+ lo_interface io_writer writer = LO_BOX(io_writer, &_writer);
+ error err = LO_CALL(fidinfo->file.io, pread, ctx, writer, offset, count);
+ assert(ERROR_IS_NULL(err) == _writer.written);
+ if (!ERROR_IS_NULL(err))
+ srv_respond(ctx, read, NULL, err);
+}
+
+#if _LIB9P_ENABLE_stat
+
+static error stat_and_convert(struct lib9p_stat *out, struct srv_req *ctx, lo_interface lib9p_srv_file file) {
+ struct lib9p_srv_stat hoststat;
+ error err = LO_CALL(file, stat, ctx, &hoststat);
+ if (!ERROR_IS_NULL(err))
+ return err;
+
+ srv_stat_to_net_stat(out, &hoststat);
+ return ERROR_NULL;
+}
+
+static uint32_t_or_error stat_and_encode(struct srv_req *ctx, lo_interface lib9p_srv_file file, void *out, size_t out_len) {
+ struct lib9p_srv_stat hoststat;
+ struct lib9p_stat netstat;
+ error err;
+
+ err = LO_CALL(file, stat, ctx, &hoststat);
+
+ if (!ERROR_IS_NULL(err))
+ return ERROR_NEW_ERR(uint32_t, err);
+ srv_stat_to_net_stat(&netstat, &hoststat);
+ return ERROR_NEW_VAL(uint32_t, lib9p_stat_marshal(&ctx->basectx, out_len, &netstat, (uint8_t *)out));
+}
+
+static void handle_read_dir(struct srv_req *ctx, struct srv_fidinfo *fidinfo, uint64_t offset, uint32_t count) {
+ /* Seek. */
+ if (offset == 0) {
+ fidinfo->dir.idx = 0;
+ fidinfo->dir.off = 0;
+ fidinfo->dir.buffered_dirent = (struct lib9p_srv_dirent){};
+ } else if (offset != fidinfo->dir.off) {
+ srv_respond(ctx, read, NULL, error_new(E_POSIX_EINVAL, "invalid offset (must be 0 or ", fidinfo->dir.off, "): ", offset));
+ return;
+ }
+
+ /* Allocate. */
+ [[gnu::cleanup(heap_cleanup)]] char *heap = NULL;
+ struct lib9p_msg_Rread resp = {
+ .tag = ctx->tag,
+ .data = heap = heap_alloc(count, char),
+ .count = 0,
+ };
+
+ /* Read. */
+ struct srv_pathinfo *dir_pathinfo = NULL;
+ for (;;) {
+ /* 1. Call ->dread() to get `member_dirent`. */
+ struct lib9p_srv_dirent member_dirent;
+ if (fidinfo->dir.buffered_dirent.name.len) {
+ member_dirent = fidinfo->dir.buffered_dirent;
+ } else {
+ lib9p_srv_dirent_or_error r = LO_CALL(fidinfo->dir.io, dread, ctx, fidinfo->dir.idx);
+ if (r.is_err) {
+ if (resp.count) {
+ /* Just do a short-read; discard the error. */
+ error_cleanup(&r.err);
+ break;
+ }
+ srv_respond(ctx, read, NULL, r.err);
+ return;
+ }
+ member_dirent = r.lib9p_srv_dirent;
}
- /* Do it. */
- resp->data = (char *)(&resp[1]);
- size_t num = LO_CALL(fidinfo->dir.io, dread, &ctx->ctx, (uint8_t *)resp->data, req->count, idx);
- /* Translate object-count back to byte-count. */
- uint32_t len = 0;
- for (size_t i = 0; i < num; i++) {
- uint32_t i_len;
- lib9p_stat_validate(&ctx->ctx.basectx, req->count, &((uint8_t *)resp->data)[len], &i_len, NULL);
- len += i_len;
+ if (!member_dirent.name.len)
+ /* end-of-directory */
+ break;
+
+ /* 2. Call ->dwalk() to get the `member_file` object to call ->stat() on. */
+ lo_interface lib9p_srv_file member_file;
+ bool free_member_file;
+ {
+ struct srv_pathinfo *member_pathinfo = map_load(&ctx->parent_sess->paths, member_dirent.qid.path);
+ if (member_pathinfo) {
+ member_file = member_pathinfo->file;
+ free_member_file = false;
+ } else {
+ if (!dir_pathinfo)
+ dir_pathinfo = map_load(&ctx->parent_sess->paths, fidinfo->path);
+ assert(dir_pathinfo);
+ lib9p_srv_file_or_error r = LO_CALL(dir_pathinfo->file, dwalk, ctx, member_dirent.name);
+ if (r.is_err) {
+ if (resp.count) {
+ /* Just do a short-read; discard the error. */
+ error_cleanup(&r.err);
+ break;
+ }
+ srv_respond(ctx, read, NULL, r.err);
+ return;
+ }
+ member_file = r.lib9p_srv_file;
+ free_member_file = true;
+ }
}
- resp->count = len;
- /* Remember. */
- fidinfo->dir.idx = idx+num;
- fidinfo->dir.off = req->offset + len;
- } else {
- struct iovec iov;
- LO_CALL(fidinfo->file.io, pread, &ctx->ctx, req->count, req->offset, &iov);
- if (!lib9p_ctx_has_error(&ctx->ctx.basectx)) {
- resp->count = iov.iov_len;
- resp->data = iov.iov_base;
- if (resp->count > req->count)
- resp->count = req->count;
+
+ /* 3. Call ->stat() to get `member_stat``. */
+ /* 4. Encode `member_stat` into `resp.data`/`heap` and increment `resp.count`. */
+ uint32_t_or_error r = stat_and_encode(ctx, member_file, &heap[resp.count], count - resp.count);
+ if (free_member_file)
+ LO_CALL(member_file, free);
+ if (r.is_err) {
+ if (resp.count) {
+ /* Just do a short-read; discard the error. */
+ error_cleanup(&r.err);
+ break;
+ }
+ srv_respond(ctx, read, NULL, r.err);
+ return;
+ }
+ uint32_t nbytes = r.uint32_t;
+ if (!nbytes) {
+ if (resp.count) {
+ /* Just do a short-read; discard the error.
+ * But save the member_dirent for next time. */
+ fidinfo->dir.buffered_dirent = member_dirent;
+ break;
+ }
+ srv_respond(ctx, read, NULL,
+ error_new(E_POSIX_ERANGE, "stat object does not fit into negotiated max message size"));
+ return;
}
+ resp.count += nbytes;
+ fidinfo->dir.idx++;
+ fidinfo->dir.off += nbytes;
+ fidinfo->dir.buffered_dirent = (struct lib9p_srv_dirent){};
}
+ srv_respond(ctx, read, &resp, ERROR_NULL);
}
+#endif
-static void handle_Twrite(struct _lib9p_srv_req *ctx,
- struct lib9p_msg_Twrite *req,
- struct lib9p_msg_Rwrite *resp) {
- util_handler_common(ctx, req, resp);
+static void handle_Twrite(struct srv_req *ctx,
+ struct lib9p_msg_Twrite *req) {
+ srv_handler_common(ctx, write, req);
+
+ /* TODO: serialize simultaneous writes to the same FID */
/* Check that the FID is valid for this. */
- struct _srv_fidinfo *fidinfo = fidmap_load(&ctx->parent_sess->fids, req->fid);
+ struct srv_fidinfo *fidinfo = map_load(&ctx->parent_sess->fids, req->fid);
if (!fidinfo) {
- lib9p_errorf(&ctx->ctx.basectx,
- LINUX_EBADF, "bad file number %"PRIu32, req->fid);
- return;
+ err = error_new(E_POSIX_EBADF, "bad file number ", req->fid);
+ goto twrite_return;
}
if (!(fidinfo->flags & FIDFLAG_OPEN_W)) {
- lib9p_error(&ctx->ctx.basectx,
- LINUX_EINVAL, "FID not open for writing");
- return;
+ err = error_new(E_POSIX_EINVAL, "FID not open for writing");
+ goto twrite_return;
}
-
- /* Variables. */
- struct srv_pathinfo *pathinfo = pathmap_load(&ctx->parent_sess->paths, fidinfo->path);
- assert(pathinfo);
+ if (fidinfo->flags & FIDFLAG_APPEND)
+ req->offset = 0;
/* Do it. */
- resp->count = LO_CALL(fidinfo->file.io, pwrite, &ctx->ctx, req->data, req->count, req->offset);
+ ctx->user = srv_userid_incref(fidinfo->user);
+ uint32_t_or_error count = LO_CALL(fidinfo->file.io, pwrite, ctx, req->data, req->count, req->offset);
+ if (count.is_err)
+ err = count.err;
+ else
+ resp.count = count.uint32_t;
+ twrite_return:
+ if (ctx->user)
+ ctx->user = srv_userid_decref(ctx->user);
+ srv_respond(ctx, write, &resp, err);
}
-static void clunkremove(struct _lib9p_srv_req *ctx, lib9p_fid_t fid, bool remove) {
- struct _srv_fidinfo *fidinfo = fidmap_load(&ctx->parent_sess->fids, fid);
+static void handle_Tclunk(struct srv_req *ctx,
+ struct lib9p_msg_Tclunk *req) {
+ srv_handler_common_no_err(ctx, clunk, req);
+
+ struct srv_fidinfo *fidinfo = map_load(&ctx->parent_sess->fids, req->fid);
if (!fidinfo) {
- lib9p_errorf(&ctx->ctx.basectx,
- LINUX_EBADF, "bad file number %"PRIu32, fid);
+ srv_respond(ctx, clunk, NULL, error_new(E_POSIX_EBADF, "bad file number ", req->fid));
return;
}
- if (fidinfo->flags & FIDFLAG_RCLOSE)
- remove = true;
- struct srv_pathinfo *pathinfo = pathmap_load(&ctx->parent_sess->paths, fidinfo->path);
- assert(pathinfo);
- if (remove) {
- if (pathinfo->parent_dir == fidinfo->path) {
- lib9p_errorf(&ctx->ctx.basectx,
- LINUX_EBUSY, "cannot remove root");
- goto clunk;
- }
- struct srv_pathinfo *parent = pathmap_load(&ctx->parent_sess->paths, pathinfo->parent_dir);
- assert(parent);
- struct lib9p_stat parent_stat = LO_CALL(parent->file, stat, &ctx->ctx);
- if (!srv_util_check_perm(ctx, &parent_stat, 0b010)) {
- lib9p_error(&ctx->ctx.basectx,
- LINUX_EACCES, "you do not have write permission on the parent directory");
- goto clunk;
- }
- LO_CALL(pathinfo->file, remove, &ctx->ctx);
- }
-
- clunk:
- if (fidinfo->flags & FIDFLAG_OPEN) {
- if (srv_util_pathisdir(pathinfo))
- LO_CALL(fidinfo->dir.io, iofree);
- else
- LO_CALL(fidinfo->file.io, iofree);
- pathinfo->io_refcount--;
- }
- srv_util_pathfree(ctx, LO_CALL(pathinfo->file, qid).path);
- fidmap_del(&ctx->parent_sess->fids, fid);
+ srv_respond(ctx, clunk, &resp, ERROR_NULL);
+ /* Yes, don't actually perform the clunk until *after* we send Rclunk. */
+ srv_fid_del(ctx, req->fid, fidinfo, false);
}
-static void handle_Tclunk(struct _lib9p_srv_req *ctx,
- struct lib9p_msg_Tclunk *req,
- struct lib9p_msg_Rclunk *resp) {
- util_handler_common(ctx, req, resp);
-
- clunkremove(ctx, req->fid, false);
-}
+static void handle_Tremove(struct srv_req *ctx,
+ struct lib9p_msg_Tremove *req) {
+ srv_handler_common(ctx, remove, req);
+ struct srv_fidinfo *fidinfo = map_load(&ctx->parent_sess->fids, req->fid);
+ if (!fidinfo) {
+ err = error_new(E_POSIX_EBADF, "bad file number ", req->fid);
+ goto tremove_return;
+ }
-static void handle_Tremove(struct _lib9p_srv_req *ctx,
- struct lib9p_msg_Tremove *req,
- struct lib9p_msg_Rremove *resp) {
- util_handler_common(ctx, req, resp);
+ bool remove = true;
+ struct srv_pathinfo *pathinfo = map_load(&ctx->parent_sess->paths, fidinfo->path);
+ assert(pathinfo);
+ if (pathinfo->parent_dir == fidinfo->path) {
+ err = error_new(E_POSIX_EBUSY, "cannot remove root");
+ remove = false;
+ goto tremove_main;
+ }
+ struct srv_pathinfo *parent = map_load(&ctx->parent_sess->paths, pathinfo->parent_dir);
+ assert(parent);
+ ok_or_error have_perm = srv_file_check_perm(ctx, parent->file, 0b010);
+ if (have_perm.is_err) {
+ err = have_perm.err;
+ remove = false;
+ goto tremove_main;
+ } else if (!have_perm.ok) {
+ err = error_new(E_POSIX_EACCES, "you do not have write permission on the parent directory");
+ remove = false;
+ goto tremove_main;
+ }
- clunkremove(ctx, req->fid, true);
+ tremove_main:
+ srv_fid_del(ctx, req->fid, fidinfo, remove);
+ tremove_return:
+ srv_respond(ctx, remove, &resp, err);
}
-static void handle_Tstat(struct _lib9p_srv_req *ctx,
- struct lib9p_msg_Tstat *req,
- struct lib9p_msg_Rstat *resp) {
- util_handler_common(ctx, req, resp);
+static void handle_Tstat(struct srv_req *ctx,
+ struct lib9p_msg_Tstat *req) {
+ srv_handler_common(ctx, stat, req);
- struct _srv_fidinfo *fidinfo = fidmap_load(&ctx->parent_sess->fids, req->fid);
+ struct srv_fidinfo *fidinfo = map_load(&ctx->parent_sess->fids, req->fid);
if (!fidinfo) {
- lib9p_errorf(&ctx->ctx.basectx,
- LINUX_EBADF, "bad file number %"PRIu32, req->fid);
- return;
+ err = error_new(E_POSIX_EBADF, "bad file number ", req->fid);
+ goto tstat_return;
}
- struct srv_pathinfo *pathinfo = pathmap_load(&ctx->parent_sess->paths, fidinfo->path);
+ struct srv_pathinfo *pathinfo = map_load(&ctx->parent_sess->paths, fidinfo->path);
assert(pathinfo);
- resp->stat = LO_CALL(pathinfo->file, stat, &ctx->ctx);
- if (!lib9p_ctx_has_error(&ctx->ctx.basectx))
- lib9p_stat_assert(resp->stat);
+ ctx->user = srv_userid_incref(fidinfo->user);
+ err = stat_and_convert(&resp.stat, ctx, pathinfo->file);
+ tstat_return:
+ if (ctx->user)
+ ctx->user = srv_userid_decref(ctx->user);
+ srv_respond(ctx, stat, &resp, err);
+}
+
+static void handle_Twstat(struct srv_req *ctx,
+ struct lib9p_msg_Twstat *req) {
+ srv_handler_common(ctx, wstat, req);
+
+ err = error_new(E_POSIX_EOPNOTSUPP, "wstat not (yet?) implemented");
+
+ srv_respond(ctx, wstat, &resp, err);
}
+#endif
+
+#if CONFIG_9P_ENABLE_9P2000_p9p
+static void handle_Topenfd(struct srv_req *ctx,
+ struct lib9p_msg_Topenfd *req) {
+ srv_handler_common(ctx, openfd, req);
-static void handle_Twstat(struct _lib9p_srv_req *ctx,
- struct lib9p_msg_Twstat *req,
- struct lib9p_msg_Rwstat *resp) {
- util_handler_common(ctx, req, resp);
+ err = error_new(E_POSIX_EOPNOTSUPP, "openfd not (yet?) implemented");
- lib9p_error(&ctx->ctx.basectx,
- LINUX_EOPNOTSUPP, "wstat not (yet?) implemented");
+ srv_respond(ctx, openfd, &resp, err);
}
+#endif
#if CONFIG_9P_ENABLE_9P2000_e
-static void handle_Tsession(struct _lib9p_srv_req *ctx,
- struct lib9p_msg_Tsession *req,
- struct lib9p_msg_Rsession *resp) {
- util_handler_common(ctx, req, resp);
+static void handle_Tsession(struct srv_req *ctx,
+ struct lib9p_msg_Tsession *req) {
+ srv_handler_common(ctx, session, req);
- lib9p_error(&ctx->ctx.basectx,
- LINUX_EOPNOTSUPP, "session not (yet?) implemented");
+ err = error_new(E_POSIX_EOPNOTSUPP, "session not (yet?) implemented");
+
+ srv_respond(ctx, session, &resp, err);
}
-static void handle_Tsread(struct _lib9p_srv_req *ctx,
- struct lib9p_msg_Tsread *req,
- struct lib9p_msg_Rsread *resp) {
- util_handler_common(ctx, req, resp);
+static void handle_Tsread(struct srv_req *ctx,
+ struct lib9p_msg_Tsread *req) {
+ srv_handler_common(ctx, sread, req);
+
+ err = error_new(E_POSIX_EOPNOTSUPP, "sread not (yet?) implemented");
- lib9p_error(&ctx->ctx.basectx,
- LINUX_EOPNOTSUPP, "sread not (yet?) implemented");
+ srv_respond(ctx, sread, &resp, err);
}
-static void handle_Tswrite(struct _lib9p_srv_req *ctx,
- struct lib9p_msg_Tswrite *req,
- struct lib9p_msg_Rswrite *resp) {
- util_handler_common(ctx, req, resp);
+static void handle_Tswrite(struct srv_req *ctx,
+ struct lib9p_msg_Tswrite *req) {
+ srv_handler_common(ctx, swrite, req);
+
+ err = error_new(E_POSIX_EOPNOTSUPP, "swrite not (yet?) implemented");
- lib9p_error(&ctx->ctx.basectx,
- LINUX_EOPNOTSUPP, "swrite not (yet?) implemented");
+ srv_respond(ctx, swrite, &resp, err);
}
#endif
diff --git a/lib9p/srv_errno.h b/lib9p/srv_errno.h
new file mode 100644
index 0000000..1384f97
--- /dev/null
+++ b/lib9p/srv_errno.h
@@ -0,0 +1,14 @@
+/* lib9p/srv_errno.h - TODO
+ *
+ * Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#ifndef _LIB9P_SRV_ERRNO_H_
+#define _LIB9P_SRV_ERRNO_H_
+
+#if CONFIG_9P_ENABLE_9P2000_u || CONFIG_9P_ENABLE_9P2000_L
+lib9p_errno_t libmisc_to_linuxgeneric_errno(_errnum);
+#endif
+
+#endif /* _LIB9P_SRV_ERRNO_H_ */
diff --git a/lib9p/srv_generated.c b/lib9p/srv_generated.c
new file mode 100644
index 0000000..26989c5
--- /dev/null
+++ b/lib9p/srv_generated.c
@@ -0,0 +1,91 @@
+/* lib9p/srv_generated.c - Generated by lib9p/srv_generated.c.gen. DO NOT EDIT! */
+
+#include <libmisc/error.h>
+#include <lib9p/core.h>
+#include "srv_errno.h"
+
+#if CONFIG_9P_ENABLE_9P2000_u || CONFIG_9P_ENABLE_9P2000_L
+lib9p_errno_t libmisc_to_linuxgeneric_errno(_errnum errnum) {
+ LM_PARTIAL_SWITCH (errnum) {
+ case E_POSIX_E2BIG: return LIB9P_ERRNO_L_E2BIG;
+ case E_POSIX_EACCES: return LIB9P_ERRNO_L_EACCES;
+ case E_POSIX_EADDRINUSE: return LIB9P_ERRNO_L_EADDRINUSE;
+ case E_POSIX_EADDRNOTAVAIL: return LIB9P_ERRNO_L_EADDRNOTAVAIL;
+ case E_POSIX_EAFNOSUPPORT: return LIB9P_ERRNO_L_EAFNOSUPPORT;
+ case E_POSIX_EAGAIN: return LIB9P_ERRNO_L_EAGAIN;
+ case E_POSIX_EALREADY: return LIB9P_ERRNO_L_EALREADY;
+ case E_POSIX_EBADF: return LIB9P_ERRNO_L_EBADF;
+ case E_POSIX_EBADMSG: return LIB9P_ERRNO_L_EBADMSG;
+ case E_POSIX_EBUSY: return LIB9P_ERRNO_L_EBUSY;
+ case E_POSIX_ECANCELED: return LIB9P_ERRNO_L_ECANCELED;
+ case E_POSIX_ECHILD: return LIB9P_ERRNO_L_ECHILD;
+ case E_POSIX_ECONNABORTED: return LIB9P_ERRNO_L_ECONNABORTED;
+ case E_POSIX_ECONNREFUSED: return LIB9P_ERRNO_L_ECONNREFUSED;
+ case E_POSIX_ECONNRESET: return LIB9P_ERRNO_L_ECONNRESET;
+ case E_POSIX_EDEADLK: return LIB9P_ERRNO_L_EDEADLK;
+ case E_POSIX_EDESTADDRREQ: return LIB9P_ERRNO_L_EDESTADDRREQ;
+ case E_POSIX_EDOM: return LIB9P_ERRNO_L_EDOM;
+ case E_POSIX_EDQUOT: return LIB9P_ERRNO_L_EDQUOT;
+ case E_POSIX_EEXIST: return LIB9P_ERRNO_L_EEXIST;
+ case E_POSIX_EFAULT: return LIB9P_ERRNO_L_EFAULT;
+ case E_POSIX_EFBIG: return LIB9P_ERRNO_L_EFBIG;
+ case E_POSIX_EHOSTUNREACH: return LIB9P_ERRNO_L_EHOSTUNREACH;
+ case E_POSIX_EIDRM: return LIB9P_ERRNO_L_EIDRM;
+ case E_POSIX_EILSEQ: return LIB9P_ERRNO_L_EILSEQ;
+ case E_POSIX_EINPROGRESS: return LIB9P_ERRNO_L_EINPROGRESS;
+ case E_POSIX_EINTR: return LIB9P_ERRNO_L_EINTR;
+ case E_POSIX_EINVAL: return LIB9P_ERRNO_L_EINVAL;
+ case E_POSIX_EIO: return LIB9P_ERRNO_L_EIO;
+ case E_POSIX_EISCONN: return LIB9P_ERRNO_L_EISCONN;
+ case E_POSIX_EISDIR: return LIB9P_ERRNO_L_EISDIR;
+ case E_POSIX_ELOOP: return LIB9P_ERRNO_L_ELOOP;
+ case E_POSIX_EMFILE: return LIB9P_ERRNO_L_EMFILE;
+ case E_POSIX_EMLINK: return LIB9P_ERRNO_L_EMLINK;
+ case E_POSIX_EMSGSIZE: return LIB9P_ERRNO_L_EMSGSIZE;
+ case E_POSIX_EMULTIHOP: return LIB9P_ERRNO_L_EMULTIHOP;
+ case E_POSIX_ENAMETOOLONG: return LIB9P_ERRNO_L_ENAMETOOLONG;
+ case E_POSIX_ENETDOWN: return LIB9P_ERRNO_L_ENETDOWN;
+ case E_POSIX_ENETRESET: return LIB9P_ERRNO_L_ENETRESET;
+ case E_POSIX_ENETUNREACH: return LIB9P_ERRNO_L_ENETUNREACH;
+ case E_POSIX_ENFILE: return LIB9P_ERRNO_L_ENFILE;
+ case E_POSIX_ENOBUFS: return LIB9P_ERRNO_L_ENOBUFS;
+ case E_POSIX_ENODEV: return LIB9P_ERRNO_L_ENODEV;
+ case E_POSIX_ENOENT: return LIB9P_ERRNO_L_ENOENT;
+ case E_POSIX_ENOEXEC: return LIB9P_ERRNO_L_ENOEXEC;
+ case E_POSIX_ENOLCK: return LIB9P_ERRNO_L_ENOLCK;
+ case E_POSIX_ENOLINK: return LIB9P_ERRNO_L_ENOLINK;
+ case E_POSIX_ENOMEM: return LIB9P_ERRNO_L_ENOMEM;
+ case E_POSIX_ENOMSG: return LIB9P_ERRNO_L_ENOMSG;
+ case E_POSIX_ENOPROTOOPT: return LIB9P_ERRNO_L_ENOPROTOOPT;
+ case E_POSIX_ENOSPC: return LIB9P_ERRNO_L_ENOSPC;
+ case E_POSIX_ENOSYS: return LIB9P_ERRNO_L_ENOSYS;
+ case E_POSIX_ENOTCONN: return LIB9P_ERRNO_L_ENOTCONN;
+ case E_POSIX_ENOTDIR: return LIB9P_ERRNO_L_ENOTDIR;
+ case E_POSIX_ENOTEMPTY: return LIB9P_ERRNO_L_ENOTEMPTY;
+ case E_POSIX_ENOTRECOVERABLE: return LIB9P_ERRNO_L_ENOTRECOVERABLE;
+ case E_POSIX_ENOTSOCK: return LIB9P_ERRNO_L_ENOTSOCK;
+ case E_POSIX_ENOTTY: return LIB9P_ERRNO_L_ENOTTY;
+ case E_POSIX_ENXIO: return LIB9P_ERRNO_L_ENXIO;
+ case E_POSIX_EOPNOTSUPP: return LIB9P_ERRNO_L_EOPNOTSUPP;
+ case E_POSIX_EOVERFLOW: return LIB9P_ERRNO_L_EOVERFLOW;
+ case E_POSIX_EOWNERDEAD: return LIB9P_ERRNO_L_EOWNERDEAD;
+ case E_POSIX_EPERM: return LIB9P_ERRNO_L_EPERM;
+ case E_POSIX_EPIPE: return LIB9P_ERRNO_L_EPIPE;
+ case E_POSIX_EPROTO: return LIB9P_ERRNO_L_EPROTO;
+ case E_POSIX_EPROTONOSUPPORT: return LIB9P_ERRNO_L_EPROTONOSUPPORT;
+ case E_POSIX_EPROTOTYPE: return LIB9P_ERRNO_L_EPROTOTYPE;
+ case E_POSIX_ERANGE: return LIB9P_ERRNO_L_ERANGE;
+ case E_POSIX_EROFS: return LIB9P_ERRNO_L_EROFS;
+ case E_POSIX_ESOCKTNOSUPPORT: return LIB9P_ERRNO_L_ESOCKTNOSUPPORT;
+ case E_POSIX_ESPIPE: return LIB9P_ERRNO_L_ESPIPE;
+ case E_POSIX_ESRCH: return LIB9P_ERRNO_L_ESRCH;
+ case E_POSIX_ESTALE: return LIB9P_ERRNO_L_ESTALE;
+ case E_POSIX_ETIMEDOUT: return LIB9P_ERRNO_L_ETIMEDOUT;
+ case E_POSIX_ETXTBSY: return LIB9P_ERRNO_L_ETXTBSY;
+ case E_POSIX_EXDEV: return LIB9P_ERRNO_L_EXDEV;
+ case E_POSIX_ENOTSUP: return LIB9P_ERRNO_L_EOPNOTSUPP;
+ case E_POSIX_EWOULDBLOCK: return LIB9P_ERRNO_L_EAGAIN;
+ default: return LIB9P_ERRNO_L_EIO;
+ }
+}
+#endif
diff --git a/lib9p/srv_generated.c.gen b/lib9p/srv_generated.c.gen
new file mode 100755
index 0000000..6a6335a
--- /dev/null
+++ b/lib9p/srv_generated.c.gen
@@ -0,0 +1,30 @@
+#!/usr/bin/env bash
+# lib9p/srv_generated.c.gen - Generate errno translation tables
+#
+# Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+# SPDX-License-Identifier: AGPL-3.0-or-later
+
+error_h=$1
+outfile=$2
+
+{
+ echo "/* ${outfile} - Generated by $0. DO NOT EDIT! */"
+ echo
+ echo '#include <libmisc/error.h>'
+ echo '#include <lib9p/core.h>'
+ echo '#include "srv_errno.h"'
+ echo
+ echo '#if CONFIG_9P_ENABLE_9P2000_u || CONFIG_9P_ENABLE_9P2000_L'
+ echo 'lib9p_errno_t libmisc_to_linuxgeneric_errno(_errnum errnum) {'
+ echo $'\tLM_PARTIAL_SWITCH (errnum) {'
+ sed -nE \
+ -e 's@^(#define)?\s+(E_POSIX_([_A-Z0-9]+))[ ,][^/]*/\* ([^*(]+) (\*/|\().*@'$'\tcase \\2: return LIB9P_ERRNO_L_\\3;''@p' \
+ -- "$error_h" |
+ grep -v -e '_ENOTSUP' -e '_EWOULDBLOCK'
+ echo $'\tcase E_POSIX_ENOTSUP: return LIB9P_ERRNO_L_EOPNOTSUPP;'
+ echo $'\tcase E_POSIX_EWOULDBLOCK: return LIB9P_ERRNO_L_EAGAIN;'
+ echo $'\tdefault: return LIB9P_ERRNO_L_EIO;'
+ echo $'\t}'
+ echo '}'
+ echo '#endif'
+} >"$outfile"
diff --git a/lib9p/srv_include/lib9p/srv.h b/lib9p/srv_include/lib9p/srv.h
new file mode 100644
index 0000000..cea1d79
--- /dev/null
+++ b/lib9p/srv_include/lib9p/srv.h
@@ -0,0 +1,313 @@
+/* lib9p/srv.h - 9P server
+ *
+ * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#ifndef _LIB9P_SRV_H_
+#define _LIB9P_SRV_H_
+
+#include <libcr_ipc/chan.h>
+#include <libcr_ipc/rpc.h>
+#include <libhw/generic/net.h>
+#include <libmisc/assert.h>
+#include <libmisc/obj.h>
+#include <libmisc/private.h>
+
+#include <lib9p/core.h>
+
+#ifndef CONFIG_9P_SRV_MAX_ERR_SIZE
+ #error config.h must define CONFIG_9P_SRV_MAX_ERR_SIZE
+#endif
+static_assert(CONFIG_9P_SRV_MAX_ERR_SIZE <= UINT16_MAX);
+
+/* context ********************************************************************/
+
+struct lib9p_srv_userid {
+ struct lib9p_s name;
+#if CONFIG_9P_ENABLE_9P2000_u || CONFIG_9P_ENABLE_9P2000_L
+ lib9p_nuid_t num;
+#endif
+
+ BEGIN_PRIVATE(LIB9P_SRV_H);
+ unsigned int refcount;
+ END_PRIVATE(LIB9P_SRV_H);
+};
+
+enum _lib9p_srv_flush_result {
+ _LIB9P_SRV_FLUSH_RFLUSH,
+ _LIB9P_SRV_FLUSH_SILENT,
+};
+
+CR_CHAN_DECLARE(_lib9p_srv_flush_ch, enum _lib9p_srv_flush_result);
+
+struct lib9p_srv_ctx {
+ struct lib9p_ctx basectx;
+ struct lib9p_srv_userid *user;
+
+ BEGIN_PRIVATE(LIB9P_SRV_H);
+ struct _lib9p_srv_sess *parent_sess;
+ lib9p_tag_t tag;
+ uint8_t *net_bytes;
+ _lib9p_srv_flush_ch_t flush_ch; /* flushers for this req _read_ from here */
+ bool responded;
+ END_PRIVATE(LIB9P_SRV_H);
+};
+
+/**
+ * Return whether there is an outstanding Tflush or Tversion
+ * cancellation of this request. After becoming true, this may go
+ * back to false if the Tflush itself is flushed.
+ *
+ * As a special case, returning E_POSIX_ECANCELED indicates that the
+ * flush has been observed, and a Rerror should not be sent ot the
+ * client.
+ */
+bool lib9p_srv_flush_requested(struct lib9p_srv_ctx *ctx);
+
+/* version-independent stat ***************************************************/
+
+struct lib9p_srv_stat {
+ struct lib9p_qid qid;
+ lib9p_dm_t mode;
+ uint32_t atime_sec; /* BUG: u32 seconds means a 2106 problem */
+#if CONFIG_9P_ENABLE_9P2000_L
+ uint32_t atime_nsec;
+#endif
+ uint32_t mtime_sec; /* BUG: u32 seconds means a 2106 problem */
+#if CONFIG_9P_ENABLE_9P2000_L
+ uint32_t mtime_nsec;
+ uint32_t ctime_sec; /* BUG: u32 seconds means a 2106 problem */
+ uint32_t ctime_nsec;
+ uint32_t btime_sec; /* BUG: u32 seconds means a 2106 problem */
+ uint32_t btime_nsec;
+#endif
+ uint64_t size;
+ struct lib9p_s name;
+ struct lib9p_srv_userid owner_uid;
+ struct lib9p_srv_userid owner_gid;
+ struct lib9p_srv_userid last_modifier_uid;
+#if CONFIG_9P_ENABLE_9P2000_u
+ struct lib9p_s extension;
+#endif
+};
+
+#ifdef NDEBUG
+#define lib9p_srv_stat_assert(stat) ((void)0)
+#else
+void lib9p_srv_stat_assert(const struct lib9p_srv_stat *stat);
+#endif
+
+/* interface definitions ******************************************************/
+
+struct lib9p_srv_dirent {
+ struct lib9p_qid qid;
+ struct lib9p_s name;
+};
+DECLARE_ERROR_OR_(struct lib9p_srv_dirent, lib9p_srv_dirent);
+
+/* FIXME: It would be nice if pread() could return more than 1 iovec. This
+ * API allows it, but for the "just-1-iovec" requirement inherited from
+ * io_preader_to. We enforce this requirement because otherwise we wouldn't
+ * know at compile-time how big the iovec array in lib9p_Rmsg_send_buf needs
+ * to be.
+ */
+#define lib9p_srv_fio_LO_IFACE /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ \
+ LO_FUNC(struct lib9p_qid , ioqid ) \
+ LO_FUNC(void , iofree ) \
+ LO_FUNC(uint32_t , iounit ) \
+ /** \
+ * This is similar to io_preader_to->pread_to, and must follow the \
+ * same requirements. \
+ */ \
+ LO_FUNC(error , pread , struct lib9p_srv_ctx *, \
+ lo_interface io_writer dst, \
+ uint64_t src_offset, \
+ uint32_t count) \
+ /** \
+ * If the file was append-only when fopen()ed, then byte_offset will \
+ * always be 0. \
+ * \
+ * This similar to io_pwrite, but a short-write is not an error. \
+ */ \
+ LO_FUNC(uint32_t_or_error , pwrite , struct lib9p_srv_ctx *, \
+ const void *buf, \
+ uint32_t byte_count, \
+ uint64_t byte_offset)
+LO_INTERFACE(lib9p_srv_fio); /*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+DECLARE_ERROR_OR_(lo_interface lib9p_srv_fio, lib9p_srv_fio);
+
+#define lib9p_srv_dio_LO_IFACE /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ \
+ LO_FUNC(struct lib9p_qid , ioqid ) \
+ LO_FUNC(void , iofree ) \
+ /** \
+ * Return the idx-th dirent. idx will always be either 0 or \
+ * prev_idx+1. A dirent with an empty name signals EOF. The string \
+ * must remain valid until the next dread() call or iofree(). \
+ */ \
+ LO_FUNC(lib9p_srv_dirent_or_error , dread , struct lib9p_srv_ctx *, \
+ size_t idx)
+LO_INTERFACE(lib9p_srv_dio); /*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+DECLARE_ERROR_OR_(lo_interface lib9p_srv_dio, lib9p_srv_dio);
+
+struct _lo_lib9p_srv_file_vtable;
+lo_interface lib9p_srv_file {
+ void *self;
+ const struct _lo_lib9p_srv_file_vtable *vtable;
+};
+DECLARE_ERROR_OR_(lo_interface lib9p_srv_file, lib9p_srv_file);
+#define lib9p_srv_file_LO_IFACE /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ \
+ /* resource management **********************************************/ \
+ \
+ /** \
+ * free() is be called when all FIDs associated with the file are \
+ * clunked. \
+ * \
+ * free() MUST NOT error. \
+ */ \
+ LO_FUNC(void , free ) \
+ \
+ /** \
+ * qid() is called frequently and returns the current QID of the file. \
+ * The .path field MUST never change, the .type field may change in \
+ * response to wstat() calls (but the QT_DIR bit MUST NOT change), and \
+ * the .vers field may change frequently in response to any number of \
+ * things (wstat(), write(), or non-9P events). \
+ * \
+ * qid() MUST NOT error. \
+ */ \
+ LO_FUNC(struct lib9p_qid , qid ) \
+ \
+ /* non-"opened" generic I/O *****************************************/ \
+ \
+ /** Strings returned from stat() must remain valid until free(). */ \
+ LO_FUNC(error , stat , struct lib9p_srv_ctx *, \
+ struct lib9p_srv_stat * ret) \
+ LO_FUNC(error , wstat , struct lib9p_srv_ctx *, \
+ struct lib9p_srv_stat) \
+ LO_FUNC(error , remove , struct lib9p_srv_ctx *) \
+ \
+ /* non-"opened" directory I/O ***************************************/ \
+ \
+ LO_FUNC(lib9p_srv_file_or_error , dwalk , struct lib9p_srv_ctx *, \
+ struct lib9p_s childname) \
+ LO_FUNC(lib9p_srv_file_or_error , dcreate, struct lib9p_srv_ctx *, \
+ struct lib9p_s childname, \
+ struct lib9p_srv_userid *user, \
+ struct lib9p_srv_userid *group, \
+ lib9p_dm_t perm) \
+ \
+ /* open() for I/O ***************************************************/ \
+ \
+ LO_FUNC(lib9p_srv_fio_or_error , fopen , struct lib9p_srv_ctx *, \
+ bool rd, bool wr, \
+ bool trunc) \
+ LO_FUNC(lib9p_srv_dio_or_error , dopen , struct lib9p_srv_ctx *)
+LO_INTERFACE(lib9p_srv_file); /*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+
+#define LIB9P_SRV_NOTDIR(QUALS, TYP, NAM) \
+ QUALS lib9p_srv_file_or_error NAM##_dwalk (TYP *, struct lib9p_srv_ctx *, struct lib9p_s) { assert_notreached("not a directory"); } \
+ QUALS lib9p_srv_file_or_error NAM##_dcreate(TYP *, struct lib9p_srv_ctx *, struct lib9p_s, \
+ struct lib9p_srv_userid *, struct lib9p_srv_userid *, lib9p_dm_t) { assert_notreached("not a directory"); } \
+ QUALS lib9p_srv_dio_or_error NAM##_dopen (TYP *, struct lib9p_srv_ctx *) { assert_notreached("not a directory"); } \
+ LM_FORCE_SEMICOLON
+
+#define LIB9P_SRV_NOTFILE(QUALS, TYP, NAM) \
+ QUALS lib9p_srv_fio_or_error NAM##_fopen (TYP *, struct lib9p_srv_ctx *, bool, bool, bool) { assert_notreached("not a file"); } \
+ LM_FORCE_SEMICOLON
+
+/* main server entrypoints ****************************************************/
+
+CR_RPC_DECLARE(_lib9p_srv_reqch, struct lib9p_srv_ctx *, bool);
+
+#if CONFIG_9P_ENABLE_9P2000_p9p
+#define net_stream_conn_unix_LO_IFACE \
+ LO_NEST(net_stream_conn) \
+ /** Returns 0 on success, -errno on error. */ \
+ LO_FUNC(int, send_unix_fd, int fd)
+LO_INTERFACE(net_stream_conn_unix);
+#endif
+
+struct lib9p_srv {
+ /* Things you provide */
+ error /*TODO*/ (*auth )(struct lib9p_srv_ctx *, struct lib9p_s treename); /* optional */
+ lib9p_srv_file_or_error (*rootdir)(struct lib9p_srv_ctx *, struct lib9p_s treename);
+ void (*msglog )(struct lib9p_srv_ctx *, enum lib9p_msg_type, void *hostmsg); /* optional */
+#if CONFIG_9P_ENABLE_9P2000_p9p
+ lo_interface net_stream_conn_unix (*type_assert_unix)(lo_interface net_stream_conn); /* optional */
+#endif
+
+ /* For internal use */
+ BEGIN_PRIVATE(LIB9P_SRV_H);
+ unsigned int readers;
+ unsigned int writers;
+ _lib9p_srv_reqch_t reqch;
+ END_PRIVATE(LIB9P_SRV_H);
+};
+
+/**
+ * In a loop loop, accept a connection call lib9p_srv_read() on it.
+ * If LO_CALL(listener, accept) fails, then the function returns.
+ *
+ * When the last lib9p_srv_accept_and_read_loop() instance for a given
+ * `srv` returns, it will signal all lib9p_srv_worker_loop() calls to
+ * return.
+ *
+ * @param srv: The server configuration and state; has an associated
+ * pool of lib9p_srv_worker_loop() coroutines.
+ *
+ * @param listener: The listener object to accept connections from.
+ */
+void lib9p_srv_accept_and_read_loop(struct lib9p_srv *srv, lo_interface net_stream_listener listener);
+
+/**
+ * You should probably not call this directly; you should probably use
+ * lib9p_srv_accept_and_read_loop().
+ *
+ * Given an already-established stream connection (i.e. a TCP
+ * connection), service that connection; return once the connection is
+ * closed. Requests are dispatched to a pool of
+ * lib9p_srv_worker_loop() coroutines with the same `srv`.
+ *
+ * Will just close the connection if a T-message has a size[4] <7.
+ *
+ * @param srv: The server configuration and state; has an associated
+ * pool of lib9p_srv_worker_loop() coroutines.
+ *
+ * @param conn: The listener object to accept connections from.
+ *
+ * Errors that this function itself may send to clients:
+ *
+ * @errno E_POSIX_EMSGSIZE T-message has size[4] bigger than max_msg_size
+ * @errno E_POSIX_EDOM Tversion specified an impossibly small max_msg_size
+ * @errno E_POSIX_EOPNOTSUPP T-message has an R-message type, or an unrecognized T-message type
+ * @errno E_POSIX_EBADMSG T-message has wrong size[4] for its content, or has invalid UTF-8
+ */
+void lib9p_srv_read(struct lib9p_srv *srv, lo_interface net_stream_conn conn);
+
+/**
+ * In a loop, call lib9p_srv_worker() to service requests to the
+ * `struct lib9p_srv *srv` argument that have been read by
+ * lib9p_srv_accept_and_read_loop() / lib9p_srv_read(). A "NULL"
+ * request causes the function to return.
+ *
+ * @param srv: The server configuration and state; has an associated
+ * pool of lib9p_srv_accept_and_read_loop() coroutines.
+ */
+void lib9p_srv_worker_loop(struct lib9p_srv *srv);
+
+/**
+ * You should probably not call this directly; you should probably use
+ * lib9p_srv_worker_loop().
+ *
+ * Handle and send a response to a single request.
+ *
+ * @param req: The request to handle.
+ *
+ * Errors that this function itself may send to clients:
+ *
+ * @errno E_POSIX_ERANGE R-message does not fit into max_msg_size
+ */
+void lib9p_srv_worker(struct lib9p_srv_ctx *req);
+
+#endif /* _LIB9P_SRV_H_ */
diff --git a/lib9p/tables.c b/lib9p/tables.c
deleted file mode 100644
index 271b17b..0000000
--- a/lib9p/tables.c
+++ /dev/null
@@ -1,186 +0,0 @@
-/* lib9p/tables.c - Access tables of version and message information
- *
- * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
-
-#include <string.h>
-
-#include <libmisc/endian.h>
-#include <libmisc/log.h> /* for const_byte_str() */
-
-#include "tables.h"
-
-/* bounds checks **************************************************************/
-
-static inline void assert_ver(enum lib9p_version ver) {
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wtype-limits"
- assert(0 <= ver && ver < LIB9P_VER_NUM);
-#pragma GCC diagnostic pop
-}
-
-static inline void assert_typ(enum lib9p_msg_type typ) {
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wtype-limits"
- assert(0 <= typ && typ < 0xFF);
-#pragma GCC diagnostic pop
-}
-
-/* simple lookups *************************************************************/
-
-const char *lib9p_version_str(enum lib9p_version ver) {
- assert_ver(ver);
- return _lib9p_table_ver[ver].name;
-}
-
-uint32_t lib9p_version_min_msg_size(enum lib9p_version ver) {
- assert_ver(ver);
- return _lib9p_table_ver[ver].min_msg_size;
-}
-
-const char *lib9p_msgtype_str(enum lib9p_version ver, enum lib9p_msg_type typ) {
- assert_ver(ver);
- assert_typ(typ);
- return _lib9p_table_msg[ver][typ].name ?: const_byte_str(typ);
-}
-
-lo_interface fmt_formatter lo_box_lib9p_msg_as_fmt_formatter(struct lib9p_ctx *ctx, enum lib9p_msg_type typ, void *body) {
- assert(ctx);
- assert_ver(ctx->version);
- assert_typ(typ);
- assert(_lib9p_table_msg[ctx->version][typ].box_as_fmt_formatter);
- return _lib9p_table_msg[ctx->version][typ].box_as_fmt_formatter(body);
-}
-
-/* main message functions *****************************************************/
-
-static
-ssize_t _lib9p_validate(uint8_t xxx_low_typ_bit,
- const char *xxx_errmsg,
- const struct _lib9p_recv_tentry xxx_table[LIB9P_VER_NUM][0x80],
- struct lib9p_ctx *ctx, uint8_t *net_bytes) {
- assert_ver(ctx->version);
- /* Inspect the first 5 bytes ourselves. */
- uint32_t net_size = uint32le_decode(net_bytes);
- if (net_size < 5)
- return lib9p_error(ctx, LINUX_EBADMSG, "message is impossibly short");
- uint8_t typ = net_bytes[4];
- if (typ % 2 != xxx_low_typ_bit)
- return lib9p_errorf(ctx, LINUX_EOPNOTSUPP, "%s: message_type=%s", xxx_errmsg,
- lib9p_msgtype_str(ctx->version, typ));
- struct _lib9p_recv_tentry tentry = xxx_table[ctx->version][typ/2];
- if (!tentry.validate)
- return lib9p_errorf(ctx, LINUX_EOPNOTSUPP, "unknown message type: %s (protocol_version=%s)",
- lib9p_msgtype_str(ctx->version, typ), lib9p_version_str(ctx->version));
-
- /* Now use the message-type-specific tentry to process the whole thing. */
- return tentry.validate(ctx, net_size, net_bytes);
-}
-
-ssize_t lib9p_Tmsg_validate(struct lib9p_ctx *ctx, uint8_t *net_bytes) {
- return _lib9p_validate(0, "expected a T-message but got an R-message", _lib9p_table_Tmsg_recv,
- ctx, net_bytes);
-}
-
-ssize_t lib9p_Rmsg_validate(struct lib9p_ctx *ctx, uint8_t *net_bytes) {
- return _lib9p_validate(1, "expected an R-message but got a T-message", _lib9p_table_Rmsg_recv,
- ctx, net_bytes);
-}
-
-static
-void _lib9p_unmarshal(const struct _lib9p_recv_tentry xxx_table[LIB9P_VER_NUM][0x80],
- struct lib9p_ctx *ctx, uint8_t *net_bytes,
- enum lib9p_msg_type *ret_typ, void *ret_body) {
- assert_ver(ctx->version);
- enum lib9p_msg_type typ = net_bytes[4];
- *ret_typ = typ;
- struct _lib9p_recv_tentry tentry = xxx_table[ctx->version][typ/2];
-
- tentry.unmarshal(ctx, net_bytes, ret_body);
-}
-
-void lib9p_Tmsg_unmarshal(struct lib9p_ctx *ctx, uint8_t *net_bytes,
- enum lib9p_msg_type *ret_typ, void *ret_body) {
- _lib9p_unmarshal(_lib9p_table_Tmsg_recv,
- ctx, net_bytes, ret_typ, ret_body);
-}
-
-void lib9p_Rmsg_unmarshal(struct lib9p_ctx *ctx, uint8_t *net_bytes,
- enum lib9p_msg_type *ret_typ, void *ret_body) {
- _lib9p_unmarshal(_lib9p_table_Rmsg_recv,
- ctx, net_bytes, ret_typ, ret_body);
-}
-
-static
-bool _lib9p_marshal(const struct _lib9p_send_tentry xxx_table[LIB9P_VER_NUM][0x80],
- struct lib9p_ctx *ctx, enum lib9p_msg_type typ, void *body,
- size_t *ret_iov_cnt, struct iovec *ret_iov, uint8_t *ret_copied) {
- assert_ver(ctx->version);
- assert_typ(typ);
- struct _marshal_ret ret = {
- .net_iov_cnt = 1,
- .net_iov = ret_iov,
- .net_copied_size = 0,
- .net_copied = ret_copied,
- };
-
- struct _lib9p_send_tentry tentry = xxx_table[ctx->version][typ/2];
- bool ret_erred = tentry.marshal(ctx, body, &ret);
- if (ret_iov[ret.net_iov_cnt-1].iov_len == 0)
- ret.net_iov_cnt--;
- *ret_iov_cnt = ret.net_iov_cnt;
- return ret_erred;
-}
-
-bool lib9p_Tmsg_marshal(struct lib9p_ctx *ctx, enum lib9p_msg_type typ, void *body,
- struct lib9p_Tmsg_send_buf *ret) {
- assert(typ % 2 == 0);
- memset(ret, 0, sizeof(*ret));
- return _lib9p_marshal(_lib9p_table_Tmsg_send,
- ctx, typ, body,
- &ret->iov_cnt, ret->iov, ret->copied);
-}
-
-bool lib9p_Rmsg_marshal(struct lib9p_ctx *ctx, enum lib9p_msg_type typ, void *body,
- struct lib9p_Rmsg_send_buf *ret) {
- assert(typ % 2 == 1);
- memset(ret, 0, sizeof(*ret));
- return _lib9p_marshal(_lib9p_table_Rmsg_send,
- ctx, typ, body,
- &ret->iov_cnt, ret->iov, ret->copied);
-}
-
-/* `struct lib9p_stat` helpers ************************************************/
-
-bool lib9p_stat_validate(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes,
- uint32_t *ret_net_size, ssize_t *ret_host_size) {
- ssize_t host_size = _lib9p_stat_validate(ctx, net_size, net_bytes, ret_net_size);
- if (host_size < 0)
- return true;
- if (ret_host_size)
- *ret_host_size = host_size;
- return false;
-}
-
-void lib9p_stat_unmarshal(struct lib9p_ctx *ctx, uint8_t *net_bytes,
- struct lib9p_stat *ret) {
- _lib9p_stat_unmarshal(ctx, net_bytes, ret);
-}
-
-uint32_t lib9p_stat_marshal(struct lib9p_ctx *ctx, uint32_t max_net_size, struct lib9p_stat *obj,
- uint8_t *ret_bytes) {
- struct lib9p_ctx _ctx = *ctx;
- _ctx.max_msg_size = max_net_size;
-
- struct iovec iov = {0};
- struct _marshal_ret ret = {
- .net_iov_cnt = 1,
- .net_iov = &iov,
- .net_copied_size = 0,
- .net_copied = ret_bytes,
- };
- if (_lib9p_stat_marshal(&_ctx, obj, &ret))
- return 0;
- return ret.net_iov[0].iov_len;
-}
diff --git a/lib9p/tests/client_config/config.h b/lib9p/tests/client_config/config.h
new file mode 100644
index 0000000..afcf49f
--- /dev/null
+++ b/lib9p/tests/client_config/config.h
@@ -0,0 +1,18 @@
+/* lib9p/tests/client_config/config.h - Compile-time configuration for lib9p test clients
+ *
+ * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#ifndef _CONFIG_H_
+#define _CONFIG_H_
+
+#define CONFIG_9P_MAX_9P2000_e_WELEM 16
+
+#define CONFIG_9P_ENABLE_9P2000 1 /* bool */
+#define CONFIG_9P_ENABLE_9P2000_u 1 /* bool */
+#define CONFIG_9P_ENABLE_9P2000_e 1 /* bool */
+#define CONFIG_9P_ENABLE_9P2000_L 1 /* bool */
+#define CONFIG_9P_ENABLE_9P2000_p9p 1 /* bool */
+
+#endif /* _CONFIG_H_ */
diff --git a/lib9p/tests/runtest b/lib9p/tests/runtest
index 379ea6d..6883391 100755
--- a/lib9p/tests/runtest
+++ b/lib9p/tests/runtest
@@ -1,65 +1,50 @@
#!/usr/bin/env bash
-# lib9p/tests/runtest - Simple tests for the 9P `test_server`
+# lib9p/tests/runtest - Test harness for the 9P `test_server`
#
# Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com>
# SPDX-License-Identifier: AGPL-3.0-or-later
set -euE -o pipefail
-set -x
-port=$(python -c 'import socket; s=socket.socket(); s.bind(("", 0)); print(s.getsockname()[1]); s.close()')
-valgrind --error-exitcode=2 ./tests/test_server/test_server "$port" &
-server_pid=$!
-# shellcheck disable=SC2064
-trap "kill $server_pid || true; wait $server_pid || true" EXIT
-server_addr="localhost:${port}"
+build_aux=$(realpath --canonicalize-missing -- "${BASH_SOURCE[0]}/../../../build-aux")
-client=(9p -a "$server_addr")
+if [[ $# != 2 ]]; then
+ echo >&2 "Usage: $0 CLIENTSCRIPT EXPLOG"
+ exit 2
+fi
+clientscript="$1"
+explog="$2"
-expect_lines() (
+cleanup=()
+cleanup() {
{ set +x; } &>/dev/null
- printf >&2 '+ diff -u expected.txt actual.txt\n'
- diff -u <(printf '%s\n' "$@") <(printf '%s\n' "$out")
-)
-
-while [[ -d /proc/$server_pid && "$(readlink /proc/$server_pid/fd/4 2>/dev/null)" != socket:* ]]; do sleep 0.1; done
-
-out=$("${client[@]}" ls -l '')
-expect_lines \
- 'd-r-xr-xr-x M 0 root root 0 Oct 7 2024 Documentation' \
- '--r--r--r-- M 0 root root 166 Oct 7 2024 README.md' \
- '---w--w--w- M 0 root root 0 Oct 7 2024 shutdown'
+ local i
+ for ((i = ${#cleanup[@]} - 1; i >= 0; i--)); do
+ eval "set -x; ${cleanup[$i]}"
+ { set +x; } &>/dev/null
+ done
+}
+trap cleanup EXIT
-out=$("${client[@]}" ls -l 'Documentation/')
-expect_lines \
- '--r--r--r-- M 0 root root 166 Oct 7 2024 x'
+logfile=$(mktemp -t lib9p-log.XXXXXXXXXX)
+cleanup+=("rm -f -- ${logfile@Q}")
-out=$("${client[@]}" read 'README.md')
-expect_lines \
- '<!--' \
- ' README.md - test static file' \
- '' \
- ' Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>' \
- ' SPDX-License-Identifier: AGPL-3.0-or-later' \
- '-->' \
- 'Hello, world!'
+port=$(python -c 'import socket; s=socket.socket(); s.bind(("", 0)); print(s.getsockname()[1]); s.close()')
-out=$("${client[@]}" read 'Documentation/x')
-expect_lines \
- '<!--' \
- ' Documentation/x.txt - test static file' \
- '' \
- ' Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>' \
- ' SPDX-License-Identifier: AGPL-3.0-or-later' \
- '-->' \
- 'foo'
+set -x
-out=$("${client[@]}" stat 'Documentation/x')
-expect_lines \
- "'x' 'root' 'root' 'root' q (0000000000000001 1 ) m 0444 at 1728337905 mt 1728337904 l 166 t 0 d 0"
+"${build_aux}/valgrind" ./tests/test_server/test_server "$port" "$logfile" &
+server_pid=$!
+cleanup+=("kill $server_pid || true; wait $server_pid || true")
+while [[ -d /proc/$server_pid ]] && ! (readlink /proc/$server_pid/fd/* 2>/dev/null | grep -q ^socket:); do sleep 0.1; done
-out=$("${client[@]}" write 'shutdown' <<<1)
-expect_lines ''
+if [[ "$(head -c2 -- "$clientscript")" == '#!' ]]; then
+ "$clientscript" "$port"
+else
+ "${build_aux}/valgrind" "$clientscript" "$port"
+fi
wait "$server_pid"
-trap - EXIT
+cleanup=("${cleanup[@]::1}")
+
+diff -u -- <(grep -e '^[<>]' -- "$explog") "$logfile"
diff --git a/lib9p/tests/test_compile.c b/lib9p/tests/test_compile.c
index 8f2445d..6d7fb50 100644
--- a/lib9p/tests/test_compile.c
+++ b/lib9p/tests/test_compile.c
@@ -1,299 +1,1255 @@
/* lib9p/tests/test_compile.c - Generated by lib9p/tests/test_compile.c.gen. DO NOT EDIT! */
-#include <lib9p/9p.h>
+#include <lib9p/core.h>
int main(void) {
- [[gnu::unused]] uint64_t x;
- x = LIB9P_TAG_NOTAG;
- x = LIB9P_FID_NOFID;
- x = LIB9P_DM_DIR;
- x = LIB9P_DM_APPEND;
- x = LIB9P_DM_EXCL;
- x = _LIB9P_DM_PLAN9_MOUNT;
- x = LIB9P_DM_AUTH;
- x = LIB9P_DM_TMP;
- x = _LIB9P_DM_UNUSED_25;
- x = _LIB9P_DM_UNUSED_24;
- x = LIB9P_DM_DEVICE;
- x = _LIB9P_DM_UNUSED_22;
- x = LIB9P_DM_PIPE;
- x = LIB9P_DM_SOCKET;
- x = LIB9P_DM_SETUID;
- x = LIB9P_DM_SETGID;
- x = _LIB9P_DM_UNUSED_17;
- x = _LIB9P_DM_UNUSED_16;
- x = _LIB9P_DM_UNUSED_15;
- x = _LIB9P_DM_UNUSED_14;
- x = _LIB9P_DM_UNUSED_13;
- x = _LIB9P_DM_UNUSED_12;
- x = _LIB9P_DM_UNUSED_11;
- x = _LIB9P_DM_UNUSED_10;
- x = _LIB9P_DM_UNUSED_9;
- x = LIB9P_DM_OWNER_R;
- x = LIB9P_DM_OWNER_W;
- x = LIB9P_DM_OWNER_X;
- x = LIB9P_DM_GROUP_R;
- x = LIB9P_DM_GROUP_W;
- x = LIB9P_DM_GROUP_X;
- x = LIB9P_DM_OTHER_R;
- x = LIB9P_DM_OTHER_W;
- x = LIB9P_DM_OTHER_X;
- x = LIB9P_DM_PERM_MASK;
- x = LIB9P_QT_DIR;
- x = LIB9P_QT_APPEND;
- x = LIB9P_QT_EXCL;
- x = _LIB9P_QT_PLAN9_MOUNT;
- x = LIB9P_QT_AUTH;
- x = LIB9P_QT_TMP;
- x = LIB9P_QT_SYMLINK;
- x = _LIB9P_QT_UNUSED_0;
- x = LIB9P_QT_FILE;
- x = LIB9P_NUID_NONUID;
- x = _LIB9P_O_UNUSED_7;
- x = LIB9P_O_RCLOSE;
- x = _LIB9P_O_RESERVED_CEXEC;
- x = LIB9P_O_TRUNC;
- x = _LIB9P_O_UNUSED_3;
- x = _LIB9P_O_UNUSED_2;
- x = LIB9P_O_FLAG_MASK;
- x = LIB9P_O_MODE_READ;
- x = LIB9P_O_MODE_WRITE;
- x = LIB9P_O_MODE_RDWR;
- x = LIB9P_O_MODE_EXEC;
- x = LIB9P_O_MODE_MASK;
- x = LIB9P_ERRNO_NOERROR;
- x = LIB9P_SUPER_MAGIC_V9FS_MAGIC;
- x = _LIB9P_LO_UNUSED_31;
- x = _LIB9P_LO_UNUSED_30;
- x = _LIB9P_LO_UNUSED_29;
- x = _LIB9P_LO_UNUSED_28;
- x = _LIB9P_LO_UNUSED_27;
- x = _LIB9P_LO_UNUSED_26;
- x = _LIB9P_LO_UNUSED_25;
- x = _LIB9P_LO_UNUSED_24;
- x = _LIB9P_LO_UNUSED_23;
- x = _LIB9P_LO_UNUSED_22;
- x = _LIB9P_LO_UNUSED_21;
- x = LIB9P_LO_SYNC;
- x = LIB9P_LO_CLOEXEC;
- x = LIB9P_LO_NOATIME;
- x = LIB9P_LO_NOFOLLOW;
- x = LIB9P_LO_DIRECTORY;
- x = LIB9P_LO_LARGEFILE;
- x = LIB9P_LO_DIRECT;
- x = LIB9P_LO_BSD_FASYNC;
- x = LIB9P_LO_DSYNC;
- x = LIB9P_LO_NONBLOCK;
- x = LIB9P_LO_APPEND;
- x = LIB9P_LO_TRUNC;
- x = LIB9P_LO_NOCTTY;
- x = LIB9P_LO_EXCL;
- x = LIB9P_LO_CREATE;
- x = _LIB9P_LO_UNUSED_5;
- x = _LIB9P_LO_UNUSED_4;
- x = _LIB9P_LO_UNUSED_3;
- x = _LIB9P_LO_UNUSED_2;
- x = LIB9P_LO_FLAG_MASK;
- x = LIB9P_LO_MODE_RDONLY;
- x = LIB9P_LO_MODE_WRONLY;
- x = LIB9P_LO_MODE_RDWR;
- x = LIB9P_LO_MODE_NOACCESS;
- x = LIB9P_LO_MODE_MASK;
- x = LIB9P_DT_UNKNOWN;
- x = LIB9P_DT_PIPE;
- x = LIB9P_DT_CHAR_DEV;
- x = LIB9P_DT_DIRECTORY;
- x = LIB9P_DT_BLOCK_DEV;
- x = LIB9P_DT_REGULAR;
- x = LIB9P_DT_SYMLINK;
- x = LIB9P_DT_SOCKET;
- x = _LIB9P_DT_WHITEOUT;
- x = _LIB9P_MODE_UNUSED_31;
- x = _LIB9P_MODE_UNUSED_30;
- x = _LIB9P_MODE_UNUSED_29;
- x = _LIB9P_MODE_UNUSED_28;
- x = _LIB9P_MODE_UNUSED_27;
- x = _LIB9P_MODE_UNUSED_26;
- x = _LIB9P_MODE_UNUSED_25;
- x = _LIB9P_MODE_UNUSED_24;
- x = _LIB9P_MODE_UNUSED_23;
- x = _LIB9P_MODE_UNUSED_22;
- x = _LIB9P_MODE_UNUSED_21;
- x = _LIB9P_MODE_UNUSED_20;
- x = _LIB9P_MODE_UNUSED_19;
- x = _LIB9P_MODE_UNUSED_18;
- x = _LIB9P_MODE_UNUSED_17;
- x = _LIB9P_MODE_UNUSED_16;
- x = LIB9P_MODE_PERM_SETGROUP;
- x = LIB9P_MODE_PERM_SETUSER;
- x = LIB9P_MODE_PERM_STICKY;
- x = LIB9P_MODE_PERM_OWNER_R;
- x = LIB9P_MODE_PERM_OWNER_W;
- x = LIB9P_MODE_PERM_OWNER_X;
- x = LIB9P_MODE_PERM_GROUP_R;
- x = LIB9P_MODE_PERM_GROUP_W;
- x = LIB9P_MODE_PERM_GROUP_X;
- x = LIB9P_MODE_PERM_OTHER_R;
- x = LIB9P_MODE_PERM_OTHER_W;
- x = LIB9P_MODE_PERM_OTHER_X;
- x = LIB9P_MODE_PERM_MASK;
- x = LIB9P_MODE_FMT_PIPE;
- x = LIB9P_MODE_FMT_CHAR_DEV;
- x = LIB9P_MODE_FMT_DIRECTORY;
- x = LIB9P_MODE_FMT_BLOCK_DEV;
- x = LIB9P_MODE_FMT_REGULAR;
- x = LIB9P_MODE_FMT_SYMLINK;
- x = LIB9P_MODE_FMT_SOCKET;
- x = LIB9P_MODE_FMT_MASK;
- x = LIB9P_B4_FALSE;
- x = LIB9P_B4_TRUE;
- x = _LIB9P_GETATTR_UNUSED_63;
- x = _LIB9P_GETATTR_UNUSED_62;
- x = _LIB9P_GETATTR_UNUSED_61;
- x = _LIB9P_GETATTR_UNUSED_60;
- x = _LIB9P_GETATTR_UNUSED_59;
- x = _LIB9P_GETATTR_UNUSED_58;
- x = _LIB9P_GETATTR_UNUSED_57;
- x = _LIB9P_GETATTR_UNUSED_56;
- x = _LIB9P_GETATTR_UNUSED_55;
- x = _LIB9P_GETATTR_UNUSED_54;
- x = _LIB9P_GETATTR_UNUSED_53;
- x = _LIB9P_GETATTR_UNUSED_52;
- x = _LIB9P_GETATTR_UNUSED_51;
- x = _LIB9P_GETATTR_UNUSED_50;
- x = _LIB9P_GETATTR_UNUSED_49;
- x = _LIB9P_GETATTR_UNUSED_48;
- x = _LIB9P_GETATTR_UNUSED_47;
- x = _LIB9P_GETATTR_UNUSED_46;
- x = _LIB9P_GETATTR_UNUSED_45;
- x = _LIB9P_GETATTR_UNUSED_44;
- x = _LIB9P_GETATTR_UNUSED_43;
- x = _LIB9P_GETATTR_UNUSED_42;
- x = _LIB9P_GETATTR_UNUSED_41;
- x = _LIB9P_GETATTR_UNUSED_40;
- x = _LIB9P_GETATTR_UNUSED_39;
- x = _LIB9P_GETATTR_UNUSED_38;
- x = _LIB9P_GETATTR_UNUSED_37;
- x = _LIB9P_GETATTR_UNUSED_36;
- x = _LIB9P_GETATTR_UNUSED_35;
- x = _LIB9P_GETATTR_UNUSED_34;
- x = _LIB9P_GETATTR_UNUSED_33;
- x = _LIB9P_GETATTR_UNUSED_32;
- x = _LIB9P_GETATTR_UNUSED_31;
- x = _LIB9P_GETATTR_UNUSED_30;
- x = _LIB9P_GETATTR_UNUSED_29;
- x = _LIB9P_GETATTR_UNUSED_28;
- x = _LIB9P_GETATTR_UNUSED_27;
- x = _LIB9P_GETATTR_UNUSED_26;
- x = _LIB9P_GETATTR_UNUSED_25;
- x = _LIB9P_GETATTR_UNUSED_24;
- x = _LIB9P_GETATTR_UNUSED_23;
- x = _LIB9P_GETATTR_UNUSED_22;
- x = _LIB9P_GETATTR_UNUSED_21;
- x = _LIB9P_GETATTR_UNUSED_20;
- x = _LIB9P_GETATTR_UNUSED_19;
- x = _LIB9P_GETATTR_UNUSED_18;
- x = _LIB9P_GETATTR_UNUSED_17;
- x = _LIB9P_GETATTR_UNUSED_16;
- x = _LIB9P_GETATTR_UNUSED_15;
- x = _LIB9P_GETATTR_UNUSED_14;
- x = LIB9P_GETATTR_DATA_VERSION;
- x = LIB9P_GETATTR_GEN;
- x = LIB9P_GETATTR_BTIME;
- x = LIB9P_GETATTR_BLOCKS;
- x = LIB9P_GETATTR_SIZE;
- x = LIB9P_GETATTR_INO;
- x = LIB9P_GETATTR_CTIME;
- x = LIB9P_GETATTR_MTIME;
- x = LIB9P_GETATTR_ATIME;
- x = LIB9P_GETATTR_RDEV;
- x = LIB9P_GETATTR_GID;
- x = LIB9P_GETATTR_UID;
- x = LIB9P_GETATTR_NLINK;
- x = LIB9P_GETATTR_MODE;
- x = LIB9P_GETATTR_BASIC;
- x = LIB9P_GETATTR_ALL;
- x = _LIB9P_SETATTR_UNUSED_31;
- x = _LIB9P_SETATTR_UNUSED_30;
- x = _LIB9P_SETATTR_UNUSED_29;
- x = _LIB9P_SETATTR_UNUSED_28;
- x = _LIB9P_SETATTR_UNUSED_27;
- x = _LIB9P_SETATTR_UNUSED_26;
- x = _LIB9P_SETATTR_UNUSED_25;
- x = _LIB9P_SETATTR_UNUSED_24;
- x = _LIB9P_SETATTR_UNUSED_23;
- x = _LIB9P_SETATTR_UNUSED_22;
- x = _LIB9P_SETATTR_UNUSED_21;
- x = _LIB9P_SETATTR_UNUSED_20;
- x = _LIB9P_SETATTR_UNUSED_19;
- x = _LIB9P_SETATTR_UNUSED_18;
- x = _LIB9P_SETATTR_UNUSED_17;
- x = _LIB9P_SETATTR_UNUSED_16;
- x = _LIB9P_SETATTR_UNUSED_15;
- x = _LIB9P_SETATTR_UNUSED_14;
- x = _LIB9P_SETATTR_UNUSED_13;
- x = _LIB9P_SETATTR_UNUSED_12;
- x = _LIB9P_SETATTR_UNUSED_11;
- x = _LIB9P_SETATTR_UNUSED_10;
- x = _LIB9P_SETATTR_UNUSED_9;
- x = LIB9P_SETATTR_MTIME_SET;
- x = LIB9P_SETATTR_ATIME_SET;
- x = LIB9P_SETATTR_CTIME;
- x = LIB9P_SETATTR_MTIME;
- x = LIB9P_SETATTR_ATIME;
- x = LIB9P_SETATTR_SIZE;
- x = LIB9P_SETATTR_GID;
- x = LIB9P_SETATTR_UID;
- x = LIB9P_SETATTR_MODE;
- x = LIB9P_LOCK_TYPE_RDLCK;
- x = LIB9P_LOCK_TYPE_WRLCK;
- x = LIB9P_LOCK_TYPE_UNLCK;
- x = _LIB9P_LOCK_FLAGS_UNUSED_31;
- x = _LIB9P_LOCK_FLAGS_UNUSED_30;
- x = _LIB9P_LOCK_FLAGS_UNUSED_29;
- x = _LIB9P_LOCK_FLAGS_UNUSED_28;
- x = _LIB9P_LOCK_FLAGS_UNUSED_27;
- x = _LIB9P_LOCK_FLAGS_UNUSED_26;
- x = _LIB9P_LOCK_FLAGS_UNUSED_25;
- x = _LIB9P_LOCK_FLAGS_UNUSED_24;
- x = _LIB9P_LOCK_FLAGS_UNUSED_23;
- x = _LIB9P_LOCK_FLAGS_UNUSED_22;
- x = _LIB9P_LOCK_FLAGS_UNUSED_21;
- x = _LIB9P_LOCK_FLAGS_UNUSED_20;
- x = _LIB9P_LOCK_FLAGS_UNUSED_19;
- x = _LIB9P_LOCK_FLAGS_UNUSED_18;
- x = _LIB9P_LOCK_FLAGS_UNUSED_17;
- x = _LIB9P_LOCK_FLAGS_UNUSED_16;
- x = _LIB9P_LOCK_FLAGS_UNUSED_15;
- x = _LIB9P_LOCK_FLAGS_UNUSED_14;
- x = _LIB9P_LOCK_FLAGS_UNUSED_13;
- x = _LIB9P_LOCK_FLAGS_UNUSED_12;
- x = _LIB9P_LOCK_FLAGS_UNUSED_11;
- x = _LIB9P_LOCK_FLAGS_UNUSED_10;
- x = _LIB9P_LOCK_FLAGS_UNUSED_9;
- x = _LIB9P_LOCK_FLAGS_UNUSED_8;
- x = _LIB9P_LOCK_FLAGS_UNUSED_7;
- x = _LIB9P_LOCK_FLAGS_UNUSED_6;
- x = _LIB9P_LOCK_FLAGS_UNUSED_5;
- x = _LIB9P_LOCK_FLAGS_UNUSED_4;
- x = _LIB9P_LOCK_FLAGS_UNUSED_3;
- x = _LIB9P_LOCK_FLAGS_UNUSED_2;
- x = LIB9P_LOCK_FLAGS_RECLAIM;
- x = LIB9P_LOCK_FLAGS_BLOCK;
- x = LIB9P_LOCK_STATUS_SUCCESS;
- x = LIB9P_LOCK_STATUS_BLOCKED;
- x = LIB9P_LOCK_STATUS_ERROR;
- x = LIB9P_LOCK_STATUS_GRACE;
- x = LIB9P_TMSG_MAX_IOV;
- x = LIB9P_TMSG_MAX_IOV;
- x = LIB9P_TMSG_MAX_COPY;
- x = LIB9P_TMSG_MAX_COPY;
- x = LIB9P_TMSG_MAX_COPY;
- x = LIB9P_TMSG_MAX_COPY;
- x = LIB9P_TMSG_MAX_COPY;
- x = LIB9P_TMSG_MAX_COPY;
- x = LIB9P_RMSG_MAX_IOV;
- x = LIB9P_RMSG_MAX_IOV;
- x = LIB9P_RMSG_MAX_IOV;
- x = LIB9P_RMSG_MAX_COPY;
- return 0;
+ [[maybe_unused]] uint64_t x;
+#ifdef LIB9P_B4_FALSE
+ x = LIB9P_B4_FALSE;
+#endif
+#ifdef LIB9P_B4_TRUE
+ x = LIB9P_B4_TRUE;
+#endif
+#ifdef LIB9P_DM_APPEND
+ x = LIB9P_DM_APPEND;
+#endif
+#ifdef LIB9P_DM_AUTH
+ x = LIB9P_DM_AUTH;
+#endif
+#ifdef LIB9P_DM_DEVICE
+ x = LIB9P_DM_DEVICE;
+#endif
+#ifdef LIB9P_DM_DIR
+ x = LIB9P_DM_DIR;
+#endif
+#ifdef LIB9P_DM_EXCL
+ x = LIB9P_DM_EXCL;
+#endif
+#ifdef LIB9P_DM_GROUP_R
+ x = LIB9P_DM_GROUP_R;
+#endif
+#ifdef LIB9P_DM_GROUP_W
+ x = LIB9P_DM_GROUP_W;
+#endif
+#ifdef LIB9P_DM_GROUP_X
+ x = LIB9P_DM_GROUP_X;
+#endif
+#ifdef LIB9P_DM_OTHER_R
+ x = LIB9P_DM_OTHER_R;
+#endif
+#ifdef LIB9P_DM_OTHER_W
+ x = LIB9P_DM_OTHER_W;
+#endif
+#ifdef LIB9P_DM_OTHER_X
+ x = LIB9P_DM_OTHER_X;
+#endif
+#ifdef LIB9P_DM_OWNER_R
+ x = LIB9P_DM_OWNER_R;
+#endif
+#ifdef LIB9P_DM_OWNER_W
+ x = LIB9P_DM_OWNER_W;
+#endif
+#ifdef LIB9P_DM_OWNER_X
+ x = LIB9P_DM_OWNER_X;
+#endif
+#ifdef LIB9P_DM_PERM_MASK
+ x = LIB9P_DM_PERM_MASK;
+#endif
+#ifdef LIB9P_DM_PIPE
+ x = LIB9P_DM_PIPE;
+#endif
+#ifdef LIB9P_DM_SETGID
+ x = LIB9P_DM_SETGID;
+#endif
+#ifdef LIB9P_DM_SETUID
+ x = LIB9P_DM_SETUID;
+#endif
+#ifdef LIB9P_DM_SOCKET
+ x = LIB9P_DM_SOCKET;
+#endif
+#ifdef LIB9P_DM_TMP
+ x = LIB9P_DM_TMP;
+#endif
+#ifdef LIB9P_DT_BLOCK_DEV
+ x = LIB9P_DT_BLOCK_DEV;
+#endif
+#ifdef LIB9P_DT_CHAR_DEV
+ x = LIB9P_DT_CHAR_DEV;
+#endif
+#ifdef LIB9P_DT_DIRECTORY
+ x = LIB9P_DT_DIRECTORY;
+#endif
+#ifdef LIB9P_DT_PIPE
+ x = LIB9P_DT_PIPE;
+#endif
+#ifdef LIB9P_DT_REGULAR
+ x = LIB9P_DT_REGULAR;
+#endif
+#ifdef LIB9P_DT_SOCKET
+ x = LIB9P_DT_SOCKET;
+#endif
+#ifdef LIB9P_DT_SYMLINK
+ x = LIB9P_DT_SYMLINK;
+#endif
+#ifdef LIB9P_DT_UNKNOWN
+ x = LIB9P_DT_UNKNOWN;
+#endif
+#ifdef LIB9P_ERRNO_L_E2BIG
+ x = LIB9P_ERRNO_L_E2BIG;
+#endif
+#ifdef LIB9P_ERRNO_L_EACCES
+ x = LIB9P_ERRNO_L_EACCES;
+#endif
+#ifdef LIB9P_ERRNO_L_EADDRINUSE
+ x = LIB9P_ERRNO_L_EADDRINUSE;
+#endif
+#ifdef LIB9P_ERRNO_L_EADDRNOTAVAIL
+ x = LIB9P_ERRNO_L_EADDRNOTAVAIL;
+#endif
+#ifdef LIB9P_ERRNO_L_EADV
+ x = LIB9P_ERRNO_L_EADV;
+#endif
+#ifdef LIB9P_ERRNO_L_EAFNOSUPPORT
+ x = LIB9P_ERRNO_L_EAFNOSUPPORT;
+#endif
+#ifdef LIB9P_ERRNO_L_EAGAIN
+ x = LIB9P_ERRNO_L_EAGAIN;
+#endif
+#ifdef LIB9P_ERRNO_L_EALREADY
+ x = LIB9P_ERRNO_L_EALREADY;
+#endif
+#ifdef LIB9P_ERRNO_L_EBADE
+ x = LIB9P_ERRNO_L_EBADE;
+#endif
+#ifdef LIB9P_ERRNO_L_EBADF
+ x = LIB9P_ERRNO_L_EBADF;
+#endif
+#ifdef LIB9P_ERRNO_L_EBADFD
+ x = LIB9P_ERRNO_L_EBADFD;
+#endif
+#ifdef LIB9P_ERRNO_L_EBADMSG
+ x = LIB9P_ERRNO_L_EBADMSG;
+#endif
+#ifdef LIB9P_ERRNO_L_EBADR
+ x = LIB9P_ERRNO_L_EBADR;
+#endif
+#ifdef LIB9P_ERRNO_L_EBADRQC
+ x = LIB9P_ERRNO_L_EBADRQC;
+#endif
+#ifdef LIB9P_ERRNO_L_EBADSLT
+ x = LIB9P_ERRNO_L_EBADSLT;
+#endif
+#ifdef LIB9P_ERRNO_L_EBFONT
+ x = LIB9P_ERRNO_L_EBFONT;
+#endif
+#ifdef LIB9P_ERRNO_L_EBUSY
+ x = LIB9P_ERRNO_L_EBUSY;
+#endif
+#ifdef LIB9P_ERRNO_L_ECANCELED
+ x = LIB9P_ERRNO_L_ECANCELED;
+#endif
+#ifdef LIB9P_ERRNO_L_ECHILD
+ x = LIB9P_ERRNO_L_ECHILD;
+#endif
+#ifdef LIB9P_ERRNO_L_ECHRNG
+ x = LIB9P_ERRNO_L_ECHRNG;
+#endif
+#ifdef LIB9P_ERRNO_L_ECOMM
+ x = LIB9P_ERRNO_L_ECOMM;
+#endif
+#ifdef LIB9P_ERRNO_L_ECONNABORTED
+ x = LIB9P_ERRNO_L_ECONNABORTED;
+#endif
+#ifdef LIB9P_ERRNO_L_ECONNREFUSED
+ x = LIB9P_ERRNO_L_ECONNREFUSED;
+#endif
+#ifdef LIB9P_ERRNO_L_ECONNRESET
+ x = LIB9P_ERRNO_L_ECONNRESET;
+#endif
+#ifdef LIB9P_ERRNO_L_EDEADLK
+ x = LIB9P_ERRNO_L_EDEADLK;
+#endif
+#ifdef LIB9P_ERRNO_L_EDESTADDRREQ
+ x = LIB9P_ERRNO_L_EDESTADDRREQ;
+#endif
+#ifdef LIB9P_ERRNO_L_EDOM
+ x = LIB9P_ERRNO_L_EDOM;
+#endif
+#ifdef LIB9P_ERRNO_L_EDOTDOT
+ x = LIB9P_ERRNO_L_EDOTDOT;
+#endif
+#ifdef LIB9P_ERRNO_L_EDQUOT
+ x = LIB9P_ERRNO_L_EDQUOT;
+#endif
+#ifdef LIB9P_ERRNO_L_EEXIST
+ x = LIB9P_ERRNO_L_EEXIST;
+#endif
+#ifdef LIB9P_ERRNO_L_EFAULT
+ x = LIB9P_ERRNO_L_EFAULT;
+#endif
+#ifdef LIB9P_ERRNO_L_EFBIG
+ x = LIB9P_ERRNO_L_EFBIG;
+#endif
+#ifdef LIB9P_ERRNO_L_EHOSTDOWN
+ x = LIB9P_ERRNO_L_EHOSTDOWN;
+#endif
+#ifdef LIB9P_ERRNO_L_EHOSTUNREACH
+ x = LIB9P_ERRNO_L_EHOSTUNREACH;
+#endif
+#ifdef LIB9P_ERRNO_L_EHWPOISON
+ x = LIB9P_ERRNO_L_EHWPOISON;
+#endif
+#ifdef LIB9P_ERRNO_L_EIDRM
+ x = LIB9P_ERRNO_L_EIDRM;
+#endif
+#ifdef LIB9P_ERRNO_L_EILSEQ
+ x = LIB9P_ERRNO_L_EILSEQ;
+#endif
+#ifdef LIB9P_ERRNO_L_EINPROGRESS
+ x = LIB9P_ERRNO_L_EINPROGRESS;
+#endif
+#ifdef LIB9P_ERRNO_L_EINTR
+ x = LIB9P_ERRNO_L_EINTR;
+#endif
+#ifdef LIB9P_ERRNO_L_EINVAL
+ x = LIB9P_ERRNO_L_EINVAL;
+#endif
+#ifdef LIB9P_ERRNO_L_EIO
+ x = LIB9P_ERRNO_L_EIO;
+#endif
+#ifdef LIB9P_ERRNO_L_EISCONN
+ x = LIB9P_ERRNO_L_EISCONN;
+#endif
+#ifdef LIB9P_ERRNO_L_EISDIR
+ x = LIB9P_ERRNO_L_EISDIR;
+#endif
+#ifdef LIB9P_ERRNO_L_EISNAM
+ x = LIB9P_ERRNO_L_EISNAM;
+#endif
+#ifdef LIB9P_ERRNO_L_EKEYEXPIRED
+ x = LIB9P_ERRNO_L_EKEYEXPIRED;
+#endif
+#ifdef LIB9P_ERRNO_L_EKEYREJECTED
+ x = LIB9P_ERRNO_L_EKEYREJECTED;
+#endif
+#ifdef LIB9P_ERRNO_L_EKEYREVOKED
+ x = LIB9P_ERRNO_L_EKEYREVOKED;
+#endif
+#ifdef LIB9P_ERRNO_L_EL2HLT
+ x = LIB9P_ERRNO_L_EL2HLT;
+#endif
+#ifdef LIB9P_ERRNO_L_EL2NSYNC
+ x = LIB9P_ERRNO_L_EL2NSYNC;
+#endif
+#ifdef LIB9P_ERRNO_L_EL3HLT
+ x = LIB9P_ERRNO_L_EL3HLT;
+#endif
+#ifdef LIB9P_ERRNO_L_EL3RST
+ x = LIB9P_ERRNO_L_EL3RST;
+#endif
+#ifdef LIB9P_ERRNO_L_ELIBACC
+ x = LIB9P_ERRNO_L_ELIBACC;
+#endif
+#ifdef LIB9P_ERRNO_L_ELIBBAD
+ x = LIB9P_ERRNO_L_ELIBBAD;
+#endif
+#ifdef LIB9P_ERRNO_L_ELIBEXEC
+ x = LIB9P_ERRNO_L_ELIBEXEC;
+#endif
+#ifdef LIB9P_ERRNO_L_ELIBMAX
+ x = LIB9P_ERRNO_L_ELIBMAX;
+#endif
+#ifdef LIB9P_ERRNO_L_ELIBSCN
+ x = LIB9P_ERRNO_L_ELIBSCN;
+#endif
+#ifdef LIB9P_ERRNO_L_ELNRNG
+ x = LIB9P_ERRNO_L_ELNRNG;
+#endif
+#ifdef LIB9P_ERRNO_L_ELOOP
+ x = LIB9P_ERRNO_L_ELOOP;
+#endif
+#ifdef LIB9P_ERRNO_L_EMEDIUMTYPE
+ x = LIB9P_ERRNO_L_EMEDIUMTYPE;
+#endif
+#ifdef LIB9P_ERRNO_L_EMFILE
+ x = LIB9P_ERRNO_L_EMFILE;
+#endif
+#ifdef LIB9P_ERRNO_L_EMLINK
+ x = LIB9P_ERRNO_L_EMLINK;
+#endif
+#ifdef LIB9P_ERRNO_L_EMSGSIZE
+ x = LIB9P_ERRNO_L_EMSGSIZE;
+#endif
+#ifdef LIB9P_ERRNO_L_EMULTIHOP
+ x = LIB9P_ERRNO_L_EMULTIHOP;
+#endif
+#ifdef LIB9P_ERRNO_L_ENAMETOOLONG
+ x = LIB9P_ERRNO_L_ENAMETOOLONG;
+#endif
+#ifdef LIB9P_ERRNO_L_ENAVAIL
+ x = LIB9P_ERRNO_L_ENAVAIL;
+#endif
+#ifdef LIB9P_ERRNO_L_ENETDOWN
+ x = LIB9P_ERRNO_L_ENETDOWN;
+#endif
+#ifdef LIB9P_ERRNO_L_ENETRESET
+ x = LIB9P_ERRNO_L_ENETRESET;
+#endif
+#ifdef LIB9P_ERRNO_L_ENETUNREACH
+ x = LIB9P_ERRNO_L_ENETUNREACH;
+#endif
+#ifdef LIB9P_ERRNO_L_ENFILE
+ x = LIB9P_ERRNO_L_ENFILE;
+#endif
+#ifdef LIB9P_ERRNO_L_ENOANO
+ x = LIB9P_ERRNO_L_ENOANO;
+#endif
+#ifdef LIB9P_ERRNO_L_ENOBUFS
+ x = LIB9P_ERRNO_L_ENOBUFS;
+#endif
+#ifdef LIB9P_ERRNO_L_ENOCSI
+ x = LIB9P_ERRNO_L_ENOCSI;
+#endif
+#ifdef LIB9P_ERRNO_L_ENODATA
+ x = LIB9P_ERRNO_L_ENODATA;
+#endif
+#ifdef LIB9P_ERRNO_L_ENODEV
+ x = LIB9P_ERRNO_L_ENODEV;
+#endif
+#ifdef LIB9P_ERRNO_L_ENOENT
+ x = LIB9P_ERRNO_L_ENOENT;
+#endif
+#ifdef LIB9P_ERRNO_L_ENOEXEC
+ x = LIB9P_ERRNO_L_ENOEXEC;
+#endif
+#ifdef LIB9P_ERRNO_L_ENOKEY
+ x = LIB9P_ERRNO_L_ENOKEY;
+#endif
+#ifdef LIB9P_ERRNO_L_ENOLCK
+ x = LIB9P_ERRNO_L_ENOLCK;
+#endif
+#ifdef LIB9P_ERRNO_L_ENOLINK
+ x = LIB9P_ERRNO_L_ENOLINK;
+#endif
+#ifdef LIB9P_ERRNO_L_ENOMEDIUM
+ x = LIB9P_ERRNO_L_ENOMEDIUM;
+#endif
+#ifdef LIB9P_ERRNO_L_ENOMEM
+ x = LIB9P_ERRNO_L_ENOMEM;
+#endif
+#ifdef LIB9P_ERRNO_L_ENOMSG
+ x = LIB9P_ERRNO_L_ENOMSG;
+#endif
+#ifdef LIB9P_ERRNO_L_ENONET
+ x = LIB9P_ERRNO_L_ENONET;
+#endif
+#ifdef LIB9P_ERRNO_L_ENOPKG
+ x = LIB9P_ERRNO_L_ENOPKG;
+#endif
+#ifdef LIB9P_ERRNO_L_ENOPROTOOPT
+ x = LIB9P_ERRNO_L_ENOPROTOOPT;
+#endif
+#ifdef LIB9P_ERRNO_L_ENOSPC
+ x = LIB9P_ERRNO_L_ENOSPC;
+#endif
+#ifdef LIB9P_ERRNO_L_ENOSR
+ x = LIB9P_ERRNO_L_ENOSR;
+#endif
+#ifdef LIB9P_ERRNO_L_ENOSTR
+ x = LIB9P_ERRNO_L_ENOSTR;
+#endif
+#ifdef LIB9P_ERRNO_L_ENOSYS
+ x = LIB9P_ERRNO_L_ENOSYS;
+#endif
+#ifdef LIB9P_ERRNO_L_ENOTBLK
+ x = LIB9P_ERRNO_L_ENOTBLK;
+#endif
+#ifdef LIB9P_ERRNO_L_ENOTCONN
+ x = LIB9P_ERRNO_L_ENOTCONN;
+#endif
+#ifdef LIB9P_ERRNO_L_ENOTDIR
+ x = LIB9P_ERRNO_L_ENOTDIR;
+#endif
+#ifdef LIB9P_ERRNO_L_ENOTEMPTY
+ x = LIB9P_ERRNO_L_ENOTEMPTY;
+#endif
+#ifdef LIB9P_ERRNO_L_ENOTNAM
+ x = LIB9P_ERRNO_L_ENOTNAM;
+#endif
+#ifdef LIB9P_ERRNO_L_ENOTRECOVERABLE
+ x = LIB9P_ERRNO_L_ENOTRECOVERABLE;
+#endif
+#ifdef LIB9P_ERRNO_L_ENOTSOCK
+ x = LIB9P_ERRNO_L_ENOTSOCK;
+#endif
+#ifdef LIB9P_ERRNO_L_ENOTTY
+ x = LIB9P_ERRNO_L_ENOTTY;
+#endif
+#ifdef LIB9P_ERRNO_L_ENOTUNIQ
+ x = LIB9P_ERRNO_L_ENOTUNIQ;
+#endif
+#ifdef LIB9P_ERRNO_L_ENXIO
+ x = LIB9P_ERRNO_L_ENXIO;
+#endif
+#ifdef LIB9P_ERRNO_L_EOPNOTSUPP
+ x = LIB9P_ERRNO_L_EOPNOTSUPP;
+#endif
+#ifdef LIB9P_ERRNO_L_EOVERFLOW
+ x = LIB9P_ERRNO_L_EOVERFLOW;
+#endif
+#ifdef LIB9P_ERRNO_L_EOWNERDEAD
+ x = LIB9P_ERRNO_L_EOWNERDEAD;
+#endif
+#ifdef LIB9P_ERRNO_L_EPERM
+ x = LIB9P_ERRNO_L_EPERM;
+#endif
+#ifdef LIB9P_ERRNO_L_EPFNOSUPPORT
+ x = LIB9P_ERRNO_L_EPFNOSUPPORT;
+#endif
+#ifdef LIB9P_ERRNO_L_EPIPE
+ x = LIB9P_ERRNO_L_EPIPE;
+#endif
+#ifdef LIB9P_ERRNO_L_EPROTO
+ x = LIB9P_ERRNO_L_EPROTO;
+#endif
+#ifdef LIB9P_ERRNO_L_EPROTONOSUPPORT
+ x = LIB9P_ERRNO_L_EPROTONOSUPPORT;
+#endif
+#ifdef LIB9P_ERRNO_L_EPROTOTYPE
+ x = LIB9P_ERRNO_L_EPROTOTYPE;
+#endif
+#ifdef LIB9P_ERRNO_L_ERANGE
+ x = LIB9P_ERRNO_L_ERANGE;
+#endif
+#ifdef LIB9P_ERRNO_L_EREMCHG
+ x = LIB9P_ERRNO_L_EREMCHG;
+#endif
+#ifdef LIB9P_ERRNO_L_EREMOTE
+ x = LIB9P_ERRNO_L_EREMOTE;
+#endif
+#ifdef LIB9P_ERRNO_L_EREMOTEIO
+ x = LIB9P_ERRNO_L_EREMOTEIO;
+#endif
+#ifdef LIB9P_ERRNO_L_ERESTART
+ x = LIB9P_ERRNO_L_ERESTART;
+#endif
+#ifdef LIB9P_ERRNO_L_ERFKILL
+ x = LIB9P_ERRNO_L_ERFKILL;
+#endif
+#ifdef LIB9P_ERRNO_L_EROFS
+ x = LIB9P_ERRNO_L_EROFS;
+#endif
+#ifdef LIB9P_ERRNO_L_ESHUTDOWN
+ x = LIB9P_ERRNO_L_ESHUTDOWN;
+#endif
+#ifdef LIB9P_ERRNO_L_ESOCKTNOSUPPORT
+ x = LIB9P_ERRNO_L_ESOCKTNOSUPPORT;
+#endif
+#ifdef LIB9P_ERRNO_L_ESPIPE
+ x = LIB9P_ERRNO_L_ESPIPE;
+#endif
+#ifdef LIB9P_ERRNO_L_ESRCH
+ x = LIB9P_ERRNO_L_ESRCH;
+#endif
+#ifdef LIB9P_ERRNO_L_ESRMNT
+ x = LIB9P_ERRNO_L_ESRMNT;
+#endif
+#ifdef LIB9P_ERRNO_L_ESTALE
+ x = LIB9P_ERRNO_L_ESTALE;
+#endif
+#ifdef LIB9P_ERRNO_L_ESTRPIPE
+ x = LIB9P_ERRNO_L_ESTRPIPE;
+#endif
+#ifdef LIB9P_ERRNO_L_ETIME
+ x = LIB9P_ERRNO_L_ETIME;
+#endif
+#ifdef LIB9P_ERRNO_L_ETIMEDOUT
+ x = LIB9P_ERRNO_L_ETIMEDOUT;
+#endif
+#ifdef LIB9P_ERRNO_L_ETOOMANYREFS
+ x = LIB9P_ERRNO_L_ETOOMANYREFS;
+#endif
+#ifdef LIB9P_ERRNO_L_ETXTBSY
+ x = LIB9P_ERRNO_L_ETXTBSY;
+#endif
+#ifdef LIB9P_ERRNO_L_EUCLEAN
+ x = LIB9P_ERRNO_L_EUCLEAN;
+#endif
+#ifdef LIB9P_ERRNO_L_EUNATCH
+ x = LIB9P_ERRNO_L_EUNATCH;
+#endif
+#ifdef LIB9P_ERRNO_L_EUSERS
+ x = LIB9P_ERRNO_L_EUSERS;
+#endif
+#ifdef LIB9P_ERRNO_L_EXDEV
+ x = LIB9P_ERRNO_L_EXDEV;
+#endif
+#ifdef LIB9P_ERRNO_L_EXFULL
+ x = LIB9P_ERRNO_L_EXFULL;
+#endif
+#ifdef LIB9P_ERRNO_NOERROR
+ x = LIB9P_ERRNO_NOERROR;
+#endif
+#ifdef LIB9P_FID_NOFID
+ x = LIB9P_FID_NOFID;
+#endif
+#ifdef LIB9P_GETATTR_ALL
+ x = LIB9P_GETATTR_ALL;
+#endif
+#ifdef LIB9P_GETATTR_ATIME
+ x = LIB9P_GETATTR_ATIME;
+#endif
+#ifdef LIB9P_GETATTR_BASIC
+ x = LIB9P_GETATTR_BASIC;
+#endif
+#ifdef LIB9P_GETATTR_BLOCKS
+ x = LIB9P_GETATTR_BLOCKS;
+#endif
+#ifdef LIB9P_GETATTR_BTIME
+ x = LIB9P_GETATTR_BTIME;
+#endif
+#ifdef LIB9P_GETATTR_CTIME
+ x = LIB9P_GETATTR_CTIME;
+#endif
+#ifdef LIB9P_GETATTR_DATA_VERSION
+ x = LIB9P_GETATTR_DATA_VERSION;
+#endif
+#ifdef LIB9P_GETATTR_GEN
+ x = LIB9P_GETATTR_GEN;
+#endif
+#ifdef LIB9P_GETATTR_GID
+ x = LIB9P_GETATTR_GID;
+#endif
+#ifdef LIB9P_GETATTR_INO
+ x = LIB9P_GETATTR_INO;
+#endif
+#ifdef LIB9P_GETATTR_MODE
+ x = LIB9P_GETATTR_MODE;
+#endif
+#ifdef LIB9P_GETATTR_MTIME
+ x = LIB9P_GETATTR_MTIME;
+#endif
+#ifdef LIB9P_GETATTR_NLINK
+ x = LIB9P_GETATTR_NLINK;
+#endif
+#ifdef LIB9P_GETATTR_RDEV
+ x = LIB9P_GETATTR_RDEV;
+#endif
+#ifdef LIB9P_GETATTR_SIZE
+ x = LIB9P_GETATTR_SIZE;
+#endif
+#ifdef LIB9P_GETATTR_UID
+ x = LIB9P_GETATTR_UID;
+#endif
+#ifdef LIB9P_LOCK_FLAGS_BLOCK
+ x = LIB9P_LOCK_FLAGS_BLOCK;
+#endif
+#ifdef LIB9P_LOCK_FLAGS_RECLAIM
+ x = LIB9P_LOCK_FLAGS_RECLAIM;
+#endif
+#ifdef LIB9P_LOCK_STATUS_BLOCKED
+ x = LIB9P_LOCK_STATUS_BLOCKED;
+#endif
+#ifdef LIB9P_LOCK_STATUS_ERROR
+ x = LIB9P_LOCK_STATUS_ERROR;
+#endif
+#ifdef LIB9P_LOCK_STATUS_GRACE
+ x = LIB9P_LOCK_STATUS_GRACE;
+#endif
+#ifdef LIB9P_LOCK_STATUS_SUCCESS
+ x = LIB9P_LOCK_STATUS_SUCCESS;
+#endif
+#ifdef LIB9P_LOCK_TYPE_RDLCK
+ x = LIB9P_LOCK_TYPE_RDLCK;
+#endif
+#ifdef LIB9P_LOCK_TYPE_UNLCK
+ x = LIB9P_LOCK_TYPE_UNLCK;
+#endif
+#ifdef LIB9P_LOCK_TYPE_WRLCK
+ x = LIB9P_LOCK_TYPE_WRLCK;
+#endif
+#ifdef LIB9P_LO_APPEND
+ x = LIB9P_LO_APPEND;
+#endif
+#ifdef LIB9P_LO_BSD_FASYNC
+ x = LIB9P_LO_BSD_FASYNC;
+#endif
+#ifdef LIB9P_LO_CLOEXEC
+ x = LIB9P_LO_CLOEXEC;
+#endif
+#ifdef LIB9P_LO_CREATE
+ x = LIB9P_LO_CREATE;
+#endif
+#ifdef LIB9P_LO_DIRECT
+ x = LIB9P_LO_DIRECT;
+#endif
+#ifdef LIB9P_LO_DIRECTORY
+ x = LIB9P_LO_DIRECTORY;
+#endif
+#ifdef LIB9P_LO_DSYNC
+ x = LIB9P_LO_DSYNC;
+#endif
+#ifdef LIB9P_LO_EXCL
+ x = LIB9P_LO_EXCL;
+#endif
+#ifdef LIB9P_LO_FLAG_MASK
+ x = LIB9P_LO_FLAG_MASK;
+#endif
+#ifdef LIB9P_LO_LARGEFILE
+ x = LIB9P_LO_LARGEFILE;
+#endif
+#ifdef LIB9P_LO_MODE_MASK
+ x = LIB9P_LO_MODE_MASK;
+#endif
+#ifdef LIB9P_LO_MODE_NOACCESS
+ x = LIB9P_LO_MODE_NOACCESS;
+#endif
+#ifdef LIB9P_LO_MODE_RDONLY
+ x = LIB9P_LO_MODE_RDONLY;
+#endif
+#ifdef LIB9P_LO_MODE_RDWR
+ x = LIB9P_LO_MODE_RDWR;
+#endif
+#ifdef LIB9P_LO_MODE_WRONLY
+ x = LIB9P_LO_MODE_WRONLY;
+#endif
+#ifdef LIB9P_LO_NOATIME
+ x = LIB9P_LO_NOATIME;
+#endif
+#ifdef LIB9P_LO_NOCTTY
+ x = LIB9P_LO_NOCTTY;
+#endif
+#ifdef LIB9P_LO_NOFOLLOW
+ x = LIB9P_LO_NOFOLLOW;
+#endif
+#ifdef LIB9P_LO_NONBLOCK
+ x = LIB9P_LO_NONBLOCK;
+#endif
+#ifdef LIB9P_LO_SYNC
+ x = LIB9P_LO_SYNC;
+#endif
+#ifdef LIB9P_LO_TRUNC
+ x = LIB9P_LO_TRUNC;
+#endif
+#ifdef LIB9P_MODE_FMT_BLOCK_DEV
+ x = LIB9P_MODE_FMT_BLOCK_DEV;
+#endif
+#ifdef LIB9P_MODE_FMT_CHAR_DEV
+ x = LIB9P_MODE_FMT_CHAR_DEV;
+#endif
+#ifdef LIB9P_MODE_FMT_DIRECTORY
+ x = LIB9P_MODE_FMT_DIRECTORY;
+#endif
+#ifdef LIB9P_MODE_FMT_MASK
+ x = LIB9P_MODE_FMT_MASK;
+#endif
+#ifdef LIB9P_MODE_FMT_PIPE
+ x = LIB9P_MODE_FMT_PIPE;
+#endif
+#ifdef LIB9P_MODE_FMT_REGULAR
+ x = LIB9P_MODE_FMT_REGULAR;
+#endif
+#ifdef LIB9P_MODE_FMT_SOCKET
+ x = LIB9P_MODE_FMT_SOCKET;
+#endif
+#ifdef LIB9P_MODE_FMT_SYMLINK
+ x = LIB9P_MODE_FMT_SYMLINK;
+#endif
+#ifdef LIB9P_MODE_PERM_GROUP_R
+ x = LIB9P_MODE_PERM_GROUP_R;
+#endif
+#ifdef LIB9P_MODE_PERM_GROUP_W
+ x = LIB9P_MODE_PERM_GROUP_W;
+#endif
+#ifdef LIB9P_MODE_PERM_GROUP_X
+ x = LIB9P_MODE_PERM_GROUP_X;
+#endif
+#ifdef LIB9P_MODE_PERM_MASK
+ x = LIB9P_MODE_PERM_MASK;
+#endif
+#ifdef LIB9P_MODE_PERM_OTHER_R
+ x = LIB9P_MODE_PERM_OTHER_R;
+#endif
+#ifdef LIB9P_MODE_PERM_OTHER_W
+ x = LIB9P_MODE_PERM_OTHER_W;
+#endif
+#ifdef LIB9P_MODE_PERM_OTHER_X
+ x = LIB9P_MODE_PERM_OTHER_X;
+#endif
+#ifdef LIB9P_MODE_PERM_OWNER_R
+ x = LIB9P_MODE_PERM_OWNER_R;
+#endif
+#ifdef LIB9P_MODE_PERM_OWNER_W
+ x = LIB9P_MODE_PERM_OWNER_W;
+#endif
+#ifdef LIB9P_MODE_PERM_OWNER_X
+ x = LIB9P_MODE_PERM_OWNER_X;
+#endif
+#ifdef LIB9P_MODE_PERM_SETGROUP
+ x = LIB9P_MODE_PERM_SETGROUP;
+#endif
+#ifdef LIB9P_MODE_PERM_SETUSER
+ x = LIB9P_MODE_PERM_SETUSER;
+#endif
+#ifdef LIB9P_MODE_PERM_STICKY
+ x = LIB9P_MODE_PERM_STICKY;
+#endif
+#ifdef LIB9P_NUID_NONUID
+ x = LIB9P_NUID_NONUID;
+#endif
+#ifdef LIB9P_O_FLAG_MASK
+ x = LIB9P_O_FLAG_MASK;
+#endif
+#ifdef LIB9P_O_MODE_EXEC
+ x = LIB9P_O_MODE_EXEC;
+#endif
+#ifdef LIB9P_O_MODE_MASK
+ x = LIB9P_O_MODE_MASK;
+#endif
+#ifdef LIB9P_O_MODE_RDWR
+ x = LIB9P_O_MODE_RDWR;
+#endif
+#ifdef LIB9P_O_MODE_READ
+ x = LIB9P_O_MODE_READ;
+#endif
+#ifdef LIB9P_O_MODE_WRITE
+ x = LIB9P_O_MODE_WRITE;
+#endif
+#ifdef LIB9P_O_RCLOSE
+ x = LIB9P_O_RCLOSE;
+#endif
+#ifdef LIB9P_O_TRUNC
+ x = LIB9P_O_TRUNC;
+#endif
+#ifdef LIB9P_QT_APPEND
+ x = LIB9P_QT_APPEND;
+#endif
+#ifdef LIB9P_QT_AUTH
+ x = LIB9P_QT_AUTH;
+#endif
+#ifdef LIB9P_QT_DIR
+ x = LIB9P_QT_DIR;
+#endif
+#ifdef LIB9P_QT_EXCL
+ x = LIB9P_QT_EXCL;
+#endif
+#ifdef LIB9P_QT_FILE
+ x = LIB9P_QT_FILE;
+#endif
+#ifdef LIB9P_QT_SYMLINK
+ x = LIB9P_QT_SYMLINK;
+#endif
+#ifdef LIB9P_QT_TMP
+ x = LIB9P_QT_TMP;
+#endif
+#ifdef LIB9P_RMSG_MAX_COPY
+ x = LIB9P_RMSG_MAX_COPY;
+#endif
+#ifdef LIB9P_RMSG_MAX_IOV
+ x = LIB9P_RMSG_MAX_IOV;
+#endif
+#ifdef LIB9P_SETATTR_ATIME
+ x = LIB9P_SETATTR_ATIME;
+#endif
+#ifdef LIB9P_SETATTR_ATIME_SET
+ x = LIB9P_SETATTR_ATIME_SET;
+#endif
+#ifdef LIB9P_SETATTR_CTIME
+ x = LIB9P_SETATTR_CTIME;
+#endif
+#ifdef LIB9P_SETATTR_GID
+ x = LIB9P_SETATTR_GID;
+#endif
+#ifdef LIB9P_SETATTR_MODE
+ x = LIB9P_SETATTR_MODE;
+#endif
+#ifdef LIB9P_SETATTR_MTIME
+ x = LIB9P_SETATTR_MTIME;
+#endif
+#ifdef LIB9P_SETATTR_MTIME_SET
+ x = LIB9P_SETATTR_MTIME_SET;
+#endif
+#ifdef LIB9P_SETATTR_SIZE
+ x = LIB9P_SETATTR_SIZE;
+#endif
+#ifdef LIB9P_SETATTR_UID
+ x = LIB9P_SETATTR_UID;
+#endif
+#ifdef LIB9P_SUPER_MAGIC_V9FS_MAGIC
+ x = LIB9P_SUPER_MAGIC_V9FS_MAGIC;
+#endif
+#ifdef LIB9P_TAG_NOTAG
+ x = LIB9P_TAG_NOTAG;
+#endif
+#ifdef LIB9P_TMSG_MAX_COPY
+ x = LIB9P_TMSG_MAX_COPY;
+#endif
+#ifdef LIB9P_TMSG_MAX_IOV
+ x = LIB9P_TMSG_MAX_IOV;
+#endif
+#ifdef _LIB9P_DM_PLAN9_MOUNT
+ x = _LIB9P_DM_PLAN9_MOUNT;
+#endif
+#ifdef _LIB9P_DM_UNUSED_10
+ x = _LIB9P_DM_UNUSED_10;
+#endif
+#ifdef _LIB9P_DM_UNUSED_11
+ x = _LIB9P_DM_UNUSED_11;
+#endif
+#ifdef _LIB9P_DM_UNUSED_12
+ x = _LIB9P_DM_UNUSED_12;
+#endif
+#ifdef _LIB9P_DM_UNUSED_13
+ x = _LIB9P_DM_UNUSED_13;
+#endif
+#ifdef _LIB9P_DM_UNUSED_14
+ x = _LIB9P_DM_UNUSED_14;
+#endif
+#ifdef _LIB9P_DM_UNUSED_15
+ x = _LIB9P_DM_UNUSED_15;
+#endif
+#ifdef _LIB9P_DM_UNUSED_16
+ x = _LIB9P_DM_UNUSED_16;
+#endif
+#ifdef _LIB9P_DM_UNUSED_17
+ x = _LIB9P_DM_UNUSED_17;
+#endif
+#ifdef _LIB9P_DM_UNUSED_22
+ x = _LIB9P_DM_UNUSED_22;
+#endif
+#ifdef _LIB9P_DM_UNUSED_24
+ x = _LIB9P_DM_UNUSED_24;
+#endif
+#ifdef _LIB9P_DM_UNUSED_25
+ x = _LIB9P_DM_UNUSED_25;
+#endif
+#ifdef _LIB9P_DM_UNUSED_9
+ x = _LIB9P_DM_UNUSED_9;
+#endif
+#ifdef _LIB9P_DT_WHITEOUT
+ x = _LIB9P_DT_WHITEOUT;
+#endif
+#ifdef _LIB9P_ENABLE_stat
+ x = _LIB9P_ENABLE_stat;
+#endif
+#ifdef _LIB9P_GETATTR_UNUSED_14
+ x = _LIB9P_GETATTR_UNUSED_14;
+#endif
+#ifdef _LIB9P_GETATTR_UNUSED_15
+ x = _LIB9P_GETATTR_UNUSED_15;
+#endif
+#ifdef _LIB9P_GETATTR_UNUSED_16
+ x = _LIB9P_GETATTR_UNUSED_16;
+#endif
+#ifdef _LIB9P_GETATTR_UNUSED_17
+ x = _LIB9P_GETATTR_UNUSED_17;
+#endif
+#ifdef _LIB9P_GETATTR_UNUSED_18
+ x = _LIB9P_GETATTR_UNUSED_18;
+#endif
+#ifdef _LIB9P_GETATTR_UNUSED_19
+ x = _LIB9P_GETATTR_UNUSED_19;
+#endif
+#ifdef _LIB9P_GETATTR_UNUSED_20
+ x = _LIB9P_GETATTR_UNUSED_20;
+#endif
+#ifdef _LIB9P_GETATTR_UNUSED_21
+ x = _LIB9P_GETATTR_UNUSED_21;
+#endif
+#ifdef _LIB9P_GETATTR_UNUSED_22
+ x = _LIB9P_GETATTR_UNUSED_22;
+#endif
+#ifdef _LIB9P_GETATTR_UNUSED_23
+ x = _LIB9P_GETATTR_UNUSED_23;
+#endif
+#ifdef _LIB9P_GETATTR_UNUSED_24
+ x = _LIB9P_GETATTR_UNUSED_24;
+#endif
+#ifdef _LIB9P_GETATTR_UNUSED_25
+ x = _LIB9P_GETATTR_UNUSED_25;
+#endif
+#ifdef _LIB9P_GETATTR_UNUSED_26
+ x = _LIB9P_GETATTR_UNUSED_26;
+#endif
+#ifdef _LIB9P_GETATTR_UNUSED_27
+ x = _LIB9P_GETATTR_UNUSED_27;
+#endif
+#ifdef _LIB9P_GETATTR_UNUSED_28
+ x = _LIB9P_GETATTR_UNUSED_28;
+#endif
+#ifdef _LIB9P_GETATTR_UNUSED_29
+ x = _LIB9P_GETATTR_UNUSED_29;
+#endif
+#ifdef _LIB9P_GETATTR_UNUSED_30
+ x = _LIB9P_GETATTR_UNUSED_30;
+#endif
+#ifdef _LIB9P_GETATTR_UNUSED_31
+ x = _LIB9P_GETATTR_UNUSED_31;
+#endif
+#ifdef _LIB9P_GETATTR_UNUSED_32
+ x = _LIB9P_GETATTR_UNUSED_32;
+#endif
+#ifdef _LIB9P_GETATTR_UNUSED_33
+ x = _LIB9P_GETATTR_UNUSED_33;
+#endif
+#ifdef _LIB9P_GETATTR_UNUSED_34
+ x = _LIB9P_GETATTR_UNUSED_34;
+#endif
+#ifdef _LIB9P_GETATTR_UNUSED_35
+ x = _LIB9P_GETATTR_UNUSED_35;
+#endif
+#ifdef _LIB9P_GETATTR_UNUSED_36
+ x = _LIB9P_GETATTR_UNUSED_36;
+#endif
+#ifdef _LIB9P_GETATTR_UNUSED_37
+ x = _LIB9P_GETATTR_UNUSED_37;
+#endif
+#ifdef _LIB9P_GETATTR_UNUSED_38
+ x = _LIB9P_GETATTR_UNUSED_38;
+#endif
+#ifdef _LIB9P_GETATTR_UNUSED_39
+ x = _LIB9P_GETATTR_UNUSED_39;
+#endif
+#ifdef _LIB9P_GETATTR_UNUSED_40
+ x = _LIB9P_GETATTR_UNUSED_40;
+#endif
+#ifdef _LIB9P_GETATTR_UNUSED_41
+ x = _LIB9P_GETATTR_UNUSED_41;
+#endif
+#ifdef _LIB9P_GETATTR_UNUSED_42
+ x = _LIB9P_GETATTR_UNUSED_42;
+#endif
+#ifdef _LIB9P_GETATTR_UNUSED_43
+ x = _LIB9P_GETATTR_UNUSED_43;
+#endif
+#ifdef _LIB9P_GETATTR_UNUSED_44
+ x = _LIB9P_GETATTR_UNUSED_44;
+#endif
+#ifdef _LIB9P_GETATTR_UNUSED_45
+ x = _LIB9P_GETATTR_UNUSED_45;
+#endif
+#ifdef _LIB9P_GETATTR_UNUSED_46
+ x = _LIB9P_GETATTR_UNUSED_46;
+#endif
+#ifdef _LIB9P_GETATTR_UNUSED_47
+ x = _LIB9P_GETATTR_UNUSED_47;
+#endif
+#ifdef _LIB9P_GETATTR_UNUSED_48
+ x = _LIB9P_GETATTR_UNUSED_48;
+#endif
+#ifdef _LIB9P_GETATTR_UNUSED_49
+ x = _LIB9P_GETATTR_UNUSED_49;
+#endif
+#ifdef _LIB9P_GETATTR_UNUSED_50
+ x = _LIB9P_GETATTR_UNUSED_50;
+#endif
+#ifdef _LIB9P_GETATTR_UNUSED_51
+ x = _LIB9P_GETATTR_UNUSED_51;
+#endif
+#ifdef _LIB9P_GETATTR_UNUSED_52
+ x = _LIB9P_GETATTR_UNUSED_52;
+#endif
+#ifdef _LIB9P_GETATTR_UNUSED_53
+ x = _LIB9P_GETATTR_UNUSED_53;
+#endif
+#ifdef _LIB9P_GETATTR_UNUSED_54
+ x = _LIB9P_GETATTR_UNUSED_54;
+#endif
+#ifdef _LIB9P_GETATTR_UNUSED_55
+ x = _LIB9P_GETATTR_UNUSED_55;
+#endif
+#ifdef _LIB9P_GETATTR_UNUSED_56
+ x = _LIB9P_GETATTR_UNUSED_56;
+#endif
+#ifdef _LIB9P_GETATTR_UNUSED_57
+ x = _LIB9P_GETATTR_UNUSED_57;
+#endif
+#ifdef _LIB9P_GETATTR_UNUSED_58
+ x = _LIB9P_GETATTR_UNUSED_58;
+#endif
+#ifdef _LIB9P_GETATTR_UNUSED_59
+ x = _LIB9P_GETATTR_UNUSED_59;
+#endif
+#ifdef _LIB9P_GETATTR_UNUSED_60
+ x = _LIB9P_GETATTR_UNUSED_60;
+#endif
+#ifdef _LIB9P_GETATTR_UNUSED_61
+ x = _LIB9P_GETATTR_UNUSED_61;
+#endif
+#ifdef _LIB9P_GETATTR_UNUSED_62
+ x = _LIB9P_GETATTR_UNUSED_62;
+#endif
+#ifdef _LIB9P_GETATTR_UNUSED_63
+ x = _LIB9P_GETATTR_UNUSED_63;
+#endif
+#ifdef _LIB9P_LOCK_FLAGS_UNUSED_10
+ x = _LIB9P_LOCK_FLAGS_UNUSED_10;
+#endif
+#ifdef _LIB9P_LOCK_FLAGS_UNUSED_11
+ x = _LIB9P_LOCK_FLAGS_UNUSED_11;
+#endif
+#ifdef _LIB9P_LOCK_FLAGS_UNUSED_12
+ x = _LIB9P_LOCK_FLAGS_UNUSED_12;
+#endif
+#ifdef _LIB9P_LOCK_FLAGS_UNUSED_13
+ x = _LIB9P_LOCK_FLAGS_UNUSED_13;
+#endif
+#ifdef _LIB9P_LOCK_FLAGS_UNUSED_14
+ x = _LIB9P_LOCK_FLAGS_UNUSED_14;
+#endif
+#ifdef _LIB9P_LOCK_FLAGS_UNUSED_15
+ x = _LIB9P_LOCK_FLAGS_UNUSED_15;
+#endif
+#ifdef _LIB9P_LOCK_FLAGS_UNUSED_16
+ x = _LIB9P_LOCK_FLAGS_UNUSED_16;
+#endif
+#ifdef _LIB9P_LOCK_FLAGS_UNUSED_17
+ x = _LIB9P_LOCK_FLAGS_UNUSED_17;
+#endif
+#ifdef _LIB9P_LOCK_FLAGS_UNUSED_18
+ x = _LIB9P_LOCK_FLAGS_UNUSED_18;
+#endif
+#ifdef _LIB9P_LOCK_FLAGS_UNUSED_19
+ x = _LIB9P_LOCK_FLAGS_UNUSED_19;
+#endif
+#ifdef _LIB9P_LOCK_FLAGS_UNUSED_2
+ x = _LIB9P_LOCK_FLAGS_UNUSED_2;
+#endif
+#ifdef _LIB9P_LOCK_FLAGS_UNUSED_20
+ x = _LIB9P_LOCK_FLAGS_UNUSED_20;
+#endif
+#ifdef _LIB9P_LOCK_FLAGS_UNUSED_21
+ x = _LIB9P_LOCK_FLAGS_UNUSED_21;
+#endif
+#ifdef _LIB9P_LOCK_FLAGS_UNUSED_22
+ x = _LIB9P_LOCK_FLAGS_UNUSED_22;
+#endif
+#ifdef _LIB9P_LOCK_FLAGS_UNUSED_23
+ x = _LIB9P_LOCK_FLAGS_UNUSED_23;
+#endif
+#ifdef _LIB9P_LOCK_FLAGS_UNUSED_24
+ x = _LIB9P_LOCK_FLAGS_UNUSED_24;
+#endif
+#ifdef _LIB9P_LOCK_FLAGS_UNUSED_25
+ x = _LIB9P_LOCK_FLAGS_UNUSED_25;
+#endif
+#ifdef _LIB9P_LOCK_FLAGS_UNUSED_26
+ x = _LIB9P_LOCK_FLAGS_UNUSED_26;
+#endif
+#ifdef _LIB9P_LOCK_FLAGS_UNUSED_27
+ x = _LIB9P_LOCK_FLAGS_UNUSED_27;
+#endif
+#ifdef _LIB9P_LOCK_FLAGS_UNUSED_28
+ x = _LIB9P_LOCK_FLAGS_UNUSED_28;
+#endif
+#ifdef _LIB9P_LOCK_FLAGS_UNUSED_29
+ x = _LIB9P_LOCK_FLAGS_UNUSED_29;
+#endif
+#ifdef _LIB9P_LOCK_FLAGS_UNUSED_3
+ x = _LIB9P_LOCK_FLAGS_UNUSED_3;
+#endif
+#ifdef _LIB9P_LOCK_FLAGS_UNUSED_30
+ x = _LIB9P_LOCK_FLAGS_UNUSED_30;
+#endif
+#ifdef _LIB9P_LOCK_FLAGS_UNUSED_31
+ x = _LIB9P_LOCK_FLAGS_UNUSED_31;
+#endif
+#ifdef _LIB9P_LOCK_FLAGS_UNUSED_4
+ x = _LIB9P_LOCK_FLAGS_UNUSED_4;
+#endif
+#ifdef _LIB9P_LOCK_FLAGS_UNUSED_5
+ x = _LIB9P_LOCK_FLAGS_UNUSED_5;
+#endif
+#ifdef _LIB9P_LOCK_FLAGS_UNUSED_6
+ x = _LIB9P_LOCK_FLAGS_UNUSED_6;
+#endif
+#ifdef _LIB9P_LOCK_FLAGS_UNUSED_7
+ x = _LIB9P_LOCK_FLAGS_UNUSED_7;
+#endif
+#ifdef _LIB9P_LOCK_FLAGS_UNUSED_8
+ x = _LIB9P_LOCK_FLAGS_UNUSED_8;
+#endif
+#ifdef _LIB9P_LOCK_FLAGS_UNUSED_9
+ x = _LIB9P_LOCK_FLAGS_UNUSED_9;
+#endif
+#ifdef _LIB9P_LO_UNUSED_2
+ x = _LIB9P_LO_UNUSED_2;
+#endif
+#ifdef _LIB9P_LO_UNUSED_21
+ x = _LIB9P_LO_UNUSED_21;
+#endif
+#ifdef _LIB9P_LO_UNUSED_22
+ x = _LIB9P_LO_UNUSED_22;
+#endif
+#ifdef _LIB9P_LO_UNUSED_23
+ x = _LIB9P_LO_UNUSED_23;
+#endif
+#ifdef _LIB9P_LO_UNUSED_24
+ x = _LIB9P_LO_UNUSED_24;
+#endif
+#ifdef _LIB9P_LO_UNUSED_25
+ x = _LIB9P_LO_UNUSED_25;
+#endif
+#ifdef _LIB9P_LO_UNUSED_26
+ x = _LIB9P_LO_UNUSED_26;
+#endif
+#ifdef _LIB9P_LO_UNUSED_27
+ x = _LIB9P_LO_UNUSED_27;
+#endif
+#ifdef _LIB9P_LO_UNUSED_28
+ x = _LIB9P_LO_UNUSED_28;
+#endif
+#ifdef _LIB9P_LO_UNUSED_29
+ x = _LIB9P_LO_UNUSED_29;
+#endif
+#ifdef _LIB9P_LO_UNUSED_3
+ x = _LIB9P_LO_UNUSED_3;
+#endif
+#ifdef _LIB9P_LO_UNUSED_30
+ x = _LIB9P_LO_UNUSED_30;
+#endif
+#ifdef _LIB9P_LO_UNUSED_31
+ x = _LIB9P_LO_UNUSED_31;
+#endif
+#ifdef _LIB9P_LO_UNUSED_4
+ x = _LIB9P_LO_UNUSED_4;
+#endif
+#ifdef _LIB9P_LO_UNUSED_5
+ x = _LIB9P_LO_UNUSED_5;
+#endif
+#ifdef _LIB9P_MODE_UNUSED_16
+ x = _LIB9P_MODE_UNUSED_16;
+#endif
+#ifdef _LIB9P_MODE_UNUSED_17
+ x = _LIB9P_MODE_UNUSED_17;
+#endif
+#ifdef _LIB9P_MODE_UNUSED_18
+ x = _LIB9P_MODE_UNUSED_18;
+#endif
+#ifdef _LIB9P_MODE_UNUSED_19
+ x = _LIB9P_MODE_UNUSED_19;
+#endif
+#ifdef _LIB9P_MODE_UNUSED_20
+ x = _LIB9P_MODE_UNUSED_20;
+#endif
+#ifdef _LIB9P_MODE_UNUSED_21
+ x = _LIB9P_MODE_UNUSED_21;
+#endif
+#ifdef _LIB9P_MODE_UNUSED_22
+ x = _LIB9P_MODE_UNUSED_22;
+#endif
+#ifdef _LIB9P_MODE_UNUSED_23
+ x = _LIB9P_MODE_UNUSED_23;
+#endif
+#ifdef _LIB9P_MODE_UNUSED_24
+ x = _LIB9P_MODE_UNUSED_24;
+#endif
+#ifdef _LIB9P_MODE_UNUSED_25
+ x = _LIB9P_MODE_UNUSED_25;
+#endif
+#ifdef _LIB9P_MODE_UNUSED_26
+ x = _LIB9P_MODE_UNUSED_26;
+#endif
+#ifdef _LIB9P_MODE_UNUSED_27
+ x = _LIB9P_MODE_UNUSED_27;
+#endif
+#ifdef _LIB9P_MODE_UNUSED_28
+ x = _LIB9P_MODE_UNUSED_28;
+#endif
+#ifdef _LIB9P_MODE_UNUSED_29
+ x = _LIB9P_MODE_UNUSED_29;
+#endif
+#ifdef _LIB9P_MODE_UNUSED_30
+ x = _LIB9P_MODE_UNUSED_30;
+#endif
+#ifdef _LIB9P_MODE_UNUSED_31
+ x = _LIB9P_MODE_UNUSED_31;
+#endif
+#ifdef _LIB9P_O_RESERVED_CEXEC
+ x = _LIB9P_O_RESERVED_CEXEC;
+#endif
+#ifdef _LIB9P_O_UNUSED_2
+ x = _LIB9P_O_UNUSED_2;
+#endif
+#ifdef _LIB9P_O_UNUSED_3
+ x = _LIB9P_O_UNUSED_3;
+#endif
+#ifdef _LIB9P_O_UNUSED_7
+ x = _LIB9P_O_UNUSED_7;
+#endif
+#ifdef _LIB9P_QT_PLAN9_MOUNT
+ x = _LIB9P_QT_PLAN9_MOUNT;
+#endif
+#ifdef _LIB9P_QT_UNUSED_0
+ x = _LIB9P_QT_UNUSED_0;
+#endif
+#ifdef _LIB9P_SETATTR_UNUSED_10
+ x = _LIB9P_SETATTR_UNUSED_10;
+#endif
+#ifdef _LIB9P_SETATTR_UNUSED_11
+ x = _LIB9P_SETATTR_UNUSED_11;
+#endif
+#ifdef _LIB9P_SETATTR_UNUSED_12
+ x = _LIB9P_SETATTR_UNUSED_12;
+#endif
+#ifdef _LIB9P_SETATTR_UNUSED_13
+ x = _LIB9P_SETATTR_UNUSED_13;
+#endif
+#ifdef _LIB9P_SETATTR_UNUSED_14
+ x = _LIB9P_SETATTR_UNUSED_14;
+#endif
+#ifdef _LIB9P_SETATTR_UNUSED_15
+ x = _LIB9P_SETATTR_UNUSED_15;
+#endif
+#ifdef _LIB9P_SETATTR_UNUSED_16
+ x = _LIB9P_SETATTR_UNUSED_16;
+#endif
+#ifdef _LIB9P_SETATTR_UNUSED_17
+ x = _LIB9P_SETATTR_UNUSED_17;
+#endif
+#ifdef _LIB9P_SETATTR_UNUSED_18
+ x = _LIB9P_SETATTR_UNUSED_18;
+#endif
+#ifdef _LIB9P_SETATTR_UNUSED_19
+ x = _LIB9P_SETATTR_UNUSED_19;
+#endif
+#ifdef _LIB9P_SETATTR_UNUSED_20
+ x = _LIB9P_SETATTR_UNUSED_20;
+#endif
+#ifdef _LIB9P_SETATTR_UNUSED_21
+ x = _LIB9P_SETATTR_UNUSED_21;
+#endif
+#ifdef _LIB9P_SETATTR_UNUSED_22
+ x = _LIB9P_SETATTR_UNUSED_22;
+#endif
+#ifdef _LIB9P_SETATTR_UNUSED_23
+ x = _LIB9P_SETATTR_UNUSED_23;
+#endif
+#ifdef _LIB9P_SETATTR_UNUSED_24
+ x = _LIB9P_SETATTR_UNUSED_24;
+#endif
+#ifdef _LIB9P_SETATTR_UNUSED_25
+ x = _LIB9P_SETATTR_UNUSED_25;
+#endif
+#ifdef _LIB9P_SETATTR_UNUSED_26
+ x = _LIB9P_SETATTR_UNUSED_26;
+#endif
+#ifdef _LIB9P_SETATTR_UNUSED_27
+ x = _LIB9P_SETATTR_UNUSED_27;
+#endif
+#ifdef _LIB9P_SETATTR_UNUSED_28
+ x = _LIB9P_SETATTR_UNUSED_28;
+#endif
+#ifdef _LIB9P_SETATTR_UNUSED_29
+ x = _LIB9P_SETATTR_UNUSED_29;
+#endif
+#ifdef _LIB9P_SETATTR_UNUSED_30
+ x = _LIB9P_SETATTR_UNUSED_30;
+#endif
+#ifdef _LIB9P_SETATTR_UNUSED_31
+ x = _LIB9P_SETATTR_UNUSED_31;
+#endif
+#ifdef _LIB9P_SETATTR_UNUSED_9
+ x = _LIB9P_SETATTR_UNUSED_9;
+#endif
+ return 0;
}
diff --git a/lib9p/tests/test_compile.c.gen b/lib9p/tests/test_compile.c.gen
index 47046b3..ef2aee6 100755
--- a/lib9p/tests/test_compile.c.gen
+++ b/lib9p/tests/test_compile.c.gen
@@ -1,4 +1,4 @@
-#!/bin/sh
+#!/usr/bin/env bash
# lib9p/tests/test_compile.c.gen - Generate code to make sure all generated macros work
#
# Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com>
@@ -10,10 +10,10 @@ outfile=$2
{
echo "/* ${outfile} - Generated by $0. DO NOT EDIT! */"
echo
- echo "#include <lib9p/9p.h>"
+ echo "#include <lib9p/core.h>"
echo 'int main(void) {'
- echo ' [[gnu::unused]] uint64_t x;'
- sed -nE 's/^\s*#\s*define\s*(\S[^ (]*)\s.*/ x = \1;/p' <"$generated_h"
- echo ' return 0;'
+ echo $'\t[[maybe_unused]] uint64_t x;'
+ <"$generated_h" sed -nE 's/^\s*#\s*define\s*(\S[^ (]*)\s.*/\1/p' | LC_COLLATE=C sort -u | sed $'s/.*/#ifdef &\\n\tx = &;\\n#endif/'
+ echo $'\treturn 0;'
echo '}'
} >"$outfile"
diff --git a/lib9p/tests/test_compile_config/config.h b/lib9p/tests/test_compile_config/config.h
index cc8eec1..c0846ef 100644
--- a/lib9p/tests/test_compile_config/config.h
+++ b/lib9p/tests/test_compile_config/config.h
@@ -1,4 +1,4 @@
-/* config.h - Compile-time configuration for lib9p/test/test_compile
+/* lib9p/tests/test_compile_config/config.h - Compile-time configuration for lib9p/test/test_compile
*
* Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
@@ -9,30 +9,18 @@
/* 9P *************************************************************************/
-#define CONFIG_9P_MAX_ERR_SIZE 128
#define CONFIG_9P_MAX_9P2000_e_WELEM 16
+/* 9P_SRV *********************************************************************/
+
+#define CONFIG_9P_SRV_DEBUG 1
+#define CONFIG_9P_SRV_MAX_ERR_SIZE 128
#define CONFIG_9P_SRV_MAX_MSG_SIZE ((4*1024)+24)
#define CONFIG_9P_SRV_MAX_HOSTMSG_SIZE CONFIG_9P_SRV_MAX_MSG_SIZE+16
-#define CONFIG_9P_SRV_MAX_FIDS 16
-#define CONFIG_9P_SRV_MAX_REQS 2
-#define CONFIG_9P_SRV_MAX_DEPTH 3
-
-#define CONFIG_9P_ENABLE_9P2000 1 /* bool */
-#define CONFIG_9P_ENABLE_9P2000_u 1 /* bool */
-#define CONFIG_9P_ENABLE_9P2000_e 1 /* bool */
-#define CONFIG_9P_ENABLE_9P2000_L 1 /* bool */
-#define CONFIG_9P_ENABLE_9P2000_p9p 1 /* bool */
/* COROUTINE ******************************************************************/
-#define CONFIG_COROUTINE_STACK_SIZE_DEFAULT (32*1024)
#define CONFIG_COROUTINE_NAME_LEN 16
#define CONFIG_COROUTINE_MEASURE_STACK 1 /* bool */
-#define CONFIG_COROUTINE_PROTECT_STACK 1 /* bool */
-#define CONFIG_COROUTINE_DEBUG 0 /* bool */
-#define CONFIG_COROUTINE_VALGRIND 1 /* bool */
-#define CONFIG_COROUTINE_GDB 1 /* bool */
-#define CONFIG_COROUTINE_NUM 2
#endif /* _CONFIG_H_ */
diff --git a/lib9p/tests/test_server/CMakeLists.txt b/lib9p/tests/test_server/CMakeLists.txt
index 5313917..bdb46e2 100644
--- a/lib9p/tests/test_server/CMakeLists.txt
+++ b/lib9p/tests/test_server/CMakeLists.txt
@@ -9,6 +9,9 @@ if (PICO_PLATFORM STREQUAL "host")
add_library(test_server_objs OBJECT
main.c
+ fs_flush.c
+ fs_shutdown.c
+ fs_whoami.c
)
target_include_directories(test_server_objs PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/config)
target_include_directories(test_server_objs PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
@@ -16,20 +19,19 @@ target_link_libraries(test_server_objs
libcr
libcr_ipc
libmisc
- lib9p
lib9p_util
libhw_cr
)
# Analyze the stack ############################################################
-add_stack_analysis(test_server_stack.c test_server_objs)
+#add_stack_analysis(stack.c test_server_objs)
# Link #########################################################################
add_executable(test_server)
target_sources(test_server PRIVATE
- test_server_stack.c
+ #stack.c
"$<TARGET_OBJECTS:test_server_objs>"
)
diff --git a/lib9p/tests/test_server/config/config.h b/lib9p/tests/test_server/config/config.h
index 03143e1..4350529 100644
--- a/lib9p/tests/test_server/config/config.h
+++ b/lib9p/tests/test_server/config/config.h
@@ -1,4 +1,4 @@
-/* config.h - Compile-time configuration for lib9p/test/test_server
+/* lib9p/tests/test_server/config/config.h - Compile-time configuration for lib9p/test/test_server
*
* Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
@@ -7,12 +7,20 @@
#ifndef _CONFIG_H_
#define _CONFIG_H_
-#define _CONFIG_9P_NUM_SOCKS 8
-#define CONFIG_SRV9P_NUM_CONNS _CONFIG_9P_NUM_SOCKS
+#define _CONFIG_9P_MAX_CONNS 8
+#define _CONFIG_9P_MAX_REQS (2*_CONFIG_9P_MAX_CONNS)
/* 9P *************************************************************************/
-#define CONFIG_9P_MAX_ERR_SIZE 128 /* 128 is what Plan 9 4e uses */
+#define CONFIG_9P_ENABLE_9P2000 1 /* bool */
+#define CONFIG_9P_ENABLE_9P2000_u 1 /* bool */
+#define CONFIG_9P_ENABLE_9P2000_e 0 /* bool */
+#define CONFIG_9P_ENABLE_9P2000_L 0 /* bool */
+#define CONFIG_9P_ENABLE_9P2000_p9p 0 /* bool */
+
+/* 9P_SRV *********************************************************************/
+
+#define CONFIG_9P_SRV_DEBUG 1 /* bool */
/**
* This max-msg-size is sized so that a Twrite message can return
@@ -32,21 +40,13 @@
* (8*1024)+160 in 2e and 3e.
*/
#define CONFIG_9P_SRV_MAX_MSG_SIZE ((4*1024)+24)
+#define CONFIG_9P_SRV_MAX_ERR_SIZE 128 /* 128 is what Plan 9 4e uses */
/**
* Maximum host-data-structure size. A message may be larger in
* unmarshaled-host-structures than marshaled-net-bytes due to (1)
* struct padding, (2) array pointers.
*/
#define CONFIG_9P_SRV_MAX_HOSTMSG_SIZE CONFIG_9P_SRV_MAX_MSG_SIZE+16
-#define CONFIG_9P_SRV_MAX_FIDS 16
-#define CONFIG_9P_SRV_MAX_REQS 2
-#define CONFIG_9P_SRV_MAX_DEPTH 3
-
-#define CONFIG_9P_ENABLE_9P2000 1 /* bool */
-#define CONFIG_9P_ENABLE_9P2000_u 1 /* bool */
-#define CONFIG_9P_ENABLE_9P2000_e 0 /* bool */
-#define CONFIG_9P_ENABLE_9P2000_L 0 /* bool */
-#define CONFIG_9P_ENABLE_9P2000_p9p 0 /* bool */
/* COROUTINE ******************************************************************/
@@ -57,10 +57,10 @@
#define CONFIG_COROUTINE_DEBUG 0 /* bool */
#define CONFIG_COROUTINE_VALGRIND 1 /* bool */
#define CONFIG_COROUTINE_GDB 1 /* bool */
-#define CONFIG_COROUTINE_NUM ( \
- 1 /* usb_common */ + \
- 1 /* usb_keyboard */ + \
- CONFIG_SRV9P_NUM_CONNS /* accept+read */ + \
- (CONFIG_9P_SRV_MAX_REQS*CONFIG_SRV9P_NUM_CONNS) /* work+write */ )
+#define CONFIG_COROUTINE_NUM ( \
+ 1 /* usb_common */ + \
+ 1 /* usb_keyboard */ + \
+ _CONFIG_9P_MAX_CONNS /* accept+read */ + \
+ _CONFIG_9P_MAX_REQS /* work+write */ )
#endif /* _CONFIG_H_ */
diff --git a/lib9p/tests/test_server/fs_flush.c b/lib9p/tests/test_server/fs_flush.c
new file mode 100644
index 0000000..c8152d4
--- /dev/null
+++ b/lib9p/tests/test_server/fs_flush.c
@@ -0,0 +1,128 @@
+/* lib9p/tests/test_server/fs_flush.c - flush-* API endpoints
+ *
+ * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#include <libmisc/alloc.h>
+
+#define IMPLEMENTATION_FOR_LIB9P_SRV_H YES /* for ctx->flush_ch */
+#include "fs_flush.h"
+
+LO_IMPLEMENTATION_C(lib9p_srv_file, struct flush_file, flush_file);
+
+struct flush_fio {
+ struct flush_file *parent;
+};
+LO_IMPLEMENTATION_STATIC(lib9p_srv_fio, struct flush_fio, flush_fio);
+
+/* srv_file *******************************************************************/
+
+void flush_file_free(struct flush_file *self) {
+ assert(self);
+}
+struct lib9p_qid flush_file_qid(struct flush_file *self) {
+ assert(self);
+ return (struct lib9p_qid){
+ .type = LIB9P_QT_FILE,
+ .vers = 1,
+ .path = self->pathnum,
+ };
+}
+
+error flush_file_stat(struct flush_file *self, struct lib9p_srv_ctx *ctx, struct lib9p_srv_stat *out) {
+ assert(self);
+ assert(ctx);
+ assert(out);
+
+ *out = ((struct lib9p_srv_stat){
+ .qid = flush_file_qid(self),
+ .mode = 0444,
+ .atime_sec = UTIL9P_ATIME,
+ .mtime_sec = UTIL9P_MTIME,
+ .size = 6,
+ .name = lib9p_str(self->name),
+ .owner_uid = { .name = lib9p_str("root"), .num = 0 },
+ .owner_gid = { .name = lib9p_str("root"), .num = 0 },
+ .last_modifier_uid = { .name = lib9p_str("root"), .num = 0 },
+ .extension = lib9p_str(NULL),
+ });
+ return ERROR_NULL;
+}
+error flush_file_wstat(struct flush_file *self, struct lib9p_srv_ctx *ctx, struct lib9p_srv_stat) {
+ assert(self);
+ assert(ctx);
+ return error_new(E_POSIX_EROFS, "cannot wstat API file");
+}
+error flush_file_remove(struct flush_file *self, struct lib9p_srv_ctx *ctx) {
+ assert(self);
+ assert(ctx);
+ return error_new(E_POSIX_EROFS, "cannot remove API file");
+}
+
+LIB9P_SRV_NOTDIR(, struct flush_file, flush_file);
+
+lib9p_srv_fio_or_error flush_file_fopen(struct flush_file *self, struct lib9p_srv_ctx *ctx, bool, bool, bool) {
+ assert(self);
+ assert(ctx);
+
+ struct flush_fio *ret = heap_alloc(1, struct flush_fio);
+ ret->parent = self;
+
+ return ERROR_NEW_VAL(lib9p_srv_fio, LO_BOX(lib9p_srv_fio, ret));
+}
+
+/* srv_fio ********************************************************************/
+
+static void flush_fio_iofree(struct flush_fio *self) {
+ assert(self);
+ free(self);
+}
+
+static struct lib9p_qid flush_fio_ioqid(struct flush_fio *self) {
+ assert(self);
+ return flush_file_qid(self->parent);
+}
+
+static uint32_t flush_fio_iounit(struct flush_fio *self) {
+ assert(self);
+ return 0;
+}
+
+static uint32_t_or_error flush_fio_pwrite(struct flush_fio *LM_UNUSED(self), struct lib9p_srv_ctx *LM_UNUSED(ctx),
+ const void *LM_UNUSED(buf),
+ uint32_t LM_UNUSED(byte_count),
+ uint64_t LM_UNUSED(offset)) {
+ assert_notreached("not writable");
+}
+
+static error flush_fio_pread(struct flush_fio *self, struct lib9p_srv_ctx *ctx,
+ lo_interface io_writer dst, uint64_t LM_UNUSED(src_offset), uint32_t byte_count) {
+ assert(self);
+ assert(ctx);
+
+ /* Wait for first Tflush */
+ while (!lib9p_srv_flush_requested(ctx))
+ cr_yield();
+
+ /* Wait for the specified number of Tflush (may be higher *or*
+ * lower than 1; lower would mean that the first Tflush needs
+ * to be flushed itself). */
+ while (cr_chan_num_waiters(&ctx->flush_ch) != self->parent->flush_cnt)
+ cr_yield();
+
+ /* Yield one more time, just because. */
+ cr_yield();
+
+ /* Return */
+ switch (self->parent->flush_behavior) {
+ case FLUSH_READ:
+ return io_write(dst, "Sloth\n", 6 < byte_count ? 6 : byte_count).err;
+ case FLUSH_ERROR:
+ return error_new(E_POSIX_EAGAIN, "request canceled by flush");
+ case FLUSH_SILENT:
+ return error_new(E_POSIX_ECANCELED, "request canceled by flush");
+ default:
+ assert_notreached("invalid flush_behavior");
+ }
+}
diff --git a/lib9p/tests/test_server/fs_flush.h b/lib9p/tests/test_server/fs_flush.h
new file mode 100644
index 0000000..023434b
--- /dev/null
+++ b/lib9p/tests/test_server/fs_flush.h
@@ -0,0 +1,26 @@
+/* lib9p/tests/test_server/fs_flush.h - flush-* API endpoints
+ *
+ * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#ifndef _LIB9P_TESTS_TEST_SERVER_FS_FLUSH_H_
+#define _LIB9P_TESTS_TEST_SERVER_FS_FLUSH_H_
+
+#include <libhw/host_net.h>
+#include <util9p/static.h>
+
+struct flush_file {
+ char *name;
+ uint64_t pathnum;
+
+ unsigned int flush_cnt;
+ enum {
+ FLUSH_READ,
+ FLUSH_ERROR,
+ FLUSH_SILENT,
+ } flush_behavior;
+};
+LO_IMPLEMENTATION_H(lib9p_srv_file, struct flush_file, flush_file);
+
+#endif /* _LIB9P_TESTS_TEST_SERVER_FS_FLUSH_H_ */
diff --git a/lib9p/tests/test_server/fs_shutdown.c b/lib9p/tests/test_server/fs_shutdown.c
new file mode 100644
index 0000000..53a243b
--- /dev/null
+++ b/lib9p/tests/test_server/fs_shutdown.c
@@ -0,0 +1,108 @@
+/* lib9p/tests/test_server/fs_shutdown.c - /shutdown API endpoint
+ *
+ * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#include <libmisc/alloc.h>
+
+#include "fs_shutdown.h"
+
+LO_IMPLEMENTATION_C(lib9p_srv_file, struct shutdown_file, shutdown_file);
+
+struct shutdown_fio {
+ struct shutdown_file *parent;
+};
+LO_IMPLEMENTATION_STATIC(lib9p_srv_fio, struct shutdown_fio, shutdown_fio);
+
+/* srv_file *******************************************************************/
+
+void shutdown_file_free(struct shutdown_file *self) {
+ assert(self);
+}
+struct lib9p_qid shutdown_file_qid(struct shutdown_file *self) {
+ assert(self);
+ return (struct lib9p_qid){
+ .type = LIB9P_QT_FILE | LIB9P_QT_APPEND,
+ .vers = 1,
+ .path = self->pathnum,
+ };
+}
+
+error shutdown_file_stat(struct shutdown_file *self, struct lib9p_srv_ctx *ctx, struct lib9p_srv_stat *out) {
+ assert(self);
+ assert(ctx);
+ assert(out);
+
+ *out = ((struct lib9p_srv_stat){
+ .qid = shutdown_file_qid(self),
+ .mode = 0222 | LIB9P_DM_APPEND,
+ .atime_sec = UTIL9P_ATIME,
+ .mtime_sec = UTIL9P_MTIME,
+ .size = 0,
+ .name = lib9p_str(self->name),
+ .owner_uid = { .name=lib9p_str("root"), .num=0 },
+ .owner_gid = { .name=lib9p_str("root"), .num=0 },
+ .last_modifier_uid = { .name=lib9p_str("root"), .num=0 },
+ .extension = lib9p_str(NULL),
+ });
+ return ERROR_NULL;
+}
+error shutdown_file_wstat(struct shutdown_file *self, struct lib9p_srv_ctx *ctx, struct lib9p_srv_stat) {
+ assert(self);
+ assert(ctx);
+ return error_new(E_POSIX_EROFS, "cannot wstat API file");
+}
+error shutdown_file_remove(struct shutdown_file *self, struct lib9p_srv_ctx *ctx) {
+ assert(self);
+ assert(ctx);
+ return error_new(E_POSIX_EROFS, "cannot remove API file");
+}
+
+LIB9P_SRV_NOTDIR(, struct shutdown_file, shutdown_file);
+
+lib9p_srv_fio_or_error shutdown_file_fopen(struct shutdown_file *self, struct lib9p_srv_ctx *ctx, bool, bool, bool) {
+ assert(self);
+ assert(ctx);
+
+ struct shutdown_fio *ret = heap_alloc(1, struct shutdown_fio);
+ ret->parent = self;
+
+ return ERROR_NEW_VAL(lib9p_srv_fio, LO_BOX(lib9p_srv_fio, ret));
+}
+
+/* srv_fio ********************************************************************/
+
+static void shutdown_fio_iofree(struct shutdown_fio *self) {
+ assert(self);
+ free(self);
+}
+
+static struct lib9p_qid shutdown_fio_ioqid(struct shutdown_fio *self) {
+ assert(self);
+ return shutdown_file_qid(self->parent);
+}
+
+static uint32_t shutdown_fio_iounit(struct shutdown_fio *self) {
+ assert(self);
+ return 0;
+}
+
+static uint32_t_or_error shutdown_fio_pwrite(struct shutdown_fio *self, struct lib9p_srv_ctx *ctx,
+ const void *buf,
+ uint32_t byte_count,
+ uint64_t offset) {
+ assert(self);
+ assert(ctx);
+ assert(buf);
+ assert(offset == 0);
+ if (byte_count == 0)
+ return ERROR_NEW_VAL(uint32_t, 0);
+ for (size_t i = 0; i < self->parent->nlisteners; i++)
+ LO_CALL(LO_BOX(net_stream_listener, &self->parent->listeners[i]), close);
+ return ERROR_NEW_VAL(uint32_t, byte_count);
+}
+static error shutdown_fio_pread(struct shutdown_fio *LM_UNUSED(self), struct lib9p_srv_ctx *LM_UNUSED(ctx),
+ lo_interface io_writer LM_UNUSED(dst), uint64_t LM_UNUSED(src_offset), uint32_t LM_UNUSED(count)) {
+ assert_notreached("not readable");
+}
diff --git a/lib9p/tests/test_server/fs_shutdown.h b/lib9p/tests/test_server/fs_shutdown.h
new file mode 100644
index 0000000..7b8d327
--- /dev/null
+++ b/lib9p/tests/test_server/fs_shutdown.h
@@ -0,0 +1,22 @@
+/* lib9p/tests/test_server/fs_shutdown.h - /shutdown API endpoint
+ *
+ * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#ifndef _LIB9P_TESTS_TEST_SERVER_FS_SHUTDOWN_H_
+#define _LIB9P_TESTS_TEST_SERVER_FS_SHUTDOWN_H_
+
+#include <libhw/host_net.h>
+#include <util9p/static.h>
+
+struct shutdown_file {
+ char *name;
+ uint64_t pathnum;
+
+ struct hostnet_tcp_listener *listeners;
+ size_t nlisteners;
+};
+LO_IMPLEMENTATION_H(lib9p_srv_file, struct shutdown_file, shutdown_file);
+
+#endif /* _LIB9P_TESTS_TEST_SERVER_FS_SHUTDOWN_H_ */
diff --git a/lib9p/tests/test_server/fs_whoami.c b/lib9p/tests/test_server/fs_whoami.c
new file mode 100644
index 0000000..a07fdba
--- /dev/null
+++ b/lib9p/tests/test_server/fs_whoami.c
@@ -0,0 +1,147 @@
+/* lib9p/tests/test_server/fs_whoami.c - /whoami API endpoint
+ *
+ * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#include <inttypes.h> /* for PRI* */
+#include <stdio.h> /* for snprintf() */
+#include <stdlib.h> /* for realloc(), free() */
+
+#include <libmisc/alloc.h>
+
+#include "fs_whoami.h"
+
+LO_IMPLEMENTATION_C(lib9p_srv_file, struct whoami_file, whoami_file);
+
+struct whoami_fio {
+ struct whoami_file *parent;
+ size_t buf_len;
+ char *buf;
+};
+LO_IMPLEMENTATION_STATIC(lib9p_srv_fio, struct whoami_fio, whoami_fio);
+
+size_t whoami_len(struct lib9p_srv_ctx *ctx) {
+ assert(ctx);
+ assert(ctx->user);
+
+ size_t len = 0;
+ uint32_t uid = ctx->user->num;
+ while (uid) {
+ len++;
+ uid /= 10;
+ }
+ if (!len)
+ len++;
+ len += 2;
+ len += ctx->user->name.len;
+ return len;
+}
+
+/* srv_file *******************************************************************/
+
+void whoami_file_free(struct whoami_file *self) {
+ assert(self);
+}
+struct lib9p_qid whoami_file_qid(struct whoami_file *self) {
+ assert(self);
+ return (struct lib9p_qid){
+ .type = LIB9P_QT_FILE,
+ .vers = 1,
+ .path = self->pathnum,
+ };
+}
+
+error whoami_file_stat(struct whoami_file *self, struct lib9p_srv_ctx *ctx, struct lib9p_srv_stat *out) {
+ assert(self);
+ assert(ctx);
+ assert(out);
+
+ *out = ((struct lib9p_srv_stat){
+ .qid = whoami_file_qid(self),
+ .mode = 0444,
+ .atime_sec = UTIL9P_ATIME,
+ .mtime_sec = UTIL9P_MTIME,
+ .size = whoami_len(ctx),
+ .name = lib9p_str(self->name),
+ .owner_uid = { .name=lib9p_str("root"), .num=0 },
+ .owner_gid = { .name=lib9p_str("root"), .num=0 },
+ .last_modifier_uid = { .name=lib9p_str("root"), .num=0 },
+ .extension = lib9p_str(NULL),
+ });
+ return ERROR_NULL;
+}
+error whoami_file_wstat(struct whoami_file *self, struct lib9p_srv_ctx *ctx, struct lib9p_srv_stat) {
+ assert(self);
+ assert(ctx);
+ return error_new(E_POSIX_EROFS, "cannot wstat API file");
+}
+error whoami_file_remove(struct whoami_file *self, struct lib9p_srv_ctx *ctx) {
+ assert(self);
+ assert(ctx);
+ return error_new(E_POSIX_EROFS, "cannot remove API file");
+}
+
+LIB9P_SRV_NOTDIR(, struct whoami_file, whoami_file);
+
+lib9p_srv_fio_or_error whoami_file_fopen(struct whoami_file *self, struct lib9p_srv_ctx *ctx, bool, bool, bool) {
+ assert(self);
+ assert(ctx);
+
+ struct whoami_fio *ret = heap_alloc(1, struct whoami_fio);
+ ret->parent = self;
+ ret->buf_len = 0;
+ ret->buf = NULL;
+
+ return ERROR_NEW_VAL(lib9p_srv_fio, LO_BOX(lib9p_srv_fio, ret));
+}
+
+/* srv_fio ********************************************************************/
+
+static void whoami_fio_iofree(struct whoami_fio *self) {
+ assert(self);
+ if (self->buf)
+ free(self->buf);
+ free(self);
+}
+
+static struct lib9p_qid whoami_fio_ioqid(struct whoami_fio *self) {
+ assert(self);
+ assert(self->parent);
+ return whoami_file_qid(self->parent);
+}
+
+static uint32_t whoami_fio_iounit(struct whoami_fio *self) {
+ assert(self);
+ return 0;
+}
+
+static uint32_t_or_error whoami_fio_pwrite(struct whoami_fio *LM_UNUSED(self), struct lib9p_srv_ctx *LM_UNUSED(ctx),
+ const void *LM_UNUSED(buf),
+ uint32_t LM_UNUSED(byte_count),
+ uint64_t LM_UNUSED(offset)) {
+ assert_notreached("not writable");
+}
+static error whoami_fio_pread(struct whoami_fio *self, struct lib9p_srv_ctx *ctx,
+ lo_interface io_writer dst, uint64_t byte_offset, uint32_t byte_count) {
+ assert(self);
+ assert(ctx);
+
+ size_t data_size = whoami_len(ctx);
+ if (self->buf_len < data_size+1) {
+ self->buf = realloc(self->buf, data_size+1);
+ self->buf_len = data_size+1;
+ }
+ snprintf(self->buf, self->buf_len, "%"PRIu32" %.*s\n",
+ ctx->user->num, ctx->user->name.len, ctx->user->name.utf8);
+
+ if (byte_offset > (uint64_t)data_size)
+ return error_new(E_POSIX_EINVAL, "offset is past end-of-file length");
+
+ size_t beg_off = (size_t)byte_offset;
+ size_t end_off = beg_off + (size_t)byte_count;
+ if (end_off > data_size)
+ end_off = data_size;
+
+ return io_write(dst, &self->buf[beg_off], end_off-beg_off).err;
+}
diff --git a/lib9p/tests/test_server/fs_whoami.h b/lib9p/tests/test_server/fs_whoami.h
new file mode 100644
index 0000000..518e11d
--- /dev/null
+++ b/lib9p/tests/test_server/fs_whoami.h
@@ -0,0 +1,19 @@
+/* lib9p/tests/test_server/fs_whoami.h - /whoami API endpoint
+ *
+ * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#ifndef _LIB9P_TESTS_TEST_SERVER_FS_WHOAMI_H_
+#define _LIB9P_TESTS_TEST_SERVER_FS_WHOAMI_H_
+
+#include <libhw/host_net.h>
+#include <util9p/static.h>
+
+struct whoami_file {
+ char *name;
+ uint64_t pathnum;
+};
+LO_IMPLEMENTATION_H(lib9p_srv_file, struct whoami_file, whoami_file);
+
+#endif /* _LIB9P_TESTS_TEST_SERVER_FS_WHOAMI_H_ */
diff --git a/lib9p/tests/test_server/main.c b/lib9p/tests/test_server/main.c
index c759029..2519372 100644
--- a/lib9p/tests/test_server/main.c
+++ b/lib9p/tests/test_server/main.c
@@ -4,157 +4,91 @@
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
-#include <error.h>
+#include <errno.h>
+#include <stdio.h>
#include <stdlib.h> /* for atoi() */
+#define error __error
+#include <error.h>
+#undef error
+
#include <lib9p/srv.h>
#include <libcr/coroutine.h>
-#include <libhw/generic/net.h>
#include <libhw/generic/alarmclock.h>
+#include <libhw/generic/net.h>
#include <libhw/host_alarmclock.h>
#include <libhw/host_net.h>
#include <libmisc/macro.h>
#include <util9p/static.h>
+#include "fs_flush.h"
+#include "fs_shutdown.h"
+#include "fs_whoami.h"
#include "static.h"
/* configuration **************************************************************/
#include "config.h"
-#ifndef CONFIG_SRV9P_NUM_CONNS
- #error config.h must define CONFIG_SRV9P_NUM_CONNS
+#ifndef _CONFIG_9P_MAX_CONNS
+ #error config.h must define _CONFIG_9P_MAX_CONNS
+#endif
+#ifndef _CONFIG_9P_MAX_REQS
+ #error config.h must define _CONFIG_9P_MAX_REQS
#endif
/* globals ********************************************************************/
-static lo_interface lib9p_srv_file get_root(struct lib9p_srv_ctx *, struct lib9p_s);
+static lib9p_srv_file_or_error get_root(struct lib9p_srv_ctx *, struct lib9p_s);
-const char *hexdig = "0123456789abcdef";
+static const char *hexdig = "0123456789abcdef";
-struct {
+static struct {
uint16_t port;
- struct hostnet_tcp_listener listeners[CONFIG_SRV9P_NUM_CONNS];
+ struct hostnet_tcp_listener listeners[_CONFIG_9P_MAX_CONNS];
struct lib9p_srv srv;
+ FILE *logstream;
} globals = {
.srv = (struct lib9p_srv){
.rootdir = get_root,
},
};
-/* api ************************************************************************/
-
-struct api_file {
- uint64_t pathnum;
-};
-LO_IMPLEMENTATION_H(lib9p_srv_file, struct api_file, api)
-LO_IMPLEMENTATION_H(lib9p_srv_fio, struct api_file, api)
-
-LO_IMPLEMENTATION_C(lib9p_srv_file, struct api_file, api, static)
-LO_IMPLEMENTATION_C(lib9p_srv_fio, struct api_file, api, static)
-
-static void api_free(struct api_file *self) {
- assert(self);
-}
-static struct lib9p_qid api_qid(struct api_file *self) {
- assert(self);
- return (struct lib9p_qid){
- .type = LIB9P_QT_FILE,
- .vers = 1,
- .path = self->pathnum,
- };
-}
-
-static struct lib9p_stat api_stat(struct api_file *self, struct lib9p_srv_ctx *ctx) {
- assert(self);
- assert(ctx);
- return (struct lib9p_stat){
- .kern_type = 0,
- .kern_dev = 0,
- .file_qid = api_qid(self),
- .file_mode = 0222,
- .file_atime = UTIL9P_ATIME,
- .file_mtime = UTIL9P_MTIME,
- .file_size = 0,
- .file_name = lib9p_str("shutdown"),
- .file_owner_uid = lib9p_str("root"),
- .file_owner_gid = lib9p_str("root"),
- .file_last_modified_uid = lib9p_str("root"),
- .file_extension = lib9p_str(NULL),
- .file_owner_n_uid = 0,
- .file_owner_n_gid = 0,
- .file_last_modified_n_uid = 0,
- };
-}
-static void api_wstat(struct api_file *self, struct lib9p_srv_ctx *ctx, struct lib9p_stat) {
- assert(self);
- assert(ctx);
- lib9p_error(&ctx->basectx, LINUX_EROFS, "cannot wstat API file");
-}
-static void api_remove(struct api_file *self, struct lib9p_srv_ctx *ctx) {
- assert(self);
- assert(ctx);
- lib9p_error(&ctx->basectx, LINUX_EROFS, "cannot remove API file");
-}
-
-LIB9P_SRV_NOTDIR(struct api_file, api)
-
-static lo_interface lib9p_srv_fio api_fopen(struct api_file *self, struct lib9p_srv_ctx *ctx, bool, bool, bool) {
- assert(self);
- assert(ctx);
- return lo_box_api_as_lib9p_srv_fio(self);
-}
-
-static void api_iofree(struct api_file *self) {
- assert(self);
-}
-
-static uint32_t api_iounit(struct api_file *self) {
- assert(self);
- return 0;
-}
-
-static uint32_t api_pwrite(struct api_file *self, struct lib9p_srv_ctx *ctx, void *buf, uint32_t byte_count, uint64_t LM_UNUSED(offset)) {
- assert(self);
- assert(ctx);
- assert(buf);
- if (byte_count == 0)
- return 0;
- for (int i = 0; i < CONFIG_SRV9P_NUM_CONNS; i++)
- LO_CALL(lo_box_hostnet_tcplist_as_net_stream_listener(&globals.listeners[i]), close);
- return byte_count;
-}
-static void api_pread(struct api_file *LM_UNUSED(self), struct lib9p_srv_ctx *LM_UNUSED(ctx),
- uint32_t LM_UNUSED(byte_count), uint64_t LM_UNUSED(byte_offset),
- struct iovec *LM_UNUSED(ret)) {
- assert_notreached("not readable");
-}
-
-#define lo_box_api_as_lib9p_srv_file(obj) util9p_box(api, obj)
-
/* file tree ******************************************************************/
-enum { PATH_BASE = __COUNTER__ };
-#define PATH_COUNTER __COUNTER__ - PATH_BASE
-
-#define STATIC_FILE(STRNAME, SYMNAME) \
- UTIL9P_STATIC_FILE(PATH_COUNTER, STRNAME, \
+#define STATIC_FILE(N, STRNAME, SYMNAME) \
+ UTIL9P_STATIC_FILE(N, STRNAME, \
.data_start = _binary_static_##SYMNAME##_start, \
.data_end = _binary_static_##SYMNAME##_end)
-#define STATIC_DIR(STRNAME, ...) \
- UTIL9P_STATIC_DIR(PATH_COUNTER, STRNAME, __VA_ARGS__)
-
-struct lib9p_srv_file root =
- STATIC_DIR("",
- STATIC_DIR("Documentation",
- STATIC_FILE("x", Documentation_x_txt),
+#define STATIC_DIR(N, STRNAME, ...) \
+ UTIL9P_STATIC_DIR(N, STRNAME, __VA_ARGS__)
+
+#define API_FILE(N, STRNAME, SYMNAME, ...) \
+ lo_box_##SYMNAME##_file_as_lib9p_srv_file(&((struct SYMNAME##_file){ \
+ .name = STRNAME, \
+ .pathnum = N \
+ __VA_OPT__(,) __VA_ARGS__ \
+ }))
+
+static struct lib9p_srv_file root =
+ STATIC_DIR(1, "",
+ STATIC_DIR(2, "Documentation",
+ STATIC_FILE(3, "x", Documentation_x_txt),
),
- STATIC_FILE("README.md", README_md),
- lo_box_api_as_lib9p_srv_file(&(struct api_file){.pathnum = PATH_COUNTER}),
+ STATIC_FILE(4, "README.md", README_md),
+ API_FILE(5, "shutdown", shutdown,
+ .listeners = globals.listeners,
+ .nlisteners = LM_ARRAY_LEN(globals.listeners)),
+ API_FILE(8, "whoami", whoami),
+ API_FILE(9, "flush-read", flush, .flush_cnt=1, .flush_behavior=FLUSH_READ),
+ API_FILE(10, "flush-error", flush, .flush_cnt=1, .flush_behavior=FLUSH_ERROR),
+ API_FILE(11, "flush-silent", flush, .flush_cnt=1, .flush_behavior=FLUSH_SILENT),
+ API_FILE(12, "flush-slowsilent", flush, .flush_cnt=2, .flush_behavior=FLUSH_SILENT),
+ API_FILE(13, "flush-slowread", flush, .flush_cnt=0, .flush_behavior=FLUSH_READ),
);
-static lo_interface lib9p_srv_file get_root(struct lib9p_srv_ctx *LM_UNUSED(ctx), struct lib9p_s LM_UNUSED(treename)) {
- return root;
+static lib9p_srv_file_or_error get_root(struct lib9p_srv_ctx *LM_UNUSED(ctx), struct lib9p_s LM_UNUSED(treename)) {
+ return ERROR_NEW_VAL(lib9p_srv_file, root);
}
/* main ***********************************************************************/
@@ -165,7 +99,15 @@ static COROUTINE read_cr(void *_i) {
hostnet_tcp_listener_init(&globals.listeners[i], globals.port);
- lib9p_srv_read_cr(&globals.srv, lo_box_hostnet_tcplist_as_net_stream_listener(&globals.listeners[i]));
+ lib9p_srv_accept_and_read_loop(&globals.srv, LO_BOX(net_stream_listener, &globals.listeners[i]));
+
+ cr_end();
+}
+
+static COROUTINE write_cr(void *) {
+ cr_begin();
+
+ lib9p_srv_worker_loop(&globals.srv);
cr_end();
}
@@ -173,31 +115,60 @@ static COROUTINE read_cr(void *_i) {
static COROUTINE init_cr(void *) {
cr_begin();
- sleep_for_ms(1);
+ sleep_for_ms(1); /* test that sleep works */
- for (int i = 0; i < CONFIG_SRV9P_NUM_CONNS; i++) {
+ for (int i = 0; i < _CONFIG_9P_MAX_CONNS; i++) {
char name[] = {'r', 'e', 'a', 'd', '-', hexdig[i], '\0'};
if (!coroutine_add(name, read_cr, &i))
- error(1, 0, "coroutine_add(read_cr, &i)");
+ __error(1, 0, "coroutine_add(read_cr, &i)");
}
- for (int i = 0; i < 2*CONFIG_SRV9P_NUM_CONNS; i++) {
+ for (int i = 0; i < _CONFIG_9P_MAX_REQS; i++) {
char name[] = {'w', 'r', 'i', 't', 'e', '-', hexdig[i], '\0'};
- if (!coroutine_add(name, lib9p_srv_write_cr, &globals.srv))
- error(1, 0, "coroutine_add(lib9p_srv_write_cr, &globals.srv)");
+ if (!coroutine_add(name, write_cr, NULL))
+ __error(1, 0, "coroutine_add(write_cr, NULL)");
}
cr_exit();
}
+struct tstlog_stdout {};
+LO_IMPLEMENTATION_STATIC(fmt_dest, struct tstlog_stdout, tstlog_stdout);
+
+static size_t tstlog_bytes = 0;
+
+static void tstlog_stdout_putb(struct tstlog_stdout *, uint8_t b) {
+ putc(b, globals.logstream);
+ putchar(b);
+ tstlog_bytes++;
+}
+
+static size_t tstlog_stdout_tell(struct tstlog_stdout *) {
+ return tstlog_bytes;
+}
+
+static lo_interface fmt_dest tstlog_dest = { .vtable = &_lo_tstlog_stdout_fmt_dest_vtable };
+
+static void tstlog_msg(struct lib9p_srv_ctx *ctx, enum lib9p_msg_type typ, void *hostmsg) {
+ fmt_print(tstlog_dest, typ % 2 ? "< " : "> ", (lib9p_msg, &ctx->basectx, typ, hostmsg), "\n");
+ fflush(globals.logstream);
+}
+
int main(int argc, char *argv[]) {
- if (argc != 2)
- error(2, 0, "usage: %s PORT_NUMBER", argv[0]);
+ if (argc != 3)
+ __error(2, 0, "usage: %s PORT_NUMBER LOGFILE", argv[0]);
+
globals.port = atoi(argv[1]);
+ globals.logstream = fopen(argv[2], "w");
+ if (!globals.logstream)
+ __error(2, errno, "fopen");
+ globals.srv.msglog = tstlog_msg;
+
struct hostclock clock_monotonic = {
.clock_id = CLOCK_MONOTONIC,
};
- bootclock = lo_box_hostclock_as_alarmclock(&clock_monotonic);
+ bootclock = LO_BOX(alarmclock, &clock_monotonic);
coroutine_add("init", init_cr, NULL);
coroutine_main();
+ fclose(globals.logstream);
return 0;
}
diff --git a/lib9p/tests/testclient-hangup.c b/lib9p/tests/testclient-hangup.c
new file mode 100644
index 0000000..c59d647
--- /dev/null
+++ b/lib9p/tests/testclient-hangup.c
@@ -0,0 +1,100 @@
+/* lib9p/tests/testclient-hangup.c - Test the 9P `test_server`'s handling of TCP hangups
+ *
+ * Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#include <arpa/inet.h> /* for htons(), inet_addr() */
+#include <errno.h>
+#include <netinet/in.h> /* for struct sockaddr{,_in} */
+#include <stdlib.h> /* for atoi() */
+#include <sys/socket.h> /* for socket(), connect() */
+#include <sys/uio.h> /* for writev() */
+#include <unistd.h> /* for read() */
+
+#define error __error
+#include <error.h>
+#undef error
+
+#include <lib9p/core.h>
+#include <libmisc/assert.h>
+#include <libmisc/endian.h>
+
+#define MAX_MSG_SIZE (8*1024)
+
+static void _send9p(int fd, struct lib9p_ctx *ctx, enum lib9p_msg_type typ, void *body) {
+ struct lib9p_Tmsg_send_buf buf;
+ error err = lib9p_Tmsg_marshal(ctx, typ, body, &buf);
+ assert(ERROR_IS_NULL(err));
+ size_t exp = 0;
+ for (size_t i = 0; i < buf.iov_cnt; i++)
+ exp += buf.iov[i].iov_len;
+ ssize_t act = writev(fd, (const struct iovec *)buf.iov, buf.iov_cnt);
+ if (act < 0)
+ __error(1, errno, "writev");
+ assert((size_t)act == exp);
+}
+
+#define send9p(typ, ...) _send9p(fd, &ctx, LIB9P_TYP_##typ, &((struct lib9p_msg_##typ){ __VA_ARGS__ }))
+
+static void _recv9p(int fd) {
+ uint8_t buf[MAX_MSG_SIZE];
+ size_t goal = 4;
+ size_t done = 0;
+ while (done < goal) {
+ ssize_t n = read(fd, &buf[done], goal-done);
+ if (n < 0)
+ __error(1, errno, "read");
+ done += n;
+ }
+ goal = uint32le_decode(buf);
+ assert(goal <= MAX_MSG_SIZE);
+ while (done < goal) {
+ ssize_t n = read(fd, &buf[done], goal-done);
+ if (n < 0)
+ __error(1, errno, "read");
+ done += n;
+ }
+}
+
+#define recv9p() _recv9p(fd)
+
+int main(int argc, char *argv[]) {
+ if (argc != 2)
+ __error(2, 0, "Usage: %s SERVER_PORT", argv[0]);
+ uint16_t server_port = atoi(argv[1]);
+
+ union {
+ struct sockaddr gen;
+ struct sockaddr_in in;
+ } server_addr = {};
+ server_addr.in.sin_family = AF_INET;
+ server_addr.in.sin_addr.s_addr = inet_addr("127.0.0.1");
+ server_addr.in.sin_port = htons(server_port);
+
+ int fd = socket(AF_INET, SOCK_STREAM, 0);
+ if (fd < 0)
+ __error(1, errno, "socket");
+ if (connect(fd, &server_addr.gen, sizeof(server_addr)) < 0)
+ __error(1, errno, "connect");
+
+ struct lib9p_ctx ctx = {
+ .max_msg_size = 16*1024,
+ };
+
+ struct lib9p_s wname[1];
+
+ /**********************************************************************/
+
+ send9p(Tversion, .tag=0, .max_msg_size=(8*1024), .version=lib9p_str("9P2000"));
+ recv9p(); /* Rversion */
+ ctx.version = LIB9P_VER_9P2000;
+ send9p(Tattach, .tag=0, .fid=0, .afid=LIB9P_FID_NOFID, .uname=lib9p_str("nobody"), .aname=lib9p_str(""));
+ recv9p(); /* Rattach */
+ wname[0] = lib9p_str("shutdown"); send9p(Twalk, .tag=0, .fid=0, .newfid=0, .nwname=1, .wname=wname);
+ recv9p(); /* Rwalk */
+ send9p(Topen, .tag=0, .fid=0, .mode=LIB9P_O_MODE_WRITE);
+ recv9p(); /* Ropen */
+ send9p(Twrite, .tag=0, .fid=0, .offset=0, .count=2, .data="1\n");
+ return 0; /* Hang up without waiting for Rwrite. */
+}
diff --git a/lib9p/tests/testclient-hangup.explog b/lib9p/tests/testclient-hangup.explog
new file mode 100644
index 0000000..568b0fc
--- /dev/null
+++ b/lib9p/tests/testclient-hangup.explog
@@ -0,0 +1,14 @@
+# lib9p/tests/testclient-hangup.explog - Expected 9P logfile of testclient-hangup
+#
+# Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+# SPDX-License-Identifier: AGPL-3.0-or-later
+> Tversion { tag=0 max_msg_size=8192 version="9P2000" }
+< Rversion { tag=0 max_msg_size=4120 version="9P2000" }
+> Tattach { tag=0 fid=0 afid=NOFID uname="nobody" aname="" unum=0 }
+< Rattach { tag=0 qid={ type=(DIR) vers=1 path=1 } }
+> Twalk { tag=0 fid=0 newfid=0 nwname=1 wname=[ "shutdown" ] }
+< Rwalk { tag=0 nwqid=1 wqid=[ { type=(APPEND) vers=1 path=5 } ] }
+> Topen { tag=0 fid=0 mode=(MODE_WRITE) }
+< Ropen { tag=0 qid={ type=(APPEND) vers=1 path=5 } iounit=0 }
+> Twrite { tag=0 fid=0 offset=0 count=2 data="1\n" }
+< Rwrite { tag=0 count=2 }
diff --git a/lib9p/tests/testclient-p9p b/lib9p/tests/testclient-p9p
new file mode 100755
index 0000000..09ce746
--- /dev/null
+++ b/lib9p/tests/testclient-p9p
@@ -0,0 +1,65 @@
+#!/usr/bin/env bash
+# lib9p/tests/testclient-p9p - Test the 9P `test_server` against Plan 9 Port's `9p` utility
+#
+# Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+# SPDX-License-Identifier: AGPL-3.0-or-later
+
+set -euE -o pipefail
+
+if [[ $# != 1 ]]; then
+ echo >&2 "Usage: $0 SERVER_PORT"
+ echo >&2 "Usage: ./runtest $0 EXPLOG"
+ exit 2
+fi
+
+expect_lines() (
+ { set +x; } &>/dev/null
+ printf >&2 '+ diff -u expected.txt actual.txt\n'
+ diff -u <(printf '%s\n' "$@") <(printf '%s\n' "$out")
+)
+
+set -x
+client=(unshare --user 9p -a "localhost:${1}")
+
+out=$("${client[@]}" ls -l '')
+expect_lines \
+ 'd-r-xr-xr-x M 0 root root 0 Oct 7 2024 Documentation' \
+ '--r--r--r-- M 0 root root 166 Oct 7 2024 README.md' \
+ '--r--r--r-- M 0 root root 6 Oct 7 2024 flush-error' \
+ '--r--r--r-- M 0 root root 6 Oct 7 2024 flush-read' \
+ '--r--r--r-- M 0 root root 6 Oct 7 2024 flush-silent' \
+ '--r--r--r-- M 0 root root 6 Oct 7 2024 flush-slowread' \
+ '--r--r--r-- M 0 root root 6 Oct 7 2024 flush-slowsilent' \
+ 'a--w--w--w- M 0 root root 0 Oct 7 2024 shutdown' \
+ '--r--r--r-- M 0 root root 9 Oct 7 2024 whoami'
+
+out=$("${client[@]}" ls -l 'Documentation/')
+expect_lines \
+ '--r--r--r-- M 0 root root 166 Oct 7 2024 x'
+
+out=$("${client[@]}" read 'README.md')
+expect_lines \
+ '<!--' \
+ ' README.md - test static file' \
+ '' \
+ ' Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>' \
+ ' SPDX-License-Identifier: AGPL-3.0-or-later' \
+ '-->' \
+ 'Hello, world!'
+
+out=$("${client[@]}" read 'Documentation/x')
+expect_lines \
+ '<!--' \
+ ' Documentation/x.txt - test static file' \
+ '' \
+ ' Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>' \
+ ' SPDX-License-Identifier: AGPL-3.0-or-later' \
+ '-->' \
+ 'foo'
+
+out=$("${client[@]}" stat 'Documentation/x')
+expect_lines \
+ "'x' 'root' 'root' 'root' q (0000000000000003 1 ) m 0444 at 1728337905 mt 1728337904 l 166 t 0 d 0"
+
+out=$("${client[@]}" write 'shutdown' <<<1)
+expect_lines ''
diff --git a/lib9p/tests/testclient-p9p.explog b/lib9p/tests/testclient-p9p.explog
new file mode 100644
index 0000000..54f1e4b
--- /dev/null
+++ b/lib9p/tests/testclient-p9p.explog
@@ -0,0 +1,106 @@
+# lib9p/tests/testclient-p9p.explog - Expected 9P logfile of testclient-p9p
+#
+# Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+# SPDX-License-Identifier: AGPL-3.0-or-later
+> Tversion { tag=NOTAG max_msg_size=8192 version="9P2000" }
+< Rversion { tag=NOTAG max_msg_size=4120 version="9P2000" }
+> Tauth { tag=0 afid=0 uname="nobody" aname="" unum=0 }
+< Rerror { tag=0 errstr="authentication not required" errnum=L_EOPNOTSUPP }
+> Tattach { tag=0 fid=0 afid=NOFID uname="nobody" aname="" unum=0 }
+< Rattach { tag=0 qid={ type=(DIR) vers=1 path=1 } }
+> Twalk { tag=0 fid=0 newfid=1 nwname=0 wname=[ ] }
+< Rwalk { tag=0 nwqid=0 wqid=[ ] }
+> Tstat { tag=0 fid=1 }
+< Rstat { tag=0 stat={ fstype=0 fsdev=0 qid={ type=(DIR) vers=1 path=1 } mode=(DIR|0555) atime=1728337905 mtime=1728337904 length=0 name="" owner_uname="root" owner_gname="root" last_modifier_uname="root" extension="" owner_unum=0 owner_gnum=0 last_modifier_unum=0 } }
+> Tclunk { tag=0 fid=1 }
+< Rclunk { tag=0 }
+> Twalk { tag=0 fid=0 newfid=1 nwname=0 wname=[ ] }
+< Rwalk { tag=0 nwqid=0 wqid=[ ] }
+> Topen { tag=0 fid=1 mode=(MODE_READ) }
+< Ropen { tag=0 qid={ type=(DIR) vers=1 path=1 } iounit=0 }
+> Tread { tag=0 fid=1 offset=0 count=4096 }
+< Rread { tag=0 count=648 data=<bytedata> }
+> Tread { tag=0 fid=1 offset=648 count=4096 }
+< Rread { tag=0 count=0 data="" }
+> Tclunk { tag=0 fid=1 }
+< Rclunk { tag=0 }
+> Tversion { tag=NOTAG max_msg_size=8192 version="9P2000" }
+< Rversion { tag=NOTAG max_msg_size=4120 version="9P2000" }
+> Tauth { tag=0 afid=0 uname="nobody" aname="" unum=0 }
+< Rerror { tag=0 errstr="authentication not required" errnum=L_EOPNOTSUPP }
+> Tattach { tag=0 fid=0 afid=NOFID uname="nobody" aname="" unum=0 }
+< Rattach { tag=0 qid={ type=(DIR) vers=1 path=1 } }
+> Twalk { tag=0 fid=0 newfid=1 nwname=1 wname=[ "Documentation" ] }
+< Rwalk { tag=0 nwqid=1 wqid=[ { type=(DIR) vers=1 path=2 } ] }
+> Tstat { tag=0 fid=1 }
+< Rstat { tag=0 stat={ fstype=0 fsdev=0 qid={ type=(DIR) vers=1 path=2 } mode=(DIR|0555) atime=1728337905 mtime=1728337904 length=0 name="Documentation" owner_uname="root" owner_gname="root" last_modifier_uname="root" extension="" owner_unum=0 owner_gnum=0 last_modifier_unum=0 } }
+> Tclunk { tag=0 fid=1 }
+< Rclunk { tag=0 }
+> Twalk { tag=0 fid=0 newfid=1 nwname=1 wname=[ "Documentation" ] }
+< Rwalk { tag=0 nwqid=1 wqid=[ { type=(DIR) vers=1 path=2 } ] }
+> Topen { tag=0 fid=1 mode=(MODE_READ) }
+< Ropen { tag=0 qid={ type=(DIR) vers=1 path=2 } iounit=0 }
+> Tread { tag=0 fid=1 offset=0 count=4096 }
+< Rread { tag=0 count=62 data=<bytedata> }
+> Tread { tag=0 fid=1 offset=62 count=4096 }
+< Rread { tag=0 count=0 data="" }
+> Tclunk { tag=0 fid=1 }
+< Rclunk { tag=0 }
+> Tversion { tag=NOTAG max_msg_size=8192 version="9P2000" }
+< Rversion { tag=NOTAG max_msg_size=4120 version="9P2000" }
+> Tauth { tag=0 afid=0 uname="nobody" aname="" unum=0 }
+< Rerror { tag=0 errstr="authentication not required" errnum=L_EOPNOTSUPP }
+> Tattach { tag=0 fid=0 afid=NOFID uname="nobody" aname="" unum=0 }
+< Rattach { tag=0 qid={ type=(DIR) vers=1 path=1 } }
+> Twalk { tag=0 fid=0 newfid=1 nwname=1 wname=[ "README.md" ] }
+< Rwalk { tag=0 nwqid=1 wqid=[ { type=(0) vers=1 path=4 } ] }
+> Topen { tag=0 fid=1 mode=(MODE_READ) }
+< Ropen { tag=0 qid={ type=(0) vers=1 path=4 } iounit=0 }
+> Tread { tag=0 fid=1 offset=0 count=4096 }
+< Rread { tag=0 count=166 data="<!--\n README.md - test static file\n\n Copyright ("... }
+> Tread { tag=0 fid=1 offset=166 count=4096 }
+< Rread { tag=0 count=0 data="" }
+> Tclunk { tag=0 fid=1 }
+< Rclunk { tag=0 }
+> Tversion { tag=NOTAG max_msg_size=8192 version="9P2000" }
+< Rversion { tag=NOTAG max_msg_size=4120 version="9P2000" }
+> Tauth { tag=0 afid=0 uname="nobody" aname="" unum=0 }
+< Rerror { tag=0 errstr="authentication not required" errnum=L_EOPNOTSUPP }
+> Tattach { tag=0 fid=0 afid=NOFID uname="nobody" aname="" unum=0 }
+< Rattach { tag=0 qid={ type=(DIR) vers=1 path=1 } }
+> Twalk { tag=0 fid=0 newfid=1 nwname=2 wname=[ "Documentation", "x" ] }
+< Rwalk { tag=0 nwqid=2 wqid=[ { type=(DIR) vers=1 path=2 }, { type=(0) vers=1 path=3 } ] }
+> Topen { tag=0 fid=1 mode=(MODE_READ) }
+< Ropen { tag=0 qid={ type=(0) vers=1 path=3 } iounit=0 }
+> Tread { tag=0 fid=1 offset=0 count=4096 }
+< Rread { tag=0 count=166 data="<!--\n Documentation/x.txt - test static file\n\n C"... }
+> Tread { tag=0 fid=1 offset=166 count=4096 }
+< Rread { tag=0 count=0 data="" }
+> Tclunk { tag=0 fid=1 }
+< Rclunk { tag=0 }
+> Tversion { tag=NOTAG max_msg_size=8192 version="9P2000" }
+< Rversion { tag=NOTAG max_msg_size=4120 version="9P2000" }
+> Tauth { tag=0 afid=0 uname="nobody" aname="" unum=0 }
+< Rerror { tag=0 errstr="authentication not required" errnum=L_EOPNOTSUPP }
+> Tattach { tag=0 fid=0 afid=NOFID uname="nobody" aname="" unum=0 }
+< Rattach { tag=0 qid={ type=(DIR) vers=1 path=1 } }
+> Twalk { tag=0 fid=0 newfid=1 nwname=2 wname=[ "Documentation", "x" ] }
+< Rwalk { tag=0 nwqid=2 wqid=[ { type=(DIR) vers=1 path=2 }, { type=(0) vers=1 path=3 } ] }
+> Tstat { tag=0 fid=1 }
+< Rstat { tag=0 stat={ fstype=0 fsdev=0 qid={ type=(0) vers=1 path=3 } mode=(0444) atime=1728337905 mtime=1728337904 length=166 name="x" owner_uname="root" owner_gname="root" last_modifier_uname="root" extension="" owner_unum=0 owner_gnum=0 last_modifier_unum=0 } }
+> Tclunk { tag=0 fid=1 }
+< Rclunk { tag=0 }
+> Tversion { tag=NOTAG max_msg_size=8192 version="9P2000" }
+< Rversion { tag=NOTAG max_msg_size=4120 version="9P2000" }
+> Tauth { tag=0 afid=0 uname="nobody" aname="" unum=0 }
+< Rerror { tag=0 errstr="authentication not required" errnum=L_EOPNOTSUPP }
+> Tattach { tag=0 fid=0 afid=NOFID uname="nobody" aname="" unum=0 }
+< Rattach { tag=0 qid={ type=(DIR) vers=1 path=1 } }
+> Twalk { tag=0 fid=0 newfid=1 nwname=1 wname=[ "shutdown" ] }
+< Rwalk { tag=0 nwqid=1 wqid=[ { type=(APPEND) vers=1 path=5 } ] }
+> Topen { tag=0 fid=1 mode=(TRUNC|MODE_WRITE) }
+< Ropen { tag=0 qid={ type=(APPEND) vers=1 path=5 } iounit=0 }
+> Twrite { tag=0 fid=1 offset=0 count=2 data="1\n" }
+< Rwrite { tag=0 count=2 }
+> Tclunk { tag=0 fid=1 }
+< Rclunk { tag=0 }
diff --git a/lib9p/tests/testclient-sess.c b/lib9p/tests/testclient-sess.c
new file mode 100644
index 0000000..e90c94a
--- /dev/null
+++ b/lib9p/tests/testclient-sess.c
@@ -0,0 +1,246 @@
+/* lib9p/tests/testclient-sess.c - Test the 9P `test_server`'s sessions
+ *
+ * Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#include <arpa/inet.h> /* for htons(), inet_addr() */
+#include <errno.h>
+#include <netinet/in.h> /* for struct sockaddr{,_in} */
+#include <stdlib.h> /* for atoi() */
+#include <sys/socket.h> /* for socket(), connect() */
+#include <sys/uio.h> /* for writev() */
+#include <unistd.h> /* for read() */
+
+#define error __error
+#include <error.h>
+#undef error
+
+#include <lib9p/core.h>
+#include <libmisc/assert.h>
+#include <libmisc/endian.h>
+
+#define MAX_MSG_SIZE (8*1024)
+
+static void _send9p(int fd, struct lib9p_ctx *ctx, enum lib9p_msg_type typ, void *body) {
+ struct lib9p_Tmsg_send_buf buf;
+ error err = lib9p_Tmsg_marshal(ctx, typ, body, &buf);
+ assert(ERROR_IS_NULL(err));
+ size_t exp = 0;
+ for (size_t i = 0; i < buf.iov_cnt; i++)
+ exp += buf.iov[i].iov_len;
+ ssize_t act = writev(fd, (const struct iovec *)buf.iov, buf.iov_cnt);
+ if (act < 0)
+ __error(1, errno, "writev");
+ assert((size_t)act == exp);
+}
+
+#define send9p(typ, ...) _send9p(fd, &ctx, LIB9P_TYP_##typ, &((struct lib9p_msg_##typ){ __VA_ARGS__ }))
+
+static void _recv9p(int fd) {
+ uint8_t buf[MAX_MSG_SIZE];
+ size_t goal = 4;
+ size_t done = 0;
+ while (done < goal) {
+ ssize_t n = read(fd, &buf[done], goal-done);
+ if (n < 0)
+ __error(1, errno, "read");
+ done += n;
+ }
+ goal = uint32le_decode(buf);
+ assert(goal <= MAX_MSG_SIZE);
+ while (done < goal) {
+ ssize_t n = read(fd, &buf[done], goal-done);
+ if (n < 0)
+ __error(1, errno, "read");
+ done += n;
+ }
+}
+
+#define recv9p() _recv9p(fd)
+
+int main(int argc, char *argv[]) {
+ if (argc != 2)
+ __error(2, 0, "Usage: %s SERVER_PORT", argv[0]);
+ uint16_t server_port = atoi(argv[1]);
+
+ union {
+ struct sockaddr gen;
+ struct sockaddr_in in;
+ } server_addr = {};
+ server_addr.in.sin_family = AF_INET;
+ server_addr.in.sin_addr.s_addr = inet_addr("127.0.0.1");
+ server_addr.in.sin_port = htons(server_port);
+
+ int fd = socket(AF_INET, SOCK_STREAM, 0);
+ if (fd < 0)
+ __error(1, errno, "socket");
+ if (connect(fd, &server_addr.gen, sizeof(server_addr)) < 0)
+ __error(1, errno, "connect");
+
+ struct lib9p_ctx ctx = {
+ .max_msg_size = 16*1024,
+ };
+
+ struct lib9p_s wname[1];
+
+ /* numeric downgrade, unknown ext *************************************/
+ send9p(Tversion, .tag=0, .max_msg_size=57, .version=lib9p_str("9P2025.x"));
+ recv9p(); /* Rversion */
+ ctx.version = LIB9P_VER_9P2000;
+
+ /* numeric downgrade, known ext ***************************************/
+ send9p(Tversion, .tag=0, .max_msg_size=57, .version=lib9p_str("9P2025.u"));
+ recv9p(); /* Rversion */
+ ctx.version = LIB9P_VER_9P2000_u;
+
+ /* ext version, users *************************************************/
+ send9p(Tversion, .tag=0, .max_msg_size=(8*1024), .version=lib9p_str("9P2000.u"));
+ recv9p(); /* Rversion */
+ ctx.version = LIB9P_VER_9P2000_u;
+ send9p(Tattach, .tag=0, .fid=0, .afid=LIB9P_FID_NOFID, .uname=lib9p_str("alice"), .unum=1000, .aname=lib9p_str(""));
+ recv9p(); /* Rattach */
+ send9p(Tattach, .tag=0, .fid=1, .afid=LIB9P_FID_NOFID, .uname=lib9p_str("bob"), .unum=1001, .aname=lib9p_str(""));
+ recv9p(); /* Rattach */
+ wname[0] = lib9p_str("whoami"); send9p(Twalk, .tag=0, .fid=0, .newfid=2, .nwname=1, .wname=wname);
+ recv9p(); /* Rwalk */
+ wname[0] = lib9p_str("whoami"); send9p(Twalk, .tag=0, .fid=1, .newfid=3, .nwname=1, .wname=wname);
+ recv9p(); /* Rwalk */
+ send9p(Topen, .tag=0, .fid=2, .mode=LIB9P_O_MODE_READ);
+ recv9p(); /* Ropen */
+ send9p(Topen, .tag=0, .fid=3, .mode=LIB9P_O_MODE_READ);
+ recv9p(); /* Ropen */
+ send9p(Tread, .tag=0, .fid=2, .offset=0, .count=100);
+ recv9p(); /* Rread */
+ send9p(Tread, .tag=0, .fid=3, .offset=0, .count=100);
+ recv9p(); /* Rread */
+
+ /* walk ***************************************************************/
+ send9p(Tversion, .tag=0, .max_msg_size=(8*1024), .version=lib9p_str("9P2000"));
+ recv9p(); /* Rversion */
+ ctx.version = LIB9P_VER_9P2000;
+ send9p(Tattach, .tag=0, .fid=0, .afid=LIB9P_FID_NOFID, .uname=lib9p_str("nobody"), .aname=lib9p_str(""));
+ recv9p(); /* Rattach */
+
+ /* dup */
+ send9p(Twalk, .tag=0, .fid=0, .newfid=1, .nwname=0);
+ recv9p(); /* Rwalk */
+
+ /* "The walk request carries as arguments an existing fid"... */
+ send9p(Twalk, .tag=0, .fid=2, .newfid=3, .nwname=0);
+ recv9p(); /* Rerror */
+
+ /* ..."and a proposed newfid"... */
+ send9p(Twalk, .tag=0, .fid=1, .newfid=0xffffffff, .nwname=0);
+ recv9p(); /* Rerror */
+
+ /* ..."(which must not be in use"... */
+ send9p(Twalk, .tag=0, .fid=1, .newfid=0, .nwname=0);
+ recv9p(); /* Rerror */
+
+ /* ..."unless it is the same as fid)"... */
+ send9p(Twalk, .tag=0, .fid=1, .newfid=1, .nwname=0);
+ recv9p(); /* Rwalk */
+
+ /* ... "that the client wishes to associate with the result of
+ * traversing the directory hierarchy by `walking' the heierarchy using
+ * the successive path name elements wname."... */
+
+ /* ..."The fid must represent a directory"... */
+ wname[0] = lib9p_str("README.md"); send9p(Twalk, .tag=0, .fid=1, .newfid=2, .nwname=1, .wname=wname);
+ recv9p(); /* Rwalk */
+ wname[0] = lib9p_str(".."); send9p(Twalk, .tag=0, .fid=2, .newfid=3, .nwname=1, .wname=wname);
+ recv9p(); /* Rerror */
+
+ /* ..."unless zero path name elements are specified." */
+ send9p(Twalk, .tag=0, .fid=2, .newfid=3, .nwname=0);
+ recv9p(); /* Rwalk */
+
+ /* "The fid must be valid in the current session" (tested above)... */
+
+ /* ..."and must not have been opened for I/O by an open or create
+ * message."... */
+ send9p(Topen, .tag=0, .fid=3, .mode=LIB9P_O_MODE_READ);
+ recv9p(); /* Ropen */
+ send9p(Twalk, .tag=0, .fid=3, .newfid=4, .nwname=0);
+ recv9p(); /* Rerror */
+
+ /* flush **************************************************************/
+ send9p(Tversion, .tag=0, .max_msg_size=(8*1024), .version=lib9p_str("9P2000"));
+ recv9p(); /* Rversion */
+ ctx.version = LIB9P_VER_9P2000;
+ send9p(Tattach, .tag=0, .fid=0, .afid=LIB9P_FID_NOFID, .uname=lib9p_str("nobody"), .aname=lib9p_str(""));
+ recv9p(); /* Rattach */
+
+ /* flush, but original response comes back first */
+ wname[0] = lib9p_str("flush-read"); send9p(Twalk, .tag=0, .fid=0, .newfid=1, .nwname=1, .wname=wname);
+ recv9p(); /* Rwalk */
+ send9p(Topen, .tag=0, .fid=1, .mode=LIB9P_O_MODE_READ);
+ recv9p(); /* Ropen */
+ send9p(Tread, .tag=0, .fid=1, .offset=0, .count=10);
+ send9p(Tflush, .tag=1, .oldtag=0);
+ recv9p(); /* Rread */
+ recv9p(); /* Rflush */
+
+ /* flush, original request is aborted with error */
+ wname[0] = lib9p_str("flush-error"); send9p(Twalk, .tag=0, .fid=0, .newfid=2, .nwname=1, .wname=wname);
+ recv9p(); /* Rwalk */
+ send9p(Topen, .tag=0, .fid=2, .mode=LIB9P_O_MODE_READ);
+ recv9p(); /* Ropen */
+ send9p(Tread, .tag=0, .fid=2, .offset=0, .count=10);
+ send9p(Tflush, .tag=1, .oldtag=0);
+ recv9p(); /* Rerror */
+ recv9p(); /* Rflush */
+
+ /* flush, original request is aborted without error */
+ wname[0] = lib9p_str("flush-silent"); send9p(Twalk, .tag=0, .fid=0, .newfid=3, .nwname=1, .wname=wname);
+ recv9p(); /* Rwalk */
+ send9p(Topen, .tag=0, .fid=3, .mode=LIB9P_O_MODE_READ);
+ recv9p(); /* Ropen */
+ send9p(Tread, .tag=0, .fid=3, .offset=0, .count=10);
+ send9p(Tflush, .tag=1, .oldtag=0);
+ recv9p(); /* Rflush */
+
+ /* multiflush, original request is aborted without error */
+ wname[0] = lib9p_str("flush-slowsilent"); send9p(Twalk, .tag=0, .fid=0, .newfid=4, .nwname=1, .wname=wname);
+ recv9p(); /* Rwalk */
+ send9p(Topen, .tag=0, .fid=4, .mode=LIB9P_O_MODE_READ);
+ recv9p(); /* Ropen */
+ send9p(Tread, .tag=0, .fid=4, .offset=0, .count=10);
+ send9p(Tflush, .tag=1, .oldtag=0);
+ send9p(Tflush, .tag=2, .oldtag=0);
+ recv9p(); /* Rflush */
+
+ /* flush, but flush is flushed */
+ wname[0] = lib9p_str("flush-slowread"); send9p(Twalk, .tag=0, .fid=0, .newfid=5, .nwname=1, .wname=wname);
+ recv9p(); /* Rwalk */
+ send9p(Topen, .tag=0, .fid=5, .mode=LIB9P_O_MODE_READ);
+ recv9p(); /* Ropen */
+ send9p(Tread, .tag=0, .fid=5, .offset=0, .count=10);
+ send9p(Tflush, .tag=1, .oldtag=0);
+ send9p(Tflush, .tag=2, .oldtag=1);
+ recv9p(); /* Rflush */
+ recv9p(); /* Rread */
+
+ /* flush, unknown tag */
+ send9p(Tflush, .tag=0, .oldtag=99);
+ recv9p(); /* Rflush */
+
+ /* flushed by Tversion */
+ send9p(Tread, .tag=0, .fid=3, .offset=0, .count=10);
+
+ /* shutdown ***********************************************************/
+ send9p(Tversion, .tag=0, .max_msg_size=(8*1024), .version=lib9p_str("9P2000"));
+ recv9p(); /* Rversion */
+ ctx.version = LIB9P_VER_9P2000;
+ send9p(Tattach, .tag=0, .fid=0, .afid=LIB9P_FID_NOFID, .uname=lib9p_str("nobody"), .aname=lib9p_str(""));
+ recv9p(); /* Rattach */
+ /* check the newfid==fid case */
+ wname[0] = lib9p_str("shutdown"); send9p(Twalk, .tag=0, .fid=0, .newfid=0, .nwname=1, .wname=wname);
+ recv9p(); /* Rwalk */
+ send9p(Topen, .tag=0, .fid=0, .mode=LIB9P_O_MODE_WRITE);
+ recv9p(); /* Ropen */
+ send9p(Twrite, .tag=0, .fid=0, .offset=0, .count=2, .data="1\n");
+ recv9p(); /* Rwrite */
+ return 0;
+}
diff --git a/lib9p/tests/testclient-sess.explog b/lib9p/tests/testclient-sess.explog
new file mode 100644
index 0000000..ec8d9c9
--- /dev/null
+++ b/lib9p/tests/testclient-sess.explog
@@ -0,0 +1,156 @@
+# lib9p/tests/testclient-sess.explog - Expected 9P logfile of testclient-sess.c
+#
+# Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+# SPDX-License-Identifier: AGPL-3.0-or-later
+
+# numeric downgrade, unknown ext ###############################################
+> Tversion { tag=0 max_msg_size=57 version="9P2025.x" }
+< Rversion { tag=0 max_msg_size=57 version="9P2000" }
+
+# numeric downgrade, known ext #################################################
+> Tversion { tag=0 max_msg_size=57 version="9P2025.u" }
+< Rversion { tag=0 max_msg_size=57 version="9P2000.u" }
+
+# ext version, users ###########################################################
+> Tversion { tag=0 max_msg_size=8192 version="9P2000.u" }
+< Rversion { tag=0 max_msg_size=4120 version="9P2000.u" }
+> Tattach { tag=0 fid=0 afid=NOFID uname="alice" aname="" unum=1000 }
+< Rattach { tag=0 qid={ type=(DIR) vers=1 path=1 } }
+> Tattach { tag=0 fid=1 afid=NOFID uname="bob" aname="" unum=1001 }
+< Rattach { tag=0 qid={ type=(DIR) vers=1 path=1 } }
+> Twalk { tag=0 fid=0 newfid=2 nwname=1 wname=[ "whoami" ] }
+< Rwalk { tag=0 nwqid=1 wqid=[ { type=(0) vers=1 path=8 } ] }
+> Twalk { tag=0 fid=1 newfid=3 nwname=1 wname=[ "whoami" ] }
+< Rwalk { tag=0 nwqid=1 wqid=[ { type=(0) vers=1 path=8 } ] }
+> Topen { tag=0 fid=2 mode=(MODE_READ) }
+< Ropen { tag=0 qid={ type=(0) vers=1 path=8 } iounit=0 }
+> Topen { tag=0 fid=3 mode=(MODE_READ) }
+< Ropen { tag=0 qid={ type=(0) vers=1 path=8 } iounit=0 }
+> Tread { tag=0 fid=2 offset=0 count=100 }
+< Rread { tag=0 count=11 data="1000 alice\n" }
+> Tread { tag=0 fid=3 offset=0 count=100 }
+< Rread { tag=0 count=9 data="1001 bob\n" }
+
+# walk #########################################################################
+> Tversion { tag=0 max_msg_size=8192 version="9P2000" }
+< Rversion { tag=0 max_msg_size=4120 version="9P2000" }
+> Tattach { tag=0 fid=0 afid=NOFID uname="nobody" aname="" unum=0 }
+< Rattach { tag=0 qid={ type=(DIR) vers=1 path=1 } }
+
+# dup
+> Twalk { tag=0 fid=0 newfid=1 nwname=0 wname=[ ] }
+< Rwalk { tag=0 nwqid=0 wqid=[ ] }
+
+# "The walk request carries as arguments an existing fid"...
+> Twalk { tag=0 fid=2 newfid=3 nwname=0 wname=[ ] }
+< Rerror { tag=0 errstr="bad file number 2" errnum=L_EBADF }
+
+# ..."and a proposed newfid"...
+> Twalk { tag=0 fid=1 newfid=NOFID nwname=0 wname=[ ] }
+< Rerror { tag=0 errstr="cannot assign to NOFID" errnum=L_EBADF }
+
+# ..."(which must not be in use"...
+> Twalk { tag=0 fid=1 newfid=0 nwname=0 wname=[ ] }
+< Rerror { tag=0 errstr="FID already in use" errnum=L_EBADF }
+
+# ..."unless it is the same as fid)"...
+> Twalk { tag=0 fid=1 newfid=1 nwname=0 wname=[ ] }
+< Rwalk { tag=0 nwqid=0 wqid=[ ] }
+
+# ... "that the client wishes to associate with the result of
+# traversing the directory hierarchy by `walking' the heierarchy using
+# the successive path name elements wname."...
+
+# ..."The fid must represent a directory"...
+> Twalk { tag=0 fid=1 newfid=2 nwname=1 wname=[ "README.md" ] }
+< Rwalk { tag=0 nwqid=1 wqid=[ { type=(0) vers=1 path=4 } ] }
+> Twalk { tag=0 fid=2 newfid=3 nwname=1 wname=[ ".." ] }
+< Rerror { tag=0 errstr="not a directory" errnum=L_ENOTDIR }
+
+# ..."unless zero path name elements are specified."
+> Twalk { tag=0 fid=2 newfid=3 nwname=0 wname=[ ] }
+< Rwalk { tag=0 nwqid=0 wqid=[ ] }
+
+# "The fid must be valid in the current session" (tested above)...
+
+# ..."and must not have been opened for I/O by an open or create
+# message."...
+> Topen { tag=0 fid=3 mode=(MODE_READ) }
+< Ropen { tag=0 qid={ type=(0) vers=1 path=4 } iounit=0 }
+> Twalk { tag=0 fid=3 newfid=4 nwname=0 wname=[ ] }
+< Rerror { tag=0 errstr="cannot walk on FID open for I/O" errnum=L_EALREADY }
+
+# flush ########################################################################
+> Tversion { tag=0 max_msg_size=8192 version="9P2000" }
+< Rversion { tag=0 max_msg_size=4120 version="9P2000" }
+> Tattach { tag=0 fid=0 afid=NOFID uname="nobody" aname="" unum=0 }
+< Rattach { tag=0 qid={ type=(DIR) vers=1 path=1 } }
+
+# flush, but original response comes back first
+> Twalk { tag=0 fid=0 newfid=1 nwname=1 wname=[ "flush-read" ] }
+< Rwalk { tag=0 nwqid=1 wqid=[ { type=(0) vers=1 path=9 } ] }
+> Topen { tag=0 fid=1 mode=(MODE_READ) }
+< Ropen { tag=0 qid={ type=(0) vers=1 path=9 } iounit=0 }
+> Tread { tag=0 fid=1 offset=0 count=10 }
+> Tflush { tag=1 oldtag=0 }
+< Rread { tag=0 count=6 data="Sloth\n" }
+< Rflush { tag=1 }
+
+# flush, original request is aborted with error
+> Twalk { tag=0 fid=0 newfid=2 nwname=1 wname=[ "flush-error" ] }
+< Rwalk { tag=0 nwqid=1 wqid=[ { type=(0) vers=1 path=10 } ] }
+> Topen { tag=0 fid=2 mode=(MODE_READ) }
+< Ropen { tag=0 qid={ type=(0) vers=1 path=10 } iounit=0 }
+> Tread { tag=0 fid=2 offset=0 count=10 }
+> Tflush { tag=1 oldtag=0 }
+< Rerror { tag=0 errstr="request canceled by flush" errnum=L_EAGAIN }
+< Rflush { tag=1 }
+
+# flush, original request is aborted without error
+> Twalk { tag=0 fid=0 newfid=3 nwname=1 wname=[ "flush-silent" ] }
+< Rwalk { tag=0 nwqid=1 wqid=[ { type=(0) vers=1 path=11 } ] }
+> Topen { tag=0 fid=3 mode=(MODE_READ) }
+< Ropen { tag=0 qid={ type=(0) vers=1 path=11 } iounit=0 }
+> Tread { tag=0 fid=3 offset=0 count=10 }
+> Tflush { tag=1 oldtag=0 }
+< Rflush { tag=1 }
+
+# multiflush, original request is aborted without error
+> Twalk { tag=0 fid=0 newfid=4 nwname=1 wname=[ "flush-slowsilent" ] }
+< Rwalk { tag=0 nwqid=1 wqid=[ { type=(0) vers=1 path=12 } ] }
+> Topen { tag=0 fid=4 mode=(MODE_READ) }
+< Ropen { tag=0 qid={ type=(0) vers=1 path=12 } iounit=0 }
+> Tread { tag=0 fid=4 offset=0 count=10 }
+> Tflush { tag=1 oldtag=0 }
+> Tflush { tag=2 oldtag=0 }
+< Rflush { tag=2 }
+
+# flush, but flush is flushed
+> Twalk { tag=0 fid=0 newfid=5 nwname=1 wname=[ "flush-slowread" ] }
+< Rwalk { tag=0 nwqid=1 wqid=[ { type=(0) vers=1 path=13 } ] }
+> Topen { tag=0 fid=5 mode=(MODE_READ) }
+< Ropen { tag=0 qid={ type=(0) vers=1 path=13 } iounit=0 }
+> Tread { tag=0 fid=5 offset=0 count=10 }
+> Tflush { tag=1 oldtag=0 }
+> Tflush { tag=2 oldtag=1 }
+< Rflush { tag=2 }
+< Rread { tag=0 count=6 data="Sloth\n" }
+
+# flush, unknown tag
+> Tflush { tag=0 oldtag=99 }
+< Rflush { tag=0 }
+
+# flushed by Tversion
+> Tread { tag=0 fid=3 offset=0 count=10 }
+
+# shutdown #####################################################################
+> Tversion { tag=0 max_msg_size=8192 version="9P2000" }
+< Rversion { tag=0 max_msg_size=4120 version="9P2000" }
+> Tattach { tag=0 fid=0 afid=NOFID uname="nobody" aname="" unum=0 }
+< Rattach { tag=0 qid={ type=(DIR) vers=1 path=1 } }
+> Twalk { tag=0 fid=0 newfid=0 nwname=1 wname=[ "shutdown" ] }
+< Rwalk { tag=0 nwqid=1 wqid=[ { type=(APPEND) vers=1 path=5 } ] }
+> Topen { tag=0 fid=0 mode=(MODE_WRITE) }
+< Ropen { tag=0 qid={ type=(APPEND) vers=1 path=5 } iounit=0 }
+> Twrite { tag=0 fid=0 offset=0 count=2 data="1\n" }
+< Rwrite { tag=0 count=2 }
diff --git a/lib9p/utf8.h b/lib9p/utf8.h
deleted file mode 100644
index 5ffd674..0000000
--- a/lib9p/utf8.h
+++ /dev/null
@@ -1,34 +0,0 @@
-/* lib9p/utf8.h - Internal UTF-8 validation
- *
- * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
-
-#ifndef _LIB9P_UTF8_H_
-#define _LIB9P_UTF8_H_
-
-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)
-
-#endif /* _LIB9P_UTF8_H_ */
diff --git a/lib9p_util/CMakeLists.txt b/lib9p_util/CMakeLists.txt
index 4fbdb7a..feeada7 100644
--- a/lib9p_util/CMakeLists.txt
+++ b/lib9p_util/CMakeLists.txt
@@ -4,10 +4,10 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
add_library(lib9p_util INTERFACE)
-target_include_directories(lib9p_util SYSTEM INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include)
+target_include_directories(lib9p_util PUBLIC INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include)
target_sources(lib9p_util INTERFACE
static.c
)
target_link_libraries(lib9p_util INTERFACE
- lib9p
+ lib9p_srv
)
diff --git a/lib9p_util/include/util9p/static.h b/lib9p_util/include/util9p/static.h
index 0b391b8..4bb24c4 100644
--- a/lib9p_util/include/util9p/static.h
+++ b/lib9p_util/include/util9p/static.h
@@ -9,18 +9,12 @@
#include <lib9p/srv.h>
-#define util9p_box(nam, obj) \
- ((struct lib9p_srv_file){ \
- .self = obj, \
- .vtable = (void*)&_lo_##nam##_lib9p_srv_file_vtable, \
- })
-
#define UTIL9P_ATIME 1728337905
#define UTIL9P_MTIME 1728337904
/* Common *********************************************************************/
-typedef struct {
+struct _util9p_static_common {
char *u_name;
uint32_t u_num;
char *g_name;
@@ -32,7 +26,7 @@ typedef struct {
char *name;
lib9p_dm_t perm;
uint32_t atime, mtime;
-} _util9p_static_common;
+};
#define UTIL9P_STATIC_COMMON(PATH, STRNAME, MODE) \
{ \
@@ -50,36 +44,34 @@ typedef struct {
/* Dir ************************************************************************/
struct util9p_static_dir {
- _util9p_static_common;
+ struct _util9p_static_common c;
/* NULL-terminated */
lo_interface lib9p_srv_file members[];
};
LO_IMPLEMENTATION_H(lib9p_srv_file, struct util9p_static_dir, util9p_static_dir);
-#define lo_box_util9p_static_dir_as_lib9p_srv_file(obj) util9p_box(util9p_static_dir, obj)
-#define UTIL9P_STATIC_DIR(PATH, STRNAME, ...) \
- lo_box_util9p_static_dir_as_lib9p_srv_file(&((struct util9p_static_dir){ \
- ._util9p_static_common = UTIL9P_STATIC_COMMON(PATH, STRNAME, 0555), \
- .members = { __VA_ARGS__ LO_NULL(lib9p_srv_file) }, \
+#define UTIL9P_STATIC_DIR(PATH, STRNAME, ...) \
+ lo_box_util9p_static_dir_as_lib9p_srv_file(&((struct util9p_static_dir){ \
+ .c = UTIL9P_STATIC_COMMON(PATH, STRNAME, 0555), \
+ .members = { __VA_ARGS__ LO_NULL(lib9p_srv_file) }, \
}))
/* File ***********************************************************************/
struct util9p_static_file {
- _util9p_static_common;
+ struct _util9p_static_common c;
char *data_start; /* must not be NULL */
char *data_end; /* may be NULL, in which case data_size is used */
size_t data_size; /* only used if .data_end==NULL */
};
LO_IMPLEMENTATION_H(lib9p_srv_file, struct util9p_static_file, util9p_static_file);
-#define lo_box_util9p_static_file_as_lib9p_srv_file(obj) util9p_box(util9p_static_file, obj)
-#define UTIL9P_STATIC_FILE(PATH, STRNAME, ...) \
- lo_box_util9p_static_file_as_lib9p_srv_file(&((struct util9p_static_file){ \
- ._util9p_static_common = UTIL9P_STATIC_COMMON(PATH, STRNAME, 0444), \
- __VA_ARGS__ \
+#define UTIL9P_STATIC_FILE(PATH, STRNAME, ...) \
+ lo_box_util9p_static_file_as_lib9p_srv_file(&((struct util9p_static_file){ \
+ .c = UTIL9P_STATIC_COMMON(PATH, STRNAME, 0444), \
+ __VA_ARGS__ \
}))
#endif /* _UTIL9P_STATIC_H_ */
diff --git a/lib9p_util/static.c b/lib9p_util/static.c
index 7f1e6b7..338e9c9 100644
--- a/lib9p_util/static.c
+++ b/lib9p_util/static.c
@@ -9,146 +9,134 @@
#include <util9p/static.h>
-LO_IMPLEMENTATION_C(lib9p_srv_file, struct util9p_static_dir, util9p_static_dir, static);
-LO_IMPLEMENTATION_C(lib9p_srv_file, struct util9p_static_file, util9p_static_file, static);
+LO_IMPLEMENTATION_C(lib9p_srv_file, struct util9p_static_dir, util9p_static_dir);
+LO_IMPLEMENTATION_C(lib9p_srv_file, struct util9p_static_file, util9p_static_file);
-LO_IMPLEMENTATION_H(lib9p_srv_dio, struct util9p_static_dir, util9p_static_dir);
-LO_IMPLEMENTATION_C(lib9p_srv_dio, struct util9p_static_dir, util9p_static_dir, static);
-
-LO_IMPLEMENTATION_H(lib9p_srv_fio, struct util9p_static_file, util9p_static_file);
-LO_IMPLEMENTATION_C(lib9p_srv_fio, struct util9p_static_file, util9p_static_file, static);
+LO_IMPLEMENTATION_STATIC(lib9p_srv_dio, struct util9p_static_dir, util9p_static_dio);
+LO_IMPLEMENTATION_STATIC(lib9p_srv_fio, struct util9p_static_file, util9p_static_fio);
/* dir ************************************************************************/
-static void util9p_static_dir_free(struct util9p_static_dir *self) {
+void util9p_static_dir_free(struct util9p_static_dir *self) {
assert(self);
}
-static struct lib9p_qid util9p_static_dir_qid(struct util9p_static_dir *self) {
+struct lib9p_qid util9p_static_dir_qid(struct util9p_static_dir *self) {
assert(self);
return (struct lib9p_qid){
.type = LIB9P_QT_DIR,
.vers = 1,
- .path = self->pathnum,
+ .path = self->c.pathnum,
};
}
-static struct lib9p_stat util9p_static_dir_stat(struct util9p_static_dir *self, struct lib9p_srv_ctx *ctx) {
+error util9p_static_dir_stat(struct util9p_static_dir *self, struct lib9p_srv_ctx *ctx, struct lib9p_srv_stat *out) {
assert(self);
assert(ctx);
- return (struct lib9p_stat){
- .kern_type = 0,
- .kern_dev = 0,
- .file_qid = util9p_static_dir_qid(self),
- .file_mode = LIB9P_DM_DIR | (self->perm & 0555),
- .file_atime = self->atime,
- .file_mtime = self->mtime,
- .file_size = 0,
- .file_name = lib9p_str(self->name),
- .file_owner_uid = lib9p_str(self->u_name),
- .file_owner_gid = lib9p_str(self->g_name),
- .file_last_modified_uid = lib9p_str(self->m_name),
- .file_extension = lib9p_str(NULL),
- .file_owner_n_uid = self->u_num,
- .file_owner_n_gid = self->g_num,
- .file_last_modified_n_uid = self->m_num,
- };
-}
-static void util9p_static_dir_wstat(struct util9p_static_dir *self, struct lib9p_srv_ctx *ctx,
- struct lib9p_stat) {
+ *out = ((struct lib9p_srv_stat){
+ .qid = util9p_static_dir_qid(self),
+ .mode = LIB9P_DM_DIR | (self->c.perm & 0555),
+ .atime_sec = self->c.atime,
+ .mtime_sec = self->c.mtime,
+ .size = 0,
+ .name = lib9p_str(self->c.name),
+ .owner_uid = { .name = lib9p_str(self->c.u_name), .num = self->c.u_num },
+ .owner_gid = { .name = lib9p_str(self->c.g_name), .num = self->c.g_num },
+ .last_modifier_uid = { .name = lib9p_str(self->c.m_name), .num = self->c.m_num },
+ .extension = lib9p_str(NULL),
+ });
+ return ERROR_NULL;
+}
+error util9p_static_dir_wstat(struct util9p_static_dir *self, struct lib9p_srv_ctx *ctx,
+ struct lib9p_srv_stat) {
assert(self);
assert(ctx);
- lib9p_error(&ctx->basectx, LINUX_EROFS, "read-only part of filesystem");
+ return error_new(E_POSIX_EROFS, "read-only part of filesystem");
}
-static void util9p_static_dir_remove(struct util9p_static_dir *self, struct lib9p_srv_ctx *ctx) {
+error util9p_static_dir_remove(struct util9p_static_dir *self, struct lib9p_srv_ctx *ctx) {
assert(self);
assert(ctx);
- lib9p_error(&ctx->basectx, LINUX_EROFS, "read-only part of filesystem");
+ return error_new(E_POSIX_EROFS, "read-only part of filesystem");
}
-static lo_interface lib9p_srv_file util9p_static_dir_dwalk(struct util9p_static_dir *self, struct lib9p_srv_ctx *ctx,
- struct lib9p_s childname) {
+lib9p_srv_file_or_error util9p_static_dir_dwalk(struct util9p_static_dir *self, struct lib9p_srv_ctx *ctx,
+ struct lib9p_s childname) {
assert(self);
assert(ctx);
for (size_t i = 0; !LO_IS_NULL(self->members[i]); i++) {
lo_interface lib9p_srv_file file = self->members[i];
- struct lib9p_stat stat = LO_CALL(file, stat, ctx);
- if (lib9p_ctx_has_error(&ctx->basectx))
- break;
- lib9p_stat_assert(stat);
- if (lib9p_str_eq(stat.file_name, childname))
- return file;
+ struct lib9p_srv_stat stat;
+ error err = LO_CALL(file, stat, ctx, &stat);
+ if (!ERROR_IS_NULL(err))
+ return ERROR_NEW_ERR(lib9p_srv_file, err);
+ lib9p_srv_stat_assert(&stat);
+ if (lib9p_str_eq(stat.name, childname))
+ return ERROR_NEW_VAL(lib9p_srv_file, file);
}
- lib9p_error(&ctx->basectx,
- LINUX_ENOENT, "no such file or directory");
- return LO_NULL(lib9p_srv_file);
+ return ERROR_NEW_ERR(lib9p_srv_file, error_new(E_POSIX_ENOENT, "no such file or directory"));
}
-static lo_interface lib9p_srv_file util9p_static_dir_dcreate(struct util9p_static_dir *self, struct lib9p_srv_ctx *ctx,
- struct lib9p_s LM_UNUSED(childname),
- lib9p_dm_t LM_UNUSED(perm), lib9p_o_t LM_UNUSED(flags)) {
+lib9p_srv_file_or_error util9p_static_dir_dcreate(struct util9p_static_dir *self, struct lib9p_srv_ctx *ctx,
+ struct lib9p_s LM_UNUSED(childname),
+ struct lib9p_srv_userid *LM_UNUSED(user),
+ struct lib9p_srv_userid *LM_UNUSED(group),
+ lib9p_dm_t LM_UNUSED(perm)) {
assert(self);
assert(ctx);
- lib9p_error(&ctx->basectx, LINUX_EROFS, "read-only part of filesystem");
- return LO_NULL(lib9p_srv_file);
+ return ERROR_NEW_ERR(lib9p_srv_file, error_new(E_POSIX_EROFS, "read-only part of filesystem"));
}
-LIB9P_SRV_NOTFILE(struct util9p_static_dir, util9p_static_dir);
+LIB9P_SRV_NOTFILE(, struct util9p_static_dir, util9p_static_dir);
-static lo_interface lib9p_srv_dio util9p_static_dir_dopen(struct util9p_static_dir *self, struct lib9p_srv_ctx *ctx) {
+lib9p_srv_dio_or_error util9p_static_dir_dopen(struct util9p_static_dir *self, struct lib9p_srv_ctx *ctx) {
assert(self);
assert(ctx);
- return lo_box_util9p_static_dir_as_lib9p_srv_dio(self);
+ return ERROR_NEW_VAL(lib9p_srv_dio, LO_BOX(lib9p_srv_dio, self));
+}
+
+static struct lib9p_qid util9p_static_dio_ioqid(struct util9p_static_dir *self) {
+ return util9p_static_dir_qid(self);
}
-static void util9p_static_dir_iofree(struct util9p_static_dir *self) {
+static void util9p_static_dio_iofree(struct util9p_static_dir *self) {
assert(self);
}
-static size_t util9p_static_dir_dread(struct util9p_static_dir *self, struct lib9p_srv_ctx *ctx,
- uint8_t *buf,
- uint32_t byte_count,
- size_t _obj_offset) {
+static lib9p_srv_dirent_or_error util9p_static_dio_dread(struct util9p_static_dir *self, struct lib9p_srv_ctx *ctx, size_t idx) {
assert(self);
assert(ctx);
- uint32_t byte_offset = 0;
- size_t obj_offset = _obj_offset;
- while (!LO_IS_NULL(self->members[obj_offset])) {
- lo_interface lib9p_srv_file file = self->members[obj_offset];
- struct lib9p_stat stat = LO_CALL(file, stat, ctx);
- if (lib9p_ctx_has_error(&ctx->basectx))
- break;
- lib9p_stat_assert(stat);
- uint32_t nbytes = lib9p_stat_marshal(&ctx->basectx, byte_count-byte_offset, &stat,
- &buf[byte_offset]);
- if (!nbytes) {
- if (obj_offset == _obj_offset)
- lib9p_error(&ctx->basectx,
- LINUX_ERANGE, "stat object does not fit into negotiated max message size");
- break;
- }
- byte_offset += nbytes;
- obj_offset++;
- }
- return obj_offset - _obj_offset;
+ lo_interface lib9p_srv_file file = self->members[idx];
+ if (LO_IS_NULL(file))
+ return ERROR_NEW_VAL(lib9p_srv_dirent, (struct lib9p_srv_dirent){});
+
+ struct lib9p_srv_stat stat;
+ error err = LO_CALL(file, stat, ctx, &stat);
+ if (!ERROR_IS_NULL(err))
+ return ERROR_NEW_ERR(lib9p_srv_dirent, err);
+ lib9p_srv_stat_assert(&stat);
+
+ return ERROR_NEW_VAL(lib9p_srv_dirent, ((struct lib9p_srv_dirent){
+ .qid = stat.qid,
+ .name = stat.name,
+ }));
}
/* file ***********************************************************************/
-static void util9p_static_file_free(struct util9p_static_file *self) {
+void util9p_static_file_free(struct util9p_static_file *self) {
assert(self);
}
-static struct lib9p_qid util9p_static_file_qid(struct util9p_static_file *self) {
+struct lib9p_qid util9p_static_file_qid(struct util9p_static_file *self) {
assert(self);
return (struct lib9p_qid){
.type = LIB9P_QT_FILE,
.vers = 1,
- .path = self->pathnum,
+ .path = self->c.pathnum,
};
}
@@ -164,89 +152,83 @@ static inline size_t util9p_static_file_size(struct util9p_static_file *file) {
}
-static struct lib9p_stat util9p_static_file_stat(struct util9p_static_file *self, struct lib9p_srv_ctx *ctx) {
+error util9p_static_file_stat(struct util9p_static_file *self, struct lib9p_srv_ctx *ctx, struct lib9p_srv_stat *out) {
assert(self);
assert(ctx);
-
- return (struct lib9p_stat){
- .kern_type = 0,
- .kern_dev = 0,
- .file_qid = util9p_static_file_qid(self),
- .file_mode = self->perm & 0444,
- .file_atime = self->atime,
- .file_mtime = self->mtime,
- .file_size = (uint64_t)util9p_static_file_size(self),
- .file_name = lib9p_str(self->name),
- .file_owner_uid = lib9p_str(self->u_name),
- .file_owner_gid = lib9p_str(self->g_name),
- .file_last_modified_uid = lib9p_str(self->m_name),
- .file_extension = lib9p_str(NULL),
- .file_owner_n_uid = self->u_num,
- .file_owner_n_gid = self->g_num,
- .file_last_modified_n_uid = self->m_num,
- };
-}
-static void util9p_static_file_wstat(struct util9p_static_file *self, struct lib9p_srv_ctx *ctx,
- struct lib9p_stat) {
+ assert(out);
+
+ *out = ((struct lib9p_srv_stat){
+ .qid = util9p_static_file_qid(self),
+ .mode = self->c.perm & 0444,
+ .atime_sec = self->c.atime,
+ .mtime_sec = self->c.mtime,
+ .size = (uint64_t)util9p_static_file_size(self),
+ .name = lib9p_str(self->c.name),
+ .owner_uid = { .name = lib9p_str(self->c.u_name), .num = self->c.u_num },
+ .owner_gid = { .name = lib9p_str(self->c.g_name), .num = self->c.g_num },
+ .last_modifier_uid = { .name = lib9p_str(self->c.m_name), .num = self->c.m_num },
+ .extension = lib9p_str(NULL),
+ });
+ return ERROR_NULL;
+}
+error util9p_static_file_wstat(struct util9p_static_file *self, struct lib9p_srv_ctx *ctx,
+ struct lib9p_srv_stat) {
assert(self);
assert(ctx);
- lib9p_error(&ctx->basectx, LINUX_EROFS, "read-only part of filesystem");
+ return error_new(E_POSIX_EROFS, "read-only part of filesystem");
}
-static void util9p_static_file_remove(struct util9p_static_file *self, struct lib9p_srv_ctx *ctx) {
+error util9p_static_file_remove(struct util9p_static_file *self, struct lib9p_srv_ctx *ctx) {
assert(self);
assert(ctx);
- lib9p_error(&ctx->basectx, LINUX_EROFS, "read-only part of filesystem");
+ return error_new(E_POSIX_EROFS, "read-only part of filesystem");
}
-LIB9P_SRV_NOTDIR(struct util9p_static_file, util9p_static_file);
+LIB9P_SRV_NOTDIR(, struct util9p_static_file, util9p_static_file);
-static lo_interface lib9p_srv_fio util9p_static_file_fopen(struct util9p_static_file *self, struct lib9p_srv_ctx *ctx,
- bool rd, bool wr, bool trunc) {
+lib9p_srv_fio_or_error util9p_static_file_fopen(struct util9p_static_file *self, struct lib9p_srv_ctx *ctx,
+ bool rd, bool wr, bool trunc) {
assert(self);
assert(ctx);
assert(rd);
assert(!wr);
assert(!trunc);
- return lo_box_util9p_static_file_as_lib9p_srv_fio(self);
+ return ERROR_NEW_VAL(lib9p_srv_fio, LO_BOX(lib9p_srv_fio, self));
}
-static void util9p_static_file_iofree(struct util9p_static_file *self) {
+
+static struct lib9p_qid util9p_static_fio_ioqid(struct util9p_static_file *self) {
+ return util9p_static_file_qid(self);
+}
+static void util9p_static_fio_iofree(struct util9p_static_file *self) {
assert(self);
}
-static uint32_t util9p_static_file_iounit(struct util9p_static_file *self) {
+static uint32_t util9p_static_fio_iounit(struct util9p_static_file *self) {
assert(self);
return 0;
}
-static void util9p_static_file_pread(struct util9p_static_file *self, struct lib9p_srv_ctx *ctx,
- uint32_t byte_count, uint64_t byte_offset,
- struct iovec *ret) {
+static error util9p_static_fio_pread(struct util9p_static_file *self, struct lib9p_srv_ctx *ctx,
+ lo_interface io_writer dst, uint64_t byte_offset, uint32_t byte_count) {
assert(self);
assert(ctx);
- assert(ret);
size_t data_size = util9p_static_file_size(self);
- if (byte_offset > (uint64_t)data_size) {
- lib9p_error(&ctx->basectx,
- LINUX_EINVAL, "offset is past end-of-file length");
- return;
- }
+ if (byte_offset > (uint64_t)data_size)
+ return error_new(E_POSIX_EINVAL, "offset is past end-of-file length");
size_t beg_off = (size_t)byte_offset;
size_t end_off = beg_off + (size_t)byte_count;
if (end_off > data_size)
end_off = data_size;
- ret->iov_base = &self->data_start[beg_off];
- ret->iov_len = end_off-beg_off;
+ return io_write(dst, &self->data_start[beg_off], end_off-beg_off).err;
}
-static uint32_t util9p_static_file_pwrite(struct util9p_static_file *self, struct lib9p_srv_ctx *ctx,
- void *LM_UNUSED(buf),
- uint32_t LM_UNUSED(byte_count),
- uint64_t LM_UNUSED(byte_offset)) {
+static uint32_t_or_error util9p_static_fio_pwrite(struct util9p_static_file *self, struct lib9p_srv_ctx *ctx,
+ const void *LM_UNUSED(buf),
+ uint32_t LM_UNUSED(byte_count),
+ uint64_t LM_UNUSED(byte_offset)) {
assert(self);
assert(ctx);
- lib9p_error(&ctx->basectx, LINUX_EROFS, "read-only part of filesystem");
- return 0;
+ return ERROR_NEW_ERR(uint32_t, error_new(E_POSIX_EROFS, "read-only part of filesystem"));
}
diff --git a/libcr/CMakeLists.txt b/libcr/CMakeLists.txt
index 0a4696c..472820c 100644
--- a/libcr/CMakeLists.txt
+++ b/libcr/CMakeLists.txt
@@ -3,8 +3,11 @@
# Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
# SPDX-License-Identifier: AGPL-3.0-or-later
+add_library(libcr_headers INTERFACE)
+target_include_directories(libcr_headers PUBLIC INTERFACE ${CMAKE_CURRENT_LIST_DIR}/include)
+
add_library(libcr INTERFACE)
-target_include_directories(libcr SYSTEM INTERFACE ${CMAKE_CURRENT_LIST_DIR}/include)
+target_link_libraries(libcr INTERFACE libcr_headers)
target_sources(libcr INTERFACE
coroutine.c
)
@@ -30,7 +33,7 @@ function(add_libcr_matrix_test n defs)
if ("CONFIG_COROUTINE_VALGRIND=1" IN_LIST defs)
add_test(
NAME "libcr/test_matrix${n}"
- COMMAND valgrind --error-exitcode=2 "./test_matrix${n}"
+ COMMAND "${CMAKE_SOURCE_DIR}/build-aux/valgrind" "./test_matrix${n}"
)
else()
add_test(
diff --git a/libcr/coroutine.c b/libcr/coroutine.c
index 33e8141..182e85c 100644
--- a/libcr/coroutine.c
+++ b/libcr/coroutine.c
@@ -349,7 +349,7 @@ static_assert(CONFIG_COROUTINE_NUM > 1);
uintptr_t sp;
#endif
} cr_plat_jmp_buf;
- static void _cr_plat_setjmp_pre(cr_plat_jmp_buf *env [[gnu::unused]]) {
+ static void _cr_plat_setjmp_pre(cr_plat_jmp_buf *env [[maybe_unused]]) {
#if CONFIG_COROUTINE_MEASURE_STACK
env->sp = cr_plat_get_sp();
#endif
@@ -399,6 +399,7 @@ struct coroutine {
#if CONFIG_COROUTINE_VALGRIND
unsigned stack_id;
#endif
+ bool stack_free;
/* 4. env ***************************************************/
cr_plat_jmp_buf env;
@@ -424,9 +425,9 @@ static const uint8_t stack_pattern[] = {
#endif
#if CONFIG_COROUTINE_PROTECT_STACK
#define CR_STACK_GUARD_SIZE \
- LM_ROUND_UP(sizeof(stack_pattern), CR_PLAT_STACK_ALIGNMENT)
+ ((size_t)LM_ROUND_UP(sizeof(stack_pattern), CR_PLAT_STACK_ALIGNMENT))
#else
- #define CR_STACK_GUARD_SIZE 0
+ #define CR_STACK_GUARD_SIZE ((size_t)0)
#endif
/* global variables ***********************************************************/
@@ -457,7 +458,7 @@ static cr_plat_jmp_buf coroutine_gdb_env;
* coroutine_ringbuf queue.
*/
-static struct coroutine coroutine_table[CONFIG_COROUTINE_NUM] = {0};
+static struct coroutine coroutine_table[CONFIG_COROUTINE_NUM] = {};
static struct {
/* tail == head means empty */
/* buf[tail] is the next thing to run */
@@ -468,7 +469,7 @@ static struct {
* we don't have to worry about funny wrap-around behavior
* when head or tail overflow. */
cid_t buf[LM_NEXT_POWER_OF_2(CONFIG_COROUTINE_NUM)];
-} coroutine_ringbuf = {0};
+} coroutine_ringbuf = {};
static cid_t coroutine_running = 0;
static size_t coroutine_cnt = 0;
@@ -547,17 +548,15 @@ cid_t coroutine_allocate_cid(void) {
return 0;
}
-cid_t coroutine_add_with_stack_size(size_t stack_size,
- const char *name,
- cr_fn_t fn, void *args) {
+static cid_t _coroutine_add(void *stack, size_t full_stack_size,
+ const char *name,
+ cr_fn_t fn, void *args) {
cid_t parent = coroutine_running;
if (parent)
assert_cid_state(parent, state == CR_RUNNING);
- assert(stack_size);
+ assert(full_stack_size > 2*CR_STACK_GUARD_SIZE);
assert(fn);
- debugf("coroutine_add_with_stack_size(%zu, \"%s\", %p, %p)...",
- stack_size, name, fn, args);
if (!coroutine_initialized) {
cr_plat_init();
@@ -567,7 +566,7 @@ cid_t coroutine_add_with_stack_size(size_t stack_size,
cid_t child = coroutine_allocate_cid();
if (!child)
return 0;
- debugf("...child=%zu", child);
+ log_debugln("...child=", child);
/* 1. state *************************************************/
coroutine_table[child-1].state = CR_INITIALIZING;
@@ -578,24 +577,36 @@ cid_t coroutine_add_with_stack_size(size_t stack_size,
else
memset(coroutine_table[child-1].name, 0, sizeof(coroutine_table[child-1].name));
+ log_infoln("starting cid ", child,
+ " ", (qstrn, coroutine_table[child-1].name, sizeof(coroutine_table[child-1].name)),
+ ":");
+
/* 3. stack *************************************************/
- coroutine_table[child-1].stack_size = stack_size + 2*CR_STACK_GUARD_SIZE;
- infof("allocing \"%s\" stack with size %zu+2*%zu=%zu",
- name, stack_size, CR_STACK_GUARD_SIZE, coroutine_table[child-1].stack_size);
- coroutine_table[child-1].stack =
- aligned_alloc(CR_PLAT_STACK_ALIGNMENT, coroutine_table[child-1].stack_size);
- infof("... done, stack is [%#zx,%#zx)",
- coroutine_table[child-1].stack + CR_STACK_GUARD_SIZE,
- coroutine_table[child-1].stack + CR_STACK_GUARD_SIZE + stack_size);
+ coroutine_table[child-1].stack_size = full_stack_size;
+ coroutine_table[child-1].stack_free = (stack == NULL);
+ if (!stack) {
+ log_infoln("allocing stack with size ", full_stack_size, "...");
+ stack = aligned_alloc(CR_PLAT_STACK_ALIGNMENT, full_stack_size);
+ log_infoln("...done");
+ }
+ coroutine_table[child-1].stack = stack;
+ log_infoln(" -> full stack is [",
+ (ptr, coroutine_table[child-1].stack), ",",
+ (ptr, coroutine_table[child-1].stack + full_stack_size), ")",
+ " ; size=", full_stack_size);
+ log_infoln(" -> usable stack is [",
+ (ptr, coroutine_table[child-1].stack + CR_STACK_GUARD_SIZE), ",",
+ (ptr, coroutine_table[child-1].stack + full_stack_size - CR_STACK_GUARD_SIZE), ")",
+ " ; size=", full_stack_size - 2*CR_STACK_GUARD_SIZE);
#if CONFIG_COROUTINE_MEASURE_STACK || CONFIG_COROUTINE_PROTECT_STACK
- for (size_t i = 0; i < coroutine_table[child-1].stack_size; i++)
+ for (size_t i = 0; i < full_stack_size; i++)
((uint8_t *)coroutine_table[child-1].stack)[i] =
stack_pattern[i%sizeof(stack_pattern)];
#endif
#if CONFIG_COROUTINE_VALGRIND
coroutine_table[child-1].stack_id = VALGRIND_STACK_REGISTER(
coroutine_table[child-1].stack + CR_STACK_GUARD_SIZE,
- coroutine_table[child-1].stack + CR_STACK_GUARD_SIZE + stack_size);
+ coroutine_table[child-1].stack + full_stack_size - CR_STACK_GUARD_SIZE);
#endif
/* 4. env ***************************************************/
@@ -603,13 +614,13 @@ cid_t coroutine_add_with_stack_size(size_t stack_size,
coroutine_cnt++;
if (!cr_setjmp(&coroutine_add_env)) { /* point=a */
void *stack_base = coroutine_table[child-1].stack
- + CR_STACK_GUARD_SIZE
#if CR_PLAT_STACK_GROWS_DOWNWARD
- + stack_size
+ + full_stack_size - CR_STACK_GUARD_SIZE
+#else
+ + CR_STACK_GUARD_SIZE
#endif
;
- debugf("...stack =%p", coroutine_table[child-1].stack);
- debugf("...stack_base=%p", stack_base);
+ log_debugln("...stack_base=", (ptr, stack_base));
/* run until cr_begin() */
cr_plat_call_with_stack(stack_base, fn, args);
assert_notreached("should cr_begin() instead of returning");
@@ -623,14 +634,37 @@ cid_t coroutine_add_with_stack_size(size_t stack_size,
* didn't actually check. */
cr_restore_interrupts(true);
coroutine_running = parent;
+ log_infoln(" -> done");
return child;
}
+cid_t coroutine_add_with_stack(void *stack, size_t full_stack_size,
+ const char *name,
+ cr_fn_t fn, void *args) {
+ if (name)
+ log_debugln("coroutine_add_with_stack(", (ptr, stack), full_stack_size, ", ", (qstr, name), ", ", (ptr, fn), ", ", (ptr, args), ")...");
+ else
+ log_debugln("coroutine_add_with_stack(", (ptr, stack), full_stack_size, ", ", (ptr, name), ", ", (ptr, fn), ", ", (ptr, args), ")...");
+
+ return _coroutine_add(stack, full_stack_size, name, fn, args);
+}
+
+cid_t coroutine_add_with_stack_size(size_t usable_stack_size,
+ const char *name,
+ cr_fn_t fn, void *args) {
+ if (name)
+ log_debugln("coroutine_add_with_stack_size(", usable_stack_size, ", ", (qstr, name), ", ", (ptr, fn), ", ", (ptr, args), ")...");
+ else
+ log_debugln("coroutine_add_with_stack_size(", usable_stack_size, ", ", (ptr, name), ", ", (ptr, fn), ", ", (ptr, args), ")...");
+
+ return _coroutine_add(NULL, usable_stack_size + 2*CR_STACK_GUARD_SIZE, name, fn, args);
+}
+
/* coroutine_main() ***********************************************************/
void coroutine_main(void) {
- debugf("coroutine_main()");
+ log_debugln("coroutine_main()");
if (!coroutine_initialized) {
cr_plat_init();
coroutine_initialized = true;
@@ -664,8 +698,9 @@ void coroutine_main(void) {
#if CONFIG_COROUTINE_VALGRIND
VALGRIND_STACK_DEREGISTER(coroutine_table[coroutine_running-1].stack_id);
#endif
- free(coroutine_table[coroutine_running-1].stack);
- coroutine_table[coroutine_running-1] = (struct coroutine){0};
+ if (coroutine_table[coroutine_running-1].stack_free)
+ free(coroutine_table[coroutine_running-1].stack);
+ coroutine_table[coroutine_running-1] = (struct coroutine){};
coroutine_cnt--;
}
coroutine_running = 0;
@@ -675,7 +710,7 @@ void coroutine_main(void) {
/* cr_*() *********************************************************************/
void cr_begin(void) {
- debugf("cid=%zu: cr_begin()", coroutine_running);
+ log_debugln("cid=", coroutine_running, ": cr_begin()");
assert_cid_state(coroutine_running, state == CR_INITIALIZING);
bool saved = cr_save_and_disable_interrupts();
@@ -686,7 +721,7 @@ void cr_begin(void) {
cr_restore_interrupts(saved);
}
-static inline void _cr_yield() {
+static inline void _cr_yield(void) {
cid_t next;
while ( !((next = coroutine_ringbuf_pop())) ) {
/* No coroutines are runnable, wait for an interrupt
@@ -707,7 +742,7 @@ static inline void _cr_yield() {
}
void cr_yield(void) {
- debugf("cid=%zu: cr_yield()", coroutine_running);
+ log_debugln("cid=", coroutine_running ,": cr_yield()");
assert(!cr_plat_is_in_intrhandler());
assert_cid_state(coroutine_running, state == CR_RUNNING);
@@ -719,7 +754,7 @@ void cr_yield(void) {
}
void cr_pause_and_yield(void) {
- debugf("cid=%zu: cr_pause_and_yield()", coroutine_running);
+ log_debugln("cid=", coroutine_running, ": cr_pause_and_yield()");
assert(!cr_plat_is_in_intrhandler());
assert_cid_state(coroutine_running, state == CR_RUNNING);
@@ -730,7 +765,7 @@ void cr_pause_and_yield(void) {
}
[[noreturn]] void cr_exit(void) {
- debugf("cid=%zu: cr_exit()", coroutine_running);
+ log_debugln("cid=", coroutine_running, ": cr_exit()");
assert(!cr_plat_is_in_intrhandler());
assert_cid_state(coroutine_running, state == CR_RUNNING);
@@ -747,7 +782,7 @@ static void _cr_unpause(cid_t cid) {
}
void cr_unpause(cid_t cid) {
- debugf("cr_unpause(%zu)", cid);
+ log_debugln("cr_unpause(", cid, ")");
assert(!cr_plat_is_in_intrhandler());
assert_cid_state(coroutine_running, state == CR_RUNNING);
@@ -757,7 +792,7 @@ void cr_unpause(cid_t cid) {
}
void cr_unpause_from_intrhandler(cid_t cid) {
- debugf("cr_unpause_from_intrhandler(%zu)", cid);
+ log_debugln("cr_unpause_from_intrhandler(", cid, ")");
assert(cr_plat_is_in_intrhandler());
_cr_unpause(cid);
diff --git a/libcr/include/libcr/coroutine.h b/libcr/include/libcr/coroutine.h
index 2505782..f0e6e61 100644
--- a/libcr/include/libcr/coroutine.h
+++ b/libcr/include/libcr/coroutine.h
@@ -26,8 +26,7 @@
#ifndef _LIBCR_COROUTINE_H_
#define _LIBCR_COROUTINE_H_
-#include <stddef.h> /* for size_t */
-#include <stdbool.h> /* for bool */
+#include <stddef.h> /* for size_t */
/* Configuration **************************************************************/
@@ -36,6 +35,9 @@
#ifndef CONFIG_COROUTINE_MEASURE_STACK
#error config.h must define CONFIG_COROUTINE_MEASURE_STACK (bool)
#endif
+#ifndef CONFIG_COROUTINE_NAME_LEN
+ #error config.h must define CONFIG_COROUTINE_NAME_LEN (non-negative integer)
+#endif
/* typedefs *******************************************************************/
@@ -81,7 +83,15 @@ typedef void (*cr_fn_t)(void *args);
/* managing coroutines ********************************************************/
/**
- * Call `fn(args)` in a new coroutine with stack size `stack_size`.
+ * Call `fn(args)` in a new coroutine with the `full_stack_size`-sized
+ * block of memory `stack` as the coroutine stack.
+ *
+ * There may be CPU-specific requirements on the alignment of the
+ * `stack` pointer.
+ *
+ * If CONFIG_COROUTINE_PROTECT_STACK: The usable stack size will be
+ * slightly less than `full_stack_size`, in order to make room for a
+ * stack guard at each end.
*
* See the doc comment on c_fn_t for the requirements imposed on fn.
*
@@ -92,17 +102,35 @@ typedef void (*cr_fn_t)(void *args);
* Returns the cid of the newly-created coroutine. May return 0 if
* there are already COROUTINE_NUM active coroutines.
*/
-cid_t coroutine_add_with_stack_size(size_t stack_size, const char *name, cr_fn_t fn, void *args);
+cid_t coroutine_add_with_stack(void *stack, size_t full_stack_size, const char *name, cr_fn_t fn, void *args);
+
+/**
+ * Like coroutine_add_with_stack(), but will use aligned_alloc() to
+ * allocate a block of memory to use as the stack.
+ *
+ * If CONFIG_COROUTINE_PROTECT_STACK: `usable_stack_size` does *not*
+ * include the size of the stack guard at each end; the amount of
+ * memory allocated for the stack will be slightly larger than
+ * `usable_stack_size`.
+ */
+cid_t coroutine_add_with_stack_size(size_t usable_stack_size, const char *name, cr_fn_t fn, void *args);
/**
* Like coroutine_add_with_stack_size(), but uses a default stack size so
* you don't need to think about it.
*
- * Either define CONFIG_COROUTINE_STACK_SIZE_DEFAULT to use for all
- * coroutines, or CONFIG_COROUTINE_STACK_SIZE_{fn} for each COROUTINE
- * function.
+ * Either:
+ * - define CONFIG_COROUTINE_STACK_SIZE_DEFAULT to use for all
+ * coroutines; or
+ * - define/declare CONFIG_COROUTINE_STACK_SIZE_{fn} for each COROUTINE
+ * function; or
+ * - define CONFIG_COROUTINE_STACK_PREALLOCATE and then
+ * define/declare COROUTINE_STACK_{fn} and COROUTINE_STACK_{fn}_len
+ * for each COROUTINE function.
*/
-#ifdef CONFIG_COROUTINE_STACK_SIZE_DEFAULT
+#if defined(CONFIG_COROUTINE_STACK_PREALLOCATE)
+#define coroutine_add(name, fn, args) coroutine_add_with_stack(COROUTINE_STACK_##fn, COROUTINE_STACK_##fn##_len, name, fn, args)
+#elif defined(CONFIG_COROUTINE_STACK_SIZE_DEFAULT)
#define coroutine_add(name, fn, args) coroutine_add_with_stack_size(CONFIG_COROUTINE_STACK_SIZE_DEFAULT, name, fn, args)
#else
#define coroutine_add(name, fn, args) coroutine_add_with_stack_size(CONFIG_COROUTINE_STACK_SIZE_##fn, name, fn, args)
diff --git a/libcr/tests/test_matrix.c b/libcr/tests/test_matrix.c
index 1f23455..eaa4bdc 100644
--- a/libcr/tests/test_matrix.c
+++ b/libcr/tests/test_matrix.c
@@ -1,6 +1,6 @@
/* libcr/tests/test_matrix.c - Tests for libcr
*
- * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
@@ -8,14 +8,14 @@
int a = 1;
-COROUTINE cr_init(void *) {
+COROUTINE init_cr(void *) {
cr_begin();
a = 2;
cr_end();
}
int main() {
- coroutine_add("init", cr_init, NULL);
+ coroutine_add("init", init_cr, NULL);
coroutine_main();
if (a != 2)
return 1;
diff --git a/libcr/tests/test_matrix/config.h b/libcr/tests/test_matrix/config.h
index 978b9ac..decd6de 100644
--- a/libcr/tests/test_matrix/config.h
+++ b/libcr/tests/test_matrix/config.h
@@ -1,4 +1,4 @@
-/* config.h - Compile-time configuration for libcr test_matrix
+/* libcr/tests/test_matrix/config.h - Compile-time configuration for libcr test_matrix
*
* Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/libcr_ipc/CMakeLists.txt b/libcr_ipc/CMakeLists.txt
index 3746584..ed7c0a9 100644
--- a/libcr_ipc/CMakeLists.txt
+++ b/libcr_ipc/CMakeLists.txt
@@ -3,24 +3,35 @@
# Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
# SPDX-License-Identifier: AGPL-3.0-or-later
+add_library(libcr_ipc_headers INTERFACE)
+target_include_directories(libcr_ipc_headers PUBLIC INTERFACE ${CMAKE_CURRENT_LIST_DIR}/include)
+target_link_libraries(libcr_ipc_headers INTERFACE
+ libcr_headers
+ libmisc_headers
+)
+
add_library(libcr_ipc INTERFACE)
-target_include_directories(libcr_ipc SYSTEM INTERFACE ${CMAKE_CURRENT_LIST_DIR}/include)
+target_link_libraries(libcr_ipc INTERFACE libcr_ipc_headers)
target_sources(libcr_ipc INTERFACE
chan.c
- select.c
+ mutex.c
+ rpc.c
+ rwmutex.c
+ sema.c
)
target_link_libraries(libcr_ipc INTERFACE
libcr
+ libmisc
)
set(ipc_tests
chan
- #select
- #mutex
- #owned_mutex
+ mutex
rpc
+ rwmutex
sema
)
foreach(test IN LISTS ipc_tests)
add_lib_test(libcr_ipc "test_${test}")
+ add_lib_test(libcr_ipc "test_${test}_compile")
endforeach()
diff --git a/libcr_ipc/chan.c b/libcr_ipc/chan.c
index 7dd1132..b52dab1 100644
--- a/libcr_ipc/chan.c
+++ b/libcr_ipc/chan.c
@@ -4,37 +4,131 @@
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
+#include <string.h> /* for memcpy() */
+
+#include <libcr/coroutine.h> /* for cid_t, cr_* */
+#include <libmisc/assert.h>
+#include <libmisc/rand.h>
+
+#define IMPLEMENTATION_FOR_LIBCR_IPC_CHAN_H YES
#include <libcr_ipc/chan.h>
-void _cr_chan_dequeue(void *_ch, size_t) {
- struct _cr_chan *ch = _ch;
- _cr_ipc_dll_pop_from_front(&ch->waiters);
-}
+struct _cr_select_waiter {
+ cid_t cid;
+ struct _cr_select_arg_list_node *arg_vec;
+ size_t arg_cnt;
-void _cr_chan_xfer(enum _cr_chan_waiter_typ self_typ, struct _cr_chan *ch, void *val_ptr, size_t val_size) {
- assert(ch);
- assert(val_ptr);
+ size_t ret;
+};
- if (ch->waiters.front && ch->waiter_typ != self_typ) { /* non-blocking fast-path */
- /* Copy. */
- struct _cr_chan_waiter *front = _cr_ipc_dll_node_cast(struct _cr_chan_waiter, ch->waiters.front);
- if (self_typ == _CR_CHAN_SENDER)
- memcpy(front->val_ptr, val_ptr, val_size);
+enum cr_select_class {
+ CR_SELECT_CLASS_DEFAULT,
+ CR_SELECT_CLASS_BLOCKING,
+ CR_SELECT_CLASS_NONBLOCK,
+};
+
+static inline enum cr_select_class cr_select_getclass(struct _cr_select_arg *arg) {
+ switch (arg->op) {
+ case _CR_SELECT_OP_RECV:
+ if (arg->ch->waiters.front && arg->ch->waiters.front->val.op == _CR_SELECT_OP_SEND)
+ return CR_SELECT_CLASS_NONBLOCK;
+ else
+ return CR_SELECT_CLASS_BLOCKING;
+ case _CR_SELECT_OP_SEND:
+ if (arg->ch->waiters.front && arg->ch->waiters.front->val.op == _CR_SELECT_OP_RECV)
+ return CR_SELECT_CLASS_NONBLOCK;
else
- memcpy(val_ptr, front->val_ptr, val_size);
- cr_unpause(front->cid);
- front->dequeue(front->dequeue_arg1,
- front->dequeue_arg2);
+ return CR_SELECT_CLASS_BLOCKING;
+ case _CR_SELECT_OP_DEFAULT:
+ return CR_SELECT_CLASS_DEFAULT;
+ default:
+ assert_notreached("invalid arg->op");
+ }
+}
+
+size_t cr_select_v(size_t arg_cnt, struct _cr_select_arg_list_node arg_vec[]) {
+ size_t cnt_blocking = 0;
+ size_t cnt_nonblock = 0;
+ size_t cnt_default = 0;
+
+ assert(arg_cnt);
+ assert(arg_vec);
+ cr_assert_in_coroutine();
+
+ for (size_t i = 0; i < arg_cnt; i++) {
+ switch (cr_select_getclass(&arg_vec[i].val)) {
+ case CR_SELECT_CLASS_BLOCKING:
+ cnt_blocking++;
+ break;
+ case CR_SELECT_CLASS_NONBLOCK:
+ cnt_nonblock++;
+ break;
+ case CR_SELECT_CLASS_DEFAULT:
+ cnt_default++;
+ break;
+ }
+ }
+
+ if (cnt_nonblock) {
+ size_t choice_among_nonblock = rand_uint63n(cnt_nonblock);
+ size_t choice_among_all = arg_cnt;
+ for (size_t i = 0, seen = 0; i < choice_among_all; i++) {
+ if (cr_select_getclass(&arg_vec[i].val) == CR_SELECT_CLASS_NONBLOCK) {
+ if (seen == choice_among_nonblock)
+ choice_among_all = i;
+ seen++;
+ }
+ }
+ assert(choice_among_all < arg_cnt);
+
+ struct _cr_select_arg *this = &arg_vec[choice_among_all].val;
+ assert(this->ch->waiters.front);
+ struct _cr_select_arg *other = &this->ch->waiters.front->val;
+ assert(this->val_siz == other->val_siz);
+ assert(this->ch == other->ch);
+ switch (this->op) {
+ case _CR_SELECT_OP_SEND:
+ assert(other->op == _CR_SELECT_OP_RECV);
+ memcpy(other->val_ptr, this->val_ptr, this->val_siz);
+ break;
+ case _CR_SELECT_OP_RECV:
+ assert(other->op == _CR_SELECT_OP_SEND);
+ memcpy(this->val_ptr, other->val_ptr, this->val_siz);
+ break;
+ case _CR_SELECT_OP_DEFAULT:
+ assert_notreached("_CR_SELECT_OP_DEFAULT is not CR_SELECT_CLASS_NONBLOCK");
+ }
+ struct _cr_select_waiter *waiter = other->waiter;
+ for (size_t i = 0; i < waiter->arg_cnt; i++) {
+ waiter->arg_vec[i].val.ch->nwaiters--;
+ dlist_remove(&waiter->arg_vec[i].val.ch->waiters, &waiter->arg_vec[i]);
+ if (&waiter->arg_vec[i].val == other)
+ waiter->ret = i;
+ }
+ cr_unpause(waiter->cid);
cr_yield();
- } else { /* blocking slow-path */
- struct _cr_chan_waiter self = {
- .cid = cr_getcid(),
- .val_ptr = val_ptr,
- .dequeue = _cr_chan_dequeue,
- .dequeue_arg1 = ch,
- };
- _cr_ipc_dll_push_to_rear(&ch->waiters, &self);
- ch->waiter_typ = self_typ;
- cr_pause_and_yield();
+ return choice_among_all;
+ }
+
+ if (cnt_default) {
+ for (size_t i = 0; i < arg_cnt; i++)
+ if (cr_select_getclass(&arg_vec[i].val) == CR_SELECT_CLASS_DEFAULT)
+ return i;
+ assert_notreached("should have returned from inside for() loop");
+ }
+
+ assert(cnt_blocking && cnt_blocking == arg_cnt);
+
+ struct _cr_select_waiter waiter = {
+ .cid = cr_getcid(),
+ .arg_vec = arg_vec,
+ .arg_cnt = arg_cnt,
+ };
+ for (size_t i = 0; i < arg_cnt; i++) {
+ arg_vec[i].val.waiter = &waiter;
+ arg_vec[i].val.ch->nwaiters++;
+ dlist_push_to_rear(&arg_vec[i].val.ch->waiters, &arg_vec[i]);
}
+ cr_pause_and_yield();
+ return waiter.ret;
}
diff --git a/libcr_ipc/include/libcr_ipc/_linkedlist.h b/libcr_ipc/include/libcr_ipc/_linkedlist.h
deleted file mode 100644
index 543e058..0000000
--- a/libcr_ipc/include/libcr_ipc/_linkedlist.h
+++ /dev/null
@@ -1,99 +0,0 @@
-/* libcr_ipc/_linkedlist.h - Common low-level linked lists for use in libcr_ipc
- *
- * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
-
-#ifndef _LIBCR_IPC__LINKEDLIST_H_
-#define _LIBCR_IPC__LINKEDLIST_H_
-
-#include <libmisc/assert.h>
-
-/* singly linked list *********************************************************/
-
-typedef struct __cr_ipc_sll_node {
- struct __cr_ipc_sll_node *rear;
-} _cr_ipc_sll_node;
-
-typedef struct {
- _cr_ipc_sll_node *front, *rear;
-} _cr_ipc_sll_root;
-
-#define _cr_ipc_sll_node_cast(node_typ, node_ptr) \
- ({ \
- static_assert(_Generic(node_ptr, _cr_ipc_sll_node *: 1, default: 0), \
- "typeof("#node_ptr") != _cr_ipc_sll_node *"); \
- assert(node_ptr); \
- static_assert(offsetof(node_typ, _cr_ipc_sll_node) == 0); \
- ((node_typ*)(node_ptr)); \
- })
-
-static inline void _cr_ipc_sll_push_to_rear(_cr_ipc_sll_root *root, _cr_ipc_sll_node *node) {
- assert(root);
- node->rear = NULL;
- if (root->rear)
- root->rear->rear = node;
- else
- root->front = node;
- root->rear = node;
-}
-
-static inline void _cr_ipc_sll_pop_from_front(_cr_ipc_sll_root *root) {
- assert(root);
- assert(root->front);
- root->front = root->front->rear;
- if (!root->front)
- root->rear = NULL;
-}
-
-/* doubly linked list *********************************************************/
-
-typedef struct __cr_ipc_dll_node {
- struct __cr_ipc_dll_node *front, *rear;
-} _cr_ipc_dll_node;
-
-typedef struct {
- _cr_ipc_dll_node *front, *rear;
-} _cr_ipc_dll_root;
-
-#define _cr_ipc_dll_node_cast(node_typ, node_ptr) \
- ({ \
- static_assert(_Generic(node_ptr, _cr_ipc_dll_node *: 1, default: 0), \
- "typeof("#node_ptr") != _cr_ipc_dll_node *"); \
- assert(node_ptr); \
- static_assert(offsetof(node_typ, _cr_ipc_dll_node) == 0); \
- ((node_typ*)(node_ptr)); \
- })
-
-static inline void _cr_ipc_dll_push_to_rear(_cr_ipc_dll_root *root, _cr_ipc_dll_node *node) {
- assert(root);
- assert(node);
- node->front = root->rear;
- node->rear = NULL;
- if (root->rear)
- root->rear->rear = node;
- else
- root->front = node;
- root->rear = node;
-}
-
-static inline void _cr_ipc_dll_remove(_cr_ipc_dll_root *root, _cr_ipc_dll_node *node) {
- assert(root);
- assert(node);
- if (node->front)
- node->front->rear = node->rear;
- else
- root->front = node->rear;
- if (node->rear)
- node->rear->front = node->front;
- else
- root->rear = node->front;
-}
-
-static inline void _cr_ipc_dll_pop_from_front(_cr_ipc_dll_root *root) {
- assert(root);
- assert(root->front);
- _cr_ipc_dll_remove(root, root->front);
-}
-
-#endif /* _LIBCR_IPC__LINKEDLIST_H_ */
diff --git a/libcr_ipc/include/libcr_ipc/chan.h b/libcr_ipc/include/libcr_ipc/chan.h
index 5b1e583..1755a97 100644
--- a/libcr_ipc/include/libcr_ipc/chan.h
+++ b/libcr_ipc/include/libcr_ipc/chan.h
@@ -7,21 +7,17 @@
#ifndef _LIBCR_IPC_CHAN_H_
#define _LIBCR_IPC_CHAN_H_
-#include <stdbool.h> /* for bool */
-#include <stddef.h> /* for size_t */
-#include <string.h> /* for memcpy */
+#include <stddef.h> /* for size_t */
-#include <libcr/coroutine.h> /* for cid_t, cr_* */
+#include <libmisc/linkedlist.h> /* for DLIST_DECLARE() */
+#include <libmisc/private.h>
-#include <libcr_ipc/_linkedlist.h>
+/* base channels **************************************************************/
/**
- * CR_CHAN_DECLARE(NAME, VAL_T) declares the following type and
- * methods:
+ * CR_CHAN_DECLARE(NAME, VAL_T) declares the following type:
*
- * type:
- *
- * /**
+ * / **
* * A NAME##_t is a fair unbuffered channel that transports
* * values of type `VAL_T`.
* *
@@ -32,97 +28,163 @@
* * something from an interrupt handler.
* * /
* typedef ... NAME##_t;
+ */
+#define CR_CHAN_DECLARE(NAME, VAL_T) \
+ typedef struct { \
+ struct _cr_chan core; \
+ VAL_T val_typ[0]; \
+ } NAME##_t
+
+/**
+ * cr_chan_send(ch, val) sends `val` over `ch`.
*
- * methods:
+ * @runs_in coroutine
+ * @cr_pauses maybe
+ * @cr_yields always
*
- * /**
- * * NAME##_send(ch, val) sends `val` over `ch`.
- * *
- * * @runs_in coroutine
- * * @cr_pauses maybe
- * * @cr_yields always
- * * /
- * void NAME##_send(NAME##_t *ch, VAL_T val);
+ * void cr_chan_send(NAME##_t *ch, VAL_T val);
+ */
+#define cr_chan_send(CH, VAL) do { \
+ typeof((CH)->val_typ[0]) _val_lvalue = VAL; \
+ (void)cr_select_l(CR_SELECT_SEND(CH, &_val_lvalue)); \
+} while (0)
+
+/**
+ * cr_chan_recv(ch) reads and returns a value from ch.
*
- * /**
- * * NAME##_recv(ch) reads and returns a value from ch.
- * *
- * * @runs_in coroutine
- * * @cr_pauses maybe
- * * @cr_yields always
- * * /
- * VAL_T NAME##_recv(NAME##_t *ch);
+ * @runs_in coroutine
+ * @cr_pauses maybe
+ * @cr_yields always
*
- * /**
- * * NAME##_can_send(ch) returns whether NAME##_send(ch, val)
- * * would run without pausing.
- * *
- * * @runs_in coroutine
- * * @cr_pauses never
- * * @cr_yields never
- * * /
- * bool NAME##_can_send(NAME##_t *ch);
+ * VAL_T cr_chan_recv(NAME##_T ch);
+ */
+#define cr_chan_recv(CH) ({ \
+ typeof((CH)->val_typ[0]) _val_lvalue; \
+ (void)cr_select_l(CR_SELECT_RECV(CH, &_val_lvalue)); \
+ _val_lvalue; \
+})
+
+/**
+ * cr_chan_can_send(ch) returns whether cr_chan_send(ch, val) would
+ * run without pausing.
*
- * /**
- * * NAME##_can_recv(ch) returns whether NAME##_recv(ch) would
- * * return without pausing.
- * *
- * * @runs_in coroutine
- * * @cr_pauses never
- * * @cr_yields never
- * * /
- * NAME##_can_recv(NAME##_t *ch);
+ * @runs_in coroutine
+ * @cr_pauses never
+ * @cr_yields never
+ *
+ * bool cr_chan_can_send(NAME##_t *ch);
*/
-#define CR_CHAN_DECLARE(NAME, VAL_T) \
- typedef struct { \
- struct _cr_chan core; \
- VAL_T vals[0]; \
- } NAME##_t; \
- \
- static inline void NAME##_send(NAME##_t *ch, VAL_T val) { \
- cr_assert_in_coroutine(); \
- _cr_chan_xfer(_CR_CHAN_SENDER, &ch->core, &val, sizeof(val)); \
- } \
- \
- static inline VAL_T NAME##_recv(NAME##_t *ch) { \
- cr_assert_in_coroutine(); \
- VAL_T val; \
- _cr_chan_xfer(_CR_CHAN_RECVER, &ch->core, &val, sizeof(val)); \
- return val; \
- } \
- \
- static inline bool NAME##_can_send(NAME##_t *ch) { \
- cr_assert_in_coroutine(); \
- return ch->core.waiters.front && \
- ch->core.waiter_typ == _CR_CHAN_RECVER; \
- } \
- \
- static inline bool NAME##_can_recv(NAME##_t *ch) { \
- cr_assert_in_coroutine(); \
- return ch->core.waiters.front && \
- ch->core.waiter_typ == _CR_CHAN_SENDER; \
- }
-
-enum _cr_chan_waiter_typ {
- _CR_CHAN_SENDER,
- _CR_CHAN_RECVER,
+#define cr_chan_can_send(CH) ({ \
+ cr_assert_in_coroutine(); \
+ (bool)((CH)->core.waiters.front && \
+ (CH)->core.waiters.front->val.op == _CR_SELECT_OP_RECV); \
+})
+
+/**
+ * cr_chan_can_recv(ch) returns whether cr_chan_recv(ch) would return
+ * without pausing.
+ *
+ * @runs_in coroutine
+ * @cr_pauses never
+ * @cr_yields never
+ *
+ * bool cr_chan_can_recv(NAME##_t *ch);
+ */
+#define cr_chan_can_recv(CH) ({ \
+ cr_assert_in_coroutine(); \
+ (bool)((CH)->core.waiters.front && \
+ (CH)->core.waiters.front->val.op == _CR_SELECT_OP_SEND); \
+})
+
+/**
+ * cr_chan_num_waiters(ch) returns the number of coroutines currently
+ * blocked on the channel.
+ *
+ * @runs_in coroutine
+ * @cr_pauses never
+ * @cr_yields never
+ *
+ * size_t cr_chan_num_waiters(NAME##_t *ch);
+ */
+#define cr_chan_num_waiters(CH) ({ \
+ cr_assert_in_coroutine(); \
+ ((CH)->core.nwaiters); \
+})
+
+DLIST_DECLARE(_cr_select_arg_list);
+struct _cr_chan {
+ struct _cr_select_arg_list waiters;
+ size_t nwaiters;
};
-struct _cr_chan_waiter {
- _cr_ipc_dll_node;
- cid_t cid;
+/* cr_select arguments ********************************************************/
+
+/**
+ * Do not populate cr_select_arg yourself; use the
+ * CR_SELECT_{RECV,SEND,DEFAULT} macros.
+ */
+struct _cr_select_waiter;
+struct _cr_select_arg {
+ enum {
+ _CR_SELECT_OP_RECV,
+ _CR_SELECT_OP_SEND,
+ _CR_SELECT_OP_DEFAULT,
+ } op;
+ struct _cr_chan *ch;
void *val_ptr;
- void (*dequeue)(void *, size_t);
- void *dequeue_arg1;
- size_t dequeue_arg2;
+ size_t val_siz;
+ BEGIN_PRIVATE(LIBCR_IPC_CHAN_H);
+ struct _cr_select_waiter *waiter;
+ END_PRIVATE(LIBCR_IPC_CHAN_H);
};
+DLIST_DECLARE_NODE(_cr_select_arg_list, struct _cr_select_arg);
+#define cr_select_arg _cr_select_arg_list_node
-struct _cr_chan {
- enum _cr_chan_waiter_typ waiter_typ;
- _cr_ipc_dll_root waiters;
-};
+#define CR_SELECT_RECV(CH, VALP) ((struct cr_select_arg){ .val = { \
+ .op = _CR_SELECT_OP_RECV, \
+ .ch = &((CH)->core), \
+ /* The _valp temporary variable is to get the compiler to check that \
+ * the types are compatible. */ \
+ .val_ptr = ({ typeof((CH)->val_typ[0]) *_valp = VALP; _valp; }), \
+ .val_siz = sizeof((CH)->val_typ[0]), \
+}})
+
+/* BUG: It's bogus that CR_SELECT_SEND takes VALP instead of VAL, but
+ * since we need an address, taking VAL would introduce uncomfortable
+ * questions about where VAL sits on the stack. */
+#define CR_SELECT_SEND(CH, VALP) ((struct cr_select_arg){ .val = { \
+ .op = _CR_SELECT_OP_SEND, \
+ .ch = &((CH)->core), \
+ /* The _valp temporary variable is to get the compiler to check that \
+ * the types are compatible. */ \
+ .val_ptr = ({ typeof((CH)->val_typ[0]) *_valp = VALP; _valp; }), \
+ .val_siz = sizeof((CH)->val_typ[0]), \
+}})
-void _cr_chan_dequeue(void *_ch, size_t);
-void _cr_chan_xfer(enum _cr_chan_waiter_typ self_typ, struct _cr_chan *ch, void *val_ptr, size_t val_size);
+#define CR_SELECT_DEFAULT ((struct cr_select_arg){ .val = { \
+ .op = _CR_SELECT_OP_DEFAULT, \
+}})
+
+/* cr_select_v(arg_cnt, arg_vec) **********************************************/
+
+/**
+ * @runs_in coroutine
+ * @cr_pauses maybe
+ * @cr_yields always
+ */
+size_t cr_select_v(size_t arg_cnt, struct cr_select_arg arg_vec[]);
+
+/* cr_select_l(arg1, arg2, arg3, ...) ******************************************/
+
+/**
+ * @runs_in coroutine
+ * @cr_pauses maybe
+ * @cr_yields always
+ */
+#define cr_select_l(...) ({ \
+ struct cr_select_arg _cr_select_args[] = { __VA_ARGS__ }; \
+ cr_select_v(sizeof(_cr_select_args)/sizeof(_cr_select_args[0]), \
+ _cr_select_args); \
+})
#endif /* _LIBCR_IPC_CHAN_H_ */
diff --git a/libcr_ipc/include/libcr_ipc/mutex.h b/libcr_ipc/include/libcr_ipc/mutex.h
index cba8c19..05a6b2e 100644
--- a/libcr_ipc/include/libcr_ipc/mutex.h
+++ b/libcr_ipc/include/libcr_ipc/mutex.h
@@ -1,22 +1,16 @@
/* libcr_ipc/mutex.h - Simple mutexes for libcr
*
- * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
#ifndef _LIBCR_IPC_MUTEX_H_
#define _LIBCR_IPC_MUTEX_H_
-#include <stdbool.h> /* for bool */
+#include <libmisc/linkedlist.h> /* for SLIST_DECLARE() */
+#include <libmisc/private.h>
-#include <libcr/coroutine.h> /* for cid_t, cr_* */
-
-#include <libcr_ipc/_linkedlist.h>
-
-struct _cr_mutex_waiter {
- _cr_ipc_sll_node;
- cid_t cid;
-};
+SLIST_DECLARE(_cr_mutex_waiter_list);
/**
* A cr_mutex_t is a fair mutex.
@@ -27,8 +21,10 @@ struct _cr_mutex_waiter {
* first place, then it has no business unlocking one.
*/
typedef struct {
- bool locked;
- _cr_ipc_sll_root waiters;
+ BEGIN_PRIVATE(LIBCR_IPC_MUTEX_H);
+ bool locked;
+ struct _cr_mutex_waiter_list waiters;
+ END_PRIVATE(LIBCR_IPC_MUTEX_H);
} cr_mutex_t;
/**
@@ -38,21 +34,7 @@ typedef struct {
* @cr_pauses maybe
* @cr_yields maybe
*/
-static inline void cr_mutex_lock(cr_mutex_t *mu) {
- assert(mu);
- cr_assert_in_coroutine();
-
- if (!mu->locked) /* non-blocking fast-path */
- mu->locked = true;
- else { /* blocking slow-path */
- struct _cr_mutex_waiter self = {
- .cid = cr_getcid(),
- };
- _cr_ipc_sll_push_to_rear(&mu->waiters, &self);
- cr_pause_and_yield();
- }
- assert(mu->locked);
-}
+void cr_mutex_lock(cr_mutex_t *mu);
/**
* Unlock the mutex. Unblocks a coroutine that is blocked on
@@ -62,16 +44,6 @@ static inline void cr_mutex_lock(cr_mutex_t *mu) {
* @cr_pauses never
* @cr_yields never
*/
-static inline void cr_mutex_unlock(cr_mutex_t *mu) {
- assert(mu);
- cr_assert_in_coroutine();
-
- assert(mu->locked);
- if (mu->waiters.front) {
- cr_unpause(_cr_ipc_sll_node_cast(struct _cr_mutex_waiter, mu->waiters.front)->cid);
- _cr_ipc_sll_pop_from_front(&mu->waiters);
- } else
- mu->locked = false;
-}
+void cr_mutex_unlock(cr_mutex_t *mu);
#endif /* _LIBCR_IPC_MUTEX_H_ */
diff --git a/libcr_ipc/include/libcr_ipc/owned_mutex.h b/libcr_ipc/include/libcr_ipc/owned_mutex.h
deleted file mode 100644
index c66240f..0000000
--- a/libcr_ipc/include/libcr_ipc/owned_mutex.h
+++ /dev/null
@@ -1,79 +0,0 @@
-/* libcr_ipc/owned_mutex.h - Owned mutexes for libcr
- *
- * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com>
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
-
-#ifndef _LIBCR_IPC_OWNED_MUTEX_H_
-#define _LIBCR_IPC_OWNED_MUTEX_H_
-
-#include <stdbool.h> /* for bool */
-
-#include <libcr/coroutine.h> /* for cid_t, cr_unpause(), cr_pause_and_yield() */
-
-#include <libcr_ipc/_linkedlist.h>
-
-struct _cr_owned_mutex_waiter {
- _cr_ipc_sll_node;
- cid_t cid;
-};
-
-/**
- * A cr_owned_mutex_t is a fair mutex that tracks not just whether it
- * is locked, but which coroutine has it locked.
- *
- * None of the methods have `_from_intrhandler` variants because (1)
- * an interrupt handler can't block, so it shouldn't ever lock a mutex
- * because that can block; and (2) if it can't lock a mutex in the
- * first place, then it has no business unlocking one.
- */
-typedef struct {
- cid_t owner;
- _cr_ipc_sll_root waiters;
-} cr_owned_mutex_t;
-
-/**
- * Lock the mutex. Blocks if it is already locked.
- *
- * @runs_in coroutine
- * @cr_pauses maybe
- * @cr_yields maybe
- */
-static inline void cr_owned_mutex_lock(cr_owned_mutex_t *mu) {
- assert(mu);
-
- if (!mu->owner) /* non-blocking fast-path */
- mu->owner = cr_getcid();
- else { /* blocking slow-path */
- struct _cr_owned_mutex_waiter self = {
- .cid = cr_getcid(),
- };
- _cr_ipc_sll_push_to_rear(&mu->waiters, &self);
- cr_pause_and_yield();
- }
- assert(mu->owner == cr_getcid());
-}
-
-/**
- * Unlock the mutex. Unblocks a coroutine that is blocked on
- * cr_owned_mutex_lock(). Must be the called from the same coroutine
- * that called cr_owned_mutex_lock().
- *
- * @runs_in coroutine
- * @cr_pauses never
- * @cr_yields never
- */
-static inline void cr_owned_mutex_unlock(cr_owned_mutex_t *mu) {
- assert(mu);
-
- assert(mu->owner == cr_getcid());
- if (mu->waiters.front) {
- cid_t waiter = _cr_ipc_sll_node_cast(struct _cr_owned_mutex_waiter, mu->waiters.front)->cid;
- cr_unpause(waiter);
- mu->owner = waiter;
- _cr_ipc_sll_pop_from_front(&mu->waiters);
- } else
- mu->owner = 0;
-}
-
-#endif /* _LIBCR_IPC_OWNED_MUTEX_H_ */
diff --git a/libcr_ipc/include/libcr_ipc/rpc.h b/libcr_ipc/include/libcr_ipc/rpc.h
index 0ff8bbf..85ebdb3 100644
--- a/libcr_ipc/include/libcr_ipc/rpc.h
+++ b/libcr_ipc/include/libcr_ipc/rpc.h
@@ -7,20 +7,13 @@
#ifndef _LIBCR_IPC_RPC_H_
#define _LIBCR_IPC_RPC_H_
-#include <stdbool.h> /* for bool */
-#include <string.h> /* for memcpy() */
-
-#include <libcr/coroutine.h> /* for cid_t, cr_* */
-
-#include <libcr_ipc/_linkedlist.h>
+#include <libcr/coroutine.h> /* for cid_t */
+#include <libmisc/linkedlist.h> /* for SLIST_DECLARE() */
/**
- * CR_RPC_DECLARE(NAME, REQ_T, RESP_T) declares the following types
- * and methods:
+ * CR_RPC_DECLARE(NAME, REQ_T, RESP_T) declares the following types:
*
- * type:
- *
- * /**
+ * / **
* * A NAME##_t is a fair rpc-channel on which the requester submits a
* * value of type `REQ_T` and the responder responds with a value of
* * type `RESP_T`.
@@ -35,196 +28,117 @@
* * _recv_req() and _send_resp().
* typedef ... NAME##_t;
*
- * /**
+ * / **
* * A NAME##_req_t is handle that wraps a REQ_T and is used to return
* * the response RESP_T to the correct requester. `REQ_T req` is the
* * only public member.
* typedef struct { REQ_T req; ... } NAME##_req_t;
+ */
+#define CR_RPC_DECLARE(NAME, REQ_T, RESP_T) \
+ typedef struct { \
+ REQ_T req; \
+ \
+ RESP_T *_resp; /* where to write resp to */ \
+ cid_t _requester; \
+ } NAME##_req_t; \
+ \
+ typedef struct { \
+ struct _cr_rpc core; \
+ NAME##_req_t handle_typ[0]; \
+ } NAME##_t
+
+/* Methods for NAME##_t *******************************************************/
+
+/**
+ * cr_rpc_send_req(ch, req) submits the `req` request over `ch` and
+ * returns the response.
*
- * methods:
+ * @runs_in coroutine
+ * @cr_pauses always
+ * @cr_yields always
*
- * /**
- * * NAME##_send_req(ch, req) submits the `req` request over `ch` and
- * * returns the response.
- * *
- * * @runs_in coroutine
- * * @cr_pauses always
- * * @cr_yields always
- * * /
- * RESP_T NAME##_send_req(NAME##_t *ch, REQ_T req);
+ * RESP_T cr_rpc_send_req(NAME##_t *ch, REQ_T req);
+ */
+#define cr_rpc_send_req(CH, REQ) ({ \
+ cr_assert_in_coroutine(); \
+ typeof((CH)->handle_typ[0].req) _req_lvalue = REQ; \
+ typeof(*(CH)->handle_typ[0]._resp) _resp_lvalue; \
+ _cr_rpc_send_req(&(CH)->core, \
+ &_req_lvalue, sizeof(_req_lvalue), \
+ &_resp_lvalue); \
+ _resp_lvalue; \
+})
+
+/**
+ * cr_rpc_recv_req(ch) reads a request from ch, and returns a
+ * NAME##_req_t handle wrapping that request.
*
- * /**
- * * NAME##_recv_req(ch) reads a request from ch, and returns a
- * * NAME##_req_t handle wrapping that request.
- * *
- * * @runs_in coroutine
- * * @cr_pauses maybe
- * * @cr_yields maybe
- * * /
- * NAME##_req_t NAME##_recv_req(NAME##_t *ch);
+ * @runs_in coroutine
+ * @cr_pauses maybe
+ * @cr_yields maybe
*
- * /**
- * * NAME##_can_recv_req(ch) returns whether NAME##_recv_req(ch)
- * * would return without pausing.
- * *
- * * @runs_in coroutine
- * * @cr_pauses never
- * * @cr_yields never
- * * /
- * bool NAME##_can_recv_req(NAME##_t *ch);
+ * NAME##_req_t cr_rcp_recv_req(NAME##_t *ch);
+ */
+#define cr_rpc_recv_req(CH) ({ \
+ cr_assert_in_coroutine(); \
+ typeof((CH)->handle_typ[0]) ret; \
+ _cr_rpc_recv_req(&(CH)->core, \
+ &ret.req, sizeof(ret.req), \
+ (void **)&ret._resp, \
+ &ret._requester); \
+ ret; \
+})
+
+/**
+ * cr_rpc_can_recv_req(ch) returns whether NAME##_recv_req(ch)
+ * would return without pausing.
*
- * type:
+ * @runs_in coroutine
+ * @cr_pauses never
+ * @cr_yields never
*
- * /**
- * * A NAME##_req_t is a handle that wraps a REQ_T, and is a channel
- * * that a response may be written to.
- * * /
- * typedef ... NAME##_req_t;
+ * bool cr_rpc_can_recv_req(NAME##_t *ch);
+ */
+#define cr_rpc_can_recv_req(CH) ({ \
+ cr_assert_in_coroutine(); \
+ (bool)((CH)->core.waiters.front && \
+ (CH)->core.waiter_typ == _CR_RPC_REQUESTER); \
+})
+
+/* Methods for NAME##_req_t ***************************************************/
+
+/**
+ * cr_rpc_send_resp(req, resp) sends the given response to the given
+ * request.
*
- * methods:
+ * @runs_in coroutine
+ * @cr_pauses never
+ * @cr_yields always
*
- * /**
- * * cr_rpc_send_resp(req, resp) sends the given response to the given
- * * request.
- * *
- * * @runs_in coroutine
- * * @cr_pauses never
- * * @cr_yields always
- * * /
- * void NAME##_send_resp(NAME##_req_t req, RESP_T resp);
+ * void cr_rpc_send_resp(NAME##_req_t req, RESP_T resp);
*/
-#define CR_RPC_DECLARE(NAME, REQ_T, RESP_T) \
- typedef struct { \
- REQ_T req; \
- \
- RESP_T *_resp; /* where to write resp to */ \
- cid_t _requester; \
- } NAME##_req_t; \
- \
- typedef struct { \
- struct _cr_rpc core; \
- NAME##_req_t handle[0]; \
- } NAME##_t; \
- \
- static inline RESP_T NAME##_send_req(NAME##_t *ch, REQ_T req) { \
- cr_assert_in_coroutine(); \
- RESP_T resp; \
- _cr_rpc_send_req(&ch->core, \
- &req, sizeof(req), \
- &resp, sizeof(resp)); \
- return resp; \
- } \
- \
- static inline NAME##_req_t NAME##_recv_req(NAME##_t *ch) { \
- cr_assert_in_coroutine(); \
- NAME##_req_t ret; \
- _cr_rpc_recv_req(&ch->core, \
- &ret.req, sizeof(ret.req), \
- (void **)&ret._resp, \
- &ret._requester); \
- return ret; \
- } \
- \
- static inline bool NAME##_can_recv_req(NAME##_t *ch) { \
- cr_assert_in_coroutine(); \
- return ch->core.waiters.front && \
- ch->core.waiter_typ == _CR_RPC_REQUESTER; \
- } \
- \
- static inline void NAME##_send_resp(NAME##_req_t req, RESP_T resp) { \
- cr_assert_in_coroutine(); \
- *(req._resp) = resp; \
- cr_unpause(req._requester); \
- cr_yield(); \
- }
+#define cr_rpc_send_resp(REQ, RESP) { \
+ cr_assert_in_coroutine(); \
+ *((REQ)._resp) = RESP; \
+ cr_unpause(REQ._requester); \
+ cr_yield(); \
+} while (0)
+
+/* Background details *********************************************************/
enum _cr_rpc_waiter_typ {
_CR_RPC_REQUESTER,
_CR_RPC_RESPONDER,
};
-struct _cr_rpc_requester {
- _cr_ipc_sll_node;
- cid_t cid;
- void *req_ptr; /* where to read req from */
- void *resp_ptr; /* where to write resp to */
-};
-
-struct _cr_rpc_responder {
- _cr_ipc_sll_node;
- /* /* before enqueued | after dequeued */
- /* /* -------------------+-------------------- */
- cid_t cid; /* responder cid | requester cid */
- void *ptr; /* where to write req | where to write resp */
-};
+SLIST_DECLARE(_cr_rpc_waiter_list);
struct _cr_rpc {
enum _cr_rpc_waiter_typ waiter_typ;
- _cr_ipc_sll_root waiters;
+ struct _cr_rpc_waiter_list waiters;
};
-static inline void _cr_rpc_send_req(struct _cr_rpc *ch,
- void *req_ptr, size_t req_size,
- void *resp_ptr, size_t resp_size)
-{
- assert(ch);
- assert(req_ptr);
- assert(resp_ptr);
-
- if (ch->waiters.front && ch->waiter_typ != _CR_RPC_REQUESTER) { /* fast-path (still blocks) */
- struct _cr_rpc_responder *responder =
- _cr_ipc_sll_node_cast(struct _cr_rpc_responder, ch->waiters.front);
- _cr_ipc_sll_pop_from_front(&ch->waiters);
- /* Copy the req to the responder's stack. */
- memcpy(responder->ptr, req_ptr, req_size);
- /* Notify the responder that we have done so. */
- cr_unpause(responder->cid);
- responder->cid = cr_getcid();
- responder->ptr = resp_ptr;
- /* Wait for the responder to set `*resp_ptr`. */
- cr_pause_and_yield();
- } else { /* blocking slow-path */
- struct _cr_rpc_requester self = {
- .cid = cr_getcid(),
- .req_ptr = req_ptr,
- .resp_ptr = resp_ptr,
- };
- _cr_ipc_sll_push_to_rear(&ch->waiters, &self);
- /* Wait for a responder to both copy our req and sed
- * `*resp_ptr`. */
- cr_pause_and_yield();
- }
-}
-
-static inline void _cr_rpc_recv_req(struct _cr_rpc *ch,
- void *req_ptr, size_t req_size,
- void **ret_resp_ptr,
- cid_t *ret_requester)
-{
- assert(ch);
- assert(req_ptr);
- assert(ret_resp_ptr);
- assert(ret_requester);
-
- if (ch->waiters.front && ch->waiter_typ != _CR_RPC_RESPONDER) { /* non-blocking fast-path */
- struct _cr_rpc_requester *requester =
- _cr_ipc_sll_node_cast(struct _cr_rpc_requester, ch->waiters.front);
- _cr_ipc_sll_pop_from_front(&ch->waiters);
-
- memcpy(req_ptr, requester->req_ptr, req_size);
- *ret_requester = requester->cid;
- *ret_resp_ptr = requester->resp_ptr;
- } else { /* blocking slow-path */
- struct _cr_rpc_responder self = {
- .cid = cr_getcid(),
- .ptr = req_ptr,
- };
- _cr_ipc_sll_push_to_rear(&ch->waiters, &self);
- ch->waiter_typ = _CR_RPC_RESPONDER;
- cr_pause_and_yield();
- *ret_requester = self.cid;
- *ret_resp_ptr = self.ptr;
- }
-}
+void _cr_rpc_send_req(struct _cr_rpc *ch, void *req_ptr, size_t req_size, void *resp_ptr);
+void _cr_rpc_recv_req(struct _cr_rpc *ch, void *req_ptr, size_t req_size, void **ret_resp_ptr, cid_t *ret_requester);
#endif /* _LIBCR_IPC_RPC_H_ */
diff --git a/libcr_ipc/include/libcr_ipc/rwmutex.h b/libcr_ipc/include/libcr_ipc/rwmutex.h
new file mode 100644
index 0000000..12ee863
--- /dev/null
+++ b/libcr_ipc/include/libcr_ipc/rwmutex.h
@@ -0,0 +1,76 @@
+/* libcr_ipc/rwmutex.h - Simple read/write mutexes for libcr
+ *
+ * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#ifndef _LIBCR_IPC_RWMUTEX_H_
+#define _LIBCR_IPC_RWMUTEX_H_
+
+#include <libmisc/linkedlist.h> /* for SLIST_DECLARE() */
+#include <libmisc/private.h>
+
+SLIST_DECLARE(_cr_rwmutex_waiter_list);
+
+/**
+ * A cr_rwmutex_t is a fair read/write mutex.
+ *
+ * A waiting writer blocks any new readers; this ensures that the
+ * writer is able to eventually get the lock.
+ *
+ * None of the methods have `_from_intrhandler` variants because (1)
+ * an interrupt handler can't block, so it shouldn't ever lock a mutex
+ * because that can block; and (2) if it can't lock a mutex in the
+ * first place, then it has no business unlocking one.
+ */
+typedef struct {
+ BEGIN_PRIVATE(LIBCR_IPC_RWMUTEX_H);
+ unsigned nreaders;
+ bool locked;
+ bool unpausing;
+ struct _cr_rwmutex_waiter_list waiters;
+ END_PRIVATE(LIBCR_IPC_RWMUTEX_H);
+} cr_rwmutex_t;
+
+/**
+ * Lock the mutex for writing. Blocks if it is already locked.
+ *
+ * @runs_in coroutine
+ * @cr_pauses maybe
+ * @cr_yields maybe
+ */
+void cr_rwmutex_lock(cr_rwmutex_t *mu);
+
+/**
+ * Undo a single cr_rwmutex_lock() call. Unblocks either a single
+ * coroutine that is blocked on cr_rwmutex_lock() or arbitrarily many
+ * coroutines that are blocked on cr_rwmutex_rlock().
+ *
+ * @runs_in coroutine
+ * @cr_pauses never
+ * @cr_yields never
+ */
+void cr_rwmutex_unlock(cr_rwmutex_t *mu);
+
+/**
+ * Lock the mutex for reading. Blocks if it is already locked for
+ * writing.
+ *
+ * @runs_in coroutine
+ * @cr_pauses maybe
+ * @cr_yields maybe
+ */
+void cr_rwmutex_rlock(cr_rwmutex_t *mu);
+
+/**
+ * Undo a single cr_rwmutext_rock() call. If the reader count is
+ * reduced to zero, unblocks a single coroutine that is blocked on
+ * cr_rwmutex_lock().
+ *
+ * @runs_in coroutine
+ * @cr_pauses never
+ * @cr_yields never
+ */
+void cr_rwmutex_runlock(cr_rwmutex_t *mu);
+
+#endif /* _LIBCR_IPC_RWMUTEX_H_ */
diff --git a/libcr_ipc/include/libcr_ipc/select.h b/libcr_ipc/include/libcr_ipc/select.h
deleted file mode 100644
index b845082..0000000
--- a/libcr_ipc/include/libcr_ipc/select.h
+++ /dev/null
@@ -1,88 +0,0 @@
-/* libcr_ipc/select.h - Select between channels
- *
- * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
-
-#include <alloca.h> /* for alloca() */
-#include <stddef.h> /* for size_t */
-
-#include <libmisc/assert.h>
-#include <libmisc/rand.h>
-
-#include <libcr_ipc/chan.h>
-
-#ifndef _LIBCR_IPC_SELECT_H_
-#define _LIBCR_IPC_SELECT_H_
-
-/* arguments ******************************************************************/
-
-/**
- * Do not populate cr_select_arg yourself; use the
- * CR_SELECT_{RECV,SEND,DEFAULT} macros.
- */
-struct cr_select_arg {
- enum {
- _CR_SELECT_OP_RECV,
- _CR_SELECT_OP_SEND,
- _CR_SELECT_OP_DEFAULT,
- } op;
- struct _cr_chan *ch;
- void *val_ptr;
- size_t val_siz;
-};
-
-#define CR_SELECT_RECV(CH, VALP) ({ \
- assert(CH); \
- assert(VALP); \
- /* The _valp indirection is to get the \
- * compiler to check that the types are \
- * compatible. */ \
- typeof((CH)->vals[0]) *_valp = VALP; \
- ((struct cr_select_arg){ \
- .op = _CR_SELECT_OP_RECV, \
- .ch = &((CH)->core), \
- .val_ptr = _valp, \
- .val_siz = sizeof((CH)->vals[0]), \
- }); \
- })
-#define CR_SELECT_SEND(CH, VAL) ({ \
- assert(CH); \
- typeof((CH)->vals[0]) val_lvalue = VAL; \
- ((struct cr_select_arg){ \
- .op = _CR_SELECT_OP_SEND, \
- .ch = &((CH)->core), \
- .val_ptr = &val_lvalue;, \
- .val_siz = sizeof((CH)->vals[0]), \
- }); \
- })
-#define CR_SELECT_DEFAULT \
- ((struct cr_select_arg){ \
- .op = _CR_SELECT_OP_DEFAULT, \
- })
-
-/* cr_select_v(arg_cnt, arg_vec) **********************************************/
-
-enum _cr_select_class {
- _CR_SELECT_CLASS_DEFAULT,
- _CR_SELECT_CLASS_BLOCKING,
- _CR_SELECT_CLASS_NONBLOCK,
-};
-
-struct _cr_select_waiters {
- size_t cnt;
- struct cr_select_arg *args;
- struct _cr_chan_waiter *nodes;
-};
-
-void _cr_select_dequeue(void *_waiters, size_t idx);
-size_t cr_select_v(size_t arg_cnt, struct cr_select_arg arg_vec[]);
-
-/* cr_select_l(arg1, arg2, arg3, ...) ******************************************/
-
-#define cr_select_l(...) ({ \
- struct cr_select_arg _cr_select_args[] = { __VA_ARGS__ }; \
- cr_select_v(sizeof(_cr_select_args)/sizeof(_cr_select_args[0])); \
-})
-
-#endif /* _LIBCR_IPC_SELECT_H_ */
diff --git a/libcr_ipc/include/libcr_ipc/sema.h b/libcr_ipc/include/libcr_ipc/sema.h
index 6db4015..236e9af 100644
--- a/libcr_ipc/include/libcr_ipc/sema.h
+++ b/libcr_ipc/include/libcr_ipc/sema.h
@@ -7,16 +7,10 @@
#ifndef _LIBCR_IPC_SEMA_H_
#define _LIBCR_IPC_SEMA_H_
-#include <stdbool.h>
+#include <libmisc/linkedlist.h> /* for SLIST_DECLARE() */
+#include <libmisc/private.h>
-#include <libcr/coroutine.h> /* for cid_t, cr_* */
-
-#include <libcr_ipc/_linkedlist.h>
-
-struct _cr_sema_waiter {
- _cr_ipc_sll_node;
- cid_t cid;
-};
+SLIST_DECLARE(_cr_sema_waiter_list);
/**
* A cr_sema_t is a fair unbounded[1] counting semaphore.
@@ -24,9 +18,11 @@ struct _cr_sema_waiter {
* [1]: Well, UINT_MAX
*/
typedef struct {
- unsigned int cnt;
- bool unpausing;
- _cr_ipc_sll_root waiters;
+ BEGIN_PRIVATE(LIBCR_IPC_SEMA_H);
+ unsigned int cnt;
+ bool unpausing;
+ struct _cr_sema_waiter_list waiters;
+ END_PRIVATE(LIBCR_IPC_SEMA_H);
} cr_sema_t;
/**
@@ -36,36 +32,14 @@ typedef struct {
* @cr_pauses never
* @cr_yields never
*/
-static inline void cr_sema_signal(cr_sema_t *sema) {
- assert(sema);
- cr_assert_in_coroutine();
-
- bool saved = cr_save_and_disable_interrupts();
- sema->cnt++;
- if (sema->waiters.front && !sema->unpausing) {
- cr_unpause(
- _cr_ipc_sll_node_cast(struct _cr_sema_waiter, sema->waiters.front)->cid);
- sema->unpausing = true;
- }
- cr_restore_interrupts(saved);
-}
+void cr_sema_signal(cr_sema_t *sema);
/**
* Like cr_sema_signal(), but for use from an interrupt handler.
*
* @runs_in intrhandler
*/
-static inline void cr_sema_signal_from_intrhandler(cr_sema_t *sema) {
- assert(sema);
- cr_assert_in_intrhandler();
-
- sema->cnt++;
- if (sema->waiters.front && !sema->unpausing) {
- cr_unpause_from_intrhandler(
- _cr_ipc_sll_node_cast(struct _cr_sema_waiter, sema->waiters.front)->cid);
- sema->unpausing = true;
- }
-}
+void cr_sema_signal_from_intrhandler(cr_sema_t *sema);
/**
* Wait until the semaphore is >0, then decrement it.
@@ -74,27 +48,6 @@ static inline void cr_sema_signal_from_intrhandler(cr_sema_t *sema) {
* @cr_pauses maybe
* @cr_yields maybe
*/
-static inline void cr_sema_wait(cr_sema_t *sema) {
- assert(sema);
- cr_assert_in_coroutine();
-
- bool saved = cr_save_and_disable_interrupts();
-
- struct _cr_sema_waiter self = {
- .cid = cr_getcid(),
- };
- _cr_ipc_sll_push_to_rear(&sema->waiters, &self);
- if (sema->waiters.front != &self || !sema->cnt)
- cr_pause_and_yield();
- assert(sema->waiters.front == &self && sema->cnt);
- _cr_ipc_sll_pop_from_front(&sema->waiters);
- sema->cnt--;
- if (sema->cnt && sema->waiters.front)
- cr_unpause(
- _cr_ipc_sll_node_cast(struct _cr_sema_waiter, sema->waiters.front)->cid);
- else
- sema->unpausing = false;
- cr_restore_interrupts(saved);
-}
+void cr_sema_wait(cr_sema_t *sema);
#endif /* _LIBCR_IPC_SEMA_H_ */
diff --git a/libcr_ipc/mutex.c b/libcr_ipc/mutex.c
new file mode 100644
index 0000000..1b4e626
--- /dev/null
+++ b/libcr_ipc/mutex.c
@@ -0,0 +1,44 @@
+/* libcr_ipc/mutex.c - Simple mutexes for libcr
+ *
+ * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#include <libcr/coroutine.h> /* for cid_t, cr_* */
+#include <libmisc/assert.h>
+
+#define IMPLEMENTATION_FOR_LIBCR_IPC_MUTEX_H YES
+#include <libcr_ipc/mutex.h>
+
+struct cr_mutex_waiter {
+ cid_t cid;
+};
+SLIST_DECLARE_NODE(_cr_mutex_waiter_list, struct cr_mutex_waiter);
+
+void cr_mutex_lock(cr_mutex_t *mu) {
+ assert(mu);
+ cr_assert_in_coroutine();
+
+ if (!mu->locked) /* non-blocking fast-path */
+ mu->locked = true;
+ else { /* blocking slow-path */
+ struct _cr_mutex_waiter_list_node self = { .val = {
+ .cid = cr_getcid(),
+ }};
+ slist_push_to_rear(&mu->waiters, &self);
+ cr_pause_and_yield();
+ }
+ assert(mu->locked);
+}
+
+void cr_mutex_unlock(cr_mutex_t *mu) {
+ assert(mu);
+ cr_assert_in_coroutine();
+
+ assert(mu->locked);
+ if (mu->waiters.front) {
+ cr_unpause(mu->waiters.front->val.cid);
+ slist_pop_from_front(&mu->waiters);
+ } else
+ mu->locked = false;
+}
diff --git a/libcr_ipc/rpc.c b/libcr_ipc/rpc.c
new file mode 100644
index 0000000..b1b7674
--- /dev/null
+++ b/libcr_ipc/rpc.c
@@ -0,0 +1,84 @@
+/* libcr_ipc/rpc.c - Simple request/response system for libcr
+ *
+ * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#include <string.h> /* for memcpy() */
+
+#include <libcr/coroutine.h> /* for cid_t, cr_* */
+#include <libmisc/assert.h>
+
+#include <libcr_ipc/rpc.h>
+
+struct cr_rpc_requester {
+ cid_t cid;
+ void *req_ptr; /* where to read req from */
+ void *resp_ptr; /* where to write resp to */
+};
+struct cr_rpc_responder {
+ /* before enqueued | after dequeued */
+ /* -------------------+-------------------- */
+ cid_t cid; /* responder cid | requester cid */
+ void *ptr; /* where to write req | where to write resp */
+};
+union cr_rpc_waiter {
+ struct cr_rpc_requester requester;
+ struct cr_rpc_responder responder;
+};
+SLIST_DECLARE_NODE(_cr_rpc_waiter_list, union cr_rpc_waiter);
+
+void _cr_rpc_send_req(struct _cr_rpc *ch, void *req_ptr, size_t req_size, void *resp_ptr) {
+ assert(ch);
+ assert(req_ptr);
+ assert(resp_ptr);
+
+ if (ch->waiters.front && ch->waiter_typ != _CR_RPC_REQUESTER) { /* fast-path (still blocks) */
+ struct cr_rpc_responder *responder = &ch->waiters.front->val.responder;
+ slist_pop_from_front(&ch->waiters);
+ /* Copy the req to the responder's stack. */
+ memcpy(responder->ptr, req_ptr, req_size);
+ /* Notify the responder that we have done so. */
+ cr_unpause(responder->cid);
+ responder->cid = cr_getcid();
+ responder->ptr = resp_ptr;
+ /* Wait for the responder to set `*resp_ptr`. */
+ cr_pause_and_yield();
+ } else { /* blocking slow-path */
+ struct _cr_rpc_waiter_list_node self = { .val = { .requester = {
+ .cid = cr_getcid(),
+ .req_ptr = req_ptr,
+ .resp_ptr = resp_ptr,
+ }}};
+ slist_push_to_rear(&ch->waiters, &self);
+ /* Wait for a responder to both copy our req and sed
+ * `*resp_ptr`. */
+ cr_pause_and_yield();
+ }
+}
+
+void _cr_rpc_recv_req(struct _cr_rpc *ch, void *req_ptr, size_t req_size, void **ret_resp_ptr, cid_t *ret_requester) {
+ assert(ch);
+ assert(req_ptr);
+ assert(ret_resp_ptr);
+ assert(ret_requester);
+
+ if (ch->waiters.front && ch->waiter_typ != _CR_RPC_RESPONDER) { /* non-blocking fast-path */
+ struct cr_rpc_requester *requester = &ch->waiters.front->val.requester;
+ slist_pop_from_front(&ch->waiters);
+
+ memcpy(req_ptr, requester->req_ptr, req_size);
+ *ret_requester = requester->cid;
+ *ret_resp_ptr = requester->resp_ptr;
+ } else { /* blocking slow-path */
+ struct _cr_rpc_waiter_list_node self = { .val = { .responder = {
+ .cid = cr_getcid(),
+ .ptr = req_ptr,
+ }}};
+ slist_push_to_rear(&ch->waiters, &self);
+ ch->waiter_typ = _CR_RPC_RESPONDER;
+ cr_pause_and_yield();
+ *ret_requester = self.val.responder.cid;
+ *ret_resp_ptr = self.val.responder.ptr;
+ }
+}
diff --git a/libcr_ipc/rwmutex.c b/libcr_ipc/rwmutex.c
new file mode 100644
index 0000000..191b7fe
--- /dev/null
+++ b/libcr_ipc/rwmutex.c
@@ -0,0 +1,98 @@
+/* libcr_ipc/rwmutex.c - Simple read/write mutexes for libcr
+ *
+ * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#include <libcr/coroutine.h> /* for cid_t, cr_* */
+#include <libmisc/assert.h>
+
+#define IMPLEMENTATION_FOR_LIBCR_IPC_RWMUTEX_H YES
+#include <libcr_ipc/rwmutex.h>
+
+struct cr_rwmutex_waiter {
+ bool is_reader;
+ cid_t cid;
+};
+SLIST_DECLARE_NODE(_cr_rwmutex_waiter_list, struct cr_rwmutex_waiter);
+
+void cr_rwmutex_lock(cr_rwmutex_t *mu) {
+ assert(mu);
+ cr_assert_in_coroutine();
+
+ struct _cr_rwmutex_waiter_list_node self = { .val = {
+ .is_reader = false,
+ .cid = cr_getcid(),
+ }};
+ slist_push_to_rear(&mu->waiters, &self);
+ if (mu->waiters.front != &self || mu->locked)
+ cr_pause_and_yield();
+ assert(mu->waiters.front == &self);
+
+ /* We now hold the lock (and are mu->waiters.front). */
+ slist_pop_from_front(&mu->waiters);
+ assert(mu->nreaders == 0);
+ mu->locked = true;
+ mu->unpausing = false;
+}
+
+void cr_rwmutex_rlock(cr_rwmutex_t *mu) {
+ assert(mu);
+ cr_assert_in_coroutine();
+
+ struct _cr_rwmutex_waiter_list_node self = { .val = {
+ .is_reader = true,
+ .cid = cr_getcid(),
+ }};
+ slist_push_to_rear(&mu->waiters, &self);
+ if (mu->waiters.front != &self || (mu->locked && mu->nreaders == 0))
+ cr_pause_and_yield();
+ assert(mu->waiters.front == &self);
+
+ /* We now hold the lock (and are mu->waiters.front). */
+ slist_pop_from_front(&mu->waiters);
+ mu->nreaders++;
+ mu->locked = true;
+ struct _cr_rwmutex_waiter_list_node *waiter = mu->waiters.front;
+ if (waiter && waiter->val.is_reader) {
+ assert(mu->unpausing);
+ cr_unpause(waiter->val.cid);
+ } else {
+ mu->unpausing = false;
+ }
+}
+
+void cr_rwmutex_unlock(cr_rwmutex_t *mu) {
+ assert(mu);
+ cr_assert_in_coroutine();
+
+ assert(mu->locked);
+ assert(mu->nreaders == 0);
+ assert(!mu->unpausing);
+ if (mu->waiters.front) {
+ struct _cr_rwmutex_waiter_list_node *waiter = mu->waiters.front;
+ mu->unpausing = true;
+ cr_unpause(waiter->val.cid);
+ } else {
+ mu->locked = false;
+ }
+}
+
+void cr_rwmutex_runlock(cr_rwmutex_t *mu) {
+ assert(mu);
+ cr_assert_in_coroutine();
+
+ assert(mu->locked);
+ assert(mu->nreaders > 0);
+ mu->nreaders--;
+ if (mu->nreaders == 0 && !mu->unpausing) {
+ if (mu->waiters.front) {
+ struct _cr_rwmutex_waiter_list_node *waiter = mu->waiters.front;
+ assert(!waiter->val.is_reader);
+ mu->unpausing = true;
+ cr_unpause(waiter->val.cid);
+ } else {
+ mu->locked = false;
+ }
+ }
+}
diff --git a/libcr_ipc/select.c b/libcr_ipc/select.c
deleted file mode 100644
index 4bd9067..0000000
--- a/libcr_ipc/select.c
+++ /dev/null
@@ -1,102 +0,0 @@
-/* libcr_ipc/select.c - Select between channels
- *
- * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
-
-#include <libcr_ipc/select.h>
-
-static inline enum _cr_select_class _cr_select_getclass(struct cr_select_arg arg) {
- switch (arg.op) {
- case _CR_SELECT_OP_RECV:
- if (arg.ch->waiters.front && arg.ch->waiter_typ == _CR_CHAN_SENDER)
- return _CR_SELECT_CLASS_NONBLOCK;
- else
- return _CR_SELECT_CLASS_BLOCKING;
- case _CR_SELECT_OP_SEND:
- if (arg.ch->waiters.front && arg.ch->waiter_typ == _CR_CHAN_RECVER)
- return _CR_SELECT_CLASS_NONBLOCK;
- else
- return _CR_SELECT_CLASS_BLOCKING;
- case _CR_SELECT_OP_DEFAULT:
- return _CR_SELECT_CLASS_DEFAULT;
- default:
- assert_notreached("invalid arg.op");
- }
-}
-
-void _cr_select_dequeue(void *_waiters, size_t idx) {
- struct _cr_select_waiters *waiters = _waiters;
- for (size_t i = 0; i < waiters->cnt; i++)
- _cr_ipc_dll_remove(&(waiters->args[i].ch->waiters),
- &(waiters->nodes[i]));
- waiters->cnt = idx;
-}
-
-size_t cr_select_v(size_t arg_cnt, struct cr_select_arg arg_vec[]) {
- size_t cnt_blocking = 0;
- size_t cnt_nonblock = 0;
- size_t cnt_default = 0;
-
- assert(arg_cnt);
- assert(arg_vec);
- cr_assert_in_coroutine();
-
- for (size_t i = 0; i < arg_cnt; i++) {
- switch (_cr_select_getclass(arg_vec[i])) {
- case _CR_SELECT_CLASS_BLOCKING:
- cnt_blocking++;
- break;
- case _CR_SELECT_CLASS_NONBLOCK:
- cnt_nonblock++;
- break;
- case _CR_SELECT_CLASS_DEFAULT:
- cnt_default++;
- break;
- }
- }
-
- if (cnt_nonblock) {
- size_t choice = rand_uint63n(cnt_nonblock);
- for (size_t i = 0, seen = 0; i < arg_cnt; i++) {
- if (_cr_select_getclass(arg_vec[i]) == _CR_SELECT_CLASS_NONBLOCK) {
- if (seen == choice) {
- _cr_chan_xfer(arg_vec[i].op == _CR_SELECT_OP_RECV
- ? _CR_CHAN_RECVER
- : _CR_CHAN_SENDER,
- arg_vec[i].ch,
- arg_vec[i].val_ptr,
- arg_vec[i].val_siz);
- return i;
- }
- seen++;
- }
- }
- assert_notreached("should have returned from inside for() loop");
- }
-
- if (cnt_default) {
- for (size_t i = 0; i < arg_cnt; i++)
- if (_cr_select_getclass(arg_vec[i]) == _CR_SELECT_CLASS_DEFAULT)
- return i;
- assert_notreached("should have returned from inside for() loop");
- }
-
- struct _cr_select_waiters waiters = {
- .cnt = arg_cnt,
- .args = arg_vec,
- .nodes = alloca(sizeof(struct _cr_chan_waiter) * arg_cnt),
- };
- for (size_t i = 0; i < arg_cnt; i++) {
- waiters.nodes[i] = (struct _cr_chan_waiter){
- .cid = cr_getcid(),
- .val_ptr = arg_vec[i].val_ptr,
- .dequeue = _cr_select_dequeue,
- .dequeue_arg1 = &waiters,
- .dequeue_arg2 = i,
- };
- _cr_ipc_dll_push_to_rear(&arg_vec[i].ch->waiters, &waiters.nodes[i]);
- }
- cr_pause_and_yield();
- return waiters.cnt;
-}
diff --git a/libcr_ipc/sema.c b/libcr_ipc/sema.c
new file mode 100644
index 0000000..f2ac9b6
--- /dev/null
+++ b/libcr_ipc/sema.c
@@ -0,0 +1,62 @@
+/* libcr_ipc/sema.c - Simple semaphores for libcr
+ *
+ * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#include <libcr/coroutine.h> /* for cid_t, cr_* */
+#include <libmisc/assert.h>
+
+#define IMPLEMENTATION_FOR_LIBCR_IPC_SEMA_H YES
+#include <libcr_ipc/sema.h>
+
+struct cr_sema_waiter {
+ cid_t cid;
+};
+SLIST_DECLARE_NODE(_cr_sema_waiter_list, struct cr_sema_waiter);
+
+void cr_sema_signal(cr_sema_t *sema) {
+ assert(sema);
+ cr_assert_in_coroutine();
+
+ bool saved = cr_save_and_disable_interrupts();
+ sema->cnt++;
+ if (sema->waiters.front && !sema->unpausing) {
+ cr_unpause(sema->waiters.front->val.cid);
+ sema->unpausing = true;
+ }
+ cr_restore_interrupts(saved);
+}
+
+void cr_sema_signal_from_intrhandler(cr_sema_t *sema) {
+ assert(sema);
+ cr_assert_in_intrhandler();
+
+ sema->cnt++;
+ if (sema->waiters.front && !sema->unpausing) {
+ cr_unpause_from_intrhandler(sema->waiters.front->val.cid);
+ sema->unpausing = true;
+ }
+}
+
+void cr_sema_wait(cr_sema_t *sema) {
+ assert(sema);
+ cr_assert_in_coroutine();
+
+ bool saved = cr_save_and_disable_interrupts();
+
+ struct _cr_sema_waiter_list_node self = { .val = {
+ .cid = cr_getcid(),
+ }};
+ slist_push_to_rear(&sema->waiters, &self);
+ if (sema->waiters.front != &self || !sema->cnt)
+ cr_pause_and_yield();
+ assert(sema->waiters.front == &self && sema->cnt);
+ slist_pop_from_front(&sema->waiters);
+ sema->cnt--;
+ if (sema->cnt && sema->waiters.front)
+ cr_unpause(sema->waiters.front->val.cid);
+ else
+ sema->unpausing = false;
+ cr_restore_interrupts(saved);
+}
diff --git a/libcr_ipc/tests/config.h b/libcr_ipc/tests/config.h
index b00508c..a16df1d 100644
--- a/libcr_ipc/tests/config.h
+++ b/libcr_ipc/tests/config.h
@@ -1,4 +1,4 @@
-/* config.h - Compile-time configuration for the libcr_ipc tests
+/* libcr_ipc/tests/config.h - Compile-time configuration for the libcr_ipc tests
*
* Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
@@ -7,9 +7,9 @@
#ifndef _CONFIG_H_
#define _CONFIG_H_
-#define CONFIG_COROUTINE_STACK_SIZE_DEFAULT (4*1024)
+#define CONFIG_COROUTINE_STACK_SIZE_DEFAULT (8*1024)
#define CONFIG_COROUTINE_NAME_LEN 16
-#define CONFIG_COROUTINE_NUM 8
+#define CONFIG_COROUTINE_NUM 16
#define CONFIG_COROUTINE_MEASURE_STACK 1
#define CONFIG_COROUTINE_PROTECT_STACK 1
diff --git a/libcr_ipc/tests/test_chan.c b/libcr_ipc/tests/test_chan.c
index 9d6eecf..ee1751f 100644
--- a/libcr_ipc/tests/test_chan.c
+++ b/libcr_ipc/tests/test_chan.c
@@ -1,6 +1,6 @@
/* libcr_ipc/tests/test_chan.c - Tests for <libcr_ipc/chan.h>
*
- * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
@@ -9,41 +9,123 @@
#include "test.h"
-CR_CHAN_DECLARE(intchan, int)
+CR_CHAN_DECLARE(intchan, int);
-COROUTINE cr_producer(void *_ch) {
+/* test 1 *********************************************************************/
+
+COROUTINE test1_producer_cr(void *_ch) {
intchan_t *ch = _ch;
cr_begin();
- intchan_send(ch, 1);
+ cr_chan_send(ch, 1);
- while (!intchan_can_send(ch))
+ while (!cr_chan_can_send(ch))
cr_yield();
- intchan_send(ch, 2);
-
+ cr_chan_send(ch, 2);
cr_end();
}
-COROUTINE cr_consumer(void *_ch) {
+COROUTINE test1_consumer_cr(void *_ch) {
int x;
intchan_t *ch = _ch;
cr_begin();
- x = intchan_recv(ch);
+ x = cr_chan_recv(ch);
test_assert(x == 1);
- x = intchan_recv(ch);
+ x = cr_chan_recv(ch);
test_assert(x == 2);
cr_end();
}
+/* test 2 *********************************************************************/
+
+intchan_t test2_ch[10] = {};
+intchan_t test2_fch = {};
+
+COROUTINE test2_consumer_cr(void *) {
+ cr_begin();
+
+ struct cr_select_arg args[11];
+
+ bool chdone[10] = {};
+ int arg2ch[10];
+ for (;;) {
+ int ret_ch;
+ int i_arg = 0;
+ for (int i_ch = 0; i_ch < 10; i_ch++) {
+ if (!chdone[i_ch]) {
+ args[i_arg] = CR_SELECT_RECV(&test2_ch[i_ch], &ret_ch);
+ arg2ch[i_arg] = i_ch;
+ i_arg++;
+ }
+ }
+ if (!i_arg)
+ break;
+ args[i_arg] = CR_SELECT_DEFAULT; /* check that default doesn't trigger */
+ test_assert(i_arg <= 10);
+ int ret_arg = cr_select_v(i_arg+1, args);
+ test_assert(ret_arg < i_arg);
+ test_assert(arg2ch[ret_arg] == ret_ch);
+ chdone[ret_ch] = true;
+ }
+ int ret_ch, ret_arg;
+ args[0] = CR_SELECT_RECV(&test2_ch[0], &ret_ch);
+ args[1] = CR_SELECT_DEFAULT;
+ ret_arg = cr_select_v(2, args);
+ test_assert(ret_arg == 1);
+
+ int send = 567;
+ args[0] = CR_SELECT_SEND(&test2_fch, &send);
+ args[1] = CR_SELECT_DEFAULT;
+ ret_arg = cr_select_v(2, args);
+ test_assert(ret_arg == 0);
+
+ send = 890;
+ ret_arg = cr_select_l(CR_SELECT_SEND(&test2_fch, &send),
+ CR_SELECT_DEFAULT);
+ test_assert(ret_arg == 1);
+
+ cr_end();
+}
+
+COROUTINE test2_producer_cr(void *_n) {
+ int n = *(int *)_n;
+ cr_begin();
+
+ cr_chan_send(&test2_ch[n], n);
+
+ cr_end();
+}
+
+COROUTINE test2_final_cr(void *) {
+ cr_begin();
+
+ int ret = cr_chan_recv(&test2_fch);
+ printf("ret=%d\n", ret);
+ test_assert(ret == 567);
+
+ cr_end();
+}
+
+/******************************************************************************/
+
int main() {
- intchan_t ch = {0};
- coroutine_add("producer", cr_producer, &ch);
- coroutine_add("consumer", cr_consumer, &ch);
+ printf("== test 1 =========================================\n");
+ intchan_t ch = {};
+ coroutine_add("producer", test1_producer_cr, &ch);
+ coroutine_add("consumer", test1_consumer_cr, &ch);
coroutine_main();
+
+ printf("== test 2 =========================================\n");
+ for (int i = 0; i < 10; i++)
+ coroutine_add("producer", test2_producer_cr, &i);
+ coroutine_add("consumer", test2_consumer_cr, NULL);
+ coroutine_add("final", test2_final_cr, NULL);
+ coroutine_main();
+
return 0;
}
diff --git a/libcr_ipc/tests/test_chan_compile.c b/libcr_ipc/tests/test_chan_compile.c
new file mode 100644
index 0000000..00f3bdc
--- /dev/null
+++ b/libcr_ipc/tests/test_chan_compile.c
@@ -0,0 +1,13 @@
+/* libcr_ipc/tests/test_chan_compile.c - Test that <libcr_ipc/chan.h> compiles
+ *
+ * Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#include <libcr_ipc/chan.h>
+
+CR_CHAN_DECLARE(intchan, int);
+
+int main() {
+ return 0;
+}
diff --git a/libcr_ipc/tests/test_mutex.c b/libcr_ipc/tests/test_mutex.c
new file mode 100644
index 0000000..d08315d
--- /dev/null
+++ b/libcr_ipc/tests/test_mutex.c
@@ -0,0 +1,37 @@
+/* libcr_ipc/tests/test_mutex.c - Tests for <libcr_ipc/mutex.h>
+ *
+ * Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#include <libcr/coroutine.h>
+#include <libcr_ipc/mutex.h>
+
+#include "test.h"
+
+int counter = 0;
+
+COROUTINE worker_cr(void *_mu) {
+ cr_mutex_t *mu = _mu;
+ cr_begin();
+
+ for (int i = 0; i < 100; i++) {
+ cr_mutex_lock(mu);
+ int a = counter;
+ cr_yield();
+ counter = a + 1;
+ cr_mutex_unlock(mu);
+ cr_yield();
+ }
+
+ cr_end();
+}
+
+int main() {
+ cr_mutex_t mu = {};
+ coroutine_add("a", worker_cr, &mu);
+ coroutine_add("b", worker_cr, &mu);
+ coroutine_main();
+ test_assert(counter == 200);
+ return 0;
+}
diff --git a/libcr_ipc/tests/test_mutex_compile.c b/libcr_ipc/tests/test_mutex_compile.c
new file mode 100644
index 0000000..c119ee3
--- /dev/null
+++ b/libcr_ipc/tests/test_mutex_compile.c
@@ -0,0 +1,11 @@
+/* libcr_ipc/tests/test_mutex_compile.c - Test that <libcr_ipc/mutex.h> compiles
+ *
+ * Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#include <libcr_ipc/mutex.h>
+
+int main() {
+ return 0;
+}
diff --git a/libcr_ipc/tests/test_rpc.c b/libcr_ipc/tests/test_rpc.c
index 4aff5ca..ee88f47 100644
--- a/libcr_ipc/tests/test_rpc.c
+++ b/libcr_ipc/tests/test_rpc.c
@@ -9,51 +9,51 @@
#include "test.h"
-CR_RPC_DECLARE(intrpc, int, int)
+CR_RPC_DECLARE(intrpc, int, int);
/* Test that the RPC is fair, have worker1 start waiting first, and
* ensure that it gets the first request. */
-COROUTINE cr_caller(void *_ch) {
+COROUTINE caller_cr(void *_ch) {
intrpc_t *ch = _ch;
cr_begin();
- int resp = intrpc_send_req(ch, 1);
+ int resp = cr_rpc_send_req(ch, 1);
test_assert(resp == 2);
- resp = intrpc_send_req(ch, 3);
+ resp = cr_rpc_send_req(ch, 3);
test_assert(resp == 4);
cr_exit();
}
-COROUTINE cr_worker1(void *_ch) {
+COROUTINE worker1_cr(void *_ch) {
intrpc_t *ch = _ch;
cr_begin();
- intrpc_req_t req = intrpc_recv_req(ch);
+ intrpc_req_t req = cr_rpc_recv_req(ch);
test_assert(req.req == 1);
- intrpc_send_resp(req, 2);
+ cr_rpc_send_resp(req, 2);
cr_exit();
}
-COROUTINE cr_worker2(void *_ch) {
+COROUTINE worker2_cr(void *_ch) {
intrpc_t *ch = _ch;
cr_begin();
- intrpc_req_t req = intrpc_recv_req(ch);
+ intrpc_req_t req = cr_rpc_recv_req(ch);
test_assert(req.req == 3);
- intrpc_send_resp(req, 4);
+ cr_rpc_send_resp(req, 4);
cr_exit();
}
int main() {
- intrpc_t ch = {0};
- coroutine_add("worker1", cr_worker1, &ch);
- coroutine_add("caller", cr_caller, &ch);
- coroutine_add("worker2", cr_worker2, &ch);
+ intrpc_t ch = {};
+ coroutine_add("worker1", worker1_cr, &ch);
+ coroutine_add("caller", caller_cr, &ch);
+ coroutine_add("worker2", worker2_cr, &ch);
coroutine_main();
return 0;
}
diff --git a/libcr_ipc/tests/test_rpc_compile.c b/libcr_ipc/tests/test_rpc_compile.c
new file mode 100644
index 0000000..ed0e0c1
--- /dev/null
+++ b/libcr_ipc/tests/test_rpc_compile.c
@@ -0,0 +1,13 @@
+/* libcr_ipc/tests/test_rpc_compile.c - Test that <libcr_ipc/rpc.h> compiles
+ *
+ * Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#include <libcr_ipc/rpc.h>
+
+CR_RPC_DECLARE(intrpc, int, int);
+
+int main() {
+ return 0;
+}
diff --git a/libcr_ipc/tests/test_rwmutex.c b/libcr_ipc/tests/test_rwmutex.c
new file mode 100644
index 0000000..e79e779
--- /dev/null
+++ b/libcr_ipc/tests/test_rwmutex.c
@@ -0,0 +1,88 @@
+/* libcr_ipc/tests/test_rwmutex.c - Tests for <libcr_ipc/rwmutex.h>
+ *
+ * Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#include <string.h>
+
+#include <libcr/coroutine.h>
+#include <libcr_ipc/rwmutex.h>
+
+#include "test.h"
+
+cr_rwmutex_t mu = {};
+char out[10] = {};
+size_t len = 0;
+
+COROUTINE cr1_reader(void *_mu) {
+ cr_rwmutex_t *mu = _mu;
+ cr_begin();
+
+ cr_rwmutex_rlock(mu);
+ out[len++] = 'r';
+ cr_yield();
+ cr_rwmutex_runlock(mu);
+
+ cr_end();
+}
+
+COROUTINE cr1_writer(void *_mu) {
+ cr_rwmutex_t *mu = _mu;
+ cr_begin();
+
+ cr_rwmutex_lock(mu);
+ out[len++] = 'w';
+ cr_yield();
+ cr_rwmutex_unlock(mu);
+
+ cr_end();
+}
+
+COROUTINE cr2_waiter(void *_ch) {
+ char ch = *(char *)_ch;
+ cr_begin();
+
+ cr_rwmutex_rlock(&mu);
+ out[len++] = ch;
+ cr_rwmutex_runlock(&mu);
+
+ cr_end();
+}
+
+COROUTINE cr2_init(void *) {
+ cr_begin();
+
+ char ch;
+ cr_rwmutex_lock(&mu);
+ ch = 'a'; coroutine_add("wait-a", cr2_waiter, &ch);
+ ch = 'b'; coroutine_add("wait-b", cr2_waiter, &ch);
+ cr_yield();
+ ch = 'c'; coroutine_add("wait-c", cr2_waiter, &ch);
+ cr_rwmutex_unlock(&mu);
+
+ cr_end();
+}
+
+int main() {
+ printf("== test 1 =========================================\n");
+ coroutine_add("r1", cr1_reader, &mu);
+ coroutine_add("r2", cr1_reader, &mu);
+ coroutine_add("w", cr1_writer, &mu);
+ coroutine_add("r3", cr1_reader, &mu);
+ coroutine_add("r4", cr1_reader, &mu);
+ coroutine_main();
+ test_assert(len == 5);
+ test_assert(strcmp(out, "rrwrr") == 0);
+
+ printf("== test 2 =========================================\n");
+ mu = (cr_rwmutex_t){};
+ len = 0;
+ memset(out, 0, sizeof(out));
+ coroutine_add("init", cr2_init, NULL);
+ coroutine_main();
+ test_assert(len == 3);
+ test_assert(strcmp(out, "abc") == 0);
+
+ return 0;
+}
diff --git a/libcr_ipc/tests/test_rwmutex_compile.c b/libcr_ipc/tests/test_rwmutex_compile.c
new file mode 100644
index 0000000..358a535
--- /dev/null
+++ b/libcr_ipc/tests/test_rwmutex_compile.c
@@ -0,0 +1,11 @@
+/* libcr_ipc/tests/test_rwmutex_compile.c - Test that <libcr_ipc/rwmutex.h> compiles
+ *
+ * Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#include <libcr_ipc/rwmutex.h>
+
+int main() {
+ return 0;
+}
diff --git a/libcr_ipc/tests/test_sema.c b/libcr_ipc/tests/test_sema.c
index 3208237..435c01a 100644
--- a/libcr_ipc/tests/test_sema.c
+++ b/libcr_ipc/tests/test_sema.c
@@ -5,13 +5,15 @@
*/
#include <libcr/coroutine.h>
+
+#define IMPLEMENTATION_FOR_LIBCR_IPC_SEMA_H YES /* so we can access .cnt */
#include <libcr_ipc/sema.h>
#include "test.h"
int counter = 0;
-COROUTINE cr_first(void *_sema) {
+COROUTINE first_cr(void *_sema) {
cr_sema_t *sema = _sema;
cr_begin();
@@ -22,7 +24,7 @@ COROUTINE cr_first(void *_sema) {
cr_exit();
}
-COROUTINE cr_second(void *_sema) {
+COROUTINE second_cr(void *_sema) {
cr_sema_t *sema = _sema;
cr_begin();
@@ -33,7 +35,7 @@ COROUTINE cr_second(void *_sema) {
cr_exit();
}
-COROUTINE cr_producer(void *_sema) {
+COROUTINE producer_cr(void *_sema) {
cr_sema_t *sema = _sema;
cr_begin();
@@ -43,7 +45,7 @@ COROUTINE cr_producer(void *_sema) {
cr_end();
}
-COROUTINE cr_consumer(void *_sema) {
+COROUTINE consumer_cr(void *_sema) {
cr_sema_t *sema = _sema;
cr_begin();
@@ -54,19 +56,19 @@ COROUTINE cr_consumer(void *_sema) {
}
int main() {
- cr_sema_t sema = {0};
+ cr_sema_t sema = {};
printf("== test 1 =========================================\n");
- coroutine_add("first", cr_first, &sema);
- coroutine_add("second", cr_second, &sema);
+ coroutine_add("first", first_cr, &sema);
+ coroutine_add("second", second_cr, &sema);
coroutine_main();
test_assert(sema.cnt == 0);
printf("== test 2 =========================================\n");
- coroutine_add("consumer", cr_consumer, &sema);
- coroutine_add("producer", cr_producer, &sema);
+ coroutine_add("consumer", consumer_cr, &sema);
+ coroutine_add("producer", producer_cr, &sema);
coroutine_main();
- coroutine_add("consumer", cr_consumer, &sema);
+ coroutine_add("consumer", consumer_cr, &sema);
coroutine_main();
test_assert(sema.cnt == 0);
diff --git a/libcr_ipc/tests/test_sema_compile.c b/libcr_ipc/tests/test_sema_compile.c
new file mode 100644
index 0000000..8a635b2
--- /dev/null
+++ b/libcr_ipc/tests/test_sema_compile.c
@@ -0,0 +1,11 @@
+/* libcr_ipc/tests/test_sema_compile.c - Test that <libcr_ipc/sema.h> compiles
+ *
+ * Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#include <libcr_ipc/sema.h>
+
+int main() {
+ return 0;
+}
diff --git a/libdhcp/CMakeLists.txt b/libdhcp/CMakeLists.txt
index 72b0952..f14e46d 100644
--- a/libdhcp/CMakeLists.txt
+++ b/libdhcp/CMakeLists.txt
@@ -4,10 +4,17 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
add_library(libdhcp INTERFACE)
-target_include_directories(libdhcp SYSTEM INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include)
+target_include_directories(libdhcp PUBLIC INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include)
target_sources(libdhcp INTERFACE
dhcp_client.c
+ dhcp_common.c
)
target_link_libraries(libdhcp INTERFACE
libmisc
+ libhw_generic
)
+
+if (ENABLE_TESTS)
+ add_lib_test(libdhcp test_client)
+ target_link_libraries(test_client libcr libhw_cr)
+endif()
diff --git a/libdhcp/dhcp_client.c b/libdhcp/dhcp_client.c
index 8ec3647..1164355 100644
--- a/libdhcp/dhcp_client.c
+++ b/libdhcp/dhcp_client.c
@@ -86,8 +86,8 @@
#include <string.h> /* for strlen(), memcpy(), memset() */
-#include <libmisc/rand.h>
#include <libhw/generic/alarmclock.h>
+#include <libmisc/rand.h>
#define LOG_NAME DHCP
#include <libmisc/log.h>
@@ -179,9 +179,10 @@ static const char *state_strs[] = {
};
/**
- * For convenience in switch blocks, a list of the states that, when
- * msgtyp==DHCP_MSGTYP_REQUEST, dhcp_client_send() has assert()ed the state is
- * not.
+ * IMPOSSIBLE_REQUEST_STATES is a convenience macro for use in switch
+ * blocks; it is a list of the states that (when
+ * msgtyp==DHCP_MSGTYP_REQUEST) dhcp_client_send() has assert()ed the
+ * state is not.
*/
#define IMPOSSIBLE_REQUEST_STATES \
STATE_INIT: \
@@ -251,11 +252,8 @@ static inline enum requirement dhcp_table5(typeof((struct dhcp_client){}.state)
* @param client->lease_client_addr (sometimes)
* @param client->lease_server_id (sometimes)
* @param client->sock
- *
- * @return client->last_sent_msgtyp
- * @return whether there was an error
*/
-static bool dhcp_client_send(struct dhcp_client *client, uint8_t msgtyp, const char *errstr, struct dhcp_msg *scratch_msg) {
+static error dhcp_client_send(struct dhcp_client *client, uint8_t msgtyp, const char *errstr, struct dhcp_msg *scratch_msg) {
/**********************************************************************\
* Preconditions *
\**********************************************************************/
@@ -297,7 +295,7 @@ static bool dhcp_client_send(struct dhcp_client *client, uint8_t msgtyp, const c
* Build the message *
\**********************************************************************/
- *scratch_msg = (struct dhcp_msg){0};
+ *scratch_msg = (struct dhcp_msg){};
size_t optlen = 0;
/* Base structure.
@@ -460,15 +458,15 @@ static bool dhcp_client_send(struct dhcp_client *client, uint8_t msgtyp, const c
/**********************************************************************\
* Send *
\**********************************************************************/
- debugf("state %s: sending DHCP %s", state_strs[client->state], dhcp_msgtyp_str(msgtyp));
- ssize_t r = LO_CALL(client->sock, sendto, scratch_msg, DHCP_MSG_BASE_SIZE + optlen,
- client_broadcasts ? net_ip4_addr_broadcast : client->lease_server_id, DHCP_PORT_SERVER);
- if (r < 0) {
- debugf("error: sendto: %zd", r);
- return true;
+ log_debugln("state ", state_strs[client->state], ": sending DHCP ", dhcp_msgtyp_str(msgtyp));
+ error err = LO_CALL(client->sock, sendto, scratch_msg, DHCP_MSG_BASE_SIZE + optlen,
+ client_broadcasts ? net_ip4_addr_broadcast : client->lease_server_id, DHCP_PORT_SERVER);
+ if (!ERROR_IS_NULL(err)) {
+ log_debugln("error: sendto: ", (error, err));
+ return err;
}
client->last_sent_msgtyp = msgtyp;
- return false;
+ return ERROR_NULL;
}
struct dhcp_recv_msg {
@@ -564,24 +562,20 @@ static inline enum requirement dhcp_table3(uint8_t req_msgtyp, uint8_t resp_msgt
* @param client->self_eth_addr
* @param client->xid
* @param client->lease_server_id
- *
- * @return
- * - <0: -errno
- * - 0: success
- * - >0: should not happen
*/
-static ssize_t dhcp_client_recv(struct dhcp_client *client, struct dhcp_recv_msg *ret) {
+static error dhcp_client_recv(struct dhcp_client *client, struct dhcp_recv_msg *ret) {
struct net_ip4_addr srv_addr;
uint16_t srv_port;
- ssize_t msg_len;
+ size_t msg_len;
assert(client);
ignore:
- msg_len = LO_CALL(client->sock, recvfrom, &ret->raw, sizeof(ret->raw), &srv_addr, &srv_port);
- if (msg_len < 0)
+ size_t_or_error r = LO_CALL(client->sock, recvfrom, &ret->raw, sizeof(ret->raw), &srv_addr, &srv_port);
+ if (r.is_err)
/* msg_len is -errno */
- return msg_len;
+ return r.err;
+ msg_len = r.size_t;
/* Validate L3: IP */
/* Don't validate that srv_addr matches client->server_id
@@ -594,10 +588,10 @@ static ssize_t dhcp_client_recv(struct dhcp_client *client, struct dhcp_recv_msg
goto ignore;
/* Validate L5: DHCP. */
- if ((size_t)msg_len < DHCP_MSG_BASE_SIZE + sizeof(dhcp_magic_cookie))
+ if (msg_len < DHCP_MSG_BASE_SIZE + sizeof(dhcp_magic_cookie))
/* ignore impossibly short message */
goto ignore;
- if ((size_t)msg_len > sizeof(ret->raw))
+ if (msg_len > sizeof(ret->raw))
/* ignore message that is larger than the specified
* DHCP_OPT_DHCP_MAX_MSG_SIZE */
goto ignore;
@@ -697,15 +691,7 @@ static ssize_t dhcp_client_recv(struct dhcp_client *client, struct dhcp_recv_msg
goto ignore;
}
- return 0;
-}
-
-/** @return true if there's a conflict, false if the addr appears to be unused */
-static bool dhcp_check_conflict(lo_interface net_packet_conn sock, struct net_ip4_addr addr) {
- assert(!LO_IS_NULL(sock));
- ssize_t v = LO_CALL(sock, sendto, "CHECK_IP_CONFLICT", 17, addr, 5000);
- debugf("check_ip_conflict => %zd", v);
- return v != -NET_EARP_TIMEOUT;
+ return ERROR_NULL;
}
static void dhcp_client_take_lease(struct dhcp_client *client, struct dhcp_recv_msg *msg, bool ifup) {
@@ -740,10 +726,10 @@ static void dhcp_client_take_lease(struct dhcp_client *client, struct dhcp_recv_
client->lease_time_ns_end = (dur_ns_end == DHCP_INFINITY * NS_PER_S) ? 0 : client->time_ns_init + dur_ns_end;
if (ifup) {
- infof("applying configuration to "PRI_net_eth_addr, ARG_net_eth_addr(client->self_eth_addr));
- infof(":: addr = "PRI_net_ip4_addr, ARG_net_ip4_addr(client->lease_client_addr));
- infof(":: gateway_addr = "PRI_net_ip4_addr, ARG_net_ip4_addr(client->lease_auxdata.gateway_addr));
- infof(":: subnet_mask = "PRI_net_ip4_addr, ARG_net_ip4_addr(client->lease_auxdata.subnet_mask));
+ log_infoln("applying configuration to ", (net_eth_addr, client->self_eth_addr));
+ log_infoln(":: addr = ", (net_ip4_addr, client->lease_client_addr));
+ log_infoln(":: gateway_addr = ", (net_ip4_addr, client->lease_auxdata.gateway_addr));
+ log_infoln(":: subnet_mask = ", (net_ip4_addr, client->lease_auxdata.subnet_mask));
LO_CALL(client->iface, ifup, (struct net_iface_config){
.addr = client->lease_client_addr,
.gateway_addr = client->lease_auxdata.gateway_addr,
@@ -755,19 +741,20 @@ static void dhcp_client_take_lease(struct dhcp_client *client, struct dhcp_recv_
static void dhcp_client_setstate(struct dhcp_client *client,
typeof((struct dhcp_client){}.state) newstate,
uint8_t send_msgtyp, const char *errstr, struct dhcp_recv_msg *scratch_msg) {
- if (send_msgtyp)
- (void)dhcp_client_send(client, send_msgtyp, errstr, &scratch_msg->raw);
+ if (send_msgtyp) {
+ error err = dhcp_client_send(client, send_msgtyp, errstr, &scratch_msg->raw);
+ error_cleanup(&err);
+ }
client->state = newstate;
}
[[noreturn]] static void dhcp_client_run(struct dhcp_client *client, struct dhcp_recv_msg *scratch_msg) {
assert(client);
- ssize_t r;
-
/* State transition diagram: https://datatracker.ietf.org/doc/html/rfc2131#page-35 */
for (;;) {
- debugf("loop: state=%s", state_strs[client->state]);
+ [[gnu::cleanup(error_cleanup)]] error err = {};
+ log_debugln("loop: state=", state_strs[client->state]);
switch (client->state) {
case STATE_INIT:
client->xid = rand_uint63n(UINT32_MAX);
@@ -776,8 +763,8 @@ static void dhcp_client_setstate(struct dhcp_client *client,
break;
case STATE_SELECTING:
LO_CALL(client->sock, set_recv_deadline, client->time_ns_init+CONFIG_DHCP_SELECTING_NS);
- switch ((r = dhcp_client_recv(client, scratch_msg))) {
- case 0:
+ LM_PARTIAL_SWITCH ((err = dhcp_client_recv(client, scratch_msg)).num) {
+ case E_NOERROR:
switch (scratch_msg->option_dat[scratch_msg->options[DHCP_OPT_DHCP_MSG_TYPE].off]) {
case DHCP_MSGTYP_OFFER:
/* Accept the first offer. */
@@ -788,26 +775,24 @@ static void dhcp_client_setstate(struct dhcp_client *client,
/* ignore */
}
break;
- case -NET_ERECV_TIMEOUT:
+ case E_NET_ERECV_TIMEOUT:
dhcp_client_setstate(client, STATE_INIT, 0, NULL, scratch_msg);
break;
default:
- assert(r < 0);
- debugf("error: recvfrom: %d", r);
+ log_debugln("error: recvfrom: ", (error, err));
}
break;
case STATE_REQUESTING:
LO_CALL(client->sock, set_recv_deadline, 0);
- switch ((r = dhcp_client_recv(client, scratch_msg))) {
- case 0:
+ LM_PARTIAL_SWITCH ((err = dhcp_client_recv(client, scratch_msg)).num) {
+ case E_NOERROR:
switch (scratch_msg->option_dat[scratch_msg->options[DHCP_OPT_DHCP_MSG_TYPE].off]) {
case DHCP_MSGTYP_NAK:
dhcp_client_setstate(client, STATE_INIT, 0, NULL, scratch_msg);
break;
case DHCP_MSGTYP_ACK:
- if (dhcp_check_conflict(client->sock, client->lease_client_addr)) {
- debugf("IP "PRI_net_ip4_addr" is already in use",
- ARG_net_ip4_addr(client->lease_client_addr));
+ if (LO_CALL(client->iface, arp_ping, client->lease_client_addr)) {
+ log_debugln("IP ", (net_ip4_addr, client->lease_client_addr), " is already in use");
dhcp_client_setstate(client, STATE_INIT, DHCP_MSGTYP_DECLINE, "IP is already in use", scratch_msg);
} else {
dhcp_client_take_lease(client, scratch_msg, true);
@@ -819,22 +804,20 @@ static void dhcp_client_setstate(struct dhcp_client *client,
}
break;
default:
- assert(r < 0);
- debugf("error: recvfrom: %d", r);
+ log_debugln("error: recvfrom: ", (error, err));
}
break;
case STATE_BOUND:
LO_CALL(client->sock, set_recv_deadline, client->lease_time_ns_t1);
- switch ((r = dhcp_client_recv(client, scratch_msg))) {
- case 0:
+ LM_PARTIAL_SWITCH ((err = dhcp_client_recv(client, scratch_msg)).num) {
+ case E_NOERROR:
/* discard */
break;
- case -NET_ERECV_TIMEOUT:
+ case E_NET_ERECV_TIMEOUT:
dhcp_client_setstate(client, STATE_RENEWING, DHCP_MSGTYP_REQUEST, NULL, scratch_msg);
break;
default:
- assert(r < 0);
- debugf("error: recvfrom: %d", r);
+ log_debugln("error: recvfrom: ", (error, err));
}
break;
case STATE_RENEWING:
@@ -842,8 +825,8 @@ static void dhcp_client_setstate(struct dhcp_client *client,
client->time_ns_init = LO_CALL(bootclock, get_time_ns);
LO_CALL(client->sock, set_recv_deadline, client->lease_time_ns_t2);
- switch ((r = dhcp_client_recv(client, scratch_msg))) {
- case 0:
+ LM_PARTIAL_SWITCH ((err = dhcp_client_recv(client, scratch_msg)).num) {
+ case E_NOERROR:
switch (scratch_msg->option_dat[scratch_msg->options[DHCP_OPT_DHCP_MSG_TYPE].off]) {
case DHCP_MSGTYP_NAK:
LO_CALL(client->iface, ifdown);
@@ -857,19 +840,18 @@ static void dhcp_client_setstate(struct dhcp_client *client,
/* ignore */
}
break;
- case -NET_ERECV_TIMEOUT:
+ case E_NET_ERECV_TIMEOUT:
client->lease_server_id = net_ip4_addr_zero;
dhcp_client_setstate(client, STATE_REBINDING, DHCP_MSGTYP_REQUEST, NULL, scratch_msg);
break;
default:
- assert(r < 0);
- debugf("error: recvfrom: %d", r);
+ log_debugln("error: recvfrom: ", (error, err));
}
break;
case STATE_REBINDING:
LO_CALL(client->sock, set_recv_deadline, client->lease_time_ns_end);
- switch ((r = dhcp_client_recv(client, scratch_msg))) {
- case 0:
+ LM_PARTIAL_SWITCH ((err = dhcp_client_recv(client, scratch_msg)).num) {
+ case E_NOERROR:
switch (scratch_msg->option_dat[scratch_msg->options[DHCP_OPT_DHCP_MSG_TYPE].off]) {
case DHCP_MSGTYP_NAK:
LO_CALL(client->iface, ifdown);
@@ -883,13 +865,12 @@ static void dhcp_client_setstate(struct dhcp_client *client,
/* ignore */
}
break;
- case -NET_ERECV_TIMEOUT:
+ case E_NET_ERECV_TIMEOUT:
LO_CALL(client->iface, ifdown);
dhcp_client_setstate(client, STATE_BOUND, 0, NULL, scratch_msg);
break;
default:
- assert(r < 0);
- debugf("error: recvfrom: %d", r);
+ log_debugln("error: recvfrom: ", (error, err));
}
break;
case STATE_INIT_REBOOT:
diff --git a/libdhcp/dhcp_common.c b/libdhcp/dhcp_common.c
new file mode 100644
index 0000000..d691836
--- /dev/null
+++ b/libdhcp/dhcp_common.c
@@ -0,0 +1,104 @@
+/* libdhcp/dhcp_common.c - Base definitions for the DHCP protocol
+ *
+ * Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#include "dhcp_common.h"
+
+/**
+ * DHCP Options
+ * https://www.iana.org/assignments/bootp-dhcp-parameters/bootp-dhcp-parameters.xhtml#options
+ */
+bool dhcp_opt_length_is_valid(uint8_t opt, uint16_t len) {
+ switch (opt) {
+ /* RFC 2132 */
+ case DHCP_OPT_PAD: return len == 0;
+ case DHCP_OPT_SUBNET_MASK: return len == 4;
+ case DHCP_OPT_TIME_OFFSET: return len == 4;
+ case DHCP_OPT_ROUTER: return len >= 4 && len % 4 == 0;
+ case DHCP_OPT_TIME_SERVER: return len >= 4 && len % 4 == 0;
+ case DHCP_OPT_NAME_SERVER: return len >= 4 && len % 4 == 0;
+ case DHCP_OPT_DOMAIN_SERVER: return len >= 4 && len % 4 == 0;
+ case DHCP_OPT_LOG_SERVER: return len >= 4 && len % 4 == 0;
+ case DHCP_OPT_QUOTES_SERVER: return len >= 4 && len % 4 == 0;
+ case DHCP_OPT_LPR_SERVER: return len >= 4 && len % 4 == 0;
+ case DHCP_OPT_IMPRESS_SERVER: return len >= 4 && len % 4 == 0;
+ case DHCP_OPT_RLP_SERVER: return len >= 4 && len % 4 == 0;
+ case DHCP_OPT_HOSTNAME: return len >= 1;
+ case DHCP_OPT_BOOT_FILE_SIZE: return len == 2;
+ case DHCP_OPT_MERIT_DUMP_FILE: return len >= 1;
+ case DHCP_OPT_DOMAIN_NAME: return len >= 1;
+ case DHCP_OPT_SWAP_SERVER: return len == 4; /* IANA says length is "N", but RFC 2132 says "length is 4"; likely releated to errata ID 487 */
+ case DHCP_OPT_ROOT_PATH: return len >= 1;
+ case DHCP_OPT_EXTENSION_FILE: return len >= 1;
+ case DHCP_OPT_FORWARD_ONOFF: return len == 1;
+ case DHCP_OPT_SRCRTE_ONOFF: return len == 1;
+ case DHCP_OPT_POLICY_FILTER: return len >= 8 && len % 8 == 0;
+ case DHCP_OPT_MAX_DG_ASSEMBLY: return len == 2;
+ case DHCP_OPT_DEFAULT_IP_TTL: return len == 1;
+ case DHCP_OPT_MTU_TIMEOUT: return len == 4;
+ case DHCP_OPT_MTU_PLATEAU: return len >= 2 && len % 2 == 0;
+ case DHCP_OPT_MTU_INTERFACE: return len == 2;
+ case DHCP_OPT_MTU_SUBNET: return len == 1;
+ case DHCP_OPT_BROADCAST_ADDRESS: return len == 4;
+ case DHCP_OPT_MASK_DISCOVERY: return len == 1;
+ case DHCP_OPT_MASK_SUPPLIER: return len == 1;
+ case DHCP_OPT_ROUTER_DISCOVERY: return len == 1;
+ case DHCP_OPT_ROUTER_REQUEST: return len == 4;
+ case DHCP_OPT_STATIC_ROUTE: return len >= 8 && len % 8 == 0;
+ case DHCP_OPT_TRAILERS: return len == 1;
+ case DHCP_OPT_ARP_TIMEOUT: return len == 4;
+ case DHCP_OPT_ETHERNET: return len == 1;
+ case DHCP_OPT_DEFAULT_TCP_TTL: return len == 1;
+ case DHCP_OPT_KEEPALIVE_TIME: return len == 4;
+ case DHCP_OPT_KEEPALIVE_DATA: return len == 1;
+ case DHCP_OPT_NIS_DOMAIN: return len >= 1;
+ case DHCP_OPT_NIS_SERVERS: return len >= 4 && len % 4 == 0;
+ case DHCP_OPT_NTP_SERVERS: return len >= 4 && len % 4 == 0;
+ case DHCP_OPT_VENDOR_SPECIFIC: return len >= 1;
+ case DHCP_OPT_NETBIOS_NAME_SRV: return len >= 4 && len % 4 == 0;
+ case DHCP_OPT_NETBIOS_DIST_SRV: return len >= 4 && len % 4 == 0;
+ case DHCP_OPT_NETBIOS_NODE_TYPE: return len == 1;
+ case DHCP_OPT_NETBIOS_SCOPE: return len >= 1;
+ case DHCP_OPT_X_WINDOW_FONT: return len >= 4 && len % 4 == 0;
+ case DHCP_OPT_X_WINDOW_MANAGER: return len >= 4 && len % 4 == 0;
+ case DHCP_OPT_ADDRESS_REQUEST: return len == 4;
+ case DHCP_OPT_ADDRESS_TIME: return len == 4;
+ case DHCP_OPT_OVERLOAD: return len == 1;
+ case DHCP_OPT_DHCP_MSG_TYPE: return len == 1;
+ case DHCP_OPT_DHCP_SERVER_ID: return len == 4;
+ case DHCP_OPT_PARAMETER_LIST: return len >= 1;
+ case DHCP_OPT_DHCP_MESSAGE: return len >= 1;
+ case DHCP_OPT_DHCP_MAX_MSG_SIZE: return len == 2;
+ case DHCP_OPT_RENEWAL_TIME: return len == 4;
+ case DHCP_OPT_REBINDING_TIME: return len == 4;
+ case DHCP_OPT_CLASS_ID: return len >= 1;
+ case DHCP_OPT_CLIENT_ID: return len >= 2;
+
+ /* RFC 2132 */
+ case DHCP_OPT_END: return len == 0;
+
+ /* Unrecognized */
+ default:
+ return true;
+ }
+}
+
+/**
+ * DHCP Message Type 53 Values
+ * https://www.iana.org/assignments/bootp-dhcp-parameters/bootp-dhcp-parameters.xhtml#message-type-53
+ */
+const char *dhcp_msgtyp_str(uint8_t typ) {
+ switch (typ) {
+ case DHCP_MSGTYP_DISCOVER: return "DHCP_MSGTYP_DISCOVER";
+ case DHCP_MSGTYP_OFFER: return "DHCP_MSGTYP_OFFER";
+ case DHCP_MSGTYP_REQUEST: return "DHCP_MSGTYP_REQUEST";
+ case DHCP_MSGTYP_DECLINE: return "DHCP_MSGTYP_DECLINE";
+ case DHCP_MSGTYP_ACK: return "DHCP_MSGTYP_ACK";
+ case DHCP_MSGTYP_NAK: return "DHCP_MSGTYP_NAK";
+ case DHCP_MSGTYP_RELEASE: return "DHCP_MSGTYP_RELEASE";
+ case DHCP_MSGTYP_INFORM: return "DHCP_MSGTYP_INFORM";
+ default: return const_byte_str(typ);
+ }
+}
diff --git a/libdhcp/dhcp_common.h b/libdhcp/dhcp_common.h
index 5b51ce2..a0cdd3c 100644
--- a/libdhcp/dhcp_common.h
+++ b/libdhcp/dhcp_common.h
@@ -1,6 +1,6 @@
/* libdhcp/dhcp_common.h - Base definitions for the DHCP protocol
*
- * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
*
* -----------------------------------------------------------------------------
@@ -67,8 +67,9 @@
#ifndef _LIBDHCP_DHCP_COMMON_H_
#define _LIBDHCP_DHCP_COMMON_H_
-#include <libmisc/endian.h>
-#include <libmisc/log.h> /* for const_byte_str() */
+#include <libhw/generic/net.h> /* for struct net_ip4_addr */
+#include <libmisc/endian.h> /* for uint{n}be_t */
+#include <libmisc/log.h> /* for const_byte_str() */
/* Config *********************************************************************/
@@ -215,80 +216,7 @@ static const uint8_t dhcp_magic_cookie[] = {99, 130, 83, 99};
/* ... */
#define DHCP_OPT_END ((uint8_t)255) /* RFC2132: length: 0; meaning: None */
-static inline bool dhcp_opt_length_is_valid(uint8_t opt, uint16_t len) {
- switch (opt) {
- /* RFC 2132 */
- case DHCP_OPT_PAD: return len == 0;
- case DHCP_OPT_SUBNET_MASK: return len == 4;
- case DHCP_OPT_TIME_OFFSET: return len == 4;
- case DHCP_OPT_ROUTER: return len >= 4 && len % 4 == 0;
- case DHCP_OPT_TIME_SERVER: return len >= 4 && len % 4 == 0;
- case DHCP_OPT_NAME_SERVER: return len >= 4 && len % 4 == 0;
- case DHCP_OPT_DOMAIN_SERVER: return len >= 4 && len % 4 == 0;
- case DHCP_OPT_LOG_SERVER: return len >= 4 && len % 4 == 0;
- case DHCP_OPT_QUOTES_SERVER: return len >= 4 && len % 4 == 0;
- case DHCP_OPT_LPR_SERVER: return len >= 4 && len % 4 == 0;
- case DHCP_OPT_IMPRESS_SERVER: return len >= 4 && len % 4 == 0;
- case DHCP_OPT_RLP_SERVER: return len >= 4 && len % 4 == 0;
- case DHCP_OPT_HOSTNAME: return len >= 1;
- case DHCP_OPT_BOOT_FILE_SIZE: return len == 2;
- case DHCP_OPT_MERIT_DUMP_FILE: return len >= 1;
- case DHCP_OPT_DOMAIN_NAME: return len >= 1;
- case DHCP_OPT_SWAP_SERVER: return len == 4; /* IANA says length is "N", but RFC 2132 says "length is 4"; likely releated to errata ID 487 */
- case DHCP_OPT_ROOT_PATH: return len >= 1;
- case DHCP_OPT_EXTENSION_FILE: return len >= 1;
- case DHCP_OPT_FORWARD_ONOFF: return len == 1;
- case DHCP_OPT_SRCRTE_ONOFF: return len == 1;
- case DHCP_OPT_POLICY_FILTER: return len >= 8 && len % 8 == 0;
- case DHCP_OPT_MAX_DG_ASSEMBLY: return len == 2;
- case DHCP_OPT_DEFAULT_IP_TTL: return len == 1;
- case DHCP_OPT_MTU_TIMEOUT: return len == 4;
- case DHCP_OPT_MTU_PLATEAU: return len >= 2 && len % 2 == 0;
- case DHCP_OPT_MTU_INTERFACE: return len == 2;
- case DHCP_OPT_MTU_SUBNET: return len == 1;
- case DHCP_OPT_BROADCAST_ADDRESS: return len == 4;
- case DHCP_OPT_MASK_DISCOVERY: return len == 1;
- case DHCP_OPT_MASK_SUPPLIER: return len == 1;
- case DHCP_OPT_ROUTER_DISCOVERY: return len == 1;
- case DHCP_OPT_ROUTER_REQUEST: return len == 4;
- case DHCP_OPT_STATIC_ROUTE: return len >= 8 && len % 8 == 0;
- case DHCP_OPT_TRAILERS: return len == 1;
- case DHCP_OPT_ARP_TIMEOUT: return len == 4;
- case DHCP_OPT_ETHERNET: return len == 1;
- case DHCP_OPT_DEFAULT_TCP_TTL: return len == 1;
- case DHCP_OPT_KEEPALIVE_TIME: return len == 4;
- case DHCP_OPT_KEEPALIVE_DATA: return len == 1;
- case DHCP_OPT_NIS_DOMAIN: return len >= 1;
- case DHCP_OPT_NIS_SERVERS: return len >= 4 && len % 4 == 0;
- case DHCP_OPT_NTP_SERVERS: return len >= 4 && len % 4 == 0;
- case DHCP_OPT_VENDOR_SPECIFIC: return len >= 1;
- case DHCP_OPT_NETBIOS_NAME_SRV: return len >= 4 && len % 4 == 0;
- case DHCP_OPT_NETBIOS_DIST_SRV: return len >= 4 && len % 4 == 0;
- case DHCP_OPT_NETBIOS_NODE_TYPE: return len == 1;
- case DHCP_OPT_NETBIOS_SCOPE: return len >= 1;
- case DHCP_OPT_X_WINDOW_FONT: return len >= 4 && len % 4 == 0;
- case DHCP_OPT_X_WINDOW_MANAGER: return len >= 4 && len % 4 == 0;
- case DHCP_OPT_ADDRESS_REQUEST: return len == 4;
- case DHCP_OPT_ADDRESS_TIME: return len == 4;
- case DHCP_OPT_OVERLOAD: return len == 1;
- case DHCP_OPT_DHCP_MSG_TYPE: return len == 1;
- case DHCP_OPT_DHCP_SERVER_ID: return len == 4;
- case DHCP_OPT_PARAMETER_LIST: return len >= 1;
- case DHCP_OPT_DHCP_MESSAGE: return len >= 1;
- case DHCP_OPT_DHCP_MAX_MSG_SIZE: return len == 2;
- case DHCP_OPT_RENEWAL_TIME: return len == 4;
- case DHCP_OPT_REBINDING_TIME: return len == 4;
- case DHCP_OPT_CLASS_ID: return len >= 1;
- case DHCP_OPT_CLIENT_ID: return len >= 2;
-
- /* RFC 2132 */
- case DHCP_OPT_END: return len == 0;
-
- /* Unrecognized */
- default:
- return true;
- }
-};
+bool dhcp_opt_length_is_valid(uint8_t opt, uint16_t len);
/**
* DHCP Message Type 53 Values
@@ -303,18 +231,6 @@ static inline bool dhcp_opt_length_is_valid(uint8_t opt, uint16_t len) {
#define DHCP_MSGTYP_RELEASE ((uint8_t) 7) /* RFC2132, client->server */
#define DHCP_MSGTYP_INFORM ((uint8_t) 8) /* RFC2132, client->server */
-static const char *dhcp_msgtyp_str(uint8_t typ) {
- switch (typ) {
- case DHCP_MSGTYP_DISCOVER: return "DHCP_MSGTYP_DISCOVER";
- case DHCP_MSGTYP_OFFER: return "DHCP_MSGTYP_OFFER";
- case DHCP_MSGTYP_REQUEST: return "DHCP_MSGTYP_REQUEST";
- case DHCP_MSGTYP_DECLINE: return "DHCP_MSGTYP_DECLINE";
- case DHCP_MSGTYP_ACK: return "DHCP_MSGTYP_ACK";
- case DHCP_MSGTYP_NAK: return "DHCP_MSGTYP_NAK";
- case DHCP_MSGTYP_RELEASE: return "DHCP_MSGTYP_RELEASE";
- case DHCP_MSGTYP_INFORM: return "DHCP_MSGTYP_INFORM";
- default: return const_byte_str(typ);
- }
-}
+const char *dhcp_msgtyp_str(uint8_t typ);
#endif /* _LIBDHCP_DHCP_COMMON_H_ */
diff --git a/libdhcp/include/libdhcp/client.h b/libdhcp/include/libdhcp/client.h
index f81e9b1..d1711b2 100644
--- a/libdhcp/include/libdhcp/client.h
+++ b/libdhcp/include/libdhcp/client.h
@@ -2,66 +2,6 @@
*
* Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
- *
- * -----------------------------------------------------------------------------
- * https://github.com/Wiznet/ioLibrary_Driver/blob/b981401e7f3d07015619adf44c13998e13e777f9/Internet/DHCP/dhcp.h
- *
- * Copyright (c) 2013, WIZnet Co., LTD.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * * Neither the name of the <ORGANIZATION> nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
- * THE POSSIBILITY OF SUCH DAMAGE.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- *
- * -----------------------------------------------------------------------------
- * https://github.com/Wiznet/ioLibrary_Driver/blob/b981401e7f3d07015619adf44c13998e13e777f9/license.txt
- *
- * Copyright (c) 2014 WIZnet Co.,Ltd.
- * Copyright (c) WIZnet ioLibrary Project.
- * All rights reserved.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- *
- * SPDX-License-Identifier: MIT
*/
#ifndef _LIBDHCP_CLIENT_H_
diff --git a/libdhcp/tests/config.h b/libdhcp/tests/config.h
new file mode 100644
index 0000000..299d020
--- /dev/null
+++ b/libdhcp/tests/config.h
@@ -0,0 +1,25 @@
+/* libdhcp/tests/config.h - Compile-time configuration for the libdhcp tests
+ *
+ * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#ifndef _CONFIG_H_
+#define _CONFIG_H_
+
+#define CONFIG_COROUTINE_STACK_SIZE_DEFAULT (16*1024)
+#define CONFIG_COROUTINE_NAME_LEN 16
+#define CONFIG_COROUTINE_NUM 16
+
+#define CONFIG_COROUTINE_MEASURE_STACK 1
+#define CONFIG_COROUTINE_PROTECT_STACK 1
+#define CONFIG_COROUTINE_DEBUG 1
+#define CONFIG_COROUTINE_VALGRIND 1
+#define CONFIG_COROUTINE_GDB 1
+
+#define CONFIG_DHCP_CAN_RECV_UNICAST_IP_WITHOUT_IP 0 /* bool */
+#define CONFIG_DHCP_DEBUG 1 /* bool */
+#define CONFIG_DHCP_OPT_SIZE 312 /* minimum of 312 */
+#define CONFIG_DHCP_SELECTING_NS (5*NS_PER_S)
+
+#endif /* _CONFIG_H_ */
diff --git a/libobj/tests/test.h b/libdhcp/tests/test.h
index 2fb1bd5..2fb1bd5 120000
--- a/libobj/tests/test.h
+++ b/libdhcp/tests/test.h
diff --git a/libdhcp/tests/test_client.c b/libdhcp/tests/test_client.c
new file mode 100644
index 0000000..6446cef
--- /dev/null
+++ b/libdhcp/tests/test_client.c
@@ -0,0 +1,120 @@
+/* libdhcp/tests/test_client.c - Tests for libdhcp client
+ *
+ * Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#include <string.h> /* for memcpy() */
+
+#include <libcr/coroutine.h>
+#include <libhw/generic/net.h>
+#include <libhw/host_alarmclock.h>
+
+#include <libdhcp/client.h>
+
+#include "test.h"
+
+/******************************************************************************/
+
+struct test_udp {
+};
+LO_IMPLEMENTATION_STATIC(io_closer, struct test_udp, test_udp);
+LO_IMPLEMENTATION_STATIC(net_packet_conn, struct test_udp, test_udp);
+
+static error test_udp_sendto(struct test_udp *LM_UNUSED(self), void *LM_UNUSED(buf), size_t LM_UNUSED(len), struct net_ip4_addr LM_UNUSED(node), uint16_t LM_UNUSED(port)) {
+ static unsigned cnt = 0;
+ if (cnt++ % 2 == 0)
+ return error_new(E_EUNKNOWN);
+ return ERROR_NULL;
+}
+
+static size_t_or_error test_udp_recvfrom(struct test_udp *LM_UNUSED(self), void *buf, size_t len, struct net_ip4_addr *ret_node, uint16_t *ret_port) {
+ static const uint8_t resp_offer[] = {0x02,0x01,0x06,0x00,0xE8,0x40,0xC6,0x79,0x00,0x01,0x80,0x00,0x00,0x00,0x00,0x00,0xC0,0xA8,0x0A,0x78,0xC0,0xA8,0x0A,0x01,0x00,0x00,0x00,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x63,0x82,0x53,0x63,0x35,0x01,0x02,0x36,0x04,0xC0,0xA8,0x0A,0x01,0x33,0x04,0x00,0x00,0xA8,0xC0,0x3A,0x04,0x00,0x00,0x54,0x60,0x3B,0x04,0x00,0x00,0x93,0xA8,0x01,0x04,0xFF,0xFF,0xFF,0x00,0x1C,0x04,0xC0,0xA8,0x0A,0xFF,0x03,0x04,0xC0,0xA8,0x0A,0x01,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
+ static const uint8_t resp_ack[] = {0x02,0x01,0x06,0x00,0xE8,0x40,0xC6,0x79,0x00,0x01,0x80,0x00,0x00,0x00,0x00,0x00,0xC0,0xA8,0x0A,0x78,0xC0,0xA8,0x0A,0x01,0x00,0x00,0x00,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x63,0x82,0x53,0x63,0x35,0x01,0x05,0x36,0x04,0xC0,0xA8,0x0A,0x01,0x33,0x04,0x00,0x00,0xA8,0xC0,0x3A,0x04,0x00,0x00,0x54,0x60,0x3B,0x04,0x00,0x00,0x93,0xA8,0x01,0x04,0xFF,0xFF,0xFF,0x00,0x1C,0x04,0xC0,0xA8,0x0A,0xFF,0x03,0x04,0xC0,0xA8,0x0A,0x01,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
+
+ static unsigned cnt = 0;
+ const void *resp;
+ size_t resp_len;
+ switch (cnt++) {
+ case 0: return ERROR_NEW_ERR(size_t, error_new(E_EUNKNOWN));
+ case 1: resp = resp_offer; resp_len = sizeof(resp_offer); break;
+ case 2: return ERROR_NEW_ERR(size_t, error_new(E_EUNKNOWN));
+ case 3: resp = resp_ack; resp_len = sizeof(resp_ack); break;
+ default: return ERROR_NEW_ERR(size_t, error_new(E_NET_ERECV_TIMEOUT));
+ }
+ test_assert(len >= resp_len);
+ memcpy(buf, resp, resp_len);
+ *ret_node = ((struct net_ip4_addr){{192,168,10,1}});
+ *ret_port = 67;
+ return ERROR_NEW_VAL(size_t, resp_len);
+}
+
+static void test_udp_set_recv_deadline(struct test_udp *LM_UNUSED(self), uint64_t LM_UNUSED(ns_since_boot)) {
+ /* Do nothing? */
+}
+
+static error test_udp_close(struct test_udp *LM_UNUSED(self)) {
+ assert_notreached("not implemented");
+}
+
+/******************************************************************************/
+
+struct test_iface {
+ struct net_iface_config cfg;
+
+ struct test_udp conn;
+};
+LO_IMPLEMENTATION_STATIC(net_iface, struct test_iface, test);
+
+static struct net_eth_addr test_hwaddr(struct test_iface *LM_UNUSED(self)) {
+ struct net_eth_addr ret = {{1, 2, 3, 4, 5, 6}};
+ return ret;
+}
+
+static void test_ifup(struct test_iface *LM_UNUSED(self), struct net_iface_config LM_UNUSED(cfg)) {
+ cr_exit();
+}
+
+static void test_ifdown(struct test_iface *LM_UNUSED(self)) {
+ assert_notreached("not implemented");
+}
+
+static bool test_arp_ping(struct test_iface *LM_UNUSED(self), struct net_ip4_addr LM_UNUSED(addr)) {
+ return false;
+}
+
+static lo_interface net_stream_listener test_tcp_listen(struct test_iface *LM_UNUSED(self), uint16_t LM_UNUSED(local_port)) {
+ assert_notreached("not implemented");
+}
+
+static net_stream_conn_or_error test_tcp_dial(struct test_iface *LM_UNUSED(self), struct net_ip4_addr LM_UNUSED(remote_node), uint16_t LM_UNUSED(remote_port)) {
+ assert_notreached("not implemented");
+}
+
+static lo_interface net_packet_conn test_udp_conn(struct test_iface *self, uint16_t local_port) {
+ bool once = false;
+ test_assert(local_port == 68);
+ test_assert(!once);
+ once = true;
+ return LO_BOX(net_packet_conn, &self->conn);
+}
+
+/******************************************************************************/
+
+COROUTINE dhcp_cr(void *) {
+ cr_begin();
+ struct test_iface iface = {};
+ dhcp_client_main(LO_BOX(net_iface, &iface), "test-client");
+ cr_end();
+}
+
+int main() {
+ struct hostclock clock_monotonic = {
+ .clock_id = CLOCK_MONOTONIC,
+ };
+ bootclock = LO_BOX(alarmclock, &clock_monotonic);
+
+ coroutine_add("dhcp", dhcp_cr, NULL);
+ coroutine_main();
+ return 0;
+}
diff --git a/libfmt/CMakeLists.txt b/libfmt/CMakeLists.txt
deleted file mode 100644
index 1b3a80f..0000000
--- a/libfmt/CMakeLists.txt
+++ /dev/null
@@ -1,17 +0,0 @@
-# libfmt/CMakeLists.txt - Support for pico-fmt
-#
-# Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com>
-# SPDX-License-Identifier: AGPL-3.0-or-later
-
-add_library(libfmt INTERFACE)
-target_include_directories(libfmt SYSTEM INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include)
-target_sources(libfmt INTERFACE
- libmisc.c
- libobj.c
- quote.c
-)
-target_link_libraries(libfmt INTERFACE
- pico_fmt
- libmisc
- libobj
-)
diff --git a/libfmt/include/libfmt/fmt.h b/libfmt/include/libfmt/fmt.h
deleted file mode 100644
index 4dba82f..0000000
--- a/libfmt/include/libfmt/fmt.h
+++ /dev/null
@@ -1,23 +0,0 @@
-/* libfmt/fmt.h - Support for pico-fmt
- *
- * Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com>
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
-
-#ifndef _LIBFMT_FMT_H_
-#define _LIBFMT_FMT_H_
-
-#include <pico/fmt_printf.h>
-#include <pico/fmt_install.h>
-
-#include <libobj/obj.h>
-
-/**
- * An object that implements fmt_formatter can be printed using
- * `printf("%v", boxed_obj)`.
- */
-#define fmt_formatter_LO_IFACE \
- LO_FUNC(void, format, struct fmt_state *)
-LO_INTERFACE(fmt_formatter)
-
-#endif /* _LIBFMT_FMT_H_ */
diff --git a/libfmt/libmisc.c b/libfmt/libmisc.c
deleted file mode 100644
index 4586c30..0000000
--- a/libfmt/libmisc.c
+++ /dev/null
@@ -1,59 +0,0 @@
-/* libfmt/libmisc.c - Integrate pico-fmt with libmisc
- *
- * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
-
-#include <stdarg.h> /* for va_list, va_start(), va_end() */
-#include <stdio.h> /* for vprintf(), putchar() */
-#if LIB_PICO_STDIO
-#include <pico/stdio.h> /* for stdio_putchar_raw() */
-#endif
-
-#include <libmisc/macro.h> /* for LM_UNUSED() */
-#include <libmisc/_intercept.h> /* for __lm_printf() and __lm_light_printf() */
-
-#include <libfmt/fmt.h> /* for fmt_vfctprintf() */
-
-#if LIB_PICO_STDIO
-static void libfmt_light_fct(char character, void *LM_UNUSED(arg)) {
- stdio_putchar_raw(character);
-}
-#else
-static void libfmt_libc_fct(char character, void *LM_UNUSED(arg)) {
- putchar(character);
-}
-#endif
-
-int __lm_printf(const char *format, ...) {
- va_list va;
- va_start(va, format);
-#if LIB_PICO_STDIO
- /* pico_stdio has already intercepted vprintf for us, and
- * their stdio_buffered_printer() is better than our
- * libfmt_libc_fct() because buffering. */
- int ret = vprintf(format, va);
-#else
- int ret = fmt_vfctprintf(libfmt_libc_fct, NULL, format, va);
-#endif
- va_end(va);
- return ret;
-}
-
-int __lm_light_printf(const char *format, ...) {
- va_list va;
- va_start(va, format);
-#if LIB_PICO_STDIO
- /* libfmt_light_fct() and stdio_buffered_printer() both use 68
- * bytes of stack; but the buffer lives on the stack of
- * stdio.c:__wrap_vprintf(); so that's where you'll see the
- * numbers be different if you're analyzing it. (Also, being
- * able to skip the stdio_stack_buffer_flush() call.) */
- int ret = fmt_vfctprintf(libfmt_light_fct, NULL, format, va);
- stdio_flush();
-#else
- int ret = fmt_vfctprintf(libfmt_libc_fct, NULL, format, va);
-#endif
- va_end(va);
- return ret;
-}
diff --git a/libfmt/libobj.c b/libfmt/libobj.c
deleted file mode 100644
index e4b833b..0000000
--- a/libfmt/libobj.c
+++ /dev/null
@@ -1,17 +0,0 @@
-/* libfmt/libobj.c - Integrate pico-fmt with libobj
- *
- * Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com>
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
-
-#include <libfmt/fmt.h>
-
-static void libfmt_conv_formatter(struct fmt_state *state) {
- lo_interface fmt_formatter obj = va_arg(*state->args, lo_interface fmt_formatter);
- LO_CALL(obj, format, state);
-}
-
-[[gnu::constructor]]
-static void libfmt_install_formatter(void) {
- fmt_install('v', libfmt_conv_formatter);
-}
diff --git a/libfmt/quote.c b/libfmt/quote.c
deleted file mode 100644
index c91e0b0..0000000
--- a/libfmt/quote.c
+++ /dev/null
@@ -1,159 +0,0 @@
-/* libfmt/quote.c - C-string quoting for pico-fmt
- *
- * Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com>
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
-
-#include <string.h> /* for strnlen() */
-#include <stdint.h> /* for uint{n}_t() */
-
-#include <libfmt/fmt.h>
-
-enum quote {
- QUOTE_NONE, /* c */
- QUOTE_SIMPLE, /* \c */
- QUOTE_U4, /* \uABCD */
- QUOTE_U8, /* \UABCDABCD */
-};
-
-static inline enum quote needs_quote(uint32_t ch) {
- if (ch == '\a' ||
- ch == '\b' ||
- ch == '\f' ||
- ch == '\n' ||
- ch == '\r' ||
- ch == '\t' ||
- ch == '\v' ||
- ch == '\\' ||
- ch == '\'' ||
- ch == '"' ||
- ch == '?')
- return QUOTE_SIMPLE;
- else if (' ' <= ch && ch <= '~')
- return QUOTE_NONE;
- else if (ch < 0x10000)
- return QUOTE_U4;
- else
- return QUOTE_U8;
-}
-
-/**
- * Quote a string to ASCII-only C syntax. Valid UTF-8 is quoted as
- * short C-escape characters, \uABCD or \UABCDABCD; invalid UTF-8 is
- * quoted as \xAB.
- */
-static void libfmt_conv_quote(struct fmt_state *state) {
- uint32_t ch;
- uint8_t chlen;
-
- const char *in = va_arg(*state->args, char*);
- size_t in_len = strnlen(in, (state->flags & FMT_FLAG_PRECISION) ? state->precision : (size_t)-1);
-
- size_t out_len = 2;
- for (size_t pos = 0; pos < in_len;) {
- if ((in[pos] & 0b10000000) == 0b00000000) { ch = in[pos] & 0b01111111; chlen = 1; }
- else if ((in[pos] & 0b11100000) == 0b11000000) { ch = in[pos] & 0b00011111; chlen = 2; }
- else if ((in[pos] & 0b11110000) == 0b11100000) { ch = in[pos] & 0b00001111; chlen = 3; }
- else if ((in[pos] & 0b11111000) == 0b11110000) { ch = in[pos] & 0b00000111; chlen = 4; }
- else goto measure_invalid_utf8;
- if ((ch == 0 && chlen != 1) || pos + chlen > in_len) goto measure_invalid_utf8;
- for (uint8_t i = 1; i < chlen; i++) {
- if ((in[pos+i] & 0b11000000) != 0b10000000) goto measure_invalid_utf8;
- ch = (ch << 6) | (in[pos+i] & 0b00111111);
- }
- if (ch > 0x10FFFF) goto measure_invalid_utf8;
- pos += chlen;
-
- switch (needs_quote(ch)) {
- case QUOTE_NONE : out_len += 1; break;
- case QUOTE_SIMPLE : out_len += 2; break;
- case QUOTE_U4 : out_len += 6; break;
- case QUOTE_U8 : out_len += 10; break;
- }
- continue;
- measure_invalid_utf8:
- pos++;
- out_len += 4; /* \xAB */
- }
-
- if (!(state->flags & FMT_FLAG_LEFT)) {
- for (size_t i = 0; i + out_len < state->width; i++) {
- fmt_state_putchar(state, ' ');
- }
- }
-
- fmt_state_putchar(state, '"');
- for (size_t pos = 0; pos < in_len;) {
- if ((in[pos] & 0b10000000) == 0b00000000) { ch = in[pos] & 0b01111111; chlen = 1; }
- else if ((in[pos] & 0b11100000) == 0b11000000) { ch = in[pos] & 0b00011111; chlen = 2; }
- else if ((in[pos] & 0b11110000) == 0b11100000) { ch = in[pos] & 0b00001111; chlen = 3; }
- else if ((in[pos] & 0b11111000) == 0b11110000) { ch = in[pos] & 0b00000111; chlen = 4; }
- else goto output_invalid_utf8;
- if ((ch == 0 && chlen != 1) || pos + chlen > in_len) goto output_invalid_utf8;
- for (uint8_t i = 1; i < chlen; i++) {
- if ((in[pos+i] & 0b11000000) != 0b10000000) goto output_invalid_utf8;
- ch = (ch << 6) | (in[pos+i] & 0b00111111);
- }
- if (ch > 0x10FFFF) goto output_invalid_utf8;
- pos += chlen;
-
- switch (needs_quote(ch)) {
- case QUOTE_NONE:
- fmt_state_putchar(state, ch);
- break;
- case QUOTE_SIMPLE:
- fmt_state_putchar(state, '\\');
- switch (ch) {
- case '\a': fmt_state_putchar(state, 'a'); break;
- case '\b': fmt_state_putchar(state, 'b'); break;
- case '\f': fmt_state_putchar(state, 'f'); break;
- case '\n': fmt_state_putchar(state, 'n'); break;
- case '\r': fmt_state_putchar(state, 'r'); break;
- case '\t': fmt_state_putchar(state, 't'); break;
- case '\v': fmt_state_putchar(state, 'v'); break;
- case '\\': fmt_state_putchar(state, '\\'); break;
- case '\'': fmt_state_putchar(state, '\''); break;
- case '"': fmt_state_putchar(state, '"'); break;
- case '?': fmt_state_putchar(state, '?'); break;
- }
- break;
- case QUOTE_U4:
- fmt_state_putchar(state, '\\');
- fmt_state_putchar(state, 'u');
- fmt_state_putchar(state, (ch >> 12) & 0xF);
- fmt_state_putchar(state, (ch >> 8) & 0xF);
- fmt_state_putchar(state, (ch >> 4) & 0xF);
- fmt_state_putchar(state, (ch >> 0) & 0xF);
- break;
- case QUOTE_U8:
- fmt_state_putchar(state, '\\');
- fmt_state_putchar(state, 'U');
- fmt_state_putchar(state, (ch >> 28) & 0xF);
- fmt_state_putchar(state, (ch >> 24) & 0xF);
- fmt_state_putchar(state, (ch >> 20) & 0xF);
- fmt_state_putchar(state, (ch >> 16) & 0xF);
- fmt_state_putchar(state, (ch >> 12) & 0xF);
- fmt_state_putchar(state, (ch >> 8) & 0xF);
- fmt_state_putchar(state, (ch >> 4) & 0xF);
- fmt_state_putchar(state, (ch >> 0) & 0xF);
- break;
- }
- continue;
- output_invalid_utf8:
- fmt_state_putchar(state, '\\');
- fmt_state_putchar(state, 'x');
- fmt_state_putchar(state, (in[pos] >> 4) & 0xF);
- fmt_state_putchar(state, (in[pos] >> 0) & 0xF);
- pos++;
- }
- fmt_state_putchar(state, '"');
-
- for (size_t i = 0; i + out_len < state->width; i++) {
- fmt_state_putchar(state, ' ');
- }
-}
-
-[[gnu::constructor]]
-static void libfmt_install_quote(void) {
- fmt_install('q', libfmt_conv_quote);
-}
diff --git a/libhw_cr/CMakeLists.txt b/libhw_cr/CMakeLists.txt
index caeac21..9dd6a27 100644
--- a/libhw_cr/CMakeLists.txt
+++ b/libhw_cr/CMakeLists.txt
@@ -14,7 +14,7 @@ target_sources(libhw_cr INTERFACE
)
if (PICO_PLATFORM STREQUAL "rp2040")
- target_include_directories(libhw_cr SYSTEM INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/rp2040_include)
+ target_include_directories(libhw_cr PUBLIC INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/rp2040_include)
target_link_libraries(libhw_cr INTERFACE
libcr_ipc
)
@@ -24,6 +24,7 @@ if (PICO_PLATFORM STREQUAL "rp2040")
rp2040_hwspi.c
rp2040_hwtimer.c
w5500.c
+ w5500_ll.c
)
target_link_libraries(libhw_cr INTERFACE
hardware_gpio
@@ -34,7 +35,7 @@ if (PICO_PLATFORM STREQUAL "rp2040")
endif()
if (PICO_PLATFORM STREQUAL "host")
- target_include_directories(libhw_cr SYSTEM INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/host_include)
+ target_include_directories(libhw_cr PUBLIC INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/host_include)
target_sources(libhw_cr INTERFACE
host_util.c
host_alarmclock.c
diff --git a/libhw_cr/host_alarmclock.c b/libhw_cr/host_alarmclock.c
index 2f255e0..0c42677 100644
--- a/libhw_cr/host_alarmclock.c
+++ b/libhw_cr/host_alarmclock.c
@@ -5,9 +5,12 @@
*/
#include <errno.h>
-#include <error.h>
#include <signal.h>
+#define error __error
+#include <error.h>
+#undef error
+
#include <libcr/coroutine.h>
#include <libmisc/assert.h>
@@ -19,15 +22,15 @@
#include "host_util.h" /* for host_sigrt_alloc(), ns_to_host_ns_time() */
-LO_IMPLEMENTATION_C(alarmclock, struct hostclock, hostclock, static)
+LO_IMPLEMENTATION_C(alarmclock, struct hostclock, hostclock);
-static uint64_t hostclock_get_time_ns(struct hostclock *alarmclock) {
+uint64_t hostclock_get_time_ns(struct hostclock *alarmclock) {
assert(alarmclock);
struct timespec ts;
if (clock_gettime(alarmclock->clock_id, &ts) != 0)
- error(1, errno, "clock_gettime(%d)", (int)alarmclock->clock_id);
+ __error(1, errno, "clock_gettime(%d)", (int)alarmclock->clock_id);
return ns_from_host_ns_time(ts);
}
@@ -49,18 +52,18 @@ static void hostclock_handle_sig_alarm(int LM_UNUSED(sig), siginfo_t *info, void
if (alarmclock->queue) {
struct itimerspec alarmspec = {
.it_value = ns_to_host_ns_time(alarmclock->queue->fire_at_ns),
- .it_interval = {0},
+ .it_interval = {},
};
if (timer_settime(alarmclock->timer_id, TIMER_ABSTIME, &alarmspec, NULL) != 0)
- error(1, errno, "timer_settime");
+ __error(1, errno, "timer_settime");
}
}
-static bool hostclock_add_trigger(struct hostclock *alarmclock,
- struct alarmclock_trigger *trigger,
- uint64_t fire_at_ns,
- void (*cb)(void *),
- void *cb_arg) {
+void hostclock_add_trigger(struct hostclock *alarmclock,
+ struct alarmclock_trigger *trigger,
+ uint64_t fire_at_ns,
+ void (*cb)(void *),
+ void *cb_arg) {
assert(alarmclock);
assert(trigger);
assert(fire_at_ns);
@@ -72,6 +75,7 @@ static bool hostclock_add_trigger(struct hostclock *alarmclock,
trigger->cb_arg = cb_arg;
bool saved = cr_save_and_disable_interrupts();
+
struct alarmclock_trigger **dst = &alarmclock->queue;
while (*dst && fire_at_ns >= (*dst)->fire_at_ns)
dst = &(*dst)->next;
@@ -80,6 +84,7 @@ static bool hostclock_add_trigger(struct hostclock *alarmclock,
if (*dst)
(*dst)->prev = trigger;
*dst = trigger;
+
if (!alarmclock->initialized) {
struct sigevent how_to_notify = {
.sigev_notify = SIGEV_SIGNAL,
@@ -93,26 +98,26 @@ static bool hostclock_add_trigger(struct hostclock *alarmclock,
.sa_sigaction = hostclock_handle_sig_alarm,
};
if (sigaction(how_to_notify.sigev_signo, &action, NULL) != 0)
- error(1, errno, "sigaction");
+ __error(1, errno, "sigaction");
if (timer_create(alarmclock->clock_id, &how_to_notify, &alarmclock->timer_id) != 0)
- error(1, errno, "timer_create(%d)", (int)alarmclock->clock_id);
+ __error(1, errno, "timer_create(%d)", (int)alarmclock->clock_id);
alarmclock->initialized = true;
}
+
if (alarmclock->queue == trigger) {
struct itimerspec alarmspec = {
.it_value = ns_to_host_ns_time(trigger->fire_at_ns),
- .it_interval = {0},
+ .it_interval = {},
};
if (timer_settime(alarmclock->timer_id, TIMER_ABSTIME, &alarmspec, NULL) != 0)
- error(1, errno, "timer_settime");
+ __error(1, errno, "timer_settime");
}
- cr_restore_interrupts(saved);
- return false;
+ cr_restore_interrupts(saved);
}
-static void hostclock_del_trigger(struct hostclock *alarmclock,
- struct alarmclock_trigger *trigger) {
+ void hostclock_del_trigger(struct hostclock *alarmclock,
+ struct alarmclock_trigger *trigger) {
assert(alarmclock);
assert(trigger);
diff --git a/libhw_cr/host_include/libhw/host_alarmclock.h b/libhw_cr/host_include/libhw/host_alarmclock.h
index 89df68a..2ddb054 100644
--- a/libhw_cr/host_include/libhw/host_alarmclock.h
+++ b/libhw_cr/host_include/libhw/host_alarmclock.h
@@ -7,21 +7,20 @@
#ifndef _LIBHW_HOST_ALARMCLOCK_H_
#define _LIBHW_HOST_ALARMCLOCK_H_
-#include <stdbool.h> /* for bool */
-#include <time.h> /* for clockid_t, timer_t */
+#include <time.h> /* for clockid_t, timer_t */
-#include <libmisc/private.h>
#include <libhw/generic/alarmclock.h>
+#include <libmisc/private.h>
struct hostclock {
clockid_t clock_id;
- BEGIN_PRIVATE(LIBHW_HOST_ALARMCLOCK_H)
+ BEGIN_PRIVATE(LIBHW_HOST_ALARMCLOCK_H);
bool initialized;
timer_t timer_id;
struct alarmclock_trigger *queue;
- END_PRIVATE(LIBHW_HOST_ALARMCLOCK_H)
+ END_PRIVATE(LIBHW_HOST_ALARMCLOCK_H);
};
-LO_IMPLEMENTATION_H(alarmclock, struct hostclock, hostclock)
+LO_IMPLEMENTATION_H(alarmclock, struct hostclock, hostclock);
#endif /* _LIBHW_HOST_ALARMCLOCK_H_ */
diff --git a/libhw_cr/host_include/libhw/host_net.h b/libhw_cr/host_include/libhw/host_net.h
index fced229..6ff2779 100644
--- a/libhw_cr/host_include/libhw/host_net.h
+++ b/libhw_cr/host_include/libhw/host_net.h
@@ -13,31 +13,38 @@
#include <libhw/generic/net.h>
+/* TCP connection *************************************************************/
+
struct _hostnet_tcp_conn {
- BEGIN_PRIVATE(LIBHW_HOST_NET_H)
+ BEGIN_PRIVATE(LIBHW_HOST_NET_H);
int fd;
uint64_t read_deadline_ns;
- END_PRIVATE(LIBHW_HOST_NET_H)
+ END_PRIVATE(LIBHW_HOST_NET_H);
};
-LO_IMPLEMENTATION_H(net_stream_conn, struct _hostnet_tcp_conn, hostnet_tcp)
+
+/* TCP listener ***************************************************************/
struct hostnet_tcp_listener {
- BEGIN_PRIVATE(LIBHW_HOST_NET_H)
+ BEGIN_PRIVATE(LIBHW_HOST_NET_H);
int fd;
struct _hostnet_tcp_conn active_conn;
- END_PRIVATE(LIBHW_HOST_NET_H)
+ END_PRIVATE(LIBHW_HOST_NET_H);
};
-LO_IMPLEMENTATION_H(net_stream_listener, struct hostnet_tcp_listener, hostnet_tcplist)
+LO_IMPLEMENTATION_H(io_closer, struct hostnet_tcp_listener, hostnet_tcplist);
+LO_IMPLEMENTATION_H(net_stream_listener, struct hostnet_tcp_listener, hostnet_tcplist);
void hostnet_tcp_listener_init(struct hostnet_tcp_listener *self, uint16_t port);
+/* UDP connection *************************************************************/
+
struct hostnet_udp_conn {
- BEGIN_PRIVATE(LIBHW_HOST_NET_H)
+ BEGIN_PRIVATE(LIBHW_HOST_NET_H);
int fd;
uint64_t read_deadline_ns;
- END_PRIVATE(LIBHW_HOST_NET_H)
+ END_PRIVATE(LIBHW_HOST_NET_H);
};
-LO_IMPLEMENTATION_H(net_packet_conn, struct hostnet_udp_conn, hostnet_udp)
+LO_IMPLEMENTATION_H(io_closer, struct hostnet_udp_conn, hostnet_udp);
+LO_IMPLEMENTATION_H(net_packet_conn, struct hostnet_udp_conn, hostnet_udp);
void hostnet_udp_conn_init(struct hostnet_udp_conn *self, uint16_t port);
diff --git a/libhw_cr/host_net.c b/libhw_cr/host_net.c
index f1c988c..e68ccf8 100644
--- a/libhw_cr/host_net.c
+++ b/libhw_cr/host_net.c
@@ -7,11 +7,13 @@
#define _GNU_SOURCE /* for pthread_sigqueue(3gnu) */
/* misc */
#include <errno.h> /* for errno, EAGAIN, EINVAL */
+#define error __error
#include <error.h> /* for error(3gnu) */
+#undef error
#include <stdlib.h> /* for abs(), shutdown(), SHUT_RD, SHUT_WR, SHUT_RDWR */
-#include <unistd.h> /* for read(), write() */
+#include <sys/uio.h> /* for readv(), writev(), struct iovec */
/* net */
-#include <arpa/inet.h> /* for htons(3p) */
+#include <arpa/inet.h> /* for htons() */
#include <netinet/in.h> /* for struct sockaddr_in */
#include <sys/socket.h> /* for struct sockaddr{,_storage}, SOCK_*, SOL_*, SO_*, socket(), setsockopt(), bind(), listen(), accept() */
/* async */
@@ -19,9 +21,10 @@
#include <signal.h> /* for siginfo_t, struct sigaction, enum sigval, sigaction(), SA_SIGINFO */
#include <libcr/coroutine.h>
+#include <libmisc/alloc.h>
#include <libmisc/assert.h>
#include <libmisc/macro.h>
-#include <libobj/obj.h>
+#include <libmisc/obj.h>
#include <libhw/generic/alarmclock.h>
@@ -30,18 +33,18 @@
#include "host_util.h" /* for host_sigrt_alloc(), ns_to_host_us_time() */
-LO_IMPLEMENTATION_C(io_closer, struct hostnet_tcp_listener, hostnet_tcplist, static)
-LO_IMPLEMENTATION_C(net_stream_listener, struct hostnet_tcp_listener, hostnet_tcplist, static)
+LO_IMPLEMENTATION_C(io_closer, struct hostnet_tcp_listener, hostnet_tcplist);
+LO_IMPLEMENTATION_C(net_stream_listener, struct hostnet_tcp_listener, hostnet_tcplist);
-LO_IMPLEMENTATION_C(io_reader, struct _hostnet_tcp_conn, hostnet_tcp, static)
-LO_IMPLEMENTATION_C(io_writer, struct _hostnet_tcp_conn, hostnet_tcp, static)
-LO_IMPLEMENTATION_C(io_readwriter, struct _hostnet_tcp_conn, hostnet_tcp, static)
-LO_IMPLEMENTATION_C(io_closer, struct _hostnet_tcp_conn, hostnet_tcp, static)
-LO_IMPLEMENTATION_C(io_bidi_closer, struct _hostnet_tcp_conn, hostnet_tcp, static)
-LO_IMPLEMENTATION_C(net_stream_conn, struct _hostnet_tcp_conn, hostnet_tcp, static)
+LO_IMPLEMENTATION_STATIC(io_reader, struct _hostnet_tcp_conn, hostnet_tcp);
+LO_IMPLEMENTATION_STATIC(io_writer, struct _hostnet_tcp_conn, hostnet_tcp);
+LO_IMPLEMENTATION_STATIC(io_readwriter, struct _hostnet_tcp_conn, hostnet_tcp);
+LO_IMPLEMENTATION_STATIC(io_closer, struct _hostnet_tcp_conn, hostnet_tcp);
+LO_IMPLEMENTATION_STATIC(io_bidi_closer, struct _hostnet_tcp_conn, hostnet_tcp);
+LO_IMPLEMENTATION_STATIC(net_stream_conn, struct _hostnet_tcp_conn, hostnet_tcp);
-LO_IMPLEMENTATION_C(io_closer, struct hostnet_udp_conn, hostnet_udp, static)
-LO_IMPLEMENTATION_C(net_packet_conn, struct hostnet_udp_conn, hostnet_udp, static)
+LO_IMPLEMENTATION_C(io_closer, struct hostnet_udp_conn, hostnet_udp);
+LO_IMPLEMENTATION_C(net_packet_conn, struct hostnet_udp_conn, hostnet_udp);
/* common *********************************************************************/
@@ -52,7 +55,7 @@ static void hostnet_handle_sig_io(int LM_UNUSED(sig), siginfo_t *info, void *LM_
}
static void hostnet_init(void) {
- struct sigaction action = {0};
+ struct sigaction action = {};
if (hostnet_sig_io)
return;
@@ -62,12 +65,12 @@ static void hostnet_init(void) {
action.sa_flags = SA_SIGINFO;
action.sa_sigaction = hostnet_handle_sig_io;
if (sigaction(hostnet_sig_io, &action, NULL) < 0)
- error(1, errno, "sigaction");
+ __error(1, errno, "sigaction");
}
#define WAKE_COROUTINE(args) do { \
int r; \
- union sigval val = {0}; \
+ union sigval val = {}; \
val.sival_int = (int)((args)->cr_coroutine); \
do { \
r = pthread_sigqueue((args)->cr_thread, \
@@ -77,46 +80,47 @@ static void hostnet_init(void) {
} while (r == EAGAIN); \
} while (0)
-static inline bool RUN_PTHREAD(void *(*fn)(void *), void *args) {
+static inline host_errno_t RUN_PTHREAD(void *(*fn)(void *), void *args) {
pthread_t thread;
+ host_errno_t r;
bool saved = cr_save_and_disable_interrupts();
- if (pthread_create(&thread, NULL, fn, args))
- return true;
+ r = pthread_create(&thread, NULL, fn, args);
+ if (r) {
+ cr_restore_interrupts(saved);
+ return r;
+ }
cr_pause_and_yield();
cr_restore_interrupts(saved);
- if (pthread_join(thread, NULL))
- return true;
- return false;
+ return pthread_join(thread, NULL);
}
enum hostnet_timeout_op {
- OP_NONE,
+ OP_CLOSE,
OP_SEND,
OP_RECV,
};
-static inline ssize_t hostnet_map_negerrno(ssize_t v, enum hostnet_timeout_op op) {
- if (v >= 0)
- return v;
- switch (v) {
- case -EHOSTUNREACH:
- return -NET_EARP_TIMEOUT;
- case -ETIMEDOUT:
+static inline error hostnet_error(host_errno_t errnum, enum hostnet_timeout_op op) {
+ assert(errnum > 0);
+ switch (errnum) {
+ case EHOSTUNREACH:
+ return error_new(E_NET_EARP_TIMEOUT);
+ case ETIMEDOUT:
switch (op) {
- case OP_NONE:
+ case OP_CLOSE:
assert_notreached("impossible ETIMEDOUT");
case OP_SEND:
- return -NET_EACK_TIMEOUT;
+ return error_new(E_NET_EACK_TIMEOUT);
case OP_RECV:
- return -NET_ERECV_TIMEOUT;
+ return error_new(E_NET_ERECV_TIMEOUT);
}
assert_notreached("invalid timeout op");
- case -EBADF:
- return -NET_ECLOSED;
- case -EMSGSIZE:
- return -NET_EMSGSIZE;
+ case EBADF:
+ return error_new(E_NET_ECLOSED);
+ case EMSGSIZE:
+ return error_new(E_POSIX_EMSGSIZE);
default:
- return -NET_EOTHER;
+ return errno2lm(errnum);
}
}
@@ -127,7 +131,7 @@ void hostnet_tcp_listener_init(struct hostnet_tcp_listener *self, uint16_t port)
union {
struct sockaddr_in in;
struct sockaddr gen;
- } addr = { 0 };
+ } addr = {};
hostnet_init();
@@ -135,15 +139,15 @@ void hostnet_tcp_listener_init(struct hostnet_tcp_listener *self, uint16_t port)
addr.in.sin_port = htons(port);
listenerfd = socket(AF_INET, SOCK_STREAM, 0);
if (listenerfd < 0)
- error(1, errno, "socket");
+ __error(1, errno, "socket");
if (setsockopt(listenerfd, SOL_SOCKET, SO_REUSEADDR, &(int){1}, sizeof(int)) < 0)
- error(1, errno, "setsockopt(fd=%d, SO_REUSEADDR=1)", listenerfd);
+ __error(1, errno, "setsockopt(fd=%d, SO_REUSEADDR=1)", listenerfd);
if (setsockopt(listenerfd, SOL_SOCKET, SO_REUSEPORT, &(int){1}, sizeof(int)) < 0)
- error(1, errno, "setsockopt(fd=%d, SO_REUSEPORT=1)", listenerfd);
+ __error(1, errno, "setsockopt(fd=%d, SO_REUSEPORT=1)", listenerfd);
if (bind(listenerfd, &addr.gen, sizeof addr) < 0)
- error(1, errno, "bind(fd=%d)", listenerfd);
+ __error(1, errno, "bind(fd=%d)", listenerfd);
if (listen(listenerfd, 0) < 0)
- error(1, errno, "listen(fd=%d)", listenerfd);
+ __error(1, errno, "listen(fd=%d)", listenerfd);
self->fd = listenerfd;
}
@@ -157,44 +161,48 @@ struct hostnet_pthread_accept_args {
int listenerfd;
int *ret_connfd;
+ host_errno_t *ret_errno;
};
static void *hostnet_pthread_accept(void *_args) {
struct hostnet_pthread_accept_args *args = _args;
*(args->ret_connfd) = accept(args->listenerfd, NULL, NULL);
- if (*(args->ret_connfd) < 0)
- *(args->ret_connfd) = -errno;
+ *(args->ret_errno) = errno;
WAKE_COROUTINE(args);
return NULL;
}
-static lo_interface net_stream_conn hostnet_tcplist_accept(struct hostnet_tcp_listener *listener) {
+net_stream_conn_or_error hostnet_tcplist_accept(struct hostnet_tcp_listener *listener) {
assert(listener);
int ret_connfd;
+ host_errno_t ret_errno;
struct hostnet_pthread_accept_args args = {
.cr_thread = pthread_self(),
.cr_coroutine = cr_getcid(),
.listenerfd = listener->fd,
.ret_connfd = &ret_connfd,
+ .ret_errno = &ret_errno,
};
- if (RUN_PTHREAD(hostnet_pthread_accept, &args))
- return LO_NULL(net_stream_conn);
-
+ host_errno_t thread_errno = RUN_PTHREAD(hostnet_pthread_accept, &args);
+ if (thread_errno)
+ return ERROR_NEW_ERR(net_stream_conn, errno2lm(thread_errno));
if (ret_connfd < 0)
- return LO_NULL(net_stream_conn);
+ return ERROR_NEW_ERR(net_stream_conn, hostnet_error(ret_errno, OP_RECV));
listener->active_conn.fd = ret_connfd;
listener->active_conn.read_deadline_ns = 0;
- return lo_box_hostnet_tcp_as_net_stream_conn(&listener->active_conn);
+ return ERROR_NEW_VAL(net_stream_conn, LO_BOX(net_stream_conn, &listener->active_conn));
}
/* TCP listener close() *******************************************************/
-static int hostnet_tcplist_close(struct hostnet_tcp_listener *listener) {
+error hostnet_tcplist_close(struct hostnet_tcp_listener *listener) {
assert(listener);
- return hostnet_map_negerrno(shutdown(listener->fd, SHUT_RDWR) ? -errno : 0, OP_NONE);
+ if (shutdown(listener->fd, SHUT_RDWR))
+ return hostnet_error(errno, OP_CLOSE);
+ return ERROR_NULL;
}
/* TCP read() *****************************************************************/
@@ -214,56 +222,74 @@ struct hostnet_pthread_readv_args {
const struct iovec *iov;
int iovcnt;
- ssize_t *ret;
+ size_t *ret_size;
+ host_errno_t *ret_errno;
};
static void *hostnet_pthread_readv(void *_args) {
struct hostnet_pthread_readv_args *args = _args;
- *(args->ret) = setsockopt(args->connfd, SOL_SOCKET, SO_RCVTIMEO,
- &args->timeout, sizeof(args->timeout));
- if (*(args->ret) < 0)
+ int r_i = setsockopt(args->connfd, SOL_SOCKET, SO_RCVTIMEO,
+ &args->timeout, sizeof(args->timeout));
+ if (r_i) {
+ *args->ret_size = 0;
+ *args->ret_errno = errno;
goto end;
+ }
- *(args->ret) = readv(args->connfd, args->iov, args->iovcnt);
- if (*(args->ret) < 0)
+ ssize_t r_ss = readv(args->connfd, args->iov, args->iovcnt);
+ if (r_ss < 0) {
+ *args->ret_size = 0;
+ *args->ret_errno = errno;
goto end;
+ }
+ *args->ret_size = r_ss;
+ *args->ret_errno = 0;
end:
- if (*(args->ret) < 0)
- *(args->ret) = hostnet_map_negerrno(-errno, OP_SEND);
WAKE_COROUTINE(args);
return NULL;
}
-static ssize_t hostnet_tcp_readv(struct _hostnet_tcp_conn *conn, const struct iovec *iov, int iovcnt) {
+static size_t_or_error hostnet_tcp_readv(struct _hostnet_tcp_conn *conn, const struct rd_iovec *iov, int iovcnt) {
assert(conn);
assert(iov);
assert(iovcnt > 0);
+ size_t count = 0;
+ for (int i = 0; i < iovcnt; i++)
+ count += iov[i].iov_len;
+ assert(count);
- ssize_t ret;
+ size_t ret_size;
+ host_errno_t ret_errno;
struct hostnet_pthread_readv_args args = {
.cr_thread = pthread_self(),
.cr_coroutine = cr_getcid(),
.connfd = conn->fd,
- .iov = iov,
+ .iov = (const struct iovec *)iov,
.iovcnt = iovcnt,
- .ret = &ret,
+ .ret_size = &ret_size,
+ .ret_errno = &ret_errno,
};
if (conn->read_deadline_ns) {
uint64_t now_ns = LO_CALL(bootclock, get_time_ns);
if (conn->read_deadline_ns < now_ns)
- return -NET_ERECV_TIMEOUT;
+ return ERROR_NEW_ERR(size_t, error_new(E_NET_ERECV_TIMEOUT));
args.timeout = ns_to_host_us_time(conn->read_deadline_ns-now_ns);
} else {
- args.timeout = (host_us_time_t){0};
+ args.timeout = (host_us_time_t){};
}
- if (RUN_PTHREAD(hostnet_pthread_readv, &args))
- return -NET_ETHREAD;
- return ret;
+ host_errno_t thread_errno = RUN_PTHREAD(hostnet_pthread_readv, &args);
+ if (thread_errno)
+ return ERROR_NEW_ERR(size_t, errno2lm(thread_errno));
+ if (ret_errno)
+ return ERROR_NEW_ERR(size_t, hostnet_error(ret_errno, OP_RECV));
+ if (!ret_size)
+ return ERROR_NEW_ERR(size_t, error_new(E_EOF));
+ return ERROR_NEW_VAL(size_t, ret_size);
}
/* TCP write() ****************************************************************/
@@ -276,14 +302,15 @@ struct hostnet_pthread_writev_args {
const struct iovec *iov;
int iovcnt;
- ssize_t *ret;
+ size_t *ret_size;
+ host_errno_t *ret_errno;
};
static void *hostnet_pthread_writev(void *_args) {
struct hostnet_pthread_writev_args *args = _args;
size_t count = 0;
- struct iovec *iov = alloca(sizeof(struct iovec)*args->iovcnt);
+ struct iovec *iov = stack_alloc(args->iovcnt, struct iovec);
for (int i = 0; i < args->iovcnt; i++) {
iov[i] = args->iov[i];
count += args->iov[i].iov_len;
@@ -294,7 +321,7 @@ static void *hostnet_pthread_writev(void *_args) {
while (done < count) {
ssize_t r = writev(args->connfd, iov, iovcnt);
if (r < 0) {
- hostnet_map_negerrno(-errno, OP_RECV);
+ *args->ret_errno = errno;
break;
}
done += r;
@@ -309,45 +336,59 @@ static void *hostnet_pthread_writev(void *_args) {
}
}
if (done == count)
- *(args->ret) = done;
+ *args->ret_errno = 0;
+ *args->ret_size = done;
WAKE_COROUTINE(args);
return NULL;
}
-static ssize_t hostnet_tcp_writev(struct _hostnet_tcp_conn *conn, const struct iovec *iov, int iovcnt) {
+static size_t_and_error hostnet_tcp_writev(struct _hostnet_tcp_conn *conn, const struct wr_iovec *iov, int iovcnt) {
assert(conn);
assert(iov);
assert(iovcnt > 0);
+ size_t count = 0;
+ for (int i = 0; i < iovcnt; i++)
+ count += iov[i].iov_len;
+ assert(count);
- ssize_t ret;
+ size_t ret_size;
+ host_errno_t ret_errno;
struct hostnet_pthread_writev_args args = {
.cr_thread = pthread_self(),
.cr_coroutine = cr_getcid(),
.connfd = conn->fd,
- .iov = iov,
+ .iov = (const struct iovec *)iov,
.iovcnt = iovcnt,
- .ret = &ret,
+ .ret_size = &ret_size,
+ .ret_errno = &ret_errno,
};
- if (RUN_PTHREAD(hostnet_pthread_writev, &args))
- return -NET_ETHREAD;
- return ret;
+ int thread_errno = RUN_PTHREAD(hostnet_pthread_writev, &args);
+ if (thread_errno)
+ return ERROR_AND(size_t, 0, errno2lm(thread_errno));
+ return ERROR_AND(size_t, ret_size, ret_errno ? hostnet_error(ret_errno, OP_SEND) : ERROR_NULL);
}
/* TCP close() ****************************************************************/
-static int hostnet_tcp_close(struct _hostnet_tcp_conn *conn) {
+static error hostnet_tcp_close(struct _hostnet_tcp_conn *conn) {
assert(conn);
- return hostnet_map_negerrno(shutdown(conn->fd, SHUT_RDWR) ? -errno : 0, OP_NONE);
+ if (shutdown(conn->fd, SHUT_RDWR))
+ return hostnet_error(errno, OP_CLOSE);
+ return ERROR_NULL;
}
-static int hostnet_tcp_close_read(struct _hostnet_tcp_conn *conn) {
+static error hostnet_tcp_close_read(struct _hostnet_tcp_conn *conn) {
assert(conn);
- return hostnet_map_negerrno(shutdown(conn->fd, SHUT_RD) ? -errno : 0, OP_NONE);
+ if (shutdown(conn->fd, SHUT_RD))
+ return hostnet_error(errno, OP_CLOSE);
+ return ERROR_NULL;
}
-static int hostnet_tcp_close_write(struct _hostnet_tcp_conn *conn) {
+static error hostnet_tcp_close_write(struct _hostnet_tcp_conn *conn) {
assert(conn);
- return hostnet_map_negerrno(shutdown(conn->fd, SHUT_WR) ? -errno : 0, OP_NONE);
+ if (shutdown(conn->fd, SHUT_WR))
+ return hostnet_error(errno, OP_CLOSE);
+ return ERROR_NULL;
}
/* UDP init() *****************************************************************/
@@ -358,7 +399,7 @@ void hostnet_udp_conn_init(struct hostnet_udp_conn *self, uint16_t port) {
struct sockaddr_in in;
struct sockaddr gen;
struct sockaddr_storage stor;
- } addr = { 0 };
+ } addr = {};
hostnet_init();
@@ -366,9 +407,11 @@ void hostnet_udp_conn_init(struct hostnet_udp_conn *self, uint16_t port) {
addr.in.sin_port = htons(port);
fd = socket(AF_INET, SOCK_DGRAM, 0);
if (fd < 0)
- error(1, errno, "socket");
+ __error(1, errno, "socket");
+ if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &(int){1}, sizeof(int)) < 0)
+ __error(1, errno, "setsockopt(fd=%d, SO_BROADCAST=1)", fd);
if (bind(fd, &addr.gen, sizeof addr) < 0)
- error(1, errno, "bind");
+ __error(1, errno, "bind");
self->fd = fd;
self->read_deadline_ns = 0;
@@ -386,7 +429,8 @@ struct hostnet_pthread_sendto_args {
struct net_ip4_addr node;
uint16_t port;
- ssize_t *ret;
+ size_t *ret_size;
+ host_errno_t *ret_errno;
};
static void *hostnet_pthread_sendto(void *_args) {
@@ -395,27 +439,33 @@ static void *hostnet_pthread_sendto(void *_args) {
struct sockaddr_in in;
struct sockaddr gen;
struct sockaddr_storage stor;
- } addr = { 0 };
+ } addr = {};
addr.in.sin_family = AF_INET;
addr.in.sin_addr.s_addr =
- (((uint32_t)args->node.octets[0])<<24) |
- (((uint32_t)args->node.octets[1])<<16) |
- (((uint32_t)args->node.octets[2])<< 8) |
- (((uint32_t)args->node.octets[3])<< 0) ;
+ (((uint32_t)args->node.octets[3])<<24) |
+ (((uint32_t)args->node.octets[2])<<16) |
+ (((uint32_t)args->node.octets[1])<< 8) |
+ (((uint32_t)args->node.octets[0])<< 0) ;
addr.in.sin_port = htons(args->port);
- *(args->ret) = sendto(args->connfd, args->buf, args->count, 0, &addr.gen, sizeof(addr));
- if (*(args->ret) < 0)
- *(args->ret) = hostnet_map_negerrno(-errno, OP_SEND);
+ ssize_t r = sendto(args->connfd, args->buf, args->count, 0, &addr.gen, sizeof(addr));
+ if (r < 0) {
+ *args->ret_size = 0;
+ *args->ret_errno = errno;
+ } else {
+ *args->ret_size = r;
+ *args->ret_errno = 0;
+ }
WAKE_COROUTINE(args);
return NULL;
}
-static ssize_t hostnet_udp_sendto(struct hostnet_udp_conn *conn, void *buf, size_t count,
- struct net_ip4_addr node, uint16_t port) {
+error hostnet_udp_sendto(struct hostnet_udp_conn *conn, void *buf, size_t count,
+ struct net_ip4_addr node, uint16_t port) {
assert(conn);
- ssize_t ret;
+ size_t ret_size;
+ host_errno_t ret_errno;
struct hostnet_pthread_sendto_args args = {
.cr_thread = pthread_self(),
.cr_coroutine = cr_getcid(),
@@ -426,17 +476,21 @@ static ssize_t hostnet_udp_sendto(struct hostnet_udp_conn *conn, void *buf, size
.node = node,
.port = port,
- .ret = &ret,
+ .ret_size = &ret_size,
+ .ret_errno = &ret_errno,
};
- if (RUN_PTHREAD(hostnet_pthread_sendto, &args))
- return -NET_ETHREAD;
- return ret;
+ int thread_errno = RUN_PTHREAD(hostnet_pthread_sendto, &args);
+ if (thread_errno)
+ return errno2lm(thread_errno);
+ if (ret_errno)
+ return hostnet_error(ret_errno, OP_SEND);
+ return ERROR_NULL;
}
/* UDP recvfrom() *************************************************************/
-static void hostnet_udp_set_recv_deadline(struct hostnet_udp_conn *conn,
- uint64_t ts_ns) {
+void hostnet_udp_set_recv_deadline(struct hostnet_udp_conn *conn,
+ uint64_t ts_ns) {
assert(conn);
conn->read_deadline_ns = ts_ns;
@@ -451,7 +505,8 @@ struct hostnet_pthread_recvfrom_args {
void *buf;
size_t count;
- ssize_t *ret_size;
+ size_t *ret_size;
+ host_errno_t *ret_errno;
struct net_ip4_addr *ret_node;
uint16_t *ret_port;
};
@@ -463,42 +518,49 @@ static void *hostnet_pthread_recvfrom(void *_args) {
struct sockaddr_in in;
struct sockaddr gen;
struct sockaddr_storage stor;
- } addr = { 0 };
- socklen_t addr_size;
-
- *(args->ret_size) = setsockopt(args->connfd, SOL_SOCKET, SO_RCVTIMEO,
- &args->timeout, sizeof(args->timeout));
- if (*(args->ret_size) < 0)
+ } addr = {};
+ socklen_t addr_size = sizeof(addr);
+
+ int r_i = setsockopt(args->connfd, SOL_SOCKET, SO_RCVTIMEO,
+ &args->timeout, sizeof(args->timeout));
+ if (r_i) {
+ *args->ret_size = 0;
+ *args->ret_errno = errno;
goto end;
+ }
- *(args->ret_size) = recvfrom(args->connfd, args->buf, args->count,
- MSG_TRUNC, &addr.gen, &addr_size);
- if (*(args->ret_size) < 0)
+ ssize_t r_ss = recvfrom(args->connfd, args->buf, args->count,
+ MSG_TRUNC, &addr.gen, &addr_size);
+ if (r_ss < 0) {
+ *args->ret_size = 0;
+ *args->ret_errno = errno;
goto end;
+ }
+ *args->ret_size = r_ss;
+ *args->ret_errno = 0;
assert(addr.in.sin_family == AF_INET);
if (args->ret_node) {
- args->ret_node->octets[0] = (addr.in.sin_addr.s_addr >> 24) & 0xFF;
- args->ret_node->octets[1] = (addr.in.sin_addr.s_addr >> 16) & 0xFF;
- args->ret_node->octets[2] = (addr.in.sin_addr.s_addr >> 8) & 0xFF;
- args->ret_node->octets[3] = (addr.in.sin_addr.s_addr >> 0) & 0xFF;
+ args->ret_node->octets[3] = (addr.in.sin_addr.s_addr >> 24) & 0xFF;
+ args->ret_node->octets[2] = (addr.in.sin_addr.s_addr >> 16) & 0xFF;
+ args->ret_node->octets[1] = (addr.in.sin_addr.s_addr >> 8) & 0xFF;
+ args->ret_node->octets[0] = (addr.in.sin_addr.s_addr >> 0) & 0xFF;
}
if (args->ret_port) {
(*args->ret_port) = ntohs(addr.in.sin_port);
}
end:
- if (*(args->ret_size) < 0)
- *(args->ret_size) = hostnet_map_negerrno(-errno, OP_RECV);
WAKE_COROUTINE(args);
return NULL;
}
-static ssize_t hostnet_udp_recvfrom(struct hostnet_udp_conn *conn, void *buf, size_t count,
- struct net_ip4_addr *ret_node, uint16_t *ret_port) {
+size_t_or_error hostnet_udp_recvfrom(struct hostnet_udp_conn *conn, void *buf, size_t count,
+ struct net_ip4_addr *ret_node, uint16_t *ret_port) {
assert(conn);
- ssize_t ret;
+ size_t ret_size;
+ host_errno_t ret_errno;
struct hostnet_pthread_recvfrom_args args = {
.cr_thread = pthread_self(),
.cr_coroutine = cr_getcid(),
@@ -507,28 +569,34 @@ static ssize_t hostnet_udp_recvfrom(struct hostnet_udp_conn *conn, void *buf, si
.buf = buf,
.count = count,
- .ret_size = &ret,
+ .ret_size = &ret_size,
+ .ret_errno = &ret_errno,
.ret_node = ret_node,
.ret_port = ret_port,
};
if (conn->read_deadline_ns) {
uint64_t now_ns = LO_CALL(bootclock, get_time_ns);
if (conn->read_deadline_ns < now_ns)
- return -NET_ERECV_TIMEOUT;
+ return ERROR_NEW_ERR(size_t, error_new(E_NET_ERECV_TIMEOUT));
args.timeout = ns_to_host_us_time(conn->read_deadline_ns-now_ns);
} else {
- args.timeout = (host_us_time_t){0};
+ args.timeout = (host_us_time_t){};
}
- if (RUN_PTHREAD(hostnet_pthread_recvfrom, &args))
- return -NET_ETHREAD;
- return ret;
+ host_errno_t thread_errno = RUN_PTHREAD(hostnet_pthread_recvfrom, &args);
+ if (thread_errno)
+ return ERROR_NEW_ERR(size_t, errno2lm(thread_errno));
+ if (ret_errno)
+ return ERROR_NEW_ERR(size_t, hostnet_error(ret_errno, OP_RECV));
+ return ERROR_NEW_VAL(size_t, ret_size);
}
/* UDP close() ****************************************************************/
-static int hostnet_udp_close(struct hostnet_udp_conn *conn) {
+error hostnet_udp_close(struct hostnet_udp_conn *conn) {
assert(conn);
- return hostnet_map_negerrno(close(conn->fd) ? -errno : 0, OP_NONE);
+ if (close(conn->fd))
+ return hostnet_error(errno, OP_CLOSE);
+ return ERROR_NULL;
}
diff --git a/libhw_cr/host_util.c b/libhw_cr/host_util.c
index 958ed9c..2d45490 100644
--- a/libhw_cr/host_util.c
+++ b/libhw_cr/host_util.c
@@ -1,12 +1,18 @@
/* libhw_cr/host_util.c - Utilities for GNU/Linux hosts
*
- * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
-#include <error.h> /* for error(3gnu) */
+#include <errno.h> /* for E* */
#include <signal.h> /* for SIGRTMIN, SIGRTMAX */
+#define error __error
+#include <error.h>
+#undef error
+
+#include <libhw/generic/alarmclock.h> /* for {X}S_PER_S */
+
#include "host_util.h"
int host_sigrt_alloc(void) {
@@ -16,6 +22,124 @@ int host_sigrt_alloc(void) {
next = SIGRTMIN;
int ret = next++;
if (ret > SIGRTMAX)
- error(1, 0, "SIGRTMAX exceeded");
+ __error(1, 0, "SIGRTMAX exceeded");
+ return ret;
+}
+
+host_us_time_t ns_to_host_us_time(uint64_t time_ns) {
+ host_us_time_t ret;
+ ret.tv_sec = time_ns
+ /NS_PER_S;
+ ret.tv_usec = (time_ns - ((uint64_t)ret.tv_sec)*NS_PER_S)
+ /(NS_PER_S/US_PER_S);
+ return ret;
+}
+
+host_ns_time_t ns_to_host_ns_time(uint64_t time_ns) {
+ host_ns_time_t ret;
+ ret.tv_sec = time_ns
+ /NS_PER_S;
+ ret.tv_nsec = time_ns - ((uint64_t)ret.tv_sec)*NS_PER_S;
return ret;
}
+
+uint64_t ns_from_host_us_time(host_us_time_t host_time) {
+ return (((uint64_t)host_time.tv_sec) * NS_PER_S) +
+ ((uint64_t)host_time.tv_usec * (NS_PER_S/US_PER_S));
+}
+
+uint64_t ns_from_host_ns_time(host_ns_time_t host_time) {
+ return (((uint64_t)host_time.tv_sec) * NS_PER_S) +
+ ((uint64_t)host_time.tv_nsec);
+}
+
+_errnum errno_host2lm(host_errno_t host) {
+ switch (host) {
+ case E2BIG: return E_POSIX_E2BIG;
+ case EACCES: return E_POSIX_EACCES;
+ case EADDRINUSE: return E_POSIX_EADDRINUSE;
+ case EADDRNOTAVAIL: return E_POSIX_EADDRNOTAVAIL;
+ case EAFNOSUPPORT: return E_POSIX_EAFNOSUPPORT;
+ case EAGAIN: return E_POSIX_EAGAIN;
+ case EALREADY: return E_POSIX_EALREADY;
+ case EBADF: return E_POSIX_EBADF;
+ case EBADMSG: return E_POSIX_EBADMSG;
+ case EBUSY: return E_POSIX_EBUSY;
+ case ECANCELED: return E_POSIX_ECANCELED;
+ case ECHILD: return E_POSIX_ECHILD;
+ case ECONNABORTED: return E_POSIX_ECONNABORTED;
+ case ECONNREFUSED: return E_POSIX_ECONNREFUSED;
+ case ECONNRESET: return E_POSIX_ECONNRESET;
+ case EDEADLK: return E_POSIX_EDEADLK;
+ case EDESTADDRREQ: return E_POSIX_EDESTADDRREQ;
+ case EDOM: return E_POSIX_EDOM;
+ case EDQUOT: return E_POSIX_EDQUOT;
+ case EEXIST: return E_POSIX_EEXIST;
+ case EFAULT: return E_POSIX_EFAULT;
+ case EFBIG: return E_POSIX_EFBIG;
+ case EHOSTUNREACH: return E_POSIX_EHOSTUNREACH;
+ case EIDRM: return E_POSIX_EIDRM;
+ case EILSEQ: return E_POSIX_EILSEQ;
+ case EINPROGRESS: return E_POSIX_EINPROGRESS;
+ case EINTR: return E_POSIX_EINTR;
+ case EINVAL: return E_POSIX_EINVAL;
+ case EIO: return E_POSIX_EIO;
+ case EISCONN: return E_POSIX_EISCONN;
+ case EISDIR: return E_POSIX_EISDIR;
+ case ELOOP: return E_POSIX_ELOOP;
+ case EMFILE: return E_POSIX_EMFILE;
+ case EMLINK: return E_POSIX_EMLINK;
+ case EMSGSIZE: return E_POSIX_EMSGSIZE;
+ case EMULTIHOP: return E_POSIX_EMULTIHOP;
+ case ENAMETOOLONG: return E_POSIX_ENAMETOOLONG;
+ case ENETDOWN: return E_POSIX_ENETDOWN;
+ case ENETRESET: return E_POSIX_ENETRESET;
+ case ENETUNREACH: return E_POSIX_ENETUNREACH;
+ case ENFILE: return E_POSIX_ENFILE;
+ case ENOBUFS: return E_POSIX_ENOBUFS;
+ case ENODEV: return E_POSIX_ENODEV;
+ case ENOENT: return E_POSIX_ENOENT;
+ case ENOEXEC: return E_POSIX_ENOEXEC;
+ case ENOLCK: return E_POSIX_ENOLCK;
+ case ENOLINK: return E_POSIX_ENOLINK;
+ case ENOMEM: return E_POSIX_ENOMEM;
+ case ENOMSG: return E_POSIX_ENOMSG;
+ case ENOPROTOOPT: return E_POSIX_ENOPROTOOPT;
+ case ENOSPC: return E_POSIX_ENOSPC;
+ case ENOSYS: return E_POSIX_ENOSYS;
+ case ENOTCONN: return E_POSIX_ENOTCONN;
+ case ENOTDIR: return E_POSIX_ENOTDIR;
+ case ENOTEMPTY: return E_POSIX_ENOTEMPTY;
+ case ENOTRECOVERABLE: return E_POSIX_ENOTRECOVERABLE;
+ case ENOTSOCK: return E_POSIX_ENOTSOCK;
+ case ENOTSUP: return E_POSIX_ENOTSUP;
+ case ENOTTY: return E_POSIX_ENOTTY;
+ case ENXIO: return E_POSIX_ENXIO;
+ case EOVERFLOW: return E_POSIX_EOVERFLOW;
+ case EOWNERDEAD: return E_POSIX_EOWNERDEAD;
+ case EPERM: return E_POSIX_EPERM;
+ case EPIPE: return E_POSIX_EPIPE;
+ case EPROTO: return E_POSIX_EPROTO;
+ case EPROTONOSUPPORT: return E_POSIX_EPROTONOSUPPORT;
+ case EPROTOTYPE: return E_POSIX_EPROTOTYPE;
+ case ERANGE: return E_POSIX_ERANGE;
+ case EROFS: return E_POSIX_EROFS;
+ case ESOCKTNOSUPPORT: return E_POSIX_ESOCKTNOSUPPORT;
+ case ESPIPE: return E_POSIX_ESPIPE;
+ case ESRCH: return E_POSIX_ESRCH;
+ case ESTALE: return E_POSIX_ESTALE;
+ case ETIMEDOUT: return E_POSIX_ETIMEDOUT;
+ case ETXTBSY: return E_POSIX_ETXTBSY;
+ case EXDEV: return E_POSIX_EXDEV;
+ default:
+ switch (host) {
+ case EOPNOTSUPP: return E_POSIX_EOPNOTSUPP;
+ case EWOULDBLOCK: return E_POSIX_EWOULDBLOCK;
+ default: return E_EUNKNOWN;
+ }
+ }
+}
+
+error errno2lm(host_errno_t host) {
+ return error_new(errno_host2lm(host));
+}
diff --git a/libhw_cr/host_util.h b/libhw_cr/host_util.h
index 8c53fab..7e559ef 100644
--- a/libhw_cr/host_util.h
+++ b/libhw_cr/host_util.h
@@ -1,47 +1,30 @@
/* libhw_cr/host_util.h - Utilities for GNU/Linux hosts
*
- * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
#ifndef _LIBHW_CR_HOST_UTIL_H_
#define _LIBHW_CR_HOST_UTIL_H_
-#include <time.h> /* for struct timespec */
-#include <sys/time.h> /* for struct timeval */
+#include <stdint.h> /* for uint{n}_t */
+#include <sys/time.h> /* for struct timeval */
+#include <time.h> /* for struct timespec */
-#include <libhw/generic/alarmclock.h> /* for {X}S_PER_S */
+#include <libmisc/error.h> /* for _errnum, error */
int host_sigrt_alloc(void);
typedef struct timeval host_us_time_t;
typedef struct timespec host_ns_time_t;
-static inline host_us_time_t ns_to_host_us_time(uint64_t time_ns) {
- host_us_time_t ret;
- ret.tv_sec = time_ns
- /NS_PER_S;
- ret.tv_usec = (time_ns - ((uint64_t)ret.tv_sec)*NS_PER_S)
- /(NS_PER_S/US_PER_S);
- return ret;
-}
-
-static inline host_ns_time_t ns_to_host_ns_time(uint64_t time_ns) {
- host_ns_time_t ret;
- ret.tv_sec = time_ns
- /NS_PER_S;
- ret.tv_nsec = time_ns - ((uint64_t)ret.tv_sec)*NS_PER_S;
- return ret;
-}
-
-static inline uint64_t ns_from_host_us_time(host_us_time_t host_time) {
- return (((uint64_t)host_time.tv_sec) * NS_PER_S) +
- ((uint64_t)host_time.tv_usec * (NS_PER_S/US_PER_S));
-}
-
-static inline uint64_t ns_from_host_ns_time(host_ns_time_t host_time) {
- return (((uint64_t)host_time.tv_sec) * NS_PER_S) +
- ((uint64_t)host_time.tv_nsec);
-}
+host_us_time_t ns_to_host_us_time(uint64_t time_ns);
+host_ns_time_t ns_to_host_ns_time(uint64_t time_ns);
+uint64_t ns_from_host_us_time(host_us_time_t host_time);
+uint64_t ns_from_host_ns_time(host_ns_time_t host_time);
+
+#define host_errno_t int
+_errnum errno_host2lm(host_errno_t host);
+error errno2lm(host_errno_t host);
#endif /* _LIBHW_CR_HOST_UTIL_H_ */
diff --git a/libhw_cr/rp2040_dma.c b/libhw_cr/rp2040_dma.c
index 69116bf..8901f06 100644
--- a/libhw_cr/rp2040_dma.c
+++ b/libhw_cr/rp2040_dma.c
@@ -1,22 +1,38 @@
/* libhw_cr/rp2040_dma.c - Utilities for sharing the DMA IRQs
*
+ * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
* Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
-#include <stdbool.h>
-
#include <hardware/irq.h> /* for irq_set_exclusive_handler() */
#include "rp2040_dma.h"
+/* Borrowed from <hardware/dma.h> *********************************************/
+
+dma_channel_hw_t *dma_channel_hw_addr(uint channel) {
+ assert(channel < NUM_DMA_CHANNELS);
+ return &dma_hw->ch[channel];
+}
+
+enum dma_channel_transfer_size {
+ DMA_SIZE_8 = 0, ///< Byte transfer (8 bits)
+ DMA_SIZE_16 = 1, ///< Half word transfer (16 bits)
+ DMA_SIZE_32 = 2 ///< Word transfer (32 bits)
+};
+
+/* Our own code ***************************************************************/
+
struct dmairq_handler_entry {
dmairq_handler_t fn;
void *arg;
};
-struct dmairq_handler_entry dmairq_handlers[NUM_DMA_CHANNELS] = {0};
+struct dmairq_handler_entry dmairq_handlers[NUM_DMA_CHANNELS] = {};
-bool dmairq_initialized[NUM_DMA_IRQS] = {0};
+bool dmairq_initialized[NUM_DMA_IRQS] = {};
static void dmairq_handler(void) {
enum dmairq irq = __get_current_exception() - VTABLE_FIRST_IRQ;
diff --git a/libhw_cr/rp2040_dma.h b/libhw_cr/rp2040_dma.h
index e295adf..5c1c7bb 100644
--- a/libhw_cr/rp2040_dma.h
+++ b/libhw_cr/rp2040_dma.h
@@ -11,6 +11,7 @@
#define _LIBHW_CR_RP2040_DMA_H_
#include <assert.h>
+#include <stddef.h> /* for offsetof() */
#include <stdint.h> /* for uint32_t */
#include <hardware/regs/dreq.h> /* for DREQ_* for use with DMA_CTRL_TREQ_SEL() */
@@ -20,15 +21,12 @@
/* Borrowed from <hardware/dma.h> *********************************************/
-static inline dma_channel_hw_t *dma_channel_hw_addr(uint channel) {
- assert(channel < NUM_DMA_CHANNELS);
- return &dma_hw->ch[channel];
-}
+dma_channel_hw_t *dma_channel_hw_addr(uint channel);
enum dma_channel_transfer_size {
- DMA_SIZE_8 = 0, ///< Byte transfer (8 bits)
- DMA_SIZE_16 = 1, ///< Half word transfer (16 bits)
- DMA_SIZE_32 = 2 ///< Word transfer (32 bits)
+ DMA_SIZE_8 = 0, ///< Byte transfer (8 bits)
+ DMA_SIZE_16 = 1, ///< Half word transfer (16 bits)
+ DMA_SIZE_32 = 2 ///< Word transfer (32 bits)
};
/* Our own code ***************************************************************/
@@ -105,6 +103,8 @@ struct dma_alias3_short3 { READ_ADDR ; };
struct dma_alias2_short3: &dma_channel_hw_addr(CH)->al2_write_addr_trig, \
struct dma_alias3_short3: &dma_channel_hw_addr(CH)->al3_read_addr_trig))
+#define DMA_IS_TRIGGER(TYP, FIELD) (offsetof(TYP, FIELD) == 0xC)
+
#define DMA_CHAN_WR_TRANS_COUNT(TYP) \
(sizeof(TYP)/4)
diff --git a/libhw_cr/rp2040_gpioirq.c b/libhw_cr/rp2040_gpioirq.c
index 1ae74f9..ecbdb04 100644
--- a/libhw_cr/rp2040_gpioirq.c
+++ b/libhw_cr/rp2040_gpioirq.c
@@ -4,8 +4,8 @@
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
+#include <hardware/irq.h> /* for irq_set_exclusive_handler() */
#include <hardware/structs/io_bank0.h> /* for io_bank0_hw */
-#include <hardware/irq.h> /* for irq_set_exclusive_handler() */
#include <libmisc/macro.h>
@@ -15,7 +15,7 @@ struct gpioirq_handler_entry {
gpioirq_handler_t fn;
void *arg;
};
-struct gpioirq_handler_entry gpioirq_handlers[NUM_BANK0_GPIOS][4] = {0};
+struct gpioirq_handler_entry gpioirq_handlers[NUM_BANK0_GPIOS][4] = {};
int gpioirq_core = -1;
diff --git a/libhw_cr/rp2040_hwspi.c b/libhw_cr/rp2040_hwspi.c
index d4adb11..398cd68 100644
--- a/libhw_cr/rp2040_hwspi.c
+++ b/libhw_cr/rp2040_hwspi.c
@@ -4,14 +4,12 @@
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
-#include <alloca.h>
-#include <inttypes.h> /* for PRIu{n} */
-
#include <hardware/clocks.h> /* for clock_get_hz() and clk_peri */
#include <hardware/gpio.h>
#include <hardware/spi.h>
#include <libcr/coroutine.h>
+#include <libmisc/alloc.h>
#include <libmisc/assert.h>
#define LOG_NAME RP2040_SPI
@@ -30,8 +28,8 @@
#error config.h must define CONFIG_RP2040_SPI_DEBUG (bool)
#endif
-LO_IMPLEMENTATION_C(io_duplex_readwriter, struct rp2040_hwspi, rp2040_hwspi, static)
-LO_IMPLEMENTATION_C(spi, struct rp2040_hwspi, rp2040_hwspi, static)
+LO_IMPLEMENTATION_C(io_duplex_readwriter, struct rp2040_hwspi, rp2040_hwspi);
+LO_IMPLEMENTATION_C(spi, struct rp2040_hwspi, rp2040_hwspi);
static void rp2040_hwspi_intrhandler(void *_self, enum dmairq LM_UNUSED(irq), uint LM_UNUSED(channel)) {
struct rp2040_hwspi *self = _self;
@@ -70,7 +68,7 @@ void _rp2040_hwspi_init(struct rp2040_hwspi *self,
assert(self);
assert(baudrate_hz);
uint32_t clk_peri_hz = clock_get_hz(clk_peri);
- debugf("clk_peri = %"PRIu32"Hz", clk_peri_hz);
+ log_debugln("clk_peri = ", clk_peri_hz, "Hz");
assert(baudrate_hz*2 <= clk_peri_hz);
assert_4distinct(pin_miso, pin_mosi, pin_clk, pin_cs);
assert_4distinct(dma1, dma2, dma3, dma4);
@@ -97,7 +95,7 @@ void _rp2040_hwspi_init(struct rp2040_hwspi *self,
}
actual_baudrate_hz = spi_init(inst, baudrate_hz);
- debugf("baudrate = %uHz", actual_baudrate_hz);
+ log_debugln("baudrate = ", actual_baudrate_hz, "Hz");
assert(actual_baudrate_hz == baudrate_hz);
spi_set_format(inst, 8,
(mode & 0b10) ? SPI_CPOL_1 : SPI_CPOL_0,
@@ -130,13 +128,15 @@ void _rp2040_hwspi_init(struct rp2040_hwspi *self,
self->dma_tx_data = dma3;
self->dma_rx_data = dma4;
self->dead_until_ns = 0;
- self->sema = (cr_sema_t){0};
+ self->sema = (cr_sema_t){};
/* Initialize the interrupt handler. */
+ /* We do this on (just) the rx channel, because the way the
+ * SSP works reads necessarily complete *after* writes. */
dmairq_set_and_enable_exclusive_handler(DMAIRQ_0, self->dma_rx_data, rp2040_hwspi_intrhandler, self);
}
-static void rp2040_hwspi_readwritev(struct rp2040_hwspi *self, const struct duplex_iovec *iov, int iovcnt) {
+size_t_and_error rp2040_hwspi_readwritev(struct rp2040_hwspi *self, const struct duplex_iovec *iov, int iovcnt) {
assert(self);
assert(self->inst);
assert(iov);
@@ -156,30 +156,52 @@ static void rp2040_hwspi_readwritev(struct rp2040_hwspi *self, const struct dupl
uint8_t bogus_rx_dst;
+ size_t count = 0;
int pruned_iovcnt = 0;
- for (int i = 0; i < iovcnt; i++)
+ for (int i = 0; i < iovcnt; i++) {
+ count += iov[i].iov_len;
if (iov[i].iov_len)
pruned_iovcnt++;
- if (!pruned_iovcnt)
- return;
+ }
+ assert(count);
- /* For tx_data_blocks, it doesn't really matter which aliases
- * we choose:
+ /* It doesn't *really* matter which aliases we choose:
+ *
* - None of our fields can be NULL (so no
* false-termination).
+ *
* - Moving const fields first so they don't have to be
- * re-programmed each time isn't possible for us there need
- * to be at least 2 const fields, and we only have 1
+ * re-programmed each time isn't possible for us; there
+ * need to be at least 2 const fields, and we only have 1
* (read_addr for rx_data_blocks, and write_addr for
* tx_data_blocks).
*
- * But for rx_data_blocks, we need ctrl to be the trigger
- * register so that the DMA_CTRL_IRQ_QUIET flag isn't cleared
- * before we get to the trigger; and while for tx_data_blocks
- * it doesn't really matter, the inverse would be nice.
+ * The code following this initial declaration is generic to
+ * the alias, so changing which alias is used is easy.
+ *
+ * Since we have no hard requirements, here are some mild
+ * preferences:
+ *
+ * - I like the aliases being different for each channel,
+ * because it helps prevent alias-specific code from
+ * sneaking in.
+ *
+ * - I like the rx channel (the channel the interrupt handler
+ * is wired to) having ctrl be the trigger, so that we
+ * don't have to worry about DMA_CTRL_IRQ_QUIET being
+ * cleared before the trigger, and at the end the control
+ * block is clean and zeroed-out.
+ *
+ * - Conversely, I like the tx channel (the non-interrupt
+ * channel) having ctrl *not* be the trigger, so that
+ * DMA_CTRL_IRQ_QUIET is cleared by the time the trigger
+ * happens, so the IRQ machinery doesn't need to be engaged
+ * at all.
*/
- struct dma_alias1 *tx_data_blocks = alloca(sizeof(struct dma_alias1)*(pruned_iovcnt+1));
- struct dma_alias0 *rx_data_blocks = alloca(sizeof(struct dma_alias0)*(pruned_iovcnt+1));
+ [[gnu::cleanup(heap_cleanup)]] struct dma_alias1 *tx_data_blocks = heap_alloc(pruned_iovcnt+1, struct dma_alias1);
+ [[gnu::cleanup(heap_cleanup)]] struct dma_alias0 *rx_data_blocks = heap_alloc(pruned_iovcnt+1, struct dma_alias0);
+ static_assert(!DMA_IS_TRIGGER(typeof(tx_data_blocks[0]), ctrl));
+ static_assert(DMA_IS_TRIGGER(typeof(rx_data_blocks[0]), ctrl));
for (int i = 0, j = 0; i < iovcnt; i++) {
if (!iov[i].iov_len)
@@ -208,8 +230,13 @@ static void rp2040_hwspi_readwritev(struct rp2040_hwspi *self, const struct dupl
};
j++;
}
- tx_data_blocks[pruned_iovcnt] = (typeof(tx_data_blocks[0])){0};
- rx_data_blocks[pruned_iovcnt] = (typeof(rx_data_blocks[0])){0};
+ tx_data_blocks[pruned_iovcnt] = (typeof(tx_data_blocks[0])){};
+ rx_data_blocks[pruned_iovcnt] = (typeof(rx_data_blocks[0])){};
+ /* If ctrl isn't the trigger then we need to make sure that
+ * DMA_CTRL_IRQ_QUIET isn't cleared before the trigger
+ * happens. */
+ if (!DMA_IS_TRIGGER(typeof(rx_data_blocks[0]), ctrl))
+ rx_data_blocks[pruned_iovcnt].ctrl = DMA_CTRL_IRQ_QUIET;
/* Set up ctrl. */
DMA_NONTRIGGER(self->dma_tx_ctrl, read_addr) = tx_data_blocks;
@@ -241,4 +268,5 @@ static void rp2040_hwspi_readwritev(struct rp2040_hwspi *self, const struct dupl
cr_restore_interrupts(saved);
cr_sema_wait(&self->sema);
self->dead_until_ns = LO_CALL(bootclock, get_time_ns) + self->min_delay_ns;
+ return ERROR_AND(size_t, count, ERROR_NULL);
}
diff --git a/libhw_cr/rp2040_hwtimer.c b/libhw_cr/rp2040_hwtimer.c
index 8227abb..6d7e868 100644
--- a/libhw_cr/rp2040_hwtimer.c
+++ b/libhw_cr/rp2040_hwtimer.c
@@ -27,8 +27,7 @@ struct rp2040_hwtimer {
bool initialized;
struct alarmclock_trigger *queue;
};
-LO_IMPLEMENTATION_H(alarmclock, struct rp2040_hwtimer, rp2040_hwtimer);
-LO_IMPLEMENTATION_C(alarmclock, struct rp2040_hwtimer, rp2040_hwtimer, static);
+LO_IMPLEMENTATION_STATIC(alarmclock, struct rp2040_hwtimer, rp2040_hwtimer);
/* Globals ********************************************************************/
@@ -44,7 +43,7 @@ static_assert(sizeof(hwtimers)/sizeof(hwtimers[0]) == _RP2040_HWALARM_NUM);
lo_interface alarmclock rp2040_hwtimer(enum rp2040_hwalarm_instance alarm_num) {
assert(alarm_num < _RP2040_HWALARM_NUM);
- return lo_box_rp2040_hwtimer_as_alarmclock(&hwtimers[alarm_num]);
+ return LO_BOX(alarmclock, &hwtimers[alarm_num]);
}
@@ -52,7 +51,11 @@ static uint64_t rp2040_hwtimer_get_time_ns(struct rp2040_hwtimer *) {
return timer_time_us_64(timer_hw) * (NS_PER_S/US_PER_S);
}
-#define NS_TO_US_ROUNDUP(x) LM_CEILDIV(x, NS_PER_S/US_PER_S)
+static uint32_t ns_to_us_roundup_and_cap(uint64_t ns) {
+ if (ns >= ((uint64_t)(UINT32_MAX))*1000)
+ return UINT32_MAX;
+ return (ns+999)/1000;
+}
static void rp2040_hwtimer_intrhandler(void) {
uint irq_num = __get_current_exception() - VTABLE_FIRST_IRQ;
@@ -62,7 +65,7 @@ static void rp2040_hwtimer_intrhandler(void) {
struct rp2040_hwtimer *alarmclock = &hwtimers[alarm_num];
while (alarmclock->queue &&
- NS_TO_US_ROUNDUP(alarmclock->queue->fire_at_ns) <= timer_time_us_64(timer_hw)) {
+ alarmclock->queue->fire_at_ns <= timer_time_us_64(timer_hw)*1000) {
struct alarmclock_trigger *trigger = alarmclock->queue;
trigger->cb(trigger->cb_arg);
alarmclock->queue = trigger->next;
@@ -74,10 +77,10 @@ static void rp2040_hwtimer_intrhandler(void) {
hw_clear_bits(&timer_hw->intf, 1 << alarm_num); /* Clear "force"ing the interrupt. */
hw_clear_bits(&timer_hw->intr, 1 << alarm_num); /* Clear natural firing of the alarm. */
if (alarmclock->queue)
- timer_hw->alarm[alarm_num] = (uint32_t)NS_TO_US_ROUNDUP(alarmclock->queue->fire_at_ns);
+ timer_hw->alarm[alarm_num] = ns_to_us_roundup_and_cap(alarmclock->queue->fire_at_ns);
}
-static bool rp2040_hwtimer_add_trigger(struct rp2040_hwtimer *alarmclock,
+static void rp2040_hwtimer_add_trigger(struct rp2040_hwtimer *alarmclock,
struct alarmclock_trigger *trigger,
uint64_t fire_at_ns,
void (*cb)(void *),
@@ -87,18 +90,13 @@ static bool rp2040_hwtimer_add_trigger(struct rp2040_hwtimer *alarmclock,
assert(fire_at_ns);
assert(cb);
- uint64_t now_us = timer_time_us_64(timer_hw);
- if (NS_TO_US_ROUNDUP(fire_at_ns) > now_us &&
- (NS_TO_US_ROUNDUP(fire_at_ns) - now_us) > UINT32_MAX)
- /* Too far in the future. */
- return true;
-
trigger->alarmclock = alarmclock;
trigger->fire_at_ns = fire_at_ns;
trigger->cb = cb;
trigger->cb_arg = cb_arg;
bool saved = cr_save_and_disable_interrupts();
+
struct alarmclock_trigger **dst = &alarmclock->queue;
while (*dst && fire_at_ns >= (*dst)->fire_at_ns)
dst = &(*dst)->next;
@@ -107,6 +105,7 @@ static bool rp2040_hwtimer_add_trigger(struct rp2040_hwtimer *alarmclock,
if (*dst)
(*dst)->prev = trigger;
*dst = trigger;
+
if (!alarmclock->initialized) {
hw_set_bits(&timer_hw->inte, 1 << alarmclock->alarm_num);
irq_set_exclusive_handler(TIMER_ALARM_IRQ_NUM(timer_hw, alarmclock->alarm_num),
@@ -114,6 +113,7 @@ static bool rp2040_hwtimer_add_trigger(struct rp2040_hwtimer *alarmclock,
irq_set_enabled(TIMER_ALARM_IRQ_NUM(timer_hw, alarmclock->alarm_num), true);
alarmclock->initialized = true;
}
+
if (alarmclock->queue == trigger) {
/* "Force" the interrupt handler to trigger as soon as
* we enable interrupts. This handles the case of
@@ -126,9 +126,8 @@ static bool rp2040_hwtimer_add_trigger(struct rp2040_hwtimer *alarmclock,
* fire. */
hw_set_bits(&timer_hw->intf, 1 << alarmclock->alarm_num);
}
- cr_restore_interrupts(saved);
- return false;
+ cr_restore_interrupts(saved);
}
static void rp2040_hwtimer_del_trigger(struct rp2040_hwtimer *alarmclock,
diff --git a/libhw_cr/rp2040_include/libhw/rp2040_hwspi.h b/libhw_cr/rp2040_include/libhw/rp2040_hwspi.h
index 9d99f7b..8d4effa 100644
--- a/libhw_cr/rp2040_include/libhw/rp2040_hwspi.h
+++ b/libhw_cr/rp2040_include/libhw/rp2040_hwspi.h
@@ -20,7 +20,7 @@ enum rp2040_hwspi_instance {
};
struct rp2040_hwspi {
- BEGIN_PRIVATE(LIBHW_RP2040_HWSPI_H)
+ BEGIN_PRIVATE(LIBHW_RP2040_HWSPI_H);
/* const */
LM_IF(IS_IMPLEMENTATION_FOR(LIBHW_RP2040_HWSPI_H))(spi_inst_t)(void) *inst;
uint64_t min_delay_ns;
@@ -34,10 +34,10 @@ struct rp2040_hwspi {
/* mutable */
uint64_t dead_until_ns;
cr_sema_t sema;
- END_PRIVATE(LIBHW_RP2040_HWSPI_H)
+ END_PRIVATE(LIBHW_RP2040_HWSPI_H);
};
-LO_IMPLEMENTATION_H(io_duplex_readwriter, struct rp2040_hwspi, rp2040_hwspi)
-LO_IMPLEMENTATION_H(spi, struct rp2040_hwspi, rp2040_hwspi)
+LO_IMPLEMENTATION_H(io_duplex_readwriter, struct rp2040_hwspi, rp2040_hwspi);
+LO_IMPLEMENTATION_H(spi, struct rp2040_hwspi, rp2040_hwspi);
/**
* Initialize an instance of `struct rp2040_hwspi`.
@@ -99,7 +99,7 @@ LO_IMPLEMENTATION_H(spi, struct rp2040_hwspi, rp2040_hwspi)
min_delay_ns, bogus_data, \
pin_miso, pin_mosi, pin_clk, pin_cs, \
dma1, dma2, dma3, dma4); \
- } while(0)
+ } while (0)
void _rp2040_hwspi_init(struct rp2040_hwspi *self,
enum rp2040_hwspi_instance inst_num,
enum spi_mode mode,
diff --git a/libhw_cr/rp2040_include/libhw/w5500.h b/libhw_cr/rp2040_include/libhw/w5500.h
index 51effba..43c58a3 100644
--- a/libhw_cr/rp2040_include/libhw/w5500.h
+++ b/libhw_cr/rp2040_include/libhw/w5500.h
@@ -17,10 +17,10 @@
#include <libhw/generic/net.h>
#include <libhw/generic/spi.h>
-CR_CHAN_DECLARE(_w5500_sockintr_ch, uint8_t)
+CR_CHAN_DECLARE(_w5500_sockintr_ch, uint8_t);
struct _w5500_socket {
- BEGIN_PRIVATE(LIBHW_W5500_H)
+ BEGIN_PRIVATE(LIBHW_W5500_H);
/* const-after-init */
uint8_t socknum;
@@ -38,24 +38,11 @@ struct _w5500_socket {
_w5500_sockintr_ch_t write_ch; /* MODE_{TCP,UDP} */
bool list_open, read_open, write_open; /* MODE_TCP */
- END_PRIVATE(LIBHW_W5500_H)
+ END_PRIVATE(LIBHW_W5500_H);
};
-LO_IMPLEMENTATION_H(io_closer, struct _w5500_socket, w5500_tcplist)
-LO_IMPLEMENTATION_H(net_stream_listener, struct _w5500_socket, w5500_tcplist)
-
-LO_IMPLEMENTATION_H(io_reader, struct _w5500_socket, w5500_tcp)
-LO_IMPLEMENTATION_H(io_writer, struct _w5500_socket, w5500_tcp)
-LO_IMPLEMENTATION_H(io_readwriter, struct _w5500_socket, w5500_tcp)
-LO_IMPLEMENTATION_H(io_closer, struct _w5500_socket, w5500_tcp)
-LO_IMPLEMENTATION_H(io_bidi_closer, struct _w5500_socket, w5500_tcp)
-LO_IMPLEMENTATION_H(net_stream_conn, struct _w5500_socket, w5500_tcp)
-
-LO_IMPLEMENTATION_H(io_closer, struct _w5500_socket, w5500_udp)
-LO_IMPLEMENTATION_H(net_packet_conn, struct _w5500_socket, w5500_udp)
-
struct w5500 {
- BEGIN_PRIVATE(LIBHW_W5500_H)
+ BEGIN_PRIVATE(LIBHW_W5500_H);
/* const-after-init */
lo_interface spi spidev;
uint pin_intr;
@@ -68,9 +55,9 @@ struct w5500 {
struct _w5500_socket *free;
cr_sema_t intr;
cr_mutex_t mu;
- END_PRIVATE(LIBHW_W5500_H)
+ END_PRIVATE(LIBHW_W5500_H);
};
-LO_IMPLEMENTATION_H(net_iface, struct w5500, w5500_if)
+LO_IMPLEMENTATION_H(net_iface, struct w5500, w5500_if);
/**
* Initialize a WIZnet W5500 Ethernet-and-TCP/IP-offload chip.
diff --git a/libhw_cr/w5500.c b/libhw_cr/w5500.c
index 295add2..dc11ef9 100644
--- a/libhw_cr/w5500.c
+++ b/libhw_cr/w5500.c
@@ -8,6 +8,7 @@
* https://github.com/Wiznet/ioLibrary_Driver/blob/b981401e7f3d07015619adf44c13998e13e777f9/Ethernet/W5500/w5500.h
* https://github.com/Wiznet/ioLibrary_Driver/blob/b981401e7f3d07015619adf44c13998e13e777f9/Ethernet/W5500/w5500.c
* https://github.com/Wiznet/ioLibrary_Driver/blob/b981401e7f3d07015619adf44c13998e13e777f9/Ethernet/socket.c
+ * https://github.com/Wiznet/ioLibrary_Driver/blob/b981401e7f3d07015619adf44c13998e13e777f9/Internet/DHCP/dhcp.c
*
* Copyright (c) 2013, WIZnet Co., LTD.
* All rights reserved.
@@ -67,19 +68,19 @@
* SPDX-License-Identifier: MIT
*/
-#include <inttypes.h> /* for PRIu{n} */
+#include <string.h> /* for memcmp() */
/* TODO: Write a <libhw/generic/gpio.h> to avoid w5500.c being
* pico-sdk-specific. */
-#include <hardware/gpio.h> /* pico-sdk:hardware_gpio */
#include "rp2040_gpioirq.h"
+#include <hardware/gpio.h> /* pico-sdk:hardware_gpio */
#include <libcr/coroutine.h> /* for cr_yield() */
#include <libhw/generic/alarmclock.h> /* for sleep_*() */
#define LOG_NAME W5500
-#include <libmisc/log.h> /* for errorf(), debugf(), const_byte_str() */
+#include <libmisc/log.h>
#define IMPLEMENTATION_FOR_LIBHW_W5500_H YES
#include <libhw/w5500.h>
@@ -124,22 +125,22 @@ static const char *w5500_state_str(uint8_t state) {
}
}
-/* libobj *********************************************************************/
+/* libmisc/obj.h **************************************************************/
-LO_IMPLEMENTATION_C(io_closer, struct _w5500_socket, w5500_tcplist, static)
-LO_IMPLEMENTATION_C(net_stream_listener, struct _w5500_socket, w5500_tcplist, static)
+LO_IMPLEMENTATION_STATIC(io_closer, struct _w5500_socket, w5500_tcplist);
+LO_IMPLEMENTATION_STATIC(net_stream_listener, struct _w5500_socket, w5500_tcplist);
-LO_IMPLEMENTATION_C(io_reader, struct _w5500_socket, w5500_tcp, static)
-LO_IMPLEMENTATION_C(io_writer, struct _w5500_socket, w5500_tcp, static)
-LO_IMPLEMENTATION_C(io_readwriter, struct _w5500_socket, w5500_tcp, static)
-LO_IMPLEMENTATION_C(io_closer, struct _w5500_socket, w5500_tcp, static)
-LO_IMPLEMENTATION_C(io_bidi_closer, struct _w5500_socket, w5500_tcp, static)
-LO_IMPLEMENTATION_C(net_stream_conn, struct _w5500_socket, w5500_tcp, static)
+LO_IMPLEMENTATION_STATIC(io_reader, struct _w5500_socket, w5500_tcp);
+LO_IMPLEMENTATION_STATIC(io_writer, struct _w5500_socket, w5500_tcp);
+LO_IMPLEMENTATION_STATIC(io_readwriter, struct _w5500_socket, w5500_tcp);
+LO_IMPLEMENTATION_STATIC(io_closer, struct _w5500_socket, w5500_tcp);
+LO_IMPLEMENTATION_STATIC(io_bidi_closer, struct _w5500_socket, w5500_tcp);
+LO_IMPLEMENTATION_STATIC(net_stream_conn, struct _w5500_socket, w5500_tcp);
-LO_IMPLEMENTATION_C(io_closer, struct _w5500_socket, w5500_udp, static)
-LO_IMPLEMENTATION_C(net_packet_conn, struct _w5500_socket, w5500_udp, static)
+LO_IMPLEMENTATION_STATIC(io_closer, struct _w5500_socket, w5500_udp);
+LO_IMPLEMENTATION_STATIC(net_packet_conn, struct _w5500_socket, w5500_udp);
-LO_IMPLEMENTATION_C(net_iface, struct w5500, w5500_if, static)
+LO_IMPLEMENTATION_C(net_iface, struct w5500, w5500_if);
/* mid-level utilities ********************************************************/
@@ -187,7 +188,7 @@ static COROUTINE w5500_irq_cr(void *_chip) {
bool had_intr = false;
uint8_t chipintr = w5500ll_read_common_reg(chip->spidev, chip_interrupt);
- n_debugf(W5500_LL, "w5500_irq_cr(): chipintr=%"PRIu8, chipintr);
+ log_n_debugln(W5500_LL, "w5500_irq_cr(): chipintr=", chipintr);
had_intr = had_intr || (chipintr != 0);
if (chipintr)
w5500ll_write_common_reg(chip->spidev, chip_interrupt, 0xFF);
@@ -196,7 +197,7 @@ static COROUTINE w5500_irq_cr(void *_chip) {
struct _w5500_socket *socket = &chip->sockets[socknum];
uint8_t sockintr = w5500ll_read_sock_reg(chip->spidev, socknum, interrupt);
- n_debugf(W5500_LL, "w5500_irq_cr(): sockintr[%"PRIu8"]=%"PRIu8, socknum, sockintr);
+ log_n_debugln(W5500_LL, "w5500_irq_cr(): sockintr[", socknum, "]=", sockintr);
had_intr = had_intr || (sockintr != 0);
switch (socket->mode) {
@@ -208,16 +209,16 @@ static COROUTINE w5500_irq_cr(void *_chip) {
recv_bits = sockintr & (SOCKINTR_RECV_DAT|SOCKINTR_RECV_FIN);
if (listen_bits) {
- debugf("w5500_irq_cr(): signal sock[%"PRIu8"]->listen_sema", socknum);
+ log_debugln("w5500_irq_cr(): signal sock[", socknum, "]->listen_sema");
cr_sema_signal(&socket->listen_sema);
}
if (recv_bits) {
- debugf("w5500_irq_cr(): signal sock[%"PRIu8"]->read_sema", socknum);
+ log_debugln("w5500_irq_cr(): signal sock[", socknum, "]->read_sema");
cr_sema_signal(&socket->read_sema);
}
if (send_bits) {
- debugf("w5500_irq_cr(): signal sock[%"PRIu8"]->write_ch", socknum);
- _w5500_sockintr_ch_send(&socket->write_ch, send_bits);
+ log_debugln("w5500_irq_cr(): signal sock[", socknum, "]->write_ch");
+ cr_chan_send(&socket->write_ch, send_bits);
}
break;
}
@@ -228,7 +229,7 @@ static COROUTINE w5500_irq_cr(void *_chip) {
cr_mutex_unlock(&chip->mu);
if (!had_intr && gpio_get(chip->pin_intr)) {
- debugf("w5500_irq_cr(): looks like all interrupts have been processed, sleeping...");
+ log_debugln("w5500_irq_cr(): looks like all interrupts have been processed, sleeping...");
cr_sema_wait(&chip->intr);
} else
cr_yield();
@@ -283,7 +284,7 @@ static inline void w5500_socket_close(struct _w5500_socket *socket) {
static void w5500_intrhandler(void *_chip, uint LM_UNUSED(gpio), enum gpio_irq_level LM_UNUSED(event)) {
struct w5500 *chip = _chip;
- debugf("w5500_intrhandler()");
+ log_debugln("w5500_intrhandler()");
cr_sema_signal_from_intrhandler(&chip->intr);
}
@@ -322,7 +323,7 @@ void _w5500_init(struct w5500 *chip,
w5500ll_write_sock_reg(chip->spidev, 0, mode, a);
uint8_t b = w5500ll_read_sock_reg(chip->spidev, 0, mode);
if (b != a) {
- errorf("SPI to W5500 does not appear to be functional: wrote:0x%02"PRIx16" != read:0x%02"PRIx8, a, b);
+ log_errorln("SPI to W5500 does not appear to be functional: wrote:", (base16_u8_, a), " != read:", (base16_u8_, b));
spi_ok = false;
}
}
@@ -337,7 +338,8 @@ void _w5500_init(struct w5500 *chip,
w5500_hard_reset(chip);
/* Finally, wire in the interrupt handler. */
- coroutine_add("w5500_irq", w5500_irq_cr, chip);
+ cid_t cid = coroutine_add("w5500_irq", w5500_irq_cr, chip);
+ assert(cid);
}
/* chip methods ***************************************************************/
@@ -408,7 +410,7 @@ void w5500_soft_reset(struct w5500 *chip) {
cr_mutex_unlock(&chip->mu);
}
-static struct net_eth_addr w5500_if_hwaddr(struct w5500 *chip) {
+struct net_eth_addr w5500_if_hwaddr(struct w5500 *chip) {
assert(chip);
return chip->hwaddr;
@@ -426,28 +428,28 @@ static void _w5500_if_up(struct w5500 *chip, struct net_iface_config cfg) {
cr_mutex_unlock(&chip->mu);
}
-static void w5500_if_ifup(struct w5500 *chip, struct net_iface_config cfg) {
- debugf("if_up()");
- debugf(":: addr = "PRI_net_ip4_addr, ARG_net_ip4_addr(cfg.addr));
- debugf(":: gateway_addr = "PRI_net_ip4_addr, ARG_net_ip4_addr(cfg.gateway_addr));
- debugf(":: subnet_mask = "PRI_net_ip4_addr, ARG_net_ip4_addr(cfg.subnet_mask));
+void w5500_if_ifup(struct w5500 *chip, struct net_iface_config cfg) {
+ log_debugln("if_up()");
+ log_debugln(":: addr = ", (net_ip4_addr, cfg.addr));
+ log_debugln(":: gateway_addr = ", (net_ip4_addr, cfg.gateway_addr));
+ log_debugln(":: subnet_mask = ", (net_ip4_addr, cfg.subnet_mask));
_w5500_if_up(chip, cfg);
}
-static void w5500_if_ifdown(struct w5500 *chip) {
- debugf("if_down()");
- _w5500_if_up(chip, (struct net_iface_config){0});
+void w5500_if_ifdown(struct w5500 *chip) {
+ log_debugln("if_down()");
+ _w5500_if_up(chip, (struct net_iface_config){});
}
-static lo_interface net_stream_listener w5500_if_tcp_listen(struct w5500 *chip, uint16_t local_port) {
+lo_interface net_stream_listener w5500_if_tcp_listen(struct w5500 *chip, uint16_t local_port) {
assert(chip);
struct _w5500_socket *sock = w5500_alloc_socket(chip);
if (!sock) {
- debugf("tcp_listen() => no sock");
+ log_debugln("tcp_listen() => no sock");
return LO_NULL(net_stream_listener);
}
- debugf("tcp_listen() => sock[%"PRIu8"]", sock->socknum);
+ log_debugln("tcp_listen() => sock[", sock->socknum, "]");
if (!local_port)
local_port = w5500_alloc_local_port(chip);
@@ -458,11 +460,11 @@ static lo_interface net_stream_listener w5500_if_tcp_listen(struct w5500 *chip,
sock->read_deadline_ns = 0;
sock->list_open = true;
- return lo_box_w5500_tcplist_as_net_stream_listener(sock);
+ return LO_BOX(net_stream_listener, sock);
}
-static lo_interface net_stream_conn w5500_if_tcp_dial(struct w5500 *chip,
- struct net_ip4_addr node, uint16_t port) {
+net_stream_conn_or_error w5500_if_tcp_dial(struct w5500 *chip,
+ struct net_ip4_addr node, uint16_t port) {
assert(chip);
assert(memcmp(node.octets, net_ip4_addr_zero.octets, 4));
assert(memcmp(node.octets, net_ip4_addr_broadcast.octets, 4));
@@ -470,11 +472,11 @@ static lo_interface net_stream_conn w5500_if_tcp_dial(struct w5500 *chip,
struct _w5500_socket *socket = w5500_alloc_socket(chip);
if (!socket) {
- debugf("tcp_dial() => no sock");
- return LO_NULL(net_stream_conn);
+ log_debugln("tcp_dial() => no sock");
+ return ERROR_NEW_ERR(net_stream_conn, error_new(E_POSIX_ENOTSOCK));
}
uint8_t socknum = socket->socknum;
- debugf("tcp_dial() => sock[%"PRIu8"]", socknum);
+ log_debugln("tcp_dial() => sock[", socknum, "]");
uint16_t local_port = w5500_alloc_local_port(chip);
@@ -502,29 +504,29 @@ static lo_interface net_stream_conn w5500_if_tcp_dial(struct w5500 *chip,
cr_mutex_unlock(&chip->mu);
for (;;) {
uint8_t state = w5500ll_read_sock_reg(chip->spidev, socknum, state);
- debugf("tcp_dial(): state=%s", w5500_state_str(state));
+ log_debugln("tcp_dial(): state=", w5500_state_str(state));
switch (state) {
case STATE_TCP_SYNSENT:
cr_yield();
break;
case STATE_TCP_ESTABLISHED:
- return lo_box_w5500_tcp_as_net_stream_conn(socket);
+ return ERROR_NEW_VAL(net_stream_conn, LO_BOX(net_stream_conn, socket));
default:
goto restart;
}
}
}
-static lo_interface net_packet_conn w5500_if_udp_conn(struct w5500 *chip, uint16_t local_port) {
+lo_interface net_packet_conn w5500_if_udp_conn(struct w5500 *chip, uint16_t local_port) {
assert(chip);
struct _w5500_socket *socket = w5500_alloc_socket(chip);
if (!socket) {
- debugf("udp_conn() => no sock");
+ log_debugln("udp_conn() => no sock");
return LO_NULL(net_packet_conn);
}
uint8_t socknum = socket->socknum;
- debugf("udp_conn() => sock[%"PRIu8"]", socknum);
+ log_debugln("udp_conn() => sock[", socknum, "]");
if (!local_port)
local_port = w5500_alloc_local_port(chip);
@@ -544,18 +546,34 @@ static lo_interface net_packet_conn w5500_if_udp_conn(struct w5500 *chip, uint16
cr_yield();
cr_mutex_unlock(&chip->mu);
- return lo_box_w5500_udp_as_net_packet_conn(socket);
+ return LO_BOX(net_packet_conn, socket);
+}
+
+bool w5500_if_arp_ping(struct w5500 *chip, struct net_ip4_addr addr) {
+ /* FIXME: This arp_ping implementation is really bad (and
+ * assumes that a UDP socket is open, which is "safe" because
+ * I only use it from inside of a DHCP client). */
+ assert(chip);
+ struct _w5500_socket *sock = NULL;
+ for (size_t i = 0; i < LM_ARRAY_LEN(chip->sockets) && !sock; i++) {
+ if (chip->sockets[i].mode == W5500_MODE_UDP)
+ sock = &chip->sockets[i];
+ }
+ assert(sock);
+ error err = w5500_udp_sendto(sock, "BOGUS_PACKET_TO_TRIGGER_AN_ARP", 17, addr, 5000);
+ log_debugln("arp_ping => ", (error, err));
+ return err.num != E_NET_EARP_TIMEOUT;
}
/* tcp_listener methods *******************************************************/
-static lo_interface net_stream_conn w5500_tcplist_accept(struct _w5500_socket *socket) {
+static net_stream_conn_or_error w5500_tcplist_accept(struct _w5500_socket *socket) {
ASSERT_SELF(stream_listener, TCP);
restart:
if (!socket->list_open) {
- debugf("tcp_listener.accept() => already closed");
- return LO_NULL(net_stream_conn);
+ log_debugln("tcp_listener.accept() => already closed");
+ return ERROR_NEW_ERR(net_stream_conn, error_new(E_NET_ECLOSED));
}
cr_mutex_lock(&chip->mu);
@@ -573,7 +591,7 @@ static lo_interface net_stream_conn w5500_tcplist_accept(struct _w5500_socket *s
cr_mutex_unlock(&chip->mu);
for (;;) {
uint8_t state = w5500ll_read_sock_reg(chip->spidev, socknum, state);
- debugf("tcp_listener.accept() => state=%s", w5500_state_str(state));
+ log_debugln("tcp_listener.accept() => state=", w5500_state_str(state));
switch (state) {
case STATE_TCP_LISTEN:
case STATE_TCP_SYNRECV:
@@ -581,37 +599,36 @@ static lo_interface net_stream_conn w5500_tcplist_accept(struct _w5500_socket *s
break;
case STATE_TCP_ESTABLISHED:
socket->read_open = true;
- /* fall-through */
+ [[fallthrough]];
case STATE_TCP_CLOSE_WAIT:
socket->write_open = true;
- return lo_box_w5500_tcp_as_net_stream_conn(socket);
+ return ERROR_NEW_VAL(net_stream_conn, LO_BOX(net_stream_conn, socket));
default:
goto restart;
}
}
}
-static int w5500_tcplist_close(struct _w5500_socket *socket) {
- debugf("tcp_listener.close()");
+static error w5500_tcplist_close(struct _w5500_socket *socket) {
+ log_debugln("tcp_listener.close()");
ASSERT_SELF(stream_listener, TCP);
socket->list_open = false;
w5500_tcp_maybe_free(chip, socket);
- return 0;
+ return ERROR_NULL;
}
/* tcp_conn methods ***********************************************************/
-static ssize_t w5500_tcp_writev(struct _w5500_socket *socket, const struct iovec *iov, int iovcnt) {
+static size_t_and_error w5500_tcp_writev(struct _w5500_socket *socket, const struct wr_iovec *iov, int iovcnt) {
assert(iov);
assert(iovcnt > 0);
size_t count = 0;
for (int i = 0; i < iovcnt; i++)
count += iov[i].iov_len;
- debugf("tcp_conn.write(%zu)", count);
+ assert(count);
+ log_debugln("tcp_conn.write(", count, ")");
ASSERT_SELF(stream_conn, TCP);
- if (count == 0)
- return 0;
/* What we really want is to pause until we receive an ACK for
* some data we just queued, so that we can line up some new
@@ -636,15 +653,15 @@ static ssize_t w5500_tcp_writev(struct _w5500_socket *socket, const struct iovec
size_t done = 0;
while (done < count) {
if (!socket->write_open) {
- debugf(" => soft closed");
- return -NET_ECLOSED;
+ log_debugln(" => soft closed");
+ return ERROR_AND(size_t, done, error_new(E_NET_ECLOSED));
}
cr_mutex_lock(&chip->mu);
uint8_t state = w5500ll_read_sock_reg(chip->spidev, socknum, state);
if (state != STATE_TCP_ESTABLISHED && state != STATE_TCP_CLOSE_WAIT) {
cr_mutex_unlock(&chip->mu);
- debugf(" => hard closed");
- return -NET_ECLOSED;
+ log_debugln(" => hard closed");
+ return ERROR_AND(size_t, done, error_new(E_NET_ECLOSED));
}
uint16_t freesize = uint16be_unmarshal(w5500ll_read_sock_reg(chip->spidev, socknum, tx_free_size));
@@ -666,26 +683,26 @@ static ssize_t w5500_tcp_writev(struct _w5500_socket *socket, const struct iovec
w5500_socket_cmd(socket, CMD_SEND);
cr_mutex_unlock(&chip->mu);
- switch (_w5500_sockintr_ch_recv(&socket->write_ch)) {
+ switch (cr_chan_recv(&socket->write_ch)) {
case SOCKINTR_SEND_OK:
- debugf(" => sent %zu", freesize);
+ log_debugln(" => sent ", freesize);
done += freesize;
break;
case SOCKINTR_SEND_TIMEOUT:
- debugf(" => ACK timeout");
- return -NET_EACK_TIMEOUT;
+ log_debugln(" => ACK timeout");
+ return ERROR_AND(size_t, done, error_new(E_NET_EACK_TIMEOUT));
case SOCKINTR_SEND_OK|SOCKINTR_SEND_TIMEOUT:
assert_notreached("send both OK and timed out?");
default:
assert_notreached("invalid write_ch bits");
}
}
- debugf(" => send finished");
- return done;
+ log_debugln(" => send finished");
+ return ERROR_AND(size_t, done, ERROR_NULL);
}
static void w5500_tcp_set_read_deadline(struct _w5500_socket *socket, uint64_t ns) {
- debugf("tcp_conn.set_read_deadline(%"PRIu64")", ns);
+ log_debugln("tcp_conn.set_read_deadline(", ns, ")");
ASSERT_SELF(stream_conn, TCP);
socket->read_deadline_ns = ns;
}
@@ -695,18 +712,17 @@ static void w5500_tcp_alarm_handler(void *_arg) {
cr_sema_signal_from_intrhandler(&socket->read_sema);
}
-static ssize_t w5500_tcp_readv(struct _w5500_socket *socket, const struct iovec *iov, int iovcnt) {
+static size_t_or_error w5500_tcp_readv(struct _w5500_socket *socket, const struct rd_iovec *iov, int iovcnt) {
assert(iov);
assert(iovcnt > 0);
size_t count = 0;
for (int i = 0; i < iovcnt; i++)
count += iov[i].iov_len;
- debugf("tcp_conn.read(%zu)", count);
+ assert(count);
+ log_debugln("tcp_conn.read(", count, ")");
ASSERT_SELF(stream_conn, TCP);
- if (count == 0)
- return 0;
- struct alarmclock_trigger trigger = {0};
+ struct alarmclock_trigger trigger = {};
if (socket->read_deadline_ns)
LO_CALL(bootclock, add_trigger, &trigger,
socket->read_deadline_ns,
@@ -718,13 +734,13 @@ static ssize_t w5500_tcp_readv(struct _w5500_socket *socket, const struct iovec
for (;;) {
if (!socket->read_open) {
LO_CALL(bootclock, del_trigger, &trigger);
- debugf(" => soft closed");
- return -NET_ECLOSED;
+ log_debugln(" => soft closed");
+ return ERROR_NEW_ERR(size_t, error_new(E_NET_ECLOSED));
}
if (socket->read_deadline_ns && socket->read_deadline_ns <= LO_CALL(bootclock, get_time_ns)) {
LO_CALL(bootclock, del_trigger, &trigger);
- debugf(" => recv timeout");
- return -NET_ERECV_TIMEOUT;
+ log_debugln(" => recv timeout");
+ return ERROR_NEW_ERR(size_t, error_new(E_NET_ERECV_TIMEOUT));
}
cr_mutex_lock(&chip->mu);
uint8_t state = w5500ll_read_sock_reg(chip->spidev, socknum, state);
@@ -736,8 +752,8 @@ static ssize_t w5500_tcp_readv(struct _w5500_socket *socket, const struct iovec
default:
LO_CALL(bootclock, del_trigger, &trigger);
cr_mutex_unlock(&chip->mu);
- debugf(" => hard closed");
- return -NET_ECLOSED;
+ log_debugln(" => hard closed");
+ return ERROR_NEW_ERR(size_t, error_new(E_NET_ECLOSED));
}
avail = uint16be_unmarshal(w5500ll_read_sock_reg(chip->spidev, socknum, rx_size));
@@ -747,15 +763,15 @@ static ssize_t w5500_tcp_readv(struct _w5500_socket *socket, const struct iovec
if (state == STATE_TCP_CLOSE_WAIT) {
LO_CALL(bootclock, del_trigger, &trigger);
cr_mutex_unlock(&chip->mu);
- debugf(" => EOF");
- return 0;
+ log_debugln(" => EOF");
+ return ERROR_NEW_ERR(size_t, error_new(E_EOF));
}
cr_mutex_unlock(&chip->mu);
cr_sema_wait(&socket->read_sema);
}
assert(avail);
- debugf(" => received %"PRIu16" bytes", avail);
+ log_debugln(" => received ", avail, " bytes");
uint16_t ptr = uint16be_unmarshal(w5500ll_read_sock_reg(chip->spidev, socknum, rx_read_pointer));
/* Read the data. */
if ((size_t)avail > count)
@@ -767,11 +783,11 @@ static ssize_t w5500_tcp_readv(struct _w5500_socket *socket, const struct iovec
/* Return. */
LO_CALL(bootclock, del_trigger, &trigger);
cr_mutex_unlock(&chip->mu);
- return avail;
+ return ERROR_NEW_VAL(size_t, avail);
}
-static int w5500_tcp_close_inner(struct _w5500_socket *socket, bool rd, bool wr) {
- debugf("tcp_conn.close(rd=%s, wr=%s)", rd ? "true" : "false", wr ? "true" : "false");
+static error w5500_tcp_close_inner(struct _w5500_socket *socket, bool rd, bool wr) {
+ log_debugln("tcp_conn.close(rd=", rd, ", wr=", wr, ")");
ASSERT_SELF(stream_conn, TCP);
if (rd)
@@ -798,26 +814,26 @@ static int w5500_tcp_close_inner(struct _w5500_socket *socket, bool rd, bool wr)
}
w5500_tcp_maybe_free(chip, socket);
- return 0;
+ return ERROR_NULL;
}
-static int w5500_tcp_close(struct _w5500_socket *socket) { return w5500_tcp_close_inner(socket, true, true); }
-static int w5500_tcp_close_read(struct _w5500_socket *socket) { return w5500_tcp_close_inner(socket, true, false); }
-static int w5500_tcp_close_write(struct _w5500_socket *socket) { return w5500_tcp_close_inner(socket, false, true); }
+static error w5500_tcp_close(struct _w5500_socket *socket) { return w5500_tcp_close_inner(socket, true, true); }
+static error w5500_tcp_close_read(struct _w5500_socket *socket) { return w5500_tcp_close_inner(socket, true, false); }
+static error w5500_tcp_close_write(struct _w5500_socket *socket) { return w5500_tcp_close_inner(socket, false, true); }
/* udp_conn methods ***********************************************************/
-static ssize_t w5500_udp_sendto(struct _w5500_socket *socket, void *buf, size_t count,
- struct net_ip4_addr node, uint16_t port) {
- debugf("udp_conn.sendto()");
+static error w5500_udp_sendto(struct _w5500_socket *socket, void *buf, size_t count,
+ struct net_ip4_addr node, uint16_t port) {
+ log_debugln("udp_conn.sendto()");
ASSERT_SELF(packet_conn, UDP);
assert(buf);
assert(count);
uint16_t bufsize = ((uint16_t)w5500ll_read_sock_reg(chip->spidev, socknum, tx_buf_size))*1024;
if (count > bufsize) {
- debugf(" => msg too large");
- return -NET_EMSGSIZE;
+ log_debugln(" => msg too large");
+ return error_new(E_POSIX_EMSGSIZE);
}
for (;;) {
@@ -825,8 +841,8 @@ static ssize_t w5500_udp_sendto(struct _w5500_socket *socket, void *buf, size_t
uint8_t state = w5500ll_read_sock_reg(chip->spidev, socknum, state);
if (state != STATE_UDP) {
cr_mutex_unlock(&chip->mu);
- debugf(" => closed");
- return -NET_ECLOSED;
+ log_debugln(" => closed");
+ return error_new(E_NET_ECLOSED);
}
uint16_t freesize = uint16be_unmarshal(w5500ll_read_sock_reg(chip->spidev, socknum, tx_free_size));
@@ -844,8 +860,8 @@ static ssize_t w5500_udp_sendto(struct _w5500_socket *socket, void *buf, size_t
w5500ll_write_sock_reg(chip->spidev, socknum, remote_port, uint16be_marshal(port));
/* Queue data to be sent. */
uint16_t ptr = uint16be_unmarshal(w5500ll_read_sock_reg(chip->spidev, socknum, tx_write_pointer));
- w5500ll_writev(chip->spidev, ptr, CTL_BLOCK_SOCK(socknum, TX), &((struct iovec){
- .iov_base = buf,
+ w5500ll_writev(chip->spidev, ptr, CTL_BLOCK_SOCK(socknum, TX), &((struct wr_iovec){
+ .iov_write_from = buf,
.iov_len = count,
}), 1, 0, 0);
w5500ll_write_sock_reg(chip->spidev, socknum, tx_write_pointer, uint16be_marshal(ptr+count));
@@ -853,13 +869,13 @@ static ssize_t w5500_udp_sendto(struct _w5500_socket *socket, void *buf, size_t
w5500_socket_cmd(socket, CMD_SEND);
cr_mutex_unlock(&chip->mu);
- switch (_w5500_sockintr_ch_recv(&socket->write_ch)) {
+ switch (cr_chan_recv(&socket->write_ch)) {
case SOCKINTR_SEND_OK:
- debugf(" => sent");
- return count;
+ log_debugln(" => sent");
+ return ERROR_NULL;
case SOCKINTR_SEND_TIMEOUT:
- debugf(" => ARP timeout");
- return -NET_EARP_TIMEOUT;
+ log_debugln(" => ARP timeout");
+ return error_new(E_NET_EARP_TIMEOUT);
case SOCKINTR_SEND_OK|SOCKINTR_SEND_TIMEOUT:
assert_notreached("send both OK and timed out?");
default:
@@ -868,7 +884,7 @@ static ssize_t w5500_udp_sendto(struct _w5500_socket *socket, void *buf, size_t
}
static void w5500_udp_set_recv_deadline(struct _w5500_socket *socket, uint64_t ns) {
- debugf("udp_conn.set_recv_deadline(%"PRIu64")", ns);
+ log_debugln("udp_conn.set_recv_deadline(", ns, ")");
ASSERT_SELF(packet_conn, UDP);
socket->read_deadline_ns = ns;
}
@@ -878,14 +894,14 @@ static void w5500_udp_alarm_handler(void *_arg) {
cr_sema_signal_from_intrhandler(&socket->read_sema);
}
-static ssize_t w5500_udp_recvfrom(struct _w5500_socket *socket, void *buf, size_t count,
- struct net_ip4_addr *ret_node, uint16_t *ret_port) {
- debugf("udp_conn.recvfrom()");
+static size_t_or_error w5500_udp_recvfrom(struct _w5500_socket *socket, void *buf, size_t count,
+ struct net_ip4_addr *ret_node, uint16_t *ret_port) {
+ log_debugln("udp_conn.recvfrom()");
ASSERT_SELF(packet_conn, UDP);
assert(buf);
assert(count);
- struct alarmclock_trigger trigger = {0};
+ struct alarmclock_trigger trigger = {};
if (socket->read_deadline_ns)
LO_CALL(bootclock, add_trigger, &trigger,
socket->read_deadline_ns,
@@ -897,15 +913,15 @@ static ssize_t w5500_udp_recvfrom(struct _w5500_socket *socket, void *buf, size_
for (;;) {
if (socket->read_deadline_ns && socket->read_deadline_ns <= LO_CALL(bootclock, get_time_ns)) {
LO_CALL(bootclock, del_trigger, &trigger);
- debugf(" => recv timeout");
- return -NET_ERECV_TIMEOUT;
+ log_debugln(" => recv timeout");
+ return ERROR_NEW_ERR(size_t, error_new(E_NET_ERECV_TIMEOUT));
}
cr_mutex_lock(&chip->mu);
uint8_t state = w5500ll_read_sock_reg(chip->spidev, socknum, state);
if (state != STATE_UDP) {
LO_CALL(bootclock, del_trigger, &trigger);
- debugf(" => hard closed");
- return -NET_ECLOSED;
+ log_debugln(" => hard closed");
+ return ERROR_NEW_ERR(size_t, error_new(E_NET_ECLOSED));
}
avail = uint16be_unmarshal(w5500ll_read_sock_reg(chip->spidev, socknum, rx_size));
@@ -922,8 +938,8 @@ static ssize_t w5500_udp_recvfrom(struct _w5500_socket *socket, void *buf, size_
* can't find in the datasheet where it describes
* this; this is based off of socket.c:recvfrom(). */
uint8_t hdr[8];
- w5500ll_readv(chip->spidev, ptr, CTL_BLOCK_SOCK(socknum, RX), &((struct iovec){
- .iov_base = hdr,
+ w5500ll_readv(chip->spidev, ptr, CTL_BLOCK_SOCK(socknum, RX), &((struct rd_iovec){
+ .iov_read_to = hdr,
.iov_len = sizeof(hdr),
}), 1, 0);
if (ret_node) {
@@ -935,12 +951,12 @@ static ssize_t w5500_udp_recvfrom(struct _w5500_socket *socket, void *buf, size_
if (ret_port)
*ret_port = uint16be_decode(&hdr[4]);
uint16_t len = uint16be_decode(&hdr[6]);
- debugf(" => received %"PRIu16" bytes%s", len, len < avail-8 ? " (plus more messages)" : "");
+ log_debugln(" => received ", len, " bytes", len < avail-8 ? " (plus more messages)" : "");
/* Now read the actual data. */
if (count > len)
count = len;
- w5500ll_readv(chip->spidev, ptr+8, CTL_BLOCK_SOCK(socknum, RX), &((struct iovec){
- .iov_base = buf,
+ w5500ll_readv(chip->spidev, ptr+8, CTL_BLOCK_SOCK(socknum, RX), &((struct rd_iovec){
+ .iov_read_to = buf,
.iov_len = len,
}), 1, 0);
/* Tell the chip that we read the data. */
@@ -949,14 +965,14 @@ static ssize_t w5500_udp_recvfrom(struct _w5500_socket *socket, void *buf, size_
/* Return. */
LO_CALL(bootclock, del_trigger, &trigger);
cr_mutex_unlock(&chip->mu);
- return len;
+ return ERROR_NEW_VAL(size_t, len);
}
-static int w5500_udp_close(struct _w5500_socket *socket) {
- debugf("udp_conn.close()");
+static error w5500_udp_close(struct _w5500_socket *socket) {
+ log_debugln("udp_conn.close()");
ASSERT_SELF(packet_conn, UDP);
w5500_socket_close(socket);
w5500_free_socket(chip, socket);
- return 0;
+ return ERROR_NULL;
}
diff --git a/libhw_cr/w5500_ll.c b/libhw_cr/w5500_ll.c
new file mode 100644
index 0000000..44eea32
--- /dev/null
+++ b/libhw_cr/w5500_ll.c
@@ -0,0 +1,115 @@
+/* libhw_cr/w5500_ll.c - Low-level library for the WIZnet W5500 chip
+ *
+ * Based entirely on the W5500 datasheet (v1.1.0), not on reference code.
+ * https://docs.wiznet.io/img/products/w5500/W5500_ds_v110e.pdf
+ *
+ * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#include <string.h> /* for memcmp() and mempy() */
+
+#include <libmisc/alloc.h>
+#include <libmisc/fmt.h>
+
+#include "w5500_ll.h"
+
+#if CONFIG_W5500_LL_DEBUG
+static void fmt_print_ctl_block(lo_interface fmt_dest, uint8_t b) {
+ static char *strs[] = {
+ "RES",
+ "REG",
+ "TX",
+ "RX",
+ };
+ fmt_print("CTL_BLOCK_SOCK(", (base10, (((b)>>5) & 0b111)), ", ", strs[((b)>>3)&0b11], ")");
+}
+#endif
+
+void _w5500ll_n_writev(const char *func,
+ lo_interface spi spidev, uint16_t addr, uint8_t block,
+ const struct wr_iovec *iov, int iovcnt,
+ size_t skip, size_t max)
+{
+ assert(!LO_IS_NULL(spidev));
+ assert((block & ~CTL_MASK_BLOCK) == 0);
+ assert(iov);
+ assert(iovcnt > 0);
+#if CONFIG_W5500_LL_DEBUG
+ log_n_debugln(W5500_LL,
+ func, "(): w5500ll_write(spidev",
+ ", addr=", (base16_u16_, addr),
+ ", block=", (ctl_block, block),
+ ", iov",
+ ", iovcnt=", iovcnt,
+ ")");
+#endif
+
+ uint8_t header[] = {
+ (uint8_t)((addr >> 8) & 0xFF),
+ (uint8_t)(addr & 0xFF),
+ (block & CTL_MASK_BLOCK) | CTL_W | CTL_OM_VDM,
+ };
+ int inner_cnt = 1+io_wr_slice_cnt(iov, iovcnt, skip, max);
+ [[gnu::cleanup(heap_cleanup)]] struct duplex_iovec *inner = heap_alloc(inner_cnt, struct duplex_iovec);
+ inner[0] = (struct duplex_iovec){
+ .iov_read_to = IOVEC_DISCARD,
+ .iov_write_from = header,
+ .iov_len = sizeof(header),
+ };
+ io_slice_wr_to_duplex(&inner[1], iov, iovcnt, skip, max);
+ LO_CALL(spidev, readwritev, inner, inner_cnt);
+}
+
+
+void _w5500ll_n_readv(const char *func,
+ lo_interface spi spidev, uint16_t addr, uint8_t block,
+ const struct rd_iovec *iov, int iovcnt,
+ size_t max)
+{
+ assert(!LO_IS_NULL(spidev));
+ assert((block & ~CTL_MASK_BLOCK) == 0);
+ assert(iov);
+ assert(iovcnt > 0);
+#if CONFIG_W5500_LL_DEBUG
+ log_n_debugln(W5500_LL,
+ func, "(): w5500ll_read(spidev",
+ ", addr=", (base16_u16_, addr),
+ ", block=", (ctl_block, block),
+ ", iov",
+ ", iovcnt=", iovcnt,
+ ")");
+#endif
+
+ uint8_t header[] = {
+ (uint8_t)((addr >> 8) & 0xFF),
+ (uint8_t)(addr & 0xFF),
+ (block & CTL_MASK_BLOCK) | CTL_R | CTL_OM_VDM,
+ };
+ int inner_cnt = 1+io_rd_slice_cnt(iov, iovcnt, 0, max);
+ [[gnu::cleanup(heap_cleanup)]] struct duplex_iovec *inner = heap_alloc(inner_cnt, struct duplex_iovec);
+ inner[0] = (struct duplex_iovec){
+ .iov_read_to = IOVEC_DISCARD,
+ .iov_write_from = header,
+ .iov_len = sizeof(header),
+ };
+ io_slice_rd_to_duplex(&inner[1], iov, iovcnt, 0, max);
+ LO_CALL(spidev, readwritev, inner, inner_cnt);
+}
+
+void _w5500ll_n_read_repeated(const char *func,
+ lo_interface spi spidev, uint16_t addr, uint8_t block,
+ void *buf, void *buf_scratch, size_t len)
+{
+ _w5500ll_n_readv(func, spidev, addr, block,
+ &((struct rd_iovec){.iov_read_to=buf, .iov_len=len}), 1,
+ 0);
+ for (;;) {
+ _w5500ll_n_readv(func, spidev, addr, block,
+ &((struct rd_iovec){.iov_read_to=buf_scratch, .iov_len=len}), 1,
+ 0);
+ if (memcmp(buf, buf_scratch, len) == 0)
+ break;
+ memcpy(buf, buf_scratch, len);
+ }
+}
diff --git a/libhw_cr/w5500_ll.h b/libhw_cr/w5500_ll.h
index 2506cd2..e375ebd 100644
--- a/libhw_cr/w5500_ll.h
+++ b/libhw_cr/w5500_ll.h
@@ -1,6 +1,6 @@
-/* libhw_cr/w5500_ll.h - Low-level header library for the WIZnet W5500 chip
+/* libhw_cr/w5500_ll.h - Low-level library for the WIZnet W5500 chip
*
- * Based entirely on the W5500 datasheet, v1.1.0.
+ * Based entirely on the W5500 datasheet (v1.1.0), not on reference code.
* https://docs.wiznet.io/img/products/w5500/W5500_ds_v110e.pdf
*
* Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
@@ -10,17 +10,15 @@
#ifndef _LIBHW_CR_W5500_LL_H_
#define _LIBHW_CR_W5500_LL_H_
-#include <alloca.h> /* for alloca() */
#include <stdint.h> /* for uint{n}_t */
-#include <string.h> /* for memcmp() */
-#include <libmisc/assert.h> /* for assert(), static_assert() */
+#include <libmisc/assert.h> /* for static_assert() */
#include <libmisc/endian.h> /* for uint16be_t */
#include <libhw/generic/net.h> /* for struct net_eth_addr, struct net_ip4_addr */
#include <libhw/generic/spi.h> /* for lo_interface spi */
-/* Config *********************************************************************/
+/* Config. ***************************************************************************************/
#include "config.h"
@@ -28,7 +26,7 @@
#error config.h must define CONFIG_W5500_LL_DEBUG
#endif
-/* Low-level protocol built on SPI frames. ***********************************/
+/* Low-level protocol built on SPI frames. *******************************************************/
/* A u8 control byte has 3 parts: block-ID, R/W, and operating-mode. */
@@ -53,93 +51,81 @@
#define CTL_OM_FDM2 0b10 /* fixed-length data mode: 2 byte data length */
#define CTL_OM_FDM4 0b11 /* fixed-length data mode: 4 byte data length */
-#if CONFIG_W5500_LL_DEBUG
-static char *_ctl_block_part_strs[] = {
- "RES",
- "REG",
- "TX",
- "RX",
-};
-#define PRI_ctl_block "CTL_BLOCK_SOCK(%d, %s)"
-#define ARG_ctl_block(b) (((b)>>5) & 0b111), _ctl_block_part_strs[((b)>>3)&0b11]
-#endif
-
-/* Even though SPI is a full-duplex protocol, the W5500's spiframe on top of it is only half-duplex.
- * Lame. */
+/* Even though SPI is a full-duplex protocol, the W5500's spiframe on
+ * top of it is only half-duplex. Lame. */
-static inline void
#if CONFIG_W5500_LL_DEBUG
-#define w5500ll_writev(...) _w5500ll_writev(__func__, __VA_ARGS__)
-_w5500ll_writev(const char *func,
+ #define w5500ll_writev(...) _w5500ll_n_writev (__func__, __VA_ARGS__)
+ #define w5500ll_readv(...) _w5500ll_n_readv (__func__, __VA_ARGS__)
#else
-w5500ll_writev(
-#endif
- lo_interface spi spidev, uint16_t addr, uint8_t block,
- const struct iovec *iov, int iovcnt,
- size_t skip, size_t max)
-{
- assert(!LO_IS_NULL(spidev));
- assert((block & ~CTL_MASK_BLOCK) == 0);
- assert(iov);
- assert(iovcnt > 0);
-#if CONFIG_W5500_LL_DEBUG
- n_debugf(W5500_LL,
- "%s(): w5500ll_write(spidev, addr=%#04x, block="PRI_ctl_block", iov, iovcnt=%d)",
- func, addr, ARG_ctl_block(block), iovcnt);
+ #define _w5500ll_n_writev(func, ...) w5500ll_writev (__VA_ARGS__)
+ #define _w5500ll_n_readv(func, ...) w5500ll_readv (__VA_ARGS__)
#endif
- uint8_t header[] = {
- (uint8_t)((addr >> 8) & 0xFF),
- (uint8_t)(addr & 0xFF),
- (block & CTL_MASK_BLOCK) | CTL_W | CTL_OM_VDM,
- };
- int inner_cnt = 1+io_slice_cnt(iov, iovcnt, skip, max);
- struct duplex_iovec *inner = alloca(sizeof(struct duplex_iovec)*inner_cnt);
- inner[0] = (struct duplex_iovec){
- .iov_read_to = IOVEC_DISCARD,
- .iov_write_from = header,
- .iov_len = sizeof(header),
- };
- io_slice_wr_to_duplex(&inner[1], iov, iovcnt, skip, max);
- LO_CALL(spidev, readwritev, inner, inner_cnt);
-}
-
-static inline void
+/* `skip` and `max` correspond to the <libhw/generic/io.h> `slice`
+ * functions' `byte_start` and `max_byte_cnt` arguments for working on
+ * just a subset of the iovec list. */
+
+void _w5500ll_n_writev(const char *func,
+ lo_interface spi spidev, uint16_t addr, uint8_t block,
+ const struct wr_iovec *iov, int iovcnt,
+ size_t skip, size_t max);
+void _w5500ll_n_readv(const char *func,
+ lo_interface spi spidev, uint16_t addr, uint8_t block,
+ const struct rd_iovec *iov, int iovcnt,
+ size_t max);
+
+/* Operate on registers. *************************************************************************/
+
+/*
+ * Given a `blocktyp` that is a struct describing the layout of
+ * registers in a block (e.g. `blocktyp = struct
+ * w5500_ll_block_common_reg`), read or write the `field` register
+ * (where `field` must be a field in that struct).
+ */
+
+#define w5500ll_write_reg(spidev, blockid, blocktyp, field, val) do { \
+ typeof((blocktyp){}.field) lval = val; \
+ w5500ll_writev(spidev, \
+ offsetof(blocktyp, field), blockid, \
+ &((struct wr_iovec){ \
+ .iov_write_from = &lval, \
+ .iov_len = sizeof(lval), \
+ }), 1, \
+ 0, 0); \
+ } while (0)
+
+/* The datasheet tells us that multi-byte reads are non-atomic and
+ * that "it is recommended that you read all 16-bits twice or more
+ * until getting the same value". */
#if CONFIG_W5500_LL_DEBUG
-#define w5500ll_readv(...) _w5500ll_read(__func__, __VA_ARGS__)
-_w5500ll_readv(const char *func,
+ #define _w5500ll_read_repeated(...) _w5500ll_n_read_repeated (__func__, __VA_ARGS__)
#else
-w5500ll_readv(
-#endif
- lo_interface spi spidev, uint16_t addr, uint8_t block,
- const struct iovec *iov, int iovcnt,
- size_t max)
-{
- assert(!LO_IS_NULL(spidev));
- assert((block & ~CTL_MASK_BLOCK) == 0);
- assert(iov);
- assert(iovcnt > 0);
-#if CONFIG_W5500_LL_DEBUG
- n_debugf(W5500_LL,
- "%s(): w5500ll_read(spidev, addr=%#04x, block="PRI_ctl_block", iov, iovcnt=%d)",
- func, addr, ARG_ctl_block(block), iovcnt);
+ #define _w5500ll_n_read_repeated(func, ...) _w5500ll_read_repeated (__VA_ARGS__)
#endif
+void _w5500ll_n_read_repeated(const char *func,
+ lo_interface spi spidev, uint16_t addr, uint8_t block,
+ void *buf, void *buf_scratch, size_t len);
+#define w5500ll_read_reg(spidev, blockid, blocktyp, field) ({ \
+ typeof((blocktyp){}.field) val; \
+ if (sizeof(val) == 1) { \
+ w5500ll_readv(spidev, \
+ offsetof(blocktyp, field), blockid, \
+ &((struct rd_iovec){ \
+ .iov_read_to = &val, \
+ .iov_len = sizeof(val), \
+ }), 1, \
+ 0); \
+ } else { \
+ typeof(val) val2; \
+ _w5500ll_read_repeated(spidev, \
+ offsetof(blocktyp, field), blockid, \
+ &val, &val2, sizeof(val)); \
+ } \
+ val; \
+ })
- uint8_t header[] = {
- (uint8_t)((addr >> 8) & 0xFF),
- (uint8_t)(addr & 0xFF),
- (block & CTL_MASK_BLOCK) | CTL_R | CTL_OM_VDM,
- };
- int inner_cnt = 1+io_slice_cnt(iov, iovcnt, 0, max);
- struct duplex_iovec *inner = alloca(sizeof(struct duplex_iovec)*inner_cnt);
- inner[0] = (struct duplex_iovec){
- .iov_read_to = IOVEC_DISCARD,
- .iov_write_from = header,
- .iov_len = sizeof(header),
- };
- io_slice_rd_to_duplex(&inner[1], iov, iovcnt, 0, max);
- LO_CALL(spidev, readwritev, inner, inner_cnt);
-}
+/* Register blocks. ******************************************************************************/
/* Common chip-wide registers. ***********************************************/
@@ -378,49 +364,4 @@ static_assert(sizeof(struct w5500ll_block_sock_reg) == 0x30);
struct w5500ll_block_sock_reg, \
field)
-/******************************************************************************/
-
-#define w5500ll_write_reg(spidev, blockid, blocktyp, field, val) do { \
- typeof((blocktyp){}.field) lval = val; \
- w5500ll_writev(spidev, \
- offsetof(blocktyp, field), \
- blockid, \
- &((struct iovec){ \
- &lval, \
- sizeof(lval), \
- }), \
- 1, 0, 0); \
- } while (0)
-
-/* The datasheet tells us that multi-byte reads are non-atomic and
- * that "it is recommended that you read all 16-bits twice or more
- * until getting the same value". */
-#define w5500ll_read_reg(spidev, blockid, blocktyp, field) ({ \
- typeof((blocktyp){}.field) val; \
- w5500ll_readv(spidev, \
- offsetof(blocktyp, field), \
- blockid, \
- &((struct iovec){ \
- .iov_base = &val, \
- .iov_len = sizeof(val), \
- }), \
- 1, 0); \
- if (sizeof(val) > 1) \
- for (;;) { \
- typeof(val) val2; \
- w5500ll_readv(spidev, \
- offsetof(blocktyp, field), \
- blockid, \
- &((struct iovec){ \
- .iov_base = &val2, \
- .iov_len = sizeof(val), \
- }), \
- 1, 0); \
- if (memcmp(&val2, &val, sizeof(val)) == 0) \
- break; \
- val = val2; \
- } \
- val; \
- })
-
#endif /* _LIBHW_CR_W5500_LL_H_ */
diff --git a/libhw_generic/CMakeLists.txt b/libhw_generic/CMakeLists.txt
index 5a6014b..6f5fa34 100644
--- a/libhw_generic/CMakeLists.txt
+++ b/libhw_generic/CMakeLists.txt
@@ -3,11 +3,16 @@
# Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
# SPDX-License-Identifier: AGPL-3.0-or-later
+add_library(libhw_generic_headers INTERFACE)
+target_include_directories(libhw_generic_headers PUBLIC INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include)
+target_link_libraries(libhw_generic_headers INTERFACE
+ libmisc_headers
+)
+
add_library(libhw_generic INTERFACE)
-target_include_directories(libhw_generic SYSTEM INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include)
+target_link_libraries(libhw_generic INTERFACE libhw_generic_headers)
target_link_libraries(libhw_generic INTERFACE
libmisc
- libobj
)
target_sources(libhw_generic INTERFACE
diff --git a/libhw_generic/alarmclock.c b/libhw_generic/alarmclock.c
index 31fbbaf..3579829 100644
--- a/libhw_generic/alarmclock.c
+++ b/libhw_generic/alarmclock.c
@@ -6,4 +6,20 @@
#include <libhw/generic/alarmclock.h>
-lo_interface alarmclock bootclock = {0};
+lo_interface alarmclock bootclock = {};
+
+void alarmclock_sleep_for_ns(lo_interface alarmclock clock, uint64_t delta_ns) {
+ alarmclock_sleep_until_ns(clock, LO_CALL(clock, get_time_ns) + delta_ns);
+}
+
+void alarmclock_sleep_for_us(lo_interface alarmclock clock, uint64_t delta_us) {
+ alarmclock_sleep_for_ns(clock, delta_us * (NS_PER_S/US_PER_S));
+}
+
+void alarmclock_sleep_for_ms(lo_interface alarmclock clock, uint64_t delta_ms) {
+ alarmclock_sleep_for_ns(clock, delta_ms * (NS_PER_S/MS_PER_S));
+}
+
+void alarmclock_sleep_for_s(lo_interface alarmclock clock, uint64_t delta_s) {
+ alarmclock_sleep_for_ns(clock, delta_s * NS_PER_S);
+}
diff --git a/libhw_generic/include/libhw/generic/alarmclock.h b/libhw_generic/include/libhw/generic/alarmclock.h
index 3817b4b..e7e66a1 100644
--- a/libhw_generic/include/libhw/generic/alarmclock.h
+++ b/libhw_generic/include/libhw/generic/alarmclock.h
@@ -7,11 +7,10 @@
#ifndef _LIBHW_GENERIC_ALARMCLOCK_H_
#define _LIBHW_GENERIC_ALARMCLOCK_H_
-#include <stdbool.h> /* for bool */
-#include <stdint.h> /* for uint{n}_t and UINT{n}_C */
+#include <stdint.h> /* for uint{n}_t and UINT{n}_C */
+#include <libmisc/obj.h>
#include <libmisc/private.h>
-#include <libobj/obj.h>
/* Useful constants ***********************************************************/
@@ -23,14 +22,14 @@
struct alarmclock_trigger;
struct alarmclock_trigger {
- BEGIN_PRIVATE(LIBHW_GENERIC_ALARMCLOCK_H)
+ BEGIN_PRIVATE(LIBHW_GENERIC_ALARMCLOCK_H);
void *alarmclock;
struct alarmclock_trigger *prev, *next;
uint64_t fire_at_ns;
void (*cb)(void *);
void *cb_arg;
- END_PRIVATE(LIBHW_GENERIC_ALARMCLOCK_H)
+ END_PRIVATE(LIBHW_GENERIC_ALARMCLOCK_H);
};
/* Interface ******************************************************************/
@@ -45,39 +44,25 @@ struct alarmclock_trigger {
/** \
* Returns true on error. \
* \
- * Implementations may return an error if fire_at_ns is more \
- * than UINT32_MAX µs (72 minutes) in the future. \
- * \
* If fire_at_ns is in the past, then it will fire \
* immediately. \
*/ \
- LO_FUNC(bool, add_trigger, struct alarmclock_trigger *trigger, \
+ LO_FUNC(void, add_trigger, struct alarmclock_trigger *trigger, \
uint64_t fire_at_ns, \
void (*cb)(void *), \
void *cb_arg) \
\
LO_FUNC(void, del_trigger, struct alarmclock_trigger *trigger)
-LO_INTERFACE(alarmclock)
+LO_INTERFACE(alarmclock);
/* Utilities ******************************************************************/
void alarmclock_sleep_until_ns(lo_interface alarmclock clock, uint64_t abstime_ns);
-static inline void alarmclock_sleep_for_ns(lo_interface alarmclock clock, uint64_t delta_ns) {
- alarmclock_sleep_until_ns(clock, LO_CALL(clock, get_time_ns) + delta_ns);
-}
-
-static inline void alarmclock_sleep_for_us(lo_interface alarmclock clock, uint64_t delta_us) {
- alarmclock_sleep_for_ns(clock, delta_us * (NS_PER_S/US_PER_S));
-}
-
-static inline void alarmclock_sleep_for_ms(lo_interface alarmclock clock, uint64_t delta_ms) {
- alarmclock_sleep_for_ns(clock, delta_ms * (NS_PER_S/MS_PER_S));
-}
-
-static inline void alarmclock_sleep_for_s(lo_interface alarmclock clock, uint64_t delta_s) {
- alarmclock_sleep_for_ns(clock, delta_s * NS_PER_S);
-}
+void alarmclock_sleep_for_ns(lo_interface alarmclock clock, uint64_t delta_ns);
+void alarmclock_sleep_for_us(lo_interface alarmclock clock, uint64_t delta_us);
+void alarmclock_sleep_for_ms(lo_interface alarmclock clock, uint64_t delta_ms);
+void alarmclock_sleep_for_s(lo_interface alarmclock clock, uint64_t delta_s);
/* Globals ********************************************************************/
diff --git a/libhw_generic/include/libhw/generic/io.h b/libhw_generic/include/libhw/generic/io.h
index 7825c9f..edaf30c 100644
--- a/libhw_generic/include/libhw/generic/io.h
+++ b/libhw_generic/include/libhw/generic/io.h
@@ -7,22 +7,40 @@
#ifndef _LIBHW_GENERIC_IO_H_
#define _LIBHW_GENERIC_IO_H_
-#include <stddef.h> /* for size_t */
-#include <stdint.h> /* for uintptr_t */
-#include <sys/types.h> /* for ssize_t */
+#include <stddef.h> /* for size_t */
+#include <stdint.h> /* for uintptr_t, uint{n}_t, UINT{n}_MAX */
-#include <libobj/obj.h>
+#include <libmisc/error.h>
+#include <libmisc/obj.h>
/* structs ********************************************************************/
-#if __unix__
-#include <sys/uio.h>
-#else
-struct iovec {
- void *iov_base;
+/* uoff_t ==========================================================*/
+
+typedef uint64_t uoff_t;
+#define UOFF_MAX UINT64_MAX
+DECLARE_ERROR_OR(uoff_t);
+DECLARE_ERROR_AND(uoff_t);
+
+/* rd_iovec ========================================================*/
+
+struct rd_iovec {
+ void *iov_read_to;
size_t iov_len;
};
-#endif
+DECLARE_ERROR_OR_(struct rd_iovec, rd_iovec);
+DECLARE_ERROR_AND_(struct rd_iovec, rd_iovec);
+
+/* wr_iovec ========================================================*/
+
+struct wr_iovec {
+ const void *iov_write_from;
+ size_t iov_len;
+};
+DECLARE_ERROR_OR_(struct wr_iovec, wr_iovec);
+DECLARE_ERROR_AND_(struct wr_iovec, wr_iovec);
+
+/* duplex_iovec ====================================================*/
#define IOVEC_DISCARD ((void*)(~((uintptr_t)0)))
@@ -32,87 +50,176 @@ struct duplex_iovec {
* iov_write_from. To skip a read or write, use the special
* value IOVEC_DISCARD.
*/
- void *iov_read_to;
- void *iov_write_from;
- size_t iov_len;
+ void *iov_read_to;
+ const void *iov_write_from;
+ size_t iov_len;
};
+DECLARE_ERROR_OR_(struct duplex_iovec, duplex_iovec);
+DECLARE_ERROR_AND_(struct duplex_iovec, duplex_iovec);
+
+/* iovec utilities ************************************************************/
-/* utilities ******************************************************************/
+/* If byte_max_cnt == 0, then there is no maximum. */
/* slice iovec lists */
-int io_slice_cnt ( const struct iovec *src, int src_cnt, size_t byte_start, size_t byte_max_cnt);
-void io_slice (struct iovec *dst, const struct iovec *src, int src_cnt, size_t byte_start, size_t byte_max_cnt);
+int io_rd_slice_cnt ( const struct rd_iovec *src, int src_cnt, size_t byte_start, size_t byte_max_cnt);
+void io_rd_slice (struct rd_iovec *dst, const struct rd_iovec *src, int src_cnt, size_t byte_start, size_t byte_max_cnt);
+int io_wr_slice_cnt ( const struct wr_iovec *src, int src_cnt, size_t byte_start, size_t byte_max_cnt);
+void io_wr_slice (struct wr_iovec *dst, const struct wr_iovec *src, int src_cnt, size_t byte_start, size_t byte_max_cnt);
int io_duplex_slice_cnt( const struct duplex_iovec *src, int src_cnt, size_t byte_start, size_t byte_max_cnt);
void io_duplex_slice (struct duplex_iovec *dst, const struct duplex_iovec *src, int src_cnt, size_t byte_start, size_t byte_max_cnt);
/* convert iovec lists */
-void io_rd_to_duplex(struct duplex_iovec *dst, const struct iovec *src, int iovcnt);
-void io_wr_to_duplex(struct duplex_iovec *dst, const struct iovec *src, int iovcnt);
+void io_rd_to_duplex(struct duplex_iovec *dst, const struct rd_iovec *src, int iovcnt);
+void io_wr_to_duplex(struct duplex_iovec *dst, const struct wr_iovec *src, int iovcnt);
/* slice and convert in one go */
-void io_slice_rd_to_duplex(struct duplex_iovec *dst, const struct iovec *src, int src_cnt, size_t byte_start, size_t byte_max_cnt);
-void io_slice_wr_to_duplex(struct duplex_iovec *dst, const struct iovec *src, int src_cnt, size_t byte_start, size_t byte_max_cnt);
+void io_slice_rd_to_duplex(struct duplex_iovec *dst, const struct rd_iovec *src, int src_cnt, size_t byte_start, size_t byte_max_cnt);
+void io_slice_wr_to_duplex(struct duplex_iovec *dst, const struct wr_iovec *src, int src_cnt, size_t byte_start, size_t byte_max_cnt);
/* basic interfaces ***********************************************************/
+/*
+ * Conventions:
+ *
+ * - Naming:
+ * + The "p"[osition] prefix means an explicit `uoff_t offset`,
+ * instead of using an internal cursor.
+ * + The "v"[ec] suffix means a sequence of iovecs, instead of a
+ * simple buf+len.
+ *
+ * - Errors:
+ * + Short *reads* are *not* errors (and so return size_t *or*
+ * error).
+ * + Short *writes* *are* errors (and so return size_t *and*
+ * (possibly-null) error).
+ */
+
+/* read ============================================================*/
+
/**
- * Return bytes-read on success, 0 on EOF, -errno on error; a short
- * read is *not* an error.
+ * Return bytes-read on success. A short read is *not* an error
+ * (unlike `write` methods).
+ *
+ * It is invalid to call readv when the sum length of iovecs is 0.
*/
#define io_reader_LO_IFACE \
- LO_FUNC(ssize_t, readv, const struct iovec *iov, int iovcnt)
-LO_INTERFACE(io_reader)
+ LO_FUNC(size_t_or_error, readv, const struct rd_iovec *iov, int iovcnt)
+LO_INTERFACE(io_reader);
#define io_readv(r, iov, iovcnt) LO_CALL(r, readv, iov, iovcnt)
-#define io_read(r, buf, count) LO_CALL(r, readv, &((struct iovec){.iov_base = buf, .iov_len = count}), 1)
+#define io_read(r, buf, count) LO_CALL(r, readv, &((struct rd_iovec){.iov_read_to = buf, .iov_len = count}), 1)
+
+/**
+ * Returns bytes-read on success. A short read is *not* an error
+ * (unlike `write` methods).
+ *
+ * It is invalid to call preadv when the sum length of iovecs is 0.
+ */
+#define io_preader_LO_IFACE \
+ LO_FUNC(size_t_or_error, preadv, const struct rd_iovec *iov, int iovcnt, uoff_t offset)
+LO_INTERFACE(io_preader);
+#define io_preadv(r, iov, iovcnt, off) LO_CALL(r, preadv, iov, iovcnt, off)
+#define io_pread(r, buf, count, off) LO_CALL(r, preadv, &((struct rd_iovec){.iov_read_to = buf, .iov_len = count}), 1, off)
+
+/* write ===========================================================*/
/**
- * Return `count` on success, -errno on error; a short write *is* an
- * error.
+ * Return bytes-written. A short write *is* an error (unlike `read`
+ * methods).
*
* Writes are *not* guaranteed to be atomic, so if you have concurrent
* writers then you should arrange for a mutex to protect the writer.
+ *
+ * It is invalid to call writev when the sum length of iovecs is 0.
*/
#define io_writer_LO_IFACE \
- LO_FUNC(ssize_t, writev, const struct iovec *iov, int iovcnt)
-LO_INTERFACE(io_writer)
+ LO_FUNC(size_t_and_error, writev, const struct wr_iovec *iov, int iovcnt)
+LO_INTERFACE(io_writer);
#define io_writev(w, iov, iovcnt) LO_CALL(w, writev, iov, iovcnt)
-#define io_write(w, buf, count) LO_CALL(w, writev, &((struct iovec){.iov_base = buf, .iov_len = count}), 1)
+#define io_write(w, buf, count) LO_CALL(w, writev, &((struct wr_iovec){.iov_write_from = buf, .iov_len = count}), 1)
/**
- * Return 0 on success, -errno on error.
+ * Returns bytes-written. A short write *is* an error (unlike `read`
+ * methods).
+ *
+ * Writes are *not* guaranteed to be atomic, so if you have concurrent
+ * writers then you should arrange for a mutex to protect the writer.
+ *
+ * It is invalid to call writev when the sum length of iovecs is 0.
*/
-#define io_closer_LO_IFACE \
- LO_FUNC(int, close)
-LO_INTERFACE(io_closer)
-#define io_close(c) LO_CALL(c, close)
+#define io_pwriter_LO_IFACE \
+ LO_FUNC(size_t_and_error, pwritev, const struct wr_iovec *iov, int iovcnt, uoff_t offset)
+LO_INTERFACE(io_pwriter);
+#define io_pwritev(r, iov, iovcnt, off) LO_CALL(r, pwritev, iov, iovcnt, off)
+#define io_pwrite(r, buf, count, off) LO_CALL(r, pwritev, &((struct wr_iovec){.iov_write_from = buf, .iov_len = count}), 1, off)
-/**
- * All methods return 0 on success, -errno on error.
- */
-#define io_bidi_closer_LO_IFACE \
- LO_NEST(io_closer) \
- LO_FUNC(int, close_read) \
- LO_FUNC(int, close_write)
-LO_INTERFACE(io_bidi_closer)
-#define io_close_read(c) LO_CALL(c, close_read)
-#define io_close_write(c) LO_CALL(c, close_write)
+/* readwrite =======================================================*/
/**
- * Return bytes-read and bytes-written on success, -errno on error; a
- * short read/write *is* an error.
+ * A short read/write *is* an error (like `write` methods and unlike
+ * `read` methods).
+ *
+ * Reads/writes are *not* guaranteed to be atomic, so if you have
+ * concurrent readers/writers then you should arrange for a mutex to
+ * protect the duplex_readwriter.
+ *
+ * It is invalid to call readwritev when the sum length of iovecs is 0.
*/
#define io_duplex_readwriter_LO_IFACE \
- LO_FUNC(void, readwritev, const struct duplex_iovec *iov, int iovcnt)
-LO_INTERFACE(io_duplex_readwriter)
-
+ LO_FUNC(size_t_and_error, readwritev, const struct duplex_iovec *iov, int iovcnt)
+LO_INTERFACE(io_duplex_readwriter);
#define io_readwritev(rw, iov, iovcnt) \
LO_CALL(rw, readwritev, iov, iovcnt)
+/* read then write =================================================*/
+
+/**
+ * LO_CALL(src, pread_to, dst, src_offset, count) is functionally:
+ *
+ * size_t_and_error copy(lo_interface io_writer dst, lo_interface io_preader src, uoff_t src_offset, size_t count) {
+ * buf = malloc(count);
+ * size_t_or_error read_r = io_pread(src, buf, count, src_offset);
+ * if (read_r.is_err)
+ * return ERROR_AND(size_t, 0, read_r.err);
+ * size_t_and_error write_r = io_write(dst, buf, read_r.size_t);
+ * free(buf);
+ * return write_r;
+ * }
+ *
+ * except that it does not need to allocate a buffer. That is: It
+ * allows the reader to dump its data directly to the writer. This
+ * allows us to save a copy when the reader already has an in-memory
+ * buffer containing the data.
+ *
+ * The above code defines when a short read/write is an error or not.
+ *
+ * It must call writev() exactly 0-or-1 times, and if it does call it,
+ * then it must be called with exactly 1 iovec.
+ */
+#define io_preader_to_LO_IFACE \
+ LO_FUNC(size_t_and_error, pread_to, lo_interface io_writer dst, uoff_t src_offset, size_t count)
+LO_INTERFACE(io_preader_to);
+
+/* close ===========================================================*/
+
+#define io_closer_LO_IFACE \
+ LO_FUNC(error, close)
+LO_INTERFACE(io_closer);
+#define io_close(c) LO_CALL(c, close)
+
+#define io_bidi_closer_LO_IFACE \
+ LO_NEST(io_closer) \
+ LO_FUNC(error, close_read) \
+ LO_FUNC(error, close_write)
+LO_INTERFACE(io_bidi_closer);
+#define io_close_read(c) LO_CALL(c, close_read)
+#define io_close_write(c) LO_CALL(c, close_write)
+
/* aggregate interfaces *******************************************************/
#define io_readwriter_LO_IFACE \
LO_NEST(io_reader) \
LO_NEST(io_writer)
-LO_INTERFACE(io_readwriter)
+LO_INTERFACE(io_readwriter);
#endif /* _LIBHW_GENERIC_IO_H_ */
diff --git a/libhw_generic/include/libhw/generic/net.h b/libhw_generic/include/libhw/generic/net.h
index e88d705..e2d626f 100644
--- a/libhw_generic/include/libhw/generic/net.h
+++ b/libhw_generic/include/libhw/generic/net.h
@@ -7,25 +7,11 @@
#ifndef _LIBHW_GENERIC_NET_H_
#define _LIBHW_GENERIC_NET_H_
-#include <inttypes.h> /* for PRI{u,x}{n} */
-#include <stdbool.h> /* for bool */
#include <stddef.h> /* for size_t */
#include <stdint.h> /* for uint{n}_t} */
-#include <sys/types.h> /* for ssize_t */
#include <libhw/generic/io.h>
-
-/* Errnos *********************************************************************/
-
-#define NET_EOTHER 1
-#define NET_EARP_TIMEOUT 2
-#define NET_EACK_TIMEOUT 3
-#define NET_ERECV_TIMEOUT 4
-#define NET_ETHREAD 5
-#define NET_ECLOSED 6
-#define NET_EMSGSIZE 7
-
-const char *net_strerror(int net_errno);
+#include <libmisc/fmt.h>
/* Address types **************************************************************/
@@ -35,43 +21,16 @@ struct net_ip4_addr {
static const struct net_ip4_addr net_ip4_addr_broadcast = {{255, 255, 255, 255}};
static const struct net_ip4_addr net_ip4_addr_zero = {{0, 0, 0, 0}};
-
-#define PRI_net_ip4_addr "%"PRIu8".%"PRIu8".%"PRIu8".%"PRIu8
-#define ARG_net_ip4_addr(addr) (addr).octets[0], \
- (addr).octets[1], \
- (addr).octets[2], \
- (addr).octets[3]
+void fmt_print_net_ip4_addr(lo_interface fmt_dest, struct net_ip4_addr);
struct net_eth_addr {
unsigned char octets[6];
};
-#define PRI_net_eth_addr "%02"PRIx8":%02"PRIx8":%02"PRIx8":%02"PRIx8":%02"PRIx8":%02"PRIx8
-#define ARG_net_eth_addr(addr) (addr).octets[0], \
- (addr).octets[1], \
- (addr).octets[2], \
- (addr).octets[3], \
- (addr).octets[4], \
- (addr).octets[5]
+void fmt_print_net_eth_addr(lo_interface fmt_dest, struct net_eth_addr);
/* Streams (e.g. TCP) *********************************************************/
-lo_interface net_stream_conn;
-
-#define net_stream_listener_LO_IFACE \
- /** \
- * It is invalid to accept() a new connection if an existing \
- * connection is still open. \
- */ \
- LO_FUNC(lo_interface net_stream_conn, accept) \
- \
- /** \
- * The net_stream_conn returned from accept() may still be \
- * valid after the listener is closed. \
- */ \
- LO_NEST(io_closer)
-LO_INTERFACE(net_stream_listener)
-
#define net_stream_conn_LO_IFACE \
LO_NEST(io_readwriter) \
LO_NEST(io_bidi_closer) \
@@ -88,12 +47,28 @@ LO_INTERFACE(net_stream_listener)
* risk of this overflowing) \
*/ \
LO_FUNC(void, set_read_deadline, uint64_t ns_since_boot)
-LO_INTERFACE(net_stream_conn)
+LO_INTERFACE(net_stream_conn);
+
+DECLARE_ERROR_OR_(lo_interface net_stream_conn, net_stream_conn);
+
+#define net_stream_listener_LO_IFACE \
+ /** \
+ * It is invalid to accept() a new connection if an existing \
+ * connection is still open. \
+ */ \
+ LO_FUNC(net_stream_conn_or_error, accept) \
+ \
+ /** \
+ * The net_stream_conn returned from accept() may still be \
+ * valid after the listener is closed. \
+ */ \
+ LO_NEST(io_closer)
+LO_INTERFACE(net_stream_listener);
/* Packets (e.g. UDP) *********************************************************/
#define net_packet_conn_LO_IFACE \
- LO_FUNC(ssize_t, sendto, \
+ LO_FUNC(error, sendto, \
void *buf, size_t len, \
struct net_ip4_addr node, uint16_t port) \
\
@@ -102,7 +77,7 @@ LO_INTERFACE(net_stream_conn)
* than the given `len` (as if the Linux MSG_TRUNC flag were \
* given). \
*/ \
- LO_FUNC(ssize_t, recvfrom, \
+ LO_FUNC(size_t_or_error, recvfrom, \
void *buf, size_t len, \
struct net_ip4_addr *ret_node, uint16_t *ret_port) \
\
@@ -121,7 +96,7 @@ LO_INTERFACE(net_stream_conn)
uint64_t ns_since_boot) \
\
LO_NEST(io_closer)
-LO_INTERFACE(net_packet_conn)
+LO_INTERFACE(net_packet_conn);
/* Interfaces *****************************************************************/
@@ -137,8 +112,11 @@ struct net_iface_config {
LO_FUNC(void , ifdown ) \
\
LO_FUNC(lo_interface net_stream_listener, tcp_listen, uint16_t local_port) \
- LO_FUNC(lo_interface net_stream_conn , tcp_dial , struct net_ip4_addr remote_node, uint16_t remote_port) \
- LO_FUNC(lo_interface net_packet_conn , udp_conn , uint16_t local_port)
-LO_INTERFACE(net_iface)
+ LO_FUNC(net_stream_conn_or_error , tcp_dial , struct net_ip4_addr remote_node, uint16_t remote_port) \
+ LO_FUNC(lo_interface net_packet_conn , udp_conn , uint16_t local_port) \
+ \
+ /** FIXME: arp_ping should probably have an explicit timeout or something. */ \
+ LO_FUNC(bool , arp_ping , struct net_ip4_addr)
+LO_INTERFACE(net_iface);
#endif /* _LIBHW_GENERIC_NET_H_ */
diff --git a/libhw_generic/include/libhw/generic/spi.h b/libhw_generic/include/libhw/generic/spi.h
index a4dbcae..4bbf649 100644
--- a/libhw_generic/include/libhw/generic/spi.h
+++ b/libhw_generic/include/libhw/generic/spi.h
@@ -32,6 +32,6 @@ enum spi_mode {
*/
#define spi_LO_IFACE \
LO_NEST(io_duplex_readwriter)
-LO_INTERFACE(spi)
+LO_INTERFACE(spi);
#endif /* _LIBHW_GENERIC_SPI_H_ */
diff --git a/libhw_generic/io.c b/libhw_generic/io.c
index 4ebff10..a8c9da6 100644
--- a/libhw_generic/io.c
+++ b/libhw_generic/io.c
@@ -37,22 +37,27 @@
j++; \
}
-int io_slice_cnt(const struct iovec *src, int src_cnt, size_t byte_beg, size_t byte_max_cnt) {
- IOV_ITER();
- return j;
-}
+/* count */
+int io_rd_slice_cnt (const struct rd_iovec *src, int src_cnt, size_t byte_beg, size_t byte_max_cnt) { IOV_ITER(); return j; }
+int io_wr_slice_cnt (const struct wr_iovec *src, int src_cnt, size_t byte_beg, size_t byte_max_cnt) { IOV_ITER(); return j; }
+int io_duplex_slice_cnt(const struct duplex_iovec *src, int src_cnt, size_t byte_beg, size_t byte_max_cnt) { IOV_ITER(); return j; }
-int io_duplex_slice_cnt(const struct duplex_iovec *src, int src_cnt, size_t byte_beg, size_t byte_max_cnt) {
- IOV_ITER();
- return j;
+/* slice */
+void io_rd_slice(struct rd_iovec *dst, const struct rd_iovec *src, int src_cnt, size_t byte_beg, size_t byte_max_cnt) {
+ assert(src_cnt == 0 || dst);
+ IOV_ITER(
+ dst[j] = ((struct rd_iovec){
+ .iov_read_to = src[i].iov_read_to+off,
+ .iov_len = len,
+ });
+ );
}
-
-void io_slice(struct iovec *dst, const struct iovec *src, int src_cnt, size_t byte_beg, size_t byte_max_cnt) {
+void io_wr_slice(struct wr_iovec *dst, const struct wr_iovec *src, int src_cnt, size_t byte_beg, size_t byte_max_cnt) {
assert(src_cnt == 0 || dst);
IOV_ITER(
- dst[j] = ((struct iovec){
- .iov_base = src[i].iov_base+off,
- .iov_len = len,
+ dst[j] = ((struct wr_iovec){
+ .iov_write_from = src[i].iov_write_from+off,
+ .iov_len = len,
});
);
}
@@ -67,32 +72,32 @@ void io_slice_duplex(struct duplex_iovec *dst, const struct duplex_iovec *src, i
);
}
-void io_slice_rd_to_duplex(struct duplex_iovec *dst, const struct iovec *src, int src_cnt, size_t byte_beg, size_t byte_max_cnt) {
+/* slice+convert */
+void io_slice_rd_to_duplex(struct duplex_iovec *dst, const struct rd_iovec *src, int src_cnt, size_t byte_beg, size_t byte_max_cnt) {
assert(src_cnt == 0 || dst);
IOV_ITER(
dst[j] = ((struct duplex_iovec){
- .iov_read_to = src[i].iov_base+off,
+ .iov_read_to = src[i].iov_read_to+off,
.iov_write_from = IOVEC_DISCARD,
.iov_len = len,
});
);
}
-
-void io_slice_wr_to_duplex(struct duplex_iovec *dst, const struct iovec *src, int src_cnt, size_t byte_beg, size_t byte_max_cnt) {
+void io_slice_wr_to_duplex(struct duplex_iovec *dst, const struct wr_iovec *src, int src_cnt, size_t byte_beg, size_t byte_max_cnt) {
assert(src_cnt == 0 || dst);
IOV_ITER(
dst[j] = ((struct duplex_iovec){
.iov_read_to = IOVEC_DISCARD,
- .iov_write_from = src[i].iov_base+off,
+ .iov_write_from = src[i].iov_write_from+off,
.iov_len = len,
});
);
}
-void io_rd_to_duplex(struct duplex_iovec *dst, const struct iovec *src, int iovcnt) {
+/* convert */
+void io_rd_to_duplex(struct duplex_iovec *dst, const struct rd_iovec *src, int iovcnt) {
io_slice_rd_to_duplex(dst, src, iovcnt, 0, 0);
}
-
-void io_wr_to_duplex(struct duplex_iovec *dst, const struct iovec *src, int iovcnt) {
+void io_wr_to_duplex(struct duplex_iovec *dst, const struct wr_iovec *src, int iovcnt) {
io_slice_wr_to_duplex(dst, src, iovcnt, 0, 0);
}
diff --git a/libhw_generic/net.c b/libhw_generic/net.c
index e2785ae..906e6e4 100644
--- a/libhw_generic/net.c
+++ b/libhw_generic/net.c
@@ -1,6 +1,6 @@
/* libhw_generic/net.c - Device-independent <libhw/generic/net.h> utilities
*
- * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
@@ -29,3 +29,21 @@ const char *net_strerror(int net_errno) {
assert_notreached("invalid net_errno");
}
}
+
+void fmt_print_net_ip4_addr(lo_interface fmt_dest w, struct net_ip4_addr addr) {
+ fmt_print(w,
+ addr.octets[0], ".",
+ addr.octets[1], ".",
+ addr.octets[2], ".",
+ addr.octets[3]);
+}
+
+void fmt_print_net_eth_addr(lo_interface fmt_dest w, struct net_eth_addr addr) {
+ fmt_print(w,
+ (rjust, 2, '0', (base16, addr.octets[0])), ":",
+ (rjust, 2, '0', (base16, addr.octets[1])), ":",
+ (rjust, 2, '0', (base16, addr.octets[2])), ":",
+ (rjust, 2, '0', (base16, addr.octets[3])), ":",
+ (rjust, 2, '0', (base16, addr.octets[4])), ":",
+ (rjust, 2, '0', (base16, addr.octets[5])));
+}
diff --git a/libhw_generic/tests/test_io.c b/libhw_generic/tests/test_io.c
index 0d6df11..c6eeba0 100644
--- a/libhw_generic/tests/test_io.c
+++ b/libhw_generic/tests/test_io.c
@@ -12,9 +12,9 @@
int main() {
char data[] = "abcdefghijklmnopqrstuvwxyz";
- struct iovec src[] = {
- {.iov_base = &data[1], .iov_len=3}, /* "bcd" */
- {.iov_base = &data[20], .iov_len=4}, /* "uvwx" */
+ struct wr_iovec src[] = {
+ {.iov_write_from = &data[1], .iov_len=3}, /* "bcd" */
+ {.iov_write_from = &data[20], .iov_len=4}, /* "uvwx" */
};
const int src_cnt = sizeof(src)/sizeof(src[0]);
@@ -23,7 +23,7 @@ int main() {
#define TC(start, max, ...) do { \
char *exp[] = {__VA_ARGS__}; \
int exp_cnt = sizeof(exp)/sizeof(exp[0]); \
- int act_cnt = io_slice_cnt(src, src_cnt, start, max); \
+ int act_cnt = io_wr_slice_cnt(src, src_cnt, start, max); \
test_assert(act_cnt == exp_cnt); \
memset(dst, 0, sizeof(dst)); \
io_slice_wr_to_duplex(dst, src, src_cnt, start, max); \
diff --git a/libmisc/CMakeLists.txt b/libmisc/CMakeLists.txt
index 70ec691..7388a91 100644
--- a/libmisc/CMakeLists.txt
+++ b/libmisc/CMakeLists.txt
@@ -3,20 +3,40 @@
# Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
# SPDX-License-Identifier: AGPL-3.0-or-later
+add_library(libmisc_headers INTERFACE)
+target_include_directories(libmisc_headers PUBLIC INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include)
+target_compile_options(libmisc_headers INTERFACE
+ -no-integrated-cpp
+ -wrapper "${CMAKE_CURRENT_SOURCE_DIR}/wrap-cc"
+)
+
add_library(libmisc INTERFACE)
-target_include_directories(libmisc SYSTEM INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include)
+target_link_libraries(libmisc INTERFACE libmisc_headers)
target_sources(libmisc INTERFACE
assert.c
+ endian.c
+ error.c
+ error_generated.c
+ fmt.c
+ hash.c
intercept.c
+ linkedlist.c
log.c
+ map.c
+ rand.c
+ utf8.c
)
-target_compile_options(libmisc INTERFACE "$<$<COMPILE_LANGUAGE:C>:-fplan9-extensions>")
add_lib_test(libmisc test_assert)
add_lib_test(libmisc test_assert_min)
add_lib_test(libmisc test_endian)
+add_lib_test(libmisc test_fmt)
add_lib_test(libmisc test_hash)
add_lib_test(libmisc test_log)
add_lib_test(libmisc test_macro)
+add_lib_test(libmisc test_map)
+add_lib_test(libmisc test_obj)
+add_lib_test(libmisc test_obj_nest)
+add_lib_test(libmisc test_obj_autobox)
add_lib_test(libmisc test_private)
add_lib_test(libmisc test_rand)
diff --git a/libmisc/assert.c b/libmisc/assert.c
index fdd8154..dc7f250 100644
--- a/libmisc/assert.c
+++ b/libmisc/assert.c
@@ -4,25 +4,21 @@
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
-#include <stdbool.h> /* for bool, true, false */
-
#define LOG_NAME ASSERT
-#include <libmisc/log.h> /* for errorf() */
+#include <libmisc/log.h> /* for log_errorln() */
#include <libmisc/assert.h>
#ifndef NDEBUG
-#define __lm_printf __lm_light_printf
void __assert_msg_fail(const char *expr,
- const char *file, unsigned int line, const char *func,
- const char *msg) {
+ const char *file, unsigned int line, const char *func,
+ const char *msg) {
static bool in_fail = false;
if (!in_fail) {
in_fail = true;
- errorf("%s:%u:%s(): assertion \"%s\" failed%s%s",
- file, line, func,
- expr,
- msg ? ": " : "", msg ?: "");
+ log_errorln(file, ":", line, ":", func, "(): ",
+ "assertion \"", (str, expr), "\" failed",
+ msg ? ": " : "", msg ?: "");
in_fail = false;
}
__lm_abort();
diff --git a/libmisc/endian.c b/libmisc/endian.c
new file mode 100644
index 0000000..2528f48
--- /dev/null
+++ b/libmisc/endian.c
@@ -0,0 +1,136 @@
+/* libmisc/endian.c - Endian-conversion helpers
+ *
+ * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#include <libmisc/macro.h> /* for LM_FORCE_SEMICOLON */
+
+#include <libmisc/endian.h>
+
+#define endian_declare_wrappers(NBIT, ENDIAN) \
+ uint##NBIT##ENDIAN##_t uint##NBIT##ENDIAN##_marshal(uint##NBIT##_t in) { \
+ uint##NBIT##ENDIAN##_t out; \
+ uint##NBIT##ENDIAN##_encode(out.octets, in); \
+ return out; \
+ } \
+ uint##NBIT##_t uint##NBIT##ENDIAN##_unmarshal(uint##NBIT##ENDIAN##_t in) { \
+ return uint##NBIT##ENDIAN##_decode(in.octets); \
+ } \
+ LM_FORCE_SEMICOLON
+
+/* Big endian *****************************************************************/
+
+size_t uint16be_encode(uint8_t *out, uint16_t in) {
+ out[0] = (uint8_t)((in >> 8) & 0xFF);
+ out[1] = (uint8_t)((in >> 0) & 0xFF);
+ return 2;
+}
+
+uint16_t uint16be_decode(uint8_t *in) {
+ return (((uint16_t)(in[0])) << 8)
+ | (((uint16_t)(in[1])) << 0)
+ ;
+}
+
+size_t uint32be_encode(uint8_t *out, uint32_t in) {
+ out[0] = (uint8_t)((in >> 24) & 0xFF);
+ out[1] = (uint8_t)((in >> 16) & 0xFF);
+ out[2] = (uint8_t)((in >> 8) & 0xFF);
+ out[3] = (uint8_t)((in >> 0) & 0xFF);
+ return 4;
+}
+
+uint32_t uint32be_decode(uint8_t *in) {
+ return (((uint32_t)(in[0])) << 24)
+ | (((uint32_t)(in[1])) << 16)
+ | (((uint32_t)(in[2])) << 8)
+ | (((uint32_t)(in[3])) << 0)
+ ;
+}
+
+size_t uint64be_encode(uint8_t *out, uint64_t in) {
+ out[0] = (uint8_t)((in >> 56) & 0xFF);
+ out[1] = (uint8_t)((in >> 48) & 0xFF);
+ out[2] = (uint8_t)((in >> 40) & 0xFF);
+ out[3] = (uint8_t)((in >> 32) & 0xFF);
+ out[4] = (uint8_t)((in >> 24) & 0xFF);
+ out[5] = (uint8_t)((in >> 16) & 0xFF);
+ out[6] = (uint8_t)((in >> 8) & 0xFF);
+ out[7] = (uint8_t)((in >> 0) & 0xFF);
+ return 8;
+}
+
+uint64_t uint64be_decode(uint8_t *in) {
+ return (((uint64_t)(in[0])) << 56)
+ | (((uint64_t)(in[1])) << 48)
+ | (((uint64_t)(in[2])) << 40)
+ | (((uint64_t)(in[3])) << 32)
+ | (((uint64_t)(in[4])) << 24)
+ | (((uint64_t)(in[5])) << 16)
+ | (((uint64_t)(in[6])) << 8)
+ | (((uint64_t)(in[7])) << 0)
+ ;
+}
+
+endian_declare_wrappers(16, be);
+endian_declare_wrappers(32, be);
+endian_declare_wrappers(64, be);
+
+/* Little endian **************************************************************/
+
+size_t uint16le_encode(uint8_t *out, uint16_t in) {
+ out[0] = (uint8_t)((in >> 0) & 0xFF);
+ out[1] = (uint8_t)((in >> 8) & 0xFF);
+ return 2;
+}
+
+uint16_t uint16le_decode(uint8_t *in) {
+ return (((uint16_t)(in[0])) << 0)
+ | (((uint16_t)(in[1])) << 8)
+ ;
+}
+
+size_t uint32le_encode(uint8_t *out, uint32_t in) {
+ 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);
+ return 4;
+}
+
+uint32_t uint32le_decode(uint8_t *in) {
+ return (((uint32_t)(in[0])) << 0)
+ | (((uint32_t)(in[1])) << 8)
+ | (((uint32_t)(in[2])) << 16)
+ | (((uint32_t)(in[3])) << 24)
+ ;
+}
+
+size_t uint64le_encode(uint8_t *out, uint64_t in) {
+ 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);
+ return 8;
+}
+
+uint64_t uint64le_decode(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)
+ ;
+}
+
+endian_declare_wrappers(16, le);
+endian_declare_wrappers(32, le);
+endian_declare_wrappers(64, le);
diff --git a/libmisc/error.c b/libmisc/error.c
new file mode 100644
index 0000000..345755c
--- /dev/null
+++ b/libmisc/error.c
@@ -0,0 +1,35 @@
+/* libmisc/error.c - Go-esque errors
+ *
+ * Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#include <string.h> /* for strdup() */
+
+#include <libmisc/error.h>
+
+const char *error_msg(error err) {
+ return (err._msg && err._msg[0])
+ ? err._msg
+ : _errnum_str_msg(err.num);
+}
+
+error error_dup(error err) {
+ return (error){
+ .num = err.num,
+ ._msg = err._msg ? strdup(err._msg) : NULL,
+ };
+}
+
+void error_cleanup(error *errptr) {
+ if (!errptr)
+ return;
+ if (errptr->_msg)
+ free(errptr->_msg);
+ errptr->num = E_NOERROR;
+ errptr->_msg = NULL;
+}
+
+void fmt_print_error(lo_interface fmt_dest w, error err) {
+ fmt_print(w, (str, error_msg(err)), " (", _errnum_str_sym(err.num), ")");
+}
diff --git a/libmisc/error_generated.c b/libmisc/error_generated.c
new file mode 100644
index 0000000..e0afcab
--- /dev/null
+++ b/libmisc/error_generated.c
@@ -0,0 +1,186 @@
+/* libmisc/error_generated.c - Generated by libmisc/error_generated.c.gen. DO NOT EDIT! */
+
+#include <libmisc/error.h>
+
+const char *_errnum_str_sym(_errnum errnum) {
+ switch (errnum) {
+ case E_NOERROR: return "E_NOERROR";
+ case E_EOF: return "E_EOF";
+ case E_NET_EARP_TIMEOUT: return "E_NET_EARP_TIMEOUT";
+ case E_NET_EACK_TIMEOUT: return "E_NET_EACK_TIMEOUT";
+ case E_NET_ERECV_TIMEOUT: return "E_NET_ERECV_TIMEOUT";
+ case E_NET_ECLOSED: return "E_NET_ECLOSED";
+ case E_POSIX_E2BIG: return "E_POSIX_E2BIG";
+ case E_POSIX_EACCES: return "E_POSIX_EACCES";
+ case E_POSIX_EADDRINUSE: return "E_POSIX_EADDRINUSE";
+ case E_POSIX_EADDRNOTAVAIL: return "E_POSIX_EADDRNOTAVAIL";
+ case E_POSIX_EAFNOSUPPORT: return "E_POSIX_EAFNOSUPPORT";
+ case E_POSIX_EAGAIN: return "E_POSIX_EAGAIN";
+ case E_POSIX_EALREADY: return "E_POSIX_EALREADY";
+ case E_POSIX_EBADF: return "E_POSIX_EBADF";
+ case E_POSIX_EBADMSG: return "E_POSIX_EBADMSG";
+ case E_POSIX_EBUSY: return "E_POSIX_EBUSY";
+ case E_POSIX_ECANCELED: return "E_POSIX_ECANCELED";
+ case E_POSIX_ECHILD: return "E_POSIX_ECHILD";
+ case E_POSIX_ECONNABORTED: return "E_POSIX_ECONNABORTED";
+ case E_POSIX_ECONNREFUSED: return "E_POSIX_ECONNREFUSED";
+ case E_POSIX_ECONNRESET: return "E_POSIX_ECONNRESET";
+ case E_POSIX_EDEADLK: return "E_POSIX_EDEADLK";
+ case E_POSIX_EDESTADDRREQ: return "E_POSIX_EDESTADDRREQ";
+ case E_POSIX_EDOM: return "E_POSIX_EDOM";
+ case E_POSIX_EDQUOT: return "E_POSIX_EDQUOT";
+ case E_POSIX_EEXIST: return "E_POSIX_EEXIST";
+ case E_POSIX_EFAULT: return "E_POSIX_EFAULT";
+ case E_POSIX_EFBIG: return "E_POSIX_EFBIG";
+ case E_POSIX_EHOSTUNREACH: return "E_POSIX_EHOSTUNREACH";
+ case E_POSIX_EIDRM: return "E_POSIX_EIDRM";
+ case E_POSIX_EILSEQ: return "E_POSIX_EILSEQ";
+ case E_POSIX_EINPROGRESS: return "E_POSIX_EINPROGRESS";
+ case E_POSIX_EINTR: return "E_POSIX_EINTR";
+ case E_POSIX_EINVAL: return "E_POSIX_EINVAL";
+ case E_POSIX_EIO: return "E_POSIX_EIO";
+ case E_POSIX_EISCONN: return "E_POSIX_EISCONN";
+ case E_POSIX_EISDIR: return "E_POSIX_EISDIR";
+ case E_POSIX_ELOOP: return "E_POSIX_ELOOP";
+ case E_POSIX_EMFILE: return "E_POSIX_EMFILE";
+ case E_POSIX_EMLINK: return "E_POSIX_EMLINK";
+ case E_POSIX_EMSGSIZE: return "E_POSIX_EMSGSIZE";
+ case E_POSIX_EMULTIHOP: return "E_POSIX_EMULTIHOP";
+ case E_POSIX_ENAMETOOLONG: return "E_POSIX_ENAMETOOLONG";
+ case E_POSIX_ENETDOWN: return "E_POSIX_ENETDOWN";
+ case E_POSIX_ENETRESET: return "E_POSIX_ENETRESET";
+ case E_POSIX_ENETUNREACH: return "E_POSIX_ENETUNREACH";
+ case E_POSIX_ENFILE: return "E_POSIX_ENFILE";
+ case E_POSIX_ENOBUFS: return "E_POSIX_ENOBUFS";
+ case E_POSIX_ENODEV: return "E_POSIX_ENODEV";
+ case E_POSIX_ENOENT: return "E_POSIX_ENOENT";
+ case E_POSIX_ENOEXEC: return "E_POSIX_ENOEXEC";
+ case E_POSIX_ENOLCK: return "E_POSIX_ENOLCK";
+ case E_POSIX_ENOLINK: return "E_POSIX_ENOLINK";
+ case E_POSIX_ENOMEM: return "E_POSIX_ENOMEM";
+ case E_POSIX_ENOMSG: return "E_POSIX_ENOMSG";
+ case E_POSIX_ENOPROTOOPT: return "E_POSIX_ENOPROTOOPT";
+ case E_POSIX_ENOSPC: return "E_POSIX_ENOSPC";
+ case E_POSIX_ENOSYS: return "E_POSIX_ENOSYS";
+ case E_POSIX_ENOTCONN: return "E_POSIX_ENOTCONN";
+ case E_POSIX_ENOTDIR: return "E_POSIX_ENOTDIR";
+ case E_POSIX_ENOTEMPTY: return "E_POSIX_ENOTEMPTY";
+ case E_POSIX_ENOTRECOVERABLE: return "E_POSIX_ENOTRECOVERABLE";
+ case E_POSIX_ENOTSOCK: return "E_POSIX_ENOTSOCK";
+ case E_POSIX_ENOTSUP: return "E_POSIX_ENOTSUP";
+ case E_POSIX_ENOTTY: return "E_POSIX_ENOTTY";
+ case E_POSIX_ENXIO: return "E_POSIX_ENXIO";
+ case E_POSIX_EOPNOTSUPP: return "E_POSIX_EOPNOTSUPP";
+ case E_POSIX_EOVERFLOW: return "E_POSIX_EOVERFLOW";
+ case E_POSIX_EOWNERDEAD: return "E_POSIX_EOWNERDEAD";
+ case E_POSIX_EPERM: return "E_POSIX_EPERM";
+ case E_POSIX_EPIPE: return "E_POSIX_EPIPE";
+ case E_POSIX_EPROTO: return "E_POSIX_EPROTO";
+ case E_POSIX_EPROTONOSUPPORT: return "E_POSIX_EPROTONOSUPPORT";
+ case E_POSIX_EPROTOTYPE: return "E_POSIX_EPROTOTYPE";
+ case E_POSIX_ERANGE: return "E_POSIX_ERANGE";
+ case E_POSIX_EROFS: return "E_POSIX_EROFS";
+ case E_POSIX_ESOCKTNOSUPPORT: return "E_POSIX_ESOCKTNOSUPPORT";
+ case E_POSIX_ESPIPE: return "E_POSIX_ESPIPE";
+ case E_POSIX_ESRCH: return "E_POSIX_ESRCH";
+ case E_POSIX_ESTALE: return "E_POSIX_ESTALE";
+ case E_POSIX_ETIMEDOUT: return "E_POSIX_ETIMEDOUT";
+ case E_POSIX_ETXTBSY: return "E_POSIX_ETXTBSY";
+ case E_POSIX_EWOULDBLOCK: return "E_POSIX_EWOULDBLOCK";
+ case E_POSIX_EXDEV: return "E_POSIX_EXDEV";
+ case E_EUNKNOWN: return "E_EUNKNOWN";
+ default: return "E_<invalid>";
+ }
+}
+
+const char *_errnum_str_msg(_errnum errnum) {
+ switch (errnum) {
+ case E_NOERROR: return "no error";
+ case E_EOF: return "EOF";
+ case E_NET_EARP_TIMEOUT: return "ARP timeout";
+ case E_NET_EACK_TIMEOUT: return "TCP ACK timeout";
+ case E_NET_ERECV_TIMEOUT: return "receive timeout";
+ case E_NET_ECLOSED: return "already closed";
+ case E_POSIX_E2BIG: return "Argument list too long";
+ case E_POSIX_EACCES: return "Permission denied";
+ case E_POSIX_EADDRINUSE: return "Address in use";
+ case E_POSIX_EADDRNOTAVAIL: return "Address not available";
+ case E_POSIX_EAFNOSUPPORT: return "Address family not supported";
+ case E_POSIX_EAGAIN: return "Resource unavailable, try again";
+ case E_POSIX_EALREADY: return "Connection already in progress";
+ case E_POSIX_EBADF: return "Bad file descriptor";
+ case E_POSIX_EBADMSG: return "Bad message";
+ case E_POSIX_EBUSY: return "Device or resource busy";
+ case E_POSIX_ECANCELED: return "Operation canceled";
+ case E_POSIX_ECHILD: return "No child processes";
+ case E_POSIX_ECONNABORTED: return "Connection aborted";
+ case E_POSIX_ECONNREFUSED: return "Connection refused";
+ case E_POSIX_ECONNRESET: return "Connection reset";
+ case E_POSIX_EDEADLK: return "Resource deadlock would occur";
+ case E_POSIX_EDESTADDRREQ: return "Destination address required";
+ case E_POSIX_EDOM: return "Mathematics argument out of domain of function";
+ case E_POSIX_EDQUOT: return "Reserved";
+ case E_POSIX_EEXIST: return "File exists";
+ case E_POSIX_EFAULT: return "Bad address";
+ case E_POSIX_EFBIG: return "File too large";
+ case E_POSIX_EHOSTUNREACH: return "Host is unreachable";
+ case E_POSIX_EIDRM: return "Identifier removed";
+ case E_POSIX_EILSEQ: return "Illegal byte sequence";
+ case E_POSIX_EINPROGRESS: return "Operation in progress";
+ case E_POSIX_EINTR: return "Interrupted function";
+ case E_POSIX_EINVAL: return "Invalid argument";
+ case E_POSIX_EIO: return "I/O error";
+ case E_POSIX_EISCONN: return "Socket is connected";
+ case E_POSIX_EISDIR: return "Is a directory";
+ case E_POSIX_ELOOP: return "Too many levels of symbolic links";
+ case E_POSIX_EMFILE: return "File descriptor value too large";
+ case E_POSIX_EMLINK: return "Too many hard links";
+ case E_POSIX_EMSGSIZE: return "Message too large";
+ case E_POSIX_EMULTIHOP: return "Reserved";
+ case E_POSIX_ENAMETOOLONG: return "Filename too long";
+ case E_POSIX_ENETDOWN: return "Network is down";
+ case E_POSIX_ENETRESET: return "Connection aborted by network";
+ case E_POSIX_ENETUNREACH: return "Network unreachable";
+ case E_POSIX_ENFILE: return "Too many files open in system";
+ case E_POSIX_ENOBUFS: return "No buffer space available";
+ case E_POSIX_ENODEV: return "No such device";
+ case E_POSIX_ENOENT: return "No such file or directory";
+ case E_POSIX_ENOEXEC: return "Executable file format error";
+ case E_POSIX_ENOLCK: return "No locks available";
+ case E_POSIX_ENOLINK: return "Reserved";
+ case E_POSIX_ENOMEM: return "Not enough space";
+ case E_POSIX_ENOMSG: return "No message of the desired type";
+ case E_POSIX_ENOPROTOOPT: return "Protocol not available";
+ case E_POSIX_ENOSPC: return "No space left on device";
+ case E_POSIX_ENOSYS: return "Functionality not supported";
+ case E_POSIX_ENOTCONN: return "The socket is not connected";
+ case E_POSIX_ENOTDIR: return "Not a directory or a symbolic link to a directory";
+ case E_POSIX_ENOTEMPTY: return "Directory not empty";
+ case E_POSIX_ENOTRECOVERABLE: return "State not recoverable";
+ case E_POSIX_ENOTSOCK: return "Not a socket";
+ case E_POSIX_ENOTSUP: return "Not supported";
+ case E_POSIX_ENOTTY: return "Inappropriate I/O control operation";
+ case E_POSIX_ENXIO: return "No such device or address";
+ case E_POSIX_EOPNOTSUPP: return "Operation not supported on socket";
+ case E_POSIX_EOVERFLOW: return "Value too large to be stored in data type";
+ case E_POSIX_EOWNERDEAD: return "Previous owner died";
+ case E_POSIX_EPERM: return "Operation not permitted";
+ case E_POSIX_EPIPE: return "Broken pipe";
+ case E_POSIX_EPROTO: return "Protocol error";
+ case E_POSIX_EPROTONOSUPPORT: return "Protocol not supported";
+ case E_POSIX_EPROTOTYPE: return "Protocol wrong type for socket";
+ case E_POSIX_ERANGE: return "Result too large";
+ case E_POSIX_EROFS: return "Read-only file system";
+ case E_POSIX_ESOCKTNOSUPPORT: return "Socket type not supported";
+ case E_POSIX_ESPIPE: return "Invalid seek";
+ case E_POSIX_ESRCH: return "No such process";
+ case E_POSIX_ESTALE: return "Reserved";
+ case E_POSIX_ETIMEDOUT: return "Connection timed out";
+ case E_POSIX_ETXTBSY: return "Text file busy";
+ case E_POSIX_EWOULDBLOCK: return "Operation would block";
+ case E_POSIX_EXDEV: return "Improper hard link";
+ case E_EUNKNOWN:
+ default:
+ return "unknown error";
+ }
+}
diff --git a/libmisc/error_generated.c.gen b/libmisc/error_generated.c.gen
new file mode 100755
index 0000000..944d909
--- /dev/null
+++ b/libmisc/error_generated.c.gen
@@ -0,0 +1,35 @@
+#!/usr/bin/env bash
+# libmisc/error_generated.c.gen - Generate _errnum strings
+#
+# Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+# SPDX-License-Identifier: AGPL-3.0-or-later
+
+error_h=$1
+outfile=$2
+
+{
+ echo "/* ${outfile} - Generated by $0. DO NOT EDIT! */"
+ echo
+ echo '#include <libmisc/error.h>'
+ echo
+ echo 'const char *_errnum_str_sym(_errnum errnum) {'
+ echo $'\tswitch (errnum) {'
+ sed -nE \
+ -e 's@^(#define)?\s+(E_[_A-Z0-9]+)[ ,][^/]*/\* ([^*(]+) (\*/|\().*@'$'\tcase \\2: return "\\2";''@p' \
+ -- "$error_h"
+ echo $'\tcase E_EUNKNOWN: return "E_EUNKNOWN";'
+ echo $'\tdefault: return "E_<invalid>";'
+ echo $'\t}'
+ echo '}'
+ echo
+ echo 'const char *_errnum_str_msg(_errnum errnum) {'
+ echo $'\tswitch (errnum) {'
+ sed -nE \
+ -e 's@^(#define)?\s+(E_[_A-Z0-9]+)[ ,][^/]*/\* ([^*(]+) (\*/|\().*@'$'\tcase \\2: return "\\3";''@p' \
+ -- "$error_h"
+ echo $'\tcase E_EUNKNOWN:'
+ echo $'\tdefault:'
+ echo $'\t\treturn "unknown error";'
+ echo $'\t}'
+ echo '}'
+} >"$outfile"
diff --git a/libmisc/fmt.c b/libmisc/fmt.c
new file mode 100644
index 0000000..c78be8c
--- /dev/null
+++ b/libmisc/fmt.c
@@ -0,0 +1,266 @@
+/* libmisc/fmt.c - Write formatted text
+ *
+ * Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#include <string.h> /* for strnlen() */
+
+#include <libmisc/utf8.h>
+
+#include <libmisc/fmt.h>
+
+static const char *const hexdig = "0123456789ABCDEF";
+
+/* small/trivial formatters ***************************************************/
+
+void fmt_print_mem(lo_interface fmt_dest w, const void *_str, size_t size) {
+ const uint8_t *str = _str;
+ while (size--)
+ fmt_print_byte(w, *(str++));
+}
+void fmt_print_str(lo_interface fmt_dest w, const char *str) {
+ while (*str)
+ fmt_print_byte(w, *(str++));
+}
+void fmt_print_strn(lo_interface fmt_dest w, const char *str, size_t size) {
+ while (size-- && *str)
+ fmt_print_byte(w, *(str++));
+}
+
+void fmt_print_hmem(lo_interface fmt_dest w, const void *_str, size_t size) {
+ const uint8_t *str = _str;
+ fmt_print_byte(w, '{');
+ for (size_t i = 0; i < size; i++) {
+ if (i)
+ fmt_print_byte(w, ',');
+ fmt_print_hbyte(w, str[i]);
+ }
+ fmt_print_byte(w, '}');
+}
+
+void fmt_print_byte(lo_interface fmt_dest w, uint8_t b) {
+ LO_CALL(w, putb, b);
+}
+
+void fmt_print_bool(lo_interface fmt_dest w, bool b) {
+ fmt_print_str(w, b ? "true" : "false");
+}
+
+void fmt_print_base16_u8_(lo_interface fmt_dest w, uint8_t x) {
+ fmt_print(w, "0x", (rjust, 2, '0', (base16, x)));
+}
+void fmt_print_base16_u16_(lo_interface fmt_dest w, uint16_t x) {
+ fmt_print(w, "0x", (rjust, 4, '0', (base16, x)));
+}
+void fmt_print_base16_u32_(lo_interface fmt_dest w, uint32_t x) {
+ fmt_print(w, "0x", (rjust, 8, '0', (base16, x)));
+}
+void fmt_print_base16_u64_(lo_interface fmt_dest w, uint64_t x) {
+ fmt_print(w, "0x", (rjust, 16, '0', (base16, x)));
+}
+
+void fmt_print_ptr(lo_interface fmt_dest w, const void *ptr) {
+ LM_CAT3_(fmt_print_base16_u, __INTPTR_WIDTH__, _)(w, (uintptr_t)ptr);
+}
+
+/* quote **********************************************************************/
+
+/**
+ * Quote a byte to ASCII-only C syntax.
+ */
+void fmt_print_qbyte(lo_interface fmt_dest w, uint8_t b) {
+ fmt_print_byte(w, '\'');
+ if (b == '\0' ||
+ b == '\b' ||
+ b == '\f' ||
+ b == '\n' ||
+ b == '\r' ||
+ b == '\t' ||
+ b == '\v' ||
+ b == '\\' ||
+ b == '\'' ||
+ b == '"' ||
+ b == '?') {
+ fmt_print_byte(w, '\\');
+ switch (b) {
+ case '\0': fmt_print_byte(w, '0'); break;
+ case '\a': fmt_print_byte(w, 'a'); break;
+ case '\b': fmt_print_byte(w, 'b'); break;
+ case '\f': fmt_print_byte(w, 'f'); break;
+ case '\n': fmt_print_byte(w, 'n'); break;
+ case '\r': fmt_print_byte(w, 'r'); break;
+ case '\t': fmt_print_byte(w, 't'); break;
+ case '\v': fmt_print_byte(w, 'v'); break;
+ case '\\': fmt_print_byte(w, '\\'); break;
+ case '\'': fmt_print_byte(w, '\''); break;
+ case '"': fmt_print_byte(w, '"'); break;
+ case '?': fmt_print_byte(w, '?'); break;
+ }
+ } else if (' ' <= b && b <= '~') {
+ fmt_print_byte(w, b);
+ } else {
+ fmt_print_byte(w, '\\');
+ fmt_print_byte(w, 'x');
+ fmt_print_byte(w, hexdig[(b >> 4) & 0xF]);
+ fmt_print_byte(w, hexdig[(b >> 0) & 0xF]);
+ }
+ fmt_print_byte(w, '\'');
+}
+
+/**
+ * Quote a region of memory to ASCII-only C string syntax. Valid
+ * UTF-8 is quoted as short C-escape characters, \uABCD, or
+ * \UABCDABCD; invalid UTF-8 is quoted as \xAB.
+ */
+void fmt_print_qmem(lo_interface fmt_dest w, const void *_str, size_t size) {
+ const uint8_t *str = _str;
+ fmt_print_byte(w, '"');
+ for (size_t pos = 0; pos < size;) {
+ uint32_t ch;
+ uint8_t chlen;
+ utf8_decode_codepoint(&str[pos], size-pos, &ch, &chlen);
+ if (!chlen) {
+ /* invalid UTF-8 */
+ /* \xAB */
+ fmt_print_byte(w, '\\');
+ fmt_print_byte(w, 'x');
+ fmt_print_byte(w, hexdig[(str[pos] >> 4) & 0xF]);
+ fmt_print_byte(w, hexdig[(str[pos] >> 0) & 0xF]);
+ pos++;
+ continue;
+ }
+ if (ch == '\0' ||
+ ch == '\b' ||
+ ch == '\f' ||
+ ch == '\n' ||
+ ch == '\r' ||
+ ch == '\t' ||
+ ch == '\v' ||
+ ch == '\\' ||
+ ch == '\'' ||
+ ch == '"' ||
+ ch == '?') {
+ /* short C-escape */
+ fmt_print_byte(w, '\\');
+ switch (ch) {
+ case '\0': fmt_print_byte(w, '0'); break;
+ case '\a': fmt_print_byte(w, 'a'); break;
+ case '\b': fmt_print_byte(w, 'b'); break;
+ case '\f': fmt_print_byte(w, 'f'); break;
+ case '\n': fmt_print_byte(w, 'n'); break;
+ case '\r': fmt_print_byte(w, 'r'); break;
+ case '\t': fmt_print_byte(w, 't'); break;
+ case '\v': fmt_print_byte(w, 'v'); break;
+ case '\\': fmt_print_byte(w, '\\'); break;
+ case '\'': fmt_print_byte(w, '\''); break;
+ case '"': fmt_print_byte(w, '"'); break;
+ case '?': fmt_print_byte(w, '?'); break;
+ }
+ } else if (' ' <= ch && ch <= '~') {
+ /* no escaping */
+ fmt_print_byte(w, ch);
+ } else if (ch < 0x10000) {
+ /* \uABCD */
+ fmt_print_byte(w, '\\');
+ fmt_print_byte(w, 'u');
+ fmt_print_byte(w, hexdig[(ch >> 12) & 0xF]);
+ fmt_print_byte(w, hexdig[(ch >> 8) & 0xF]);
+ fmt_print_byte(w, hexdig[(ch >> 4) & 0xF]);
+ fmt_print_byte(w, hexdig[(ch >> 0) & 0xF]);
+ } else {
+ /* \UABCDABCD */
+ fmt_print_byte(w, '\\');
+ fmt_print_byte(w, 'U');
+ fmt_print_byte(w, hexdig[(ch >> 28) & 0xF]);
+ fmt_print_byte(w, hexdig[(ch >> 24) & 0xF]);
+ fmt_print_byte(w, hexdig[(ch >> 20) & 0xF]);
+ fmt_print_byte(w, hexdig[(ch >> 16) & 0xF]);
+ fmt_print_byte(w, hexdig[(ch >> 12) & 0xF]);
+ fmt_print_byte(w, hexdig[(ch >> 8) & 0xF]);
+ fmt_print_byte(w, hexdig[(ch >> 4) & 0xF]);
+ fmt_print_byte(w, hexdig[(ch >> 0) & 0xF]);
+ }
+ pos += chlen;
+ }
+ fmt_print_byte(w, '"');
+}
+
+void fmt_print_qstr(lo_interface fmt_dest w, const char *str) {
+ fmt_print_qmem(w, str, strlen(str));
+}
+
+void fmt_print_qstrn(lo_interface fmt_dest w, const char *str, size_t n) {
+ fmt_print_qmem(w, str, strnlen(str, n));
+}
+
+/* int ************************************************************************/
+
+#define declare(BASE, BITS) \
+ void _fmt_print_base##BASE##_s##BITS(lo_interface fmt_dest w, \
+ int##BITS##_t val) { \
+ if (val < 0) { \
+ fmt_print_byte(w, '-'); \
+ val = -val; \
+ } \
+ _fmt_print_base##BASE##_u##BITS(w, (uint##BITS##_t)val); \
+ } \
+ \
+ void _fmt_print_base##BASE##_u##BITS(lo_interface fmt_dest w, \
+ uint##BITS##_t absval) { \
+ /* This digit-counting is O(log(absval)); there are \
+ * `__builtin_clz`-based O(1) ways to do this, but when I \
+ * tried them they bloated the code-size too much. And this \
+ * function as a whole is already O(log(absval)) anyway \
+ * because of actually printing the digits. */ \
+ unsigned ndigits = 1; \
+ uint##BITS##_t div = 1; \
+ while (absval / div >= BASE) { \
+ div *= BASE; \
+ ndigits++; \
+ } \
+ \
+ for (unsigned i = 0; i < ndigits; i++) { \
+ unsigned digit = (unsigned) (absval / div); \
+ absval %= div; \
+ div /= BASE; \
+ fmt_print_byte(w, hexdig[digit]); \
+ } \
+ } \
+ LM_FORCE_SEMICOLON
+
+declare(2, 8);
+declare(2, 16);
+declare(2, 32);
+declare(2, 64);
+
+declare(8, 8);
+declare(8, 16);
+declare(8, 32);
+declare(8, 64);
+
+declare(10, 8);
+declare(10, 16);
+declare(10, 32);
+declare(10, 64);
+
+declare(16, 8);
+declare(16, 16);
+declare(16, 32);
+declare(16, 64);
+
+#undef declare
+
+/* fmt_buf ********************************************************************/
+
+LO_IMPLEMENTATION_C(fmt_dest, struct fmt_buf, fmt_buf);
+
+void fmt_buf_putb(struct fmt_buf *buf, uint8_t b) {
+ if (buf->len < buf->cap)
+ ((uint8_t *)(buf->dat))[buf->len] = b;
+ buf->len++;
+}
+
+size_t fmt_buf_tell(struct fmt_buf *buf) {
+ return buf->len;
+}
diff --git a/libmisc/hash.c b/libmisc/hash.c
new file mode 100644
index 0000000..3814cec
--- /dev/null
+++ b/libmisc/hash.c
@@ -0,0 +1,24 @@
+/* libmisc/hash.c - General-purpose hash utilities
+ *
+ * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#include <libmisc/hash.h>
+
+/* djb2 hash */
+void hash_init(hash_t *hash) {
+ *hash = 5381;
+}
+void hash_write(hash_t *hash, void *dat, size_t len) {
+ for (size_t i = 0; i < len; i++)
+ *hash = (*hash * 33) + (hash_t)(((unsigned char *)dat)[i]);
+}
+
+/* utilities */
+hash_t hash(void *dat, size_t len) {
+ hash_t h;
+ hash_init(&h);
+ hash_write(&h, dat, len);
+ return h;
+}
diff --git a/libmisc/include/libmisc/_intercept.h b/libmisc/include/libmisc/_intercept.h
index ab76857..fa327d6 100644
--- a/libmisc/include/libmisc/_intercept.h
+++ b/libmisc/include/libmisc/_intercept.h
@@ -7,21 +7,18 @@
#ifndef _LIBMISC__INTERCEPT_H_
#define _LIBMISC__INTERCEPT_H_
-/* pico-sdk/newlib define these to be [[gnu:weak]] already, but
+/* pico-sdk/newlib defines abort() to be [[gnu::weak]] already, but
* depending on optimization options glibc might not, and GCC might
- * assume it knows what they do and optimize them out. So define our
- * own `__lm_` wrappers that GCC/glibc won't interfere with.
+ * assume it knows what it does and optimize it out. So define our
+ * own `__lm_` wrapper that GCC/glibc won't interfere with.
*/
-[[format(printf, 1, 2)]]
-int __lm_printf(const char *format, ...);
-
[[noreturn]] void __lm_abort(void);
-/* __lm_light_printf is expected to have less stack use than regular
- * __lm_printf, if possible. */
+/* While newlib defines putchar() to be [[gnu::weak]], pico_stdio does
+ * not. Plus the above about optimizations.
+ */
-[[format(printf, 1, 2)]]
-int __lm_light_printf(const char *format, ...);
+void __lm_putchar(unsigned char c);
#endif /* _LIBMISC__INTERCEPT_H_ */
diff --git a/libmisc/include/libmisc/alloc.h b/libmisc/include/libmisc/alloc.h
new file mode 100644
index 0000000..d8bbc38
--- /dev/null
+++ b/libmisc/include/libmisc/alloc.h
@@ -0,0 +1,41 @@
+/* libmisc/alloc.h - Type-safe wrappers around alloca and malloc
+ *
+ * Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#ifndef _LIBMISC_ALLOC_H_
+#define _LIBMISC_ALLOC_H_
+
+#include <alloca.h> /* for alloca() */
+#include <stdlib.h> /* for calloc(), free() */
+#include <string.h> /* for memset() */
+
+#include <libmisc/assert.h>
+
+#define stack_alloc(N, TYP) ({ \
+ size_t _size; \
+ TYP *_ret = NULL; \
+ if (!__builtin_mul_overflow(N, sizeof(TYP), &_size)) { \
+ _ret = alloca(_size); \
+ memset(_ret, 0, _size); \
+ } \
+ assert(_ret); \
+ _ret; \
+})
+
+#define heap_alloc(N, TYP) ({ \
+ TYP *_ret = calloc(N, sizeof(TYP)); \
+ assert(_ret); \
+ _ret; \
+})
+
+static inline void heap_cleanup(void *_ptrptr) {
+ void **ptrptr = _ptrptr;
+ if (!ptrptr)
+ return;
+ free(*ptrptr);
+ *ptrptr = NULL;
+}
+
+#endif /* _LIBMISC_ALLOC_H_ */
diff --git a/libmisc/include/libmisc/assert.h b/libmisc/include/libmisc/assert.h
index 8cf0735..f726046 100644
--- a/libmisc/include/libmisc/assert.h
+++ b/libmisc/include/libmisc/assert.h
@@ -1,24 +1,36 @@
/* libmisc/assert.h - More assertions
*
- * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
#ifndef _LIBMISC_ASSERT_H_
#define _LIBMISC_ASSERT_H_
+#define __assert_no_type_limits(EXPR) \
+ _Pragma("GCC diagnostic push") \
+ _Pragma("GCC diagnostic ignored \"-Wtype-limits\"") \
+ EXPR \
+ _Pragma("GCC diagnostic pop")
+
+
#ifdef NDEBUG
# define __assert_msg(expr, expr_str, msg) ((void)0)
#else
-# define __assert_msg(expr, expr_str, msg) do { if (!(expr)) __assert_msg_fail(expr_str, __FILE__, __LINE__, __func__, msg); } while (0)
+# define __assert_msg(expr, expr_str, msg) \
+ do { \
+ __assert_no_type_limits(if (!(expr))) \
+ __assert_msg_fail(expr_str, __FILE__, __LINE__, __func__, msg); \
+ } while (0)
[[noreturn]] void __assert_msg_fail(const char *expr,
const char *file, unsigned int line, const char *func,
const char *msg);
#endif
-#define assert_msg(expr, msg) __assert_msg(expr, #expr, msg) /* libmisc */
-#define assert(expr) __assert_msg(expr, #expr, 0) /* C89, POSIX-2001 */
-#define assert_notreached(msg) do { __assert_msg(0, "notreached", msg); __builtin_unreachable(); } while (0) /* libmisc */
-#define static_assert _Static_assert /* C11 */
+#define assert_msg(expr, msg) __assert_msg(expr, #expr, msg) /* libmisc */
+#define assert(expr) __assert_msg(expr, #expr, 0) /* C89, POSIX-2001 */
+#define assert_notreached(msg) do { __assert_msg(0, "notreached", msg); __builtin_unreachable(); } while (0) /* libmisc */
+#define static_assert(...) __assert_no_type_limits(_Static_assert(__VA_ARGS__);) _Static_assert(1, "force semicolon") /* C11 */
+#define static_assert_as_expr(...) (sizeof(struct {static_assert(__VA_ARGS__);})) /* libmisc */
#endif /* _LIBMISC_ASSERT_H_ */
diff --git a/libmisc/include/libmisc/endian.h b/libmisc/include/libmisc/endian.h
index 75240fe..966c3bc 100644
--- a/libmisc/include/libmisc/endian.h
+++ b/libmisc/include/libmisc/endian.h
@@ -10,204 +10,25 @@
#include <stddef.h> /* for size_t */
#include <stdint.h> /* for uint{n}_t */
-#include <libmisc/assert.h>
-
-/* Big endian *****************************************************************/
-
-typedef struct {
- uint8_t octets[2];
-} uint16be_t;
-static_assert(sizeof(uint16be_t) == 2);
-
-static inline size_t uint16be_encode(uint8_t *out, uint16_t in) {
- out[0] = (uint8_t)((in >> 8) & 0xFF);
- out[1] = (uint8_t)((in >> 0) & 0xFF);
- return 2;
-}
-
-static inline uint16_t uint16be_decode(uint8_t *in) {
- return (((uint16_t)(in[0])) << 8)
- | (((uint16_t)(in[1])) << 0)
- ;
-}
-
-static inline uint16be_t uint16be_marshal(uint16_t in) {
- uint16be_t out;
- uint16be_encode(out.octets, in);
- return out;
-}
-
-static inline uint16_t uint16be_unmarshal(uint16be_t in) {
- return uint16be_decode(in.octets);
-}
-
-typedef struct {
- uint8_t octets[4];
-} uint32be_t;
-static_assert(sizeof(uint32be_t) == 4);
-
-static inline size_t uint32be_encode(uint8_t *out, uint32_t in) {
- out[0] = (uint8_t)((in >> 24) & 0xFF);
- out[1] = (uint8_t)((in >> 16) & 0xFF);
- out[2] = (uint8_t)((in >> 8) & 0xFF);
- out[3] = (uint8_t)((in >> 0) & 0xFF);
- return 4;
-}
-
-static inline uint32_t uint32be_decode(uint8_t *in) {
- return (((uint32_t)(in[0])) << 24)
- | (((uint32_t)(in[1])) << 16)
- | (((uint32_t)(in[2])) << 8)
- | (((uint32_t)(in[3])) << 0)
- ;
-}
-
-static inline uint32be_t uint32be_marshal(uint32_t in) {
- uint32be_t out;
- uint32be_encode(out.octets, in);
- return out;
-}
-
-static inline uint32_t uint32be_unmarshal(uint32be_t in) {
- return uint32be_decode(in.octets);
-}
-
-typedef struct {
- uint8_t octets[8];
-} uint64be_t;
-static_assert(sizeof(uint64be_t) == 8);
-
-static inline size_t uint64be_encode(uint8_t *out, uint64_t in) {
- out[0] = (uint8_t)((in >> 56) & 0xFF);
- out[1] = (uint8_t)((in >> 48) & 0xFF);
- out[2] = (uint8_t)((in >> 40) & 0xFF);
- out[3] = (uint8_t)((in >> 32) & 0xFF);
- out[4] = (uint8_t)((in >> 24) & 0xFF);
- out[5] = (uint8_t)((in >> 16) & 0xFF);
- out[6] = (uint8_t)((in >> 8) & 0xFF);
- out[7] = (uint8_t)((in >> 0) & 0xFF);
- return 8;
-}
-
-static inline uint64_t uint64be_decode(uint8_t *in) {
- return (((uint64_t)(in[0])) << 56)
- | (((uint64_t)(in[1])) << 48)
- | (((uint64_t)(in[2])) << 40)
- | (((uint64_t)(in[3])) << 32)
- | (((uint64_t)(in[4])) << 24)
- | (((uint64_t)(in[5])) << 16)
- | (((uint64_t)(in[6])) << 8)
- | (((uint64_t)(in[7])) << 0)
- ;
-}
-
-static inline uint64be_t uint64be_marshal(uint64_t in) {
- uint64be_t out;
- uint64be_encode(out.octets, in);
- return out;
-}
-
-static inline uint64_t uint64be_unmarshal(uint64be_t in) {
- return uint64be_decode(in.octets);
-}
-
-/* Little endian **************************************************************/
-
-typedef struct {
- uint8_t octets[2];
-} uint16le_t;
-static_assert(sizeof(uint16le_t) == 2);
-
-static inline size_t uint16le_encode(uint8_t *out, uint16_t in) {
- out[0] = (uint8_t)((in >> 0) & 0xFF);
- out[1] = (uint8_t)((in >> 8) & 0xFF);
- return 2;
-}
-
-static inline uint16_t uint16le_decode(uint8_t *in) {
- return (((uint16_t)(in[0])) << 0)
- | (((uint16_t)(in[1])) << 8)
- ;
-}
-
-static inline uint16le_t uint16le_marshal(uint16_t in) {
- uint16le_t out;
- uint16le_encode(out.octets, in);
- return out;
-}
-
-static inline uint16_t uint16le_unmarshal(uint16le_t in) {
- return uint16le_decode(in.octets);
-}
-
-typedef struct {
- uint8_t octets[4];
-} uint32le_t;
-static_assert(sizeof(uint32le_t) == 4);
-
-static inline size_t uint32le_encode(uint8_t *out, uint32_t in) {
- 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);
- return 4;
-}
-
-static inline uint32_t uint32le_decode(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 uint32le_t uint32le_marshal(uint32_t in) {
- uint32le_t out;
- uint32le_encode(out.octets, in);
- return out;
-}
-
-static inline uint32_t uint32le_unmarshal(uint32le_t in) {
- return uint32le_decode(in.octets);
-}
-
-typedef struct {
- uint8_t octets[8];
-} uint64le_t;
-static_assert(sizeof(uint64le_t) == 8);
-
-static inline size_t uint64le_encode(uint8_t *out, uint64_t in) {
- 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);
- return 8;
-}
-
-static inline uint64_t uint64le_decode(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 uint64le_t uint64le_marshal(uint64_t in) {
- uint64le_t out;
- uint64le_encode(out.octets, in);
- return out;
-}
-
-static inline uint64_t uint64le_unmarshal(uint64le_t in) {
- return uint64le_decode(in.octets);
-}
+#define _endian_declare_conv(NBIT, ENDIAN) \
+ /* byte array encode/decode */ \
+ size_t uint##NBIT##ENDIAN##_encode(uint8_t *out, uint##NBIT##_t in); \
+ uint##NBIT##_t uint##NBIT##ENDIAN##_decode(uint8_t *in); \
+ /* struct marshal/unmarshal */ \
+ typedef struct { \
+ uint8_t octets[NBIT/8]; \
+ } uint##NBIT##ENDIAN##_t; \
+ uint##NBIT##ENDIAN##_t uint##NBIT##ENDIAN##_marshal(uint##NBIT##_t in); \
+ uint##NBIT##_t uint##NBIT##ENDIAN##_unmarshal(uint##NBIT##ENDIAN##_t in)
+
+_endian_declare_conv(16, be);
+_endian_declare_conv(32, be);
+_endian_declare_conv(64, be);
+
+_endian_declare_conv(16, le);
+_endian_declare_conv(32, le);
+_endian_declare_conv(64, le);
+
+#undef _endian_declare_conv
#endif /* _LIBMISC_ENDIAN_H_ */
diff --git a/libmisc/include/libmisc/error.h b/libmisc/include/libmisc/error.h
new file mode 100644
index 0000000..8a64a9f
--- /dev/null
+++ b/libmisc/include/libmisc/error.h
@@ -0,0 +1,171 @@
+/* libmisc/error.h - Go-esque errors
+ *
+ * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#ifndef _LIBMISC_ERROR_H_
+#define _LIBMISC_ERROR_H_
+
+#include <stddef.h> /* for size_t */
+#include <stdint.h> /* for uint{n}_t */
+
+#include <libmisc/assert.h>
+#include <libmisc/fmt.h>
+
+/* _errnum ********************************************************************/
+
+typedef enum {
+ /* Original to libmisc */
+ E_NOERROR = 0, /* no error */
+ E_EOF, /* EOF */
+ E_NET_EARP_TIMEOUT, /* ARP timeout */
+ E_NET_EACK_TIMEOUT, /* TCP ACK timeout */
+ E_NET_ERECV_TIMEOUT, /* receive timeout */
+ E_NET_ECLOSED, /* already closed */
+ /* At least C99 through C23 */
+ E_STDC_EDOM,
+ E_STDC_EILSEQ,
+ E_STDC_ERANGE,
+ /* POSIX-2024 */
+ E_POSIX_E2BIG, /* Argument list too long */
+ E_POSIX_EACCES, /* Permission denied */
+ E_POSIX_EADDRINUSE, /* Address in use */
+ E_POSIX_EADDRNOTAVAIL, /* Address not available */
+ E_POSIX_EAFNOSUPPORT, /* Address family not supported */
+ E_POSIX_EAGAIN, /* Resource unavailable, try again (may be the same value as [EWOULDBLOCK]) */
+ E_POSIX_EALREADY, /* Connection already in progress */
+ E_POSIX_EBADF, /* Bad file descriptor */
+ E_POSIX_EBADMSG, /* Bad message */
+ E_POSIX_EBUSY, /* Device or resource busy */
+ E_POSIX_ECANCELED, /* Operation canceled */
+ E_POSIX_ECHILD, /* No child processes */
+ E_POSIX_ECONNABORTED, /* Connection aborted */
+ E_POSIX_ECONNREFUSED, /* Connection refused */
+ E_POSIX_ECONNRESET, /* Connection reset */
+ E_POSIX_EDEADLK, /* Resource deadlock would occur */
+ E_POSIX_EDESTADDRREQ, /* Destination address required */
+#define E_POSIX_EDOM E_STDC_EDOM /* Mathematics argument out of domain of function */
+ E_POSIX_EDQUOT, /* Reserved */
+ E_POSIX_EEXIST, /* File exists */
+ E_POSIX_EFAULT, /* Bad address */
+ E_POSIX_EFBIG, /* File too large */
+ E_POSIX_EHOSTUNREACH, /* Host is unreachable */
+ E_POSIX_EIDRM, /* Identifier removed */
+#define E_POSIX_EILSEQ E_STDC_EILSEQ /* Illegal byte sequence */ /* (in the context of text encoding) */
+ E_POSIX_EINPROGRESS, /* Operation in progress */
+ E_POSIX_EINTR, /* Interrupted function */
+ E_POSIX_EINVAL, /* Invalid argument */
+ E_POSIX_EIO, /* I/O error */
+ E_POSIX_EISCONN, /* Socket is connected */
+ E_POSIX_EISDIR, /* Is a directory */
+ E_POSIX_ELOOP, /* Too many levels of symbolic links */
+ E_POSIX_EMFILE, /* File descriptor value too large */
+ E_POSIX_EMLINK, /* Too many hard links */
+ E_POSIX_EMSGSIZE, /* Message too large */
+ E_POSIX_EMULTIHOP, /* Reserved */
+ E_POSIX_ENAMETOOLONG, /* Filename too long */
+ E_POSIX_ENETDOWN, /* Network is down */
+ E_POSIX_ENETRESET, /* Connection aborted by network */
+ E_POSIX_ENETUNREACH, /* Network unreachable */
+ E_POSIX_ENFILE, /* Too many files open in system */
+ E_POSIX_ENOBUFS, /* No buffer space available */
+ E_POSIX_ENODEV, /* No such device */
+ E_POSIX_ENOENT, /* No such file or directory */
+ E_POSIX_ENOEXEC, /* Executable file format error */
+ E_POSIX_ENOLCK, /* No locks available */
+ E_POSIX_ENOLINK, /* Reserved */
+ E_POSIX_ENOMEM, /* Not enough space */
+ E_POSIX_ENOMSG, /* No message of the desired type */
+ E_POSIX_ENOPROTOOPT, /* Protocol not available */
+ E_POSIX_ENOSPC, /* No space left on device */
+ E_POSIX_ENOSYS, /* Functionality not supported */
+ E_POSIX_ENOTCONN, /* The socket is not connected */
+ E_POSIX_ENOTDIR, /* Not a directory or a symbolic link to a directory */
+ E_POSIX_ENOTEMPTY, /* Directory not empty */
+ E_POSIX_ENOTRECOVERABLE, /* State not recoverable */ /* Added in POSIX-2008 */
+ E_POSIX_ENOTSOCK, /* Not a socket */
+ E_POSIX_ENOTSUP, /* Not supported (may be the same value as [EOPNOTSUPP]) */
+ E_POSIX_ENOTTY, /* Inappropriate I/O control operation */
+ E_POSIX_ENXIO, /* No such device or address */
+ E_POSIX_EOPNOTSUPP, /* Operation not supported on socket (may be the same value as [ENOTSUP]) */
+ E_POSIX_EOVERFLOW, /* Value too large to be stored in data type */
+ E_POSIX_EOWNERDEAD, /* Previous owner died */ /* Added in POSIX-2008 */
+ E_POSIX_EPERM, /* Operation not permitted */
+ E_POSIX_EPIPE, /* Broken pipe */
+ E_POSIX_EPROTO, /* Protocol error */
+ E_POSIX_EPROTONOSUPPORT, /* Protocol not supported */
+ E_POSIX_EPROTOTYPE, /* Protocol wrong type for socket */
+#define E_POSIX_ERANGE E_STDC_ERANGE /* Result too large */
+ E_POSIX_EROFS, /* Read-only file system */
+ E_POSIX_ESOCKTNOSUPPORT, /* Socket type not supported */
+ E_POSIX_ESPIPE, /* Invalid seek */
+ E_POSIX_ESRCH, /* No such process */
+ E_POSIX_ESTALE, /* Reserved */
+ E_POSIX_ETIMEDOUT, /* Connection timed out */
+ E_POSIX_ETXTBSY, /* Text file busy */
+ E_POSIX_EWOULDBLOCK, /* Operation would block (may be the same value as [EAGAIN]) */
+ E_POSIX_EXDEV, /* Improper hard link */
+ /* End cap */
+ E_EUNKNOWN,
+} _errnum;
+
+const char *_errnum_str_sym(_errnum);
+const char *_errnum_str_msg(_errnum);
+
+/* error **********************************************************************/
+
+typedef struct {
+ _errnum num;
+ char *_msg;
+} error;
+
+#ifdef NDEBUG
+#define error_new(ERRNUM, ...) ((error){ \
+ .num = ERRNUM , \
+ __VA_OPT__(._msg = fmt_asprint(__VA_ARGS__),) \
+})
+#else
+#define error_new(ERRNUM, ...) ((error){ \
+ .num = ERRNUM, \
+ ._msg = fmt_asprint("" __VA_OPT__(,) __VA_ARGS__), \
+})
+#endif
+
+#define ERROR_NULL ((error){})
+#define ERROR_IS_NULL(err) ((err).num == 0 && (err)._msg == NULL)
+
+const char *error_msg(error err);
+error error_dup(error err);
+void error_cleanup(error *errptr);
+void fmt_print_error(lo_interface fmt_dest w, error err);
+
+/* or_error ******************************************************************/
+
+#define DECLARE_ERROR_OR_(TYP, NAM) typedef struct { union { TYP NAM; error err; }; bool is_err; } NAM##_or_error
+#define DECLARE_ERROR_OR(TYP) DECLARE_ERROR_OR_(TYP, TYP)
+#define ERROR_NEW_VAL(TYP, VAL) ((TYP##_or_error){ .TYP = (VAL), .is_err = false })
+#define ERROR_NEW_ERR(TYP, ERR) ((TYP##_or_error){ .err = (ERR), .is_err = true })
+
+/* and_error *****************************************************************/
+
+#define DECLARE_ERROR_AND_(TYP, NAM) typedef struct { TYP NAM; error err; } NAM##_and_error
+#define DECLARE_ERROR_AND(TYP) DECLARE_ERROR_AND_(TYP, TYP)
+#define ERROR_AND(TYP, VAL, ERR) ((TYP##_and_error){ .TYP = (VAL), .err = (ERR) })
+
+/* some pre-defined types ****************************************************/
+
+DECLARE_ERROR_OR(size_t);
+DECLARE_ERROR_OR(uint8_t);
+DECLARE_ERROR_OR(uint16_t);
+DECLARE_ERROR_OR(uint32_t);
+DECLARE_ERROR_OR(uint64_t);
+DECLARE_ERROR_OR_(bool, ok);
+
+DECLARE_ERROR_AND(size_t);
+DECLARE_ERROR_AND(uint8_t);
+DECLARE_ERROR_AND(uint16_t);
+DECLARE_ERROR_AND(uint32_t);
+DECLARE_ERROR_AND(uint64_t);
+
+#endif /* _LIBMISC_ERROR_H_ */
diff --git a/libmisc/include/libmisc/fmt.h b/libmisc/include/libmisc/fmt.h
new file mode 100644
index 0000000..530fc63
--- /dev/null
+++ b/libmisc/include/libmisc/fmt.h
@@ -0,0 +1,163 @@
+/* libmisc/fmt.h - Write formatted text
+ *
+ * Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#ifndef _LIBMISC_FMT_H_
+#define _LIBMISC_FMT_H_
+
+#include <stddef.h> /* for size_t */
+#include <stdint.h> /* for (u)int{n}_t */
+#include <stdlib.h> /* for realloc() */
+
+#include <libmisc/macro.h>
+#include <libmisc/obj.h>
+
+/* destination interface ******************************************************/
+
+#define fmt_dest_LO_IFACE \
+ LO_FUNC(void , putb, uint8_t b) \
+ LO_FUNC(size_t, tell)
+LO_INTERFACE(fmt_dest);
+
+/* type-specific fmt_print_() functions ***************************************/
+
+/* Simple bytes. */
+void fmt_print_byte(lo_interface fmt_dest w, uint8_t b);
+void fmt_print_mem(lo_interface fmt_dest w, const void *str, size_t size);
+void fmt_print_str(lo_interface fmt_dest w, const char *str);
+void fmt_print_strn(lo_interface fmt_dest w, const char *str, size_t size);
+
+/* Quoted bytes. */
+void fmt_print_qbyte(lo_interface fmt_dest w, uint8_t b);
+void fmt_print_qmem(lo_interface fmt_dest w, const void *str, size_t size);
+void fmt_print_qstr(lo_interface fmt_dest w, const char *str);
+void fmt_print_qstrn(lo_interface fmt_dest w, const char *str, size_t size);
+
+/* Hex bytes. */
+#define fmt_print_hbyte fmt_print_base16_u8_
+void fmt_print_hmem(lo_interface fmt_dest w, const void *str, size_t size);
+
+/* Integers. */
+#define _fmt_declare_base(base) \
+ void _fmt_print_base##base##_u8(lo_interface fmt_dest w, uint8_t val); \
+ void _fmt_print_base##base##_u16(lo_interface fmt_dest w, uint16_t val); \
+ void _fmt_print_base##base##_u32(lo_interface fmt_dest w, uint32_t val); \
+ void _fmt_print_base##base##_u64(lo_interface fmt_dest w, uint64_t val); \
+ void _fmt_print_base##base##_s8(lo_interface fmt_dest w, int8_t val); \
+ void _fmt_print_base##base##_s16(lo_interface fmt_dest w, int16_t val); \
+ void _fmt_print_base##base##_s32(lo_interface fmt_dest w, int32_t val); \
+ void _fmt_print_base##base##_s64(lo_interface fmt_dest w, int64_t val); \
+ LM_FORCE_SEMICOLON
+_fmt_declare_base(2);
+_fmt_declare_base(8);
+_fmt_declare_base(10);
+_fmt_declare_base(16);
+#undef _fmt_declare_base
+
+#define _fmt_intswitch(val, prefix) _Generic((val) , \
+ unsigned char : LM_CAT3_(prefix, u, __SCHAR_WIDTH__) , \
+ unsigned short : LM_CAT3_(prefix, u, __SHRT_WIDTH__) , \
+ unsigned int : LM_CAT3_(prefix, u, __INT_WIDTH__) , \
+ unsigned long : LM_CAT3_(prefix, u, __LONG_WIDTH__) , \
+ unsigned long long : LM_CAT3_(prefix, u, __LONG_LONG_WIDTH__) , \
+ signed char : LM_CAT3_(prefix, s, __SCHAR_WIDTH__) , \
+ signed short : LM_CAT3_(prefix, s, __SHRT_WIDTH__) , \
+ signed int : LM_CAT3_(prefix, s, __INT_WIDTH__) , \
+ signed long : LM_CAT3_(prefix, s, __LONG_WIDTH__) , \
+ signed long long : LM_CAT3_(prefix, s, __LONG_LONG_WIDTH__) )
+#define fmt_print_base2(w, val) (_fmt_intswitch((val), _fmt_print_base2_)((w), (val)))
+#define fmt_print_base8(w, val) (_fmt_intswitch((val), _fmt_print_base8_)((w), (val)))
+#define fmt_print_base10(w, val) (_fmt_intswitch((val), _fmt_print_base10_)((w), (val)))
+#define fmt_print_base16(w, val) (_fmt_intswitch((val), _fmt_print_base16_)((w), (val)))
+
+/* Booleans. */
+void fmt_print_bool(lo_interface fmt_dest w, bool b);
+
+/* The high-level fmt_print() interface ***************************************/
+
+#define fmt_print(w, ...) do { LM_FOREACH_PARAM_(_fmt_param, (w), __VA_ARGS__) } while (0)
+#define _fmt_param(w, arg) \
+ LM_IF(LM_IS_TUPLE(arg))( \
+ _fmt_param_tuple LM_EAT() (w, LM_EXPAND arg) \
+ )( \
+ _fmt_param_nontuple(w, arg) \
+ );
+#define _fmt_param_tuple(w, fn, ...) fmt_print_##fn(w __VA_OPT__(,) __VA_ARGS__)
+#define _fmt_param_nontuple(w, val) _Generic((val), \
+ unsigned char : LM_CAT2_(_fmt_print_base10_u, __SCHAR_WIDTH__) , \
+ unsigned short : LM_CAT2_(_fmt_print_base10_u, __SHRT_WIDTH__) , \
+ unsigned int : LM_CAT2_(_fmt_print_base10_u, __INT_WIDTH__) , \
+ unsigned long : LM_CAT2_(_fmt_print_base10_u, __LONG_WIDTH__) , \
+ unsigned long long : LM_CAT2_(_fmt_print_base10_u, __LONG_LONG_WIDTH__) , \
+ signed char : LM_CAT2_(_fmt_print_base10_s, __SCHAR_WIDTH__) , \
+ signed short : LM_CAT2_(_fmt_print_base10_s, __SHRT_WIDTH__) , \
+ signed int : LM_CAT2_(_fmt_print_base10_s, __INT_WIDTH__) , \
+ signed long : LM_CAT2_(_fmt_print_base10_s, __LONG_WIDTH__) , \
+ signed long long : LM_CAT2_(_fmt_print_base10_s, __LONG_LONG_WIDTH__) , \
+ char * : fmt_print_str , \
+ const char * : fmt_print_str , \
+ bool : fmt_print_bool )(w, val)
+
+/** Same as fmt_print(), but usable from inside of fmt_print(). */
+#define fmt_print2(w, ...) do { LM_FOREACH_PARAM2_(_fmt_param2, (w), __VA_ARGS__) } while (0)
+#define _fmt_param2(...) _LM_DEFER2(_fmt_param_indirect)()(__VA_ARGS__)
+#define _fmt_param_indirect() _fmt_param
+
+/* print-to-memory ************************************************************/
+
+struct fmt_buf {
+ void *dat;
+ size_t len, cap;
+};
+LO_IMPLEMENTATION_H(fmt_dest, struct fmt_buf, fmt_buf);
+
+#define fmt_snprint(buf, n, ...) ({ \
+ struct fmt_buf _w = { .dat = buf, .cap = n }; \
+ lo_interface fmt_dest w = lo_box_fmt_buf_as_fmt_dest(&_w); \
+ fmt_print(w, __VA_ARGS__); \
+ if (_w.len < _w.cap) \
+ ((char *)_w.dat)[_w.len] = '\0'; \
+ _w.len; \
+})
+
+#define fmt_asprint(...) ({ \
+ struct fmt_buf _w = {}; \
+ lo_interface fmt_dest w = lo_box_fmt_buf_as_fmt_dest(&_w); \
+ fmt_print(w, __VA_ARGS__); \
+ while (_w.cap <= _w.len) { \
+ _w.cap = _w.len + 1; \
+ _w.len = 0; \
+ _w.dat = realloc(_w.dat, _w.cap); \
+ fmt_print(w, __VA_ARGS__); \
+ } \
+ ((char *)_w.dat)[_w.len] = '\0'; \
+ _w.dat; \
+})
+
+/* justify ********************************************************************/
+
+#define fmt_print_ljust(w, width, fillchar, ...) do { \
+ size_t beg = LO_CALL(w, tell); \
+ fmt_print2(w, __VA_ARGS__); \
+ while ((LO_CALL(w, tell) - beg) < width) \
+ fmt_print_byte(w, fillchar); \
+} while (0)
+
+#define fmt_print_rjust(w, width, fillchar, ...) do { \
+ struct fmt_buf _discard = {}; \
+ lo_interface fmt_dest discard = lo_box_fmt_buf_as_fmt_dest(&_discard); \
+ fmt_print2(discard, __VA_ARGS__); \
+ while (_discard.len++ < width) \
+ fmt_print_byte(w, fillchar); \
+ fmt_print2(w, __VA_ARGS__); \
+} while (0)
+
+void fmt_print_base16_u8_(lo_interface fmt_dest w, uint8_t x);
+void fmt_print_base16_u16_(lo_interface fmt_dest w, uint16_t x);
+void fmt_print_base16_u32_(lo_interface fmt_dest w, uint32_t x);
+void fmt_print_base16_u64_(lo_interface fmt_dest w, uint64_t x);
+void fmt_print_ptr(lo_interface fmt_dest w, const void *ptr);
+
+#endif /* _LIBMISC_FMT_H_ */
diff --git a/libmisc/include/libmisc/hash.h b/libmisc/include/libmisc/hash.h
index 91e6b10..029bd3b 100644
--- a/libmisc/include/libmisc/hash.h
+++ b/libmisc/include/libmisc/hash.h
@@ -1,31 +1,21 @@
/* libmisc/hash.h - General-purpose hash utilities
*
- * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
#ifndef _LIBMISC_HASH_H_
#define _LIBMISC_HASH_H_
-#include <stdint.h> /* for uint{n}_t */
#include <stddef.h> /* for size_t */
+#include <stdint.h> /* for uint{n}_t */
-/* djb2 hash */
-typedef uint32_t hash_t;
-static inline void hash_init(hash_t *hash) {
- *hash = 5381;
-}
-static inline void hash_write(hash_t *hash, void *dat, size_t len) {
- for (size_t i = 0; i < len; i++)
- *hash = (*hash * 33) + (hash_t)(((unsigned char *)dat)[i]);
-}
+/* base */
+typedef uint32_t hash_t; /* size subject to change */
+void hash_init(hash_t *hash);
+void hash_write(hash_t *hash, void *dat, size_t len);
/* utilities */
-static inline hash_t hash(void *dat, size_t len) {
- hash_t h;
- hash_init(&h);
- hash_write(&h, dat, len);
- return h;
-}
+hash_t hash(void *dat, size_t len);
#endif /* _LIBMISC_HASH_H_ */
diff --git a/libmisc/include/libmisc/linkedlist.h b/libmisc/include/libmisc/linkedlist.h
new file mode 100644
index 0000000..b6ff688
--- /dev/null
+++ b/libmisc/include/libmisc/linkedlist.h
@@ -0,0 +1,108 @@
+/* libmisc/linkedlist.h - Singly- and doubly- linked lists
+ *
+ * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#ifndef _LIBMISC_LINKEDLIST_H_
+#define _LIBMISC_LINKEDLIST_H_
+
+/* low-level (intrusive) singly linked list ***********************************/
+
+struct _slist_node {
+ struct _slist_node *rear;
+};
+
+struct _slist_root {
+ struct _slist_node *front, *rear;
+};
+
+void _slist_push_to_rear(struct _slist_root *root, struct _slist_node *node);
+void _slist_pop_from_front(struct _slist_root *root);
+
+/* low-level (intrusive) doubly linked list ***********************************/
+
+struct _dlist_node {
+ struct _dlist_node *front, *rear;
+};
+
+struct _dlist_root {
+ struct _dlist_node *front, *rear;
+};
+
+void _dlist_push_to_rear(struct _dlist_root *root, struct _dlist_node *node);
+void _dlist_remove(struct _dlist_root *root, struct _dlist_node *node);
+void _dlist_pop_from_front(struct _dlist_root *root);
+
+/* singly linked list (non-intrusive) *****************************************/
+
+#define SLIST_DECLARE(NAME) \
+ struct NAME##_node; \
+ struct NAME { \
+ struct NAME##_node *front, *rear; \
+ struct NAME *_slist_root_typ[0]; \
+ }
+
+#define SLIST_DECLARE_NODE(NAME, VAL_T) \
+ struct NAME##_node { \
+ struct NAME##_node *rear; \
+ VAL_T val; \
+ }
+
+#define slist_push_to_rear(ROOT, NODE) { \
+ /* These temporary variables are to get the compiler to check \
+ * the types. */ \
+ typeof(*(ROOT)->_slist_root_typ[0]) *_rootp = ROOT; \
+ typeof(*_rootp->front) *_nodep = NODE; \
+ _slist_push_to_rear((struct _slist_root *)_rootp, \
+ (struct _slist_node *)_nodep); \
+} while (0)
+
+#define slist_pop_from_front(ROOT) { \
+ /* This temporary variables are to get the compiler to check \
+ * the type. */ \
+ typeof(*(ROOT)->_slist_root_typ[0]) *_rootp = ROOT; \
+ _slist_pop_from_front((struct _slist_root *)_rootp); \
+} while (0)
+
+/* doubly linked list (non-intrusive) *****************************************/
+
+#define DLIST_DECLARE(NAME) \
+ struct NAME##_node; \
+ struct NAME { \
+ struct NAME##_node *front, *rear; \
+ struct NAME *_dlist_root_typ[0]; \
+ }
+
+#define DLIST_DECLARE_NODE(NAME, VAL_T) \
+ struct NAME##_node { \
+ struct NAME##_node *front, *rear; \
+ VAL_T val; \
+ }
+
+#define dlist_push_to_rear(ROOT, NODE) { \
+ /* These temporary variables are to get the compiler to check \
+ * the types. */ \
+ typeof(*(ROOT)->_dlist_root_typ[0]) *_rootp = ROOT; \
+ typeof(*_rootp->front) *_nodep = NODE; \
+ _dlist_push_to_rear((struct _dlist_root *)_rootp, \
+ (struct _dlist_node *)_nodep); \
+} while (0)
+
+#define dlist_remove(ROOT, NODE) { \
+ /* These temporary variables are to get the compiler to check \
+ * the types. */ \
+ typeof(*(ROOT)->_dlist_root_typ[0]) *_rootp = ROOT; \
+ typeof(*_rootp->front) *_nodep = NODE; \
+ _dlist_remove((struct _dlist_root *)_rootp, \
+ (struct _dlist_node *)_nodep); \
+} while (0)
+
+#define dlist_pop_from_front(ROOT) { \
+ /* This temporary variables are to get the compiler to check \
+ * the type. */ \
+ typeof(*(ROOT)->_dlist_root_typ[0]) *_rootp = ROOT; \
+ _dlist_pop_from_front((struct _dlist_root *)_rootp); \
+} while (0)
+
+#endif /* _LIBMISC_LINKEDLIST_H_ */
diff --git a/libmisc/include/libmisc/log.h b/libmisc/include/libmisc/log.h
index 79c0ab6..c40b642 100644
--- a/libmisc/include/libmisc/log.h
+++ b/libmisc/include/libmisc/log.h
@@ -9,8 +9,9 @@
#include <stdint.h> /* for uint8_t */
-#include <libmisc/macro.h>
#include <libmisc/_intercept.h>
+#include <libmisc/fmt.h>
+#include <libmisc/macro.h>
#ifdef NDEBUG
#define _LOG_NDEBUG 1
@@ -20,15 +21,17 @@
const char *const_byte_str(uint8_t b);
-#define n_errorf(nam, fmt, ...) do { __lm_printf("error: " LM_STR_(nam) ": " fmt "\n" __VA_OPT__(,) __VA_ARGS__); } while (0)
-#define n_infof(nam, fmt, ...) do { __lm_printf("info : " LM_STR_(nam) ": " fmt "\n" __VA_OPT__(,) __VA_ARGS__); } while (0)
-#define n_debugf(nam, fmt, ...) do { if (LM_CAT3_(CONFIG_, nam, _DEBUG) && !_LOG_NDEBUG) \
- __lm_printf("debug: " LM_STR_(nam) ": " fmt "\n" __VA_OPT__(,) __VA_ARGS__); } while (0)
+extern lo_interface fmt_dest _log_dest;
+
+#define log_n_errorln(nam, ...) fmt_print(_log_dest, "error: " LM_STR_(nam) ": ", __VA_ARGS__, "\n")
+#define log_n_infoln(nam, ...) fmt_print(_log_dest, "info : " LM_STR_(nam) ": ", __VA_ARGS__, "\n")
+#define log_n_debugln(nam, ...) do { if (LM_CAT3_(CONFIG_, nam, _DEBUG) && !_LOG_NDEBUG) \
+ fmt_print(_log_dest, "debug: " LM_STR_(nam) ": ", __VA_ARGS__, "\n"); } while (0)
#endif /* _LIBMISC_LOG_H_ */
-#if defined(LOG_NAME) && !defined(errorf)
-#define errorf(fmt, ...) n_errorf(LOG_NAME, fmt, __VA_ARGS__)
-#define infof(fmt, ...) n_infof(LOG_NAME, fmt, __VA_ARGS__)
-#define debugf(fmt, ...) n_debugf(LOG_NAME, fmt, __VA_ARGS__)
+#if defined(LOG_NAME) && !defined(log_errorln)
+#define log_errorln(...) log_n_errorln(LOG_NAME, __VA_ARGS__)
+#define log_infoln(...) log_n_infoln(LOG_NAME, __VA_ARGS__)
+#define log_debugln(...) log_n_debugln(LOG_NAME, __VA_ARGS__)
#endif
diff --git a/libmisc/include/libmisc/macro.h b/libmisc/include/libmisc/macro.h
index b3e235c..0f9fc3f 100644
--- a/libmisc/include/libmisc/macro.h
+++ b/libmisc/include/libmisc/macro.h
@@ -7,28 +7,53 @@
#ifndef _LIBMISC_MACRO_H_
#define _LIBMISC_MACRO_H_
-/* for function definitions */
+#include <libmisc/assert.h>
+
+/* C: syntax ******************************************************************/
+
+#define LM_FORCE_SEMICOLON _Static_assert(1, "force semicolon")
+
+#define LM_PARTIAL_SWITCH(VAL) \
+ _Pragma("GCC diagnostic push") \
+ _Pragma("GCC diagnostic ignored \"-Wswitch-enum\"") \
+ switch (VAL) \
+ _Pragma("GCC diagnostic pop") \
+
+/* C: function definitions ****************************************************/
#define LM_UNUSED(argname)
#define LM_ALWAYS_INLINE [[gnu::always_inline]] inline
#define LM_NEVER_INLINE [[gnu::noinline]]
#define LM_FLATTEN [[gnu::flatten]]
-/* numeric */
+/* C: types *******************************************************************/
+
+/* If it's a pointer instead of an array, then typeof(&ptr[0]) == typeof(ptr) */
+#define _LM_IS_ARRAY(ary) (!__builtin_types_compatible_p(typeof(&(ary)[0]), typeof(ary)))
+#define LM_ARRAY_LEN(ary) ( (sizeof(ary)/sizeof((ary)[0])) + static_assert_as_expr(_LM_IS_ARRAY(ary)) )
+
+#define LM_CAST_FIELD_TO_STRUCT(STRUCT_TYP, FIELD_NAME, PTR_TO_FIELD) ({ \
+ /* The _fptr assignment is to get the compiler to do type checking. */ \
+ typeof(((STRUCT_TYP *)NULL)->FIELD_NAME) *_fptr = (PTR_TO_FIELD); \
+ _fptr \
+ ? ((STRUCT_TYP*)( ((void*)_fptr) - offsetof(STRUCT_TYP, FIELD_NAME))) \
+ : NULL; \
+})
+
+/* C: numeric *****************************************************************/
-#define LM_ARRAY_LEN(ary) (sizeof(ary)/sizeof((ary)[0]))
#define LM_CEILDIV(n, d) ( ((n)+(d)-1) / (d) ) /** Return ceil(n/d) */
#define LM_ROUND_UP(n, d) ( LM_CEILDIV(n, d) * (d) ) /** Return `n` rounded up to the nearest multiple of `d` */
#define LM_ROUND_DOWN(n, d) ( ((n)/(d)) * (d) ) /** Return `n` rounded down to the nearest multiple of `d` */
#define LM_NEXT_POWER_OF_2(x) ( (x) ? 1ULL<<((sizeof(unsigned long long)*8)-__builtin_clzll(x)) : 1) /** Return the lowest power of 2 that is > x */
#define LM_FLOORLOG2(x) ((sizeof(unsigned long long)*8)-__builtin_clzll(x)-1) /** Return floor(log_2(x) */
-/* strings */
+/* CPP: strings ***************************************************************/
#define LM_STR(x) #x
#define LM_STR_(x) LM_STR(x)
-/* token pasting */
+/* CPP: token pasting *********************************************************/
#define LM_CAT2(a, b) a ## b
#define LM_CAT3(a, b, c) a ## b ## c
@@ -36,14 +61,18 @@
#define LM_CAT2_(a, b) LM_CAT2(a, b)
#define LM_CAT3_(a, b, c) LM_CAT3(a, b, c)
-/* macro arguments */
+/* CPP: macro arguments *******************************************************/
#define LM_FIRST(a, ...) a
#define LM_SECOND(a, b, ...) b
+
+#define LM_FIRST_(...) LM_FIRST(__VA_ARGS__)
+#define LM_SECOND_(...) LM_SECOND(__VA_ARGS__)
+
#define LM_EAT(...)
#define LM_EXPAND(...) __VA_ARGS__
-/* conditionals */
+/* CPP: conditionals **********************************************************/
#define LM_T xxTxx
#define LM_F xxFxx
@@ -55,42 +84,104 @@
#define _LM_IF__xxTxx(...) __VA_ARGS__ LM_EAT
#define _LM_IF__xxFxx(...) LM_EXPAND
-/* tuples */
+/* CPP: tuples ****************************************************************/
-#define LM_IS_TUPLE(x) LM_IS_SENTINEL(_LM_IS_TUPLE x)
-#define _LM_IS_TUPLE(...) LM_SENTINEL()
+#define LM_IS_TUPLE(x) LM_IS_SENTINEL(_LM_IS_TUPLE x)
+#define _LM_IS_TUPLE(...) LM_SENTINEL()
+/* For LM_IS_EMPTY_TUPLE:
+ *
+ * Given
+ *
+ * #define HELPER(...) B, __VA_OPT__(C,) D
+ *
+ * then evaluating the sequence of tokens `HELPER x , A` will give us a
+ * new sequence of tokens according to the following table:
+ *
+ * not a tuple : HELPER x , A
+ * tuple, nonempty: B , C , D , A
+ * tuple, empty : B , D , A
+ *
+ * Looking at this table, it is clear that we must look at the 2nd
+ * resulting comma-separated-value (argument), and set A=false,
+ * C=false, D=true (and B doesn't matter).
+ */
+#define LM_IS_EMPTY_TUPLE(x) LM_SECOND_(_LM_IS_EMPTY_TUPLE x, LM_F)
+#define _LM_IS_EMPTY_TUPLE(...) bogus, __VA_OPT__(LM_F,) LM_T
/* `tuples` is a sequence of `(tuple1)(tuple2)(tuple3)` */
-#define _LM_TUPLES_COMMA(tuple...) (tuple),
-#define LM_TUPLES_NONEMPTY(tuples) LM_IS_TUPLE(_LM_TUPLES_COMMA tuples)
+#define _LM_TUPLES_COMMA(...) (__VA_ARGS__),
+#define LM_TUPLES_IS_NONEMPTY(tuples) LM_IS_TUPLE(_LM_TUPLES_COMMA tuples)
#define LM_TUPLES_HEAD(tuples) LM_EXPAND(LM_FIRST LM_EAT() (_LM_TUPLES_COMMA tuples))
#define LM_TUPLES_TAIL(tuples) LM_EAT tuples
-/* iteration */
+/* CPP: iteration *************************************************************/
-/* BUG: LM_FOREACH_TUPLE maxes out at 1024 tuples. */
+/* The desire to support a high number of iterations is in competition
+ * with the desire for short compile times. 16 is the lowest
+ * power-of-2 for which the current code compiles. */
+#define _LM_EVAL _LM_EVAL__16
+#define _LM_EVAL__16(...) _LM_EVAL__8(_LM_EVAL__8(__VA_ARGS__))
+#define _LM_EVAL__8(...) _LM_EVAL__4(_LM_EVAL__4(__VA_ARGS__))
+#define _LM_EVAL__4(...) _LM_EVAL__2(_LM_EVAL__2(__VA_ARGS__))
+#define _LM_EVAL__2(...) _LM_EVAL__1(_LM_EVAL__1(__VA_ARGS__))
+#define _LM_EVAL__1(...) __VA_ARGS__
+
+#define _LM_DEFER2(macro) macro LM_EAT LM_EAT()()
+
+/**
+ * LM_FOREACH_PARAM(func, (fixedparams), params...) calls
+ * func(fixedparams..., param) for each param.
+ *
+ * BUG: LM_FOREACH_PARAM is limited to (16*2)-1=31 params.
+ */
+#define LM_FOREACH_PARAM(func, fixedparams, ...) _LM_EVAL(_LM_FOREACH_PARAM(func, fixedparams, __VA_ARGS__))
+#define _LM_FOREACH_PARAM(func, fixedparams, ...) _LM_FOREACH_PARAM_ITEM(func, fixedparams, __VA_ARGS__, ())
+#define _LM_FOREACH_PARAM_FIXEDPARAMS(fixedparams) _LM_FOREACH_PARAM_FIXEDPARAMS_inner fixedparams
+#define _LM_FOREACH_PARAM_FIXEDPARAMS_inner(...) __VA_ARGS__ __VA_OPT__(,)
+#define _LM_FOREACH_PARAM_ITEM(func, fixedparams, param, ...) \
+ LM_IF(LM_IS_EMPTY_TUPLE(param))()( \
+ _LM_DEFER2(func)(_LM_FOREACH_PARAM_FIXEDPARAMS(fixedparams) param) \
+ _LM_DEFER2(_LM_FOREACH_PARAM_ITEM_indirect)()(func, fixedparams, __VA_ARGS__) \
+ )
+#define _LM_FOREACH_PARAM_ITEM_indirect() _LM_FOREACH_PARAM_ITEM
+
+/** The same as LM_FOREACH_PARAM(), but callable from inside of LM_FOREACH_PARAM(). */
+#define LM_FOREACH_PARAM2(...) _LM_DEFER2(_LM_FOREACH_PARAM_ITEM_indirect)()(__VA_ARGS__, ())
+
+/** The same as above, but evaluates the arguments first. */
+#define LM_FOREACH_PARAM_(...) LM_FOREACH_PARAM(__VA_ARGS__)
+#define LM_FOREACH_PARAM2_(...) LM_FOREACH_PARAM2(__VA_ARGS__)
+
+/**
+ * LM_FOREACH_TUPLE( (tup1) (tup2) (tup3), func, args...) calls
+ * func(args..., tup...) for each tuple.
+ *
+ * BUG: LM_FOREACH_TUPLE is limited to (16*2)-1=31 tuples.
+ */
#define LM_FOREACH_TUPLE(tuples, func, ...) \
_LM_EVAL(_LM_FOREACH_TUPLE(tuples, func, __VA_ARGS__))
#define _LM_FOREACH_TUPLE(tuples, func, ...) \
- LM_IF(LM_TUPLES_NONEMPTY(tuples))( \
+ LM_IF(LM_TUPLES_IS_NONEMPTY(tuples))( \
_LM_DEFER2(func)(__VA_ARGS__ __VA_OPT__(,) LM_EXPAND LM_TUPLES_HEAD(tuples)) \
_LM_DEFER2(_LM_FOREACH_TUPLE_indirect)()(LM_TUPLES_TAIL(tuples), func, __VA_ARGS__) \
)()
#define _LM_FOREACH_TUPLE_indirect() _LM_FOREACH_TUPLE
-#define _LM_DEFER2(macro) macro LM_EAT LM_EAT()()
+/** The same as LM_FOREACH_TUPLE(), but callable from inside of LM_FOREACH_TUPLE(). */
+#define LM_FOREACH_TUPLE2(...) _LM_DEFER2(_LM_FOREACH_TUPLE_indirect)()(__VA_ARGS__)
-#define _LM_EVAL(...) _LM_EVAL__1024(__VA_ARGS__) /* 1024 iterations aught to be enough for anybody */
-#define _LM_EVAL__1024(...) _LM_EVAL__512(_LM_EVAL__512(__VA_ARGS__))
-#define _LM_EVAL__512(...) _LM_EVAL__256(_LM_EVAL__256(__VA_ARGS__))
-#define _LM_EVAL__256(...) _LM_EVAL__128(_LM_EVAL__128(__VA_ARGS__))
-#define _LM_EVAL__128(...) _LM_EVAL__64(_LM_EVAL__64(__VA_ARGS__))
-#define _LM_EVAL__64(...) _LM_EVAL__32(_LM_EVAL__32(__VA_ARGS__))
-#define _LM_EVAL__32(...) _LM_EVAL__16(_LM_EVAL__16(__VA_ARGS__))
-#define _LM_EVAL__16(...) _LM_EVAL__8(_LM_EVAL__8(__VA_ARGS__))
-#define _LM_EVAL__8(...) _LM_EVAL__4(_LM_EVAL__4(__VA_ARGS__))
-#define _LM_EVAL__4(...) _LM_EVAL__2(_LM_EVAL__2(__VA_ARGS__))
-#define _LM_EVAL__2(...) _LM_EVAL__1(_LM_EVAL__1(__VA_ARGS__))
-#define _LM_EVAL__1(...) __VA_ARGS__
+/* CPP: wrap-cc extensions ****************************************************/
+
+#ifdef __LIBMISC_ENHANCED_CPP__
+/**
+ * `LM_DEFAPPEND(macro, val)` is like `#define macro val`, but can (1)
+ * be used from inside of a macro, and (2) appends to a value if it is
+ * already defined with LM_DEFAPPEND. There are lots of edge-cases,
+ * don't get cute.
+ */
+#define LM_DEFAPPEND(macro, ...) __xx__LM_DEFAPPEND__xx__(#macro, #__VA_ARGS__) LM_FORCE_SEMICOLON
+#define LM_DEFAPPEND_(macro, ...) _LM_DEFAPPEND_(#macro, __VA_ARGS__)
+#define _LM_DEFAPPEND_(macrostr, ...) __xx__LM_DEFAPPEND__xx__(macrostr, #__VA_ARGS__) LM_FORCE_SEMICOLON
+#endif
#endif /* _LIBMISC_MACRO_H_ */
diff --git a/libmisc/include/libmisc/map.h b/libmisc/include/libmisc/map.h
new file mode 100644
index 0000000..65a71bc
--- /dev/null
+++ b/libmisc/include/libmisc/map.h
@@ -0,0 +1,145 @@
+/* libmisc/map.h - A map/dict data structure
+ *
+ * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#ifndef _LIBMISC_MAP_H_
+#define _LIBMISC_MAP_H_
+
+#include <stddef.h> /* for size_t */
+#include <stdint.h> /* for uint8_t */
+
+#include <libmisc/linkedlist.h>
+
+/* Type ***********************************************************************/
+
+DLIST_DECLARE(_map_kv_list);
+
+struct _map {
+ size_t len;
+ size_t nbuckets;
+ struct _map_kv_list *buckets;
+
+ unsigned int iterating;
+
+ size_t sizeof_kv;
+ size_t offsetof_k, sizeof_k;
+ size_t offsetof_v, sizeof_v;
+};
+
+/**
+ * MAP_DECLARE(MAPNAME, KEY_T, VAL_T) declares `struct MAPNAME`.
+ */
+#define MAP_DECLARE(MAPNAME, KEY_T, VAL_T) \
+ struct _##MAPNAME##_kv { \
+ uint8_t flags; \
+ KEY_T key; \
+ VAL_T val; \
+ }; \
+ DLIST_DECLARE_NODE(_##MAPNAME##_kv_list, struct _##MAPNAME##_kv); \
+ struct MAPNAME { \
+ struct _map core; \
+ struct _##MAPNAME##_kv_list_node kv_typ[0]; \
+ }
+
+#define _map_init(M) do { \
+ if (!(M)->core.sizeof_kv) { \
+ (M)->core.sizeof_kv = sizeof((M)->kv_typ[0]); \
+ (M)->core.sizeof_k = sizeof((M)->kv_typ[0].val.key); \
+ (M)->core.sizeof_v = sizeof((M)->kv_typ[0].val.val); \
+ (M)->core.offsetof_k = offsetof(typeof((M)->kv_typ[0]), val.key); \
+ (M)->core.offsetof_v = offsetof(typeof((M)->kv_typ[0]), val.val); \
+ } \
+} while (0)
+
+/* Methods ********************************************************************/
+
+/**
+ * map_len(map) returns the number of entries currently in `map`.
+ */
+#define map_len(M) ((M)->core.len)
+
+/**
+ * map_load(map, key) returns a pointer to the value in `map`
+ * associated with `key`, or else NULL.
+ */
+#define map_load(M, K) ({ \
+ _map_init(M); \
+ typeof((M)->kv_typ[0].val.key) _k = (K); \
+ (typeof((M)->kv_typ[0].val.val)*)_map_load(&(M)->core, &_k); \
+})
+void *_map_load(struct _map *m, void *kp);
+
+/**
+ * map_del(map, key) ensures that `key` is not present in `map`.
+ * Returns whether `key` was in `map` before the call.
+ */
+#define map_del(M, K) ({ \
+ _map_init(M); \
+ typeof((M)->kv_typ[0].val.key) _k = (K); \
+ _map_del(&(M)->core, &_k); \
+})
+bool _map_del(struct _map *m, void *kp);
+
+/**
+ * map_store(map, key, val) sets a value in the map. Returns a
+ * pointer to the map's copy of `val`.
+ */
+#define map_store(M, K, ...) ({ \
+ _map_init(M); \
+ typeof((M)->kv_typ[0].val.key) _k = (K); \
+ typeof((M)->kv_typ[0].val.val) _v = (__VA_ARGS__); \
+ (typeof((M)->kv_typ[0].val.val)*)_map_store(&(M)->core, &_k, &_v); \
+})
+void *_map_store(struct _map *m, void *kp, void *vp);
+
+/**
+ * map_free(map) frees the memory associated with the map.
+ */
+#define map_free(M) _map_free(&(M)->core);
+void _map_free(struct _map *m);
+
+/* Iteration ******************************************************************/
+
+struct _map_iter {
+ struct _map *m;
+ void *keyp;
+ void **valpp;
+
+ size_t i;
+ struct _map_kv_list_node *kv;
+};
+
+/**
+ * MAP_FOREACH iterates over a map:
+ *
+ * MAP_FOREACH(MAP_EACH(MAP, key, valp)) {
+ * ...
+ * }
+ *
+ * It is safe to mutate the map with map_store() and map_del() while
+ * iterating, but entries added by map_store() may or may not be
+ * visited by the iteration.
+ */
+#define MAP_FOREACH(M, KNAME, VNAME) _MAP_FOREACH(__COUNTER__, M, KNAME, VNAME)
+#define _MAP_FOREACH(CNT, M, KNAME, VNAME) \
+ for (bool _once_##CNT = true; _once_##CNT;) \
+ for (typeof((M)->kv_typ[0].val.key) KNAME; _once_##CNT;) \
+ for (typeof((M)->kv_typ[0].val.val) *VNAME; _once_##CNT;) \
+ for ( \
+ struct _map_iter _iter_##CNT = ({ \
+ _map_init(M); \
+ _map_iter_before(&(M)->core, &KNAME, (void**)&VNAME); \
+ }); \
+ _once_##CNT; \
+ ({ \
+ _once_##CNT = false; \
+ _map_iter_after(&_iter_##CNT); \
+ })) \
+ while (_map_iter_next(&_iter_##CNT))
+struct _map_iter _map_iter_before(struct _map *m, void *keyp, void **valpp);
+bool _map_iter_next(struct _map_iter *iter);
+void _map_iter_after(struct _map_iter *iter);
+
+#endif /* _LIBMISC_MAP_H_ */
diff --git a/libmisc/include/libmisc/obj.h b/libmisc/include/libmisc/obj.h
new file mode 100644
index 0000000..c00e512
--- /dev/null
+++ b/libmisc/include/libmisc/obj.h
@@ -0,0 +1,184 @@
+/* libmisc/obj.h - A simple Go-ish object system
+ *
+ * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#ifndef _LIBMISC_OBJ_H_
+#define _LIBMISC_OBJ_H_
+
+#include <libmisc/macro.h>
+
+/**
+ * Use `lo_interface` similarly to how you would use
+ * `struct`/`enum`/`union` when writing the type of an interface
+ * value.
+ */
+#define lo_interface struct
+
+/**
+ * Use `LO_INTERFACE` in a .h file to define an interface.
+ *
+ * First define a macro named `{iface_name}_LO_IFACE` consisting of a
+ * series of calls to LO_NEST and/or LO_FUNC, then call
+ * `LO_INTERFACE({iface_name})`:
+ *
+ * #define myiface_LO_IFACE \
+ * LO_NEST(wrapped_iface_name) \
+ * LO_FUNC(ret_type, func_name, args...)
+ * LO_INTERFACE(myiface)
+ *
+ * Use `lo_interface {iface_name}` as the type of this interface; it
+ * should not be a pointer type.
+ *
+ * If there are any LO_NEST interfaces, this will define a
+ * `lo_box_{iface_name}_as_{wrapped_iface_name}(obj)` function for
+ * each.
+ */
+#define LO_NEST(_ARG_child_iface_name) \
+ (lo_nest, _ARG_child_iface_name)
+#define LO_FUNC(_ARG_ret_type, _ARG_func_name, ...) \
+ (lo_func, _ARG_ret_type, _ARG_func_name __VA_OPT__(,) __VA_ARGS__)
+#define LO_INTERFACE(_ARG_iface_name) \
+ struct _lo_##_ARG_iface_name##_vtable { \
+ LM_FOREACH_TUPLE(_ARG_iface_name##_LO_IFACE, \
+ _LO_IFACE_VTABLE) \
+ }; \
+ struct _ARG_iface_name { \
+ void *self; \
+ const struct _lo_##_ARG_iface_name##_vtable *vtable; \
+ }; \
+ LM_FOREACH_TUPLE(_ARG_iface_name##_LO_IFACE, \
+ _LO_IFACE_PROTO, _ARG_iface_name) \
+ LM_FORCE_SEMICOLON
+
+#define _LO_IFACE_VTABLE(_tuple_typ, ...) _LO_IFACE_VTABLE_##_tuple_typ(__VA_ARGS__)
+#define _LO_IFACE_VTABLE_lo_nest(_ARG_child_iface_name) \
+ const struct _lo_##_ARG_child_iface_name##_vtable *_lo_##_ARG_child_iface_name##_vtable; \
+ LM_FOREACH_TUPLE2(_ARG_child_iface_name##_LO_IFACE, _LO_IFACE_VTABLE2)
+#define _LO_IFACE_VTABLE_lo_func(_ARG_ret_type, _ARG_func_name, ...) \
+ _ARG_ret_type (*_ARG_func_name)(void * __VA_OPT__(,) __VA_ARGS__);
+
+#define _LO_IFACE_VTABLE_indirect() _LO_IFACE_VTABLE
+#define _LO_IFACE_VTABLE2(...) _LM_DEFER2(_LO_IFACE_VTABLE_indirect)()(__VA_ARGS__)
+
+#define _LO_IFACE_PROTO(_ARG_iface_name, _tuple_typ, ...) _LO_IFACE_PROTO_##_tuple_typ(_ARG_iface_name, __VA_ARGS__)
+#define _LO_IFACE_PROTO_lo_nest(_ARG_iface_name, _ARG_child_iface_name) \
+ LM_DEFAPPEND(_LO_REGISTRY_##_ARG_child_iface_name, \
+ (lo_interface _ARG_iface_name, _ARG_iface_name)); \
+ LM_ALWAYS_INLINE static lo_interface _ARG_child_iface_name \
+ lo_box_##_ARG_iface_name##_as_##_ARG_child_iface_name(lo_interface _ARG_iface_name obj) { \
+ return (lo_interface _ARG_child_iface_name){ \
+ .self = obj.self, \
+ .vtable = obj.vtable->_lo_##_ARG_child_iface_name##_vtable, \
+ }; \
+ }
+#define _LO_IFACE_PROTO_lo_func(_ARG_iface_name, _ARG_ret_type, _ARG_func_name, ...) \
+ /* empty */
+
+/**
+ * `LO_BOX(iface_name, obj)` boxes `obj` as a `lo_interface
+ * iface_name`; if `obj` is not already a `lo_interface iface_name`
+ * box, then it simply calls the appropriate
+ * lo_box_{XXX}_as_{iface_name}() function/macro.
+ */
+#define LO_BOX(_ARG_iface_name, obj) _Generic((obj), \
+ lo_interface _ARG_iface_name: obj \
+ LM_FOREACH_TUPLE(_LO_REGISTRY_##_ARG_iface_name, \
+ _LO_BOX, _ARG_iface_name, obj))
+#define LO_BOX2(_ARG_iface_name, obj) _Generic((obj), \
+ lo_interface _ARG_iface_name: obj \
+ LM_FOREACH_TUPLE2(_LO_REGISTRY_##_ARG_iface_name, \
+ _LO_BOX, _ARG_iface_name, obj))
+#define _LO_BOX(_ARG_iface_name, _ARG_obj, _ARG_typ, _ARG_typnam) \
+ , _ARG_typ: lo_box_##_ARG_typnam##_as_##_ARG_iface_name(_LO_coerce(_ARG_typ, _ARG_obj))
+#define _LO_coerce(typ, obj) \
+ _Generic((obj), typ: (obj), default: *((typ*)0))
+
+/**
+ * `LO_NULL(iface_name)` is the null/nil/zero value for `lo_interface {iface_name}`.
+ */
+#define LO_NULL(_ARG_iface_name) ((lo_interface _ARG_iface_name){})
+
+/**
+ * `LO_IS_NULL(iface_val)` returns whether `iface_val` is LO_NULL.
+ */
+#define LO_IS_NULL(_ARG_iface_val) ((_ARG_iface_val).vtable == NULL)
+
+/**
+ * `LO_IFACE_EQ(a, b)` returns whether the interface values `a` and
+ * `b` are the same object.
+ */
+#define LO_EQ(_ARG_iface_val_a, _ARG_iface_val_b) \
+ ((_ARG_iface_val_a).self == (_ARG_iface_val_b).self)
+
+/**
+ * Use LO_CALL(obj, method_name, args...) to call a method on an `lo_interface`.
+ */
+#define LO_CALL(_ARG_obj, _ARG_meth, ...) \
+ (_ARG_obj).vtable->_ARG_meth((_ARG_obj).self __VA_OPT__(,) __VA_ARGS__)
+
+/**
+ * `LO_IMPLEMENTATION_{H,C,STATIC}` declare that `{impl_type}`
+ * implements the `{iface_name}` interface with functions named
+ * `{impl_name}_{method_name}`.
+ *
+ * Either use _H and _C in the .h file and .c file respectively, or
+ * use _STATIC in just a .c file.
+ *
+ * These define:
+ * - The vtable symbol
+ * - The prototypes for the `{impl_name}_{method_name}` method
+ * functions.
+ * - A `lo_box_{impl_name}_as_{iface_name}(obj)` const-expr macro.
+ */
+#define LO_IMPLEMENTATION_H( iface_name, impl_type, impl_name) _LO_IMPL_H(extern, iface_name, impl_type, impl_name)
+#define LO_IMPLEMENTATION_C( iface_name, impl_type, impl_name) _LO_IMPL_C(extern, iface_name, impl_type, impl_name)
+#define LO_IMPLEMENTATION_STATIC(iface_name, impl_type, impl_name) _LO_IMPL_H(static, iface_name, impl_type, impl_name); \
+ _LO_IMPL_C(static, iface_name, impl_type, impl_name)
+
+#define _LO_IMPL_H(_ARG_visibility, _ARG_iface_name, _ARG_impl_type, _ARG_impl_name) \
+ /* Vtable. */ \
+ _LO_h_vis_vtable_##_ARG_visibility const struct _lo_##_ARG_iface_name##_vtable \
+ _lo_##_ARG_impl_name##_##_ARG_iface_name##_vtable; \
+ /* Method prototypes. */ \
+ LM_FOREACH_TUPLE(_ARG_iface_name##_LO_IFACE, \
+ _LO_IMPL_PROTO, _LO_h_vis_fn_##_ARG_visibility, _ARG_impl_type, _ARG_impl_name) \
+ /* Boxing. */ \
+ LM_DEFAPPEND(_LO_REGISTRY_##_ARG_iface_name, \
+ (_ARG_impl_type *, _ARG_impl_name)); \
+ LM_DEFAPPEND(lo_box_##_ARG_impl_name##_as_##_ARG_iface_name(_ARG_self), ( \
+ (lo_interface _ARG_iface_name){ \
+ .self = (_ARG_self), \
+ .vtable = &_lo_##_ARG_impl_name##_##_ARG_iface_name##_vtable, \
+ } \
+ )); \
+ LM_FORCE_SEMICOLON
+#define _LO_h_vis_vtable_extern extern
+#define _LO_h_vis_vtable_static static
+#define _LO_h_vis_fn_extern
+#define _LO_h_vis_fn_static static
+
+#define _LO_IMPL_PROTO( _ARG_quals, _ARG_impl_type, _ARG_impl_name, _tuple_typ, ...) _LO_IMPL_PROTO_##_tuple_typ(_ARG_quals, _ARG_impl_type, _ARG_impl_name, __VA_ARGS__)
+#define _LO_IMPL_PROTO_lo_nest(_ARG_quals, _ARG_impl_type, _ARG_impl_name, _ARG_child_iface_name) /* empty */
+#define _LO_IMPL_PROTO_lo_func(_ARG_quals, _ARG_impl_type, _ARG_impl_name, _ARG_ret_type, _ARG_func_name, ...) _ARG_quals _ARG_ret_type _ARG_impl_name##_##_ARG_func_name(_ARG_impl_type * __VA_OPT__(,) __VA_ARGS__);
+
+#define _LO_IMPL_C(_ARG_visibility, _ARG_iface_name, _ARG_impl_type, _ARG_impl_name) \
+ /* Vtable. */ \
+ _LO_c_vis_vtable_##_ARG_visibility const struct _lo_##_ARG_iface_name##_vtable \
+ _lo_##_ARG_impl_name##_##_ARG_iface_name##_vtable = { \
+ LM_FOREACH_TUPLE(_ARG_iface_name##_LO_IFACE, \
+ _LO_IMPL_VTABLE, _ARG_impl_name) \
+ }; \
+ LM_FORCE_SEMICOLON
+#define _LO_c_vis_vtable_extern
+#define _LO_c_vis_vtable_static [[maybe_unused]] static
+
+#define _LO_IMPL_VTABLE( _ARG_impl_name, _tuple_typ, ...) _LO_IMPL_VTABLE_##_tuple_typ(_ARG_impl_name, __VA_ARGS__)
+#define _LO_IMPL_VTABLE_lo_nest(_ARG_impl_name, _ARG_child_iface_name) ._lo_##_ARG_child_iface_name##_vtable = &_lo_##_ARG_impl_name##_##_ARG_child_iface_name##_vtable, LM_FOREACH_TUPLE2(_ARG_child_iface_name##_LO_IFACE, _LO_IMPL_VTABLE2, _ARG_impl_name)
+#define _LO_IMPL_VTABLE_lo_func(_ARG_impl_name, _ARG_ret_type, _ARG_func_name, ...) ._ARG_func_name = (void*)_ARG_impl_name##_##_ARG_func_name,
+
+#define _LO_IMPL_VTABLE_indirect() _LO_IMPL_VTABLE
+#define _LO_IMPL_VTABLE2(...) _LM_DEFER2(_LO_IMPL_VTABLE_indirect)()(__VA_ARGS__)
+
+#endif /* _LIBMISC_OBJ_H_ */
diff --git a/libmisc/include/libmisc/private.h b/libmisc/include/libmisc/private.h
index c5382a7..5a8777c 100644
--- a/libmisc/include/libmisc/private.h
+++ b/libmisc/include/libmisc/private.h
@@ -11,7 +11,7 @@
#define YES LM_SENTINEL()
#define IS_IMPLEMENTATION_FOR(name) LM_IS_SENTINEL(IMPLEMENTATION_FOR_##name)
-#define BEGIN_PRIVATE(name) LM_IF(IS_IMPLEMENTATION_FOR(name))()(struct {)
-#define END_PRIVATE(name) LM_IF(IS_IMPLEMENTATION_FOR(name))()(} LM_CAT2_(_HIDDEN_, __COUNTER__);)
+#define BEGIN_PRIVATE(name) LM_IF(IS_IMPLEMENTATION_FOR(name))()(struct {) LM_FORCE_SEMICOLON
+#define END_PRIVATE(name) LM_IF(IS_IMPLEMENTATION_FOR(name))(LM_FORCE_SEMICOLON)(} LM_CAT2_(_PRIVATE_, __COUNTER__))
#endif /* _LIBMISC_PRIVATE_H_ */
diff --git a/libmisc/include/libmisc/rand.h b/libmisc/include/libmisc/rand.h
index 8072841..ca16f42 100644
--- a/libmisc/include/libmisc/rand.h
+++ b/libmisc/include/libmisc/rand.h
@@ -1,46 +1,18 @@
/* libmisc/rand.h - Non-crytpographic random-number utilities
*
- * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
#ifndef _LIBMISC_RAND_H_
#define _LIBMISC_RAND_H_
-#include <stdint.h> /* for uint{n}_t, UINT{n}_C() */
-#include <stdlib.h> /* for random() */
-
-#include <libmisc/assert.h>
+#include <stdint.h> /* for uint{n}_t */
/**
* Return a psuedo-random number in the half-open interval [0,cnt).
* `cnt` must not be greater than 1<<63.
*/
-static inline uint64_t rand_uint63n(uint64_t cnt) {
- assert(cnt != 0 && ((cnt-1) & 0x8000000000000000) == 0);
- if (cnt <= UINT64_C(1)<<31) {
- uint32_t fair_cnt = ((UINT32_C(1)<<31) / cnt) * cnt;
- uint32_t rnd;
- do {
- rnd = random();
- } while (rnd >= fair_cnt);
- return rnd % cnt;
- } else if (cnt <= UINT64_C(1)<<62) {
- uint64_t fair_cnt = ((UINT64_C(1)<<62) / cnt) * cnt;
- uint64_t rnd;
- do {
- rnd = (random() << 31) | random();
- } while (rnd >= fair_cnt);
- return rnd % cnt;
- } else if (cnt <= UINT64_C(1)<<63) {
- uint64_t fair_cnt = ((UINT64_C(1)<<63) / cnt) * cnt;
- uint64_t rnd;
- do {
- rnd = (random() << 62) | (random() << 31) | random();
- } while (rnd >= fair_cnt);
- return rnd % cnt;
- }
- assert_notreached("cnt is out of bounds");
-}
+uint64_t rand_uint63n(uint64_t cnt);
#endif /* _LIBMISC_RAND_H_ */
diff --git a/libmisc/include/libmisc/utf8.h b/libmisc/include/libmisc/utf8.h
new file mode 100644
index 0000000..54fcc92
--- /dev/null
+++ b/libmisc/include/libmisc/utf8.h
@@ -0,0 +1,25 @@
+/* libmisc/utf8.h - UTF-8 routines
+ *
+ * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#ifndef _LIBMISC_UTF8_H_
+#define _LIBMISC_UTF8_H_
+
+#include <stddef.h> /* for size_t */
+#include <stdint.h> /* for uint{n}_t */
+
+/**
+ * Decode the codepoint starting at `str` and consuming at most `len`
+ * bytes. Invalid UTF-8 is indicated with chlen=0. For valid UTF-8,
+ * chlen is always in the range [1, 4].
+ */
+void utf8_decode_codepoint(const uint8_t *str, size_t len, uint32_t *ret_ch, uint8_t *ret_chlen);
+
+bool _utf8_is_valid(const uint8_t *str, size_t len, bool forbid_nul);
+
+#define utf8_is_valid(str, len) _utf8_is_valid(str, len, false)
+#define utf8_is_valid_without_nul(str, len) _utf8_is_valid(str, len, true)
+
+#endif /* _LIBMISC_UTF8_H_ */
diff --git a/libmisc/intercept.c b/libmisc/intercept.c
index 85a3801..d0e3602 100644
--- a/libmisc/intercept.c
+++ b/libmisc/intercept.c
@@ -4,28 +4,14 @@
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
-#include <stdarg.h> /* for va_list, va_start(), va_end() */
-#include <stdio.h> /* for vprintf() */
+#include <stdio.h> /* for putchar() */
#include <stdlib.h> /* for abort() */
#include <libmisc/_intercept.h>
[[gnu::weak]]
-int __lm_printf(const char *format, ...) {
- va_list va;
- va_start(va, format);
- int ret = vprintf(format, va);
- va_end(va);
- return ret;
-}
-
-[[gnu::weak]]
-int __lm_light_printf(const char *format, ...) {
- va_list va;
- va_start(va, format);
- int ret = vprintf(format, va);
- va_end(va);
- return ret;
+void __lm_putchar(unsigned char c) {
+ (void)putchar(c);
}
[[gnu::weak]]
diff --git a/libmisc/linkedlist.c b/libmisc/linkedlist.c
new file mode 100644
index 0000000..71a0aa9
--- /dev/null
+++ b/libmisc/linkedlist.c
@@ -0,0 +1,64 @@
+/* libmisc/linkedlist.c - Singly- and doubly- linked lists
+ *
+ * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#include <stddef.h> /* for NULL */
+
+#include <libmisc/assert.h>
+
+#include <libmisc/linkedlist.h>
+
+/* singly linked list *********************************************************/
+
+void _slist_push_to_rear(struct _slist_root *root, struct _slist_node *node) {
+ assert(root);
+ node->rear = NULL;
+ if (root->rear)
+ root->rear->rear = node;
+ else
+ root->front = node;
+ root->rear = node;
+}
+
+void _slist_pop_from_front(struct _slist_root *root) {
+ assert(root);
+ assert(root->front);
+ root->front = root->front->rear;
+ if (!root->front)
+ root->rear = NULL;
+}
+
+/* doubly linked list *********************************************************/
+
+void _dlist_push_to_rear(struct _dlist_root *root, struct _dlist_node *node) {
+ assert(root);
+ assert(node);
+ node->front = root->rear;
+ node->rear = NULL;
+ if (root->rear)
+ root->rear->rear = node;
+ else
+ root->front = node;
+ root->rear = node;
+}
+
+void _dlist_remove(struct _dlist_root *root, struct _dlist_node *node) {
+ assert(root);
+ assert(node);
+ if (node->front)
+ node->front->rear = node->rear;
+ else
+ root->front = node->rear;
+ if (node->rear)
+ node->rear->front = node->front;
+ else
+ root->rear = node->front;
+}
+
+void _dlist_pop_from_front(struct _dlist_root *root) {
+ assert(root);
+ assert(root->front);
+ _dlist_remove(root, root->front);
+}
diff --git a/libmisc/log.c b/libmisc/log.c
index be87de6..96e9ca4 100644
--- a/libmisc/log.c
+++ b/libmisc/log.c
@@ -8,6 +8,25 @@
#include <libmisc/assert.h> /* for static_assert() */
+#include <libmisc/_intercept.h>
+#include <libmisc/log.h>
+
+struct log_stdout {};
+LO_IMPLEMENTATION_STATIC(fmt_dest, struct log_stdout, log_stdout);
+
+static size_t log_bytes = 0;
+
+static void log_stdout_putb(struct log_stdout *, uint8_t b) {
+ __lm_putchar(b);
+ log_bytes++;
+}
+
+static size_t log_stdout_tell(struct log_stdout *) {
+ return log_bytes;
+}
+
+lo_interface fmt_dest _log_dest = { .vtable = &_lo_log_stdout_fmt_dest_vtable };
+
static const char *byte_strs[] = {
"0x00",
"0x01",
diff --git a/libmisc/map.c b/libmisc/map.c
new file mode 100644
index 0000000..d1b2a57
--- /dev/null
+++ b/libmisc/map.c
@@ -0,0 +1,230 @@
+/* libmisc/map.c - A map/dict data structure
+ *
+ * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <libmisc/alloc.h>
+#include <libmisc/assert.h>
+#include <libmisc/hash.h>
+#include <libmisc/map.h>
+
+#define FLAG_ITER (UINT8_C(1)<<0)
+#define FLAG_DEL (UINT8_C(1)<<1)
+
+/* Internal utilities *********************************************************/
+
+struct _map_kv {
+ uint8_t flags;
+ /* opaque key; */
+ /* opaque val; */
+};
+DLIST_DECLARE_NODE(_map_kv_list, struct _map_kv);
+
+static inline void *_map_kv_keyp(struct _map *m, struct _map_kv_list_node *kv) {
+ assert(m);
+ assert(kv);
+ return ((void*)kv)+m->offsetof_k;
+}
+static inline void *_map_kv_valp(struct _map *m, struct _map_kv_list_node *kv) {
+ assert(m);
+ assert(kv);
+ return ((void*)kv)+m->offsetof_v;
+}
+
+static inline void _map_lookup(struct _map *m, void *keyp,
+ hash_t *ret_hash,
+ struct _map_kv_list **ret_bucket,
+ struct _map_kv_list_node **ret_kv) {
+ assert(m);
+ assert(keyp);
+ assert(ret_hash);
+ assert(ret_bucket);
+ assert(ret_kv);
+ *ret_hash = hash(keyp, m->sizeof_k);
+ if (m->nbuckets == 0) {
+ *ret_bucket = NULL;
+ *ret_kv = NULL;
+ return;
+ }
+ *ret_bucket = &m->buckets[*ret_hash % m->nbuckets];
+ for (struct _map_kv_list_node *kv = (*ret_bucket)->front; kv; kv = kv->rear) {
+ if (!(kv->val.flags & FLAG_DEL) &&
+ memcmp(_map_kv_keyp(m, kv), keyp, m->sizeof_k) == 0) {
+ *ret_kv = kv;
+ return;
+ }
+ }
+ *ret_kv = NULL;
+}
+
+static inline void _map_resize(struct _map *m, size_t new_nbuckets) {
+ assert(m);
+ assert(new_nbuckets);
+ struct _map_kv_list *new_buckets = heap_alloc(new_nbuckets, struct _map_kv_list);
+ for (size_t i = 0; i < m->nbuckets; i++) {
+ while (m->buckets[i].front) {
+ struct _map_kv_list_node *kv = m->buckets[i].front;
+ dlist_pop_from_front(&m->buckets[i]);
+ hash_t h = hash(_map_kv_keyp(m, kv), m->sizeof_k);
+ dlist_push_to_rear(&new_buckets[h % new_nbuckets], kv);
+ }
+ }
+ m->nbuckets = new_nbuckets;
+ free(m->buckets);
+ m->buckets = new_buckets;
+}
+
+static bool _map_autoresize(struct _map *m) {
+ assert(m);
+ if (m->len > (m->nbuckets * 8/10)) {
+ size_t nbuckets = 1;
+ while (m->len > (nbuckets * 8/10))
+ nbuckets <<= 1;
+ _map_resize(m, nbuckets);
+ return true;
+ }
+ return false;
+}
+
+/* Methods ********************************************************************/
+
+void *_map_load(struct _map *m, void *keyp) {
+ assert(m);
+ assert(keyp);
+
+ hash_t h;
+ struct _map_kv_list *bucket;
+ struct _map_kv_list_node *kv;
+ _map_lookup(m, keyp, &h, &bucket, &kv);
+
+ if (!kv)
+ return NULL;
+ return _map_kv_valp(m, kv);
+}
+
+bool _map_del(struct _map *m, void *keyp) {
+ assert(m);
+ assert(keyp);
+
+ hash_t h;
+ struct _map_kv_list *bucket;
+ struct _map_kv_list_node *kv;
+ _map_lookup(m, keyp, &h, &bucket, &kv);
+
+ if (!kv)
+ return false;
+ if (kv->val.flags & FLAG_ITER) {
+ kv->val.flags |= FLAG_DEL;
+ } else {
+ dlist_remove(bucket, kv);
+ free(kv);
+ }
+ m->len--;
+ return true;
+}
+
+void *_map_store(struct _map *m, void *keyp, void *valp) {
+ assert(m);
+ assert(keyp);
+ assert(valp);
+
+ hash_t h;
+ struct _map_kv_list *bucket;
+ struct _map_kv_list_node *old;
+ _map_lookup(m, keyp, &h, &bucket, &old);
+
+ if (old) {
+ dlist_remove(bucket, old);
+ free(old);
+ m->len--;
+ }
+ m->len++;
+ if (!m->iterating && _map_autoresize(m)) {
+ h = hash(keyp, m->sizeof_k);
+ bucket = &m->buckets[h % m->nbuckets];
+ }
+ struct _map_kv_list_node *kv = calloc(1, m->sizeof_kv);
+ memcpy(_map_kv_keyp(m, kv), keyp, m->sizeof_k);
+ memcpy(_map_kv_valp(m, kv), valp, m->sizeof_v);
+ dlist_push_to_rear(bucket, kv);
+ return _map_kv_valp(m, kv);
+}
+
+void _map_free(struct _map *m) {
+ assert(m);
+
+ for (size_t i = 0; i < m->nbuckets; i++) {
+ while (m->buckets[i].front) {
+ struct _map_kv_list_node *kv = m->buckets[i].front;
+ dlist_pop_from_front(&m->buckets[i]);
+ free(kv);
+ }
+ }
+ free(m->buckets);
+ m->len = 0;
+ m->nbuckets = 0;
+ m->buckets = NULL;
+}
+
+/* Iteration ******************************************************************/
+
+struct _map_iter _map_iter_before(struct _map *m, void *keyp, void **valpp) {
+ assert(m);
+ assert(keyp);
+ assert(valpp);
+
+ struct _map_iter state = {
+ .m = m,
+ .keyp = keyp,
+ .valpp = valpp,
+ };
+ m->iterating++;
+ return state;
+}
+
+void _map_iter_after(struct _map_iter *state) {
+ assert(state);
+ assert(state->m);
+
+ state->m->iterating--;
+ if (!state->m->iterating)
+ _map_autoresize(state->m);
+}
+
+bool _map_iter_next(struct _map_iter *state) {
+ assert(state);
+ assert(state->m);
+ assert(state->valpp);
+
+ if (!state->kv) {
+ if (!state->m->len)
+ return false;
+ while (!state->m->buckets[state->i].front)
+ state->i++;
+ state->kv = state->m->buckets[state->i].front;
+ } else {
+ struct _map_kv_list_node *old_kv = state->kv;
+ state->kv = old_kv->rear;
+
+ old_kv->val.flags &= ~FLAG_ITER;
+ if (old_kv->val.flags & FLAG_DEL) {
+ dlist_remove(&state->m->buckets[state->i], old_kv);
+ free(old_kv);
+ }
+
+ while (!state->kv) {
+ state->i++;
+ if (state->i == state->m->nbuckets)
+ return false;
+ state->kv = state->m->buckets[state->i].front;
+ }
+ }
+ state->kv->val.flags |= FLAG_ITER;
+ memcpy(state->keyp, _map_kv_keyp(state->m, state->kv), state->m->sizeof_k);
+ *(state->valpp) = _map_kv_valp(state->m, state->kv);
+ return true;
+}
diff --git a/libmisc/rand.c b/libmisc/rand.c
new file mode 100644
index 0000000..d1643ee
--- /dev/null
+++ b/libmisc/rand.c
@@ -0,0 +1,38 @@
+/* libmisc/rand.c - Non-crytpographic random-number utilities
+ *
+ * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#include <stdlib.h> /* for random() */
+
+#include <libmisc/assert.h>
+
+#include <libmisc/rand.h>
+
+uint64_t rand_uint63n(uint64_t cnt) {
+ assert(cnt != 0 && ((cnt-1) & 0x8000000000000000) == 0);
+ if (cnt <= UINT64_C(1)<<31) {
+ uint32_t fair_cnt = ((UINT32_C(1)<<31) / cnt) * cnt;
+ uint32_t rnd;
+ do {
+ rnd = random();
+ } while (rnd >= fair_cnt);
+ return rnd % cnt;
+ } else if (cnt <= UINT64_C(1)<<62) {
+ uint64_t fair_cnt = ((UINT64_C(1)<<62) / cnt) * cnt;
+ uint64_t rnd;
+ do {
+ rnd = (((uint64_t)random()) << 31) | random();
+ } while (rnd >= fair_cnt);
+ return rnd % cnt;
+ } else if (cnt <= UINT64_C(1)<<63) {
+ uint64_t fair_cnt = ((UINT64_C(1)<<63) / cnt) * cnt;
+ uint64_t rnd;
+ do {
+ rnd = (((uint64_t)random()) << 62) | (((uint64_t)random()) << 31) | random();
+ } while (rnd >= fair_cnt);
+ return rnd % cnt;
+ }
+ assert_notreached("cnt is out of bounds");
+}
diff --git a/libmisc/tests/test_assert.c b/libmisc/tests/test_assert.c
index 15f9446..84b4d36 100644
--- a/libmisc/tests/test_assert.c
+++ b/libmisc/tests/test_assert.c
@@ -5,28 +5,25 @@
*/
#include <setjmp.h>
-#include <stdarg.h> /* for va_list, va_start(), va_end() */
-#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
-#include <libmisc/macro.h>
-#include <libmisc/assert.h>
#include <libmisc/_intercept.h>
+#include <libmisc/assert.h>
+#include <libmisc/fmt.h>
+#include <libmisc/macro.h>
#include "test.h"
/* Intercept failures and logging *********************************************/
-bool global_failed;
-char *global_log;
-jmp_buf global_env;
+static bool global_failed;
+static struct fmt_buf global_log;
+static jmp_buf global_env;
#define with_intercept() ({ \
global_failed = false; \
- if (global_log) \
- free(global_log); \
- global_log = NULL; \
+ global_log_clear(); \
setjmp(global_env) == 0; \
})
@@ -35,12 +32,19 @@ void __lm_abort(void) {
longjmp(global_env, 1);
}
-int __lm_light_printf(const char *format, ...) {
- va_list va;
- va_start(va, format);
- int ret = vasprintf(&global_log, format, va);
- va_end(va);
- return ret;
+void __lm_putchar(unsigned char c) {
+ if (global_log.len+1 >= global_log.cap) {
+ global_log.cap += 16;
+ global_log.dat = realloc(global_log.dat, global_log.cap);
+ memset(global_log.dat + global_log.len, 0, global_log.cap - global_log.len);
+ }
+ ((uint8_t *)global_log.dat)[global_log.len++] = (uint8_t)c;
+}
+
+static void global_log_clear(void) {
+ if (global_log.dat)
+ memset(global_log.dat, 0, global_log.cap);
+ global_log.len = 0;
}
#define __builtin_unreachable() test_assert(0)
@@ -52,21 +56,21 @@ int __lm_light_printf(const char *format, ...) {
test; \
} \
test_assert(global_failed == false); \
- test_assert(global_log == NULL); \
+ test_assert(global_log.len == 0); \
} while (0)
-#define test_should_fail(test, exp_log) do { \
- if (with_intercept()) { \
- test; \
- } \
- test_assert(global_failed == true); \
- if (!(global_log != NULL && \
- strcmp(global_log, exp_log) == 0)) { \
- printf("exp = \"%s\"\n" \
- "act = \"%s\"\n", \
- exp_log, global_log); \
- test_assert(0); \
- } \
+#define test_should_fail(test, exp_log) do { \
+ if (with_intercept()) { \
+ test; \
+ } \
+ test_assert(global_failed == true); \
+ if (!(global_log.len != 0 && \
+ strcmp(global_log.dat, exp_log) == 0)) { \
+ printf("exp = \"%s\"\n" \
+ "act = \"%s\"\n", \
+ exp_log, (char *)global_log.dat); \
+ test_assert(0); \
+ } \
} while (0)
/* Actual tests ***************************************************************/
@@ -74,6 +78,10 @@ int __lm_light_printf(const char *format, ...) {
static_assert(sizeof(char) == 1);
int main() {
+ unsigned char x = 4;
+ static_assert(0 <= x);
+ static_assert(x < 256);
+
#ifndef NDEBUG
test_should_succeed(assert(true));
test_should_fail(assert(false), "error: ASSERT: "__FILE__":"LM_STR_(__LINE__)":main(): assertion \"false\" failed\n");
@@ -84,11 +92,13 @@ int main() {
test_should_fail(assert_msg(false, NULL), "error: ASSERT: "__FILE__":"LM_STR_(__LINE__)":main(): assertion \"false\" failed\n");
test_should_fail(assert_notreached("xxx"), "error: ASSERT: "__FILE__":"LM_STR_(__LINE__)":main(): assertion \"notreached\" failed: xxx\n");
+#endif
- if (global_log) {
- free(global_log);
- global_log = NULL;
+ if (global_log.dat) {
+ global_log_clear();
+ free(global_log.dat);
+ global_log.dat = NULL;
+ global_log.cap = 0;
}
-#endif
return 0;
}
diff --git a/libmisc/tests/test_endian.c b/libmisc/tests/test_endian.c
index dcb3cc2..8c48727 100644
--- a/libmisc/tests/test_endian.c
+++ b/libmisc/tests/test_endian.c
@@ -11,7 +11,7 @@
#include "test.h"
int main() {
- uint8_t act[(2+4+8)*2] = {0};
+ uint8_t act[(2+4+8)*2] = {};
size_t pos = 0;
pos += uint16be_encode(&act[pos], UINT16_C(0x1234));
pos += uint32be_encode(&act[pos], UINT32_C(0x56789ABC));
diff --git a/libmisc/tests/test_fmt.c b/libmisc/tests/test_fmt.c
new file mode 100644
index 0000000..64b3b8a
--- /dev/null
+++ b/libmisc/tests/test_fmt.c
@@ -0,0 +1,243 @@
+/* libmisc/tests/test_fmt.c - Tests for <libmisc/fmt.h>
+ *
+ * Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#include <stdlib.h> /* for free() */
+#include <string.h> /* for strcmp(), memcmp(), memset() */
+
+#include <libmisc/fmt.h>
+
+#include "test.h"
+
+int main() {
+ char str[128] = {};
+#define do_print(...) fmt_snprint(str, sizeof(str), __VA_ARGS__)
+
+ do_print("hello ", 9, " world!\n");
+ test_assert(strcmp(str, "hello 9 world!\n") == 0);
+ memset(str, 0, sizeof(str));
+
+ do_print("hello ", (base8, 9), " world!\n");
+ test_assert(strcmp(str, "hello 11 world!\n") == 0);
+ memset(str, 0, sizeof(str));
+
+ do_print("hello ", (base2, 9), (qstr, " world!\n"));
+ test_assert(strcmp(str, "hello 1001\" world!\\n\"") == 0);
+ memset(str, 0, sizeof(str));
+
+ do_print("hello ", (base16, 17), " world!\n");
+ test_assert(strcmp(str, "hello 11 world!\n") == 0);
+ memset(str, 0, sizeof(str));
+
+ do_print((strn, "hello ", 4));
+ test_assert(strcmp(str, "hell") == 0);
+ memset(str, 0, sizeof(str));
+
+ do_print((strn, "h\0ello ", 4));
+ test_assert(memcmp(str, "h\0\0", 3) == 0);
+ memset(str, 0, sizeof(str));
+
+ do_print((mem, "hello ", 4));
+ test_assert(strcmp(str, "hell") == 0);
+ memset(str, 0, sizeof(str));
+
+ do_print((mem, "hello\0world", strlen("hello world")+1));
+ test_assert(memcmp(str, "hello\0world", strlen("hello world")+1) == 0);
+ memset(str, 0, sizeof(str));
+
+ do_print((qmem, "hello\0world", strlen("hello world")+1));
+ test_assert(strcmp(str, "\"hello\\0world\\0\"") == 0);
+ memset(str, 0, sizeof(str));
+
+ do_print((qstr, "hello\0world"));
+ test_assert(strcmp(str, "\"hello\"") == 0);
+ memset(str, 0, sizeof(str));
+
+ do_print((qstrn, "hello\0world", strlen("hello world")+1));
+ test_assert(strcmp(str, "\"hello\"") == 0);
+ memset(str, 0, sizeof(str));
+
+ do_print((qstrn, "hello\0world", 4));
+ test_assert(strcmp(str, "\"hell\"") == 0);
+ memset(str, 0, sizeof(str));
+
+ do_print((qstr, "hello\xFFworld🚧"));
+ test_assert(strcmp(str, "\"hello\\xFFworld\\U0001F6A7\"") == 0);
+ memset(str, 0, sizeof(str));
+
+ do_print((qstr, "¡hello world!"));
+ test_assert(strcmp(str, "\"\\u00A1hello world!\"") == 0);
+ memset(str, 0, sizeof(str));
+
+ do_print((qmem, "🚧", 3)); /* truncated UTF-8 */
+ test_assert(strcmp(str, "\"\\xF0\\x9F\\x9A\"") == 0);
+ memset(str, 0, sizeof(str));
+
+ do_print((qmem, "\xF7\xBF\xBF\xBF", 4)); /* over unicode_max */
+ test_assert(strcmp(str, "\"\\xF7\\xBF\\xBF\\xBF\"") == 0);
+ memset(str, 0, sizeof(str));
+
+ do_print((qmem, "\xE0\xA0", 2)); /* non-optimal encoding (of ' ') */
+ test_assert(strcmp(str, "\"\\xE0\\xA0\"") == 0);
+ memset(str, 0, sizeof(str));
+
+ do_print((byte, 'h'), (byte, 'w'));
+ test_assert(strcmp(str, "hw") == 0);
+ memset(str, 0, sizeof(str));
+
+ do_print((qbyte, 'h'), (qbyte, 'w'));
+ test_assert(strcmp(str, "'h''w'") == 0);
+ memset(str, 0, sizeof(str));
+
+ do_print((qbyte, 0));
+ test_assert(strcmp(str, "'\\0'") == 0);
+ memset(str, 0, sizeof(str));
+
+ do_print((qbyte, '\\'));
+ test_assert(strcmp(str, "'\\\\'") == 0);
+ memset(str, 0, sizeof(str));
+
+ do_print((qbyte, '\''));
+ test_assert(strcmp(str, "'\\''") == 0);
+ memset(str, 0, sizeof(str));
+
+ do_print((qbyte, '\n'));
+ test_assert(strcmp(str, "'\\n'") == 0);
+ memset(str, 0, sizeof(str));
+
+ do_print((qbyte, 0xff));
+ test_assert(strcmp(str, "'\\xFF'") == 0);
+ memset(str, 0, sizeof(str));
+
+ do_print("zero ", 0);
+ test_assert(strcmp(str, "zero 0") == 0);
+ memset(str, 0, sizeof(str));
+
+ const char *const_str = "hello";
+ do_print(const_str);
+ test_assert(strcmp(str, "hello") == 0);
+ memset(str, 0, sizeof(str));
+
+ bool t = true;
+ do_print(t);
+ test_assert(strcmp(str, "true") == 0);
+ memset(str, 0, sizeof(str));
+
+ bool f = false;
+ do_print(f);
+ test_assert(strcmp(str, "false") == 0);
+ memset(str, 0, sizeof(str));
+
+ /* Check that it accepts all primitive types of integer, not
+ * just all sizes of integer (e.g., on x86-64,
+ * sizeof(long)==sizeof(int), but they're different primitive
+ * types). */
+ {
+ signed char x = 42;
+ do_print("schar ", x);
+ test_assert(strcmp(str, "schar 42") == 0);
+ memset(str, 0, sizeof(str));
+ }
+ {
+ unsigned char x = 43;
+ do_print("uchar ", x);
+ test_assert(strcmp(str, "uchar 43") == 0);
+ memset(str, 0, sizeof(str));
+ }
+
+ {
+ short x = 44;
+ do_print("short ", x);
+ test_assert(strcmp(str, "short 44") == 0);
+ memset(str, 0, sizeof(str));
+ }
+ {
+ unsigned short x = 45;
+ do_print("ushort ", x);
+ test_assert(strcmp(str, "ushort 45") == 0);
+ memset(str, 0, sizeof(str));
+ }
+
+ {
+ int x = 46;
+ do_print("int ", x);
+ test_assert(strcmp(str, "int 46") == 0);
+ memset(str, 0, sizeof(str));
+ }
+ {
+ unsigned int x = 47;
+ do_print("uint ", x);
+ test_assert(strcmp(str, "uint 47") == 0);
+ memset(str, 0, sizeof(str));
+ }
+
+ {
+ long x = 48;
+ do_print("long ", x);
+ test_assert(strcmp(str, "long 48") == 0);
+ memset(str, 0, sizeof(str));
+ }
+ {
+ unsigned long x = 49;
+ do_print("ulong ", x);
+ test_assert(strcmp(str, "ulong 49") == 0);
+ memset(str, 0, sizeof(str));
+ }
+
+ {
+ long long x = 50;
+ do_print("long long ", x);
+ test_assert(strcmp(str, "long long 50") == 0);
+ memset(str, 0, sizeof(str));
+ }
+ {
+ unsigned long long x = 51;
+ do_print("ulong long ", x);
+ test_assert(strcmp(str, "ulong long 51") == 0);
+ memset(str, 0, sizeof(str));
+ }
+
+ do_print((ljust, 10, ' ', (base10, 1), "x"));
+ test_assert(strcmp(str, "1x ") == 0);
+ memset(str, 0, sizeof(str));
+
+ do_print((rjust, 10, ' ', (base10, 1), "x"));
+ test_assert(strcmp(str, " 1x") == 0);
+ memset(str, 0, sizeof(str));
+
+ do_print((base16_u8_, 1));
+ test_assert(strcmp(str, "0x01") == 0);
+ memset(str, 0, sizeof(str));
+
+ do_print((base16_u16_, 1));
+ test_assert(strcmp(str, "0x0001") == 0);
+ memset(str, 0, sizeof(str));
+
+ do_print((base16_u32_, 1));
+ test_assert(strcmp(str, "0x00000001") == 0);
+ memset(str, 0, sizeof(str));
+
+ do_print((base16_u64_, 1));
+ test_assert(strcmp(str, "0x0000000000000001") == 0);
+ memset(str, 0, sizeof(str));
+
+ do_print((hbyte, 1));
+ test_assert(strcmp(str, "0x01") == 0);
+ memset(str, 0, sizeof(str));
+
+ do_print((hmem, "hello", 6));
+ test_assert(strcmp(str, "{0x68,0x65,0x6C,0x6C,0x6F,0x00}") == 0);
+ memset(str, 0, sizeof(str));
+
+ char *astr = fmt_asprint("");
+ test_assert(astr != NULL && astr[0] == '\0');
+ free(astr);
+
+ astr = fmt_asprint("hello ", (base2, 9), (qstr, " world!\n"));
+ test_assert(strcmp(astr, "hello 1001\" world!\\n\"") == 0);
+ free(astr);
+
+ return 0;
+}
diff --git a/libmisc/tests/test_log.c b/libmisc/tests/test_log.c
index 49a76ca..6e7cdfd 100644
--- a/libmisc/tests/test_log.c
+++ b/libmisc/tests/test_log.c
@@ -1,14 +1,12 @@
/* libmisc/tests/test_log.c - Tests for <libmisc/log.h>
*
- * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
-#define _GNU_SOURCE /* for vasprintf() */
-#include <stdarg.h> /* for va_list */
-#include <stdio.h> /* for vasprintf() */
-#include <stdlib.h> /* for free() */
-#include <string.h> /* for strcmp() */
+#include <stdio.h> /* for vsnprintf() */
+#include <stdlib.h> /* for realloc(), free() */
+#include <string.h> /* for strlen(), strcmp() */
#define LOG_NAME FROBNICATE
#include <libmisc/log.h>
@@ -19,52 +17,64 @@
/* Intercept output ***********************************************************/
-static char *log_output = NULL;
+static struct fmt_buf log_output = {};
-int __lm_printf(const char *format, ...) {
- va_list va;
- va_start(va, format);
- int ret = vasprintf(&log_output, format, va);
- va_end(va);
- return ret;
+void __lm_putchar(unsigned char c) {
+ if (log_output.len+1 >= log_output.cap) {
+ log_output.cap += 16;
+ log_output.dat = realloc(log_output.dat, log_output.cap);
+ memset(log_output.dat + log_output.len, 0, log_output.cap - log_output.len);
+ }
+ ((uint8_t *)log_output.dat)[log_output.len++] = (uint8_t)c;
+}
+
+static void log_output_clear(void) {
+ if (log_output.dat)
+ memset(log_output.dat, 0, log_output.cap);
+ log_output.len = 0;
}
/* Actual tests ***************************************************************/
-#define should_print(_exp, cmd) do { \
- char *exp = _exp; \
- test_assert(!log_output); \
- cmd; \
- if (!exp) \
- test_assert(!log_output); \
- else { \
- test_assert(log_output); \
- if (strcmp(log_output, exp)) { \
- printf("exp = \"%s\"\n" \
- "act = \"%s\"\n", \
- exp, log_output); \
- test_assert(0); \
- } \
- } \
- if (log_output) { \
- free(log_output); \
- log_output = NULL; \
- } \
+#define should_print(_exp, cmd) do { \
+ char *exp = _exp; \
+ test_assert(log_output.len == 0); \
+ cmd; \
+ if (!exp) \
+ test_assert(log_output.len == 0); \
+ else { \
+ test_assert(log_output.dat); \
+ test_assert(strlen(log_output.dat) == log_output.len); \
+ if (strcmp(log_output.dat, exp)) { \
+ printf("exp = \"%s\"\n" \
+ "act = \"%s\"\n", \
+ exp, (char *)log_output.dat); \
+ test_assert(0); \
+ } \
+ } \
+ log_output_clear(); \
} while (0)
int main() {
should_print("error: FROBNICATE: val=42\n",
- errorf("val=%d", 42));
+ log_errorln("val=", 42));
should_print("info : FROBNICATE: val=0\n",
- infof("val=%d", 0));
+ log_infoln("val=", 0));
#ifndef NDEBUG
#define CONFIG_FROBNICATE_DEBUG 1
should_print("debug: FROBNICATE: val=-2\n",
- debugf("val=%d", -2));
+ log_debugln("val=", -2));
#undef CONFIG_FROBNICATE_DEBUG
#define CONFIG_FROBNICATE_DEBUG 0
should_print(NULL,
- debugf("val=%d", -2));
+ log_debugln("val=", -2));
+#undef CONFIG_FROBNICATE_DEBUG
#endif
+
+ if (log_output.dat) {
+ free(log_output.dat);
+ log_output.dat = NULL;
+ log_output.cap = 0;
+ }
return 0;
}
diff --git a/libmisc/tests/test_macro.c b/libmisc/tests/test_macro.c
index 69655d1..6810005 100644
--- a/libmisc/tests/test_macro.c
+++ b/libmisc/tests/test_macro.c
@@ -4,10 +4,34 @@
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
+#include <stdlib.h> /* for free() */
+#include <string.h> /* for strcmp(), strlen(), memcmp(), strdup() */
+
#include <libmisc/macro.h>
#include "test.h"
+/** Given `N` from `#define _LM_EVAL _LM_EVAL__{N}`, UNDER is `(N*2)-2`. (16*2)-2=30. */
+#define UNDER 30
+/** Given `N` from `#define _LM_EVAL _LM_EVAL__{N}`, OVER is `(N*2)-1`. (16*2)-1=31. */
+#define OVER 31
+
+/** XUNDER is 0 through `UNDER` inclusive. */
+#define XUNDER \
+ X(0) X(1) X(2) X(3) X(4) X(5) X(6) X(7) X(8) X(9) X(10) X(11) X(12) X(13) X(14) X(15) \
+ X(16) X(17) X(18) X(19) X(20) X(21) X(22) X(23) X(24) X(25) X(26) X(27) X(28) X(29) X(30)
+/** XUNDER is 0 through `OVER` inclusive. */
+#define XOVER XUNDER X(OVER)
+
+static char *without_spaces(const char *in) {
+ char *out = strdup(in);
+ for (size_t i = 0; out[i]; i++)
+ while (out[i] == ' ')
+ for (size_t j = i; out[j]; j++)
+ out[j] = out[j+1];
+ return out;
+}
+
int main() {
printf("== LM_NEXT_POWER_OF_2 =====================================\n");
/* valid down to 0. */
@@ -27,7 +51,6 @@ int main() {
/* ... */
test_assert(LM_NEXT_POWER_OF_2(0x8000000000000000-1) == 0x8000000000000000);
/* Valid up to 0x8000000000000000-1 = (1<<63)-1 */
- test_assert(LM_NEXT_POWER_OF_2(0x8000000000000000) == 0); /* :( */
printf("== LM_FLOORLOG2 ===========================================\n");
/* valid down to 1. */
@@ -51,5 +74,118 @@ int main() {
/* ... */
test_assert(LM_FLOORLOG2(0xFFFFFFFFFFFFFFFF) == 63);
+ printf("== LM_TUPLE ===============================================\n");
+ test_assert(LM_IF(LM_IS_TUPLE( 9 ))(0)(1));
+ test_assert(LM_IF(LM_IS_TUPLE( a ))(0)(1));
+ test_assert(LM_IF(LM_IS_TUPLE( () ))(1)(0));
+ test_assert(LM_IF(LM_IS_TUPLE( (9) ))(1)(0));
+ test_assert(LM_IF(LM_IS_TUPLE( (a) ))(1)(0));
+ test_assert(LM_IF(LM_IS_TUPLE( (a, b) ))(1)(0));
+
+ test_assert(LM_IF(LM_IS_EMPTY_TUPLE( () ))(1)(0));
+ test_assert(LM_IF(LM_IS_EMPTY_TUPLE( 9 ))(0)(1));
+ test_assert(LM_IF(LM_IS_EMPTY_TUPLE( a ))(0)(1));
+ test_assert(LM_IF(LM_IS_EMPTY_TUPLE( (9) ))(0)(1));
+ test_assert(LM_IF(LM_IS_EMPTY_TUPLE( (a) ))(0)(1));
+ test_assert(LM_IF(LM_IS_EMPTY_TUPLE( (a, b) ))(0)(1));
+
+ printf("== LM_TUPLES ==============================================\n");
+ test_assert(LM_IF(LM_TUPLES_IS_NONEMPTY( ))(0)(1));
+ test_assert(LM_IF(LM_TUPLES_IS_NONEMPTY( () ))(1)(0));
+ test_assert(LM_IF(LM_TUPLES_IS_NONEMPTY( (a) ))(1)(0));
+ test_assert(LM_IF(LM_TUPLES_IS_NONEMPTY( (a)(b) ))(1)(0));
+ test_assert(LM_IF(LM_TUPLES_IS_NONEMPTY( (a)(b)(c) ))(1)(0));
+
+ printf("== LM_FOREACH_PARAM =======================================\n");
+ /* Basic test. */
+ {
+ #define FN(A, B) A "-" #B
+ const char *str = LM_FOREACH_PARAM(FN, (" "), a, (b), c);
+ #undef FN
+ test_assert(strcmp(str, " -a -(b) -c") == 0);
+ }
+
+ /* Test that it works with the documented limit of params. */
+ {
+ #define X(n) , n
+ #define FN(n) #n "\n"
+ const char *str = LM_FOREACH_PARAM_(FN, () XUNDER);
+ #undef FN
+ #undef X
+ #define X(n) #n "\n"
+ test_assert(strcmp(str, XUNDER) == 0);
+ #undef X
+ }
+
+ /* Test that it breaks at documented_limit+1 tuples. */
+ {
+ #define X(n) , n
+ #define FN(n) n
+ const char *str = LM_STR_(LM_FOREACH_PARAM_(FN, () XOVER));
+ #undef FN
+ #undef X
+ /* This comparison is a little extra complicated in
+ * order to not be sensitive to whitespace in the
+ * suffix. */
+ #define X(n) #n " "
+ const char *exp_prefix = XUNDER;
+ #undef X
+ const char *exp_suffix = "FN(" LM_STR_(OVER) ")_LM_FOREACH_PARAM_ITEM_indirect()(FN,(),())";
+ test_assert(strlen(exp_prefix) < strlen(str) && memcmp(exp_prefix, str, strlen(exp_prefix)) == 0);
+ char *act_suffix = without_spaces(&str[strlen(exp_prefix)]);
+ test_assert(strcmp(act_suffix, exp_suffix) == 0);
+ free(act_suffix);
+ }
+
+ printf("== LM_FOREACH_TUPLE =======================================\n");
+ /* Basic test. */
+ {
+ #define FN(a, b) a "-" b
+ const char *str = LM_FOREACH_TUPLE( ("a") ("b") ("c"), FN, " ");
+ #undef FN
+ test_assert(strcmp(str, " -a -b -c") == 0);
+ }
+
+ /* Test that it works with the documented limit of tuples. */
+ {
+ #define X(n) (n)
+ #define FN(n) #n "\n"
+ const char *str = LM_FOREACH_TUPLE(XUNDER, FN);
+ #undef FN
+ #undef X
+ #define X(n) #n "\n"
+ test_assert(strcmp(str, XUNDER) == 0);
+ #undef X
+ }
+
+ /* Test that it breaks at documented_limit+1 tuples. */
+ {
+ #define X(n) (n)
+ #define FN(n) n
+ const char *str = LM_STR_(LM_FOREACH_TUPLE(XOVER, FN));
+ #undef FN
+ #undef X
+ /* This comparison is a little extra complicated in
+ * order to not be sensitive to whitespace in the
+ * suffix. */
+ #define X(n) #n " "
+ const char *exp_prefix = XUNDER;
+ #undef X
+ const char *exp_suffix = "FN(" LM_STR_(OVER) ")_LM_FOREACH_TUPLE_indirect()(,FN,)";
+ test_assert(strlen(exp_prefix) < strlen(str) && memcmp(exp_prefix, str, strlen(exp_prefix)) == 0);
+ char *act_suffix = without_spaces(&str[strlen(exp_prefix)]);
+ test_assert(strcmp(act_suffix, exp_suffix) == 0);
+ free(act_suffix);
+ }
+
+ printf("== LM_DEFAPPEND ===========================================\n");
+ LM_DEFAPPEND(mylist, a);
+ LM_DEFAPPEND(mylist,
+ b);
+ {
+ const char *str = LM_STR_(mylist);
+ test_assert(strcmp(str, "a b") == 0);
+ }
+
return 0;
}
diff --git a/libmisc/tests/test_map.c b/libmisc/tests/test_map.c
new file mode 100644
index 0000000..855dace
--- /dev/null
+++ b/libmisc/tests/test_map.c
@@ -0,0 +1,60 @@
+/* libmisc/tests/test_map.c - Tests for <libmisc/map.h>
+ *
+ * Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#include <libmisc/map.h>
+
+#include "test.h"
+
+MAP_DECLARE(intmap, int, int);
+
+int main() {
+ struct intmap m = {};
+ test_assert(map_len(&m) == 0);
+
+ int *v = map_store(&m, 3, 8);
+ test_assert(v && *v == 8);
+ test_assert(map_len(&m) == 1);
+
+ v = map_load(&m, 3);
+ test_assert(v);
+ test_assert(*v == 8);
+
+ v = NULL;
+
+ test_assert(map_del(&m, 3));
+ test_assert(map_len(&m) == 0);
+ test_assert(!map_del(&m, 3));
+
+ map_store(&m, 1, 11);
+ map_store(&m, 2, 12);
+ map_store(&m, 3, 13);
+ test_assert(map_len(&m) == 3);
+ bool seen_1 = false, seen_2 = false, seen_3 = false;
+ MAP_FOREACH(&m, ik, iv) {
+ switch (ik) {
+ case 1: seen_1 = true; break;
+ case 2: seen_2 = true; break;
+ case 3: seen_3 = true; break;
+ }
+ switch (ik) {
+ case 1: case 2: case 3:
+ map_store(&m, ik+20, (*iv)+20);
+ test_assert(map_del(&m, ik));
+ test_assert(!map_del(&m, ik));
+ test_assert(map_load(&m, ik) == NULL);
+ break;
+ }
+ }
+ test_assert(map_len(&m) == 3);
+ test_assert(seen_1); v = map_load(&m, 21); test_assert(v && *v == 31); v = map_load(&m, 1); test_assert(!v);
+ test_assert(seen_2); v = map_load(&m, 22); test_assert(v && *v == 32); v = map_load(&m, 2); test_assert(!v);
+ test_assert(seen_3); v = map_load(&m, 23); test_assert(v && *v == 33); v = map_load(&m, 3); test_assert(!v);
+
+ map_free(&m);
+ test_assert(map_len(&m) == 0);
+
+ return 0;
+}
diff --git a/libobj/tests/test_obj.c b/libmisc/tests/test_obj.c
index 89fff68..c3c6786 100644
--- a/libobj/tests/test_obj.c
+++ b/libmisc/tests/test_obj.c
@@ -1,10 +1,10 @@
-/* libobj/tests/test_obj.c - Tests for <libobj/obj.h>
+/* libmisc/tests/test_obj.c - Tests for <libmisc/obj.h>
*
* Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
-#include <libobj/obj.h>
+#include <libmisc/obj.h>
#include "test.h"
@@ -17,30 +17,30 @@
LO_FUNC(int, frob1, int) \
/** Function that returns nothing. */ \
LO_FUNC(void, frob0)
-LO_INTERFACE(frobber)
+LO_INTERFACE(frobber);
/* `struct myclass` header ****************************************************/
struct myclass {
int a;
};
-LO_IMPLEMENTATION_H(frobber, struct myclass, myclass)
+LO_IMPLEMENTATION_H(frobber, struct myclass, myclass);
/* `struct myclass` implementation ********************************************/
-LO_IMPLEMENTATION_C(frobber, struct myclass, myclass, static)
+LO_IMPLEMENTATION_C(frobber, struct myclass, myclass);
-static int myclass_frob(struct myclass *self) {
+int myclass_frob(struct myclass *self) {
test_assert(self);
return self->a;
}
-static int myclass_frob1(struct myclass *self, int arg) {
+int myclass_frob1(struct myclass *self, int arg) {
test_assert(self);
return arg;
}
-static void myclass_frob0(struct myclass *self) {
+void myclass_frob0(struct myclass *self) {
test_assert(self);
}
diff --git a/libmisc/tests/test_obj_autobox.c b/libmisc/tests/test_obj_autobox.c
new file mode 100644
index 0000000..394f716
--- /dev/null
+++ b/libmisc/tests/test_obj_autobox.c
@@ -0,0 +1,77 @@
+/* libmisc/tests/test_obj_autobox.c - Generated by `libmisc/tests/test_obj_nest.c libmisc/tests/test_obj_autobox.c`. DO NOT EDIT! */
+/* libmisc/tests/test_obj_nest.c - Tests for <libmisc/obj.h> nesting
+ *
+ * Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#include <string.h> /* for memcpy() */
+
+#include <libmisc/obj.h>
+
+#include "test.h"
+
+/* interfaces *****************************************************************/
+
+#define reader_LO_IFACE \
+ LO_FUNC(size_t, read, void *, size_t)
+LO_INTERFACE(reader);
+
+#define writer_LO_IFACE \
+ LO_FUNC(size_t, write, void *, size_t)
+LO_INTERFACE(writer);
+
+#define read_writer_LO_IFACE \
+ LO_NEST(reader) \
+ LO_NEST(writer)
+LO_INTERFACE(read_writer);
+
+/* implementation *************************************************************/
+
+struct myclass {
+ size_t len;
+ char buf[512];
+};
+LO_IMPLEMENTATION_STATIC(reader, struct myclass, myclass);
+LO_IMPLEMENTATION_STATIC(writer, struct myclass, myclass);
+LO_IMPLEMENTATION_STATIC(read_writer, struct myclass, myclass);
+
+static size_t myclass_read(struct myclass *self, void *buf, size_t count) {
+ test_assert(self);
+ if (count > self->len)
+ count = self->len;
+ memcpy(buf, self->buf, count);
+ return count;
+}
+
+static size_t myclass_write(struct myclass *self, void *buf, size_t count) {
+ test_assert(self);
+ if (self->len)
+ return 0;
+ if (count > sizeof(self->buf))
+ count = sizeof(self->buf);
+ memcpy(self->buf, buf, count);
+ self->len = count;
+ return count;
+}
+
+/* main test body *************************************************************/
+
+int main() {
+ struct myclass _obj = {};
+ lo_interface read_writer obj = LO_BOX(read_writer, &_obj);
+ test_assert(LO_CALL(obj, write, "Hello", 6) == 6);
+ char buf[6] = {};
+ test_assert(LO_CALL(obj, read, buf, 3) == 3);
+ test_assert(memcmp(buf, "Hel\0\0\0", 6) == 0);
+
+ lo_interface reader rd = LO_BOX(reader, &_obj);
+ rd = LO_BOX(reader, obj);
+ (void) rd;
+
+ lo_interface writer wr = LO_BOX(writer, &_obj);
+ wr = LO_BOX(writer, obj);
+ (void) wr;
+
+ return 0;
+}
diff --git a/libmisc/tests/test_obj_autobox.c.gen b/libmisc/tests/test_obj_autobox.c.gen
new file mode 100755
index 0000000..3cfa3d4
--- /dev/null
+++ b/libmisc/tests/test_obj_autobox.c.gen
@@ -0,0 +1,17 @@
+#!/bin/sh
+# libmisc/tests/test_obj_autobox.c.gen - Generate tests for LO_BOX()
+#
+# Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+# SPDX-License-Identifier: AGPL-3.0-or-later
+
+infile=$1
+outfile=$2
+
+grep --quiet lo_box_ -- "$infile"
+
+{
+ echo "/* ${outfile} - Generated by \`$*\`. DO NOT EDIT! */"
+ sed -E 's/lo_box_([a-z0-9_]+)_as_([a-z0-9_]+)\(/LO_BOX(\2, /g' <"$infile"
+} >"$outfile"
+
+! grep -H lo_box_ -- "$outfile"
diff --git a/libobj/tests/test_nest.c b/libmisc/tests/test_obj_nest.c
index c9f9eba..b52cd7b 100644
--- a/libobj/tests/test_nest.c
+++ b/libmisc/tests/test_obj_nest.c
@@ -1,4 +1,4 @@
-/* libobj/tests/test_nest.c - Tests for <libobj/obj.h>
+/* libmisc/tests/test_obj_nest.c - Tests for <libmisc/obj.h> nesting
*
* Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
@@ -6,42 +6,36 @@
#include <string.h> /* for memcpy() */
-#include <libobj/obj.h>
+#include <libmisc/obj.h>
#include "test.h"
/* interfaces *****************************************************************/
#define reader_LO_IFACE \
- LO_FUNC(ssize_t, read, void *, size_t)
-LO_INTERFACE(reader)
+ LO_FUNC(size_t, read, void *, size_t)
+LO_INTERFACE(reader);
#define writer_LO_IFACE \
- LO_FUNC(ssize_t, write, void *, size_t)
-LO_INTERFACE(writer)
+ LO_FUNC(size_t, write, void *, size_t)
+LO_INTERFACE(writer);
#define read_writer_LO_IFACE \
LO_NEST(reader) \
LO_NEST(writer)
-LO_INTERFACE(read_writer)
+LO_INTERFACE(read_writer);
-/* implementation header ******************************************************/
+/* implementation *************************************************************/
struct myclass {
size_t len;
char buf[512];
};
-LO_IMPLEMENTATION_H(reader, struct myclass, myclass)
-LO_IMPLEMENTATION_H(writer, struct myclass, myclass)
-LO_IMPLEMENTATION_H(read_writer, struct myclass, myclass)
+LO_IMPLEMENTATION_STATIC(reader, struct myclass, myclass);
+LO_IMPLEMENTATION_STATIC(writer, struct myclass, myclass);
+LO_IMPLEMENTATION_STATIC(read_writer, struct myclass, myclass);
-/* implementation main ********************************************************/
-
-LO_IMPLEMENTATION_C(reader, struct myclass, myclass, static)
-LO_IMPLEMENTATION_C(writer, struct myclass, myclass, static)
-LO_IMPLEMENTATION_C(read_writer, struct myclass, myclass, static)
-
-static ssize_t myclass_read(struct myclass *self, void *buf, size_t count) {
+static size_t myclass_read(struct myclass *self, void *buf, size_t count) {
test_assert(self);
if (count > self->len)
count = self->len;
@@ -49,10 +43,10 @@ static ssize_t myclass_read(struct myclass *self, void *buf, size_t count) {
return count;
}
-static ssize_t myclass_write(struct myclass *self, void *buf, size_t count) {
+static size_t myclass_write(struct myclass *self, void *buf, size_t count) {
test_assert(self);
if (self->len)
- return -1;
+ return 0;
if (count > sizeof(self->buf))
count = sizeof(self->buf);
memcpy(self->buf, buf, count);
@@ -63,11 +57,20 @@ static ssize_t myclass_write(struct myclass *self, void *buf, size_t count) {
/* main test body *************************************************************/
int main() {
- struct myclass _obj = {0};
+ struct myclass _obj = {};
lo_interface read_writer obj = lo_box_myclass_as_read_writer(&_obj);
test_assert(LO_CALL(obj, write, "Hello", 6) == 6);
- char buf[6] = {0};
+ char buf[6] = {};
test_assert(LO_CALL(obj, read, buf, 3) == 3);
test_assert(memcmp(buf, "Hel\0\0\0", 6) == 0);
+
+ lo_interface reader rd = lo_box_myclass_as_reader(&_obj);
+ rd = lo_box_read_writer_as_reader(obj);
+ (void) rd;
+
+ lo_interface writer wr = lo_box_myclass_as_writer(&_obj);
+ wr = lo_box_read_writer_as_writer(obj);
+ (void) wr;
+
return 0;
}
diff --git a/libmisc/tests/test_private.c b/libmisc/tests/test_private.c
index 7aaf1ee..024dddb 100644
--- a/libmisc/tests/test_private.c
+++ b/libmisc/tests/test_private.c
@@ -1,6 +1,6 @@
/* libmisc/tests/test_private.c - Tests for <libmisc/private.h>
*
- * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
@@ -8,18 +8,18 @@
struct a {
int foo;
- BEGIN_PRIVATE(A)
+ BEGIN_PRIVATE(A);
int bar;
- END_PRIVATE(A)
+ END_PRIVATE(A);
};
#define IMPLEMENTATION_FOR_B YES
struct b {
int foo;
- BEGIN_PRIVATE(B)
+ BEGIN_PRIVATE(B);
int bar;
- END_PRIVATE(B)
+ END_PRIVATE(B);
};
int main() {
diff --git a/libmisc/tests/test_rand.c b/libmisc/tests/test_rand.c
index 8076155..1cfbd65 100644
--- a/libmisc/tests/test_rand.c
+++ b/libmisc/tests/test_rand.c
@@ -1,14 +1,13 @@
/* libmisc/tests/test_rand.c - Tests for <libmisc/rand.h>
*
- * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
-#include <stdbool.h>
#include <setjmp.h>
-#include <libmisc/rand.h>
#include <libmisc/_intercept.h>
+#include <libmisc/rand.h>
#include "test.h"
@@ -51,7 +50,7 @@ static void test_n(uint64_t cnt) {
#endif
} else {
double sum = 0;
- bool seen[MAX_SEE_ALL] = {0};
+ bool seen[MAX_SEE_ALL] = {};
for (int i = 0; i < ROUNDS; i++) {
uint64_t val = rand_uint63n(cnt);
sum += val;
diff --git a/libmisc/utf8.c b/libmisc/utf8.c
new file mode 100644
index 0000000..28357f0
--- /dev/null
+++ b/libmisc/utf8.c
@@ -0,0 +1,44 @@
+/* libmisc/utf8.c - UTF-8 routines
+ *
+ * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#include <libmisc/utf8.h>
+
+void utf8_decode_codepoint(const uint8_t *str, size_t len, uint32_t *ret_ch, uint8_t *ret_chlen) {
+ uint32_t ch;
+ uint8_t chlen;
+ uint32_t chmin;
+ if ((str[0] & 0b10000000) == 0b00000000) { ch = str[0] & 0b01111111; chlen = 1; chmin = 0; } /* bits=7+(0*6)= 7 */
+ else if ((str[0] & 0b11100000) == 0b11000000) { ch = str[0] & 0b00011111; chlen = 2; chmin = UINT32_C(1)<< 7; } /* bits=5+(1*6)=11 */
+ else if ((str[0] & 0b11110000) == 0b11100000) { ch = str[0] & 0b00001111; chlen = 3; chmin = UINT32_C(1)<<11; } /* bits=4+(2*6)=16 */
+ else if ((str[0] & 0b11111000) == 0b11110000) { ch = str[0] & 0b00000111; chlen = 4; chmin = UINT32_C(1)<<16; } /* bits=3+(3*6)=21 */
+ else goto invalid;
+ if (chlen > len)
+ goto invalid;
+ for (uint8_t i = 1; i < chlen; i++) {
+ if ((str[i] & 0b11000000) != 0b10000000)
+ goto invalid;
+ ch = (ch << 6) | (str[i] & 0b00111111);
+ }
+ if (ch > 0x10FFFF || ch < chmin)
+ goto invalid;
+ *ret_ch = ch;
+ *ret_chlen = chlen;
+ return;
+ invalid:
+ *ret_chlen = 0;
+}
+
+bool _utf8_is_valid(const uint8_t *str, size_t len, bool forbid_nul) {
+ for (size_t pos = 0; pos < len;) {
+ uint32_t ch;
+ uint8_t chlen;
+ utf8_decode_codepoint(&str[pos], len-pos, &ch, &chlen);
+ if (chlen == 0 || (forbid_nul && ch == 0))
+ return false;
+ pos += chlen;
+ }
+ return true;
+}
diff --git a/libmisc/wrap-cc b/libmisc/wrap-cc
new file mode 100755
index 0000000..e7a0b91
--- /dev/null
+++ b/libmisc/wrap-cc
@@ -0,0 +1,186 @@
+#!/usr/bin/env python3
+# libmisc/wrap-cc - Wrapper around GCC to enhance the preprocessor
+#
+# Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+# SPDX-License-Identifier: AGPL-3.0-or-later
+
+import os
+import subprocess
+import sys
+import typing
+
+
+def scan_tuple(
+ text: str, beg: int, on_part: typing.Callable[[str], None] | None = None
+) -> int:
+ assert text[beg] == "("
+ pos = beg + 1
+ arg_start = pos
+ parens = 1
+ instring = False
+ while parens:
+ c = text[pos]
+ if instring:
+ match c:
+ case "\\":
+ pos += 1
+ case '"':
+ instring = False
+ else:
+ match c:
+ case "(":
+ parens += 1
+ case ")":
+ parens -= 1
+ if on_part and parens == 0 and text[beg + 1 : pos].strip():
+ on_part(text[arg_start:pos])
+ case ",":
+ if on_part and parens == 1:
+ on_part(text[arg_start:pos])
+ arg_start = pos + 1
+ case '"':
+ instring = True
+ pos += 1
+ assert text[pos - 1] == ")"
+ return pos - 1
+
+
+def unquote(cstr: str) -> str:
+ assert len(cstr) >= 2 and cstr[0] == '"' and cstr[-1] == '"'
+ cstr = cstr[1:-1]
+ out = ""
+ while cstr:
+ if cstr[0] == "\\":
+ match cstr[1]:
+ case "n":
+ out += "\n"
+ cstr = cstr[2:]
+ case "\\":
+ out += "\\"
+ cstr = cstr[2:]
+ case '"':
+ out += '"'
+ cstr = cstr[2:]
+ else:
+ out += cstr[0]
+ cstr = cstr[1:]
+ return out
+
+
+def preprocess(all_args: list[str]) -> typing.NoReturn:
+ # argparse #################################################################
+ _args = all_args
+
+ def shift(n: int) -> list[str]:
+ nonlocal _args
+ ret = _args[:n]
+ _args = _args[n:]
+ return ret
+
+ arg0 = shift(1)[0]
+ common_flags: list[str] = []
+ output_flags: list[str] = []
+ positional: list[str] = []
+ while _args:
+ if len(_args[0]) > 2 and _args[0][0] == "-" and _args[0][1] in "IDU":
+ _args = [_args[0][:2], _args[0][2:], *_args[1:]]
+ match _args[0]:
+ # Mode
+ case "-E" | "-quiet":
+ common_flags += shift(1)
+ case "-lang-asm":
+ os.execvp(all_args[0], all_args)
+ # Search path
+ case "-I" | "-imultilib" | "-isystem":
+ common_flags += shift(2)
+ # Define/Undefine
+ case "-D" | "-U":
+ common_flags += shift(2)
+ # Optimization
+ case "-O0" | "-O1" | "-O2" | "-O3" | "-Os" | "-Ofast" | "-Og" | "-Oz":
+ common_flags += shift(1)
+ case "-g":
+ common_flags += shift(1)
+ # Output files
+ case "-MD" | "-MF" | "-MT" | "-dumpbase" | "-dumpbase-ext":
+ output_flags += shift(2)
+ case "-o":
+ output_flags += shift(2)
+ # Other
+ case _:
+ if _args[0].startswith("-"):
+ if _args[0].startswith("-std="):
+ common_flags += shift(1)
+ elif _args[0].startswith("-m"):
+ common_flags += shift(1)
+ elif _args[0].startswith("-f"):
+ common_flags += shift(1)
+ elif _args[0].startswith("-W"):
+ common_flags += shift(1)
+ else:
+ raise ValueError(f"unknown flag: {_args!r}")
+ else:
+ positional += shift(1)
+ if len(positional) != 1:
+ raise ValueError("expected 1 input file")
+ infile = positional[0]
+
+ # enhance ##################################################################
+
+ common_flags += ["-D", "__LIBMISC_ENHANCED_CPP__"]
+
+ text = subprocess.run(
+ [arg0, *common_flags, infile],
+ stdin=subprocess.DEVNULL,
+ stdout=subprocess.PIPE,
+ stderr=sys.stderr,
+ check=True,
+ text=True,
+ ).stdout
+
+ macros: dict[str, str] = {}
+
+ marker = "__xx__LM_DEFAPPEND__xx__"
+ pos = 0
+ while (marker_beg := text.find(marker, pos)) >= 0:
+ args: list[str] = []
+
+ def add_arg(arg: str) -> None:
+ nonlocal args
+ args.append(arg)
+
+ beg_paren = marker_beg + len(marker)
+ end_paren = scan_tuple(text, beg_paren, add_arg)
+
+ before = text[:marker_beg]
+ # old = text[marker_beg : end_paren + 1]
+ after = text[end_paren + 1 :]
+
+ assert len(args) == 2
+ k = unquote(args[0].strip())
+ v = unquote(args[1].strip())
+ if k not in macros:
+ macros[k] = v
+ else:
+ macros[k] += " " + v
+
+ text = before + after
+ pos = len(before)
+
+ common_flags += ["-D", marker + "=LM_EAT"]
+ for k, v in macros.items():
+ common_flags += ["-D", k + "=" + v]
+
+ # Run, for-real ############################################################
+ os.execvp(arg0, [arg0, *common_flags, *output_flags, infile])
+
+
+def main(all_args: list[str]) -> typing.NoReturn:
+ if len(all_args) >= 2 and all_args[0].endswith("cc1") and all_args[1] == "-E":
+ preprocess(all_args)
+ else:
+ os.execvp(all_args[0], all_args)
+
+
+if __name__ == "__main__":
+ main(sys.argv[1:])
diff --git a/libobj/CMakeLists.txt b/libobj/CMakeLists.txt
deleted file mode 100644
index 1cc552c..0000000
--- a/libobj/CMakeLists.txt
+++ /dev/null
@@ -1,14 +0,0 @@
-# libobj/CMakeLists.txt - A simple Go-ish object system built on GCC -fplan9-extensions
-#
-# Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
-# SPDX-License-Identifier: AGPL-3.0-or-later
-
-add_library(libobj INTERFACE)
-target_include_directories(libobj SYSTEM INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include)
-target_link_libraries(libobj INTERFACE
- libmisc
-)
-target_compile_options(libobj INTERFACE "$<$<COMPILE_LANGUAGE:C>:-fplan9-extensions>")
-
-add_lib_test(libobj test_obj)
-add_lib_test(libobj test_nest)
diff --git a/libobj/include/libobj/obj.h b/libobj/include/libobj/obj.h
deleted file mode 100644
index d8a528a..0000000
--- a/libobj/include/libobj/obj.h
+++ /dev/null
@@ -1,154 +0,0 @@
-/* libobj/obj.h - A simple Go-ish object system
- *
- * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
-
-#ifndef _LIBOBJ_OBJ_H_
-#define _LIBOBJ_OBJ_H_
-
-#include <libmisc/macro.h>
-
-/**
- * Use `lo_interface` similarly to how you would use
- * `struct`/`enum`/`union` when writing the type of an interface
- * value.
- */
-#define lo_interface struct
-
-/**
- * Use `LO_INTERFACE` in a .h file to define an interface.
- *
- * First define a macro named `{iface_name}_LO_IFACE` consisting of a
- * series of calls to LO_NEST and/or LO_FUNC, then call
- * `LO_INTERFACE({iface_name})`:
- *
- * #define myiface_LO_IFACE \
- * LO_NEST(wrapped_iface_name) \
- * LO_FUNC(ret_type, func_name, args...)
- * LO_INTERFACE(myiface)
- *
- * Use `lo_interface {iface_name}` as the type of this interface; it
- * should not be a pointer type.
- *
- * If there are any LO_NEST interfaces, this will define a
- * `lo_box_{iface_name}_as_{wrapped_iface_name}(obj)` function for
- * each.
- */
-#define LO_NEST(_ARG_child_iface_name) \
- (lo_nest, _ARG_child_iface_name)
-#define LO_FUNC(_ARG_ret_type, _ARG_func_name, ...) \
- (lo_func, _ARG_ret_type, _ARG_func_name __VA_OPT__(,) __VA_ARGS__)
-#define LO_INTERFACE(_ARG_iface_name) \
- typedef struct { \
- LM_FOREACH_TUPLE(_ARG_iface_name##_LO_IFACE, \
- _LO_IFACE_VTABLE) \
- } _lo_##_ARG_iface_name##_vtable; \
- struct _ARG_iface_name { \
- void *self; \
- const _lo_##_ARG_iface_name##_vtable *vtable; \
- }; \
- LM_FOREACH_TUPLE(_ARG_iface_name##_LO_IFACE, \
- _LO_IFACE_PROTO, _ARG_iface_name)
-#define _LO_IFACE_VTABLE(_tuple_typ, ...) \
- _LO_IFACE_VTABLE_##_tuple_typ(__VA_ARGS__)
-#define _LO_IFACE_VTABLE_lo_nest(_ARG_child_iface_name) \
- _lo_##_ARG_child_iface_name##_vtable;
-#define _LO_IFACE_VTABLE_lo_func(_ARG_ret_type, _ARG_func_name, ...) \
- _ARG_ret_type (*_ARG_func_name)(void * __VA_OPT__(,) __VA_ARGS__);
-#define _LO_IFACE_PROTO(_ARG_iface_name, _tuple_typ, ...) \
- _LO_IFACE_PROTO_##_tuple_typ(_ARG_iface_name, __VA_ARGS__)
-#define _LO_IFACE_PROTO_lo_nest(_ARG_iface_name, _ARG_child_iface_name) \
- LM_ALWAYS_INLINE static lo_interface _ARG_child_iface_name \
- box_##_ARG_iface_name##_as_##_ARG_child_iface_name(lo_interface _ARG_iface_name obj) { \
- return (lo_interface _ARG_child_iface_name){ \
- .self = obj.self, \
- .vtable = &obj.vtable->_lo_##_ARG_child_iface_name##_vtable, \
- }; \
- }
-#define _LO_IFACE_PROTO_lo_func(_ARG_iface_name, _ARG_ret_type, _ARG_func_name, ...) \
- /* empty */
-
-/**
- * `LO_NULL(iface_name)` is the null/nil/zero value for `lo_interface {iface_name}`.
- */
-#define LO_NULL(_ARG_iface_name) ((lo_interface _ARG_iface_name){0})
-
-/**
- * `LO_IS_NULL(iface_val)` returns whether `iface_val` is LO_NULL.
- */
-#define LO_IS_NULL(_ARG_iface_val) ((_ARG_iface_val).vtable == NULL)
-
-/**
- * `LO_IFACE_EQ(a, b)` returns whether the interface values `a` and
- * `b` are the same object.
- */
-#define LO_EQ(_ARG_iface_val_a, _ARG_iface_val_b) \
- ((_ARG_iface_val_a).self == (_ARG_iface_val_b).self)
-
-/**
- * Use LO_CALL(obj, method_name, args...) to call a method on an `lo_interface`.
- */
-#define LO_CALL(_ARG_obj, _ARG_meth, ...) \
- (_ARG_obj).vtable->_ARG_meth((_ARG_obj).self __VA_OPT__(,) __VA_ARGS__)
-
-/**
- * Use `LO_IMPLEMENTATION_H(iface_name, impl_type, impl_name)` in a .h
- * file to declare that `{impl_type}` implements the `{iface_name}`
- * interface with functions named `{impl_name}_{method_name}`.
- *
- * This will also define a `lo_box_{impl_name}_as_{iface_name}(obj)`
- * function.
- *
- * You must also call the LO_IMPLEMENTATION_C in a single .c file.
- */
-#define LO_IMPLEMENTATION_H(_ARG_iface_name, _ARG_impl_type, _ARG_impl_name) \
- /* Vtable. */ \
- extern const _lo_##_ARG_iface_name##_vtable \
- _lo_##_ARG_impl_name##_##_ARG_iface_name##_vtable; \
- /* Boxing. */ \
- LM_ALWAYS_INLINE static lo_interface _ARG_iface_name \
- lo_box_##_ARG_impl_name##_as_##_ARG_iface_name(_ARG_impl_type *self) { \
- return (lo_interface _ARG_iface_name){ \
- .self = self, \
- .vtable = &_lo_##_ARG_impl_name##_##_ARG_iface_name##_vtable, \
- }; \
- }
-
-/**
- * Use `LO_IMPLEMENTATION_C(iface_name, impl_type, impl_name[, static])` in a .c
- * file to declare that `{impl_type}` implements the `{iface_name}` interface
- * with functions named `{impl_name}_{method_name}`.
- *
- * You must also call the LO_IMPLEMENTATION_H in the corresponding .h file.
- *
- * If `iface_name` contains a nested interface, then the
- * implementation of the nested interfaces must be declared with
- * `LO_IMPLEMENTATION_C` first.
- */
-#define LO_IMPLEMENTATION_C(_ARG_iface_name, _ARG_impl_type, _ARG_impl_name, ...) \
- /* Method prototypes. */ \
- LM_FOREACH_TUPLE(_ARG_iface_name##_LO_IFACE, \
- _LO_IMPL_PROTO, _ARG_impl_type, _ARG_impl_name, __VA_ARGS__) \
- /* Vtable. */ \
- const _lo_##_ARG_iface_name##_vtable \
- _lo_##_ARG_impl_name##_##_ARG_iface_name##_vtable = { \
- LM_FOREACH_TUPLE(_ARG_iface_name##_LO_IFACE, \
- _LO_IMPL_VTABLE, _ARG_impl_name) \
- }; \
-
-#define _LO_IMPL_PROTO(_ARG_impl_type, _ARG_impl_name, _ARG_quals, _tuple_typ, ...) \
- _LO_IMPL_PROTO_##_tuple_typ(_ARG_impl_type, _ARG_impl_name, _ARG_quals, __VA_ARGS__)
-#define _LO_IMPL_PROTO_lo_nest(_ARG_impl_type, _ARG_impl_name, _ARG_quals, _ARG_child_iface_name) \
- /* empty */
-#define _LO_IMPL_PROTO_lo_func(_ARG_impl_type, _ARG_impl_name, _ARG_quals, _ARG_ret_type, _ARG_func_name, ...) \
- _ARG_quals _ARG_ret_type _ARG_impl_name##_##_ARG_func_name(_ARG_impl_type * __VA_OPT__(,) __VA_ARGS__);
-
-#define _LO_IMPL_VTABLE(_ARG_impl_name, _tuple_typ, ...) \
- _LO_IMPL_VTABLE_##_tuple_typ(_ARG_impl_name, __VA_ARGS__)
-#define _LO_IMPL_VTABLE_lo_nest(_ARG_impl_name, _ARG_child_iface_name) \
- ._lo_##_ARG_child_iface_name##_vtable = _lo_##_ARG_impl_name##_##_ARG_child_iface_name##_vtable,
-#define _LO_IMPL_VTABLE_lo_func(_ARG_impl_name, _ARG_ret_type, _ARG_func_name, ...) \
- ._ARG_func_name = (void*)_ARG_impl_name##_##_ARG_func_name,
-
-#endif /* _LIBOBJ_OBJ_H_ */
diff --git a/libusb/CMakeLists.txt b/libusb/CMakeLists.txt
index 012ab71..b11e798 100644
--- a/libusb/CMakeLists.txt
+++ b/libusb/CMakeLists.txt
@@ -1,10 +1,10 @@
# libusb/CMakeLists.txt - Build script for libusb support library
#
-# Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com>
+# Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
# SPDX-License-Identifier: AGPL-3.0-or-later
add_library(libusb INTERFACE)
-target_include_directories(libusb SYSTEM INTERFACE ${CMAKE_CURRENT_LIST_DIR}/include)
+target_include_directories(libusb PUBLIC INTERFACE ${CMAKE_CURRENT_LIST_DIR}/include)
target_sources(libusb INTERFACE
usb_common.c
)
diff --git a/libusb/include/libusb/tusb_helpers.h b/libusb/include/libusb/tusb_helpers.h
index 1b4e48e..0c35f62 100644
--- a/libusb/include/libusb/tusb_helpers.h
+++ b/libusb/include/libusb/tusb_helpers.h
@@ -1,7 +1,7 @@
/* Generated by `libusb/include/libusb/tusb_helpers.h.gen `. DO NOT EDIT! */
/* libusb/tusb_helpers.h - Preprocessor macros that I think should be included in TinyUSB
*
- * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
*
* SPDX-License-Identifier: MIT
*
diff --git a/libusb/include/libusb/tusb_helpers.h.gen b/libusb/include/libusb/tusb_helpers.h.gen
index 5a5d1ac..1de1d09 100755
--- a/libusb/include/libusb/tusb_helpers.h.gen
+++ b/libusb/include/libusb/tusb_helpers.h.gen
@@ -7,7 +7,7 @@ echo "/* Generated by \`$0 $*\`. DO NOT EDIT! */"
cat <<'EOT'
/* libusb/tusb_helpers.h - Preprocessor macros that I think should be included in TinyUSB
*
- * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
*
* SPDX-License-Identifier: MIT
*
@@ -70,12 +70,19 @@ fi
if [[ ! -f 3rd-party/MS-LCID.txt || 3rd-party/MS-LCID.txt -ot 3rd-party/MS-LCID.pdf ]]; then
pdftotext -layout 3rd-party/MS-LCID.pdf
fi
-<3rd-party/MS-LCID.txt \
- grep -E '^\s*0x[0-9A-F]{4}\s+[a-z]' | sed 's/,.*//' | grep -v reserved | # find the lines we're interested in
- sed -E 's/^\s*0x(..)(..)\s+(\S.*)/\2 \1 \3/p' | tr '[:lower:]-' '[:upper:]_' | # format them as 'PRIhex SUBhex UPPER_STR'
- sort |
- sed -E 's/(..) (..) (.*)/#define LANGID_\3 0x\2\1/' | # format them as '#define LANGID_UPPER_STR 0xSUBPRI'
- column --table --output-separator=' '
+{
+ {
+ # find the lines we're interested in
+ grep -E '^\s*0x[0-9A-F]{4}\s+[a-z]' | sed 's/,.*//' | grep -v reserved
+ } | {
+ # format them as 'PRIhex SUBhex UPPER_STR'
+ sed -E 's/^\s*0x(..)(..)\s+(\S.*)/\2 \1 \3/p' | tr '[:lower:]-' '[:upper:]_'
+ } | sort | {
+ # format them as '#define LANGID_UPPER_STR 0xSUBPRI'
+ sed -E 's/(..) (..) (.*)/#define LANGID_\3 0x\2\1/' |
+ column --table --output-separator=' '
+ }
+} <3rd-party/MS-LCID.txt
cat <<'EOT'
/** USB 2.0 §9.6.6 "Endpoint", field bEndpointAddress, bit 7 */
diff --git a/libusb/usb_common.c b/libusb/usb_common.c
index 29dec42..4fe7dd4 100644
--- a/libusb/usb_common.c
+++ b/libusb/usb_common.c
@@ -1,15 +1,15 @@
/* libusb/usb_common.c - Common framework for implementing multiple USB devices at once
*
- * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
-#include <stdint.h> /* for uint{n}_t types */
-#include <stddef.h> /* for size_t */
-#include <string.h> /* memcpy() */
-#include <stdlib.h> /* for malloc(), realloc(), reallocarray() */
+#include <stddef.h> /* for size_t */
+#include <stdint.h> /* for uint{n}_t types */
+#include <stdlib.h> /* for malloc(), realloc(), reallocarray() */
+#include <string.h> /* memcpy() */
-#include <tusb.h> /* for various tusb_*_t types */
+#include <tusb.h> /* for various tusb_*_t types */
#include <libmisc/assert.h>
@@ -30,7 +30,7 @@ static struct {
uint8_t **configv;
uint16_t *serial;
uint8_t serial_bytelen;
-} globals = {0};
+} globals = {};
/* Strings ********************************************************************/
@@ -68,12 +68,12 @@ uint16_t const *tud_descriptor_string_cb(uint8_t strid, uint16_t langid) {
memcpy(desc.bString, globals.serial, bytelen);
break;
default:
- debugf("GET STRING: unknown string id=%"PRIu8, strid);
+ log_debugln("GET STRING: unknown string id=", strid);
return NULL;
}
break;
default:
- debugf("GET STRING: unknown LANGID=%"PRIx16, langid);
+ log_debugln("GET STRING: unknown LANGID=", (base16_u16_, langid));
return NULL;
}
}
@@ -100,8 +100,7 @@ void usb_common_lateinit(void) {
tud_init(CONFIG_USB_COMMON_RHPORT);
}
-COROUTINE usb_common_cr(void *_arg) {
- (void) _arg;
+COROUTINE usb_common_cr(void *LM_UNUSED(_arg)) {
cr_begin();
for (;;) {
@@ -185,7 +184,7 @@ uint8_t const *tud_descriptor_device_cb(void) {
.bNumConfigurations = 0, /* Number of possible configurations */
};
desc.bNumConfigurations = globals.configc;
- return (uint8_t const *) &desc;
+ return (uint8_t const *)&desc;
}
/**
diff --git a/notes.md b/notes.md
deleted file mode 100644
index 497363e..0000000
--- a/notes.md
+++ /dev/null
@@ -1,168 +0,0 @@
-<!--
- notes.md - Misc notes
-
- Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
- SPDX-License-Identifier: AGPL-3.0-or-later
--->
-
-Which file to include:
-
-> The <stdint.h> header is a subset of the <inttypes.h> header
-
-|------------------------------------------|-----------------------------------|--------------------------------|
-| C INTS | | |
-| `{CHAR,SHRT,INT,LONG,LLONG}_{MIN,MAX}` | `<limits.h>` | |
-| `U{CHAR,SHRT,INT,LONG,LLONG}_MAX` | `<limits.h>` | |
-|------------------------------------------|-----------------------------------|--------------------------------|
-| C SIZED INTS | | |
-| `(u)int{n}_t` (and `_{MIN,MAX,C}`) | `<stdint.h>` | exact |
-| `(u)int_least{n}_t` (and `_{MIN,MAX,C}`) | `<stdint.h>` | type may be more than `n` bits |
-| `(u)int_fast{n}_t` (and `_{MIN,MAX,C}`) | `<stdint.h>` | type may be more than `n` bits |
-| `(u)intptr_t` (and `_{MIN,MAX,C}`) | `<stdint.h>` | |
-| `(u)intmax_t` (and `_{MIN,MAX,C}`) | `<stdint.h>` | |
-| `PRI*` | `<inttypes.h>` | |
-| `SCN*` | `<inttypes.h>` | |
-|------------------------------------------|-----------------------------------|--------------------------------|
-| C ADDRESS INTS | | |
-| `ptrdiff_t` | `<stddef.h>` | |
-| `PTRDIFF_{MIN,MAX}` | `<stdint.h>` | |
-| `size_t` | `<stddef.h>` (or `<sys/types.h>`) | |
-| `SIZE_MAX` | `<stdint.h>` | |
-|------------------------------------------|-----------------------------------|--------------------------------|
-| C WCHAR INTS | | |
-| `wchar_t` | `<stddef.h>` | |
-| `WCHAR_{MIN,MAX}` | `<stdint.h>` | |
-| `wint_t` | `<wchar.h>` | |
-| `WINT_{MIN,MAX}` | `<stdint.h>` | |
-|------------------------------------------|-----------------------------------|--------------------------------|
-| POSIX INTS | | |
-| `sig_atomic_t` | `<signal.h>` | |
-| `SIG_ATOMIC_{MIN,MAX}` | `<stdint.h>` | |
-| `mode_t` | `<sys/types.h>` | unsigned |
-| `dev_t` | `<sys/types.h>` | unsigned |
-| `nlink_t` | `<sys/types.h>` | unsigned |
-| `{u,g,}id_t` | `<sys/types.h>` | unsigned |
-| `blkcnt_t` | `<sys/types.h>` | signed |
-| `off_t` | `<sys/types.h>` | signed |
-| `fsblkcnt_t` | `<sys/types.h>` | unsigned |
-| `fsfilecnt_t` | `<sys/types.h>` | unsigned |
-| `ino_t` | `<sys/types.h>` | unsigned |
-| `blksize_t` | `<sys/types.h>` | signed |
-| `pid_t` | `<sys/types.h>` | signed |
-| `ssize_t` | `<sys/types.h>` | signed |
-| `SSIZE_MAX` | `<limits.h>` | not in newlib |
-| `suseconds_t` | `<sys/types.h>` | signed |
-| `clock_t` | `<sys/types.h>` | could be float |
-| `time_t` | `<sys/types.h>` | signed |
-
-
-Here's my reading of the lowest-bitrate possible for our HDMI sink:
-
-HDMI v1.4 (2009/06/05) § 6.2.1 "Format Support Requirements"
-
-> - An HDMI Source shall support at least one of the following video
-> format timings:
-> + 640x480p @ 59.94/60Hz
-> + 720x480p @ 59.94/60Hz
-> + 720x576p @ 50Hz
-> …
->
-> - An HDMI Sink that accepts 60Hz video formats shall support the
-> 640x480p @ 59.94/60Hz and 720x480p @ 59.94/60Hz video format
-> timings
->
-> - An HDMI Sink that accepts 50Hz video formats shall support the
-> 640x480p @ 59.94/60Hz and 720x576p @ 50Hz video format timings.
-
-These latter 2 requirements match what is in CEI-861-D §3.1 "General
-Video Format requirements" Table 1.
-
-I'm a little confused about the 50Hz systems requirement; if it
-needs to support 640x480@60Hz, does that mean that it's *also* a 60Hz
-system and must therefore also support 720x480@60Hz?
-
-Anyway, I need to support at least 640x480p@60Hz and 720x480@60Hz, and
-it would be nice to support 720x576p@50Hz. Note that PicoDVI supports
-these first two, but not the @50Hz one.
-
-| format | bitrate |
-|---------------|-----------------------|
-| 640x480p@60Hz | 18,432,000 pixels/sec |
-| 720x480p@60Hz | 20,736,000 pixels/sec |
-| 720x576p@50Hz | 20,736,000 pixels/sec |
-
-https://forums.parallax.com/discussion/download/128730/Hdmi-1.4-1000008562-6364143185282736974850538.pdf
-https://ia803002.us.archive.org/1/items/CEA-861-D/CEA-861-D.pdf
-
-The RP2040 has several clocks:
-
-Sources:
-
-- GPCLK0 (GPIO-based clock 0)
-- GPCLK1 (GPIO-based clock 1)
-- XOSC (External (Crystal) Oscillator)
- + System PLL
- + USB PLL
-- ROSC (Ring Oscillator)
-
-These can be muxed onto several clocks which each have dividers (and
-most of them enable/disable too):
-
-- clk_gpout0 (GPIO Muxing)
-- clk_gpout1 (GPIO Muxing)
-- clk_gpout2 (GPIO Muxing)
-- clk_gpout3 (GPIO Muxing)
-- clk_adc (ADC)
-- clk_usb (USB)
-- clk_RTC (RTC)
-- clk_peri (UART and SPI)
-- clk_sys (CPU, bus, RAM)
-- clk_ref (watchdog and timers)
-
-```
-SSP = ARM Primecell Synchronous Serial Port
- ^ ^ ^
-```
-
-- SPI (Serial Peripheral Interface - Motorola)
-- SSI (Synchronous Serial Interface - Texas Instruments)
-- Microwire (National Semiconductor)
-
-| `sclk` | `SSPCLKOUT` | `SSP_CLK_OUT` | SSP clock output |
-| `ss_n` | `SSPFSSOUT` | `SSP_FSS_OUT` | SSP frame/slave select output |
-| `tx` | `SSPTXD` | `SSP_TX_D` | SSP transmit data |
-| `rd` | `SSPRXD` | `SSP_RX_D` | SSP receive data |
-
-"The SPI uses `clk_peri` as its reference clock for SPI timing, and is
-referred to as `SSPCLK` in the following sections. `clk_sys` is used
-as the bus clock, and is referred to as `PCLK` in the following
-sections" wut does that mean
-
-8 16-bit values in both the TX buffer and the RX buffer
-
-| Ver. 1.0.0 | 2013-08-01 | https://www.alldatasheet.com/datasheet-pdf/view/554784/ETC2/W5500.html https://www.alldatasheet.com/pdfjsview/web/viewer.html?file=//www.alldatasheet.com/datasheet-pdf/view/554784/ETC2/W5500/+_44J97VwSw9bZYvAB+/datasheet.pdf |
-| Ver. 1.0.1 | 2013-09-13 | |
-| Ver. 1.0.2 | 2013-11-14 | https://cdn.sparkfun.com/datasheets/Dev/Arduino/Shields/W5500_datasheet_v1.0.2_1.pdf |
-| Ver. 1.0.3 | 2014-05-29 | |
-| Ver. 1.0.4 | 2014-06-13 | |
-| Ver. 1.0.5 | 2014-11-10 | |
-| Ver. 1.0.6 | 2014-12-30 | |
-| Ver. 1.0.7 | 2016-02-24 | |
-| Ver. 1.0.8 | 2017-05-19 | https://docs.wiznet.io/img/products/w5500/w5500_ds_v108e.pdf (on-page version and date are wrong) |
-| Ver. 1.0.9 | 2019-05-22 | https://docs.wiznet.io/img/products/w5500/w5500_ds_v109e.pdf |
-| Ver. 1.1.0 | 2022-12-17 | https://docs.wiznet.io/img/products/w5500/W5500_ds_v110e.pdf |
-
-https://github.com/Bodmer/TFT_eSPI/discussions/2432
-
-----
-
-The theoretical max rate of the the W5500 is just shy of 80 Mb/s = 10 MB/s
-
-IDK about HDMI compression yet, but naively uncompressed we're looking
-at wanting to shove ~60 MB/s (480 Mb/s).
-
-Compression is an optional feature introduced in HDMI 2.1 :(
-
-----
-
-PIO-based USB needs the system clock to be a multiple of 120mhz