Files
mars-dosutils/revoke.c
Mario Fetka 34ec41e760 trustee: share trustee header path formatting
Move the shared GRANT/REMOVE/REVOKE header path formatting into trustee.c.

The Novell trustee tools display the server and volume separator as
SERVER/SYS: while keeping the remaining path separators as DOS backslashes.
Use one trustee_header_path() helper for that formatting instead of keeping
separate local copies in GRANT, REMOVE and REVOKE.

No behavior change.
2026-05-29 12:33:32 +02:00

362 lines
10 KiB
C

/*
* mars-nwe-dosutils - NetWare/DOS utility tools.
*
* Copyright (C) 2026 Mario Fetka
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
/*
* Purpose: REVOKE utility for deleting trustee assignments from NetWare directories and files.
* Depends on: net.h, ncpapi.h, trustee.h/trustee.c trustee helpers, netcall.c requester helpers, tools.c shared utility routines.
*/
#include "net.h"
#include "ncpapi.h"
#include "trustee.h"
#ifndef _A_NORMAL
#define _A_NORMAL 0x00
#endif
static int revoke_last_rc = 0;
static void revoke_usage_short(int leading_blank, int bell_first)
{
if (bell_first)
fprintf(stdout, "\a");
if (leading_blank || bell_first)
fprintf(stdout, "\n");
fprintf(stdout, "Usage: REVOKE rightslist* [FOR path] FROM [USER|GROUP] name [options]\n");
fprintf(stdout, "Options: /SubDirectories | /Files\n");
fprintf(stdout, " ");
if (bell_first)
fprintf(stdout, "\n");
}
static void revoke_usage_full(int leading_blank, int bell_before_tail)
{
if (leading_blank)
fprintf(stdout, "\n");
fprintf(stdout, "Usage: REVOKE rightslist* [FOR path] FROM [USER|GROUP] name [options]\n");
fprintf(stdout, "Options: /SubDirectories | /Files\n");
fprintf(stdout, " \n");
fprintf(stdout, "286 Rights:\t\t386 Rights:\n");
fprintf(stdout, "---------------\t\t--------------------\n");
fprintf(stdout, "ALL = All\t\tALL = All\n");
fprintf(stdout, "R = Read\t\tS = Supervisor\n");
fprintf(stdout, "W = Write\t\tR = Read\n");
fprintf(stdout, "O = Open\t\tW = Write\n");
fprintf(stdout, "C = Create\t\tC = Create\n");
fprintf(stdout, "D = Delete\t\tE = Erase\n");
fprintf(stdout, "P = Parental\t\tM = Modify\n");
fprintf(stdout, "S = Search\t\tF = File Scan\n");
fprintf(stdout, "M = Modify\t\tA = Access Control\n");
fprintf(stdout, "\n");
if (bell_before_tail)
fprintf(stdout, "\a");
fprintf(stdout, "* Use abbreviations listed above, separated by spaces.\n");
}
static int revoke_one(char *path, uint16 dhandle, uint32 object_id,
uint16 revoke_mask, int forced_is_file)
{
uint16 old_rights = 0;
uint16 new_rights;
int is_dir;
int rc;
/* Without /FILES, Novell REVOKE reports directory-oriented messages even
* if the path cannot be located. */
is_dir = forced_is_file ? 0 : 1;
rc = ncp87_05_find_trustee_rights(path, dhandle, object_id, &old_rights,
NULL, NULL, NULL);
revoke_last_rc = rc;
if (rc) {
if (rc == 0xff)
fprintf(stdout, "\aNo trustee for the specified %s.\n", is_dir ? "directory" : "file");
else
fprintf(stdout, "\aError scanning trustee list.\n");
return(1);
}
new_rights = (uint16)(old_rights & ~revoke_mask);
if (new_rights == 0) {
/* Novell REVOKE removes the trustee through the old NCP22 trustee path.
* Keep the Client32/NCP87 delete call as a compatibility fallback. */
rc = ncp22_2b_delete_trustee_rights(path, dhandle, object_id);
if (rc)
rc = ncp87_0b_delete_trustee_rights(path, dhandle, object_id,
NULL, NULL, NULL);
revoke_last_rc = rc;
if (rc) {
fprintf(stdout, "Error deleting trustee.\n");
return(1);
}
} else {
/* Updating the remaining trustee mask is the same old SetTrustee flow
* used by GRANT. Fall back to NCP87 add-trustee if a server/client
* does not accept NCP22/27. */
rc = ncp22_27_set_trustee_rights(path, dhandle, object_id, new_rights);
if (rc)
rc = ncp87_0a_add_trustee_rights(path, dhandle, object_id, new_rights,
0xffff, NULL, NULL, NULL);
revoke_last_rc = rc;
if (rc) {
fprintf(stdout, "Fatal error revoking access rights.\n");
return(1);
}
}
{
char header[300];
char bracket[10];
trustee_header_path(header, path, sizeof(header));
trustee_rights_bracket(new_rights, bracket);
fprintf(stdout, "%s\n", header);
fprintf(stdout, "Trustee's access rights set to [%s]\r\n\n", bracket);
}
return(0);
}
static int revoke_subdirs_inner(char *path, uint16 dhandle, uint32 object_id,
uint16 revoke_mask, int *count,
int include_this)
{
struct find_t ff;
char pattern[260];
char child[260];
/* Novell REVOKE /SUBDIRECTORIES follows the REMOVE behaviour: the
* specified start directory is a container for the recursive operation,
* not itself part of the operation. Once a listed subdirectory reports
* no direct trustee, Novell stops after that first failure. */
if (include_this) {
if (revoke_one(path, dhandle, object_id, revoke_mask, 0) == 0)
(*count)++;
else
return(1);
}
tool_join_path(pattern, path, "*.*", sizeof(pattern));
if (_dos_findfirst(pattern, _A_SUBDIR, &ff) == 0) {
do {
if ((ff.attrib & _A_SUBDIR) && !tool_is_dot_dir(ff.name)) {
tool_join_path(child, path, ff.name, sizeof(child));
if (revoke_subdirs_inner(child, dhandle, object_id, revoke_mask,
count, 1))
return(1);
}
} while (_dos_findnext(&ff) == 0);
}
return(0);
}
static int revoke_subdirs(char *path, uint16 dhandle, uint32 object_id,
uint16 revoke_mask, int *count)
{
return(revoke_subdirs_inner(path, dhandle, object_id, revoke_mask,
count, 0));
}
static int revoke_files(char *path, uint16 dhandle, uint32 object_id,
uint16 revoke_mask, int *count)
{
struct find_t ff;
char dir[260];
char pat[80];
char spec[260];
char target[260];
int rc = 0;
if (trustee_path_is_dir(path)) {
strmaxcpy(dir, path, sizeof(dir) - 1);
strmaxcpy(pat, "*.*", sizeof(pat) - 1);
} else if (tool_has_wildcards(path)) {
tool_parent_pattern(dir, pat, path, sizeof(dir), sizeof(pat));
} else {
if (revoke_one(path, dhandle, object_id, revoke_mask, 1) == 0)
(*count)++;
else
rc = 1;
return(rc);
}
tool_join_path(spec, dir, pat, sizeof(spec));
if (_dos_findfirst(spec, _A_NORMAL | _A_HIDDEN | _A_SYSTEM | _A_ARCH, &ff) == 0) {
do {
if (!(ff.attrib & _A_SUBDIR)) {
tool_join_path(target, dir, ff.name, sizeof(target));
if (revoke_one(target, dhandle, object_id, revoke_mask, 1) == 0)
(*count)++;
else
rc = 1;
}
} while (_dos_findnext(&ff) == 0);
}
return(rc);
}
int func_revoke(int argc, char *argv[], int mode)
{
uint16 rights = 0;
char *path = ".";
char *objname = NULL;
char objprint[48];
uint16 objtype = TRUSTEE_BINDERY_USER;
int objtype_given = 0;
uint8 connid = 0;
uint8 dhandle = 0;
uint32 object_id;
int use_subdirs = 0;
int use_files = 0;
int count = 0;
int i = 1;
int have_rights = 0;
int rc;
(void)mode;
if (argc < 2 || tool_is_help_arg(argv[1])) {
if (argc < 2)
revoke_usage_full(1, 1);
else
revoke_usage_short(1, 0);
return(argc < 2 ? 1 : 0);
}
while (i < argc) {
if (tool_strsame(argv[i], "FOR") || tool_strsame(argv[i], "FROM"))
break;
if (tool_is_option(argv[i]))
break;
if (trustee_parse_right_word(argv[i], &rights)) {
fprintf(stdout, "Invalid right specified.\n\n");
revoke_usage_full(0, 1);
return(1);
}
have_rights = 1;
i++;
}
if (!have_rights || i >= argc) {
revoke_usage_short(0, 1);
return(1);
}
if (tool_strsame(argv[i], "FOR")) {
i++;
if (i >= argc) {
revoke_usage_short(0, 1);
return(1);
}
path = argv[i++];
}
if (i >= argc || !tool_strsame(argv[i], "FROM")) {
revoke_usage_short(0, 1);
return(1);
}
i++;
if (i < argc && tool_strsame(argv[i], "USER")) {
objtype = TRUSTEE_BINDERY_USER;
objtype_given = 1;
i++;
} else if (i < argc && tool_strsame(argv[i], "GROUP")) {
objtype = TRUSTEE_BINDERY_GROUP;
objtype_given = 1;
i++;
}
if (i >= argc) {
revoke_usage_short(0, 1);
return(1);
}
objname = argv[i++];
while (i < argc) {
if (!tool_is_option(argv[i])) {
revoke_usage_short(0, 1);
return(1);
}
if (tool_is_files_option(argv[i])) {
use_files = 1;
i++;
continue;
}
if (tool_is_subdirs_option(argv[i])) {
use_subdirs = 1;
i++;
continue;
}
revoke_usage_short(0, 1);
return(1);
}
/* Novell REVOKE does not emit a grammar-specific message for /FILES plus
* /SUBDIRECTORIES. It reports the same directory trustee failure text. */
if (tool_current_dhandle(&connid, &dhandle)) {
fprintf(stdout, "Error: Drive not mapped to Network.\n");
return(1);
}
object_id = trustee_lookup_object(objname, &objtype, objtype_given);
if (!object_id) {
if (objtype_given && objtype == TRUSTEE_BINDERY_GROUP)
fprintf(stdout, "\aGroup \"%s\" not found.\n", objname);
else if (objtype_given)
fprintf(stdout, "\aUser \"%s\" not found.\n", objname);
else
fprintf(stdout, "\aUser or group \"%s\" not found.\n", objname);
return(1);
}
tool_upcopy(objprint, objname, sizeof(objprint));
if (use_files && use_subdirs) {
fprintf(stdout, "\aNo trustee for the specified directory.\n");
revoke_last_rc = 0xff;
return(1);
} else if (use_subdirs)
rc = revoke_subdirs(path, (uint16)dhandle, object_id, rights, &count);
else if (use_files)
rc = revoke_files(path, (uint16)dhandle, object_id, rights, &count);
else {
rc = revoke_one(path, (uint16)dhandle, object_id, rights, 0);
if (!rc)
count = 1;
}
if ((use_subdirs || (!use_files && !rc)) && count > 0)
fprintf(stdout, "Rights for %d directories were changed for %s.\n\n", count, objprint);
else if (use_files && count > 0)
fprintf(stdout, "Rights for %d files were changed for %s.\n\n", count, objprint);
return(rc ? (revoke_last_rc ? revoke_last_rc : 1) : 0);
}