Add GPL-2-or-later license headers to the DOS utility source files and document the purpose and local dependencies of each C, header and assembler file. Preserve the original Martin Stover copyright attribution for the historic MARS-NWE utility sources, including files that did not previously carry an explicit header but are part of the original tool set. Add Mario Fetka as the 2026 copyright holder for the current maintenance work, and use Mario-only headers for files without original Martin Stover ownership. Also add a root-level COPYING file containing the GPL-2 license text.
319 lines
8.7 KiB
C
319 lines
8.7 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: REMOVE utility for removing trustee rights from NetWare directories and files.
|
|
* Depends on: net.h, c32ncp.h, trustee.h/trustee.c trustee helpers, netcall.c requester helpers, tools.c shared utility routines.
|
|
*/
|
|
|
|
#include "net.h"
|
|
#include "c32ncp.h"
|
|
#include "trustee.h"
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
|
|
#ifndef _A_NORMAL
|
|
#define _A_NORMAL 0x00
|
|
#endif
|
|
|
|
static int remove_last_rc = 0;
|
|
|
|
static void remove_usage(void)
|
|
{
|
|
fprintf(stdout, "\n");
|
|
fprintf(stdout, "Usage: REMOVE [USER | GROUP] name [FROM path] [option]\n");
|
|
fprintf(stdout, "Options: /Subdirs | /Files \n");
|
|
}
|
|
|
|
static void remove_usage_after_error(void)
|
|
{
|
|
fprintf(stdout, "\007\n");
|
|
fprintf(stdout, "Usage: REMOVE [USER | GROUP] name [FROM path] [option]\n");
|
|
fprintf(stdout, "Options: /Subdirs | /Files \n");
|
|
fprintf(stdout, "\n");
|
|
}
|
|
|
|
static void remove_header_path(char *out, char *path, int max)
|
|
{
|
|
char *p;
|
|
|
|
trustee_header_path(out, path, max);
|
|
|
|
/* Novell REMOVE displays server and volume as SERVER/SYS: while
|
|
* RIGHTS uses SERVER\SYS:. Keep the rest of the path with DOS
|
|
* backslashes. */
|
|
p = strchr(out, '\\');
|
|
if (p && strchr(out, ':') && p < strchr(out, ':'))
|
|
*p = '/';
|
|
}
|
|
|
|
static int remove_one(char *path, uint16 dhandle, uint32 object_id,
|
|
uint16 objtype, char *objname, int forced_is_file)
|
|
{
|
|
uint16 old_rights = 0;
|
|
int is_dir;
|
|
int rc;
|
|
|
|
/* Without /FILES, Novell REMOVE reports directory-oriented messages even
|
|
* if the specified path cannot be located. */
|
|
is_dir = forced_is_file ? 0 : 1;
|
|
|
|
rc = c32_ncp87_find_trustee_rights(path, dhandle, object_id, &old_rights,
|
|
NULL, NULL, NULL);
|
|
remove_last_rc = rc;
|
|
if (rc) {
|
|
if (rc == 0xff)
|
|
fprintf(stdout, "No trustee for the specified %s.\n", is_dir ? "directory" : "file");
|
|
else
|
|
fprintf(stdout, "Error scanning trustee list.\n");
|
|
return(1);
|
|
}
|
|
|
|
rc = c32_ncp22_delete_trustee_rights(path, dhandle, object_id);
|
|
if (rc)
|
|
rc = c32_ncp87_delete_trustee_rights(path, dhandle, object_id,
|
|
NULL, NULL, NULL);
|
|
remove_last_rc = rc;
|
|
if (rc) {
|
|
fprintf(stdout, "Error deleting trustee.\n");
|
|
return(1);
|
|
}
|
|
|
|
{
|
|
char header[300];
|
|
remove_header_path(header, path, sizeof(header));
|
|
fprintf(stdout, "%s\n", header);
|
|
}
|
|
|
|
if (objtype == TRUSTEE_BINDERY_GROUP)
|
|
fprintf(stdout, "Group \"%s\" no longer a trustee to the specified %s.\n\n",
|
|
objname, is_dir ? "directory" : "file");
|
|
else
|
|
fprintf(stdout, "User \"%s\" no longer a trustee to the specified %s.\n\n",
|
|
objname, is_dir ? "directory" : "file");
|
|
|
|
return(0);
|
|
}
|
|
|
|
static int remove_subdirs_inner(char *path, uint16 dhandle, uint32 object_id,
|
|
uint16 objtype, char *objname, int *count,
|
|
int include_this)
|
|
{
|
|
struct find_t ff;
|
|
char pattern[260];
|
|
char child[260];
|
|
int rc = 0;
|
|
|
|
if (include_this) {
|
|
if (remove_one(path, dhandle, object_id, objtype, objname, 0) == 0)
|
|
(*count)++;
|
|
else
|
|
rc = 1;
|
|
}
|
|
|
|
trustee_join_path(pattern, path, "*.*", sizeof(pattern));
|
|
|
|
if (_dos_findfirst(pattern, _A_SUBDIR, &ff) == 0) {
|
|
do {
|
|
if ((ff.attrib & _A_SUBDIR) && !trustee_is_dot_dir(ff.name)) {
|
|
trustee_join_path(child, path, ff.name, sizeof(child));
|
|
if (remove_subdirs_inner(child, dhandle, object_id, objtype, objname,
|
|
count, 1))
|
|
rc = 1;
|
|
}
|
|
} while (_dos_findnext(&ff) == 0);
|
|
}
|
|
|
|
return(rc);
|
|
}
|
|
|
|
static int remove_subdirs(char *path, uint16 dhandle, uint32 object_id,
|
|
uint16 objtype, char *objname, int *count)
|
|
{
|
|
/* Novell REMOVE /SUBDIRS affects the subdirectories under the specified
|
|
* start directory, not the start directory itself. */
|
|
return(remove_subdirs_inner(path, dhandle, object_id, objtype, objname,
|
|
count, 0));
|
|
}
|
|
|
|
static int remove_files(char *path, uint16 dhandle, uint32 object_id,
|
|
uint16 objtype, char *objname, 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 (trustee_path_has_wildcards(path)) {
|
|
trustee_parent_pattern(dir, pat, path, sizeof(dir), sizeof(pat));
|
|
} else {
|
|
if (remove_one(path, dhandle, object_id, objtype, objname, 1) == 0)
|
|
(*count)++;
|
|
else
|
|
rc = 1;
|
|
return(rc);
|
|
}
|
|
|
|
trustee_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)) {
|
|
trustee_join_path(target, dir, ff.name, sizeof(target));
|
|
if (remove_one(target, dhandle, object_id, objtype, objname, 1) == 0)
|
|
(*count)++;
|
|
else
|
|
rc = 1;
|
|
}
|
|
} while (_dos_findnext(&ff) == 0);
|
|
}
|
|
|
|
return(rc);
|
|
}
|
|
|
|
int func_remove(int argc, char *argv[], int mode)
|
|
{
|
|
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 rc;
|
|
|
|
(void)mode;
|
|
|
|
if (argc < 2 || trustee_is_help(argv[1])) {
|
|
if (argc < 2) {
|
|
remove_usage_after_error();
|
|
return(1);
|
|
}
|
|
remove_usage();
|
|
return(argc < 2 ? 1 : 0);
|
|
}
|
|
|
|
if (i < argc && trustee_same(argv[i], "USER")) {
|
|
/* Novell treats "REMOVE USER FROM path" as an object lookup for
|
|
* USER, not as a grammar error. */
|
|
if ((i + 1) < argc && trustee_same(argv[i + 1], "FROM")) {
|
|
objtype_given = 0;
|
|
} else {
|
|
objtype = TRUSTEE_BINDERY_USER;
|
|
objtype_given = 1;
|
|
i++;
|
|
}
|
|
} else if (i < argc && trustee_same(argv[i], "GROUP")) {
|
|
if ((i + 1) < argc && trustee_same(argv[i + 1], "FROM")) {
|
|
objtype_given = 0;
|
|
} else {
|
|
objtype = TRUSTEE_BINDERY_GROUP;
|
|
objtype_given = 1;
|
|
i++;
|
|
}
|
|
}
|
|
|
|
if (i >= argc) {
|
|
remove_usage_after_error();
|
|
return(1);
|
|
}
|
|
|
|
objname = argv[i++];
|
|
|
|
if (i < argc && trustee_same(argv[i], "FROM")) {
|
|
i++;
|
|
if (i >= argc) {
|
|
remove_usage_after_error();
|
|
return(1);
|
|
}
|
|
path = argv[i++];
|
|
}
|
|
|
|
while (i < argc) {
|
|
if (!trustee_is_option(argv[i])) {
|
|
remove_usage_after_error();
|
|
return(1);
|
|
}
|
|
|
|
if (trustee_is_files_option(argv[i])) {
|
|
use_files = 1;
|
|
i++;
|
|
continue;
|
|
}
|
|
|
|
if (trustee_is_subdirs_option(argv[i])) {
|
|
use_subdirs = 1;
|
|
i++;
|
|
continue;
|
|
}
|
|
|
|
remove_usage_after_error();
|
|
return(1);
|
|
}
|
|
|
|
if (use_files && use_subdirs) {
|
|
fprintf(stdout, "Remove cannot do both directories and files in a single pass.\n");
|
|
return(1);
|
|
}
|
|
|
|
if (trustee_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, "\007Group \"%s\" not found.\n", objname);
|
|
else if (objtype_given)
|
|
fprintf(stdout, "\007User \"%s\" not found.\n", objname);
|
|
else
|
|
fprintf(stdout, "\007User or group \"%s\" not found.\n", objname);
|
|
return(1);
|
|
}
|
|
|
|
trustee_upcopy(objprint, objname, sizeof(objprint));
|
|
|
|
if (use_subdirs)
|
|
rc = remove_subdirs(path, (uint16)dhandle, object_id, objtype, objprint, &count);
|
|
else if (use_files)
|
|
rc = remove_files(path, (uint16)dhandle, object_id, objtype, objprint, &count);
|
|
else {
|
|
rc = remove_one(path, (uint16)dhandle, object_id, objtype, objprint, 0);
|
|
if (!rc)
|
|
count = 1;
|
|
}
|
|
|
|
if (use_subdirs || (!use_files && !rc))
|
|
fprintf(stdout, "Trustee \"%s\" removed from %d directories.\n\n", objprint, count);
|
|
else if (use_files)
|
|
fprintf(stdout, "Trustee \"%s\" removed from %d files.\n\n", objprint, count);
|
|
|
|
return(rc ? (remove_last_rc ? remove_last_rc : 1) : 0);
|
|
}
|