From f5ec189c241f7b749213abdc7fac0c1ff92f3dde Mon Sep 17 00:00:00 2001 From: OAI Date: Sun, 31 May 2026 05:26:07 +0000 Subject: [PATCH] tests: add AFP temporary directory handle entry-id smoke --- tests/afp/README.md | 6 +- tests/afp/afp_smoke_suite.sh | 5 ++ tests/afp/afp_temp_dir_handle_smoke.c | 82 ++++++++++++++++++++++++--- 3 files changed, 82 insertions(+), 11 deletions(-) diff --git a/tests/afp/README.md b/tests/afp/README.md index 4438f16..7f5f951 100644 --- a/tests/afp/README.md +++ b/tests/afp/README.md @@ -433,7 +433,7 @@ mapping through the existing NetWare namespace identity path. ## AFP Alloc Temporary Directory Handle smoke test -`afp_temp_dir_handle_smoke` sends the WebSDK-documented NetWare AFP request: +`afp_temp_dir_handle_smoke` sends the WebSDK-documented NetWare AFP request. It can send either a path-backed request or `--entry-id-only`, where it first resolves the directory entry ID through AFP Get Entry ID From Path Name and then sends Alloc Temporary Directory Handle with `path_len=0`: ```text NCP 0x2222/35/11 AFP Alloc Temporary Directory Handle @@ -454,6 +454,7 @@ Useful smoke cases for a standard MARS-NWE `SYS` volume are: ```sh ./tests/afp/afp_temp_dir_handle_smoke -S MARS -U SUPERVISOR -P secret SYS: ./tests/afp/afp_temp_dir_handle_smoke -S MARS -U SUPERVISOR -P secret SYS:PUBLIC +./tests/afp/afp_temp_dir_handle_smoke --entry-id-only -S MARS -U SUPERVISOR -P secret SYS:PUBLIC ./tests/afp/afp_temp_dir_handle_smoke -S MARS -U SUPERVISOR -P secret SYS:SYSTEM ./tests/afp/afp_temp_dir_handle_smoke -S MARS -U SUPERVISOR -P secret SYS:BURST ``` @@ -472,7 +473,8 @@ Runtime-verified output and server diagnostic shape: ```text AFP Alloc Temporary Dir Handle path=SYS: dir_handle=2 rights=0xff -AFP Alloc Temporary Dir Handle path=SYS:PUBLIC dir_handle=2 rights=0xff +AFP Alloc Temporary Dir Handle path=SYS:PUBLIC entry_id=0x00000000 dir_handle=2 rights=0xff +AFP Alloc Temporary Dir Handle path=SYS:PUBLIC entry_id=0x00000004 dir_handle=2 rights=0xff entry-id-only AFP Alloc Temporary Dir Handle path=SYS:SYSTEM dir_handle=2 rights=0xff AFP Alloc Temporary Dir Handle path=SYS:BURST dir_handle=2 rights=0xff AFP Alloc Temporary Dir Handle: vol=0 request_vol=0 entry=0x00000000 path='SYS:' dir_handle=2 rights=0x1ff diff --git a/tests/afp/afp_smoke_suite.sh b/tests/afp/afp_smoke_suite.sh index 2a4d17f..56bf9d2 100755 --- a/tests/afp/afp_smoke_suite.sh +++ b/tests/afp/afp_smoke_suite.sh @@ -461,6 +461,11 @@ run_cmd \ "./afp_temp_dir_handle_smoke $COMMON_PRINT '$DIR_PATH'" \ "$SCRIPT_DIR/afp_temp_dir_handle_smoke" -S "$SERVER" -U "$USER_NAME" -P "$PASSWORD" "$DIR_PATH" +run_cmd \ + "AFP Alloc Temporary Directory Handle by Entry ID" \ + "./afp_temp_dir_handle_smoke --entry-id-only $COMMON_PRINT '$DIR_PATH'" \ + "$SCRIPT_DIR/afp_temp_dir_handle_smoke" --entry-id-only -S "$SERVER" -U "$USER_NAME" -P "$PASSWORD" "$DIR_PATH" + if [ -d "$UNIX_DIR_PATH" ] || [ -d "$UNIX_DIR_PATH_DOS" ]; then run_optional_cmd \ "Prepare AFP Create Directory cleanup" \ diff --git a/tests/afp/afp_temp_dir_handle_smoke.c b/tests/afp/afp_temp_dir_handle_smoke.c index ab6451b..4577cd7 100644 --- a/tests/afp/afp_temp_dir_handle_smoke.c +++ b/tests/afp/afp_temp_dir_handle_smoke.c @@ -3,8 +3,8 @@ * * This uses ncpfs/libncp so the request travels through the same NCP path as a * normal Linux requester. The server-side AFP endpoint returns a temporary - * NetWare directory handle and an effective-rights mask for a path-backed AFP - * request. + * NetWare directory handle and an effective-rights mask for either a + * path-backed AFP request or an entry-id-only request. */ #include @@ -25,6 +25,7 @@ #endif #define AFP_ALLOC_TEMPORARY_DIR_HANDLE 0x0b +#define AFP_GET_ENTRY_ID_FROM_PATH_NAME 0x0c #define NWE_INVALID_NAMESPACE 0xbf #define NWE_INVALID_PATH 0x9c #define AFP_TEMP_DH_NONE 0xff @@ -32,17 +33,19 @@ static void usage(const char *prog) { fprintf(stderr, - "Usage: %s [--allow-invalid-namespace] [--allow-invalid-path] " - "[--volume N] [--entry-id ID] [ncpfs options] PATH\n" + "Usage: %s [--entry-id-only] [--allow-invalid-namespace] " + "[--allow-invalid-path] [--volume N] [--entry-id ID] " + "[ncpfs options] PATH\n" "\n" "ncpfs options are parsed by ncp_initialize(), for example:\n" " -S SERVER -U USER -P PASSWORD -n\n" "\n" "Examples:\n" " %s -S MARS -U SUPERVISOR -P secret SYS:PUBLIC\n" + " %s --entry-id-only -S MARS -U SUPERVISOR -P secret SYS:PUBLIC\n" " %s --allow-invalid-namespace -S MARS -U SUPERVISOR -P secret SYS:PUBLIC\n" " %s --allow-invalid-path -S MARS -U SUPERVISOR -P secret SYS:NO_SUCH_PATH\n", - prog, prog, prog, prog); + prog, prog, prog, prog, prog); } static int parse_u32(const char *text, uint32_t *value) @@ -66,6 +69,46 @@ static void cpu_to_be32(uint32_t v, uint8_t p[4]) p[3] = (uint8_t)v; } +static uint32_t be32_to_cpu(const uint8_t p[4]) +{ + return ((uint32_t)p[0] << 24) | + ((uint32_t)p[1] << 16) | + ((uint32_t)p[2] << 8) | + (uint32_t)p[3]; +} + +static NWCCODE afp_get_entry_id_from_path(NWCONN_HANDLE conn, const char *path, + uint32_t *entry_id) +{ + NW_FRAGMENT reply; + uint8_t request[1 + 1 + 255]; + uint8_t reply_buf[4]; + size_t path_len = strlen(path); + NWCCODE err; + + if (path_len > 255) + return NWE_INVALID_PATH; + + request[0] = 0; /* directory handle 0, raw VOL:path */ + request[1] = (uint8_t)path_len; + memcpy(request + 2, path, path_len); + + memset(reply_buf, 0, sizeof(reply_buf)); + reply.fragAddr.rw = reply_buf; + reply.fragSize = sizeof(reply_buf); + + err = NWRequestSimple(conn, + NCPC_SFN(0x23, AFP_GET_ENTRY_ID_FROM_PATH_NAME), + request, 2 + path_len, &reply); + if (err) + return err; + if (reply.fragSize < 4) + return NWE_INVALID_PATH; + + *entry_id = be32_to_cpu(reply_buf); + return 0; +} + static void deallocate_dir_handle(NWCONN_HANDLE conn, unsigned int dir_handle) { uint8_t rq[1]; @@ -85,6 +128,7 @@ int main(int argc, char **argv) const char *path = NULL; int allow_invalid_namespace = 0; int allow_invalid_path = 0; + int entry_id_only = 0; uint32_t volume_number = 0; uint32_t base_entry_id = 0; unsigned int allocated_dir_handle = AFP_TEMP_DH_NONE; @@ -109,6 +153,8 @@ int main(int argc, char **argv) for (i = 1; i < argc; i++) { if (!strcmp(argv[i], "--allow-invalid-namespace")) { allow_invalid_namespace = 1; + } else if (!strcmp(argv[i], "--entry-id-only")) { + entry_id_only = 1; } else if (!strcmp(argv[i], "--allow-invalid-path")) { allow_invalid_path = 1; } else if (!strcmp(argv[i], "--volume")) { @@ -145,7 +191,23 @@ int main(int argc, char **argv) return 2; } - path_len = strlen(path); + if (entry_id_only && !base_entry_id) { + err = afp_get_entry_id_from_path(conn, path, &base_entry_id); + if (err) { + fprintf(stderr, + "AFP Get Entry ID From Path Name failed before entry-id-only temporary handle allocation: completion=0x%02x (%u) path=%s\n", + (unsigned int)err & 0xff, (unsigned int)err, path); + ncp_close(conn); + return 1; + } + if (!base_entry_id) { + fprintf(stderr, "AFP Get Entry ID From Path Name returned zero for %s\n", path); + ncp_close(conn); + return 1; + } + } + + path_len = entry_id_only ? 0 : strlen(path); if (path_len > 255) { fprintf(stderr, "PATH is too long for AFP Alloc Temporary Directory Handle: %zu\n", @@ -157,7 +219,8 @@ int main(int argc, char **argv) request[0] = (uint8_t)volume_number; cpu_to_be32(base_entry_id, request + 1); request[5] = (uint8_t)path_len; - memcpy(request + 6, path, path_len); + if (path_len) + memcpy(request + 6, path, path_len); memset(reply_buf, 0, sizeof(reply_buf)); reply.fragAddr.rw = reply_buf; @@ -199,8 +262,9 @@ int main(int argc, char **argv) } allocated_dir_handle = reply_buf[0]; - printf("AFP Alloc Temporary Dir Handle path=%s dir_handle=%u rights=0x%02x\n", - path, allocated_dir_handle, (unsigned int)reply_buf[1]); + printf("AFP Alloc Temporary Dir Handle path=%s entry_id=0x%08x dir_handle=%u rights=0x%02x%s\n", + path, (unsigned int)base_entry_id, allocated_dir_handle, + (unsigned int)reply_buf[1], entry_id_only ? " entry-id-only" : ""); deallocate_dir_handle(conn, allocated_dir_handle); ncp_close(conn);