nwconn: implement AFP entry id from NetWare handle
All checks were successful
Source release / source-package (push) Successful in 49s

Wire NCP 0x23/0x06 AFP Get Entry ID From NetWare Handle to the existing AFP entry-id backend.

The WebSDK documents NCP 0x2222/35/06 as taking a 6-byte NetWare file handle and returning the volume number, a 32-bit AFP Entry ID, and a fork indicator. The SDK headers expose the same operation as AFPGetEntryIDFromNetWareHandle() and NWAFPGetEntryIDFromNetWareHandle().

Use the connection-local mars_nwe file handle table to map the supplied NetWare file handle back to its Unix path, require the optional libatalk backend as for the other AFP calls, and then return a libatalk Entry ID when available or the existing stat-derived fallback ID otherwise. Report the data fork for now because mars_nwe does not yet expose AFP resource-fork open semantics.

Extend the Linux AFP entry-id smoke test with --from-handle. The test opens the requested file through libncp in the same connection, sends the returned 6-byte NetWare file handle to the AFP call, and closes the file afterwards.

This implements the read-only data-fork handle-to-entry-id path; persistent CNID mapping and resource-fork handle semantics remain future work.
This commit is contained in:
Mario Fetka
2026-05-30 07:07:32 +00:00
parent 8cd0cad6fb
commit a10f256b77
2 changed files with 140 additions and 17 deletions

View File

