All checks were successful
Source release / source-package (push) Successful in 48s
Wire NCP 0x23/0x05 AFP Get File Information to a conservative read-only reply for SYS:-style paths. The WebSDK documents NCP 0x2222/35/05 as taking a Volume Number, AFP Entry ID, request bit map, and AFP path modifier string, and returning an AFP file information record with entry id, parent id, attributes, data and resource fork lengths, offspring count, NetWare dates, Finder Info, long and short names, owner id, access privileges, and ProDOS information. The SDK headers expose the same call as AFPGetFileInformation() and NWAFPGetFileInformation(), with the wire reply matching RECPKT_AFPFILEINFO. Resolve the supplied path through the existing mars_nwe path machinery, require the optional Netatalk/libatalk backend as for the entry-id probe, and fill the fields that can be derived safely from Unix stat data and the existing libatalk helpers. Finder Info and resource fork length are read through nwatalk when present; entry ids fall back to the existing stat-derived AFP id until persistent CNID/AppleDouble ids are implemented. Parent id and ProDOS-specific data remain zero for now. Add a Linux afp_file_info_smoke test using ncpfs/libncp so the new call can be exercised without an AppleTalk client. The test sends raw SYS:-style paths with directory handle 0, matching the verified AFP Entry ID smoke-test path. This implements only the read-only AFP file information query for path-based requests; entry-id-only lookup, persistent CNID mapping, and write-side AFP semantics remain future work.
238 lines
6.9 KiB
C
238 lines
6.9 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 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 [--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 --allow-invalid-namespace -S MARS SYS:PUBLIC\n"
|
|
" %s --allow-invalid-path -S MARS 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 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;
|
|
int i;
|
|
size_t path_len;
|
|
uint8_t request[1 + 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], "--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] = AFP_GET_FILE_INFORMATION;
|
|
request[1] = (uint8_t)volume_number;
|
|
cpu_to_be32(entry_id, request + 2);
|
|
cpu_to_be16((uint16_t)request_mask, request + 6);
|
|
request[8] = (uint8_t)path_len;
|
|
memcpy(request + 9, 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_FILE_INFORMATION),
|
|
request,
|
|
9 + path_len,
|
|
&reply);
|
|
if (err == NWE_INVALID_NAMESPACE && allow_invalid_namespace) {
|
|
printf("AFP Get File Information returned invalid namespace as expected for path=%s\n", path);
|
|
ncp_close(conn);
|
|
return 0;
|
|
}
|
|
if (err == NWE_INVALID_PATH && allow_invalid_path) {
|
|
printf("AFP Get File Information returned invalid path as expected for path=%s\n", path);
|
|
ncp_close(conn);
|
|
return 0;
|
|
}
|
|
if (err) {
|
|
fprintf(stderr,
|
|
"AFP Get File Information failed: completion=0x%02x (%u) path=%s\n",
|
|
(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 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",
|
|
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;
|
|
}
|