All checks were successful
Source release / source-package (push) Successful in 46s
Pass NCP_PATH_STD to ncp_ns_alloc_short_dir_handle() when the AFP Entry ID smoke test allocates a temporary directory handle for a VOL: path. The test already uses libncp to allocate the handle, but it passed the raw "SYS:" bytes together with an explicit byte length. ncpfs helpers expect NCP_PATH_STD when the caller wants libncp to encode a normal NetWare path into the component-counted wire format. Sending the raw VOL: string made the server reject the handle allocation with completion 0x98 before the AFP endpoint was reached. Keep the libncp handle allocation path, but let libncp encode the root volume path the same way the ncpfs tools do. This changes only the Linux smoke test; server AFP protocol behavior is unchanged.
291 lines
8.8 KiB
C
291 lines
8.8 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)
|
|
{
|
|
char root_path[64];
|
|
size_t volume_len = strlen(volume);
|
|
NWDIR_HANDLE handle = 0;
|
|
NWCCODE err;
|
|
|
|
if (!volume_len || volume_len + 1 >= sizeof(root_path))
|
|
return NWE_INVALID_PATH;
|
|
|
|
memcpy(root_path, volume, volume_len);
|
|
root_path[volume_len] = ':';
|
|
root_path[volume_len + 1] = '\0';
|
|
|
|
/*
|
|
* Use libncp's namespace-aware helper instead of hand-building the old
|
|
* Allocate Temporary Directory Handle request. This matches the way the
|
|
* ncpfs tools allocate short directory handles and lets libncp choose the
|
|
* correct request encoding for the connected server.
|
|
*
|
|
* Pass NCP_PATH_STD so libncp converts the VOL: path to NetWare's
|
|
* component-counted path format. Passing the raw "SYS:" bytes makes the
|
|
* server interpret the request payload incorrectly and returns 0x98 before
|
|
* the AFP endpoint is reached.
|
|
*/
|
|
err = ncp_ns_alloc_short_dir_handle(conn,
|
|
NW_NS_DOS,
|
|
NCP_DIRSTYLE_NOHANDLE,
|
|
0,
|
|
0,
|
|
(const unsigned char *)root_path,
|
|
NCP_PATH_STD,
|
|
NCP_ALLOC_TEMPORARY,
|
|
&handle,
|
|
NULL);
|
|
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;
|
|
}
|