Files
mars-dosutils/rights.c
Mario Fetka 4ad455c6df dosutils: add maintainer helpers and compare-ready tools
Add maintainer-only support used by the automated DOS compatibility
tests.

This introduces the MAINTAINER_BUILD option for the DOS tools. In
maintainer builds, LOGIN accepts the hidden /PWD: and /PASSWORD:
arguments for automated test relogin, and the DLYSTRT helper is built to
delay-start DOS batch files after the invoking batch has returned to the
prompt.

Add the WHOAMI utility and wire it into the NET command dispatch. Also
adjust SLIST and RIGHTS output to match Novell behavior more closely,
including server-not-found handling, path formatting, Supervisor rights,
missing-path errors and usage text.

Extend the test scripts to compare NPUBLIC Novell baselines against the
PUBLIC implementations. LOGIN/LOGOUT can now run automatically via
DLYSTRT and the maintainer LOGIN password option. RIGHTS gains an
additional NOPASSUSER effective-rights matrix that covers single rights,
mixed rights, Supervisor rights, ALL/N and file trustee cases.

Normal builds remain free of maintainer-only helpers and hidden password
handling.
2026-05-27 20:14:01 +02:00

321 lines
8.4 KiB
C

/* rights.c - Novell RIGHTS-like DOS utility, read-only v4 */
#include "net.h"
#include "c32ncp.h"
#include <dos.h>
#include <sys/types.h>
#include <sys/stat.h>
#ifndef S_IFDIR
#define S_IFDIR 0040000
#endif
#define NW_RIGHT_S 0x01
#define NW_RIGHT_R 0x02
#define NW_RIGHT_W 0x04
#define NW_RIGHT_C 0x08
#define NW_RIGHT_E 0x10
#define NW_RIGHT_M 0x20
#define NW_RIGHT_F 0x40
#define NW_RIGHT_A 0x80
/* NCP effective-rights bits returned by NCP87 subfunction 29. */
#define NCP_RIGHT_READ 0x0001
#define NCP_RIGHT_WRITE 0x0002
#define NCP_RIGHT_OPEN 0x0004
#define NCP_RIGHT_CREATE 0x0008
#define NCP_RIGHT_DELETE 0x0010
#define NCP_RIGHT_OWNER 0x0020
#define NCP_RIGHT_SEARCH 0x0040
#define NCP_RIGHT_MODIFY 0x0080
#define NCP_RIGHT_SUPER 0x0100
static uint8 rights_map_ncp_mask(uint16 ncp_rights);
static void rights_ncp_path(char *dst, char *src, int max);
static void rights_usage(void)
{
fprintf(stdout, "\nUsage: RIGHTS [path]\n \n");
fprintf(stdout, " Rights = All | Supervisor | Read | Write | Create | Erase\n");
fprintf(stdout, " | Modify | File scan | Access Control\n");
}
static void rights_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 rights_ncp_path(char *dst, char *src, int max)
{
char up[260];
char *p;
if (tool_is_current_path(src)) {
dst[0] = '\0';
return;
}
tool_upcopy(up, src, sizeof(up));
p = up;
if (p[0] && p[1] == ':') {
p += 2;
if (*p == '\\') p++;
}
while (*p == '\\') p++;
strmaxcpy(dst, p, max - 1);
}
static int rights_get_ncp_info(char *path, C32_NDIR_INFO *info)
{
uint8 connid = 0;
uint8 dhandle = 0;
char npath[260];
if (!info)
return(1);
memset(info, 0, sizeof(*info));
if (tool_current_dhandle(&connid, &dhandle))
return(1);
rights_ncp_path(npath, path, sizeof(npath));
return(c32_ncp87_obtain_ndir_info(npath, (uint16)dhandle,
info, NULL, NULL, NULL));
}
static int rights_path_exists(char *path, int *is_dir)
{
C32_NDIR_INFO info;
if (rights_get_ncp_info(path, &info))
return(0);
if (is_dir)
*is_dir = (info.attributes & 0x10) ? 1 : 0;
return(1);
}
static int rights_effective_mask(char *path, int is_dir, uint8 *mask)
{
uint8 connid = 0;
uint8 dhandle = 0;
uint8 eff = 0;
char usepath[260];
int newhandle;
uint16 ncp_rights = 0;
if (mask) *mask = 0;
if (tool_current_dhandle(&connid, &dhandle))
return(-1);
/*
* Prefer the explicit Client32 NCP87 effective-rights request. This is
* the DOS Client32 equivalent of ncpfs ncp_get_eff_directory_rights().
* It works for both files and directories, so pass the requested path
* itself instead of mapping files to their parent directory.
*/
rights_ncp_path(usepath, path, sizeof(usepath));
if (!c32_ncp87_get_effective_rights(usepath,
(uint16)dhandle,
&ncp_rights,
NULL, NULL, NULL)) {
if (mask) *mask = rights_map_ncp_mask(ncp_rights);
return(0);
}
/*
* Fallback for older requesters/paths: allocate a temporary directory
* handle and use the returned old-style effective-rights byte.
* This cannot directly target a file, so files are mapped to their parent.
*/
if (tool_is_current_path(path)) {
usepath[0] = '\0';
} else if (is_dir) {
rights_ncp_path(usepath, path, sizeof(usepath));
} else {
char npath[260];
rights_ncp_path(npath, path, sizeof(npath));
rights_parent_path(usepath, npath, sizeof(usepath));
}
newhandle = alloc_temp_dir_handle(dhandle, usepath, 0, &eff);
if (newhandle < 0) {
if (usepath[0]) {
int subdir = 1;
int r = ncp_16_02(dhandle, (uint8 *)usepath, &subdir,
NULL, NULL, NULL);
if (r >= 0) {
eff = (uint8)r;
} else {
return(-1);
}
} else {
eff = 0xff;
}
} else {
dealloc_dir_handle(newhandle);
}
if (mask) *mask = eff;
return(0);
}
static uint8 rights_map_ncp_mask(uint16 ncp_rights)
{
uint8 mask = 0;
if (ncp_rights & NCP_RIGHT_SUPER) mask |= NW_RIGHT_S;
if (ncp_rights & NCP_RIGHT_READ) mask |= NW_RIGHT_R;
if (ncp_rights & NCP_RIGHT_WRITE) mask |= NW_RIGHT_W;
if (ncp_rights & NCP_RIGHT_CREATE) mask |= NW_RIGHT_C;
if (ncp_rights & NCP_RIGHT_DELETE) mask |= NW_RIGHT_E;
if (ncp_rights & NCP_RIGHT_MODIFY) mask |= NW_RIGHT_M;
if (ncp_rights & NCP_RIGHT_SEARCH) mask |= NW_RIGHT_F;
if (ncp_rights & NCP_RIGHT_OWNER) mask |= NW_RIGHT_A;
return(mask);
}
static void rights_mask_string(uint8 mask, char *out)
{
out[0] = (mask & NW_RIGHT_S) ? 'S' : ' ';
out[1] = (mask & NW_RIGHT_R) ? 'R' : ' ';
out[2] = (mask & NW_RIGHT_W) ? 'W' : ' ';
out[3] = (mask & NW_RIGHT_C) ? 'C' : ' ';
out[4] = (mask & NW_RIGHT_E) ? 'E' : ' ';
out[5] = (mask & NW_RIGHT_M) ? 'M' : ' ';
out[6] = (mask & NW_RIGHT_F) ? 'F' : ' ';
out[7] = (mask & NW_RIGHT_A) ? 'A' : ' ';
out[8] = '\0';
}
/*
* Novell RIGHTS layout:
* - lines with star: two leading blanks, star, blank, text
* - lines without star: four leading blanks, text
* Keep the right-column letter at a fixed-ish DOS-screen position.
*/
static void rights_print_line(int have, int star, char *text, char letter)
{
(void)have;
if (star)
fprintf(stdout, " * %-43s(%c)\n", text, letter);
else
fprintf(stdout, " %-43s(%c)\n", text, letter);
}
static void rights_display(char *path, int is_dir, uint8 mask)
{
char hdr[300];
char mstr[10];
tool_header_path(hdr, path, sizeof(hdr));
rights_mask_string(mask, mstr);
fprintf(stdout, "%s\n", hdr);
if (is_dir)
fprintf(stdout, "Your Effective Rights for this directory are [%s]\n", mstr);
else
fprintf(stdout, "Your Effective Rights for this file are [%s]\n", mstr);
if (is_dir) {
rights_print_line(mask & NW_RIGHT_S, 0, "You have Supervisor Rights to Directory.", 'S');
rights_print_line(mask & NW_RIGHT_R, 1, "May Read from File.", 'R');
rights_print_line(mask & NW_RIGHT_W, 1, "May Write to File.", 'W');
rights_print_line(mask & NW_RIGHT_C, 0, "May Create Subdirectories and Files.", 'C');
rights_print_line(mask & NW_RIGHT_E, 0, "May Erase Directory.", 'E');
rights_print_line(mask & NW_RIGHT_M, 0, "May Modify Directory.", 'M');
rights_print_line(mask & NW_RIGHT_F, 0, "May Scan for Files.", 'F');
rights_print_line(mask & NW_RIGHT_A, 0, "May Change Access Control.", 'A');
fprintf(stdout, "\n");
fprintf(stdout, "* Has no effect on directory.\n\n");
fprintf(stdout, " Entries in Directory May Inherit [%s] rights.\n", mstr);
if (mask == 0xff)
fprintf(stdout, " You have ALL RIGHTS to Directory Entry.\n");
} else {
rights_print_line(mask & NW_RIGHT_S, 0, "You have Supervisor Rights to File.", 'S');
rights_print_line(mask & NW_RIGHT_R, 0, "May Read from File.", 'R');
rights_print_line(mask & NW_RIGHT_W, 0, "May Write to File.", 'W');
rights_print_line(mask & NW_RIGHT_C, 1, "May Create Subdirectories and Files.", 'C');
rights_print_line(mask & NW_RIGHT_E, 0, "May Erase File.", 'E');
rights_print_line(mask & NW_RIGHT_M, 0, "May Modify File.", 'M');
rights_print_line(mask & NW_RIGHT_F, 0, "May Scan for File.", 'F');
rights_print_line(mask & NW_RIGHT_A, 0, "May Change Access Control.", 'A');
fprintf(stdout, "\n");
fprintf(stdout, "* Create is necessary to salvage a file that has been deleted.\n\n");
if (mask == 0xff)
fprintf(stdout, " You have ALL RIGHTS to Directory Entry.\n");
}
}
int func_rights(int argc, char *argv[], int mode)
{
char *path = ".";
uint8 mask = 0;
int is_dir;
(void)mode;
if (argc > 2) {
fprintf(stdout, "Too many parameters on command line.\n");
rights_usage();
return(1);
}
if (argc == 2) {
if (tool_is_help_arg(argv[1])) {
rights_usage();
return(0);
}
path = argv[1];
}
if (!rights_path_exists(path, &is_dir)) {
fprintf(stdout, "Specified path not locatable.\n");
rights_usage();
return(1);
}
if (rights_effective_mask(path, is_dir, &mask)) {
fprintf(stdout, "Specified path not locatable.\n");
rights_usage();
return(1);
}
rights_display(path, is_dir, mask);
return(0);
}