Cover pre-22 NetWare compatibility calls
All checks were successful
Source release / source-package (push) Successful in 1m31s
All checks were successful
Source release / source-package (push) Successful in 1m31s
This commit is contained in:
19
ENDPOINTS.md
19
ENDPOINTS.md
@@ -152,12 +152,31 @@ MARS routes many NCP 23 selectors through `src/nwbind.c`; some FSE monitor/admin
|
||||
|
||||
### Direct file and lock NCPs
|
||||
|
||||
The WebSDK `NCP Calls` index for NetWare 1.x/2.x/3.x selectors before the
|
||||
Directory Services group (`22`) currently yields these direct or nested groups:
|
||||
|
||||
- `0x2222/03`-`0x2222/0E`: old file and logical-record log/lock/release/clear.
|
||||
- `0x2222/17/06` and `0x2222/17/10`: direct print/printer status queue helpers.
|
||||
- `0x2222/18`: Get Volume Info With Number.
|
||||
- `0x2222/20`: Get File Server Date And Time.
|
||||
- `0x2222/21/00`, `/01`, `/02`, `/03`, `/09`, `/10`, `/11`: old/new broadcast
|
||||
and broadcast-mode/message calls.
|
||||
|
||||
`tests/ncp/ncp_pre22_smoke.c` directly covers the stable old compatibility
|
||||
surface for `18`, `19`, `20`, and `21/00`-`21/03`. The lock and direct-print
|
||||
families need separate semantic tests because they depend on file-lock state or
|
||||
spool/printer backend behaviour.
|
||||
|
||||
| PDF decimal / code | Name | MARS status | Handler / notes |
|
||||
| --- | --- | --- | --- |
|
||||
| `0x2222/03` | Log File | implemented | `src/nwconn.c`; old lock/log path. |
|
||||
| `0x2222/04`, `0x2222/6A` | Lock File Set old/new | partial | Lock semantics are compatibility-level. |
|
||||
| `0x2222/05`, `0x2222/06`, `0x2222/07`, `0x2222/08` | Release/Clear File and File Set | partial | Check old-client locking behaviour. |
|
||||
| `0x2222/09`-`0x2222/0E`, `0x2222/6B`, `0x2222/6C` | Logical record log/lock/release/clear | partial | Active compatibility paths; tests needed. |
|
||||
| `0x2222/11` / `17/06`, `17/10` | Direct Print / Printer Status Queue helpers | open | `src/nwconn.c` has only a disabled documentation stub; queue printing exists elsewhere, but these direct old spool NCPs still need implementation/tests. |
|
||||
| `0x2222/12` / `18` | Get Volume Info With Number | implemented | Direct old call covered by `ncp.pre22.smoke`. |
|
||||
| `0x2222/14` / `20` | Get File Server Date And Time | implemented | Direct old call covered by `ncp.pre22.smoke`. |
|
||||
| `0x2222/15` / `21/00`-`21/03`, `21/09`-`21/11` | Message / Broadcast group | implemented | Forwarded to `src/nwbind.c`; old send/get and enable/disable covered by `ncp.pre22.smoke`. |
|
||||
| `0x2222/1A`-`0x2222/1F`, `0x2222/6D`, `0x2222/6E` | Physical record log/lock/release/clear | partial | Active compatibility paths; tests needed. |
|
||||
| `0x2222/41`-`0x2222/4D`, `0x2222/54` | Old direct file open/create/close/read/write/rename/erase | implemented/partial | Active in `src/nwconn.c`; file write now covered indirectly by quota smokes. |
|
||||
| `0x2222/4F` | Set File Extended Attributes | stub/no-op | Active compatibility stub; document request layout before changing. |
|
||||
|
||||
@@ -3501,7 +3501,7 @@ static int handle_ncp_serv(void)
|
||||
U16_TO_BE16(fsp.fsu_files, xdata->total_dirs);
|
||||
U16_TO_BE16(fsp.fsu_ffree, xdata->avail_dirs);
|
||||
if ( get_volume_options(volume) & VOL_OPTION_REMOUNT) {
|
||||
U16_TO_BE16(1, xdata->removable);
|
||||
U16_TO_BE16(0xffff, xdata->removable);
|
||||
} else {
|
||||
U16_TO_BE16(0, xdata->removable);
|
||||
}
|
||||
|
||||
@@ -47,6 +47,12 @@ builds list them, but they print `SKIP/WARN` and exit with CTest skip code 77
|
||||
unless `MARS_NWE_RUN_NETWORKED_TESTS=1` is set. This gives the test report a
|
||||
warning-like skipped entry instead of making normal offline builds fail.
|
||||
|
||||
`ncp.pre22.smoke` is the direct NetWare 1.x/2.x/3.x compatibility smoke for
|
||||
selected NCPs before the NCP 22 Directory Services group. It uses raw
|
||||
`NWRequestSimple` calls for `18` Get Volume Info With Number, `19` Get Station
|
||||
Number, `20` Get File Server Date And Time, and the old `21/00`-`21/03`
|
||||
broadcast paths.
|
||||
|
||||
Run only offline tests with:
|
||||
|
||||
```sh
|
||||
|
||||
@@ -17,6 +17,28 @@ add_executable(ncp22_directory_smoke ncp22_directory_smoke.c)
|
||||
target_include_directories(ncp22_directory_smoke PRIVATE ${NCPFS_INCLUDE_DIR})
|
||||
target_link_libraries(ncp22_directory_smoke ${NCPFS_LIBRARY})
|
||||
|
||||
add_executable(ncp_pre22_smoke ncp_pre22_smoke.c)
|
||||
target_include_directories(ncp_pre22_smoke PRIVATE ${NCPFS_INCLUDE_DIR})
|
||||
target_link_libraries(ncp_pre22_smoke ${NCPFS_LIBRARY})
|
||||
|
||||
add_test(NAME ncp.pre22.smoke
|
||||
COMMAND sh ${MARS_NWE_NETWORKED_CTEST_GATE}
|
||||
ncp.pre22.smoke
|
||||
"a running mars-nwe server, NCPFS tools, credentials, and a readable NetWare volume path"
|
||||
MARS_NWE_TEST_SERVER
|
||||
MARS_NWE_TEST_ADMIN
|
||||
MARS_NWE_TEST_PASSWORD
|
||||
--
|
||||
sh -c "exec \"\$1\" -S \"\$MARS_NWE_TEST_SERVER\" -U \"\$MARS_NWE_TEST_ADMIN\" -P \"\$MARS_NWE_TEST_PASSWORD\" \"\${MARS_NWE_TEST_NCP22_PATH:-SYS:PUBLIC}\""
|
||||
sh
|
||||
$<TARGET_FILE:ncp_pre22_smoke>
|
||||
)
|
||||
set_tests_properties(ncp.pre22.smoke PROPERTIES
|
||||
LABELS "networked;integration;ncp;pre22"
|
||||
SKIP_RETURN_CODE 77
|
||||
TIMEOUT 120
|
||||
)
|
||||
|
||||
add_test(NAME ncp22.directory.smoke
|
||||
COMMAND sh ${MARS_NWE_NETWORKED_CTEST_GATE}
|
||||
ncp22.directory.smoke
|
||||
|
||||
275
tests/ncp/ncp_pre22_smoke.c
Normal file
275
tests/ncp/ncp_pre22_smoke.c
Normal file
@@ -0,0 +1,275 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Direct network smoke test for NetWare 1.x/2.x/3.x-compatible NCPs before
|
||||
* the NCP 22 Directory Services group.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <strings.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 NWE_INVALID_PATH 0x9c
|
||||
|
||||
static void usage(const char *prog)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"Usage: %s [ncpfs options] VOL:PATH\n"
|
||||
"\n"
|
||||
"Exercises selected pre-22 NetWare 1.x/2.x/3.x-compatible NCPs:\n"
|
||||
" 18 Get Volume Info With Number\n"
|
||||
" 19 Get Station Number\n"
|
||||
" 20 Get File Server Date And Time\n"
|
||||
" 21/00 Send Broadcast Message (old)\n"
|
||||
" 21/01 Get Broadcast Message (old)\n"
|
||||
" 21/02 Disable Broadcasts, 21/03 Enable Broadcasts\n"
|
||||
"\n"
|
||||
"Example:\n"
|
||||
" %s -S MARS -U SUPERVISOR -P secret SYS:PUBLIC\n",
|
||||
prog, prog);
|
||||
}
|
||||
|
||||
static uint16_t be16_to_cpu(const uint8_t p[2])
|
||||
{
|
||||
return ((uint16_t)p[0] << 8) | (uint16_t)p[1];
|
||||
}
|
||||
|
||||
static long get_station_number(NWCONN_HANDLE conn, uint8_t *station)
|
||||
{
|
||||
uint8_t reply_buf[3];
|
||||
NW_FRAGMENT reply;
|
||||
long err;
|
||||
|
||||
memset(reply_buf, 0, sizeof(reply_buf));
|
||||
reply.fragAddr.rw = reply_buf;
|
||||
reply.fragSize = sizeof(reply_buf);
|
||||
|
||||
err = NWRequestSimple(conn, 19, NULL, 0, &reply);
|
||||
if (err)
|
||||
return err;
|
||||
if (reply.fragSize < 1 || reply_buf[0] == 0)
|
||||
return NWE_INVALID_PATH;
|
||||
|
||||
*station = reply_buf[0];
|
||||
printf("NCP 19 station=%u reply_len=%lu\n",
|
||||
(unsigned int)*station, (unsigned long)reply.fragSize);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long check_volume_info(NWCONN_HANDLE conn, const char *path)
|
||||
{
|
||||
char volume_name[256];
|
||||
uint8_t request[1];
|
||||
uint8_t reply_buf[28];
|
||||
NW_FRAGMENT reply;
|
||||
char returned_name[17];
|
||||
int volume_number = -1;
|
||||
char *colon;
|
||||
long err;
|
||||
|
||||
snprintf(volume_name, sizeof(volume_name), "%s", path);
|
||||
colon = strchr(volume_name, ':');
|
||||
if (colon)
|
||||
*colon = '\0';
|
||||
if (!volume_name[0])
|
||||
return NWE_INVALID_PATH;
|
||||
|
||||
err = ncp_get_volume_number(conn, volume_name, &volume_number);
|
||||
if (err)
|
||||
return err;
|
||||
if (volume_number < 0 || volume_number > 255)
|
||||
return NWE_INVALID_PATH;
|
||||
|
||||
request[0] = (uint8_t)volume_number;
|
||||
memset(reply_buf, 0, sizeof(reply_buf));
|
||||
reply.fragAddr.rw = reply_buf;
|
||||
reply.fragSize = sizeof(reply_buf);
|
||||
|
||||
err = NWRequestSimple(conn, 18, request, sizeof(request), &reply);
|
||||
if (err)
|
||||
return err;
|
||||
if (reply.fragSize < sizeof(reply_buf))
|
||||
return NWE_INVALID_PATH;
|
||||
|
||||
memcpy(returned_name, reply_buf + 10, 16);
|
||||
returned_name[16] = '\0';
|
||||
printf("NCP 18 volume=%d name=%s sectors_per_cluster=%u total_clusters=%u avail_clusters=%u removable=%u\n",
|
||||
volume_number, returned_name,
|
||||
(unsigned int)be16_to_cpu(reply_buf + 0),
|
||||
(unsigned int)be16_to_cpu(reply_buf + 2),
|
||||
(unsigned int)be16_to_cpu(reply_buf + 4),
|
||||
(unsigned int)be16_to_cpu(reply_buf + 26));
|
||||
if (strncasecmp(returned_name, volume_name, strlen(volume_name)))
|
||||
return NWE_INVALID_PATH;
|
||||
if (be16_to_cpu(reply_buf + 26) != 0 &&
|
||||
be16_to_cpu(reply_buf + 26) != 0xffff)
|
||||
return NWE_INVALID_PATH;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long check_datetime(NWCONN_HANDLE conn)
|
||||
{
|
||||
uint8_t reply_buf[7];
|
||||
NW_FRAGMENT reply;
|
||||
long err;
|
||||
|
||||
memset(reply_buf, 0, sizeof(reply_buf));
|
||||
reply.fragAddr.rw = reply_buf;
|
||||
reply.fragSize = sizeof(reply_buf);
|
||||
|
||||
err = NWRequestSimple(conn, 20, NULL, 0, &reply);
|
||||
if (err)
|
||||
return err;
|
||||
if (reply.fragSize < sizeof(reply_buf))
|
||||
return NWE_INVALID_PATH;
|
||||
|
||||
printf("NCP 20 date=%u-%02u-%02u time=%02u:%02u:%02u dow=%u\n",
|
||||
1900U + (unsigned int)reply_buf[0],
|
||||
(unsigned int)reply_buf[1], (unsigned int)reply_buf[2],
|
||||
(unsigned int)reply_buf[3], (unsigned int)reply_buf[4],
|
||||
(unsigned int)reply_buf[5], (unsigned int)reply_buf[6]);
|
||||
|
||||
if (reply_buf[1] < 1 || reply_buf[1] > 12 ||
|
||||
reply_buf[2] < 1 || reply_buf[2] > 31 ||
|
||||
reply_buf[3] > 23 || reply_buf[4] > 59 ||
|
||||
reply_buf[5] > 59 || reply_buf[6] > 6)
|
||||
return NWE_INVALID_PATH;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long check_broadcasts(NWCONN_HANDLE conn, uint8_t station)
|
||||
{
|
||||
static const char message[] = "mars_nwe pre22 broadcast";
|
||||
uint8_t request[1 + 1 + sizeof(message)];
|
||||
uint8_t reply_buf[64];
|
||||
NW_FRAGMENT reply;
|
||||
long err;
|
||||
size_t message_len = strlen(message);
|
||||
|
||||
err = NWRequestSimple(conn, NCPC_SFN(21, 0x02), NULL, 0, NULL);
|
||||
if (err)
|
||||
return err;
|
||||
err = NWRequestSimple(conn, NCPC_SFN(21, 0x03), NULL, 0, NULL);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
request[0] = 1;
|
||||
request[1] = station;
|
||||
request[2] = (uint8_t)message_len;
|
||||
memcpy(request + 3, message, message_len);
|
||||
|
||||
memset(reply_buf, 0, sizeof(reply_buf));
|
||||
reply.fragAddr.rw = reply_buf;
|
||||
reply.fragSize = sizeof(reply_buf);
|
||||
|
||||
err = NWRequestSimple(conn, NCPC_SFN(21, 0x00),
|
||||
request, 3 + message_len, &reply);
|
||||
if (err)
|
||||
return err;
|
||||
if (reply.fragSize < 2 || reply_buf[0] != 1 || reply_buf[1] != 0)
|
||||
return NWE_INVALID_PATH;
|
||||
|
||||
memset(reply_buf, 0, sizeof(reply_buf));
|
||||
reply.fragAddr.rw = reply_buf;
|
||||
reply.fragSize = sizeof(reply_buf);
|
||||
|
||||
err = NWRequestSimple(conn, NCPC_SFN(21, 0x01), NULL, 0, &reply);
|
||||
if (err)
|
||||
return err;
|
||||
if (reply.fragSize < 1 + message_len ||
|
||||
reply_buf[0] != message_len ||
|
||||
memcmp(reply_buf + 1, message, message_len))
|
||||
return NWE_INVALID_PATH;
|
||||
|
||||
printf("NCP 21/00+01 old broadcast roundtrip station=%u message=%s\n",
|
||||
(unsigned int)station, message);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
NWCONN_HANDLE conn;
|
||||
long init_err = 0;
|
||||
const char *path = NULL;
|
||||
uint8_t station = 0;
|
||||
long err;
|
||||
int i;
|
||||
|
||||
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], "-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 || !strchr(path, ':')) {
|
||||
usage(argv[0]);
|
||||
ncp_close(conn);
|
||||
return 2;
|
||||
}
|
||||
|
||||
err = check_volume_info(conn, path);
|
||||
if (err) {
|
||||
fprintf(stderr, "NCP 18 failed: path=%s error=0x%04x\n",
|
||||
path, (unsigned int)err);
|
||||
ncp_close(conn);
|
||||
return 1;
|
||||
}
|
||||
|
||||
err = get_station_number(conn, &station);
|
||||
if (err) {
|
||||
fprintf(stderr, "NCP 19 failed: error=0x%04x\n", (unsigned int)err);
|
||||
ncp_close(conn);
|
||||
return 1;
|
||||
}
|
||||
|
||||
err = check_datetime(conn);
|
||||
if (err) {
|
||||
fprintf(stderr, "NCP 20 failed: error=0x%04x\n", (unsigned int)err);
|
||||
ncp_close(conn);
|
||||
return 1;
|
||||
}
|
||||
|
||||
err = check_broadcasts(conn, station);
|
||||
if (err) {
|
||||
fprintf(stderr, "NCP 21 broadcast check failed: error=0x%04x\n",
|
||||
(unsigned int)err);
|
||||
ncp_close(conn);
|
||||
return 1;
|
||||
}
|
||||
|
||||
ncp_close(conn);
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user