From 2482c2bd99e3a1a04a92cc5049f17e00582df5d1 Mon Sep 17 00:00:00 2001 From: OpenAI Date: Sat, 30 May 2026 13:47:16 +0000 Subject: [PATCH] tests: assert unsupported AFP open fork modes Document and exercise the conservative AFP Open File Fork boundary before adding more destructive AFP write endpoints. The current NCP 0x2222/35/08 implementation intentionally supports only path-backed data-fork read opens. Resource-fork access still requires AppleDouble/resource-fork semantics, and write-open access must wait for an AFP write-fork path that preserves the existing NetWare share, lock, trustee, and file-handle behavior. Extend the Linux smoke helper with a generic expected-completion option so unsupported paths can be asserted as successful negative coverage rather than treated as ad-hoc failures. The smoke suite now checks that write-open requests return 0x84 and resource-fork opens return 0x9c while the positive data-fork read-open path remains unchanged. This keeps the currently incomplete Open File Fork semantics explicit before moving on to Create/Rename/Remove. It also gives Gitea reviewers a small incremental diff: helper option parsing, two suite probes, and README/TODO status updates only. Tests: - git diff --check - bash -n tests/linux/afp_smoke_suite.sh - gcc -Iinclude -I/mnt/data/stubs -fsyntax-only tests/linux/afp_open_file_fork_smoke.c TODO: - Implement AFP write-open semantics only after the server can route them through the existing NetWare handle/share/lock code paths. - Implement resource-fork opens only after AppleDouble/libatalk resource-fork storage semantics are available. --- TODO.md | 8 +++-- tests/linux/README.md | 29 +++++++++++++---- tests/linux/afp_open_file_fork_smoke.c | 44 ++++++++++++++++++++++++-- tests/linux/afp_smoke_suite.sh | 12 +++++++ 4 files changed, 81 insertions(+), 12 deletions(-) diff --git a/TODO.md b/TODO.md index 21b3b9a..a2d7bc3 100644 --- a/TODO.md +++ b/TODO.md @@ -254,9 +254,11 @@ Current status: `tests/linux/afp_open_file_fork_smoke` closes the returned handle in the same connection. Runtime smoke coverage is green for `SYS:PUBLIC/pmdflts.ini` (`fork_len=8161`) and `SYS:PUBLIC/ohlogscr.bat` - (`fork_len=1296`) using `fork=0` and read access `0x01`. Resource-fork - opens, write access, and Entry-ID-only lookup stay TODO until - AppleDouble/resource-fork and persistent CNID/base-ID semantics are available. + (`fork_len=1296`) using `fork=0` and read access `0x01`. The smoke suite + now also asserts that write-open requests fail with completion `0x84` and + resource-fork opens fail with completion `0x9c`, keeping the current + unsupported cases explicit until AppleDouble/resource-fork, AFP write-open, + and persistent CNID/base-ID semantics are available. - `AFP Alloc Temporary Directory Handle` is implemented for the same path-backed smoke subset. Raw `VOL:`-style paths resolve the effective NetWare volume from the path prefix instead of assuming volume 0. Linux diff --git a/tests/linux/README.md b/tests/linux/README.md index 2ee103a..2673c0a 100644 --- a/tests/linux/README.md +++ b/tests/linux/README.md @@ -354,15 +354,32 @@ AFP Open File Fork: vol=0 request_vol=0 entry=0x00000000 fork=0 access=0x01 path ``` The exact handle number is connection-local and must not be reused across -processes. The exact `fork_len` depends on the backing file contents. Resource fork -opens (`--fork 1`), write access (`--access 2`), and Entry-ID-only open remain -negative/TODO coverage until persistent CNID/base-ID lookup and AppleDouble -resource-fork semantics are available. +processes. The exact `fork_len` depends on the backing file contents. +Resource-fork opens (`--fork 1`), write access (`--access 2`), and +Entry-ID-only open remain unsupported until persistent CNID/base-ID lookup, +AppleDouble/resource-fork, and AFP write-open semantics are available. The +smoke helper can assert those conservative rejections explicitly: + +```sh +./tests/linux/afp_open_file_fork_smoke --expect-completion 0x84 --access 0x02 -S MARS -U SUPERVISOR -P secret SYS:PUBLIC/pmdflts.ini +./tests/linux/afp_open_file_fork_smoke --expect-completion 0x9c --fork 1 -S MARS -U SUPERVISOR -P secret SYS:PUBLIC/pmdflts.ini +``` + +A rejected write-open probe should print: + +```text +AFP Open File Fork returned expected completion 0x84: path=SYS:PUBLIC/pmdflts.ini fork=0 access=0x02 +``` + +A rejected resource-fork probe should print: + +```text +AFP Open File Fork returned expected completion 0x9c: path=SYS:PUBLIC/pmdflts.ini fork=1 access=0x01 +``` If the server was built without the optional Netatalk/libatalk backend, use `--allow-invalid-namespace` for the expected negative test. Use -`--allow-invalid-path` for path-resolution, resource-fork, or Entry-ID-only -negative tests. +`--allow-invalid-path` for path-resolution or Entry-ID-only negative tests. ## AFP File Information smoke test diff --git a/tests/linux/afp_open_file_fork_smoke.c b/tests/linux/afp_open_file_fork_smoke.c index 0b8582a..48218e1 100644 --- a/tests/linux/afp_open_file_fork_smoke.c +++ b/tests/linux/afp_open_file_fork_smoke.c @@ -34,7 +34,8 @@ static void usage(const char *prog) { fprintf(stderr, "Usage: %s [--allow-invalid-namespace] [--allow-invalid-path] " - "[--volume N] [--entry-id ID] [--fork N] [--access N] " + "[--expect-completion CODE] [--volume N] [--entry-id ID] " + "[--fork N] [--access N] " "[ncpfs options] PATH\n" "\n" "ncpfs options are parsed by ncp_initialize(), for example:\n" @@ -43,8 +44,10 @@ static void usage(const char *prog) "Examples:\n" " %s -S MARS -U SUPERVISOR -P secret SYS:PUBLIC/pmdflts.ini\n" " %s --allow-invalid-namespace -S MARS -U SUPERVISOR -P secret SYS:PUBLIC/pmdflts.ini\n" - " %s --allow-invalid-path -S MARS -U SUPERVISOR -P secret SYS:NO_SUCH_FILE\n", - prog, prog, prog, prog); + " %s --allow-invalid-path -S MARS -U SUPERVISOR -P secret SYS:NO_SUCH_FILE\n" + " %s --expect-completion 0x84 --access 0x02 -S MARS -U SUPERVISOR -P secret SYS:PUBLIC/pmdflts.ini\n" + " %s --expect-completion 0x9c --fork 1 -S MARS -U SUPERVISOR -P secret SYS:PUBLIC/pmdflts.ini\n", + prog, prog, prog, prog, prog, prog); } static int parse_u32(const char *text, uint32_t *value) @@ -109,6 +112,7 @@ int main(int argc, char **argv) const char *path = NULL; int allow_invalid_namespace = 0; int allow_invalid_path = 0; + int expected_completion = -1; uint32_t volume_number = 0; uint32_t base_entry_id = 0; uint32_t fork_indicator = AFP_DATA_FORK; @@ -138,6 +142,14 @@ int main(int argc, char **argv) allow_invalid_namespace = 1; } else if (!strcmp(argv[i], "--allow-invalid-path")) { allow_invalid_path = 1; + } else if (!strcmp(argv[i], "--expect-completion")) { + uint32_t expected; + if (++i >= argc || parse_u32(argv[i], &expected) || expected > 255) { + fprintf(stderr, "invalid --expect-completion value\n"); + ncp_close(conn); + return 2; + } + expected_completion = (int)expected; } else if (!strcmp(argv[i], "--volume")) { if (++i >= argc || parse_u32(argv[i], &volume_number) || volume_number > 255) { @@ -210,6 +222,32 @@ int main(int argc, char **argv) 8 + path_len, &reply); + if (expected_completion >= 0) { + if (((unsigned int)err & 0xff) == (unsigned int)expected_completion) { + printf("AFP Open File Fork returned expected completion 0x%02x: path=%s fork=%u access=0x%02x\n", + expected_completion, path, (unsigned int)fork_indicator, + (unsigned int)access_mode); + ncp_close(conn); + return 0; + } + if (!err) { + fprintf(stderr, + "AFP Open File Fork unexpectedly succeeded: expected=0x%02x path=%s fork=%u access=0x%02x\n", + expected_completion, path, (unsigned int)fork_indicator, + (unsigned int)access_mode); + if (reply.fragSize >= 6) + close_file_handle(conn, le32_to_cpu(reply_buf + 2)); + ncp_close(conn); + return 1; + } + fprintf(stderr, + "AFP Open File Fork returned completion 0x%02x, expected 0x%02x: path=%s fork=%u access=0x%02x\n", + (unsigned int)err & 0xff, expected_completion, path, + (unsigned int)fork_indicator, (unsigned int)access_mode); + ncp_close(conn); + return 1; + } + if (((unsigned int)err & 0xff) == NWE_INVALID_NAMESPACE && allow_invalid_namespace) { printf("AFP Open File Fork returned invalid namespace as expected: path=%s\n", diff --git a/tests/linux/afp_smoke_suite.sh b/tests/linux/afp_smoke_suite.sh index 275206d..5c81816 100755 --- a/tests/linux/afp_smoke_suite.sh +++ b/tests/linux/afp_smoke_suite.sh @@ -234,6 +234,18 @@ run_cmd \ "./afp_open_file_fork_smoke $COMMON_PRINT '$NETWARE_PATH'" \ "$SCRIPT_DIR/afp_open_file_fork_smoke" -S "$SERVER" -U "$USER_NAME" -P "$PASSWORD" "$NETWARE_PATH" +run_cmd \ + "AFP Open File Fork write access rejected" \ + "./afp_open_file_fork_smoke --expect-completion 0x84 --access 0x02 $COMMON_PRINT '$NETWARE_PATH'" \ + "$SCRIPT_DIR/afp_open_file_fork_smoke" --expect-completion 0x84 --access 0x02 \ + -S "$SERVER" -U "$USER_NAME" -P "$PASSWORD" "$NETWARE_PATH" + +run_cmd \ + "AFP Open File Fork resource fork rejected" \ + "./afp_open_file_fork_smoke --expect-completion 0x9c --fork 1 $COMMON_PRINT '$NETWARE_PATH'" \ + "$SCRIPT_DIR/afp_open_file_fork_smoke" --expect-completion 0x9c --fork 1 \ + -S "$SERVER" -U "$USER_NAME" -P "$PASSWORD" "$NETWARE_PATH" + run_cmd \ "AFP Set File Information FinderInfo" \ "./afp_set_file_info_smoke $COMMON_PRINT --finder-info-only --type '$FINDER_TYPE' --creator '$FINDER_CREATOR' '$NETWARE_PATH'" \