tests: add AFP open file fork entry-id smoke
All checks were successful
Source release / source-package (push) Successful in 52s

This commit is contained in:
a
2026-05-30 21:28:24 +00:00
committed by Mario Fetka
parent 1cec6a64aa
commit b3a5468441
3 changed files with 115 additions and 34 deletions

View File

@@ -502,20 +502,25 @@ NCP 0x2222/35/08 AFP Open File Fork
```
mars_nwe supports raw `VOL:`-style path requests such as `SYS:` or
`HOME:` with base Entry ID zero and opens only the AFP data fork. Read and
write data-fork opens are routed through the existing NetWare file open/share
path (`nw_creat_open_file()`/`file_creat_open()`), so trustee rights, read-only
`HOME:` with base Entry ID zero and opens only the AFP data fork. It also
accepts entry-id-only data-fork opens for files whose AFP entry id is already
persisted in the `org.mars-nwe.afp.entry-id` nwatalk metadata. Read and write
data-fork opens are routed through the existing NetWare file open/share path
(`nw_creat_open_file()`/`file_creat_open()`), so trustee rights, read-only
attributes, and share-deny checks stay shared with regular NCP file opens. For
path-backed requests, mars_nwe resolves the effective NetWare volume from the
raw path prefix instead of assuming volume 0. On success the server returns the
normal six-byte NetWare file handle shape used by AFP handle APIs plus the
current data-fork length. The smoke helper immediately closes the returned
NetWare file handle in the same connection.
raw path prefix instead of assuming volume 0. Entry-id-only file opens reverse
resolve through the AFP/nwatalk metadata cache, not through DOS namespace
directory numbers. On success the server returns the normal six-byte NetWare
file handle shape used by AFP handle APIs plus the current data-fork length.
The smoke helper immediately closes the returned NetWare file handle in the
same connection.
Useful smoke cases for a standard MARS-NWE `SYS` volume are:
```sh
./tests/linux/afp_open_file_fork_smoke -S MARS -U SUPERVISOR -P secret SYS:PUBLIC/pmdflts.ini
./tests/linux/afp_open_file_fork_smoke --entry-id-only -S MARS -U SUPERVISOR -P secret SYS:PUBLIC/pmdflts.ini
./tests/linux/afp_open_file_fork_smoke -S MARS -U SUPERVISOR -P secret SYS:PUBLIC/ohlogscr.bat
```
@@ -529,6 +534,7 @@ against the standard DOS utility files produced:
```text
AFP Open File Fork path=SYS:PUBLIC/pmdflts.ini handle=1 fork=0 access=0x01 fork_len=8161
AFP Open File Fork path=SYS:PUBLIC/pmdflts.ini entry_id=0x32d8e6b2 handle=1 fork=0 access=0x01 fork_len=8161 entry-id-only
AFP Open File Fork path=SYS:PUBLIC/ohlogscr.bat handle=1 fork=0 access=0x01 fork_len=1296
```
@@ -548,12 +554,13 @@ then removed with AFP Delete:
```sh
./tests/linux/afp_create_file_smoke -S MARS -U SUPERVISOR -P secret SYS:PUBLIC/afpwrit
./tests/linux/afp_open_file_fork_smoke --access 0x02 -S MARS -U SUPERVISOR -P secret SYS:PUBLIC/afpwrit
./tests/linux/afp_open_file_fork_smoke --entry-id-only --access 0x02 -S MARS -U SUPERVISOR -P secret SYS:PUBLIC/afpwrit
./tests/linux/afp_delete_smoke -S MARS -U SUPERVISOR -P secret SYS:PUBLIC/afpwrit
```
Resource-fork opens (`--fork 1`) and Entry-ID-only open remain unsupported until
AppleDouble/resource-fork support and persistent CNID/base-ID lookup exist. The
smoke helper can assert the resource-fork rejection explicitly:
Resource-fork opens (`--fork 1`) remain unsupported until AppleDouble/resource
fork support exists. The smoke helper can assert the resource-fork rejection
explicitly:
```sh
./tests/linux/afp_open_file_fork_smoke --expect-completion 0x9c --fork 1 -S MARS -U SUPERVISOR -P secret SYS:PUBLIC/pmdflts.ini

