Files
mars-dosutils/c32ncp.c
Mario Fetka 4a952b4c4c dosutils: align Novell-compatible tests and stage NCOPY work
Update the DOS utilities and test suite with the current Novell comparison
state.

Validated/updated tool behavior:
- improve CREATOR output by showing Novell-style attribute and rights masks
- extend FLAGDIR handling with old NCP22 directory attribute read/write
  fallback paths
- expand NDIR Novell-style formatting, filtering, /SUB handling, date output,
  DI/RI attribute display and richer metadata collection
- adjust REVOKE output/grammar, recursive /SUBDIRECTORIES behavior and trustee
  update/delete paths to better match Novell tools
- adjust SLIST header/output behavior for logged-in and logged-out cases
- update README status to reflect the currently green/tested tools

Test-suite changes:
- add/refresh Novell comparison tests for CREATOR, NDIR, REVOKE and SLIST
- update NCOPY tests and collection scripts for the current investigation state
- refresh per-tool README files and top-level test documentation
- keep MAP documented as still separately open

NCOPY:
- add the current NCOPY implementation and experimental NCP74/server-side-copy
  scaffolding
- build ncopy.c so it stays compile-tested
- keep NCOPY disabled in the NET multicall dispatch for now because the
  server-side-copy/open-handle path is still unsafe and needs further analysis

Build:
- include ncopy.c in the DOS utility build
- drop the temporary MARS_DOSUTILS_VERSION define wiring from CMake
2026-05-29 07:40:04 +02:00

1350 lines
37 KiB
C

