diff options
179 files changed, 7216 insertions, 4703 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/CMakeLists.txt b/CMakeLists.txt index 5ce49ed..2d40b05 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -44,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) @@ -159,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 e5e59b2..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 $^ $@ @@ -57,18 +69,28 @@ 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,$(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 \ + 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: @@ -77,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` ########################################################## @@ -108,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 @@ -27,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 @@ -153,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 19d3a3b..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) ) } @@ -132,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 97f1769..f151642 100644 --- a/build-aux/measurestack/analyze.py +++ b/build-aux/measurestack/analyze.py @@ -268,6 +268,7 @@ class SkipModel(typing.NamedTuple): 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]: ... @@ -448,6 +449,9 @@ def _make_graph( raise ValueError(f"unknown caller: {caller}") if callee == QName("__indirect_call"): callees, missing_ok = app.indirect_callees(elem) + 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 @@ -469,6 +473,9 @@ def _make_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 = {} diff --git a/build-aux/measurestack/app_main.py b/build-aux/measurestack/app_main.py index 4fdfd5c..884aeee 100644 --- a/build-aux/measurestack/app_main.py +++ b/build-aux/measurestack/app_main.py @@ -67,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,9 +90,6 @@ def main( QName("__assert_msg_fail"), ]: return 1, False - for prefix in ["fmt_print_", "_fmt_print_"]: - if str(name.base()).startswith(prefix): - return 1, False if str(name.base()).endswith("_putb"): return 1, False return 0, False 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 6fc81ec..a921407 100644 --- a/build-aux/measurestack/app_plugins.py +++ b/build-aux/measurestack/app_plugins.py @@ -39,15 +39,35 @@ 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]: @@ -60,9 +80,12 @@ class LibMiscPlugin: 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*[,)].*" + 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_call_objcall = re.compile(r"LO_CALL\((?P<obj>[^,]+), (?P<meth>[^,)]+)[,)].*") + re_lo_call = re.compile(r".*\bLO_CALL\((?P<obj>[^,]+), (?P<meth>[^,)]+)[,)].*") objcalls: dict[str, set[QName]] # method_name => {method_impls} @@ -114,14 +137,25 @@ class LibMiscPlugin: 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 := self.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 @@ -157,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: @@ -172,10 +209,13 @@ class LibHWPlugin: ]: if f"{fn}(" in line: return self.libmisc.indirect_callees(loc, f"LO_CALL(x, {fn[3:]})") - if "io_read(" in line: - return self.libmisc.indirect_callees(loc, "LO_CALL(x, readv)") - if "io_writev(" in line: - return self.libmisc.indirect_callees(loc, "LO_CALL(x, writev)") + 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"), @@ -215,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: @@ -237,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: @@ -299,7 +345,7 @@ 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 ######################################################### @@ -335,6 +381,9 @@ class Lib9PPlugin: def extra_nodes(self) -> typing.Collection[Node]: return [] + def mutate_node(self, node: Node) -> None: + pass + re_table_call = re.compile( r"\s*_lib9p_(?P<meth>validate|unmarshal|marshal)\(.*(?P<grp>[RT])msg.*\);\s*" ) @@ -372,11 +421,13 @@ 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 @@ -410,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"), @@ -468,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 @@ -482,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"})] @@ -502,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"}), @@ -560,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)"} @@ -576,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"}), @@ -596,18 +656,34 @@ 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 + 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( @@ -686,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: @@ -702,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 @@ -727,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"}), @@ -741,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), @@ -771,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: @@ -801,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/testutil.py b/build-aux/measurestack/testutil.py index 751e57f..3c32134 100644 --- a/build-aux/measurestack/testutil.py +++ b/build-aux/measurestack/testutil.py @@ -69,6 +69,9 @@ class NopPlugin: 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] diff --git a/build-aux/measurestack/util.py b/build-aux/measurestack/util.py index 0af3d02..c94ce07 100644 --- a/build-aux/measurestack/util.py +++ b/build-aux/measurestack/util.py @@ -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 0e904ab..0000000 --- a/cmd/sbc_harness/CMakeLists.txt +++ /dev/null @@ -1,81 +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 - libusb - libdhcp - libhw_cr - lib9p_srv - lib9p_util -) -pico_minimize_runtime(sbc_harness_objs) -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/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() 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 3c3fa16..0000000 --- a/cmd/sbc_harness/fs_harness_flash_bin.c +++ /dev/null @@ -1,307 +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); - - 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 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); - - log_infoln("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); - } - log_debugln("... 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); - - log_infoln("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); - } - log_debugln("... 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; - - log_infoln("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); - } - log_debugln("... 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_srv_stat flash_file_stat(struct flash_file *self, struct lib9p_srv_ctx *ctx) { - assert(self); - assert(ctx); - - return (struct lib9p_srv_stat){ - .qid = flash_file_qid(self), - .mode = LIB9P_DM_EXCL|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), - }; -} -static void flash_file_wstat(struct flash_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"); -} -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 97246ea..0000000 --- a/cmd/sbc_harness/fs_harness_uptime_txt.c +++ /dev/null @@ -1,150 +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 <libhw/generic/alarmclock.h> -#include <util9p/static.h> -#include <libmisc/alloc.h> /* for heap_alloc(), free() */ -#include <libmisc/fmt.h> /* for fmt_snprint() */ - -#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_srv_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_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), - }; -} -static void uptime_file_wstat(struct uptime_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"); -} -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 = heap_alloc(1, 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 = fmt_snprint(self->buf, sizeof(self->buf), now, "ns\n"); - } - - 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/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/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 ca23462..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,8 +37,6 @@ /* 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 */ @@ -49,8 +47,6 @@ #define CONFIG_9P_SRV_DEBUG 1 /* bool */ -#define CONFIG_9P_SRV_MAX_MSG_SIZE ((4*1024)+24) - /** * This max-msg-size is sized so that a Twrite message can return * 8KiB of data. @@ -69,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) @@ -96,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 */ @@ -110,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 2c7c02a..18be9b5 100644 --- a/cmd/sbc_harness/config/tusb_config.h +++ b/flashimg/cpu_main/config/tusb_config.h @@ -1,4 +1,4 @@ -/* 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-2025 Luke T. Shumaker <lukeshu@lukeshu.com> * SPDX-License-Identifier: AGPL-3.0-or-later 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..02e4171 --- /dev/null +++ b/flashimg/cpu_main/fs_harness_uptime_txt.c @@ -0,0 +1,157 @@ +/* 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); + + *out = ((struct lib9p_srv_stat){ + .qid = uptime_file_qid(self), + .mode = 0444, + .atime_sec = UTIL9P_ATIME, + .mtime_sec = UTIL9P_MTIME, + .size = 0, + .name = lib9p_str(self->name), + .owner_uid = { .name = lib9p_str("root"), .num = 0 }, + .owner_gid = { .name = lib9p_str("root"), .num = 0 }, + .last_modifier_uid = { .name = lib9p_str("root"), .num = 0 }, + .extension = lib9p_str(NULL), + }); + return ERROR_NULL; +} +error 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 143bae0..a471d2a 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,10 +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 */ #include <libmisc/hash.h> @@ -30,10 +30,13 @@ #include <libmisc/log.h> /* local headers */ -#include "usb_keyboard.h" #include "static.h" +#include "usb_keyboard.h" + +/* 9P files */ +#include <util9p/static.h> #include "fs_harness_flash_bin.h" -#include "fs_harness_uptime_txt.h" +#include <fs_harness_uptime_txt.h> /* configuration **************************************************************/ @@ -95,11 +98,11 @@ static struct lib9p_srv_file root = .data_end = _binary_static_Documentation_harness_uptime_txt_txt_end), ), STATIC_DIR("harness", + API_FILE("flash.bin", + flash), STATIC_FILE("rom.bin", .data_start = (void*)0x00000000, .data_size = 16*1024), - API_FILE("flash.bin", - flash), API_FILE("uptime.txt", uptime), // TODO: system.log @@ -114,8 +117,8 @@ static 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 ***********************************************************************/ @@ -149,7 +152,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 +160,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); @@ -204,7 +207,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){{ @@ -226,28 +229,66 @@ COROUTINE init_cr(void *) { 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: ")); */ log_infoln("==================================================================="); - coroutine_add("init", init_cr, NULL); + 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/cmd/sbc_harness/static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS/dhcp.bsd3-mit.txt b/flashimg/cpu_main/static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS/dhcp.bsd3-mit.txt index 0277bc8..0277bc8 120000 --- a/cmd/sbc_harness/static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS/dhcp.bsd3-mit.txt +++ b/flashimg/cpu_main/static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS/dhcp.bsd3-mit.txt 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/cmd/sbc_harness/tusb_log.c b/flashimg/cpu_main/tusb_log.c index 09fe755..aacec87 100644 --- a/cmd/sbc_harness/tusb_log.c +++ b/flashimg/cpu_main/tusb_log.c @@ -1,4 +1,4 @@ -/* sbc_harness/tusb_log.c - Logger for tusb_config.h +/* 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 diff --git a/cmd/sbc_harness/usb_keyboard.c b/flashimg/cpu_main/usb_keyboard.c index 0573dba..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; @@ -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/lib9p/CMakeLists.txt b/lib9p/CMakeLists.txt index 2a2f858..9ef8465 100644 --- a/lib9p/CMakeLists.txt +++ b/lib9p/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(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 @@ -14,10 +21,18 @@ target_link_libraries(lib9p_core INTERFACE 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 @@ -48,8 +63,10 @@ if (ENABLE_TESTS) add_lib9p_executable("testclient-sess") add_lib9p_test("./testclient-sess") + add_lib9p_executable("testclient-hangup") + add_lib9p_test("./testclient-hangup") + set(cfg_matrix - "CONFIG_9P_SRV_DEBUG;[0;1]" "CONFIG_9P_ENABLE_9P2000;[0;1]" "CONFIG_9P_ENABLE_9P2000_u;[0;1]" "CONFIG_9P_ENABLE_9P2000_e;[0;1]" @@ -57,11 +74,15 @@ if (ENABLE_TESTS) "CONFIG_9P_ENABLE_9P2000_p9p;[0;1]" ) function(add_compile_test n defs) - add_executable("test_compile${n}" "tests/test_compile.c") - target_link_libraries("test_compile${n}" lib9p_srv) + 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}") - # Don't bother running it (don't bother calling add_test()) endfunction() apply_matrix(add_compile_test "${cfg_matrix}") endif() diff --git a/lib9p/core.c b/lib9p/core.c index 464b31d..ae2d3ca 100644 --- a/lib9p/core.c +++ b/lib9p/core.c @@ -4,8 +4,7 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ -#include <stdarg.h> /* for va_* */ -#include <string.h> /* for strlen(), strnlen(), strncpy(), memcmp(), memset() */ +#include <string.h> /* for strlen(), strnlen(), strncpy(), memcmp(), memset() */ #include <libmisc/assert.h> /* for assert() */ #include <libmisc/endian.h> /* for uint32le_decode() */ @@ -17,7 +16,7 @@ /* strings ********************************************************************/ -struct lib9p_s lib9p_str(char *s) { +struct lib9p_s lib9p_str(const char *s) { if (!s) return (struct lib9p_s){}; return (struct lib9p_s){ @@ -25,7 +24,7 @@ struct lib9p_s lib9p_str(char *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){}; return (struct lib9p_s){ @@ -46,35 +45,14 @@ bool lib9p_str_eq(struct lib9p_s a, struct lib9p_s b) { (a.len == 0 || memcmp(a.utf8, b.utf8, a.len) == 0); } -/* ctx ************************************************************************/ - -void lib9p_ctx_clear_error(struct lib9p_ctx *ctx) { - assert(ctx); -#if CONFIG_9P_ENABLE_9P2000_u || CONFIG_9P_ENABLE_9P2000_L - ctx->err_num = 0; -#endif - ctx->err_msg[0] = '\0'; -} - -bool lib9p_ctx_has_error(struct lib9p_ctx *ctx) { - assert(ctx); - return ctx->err_msg[0]; -} - /* 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 *************************************************************/ @@ -103,36 +81,38 @@ const char *lib9p_msgtype_str(enum lib9p_version ver, enum lib9p_msg_type typ) { /* main message functions *****************************************************/ void fmt_print_lib9p_msg(lo_interface fmt_dest w, struct lib9p_ctx *ctx, enum lib9p_msg_type typ, void *body) { - assert(ctx); - assert_ver(ctx->version); - assert_typ(typ); - assert(_lib9p_table_msg[ctx->version][typ].print); - _lib9p_table_msg[ctx->version][typ].print(w, ctx, body); -} - -#define _lib9p_validate(LOW_TYP_BIT, ERRMSG, TABLE) do { \ - assert_ver(ctx->version); \ - /* Inspect the first 5 bytes ourselves. */ \ - uint32_t net_size = uint32le_decode(net_bytes); \ - if (net_size < 5) \ - return lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "message is impossibly short"); \ - uint8_t typ = net_bytes[4]; \ - if (typ % 2 != LOW_TYP_BIT) \ - return lib9p_error(ctx, LIB9P_ERRNO_L_EOPNOTSUPP, ERRMSG ": message_type=", lib9p_msgtype_str(ctx->version, typ)); \ - struct _lib9p_recv_tentry tentry = TABLE[ctx->version][typ/2]; \ - if (!tentry.validate) \ - return lib9p_error(ctx, LIB9P_ERRNO_L_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); \ + assert(ctx); + assert_ver(ctx->version); + assert_typ(typ); + assert(_lib9p_table_msg[ctx->version][typ].print); + _lib9p_table_msg[ctx->version][typ].print(w, ctx, body); +} + +#define _lib9p_validate(LOW_TYP_BIT, ERRMSG, TABLE) do { \ + assert_ver(ctx->version); \ + /* Inspect the first 5 bytes ourselves. */ \ + uint32_t net_size = uint32le_decode(net_bytes); \ + if (net_size < 5) \ + return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "message is impossibly short")); \ + uint8_t typ = net_bytes[4]; \ + if (typ % 2 != LOW_TYP_BIT) \ + return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EOPNOTSUPP, ERRMSG ": message_type=", \ + lib9p_msgtype_str(ctx->version, typ))); \ + struct _lib9p_recv_tentry tentry = TABLE[ctx->version][typ/2]; \ + if (!tentry.validate) \ + return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EOPNOTSUPP, "unknown message type: ", \ + lib9p_msgtype_str(ctx->version, typ), \ + " (protocol_version=", lib9p_version_str(ctx->version), ")")); \ + \ + /* Now use the message-type-specific tentry to process the whole thing. */ \ + return tentry.validate(ctx, net_size, net_bytes); \ } while (0) -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) { _lib9p_validate(0, "expected a T-message but got an R-message", _lib9p_table_Tmsg_recv); } -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) { _lib9p_validate(1, "expected an R-message but got a T-message", _lib9p_table_Rmsg_recv); } @@ -172,20 +152,20 @@ void lib9p_Rmsg_unmarshal(struct lib9p_ctx *ctx, uint8_t *net_bytes, struct _lib9p_send_tentry tentry = TABLE[ctx->version][typ/2]; \ assert(tentry.marshal); \ \ - bool ret_erred = tentry.marshal(ctx, body, &_ret); \ + 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_erred; \ + return ret_err; \ } while (0) -bool lib9p_Tmsg_marshal(struct lib9p_ctx *ctx, enum lib9p_msg_type typ, void *body, +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); } -bool lib9p_Rmsg_marshal(struct lib9p_ctx *ctx, enum lib9p_msg_type typ, void *body, +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); } @@ -193,14 +173,14 @@ bool lib9p_Rmsg_marshal(struct lib9p_ctx *ctx, enum lib9p_msg_type typ, void *bo /* `struct lib9p_stat` helpers ************************************************/ #if _LIB9P_ENABLE_stat -bool lib9p_stat_validate(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes, +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) { - ssize_t host_size = _lib9p_stat_validate(ctx, net_size, net_bytes, ret_net_size); - if (host_size < 0) - return true; + 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 = (size_t)host_size; - return false; + *ret_host_size = host_size.size_t; + return ERROR_NULL; } void lib9p_stat_unmarshal(struct lib9p_ctx *ctx, uint8_t *net_bytes, @@ -213,7 +193,7 @@ uint32_t lib9p_stat_marshal(struct lib9p_ctx *ctx, uint32_t max_net_size, struct struct lib9p_ctx _ctx = *ctx; _ctx.max_msg_size = max_net_size; - struct iovec iov = {}; + struct wr_iovec iov = {}; struct _marshal_ret ret = { .net_iov_cnt = 1, .net_iov = &iov, 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 60ceb70..ab34387 100644 --- a/lib9p/core_gen/c.py +++ b/lib9p/core_gen/c.py @@ -23,17 +23,16 @@ def gen_c(versions: set[str], typs: list[idl.UserType]) -> str: ret = f"""/* Generated by `{' '.join(sys.argv)}`. DO NOT EDIT! */ -#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" """ # utilities ################################################################ ret += """ @@ -176,7 +175,7 @@ def gen_c(versions: set[str], typs: list[idl.UserType]) -> str: ret += f""" {cutil.ifdef_push(1, c9util.ver_ifdef(next(typ for typ in typs if typ.typname == 'stat').in_versions)).rstrip()} -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) {{ +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) {{ 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_fmt_print.py b/lib9p/core_gen/c_fmt_print.py index eaacddb..7a0a9d3 100644 --- a/lib9p/core_gen/c_fmt_print.py +++ b/lib9p/core_gen/c_fmt_print.py @@ -112,7 +112,7 @@ def gen_c_fmt_print(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 += 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" diff --git a/lib9p/core_gen/c_marshal.py b/lib9p/core_gen/c_marshal.py index bddf55f..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,7 +365,10 @@ 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) @@ -380,11 +383,12 @@ def gen_c_marshal(versions: set[str], typs: list[idl.UserType]) -> str: ret += "\tif (needed_size > (uint64_t)(ctx->max_msg_size)) {\n" else: ret += "\tif (needed_size > ctx->max_msg_size) {\n" - if isinstance(typ, idl.Message): # SPECIAL (disable for stat) - ret += f'\t\tlib9p_error(ctx, {c9util.IDENT("ERRNO_L_ERANGE")}, "{typ.typname} message too large to marshal into ",\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\treturn true;\n" ret += "\t}\n" # Pass 2 - write data @@ -396,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_validate.py b/lib9p/core_gen/c_validate.py index 9c55d8d..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_error(ctx, {c9util.IDENT("ERRNO_L_EBADMSG")}, "message is too short for content (", net_offset, " > ", net_size, ")");\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" @@ -193,7 +193,7 @@ def gen_c_validate(versions: set[str], typs: list[idl.UserType]) -> str: 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_error(ctx, {c9util.IDENT("ERRNO_L_EBADMSG")}, "{path} value is wrong: actual:", (base10, {act}), " != correct:", (base10, {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 @@ -207,15 +207,15 @@ def gen_c_validate(versions: set[str], typs: list[idl.UserType]) -> str: 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_error(ctx, {c9util.IDENT("ERRNO_L_EBADMSG")}, "{path} value is too large: ", (base10, {act}), " > ", (base10, {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_error(ctx, {c9util.IDENT("ERRNO_L_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" + 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, @@ -271,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)] @@ -291,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 acf8415..eba1492 100644 --- a/lib9p/core_gen/h.py +++ b/lib9p/core_gen/h.py @@ -162,7 +162,6 @@ 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 - """ id2typ: dict[int, idl.Message] = {} for msg in [msg for msg in typs if isinstance(msg, idl.Message)]: @@ -170,8 +169,6 @@ def gen_h(versions: set[str], typs: list[idl.UserType]) -> str: ret += """ /* config *********************************************************************/ - -#include "config.h" """ for ver in sorted(versions): ret += "\n" @@ -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" diff --git a/lib9p/core_generated.c b/lib9p/core_generated.c index 81ace7d..5acc551 100644 --- a/lib9p/core_generated.c +++ b/lib9p/core_generated.c @@ -1,16 +1,15 @@ /* Generated by `lib9p/core.gen lib9p/idl/0000-uninitialized.9p lib9p/idl/2002-9P2000.9p lib9p/idl/2003-9P2000.p9p.9p lib9p/idl/2005-9P2000.u.9p lib9p/idl/2010-9P2000.L.9p lib9p/idl/2012-9P2000.e.9p`. DO NOT EDIT! */ -#include <stddef.h> /* for size_t */ -#include <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" /* utilities ******************************************************************/ #if CONFIG_9P_ENABLE_9P2000 @@ -222,27 +221,27 @@ static const lib9p_lock_flags_t lock_flags_masks[LIB9P_VER_NUM] = { /* 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_error(ctx, LIB9P_ERRNO_L_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 (!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]) @@ -253,16 +252,16 @@ 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); + size_t host_size = sizeof(struct lib9p_stat); uint32_t offsetof__stat_size = net_offset + 0; uint32_t offsetof_fstype = net_offset + 2; uint32_t offsetof_qid_type = net_offset + 8; VALIDATE_NET_BYTES(21); if (GET_U8LE(offsetof_qid_type) & ~qt_masks[ctx->version]) - return lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "unknown bits in qt bitfield: ", - (base16_u8_, 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()); @@ -281,52 +280,52 @@ 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 (GET_U32LE(offsetof__stat_size) != offsetof_end - offsetof_fstype) - return lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "stat->_stat_size value is wrong: actual:", (base10, GET_U32LE(offsetof__stat_size)), " != correct:", (base10, 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 lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "unknown bits in dm bitfield: ", - (base16_u32_, 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 || CONFIG_9P_ENABLE_uninitialized -static ssize_t validate_Tversion(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { +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 (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size) - return lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Tversion->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, 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 lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Tversion->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 100)); - return (ssize_t)host_size; + 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 (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size) - return lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Rversion->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, 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 lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Rversion->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 101)); - return (ssize_t)host_size; + return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Rversion->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 101))); + return ERROR_NEW_VAL(size_t, host_size); } #endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u || CONFIG_9P_ENABLE_uninitialized */ #if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u -static ssize_t validate_Tauth(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { +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); @@ -340,33 +339,33 @@ 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 (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size) - return lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Tauth->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, 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 lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Tauth->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 102)); - return (ssize_t)host_size; + 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_error(ctx, LIB9P_ERRNO_L_EBADMSG, "unknown bits in qt bitfield: ", - (base16_u8_, GET_U8LE(offsetof_aqid_type) & ~qt_masks[ctx->version])); + return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "unknown bits in qt bitfield: ", + (base16_u8_, GET_U8LE(offsetof_aqid_type) & ~qt_masks[ctx->version]))); uint32_t offsetof_end = net_offset + 0; if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size) - return lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Rauth->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, 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 lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Rauth->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 103)); - return (ssize_t)host_size; + 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); @@ -380,35 +379,35 @@ 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 (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size) - return lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Tattach->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, 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 lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Tattach->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 104)); - return (ssize_t)host_size; + 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_error(ctx, LIB9P_ERRNO_L_EBADMSG, "unknown bits in qt bitfield: ", - (base16_u8_, GET_U8LE(offsetof_qid_type) & ~qt_masks[ctx->version])); + return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "unknown bits in qt bitfield: ", + (base16_u8_, GET_U8LE(offsetof_qid_type) & ~qt_masks[ctx->version]))); uint32_t offsetof_end = net_offset + 0; if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size) - return lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Rattach->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, 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 lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Rattach->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 105)); - return (ssize_t)host_size; + return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Rattach->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 105))); + return ERROR_NEW_VAL(size_t, host_size); } #endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */ #if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u || CONFIG_9P_ENABLE_uninitialized -static ssize_t validate_Rerror(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { +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); @@ -420,45 +419,45 @@ 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 (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size) - return lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Rerror->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, 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 lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Rerror->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 107)); - return (ssize_t)host_size; + return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "Rerror->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 107))); + return ERROR_NEW_VAL(size_t, host_size); } #endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u || CONFIG_9P_ENABLE_uninitialized */ #if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u -static 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 (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size) - return lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Tflush->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, 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 lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Tflush->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 108)); - return (ssize_t)host_size; + 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 (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size) - return lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Rflush->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, 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 lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Rflush->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 109)); - return (ssize_t)host_size; + 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; @@ -470,17 +469,17 @@ static ssize_t validate_Twalk(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t } uint32_t offsetof_end = net_offset + 0; if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size) - return lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Twalk->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, 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 lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Twalk->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 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 lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Twalk->nwname value is too large: ", (base10, GET_U16LE(offsetof_nwname)), " > ", (base10, 16)); - return (ssize_t)host_size; + 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; @@ -490,61 +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_error(ctx, LIB9P_ERRNO_L_EBADMSG, "unknown bits in qt bitfield: ", - (base16_u8_, GET_U8LE(offsetof_wqid_type) & ~qt_masks[ctx->version])); + return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "unknown bits in qt bitfield: ", + (base16_u8_, GET_U8LE(offsetof_wqid_type) & ~qt_masks[ctx->version]))); } uint32_t offsetof_end = net_offset + 0; if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size) - return lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Rwalk->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, 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 lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Rwalk->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 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 lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Rwalk->nwqid value is too large: ", (base10, GET_U16LE(offsetof_nwqid)), " > ", (base10, 16)); - return (ssize_t)host_size; + 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 (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size) - return lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Topen->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, 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 lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Topen->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 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_error(ctx, LIB9P_ERRNO_L_EBADMSG, "unknown bits in o bitfield: ", - (base16_u8_, 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_error(ctx, LIB9P_ERRNO_L_EBADMSG, "unknown bits in qt bitfield: ", - (base16_u8_, GET_U8LE(offsetof_qid_type) & ~qt_masks[ctx->version])); + return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "unknown bits in qt bitfield: ", + (base16_u8_, GET_U8LE(offsetof_qid_type) & ~qt_masks[ctx->version]))); uint32_t offsetof_end = net_offset + 4; VALIDATE_NET_BYTES(4); if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size) - return lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Ropen->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, 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 lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Ropen->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 113)); - return (ssize_t)host_size; + 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); @@ -554,42 +553,42 @@ static ssize_t validate_Tcreate(struct lib9p_ctx *ctx, uint32_t net_size, uint8_ uint32_t offsetof_end = net_offset + 5; VALIDATE_NET_BYTES(5); if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size) - return lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Tcreate->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, 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 lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Tcreate->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 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_error(ctx, LIB9P_ERRNO_L_EBADMSG, "unknown bits in dm bitfield: ", - (base16_u32_, 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_error(ctx, LIB9P_ERRNO_L_EBADMSG, "unknown bits in o bitfield: ", - (base16_u8_, 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_error(ctx, LIB9P_ERRNO_L_EBADMSG, "unknown bits in qt bitfield: ", - (base16_u8_, GET_U8LE(offsetof_qid_type) & ~qt_masks[ctx->version])); + return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "unknown bits in qt bitfield: ", + (base16_u8_, GET_U8LE(offsetof_qid_type) & ~qt_masks[ctx->version]))); uint32_t offsetof_end = net_offset + 4; VALIDATE_NET_BYTES(4); if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size) - return lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Rcreate->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, 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 lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Rcreate->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 115)); - return (ssize_t)host_size; + 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; @@ -597,19 +596,19 @@ static ssize_t validate_Tread(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t uint32_t offsetof_end = net_offset + 23; VALIDATE_NET_BYTES(23); if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size) - return lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Tread->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, 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 lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Tread->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 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 lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Tread->offset value is too large: ", (base10, GET_U64LE(offsetof_offset)), " > ", (base10, 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 lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Tread->count value is too large: ", (base10, GET_U32LE(offsetof_count)), " > ", (base10, INT32_MAX)); - return (ssize_t)host_size; + 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 ssize_t validate_Rread(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { +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; - ssize_t host_size = sizeof(struct lib9p_msg_Rread); + 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; @@ -617,17 +616,17 @@ static ssize_t validate_Rread(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t VALIDATE_NET_BYTES(LAST_U32LE()); uint32_t offsetof_end = net_offset + 0; if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size) - return lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Rread->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, 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 lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Rread->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 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 lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Rread->count value is too large: ", (base10, GET_U32LE(offsetof_count)), " > ", (base10, INT32_MAX)); - return (ssize_t)host_size; + 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; @@ -636,108 +635,108 @@ static ssize_t validate_Twrite(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t VALIDATE_NET_BYTES(LAST_U32LE()); uint32_t offsetof_end = net_offset + 0; if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size) - return lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Twrite->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, 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 lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Twrite->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 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 lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Twrite->offset value is too large: ", (base10, GET_U64LE(offsetof_offset)), " > ", (base10, 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 lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Twrite->count value is too large: ", (base10, GET_U32LE(offsetof_count)), " > ", (base10, INT32_MAX)); - return (ssize_t)host_size; + 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 ssize_t validate_Rwrite(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes) { +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; - ssize_t host_size = sizeof(struct lib9p_msg_Rwrite); + size_t host_size = sizeof(struct lib9p_msg_Rwrite); uint32_t offsetof_size = net_offset + 0; uint32_t offsetof_typ = net_offset + 4; uint32_t offsetof_count = net_offset + 7; uint32_t offsetof_end = net_offset + 11; VALIDATE_NET_BYTES(11); if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size) - return lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Rwrite->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, 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 lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Rwrite->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 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 lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Rwrite->count value is too large: ", (base10, GET_U32LE(offsetof_count)), " > ", (base10, INT32_MAX)); - return (ssize_t)host_size; + 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 (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size) - return lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Tclunk->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, 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 lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Tclunk->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 120)); - return (ssize_t)host_size; + 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 (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size) - return lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Rclunk->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, 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 lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Rclunk->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 121)); - return (ssize_t)host_size; + 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 (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size) - return lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Tremove->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, 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 lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Tremove->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 122)); - return (ssize_t)host_size; + 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 (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size) - return lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Rremove->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, 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 lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Rremove->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 123)); - return (ssize_t)host_size; + 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 (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size) - return lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Tstat->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, 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 lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Tstat->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 124)); - return (ssize_t)host_size; + 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; @@ -747,8 +746,8 @@ static ssize_t validate_Rstat(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t uint32_t offsetof_stat_qid_type = net_offset + 17; VALIDATE_NET_BYTES(30); if (GET_U8LE(offsetof_stat_qid_type) & ~qt_masks[ctx->version]) - return lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "unknown bits in qt bitfield: ", - (base16_u8_, 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()); @@ -767,23 +766,23 @@ 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 (GET_U32LE(offsetof_stat__stat_size) != offsetof_stat_end - offsetof_stat_fstype) - return lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Rstat->stat._stat_size value is wrong: actual:", (base10, GET_U32LE(offsetof_stat__stat_size)), " != correct:", (base10, 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 lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "unknown bits in dm bitfield: ", - (base16_u32_, GET_U32LE(offsetof_stat_mode) & ~dm_masks[ctx->version])); + return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "unknown bits in dm bitfield: ", + (base16_u32_, GET_U32LE(offsetof_stat_mode) & ~dm_masks[ctx->version]))); uint32_t offsetof_end = net_offset + 0; if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size) - return lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Rstat->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, 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 lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Rstat->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 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 lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Rstat->nstat value is wrong: actual:", (base10, GET_U32LE(offsetof_nstat)), " != correct:", (base10, offsetof_end - offsetof_stat)); - return (ssize_t)host_size; + 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; @@ -793,8 +792,8 @@ static ssize_t validate_Twstat(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t uint32_t offsetof_stat_qid_type = net_offset + 21; VALIDATE_NET_BYTES(34); if (GET_U8LE(offsetof_stat_qid_type) & ~qt_masks[ctx->version]) - return lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "unknown bits in qt bitfield: ", - (base16_u8_, 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()); @@ -813,157 +812,157 @@ 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 (GET_U32LE(offsetof_stat__stat_size) != offsetof_stat_end - offsetof_stat_fstype) - return lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Twstat->stat._stat_size value is wrong: actual:", (base10, GET_U32LE(offsetof_stat__stat_size)), " != correct:", (base10, 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 lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "unknown bits in dm bitfield: ", - (base16_u32_, GET_U32LE(offsetof_stat_mode) & ~dm_masks[ctx->version])); + return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "unknown bits in dm bitfield: ", + (base16_u32_, GET_U32LE(offsetof_stat_mode) & ~dm_masks[ctx->version]))); uint32_t offsetof_end = net_offset + 0; if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size) - return lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Twstat->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, 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 lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Twstat->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 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 lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Twstat->nstat value is wrong: actual:", (base10, GET_U32LE(offsetof_nstat)), " != correct:", (base10, offsetof_end - offsetof_stat)); - return (ssize_t)host_size; + 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 (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size) - return lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Rwstat->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, 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 lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Rwstat->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 127)); - return (ssize_t)host_size; + 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 (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size) - return lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Topenfd->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, 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 lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Topenfd->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 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_error(ctx, LIB9P_ERRNO_L_EBADMSG, "unknown bits in o bitfield: ", - (base16_u8_, 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_error(ctx, LIB9P_ERRNO_L_EBADMSG, "unknown bits in qt bitfield: ", - (base16_u8_, GET_U8LE(offsetof_qid_type) & ~qt_masks[ctx->version])); + return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "unknown bits in qt bitfield: ", + (base16_u8_, GET_U8LE(offsetof_qid_type) & ~qt_masks[ctx->version]))); uint32_t offsetof_end = net_offset + 8; VALIDATE_NET_BYTES(8); if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size) - return lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Ropenfd->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, 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 lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Ropenfd->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 99)); - return (ssize_t)host_size; + 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 (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size) - return lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Rlerror->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, 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 lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Rlerror->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 7)); - return (ssize_t)host_size; + 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 (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size) - return lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Tstatfs->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, 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 lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Tstatfs->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 8)); - return (ssize_t)host_size; + 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 (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size) - return lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Rstatfs->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, 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 lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Rstatfs->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 9)); - return (ssize_t)host_size; + 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 (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size) - return lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Tlopen->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, 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 lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Tlopen->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 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_error(ctx, LIB9P_ERRNO_L_EBADMSG, "unknown bits in lo bitfield: ", - (base16_u32_, 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_error(ctx, LIB9P_ERRNO_L_EBADMSG, "unknown bits in qt bitfield: ", - (base16_u8_, GET_U8LE(offsetof_qid_type) & ~qt_masks[ctx->version])); + return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "unknown bits in qt bitfield: ", + (base16_u8_, GET_U8LE(offsetof_qid_type) & ~qt_masks[ctx->version]))); uint32_t offsetof_end = net_offset + 4; VALIDATE_NET_BYTES(4); if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size) - return lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Rlopen->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, 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 lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Rlopen->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 13)); - return (ssize_t)host_size; + 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); @@ -973,40 +972,40 @@ static ssize_t validate_Tlcreate(struct lib9p_ctx *ctx, uint32_t net_size, uint8 uint32_t offsetof_end = net_offset + 12; VALIDATE_NET_BYTES(12); if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size) - return lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Tlcreate->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, 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 lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Tlcreate->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 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_error(ctx, LIB9P_ERRNO_L_EBADMSG, "unknown bits in lo bitfield: ", - (base16_u32_, 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_error(ctx, LIB9P_ERRNO_L_EBADMSG, "unknown bits in mode bitfield: ", - (base16_u32_, 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_error(ctx, LIB9P_ERRNO_L_EBADMSG, "unknown bits in qt bitfield: ", - (base16_u8_, GET_U8LE(offsetof_qid_type) & ~qt_masks[ctx->version])); + return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "unknown bits in qt bitfield: ", + (base16_u8_, GET_U8LE(offsetof_qid_type) & ~qt_masks[ctx->version]))); uint32_t offsetof_end = net_offset + 4; VALIDATE_NET_BYTES(4); if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size) - return lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Rlcreate->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, 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 lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Rlcreate->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 15)); - return (ssize_t)host_size; + 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); @@ -1016,33 +1015,33 @@ static ssize_t validate_Tsymlink(struct lib9p_ctx *ctx, uint32_t net_size, uint8 uint32_t offsetof_end = net_offset + 4; VALIDATE_NET_BYTES(4); if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size) - return lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Tsymlink->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, 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 lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Tsymlink->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 16)); - return (ssize_t)host_size; + 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_error(ctx, LIB9P_ERRNO_L_EBADMSG, "unknown bits in qt bitfield: ", - (base16_u8_, GET_U8LE(offsetof_qid_type) & ~qt_masks[ctx->version])); + return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "unknown bits in qt bitfield: ", + (base16_u8_, GET_U8LE(offsetof_qid_type) & ~qt_masks[ctx->version]))); uint32_t offsetof_end = net_offset + 0; if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size) - return lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Rsymlink->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, 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 lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Rsymlink->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 17)); - return (ssize_t)host_size; + 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); @@ -1051,139 +1050,139 @@ static ssize_t validate_Tmknod(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t uint32_t offsetof_end = net_offset + 16; VALIDATE_NET_BYTES(16); if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size) - return lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Tmknod->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, 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 lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Tmknod->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 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_error(ctx, LIB9P_ERRNO_L_EBADMSG, "unknown bits in mode bitfield: ", - (base16_u32_, 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_error(ctx, LIB9P_ERRNO_L_EBADMSG, "unknown bits in qt bitfield: ", - (base16_u8_, GET_U8LE(offsetof_qid_type) & ~qt_masks[ctx->version])); + return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "unknown bits in qt bitfield: ", + (base16_u8_, GET_U8LE(offsetof_qid_type) & ~qt_masks[ctx->version]))); uint32_t offsetof_end = net_offset + 0; if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size) - return lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Rmknod->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, 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 lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Rmknod->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 19)); - return (ssize_t)host_size; + 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 (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size) - return lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Trename->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, 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 lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Trename->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 20)); - return (ssize_t)host_size; + 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 (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size) - return lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Rrename->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, 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 lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Rrename->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 21)); - return (ssize_t)host_size; + 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 (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size) - return lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Treadlink->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, 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 lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Treadlink->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 22)); - return (ssize_t)host_size; + 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 (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size) - return lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Rreadlink->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, 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 lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Rreadlink->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 23)); - return (ssize_t)host_size; + 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 (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size) - return lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Tgetattr->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, 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 lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Tgetattr->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 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_error(ctx, LIB9P_ERRNO_L_EBADMSG, "unknown bits in getattr bitfield: ", - (base16_u64_, 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_error(ctx, LIB9P_ERRNO_L_EBADMSG, "unknown bits in qt bitfield: ", - (base16_u8_, GET_U8LE(offsetof_qid_type) & ~qt_masks[ctx->version])); + return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "unknown bits in qt bitfield: ", + (base16_u8_, GET_U8LE(offsetof_qid_type) & ~qt_masks[ctx->version]))); uint32_t offsetof_mode = net_offset + 0; uint32_t offsetof_end = net_offset + 132; VALIDATE_NET_BYTES(132); if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size) - return lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Rgetattr->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, 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 lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Rgetattr->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 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_error(ctx, LIB9P_ERRNO_L_EBADMSG, "unknown bits in getattr bitfield: ", - (base16_u64_, 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_error(ctx, LIB9P_ERRNO_L_EBADMSG, "unknown bits in mode bitfield: ", - (base16_u32_, 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; @@ -1191,64 +1190,64 @@ static ssize_t validate_Tsetattr(struct lib9p_ctx *ctx, uint32_t net_size, uint8 uint32_t offsetof_end = net_offset + 67; VALIDATE_NET_BYTES(67); if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size) - return lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Tsetattr->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, 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 lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Tsetattr->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 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_error(ctx, LIB9P_ERRNO_L_EBADMSG, "unknown bits in setattr bitfield: ", - (base16_u32_, 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_error(ctx, LIB9P_ERRNO_L_EBADMSG, "unknown bits in mode bitfield: ", - (base16_u32_, 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 (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size) - return lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Rsetattr->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, 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 lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Rsetattr->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 27)); - return (ssize_t)host_size; + 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 (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size) - return lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Txattrwalk->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, 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 lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Txattrwalk->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 30)); - return (ssize_t)host_size; + 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 (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size) - return lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Rxattrwalk->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, 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 lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Rxattrwalk->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 31)); - return (ssize_t)host_size; + 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); @@ -1256,86 +1255,86 @@ static ssize_t validate_Txattrcreate(struct lib9p_ctx *ctx, uint32_t net_size, u uint32_t offsetof_end = net_offset + 12; VALIDATE_NET_BYTES(12); if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size) - return lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Txattrcreate->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, 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 lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Txattrcreate->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 32)); - return (ssize_t)host_size; + 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 (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size) - return lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Rxattrcreate->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, 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 lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Rxattrcreate->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 33)); - return (ssize_t)host_size; + 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 (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size) - return lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Treaddir->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, 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 lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Treaddir->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 40)); - return (ssize_t)host_size; + 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 (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size) - return lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Rreaddir->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, 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 lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Rreaddir->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 41)); - return (ssize_t)host_size; + 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 (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size) - return lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Tfsync->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, 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 lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Tfsync->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 50)); - return (ssize_t)host_size; + 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 (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size) - return lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Rfsync->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, 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 lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Rfsync->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 51)); - return (ssize_t)host_size; + 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; @@ -1343,91 +1342,91 @@ static ssize_t validate_Tlock(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t VALIDATE_NET_UTF8(LAST_U16LE()); uint32_t offsetof_end = net_offset + 0; if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size) - return lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Tlock->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, 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 lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Tlock->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 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_error(ctx, LIB9P_ERRNO_L_EBADMSG, "unknown bits in lock_flags bitfield: ", - (base16_u32_, 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 (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size) - return lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Rlock->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, 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 lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Rlock->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 53)); - return (ssize_t)host_size; + 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 (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size) - return lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Tgetlock->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, 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 lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Tgetlock->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 54)); - return (ssize_t)host_size; + 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 (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size) - return lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Rgetlock->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, 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 lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Rgetlock->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 55)); - return (ssize_t)host_size; + 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 (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size) - return lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Tlink->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, 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 lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Tlink->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 70)); - return (ssize_t)host_size; + 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 (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size) - return lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Rlink->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, 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 lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Rlink->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 71)); - return (ssize_t)host_size; + 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); @@ -1436,36 +1435,36 @@ static ssize_t validate_Tmkdir(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t uint32_t offsetof_end = net_offset + 8; VALIDATE_NET_BYTES(8); if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size) - return lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Tmkdir->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, 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 lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Tmkdir->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 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_error(ctx, LIB9P_ERRNO_L_EBADMSG, "unknown bits in mode bitfield: ", - (base16_u32_, 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_error(ctx, LIB9P_ERRNO_L_EBADMSG, "unknown bits in qt bitfield: ", - (base16_u8_, GET_U8LE(offsetof_qid_type) & ~qt_masks[ctx->version])); + return ERROR_NEW_ERR(size_t, error_new(E_POSIX_EBADMSG, "unknown bits in qt bitfield: ", + (base16_u8_, GET_U8LE(offsetof_qid_type) & ~qt_masks[ctx->version]))); uint32_t offsetof_end = net_offset + 0; if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size) - return lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Rmkdir->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, 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 lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Rmkdir->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 73)); - return (ssize_t)host_size; + 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); @@ -1474,29 +1473,29 @@ static ssize_t validate_Trenameat(struct lib9p_ctx *ctx, uint32_t net_size, uint VALIDATE_NET_UTF8(LAST_U16LE()); uint32_t offsetof_end = net_offset + 0; if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size) - return lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Trenameat->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, 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 lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Trenameat->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 74)); - return (ssize_t)host_size; + 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 (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size) - return lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Rrenameat->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, 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 lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Rrenameat->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 75)); - return (ssize_t)host_size; + 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); @@ -1504,59 +1503,59 @@ static ssize_t validate_Tunlinkat(struct lib9p_ctx *ctx, uint32_t net_size, uint uint32_t offsetof_end = net_offset + 4; VALIDATE_NET_BYTES(4); if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size) - return lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Tunlinkat->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, 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 lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Tunlinkat->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 76)); - return (ssize_t)host_size; + 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 (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size) - return lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Runlinkat->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, 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 lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Runlinkat->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 77)); - return (ssize_t)host_size; + 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 (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size) - return lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Tsession->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, 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 lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Tsession->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 150)); - return (ssize_t)host_size; + 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 (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size) - return lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Rsession->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, 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 lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Rsession->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 151)); - return (ssize_t)host_size; + 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); @@ -1567,30 +1566,30 @@ static ssize_t validate_Tsread(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t } uint32_t offsetof_end = net_offset + 0; if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size) - return lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Tsread->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, 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 lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Tsread->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 152)); - return (ssize_t)host_size; + 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 (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size) - return lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Rsread->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, 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 lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Rsread->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 153)); - return (ssize_t)host_size; + 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); @@ -1603,24 +1602,24 @@ static ssize_t validate_Tswrite(struct lib9p_ctx *ctx, uint32_t net_size, uint8_ VALIDATE_NET_BYTES(LAST_U32LE()); uint32_t offsetof_end = net_offset + 0; if (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size) - return lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Tswrite->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, 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 lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Tswrite->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 154)); - return (ssize_t)host_size; + 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 (GET_U32LE(offsetof_size) != offsetof_end - offsetof_size) - return lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Rswrite->size value is wrong: actual:", (base10, GET_U32LE(offsetof_size)), " != correct:", (base10, 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 lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "Rswrite->typ value is wrong: actual:", (base10, GET_U8LE(offsetof_typ)), " != correct:", (base10, 155)); - return (ssize_t)host_size; + 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 */ @@ -2685,41 +2684,41 @@ static void unmarshal_Rswrite([[maybe_unused]] struct lib9p_ctx *ctx, uint8_t *n /* 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 @@ -2767,13 +2766,12 @@ static bool marshal_stat(struct lib9p_ctx *ctx, struct lib9p_stat *val, struct _ #endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u */ #if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u || CONFIG_9P_ENABLE_uninitialized -static bool marshal_Tversion(struct lib9p_ctx *ctx, struct lib9p_msg_Tversion *val, struct _marshal_ret *ret) { +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_error(ctx, LIB9P_ERRNO_L_ERANGE, "Tversion message too large to marshal into ", + return error_new(E_POSIX_ERANGE, "Tversion message too large to marshal into ", ctx->version ? "negotiated" : "client", " limit", " (", needed_size, " > ", ctx->max_msg_size, ")"); - return true; } uint32_t offsetof_end = needed_size; uint32_t offsetof_size = 0; @@ -2783,16 +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_error(ctx, LIB9P_ERRNO_L_ERANGE, "Rversion message too large to marshal into ", + return error_new(E_POSIX_ERANGE, "Rversion message too large to marshal into ", ctx->version ? "negotiated" : "server", " limit", " (", needed_size, " > ", ctx->max_msg_size, ")"); - return true; } uint32_t offsetof_end = needed_size; uint32_t offsetof_size = 0; @@ -2802,12 +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; } #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 bool marshal_Tauth(struct lib9p_ctx *ctx, struct lib9p_msg_Tauth *val, struct _marshal_ret *ret) { +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) ) { @@ -2815,10 +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_error(ctx, LIB9P_ERRNO_L_ERANGE, "Tauth message too large to marshal into ", + return error_new(E_POSIX_ERANGE, "Tauth message too large to marshal into ", ctx->version ? "negotiated" : "client", " limit", " (", needed_size, " > ", ctx->max_msg_size, ")"); - return true; } uint32_t offsetof_end = needed_size; uint32_t offsetof_size = 0; @@ -2835,16 +2831,15 @@ static bool marshal_Tauth(struct lib9p_ctx *ctx, struct lib9p_msg_Tauth *val, st 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_error(ctx, LIB9P_ERRNO_L_ERANGE, "Rauth message too large to marshal into ", + return error_new(E_POSIX_ERANGE, "Rauth message too large to marshal into ", ctx->version ? "negotiated" : "server", " limit", " (", needed_size, " > ", ctx->max_msg_size, ")"); - return true; } uint32_t offsetof_end = needed_size; uint32_t offsetof_size = 0; @@ -2854,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) ) { @@ -2865,10 +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_error(ctx, LIB9P_ERRNO_L_ERANGE, "Tattach message too large to marshal into ", + return error_new(E_POSIX_ERANGE, "Tattach message too large to marshal into ", ctx->version ? "negotiated" : "client", " limit", " (", needed_size, " > ", ctx->max_msg_size, ")"); - return true; } uint32_t offsetof_end = needed_size; uint32_t offsetof_size = 0; @@ -2886,16 +2880,15 @@ static bool marshal_Tattach(struct lib9p_ctx *ctx, struct lib9p_msg_Tattach *val 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_error(ctx, LIB9P_ERRNO_L_ERANGE, "Rattach message too large to marshal into ", + return error_new(E_POSIX_ERANGE, "Rattach message too large to marshal into ", ctx->version ? "negotiated" : "server", " limit", " (", needed_size, " > ", ctx->max_msg_size, ")"); - return true; } uint32_t offsetof_end = needed_size; uint32_t offsetof_size = 0; @@ -2905,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 || CONFIG_9P_ENABLE_uninitialized -static bool marshal_Rerror(struct lib9p_ctx *ctx, struct lib9p_msg_Rerror *val, struct _marshal_ret *ret) { +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) { @@ -2918,10 +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_error(ctx, LIB9P_ERRNO_L_ERANGE, "Rerror message too large to marshal into ", + return error_new(E_POSIX_ERANGE, "Rerror message too large to marshal into ", ctx->version ? "negotiated" : "server", " limit", " (", needed_size, " > ", ctx->max_msg_size, ")"); - return true; } uint32_t offsetof_end = needed_size; uint32_t offsetof_size = 0; @@ -2935,18 +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 || 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_error(ctx, LIB9P_ERRNO_L_ERANGE, "Tflush message too large to marshal into ", + return error_new(E_POSIX_ERANGE, "Tflush message too large to marshal into ", ctx->version ? "negotiated" : "client", " limit", " (", needed_size, " > ", ctx->max_msg_size, ")"); - return true; } uint32_t offsetof_end = needed_size; uint32_t offsetof_size = 0; @@ -2954,35 +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_error(ctx, LIB9P_ERRNO_L_ERANGE, "Rflush message too large to marshal into ", + return error_new(E_POSIX_ERANGE, "Rflush message too large to marshal into ", ctx->version ? "negotiated" : "server", " limit", " (", needed_size, " > ", ctx->max_msg_size, ")"); - return true; } uint32_t offsetof_end = needed_size; uint32_t offsetof_size = 0; MARSHAL_U32LE(ctx, offsetof_end - offsetof_size); MARSHAL_U8LE(ctx, 109); MARSHAL_U16LE(ctx, val->tag); - return false; + 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_error(ctx, LIB9P_ERRNO_L_ERANGE, "Twalk message too large to marshal into ", + return error_new(E_POSIX_ERANGE, "Twalk message too large to marshal into ", ctx->version ? "negotiated" : "client", " limit", " (", needed_size, " > ", ctx->max_msg_size, ")"); - return true; } uint32_t offsetof_end = needed_size; uint32_t offsetof_size = 0; @@ -2996,16 +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_error(ctx, LIB9P_ERRNO_L_ERANGE, "Rwalk message too large to marshal into ", + return error_new(E_POSIX_ERANGE, "Rwalk message too large to marshal into ", ctx->version ? "negotiated" : "server", " limit", " (", needed_size, " > ", ctx->max_msg_size, ")"); - return true; } uint32_t offsetof_end = needed_size; uint32_t offsetof_size = 0; @@ -3018,18 +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_error(ctx, LIB9P_ERRNO_L_ERANGE, "Topen message too large to marshal into ", + return error_new(E_POSIX_ERANGE, "Topen message too large to marshal into ", ctx->version ? "negotiated" : "client", " limit", " (", needed_size, " > ", ctx->max_msg_size, ")"); - return true; } uint32_t offsetof_end = needed_size; uint32_t offsetof_size = 0; @@ -3038,16 +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_error(ctx, LIB9P_ERRNO_L_ERANGE, "Ropen message too large to marshal into ", + return error_new(E_POSIX_ERANGE, "Ropen message too large to marshal into ", ctx->version ? "negotiated" : "server", " limit", " (", needed_size, " > ", ctx->max_msg_size, ")"); - return true; } uint32_t offsetof_end = needed_size; uint32_t offsetof_size = 0; @@ -3058,16 +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_error(ctx, LIB9P_ERRNO_L_ERANGE, "Tcreate message too large to marshal into ", + return error_new(E_POSIX_ERANGE, "Tcreate message too large to marshal into ", ctx->version ? "negotiated" : "client", " limit", " (", needed_size, " > ", ctx->max_msg_size, ")"); - return true; } uint32_t offsetof_end = needed_size; uint32_t offsetof_size = 0; @@ -3079,16 +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_error(ctx, LIB9P_ERRNO_L_ERANGE, "Rcreate message too large to marshal into ", + return error_new(E_POSIX_ERANGE, "Rcreate message too large to marshal into ", ctx->version ? "negotiated" : "server", " limit", " (", needed_size, " > ", ctx->max_msg_size, ")"); - return true; } uint32_t offsetof_end = needed_size; uint32_t offsetof_size = 0; @@ -3099,18 +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_error(ctx, LIB9P_ERRNO_L_ERANGE, "Tread message too large to marshal into ", + return error_new(E_POSIX_ERANGE, "Tread message too large to marshal into ", ctx->version ? "negotiated" : "client", " limit", " (", needed_size, " > ", ctx->max_msg_size, ")"); - return true; } uint32_t offsetof_end = needed_size; uint32_t offsetof_size = 0; @@ -3120,16 +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_error(ctx, LIB9P_ERRNO_L_ERANGE, "Rread message too large to marshal into ", + return error_new(E_POSIX_ERANGE, "Rread message too large to marshal into ", ctx->version ? "negotiated" : "server", " limit", " (", needed_size, " > ", ctx->max_msg_size, ")"); - return true; } uint32_t offsetof_end = needed_size; uint32_t offsetof_size = 0; @@ -3138,16 +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_error(ctx, LIB9P_ERRNO_L_ERANGE, "Twrite message too large to marshal into ", + return error_new(E_POSIX_ERANGE, "Twrite message too large to marshal into ", ctx->version ? "negotiated" : "client", " limit", " (", needed_size, " > ", ctx->max_msg_size, ")"); - return true; } uint32_t offsetof_end = needed_size; uint32_t offsetof_size = 0; @@ -3158,16 +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_error(ctx, LIB9P_ERRNO_L_ERANGE, "Rwrite message too large to marshal into ", + return error_new(E_POSIX_ERANGE, "Rwrite message too large to marshal into ", ctx->version ? "negotiated" : "server", " limit", " (", needed_size, " > ", ctx->max_msg_size, ")"); - return true; } uint32_t offsetof_end = needed_size; uint32_t offsetof_size = 0; @@ -3175,16 +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_error(ctx, LIB9P_ERRNO_L_ERANGE, "Tclunk message too large to marshal into ", + return error_new(E_POSIX_ERANGE, "Tclunk message too large to marshal into ", ctx->version ? "negotiated" : "client", " limit", " (", needed_size, " > ", ctx->max_msg_size, ")"); - return true; } uint32_t offsetof_end = needed_size; uint32_t offsetof_size = 0; @@ -3192,32 +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_error(ctx, LIB9P_ERRNO_L_ERANGE, "Rclunk message too large to marshal into ", + return error_new(E_POSIX_ERANGE, "Rclunk message too large to marshal into ", ctx->version ? "negotiated" : "server", " limit", " (", needed_size, " > ", ctx->max_msg_size, ")"); - return true; } uint32_t offsetof_end = needed_size; uint32_t offsetof_size = 0; MARSHAL_U32LE(ctx, offsetof_end - offsetof_size); MARSHAL_U8LE(ctx, 121); MARSHAL_U16LE(ctx, val->tag); - return false; + 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_error(ctx, LIB9P_ERRNO_L_ERANGE, "Tremove message too large to marshal into ", + return error_new(E_POSIX_ERANGE, "Tremove message too large to marshal into ", ctx->version ? "negotiated" : "client", " limit", " (", needed_size, " > ", ctx->max_msg_size, ")"); - return true; } uint32_t offsetof_end = needed_size; uint32_t offsetof_size = 0; @@ -3225,34 +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_error(ctx, LIB9P_ERRNO_L_ERANGE, "Rremove message too large to marshal into ", + return error_new(E_POSIX_ERANGE, "Rremove message too large to marshal into ", ctx->version ? "negotiated" : "server", " limit", " (", needed_size, " > ", ctx->max_msg_size, ")"); - return true; } uint32_t offsetof_end = needed_size; uint32_t offsetof_size = 0; MARSHAL_U32LE(ctx, offsetof_end - offsetof_size); MARSHAL_U8LE(ctx, 123); MARSHAL_U16LE(ctx, val->tag); - return false; + 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_error(ctx, LIB9P_ERRNO_L_ERANGE, "Tstat message too large to marshal into ", + return error_new(E_POSIX_ERANGE, "Tstat message too large to marshal into ", ctx->version ? "negotiated" : "client", " limit", " (", needed_size, " > ", ctx->max_msg_size, ")"); - return true; } uint32_t offsetof_end = needed_size; uint32_t offsetof_size = 0; @@ -3260,10 +3235,10 @@ 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) { +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) { @@ -3271,10 +3246,9 @@ static bool marshal_Rstat(struct lib9p_ctx *ctx, struct lib9p_msg_Rstat *val, st } #endif /* CONFIG_9P_ENABLE_9P2000_u */ if (needed_size > ctx->max_msg_size) { - lib9p_error(ctx, LIB9P_ERRNO_L_ERANGE, "Rstat message too large to marshal into ", + return error_new(E_POSIX_ERANGE, "Rstat message too large to marshal into ", ctx->version ? "negotiated" : "server", " limit", " (", needed_size, " > ", ctx->max_msg_size, ")"); - return true; } uint32_t offsetof_end = needed_size; uint32_t offsetof_size = 0; @@ -3317,10 +3291,10 @@ static bool marshal_Rstat(struct lib9p_ctx *ctx, struct lib9p_msg_Rstat *val, st 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) { +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) { @@ -3328,10 +3302,9 @@ static bool marshal_Twstat(struct lib9p_ctx *ctx, struct lib9p_msg_Twstat *val, } #endif /* CONFIG_9P_ENABLE_9P2000_u */ if (needed_size > ctx->max_msg_size) { - lib9p_error(ctx, LIB9P_ERRNO_L_ERANGE, "Twstat message too large to marshal into ", + return error_new(E_POSIX_ERANGE, "Twstat message too large to marshal into ", ctx->version ? "negotiated" : "client", " limit", " (", needed_size, " > ", ctx->max_msg_size, ")"); - return true; } uint32_t offsetof_end = needed_size; uint32_t offsetof_size = 0; @@ -3375,34 +3348,32 @@ static bool marshal_Twstat(struct lib9p_ctx *ctx, struct lib9p_msg_Twstat *val, 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_error(ctx, LIB9P_ERRNO_L_ERANGE, "Rwstat message too large to marshal into ", + return error_new(E_POSIX_ERANGE, "Rwstat message too large to marshal into ", ctx->version ? "negotiated" : "server", " limit", " (", needed_size, " > ", ctx->max_msg_size, ")"); - return true; } uint32_t offsetof_end = needed_size; uint32_t offsetof_size = 0; MARSHAL_U32LE(ctx, offsetof_end - offsetof_size); MARSHAL_U8LE(ctx, 127); MARSHAL_U16LE(ctx, val->tag); - return false; + 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_error(ctx, LIB9P_ERRNO_L_ERANGE, "Topenfd message too large to marshal into ", + return error_new(E_POSIX_ERANGE, "Topenfd message too large to marshal into ", ctx->version ? "negotiated" : "client", " limit", " (", needed_size, " > ", ctx->max_msg_size, ")"); - return true; } uint32_t offsetof_end = needed_size; uint32_t offsetof_size = 0; @@ -3411,16 +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_error(ctx, LIB9P_ERRNO_L_ERANGE, "Ropenfd message too large to marshal into ", + return error_new(E_POSIX_ERANGE, "Ropenfd message too large to marshal into ", ctx->version ? "negotiated" : "server", " limit", " (", needed_size, " > ", ctx->max_msg_size, ")"); - return true; } uint32_t offsetof_end = needed_size; uint32_t offsetof_size = 0; @@ -3432,18 +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_error(ctx, LIB9P_ERRNO_L_ERANGE, "Rlerror message too large to marshal into ", + return error_new(E_POSIX_ERANGE, "Rlerror message too large to marshal into ", ctx->version ? "negotiated" : "server", " limit", " (", needed_size, " > ", ctx->max_msg_size, ")"); - return true; } uint32_t offsetof_end = needed_size; uint32_t offsetof_size = 0; @@ -3451,16 +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_error(ctx, LIB9P_ERRNO_L_ERANGE, "Tstatfs message too large to marshal into ", + return error_new(E_POSIX_ERANGE, "Tstatfs message too large to marshal into ", ctx->version ? "negotiated" : "client", " limit", " (", needed_size, " > ", ctx->max_msg_size, ")"); - return true; } uint32_t offsetof_end = needed_size; uint32_t offsetof_size = 0; @@ -3468,16 +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_error(ctx, LIB9P_ERRNO_L_ERANGE, "Rstatfs message too large to marshal into ", + return error_new(E_POSIX_ERANGE, "Rstatfs message too large to marshal into ", ctx->version ? "negotiated" : "server", " limit", " (", needed_size, " > ", ctx->max_msg_size, ")"); - return true; } uint32_t offsetof_end = needed_size; uint32_t offsetof_size = 0; @@ -3493,16 +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_error(ctx, LIB9P_ERRNO_L_ERANGE, "Tlopen message too large to marshal into ", + return error_new(E_POSIX_ERANGE, "Tlopen message too large to marshal into ", ctx->version ? "negotiated" : "client", " limit", " (", needed_size, " > ", ctx->max_msg_size, ")"); - return true; } uint32_t offsetof_end = needed_size; uint32_t offsetof_size = 0; @@ -3511,16 +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_error(ctx, LIB9P_ERRNO_L_ERANGE, "Rlopen message too large to marshal into ", + return error_new(E_POSIX_ERANGE, "Rlopen message too large to marshal into ", ctx->version ? "negotiated" : "server", " limit", " (", needed_size, " > ", ctx->max_msg_size, ")"); - return true; } uint32_t offsetof_end = needed_size; uint32_t offsetof_size = 0; @@ -3531,16 +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_error(ctx, LIB9P_ERRNO_L_ERANGE, "Tlcreate message too large to marshal into ", + return error_new(E_POSIX_ERANGE, "Tlcreate message too large to marshal into ", ctx->version ? "negotiated" : "client", " limit", " (", needed_size, " > ", ctx->max_msg_size, ")"); - return true; } uint32_t offsetof_end = needed_size; uint32_t offsetof_size = 0; @@ -3553,16 +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_error(ctx, LIB9P_ERRNO_L_ERANGE, "Rlcreate message too large to marshal into ", + return error_new(E_POSIX_ERANGE, "Rlcreate message too large to marshal into ", ctx->version ? "negotiated" : "server", " limit", " (", needed_size, " > ", ctx->max_msg_size, ")"); - return true; } uint32_t offsetof_end = needed_size; uint32_t offsetof_size = 0; @@ -3573,16 +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_error(ctx, LIB9P_ERRNO_L_ERANGE, "Tsymlink message too large to marshal into ", + return error_new(E_POSIX_ERANGE, "Tsymlink message too large to marshal into ", ctx->version ? "negotiated" : "client", " limit", " (", needed_size, " > ", ctx->max_msg_size, ")"); - return true; } uint32_t offsetof_end = needed_size; uint32_t offsetof_size = 0; @@ -3595,16 +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_error(ctx, LIB9P_ERRNO_L_ERANGE, "Rsymlink message too large to marshal into ", + return error_new(E_POSIX_ERANGE, "Rsymlink message too large to marshal into ", ctx->version ? "negotiated" : "server", " limit", " (", needed_size, " > ", ctx->max_msg_size, ")"); - return true; } uint32_t offsetof_end = needed_size; uint32_t offsetof_size = 0; @@ -3614,16 +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_error(ctx, LIB9P_ERRNO_L_ERANGE, "Tmknod message too large to marshal into ", + return error_new(E_POSIX_ERANGE, "Tmknod message too large to marshal into ", ctx->version ? "negotiated" : "client", " limit", " (", needed_size, " > ", ctx->max_msg_size, ")"); - return true; } uint32_t offsetof_end = needed_size; uint32_t offsetof_size = 0; @@ -3637,16 +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_error(ctx, LIB9P_ERRNO_L_ERANGE, "Rmknod message too large to marshal into ", + return error_new(E_POSIX_ERANGE, "Rmknod message too large to marshal into ", ctx->version ? "negotiated" : "server", " limit", " (", needed_size, " > ", ctx->max_msg_size, ")"); - return true; } uint32_t offsetof_end = needed_size; uint32_t offsetof_size = 0; @@ -3656,16 +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_error(ctx, LIB9P_ERRNO_L_ERANGE, "Trename message too large to marshal into ", + return error_new(E_POSIX_ERANGE, "Trename message too large to marshal into ", ctx->version ? "negotiated" : "client", " limit", " (", needed_size, " > ", ctx->max_msg_size, ")"); - return true; } uint32_t offsetof_end = needed_size; uint32_t offsetof_size = 0; @@ -3676,32 +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_error(ctx, LIB9P_ERRNO_L_ERANGE, "Rrename message too large to marshal into ", + return error_new(E_POSIX_ERANGE, "Rrename message too large to marshal into ", ctx->version ? "negotiated" : "server", " limit", " (", needed_size, " > ", ctx->max_msg_size, ")"); - return true; } uint32_t offsetof_end = needed_size; uint32_t offsetof_size = 0; MARSHAL_U32LE(ctx, offsetof_end - offsetof_size); MARSHAL_U8LE(ctx, 21); MARSHAL_U16LE(ctx, val->tag); - return false; + 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_error(ctx, LIB9P_ERRNO_L_ERANGE, "Treadlink message too large to marshal into ", + return error_new(E_POSIX_ERANGE, "Treadlink message too large to marshal into ", ctx->version ? "negotiated" : "client", " limit", " (", needed_size, " > ", ctx->max_msg_size, ")"); - return true; } uint32_t offsetof_end = needed_size; uint32_t offsetof_size = 0; @@ -3709,16 +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_error(ctx, LIB9P_ERRNO_L_ERANGE, "Rreadlink message too large to marshal into ", + return error_new(E_POSIX_ERANGE, "Rreadlink message too large to marshal into ", ctx->version ? "negotiated" : "server", " limit", " (", needed_size, " > ", ctx->max_msg_size, ")"); - return true; } uint32_t offsetof_end = needed_size; uint32_t offsetof_size = 0; @@ -3727,16 +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_error(ctx, LIB9P_ERRNO_L_ERANGE, "Tgetattr message too large to marshal into ", + return error_new(E_POSIX_ERANGE, "Tgetattr message too large to marshal into ", ctx->version ? "negotiated" : "client", " limit", " (", needed_size, " > ", ctx->max_msg_size, ")"); - return true; } uint32_t offsetof_end = needed_size; uint32_t offsetof_size = 0; @@ -3745,16 +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_error(ctx, LIB9P_ERRNO_L_ERANGE, "Rgetattr message too large to marshal into ", + return error_new(E_POSIX_ERANGE, "Rgetattr message too large to marshal into ", ctx->version ? "negotiated" : "server", " limit", " (", needed_size, " > ", ctx->max_msg_size, ")"); - return true; } uint32_t offsetof_end = needed_size; uint32_t offsetof_size = 0; @@ -3783,16 +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_error(ctx, LIB9P_ERRNO_L_ERANGE, "Tsetattr message too large to marshal into ", + return error_new(E_POSIX_ERANGE, "Tsetattr message too large to marshal into ", ctx->version ? "negotiated" : "client", " limit", " (", needed_size, " > ", ctx->max_msg_size, ")"); - return true; } uint32_t offsetof_end = needed_size; uint32_t offsetof_size = 0; @@ -3809,32 +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_error(ctx, LIB9P_ERRNO_L_ERANGE, "Rsetattr message too large to marshal into ", + return error_new(E_POSIX_ERANGE, "Rsetattr message too large to marshal into ", ctx->version ? "negotiated" : "server", " limit", " (", needed_size, " > ", ctx->max_msg_size, ")"); - return true; } uint32_t offsetof_end = needed_size; uint32_t offsetof_size = 0; MARSHAL_U32LE(ctx, offsetof_end - offsetof_size); MARSHAL_U8LE(ctx, 27); MARSHAL_U16LE(ctx, val->tag); - return false; + 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_error(ctx, LIB9P_ERRNO_L_ERANGE, "Txattrwalk message too large to marshal into ", + return error_new(E_POSIX_ERANGE, "Txattrwalk message too large to marshal into ", ctx->version ? "negotiated" : "client", " limit", " (", needed_size, " > ", ctx->max_msg_size, ")"); - return true; } uint32_t offsetof_end = needed_size; uint32_t offsetof_size = 0; @@ -3845,16 +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_error(ctx, LIB9P_ERRNO_L_ERANGE, "Rxattrwalk message too large to marshal into ", + return error_new(E_POSIX_ERANGE, "Rxattrwalk message too large to marshal into ", ctx->version ? "negotiated" : "server", " limit", " (", needed_size, " > ", ctx->max_msg_size, ")"); - return true; } uint32_t offsetof_end = needed_size; uint32_t offsetof_size = 0; @@ -3862,16 +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_error(ctx, LIB9P_ERRNO_L_ERANGE, "Txattrcreate message too large to marshal into ", + return error_new(E_POSIX_ERANGE, "Txattrcreate message too large to marshal into ", ctx->version ? "negotiated" : "client", " limit", " (", needed_size, " > ", ctx->max_msg_size, ")"); - return true; } uint32_t offsetof_end = needed_size; uint32_t offsetof_size = 0; @@ -3883,32 +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_error(ctx, LIB9P_ERRNO_L_ERANGE, "Rxattrcreate message too large to marshal into ", + return error_new(E_POSIX_ERANGE, "Rxattrcreate message too large to marshal into ", ctx->version ? "negotiated" : "server", " limit", " (", needed_size, " > ", ctx->max_msg_size, ")"); - return true; } uint32_t offsetof_end = needed_size; uint32_t offsetof_size = 0; MARSHAL_U32LE(ctx, offsetof_end - offsetof_size); MARSHAL_U8LE(ctx, 33); MARSHAL_U16LE(ctx, val->tag); - return false; + 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_error(ctx, LIB9P_ERRNO_L_ERANGE, "Treaddir message too large to marshal into ", + return error_new(E_POSIX_ERANGE, "Treaddir message too large to marshal into ", ctx->version ? "negotiated" : "client", " limit", " (", needed_size, " > ", ctx->max_msg_size, ")"); - return true; } uint32_t offsetof_end = needed_size; uint32_t offsetof_size = 0; @@ -3918,16 +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_error(ctx, LIB9P_ERRNO_L_ERANGE, "Rreaddir message too large to marshal into ", + return error_new(E_POSIX_ERANGE, "Rreaddir message too large to marshal into ", ctx->version ? "negotiated" : "server", " limit", " (", needed_size, " > ", ctx->max_msg_size, ")"); - return true; } uint32_t offsetof_end = (uint32_t)needed_size; uint32_t offsetof_size = 0; @@ -3936,16 +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_error(ctx, LIB9P_ERRNO_L_ERANGE, "Tfsync message too large to marshal into ", + return error_new(E_POSIX_ERANGE, "Tfsync message too large to marshal into ", ctx->version ? "negotiated" : "client", " limit", " (", needed_size, " > ", ctx->max_msg_size, ")"); - return true; } uint32_t offsetof_end = needed_size; uint32_t offsetof_size = 0; @@ -3954,32 +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_error(ctx, LIB9P_ERRNO_L_ERANGE, "Rfsync message too large to marshal into ", + return error_new(E_POSIX_ERANGE, "Rfsync message too large to marshal into ", ctx->version ? "negotiated" : "server", " limit", " (", needed_size, " > ", ctx->max_msg_size, ")"); - return true; } uint32_t offsetof_end = needed_size; uint32_t offsetof_size = 0; MARSHAL_U32LE(ctx, offsetof_end - offsetof_size); MARSHAL_U8LE(ctx, 51); MARSHAL_U16LE(ctx, val->tag); - return false; + 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_error(ctx, LIB9P_ERRNO_L_ERANGE, "Tlock message too large to marshal into ", + return error_new(E_POSIX_ERANGE, "Tlock message too large to marshal into ", ctx->version ? "negotiated" : "client", " limit", " (", needed_size, " > ", ctx->max_msg_size, ")"); - return true; } uint32_t offsetof_end = needed_size; uint32_t offsetof_size = 0; @@ -3994,16 +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_error(ctx, LIB9P_ERRNO_L_ERANGE, "Rlock message too large to marshal into ", + return error_new(E_POSIX_ERANGE, "Rlock message too large to marshal into ", ctx->version ? "negotiated" : "server", " limit", " (", needed_size, " > ", ctx->max_msg_size, ")"); - return true; } uint32_t offsetof_end = needed_size; uint32_t offsetof_size = 0; @@ -4011,16 +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_error(ctx, LIB9P_ERRNO_L_ERANGE, "Tgetlock message too large to marshal into ", + return error_new(E_POSIX_ERANGE, "Tgetlock message too large to marshal into ", ctx->version ? "negotiated" : "client", " limit", " (", needed_size, " > ", ctx->max_msg_size, ")"); - return true; } uint32_t offsetof_end = needed_size; uint32_t offsetof_size = 0; @@ -4034,16 +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_error(ctx, LIB9P_ERRNO_L_ERANGE, "Rgetlock message too large to marshal into ", + return error_new(E_POSIX_ERANGE, "Rgetlock message too large to marshal into ", ctx->version ? "negotiated" : "server", " limit", " (", needed_size, " > ", ctx->max_msg_size, ")"); - return true; } uint32_t offsetof_end = needed_size; uint32_t offsetof_size = 0; @@ -4056,16 +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_error(ctx, LIB9P_ERRNO_L_ERANGE, "Tlink message too large to marshal into ", + return error_new(E_POSIX_ERANGE, "Tlink message too large to marshal into ", ctx->version ? "negotiated" : "client", " limit", " (", needed_size, " > ", ctx->max_msg_size, ")"); - return true; } uint32_t offsetof_end = needed_size; uint32_t offsetof_size = 0; @@ -4076,32 +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_error(ctx, LIB9P_ERRNO_L_ERANGE, "Rlink message too large to marshal into ", + return error_new(E_POSIX_ERANGE, "Rlink message too large to marshal into ", ctx->version ? "negotiated" : "server", " limit", " (", needed_size, " > ", ctx->max_msg_size, ")"); - return true; } uint32_t offsetof_end = needed_size; uint32_t offsetof_size = 0; MARSHAL_U32LE(ctx, offsetof_end - offsetof_size); MARSHAL_U8LE(ctx, 71); MARSHAL_U16LE(ctx, val->tag); - return false; + 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_error(ctx, LIB9P_ERRNO_L_ERANGE, "Tmkdir message too large to marshal into ", + return error_new(E_POSIX_ERANGE, "Tmkdir message too large to marshal into ", ctx->version ? "negotiated" : "client", " limit", " (", needed_size, " > ", ctx->max_msg_size, ")"); - return true; } uint32_t offsetof_end = needed_size; uint32_t offsetof_size = 0; @@ -4113,16 +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_error(ctx, LIB9P_ERRNO_L_ERANGE, "Rmkdir message too large to marshal into ", + return error_new(E_POSIX_ERANGE, "Rmkdir message too large to marshal into ", ctx->version ? "negotiated" : "server", " limit", " (", needed_size, " > ", ctx->max_msg_size, ")"); - return true; } uint32_t offsetof_end = needed_size; uint32_t offsetof_size = 0; @@ -4132,16 +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_error(ctx, LIB9P_ERRNO_L_ERANGE, "Trenameat message too large to marshal into ", + return error_new(E_POSIX_ERANGE, "Trenameat message too large to marshal into ", ctx->version ? "negotiated" : "client", " limit", " (", needed_size, " > ", ctx->max_msg_size, ")"); - return true; } uint32_t offsetof_end = needed_size; uint32_t offsetof_size = 0; @@ -4154,32 +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_error(ctx, LIB9P_ERRNO_L_ERANGE, "Rrenameat message too large to marshal into ", + return error_new(E_POSIX_ERANGE, "Rrenameat message too large to marshal into ", ctx->version ? "negotiated" : "server", " limit", " (", needed_size, " > ", ctx->max_msg_size, ")"); - return true; } uint32_t offsetof_end = needed_size; uint32_t offsetof_size = 0; MARSHAL_U32LE(ctx, offsetof_end - offsetof_size); MARSHAL_U8LE(ctx, 75); MARSHAL_U16LE(ctx, val->tag); - return false; + 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_error(ctx, LIB9P_ERRNO_L_ERANGE, "Tunlinkat message too large to marshal into ", + return error_new(E_POSIX_ERANGE, "Tunlinkat message too large to marshal into ", ctx->version ? "negotiated" : "client", " limit", " (", needed_size, " > ", ctx->max_msg_size, ")"); - return true; } uint32_t offsetof_end = needed_size; uint32_t offsetof_size = 0; @@ -4190,34 +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_error(ctx, LIB9P_ERRNO_L_ERANGE, "Runlinkat message too large to marshal into ", + return error_new(E_POSIX_ERANGE, "Runlinkat message too large to marshal into ", ctx->version ? "negotiated" : "server", " limit", " (", needed_size, " > ", ctx->max_msg_size, ")"); - return true; } uint32_t offsetof_end = needed_size; uint32_t offsetof_size = 0; MARSHAL_U32LE(ctx, offsetof_end - offsetof_size); MARSHAL_U8LE(ctx, 77); MARSHAL_U16LE(ctx, val->tag); - return false; + 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_error(ctx, LIB9P_ERRNO_L_ERANGE, "Tsession message too large to marshal into ", + return error_new(E_POSIX_ERANGE, "Tsession message too large to marshal into ", ctx->version ? "negotiated" : "client", " limit", " (", needed_size, " > ", ctx->max_msg_size, ")"); - return true; } uint32_t offsetof_end = needed_size; uint32_t offsetof_size = 0; @@ -4225,35 +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_error(ctx, LIB9P_ERRNO_L_ERANGE, "Rsession message too large to marshal into ", + return error_new(E_POSIX_ERANGE, "Rsession message too large to marshal into ", ctx->version ? "negotiated" : "server", " limit", " (", needed_size, " > ", ctx->max_msg_size, ")"); - return true; } uint32_t offsetof_end = needed_size; uint32_t offsetof_size = 0; MARSHAL_U32LE(ctx, offsetof_end - offsetof_size); MARSHAL_U8LE(ctx, 151); MARSHAL_U16LE(ctx, val->tag); - return false; + 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_error(ctx, LIB9P_ERRNO_L_ERANGE, "Tsread message too large to marshal into ", + return error_new(E_POSIX_ERANGE, "Tsread message too large to marshal into ", ctx->version ? "negotiated" : "client", " limit", " (", needed_size, " > ", ctx->max_msg_size, ")"); - return true; } uint32_t offsetof_end = (uint32_t)needed_size; uint32_t offsetof_size = 0; @@ -4266,16 +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_error(ctx, LIB9P_ERRNO_L_ERANGE, "Rsread message too large to marshal into ", + return error_new(E_POSIX_ERANGE, "Rsread message too large to marshal into ", ctx->version ? "negotiated" : "server", " limit", " (", needed_size, " > ", ctx->max_msg_size, ")"); - return true; } uint32_t offsetof_end = (uint32_t)needed_size; uint32_t offsetof_size = 0; @@ -4284,19 +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_error(ctx, LIB9P_ERRNO_L_ERANGE, "Tswrite message too large to marshal into ", + return error_new(E_POSIX_ERANGE, "Tswrite message too large to marshal into ", ctx->version ? "negotiated" : "client", " limit", " (", needed_size, " > ", ctx->max_msg_size, ")"); - return true; } uint32_t offsetof_end = (uint32_t)needed_size; uint32_t offsetof_size = 0; @@ -4311,16 +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_error(ctx, LIB9P_ERRNO_L_ERANGE, "Rswrite message too large to marshal into ", + return error_new(E_POSIX_ERANGE, "Rswrite message too large to marshal into ", ctx->version ? "negotiated" : "server", " limit", " (", needed_size, " > ", ctx->max_msg_size, ")"); - return true; } uint32_t offsetof_end = needed_size; uint32_t offsetof_size = 0; @@ -4328,7 +4253,7 @@ 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 */ @@ -6507,7 +6432,7 @@ static bool marshal_Rswrite(struct lib9p_ctx *ctx, struct lib9p_msg_Rswrite *val fmt_print_tag(w, ctx, &self->tag); fmt_print_str(w, " count="); fmt_print_base10(w, self->count); - if (is_valid_utf8_without_nul((uint8_t *)self->data, (size_t)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) @@ -6528,7 +6453,7 @@ static bool marshal_Rswrite(struct lib9p_ctx *ctx, struct lib9p_msg_Rswrite *val fmt_print_base10(w, self->offset); fmt_print_str(w, " count="); fmt_print_base10(w, self->count); - if (is_valid_utf8_without_nul((uint8_t *)self->data, (size_t)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) @@ -6763,7 +6688,7 @@ static bool marshal_Rswrite(struct lib9p_ctx *ctx, struct lib9p_msg_Rswrite *val fmt_print_tag(w, ctx, &self->tag); fmt_print_str(w, " count="); fmt_print_base10(w, self->count); - if (is_valid_utf8_without_nul((uint8_t *)self->data, (size_t)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) @@ -6846,7 +6771,7 @@ static bool marshal_Rswrite(struct lib9p_ctx *ctx, struct lib9p_msg_Rswrite *val fmt_print_tag(w, ctx, &self->tag); fmt_print_str(w, " count="); fmt_print_base10(w, self->count); - if (is_valid_utf8_without_nul((uint8_t *)self->data, (size_t)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) @@ -7445,7 +7370,7 @@ static bool marshal_Rswrite(struct lib9p_ctx *ctx, struct lib9p_msg_Rswrite *val fmt_print_str(w, " ]"); fmt_print_str(w, " count="); fmt_print_base10(w, self->count); - if (is_valid_utf8_without_nul((uint8_t *)self->data, (size_t)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) @@ -8178,7 +8103,7 @@ const struct _lib9p_send_tentry _lib9p_table_Rmsg_send[LIB9P_VER_NUM][0x80] = { }; #if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u -LM_FLATTEN ssize_t _lib9p_stat_validate(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes, uint32_t *ret_net_size) { +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) { diff --git a/lib9p/core_include/lib9p/_core_generated.h b/lib9p/core_include/lib9p/_core_generated.h index 5b9a67d..3f87181 100644 --- a/lib9p/core_include/lib9p/_core_generated.h +++ b/lib9p/core_include/lib9p/_core_generated.h @@ -4,11 +4,8 @@ #error Do not include <lib9p/_core_generated.h> directly; include <lib9p/core.h> instead #endif - /* config *********************************************************************/ -#include "config.h" - #ifndef CONFIG_9P_ENABLE_9P2000 #error config.h must define CONFIG_9P_ENABLE_9P2000 #endif @@ -673,8 +670,8 @@ typedef uint8_t lib9p_lock_status_t; #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; }; /* size = 13 ; max_iov = 1 ; max_copy = 13 */ @@ -718,18 +715,18 @@ struct 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; }; /* 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; }; /* size = 11 ; max_iov = 1 ; max_copy = 11 */ @@ -876,9 +873,9 @@ struct 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; }; /* size = 15 ; max_iov = 1 ; max_copy = 15 */ @@ -929,9 +926,9 @@ struct 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; }; /* size = 11 ; max_iov = 1 ; max_copy = 11 */ @@ -1294,12 +1291,12 @@ struct 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; }; #endif /* CONFIG_9P_ENABLE_9P2000_e */ @@ -1376,12 +1373,12 @@ struct lib9p_msg_Twstat { 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 4941220..84ecd0f 100644 --- a/lib9p/core_include/lib9p/core.h +++ b/lib9p/core_include/lib9p/core.h @@ -7,21 +7,17 @@ #ifndef _LIB9P_CORE_H_ #define _LIB9P_CORE_H_ -#include <stdint.h> /* for uint{n}_t */ -#include <string.h> /* for memset() */ -#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 iovec */ +#include <libhw/generic/io.h> /* for struct wr_iovec */ #include <libmisc/assert.h> #include <libmisc/fmt.h> #define CONFIG_9P_ENABLE_uninitialized 1 -#include <lib9p/_core_generated.h> +#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 ******************************************************************/ @@ -35,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); @@ -47,40 +43,8 @@ struct lib9p_ctx { /* negotiated */ enum lib9p_version version; uint32_t max_msg_size; - - /* state */ -#if CONFIG_9P_ENABLE_9P2000_u || CONFIG_9P_ENABLE_9P2000_L - 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); - -#if CONFIG_9P_ENABLE_9P2000_u || CONFIG_9P_ENABLE_9P2000_L - #define _lib9p_set_err_num(ctx, linux_errno) do { (ctx)->err_num = linux_errno; } while (0) -#else - #define _lib9p_set_err_num(ctx, linux_errno) do { } while (0) -#endif - -/** Write a <libmisc/fmt.h>-style error into ctx, return -1. */ -#define lib9p_error(ctx, linux_errno, ...) ({ \ - if (!lib9p_ctx_has_error(ctx)) { \ - _lib9p_set_err_num(ctx, linux_errno); \ - struct fmt_buf _w = { \ - .dat = (ctx)->err_msg, \ - .cap = sizeof((ctx)->err_msg), \ - }; \ - lo_interface fmt_dest w = lo_box_fmt_buf_as_fmt_dest(&_w); \ - fmt_print(w, __VA_ARGS__); \ - if (_w.len < _w.cap) \ - memset(_w.dat + _w.len, 0, _w.cap - _w.len); \ - } \ - -1; \ -}) - /* misc utilities *************************************************************/ uint32_t lib9p_version_min_Rerror_size(enum lib9p_version); @@ -98,23 +62,19 @@ void fmt_print_lib9p_msg(lo_interface fmt_dest w, struct lib9p_ctx *ctx, enum li * 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`. @@ -140,27 +100,27 @@ 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 ************************************************/ @@ -172,16 +132,16 @@ void fmt_print_lib9p_stat(lo_interface fmt_dest w, struct lib9p_ctx *ctx, struct /** * 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`. @@ -206,14 +166,12 @@ 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); diff --git a/lib9p/core_tables.h b/lib9p/core_tables.h index e3dc8f4..94cdbe1 100644 --- a/lib9p/core_tables.h +++ b/lib9p/core_tables.h @@ -27,7 +27,7 @@ struct _lib9p_msg_tentry { _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; @@ -36,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; }; @@ -54,7 +54,7 @@ extern const struct _lib9p_send_tentry _lib9p_table_Rmsg_send[LIB9P_VER_NUM][0x8 /* stat ***********************************************************************/ #if _LIB9P_ENABLE_stat -ssize_t _lib9p_stat_validate(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes, uint32_t *ret_net_size); +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 diff --git a/lib9p/core_utf8.h b/lib9p/core_utf8.h deleted file mode 100644 index 2c451e0..0000000 --- a/lib9p/core_utf8.h +++ /dev/null @@ -1,36 +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_ - -#include <stddef.h> /* for size_t */ -#include <stdint.h> /* for uint{n}_t */ - -static inline bool _is_valid_utf8(uint8_t *str, size_t len, bool forbid_nul) { - uint32_t ch; - uint8_t chlen; - 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/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/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 18165e0..3e4de91 100644 --- a/lib9p/srv.c +++ b/lib9p/srv.c @@ -4,23 +4,16 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ -#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,7 +34,7 @@ #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 ********************************************************************/ @@ -48,12 +43,6 @@ bool lib9p_srv_flush_requested(struct lib9p_srv_ctx *ctx) { return cr_chan_can_send(&ctx->flush_ch); } -void lib9p_srv_acknowledge_flush(struct lib9p_srv_ctx *ctx) { - assert(ctx); - assert(cr_chan_can_send(&ctx->flush_ch)); - ctx->flush_acknowledged = true; -} - #define req_debug(...) \ log_debugln( \ "cid=", cr_getcid(), ": ", \ @@ -62,6 +51,18 @@ void lib9p_srv_acknowledge_flush(struct lib9p_srv_ctx *ctx) { /* 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, @@ -115,6 +116,7 @@ struct srv_fidinfo { } auth; }; }; +DECLARE_ERROR_OR_(struct srv_fidinfo *, srv_fidinfop); /* contexts ************************************** * @@ -130,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; }; @@ -145,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 */ @@ -156,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) @@ -164,7 +165,8 @@ 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_srv_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); @@ -176,20 +178,33 @@ static inline bool srv_check_perm(struct srv_req *ctx, struct lib9p_srv_stat *st } [[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 = malloc(sizeof(struct lib9p_srv_userid) + name.len); - if (!ret) - return NULL; + 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 = (void *)&ret[1]; - memcpy(ret->name.utf8, name.utf8, name.len); + ret->name.utf8 = end_dat; ret->refcount = 1; return ret; } @@ -219,9 +234,10 @@ static struct lib9p_srv_userid *srv_userid_incref(struct lib9p_srv_userid *useri * * 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)); @@ -253,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 (;;) { @@ -271,11 +287,13 @@ 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, struct srv_fidinfo *fidinfo, bool remove) { +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); @@ -284,7 +302,7 @@ static inline void srv_fid_del(struct srv_req *ctx, lib9p_fid_t fid, struct srv_ assert(pathinfo); if (remove) - LO_CALL(pathinfo->file, remove, ctx); + err = LO_CALL(pathinfo->file, remove, ctx); if (fidinfo->flags & FIDFLAG_OPEN) { switch (fidinfo->type) { @@ -305,6 +323,8 @@ static inline void srv_fid_del(struct srv_req *ctx, lib9p_fid_t fid, struct srv_ map_del(&ctx->parent_sess->fids, fid); ctx->user = srv_userid_decref(ctx->user); + + return err; } /** @@ -312,7 +332,8 @@ static inline void srv_fid_del(struct srv_req *ctx, lib9p_fid_t fid, struct srv_ * 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); @@ -333,9 +354,7 @@ static inline struct srv_fidinfo *srv_fid_store(struct srv_req *ctx, lib9p_fid_t 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){ @@ -344,7 +363,7 @@ static inline struct srv_fidinfo *srv_fid_store(struct srv_req *ctx, lib9p_fid_t .user = srv_userid_incref(ctx->user), }); assert(fidinfo); - return fidinfo; + return ERROR_NEW_VAL(srv_fidinfop, fidinfo); } /* base utilities *************************************************************/ @@ -358,29 +377,40 @@ static void srv_msglog(struct srv_req *req, enum lib9p_msg_type typ, void *hostm 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_error log_errorln - -static void srv_respond_error(struct srv_req *req) { -#if CONFIG_9P_ENABLE_9P2000_u || CONFIG_9P_ENABLE_9P2000_L - 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 }; @@ -389,39 +419,36 @@ static void srv_respond_error(struct srv_req *req) { 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; - - lib9p_Rmsg_marshal(&req->basectx, - LIB9P_TYP_Rerror, &host, - &net); + error marshal_err = srv_write_Rmsg(req, LIB9P_TYP_Rerror, &host); + assert(ERROR_IS_NULL(marshal_err)); - srv_msglog(req, LIB9P_TYP_Rerror, &host); - r = srv_write_Rmsg(req, &net); - if (r < 0) - srv_nonrespond_error("write: ", 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_error("read: ", net_strerror(-r)); - return true; - } else if (r == 0) { - if (*done != 0) - srv_nonrespond_error("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; } @@ -434,112 +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_error("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) - cr_rpc_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 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, + 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_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_error(&req.basectx, LIB9P_ERRNO_L_EMSGSIZE, - "T-message larger than ", sess.initialized ? "negotiated" : "server", " limit", - " (", 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, */ - lib9p_srv_worker(&req); + lib9p_srv_worker_Tversion(req); else /* ...but usually in another coroutine. */ - cr_rpc_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); + 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); - assert(map_len(&sess.reqs) == 0); - map_free(&sess.reqs); - - struct srv_req pseudoreq = { + /* Cleanup `sess->fids`. */ + *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, }; - MAP_FOREACH(&sess.fids, fid, fidinfo) { - srv_fid_del(&pseudoreq, fid, fidinfo, false); - if (lib9p_ctx_has_error(&pseudoreq.basectx)) { - srv_nonrespond_error("clunk: ", (strn, pseudoreq.basectx.err_msg, CONFIG_9P_MAX_ERR_SIZE)); - lib9p_ctx_clear_error(&pseudoreq.basectx); + 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 ************************************************************/ @@ -555,7 +596,7 @@ void lib9p_srv_worker_loop(struct lib9p_srv *srv) { for (;;) { /* Receive the request from the reader coroutine. ************/ - rpc_handle = cr_rpc_recv_req(&srv->_reqch); + rpc_handle = cr_rpc_recv_req(&srv->reqch); if (!rpc_handle.req) { srv->writers--; cr_rpc_send_resp(rpc_handle, 0); @@ -604,16 +645,16 @@ _HANDLER_PROTO(swrite); #endif void lib9p_srv_worker(struct srv_req *ctx) { - uint8_t *host_req = NULL; + [[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) { - srv_respond_error(ctx); + 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; } - host_req = calloc(1, host_size); - assert(host_req); + 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); @@ -622,7 +663,6 @@ void lib9p_srv_worker(struct srv_req *ctx) { /* Handle it. ********************************************************/ #define CASE(typ) case LIB9P_TYP_##typ: handle_##typ(ctx, (void *)host_req); break LM_PARTIAL_SWITCH (typ) { - CASE(Tversion); #if _LIB9P_ENABLE_stat CASE(Tauth); CASE(Tattach); @@ -662,42 +702,75 @@ void lib9p_srv_worker(struct srv_req *ctx) { } if (ctx->parent_sess->closing && !map_len(&ctx->parent_sess->reqs)) cr_unpause(ctx->parent_sess->parent_conn->reader); - if (host_req) - free(host_req); free(ctx->net_bytes); } -static inline void _srv_respond(struct srv_req *ctx, enum lib9p_msg_type resp_typ, void *host_resp) { +static void lib9p_srv_worker_Tversion(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. ********************************************************/ + handle_Tversion(ctx, (void *)host_req); + assert(ctx->responded); + + /* Release resources. ************************************************/ + release: + map_del(&ctx->parent_sess->reqs, ctx->tag); + size_t nwaiters; + while ((nwaiters = cr_chan_num_waiters(&ctx->flush_ch))) { + cr_chan_send(&ctx->flush_ch, (nwaiters == 1) + ? _LIB9P_SRV_FLUSH_RFLUSH + : _LIB9P_SRV_FLUSH_SILENT); + } + if (ctx->parent_sess->closing && !map_len(&ctx->parent_sess->reqs)) + cr_unpause(ctx->parent_sess->parent_conn->reader); + free(ctx->net_bytes); +} + +static void _srv_respond(struct srv_req *ctx, enum lib9p_msg_type resp_typ, void *host_resp, error err) { assert(!ctx->responded); - if (lib9p_ctx_has_error(&ctx->basectx)) { - error: - srv_respond_error(ctx); - } else if (ctx->flush_acknowledged) { - /* do nothing */ + 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); - struct lib9p_Rmsg_send_buf net_resp; - if (lib9p_Rmsg_marshal(&ctx->basectx, - resp_typ, host_resp, - &net_resp)) + err = srv_write_Rmsg(ctx, resp_typ, host_resp); + if (!ERROR_IS_NULL(err)) goto error; - srv_msglog(ctx, resp_typ, host_resp); - srv_write_Rmsg(ctx, &net_resp); } ctx->responded = true; } -#define srv_respond(CTX, TYP, HOST_RESP) do { \ - struct lib9p_msg_R##TYP *_host_resp = HOST_RESP; \ - _srv_respond(CTX, LIB9P_TYP_R##TYP, _host_resp); \ +#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, typ, req) \ +#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) { @@ -737,13 +810,12 @@ static void handle_Tversion(struct srv_req *ctx, 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_error(&ctx->basectx, - LIB9P_ERRNO_L_EDOM, "requested max_msg_size is less than minimum for ", lib9p_version_str(version), - " (", req->max_msg_size, " < ", min_msg_size,")"); + 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"); @@ -756,7 +828,7 @@ 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 *args = stack_alloc(map_len(&ctx->parent_sess->reqs), struct cr_select_arg); + 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; MAP_FOREACH(&ctx->parent_sess->reqs, tag, reqpp) { @@ -766,13 +838,14 @@ static void handle_Tversion(struct srv_req *ctx, assert(i == map_len(&ctx->parent_sess->reqs)); cr_select_v(i, args); } + free(args); } /* Close all FIDs. */ MAP_FOREACH(&ctx->parent_sess->fids, fid, fidinfo) { - srv_fid_del(ctx, fid, fidinfo, false); - if (lib9p_ctx_has_error(&ctx->basectx)) { - srv_nonrespond_error("clunk: ", (strn, ctx->basectx.err_msg, CONFIG_9P_MAX_ERR_SIZE)); - lib9p_ctx_clear_error(&ctx->basectx); + error fiderr = srv_fid_del(ctx, fid, fidinfo, false); + if (!ERROR_IS_NULL(fiderr)) { + srv_nonrespond_error("clunk: ", (error, fiderr)); + error_cleanup(&fiderr); } } @@ -781,7 +854,7 @@ static void handle_Tversion(struct srv_req *ctx, ctx->parent_sess->max_msg_size = resp.max_msg_size; tversion_return: - srv_respond(ctx, version, &resp); + srv_respond(ctx, version, &resp, err); } #if _LIB9P_ENABLE_stat @@ -791,23 +864,22 @@ static void handle_Tauth(struct srv_req *ctx, 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"); + err = error_new(E_POSIX_EOPNOTSUPP, "authentication not required"); goto tauth_return; } ctx->user = srv_userid_new(req->uname, req->unum); - srv->auth(ctx, req->aname); - - lib9p_error(&ctx->basectx, - LIB9P_ERRNO_L_EOPNOTSUPP, "TODO: auth not implemented"); + err = srv->auth(ctx, req->aname); + if (!ERROR_IS_NULL(err)) + goto tauth_return; - if (lib9p_ctx_has_error(&ctx->basectx)) - ctx->user = srv_userid_decref(ctx->user); + err = error_new(E_POSIX_EOPNOTSUPP, "TODO: auth not implemented"); tauth_return: - srv_respond(ctx, auth, &resp); + 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, @@ -815,8 +887,7 @@ static void handle_Tattach(struct srv_req *ctx, srv_handler_common(ctx, attach, req); if (req->fid == LIB9P_FID_NOFID) { - lib9p_error(&ctx->basectx, - LIB9P_ERRNO_L_EBADF, "cannot assign to NOFID"); + err = error_new(E_POSIX_EBADF, "cannot assign to NOFID"); goto tattach_return; } @@ -824,48 +895,43 @@ static void handle_Tattach(struct srv_req *ctx, 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"); + 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)) - lib9p_error(&ctx->basectx, - LIB9P_ERRNO_L_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)); + 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) - lib9p_error(&ctx->basectx, - LIB9P_ERRNO_L_EACCES, - "FID provided as auth-file is for user=", afid->user->num, - " and cannot be used for user=", 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_error(&ctx->basectx, - LIB9P_ERRNO_L_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)); + 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)) + 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"); + err = error_new(E_POSIX_EACCES, "FID provided as auth-file, but no auth-file is required"); goto tattach_return; } 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)) + 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); @@ -874,7 +940,9 @@ 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); goto tattach_return; } @@ -883,7 +951,7 @@ static void handle_Tattach(struct srv_req *ctx, tattach_return: if (ctx->user) ctx->user = srv_userid_decref(ctx->user); - srv_respond(ctx, attach, &resp); + srv_respond(ctx, attach, &resp, err); } static void handle_Tflush(struct srv_req *ctx, @@ -898,15 +966,17 @@ static void handle_Tflush(struct srv_req *ctx, CR_SELECT_SEND(&ctx->flush_ch, &res))) { case 0: /* original request returned */ req_debug("original request (tag=", req->oldtag, ") returned"); - ctx->flush_acknowledged = (res == _LIB9P_SRV_FLUSH_SILENT); + if (res == _LIB9P_SRV_FLUSH_SILENT) { + ctx->responded = true; + return; + } break; case 1: /* flush itself got flushed */ - req_debug("flush itself flushed"); - ctx->flush_acknowledged = true; - break; + ctx->responded = true; + return; } } - srv_respond(ctx, flush, &resp); + srv_respond(ctx, flush, &resp, err); } static void handle_Twalk(struct srv_req *ctx, @@ -914,20 +984,17 @@ static void handle_Twalk(struct srv_req *ctx, srv_handler_common(ctx, walk, req); if (req->newfid == LIB9P_FID_NOFID) { - lib9p_error(&ctx->basectx, - LIB9P_ERRNO_L_EBADF, "cannot assign to NOFID"); + 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_error(&ctx->basectx, - LIB9P_ERRNO_L_EBADF, "bad file number ", req->fid); + 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"); + err = error_new(E_POSIX_EALREADY, "cannot walk on FID open for I/O"); goto twalk_return; } ctx->user = srv_userid_incref(fidinfo->user); @@ -936,12 +1003,10 @@ static void handle_Twalk(struct srv_req *ctx, assert(pathinfo); pathinfo->gc_refcount++; - struct lib9p_qid _resp_qid[16]; - resp.wqid = _resp_qid; + 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; } @@ -951,22 +1016,22 @@ static void handle_Twalk(struct srv_req *ctx, 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_srv_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_srv_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; } @@ -978,18 +1043,23 @@ static void handle_Twalk(struct srv_req *ctx, pathinfo = new_pathinfo; } if (resp.nwqid == req->nwname) { - if (!srv_fid_store(ctx, req->newfid, pathinfo, req->newfid == req->fid)) + 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); + error_cleanup(&err); } twalk_return: if (ctx->user) ctx->user = srv_userid_decref(ctx->user); - srv_respond(ctx, walk, &resp); + srv_respond(ctx, walk, &resp, err); + if (resp.wqid) + free(resp.wqid); } static void handle_Topen(struct srv_req *ctx, @@ -999,21 +1069,18 @@ static void handle_Topen(struct srv_req *ctx, /* Check that the FID is valid for this. */ struct srv_fidinfo *fidinfo = map_load(&ctx->parent_sess->fids, req->fid); if (!fidinfo) { - lib9p_error(&ctx->basectx, - LIB9P_ERRNO_L_EBADF, "bad file number ", req->fid); + 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"); + 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"); + err = error_new(E_POSIX_EISDIR, "directories cannot be written, executed, truncated, or removed-on-close"); goto topen_return; } } @@ -1029,24 +1096,23 @@ 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_srv_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_srv_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_srv_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_srv_stat_assert(stat); + lib9p_srv_stat_assert(&stat); if ((stat.mode & LIB9P_DM_EXCL) && pathinfo->io_refcount) { - lib9p_error(&ctx->basectx, - LIB9P_ERRNO_L_EEXIST, "exclusive file is already opened"); + err = error_new(E_POSIX_EEXIST, "exclusive file is already opened"); goto topen_return; } if (stat.mode & LIB9P_DM_APPEND) { @@ -1073,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; } @@ -1084,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: @@ -1123,60 +1194,66 @@ static void handle_Topen(struct srv_req *ctx, topen_return: if (ctx->user) ctx->user = srv_userid_decref(ctx->user); - srv_respond(ctx, open, &resp); + srv_respond(ctx, open, &resp, err); } static void handle_Tcreate(struct srv_req *ctx, struct lib9p_msg_Tcreate *req) { srv_handler_common(ctx, create, req); - lib9p_error(&ctx->basectx, - LIB9P_ERRNO_L_EOPNOTSUPP, "create not (yet?) implemented"); + err = error_new(E_POSIX_EOPNOTSUPP, "create not (yet?) implemented"); - srv_respond(ctx, create, &resp); + srv_respond(ctx, create, &resp, err); } -static inline struct lib9p_stat srv_stat_to_net_stat(struct lib9p_srv_stat in) { - return (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, +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, + .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) { - srv_handler_common(ctx, read, req); - char *heap = NULL; + 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_error(&ctx->basectx, - LIB9P_ERRNO_L_EBADF, "bad file number ", req->fid); - goto tread_return; + 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"); - goto tread_return; + srv_respond(ctx, read, NULL, error_new(E_POSIX_EINVAL, "FID not open for reading")); + return; } /* Do it. */ @@ -1184,102 +1261,190 @@ static void handle_Tread(struct srv_req *ctx, switch (fidinfo->type) { case SRV_FILETYPE_DIR: #if _LIB9P_ENABLE_stat - /* Seek. */ - if (req->offset == 0) { - fidinfo->dir.idx = 0; - fidinfo->dir.off = 0; - fidinfo->dir.buffered_dirent = (struct lib9p_srv_dirent){}; - } else if (req->offset != fidinfo->dir.off) { - lib9p_error(&ctx->basectx, - LIB9P_ERRNO_L_EINVAL, "invalid offset (must be 0 or ", fidinfo->dir.off, "): ", req->offset); - goto tread_return; - } - /* Read. */ - resp.data = heap = malloc(req->count); - resp.count = 0; - struct srv_pathinfo *dir_pathinfo = NULL; - for (;;) { - lo_interface lib9p_srv_file member_file = {}; - struct lib9p_srv_dirent member_dirent; - if (fidinfo->dir.buffered_dirent.name.len) { - member_dirent = fidinfo->dir.buffered_dirent; - } else { - member_dirent = LO_CALL(fidinfo->dir.io, dread, ctx, fidinfo->dir.idx); - if (lib9p_ctx_has_error(&ctx->basectx)) { - if (!resp.count) - goto tread_return; - lib9p_ctx_clear_error(&ctx->basectx); + handle_read_dir(ctx, fidinfo, req->offset, req->count); +#else + assert_notreached("Tread for directory on protocol version without that"); +#endif + break; + case SRV_FILETYPE_FILE: + handle_read_file(ctx, fidinfo, req->offset, req->count); + break; + case SRV_FILETYPE_AUTH: + assert_notreached("TODO: auth not yet implemented"); + break; + } + ctx->user = srv_userid_decref(ctx->user); +} + +struct rread_writer { + struct srv_req *ctx; + size_t count; + bool written; + +}; +LO_IMPLEMENTATION_STATIC(io_writer, struct rread_writer, rread); + +static size_t_and_error rread_writev(struct rread_writer *self, const struct wr_iovec *iov, int iovcnt) { + assert(self); + assert(!self->written); + assert(iovcnt == 1); + assert(iov); + assert(iov->iov_len <= self->count); + + struct lib9p_msg_Rread resp = { + .tag = self->ctx->tag, + .count = iov->iov_len, + .data = iov->iov_write_from, + }; + + srv_respond(self->ctx, read, &resp, ERROR_NULL); + + self->written = true; + return ERROR_AND(size_t, iov->iov_len, ERROR_NULL); +} + +static void handle_read_file(struct srv_req *ctx, struct srv_fidinfo *fidinfo, uint64_t offset, uint32_t count) { + struct rread_writer _writer = { + .ctx = ctx, + .count = (size_t) count, + .written = false, + }; + lo_interface io_writer writer = LO_BOX(io_writer, &_writer); + error err = LO_CALL(fidinfo->file.io, pread, ctx, writer, offset, count); + assert(ERROR_IS_NULL(err) == _writer.written); + if (!ERROR_IS_NULL(err)) + srv_respond(ctx, read, NULL, err); +} + +#if _LIB9P_ENABLE_stat + +static error stat_and_convert(struct lib9p_stat *out, struct srv_req *ctx, lo_interface lib9p_srv_file file) { + struct lib9p_srv_stat hoststat; + error err = LO_CALL(file, stat, ctx, &hoststat); + if (!ERROR_IS_NULL(err)) + return err; + + srv_stat_to_net_stat(out, &hoststat); + return ERROR_NULL; +} + +static uint32_t_or_error stat_and_encode(struct srv_req *ctx, lo_interface lib9p_srv_file file, void *out, size_t out_len) { + struct lib9p_srv_stat hoststat; + struct lib9p_stat netstat; + error err; + + err = LO_CALL(file, stat, ctx, &hoststat); + + if (!ERROR_IS_NULL(err)) + return ERROR_NEW_ERR(uint32_t, err); + srv_stat_to_net_stat(&netstat, &hoststat); + return ERROR_NEW_VAL(uint32_t, lib9p_stat_marshal(&ctx->basectx, out_len, &netstat, (uint8_t *)out)); +} + +static void handle_read_dir(struct srv_req *ctx, struct srv_fidinfo *fidinfo, uint64_t offset, uint32_t count) { + /* Seek. */ + if (offset == 0) { + fidinfo->dir.idx = 0; + fidinfo->dir.off = 0; + fidinfo->dir.buffered_dirent = (struct lib9p_srv_dirent){}; + } else if (offset != fidinfo->dir.off) { + srv_respond(ctx, read, NULL, error_new(E_POSIX_EINVAL, "invalid offset (must be 0 or ", fidinfo->dir.off, "): ", offset)); + return; + } + + /* Allocate. */ + [[gnu::cleanup(heap_cleanup)]] char *heap = NULL; + struct lib9p_msg_Rread resp = { + .tag = ctx->tag, + .data = heap = heap_alloc(count, char), + .count = 0, + }; + + /* Read. */ + struct srv_pathinfo *dir_pathinfo = NULL; + for (;;) { + /* 1. Call ->dread() to get `member_dirent`. */ + struct lib9p_srv_dirent member_dirent; + if (fidinfo->dir.buffered_dirent.name.len) { + member_dirent = fidinfo->dir.buffered_dirent; + } else { + lib9p_srv_dirent_or_error r = LO_CALL(fidinfo->dir.io, dread, ctx, fidinfo->dir.idx); + if (r.is_err) { + if (resp.count) { + /* Just do a short-read; discard the error. */ + error_cleanup(&r.err); break; } + srv_respond(ctx, read, NULL, r.err); + return; } - if (!member_dirent.name.len) - break; - struct lib9p_srv_stat member_stat; + 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_stat = LO_CALL(member_pathinfo->file, stat, ctx); + 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); - member_file = LO_CALL(dir_pathinfo->file, dwalk, ctx, member_dirent.name); - assert(LO_IS_NULL(member_file) == lib9p_ctx_has_error(&ctx->basectx)); - if (!lib9p_ctx_has_error(&ctx->basectx)) - member_stat = LO_CALL(member_file, stat, ctx); + 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; } - if (lib9p_ctx_has_error(&ctx->basectx)) { - if (!LO_IS_NULL(member_file)) - LO_CALL(member_file, free); - if (!resp.count) - goto tread_return; - lib9p_ctx_clear_error(&ctx->basectx); + } + + /* 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; } - lib9p_srv_stat_assert(member_stat); - struct lib9p_stat member_netstat = srv_stat_to_net_stat(member_stat); - uint32_t nbytes = lib9p_stat_marshal(&ctx->basectx, req->count-resp.count, &member_netstat, - (uint8_t *)&resp.data[resp.count]); - if (!LO_IS_NULL(member_file)) - LO_CALL(member_file, free); - if (!nbytes) { - if (!resp.count) { - lib9p_error(&ctx->basectx, - LIB9P_ERRNO_L_ERANGE, "stat object does not fit into negotiated max message size"); - goto tread_return; - } + 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; } - resp.count += nbytes; - fidinfo->dir.idx++; - fidinfo->dir.off += nbytes; - fidinfo->dir.buffered_dirent = (struct lib9p_srv_dirent){}; - } -#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) && !ctx->flush_acknowledged) { - resp.count = iov.iov_len; - resp.data = iov.iov_base; - if (resp.count > req->count) - resp.count = req->count; + srv_respond(ctx, read, NULL, + error_new(E_POSIX_ERANGE, "stat object does not fit into negotiated max message size")); + return; } - break; - case SRV_FILETYPE_AUTH: - assert_notreached("TODO: auth not yet implemented"); - break; + resp.count += nbytes; + fidinfo->dir.idx++; + fidinfo->dir.off += nbytes; + fidinfo->dir.buffered_dirent = (struct lib9p_srv_dirent){}; } - tread_return: - if (ctx->user) - ctx->user = srv_userid_decref(ctx->user); - srv_respond(ctx, read, &resp); - if (heap) - free(heap); + srv_respond(ctx, read, &resp, ERROR_NULL); } +#endif static void handle_Twrite(struct srv_req *ctx, struct lib9p_msg_Twrite *req) { @@ -1290,13 +1455,11 @@ static void handle_Twrite(struct srv_req *ctx, /* Check that the FID is valid for this. */ struct srv_fidinfo *fidinfo = map_load(&ctx->parent_sess->fids, req->fid); if (!fidinfo) { - lib9p_error(&ctx->basectx, - LIB9P_ERRNO_L_EBADF, "bad file number ", req->fid); + 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"); + err = error_new(E_POSIX_EINVAL, "FID not open for writing"); goto twrite_return; } if (fidinfo->flags & FIDFLAG_APPEND) @@ -1304,28 +1467,30 @@ static void handle_Twrite(struct srv_req *ctx, /* Do it. */ ctx->user = srv_userid_incref(fidinfo->user); - resp.count = LO_CALL(fidinfo->file.io, pwrite, ctx, req->data, req->count, req->offset); + 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); + srv_respond(ctx, write, &resp, err); } static void handle_Tclunk(struct srv_req *ctx, struct lib9p_msg_Tclunk *req) { - srv_handler_common(ctx, clunk, req); + srv_handler_common_no_err(ctx, clunk, req); struct srv_fidinfo *fidinfo = map_load(&ctx->parent_sess->fids, req->fid); if (!fidinfo) { - lib9p_error(&ctx->basectx, - LIB9P_ERRNO_L_EBADF, "bad file number ", req->fid); - goto tclunk_return; + srv_respond(ctx, clunk, NULL, error_new(E_POSIX_EBADF, "bad file number ", req->fid)); + return; } + 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); - - tclunk_return: - srv_respond(ctx, clunk, &resp); } static void handle_Tremove(struct srv_req *ctx, @@ -1334,8 +1499,7 @@ static void handle_Tremove(struct srv_req *ctx, struct srv_fidinfo *fidinfo = map_load(&ctx->parent_sess->fids, req->fid); if (!fidinfo) { - lib9p_error(&ctx->basectx, - LIB9P_ERRNO_L_EBADF, "bad file number ", req->fid); + err = error_new(E_POSIX_EBADF, "bad file number ", req->fid); goto tremove_return; } @@ -1343,17 +1507,19 @@ static void handle_Tremove(struct srv_req *ctx, struct srv_pathinfo *pathinfo = map_load(&ctx->parent_sess->paths, fidinfo->path); assert(pathinfo); if (pathinfo->parent_dir == fidinfo->path) { - lib9p_error(&ctx->basectx, - LIB9P_ERRNO_L_EBUSY, "cannot remove root"); + 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); - struct lib9p_srv_stat parent_stat = LO_CALL(parent->file, stat, ctx); - if (!lib9p_ctx_has_error(&ctx->basectx) && !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"); + 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; } @@ -1361,7 +1527,7 @@ static void handle_Tremove(struct srv_req *ctx, tremove_main: srv_fid_del(ctx, req->fid, fidinfo, remove); tremove_return: - srv_respond(ctx, remove, &resp); + srv_respond(ctx, remove, &resp, err); } static void handle_Tstat(struct srv_req *ctx, @@ -1370,33 +1536,27 @@ static void handle_Tstat(struct srv_req *ctx, struct srv_fidinfo *fidinfo = map_load(&ctx->parent_sess->fids, req->fid); if (!fidinfo) { - lib9p_error(&ctx->basectx, - LIB9P_ERRNO_L_EBADF, "bad file number ", req->fid); + 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->user = srv_userid_incref(fidinfo->user); - struct lib9p_srv_stat stat = LO_CALL(pathinfo->file, stat, ctx); - if (lib9p_ctx_has_error(&ctx->basectx)) - goto tstat_return; - lib9p_srv_stat_assert(stat); - resp.stat = srv_stat_to_net_stat(stat); + 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); + srv_respond(ctx, stat, &resp, err); } static void handle_Twstat(struct srv_req *ctx, struct lib9p_msg_Twstat *req) { srv_handler_common(ctx, wstat, req); - lib9p_error(&ctx->basectx, - LIB9P_ERRNO_L_EOPNOTSUPP, "wstat not (yet?) implemented"); + err = error_new(E_POSIX_EOPNOTSUPP, "wstat not (yet?) implemented"); - srv_respond(ctx, wstat, &resp); + srv_respond(ctx, wstat, &resp, err); } #endif @@ -1405,10 +1565,9 @@ static void handle_Topenfd(struct srv_req *ctx, struct lib9p_msg_Topenfd *req) { srv_handler_common(ctx, openfd, req); - lib9p_error(&ctx->basectx, - LIB9P_ERRNO_L_EOPNOTSUPP, "openfd not (yet?) implemented"); + err = error_new(E_POSIX_EOPNOTSUPP, "openfd not (yet?) implemented"); - srv_respond(ctx, openfd, &resp); + srv_respond(ctx, openfd, &resp, err); } #endif @@ -1417,29 +1576,26 @@ static void handle_Tsession(struct srv_req *ctx, struct lib9p_msg_Tsession *req) { srv_handler_common(ctx, session, req); - lib9p_error(&ctx->basectx, - LIB9P_ERRNO_L_EOPNOTSUPP, "session not (yet?) implemented"); + err = error_new(E_POSIX_EOPNOTSUPP, "session not (yet?) implemented"); - srv_respond(ctx, session, &resp); + srv_respond(ctx, session, &resp, err); } static void handle_Tsread(struct srv_req *ctx, 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); + srv_respond(ctx, sread, &resp, err); } static void handle_Tswrite(struct srv_req *ctx, struct lib9p_msg_Tswrite *req) { srv_handler_common(ctx, swrite, req); - lib9p_error(&ctx->basectx, - LIB9P_ERRNO_L_EOPNOTSUPP, "swrite not (yet?) implemented"); + err = error_new(E_POSIX_EOPNOTSUPP, "swrite not (yet?) implemented"); - srv_respond(ctx, swrite, &resp); + 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 c40c85a..cea1d79 100644 --- a/lib9p/srv_include/lib9p/srv.h +++ b/lib9p/srv_include/lib9p/srv.h @@ -7,7 +7,6 @@ #ifndef _LIB9P_SRV_H_ #define _LIB9P_SRV_H_ -#include <libcr/coroutine.h> #include <libcr_ipc/chan.h> #include <libcr_ipc/rpc.h> #include <libhw/generic/net.h> @@ -17,6 +16,11 @@ #include <lib9p/core.h> +#ifndef CONFIG_9P_SRV_MAX_ERR_SIZE + #error config.h must define CONFIG_9P_SRV_MAX_ERR_SIZE +#endif +static_assert(CONFIG_9P_SRV_MAX_ERR_SIZE <= UINT16_MAX); + /* context ********************************************************************/ struct lib9p_srv_userid { @@ -45,8 +49,7 @@ struct lib9p_srv_ctx { struct _lib9p_srv_sess *parent_sess; lib9p_tag_t tag; uint8_t *net_bytes; - _lib9p_srv_flush_ch_t flush_ch; - bool flush_acknowledged; + _lib9p_srv_flush_ch_t flush_ch; /* flushers for this req _read_ from here */ bool responded; END_PRIVATE(LIB9P_SRV_H); }; @@ -55,20 +58,13 @@ struct lib9p_srv_ctx { * 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); -/** - * Acknowledge that the handler is responding to an outstanding flush; - * a non-Rerror R-message will be elided in favor of Rflush/Rversion. - * lib9p_srv_flush_requested() must be true; so do not cr_yield() - * between checking lib9p_srv_flush_requested() and calling - * lib9p_srv_acknowledge_flush(). These are separate calls to - * facilitate cases where a flush merely truncates a call, instead of - * totally canceling it. - */ -void lib9p_srv_acknowledge_flush(struct lib9p_srv_ctx *ctx); - /* version-independent stat ***************************************************/ struct lib9p_srv_stat { @@ -96,14 +92,11 @@ struct lib9p_srv_stat { #endif }; -static inline void lib9p_srv_stat_assert(struct lib9p_srv_stat 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) ); -} +#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 ******************************************************/ @@ -111,16 +104,58 @@ struct lib9p_srv_dirent { struct lib9p_qid qid; struct lib9p_s name; }; +DECLARE_ERROR_OR_(struct lib9p_srv_dirent, lib9p_srv_dirent); -lo_interface lib9p_srv_fio; -lo_interface lib9p_srv_dio; - -/* FIXME: I don't like that the pointer returned by pread() has to - * remain live after it returns. 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_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 **********************************************/ \ \ @@ -146,16 +181,17 @@ lo_interface lib9p_srv_dio; /* non-"opened" generic I/O *****************************************/ \ \ /** Strings returned from stat() must remain valid until free(). */ \ - LO_FUNC(struct lib9p_srv_stat , stat , struct lib9p_srv_ctx *) \ - LO_FUNC(void , wstat , struct lib9p_srv_ctx *, \ + 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(void , remove , struct lib9p_srv_ctx *) \ + 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 *, \ + LO_FUNC(lib9p_srv_file_or_error , dwalk , struct lib9p_srv_ctx *, \ struct lib9p_s childname) \ - LO_FUNC(lo_interface lib9p_srv_file, dcreate, struct lib9p_srv_ctx *, \ + 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, \ @@ -163,50 +199,22 @@ lo_interface lib9p_srv_dio; \ /* open() for I/O ***************************************************/ \ \ - LO_FUNC(lo_interface lib9p_srv_fio , fopen , struct lib9p_srv_ctx *, \ + LO_FUNC(lib9p_srv_fio_or_error , fopen , struct lib9p_srv_ctx *, \ bool rd, bool wr, \ bool trunc) \ - LO_FUNC(lo_interface lib9p_srv_dio , dopen , struct lib9p_srv_ctx *) + LO_FUNC(lib9p_srv_dio_or_error , 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) \ - /** \ - * If the file was append-only when fopen()ed, then byte_offset will \ - * always be 0. \ - */ \ - LO_FUNC(uint32_t , pwrite , struct lib9p_srv_ctx *, \ - void *buf, \ - uint32_t byte_count, \ - uint64_t byte_offset) -LO_INTERFACE(lib9p_srv_fio); /*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ - -#define lib9p_srv_dio_LO_IFACE /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ \ - LO_FUNC(struct lib9p_qid , qid ) \ - LO_FUNC(void , iofree ) \ - /** \ - * Return the idx-th dirent. idx will always be either 0 or \ - * prev_idx+1. A dirrent with an empty name signals EOF. The string \ - * must remain valid until the next dread() call or iofree(). \ - */ \ - LO_FUNC(struct lib9p_srv_dirent , dread , struct lib9p_srv_ctx *, \ - size_t idx) -LO_INTERFACE(lib9p_srv_dio); /*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ - -#define LIB9P_SRV_NOTDIR(TYP, NAM) \ - static lo_interface lib9p_srv_file NAM##_dwalk (TYP *, struct lib9p_srv_ctx *, struct lib9p_s) { assert_notreached("not a directory"); } \ - static lo_interface lib9p_srv_file NAM##_dcreate(TYP *, struct lib9p_srv_ctx *, struct lib9p_s, \ - struct lib9p_srv_userid *, struct lib9p_srv_userid *, lib9p_dm_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 ****************************************************/ @@ -222,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 @@ -233,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); }; @@ -270,10 +278,10 @@ 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 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); @@ -298,7 +306,7 @@ void lib9p_srv_worker_loop(struct lib9p_srv *srv); * * Errors that this function itself may send to clients: * - * @errno L_ERANGE R-message does not fit into max_msg_size + * @errno E_POSIX_ERANGE R-message does not fit into max_msg_size */ void lib9p_srv_worker(struct lib9p_srv_ctx *req); 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 e814bf9..6d7fb50 100644 --- a/lib9p/tests/test_compile.c +++ b/lib9p/tests/test_compile.c @@ -2,1254 +2,1254 @@ #include <lib9p/core.h> int main(void) { - [[maybe_unused]] uint64_t x; + [[maybe_unused]] uint64_t x; #ifdef LIB9P_B4_FALSE - x = LIB9P_B4_FALSE; + x = LIB9P_B4_FALSE; #endif #ifdef LIB9P_B4_TRUE - x = LIB9P_B4_TRUE; + x = LIB9P_B4_TRUE; #endif #ifdef LIB9P_DM_APPEND - x = LIB9P_DM_APPEND; + x = LIB9P_DM_APPEND; #endif #ifdef LIB9P_DM_AUTH - x = LIB9P_DM_AUTH; + x = LIB9P_DM_AUTH; #endif #ifdef LIB9P_DM_DEVICE - x = LIB9P_DM_DEVICE; + x = LIB9P_DM_DEVICE; #endif #ifdef LIB9P_DM_DIR - x = LIB9P_DM_DIR; + x = LIB9P_DM_DIR; #endif #ifdef LIB9P_DM_EXCL - x = LIB9P_DM_EXCL; + x = LIB9P_DM_EXCL; #endif #ifdef LIB9P_DM_GROUP_R - x = LIB9P_DM_GROUP_R; + x = LIB9P_DM_GROUP_R; #endif #ifdef LIB9P_DM_GROUP_W - x = LIB9P_DM_GROUP_W; + x = LIB9P_DM_GROUP_W; #endif #ifdef LIB9P_DM_GROUP_X - x = LIB9P_DM_GROUP_X; + x = LIB9P_DM_GROUP_X; #endif #ifdef LIB9P_DM_OTHER_R - x = LIB9P_DM_OTHER_R; + x = LIB9P_DM_OTHER_R; #endif #ifdef LIB9P_DM_OTHER_W - x = LIB9P_DM_OTHER_W; + x = LIB9P_DM_OTHER_W; #endif #ifdef LIB9P_DM_OTHER_X - x = LIB9P_DM_OTHER_X; + x = LIB9P_DM_OTHER_X; #endif #ifdef LIB9P_DM_OWNER_R - x = LIB9P_DM_OWNER_R; + x = LIB9P_DM_OWNER_R; #endif #ifdef LIB9P_DM_OWNER_W - x = LIB9P_DM_OWNER_W; + x = LIB9P_DM_OWNER_W; #endif #ifdef LIB9P_DM_OWNER_X - x = LIB9P_DM_OWNER_X; + x = LIB9P_DM_OWNER_X; #endif #ifdef LIB9P_DM_PERM_MASK - x = LIB9P_DM_PERM_MASK; + x = LIB9P_DM_PERM_MASK; #endif #ifdef LIB9P_DM_PIPE - x = LIB9P_DM_PIPE; + x = LIB9P_DM_PIPE; #endif #ifdef LIB9P_DM_SETGID - x = LIB9P_DM_SETGID; + x = LIB9P_DM_SETGID; #endif #ifdef LIB9P_DM_SETUID - x = LIB9P_DM_SETUID; + x = LIB9P_DM_SETUID; #endif #ifdef LIB9P_DM_SOCKET - x = LIB9P_DM_SOCKET; + x = LIB9P_DM_SOCKET; #endif #ifdef LIB9P_DM_TMP - x = LIB9P_DM_TMP; + x = LIB9P_DM_TMP; #endif #ifdef LIB9P_DT_BLOCK_DEV - x = LIB9P_DT_BLOCK_DEV; + x = LIB9P_DT_BLOCK_DEV; #endif #ifdef LIB9P_DT_CHAR_DEV - x = LIB9P_DT_CHAR_DEV; + x = LIB9P_DT_CHAR_DEV; #endif #ifdef LIB9P_DT_DIRECTORY - x = LIB9P_DT_DIRECTORY; + x = LIB9P_DT_DIRECTORY; #endif #ifdef LIB9P_DT_PIPE - x = LIB9P_DT_PIPE; + x = LIB9P_DT_PIPE; #endif #ifdef LIB9P_DT_REGULAR - x = LIB9P_DT_REGULAR; + x = LIB9P_DT_REGULAR; #endif #ifdef LIB9P_DT_SOCKET - x = LIB9P_DT_SOCKET; + x = LIB9P_DT_SOCKET; #endif #ifdef LIB9P_DT_SYMLINK - x = LIB9P_DT_SYMLINK; + x = LIB9P_DT_SYMLINK; #endif #ifdef LIB9P_DT_UNKNOWN - x = LIB9P_DT_UNKNOWN; + x = LIB9P_DT_UNKNOWN; #endif #ifdef LIB9P_ERRNO_L_E2BIG - x = LIB9P_ERRNO_L_E2BIG; + x = LIB9P_ERRNO_L_E2BIG; #endif #ifdef LIB9P_ERRNO_L_EACCES - x = LIB9P_ERRNO_L_EACCES; + x = LIB9P_ERRNO_L_EACCES; #endif #ifdef LIB9P_ERRNO_L_EADDRINUSE - x = LIB9P_ERRNO_L_EADDRINUSE; + x = LIB9P_ERRNO_L_EADDRINUSE; #endif #ifdef LIB9P_ERRNO_L_EADDRNOTAVAIL - x = LIB9P_ERRNO_L_EADDRNOTAVAIL; + x = LIB9P_ERRNO_L_EADDRNOTAVAIL; #endif #ifdef LIB9P_ERRNO_L_EADV - x = LIB9P_ERRNO_L_EADV; + x = LIB9P_ERRNO_L_EADV; #endif #ifdef LIB9P_ERRNO_L_EAFNOSUPPORT - x = LIB9P_ERRNO_L_EAFNOSUPPORT; + x = LIB9P_ERRNO_L_EAFNOSUPPORT; #endif #ifdef LIB9P_ERRNO_L_EAGAIN - x = LIB9P_ERRNO_L_EAGAIN; + x = LIB9P_ERRNO_L_EAGAIN; #endif #ifdef LIB9P_ERRNO_L_EALREADY - x = LIB9P_ERRNO_L_EALREADY; + x = LIB9P_ERRNO_L_EALREADY; #endif #ifdef LIB9P_ERRNO_L_EBADE - x = LIB9P_ERRNO_L_EBADE; + x = LIB9P_ERRNO_L_EBADE; #endif #ifdef LIB9P_ERRNO_L_EBADF - x = LIB9P_ERRNO_L_EBADF; + x = LIB9P_ERRNO_L_EBADF; #endif #ifdef LIB9P_ERRNO_L_EBADFD - x = LIB9P_ERRNO_L_EBADFD; + x = LIB9P_ERRNO_L_EBADFD; #endif #ifdef LIB9P_ERRNO_L_EBADMSG - x = LIB9P_ERRNO_L_EBADMSG; + x = LIB9P_ERRNO_L_EBADMSG; #endif #ifdef LIB9P_ERRNO_L_EBADR - x = LIB9P_ERRNO_L_EBADR; + x = LIB9P_ERRNO_L_EBADR; #endif #ifdef LIB9P_ERRNO_L_EBADRQC - x = LIB9P_ERRNO_L_EBADRQC; + x = LIB9P_ERRNO_L_EBADRQC; #endif #ifdef LIB9P_ERRNO_L_EBADSLT - x = LIB9P_ERRNO_L_EBADSLT; + x = LIB9P_ERRNO_L_EBADSLT; #endif #ifdef LIB9P_ERRNO_L_EBFONT - x = LIB9P_ERRNO_L_EBFONT; + x = LIB9P_ERRNO_L_EBFONT; #endif #ifdef LIB9P_ERRNO_L_EBUSY - x = LIB9P_ERRNO_L_EBUSY; + x = LIB9P_ERRNO_L_EBUSY; #endif #ifdef LIB9P_ERRNO_L_ECANCELED - x = LIB9P_ERRNO_L_ECANCELED; + x = LIB9P_ERRNO_L_ECANCELED; #endif #ifdef LIB9P_ERRNO_L_ECHILD - x = LIB9P_ERRNO_L_ECHILD; + x = LIB9P_ERRNO_L_ECHILD; #endif #ifdef LIB9P_ERRNO_L_ECHRNG - x = LIB9P_ERRNO_L_ECHRNG; + x = LIB9P_ERRNO_L_ECHRNG; #endif #ifdef LIB9P_ERRNO_L_ECOMM - x = LIB9P_ERRNO_L_ECOMM; + x = LIB9P_ERRNO_L_ECOMM; #endif #ifdef LIB9P_ERRNO_L_ECONNABORTED - x = LIB9P_ERRNO_L_ECONNABORTED; + x = LIB9P_ERRNO_L_ECONNABORTED; #endif #ifdef LIB9P_ERRNO_L_ECONNREFUSED - x = LIB9P_ERRNO_L_ECONNREFUSED; + x = LIB9P_ERRNO_L_ECONNREFUSED; #endif #ifdef LIB9P_ERRNO_L_ECONNRESET - x = LIB9P_ERRNO_L_ECONNRESET; + x = LIB9P_ERRNO_L_ECONNRESET; #endif #ifdef LIB9P_ERRNO_L_EDEADLK - x = LIB9P_ERRNO_L_EDEADLK; + x = LIB9P_ERRNO_L_EDEADLK; #endif #ifdef LIB9P_ERRNO_L_EDESTADDRREQ - x = LIB9P_ERRNO_L_EDESTADDRREQ; + x = LIB9P_ERRNO_L_EDESTADDRREQ; #endif #ifdef LIB9P_ERRNO_L_EDOM - x = LIB9P_ERRNO_L_EDOM; + x = LIB9P_ERRNO_L_EDOM; #endif #ifdef LIB9P_ERRNO_L_EDOTDOT - x = LIB9P_ERRNO_L_EDOTDOT; + x = LIB9P_ERRNO_L_EDOTDOT; #endif #ifdef LIB9P_ERRNO_L_EDQUOT - x = LIB9P_ERRNO_L_EDQUOT; + x = LIB9P_ERRNO_L_EDQUOT; #endif #ifdef LIB9P_ERRNO_L_EEXIST - x = LIB9P_ERRNO_L_EEXIST; + x = LIB9P_ERRNO_L_EEXIST; #endif #ifdef LIB9P_ERRNO_L_EFAULT - x = LIB9P_ERRNO_L_EFAULT; + x = LIB9P_ERRNO_L_EFAULT; #endif #ifdef LIB9P_ERRNO_L_EFBIG - x = LIB9P_ERRNO_L_EFBIG; + x = LIB9P_ERRNO_L_EFBIG; #endif #ifdef LIB9P_ERRNO_L_EHOSTDOWN - x = LIB9P_ERRNO_L_EHOSTDOWN; + x = LIB9P_ERRNO_L_EHOSTDOWN; #endif #ifdef LIB9P_ERRNO_L_EHOSTUNREACH - x = LIB9P_ERRNO_L_EHOSTUNREACH; + x = LIB9P_ERRNO_L_EHOSTUNREACH; #endif #ifdef LIB9P_ERRNO_L_EHWPOISON - x = LIB9P_ERRNO_L_EHWPOISON; + x = LIB9P_ERRNO_L_EHWPOISON; #endif #ifdef LIB9P_ERRNO_L_EIDRM - x = LIB9P_ERRNO_L_EIDRM; + x = LIB9P_ERRNO_L_EIDRM; #endif #ifdef LIB9P_ERRNO_L_EILSEQ - x = LIB9P_ERRNO_L_EILSEQ; + x = LIB9P_ERRNO_L_EILSEQ; #endif #ifdef LIB9P_ERRNO_L_EINPROGRESS - x = LIB9P_ERRNO_L_EINPROGRESS; + x = LIB9P_ERRNO_L_EINPROGRESS; #endif #ifdef LIB9P_ERRNO_L_EINTR - x = LIB9P_ERRNO_L_EINTR; + x = LIB9P_ERRNO_L_EINTR; #endif #ifdef LIB9P_ERRNO_L_EINVAL - x = LIB9P_ERRNO_L_EINVAL; + x = LIB9P_ERRNO_L_EINVAL; #endif #ifdef LIB9P_ERRNO_L_EIO - x = LIB9P_ERRNO_L_EIO; + x = LIB9P_ERRNO_L_EIO; #endif #ifdef LIB9P_ERRNO_L_EISCONN - x = LIB9P_ERRNO_L_EISCONN; + x = LIB9P_ERRNO_L_EISCONN; #endif #ifdef LIB9P_ERRNO_L_EISDIR - x = LIB9P_ERRNO_L_EISDIR; + x = LIB9P_ERRNO_L_EISDIR; #endif #ifdef LIB9P_ERRNO_L_EISNAM - x = LIB9P_ERRNO_L_EISNAM; + x = LIB9P_ERRNO_L_EISNAM; #endif #ifdef LIB9P_ERRNO_L_EKEYEXPIRED - x = LIB9P_ERRNO_L_EKEYEXPIRED; + x = LIB9P_ERRNO_L_EKEYEXPIRED; #endif #ifdef LIB9P_ERRNO_L_EKEYREJECTED - x = LIB9P_ERRNO_L_EKEYREJECTED; + x = LIB9P_ERRNO_L_EKEYREJECTED; #endif #ifdef LIB9P_ERRNO_L_EKEYREVOKED - x = LIB9P_ERRNO_L_EKEYREVOKED; + x = LIB9P_ERRNO_L_EKEYREVOKED; #endif #ifdef LIB9P_ERRNO_L_EL2HLT - x = LIB9P_ERRNO_L_EL2HLT; + x = LIB9P_ERRNO_L_EL2HLT; #endif #ifdef LIB9P_ERRNO_L_EL2NSYNC - x = LIB9P_ERRNO_L_EL2NSYNC; + x = LIB9P_ERRNO_L_EL2NSYNC; #endif #ifdef LIB9P_ERRNO_L_EL3HLT - x = LIB9P_ERRNO_L_EL3HLT; + x = LIB9P_ERRNO_L_EL3HLT; #endif #ifdef LIB9P_ERRNO_L_EL3RST - x = LIB9P_ERRNO_L_EL3RST; + x = LIB9P_ERRNO_L_EL3RST; #endif #ifdef LIB9P_ERRNO_L_ELIBACC - x = LIB9P_ERRNO_L_ELIBACC; + x = LIB9P_ERRNO_L_ELIBACC; #endif #ifdef LIB9P_ERRNO_L_ELIBBAD - x = LIB9P_ERRNO_L_ELIBBAD; + x = LIB9P_ERRNO_L_ELIBBAD; #endif #ifdef LIB9P_ERRNO_L_ELIBEXEC - x = LIB9P_ERRNO_L_ELIBEXEC; + x = LIB9P_ERRNO_L_ELIBEXEC; #endif #ifdef LIB9P_ERRNO_L_ELIBMAX - x = LIB9P_ERRNO_L_ELIBMAX; + x = LIB9P_ERRNO_L_ELIBMAX; #endif #ifdef LIB9P_ERRNO_L_ELIBSCN - x = LIB9P_ERRNO_L_ELIBSCN; + x = LIB9P_ERRNO_L_ELIBSCN; #endif #ifdef LIB9P_ERRNO_L_ELNRNG - x = LIB9P_ERRNO_L_ELNRNG; + x = LIB9P_ERRNO_L_ELNRNG; #endif #ifdef LIB9P_ERRNO_L_ELOOP - x = LIB9P_ERRNO_L_ELOOP; + x = LIB9P_ERRNO_L_ELOOP; #endif #ifdef LIB9P_ERRNO_L_EMEDIUMTYPE - x = LIB9P_ERRNO_L_EMEDIUMTYPE; + x = LIB9P_ERRNO_L_EMEDIUMTYPE; #endif #ifdef LIB9P_ERRNO_L_EMFILE - x = LIB9P_ERRNO_L_EMFILE; + x = LIB9P_ERRNO_L_EMFILE; #endif #ifdef LIB9P_ERRNO_L_EMLINK - x = LIB9P_ERRNO_L_EMLINK; + x = LIB9P_ERRNO_L_EMLINK; #endif #ifdef LIB9P_ERRNO_L_EMSGSIZE - x = LIB9P_ERRNO_L_EMSGSIZE; + x = LIB9P_ERRNO_L_EMSGSIZE; #endif #ifdef LIB9P_ERRNO_L_EMULTIHOP - x = LIB9P_ERRNO_L_EMULTIHOP; + x = LIB9P_ERRNO_L_EMULTIHOP; #endif #ifdef LIB9P_ERRNO_L_ENAMETOOLONG - x = LIB9P_ERRNO_L_ENAMETOOLONG; + x = LIB9P_ERRNO_L_ENAMETOOLONG; #endif #ifdef LIB9P_ERRNO_L_ENAVAIL - x = LIB9P_ERRNO_L_ENAVAIL; + x = LIB9P_ERRNO_L_ENAVAIL; #endif #ifdef LIB9P_ERRNO_L_ENETDOWN - x = LIB9P_ERRNO_L_ENETDOWN; + x = LIB9P_ERRNO_L_ENETDOWN; #endif #ifdef LIB9P_ERRNO_L_ENETRESET - x = LIB9P_ERRNO_L_ENETRESET; + x = LIB9P_ERRNO_L_ENETRESET; #endif #ifdef LIB9P_ERRNO_L_ENETUNREACH - x = LIB9P_ERRNO_L_ENETUNREACH; + x = LIB9P_ERRNO_L_ENETUNREACH; #endif #ifdef LIB9P_ERRNO_L_ENFILE - x = LIB9P_ERRNO_L_ENFILE; + x = LIB9P_ERRNO_L_ENFILE; #endif #ifdef LIB9P_ERRNO_L_ENOANO - x = LIB9P_ERRNO_L_ENOANO; + x = LIB9P_ERRNO_L_ENOANO; #endif #ifdef LIB9P_ERRNO_L_ENOBUFS - x = LIB9P_ERRNO_L_ENOBUFS; + x = LIB9P_ERRNO_L_ENOBUFS; #endif #ifdef LIB9P_ERRNO_L_ENOCSI - x = LIB9P_ERRNO_L_ENOCSI; + x = LIB9P_ERRNO_L_ENOCSI; #endif #ifdef LIB9P_ERRNO_L_ENODATA - x = LIB9P_ERRNO_L_ENODATA; + x = LIB9P_ERRNO_L_ENODATA; #endif #ifdef LIB9P_ERRNO_L_ENODEV - x = LIB9P_ERRNO_L_ENODEV; + x = LIB9P_ERRNO_L_ENODEV; #endif #ifdef LIB9P_ERRNO_L_ENOENT - x = LIB9P_ERRNO_L_ENOENT; + x = LIB9P_ERRNO_L_ENOENT; #endif #ifdef LIB9P_ERRNO_L_ENOEXEC - x = LIB9P_ERRNO_L_ENOEXEC; + x = LIB9P_ERRNO_L_ENOEXEC; #endif #ifdef LIB9P_ERRNO_L_ENOKEY - x = LIB9P_ERRNO_L_ENOKEY; + x = LIB9P_ERRNO_L_ENOKEY; #endif #ifdef LIB9P_ERRNO_L_ENOLCK - x = LIB9P_ERRNO_L_ENOLCK; + x = LIB9P_ERRNO_L_ENOLCK; #endif #ifdef LIB9P_ERRNO_L_ENOLINK - x = LIB9P_ERRNO_L_ENOLINK; + x = LIB9P_ERRNO_L_ENOLINK; #endif #ifdef LIB9P_ERRNO_L_ENOMEDIUM - x = LIB9P_ERRNO_L_ENOMEDIUM; + x = LIB9P_ERRNO_L_ENOMEDIUM; #endif #ifdef LIB9P_ERRNO_L_ENOMEM - x = LIB9P_ERRNO_L_ENOMEM; + x = LIB9P_ERRNO_L_ENOMEM; #endif #ifdef LIB9P_ERRNO_L_ENOMSG - x = LIB9P_ERRNO_L_ENOMSG; + x = LIB9P_ERRNO_L_ENOMSG; #endif #ifdef LIB9P_ERRNO_L_ENONET - x = LIB9P_ERRNO_L_ENONET; + x = LIB9P_ERRNO_L_ENONET; #endif #ifdef LIB9P_ERRNO_L_ENOPKG - x = LIB9P_ERRNO_L_ENOPKG; + x = LIB9P_ERRNO_L_ENOPKG; #endif #ifdef LIB9P_ERRNO_L_ENOPROTOOPT - x = LIB9P_ERRNO_L_ENOPROTOOPT; + x = LIB9P_ERRNO_L_ENOPROTOOPT; #endif #ifdef LIB9P_ERRNO_L_ENOSPC - x = LIB9P_ERRNO_L_ENOSPC; + x = LIB9P_ERRNO_L_ENOSPC; #endif #ifdef LIB9P_ERRNO_L_ENOSR - x = LIB9P_ERRNO_L_ENOSR; + x = LIB9P_ERRNO_L_ENOSR; #endif #ifdef LIB9P_ERRNO_L_ENOSTR - x = LIB9P_ERRNO_L_ENOSTR; + x = LIB9P_ERRNO_L_ENOSTR; #endif #ifdef LIB9P_ERRNO_L_ENOSYS - x = LIB9P_ERRNO_L_ENOSYS; + x = LIB9P_ERRNO_L_ENOSYS; #endif #ifdef LIB9P_ERRNO_L_ENOTBLK - x = LIB9P_ERRNO_L_ENOTBLK; + x = LIB9P_ERRNO_L_ENOTBLK; #endif #ifdef LIB9P_ERRNO_L_ENOTCONN - x = LIB9P_ERRNO_L_ENOTCONN; + x = LIB9P_ERRNO_L_ENOTCONN; #endif #ifdef LIB9P_ERRNO_L_ENOTDIR - x = LIB9P_ERRNO_L_ENOTDIR; + x = LIB9P_ERRNO_L_ENOTDIR; #endif #ifdef LIB9P_ERRNO_L_ENOTEMPTY - x = LIB9P_ERRNO_L_ENOTEMPTY; + x = LIB9P_ERRNO_L_ENOTEMPTY; #endif #ifdef LIB9P_ERRNO_L_ENOTNAM - x = LIB9P_ERRNO_L_ENOTNAM; + x = LIB9P_ERRNO_L_ENOTNAM; #endif #ifdef LIB9P_ERRNO_L_ENOTRECOVERABLE - x = LIB9P_ERRNO_L_ENOTRECOVERABLE; + x = LIB9P_ERRNO_L_ENOTRECOVERABLE; #endif #ifdef LIB9P_ERRNO_L_ENOTSOCK - x = LIB9P_ERRNO_L_ENOTSOCK; + x = LIB9P_ERRNO_L_ENOTSOCK; #endif #ifdef LIB9P_ERRNO_L_ENOTTY - x = LIB9P_ERRNO_L_ENOTTY; + x = LIB9P_ERRNO_L_ENOTTY; #endif #ifdef LIB9P_ERRNO_L_ENOTUNIQ - x = LIB9P_ERRNO_L_ENOTUNIQ; + x = LIB9P_ERRNO_L_ENOTUNIQ; #endif #ifdef LIB9P_ERRNO_L_ENXIO - x = LIB9P_ERRNO_L_ENXIO; + x = LIB9P_ERRNO_L_ENXIO; #endif #ifdef LIB9P_ERRNO_L_EOPNOTSUPP - x = LIB9P_ERRNO_L_EOPNOTSUPP; + x = LIB9P_ERRNO_L_EOPNOTSUPP; #endif #ifdef LIB9P_ERRNO_L_EOVERFLOW - x = LIB9P_ERRNO_L_EOVERFLOW; + x = LIB9P_ERRNO_L_EOVERFLOW; #endif #ifdef LIB9P_ERRNO_L_EOWNERDEAD - x = LIB9P_ERRNO_L_EOWNERDEAD; + x = LIB9P_ERRNO_L_EOWNERDEAD; #endif #ifdef LIB9P_ERRNO_L_EPERM - x = LIB9P_ERRNO_L_EPERM; + x = LIB9P_ERRNO_L_EPERM; #endif #ifdef LIB9P_ERRNO_L_EPFNOSUPPORT - x = LIB9P_ERRNO_L_EPFNOSUPPORT; + x = LIB9P_ERRNO_L_EPFNOSUPPORT; #endif #ifdef LIB9P_ERRNO_L_EPIPE - x = LIB9P_ERRNO_L_EPIPE; + x = LIB9P_ERRNO_L_EPIPE; #endif #ifdef LIB9P_ERRNO_L_EPROTO - x = LIB9P_ERRNO_L_EPROTO; + x = LIB9P_ERRNO_L_EPROTO; #endif #ifdef LIB9P_ERRNO_L_EPROTONOSUPPORT - x = LIB9P_ERRNO_L_EPROTONOSUPPORT; + x = LIB9P_ERRNO_L_EPROTONOSUPPORT; #endif #ifdef LIB9P_ERRNO_L_EPROTOTYPE - x = LIB9P_ERRNO_L_EPROTOTYPE; + x = LIB9P_ERRNO_L_EPROTOTYPE; #endif #ifdef LIB9P_ERRNO_L_ERANGE - x = LIB9P_ERRNO_L_ERANGE; + x = LIB9P_ERRNO_L_ERANGE; #endif #ifdef LIB9P_ERRNO_L_EREMCHG - x = LIB9P_ERRNO_L_EREMCHG; + x = LIB9P_ERRNO_L_EREMCHG; #endif #ifdef LIB9P_ERRNO_L_EREMOTE - x = LIB9P_ERRNO_L_EREMOTE; + x = LIB9P_ERRNO_L_EREMOTE; #endif #ifdef LIB9P_ERRNO_L_EREMOTEIO - x = LIB9P_ERRNO_L_EREMOTEIO; + x = LIB9P_ERRNO_L_EREMOTEIO; #endif #ifdef LIB9P_ERRNO_L_ERESTART - x = LIB9P_ERRNO_L_ERESTART; + x = LIB9P_ERRNO_L_ERESTART; #endif #ifdef LIB9P_ERRNO_L_ERFKILL - x = LIB9P_ERRNO_L_ERFKILL; + x = LIB9P_ERRNO_L_ERFKILL; #endif #ifdef LIB9P_ERRNO_L_EROFS - x = LIB9P_ERRNO_L_EROFS; + x = LIB9P_ERRNO_L_EROFS; #endif #ifdef LIB9P_ERRNO_L_ESHUTDOWN - x = LIB9P_ERRNO_L_ESHUTDOWN; + x = LIB9P_ERRNO_L_ESHUTDOWN; #endif #ifdef LIB9P_ERRNO_L_ESOCKTNOSUPPORT - x = LIB9P_ERRNO_L_ESOCKTNOSUPPORT; + x = LIB9P_ERRNO_L_ESOCKTNOSUPPORT; #endif #ifdef LIB9P_ERRNO_L_ESPIPE - x = LIB9P_ERRNO_L_ESPIPE; + x = LIB9P_ERRNO_L_ESPIPE; #endif #ifdef LIB9P_ERRNO_L_ESRCH - x = LIB9P_ERRNO_L_ESRCH; + x = LIB9P_ERRNO_L_ESRCH; #endif #ifdef LIB9P_ERRNO_L_ESRMNT - x = LIB9P_ERRNO_L_ESRMNT; + x = LIB9P_ERRNO_L_ESRMNT; #endif #ifdef LIB9P_ERRNO_L_ESTALE - x = LIB9P_ERRNO_L_ESTALE; + x = LIB9P_ERRNO_L_ESTALE; #endif #ifdef LIB9P_ERRNO_L_ESTRPIPE - x = LIB9P_ERRNO_L_ESTRPIPE; + x = LIB9P_ERRNO_L_ESTRPIPE; #endif #ifdef LIB9P_ERRNO_L_ETIME - x = LIB9P_ERRNO_L_ETIME; + x = LIB9P_ERRNO_L_ETIME; #endif #ifdef LIB9P_ERRNO_L_ETIMEDOUT - x = LIB9P_ERRNO_L_ETIMEDOUT; + x = LIB9P_ERRNO_L_ETIMEDOUT; #endif #ifdef LIB9P_ERRNO_L_ETOOMANYREFS - x = LIB9P_ERRNO_L_ETOOMANYREFS; + x = LIB9P_ERRNO_L_ETOOMANYREFS; #endif #ifdef LIB9P_ERRNO_L_ETXTBSY - x = LIB9P_ERRNO_L_ETXTBSY; + x = LIB9P_ERRNO_L_ETXTBSY; #endif #ifdef LIB9P_ERRNO_L_EUCLEAN - x = LIB9P_ERRNO_L_EUCLEAN; + x = LIB9P_ERRNO_L_EUCLEAN; #endif #ifdef LIB9P_ERRNO_L_EUNATCH - x = LIB9P_ERRNO_L_EUNATCH; + x = LIB9P_ERRNO_L_EUNATCH; #endif #ifdef LIB9P_ERRNO_L_EUSERS - x = LIB9P_ERRNO_L_EUSERS; + x = LIB9P_ERRNO_L_EUSERS; #endif #ifdef LIB9P_ERRNO_L_EXDEV - x = LIB9P_ERRNO_L_EXDEV; + x = LIB9P_ERRNO_L_EXDEV; #endif #ifdef LIB9P_ERRNO_L_EXFULL - x = LIB9P_ERRNO_L_EXFULL; + x = LIB9P_ERRNO_L_EXFULL; #endif #ifdef LIB9P_ERRNO_NOERROR - x = LIB9P_ERRNO_NOERROR; + x = LIB9P_ERRNO_NOERROR; #endif #ifdef LIB9P_FID_NOFID - x = LIB9P_FID_NOFID; + x = LIB9P_FID_NOFID; #endif #ifdef LIB9P_GETATTR_ALL - x = LIB9P_GETATTR_ALL; + x = LIB9P_GETATTR_ALL; #endif #ifdef LIB9P_GETATTR_ATIME - x = LIB9P_GETATTR_ATIME; + x = LIB9P_GETATTR_ATIME; #endif #ifdef LIB9P_GETATTR_BASIC - x = LIB9P_GETATTR_BASIC; + x = LIB9P_GETATTR_BASIC; #endif #ifdef LIB9P_GETATTR_BLOCKS - x = LIB9P_GETATTR_BLOCKS; + x = LIB9P_GETATTR_BLOCKS; #endif #ifdef LIB9P_GETATTR_BTIME - x = LIB9P_GETATTR_BTIME; + x = LIB9P_GETATTR_BTIME; #endif #ifdef LIB9P_GETATTR_CTIME - x = LIB9P_GETATTR_CTIME; + x = LIB9P_GETATTR_CTIME; #endif #ifdef LIB9P_GETATTR_DATA_VERSION - x = LIB9P_GETATTR_DATA_VERSION; + x = LIB9P_GETATTR_DATA_VERSION; #endif #ifdef LIB9P_GETATTR_GEN - x = LIB9P_GETATTR_GEN; + x = LIB9P_GETATTR_GEN; #endif #ifdef LIB9P_GETATTR_GID - x = LIB9P_GETATTR_GID; + x = LIB9P_GETATTR_GID; #endif #ifdef LIB9P_GETATTR_INO - x = LIB9P_GETATTR_INO; + x = LIB9P_GETATTR_INO; #endif #ifdef LIB9P_GETATTR_MODE - x = LIB9P_GETATTR_MODE; + x = LIB9P_GETATTR_MODE; #endif #ifdef LIB9P_GETATTR_MTIME - x = LIB9P_GETATTR_MTIME; + x = LIB9P_GETATTR_MTIME; #endif #ifdef LIB9P_GETATTR_NLINK - x = LIB9P_GETATTR_NLINK; + x = LIB9P_GETATTR_NLINK; #endif #ifdef LIB9P_GETATTR_RDEV - x = LIB9P_GETATTR_RDEV; + x = LIB9P_GETATTR_RDEV; #endif #ifdef LIB9P_GETATTR_SIZE - x = LIB9P_GETATTR_SIZE; + x = LIB9P_GETATTR_SIZE; #endif #ifdef LIB9P_GETATTR_UID - x = LIB9P_GETATTR_UID; + x = LIB9P_GETATTR_UID; #endif #ifdef LIB9P_LOCK_FLAGS_BLOCK - x = LIB9P_LOCK_FLAGS_BLOCK; + x = LIB9P_LOCK_FLAGS_BLOCK; #endif #ifdef LIB9P_LOCK_FLAGS_RECLAIM - x = LIB9P_LOCK_FLAGS_RECLAIM; + x = LIB9P_LOCK_FLAGS_RECLAIM; #endif #ifdef LIB9P_LOCK_STATUS_BLOCKED - x = LIB9P_LOCK_STATUS_BLOCKED; + x = LIB9P_LOCK_STATUS_BLOCKED; #endif #ifdef LIB9P_LOCK_STATUS_ERROR - x = LIB9P_LOCK_STATUS_ERROR; + x = LIB9P_LOCK_STATUS_ERROR; #endif #ifdef LIB9P_LOCK_STATUS_GRACE - x = LIB9P_LOCK_STATUS_GRACE; + x = LIB9P_LOCK_STATUS_GRACE; #endif #ifdef LIB9P_LOCK_STATUS_SUCCESS - x = LIB9P_LOCK_STATUS_SUCCESS; + x = LIB9P_LOCK_STATUS_SUCCESS; #endif #ifdef LIB9P_LOCK_TYPE_RDLCK - x = LIB9P_LOCK_TYPE_RDLCK; + x = LIB9P_LOCK_TYPE_RDLCK; #endif #ifdef LIB9P_LOCK_TYPE_UNLCK - x = LIB9P_LOCK_TYPE_UNLCK; + x = LIB9P_LOCK_TYPE_UNLCK; #endif #ifdef LIB9P_LOCK_TYPE_WRLCK - x = LIB9P_LOCK_TYPE_WRLCK; + x = LIB9P_LOCK_TYPE_WRLCK; #endif #ifdef LIB9P_LO_APPEND - x = LIB9P_LO_APPEND; + x = LIB9P_LO_APPEND; #endif #ifdef LIB9P_LO_BSD_FASYNC - x = LIB9P_LO_BSD_FASYNC; + x = LIB9P_LO_BSD_FASYNC; #endif #ifdef LIB9P_LO_CLOEXEC - x = LIB9P_LO_CLOEXEC; + x = LIB9P_LO_CLOEXEC; #endif #ifdef LIB9P_LO_CREATE - x = LIB9P_LO_CREATE; + x = LIB9P_LO_CREATE; #endif #ifdef LIB9P_LO_DIRECT - x = LIB9P_LO_DIRECT; + x = LIB9P_LO_DIRECT; #endif #ifdef LIB9P_LO_DIRECTORY - x = LIB9P_LO_DIRECTORY; + x = LIB9P_LO_DIRECTORY; #endif #ifdef LIB9P_LO_DSYNC - x = LIB9P_LO_DSYNC; + x = LIB9P_LO_DSYNC; #endif #ifdef LIB9P_LO_EXCL - x = LIB9P_LO_EXCL; + x = LIB9P_LO_EXCL; #endif #ifdef LIB9P_LO_FLAG_MASK - x = LIB9P_LO_FLAG_MASK; + x = LIB9P_LO_FLAG_MASK; #endif #ifdef LIB9P_LO_LARGEFILE - x = LIB9P_LO_LARGEFILE; + x = LIB9P_LO_LARGEFILE; #endif #ifdef LIB9P_LO_MODE_MASK - x = LIB9P_LO_MODE_MASK; + x = LIB9P_LO_MODE_MASK; #endif #ifdef LIB9P_LO_MODE_NOACCESS - x = LIB9P_LO_MODE_NOACCESS; + x = LIB9P_LO_MODE_NOACCESS; #endif #ifdef LIB9P_LO_MODE_RDONLY - x = LIB9P_LO_MODE_RDONLY; + x = LIB9P_LO_MODE_RDONLY; #endif #ifdef LIB9P_LO_MODE_RDWR - x = LIB9P_LO_MODE_RDWR; + x = LIB9P_LO_MODE_RDWR; #endif #ifdef LIB9P_LO_MODE_WRONLY - x = LIB9P_LO_MODE_WRONLY; + x = LIB9P_LO_MODE_WRONLY; #endif #ifdef LIB9P_LO_NOATIME - x = LIB9P_LO_NOATIME; + x = LIB9P_LO_NOATIME; #endif #ifdef LIB9P_LO_NOCTTY - x = LIB9P_LO_NOCTTY; + x = LIB9P_LO_NOCTTY; #endif #ifdef LIB9P_LO_NOFOLLOW - x = LIB9P_LO_NOFOLLOW; + x = LIB9P_LO_NOFOLLOW; #endif #ifdef LIB9P_LO_NONBLOCK - x = LIB9P_LO_NONBLOCK; + x = LIB9P_LO_NONBLOCK; #endif #ifdef LIB9P_LO_SYNC - x = LIB9P_LO_SYNC; + x = LIB9P_LO_SYNC; #endif #ifdef LIB9P_LO_TRUNC - x = LIB9P_LO_TRUNC; + x = LIB9P_LO_TRUNC; #endif #ifdef LIB9P_MODE_FMT_BLOCK_DEV - x = LIB9P_MODE_FMT_BLOCK_DEV; + x = LIB9P_MODE_FMT_BLOCK_DEV; #endif #ifdef LIB9P_MODE_FMT_CHAR_DEV - x = LIB9P_MODE_FMT_CHAR_DEV; + x = LIB9P_MODE_FMT_CHAR_DEV; #endif #ifdef LIB9P_MODE_FMT_DIRECTORY - x = LIB9P_MODE_FMT_DIRECTORY; + x = LIB9P_MODE_FMT_DIRECTORY; #endif #ifdef LIB9P_MODE_FMT_MASK - x = LIB9P_MODE_FMT_MASK; + x = LIB9P_MODE_FMT_MASK; #endif #ifdef LIB9P_MODE_FMT_PIPE - x = LIB9P_MODE_FMT_PIPE; + x = LIB9P_MODE_FMT_PIPE; #endif #ifdef LIB9P_MODE_FMT_REGULAR - x = LIB9P_MODE_FMT_REGULAR; + x = LIB9P_MODE_FMT_REGULAR; #endif #ifdef LIB9P_MODE_FMT_SOCKET - x = LIB9P_MODE_FMT_SOCKET; + x = LIB9P_MODE_FMT_SOCKET; #endif #ifdef LIB9P_MODE_FMT_SYMLINK - x = LIB9P_MODE_FMT_SYMLINK; + x = LIB9P_MODE_FMT_SYMLINK; #endif #ifdef LIB9P_MODE_PERM_GROUP_R - x = LIB9P_MODE_PERM_GROUP_R; + x = LIB9P_MODE_PERM_GROUP_R; #endif #ifdef LIB9P_MODE_PERM_GROUP_W - x = LIB9P_MODE_PERM_GROUP_W; + x = LIB9P_MODE_PERM_GROUP_W; #endif #ifdef LIB9P_MODE_PERM_GROUP_X - x = LIB9P_MODE_PERM_GROUP_X; + x = LIB9P_MODE_PERM_GROUP_X; #endif #ifdef LIB9P_MODE_PERM_MASK - x = LIB9P_MODE_PERM_MASK; + x = LIB9P_MODE_PERM_MASK; #endif #ifdef LIB9P_MODE_PERM_OTHER_R - x = LIB9P_MODE_PERM_OTHER_R; + x = LIB9P_MODE_PERM_OTHER_R; #endif #ifdef LIB9P_MODE_PERM_OTHER_W - x = LIB9P_MODE_PERM_OTHER_W; + x = LIB9P_MODE_PERM_OTHER_W; #endif #ifdef LIB9P_MODE_PERM_OTHER_X - x = LIB9P_MODE_PERM_OTHER_X; + x = LIB9P_MODE_PERM_OTHER_X; #endif #ifdef LIB9P_MODE_PERM_OWNER_R - x = LIB9P_MODE_PERM_OWNER_R; + x = LIB9P_MODE_PERM_OWNER_R; #endif #ifdef LIB9P_MODE_PERM_OWNER_W - x = LIB9P_MODE_PERM_OWNER_W; + x = LIB9P_MODE_PERM_OWNER_W; #endif #ifdef LIB9P_MODE_PERM_OWNER_X - x = LIB9P_MODE_PERM_OWNER_X; + x = LIB9P_MODE_PERM_OWNER_X; #endif #ifdef LIB9P_MODE_PERM_SETGROUP - x = LIB9P_MODE_PERM_SETGROUP; + x = LIB9P_MODE_PERM_SETGROUP; #endif #ifdef LIB9P_MODE_PERM_SETUSER - x = LIB9P_MODE_PERM_SETUSER; + x = LIB9P_MODE_PERM_SETUSER; #endif #ifdef LIB9P_MODE_PERM_STICKY - x = LIB9P_MODE_PERM_STICKY; + x = LIB9P_MODE_PERM_STICKY; #endif #ifdef LIB9P_NUID_NONUID - x = LIB9P_NUID_NONUID; + x = LIB9P_NUID_NONUID; #endif #ifdef LIB9P_O_FLAG_MASK - x = LIB9P_O_FLAG_MASK; + x = LIB9P_O_FLAG_MASK; #endif #ifdef LIB9P_O_MODE_EXEC - x = LIB9P_O_MODE_EXEC; + x = LIB9P_O_MODE_EXEC; #endif #ifdef LIB9P_O_MODE_MASK - x = LIB9P_O_MODE_MASK; + x = LIB9P_O_MODE_MASK; #endif #ifdef LIB9P_O_MODE_RDWR - x = LIB9P_O_MODE_RDWR; + x = LIB9P_O_MODE_RDWR; #endif #ifdef LIB9P_O_MODE_READ - x = LIB9P_O_MODE_READ; + x = LIB9P_O_MODE_READ; #endif #ifdef LIB9P_O_MODE_WRITE - x = LIB9P_O_MODE_WRITE; + x = LIB9P_O_MODE_WRITE; #endif #ifdef LIB9P_O_RCLOSE - x = LIB9P_O_RCLOSE; + x = LIB9P_O_RCLOSE; #endif #ifdef LIB9P_O_TRUNC - x = LIB9P_O_TRUNC; + x = LIB9P_O_TRUNC; #endif #ifdef LIB9P_QT_APPEND - x = LIB9P_QT_APPEND; + x = LIB9P_QT_APPEND; #endif #ifdef LIB9P_QT_AUTH - x = LIB9P_QT_AUTH; + x = LIB9P_QT_AUTH; #endif #ifdef LIB9P_QT_DIR - x = LIB9P_QT_DIR; + x = LIB9P_QT_DIR; #endif #ifdef LIB9P_QT_EXCL - x = LIB9P_QT_EXCL; + x = LIB9P_QT_EXCL; #endif #ifdef LIB9P_QT_FILE - x = LIB9P_QT_FILE; + x = LIB9P_QT_FILE; #endif #ifdef LIB9P_QT_SYMLINK - x = LIB9P_QT_SYMLINK; + x = LIB9P_QT_SYMLINK; #endif #ifdef LIB9P_QT_TMP - x = LIB9P_QT_TMP; + x = LIB9P_QT_TMP; #endif #ifdef LIB9P_RMSG_MAX_COPY - x = LIB9P_RMSG_MAX_COPY; + x = LIB9P_RMSG_MAX_COPY; #endif #ifdef LIB9P_RMSG_MAX_IOV - x = LIB9P_RMSG_MAX_IOV; + x = LIB9P_RMSG_MAX_IOV; #endif #ifdef LIB9P_SETATTR_ATIME - x = LIB9P_SETATTR_ATIME; + x = LIB9P_SETATTR_ATIME; #endif #ifdef LIB9P_SETATTR_ATIME_SET - x = LIB9P_SETATTR_ATIME_SET; + x = LIB9P_SETATTR_ATIME_SET; #endif #ifdef LIB9P_SETATTR_CTIME - x = LIB9P_SETATTR_CTIME; + x = LIB9P_SETATTR_CTIME; #endif #ifdef LIB9P_SETATTR_GID - x = LIB9P_SETATTR_GID; + x = LIB9P_SETATTR_GID; #endif #ifdef LIB9P_SETATTR_MODE - x = LIB9P_SETATTR_MODE; + x = LIB9P_SETATTR_MODE; #endif #ifdef LIB9P_SETATTR_MTIME - x = LIB9P_SETATTR_MTIME; + x = LIB9P_SETATTR_MTIME; #endif #ifdef LIB9P_SETATTR_MTIME_SET - x = LIB9P_SETATTR_MTIME_SET; + x = LIB9P_SETATTR_MTIME_SET; #endif #ifdef LIB9P_SETATTR_SIZE - x = LIB9P_SETATTR_SIZE; + x = LIB9P_SETATTR_SIZE; #endif #ifdef LIB9P_SETATTR_UID - x = LIB9P_SETATTR_UID; + x = LIB9P_SETATTR_UID; #endif #ifdef LIB9P_SUPER_MAGIC_V9FS_MAGIC - x = LIB9P_SUPER_MAGIC_V9FS_MAGIC; + x = LIB9P_SUPER_MAGIC_V9FS_MAGIC; #endif #ifdef LIB9P_TAG_NOTAG - x = LIB9P_TAG_NOTAG; + x = LIB9P_TAG_NOTAG; #endif #ifdef LIB9P_TMSG_MAX_COPY - x = LIB9P_TMSG_MAX_COPY; + x = LIB9P_TMSG_MAX_COPY; #endif #ifdef LIB9P_TMSG_MAX_IOV - x = LIB9P_TMSG_MAX_IOV; + x = LIB9P_TMSG_MAX_IOV; #endif #ifdef _LIB9P_DM_PLAN9_MOUNT - x = _LIB9P_DM_PLAN9_MOUNT; + x = _LIB9P_DM_PLAN9_MOUNT; #endif #ifdef _LIB9P_DM_UNUSED_10 - x = _LIB9P_DM_UNUSED_10; + x = _LIB9P_DM_UNUSED_10; #endif #ifdef _LIB9P_DM_UNUSED_11 - x = _LIB9P_DM_UNUSED_11; + x = _LIB9P_DM_UNUSED_11; #endif #ifdef _LIB9P_DM_UNUSED_12 - x = _LIB9P_DM_UNUSED_12; + x = _LIB9P_DM_UNUSED_12; #endif #ifdef _LIB9P_DM_UNUSED_13 - x = _LIB9P_DM_UNUSED_13; + x = _LIB9P_DM_UNUSED_13; #endif #ifdef _LIB9P_DM_UNUSED_14 - x = _LIB9P_DM_UNUSED_14; + x = _LIB9P_DM_UNUSED_14; #endif #ifdef _LIB9P_DM_UNUSED_15 - x = _LIB9P_DM_UNUSED_15; + x = _LIB9P_DM_UNUSED_15; #endif #ifdef _LIB9P_DM_UNUSED_16 - x = _LIB9P_DM_UNUSED_16; + x = _LIB9P_DM_UNUSED_16; #endif #ifdef _LIB9P_DM_UNUSED_17 - x = _LIB9P_DM_UNUSED_17; + x = _LIB9P_DM_UNUSED_17; #endif #ifdef _LIB9P_DM_UNUSED_22 - x = _LIB9P_DM_UNUSED_22; + x = _LIB9P_DM_UNUSED_22; #endif #ifdef _LIB9P_DM_UNUSED_24 - x = _LIB9P_DM_UNUSED_24; + x = _LIB9P_DM_UNUSED_24; #endif #ifdef _LIB9P_DM_UNUSED_25 - x = _LIB9P_DM_UNUSED_25; + x = _LIB9P_DM_UNUSED_25; #endif #ifdef _LIB9P_DM_UNUSED_9 - x = _LIB9P_DM_UNUSED_9; + x = _LIB9P_DM_UNUSED_9; #endif #ifdef _LIB9P_DT_WHITEOUT - x = _LIB9P_DT_WHITEOUT; + x = _LIB9P_DT_WHITEOUT; #endif #ifdef _LIB9P_ENABLE_stat - x = _LIB9P_ENABLE_stat; + x = _LIB9P_ENABLE_stat; #endif #ifdef _LIB9P_GETATTR_UNUSED_14 - x = _LIB9P_GETATTR_UNUSED_14; + x = _LIB9P_GETATTR_UNUSED_14; #endif #ifdef _LIB9P_GETATTR_UNUSED_15 - x = _LIB9P_GETATTR_UNUSED_15; + x = _LIB9P_GETATTR_UNUSED_15; #endif #ifdef _LIB9P_GETATTR_UNUSED_16 - x = _LIB9P_GETATTR_UNUSED_16; + x = _LIB9P_GETATTR_UNUSED_16; #endif #ifdef _LIB9P_GETATTR_UNUSED_17 - x = _LIB9P_GETATTR_UNUSED_17; + x = _LIB9P_GETATTR_UNUSED_17; #endif #ifdef _LIB9P_GETATTR_UNUSED_18 - x = _LIB9P_GETATTR_UNUSED_18; + x = _LIB9P_GETATTR_UNUSED_18; #endif #ifdef _LIB9P_GETATTR_UNUSED_19 - x = _LIB9P_GETATTR_UNUSED_19; + x = _LIB9P_GETATTR_UNUSED_19; #endif #ifdef _LIB9P_GETATTR_UNUSED_20 - x = _LIB9P_GETATTR_UNUSED_20; + x = _LIB9P_GETATTR_UNUSED_20; #endif #ifdef _LIB9P_GETATTR_UNUSED_21 - x = _LIB9P_GETATTR_UNUSED_21; + x = _LIB9P_GETATTR_UNUSED_21; #endif #ifdef _LIB9P_GETATTR_UNUSED_22 - x = _LIB9P_GETATTR_UNUSED_22; + x = _LIB9P_GETATTR_UNUSED_22; #endif #ifdef _LIB9P_GETATTR_UNUSED_23 - x = _LIB9P_GETATTR_UNUSED_23; + x = _LIB9P_GETATTR_UNUSED_23; #endif #ifdef _LIB9P_GETATTR_UNUSED_24 - x = _LIB9P_GETATTR_UNUSED_24; + x = _LIB9P_GETATTR_UNUSED_24; #endif #ifdef _LIB9P_GETATTR_UNUSED_25 - x = _LIB9P_GETATTR_UNUSED_25; + x = _LIB9P_GETATTR_UNUSED_25; #endif #ifdef _LIB9P_GETATTR_UNUSED_26 - x = _LIB9P_GETATTR_UNUSED_26; + x = _LIB9P_GETATTR_UNUSED_26; #endif #ifdef _LIB9P_GETATTR_UNUSED_27 - x = _LIB9P_GETATTR_UNUSED_27; + x = _LIB9P_GETATTR_UNUSED_27; #endif #ifdef _LIB9P_GETATTR_UNUSED_28 - x = _LIB9P_GETATTR_UNUSED_28; + x = _LIB9P_GETATTR_UNUSED_28; #endif #ifdef _LIB9P_GETATTR_UNUSED_29 - x = _LIB9P_GETATTR_UNUSED_29; + x = _LIB9P_GETATTR_UNUSED_29; #endif #ifdef _LIB9P_GETATTR_UNUSED_30 - x = _LIB9P_GETATTR_UNUSED_30; + x = _LIB9P_GETATTR_UNUSED_30; #endif #ifdef _LIB9P_GETATTR_UNUSED_31 - x = _LIB9P_GETATTR_UNUSED_31; + x = _LIB9P_GETATTR_UNUSED_31; #endif #ifdef _LIB9P_GETATTR_UNUSED_32 - x = _LIB9P_GETATTR_UNUSED_32; + x = _LIB9P_GETATTR_UNUSED_32; #endif #ifdef _LIB9P_GETATTR_UNUSED_33 - x = _LIB9P_GETATTR_UNUSED_33; + x = _LIB9P_GETATTR_UNUSED_33; #endif #ifdef _LIB9P_GETATTR_UNUSED_34 - x = _LIB9P_GETATTR_UNUSED_34; + x = _LIB9P_GETATTR_UNUSED_34; #endif #ifdef _LIB9P_GETATTR_UNUSED_35 - x = _LIB9P_GETATTR_UNUSED_35; + x = _LIB9P_GETATTR_UNUSED_35; #endif #ifdef _LIB9P_GETATTR_UNUSED_36 - x = _LIB9P_GETATTR_UNUSED_36; + x = _LIB9P_GETATTR_UNUSED_36; #endif #ifdef _LIB9P_GETATTR_UNUSED_37 - x = _LIB9P_GETATTR_UNUSED_37; + x = _LIB9P_GETATTR_UNUSED_37; #endif #ifdef _LIB9P_GETATTR_UNUSED_38 - x = _LIB9P_GETATTR_UNUSED_38; + x = _LIB9P_GETATTR_UNUSED_38; #endif #ifdef _LIB9P_GETATTR_UNUSED_39 - x = _LIB9P_GETATTR_UNUSED_39; + x = _LIB9P_GETATTR_UNUSED_39; #endif #ifdef _LIB9P_GETATTR_UNUSED_40 - x = _LIB9P_GETATTR_UNUSED_40; + x = _LIB9P_GETATTR_UNUSED_40; #endif #ifdef _LIB9P_GETATTR_UNUSED_41 - x = _LIB9P_GETATTR_UNUSED_41; + x = _LIB9P_GETATTR_UNUSED_41; #endif #ifdef _LIB9P_GETATTR_UNUSED_42 - x = _LIB9P_GETATTR_UNUSED_42; + x = _LIB9P_GETATTR_UNUSED_42; #endif #ifdef _LIB9P_GETATTR_UNUSED_43 - x = _LIB9P_GETATTR_UNUSED_43; + x = _LIB9P_GETATTR_UNUSED_43; #endif #ifdef _LIB9P_GETATTR_UNUSED_44 - x = _LIB9P_GETATTR_UNUSED_44; + x = _LIB9P_GETATTR_UNUSED_44; #endif #ifdef _LIB9P_GETATTR_UNUSED_45 - x = _LIB9P_GETATTR_UNUSED_45; + x = _LIB9P_GETATTR_UNUSED_45; #endif #ifdef _LIB9P_GETATTR_UNUSED_46 - x = _LIB9P_GETATTR_UNUSED_46; + x = _LIB9P_GETATTR_UNUSED_46; #endif #ifdef _LIB9P_GETATTR_UNUSED_47 - x = _LIB9P_GETATTR_UNUSED_47; + x = _LIB9P_GETATTR_UNUSED_47; #endif #ifdef _LIB9P_GETATTR_UNUSED_48 - x = _LIB9P_GETATTR_UNUSED_48; + x = _LIB9P_GETATTR_UNUSED_48; #endif #ifdef _LIB9P_GETATTR_UNUSED_49 - x = _LIB9P_GETATTR_UNUSED_49; + x = _LIB9P_GETATTR_UNUSED_49; #endif #ifdef _LIB9P_GETATTR_UNUSED_50 - x = _LIB9P_GETATTR_UNUSED_50; + x = _LIB9P_GETATTR_UNUSED_50; #endif #ifdef _LIB9P_GETATTR_UNUSED_51 - x = _LIB9P_GETATTR_UNUSED_51; + x = _LIB9P_GETATTR_UNUSED_51; #endif #ifdef _LIB9P_GETATTR_UNUSED_52 - x = _LIB9P_GETATTR_UNUSED_52; + x = _LIB9P_GETATTR_UNUSED_52; #endif #ifdef _LIB9P_GETATTR_UNUSED_53 - x = _LIB9P_GETATTR_UNUSED_53; + x = _LIB9P_GETATTR_UNUSED_53; #endif #ifdef _LIB9P_GETATTR_UNUSED_54 - x = _LIB9P_GETATTR_UNUSED_54; + x = _LIB9P_GETATTR_UNUSED_54; #endif #ifdef _LIB9P_GETATTR_UNUSED_55 - x = _LIB9P_GETATTR_UNUSED_55; + x = _LIB9P_GETATTR_UNUSED_55; #endif #ifdef _LIB9P_GETATTR_UNUSED_56 - x = _LIB9P_GETATTR_UNUSED_56; + x = _LIB9P_GETATTR_UNUSED_56; #endif #ifdef _LIB9P_GETATTR_UNUSED_57 - x = _LIB9P_GETATTR_UNUSED_57; + x = _LIB9P_GETATTR_UNUSED_57; #endif #ifdef _LIB9P_GETATTR_UNUSED_58 - x = _LIB9P_GETATTR_UNUSED_58; + x = _LIB9P_GETATTR_UNUSED_58; #endif #ifdef _LIB9P_GETATTR_UNUSED_59 - x = _LIB9P_GETATTR_UNUSED_59; + x = _LIB9P_GETATTR_UNUSED_59; #endif #ifdef _LIB9P_GETATTR_UNUSED_60 - x = _LIB9P_GETATTR_UNUSED_60; + x = _LIB9P_GETATTR_UNUSED_60; #endif #ifdef _LIB9P_GETATTR_UNUSED_61 - x = _LIB9P_GETATTR_UNUSED_61; + x = _LIB9P_GETATTR_UNUSED_61; #endif #ifdef _LIB9P_GETATTR_UNUSED_62 - x = _LIB9P_GETATTR_UNUSED_62; + x = _LIB9P_GETATTR_UNUSED_62; #endif #ifdef _LIB9P_GETATTR_UNUSED_63 - x = _LIB9P_GETATTR_UNUSED_63; + x = _LIB9P_GETATTR_UNUSED_63; #endif #ifdef _LIB9P_LOCK_FLAGS_UNUSED_10 - x = _LIB9P_LOCK_FLAGS_UNUSED_10; + x = _LIB9P_LOCK_FLAGS_UNUSED_10; #endif #ifdef _LIB9P_LOCK_FLAGS_UNUSED_11 - x = _LIB9P_LOCK_FLAGS_UNUSED_11; + x = _LIB9P_LOCK_FLAGS_UNUSED_11; #endif #ifdef _LIB9P_LOCK_FLAGS_UNUSED_12 - x = _LIB9P_LOCK_FLAGS_UNUSED_12; + x = _LIB9P_LOCK_FLAGS_UNUSED_12; #endif #ifdef _LIB9P_LOCK_FLAGS_UNUSED_13 - x = _LIB9P_LOCK_FLAGS_UNUSED_13; + x = _LIB9P_LOCK_FLAGS_UNUSED_13; #endif #ifdef _LIB9P_LOCK_FLAGS_UNUSED_14 - x = _LIB9P_LOCK_FLAGS_UNUSED_14; + x = _LIB9P_LOCK_FLAGS_UNUSED_14; #endif #ifdef _LIB9P_LOCK_FLAGS_UNUSED_15 - x = _LIB9P_LOCK_FLAGS_UNUSED_15; + x = _LIB9P_LOCK_FLAGS_UNUSED_15; #endif #ifdef _LIB9P_LOCK_FLAGS_UNUSED_16 - x = _LIB9P_LOCK_FLAGS_UNUSED_16; + x = _LIB9P_LOCK_FLAGS_UNUSED_16; #endif #ifdef _LIB9P_LOCK_FLAGS_UNUSED_17 - x = _LIB9P_LOCK_FLAGS_UNUSED_17; + x = _LIB9P_LOCK_FLAGS_UNUSED_17; #endif #ifdef _LIB9P_LOCK_FLAGS_UNUSED_18 - x = _LIB9P_LOCK_FLAGS_UNUSED_18; + x = _LIB9P_LOCK_FLAGS_UNUSED_18; #endif #ifdef _LIB9P_LOCK_FLAGS_UNUSED_19 - x = _LIB9P_LOCK_FLAGS_UNUSED_19; + x = _LIB9P_LOCK_FLAGS_UNUSED_19; #endif #ifdef _LIB9P_LOCK_FLAGS_UNUSED_2 - x = _LIB9P_LOCK_FLAGS_UNUSED_2; + x = _LIB9P_LOCK_FLAGS_UNUSED_2; #endif #ifdef _LIB9P_LOCK_FLAGS_UNUSED_20 - x = _LIB9P_LOCK_FLAGS_UNUSED_20; + x = _LIB9P_LOCK_FLAGS_UNUSED_20; #endif #ifdef _LIB9P_LOCK_FLAGS_UNUSED_21 - x = _LIB9P_LOCK_FLAGS_UNUSED_21; + x = _LIB9P_LOCK_FLAGS_UNUSED_21; #endif #ifdef _LIB9P_LOCK_FLAGS_UNUSED_22 - x = _LIB9P_LOCK_FLAGS_UNUSED_22; + x = _LIB9P_LOCK_FLAGS_UNUSED_22; #endif #ifdef _LIB9P_LOCK_FLAGS_UNUSED_23 - x = _LIB9P_LOCK_FLAGS_UNUSED_23; + x = _LIB9P_LOCK_FLAGS_UNUSED_23; #endif #ifdef _LIB9P_LOCK_FLAGS_UNUSED_24 - x = _LIB9P_LOCK_FLAGS_UNUSED_24; + x = _LIB9P_LOCK_FLAGS_UNUSED_24; #endif #ifdef _LIB9P_LOCK_FLAGS_UNUSED_25 - x = _LIB9P_LOCK_FLAGS_UNUSED_25; + x = _LIB9P_LOCK_FLAGS_UNUSED_25; #endif #ifdef _LIB9P_LOCK_FLAGS_UNUSED_26 - x = _LIB9P_LOCK_FLAGS_UNUSED_26; + x = _LIB9P_LOCK_FLAGS_UNUSED_26; #endif #ifdef _LIB9P_LOCK_FLAGS_UNUSED_27 - x = _LIB9P_LOCK_FLAGS_UNUSED_27; + x = _LIB9P_LOCK_FLAGS_UNUSED_27; #endif #ifdef _LIB9P_LOCK_FLAGS_UNUSED_28 - x = _LIB9P_LOCK_FLAGS_UNUSED_28; + x = _LIB9P_LOCK_FLAGS_UNUSED_28; #endif #ifdef _LIB9P_LOCK_FLAGS_UNUSED_29 - x = _LIB9P_LOCK_FLAGS_UNUSED_29; + x = _LIB9P_LOCK_FLAGS_UNUSED_29; #endif #ifdef _LIB9P_LOCK_FLAGS_UNUSED_3 - x = _LIB9P_LOCK_FLAGS_UNUSED_3; + x = _LIB9P_LOCK_FLAGS_UNUSED_3; #endif #ifdef _LIB9P_LOCK_FLAGS_UNUSED_30 - x = _LIB9P_LOCK_FLAGS_UNUSED_30; + x = _LIB9P_LOCK_FLAGS_UNUSED_30; #endif #ifdef _LIB9P_LOCK_FLAGS_UNUSED_31 - x = _LIB9P_LOCK_FLAGS_UNUSED_31; + x = _LIB9P_LOCK_FLAGS_UNUSED_31; #endif #ifdef _LIB9P_LOCK_FLAGS_UNUSED_4 - x = _LIB9P_LOCK_FLAGS_UNUSED_4; + x = _LIB9P_LOCK_FLAGS_UNUSED_4; #endif #ifdef _LIB9P_LOCK_FLAGS_UNUSED_5 - x = _LIB9P_LOCK_FLAGS_UNUSED_5; + x = _LIB9P_LOCK_FLAGS_UNUSED_5; #endif #ifdef _LIB9P_LOCK_FLAGS_UNUSED_6 - x = _LIB9P_LOCK_FLAGS_UNUSED_6; + x = _LIB9P_LOCK_FLAGS_UNUSED_6; #endif #ifdef _LIB9P_LOCK_FLAGS_UNUSED_7 - x = _LIB9P_LOCK_FLAGS_UNUSED_7; + x = _LIB9P_LOCK_FLAGS_UNUSED_7; #endif #ifdef _LIB9P_LOCK_FLAGS_UNUSED_8 - x = _LIB9P_LOCK_FLAGS_UNUSED_8; + x = _LIB9P_LOCK_FLAGS_UNUSED_8; #endif #ifdef _LIB9P_LOCK_FLAGS_UNUSED_9 - x = _LIB9P_LOCK_FLAGS_UNUSED_9; + x = _LIB9P_LOCK_FLAGS_UNUSED_9; #endif #ifdef _LIB9P_LO_UNUSED_2 - x = _LIB9P_LO_UNUSED_2; + x = _LIB9P_LO_UNUSED_2; #endif #ifdef _LIB9P_LO_UNUSED_21 - x = _LIB9P_LO_UNUSED_21; + x = _LIB9P_LO_UNUSED_21; #endif #ifdef _LIB9P_LO_UNUSED_22 - x = _LIB9P_LO_UNUSED_22; + x = _LIB9P_LO_UNUSED_22; #endif #ifdef _LIB9P_LO_UNUSED_23 - x = _LIB9P_LO_UNUSED_23; + x = _LIB9P_LO_UNUSED_23; #endif #ifdef _LIB9P_LO_UNUSED_24 - x = _LIB9P_LO_UNUSED_24; + x = _LIB9P_LO_UNUSED_24; #endif #ifdef _LIB9P_LO_UNUSED_25 - x = _LIB9P_LO_UNUSED_25; + x = _LIB9P_LO_UNUSED_25; #endif #ifdef _LIB9P_LO_UNUSED_26 - x = _LIB9P_LO_UNUSED_26; + x = _LIB9P_LO_UNUSED_26; #endif #ifdef _LIB9P_LO_UNUSED_27 - x = _LIB9P_LO_UNUSED_27; + x = _LIB9P_LO_UNUSED_27; #endif #ifdef _LIB9P_LO_UNUSED_28 - x = _LIB9P_LO_UNUSED_28; + x = _LIB9P_LO_UNUSED_28; #endif #ifdef _LIB9P_LO_UNUSED_29 - x = _LIB9P_LO_UNUSED_29; + x = _LIB9P_LO_UNUSED_29; #endif #ifdef _LIB9P_LO_UNUSED_3 - x = _LIB9P_LO_UNUSED_3; + x = _LIB9P_LO_UNUSED_3; #endif #ifdef _LIB9P_LO_UNUSED_30 - x = _LIB9P_LO_UNUSED_30; + x = _LIB9P_LO_UNUSED_30; #endif #ifdef _LIB9P_LO_UNUSED_31 - x = _LIB9P_LO_UNUSED_31; + x = _LIB9P_LO_UNUSED_31; #endif #ifdef _LIB9P_LO_UNUSED_4 - x = _LIB9P_LO_UNUSED_4; + x = _LIB9P_LO_UNUSED_4; #endif #ifdef _LIB9P_LO_UNUSED_5 - x = _LIB9P_LO_UNUSED_5; + x = _LIB9P_LO_UNUSED_5; #endif #ifdef _LIB9P_MODE_UNUSED_16 - x = _LIB9P_MODE_UNUSED_16; + x = _LIB9P_MODE_UNUSED_16; #endif #ifdef _LIB9P_MODE_UNUSED_17 - x = _LIB9P_MODE_UNUSED_17; + x = _LIB9P_MODE_UNUSED_17; #endif #ifdef _LIB9P_MODE_UNUSED_18 - x = _LIB9P_MODE_UNUSED_18; + x = _LIB9P_MODE_UNUSED_18; #endif #ifdef _LIB9P_MODE_UNUSED_19 - x = _LIB9P_MODE_UNUSED_19; + x = _LIB9P_MODE_UNUSED_19; #endif #ifdef _LIB9P_MODE_UNUSED_20 - x = _LIB9P_MODE_UNUSED_20; + x = _LIB9P_MODE_UNUSED_20; #endif #ifdef _LIB9P_MODE_UNUSED_21 - x = _LIB9P_MODE_UNUSED_21; + x = _LIB9P_MODE_UNUSED_21; #endif #ifdef _LIB9P_MODE_UNUSED_22 - x = _LIB9P_MODE_UNUSED_22; + x = _LIB9P_MODE_UNUSED_22; #endif #ifdef _LIB9P_MODE_UNUSED_23 - x = _LIB9P_MODE_UNUSED_23; + x = _LIB9P_MODE_UNUSED_23; #endif #ifdef _LIB9P_MODE_UNUSED_24 - x = _LIB9P_MODE_UNUSED_24; + x = _LIB9P_MODE_UNUSED_24; #endif #ifdef _LIB9P_MODE_UNUSED_25 - x = _LIB9P_MODE_UNUSED_25; + x = _LIB9P_MODE_UNUSED_25; #endif #ifdef _LIB9P_MODE_UNUSED_26 - x = _LIB9P_MODE_UNUSED_26; + x = _LIB9P_MODE_UNUSED_26; #endif #ifdef _LIB9P_MODE_UNUSED_27 - x = _LIB9P_MODE_UNUSED_27; + x = _LIB9P_MODE_UNUSED_27; #endif #ifdef _LIB9P_MODE_UNUSED_28 - x = _LIB9P_MODE_UNUSED_28; + x = _LIB9P_MODE_UNUSED_28; #endif #ifdef _LIB9P_MODE_UNUSED_29 - x = _LIB9P_MODE_UNUSED_29; + x = _LIB9P_MODE_UNUSED_29; #endif #ifdef _LIB9P_MODE_UNUSED_30 - x = _LIB9P_MODE_UNUSED_30; + x = _LIB9P_MODE_UNUSED_30; #endif #ifdef _LIB9P_MODE_UNUSED_31 - x = _LIB9P_MODE_UNUSED_31; + x = _LIB9P_MODE_UNUSED_31; #endif #ifdef _LIB9P_O_RESERVED_CEXEC - x = _LIB9P_O_RESERVED_CEXEC; + x = _LIB9P_O_RESERVED_CEXEC; #endif #ifdef _LIB9P_O_UNUSED_2 - x = _LIB9P_O_UNUSED_2; + x = _LIB9P_O_UNUSED_2; #endif #ifdef _LIB9P_O_UNUSED_3 - x = _LIB9P_O_UNUSED_3; + x = _LIB9P_O_UNUSED_3; #endif #ifdef _LIB9P_O_UNUSED_7 - x = _LIB9P_O_UNUSED_7; + x = _LIB9P_O_UNUSED_7; #endif #ifdef _LIB9P_QT_PLAN9_MOUNT - x = _LIB9P_QT_PLAN9_MOUNT; + x = _LIB9P_QT_PLAN9_MOUNT; #endif #ifdef _LIB9P_QT_UNUSED_0 - x = _LIB9P_QT_UNUSED_0; + x = _LIB9P_QT_UNUSED_0; #endif #ifdef _LIB9P_SETATTR_UNUSED_10 - x = _LIB9P_SETATTR_UNUSED_10; + x = _LIB9P_SETATTR_UNUSED_10; #endif #ifdef _LIB9P_SETATTR_UNUSED_11 - x = _LIB9P_SETATTR_UNUSED_11; + x = _LIB9P_SETATTR_UNUSED_11; #endif #ifdef _LIB9P_SETATTR_UNUSED_12 - x = _LIB9P_SETATTR_UNUSED_12; + x = _LIB9P_SETATTR_UNUSED_12; #endif #ifdef _LIB9P_SETATTR_UNUSED_13 - x = _LIB9P_SETATTR_UNUSED_13; + x = _LIB9P_SETATTR_UNUSED_13; #endif #ifdef _LIB9P_SETATTR_UNUSED_14 - x = _LIB9P_SETATTR_UNUSED_14; + x = _LIB9P_SETATTR_UNUSED_14; #endif #ifdef _LIB9P_SETATTR_UNUSED_15 - x = _LIB9P_SETATTR_UNUSED_15; + x = _LIB9P_SETATTR_UNUSED_15; #endif #ifdef _LIB9P_SETATTR_UNUSED_16 - x = _LIB9P_SETATTR_UNUSED_16; + x = _LIB9P_SETATTR_UNUSED_16; #endif #ifdef _LIB9P_SETATTR_UNUSED_17 - x = _LIB9P_SETATTR_UNUSED_17; + x = _LIB9P_SETATTR_UNUSED_17; #endif #ifdef _LIB9P_SETATTR_UNUSED_18 - x = _LIB9P_SETATTR_UNUSED_18; + x = _LIB9P_SETATTR_UNUSED_18; #endif #ifdef _LIB9P_SETATTR_UNUSED_19 - x = _LIB9P_SETATTR_UNUSED_19; + x = _LIB9P_SETATTR_UNUSED_19; #endif #ifdef _LIB9P_SETATTR_UNUSED_20 - x = _LIB9P_SETATTR_UNUSED_20; + x = _LIB9P_SETATTR_UNUSED_20; #endif #ifdef _LIB9P_SETATTR_UNUSED_21 - x = _LIB9P_SETATTR_UNUSED_21; + x = _LIB9P_SETATTR_UNUSED_21; #endif #ifdef _LIB9P_SETATTR_UNUSED_22 - x = _LIB9P_SETATTR_UNUSED_22; + x = _LIB9P_SETATTR_UNUSED_22; #endif #ifdef _LIB9P_SETATTR_UNUSED_23 - x = _LIB9P_SETATTR_UNUSED_23; + x = _LIB9P_SETATTR_UNUSED_23; #endif #ifdef _LIB9P_SETATTR_UNUSED_24 - x = _LIB9P_SETATTR_UNUSED_24; + x = _LIB9P_SETATTR_UNUSED_24; #endif #ifdef _LIB9P_SETATTR_UNUSED_25 - x = _LIB9P_SETATTR_UNUSED_25; + x = _LIB9P_SETATTR_UNUSED_25; #endif #ifdef _LIB9P_SETATTR_UNUSED_26 - x = _LIB9P_SETATTR_UNUSED_26; + x = _LIB9P_SETATTR_UNUSED_26; #endif #ifdef _LIB9P_SETATTR_UNUSED_27 - x = _LIB9P_SETATTR_UNUSED_27; + x = _LIB9P_SETATTR_UNUSED_27; #endif #ifdef _LIB9P_SETATTR_UNUSED_28 - x = _LIB9P_SETATTR_UNUSED_28; + x = _LIB9P_SETATTR_UNUSED_28; #endif #ifdef _LIB9P_SETATTR_UNUSED_29 - x = _LIB9P_SETATTR_UNUSED_29; + x = _LIB9P_SETATTR_UNUSED_29; #endif #ifdef _LIB9P_SETATTR_UNUSED_30 - x = _LIB9P_SETATTR_UNUSED_30; + x = _LIB9P_SETATTR_UNUSED_30; #endif #ifdef _LIB9P_SETATTR_UNUSED_31 - x = _LIB9P_SETATTR_UNUSED_31; + x = _LIB9P_SETATTR_UNUSED_31; #endif #ifdef _LIB9P_SETATTR_UNUSED_9 - x = _LIB9P_SETATTR_UNUSED_9; + x = _LIB9P_SETATTR_UNUSED_9; #endif - return 0; + return 0; } diff --git a/lib9p/tests/test_compile.c.gen b/lib9p/tests/test_compile.c.gen index c57ce9f..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 ' [[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 x = &;\n#endif/' - 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 02cb8e5..c0846ef 100644 --- a/lib9p/tests/test_compile_config/config.h +++ b/lib9p/tests/test_compile_config/config.h @@ -1,4 +1,4 @@ -/* config.h - Compile-time configuration for lib9p/test/test_compile +/* lib9p/tests/test_compile_config/config.h - Compile-time configuration for lib9p/test/test_compile * * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com> * SPDX-License-Identifier: AGPL-3.0-or-later @@ -9,23 +9,18 @@ /* 9P *************************************************************************/ -#define CONFIG_9P_MAX_ERR_SIZE 128 #define CONFIG_9P_MAX_9P2000_e_WELEM 16 /* 9P_SRV *********************************************************************/ +#define CONFIG_9P_SRV_DEBUG 1 +#define CONFIG_9P_SRV_MAX_ERR_SIZE 128 #define CONFIG_9P_SRV_MAX_MSG_SIZE ((4*1024)+24) #define CONFIG_9P_SRV_MAX_HOSTMSG_SIZE CONFIG_9P_SRV_MAX_MSG_SIZE+16 /* COROUTINE ******************************************************************/ -#define CONFIG_COROUTINE_STACK_SIZE_DEFAULT (32*1024) #define CONFIG_COROUTINE_NAME_LEN 16 #define CONFIG_COROUTINE_MEASURE_STACK 1 /* bool */ -#define CONFIG_COROUTINE_PROTECT_STACK 1 /* bool */ -#define CONFIG_COROUTINE_DEBUG 0 /* bool */ -#define CONFIG_COROUTINE_VALGRIND 1 /* bool */ -#define CONFIG_COROUTINE_GDB 1 /* bool */ -#define CONFIG_COROUTINE_NUM 8 #endif /* _CONFIG_H_ */ diff --git a/lib9p/tests/test_server/CMakeLists.txt b/lib9p/tests/test_server/CMakeLists.txt index c61d344..76b6ff3 100644 --- a/lib9p/tests/test_server/CMakeLists.txt +++ b/lib9p/tests/test_server/CMakeLists.txt @@ -5,17 +5,15 @@ if (PICO_PLATFORM STREQUAL "host") -# Compile ###################################################################### - -add_library(test_server_objs OBJECT +add_executable(test_server main.c fs_flush.c fs_shutdown.c fs_whoami.c ) -target_include_directories(test_server_objs PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/config) -target_include_directories(test_server_objs PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) -target_link_libraries(test_server_objs +target_include_directories(test_server PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/config) +target_include_directories(test_server PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) # for static.h +target_link_libraries(test_server libcr libcr_ipc libmisc @@ -23,21 +21,7 @@ target_link_libraries(test_server_objs libhw_cr ) -# Analyze the stack ############################################################ - -add_stack_analysis(test_server_stack.c test_server_objs) - -# Link ######################################################################### - -add_executable(test_server) -target_sources(test_server PRIVATE - test_server_stack.c - "$<TARGET_OBJECTS:test_server_objs>" -) - -# Embed ######################################################################## - -target_embed_sources(test_server_objs test_server static.h +target_embed_sources(test_server test_server static.h static/README.md static/Documentation/x.txt ) diff --git a/lib9p/tests/test_server/config/config.h b/lib9p/tests/test_server/config/config.h index f49894b..1af0213 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/tests/test_server * * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com> * SPDX-License-Identifier: AGPL-3.0-or-later @@ -12,8 +12,6 @@ /* 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 */ @@ -42,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) @@ -58,10 +57,9 @@ #define CONFIG_COROUTINE_DEBUG 0 /* bool */ #define CONFIG_COROUTINE_VALGRIND 1 /* bool */ #define CONFIG_COROUTINE_GDB 1 /* bool */ -#define CONFIG_COROUTINE_NUM ( \ - 1 /* usb_common */ + \ - 1 /* usb_keyboard */ + \ - _CONFIG_9P_MAX_CONNS /* accept+read */ + \ - _CONFIG_9P_MAX_REQS /* work+write */ ) +#define CONFIG_COROUTINE_NUM ( \ + 1 /* init_cr */ + \ + _CONFIG_9P_MAX_CONNS /* read_cr */ + \ + _CONFIG_9P_MAX_REQS /* write_cr */ ) #endif /* _CONFIG_H_ */ diff --git a/lib9p/tests/test_server/fs_flush.c b/lib9p/tests/test_server/fs_flush.c index e6408d7..c8152d4 100644 --- a/lib9p/tests/test_server/fs_flush.c +++ b/lib9p/tests/test_server/fs_flush.c @@ -9,20 +9,19 @@ #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, static); +LO_IMPLEMENTATION_C(lib9p_srv_file, struct flush_file, flush_file); struct flush_fio { struct flush_file *parent; }; -LO_IMPLEMENTATION_H(lib9p_srv_fio, struct flush_fio, flush_fio); -LO_IMPLEMENTATION_C(lib9p_srv_fio, struct flush_fio, flush_fio, static); +LO_IMPLEMENTATION_STATIC(lib9p_srv_fio, struct flush_fio, flush_fio); /* srv_file *******************************************************************/ -static void flush_file_free(struct flush_file *self) { +void flush_file_free(struct flush_file *self) { assert(self); } -static struct lib9p_qid flush_file_qid(struct flush_file *self) { +struct lib9p_qid flush_file_qid(struct flush_file *self) { assert(self); return (struct lib9p_qid){ .type = LIB9P_QT_FILE, @@ -31,10 +30,12 @@ static struct lib9p_qid flush_file_qid(struct flush_file *self) { }; } -static struct lib9p_srv_stat flush_file_stat(struct flush_file *self, struct lib9p_srv_ctx *ctx) { +error flush_file_stat(struct flush_file *self, struct lib9p_srv_ctx *ctx, struct lib9p_srv_stat *out) { assert(self); assert(ctx); - return (struct lib9p_srv_stat){ + assert(out); + + *out = ((struct lib9p_srv_stat){ .qid = flush_file_qid(self), .mode = 0444, .atime_sec = UTIL9P_ATIME, @@ -45,29 +46,30 @@ static struct lib9p_srv_stat flush_file_stat(struct flush_file *self, struct lib .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 flush_file_wstat(struct flush_file *self, struct lib9p_srv_ctx *ctx, struct lib9p_srv_stat) { +error flush_file_wstat(struct flush_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 flush_file_remove(struct flush_file *self, struct lib9p_srv_ctx *ctx) { +error flush_file_remove(struct flush_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 flush_file, flush_file) +LIB9P_SRV_NOTDIR(, struct flush_file, flush_file); -static lo_interface lib9p_srv_fio flush_file_fopen(struct flush_file *self, struct lib9p_srv_ctx *ctx, bool, bool, bool) { +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 lo_box_flush_fio_as_lib9p_srv_fio(ret); + return ERROR_NEW_VAL(lib9p_srv_fio, LO_BOX(lib9p_srv_fio, ret)); } /* srv_fio ********************************************************************/ @@ -77,7 +79,7 @@ static void flush_fio_iofree(struct flush_fio *self) { free(self); } -static struct lib9p_qid flush_fio_qid(struct flush_fio *self) { +static struct lib9p_qid flush_fio_ioqid(struct flush_fio *self) { assert(self); return flush_file_qid(self->parent); } @@ -87,19 +89,17 @@ static uint32_t flush_fio_iounit(struct flush_fio *self) { return 0; } -static uint32_t flush_fio_pwrite(struct flush_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 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 void flush_fio_pread(struct flush_fio *self, struct lib9p_srv_ctx *ctx, - uint32_t byte_count, uint64_t LM_UNUSED(byte_offset), - struct iovec *ret) { +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); - assert(ret); /* Wait for first Tflush */ while (!lib9p_srv_flush_requested(ctx)) @@ -111,21 +111,18 @@ static void flush_fio_pread(struct flush_fio *self, struct lib9p_srv_ctx *ctx, 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: - *ret = (struct iovec){ - .iov_base = "Sloth\n", - .iov_len = 6 < byte_count ? 6 : byte_count, - }; - break; + return io_write(dst, "Sloth\n", 6 < byte_count ? 6 : byte_count).err; case FLUSH_ERROR: - lib9p_srv_acknowledge_flush(ctx); - lib9p_error(&ctx->basectx, LIB9P_ERRNO_L_ECANCELED, "request canceled by flush"); - break; + return error_new(E_POSIX_EAGAIN, "request canceled by flush"); case FLUSH_SILENT: - lib9p_srv_acknowledge_flush(ctx); - break; + return error_new(E_POSIX_ECANCELED, "request canceled by flush"); + default: + assert_notreached("invalid flush_behavior"); } - cr_yield(); } diff --git a/lib9p/tests/test_server/fs_flush.h b/lib9p/tests/test_server/fs_flush.h index a509c4a..023434b 100644 --- a/lib9p/tests/test_server/fs_flush.h +++ b/lib9p/tests/test_server/fs_flush.h @@ -7,8 +7,8 @@ #ifndef _LIB9P_TESTS_TEST_SERVER_FS_FLUSH_H_ #define _LIB9P_TESTS_TEST_SERVER_FS_FLUSH_H_ -#include <util9p/static.h> #include <libhw/host_net.h> +#include <util9p/static.h> struct flush_file { char *name; @@ -22,6 +22,5 @@ struct flush_file { } flush_behavior; }; LO_IMPLEMENTATION_H(lib9p_srv_file, struct flush_file, flush_file); -#define lo_box_flush_file_as_lib9p_srv_file(obj) util9p_box(flush_file, obj) #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 d4ae67e..53a243b 100644 --- a/lib9p/tests/test_server/fs_shutdown.c +++ b/lib9p/tests/test_server/fs_shutdown.c @@ -8,20 +8,19 @@ #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 | LIB9P_QT_APPEND, @@ -30,10 +29,12 @@ static struct lib9p_qid shutdown_file_qid(struct shutdown_file *self) { }; } -static struct lib9p_srv_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_srv_stat){ + assert(out); + + *out = ((struct lib9p_srv_stat){ .qid = shutdown_file_qid(self), .mode = 0222 | LIB9P_DM_APPEND, .atime_sec = UTIL9P_ATIME, @@ -44,29 +45,30 @@ static struct lib9p_srv_stat shutdown_file_stat(struct shutdown_file *self, stru .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_srv_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 = 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 ********************************************************************/ @@ -76,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); } @@ -86,19 +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 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_whoami.c b/lib9p/tests/test_server/fs_whoami.c index 8d9752a..a07fdba 100644 --- a/lib9p/tests/test_server/fs_whoami.c +++ b/lib9p/tests/test_server/fs_whoami.c @@ -4,22 +4,22 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ -#include <stdio.h> /* for snprintf() */ -#include <stdlib.h> /* for 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); @@ -40,10 +40,10 @@ size_t whoami_len(struct lib9p_srv_ctx *ctx) { /* 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, @@ -52,11 +52,12 @@ static struct lib9p_qid whoami_file_qid(struct whoami_file *self) { }; } -static struct lib9p_srv_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); + assert(out); - return (struct lib9p_srv_stat){ + *out = ((struct lib9p_srv_stat){ .qid = whoami_file_qid(self), .mode = 0444, .atime_sec = UTIL9P_ATIME, @@ -67,22 +68,23 @@ static struct lib9p_srv_stat whoami_file_stat(struct whoami_file *self, struct l .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_srv_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); @@ -91,7 +93,7 @@ static lo_interface lib9p_srv_fio whoami_file_fopen(struct whoami_file *self, st 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 ********************************************************************/ @@ -103,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); @@ -114,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) { @@ -135,19 +135,13 @@ static void whoami_fio_pread(struct whoami_fio *self, struct lib9p_srv_ctx *ctx, snprintf(self->buf, self->buf_len, "%"PRIu32" %.*s\n", ctx->user->num, ctx->user->name.len, ctx->user->name.utf8); - if (byte_offset > (uint64_t)data_size) { - 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 4caff16..0016d21 100644 --- a/lib9p/tests/test_server/main.c +++ b/lib9p/tests/test_server/main.c @@ -4,21 +4,26 @@ * 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" + +/* 9P files */ +#include <util9p/static.h> #include "fs_flush.h" #include "fs_shutdown.h" #include "fs_whoami.h" @@ -36,7 +41,7 @@ /* 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); static const char *hexdig = "0123456789abcdef"; @@ -84,8 +89,8 @@ static struct lib9p_srv_file root = 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 ***********************************************************************/ @@ -96,7 +101,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(); } @@ -117,20 +122,19 @@ 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(); } struct tstlog_stdout {}; -LO_IMPLEMENTATION_H(fmt_dest, struct tstlog_stdout, tstlog_stdout); -LO_IMPLEMENTATION_C(fmt_dest, struct tstlog_stdout, tstlog_stdout, static); +LO_IMPLEMENTATION_STATIC(fmt_dest, struct tstlog_stdout, tstlog_stdout); static size_t tstlog_bytes = 0; @@ -153,18 +157,18 @@ static void tstlog_msg(struct lib9p_srv_ctx *ctx, enum lib9p_msg_type typ, void 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"); + __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-sess.c b/lib9p/tests/testclient-sess.c index 7cb7f97..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, diff --git a/lib9p/tests/testclient-sess.explog b/lib9p/tests/testclient-sess.explog index a3838ac..ec8d9c9 100644 --- a/lib9p/tests/testclient-sess.explog +++ b/lib9p/tests/testclient-sess.explog @@ -103,7 +103,7 @@ < 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_ECANCELED } +< Rerror { tag=0 errstr="request canceled by flush" errnum=L_EAGAIN } < Rflush { tag=1 } # flush, original request is aborted without error diff --git a/lib9p_util/include/util9p/static.h b/lib9p_util/include/util9p/static.h index 5454c24..4bb24c4 100644 --- a/lib9p_util/include/util9p/static.h +++ b/lib9p_util/include/util9p/static.h @@ -9,12 +9,6 @@ #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 @@ -56,7 +50,6 @@ struct util9p_static_dir { 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){ \ @@ -74,7 +67,6 @@ struct util9p_static_file { 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){ \ diff --git a/lib9p_util/static.c b/lib9p_util/static.c index c35d28c..338e9c9 100644 --- a/lib9p_util/static.c +++ b/lib9p_util/static.c @@ -9,21 +9,18 @@ #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){ @@ -33,11 +30,11 @@ static struct lib9p_qid util9p_static_dir_qid(struct util9p_static_dir *self) { }; } -static struct lib9p_srv_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_srv_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, @@ -48,88 +45,92 @@ static struct lib9p_srv_stat util9p_static_dir_stat(struct util9p_static_dir *se .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; } -static void util9p_static_dir_wstat(struct util9p_static_dir *self, struct lib9p_srv_ctx *ctx, - struct lib9p_srv_stat) { +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_srv_stat stat = LO_CALL(file, stat, ctx); - if (lib9p_ctx_has_error(&ctx->basectx)) - break; - lib9p_srv_stat_assert(stat); + 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 file; + 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), - struct lib9p_srv_userid *LM_UNUSED(user), - struct lib9p_srv_userid *LM_UNUSED(group), - lib9p_dm_t LM_UNUSED(perm)) { +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 void util9p_static_dir_iofree(struct util9p_static_dir *self) { + +static struct lib9p_qid util9p_static_dio_ioqid(struct util9p_static_dir *self) { + return util9p_static_dir_qid(self); +} +static void util9p_static_dio_iofree(struct util9p_static_dir *self) { assert(self); } -static struct lib9p_srv_dirent util9p_static_dir_dread(struct util9p_static_dir *self, struct lib9p_srv_ctx *ctx, size_t idx) { +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); lo_interface lib9p_srv_file file = self->members[idx]; if (LO_IS_NULL(file)) - return (struct lib9p_srv_dirent){}; + return ERROR_NEW_VAL(lib9p_srv_dirent, (struct lib9p_srv_dirent){}); - struct lib9p_srv_stat stat = LO_CALL(file, stat, ctx); - if (lib9p_ctx_has_error(&ctx->basectx)) - return (struct lib9p_srv_dirent){}; - lib9p_srv_stat_assert(stat); + 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 (struct lib9p_srv_dirent){ + 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){ @@ -151,11 +152,12 @@ static inline size_t util9p_static_file_size(struct util9p_static_file *file) { } -static struct lib9p_srv_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); + assert(out); - return (struct lib9p_srv_stat){ + *out = ((struct lib9p_srv_stat){ .qid = util9p_static_file_qid(self), .mode = self->c.perm & 0444, .atime_sec = self->c.atime, @@ -166,69 +168,67 @@ static struct lib9p_srv_stat util9p_static_file_stat(struct util9p_static_file * .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; } -static void util9p_static_file_wstat(struct util9p_static_file *self, struct lib9p_srv_ctx *ctx, +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 struct lib9p_qid util9p_static_fio_ioqid(struct util9p_static_file *self) { + return util9p_static_file_qid(self); } -static void util9p_static_file_iofree(struct util9p_static_file *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 920c371..182e85c 100644 --- a/libcr/coroutine.c +++ b/libcr/coroutine.c @@ -399,6 +399,7 @@ struct coroutine { #if CONFIG_COROUTINE_VALGRIND unsigned stack_id; #endif + bool stack_free; /* 4. env ***************************************************/ cr_plat_jmp_buf env; @@ -547,16 +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); - log_debugln("coroutine_add_with_stack_size(", stack_size, ", ", (qstr, name), ", ", (ptr, fn), ", ", (ptr, args), ")..."); if (!coroutine_initialized) { cr_plat_init(); @@ -577,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; - log_infoln("allocing ", (qstr, name), " stack with size ", - stack_size, "+2*", (base10, 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); - log_infoln("... done, stack is [", + 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 + CR_STACK_GUARD_SIZE + stack_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 ***************************************************/ @@ -602,12 +614,12 @@ 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 ; - log_debugln("...stack =", (ptr, coroutine_table[child-1].stack)); log_debugln("...stack_base=", (ptr, stack_base)); /* run until cr_begin() */ cr_plat_call_with_stack(stack_base, fn, args); @@ -622,10 +634,33 @@ 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) { @@ -663,7 +698,8 @@ 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); + if (coroutine_table[coroutine_running-1].stack_free) + free(coroutine_table[coroutine_running-1].stack); coroutine_table[coroutine_running-1] = (struct coroutine){}; coroutine_cnt--; } @@ -685,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 diff --git a/libcr/include/libcr/coroutine.h b/libcr/include/libcr/coroutine.h index f72dc96..f0e6e61 100644 --- a/libcr/include/libcr/coroutine.h +++ b/libcr/include/libcr/coroutine.h @@ -26,7 +26,7 @@ #ifndef _LIBCR_COROUTINE_H_ #define _LIBCR_COROUTINE_H_ -#include <stddef.h> /* for size_t */ +#include <stddef.h> /* for size_t */ /* Configuration **************************************************************/ @@ -35,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 *******************************************************************/ @@ -80,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. * @@ -91,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/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 b7ecfc8..b52dab1 100644 --- a/libcr_ipc/chan.c +++ b/libcr_ipc/chan.c @@ -4,7 +4,7 @@ * 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> diff --git a/libcr_ipc/include/libcr_ipc/chan.h b/libcr_ipc/include/libcr_ipc/chan.h index 09d269f..1755a97 100644 --- a/libcr_ipc/include/libcr_ipc/chan.h +++ b/libcr_ipc/include/libcr_ipc/chan.h @@ -7,7 +7,7 @@ #ifndef _LIBCR_IPC_CHAN_H_ #define _LIBCR_IPC_CHAN_H_ -#include <stddef.h> /* for size_t */ +#include <stddef.h> /* for size_t */ #include <libmisc/linkedlist.h> /* for DLIST_DECLARE() */ #include <libmisc/private.h> diff --git a/libcr_ipc/include/libcr_ipc/rpc.h b/libcr_ipc/include/libcr_ipc/rpc.h index c5336cd..85ebdb3 100644 --- a/libcr_ipc/include/libcr_ipc/rpc.h +++ b/libcr_ipc/include/libcr_ipc/rpc.h @@ -7,6 +7,7 @@ #ifndef _LIBCR_IPC_RPC_H_ #define _LIBCR_IPC_RPC_H_ +#include <libcr/coroutine.h> /* for cid_t */ #include <libmisc/linkedlist.h> /* for SLIST_DECLARE() */ /** diff --git a/libcr_ipc/rpc.c b/libcr_ipc/rpc.c index fcf51ba..b1b7674 100644 --- a/libcr_ipc/rpc.c +++ b/libcr_ipc/rpc.c @@ -4,7 +4,7 @@ * 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> 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 a6eba82..ee1751f 100644 --- a/libcr_ipc/tests/test_chan.c +++ b/libcr_ipc/tests/test_chan.c @@ -11,7 +11,9 @@ CR_CHAN_DECLARE(intchan, int); -COROUTINE producer_cr(void *_ch) { +/* test 1 *********************************************************************/ + +COROUTINE test1_producer_cr(void *_ch) { intchan_t *ch = _ch; cr_begin(); @@ -22,11 +24,10 @@ COROUTINE producer_cr(void *_ch) { cr_chan_send(ch, 2); - cr_end(); } -COROUTINE consumer_cr(void *_ch) { +COROUTINE test1_consumer_cr(void *_ch) { int x; intchan_t *ch = _ch; cr_begin(); @@ -40,10 +41,91 @@ COROUTINE consumer_cr(void *_ch) { 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() { + printf("== test 1 =========================================\n"); intchan_t ch = {}; - coroutine_add("producer", producer_cr, &ch); - coroutine_add("consumer", consumer_cr, &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_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_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_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 3107155..0000000 --- a/libcr_ipc/tests/test_select.c +++ /dev/null @@ -1,89 +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] = {}; -intchan_t fch = {}; - -COROUTINE 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(&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; - ret_arg = cr_select_l(CR_SELECT_SEND(&fch, &send), - CR_SELECT_DEFAULT); - test_assert(ret_arg == 1); - - cr_end(); -} - -COROUTINE producer_cr(void *_n) { - int n = *(int *)_n; - cr_begin(); - - cr_chan_send(&ch[n], n); - - cr_end(); -} - -COROUTINE final_cr(void *) { - cr_begin(); - - int ret = cr_chan_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", producer_cr, &i); - coroutine_add("consumer", consumer_cr, NULL); - coroutine_add("final", final_cr, NULL); - coroutine_main(); - return 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 8e8361d..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 * \**********************************************************************/ @@ -461,14 +459,14 @@ static bool dhcp_client_send(struct dhcp_client *client, uint8_t msgtyp, const c * Send * \**********************************************************************/ log_debugln("state ", state_strs[client->state], ": sending DHCP ", 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) { - log_debugln("error: sendto: ", r); - return true; + 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); - log_debugln("check_ip_conflict => ", 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) { @@ -755,18 +741,19 @@ 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 (;;) { + [[gnu::cleanup(error_cleanup)]] error err = {}; log_debugln("loop: state=", state_strs[client->state]); switch (client->state) { case STATE_INIT: @@ -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,24 +775,23 @@ 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); - log_debugln("error: recvfrom: ", 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)) { + 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 { @@ -818,22 +804,20 @@ static void dhcp_client_setstate(struct dhcp_client *client, } break; default: - assert(r < 0); - log_debugln("error: recvfrom: ", 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); - log_debugln("error: recvfrom: ", r); + log_debugln("error: recvfrom: ", (error, err)); } break; case STATE_RENEWING: @@ -841,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); @@ -856,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); - log_debugln("error: recvfrom: ", 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); @@ -882,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); - log_debugln("error: recvfrom: ", 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/libdhcp/tests/test.h b/libdhcp/tests/test.h new file mode 120000 index 0000000..2fb1bd5 --- /dev/null +++ b/libdhcp/tests/test.h @@ -0,0 +1 @@ +../../libmisc/tests/test.h
\ No newline at end of file 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/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 ac2093c..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); } @@ -52,15 +55,15 @@ static void hostclock_handle_sig_alarm(int LM_UNUSED(sig), siginfo_t *info, void .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 = {}, }; 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 3cc43c1..2ddb054 100644 --- a/libhw_cr/host_include/libhw/host_alarmclock.h +++ b/libhw_cr/host_include/libhw/host_alarmclock.h @@ -7,10 +7,10 @@ #ifndef _LIBHW_HOST_ALARMCLOCK_H_ #define _LIBHW_HOST_ALARMCLOCK_H_ -#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 4a2e65f..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 */ @@ -31,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 *********************************************************************/ @@ -63,7 +65,7 @@ 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 { \ @@ -78,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); } } @@ -136,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; } @@ -158,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() *****************************************************************/ @@ -215,29 +222,36 @@ 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); @@ -246,29 +260,36 @@ static ssize_t hostnet_tcp_readv(struct _hostnet_tcp_conn *conn, const struct io 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){}; } - 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() ****************************************************************/ @@ -281,7 +302,8 @@ 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) { @@ -299,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; @@ -314,12 +336,13 @@ 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); @@ -328,35 +351,44 @@ static ssize_t hostnet_tcp_writev(struct _hostnet_tcp_conn *conn, const struct 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() *****************************************************************/ @@ -375,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; @@ -395,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) { @@ -408,23 +443,29 @@ static void *hostnet_pthread_sendto(void *_args) { 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(), @@ -435,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; @@ -460,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; }; @@ -473,41 +519,48 @@ static void *hostnet_pthread_recvfrom(void *_args) { struct sockaddr gen; struct sockaddr_storage stor; } addr = {}; - socklen_t addr_size; + socklen_t addr_size = sizeof(addr); - *(args->ret_size) = setsockopt(args->connfd, SOL_SOCKET, SO_RCVTIMEO, - &args->timeout, sizeof(args->timeout)); - if (*(args->ret_size) < 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_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(), @@ -516,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){}; } - 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 e117c19..0248f23 100644 --- a/libhw_cr/rp2040_dma.c +++ b/libhw_cr/rp2040_dma.c @@ -1,5 +1,8 @@ /* 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 */ @@ -8,6 +11,21 @@ #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; @@ -22,6 +40,7 @@ static void dmairq_handler(void) { assert(irq_idx < NUM_DMA_IRQS); uint32_t regval = dma_hw->irq_ctrl[irq_idx].ints; + dma_hw->intr = regval; /* acknowledge irq */ for (uint channel = 0; channel < NUM_DMA_CHANNELS; channel++) { if (regval & 1u<<channel) { struct dmairq_handler_entry *handler = &dmairq_handlers[channel]; @@ -29,8 +48,6 @@ static void dmairq_handler(void) { handler->fn(handler->arg, irq, channel); } } - /* acknowledge irq */ - dma_hw->intr = regval; } void dmairq_set_and_enable_exclusive_handler(enum dmairq irq, uint channel, dmairq_handler_t fn, void *arg) { diff --git a/libhw_cr/rp2040_dma.h b/libhw_cr/rp2040_dma.h index c7f5a8f..13862d0 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 ***************************************************************/ @@ -46,26 +43,26 @@ typedef void (*dmairq_handler_t)(void *arg, enum dmairq irq, uint channel); * has a NULL trigger (depending on the channel's configuration). * * Your handler does not need to acknowledge the IRQ; that will be - * done for you after your handler is called. + * done for you before your handler is called. * * It is illegal to enable the same channel on more than one IRQ. */ void dmairq_set_and_enable_exclusive_handler(enum dmairq irq, uint channel, dmairq_handler_t fn, void *arg); -#define DMA_CTRL_ENABLE (1<<0) -#define DMA_CTRL_HI_PRIO (1<<1) -#define DMA_CTRL_DATA_SIZE(sz) ((sz)<<2) -#define DMA_CTRL_INCR_READ (1<<4) -#define DMA_CTRL_INCR_WRITE (1<<5) -#define _DMA_CTRL_RING_BITS(b) ((b)<<6) -#define _DMA_CTRL_RING_RD (0) -#define _DMA_CTRL_RING_WR (1<<10) +#define DMA_CTRL_ENABLE (1<<0) +#define DMA_CTRL_HI_PRIO (1<<1) +#define DMA_CTRL_DATA_SIZE(sz) ((sz)<<2) +#define DMA_CTRL_INCR_READ (1<<4) +#define DMA_CTRL_INCR_WRITE (1<<5) +#define _DMA_CTRL_RING_BITS(b) ((b)<<6) +#define _DMA_CTRL_RING_RD (0) +#define _DMA_CTRL_RING_WR (1<<10) #define DMA_CTRL_RING(rdwr, bits) (_DMA_CTRL_RING_##rdwr | _DMA_CTRL_RING_BITS(bits)) -#define DMA_CTRL_CHAIN_TO(ch) ((ch)<<11) -#define DMA_CTRL_TREQ_SEL(dreq) ((dreq)<<15) -#define DMA_CTRL_IRQ_QUIET (1<<21) -#define DMA_CTRL_BSWAP (1<<22) -#define DMA_CTRL_SNIFF_EN (1<<23) +#define DMA_CTRL_CHAIN_TO(ch) ((ch)<<11) +#define DMA_CTRL_TREQ_SEL(dreq) ((dreq)<<15) +#define DMA_CTRL_IRQ_QUIET (1<<21) +#define DMA_CTRL_BSWAP (1<<22) +#define DMA_CTRL_SNIFF_EN (1<<23) /* | elem | val | name */ #define READ_ADDR /*|*/volatile const void/*|*/ * /*|*/read_addr diff --git a/libhw_cr/rp2040_gpioirq.c b/libhw_cr/rp2040_gpioirq.c index 5b3d616..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> diff --git a/libhw_cr/rp2040_hwspi.c b/libhw_cr/rp2040_hwspi.c index 75acd58..6b0d1f5 100644 --- a/libhw_cr/rp2040_hwspi.c +++ b/libhw_cr/rp2040_hwspi.c @@ -4,8 +4,6 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ -#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> @@ -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; @@ -96,6 +94,7 @@ void _rp2040_hwspi_init(struct rp2040_hwspi *self, assert_notreached("invalid hwspi instance number"); } + /* Initialize the PL022. */ actual_baudrate_hz = spi_init(inst, baudrate_hz); log_debugln("baudrate = ", actual_baudrate_hz, "Hz"); assert(actual_baudrate_hz == baudrate_hz); @@ -138,7 +137,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); @@ -200,8 +199,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 = stack_alloc(pruned_iovcnt+1, struct dma_alias1); - struct dma_alias0 *rx_data_blocks = stack_alloc(pruned_iovcnt+1, struct dma_alias0); + [[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)); @@ -270,4 +269,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/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 6f3f302..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,12 +68,12 @@ * 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() */ @@ -126,20 +127,20 @@ static const char *w5500_state_str(uint8_t state) { /* 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 ********************************************************/ @@ -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,7 +428,7 @@ 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) { +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)); @@ -434,12 +436,12 @@ static void w5500_if_ifup(struct w5500 *chip, struct net_iface_config cfg) { _w5500_if_up(chip, cfg); } -static void w5500_if_ifdown(struct w5500 *chip) { +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); @@ -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)); @@ -471,7 +473,7 @@ static lo_interface net_stream_conn w5500_if_tcp_dial(struct w5500 *chip, struct _w5500_socket *socket = w5500_alloc_socket(chip); if (!socket) { log_debugln("tcp_dial() => no sock"); - return LO_NULL(net_stream_conn); + return ERROR_NEW_ERR(net_stream_conn, error_new(E_POSIX_ENOTSOCK)); } uint8_t socknum = socket->socknum; log_debugln("tcp_dial() => sock[", socknum, "]"); @@ -508,14 +510,14 @@ static lo_interface net_stream_conn w5500_if_tcp_dial(struct w5500 *chip, 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); @@ -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) { log_debugln("tcp_listener.accept() => already closed"); - return LO_NULL(net_stream_conn); + return ERROR_NEW_ERR(net_stream_conn, error_new(E_NET_ECLOSED)); } cr_mutex_lock(&chip->mu); @@ -581,28 +599,28 @@ 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) { +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; @@ -636,14 +654,14 @@ static ssize_t w5500_tcp_writev(struct _w5500_socket *socket, const struct iovec while (done < count) { if (!socket->write_open) { log_debugln(" => soft closed"); - return -NET_ECLOSED; + 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); log_debugln(" => hard closed"); - return -NET_ECLOSED; + 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)); @@ -672,7 +690,7 @@ static ssize_t w5500_tcp_writev(struct _w5500_socket *socket, const struct iovec break; case SOCKINTR_SEND_TIMEOUT: log_debugln(" => ACK timeout"); - return -NET_EACK_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: @@ -680,7 +698,7 @@ static ssize_t w5500_tcp_writev(struct _w5500_socket *socket, const struct iovec } } log_debugln(" => send finished"); - return done; + return ERROR_AND(size_t, done, ERROR_NULL); } static void w5500_tcp_set_read_deadline(struct _w5500_socket *socket, uint64_t ns) { @@ -694,7 +712,7 @@ 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; @@ -717,12 +735,12 @@ static ssize_t w5500_tcp_readv(struct _w5500_socket *socket, const struct iovec if (!socket->read_open) { LO_CALL(bootclock, del_trigger, &trigger); log_debugln(" => soft closed"); - return -NET_ECLOSED; + 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); log_debugln(" => recv timeout"); - return -NET_ERECV_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); @@ -735,7 +753,7 @@ static ssize_t w5500_tcp_readv(struct _w5500_socket *socket, const struct iovec LO_CALL(bootclock, del_trigger, &trigger); cr_mutex_unlock(&chip->mu); log_debugln(" => hard closed"); - return -NET_ECLOSED; + return ERROR_NEW_ERR(size_t, error_new(E_NET_ECLOSED)); } avail = uint16be_unmarshal(w5500ll_read_sock_reg(chip->spidev, socknum, rx_size)); @@ -746,7 +764,7 @@ static ssize_t w5500_tcp_readv(struct _w5500_socket *socket, const struct iovec LO_CALL(bootclock, del_trigger, &trigger); cr_mutex_unlock(&chip->mu); log_debugln(" => EOF"); - return 0; + return ERROR_NEW_ERR(size_t, error_new(E_EOF)); } cr_mutex_unlock(&chip->mu); @@ -765,10 +783,10 @@ 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) { +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); @@ -796,17 +814,17 @@ 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) { +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); @@ -815,7 +833,7 @@ static ssize_t w5500_udp_sendto(struct _w5500_socket *socket, void *buf, size_t uint16_t bufsize = ((uint16_t)w5500ll_read_sock_reg(chip->spidev, socknum, tx_buf_size))*1024; if (count > bufsize) { log_debugln(" => msg too large"); - return -NET_EMSGSIZE; + return error_new(E_POSIX_EMSGSIZE); } for (;;) { @@ -824,7 +842,7 @@ static ssize_t w5500_udp_sendto(struct _w5500_socket *socket, void *buf, size_t if (state != STATE_UDP) { cr_mutex_unlock(&chip->mu); log_debugln(" => closed"); - return -NET_ECLOSED; + return error_new(E_NET_ECLOSED); } uint16_t freesize = uint16be_unmarshal(w5500ll_read_sock_reg(chip->spidev, socknum, tx_free_size)); @@ -842,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)); @@ -854,10 +872,10 @@ static ssize_t w5500_udp_sendto(struct _w5500_socket *socket, void *buf, size_t switch (cr_chan_recv(&socket->write_ch)) { case SOCKINTR_SEND_OK: log_debugln(" => sent"); - return count; + return ERROR_NULL; case SOCKINTR_SEND_TIMEOUT: log_debugln(" => ARP timeout"); - return -NET_EARP_TIMEOUT; + return error_new(E_NET_EARP_TIMEOUT); case SOCKINTR_SEND_OK|SOCKINTR_SEND_TIMEOUT: assert_notreached("send both OK and timed out?"); default: @@ -876,8 +894,8 @@ 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) { +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); @@ -896,14 +914,14 @@ static ssize_t w5500_udp_recvfrom(struct _w5500_socket *socket, void *buf, size_ if (socket->read_deadline_ns && socket->read_deadline_ns <= LO_CALL(bootclock, get_time_ns)) { LO_CALL(bootclock, del_trigger, &trigger); log_debugln(" => recv timeout"); - return -NET_ERECV_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); log_debugln(" => hard closed"); - return -NET_ECLOSED; + return ERROR_NEW_ERR(size_t, error_new(E_NET_ECLOSED)); } avail = uint16be_unmarshal(w5500ll_read_sock_reg(chip->spidev, socknum, rx_size)); @@ -920,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) { @@ -937,8 +955,8 @@ static ssize_t w5500_udp_recvfrom(struct _w5500_socket *socket, void *buf, size_ /* 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. */ @@ -947,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) { +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..ec40e5e --- /dev/null +++ b/libhw_cr/w5500_ll.c @@ -0,0 +1,116 @@ +/* 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 <libmisc/log.h> + +#include "w5500_ll.h" + +#if CONFIG_W5500_LL_DEBUG +static void fmt_print_ctl_block(lo_interface fmt_dest w, uint8_t b) { + static char *strs[] = { + "RES", + "REG", + "TX", + "RX", + }; + fmt_print(w, "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 aa41e7a..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> @@ -11,16 +11,14 @@ #define _LIBHW_CR_W5500_LL_H_ #include <stdint.h> /* for uint{n}_t */ -#include <string.h> /* for memcmp() */ -#include <libmisc/alloc.h> /* for stack_alloc() */ -#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,102 +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 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 +/* 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 - log_n_debugln(W5500_LL, - func, "(): w5500ll_write(spidev", - ", addr=", (base16_u16_, addr), - ", block=", (ctl_block, block), - ", iov", - ", iovcnt=", 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 = stack_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); -} - -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 - log_n_debugln(W5500_LL, - func, "(): w5500ll_read(spidev", - ", addr=", (base16_u16_, addr), - ", block=", (ctl_block, block), - ", iov", - ", iovcnt=", 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 = stack_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); -} +/* Register blocks. ******************************************************************************/ /* Common chip-wide registers. ***********************************************/ @@ -387,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 602168b..6f5fa34 100644 --- a/libhw_generic/CMakeLists.txt +++ b/libhw_generic/CMakeLists.txt @@ -3,8 +3,14 @@ # 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 ) diff --git a/libhw_generic/alarmclock.c b/libhw_generic/alarmclock.c index e3aaea3..3579829 100644 --- a/libhw_generic/alarmclock.c +++ b/libhw_generic/alarmclock.c @@ -7,3 +7,19 @@ #include <libhw/generic/alarmclock.h> 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 6ce2513..e7e66a1 100644 --- a/libhw_generic/include/libhw/generic/alarmclock.h +++ b/libhw_generic/include/libhw/generic/alarmclock.h @@ -7,7 +7,7 @@ #ifndef _LIBHW_GENERIC_ALARMCLOCK_H_ #define _LIBHW_GENERIC_ALARMCLOCK_H_ -#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> @@ -44,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) \ @@ -62,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 96680bb..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 <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,44 +50,82 @@ 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) /** - * Return `count` on success, -errno on error; a short write *is* an - * error. + * 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 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. @@ -77,43 +133,88 @@ LO_INTERFACE(io_reader); * 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_closer_LO_IFACE \ - LO_FUNC(int, close) -LO_INTERFACE(io_closer); -#define io_close(c) LO_CALL(c, close) +#define io_pwriter_LO_IFACE \ + LO_FUNC(size_t_and_error, pwritev, const struct wr_iovec *iov, int iovcnt, uoff_t offset) +LO_INTERFACE(io_pwriter); +#define io_pwritev(r, iov, iovcnt, off) LO_CALL(r, pwritev, iov, iovcnt, off) +#define io_pwrite(r, buf, count, off) LO_CALL(r, pwritev, &((struct wr_iovec){.iov_write_from = buf, .iov_len = count}), 1, off) -/** - * All methods return 0 on success, -errno on error. - */ -#define io_bidi_closer_LO_IFACE \ - LO_NEST(io_closer) \ - LO_FUNC(int, close_read) \ - LO_FUNC(int, close_write) -LO_INTERFACE(io_bidi_closer); -#define io_close_read(c) LO_CALL(c, close_read) -#define io_close_write(c) LO_CALL(c, close_write) +/* readwrite =======================================================*/ /** - * Return bytes-read and bytes-written on success, -errno on error; a - * short read/write *is* an error. + * A short read/write *is* an error (like `write` methods and unlike + * `read` methods). + * + * Reads/writes are *not* guaranteed to be atomic, so if you have + * concurrent readers/writers then you should arrange for a mutex to + * protect the duplex_readwriter. * * It is invalid to call readwritev when the sum length of iovecs is 0. */ #define io_duplex_readwriter_LO_IFACE \ - LO_FUNC(void, readwritev, const struct duplex_iovec *iov, int iovcnt) + LO_FUNC(size_t_and_error, readwritev, const struct duplex_iovec *iov, int iovcnt) LO_INTERFACE(io_duplex_readwriter); - #define io_readwritev(rw, iov, iovcnt) \ LO_CALL(rw, readwritev, iov, iovcnt) +/* read then write =================================================*/ + +/** + * LO_CALL(src, pread_to, dst, src_offset, count) is functionally: + * + * size_t_and_error copy(lo_interface io_writer dst, lo_interface io_preader src, uoff_t src_offset, size_t count) { + * buf = malloc(count); + * size_t_or_error read_r = io_pread(src, buf, count, src_offset); + * if (read_r.is_err) + * return ERROR_AND(size_t, 0, read_r.err); + * size_t_and_error write_r = io_write(dst, buf, read_r.size_t); + * free(buf); + * return write_r; + * } + * + * except that it does not need to allocate a buffer. That is: It + * allows the reader to dump its data directly to the writer. This + * allows us to save a copy when the reader already has an in-memory + * buffer containing the data. + * + * The above code defines when a short read/write is an error or not. + * + * It must call writev() exactly 0-or-1 times, and if it does call it, + * then it must be called with exactly 1 iovec. + */ +#define io_preader_to_LO_IFACE \ + LO_FUNC(size_t_and_error, pread_to, lo_interface io_writer dst, uoff_t src_offset, size_t count) +LO_INTERFACE(io_preader_to); + +/* close ===========================================================*/ + +#define io_closer_LO_IFACE \ + LO_FUNC(error, close) +LO_INTERFACE(io_closer); +#define io_close(c) LO_CALL(c, close) + +#define io_bidi_closer_LO_IFACE \ + LO_NEST(io_closer) \ + LO_FUNC(error, close_read) \ + LO_FUNC(error, close_write) +LO_INTERFACE(io_bidi_closer); +#define io_close_read(c) LO_CALL(c, close_read) +#define io_close_write(c) LO_CALL(c, close_write) + /* aggregate interfaces *******************************************************/ #define io_readwriter_LO_IFACE \ diff --git a/libhw_generic/include/libhw/generic/net.h b/libhw_generic/include/libhw/generic/net.h index 573a01f..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 <stddef.h> /* for size_t */ #include <stdint.h> /* for uint{n}_t} */ -#include <sys/types.h> /* for ssize_t */ -#include <libmisc/fmt.h> #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 **************************************************************/ @@ -45,22 +31,6 @@ 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) \ @@ -79,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) \ \ @@ -91,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) \ \ @@ -126,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/include/libhw/generic/spi.h b/libhw_generic/include/libhw/generic/spi.h index 4bbf649..c0d9d91 100644 --- a/libhw_generic/include/libhw/generic/spi.h +++ b/libhw_generic/include/libhw/generic/spi.h @@ -12,10 +12,10 @@ #include <libhw/generic/io.h> enum spi_mode { - SPI_MODE_0 = 0, /* clk_polarity=0 (idle low), clk_phase=0 (sample on rise) */ - SPI_MODE_1 = 1, /* clk_polarity=0 (idle low), clk_phase=1 (sample on fall) */ - SPI_MODE_2 = 2, /* clk_polarity=1 (idle high), clk_phase=0 (sample on rise) */ - SPI_MODE_3 = 3, /* clk_polarity=1 (idle high), clk_phase=1 (sample on fall) */ + SPI_MODE_0 = 0, /* clk_polarity=0 (idle low), clk_phase=0 (sample on transition-from-idle (rise)) ; shift on clock fall and CS fall */ + SPI_MODE_1 = 1, /* clk_polarity=0 (idle low), clk_phase=1 (sample on transition-to-idle (fall)) ; shift on clock rise */ + SPI_MODE_2 = 2, /* clk_polarity=1 (idle high), clk_phase=0 (sample on transition-from-idle (fall)) ; shift on clock rise and CS fall */ + SPI_MODE_3 = 3, /* clk_polarity=1 (idle high), clk_phase=1 (sample on transition-to-idle (rise)) ; shift on clock fall */ }; /* This API assumes that an SPI frame is a multiple of 8-bits. 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 d21626e..906e6e4 100644 --- a/libhw_generic/net.c +++ b/libhw_generic/net.c @@ -40,10 +40,10 @@ void fmt_print_net_ip4_addr(lo_interface fmt_dest w, struct net_ip4_addr addr) { void fmt_print_net_eth_addr(lo_interface fmt_dest w, struct net_eth_addr addr) { fmt_print(w, - (base16_u8_, addr.octets[0]), ":", - (base16_u8_, addr.octets[1]), ":", - (base16_u8_, addr.octets[2]), ":", - (base16_u8_, addr.octets[3]), ":", - (base16_u8_, addr.octets[4]), ":", - (base16_u8_, addr.octets[5])); + (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 48407bd..7388a91 100644 --- a/libmisc/CMakeLists.txt +++ b/libmisc/CMakeLists.txt @@ -3,15 +3,28 @@ # 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 ) add_lib_test(libmisc test_assert) @@ -24,5 +37,6 @@ 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 cb3a270..dc7f250 100644 --- a/libmisc/assert.c +++ b/libmisc/assert.c @@ -11,13 +11,13 @@ #ifndef NDEBUG 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; log_errorln(file, ":", line, ":", func, "(): ", - "assertion ", (qstr, expr), " failed", + "assertion \"", (str, expr), "\" failed", msg ? ": " : "", msg ?: ""); in_fail = false; } 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 index 33788b6..00b39d1 100644 --- a/libmisc/fmt.c +++ b/libmisc/fmt.c @@ -6,12 +6,39 @@ #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); } @@ -33,7 +60,7 @@ 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, void *ptr) { +void fmt_print_ptr(lo_interface fmt_dest w, volatile const void *ptr) { LM_CAT3_(fmt_print_base16_u, __INTPTR_WIDTH__, _)(w, (uintptr_t)ptr); } @@ -44,9 +71,33 @@ void fmt_print_ptr(lo_interface fmt_dest w, void *ptr) { */ void fmt_print_qbyte(lo_interface fmt_dest w, uint8_t b) { fmt_print_byte(w, '\''); - if (' ' <= b && b <= '~') { - if (b == '\'' || 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, '\\'); @@ -67,19 +118,18 @@ void fmt_print_qmem(lo_interface fmt_dest w, const void *_str, size_t size) { fmt_print_byte(w, '"'); for (size_t pos = 0; pos < size;) { uint32_t ch; - uint8_t chlen; - 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 goto invalid_utf8; - if ((ch == 0 && chlen != 1) || pos + chlen > size) goto invalid_utf8; - for (uint8_t i = 1; i < chlen; i++) { - if ((str[pos+i] & 0b11000000) != 0b10000000) goto invalid_utf8; - ch = (ch << 6) | (str[pos+i] & 0b00111111); + 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 > 0x10FFFF) goto invalid_utf8; - if (ch == '\0' || ch == '\b' || ch == '\f' || @@ -132,15 +182,6 @@ void fmt_print_qmem(lo_interface fmt_dest w, const void *_str, size_t size) { fmt_print_byte(w, hexdig[(ch >> 0) & 0xF]); } pos += chlen; - continue; - - invalid_utf8: - /* \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++; } fmt_print_byte(w, '"'); } @@ -212,14 +253,14 @@ declare(16, 64); /* fmt_buf ********************************************************************/ -LO_IMPLEMENTATION_C(fmt_dest, struct fmt_buf, fmt_buf, static); +LO_IMPLEMENTATION_C(fmt_dest, struct fmt_buf, fmt_buf); -static void fmt_buf_putb(struct fmt_buf *buf, uint8_t b) { +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++; } -static size_t fmt_buf_tell(struct fmt_buf *buf) { +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/alloc.h b/libmisc/include/libmisc/alloc.h index afddbce..d8bbc38 100644 --- a/libmisc/include/libmisc/alloc.h +++ b/libmisc/include/libmisc/alloc.h @@ -11,6 +11,8 @@ #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; \ @@ -18,9 +20,22 @@ _ret = alloca(_size); \ memset(_ret, 0, _size); \ } \ + assert(_ret); \ _ret; \ }) -#define heap_alloc(N, TYP) ((TYP *)calloc(N, sizeof(TYP))) +#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 ccdb288..f726046 100644 --- a/libmisc/include/libmisc/assert.h +++ b/libmisc/include/libmisc/assert.h @@ -7,12 +7,19 @@ #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_no_type_limits(if (!(expr))) \ __assert_msg_fail(expr_str, __FILE__, __LINE__, __func__, msg); \ } while (0) [[noreturn]] void __assert_msg_fail(const char *expr, @@ -23,7 +30,7 @@ #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 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 index c29c085..0fb2eaf 100644 --- a/libmisc/include/libmisc/fmt.h +++ b/libmisc/include/libmisc/fmt.h @@ -9,6 +9,7 @@ #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> @@ -24,21 +25,9 @@ LO_INTERFACE(fmt_dest); /* Simple bytes. */ void fmt_print_byte(lo_interface fmt_dest w, uint8_t b); - -/* These are `static inline` so that the compiler can unroll the loops. */ -static inline 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++)); -} -static inline void fmt_print_str(lo_interface fmt_dest w, const char *str) { - while (*str) - fmt_print_byte(w, *(str++)); -} -static inline 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_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); @@ -46,6 +35,10 @@ 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); \ @@ -107,6 +100,11 @@ void fmt_print_bool(lo_interface fmt_dest w, bool b); 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 { @@ -124,16 +122,25 @@ LO_IMPLEMENTATION_H(fmt_dest, struct fmt_buf, fmt_buf); _w.len; \ }) -/* justify ********************************************************************/ +#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; \ +}) -/* *grubles about not being allowed to nest things* */ -#define _fmt_param_indirect() _fmt_param -#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__) +/* justify ********************************************************************/ #define fmt_print_ljust(w, width, fillchar, ...) do { \ size_t beg = LO_CALL(w, tell); \ - _fmt_print2(w, __VA_ARGS__); \ + fmt_print2(w, __VA_ARGS__); \ while ((LO_CALL(w, tell) - beg) < width) \ fmt_print_byte(w, fillchar); \ } while (0) @@ -141,16 +148,16 @@ LO_IMPLEMENTATION_H(fmt_dest, struct fmt_buf, fmt_buf); #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__); \ + fmt_print2(discard, __VA_ARGS__); \ while (_discard.len++ < width) \ fmt_print_byte(w, fillchar); \ - _fmt_print2(w, __VA_ARGS__); \ + 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, void *ptr); +void fmt_print_ptr(lo_interface fmt_dest w, volatile 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/log.h b/libmisc/include/libmisc/log.h index e6dfb52..c40b642 100644 --- a/libmisc/include/libmisc/log.h +++ b/libmisc/include/libmisc/log.h @@ -9,9 +9,9 @@ #include <stdint.h> /* for uint8_t */ -#include <libmisc/macro.h> -#include <libmisc/fmt.h> #include <libmisc/_intercept.h> +#include <libmisc/fmt.h> +#include <libmisc/macro.h> #ifdef NDEBUG #define _LOG_NDEBUG 1 diff --git a/libmisc/include/libmisc/macro.h b/libmisc/include/libmisc/macro.h index a2d4264..0f9fc3f 100644 --- a/libmisc/include/libmisc/macro.h +++ b/libmisc/include/libmisc/macro.h @@ -9,7 +9,9 @@ #include <libmisc/assert.h> -#define LM_FORCE_SEMICOLON static_assert(1, "force semicolon") +/* C: syntax ******************************************************************/ + +#define LM_FORCE_SEMICOLON _Static_assert(1, "force semicolon") #define LM_PARTIAL_SWITCH(VAL) \ _Pragma("GCC diagnostic push") \ @@ -17,14 +19,14 @@ switch (VAL) \ _Pragma("GCC diagnostic pop") \ -/* for function definitions */ +/* 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 *******************************************************************/ /* 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))) @@ -38,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` */ @@ -46,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 @@ -59,7 +61,7 @@ #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 @@ -70,7 +72,7 @@ #define LM_EAT(...) #define LM_EXPAND(...) __VA_ARGS__ -/* conditionals */ +/* CPP: conditionals **********************************************************/ #define LM_T xxTxx #define LM_F xxFxx @@ -82,7 +84,7 @@ #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() @@ -112,7 +114,7 @@ #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 @@ -168,4 +170,18 @@ /** 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__) +/* 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 113bc0e..65a71bc 100644 --- a/libmisc/include/libmisc/map.h +++ b/libmisc/include/libmisc/map.h @@ -64,9 +64,9 @@ 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].val.key) _k = 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); @@ -75,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].val.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); @@ -88,8 +88,8 @@ bool _map_del(struct _map *m, void *kp); */ #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.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); @@ -118,7 +118,7 @@ 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. */ diff --git a/libmisc/include/libmisc/obj.h b/libmisc/include/libmisc/obj.h index 6645db7..c00e512 100644 --- a/libmisc/include/libmisc/obj.h +++ b/libmisc/include/libmisc/obj.h @@ -50,28 +50,52 @@ }; \ LM_FOREACH_TUPLE(_ARG_iface_name##_LO_IFACE, \ _LO_IFACE_PROTO, _ARG_iface_name) \ - extern int LM_CAT2_(_HIDDEN_BOGUS_, __COUNTER__) + 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(_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_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(_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){}) @@ -95,56 +119,62 @@ (_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}`. + * `LO_IMPLEMENTATION_{H,C,STATIC}` 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. + * Either use _H and _C in the .h file and .c file respectively, or + * use _STATIC in just a .c file. * - * You must also call the LO_IMPLEMENTATION_C in a single .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(_ARG_iface_name, _ARG_impl_type, _ARG_impl_name) \ - /* Vtable. */ \ - extern const struct _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, \ - }; \ - } \ +#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 -/** - * 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 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) \ - } - -#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( _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, 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 30870bf..d0e3602 100644 --- a/libmisc/intercept.c +++ b/libmisc/intercept.c @@ -11,7 +11,7 @@ [[gnu::weak]] void __lm_putchar(unsigned char c) { - (void) putchar(c); + (void)putchar(c); } [[gnu::weak]] diff --git a/libmisc/log.c b/libmisc/log.c index da4c92e..96e9ca4 100644 --- a/libmisc/log.c +++ b/libmisc/log.c @@ -8,12 +8,11 @@ #include <libmisc/assert.h> /* for static_assert() */ -#include <libmisc/log.h> #include <libmisc/_intercept.h> +#include <libmisc/log.h> struct log_stdout {}; -LO_IMPLEMENTATION_H(fmt_dest, struct log_stdout, log_stdout); -LO_IMPLEMENTATION_C(fmt_dest, struct log_stdout, log_stdout, static); +LO_IMPLEMENTATION_STATIC(fmt_dest, struct log_stdout, log_stdout); static size_t log_bytes = 0; diff --git a/libmisc/map.c b/libmisc/map.c index cc34c16..d1b2a57 100644 --- a/libmisc/map.c +++ b/libmisc/map.c @@ -7,9 +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) 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 cdbc567..84b4d36 100644 --- a/libmisc/tests/test_assert.c +++ b/libmisc/tests/test_assert.c @@ -5,7 +5,6 @@ */ #include <setjmp.h> -#include <stdarg.h> /* for va_list, va_start(), va_end() */ #include <stdlib.h> #include <string.h> @@ -79,6 +78,10 @@ static void global_log_clear(void) { 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"); diff --git a/libmisc/tests/test_fmt.c b/libmisc/tests/test_fmt.c index 6a6eb7c..64b3b8a 100644 --- a/libmisc/tests/test_fmt.c +++ b/libmisc/tests/test_fmt.c @@ -4,6 +4,7 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ +#include <stdlib.h> /* for free() */ #include <string.h> /* for strcmp(), memcmp(), memset() */ #include <libmisc/fmt.h> @@ -62,6 +63,26 @@ int main() { 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)); @@ -70,6 +91,26 @@ int main() { 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)); @@ -166,5 +207,37 @@ int main() { 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 ee762e2..6e7cdfd 100644 --- a/libmisc/tests/test_log.c +++ b/libmisc/tests/test_log.c @@ -4,7 +4,6 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ -#include <stdarg.h> /* for va_list */ #include <stdio.h> /* for vsnprintf() */ #include <stdlib.h> /* for realloc(), free() */ #include <string.h> /* for strlen(), strcmp() */ diff --git a/libmisc/tests/test_macro.c b/libmisc/tests/test_macro.c index 5157820..6810005 100644 --- a/libmisc/tests/test_macro.c +++ b/libmisc/tests/test_macro.c @@ -178,5 +178,14 @@ int main() { free(act_suffix); } + printf("== LM_DEFAPPEND ===========================================\n"); + LM_DEFAPPEND(mylist, a); + LM_DEFAPPEND(mylist, + b); + { + const char *str = LM_STR_(mylist); + test_assert(strcmp(str, "a b") == 0); + } + return 0; } diff --git a/libmisc/tests/test_obj.c b/libmisc/tests/test_obj.c index 687ad4e..c3c6786 100644 --- a/libmisc/tests/test_obj.c +++ b/libmisc/tests/test_obj.c @@ -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/libmisc/tests/test_obj_nest.c b/libmisc/tests/test_obj_nest.c index d5e563e..b52cd7b 100644 --- a/libmisc/tests/test_obj_nest.c +++ b/libmisc/tests/test_obj_nest.c @@ -13,11 +13,11 @@ /* 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); @@ -69,5 +63,14 @@ int main() { 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 ecb1c49..1cfbd65 100644 --- a/libmisc/tests/test_rand.c +++ b/libmisc/tests/test_rand.c @@ -6,8 +6,8 @@ #include <setjmp.h> -#include <libmisc/rand.h> #include <libmisc/_intercept.h> +#include <libmisc/rand.h> #include "test.h" 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/libusb/usb_common.c b/libusb/usb_common.c index 32aa53f..4fe7dd4 100644 --- a/libusb/usb_common.c +++ b/libusb/usb_common.c @@ -4,12 +4,12 @@ * 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> @@ -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/ |