All checks were successful
Source release / source-package (push) Successful in 46s
Route NCP 0x23/0x0f AFP 2.0 Get File Information through the existing read-only AFP file-information helper. The WebSDK and nwafp.h list both AFP Get File Information and AFP 2.0 Get File Information as path/file-information queries that return the AFP file-information record used by Mac namespace clients. The already implemented 0x05 path handles SYS:-style path requests and fills the safe read-only fields from Unix stat data plus the optional libatalk Finder Info and resource-fork helpers. Use the same conservative path-backed reply for 0x0f for now. This gives Linux smoke-test coverage for the AFP 2.0 subfunction without adding entry-id-only lookup, persistent CNID mapping, write-side metadata updates, or fuller resource-fork semantics. Extend afp_file_info_smoke with --afp20 so the same SYS:, SYS:PUBLIC, SYS:SYSTEM, and SYS:BURST cases can exercise the AFP 2.0 subfunction. Update the Linux test README and TODO tracking to record that AFP 2.0 file information is covered by the same temporary fallback model. This implements only the read-only path-based subset; richer AFP 2.0 behavior remains future Mac-namespace work.
246 lines
7.3 KiB
C
246 lines
7.3 KiB
C
/*
|
|
* Linux smoke test for NetWare AFP Get File Information.
|
|
*/
|
|
|
|
#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_FILE_INFORMATION 0x05
|
|
#define AFP20_GET_FILE_INFORMATION 0x0f
|
|
#define NWE_INVALID_NAMESPACE 0xbf
|
|
#define NWE_INVALID_PATH 0x9c
|
|
#define AFP_REPLY_LEN 120
|
|
#define AFP_GET_ALL 0xffff
|
|
|
|
static void usage(const char *prog)
|
|
{
|
|
fprintf(stderr,
|
|
"Usage: %s [--afp20] [--allow-invalid-namespace] [--allow-invalid-path] "
|
|
"[--volume N] [--entry-id ID] [--request-mask MASK] [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 --afp20 -S MARS -U SUPERVISOR -P secret SYS:PUBLIC\n"
|
|
" %s --allow-invalid-namespace -S MARS SYS:PUBLIC\n"
|
|
" %s --allow-invalid-path -S MARS SYS:NO_SUCH_PATH\n",
|
|
prog, 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 uint16_t be16_to_cpu(const uint8_t p[2])
|
|
{
|
|
return ((uint16_t)p[0] << 8) | p[1];
|
|
}
|
|
|
|
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 void cpu_to_be16(uint16_t v, uint8_t p[2])
|
|
{
|
|
p[0] = (uint8_t)(v >> 8);
|
|
p[1] = (uint8_t)v;
|
|
}
|
|
|
|
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 copy_fixed_string(char *dst, size_t dstlen,
|
|
const uint8_t *src, size_t srclen)
|
|
{
|
|
size_t i;
|
|
|
|
if (!dstlen)
|
|
return;
|
|
for (i = 0; i + 1 < dstlen && i < srclen && src[i]; i++)
|
|
dst[i] = (char)src[i];
|
|
dst[i] = '\0';
|
|
}
|
|
|
|
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 entry_id = 0;
|
|
uint32_t request_mask = AFP_GET_ALL;
|
|
uint32_t afp_subfunction = AFP_GET_FILE_INFORMATION;
|
|
int i;
|
|
size_t path_len;
|
|
uint8_t request[1 + 4 + 2 + 1 + 255];
|
|
uint8_t reply_buf[AFP_REPLY_LEN];
|
|
char long_name[33];
|
|
char short_name[13];
|
|
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], "--afp20")) {
|
|
afp_subfunction = AFP20_GET_FILE_INFORMATION;
|
|
} else 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], &entry_id)) {
|
|
fprintf(stderr, "invalid --entry-id value\n");
|
|
ncp_close(conn);
|
|
return 2;
|
|
}
|
|
} else if (!strcmp(argv[i], "--request-mask")) {
|
|
if (++i >= argc || parse_u32(argv[i], &request_mask) || request_mask > 0xffff) {
|
|
fprintf(stderr, "invalid --request-mask 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 Get File Information: %zu\n", path_len);
|
|
ncp_close(conn);
|
|
return 2;
|
|
}
|
|
|
|
memset(request, 0, sizeof(request));
|
|
request[0] = (uint8_t)volume_number;
|
|
cpu_to_be32(entry_id, request + 1);
|
|
cpu_to_be16((uint16_t)request_mask, request + 5);
|
|
request[7] = (uint8_t)path_len;
|
|
memcpy(request + 8, 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_subfunction),
|
|
request,
|
|
8 + path_len,
|
|
&reply);
|
|
if (err == NWE_INVALID_NAMESPACE && allow_invalid_namespace) {
|
|
printf("AFP Get File Information subfunction=0x%02x returned invalid namespace as expected for path=%s\n",
|
|
(unsigned int)afp_subfunction, path);
|
|
ncp_close(conn);
|
|
return 0;
|
|
}
|
|
if (err == NWE_INVALID_PATH && allow_invalid_path) {
|
|
printf("AFP Get File Information subfunction=0x%02x returned invalid path as expected for path=%s\n",
|
|
(unsigned int)afp_subfunction, path);
|
|
ncp_close(conn);
|
|
return 0;
|
|
}
|
|
if (err) {
|
|
fprintf(stderr,
|
|
"AFP Get File Information subfunction=0x%02x failed: completion=0x%02x (%u) path=%s\n",
|
|
(unsigned int)afp_subfunction,
|
|
(unsigned int)err & 0xff, (unsigned int)err, path);
|
|
ncp_close(conn);
|
|
return 1;
|
|
}
|
|
|
|
if (reply.fragSize < AFP_REPLY_LEN) {
|
|
fprintf(stderr, "short AFP file-info reply: %zu bytes\n", reply.fragSize);
|
|
ncp_close(conn);
|
|
return 1;
|
|
}
|
|
|
|
copy_fixed_string(long_name, sizeof(long_name), reply_buf + 64, 32);
|
|
copy_fixed_string(short_name, sizeof(short_name), reply_buf + 100, 12);
|
|
|
|
printf("AFP File Info subfunction=0x%02x path=%s entry_id=0x%08x parent_id=0x%08x attrs=0x%04x "
|
|
"data_len=%u resource_len=%u offspring=%u long_name=%s short_name=%s rights=0x%04x\n",
|
|
(unsigned int)afp_subfunction,
|
|
path,
|
|
be32_to_cpu(reply_buf + 0),
|
|
be32_to_cpu(reply_buf + 4),
|
|
be16_to_cpu(reply_buf + 8),
|
|
be32_to_cpu(reply_buf + 10),
|
|
be32_to_cpu(reply_buf + 14),
|
|
be16_to_cpu(reply_buf + 18),
|
|
long_name,
|
|
short_name,
|
|
be16_to_cpu(reply_buf + 112));
|
|
|
|
ncp_close(conn);
|
|
return 0;
|
|
}
|