All checks were successful
Source release / source-package (push) Successful in 49s
Implement the WebSDK/nwafp.h NCP 0x2222/35 AFP subfunction 0x0b, Alloc Temporary Directory Handle, for the same conservative path-backed subset used by the current AFP Entry ID and File Information probes.\n\nThe documented request carries an AFP volume number, a base AFP Entry ID, and an optional AFP-style path. mars_nwe still has no persistent CNID/base-ID lookup, so this patch deliberately accepts only requests with a path component and rejects entry-id-only allocation as Invalid Path. For the supported smoke-test path, raw SYS:-style paths are resolved through the existing NetWare path machinery and the final handle is allocated with nw_alloc_dir_handle() as a temporary, task-scoped NetWare directory handle.\n\nThe implementation keeps the AFP namespace gate intact: without the optional Netatalk/libatalk backend, the endpoint continues to return Invalid Namespace rather than pretending that the Mac namespace exists. With the backend enabled, successful replies contain the allocated temporary directory handle and the effective-rights mask returned by the existing directory-handle table. Diagnostics include the AFP volume/base Entry ID input, path, returned handle, and rights so smoke-test output can be matched to server logs.\n\nAdd a Linux ncpfs/libncp smoke helper for the new endpoint. The helper sends the AFP 0x0b request through NWRequestSimple(), prints the returned handle and rights mask, and immediately deallocates the handle in the same connection via the normal NetWare Deallocate Directory Handle call. The README documents that these handles are connection/task-local and must not be copied into later tests or reused from server logs.\n\nTests:\n- git diff --check\n- gcc -fsyntax-only tests/linux/afp_temp_dir_handle_smoke.c with temporary local ncpfs header stubs\n\nNot run:\n- Full CMake build in this container: missing gdbm/ncpfs development headers/libraries.\n\nTODO:\n- Replace the path-backed subset with persistent CNID/base-ID lookup once the AFP metadata backend grows durable directory identity support.\n- Verify live Linux smoke cases against SYS:, SYS:PUBLIC, SYS:SYSTEM, and SYS:BURST on a mars_nwe host with ENABLE_NETATALK_LIBATALK=ON.
209 lines
6.0 KiB
C
209 lines
6.0 KiB
C
/*
|
|
* Linux smoke test for NetWare AFP Alloc Temporary Directory Handle.
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
#include <errno.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include <ncp/nwcalls.h>
|
|
#include <ncp/ncplib.h>
|
|
#include <ncp/kernel/ncp.h>
|
|
|
|
#ifndef NCPC_SUBFUNCTION
|
|
#define NCPC_SUBFUNCTION 0x10000
|
|
#endif
|
|
#ifndef NCPC_SFN
|
|
#define NCPC_SFN(FN, SFN) ((FN) | ((SFN) << 8) | NCPC_SUBFUNCTION)
|
|
#endif
|
|
|
|
#define AFP_ALLOC_TEMPORARY_DIR_HANDLE 0x0b
|
|
#define NWE_INVALID_NAMESPACE 0xbf
|
|
#define NWE_INVALID_PATH 0x9c
|
|
#define AFP_TEMP_DH_NONE 0xff
|
|
|
|
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"
|
|
"\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 --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);
|
|
}
|
|
|
|
static int parse_u32(const char *text, uint32_t *value)
|
|
{
|
|
char *end = NULL;
|
|
unsigned long v;
|
|
|
|
errno = 0;
|
|
v = strtoul(text, &end, 0);
|
|
if (errno || !end || *end || v > 0xffffffffUL)
|
|
return -1;
|
|
*value = (uint32_t)v;
|
|
return 0;
|
|
}
|
|
|
|
static void cpu_to_be32(uint32_t v, uint8_t p[4])
|
|
{
|
|
p[0] = (uint8_t)(v >> 24);
|
|
p[1] = (uint8_t)(v >> 16);
|
|
p[2] = (uint8_t)(v >> 8);
|
|
p[3] = (uint8_t)v;
|
|
}
|
|
|
|
static void deallocate_dir_handle(NWCONN_HANDLE conn, unsigned int dir_handle)
|
|
{
|
|
uint8_t rq[1];
|
|
|
|
if (dir_handle == AFP_TEMP_DH_NONE)
|
|
return;
|
|
|
|
rq[0] = (uint8_t)dir_handle;
|
|
(void)NWRequestSimple(conn, NCPC_SFN(0x16, 0x14), rq, sizeof(rq), NULL);
|
|
}
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
NWCONN_HANDLE conn;
|
|
NW_FRAGMENT reply;
|
|
long init_err = 0;
|
|
const char *path = NULL;
|
|
int allow_invalid_namespace = 0;
|
|
int allow_invalid_path = 0;
|
|
uint32_t volume_number = 0;
|
|
uint32_t base_entry_id = 0;
|
|
unsigned int allocated_dir_handle = AFP_TEMP_DH_NONE;
|
|
int i;
|
|
size_t path_len;
|
|
uint8_t request[1 + 4 + 1 + 255];
|
|
uint8_t reply_buf[2];
|
|
NWCCODE err;
|
|
|
|
if (NWCallsInit(NULL, NULL)) {
|
|
fprintf(stderr, "NWCallsInit failed\n");
|
|
return 2;
|
|
}
|
|
|
|
conn = ncp_initialize(&argc, argv, 1, &init_err);
|
|
if (!conn) {
|
|
fprintf(stderr, "ncp_initialize/login failed: %ld\n", init_err);
|
|
usage(argv[0]);
|
|
return 2;
|
|
}
|
|
|
|
for (i = 1; i < argc; i++) {
|
|
if (!strcmp(argv[i], "--allow-invalid-namespace")) {
|
|
allow_invalid_namespace = 1;
|
|
} else if (!strcmp(argv[i], "--allow-invalid-path")) {
|
|
allow_invalid_path = 1;
|
|
} else if (!strcmp(argv[i], "--volume")) {
|
|
if (++i >= argc || parse_u32(argv[i], &volume_number) ||
|
|
volume_number > 255) {
|
|
fprintf(stderr, "invalid --volume value\n");
|
|
ncp_close(conn);
|
|
return 2;
|
|
}
|
|
} else if (!strcmp(argv[i], "--entry-id")) {
|
|
if (++i >= argc || parse_u32(argv[i], &base_entry_id)) {
|
|
fprintf(stderr, "invalid --entry-id value\n");
|
|
ncp_close(conn);
|
|
return 2;
|
|
}
|
|
} else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
|
|
usage(argv[0]);
|
|
ncp_close(conn);
|
|
return 0;
|
|
} else if (!path) {
|
|
path = argv[i];
|
|
} else {
|
|
fprintf(stderr, "unexpected argument: %s\n", argv[i]);
|
|
usage(argv[0]);
|
|
ncp_close(conn);
|
|
return 2;
|
|
}
|
|
}
|
|
|
|
if (!path) {
|
|
fprintf(stderr, "missing PATH\n");
|
|
usage(argv[0]);
|
|
ncp_close(conn);
|
|
return 2;
|
|
}
|
|
|
|
path_len = strlen(path);
|
|
if (path_len > 255) {
|
|
fprintf(stderr,
|
|
"PATH is too long for AFP Alloc Temporary Directory Handle: %zu\n",
|
|
path_len);
|
|
ncp_close(conn);
|
|
return 2;
|
|
}
|
|
|
|
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);
|
|
|
|
memset(reply_buf, 0, sizeof(reply_buf));
|
|
reply.fragAddr.rw = reply_buf;
|
|
reply.fragSize = sizeof(reply_buf);
|
|
|
|
err = NWRequestSimple(conn,
|
|
NCPC_SFN(0x23, AFP_ALLOC_TEMPORARY_DIR_HANDLE),
|
|
request,
|
|
6 + path_len,
|
|
&reply);
|
|
|
|
if (((unsigned int)err & 0xff) == NWE_INVALID_NAMESPACE &&
|
|
allow_invalid_namespace) {
|
|
printf("AFP Alloc Temporary Dir Handle returned invalid namespace "
|
|
"as expected: path=%s\n", path);
|
|
ncp_close(conn);
|
|
return 0;
|
|
}
|
|
|
|
if (((unsigned int)err & 0xff) == NWE_INVALID_PATH && allow_invalid_path) {
|
|
printf("AFP Alloc Temporary Dir Handle returned invalid path "
|
|
"as expected: path=%s\n", path);
|
|
ncp_close(conn);
|
|
return 0;
|
|
}
|
|
|
|
if (err) {
|
|
fprintf(stderr,
|
|
"AFP Alloc Temporary Dir Handle failed: completion=0x%02x (%u) path=%s\n",
|
|
(unsigned int)err & 0xff, (unsigned int)err, path);
|
|
ncp_close(conn);
|
|
return 1;
|
|
}
|
|
|
|
if (reply.fragSize < 2) {
|
|
fprintf(stderr, "short AFP reply: %zu bytes\n", reply.fragSize);
|
|
ncp_close(conn);
|
|
return 1;
|
|
}
|
|
|
|
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]);
|
|
|
|
deallocate_dir_handle(conn, allocated_dir_handle);
|
|
ncp_close(conn);
|
|
return 0;
|
|
}
|