/* * 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 . */ /* * Purpose: Namespace and file-system NCP API 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. This file is planned to become ncpapi.c. */ #include "net.h" #include "c32ncp.h" /* c32ncp.c - namespace/file-system NCP API helpers for mars-dosutils */ /* * c32_put_word_lh * * Purpose: * Writes a 16-bit little-endian value into a request buffer. * * Notes: * This file will later become ncpapi.c. These local helpers remain here * for now so the NCP API wrappers keep their packet construction isolated * from the more general tools.c helpers. */ static void c32_put_word_lh(uint8 *p, uint16 v) { p[0] = (uint8)(v & 0xff); p[1] = (uint8)((v >> 8) & 0xff); } /* * c32_put_dword_lh * * Purpose: * Writes a 32-bit little-endian value into a request buffer. */ 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); } /* * c32_put_dword_hl * * Purpose: * Writes a 32-bit high-low value into a request buffer. * * Notes: * Some older file and trustee NCPs use high-low object ids or offsets even * when the surrounding NCP87 fields are little-endian. */ 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); } /* * c32_get_word_lh * * Purpose: * Reads a 16-bit little-endian value from a reply buffer. */ static uint16 c32_get_word_lh(uint8 *p) { return((uint16)(p[0] | ((uint16)p[1] << 8))); } /* * c32_get_dword_lh * * Purpose: * Reads a 32-bit little-endian value from a reply buffer. */ static uint32 c32_get_dword_lh(uint8 *p) { return((uint32)p[0] | ((uint32)p[1] << 8) | ((uint32)p[2] << 16) | ((uint32)p[3] << 24)); } /* * c32_get_dword_hl * * Purpose: * Reads a 32-bit high-low value from a reply buffer. */ static uint32 c32_get_dword_hl(uint8 *p) { return(((uint32)p[0] << 24) | ((uint32)p[1] << 16) | ((uint32)p[2] << 8) | (uint32)p[3]); } /* * c32_build_handle_path * * Purpose: * Builds the Client32-compatible NWHandlePathStruct used by NCP87 request * wrappers when the caller already has one to three path components. * * Parameters: * dhandle/dirbase/style describe the starting directory context. c1..c3 * are written as length-prefixed path components. * * Returns: * Number of bytes used by the path structure. */ 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); } /* * c32_build_handle_path_from_dos_path * * Purpose: * Converts a DOS-style path string into the Client32-compatible * NWHandlePathStruct used by the NCP87 wrappers. * * Notes: * Drive prefixes, leading slashes and simple current-directory components * are skipped so tool callers can pass ordinary DOS paths. */ 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. */ /* * c32_get_ncp_handle * * Purpose: * Resolves the active MARS/NetWare connection into the Client32 NCP handle * used by the Raw5 requester probe. * * Requester path: * C32_MapVar_Probe followed by C32_OpenRef_Probe. * * Returns: * 0 on success. Non-zero values indicate that the connection reference or * Client32 NCP handle could not be obtained. */ 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); } /* * c32_copy_open_reply_to_handle6 * * Purpose: * Extracts the six-byte server file handle from an NCP87/01 Open/Create * reply fragment for later NCP74 Copy or NCP66 Close calls. * * Notes: * The exact handle layout is still part of the NCOPY investigation. This * helper keeps the extraction in one place so the code can be corrected * without touching the callers. */ 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); } /* * ncp87_01_open_create_entry * * Purpose: * Opens or creates a namespace file/directory entry and returns the server * file handle required by copy/close style NCPs. * * NCP: * Function 0x57 / subfunction 0x01, Open/Create File or Subdirectory. * * Requester path: * Client32 Raw5 fragment request. * * Notes: * This wrapper is currently experimental for NCOPY. Earlier tests showed * that the old INT 21h F257 path reaches MARS-NWE but does not reliably * return the open file handle to the DOS caller. */ int ncp87_01_open_create_entry(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 * * The Client32 Raw5 requester path sends 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); } /* * ncp74_file_server_copy * * Purpose: * Copies file data server-side between two already-open NetWare file * handles. * * NCP: * Function 0x4a, File Server Copy. * * Requester path: * Direct F24A Net_Call wrapper. * * Notes: * Offsets and byte count are encoded high-low. The source and destination * handles must be the six-byte server handles accepted by the file server. */ int 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); } /* * ncp66_close_file * * Purpose: * Closes a NetWare server file handle returned by an open/create wrapper. * * NCP: * Function 0x42 / decimal 66, Close File. * * Requester path: * Direct F242 Net_Call wrapper. */ int 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); } /* * ncp87_06_obtain_rim_attributes * * Purpose: * Reads only the DOS attribute field for a directory entry using the * NCP87 return-information-mask mechanism. * * NCP: * Function 0x57 / subfunction 0x06, Obtain File or Subdirectory * Information, RIM_ATTRIBUTES. * * Requester path: * Client32 Raw5 fragment request. */ int ncp87_06_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); } /* * ncp87_06_obtain_ndir_info * * Purpose: * Reads the DOS namespace information block used by NDIR for Novell-style * metadata output. * * NCP: * Function 0x57 / subfunction 0x06, Obtain File or Subdirectory * Information, RIM_ALL. * * Requester path: * Client32 Raw5 fragment request. */ int ncp87_06_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); } /* * ncp87_07_modify_dos_info * * Purpose: * Writes selected DOS namespace metadata fields for a file or directory. * * NCP: * Function 0x57 / subfunction 0x07, Modify File or Subdirectory DOS * Information. * * Requester path: * Client32 Raw5 fragment request. */ int ncp87_07_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); } /* * ncp87_07_modify_dos_attributes * * Purpose: * Convenience wrapper around ncp87_07_modify_dos_info() for changing only * the DOS attributes field. * * NCP: * Function 0x57 / subfunction 0x07 with DM_ATTRIBUTES. */ int ncp87_07_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(ncp87_07_modify_dos_info(name, dir_handle, 0x00000002UL, &info, actual_out, handle_lo_out, handle_hi_out)); } /* * ncp87_1d_get_effective_rights * * Purpose: * Reads the effective rights mask for a path. * * NCP: * Function 0x57 / subfunction 0x1d, Get Effective Rights. * * Requester path: * Client32 Raw5 fragment request with an NWHandlePathStruct. */ int ncp87_1d_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); } /* * ncp87_1d_get_effective_rights_by_dirent * * Purpose: * Reads effective rights using a volume number and DOS directory number * instead of a textual path. * * NCP: * Function 0x57 / subfunction 0x1d, Get Effective Rights. * * Notes: * NDIR uses this form after obtaining directory-entry metadata. */ int ncp87_1d_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); } /* * c32_build_ncp22_trustee_path * * Purpose: * Builds the volume-qualified path format required by old NCP22 trustee * calls from the DOS utility display/header path. * * Notes: * This helper intentionally strips the server component and leaves a * SYS:DIR\FILE style path for directory handle 0 requests. */ 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); } /* * ncp22_27_set_trustee_rights * * Purpose: * Adds or updates a trustee assignment using the old directory services NCP * fallback path. * * NCP: * Function 0x16 / subfunction 0x27, Add Extended Trustee. * * Requester path: * E200 Net_Call wrapper with a volume-qualified path. */ int ncp22_27_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); } /* * ncp22_2b_delete_trustee_rights * * Purpose: * Deletes a trustee assignment using the old directory services NCP * fallback path. * * NCP: * Function 0x16 / subfunction 0x2b, Delete Trustee. * * Requester path: * E200 Net_Call wrapper with a volume-qualified path. */ int ncp22_2b_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); } /* * ncp87_0a_add_trustee_rights * * Purpose: * Adds trustee rights using the namespace-aware NCP87 trustee API. * * NCP: * Function 0x57 / subfunction 0x0a, Add Trustee Set. * * Requester path: * Client32 Raw5 fragment request. */ int ncp87_0a_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); } /* * ncp87_05_find_trustee_rights * * Purpose: * Scans trustee entries for a path until the requested bindery object id is * found. * * NCP: * Function 0x57 / subfunction 0x05, Scan Trustee Set. * * Requester path: * Client32 Raw5 fragment request. */ int ncp87_05_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 */ } /* * ncp87_0b_delete_trustee_rights * * Purpose: * Deletes trustee rights using the namespace-aware NCP87 trustee API. * * NCP: * Function 0x57 / subfunction 0x0b, Delete Trustee Set. * * Requester path: * Client32 Raw5 fragment request. */ int ncp87_0b_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); }