diff options
219 files changed, 13966 insertions, 9899 deletions
diff --git a/.editorconfig b/.editorconfig index c632054..ced4c65 100644 --- a/.editorconfig +++ b/.editorconfig @@ -45,13 +45,13 @@ _mode = gitignore # By specific filename (non-lib9p) ############################################# -[{build-aux/lint-{generic,unknown},build-aux/embed-sources.h.gen}] +[{build-aux/embed-sources.h.gen,build-aux/valgrind,libmisc/tests/test_obj_autobox.c.gen}] _mode = sh -[{build-aux/lint-{bin,h},build-aux/get-dscname,build-aux/valgrind,libusb/include/libusb/tusb_helpers.h.gen}] +[{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/stack.c.gen,build-aux/tent-graph,libmisc/wrap-cc}] _mode = python3 indent_style = space indent_size = 4 @@ -67,10 +67,10 @@ _mode = 9p-idl [lib9p/tests/*.explog] _mode = 9p-log -[{lib9p/tests/test_server/static.h.gen,lib9p/tests/test_compile.c.gen}] +[lib9p/tests/test_server/static.h.gen] _mode = sh -[{lib9p/linux-errno.txt.gen,lib9p/tests/runtest,lib9p/tests/testclient-p9p}] +[{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}] @@ -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/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 c8a16c00453e4db4b771d7f1281391057c7477d +Subproject bbad146d79b42081d85b76e44f136ff8d42f1a1 diff --git a/CMakeLists.txt b/CMakeLists.txt index 9fa048f..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) @@ -117,9 +125,36 @@ function(add_lib_test arg_libname 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 8636973..272306a 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -33,10 +33,18 @@ 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: lib9p/linux-errno.txt.gen $< $(linux.git) $@ +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 $^ >$@ @@ -45,6 +53,10 @@ generate/files += lib9p/core_generated.c lib9p/core_include/lib9p/_core_generate 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/core_include/lib9p/_core_generated.h $^ $@ @@ -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,26 +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 -build: $(foreach t,$(build_types),$(foreach p,$(platforms),build/$p-$t/build)) +# span the matrix + +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: - $(MAKE) -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/%/build +# 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) -.PHONY: $(foreach t,$(build_types),$(foreach p,$(platforms),build/$p-$t/check)) + gcovr $(gcovr_flags) -- $(@D) +.PHONY: $(foreach t,$(build_types),$(foreach p,$(platforms_check),build/$p-$t/check)) # `lint` and `format` ########################################################## @@ -106,47 +154,30 @@ build-aux/venv: build-aux/requirements.txt $@/bin/pip install -r $< touch --no-create $@ +flashimgs = $(patsubst flashimg/%/CMakeLists.txt,%,$(filter flashimg/%/CMakeLists.txt,$(sources_cmake))) + # `lint` ############################### -# generic ########## -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 +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/all: lint/%: build-aux/lint-generic build-aux/get-dscname - ./build-aux/lint-generic '%s\n' $(sources_$*) -lint/unknown: lint/%: build-aux/lint-unknown - ./build-aux/lint-unknown $(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/% -# specific ######### -lint/c: lint/%: build-aux/lint-h build-aux/get-dscname - ./build-aux/lint-h $(filter %.h,$(sources_$*)) -lint/sh lint/bash: lint/%: - shellcheck $(sources_$*) - shfmt --diff --case-indent --simplify $(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/core.gen lib9p/core_gen/*.py - ./build-aux/venv/bin/pytest $(foreach f,$(sources_python3),$(if $(filter test_%.py,$(notdir $f)),$f)) -lint/make lint/cmake lint/gitignore lint/ini lint/9p-idl lint/9p-log lint/markdown lint/pip lint/man-cat: lint/%: - @: TODO: Write/adopt linters for these file types # `format` ############################# # generic ########## -format: - $(MAKE) -k INNER=t $(patsubst sources_%,format/%,$(filter-out sources_all sources_unknown,$(filter sources_%,$(.VARIABLES)))) +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/sh format/bash: format/%: - shfmt --write --case-indent --simplify $(sources_$*) -format/python3: format/%: ./build-aux/venv - ./build-aux/venv/bin/black $(sources_$*) - ./build-aux/venv/bin/isort $(sources_$*) -format/make format/cmake format/gitignore format/ini format/9p-idl format/9p-log format/markdown format/pip format/man-cat: 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 @@ -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 3201122..0000000 --- a/PLAN.md +++ /dev/null @@ -1,31 +0,0 @@ -<!-- - PLAN.md - Misc planning notes - - Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com> - SPDX-License-Identifier: AGPL-3.0-or-later ---> - -- [ ] syslog on 9p -- [ ] better 9p-shutdown -- [ ] PIO-based USB -- [ ] wire the USB keyboard to 9P -- [ ] wire uart to 9p - -- SDcard to 9p -- PicoDVI -- solder up the bus switch - -- 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 @@ -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,20 +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` (TODO: I don't really - understand what OpenOCD is doing that it wants the `.elf` instead - of the `.bin`) - . + 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 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 91f1612..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) @@ -97,13 +97,13 @@ 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) \ + <(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) ) } @@ -114,6 +114,7 @@ lint_func_blocklist() { local blocklist=( gpio_default_irq_handler + {,__wrap,weak_raw_,stdio_,_}{,v}{,sn}printf ) while read -r func; do @@ -131,6 +132,12 @@ main() { { echo 'Global variables:' lint_globals "${elf}.map" | sed 's/^/ /' + 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" diff --git a/build-aux/lint-generic b/build-aux/lint-generic deleted file mode 100755 index 70e814a..0000000 --- a/build-aux/lint-generic +++ /dev/null @@ -1,60 +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 not 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 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=$(./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 7459032..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 bbb0eae..a921407 100644 --- a/build-aux/measurestack/app_plugins.py +++ b/build-aux/measurestack/app_plugins.py @@ -14,13 +14,11 @@ from .util import synthetic_node # pylint: disable=unused-variable __all__ = [ "CmdPlugin", - "LibObjPlugin", "LibHWPlugin", "LibCRPlugin", "LibCRIPCPlugin", "Lib9PPlugin", "LibMiscPlugin", - "PicoFmtPlugin", "PicoSDKPlugin", "TinyUSBDevicePlugin", "NewlibPlugin", @@ -41,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: @@ -74,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} @@ -93,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} @@ -117,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 @@ -137,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 [ @@ -161,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: @@ -175,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"), @@ -219,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: @@ -241,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: @@ -257,37 +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_MAX_CONNS: int | None _CONFIG_9P_MAX_REQS: int | None - formatters: typing.Collection[BaseName] 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,7 +328,7 @@ class Lib9PPlugin: ) lib9p_generated_c_fname = util.get_zero_or_one( - lambda fname: fname.endswith("lib9p/_core_generated.c"), arg_c_fnames + lambda fname: fname.endswith("lib9p/core_generated.c"), arg_c_fnames ) # Read config ########################################################## @@ -322,27 +345,17 @@ class Lib9PPlugin: return int(eval(line)) # pylint: disable=eval-used return None - self._CONFIG_9P_MAX_CONNS = config_h_get("_CONFIG_9P_MAX_REQS") + 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 @@ -368,195 +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 - ), - } - 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__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 @@ -590,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"), @@ -626,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: @@ -646,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 @@ -660,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"})] @@ -680,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"}), @@ -738,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)"} @@ -754,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"}), @@ -774,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: @@ -818,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)) @@ -830,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: @@ -863,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: @@ -879,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 @@ -904,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"}), @@ -918,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), @@ -948,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: @@ -962,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 [] @@ -981,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 index 7ad2712..0700e4d 100755 --- a/build-aux/valgrind +++ b/build-aux/valgrind @@ -1,4 +1,4 @@ -#!/bin/env bash +#!/bin/sh # build-aux/valgrind - Wrapper around valgrind to keep flags consistent # # Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com> diff --git a/cmd/sbc_harness/CMakeLists.txt b/cmd/sbc_harness/CMakeLists.txt deleted file mode 100644 index 6e722d7..0000000 --- a/cmd/sbc_harness/CMakeLists.txt +++ /dev/null @@ -1,84 +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 - - fs_harness_flash_bin.c - fs_harness_uptime_txt.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 - hardware_watchdog - - libmisc - libfmt - libusb - libdhcp - libhw_cr - lib9p_srv - 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 - static/Documentation/harness_uptime_txt.txt -) - -endif() diff --git a/cmd/sbc_harness/fs_harness_flash_bin.c b/cmd/sbc_harness/fs_harness_flash_bin.c deleted file mode 100644 index bc3d061..0000000 --- a/cmd/sbc_harness/fs_harness_flash_bin.c +++ /dev/null @@ -1,312 +0,0 @@ -/* sbc_harness/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, static); - -LO_IMPLEMENTATION_H(lib9p_srv_fio, struct flash_file, flash_file); -LO_IMPLEMENTATION_C(lib9p_srv_fio, struct flash_file, flash_file, static); - -#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 can (and - * arguably 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); - - infof("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); - } - - infof("rebooting..."); - - watchdog_reboot(0, 0, 300); - - for (;;) - asm volatile ("nop"); -} - -/** - * Set the upper half of flash to all zero bytes. - * - * @param buf : a scratch buffer that is at least FLASH_SECTOR_SIZE - */ -static void ab_flash_initialize_zero(uint8_t *buf) { - assert(buf); - - memset(buf, 0, FLASH_SECTOR_SIZE); - - infof("zeroing 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(); - /* No need to `flash_range_erase()`; the way the flash - * works is that _erase() sets all bits to 1, and - * _program() sets some bits to 0. If we don't need - * any bits to be 1, then we can skip the - * _erase(). */ - flash_range_program(off, buf, FLASH_SECTOR_SIZE); - cr_restore_interrupts(saved); - } - debugf("... zeroed"); -} - -/** - * Copy the lower half of flash to the upper half of flash. - * - * @param buf : a scratch buffer that is at least FLASH_SECTOR_SIZE - */ -static void ab_flash_initialize(uint8_t *buf) { - assert(buf); - - infof("initializing upper flash..."); - for (size_t off = 0; off < DATA_HSIZE; off += FLASH_SECTOR_SIZE) { - memcpy(buf, DATA_START+off, FLASH_SECTOR_SIZE); - if (memcmp(buf, DATA_START+DATA_HSIZE+off, FLASH_SECTOR_SIZE) == 0) - continue; - bool saved = cr_save_and_disable_interrupts(); - flash_range_erase(DATA_HSIZE+off, FLASH_SECTOR_SIZE); - flash_range_program(DATA_HSIZE+off, buf, FLASH_SECTOR_SIZE); - cr_restore_interrupts(saved); - } - debugf("... initialized"); -} - -/** - * 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, must be in the upper half of the flash - * @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; - - infof("write flash sector @ %zu...", 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); - } - debugf("... written"); -} - -/* srv_file *******************************************************************/ - -static void flash_file_free(struct flash_file *self) { - assert(self); -} -static struct lib9p_qid flash_file_qid(struct flash_file *self) { - assert(self); - - return (struct lib9p_qid){ - .type = LIB9P_QT_FILE|LIB9P_QT_EXCL, - .vers = 1, - .path = self->pathnum, - }; -} - -static struct lib9p_stat flash_file_stat(struct flash_file *self, struct lib9p_srv_ctx *ctx) { - assert(self); - assert(ctx); - - return (struct lib9p_stat){ - .kern_type = 0, - .kern_dev = 0, - .file_qid = flash_file_qid(self), - .file_mode = LIB9P_DM_EXCL|0666, - .file_atime = UTIL9P_ATIME, - .file_mtime = UTIL9P_MTIME, - .file_size = DATA_SIZE, - .file_name = lib9p_str(self->name), - .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 flash_file_wstat(struct flash_file *self, struct lib9p_srv_ctx *ctx, - struct lib9p_stat) { - assert(self); - assert(ctx); - - lib9p_error(&ctx->basectx, LIB9P_ERRNO_L_EROFS, "read-only part of filesystem"); -} -static void flash_file_remove(struct flash_file *self, struct lib9p_srv_ctx *ctx) { - assert(self); - assert(ctx); - - lib9p_error(&ctx->basectx, LIB9P_ERRNO_L_EROFS, "read-only part of filesystem"); -} - -LIB9P_SRV_NOTDIR(struct flash_file, flash_file); - -static lo_interface lib9p_srv_fio flash_file_fopen(struct flash_file *self, struct lib9p_srv_ctx *ctx, - bool rd, bool wr, bool trunc) { - assert(self); - assert(ctx); - - if (rd) { - self->rbuf.ok = false; - } - - if (wr) { - if (trunc) { - ab_flash_initialize_zero(self->wbuf.dat); - self->written = true; - } else { - ab_flash_initialize(self->wbuf.dat); - self->written = false; - } - self->wbuf.ok = false; - } - - return lo_box_flash_file_as_lib9p_srv_fio(self); -} - -/* srv_fio ********************************************************************/ - -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); - - if (self->wbuf.ok) - ab_flash_write_sector(self->wbuf.pos, self->wbuf.dat); - - if (self->written) - ab_flash_finalize(self->wbuf.dat); -} - -static void flash_file_pread(struct flash_file *self, struct lib9p_srv_ctx *ctx, - uint32_t byte_count, uint64_t byte_offset, - struct iovec *ret) { - assert(self); - assert(ctx); - assert(ret); - - if (byte_offset > DATA_SIZE) { - lib9p_error(&ctx->basectx, - LIB9P_ERRNO_L_EINVAL, "offset is past the chip size"); - return; - } - - /* Assume that somewhere down the line the iovec we return - * 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. - */ - if (byte_offset == DATA_SIZE) { - *ret = (struct iovec){ - .iov_len = 0, - }; - return; - } - size_t sector_base = LM_ROUND_DOWN(byte_offset, FLASH_SECTOR_SIZE); - if (byte_offset + byte_count > sector_base + FLASH_SECTOR_SIZE) - byte_count = (sector_base + FLASH_SECTOR_SIZE) - byte_offset; - assert(byte_offset + byte_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); - } - - *ret = (struct iovec){ - .iov_base = &self->rbuf.dat[byte_offset-sector_base], - .iov_len = byte_count, - }; -} - -/* TODO: Short/corrupt writes are dangerous. This should either (1) - * check a checksum, (2) use uf2 instead of verbatim data, or (3) use - * ihex instead of verbatim data. */ -static uint32_t flash_file_pwrite(struct flash_file *self, struct lib9p_srv_ctx *ctx, - void *buf, - uint32_t byte_count, - uint64_t byte_offset) { - assert(self); - assert(ctx); - - if (byte_offset > DATA_HSIZE) { - lib9p_error(&ctx->basectx, - LIB9P_ERRNO_L_EINVAL, "offset is past half the chip size"); - return 0; - } - if (byte_count == 0) - return 0; - if (byte_offset == DATA_HSIZE) { - lib9p_error(&ctx->basectx, - LIB9P_ERRNO_L_EINVAL, "offset is at half the chip size"); - return 0; - } - - size_t sector_base = LM_ROUND_DOWN(byte_offset, FLASH_SECTOR_SIZE); - if (byte_offset + byte_count > sector_base + FLASH_SECTOR_SIZE) - byte_count = (sector_base + FLASH_SECTOR_SIZE) - byte_offset; - assert(byte_offset + byte_count < 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 (byte_count != FLASH_SECTOR_SIZE) - memcpy(self->wbuf.dat, DATA_START+DATA_HSIZE+sector_base, FLASH_SECTOR_SIZE); - } - memcpy(&self->wbuf.dat[byte_offset-sector_base], buf, byte_count); - - self->written = true; - return byte_count; -} diff --git a/cmd/sbc_harness/fs_harness_uptime_txt.c b/cmd/sbc_harness/fs_harness_uptime_txt.c deleted file mode 100644 index 1425bf9..0000000 --- a/cmd/sbc_harness/fs_harness_uptime_txt.c +++ /dev/null @@ -1,156 +0,0 @@ -/* sbc_harness/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 <stdio.h> /* for snprintf() */ -#include <stdlib.h> /* for malloc(), free() */ - -#include <libhw/generic/alarmclock.h> -#include <util9p/static.h> - -#include "fs_harness_uptime_txt.h" - -LO_IMPLEMENTATION_C(lib9p_srv_file, struct uptime_file, uptime_file, static); - -struct uptime_fio { - struct uptime_file *parent; - size_t buf_len; - char buf[24]; /* len(str(UINT64_MAX)+"ns\n\0") */ -}; - -LO_IMPLEMENTATION_H(lib9p_srv_fio, struct uptime_fio, uptime_fio); -LO_IMPLEMENTATION_C(lib9p_srv_fio, struct uptime_fio, uptime_fio, static); - -/* srv_file *******************************************************************/ - -static void uptime_file_free(struct uptime_file *self) { - assert(self); -} -static 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, - }; -} - -static struct lib9p_stat uptime_file_stat(struct uptime_file *self, struct lib9p_srv_ctx *ctx) { - assert(self); - assert(ctx); - - uint64_t now = LO_CALL(bootclock, get_time_ns); - uint64_t size = 0; - while (now) { - size++; - now /= 10; - } - if (!size) - size++; - size += 3; - - return (struct lib9p_stat){ - .kern_type = 0, - .kern_dev = 0, - .file_qid = uptime_file_qid(self), - .file_mode = 0444, - .file_atime = UTIL9P_ATIME, - .file_mtime = UTIL9P_MTIME, - .file_size = size, - .file_name = lib9p_str(self->name), - .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 uptime_file_wstat(struct uptime_file *self, struct lib9p_srv_ctx *ctx, - struct lib9p_stat) { - assert(self); - assert(ctx); - - lib9p_error(&ctx->basectx, LIB9P_ERRNO_L_EROFS, "read-only part of filesystem"); -} -static void uptime_file_remove(struct uptime_file *self, struct lib9p_srv_ctx *ctx) { - assert(self); - assert(ctx); - - lib9p_error(&ctx->basectx, LIB9P_ERRNO_L_EROFS, "read-only part of filesystem"); -} - -LIB9P_SRV_NOTDIR(struct uptime_file, uptime_file); - -static lo_interface lib9p_srv_fio 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 = malloc(sizeof(struct uptime_fio)); - ret->parent = self; - ret->buf_len = 0; - - return lo_box_uptime_fio_as_lib9p_srv_fio(ret); -} - -/* srv_fio ********************************************************************/ - -static uint32_t uptime_fio_iounit(struct uptime_fio *self) { - assert(self); - return sizeof(self->buf)-1; -} - -static void uptime_fio_iofree(struct uptime_fio *self) { - assert(self); - free(self); -} - -static struct lib9p_qid uptime_fio_qid(struct uptime_fio *self) { - assert(self); - assert(self->parent); - return uptime_file_qid(self->parent); -} - -static void uptime_fio_pread(struct uptime_fio *self, struct lib9p_srv_ctx *ctx, - uint32_t byte_count, uint64_t byte_offset, - struct iovec *ret) { - assert(self); - assert(ctx); - assert(ret); - - if (byte_offset == 0 || self->buf_len == 0) { - uint64_t now = LO_CALL(bootclock, get_time_ns); - self->buf_len = snprintf(self->buf, sizeof(self->buf), "%"PRIu64"ns\n", now); - } - - if (byte_offset > (uint64_t)self->buf_len) { - lib9p_error(&ctx->basectx, - LIB9P_ERRNO_L_EINVAL, "offset is past end-of-file length"); - return; - } - - 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; - *ret = (struct iovec){ - .iov_base = &self->buf[beg_off], - .iov_len = end_off-beg_off, - }; -} - -static uint32_t uptime_fio_pwrite(struct uptime_fio *self, struct lib9p_srv_ctx *ctx, - void *LM_UNUSED(buf), - uint32_t LM_UNUSED(byte_count), - uint64_t LM_UNUSED(byte_offset)) { - assert(self); - assert(ctx); - - lib9p_error(&ctx->basectx, LIB9P_ERRNO_L_EROFS, "read-only part of filesystem"); - return 0; -} diff --git a/cmd/sbc_harness/fs_harness_uptime_txt.h b/cmd/sbc_harness/fs_harness_uptime_txt.h deleted file mode 100644 index 7bf2945..0000000 --- a/cmd/sbc_harness/fs_harness_uptime_txt.h +++ /dev/null @@ -1,19 +0,0 @@ -/* sbc_harness/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 _SBC_HARNESS_FS_HARNESS_UPTIME_TXT_H_ -#define _SBC_HARNESS_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); -#define lo_box_uptime_file_as_lib9p_srv_file(obj) util9p_box(uptime_file, obj) - -#endif /* _SBC_HARNESS_FS_HARNESS_UPTIME_TXT_H_ */ 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_uptime_txt.txt b/cmd/sbc_harness/static/Documentation/harness_uptime_txt.txt deleted file mode 100644 index 1ab86f7..0000000 --- a/cmd/sbc_harness/static/Documentation/harness_uptime_txt.txt +++ /dev/null @@ -1,17 +0,0 @@ -NAME - /harness/uptime.txt - -DESCRIPTION - Reading this file gives a text string of the format - `sprintf("%uns\n", uptime_ns)` indicating the harness's uptime - in an integer number of nanoseconds. - -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. - -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/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 5367dbe..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 @@ -37,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 @@ -57,6 +65,7 @@ * (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) @@ -64,12 +73,6 @@ */ #define CONFIG_9P_SRV_MAX_HOSTMSG_SIZE CONFIG_9P_SRV_MAX_MSG_SIZE+16 -#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 ***********************************************************************/ #define CONFIG_DHCP_CAN_RECV_UNICAST_IP_WITHOUT_IP 0 /* bool */ @@ -90,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_read9p_cr; -extern const size_t CONFIG_COROUTINE_STACK_SIZE_write9p_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 */ @@ -104,11 +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_COROUTINE_NUM ( \ - 1 /* usb_common */ + \ - 1 /* usb_keyboard */ + \ - 1 /* W5500 irq handler */ + \ - _CONFIG_9P_MAX_CONNS /* 9P accept()+read() */ + \ - _CONFIG_9P_MAX_REQS /* 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/cmd/sbc_harness/fs_harness_flash_bin.h b/flashimg/cpu_main/fs_harness_flash_bin.h index 36382be..68cc953 100644 --- a/cmd/sbc_harness/fs_harness_flash_bin.h +++ b/flashimg/cpu_main/fs_harness_flash_bin.h @@ -1,30 +1,36 @@ -/* sbc_harness/fs_harness_flash_bin.h - 9P access to flash storage +/* 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 _SBC_HARNESS_FS_HARNESS_FLASH_BIN_H_ -#define _SBC_HARNESS_FS_HARNESS_FLASH_BIN_H_ +#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> -struct flash_file { - char *name; - uint64_t pathnum; +#include "ihex.h" - BEGIN_PRIVATE(FS_HARNESS_FLASH_BIN); - bool written; +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); -#define lo_box_flash_file_as_lib9p_srv_file(obj) util9p_box(flash_file, obj) -#endif /* _SBC_HARNESS_FS_HARNESS_FLASH_BIN_H_ */ +#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 5630e83..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,10 +31,10 @@ #include <libmisc/log.h> /* local headers */ -#include "usb_keyboard.h" -#include "static.h" #include "fs_harness_flash_bin.h" #include "fs_harness_uptime_txt.h" +#include "static.h" +#include "usb_keyboard.h" /* configuration **************************************************************/ @@ -61,7 +62,7 @@ enum { PATH_BASE = __COUNTER__ }; __VA_OPT__(,) __VA_ARGS__ \ })) -struct lib9p_srv_file root = +static struct lib9p_srv_file root = STATIC_DIR("", STATIC_DIR("Documentation", STATIC_FILE("YOUR_RIGHTS_AND_OBLIGATIONS.md", @@ -71,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, @@ -114,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 ***********************************************************************/ @@ -129,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; } } @@ -149,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(); } @@ -157,7 +158,7 @@ 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_accept_and_read_loop(&globals.srv, listener); @@ -173,7 +174,7 @@ static COROUTINE write9p_cr(void *) { cr_end(); } -const char *const hexdig = "0123456789ABCDEF"; +static const char *const hexdig = "0123456789ABCDEF"; static_assert(_CONFIG_9P_MAX_REQS <= 16); COROUTINE init_cr(void *) { @@ -204,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){{ @@ -221,33 +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); + 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_MAX_REQS; i++) { char name[] = {'w', 'r', 'i', 't', 'e', '-', hexdig[i], '\0'}; - coroutine_add(name, write9p_cr, NULL); + 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 b3fc12e..b3fc12e 100644 --- a/cmd/sbc_harness/static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS.md +++ b/flashimg/cpu_main/static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS.md 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/cmd/sbc_harness/static/Documentation/harness_flash_bin.txt b/flashimg/cpu_main/static/Documentation/harness_flash.bin.txt index 115f2ee..1b58d6d 100644 --- a/cmd/sbc_harness/static/Documentation/harness_flash_bin.txt +++ b/flashimg/cpu_main/static/Documentation/harness_flash.bin.txt @@ -2,14 +2,20 @@ NAME /harness/flash.bin DESCRIPTION - Access to the flash storage chip (where the harness firmware - is stored). + Access the flash storage chip (where the harness firmware is + stored). - Any number of readers may read the flash contents. + Reading from the file reads the raw flash contents. - Only one writer can have the file open at a time; once the + 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. - Writes to the top half of the chip will fail. BUGS - The size of the chip is configured at compile-time. If the @@ -21,13 +27,17 @@ BUGS chip will crash. - When writing to the flash using this file, only half of the - chip capacity is usable; the top half and bottom half are - mirrors of each-other. This is to avoid the firmware + 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/cmd/sbc_harness/usb_keyboard.h b/flashimg/cpu_main/usb_keyboard.h index cf8483b..52b9b67 100644 --- a/cmd/sbc_harness/usb_keyboard.h +++ b/flashimg/cpu_main/usb_keyboard.h @@ -1,11 +1,11 @@ -/* sbc_harness/usb_keyboard.h - Implementation of a USB keyboard device +/* 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 _SBC_HARNESS_USB_KEYBOARD_H_ -#define _SBC_HARNESS_USB_KEYBOARD_H_ +#ifndef _FLASHIMG_CPU_MAIN_USB_KEYBOARD_H_ +#define _FLASHIMG_CPU_MAIN_USB_KEYBOARD_H_ #include <stdint.h> /* for uint32_t */ @@ -17,4 +17,4 @@ 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_ */ +#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/lib9p/CMakeLists.txt b/lib9p/CMakeLists.txt index 543d01a..9ef8465 100644 --- a/lib9p/CMakeLists.txt +++ b/lib9p/CMakeLists.txt @@ -3,23 +3,36 @@ # Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com> # SPDX-License-Identifier: AGPL-3.0-or-later +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 +) + add_library(lib9p_core INTERFACE) -target_include_directories(lib9p_core PUBLIC INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/core_include) +target_link_libraries(lib9p_core INTERFACE lib9p_core_headers) target_sources(lib9p_core INTERFACE core.c core_generated.c - core_tables.c ) target_link_libraries(lib9p_core INTERFACE - libfmt 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_include_directories(lib9p_srv PUBLIC INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/srv_include) +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 @@ -50,6 +63,26 @@ if (ENABLE_TESTS) add_lib9p_executable("testclient-sess") add_lib9p_test("./testclient-sess") - add_lib_test(lib9p_core test_compile) - target_include_directories(test_compile PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_compile_config) + 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]" + ) + 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 index a07461d..ae2d3ca 100644 --- a/lib9p/core.c +++ b/lib9p/core.c @@ -4,27 +4,29 @@ * 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 <string.h> /* for strlen(), strnlen(), strncpy(), memcmp(), memset() */ -#include <libfmt/fmt.h> /* for fmt_vsnprintf() */ +#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(char *s) { +struct lib9p_s lib9p_str(const char *s) { if (!s) - return (struct lib9p_s){0}; + return (struct lib9p_s){}; return (struct lib9p_s){ .len = strlen(s), .utf8 = s, }; } -struct lib9p_s lib9p_strn(char *s, size_t maxlen) { +struct lib9p_s lib9p_strn(const char *s, size_t maxlen) { if (maxlen == 0 || !s) - return (struct lib9p_s){0}; + return (struct lib9p_s){}; return (struct lib9p_s){ .len = strnlen(s, maxlen), .utf8 = s, @@ -43,53 +45,163 @@ bool lib9p_str_eq(struct lib9p_s a, struct lib9p_s b) { (a.len == 0 || memcmp(a.utf8, b.utf8, a.len) == 0); } -/* ctx ************************************************************************/ +/* bounds checks **************************************************************/ -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'; +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); } -bool lib9p_ctx_has_error(struct lib9p_ctx *ctx) { +/* 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); - return ctx->err_msg[0]; + 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); } -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'; +#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) -#if CONFIG_9P_ENABLE_9P2000_u - ctx->err_num = linux_errno; -#else - (void)(linux_errno); -#endif +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); +} - return -1; +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); } -int lib9p_errorf(struct lib9p_ctx *ctx, lib9p_errno_t linux_errno, char const *fmt, ...) { - int n; - va_list args; +#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) - 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)); +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); +} -#if CONFIG_9P_ENABLE_9P2000_u - ctx->err_num = linux_errno; -#else - (void)(linux_errno); -#endif +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); +} - return -1; +/* `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/core.gen b/lib9p/core.gen index b30ec31..24f66de 100755 --- a/lib9p/core.gen +++ b/lib9p/core.gen @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # lib9p/core.gen - Generate C marshalers/unmarshalers for .9p files # defining 9P protocol variants. # diff --git a/lib9p/core_gen/c.py b/lib9p/core_gen/c.py index b2e856d..ab34387 100644 --- a/lib9p/core_gen/c.py +++ b/lib9p/core_gen/c.py @@ -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/core.h> #include "core_tables.h" -#include "core_utf8.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,21 +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)]: - # XXX: There are good arguments that min_msg_size should be - # something larger than rerror.min_size(). - # srv.c:respond_error() assumes that min_msg_size is - # rerror.min_size(); if you do change min_msg_size to - # something larger, then be sure to update respond_error(). - 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) - if ver == "9P2000.L": # SPECIAL (9P2000.L) - rlerror = next(typ for typ in typs if typ.typname == "Rlerror") - min_msg_size = rlerror.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" @@ -143,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" @@ -166,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)) @@ -194,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) {{ @@ -203,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/core_gen/c9util.py b/lib9p/core_gen/c9util.py index 84fdee4..7e1e41d 100644 --- a/lib9p/core_gen/c9util.py +++ b/lib9p/core_gen/c9util.py @@ -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/core_gen/c_format.py b/lib9p/core_gen/c_fmt_print.py index f9eee90..7a0a9d3 100644 --- a/lib9p/core_gen/c_format.py +++ b/lib9p/core_gen/c_fmt_print.py @@ -1,4 +1,4 @@ -# lib9p/core_gen/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,46 +21,34 @@ def bf_numname(typ: idl.Bitfield, num: idl.BitNum, base: str) -> str: return c9util.Ident(c9util.add_prefix(prefix, base)) -def ext_printf(line: str) -> str: - assert line.startswith("\t") - assert line.endswith("\n") - # It sucks that %v trips -Wformat and -Wformat-extra-args - # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47781 - ret = "#pragma GCC diagnostic push\n" - ret += '#pragma GCC diagnostic ignored "-Wformat"\n' - ret += '#pragma GCC diagnostic ignored "-Wformat-extra-args"\n' - ret += line - ret += "#pragma GCC diagnostic pop\n" - return ret - - -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): @@ -72,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(): @@ -86,34 +74,32 @@ 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 += ext_printf( - '\tfmt_state_printf(state, "%.*q", self->len, self->utf8);\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 @@ -126,36 +112,34 @@ def gen_c_format(versions: set[str], typs: list[idl.UserType]) -> str: cnt_str = f"self->{member.cnt.membname}" cnt_typ = c9util.typename(member.cnt.typ) if member.typ.static_size == 1: # SPECIAL (data) - ret += f"\tif (is_valid_utf8_without_nul((uint8_t *)self->{member.membname}, (size_t){cnt_str})) {{\n" - ret += ext_printf( - f'\t\tfmt_state_printf(state, " {member.membname}=%.*q%s",\n' - f"\t\t\t(int)({cnt_str} < 50 ? {cnt_str} : 50),\n" - f"\t\t\t(char *)self->{member.membname},\n" - f'\t\t\t{cnt_str} < 50 ? "" : "...");\n' - ) + 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_state_puts(state, " {member.membname}=<bytedata>");\n' + ret += f'\t\tfmt_print_str(w, " {member.membname}=<bytedata>");\n' ret += "\t}\n" continue - ret += f'\tfmt_state_puts(state, " {member.membname}=[");\n' + 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_putchar(state, ',');\n" - ret += "\t\tfmt_state_putchar(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/core_gen/c_marshal.py b/lib9p/core_gen/c_marshal.py index 620bdea..2747ce8 100644 --- a/lib9p/core_gen/c_marshal.py +++ b/lib9p/core_gen/c_marshal.py @@ -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 += f'\t\tlib9p_errorf(ctx, {c9util.IDENT("ERRNO_L_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/core_gen/c_unmarshal.py b/lib9p/core_gen/c_unmarshal.py index 1afbe1d..206a85b 100644 --- a/lib9p/core_gen/c_unmarshal.py +++ b/lib9p/core_gen/c_unmarshal.py @@ -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/core_gen/c_validate.py b/lib9p/core_gen/c_validate.py index 3073ed0..1bfe329 100644 --- a/lib9p/core_gen/c_validate.py +++ b/lib9p/core_gen/c_validate.py @@ -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" - f'\t\treturn lib9p_error(ctx, {c9util.IDENT("ERRNO_L_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" - f'\t\treturn lib9p_errorf(ctx, {c9util.IDENT("ERRNO_L_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" - f'\t\t\treturn lib9p_error(ctx, {c9util.IDENT("ERRNO_L_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" - f'\t\treturn lib9p_error(ctx, {c9util.IDENT("ERRNO_L_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, {c9util.IDENT("ERRNO_L_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, {c9util.IDENT("ERRNO_L_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, {c9util.IDENT("ERRNO_L_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/core_gen/h.py b/lib9p/core_gen/h.py index 3defcb8..eba1492 100644 --- a/lib9p/core_gen/h.py +++ b/lib9p/core_gen/h.py @@ -162,21 +162,13 @@ def gen_h(versions: set[str], typs: list[idl.UserType]) -> str: #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/io.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/core_generated.c b/lib9p/core_generated.c index 1789bdc..5acc551 100644 --- a/lib9p/core_generated.c +++ b/lib9p/core_generated.c @@ -1,141 +1,15 @@ -/* Generated by `lib9p/core.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! */ -#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/core.h> #include "core_tables.h" -#include "core_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); -#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_Rerror, lib9p_msg_Rerror, 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_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 @@ -163,6 +37,11 @@ LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Rswrite, lib9p_msg_Rswrite, #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 @@ -177,168 +56,192 @@ LO_IMPLEMENTATION_C(fmt_formatter, struct lib9p_msg_Rswrite, lib9p_msg_Rswrite, #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, + [LIB9P_VER_9P2000] = 0b11111100000000000000000111111111, #endif /* CONFIG_9P_ENABLE_9P2000 */ #if CONFIG_9P_ENABLE_9P2000_L - [LIB9P_VER_9P2000_L] = 0b00000000000000000000000000000000, + [LIB9P_VER_9P2000_L] = 0b00000000000000000000000000000000, #endif /* CONFIG_9P_ENABLE_9P2000_L */ #if CONFIG_9P_ENABLE_9P2000_e - [LIB9P_VER_9P2000_e] = 0b11111100000000000000000111111111, + [LIB9P_VER_9P2000_e] = 0b11111100000000000000000111111111, #endif /* CONFIG_9P_ENABLE_9P2000_e */ #if CONFIG_9P_ENABLE_9P2000_p9p - [LIB9P_VER_9P2000_p9p] = 0b11111100000000000000000111111111, + [LIB9P_VER_9P2000_p9p] = 0b11111100000000000000000111111111, #endif /* CONFIG_9P_ENABLE_9P2000_p9p */ #if CONFIG_9P_ENABLE_9P2000_u - [LIB9P_VER_9P2000_u] = 0b11111100101111000000000111111111, + [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, + [LIB9P_VER_9P2000] = 0b11111100, #endif /* CONFIG_9P_ENABLE_9P2000 */ #if CONFIG_9P_ENABLE_9P2000_L - [LIB9P_VER_9P2000_L] = 0b11111100, + [LIB9P_VER_9P2000_L] = 0b11111100, #endif /* CONFIG_9P_ENABLE_9P2000_L */ #if CONFIG_9P_ENABLE_9P2000_e - [LIB9P_VER_9P2000_e] = 0b11111100, + [LIB9P_VER_9P2000_e] = 0b11111100, #endif /* CONFIG_9P_ENABLE_9P2000_e */ #if CONFIG_9P_ENABLE_9P2000_p9p - [LIB9P_VER_9P2000_p9p] = 0b11111100, + [LIB9P_VER_9P2000_p9p] = 0b11111100, #endif /* CONFIG_9P_ENABLE_9P2000_p9p */ #if CONFIG_9P_ENABLE_9P2000_u - [LIB9P_VER_9P2000_u] = 0b11111110, + [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, + [LIB9P_VER_9P2000] = 0b01010011, #endif /* CONFIG_9P_ENABLE_9P2000 */ #if CONFIG_9P_ENABLE_9P2000_L - [LIB9P_VER_9P2000_L] = 0b00000000, + [LIB9P_VER_9P2000_L] = 0b00000000, #endif /* CONFIG_9P_ENABLE_9P2000_L */ #if CONFIG_9P_ENABLE_9P2000_e - [LIB9P_VER_9P2000_e] = 0b01010011, + [LIB9P_VER_9P2000_e] = 0b01010011, #endif /* CONFIG_9P_ENABLE_9P2000_e */ #if CONFIG_9P_ENABLE_9P2000_p9p - [LIB9P_VER_9P2000_p9p] = 0b01010011, + [LIB9P_VER_9P2000_p9p] = 0b01010011, #endif /* CONFIG_9P_ENABLE_9P2000_p9p */ #if CONFIG_9P_ENABLE_9P2000_u - [LIB9P_VER_9P2000_u] = 0b01010011, + [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, + [LIB9P_VER_9P2000] = 0b00000000000000000000000000000000, #endif /* CONFIG_9P_ENABLE_9P2000 */ - [LIB9P_VER_9P2000_L] = 0b00000000000111111111111111000011, + [LIB9P_VER_9P2000_L] = 0b00000000000111111111111111000011, #if CONFIG_9P_ENABLE_9P2000_e - [LIB9P_VER_9P2000_e] = 0b00000000000000000000000000000000, + [LIB9P_VER_9P2000_e] = 0b00000000000000000000000000000000, #endif /* CONFIG_9P_ENABLE_9P2000_e */ #if CONFIG_9P_ENABLE_9P2000_p9p - [LIB9P_VER_9P2000_p9p] = 0b00000000000000000000000000000000, + [LIB9P_VER_9P2000_p9p] = 0b00000000000000000000000000000000, #endif /* CONFIG_9P_ENABLE_9P2000_p9p */ #if CONFIG_9P_ENABLE_9P2000_u - [LIB9P_VER_9P2000_u] = 0b00000000000000000000000000000000, + [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, + [LIB9P_VER_9P2000] = 0b00000000000000000000000000000000, #endif /* CONFIG_9P_ENABLE_9P2000 */ - [LIB9P_VER_9P2000_L] = 0b00000000000000001111111111111111, + [LIB9P_VER_9P2000_L] = 0b00000000000000001111111111111111, #if CONFIG_9P_ENABLE_9P2000_e - [LIB9P_VER_9P2000_e] = 0b00000000000000000000000000000000, + [LIB9P_VER_9P2000_e] = 0b00000000000000000000000000000000, #endif /* CONFIG_9P_ENABLE_9P2000_e */ #if CONFIG_9P_ENABLE_9P2000_p9p - [LIB9P_VER_9P2000_p9p] = 0b00000000000000000000000000000000, + [LIB9P_VER_9P2000_p9p] = 0b00000000000000000000000000000000, #endif /* CONFIG_9P_ENABLE_9P2000_p9p */ #if CONFIG_9P_ENABLE_9P2000_u - [LIB9P_VER_9P2000_u] = 0b00000000000000000000000000000000, + [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, + [LIB9P_VER_9P2000] = 0b0000000000000000000000000000000000000000000000000000000000000000, #endif /* CONFIG_9P_ENABLE_9P2000 */ - [LIB9P_VER_9P2000_L] = 0b0000000000000000000000000000000000000000000000000011111111111111, + [LIB9P_VER_9P2000_L] = 0b0000000000000000000000000000000000000000000000000011111111111111, #if CONFIG_9P_ENABLE_9P2000_e - [LIB9P_VER_9P2000_e] = 0b0000000000000000000000000000000000000000000000000000000000000000, + [LIB9P_VER_9P2000_e] = 0b0000000000000000000000000000000000000000000000000000000000000000, #endif /* CONFIG_9P_ENABLE_9P2000_e */ #if CONFIG_9P_ENABLE_9P2000_p9p - [LIB9P_VER_9P2000_p9p] = 0b0000000000000000000000000000000000000000000000000000000000000000, + [LIB9P_VER_9P2000_p9p] = 0b0000000000000000000000000000000000000000000000000000000000000000, #endif /* CONFIG_9P_ENABLE_9P2000_p9p */ #if CONFIG_9P_ENABLE_9P2000_u - [LIB9P_VER_9P2000_u] = 0b0000000000000000000000000000000000000000000000000000000000000000, + [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, + [LIB9P_VER_9P2000] = 0b00000000000000000000000000000000, #endif /* CONFIG_9P_ENABLE_9P2000 */ - [LIB9P_VER_9P2000_L] = 0b00000000000000000000000111111111, + [LIB9P_VER_9P2000_L] = 0b00000000000000000000000111111111, #if CONFIG_9P_ENABLE_9P2000_e - [LIB9P_VER_9P2000_e] = 0b00000000000000000000000000000000, + [LIB9P_VER_9P2000_e] = 0b00000000000000000000000000000000, #endif /* CONFIG_9P_ENABLE_9P2000_e */ #if CONFIG_9P_ENABLE_9P2000_p9p - [LIB9P_VER_9P2000_p9p] = 0b00000000000000000000000000000000, + [LIB9P_VER_9P2000_p9p] = 0b00000000000000000000000000000000, #endif /* CONFIG_9P_ENABLE_9P2000_p9p */ #if CONFIG_9P_ENABLE_9P2000_u - [LIB9P_VER_9P2000_u] = 0b00000000000000000000000000000000, + [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, + [LIB9P_VER_9P2000] = 0b00000000000000000000000000000000, #endif /* CONFIG_9P_ENABLE_9P2000 */ - [LIB9P_VER_9P2000_L] = 0b00000000000000000000000000000011, + [LIB9P_VER_9P2000_L] = 0b00000000000000000000000000000011, #if CONFIG_9P_ENABLE_9P2000_e - [LIB9P_VER_9P2000_e] = 0b00000000000000000000000000000000, + [LIB9P_VER_9P2000_e] = 0b00000000000000000000000000000000, #endif /* CONFIG_9P_ENABLE_9P2000_e */ #if CONFIG_9P_ENABLE_9P2000_p9p - [LIB9P_VER_9P2000_p9p] = 0b00000000000000000000000000000000, + [LIB9P_VER_9P2000_p9p] = 0b00000000000000000000000000000000, #endif /* CONFIG_9P_ENABLE_9P2000_p9p */ #if CONFIG_9P_ENABLE_9P2000_u - [LIB9P_VER_9P2000_u] = 0b00000000000000000000000000000000, + [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 lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "message is too short for content"); \ - if (net_offset > net_size) \ - return lib9p_errorf(ctx, LIB9P_ERRNO_L_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, LIB9P_ERRNO_L_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, LIB9P_ERRNO_L_EBADMSG, "message is too short for content"); +#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]) @@ -349,17 +252,17 @@ static const lib9p_lock_flags_t lock_flags_masks[LIB9P_VER_NUM] = { #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) { +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; - 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; + 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_file_qid_type) & ~qt_masks[ctx->version]) - return lib9p_errorf(ctx, LIB9P_ERRNO_L_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; + 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); @@ -376,56 +279,53 @@ static ssize_t validate_stat(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t * } #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, LIB9P_ERRNO_L_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, LIB9P_ERRNO_L_EBADMSG, "unknown bits in dm bitfield: %#08"PRIx32, - GET_U32LE(offsetof_file_mode) & ~dm_masks[ctx->version]); + 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 (ssize_t)host_size; + 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 ssize_t validate_Tversion(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { +#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; - ssize_t host_size = sizeof(struct lib9p_msg_Tversion); + 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 ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) - return lib9p_errorf(ctx, LIB9P_ERRNO_L_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, LIB9P_ERRNO_L_EBADMSG, "Tversion->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, - (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(100)); - return (ssize_t)host_size; + 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 ssize_t validate_Rversion(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { +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; - ssize_t host_size = sizeof(struct lib9p_msg_Rversion); + 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 ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) - return lib9p_errorf(ctx, LIB9P_ERRNO_L_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, LIB9P_ERRNO_L_EBADMSG, "Rversion->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, - (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(101)); - return (ssize_t)host_size; + 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); } -static ssize_t validate_Tauth(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { +#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; - ssize_t host_size = sizeof(struct lib9p_msg_Tauth); + 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); @@ -438,38 +338,34 @@ static ssize_t validate_Tauth(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t } #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, LIB9P_ERRNO_L_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, LIB9P_ERRNO_L_EBADMSG, "Tauth->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, - (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(102)); - return (ssize_t)host_size; + 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 ssize_t validate_Rauth(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { +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; - ssize_t host_size = sizeof(struct lib9p_msg_Rauth); + 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 lib9p_errorf(ctx, LIB9P_ERRNO_L_EBADMSG, "unknown bits in qt bitfield: %#02"PRIx8, - 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 ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) - return lib9p_errorf(ctx, LIB9P_ERRNO_L_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, LIB9P_ERRNO_L_EBADMSG, "Rauth->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, - (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(103)); - return (ssize_t)host_size; + 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 ssize_t validate_Tattach(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { +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; - ssize_t host_size = sizeof(struct lib9p_msg_Tattach); + 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); @@ -482,40 +378,36 @@ static ssize_t validate_Tattach(struct lib9p_ctx *ctx, uint32_t net_size, uint8_ } #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, LIB9P_ERRNO_L_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, LIB9P_ERRNO_L_EBADMSG, "Tattach->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, - (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(104)); - return (ssize_t)host_size; + 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 ssize_t validate_Rattach(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { +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; - ssize_t host_size = sizeof(struct lib9p_msg_Rattach); + 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 lib9p_errorf(ctx, LIB9P_ERRNO_L_EBADMSG, "unknown bits in qt bitfield: %#02"PRIx8, - 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 ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) - return lib9p_errorf(ctx, LIB9P_ERRNO_L_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, LIB9P_ERRNO_L_EBADMSG, "Rattach->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, - (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(105)); - return (ssize_t)host_size; + 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 -static ssize_t validate_Rerror(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { +#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; - ssize_t host_size = sizeof(struct lib9p_msg_Rerror); + 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); @@ -526,52 +418,46 @@ static ssize_t validate_Rerror(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t } #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, LIB9P_ERRNO_L_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, LIB9P_ERRNO_L_EBADMSG, "Rerror->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, - (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(107)); - return (ssize_t)host_size; + 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 */ +#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 ssize_t validate_Tflush(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { +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; - ssize_t host_size = sizeof(struct lib9p_msg_Tflush); + 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 ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) - return lib9p_errorf(ctx, LIB9P_ERRNO_L_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, LIB9P_ERRNO_L_EBADMSG, "Tflush->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, - (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(108)); - return (ssize_t)host_size; + 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 ssize_t validate_Rflush(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { +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; - ssize_t host_size = sizeof(struct lib9p_msg_Rflush); + 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 ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) - return lib9p_errorf(ctx, LIB9P_ERRNO_L_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, LIB9P_ERRNO_L_EBADMSG, "Rflush->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, - (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(109)); - return (ssize_t)host_size; + 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 ssize_t validate_Twalk(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { +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; - ssize_t host_size = sizeof(struct lib9p_msg_Twalk); + 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; @@ -582,21 +468,18 @@ static ssize_t validate_Twalk(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t 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, LIB9P_ERRNO_L_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, LIB9P_ERRNO_L_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, LIB9P_ERRNO_L_EBADMSG, "Twalk->nwname value is too large: %"PRIu16" > %"PRIu16, - (uint16_t)GET_U16LE(offsetof_nwname), (uint16_t)(16)); - return (ssize_t)host_size; + 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 ssize_t validate_Rwalk(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { +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; - ssize_t host_size = sizeof(struct lib9p_msg_Rwalk); + 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; @@ -606,68 +489,61 @@ static ssize_t validate_Rwalk(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t 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, LIB9P_ERRNO_L_EBADMSG, "unknown bits in qt bitfield: %#02"PRIx8, - 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 ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) - return lib9p_errorf(ctx, LIB9P_ERRNO_L_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, LIB9P_ERRNO_L_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, LIB9P_ERRNO_L_EBADMSG, "Rwalk->nwqid value is too large: %"PRIu16" > %"PRIu16, - (uint16_t)GET_U16LE(offsetof_nwqid), (uint16_t)(16)); - return (ssize_t)host_size; + 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 ssize_t validate_Topen(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { +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; - ssize_t host_size = sizeof(struct lib9p_msg_Topen); + 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 ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) - return lib9p_errorf(ctx, LIB9P_ERRNO_L_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, LIB9P_ERRNO_L_EBADMSG, "Topen->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, - (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(112)); + 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 lib9p_errorf(ctx, LIB9P_ERRNO_L_EBADMSG, "unknown bits in o bitfield: %#02"PRIx8, - GET_U8LE(offsetof_mode) & ~o_masks[ctx->version]); - return (ssize_t)host_size; + 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 ssize_t validate_Ropen(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { +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; - ssize_t host_size = sizeof(struct lib9p_msg_Ropen); + 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 lib9p_errorf(ctx, LIB9P_ERRNO_L_EBADMSG, "unknown bits in qt bitfield: %#02"PRIx8, - 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 ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) - return lib9p_errorf(ctx, LIB9P_ERRNO_L_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, LIB9P_ERRNO_L_EBADMSG, "Ropen->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, - (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(113)); - return (ssize_t)host_size; + 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 ssize_t validate_Tcreate(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { +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; - ssize_t host_size = sizeof(struct lib9p_msg_Tcreate); + 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); @@ -676,92 +552,81 @@ static ssize_t validate_Tcreate(struct lib9p_ctx *ctx, uint32_t net_size, uint8_ 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, LIB9P_ERRNO_L_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, LIB9P_ERRNO_L_EBADMSG, "Tcreate->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, - (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(114)); + 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 lib9p_errorf(ctx, LIB9P_ERRNO_L_EBADMSG, "unknown bits in dm bitfield: %#08"PRIx32, - 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 lib9p_errorf(ctx, LIB9P_ERRNO_L_EBADMSG, "unknown bits in o bitfield: %#02"PRIx8, - GET_U8LE(offsetof_mode) & ~o_masks[ctx->version]); - return (ssize_t)host_size; + 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 ssize_t validate_Rcreate(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { +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; - ssize_t host_size = sizeof(struct lib9p_msg_Rcreate); + 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 lib9p_errorf(ctx, LIB9P_ERRNO_L_EBADMSG, "unknown bits in qt bitfield: %#02"PRIx8, - 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 ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) - return lib9p_errorf(ctx, LIB9P_ERRNO_L_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, LIB9P_ERRNO_L_EBADMSG, "Rcreate->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, - (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(115)); - return (ssize_t)host_size; + 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 ssize_t validate_Tread(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { +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; - ssize_t host_size = sizeof(struct lib9p_msg_Tread); + 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 ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) - return lib9p_errorf(ctx, LIB9P_ERRNO_L_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, LIB9P_ERRNO_L_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, LIB9P_ERRNO_L_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, LIB9P_ERRNO_L_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); + 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 ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) - return lib9p_errorf(ctx, LIB9P_ERRNO_L_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, LIB9P_ERRNO_L_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, LIB9P_ERRNO_L_EBADMSG, "Rread->count value is too large: %"PRIu32" > %"PRIu32, - (uint32_t)GET_U32LE(offsetof_count), (uint32_t)(INT32_MAX)); - return (ssize_t)host_size; + 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 ssize_t validate_Twrite(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { +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; - ssize_t host_size = sizeof(struct lib9p_msg_Twrite); + 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; @@ -769,138 +634,121 @@ static ssize_t validate_Twrite(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t 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, LIB9P_ERRNO_L_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, LIB9P_ERRNO_L_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, LIB9P_ERRNO_L_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, LIB9P_ERRNO_L_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); + 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 ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) - return lib9p_errorf(ctx, LIB9P_ERRNO_L_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, LIB9P_ERRNO_L_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, LIB9P_ERRNO_L_EBADMSG, "Rwrite->count value is too large: %"PRIu32" > %"PRIu32, - (uint32_t)GET_U32LE(offsetof_count), (uint32_t)(INT32_MAX)); - return (ssize_t)host_size; + 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 ssize_t validate_Tclunk(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { +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; - ssize_t host_size = sizeof(struct lib9p_msg_Tclunk); + 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 ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) - return lib9p_errorf(ctx, LIB9P_ERRNO_L_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, LIB9P_ERRNO_L_EBADMSG, "Tclunk->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, - (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(120)); - return (ssize_t)host_size; + 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 ssize_t validate_Rclunk(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { +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; - ssize_t host_size = sizeof(struct lib9p_msg_Rclunk); + 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 ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) - return lib9p_errorf(ctx, LIB9P_ERRNO_L_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, LIB9P_ERRNO_L_EBADMSG, "Rclunk->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, - (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(121)); - return (ssize_t)host_size; + 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 ssize_t validate_Tremove(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { +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; - ssize_t host_size = sizeof(struct lib9p_msg_Tremove); + 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 ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) - return lib9p_errorf(ctx, LIB9P_ERRNO_L_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, LIB9P_ERRNO_L_EBADMSG, "Tremove->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, - (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(122)); - return (ssize_t)host_size; + 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 ssize_t validate_Rremove(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { +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; - ssize_t host_size = sizeof(struct lib9p_msg_Rremove); + 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 ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) - return lib9p_errorf(ctx, LIB9P_ERRNO_L_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, LIB9P_ERRNO_L_EBADMSG, "Rremove->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, - (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(123)); - return (ssize_t)host_size; + 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 ssize_t validate_Tstat(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { +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; - ssize_t host_size = sizeof(struct lib9p_msg_Tstat); + 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 ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) - return lib9p_errorf(ctx, LIB9P_ERRNO_L_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, LIB9P_ERRNO_L_EBADMSG, "Tstat->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, - (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(124)); - return (ssize_t)host_size; + 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 ssize_t validate_Rstat(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { +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; - ssize_t host_size = sizeof(struct lib9p_msg_Rstat); + 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_kern_type = net_offset + 11; - uint32_t offsetof_stat_file_qid_type = net_offset + 17; + 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_file_qid_type) & ~qt_masks[ctx->version]) - return lib9p_errorf(ctx, LIB9P_ERRNO_L_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; + 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); @@ -917,40 +765,36 @@ static ssize_t validate_Rstat(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t } #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, LIB9P_ERRNO_L_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, LIB9P_ERRNO_L_EBADMSG, "unknown bits in dm bitfield: %#08"PRIx32, - GET_U32LE(offsetof_stat_file_mode) & ~dm_masks[ctx->version]); + 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 ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) - return lib9p_errorf(ctx, LIB9P_ERRNO_L_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, LIB9P_ERRNO_L_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, LIB9P_ERRNO_L_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; + 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 ssize_t validate_Twstat(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { +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; - ssize_t host_size = sizeof(struct lib9p_msg_Twstat); + 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_kern_type = net_offset + 15; - uint32_t offsetof_stat_file_qid_type = net_offset + 21; + 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_file_qid_type) & ~qt_masks[ctx->version]) - return lib9p_errorf(ctx, LIB9P_ERRNO_L_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; + 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); @@ -967,178 +811,158 @@ static ssize_t validate_Twstat(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t } #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, LIB9P_ERRNO_L_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, LIB9P_ERRNO_L_EBADMSG, "unknown bits in dm bitfield: %#08"PRIx32, - GET_U32LE(offsetof_stat_file_mode) & ~dm_masks[ctx->version]); + 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 ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) - return lib9p_errorf(ctx, LIB9P_ERRNO_L_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, LIB9P_ERRNO_L_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, LIB9P_ERRNO_L_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; + 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 ssize_t validate_Rwstat(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { +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; - ssize_t host_size = sizeof(struct lib9p_msg_Rwstat); + 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 ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) - return lib9p_errorf(ctx, LIB9P_ERRNO_L_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, LIB9P_ERRNO_L_EBADMSG, "Rwstat->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, - (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(127)); - return (ssize_t)host_size; + 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 ssize_t validate_Topenfd(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { +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; - ssize_t host_size = sizeof(struct lib9p_msg_Topenfd); + 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 ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) - return lib9p_errorf(ctx, LIB9P_ERRNO_L_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, LIB9P_ERRNO_L_EBADMSG, "Topenfd->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, - (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(98)); + 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 lib9p_errorf(ctx, LIB9P_ERRNO_L_EBADMSG, "unknown bits in o bitfield: %#02"PRIx8, - GET_U8LE(offsetof_mode) & ~o_masks[ctx->version]); - return (ssize_t)host_size; + 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 ssize_t validate_Ropenfd(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { +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; - ssize_t host_size = sizeof(struct lib9p_msg_Ropenfd); + 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 lib9p_errorf(ctx, LIB9P_ERRNO_L_EBADMSG, "unknown bits in qt bitfield: %#02"PRIx8, - 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 ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) - return lib9p_errorf(ctx, LIB9P_ERRNO_L_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, LIB9P_ERRNO_L_EBADMSG, "Ropenfd->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, - (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(99)); - return (ssize_t)host_size; + 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 ssize_t validate_Rlerror(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { +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; - ssize_t host_size = sizeof(struct lib9p_msg_Rlerror); + 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 ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) - return lib9p_errorf(ctx, LIB9P_ERRNO_L_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, LIB9P_ERRNO_L_EBADMSG, "Rlerror->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, - (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(7)); - return (ssize_t)host_size; + 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 ssize_t validate_Tstatfs(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { +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; - ssize_t host_size = sizeof(struct lib9p_msg_Tstatfs); + 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 ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) - return lib9p_errorf(ctx, LIB9P_ERRNO_L_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, LIB9P_ERRNO_L_EBADMSG, "Tstatfs->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, - (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(8)); - return (ssize_t)host_size; + 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 ssize_t validate_Rstatfs(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { +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; - ssize_t host_size = sizeof(struct lib9p_msg_Rstatfs); + 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 ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) - return lib9p_errorf(ctx, LIB9P_ERRNO_L_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, LIB9P_ERRNO_L_EBADMSG, "Rstatfs->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, - (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(9)); - return (ssize_t)host_size; + 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 ssize_t validate_Tlopen(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { +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; - ssize_t host_size = sizeof(struct lib9p_msg_Tlopen); + 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 ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) - return lib9p_errorf(ctx, LIB9P_ERRNO_L_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, LIB9P_ERRNO_L_EBADMSG, "Tlopen->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, - (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(12)); + 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 lib9p_errorf(ctx, LIB9P_ERRNO_L_EBADMSG, "unknown bits in lo bitfield: %#08"PRIx32, - GET_U32LE(offsetof_flags) & ~lo_masks[ctx->version]); - return (ssize_t)host_size; + 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 ssize_t validate_Rlopen(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { +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; - ssize_t host_size = sizeof(struct lib9p_msg_Rlopen); + 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 lib9p_errorf(ctx, LIB9P_ERRNO_L_EBADMSG, "unknown bits in qt bitfield: %#02"PRIx8, - 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 ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) - return lib9p_errorf(ctx, LIB9P_ERRNO_L_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, LIB9P_ERRNO_L_EBADMSG, "Rlopen->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, - (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(13)); - return (ssize_t)host_size; + 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 ssize_t validate_Tlcreate(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { +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; - ssize_t host_size = sizeof(struct lib9p_msg_Tlcreate); + 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); @@ -1147,45 +971,41 @@ static ssize_t validate_Tlcreate(struct lib9p_ctx *ctx, uint32_t net_size, uint8 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, LIB9P_ERRNO_L_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, LIB9P_ERRNO_L_EBADMSG, "Tlcreate->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, - (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(14)); + 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 lib9p_errorf(ctx, LIB9P_ERRNO_L_EBADMSG, "unknown bits in lo bitfield: %#08"PRIx32, - 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 lib9p_errorf(ctx, LIB9P_ERRNO_L_EBADMSG, "unknown bits in mode bitfield: %#08"PRIx32, - GET_U32LE(offsetof_mode) & ~mode_masks[ctx->version]); - return (ssize_t)host_size; + 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 ssize_t validate_Rlcreate(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { +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; - ssize_t host_size = sizeof(struct lib9p_msg_Rlcreate); + 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 lib9p_errorf(ctx, LIB9P_ERRNO_L_EBADMSG, "unknown bits in qt bitfield: %#02"PRIx8, - 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 ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) - return lib9p_errorf(ctx, LIB9P_ERRNO_L_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, LIB9P_ERRNO_L_EBADMSG, "Rlcreate->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, - (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(15)); - return (ssize_t)host_size; + 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 ssize_t validate_Tsymlink(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { +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; - ssize_t host_size = sizeof(struct lib9p_msg_Tsymlink); + 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); @@ -1194,38 +1014,34 @@ static ssize_t validate_Tsymlink(struct lib9p_ctx *ctx, uint32_t net_size, uint8 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, LIB9P_ERRNO_L_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, LIB9P_ERRNO_L_EBADMSG, "Tsymlink->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, - (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(16)); - return (ssize_t)host_size; + 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 ssize_t validate_Rsymlink(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { +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; - ssize_t host_size = sizeof(struct lib9p_msg_Rsymlink); + 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 lib9p_errorf(ctx, LIB9P_ERRNO_L_EBADMSG, "unknown bits in qt bitfield: %#02"PRIx8, - 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 ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) - return lib9p_errorf(ctx, LIB9P_ERRNO_L_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, LIB9P_ERRNO_L_EBADMSG, "Rsymlink->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, - (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(17)); - return (ssize_t)host_size; + 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 ssize_t validate_Tmknod(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { +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; - ssize_t host_size = sizeof(struct lib9p_msg_Tmknod); + 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); @@ -1233,432 +1049,384 @@ static ssize_t validate_Tmknod(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t 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, LIB9P_ERRNO_L_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, LIB9P_ERRNO_L_EBADMSG, "Tmknod->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, - (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(18)); + 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 lib9p_errorf(ctx, LIB9P_ERRNO_L_EBADMSG, "unknown bits in mode bitfield: %#08"PRIx32, - GET_U32LE(offsetof_mode) & ~mode_masks[ctx->version]); - return (ssize_t)host_size; + 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 ssize_t validate_Rmknod(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { +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; - ssize_t host_size = sizeof(struct lib9p_msg_Rmknod); + 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 lib9p_errorf(ctx, LIB9P_ERRNO_L_EBADMSG, "unknown bits in qt bitfield: %#02"PRIx8, - 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 ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) - return lib9p_errorf(ctx, LIB9P_ERRNO_L_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, LIB9P_ERRNO_L_EBADMSG, "Rmknod->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, - (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(19)); - return (ssize_t)host_size; + 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 ssize_t validate_Trename(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { +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; - ssize_t host_size = sizeof(struct lib9p_msg_Trename); + 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 ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) - return lib9p_errorf(ctx, LIB9P_ERRNO_L_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, LIB9P_ERRNO_L_EBADMSG, "Trename->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, - (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(20)); - return (ssize_t)host_size; + 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 ssize_t validate_Rrename(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { +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; - ssize_t host_size = sizeof(struct lib9p_msg_Rrename); + 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 ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) - return lib9p_errorf(ctx, LIB9P_ERRNO_L_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, LIB9P_ERRNO_L_EBADMSG, "Rrename->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, - (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(21)); - return (ssize_t)host_size; + 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 ssize_t validate_Treadlink(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { +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; - ssize_t host_size = sizeof(struct lib9p_msg_Treadlink); + 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 ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) - return lib9p_errorf(ctx, LIB9P_ERRNO_L_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, LIB9P_ERRNO_L_EBADMSG, "Treadlink->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, - (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(22)); - return (ssize_t)host_size; + 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 ssize_t validate_Rreadlink(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { +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; - ssize_t host_size = sizeof(struct lib9p_msg_Rreadlink); + 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 ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) - return lib9p_errorf(ctx, LIB9P_ERRNO_L_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, LIB9P_ERRNO_L_EBADMSG, "Rreadlink->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, - (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(23)); - return (ssize_t)host_size; + 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 ssize_t validate_Tgetattr(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { +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; - ssize_t host_size = sizeof(struct lib9p_msg_Tgetattr); + 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 ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) - return lib9p_errorf(ctx, LIB9P_ERRNO_L_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, LIB9P_ERRNO_L_EBADMSG, "Tgetattr->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, - (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(24)); + 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 lib9p_errorf(ctx, LIB9P_ERRNO_L_EBADMSG, "unknown bits in getattr bitfield: %#016"PRIx64, - GET_U64LE(offsetof_request_mask) & ~getattr_masks[ctx->version]); - return (ssize_t)host_size; + 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 ssize_t validate_Rgetattr(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { +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; - ssize_t host_size = sizeof(struct lib9p_msg_Rgetattr); + 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 lib9p_errorf(ctx, LIB9P_ERRNO_L_EBADMSG, "unknown bits in qt bitfield: %#02"PRIx8, - 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 ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) - return lib9p_errorf(ctx, LIB9P_ERRNO_L_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, LIB9P_ERRNO_L_EBADMSG, "Rgetattr->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, - (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(25)); + 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 lib9p_errorf(ctx, LIB9P_ERRNO_L_EBADMSG, "unknown bits in getattr bitfield: %#016"PRIx64, - 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 lib9p_errorf(ctx, LIB9P_ERRNO_L_EBADMSG, "unknown bits in mode bitfield: %#08"PRIx32, - GET_U32LE(offsetof_mode) & ~mode_masks[ctx->version]); - return (ssize_t)host_size; + 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 ssize_t validate_Tsetattr(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { +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; - ssize_t host_size = sizeof(struct lib9p_msg_Tsetattr); + 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 ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) - return lib9p_errorf(ctx, LIB9P_ERRNO_L_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, LIB9P_ERRNO_L_EBADMSG, "Tsetattr->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, - (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(26)); + 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 lib9p_errorf(ctx, LIB9P_ERRNO_L_EBADMSG, "unknown bits in setattr bitfield: %#08"PRIx32, - 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 lib9p_errorf(ctx, LIB9P_ERRNO_L_EBADMSG, "unknown bits in mode bitfield: %#08"PRIx32, - GET_U32LE(offsetof_mode) & ~mode_masks[ctx->version]); - return (ssize_t)host_size; + 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 ssize_t validate_Rsetattr(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { +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; - ssize_t host_size = sizeof(struct lib9p_msg_Rsetattr); + 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 ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) - return lib9p_errorf(ctx, LIB9P_ERRNO_L_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, LIB9P_ERRNO_L_EBADMSG, "Rsetattr->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, - (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(27)); - return (ssize_t)host_size; + 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 ssize_t validate_Txattrwalk(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { +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; - ssize_t host_size = sizeof(struct lib9p_msg_Txattrwalk); + 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 ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) - return lib9p_errorf(ctx, LIB9P_ERRNO_L_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, LIB9P_ERRNO_L_EBADMSG, "Txattrwalk->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, - (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(30)); - return (ssize_t)host_size; + 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 ssize_t validate_Rxattrwalk(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { +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; - ssize_t host_size = sizeof(struct lib9p_msg_Rxattrwalk); + 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 ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) - return lib9p_errorf(ctx, LIB9P_ERRNO_L_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, LIB9P_ERRNO_L_EBADMSG, "Rxattrwalk->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, - (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(31)); - return (ssize_t)host_size; + 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 ssize_t validate_Txattrcreate(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { +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; - ssize_t host_size = sizeof(struct lib9p_msg_Txattrcreate); + 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 ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) - return lib9p_errorf(ctx, LIB9P_ERRNO_L_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, LIB9P_ERRNO_L_EBADMSG, "Txattrcreate->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, - (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(32)); - return (ssize_t)host_size; + 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 ssize_t validate_Rxattrcreate(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { +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; - ssize_t host_size = sizeof(struct lib9p_msg_Rxattrcreate); + 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 ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) - return lib9p_errorf(ctx, LIB9P_ERRNO_L_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, LIB9P_ERRNO_L_EBADMSG, "Rxattrcreate->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, - (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(33)); - return (ssize_t)host_size; + 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 ssize_t validate_Treaddir(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { +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; - ssize_t host_size = sizeof(struct lib9p_msg_Treaddir); + 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 ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) - return lib9p_errorf(ctx, LIB9P_ERRNO_L_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, LIB9P_ERRNO_L_EBADMSG, "Treaddir->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, - (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(40)); - return (ssize_t)host_size; + 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 ssize_t validate_Rreaddir(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { +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; - ssize_t host_size = sizeof(struct lib9p_msg_Rreaddir); + 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 ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) - return lib9p_errorf(ctx, LIB9P_ERRNO_L_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, LIB9P_ERRNO_L_EBADMSG, "Rreaddir->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, - (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(41)); - return (ssize_t)host_size; + 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 ssize_t validate_Tfsync(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { +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; - ssize_t host_size = sizeof(struct lib9p_msg_Tfsync); + 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 ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) - return lib9p_errorf(ctx, LIB9P_ERRNO_L_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, LIB9P_ERRNO_L_EBADMSG, "Tfsync->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, - (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(50)); - return (ssize_t)host_size; + 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 ssize_t validate_Rfsync(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { +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; - ssize_t host_size = sizeof(struct lib9p_msg_Rfsync); + 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 ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) - return lib9p_errorf(ctx, LIB9P_ERRNO_L_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, LIB9P_ERRNO_L_EBADMSG, "Rfsync->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, - (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(51)); - return (ssize_t)host_size; + 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 ssize_t validate_Tlock(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { +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; - ssize_t host_size = sizeof(struct lib9p_msg_Tlock); + 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 ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) - return lib9p_errorf(ctx, LIB9P_ERRNO_L_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, LIB9P_ERRNO_L_EBADMSG, "Tlock->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, - (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(52)); + 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 lib9p_errorf(ctx, LIB9P_ERRNO_L_EBADMSG, "unknown bits in lock_flags bitfield: %#08"PRIx32, - GET_U32LE(offsetof_flags) & ~lock_flags_masks[ctx->version]); - return (ssize_t)host_size; + 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 ssize_t validate_Rlock(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { +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; - ssize_t host_size = sizeof(struct lib9p_msg_Rlock); + 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 ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) - return lib9p_errorf(ctx, LIB9P_ERRNO_L_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, LIB9P_ERRNO_L_EBADMSG, "Rlock->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, - (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(53)); - return (ssize_t)host_size; + 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 ssize_t validate_Tgetlock(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { +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; - ssize_t host_size = sizeof(struct lib9p_msg_Tgetlock); + 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 ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) - return lib9p_errorf(ctx, LIB9P_ERRNO_L_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, LIB9P_ERRNO_L_EBADMSG, "Tgetlock->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, - (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(54)); - return (ssize_t)host_size; + 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 ssize_t validate_Rgetlock(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { +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; - ssize_t host_size = sizeof(struct lib9p_msg_Rgetlock); + 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 ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) - return lib9p_errorf(ctx, LIB9P_ERRNO_L_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, LIB9P_ERRNO_L_EBADMSG, "Rgetlock->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, - (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(55)); - return (ssize_t)host_size; + 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 ssize_t validate_Tlink(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { +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; - ssize_t host_size = sizeof(struct lib9p_msg_Tlink); + 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 ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) - return lib9p_errorf(ctx, LIB9P_ERRNO_L_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, LIB9P_ERRNO_L_EBADMSG, "Tlink->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, - (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(70)); - return (ssize_t)host_size; + 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 ssize_t validate_Rlink(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { +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; - ssize_t host_size = sizeof(struct lib9p_msg_Rlink); + 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 ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) - return lib9p_errorf(ctx, LIB9P_ERRNO_L_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, LIB9P_ERRNO_L_EBADMSG, "Rlink->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, - (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(71)); - return (ssize_t)host_size; + 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 ssize_t validate_Tmkdir(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { +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; - ssize_t host_size = sizeof(struct lib9p_msg_Tmkdir); + 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); @@ -1666,41 +1434,37 @@ static ssize_t validate_Tmkdir(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t 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, LIB9P_ERRNO_L_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, LIB9P_ERRNO_L_EBADMSG, "Tmkdir->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, - (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(72)); + 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 lib9p_errorf(ctx, LIB9P_ERRNO_L_EBADMSG, "unknown bits in mode bitfield: %#08"PRIx32, - GET_U32LE(offsetof_mode) & ~mode_masks[ctx->version]); - return (ssize_t)host_size; + 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 ssize_t validate_Rmkdir(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { +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; - ssize_t host_size = sizeof(struct lib9p_msg_Rmkdir); + 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 lib9p_errorf(ctx, LIB9P_ERRNO_L_EBADMSG, "unknown bits in qt bitfield: %#02"PRIx8, - 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 ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) - return lib9p_errorf(ctx, LIB9P_ERRNO_L_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, LIB9P_ERRNO_L_EBADMSG, "Rmkdir->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, - (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(73)); - return (ssize_t)host_size; + 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 ssize_t validate_Trenameat(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { +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; - ssize_t host_size = sizeof(struct lib9p_msg_Trenameat); + 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); @@ -1708,102 +1472,90 @@ static ssize_t validate_Trenameat(struct lib9p_ctx *ctx, uint32_t net_size, uint 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, LIB9P_ERRNO_L_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, LIB9P_ERRNO_L_EBADMSG, "Trenameat->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, - (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(74)); - return (ssize_t)host_size; + 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 ssize_t validate_Rrenameat(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { +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; - ssize_t host_size = sizeof(struct lib9p_msg_Rrenameat); + 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 ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) - return lib9p_errorf(ctx, LIB9P_ERRNO_L_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, LIB9P_ERRNO_L_EBADMSG, "Rrenameat->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, - (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(75)); - return (ssize_t)host_size; + 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 ssize_t validate_Tunlinkat(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { +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; - ssize_t host_size = sizeof(struct lib9p_msg_Tunlinkat); + 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 ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) - return lib9p_errorf(ctx, LIB9P_ERRNO_L_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, LIB9P_ERRNO_L_EBADMSG, "Tunlinkat->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, - (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(76)); - return (ssize_t)host_size; + 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 ssize_t validate_Runlinkat(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { +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; - ssize_t host_size = sizeof(struct lib9p_msg_Runlinkat); + 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 ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) - return lib9p_errorf(ctx, LIB9P_ERRNO_L_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, LIB9P_ERRNO_L_EBADMSG, "Runlinkat->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, - (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(77)); - return (ssize_t)host_size; + 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 ssize_t validate_Tsession(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { +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; - ssize_t host_size = sizeof(struct lib9p_msg_Tsession); + 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 ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) - return lib9p_errorf(ctx, LIB9P_ERRNO_L_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, LIB9P_ERRNO_L_EBADMSG, "Tsession->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, - (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(150)); - return (ssize_t)host_size; + 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 ssize_t validate_Rsession(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { +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; - ssize_t host_size = sizeof(struct lib9p_msg_Rsession); + 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 ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) - return lib9p_errorf(ctx, LIB9P_ERRNO_L_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, LIB9P_ERRNO_L_EBADMSG, "Rsession->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, - (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(151)); - return (ssize_t)host_size; + 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 ssize_t validate_Tsread(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { +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; - ssize_t host_size = sizeof(struct lib9p_msg_Tsread); + 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); @@ -1813,35 +1565,31 @@ static ssize_t validate_Tsread(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t 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, LIB9P_ERRNO_L_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, LIB9P_ERRNO_L_EBADMSG, "Tsread->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, - (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(152)); - return (ssize_t)host_size; + 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 ssize_t validate_Rsread(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { +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; - ssize_t host_size = sizeof(struct lib9p_msg_Rsread); + 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 ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) - return lib9p_errorf(ctx, LIB9P_ERRNO_L_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, LIB9P_ERRNO_L_EBADMSG, "Rsread->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, - (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(153)); - return (ssize_t)host_size; + 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 ssize_t validate_Tswrite(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { +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; - ssize_t host_size = sizeof(struct lib9p_msg_Tswrite); + 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); @@ -1853,29 +1601,25 @@ static ssize_t validate_Tswrite(struct lib9p_ctx *ctx, uint32_t net_size, uint8_ 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, LIB9P_ERRNO_L_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, LIB9P_ERRNO_L_EBADMSG, "Tswrite->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, - (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(154)); - return (ssize_t)host_size; + 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 ssize_t validate_Rswrite(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { +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; - ssize_t host_size = sizeof(struct lib9p_msg_Rswrite); + 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 ((uint32_t)GET_U32LE(offsetof_size) != (uint32_t)(offsetof_end - offsetof_size)) - return lib9p_errorf(ctx, LIB9P_ERRNO_L_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, LIB9P_ERRNO_L_EBADMSG, "Rswrite->typ value is wrong: actual: %"PRIu8" != correct:%"PRIu8, - (uint8_t)GET_U8LE(offsetof_typ), (uint8_t)(155)); - return (ssize_t)host_size; + 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 */ @@ -1898,44 +1642,44 @@ static ssize_t validate_Rswrite(struct lib9p_ctx *ctx, uint32_t net_size, uint8_ 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) { +static void unmarshal_stat([[maybe_unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { struct lib9p_stat *out = out_buf; - [[gnu::unused]] void *extra = &out[1]; + [[maybe_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); + 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->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); + 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 -static void unmarshal_Tversion([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { +#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; - [[gnu::unused]] void *extra = &out[1]; + [[maybe_unused]] void *extra = &out[1]; uint32_t net_offset = 0; net_offset += 4; net_offset += 1; @@ -1945,9 +1689,9 @@ static void unmarshal_Tversion([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *n 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) { +static void unmarshal_Rversion([[maybe_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]; + [[maybe_unused]] void *extra = &out[1]; uint32_t net_offset = 0; net_offset += 4; net_offset += 1; @@ -1957,9 +1701,11 @@ static void unmarshal_Rversion([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *n 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) { +#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; - [[gnu::unused]] void *extra = &out[1]; + [[maybe_unused]] void *extra = &out[1]; uint32_t net_offset = 0; net_offset += 4; net_offset += 1; @@ -1971,14 +1717,14 @@ static void unmarshal_Tauth([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_ 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); + UNMARSHAL_U32LE(ctx, out->unum); } #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) { +static void unmarshal_Rauth([[maybe_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]; + [[maybe_unused]] void *extra = &out[1]; uint32_t net_offset = 0; net_offset += 4; net_offset += 1; @@ -1988,9 +1734,9 @@ static void unmarshal_Rauth([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_ UNMARSHAL_U64LE(ctx, out->aqid.path); } -static void unmarshal_Tattach([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { +static void unmarshal_Tattach([[maybe_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]; + [[maybe_unused]] void *extra = &out[1]; uint32_t net_offset = 0; net_offset += 4; net_offset += 1; @@ -2003,14 +1749,14 @@ static void unmarshal_Tattach([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *ne 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); + UNMARSHAL_U32LE(ctx, out->unum); } #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) { +static void unmarshal_Rattach([[maybe_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]; + [[maybe_unused]] void *extra = &out[1]; uint32_t net_offset = 0; net_offset += 4; net_offset += 1; @@ -2021,10 +1767,10 @@ static void unmarshal_Rattach([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *ne } #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_Rerror([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { +#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; - [[gnu::unused]] void *extra = &out[1]; + [[maybe_unused]] void *extra = &out[1]; uint32_t net_offset = 0; net_offset += 4; net_offset += 1; @@ -2038,11 +1784,11 @@ static void unmarshal_Rerror([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net #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 */ +#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([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { +static void unmarshal_Tflush([[maybe_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]; + [[maybe_unused]] void *extra = &out[1]; uint32_t net_offset = 0; net_offset += 4; net_offset += 1; @@ -2050,18 +1796,18 @@ static void unmarshal_Tflush([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net UNMARSHAL_U16LE(ctx, out->oldtag); } -static void unmarshal_Rflush([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { +static void unmarshal_Rflush([[maybe_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]; + [[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([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { +static void unmarshal_Twalk([[maybe_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]; + [[maybe_unused]] void *extra = &out[1]; uint32_t net_offset = 0; net_offset += 4; net_offset += 1; @@ -2077,9 +1823,9 @@ static void unmarshal_Twalk([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_ } } -static void unmarshal_Rwalk([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { +static void unmarshal_Rwalk([[maybe_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]; + [[maybe_unused]] void *extra = &out[1]; uint32_t net_offset = 0; net_offset += 4; net_offset += 1; @@ -2096,9 +1842,9 @@ static void unmarshal_Rwalk([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_ #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) { +static void unmarshal_Topen([[maybe_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]; + [[maybe_unused]] void *extra = &out[1]; uint32_t net_offset = 0; net_offset += 4; net_offset += 1; @@ -2107,9 +1853,9 @@ static void unmarshal_Topen([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_ UNMARSHAL_U8LE(ctx, out->mode); } -static void unmarshal_Ropen([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { +static void unmarshal_Ropen([[maybe_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]; + [[maybe_unused]] void *extra = &out[1]; uint32_t net_offset = 0; net_offset += 4; net_offset += 1; @@ -2120,9 +1866,9 @@ static void unmarshal_Ropen([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_ UNMARSHAL_U32LE(ctx, out->iounit); } -static void unmarshal_Tcreate([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { +static void unmarshal_Tcreate([[maybe_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]; + [[maybe_unused]] void *extra = &out[1]; uint32_t net_offset = 0; net_offset += 4; net_offset += 1; @@ -2134,9 +1880,9 @@ static void unmarshal_Tcreate([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *ne UNMARSHAL_U8LE(ctx, out->mode); } -static void unmarshal_Rcreate([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { +static void unmarshal_Rcreate([[maybe_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]; + [[maybe_unused]] void *extra = &out[1]; uint32_t net_offset = 0; net_offset += 4; net_offset += 1; @@ -2149,9 +1895,9 @@ static void unmarshal_Rcreate([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *ne #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) { +static void unmarshal_Tread([[maybe_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]; + [[maybe_unused]] void *extra = &out[1]; uint32_t net_offset = 0; net_offset += 4; net_offset += 1; @@ -2161,9 +1907,9 @@ static void unmarshal_Tread([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_ UNMARSHAL_U32LE(ctx, out->count); } -static void unmarshal_Rread([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { +static void unmarshal_Rread([[maybe_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]; + [[maybe_unused]] void *extra = &out[1]; uint32_t net_offset = 0; net_offset += 4; net_offset += 1; @@ -2172,9 +1918,9 @@ static void unmarshal_Rread([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_ UNMARSHAL_BYTES(ctx, out->data, out->count); } -static void unmarshal_Twrite([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { +static void unmarshal_Twrite([[maybe_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]; + [[maybe_unused]] void *extra = &out[1]; uint32_t net_offset = 0; net_offset += 4; net_offset += 1; @@ -2185,9 +1931,9 @@ static void unmarshal_Twrite([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net UNMARSHAL_BYTES(ctx, out->data, out->count); } -static void unmarshal_Rwrite([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { +static void unmarshal_Rwrite([[maybe_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]; + [[maybe_unused]] void *extra = &out[1]; uint32_t net_offset = 0; net_offset += 4; net_offset += 1; @@ -2195,9 +1941,9 @@ static void unmarshal_Rwrite([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net UNMARSHAL_U32LE(ctx, out->count); } -static void unmarshal_Tclunk([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { +static void unmarshal_Tclunk([[maybe_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]; + [[maybe_unused]] void *extra = &out[1]; uint32_t net_offset = 0; net_offset += 4; net_offset += 1; @@ -2205,18 +1951,18 @@ static void unmarshal_Tclunk([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net UNMARSHAL_U32LE(ctx, out->fid); } -static void unmarshal_Rclunk([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { +static void unmarshal_Rclunk([[maybe_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]; + [[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([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { +static void unmarshal_Tremove([[maybe_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]; + [[maybe_unused]] void *extra = &out[1]; uint32_t net_offset = 0; net_offset += 4; net_offset += 1; @@ -2224,9 +1970,9 @@ static void unmarshal_Tremove([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *ne UNMARSHAL_U32LE(ctx, out->fid); } -static void unmarshal_Rremove([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { +static void unmarshal_Rremove([[maybe_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]; + [[maybe_unused]] void *extra = &out[1]; uint32_t net_offset = 0; net_offset += 4; net_offset += 1; @@ -2235,9 +1981,9 @@ static void unmarshal_Rremove([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *ne #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) { +static void unmarshal_Tstat([[maybe_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]; + [[maybe_unused]] void *extra = &out[1]; uint32_t net_offset = 0; net_offset += 4; net_offset += 1; @@ -2245,46 +1991,46 @@ static void unmarshal_Tstat([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_ UNMARSHAL_U32LE(ctx, out->fid); } -static void unmarshal_Rstat([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { +static void unmarshal_Rstat([[maybe_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]; + [[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.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); + 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.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); + 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([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { +static void unmarshal_Twstat([[maybe_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]; + [[maybe_unused]] void *extra = &out[1]; uint32_t net_offset = 0; net_offset += 4; net_offset += 1; @@ -2292,37 +2038,37 @@ static void unmarshal_Twstat([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net 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); + 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.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); + 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([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { +static void unmarshal_Rwstat([[maybe_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]; + [[maybe_unused]] void *extra = &out[1]; uint32_t net_offset = 0; net_offset += 4; net_offset += 1; @@ -2331,9 +2077,9 @@ static void unmarshal_Rwstat([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net #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) { +static void unmarshal_Topenfd([[maybe_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]; + [[maybe_unused]] void *extra = &out[1]; uint32_t net_offset = 0; net_offset += 4; net_offset += 1; @@ -2342,9 +2088,9 @@ static void unmarshal_Topenfd([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *ne UNMARSHAL_U8LE(ctx, out->mode); } -static void unmarshal_Ropenfd([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { +static void unmarshal_Ropenfd([[maybe_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]; + [[maybe_unused]] void *extra = &out[1]; uint32_t net_offset = 0; net_offset += 4; net_offset += 1; @@ -2358,9 +2104,9 @@ static void unmarshal_Ropenfd([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *ne #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) { +static void unmarshal_Rlerror([[maybe_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]; + [[maybe_unused]] void *extra = &out[1]; uint32_t net_offset = 0; net_offset += 4; net_offset += 1; @@ -2368,9 +2114,9 @@ static void unmarshal_Rlerror([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *ne UNMARSHAL_U32LE(ctx, out->errnum); } -static void unmarshal_Tstatfs([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { +static void unmarshal_Tstatfs([[maybe_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]; + [[maybe_unused]] void *extra = &out[1]; uint32_t net_offset = 0; net_offset += 4; net_offset += 1; @@ -2378,9 +2124,9 @@ static void unmarshal_Tstatfs([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *ne UNMARSHAL_U32LE(ctx, out->fid); } -static void unmarshal_Rstatfs([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { +static void unmarshal_Rstatfs([[maybe_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]; + [[maybe_unused]] void *extra = &out[1]; uint32_t net_offset = 0; net_offset += 4; net_offset += 1; @@ -2396,9 +2142,9 @@ static void unmarshal_Rstatfs([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *ne UNMARSHAL_U32LE(ctx, out->namelen); } -static void unmarshal_Tlopen([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { +static void unmarshal_Tlopen([[maybe_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]; + [[maybe_unused]] void *extra = &out[1]; uint32_t net_offset = 0; net_offset += 4; net_offset += 1; @@ -2407,9 +2153,9 @@ static void unmarshal_Tlopen([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net UNMARSHAL_U32LE(ctx, out->flags); } -static void unmarshal_Rlopen([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { +static void unmarshal_Rlopen([[maybe_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]; + [[maybe_unused]] void *extra = &out[1]; uint32_t net_offset = 0; net_offset += 4; net_offset += 1; @@ -2420,9 +2166,9 @@ static void unmarshal_Rlopen([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net UNMARSHAL_U32LE(ctx, out->iounit); } -static void unmarshal_Tlcreate([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { +static void unmarshal_Tlcreate([[maybe_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]; + [[maybe_unused]] void *extra = &out[1]; uint32_t net_offset = 0; net_offset += 4; net_offset += 1; @@ -2435,9 +2181,9 @@ static void unmarshal_Tlcreate([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *n UNMARSHAL_U32LE(ctx, out->gid); } -static void unmarshal_Rlcreate([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { +static void unmarshal_Rlcreate([[maybe_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]; + [[maybe_unused]] void *extra = &out[1]; uint32_t net_offset = 0; net_offset += 4; net_offset += 1; @@ -2448,9 +2194,9 @@ static void unmarshal_Rlcreate([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *n UNMARSHAL_U32LE(ctx, out->iounit); } -static void unmarshal_Tsymlink([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { +static void unmarshal_Tsymlink([[maybe_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]; + [[maybe_unused]] void *extra = &out[1]; uint32_t net_offset = 0; net_offset += 4; net_offset += 1; @@ -2463,9 +2209,9 @@ static void unmarshal_Tsymlink([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *n UNMARSHAL_U32LE(ctx, out->gid); } -static void unmarshal_Rsymlink([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { +static void unmarshal_Rsymlink([[maybe_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]; + [[maybe_unused]] void *extra = &out[1]; uint32_t net_offset = 0; net_offset += 4; net_offset += 1; @@ -2475,9 +2221,9 @@ static void unmarshal_Rsymlink([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *n UNMARSHAL_U64LE(ctx, out->qid.path); } -static void unmarshal_Tmknod([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { +static void unmarshal_Tmknod([[maybe_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]; + [[maybe_unused]] void *extra = &out[1]; uint32_t net_offset = 0; net_offset += 4; net_offset += 1; @@ -2491,9 +2237,9 @@ static void unmarshal_Tmknod([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net UNMARSHAL_U32LE(ctx, out->gid); } -static void unmarshal_Rmknod([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { +static void unmarshal_Rmknod([[maybe_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]; + [[maybe_unused]] void *extra = &out[1]; uint32_t net_offset = 0; net_offset += 4; net_offset += 1; @@ -2503,9 +2249,9 @@ static void unmarshal_Rmknod([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net UNMARSHAL_U64LE(ctx, out->qid.path); } -static void unmarshal_Trename([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { +static void unmarshal_Trename([[maybe_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]; + [[maybe_unused]] void *extra = &out[1]; uint32_t net_offset = 0; net_offset += 4; net_offset += 1; @@ -2516,18 +2262,18 @@ static void unmarshal_Trename([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *ne 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) { +static void unmarshal_Rrename([[maybe_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]; + [[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([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { +static void unmarshal_Treadlink([[maybe_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]; + [[maybe_unused]] void *extra = &out[1]; uint32_t net_offset = 0; net_offset += 4; net_offset += 1; @@ -2535,9 +2281,9 @@ static void unmarshal_Treadlink([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t * UNMARSHAL_U32LE(ctx, out->fid); } -static void unmarshal_Rreadlink([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { +static void unmarshal_Rreadlink([[maybe_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]; + [[maybe_unused]] void *extra = &out[1]; uint32_t net_offset = 0; net_offset += 4; net_offset += 1; @@ -2546,9 +2292,9 @@ static void unmarshal_Rreadlink([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t * 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) { +static void unmarshal_Tgetattr([[maybe_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]; + [[maybe_unused]] void *extra = &out[1]; uint32_t net_offset = 0; net_offset += 4; net_offset += 1; @@ -2557,9 +2303,9 @@ static void unmarshal_Tgetattr([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *n UNMARSHAL_U64LE(ctx, out->request_mask); } -static void unmarshal_Rgetattr([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { +static void unmarshal_Rgetattr([[maybe_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]; + [[maybe_unused]] void *extra = &out[1]; uint32_t net_offset = 0; net_offset += 4; net_offset += 1; @@ -2588,9 +2334,9 @@ static void unmarshal_Rgetattr([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *n UNMARSHAL_U64LE(ctx, out->data_version); } -static void unmarshal_Tsetattr([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { +static void unmarshal_Tsetattr([[maybe_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]; + [[maybe_unused]] void *extra = &out[1]; uint32_t net_offset = 0; net_offset += 4; net_offset += 1; @@ -2607,18 +2353,18 @@ static void unmarshal_Tsetattr([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *n UNMARSHAL_U64LE(ctx, out->mtime_nsec); } -static void unmarshal_Rsetattr([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { +static void unmarshal_Rsetattr([[maybe_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]; + [[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([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { +static void unmarshal_Txattrwalk([[maybe_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]; + [[maybe_unused]] void *extra = &out[1]; uint32_t net_offset = 0; net_offset += 4; net_offset += 1; @@ -2629,9 +2375,9 @@ static void unmarshal_Txattrwalk([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t 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) { +static void unmarshal_Rxattrwalk([[maybe_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]; + [[maybe_unused]] void *extra = &out[1]; uint32_t net_offset = 0; net_offset += 4; net_offset += 1; @@ -2639,9 +2385,9 @@ static void unmarshal_Rxattrwalk([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t UNMARSHAL_U64LE(ctx, out->attr_size); } -static void unmarshal_Txattrcreate([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { +static void unmarshal_Txattrcreate([[maybe_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]; + [[maybe_unused]] void *extra = &out[1]; uint32_t net_offset = 0; net_offset += 4; net_offset += 1; @@ -2653,18 +2399,18 @@ static void unmarshal_Txattrcreate([[gnu::unused]] struct lib9p_ctx *ctx, uint8_ UNMARSHAL_U32LE(ctx, out->flags); } -static void unmarshal_Rxattrcreate([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { +static void unmarshal_Rxattrcreate([[maybe_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]; + [[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([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { +static void unmarshal_Treaddir([[maybe_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]; + [[maybe_unused]] void *extra = &out[1]; uint32_t net_offset = 0; net_offset += 4; net_offset += 1; @@ -2674,9 +2420,9 @@ static void unmarshal_Treaddir([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *n UNMARSHAL_U32LE(ctx, out->count); } -static void unmarshal_Rreaddir([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { +static void unmarshal_Rreaddir([[maybe_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]; + [[maybe_unused]] void *extra = &out[1]; uint32_t net_offset = 0; net_offset += 4; net_offset += 1; @@ -2685,9 +2431,9 @@ static void unmarshal_Rreaddir([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *n UNMARSHAL_BYTES(ctx, out->data, out->count); } -static void unmarshal_Tfsync([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { +static void unmarshal_Tfsync([[maybe_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]; + [[maybe_unused]] void *extra = &out[1]; uint32_t net_offset = 0; net_offset += 4; net_offset += 1; @@ -2696,18 +2442,18 @@ static void unmarshal_Tfsync([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net UNMARSHAL_U32LE(ctx, out->datasync); } -static void unmarshal_Rfsync([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { +static void unmarshal_Rfsync([[maybe_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]; + [[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([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { +static void unmarshal_Tlock([[maybe_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]; + [[maybe_unused]] void *extra = &out[1]; uint32_t net_offset = 0; net_offset += 4; net_offset += 1; @@ -2722,9 +2468,9 @@ static void unmarshal_Tlock([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_ 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) { +static void unmarshal_Rlock([[maybe_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]; + [[maybe_unused]] void *extra = &out[1]; uint32_t net_offset = 0; net_offset += 4; net_offset += 1; @@ -2732,9 +2478,9 @@ static void unmarshal_Rlock([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_ UNMARSHAL_U8LE(ctx, out->status); } -static void unmarshal_Tgetlock([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { +static void unmarshal_Tgetlock([[maybe_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]; + [[maybe_unused]] void *extra = &out[1]; uint32_t net_offset = 0; net_offset += 4; net_offset += 1; @@ -2748,9 +2494,9 @@ static void unmarshal_Tgetlock([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *n 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) { +static void unmarshal_Rgetlock([[maybe_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]; + [[maybe_unused]] void *extra = &out[1]; uint32_t net_offset = 0; net_offset += 4; net_offset += 1; @@ -2763,9 +2509,9 @@ static void unmarshal_Rgetlock([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *n 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) { +static void unmarshal_Tlink([[maybe_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]; + [[maybe_unused]] void *extra = &out[1]; uint32_t net_offset = 0; net_offset += 4; net_offset += 1; @@ -2776,18 +2522,18 @@ static void unmarshal_Tlink([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_ 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) { +static void unmarshal_Rlink([[maybe_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]; + [[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([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { +static void unmarshal_Tmkdir([[maybe_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]; + [[maybe_unused]] void *extra = &out[1]; uint32_t net_offset = 0; net_offset += 4; net_offset += 1; @@ -2799,9 +2545,9 @@ static void unmarshal_Tmkdir([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net UNMARSHAL_U32LE(ctx, out->gid); } -static void unmarshal_Rmkdir([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { +static void unmarshal_Rmkdir([[maybe_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]; + [[maybe_unused]] void *extra = &out[1]; uint32_t net_offset = 0; net_offset += 4; net_offset += 1; @@ -2811,9 +2557,9 @@ static void unmarshal_Rmkdir([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net UNMARSHAL_U64LE(ctx, out->qid.path); } -static void unmarshal_Trenameat([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { +static void unmarshal_Trenameat([[maybe_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]; + [[maybe_unused]] void *extra = &out[1]; uint32_t net_offset = 0; net_offset += 4; net_offset += 1; @@ -2826,18 +2572,18 @@ static void unmarshal_Trenameat([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t * 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) { +static void unmarshal_Rrenameat([[maybe_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]; + [[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([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { +static void unmarshal_Tunlinkat([[maybe_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]; + [[maybe_unused]] void *extra = &out[1]; uint32_t net_offset = 0; net_offset += 4; net_offset += 1; @@ -2848,9 +2594,9 @@ static void unmarshal_Tunlinkat([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t * UNMARSHAL_U32LE(ctx, out->flags); } -static void unmarshal_Runlinkat([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { +static void unmarshal_Runlinkat([[maybe_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]; + [[maybe_unused]] void *extra = &out[1]; uint32_t net_offset = 0; net_offset += 4; net_offset += 1; @@ -2859,9 +2605,9 @@ static void unmarshal_Runlinkat([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t * #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) { +static void unmarshal_Tsession([[maybe_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]; + [[maybe_unused]] void *extra = &out[1]; uint32_t net_offset = 0; net_offset += 4; net_offset += 1; @@ -2869,18 +2615,18 @@ static void unmarshal_Tsession([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *n UNMARSHAL_U64LE(ctx, out->key); } -static void unmarshal_Rsession([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { +static void unmarshal_Rsession([[maybe_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]; + [[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([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { +static void unmarshal_Tsread([[maybe_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]; + [[maybe_unused]] void *extra = &out[1]; uint32_t net_offset = 0; net_offset += 4; net_offset += 1; @@ -2895,9 +2641,9 @@ static void unmarshal_Tsread([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net } } -static void unmarshal_Rsread([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { +static void unmarshal_Rsread([[maybe_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]; + [[maybe_unused]] void *extra = &out[1]; uint32_t net_offset = 0; net_offset += 4; net_offset += 1; @@ -2906,9 +2652,9 @@ static void unmarshal_Rsread([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net UNMARSHAL_BYTES(ctx, out->data, out->count); } -static void unmarshal_Tswrite([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { +static void unmarshal_Tswrite([[maybe_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]; + [[maybe_unused]] void *extra = &out[1]; uint32_t net_offset = 0; net_offset += 4; net_offset += 1; @@ -2925,9 +2671,9 @@ static void unmarshal_Tswrite([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *ne UNMARSHAL_BYTES(ctx, out->data, out->count); } -static void unmarshal_Rswrite([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) { +static void unmarshal_Rswrite([[maybe_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]; + [[maybe_unused]] void *extra = &out[1]; uint32_t net_offset = 0; net_offset += 4; net_offset += 1; @@ -2938,96 +2684,94 @@ static void unmarshal_Rswrite([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *ne /* 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; \ +#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_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; \ +#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_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; \ +#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_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; \ +#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_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; \ +#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_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; \ +#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->file_name.len + val->file_owner_uid.len + val->file_owner_gid.len + val->file_last_modified_uid.len; + 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->file_extension.len; + 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_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); + 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->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); + 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 -static bool marshal_Tversion(struct lib9p_ctx *ctx, struct lib9p_msg_Tversion *val, struct _marshal_ret *ret) { +#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) { - lib9p_errorf(ctx, LIB9P_ERRNO_L_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", - "Tversion", - ctx->version ? "negotiated" : "client", - ctx->max_msg_size); - return true; + 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; @@ -3037,17 +2781,15 @@ static bool marshal_Tversion(struct lib9p_ctx *ctx, struct lib9p_msg_Tversion *v 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; + return ERROR_NULL; } -static bool marshal_Rversion(struct lib9p_ctx *ctx, struct lib9p_msg_Rversion *val, struct _marshal_ret *ret) { +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) { - lib9p_errorf(ctx, LIB9P_ERRNO_L_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", - "Rversion", - ctx->version ? "negotiated" : "server", - ctx->max_msg_size); - return true; + 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; @@ -3057,10 +2799,12 @@ static bool marshal_Rversion(struct lib9p_ctx *ctx, struct lib9p_msg_Rversion *v 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; + return ERROR_NULL; } -static bool marshal_Tauth(struct lib9p_ctx *ctx, struct lib9p_msg_Tauth *val, struct _marshal_ret *ret) { +#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) ) { @@ -3068,11 +2812,9 @@ static bool marshal_Tauth(struct lib9p_ctx *ctx, struct lib9p_msg_Tauth *val, st } #endif /* CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_u */ if (needed_size > ctx->max_msg_size) { - lib9p_errorf(ctx, LIB9P_ERRNO_L_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", - "Tauth", - ctx->version ? "negotiated" : "client", - ctx->max_msg_size); - return true; + 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; @@ -3086,20 +2828,18 @@ static bool marshal_Tauth(struct lib9p_ctx *ctx, struct lib9p_msg_Tauth *val, st 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); + MARSHAL_U32LE(ctx, val->unum); } #endif /* CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_u */ - return false; + return ERROR_NULL; } -static bool marshal_Rauth(struct lib9p_ctx *ctx, struct lib9p_msg_Rauth *val, struct _marshal_ret *ret) { +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) { - lib9p_errorf(ctx, LIB9P_ERRNO_L_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", - "Rauth", - ctx->version ? "negotiated" : "server", - ctx->max_msg_size); - return true; + 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; @@ -3109,10 +2849,10 @@ static bool marshal_Rauth(struct lib9p_ctx *ctx, struct lib9p_msg_Rauth *val, st MARSHAL_U8LE(ctx, val->aqid.type & qt_masks[ctx->version]); MARSHAL_U32LE(ctx, val->aqid.vers); MARSHAL_U64LE(ctx, val->aqid.path); - return false; + return ERROR_NULL; } -static bool marshal_Tattach(struct lib9p_ctx *ctx, struct lib9p_msg_Tattach *val, struct _marshal_ret *ret) { +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) ) { @@ -3120,11 +2860,9 @@ static bool marshal_Tattach(struct lib9p_ctx *ctx, struct lib9p_msg_Tattach *val } #endif /* CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_u */ if (needed_size > ctx->max_msg_size) { - lib9p_errorf(ctx, LIB9P_ERRNO_L_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", - "Tattach", - ctx->version ? "negotiated" : "client", - ctx->max_msg_size); - return true; + 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; @@ -3139,20 +2877,18 @@ static bool marshal_Tattach(struct lib9p_ctx *ctx, struct lib9p_msg_Tattach *val 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); + MARSHAL_U32LE(ctx, val->unum); } #endif /* CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_u */ - return false; + return ERROR_NULL; } -static bool marshal_Rattach(struct lib9p_ctx *ctx, struct lib9p_msg_Rattach *val, struct _marshal_ret *ret) { +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) { - lib9p_errorf(ctx, LIB9P_ERRNO_L_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", - "Rattach", - ctx->version ? "negotiated" : "server", - ctx->max_msg_size); - return true; + 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; @@ -3162,12 +2898,12 @@ static bool marshal_Rattach(struct lib9p_ctx *ctx, struct lib9p_msg_Rattach *val MARSHAL_U8LE(ctx, val->qid.type & qt_masks[ctx->version]); MARSHAL_U32LE(ctx, val->qid.vers); MARSHAL_U64LE(ctx, val->qid.path); - return false; + 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 bool marshal_Rerror(struct lib9p_ctx *ctx, struct lib9p_msg_Rerror *val, struct _marshal_ret *ret) { +#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) { @@ -3175,11 +2911,9 @@ static bool marshal_Rerror(struct lib9p_ctx *ctx, struct lib9p_msg_Rerror *val, } #endif /* CONFIG_9P_ENABLE_9P2000_u */ if (needed_size > ctx->max_msg_size) { - lib9p_errorf(ctx, LIB9P_ERRNO_L_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", - "Rerror", - ctx->version ? "negotiated" : "server", - ctx->max_msg_size); - return true; + 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; @@ -3193,19 +2927,17 @@ static bool marshal_Rerror(struct lib9p_ctx *ctx, struct lib9p_msg_Rerror *val, MARSHAL_U32LE(ctx, val->errnum); } #endif /* CONFIG_9P_ENABLE_9P2000_u */ - return false; + return ERROR_NULL; } -#endif /* 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_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 bool marshal_Tflush(struct lib9p_ctx *ctx, struct lib9p_msg_Tflush *val, struct _marshal_ret *ret) { +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) { - lib9p_errorf(ctx, LIB9P_ERRNO_L_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", - "Tflush", - ctx->version ? "negotiated" : "client", - ctx->max_msg_size); - return true; + 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; @@ -3213,37 +2945,33 @@ static bool marshal_Tflush(struct lib9p_ctx *ctx, struct lib9p_msg_Tflush *val, MARSHAL_U8LE(ctx, 108); MARSHAL_U16LE(ctx, val->tag); MARSHAL_U16LE(ctx, val->oldtag); - return false; + return ERROR_NULL; } -static bool marshal_Rflush(struct lib9p_ctx *ctx, struct lib9p_msg_Rflush *val, struct _marshal_ret *ret) { +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) { - lib9p_errorf(ctx, LIB9P_ERRNO_L_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", - "Rflush", - ctx->version ? "negotiated" : "server", - ctx->max_msg_size); - return true; + 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 false; + return ERROR_NULL; } -static bool marshal_Twalk(struct lib9p_ctx *ctx, struct lib9p_msg_Twalk *val, struct _marshal_ret *ret) { +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) { - lib9p_errorf(ctx, LIB9P_ERRNO_L_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", - "Twalk", - ctx->version ? "negotiated" : "client", - ctx->max_msg_size); - return true; + 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; @@ -3257,17 +2985,15 @@ static bool marshal_Twalk(struct lib9p_ctx *ctx, struct lib9p_msg_Twalk *val, st MARSHAL_U16LE(ctx, val->wname[i].len); MARSHAL_BYTES_ZEROCOPY(ctx, val->wname[i].utf8, val->wname[i].len); } - return false; + return ERROR_NULL; } -static bool marshal_Rwalk(struct lib9p_ctx *ctx, struct lib9p_msg_Rwalk *val, struct _marshal_ret *ret) { +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) { - lib9p_errorf(ctx, LIB9P_ERRNO_L_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", - "Rwalk", - ctx->version ? "negotiated" : "server", - ctx->max_msg_size); - return true; + 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; @@ -3280,19 +3006,17 @@ static bool marshal_Rwalk(struct lib9p_ctx *ctx, struct lib9p_msg_Rwalk *val, st MARSHAL_U32LE(ctx, val->wqid[i].vers); MARSHAL_U64LE(ctx, val->wqid[i].path); } - return false; + 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 bool marshal_Topen(struct lib9p_ctx *ctx, struct lib9p_msg_Topen *val, struct _marshal_ret *ret) { +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) { - lib9p_errorf(ctx, LIB9P_ERRNO_L_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", - "Topen", - ctx->version ? "negotiated" : "client", - ctx->max_msg_size); - return true; + 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; @@ -3301,17 +3025,15 @@ static bool marshal_Topen(struct lib9p_ctx *ctx, struct lib9p_msg_Topen *val, st MARSHAL_U16LE(ctx, val->tag); MARSHAL_U32LE(ctx, val->fid); MARSHAL_U8LE(ctx, val->mode & o_masks[ctx->version]); - return false; + return ERROR_NULL; } -static bool marshal_Ropen(struct lib9p_ctx *ctx, struct lib9p_msg_Ropen *val, struct _marshal_ret *ret) { +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) { - lib9p_errorf(ctx, LIB9P_ERRNO_L_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", - "Ropen", - ctx->version ? "negotiated" : "server", - ctx->max_msg_size); - return true; + 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; @@ -3322,17 +3044,15 @@ static bool marshal_Ropen(struct lib9p_ctx *ctx, struct lib9p_msg_Ropen *val, st MARSHAL_U32LE(ctx, val->qid.vers); MARSHAL_U64LE(ctx, val->qid.path); MARSHAL_U32LE(ctx, val->iounit); - return false; + return ERROR_NULL; } -static bool marshal_Tcreate(struct lib9p_ctx *ctx, struct lib9p_msg_Tcreate *val, struct _marshal_ret *ret) { +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) { - lib9p_errorf(ctx, LIB9P_ERRNO_L_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", - "Tcreate", - ctx->version ? "negotiated" : "client", - ctx->max_msg_size); - return true; + 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; @@ -3344,17 +3064,15 @@ static bool marshal_Tcreate(struct lib9p_ctx *ctx, struct lib9p_msg_Tcreate *val 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; + return ERROR_NULL; } -static bool marshal_Rcreate(struct lib9p_ctx *ctx, struct lib9p_msg_Rcreate *val, struct _marshal_ret *ret) { +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) { - lib9p_errorf(ctx, LIB9P_ERRNO_L_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", - "Rcreate", - ctx->version ? "negotiated" : "server", - ctx->max_msg_size); - return true; + 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; @@ -3365,19 +3083,17 @@ static bool marshal_Rcreate(struct lib9p_ctx *ctx, struct lib9p_msg_Rcreate *val MARSHAL_U32LE(ctx, val->qid.vers); MARSHAL_U64LE(ctx, val->qid.path); MARSHAL_U32LE(ctx, val->iounit); - return false; + 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 bool marshal_Tread(struct lib9p_ctx *ctx, struct lib9p_msg_Tread *val, struct _marshal_ret *ret) { +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) { - lib9p_errorf(ctx, LIB9P_ERRNO_L_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", - "Tread", - ctx->version ? "negotiated" : "client", - ctx->max_msg_size); - return true; + 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; @@ -3387,17 +3103,15 @@ static bool marshal_Tread(struct lib9p_ctx *ctx, struct lib9p_msg_Tread *val, st MARSHAL_U32LE(ctx, val->fid); MARSHAL_U64LE(ctx, val->offset); MARSHAL_U32LE(ctx, val->count); - return false; + return ERROR_NULL; } -static bool marshal_Rread(struct lib9p_ctx *ctx, struct lib9p_msg_Rread *val, struct _marshal_ret *ret) { +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) { - lib9p_errorf(ctx, LIB9P_ERRNO_L_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", - "Rread", - ctx->version ? "negotiated" : "server", - ctx->max_msg_size); - return true; + 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; @@ -3406,17 +3120,15 @@ static bool marshal_Rread(struct lib9p_ctx *ctx, struct lib9p_msg_Rread *val, st MARSHAL_U16LE(ctx, val->tag); MARSHAL_U32LE(ctx, val->count); MARSHAL_BYTES_ZEROCOPY(ctx, val->data, val->count); - return false; + return ERROR_NULL; } -static bool marshal_Twrite(struct lib9p_ctx *ctx, struct lib9p_msg_Twrite *val, struct _marshal_ret *ret) { +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) { - lib9p_errorf(ctx, LIB9P_ERRNO_L_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", - "Twrite", - ctx->version ? "negotiated" : "client", - ctx->max_msg_size); - return true; + 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; @@ -3427,17 +3139,15 @@ static bool marshal_Twrite(struct lib9p_ctx *ctx, struct lib9p_msg_Twrite *val, MARSHAL_U64LE(ctx, val->offset); MARSHAL_U32LE(ctx, val->count); MARSHAL_BYTES_ZEROCOPY(ctx, val->data, val->count); - return false; + return ERROR_NULL; } -static bool marshal_Rwrite(struct lib9p_ctx *ctx, struct lib9p_msg_Rwrite *val, struct _marshal_ret *ret) { +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) { - lib9p_errorf(ctx, LIB9P_ERRNO_L_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", - "Rwrite", - ctx->version ? "negotiated" : "server", - ctx->max_msg_size); - return true; + 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; @@ -3445,17 +3155,15 @@ static bool marshal_Rwrite(struct lib9p_ctx *ctx, struct lib9p_msg_Rwrite *val, MARSHAL_U8LE(ctx, 119); MARSHAL_U16LE(ctx, val->tag); MARSHAL_U32LE(ctx, val->count); - return false; + return ERROR_NULL; } -static bool marshal_Tclunk(struct lib9p_ctx *ctx, struct lib9p_msg_Tclunk *val, struct _marshal_ret *ret) { +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) { - lib9p_errorf(ctx, LIB9P_ERRNO_L_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", - "Tclunk", - ctx->version ? "negotiated" : "client", - ctx->max_msg_size); - return true; + 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; @@ -3463,34 +3171,30 @@ static bool marshal_Tclunk(struct lib9p_ctx *ctx, struct lib9p_msg_Tclunk *val, MARSHAL_U8LE(ctx, 120); MARSHAL_U16LE(ctx, val->tag); MARSHAL_U32LE(ctx, val->fid); - return false; + return ERROR_NULL; } -static bool marshal_Rclunk(struct lib9p_ctx *ctx, struct lib9p_msg_Rclunk *val, struct _marshal_ret *ret) { +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) { - lib9p_errorf(ctx, LIB9P_ERRNO_L_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", - "Rclunk", - ctx->version ? "negotiated" : "server", - ctx->max_msg_size); - return true; + 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 false; + return ERROR_NULL; } -static bool marshal_Tremove(struct lib9p_ctx *ctx, struct lib9p_msg_Tremove *val, struct _marshal_ret *ret) { +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) { - lib9p_errorf(ctx, LIB9P_ERRNO_L_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", - "Tremove", - ctx->version ? "negotiated" : "client", - ctx->max_msg_size); - return true; + 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; @@ -3498,36 +3202,32 @@ static bool marshal_Tremove(struct lib9p_ctx *ctx, struct lib9p_msg_Tremove *val MARSHAL_U8LE(ctx, 122); MARSHAL_U16LE(ctx, val->tag); MARSHAL_U32LE(ctx, val->fid); - return false; + return ERROR_NULL; } -static bool marshal_Rremove(struct lib9p_ctx *ctx, struct lib9p_msg_Rremove *val, struct _marshal_ret *ret) { +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) { - lib9p_errorf(ctx, LIB9P_ERRNO_L_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", - "Rremove", - ctx->version ? "negotiated" : "server", - ctx->max_msg_size); - return true; + 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 false; + 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 bool marshal_Tstat(struct lib9p_ctx *ctx, struct lib9p_msg_Tstat *val, struct _marshal_ret *ret) { +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) { - lib9p_errorf(ctx, LIB9P_ERRNO_L_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", - "Tstat", - ctx->version ? "negotiated" : "client", - ctx->max_msg_size); - return true; + 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; @@ -3535,22 +3235,20 @@ static bool marshal_Tstat(struct lib9p_ctx *ctx, struct lib9p_msg_Tstat *val, st MARSHAL_U8LE(ctx, 124); MARSHAL_U16LE(ctx, val->tag); MARSHAL_U32LE(ctx, val->fid); - return false; + return ERROR_NULL; } -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; +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.file_extension.len; + needed_size += 14 + val->stat.extension.len; } #endif /* CONFIG_9P_ENABLE_9P2000_u */ if (needed_size > ctx->max_msg_size) { - lib9p_errorf(ctx, LIB9P_ERRNO_L_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", - "Rstat", - ctx->version ? "negotiated" : "server", - ctx->max_msg_size); - return true; + 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; @@ -3559,56 +3257,54 @@ static bool marshal_Rstat(struct lib9p_ctx *ctx, struct lib9p_msg_Rstat *val, st 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; + 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.file_extension.len; + offsetof_stat_end += 14 + val->stat.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); + 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.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); + 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 false; + return ERROR_NULL; } -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; +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.file_extension.len; + needed_size += 14 + val->stat.extension.len; } #endif /* CONFIG_9P_ENABLE_9P2000_u */ if (needed_size > ctx->max_msg_size) { - lib9p_errorf(ctx, LIB9P_ERRNO_L_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", - "Twstat", - ctx->version ? "negotiated" : "client", - ctx->max_msg_size); - return true; + 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; @@ -3618,70 +3314,66 @@ static bool marshal_Twstat(struct lib9p_ctx *ctx, struct lib9p_msg_Twstat *val, 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; + 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.file_extension.len; + offsetof_stat_end += 14 + val->stat.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); + 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.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); + 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 false; + return ERROR_NULL; } -static bool marshal_Rwstat(struct lib9p_ctx *ctx, struct lib9p_msg_Rwstat *val, struct _marshal_ret *ret) { +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) { - lib9p_errorf(ctx, LIB9P_ERRNO_L_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", - "Rwstat", - ctx->version ? "negotiated" : "server", - ctx->max_msg_size); - return true; + 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 false; + 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 bool marshal_Topenfd(struct lib9p_ctx *ctx, struct lib9p_msg_Topenfd *val, struct _marshal_ret *ret) { +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) { - lib9p_errorf(ctx, LIB9P_ERRNO_L_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", - "Topenfd", - ctx->version ? "negotiated" : "client", - ctx->max_msg_size); - return true; + 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; @@ -3690,17 +3382,15 @@ static bool marshal_Topenfd(struct lib9p_ctx *ctx, struct lib9p_msg_Topenfd *val MARSHAL_U16LE(ctx, val->tag); MARSHAL_U32LE(ctx, val->fid); MARSHAL_U8LE(ctx, val->mode & o_masks[ctx->version]); - return false; + return ERROR_NULL; } -static bool marshal_Ropenfd(struct lib9p_ctx *ctx, struct lib9p_msg_Ropenfd *val, struct _marshal_ret *ret) { +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) { - lib9p_errorf(ctx, LIB9P_ERRNO_L_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", - "Ropenfd", - ctx->version ? "negotiated" : "server", - ctx->max_msg_size); - return true; + 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; @@ -3712,19 +3402,17 @@ static bool marshal_Ropenfd(struct lib9p_ctx *ctx, struct lib9p_msg_Ropenfd *val MARSHAL_U64LE(ctx, val->qid.path); MARSHAL_U32LE(ctx, val->iounit); MARSHAL_U32LE(ctx, val->unixfd); - return false; + return ERROR_NULL; } #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) { +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) { - lib9p_errorf(ctx, LIB9P_ERRNO_L_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", - "Rlerror", - ctx->version ? "negotiated" : "server", - ctx->max_msg_size); - return true; + 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; @@ -3732,17 +3420,15 @@ static bool marshal_Rlerror(struct lib9p_ctx *ctx, struct lib9p_msg_Rlerror *val MARSHAL_U8LE(ctx, 7); MARSHAL_U16LE(ctx, val->tag); MARSHAL_U32LE(ctx, val->errnum); - return false; + return ERROR_NULL; } -static bool marshal_Tstatfs(struct lib9p_ctx *ctx, struct lib9p_msg_Tstatfs *val, struct _marshal_ret *ret) { +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) { - lib9p_errorf(ctx, LIB9P_ERRNO_L_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", - "Tstatfs", - ctx->version ? "negotiated" : "client", - ctx->max_msg_size); - return true; + 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; @@ -3750,17 +3436,15 @@ static bool marshal_Tstatfs(struct lib9p_ctx *ctx, struct lib9p_msg_Tstatfs *val MARSHAL_U8LE(ctx, 8); MARSHAL_U16LE(ctx, val->tag); MARSHAL_U32LE(ctx, val->fid); - return false; + return ERROR_NULL; } -static bool marshal_Rstatfs(struct lib9p_ctx *ctx, struct lib9p_msg_Rstatfs *val, struct _marshal_ret *ret) { +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) { - lib9p_errorf(ctx, LIB9P_ERRNO_L_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", - "Rstatfs", - ctx->version ? "negotiated" : "server", - ctx->max_msg_size); - return true; + 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; @@ -3776,17 +3460,15 @@ static bool marshal_Rstatfs(struct lib9p_ctx *ctx, struct lib9p_msg_Rstatfs *val MARSHAL_U64LE(ctx, val->ffree); MARSHAL_U64LE(ctx, val->fsid); MARSHAL_U32LE(ctx, val->namelen); - return false; + return ERROR_NULL; } -static bool marshal_Tlopen(struct lib9p_ctx *ctx, struct lib9p_msg_Tlopen *val, struct _marshal_ret *ret) { +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) { - lib9p_errorf(ctx, LIB9P_ERRNO_L_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", - "Tlopen", - ctx->version ? "negotiated" : "client", - ctx->max_msg_size); - return true; + 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; @@ -3795,17 +3477,15 @@ static bool marshal_Tlopen(struct lib9p_ctx *ctx, struct lib9p_msg_Tlopen *val, MARSHAL_U16LE(ctx, val->tag); MARSHAL_U32LE(ctx, val->fid); MARSHAL_U32LE(ctx, val->flags & lo_masks[ctx->version]); - return false; + return ERROR_NULL; } -static bool marshal_Rlopen(struct lib9p_ctx *ctx, struct lib9p_msg_Rlopen *val, struct _marshal_ret *ret) { +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) { - lib9p_errorf(ctx, LIB9P_ERRNO_L_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", - "Rlopen", - ctx->version ? "negotiated" : "server", - ctx->max_msg_size); - return true; + 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; @@ -3816,17 +3496,15 @@ static bool marshal_Rlopen(struct lib9p_ctx *ctx, struct lib9p_msg_Rlopen *val, MARSHAL_U32LE(ctx, val->qid.vers); MARSHAL_U64LE(ctx, val->qid.path); MARSHAL_U32LE(ctx, val->iounit); - return false; + return ERROR_NULL; } -static bool marshal_Tlcreate(struct lib9p_ctx *ctx, struct lib9p_msg_Tlcreate *val, struct _marshal_ret *ret) { +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) { - lib9p_errorf(ctx, LIB9P_ERRNO_L_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", - "Tlcreate", - ctx->version ? "negotiated" : "client", - ctx->max_msg_size); - return true; + 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; @@ -3839,17 +3517,15 @@ static bool marshal_Tlcreate(struct lib9p_ctx *ctx, struct lib9p_msg_Tlcreate *v 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; + return ERROR_NULL; } -static bool marshal_Rlcreate(struct lib9p_ctx *ctx, struct lib9p_msg_Rlcreate *val, struct _marshal_ret *ret) { +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) { - lib9p_errorf(ctx, LIB9P_ERRNO_L_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", - "Rlcreate", - ctx->version ? "negotiated" : "server", - ctx->max_msg_size); - return true; + 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; @@ -3860,17 +3536,15 @@ static bool marshal_Rlcreate(struct lib9p_ctx *ctx, struct lib9p_msg_Rlcreate *v MARSHAL_U32LE(ctx, val->qid.vers); MARSHAL_U64LE(ctx, val->qid.path); MARSHAL_U32LE(ctx, val->iounit); - return false; + return ERROR_NULL; } -static bool marshal_Tsymlink(struct lib9p_ctx *ctx, struct lib9p_msg_Tsymlink *val, struct _marshal_ret *ret) { +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) { - lib9p_errorf(ctx, LIB9P_ERRNO_L_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", - "Tsymlink", - ctx->version ? "negotiated" : "client", - ctx->max_msg_size); - return true; + 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; @@ -3883,17 +3557,15 @@ static bool marshal_Tsymlink(struct lib9p_ctx *ctx, struct lib9p_msg_Tsymlink *v MARSHAL_U16LE(ctx, val->symtgt.len); MARSHAL_BYTES_ZEROCOPY(ctx, val->symtgt.utf8, val->symtgt.len); MARSHAL_U32LE(ctx, val->gid); - return false; + return ERROR_NULL; } -static bool marshal_Rsymlink(struct lib9p_ctx *ctx, struct lib9p_msg_Rsymlink *val, struct _marshal_ret *ret) { +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) { - lib9p_errorf(ctx, LIB9P_ERRNO_L_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", - "Rsymlink", - ctx->version ? "negotiated" : "server", - ctx->max_msg_size); - return true; + 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; @@ -3903,17 +3575,15 @@ static bool marshal_Rsymlink(struct lib9p_ctx *ctx, struct lib9p_msg_Rsymlink *v MARSHAL_U8LE(ctx, val->qid.type & qt_masks[ctx->version]); MARSHAL_U32LE(ctx, val->qid.vers); MARSHAL_U64LE(ctx, val->qid.path); - return false; + return ERROR_NULL; } -static bool marshal_Tmknod(struct lib9p_ctx *ctx, struct lib9p_msg_Tmknod *val, struct _marshal_ret *ret) { +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) { - lib9p_errorf(ctx, LIB9P_ERRNO_L_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", - "Tmknod", - ctx->version ? "negotiated" : "client", - ctx->max_msg_size); - return true; + 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; @@ -3927,17 +3597,15 @@ static bool marshal_Tmknod(struct lib9p_ctx *ctx, struct lib9p_msg_Tmknod *val, MARSHAL_U32LE(ctx, val->major); MARSHAL_U32LE(ctx, val->minor); MARSHAL_U32LE(ctx, val->gid); - return false; + return ERROR_NULL; } -static bool marshal_Rmknod(struct lib9p_ctx *ctx, struct lib9p_msg_Rmknod *val, struct _marshal_ret *ret) { +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) { - lib9p_errorf(ctx, LIB9P_ERRNO_L_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", - "Rmknod", - ctx->version ? "negotiated" : "server", - ctx->max_msg_size); - return true; + 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; @@ -3947,17 +3615,15 @@ static bool marshal_Rmknod(struct lib9p_ctx *ctx, struct lib9p_msg_Rmknod *val, MARSHAL_U8LE(ctx, val->qid.type & qt_masks[ctx->version]); MARSHAL_U32LE(ctx, val->qid.vers); MARSHAL_U64LE(ctx, val->qid.path); - return false; + return ERROR_NULL; } -static bool marshal_Trename(struct lib9p_ctx *ctx, struct lib9p_msg_Trename *val, struct _marshal_ret *ret) { +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) { - lib9p_errorf(ctx, LIB9P_ERRNO_L_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", - "Trename", - ctx->version ? "negotiated" : "client", - ctx->max_msg_size); - return true; + 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; @@ -3968,34 +3634,30 @@ static bool marshal_Trename(struct lib9p_ctx *ctx, struct lib9p_msg_Trename *val MARSHAL_U32LE(ctx, val->dfid); MARSHAL_U16LE(ctx, val->name.len); MARSHAL_BYTES_ZEROCOPY(ctx, val->name.utf8, val->name.len); - return false; + return ERROR_NULL; } -static bool marshal_Rrename(struct lib9p_ctx *ctx, struct lib9p_msg_Rrename *val, struct _marshal_ret *ret) { +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) { - lib9p_errorf(ctx, LIB9P_ERRNO_L_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", - "Rrename", - ctx->version ? "negotiated" : "server", - ctx->max_msg_size); - return true; + 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 false; + return ERROR_NULL; } -static bool marshal_Treadlink(struct lib9p_ctx *ctx, struct lib9p_msg_Treadlink *val, struct _marshal_ret *ret) { +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) { - lib9p_errorf(ctx, LIB9P_ERRNO_L_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", - "Treadlink", - ctx->version ? "negotiated" : "client", - ctx->max_msg_size); - return true; + 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; @@ -4003,17 +3665,15 @@ static bool marshal_Treadlink(struct lib9p_ctx *ctx, struct lib9p_msg_Treadlink MARSHAL_U8LE(ctx, 22); MARSHAL_U16LE(ctx, val->tag); MARSHAL_U32LE(ctx, val->fid); - return false; + return ERROR_NULL; } -static bool marshal_Rreadlink(struct lib9p_ctx *ctx, struct lib9p_msg_Rreadlink *val, struct _marshal_ret *ret) { +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) { - lib9p_errorf(ctx, LIB9P_ERRNO_L_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", - "Rreadlink", - ctx->version ? "negotiated" : "server", - ctx->max_msg_size); - return true; + 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; @@ -4022,17 +3682,15 @@ static bool marshal_Rreadlink(struct lib9p_ctx *ctx, struct lib9p_msg_Rreadlink MARSHAL_U16LE(ctx, val->tag); MARSHAL_U16LE(ctx, val->target.len); MARSHAL_BYTES_ZEROCOPY(ctx, val->target.utf8, val->target.len); - return false; + return ERROR_NULL; } -static bool marshal_Tgetattr(struct lib9p_ctx *ctx, struct lib9p_msg_Tgetattr *val, struct _marshal_ret *ret) { +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) { - lib9p_errorf(ctx, LIB9P_ERRNO_L_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", - "Tgetattr", - ctx->version ? "negotiated" : "client", - ctx->max_msg_size); - return true; + 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; @@ -4041,17 +3699,15 @@ static bool marshal_Tgetattr(struct lib9p_ctx *ctx, struct lib9p_msg_Tgetattr *v MARSHAL_U16LE(ctx, val->tag); MARSHAL_U32LE(ctx, val->fid); MARSHAL_U64LE(ctx, val->request_mask & getattr_masks[ctx->version]); - return false; + return ERROR_NULL; } -static bool marshal_Rgetattr(struct lib9p_ctx *ctx, struct lib9p_msg_Rgetattr *val, struct _marshal_ret *ret) { +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) { - lib9p_errorf(ctx, LIB9P_ERRNO_L_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", - "Rgetattr", - ctx->version ? "negotiated" : "server", - ctx->max_msg_size); - return true; + 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; @@ -4080,17 +3736,15 @@ static bool marshal_Rgetattr(struct lib9p_ctx *ctx, struct lib9p_msg_Rgetattr *v MARSHAL_U64LE(ctx, val->btime_nsec); MARSHAL_U64LE(ctx, val->gen); MARSHAL_U64LE(ctx, val->data_version); - return false; + return ERROR_NULL; } -static bool marshal_Tsetattr(struct lib9p_ctx *ctx, struct lib9p_msg_Tsetattr *val, struct _marshal_ret *ret) { +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) { - lib9p_errorf(ctx, LIB9P_ERRNO_L_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", - "Tsetattr", - ctx->version ? "negotiated" : "client", - ctx->max_msg_size); - return true; + 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; @@ -4107,34 +3761,30 @@ static bool marshal_Tsetattr(struct lib9p_ctx *ctx, struct lib9p_msg_Tsetattr *v MARSHAL_U64LE(ctx, val->atime_nsec); MARSHAL_U64LE(ctx, val->mtime_sec); MARSHAL_U64LE(ctx, val->mtime_nsec); - return false; + return ERROR_NULL; } -static bool marshal_Rsetattr(struct lib9p_ctx *ctx, struct lib9p_msg_Rsetattr *val, struct _marshal_ret *ret) { +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) { - lib9p_errorf(ctx, LIB9P_ERRNO_L_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", - "Rsetattr", - ctx->version ? "negotiated" : "server", - ctx->max_msg_size); - return true; + 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 false; + return ERROR_NULL; } -static bool marshal_Txattrwalk(struct lib9p_ctx *ctx, struct lib9p_msg_Txattrwalk *val, struct _marshal_ret *ret) { +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) { - lib9p_errorf(ctx, LIB9P_ERRNO_L_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", - "Txattrwalk", - ctx->version ? "negotiated" : "client", - ctx->max_msg_size); - return true; + 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; @@ -4145,17 +3795,15 @@ static bool marshal_Txattrwalk(struct lib9p_ctx *ctx, struct lib9p_msg_Txattrwal MARSHAL_U32LE(ctx, val->newfid); MARSHAL_U16LE(ctx, val->name.len); MARSHAL_BYTES_ZEROCOPY(ctx, val->name.utf8, val->name.len); - return false; + return ERROR_NULL; } -static bool marshal_Rxattrwalk(struct lib9p_ctx *ctx, struct lib9p_msg_Rxattrwalk *val, struct _marshal_ret *ret) { +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) { - lib9p_errorf(ctx, LIB9P_ERRNO_L_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", - "Rxattrwalk", - ctx->version ? "negotiated" : "server", - ctx->max_msg_size); - return true; + 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; @@ -4163,17 +3811,15 @@ static bool marshal_Rxattrwalk(struct lib9p_ctx *ctx, struct lib9p_msg_Rxattrwal MARSHAL_U8LE(ctx, 31); MARSHAL_U16LE(ctx, val->tag); MARSHAL_U64LE(ctx, val->attr_size); - return false; + return ERROR_NULL; } -static bool marshal_Txattrcreate(struct lib9p_ctx *ctx, struct lib9p_msg_Txattrcreate *val, struct _marshal_ret *ret) { +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) { - lib9p_errorf(ctx, LIB9P_ERRNO_L_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", - "Txattrcreate", - ctx->version ? "negotiated" : "client", - ctx->max_msg_size); - return true; + 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; @@ -4185,34 +3831,30 @@ static bool marshal_Txattrcreate(struct lib9p_ctx *ctx, struct lib9p_msg_Txattrc MARSHAL_BYTES_ZEROCOPY(ctx, val->name.utf8, val->name.len); MARSHAL_U64LE(ctx, val->attr_size); MARSHAL_U32LE(ctx, val->flags); - return false; + return ERROR_NULL; } -static bool marshal_Rxattrcreate(struct lib9p_ctx *ctx, struct lib9p_msg_Rxattrcreate *val, struct _marshal_ret *ret) { +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) { - lib9p_errorf(ctx, LIB9P_ERRNO_L_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", - "Rxattrcreate", - ctx->version ? "negotiated" : "server", - ctx->max_msg_size); - return true; + 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 false; + return ERROR_NULL; } -static bool marshal_Treaddir(struct lib9p_ctx *ctx, struct lib9p_msg_Treaddir *val, struct _marshal_ret *ret) { +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) { - lib9p_errorf(ctx, LIB9P_ERRNO_L_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", - "Treaddir", - ctx->version ? "negotiated" : "client", - ctx->max_msg_size); - return true; + 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; @@ -4222,17 +3864,15 @@ static bool marshal_Treaddir(struct lib9p_ctx *ctx, struct lib9p_msg_Treaddir *v MARSHAL_U32LE(ctx, val->fid); MARSHAL_U64LE(ctx, val->offset); MARSHAL_U32LE(ctx, val->count); - return false; + return ERROR_NULL; } -static bool marshal_Rreaddir(struct lib9p_ctx *ctx, struct lib9p_msg_Rreaddir *val, struct _marshal_ret *ret) { +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)) { - lib9p_errorf(ctx, LIB9P_ERRNO_L_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", - "Rreaddir", - ctx->version ? "negotiated" : "server", - ctx->max_msg_size); - return true; + 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; @@ -4241,17 +3881,15 @@ static bool marshal_Rreaddir(struct lib9p_ctx *ctx, struct lib9p_msg_Rreaddir *v MARSHAL_U16LE(ctx, val->tag); MARSHAL_U32LE(ctx, val->count); MARSHAL_BYTES_ZEROCOPY(ctx, val->data, val->count); - return false; + return ERROR_NULL; } -static bool marshal_Tfsync(struct lib9p_ctx *ctx, struct lib9p_msg_Tfsync *val, struct _marshal_ret *ret) { +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) { - lib9p_errorf(ctx, LIB9P_ERRNO_L_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", - "Tfsync", - ctx->version ? "negotiated" : "client", - ctx->max_msg_size); - return true; + 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; @@ -4260,34 +3898,30 @@ static bool marshal_Tfsync(struct lib9p_ctx *ctx, struct lib9p_msg_Tfsync *val, MARSHAL_U16LE(ctx, val->tag); MARSHAL_U32LE(ctx, val->fid); MARSHAL_U32LE(ctx, val->datasync); - return false; + return ERROR_NULL; } -static bool marshal_Rfsync(struct lib9p_ctx *ctx, struct lib9p_msg_Rfsync *val, struct _marshal_ret *ret) { +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) { - lib9p_errorf(ctx, LIB9P_ERRNO_L_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", - "Rfsync", - ctx->version ? "negotiated" : "server", - ctx->max_msg_size); - return true; + 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 false; + return ERROR_NULL; } -static bool marshal_Tlock(struct lib9p_ctx *ctx, struct lib9p_msg_Tlock *val, struct _marshal_ret *ret) { +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) { - lib9p_errorf(ctx, LIB9P_ERRNO_L_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", - "Tlock", - ctx->version ? "negotiated" : "client", - ctx->max_msg_size); - return true; + 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; @@ -4302,17 +3936,15 @@ static bool marshal_Tlock(struct lib9p_ctx *ctx, struct lib9p_msg_Tlock *val, st 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; + return ERROR_NULL; } -static bool marshal_Rlock(struct lib9p_ctx *ctx, struct lib9p_msg_Rlock *val, struct _marshal_ret *ret) { +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) { - lib9p_errorf(ctx, LIB9P_ERRNO_L_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", - "Rlock", - ctx->version ? "negotiated" : "server", - ctx->max_msg_size); - return true; + 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; @@ -4320,17 +3952,15 @@ static bool marshal_Rlock(struct lib9p_ctx *ctx, struct lib9p_msg_Rlock *val, st MARSHAL_U8LE(ctx, 53); MARSHAL_U16LE(ctx, val->tag); MARSHAL_U8LE(ctx, val->status); - return false; + return ERROR_NULL; } -static bool marshal_Tgetlock(struct lib9p_ctx *ctx, struct lib9p_msg_Tgetlock *val, struct _marshal_ret *ret) { +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) { - lib9p_errorf(ctx, LIB9P_ERRNO_L_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", - "Tgetlock", - ctx->version ? "negotiated" : "client", - ctx->max_msg_size); - return true; + 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; @@ -4344,17 +3974,15 @@ static bool marshal_Tgetlock(struct lib9p_ctx *ctx, struct lib9p_msg_Tgetlock *v 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; + return ERROR_NULL; } -static bool marshal_Rgetlock(struct lib9p_ctx *ctx, struct lib9p_msg_Rgetlock *val, struct _marshal_ret *ret) { +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) { - lib9p_errorf(ctx, LIB9P_ERRNO_L_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", - "Rgetlock", - ctx->version ? "negotiated" : "server", - ctx->max_msg_size); - return true; + 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; @@ -4367,17 +3995,15 @@ static bool marshal_Rgetlock(struct lib9p_ctx *ctx, struct lib9p_msg_Rgetlock *v 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; + return ERROR_NULL; } -static bool marshal_Tlink(struct lib9p_ctx *ctx, struct lib9p_msg_Tlink *val, struct _marshal_ret *ret) { +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) { - lib9p_errorf(ctx, LIB9P_ERRNO_L_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", - "Tlink", - ctx->version ? "negotiated" : "client", - ctx->max_msg_size); - return true; + 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; @@ -4388,34 +4014,30 @@ static bool marshal_Tlink(struct lib9p_ctx *ctx, struct lib9p_msg_Tlink *val, st MARSHAL_U32LE(ctx, val->fid); MARSHAL_U16LE(ctx, val->name.len); MARSHAL_BYTES_ZEROCOPY(ctx, val->name.utf8, val->name.len); - return false; + return ERROR_NULL; } -static bool marshal_Rlink(struct lib9p_ctx *ctx, struct lib9p_msg_Rlink *val, struct _marshal_ret *ret) { +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) { - lib9p_errorf(ctx, LIB9P_ERRNO_L_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", - "Rlink", - ctx->version ? "negotiated" : "server", - ctx->max_msg_size); - return true; + 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 false; + return ERROR_NULL; } -static bool marshal_Tmkdir(struct lib9p_ctx *ctx, struct lib9p_msg_Tmkdir *val, struct _marshal_ret *ret) { +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) { - lib9p_errorf(ctx, LIB9P_ERRNO_L_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", - "Tmkdir", - ctx->version ? "negotiated" : "client", - ctx->max_msg_size); - return true; + 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; @@ -4427,17 +4049,15 @@ static bool marshal_Tmkdir(struct lib9p_ctx *ctx, struct lib9p_msg_Tmkdir *val, 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; + return ERROR_NULL; } -static bool marshal_Rmkdir(struct lib9p_ctx *ctx, struct lib9p_msg_Rmkdir *val, struct _marshal_ret *ret) { +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) { - lib9p_errorf(ctx, LIB9P_ERRNO_L_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", - "Rmkdir", - ctx->version ? "negotiated" : "server", - ctx->max_msg_size); - return true; + 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; @@ -4447,17 +4067,15 @@ static bool marshal_Rmkdir(struct lib9p_ctx *ctx, struct lib9p_msg_Rmkdir *val, MARSHAL_U8LE(ctx, val->qid.type & qt_masks[ctx->version]); MARSHAL_U32LE(ctx, val->qid.vers); MARSHAL_U64LE(ctx, val->qid.path); - return false; + return ERROR_NULL; } -static bool marshal_Trenameat(struct lib9p_ctx *ctx, struct lib9p_msg_Trenameat *val, struct _marshal_ret *ret) { +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) { - lib9p_errorf(ctx, LIB9P_ERRNO_L_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", - "Trenameat", - ctx->version ? "negotiated" : "client", - ctx->max_msg_size); - return true; + 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; @@ -4470,34 +4088,30 @@ static bool marshal_Trenameat(struct lib9p_ctx *ctx, struct lib9p_msg_Trenameat MARSHAL_U32LE(ctx, val->newdirfid); MARSHAL_U16LE(ctx, val->newname.len); MARSHAL_BYTES_ZEROCOPY(ctx, val->newname.utf8, val->newname.len); - return false; + return ERROR_NULL; } -static bool marshal_Rrenameat(struct lib9p_ctx *ctx, struct lib9p_msg_Rrenameat *val, struct _marshal_ret *ret) { +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) { - lib9p_errorf(ctx, LIB9P_ERRNO_L_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", - "Rrenameat", - ctx->version ? "negotiated" : "server", - ctx->max_msg_size); - return true; + 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 false; + return ERROR_NULL; } -static bool marshal_Tunlinkat(struct lib9p_ctx *ctx, struct lib9p_msg_Tunlinkat *val, struct _marshal_ret *ret) { +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) { - lib9p_errorf(ctx, LIB9P_ERRNO_L_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", - "Tunlinkat", - ctx->version ? "negotiated" : "client", - ctx->max_msg_size); - return true; + 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; @@ -4508,36 +4122,32 @@ static bool marshal_Tunlinkat(struct lib9p_ctx *ctx, struct lib9p_msg_Tunlinkat MARSHAL_U16LE(ctx, val->name.len); MARSHAL_BYTES_ZEROCOPY(ctx, val->name.utf8, val->name.len); MARSHAL_U32LE(ctx, val->flags); - return false; + return ERROR_NULL; } -static bool marshal_Runlinkat(struct lib9p_ctx *ctx, struct lib9p_msg_Runlinkat *val, struct _marshal_ret *ret) { +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) { - lib9p_errorf(ctx, LIB9P_ERRNO_L_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", - "Runlinkat", - ctx->version ? "negotiated" : "server", - ctx->max_msg_size); - return true; + 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 false; + return ERROR_NULL; } #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) { +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) { - lib9p_errorf(ctx, LIB9P_ERRNO_L_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", - "Tsession", - ctx->version ? "negotiated" : "client", - ctx->max_msg_size); - return true; + 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; @@ -4545,37 +4155,33 @@ static bool marshal_Tsession(struct lib9p_ctx *ctx, struct lib9p_msg_Tsession *v MARSHAL_U8LE(ctx, 150); MARSHAL_U16LE(ctx, val->tag); MARSHAL_U64LE(ctx, val->key); - return false; + return ERROR_NULL; } -static bool marshal_Rsession(struct lib9p_ctx *ctx, struct lib9p_msg_Rsession *val, struct _marshal_ret *ret) { +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) { - lib9p_errorf(ctx, LIB9P_ERRNO_L_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", - "Rsession", - ctx->version ? "negotiated" : "server", - ctx->max_msg_size); - return true; + 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 false; + return ERROR_NULL; } -static bool marshal_Tsread(struct lib9p_ctx *ctx, struct lib9p_msg_Tsread *val, struct _marshal_ret *ret) { +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)) { - lib9p_errorf(ctx, LIB9P_ERRNO_L_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", - "Tsread", - ctx->version ? "negotiated" : "client", - ctx->max_msg_size); - return true; + 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; @@ -4588,17 +4194,15 @@ static bool marshal_Tsread(struct lib9p_ctx *ctx, struct lib9p_msg_Tsread *val, MARSHAL_U16LE(ctx, val->wname[i].len); MARSHAL_BYTES_ZEROCOPY(ctx, val->wname[i].utf8, val->wname[i].len); } - return false; + return ERROR_NULL; } -static bool marshal_Rsread(struct lib9p_ctx *ctx, struct lib9p_msg_Rsread *val, struct _marshal_ret *ret) { +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)) { - lib9p_errorf(ctx, LIB9P_ERRNO_L_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", - "Rsread", - ctx->version ? "negotiated" : "server", - ctx->max_msg_size); - return true; + 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; @@ -4607,20 +4211,18 @@ static bool marshal_Rsread(struct lib9p_ctx *ctx, struct lib9p_msg_Rsread *val, MARSHAL_U16LE(ctx, val->tag); MARSHAL_U32LE(ctx, val->count); MARSHAL_BYTES_ZEROCOPY(ctx, val->data, val->count); - return false; + return ERROR_NULL; } -static bool marshal_Tswrite(struct lib9p_ctx *ctx, struct lib9p_msg_Tswrite *val, struct _marshal_ret *ret) { +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)) { - lib9p_errorf(ctx, LIB9P_ERRNO_L_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", - "Tswrite", - ctx->version ? "negotiated" : "client", - ctx->max_msg_size); - return true; + 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; @@ -4635,17 +4237,15 @@ static bool marshal_Tswrite(struct lib9p_ctx *ctx, struct lib9p_msg_Tswrite *val } MARSHAL_U32LE(ctx, val->count); MARSHAL_BYTES_ZEROCOPY(ctx, val->data, val->count); - return false; + return ERROR_NULL; } -static bool marshal_Rswrite(struct lib9p_ctx *ctx, struct lib9p_msg_Rswrite *val, struct _marshal_ret *ret) { +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) { - lib9p_errorf(ctx, LIB9P_ERRNO_L_ERANGE, "%s message too large to marshal into %s limit (limit=%"PRIu32")", - "Rswrite", - ctx->version ? "negotiated" : "server", - ctx->max_msg_size); - return true; + 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; @@ -4653,3192 +4253,3185 @@ static bool marshal_Rswrite(struct lib9p_ctx *ctx, struct lib9p_msg_Rswrite *val MARSHAL_U8LE(ctx, 155); MARSHAL_U16LE(ctx, val->tag); MARSHAL_U32LE(ctx, val->count); - return false; + return ERROR_NULL; } #endif /* CONFIG_9P_ENABLE_9P2000_e */ -/* *_format *******************************************************************/ +/* 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 -static void lib9p_tag_format(lib9p_tag_t *self, struct fmt_state *state) { +#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_state_puts(state, "NOTAG"); + fmt_print_str(w, "NOTAG"); break; default: - fmt_state_printf(state, "%"PRIu16, *self); + fmt_print_base10(w, *self); } } -static void lib9p_fid_format(lib9p_fid_t *self, struct fmt_state *state) { +[[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_state_puts(state, "NOFID"); + fmt_print_str(w, "NOFID"); break; default: - fmt_state_printf(state, "%"PRIu32, *self); + fmt_print_base10(w, *self); } } -static void lib9p_s_format(struct lib9p_s *self, struct fmt_state *state) { -#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) { +#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_state_putchar(state, '('); + fmt_print_byte(w, '('); if ((*self & ~((lib9p_dm_t)0777)) & (UINT32_C(1)<<31)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "DIR"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "DIR"); empty = false; } if ((*self & ~((lib9p_dm_t)0777)) & (UINT32_C(1)<<30)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "APPEND"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "APPEND"); empty = false; } if ((*self & ~((lib9p_dm_t)0777)) & (UINT32_C(1)<<29)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "EXCL"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "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"); + 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_state_putchar(state, '|'); - fmt_state_puts(state, "AUTH"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "AUTH"); empty = false; } if ((*self & ~((lib9p_dm_t)0777)) & (UINT32_C(1)<<26)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "TMP"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "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"); + 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_state_putchar(state, '|'); - fmt_state_puts(state, "1<<24"); + 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_state_putchar(state, '|'); - fmt_state_puts(state, "DEVICE"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "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"); + 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_state_putchar(state, '|'); - fmt_state_puts(state, "PIPE"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "PIPE"); empty = false; } if ((*self & ~((lib9p_dm_t)0777)) & (UINT32_C(1)<<20)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "SOCKET"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "SOCKET"); empty = false; } if ((*self & ~((lib9p_dm_t)0777)) & (UINT32_C(1)<<19)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "SETUID"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "SETUID"); empty = false; } if ((*self & ~((lib9p_dm_t)0777)) & (UINT32_C(1)<<18)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "SETGID"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "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"); + 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_state_putchar(state, '|'); - fmt_state_puts(state, "1<<16"); + 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_state_putchar(state, '|'); - fmt_state_puts(state, "1<<15"); + 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_state_putchar(state, '|'); - fmt_state_puts(state, "1<<14"); + 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_state_putchar(state, '|'); - fmt_state_puts(state, "1<<13"); + 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_state_putchar(state, '|'); - fmt_state_puts(state, "1<<12"); + 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_state_putchar(state, '|'); - fmt_state_puts(state, "1<<11"); + 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_state_putchar(state, '|'); - fmt_state_puts(state, "1<<10"); + 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_state_putchar(state, '|'); - fmt_state_puts(state, "1<<9"); + 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_state_putchar(state, '|'); - fmt_state_puts(state, "OWNER_R"); + 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_state_putchar(state, '|'); - fmt_state_puts(state, "OWNER_W"); + 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_state_putchar(state, '|'); - fmt_state_puts(state, "OWNER_X"); + 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_state_putchar(state, '|'); - fmt_state_puts(state, "GROUP_R"); + 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_state_putchar(state, '|'); - fmt_state_puts(state, "GROUP_W"); + 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_state_putchar(state, '|'); - fmt_state_puts(state, "GROUP_X"); + 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_state_putchar(state, '|'); - fmt_state_puts(state, "OTHER_R"); + 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_state_putchar(state, '|'); - fmt_state_puts(state, "OTHER_W"); + 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_state_putchar(state, '|'); - fmt_state_puts(state, "OTHER_X"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "OTHER_X"); empty = false; } if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_printf(state, "%#04"PRIo32, *self & 0777); - fmt_state_putchar(state, ')'); + 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 */ -#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) { +#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_state_putchar(state, '('); + fmt_print_byte(w, '('); if (*self & (UINT8_C(1)<<7)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "DIR"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "DIR"); empty = false; } if (*self & (UINT8_C(1)<<6)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "APPEND"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "APPEND"); empty = false; } if (*self & (UINT8_C(1)<<5)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "EXCL"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "EXCL"); empty = false; } if (*self & (UINT8_C(1)<<4)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "_PLAN9_MOUNT"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "_PLAN9_MOUNT"); empty = false; } if (*self & (UINT8_C(1)<<3)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "AUTH"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "AUTH"); empty = false; } if (*self & (UINT8_C(1)<<2)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "TMP"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "TMP"); empty = false; } if (*self & (UINT8_C(1)<<1)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "SYMLINK"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "SYMLINK"); empty = false; } if (*self & (UINT8_C(1)<<0)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<0"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "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, " }"); + 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 */ +#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 -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) { +[[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_state_putchar(state, '('); + fmt_print_byte(w, '('); if (*self & (UINT8_C(1)<<7)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<7"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<7"); empty = false; } if (*self & (UINT8_C(1)<<6)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "RCLOSE"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "RCLOSE"); empty = false; } if (*self & (UINT8_C(1)<<5)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "CEXEC"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "CEXEC"); empty = false; } if (*self & (UINT8_C(1)<<4)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "TRUNC"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "TRUNC"); empty = false; } if (*self & (UINT8_C(1)<<3)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<3"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<3"); empty = false; } if (*self & (UINT8_C(1)<<2)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<2"); + 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_state_putchar(state, '|'); - fmt_state_puts(state, "MODE_READ"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "MODE_READ"); empty = false; break; case LIB9P_O_MODE_WRITE: if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "MODE_WRITE"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "MODE_WRITE"); empty = false; break; case LIB9P_O_MODE_RDWR: if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "MODE_RDWR"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "MODE_RDWR"); empty = false; break; case LIB9P_O_MODE_EXEC: if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "MODE_EXEC"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "MODE_EXEC"); empty = false; break; default: if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_printf(state, "%"PRIu8, *self & LIB9P_O_MODE_MASK); + fmt_print_byte(w, '|'); + fmt_print_base10(w, *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, " }"); -} - -#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_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, " errstr="); - lib9p_s_format(&self->errstr, state); -#if CONFIG_9P_ENABLE_9P2000_u - fmt_state_puts(state, " errnum="); - lib9p_errno_format(&self->errnum, state); -#endif /* CONFIG_9P_ENABLE_9P2000_u */ - 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_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_putchar(state, ','); - fmt_state_putchar(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_putchar(state, ','); - fmt_state_putchar(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); - if (is_valid_utf8_without_nul((uint8_t *)self->data, (size_t)self->count)) { -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wformat" -#pragma GCC diagnostic ignored "-Wformat-extra-args" - fmt_state_printf(state, " data=%.*q%s", - (int)(self->count < 50 ? self->count : 50), - (char *)self->data, - self->count < 50 ? "" : "..."); -#pragma GCC diagnostic pop - } else { - 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); - if (is_valid_utf8_without_nul((uint8_t *)self->data, (size_t)self->count)) { -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wformat" -#pragma GCC diagnostic ignored "-Wformat-extra-args" - fmt_state_printf(state, " data=%.*q%s", - (int)(self->count < 50 ? self->count : 50), - (char *)self->data, - self->count < 50 ? "" : "..."); -#pragma GCC diagnostic pop - } else { - 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, " }"); + 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_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) { +[[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_state_puts(state, "NONUID"); + fmt_print_str(w, "NONUID"); break; default: - fmt_state_printf(state, "%"PRIu32, *self); + fmt_print_base10(w, *self); } } -static void lib9p_errno_format(lib9p_errno_t *self, struct fmt_state *state) { +[[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_state_puts(state, "NOERROR"); + fmt_print_str(w, "NOERROR"); break; case LIB9P_ERRNO_L_EPERM: - fmt_state_puts(state, "L_EPERM"); + fmt_print_str(w, "L_EPERM"); break; case LIB9P_ERRNO_L_ENOENT: - fmt_state_puts(state, "L_ENOENT"); + fmt_print_str(w, "L_ENOENT"); break; case LIB9P_ERRNO_L_ESRCH: - fmt_state_puts(state, "L_ESRCH"); + fmt_print_str(w, "L_ESRCH"); break; case LIB9P_ERRNO_L_EINTR: - fmt_state_puts(state, "L_EINTR"); + fmt_print_str(w, "L_EINTR"); break; case LIB9P_ERRNO_L_EIO: - fmt_state_puts(state, "L_EIO"); + fmt_print_str(w, "L_EIO"); break; case LIB9P_ERRNO_L_ENXIO: - fmt_state_puts(state, "L_ENXIO"); + fmt_print_str(w, "L_ENXIO"); break; case LIB9P_ERRNO_L_E2BIG: - fmt_state_puts(state, "L_E2BIG"); + fmt_print_str(w, "L_E2BIG"); break; case LIB9P_ERRNO_L_ENOEXEC: - fmt_state_puts(state, "L_ENOEXEC"); + fmt_print_str(w, "L_ENOEXEC"); break; case LIB9P_ERRNO_L_EBADF: - fmt_state_puts(state, "L_EBADF"); + fmt_print_str(w, "L_EBADF"); break; case LIB9P_ERRNO_L_ECHILD: - fmt_state_puts(state, "L_ECHILD"); + fmt_print_str(w, "L_ECHILD"); break; case LIB9P_ERRNO_L_EAGAIN: - fmt_state_puts(state, "L_EAGAIN"); + fmt_print_str(w, "L_EAGAIN"); break; case LIB9P_ERRNO_L_ENOMEM: - fmt_state_puts(state, "L_ENOMEM"); + fmt_print_str(w, "L_ENOMEM"); break; case LIB9P_ERRNO_L_EACCES: - fmt_state_puts(state, "L_EACCES"); + fmt_print_str(w, "L_EACCES"); break; case LIB9P_ERRNO_L_EFAULT: - fmt_state_puts(state, "L_EFAULT"); + fmt_print_str(w, "L_EFAULT"); break; case LIB9P_ERRNO_L_ENOTBLK: - fmt_state_puts(state, "L_ENOTBLK"); + fmt_print_str(w, "L_ENOTBLK"); break; case LIB9P_ERRNO_L_EBUSY: - fmt_state_puts(state, "L_EBUSY"); + fmt_print_str(w, "L_EBUSY"); break; case LIB9P_ERRNO_L_EEXIST: - fmt_state_puts(state, "L_EEXIST"); + fmt_print_str(w, "L_EEXIST"); break; case LIB9P_ERRNO_L_EXDEV: - fmt_state_puts(state, "L_EXDEV"); + fmt_print_str(w, "L_EXDEV"); break; case LIB9P_ERRNO_L_ENODEV: - fmt_state_puts(state, "L_ENODEV"); + fmt_print_str(w, "L_ENODEV"); break; case LIB9P_ERRNO_L_ENOTDIR: - fmt_state_puts(state, "L_ENOTDIR"); + fmt_print_str(w, "L_ENOTDIR"); break; case LIB9P_ERRNO_L_EISDIR: - fmt_state_puts(state, "L_EISDIR"); + fmt_print_str(w, "L_EISDIR"); break; case LIB9P_ERRNO_L_EINVAL: - fmt_state_puts(state, "L_EINVAL"); + fmt_print_str(w, "L_EINVAL"); break; case LIB9P_ERRNO_L_ENFILE: - fmt_state_puts(state, "L_ENFILE"); + fmt_print_str(w, "L_ENFILE"); break; case LIB9P_ERRNO_L_EMFILE: - fmt_state_puts(state, "L_EMFILE"); + fmt_print_str(w, "L_EMFILE"); break; case LIB9P_ERRNO_L_ENOTTY: - fmt_state_puts(state, "L_ENOTTY"); + fmt_print_str(w, "L_ENOTTY"); break; case LIB9P_ERRNO_L_ETXTBSY: - fmt_state_puts(state, "L_ETXTBSY"); + fmt_print_str(w, "L_ETXTBSY"); break; case LIB9P_ERRNO_L_EFBIG: - fmt_state_puts(state, "L_EFBIG"); + fmt_print_str(w, "L_EFBIG"); break; case LIB9P_ERRNO_L_ENOSPC: - fmt_state_puts(state, "L_ENOSPC"); + fmt_print_str(w, "L_ENOSPC"); break; case LIB9P_ERRNO_L_ESPIPE: - fmt_state_puts(state, "L_ESPIPE"); + fmt_print_str(w, "L_ESPIPE"); break; case LIB9P_ERRNO_L_EROFS: - fmt_state_puts(state, "L_EROFS"); + fmt_print_str(w, "L_EROFS"); break; case LIB9P_ERRNO_L_EMLINK: - fmt_state_puts(state, "L_EMLINK"); + fmt_print_str(w, "L_EMLINK"); break; case LIB9P_ERRNO_L_EPIPE: - fmt_state_puts(state, "L_EPIPE"); + fmt_print_str(w, "L_EPIPE"); break; case LIB9P_ERRNO_L_EDOM: - fmt_state_puts(state, "L_EDOM"); + fmt_print_str(w, "L_EDOM"); break; case LIB9P_ERRNO_L_ERANGE: - fmt_state_puts(state, "L_ERANGE"); + fmt_print_str(w, "L_ERANGE"); break; case LIB9P_ERRNO_L_EDEADLK: - fmt_state_puts(state, "L_EDEADLK"); + fmt_print_str(w, "L_EDEADLK"); break; case LIB9P_ERRNO_L_ENAMETOOLONG: - fmt_state_puts(state, "L_ENAMETOOLONG"); + fmt_print_str(w, "L_ENAMETOOLONG"); break; case LIB9P_ERRNO_L_ENOLCK: - fmt_state_puts(state, "L_ENOLCK"); + fmt_print_str(w, "L_ENOLCK"); break; case LIB9P_ERRNO_L_ENOSYS: - fmt_state_puts(state, "L_ENOSYS"); + fmt_print_str(w, "L_ENOSYS"); break; case LIB9P_ERRNO_L_ENOTEMPTY: - fmt_state_puts(state, "L_ENOTEMPTY"); + fmt_print_str(w, "L_ENOTEMPTY"); break; case LIB9P_ERRNO_L_ELOOP: - fmt_state_puts(state, "L_ELOOP"); + fmt_print_str(w, "L_ELOOP"); break; case LIB9P_ERRNO_L_ENOMSG: - fmt_state_puts(state, "L_ENOMSG"); + fmt_print_str(w, "L_ENOMSG"); break; case LIB9P_ERRNO_L_EIDRM: - fmt_state_puts(state, "L_EIDRM"); + fmt_print_str(w, "L_EIDRM"); break; case LIB9P_ERRNO_L_ECHRNG: - fmt_state_puts(state, "L_ECHRNG"); + fmt_print_str(w, "L_ECHRNG"); break; case LIB9P_ERRNO_L_EL2NSYNC: - fmt_state_puts(state, "L_EL2NSYNC"); + fmt_print_str(w, "L_EL2NSYNC"); break; case LIB9P_ERRNO_L_EL3HLT: - fmt_state_puts(state, "L_EL3HLT"); + fmt_print_str(w, "L_EL3HLT"); break; case LIB9P_ERRNO_L_EL3RST: - fmt_state_puts(state, "L_EL3RST"); + fmt_print_str(w, "L_EL3RST"); break; case LIB9P_ERRNO_L_ELNRNG: - fmt_state_puts(state, "L_ELNRNG"); + fmt_print_str(w, "L_ELNRNG"); break; case LIB9P_ERRNO_L_EUNATCH: - fmt_state_puts(state, "L_EUNATCH"); + fmt_print_str(w, "L_EUNATCH"); break; case LIB9P_ERRNO_L_ENOCSI: - fmt_state_puts(state, "L_ENOCSI"); + fmt_print_str(w, "L_ENOCSI"); break; case LIB9P_ERRNO_L_EL2HLT: - fmt_state_puts(state, "L_EL2HLT"); + fmt_print_str(w, "L_EL2HLT"); break; case LIB9P_ERRNO_L_EBADE: - fmt_state_puts(state, "L_EBADE"); + fmt_print_str(w, "L_EBADE"); break; case LIB9P_ERRNO_L_EBADR: - fmt_state_puts(state, "L_EBADR"); + fmt_print_str(w, "L_EBADR"); break; case LIB9P_ERRNO_L_EXFULL: - fmt_state_puts(state, "L_EXFULL"); + fmt_print_str(w, "L_EXFULL"); break; case LIB9P_ERRNO_L_ENOANO: - fmt_state_puts(state, "L_ENOANO"); + fmt_print_str(w, "L_ENOANO"); break; case LIB9P_ERRNO_L_EBADRQC: - fmt_state_puts(state, "L_EBADRQC"); + fmt_print_str(w, "L_EBADRQC"); break; case LIB9P_ERRNO_L_EBADSLT: - fmt_state_puts(state, "L_EBADSLT"); + fmt_print_str(w, "L_EBADSLT"); break; case LIB9P_ERRNO_L_EBFONT: - fmt_state_puts(state, "L_EBFONT"); + fmt_print_str(w, "L_EBFONT"); break; case LIB9P_ERRNO_L_ENOSTR: - fmt_state_puts(state, "L_ENOSTR"); + fmt_print_str(w, "L_ENOSTR"); break; case LIB9P_ERRNO_L_ENODATA: - fmt_state_puts(state, "L_ENODATA"); + fmt_print_str(w, "L_ENODATA"); break; case LIB9P_ERRNO_L_ETIME: - fmt_state_puts(state, "L_ETIME"); + fmt_print_str(w, "L_ETIME"); break; case LIB9P_ERRNO_L_ENOSR: - fmt_state_puts(state, "L_ENOSR"); + fmt_print_str(w, "L_ENOSR"); break; case LIB9P_ERRNO_L_ENONET: - fmt_state_puts(state, "L_ENONET"); + fmt_print_str(w, "L_ENONET"); break; case LIB9P_ERRNO_L_ENOPKG: - fmt_state_puts(state, "L_ENOPKG"); + fmt_print_str(w, "L_ENOPKG"); break; case LIB9P_ERRNO_L_EREMOTE: - fmt_state_puts(state, "L_EREMOTE"); + fmt_print_str(w, "L_EREMOTE"); break; case LIB9P_ERRNO_L_ENOLINK: - fmt_state_puts(state, "L_ENOLINK"); + fmt_print_str(w, "L_ENOLINK"); break; case LIB9P_ERRNO_L_EADV: - fmt_state_puts(state, "L_EADV"); + fmt_print_str(w, "L_EADV"); break; case LIB9P_ERRNO_L_ESRMNT: - fmt_state_puts(state, "L_ESRMNT"); + fmt_print_str(w, "L_ESRMNT"); break; case LIB9P_ERRNO_L_ECOMM: - fmt_state_puts(state, "L_ECOMM"); + fmt_print_str(w, "L_ECOMM"); break; case LIB9P_ERRNO_L_EPROTO: - fmt_state_puts(state, "L_EPROTO"); + fmt_print_str(w, "L_EPROTO"); break; case LIB9P_ERRNO_L_EMULTIHOP: - fmt_state_puts(state, "L_EMULTIHOP"); + fmt_print_str(w, "L_EMULTIHOP"); break; case LIB9P_ERRNO_L_EDOTDOT: - fmt_state_puts(state, "L_EDOTDOT"); + fmt_print_str(w, "L_EDOTDOT"); break; case LIB9P_ERRNO_L_EBADMSG: - fmt_state_puts(state, "L_EBADMSG"); + fmt_print_str(w, "L_EBADMSG"); break; case LIB9P_ERRNO_L_EOVERFLOW: - fmt_state_puts(state, "L_EOVERFLOW"); + fmt_print_str(w, "L_EOVERFLOW"); break; case LIB9P_ERRNO_L_ENOTUNIQ: - fmt_state_puts(state, "L_ENOTUNIQ"); + fmt_print_str(w, "L_ENOTUNIQ"); break; case LIB9P_ERRNO_L_EBADFD: - fmt_state_puts(state, "L_EBADFD"); + fmt_print_str(w, "L_EBADFD"); break; case LIB9P_ERRNO_L_EREMCHG: - fmt_state_puts(state, "L_EREMCHG"); + fmt_print_str(w, "L_EREMCHG"); break; case LIB9P_ERRNO_L_ELIBACC: - fmt_state_puts(state, "L_ELIBACC"); + fmt_print_str(w, "L_ELIBACC"); break; case LIB9P_ERRNO_L_ELIBBAD: - fmt_state_puts(state, "L_ELIBBAD"); + fmt_print_str(w, "L_ELIBBAD"); break; case LIB9P_ERRNO_L_ELIBSCN: - fmt_state_puts(state, "L_ELIBSCN"); + fmt_print_str(w, "L_ELIBSCN"); break; case LIB9P_ERRNO_L_ELIBMAX: - fmt_state_puts(state, "L_ELIBMAX"); + fmt_print_str(w, "L_ELIBMAX"); break; case LIB9P_ERRNO_L_ELIBEXEC: - fmt_state_puts(state, "L_ELIBEXEC"); + fmt_print_str(w, "L_ELIBEXEC"); break; case LIB9P_ERRNO_L_EILSEQ: - fmt_state_puts(state, "L_EILSEQ"); + fmt_print_str(w, "L_EILSEQ"); break; case LIB9P_ERRNO_L_ERESTART: - fmt_state_puts(state, "L_ERESTART"); + fmt_print_str(w, "L_ERESTART"); break; case LIB9P_ERRNO_L_ESTRPIPE: - fmt_state_puts(state, "L_ESTRPIPE"); + fmt_print_str(w, "L_ESTRPIPE"); break; case LIB9P_ERRNO_L_EUSERS: - fmt_state_puts(state, "L_EUSERS"); + fmt_print_str(w, "L_EUSERS"); break; case LIB9P_ERRNO_L_ENOTSOCK: - fmt_state_puts(state, "L_ENOTSOCK"); + fmt_print_str(w, "L_ENOTSOCK"); break; case LIB9P_ERRNO_L_EDESTADDRREQ: - fmt_state_puts(state, "L_EDESTADDRREQ"); + fmt_print_str(w, "L_EDESTADDRREQ"); break; case LIB9P_ERRNO_L_EMSGSIZE: - fmt_state_puts(state, "L_EMSGSIZE"); + fmt_print_str(w, "L_EMSGSIZE"); break; case LIB9P_ERRNO_L_EPROTOTYPE: - fmt_state_puts(state, "L_EPROTOTYPE"); + fmt_print_str(w, "L_EPROTOTYPE"); break; case LIB9P_ERRNO_L_ENOPROTOOPT: - fmt_state_puts(state, "L_ENOPROTOOPT"); + fmt_print_str(w, "L_ENOPROTOOPT"); break; case LIB9P_ERRNO_L_EPROTONOSUPPORT: - fmt_state_puts(state, "L_EPROTONOSUPPORT"); + fmt_print_str(w, "L_EPROTONOSUPPORT"); break; case LIB9P_ERRNO_L_ESOCKTNOSUPPORT: - fmt_state_puts(state, "L_ESOCKTNOSUPPORT"); + fmt_print_str(w, "L_ESOCKTNOSUPPORT"); break; case LIB9P_ERRNO_L_EOPNOTSUPP: - fmt_state_puts(state, "L_EOPNOTSUPP"); + fmt_print_str(w, "L_EOPNOTSUPP"); break; case LIB9P_ERRNO_L_EPFNOSUPPORT: - fmt_state_puts(state, "L_EPFNOSUPPORT"); + fmt_print_str(w, "L_EPFNOSUPPORT"); break; case LIB9P_ERRNO_L_EAFNOSUPPORT: - fmt_state_puts(state, "L_EAFNOSUPPORT"); + fmt_print_str(w, "L_EAFNOSUPPORT"); break; case LIB9P_ERRNO_L_EADDRINUSE: - fmt_state_puts(state, "L_EADDRINUSE"); + fmt_print_str(w, "L_EADDRINUSE"); break; case LIB9P_ERRNO_L_EADDRNOTAVAIL: - fmt_state_puts(state, "L_EADDRNOTAVAIL"); + fmt_print_str(w, "L_EADDRNOTAVAIL"); break; case LIB9P_ERRNO_L_ENETDOWN: - fmt_state_puts(state, "L_ENETDOWN"); + fmt_print_str(w, "L_ENETDOWN"); break; case LIB9P_ERRNO_L_ENETUNREACH: - fmt_state_puts(state, "L_ENETUNREACH"); + fmt_print_str(w, "L_ENETUNREACH"); break; case LIB9P_ERRNO_L_ENETRESET: - fmt_state_puts(state, "L_ENETRESET"); + fmt_print_str(w, "L_ENETRESET"); break; case LIB9P_ERRNO_L_ECONNABORTED: - fmt_state_puts(state, "L_ECONNABORTED"); + fmt_print_str(w, "L_ECONNABORTED"); break; case LIB9P_ERRNO_L_ECONNRESET: - fmt_state_puts(state, "L_ECONNRESET"); + fmt_print_str(w, "L_ECONNRESET"); break; case LIB9P_ERRNO_L_ENOBUFS: - fmt_state_puts(state, "L_ENOBUFS"); + fmt_print_str(w, "L_ENOBUFS"); break; case LIB9P_ERRNO_L_EISCONN: - fmt_state_puts(state, "L_EISCONN"); + fmt_print_str(w, "L_EISCONN"); break; case LIB9P_ERRNO_L_ENOTCONN: - fmt_state_puts(state, "L_ENOTCONN"); + fmt_print_str(w, "L_ENOTCONN"); break; case LIB9P_ERRNO_L_ESHUTDOWN: - fmt_state_puts(state, "L_ESHUTDOWN"); + fmt_print_str(w, "L_ESHUTDOWN"); break; case LIB9P_ERRNO_L_ETOOMANYREFS: - fmt_state_puts(state, "L_ETOOMANYREFS"); + fmt_print_str(w, "L_ETOOMANYREFS"); break; case LIB9P_ERRNO_L_ETIMEDOUT: - fmt_state_puts(state, "L_ETIMEDOUT"); + fmt_print_str(w, "L_ETIMEDOUT"); break; case LIB9P_ERRNO_L_ECONNREFUSED: - fmt_state_puts(state, "L_ECONNREFUSED"); + fmt_print_str(w, "L_ECONNREFUSED"); break; case LIB9P_ERRNO_L_EHOSTDOWN: - fmt_state_puts(state, "L_EHOSTDOWN"); + fmt_print_str(w, "L_EHOSTDOWN"); break; case LIB9P_ERRNO_L_EHOSTUNREACH: - fmt_state_puts(state, "L_EHOSTUNREACH"); + fmt_print_str(w, "L_EHOSTUNREACH"); break; case LIB9P_ERRNO_L_EALREADY: - fmt_state_puts(state, "L_EALREADY"); + fmt_print_str(w, "L_EALREADY"); break; case LIB9P_ERRNO_L_EINPROGRESS: - fmt_state_puts(state, "L_EINPROGRESS"); + fmt_print_str(w, "L_EINPROGRESS"); break; case LIB9P_ERRNO_L_ESTALE: - fmt_state_puts(state, "L_ESTALE"); + fmt_print_str(w, "L_ESTALE"); break; case LIB9P_ERRNO_L_EUCLEAN: - fmt_state_puts(state, "L_EUCLEAN"); + fmt_print_str(w, "L_EUCLEAN"); break; case LIB9P_ERRNO_L_ENOTNAM: - fmt_state_puts(state, "L_ENOTNAM"); + fmt_print_str(w, "L_ENOTNAM"); break; case LIB9P_ERRNO_L_ENAVAIL: - fmt_state_puts(state, "L_ENAVAIL"); + fmt_print_str(w, "L_ENAVAIL"); break; case LIB9P_ERRNO_L_EISNAM: - fmt_state_puts(state, "L_EISNAM"); + fmt_print_str(w, "L_EISNAM"); break; case LIB9P_ERRNO_L_EREMOTEIO: - fmt_state_puts(state, "L_EREMOTEIO"); + fmt_print_str(w, "L_EREMOTEIO"); break; case LIB9P_ERRNO_L_EDQUOT: - fmt_state_puts(state, "L_EDQUOT"); + fmt_print_str(w, "L_EDQUOT"); break; case LIB9P_ERRNO_L_ENOMEDIUM: - fmt_state_puts(state, "L_ENOMEDIUM"); + fmt_print_str(w, "L_ENOMEDIUM"); break; case LIB9P_ERRNO_L_EMEDIUMTYPE: - fmt_state_puts(state, "L_EMEDIUMTYPE"); + fmt_print_str(w, "L_EMEDIUMTYPE"); break; case LIB9P_ERRNO_L_ECANCELED: - fmt_state_puts(state, "L_ECANCELED"); + fmt_print_str(w, "L_ECANCELED"); break; case LIB9P_ERRNO_L_ENOKEY: - fmt_state_puts(state, "L_ENOKEY"); + fmt_print_str(w, "L_ENOKEY"); break; case LIB9P_ERRNO_L_EKEYEXPIRED: - fmt_state_puts(state, "L_EKEYEXPIRED"); + fmt_print_str(w, "L_EKEYEXPIRED"); break; case LIB9P_ERRNO_L_EKEYREVOKED: - fmt_state_puts(state, "L_EKEYREVOKED"); + fmt_print_str(w, "L_EKEYREVOKED"); break; case LIB9P_ERRNO_L_EKEYREJECTED: - fmt_state_puts(state, "L_EKEYREJECTED"); + fmt_print_str(w, "L_EKEYREJECTED"); break; case LIB9P_ERRNO_L_EOWNERDEAD: - fmt_state_puts(state, "L_EOWNERDEAD"); + fmt_print_str(w, "L_EOWNERDEAD"); break; case LIB9P_ERRNO_L_ENOTRECOVERABLE: - fmt_state_puts(state, "L_ENOTRECOVERABLE"); + fmt_print_str(w, "L_ENOTRECOVERABLE"); break; case LIB9P_ERRNO_L_ERFKILL: - fmt_state_puts(state, "L_ERFKILL"); + fmt_print_str(w, "L_ERFKILL"); break; case LIB9P_ERRNO_L_EHWPOISON: - fmt_state_puts(state, "L_EHWPOISON"); + fmt_print_str(w, "L_EHWPOISON"); break; default: - fmt_state_printf(state, "%"PRIu32, *self); + fmt_print_base10(w, *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) { +[[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_state_puts(state, "V9FS_MAGIC"); + fmt_print_str(w, "V9FS_MAGIC"); break; default: - fmt_state_printf(state, "%"PRIu32, *self); + fmt_print_base10(w, *self); } } -static void lib9p_lo_format(lib9p_lo_t *self, struct fmt_state *state) { +[[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_state_putchar(state, '('); + fmt_print_byte(w, '('); if (*self & (UINT32_C(1)<<31)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<31"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<31"); empty = false; } if (*self & (UINT32_C(1)<<30)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<30"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<30"); empty = false; } if (*self & (UINT32_C(1)<<29)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<29"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<29"); empty = false; } if (*self & (UINT32_C(1)<<28)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<28"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<28"); empty = false; } if (*self & (UINT32_C(1)<<27)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<27"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<27"); empty = false; } if (*self & (UINT32_C(1)<<26)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<26"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<26"); empty = false; } if (*self & (UINT32_C(1)<<25)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<25"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<25"); empty = false; } if (*self & (UINT32_C(1)<<24)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<24"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<24"); empty = false; } if (*self & (UINT32_C(1)<<23)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<23"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<23"); empty = false; } if (*self & (UINT32_C(1)<<22)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<22"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<22"); empty = false; } if (*self & (UINT32_C(1)<<21)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<21"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<21"); empty = false; } if (*self & (UINT32_C(1)<<20)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "SYNC"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "SYNC"); empty = false; } if (*self & (UINT32_C(1)<<19)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "CLOEXEC"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "CLOEXEC"); empty = false; } if (*self & (UINT32_C(1)<<18)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "NOATIME"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "NOATIME"); empty = false; } if (*self & (UINT32_C(1)<<17)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "NOFOLLOW"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "NOFOLLOW"); empty = false; } if (*self & (UINT32_C(1)<<16)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "DIRECTORY"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "DIRECTORY"); empty = false; } if (*self & (UINT32_C(1)<<15)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "LARGEFILE"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "LARGEFILE"); empty = false; } if (*self & (UINT32_C(1)<<14)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "DIRECT"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "DIRECT"); empty = false; } if (*self & (UINT32_C(1)<<13)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "BSD_FASYNC"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "BSD_FASYNC"); empty = false; } if (*self & (UINT32_C(1)<<12)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "DSYNC"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "DSYNC"); empty = false; } if (*self & (UINT32_C(1)<<11)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "NONBLOCK"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "NONBLOCK"); empty = false; } if (*self & (UINT32_C(1)<<10)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "APPEND"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "APPEND"); empty = false; } if (*self & (UINT32_C(1)<<9)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "TRUNC"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "TRUNC"); empty = false; } if (*self & (UINT32_C(1)<<8)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "NOCTTY"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "NOCTTY"); empty = false; } if (*self & (UINT32_C(1)<<7)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "EXCL"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "EXCL"); empty = false; } if (*self & (UINT32_C(1)<<6)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "CREATE"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "CREATE"); empty = false; } if (*self & (UINT32_C(1)<<5)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<5"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<5"); empty = false; } if (*self & (UINT32_C(1)<<4)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<4"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<4"); empty = false; } if (*self & (UINT32_C(1)<<3)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<3"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<3"); empty = false; } if (*self & (UINT32_C(1)<<2)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<2"); + 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_state_putchar(state, '|'); - fmt_state_puts(state, "MODE_RDONLY"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "MODE_RDONLY"); empty = false; break; case LIB9P_LO_MODE_WRONLY: if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "MODE_WRONLY"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "MODE_WRONLY"); empty = false; break; case LIB9P_LO_MODE_RDWR: if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "MODE_RDWR"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "MODE_RDWR"); empty = false; break; case LIB9P_LO_MODE_NOACCESS: if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "MODE_NOACCESS"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "MODE_NOACCESS"); empty = false; break; default: if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_printf(state, "%"PRIu32, *self & LIB9P_LO_MODE_MASK); + fmt_print_byte(w, '|'); + fmt_print_base10(w, *self & LIB9P_LO_MODE_MASK); empty = false; } if (empty) - fmt_state_putchar(state, '0'); - fmt_state_putchar(state, ')'); + fmt_print_byte(w, '0'); + fmt_print_byte(w, ')'); } -static void lib9p_dt_format(lib9p_dt_t *self, struct fmt_state *state) { +[[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_state_puts(state, "UNKNOWN"); + fmt_print_str(w, "UNKNOWN"); break; case LIB9P_DT_PIPE: - fmt_state_puts(state, "PIPE"); + fmt_print_str(w, "PIPE"); break; case LIB9P_DT_CHAR_DEV: - fmt_state_puts(state, "CHAR_DEV"); + fmt_print_str(w, "CHAR_DEV"); break; case LIB9P_DT_DIRECTORY: - fmt_state_puts(state, "DIRECTORY"); + fmt_print_str(w, "DIRECTORY"); break; case LIB9P_DT_BLOCK_DEV: - fmt_state_puts(state, "BLOCK_DEV"); + fmt_print_str(w, "BLOCK_DEV"); break; case LIB9P_DT_REGULAR: - fmt_state_puts(state, "REGULAR"); + fmt_print_str(w, "REGULAR"); break; case LIB9P_DT_SYMLINK: - fmt_state_puts(state, "SYMLINK"); + fmt_print_str(w, "SYMLINK"); break; case LIB9P_DT_SOCKET: - fmt_state_puts(state, "SOCKET"); + fmt_print_str(w, "SOCKET"); break; case _LIB9P_DT_WHITEOUT: - fmt_state_puts(state, "_WHITEOUT"); + fmt_print_str(w, "_WHITEOUT"); break; default: - fmt_state_printf(state, "%"PRIu8, *self); + fmt_print_base10(w, *self); } } -static void lib9p_mode_format(lib9p_mode_t *self, struct fmt_state *state) { +[[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_state_putchar(state, '('); + fmt_print_byte(w, '('); if (*self & (UINT32_C(1)<<31)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<31"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<31"); empty = false; } if (*self & (UINT32_C(1)<<30)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<30"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<30"); empty = false; } if (*self & (UINT32_C(1)<<29)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<29"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<29"); empty = false; } if (*self & (UINT32_C(1)<<28)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<28"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<28"); empty = false; } if (*self & (UINT32_C(1)<<27)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<27"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<27"); empty = false; } if (*self & (UINT32_C(1)<<26)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<26"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<26"); empty = false; } if (*self & (UINT32_C(1)<<25)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<25"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<25"); empty = false; } if (*self & (UINT32_C(1)<<24)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<24"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<24"); empty = false; } if (*self & (UINT32_C(1)<<23)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<23"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<23"); empty = false; } if (*self & (UINT32_C(1)<<22)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<22"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<22"); empty = false; } if (*self & (UINT32_C(1)<<21)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<21"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<21"); empty = false; } if (*self & (UINT32_C(1)<<20)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<20"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<20"); empty = false; } if (*self & (UINT32_C(1)<<19)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<19"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<19"); empty = false; } if (*self & (UINT32_C(1)<<18)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<18"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<18"); empty = false; } if (*self & (UINT32_C(1)<<17)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<17"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<17"); empty = false; } if (*self & (UINT32_C(1)<<16)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<16"); + 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_state_putchar(state, '|'); - fmt_state_puts(state, "FMT_PIPE"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "FMT_PIPE"); empty = false; break; case LIB9P_MODE_FMT_CHAR_DEV: if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "FMT_CHAR_DEV"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "FMT_CHAR_DEV"); empty = false; break; case LIB9P_MODE_FMT_DIRECTORY: if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "FMT_DIRECTORY"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "FMT_DIRECTORY"); empty = false; break; case LIB9P_MODE_FMT_BLOCK_DEV: if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "FMT_BLOCK_DEV"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "FMT_BLOCK_DEV"); empty = false; break; case LIB9P_MODE_FMT_REGULAR: if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "FMT_REGULAR"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "FMT_REGULAR"); empty = false; break; case LIB9P_MODE_FMT_SYMLINK: if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "FMT_SYMLINK"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "FMT_SYMLINK"); empty = false; break; case LIB9P_MODE_FMT_SOCKET: if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "FMT_SOCKET"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "FMT_SOCKET"); empty = false; break; default: if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_printf(state, "%"PRIu32, *self & LIB9P_MODE_FMT_MASK); + fmt_print_byte(w, '|'); + fmt_print_base10(w, *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"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "PERM_SETGROUP"); empty = false; } if (*self & (UINT32_C(1)<<10)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "PERM_SETUSER"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "PERM_SETUSER"); empty = false; } if (*self & (UINT32_C(1)<<9)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "PERM_STICKY"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "PERM_STICKY"); empty = false; } if (*self & (UINT32_C(1)<<8)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "PERM_OWNER_R"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "PERM_OWNER_R"); empty = false; } if (*self & (UINT32_C(1)<<7)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "PERM_OWNER_W"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "PERM_OWNER_W"); empty = false; } if (*self & (UINT32_C(1)<<6)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "PERM_OWNER_X"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "PERM_OWNER_X"); empty = false; } if (*self & (UINT32_C(1)<<5)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "PERM_GROUP_R"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "PERM_GROUP_R"); empty = false; } if (*self & (UINT32_C(1)<<4)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "PERM_GROUP_W"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "PERM_GROUP_W"); empty = false; } if (*self & (UINT32_C(1)<<3)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "PERM_GROUP_X"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "PERM_GROUP_X"); empty = false; } if (*self & (UINT32_C(1)<<2)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "PERM_OTHER_R"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "PERM_OTHER_R"); empty = false; } if (*self & (UINT32_C(1)<<1)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "PERM_OTHER_W"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "PERM_OTHER_W"); empty = false; } if (*self & (UINT32_C(1)<<0)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "PERM_OTHER_X"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "PERM_OTHER_X"); empty = false; } if (empty) - fmt_state_putchar(state, '0'); - fmt_state_putchar(state, ')'); + fmt_print_byte(w, '0'); + fmt_print_byte(w, ')'); } -static void lib9p_b4_format(lib9p_b4_t *self, struct fmt_state *state) { +[[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_state_puts(state, "FALSE"); + fmt_print_str(w, "FALSE"); break; case LIB9P_B4_TRUE: - fmt_state_puts(state, "TRUE"); + fmt_print_str(w, "TRUE"); break; default: - fmt_state_printf(state, "%"PRIu32, *self); + fmt_print_base10(w, *self); } } -static void lib9p_getattr_format(lib9p_getattr_t *self, struct fmt_state *state) { +[[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_state_putchar(state, '('); + fmt_print_byte(w, '('); if (*self & (UINT64_C(1)<<63)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<63"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<63"); empty = false; } if (*self & (UINT64_C(1)<<62)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<62"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<62"); empty = false; } if (*self & (UINT64_C(1)<<61)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<61"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<61"); empty = false; } if (*self & (UINT64_C(1)<<60)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<60"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<60"); empty = false; } if (*self & (UINT64_C(1)<<59)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<59"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<59"); empty = false; } if (*self & (UINT64_C(1)<<58)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<58"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<58"); empty = false; } if (*self & (UINT64_C(1)<<57)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<57"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<57"); empty = false; } if (*self & (UINT64_C(1)<<56)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<56"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<56"); empty = false; } if (*self & (UINT64_C(1)<<55)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<55"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<55"); empty = false; } if (*self & (UINT64_C(1)<<54)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<54"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<54"); empty = false; } if (*self & (UINT64_C(1)<<53)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<53"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<53"); empty = false; } if (*self & (UINT64_C(1)<<52)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<52"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<52"); empty = false; } if (*self & (UINT64_C(1)<<51)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<51"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<51"); empty = false; } if (*self & (UINT64_C(1)<<50)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<50"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<50"); empty = false; } if (*self & (UINT64_C(1)<<49)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<49"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<49"); empty = false; } if (*self & (UINT64_C(1)<<48)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<48"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<48"); empty = false; } if (*self & (UINT64_C(1)<<47)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<47"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<47"); empty = false; } if (*self & (UINT64_C(1)<<46)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<46"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<46"); empty = false; } if (*self & (UINT64_C(1)<<45)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<45"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<45"); empty = false; } if (*self & (UINT64_C(1)<<44)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<44"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<44"); empty = false; } if (*self & (UINT64_C(1)<<43)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<43"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<43"); empty = false; } if (*self & (UINT64_C(1)<<42)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<42"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<42"); empty = false; } if (*self & (UINT64_C(1)<<41)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<41"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<41"); empty = false; } if (*self & (UINT64_C(1)<<40)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<40"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<40"); empty = false; } if (*self & (UINT64_C(1)<<39)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<39"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<39"); empty = false; } if (*self & (UINT64_C(1)<<38)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<38"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<38"); empty = false; } if (*self & (UINT64_C(1)<<37)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<37"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<37"); empty = false; } if (*self & (UINT64_C(1)<<36)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<36"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<36"); empty = false; } if (*self & (UINT64_C(1)<<35)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<35"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<35"); empty = false; } if (*self & (UINT64_C(1)<<34)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<34"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<34"); empty = false; } if (*self & (UINT64_C(1)<<33)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<33"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<33"); empty = false; } if (*self & (UINT64_C(1)<<32)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<32"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<32"); empty = false; } if (*self & (UINT64_C(1)<<31)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<31"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<31"); empty = false; } if (*self & (UINT64_C(1)<<30)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<30"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<30"); empty = false; } if (*self & (UINT64_C(1)<<29)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<29"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<29"); empty = false; } if (*self & (UINT64_C(1)<<28)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<28"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<28"); empty = false; } if (*self & (UINT64_C(1)<<27)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<27"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<27"); empty = false; } if (*self & (UINT64_C(1)<<26)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<26"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<26"); empty = false; } if (*self & (UINT64_C(1)<<25)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<25"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<25"); empty = false; } if (*self & (UINT64_C(1)<<24)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<24"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<24"); empty = false; } if (*self & (UINT64_C(1)<<23)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<23"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<23"); empty = false; } if (*self & (UINT64_C(1)<<22)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<22"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<22"); empty = false; } if (*self & (UINT64_C(1)<<21)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<21"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<21"); empty = false; } if (*self & (UINT64_C(1)<<20)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<20"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<20"); empty = false; } if (*self & (UINT64_C(1)<<19)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<19"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<19"); empty = false; } if (*self & (UINT64_C(1)<<18)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<18"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<18"); empty = false; } if (*self & (UINT64_C(1)<<17)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<17"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<17"); empty = false; } if (*self & (UINT64_C(1)<<16)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<16"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<16"); empty = false; } if (*self & (UINT64_C(1)<<15)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<15"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<15"); empty = false; } if (*self & (UINT64_C(1)<<14)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<14"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<14"); empty = false; } if (*self & (UINT64_C(1)<<13)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "DATA_VERSION"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "DATA_VERSION"); empty = false; } if (*self & (UINT64_C(1)<<12)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "GEN"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "GEN"); empty = false; } if (*self & (UINT64_C(1)<<11)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "BTIME"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "BTIME"); empty = false; } if (*self & (UINT64_C(1)<<10)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "BLOCKS"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "BLOCKS"); empty = false; } if (*self & (UINT64_C(1)<<9)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "SIZE"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "SIZE"); empty = false; } if (*self & (UINT64_C(1)<<8)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "INO"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "INO"); empty = false; } if (*self & (UINT64_C(1)<<7)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "CTIME"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "CTIME"); empty = false; } if (*self & (UINT64_C(1)<<6)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "MTIME"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "MTIME"); empty = false; } if (*self & (UINT64_C(1)<<5)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "ATIME"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "ATIME"); empty = false; } if (*self & (UINT64_C(1)<<4)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "RDEV"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "RDEV"); empty = false; } if (*self & (UINT64_C(1)<<3)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "GID"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "GID"); empty = false; } if (*self & (UINT64_C(1)<<2)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "UID"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "UID"); empty = false; } if (*self & (UINT64_C(1)<<1)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "NLINK"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "NLINK"); empty = false; } if (*self & (UINT64_C(1)<<0)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "MODE"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "MODE"); empty = false; } if (empty) - fmt_state_putchar(state, '0'); - fmt_state_putchar(state, ')'); + fmt_print_byte(w, '0'); + fmt_print_byte(w, ')'); } -static void lib9p_setattr_format(lib9p_setattr_t *self, struct fmt_state *state) { +[[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_state_putchar(state, '('); + fmt_print_byte(w, '('); if (*self & (UINT32_C(1)<<31)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<31"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<31"); empty = false; } if (*self & (UINT32_C(1)<<30)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<30"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<30"); empty = false; } if (*self & (UINT32_C(1)<<29)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<29"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<29"); empty = false; } if (*self & (UINT32_C(1)<<28)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<28"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<28"); empty = false; } if (*self & (UINT32_C(1)<<27)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<27"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<27"); empty = false; } if (*self & (UINT32_C(1)<<26)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<26"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<26"); empty = false; } if (*self & (UINT32_C(1)<<25)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<25"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<25"); empty = false; } if (*self & (UINT32_C(1)<<24)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<24"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<24"); empty = false; } if (*self & (UINT32_C(1)<<23)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<23"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<23"); empty = false; } if (*self & (UINT32_C(1)<<22)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<22"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<22"); empty = false; } if (*self & (UINT32_C(1)<<21)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<21"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<21"); empty = false; } if (*self & (UINT32_C(1)<<20)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<20"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<20"); empty = false; } if (*self & (UINT32_C(1)<<19)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<19"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<19"); empty = false; } if (*self & (UINT32_C(1)<<18)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<18"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<18"); empty = false; } if (*self & (UINT32_C(1)<<17)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<17"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<17"); empty = false; } if (*self & (UINT32_C(1)<<16)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<16"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<16"); empty = false; } if (*self & (UINT32_C(1)<<15)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<15"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<15"); empty = false; } if (*self & (UINT32_C(1)<<14)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<14"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<14"); empty = false; } if (*self & (UINT32_C(1)<<13)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<13"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<13"); empty = false; } if (*self & (UINT32_C(1)<<12)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<12"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<12"); empty = false; } if (*self & (UINT32_C(1)<<11)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<11"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<11"); empty = false; } if (*self & (UINT32_C(1)<<10)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<10"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<10"); empty = false; } if (*self & (UINT32_C(1)<<9)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<9"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<9"); empty = false; } if (*self & (UINT32_C(1)<<8)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "MTIME_SET"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "MTIME_SET"); empty = false; } if (*self & (UINT32_C(1)<<7)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "ATIME_SET"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "ATIME_SET"); empty = false; } if (*self & (UINT32_C(1)<<6)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "CTIME"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "CTIME"); empty = false; } if (*self & (UINT32_C(1)<<5)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "MTIME"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "MTIME"); empty = false; } if (*self & (UINT32_C(1)<<4)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "ATIME"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "ATIME"); empty = false; } if (*self & (UINT32_C(1)<<3)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "SIZE"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "SIZE"); empty = false; } if (*self & (UINT32_C(1)<<2)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "GID"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "GID"); empty = false; } if (*self & (UINT32_C(1)<<1)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "UID"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "UID"); empty = false; } if (*self & (UINT32_C(1)<<0)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "MODE"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "MODE"); empty = false; } if (empty) - fmt_state_putchar(state, '0'); - fmt_state_putchar(state, ')'); + fmt_print_byte(w, '0'); + fmt_print_byte(w, ')'); } -static void lib9p_lock_type_format(lib9p_lock_type_t *self, struct fmt_state *state) { +[[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_state_puts(state, "RDLCK"); + fmt_print_str(w, "RDLCK"); break; case LIB9P_LOCK_TYPE_WRLCK: - fmt_state_puts(state, "WRLCK"); + fmt_print_str(w, "WRLCK"); break; case LIB9P_LOCK_TYPE_UNLCK: - fmt_state_puts(state, "UNLCK"); + fmt_print_str(w, "UNLCK"); break; default: - fmt_state_printf(state, "%"PRIu8, *self); + fmt_print_base10(w, *self); } } -static void lib9p_lock_flags_format(lib9p_lock_flags_t *self, struct fmt_state *state) { +[[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_state_putchar(state, '('); + fmt_print_byte(w, '('); if (*self & (UINT32_C(1)<<31)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<31"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<31"); empty = false; } if (*self & (UINT32_C(1)<<30)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<30"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<30"); empty = false; } if (*self & (UINT32_C(1)<<29)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<29"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<29"); empty = false; } if (*self & (UINT32_C(1)<<28)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<28"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<28"); empty = false; } if (*self & (UINT32_C(1)<<27)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<27"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<27"); empty = false; } if (*self & (UINT32_C(1)<<26)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<26"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<26"); empty = false; } if (*self & (UINT32_C(1)<<25)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<25"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<25"); empty = false; } if (*self & (UINT32_C(1)<<24)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<24"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<24"); empty = false; } if (*self & (UINT32_C(1)<<23)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<23"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<23"); empty = false; } if (*self & (UINT32_C(1)<<22)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<22"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<22"); empty = false; } if (*self & (UINT32_C(1)<<21)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<21"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<21"); empty = false; } if (*self & (UINT32_C(1)<<20)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<20"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<20"); empty = false; } if (*self & (UINT32_C(1)<<19)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<19"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<19"); empty = false; } if (*self & (UINT32_C(1)<<18)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<18"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<18"); empty = false; } if (*self & (UINT32_C(1)<<17)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<17"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<17"); empty = false; } if (*self & (UINT32_C(1)<<16)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<16"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<16"); empty = false; } if (*self & (UINT32_C(1)<<15)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<15"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<15"); empty = false; } if (*self & (UINT32_C(1)<<14)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<14"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<14"); empty = false; } if (*self & (UINT32_C(1)<<13)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<13"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<13"); empty = false; } if (*self & (UINT32_C(1)<<12)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<12"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<12"); empty = false; } if (*self & (UINT32_C(1)<<11)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<11"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<11"); empty = false; } if (*self & (UINT32_C(1)<<10)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<10"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<10"); empty = false; } if (*self & (UINT32_C(1)<<9)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<9"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<9"); empty = false; } if (*self & (UINT32_C(1)<<8)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<8"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<8"); empty = false; } if (*self & (UINT32_C(1)<<7)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<7"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<7"); empty = false; } if (*self & (UINT32_C(1)<<6)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<6"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<6"); empty = false; } if (*self & (UINT32_C(1)<<5)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<5"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<5"); empty = false; } if (*self & (UINT32_C(1)<<4)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<4"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<4"); empty = false; } if (*self & (UINT32_C(1)<<3)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<3"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<3"); empty = false; } if (*self & (UINT32_C(1)<<2)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "1<<2"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "1<<2"); empty = false; } if (*self & (UINT32_C(1)<<1)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "RECLAIM"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "RECLAIM"); empty = false; } if (*self & (UINT32_C(1)<<0)) { if (!empty) - fmt_state_putchar(state, '|'); - fmt_state_puts(state, "BLOCK"); + fmt_print_byte(w, '|'); + fmt_print_str(w, "BLOCK"); empty = false; } if (empty) - fmt_state_putchar(state, '0'); - fmt_state_putchar(state, ')'); + fmt_print_byte(w, '0'); + fmt_print_byte(w, ')'); } -static void lib9p_lock_status_format(lib9p_lock_status_t *self, struct fmt_state *state) { +[[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_state_puts(state, "SUCCESS"); + fmt_print_str(w, "SUCCESS"); break; case LIB9P_LOCK_STATUS_BLOCKED: - fmt_state_puts(state, "BLOCKED"); + fmt_print_str(w, "BLOCKED"); break; case LIB9P_LOCK_STATUS_ERROR: - fmt_state_puts(state, "ERROR"); + fmt_print_str(w, "ERROR"); break; case LIB9P_LOCK_STATUS_GRACE: - fmt_state_puts(state, "GRACE"); + fmt_print_str(w, "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, " errnum="); - lib9p_errno_format(&self->errnum, 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); - if (is_valid_utf8_without_nul((uint8_t *)self->data, (size_t)self->count)) { -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wformat" -#pragma GCC diagnostic ignored "-Wformat-extra-args" - fmt_state_printf(state, " data=%.*q%s", - (int)(self->count < 50 ? self->count : 50), - (char *)self->data, - self->count < 50 ? "" : "..."); -#pragma GCC diagnostic pop + 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_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, " }"); + 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 -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=["); +[[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_state_putchar(state, ','); - fmt_state_putchar(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); - if (is_valid_utf8_without_nul((uint8_t *)self->data, (size_t)self->count)) { -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wformat" -#pragma GCC diagnostic ignored "-Wformat-extra-args" - fmt_state_printf(state, " data=%.*q%s", - (int)(self->count < 50 ? self->count : 50), - (char *)self->data, - self->count < 50 ? "" : "..."); -#pragma GCC diagnostic pop - } else { - fmt_state_puts(state, " data=<bytedata>"); + 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_state_puts(state, " }"); + 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, " }"); } -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=["); +#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_state_putchar(state, ','); - fmt_state_putchar(state, ' '); - lib9p_s_format(&self->wname[i], state); - } - fmt_state_puts(state, " ]"); - fmt_state_puts(state, " count="); - fmt_state_printf(state, "%"PRIu32, self->count); - if (is_valid_utf8_without_nul((uint8_t *)self->data, (size_t)self->count)) { -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wformat" -#pragma GCC diagnostic ignored "-Wformat-extra-args" - fmt_state_printf(state, " data=%.*q%s", - (int)(self->count < 50 ? self->count : 50), - (char *)self->data, - self->count < 50 ? "" : "..."); -#pragma GCC diagnostic pop + 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_state_puts(state, " data=<bytedata>"); + fmt_print_str(w, " data=<bytedata>"); } - fmt_state_puts(state, " }"); + fmt_print_str(w, " }"); } -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 */ +#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] = { - [LIB9P_VER_unknown] = {.name="unknown", .min_msg_size=9}, #if CONFIG_9P_ENABLE_9P2000 - [LIB9P_VER_9P2000] = {.name="9P2000", .min_msg_size=9}, + [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_msg_size=11}, + [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_msg_size=9}, + [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_msg_size=9}, + [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_msg_size=13}, + [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, \ - .box_as_fmt_formatter = (_box_as_fmt_formatter_fn_t)lo_box_lib9p_msg_##typ##_as_fmt_formatter, \ +#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] = { - [LIB9P_VER_unknown] = { - _MSG(Tversion), - _MSG(Rversion), - _MSG(Rerror), - }, #if CONFIG_9P_ENABLE_9P2000 [LIB9P_VER_9P2000] = { _MSG(Tversion), @@ -8032,6 +7625,13 @@ const struct _lib9p_msg_tentry _lib9p_table_msg[LIB9P_VER_NUM][0x100] = { _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] = { \ @@ -8043,9 +7643,6 @@ const struct _lib9p_msg_tentry _lib9p_table_msg[LIB9P_VER_NUM][0x100] = { } 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), @@ -8150,13 +7747,14 @@ const struct _lib9p_recv_tentry _lib9p_table_Tmsg_recv[LIB9P_VER_NUM][0x80] = { _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] = { - [LIB9P_VER_unknown] = { - _MSG_RECV(Rversion), - _MSG_RECV(Rerror), - }, #if CONFIG_9P_ENABLE_9P2000 [LIB9P_VER_9P2000] = { _MSG_RECV(Rversion), @@ -8266,12 +7864,15 @@ const struct _lib9p_recv_tentry _lib9p_table_Rmsg_recv[LIB9P_VER_NUM][0x80] = { _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] = { - [LIB9P_VER_unknown] = { - _MSG_SEND(Tversion), - }, #if CONFIG_9P_ENABLE_9P2000 [LIB9P_VER_9P2000] = { _MSG_SEND(Tversion), @@ -8376,13 +7977,14 @@ const struct _lib9p_send_tentry _lib9p_table_Tmsg_send[LIB9P_VER_NUM][0x80] = { _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] = { - [LIB9P_VER_unknown] = { - _MSG_SEND(Rversion), - _MSG_SEND(Rerror), - }, #if CONFIG_9P_ENABLE_9P2000 [LIB9P_VER_9P2000] = { _MSG_SEND(Rversion), @@ -8492,9 +8094,16 @@ const struct _lib9p_send_tentry _lib9p_table_Rmsg_send[LIB9P_VER_NUM][0x80] = { _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 */ }; -LM_FLATTEN ssize_t _lib9p_stat_validate(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes, uint32_t *ret_net_size) { +#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) { @@ -8503,3 +8112,4 @@ LM_FLATTEN void _lib9p_stat_unmarshal(struct lib9p_ctx *ctx, uint8_t *net_bytes, 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/core_include/lib9p/_core_generated.h b/lib9p/core_include/lib9p/_core_generated.h index 68eb636..3f87181 100644 --- a/lib9p/core_include/lib9p/_core_generated.h +++ b/lib9p/core_include/lib9p/_core_generated.h @@ -1,18 +1,11 @@ -/* Generated by `lib9p/core.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_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/io.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,17 +109,19 @@ 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 +#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 */ +#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, @@ -159,26 +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)) -#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)) @@ -219,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)) @@ -238,11 +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 */ +#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)) @@ -265,12 +259,10 @@ LO_IMPLEMENTATION_H(fmt_formatter, lib9p_o_t, lib9p_o); #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)) /* 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_L_EPERM ((lib9p_errno_t)(1)) #define LIB9P_ERRNO_L_ENOENT ((lib9p_errno_t)(2)) @@ -408,12 +400,10 @@ LO_IMPLEMENTATION_H(fmt_formatter, lib9p_errno_t, lib9p_errno); #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)) @@ -458,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)) @@ -471,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)) @@ -519,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)) @@ -597,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)) @@ -634,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)) @@ -678,20 +661,18 @@ 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 +#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]] char *utf8; + uint16_t len; + [[gnu::nonstring]] const char *utf8; }; -LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_s, lib9p_s); /* size = 13 ; max_iov = 1 ; max_copy = 13 */ struct lib9p_qid { @@ -699,20 +680,19 @@ struct lib9p_qid { uint32_t vers; uint64_t path; }; -LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_qid, lib9p_qid); +#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 @@ -722,7 +702,6 @@ struct lib9p_msg_Topen { lib9p_fid_t fid; lib9p_o_t mode; }; -LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Topen, lib9p_msg_Topen); #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 @@ -733,58 +712,50 @@ struct lib9p_msg_Tread { uint64_t offset; uint32_t count; }; -LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Tread, lib9p_msg_Tread); /* 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; }; -LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Rread, lib9p_msg_Rread); /* 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; + 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_Twrite, lib9p_msg_Twrite); /* 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; }; -LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Tclunk, lib9p_msg_Tclunk); /* 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; }; -LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Tremove, lib9p_msg_Tremove); /* 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 @@ -793,13 +764,11 @@ struct lib9p_msg_Tstat { lib9p_tag_t tag; lib9p_fid_t fid; }; -LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Tstat, lib9p_msg_Tstat); /* 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 @@ -809,7 +778,6 @@ struct lib9p_msg_Topenfd { lib9p_fid_t fid; lib9p_o_t mode; }; -LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Topenfd, lib9p_msg_Topenfd); #endif /* CONFIG_9P_ENABLE_9P2000_p9p */ #if CONFIG_9P_ENABLE_9P2000_L @@ -818,14 +786,12 @@ struct lib9p_msg_Rlerror { lib9p_tag_t tag; lib9p_errno_t errnum; }; -LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Rlerror, lib9p_msg_Rlerror); /* 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 = 67 ; max_iov = 1 ; max_copy = 67 */ struct lib9p_msg_Rstatfs { @@ -840,7 +806,6 @@ struct lib9p_msg_Rstatfs { 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 { @@ -848,20 +813,17 @@ struct lib9p_msg_Tlopen { lib9p_fid_t fid; lib9p_lo_t flags; }; -LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Tlopen, lib9p_msg_Tlopen); /* 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; }; -LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Treadlink, lib9p_msg_Treadlink); /* size = 19 ; max_iov = 1 ; max_copy = 19 */ struct lib9p_msg_Tgetattr { @@ -869,7 +831,6 @@ struct lib9p_msg_Tgetattr { 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 { @@ -885,26 +846,22 @@ struct lib9p_msg_Tsetattr { uint64_t mtime_sec; uint64_t mtime_nsec; }; -LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Tsetattr, lib9p_msg_Tsetattr); /* 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 { @@ -913,15 +870,13 @@ struct lib9p_msg_Treaddir { uint64_t offset; uint32_t count; }; -LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Treaddir, lib9p_msg_Treaddir); /* 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; }; -LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Rreaddir, lib9p_msg_Rreaddir); /* size = 15 ; max_iov = 1 ; max_copy = 15 */ struct lib9p_msg_Tfsync { @@ -929,38 +884,32 @@ struct lib9p_msg_Tfsync { lib9p_fid_t fid; lib9p_b4_t datasync; }; -LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Tfsync, lib9p_msg_Tfsync); /* 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; }; -LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Rlock, lib9p_msg_Rlock); /* 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 @@ -969,28 +918,24 @@ 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_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u @@ -999,35 +944,33 @@ LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Rswrite, lib9p_msg_Rswrite); /* 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; + 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 file_extension; - lib9p_nuid_t file_owner_n_uid; - lib9p_nuid_t file_owner_n_gid; - lib9p_nuid_t file_last_modified_n_uid; + 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_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 +#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 { @@ -1035,8 +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); +#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_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 */ @@ -1048,17 +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; }; -LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Rauth, lib9p_msg_Rauth); /* 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 */ @@ -1072,24 +1014,23 @@ 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); /* 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_Rattach, lib9p_msg_Rattach); #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 -/* 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 */ +#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 errstr; @@ -1097,9 +1038,8 @@ struct lib9p_msg_Rerror { 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_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 /* min_size = 17 ; exp_size = 481 ; max_size = 1,048,609 ; max_iov = 32 ; max_copy = 49 */ struct lib9p_msg_Twalk { @@ -1109,7 +1049,6 @@ struct lib9p_msg_Twalk { uint16_t nwname; struct lib9p_s *wname; }; -LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Twalk, lib9p_msg_Twalk); /* min_size = 9 ; exp_size = 217 ; max_size = 217 ; max_iov = 1 ; max_copy = 217 */ struct lib9p_msg_Rwalk { @@ -1117,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 @@ -1127,7 +1065,6 @@ 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 { @@ -1137,7 +1074,6 @@ struct lib9p_msg_Tcreate { lib9p_dm_t perm; lib9p_o_t mode; }; -LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Tcreate, lib9p_msg_Tcreate); /* size = 24 ; max_iov = 1 ; max_copy = 24 */ struct lib9p_msg_Rcreate { @@ -1145,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 @@ -1156,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 @@ -1166,7 +1100,6 @@ 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 { @@ -1177,7 +1110,6 @@ struct lib9p_msg_Tlcreate { lib9p_mode_t mode; lib9p_nuid_t gid; }; -LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Tlcreate, lib9p_msg_Tlcreate); /* size = 24 ; max_iov = 1 ; max_copy = 24 */ struct lib9p_msg_Rlcreate { @@ -1185,7 +1117,6 @@ 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 { @@ -1195,14 +1126,12 @@ struct lib9p_msg_Tsymlink { struct lib9p_s symtgt; lib9p_nuid_t gid; }; -LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Tsymlink, lib9p_msg_Tsymlink); /* 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 { @@ -1214,14 +1143,12 @@ struct lib9p_msg_Tmknod { uint32_t minor; lib9p_nuid_t gid; }; -LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Tmknod, lib9p_msg_Tmknod); /* 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 { @@ -1230,14 +1157,12 @@ struct lib9p_msg_Trename { 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); /* size = 160 ; max_iov = 1 ; max_copy = 160 */ struct lib9p_msg_Rgetattr { @@ -1263,7 +1188,6 @@ 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 { @@ -1272,7 +1196,6 @@ struct lib9p_msg_Txattrwalk { 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 { @@ -1282,7 +1205,6 @@ struct lib9p_msg_Txattrcreate { uint64_t attr_size; uint32_t flags; }; -LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Txattrcreate, lib9p_msg_Txattrcreate); /* min_size = 38 ; exp_size = 65 ; max_size = 65,573 ; max_iov = 2 ; max_copy = 38 */ struct lib9p_msg_Tlock { @@ -1295,7 +1217,6 @@ struct lib9p_msg_Tlock { uint32_t proc_id; struct lib9p_s client_id; }; -LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Tlock, lib9p_msg_Tlock); /* min_size = 34 ; exp_size = 61 ; max_size = 65,569 ; max_iov = 2 ; max_copy = 34 */ struct lib9p_msg_Tgetlock { @@ -1307,7 +1228,6 @@ struct lib9p_msg_Tgetlock { 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 { @@ -1318,7 +1238,6 @@ struct lib9p_msg_Rgetlock { uint32_t proc_id; struct lib9p_s client_id; }; -LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Rgetlock, lib9p_msg_Rgetlock); /* min_size = 17 ; exp_size = 44 ; max_size = 65,552 ; max_iov = 2 ; max_copy = 17 */ struct lib9p_msg_Tlink { @@ -1327,7 +1246,6 @@ struct lib9p_msg_Tlink { lib9p_fid_t fid; struct lib9p_s name; }; -LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Tlink, lib9p_msg_Tlink); /* min_size = 21 ; exp_size = 48 ; max_size = 65,556 ; max_iov = 3 ; max_copy = 21 */ struct lib9p_msg_Tmkdir { @@ -1337,14 +1255,12 @@ struct lib9p_msg_Tmkdir { lib9p_mode_t mode; lib9p_nuid_t gid; }; -LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Tmkdir, lib9p_msg_Tmkdir); /* 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 { @@ -1354,7 +1270,6 @@ struct lib9p_msg_Trenameat { 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 { @@ -1363,7 +1278,6 @@ struct lib9p_msg_Tunlinkat { 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 @@ -1374,18 +1288,16 @@ struct lib9p_msg_Tsread { 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; + lib9p_tag_t tag; + uint32_t fid; + uint16_t nwname; + struct lib9p_s *wname; + uint32_t count; + const void *data; }; -LO_IMPLEMENTATION_H(fmt_formatter, struct lib9p_msg_Tswrite, lib9p_msg_Tswrite); #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 @@ -1397,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 */ @@ -1408,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 *****************************************************************/ @@ -1421,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 @@ -1441,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/core_include/lib9p/core.h b/lib9p/core_include/lib9p/core.h index ff822ed..84ecd0f 100644 --- a/lib9p/core_include/lib9p/core.h +++ b/lib9p/core_include/lib9p/core.h @@ -7,17 +7,17 @@ #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/_core_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 ******************************************************************/ @@ -31,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); @@ -43,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 ***************************************************/ @@ -76,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 L_EOPNOTSUPP: message is an R-message - * @errno L_EOPNOTSUPP: message has unknown type - * @errno L_EBADMSG: message is wrong size for content - * @errno L_EBADMSG: message contains invalid UTF-8 - * @errno L_EBADMSG: message contains a bitfield with unknown bits - * @errno L_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`. @@ -118,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 L_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, size_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`. @@ -190,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 L_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_CORE_H_ */ diff --git a/lib9p/core_tables.c b/lib9p/core_tables.c deleted file mode 100644 index a04a5f0..0000000 --- a/lib9p/core_tables.c +++ /dev/null @@ -1,188 +0,0 @@ -/* lib9p/core_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 "core_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, LIB9P_ERRNO_L_EBADMSG, "message is impossibly short"); - uint8_t typ = net_bytes[4]; - if (typ % 2 != xxx_low_typ_bit) - return lib9p_errorf(ctx, LIB9P_ERRNO_L_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, LIB9P_ERRNO_L_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]; - assert(tentry.unmarshal); - - 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]; - assert(tentry.marshal); - - 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, size_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 = (size_t)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/core_tables.h b/lib9p/core_tables.h index 2c5f745..94cdbe1 100644 --- a/lib9p/core_tables.h +++ b/lib9p/core_tables.h @@ -13,20 +13,21 @@ 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_CORE_TABLES_H_ */ diff --git a/lib9p/core_utf8.h b/lib9p/core_utf8.h deleted file mode 100644 index 636d4eb..0000000 --- a/lib9p/core_utf8.h +++ /dev/null @@ -1,34 +0,0 @@ -/* lib9p/core_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_CORE_UTF8_H_ -#define _LIB9P_CORE_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_CORE_UTF8_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 c9432c9..de902dd 100644 --- a/lib9p/idl/1992-9P0.9p.wip +++ b/lib9p/idl/1992-9P0.9p.wip @@ -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/2002-9P2000.9p b/lib9p/idl/2002-9P2000.9p index 13393c6..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. diff --git a/lib9p/idl/2005-9P2000.u.9p b/lib9p/idl/2005-9P2000.u.9p index 1d630f9..446385c 100644 --- a/lib9p/idl/2005-9P2000.u.9p +++ b/lib9p/idl/2005-9P2000.u.9p @@ -17,13 +17,13 @@ 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 += "errnum[errno]" diff --git a/lib9p/idl/2010-9P2000.L.9p.gen b/lib9p/idl/2010-9P2000.L.9p.gen index cb32585..f0bdb6b 100755 --- a/lib9p/idl/2010-9P2000.L.9p.gen +++ b/lib9p/idl/2010-9P2000.L.9p.gen @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # lib9p/idl/2010-9P2000.L.9p.gen - Generate definitions of 9P2000.L messages import sys diff --git a/lib9p/srv.c b/lib9p/srv.c index 7e2bab0..3e4de91 100644 --- a/lib9p/srv.c +++ b/lib9p/srv.c @@ -4,23 +4,16 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ -#include <alloca.h> -#include <inttypes.h> /* for PRI* */ -#include <limits.h> /* for SSIZE_MAX, not set by newlib */ #include <stddef.h> /* for size_t */ -#include <stdlib.h> /* for malloc() */ #include <string.h> /* for memcpy() */ -#ifndef SSIZE_MAX -#define SSIZE_MAX (SIZE_MAX >> 1) -#endif #include <libcr/coroutine.h> -#include <libcr_ipc/chan.h> #include <libcr_ipc/mutex.h> +#include <libhw/generic/net.h> +#include <libmisc/alloc.h> #include <libmisc/assert.h> #include <libmisc/endian.h> #include <libmisc/map.h> -#include <libhw/generic/net.h> #define LOG_NAME 9P_SRV #include <libmisc/log.h> @@ -28,6 +21,8 @@ #define IMPLEMENTATION_FOR_LIB9P_SRV_H YES #include <lib9p/srv.h> +#include "srv_errno.h" + /* config *********************************************************************/ #include "config.h" @@ -39,24 +34,35 @@ #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); } -void lib9p_srv_acknowledge_flush(struct lib9p_srv_ctx *ctx) { - assert(ctx); - assert(_lib9p_srv_flushch_can_send(&ctx->flushch)); - lib9p_error(&ctx->basectx, LIB9P_ERRNO_L_ECANCELED, "request canceled by flush"); - _lib9p_srv_flushch_send(&ctx->flushch, true); -} +#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, @@ -86,11 +92,12 @@ struct srv_pathinfo { #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 { srv_path_t path; - struct lib9p_srv_authinfo *authinfo; + struct lib9p_srv_userid *user; uint8_t flags; enum srv_filetype type; union { @@ -101,6 +108,7 @@ 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; @@ -108,6 +116,7 @@ struct srv_fidinfo { } auth; }; }; +DECLARE_ERROR_OR_(struct srv_fidinfo *, srv_fidinfop); /* contexts ************************************** * @@ -123,7 +132,7 @@ 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; }; @@ -138,7 +147,6 @@ struct srv_sess { enum lib9p_version version; uint32_t max_msg_size; /* mutable */ - bool initialized; bool closing; struct srv_pathmap paths; /* srv_path_t => `lib9p_srv_file` + metadata */ struct srv_fidmap fids; /* lib9p_fid_t => `lib9p_srv_{fio,dio}` + metadata */ @@ -149,7 +157,7 @@ struct srv_sess { /* utilities for the above types **********************************************/ -static inline enum srv_filetype srv_qid_filetype(struct lib9p_qid qid) { +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) @@ -157,44 +165,66 @@ static inline enum srv_filetype srv_qid_filetype(struct lib9p_qid qid) { return SRV_FILETYPE_FILE; } -static inline bool srv_check_perm(struct srv_req *ctx, struct lib9p_stat *stat, uint8_t action) { +[[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->file_mode & 07); + uint8_t mode = (uint8_t)(stat->mode & 07); return mode & action; } -struct lib9p_srv_authinfo *srv_authinfo_new(struct lib9p_s uname, lib9p_nuid_t uid) { - struct lib9p_srv_authinfo *ret = malloc(sizeof(struct lib9p_srv_authinfo) + uname.len); - if (!ret) - return NULL; - ret->uid = uid; - ret->uname.len = uname.len; - ret->uname.utf8 = (void *)&ret[1]; - memcpy(ret->uname.utf8, uname.utf8, uname.len); +[[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 -struct lib9p_srv_authinfo *srv_authinfo_decref(struct lib9p_srv_authinfo *authinfo) { - assert(authinfo); - assert(authinfo->refcount); - authinfo->refcount--; - if (!authinfo->refcount) { - free(authinfo); - return NULL; - } - return authinfo; +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; } -struct lib9p_srv_authinfo *srv_authinfo_incref(struct lib9p_srv_authinfo *authinfo) { - assert(authinfo); - authinfo->refcount++; - return authinfo; +static struct lib9p_srv_userid *srv_userid_incref(struct lib9p_srv_userid *userid) { + assert(userid); + userid->refcount++; + return userid; } /** @@ -204,9 +234,10 @@ struct lib9p_srv_authinfo *srv_authinfo_incref(struct lib9p_srv_authinfo *authin * * Returns a pointer to the stored pathinfo. */ -static inline struct srv_pathinfo *srv_path_save(struct srv_req *ctx, - lo_interface lib9p_srv_file file, - srv_path_t parent_path) { +[[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)); @@ -238,7 +269,7 @@ static inline struct srv_pathinfo *srv_path_save(struct srv_req *ctx, * Decrement the path's gc_refcount, and trigger garbage collection as * appropriate. */ -static inline void srv_path_decref(struct srv_req *ctx, srv_path_t path) { +static void srv_path_decref(struct srv_req *ctx, srv_path_t path) { assert(ctx); for (;;) { @@ -256,32 +287,23 @@ static inline void srv_path_decref(struct srv_req *ctx, srv_path_t path) { } } -static inline void srv_fid_del(struct srv_req *ctx, lib9p_fid_t fid, bool remove) { - struct srv_fidinfo *fidinfo = map_load(&ctx->parent_sess->fids, fid); +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) { - if (pathinfo->parent_dir == fidinfo->path) { - lib9p_errorf(&ctx->basectx, - LIB9P_ERRNO_L_EBUSY, "cannot remove root"); - goto clunk; - } - 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); - if (!srv_check_perm(ctx, &parent_stat, 0b010)) { - lib9p_error(&ctx->basectx, - LIB9P_ERRNO_L_EACCES, "you do not have write permission on the parent directory"); - goto clunk; - } - LO_CALL(pathinfo->file, remove, ctx); - } + if (remove) + err = LO_CALL(pathinfo->file, remove, ctx); - clunk: if (fidinfo->flags & FIDFLAG_OPEN) { switch (fidinfo->type) { case SRV_FILETYPE_DIR: @@ -296,9 +318,13 @@ static inline void srv_fid_del(struct srv_req *ctx, lib9p_fid_t fid, bool remove } pathinfo->io_refcount--; } - fidinfo->authinfo = srv_authinfo_decref(fidinfo->authinfo); + 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; } /** @@ -306,7 +332,8 @@ static inline void srv_fid_del(struct srv_req *ctx, lib9p_fid_t fid, bool remove * pathinfo->gc_refcount has already been incremented; does *not* * decrement it on failure. */ -static inline struct srv_fidinfo *srv_fid_store(struct srv_req *ctx, lib9p_fid_t fid, struct srv_pathinfo *pathinfo, bool overwrite) { +[[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); @@ -323,22 +350,20 @@ static inline struct srv_fidinfo *srv_fid_store(struct srv_req *ctx, lib9p_fid_t assert(old_fidinfo->type == SRV_FILETYPE_DIR); assert(old_fidinfo->flags == 0); - old_fidinfo->authinfo = srv_authinfo_decref(old_fidinfo->authinfo); + old_fidinfo->user = srv_userid_decref(old_fidinfo->user); srv_path_decref(ctx, old_fidinfo->path); map_del(&ctx->parent_sess->fids, fid); } else { - lib9p_error(&ctx->basectx, - LIB9P_ERRNO_L_EBADF, "FID already in use"); - return NULL; + 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), - .authinfo = srv_authinfo_incref(ctx->authinfo), + .path = qid.path, + .type = srv_qid_filetype(qid), + .user = srv_userid_incref(ctx->user), }); assert(fidinfo); - return fidinfo; + return ERROR_NEW_VAL(srv_fidinfop, fidinfo); } /* base utilities *************************************************************/ @@ -349,82 +374,81 @@ static void srv_msglog(struct srv_req *req, enum lib9p_msg_type typ, void *hostm srv->msglog(req, typ, hostmsg); return; } - /* It sucks that %v trips -Wformat and -Wformat-extra-args - * 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" - infof("%c %v", typ % 2 ? '<' : '>', lo_box_lib9p_msg_as_fmt_formatter(&req->basectx, typ, hostmsg)); -#pragma GCC diagnostic pop + log_infoln(typ % 2 ? "< " : "> ", (lib9p_msg, &req->basectx, typ, hostmsg)); } -static ssize_t srv_write_Rmsg(struct srv_req *req, struct lib9p_Rmsg_send_buf *resp) { - ssize_t r; +#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); + 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; } -#define srv_nonrespond_errorf errorf - -static void srv_respond_error(struct srv_req *req) { -#if CONFIG_9P_ENABLE_9P2000_u - assert(req->basectx.err_num); -#endif - assert(req->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, - .errstr = lib9p_strn(req->basectx.err_msg, - CONFIG_9P_MAX_ERR_SIZE), + .errstr = lib9p_str(error_msg(err)), #if CONFIG_9P_ENABLE_9P2000_u - .errnum = req->basectx.err_num, + .errnum = libmisc_to_linuxgeneric_errno(err.num), #endif }; struct srv_sess *sess = req->parent_sess; - /* XXX: This assumes that a version's min_msg_size is the - * Rerror overhead. That's true for the current - * implementation of core_gen, but is a sneaky assumption. */ - uint32_t overhead = lib9p_version_min_msg_size(sess->version); + uint32_t overhead = lib9p_version_min_Rerror_size(sess->version); /* Truncate the error-string if necessary to avoid needing to - * return LIB9P_ERRNO_L_ERANGE. */ + * return E_POSIX_ERANGE. */ if (((uint32_t)host.errstr.len) + overhead > sess->max_msg_size) host.errstr.len = sess->max_msg_size - overhead; - struct lib9p_Rmsg_send_buf net; + error marshal_err = srv_write_Rmsg(req, LIB9P_TYP_Rerror, &host); + assert(ERROR_IS_NULL(marshal_err)); - lib9p_Rmsg_marshal(&req->basectx, - LIB9P_TYP_Rerror, &host, - &net); - - srv_msglog(req, LIB9P_TYP_Rerror, &host); - r = srv_write_Rmsg(req, &net); - if (r < 0) - srv_nonrespond_errorf("write: %s", net_strerror(-r)); + error_cleanup(&err); } /* read coroutine *************************************************************/ -static inline bool srv_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) { - srv_nonrespond_errorf("read: %s", net_strerror(-r)); - return true; - } else if (r == 0) { - if (*done != 0) - srv_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; } @@ -437,114 +461,126 @@ void lib9p_srv_accept_and_read_loop(struct lib9p_srv *srv, lo_interface net_stre srv->readers++; for (;;) { - lo_interface net_stream_conn conn = LO_CALL(listener, accept); - if (LO_IS_NULL(conn)) { - srv_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_rpc_send_req(&srv->reqch, NULL); return; } - lib9p_srv_read(srv, conn); + lib9p_srv_read(srv, r.net_stream_conn); } } -static void handle_message(struct srv_req *ctx); +static void lib9p_srv_worker_Tversion(struct srv_req *ctx); 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 = { + struct srv_conn _conn_ = { .parent_srv = srv, .fd = _conn, .reader = cr_getcid(), }; - struct srv_sess sess = { - .parent_conn = &conn, - .version = LIB9P_VER_unknown, + 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, - .initialized = false, }; + 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)) + if (srv_read_exactly(conn->fd, buf, 4, &done)) break; size_t goal = uint32le_decode(buf); if (goal < 7) { - srv_nonrespond_errorf("T-message is impossibly small"); + srv_nonrespond_error("T-message is impossibly small"); break; } - if (srv_read_exactly(conn.fd, buf, 7, &done)) + if (srv_read_exactly(conn->fd, buf, 7, &done)) break; - struct srv_req req = { + *req = (struct srv_req){ .basectx = { - .version = sess.version, - .max_msg_size = sess.max_msg_size, + .version = sess->version, + .max_msg_size = sess->max_msg_size, }, - .parent_sess = &sess, + .parent_sess = sess, .tag = uint16le_decode(&buf[5]), .net_bytes = buf, }; - if (goal > sess.max_msg_size) { - lib9p_errorf(&req.basectx, - LIB9P_ERRNO_L_EMSGSIZE, "T-message larger than %s limit (%zu > %"PRIu32")", - sess.initialized ? "negotiated" : "server", - goal, - sess.max_msg_size); - srv_respond_error(&req); + 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; } - req.net_bytes = malloc(goal); - assert(req.net_bytes); - memcpy(req.net_bytes, buf, done); - if (srv_read_exactly(conn.fd, req.net_bytes, goal, &done)) { - free(req.net_bytes); + 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) + if (req->net_bytes[4] == LIB9P_TYP_Tversion) /* ...in this coroutine for Tversion, */ - handle_message(&req); + lib9p_srv_worker_Tversion(req); else /* ...but usually in another coroutine. */ - _lib9p_srv_reqch_send_req(&srv->_reqch, &req); + cr_rpc_send_req(&srv->reqch, req); } - if (map_len(&sess.reqs) == 0) - io_close(conn.fd); - else { - io_close_read(conn.fd); - sess.closing = true; + + /* 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); - io_close_write(conn.fd); } - - assert(map_len(&sess.reqs) == 0); - map_free(&sess.reqs); - - MAP_FOREACH(&sess.fids, fid, fidinfo) { - struct srv_req req = { - .basectx = { - .version = sess.version, - .max_msg_size = sess.max_msg_size, - }, - .parent_sess = &sess, - }; - srv_fid_del(&req, fid, false); - if (lib9p_ctx_has_error(&req.basectx)) - errorf("clunk: %.*s", CONFIG_9P_MAX_ERR_SIZE, req.basectx.err_msg); + 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); + } } - map_free(&sess.fids); + assert(map_len(&sess->fids) == 0); + map_free(&sess->fids); - assert(map_len(&sess.paths) == 0); - map_free(&sess.paths); + /* Cleanup `sess->paths`. */ + assert(map_len(&sess->paths) == 0); + map_free(&sess->paths); } /* write coroutine ************************************************************/ @@ -560,10 +596,10 @@ void lib9p_srv_worker_loop(struct lib9p_srv *srv) { 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_rpc_send_resp(rpc_handle, 0); return; } /* Copy the request from the reader coroutine's @@ -574,25 +610,18 @@ void lib9p_srv_worker_loop(struct lib9p_srv *srv) { 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.flushch)) - _lib9p_srv_flushch_send(&req.flushch, false); - map_del(&req.parent_sess->reqs, req.tag); - if (req.parent_sess->closing && !map_len(&req.parent_sess->reqs)) - cr_unpause(req.parent_sess->parent_conn->reader); + lib9p_srv_worker(&req); } } #define _HANDLER_PROTO(typ) \ static void handle_T##typ(struct srv_req *, \ - struct lib9p_msg_T##typ *, \ - struct lib9p_msg_R##typ *) + struct lib9p_msg_T##typ *) _HANDLER_PROTO(version); +#if _LIB9P_ENABLE_stat _HANDLER_PROTO(auth); _HANDLER_PROTO(attach); _HANDLER_PROTO(flush); @@ -605,6 +634,7 @@ _HANDLER_PROTO(clunk); _HANDLER_PROTO(remove); _HANDLER_PROTO(stat); _HANDLER_PROTO(wstat); +#endif #if CONFIG_9P_ENABLE_9P2000_p9p _HANDLER_PROTO(openfd); #endif @@ -614,82 +644,139 @@ _HANDLER_PROTO(sread); _HANDLER_PROTO(swrite); #endif -typedef void (*tmessage_handler)(struct 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, +void lib9p_srv_worker(struct srv_req *ctx) { + [[gnu::cleanup(heap_cleanup)]] void *host_req = NULL; + + /* 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->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 - [LIB9P_TYP_Topenfd] = (tmessage_handler)handle_Topenfd, + CASE(Topenfd); #endif #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, + CASE(Tsession); + CASE(Tsread); + CASE(Tswrite); #endif -}; +#undef CASE + default: + assert_notreached("lib9p_Tmsg_validate() should have rejected unknown typ"); + } + 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 void handle_message(struct srv_req *ctx) { - uint8_t *host_req = NULL; - uint8_t host_resp[CONFIG_9P_SRV_MAX_HOSTMSG_SIZE]; +static void lib9p_srv_worker_Tversion(struct srv_req *ctx) { + [[gnu::cleanup(heap_cleanup)]] void *host_req = NULL; - /* Unmarshal it. */ - ssize_t host_size = lib9p_Tmsg_validate(&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->basectx, ctx->net_bytes, &typ, host_req); srv_msglog(ctx, typ, host_req); - /* Handle it. */ - tmessage_handlers[typ](ctx, (void *)host_req, (void *)host_resp); - - write: - if (lib9p_ctx_has_error(&ctx->basectx)) - srv_respond_error(ctx); - else { - struct lib9p_Rmsg_send_buf net_resp; - if (lib9p_Rmsg_marshal(&ctx->basectx, - typ+1, host_resp, - &net_resp)) - goto write; - srv_msglog(ctx, typ+1, &host_resp); - srv_write_Rmsg(ctx, &net_resp); + /* 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 (host_req) - free(host_req); + if (ctx->parent_sess->closing && !map_len(&ctx->parent_sess->reqs)) + cr_unpause(ctx->parent_sess->parent_conn->reader); free(ctx->net_bytes); } +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 { + error: + srv_respond_error(ctx, err); + } + } else { + assert(host_resp); + err = srv_write_Rmsg(ctx, resp_typ, host_resp); + if (!ERROR_IS_NULL(err)) + goto error; + } + 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(ctx, req, resp) do { \ - assert(ctx); \ - assert(req); \ - assert(resp); \ - resp->tag = req->tag; \ - } while (0) +#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 srv_req *ctx, - struct lib9p_msg_Tversion *req, - struct lib9p_msg_Rversion *resp) { - srv_handler_common(ctx, req, resp); + 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' && @@ -699,7 +786,9 @@ static void handle_Tversion(struct 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))) @@ -715,20 +804,23 @@ static void handle_Tversion(struct 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->basectx, - LIB9P_ERRNO_L_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.version = lib9p_str(lib9p_version_str(version)); #if CONFIG_9P_ENABLE_9P2000_p9p if (version == LIB9P_VER_9P2000_p9p) - resp->version = lib9p_str("9P2000"); + resp.version = lib9p_str("9P2000"); #endif - resp->max_msg_size = (CONFIG_9P_SRV_MAX_MSG_SIZE < req->max_msg_size) + resp.max_msg_size = (CONFIG_9P_SRV_MAX_MSG_SIZE < req->max_msg_size) ? CONFIG_9P_SRV_MAX_MSG_SIZE : req->max_msg_size; @@ -736,112 +828,110 @@ static void handle_Tversion(struct srv_req *ctx, 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) * map_len(&ctx->parent_sess->reqs)); + 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)->flushch), &flushed); + enum _lib9p_srv_flush_result flushed; + args[i++] = CR_SELECT_RECV(&((*reqpp)->flush_ch), &flushed); } assert(i == map_len(&ctx->parent_sess->reqs)); - cr_select_v(i, list); + cr_select_v(i, args); } + free(args); } - if (map_len(&ctx->parent_sess->fids)) { - /* Close all FIDs. */ - 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->max_msg_size = resp.max_msg_size; + + tversion_return: + srv_respond(ctx, version, &resp, err); } +#if _LIB9P_ENABLE_stat static void handle_Tauth(struct srv_req *ctx, - struct lib9p_msg_Tauth *req, - struct lib9p_msg_Rauth *resp) { - srv_handler_common(ctx, req, resp); + struct lib9p_msg_Tauth *req) { + srv_handler_common(ctx, auth, req); struct lib9p_srv *srv = ctx->parent_sess->parent_conn->parent_srv; if (!srv->auth) { - lib9p_error(&ctx->basectx, - LIB9P_ERRNO_L_EOPNOTSUPP, "authentication not required"); - return; + err = error_new(E_POSIX_EOPNOTSUPP, "authentication not required"); + goto tauth_return; } - ctx->authinfo = srv_authinfo_new(req->uname, req->n_uid); + ctx->user = srv_userid_new(req->uname, req->unum); - srv->auth(ctx, req->aname); + err = srv->auth(ctx, req->aname); + if (!ERROR_IS_NULL(err)) + goto tauth_return; - lib9p_error(&ctx->basectx, - LIB9P_ERRNO_L_EOPNOTSUPP, "TODO: auth not implemented"); + err = error_new(E_POSIX_EOPNOTSUPP, "TODO: auth not implemented"); - if (lib9p_ctx_has_error(&ctx->basectx)) - ctx->authinfo = srv_authinfo_decref(ctx->authinfo); + 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 srv_req *ctx, - struct lib9p_msg_Tattach *req, - struct lib9p_msg_Rattach *resp) { - srv_handler_common(ctx, req, resp); + struct lib9p_msg_Tattach *req) { + srv_handler_common(ctx, attach, req); if (req->fid == LIB9P_FID_NOFID) { - lib9p_error(&ctx->basectx, - LIB9P_ERRNO_L_EBADF, "cannot assign to NOFID"); - return; + 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 srv_fidinfo *afid = map_load(&ctx->parent_sess->fids, req->afid); if (!afid) - lib9p_error(&ctx->basectx, - LIB9P_ERRNO_L_EACCES, "FID provided as auth-file is not a valid FID"); + err = error_new(E_POSIX_EACCES, "FID provided as auth-file is not a valid FID"); else if (afid->type != SRV_FILETYPE_AUTH) - lib9p_error(&ctx->basectx, - LIB9P_ERRNO_L_EACCES, "FID provided as auth-file is not an auth-file"); - else if (!lib9p_str_eq(afid->authinfo->uname, req->uname)) - lib9p_errorf(&ctx->basectx, - LIB9P_ERRNO_L_EACCES, "FID provided as auth-file is for user=\"%.*s\" and cannot be used for user=\"%.*s\"", - afid->authinfo->uname.len, afid->authinfo->uname.utf8, - req->uname.len, req->uname.utf8); -#if CONFIG_9P_ENABLE_9P2000_u - else if (afid->authinfo->uid != req->n_uid) - lib9p_errorf(&ctx->basectx, - LIB9P_ERRNO_L_EACCES, "FID provided as auth-file is for user=%"PRIu32" and cannot be used for user=%"PRIu32, - afid->authinfo->uid, req->n_uid); + 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)) - lib9p_errorf(&ctx->basectx, - LIB9P_ERRNO_L_EACCES, "FID provided as auth-file is for tree=\"%.*s\" and cannot be used for tree=\"%.*s\"", - afid->auth.aname.len, afid->auth.aname.utf8, - req->aname.len, req->aname.utf8); + 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) - lib9p_error(&ctx->basectx, - LIB9P_ERRNO_L_EACCES, "FID provided as auth-file has not completed authentication"); - if (lib9p_ctx_has_error(&ctx->basectx)) - return; - ctx->authinfo = srv_authinfo_incref(afid->authinfo); + 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->basectx, - LIB9P_ERRNO_L_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; } - ctx->authinfo = srv_authinfo_new(req->uname, req->n_uid); + ctx->user = srv_userid_new(req->uname, req->unum); } /* 1. File object */ - lo_interface lib9p_srv_file root_file = srv->rootdir(ctx, req->aname); - assert(LO_IS_NULL(root_file) == lib9p_ctx_has_error(&ctx->basectx)); - if (lib9p_ctx_has_error(&ctx->basectx)) { - ctx->authinfo = srv_authinfo_decref(ctx->authinfo); - 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(srv_qid_filetype(root_qid) == SRV_FILETYPE_DIR); @@ -850,134 +940,151 @@ static void handle_Tattach(struct srv_req *ctx, struct srv_pathinfo *root_pathinfo = srv_path_save(ctx, root_file, root_qid.path); /* 3. fidinfo */ - if (!srv_fid_store(ctx, req->fid, root_pathinfo, false)) { + 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); - ctx->authinfo = srv_authinfo_decref(ctx->authinfo); - return; + goto tattach_return; } - ctx->authinfo = srv_authinfo_decref(ctx->authinfo); - 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 srv_req *ctx, - struct lib9p_msg_Tflush *req, - struct lib9p_msg_Rflush *resp) { - srv_handler_common(ctx, req, resp); + 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) - _lib9p_srv_flushch_recv(&((*oldreqp)->flushch)); + 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 srv_req *ctx, - struct lib9p_msg_Twalk *req, - struct lib9p_msg_Rwalk *resp) { - srv_handler_common(ctx, req, resp); + struct lib9p_msg_Twalk *req) { + srv_handler_common(ctx, walk, req); if (req->newfid == LIB9P_FID_NOFID) { - lib9p_error(&ctx->basectx, - LIB9P_ERRNO_L_EBADF, "cannot assign to NOFID"); - return; + err = error_new(E_POSIX_EBADF, "cannot assign to NOFID"); + goto twalk_return; } struct srv_fidinfo *fidinfo = map_load(&ctx->parent_sess->fids, req->fid); if (!fidinfo) { - lib9p_errorf(&ctx->basectx, - LIB9P_ERRNO_L_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) { - lib9p_error(&ctx->basectx, - LIB9P_ERRNO_L_EALREADY, "cannot walk on FID open for I/O"); - return; + err = error_new(E_POSIX_EALREADY, "cannot walk on FID open for I/O"); + goto twalk_return; } - ctx->authinfo = srv_authinfo_incref(fidinfo->authinfo); + ctx->user = srv_userid_incref(fidinfo->user); 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) { - lib9p_error(&ctx->basectx, - LIB9P_ERRNO_L_ENOTDIR, "not a directory"); + 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(".."))) { + 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 { - lo_interface lib9p_srv_file member_file = LO_CALL(pathinfo->file, dwalk, ctx, req->wname[resp->nwqid]); - assert(LO_IS_NULL(member_file) == lib9p_ctx_has_error(&ctx->basectx)); - if (lib9p_ctx_has_error(&ctx->basectx)) + 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; - new_pathinfo = srv_path_save(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 (new_pathinfo->type == SRV_FILETYPE_DIR) { - struct lib9p_stat stat = LO_CALL(new_pathinfo->file, stat, ctx); - if (lib9p_ctx_has_error(&ctx->basectx)) + 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_check_perm(ctx, &stat, 0b001)) { - lib9p_error(&ctx->basectx, - LIB9P_ERRNO_L_EACCES, "you do not have execute permission on that directory"); + } 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_path_decref(ctx, LO_CALL(pathinfo->file, qid).path); pathinfo = new_pathinfo; } - if (resp->nwqid == req->nwname) { - if (!srv_fid_store(ctx, req->newfid, pathinfo, req->newfid == req->fid)) + 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); + } } else { - assert(lib9p_ctx_has_error(&ctx->basectx)); + assert(!ERROR_IS_NULL(err)); srv_path_decref(ctx, LO_CALL(pathinfo->file, qid).path); - if (resp->nwqid > 0) - lib9p_ctx_clear_error(&ctx->basectx); + if (resp.nwqid > 0) + error_cleanup(&err); } - ctx->authinfo = srv_authinfo_decref(ctx->authinfo); + 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 srv_req *ctx, - struct lib9p_msg_Topen *req, - struct lib9p_msg_Ropen *resp) { - srv_handler_common(ctx, req, resp); + struct lib9p_msg_Topen *req) { + srv_handler_common(ctx, open, req); /* Check that the FID is valid for this. */ struct srv_fidinfo *fidinfo = map_load(&ctx->parent_sess->fids, req->fid); if (!fidinfo) { - lib9p_errorf(&ctx->basectx, - LIB9P_ERRNO_L_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->basectx, - LIB9P_ERRNO_L_EALREADY, "FID is already open"); - return; + err = error_new(E_POSIX_EALREADY, "FID is already open"); + goto topen_return; } 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->basectx, - LIB9P_ERRNO_L_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->authinfo = srv_authinfo_incref(fidinfo->authinfo); + ctx->user = srv_userid_incref(fidinfo->user); /* Variables. */ lib9p_o_t reqmode = req->mode; @@ -989,28 +1096,29 @@ static void handle_Topen(struct srv_req *ctx, if (reqmode & LIB9P_O_RCLOSE) { 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); - if (lib9p_ctx_has_error(&ctx->basectx)) + 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; - lib9p_stat_assert(parent_stat); - if (!srv_check_perm(ctx, &parent_stat, 0b010)) { - lib9p_error(&ctx->basectx, - LIB9P_ERRNO_L_EACCES, "permission denied to remove-on-close"); + } 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); - if (lib9p_ctx_has_error(&ctx->basectx)) + struct lib9p_srv_stat stat; + err = LO_CALL(pathinfo->file, stat, ctx, &stat); + if (!ERROR_IS_NULL(err)) goto topen_return; - lib9p_stat_assert(stat); - if ((stat.file_mode & LIB9P_DM_EXCL) && pathinfo->io_refcount) { - lib9p_error(&ctx->basectx, - LIB9P_ERRNO_L_EEXIST, "exclusive file is already opened"); + 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) { @@ -1031,9 +1139,8 @@ static void handle_Topen(struct srv_req *ctx, rd = true; break; } - if (!srv_check_perm(ctx, &stat, perm_bits)) { - lib9p_error(&ctx->basectx, - LIB9P_ERRNO_L_EACCES, "permission denied"); + if (!srv_stat_check_perm(ctx, &stat, perm_bits)) { + err = error_new(E_POSIX_EACCES, "permission denied"); goto topen_return; } @@ -1042,23 +1149,29 @@ static void handle_Topen(struct srv_req *ctx, struct lib9p_qid qid; switch (pathinfo->type) { case SRV_FILETYPE_DIR: - fidinfo->dir.io = LO_CALL(pathinfo->file, dopen, ctx); - assert(LO_IS_NULL(fidinfo->dir.io) == lib9p_ctx_has_error(&ctx->basectx)); - if (lib9p_ctx_has_error(&ctx->basectx)) + 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; break; case SRV_FILETYPE_FILE: - fidinfo->file.io = LO_CALL(pathinfo->file, fopen, ctx, - rd, wr, - reqmode & LIB9P_O_TRUNC); - assert(LO_IS_NULL(fidinfo->file.io) == lib9p_ctx_has_error(&ctx->basectx)); - if (lib9p_ctx_has_error(&ctx->basectx)) + 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; - qid = LO_CALL(fidinfo->file.io, qid); + } + 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: @@ -1076,216 +1189,413 @@ static void handle_Topen(struct 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: - ctx->authinfo = srv_authinfo_decref(ctx->authinfo); + if (ctx->user) + ctx->user = srv_userid_decref(ctx->user); + srv_respond(ctx, open, &resp, err); } static void handle_Tcreate(struct srv_req *ctx, - struct lib9p_msg_Tcreate *req, - struct lib9p_msg_Rcreate *resp) { - srv_handler_common(ctx, req, resp); + struct lib9p_msg_Tcreate *req) { + srv_handler_common(ctx, create, req); + + err = error_new(E_POSIX_EOPNOTSUPP, "create not (yet?) implemented"); - lib9p_error(&ctx->basectx, - LIB9P_ERRNO_L_EOPNOTSUPP, "create not (yet?) implemented"); + srv_respond(ctx, create, &resp, err); +} + +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, - struct lib9p_msg_Rread *resp) { - srv_handler_common(ctx, req, resp); + 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 = map_load(&ctx->parent_sess->fids, req->fid); if (!fidinfo) { - lib9p_errorf(&ctx->basectx, - LIB9P_ERRNO_L_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->basectx, - LIB9P_ERRNO_L_EINVAL, "FID not open for reading"); + srv_respond(ctx, read, NULL, error_new(E_POSIX_EINVAL, "FID not open for reading")); return; } /* Do it. */ - ctx->authinfo = srv_authinfo_incref(fidinfo->authinfo); + ctx->user = srv_userid_incref(fidinfo->user); switch (fidinfo->type) { case SRV_FILETYPE_DIR: - /* 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->basectx, - LIB9P_ERRNO_L_EINVAL, "invalid offset (must be 0 or %"PRIu64"): %"PRIu64, - fidinfo->dir.off, req->offset); - ctx->authinfo = srv_authinfo_decref(ctx->authinfo); - return; - } - /* Do it. */ - resp->data = (char *)(&resp[1]); - size_t num = LO_CALL(fidinfo->dir.io, dread, 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->basectx, req->count, &((uint8_t *)resp->data)[len], &i_len, NULL); - len += i_len; - } - resp->count = len; - /* Remember. */ - fidinfo->dir.idx = idx+num; - fidinfo->dir.off = req->offset + len; +#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: - struct iovec iov; - LO_CALL(fidinfo->file.io, pread, ctx, req->count, req->offset, &iov); - if (!lib9p_ctx_has_error(&ctx->basectx)) { - resp->count = iov.iov_len; - resp->data = iov.iov_base; - if (resp->count > req->count) - resp->count = req->count; - } + handle_read_file(ctx, fidinfo, req->offset, req->count); break; case SRV_FILETYPE_AUTH: assert_notreached("TODO: auth not yet implemented"); break; } - ctx->authinfo = srv_authinfo_decref(ctx->authinfo); + 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; + } + 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; + } + } + + /* 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 srv_req *ctx, - struct lib9p_msg_Twrite *req, - struct lib9p_msg_Rwrite *resp) { - srv_handler_common(ctx, req, resp); + 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 = map_load(&ctx->parent_sess->fids, req->fid); if (!fidinfo) { - lib9p_errorf(&ctx->basectx, - LIB9P_ERRNO_L_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->basectx, - LIB9P_ERRNO_L_EINVAL, "FID not open for writing"); - return; + err = error_new(E_POSIX_EINVAL, "FID not open for writing"); + goto twrite_return; } + if (fidinfo->flags & FIDFLAG_APPEND) + req->offset = 0; /* Do it. */ - ctx->authinfo = srv_authinfo_incref(fidinfo->authinfo); - resp->count = LO_CALL(fidinfo->file.io, pwrite, ctx, req->data, req->count, req->offset); - ctx->authinfo = srv_authinfo_decref(ctx->authinfo); + 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 handle_Tclunk(struct srv_req *ctx, - struct lib9p_msg_Tclunk *req, - struct lib9p_msg_Rclunk *resp) { - srv_handler_common(ctx, req, resp); + 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->basectx, - LIB9P_ERRNO_L_EBADF, "bad file number %"PRIu32, req->fid); + srv_respond(ctx, clunk, NULL, error_new(E_POSIX_EBADF, "bad file number ", req->fid)); return; } - ctx->authinfo = srv_authinfo_incref(fidinfo->authinfo); - srv_fid_del(ctx, req->fid, false); - ctx->authinfo = srv_authinfo_decref(ctx->authinfo); + 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_Tremove(struct srv_req *ctx, - struct lib9p_msg_Tremove *req, - struct lib9p_msg_Rremove *resp) { - srv_handler_common(ctx, req, resp); + 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) { - lib9p_errorf(&ctx->basectx, - LIB9P_ERRNO_L_EBADF, "bad file number %"PRIu32, req->fid); - return; + err = error_new(E_POSIX_EBADF, "bad file number ", req->fid); + goto tremove_return; + } + + 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; } - ctx->authinfo = srv_authinfo_incref(fidinfo->authinfo); - srv_fid_del(ctx, req->fid, true); - ctx->authinfo = srv_authinfo_decref(ctx->authinfo); + tremove_main: + srv_fid_del(ctx, req->fid, fidinfo, remove); + tremove_return: + srv_respond(ctx, remove, &resp, err); } static void handle_Tstat(struct srv_req *ctx, - struct lib9p_msg_Tstat *req, - struct lib9p_msg_Rstat *resp) { - srv_handler_common(ctx, req, resp); + struct lib9p_msg_Tstat *req) { + srv_handler_common(ctx, stat, req); struct srv_fidinfo *fidinfo = map_load(&ctx->parent_sess->fids, req->fid); if (!fidinfo) { - lib9p_errorf(&ctx->basectx, - LIB9P_ERRNO_L_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 = map_load(&ctx->parent_sess->paths, fidinfo->path); assert(pathinfo); - ctx->authinfo = srv_authinfo_incref(fidinfo->authinfo); - resp->stat = LO_CALL(pathinfo->file, stat, ctx); - if (!lib9p_ctx_has_error(&ctx->basectx)) - lib9p_stat_assert(resp->stat); - ctx->authinfo = srv_authinfo_decref(ctx->authinfo); + 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, - struct lib9p_msg_Rwstat *resp) { - srv_handler_common(ctx, req, resp); + struct lib9p_msg_Twstat *req) { + srv_handler_common(ctx, wstat, req); + + err = error_new(E_POSIX_EOPNOTSUPP, "wstat not (yet?) implemented"); - lib9p_error(&ctx->basectx, - LIB9P_ERRNO_L_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, - struct lib9p_msg_Ropenfd *resp) { - srv_handler_common(ctx, req, resp); + struct lib9p_msg_Topenfd *req) { + srv_handler_common(ctx, openfd, req); + + err = error_new(E_POSIX_EOPNOTSUPP, "openfd not (yet?) implemented"); - lib9p_error(&ctx->basectx, - LIB9P_ERRNO_L_EOPNOTSUPP, "openfd not (yet?) implemented"); + srv_respond(ctx, openfd, &resp, err); } #endif #if CONFIG_9P_ENABLE_9P2000_e static void handle_Tsession(struct srv_req *ctx, - struct lib9p_msg_Tsession *req, - struct lib9p_msg_Rsession *resp) { - srv_handler_common(ctx, req, resp); + struct lib9p_msg_Tsession *req) { + srv_handler_common(ctx, session, req); + + err = error_new(E_POSIX_EOPNOTSUPP, "session not (yet?) implemented"); - lib9p_error(&ctx->basectx, - LIB9P_ERRNO_L_EOPNOTSUPP, "session not (yet?) implemented"); + srv_respond(ctx, session, &resp, err); } static void handle_Tsread(struct srv_req *ctx, - struct lib9p_msg_Tsread *req, - struct lib9p_msg_Rsread *resp) { - srv_handler_common(ctx, req, resp); + struct lib9p_msg_Tsread *req) { + srv_handler_common(ctx, sread, req); - lib9p_error(&ctx->basectx, - LIB9P_ERRNO_L_EOPNOTSUPP, "sread not (yet?) implemented"); + err = error_new(E_POSIX_EOPNOTSUPP, "sread not (yet?) implemented"); + + srv_respond(ctx, sread, &resp, err); } static void handle_Tswrite(struct srv_req *ctx, - struct lib9p_msg_Tswrite *req, - struct lib9p_msg_Rswrite *resp) { - srv_handler_common(ctx, req, resp); + struct lib9p_msg_Tswrite *req) { + srv_handler_common(ctx, swrite, req); + + err = error_new(E_POSIX_EOPNOTSUPP, "swrite not (yet?) implemented"); - lib9p_error(&ctx->basectx, - LIB9P_ERRNO_L_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 index db5be41..cea1d79 100644 --- a/lib9p/srv_include/lib9p/srv.h +++ b/lib9p/srv_include/lib9p/srv.h @@ -7,57 +7,156 @@ #ifndef _LIB9P_SRV_H_ #define _LIB9P_SRV_H_ -#include <libcr/coroutine.h> -#include <libcr_ipc/rpc.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 <libobj/obj.h> #include <lib9p/core.h> -/* context ********************************************************************/ +#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); -CR_CHAN_DECLARE(_lib9p_srv_flushch, bool); +/* context ********************************************************************/ -struct lib9p_srv_authinfo { - lib9p_nuid_t uid; - struct lib9p_s uname; +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_authinfo *authinfo; + 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_flushch_t flushch; + 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); -void lib9p_srv_acknowledge_flush(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 ******************************************************/ -lo_interface lib9p_srv_fio; -lo_interface lib9p_srv_dio; +struct lib9p_srv_dirent { + struct lib9p_qid qid; + struct lib9p_s name; +}; +DECLARE_ERROR_OR_(struct lib9p_srv_dirent, lib9p_srv_dirent); -/* 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. +/* 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_file_LO_IFACE \ +#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 **********************************************/ \ \ /** \ @@ -81,65 +180,41 @@ lo_interface lib9p_srv_dio; \ /* 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 *) \ + /** 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(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) \ + 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(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); + 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(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_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(TYP, NAM) \ - static lo_interface lib9p_srv_fio NAM##_fopen (TYP *, struct lib9p_srv_ctx *, bool, bool, bool) { assert_notreached("not a file"); } +#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 ****************************************************/ @@ -155,9 +230,9 @@ LO_INTERFACE(net_stream_conn_unix); 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); - void (*msglog )(struct lib9p_srv_ctx *, enum lib9p_msg_type, void *hostmsg); /* optional */ + 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 @@ -166,7 +241,7 @@ struct lib9p_srv { BEGIN_PRIVATE(LIB9P_SRV_H); unsigned int readers; unsigned int writers; - _lib9p_srv_reqch_t _reqch; + _lib9p_srv_reqch_t reqch; END_PRIVATE(LIB9P_SRV_H); }; @@ -203,24 +278,36 @@ void lib9p_srv_accept_and_read_loop(struct lib9p_srv *srv, lo_interface net_stre * * Errors that this function itself may send to clients: * - * @errno L_EMSGSIZE T-message has size[4] bigger than max_msg_size - * @errno L_EDOM Tversion specified an impossibly small max_msg_size - * @errno L_EOPNOTSUPP T-message has an R-message type, or an unrecognized T-message type - * @errno L_EBADMSG T-message has wrong size[4] for its content, or has invalid UTF-8 - * @errno L_ERANGE R-message does not fit into max_msg_size + * @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, 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. + * 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/tests/client_config/config.h b/lib9p/tests/client_config/config.h index bcf030d..afcf49f 100644 --- a/lib9p/tests/client_config/config.h +++ b/lib9p/tests/client_config/config.h @@ -1,4 +1,4 @@ -/* config.h - Compile-time configuration for lib9p test clients +/* 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 @@ -7,7 +7,6 @@ #ifndef _CONFIG_H_ #define _CONFIG_H_ -#define CONFIG_9P_MAX_ERR_SIZE 128 #define CONFIG_9P_MAX_9P2000_e_WELEM 16 #define CONFIG_9P_ENABLE_9P2000 1 /* bool */ diff --git a/lib9p/tests/test_compile.c b/lib9p/tests/test_compile.c index 4532655..6d7fb50 100644 --- a/lib9p/tests/test_compile.c +++ b/lib9p/tests/test_compile.c @@ -2,429 +2,1254 @@ #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_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_NUID_NONUID; - x = LIB9P_ERRNO_NOERROR; - x = LIB9P_ERRNO_L_EPERM; - x = LIB9P_ERRNO_L_ENOENT; - x = LIB9P_ERRNO_L_ESRCH; - x = LIB9P_ERRNO_L_EINTR; - x = LIB9P_ERRNO_L_EIO; - x = LIB9P_ERRNO_L_ENXIO; - x = LIB9P_ERRNO_L_E2BIG; - x = LIB9P_ERRNO_L_ENOEXEC; - x = LIB9P_ERRNO_L_EBADF; - x = LIB9P_ERRNO_L_ECHILD; - x = LIB9P_ERRNO_L_EAGAIN; - x = LIB9P_ERRNO_L_ENOMEM; - x = LIB9P_ERRNO_L_EACCES; - x = LIB9P_ERRNO_L_EFAULT; - x = LIB9P_ERRNO_L_ENOTBLK; - x = LIB9P_ERRNO_L_EBUSY; - x = LIB9P_ERRNO_L_EEXIST; - x = LIB9P_ERRNO_L_EXDEV; - x = LIB9P_ERRNO_L_ENODEV; - x = LIB9P_ERRNO_L_ENOTDIR; - x = LIB9P_ERRNO_L_EISDIR; - x = LIB9P_ERRNO_L_EINVAL; - x = LIB9P_ERRNO_L_ENFILE; - x = LIB9P_ERRNO_L_EMFILE; - x = LIB9P_ERRNO_L_ENOTTY; - x = LIB9P_ERRNO_L_ETXTBSY; - x = LIB9P_ERRNO_L_EFBIG; - x = LIB9P_ERRNO_L_ENOSPC; - x = LIB9P_ERRNO_L_ESPIPE; - x = LIB9P_ERRNO_L_EROFS; - x = LIB9P_ERRNO_L_EMLINK; - x = LIB9P_ERRNO_L_EPIPE; - x = LIB9P_ERRNO_L_EDOM; - x = LIB9P_ERRNO_L_ERANGE; - x = LIB9P_ERRNO_L_EDEADLK; - x = LIB9P_ERRNO_L_ENAMETOOLONG; - x = LIB9P_ERRNO_L_ENOLCK; - x = LIB9P_ERRNO_L_ENOSYS; - x = LIB9P_ERRNO_L_ENOTEMPTY; - x = LIB9P_ERRNO_L_ELOOP; - x = LIB9P_ERRNO_L_ENOMSG; - x = LIB9P_ERRNO_L_EIDRM; - x = LIB9P_ERRNO_L_ECHRNG; - x = LIB9P_ERRNO_L_EL2NSYNC; - x = LIB9P_ERRNO_L_EL3HLT; - x = LIB9P_ERRNO_L_EL3RST; - x = LIB9P_ERRNO_L_ELNRNG; - x = LIB9P_ERRNO_L_EUNATCH; - x = LIB9P_ERRNO_L_ENOCSI; - x = LIB9P_ERRNO_L_EL2HLT; - x = LIB9P_ERRNO_L_EBADE; - x = LIB9P_ERRNO_L_EBADR; - x = LIB9P_ERRNO_L_EXFULL; - x = LIB9P_ERRNO_L_ENOANO; - x = LIB9P_ERRNO_L_EBADRQC; - x = LIB9P_ERRNO_L_EBADSLT; - x = LIB9P_ERRNO_L_EBFONT; - x = LIB9P_ERRNO_L_ENOSTR; - x = LIB9P_ERRNO_L_ENODATA; - x = LIB9P_ERRNO_L_ETIME; - x = LIB9P_ERRNO_L_ENOSR; - x = LIB9P_ERRNO_L_ENONET; - x = LIB9P_ERRNO_L_ENOPKG; - x = LIB9P_ERRNO_L_EREMOTE; - x = LIB9P_ERRNO_L_ENOLINK; - x = LIB9P_ERRNO_L_EADV; - x = LIB9P_ERRNO_L_ESRMNT; - x = LIB9P_ERRNO_L_ECOMM; - x = LIB9P_ERRNO_L_EPROTO; - x = LIB9P_ERRNO_L_EMULTIHOP; - x = LIB9P_ERRNO_L_EDOTDOT; - x = LIB9P_ERRNO_L_EBADMSG; - x = LIB9P_ERRNO_L_EOVERFLOW; - x = LIB9P_ERRNO_L_ENOTUNIQ; - x = LIB9P_ERRNO_L_EBADFD; - x = LIB9P_ERRNO_L_EREMCHG; - x = LIB9P_ERRNO_L_ELIBACC; - x = LIB9P_ERRNO_L_ELIBBAD; - x = LIB9P_ERRNO_L_ELIBSCN; - x = LIB9P_ERRNO_L_ELIBMAX; - x = LIB9P_ERRNO_L_ELIBEXEC; - x = LIB9P_ERRNO_L_EILSEQ; - x = LIB9P_ERRNO_L_ERESTART; - x = LIB9P_ERRNO_L_ESTRPIPE; - x = LIB9P_ERRNO_L_EUSERS; - x = LIB9P_ERRNO_L_ENOTSOCK; - x = LIB9P_ERRNO_L_EDESTADDRREQ; - x = LIB9P_ERRNO_L_EMSGSIZE; - x = LIB9P_ERRNO_L_EPROTOTYPE; - x = LIB9P_ERRNO_L_ENOPROTOOPT; - x = LIB9P_ERRNO_L_EPROTONOSUPPORT; - x = LIB9P_ERRNO_L_ESOCKTNOSUPPORT; - x = LIB9P_ERRNO_L_EOPNOTSUPP; - x = LIB9P_ERRNO_L_EPFNOSUPPORT; - x = LIB9P_ERRNO_L_EAFNOSUPPORT; - x = LIB9P_ERRNO_L_EADDRINUSE; - x = LIB9P_ERRNO_L_EADDRNOTAVAIL; - x = LIB9P_ERRNO_L_ENETDOWN; - x = LIB9P_ERRNO_L_ENETUNREACH; - x = LIB9P_ERRNO_L_ENETRESET; - x = LIB9P_ERRNO_L_ECONNABORTED; - x = LIB9P_ERRNO_L_ECONNRESET; - x = LIB9P_ERRNO_L_ENOBUFS; - x = LIB9P_ERRNO_L_EISCONN; - x = LIB9P_ERRNO_L_ENOTCONN; - x = LIB9P_ERRNO_L_ESHUTDOWN; - x = LIB9P_ERRNO_L_ETOOMANYREFS; - x = LIB9P_ERRNO_L_ETIMEDOUT; - x = LIB9P_ERRNO_L_ECONNREFUSED; - x = LIB9P_ERRNO_L_EHOSTDOWN; - x = LIB9P_ERRNO_L_EHOSTUNREACH; - x = LIB9P_ERRNO_L_EALREADY; - x = LIB9P_ERRNO_L_EINPROGRESS; - x = LIB9P_ERRNO_L_ESTALE; - x = LIB9P_ERRNO_L_EUCLEAN; - x = LIB9P_ERRNO_L_ENOTNAM; - x = LIB9P_ERRNO_L_ENAVAIL; - x = LIB9P_ERRNO_L_EISNAM; - x = LIB9P_ERRNO_L_EREMOTEIO; - x = LIB9P_ERRNO_L_EDQUOT; - x = LIB9P_ERRNO_L_ENOMEDIUM; - x = LIB9P_ERRNO_L_EMEDIUMTYPE; - x = LIB9P_ERRNO_L_ECANCELED; - x = LIB9P_ERRNO_L_ENOKEY; - x = LIB9P_ERRNO_L_EKEYEXPIRED; - x = LIB9P_ERRNO_L_EKEYREVOKED; - x = LIB9P_ERRNO_L_EKEYREJECTED; - x = LIB9P_ERRNO_L_EOWNERDEAD; - x = LIB9P_ERRNO_L_ENOTRECOVERABLE; - x = LIB9P_ERRNO_L_ERFKILL; - x = LIB9P_ERRNO_L_EHWPOISON; - 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 1289943..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> @@ -12,8 +12,8 @@ outfile=$2 echo 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 f899dfa..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 @@ -7,13 +7,20 @@ #ifndef _CONFIG_H_ #define _CONFIG_H_ -#define CONFIG_9P_MAX_ERR_SIZE 128 +/* 9P *************************************************************************/ + #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 */ +/* 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 + +/* COROUTINE ******************************************************************/ + +#define CONFIG_COROUTINE_NAME_LEN 16 +#define CONFIG_COROUTINE_MEASURE_STACK 1 /* bool */ #endif /* _CONFIG_H_ */ diff --git a/lib9p/tests/test_server/CMakeLists.txt b/lib9p/tests/test_server/CMakeLists.txt index b659373..bdb46e2 100644 --- a/lib9p/tests/test_server/CMakeLists.txt +++ b/lib9p/tests/test_server/CMakeLists.txt @@ -9,8 +9,8 @@ if (PICO_PLATFORM STREQUAL "host") add_library(test_server_objs OBJECT main.c + fs_flush.c fs_shutdown.c - fs_slowread.c fs_whoami.c ) target_include_directories(test_server_objs PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/config) @@ -25,13 +25,13 @@ target_link_libraries(test_server_objs # 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 d9cf008..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 @@ -12,7 +12,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 @@ -32,6 +40,7 @@ * (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) @@ -39,12 +48,6 @@ */ #define CONFIG_9P_SRV_MAX_HOSTMSG_SIZE CONFIG_9P_SRV_MAX_MSG_SIZE+16 -#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 ******************************************************************/ #define CONFIG_COROUTINE_STACK_SIZE_DEFAULT (32*1024) 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 index e872b78..53a243b 100644 --- a/lib9p/tests/test_server/fs_shutdown.c +++ b/lib9p/tests/test_server/fs_shutdown.c @@ -4,74 +4,71 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ -#include <stdlib.h> +#include <libmisc/alloc.h> #include "fs_shutdown.h" -LO_IMPLEMENTATION_C(lib9p_srv_file, struct shutdown_file, shutdown_file, static); +LO_IMPLEMENTATION_C(lib9p_srv_file, struct shutdown_file, shutdown_file); struct shutdown_fio { struct shutdown_file *parent; }; -LO_IMPLEMENTATION_H(lib9p_srv_fio, struct shutdown_fio, shutdown_fio); -LO_IMPLEMENTATION_C(lib9p_srv_fio, struct shutdown_fio, shutdown_fio, static); +LO_IMPLEMENTATION_STATIC(lib9p_srv_fio, struct shutdown_fio, shutdown_fio); /* srv_file *******************************************************************/ -static void shutdown_file_free(struct shutdown_file *self) { +void shutdown_file_free(struct shutdown_file *self) { assert(self); } -static struct lib9p_qid shutdown_file_qid(struct shutdown_file *self) { +struct lib9p_qid shutdown_file_qid(struct shutdown_file *self) { assert(self); return (struct lib9p_qid){ - .type = LIB9P_QT_FILE, + .type = LIB9P_QT_FILE | LIB9P_QT_APPEND, .vers = 1, .path = self->pathnum, }; } -static struct lib9p_stat shutdown_file_stat(struct shutdown_file *self, struct lib9p_srv_ctx *ctx) { +error shutdown_file_stat(struct shutdown_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 = shutdown_file_qid(self), - .file_mode = 0222, - .file_atime = UTIL9P_ATIME, - .file_mtime = UTIL9P_MTIME, - .file_size = 0, - .file_name = lib9p_str(self->name), - .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, - }; + 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; } -static void shutdown_file_wstat(struct shutdown_file *self, struct lib9p_srv_ctx *ctx, struct lib9p_stat) { +error shutdown_file_wstat(struct shutdown_file *self, struct lib9p_srv_ctx *ctx, struct lib9p_srv_stat) { assert(self); assert(ctx); - lib9p_error(&ctx->basectx, LIB9P_ERRNO_L_EROFS, "cannot wstat API file"); + return error_new(E_POSIX_EROFS, "cannot wstat API file"); } -static void shutdown_file_remove(struct shutdown_file *self, struct lib9p_srv_ctx *ctx) { +error shutdown_file_remove(struct shutdown_file *self, struct lib9p_srv_ctx *ctx) { assert(self); assert(ctx); - lib9p_error(&ctx->basectx, LIB9P_ERRNO_L_EROFS, "cannot remove API file"); + return error_new(E_POSIX_EROFS, "cannot remove API file"); } -LIB9P_SRV_NOTDIR(struct shutdown_file, shutdown_file) +LIB9P_SRV_NOTDIR(, struct shutdown_file, shutdown_file); -static lo_interface lib9p_srv_fio shutdown_file_fopen(struct shutdown_file *self, struct lib9p_srv_ctx *ctx, bool, bool, bool) { +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 = malloc(sizeof(struct shutdown_fio)); + struct shutdown_fio *ret = heap_alloc(1, struct shutdown_fio); ret->parent = self; - return lo_box_shutdown_fio_as_lib9p_srv_fio(ret); + return ERROR_NEW_VAL(lib9p_srv_fio, LO_BOX(lib9p_srv_fio, ret)); } /* srv_fio ********************************************************************/ @@ -81,7 +78,7 @@ static void shutdown_fio_iofree(struct shutdown_fio *self) { free(self); } -static struct lib9p_qid shutdown_fio_qid(struct shutdown_fio *self) { +static struct lib9p_qid shutdown_fio_ioqid(struct shutdown_fio *self) { assert(self); return shutdown_file_qid(self->parent); } @@ -91,18 +88,21 @@ static uint32_t shutdown_fio_iounit(struct shutdown_fio *self) { return 0; } -static uint32_t shutdown_fio_pwrite(struct shutdown_fio *self, struct lib9p_srv_ctx *ctx, void *buf, uint32_t byte_count, uint64_t LM_UNUSED(offset)) { +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 0; + return ERROR_NEW_VAL(uint32_t, 0); for (size_t i = 0; i < self->parent->nlisteners; i++) - LO_CALL(lo_box_hostnet_tcplist_as_net_stream_listener(&self->parent->listeners[i]), close); - return byte_count; + LO_CALL(LO_BOX(net_stream_listener, &self->parent->listeners[i]), close); + return ERROR_NEW_VAL(uint32_t, byte_count); } -static void shutdown_fio_pread(struct shutdown_fio *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)) { +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 index 65956db..7b8d327 100644 --- a/lib9p/tests/test_server/fs_shutdown.h +++ b/lib9p/tests/test_server/fs_shutdown.h @@ -7,8 +7,8 @@ #ifndef _LIB9P_TESTS_TEST_SERVER_FS_SHUTDOWN_H_ #define _LIB9P_TESTS_TEST_SERVER_FS_SHUTDOWN_H_ -#include <util9p/static.h> #include <libhw/host_net.h> +#include <util9p/static.h> struct shutdown_file { char *name; @@ -18,6 +18,5 @@ struct shutdown_file { size_t nlisteners; }; LO_IMPLEMENTATION_H(lib9p_srv_file, struct shutdown_file, shutdown_file); -#define lo_box_shutdown_file_as_lib9p_srv_file(obj) util9p_box(shutdown_file, obj) #endif /* _LIB9P_TESTS_TEST_SERVER_FS_SHUTDOWN_H_ */ diff --git a/lib9p/tests/test_server/fs_slowread.c b/lib9p/tests/test_server/fs_slowread.c deleted file mode 100644 index c94fba0..0000000 --- a/lib9p/tests/test_server/fs_slowread.c +++ /dev/null @@ -1,116 +0,0 @@ -/* lib9p/tests/test_server/fs_slowread.c - slowread API endpoint - * - * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com> - * SPDX-License-Identifier: AGPL-3.0-or-later - */ - -#include <stdlib.h> - -#include "fs_slowread.h" - -LO_IMPLEMENTATION_C(lib9p_srv_file, struct slowread_file, slowread_file, static); - -struct slowread_fio { - struct slowread_file *parent; -}; -LO_IMPLEMENTATION_H(lib9p_srv_fio, struct slowread_fio, slowread_fio); -LO_IMPLEMENTATION_C(lib9p_srv_fio, struct slowread_fio, slowread_fio, static); - -/* srv_file *******************************************************************/ - -static void slowread_file_free(struct slowread_file *self) { - assert(self); -} -static struct lib9p_qid slowread_file_qid(struct slowread_file *self) { - assert(self); - return (struct lib9p_qid){ - .type = LIB9P_QT_FILE, - .vers = 1, - .path = self->pathnum, - }; -} - -static struct lib9p_stat slowread_file_stat(struct slowread_file *self, struct lib9p_srv_ctx *ctx) { - assert(self); - assert(ctx); - return (struct lib9p_stat){ - .kern_type = 0, - .kern_dev = 0, - .file_qid = slowread_file_qid(self), - .file_mode = 0444, - .file_atime = UTIL9P_ATIME, - .file_mtime = UTIL9P_MTIME, - .file_size = 6, - .file_name = lib9p_str(self->name), - .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 slowread_file_wstat(struct slowread_file *self, struct lib9p_srv_ctx *ctx, struct lib9p_stat) { - assert(self); - assert(ctx); - lib9p_error(&ctx->basectx, LIB9P_ERRNO_L_EROFS, "cannot wstat API file"); -} -static void slowread_file_remove(struct slowread_file *self, struct lib9p_srv_ctx *ctx) { - assert(self); - assert(ctx); - lib9p_error(&ctx->basectx, LIB9P_ERRNO_L_EROFS, "cannot remove API file"); -} - -LIB9P_SRV_NOTDIR(struct slowread_file, slowread_file) - -static lo_interface lib9p_srv_fio slowread_file_fopen(struct slowread_file *self, struct lib9p_srv_ctx *ctx, bool, bool, bool) { - assert(self); - assert(ctx); - - struct slowread_fio *ret = malloc(sizeof(struct slowread_fio)); - ret->parent = self; - - return lo_box_slowread_fio_as_lib9p_srv_fio(ret); -} - -/* srv_fio ********************************************************************/ - -static void slowread_fio_iofree(struct slowread_fio *self) { - assert(self); - free(self); -} - -static struct lib9p_qid slowread_fio_qid(struct slowread_fio *self) { - assert(self); - return slowread_file_qid(self->parent); -} - -static uint32_t slowread_fio_iounit(struct slowread_fio *self) { - assert(self); - return 0; -} - -static uint32_t slowread_fio_pwrite(struct slowread_fio *LM_UNUSED(self), - struct lib9p_srv_ctx *LM_UNUSED(ctx), - void *LM_UNUSED(buf), uint32_t LM_UNUSED(byte_count), - uint64_t LM_UNUSED(offset)) { - assert_notreached("not writable"); -} -static void slowread_fio_pread(struct slowread_fio *self, struct lib9p_srv_ctx *ctx, - uint32_t byte_count, uint64_t LM_UNUSED(byte_offset), - struct iovec *ret) { - assert(self); - assert(ctx); - assert(ret); - - while (!lib9p_srv_flush_requested(ctx)) - cr_yield(); - if (self->parent->flushable) - lib9p_srv_acknowledge_flush(ctx); - else - *ret = (struct iovec){ - .iov_base = "Sloth\n", - .iov_len = 6 < byte_count ? 6 : byte_count, - }; -} diff --git a/lib9p/tests/test_server/fs_slowread.h b/lib9p/tests/test_server/fs_slowread.h deleted file mode 100644 index ef4b65f..0000000 --- a/lib9p/tests/test_server/fs_slowread.h +++ /dev/null @@ -1,22 +0,0 @@ -/* lib9p/tests/test_server/fs_slowread.h - slowread 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_SLOWREAD_H_ -#define _LIB9P_TESTS_TEST_SERVER_FS_SLOWREAD_H_ - -#include <util9p/static.h> -#include <libhw/host_net.h> - -struct slowread_file { - char *name; - uint64_t pathnum; - - bool flushable; -}; -LO_IMPLEMENTATION_H(lib9p_srv_file, struct slowread_file, slowread_file); -#define lo_box_slowread_file_as_lib9p_srv_file(obj) util9p_box(slowread_file, obj) - -#endif /* _LIB9P_TESTS_TEST_SERVER_FS_SLOWREAD_H_ */ diff --git a/lib9p/tests/test_server/fs_whoami.c b/lib9p/tests/test_server/fs_whoami.c index 560e31f..a07fdba 100644 --- a/lib9p/tests/test_server/fs_whoami.c +++ b/lib9p/tests/test_server/fs_whoami.c @@ -4,27 +4,29 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ -#include <stdio.h> /* for snprintf() */ -#include <stdlib.h> /* for malloc(), realloc(), free() */ +#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, static); +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_H(lib9p_srv_fio, struct whoami_fio, whoami_fio); -LO_IMPLEMENTATION_C(lib9p_srv_fio, struct whoami_fio, whoami_fio, static); +LO_IMPLEMENTATION_STATIC(lib9p_srv_fio, struct whoami_fio, whoami_fio); size_t whoami_len(struct lib9p_srv_ctx *ctx) { assert(ctx); - assert(ctx->authinfo); + assert(ctx->user); size_t len = 0; - uint32_t uid = ctx->authinfo->uid; + uint32_t uid = ctx->user->num; while (uid) { len++; uid /= 10; @@ -32,16 +34,16 @@ size_t whoami_len(struct lib9p_srv_ctx *ctx) { if (!len) len++; len += 2; - len += ctx->authinfo->uname.len; + len += ctx->user->name.len; return len; } /* srv_file *******************************************************************/ -static void whoami_file_free(struct whoami_file *self) { +void whoami_file_free(struct whoami_file *self) { assert(self); } -static struct lib9p_qid whoami_file_qid(struct whoami_file *self) { +struct lib9p_qid whoami_file_qid(struct whoami_file *self) { assert(self); return (struct lib9p_qid){ .type = LIB9P_QT_FILE, @@ -50,51 +52,48 @@ static struct lib9p_qid whoami_file_qid(struct whoami_file *self) { }; } -static struct lib9p_stat whoami_file_stat(struct whoami_file *self, struct lib9p_srv_ctx *ctx) { +error whoami_file_stat(struct whoami_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 = whoami_file_qid(self), - .file_mode = 0444, - .file_atime = UTIL9P_ATIME, - .file_mtime = UTIL9P_MTIME, - .file_size = whoami_len(ctx), - .file_name = lib9p_str(self->name), - .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, - }; + 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; } -static void whoami_file_wstat(struct whoami_file *self, struct lib9p_srv_ctx *ctx, struct lib9p_stat) { +error whoami_file_wstat(struct whoami_file *self, struct lib9p_srv_ctx *ctx, struct lib9p_srv_stat) { assert(self); assert(ctx); - lib9p_error(&ctx->basectx, LIB9P_ERRNO_L_EROFS, "cannot wstat API file"); + return error_new(E_POSIX_EROFS, "cannot wstat API file"); } -static void whoami_file_remove(struct whoami_file *self, struct lib9p_srv_ctx *ctx) { +error whoami_file_remove(struct whoami_file *self, struct lib9p_srv_ctx *ctx) { assert(self); assert(ctx); - lib9p_error(&ctx->basectx, LIB9P_ERRNO_L_EROFS, "cannot remove API file"); + return error_new(E_POSIX_EROFS, "cannot remove API file"); } -LIB9P_SRV_NOTDIR(struct whoami_file, whoami_file) +LIB9P_SRV_NOTDIR(, struct whoami_file, whoami_file); -static lo_interface lib9p_srv_fio whoami_file_fopen(struct whoami_file *self, struct lib9p_srv_ctx *ctx, bool, bool, bool) { +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 = malloc(sizeof(struct whoami_fio)); + struct whoami_fio *ret = heap_alloc(1, struct whoami_fio); ret->parent = self; ret->buf_len = 0; ret->buf = NULL; - return lo_box_whoami_fio_as_lib9p_srv_fio(ret); + return ERROR_NEW_VAL(lib9p_srv_fio, LO_BOX(lib9p_srv_fio, ret)); } /* srv_fio ********************************************************************/ @@ -106,7 +105,7 @@ static void whoami_fio_iofree(struct whoami_fio *self) { free(self); } -static struct lib9p_qid whoami_fio_qid(struct whoami_fio *self) { +static struct lib9p_qid whoami_fio_ioqid(struct whoami_fio *self) { assert(self); assert(self->parent); return whoami_file_qid(self->parent); @@ -117,18 +116,16 @@ static uint32_t whoami_fio_iounit(struct whoami_fio *self) { return 0; } -static uint32_t whoami_fio_pwrite(struct whoami_fio *LM_UNUSED(self), - struct lib9p_srv_ctx *LM_UNUSED(ctx), - void *LM_UNUSED(buf), uint32_t LM_UNUSED(byte_count), - uint64_t LM_UNUSED(offset)) { +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 void whoami_fio_pread(struct whoami_fio *self, struct lib9p_srv_ctx *ctx, - uint32_t byte_count, uint64_t byte_offset, - struct iovec *ret) { +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); - assert(ret); size_t data_size = whoami_len(ctx); if (self->buf_len < data_size+1) { @@ -136,21 +133,15 @@ static void whoami_fio_pread(struct whoami_fio *self, struct lib9p_srv_ctx *ctx, self->buf_len = data_size+1; } snprintf(self->buf, self->buf_len, "%"PRIu32" %.*s\n", - ctx->authinfo->uid, ctx->authinfo->uname.len, ctx->authinfo->uname.utf8); + ctx->user->num, ctx->user->name.len, ctx->user->name.utf8); - if (byte_offset > (uint64_t)data_size) { - lib9p_error(&ctx->basectx, - LIB9P_ERRNO_L_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 = (struct iovec){ - .iov_base = &self->buf[beg_off], - .iov_len = end_off-beg_off, - }; + 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 index 0d3d311..518e11d 100644 --- a/lib9p/tests/test_server/fs_whoami.h +++ b/lib9p/tests/test_server/fs_whoami.h @@ -7,14 +7,13 @@ #ifndef _LIB9P_TESTS_TEST_SERVER_FS_WHOAMI_H_ #define _LIB9P_TESTS_TEST_SERVER_FS_WHOAMI_H_ -#include <util9p/static.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); -#define lo_box_whoami_file_as_lib9p_srv_file(obj) util9p_box(whoami_file, obj) #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 0705747..2519372 100644 --- a/lib9p/tests/test_server/main.c +++ b/lib9p/tests/test_server/main.c @@ -4,24 +4,27 @@ * 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 "static.h" +#include "fs_flush.h" #include "fs_shutdown.h" -#include "fs_slowread.h" #include "fs_whoami.h" +#include "static.h" /* configuration **************************************************************/ @@ -36,11 +39,11 @@ /* 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_9P_MAX_CONNS]; struct lib9p_srv srv; @@ -67,7 +70,7 @@ struct { __VA_OPT__(,) __VA_ARGS__ \ })) -struct lib9p_srv_file root = +static struct lib9p_srv_file root = STATIC_DIR(1, "", STATIC_DIR(2, "Documentation", STATIC_FILE(3, "x", Documentation_x_txt), @@ -76,15 +79,16 @@ struct lib9p_srv_file root = API_FILE(5, "shutdown", shutdown, .listeners = globals.listeners, .nlisteners = LM_ARRAY_LEN(globals.listeners)), - API_FILE(6, "slowread", slowread, - .flushable = false), - API_FILE(7, "slowread-flushable", slowread, - .flushable = true), 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 ***********************************************************************/ @@ -95,7 +99,7 @@ static COROUTINE read_cr(void *_i) { hostnet_tcp_listener_init(&globals.listeners[i], globals.port); - lib9p_srv_accept_and_read_loop(&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(); } @@ -116,50 +120,53 @@ static COROUTINE init_cr(void *) { 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 < _CONFIG_9P_MAX_REQS; i++) { char name[] = {'w', 'r', 'i', 't', 'e', '-', hexdig[i], '\0'}; if (!coroutine_add(name, write_cr, NULL)) - error(1, 0, "coroutine_add(write_cr, NULL)"); + __error(1, 0, "coroutine_add(write_cr, NULL)"); } cr_exit(); } -static void log_fct(char character, void *_stream) { - FILE *stream = _stream; - putc(character, stream); - putchar(character); +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 void log_msg(struct lib9p_srv_ctx *ctx, enum lib9p_msg_type typ, void *hostmsg) { - /* It sucks that %v trips -Wformat and -Wformat-extra-args - * 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_fctprintf(log_fct, globals.logstream, - "%c %v\n", typ % 2 ? '<' : '>', - lo_box_lib9p_msg_as_fmt_formatter(&ctx->basectx, typ, hostmsg)); -#pragma GCC diagnostic pop +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 != 3) - error(2, 0, "usage: %s PORT_NUMBER LOGFILE", argv[0]); + __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 = log_msg; + __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); 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 index 9c9fb5e..09ce746 100755 --- a/lib9p/tests/testclient-p9p +++ b/lib9p/tests/testclient-p9p @@ -25,9 +25,12 @@ 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' \ - '--r--r--r-- M 0 root root 6 Oct 7 2024 slowread' \ - '--r--r--r-- M 0 root root 6 Oct 7 2024 slowread-flushable' \ + '--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/') diff --git a/lib9p/tests/testclient-p9p.explog b/lib9p/tests/testclient-p9p.explog index e5901d2..54f1e4b 100644 --- a/lib9p/tests/testclient-p9p.explog +++ b/lib9p/tests/testclient-p9p.explog @@ -4,14 +4,14 @@ # 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="" n_uid=0 } +> 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="" n_uid=0 } +> 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={ kern_type=0 kern_dev=0 file_qid={ type=(DIR) vers=1 path=1 } file_mode=(DIR|0555) file_atime=1728337905 file_mtime=1728337904 file_size=0 file_name="" file_owner_uid="root" file_owner_gid="root" file_last_modified_uid="root" file_extension="" file_owner_n_uid=0 file_owner_n_gid=0 file_last_modified_n_uid=0 } } +< 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=[ ] } @@ -19,21 +19,21 @@ > 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=428 data=<bytedata> } -> Tread { tag=0 fid=1 offset=428 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="" n_uid=0 } +> 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="" n_uid=0 } +> 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={ kern_type=0 kern_dev=0 file_qid={ type=(DIR) vers=1 path=2 } file_mode=(DIR|0555) file_atime=1728337905 file_mtime=1728337904 file_size=0 file_name="Documentation" file_owner_uid="root" file_owner_gid="root" file_last_modified_uid="root" file_extension="" file_owner_n_uid=0 file_owner_n_gid=0 file_last_modified_n_uid=0 } } +< 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" ] } @@ -48,9 +48,9 @@ < 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="" n_uid=0 } +> 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="" n_uid=0 } +> 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 } ] } @@ -64,9 +64,9 @@ < 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="" n_uid=0 } +> 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="" n_uid=0 } +> 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 } ] } @@ -80,26 +80,26 @@ < 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="" n_uid=0 } +> 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="" n_uid=0 } +> 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={ kern_type=0 kern_dev=0 file_qid={ type=(0) vers=1 path=3 } file_mode=(0444) file_atime=1728337905 file_mtime=1728337904 file_size=166 file_name="x" file_owner_uid="root" file_owner_gid="root" file_last_modified_uid="root" file_extension="" file_owner_n_uid=0 file_owner_n_gid=0 file_last_modified_n_uid=0 } } +< 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="" n_uid=0 } +> 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="" n_uid=0 } +> 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=(0) vers=1 path=5 } ] } +< 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=(0) vers=1 path=5 } iounit=0 } +< 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 } diff --git a/lib9p/tests/testclient-sess.c b/lib9p/tests/testclient-sess.c index ded70d1..e90c94a 100644 --- a/lib9p/tests/testclient-sess.c +++ b/lib9p/tests/testclient-sess.c @@ -4,31 +4,34 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ -#include <arpa/inet.h> /* for htons(), inet_addr() */ +#include <arpa/inet.h> /* for htons(), inet_addr() */ #include <errno.h> -#include <error.h> #include <netinet/in.h> /* for struct sockaddr{,_in} */ -#include <stdlib.h> /* for atoi() */ +#include <stdlib.h> /* for atoi() */ #include <sys/socket.h> /* for socket(), connect() */ -#include <sys/uio.h> /* for writev() */ -#include <unistd.h> /* for read() */ +#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> -#include <lib9p/core.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; - bool err = lib9p_Tmsg_marshal(ctx, typ, body, &buf); - assert(!err); + 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, buf.iov, buf.iov_cnt); + ssize_t act = writev(fd, (const struct iovec *)buf.iov, buf.iov_cnt); if (act < 0) - error(1, errno, "writev"); + __error(1, errno, "writev"); assert((size_t)act == exp); } @@ -41,7 +44,7 @@ static void _recv9p(int fd) { while (done < goal) { ssize_t n = read(fd, &buf[done], goal-done); if (n < 0) - error(1, errno, "read"); + __error(1, errno, "read"); done += n; } goal = uint32le_decode(buf); @@ -49,7 +52,7 @@ static void _recv9p(int fd) { while (done < goal) { ssize_t n = read(fd, &buf[done], goal-done); if (n < 0) - error(1, errno, "read"); + __error(1, errno, "read"); done += n; } } @@ -58,7 +61,7 @@ static void _recv9p(int fd) { int main(int argc, char *argv[]) { if (argc != 2) - error(2, 0, "Usage: %s SERVER_PORT", argv[0]); + __error(2, 0, "Usage: %s SERVER_PORT", argv[0]); uint16_t server_port = atoi(argv[1]); union { @@ -71,9 +74,9 @@ int main(int argc, char *argv[]) { int fd = socket(AF_INET, SOCK_STREAM, 0); if (fd < 0) - error(1, errno, "socket"); + __error(1, errno, "socket"); if (connect(fd, &server_addr.gen, sizeof(server_addr)) < 0) - error(1, errno, "connect"); + __error(1, errno, "connect"); struct lib9p_ctx ctx = { .max_msg_size = 16*1024, @@ -95,9 +98,9 @@ int main(int argc, char *argv[]) { 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"), .n_uid=1000, .aname=lib9p_str("")); + 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"), .n_uid=1001, .aname=lib9p_str("")); + 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 */ @@ -170,29 +173,62 @@ int main(int argc, char *argv[]) { recv9p(); /* Rattach */ /* flush, but original response comes back first */ - wname[0] = lib9p_str("slowread"); send9p(Twalk, .tag=0, .fid=0, .newfid=1, .nwname=1, .wname=wname); + 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=1, .fid=1, .offset=0, .count=6); - send9p(Tflush, .tag=2, .oldtag=1); + 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("slowread-flushable"); send9p(Twalk, .tag=1, .fid=0, .newfid=2, .nwname=1, .wname=wname); + 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=1, .fid=2, .offset=0, .count=6); - send9p(Tflush, .tag=2, .oldtag=1); + 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 */ diff --git a/lib9p/tests/testclient-sess.explog b/lib9p/tests/testclient-sess.explog index 3e2209a..ec8d9c9 100644 --- a/lib9p/tests/testclient-sess.explog +++ b/lib9p/tests/testclient-sess.explog @@ -14,9 +14,9 @@ # 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="" n_uid=1000 } +> 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="" n_uid=1001 } +> 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 } ] } @@ -34,7 +34,7 @@ # 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="" n_uid=0 } +> Tattach { tag=0 fid=0 afid=NOFID uname="nobody" aname="" unum=0 } < Rattach { tag=0 qid={ type=(DIR) vers=1 path=1 } } # dup @@ -83,41 +83,74 @@ # 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="" n_uid=0 } +> 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=[ "slowread" ] } -< Rwalk { tag=0 nwqid=1 wqid=[ { type=(0) vers=1 path=6 } ] } +> 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=6 } iounit=0 } -> Tread { tag=1 fid=1 offset=0 count=6 } -> Tflush { tag=2 oldtag=1 } -< Rread { tag=1 count=6 data="Sloth\n" } +< 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, succeeds -> Twalk { tag=1 fid=0 newfid=2 nwname=1 wname=[ "slowread-flushable" ] } -< Rwalk { tag=1 nwqid=1 wqid=[ { type=(0) vers=1 path=7 } ] } -> Topen { tag=0 fid=2 mode=(MODE_READ) } -< Ropen { tag=0 qid={ type=(0) vers=1 path=7 } iounit=0 } -> Tread { tag=1 fid=2 offset=0 count=6 } +# 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 } -< Rerror { tag=1 errstr="request canceled by flush" errnum=L_ECANCELED } +< 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="" n_uid=0 } +> 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=(0) vers=1 path=5 } ] } +< 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=(0) vers=1 path=5 } iounit=0 } +< 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_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 4fba35a..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, LIB9P_ERRNO_L_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, LIB9P_ERRNO_L_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, - LIB9P_ERRNO_L_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, LIB9P_ERRNO_L_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, - LIB9P_ERRNO_L_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, LIB9P_ERRNO_L_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, LIB9P_ERRNO_L_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, - LIB9P_ERRNO_L_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, LIB9P_ERRNO_L_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 2e66020..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 PUBLIC INTERFACE ${CMAKE_CURRENT_LIST_DIR}/include) +target_link_libraries(libcr INTERFACE libcr_headers) target_sources(libcr INTERFACE coroutine.c ) diff --git a/libcr/coroutine.c b/libcr/coroutine.c index bf44219..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; @@ -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 [0x%p,0x%p)", - 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 60d3f2d..ed7c0a9 100644 --- a/libcr_ipc/CMakeLists.txt +++ b/libcr_ipc/CMakeLists.txt @@ -3,8 +3,15 @@ # 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 PUBLIC INTERFACE ${CMAKE_CURRENT_LIST_DIR}/include) +target_link_libraries(libcr_ipc INTERFACE libcr_ipc_headers) target_sources(libcr_ipc INTERFACE chan.c mutex.c @@ -22,9 +29,9 @@ set(ipc_tests mutex rpc rwmutex - select 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 6ccfa44..b52dab1 100644 --- a/libcr_ipc/chan.c +++ b/libcr_ipc/chan.c @@ -4,60 +4,22 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ -#include <alloca.h> /* for alloca() */ -#include <string.h> /* for memcpy() */ +#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> -/* base channels **************************************************************/ - -struct cr_chan_waiter { - lm_dll_node; +struct _cr_select_waiter { cid_t cid; - void *val_ptr; - void (*dequeue)(void *, size_t); - void *dequeue_arg1; - size_t dequeue_arg2; -}; - -void cr_chan_dequeue(void *_ch, size_t) { - struct _cr_chan *ch = _ch; - lm_dll_pop_from_front(&ch->waiters); -} - -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); + struct _cr_select_arg_list_node *arg_vec; + size_t arg_cnt; - if (ch->waiters.front && ch->waiter_typ != self_typ) { /* non-blocking fast-path */ - /* Copy. */ - struct cr_chan_waiter *front = lm_dll_node_cast(struct cr_chan_waiter, ch->waiters.front); - if (self_typ == _CR_CHAN_SENDER) - memcpy(front->val_ptr, val_ptr, val_size); - else - memcpy(val_ptr, front->val_ptr, val_size); - cr_unpause(front->cid); - front->dequeue(front->dequeue_arg1, - front->dequeue_arg2); - 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, - }; - lm_dll_push_to_rear(&ch->waiters, &self); - ch->waiter_typ = self_typ; - cr_pause_and_yield(); - } -} - -/* select *********************************************************************/ + size_t ret; +}; enum cr_select_class { CR_SELECT_CLASS_DEFAULT, @@ -65,40 +27,26 @@ enum cr_select_class { CR_SELECT_CLASS_NONBLOCK, }; -struct cr_select_waiters { - size_t cnt; - struct cr_select_arg *args; - struct cr_chan_waiter *nodes; -}; - -static inline enum cr_select_class cr_select_getclass(struct cr_select_arg arg) { - switch (arg.op) { +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) + 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->waiter_typ == _CR_CHAN_RECVER) + if (arg->ch->waiters.front && arg->ch->waiters.front->val.op == _CR_SELECT_OP_RECV) 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"); + 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++) - lm_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 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; @@ -108,7 +56,7 @@ size_t cr_select_v(size_t arg_cnt, struct cr_select_arg arg_vec[]) { cr_assert_in_coroutine(); for (size_t i = 0; i < arg_cnt; i++) { - switch (cr_select_getclass(arg_vec[i])) { + switch (cr_select_getclass(&arg_vec[i].val)) { case CR_SELECT_CLASS_BLOCKING: cnt_blocking++; break; @@ -122,46 +70,65 @@ size_t cr_select_v(size_t arg_cnt, struct cr_select_arg arg_vec[]) { } 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; - } + 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_notreached("should have returned from inside for() loop"); + 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(); + return choice_among_all; } if (cnt_default) { for (size_t i = 0; i < arg_cnt; i++) - if (cr_select_getclass(arg_vec[i]) == CR_SELECT_CLASS_DEFAULT) + if (cr_select_getclass(&arg_vec[i].val) == 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), + 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++) { - 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, - }; - lm_dll_push_to_rear(&arg_vec[i].ch->waiters, &waiters.nodes[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 waiters.cnt; + return waiter.ret; } diff --git a/libcr_ipc/include/libcr_ipc/chan.h b/libcr_ipc/include/libcr_ipc/chan.h index 80acdb8..1755a97 100644 --- a/libcr_ipc/include/libcr_ipc/chan.h +++ b/libcr_ipc/include/libcr_ipc/chan.h @@ -7,19 +7,15 @@ #ifndef _LIBCR_IPC_CHAN_H_ #define _LIBCR_IPC_CHAN_H_ -#include <stdbool.h> /* for bool */ -#include <stddef.h> /* for size_t */ +#include <stddef.h> /* for size_t */ -#include <libmisc/linkedlist.h> /* for lm_dll_root */ -#include <libmisc/macro.h> /* for LM_CAT2_() */ +#include <libmisc/linkedlist.h> /* for DLIST_DECLARE() */ +#include <libmisc/private.h> /* base channels **************************************************************/ /** - * CR_CHAN_DECLARE(NAME, VAL_T) declares the following type and - * methods: - * - * type: + * CR_CHAN_DECLARE(NAME, VAL_T) declares the following type: * * / ** * * A NAME##_t is a fair unbuffered channel that transports @@ -32,143 +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; \ - } \ - \ - extern int LM_CAT2_(_CR_CHAN_FORCE_SEMICOLON_, __COUNTER__) - -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 { - enum _cr_chan_waiter_typ waiter_typ; - lm_dll_root waiters; + struct _cr_select_arg_list waiters; + size_t nwaiters; }; -void _cr_chan_xfer(enum _cr_chan_waiter_typ self_typ, struct _cr_chan *ch, void *val_ptr, size_t val_size); - /* cr_select arguments ********************************************************/ /** * Do not populate cr_select_arg yourself; use the * CR_SELECT_{RECV,SEND,DEFAULT} macros. */ -struct cr_select_arg { +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; - size_t val_siz; + } op; + struct _cr_chan *ch; + void *val_ptr; + 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 + +#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]), \ +}}) -#define CR_SELECT_RECV(CH, VALP) \ - /* The _valp temporary variable is to get the compiler to check that \ - * the types are compatible. */ \ - ((struct cr_select_arg){ \ - .op = _CR_SELECT_OP_RECV, \ - .ch = &((CH)->core), \ - .val_ptr = ({ typeof((CH)->vals[0]) *_valp = VALP; _valp; }), \ - .val_siz = sizeof((CH)->vals[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) \ - /* The _valp temporary variable is to get the compiler to check that \ - * the types are compatible. */ \ - ((struct cr_select_arg){ \ - .op = _CR_SELECT_OP_SEND, \ - .ch = &((CH)->core), \ - .val_ptr = ({ typeof((CH)->vals[0]) *_valp = VALP; _valp; }), \ - .val_siz = sizeof((CH)->vals[0]), \ - }) -#define CR_SELECT_DEFAULT \ - ((struct cr_select_arg){ \ - .op = _CR_SELECT_OP_DEFAULT, \ - }) +#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]), \ +}}) + +#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, ...) ******************************************/ -#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])); \ +/** + * @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 0f3c9c2..05a6b2e 100644 --- a/libcr_ipc/include/libcr_ipc/mutex.h +++ b/libcr_ipc/include/libcr_ipc/mutex.h @@ -7,11 +7,11 @@ #ifndef _LIBCR_IPC_MUTEX_H_ #define _LIBCR_IPC_MUTEX_H_ -#include <stdbool.h> /* for bool */ - -#include <libmisc/linkedlist.h> +#include <libmisc/linkedlist.h> /* for SLIST_DECLARE() */ #include <libmisc/private.h> +SLIST_DECLARE(_cr_mutex_waiter_list); + /** * A cr_mutex_t is a fair mutex. * @@ -22,8 +22,8 @@ */ typedef struct { BEGIN_PRIVATE(LIBCR_IPC_MUTEX_H); - bool locked; - lm_sll_root waiters; + bool locked; + struct _cr_mutex_waiter_list waiters; END_PRIVATE(LIBCR_IPC_MUTEX_H); } cr_mutex_t; diff --git a/libcr_ipc/include/libcr_ipc/rpc.h b/libcr_ipc/include/libcr_ipc/rpc.h index f091685..85ebdb3 100644 --- a/libcr_ipc/include/libcr_ipc/rpc.h +++ b/libcr_ipc/include/libcr_ipc/rpc.h @@ -7,16 +7,11 @@ #ifndef _LIBCR_IPC_RPC_H_ #define _LIBCR_IPC_RPC_H_ -#include <stdbool.h> /* for bool */ - -#include <libmisc/linkedlist.h> /* for lm_sll_root */ -#include <libmisc/macro.h> /* for LM_CAT2_() */ +#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: - * - * type: + * CR_RPC_DECLARE(NAME, REQ_T, RESP_T) declares the following types: * * / ** * * A NAME##_t is a fair rpc-channel on which the requester submits a @@ -38,114 +33,109 @@ * * 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); \ - 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(); \ - } \ - \ - extern int LM_CAT2_(_CR_RPC_FORCE_SEMICOLON_, __COUNTER__) +#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, }; +SLIST_DECLARE(_cr_rpc_waiter_list); + struct _cr_rpc { enum _cr_rpc_waiter_typ waiter_typ; - lm_sll_root waiters; + struct _cr_rpc_waiter_list waiters; }; void _cr_rpc_send_req(struct _cr_rpc *ch, void *req_ptr, size_t req_size, void *resp_ptr); diff --git a/libcr_ipc/include/libcr_ipc/rwmutex.h b/libcr_ipc/include/libcr_ipc/rwmutex.h index d48abe9..12ee863 100644 --- a/libcr_ipc/include/libcr_ipc/rwmutex.h +++ b/libcr_ipc/include/libcr_ipc/rwmutex.h @@ -7,11 +7,11 @@ #ifndef _LIBCR_IPC_RWMUTEX_H_ #define _LIBCR_IPC_RWMUTEX_H_ -#include <stdbool.h> - -#include <libmisc/linkedlist.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. * @@ -25,10 +25,10 @@ */ typedef struct { BEGIN_PRIVATE(LIBCR_IPC_RWMUTEX_H); - unsigned nreaders; - bool locked; - bool unpausing; - lm_sll_root waiters; + unsigned nreaders; + bool locked; + bool unpausing; + struct _cr_rwmutex_waiter_list waiters; END_PRIVATE(LIBCR_IPC_RWMUTEX_H); } cr_rwmutex_t; diff --git a/libcr_ipc/include/libcr_ipc/sema.h b/libcr_ipc/include/libcr_ipc/sema.h index cc387f4..236e9af 100644 --- a/libcr_ipc/include/libcr_ipc/sema.h +++ b/libcr_ipc/include/libcr_ipc/sema.h @@ -7,11 +7,11 @@ #ifndef _LIBCR_IPC_SEMA_H_ #define _LIBCR_IPC_SEMA_H_ -#include <stdbool.h> - -#include <libmisc/linkedlist.h> +#include <libmisc/linkedlist.h> /* for SLIST_DECLARE() */ #include <libmisc/private.h> +SLIST_DECLARE(_cr_sema_waiter_list); + /** * A cr_sema_t is a fair unbounded[1] counting semaphore. * @@ -19,9 +19,9 @@ */ typedef struct { BEGIN_PRIVATE(LIBCR_IPC_SEMA_H); - unsigned int cnt; - bool unpausing; - lm_sll_root waiters; + unsigned int cnt; + bool unpausing; + struct _cr_sema_waiter_list waiters; END_PRIVATE(LIBCR_IPC_SEMA_H); } cr_sema_t; diff --git a/libcr_ipc/mutex.c b/libcr_ipc/mutex.c index b0ebe05..1b4e626 100644 --- a/libcr_ipc/mutex.c +++ b/libcr_ipc/mutex.c @@ -5,14 +5,15 @@ */ #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 { - lm_sll_node; cid_t cid; }; +SLIST_DECLARE_NODE(_cr_mutex_waiter_list, struct cr_mutex_waiter); void cr_mutex_lock(cr_mutex_t *mu) { assert(mu); @@ -21,10 +22,10 @@ void cr_mutex_lock(cr_mutex_t *mu) { if (!mu->locked) /* non-blocking fast-path */ mu->locked = true; else { /* blocking slow-path */ - struct cr_mutex_waiter self = { + struct _cr_mutex_waiter_list_node self = { .val = { .cid = cr_getcid(), - }; - lm_sll_push_to_rear(&mu->waiters, &self); + }}; + slist_push_to_rear(&mu->waiters, &self); cr_pause_and_yield(); } assert(mu->locked); @@ -36,8 +37,8 @@ void cr_mutex_unlock(cr_mutex_t *mu) { assert(mu->locked); if (mu->waiters.front) { - cr_unpause(lm_sll_node_cast(struct cr_mutex_waiter, mu->waiters.front)->cid); - lm_sll_pop_from_front(&mu->waiters); + 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 index 6d9422f..b1b7674 100644 --- a/libcr_ipc/rpc.c +++ b/libcr_ipc/rpc.c @@ -4,26 +4,29 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ -#include <string.h> /* for memcpy() */ +#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 { - lm_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 { - lm_sll_node; /* 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); @@ -31,9 +34,8 @@ void _cr_rpc_send_req(struct _cr_rpc *ch, void *req_ptr, size_t req_size, void * assert(resp_ptr); if (ch->waiters.front && ch->waiter_typ != _CR_RPC_REQUESTER) { /* fast-path (still blocks) */ - struct cr_rpc_responder *responder = - lm_sll_node_cast(struct cr_rpc_responder, ch->waiters.front); - lm_sll_pop_from_front(&ch->waiters); + 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. */ @@ -43,12 +45,12 @@ void _cr_rpc_send_req(struct _cr_rpc *ch, void *req_ptr, size_t req_size, void * /* Wait for the responder to set `*resp_ptr`. */ cr_pause_and_yield(); } else { /* blocking slow-path */ - struct cr_rpc_requester self = { + struct _cr_rpc_waiter_list_node self = { .val = { .requester = { .cid = cr_getcid(), .req_ptr = req_ptr, .resp_ptr = resp_ptr, - }; - lm_sll_push_to_rear(&ch->waiters, &self); + }}}; + slist_push_to_rear(&ch->waiters, &self); /* Wait for a responder to both copy our req and sed * `*resp_ptr`. */ cr_pause_and_yield(); @@ -62,22 +64,21 @@ void _cr_rpc_recv_req(struct _cr_rpc *ch, void *req_ptr, size_t req_size, void * assert(ret_requester); if (ch->waiters.front && ch->waiter_typ != _CR_RPC_RESPONDER) { /* non-blocking fast-path */ - struct cr_rpc_requester *requester = - lm_sll_node_cast(struct cr_rpc_requester, ch->waiters.front); - lm_sll_pop_from_front(&ch->waiters); + 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_responder self = { + struct _cr_rpc_waiter_list_node self = { .val = { .responder = { .cid = cr_getcid(), .ptr = req_ptr, - }; - lm_sll_push_to_rear(&ch->waiters, &self); + }}}; + slist_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; + *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 index 4c5da81..191b7fe 100644 --- a/libcr_ipc/rwmutex.c +++ b/libcr_ipc/rwmutex.c @@ -5,31 +5,32 @@ */ #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 { - lm_sll_node; 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 self = { + struct _cr_rwmutex_waiter_list_node self = { .val = { .is_reader = false, .cid = cr_getcid(), - }; - lm_sll_push_to_rear(&mu->waiters, &self); - if (mu->waiters.front != &self.lm_sll_node || mu->locked) + }}; + slist_push_to_rear(&mu->waiters, &self); + if (mu->waiters.front != &self || mu->locked) cr_pause_and_yield(); - assert(mu->waiters.front == &self.lm_sll_node); + assert(mu->waiters.front == &self); /* We now hold the lock (and are mu->waiters.front). */ - lm_sll_pop_from_front(&mu->waiters); + slist_pop_from_front(&mu->waiters); assert(mu->nreaders == 0); mu->locked = true; mu->unpausing = false; @@ -39,24 +40,23 @@ void cr_rwmutex_rlock(cr_rwmutex_t *mu) { assert(mu); cr_assert_in_coroutine(); - struct cr_rwmutex_waiter self = { + struct _cr_rwmutex_waiter_list_node self = { .val = { .is_reader = true, .cid = cr_getcid(), - }; - lm_sll_push_to_rear(&mu->waiters, &self); - if (mu->waiters.front != &self.lm_sll_node || (mu->locked && mu->nreaders == 0)) + }}; + 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.lm_sll_node); + assert(mu->waiters.front == &self); /* We now hold the lock (and are mu->waiters.front). */ - lm_sll_pop_from_front(&mu->waiters); + slist_pop_from_front(&mu->waiters); mu->nreaders++; mu->locked = true; - struct cr_rwmutex_waiter *waiter = - lm_sll_node_cast(struct cr_rwmutex_waiter, mu->waiters.front); - if (waiter && waiter->is_reader) { + struct _cr_rwmutex_waiter_list_node *waiter = mu->waiters.front; + if (waiter && waiter->val.is_reader) { assert(mu->unpausing); - cr_unpause(waiter->cid); + cr_unpause(waiter->val.cid); } else { mu->unpausing = false; } @@ -70,10 +70,9 @@ void cr_rwmutex_unlock(cr_rwmutex_t *mu) { assert(mu->nreaders == 0); assert(!mu->unpausing); if (mu->waiters.front) { - struct cr_rwmutex_waiter *waiter = - lm_sll_node_cast(struct cr_rwmutex_waiter, mu->waiters.front); + struct _cr_rwmutex_waiter_list_node *waiter = mu->waiters.front; mu->unpausing = true; - cr_unpause(waiter->cid); + cr_unpause(waiter->val.cid); } else { mu->locked = false; } @@ -88,11 +87,10 @@ void cr_rwmutex_runlock(cr_rwmutex_t *mu) { mu->nreaders--; if (mu->nreaders == 0 && !mu->unpausing) { if (mu->waiters.front) { - struct cr_rwmutex_waiter *waiter = - lm_sll_node_cast(struct cr_rwmutex_waiter, mu->waiters.front); - assert(!waiter->is_reader); + struct _cr_rwmutex_waiter_list_node *waiter = mu->waiters.front; + assert(!waiter->val.is_reader); mu->unpausing = true; - cr_unpause(waiter->cid); + cr_unpause(waiter->val.cid); } else { mu->locked = false; } diff --git a/libcr_ipc/sema.c b/libcr_ipc/sema.c index cb984b6..f2ac9b6 100644 --- a/libcr_ipc/sema.c +++ b/libcr_ipc/sema.c @@ -5,14 +5,15 @@ */ #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 { - lm_sll_node; - cid_t cid; + cid_t cid; }; +SLIST_DECLARE_NODE(_cr_sema_waiter_list, struct cr_sema_waiter); void cr_sema_signal(cr_sema_t *sema) { assert(sema); @@ -21,8 +22,7 @@ void cr_sema_signal(cr_sema_t *sema) { bool saved = cr_save_and_disable_interrupts(); sema->cnt++; if (sema->waiters.front && !sema->unpausing) { - cr_unpause( - lm_sll_node_cast(struct cr_sema_waiter, sema->waiters.front)->cid); + cr_unpause(sema->waiters.front->val.cid); sema->unpausing = true; } cr_restore_interrupts(saved); @@ -34,8 +34,7 @@ void cr_sema_signal_from_intrhandler(cr_sema_t *sema) { sema->cnt++; if (sema->waiters.front && !sema->unpausing) { - cr_unpause_from_intrhandler( - lm_sll_node_cast(struct cr_sema_waiter, sema->waiters.front)->cid); + cr_unpause_from_intrhandler(sema->waiters.front->val.cid); sema->unpausing = true; } } @@ -46,18 +45,17 @@ void cr_sema_wait(cr_sema_t *sema) { bool saved = cr_save_and_disable_interrupts(); - struct cr_sema_waiter self = { + struct _cr_sema_waiter_list_node self = { .val = { .cid = cr_getcid(), - }; - lm_sll_push_to_rear(&sema->waiters, &self); - if (sema->waiters.front != &self.lm_sll_node || !sema->cnt) + }}; + slist_push_to_rear(&sema->waiters, &self); + if (sema->waiters.front != &self || !sema->cnt) cr_pause_and_yield(); - assert(sema->waiters.front == &self.lm_sll_node && sema->cnt); - lm_sll_pop_from_front(&sema->waiters); + assert(sema->waiters.front == &self && sema->cnt); + slist_pop_from_front(&sema->waiters); sema->cnt--; if (sema->cnt && sema->waiters.front) - cr_unpause( - lm_sll_node_cast(struct cr_sema_waiter, sema->waiters.front)->cid); + 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 a648589..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 diff --git a/libcr_ipc/tests/test_chan.c b/libcr_ipc/tests/test_chan.c index 9b6f018..ee1751f 100644 --- a/libcr_ipc/tests/test_chan.c +++ b/libcr_ipc/tests/test_chan.c @@ -11,39 +11,121 @@ 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 index 43714c9..d08315d 100644 --- a/libcr_ipc/tests/test_mutex.c +++ b/libcr_ipc/tests/test_mutex.c @@ -11,7 +11,7 @@ int counter = 0; -COROUTINE cr_worker(void *_mu) { +COROUTINE worker_cr(void *_mu) { cr_mutex_t *mu = _mu; cr_begin(); @@ -29,8 +29,8 @@ COROUTINE cr_worker(void *_mu) { int main() { cr_mutex_t mu = {}; - coroutine_add("a", cr_worker, &mu); - coroutine_add("b", cr_worker, &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 1e3c471..ee88f47 100644 --- a/libcr_ipc/tests/test_rpc.c +++ b/libcr_ipc/tests/test_rpc.c @@ -14,46 +14,46 @@ 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 index 77e8c7c..e79e779 100644 --- a/libcr_ipc/tests/test_rwmutex.c +++ b/libcr_ipc/tests/test_rwmutex.c @@ -12,7 +12,7 @@ #include "test.h" cr_rwmutex_t mu = {}; -char out[10] = {0}; +char out[10] = {}; size_t len = 0; COROUTINE cr1_reader(void *_mu) { 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_select.c b/libcr_ipc/tests/test_select.c deleted file mode 100644 index 1db645b..0000000 --- a/libcr_ipc/tests/test_select.c +++ /dev/null @@ -1,90 +0,0 @@ -/* libcr_ipc/tests/test_select.c - Tests for <libcr_ipc/select.h> - * - * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com> - * SPDX-License-Identifier: AGPL-3.0-or-later - */ - -#include <libcr/coroutine.h> -#include <libcr_ipc/chan.h> - -#include "test.h" - -CR_CHAN_DECLARE(intchan, int); - -intchan_t ch[10] = {0}; -intchan_t fch = {0}; - -COROUTINE cr_consumer(void *) { - cr_begin(); - - struct cr_select_arg args[11]; - - bool chdone[10] = {0}; - 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(&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(&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(&fch, &send); - args[1] = CR_SELECT_DEFAULT; - ret_arg = cr_select_v(2, args); - test_assert(ret_arg == 0); - - send = 890; - args[0] = CR_SELECT_SEND(&fch, &send); - args[1] = CR_SELECT_DEFAULT; - ret_arg = cr_select_v(2, args); - test_assert(ret_arg == 1); - - cr_end(); -} - -COROUTINE cr_producer(void *_n) { - int n = *(int *)_n; - cr_begin(); - - intchan_send(&ch[n], n); - - cr_end(); -} - -COROUTINE cr_final(void *) { - cr_begin(); - - int ret = intchan_recv(&fch); - printf("ret=%d\n", ret); - test_assert(ret == 567); - - cr_end(); -} - -int main() { - for (int i = 0; i < 10; i++) - coroutine_add("producer", cr_producer, &i); - coroutine_add("consumer", cr_consumer, NULL); - coroutine_add("final", cr_final, NULL); - coroutine_main(); - return 0; -} diff --git a/libcr_ipc/tests/test_sema.c b/libcr_ipc/tests/test_sema.c index e5b22a5..435c01a 100644 --- a/libcr_ipc/tests/test_sema.c +++ b/libcr_ipc/tests/test_sema.c @@ -13,7 +13,7 @@ int counter = 0; -COROUTINE cr_first(void *_sema) { +COROUTINE first_cr(void *_sema) { cr_sema_t *sema = _sema; cr_begin(); @@ -24,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(); @@ -35,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(); @@ -45,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(); @@ -59,16 +59,16 @@ int main() { 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 dee7cb6..f14e46d 100644 --- a/libdhcp/CMakeLists.txt +++ b/libdhcp/CMakeLists.txt @@ -7,7 +7,14 @@ add_library(libdhcp INTERFACE) 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 f65d462..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 PUBLIC 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 1b5bde1..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 803b281..0000000 --- a/libfmt/libmisc.c +++ /dev/null @@ -1,63 +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(), fflush() */ -#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)) { - if (character == '\n') - stdio_putchar_raw('\r'); - 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); - fflush(stdout); -#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); - fflush(stdout); -#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 ba20b26..9dd6a27 100644 --- a/libhw_cr/CMakeLists.txt +++ b/libhw_cr/CMakeLists.txt @@ -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 diff --git a/libhw_cr/host_alarmclock.c b/libhw_cr/host_alarmclock.c index 9eedec2..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 0cb8d30..2ddb054 100644 --- a/libhw_cr/host_include/libhw/host_alarmclock.h +++ b/libhw_cr/host_include/libhw/host_alarmclock.h @@ -7,11 +7,10 @@ #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; diff --git a/libhw_cr/host_include/libhw/host_net.h b/libhw_cr/host_include/libhw/host_net.h index a16ed01..6ff2779 100644 --- a/libhw_cr/host_include/libhw/host_net.h +++ b/libhw_cr/host_include/libhw/host_net.h @@ -13,13 +13,16 @@ #include <libhw/generic/net.h> +/* TCP connection *************************************************************/ + struct _hostnet_tcp_conn { BEGIN_PRIVATE(LIBHW_HOST_NET_H); int fd; uint64_t read_deadline_ns; 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); @@ -27,16 +30,20 @@ struct hostnet_tcp_listener { struct _hostnet_tcp_conn active_conn; END_PRIVATE(LIBHW_HOST_NET_H); }; +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); int fd; uint64_t read_deadline_ns; END_PRIVATE(LIBHW_HOST_NET_H); }; +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 6ed6e46..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 7b3200c..2d45490 100644 --- a/libhw_cr/host_util.c +++ b/libhw_cr/host_util.c @@ -4,9 +4,15 @@ * 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 02c04dc..7e559ef 100644 --- a/libhw_cr/host_util.h +++ b/libhw_cr/host_util.h @@ -7,41 +7,24 @@ #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 c7f5a8f..5c1c7bb 100644 --- a/libhw_cr/rp2040_dma.h +++ b/libhw_cr/rp2040_dma.h @@ -21,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 ***************************************************************/ 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 d181650..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, @@ -138,7 +136,7 @@ void _rp2040_hwspi_init(struct rp2040_hwspi *self, 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); @@ -158,12 +156,14 @@ 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); /* It doesn't *really* matter which aliases we choose: * @@ -198,8 +198,8 @@ static void rp2040_hwspi_readwritev(struct rp2040_hwspi *self, const struct dupl * 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)); @@ -230,8 +230,8 @@ 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. */ @@ -268,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 4951136..8d4effa 100644 --- a/libhw_cr/rp2040_include/libhw/rp2040_hwspi.h +++ b/libhw_cr/rp2040_include/libhw/rp2040_hwspi.h @@ -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 8dda1a1..43c58a3 100644 --- a/libhw_cr/rp2040_include/libhw/w5500.h +++ b/libhw_cr/rp2040_include/libhw/w5500.h @@ -41,19 +41,6 @@ struct _w5500_socket { 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); /* const-after-init */ diff --git a/libhw_cr/w5500.c b/libhw_cr/w5500.c index fa427d9..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,16 +712,15 @@ 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 = {}; if (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,9 +894,9 @@ 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); @@ -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 603f30b..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 PUBLIC 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 02789f5..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 ***********************************************************/ @@ -45,13 +44,10 @@ 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) \ @@ -63,21 +59,10 @@ LO_INTERFACE(alarmclock); 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 62ddbb3..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,82 +50,171 @@ 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); -/* utilities ******************************************************************/ +/* iovec 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_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_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_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) + +/* readwrite =======================================================*/ + +/** + * 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(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(int, close) + LO_FUNC(error, close) LO_INTERFACE(io_closer); #define io_close(c) LO_CALL(c, close) -/** - * 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_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) -/** - * Return bytes-read and bytes-written on success, -errno on error; a - * short read/write *is* an error. - */ -#define io_duplex_readwriter_LO_IFACE \ - LO_FUNC(void, 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) - /* aggregate interfaces *******************************************************/ #define io_readwriter_LO_IFACE \ diff --git a/libhw_generic/include/libhw/generic/net.h b/libhw_generic/include/libhw/generic/net.h index 4af574b..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) \ @@ -90,10 +49,26 @@ LO_INTERFACE(net_stream_listener); LO_FUNC(void, set_read_deadline, uint64_t ns_since_boot) 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) \ \ @@ -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_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/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 fdbe949..7388a91 100644 --- a/libmisc/CMakeLists.txt +++ b/libmisc/CMakeLists.txt @@ -3,23 +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 PUBLIC 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 a264144..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. */ -[[gnu::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. + */ -[[gnu::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 index 8adef66..b6ff688 100644 --- a/libmisc/include/libmisc/linkedlist.h +++ b/libmisc/include/libmisc/linkedlist.h @@ -7,40 +7,102 @@ #ifndef _LIBMISC_LINKEDLIST_H_ #define _LIBMISC_LINKEDLIST_H_ -#include <libmisc/assert.h> -#include <libmisc/macro.h> +/* low-level (intrusive) singly linked list ***********************************/ -/* singly linked list *********************************************************/ +struct _slist_node { + struct _slist_node *rear; +}; -typedef struct _lm_sll_node { - struct _lm_sll_node *rear; -} lm_sll_node; +struct _slist_root { + struct _slist_node *front, *rear; +}; -typedef struct { - lm_sll_node *front, *rear; -} lm_sll_root; +void _slist_push_to_rear(struct _slist_root *root, struct _slist_node *node); +void _slist_pop_from_front(struct _slist_root *root); -#define lm_sll_node_cast(node_typ, node_ptr) \ - LM_CAST_FIELD_TO_STRUCT(node_typ, lm_sll_node, node_ptr) +/* low-level (intrusive) doubly linked list ***********************************/ -void lm_sll_push_to_rear(lm_sll_root *root, lm_sll_node *node); -void lm_sll_pop_from_front(lm_sll_root *root); +struct _dlist_node { + struct _dlist_node *front, *rear; +}; -/* doubly linked list *********************************************************/ +struct _dlist_root { + struct _dlist_node *front, *rear; +}; -typedef struct _lm_dll_node { - struct _lm_dll_node *front, *rear; -} lm_dll_node; +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); -typedef struct { - lm_dll_node *front, *rear; -} lm_dll_root; +/* singly linked list (non-intrusive) *****************************************/ -#define lm_dll_node_cast(node_typ, node_ptr) \ - LM_CAST_FIELD_TO_STRUCT(node_typ, lm_dll_node, node_ptr) +#define SLIST_DECLARE(NAME) \ + struct NAME##_node; \ + struct NAME { \ + struct NAME##_node *front, *rear; \ + struct NAME *_slist_root_typ[0]; \ + } -void lm_dll_push_to_rear(lm_dll_root *root, lm_dll_node *node); -void lm_dll_remove(lm_dll_root *root, lm_dll_node *node); -void lm_dll_pop_from_front(lm_dll_root *root); +#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 6cb15fb..0f9fc3f 100644 --- a/libmisc/include/libmisc/macro.h +++ b/libmisc/include/libmisc/macro.h @@ -7,16 +7,30 @@ #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]] -/* types */ +/* C: types *******************************************************************/ -#define LM_ARRAY_LEN(ary) (sizeof(ary)/sizeof((ary)[0])) +/* 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. */ \ @@ -26,7 +40,7 @@ : NULL; \ }) -/* numeric */ +/* C: numeric *****************************************************************/ #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` */ @@ -34,12 +48,12 @@ #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 @@ -47,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 @@ -66,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 *************************************************************/ + +/* 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()() -/* BUG: LM_FOREACH_TUPLE maxes out at 1024 tuples. */ +/** + * 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 index f846f57..65a71bc 100644 --- a/libmisc/include/libmisc/map.h +++ b/libmisc/include/libmisc/map.h @@ -7,7 +7,6 @@ #ifndef _LIBMISC_MAP_H_ #define _LIBMISC_MAP_H_ -#include <stdbool.h> #include <stddef.h> /* for size_t */ #include <stdint.h> /* for uint8_t */ @@ -15,12 +14,14 @@ /* Type ***********************************************************************/ +DLIST_DECLARE(_map_kv_list); + struct _map { - size_t len; - size_t nbuckets; - lm_dll_root *buckets; + size_t len; + size_t nbuckets; + struct _map_kv_list *buckets; - unsigned iterating; + unsigned int iterating; size_t sizeof_kv; size_t offsetof_k, sizeof_k; @@ -30,26 +31,27 @@ struct _map { /** * MAP_DECLARE(MAPNAME, KEY_T, VAL_T) declares `struct MAPNAME`. */ -#define MAP_DECLARE(MAPNAME, KEY_T, VAL_T) \ - struct MAPNAME { \ - struct _map core; \ - struct { \ - lm_dll_node; \ - uint8_t flags; \ - KEY_T key; \ - VAL_T val; \ - } kv_typ[0]; \ +#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].key); \ - (M)->core.sizeof_v = sizeof((M)->kv_typ[0].val); \ - (M)->core.offsetof_k = offsetof(typeof((M)->kv_typ[0]), key); \ - (M)->core.offsetof_v = offsetof(typeof((M)->kv_typ[0]), val); \ - } \ -} while(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 ********************************************************************/ @@ -62,10 +64,10 @@ struct _map { * 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].key) _k = K; \ - (typeof((M)->kv_typ[0].val)*)_map_load(&(M)->core, &_k); \ +#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); @@ -73,10 +75,10 @@ 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].key) _k = K; \ - _map_del(&(M)->core, &_k); \ +#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); @@ -84,11 +86,11 @@ 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].key) _k = K; \ - typeof((M)->kv_typ[0].val) _v = __VA_ARGS__; \ - (typeof((M)->kv_typ[0].val)*)_map_store(&(M)->core, &_k, &_v); \ +#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); @@ -100,18 +102,13 @@ void _map_free(struct _map *m); /* Iteration ******************************************************************/ -struct _map_kv { - lm_dll_node; - uint8_t flags; -}; - struct _map_iter { - struct _map *m; - void *keyp; - void **valpp; + struct _map *m; + void *keyp; + void **valpp; - size_t i; - struct _map_kv *kv; + size_t i; + struct _map_kv_list_node *kv; }; /** @@ -121,15 +118,15 @@ struct _map_iter { * ... * } * - * It is safe to mutate the map with map_store() and, map_del() while + * 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].key) KNAME; _once_##CNT;) \ - for (typeof((M)->kv_typ[0].val) *VNAME; _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); \ 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 5518d1f..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 {) struct {} LM_CAT2_(_PRIVATE_FORCE_SEMICOLON_, __COUNTER__) -#define END_PRIVATE(name) LM_IF(IS_IMPLEMENTATION_FOR(name))(struct {} LM_CAT2_(_PRIVATE_FORCE_SEMICOLON_, __COUNTER__))(} LM_CAT2_(_PRIVATE_, __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 7ef238b..ca16f42 100644 --- a/libmisc/include/libmisc/rand.h +++ b/libmisc/include/libmisc/rand.h @@ -7,40 +7,12 @@ #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 = (((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"); -} +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 index 5fe0977..71a0aa9 100644 --- a/libmisc/linkedlist.c +++ b/libmisc/linkedlist.c @@ -6,11 +6,13 @@ #include <stddef.h> /* for NULL */ +#include <libmisc/assert.h> + #include <libmisc/linkedlist.h> /* singly linked list *********************************************************/ -void lm_sll_push_to_rear(lm_sll_root *root, lm_sll_node *node) { +void _slist_push_to_rear(struct _slist_root *root, struct _slist_node *node) { assert(root); node->rear = NULL; if (root->rear) @@ -20,7 +22,7 @@ void lm_sll_push_to_rear(lm_sll_root *root, lm_sll_node *node) { root->rear = node; } -void lm_sll_pop_from_front(lm_sll_root *root) { +void _slist_pop_from_front(struct _slist_root *root) { assert(root); assert(root->front); root->front = root->front->rear; @@ -30,7 +32,7 @@ void lm_sll_pop_from_front(lm_sll_root *root) { /* doubly linked list *********************************************************/ -void lm_dll_push_to_rear(lm_dll_root *root, lm_dll_node *node) { +void _dlist_push_to_rear(struct _dlist_root *root, struct _dlist_node *node) { assert(root); assert(node); node->front = root->rear; @@ -42,7 +44,7 @@ void lm_dll_push_to_rear(lm_dll_root *root, lm_dll_node *node) { root->rear = node; } -void lm_dll_remove(lm_dll_root *root, lm_dll_node *node) { +void _dlist_remove(struct _dlist_root *root, struct _dlist_node *node) { assert(root); assert(node); if (node->front) @@ -55,8 +57,8 @@ void lm_dll_remove(lm_dll_root *root, lm_dll_node *node) { root->rear = node->front; } -void lm_dll_pop_from_front(lm_dll_root *root) { +void _dlist_pop_from_front(struct _dlist_root *root) { assert(root); assert(root->front); - lm_dll_remove(root, 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 index bb8a2d2..d1b2a57 100644 --- a/libmisc/map.c +++ b/libmisc/map.c @@ -7,8 +7,9 @@ #include <stdlib.h> #include <string.h> -#include <libmisc/hash.h> +#include <libmisc/alloc.h> #include <libmisc/assert.h> +#include <libmisc/hash.h> #include <libmisc/map.h> #define FLAG_ITER (UINT8_C(1)<<0) @@ -16,14 +17,19 @@ /* Internal utilities *********************************************************/ -#define cast(n) lm_dll_node_cast(struct _map_kv, n) +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 *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 *kv) { +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; @@ -31,8 +37,8 @@ static inline void *_map_kv_valp(struct _map *m, struct _map_kv *kv) { static inline void _map_lookup(struct _map *m, void *keyp, hash_t *ret_hash, - lm_dll_root **ret_bucket, - struct _map_kv **ret_kv) { + struct _map_kv_list **ret_bucket, + struct _map_kv_list_node **ret_kv) { assert(m); assert(keyp); assert(ret_hash); @@ -45,8 +51,8 @@ static inline void _map_lookup(struct _map *m, void *keyp, return; } *ret_bucket = &m->buckets[*ret_hash % m->nbuckets]; - for (struct _map_kv *kv = cast((*ret_bucket)->front); kv; kv = cast(kv->rear)) { - if (!(kv->flags & FLAG_DEL) && + 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; @@ -58,13 +64,13 @@ static inline void _map_lookup(struct _map *m, void *keyp, static inline void _map_resize(struct _map *m, size_t new_nbuckets) { assert(m); assert(new_nbuckets); - lm_dll_root *new_buckets = calloc(new_nbuckets, sizeof(lm_dll_root)); + 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 *kv = cast(m->buckets[i].front); - lm_dll_pop_from_front(&m->buckets[i]); + 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); - lm_dll_push_to_rear(&new_buckets[h % new_nbuckets], kv); + dlist_push_to_rear(&new_buckets[h % new_nbuckets], kv); } } m->nbuckets = new_nbuckets; @@ -91,8 +97,8 @@ void *_map_load(struct _map *m, void *keyp) { assert(keyp); hash_t h; - lm_dll_root *bucket; - struct _map_kv *kv; + struct _map_kv_list *bucket; + struct _map_kv_list_node *kv; _map_lookup(m, keyp, &h, &bucket, &kv); if (!kv) @@ -105,16 +111,16 @@ bool _map_del(struct _map *m, void *keyp) { assert(keyp); hash_t h; - lm_dll_root *bucket; - struct _map_kv *kv; + struct _map_kv_list *bucket; + struct _map_kv_list_node *kv; _map_lookup(m, keyp, &h, &bucket, &kv); if (!kv) return false; - if (kv->flags & FLAG_ITER) { - kv->flags |= FLAG_DEL; + if (kv->val.flags & FLAG_ITER) { + kv->val.flags |= FLAG_DEL; } else { - lm_dll_remove(bucket, kv); + dlist_remove(bucket, kv); free(kv); } m->len--; @@ -127,12 +133,12 @@ void *_map_store(struct _map *m, void *keyp, void *valp) { assert(valp); hash_t h; - lm_dll_root *bucket; - struct _map_kv *old; + struct _map_kv_list *bucket; + struct _map_kv_list_node *old; _map_lookup(m, keyp, &h, &bucket, &old); if (old) { - lm_dll_remove(bucket, old); + dlist_remove(bucket, old); free(old); m->len--; } @@ -141,10 +147,10 @@ void *_map_store(struct _map *m, void *keyp, void *valp) { h = hash(keyp, m->sizeof_k); bucket = &m->buckets[h % m->nbuckets]; } - struct _map_kv *kv = calloc(1, m->sizeof_kv); + 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); - lm_dll_push_to_rear(bucket, kv); + dlist_push_to_rear(bucket, kv); return _map_kv_valp(m, kv); } @@ -153,8 +159,8 @@ void _map_free(struct _map *m) { for (size_t i = 0; i < m->nbuckets; i++) { while (m->buckets[i].front) { - struct _map_kv *kv = cast(m->buckets[i].front); - lm_dll_pop_from_front(&m->buckets[i]); + struct _map_kv_list_node *kv = m->buckets[i].front; + dlist_pop_from_front(&m->buckets[i]); free(kv); } } @@ -199,14 +205,14 @@ bool _map_iter_next(struct _map_iter *state) { return false; while (!state->m->buckets[state->i].front) state->i++; - state->kv = cast(state->m->buckets[state->i].front); + state->kv = state->m->buckets[state->i].front; } else { - struct _map_kv *old_kv = state->kv; - state->kv = cast(old_kv->rear); + struct _map_kv_list_node *old_kv = state->kv; + state->kv = old_kv->rear; - old_kv->flags &= ~FLAG_ITER; - if (old_kv->flags & FLAG_DEL) { - lm_dll_remove(&state->m->buckets[state->i], old_kv); + 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); } @@ -214,10 +220,10 @@ bool _map_iter_next(struct _map_iter *state) { state->i++; if (state->i == state->m->nbuckets) return false; - state->kv = cast(state->m->buckets[state->i].front); + state->kv = state->m->buckets[state->i].front; } } - state->kv->flags |= FLAG_ITER; + 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 1320eb3..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. */ @@ -50,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/libobj/tests/test_obj.c b/libmisc/tests/test_obj.c index d6861dc..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" @@ -28,19 +28,19 @@ 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 f18b018..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,18 +6,18 @@ #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_FUNC(size_t, read, void *, size_t) LO_INTERFACE(reader); #define writer_LO_IFACE \ - LO_FUNC(ssize_t, write, void *, size_t) + LO_FUNC(size_t, write, void *, size_t) LO_INTERFACE(writer); #define read_writer_LO_IFACE \ @@ -25,23 +25,17 @@ LO_INTERFACE(writer); LO_NEST(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_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 e4d8095..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 PUBLIC 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 7a9041e..0000000 --- a/libobj/include/libobj/obj.h +++ /dev/null @@ -1,156 +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) \ - extern int LM_CAT2_(_HIDDEN_BOGUS_, __COUNTER__) -#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, \ - }; \ - } \ - extern int LM_CAT2_(_LO_FORCE_SEMICOLON_, __COUNTER__) - -/** - * 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/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 84c2e72..0000000 --- a/notes.md +++ /dev/null @@ -1,193 +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 - ----- - -https://electronics.stackexchange.com/questions/684221/usb-c-on-off-switch-design-that-is-pd-compatible-off-similar-to-cold-plugging - ----- - -OpenBMC - -~$300 price range: - - Motherboards with a BMC - - Old proprietary IP-KVM - - TinyPilot KVM - - PiKVM -~$60 price range: - - DIY PiKVM - - JetKVM -~$30 price range - - Sipeed NanoKVM - - https://github.com/stefanklug/picoKVM (~$20 Arduino, ~$5 HDMI - capture), non-IP - ----- - -https://hackaday.com/2022/08/26/bit-banged-ethernet-on-the-raspberry-pi-pico/ |