/* 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, " "); 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); }