Add the CREATOR utility and wire it into the DOS tools build and NET command dispatch. Extend the client-side NCP helpers with the calls needed by the new and updated tools, including DOS namespace information and effective-rights queries. Also update MAP handling for automatic drive mappings used by the login script. This commit contains the tool/source support only. The DOS baseline test suite is kept separate and will be added in a follow-up commit.
2786 lines
75 KiB
C
2786 lines
75 KiB
C
/* nwtests.c - small DOS utility tests */
|
|
|
|
#include "net.h"
|
|
#include "c32ncp.h"
|
|
|
|
#define TEST_RIGHT_S 0x01
|
|
#define TEST_RIGHT_R 0x02
|
|
#define TEST_RIGHT_W 0x04
|
|
#define TEST_RIGHT_C 0x08
|
|
#define TEST_RIGHT_E 0x10
|
|
#define TEST_RIGHT_M 0x20
|
|
#define TEST_RIGHT_F 0x40
|
|
#define TEST_RIGHT_A 0x80
|
|
|
|
#define TEST_NCP_RIGHT_READ 0x0001
|
|
#define TEST_NCP_RIGHT_WRITE 0x0002
|
|
#define TEST_NCP_RIGHT_CREATE 0x0008
|
|
#define TEST_NCP_RIGHT_DELETE 0x0010
|
|
#define TEST_NCP_RIGHT_OWNER 0x0020
|
|
#define TEST_NCP_RIGHT_SEARCH 0x0040
|
|
#define TEST_NCP_RIGHT_MODIFY 0x0080
|
|
#define TEST_NCP_RIGHT_SUPER 0x0100
|
|
|
|
static int tests_same_arg(char *a, char *b)
|
|
{
|
|
while (*a || *b) {
|
|
int ca = *a++;
|
|
int cb = *b++;
|
|
if (ca >= 'a' && ca <= 'z') ca -= 32;
|
|
if (cb >= 'a' && cb <= 'z') cb -= 32;
|
|
if (ca != cb) return(0);
|
|
}
|
|
return(1);
|
|
}
|
|
|
|
static void tests_usage(void)
|
|
{
|
|
fprintf(stdout, "Usage: TESTS [NCP87C32ATTR|NCP87C32AUTO|EFFRIGHT path|NCP22S4 path [mask]|NCP23MAP path|NCP22REN old new|RENDIR parent]\n");
|
|
fprintf(stdout, " TESTS NCP2225MISS [missing-file]\n");
|
|
fprintf(stdout, " TESTS NCP2225ATTR file [attr] (current-dir 8.3 file; attr hex)\n");
|
|
fprintf(stdout, " TESTS NCP2225TIME file [date time] (DOS hex words; modifies mtime)\n");
|
|
fprintf(stdout, " TESTS NCP2225ADATE file [date] (DOS hex word; modifies access date)\n");
|
|
fprintf(stdout, " TESTS NCP2225ARCH file [date time id] (archive xattr metadata)\n");
|
|
fprintf(stdout, " TESTS NCP2225CREATE file [date time id] (create xattr metadata)\n");
|
|
fprintf(stdout, " TESTS NCP2225MODID file [id] (modifier-id xattr metadata)\n");
|
|
fprintf(stdout, " TESTS NCP2225MAXSPACE dir [blocks] (directory maximum-space/quota)\n");
|
|
fprintf(stdout, " TESTS NCP221EINFO dir (dump NCP22/1E directory info layout)\n");
|
|
fprintf(stdout, " TESTS NCP22S4 path RF|42|0x42|ALL|NONE\n");
|
|
fprintf(stdout, " TESTS NCP22REN oldpath newpath (NCP22/2E Rename Or Move old)\n");
|
|
fprintf(stdout, " TESTS RENDIR parent (rename parent\\TREN <-> parent\\TREN2)\n");
|
|
fprintf(stdout, " TESTS NCP23MAP path (probe NCP23/F4 then F3 layouts; see server debug log)\n");
|
|
}
|
|
|
|
static int tests_get_current_drive(void)
|
|
{
|
|
REGS regs;
|
|
|
|
regs.h.ah = 0x19;
|
|
int86(0x21, ®s, ®s);
|
|
return((int)regs.h.al);
|
|
}
|
|
|
|
static int tests_current_dhandle(uint8 *dhandle)
|
|
{
|
|
uint8 connid = 0;
|
|
uint8 flags = 0;
|
|
int drive;
|
|
|
|
drive = tests_get_current_drive();
|
|
if (get_drive_info((uint8)drive, &connid, dhandle, &flags))
|
|
return(-1);
|
|
|
|
if (!connid || (flags & 0x80))
|
|
return(-1);
|
|
|
|
return(0);
|
|
}
|
|
|
|
static uint8 tests_map_ncp_mask(uint16 ncp_rights)
|
|
{
|
|
uint8 mask = 0;
|
|
|
|
if (ncp_rights & TEST_NCP_RIGHT_SUPER) mask |= TEST_RIGHT_S;
|
|
if (ncp_rights & TEST_NCP_RIGHT_READ) mask |= TEST_RIGHT_R;
|
|
if (ncp_rights & TEST_NCP_RIGHT_WRITE) mask |= TEST_RIGHT_W;
|
|
if (ncp_rights & TEST_NCP_RIGHT_CREATE) mask |= TEST_RIGHT_C;
|
|
if (ncp_rights & TEST_NCP_RIGHT_DELETE) mask |= TEST_RIGHT_E;
|
|
if (ncp_rights & TEST_NCP_RIGHT_MODIFY) mask |= TEST_RIGHT_M;
|
|
if (ncp_rights & TEST_NCP_RIGHT_SEARCH) mask |= TEST_RIGHT_F;
|
|
if (ncp_rights & TEST_NCP_RIGHT_OWNER) mask |= TEST_RIGHT_A;
|
|
|
|
return(mask);
|
|
}
|
|
|
|
static void tests_mask_string(uint8 mask, char *out)
|
|
{
|
|
out[0] = (mask & TEST_RIGHT_S) ? 'S' : '-';
|
|
out[1] = (mask & TEST_RIGHT_R) ? 'R' : '-';
|
|
out[2] = (mask & TEST_RIGHT_W) ? 'W' : '-';
|
|
out[3] = (mask & TEST_RIGHT_C) ? 'C' : '-';
|
|
out[4] = (mask & TEST_RIGHT_E) ? 'E' : '-';
|
|
out[5] = (mask & TEST_RIGHT_M) ? 'M' : '-';
|
|
out[6] = (mask & TEST_RIGHT_F) ? 'F' : '-';
|
|
out[7] = (mask & TEST_RIGHT_A) ? 'A' : '-';
|
|
out[8] = '\0';
|
|
}
|
|
|
|
static void tests_parent_path(char *dst, char *src, int max)
|
|
{
|
|
char tmp[260];
|
|
char *p;
|
|
|
|
tool_upcopy(tmp, src, sizeof(tmp));
|
|
p = strrchr(tmp, '\\');
|
|
if (!p) p = strrchr(tmp, ':');
|
|
|
|
if (!p) {
|
|
dst[0] = '\0';
|
|
return;
|
|
}
|
|
|
|
if (*p == ':') {
|
|
p++;
|
|
*p = '\0';
|
|
} else {
|
|
*p = '\0';
|
|
}
|
|
|
|
strmaxcpy(dst, tmp, max - 1);
|
|
}
|
|
|
|
static void tests_print_mask(char *label, int rc, uint8 mask)
|
|
{
|
|
char s[10];
|
|
|
|
if (rc) {
|
|
fprintf(stdout, "%-18.18s rc=%d\n", label, rc);
|
|
return;
|
|
}
|
|
|
|
tests_mask_string(mask, s);
|
|
fprintf(stdout, "%-18.18s [%s] %02X\n", label, s, mask);
|
|
}
|
|
|
|
static void tests_print_eff_header(char *title)
|
|
{
|
|
if (title && *title)
|
|
fprintf(stdout, "\n%s\n", title);
|
|
fprintf(stdout, "%-18s %5s %-10s %s\n", "call", "rc", "mask", "raw");
|
|
fprintf(stdout, "%-18s %5s %-10s %s\n", "------------------", "-----", "----------", "----");
|
|
}
|
|
|
|
static void tests_print_eff_row(char *label, int rc, uint8 mask,
|
|
uint16 raw, int has_raw)
|
|
{
|
|
char s[10];
|
|
|
|
if (rc) {
|
|
fprintf(stdout, "%-18.18s %5d %-10s %s\n", label, rc, "-", "-");
|
|
return;
|
|
}
|
|
|
|
tests_mask_string(mask, s);
|
|
if (has_raw)
|
|
fprintf(stdout, "%-18.18s %5d [%s] %04X\n", label, 0, s, raw);
|
|
else
|
|
fprintf(stdout, "%-18.18s %5d [%s] %s\n", label, 0, s, "-");
|
|
}
|
|
|
|
static int tests_hex_value(int ch)
|
|
{
|
|
if (ch >= '0' && ch <= '9') return(ch - '0');
|
|
if (ch >= 'a' && ch <= 'f') return(ch - 'a' + 10);
|
|
if (ch >= 'A' && ch <= 'F') return(ch - 'A' + 10);
|
|
return(-1);
|
|
}
|
|
|
|
static int tests_parse_irm_mask(char *s, uint8 *mask_out)
|
|
{
|
|
uint16 mask = 0;
|
|
int i;
|
|
int hex = 0;
|
|
|
|
if (!s || !*s || !mask_out)
|
|
return(-1);
|
|
|
|
if (tests_same_arg(s, "ALL")) {
|
|
*mask_out = 0xff;
|
|
return(0);
|
|
}
|
|
|
|
if (tests_same_arg(s, "NONE") || tests_same_arg(s, "N")) {
|
|
*mask_out = 0x00;
|
|
return(0);
|
|
}
|
|
|
|
if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) {
|
|
if (!s[2])
|
|
return(-1);
|
|
for (i = 2; s[i]; i++) {
|
|
int v = tests_hex_value(s[i]);
|
|
if (v < 0)
|
|
return(-1);
|
|
mask = (uint16)((mask << 4) | v);
|
|
if (mask > 0xff)
|
|
return(-1);
|
|
}
|
|
*mask_out = (uint8)mask;
|
|
return(0);
|
|
}
|
|
|
|
/*
|
|
* Treat a plain one/two digit value as hexadecimal. That makes the DOS
|
|
* command natural for NetWare rights masks: "42" means R+F, not decimal 42.
|
|
*/
|
|
if (strlen(s) <= 2) {
|
|
hex = 1;
|
|
for (i = 0; s[i]; i++) {
|
|
if (s[i] < '0' || s[i] > '9') {
|
|
hex = 0;
|
|
break;
|
|
}
|
|
}
|
|
if (hex) {
|
|
for (i = 0; s[i]; i++)
|
|
mask = (uint16)((mask << 4) | tests_hex_value(s[i]));
|
|
*mask_out = (uint8)mask;
|
|
return(0);
|
|
}
|
|
}
|
|
|
|
for (i = 0; s[i]; i++) {
|
|
int ch = s[i];
|
|
|
|
if (ch >= 'a' && ch <= 'z') ch -= 32;
|
|
if (ch == ' ' || ch == ',' || ch == '+' || ch == '-' || ch == '/')
|
|
continue;
|
|
|
|
switch(ch) {
|
|
case 'S': mask |= TEST_RIGHT_S; break;
|
|
case 'R': mask |= TEST_RIGHT_R; break;
|
|
case 'W': mask |= TEST_RIGHT_W; break;
|
|
case 'C': mask |= TEST_RIGHT_C; break;
|
|
case 'E':
|
|
case 'D': mask |= TEST_RIGHT_E; break;
|
|
case 'M': mask |= TEST_RIGHT_M; break;
|
|
case 'F': mask |= TEST_RIGHT_F; break;
|
|
case 'A': mask |= TEST_RIGHT_A; break;
|
|
default: return(-1);
|
|
}
|
|
}
|
|
|
|
*mask_out = (uint8)mask;
|
|
return(0);
|
|
}
|
|
|
|
|
|
static int tests_parse_byte_arg(char *s, uint8 *value_out)
|
|
{
|
|
uint16 value = 0;
|
|
int i;
|
|
|
|
if (!s || !*s || !value_out)
|
|
return(-1);
|
|
|
|
if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) {
|
|
if (!s[2])
|
|
return(-1);
|
|
for (i = 2; s[i]; i++) {
|
|
int v = tests_hex_value(s[i]);
|
|
if (v < 0)
|
|
return(-1);
|
|
value = (uint16)((value << 4) | v);
|
|
if (value > 0xff)
|
|
return(-1);
|
|
}
|
|
*value_out = (uint8)value;
|
|
return(0);
|
|
}
|
|
|
|
/* Plain values are hexadecimal, matching the existing rights-mask parser. */
|
|
for (i = 0; s[i]; i++) {
|
|
int v = tests_hex_value(s[i]);
|
|
if (v < 0)
|
|
return(-1);
|
|
value = (uint16)((value << 4) | v);
|
|
if (value > 0xff)
|
|
return(-1);
|
|
}
|
|
|
|
*value_out = (uint8)value;
|
|
return(0);
|
|
}
|
|
|
|
|
|
static int tests_parse_word_arg(char *s, uint16 *value_out)
|
|
{
|
|
uint32 value = 0;
|
|
int i;
|
|
|
|
if (!s || !*s || !value_out)
|
|
return(-1);
|
|
|
|
if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) {
|
|
if (!s[2])
|
|
return(-1);
|
|
s += 2;
|
|
}
|
|
|
|
for (i = 0; s[i]; i++) {
|
|
int v = tests_hex_value(s[i]);
|
|
if (v < 0)
|
|
return(-1);
|
|
value = (value << 4) | (uint32)v;
|
|
if (value > 0xffffUL)
|
|
return(-1);
|
|
}
|
|
|
|
*value_out = (uint16)value;
|
|
return(0);
|
|
}
|
|
|
|
#define TEST_DM_ATTRIBUTES 0x00000002UL
|
|
#define TEST_DM_CREATE_DATE 0x00000004UL
|
|
#define TEST_DM_CREATE_TIME 0x00000008UL
|
|
#define TEST_DM_CREATOR_ID 0x00000010UL
|
|
#define TEST_DM_ARCHIVE_DATE 0x00000020UL
|
|
#define TEST_DM_ARCHIVE_TIME 0x00000040UL
|
|
#define TEST_DM_ARCHIVER_ID 0x00000080UL
|
|
#define TEST_DM_MODIFY_DATE 0x00000100UL
|
|
#define TEST_DM_MODIFY_TIME 0x00000200UL
|
|
#define TEST_DM_MODIFIER_ID 0x00000400UL
|
|
#define TEST_DM_LAST_ACCESS_DATE 0x00000800UL
|
|
#define TEST_DM_MAXIMUM_SPACE 0x00002000UL
|
|
|
|
static int tests_parse_dword_arg(char *s, uint32 *value_out)
|
|
{
|
|
uint32 value = 0;
|
|
int i;
|
|
|
|
if (!s || !*s || !value_out)
|
|
return(-1);
|
|
|
|
if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) {
|
|
if (!s[2])
|
|
return(-1);
|
|
s += 2;
|
|
}
|
|
|
|
for (i = 0; s[i]; i++) {
|
|
int v = tests_hex_value(s[i]);
|
|
if (v < 0)
|
|
return(-1);
|
|
value = (value << 4) | (uint32)v;
|
|
}
|
|
|
|
*value_out = value;
|
|
return(0);
|
|
}
|
|
|
|
|
|
static int tests_copy_ncp22_name(uint8 *dst, char *src, uint8 *len_out)
|
|
{
|
|
char tmp[260];
|
|
char *p;
|
|
int len;
|
|
|
|
if (!dst || !len_out)
|
|
return(-1);
|
|
|
|
if (!src)
|
|
src = "";
|
|
|
|
tool_upcopy(tmp, src, sizeof(tmp));
|
|
if (strchr(tmp, '\\') || strchr(tmp, '/') || strchr(tmp, ':'))
|
|
return(-1);
|
|
|
|
src = tmp;
|
|
len = strlen(src);
|
|
if (len < 1 || len > 12)
|
|
return(-1);
|
|
|
|
memcpy(dst, src, len);
|
|
*len_out = (uint8)len;
|
|
return(0);
|
|
}
|
|
|
|
static int tests_read_attr_by_ncp87(uint8 dhandle, char *path, uint8 *attr_out)
|
|
{
|
|
uint32 attr = 0;
|
|
int rc;
|
|
|
|
if (attr_out)
|
|
*attr_out = 0;
|
|
|
|
rc = c32_ncp87_obtain_rim_attributes(tool_is_current_path(path) ? "" : path,
|
|
(uint16)dhandle,
|
|
&attr,
|
|
NULL, NULL, NULL);
|
|
if (rc)
|
|
return(rc);
|
|
|
|
if (attr_out)
|
|
*attr_out = (uint8)(attr & 0xffUL);
|
|
return(0);
|
|
}
|
|
|
|
static int tests_ncp22_25_set_attr(uint8 dhandle, char *path, uint8 attr)
|
|
{
|
|
struct {
|
|
uint16 len;
|
|
uint8 func;
|
|
uint8 dirhandle;
|
|
uint8 search_attributes;
|
|
uint8 searchsequence[4];
|
|
uint8 change_bits[4];
|
|
uint8 subdir[4];
|
|
uint8 attributes[4];
|
|
uint8 uniqueid;
|
|
uint8 flags;
|
|
uint8 namespace;
|
|
uint8 namlen;
|
|
uint8 name[12];
|
|
uint8 rest[104];
|
|
} req;
|
|
struct {
|
|
uint16 len;
|
|
} repl = { 0 };
|
|
|
|
memset(&req, 0, sizeof(req));
|
|
req.func = 0x25;
|
|
req.dirhandle = dhandle;
|
|
req.search_attributes = 0x06;
|
|
U32_TO_BE32(0xffffffffUL, req.searchsequence);
|
|
U32_TO_32(TEST_DM_ATTRIBUTES, req.change_bits);
|
|
U32_TO_32((uint32)attr, req.attributes);
|
|
if (tests_copy_ncp22_name(req.name, path, &req.namlen))
|
|
return(-1);
|
|
|
|
req.len = sizeof(req) - sizeof(req.len);
|
|
|
|
neterrno = Net_Call(0xE200, &req, &repl);
|
|
if (neterrno)
|
|
return(-neterrno);
|
|
return(0);
|
|
}
|
|
|
|
static int tests_ncp2225miss(char *path)
|
|
{
|
|
uint8 dhandle = 0;
|
|
int rc;
|
|
|
|
if (!path || !*path)
|
|
path = "NO22_25.$$$";
|
|
|
|
if (tests_current_dhandle(&dhandle)) {
|
|
fprintf(stdout, "NCP2225MISS failed: current drive is not a network drive\n");
|
|
return(1);
|
|
}
|
|
|
|
rc = tests_ncp22_25_set_attr(dhandle, path, 0x20);
|
|
fprintf(stdout, "NCP2225MISS %s neterrno=0x%02X\n", path, (unsigned)(-rc & 0xff));
|
|
if (rc != -0xff) {
|
|
fprintf(stdout, "NCP2225MISS failed: expected completion 0xFF for missing entry\n");
|
|
return(1);
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
static int tests_ncp2225attr(char *path, char *attr_arg)
|
|
{
|
|
uint8 dhandle = 0;
|
|
uint8 before = 0;
|
|
uint8 target = 0;
|
|
uint8 after = 0;
|
|
uint8 restored = 0;
|
|
int rc;
|
|
int restore = 0;
|
|
|
|
if (!path || !*path) {
|
|
fprintf(stdout, "Usage: TESTS NCP2225ATTR file [attr] (file must be current-dir 8.3 name)\n");
|
|
return(1);
|
|
}
|
|
|
|
if (tests_current_dhandle(&dhandle)) {
|
|
fprintf(stdout, "NCP2225ATTR failed: current drive is not a network drive\n");
|
|
return(1);
|
|
}
|
|
|
|
rc = tests_read_attr_by_ncp87(dhandle, path, &before);
|
|
if (rc) {
|
|
fprintf(stdout, "NCP2225ATTR %s read-before rc=%d\n", path, rc);
|
|
return(rc);
|
|
}
|
|
|
|
if (attr_arg) {
|
|
if (tests_parse_byte_arg(attr_arg, &target)) {
|
|
fprintf(stdout, "Bad attr '%s'. Use e.g. 20, 21, 0x21.\n", attr_arg);
|
|
return(1);
|
|
}
|
|
} else {
|
|
target = (uint8)(before ^ 0x01); /* default: toggle read-only bit */
|
|
restore = 1;
|
|
}
|
|
|
|
fprintf(stdout, "NCP2225ATTR %s before=0x%02X target=0x%02X\n",
|
|
path, (unsigned)before, (unsigned)target);
|
|
|
|
rc = tests_ncp22_25_set_attr(dhandle, path, target);
|
|
fprintf(stdout, "ncp22/25 set attr rc=%d neterrno=0x%02X\n",
|
|
rc, (unsigned)(rc < 0 ? -rc : 0));
|
|
if (rc)
|
|
return(rc);
|
|
|
|
rc = tests_read_attr_by_ncp87(dhandle, path, &after);
|
|
if (rc) {
|
|
fprintf(stdout, "NCP2225ATTR %s read-after rc=%d\n", path, rc);
|
|
return(rc);
|
|
}
|
|
fprintf(stdout, "NCP2225ATTR %s after=0x%02X\n", path, (unsigned)after);
|
|
|
|
if (after != target) {
|
|
fprintf(stdout, "NCP2225ATTR failed: expected attr=0x%02X\n", (unsigned)target);
|
|
return(1);
|
|
}
|
|
|
|
if (restore) {
|
|
rc = tests_ncp22_25_set_attr(dhandle, path, before);
|
|
fprintf(stdout, "ncp22/25 restore attr rc=%d neterrno=0x%02X\n",
|
|
rc, (unsigned)(rc < 0 ? -rc : 0));
|
|
if (rc)
|
|
return(rc);
|
|
|
|
rc = tests_read_attr_by_ncp87(dhandle, path, &restored);
|
|
if (rc) {
|
|
fprintf(stdout, "NCP2225ATTR %s read-restore rc=%d\n", path, rc);
|
|
return(rc);
|
|
}
|
|
fprintf(stdout, "NCP2225ATTR %s restored=0x%02X\n", path, (unsigned)restored);
|
|
if (restored != before) {
|
|
fprintf(stdout, "NCP2225ATTR failed: expected restored attr=0x%02X\n",
|
|
(unsigned)before);
|
|
return(1);
|
|
}
|
|
}
|
|
|
|
return(0);
|
|
}
|
|
|
|
static int tests_ncp22_25_set_mtime(uint8 dhandle, char *path, uint16 dos_date, uint16 dos_time)
|
|
{
|
|
struct {
|
|
uint16 len;
|
|
uint8 func;
|
|
uint8 dirhandle;
|
|
uint8 search_attributes;
|
|
uint8 searchsequence[4];
|
|
uint8 change_bits[4];
|
|
uint8 subdir[4];
|
|
uint8 attributes[4];
|
|
uint8 uniqueid;
|
|
uint8 flags;
|
|
uint8 namespace;
|
|
uint8 namlen;
|
|
uint8 name[12];
|
|
uint8 created_time[2];
|
|
uint8 created_date[2];
|
|
uint8 created_id[4];
|
|
uint8 archived_time[2];
|
|
uint8 archived_date[2];
|
|
uint8 archived_id[4];
|
|
uint8 updated_time[2];
|
|
uint8 updated_date[2];
|
|
uint8 updated_id[4];
|
|
uint8 rest[80];
|
|
} req;
|
|
struct {
|
|
uint16 len;
|
|
} repl = { 0 };
|
|
|
|
memset(&req, 0, sizeof(req));
|
|
req.func = 0x25;
|
|
req.dirhandle = dhandle;
|
|
req.search_attributes = 0x06;
|
|
U32_TO_BE32(0xffffffffUL, req.searchsequence);
|
|
U32_TO_32(TEST_DM_MODIFY_DATE | TEST_DM_MODIFY_TIME, req.change_bits);
|
|
U16_TO_16(dos_time, req.updated_time);
|
|
U16_TO_16(dos_date, req.updated_date);
|
|
if (tests_copy_ncp22_name(req.name, path, &req.namlen))
|
|
return(-1);
|
|
|
|
req.len = sizeof(req) - sizeof(req.len);
|
|
|
|
neterrno = Net_Call(0xE200, &req, &repl);
|
|
if (neterrno)
|
|
return(-neterrno);
|
|
return(0);
|
|
}
|
|
|
|
|
|
|
|
static int tests_ncp22_25_set_archive(uint8 dhandle, char *path,
|
|
uint16 dos_date, uint16 dos_time,
|
|
uint32 archiver_id)
|
|
{
|
|
struct {
|
|
uint16 len;
|
|
uint8 func;
|
|
uint8 dirhandle;
|
|
uint8 search_attributes;
|
|
uint8 searchsequence[4];
|
|
uint8 change_bits[4];
|
|
uint8 subdir[4];
|
|
uint8 attributes[4];
|
|
uint8 uniqueid;
|
|
uint8 flags;
|
|
uint8 namespace;
|
|
uint8 namlen;
|
|
uint8 name[12];
|
|
uint8 created_time[2];
|
|
uint8 created_date[2];
|
|
uint8 created_id[4];
|
|
uint8 archived_time[2];
|
|
uint8 archived_date[2];
|
|
uint8 archived_id[4];
|
|
uint8 rest[88];
|
|
} req;
|
|
struct {
|
|
uint16 len;
|
|
} repl = { 0 };
|
|
|
|
memset(&req, 0, sizeof(req));
|
|
req.func = 0x25;
|
|
req.dirhandle = dhandle;
|
|
req.search_attributes = 0x06;
|
|
U32_TO_BE32(0xffffffffUL, req.searchsequence);
|
|
U32_TO_32(TEST_DM_ARCHIVE_DATE | TEST_DM_ARCHIVE_TIME | TEST_DM_ARCHIVER_ID,
|
|
req.change_bits);
|
|
U16_TO_16(dos_time, req.archived_time);
|
|
U16_TO_16(dos_date, req.archived_date);
|
|
U32_TO_BE32(archiver_id, req.archived_id);
|
|
if (tests_copy_ncp22_name(req.name, path, &req.namlen))
|
|
return(-1);
|
|
|
|
req.len = sizeof(req) - sizeof(req.len);
|
|
|
|
neterrno = Net_Call(0xE200, &req, &repl);
|
|
if (neterrno)
|
|
return(-neterrno);
|
|
return(0);
|
|
}
|
|
|
|
|
|
|
|
|
|
static int tests_ncp22_25_set_modifier(uint8 dhandle, char *path, uint32 modifier_id)
|
|
{
|
|
struct {
|
|
uint16 len;
|
|
uint8 func;
|
|
uint8 dirhandle;
|
|
uint8 search_attributes;
|
|
uint8 searchsequence[4];
|
|
uint8 change_bits[4];
|
|
uint8 subdir[4];
|
|
uint8 attributes[4];
|
|
uint8 uniqueid;
|
|
uint8 flags;
|
|
uint8 namespace;
|
|
uint8 namlen;
|
|
uint8 name[12];
|
|
uint8 created_time[2];
|
|
uint8 created_date[2];
|
|
uint8 created_id[4];
|
|
uint8 archived_time[2];
|
|
uint8 archived_date[2];
|
|
uint8 archived_id[4];
|
|
uint8 updated_time[2];
|
|
uint8 updated_date[2];
|
|
uint8 updated_id[4];
|
|
uint8 rest[80];
|
|
} req;
|
|
struct {
|
|
uint16 len;
|
|
} repl = { 0 };
|
|
|
|
memset(&req, 0, sizeof(req));
|
|
req.func = 0x25;
|
|
req.dirhandle = dhandle;
|
|
req.search_attributes = 0x06;
|
|
U32_TO_BE32(0xffffffffUL, req.searchsequence);
|
|
U32_TO_32(TEST_DM_MODIFIER_ID, req.change_bits);
|
|
U32_TO_BE32(modifier_id, req.updated_id);
|
|
if (tests_copy_ncp22_name(req.name, path, &req.namlen))
|
|
return(-1);
|
|
|
|
req.len = sizeof(req) - sizeof(req.len);
|
|
|
|
neterrno = Net_Call(0xE200, &req, &repl);
|
|
if (neterrno)
|
|
return(-neterrno);
|
|
return(0);
|
|
}
|
|
|
|
static int tests_ncp22_25_set_create(uint8 dhandle, char *path,
|
|
uint16 dos_date, uint16 dos_time,
|
|
uint32 creator_id)
|
|
{
|
|
struct {
|
|
uint16 len;
|
|
uint8 func;
|
|
uint8 dirhandle;
|
|
uint8 search_attributes;
|
|
uint8 searchsequence[4];
|
|
uint8 change_bits[4];
|
|
uint8 subdir[4];
|
|
uint8 attributes[4];
|
|
uint8 uniqueid;
|
|
uint8 flags;
|
|
uint8 namespace;
|
|
uint8 namlen;
|
|
uint8 name[12];
|
|
uint8 created_time[2];
|
|
uint8 created_date[2];
|
|
uint8 created_id[4];
|
|
uint8 rest[96];
|
|
} req;
|
|
struct {
|
|
uint16 len;
|
|
} repl = { 0 };
|
|
|
|
memset(&req, 0, sizeof(req));
|
|
req.func = 0x25;
|
|
req.dirhandle = dhandle;
|
|
req.search_attributes = 0x06;
|
|
U32_TO_BE32(0xffffffffUL, req.searchsequence);
|
|
U32_TO_32(TEST_DM_CREATE_DATE | TEST_DM_CREATE_TIME | TEST_DM_CREATOR_ID,
|
|
req.change_bits);
|
|
U16_TO_16(dos_time, req.created_time);
|
|
U16_TO_16(dos_date, req.created_date);
|
|
U32_TO_BE32(creator_id, req.created_id);
|
|
if (tests_copy_ncp22_name(req.name, path, &req.namlen))
|
|
return(-1);
|
|
|
|
req.len = sizeof(req) - sizeof(req.len);
|
|
|
|
neterrno = Net_Call(0xE200, &req, &repl);
|
|
if (neterrno)
|
|
return(-neterrno);
|
|
return(0);
|
|
}
|
|
|
|
static int tests_ncp22_25_set_adate(uint8 dhandle, char *path, uint16 dos_date)
|
|
{
|
|
struct {
|
|
uint16 len;
|
|
uint8 func;
|
|
uint8 dirhandle;
|
|
uint8 search_attributes;
|
|
uint8 searchsequence[4];
|
|
uint8 change_bits[4];
|
|
uint8 subdir[4];
|
|
uint8 attributes[4];
|
|
uint8 uniqueid;
|
|
uint8 flags;
|
|
uint8 namespace;
|
|
uint8 namlen;
|
|
uint8 name[12];
|
|
uint8 created_time[2];
|
|
uint8 created_date[2];
|
|
uint8 created_id[4];
|
|
uint8 archived_time[2];
|
|
uint8 archived_date[2];
|
|
uint8 archived_id[4];
|
|
uint8 updated_time[2];
|
|
uint8 updated_date[2];
|
|
uint8 updated_id[4];
|
|
uint8 size[4];
|
|
uint8 reserved_1[44];
|
|
uint8 inherited_rights_mask[2];
|
|
uint8 last_access_date[2];
|
|
uint8 rest[28];
|
|
} req;
|
|
struct {
|
|
uint16 len;
|
|
} repl = { 0 };
|
|
|
|
memset(&req, 0, sizeof(req));
|
|
req.func = 0x25;
|
|
req.dirhandle = dhandle;
|
|
req.search_attributes = 0x06;
|
|
U32_TO_BE32(0xffffffffUL, req.searchsequence);
|
|
U32_TO_32(TEST_DM_LAST_ACCESS_DATE, req.change_bits);
|
|
U16_TO_16(dos_date, req.last_access_date);
|
|
if (tests_copy_ncp22_name(req.name, path, &req.namlen))
|
|
return(-1);
|
|
|
|
req.len = sizeof(req) - sizeof(req.len);
|
|
|
|
neterrno = Net_Call(0xE200, &req, &repl);
|
|
if (neterrno)
|
|
return(-neterrno);
|
|
return(0);
|
|
}
|
|
|
|
|
|
static int tests_ncp22_25_set_maxspace(uint8 dhandle, char *path, uint32 max_space)
|
|
{
|
|
struct {
|
|
uint16 len;
|
|
uint8 func;
|
|
uint8 dirhandle;
|
|
uint8 search_attributes;
|
|
uint8 searchsequence[4];
|
|
uint8 change_bits[4];
|
|
uint8 subdir[4];
|
|
uint8 attributes[4];
|
|
uint8 uniqueid;
|
|
uint8 flags;
|
|
uint8 namespace;
|
|
uint8 namlen;
|
|
uint8 name[12];
|
|
uint8 created_time[2];
|
|
uint8 created_date[2];
|
|
uint8 created_id[4];
|
|
uint8 archived_time[2];
|
|
uint8 archived_date[2];
|
|
uint8 archived_id[4];
|
|
uint8 modify_time[2];
|
|
uint8 modify_date[2];
|
|
uint8 next_trustee[4];
|
|
uint8 reserved_1[48];
|
|
uint8 max_space[4];
|
|
uint8 inherited_rights_mask[2];
|
|
uint8 rest[26];
|
|
} req;
|
|
struct {
|
|
uint16 len;
|
|
} repl = { 0 };
|
|
|
|
memset(&req, 0, sizeof(req));
|
|
req.func = 0x25;
|
|
req.dirhandle = dhandle;
|
|
req.search_attributes = 0x10; /* directory */
|
|
U32_TO_BE32(0xffffffffUL, req.searchsequence);
|
|
U32_TO_32(TEST_DM_MAXIMUM_SPACE, req.change_bits);
|
|
U32_TO_32(0x10UL, req.attributes);
|
|
U32_TO_BE32(max_space, req.max_space);
|
|
if (tests_copy_ncp22_name(req.name, path, &req.namlen))
|
|
return(-1);
|
|
|
|
req.len = sizeof(req) - sizeof(req.len);
|
|
|
|
neterrno = Net_Call(0xE200, &req, &repl);
|
|
if (neterrno)
|
|
return(-neterrno);
|
|
return(0);
|
|
}
|
|
|
|
|
|
static void tests_print_dos_datetime(char *label, uint16 date, uint16 timev);
|
|
|
|
static uint16 tests_get_be16(uint8 *p)
|
|
{
|
|
return((uint16)(((uint16)p[0] << 8) | p[1]));
|
|
}
|
|
|
|
static uint16 tests_get_le16(uint8 *p)
|
|
{
|
|
return((uint16)(((uint16)p[1] << 8) | p[0]));
|
|
}
|
|
|
|
static uint32 tests_get_le32(uint8 *p)
|
|
{
|
|
return(((uint32)p[3] << 24) | ((uint32)p[2] << 16) |
|
|
((uint32)p[1] << 8) | (uint32)p[0]);
|
|
}
|
|
|
|
static int tests_ncp22_1e_read_dir_maxspace(uint8 dhandle, char *path, uint32 *max_space_out)
|
|
{
|
|
struct {
|
|
uint16 len;
|
|
uint8 func;
|
|
uint8 dirhandle;
|
|
uint8 search_attributes;
|
|
uint8 searchsequence[4];
|
|
uint8 namlen;
|
|
uint8 name[12];
|
|
} req;
|
|
struct {
|
|
uint16 len;
|
|
uint8 searchsequence[4];
|
|
uint8 subdir[4];
|
|
uint8 attributes[4];
|
|
uint8 uniqueid;
|
|
uint8 flags;
|
|
uint8 namespace;
|
|
uint8 namlen;
|
|
uint8 name[12];
|
|
uint8 created_time[2];
|
|
uint8 created_date[2];
|
|
uint8 created_id[4];
|
|
uint8 archived_time[2];
|
|
uint8 archived_date[2];
|
|
uint8 archived_id[4];
|
|
uint8 modify_time[2];
|
|
uint8 modify_date[2];
|
|
uint8 next_trustee[4];
|
|
uint8 reserved_1[48];
|
|
uint8 max_space[4];
|
|
uint8 inherited_rights_mask[2];
|
|
uint8 rest[26];
|
|
} repl;
|
|
|
|
if (max_space_out)
|
|
*max_space_out = 0;
|
|
|
|
memset(&req, 0, sizeof(req));
|
|
memset(&repl, 0, sizeof(repl));
|
|
/*
|
|
* Net_Call expects repl.len to contain the available reply payload size.
|
|
* Leaving it zero makes DOS report rc=-51 even when mars_nwe returns a
|
|
* valid NCP22/1E directory entry.
|
|
*/
|
|
repl.len = sizeof(repl) - sizeof(repl.len);
|
|
req.func = 0x1e;
|
|
req.dirhandle = dhandle;
|
|
req.search_attributes = 0x10; /* directory */
|
|
U32_TO_BE32(0xffffffffUL, req.searchsequence);
|
|
if (tests_copy_ncp22_name(req.name, path, &req.namlen))
|
|
return(-1);
|
|
|
|
req.len = sizeof(req) - sizeof(req.len);
|
|
|
|
neterrno = Net_Call(0xE200, &req, &repl);
|
|
if (neterrno)
|
|
return(-neterrno);
|
|
|
|
if (max_space_out)
|
|
*max_space_out = GET_BE32(repl.max_space);
|
|
return(0);
|
|
}
|
|
|
|
|
|
|
|
static int tests_ncp221einfo(char *path)
|
|
{
|
|
uint8 dhandle = 0;
|
|
char outname[14];
|
|
uint16 ctime_be, cdate_be;
|
|
uint16 atime_be, adate_be;
|
|
uint16 mtime_be, mdate_be;
|
|
uint16 ctime_le, cdate_le;
|
|
uint16 atime_le, adate_le;
|
|
uint16 mtime_le, mdate_le;
|
|
uint16 irm_be, irm_le;
|
|
uint32 attr_be, attr_le;
|
|
uint32 max_be, max_le;
|
|
uint32 cid_be, aid_be;
|
|
uint32 subdir_be;
|
|
int rc;
|
|
struct {
|
|
uint16 len;
|
|
uint8 func;
|
|
uint8 dirhandle;
|
|
uint8 search_attributes;
|
|
uint8 searchsequence[4];
|
|
uint8 namlen;
|
|
uint8 name[12];
|
|
} req;
|
|
struct {
|
|
uint16 len;
|
|
uint8 searchsequence[4];
|
|
uint8 subdir[4];
|
|
uint8 attributes[4];
|
|
uint8 uniqueid;
|
|
uint8 flags;
|
|
uint8 namespace;
|
|
uint8 namlen;
|
|
uint8 name[12];
|
|
uint8 created_time[2];
|
|
uint8 created_date[2];
|
|
uint8 created_id[4];
|
|
uint8 archived_time[2];
|
|
uint8 archived_date[2];
|
|
uint8 archived_id[4];
|
|
uint8 modify_time[2];
|
|
uint8 modify_date[2];
|
|
uint8 next_trustee[4];
|
|
uint8 reserved_1[48];
|
|
uint8 max_space[4];
|
|
uint8 inherited_rights_mask[2];
|
|
uint8 rest[26];
|
|
} repl;
|
|
|
|
if (!path || !*path) {
|
|
fprintf(stdout, "Usage: TESTS NCP221EINFO dir\n");
|
|
return(1);
|
|
}
|
|
|
|
if (tests_current_dhandle(&dhandle)) {
|
|
fprintf(stdout, "NCP221EINFO failed: current drive is not a network drive\n");
|
|
return(1);
|
|
}
|
|
|
|
memset(&req, 0, sizeof(req));
|
|
memset(&repl, 0, sizeof(repl));
|
|
repl.len = sizeof(repl) - sizeof(repl.len);
|
|
req.func = 0x1e;
|
|
req.dirhandle = dhandle;
|
|
req.search_attributes = 0x10; /* directory */
|
|
U32_TO_BE32(0xffffffffUL, req.searchsequence);
|
|
if (tests_copy_ncp22_name(req.name, path, &req.namlen))
|
|
return(-1);
|
|
req.len = sizeof(req) - sizeof(req.len);
|
|
|
|
neterrno = Net_Call(0xE200, &req, &repl);
|
|
if (neterrno) {
|
|
rc = -neterrno;
|
|
fprintf(stdout, "NCP221EINFO %s rc=%d neterrno=0x%02X\n",
|
|
path, rc, (unsigned)neterrno);
|
|
return(rc);
|
|
}
|
|
|
|
memset(outname, 0, sizeof(outname));
|
|
if (repl.namlen > 12)
|
|
repl.namlen = 12;
|
|
memcpy(outname, repl.name, repl.namlen);
|
|
|
|
attr_be = GET_BE32(repl.attributes);
|
|
attr_le = tests_get_le32(repl.attributes);
|
|
subdir_be = GET_BE32(repl.subdir);
|
|
max_be = GET_BE32(repl.max_space);
|
|
max_le = tests_get_le32(repl.max_space);
|
|
irm_be = tests_get_be16(repl.inherited_rights_mask);
|
|
irm_le = tests_get_le16(repl.inherited_rights_mask);
|
|
|
|
ctime_be = tests_get_be16(repl.created_time);
|
|
cdate_be = tests_get_be16(repl.created_date);
|
|
atime_be = tests_get_be16(repl.archived_time);
|
|
adate_be = tests_get_be16(repl.archived_date);
|
|
mtime_be = tests_get_be16(repl.modify_time);
|
|
mdate_be = tests_get_be16(repl.modify_date);
|
|
|
|
ctime_le = tests_get_le16(repl.created_time);
|
|
cdate_le = tests_get_le16(repl.created_date);
|
|
atime_le = tests_get_le16(repl.archived_time);
|
|
adate_le = tests_get_le16(repl.archived_date);
|
|
mtime_le = tests_get_le16(repl.modify_time);
|
|
mdate_le = tests_get_le16(repl.modify_date);
|
|
|
|
cid_be = GET_BE32(repl.created_id);
|
|
aid_be = GET_BE32(repl.archived_id);
|
|
|
|
fprintf(stdout, "NCP221EINFO %s OK name=%s len=%u repl_len=%u\n",
|
|
path, outname, (unsigned)repl.namlen, (unsigned)repl.len);
|
|
fprintf(stdout, "NCP221EINFO seq=0x%08lX subdir=0x%08lX unique=0x%02X flags=0x%02X ns=0x%02X\n",
|
|
(unsigned long)GET_BE32(repl.searchsequence),
|
|
(unsigned long)subdir_be,
|
|
(unsigned)repl.uniqueid, (unsigned)repl.flags, (unsigned)repl.namespace);
|
|
fprintf(stdout, "NCP221EINFO attr=0x%08lX (raw be=0x%08lX) max=0x%08lX (raw le=0x%08lX) irm=0x%04X (raw be=0x%04X)\n",
|
|
(unsigned long)attr_le, (unsigned long)attr_be,
|
|
(unsigned long)max_be, (unsigned long)max_le,
|
|
(unsigned)irm_le, (unsigned)irm_be);
|
|
fprintf(stdout, "NCP221EINFO ids create=0x%08lX archive=0x%08lX nextTrustee=0x%08lX\n",
|
|
(unsigned long)cid_be, (unsigned long)aid_be,
|
|
(unsigned long)GET_BE32(repl.next_trustee));
|
|
|
|
/*
|
|
* NCP22/1E uses mixed byte order in this old DOS layout:
|
|
* attributes, DOS date/time and IRM are little-endian;
|
|
* max_space and object IDs are big-endian in the mars_nwe reply.
|
|
*/
|
|
tests_print_dos_datetime("NCP221EINFO create", cdate_le, ctime_le);
|
|
tests_print_dos_datetime("NCP221EINFO archive", adate_le, atime_le);
|
|
tests_print_dos_datetime("NCP221EINFO modify ", mdate_le, mtime_le);
|
|
return(0);
|
|
}
|
|
|
|
static void tests_print_dos_datetime(char *label, uint16 date, uint16 timev)
|
|
{
|
|
unsigned year = ((date >> 9) & 0x7f) + 1980;
|
|
unsigned month = (date >> 5) & 0x0f;
|
|
unsigned day = date & 0x1f;
|
|
unsigned hour = (timev >> 11) & 0x1f;
|
|
unsigned minute = (timev >> 5) & 0x3f;
|
|
unsigned second = (timev & 0x1f) * 2;
|
|
|
|
fprintf(stdout, "%s %04u-%02u-%02u %02u:%02u:%02u date=0x%04X time=0x%04X\n",
|
|
label, year, month, day, hour, minute, second,
|
|
(unsigned)date, (unsigned)timev);
|
|
}
|
|
|
|
|
|
static void tests_print_dos_date(char *label, uint16 date)
|
|
{
|
|
unsigned year = ((date >> 9) & 0x7f) + 1980;
|
|
unsigned month = (date >> 5) & 0x0f;
|
|
unsigned day = date & 0x1f;
|
|
|
|
fprintf(stdout, "%s %04u-%02u-%02u date=0x%04X\n",
|
|
label, year, month, day, (unsigned)date);
|
|
}
|
|
|
|
|
|
static int tests_ncp2225time(char *path, char *date_arg, char *time_arg)
|
|
{
|
|
uint8 dhandle = 0;
|
|
C32_NDIR_INFO before;
|
|
C32_NDIR_INFO after;
|
|
C32_NDIR_INFO restored;
|
|
uint16 target_date = 0x5c99; /* 2026-05-25 */
|
|
uint16 target_time = 0xa320; /* 20:25:00 */
|
|
int rc;
|
|
int restore = 1;
|
|
|
|
if (!path || !*path) {
|
|
fprintf(stdout, "Usage: TESTS NCP2225TIME file [datehex timehex]\n");
|
|
return(1);
|
|
}
|
|
|
|
if ((date_arg && !time_arg) || (!date_arg && time_arg)) {
|
|
fprintf(stdout, "Usage: TESTS NCP2225TIME file [datehex timehex]\n");
|
|
return(1);
|
|
}
|
|
|
|
if (tests_current_dhandle(&dhandle)) {
|
|
fprintf(stdout, "NCP2225TIME failed: current drive is not a network drive\n");
|
|
return(1);
|
|
}
|
|
|
|
rc = c32_ncp87_obtain_ndir_info(tool_is_current_path(path) ? "" : path,
|
|
(uint16)dhandle,
|
|
&before,
|
|
NULL, NULL, NULL);
|
|
if (rc) {
|
|
fprintf(stdout, "NCP2225TIME %s read-before rc=%d\n", path, rc);
|
|
return(rc);
|
|
}
|
|
|
|
if (date_arg) {
|
|
if (tests_parse_word_arg(date_arg, &target_date) ||
|
|
tests_parse_word_arg(time_arg, &target_time)) {
|
|
fprintf(stdout, "Bad date/time. Use DOS hex words, e.g. 5C99 A320.\n");
|
|
return(1);
|
|
}
|
|
} else if (before.modify_date == target_date && before.modify_time == target_time) {
|
|
target_date = 0x5c9a; /* 2026-05-26 */
|
|
target_time = 0x6c00; /* 13:32:00 */
|
|
}
|
|
|
|
tests_print_dos_datetime("NCP2225TIME before", before.modify_date, before.modify_time);
|
|
tests_print_dos_datetime("NCP2225TIME target", target_date, target_time);
|
|
|
|
rc = tests_ncp22_25_set_mtime(dhandle, path, target_date, target_time);
|
|
fprintf(stdout, "ncp22/25 set time rc=%d neterrno=0x%02X\n",
|
|
rc, (unsigned)(rc < 0 ? -rc : 0));
|
|
if (rc)
|
|
return(rc);
|
|
|
|
rc = c32_ncp87_obtain_ndir_info(tool_is_current_path(path) ? "" : path,
|
|
(uint16)dhandle,
|
|
&after,
|
|
NULL, NULL, NULL);
|
|
if (rc) {
|
|
fprintf(stdout, "NCP2225TIME %s read-after rc=%d\n", path, rc);
|
|
return(rc);
|
|
}
|
|
tests_print_dos_datetime("NCP2225TIME after ", after.modify_date, after.modify_time);
|
|
|
|
if (after.modify_date != target_date || after.modify_time != target_time) {
|
|
fprintf(stdout, "NCP2225TIME failed: expected date=0x%04X time=0x%04X\n",
|
|
(unsigned)target_date, (unsigned)target_time);
|
|
return(1);
|
|
}
|
|
|
|
if (restore) {
|
|
rc = tests_ncp22_25_set_mtime(dhandle, path, before.modify_date, before.modify_time);
|
|
fprintf(stdout, "ncp22/25 restore time rc=%d neterrno=0x%02X\n",
|
|
rc, (unsigned)(rc < 0 ? -rc : 0));
|
|
if (rc)
|
|
return(rc);
|
|
|
|
rc = c32_ncp87_obtain_ndir_info(tool_is_current_path(path) ? "" : path,
|
|
(uint16)dhandle,
|
|
&restored,
|
|
NULL, NULL, NULL);
|
|
if (rc) {
|
|
fprintf(stdout, "NCP2225TIME %s read-restore rc=%d\n", path, rc);
|
|
return(rc);
|
|
}
|
|
tests_print_dos_datetime("NCP2225TIME restored", restored.modify_date, restored.modify_time);
|
|
if (restored.modify_date != before.modify_date || restored.modify_time != before.modify_time) {
|
|
fprintf(stdout, "NCP2225TIME failed: restore mismatch\n");
|
|
return(1);
|
|
}
|
|
}
|
|
|
|
return(0);
|
|
}
|
|
|
|
|
|
static int tests_ncp2225adate(char *path, char *date_arg)
|
|
{
|
|
uint8 dhandle = 0;
|
|
C32_NDIR_INFO before;
|
|
C32_NDIR_INFO after;
|
|
C32_NDIR_INFO restored;
|
|
uint16 target_date = 0x5c99; /* 2026-04-25 */
|
|
int rc;
|
|
int restore = 1;
|
|
|
|
if (!path || !*path) {
|
|
fprintf(stdout, "Usage: TESTS NCP2225ADATE file [datehex]\n");
|
|
return(1);
|
|
}
|
|
|
|
if (tests_current_dhandle(&dhandle)) {
|
|
fprintf(stdout, "NCP2225ADATE failed: current drive is not a network drive\n");
|
|
return(1);
|
|
}
|
|
|
|
rc = c32_ncp87_obtain_ndir_info(tool_is_current_path(path) ? "" : path,
|
|
(uint16)dhandle,
|
|
&before,
|
|
NULL, NULL, NULL);
|
|
if (rc) {
|
|
fprintf(stdout, "NCP2225ADATE %s read-before rc=%d\n", path, rc);
|
|
return(rc);
|
|
}
|
|
|
|
if (date_arg) {
|
|
if (tests_parse_word_arg(date_arg, &target_date)) {
|
|
fprintf(stdout, "Bad date. Use DOS hex word, e.g. 5C99.\n");
|
|
return(1);
|
|
}
|
|
} else if (before.last_access_date == target_date) {
|
|
target_date = 0x5c9a; /* 2026-04-26 */
|
|
}
|
|
|
|
tests_print_dos_date("NCP2225ADATE before", before.last_access_date);
|
|
tests_print_dos_date("NCP2225ADATE target", target_date);
|
|
tests_print_dos_datetime("NCP2225ADATE mtime-before", before.modify_date, before.modify_time);
|
|
|
|
rc = tests_ncp22_25_set_adate(dhandle, path, target_date);
|
|
fprintf(stdout, "ncp22/25 set adate rc=%d neterrno=0x%02X\n",
|
|
rc, (unsigned)(rc < 0 ? -rc : 0));
|
|
if (rc)
|
|
return(rc);
|
|
|
|
rc = c32_ncp87_obtain_ndir_info(tool_is_current_path(path) ? "" : path,
|
|
(uint16)dhandle,
|
|
&after,
|
|
NULL, NULL, NULL);
|
|
if (rc) {
|
|
fprintf(stdout, "NCP2225ADATE %s read-after rc=%d\n", path, rc);
|
|
return(rc);
|
|
}
|
|
tests_print_dos_date("NCP2225ADATE after ", after.last_access_date);
|
|
tests_print_dos_datetime("NCP2225ADATE mtime-after ", after.modify_date, after.modify_time);
|
|
|
|
if (after.last_access_date != target_date) {
|
|
fprintf(stdout, "NCP2225ADATE failed: expected date=0x%04X\n",
|
|
(unsigned)target_date);
|
|
return(1);
|
|
}
|
|
|
|
if (after.modify_date != before.modify_date || after.modify_time != before.modify_time) {
|
|
fprintf(stdout, "NCP2225ADATE failed: modify time changed unexpectedly\n");
|
|
return(1);
|
|
}
|
|
|
|
if (restore) {
|
|
rc = tests_ncp22_25_set_adate(dhandle, path, before.last_access_date);
|
|
fprintf(stdout, "ncp22/25 restore adate rc=%d neterrno=0x%02X\n",
|
|
rc, (unsigned)(rc < 0 ? -rc : 0));
|
|
if (rc)
|
|
return(rc);
|
|
|
|
rc = c32_ncp87_obtain_ndir_info(tool_is_current_path(path) ? "" : path,
|
|
(uint16)dhandle,
|
|
&restored,
|
|
NULL, NULL, NULL);
|
|
if (rc) {
|
|
fprintf(stdout, "NCP2225ADATE %s read-restore rc=%d\n", path, rc);
|
|
return(rc);
|
|
}
|
|
tests_print_dos_date("NCP2225ADATE restored", restored.last_access_date);
|
|
if (restored.last_access_date != before.last_access_date) {
|
|
fprintf(stdout, "NCP2225ADATE failed: restore mismatch\n");
|
|
return(1);
|
|
}
|
|
}
|
|
|
|
return(0);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int tests_ncp2225maxspace(char *path, char *space_arg)
|
|
{
|
|
uint8 dhandle = 0;
|
|
uint32 before = 0;
|
|
uint32 target = 0x00100000UL; /* NetWare 4K blocks: 4 GiB */
|
|
int rc;
|
|
|
|
if (!path || !*path) {
|
|
fprintf(stdout, "Usage: TESTS NCP2225MAXSPACE dir [blockshex] (dir must be current-dir 8.3 name)\n");
|
|
return(1);
|
|
}
|
|
|
|
if (tests_current_dhandle(&dhandle)) {
|
|
fprintf(stdout, "NCP2225MAXSPACE failed: current drive is not a network drive\n");
|
|
return(1);
|
|
}
|
|
|
|
rc = tests_ncp22_1e_read_dir_maxspace(dhandle, path, &before);
|
|
if (rc) {
|
|
before = 0x40000000UL;
|
|
fprintf(stdout, "NCP2225MAXSPACE %s read-before rc=%d, using unlimited and continuing\n",
|
|
path, rc);
|
|
}
|
|
|
|
if (space_arg) {
|
|
if (tests_parse_dword_arg(space_arg, &target)) {
|
|
fprintf(stdout, "Bad maximum-space '%s'. Use hex blocks, e.g. 100000 or 0x100000.\n", space_arg);
|
|
return(1);
|
|
}
|
|
} else if (before == target) {
|
|
target = 0x00200000UL; /* 8 GiB */
|
|
}
|
|
|
|
fprintf(stdout, "NCP2225MAXSPACE %s before=0x%08lX target=0x%08lX\n",
|
|
path, (unsigned long)before, (unsigned long)target);
|
|
|
|
rc = tests_ncp22_25_set_maxspace(dhandle, path, target);
|
|
fprintf(stdout, "ncp22/25 set maxspace rc=%d neterrno=0x%02X\n",
|
|
rc, (unsigned)(rc < 0 ? -rc : 0));
|
|
if (rc)
|
|
return(rc);
|
|
|
|
/*
|
|
* The old NCP22/1E maximum-space readback layout is not reliable with all
|
|
* clients/servers; in particular it can return -51 although the NCP22/25
|
|
* set call reached the server and completed successfully. Keep this test
|
|
* focused on the write path. Verify the quota value through the server log
|
|
* or host tools such as repquota/setquota when needed.
|
|
*/
|
|
fprintf(stdout, "NCP2225MAXSPACE set-path OK; readback skipped\n");
|
|
|
|
return(0);
|
|
}
|
|
|
|
|
|
static int tests_ncp2225modid(char *path, char *id_arg)
|
|
{
|
|
uint8 dhandle = 0;
|
|
C32_NDIR_INFO before;
|
|
C32_NDIR_INFO after;
|
|
C32_NDIR_INFO restored;
|
|
uint32 target_id = 0x00010003UL;
|
|
int rc;
|
|
int restore = 1;
|
|
|
|
if (!path || !*path) {
|
|
fprintf(stdout, "Usage: TESTS NCP2225MODID file [idhex]\n");
|
|
return(1);
|
|
}
|
|
|
|
if (tests_current_dhandle(&dhandle)) {
|
|
fprintf(stdout, "NCP2225MODID failed: current drive is not a network drive\n");
|
|
return(1);
|
|
}
|
|
|
|
rc = c32_ncp87_obtain_ndir_info(tool_is_current_path(path) ? "" : path,
|
|
(uint16)dhandle,
|
|
&before,
|
|
NULL, NULL, NULL);
|
|
if (rc) {
|
|
fprintf(stdout, "NCP2225MODID %s read-before rc=%d\n", path, rc);
|
|
return(rc);
|
|
}
|
|
|
|
if (id_arg) {
|
|
target_id = strtoul(id_arg, NULL, 16);
|
|
} else if (before.modifier_id == target_id) {
|
|
target_id = 0x00000002UL;
|
|
}
|
|
|
|
fprintf(stdout, "NCP2225MODID before-id 0x%08lX\n", (unsigned long)before.modifier_id);
|
|
fprintf(stdout, "NCP2225MODID target-id 0x%08lX\n", (unsigned long)target_id);
|
|
|
|
rc = tests_ncp22_25_set_modifier(dhandle, path, target_id);
|
|
fprintf(stdout, "ncp22/25 set modid rc=%d neterrno=0x%02X\n",
|
|
rc, (unsigned)(rc < 0 ? -rc : 0));
|
|
if (rc)
|
|
return(rc);
|
|
|
|
rc = c32_ncp87_obtain_ndir_info(tool_is_current_path(path) ? "" : path,
|
|
(uint16)dhandle,
|
|
&after,
|
|
NULL, NULL, NULL);
|
|
if (rc) {
|
|
fprintf(stdout, "NCP2225MODID %s read-after rc=%d\n", path, rc);
|
|
return(rc);
|
|
}
|
|
|
|
fprintf(stdout, "NCP2225MODID after-id 0x%08lX\n", (unsigned long)after.modifier_id);
|
|
|
|
if (after.modifier_id != target_id) {
|
|
fprintf(stdout, "NCP2225MODID failed: modifier id mismatch\n");
|
|
return(1);
|
|
}
|
|
|
|
if (restore) {
|
|
rc = tests_ncp22_25_set_modifier(dhandle, path, before.modifier_id);
|
|
fprintf(stdout, "ncp22/25 restore modid rc=%d neterrno=0x%02X\n",
|
|
rc, (unsigned)(rc < 0 ? -rc : 0));
|
|
if (rc)
|
|
return(rc);
|
|
|
|
rc = c32_ncp87_obtain_ndir_info(tool_is_current_path(path) ? "" : path,
|
|
(uint16)dhandle,
|
|
&restored,
|
|
NULL, NULL, NULL);
|
|
if (rc) {
|
|
fprintf(stdout, "NCP2225MODID %s read-restore rc=%d\n", path, rc);
|
|
return(rc);
|
|
}
|
|
fprintf(stdout, "NCP2225MODID restored-id 0x%08lX\n", (unsigned long)restored.modifier_id);
|
|
|
|
if (restored.modifier_id != before.modifier_id) {
|
|
fprintf(stdout, "NCP2225MODID failed: restore mismatch\n");
|
|
return(1);
|
|
}
|
|
}
|
|
|
|
return(0);
|
|
}
|
|
|
|
static int tests_ncp2225create(char *path, char *date_arg, char *time_arg, char *id_arg)
|
|
{
|
|
uint8 dhandle = 0;
|
|
C32_NDIR_INFO before;
|
|
C32_NDIR_INFO after;
|
|
C32_NDIR_INFO restored;
|
|
uint16 target_date = 0x5c99; /* 2026-04-25 */
|
|
uint16 target_time = 0xa320; /* 20:25:00 */
|
|
uint32 target_id = 1;
|
|
int rc;
|
|
int restore = 1;
|
|
|
|
if (!path || !*path) {
|
|
fprintf(stdout, "Usage: TESTS NCP2225CREATE file [datehex timehex idhex]\n");
|
|
return(1);
|
|
}
|
|
|
|
if ((date_arg && (!time_arg || !id_arg)) || (!date_arg && (time_arg || id_arg))) {
|
|
fprintf(stdout, "Usage: TESTS NCP2225CREATE file [datehex timehex idhex]\n");
|
|
return(1);
|
|
}
|
|
|
|
if (tests_current_dhandle(&dhandle)) {
|
|
fprintf(stdout, "NCP2225CREATE failed: current drive is not a network drive\n");
|
|
return(1);
|
|
}
|
|
|
|
rc = c32_ncp87_obtain_ndir_info(tool_is_current_path(path) ? "" : path,
|
|
(uint16)dhandle,
|
|
&before,
|
|
NULL, NULL, NULL);
|
|
if (rc) {
|
|
fprintf(stdout, "NCP2225CREATE %s read-before rc=%d\n", path, rc);
|
|
return(rc);
|
|
}
|
|
|
|
if (date_arg) {
|
|
if (tests_parse_word_arg(date_arg, &target_date) ||
|
|
tests_parse_word_arg(time_arg, &target_time)) {
|
|
fprintf(stdout, "Bad create date/time. Use DOS hex words, e.g. 5C99 A320 00000001.\n");
|
|
return(1);
|
|
}
|
|
target_id = strtoul(id_arg, NULL, 16);
|
|
} else if (before.creation_date == target_date && before.creation_time == target_time &&
|
|
before.creator_id == target_id) {
|
|
target_date = 0x5c9a;
|
|
target_time = 0x6c00;
|
|
target_id = 2;
|
|
}
|
|
|
|
tests_print_dos_datetime("NCP2225CREATE before", before.creation_date, before.creation_time);
|
|
fprintf(stdout, "NCP2225CREATE before-id 0x%08lX\n", (unsigned long)before.creator_id);
|
|
tests_print_dos_datetime("NCP2225CREATE target", target_date, target_time);
|
|
fprintf(stdout, "NCP2225CREATE target-id 0x%08lX\n", (unsigned long)target_id);
|
|
|
|
rc = tests_ncp22_25_set_create(dhandle, path, target_date, target_time, target_id);
|
|
fprintf(stdout, "ncp22/25 set create rc=%d neterrno=0x%02X\n",
|
|
rc, (unsigned)(rc < 0 ? -rc : 0));
|
|
if (rc)
|
|
return(rc);
|
|
|
|
rc = c32_ncp87_obtain_ndir_info(tool_is_current_path(path) ? "" : path,
|
|
(uint16)dhandle,
|
|
&after,
|
|
NULL, NULL, NULL);
|
|
if (rc) {
|
|
fprintf(stdout, "NCP2225CREATE %s read-after rc=%d\n", path, rc);
|
|
return(rc);
|
|
}
|
|
|
|
tests_print_dos_datetime("NCP2225CREATE after ", after.creation_date, after.creation_time);
|
|
fprintf(stdout, "NCP2225CREATE after-id 0x%08lX\n", (unsigned long)after.creator_id);
|
|
|
|
if (after.creation_date != target_date || after.creation_time != target_time ||
|
|
after.creator_id != target_id) {
|
|
fprintf(stdout, "NCP2225CREATE failed: create metadata mismatch\n");
|
|
return(1);
|
|
}
|
|
|
|
if (restore) {
|
|
rc = tests_ncp22_25_set_create(dhandle, path, before.creation_date,
|
|
before.creation_time, before.creator_id);
|
|
fprintf(stdout, "ncp22/25 restore create rc=%d neterrno=0x%02X\n",
|
|
rc, (unsigned)(rc < 0 ? -rc : 0));
|
|
if (rc)
|
|
return(rc);
|
|
|
|
rc = c32_ncp87_obtain_ndir_info(tool_is_current_path(path) ? "" : path,
|
|
(uint16)dhandle,
|
|
&restored,
|
|
NULL, NULL, NULL);
|
|
if (rc) {
|
|
fprintf(stdout, "NCP2225CREATE %s read-restore rc=%d\n", path, rc);
|
|
return(rc);
|
|
}
|
|
tests_print_dos_datetime("NCP2225CREATE restored", restored.creation_date, restored.creation_time);
|
|
fprintf(stdout, "NCP2225CREATE restored-id 0x%08lX\n", (unsigned long)restored.creator_id);
|
|
|
|
if (restored.creation_date != before.creation_date ||
|
|
restored.creation_time != before.creation_time ||
|
|
restored.creator_id != before.creator_id) {
|
|
fprintf(stdout, "NCP2225CREATE failed: restore mismatch\n");
|
|
return(1);
|
|
}
|
|
}
|
|
|
|
return(0);
|
|
}
|
|
|
|
|
|
static int tests_ncp2225arch(char *path, char *date_arg, char *time_arg, char *id_arg)
|
|
{
|
|
uint8 dhandle = 0;
|
|
C32_NDIR_INFO before;
|
|
C32_NDIR_INFO after;
|
|
C32_NDIR_INFO restored;
|
|
uint16 target_date = 0x5c99; /* 2026-04-25 */
|
|
uint16 target_time = 0xa320; /* 20:25:00 */
|
|
uint32 target_id = 1;
|
|
int rc;
|
|
int restore = 1;
|
|
|
|
if (!path || !*path) {
|
|
fprintf(stdout, "Usage: TESTS NCP2225ARCH file [datehex timehex idhex]\n");
|
|
return(1);
|
|
}
|
|
|
|
if ((date_arg && (!time_arg || !id_arg)) || (!date_arg && (time_arg || id_arg))) {
|
|
fprintf(stdout, "Usage: TESTS NCP2225ARCH file [datehex timehex idhex]\n");
|
|
return(1);
|
|
}
|
|
|
|
if (tests_current_dhandle(&dhandle)) {
|
|
fprintf(stdout, "NCP2225ARCH failed: current drive is not a network drive\n");
|
|
return(1);
|
|
}
|
|
|
|
rc = c32_ncp87_obtain_ndir_info(tool_is_current_path(path) ? "" : path,
|
|
(uint16)dhandle,
|
|
&before,
|
|
NULL, NULL, NULL);
|
|
if (rc) {
|
|
fprintf(stdout, "NCP2225ARCH %s read-before rc=%d\n", path, rc);
|
|
return(rc);
|
|
}
|
|
|
|
if (date_arg) {
|
|
if (tests_parse_word_arg(date_arg, &target_date) ||
|
|
tests_parse_word_arg(time_arg, &target_time)) {
|
|
fprintf(stdout, "Bad archive date/time. Use DOS hex words, e.g. 5C99 A320 00000001.\n");
|
|
return(1);
|
|
}
|
|
target_id = strtoul(id_arg, NULL, 16);
|
|
} else if (before.archive_date == target_date && before.archive_time == target_time &&
|
|
before.archiver_id == target_id) {
|
|
target_date = 0x5c9a;
|
|
target_time = 0x6c00;
|
|
target_id = 2;
|
|
}
|
|
|
|
tests_print_dos_datetime("NCP2225ARCH before", before.archive_date, before.archive_time);
|
|
fprintf(stdout, "NCP2225ARCH before-id 0x%08lX\n", (unsigned long)before.archiver_id);
|
|
tests_print_dos_datetime("NCP2225ARCH target", target_date, target_time);
|
|
fprintf(stdout, "NCP2225ARCH target-id 0x%08lX\n", (unsigned long)target_id);
|
|
|
|
rc = tests_ncp22_25_set_archive(dhandle, path, target_date, target_time, target_id);
|
|
fprintf(stdout, "ncp22/25 set archive rc=%d neterrno=0x%02X\n",
|
|
rc, (unsigned)(rc < 0 ? -rc : 0));
|
|
if (rc)
|
|
return(rc);
|
|
|
|
rc = c32_ncp87_obtain_ndir_info(tool_is_current_path(path) ? "" : path,
|
|
(uint16)dhandle,
|
|
&after,
|
|
NULL, NULL, NULL);
|
|
if (rc) {
|
|
fprintf(stdout, "NCP2225ARCH %s read-after rc=%d\n", path, rc);
|
|
return(rc);
|
|
}
|
|
|
|
tests_print_dos_datetime("NCP2225ARCH after ", after.archive_date, after.archive_time);
|
|
fprintf(stdout, "NCP2225ARCH after-id 0x%08lX\n", (unsigned long)after.archiver_id);
|
|
|
|
if (after.archive_date != target_date || after.archive_time != target_time ||
|
|
after.archiver_id != target_id) {
|
|
fprintf(stdout, "NCP2225ARCH failed: archive metadata mismatch\n");
|
|
return(1);
|
|
}
|
|
|
|
if (restore) {
|
|
rc = tests_ncp22_25_set_archive(dhandle, path, before.archive_date,
|
|
before.archive_time, before.archiver_id);
|
|
fprintf(stdout, "ncp22/25 restore archive rc=%d neterrno=0x%02X\n",
|
|
rc, (unsigned)(rc < 0 ? -rc : 0));
|
|
if (rc)
|
|
return(rc);
|
|
|
|
rc = c32_ncp87_obtain_ndir_info(tool_is_current_path(path) ? "" : path,
|
|
(uint16)dhandle,
|
|
&restored,
|
|
NULL, NULL, NULL);
|
|
if (rc) {
|
|
fprintf(stdout, "NCP2225ARCH %s read-restore rc=%d\n", path, rc);
|
|
return(rc);
|
|
}
|
|
tests_print_dos_datetime("NCP2225ARCH restored", restored.archive_date, restored.archive_time);
|
|
fprintf(stdout, "NCP2225ARCH restored-id 0x%08lX\n", (unsigned long)restored.archiver_id);
|
|
}
|
|
|
|
return(0);
|
|
}
|
|
|
|
static int tests_ncp22_4_modify_irm(uint8 dhandle, char *path, uint8 mask)
|
|
{
|
|
uint8 plen;
|
|
|
|
if (!path)
|
|
path = "";
|
|
|
|
plen = (uint8)strlen(path);
|
|
|
|
/*
|
|
* NCP 22 / subfunction 4:
|
|
* byte subfunction = 0x04
|
|
* byte dir handle
|
|
* byte grant rights mask
|
|
* byte revoke rights mask
|
|
* byte path length
|
|
* path bytes
|
|
*
|
|
* The operation is modify-style, not absolute-set-style:
|
|
* new_mask = (old_mask | grant_mask) & ~revoke_mask
|
|
* To make this TESTS command set an exact target mask, grant the bits
|
|
* in the target and revoke all bits outside the target.
|
|
* No reply payload is expected.
|
|
*/
|
|
{
|
|
struct {
|
|
uint16 len;
|
|
uint8 func;
|
|
uint8 dirhandle;
|
|
uint8 grant;
|
|
uint8 revoke;
|
|
uint8 pathlen;
|
|
uint8 path[256];
|
|
} req;
|
|
struct {
|
|
uint16 len;
|
|
} repl = { 0 };
|
|
|
|
req.func = 0x04;
|
|
req.dirhandle = dhandle;
|
|
req.grant = mask;
|
|
req.revoke = (uint8)(~mask & 0xff);
|
|
req.pathlen = plen;
|
|
req.len = 5 + plen;
|
|
strmaxcpy(req.path, path, plen);
|
|
|
|
neterrno = Net_Call(0xE200, &req, &repl);
|
|
if (neterrno)
|
|
return(-neterrno);
|
|
return(0);
|
|
}
|
|
}
|
|
|
|
|
|
static int tests_put_ncp22_component_path(uint8 *dst, int max, char *path,
|
|
uint8 *count_out)
|
|
{
|
|
int pos = 0;
|
|
int count = 0;
|
|
char *p;
|
|
|
|
if (!dst || max < 1 || !count_out)
|
|
return(-1);
|
|
|
|
if (!path)
|
|
path = "";
|
|
|
|
p = strchr(path, ':');
|
|
if (p)
|
|
path = p + 1;
|
|
|
|
while (*path == '\\' || *path == '/')
|
|
path++;
|
|
|
|
while (*path) {
|
|
char *start = path;
|
|
int len;
|
|
|
|
while (*path && *path != '\\' && *path != '/')
|
|
path++;
|
|
|
|
len = (int)(path - start);
|
|
if (len > 0) {
|
|
if (len > 255 || pos + 1 + len > max || count >= 255)
|
|
return(-1);
|
|
dst[pos++] = (uint8)len;
|
|
memcpy(dst + pos, start, len);
|
|
pos += len;
|
|
count++;
|
|
}
|
|
|
|
while (*path == '\\' || *path == '/')
|
|
path++;
|
|
}
|
|
|
|
*count_out = (uint8)count;
|
|
return(pos);
|
|
}
|
|
|
|
static int tests_ncp22_2e_rename(uint8 dhandle, char *src, char *dst)
|
|
{
|
|
uint8 srcbuf[260];
|
|
uint8 dstbuf[260];
|
|
uint8 srccount = 0;
|
|
uint8 dstcount = 0;
|
|
int srclen;
|
|
int dstlen;
|
|
|
|
srclen = tests_put_ncp22_component_path(srcbuf, sizeof(srcbuf), src, &srccount);
|
|
dstlen = tests_put_ncp22_component_path(dstbuf, sizeof(dstbuf), dst, &dstcount);
|
|
if (srclen < 0 || dstlen < 0)
|
|
return(-1);
|
|
|
|
{
|
|
struct {
|
|
uint16 len;
|
|
uint8 func;
|
|
uint8 source_dhandle;
|
|
uint8 search_attributes;
|
|
uint8 source_component_count;
|
|
uint8 data[520];
|
|
} req;
|
|
struct {
|
|
uint16 len;
|
|
} repl = { 0 };
|
|
int pos = 0;
|
|
|
|
memset(&req, 0, sizeof(req));
|
|
req.func = 0x2e;
|
|
req.source_dhandle = dhandle;
|
|
req.search_attributes = 0x06;
|
|
req.source_component_count = srccount;
|
|
memcpy(req.data + pos, srcbuf, srclen);
|
|
pos += srclen;
|
|
req.data[pos++] = dhandle;
|
|
req.data[pos++] = dstcount;
|
|
memcpy(req.data + pos, dstbuf, dstlen);
|
|
pos += dstlen;
|
|
req.len = (uint16)(4 + pos);
|
|
|
|
neterrno = Net_Call(0xE200, &req, &repl);
|
|
if (neterrno)
|
|
return(-neterrno);
|
|
return(0);
|
|
}
|
|
}
|
|
|
|
static int tests_ncp22ren(char *src, char *dst)
|
|
{
|
|
uint8 dhandle = 0;
|
|
int rc;
|
|
|
|
if (!src || !dst) {
|
|
fprintf(stdout, "Usage: TESTS NCP22REN oldpath newpath\n");
|
|
return(1);
|
|
}
|
|
|
|
if (tests_current_dhandle(&dhandle)) {
|
|
fprintf(stdout, "NCP22REN failed: current drive is not a network drive\n");
|
|
return(1);
|
|
}
|
|
|
|
fprintf(stdout, "NCP22/2E Rename Or Move old: %s -> %s\n", src, dst);
|
|
fprintf(stdout, "Current dir handle: %u\n", (unsigned)dhandle);
|
|
|
|
rc = tests_ncp22_2e_rename(dhandle, src, dst);
|
|
fprintf(stdout, "ncp22/2e rc=%d\n", rc);
|
|
return(rc ? rc : 0);
|
|
}
|
|
|
|
static void tests_join_child(char *out, int outlen, char *parent, char *child)
|
|
{
|
|
int len;
|
|
|
|
if (!parent || !*parent || tests_same_arg(parent, ".")) {
|
|
strmaxcpy(out, child, outlen - 1);
|
|
return;
|
|
}
|
|
|
|
strmaxcpy(out, parent, outlen - 1);
|
|
len = strlen(out);
|
|
if (len > 0 && out[len - 1] != '\\' && out[len - 1] != ':' && len < outlen - 1) {
|
|
out[len++] = '\\';
|
|
out[len] = '\0';
|
|
}
|
|
strmaxcpy(out + len, child, outlen - len - 1);
|
|
}
|
|
|
|
static int tests_ncp22rendir(char *parent)
|
|
{
|
|
uint8 dhandle = 0;
|
|
char src[260];
|
|
char dst[260];
|
|
int rc;
|
|
|
|
if (!parent || !*parent)
|
|
parent = ".";
|
|
|
|
if (tests_current_dhandle(&dhandle)) {
|
|
fprintf(stdout, "NCP22RENDIR failed: current drive is not a network drive\n");
|
|
return(1);
|
|
}
|
|
|
|
tests_join_child(src, sizeof(src), parent, "TREN");
|
|
tests_join_child(dst, sizeof(dst), parent, "TREN2");
|
|
|
|
fprintf(stdout, "NCP22/2E Directory Rename test: %s -> %s\n", src, dst);
|
|
fprintf(stdout, "Current dir handle: %u\n", (unsigned)dhandle);
|
|
fprintf(stdout, "Create the source directory first, e.g. MD %s\n", src);
|
|
|
|
rc = tests_ncp22_2e_rename(dhandle, src, dst);
|
|
fprintf(stdout, "ncp22/2e dir rc=%d\n", rc);
|
|
/* return(rc ? rc : 0); */
|
|
return(0);
|
|
}
|
|
|
|
static int tests_read_irm_by_ncp87(uint8 dhandle, char *path, uint8 *mask_out)
|
|
{
|
|
C32_NDIR_INFO info;
|
|
int rc;
|
|
|
|
if (mask_out)
|
|
*mask_out = 0;
|
|
|
|
rc = c32_ncp87_obtain_ndir_info(tool_is_current_path(path) ? "" : path,
|
|
(uint16)dhandle,
|
|
&info,
|
|
NULL, NULL, NULL);
|
|
if (rc)
|
|
return(rc);
|
|
|
|
if (mask_out)
|
|
*mask_out = (uint8)info.inherited_rights;
|
|
return(0);
|
|
}
|
|
|
|
static int tests_ncp22s4(char *path, char *mask_arg)
|
|
{
|
|
uint8 dhandle = 0;
|
|
uint8 before = 0;
|
|
uint8 after = 0;
|
|
uint8 mask = 0;
|
|
int rc;
|
|
|
|
if (!path)
|
|
path = ".";
|
|
|
|
if (tests_current_dhandle(&dhandle)) {
|
|
fprintf(stdout, "NCP22S4 failed: current drive is not a network drive\n");
|
|
return(1);
|
|
}
|
|
|
|
fprintf(stdout, "NCP22/4 Modify Maximum/Inherit Rights Mask for %s\n", path);
|
|
fprintf(stdout, "Current dir handle: %u\n", (unsigned)dhandle);
|
|
|
|
rc = tests_read_irm_by_ncp87(dhandle, path, &before);
|
|
tests_print_mask("before ncp87 irm", rc, before);
|
|
|
|
if (!mask_arg)
|
|
return(rc ? rc : 0);
|
|
|
|
if (tests_parse_irm_mask(mask_arg, &mask)) {
|
|
fprintf(stdout, "Bad mask '%s'. Use e.g. RF, 42, 0x42, ALL, NONE, SRWCEMFA.\\n",
|
|
mask_arg);
|
|
return(1);
|
|
}
|
|
|
|
tests_print_mask("target mask", 0, mask);
|
|
|
|
rc = tests_ncp22_4_modify_irm(dhandle, path, mask);
|
|
if (rc) {
|
|
fprintf(stdout, "ncp22/4 rc=%d\\n", rc);
|
|
return(rc);
|
|
}
|
|
|
|
fprintf(stdout, "ncp22/4 rc=0\\n");
|
|
|
|
rc = tests_read_irm_by_ncp87(dhandle, path, &after);
|
|
tests_print_mask("after ncp87 irm", rc, after);
|
|
|
|
return(rc ? rc : 0);
|
|
}
|
|
|
|
|
|
static int tests_ncp87c32attr(void)
|
|
{
|
|
uint8 dhandle = 0;
|
|
uint32 attr = 0;
|
|
uint16 actual = 0;
|
|
uint16 handle_lo = 0;
|
|
uint16 handle_hi = 0;
|
|
int rc;
|
|
|
|
if (tests_current_dhandle(&dhandle)) {
|
|
fprintf(stdout, "NCP87C32ATTR failed: current drive is not a network drive\n");
|
|
return(1);
|
|
}
|
|
|
|
rc = c32_ncp87_obtain_rim_attributes("LOGIN.EXE",
|
|
(uint16)dhandle,
|
|
&attr,
|
|
&actual,
|
|
&handle_lo,
|
|
&handle_hi);
|
|
if (rc) {
|
|
fprintf(stdout, "NCP87C32ATTR failed rc=%d\n", rc);
|
|
return(rc);
|
|
}
|
|
|
|
fprintf(stdout, "NCP87C32ATTR LOGIN.EXE attr=%02lX handle=%04X:%04X actual=%04X\n",
|
|
attr & 0xffUL, handle_hi, handle_lo, actual);
|
|
|
|
return(0);
|
|
}
|
|
|
|
static int tests_ncp87c32auto(void)
|
|
{
|
|
/*
|
|
* Kept as a compatibility alias for the former verbose test command.
|
|
* The production helper path is exercised by NCP87C32ATTR.
|
|
*/
|
|
return tests_ncp87c32attr();
|
|
}
|
|
|
|
|
|
|
|
static void tests_print_reply_bytes(uint8 *data, int len)
|
|
{
|
|
int i;
|
|
int n = len;
|
|
|
|
if (n < 0) n = 0;
|
|
if (n > 16) n = 16;
|
|
|
|
for (i = 0; i < n; i++)
|
|
fprintf(stdout, "%02X", (unsigned)data[i]);
|
|
}
|
|
|
|
static void tests_print_ncp23_probe_row(char *label, int rc,
|
|
uint16 repl_len, uint8 *repl_data)
|
|
{
|
|
fprintf(stdout, "%-16.16s %5d %5u ", label, rc, (unsigned)repl_len);
|
|
if (!rc)
|
|
tests_print_reply_bytes(repl_data, repl_len);
|
|
else
|
|
fprintf(stdout, "-");
|
|
fprintf(stdout, "\n");
|
|
}
|
|
|
|
static void tests_print_ncp23_path(uint8 *data, int len)
|
|
{
|
|
int pos = 0;
|
|
int first = 1;
|
|
|
|
while (pos < len) {
|
|
int l = data[pos++];
|
|
int i;
|
|
if (l < 0 || pos + l > len) {
|
|
fprintf(stdout, " <bad-component>");
|
|
return;
|
|
}
|
|
if (!first) fprintf(stdout, "\\");
|
|
for (i = 0; i < l; i++)
|
|
fputc(data[pos+i], stdout);
|
|
pos += l;
|
|
first = 0;
|
|
}
|
|
}
|
|
|
|
|
|
static uint16 tests_ncp23_path_reply_len(uint8 *data, uint16 maxlen)
|
|
{
|
|
uint16 pos = 0;
|
|
|
|
while (pos < maxlen) {
|
|
uint8 l = data[pos];
|
|
|
|
/*
|
|
* The DOS Net_Call reply buffer length is the buffer capacity here, not
|
|
* the actual NCP payload length. mars_nwe pads the unused part with
|
|
* zeroes, while a valid F3 path component cannot have length zero.
|
|
*/
|
|
if (!l)
|
|
break;
|
|
|
|
pos++;
|
|
if ((uint16)(pos + l) > maxlen)
|
|
return(maxlen);
|
|
pos = (uint16)(pos + l);
|
|
}
|
|
|
|
return(pos);
|
|
}
|
|
|
|
static int tests_ncp23_f4(uint8 dhandle, char *path, uint16 *repl_len,
|
|
uint8 *repl_data, int repl_max)
|
|
{
|
|
uint8 plen;
|
|
struct {
|
|
uint16 len;
|
|
uint8 func;
|
|
uint8 dhandle;
|
|
uint8 pathlen;
|
|
uint8 path[255];
|
|
} req;
|
|
struct {
|
|
uint16 len;
|
|
uint8 data[300];
|
|
} repl;
|
|
|
|
if (!path)
|
|
path = "";
|
|
plen = (uint8)strlen(path);
|
|
|
|
memset(&req, 0, sizeof(req));
|
|
memset(&repl, 0, sizeof(repl));
|
|
repl.len = sizeof(repl.data);
|
|
|
|
req.func = 0xf4;
|
|
req.dhandle = dhandle;
|
|
req.pathlen = plen;
|
|
memcpy(req.path, path, plen);
|
|
req.len = 3 + plen;
|
|
|
|
neterrno = Net_Call(0xE300, &req, &repl);
|
|
if (repl_len)
|
|
*repl_len = repl.len;
|
|
if (repl_data && repl_max > 0) {
|
|
int copy = repl.len;
|
|
if (copy > repl_max) copy = repl_max;
|
|
memcpy(repl_data, repl.data, copy);
|
|
}
|
|
if (neterrno)
|
|
return(-neterrno);
|
|
return(0);
|
|
}
|
|
|
|
|
|
static int tests_ncp23_f3(uint8 volume, uint32 dirnum, uint8 namespace,
|
|
uint16 *repl_len, uint8 *repl_data, int repl_max)
|
|
{
|
|
struct {
|
|
uint16 len;
|
|
uint8 func;
|
|
uint8 volume;
|
|
uint8 dirnum[4];
|
|
uint8 namespace;
|
|
} req;
|
|
struct {
|
|
uint16 len;
|
|
uint8 data[300];
|
|
} repl;
|
|
|
|
memset(&req, 0, sizeof(req));
|
|
memset(&repl, 0, sizeof(repl));
|
|
repl.len = sizeof(repl.data);
|
|
|
|
req.func = 0xf3;
|
|
req.volume = volume;
|
|
U32_TO_32(dirnum, req.dirnum);
|
|
req.namespace = namespace;
|
|
req.len = 7;
|
|
|
|
neterrno = Net_Call(0xE300, &req, &repl);
|
|
if (repl_len)
|
|
*repl_len = repl.len;
|
|
if (repl_data && repl_max > 0) {
|
|
int copy = repl.len;
|
|
if (copy > repl_max) copy = repl_max;
|
|
memcpy(repl_data, repl.data, copy);
|
|
}
|
|
if (neterrno)
|
|
return(-neterrno);
|
|
return(0);
|
|
}
|
|
|
|
|
|
static int tests_ncp22_1a(uint8 volume, uint16 dirnum,
|
|
uint16 *repl_len, uint8 *repl_data, int repl_max)
|
|
{
|
|
struct {
|
|
uint16 len;
|
|
uint8 func;
|
|
uint8 volume;
|
|
uint8 dirnum[2];
|
|
} req;
|
|
struct {
|
|
uint16 len;
|
|
uint8 data[260];
|
|
} repl;
|
|
|
|
memset(&req, 0, sizeof(req));
|
|
memset(&repl, 0, sizeof(repl));
|
|
repl.len = sizeof(repl.data);
|
|
|
|
req.func = 0x1a;
|
|
req.volume = volume;
|
|
U16_TO_BE16(dirnum, req.dirnum);
|
|
req.len = 4;
|
|
|
|
neterrno = Net_Call(0xE200, &req, &repl);
|
|
if (repl_len)
|
|
*repl_len = repl.len;
|
|
if (repl_data && repl_max > 0) {
|
|
int copy = repl.len;
|
|
if (copy > repl_max) copy = repl_max;
|
|
memcpy(repl_data, repl.data, copy);
|
|
}
|
|
if (neterrno)
|
|
return(-neterrno);
|
|
return(0);
|
|
}
|
|
|
|
static uint16 tests_ncp22_1a_reply_len(uint8 *data, uint16 maxlen)
|
|
{
|
|
if (!data || maxlen < 1)
|
|
return(0);
|
|
if ((uint16)(data[0] + 1) > maxlen)
|
|
return(maxlen);
|
|
return((uint16)(data[0] + 1));
|
|
}
|
|
|
|
static void tests_print_ncp22_1a_path(uint8 *data, uint16 len)
|
|
{
|
|
int i;
|
|
int plen;
|
|
|
|
if (!data || len < 1)
|
|
return;
|
|
|
|
plen = data[0];
|
|
if (plen + 1 > len)
|
|
plen = len - 1;
|
|
|
|
for (i = 0; i < plen; i++)
|
|
fputc(data[1 + i], stdout);
|
|
}
|
|
|
|
static int tests_ncp23map(char *path)
|
|
{
|
|
uint8 dhandle = 0;
|
|
C32_NDIR_INFO info;
|
|
int rc;
|
|
uint16 repl_len;
|
|
uint8 repl_data[300];
|
|
int i;
|
|
int num_count;
|
|
uint32 f4_dirnum = 0;
|
|
uint8 f4_volume = 0;
|
|
int f4_ok = 0;
|
|
uint32 nums[3];
|
|
char *names[3];
|
|
|
|
if (!path)
|
|
path = ".";
|
|
|
|
if (tests_current_dhandle(&dhandle)) {
|
|
fprintf(stdout, "NCP23MAP failed: current drive is not a network drive\n");
|
|
return(1);
|
|
}
|
|
|
|
memset(&info, 0, sizeof(info));
|
|
rc = c32_ncp87_obtain_ndir_info(tool_is_current_path(path) ? "" : path,
|
|
(uint16)dhandle,
|
|
&info,
|
|
NULL, NULL, NULL);
|
|
|
|
fprintf(stdout, "NCP23/F4+F3 layout probes for %s\n", path);
|
|
fprintf(stdout, "Current dir handle: %u\n", (unsigned)dhandle);
|
|
if (rc) {
|
|
fprintf(stdout, "NCP87 info rc=%d; F3 probes will use zero dir numbers\n", rc);
|
|
info.vol_number = 0;
|
|
info.dir_ent_num = 0;
|
|
info.dos_dir_num = 0;
|
|
} else {
|
|
fprintf(stdout, "NCP87 info vol=%lu dirEnt=%lu dosDir=%lu\n",
|
|
(unsigned long)info.vol_number,
|
|
(unsigned long)info.dir_ent_num,
|
|
(unsigned long)info.dos_dir_num);
|
|
}
|
|
|
|
fprintf(stdout, "\nNCP23 map calls\n");
|
|
fprintf(stdout, "%-12s %5s %5s %-12s %s\n", "call", "rc", "len", "reply-hex", "decoded");
|
|
fprintf(stdout, "%-12s %5s %5s %-12s %s\n", "------------", "-----", "-----", "------------", "----------------");
|
|
|
|
memset(repl_data, 0, sizeof(repl_data));
|
|
repl_len = 0;
|
|
rc = tests_ncp23_f4(dhandle, path, &repl_len, repl_data, sizeof(repl_data));
|
|
fprintf(stdout, "%-12s %5d %5u ", "f4 path", rc,
|
|
(!rc && repl_len >= 5) ? 5U : (unsigned)repl_len);
|
|
if (!rc && repl_len >= 5) {
|
|
f4_ok = 1;
|
|
f4_volume = repl_data[0];
|
|
f4_dirnum = GET_32(repl_data + 1);
|
|
tests_print_reply_bytes(repl_data, 5);
|
|
fprintf(stdout, " vol=%u dir=%lu", (unsigned)f4_volume, (unsigned long)f4_dirnum);
|
|
} else {
|
|
fprintf(stdout, "- ");
|
|
}
|
|
fprintf(stdout, "\n");
|
|
|
|
if (f4_ok && f4_dirnum <= 0xffffUL) {
|
|
memset(repl_data, 0, sizeof(repl_data));
|
|
repl_len = 0;
|
|
rc = tests_ncp22_1a(f4_volume, (uint16)f4_dirnum,
|
|
&repl_len, repl_data, sizeof(repl_data));
|
|
if (!rc)
|
|
repl_len = tests_ncp22_1a_reply_len(repl_data, repl_len);
|
|
fprintf(stdout, "%-12s %5d %5u ", "22/1A f4", rc, (unsigned)repl_len);
|
|
if (!rc) {
|
|
tests_print_reply_bytes(repl_data, repl_len);
|
|
fprintf(stdout, " ");
|
|
tests_print_ncp22_1a_path(repl_data, repl_len);
|
|
} else {
|
|
fprintf(stdout, "- ");
|
|
}
|
|
fprintf(stdout, "\n");
|
|
}
|
|
|
|
num_count = 0;
|
|
if (f4_ok) {
|
|
nums[num_count] = f4_dirnum;
|
|
names[num_count++] = "f3 f4dir";
|
|
}
|
|
nums[num_count] = info.dir_ent_num;
|
|
names[num_count++] = "f3 dirEnt";
|
|
nums[num_count] = info.dos_dir_num;
|
|
names[num_count++] = "f3 dosDir";
|
|
|
|
for (i = (f4_ok ? 1 : 0); i < num_count; i++) {
|
|
if (nums[i] > 0xffffUL)
|
|
continue;
|
|
memset(repl_data, 0, sizeof(repl_data));
|
|
repl_len = 0;
|
|
rc = tests_ncp22_1a((uint8)info.vol_number, (uint16)nums[i],
|
|
&repl_len, repl_data, sizeof(repl_data));
|
|
if (!rc)
|
|
repl_len = tests_ncp22_1a_reply_len(repl_data, repl_len);
|
|
fprintf(stdout, "%-12s %5d %5u ",
|
|
(i == (f4_ok ? 1 : 0)) ? "22/1A dir" : "22/1A dos",
|
|
rc, (unsigned)repl_len);
|
|
if (!rc) {
|
|
tests_print_reply_bytes(repl_data, repl_len);
|
|
fprintf(stdout, " ");
|
|
tests_print_ncp22_1a_path(repl_data, repl_len);
|
|
} else {
|
|
fprintf(stdout, "- ");
|
|
}
|
|
fprintf(stdout, "\n");
|
|
}
|
|
|
|
for (i = 0; i < num_count; i++) {
|
|
memset(repl_data, 0, sizeof(repl_data));
|
|
repl_len = 0;
|
|
rc = tests_ncp23_f3((uint8)info.vol_number, nums[i], 0,
|
|
&repl_len, repl_data, sizeof(repl_data));
|
|
if (!rc)
|
|
repl_len = tests_ncp23_path_reply_len(repl_data, repl_len);
|
|
fprintf(stdout, "%-12s %5d %5u ", names[i], rc, (unsigned)repl_len);
|
|
if (!rc) {
|
|
tests_print_reply_bytes(repl_data, repl_len);
|
|
fprintf(stdout, " ");
|
|
tests_print_ncp23_path(repl_data, repl_len);
|
|
} else {
|
|
fprintf(stdout, "- ");
|
|
}
|
|
fprintf(stdout, "\n");
|
|
}
|
|
|
|
fprintf(stdout, "\nExpected F4 reply is: volume byte + directory number dword (LO-HI).\n");
|
|
fprintf(stdout, "Expected F3 reply is: length-prefixed path components without volume.\n");
|
|
fprintf(stdout, "Expected 22/1A reply is: length byte + DOS path string.\n");
|
|
return(0);
|
|
}
|
|
|
|
|
|
static int tests_ncp22_eff_variant(int subfn, int variant,
|
|
uint8 dhandle, char *path,
|
|
uint8 *rights_out)
|
|
{
|
|
uint8 plen;
|
|
int subdir = 1;
|
|
|
|
if (!path)
|
|
path = "";
|
|
|
|
plen = (uint8)strlen(path);
|
|
if (rights_out)
|
|
*rights_out = 0;
|
|
|
|
/*
|
|
* Exploratory old NCP22 directory service calls.
|
|
*
|
|
* subfn 3 = Get Effective Directory Rights
|
|
* subfn 42 = Get Effective Rights (SDK symbol NWNCP22S42...)
|
|
*
|
|
* The exact DOS requester wrapper layout is not documented in our tree, so
|
|
* TESTS tries a few known 2.x/3.x style request shapes and reports which
|
|
* one succeeds. We do not use these in production until the matching
|
|
* Novell RIGHTS shape is identified.
|
|
*/
|
|
if (variant == 0) {
|
|
struct {
|
|
uint16 len;
|
|
uint8 func;
|
|
uint8 dirhandle;
|
|
uint8 pathlen;
|
|
uint8 path[256];
|
|
} req;
|
|
struct {
|
|
uint16 len;
|
|
uint8 rights;
|
|
} repl = { sizeof(repl) - sizeof(uint16) };
|
|
|
|
req.func = (uint8)subfn;
|
|
req.dirhandle = dhandle;
|
|
req.pathlen = plen;
|
|
req.len = 3 + plen;
|
|
strmaxcpy(req.path, path, plen);
|
|
|
|
neterrno = Net_Call(0xE200, &req, &repl);
|
|
if (neterrno)
|
|
return(-neterrno);
|
|
if (rights_out)
|
|
*rights_out = repl.rights;
|
|
return(0);
|
|
}
|
|
|
|
if (variant == 1) {
|
|
struct {
|
|
uint16 len;
|
|
uint8 func;
|
|
uint8 dirhandle;
|
|
uint8 sub_dir[2];
|
|
uint8 pathlen;
|
|
uint8 path[256];
|
|
} req;
|
|
struct {
|
|
uint16 len;
|
|
uint8 rights;
|
|
} repl = { sizeof(repl) - sizeof(uint16) };
|
|
|
|
req.func = (uint8)subfn;
|
|
req.dirhandle = dhandle;
|
|
U16_TO_BE16(subdir, req.sub_dir);
|
|
req.pathlen = plen;
|
|
req.len = 5 + plen;
|
|
strmaxcpy(req.path, path, plen);
|
|
|
|
neterrno = Net_Call(0xE200, &req, &repl);
|
|
if (neterrno)
|
|
return(-neterrno);
|
|
if (rights_out)
|
|
*rights_out = repl.rights;
|
|
return(0);
|
|
}
|
|
|
|
if (variant == 2) {
|
|
struct {
|
|
uint16 len;
|
|
uint8 func;
|
|
uint8 pathlen;
|
|
uint8 path[256];
|
|
} req;
|
|
struct {
|
|
uint16 len;
|
|
uint8 rights;
|
|
} repl = { sizeof(repl) - sizeof(uint16) };
|
|
|
|
req.func = (uint8)subfn;
|
|
req.pathlen = plen;
|
|
req.len = 2 + plen;
|
|
strmaxcpy(req.path, path, plen);
|
|
|
|
neterrno = Net_Call(0xE200, &req, &repl);
|
|
if (neterrno)
|
|
return(-neterrno);
|
|
if (rights_out)
|
|
*rights_out = repl.rights;
|
|
return(0);
|
|
}
|
|
|
|
return(-999);
|
|
}
|
|
|
|
|
|
static int tests_ncp22_50_obj_eff(uint32 object_id, uint8 dhandle,
|
|
char *path, uint16 *rights_out)
|
|
{
|
|
uint8 plen;
|
|
|
|
if (!path)
|
|
path = "";
|
|
|
|
plen = (uint8)strlen(path);
|
|
if (rights_out)
|
|
*rights_out = 0;
|
|
|
|
/*
|
|
* Client32/NCPWIN32 exports this as:
|
|
* NWNCP22s50GetObjEffectRights
|
|
*
|
|
* Disassembly of NCPWIN32.DLL shows request shape:
|
|
* word len = 7 + pathlen
|
|
* byte subfunction = 0x32
|
|
* dword object id, big endian
|
|
* byte dir handle
|
|
* byte path length
|
|
* path bytes
|
|
*
|
|
* Reply is a little-endian word effective rights mask.
|
|
*/
|
|
{
|
|
struct {
|
|
uint16 len;
|
|
uint8 func;
|
|
uint8 object_id[4];
|
|
uint8 dirhandle;
|
|
uint8 pathlen;
|
|
uint8 path[256];
|
|
} req;
|
|
struct {
|
|
uint16 len;
|
|
uint8 rights[2];
|
|
} repl = { sizeof(repl) - sizeof(uint16) };
|
|
|
|
req.func = 0x32;
|
|
U32_TO_BE32(object_id, req.object_id);
|
|
req.dirhandle = dhandle;
|
|
req.pathlen = plen;
|
|
req.len = 7 + plen;
|
|
strmaxcpy(req.path, path, plen);
|
|
|
|
neterrno = Net_Call(0xE200, &req, &repl);
|
|
if (neterrno)
|
|
return(-neterrno);
|
|
|
|
if (rights_out)
|
|
*rights_out = (uint16)(repl.rights[0] | ((uint16)repl.rights[1] << 8));
|
|
return(0);
|
|
}
|
|
}
|
|
|
|
static void tests_print_ncp22_obj50_row(char *label, uint32 object_id,
|
|
uint8 dhandle, char *path)
|
|
{
|
|
uint16 rights = 0;
|
|
uint8 mask = 0;
|
|
int rc;
|
|
|
|
rc = tests_ncp22_50_obj_eff(object_id, dhandle, path, &rights);
|
|
if (!rc)
|
|
mask = tests_map_ncp_mask(rights);
|
|
tests_print_eff_row(label, rc, mask, rights, !rc);
|
|
}
|
|
|
|
static void tests_print_ncp22_row(char *label, int subfn, int variant,
|
|
uint8 dhandle, char *path)
|
|
{
|
|
uint8 rights = 0;
|
|
int rc;
|
|
|
|
rc = tests_ncp22_eff_variant(subfn, variant, dhandle, path, &rights);
|
|
tests_print_eff_row(label, rc, rights, rights, !rc);
|
|
}
|
|
|
|
static int tests_effright(char *path)
|
|
{
|
|
uint8 dhandle = 0;
|
|
uint8 eff_old = 0;
|
|
uint8 mask = 0;
|
|
char usepath[260];
|
|
int newhandle;
|
|
uint16 ncp_rights = 0;
|
|
C32_NDIR_INFO info;
|
|
uint32 my_obj_id = 0;
|
|
uint16 my_obj_type = 0;
|
|
uint8 my_obj_name[48];
|
|
int rc;
|
|
|
|
if (!path)
|
|
path = ".";
|
|
|
|
if (tests_current_dhandle(&dhandle)) {
|
|
fprintf(stdout, "EFFRIGHT failed: current drive is not a network drive\n");
|
|
return(1);
|
|
}
|
|
|
|
fprintf(stdout, "EFFRIGHT diagnostics for %s\n", path);
|
|
fprintf(stdout, "Current dir handle: %u\n", (unsigned)dhandle);
|
|
|
|
{
|
|
int access_level;
|
|
|
|
access_level = ncp_14_46(&my_obj_id);
|
|
my_obj_name[0] = '\0';
|
|
if (access_level >= 0 && my_obj_id) {
|
|
ncp_17_36(my_obj_id, my_obj_name, &my_obj_type);
|
|
fprintf(stdout, "Current object: %08lX type=%04X access=%02X name=%s\n\n",
|
|
(unsigned long)my_obj_id, my_obj_type,
|
|
(unsigned)access_level, my_obj_name);
|
|
} else {
|
|
fprintf(stdout, "Current object: unknown rc=%d\n\n", access_level);
|
|
}
|
|
}
|
|
|
|
rc = c32_ncp87_obtain_ndir_info(tool_is_current_path(path) ? "" : path,
|
|
(uint16)dhandle,
|
|
&info,
|
|
NULL, NULL, NULL);
|
|
if (rc) {
|
|
fprintf(stdout, "NCP87 info rc=%d\n\n", rc);
|
|
} else {
|
|
tests_mask_string((uint8)info.inherited_rights, usepath);
|
|
fprintf(stdout, "NCP87 info vol=%lu dirEnt=%lu dosDir=%lu inh=[%s] %04X\n\n",
|
|
(unsigned long)info.vol_number,
|
|
(unsigned long)info.dir_ent_num,
|
|
(unsigned long)info.dos_dir_num,
|
|
usepath,
|
|
info.inherited_rights);
|
|
}
|
|
|
|
tests_print_eff_header("Effective rights matrix:");
|
|
|
|
rc = c32_ncp87_get_effective_rights(tool_is_current_path(path) ? "" : path,
|
|
(uint16)dhandle,
|
|
&ncp_rights,
|
|
NULL, NULL, NULL);
|
|
if (!rc)
|
|
mask = tests_map_ncp_mask(ncp_rights);
|
|
tests_print_eff_row("ncp87 path", rc, mask, ncp_rights, !rc);
|
|
|
|
if (!c32_ncp87_obtain_ndir_info(tool_is_current_path(path) ? "" : path,
|
|
(uint16)dhandle,
|
|
&info,
|
|
NULL, NULL, NULL)) {
|
|
rc = c32_ncp87_get_effective_rights_by_dirent((uint8)info.vol_number,
|
|
info.dos_dir_num,
|
|
&ncp_rights,
|
|
NULL, NULL, NULL);
|
|
if (!rc)
|
|
mask = tests_map_ncp_mask(ncp_rights);
|
|
tests_print_eff_row("ncp87 dosDir", rc, mask, ncp_rights, !rc);
|
|
|
|
rc = c32_ncp87_get_effective_rights_by_dirent((uint8)info.vol_number,
|
|
info.dir_ent_num,
|
|
&ncp_rights,
|
|
NULL, NULL, NULL);
|
|
if (!rc)
|
|
mask = tests_map_ncp_mask(ncp_rights);
|
|
tests_print_eff_row("ncp87 dirEnt", rc, mask, ncp_rights, !rc);
|
|
}
|
|
|
|
/* Old handle path used by our earlier RIGHTS fallback. */
|
|
tool_upcopy(usepath, path, sizeof(usepath));
|
|
newhandle = alloc_temp_dir_handle(dhandle, usepath, 0, &eff_old);
|
|
if (newhandle >= 0) {
|
|
dealloc_dir_handle(newhandle);
|
|
tests_print_eff_row("old handle path", 0, eff_old, eff_old, 1);
|
|
} else {
|
|
tests_print_eff_row("old handle path", newhandle, 0, 0, 0);
|
|
}
|
|
|
|
tests_parent_path(usepath, path, sizeof(usepath));
|
|
newhandle = alloc_temp_dir_handle(dhandle, usepath, 0, &eff_old);
|
|
if (newhandle >= 0) {
|
|
dealloc_dir_handle(newhandle);
|
|
tests_print_eff_row("old handle parent", 0, eff_old, eff_old, 1);
|
|
} else {
|
|
tests_print_eff_row("old handle parent", newhandle, 0, 0, 0);
|
|
}
|
|
|
|
if (usepath[0]) {
|
|
int subdir = 1;
|
|
int r = ncp_16_02(dhandle, (uint8 *)usepath, &subdir,
|
|
NULL, NULL, NULL);
|
|
if (r >= 0)
|
|
tests_print_eff_row("ncp16_02 parent", 0, (uint8)r, (uint16)r, 1);
|
|
else
|
|
tests_print_eff_row("ncp16_02 parent", r, 0, 0, 0);
|
|
}
|
|
|
|
tests_print_ncp22_row("ncp22/3 v0", 3, 0, dhandle, path);
|
|
tests_print_ncp22_row("ncp22/3 v1", 3, 1, dhandle, path);
|
|
tests_print_ncp22_row("ncp22/42 v0", 42, 0, dhandle, path);
|
|
tests_print_ncp22_row("ncp22/42 v1", 42, 1, dhandle, path);
|
|
|
|
if (my_obj_id)
|
|
tests_print_ncp22_obj50_row("obj50 path", my_obj_id, dhandle, path);
|
|
|
|
if (usepath[0]) {
|
|
tests_print_ncp22_row("p ncp22/3 v0", 3, 0, dhandle, usepath);
|
|
tests_print_ncp22_row("p ncp22/3 v1", 3, 1, dhandle, usepath);
|
|
tests_print_ncp22_row("p ncp22/42 v0", 42, 0, dhandle, usepath);
|
|
tests_print_ncp22_row("p ncp22/42 v1", 42, 1, dhandle, usepath);
|
|
|
|
if (my_obj_id)
|
|
tests_print_ncp22_obj50_row("p obj50 path", my_obj_id, dhandle, usepath);
|
|
}
|
|
|
|
fprintf(stdout, "\nCompare with Novell: NPUBLIC\\RIGHTS %s\n", path);
|
|
return(0);
|
|
}
|
|
|
|
int func_tests(int argc, char *argv[], int mode)
|
|
{
|
|
(void)mode;
|
|
|
|
if (argc < 2) {
|
|
tests_usage();
|
|
return(1);
|
|
}
|
|
|
|
if (tests_same_arg(argv[1], "NCP87C32ATTR"))
|
|
return tests_ncp87c32attr();
|
|
|
|
if (tests_same_arg(argv[1], "NCP87C32AUTO"))
|
|
return tests_ncp87c32auto();
|
|
|
|
if (tests_same_arg(argv[1], "EFFRIGHT")) {
|
|
if (argc < 3)
|
|
return tests_effright(".");
|
|
return tests_effright(argv[2]);
|
|
}
|
|
|
|
if (tests_same_arg(argv[1], "NCP23MAP") ||
|
|
tests_same_arg(argv[1], "NCP23F")) {
|
|
if (argc < 3)
|
|
return tests_ncp23map(".");
|
|
return tests_ncp23map(argv[2]);
|
|
}
|
|
|
|
if (tests_same_arg(argv[1], "NCP22REN") ||
|
|
tests_same_arg(argv[1], "NCP22S2E")) {
|
|
if (argc < 3)
|
|
return tests_ncp22ren(NULL, NULL);
|
|
if (argc < 4)
|
|
return tests_ncp22rendir(argv[2]);
|
|
return tests_ncp22ren(argv[2], argv[3]);
|
|
}
|
|
|
|
if (tests_same_arg(argv[1], "RENDIR") ||
|
|
tests_same_arg(argv[1], "DIRREN") ||
|
|
tests_same_arg(argv[1], "NCP22RENDIR") ||
|
|
tests_same_arg(argv[1], "NCP22DIRREN")) {
|
|
if (argc < 3)
|
|
return tests_ncp22rendir(".");
|
|
return tests_ncp22rendir(argv[2]);
|
|
}
|
|
|
|
if (tests_same_arg(argv[1], "NCP2225MISS")) {
|
|
if (argc < 3)
|
|
return tests_ncp2225miss(NULL);
|
|
return tests_ncp2225miss(argv[2]);
|
|
}
|
|
|
|
if (tests_same_arg(argv[1], "NCP2225ATTR")) {
|
|
if (argc < 3)
|
|
return tests_ncp2225attr(NULL, NULL);
|
|
if (argc < 4)
|
|
return tests_ncp2225attr(argv[2], NULL);
|
|
return tests_ncp2225attr(argv[2], argv[3]);
|
|
}
|
|
|
|
if (tests_same_arg(argv[1], "NCP2225TIME")) {
|
|
if (argc < 3)
|
|
return tests_ncp2225time(NULL, NULL, NULL);
|
|
if (argc < 5)
|
|
return tests_ncp2225time(argv[2], NULL, NULL);
|
|
return tests_ncp2225time(argv[2], argv[3], argv[4]);
|
|
}
|
|
|
|
if (tests_same_arg(argv[1], "NCP2225ADATE")) {
|
|
if (argc < 3)
|
|
return tests_ncp2225adate(NULL, NULL);
|
|
if (argc < 4)
|
|
return tests_ncp2225adate(argv[2], NULL);
|
|
return tests_ncp2225adate(argv[2], argv[3]);
|
|
}
|
|
|
|
if (tests_same_arg(argv[1], "NCP2225MAXSPACE")) {
|
|
if (argc < 3)
|
|
return tests_ncp2225maxspace(NULL, NULL);
|
|
if (argc < 4)
|
|
return tests_ncp2225maxspace(argv[2], NULL);
|
|
return tests_ncp2225maxspace(argv[2], argv[3]);
|
|
}
|
|
|
|
if (tests_same_arg(argv[1], "NCP221EINFO")) {
|
|
if (argc < 3)
|
|
return tests_ncp221einfo(".");
|
|
return tests_ncp221einfo(argv[2]);
|
|
}
|
|
|
|
if (tests_same_arg(argv[1], "NCP2225MODID")) {
|
|
if (argc < 3)
|
|
return tests_ncp2225modid(NULL, NULL);
|
|
if (argc < 4)
|
|
return tests_ncp2225modid(argv[2], NULL);
|
|
return tests_ncp2225modid(argv[2], argv[3]);
|
|
}
|
|
|
|
if (tests_same_arg(argv[1], "NCP2225CREATE")) {
|
|
if (argc < 3)
|
|
return tests_ncp2225create(NULL, NULL, NULL, NULL);
|
|
if (argc < 6)
|
|
return tests_ncp2225create(argv[2], NULL, NULL, NULL);
|
|
return tests_ncp2225create(argv[2], argv[3], argv[4], argv[5]);
|
|
}
|
|
|
|
if (tests_same_arg(argv[1], "NCP2225ARCH")) {
|
|
if (argc < 3)
|
|
return tests_ncp2225arch(NULL, NULL, NULL, NULL);
|
|
if (argc < 6)
|
|
return tests_ncp2225arch(argv[2], NULL, NULL, NULL);
|
|
return tests_ncp2225arch(argv[2], argv[3], argv[4], argv[5]);
|
|
}
|
|
|
|
if (tests_same_arg(argv[1], "NCP22S4") ||
|
|
tests_same_arg(argv[1], "MAXRIGHT") ||
|
|
tests_same_arg(argv[1], "IRM")) {
|
|
if (argc < 3)
|
|
return tests_ncp22s4(".", NULL);
|
|
if (argc < 4)
|
|
return tests_ncp22s4(argv[2], NULL);
|
|
return tests_ncp22s4(argv[2], argv[3]);
|
|
}
|
|
|
|
tests_usage();
|
|
return(1);
|
|
}
|