- add RIGHTS as a new multi-call DOS utility - implement Client32 NCP87 effective-rights query helper - map NCP effective-rights bits to Novell RIGHTS display order - keep legacy directory-handle based rights lookup as fallback - match Novell RIGHTS output for directories and files more closely - remove hardcoded MARS/SYS and SYS display fallbacks from RIGHTS/FLAGDIR - derive display prefixes from the active network drive where available - adjust FLAGDIR output formatting to match Novell field alignment - keep FLAG and SLIST unchanged after checking for hardcoded prefixes
409 lines
9.8 KiB
C
409 lines
9.8 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 int rights_same(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 int rights_is_help(char *s)
|
|
{
|
|
if (!s) return(0);
|
|
return(rights_same(s, "/?") || rights_same(s, "-?") || rights_same(s, "?"));
|
|
}
|
|
|
|
static void rights_usage(void)
|
|
{
|
|
fprintf(stdout, "Usage: RIGHTS [path]\n\n");
|
|
fprintf(stdout, "Rights = All | Supervisor | Read | Write | Create | Erase\n");
|
|
fprintf(stdout, " Modify | File scan | Access Control\n");
|
|
}
|
|
|
|
static int rights_get_current_drive(void)
|
|
{
|
|
REGS regs;
|
|
|
|
regs.h.ah = 0x19;
|
|
int86(0x21, ®s, ®s);
|
|
return((int)regs.h.al);
|
|
}
|
|
|
|
static int rights_current_dhandle(uint8 *connid, uint8 *dhandle)
|
|
{
|
|
uint8 flags = 0;
|
|
int drive = rights_get_current_drive();
|
|
|
|
if (get_drive_info((uint8)drive, connid, dhandle, &flags))
|
|
return(-1);
|
|
|
|
if (!*connid || (flags & 0x80))
|
|
return(-1);
|
|
|
|
return(0);
|
|
}
|
|
|
|
static int rights_is_current_path(char *path)
|
|
{
|
|
if (!path || !*path) return(1);
|
|
if (rights_same(path, ".")) return(1);
|
|
if (rights_same(path, ".\\")) return(1);
|
|
if (rights_same(path, "./")) return(1);
|
|
return(0);
|
|
}
|
|
|
|
static void rights_upcopy(char *dst, char *src, int max)
|
|
{
|
|
int i = 0;
|
|
|
|
if (!src) src = "";
|
|
|
|
while (*src && i < max - 1) {
|
|
char c = *src++;
|
|
if (c == '/') c = '\\';
|
|
if (c >= 'a' && c <= 'z') c -= 32;
|
|
dst[i++] = c;
|
|
}
|
|
dst[i] = 0;
|
|
}
|
|
|
|
static void rights_parent_path(char *dst, char *src, int max)
|
|
{
|
|
char tmp[260];
|
|
char *p;
|
|
|
|
rights_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 int rights_path_is_dir(char *path)
|
|
{
|
|
struct stat st;
|
|
|
|
if (rights_is_current_path(path))
|
|
return(1);
|
|
|
|
if (stat(path, &st) == 0) {
|
|
if (st.st_mode & S_IFDIR)
|
|
return(1);
|
|
}
|
|
|
|
return(0);
|
|
}
|
|
|
|
|
|
static int rights_current_prefix(char *out, int max)
|
|
{
|
|
uint8 connid = 0;
|
|
uint8 dhandle = 0;
|
|
uint8 flags = 0;
|
|
int drive;
|
|
char server[52];
|
|
char dpath[260];
|
|
char volume[32];
|
|
char *p;
|
|
int i = 0;
|
|
|
|
if (!out || max < 8)
|
|
return(-1);
|
|
|
|
out[0] = '\0';
|
|
|
|
drive = rights_get_current_drive();
|
|
if (get_drive_info((uint8)drive, &connid, &dhandle, &flags))
|
|
return(-1);
|
|
|
|
if (!connid || (flags & 0x80))
|
|
return(-1);
|
|
|
|
server[0] = '\0';
|
|
if (get_fs_name(connid, server))
|
|
server[0] = '\0';
|
|
|
|
dpath[0] = '\0';
|
|
if (get_dir_path(dhandle, dpath) || !dpath[0])
|
|
return(-1);
|
|
|
|
p = strchr(dpath, ':');
|
|
if (!p)
|
|
return(-1);
|
|
|
|
while (dpath + i < p && i < (int)sizeof(volume) - 1) {
|
|
volume[i] = dpath[i];
|
|
i++;
|
|
}
|
|
volume[i] = '\0';
|
|
|
|
if (!volume[0])
|
|
return(-1);
|
|
|
|
if (server[0])
|
|
sprintf(out, "%s/%s:", server, volume);
|
|
else
|
|
sprintf(out, "%s:", volume);
|
|
|
|
return(0);
|
|
}
|
|
|
|
|
|
static void rights_make_header_path(char *out, char *path)
|
|
{
|
|
char up[260];
|
|
char prefix[90];
|
|
|
|
if (rights_current_prefix(prefix, sizeof(prefix)))
|
|
prefix[0] = '\0';
|
|
|
|
if (rights_is_current_path(path)) {
|
|
strcpy(out, prefix);
|
|
return;
|
|
}
|
|
|
|
rights_upcopy(up, path, sizeof(up));
|
|
strcpy(out, prefix);
|
|
strcat(out, up);
|
|
}
|
|
|
|
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 (rights_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.
|
|
*/
|
|
if (!c32_ncp87_get_effective_rights(rights_is_current_path(path) ? "" : path,
|
|
(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 (rights_is_current_path(path)) {
|
|
usepath[0] = '\0';
|
|
} else if (is_dir) {
|
|
rights_upcopy(usepath, path, sizeof(usepath));
|
|
} else {
|
|
rights_parent_path(usepath, path, 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];
|
|
|
|
rights_make_header_path(hdr, path);
|
|
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) {
|
|
rights_usage();
|
|
return(1);
|
|
}
|
|
|
|
if (argc == 2) {
|
|
if (rights_is_help(argv[1])) {
|
|
rights_usage();
|
|
return(0);
|
|
}
|
|
path = argv[1];
|
|
}
|
|
|
|
is_dir = rights_path_is_dir(path);
|
|
|
|
if (rights_effective_mask(path, is_dir, &mask)) {
|
|
fprintf(stdout, "Specified path not locatable.\n");
|
|
return(1);
|
|
}
|
|
|
|
rights_display(path, is_dir, mask);
|
|
return(0);
|
|
}
|