@@ -23,8 +23,9 @@
#define NCPC_SFN(FN, SFN) ((FN) | ((SFN) << 8) | NCPC_SUBFUNCTION)
#endif
#define AFP_GET_ENTRY_ID_FROM_NAME 0x04
#define AFP_GET_ENTRY_ID_FROM_PATH_NAME 0x0c
#define AFP_GET_ENTRY_ID_FROM_NAME 0x04
#define AFP_GET_ENTRY_ID_FROM_NETWARE_HANDLE 0x06
#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
@@ -33,7 +34,7 @@ static void usage(const char *prog)
{
fprintf(stderr,
"Usage: %s [--allow-invalid-namespace] [--allow-invalid-path] "
"[--from-name] [--volume N] [--entry-id ID] "
"[--from-name] [--from-handle] [--volume N] [--entry-id ID] "
"[--dir-handle N] [--alloc-handle] [--raw-path] [ncpfs options] PATH\n"
"\n"
"ncpfs options are parsed by ncp_initialize(), for example:\n"
@@ -42,11 +43,12 @@ static void usage(const char *prog)
"Examples:\n"
" %s -S MARS -U SUPERVISOR -P secret SYS:PUBLIC\n"
" %s --from-name -S MARS -U SUPERVISOR -P secret SYS:PUBLIC\n"
" %s --from-handle -S MARS -U SUPERVISOR -P secret SYS:PUBLIC/pmdflts.ini\n"
" %s --dir-handle 2 -S MARS -U SUPERVISOR -P secret PUBLIC\n"
" %s --alloc-handle -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, prog, prog);
prog, prog, prog, prog, prog, prog, prog, prog);
}
static int parse_u8(const char *text, unsigned int *value)
@@ -194,13 +196,16 @@ int main(int argc, char **argv)
int raw_path = 1;
int alloc_handle = 0;
int from_name = 0;
int from_handle = 0;
uint32_t volume_number = 0;
uint32_t base_entry_id = 0;
struct ncp_file_info opened_file;
int opened_file_valid = 0;
int i;
size_t path_len;
char volume[32];
uint8_t request[1 + 4 + 1 + 255];
uint8_t reply_buf[4];
uint8_t reply_buf[6];
NWCCODE err;
if (NWCallsInit(NULL, NULL)) {
@@ -230,6 +235,12 @@ int main(int argc, char **argv)
allow_invalid_path = 1;
} else if (!strcmp(argv[i], "--from-name")) {
from_name = 1;
from_handle = 0;
raw_path = 1;
alloc_handle = 0;
} else if (!strcmp(argv[i], "--from-handle")) {
from_handle = 1;
from_name = 0;
raw_path = 1;
alloc_handle = 0;
} else if (!strcmp(argv[i], "--volume")) {
@@ -300,16 +311,34 @@ int main(int argc, char **argv)
allocated_dir_handle = dir_handle;
}
if (from_handle) {
memset(&opened_file, 0, sizeof(opened_file));
err = ncp_open_file(conn, 0, request_path, 0, AR_READ_ONLY, &opened_file);
if (err) {
fprintf(stderr,
"Open file failed before AFP request: completion=0x%02x (%u) path=%s request_path=%s\n",
(unsigned int)err & 0xff, (unsigned int)err, path, request_path);
deallocate_dir_handle(conn, allocated_dir_handle);
ncp_close(conn);
return 1;
}
opened_file_valid = 1;
}
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);
if (opened_file_valid)
(void)ncp_close_file(conn, opened_file.file_id);
deallocate_dir_handle(conn, allocated_dir_handle);
ncp_close(conn);
return 2;
}
if (from_name) {
if (from_handle) {
memcpy(request, opened_file.file_id, 6);
} else if (from_name) {
request[0] = (uint8_t)volume_number;
cpu_to_be32(base_entry_id, request + 1);
request[5] = (uint8_t)path_len;
@@ -325,17 +354,21 @@ int main(int argc, char **argv)
reply.fragSize = sizeof(reply_buf);
err = NWRequestSimple(conn,
NCPC_SFN(0x23, from_name
? AFP_GET_ENTRY_ID_FROM_NAME
: AFP_GET_ENTRY_ID_FROM_PATH_NAME),
NCPC_SFN(0x23, from_handle
? AFP_GET_ENTRY_ID_FROM_NETWARE_HANDLE
: (from_name
? AFP_GET_ENTRY_ID_FROM_NAME
: AFP_GET_ENTRY_ID_FROM_PATH_NAME)),
request,
(from_name ? 6 : 2) + path_len,
from_handle ? 6 : ((from_name ? 6 : 2) + path_len),
&reply);
if (((unsigned int)err & 0xff) == NWE_INVALID_NAMESPACE && allow_invalid_namespace) {
printf("AFP Get Entry ID returned invalid namespace "
"as expected: path=%s request_path=%s dir_handle=%u\n",
path, request_path, dir_handle);
if (opened_file_valid)
(void)ncp_close_file(conn, opened_file.file_id);
deallocate_dir_handle(conn, allocated_dir_handle);
ncp_close(conn);
return 0;
@@ -345,6 +378,8 @@ int main(int argc, char **argv)
printf("AFP Get Entry ID returned invalid path "
"as expected: path=%s request_path=%s dir_handle=%u\n",
path, request_path, dir_handle);
if (opened_file_valid)
(void)ncp_close_file(conn, opened_file.file_id);
deallocate_dir_handle(conn, allocated_dir_handle);
ncp_close(conn);
return 0;
@@ -355,23 +390,37 @@ int main(int argc, char **argv)
"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);
if (opened_file_valid)
(void)ncp_close_file(conn, opened_file.file_id);
deallocate_dir_handle(conn, allocated_dir_handle);
ncp_close(conn);
return 1;
}
if (reply.fragSize < 4) {
if (reply.fragSize < (from_handle ? 6 : 4)) {
fprintf(stderr, "short AFP reply: %zu bytes\n", reply.fragSize);
if (opened_file_valid)
(void)ncp_close_file(conn, opened_file.file_id);
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));
if (from_handle) {
printf("AFP Entry ID From NetWare Handle path=%s volume=%u entry_id=0x%08x (%u) fork=%u\n",
path, (unsigned int)reply_buf[0],
(unsigned int)be32_to_cpu(reply_buf + 1),
(unsigned int)be32_to_cpu(reply_buf + 1),
(unsigned int)reply_buf[5]);
} else {
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));
}
if (opened_file_valid)
(void)ncp_close_file(conn, opened_file.file_id);
deallocate_dir_handle(conn, allocated_dir_handle);
ncp_close(conn);
return 0;