View File

@@ -2,9 +2,9 @@
* Linux smoke test for NetWare AFP Open File Fork.
*
* This uses ncpfs/libncp so the request travels through the same NCP path as a
* normal Linux requester. mars_nwe routes path-backed AFP data-fork read and
* write opens through the normal NetWare open/share path and returns a normal
* NetWare file handle, which this helper closes before exiting.
* normal Linux requester. mars_nwe routes path-backed and entry-id-backed AFP
* data-fork read/write opens through the normal NetWare open/share path and
* returns a normal NetWare file handle, which this helper closes before exiting.
*/
#include <errno.h>
@@ -36,8 +36,8 @@ static void usage(const char *prog)
fprintf(stderr,
"Usage: %s [--allow-invalid-namespace] [--allow-invalid-path] "
"[--expect-completion CODE] [--volume N] [--entry-id ID] "
"[--fork N] [--access N] "
"[ncpfs options] PATH\n"
"[--entry-id-only] [--fork N] [--access N] "
"[ncpfs options] [PATH]\n"
"\n"
"ncpfs options are parsed by ncp_initialize(), for example:\n"
" -S SERVER -U USER -P PASSWORD -n\n"
@@ -47,8 +47,9 @@ static void usage(const char *prog)
" %s --allow-invalid-namespace -S MARS -U SUPERVISOR -P secret SYS:PUBLIC/pmdflts.ini\n"
" %s --allow-invalid-path -S MARS -U SUPERVISOR -P secret SYS:NO_SUCH_FILE\n"
" %s --access 0x02 -S MARS -U SUPERVISOR -P secret SYS:PUBLIC/testfile.dat\n"
" %s --entry-id-only -S MARS -U SUPERVISOR -P secret SYS:PUBLIC/pmdflts.ini\n"
" %s --expect-completion 0x9c --fork 1 -S MARS -U SUPERVISOR -P secret SYS:PUBLIC/pmdflts.ini\n",
prog, prog, prog, prog, prog, prog);
prog, prog, prog, prog, prog, prog, prog);
}
static int parse_u32(const char *text, uint32_t *value)
@@ -96,6 +97,40 @@ static void cpu_to_le32(uint32_t v, uint8_t p[4])
p[3] = (uint8_t)(v >> 24);
}
static NWCCODE afp_get_entry_id_from_path(NWCONN_HANDLE conn, const char *path,
uint32_t volume_number,
uint32_t *entry_id)
{
NW_FRAGMENT reply;
uint8_t request[1 + 4 + 1 + 255];
uint8_t reply_buf[4];
size_t path_len = strlen(path);
NWCCODE err;
if (path_len > 255)
return NWE_INVALID_PATH;
request[0] = (uint8_t)volume_number;
cpu_to_be32(0, request + 1);
request[5] = (uint8_t)path_len;
memcpy(request + 6, 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, 0x0c),
request, 6 + path_len, &reply);
if (err)
return err;
if (reply.fragSize < 4)
return NWE_INVALID_PATH;
*entry_id = be32_to_cpu(reply_buf);
return 0;
}
static void close_file_handle(NWCONN_HANDLE conn, uint32_t fhandle)
{
uint8_t rq[7];
@@ -114,6 +149,7 @@ int main(int argc, char **argv)
int allow_invalid_namespace = 0;
int allow_invalid_path = 0;
int expected_completion = -1;
int entry_id_only = 0;
uint32_t volume_number = 0;
uint32_t base_entry_id = 0;
uint32_t fork_indicator = AFP_DATA_FORK;
@@ -164,6 +200,9 @@ int main(int argc, char **argv)
ncp_close(conn);
return 2;
}
entry_id_only = 1;
} else if (!strcmp(argv[i], "--entry-id-only")) {
entry_id_only = 1;
} else if (!strcmp(argv[i], "--fork")) {
if (++i >= argc || parse_u32(argv[i], &fork_indicator) ||
fork_indicator > 255) {
@@ -192,14 +231,31 @@ int main(int argc, char **argv)
}
}
if (!path) {
fprintf(stderr, "missing PATH\n");
if (!path && !base_entry_id) {
fprintf(stderr, "missing PATH or --entry-id\n");
usage(argv[0]);
ncp_close(conn);
return 2;
}
path_len = strlen(path);
if (entry_id_only && !base_entry_id) {
err = afp_get_entry_id_from_path(conn, path, volume_number,
&base_entry_id);
if (err) {
fprintf(stderr,
"AFP Get Entry ID From Path Name failed before entry-id-only open: completion=0x%02x (%u) path=%s\n",
(unsigned int)err & 0xff, (unsigned int)err, path);
ncp_close(conn);
return 1;
}
if (!base_entry_id) {
fprintf(stderr, "AFP Get Entry ID From Path Name returned zero for %s\n", path);
ncp_close(conn);
return 1;
}
}
path_len = (path && !entry_id_only) ? strlen(path) : 0;
if (path_len > 255) {
fprintf(stderr, "PATH is too long for AFP Open File Fork: %zu\n", path_len);
ncp_close(conn);
@@ -211,7 +267,8 @@ int main(int argc, char **argv)
request[5] = (uint8_t)fork_indicator;
request[6] = (uint8_t)access_mode;
request[7] = (uint8_t)path_len;
memcpy(request + 8, path, path_len);
if (path_len)
memcpy(request + 8, path, path_len);
memset(reply_buf, 0, sizeof(reply_buf));
reply.fragAddr.rw = reply_buf;
@@ -225,16 +282,18 @@ int main(int argc, char **argv)
if (expected_completion >= 0) {
if (((unsigned int)err & 0xff) == (unsigned int)expected_completion) {
printf("AFP Open File Fork returned expected completion 0x%02x: path=%s fork=%u access=0x%02x\n",
expected_completion, path, (unsigned int)fork_indicator,
printf("AFP Open File Fork returned expected completion 0x%02x: path=%s entry_id=0x%08x fork=%u access=0x%02x\n",
expected_completion, path ? path : "(entry-id-only)",
(unsigned int)base_entry_id, (unsigned int)fork_indicator,
(unsigned int)access_mode);
ncp_close(conn);
return 0;
}
if (!err) {
fprintf(stderr,
"AFP Open File Fork unexpectedly succeeded: expected=0x%02x path=%s fork=%u access=0x%02x\n",
expected_completion, path, (unsigned int)fork_indicator,
"AFP Open File Fork unexpectedly succeeded: expected=0x%02x path=%s entry_id=0x%08x fork=%u access=0x%02x\n",
expected_completion, path ? path : "(entry-id-only)",
(unsigned int)base_entry_id, (unsigned int)fork_indicator,
(unsigned int)access_mode);
if (reply.fragSize >= 6)
close_file_handle(conn, le32_to_cpu(reply_buf + 2));
@@ -242,8 +301,9 @@ int main(int argc, char **argv)
return 1;
}
fprintf(stderr,
"AFP Open File Fork returned completion 0x%02x, expected 0x%02x: path=%s fork=%u access=0x%02x\n",
(unsigned int)err & 0xff, expected_completion, path,
"AFP Open File Fork returned completion 0x%02x, expected 0x%02x: path=%s entry_id=0x%08x fork=%u access=0x%02x\n",
(unsigned int)err & 0xff, expected_completion,
path ? path : "(entry-id-only)", (unsigned int)base_entry_id,
(unsigned int)fork_indicator, (unsigned int)access_mode);
ncp_close(conn);
return 1;
@@ -251,22 +311,24 @@ int main(int argc, char **argv)
if (((unsigned int)err & 0xff) == NWE_INVALID_NAMESPACE &&
allow_invalid_namespace) {
printf("AFP Open File Fork returned invalid namespace as expected: path=%s\n",
path);
printf("AFP Open File Fork returned invalid namespace as expected: path=%s entry_id=0x%08x\n",
path ? path : "(entry-id-only)", (unsigned int)base_entry_id);
ncp_close(conn);
return 0;
}
if (((unsigned int)err & 0xff) == NWE_INVALID_PATH && allow_invalid_path) {
printf("AFP Open File Fork returned invalid path as expected: path=%s\n", path);
printf("AFP Open File Fork returned invalid path as expected: path=%s entry_id=0x%08x\n",
path ? path : "(entry-id-only)", (unsigned int)base_entry_id);
ncp_close(conn);
return 0;
}
if (err) {
fprintf(stderr,
"AFP Open File Fork failed: completion=0x%02x (%u) path=%s\n",
(unsigned int)err & 0xff, (unsigned int)err, path);
"AFP Open File Fork failed: completion=0x%02x (%u) path=%s entry_id=0x%08x\n",
(unsigned int)err & 0xff, (unsigned int)err,
path ? path : "(entry-id-only)", (unsigned int)base_entry_id);
ncp_close(conn);
return 1;
}
@@ -279,9 +341,11 @@ int main(int argc, char **argv)
fhandle = le32_to_cpu(reply_buf + 2);
fork_len = be32_to_cpu(reply_buf + 6);
printf("AFP Open File Fork path=%s handle=%u fork=%u access=0x%02x fork_len=%u\n",
path, fhandle, (unsigned int)fork_indicator,
(unsigned int)access_mode, fork_len);
printf("AFP Open File Fork path=%s entry_id=0x%08x handle=%u fork=%u access=0x%02x fork_len=%u%s\n",
path ? path : "(entry-id-only)", (unsigned int)base_entry_id,
fhandle, (unsigned int)fork_indicator,
(unsigned int)access_mode, fork_len,
entry_id_only ? " entry-id-only" : "");
close_file_handle(conn, fhandle);
ncp_close(conn);

View File

@@ -576,6 +576,11 @@ run_cmd \
"AFP Open File Fork" \
"./afp_open_file_fork_smoke $COMMON_PRINT '$NETWARE_PATH'" \
"$SCRIPT_DIR/afp_open_file_fork_smoke" -S "$SERVER" -U "$USER_NAME" -P "$PASSWORD" "$NETWARE_PATH"
run_cmd \
"AFP Open File Fork by Entry ID" \
"./afp_open_file_fork_smoke --entry-id-only $COMMON_PRINT '$NETWARE_PATH'" \
"$SCRIPT_DIR/afp_open_file_fork_smoke" --entry-id-only \
-S "$SERVER" -U "$USER_NAME" -P "$PASSWORD" "$NETWARE_PATH"
run_optional_cmd \
"Prepare AFP Open File Fork write cleanup" \
@@ -590,6 +595,11 @@ run_cmd \
"./afp_open_file_fork_smoke --access 0x02 $COMMON_PRINT '$CREATE_FILE_PATH'" \
"$SCRIPT_DIR/afp_open_file_fork_smoke" --access 0x02 \
-S "$SERVER" -U "$USER_NAME" -P "$PASSWORD" "$CREATE_FILE_PATH"
run_cmd \
"AFP Open File Fork write access by Entry ID" \
"./afp_open_file_fork_smoke --entry-id-only --access 0x02 $COMMON_PRINT '$CREATE_FILE_PATH'" \
"$SCRIPT_DIR/afp_open_file_fork_smoke" --entry-id-only --access 0x02 \
-S "$SERVER" -U "$USER_NAME" -P "$PASSWORD" "$CREATE_FILE_PATH"
run_cmd \
"AFP Open File Fork write cleanup" \
"./afp_delete_smoke $COMMON_PRINT '$CREATE_FILE_PATH'" \