dosutils: match Novell paths for flags and trustees

Move FLAG, GRANT and REMOVE closer to the request paths used by the
Novell tools and extend the DOS comparison tests.

FLAG now reads attributes through the old NCP22 directory scan path and
writes them through NCP22/25 Set Directory/File Information. This keeps
extended attributes such as Delete Inhibit and Rename Inhibit intact and
matches the Novell behavior observed in the server logs.

GRANT now prefers NCP22/27 SetTrustee with an NCP87 fallback. Supervisor
rights are expanded like Novell does, so granting S sends and reports the
full SRWCEMFA mask. The visible output, path formatting and error text
are adjusted to match the Novell baseline.

REMOVE now prefers NCP22/2B DelTrustee with an NCP87 fallback. The
DelTrustee request layout is corrected, /SUBDIRS handling is aligned
with Novell, and the output/error text is matched to the baseline.

The FLAG, FLAGDIR, GRANT and REMOVE tests now compare NPUBLIC baselines
against the PUBLIC implementations and add delayed NOPASSUSER readback
checks using DLYSTRT and the maintainer LOGIN password option.
This commit is contained in:
Mario Fetka
2026-05-28 07:54:41 +02:00
parent 0fa4a6f700
commit 5da600c2a5
23 changed files with 2653 additions and 659 deletions

125
remove.c
View File

@@ -12,15 +12,33 @@
static int remove_last_rc = 0;
static void remove_usage_error(void)
{
fprintf(stdout, "Command line arguments violate grammar defined for REMOVE.\n\n");
}
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");
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,
@@ -30,7 +48,9 @@ static int remove_one(char *path, uint16 dhandle, uint32 object_id,
int is_dir;
int rc;
is_dir = forced_is_file ? 0 : trustee_path_is_dir(path);
/* 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);
@@ -43,8 +63,10 @@ static int remove_one(char *path, uint16 dhandle, uint32 object_id,
return(1);
}
rc = c32_ncp87_delete_trustee_rights(path, dhandle, object_id,
NULL, NULL, NULL);
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");
@@ -53,32 +75,35 @@ static int remove_one(char *path, uint16 dhandle, uint32 object_id,
{
char header[300];
trustee_header_path(header, path, sizeof(header));
fprintf(stdout, "%s\n\n", header);
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",
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",
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(char *path, uint16 dhandle, uint32 object_id,
uint16 objtype, char *objname, int *count)
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 (remove_one(path, dhandle, object_id, objtype, objname, 0) == 0)
(*count)++;
else
rc = 1;
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));
@@ -86,7 +111,8 @@ static int remove_subdirs(char *path, uint16 dhandle, uint32 object_id,
do {
if ((ff.attrib & _A_SUBDIR) && !trustee_is_dot_dir(ff.name)) {
trustee_join_path(child, path, ff.name, sizeof(child));
if (remove_subdirs(child, dhandle, object_id, objtype, objname, count))
if (remove_subdirs_inner(child, dhandle, object_id, objtype, objname,
count, 1))
rc = 1;
}
} while (_dos_findnext(&ff) == 0);
@@ -95,6 +121,15 @@ static int remove_subdirs(char *path, uint16 dhandle, uint32 object_id,
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)
{
@@ -153,25 +188,36 @@ int func_remove(int argc, char *argv[], int mode)
(void)mode;
if (argc < 2 || trustee_is_help(argv[1])) {
if (argc < 2)
remove_usage_error();
if (argc < 2) {
remove_usage_after_error();
return(1);
}
remove_usage();
return(argc < 2 ? 1 : 0);
}
if (i < argc && trustee_same(argv[i], "USER")) {
objtype = TRUSTEE_BINDERY_USER;
objtype_given = 1;
i++;
/* 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")) {
objtype = TRUSTEE_BINDERY_GROUP;
objtype_given = 1;
i++;
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_error();
remove_usage();
remove_usage_after_error();
return(1);
}
@@ -180,8 +226,7 @@ int func_remove(int argc, char *argv[], int mode)
if (i < argc && trustee_same(argv[i], "FROM")) {
i++;
if (i >= argc) {
remove_usage_error();
remove_usage();
remove_usage_after_error();
return(1);
}
path = argv[i++];
@@ -189,8 +234,7 @@ int func_remove(int argc, char *argv[], int mode)
while (i < argc) {
if (!trustee_is_option(argv[i])) {
remove_usage_error();
remove_usage();
remove_usage_after_error();
return(1);
}
@@ -206,8 +250,7 @@ int func_remove(int argc, char *argv[], int mode)
continue;
}
remove_usage_error();
remove_usage();
remove_usage_after_error();
return(1);
}
@@ -224,11 +267,11 @@ int func_remove(int argc, char *argv[], int mode)
object_id = trustee_lookup_object(objname, &objtype, objtype_given);
if (!object_id) {
if (objtype_given && objtype == TRUSTEE_BINDERY_GROUP)
fprintf(stdout, "Group \"%s\" not found.\n", objname);
fprintf(stdout, "\007Group \"%s\" not found.\n", objname);
else if (objtype_given)
fprintf(stdout, "User \"%s\" not found.\n", objname);
fprintf(stdout, "\007User \"%s\" not found.\n", objname);
else
fprintf(stdout, "User or group \"%s\" not found.\n", objname);
fprintf(stdout, "\007User or group \"%s\" not found.\n", objname);
return(1);
}
@@ -245,9 +288,9 @@ int func_remove(int argc, char *argv[], int mode)
}
if (use_subdirs || (!use_files && !rc))
fprintf(stdout, "Trustee \"%s\" removed from %d directories.\n", objprint, count);
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", objprint, count);
fprintf(stdout, "Trustee \"%s\" removed from %d files.\n\n", objprint, count);
return(rc ? (remove_last_rc ? remove_last_rc : 1) : 0);
}