#include "net.h"
#include "c32ncp.h"
/* c32ncp.c - Client32 NCP helpers for mars-dosutils */
static void c32_put_word_lh(uint8 *p, uint16 v)
{
p[0] = (uint8)(v & 0xff);
p[1] = (uint8)((v >> 8) & 0xff);
}
static void c32_put_dword_lh(uint8 *p, uint32 v)
{
p[0] = (uint8)(v & 0xff);
p[1] = (uint8)((v >> 8) & 0xff);
p[2] = (uint8)((v >> 16) & 0xff);
p[3] = (uint8)((v >> 24) & 0xff);
}
static void c32_put_dword_hl(uint8 *p, uint32 v)
{
p[0] = (uint8)((v >> 24) & 0xff);
p[1] = (uint8)((v >> 16) & 0xff);
p[2] = (uint8)((v >> 8) & 0xff);
p[3] = (uint8)(v & 0xff);
}
static uint16 c32_get_word_lh(uint8 *p)
{
return((uint16)(p[0] | ((uint16)p[1] << 8)));
}
static uint32 c32_get_dword_lh(uint8 *p)
{
return((uint32)p[0] |
((uint32)p[1] << 8) |
((uint32)p[2] << 16) |
((uint32)p[3] << 24));
}
static uint32 c32_get_dword_hl(uint8 *p)
{
return(((uint32)p[0] << 24) |
((uint32)p[1] << 16) |
((uint32)p[2] << 8) |
(uint32)p[3]);
}
static UI c32_build_handle_path(uint8 *buf, uint8 dhandle,
uint16 dirbase, uint8 style,
int count,
const char *c1, const char *c2, const char *c3)
{
uint8 *p;
int l;
UI used;
/*
* DeveloperNet/ncpdos16 path structure used by NCP87/S6 through
* Client32 COMPATNcpRequestReply.
*
* This is the exact shape verified by TESTS NCP87C32AUTO:
* 00 02 00 00 00 00 01 09 4C 4F 47 49 4E 2E 45 58 45
*
* Meaning:
* word[1] = short dir handle
* word[3] = dir base
* byte[5] = dirstyle
* byte[6] = component count
* then len/name components
*
* The old/simple struct used by the INT 21h F257 fallback is not accepted
* by this Client32 path.
*/
memset(buf, 0, 0x140);
if (dhandle) {
c32_put_word_lh(buf + 1, (uint16)dhandle);
c32_put_word_lh(buf + 3, dirbase);
buf[5] = style;
} else {
buf[5] = 0xff;
}
p = buf + 6;
*p++ = (uint8)count;
if (count > 0 && c1) {
l = strlen(c1);
if (l > 255) l = 255;
*p++ = (uint8)l;
memcpy(p, c1, l);
p += l;
}
if (count > 1 && c2) {
l = strlen(c2);
if (l > 255) l = 255;
*p++ = (uint8)l;
memcpy(p, c2, l);
p += l;
}
if (count > 2 && c3) {
l = strlen(c3);
if (l > 255) l = 255;
*p++ = (uint8)l;
memcpy(p, c3, l);
p += l;
}
used = (UI)(p - buf);
c32_put_word_lh(buf + 0x13c, used);
return(used);
}
static UI c32_build_handle_path_from_dos_path(uint8 *buf, uint8 dhandle,
uint16 dirbase, uint8 style,
const char *dospath)
{
uint8 *p;
uint8 *countp;
int count = 0;
const char *s;
UI used;
memset(buf, 0, 0x140);
if (dhandle) {
c32_put_word_lh(buf + 1, (uint16)dhandle);
c32_put_word_lh(buf + 3, dirbase);
buf[5] = style;
} else {
buf[5] = 0xff;
}
p = buf + 6;
countp = p++;
s = dospath;
if (!s) s = "";
/*
* DOS tools mostly pass relative paths against the current directory
* handle. Accept simple DOS decoration here so RIGHTS can pass "." or
* ".\\UDIR\\FILE" without constructing path components in the caller.
*/
if (s[0] && s[1] == ':')
s += 2;
while (*s == '\\' || *s == '/')
s++;
while (*s && p < buf + 0x138 && count < 32) {
const char *start;
int len;
while (*s == '\\' || *s == '/')
s++;
if (*s == '.'
&& (s[1] == '\0' || s[1] == '\\' || s[1] == '/')) {
s++;
continue;
}
start = s;
while (*s && *s != '\\' && *s != '/')
s++;
len = (int)(s - start);
if (len <= 0)
continue;
if (len > 255)
len = 255;
if (p + 1 + len >= buf + 0x138)
break;
*p++ = (uint8)len;
memcpy(p, start, len);
p += len;
count++;
}
*countp = (uint8)count;
used = (UI)(p - buf);
c32_put_word_lh(buf + 0x13c, used);
return(used);
}
/*
* Current verified Client32 path for mars-nwe DOS utilities:
*
* C32_MapVar_Probe(4,0) -> connRefLocal FFFF:FFFE
* C32_OpenRef_Probe(connRefLocal) -> Client32 handle, e.g. 0101:0001
*
* C32_MapVar_Probe currently contains the confirmed Mars server-name scan
* shape. It is intentionally kept small and isolated here so FLAG and later
* tools do not carry the old exploratory tests.
*/
int c32_get_ncp_handle(uint16 *handle_lo, uint16 *handle_hi)
{
uint8 mapout[32];
uint8 openout[32];
uint16 map_ret_ax, map_ret_dx;
uint16 cref_lo, cref_hi;
uint16 open_ret_ax, open_ret_dx;
if (!handle_lo || !handle_hi)
return(1);
*handle_lo = 0;
*handle_hi = 0;
memset(mapout, 0, sizeof(mapout));
C32_MapVar_Probe(4, 0, mapout);
map_ret_ax = c32_get_word_lh(mapout + 14);
map_ret_dx = c32_get_word_lh(mapout + 16);
cref_lo = c32_get_word_lh(mapout + 22);
cref_hi = c32_get_word_lh(mapout + 24);
if (map_ret_ax != 0 || map_ret_dx != 0 || (cref_lo == 0 && cref_hi == 0))
return(2);
memset(openout, 0, sizeof(openout));
C32_OpenRef_Probe(cref_lo, cref_hi, openout);
open_ret_ax = c32_get_word_lh(openout + 14);
open_ret_dx = c32_get_word_lh(openout + 16);
*handle_lo = c32_get_word_lh(openout + 18);
*handle_hi = c32_get_word_lh(openout + 20);
if (open_ret_ax != 0 || open_ret_dx != 0 || (*handle_lo == 0 && *handle_hi == 0))
return(3);
return(0);
}
static void c32_copy_open_reply_to_handle6(C32_NWFILE_HANDLE6 *dst,
const uint8 *src)
{
/*
* NCP 74 File Server Copy and NCP 66 Close File both take a six-byte
* server file handle. Client32's NCP87 Open/Create reply supplies that
* six-byte handle at the start of the first reply fragment; the following
* bytes are OpenCreateAction and Reserved.
*
* The previous patch only copied four bytes and zero-filled h[4..5]. That
* is enough to create a visible zero-length target file, but it is not the
* handle that NCP74/NCP66 own. The bad close leaves Client32/MARS seeing
* the target as still open, so the fallback DOS/requester open reports
* "File in use during a file open" for every file.
*/
memset(dst->h, 0, sizeof(dst->h));
memcpy(dst->h, src, 6);
}
int c32_ncp87_open_create_file(const char *path_name,
uint16 dir_handle,
uint8 open_create_mode,
uint32 create_attrs,
uint16 desired_access,
uint16 search_attrs,
C32_NWFILE_HANDLE6 *handle_out,
uint32 *file_size_out,
uint8 *open_create_action_out,
uint16 *actual_out,
uint16 *handle_lo_out,
uint16 *handle_hi_out)
{
uint16 handle_lo, handle_hi;
uint8 hdr[16];
uint8 path[0x140];
uint8 rep0[0x180];
uint8 rep1[0x40];
uint8 rawout[32];
uint16 raw_ret_ax, raw_ret_dx;
uint16 actual_lo;
UI path_len;
int rc;
if (handle_out)
memset(handle_out, 0, sizeof(*handle_out));
if (file_size_out) *file_size_out = 0;
if (open_create_action_out) *open_create_action_out = 0;
if (actual_out) *actual_out = 0;
if (handle_lo_out) *handle_lo_out = 0;
if (handle_hi_out) *handle_hi_out = 0;
if (!path_name || !handle_out)
return(1);
rc = c32_get_ncp_handle(&handle_lo, &handle_hi);
if (rc)
return(10 + rc);
/*
* NCP 87 subfunction 1: Open/Create File or Subdirectory.
*
* Request payload after the NCP87 subfunction byte:
* byte NameSpace (0 = DOS)
* byte OpenCreateMode (OPEN/TRUNCATE/CREATE bits)
* word SearchAttributes (Lo-Hi)
* dword ReturnInfoMask (Lo-Hi)
* dword CreateAttributes (Lo-Hi)
* word DesiredAccessRights (Lo-Hi)
* NWHandlePathStruct
*
* Existing C32_NCP87_Raw5_Probe users in this file send the subfunction
* header as fragment 0 and the NWHandlePathStruct as fragment 1.
*/
memset(hdr, 0, sizeof(hdr));
hdr[0] = 1; /* NCP87 subfunction 1 */
hdr[1] = 0; /* DOS namespace */
hdr[2] = open_create_mode;
c32_put_word_lh(hdr + 3, search_attrs);
c32_put_dword_lh(hdr + 5, 0x00000FFFUL); /* RIM_ALL, keeps size handy */
c32_put_dword_lh(hdr + 9, create_attrs);
c32_put_word_lh(hdr + 13, desired_access);
path_len = c32_build_handle_path_from_dos_path(path, (uint8)dir_handle,
0, 0, path_name);
memset(rep0, 0, sizeof(rep0));
memset(rep1, 0, sizeof(rep1));
memset(rawout, 0, sizeof(rawout));
C32_NCP87_Raw5_Probe(handle_lo, handle_hi,
hdr, 15,
path, path_len,
rep0, sizeof(rep0),
rep1, sizeof(rep1),
rawout);
raw_ret_ax = c32_get_word_lh(rawout + 14);
raw_ret_dx = c32_get_word_lh(rawout + 16);
actual_lo = c32_get_word_lh(rawout + 18);
if (actual_out) *actual_out = actual_lo;
if (handle_lo_out) *handle_lo_out = handle_lo;
if (handle_hi_out) *handle_hi_out = handle_hi;
if (raw_ret_ax != 0 || raw_ret_dx != 0)
return(20);
c32_copy_open_reply_to_handle6(handle_out, rep0 + 0);
if (open_create_action_out)
*open_create_action_out = rep0[6];
/* NetWareInfoStruct follows FileHandle[6]/OpenCreateAction/Reserved. */
if (file_size_out)
*file_size_out = c32_get_dword_lh(rep0 + 8 + 10);
return(0);
}
int c32_ncp74_file_server_copy(const C32_NWFILE_HANDLE6 *src,
const C32_NWFILE_HANDLE6 *dst,
uint32 src_offset,
uint32 dst_offset,
uint32 count,
uint32 *copied_out)
{
struct {
uint16 len;
uint8 reserved;
uint8 src_handle[6];
uint8 dst_handle[6];
uint8 src_offset[4];
uint8 dst_offset[4];
uint8 count[4];
} req;
struct {
uint16 len;
uint8 copied[4];
} repl;
if (copied_out)
*copied_out = 0;
if (!src || !dst)
return(1);
memset(&req, 0, sizeof(req));
memset(&repl, 0, sizeof(repl));
memcpy(req.src_handle, src->h, 6);
memcpy(req.dst_handle, dst->h, 6);
c32_put_dword_hl(req.src_offset, src_offset);
c32_put_dword_hl(req.dst_offset, dst_offset);
c32_put_dword_hl(req.count, count);
req.len = 1 + 6 + 6 + 4 + 4 + 4;
repl.len = 4;
neterrno = Net_Call(0xF24A, &req, &repl);
if (neterrno)
return(-neterrno);
if (copied_out)
*copied_out = c32_get_dword_hl(repl.copied);
return(0);
}
int c32_ncp66_close_file(const C32_NWFILE_HANDLE6 *handle)
{
struct {
uint8 len;
uint8 reserved;
uint8 handle[6];
} req;
struct { uint8 len; } repl;
if (!handle)
return(1);
memset(&req, 0, sizeof(req));
memset(&repl, 0, sizeof(repl));
memcpy(req.handle, handle->h, 6);
req.len = 1 + 6;
repl.len = 0;
neterrno = Net_Call(0xF242, &req, &repl);
if (neterrno)
return(-neterrno);
return(0);
}
int c32_ncp87_obtain_rim_attributes(const char *name,
uint16 dir_handle,
uint32 *attr_out,
uint16 *actual_out,
uint16 *handle_lo_out,
uint16 *handle_hi_out)
{
uint16 handle_lo, handle_hi;
uint8 hdr[16];
uint8 path[0x140];
uint8 rep0[0x60];
uint8 rep1[0x110];
uint8 rawout[32];
uint16 raw_ret_ax, raw_ret_dx;
uint16 actual_lo;
int path_len;
int rc;
if (!name || !attr_out)
return(1);
*attr_out = 0;
if (actual_out) *actual_out = 0;
if (handle_lo_out) *handle_lo_out = 0;
if (handle_hi_out) *handle_hi_out = 0;
rc = c32_get_ncp_handle(&handle_lo, &handle_hi);
if (rc)
return(10 + rc);
memset(hdr, 0, sizeof(hdr));
hdr[0] = 6; /* NCP87 subfunction 6 */
hdr[1] = 0; /* source namespace DOS */
hdr[2] = 0; /* target namespace DOS */
c32_put_word_lh(hdr + 3, 0x0006); /* SA_ALL */
c32_put_dword_lh(hdr + 5, 0x00000004UL); /* RIM_ATTRIBUTES */
path_len = c32_build_handle_path(path, (uint8)dir_handle, 0, 0, 1,
name, NULL, NULL);
memset(rep0, 0, sizeof(rep0));
memset(rep1, 0, sizeof(rep1));
memset(rawout, 0, sizeof(rawout));
C32_NCP87_Raw5_Probe(handle_lo, handle_hi,
hdr, 9,
path, (UI)path_len,
rep0, 0x4d,
rep1, 0x100,
rawout);
raw_ret_ax = c32_get_word_lh(rawout + 14);
raw_ret_dx = c32_get_word_lh(rawout + 16);
actual_lo = c32_get_word_lh(rawout + 18);
if (raw_ret_ax != 0 || raw_ret_dx != 0)
return(20);
/*
* Verified reply layout for RIM_ATTRIBUTES:
* REP0+4 little-endian dword = DOS attributes
* Example LOGIN.EXE: 20h archive.
*/
*attr_out = c32_get_dword_lh(rep0 + 4);
if (actual_out) *actual_out = actual_lo;
if (handle_lo_out) *handle_lo_out = handle_lo;
if (handle_hi_out) *handle_hi_out = handle_hi;
return(0);
}
int c32_ncp87_obtain_ndir_info(const char *path_name,
uint16 dir_handle,
C32_NDIR_INFO *info_out,
uint16 *actual_out,
uint16 *handle_lo_out,
uint16 *handle_hi_out)
{
uint16 handle_lo, handle_hi;
uint8 hdr[16];
uint8 path[0x140];
uint8 rep0[0x180];
uint8 rep1[0x40];
uint8 rawout[32];
uint16 raw_ret_ax, raw_ret_dx;
uint16 actual_lo;
UI path_len;
int rc;
int namelen;
if (!info_out)
return(1);
memset(info_out, 0, sizeof(*info_out));
if (actual_out) *actual_out = 0;
if (handle_lo_out) *handle_lo_out = 0;
if (handle_hi_out) *handle_hi_out = 0;
rc = c32_get_ncp_handle(&handle_lo, &handle_hi);
if (rc)
return(10 + rc);
/*
* NCP87 subfunction 6: Obtain File or Subdirectory Information.
*
* This asks for the classic DOS info block (RIM_ALL). ncpfs' old
* nw_info_struct layout is:
* +00 space allocated
* +04 attributes
* +20 creation time/date/id
* +28 modify time/date/id
* +36 last access date
* +38 archive time/date/id
* +46 inherited rights mask
* +48 directory numbers
* +76 name length/name
*/
memset(hdr, 0, sizeof(hdr));
hdr[0] = 6; /* NCP87 subfunction 6 */
hdr[1] = 0; /* source namespace DOS */
hdr[2] = 0; /* target namespace DOS */
c32_put_word_lh(hdr + 3, 0x0006); /* SA_ALL */
c32_put_dword_lh(hdr + 5, 0x00000FFFUL); /* RIM_ALL */
path_len = c32_build_handle_path_from_dos_path(path, (uint8)dir_handle,
0, 0, path_name);
memset(rep0, 0, sizeof(rep0));
memset(rep1, 0, sizeof(rep1));
memset(rawout, 0, sizeof(rawout));
C32_NCP87_Raw5_Probe(handle_lo, handle_hi,
hdr, 9,
path, path_len,
rep0, sizeof(rep0),
rep1, sizeof(rep1),
rawout);
raw_ret_ax = c32_get_word_lh(rawout + 14);
raw_ret_dx = c32_get_word_lh(rawout + 16);
actual_lo = c32_get_word_lh(rawout + 18);
if (actual_out) *actual_out = actual_lo;
if (handle_lo_out) *handle_lo_out = handle_lo;
if (handle_hi_out) *handle_hi_out = handle_hi;
if (raw_ret_ax != 0 || raw_ret_dx != 0)
return(20);
info_out->space_allocated = c32_get_dword_lh(rep0 + 0);
info_out->attributes = c32_get_dword_lh(rep0 + 4);
info_out->flags = c32_get_word_lh(rep0 + 8);
info_out->data_size = c32_get_dword_lh(rep0 + 10);
info_out->total_size = c32_get_dword_lh(rep0 + 14);
info_out->number_of_streams = c32_get_word_lh(rep0 + 18);
info_out->creation_time = c32_get_word_lh(rep0 + 20);
info_out->creation_date = c32_get_word_lh(rep0 + 22);
info_out->creator_id = c32_get_dword_hl(rep0 + 24);
info_out->modify_time = c32_get_word_lh(rep0 + 28);
info_out->modify_date = c32_get_word_lh(rep0 + 30);
info_out->modifier_id = c32_get_dword_hl(rep0 + 32);
info_out->last_access_date = c32_get_word_lh(rep0 + 36);
info_out->archive_time = c32_get_word_lh(rep0 + 38);
info_out->archive_date = c32_get_word_lh(rep0 + 40);
info_out->archiver_id = c32_get_dword_hl(rep0 + 42);
info_out->inherited_rights = c32_get_word_lh(rep0 + 46);
info_out->dir_ent_num = c32_get_dword_lh(rep0 + 48);
info_out->dos_dir_num = c32_get_dword_lh(rep0 + 52);
info_out->vol_number = c32_get_dword_lh(rep0 + 56);
info_out->ea_data_size = c32_get_dword_lh(rep0 + 60);
info_out->ea_key_count = c32_get_dword_lh(rep0 + 64);
info_out->ea_key_size = c32_get_dword_lh(rep0 + 68);
info_out->ns_creator = c32_get_dword_lh(rep0 + 72);
namelen = rep0[76];
if (namelen > 255)
namelen = 255;
info_out->name_len = (uint8)namelen;
memcpy(info_out->name, rep0 + 77, namelen);
info_out->name[namelen] = '\0';
return(0);
}
int c32_ncp87_modify_dos_info(const char *name,
uint16 dir_handle,
uint32 change_mask,
C32_DOS_MODIFY_INFO *info,
uint16 *actual_out,
uint16 *handle_lo_out,
uint16 *handle_hi_out)
{
uint16 handle_lo, handle_hi;
uint8 modbuf[80];
uint8 path[0x140];
uint8 rep0[0x20];
uint8 rep1[0x20];
uint8 rawout[32];
uint8 *p;
UI mod_len;
UI path_len;
uint16 raw_ret_ax, raw_ret_dx;
uint16 actual_lo;
int rc;
if (!name || !info)
return(1);
if (actual_out)
*actual_out = 0;
if (handle_lo_out)
*handle_lo_out = 0;
if (handle_hi_out)
*handle_hi_out = 0;
rc = c32_get_ncp_handle(&handle_lo, &handle_hi);
if (rc)
return(10 + rc);
/*
* NCP 87 subfunction 7: Modify DOS information.
*
* DOS info payload layout verified by FLAG and MARS' NCP22/25 handler:
* dword attributes
* word creation time
* word creation date
* dword creator id (HI-LOW)
* word modify time
* word modify date
* dword modifier id (HI-LOW)
* word last access date
* word archive time
* word archive date
* dword archiver id (HI-LOW)
* word inherited rights mask
* dword maximum space
*/
memset(modbuf, 0, sizeof(modbuf));
p = modbuf;
*p++ = 7; /* subfunction: modify DOS info */
*p++ = 0; /* namespace DOS */
*p++ = 0; /* reserved */
c32_put_word_lh(p, 0x0006); p += 2; /* SA_ALL */
c32_put_dword_lh(p, change_mask); p += 4;
c32_put_dword_lh(p, info->attributes); p += 4;
c32_put_word_lh(p, info->creation_time); p += 2;
c32_put_word_lh(p, info->creation_date); p += 2;
c32_put_dword_hl(p, info->creator_id); p += 4;
c32_put_word_lh(p, info->modify_time); p += 2;
c32_put_word_lh(p, info->modify_date); p += 2;
c32_put_dword_hl(p, info->modifier_id); p += 4;
c32_put_word_lh(p, info->last_access_date); p += 2;
c32_put_word_lh(p, info->archive_time); p += 2;
c32_put_word_lh(p, info->archive_date); p += 2;
c32_put_dword_hl(p, info->archiver_id); p += 4;
c32_put_word_lh(p, info->inherited_rights); p += 2;
c32_put_dword_lh(p, info->maximum_space); p += 4;
mod_len = (UI)(p - modbuf);
path_len = c32_build_handle_path(path, (uint8)dir_handle, 0, 0, 1,
name, NULL, NULL);
memset(rep0, 0, sizeof(rep0));
memset(rep1, 0, sizeof(rep1));
memset(rawout, 0, sizeof(rawout));
C32_NCP87_Raw5_Probe(handle_lo, handle_hi,
modbuf, mod_len,
path, path_len,
rep0, sizeof(rep0),
rep1, sizeof(rep1),
rawout);
raw_ret_ax = c32_get_word_lh(rawout + 14);
raw_ret_dx = c32_get_word_lh(rawout + 16);
actual_lo = c32_get_word_lh(rawout + 18);
if (actual_out)
*actual_out = actual_lo;
if (handle_lo_out)
*handle_lo_out = handle_lo;
if (handle_hi_out)
*handle_hi_out = handle_hi;
if (raw_ret_ax != 0 || raw_ret_dx != 0)
return(20);
return(0);
}
int c32_ncp87_modify_dos_attributes(char *name,
uint16 dir_handle,
uint32 attrs,
uint16 *actual_out,
uint16 *handle_lo_out,
uint16 *handle_hi_out)
{
C32_DOS_MODIFY_INFO info;
memset(&info, 0, sizeof(info));
info.attributes = attrs;
/*
* NCP 87 modify DOS information uses DM_ATTRIBUTES (0x00000002).
* RIM_ATTRIBUTES (0x00000004) is the read/obtain mask; using it here
* leaves high attribute bits such as Delete/Rename Inhibit unchanged or
* cleared on some servers/clients.
*/
return(c32_ncp87_modify_dos_info(name, dir_handle, 0x00000002UL,
&info, actual_out,
handle_lo_out, handle_hi_out));
}
int c32_ncp87_get_effective_rights(const char *path_name,
uint16 dir_handle,
uint16 *rights_out,
uint16 *actual_out,
uint16 *handle_lo_out,
uint16 *handle_hi_out)
{
uint16 handle_lo, handle_hi;
uint8 hdr[16];
uint8 path[0x140];
uint8 rep0[0x20];
uint8 rep1[0x20];
uint8 rawout[32];
uint16 raw_ret_ax, raw_ret_dx;
uint16 actual_lo;
uint16 rights0;
uint16 rights4;
UI path_len;
int rc;
if (!rights_out)
return(1);
*rights_out = 0;
if (actual_out) *actual_out = 0;
if (handle_lo_out) *handle_lo_out = 0;
if (handle_hi_out) *handle_hi_out = 0;
rc = c32_get_ncp_handle(&handle_lo, &handle_hi);
if (rc)
return(10 + rc);
/*
* NCP 87 subfunction 29: Get effective rights.
*
* This mirrors ncpfs ncp_get_eff_directory_rights():
* byte 29
* byte source namespace
* byte target namespace
* word search attributes, little endian
* dword reserved, zero
* handle/path
*
* Reply is a little-endian word with NCP rights bits.
*/
memset(hdr, 0, sizeof(hdr));
hdr[0] = 29;
hdr[1] = 0; /* source namespace DOS */
hdr[2] = 0; /* target namespace DOS */
c32_put_word_lh(hdr + 3, 0x0006); /* SA_ALL */
c32_put_dword_lh(hdr + 5, 0L); /* reserved */
path_len = c32_build_handle_path_from_dos_path(path, (uint8)dir_handle,
0, 0, path_name);
memset(rep0, 0, sizeof(rep0));
memset(rep1, 0, sizeof(rep1));
memset(rawout, 0, sizeof(rawout));
C32_NCP87_Raw5_Probe(handle_lo, handle_hi,
hdr, 9,
path, path_len,
rep0, sizeof(rep0),
rep1, sizeof(rep1),
rawout);
raw_ret_ax = c32_get_word_lh(rawout + 14);
raw_ret_dx = c32_get_word_lh(rawout + 16);
actual_lo = c32_get_word_lh(rawout + 18);
if (actual_out) *actual_out = actual_lo;
if (handle_lo_out) *handle_lo_out = handle_lo;
if (handle_hi_out) *handle_hi_out = handle_hi;
if (raw_ret_ax != 0 || raw_ret_dx != 0)
return(20);
rights0 = c32_get_word_lh(rep0 + 0);
rights4 = c32_get_word_lh(rep0 + 4);
/*
* Most NCP replies start at REP0+0. The existing RIM_ATTRIBUTES helper
* found attributes at REP0+4 on Client32. Accept the +4 location only
* if +0 is empty, so restricted rights value 0 still works when +4 is
* also zero.
*/
if (rights0 == 0 && rights4 != 0)
rights0 = rights4;
*rights_out = rights0;
return(0);
}
int c32_ncp87_get_effective_rights_by_dirent(uint8 vol_number,
uint32 dos_dir_number,
uint16 *rights_out,
uint16 *actual_out,
uint16 *handle_lo_out,
uint16 *handle_hi_out)
{
uint16 handle_lo, handle_hi;
uint8 hdr[16];
uint8 path[16];
uint8 rep0[0x20];
uint8 rep1[0x20];
uint8 rawout[32];
uint16 raw_ret_ax, raw_ret_dx;
uint16 actual_lo;
uint16 rights0;
uint16 rights4;
int rc;
if (!rights_out)
return(1);
*rights_out = 0;
if (actual_out) *actual_out = 0;
if (handle_lo_out) *handle_lo_out = 0;
if (handle_hi_out) *handle_hi_out = 0;
rc = c32_get_ncp_handle(&handle_lo, &handle_hi);
if (rc)
return(10 + rc);
/*
* SDK/ncpfs-style NCP87 subfunction 29:
* byte 0x1D
* byte source namespace DOS
* byte reserved/target namespace
* word SA_ALL
* dword 0
* byte volume number
* dword directory number
* byte dirstyle 1
* byte component count 0
*/
memset(hdr, 0, sizeof(hdr));
hdr[0] = 0x1d;
hdr[1] = 0;
hdr[2] = 0;
c32_put_word_lh(hdr + 3, 0x0006);
c32_put_dword_lh(hdr + 5, 0L);
memset(path, 0, sizeof(path));
path[0] = vol_number;
c32_put_dword_lh(path + 1, dos_dir_number);
path[5] = 1;
path[6] = 0;
memset(rep0, 0, sizeof(rep0));
memset(rep1, 0, sizeof(rep1));
memset(rawout, 0, sizeof(rawout));
C32_NCP87_Raw5_Probe(handle_lo, handle_hi,
hdr, 9,
path, 7,
rep0, sizeof(rep0),
rep1, sizeof(rep1),
rawout);
raw_ret_ax = c32_get_word_lh(rawout + 14);
raw_ret_dx = c32_get_word_lh(rawout + 16);
actual_lo = c32_get_word_lh(rawout + 18);
if (actual_out) *actual_out = actual_lo;
if (handle_lo_out) *handle_lo_out = handle_lo;
if (handle_hi_out) *handle_hi_out = handle_hi;
if (raw_ret_ax != 0 || raw_ret_dx != 0)
return(20);
rights0 = (uint16)(rep0[0] | ((uint16)rep0[1] << 8));
rights4 = (uint16)(rep0[4] | ((uint16)rep0[5] << 8));
if (rights0 == 0 && rights4 != 0)
rights0 = rights4;
*rights_out = rights0;
return(0);
}
static int c32_build_ncp22_trustee_path(char *out, const char *path_name, int max)
{
char header[300];
char *p;
char *colon;
if (!out || max < 2)
return(-1);
out[0] = '\0';
/* Build the same server/volume display path used by the DOS tools, then
* strip the server component. NCP22/27 accepts a volume-qualified path
* such as SYS:DIR\FILE with directory handle 0, matching Novell GRANT's
* old trustee call as seen in the server trace. */
tool_header_path(header, (char *)(path_name ? path_name : "."), sizeof(header));
colon = strchr(header, ':');
if (!colon)
return(-1);
p = header;
while (*p && p < colon) {
if (*p == '\\' || *p == '/') {
p++;
break;
}
p++;
}
if (!*p || p >= colon)
p = header;
strmaxcpy(out, p, max - 1);
return(0);
}
int c32_ncp22_set_trustee_rights(const char *path_name,
uint16 dir_handle,
uint32 object_id,
uint16 rights)
{
struct {
uint16 len;
uint8 func;
uint8 dirhandle;
uint8 trustee_id[4];
uint8 trustee_rights[2];
uint8 pathlen;
uint8 path[255];
} req;
struct { uint16 len; } repl;
char ncppath[260];
int pathlen;
(void)dir_handle;
if (c32_build_ncp22_trustee_path(ncppath, path_name, sizeof(ncppath)))
return(30);
pathlen = strlen(ncppath);
if (pathlen > 255)
return(31);
memset(&req, 0, sizeof(req));
memset(&repl, 0, sizeof(repl));
req.func = 0x27; /* NCP22/27 Add Ext Trustee */
req.dirhandle = 0; /* volume-qualified path follows */
c32_put_dword_hl(req.trustee_id, object_id);
c32_put_word_lh(req.trustee_rights, rights);
req.pathlen = (uint8)pathlen;
memcpy(req.path, ncppath, pathlen);
req.len = 9 + pathlen;
repl.len = 0;
neterrno = Net_Call(0xE200, &req, &repl);
if (neterrno)
return(-neterrno);
return(0);
}
int c32_ncp22_delete_trustee_rights(const char *path_name,
uint16 dir_handle,
uint32 object_id)
{
struct {
uint16 len;
uint8 func;
uint8 dirhandle;
uint8 trustee_id[4];
uint8 reserved;
uint8 pathlen;
uint8 path[255];
} req;
struct { uint16 len; } repl;
char ncppath[260];
int pathlen;
(void)dir_handle;
if (c32_build_ncp22_trustee_path(ncppath, path_name, sizeof(ncppath)))
return(30);
pathlen = strlen(ncppath);
if (pathlen > 255)
return(31);
memset(&req, 0, sizeof(req));
memset(&repl, 0, sizeof(repl));
req.func = 0x2b; /* NCP22/43 Delete Trustee */
req.dirhandle = 0; /* volume-qualified path follows */
c32_put_dword_hl(req.trustee_id, object_id);
req.reserved = 0; /* NCP22/2B has a reserved byte before pathlen */
req.pathlen = (uint8)pathlen;
memcpy(req.path, ncppath, pathlen);
req.len = 8 + pathlen;
repl.len = 0;
neterrno = Net_Call(0xE200, &req, &repl);
if (neterrno)
return(-neterrno);
return(0);
}
int c32_ncp87_add_trustee_rights(const char *path_name,
uint16 dir_handle,
uint32 object_id,
uint16 rights,
uint16 rights_mask,
uint16 *actual_out,
uint16 *handle_lo_out,
uint16 *handle_hi_out)
{
uint16 handle_lo, handle_hi;
uint8 hdr[16];
uint8 reqpath[0x180];
uint8 rep0[0x20];
uint8 rep1[0x20];
uint8 rawout[32];
uint16 raw_ret_ax, raw_ret_dx;
uint16 actual_lo;
UI path_struct_len;
UI reqpath_len;
uint8 *tp;
int rc;
if (actual_out) *actual_out = 0;
if (handle_lo_out) *handle_lo_out = 0;
if (handle_hi_out) *handle_hi_out = 0;
rc = c32_get_ncp_handle(&handle_lo, &handle_hi);
if (rc)
return(10 + rc);
/*
* NCP 87 subfunction 10: Add trustee.
*
* This mirrors ncpfs ncp_ns_trustee_add():
* byte 10
* byte namespace DOS
* byte reserved
* word search attributes, little endian
* word rights mask, little endian
* word object count, little endian
* handle/path
* trustee array at request offset 16 + 307
*
* Client32 Raw5 has two request fragments, so the second fragment carries
* the handle/path and the padded trustee record.
*/
memset(hdr, 0, sizeof(hdr));
hdr[0] = 10;
hdr[1] = 0; /* DOS namespace */
hdr[2] = 0; /* reserved */
c32_put_word_lh(hdr + 3, 0x8006); /* SA_ALL: files/subdirs + system + hidden */
c32_put_word_lh(hdr + 5, rights_mask);
c32_put_word_lh(hdr + 7, 1); /* one trustee */
memset(reqpath, 0, sizeof(reqpath));
path_struct_len = c32_build_handle_path_from_dos_path(reqpath,
(uint8)dir_handle,
0, 0,
path_name);
/*
* ncpfs seeks to absolute packet offset 16+307 before writing the
* trustee list. The NCP request header is 7 bytes, so inside the NCP
* payload the trustee list begins at:
*
* (16 + 307) - 7 = 316
*
* Our first Raw5 request fragment is the 9-byte subfunction header,
* so the trustee list begins in the second fragment at:
*
* 316 - 9 = 307
*/
if (path_struct_len > 307)
return(2);
tp = reqpath + 307;
c32_put_dword_hl(tp, object_id); tp += 4;
c32_put_word_lh(tp, rights); tp += 2;
reqpath_len = (UI)(tp - reqpath);
memset(rep0, 0, sizeof(rep0));
memset(rep1, 0, sizeof(rep1));
memset(rawout, 0, sizeof(rawout));
C32_NCP87_Raw5_Probe(handle_lo, handle_hi,
hdr, 9,
reqpath, reqpath_len,
rep0, sizeof(rep0),
rep1, sizeof(rep1),
rawout);
raw_ret_ax = c32_get_word_lh(rawout + 14);
raw_ret_dx = c32_get_word_lh(rawout + 16);
actual_lo = c32_get_word_lh(rawout + 18);
if (actual_out) *actual_out = actual_lo;
if (handle_lo_out) *handle_lo_out = handle_lo;
if (handle_hi_out) *handle_hi_out = handle_hi;
if (raw_ret_ax != 0 || raw_ret_dx != 0)
return(20);
return(0);
}
int c32_ncp87_find_trustee_rights(const char *path_name,
uint16 dir_handle,
uint32 object_id,
uint16 *rights_out,
uint16 *actual_out,
uint16 *handle_lo_out,
uint16 *handle_hi_out)
{
uint16 handle_lo, handle_hi;
uint8 hdr[16];
uint8 path[0x140];
uint8 rep0[0x120];
uint8 rep1[0x120];
uint8 rawout[32];
uint16 raw_ret_ax, raw_ret_dx;
uint16 actual_lo;
uint32 seq = 0;
UI path_len;
int rc;
if (rights_out) *rights_out = 0;
if (actual_out) *actual_out = 0;
if (handle_lo_out) *handle_lo_out = 0;
if (handle_hi_out) *handle_hi_out = 0;
if (!rights_out)
return(1);
rc = c32_get_ncp_handle(&handle_lo, &handle_hi);
if (rc)
return(10 + rc);
for (;;) {
uint16 count;
uint16 i;
uint8 *tp;
uint32 next_seq;
memset(hdr, 0, sizeof(hdr));
hdr[0] = 5; /* NCP87 subfunction 5: scan trustees */
hdr[1] = 0; /* DOS namespace */
hdr[2] = 0; /* reserved */
c32_put_word_lh(hdr + 3, 0x8006); /* SA_ALL: files/subdirs + system + hidden */
c32_put_dword_lh(hdr + 5, seq); /* search sequence, starts at zero */
path_len = c32_build_handle_path_from_dos_path(path,
(uint8)dir_handle,
0, 0,
path_name);
memset(rep0, 0, sizeof(rep0));
memset(rep1, 0, sizeof(rep1));
memset(rawout, 0, sizeof(rawout));
C32_NCP87_Raw5_Probe(handle_lo, handle_hi,
hdr, 9,
path, path_len,
rep0, sizeof(rep0),
rep1, sizeof(rep1),
rawout);
raw_ret_ax = c32_get_word_lh(rawout + 14);
raw_ret_dx = c32_get_word_lh(rawout + 16);
actual_lo = c32_get_word_lh(rawout + 18);
if (actual_out) *actual_out = actual_lo;
if (handle_lo_out) *handle_lo_out = handle_lo;
if (handle_hi_out) *handle_hi_out = handle_hi;
if (raw_ret_ax != 0 || raw_ret_dx != 0)
return(0xff); /* Client32 returns an error when no trustees are present. */
next_seq = c32_get_dword_lh(rep0 + 0);
count = c32_get_word_lh(rep0 + 4);
if (count > 20)
count = 20;
tp = rep0 + 6;
for (i = 0; i < count; i++) {
uint32 tid = c32_get_dword_hl(tp);
uint16 trights = c32_get_word_lh(tp + 4);
if (tid == object_id || c32_get_dword_lh(tp) == object_id) {
*rights_out = trights;
return(0);
}
tp += 6;
}
if (next_seq == 0xffffffffUL || next_seq == seq)
break;
seq = next_seq;
}
return(0xff); /* no trustee found / no more entries */
}
int c32_ncp87_delete_trustee_rights(const char *path_name,
uint16 dir_handle,
uint32 object_id,
uint16 *actual_out,
uint16 *handle_lo_out,
uint16 *handle_hi_out)
{
uint16 handle_lo, handle_hi;
uint8 hdr[16];
uint8 reqpath[0x180];
uint8 rep0[0x20];
uint8 rep1[0x20];
uint8 rawout[32];
uint16 raw_ret_ax, raw_ret_dx;
uint16 actual_lo;
UI path_struct_len;
UI reqpath_len;
uint8 *tp;
int rc;
if (actual_out) *actual_out = 0;
if (handle_lo_out) *handle_lo_out = 0;
if (handle_hi_out) *handle_hi_out = 0;
rc = c32_get_ncp_handle(&handle_lo, &handle_hi);
if (rc)
return(10 + rc);
memset(hdr, 0, sizeof(hdr));
hdr[0] = 11; /* NCP87 subfunction 11: delete trustee */
hdr[1] = 0; /* DOS namespace */
hdr[2] = 0; /* reserved */
c32_put_word_lh(hdr + 3, 1); /* one trustee */
memset(reqpath, 0, sizeof(reqpath));
path_struct_len = c32_build_handle_path_from_dos_path(reqpath,
(uint8)dir_handle,
0, 0,
path_name);
if (path_struct_len > 307)
return(2);
tp = reqpath + 307;
c32_put_dword_hl(tp, object_id); tp += 4;
c32_put_word_lh(tp, 0); tp += 2;
reqpath_len = (UI)(tp - reqpath);
memset(rep0, 0, sizeof(rep0));
memset(rep1, 0, sizeof(rep1));
memset(rawout, 0, sizeof(rawout));
C32_NCP87_Raw5_Probe(handle_lo, handle_hi,
hdr, 5,
reqpath, reqpath_len,
rep0, sizeof(rep0),
rep1, sizeof(rep1),
rawout);
raw_ret_ax = c32_get_word_lh(rawout + 14);
raw_ret_dx = c32_get_word_lh(rawout + 16);
actual_lo = c32_get_word_lh(rawout + 18);
if (actual_out) *actual_out = actual_lo;
if (handle_lo_out) *handle_lo_out = handle_lo;
if (handle_hi_out) *handle_hi_out = handle_hi;
if (raw_ret_ax != 0 || raw_ret_dx != 0)
return(20);
return(0);
}