Files
mars-nwe/tests/linux/afp_entry_id_smoke.c
Mario Fetka 8887c4d7a5 tests: allocate AFP smoke handle from volume root
Resolve the volume name before allocating the temporary directory handle used by the AFP Entry ID smoke test.

ncpfs does not block the AFP endpoint. Its ncp_ns_alloc_short_dir_handle() helper sends the namespace-aware handle allocation request using the supplied directory style, volume, directory entry, and path tuple. The ncpfs tests allocate a handle for a volume root with NCP_DIRSTYLE_DIRBASE, a volume number, directory entry 0, and no encoded path, then use that handle with relative paths.

The smoke test was still trying to allocate the handle as a NOHANDLE path using a raw VOL: string. mars_nwe rejects that request before the AFP request is sent, so the test never reaches NCP 0x23/0x0c and no AFP diagnostics appear in the server log.

Look up the volume number with ncp_get_volume_number(), allocate the temporary handle as DIRBASE(volume, 0, NULL), and keep passing only the relative path to AFP Get Entry ID From Path Name.

This changes only the Linux smoke test; server AFP protocol behavior is unchanged.
2026-05-30 03:16:49 +02:00

289 lines
8.6 KiB
C

/*
* Linux smoke test for NetWare AFP Entry ID path lookup.
*
* This intentionally uses ncpfs/libncp instead of a private socket path so the
* request is sent through the same client stack that is commonly available on
* Linux mars_nwe test hosts.
*/
#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_GET_ENTRY_ID_FROM_PATH_NAME 0x0c
#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] "
"[--dir-handle N] [--raw-path] [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:LOGIN\n"
" %s --allow-invalid-namespace -S MARS SYS:LOGIN\n"
" %s --allow-invalid-path -S MARS SYS:NO_SUCH_PATH\n",
prog, prog, prog, prog);
}
static int parse_u8(const char *text, unsigned int *value)
{
char *end = NULL;
unsigned long v;
errno = 0;
v = strtoul(text, &end, 0);
if (errno || !end || *end || v > 255)
return -1;
*value = (unsigned int)v;
return 0;
}
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) |
p[3];
}
static int split_volume_path(const char *path, char *volume, size_t volume_size,
const char **relpath)
{
const char *colon = strchr(path, ':');
size_t len;
if (!colon)
return -1;
len = (size_t)(colon - path);
if (!len || len >= volume_size)
return -1;
memcpy(volume, path, len);
volume[len] = '\0';
*relpath = colon + 1;
while (**relpath == '/' || **relpath == '\\')
(*relpath)++;
return 0;
}
static NWCCODE allocate_temp_dir_handle(NWCONN_HANDLE conn, const char *volume,
unsigned int *dir_handle)
{
int volnum = 0;
NWDIR_HANDLE handle = 0;
NWVOL_NUM returned_volume = 0;
NWCCODE err;
if (!volume || !*volume)
return NWE_INVALID_PATH;
err = ncp_get_volume_number(conn, volume, &volnum);
if (err)
return err;
/*
* Allocate a short handle for the volume root using the same DIRBASE form
* as the ncpfs tests. The AFP request itself receives only the relative
* path below that handle.
*
* Passing "SYS:" as a NOHANDLE path reaches mars_nwe's path parser in a
* form it rejects before the AFP endpoint is sent. Resolve the volume
* number first, then allocate the root handle as DIRBASE(vol, 0, NULL).
*/
err = ncp_ns_alloc_short_dir_handle(conn,
NW_NS_DOS,
NCP_DIRSTYLE_DIRBASE,
(unsigned int)volnum,
0,
NULL,
0,
NCP_ALLOC_TEMPORARY,
&handle,
&returned_volume);
if (err)
return err;
*dir_handle = (unsigned int)handle;
return 0;
}
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;
const char *request_path = NULL;
unsigned int dir_handle = 0;
unsigned int allocated_dir_handle = AFP_TEMP_DH_NONE;
int allow_invalid_namespace = 0;
int allow_invalid_path = 0;
int raw_path = 0;
int i;
size_t path_len;
char volume[32];
uint8_t request[1 + 1 + 255];
uint8_t reply_buf[4];
NWCCODE err;
if (NWCallsInit(NULL, NULL)) {
fprintf(stderr, "NWCallsInit failed\n");
return 2;
}
conn = ncp_initialize(&argc, argv, 0, &init_err);
if (!conn) {
fprintf(stderr, "ncp_initialize 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], "--raw-path")) {
raw_path = 1;
} else if (!strcmp(argv[i], "--dir-handle")) {
if (++i >= argc || parse_u8(argv[i], &dir_handle)) {
fprintf(stderr, "invalid --dir-handle 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;
}
request_path = path;
if (!raw_path && dir_handle == 0 &&
split_volume_path(path, volume, sizeof(volume), &request_path) == 0) {
err = allocate_temp_dir_handle(conn, volume, &dir_handle);
if (err) {
fprintf(stderr,
"Allocate Temp Dir Handle failed: completion=0x%02x (%u) volume=%s path=%s\n",
(unsigned int)err & 0xff, (unsigned int)err, volume, path);
ncp_close(conn);
return 1;
}
allocated_dir_handle = dir_handle;
}
path_len = strlen(request_path);
if (path_len > 255) {
fprintf(stderr, "PATH is too long for AFP Get Entry ID From Path Name: %zu\n",
path_len);
deallocate_dir_handle(conn, allocated_dir_handle);
ncp_close(conn);
return 2;
}
request[0] = (uint8_t)dir_handle;
request[1] = (uint8_t)path_len;
memcpy(request + 2, request_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 (((unsigned int)err & 0xff) == NWE_INVALID_NAMESPACE && allow_invalid_namespace) {
printf("AFP Get Entry ID From Path Name returned invalid namespace "
"as expected: path=%s request_path=%s dir_handle=%u\n",
path, request_path, dir_handle);
deallocate_dir_handle(conn, allocated_dir_handle);
ncp_close(conn);
return 0;
}
if (((unsigned int)err & 0xff) == NWE_INVALID_PATH && allow_invalid_path) {
printf("AFP Get Entry ID From Path Name returned invalid path "
"as expected: path=%s request_path=%s dir_handle=%u\n",
path, request_path, dir_handle);
deallocate_dir_handle(conn, allocated_dir_handle);
ncp_close(conn);
return 0;
}
if (err) {
fprintf(stderr,
"AFP Get Entry ID From Path Name failed: completion=0x%02x (%u) path=%s request_path=%s dir_handle=%u\n",
(unsigned int)err & 0xff, (unsigned int)err, path,
request_path, dir_handle);
deallocate_dir_handle(conn, allocated_dir_handle);
ncp_close(conn);
return 1;
}
if (reply.fragSize < 4) {
fprintf(stderr, "short AFP reply: %zu bytes\n", reply.fragSize);
deallocate_dir_handle(conn, allocated_dir_handle);
ncp_close(conn);
return 1;
}
printf("AFP Entry ID path=%s request_path=%s dir_handle=%u entry_id=0x%08x (%u)\n",
path, request_path, dir_handle,
(unsigned int)be32_to_cpu(reply_buf),
(unsigned int)be32_to_cpu(reply_buf));
deallocate_dir_handle(conn, allocated_dir_handle);
ncp_close(conn);
return 0;
}