Files
mars-dosutils/c32ncp.c
Mario Fetka 50524cf759 dosutils: add GPL-2 headers and file descriptions
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.
2026-05-29 08:07:09 +02:00

1374 lines
38 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: Client32 and raw NCP helper implementation used by the NetWare DOS tools.
* Depends on: net.h, c32ncp.h, kern_wasm.asm/kern.asm for Client32 probe entry points, and netcall.c for shared requester state.
*/
#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);
}