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

@@ -450,6 +450,20 @@ static int afp_request_offset(void)
return(0);
}
static int afp_volume_from_unixname(const char *unixname)
{
int k;
if (!unixname) return(-1);
for (k = 0; k < used_nw_volumes; k++) {
int len = nw_volumes[k].unixnamlen;
if (len > 0 && !strncmp(unixname, (char *)nw_volumes[k].unixname, len))
return(k);
}
return(-1);
}
static uint32 afp_fallback_entry_id(int volume, const struct stat *stb)
/*
* Build a stable local AFP entry id from Unix identity data when libatalk has
@@ -544,6 +558,59 @@ static int afp_get_entry_id_from_name(uint8 *afp_req, int afp_len,
return(4);
}
static int afp_get_entry_id_from_netware_handle(uint8 *afp_req, int afp_len,
uint8 *response)
{
uint32 fhandle;
uint8 *unixname;
int volume;
struct stat stbuff;
uint32 entry_id = 0;
int result;
if (afp_len < 7) {
XDPRINTF((2,0, "AFP Get Entry ID From NetWare Handle rejected: short request len=%d",
afp_len));
return(-0x7e); /* NCP Boundary Check Failed */
}
fhandle = GET_32(afp_req + 3); /* bytes 1..2 are the extended handle */
unixname = file_get_unix_name(fhandle);
if (!unixname) {
XDPRINTF((2,0, "AFP Get Entry ID From NetWare Handle rejected: bad handle=%u",
fhandle));
return(-0x88); /* Invalid File Handle */
}
if (!nwatalk_backend_available()) {
XDPRINTF((3,0, "AFP Get Entry ID From NetWare Handle rejected: libatalk backend unavailable"));
return(-0xbf); /* invalid namespace */
}
if (stat((char *)unixname, &stbuff)) {
XDPRINTF((2,0, "AFP Get Entry ID From NetWare Handle stat failed: handle=%u unix='%s' errno=%d",
fhandle, unixname, errno));
return(-0x88); /* Invalid File Handle */
}
volume = afp_volume_from_unixname((char *)unixname);
if (volume < 0) volume = 0;
result = nwatalk_get_entry_id((char *)unixname, &entry_id);
if (result < 0 || !entry_id)
entry_id = afp_fallback_entry_id(volume, &stbuff);
response[0] = (uint8)volume;
U32_TO_BE32(entry_id, response + 1);
response[5] = 0; /* data fork */
XDPRINTF((3,0, "AFP Get Entry ID From NetWare Handle: handle=%u volume=%d unix='%s' entry=0x%08x%s",
fhandle, volume, unixname, entry_id,
(result < 0) ? " fallback" : ""));
return(6);
}
static int afp_get_entry_id_from_path_name(uint8 *afp_req, int afp_len,
uint8 *response)
{
@@ -2900,8 +2967,10 @@ static int handle_ncp_serv(void)
* Get Entry ID From Name call accepts a volume/base AFP ID
* plus a modifying path string; until persistent CNID/base
* lookup exists, support the same path-backed SYS:-style
* smoke-test subset. Then expose the
* read-only AFP Get File Information query for the same
* smoke-test subset. Get Entry ID From NetWare Handle
* maps an already-open mars_nwe file handle back to its
* Unix path and returns the corresponding AFP ID. Then expose
* the read-only AFP Get File Information query for the same
* SYS:-style path inputs. AFP 2.0 Get File Information
* uses the same request/reply layout for this read-only
* path-backed subset, so route it through the same helper
@@ -2915,6 +2984,11 @@ static int handle_ncp_serv(void)
afp_len, responsedata);
if (result > -1) data_len = result;
else completition = (uint8)-result;
} else if (ufunc == 0x06) {
int result = afp_get_entry_id_from_netware_handle(afp_req,
afp_len, responsedata);
if (result > -1) data_len = result;
else completition = (uint8)-result;
} else if (ufunc == 0x0c) {
int result = afp_get_entry_id_from_path_name(afp_req,
afp_len, responsedata);

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;